I2C Drivers, Part II

Here's what has to happen to read the sensors that report the temperature, fan speed and other important system health information.
What to Do When the Chip Is Found

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.

I2C and sysfs

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);

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

How the adapter driver verifies the specified address

Anonymous's picture

"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"

Note quite understand, how the adapter driver looks for the address specified. How does it know there is a device at the other end bus would have the address, if it does not read/write some thing from the device. But the adapter driver does not know how to read/write to the device.

RE: How the adapter driver verifies the specified address

Anonymous's picture

Same question here

To probe or not to probe...

Marc's picture

kamou:

I'm currently struggling with a similar issue. I'm trying to write a driver for an image sensor which can be controlled via I2C. I'm still trying to wrap my head around how to get a character device and an I2C driver to coexist in a single device driver. I got around the whole SENSORS_INSMOD_1 macro issue by hard coding the I2C address since this is known a priori for my application. Therefore I do not call i2c_detect in the attach_adapter() callback. However, to enable probing, I found that if the normal_i2c, normal_i2c_range, normal_isa, and normal_isa_range macros are populated with proper address data, the invocation of the SENSORS_INSMOD_1 macro will set up the addr_data structure accordingly. The i2c_detect then probes only those addresses in the range specified by these arrays. When one is found the callback provided to i2c_detect (chip_detect in the article's example) is invoked to setup a client for that chip.

My question, however, is there seems to be an m:n ratio of adapters to i2c busses. I'm trying to figure out how to manage that within a single char device driver. I'm currently looking at the i2c-dev implementation, however, this uses the old style of registering character devices which I'm not familiar with so I'm not 100% sure what's going on. Does anyone have any insight and silently using an I2C chip driver from within a char device driver?

trying to write a driver for an audio chip

kamou's picture

hello !

I'm trying to write a driver for an audio chip, this chip can be controlled via i2c bus (i2c-0)

the problem is that I don't understand how I can "open" the i2c-0 in kernel space...
I tried to do as le lm75 sensor driver...but it's not better..
I know that I have to register my driver as an i2c driver with the i2c_add_driver function

does this:

static int
chip_attach_adapter(struct i2c_adapter *adapter)
{
return i2c_detect(adapter, &addr_data,
chip_detect);
}

probes every i2c bus on my board ?
and I don't understand what addr_data is or what it does,
I know it is in the SENSORS_INSMOD_1 macro
but I'm not writing a driver for a sensor, but for an audio chip...

can anyone help me understand better all this ?

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState