DUDS
Distributed Update of Data from Something
DevI2c.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  */
13 #include <duds/general/Errors.hpp>
14 #include <boost/exception/errinfo_errno.hpp>
15 #include <boost/exception/errinfo_file_name.hpp>
16 #include <fcntl.h> // for open and O_RDWR
17 #include <sys/ioctl.h> // for ioctl
18 #include <linux/i2c.h>
19 #include <linux/i2c-dev.h>
20 
21 namespace duds { namespace hardware { namespace interface { namespace linux {
22 
23 DevI2c::DevI2c(const std::string &devname, int devaddr) :
24 dev(devname), addr(devaddr) {
25  fd = open(dev.c_str(), O_RDWR);
26  if (fd < 0) {
27  DUDS_THROW_EXCEPTION(I2cErrorNoBus() << boost::errinfo_errno(errno) <<
28  boost::errinfo_file_name(dev) << I2cDeviceAddr(addr)
29  );
30  }
31  if ((addr > 127) && (ioctl(fd, I2C_TENBIT, 1) < 0)) {
32  close(fd);
34  boost::errinfo_file_name(dev) << I2cDeviceAddr(addr)
35  );
36  }
37 }
38 
40  close(fd);
41 }
42 
43 void DevI2c::io(i2c_rdwr_ioctl_data &idat) {
44  if (ioctl(fd, I2C_RDWR, &idat) < 0) {
45  int res = errno;
46  switch (res) {
47  case EBUSY:
49  boost::errinfo_file_name(dev) <<
51  );
52  case ENXIO:
53  case ENODEV:
54  case EREMOTEIO: // seems to be used for the same thing as above,
55  // but not documented as such in Linux I2C docs
57  boost::errinfo_file_name(dev) <<
58  boost::errinfo_errno(res) <<
60  );
61  case EOPNOTSUPP:
63  boost::errinfo_file_name(dev) <<
65  );
66  case EPROTO:
68  boost::errinfo_file_name(dev) <<
70  );
71  case ETIMEDOUT:
73  boost::errinfo_file_name(dev) <<
75  );
76  default:
78  boost::errinfo_file_name(dev) <<
79  boost::errinfo_errno(res) <<
81  );
82  }
83  }
84 }
85 
86 // The maximum number of supported I2C messages in a single ioctl call has a
87 // typo in some earlier kernels. A fix was put in by 4.4. Attempt to use
88 // the non-typo first so that when the typo is eventually removed this code
89 // doesn't break.
90 #ifdef I2C_RDWR_IOCTL_MAX_MSGS
91 #define I2C_MAX_MSGS I2C_RDWR_IOCTL_MAX_MSGS
92 #elif defined(I2C_RDRW_IOCTL_MAX_MSGS)
93 #define I2C_MAX_MSGS I2C_RDRW_IOCTL_MAX_MSGS
94 #else
95 #error Neither I2C_RDWR_IOCTL_MAX_MSGS nor I2C_RDRW_IOCTL_MAX_MSGS is defined.
96 #endif
97 
99  // empty conversation check
100  if (conv.empty()) {
101  return; // nothing to do
102  }
103  // no larger than I2C_MAX_MSGS
104  i2c_msg iparts[(conv.size() > I2C_MAX_MSGS) ? I2C_MAX_MSGS : conv.size()];
105  i2c_rdwr_ioctl_data idat = {
106  .msgs = iparts,
107  .nmsgs = 0 // fill in later
108  };
109  // visit each conversation part
110  Conversation::PartVector::iterator iter = conv.begin();
111  for (int idx = 0; iter != conv.end(); ++iter, ++idx) {
112  ConversationPart &cp = *(*iter);
113  // check for a break in the conversation, but ignore first part
114  if (idx && (cp.flags() & ConversationPart::MpfBreak)) {
115  // do communication
116  io(idat);
117  // message count now zero
118  idat.nmsgs = 0;
119  } else if (idat.nmsgs == (I2C_MAX_MSGS - 1)) {
121  boost::errinfo_file_name(dev) << I2cDeviceAddr(addr) <<
123  );
124  }
125  __u16 flags;
126  // input part?
127  if (cp.input()) {
128  flags = I2C_M_RD;
129  if (cp.varyingLength()) {
130  // Check for inadequate length.
131  // Kernel header comments seem to imply that I2C_SMBUS_BLOCK_MAX
132  // is not intended to be the correct value for this check
133  // because I2C != SMBus, but kernel code seems to be using it
134  // anyway.
135  if (cp.length() <= 32) {
137  boost::errinfo_file_name(dev) << I2cDeviceAddr(addr) <<
139  );
140  }
141  // possibility that the first byte of the buffer needs to be
142  // set to 1 (number of bytes in addition to max recv len)
143  flags |= I2C_M_RECV_LEN;
144  } else {
145  iparts[idat.nmsgs].flags = I2C_M_RD;
146  }
147  } else {
148  flags = 0;
149  }
150  if (addr > 127) {
151  flags |= I2C_TENBIT;
152  }
153  iparts[idat.nmsgs].addr = addr;
154  iparts[idat.nmsgs].flags = flags;
155  iparts[idat.nmsgs].len = cp.length();
156  iparts[idat.nmsgs].buf = (__u8*)cp.start();
157  // increment the message count
158  ++idat.nmsgs;
159  }
160  // should have one more ioctl call to complete the task
161  io(idat);
162 }
163 
164 int DevI2c::address() const {
165  return addr;
166 }
167 
168 } } } }
static constexpr Flags MpfBreak
True/set to indicate that any kind of selection signal should be toggled, or a stop condition should ...
std::size_t size() const
Returns the number of parts within this conversation.
boost::error_info< struct Info_i2cdevaddr, int > I2cDeviceAddr
Provides the device (slave) address along with an error.
Definition: I2cErrors.hpp:83
DevI2c(const std::string &devname, int devaddr)
Opens the device file for the bus.
Definition: DevI2c.cpp:23
bool varyingLength() const
True if this part is flagged as having a variable length.
The number of conversation parts is too great for the implementation to handle.
Definition: I2cErrors.hpp:34
The device did not respond to its address (NACK).
Definition: I2cErrors.hpp:47
Represents a section of a half-duplex conversation with a device.
A ConversationPart of an invalid length was specified.
Definition: I2cErrors.hpp:28
virtual void converse(Conversation &conv)
Conducts I2C communication with a device using the Linux i2c-dev driver.
Definition: DevI2c.cpp:98
void io(i2c_rdwr_ioctl_data &idat)
Calls ioctl to request the kernel do the I2C communication, the check for error conditions and throw ...
Definition: DevI2c.cpp:43
static std::wstring_convert< std::codecvt_utf8< char32_t >, char32_t > conv
String converter; UTF-8 to/from UTF-32.
Definition: BppFont.cpp:165
Base class for all errors specific to I2C communications and used for very general errors...
Definition: I2cErrors.hpp:20
Flags flags() const
Returns the flags.
virtual std::size_t length() const =0
Returns the length of the buffer following the start pointer.
virtual char * start() const =0
Returns a pointer to the begining of the conversation part&#39;s buffer.
An attempt was made to use a non-existent I2C bus.
Definition: I2cErrors.hpp:39
The operation took too long.
Definition: I2cErrors.hpp:67
Header for Conversarion; includes ConversationVector.hpp and ConversationExternal.hpp.
PartVector::iterator begin()
An iterator to the first ConversationPart that allows modification.
The device has failed to conform to the protocol.
Definition: I2cErrors.hpp:57
int addr
The device (slave) address.
Definition: DevI2c.hpp:48
~DevI2c()
Closes the device file.
Definition: DevI2c.cpp:39
The attempted operation is not supported by the bus master.
Definition: I2cErrors.hpp:52
PartVector::iterator end()
An iterator to the end of ConversationPart vector.
A timeout occured while waiting to use the bus.
Definition: I2cErrors.hpp:78
virtual int address() const
Returns the address of the device that this object will attempt to communicate with.
Definition: DevI2c.cpp:164
int fd
The file descriptor for the open device.
Definition: DevI2c.hpp:44
std::string dev
Stores the device file name for later error reporting.
Definition: DevI2c.hpp:40
General errors.
bool input() const
True if this part is flagged for input use.
bool empty() const
Returns true if the conversation has no parts.
#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
Represents a two-way conversation with a device.
boost::error_info< struct Info_ConversationPartIndex, int > ConversationPartIndex
An attribute for errors when using Conversation objects that references the ConversationPart by index...