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