The I2C layer (2.4)

References:
http://secure.netroedge.com/~lm78/
Documentation/i2c



I2C (Inter-Integrated Circuit), pronounced "i square c", is a simple two wire bus protocol developed by Philips for intercommunication among integrated circuits. It has become a de-facto standard due to its semplicity and the possibility to manage flexible addressing.
The I2C core layer manages bus adapters, device drivers and device clients. Devices can be on the i2c bus or an attached bus (eg, isa); drivers manages classes of devices, and clients describe individual devices. The I2C core provides the communication protocol(s). It uses a callback mechanims the set up the relations among the components. Callbacks that manage the components relations cannot be NULL, the other callbacks may be NULL if the object does not need to do anything.
Every object (driver, client, adapter, and algorithm) has a name (an descriptive string up to 32 chars), an id (unique identification number), and flags (that define how it should be used). clients and adapters have a private data field which can point to specific structures.
The driver id's in the range 0xf000 to 0xffff are reserved for local use. So one can use one of these to test an I2C driver. As for the flags it is important to define I2C_DF_NOTIFY so that the driver is notified whenever a new adapter is found. Finally the driver's name should be a descriptive string (up to 40 chars).
The driver's inc/dec_use() can just be MOD_INC/DEC_USE_COUNT, so that the driver's code is not unloaded while an application is using it, if the driver is compiled as a module. command() is an ioctl-like interface. attach_adapter() is called by the I2C core when the driver is registered or when a new adapter is inserted. detach_client() is called when a client is removed.
The client represents an individual device. It has a pointer to its driver and to the adapter used for communications. Clients that want to have usage_count managed by I2C core should set the flags I2C_CLIENT_ALLOW_USE, and I2C_CLIENT_ALLOW_MULTIPLE_USE for multiple use. These are checked by i2c_use_client() and i2c_release_client().
The algorithm encompasses the data manipulation of a transfer. functionality() shuold return a flag of supported functionalities (I2C_FUNC_XXX things). master_xfer() is the core transfer function, on which other i/o functions rely.
The adapter describes a bus. It has a pointer to the algorithm used to transfer data, a list of clients, and two methods to allow client registration and unregistration.


The I2C core functions

