IIC(4)

πŸ’– IIC(4) β€” Kernel Interfaces Manual πŸ’–

okay bestie, sit DOWN because we're talking about the iic driver and honestly? it's giving legend behavior. this is the I2C generic I/O device driver, and it lives in the Kernel Interfaces Manual. let's get into it ✨


🌸 NAME 🌸

iic β€” the I2C generic I/O device driver. that's it, that's the whole name, no cap.


✨ SYNOPSIS ✨

device iic

#include <dev/iicbus/iic.h>

πŸ¦‹ DESCRIPTION πŸ¦‹

so the iic device driver is your generic I/O plug to literally any iicbus(4) instance. it's the main character that lets you talk to your I2C devices.

to actually control I2C devices, you use /dev/iic? with these ioctls, and each one is a whole vibe:

  • I2CSTART β€” takes a (struct iiccmd). this one sends the start condition to the slave specified by the slave element, out to the bus. now the slave element is a 7-bit address plus a read/write bit β€” meaning it's 7-bit address << 1 | r/w. if that read/write bit is set, you're initiating a read operation; if it's cleared, it's a write operation. all the other elements? ignored, bestie, they're not invited. and if this works, the file descriptor gets exclusive ownership of the underlying iicbus instance. iconic.
  • I2CRPTSTART β€” takes a (struct iiccmd). sends the repeated start condition to the slave specified by the slave element to the bus. specify the slave address the same way you do in I2CSTART. all the other elements are ignored. heads up though: I2CSTART must have already been issued on the same file descriptor before this one, no skipping steps πŸ’…
  • I2CSTOP β€” no argument gets passed here. it sends the stop condition to the bus. if I2CSTART was previously issued on the file descriptor, the current transaction gets terminated and exclusive ownership of the underlying iicbus instance is released. if it wasn't issued? nothing happens. no action performed, we simply move on.
  • I2CRSTCARD β€” takes a (struct iiccmd). this one resets the bus, and the argument is completely and utterly ignored, so don't even stress about it. this command does not require I2CSTART to have been previously issued on the file descriptor. but if it was previously issued, then exclusive ownership of the underlying iicbus instance gets released. either way we're fine ✨
  • I2CWRITE β€” takes a (struct iiccmd). writes data to the iicbus(4). the bus must already be started by a previous I2CSTART on the file descriptor β€” no starting from scratch. the slave element is ignored. the count element is the number of bytes to write. the last element is a boolean flag, and it must be zero when additional read commands will follow, or non-zero if this is the last command. the buf element is a pointer to the data you're writing to the bus. slay.
  • I2CREAD β€” takes a (struct iiccmd). reads data from the iicbus(4). same deal: the bus must already be started by a previous I2CSTART on the file descriptor. the slave element is ignored. the count element is the number of bytes to read. the last element is a boolean flag β€” zero when additional read commands will follow, non-zero if this is the last command. the buf element is a pointer to where the data read from the bus gets stored.
⚠️ real talk: short reads on the bus produce undefined results. that's not a vibe, that's a warning. protect your peace (and your buffers).
  • I2CRDWR β€” takes a (struct iic_rdwr_data). this is the generic read/write interface, and it's lowkey powerful: it allows an arbitrary number of commands to be sent to an arbitrary number of devices on the bus. but listen β€” any previous transaction started by I2CSTART must be terminated by I2CSTOP or I2CRSTCARD before I2CRDWR can be issued on the same file descriptor. no loose ends. a read transfer is specified if IIC_M_RD is set in flags; otherwise it's a write transfer. the slave element specifies the 7-bit address along with the read/write bit for the transfer, and the read/write bit gets handled by the iicbus stack based on the specified transfer operation (so you don't gotta do it yourself, king). the len element is the number of (struct iic_msg) messages encoded on (struct iic_rdwr_data). the buf element is a buffer for that data. this ioctl is intended to be Linux compatible, iykyk.
  • I2CSADDR β€” takes a (uint8_t). this associates the specified address with the file descriptor so it can be used by subsequent read(2) or write(2) calls. the argument is an 8-bit address β€” that's 7-bit address << 1. the read/write bit in the least-significant position is ignored, and any subsequent read or write operation will set or clear that bit as needed. so considerate of it honestly.

πŸŽ€ the data structures πŸŽ€

these following data structures are defined in <dev/iicbus/iic.h> and were referenced up above, so here they are in all their glory:

struct iiccmd {
    u_char slave;
    int count;
    int last;
    char *buf;
};

/* Designed to be compatible with linux's struct i2c_msg */
struct iic_msg
{
    uint16_t    slave;
    uint16_t    flags;
#define IIC_M_WR    0    /* Fake flag for write */
#define IIC_M_RD    0x0001    /* read vs write */
#define IIC_M_NOSTOP    0x0002    /* do not send a I2C stop after message */
#define IIC_M_NOSTART    0x0004    /* do not send a I2C start before message */
    uint16_t    len;    /* msg length */
    uint8_t *    buf;
};

struct iic_rdwr_data {
    struct iic_msg *msgs;
    uint32_t nmsgs;
};

πŸ’… read(2) and write(2), the casual route πŸ’…

you can also just use read(2) or write(2), in which case the I2C start/stop handshake is managed by iicbus(4) for you. the address used for the read/write operation is the one passed to the most recent I2CSTART ioctl(2) or I2CSADDR ioctl(2) on the open /dev/iic? file descriptor.

and here's the part where it gets cute: closing the file descriptor clears any addressing state established by a previous I2CSTART or I2CSADDR, stops any transaction established by a not-yet-terminated I2CSTART, and releases iicbus ownership. clean slate, we love a fresh start 🫢

and because addressing state is stored on a per-file-descriptor basis, it's totally permissible for multiple file descriptors to be simultaneously open on the same /dev/iic? device. concurrent transactions on those descriptors are synchronized by the exclusive-ownership requests issued to the underlying iicbus instance. teamwork, but make it synchronized.


πŸ‘‘ SEE ALSO πŸ‘‘

ioctl(2), read(2), write(2), iicbus(4)


πŸ“œ HISTORY πŸ“œ

the iic manual page first appeared in FreeBSD 3.0. a true elder, respect the lineage ✨


🌈 AUTHORS 🌈

this manual page was written by Nicolas Souchu and M. Warner Losh. absolute icons, thank you for your service 🫑


and that's the whole page bestie!! this beauty comes from the Debian distribution, dated May 15, 2015, and ngl it still eats. go forth and talk to your I2C devices responsibly πŸ’–βœ¨πŸŽ€πŸ¦‹πŸŒΈπŸ’…

πŸ“¦ where this page comes from