1 #pragma once 2 3 #include "i2c_interface.hpp" 4 5 namespace i2c 6 { 7 8 class I2CDevice : public I2CInterface 9 { 10 private: 11 I2CDevice() = delete; 12 13 /** @brief Constructor 14 * 15 * Construct I2CDevice object from the bus id and device address 16 * 17 * Automatically opens the I2CDevice if initialState is OPEN. 18 * 19 * @param[in] busId - The i2c bus ID 20 * @param[in] devAddr - The device address of the I2C device 21 * @param[in] initialState - Initial state of the I2CDevice object 22 * @param[in] maxRetries - Maximum number of times to retry an I2C operation 23 */ I2CDevice(uint8_t busId,uint8_t devAddr,InitialState initialState=InitialState::OPEN,int maxRetries=0)24 explicit I2CDevice(uint8_t busId, uint8_t devAddr, 25 InitialState initialState = InitialState::OPEN, 26 int maxRetries = 0) : 27 devAddr(devAddr), maxRetries(maxRetries), 28 busStr("/dev/i2c-" + std::to_string(busId)) 29 { 30 if (initialState == InitialState::OPEN) 31 { 32 open(); 33 } 34 } 35 36 /** @brief Invalid file descriptor */ 37 static constexpr int INVALID_FD = -1; 38 39 /** @brief Empty adapter functionality value with no bit flags set */ 40 static constexpr unsigned long NO_FUNCS = 0; 41 42 /** @brief The i2c device address in the bus */ 43 uint8_t devAddr; 44 45 /** @brief Maximum number of times to retry an I2C operation */ 46 int maxRetries = 0; 47 48 /** @brief The file descriptor of the opened i2c device */ 49 int fd = INVALID_FD; 50 51 /** @brief The i2c bus path in /dev */ 52 std::string busStr; 53 54 /** @brief Cached I2C adapter functionality value */ 55 unsigned long cachedFuncs = NO_FUNCS; 56 57 /** @brief Check that device interface is open 58 * 59 * @throw I2CException if device is not open 60 */ checkIsOpen() const61 void checkIsOpen() const 62 { 63 if (!isOpen()) 64 { 65 throw I2CException("Device not open", busStr, devAddr); 66 } 67 } 68 69 /** @brief Close device without throwing an exception if an error occurs */ closeWithoutException()70 void closeWithoutException() noexcept 71 { 72 try 73 { 74 close(); 75 } 76 catch (...) 77 {} 78 } 79 80 /** @brief Get I2C adapter functionality 81 * 82 * Caches the adapter functionality value since it shouldn't change after 83 * opening the device. 84 * 85 * @throw I2CException on error 86 * @return Adapter functionality value 87 */ 88 unsigned long getFuncs(); 89 90 /** @brief Check i2c adapter read functionality 91 * 92 * Check if the i2c adapter has the functionality specified by the SMBus 93 * transaction type 94 * 95 * @param[in] type - The SMBus transaction type defined in linux/i2c.h 96 * 97 * @throw I2CException if the function is not supported 98 */ 99 void checkReadFuncs(int type); 100 101 /** @brief Check i2c adapter write functionality 102 * 103 * Check if the i2c adapter has the functionality specified by the SMBus 104 * transaction type 105 * 106 * @param[in] type - The SMBus transaction type defined in linux/i2c.h 107 * 108 * @throw I2CException if the function is not supported 109 */ 110 void checkWriteFuncs(int type); 111 112 /** @brief SMBus Block Write-Block Read Process Call using SMBus function 113 * 114 * In SMBus 2.0 the maximum write size + read size is <= 32 bytes. 115 * In SMBus 3.0 the maximum write size + read size is <= 255 bytes. 116 * The Linux SMBus function currently only supports the SMBus 2.0 maximum. 117 * 118 * @param[in] addr - The register address of the i2c device 119 * @param[in] writeSize - The size of data to write. Write size + read size 120 * must be <= 32 bytes. 121 * @param[in] writeData - The data to write to the i2c device 122 * @param[out] readSize - The size of data read from i2c device. Write size 123 * + read size must be <= 32 bytes. 124 * @param[out] readData - Pointer to buffer to hold the data read from the 125 * i2c device. Must be large enough to hold the data 126 * returned by the device (max is 32 bytes). 127 * 128 * @throw I2CException on error 129 */ 130 void processCallSMBus(uint8_t addr, uint8_t writeSize, 131 const uint8_t* writeData, uint8_t& readSize, 132 uint8_t* readData); 133 134 /** @brief SMBus Block Write-Block Read Process Call using I2C messages 135 * 136 * This method supports block writes of more than 32 bytes. It can also be 137 * used with I2C adapters that do not support the block process call 138 * protocol but do support I2C-level commands. 139 * 140 * This method implements the block process call using the lower level 141 * I2C_RDWR ioctl to send I2C messages. Using this ioctl allows for writes 142 * up to 255 bytes. The write size + read size must be <= 255 bytes. 143 * 144 * @param[in] addr - The register address of the i2c device 145 * @param[in] writeSize - The size of data to write. Write size + read size 146 * must be <= 255 bytes. 147 * @param[in] writeData - The data to write to the i2c device 148 * @param[out] readSize - The size of data read from i2c device. Max read 149 * size is 32 bytes, and write size + read size must 150 * be <= 255 bytes. 151 * @param[out] readData - Pointer to buffer to hold the data read from the 152 * i2c device. Must be large enough to hold the data 153 * returned by the device (max is 32 bytes). 154 * 155 * @throw I2CException on error 156 */ 157 void processCallI2C(uint8_t addr, uint8_t writeSize, 158 const uint8_t* writeData, uint8_t& readSize, 159 uint8_t* readData); 160 161 public: 162 /** @copydoc I2CInterface::~I2CInterface() */ ~I2CDevice()163 ~I2CDevice() 164 { 165 if (isOpen()) 166 { 167 // Note: destructors must not throw exceptions 168 closeWithoutException(); 169 } 170 } 171 172 /** @copydoc I2CInterface::open() */ 173 void open() override; 174 175 /** @copydoc I2CInterface::isOpen() */ isOpen() const176 bool isOpen() const override 177 { 178 return (fd != INVALID_FD); 179 } 180 181 /** @copydoc I2CInterface::close() */ 182 void close() override; 183 184 /** @copydoc I2CInterface::read(uint8_t&) */ 185 void read(uint8_t& data) override; 186 187 /** @copydoc I2CInterface::read(uint8_t,uint8_t&) */ 188 void read(uint8_t addr, uint8_t& data) override; 189 190 /** @copydoc I2CInterface::read(uint8_t,uint16_t&) */ 191 void read(uint8_t addr, uint16_t& data) override; 192 193 /** @copydoc I2CInterface::read(uint8_t,uint8_t&,uint8_t*,Mode) */ 194 void read(uint8_t addr, uint8_t& size, uint8_t* data, 195 Mode mode = Mode::SMBUS) override; 196 197 /** @copydoc I2CInterface::write(uint8_t) */ 198 void write(uint8_t data) override; 199 200 /** @copydoc I2CInterface::write(uint8_t,uint8_t) */ 201 void write(uint8_t addr, uint8_t data) override; 202 203 /** @copydoc I2CInterface::write(uint8_t,uint16_t) */ 204 void write(uint8_t addr, uint16_t data) override; 205 206 /** @copydoc I2CInterface::write(uint8_t,uint8_t,const uint8_t*,Mode) */ 207 void write(uint8_t addr, uint8_t size, const uint8_t* data, 208 Mode mode = Mode::SMBUS) override; 209 210 /** @copydoc I2CInterface::processCall(uint8_t,uint16_t,uint16_t&) */ 211 void processCall(uint8_t addr, uint16_t writeData, 212 uint16_t& readData) override; 213 214 /** @copydoc I2CInterface::processCall(uint8_t,uint8_t,const 215 * uint8_t*,uint8_t&,uint8_t*) */ 216 void processCall(uint8_t addr, uint8_t writeSize, const uint8_t* writeData, 217 uint8_t& readSize, uint8_t* readData) override; 218 219 /** @brief Create an I2CInterface instance 220 * 221 * Automatically opens the I2CInterface if initialState is OPEN. 222 * 223 * @param[in] busId - The i2c bus ID 224 * @param[in] devAddr - The device address of the i2c 225 * @param[in] initialState - Initial state of the I2CInterface object 226 * @param[in] maxRetries - Maximum number of times to retry an I2C operation 227 * 228 * @return The unique_ptr holding the I2CInterface 229 */ 230 static std::unique_ptr<I2CInterface> 231 create(uint8_t busId, uint8_t devAddr, 232 InitialState initialState = InitialState::OPEN, 233 int maxRetries = 0); 234 }; 235 236 } // namespace i2c 237