1 #pragma once
2 
3 #include <cstdint>
4 #include <cstring>
5 #include <exception>
6 #include <iostream>
7 #include <memory>
8 #include <sstream>
9 #include <string>
10 #include <vector>
11 
12 namespace i2c
13 {
14 
15 class I2CException : public std::exception
16 {
17   public:
18     explicit I2CException(const std::string& info, const std::string& bus,
19                           uint8_t addr, int errorCode = 0) :
20         bus(bus),
21         addr(addr), errorCode(errorCode)
22     {
23         std::stringstream ss;
24         ss << "I2CException: " << info << ": bus " << bus << ", addr 0x"
25            << std::hex << static_cast<int>(addr);
26         if (errorCode != 0)
27         {
28             ss << std::dec << ", errno " << errorCode << ": "
29                << strerror(errorCode);
30         }
31         errStr = ss.str();
32     }
33     virtual ~I2CException() = default;
34 
35     const char* what() const noexcept override
36     {
37         return errStr.c_str();
38     }
39     std::string bus;
40     uint8_t addr;
41     int errorCode;
42     std::string errStr;
43 };
44 
45 class I2CInterface
46 {
47   public:
48     /** @brief Destructor
49      *
50      * Closes the I2C interface to the device if necessary.
51      */
52     virtual ~I2CInterface() = default;
53 
54     /** @brief Initial state when an I2CInterface object is created */
55     enum class InitialState
56     {
57         OPEN,
58         CLOSED
59     };
60 
61     /** @brief The block transaction mode */
62     enum class Mode
63     {
64         SMBUS,
65         I2C,
66     };
67 
68     /** @brief Open the I2C interface to the device
69      *
70      * Throws an I2CException if the interface is already open.  See isOpen().
71      *
72      * @throw I2CException on error
73      */
74     virtual void open() = 0;
75 
76     /** @brief Indicates whether the I2C interface to the device is open
77      *
78      * @return true if interface is open, false otherwise
79      */
80     virtual bool isOpen() const = 0;
81 
82     /** @brief Close the I2C interface to the device
83      *
84      * The interface can later be re-opened by calling open().
85      *
86      * Note that the destructor will automatically close the I2C interface if
87      * necessary.
88      *
89      * Throws an I2CException if the interface is not open.  See isOpen().
90      *
91      * @throw I2CException on error
92      */
93     virtual void close() = 0;
94 
95     /** @brief Read byte data from i2c
96      *
97      * @param[out] data - The data read from the i2c device
98      *
99      * @throw I2CException on error
100      */
101     virtual void read(uint8_t& data) = 0;
102 
103     /** @brief Read byte data from i2c
104      *
105      * @param[in] addr - The register address of the i2c device
106      * @param[out] data - The data read from the i2c device
107      *
108      * @throw I2CException on error
109      */
110     virtual void read(uint8_t addr, uint8_t& data) = 0;
111 
112     /** @brief Read word data from i2c
113      *
114      * Uses the SMBus Read Word protocol.  Reads two bytes from the device, and
115      * the first byte read is the low-order byte.
116      *
117      * @param[in] addr - The register address of the i2c device
118      * @param[out] data - The data read from the i2c device
119      *
120      * @throw I2CException on error
121      */
122     virtual void read(uint8_t addr, uint16_t& data) = 0;
123 
124     /** @brief Read block data from i2c
125      *
126      * @param[in] addr - The register address of the i2c device
127      * @param[in,out] size - The out parameter to indicate the size of data read
128      *                       from i2c device; when mode is I2C, it is also the
129      *                       input parameter to indicate how many bytes to read
130      * @param[out] data - The pointer to buffer to read from the i2c device,
131      *                    the buffer shall be big enough to hold the data
132      *                    returned by the device. SMBus allows at most 32
133      *                    bytes.
134      * @param[in] mode - The block read mode, either SMBus or I2C.
135      *                   Internally, it invokes functions from libi2c:
136      *                    * For SMBus: i2c_smbus_read_block_data()
137      *                    * For I2C: i2c_smbus_read_i2c_block_data()
138      *
139      * @throw I2CException on error
140      */
141     virtual void read(uint8_t addr, uint8_t& size, uint8_t* data,
142                       Mode mode = Mode::SMBUS) = 0;
143 
144     /** @brief Write byte data to i2c
145      *
146      * @param[in] data - The data to write to the i2c device
147      *
148      * @throw I2CException on error
149      */
150     virtual void write(uint8_t data) = 0;
151 
152     /** @brief Write byte data to i2c
153      *
154      * @param[in] addr - The register address of the i2c device
155      * @param[in] data - The data to write to the i2c device
156      *
157      * @throw I2CException on error
158      */
159     virtual void write(uint8_t addr, uint8_t data) = 0;
160 
161     /** @brief Write word data to i2c
162      *
163      * Uses the SMBus Write Word protocol.  Writes two bytes to the device, and
164      * the first byte written is the low-order byte.
165      *
166      * @param[in] addr - The register address of the i2c device
167      * @param[in] data - The data to write to the i2c device
168      *
169      * @throw I2CException on error
170      */
171     virtual void write(uint8_t addr, uint16_t data) = 0;
172 
173     /** @brief Write block data to i2c
174      *
175      * @param[in] addr - The register address of the i2c device
176      * @param[in] size - The size of data to write, SMBus allows at most 32
177      *                   bytes
178      * @param[in] data - The data to write to the i2c device
179      * @param[in] mode - The block write mode, either SMBus or I2C.
180      *                   Internally, it invokes functions from libi2c:
181      *                    * For SMBus: i2c_smbus_write_block_data()
182      *                    * For I2C: i2c_smbus_write_i2c_block_data()
183      *
184      * @throw I2CException on error
185      */
186     virtual void write(uint8_t addr, uint8_t size, const uint8_t* data,
187                        Mode mode = Mode::SMBUS) = 0;
188 };
189 
190 /** @brief Create an I2CInterface instance
191  *
192  * Automatically opens the I2CInterface if initialState is OPEN.
193  *
194  * @param[in] busId - The i2c bus ID
195  * @param[in] devAddr - The device address of the i2c
196  * @param[in] initialState - Initial state of the I2CInterface object
197  * @param[in] maxRetries - Maximum number of times to retry an I2C operation
198  *
199  * @return The unique_ptr holding the I2CInterface
200  */
201 std::unique_ptr<I2CInterface> create(
202     uint8_t busId, uint8_t devAddr,
203     I2CInterface::InitialState initialState = I2CInterface::InitialState::OPEN,
204     int maxRetries = 0);
205 
206 } // namespace i2c
207