| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <lib/device-protocol/i2c.h> |
| #include <lib/zx/interrupt.h> |
| |
| #include <ddktl/protocol/i2c.h> |
| |
| #ifndef SRC_DEVICES_I2C_TESTING_FAKE_I2C_INCLUDE_LIB_FAKE_I2C_FAKE_I2C_H_ |
| #define SRC_DEVICES_I2C_TESTING_FAKE_I2C_INCLUDE_LIB_FAKE_I2C_FAKE_I2C_H_ |
| |
| namespace fake_i2c { |
| |
| // This class fakes an I2c device by implementing the I2cProtocol. When a tester wants to |
| // have a fake I2c device, they should create a class that inherents FakeI2c and implements |
| // the |Transact| function. The |Transact| function is where the Fake driver can view the I2C |
| // Write data and return Read data. |
| // |
| // The FakeI2c class is helpful because it serializes the Read and Write data, making it easier |
| // for a tester to write a fake. |
| // |
| // Here's an example: |
| // |
| // class FakeLightSensor : public FakeI2c { |
| // virtual zx_status_t Transact(const uint8_t* write_buffer, size_t write_buffer_size, |
| // uint8_t* read_buffer, size_t* read_buffer_size) { |
| // if (CompareWrite(write_buffer, write_buffer_size, kReadLightRegisterCommand, |
| // sizeof(kReadLightRegisterCommand)) { |
| // SetRead(light_sensor_data_, sizeof(light_sensor_data_), read_buffer, |
| // sizeof(read_buffer_size); |
| // return ZX_OK; |
| // } |
| // return ZX_ERR_NOT_SUPPORTED; |
| // } |
| // } |
| // |
| class FakeI2c : public ddk::I2cProtocol<FakeI2c> { |
| public: |
| FakeI2c() : proto_({&i2c_protocol_ops_, this}) {} |
| |
| // This function takes the |op_list| and serialies the write data so it is easier to use |
| // in a fake. This will call |Transact| with the serialzed data. |
| void I2cTransact(const i2c_op_t* op_list, size_t op_count, i2c_transact_callback callback, |
| void* cookie) { |
| zx_status_t status; |
| |
| // Serialize the write information. |
| uint8_t write_buffer[I2C_MAX_TOTAL_TRANSFER]; |
| size_t write_buffer_index = 0; |
| size_t read_request_size = 0; |
| for (size_t i = 0; i < op_count; ++i) { |
| if (!op_list[i].is_read) { |
| if (write_buffer_index + op_list[i].data_size >= I2C_MAX_TOTAL_TRANSFER) { |
| callback(cookie, ZX_ERR_NO_MEMORY, nullptr, 0); |
| return; |
| } |
| memcpy(write_buffer + write_buffer_index, op_list[i].data_buffer, op_list[i].data_size); |
| write_buffer_index += op_list[i].data_size; |
| } else { |
| read_request_size += op_list[i].data_size; |
| } |
| } |
| |
| // Process the serialized ops. |
| uint8_t read_buffer[I2C_MAX_TOTAL_TRANSFER]; |
| size_t read_buffer_size = 0; |
| status = Transact(write_buffer, write_buffer_index, read_buffer, &read_buffer_size); |
| if (status != ZX_OK) { |
| callback(cookie, status, nullptr, 0); |
| return; |
| } |
| |
| // Return a read op if we have one. |
| if (read_buffer_size > 0) { |
| i2c_op_t read_op = {}; |
| read_op.is_read = true; |
| read_op.stop = true; |
| read_op.data_buffer = read_buffer; |
| read_op.data_size = read_buffer_size; |
| callback(cookie, ZX_OK, &read_op, 1); |
| } else { |
| callback(cookie, ZX_OK, nullptr, 0); |
| } |
| } |
| |
| zx_status_t I2cGetMaxTransferSize(size_t* out_size) { |
| *out_size = I2C_MAX_TOTAL_TRANSFER; |
| return ZX_OK; |
| } |
| |
| zx_status_t I2cGetInterrupt(uint32_t flags, zx::interrupt* out_irq) { |
| return irq_.duplicate(ZX_RIGHT_SAME_RIGHTS, out_irq); |
| } |
| |
| void SetInterrupt(zx::interrupt irq) { irq_ = std::move(irq); } |
| |
| i2c_protocol_t* GetProto() { return &proto_; } |
| |
| protected: |
| // The main function to be overriden for a specific fake. This is called on each |
| // I2cTransact, but with serialized write and read information so it is easier to |
| // use. |
| virtual zx_status_t Transact(const uint8_t* write_buffer, size_t write_buffer_size, |
| uint8_t* read_buffer, size_t* read_buffer_size) = 0; |
| |
| // Helper functions for specific fakes to use inside of |Transact|. |
| bool CompareWrite(const uint8_t* write_buffer, size_t write_buffer_size, |
| const uint8_t* command_buffer, size_t command_buffer_size) { |
| return (write_buffer_size == command_buffer_size) && |
| (memcmp(write_buffer, command_buffer, write_buffer_size) == 0); |
| } |
| |
| void SetRead(const void* return_buffer, size_t return_buffer_size, uint8_t* read_buffer, |
| size_t* read_buffer_size) { |
| size_t read_size = |
| (return_buffer_size < I2C_MAX_TOTAL_TRANSFER) ? return_buffer_size : I2C_MAX_TOTAL_TRANSFER; |
| memcpy(read_buffer, return_buffer, read_size); |
| *read_buffer_size = read_size; |
| } |
| |
| zx::interrupt irq_; |
| i2c_protocol_t proto_; |
| }; |
| |
| } // namespace fake_i2c |
| |
| #endif // SRC_DEVICES_I2C_TESTING_FAKE_I2C_INCLUDE_LIB_FAKE_I2C_FAKE_I2C_H_ |