Before analyzing the driver, we will analyze the model of the IIC subsystem. There are 2 ways to IIC device drivers, one of which is to write user-driven through a universal driver. The other is to add a IIC device driver directly to the IIC subsystem, such as a driver for AT24C02.
Next we'll learn how to write a IIC device driver.
1. Driver AnalysisLet's first open a file called at24.c in the Linux kernel code, as long as it's
Devices that are part of the AT24 can use this drive. We'll analyze this drive next.
/*-------------------------------------------------------------------------/
static struct I2c_driver at24_ Driver = {
. Driver = {.
name = ' At24 ',
. Owner = This_module,
},.
probe = At24_probe,
. remove = __d Evexit_p (At24_remove),
. id_table = At24_ids,
};
static int __init at24_init (void)
{
io_limit = Rounddown_pow_of_two (io_limit);
Return I2c_add_driver (&at24_driver);
}
Module_init (at24_init);
static void __exit at24_exit (void)
{
i2c_del_driver (&at24_driver);
}
Module_exit (At24_exit);
The initialization function is primarily registered with a I2C device driver. We analyze (&at24_driver), the members of the inside, the more important members have 2, one is the probe function, the other is At24_ids, which holds the ID list of support AT24 devices, such as AT24C02,AT24C08, etc. Interested can look at this table.
We then analyze the probe function. This function is very long, if you want to read every line of code is certainly very difficult, we should be the most basic grasp of the function of the approximate flow and center. such as registering something, creating something, initializing something.
/*-------------------------------------------------------------------------*/static int at24_probe (struct i2c_
Client *client, const struct i2c_device_id *id) {struct at24_platform_data;
BOOL writable;
BOOL Use_smbus = false;
struct At24_data *at24;
int err;
unsigned i, num_addresses;
Kernel_ulong_t Magic;
if (client->dev.platform_data) {chip = * (struct At24_platform_data *) client->dev.platform_data;
else {if (!id->driver_data) {err =-enodev;
Goto Err_out;
} Magic = id->driver_data;
Chip.byte_len = BIT (Magic & At24_bitmask (At24_size_bytelen));
Magic >>= At24_size_bytelen;
Chip.flags = Magic & At24_bitmask (at24_size_flags); * * * is slow, but we can ' t know all eeproms, so we better * play safe.
Specifying custom Eeprom-types via Platform_data * is recommended anyhow.
* * chip.page_size = 1;
Chip.setup = NULL;
Chip.context = NULL; } if (!is_power_of_2 (Chip.byte_len)) Dev_warn (&client->d ev, "Byte_len looks suspicious (no power of 2)!\n");
if (!is_power_of_2 (chip.page_size)) Dev_warn (&client->dev, Page_size looks suspicious (no power of 2)!\n "); /* Use I2C operations unless we ' re stuck with SMBus extensions. */if (!i2c_check_functionality (Client->adapter, I2C_FUNC_I2C)) {if (Chip.flags & at24_flag_addr16) {err =
-epfnosupport;
Goto Err_out;
} if (!i2c_check_functionality (Client->adapter, I2c_func_smbus_read_i2c_block)) {err =-epfnosupport;
Goto Err_out;
} Use_smbus = true;
} if (Chip.flags & at24_flag_take8addr) num_addresses = 8;
else num_addresses = Div_round_up (Chip.byte_len, (Chip.flags & at24_flag_addr16)? 65,536:256);
At24 = kzalloc (sizeof (struct at24_data) + num_addresses * sizeof (struct i2c_client *), Gfp_kernel);
if (!at24) {err =-enomem;
Goto Err_out;
} mutex_init (&at24->lock);
At24->use_smbus = Use_smbus;
At24->chip = chip; At24->num_addresses = num_addresses;
* * Export the EEPROM bytes through SYSFS, since that ' s convenient.
* By default, only root should the data (maybe passwords etc) * * At24->bin.attr.name = "EEPROM"; At24->bin.attr.mode = chip.flags & At24_flag_irugo?
S_IRUGO:S_IRUSR;
At24->bin.read = At24_bin_read;
At24->bin.size = Chip.byte_len;
At24->macc.read = At24_macc_read; writable =!
(Chip.flags & At24_flag_readonly);
if (writable) {if (!use_smbus | | i2c_check_functionality (client->adapter, I2c_func_smbus_write_i2c_block)) {
unsigned write_max = chip.page_size;
At24->macc.write = At24_macc_write;
At24->bin.write = At24_bin_write;
At24->bin.attr.mode |= s_iwusr;
if (Write_max > Io_limit) write_max = Io_limit;
if (Use_smbus && write_max > I2c_smbus_block_max) write_max = I2c_smbus_block_max;
At24->write_max = Write_max; /* Buffer (data + address at the beginning) * * At24->writebuf = KmalLOC (Write_max + 2, gfp_kernel);
if (!at24->writebuf) {err =-enomem;
Goto err_struct;
} else {Dev_warn (&client->dev, "cannot write due to controller restrictions.");
} At24->client[0] = client; /* Use dummy devices for multiple-address chips * * for (i = 1; i < num_addresses; i++) {At24->client[i] = I2c_ne
W_dummy (Client->adapter, client->addr + i);
if (!at24->client[i]) {Dev_err (&client->dev, "Address 0x%02x unavailable\n", client->addr + i);
err =-eaddrinuse;
Goto err_clients;
} err = Sysfs_create_bin_file (&client->dev.kobj, &at24->bin);
if (err) goto err_clients;
I2c_set_clientdata (client, At24); Dev_info (&client->dev, "%zu byte%s EEPROM%s\n", At24->bin.size, Client->name, writable?
"(writable)": "(read-only)"); dev_dbg (&client->dev, "page_size%d, num_addresses%d, Write_max%d%s\n", Chip.page_size, Num_addresses, At24 -; Write_max, Use_smbus?
", Use_smbus": "");
/* Export data to Kernel code */if (chip.setup) Chip.setup (&AT24->MACC, Chip.context);
return 0; Err_clients:for (i = 1; i < num_addresses i++) if (At24->client[i]) I2c_unregister_device (at24->client[i)
);
Kfree (AT24->WRITEBUF);
Err_struct:kfree (AT24);
err_out:dev_dbg (&client->dev, "Probe error%d\n", err);
return err;
}
There seems to be nothing registered here, so there is no creation of something. You can see that he created a file under sys err = Sysfs_create_bin_file (&client->dev.kobj, &at24->bin); What does this file contain? To view the initialization of the at24->bin before, you can know that this file is called EEPROM, at the same time, we can guess, when we use this device file to read and write EEPROM need to use the file to implement the function of read and write operations,
View the code we can also know that these 2 functions are at24_bin_read and at24_bin_write respectively. We will then analyze these 2 functions.
First to analyze the Write function At24_bin_write, this function call At24_write, and then call the At24_eeprom_write function, implementation of the EEPROM write operation, we now analyze This function:
/* * Note this if the hardware Write-protect pin is pulled high, the whole * chip is normally write protect Ed.
But There are plenty of product * variants here, including OTP fuses and partial chip. * * We use page mode writes; The alternative is sloooow.
This is routine * writes at most one page. */static ssize_t at24_eeprom_write (struct at24_data *at24, const char *buf, unsigned offset, size_t count) {str
UCT i2c_client *client;
struct i2c_msg msg;
ssize_t status;
unsigned long timeout, write_time;
unsigned next_page;
/* Get corresponding I2C address and adjust offset */client = At24_translate_offset (At24, &offset);
/* Write_max is at most a page */if (Count > At24->write_max) Count = at24->write_max;
/* Never roll over backwards, to the "start of this page * * * next_page = Roundup (offset + 1, at24->chip.page_size);
if (offset + count > Next_page) count = Next_page-offset; /* If we ' lL Use I2C calls for I/O, set up the message */if (!at24->use_smbus) {int I = 0;
MSG.ADDR = client->addr;
msg.flags = 0;
/* Msg.buf is U8 and casts would mask the values * * * * msg.buf = at24->writebuf;
if (At24->chip.flags & at24_flag_addr16) msg.buf[i++] = offset >> 8;
msg.buf[i++] = offset;
memcpy (&msg.buf[i], buf, count);
Msg.len = i + count; /* * Writes fail if the previous one didn ' t complete yet.
We could * loop a few times until this one succeeds, waiting at least * long enough for one entire page write to work.
* * Timeout = jiffies + msecs_to_jiffies (write_timeout);
do {write_time = jiffies;
if (at24->use_smbus) {status = I2c_smbus_write_i2c_block_data (client, offset, count, buf);
if (status = = 0) status = Count;
else {status = I2c_transfer (Client->adapter, &msg, 1);
if (status = = 1) status = Count;
} dev_dbg (&client->dev, "Write%zu@%d-->%zd (%ld) \ n", Count, offset, status, Jiffies);
if (status = = count) return count;
/* Revisit:at hz=100, this is sloooow * * MSLEEP (1);
while (Time_before (Write_time, timeout));
Return-etimedout;
}
The brief analysis can know, it mainly did 2 things, one is constructs the MSG, another thing is uses the I2c_transfer function to transmit the data, the analysis in the previous section can know, I2C device driver if must read and write the data, all is passes through the i2c_ Transfer it to the I2C bus driver, or I2C controller driver. Corresponding to the construction of MSG we have made it very clear in the last lesson, for the I2c_transfer function, it belongs to the function inside the I2c-core, see his code can know that he did not do any substantive operations, but call the I2C adapter inside the read and write method to achieve data transmission ret = Adap->algo->master_xfer (ADAP, msgs, num);
Then the analysis of the reading function, read function and write function of the call process is similar, called At24_bin_read->at24_read->at24_eeprom_read to achieve the EEPROM reading data, at24_eeprom_ The implementation of read is mainly divided into the construction of MSG and the transmission of data, the structure of MSG is similar to the message structure in the last lesson, it also needs to be divided into 2 messages, one is to write the message, then the construction of the read message.
if (At24->chip.flags & at24_flag_addr16)
msgbuf[i++] = offset >> 8;
msgbuf[i++] = offset;
MSG[0].ADDR = client->addr;
Msg[0].buf = Msgbuf;
Msg[0].len = i;
MSG[1].ADDR = client->addr;
Msg[1].flags = I2C_M_RD;
Msg[1].buf = buf;
Msg[1].len = count;
2. Driver porting
We are now going to modify and migrate the I2C driver code for Linux. First is the registration of I2C equipment, why to register I2C equipment. We know that the probe function will be invoked when the Linux system detects a id_table device, so we need not only register the I2C driver, but also register the I2C device. How to register a I2C device.
For each IIC device there will be a structure to describe, for example AT24C08:
static struct At24_platform_data At24 = {
. Byte_len = 8*1024/8,
. page_size =.
flags = 0,
};
static struct I2c_board_info __initdata mini2440_i2c_devices[]= {
i2c_board_info ("24C08", 0x50)
. Platform_data = &AT24C08,
}
;
Because there may be multiple I2C devices, keep them in an array.
Then use the I2c_register_board_info function to register the array of our defined I2C devices into the Linux kernel:
I2c_register_board_info (0, Mini2440_i2c_devices, array_size (mini2440_i2_devices));
So where to register this I2C device. Typically in the system initialization of the Development Board, the corresponding mini2440 is registered in the Mini2440_machine_init function in the/arch/arm/mach-s3c2440/mach-mini2440.c file. Add the following 2 header files.
#include <linux/i2c.h>
#include <linux/i2c/at24.h>
This is a transplant of the I2C drive.
The next step is to start supporting the EEPROM in the kernel:
#make Menuconfig
Select Device driver, select and enter the Misc devices, and then enter the EEPROM Support, select all the configuration inside, so that the EEPROM driver configuration, and then when registering I2C device, will call the probe function, thus generating/ Sys/bus/i2c/devices/0-0050/eeprom file
To compile the kernel:
#make uimage Arch=arm cross_compile=arm-linux-
3. Driver Test
Test program We are divided into these steps:
1, open the file
2. Write Data S
3, read the data
4, printing
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main ()
{
int fd ;
int i;
Char write_data[256];
Char read_data[256]={0};
Opening file
fd = open ("/sys/bus/i2c/devices/0-0050/eeprom", O_RDWR);
Write Data for
(i=0; i<256; i++)
write_data[i] = i;
Lseek (FD, 0, seek_set);
Write (FD, write_data, 256);
read out data
lseek (FD, 0, seek_set);
Read (FD, Read_data, 256);
Print data for
(i=0; i<256; i++)
printf ("%3d\n", Read_data[i]);
Closes file close
(FD);
return 0;
}
Compile this program:
#arm-linux-gcc-stati app.c-o App
And then copy it to the Development Board and run it.
The printed information should be the same as the written one.