The object management functions
The bus functions
The smbus functions
The I/O functions are defined in terms of i2c_smbus_xfer():
  • i2c_smbus_xfer(adapter, addr, flags, rw, cmd, size, data) if the adapter's algorithm has smbus_xfer() method use it, otherwise use the i2c_smbus_xfer_emulate() method;
  • i2c_smbus_xfer_emulate(...) emulates the SMBus on the I2C bus. It has an array of two messages (uses 2 for READ, 1 for WRITE). If the size is I2C_SMBUS_QUICK is a zero-length message. Otherwise sets up the message(s) data properly and calls i2c_transfer(adapter, msg, num). In case of read, reads the response back into the data.
  • i2c_smbus_write_quick(client, value)
  • i2c_smbus_read_byte(client)
  • i2c_smbus_write_byte(client, value(;
  • i2c_smbus_read_byte_data(client, cmd);
  • i2c_smbus_write_byte_data(client, cmd, value);
  • i2c_smbus_read_word_data(client, cmd);
  • i2c_smbus_write_word_data(client, cmd, value);
  • i2c_smbus_process_call(client, cmd, value);
  • i2c_smbus_read_block_data(client, cmd, values);
  • i2c_smbus_write_block_data(client, cmd, len, values);
  • i2c_smbus_write_i2c_block_data(client, cmd, len, values);

The I2C device functions

The module i2c-dev.c provides a /dev interface of i2c adapters to user applications. Each i2c adapter receives a number from the I2C core. This number can be seen through the /proc/bus/i2c interface. The I2C adapters are character devices with major 89 and minor that number assigned by the I2C core.
The code i2c-dev.c defines a dummy i2c_driver (named i2cdev_driver) with methods i2cdev_attach_adapter, i2cdev_detach_client, and i2cdev_command, and an i2c_client template (named i2cdev_client_template) with the i2cdev_driver, no adapter, and no private data.
The I2C device operations are (these oerations act on the client which is stored in the file' private_data filed)
  • i2cdev_lseek (from version 2.4.10 only): return -ESPIPE.
  • i2cdev_read: an i2c_master_recv() on the client.
  • i2cdev_write: an i2c_master_send() on the client.
  • i2cdev_ioctl.
  • i2cdev_open, creates a new client (after the i2cdev_client_template) with adapter the i2cdev_adaps indexed by the inode minor. Possibly calls the adapter' inc_use().
  • i2cdev_release: free the file' private_data (the client) and possibly calls the adapter' dec_use().

The ioctl commands are (arg is the ioctl argument)
  • I2C_SLAVE or I2C_SLAVE_FORCE, set the client address to arg;
  • I2C_TENBIT: set (arg != 0) or clear the flag I2C_M_TEN (ten-bit addresses);
  • I2C_FUNCS: get the functionality of the client' adapter;
  • I2C_RDWR: arg should point to an i2c_rdwr_ioctl_data structure. It copies the data from user space, calls i2c_transfer(), and eventually copies the data back to user space.
  • I2C_SMBUS: arg should point to an i2c_smbus_ioctl_data structure. It calls i2c_smbus_xfer(). The i2c_smbus_ioctl_data struct contains a rw flag, a command, the size of the data and the data pointer (of type i2c_smbus_data).
The management functions are
  • i2cdev_attach_adapter: inserts the adapter in the i2cdev_adaps[] list.
  • i2cdev_detach_client: does nothing, returns 0.
  • i2cdev_command: always returns -1.
  • i2cdev_init: registers a char device ("i2c") with i2cdev_fops, and adds to it the dummy driver i2cdev_driver.
  • i2cdev_cleanup: (possibly) unregister the char dev.
The SMbus I/O functions are defined in terms of i2c_smbus_access(file, rw, cmd, size, data), which fills an i2c_smbus_ioctl_data with the arguments and calls ioctl(file, I2C_SMBUS, args) which in turns calls i2c_smbus_xfer() to do the actual i/o.

The I2C proc functions

The I2C proc interface is defined in the header file include/linux/i2c-proc.h. A general callback function type is declared as
   i2c_real_callback( client, op, ctl_name, nrels_mag, results )
where "client" is a pointer to a client device we want to interact with, "op" is an operation flag that can be one of SENSORS_PROC_REAL_INFO/READ/WRITE. Before using the callback function a ctl_table must be registered; ctl_name -s the SYSCTL id of the file being accessed. The function reads or writes real numbers; these are coded as an integer ("results") and a magnitude ("nrels_mag") which is the power of 10 by which the integer must be divided to get the real number (the magnitude can also be negative). For READ/WRITE operations "nrels_mag" contains on return the number of elements read or written.
The structure ctl_table (include/linux/sysctl.h) contains
  • ctl_name: a binary id;
  • procname: string;
  • data: private data pointer, and its "maxlen";
  • mode: protection rights;
  • two callbacks, proc_handler(), for text formatting, and ctl_handler(), for all i/o;
  • a proc_dir_entry pointer;
  • two extra generic (void *) pointers.
Four control tables are defined in drivers/linux/i2c-proc.c (all terminated by a NULL entry),
  • sysctl_table, with CTL_DEV, DEV_SENSORS entries, and a zero entry;
  • i2c_proc_dev_sensors, with SENSORS_CHIPS entry (callbacks: i2c_proc_chips, i2c_sysctl_chips);
  • i2c_proc_dev, with DEV_SENSORS entry (callback: i2c_proc_dev_sensors);
  • i2c_proc, with CTL_DEV entry (callback: i2c_proc_dev).
i2c_register_entry( client, prefix, ctl_table, module) and i2c_deregister_entry( id ) are used to add and remove an entry in the /proc/sys/dev/sensors/chips and a directory in /proc/sys/dev/sensors/. "ctl_table" should be a template for the newly created directory. A new ctl_table is allocated and filled. The second extra field is pointed to "client". The controlling "module" should usually be THIS_MODULE The new table is registered with sysctl ( register_sysctl_table ) and the etries of the lists i2c_entries[] (ctl_table_header), i2c_clients[], i2c_inodes[] are updated. Deregistration is the reversed actions.
i2c_create_name() returns a nice name for a new proc directory.
i2c_fill_inode(inode, fill) increases/decreases the MOD_USE_COUNT, depending on the "fill" parameter. i2c_dir_fill_inode(inode, fill) finds the i2c_client with the given inode number (inode->i_ino), and calls its driver's inc/dec_use().
i2c_proc_chips( ctl_table, rw, filp, buffer, len ) is the proc interface to the chips names. For each non-NULL entry in i2c_entries[] writes the ctl_table->child->child name and procname to the user buffer.
i2c_sysctl_chips(ctl_table, name, len, oldval, oldlen, newval, newlen, context) is the sysctl interface. [TO DO]
i2c_sysctl_real( ctl_table, name, len, oldval, oldlen, newval, newlen, context) and i2c_proc_real( ctl_table, rw, filp, buffer, len) are the functions that perform the i/o. i2c_proc_real() firts gets the magnitude, next does the i/o, which consists of real-long conversion (i2c_parse_reals / i2c_write_reals) and i/o on the client (using the function callback()). i2c_sysctl_real() ... [TO DO]. i2c_parse_reals() and i2c_write_reals() are not discussed.
i2c_detect(adapter, addr_data, found_proc) is an inefficient ISA detect function, similar to i2c_probe(). [TO DO].

The I2C protocol

References:
Philips Semiconductors, "The I2C-Bus Specification?Version 2.1," January 2000, http://www.semiconductors.philips.com/acrobat/literature/9398/39340011.pdf.
Documentation/i2c/i2c-protocol
http://www.esacademy.com/faq/i2c/general/i2cproto.htm



The I2C bus consists of two active bidirectional wires, besides the ground wire. One wire carries the data SDA (Serial Data), the other is for clock SCL (Serial Click Line). Every device on the bus has its own address, and can act as a receiver and a transmitter (although some devices act only as one of the two).
The bus is multi-master: more than one IC capable of initiating a data transfer can be connected to it. The IC that starts the transfer is the bus master, the other become the bus slaves. The master issues a START (pull SDA, then pull SCL) to signal the beginning of a transfer. The end of the transfer will be signalled with a STOP (release SCL, then release SDA ). Then the master sends one byte containing the address of the ather part and a read/write flag: bits are send MSB first, LSB last; the seven most significant bits are the address, the LSB is the flag (0 for WRITE). Bits are sent on the SDA line, one bit for each clock pulse (SCL). Some addresses are reserved:
	0000-000(0)   general call
	0000-000(1)   START byte
	0000-001(x)   C-bus (obsolete)
	0000-010(x)   a different bus
	0000-011(x)   future
	0000-1xx(x)   future
	1111-1xx(x)   future
	1111-0xx(x)   10-bit slave addressing
EXtended address mode (10-bit addresses) has a two-byte address. The first byte contains the two most significant bits of the address and the r/w flag. The second byte contains the other eight bits.
Slaves respond to an address byte that matches and to a data byte (it they are being addressed) with an ACK: pull SDA, and when the master has issued the next SCL clock pulse, release SDA.
To receive a byte from the slave, the master must send eight clock pulses (on SCL) while the slave writes the bits on the SDA (high for 1, low for 0). Before all the master releases tha SDA and the slaves takes control of it. Then the master pulses the SCL; when it is high it read the bit from SDA.


Example

References:
G. Kroah-Hartman, I2C drivers, part I, Linux Journal 115, Dec. 2003, 30-35
G. Kroah-Hartman, I2C drivers, part II, Linux Journal 116, Jan. 2004, ...



Here are some instructions to write a sample adapter and driver for the I2C layer.
Some of the theory behind I2C has been discussed in the introduction. Now we come to a working example, ... something you can do even if you do not have any special i2c device to play with. Of course the first thing to do is to read the kernel code for the I2C core and supported devices.
To be independent from any hardware i decided to write an I2C adapter for a virtual bus that exiest only in memory, and a driver for virtual devices that sit on that bus. I use the I2C core functionalities and structures to manage adapter and devices and the data transfer. The figure below shows the main relations between the I2C core and the driver/adapter callback functions (shown in red).


The i2c_adapter i2c_mem_adpt.c

The adapter i2c_mem_adapter does not have to do anything special; the list of clients[] is already managed by the i2c_core, it must specify that it is using the algorithm i2c_mem_algorithm (about which i will say shortly), and declare an id that is not used by other adapters (so check include/linux/i2c.h). All the other functons may be NULL. Here i put some printk to see when they are invoked.
The algorithm i2c_mem_algorithm implements two important functions. First functionality() that returns a functionality flag that must be chosen so that clients that are not "memory" virtual devices do not get attached to the adapter. I chose a flag that is not already used by the I2C core (again see include/linux/i2c.h). Second master_xfer() must be implemented to code the actual data transfer. If smbus_xfer() is NULL, the algorithm implements a pure "i2c protocol". In the example i did not conecrn with protocol issues and did a simple byte copy (after all everything is in memory, and i am more concerned with the dynamics of the intercations among the I2C layer components).
The adapter i2c_mem_adapter and the algorithm i2c_mem_algorithm are coded in the file i2c_mem_adpt.c. You need also the header file i2c_mem.h.
Compile with the Makefile and insert the module with insmod. You should see the kernel messages that tell that the adapter has registerd with the I2C core:
  i2c-core.o: i2c core module
  i2c_mem version 0001
  i2c-core.o: adapter mem adapter registered as adapter 0.
  i2c_mem_adpt: initialized 1 
Check the /proc/bus/i2c interface. You should find
   i2c-0   smbus/i2c       mem adapter      mem bus algorithm
where the fields are the device name (as assigned by the I2c core), the bus, the adapter name, and its description. /proc/bus/i2c-0 is still empty because there is no client on the adapter.

The i2c_driver i2c_mem_drv.c

Once the memory virtual adapter is in place, you can go on to the memory virtual clients and driver. This driver should manage mamory-based virtual clients, that rely on the adapter i2c_mem_adapter to transfer data.
The driver and the client are coded in the file i2c_mem_drv.c. Compile it with the same makefile, and install it.
After installing check /proc/bus/i2c-0 again and you should see that the client is now there,
  37      i2c_mem_client 0xp001    i2c mem driver
where the fields are the address, the client name, and its driver name.

Accessing the I2C memory client from user space

Our last effort consists in accessing the client from user space. We need the i2c-dev.c module which provides the /dev interface to I2C adapters.
After opening the device, the program should bound to the adapter with an address (not already taken by any client). This is done with the ioctl I2C_SLAVE, with argument the address. Next it can transfer data with the ioctl I2C_RDWR; the argument is a pointer to a i2c_rdwr_ioctl_data structure. This structure contains
  • msgs, a pointer to i2c_msg struct;
  • nmsgs, the number of messagges.
Each i2c_msg struct has the following fields
  • addr (unsigned short) address of the slave;
  • flags (unsigned short), can have I2C_M_TEN, I2C_M_RD, I2C_M_NOSTART, I2C_M_REV_DIR_ADDR;
  • len (short) the message length;
  • buf (char *) pointer to the message data.
i2c_test.c is a simple user application that shows how to read/write to the memory virtual client, through the i2c_mem_adapter.


Marco Corvi - 2003