blob: e71ac12ec330eb7703eeba293c985473993f2838 [file] [log] [blame]
// 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_