I2C Drivers, Part I
In the June and August 2003 issues of Linux Journal, my column covered the Linux kernel driver model, and the I2C subsystem was used as an example. This month, we discuss what the I2C subsystem does and how to write a driver for it.
I2C is the name for a two-wire serial bus protocol originally developed by Phillips. It commonly is used in embedded systems so different components can communicate; PC motherboards use I2C to talk to different sensor chips. Those sensors typically report back fan speeds, processor temperatures and a whole raft of system hardware information. The protocol also is used in some RAM chips to report information about the DIMM itself back to the operating system.
The I2C kernel code has lived outside of the main kernel tree for much of its development life-it originally was written back in the 2.0 days. The 2.4 kernel contains a bit of I2C support, mainly for some video drivers. With the 2.6 kernel, a large portion of the I2C code has made it into the main kernel tree, thanks to the effort of a number of kernel developers who changed the interfaces to be more acceptable to the kernel community. A few drivers still live only in the external CVS tree and have not been moved into the main kernel.org tree, but it is only a matter of time before they, too, are ported.
The I2C kernel code is broken up into a number of logical pieces: the I2C core, I2C bus drivers, I2C algorithm drivers and I2C chip drivers. We ignore how the I2C core operates in this article and focus instead on how to write a bus and algorithm driver. In Part II, we will cover how to write an I2C chip driver.
An I2C bus driver is described by a struct named i2c_adapter, which is defined in the include/linux/i2c.h file. Only the following fields need to be set up by the bus driver:
-
struct module *owner; -set to the value (THIS_MODULE) that allows the proper module reference counting.
-
unsigned int class; -the type of I2C class devices that this driver supports. Usually this is set to the value I2C_ADAP_CLASS_SMBUS.
-
struct i2c_algorithm *algo; -a pointer to the struct i2c_algorithm structure that describes the way data is transferred through this I2C bus controller. More information on this structure is provided below.
-
char name[I2C_NAME_SIZE]; -set to a descriptive name of the I2C bus driver. This value shows up in the sysfs filename associated with this I2C adapter.
The code below comes from an example I2C adapter driver called tiny_i2c_adap.c, available from the Linux Journal FTP site [ftp.ssc.com/pub/lj/listings/issue116/7136.tgz [1]] and shows how the struct i2c_adapter is set up:
static struct i2c_adapter tiny_adapter = {
.owner = THIS_MODULE,
.class = I2C_ADAP_CLASS_SMBUS,
.algo = &tiny_algorithm,
.name = "tiny adapter",
};
retval = i2c_add_adapter(&tiny_adapter);
/* set up sysfs linkage to our parent device */
piix4_adapter.dev.parent = &dev->dev;
$ tree /sys/devices/legacy/
/sys/devices/legacy/
|-- detach_state
|-- floppy0
| |-- detach_state
| `-- power
| `-- state
|-- i2c-0
| |-- detach_state
| |-- name
| `-- power
| `-- state
`-- power
`-- state
$ tree /sys/class/i2c-adapter/
/sys/class/i2c-adapter/
`-- i2c-0
|-- device -> ../../../devices/legacy/i2c-0
`-- driver -> ../../../bus/i2c/drivers/i2c_adapter
i2c_del_adapter(&tiny_adapter);
An I2C algorithm is used by the I2C bus driver to talk to the I2C bus. Most I2C bus drivers define their own I2C algorithms and use them, as they are tied closely to how the bus driver talks to that specific type of hardware. For some classes of I2C bus drivers, a number of I2C algorithm drivers already have been written. Examples of these are ITE adapters found in drivers/i2c/i2c-algo-ite.c, IBM PPC 405 adapters found in drivers/i2c/i2c-algo-ibm_ocp.c and a generic I2C bit shift algorithm found in drivers/i2c/i2c-algo-bit.c. All of these already written algorithms have their own functions with which an I2C bus driver needs to register to use. For more information on these, please see all of the drivers/i2c/i2c-algo-*.c files in the kernel tree.
For our example driver, we are going to create our own I2C algorithm driver. An algorithm driver is defined by a struct i2c_algorithm structure and is defined in the include/linux/i2c.h file. Here is a description of some of the commonly used fields:
-
char name[32];: the name of the algorithm.
-
unsigned int id;: description of the type of algorithm this structure defines. These different types are defined in the include/linux/i2c-id.h file and start with the characters I2C_ALGO_.
-
int (*master_xfer)(struct i2c_adapter *adap,struct i2c_msg msgs[], int num);: a function pointer to be set if this algorithm driver can do I2C direct-level accesses. If it is set, this function is called whenever an I2C chip driver wants to communicate with the chip device. If it is set to NULL, the smbus_xfer function is used instead.
-
int (*smbus_xfer) (struct i2c_adapter *adap, u16 addr, unsigned short flags, char read_write, u8 command, int size, union i2c_smbus_data *data);: a function pointer to be set if this algorithm driver can do SMB bus accesses. Most PCI-based I2C bus drivers are able to do this, and they should set this function pointer. If it is set, this function is called whenever an I2C chip driver wants to communicate with the chip device. If it is set to NULL, the master_xfer function is used instead.
-
u32 (*functionality) (struct i2c_adapter *);: a function pointer called by the I2C core to determine what kind of reads and writes the I2C adapter driver can do.
In our example I2C adapter driver, the i2c_adapter structure referenced the tiny_algorithm variable. That structure is defined as the following:
static struct i2c_algorithm tiny_algorithm = {
.name = "tiny algorithm",
.id = I2C_ALGO_SMBUS,
.smbus_xfer = tiny_access,
.functionality = tiny_func,
};
The tiny_func function is small and tells the I2C core what types of I2C messages this algorithm can support. For this driver, we want to be able to support a few different I2C message types:
static u32 tiny_func(struct i2c_adapter *adapter)
{
return I2C_FUNC_SMBUS_QUICK |
I2C_FUNC_SMBUS_BYTE |
I2C_FUNC_SMBUS_BYTE_DATA |
I2C_FUNC_SMBUS_WORD_DATA |
I2C_FUNC_SMBUS_BLOCK_DATA;
}
All of the different I2C message types are defined in include/linux/i2c.h and start with the characters I2C_FUNC_.
The tiny_access function is called when an I2C client driver wants to talk to the I2C bus. Our example function is quite simple; it merely logs all of the requests the I2C chip driver makes to the syslog and reports success back to the caller. This log allows you to see all of the different addresses and data types that an I2C chip driver may request. The implementation looks like: Garrick, please kern the double underscores in the code below.
static s32 tiny_access(struct i2c_adapter *adap,
u16 addr,
unsigned short flags,
char read_write,
u8 command,
int size,
union i2c_smbus_data *data)
{
int i, len;
dev_info(&adap->dev, "%s was called with the "
"following parameters:\n",
__FUNCTION__);
dev_info(&adap->dev, "addr = %.4x\n", addr);
dev_info(&adap->dev, "flags = %.4x\n", flags);
dev_info(&adap->dev, "read_write = %s\n",
read_write == I2C_SMBUS_WRITE ?
"write" : "read");
dev_info(&adap->dev, "command = %d\n",
command);
switch (size) {
case I2C_SMBUS_PROC_CALL:
dev_info(&adap->dev,
"size = I2C_SMBUS_PROC_CALL\n");
break;
case I2C_SMBUS_QUICK:
dev_info(&adap->dev,
"size = I2C_SMBUS_QUICK\n");
break;
case I2C_SMBUS_BYTE:
dev_info(&adap->dev,
"size = I2C_SMBUS_BYTE\n");
break;
case I2C_SMBUS_BYTE_DATA:
dev_info(&adap->dev,
"size = I2C_SMBUS_BYTE_DATA\n");
if (read_write == I2C_SMBUS_WRITE)
dev_info(&adap->dev,
"data = %.2x\n", data->byte);
break;
case I2C_SMBUS_WORD_DATA:
dev_info(&adap->dev,
"size = I2C_SMBUS_WORD_DATA\n");
if (read_write == I2C_SMBUS_WRITE)
dev_info(&adap->dev,
"data = %.4x\n", data->word);
break;
case I2C_SMBUS_BLOCK_DATA:
dev_info(&adap->dev,
"size = I2C_SMBUS_BLOCK_DATA\n");
if (read_write == I2C_SMBUS_WRITE) {
dev_info(&adap->dev, "data = %.4x\n",
data->word);
len = data->block[0];
if (len < 0)
len = 0;
if (len > 32)
len = 32;
for (i = 1; i <= len; i++)
dev_info(&adap->dev,
"data->block[%d] = %x\n",
i, data->block[i]);
}
break;
}
return 0;
}
Now that the tiny_i2c_adap driver is built and loaded, what can it do? On its own, it cannot do anything. An I2C bus driver needs an I2C client driver in order to do anything besides sit in the sysfs tree. So, if the lm75 I2C client driver is loaded, it tries to use the tiny_i2c_adap driver to find the chip for which it was written: Garrick, please use a small font so these lines don't have to break.
$ modprobe lm75
$ tree /sys/bus/i2c/
/sys/bus/i2c/
|-- devices
| |-- 0-0048 -> ../../../devices/legacy/i2c-0/0-0048
| |-- 0-0049 -> ../../../devices/legacy/i2c-0/0-0049
| |-- 0-004a -> ../../../devices/legacy/i2c-0/0-004a
| |-- 0-004b -> ../../../devices/legacy/i2c-0/0-004b
| |-- 0-004c -> ../../../devices/legacy/i2c-0/0-004c
| |-- 0-004d -> ../../../devices/legacy/i2c-0/0-004d
| |-- 0-004e -> ../../../devices/legacy/i2c-0/0-004e
| `-- 0-004f -> ../../../devices/legacy/i2c-0/0-004f
`-- drivers
|-- i2c_adapter
`-- lm75
|-- 0-0048 -> ../../../../devices/legacy/i2c-0/0-0048
|-- 0-0049 -> ../../../../devices/legacy/i2c-0/0-0049
|-- 0-004a -> ../../../../devices/legacy/i2c-0/0-004a
|-- 0-004b -> ../../../../devices/legacy/i2c-0/0-004b
|-- 0-004c -> ../../../../devices/legacy/i2c-0/0-004c
|-- 0-004d -> ../../../../devices/legacy/i2c-0/0-004d
|-- 0-004e -> ../../../../devices/legacy/i2c-0/0-004e
`-- 0-004f -> ../../../../devices/legacy/i2c-0/0-004f
Because the tiny_i2c_adap driver responds with a success to every read and write request it is asked to accomplish, the lm75 I2C chip driver thinks it has found an lm75 chip at every known possible I2C address for this chip. This abundance of addresses is why I2C devices 0-0048 through 0-004f have been created. If we look at the directory for one of these devices, the sensor files for this chip driver are shown:
$ tree /sys/devices/legacy/i2c-0/0-0048/
/sys/devices/legacy/i2c-0/0-0048/
|-- detach_state
|-- name
|-- power
| `-- state
|-- temp_input
|-- temp_max
`-- temp_min
The detach_state file and power directory is created by the kernel driver core and is used for power management. It is not created by the lm75 driver. The functions of the other files in this directory are described below.
If we ask the lm75 driver for the current value of temp_max, we receive the following:
$ cat /sys/devices/legacy/i2c-0/0-0048/temp_max
1000
To get that value, the lm75 driver asked the tiny_i2c_adap driver to read some addresses on the I2C bus. This request is shown in the syslog: Garrick, please use a really small font so this will fit without breaking the lines.
$ dmesg
i2c_adapter i2c-0: tiny_access was called with the following parameters:
i2c_adapter i2c-0: addr = 0048
i2c_adapter i2c-0: flags = 0000
i2c_adapter i2c-0: read_write = read
i2c_adapter i2c-0: command = 0
i2c_adapter i2c-0: size = I2C_SMBUS_WORD_DATA
i2c_adapter i2c-0: tiny_access was called with the following parameters:
i2c_adapter i2c-0: addr = 0048
i2c_adapter i2c-0: flags = 0000
i2c_adapter i2c-0: read_write = read
i2c_adapter i2c-0: command = 3
i2c_adapter i2c-0: size = I2C_SMBUS_WORD_DATA
i2c_adapter i2c-0: tiny_access was called with the following parameters:
i2c_adapter i2c-0: addr = 0048
i2c_adapter i2c-0: flags = 0000
i2c_adapter i2c-0: read_write = read
i2c_adapter i2c-0: command = 2
i2c_adapter i2c-0: size = I2C_SMBUS_WORD_DATA
The log file shows that the tiny_access function was called three times. The first command wanted to read a word of data from register 0 out of the device with the address 0048. The second and third reads asked for register 3 and register 2 from the same device. The commands match up with the following code from the drivers/i2c/chips/lm75.c file in the lm75_update_client function:
data->temp_input = lm75_read_value(client,
LM75_REG_TEMP);
data->temp_max = lm75_read_value(client,
LM75_REG_TEMP_OS);
data->temp_hyst = lm75_read_value(client,
LM75_REG_TEMP_HYST);
The lm75_read_value function in that same file contains the following code:
/* All registers are word-sized, except for the
configuration register. LM75 uses a high-byte
first convention, which is exactly opposite to
the usual practice. */
static int lm75_read_value(struct i2c_client
*client, u8 reg)
{
if (reg == LM75_REG_CONF)
return i2c_smbus_read_byte_data(client,
reg);
else
return swap_bytes(
i2c_smbus_read_word_data(client,
reg));
}
Therefore, when the lm75 driver wants to read the value of the max temperature, it calls the lm75_read_value function with the register number, which then calls the I2C core function i2c_smbus_read_word_data. That I2C core function looks up on which I2C bus the client device is, and then it calls the I2C algorithm associated with that specific I2C bus to do the data transfer. This is the method, then, by which our tiny_i2c_adap driver is asked to complete the transfer.
If this same sysfs file is written to, the lm75 driver asks the tiny_i2c_adap driver to write some data to a specific address on the I2C bus in the same way the read was requested. This request also is shown in the syslog: Garrick, again, use small font below.
$ echo 300 > /sys/devices/legacy/i2c-0/0-0048/temp_max
$ dmesg
i2c_adapter i2c-0: tiny_access was called with the following parameters:
i2c_adapter i2c-0: addr = 0048
i2c_adapter i2c-0: flags = 0000
i2c_adapter i2c-0: read_write = write
i2c_adapter i2c-0: command = 3
i2c_adapter i2c-0: size = I2C_SMBUS_WORD_DATA
i2c_adapter i2c-0: data = 8000
This month we covered the basics of the I2C driver subsystem and explained how to write a simple I2C bus and I2C algorithm driver that work with any existing I2C client driver. The complete driver, dmn-09-i2c-adap.c, is available from the Linux Journal FTP site at ftp.ssc.com/pub/lj/listings/issue116/7136.tgz [2]. In Part II, we will cover how to write an I2C chip driver.
Greg Kroah-Hartman currently is the Linux kernel maintainer for a variety of different driver subsystems. He works for IBM, doing Linux kernel-related things and can be reached at greg@kroah.com [3]. 7136aa.tif
I2C Drivers, Part II
In my last column [LJ, December 2003], we discussed how I2C bus drivers and I2C algorithm drivers work. We also described how to make a tiny dummy I2C bus driver. This month, we discuss how an I2C chip driver works and provide an example of one in action.
An I2C chip driver controls the process of talking to an individual I2C device that lives on an I2C bus. I2C chip devices usually monitor a number of different physical devices on a motherboard, such as the different fan speeds, temperature values and voltages.
The struct i2c_driver structure describes a I2C chip driver. This structure is defined in the include/linux/i2c.h file. Only the following fields are necessary to create a working chip driver:
-
struct module *owner; - set to the value THIS_MODULE that allows the proper module reference counting.
-
char name[I2C_NAME_SIZE]; - set to a descriptive name of the I2C chip driver. This value shows up in the sysfs file name created for every I2C chip device.
-
unsigned int flags; - set to the value I2C_DF_NOTIFY in order for the chip driver to be notified of any new I2C devices loaded after this driver is loaded. This field probably will go away soon, as almost all drivers set this field.
-
int (*attach_adapter)(struct i2c_adapter *); - called whenever a new I2C bus driver is loaded in the system. This function is described in more detail below.
-
int (*detach_client)(struct i2c_client *); - called when the i2c_client device is to be removed from the system. More information about this function is provided below.
The following code is from an example I2C chip driver called tiny_i2c_chip.c., which is available from the Linux Journal FTP site [ftp.ssc.com/pub/lj/listings/issue118/7252.tgz [1]]. It shows how the struct i2c_driver structure is set up:
static struct i2c_driver chip_driver = {
.owner = THIS_MODULE,
.name = "tiny_chip",
.flags = I2C_DF_NOTIFY,
.attach_adapter = chip_attach_adapter,
.detach_client = chip_detach_client,
};
To register this I2C chip driver, the function i2c_add_driver should be called with a pointer to the struct i2c_driver:
static int __init tiny_init(void)
{
return i2c_add_driver(&chip_driver);
}
To unregister the I2C chip driver, the i2c_del_driver function should be called with the same pointer to the struct i2c_driver:
static void __exit tiny_exit(void)
{
i2c_del_driver(&chip_driver);
}
After the I2C chip driver is registered, the attach_adapter function callback is called when an I2C bus driver is loaded. This function checks to see if any I2C devices are on this I2C bus to which the client driver wants to attach. Almost all I2C chip drivers call the core I2C function i2c_detect to determine this. For example, the tiny_i2c_chip.c driver does this:
static int
chip_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data,
chip_detect);
}
The i2c_detect function probes the I2C adapter, looking for the different addresses specified in the addr_data structure. If a device is found, the chip_detect function then is called.
If you look closely at the source code, you cannot find the addr_data structure anywhere. The reason for this is it is created by the SENSORS_INSMOD_1 macro. This macro is defined in the include/linux/i2c-sensor.h file and is quite complicated. It sets up a static variable called addr_data based on the number of different types of chips that this driver supports and the addresses at which these chips typically are present. It then provides the ability to override these values by using module parameters. An I2C chip driver must provide the variables normal_i2c, normal_i2c_range, normal_isa and normal_isa_range. These variables define the i2c smbus and i2c isa addresses this chip driver supports. They are an array of addresses, all terminated by either the special value I2C_CLIENT_END or I2C_CLIENT_ISA_END. Usually a specific type of I2C chip shows up in only a limited range of addresses. The tiny_i2c_client.c driver defines these variables as:
static unsigned short normal_i2c[] =
{ I2C_CLIENT_END };
static unsigned short normal_i2c_range[] =
{ 0x00, 0xff, I2C_CLIENT_END };
static unsigned int normal_isa[] =
{ I2C_CLIENT_ISA_END };
static unsigned int normal_isa_range[] =
{ I2C_CLIENT_ISA_END };
The normal_i2c_range variable specifies that we can find this chip device at any I2C smbus address. This allows us to test this driver on almost any I2C bus driver.
In the tiny_i2c_chip.c driver, when an I2C chip device is found, the function chip_detect is called by the I2C core. This function is declared with the following parameters:
static int
chip_detect(struct i2c_adapter *adapter,
int address, int kind);
The adapter variable is the I2C adapter structure on which this chip is located. The address variable contains the address where the chip was found, and the kind variable indicates what kind of chip was found. The kind variable usually is ignored, but some I2C chip drivers support different kinds of I2C chips, so this variable can be used to determine the type of chip present.
This function is responsible for creating a struct i2c_client structure that then is registered with the I2C core. The I2C core uses that structure as an individual I2C chip device. To create this structure, the chip_detect function does the following:
struct i2c_client *new_client = NULL;
struct chip_data *data = NULL;
int err = 0;
new_client = kmalloc(sizeof(*new_client),
GFP_KERNEL);
if (!new_client) {
err = -ENOMEM;
goto error;
}
memset(new_client, 0x00, sizeof(*new_client));
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data) {
err = -ENOMEM;
goto error;
}
memset(data, 0x00, sizeof(*data));
i2c_set_clientdata(new_client, data);
new_client->addr = address;
new_client->adapter = adapter;
new_client->driver = &chip_driver;
new_client->flags = 0;
strncpy(new_client->name, "tiny_chip",
I2C_NAME_SIZE);
First, the struct i2c_client structure and a separate local data structure (called struct chip_data) are created and initialized to zero. It is important that the i2c_client structure is initialized to zero, as the lower levels of the kernel driver core require this in order to work properly. After the memory is allocated successfully, some fields in the struct i2c_client are set to point to this specific device and this specific driver. Notably, the addr, adapter and driver variables must be initialized. The name of the struct i2c_client also must be set if it is to show up properly in the sysfs tree for this I2C device.
After the struct i2c_client structure is initialized, it must be registered with the I2C core. This is done with a call to the i2c_attach_client function:
/* Tell the I2C layer a new client has arrived */
err = i2c_attach_client(new_client);
if (err)
goto error;
When this function returns, with no errors reported, the I2C chip device is set up properly in the kernel.
In the 2.0, 2.2 and 2.4 kernels, the I2C code would place the I2C chip devices in the /proc/bus/i2c directory. In the 2.6 kernel, all I2C chip devices and adapters show up in the sysfs filesystem. I2C chip devices can be found at /sys/bus/i2c/devices, listed by their adapter address and chip address. For example, the tiny_i2c_chip driver loaded on a machine might produce the following sysfs tree structure:
$ tree /sys/bus/i2c/
/sys/bus/i2c/
|-- devices
| |-- 0-0009 -> ../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-0009
| |-- 0-000a -> ../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-000a
| |-- 0-000b -> ../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-000b
| `-- 0-0019 -> ../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-0019
`-- drivers
|-- i2c_adapter
`-- tiny_chip
|-- 0-0009 -> ../../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-0009
|-- 0-000a -> ../../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-000a
|-- 0-000b -> ../../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-000b
`-- 0-0019 -> ../../../../devices/pci0000:00/0000:00:06.0/i2c-0/0-0019
This shows four different I2C chip devices, all controlled by the same tiny_chip driver. The controlling driver can be located by looking at the devices in the /sys/bus/i2c/drivers directory or by looking in the directory of the chip device itself and reading the name file:
$ cat /sys/devices/pci0000\:00/0000\:00\:06.0/i2c-0/0-0009/name
tiny_chip
All I2C chip drivers export the different sensor values through sysfs files within the I2C chip device directory. These filenames are standardized, along with the units in which the values are expressed, and are documented within the kernel tree in the file Documentation/i2c/sysfs-interface (Table 1).
Table 1. Sensor Values Exported through sysfs Files
temp_max[1-3] | Temperature max value. Fixed point value in form XXXXX and should be divided by 1,000 to get degrees Celsius. Read/Write value. |
temp_min[1-3] | Temperature min or hysteresis value. Fixed point value in form XXXXX and should be divided by 1,000 to get degrees Celsius. This is preferably a hysteresis value, reported as an absolute temperature, not a delta from the max value. Read/Write value. |
temp_input[1-3] | Temperature input value. Read-only value. |
As the information in Table 1 shows, there is only one value per file. All files are readable and some can be written to by users with the proper privileges.
The tiny_i2c_chip.c driver emulates an I2C chip device that can report temperature values. It creates the files, temp_max1, temp_min1 and temp_input1 in sysfs. The values it returns when these files are read from is incremented every time the file is read to show how to access different unique chip values.
In order to create a file in sysfs, the DEVICE_ATTR macro is used:
static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO,
show_temp_max, set_temp_max);
static DEVICE_ATTR(temp_min, S_IWUSR | S_IRUGO,
show_temp_hyst, set_temp_hyst);
static DEVICE_ATTR(temp_input, S_IRUGO,
show_temp_input, NULL);
This macro creates a structure that then is passed to the function device_create_file at the end of the chip_detect function:
/* Register sysfs files */
device_create_file(&new_client->dev,
&dev_attr_temp_max);
device_create_file(&new_client->dev,
&dev_attr_temp_min);
device_create_file(&new_client->dev,
&dev_attr_temp_input);
That call creates the sysfs files for the device:
/sys/devices/pci0000:00/0000:00:06.0/i2c-0/0-0009
|-- detach_state
|-- name
|-- power
| `-- state
|-- temp_input
|-- temp_max
`-- temp_min
The file name is created by the I2C core, and the files detach_state and power/state are created by the driver core.
But, let's go back to the DEVICE_ATTR macro. That macro wants to know the name of the file to be created, the mode of the file to be created, the name of the function to be called when the file is read from and the name of the function to be called when the file is written to. For the file temp_max, this declaration was:
static DEVICE_ATTR(temp_max, S_IWUSR | S_IRUGO,
show_temp_max, set_temp_max);
The function called when the file is read from is show_temp_max. This is defined, as are many sysfs files, with another macro that creates a function:
#define show(value) \
static ssize_t \
show_##value(struct device *dev, char *buf) \
{ \
struct i2c_client *client = to_i2c_client(dev);\
struct chip_data *data = \
i2c_get_clientdata(client); \
\
chip_update_client(client); \
return sprintf(buf, "%d\n", data->value); \
}
show(temp_max);
show(temp_hyst);
show(temp_input);
The reason this function is created with a macro is that it is quite simple to create other sysfs files that do almost the same thing, with different names and that read from different variables, without having to duplicate code. This single macro creates three different functions to read from three different variables from the struct chip_data structure.
In this function, the struct device * is converted into a struct i2c_client *. Then the private struct chip_data * is obtained from the struct i2c_client *. After that the chip data is updated with a call to chip_update_client. From there, the variable that has been asked for is printed into a buffer and returned to the driver core, which then returns it to the user:
$ cat /sys/devices/pci0000:00/0000:00:06.0/i2c-0/0-0009/temp_input
1
The chip_update_client increments all values by one every time it is called:
static void
chip_update_client(struct i2c_client *client)
{
struct chip_data *data =
i2c_get_clientdata(client);
down(&data->update_lock);
dev_dbg(&client->dev, "%s\n", __FUNCTION__);
++data->temp_input;
++data->temp_max;
++data->temp_hyst;
data->last_updated = jiffies;
data->valid = 1;
up(&data->update_lock);
}
So, all subsequent requests for this value are different:
$ cat /sys/devices/pci0000:00/0000:00:06.0/i2c-0/0-0009/temp_input
2
$ cat /sys/devices/pci0000:00/0000:00:06.0/i2c-0/0-0009/temp_input
3
The set_temp_max function also is created from a macro to allow variables to be written to:
#define set(value, reg) \
static ssize_t \
set_##value(struct device *dev, \
const char *buf, size_t count) \
{ \
struct i2c_client *client = to_i2c_client(dev);\
struct chip_data *data = \
i2c_get_clientdata(client); \
int temp = simple_strtoul(buf, NULL, 10); \
\
down(&data->update_lock); \
data->value = temp; \
up(&data->update_lock); \
return count; \
}
set(temp_max, REG_TEMP_OS);
set(temp_hyst, REG_TEMP_HYST);
Just like the show functions, this function converts the struct device * to a struct i2c_client *, and then the private struct chip_data * is found. The data the user provides then is turned into a number with a call to simple_strtoul and is saved into the proper variable:
$ cat /sys/devices/pci0000:00/0000:00:06.0/i2c-0/0-0009/temp_max
1
$ echo 41 > /sys/devices/pci0000:00/0000:00:06.0/i2c-0/0-0009/temp_max
$ cat /sys/devices/pci0000:00/0000:00:06.0/i2c-0/0-0009/temp_max
42
When the I2C chip device is removed from the system, either by the I2C bus driver being unloaded or by the I2C chip driver being unloaded, the I2C core calls the detatch_client function specified in the struct i2c_driver structure. This usually is a simple function, as can be seen in the example driver's implementation:
static int chip_detach_client(struct i2c_client *client)
{
struct chip_data *data = i2c_get_clientdata(client);
int err;
err = i2c_detach_client(client);
if (err) {
dev_err(&client->dev,
"Client deregistration failed, "
"client not detached.\n");
return err;
}
kfree(client);
kfree(data);
return 0;
}
As the i2c_attach_client function was called to register the struct i2c_client structure with the I2C core, the i2c_detach_client function must be called to unregister it. If that function succeeds, the memory the driver has allocated for the I2C device then needs to be freed before returning from the function.
This example driver does not specifically remove the sysfs files from the sysfs core. This step is done automatically in the driver core within the i2c_detach_client function. But if the author would like, the file can be removed manually by a call to device_remove_file.
This two-part series of articles has explained the basics of how to write a kernel I2C bus driver, I2C algorithm driver and I2C chip driver. A lot of good information on how to write I2C drivers can be found in the Documentation/i2c directory in the kernel tree and on the Lm_sensors Web site (secure.netroedge.com/~lm78 [2]).
Greg Kroah-Hartman currently is the Linux kernel maintainer for a variety of different driver subsystems. He works for IBM, doing Linux kernel-related things, and can be reached at greg@kroah.com [3]. 7252aa.tif
Links
[1] http://www.linuxjournal.com/[2] http://www.linuxjournal.com/
[3] http://www.linuxjournal.com/
Links
[1] http://www.linuxjournal.com/[2] ftp://ftp.ssc.com/pub/lj/listings/issue116/7136.tgz
[3] http://www.linuxjournal.com/
没有评论:
发表评论