DUDS
Distributed Update of Data from Something
DevSmbus.cpp
Go to the documentation of this file.
1 /*
2  * This file is part of the DUDS project. It is subject to the BSD-style
3  * license terms in the LICENSE file found in the top-level directory of this
4  * distribution and at https://github.com/jjackowski/duds/blob/master/LICENSE.
5  * No part of DUDS, including this file, may be copied, modified, propagated,
6  * or distributed except according to the terms contained in the LICENSE file.
7  *
8  * Copyright (C) 2017 Jeff Jackowski
9  */
12 #include <boost/exception/errinfo_errno.hpp>
13 #include <boost/exception/errinfo_file_name.hpp>
14 #include <cstring> // for memcpy
15 #include <algorithm> // for max
16 #include <thread> // for yield
17 #include <fcntl.h> // for open and O_RDWR
18 #include <sys/ioctl.h> // for ioctl
19 #include <linux/i2c.h>
20 #include <linux/i2c-dev.h>
21 
22 namespace duds { namespace hardware { namespace interface { namespace linux {
23 
24 DevSmbus::DevSmbus(const std::string &devname, int devaddr, bool pec) :
25 dev(devname), addr(devaddr) {
26  fd = open(dev.c_str(), O_RDWR);
27  if (fd < 0) {
28  DUDS_THROW_EXCEPTION(SmbusErrorNoBus() << boost::errinfo_errno(errno) <<
29  boost::errinfo_file_name(dev) << SmbusDeviceAddr(addr)
30  );
31  }
32  if ((addr > 127) && (ioctl(fd, I2C_TENBIT, 1) < 0)) {
33  close(fd);
35  boost::errinfo_file_name(dev) << SmbusDeviceAddr(addr)
36  );
37  }
38  if (ioctl(fd, I2C_SLAVE, addr) < 0) {
39  int err = errno;
40  close(fd);
41  DUDS_THROW_EXCEPTION(SmbusError() << boost::errinfo_errno(err) <<
42  boost::errinfo_file_name(dev) << SmbusDeviceAddr(addr)
43  );
44  }
45  if (ioctl(fd, I2C_PEC, pec ? 1 : 0) < 0) {
46  close(fd);
48  boost::errinfo_file_name(dev) << SmbusDeviceAddr(addr)
49  );
50  }
51 }
52 
54  close(fd);
55 }
56 
57 void DevSmbus::io(i2c_smbus_ioctl_data &sdat) {
58  int res;
59  do {
60  if (ioctl(fd, I2C_SMBUS, &sdat) < 0) {
61  res = errno;
62  switch (res) {
63  case EAGAIN: // is this possible?
64  // brief wait before next attempt
65  std::this_thread::yield();
66  break;
67  case EBADMSG:
69  boost::errinfo_file_name(dev) <<
71  );
72  case EBUSY:
74  boost::errinfo_file_name(dev) <<
76  );
77  case ENXIO:
78  case ENODEV:
79  case EREMOTEIO: // seems to be used for the same thing as above,
80  // but not documented as such in Linux I2C docs
82  boost::errinfo_file_name(dev) <<
83  boost::errinfo_errno(res) <<
85  );
86  case EOPNOTSUPP:
88  boost::errinfo_file_name(dev) <<
90  );
91  case EPROTO:
93  boost::errinfo_file_name(dev) <<
95  );
96  case ETIMEDOUT:
98  boost::errinfo_file_name(dev) <<
100  );
101  default:
103  boost::errinfo_file_name(dev) <<
104  boost::errinfo_errno(res) <<
106  );
107  }
108  }
109  } while (res == EAGAIN); // is this possible?
110 }
111 
112 void DevSmbus::transmitBool(bool out) {
113  i2c_smbus_ioctl_data sdat = {
114  .read_write = (std::uint8_t)(out ? I2C_SMBUS_READ : I2C_SMBUS_WRITE),
115  .command = 0,
116  .size = I2C_SMBUS_QUICK,
117  .data = NULL
118  };
119  io(sdat);
120 }
121 
122 std::uint8_t DevSmbus::receiveByte() {
123  i2c_smbus_data msg;
124  i2c_smbus_ioctl_data sdat = {
125  .read_write = I2C_SMBUS_READ,
126  .command = 0,
127  .size = I2C_SMBUS_BYTE,
128  .data = &msg
129  };
130  io(sdat);
131  return msg.byte;
132 }
133 
134 void DevSmbus::transmitByte(std::uint8_t byte) {
135  i2c_smbus_data msg;
136  i2c_smbus_ioctl_data sdat = {
137  .read_write = I2C_SMBUS_WRITE,
138  .command = 0,
139  .size = I2C_SMBUS_BYTE,
140  .data = &msg
141  };
142  msg.byte = byte;
143  io(sdat);
144 }
145 
146 std::uint8_t DevSmbus::receiveByte(std::uint8_t cmd) {
147  i2c_smbus_data msg;
148  i2c_smbus_ioctl_data sdat = {
149  .read_write = I2C_SMBUS_READ,
150  .command = cmd,
151  .size = I2C_SMBUS_BYTE_DATA,
152  .data = &msg
153  };
154  io(sdat);
155  return msg.byte;
156 }
157 
158 void DevSmbus::transmitByte(std::uint8_t cmd, std::uint8_t byte) {
159  i2c_smbus_data msg;
160  i2c_smbus_ioctl_data sdat = {
161  .read_write = I2C_SMBUS_WRITE,
162  .command = cmd,
163  .size = I2C_SMBUS_BYTE_DATA,
164  .data = &msg
165  };
166  msg.byte = byte;
167  io(sdat);
168 }
169 
170 std::uint16_t DevSmbus::receiveWord(std::uint8_t cmd) {
171  i2c_smbus_data msg;
172  i2c_smbus_ioctl_data sdat = {
173  .read_write = I2C_SMBUS_READ,
174  .command = cmd,
175  .size = I2C_SMBUS_WORD_DATA,
176  .data = &msg
177  };
178  io(sdat);
179  return msg.word;
180 }
181 
182 void DevSmbus::transmitWord(std::uint8_t cmd, std::uint16_t word) {
183  i2c_smbus_data msg;
184  i2c_smbus_ioctl_data sdat = {
185  .read_write = I2C_SMBUS_WRITE,
186  .command = cmd,
187  .size = I2C_SMBUS_WORD_DATA,
188  .data = &msg
189  };
190  msg.word = word;
191  io(sdat);
192 }
193 
194 int DevSmbus::receive(std::uint8_t cmd, std::uint8_t *in, const int maxlen) {
195  i2c_smbus_data msg;
196  i2c_smbus_ioctl_data sdat = {
197  .read_write = I2C_SMBUS_READ,
198  .command = cmd,
199  .size = I2C_SMBUS_BLOCK_DATA,
200  .data = &msg
201  };
202  io(sdat);
203  std::memcpy(in, msg.block + 1, std::max(maxlen, (int)msg.block[0]));
204  if (maxlen < (int)msg.block[0]) {
206  boost::errinfo_file_name(dev) <<
208  );
209  }
210  return msg.block[0];
211 }
212 
213 void DevSmbus::receive(std::uint8_t cmd, std::vector<std::uint8_t> &in) {
214  i2c_smbus_data msg;
215  i2c_smbus_ioctl_data sdat = {
216  .read_write = I2C_SMBUS_READ,
217  .command = cmd,
218  .size = I2C_SMBUS_BLOCK_DATA,
219  .data = &msg
220  };
221  io(sdat);
222  in.resize(msg.block[0]);
223  std::memcpy(&(in[0]), msg.block + 1, msg.block[0]);
224 }
225 
227  std::uint8_t cmd,
228  const std::uint8_t *out,
229  const int len
230 ) {
231  if ((len <= 0) || (len > 32)) {
233  boost::errinfo_file_name(dev) << SmbusDeviceAddr(addr)
234  );
235  }
236  i2c_smbus_data msg;
237  i2c_smbus_ioctl_data sdat = {
238  .read_write = I2C_SMBUS_WRITE,
239  .command = cmd,
240  .size = I2C_SMBUS_BLOCK_DATA,
241  .data = &msg
242  };
243  msg.block[0] = len;
244  std::memcpy(msg.block + 1, out, len);
245  io(sdat);
246 }
247 
248 std::uint16_t DevSmbus::call(std::uint8_t cmd, std::uint16_t word) {
249  i2c_smbus_data msg;
250  i2c_smbus_ioctl_data sdat = {
251  .read_write = I2C_SMBUS_WRITE,
252  .command = cmd,
253  .size = I2C_SMBUS_BLOCK_DATA,
254  .data = &msg
255  };
256  msg.word = word;
257  io(sdat);
258  return msg.word;
259 }
260 
262  std::uint8_t cmd,
263  const std::vector<std::uint8_t> &out,
264  std::vector<std::uint8_t> &in
265 ) {
266  if (out.size() > 32) {
268  boost::errinfo_file_name(dev) << SmbusDeviceAddr(addr)
269  );
270  }
271  i2c_smbus_data msg;
272  i2c_smbus_ioctl_data sdat = {
273  .read_write = I2C_SMBUS_WRITE,
274  .command = cmd,
275  .size = I2C_SMBUS_BLOCK_DATA,
276  .data = &msg
277  };
278  msg.block[0] = out.size();
279  std::memcpy(msg.block + 1, &(out[0]), out.size());
280  io(sdat);
281  in.resize(msg.block[0]);
282  std::memcpy(&(in[0]), msg.block + 1, msg.block[0]);
283 }
284 
285 int DevSmbus::address() const {
286  return addr;
287 }
288 
289 } } } }
DevSmbus(const std::string &devname, int devaddr, bool pec=true)
Opens the device file for the bus.
Definition: DevSmbus.cpp:24
virtual void transmitByte(std::uint8_t byte)
Sends a single byte to the device.
Definition: DevSmbus.cpp:134
A timeout occured while waiting to use the bus.
Definition: SmbusErrors.hpp:75
The attempted operation is not supported by the bus master.
Definition: SmbusErrors.hpp:49
virtual int receive(std::uint8_t cmd, std::uint8_t *in, const int maxlen)
Sends a command byte, then reads a block of data from the device.
Definition: DevSmbus.cpp:194
The device did not respond to its address.
Definition: SmbusErrors.hpp:44
An attempt was made to use a non-existent SMBus bus.
Definition: SmbusErrors.hpp:38
~DevSmbus()
Closes the device file.
Definition: DevSmbus.cpp:53
The device has failed to conform to the protocol.
Definition: SmbusErrors.hpp:54
The received message included a bad checksum and Packet Error Checking is in use. ...
Definition: SmbusErrors.hpp:26
virtual int address() const
Returns the address of the device that this object will attempt to communicate with.
Definition: DevSmbus.cpp:285
boost::error_info< struct Info_smbusdevaddr, int > SmbusDeviceAddr
Provides the device (slave) address along with an error.
Definition: SmbusErrors.hpp:80
virtual void transmit(std::uint8_t cmd, const std::uint8_t *out, const int len)
Sends a command byte and a block of data to the device.
Definition: DevSmbus.cpp:226
Base class for all errors specific to SMBus communications and used for very general errors...
Definition: SmbusErrors.hpp:20
virtual std::uint8_t receiveByte()
Read a single byte from the device without sending a command/register byte first. ...
Definition: DevSmbus.cpp:122
int addr
The device (slave) address; used for error reporting.
Definition: DevSmbus.hpp:52
int fd
The file descriptor for the open device.
Definition: DevSmbus.hpp:48
A message of an invalid length was specified, or a message too big for a buffer was received...
Definition: SmbusErrors.hpp:33
void io(i2c_smbus_ioctl_data &sdat)
Sends I/O requests to the kernel, then checks for an error and if found throws the appropriate except...
Definition: DevSmbus.cpp:57
std::string dev
Stores the device file name for later error reporting.
Definition: DevSmbus.hpp:44
virtual std::uint16_t receiveWord(std::uint8_t cmd)
Sends a command byte, then reads a word, two bytes, from the device.
Definition: DevSmbus.cpp:170
virtual void transmitWord(std::uint8_t cmd, std::uint16_t word)
Sends a command byte and a data word to the device.
Definition: DevSmbus.cpp:182
virtual std::uint16_t call(std::uint8_t cmd, std::uint16_t word)
Does a process call operation.
Definition: DevSmbus.cpp:248
#define DUDS_THROW_EXCEPTION(x)
Works like BOOST_THROW_EXCEPTION, but includes a stack trace if DUDS_ERRORS_VERBOSE is defined...
Definition: Errors.hpp:48
virtual void transmitBool(bool out)
Sends a single bit to the device.
Definition: DevSmbus.cpp:112