Original Works are allowed to be reprinted. During reprinting, please mark the article in hyperlink form
Original source, author information, and this statement. Otherwise, legal liability will be held. Http://ticktick.blog.51cto.com/823160/760020
Linux I2C driver is a kind of driver that embedded Linux driver developers often need to write, because any I2C device used in the system, almost all need to write the corresponding I2C driver to configure and control it, such as RTC real-time clock chip, audio and video acquisition chip, audio and video output chip, eerom chip, AD/DA conversion chip and so on.
Linux I2C drivers involve many knowledge points, mainly including Linux I2C bus drivers and I2C clients drivers ), this article focuses on how to quickly complete a specific I2C Device Driver (I2C clients driver ). You can search for other articles on the Internet to learn about the overall architecture and core principles of the Linux I2C driver.
Note: the I2C device driver in this series is based on the Linux 2.6.18 kernel.
This article mainly references the./documentation/I2C/writing-clients document under the Linux kernel source code directory. Compile the Linux I2C device driver with a video capture chip tvp5158 as the driver.
1. i2c_driver struct object
For each I2C device driver, you must first create an i2c_driver struct object, which contains some basic methods and information for I2C Device Detection and cancellation. The example is as follows:
- static struct i2c_driver tvp5158_i2c_driver = {
- .driver = {
- .name = "tvp5158_i2c_driver",
- },
- .attach_adapter = &tvp5158_attach_adapter,
- .detach_client = &tvp5158_detach_client,
- .command = NULL,
- };
The name field identifies the driver name (up to 31 characters), and The attach_adapter and detach_client fields are function pointers. These two functions are automatically called when the I2C device is registered, you need to implement these two functions by yourself, which will be detailed later.
2.I2c_client struct object
The i2c_driver object defined above is abstracted as an I2C Driver Model and provides methods for detecting and canceling I2C devices. The i2c_client structure represents a specific I2C device, this struct has a Data Pointer that can point to any private device data and may be used in drivers of complex points. Example:
- struct tvp5158_obj{
- struct i2c_client client;
- int users; // how many users using the driver
- };
-
- struct tvp5158_obj* g_tvp5158_obj;
Here, users is an example. You can add fields of interest to the structure tvp5158_obj, but the i2c_client field is indispensable. The specific usage will be detailed later.
3. device registration and Detection Functions
This step is critical and is written according to the standard requirements. The Linux system will automatically call the relevant code to detect your I2C device, and added to the system's I2C device list for later access.
We know that the I2C device address is set for each I2C device chip through a hardware connection. Therefore, I2C device detection is generally completed by the device address. First, declare the list of I2C device addresses you want to detect and a macro in the driver code. Example:
- static unsigned short normal_i2c[] = {
- 0xbc >> 1,
- 0xbe >> 1,
- I2C_CLIENT_END
- };
- I2C_CLIENT_INSMOD;
The normal_i2c array contains the list of I2C device addresses to be tested and must end with i2c_client_end. Note that 0xbc and 0xbe in the above Code are the addresses I allocated to my tvp5158 on the hardware, on the hardware, you can set the address to 0xbc or 0xbe by using a jumper. Therefore, you can write these two addresses to the detection list for system testing. If the address of your I2C device is fixed, you can only write your own I2C device address here. Note that you must shift 1 to the right.
The role of macro i2c_client_insmod has been explained in detail in many articles on the Internet. I will not describe it in detail here. Remember to add it. We will focus on implementation.
In the next step, you should write two callback functions, one for device registration and the other for device logout. An example of the probe function is as follows:
- static int tvp5158_attach_adapter(struct i2c_adapter *adapter)
- {
- return i2c_probe(adapter, &addr_data, &tvp5158_detect_client);
- }
This callback function is automatically called by the system. You only need to write it in the above code format. Here, the system's I2C device detection function is called, i2c_probe (), the third parameter is the device detection callback function. The system will call this function when detecting the device. You need to implement this function by yourself. Example:
- static int tvp5158_detect_client(struct i2c_adapter *adapter,int address,int kind)
- {
- struct tvp5158_obj *pObj;
- int err = 0;
-
- printk(KERN_INFO "I2C: tvp5158_detect_client at address %x ...\n", address);
-
- if( g_tvp5158_obj != NULL ) {
- //already allocated,inc user count, and return the allocated handle
- g_tvp5158_obj->users++;
- return 0;
- }
-
- /* alloc obj */
- pObj = kmalloc(sizeof(struct tvp5158_obj), GFP_KERNEL);
- if (pObj==0){
- return -ENOMEM;
- }
- memset(pObj, 0, sizeof(struct tvp5158_obj));
- pObj->client.addr = address;
- pObj->client.adapter = adapter;
- pObj->client.driver = &tvp5158_i2c_driver;
- pObj->client.flags = I2C_CLIENT_ALLOW_USE;
- pObj->users++;
-
- /* attach i2c client to sys i2c clients list */
- if((err = i2c_attach_client(&pObj->client))){
- printk( KERN_ERR "I2C: ERROR: i2c_attach_client fail! address=%x\n",address);
- return err;
- }
-
- // store the pObj
- g_tvp5158_obj = pObj;
-
- printk( KERN_ERR "I2C: i2c_attach_client ok! address=%x\n",address);
-
- return 0;
- }
So far, the code for detecting and registering the device has been completed, and later access to the I2C device can be done through the Global pointer g_tvp5158_obj.
4. log out of the I2C Device
Similarly, the device logout callback function is automatically called by the system. You only need to write the device logout code according to the template, as shown in the following example:
- static int tvp5158_detach_client(struct i2c_client *client)
- {
- int err;
-
- if( ! client->adapter ){
- return -ENODEV;
- }
-
- if( (err = i2c_detach_client(client)) ) {
- printk( KERN_ERR "Client deregistration failed (address=%x), client not detached.\n", client->addr);
- return err;
- }
-
- client->adapter = NULL;
-
- if( g_tvp5158_obj ){
- kfree(g_tvp5158_obj);
- }
-
- return 0;
- }
By now, all the device registration and cancellation codes have been completed. The following describes how to read and write I2C devices.
5. read/write of I2C Devices
Linux provides multiple interfaces for reading and writing I2C devices, which can be found in I2C. H of the kernel. Here we will briefly introduce the two interfaces.
[Interface 1 ]:
- extern int i2c_master_send(struct i2c_client *,const char* ,int);
-
- extern int i2c_master_recv(struct i2c_client *,char* ,int);
The first parameter is the i2c_client Object Pointer, the second parameter is the data buffer pointer to be transmitted, and the third parameter is the buffer size.
[Interface 2 ]:
- extern int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msg, int num);
This interface supports sending multiple messages to the I2C device at a time. Each message can be a read, write, read, or read/write Destination Address (Register address) are included in MSG message parameters.
These interfaces are only the underlying read/write methods. For details about how to interact with I2C devices, such as how to read the value of a specific register of a chip, you need to refer to the specific chip manual, each I2C chip has a specific I2C register read/write timing diagram. Therefore, in order to provide better access interfaces in the driver, you need to further encapsulate these read/write functions according to the specific time series requirements, which will be described in the following article.
6. module initialization and others
The next step is the initialization code and inverse initialization code of the entire module, and the module declaration.
- static int __init tvp5158_i2c_init(void)
- {
- g_tvp5158_obj = NULL;
-
- return i2c_add_driver(&tvp5158_i2c_driver);
- }
-
- static void __exit tvp5158_i2c_exit(void)
- {
- i2c_del_driver(&tvp5158_i2c_driver);
- }
-
- module_init(tvp5158_i2c_init);
- module_exit(tvp5158_i2c_exit);
-
- MODULE_DESCRIPTION("TVP5158 i2c driver");
- MODULE_AUTHOR("Lujun @hust");
- MODULE_LICENSE("GPL");
In the initialization code, add the I2C driver object of this module. In the inverse initialization code, delete the I2C driver object of this module.
7. Summary
So far, I have finished writing an I2C device driver code from the perspective of the application. I have not analyzed a lot of principles (I do not know much about them ), later will slowly more in-depth study and understanding, what is the story of the incorrect place, welcome to leave a message or send a letter to lujun.hust@gmail.com exchange.
At the end of this article, you may have another question: how can I use this driver in the user space (Application Layer) after writing it? Because this article does not want to make the code too complicated and is afraid of improving the understanding difficulty, I will not talk about it. In fact, to use the I2C device driver in the user space, the I2C device driver also needs to be used to encapsulate a layer of character device drivers. In this way, the user space can access the I2C device by accessing the driver of the character device, this method will be described in later articles.
This article from the "three shadows" blog, please be sure to keep this source http://ticktick.blog.51cto.com/823160/760020