I have an i2c adapter exposed to userspace as /dev/i2c-0
. When I use i2cdetect
from the i2c-tools package on that adapter, I see my device listed on this adapter and I am able to perform get, set, and dump operations on it from the command line.
I have some C code that tries to perform a write on it using the ioctl I2C_RDWR
operation.
Here's the stripped-down code that I am working with (provided only for context, and running it isn't important to answering the questions).
fd = open ("/dev/i2c-0", O_RDWR | O_NONBLOCK);
/* other stuff happens here */
if (ioctl (fd, I2C_FUNCS, &funcs) != 0)
return -1;
if (!(funcs & I2C_FUNC_I2C))
return -1;
/* i added this bit of debug just to be sure fd didn't
* get inadvertently closed along the way.
*/
if (fcntl (fd, F_GETFD) != 0) {
return -1;
}
/* build the ioctl message payload */
ret = ioctl (fd, I2C_RDWR, &payload)
if (ret) {
fprintf (stderr, "ioctl returned %d. Reason: %s (errno=%d).",
ret, strerror(errno), errno);
return -1;
}
return 0;
I've also tried using the smbus ioctl functions, which looked like this.
fd = open ("/dev/i2c-0", O_RDWR | O_NONBLOCK);
/* other stuff happens here */
if (ioctl (fd, I2C_FUNCS, &funcs) != 0)
return -1;
if (!(funcs & I2C_FUNC_SMBUS_WORD_DATA))
return -1;
/* i added this bit of debug just to be sure fd didn't
* get inadvertently closed along the way.
*/
if (fcntl (fd, F_GETFD) != 0) {
return -1;
}
/* build the ioctl smbus message payload */
if (ioctl (fd, I2C_SLAVE_FORCE, dev) != 0)
return -1;
ret = ioctl (fd, I2C_SMBUS, &payload);
if (ret) {
fprintf (stderr, "ioctl returned %d. Reason: %s (errno=%d).",
ret, strerror(errno), errno);
return -1;
}
return 0;
In all cases, the program always hits the fprintf
and this is the output:
ioctl returned -1. Reason: No such device or address (errno=6).
According to man 3 ioctl
, the "generic" reason for an ENXIO error is:
ENXIO The request and arg arguments are valid for this device driver, but the service requested cannot be performed on this particular sub-device.
As Ian pointed out in the comments, however, the information regarding STREAMS in the above man page isn't relevant on Linux and I should be using man 2 ioctl
-- which, unfortunately, mostly just says that ioctls don't really conform to any standard, and anything goes.
This raises three questions.
Usually, it means the device did not respond. The specifics depend on your hardware.
The common I2C ioctl code is part of the i2c-dev package, which is part of the lm-sensors project. These ioctls do not have their own man page, but do have documentation in the kernel source tree.
In the kernel, the implementation is all kept in drivers/i2c/i2c-dev.c
and drivers/i2c/i2c-core.c
. The entry point is i2cdev_ioctl()
. None of this common code returns ENXIO.
Eventually, this common code needs to call down to hardware-level driver functions on the i2c bus. The hardware drivers for i2c busses are stored in drivers/i2c/busses/
. Each of these drivers can implement one or both of the master_xfer
(used for I2C_RDWR
ioctls) or smbus_xfer
(used for I2C_SMBUS
) functions.
In my case, the device actually didn't have dedicated i2c hardware and was using what appears to be a bit-banging software emulation implemented in function bit_xfer
of drivers/i2c/algos/i2c-algo-bit.c
. The ENXIO came from function bit_doAddress
.