Analysis of S3C2440 driver-I2C Driver (3)

Source: Internet
Author: User

After discussing the initialization and deletion of drivers for I2C devices and I2C adapters, we will focus on several function operations in file_operations, paste the file_operations struct code first. Let's see which functions are included.

 

Static const struct file_operations i2cdev_fops ={< br/>. owner = this_module, <br/>. llseek = no_llseek, <br/>. read = i2cdev_read, <br/>. write = i2cdev_write, <br/>. unlocked_ioctl = i2cdev_ioctl, <br/>. open = i2cdev_open, <br/>. release = i2cdev_release, <br/> };

 

The structure must be quite clear. Therefore, the I2C Driver provides the following operation functions for the user space:

1. no_llseek

2. i2cdev_read

3. i2cdev_write

4. i2cdev_ioctl

5. i2cdev_open

6. i2cdev_release

 

Next we will analyze them one by one:

1. no_llseek

Loff_t no_llseek (struct file * file, loff_t offset, int origin) <br/>{< br/> return-espipe; <br/>}

From the member name llseek of i2cdev_fops, the role of llseek is to change the reading and writing location of the I2C device. As you can see, the driver does not implement this function.

 

 

2. i2cdev_read

 

Static ssize_t i2cdev_read (struct file * file, char _ User * Buf, size_t count, <br/> loff_t * offset) <br/>{< br/> char * TMP; <br/> int ret; </P> <p> struct i2c_client * client = file-> private_data; </P> <p> If (count> 8192) <br/> COUNT = 8192; </P> <p> TMP = kmalloc (count, gfp_kernel); <br/> If (TMP = NULL) <br/> return-enomem; </P> <p> pr_debug ("i2c-dev: I2C-% d reading % zu bytes. /n ", <br/> iminor (file-> f_path.dentr Y-> d_inode), count); </P> <p> ret = i2c_master_recv (client, TMP, count); <br/> If (Ret> = 0) <br/> ret = copy_to_user (BUF, TMP, count )? -Efault: ret; <br/> kfree (TMP); <br/> return ret; <br/>}< br/>

The READ function mainly performs the following tasks:

(1) struct i2c_client * client = file-> private_data;

Find the I2C device corresponding to the image through the file passed in the parameter (described in the previous blog). Then the function is to operate the device.

(2) ret = i2c_master_recv (client, TMP, count );

The i2c_master_recv function is implemented in the i2c-core.c. In order to make the structure of this article clearer, the i2c-core.c of the code file dealing with hardware is put in the blog post analysis. Here, we simply need to solve the i2c_master_recv function by calling the I2C device mentioned above and extracting the Count byte content from it to the TMP buffer.

(3) ret = copy_to_user (BUF, TMP, count )? -Efault: ret;

If no exception occurs, we will call the copy_to_user function, which we are very familiar with. This function will not be described here. We recommend that you do not know the relevant information, or I have mentioned it in my previous blog post. This is the foundation!

 

 

3. i2cdev_write

 

Static ssize_t i2cdev_write (struct file * file, const char _ User * Buf, <br/> size_t count, loff_t * offset) <br/>{< br/> int ret; <br/> char * TMP; <br/> struct i2c_client * client = file-> private_data; </P> <p> If (count> 8192) <br/> COUNT = 8192; </P> <p> TMP = memdup_user (BUF, count); <br/> If (is_err (TMP )) <br/> return ptr_err (TMP); </P> <p> pr_debug ("i2c-dev: I2C-% d writing % zu bytes. /n ", <br/> iminor (file-> f_path.dentry-> d_inode), count); </P> <p> ret = i2c_master_send (client, TMP, count ); <br/> kfree (TMP); <br/> return ret; <br/>}< br/>

What has the write function done?

(1) struct i2c_client * client = file-> private_data;

See the description in the READ function above.

(2) TMP = memdup_user (BUF, count );

The memdup_user function is implemented in util. C. The source code is as follows:

Void * memdup_user (const void _ User * SRC, size_t Len) <br/>{< br/> void * P; </P> <p>/* <br/> * always use gfp_kernel, since copy_from_user () can sleep and <br/> * cause pagefault, which makes it pointless to use gfp_nofs <br/> * or gfp_atomic. <br/> */<br/> P = kmalloc_track_caller (Len, gfp_kernel); <br/> If (! P) <br/> return err_ptr (-enomem); </P> <p> If (copy_from_user (p, SRC, Len )) {<br/> kfree (p); <br/> return err_ptr (-efault); <br/>}</P> <p> return P; <br/>}< br/>

The function is clear at a glance, that is, the pointer SRC points to the copy_from_user In the content with the length of Len to the pointer returned by the function.

(3) ret = i2c_master_send (client, TMP, count );

Send the content in the cache TMP to the client.

 

 

4. i2cdev_ioctl

 

Static long i2cdev_ioctl (struct file * file, unsigned int cmd, unsigned long Arg) <br/>{< br/> struct i2c_client * client = file-> private_data; <br/> unsigned long funcs; </P> <p> dev_dbg (& client-> adapter-> Dev, "ioctl, cmd = 0x % 02x, arg = 0x % 02lx/N ", <br/> cmd, ARG); </P> <p> switch (CMD) {<br/> case i2c_slave: <br/> case i2c_slave_force: <br/>/* Note: devices set up to work with "New Style" drivers <br/> * can't Use i2c_slave, even when the device node is not <br/> * bound to a driver. only i2c_slave_force will work. <br/> * setting the PEC flag here won't affect kernel drivers, <br/> * which will be using the i2c_client node registered with <br/> * The driver model core. likewise, when that client has <br/> * The PEC flag already set, the i2c-dev driver won't see * (or use) This setting. <br /> */<Br/> If (Arg> 0x3ff) | <br/> (client-> flags & i2c_m_ten) = 0) & Arg> 0x7f) <br/> return-einval; <br/> If (cmd = i2c_slave & i2cdev_check_addr (client-> adapter, ARG )) <br/> return-ebusy; <br/>/* revisit: Address cocould become busy later */<br/> client-> ADDR = ARG; <br/> return 0; <br/> case i2c_tenbit: <br/> If (ARG) <br/> client-> flags | = i2c_m_ten; <br/> else <br/> client-> flags & = ~ I2c_m_ten; <br/> return 0; <br/> case i2c_pec: <br/> If (ARG) <br/> client-> flags | = i2c_client_pec; <br/> else <br/> client-> flags & = ~ I2c_client_pec; <br/> return 0; <br/> case i2c_funcs: <br/> funcs = i2c_get_functionality (client-> adapter); <br/> return put_user (funcs, (unsigned long _ User *) Arg); </P> <p> case i2c_rdwr: <br/> return i2cdev_ioctl_rdrw (client, ARG ); </P> <p> case i2c_smbus: <br/> return i2cdev_ioctl_smbus (client, ARG); </P> <p> case i2c_retries: <br/> client-> adapter-> retries = ARG; <br/> break; <br/> case i2c_timeout: <br/>/* for historical reasons, user-space sets the timeout <br/> * value in units of 10 ms. <br/> */<br/> client-> adapter-> timeout = msecs_to_jiffies (Arg * 10); <br/> break; <br/> default: <br/>/* Note: returning a fault code here cocould cause trouble <br/> * In buggy userspace code. some old kernel bugs returned <br/> * zero in this case, and userspace code might accidentally <br/> * have depended on that bug. <br/> */<br/> return-enotty; <br/>}< br/> return 0; <br/>}< br/>

Because the device node of the I2C adapter represents the entire I2C bus, you must specify
The bus address of the device. The operation that specifies the address is completed by calling the ioctl system. It finally calls the device method i2cdev_ioctl. The ioctl function still adopts the switch-case logic structure that we are very familiar with. The operation analysis of various commands is as follows:

Case i2c_slave:
Case i2c_slave_force: sets the address of the slave device.

 

Case i2c_tenbit: Set the 7bit address or 10bit address.

 

Case i2c_pec: If i2c_pec! = 0, to use Pec with SMBus

 

Case i2c_funcs: return the transmission types supported by the controller algorithm

 

Case i2c_rdwr: Execute the i2cdev_ioctl_rdrw function. The Code is as follows:

Static noinline int struct (struct i2c_client * client, <br/> unsigned long Arg) <br/>{< br/> struct i2c_rdwr_ioctl_data rdwr_arg; <br/> struct i2c_msg * rdwr_pa; <br/> u8 _ User ** data_ptrs; <br/> int I, Res; </P> <p> If (copy_from_user (& rdwr_arg, <br/> (struct i2c_rdwr_ioctl_data _ User *) Arg, <br/> sizeof (rdwr_arg) <br/> return-efault; </P> <p>/* put an arbitrary limit on the number of messages That can <br/> * be sent at once */<br/> If (rdwr_arg.nmsgs> i2c_rdrw_ioctl_max_msgs) <br/> return-einval; </P> <p> rdwr_pa = (struct i2c_msg *) <br/> kmalloc (rdwr_arg.nmsgs * sizeof (struct i2c_msg), <br/> gfp_kernel ); <br/> If (! Rdwr_pa) <br/> return-enomem; </P> <p> If (copy_from_user (rdwr_pa, rdwr_arg.msgs, <br/> rdwr_arg.nmsgs * sizeof (struct i2c_msg ))) {<br/> kfree (rdwr_pa); <br/> return-efault; <br/>}</P> <p> data_ptrs = kmalloc (rdwr_arg.nmsgs * sizeof (u8 _ User *), gfp_kernel ); <br/> If (data_ptrs = NULL) {<br/> kfree (rdwr_pa); <br/> return-enomem; <br/>}</P> <p> res = 0; <br/> for (I = 0; I <rdwr_arg.nmsgs; I ++) {<br/>/* limit the size of the message to a sane amount; <br/> * and don't let length change either. */<br/> If (rdwr_pa [I]. len> 8192) | <br/> (rdwr_pa [I]. flags & i2c_m_recv_len) {<br/> res =-einval; <br/> break; <br/>}< br/> data_ptrs [I] = (u8 _ User *) rdwr_pa [I]. buf; <br/> rdwr_pa [I]. buf = kmalloc (rdwr_pa [I]. len, gfp_kernel); <br/> If (rdwr_pa [I]. buf = NULL) {<br/> res =-enomem; <br/> break; <br/>}< br/> If (copy_from_user (rdwr_pa [I]. buf, data_ptrs [I], <br/> rdwr_pa [I]. len) {<br/> + I;/* needs to be kfreed too */<br/> res =-efault; <br/> break; <br/>}< br/> If (RES <0) {<br/> Int J; <br/> for (j = 0; j <I; ++ J) <br/> kfree (rdwr_pa [J]. buf); <br/> kfree (data_ptrs); <br/> kfree (rdwr_pa); <br/> return res; <br/>}</P> <p> res = i2c_transfer (client-> adapter, rdwr_pa, rdwr_arg.nmsgs); <br/> while (I --> 0) {<br/> If (RES> = 0 & (rdwr_pa [I]. flags & i2c_m_rd) {<br/> If (copy_to_user (data_ptrs [I], rdwr_pa [I]. buf, <br/> rdwr_pa [I]. len) <br/> res =-efault; <br/>}< br/> kfree (rdwr_pa [I]. buf); <br/>}< br/> kfree (data_ptrs); <br/> kfree (rdwr_pa); <br/> return res; <br/>}< br/>

The entire function mainly does the following:

(1) copy_from_user (& rdwr_arg,
(Struct i2c_rdwr_ioctl_data _ User *) Arg,
Sizeof (rdwr_arg ))

Obtain the commands passed in by the user and store them in rdwr_arg.

(2) copy_from_user (rdwr_pa, rdwr_arg.msgs,
Rdwr_arg.nmsgs * sizeof (struct i2c_msg ))

Point the rdwr_pa pointer to the msgs Member of the rdwr_arg struct.

(3) The essence of the For Loop is to copy the information of each MSG to be transmitted to the string pointer data_ptrs.

(4) RES = i2c_transfer (client-> adapter, rdwr_pa, rdwr_arg.nmsgs );

The i2c_transfer () function is used to interact messages between I2C adapters and I2C devices. The i2c_transfer () function itself does not have the ability to drive the physical hardware of the adapter to complete message interaction, it only finds the i2c_algorithm corresponding to the i2c_adapter, and uses the master_xfer () function of i2c_algorithm to actually drive the hardware process. I2c_algorithm and master_xfer will be introduced in the following blog.

(5) In the while loop, all the information stored in data_ptrs is copy_to_user.

Case i2c_smbus: Execute i2cdev_ioctl_smbus function, mainly call i2c_smbus_xfer, function is implemented in the i2c-core.c. Here, we will simply understand that this function serves to select the SMBus communication protocol.

Case i2c_retries: set the number of resends

Case i2c_timeout: Set the timeout to Arg * 10 (MS)

Call ~ Ioctl function finally brought over, the specific not in-depth, are involved in the i2c-core.c and data structure i2c_algorithm place, these two stuff stay for the next blog to clean up.

5. i2cdev_open

Static int i2cdev_open (struct inode * inode, struct file * file) <br/>{< br/> unsigned int minor = iminor (inode ); <br/> struct i2c_client * client; <br/> struct i2c_adapter * ADAP; <br/> struct i2c_dev * i2c_dev; </P> <p> i2c_dev = i2c_dev_get_by_minor (minor); <br/> If (! I2c_dev) <br/> return-enodev; </P> <p> ADAP = i2c_get_adapter (i2c_dev-> ADAP-> nr); <br/> If (! ADAP) <br/> return-enodev; </P> <p>/* This creates an anonymous i2c_client, which may later be <br/> * pointed to some address using i2c_slave or i2c_slave_force. <br/> * this client is ** never registered ** with the driver model <br/> * or I2C core code !! It just holds private copies of addressing <br/> * Information and maybe a PEC flag. <br/> */<br/> client = kzarloc (sizeof (* client), gfp_kernel); <br/> If (! Client) {<br/> i2c_put_adapter (ADAP); <br/> return-enomem; <br/>}< br/> snprintf (client-> name, i2c_name_size, "i2c-dev % d", ADAP-> nr); <br/> client-> driver = & i2cdev_driver; </P> <p> client-> adapter = ADAP; <br/> file-> private_data = client; </P> <p> return 0; <br/>}< br/>

The last two functions are easy. Some registration and initialization work is taken at a glance. The only thing to note is that this open function is bound a little more than the other drivers described earlier, for example:

Client-> driver = & i2cdev_driver;

Client-> adapter = ADAP;
File-> private_data = client;

As for the purpose, I do not remember to suggest reading the first blog post of this I2C driver series.

 

6. i2cdev_release

Static int i2cdev_release (struct inode * inode, struct file * file) <br/>{< br/> struct i2c_client * client = file-> private_data; </P> <p> i2c_put_adapter (client-> adapter); <br/> kfree (client); <br/> file-> private_data = NULL; </P> <p> return 0; <br/>}

Compared with the open function, isn't this release a piece of cake?

 

 

 

So far, the i2c-dev.c driver code analysis has come to an end, because of my level limit, can not be very profound, in place to write this driver code analysis, please also read a lot of comments. Content error. Please comment it out! Next this series of 4th blog posts will talk about i2c-core.c and data structure i2c_algorithm, stay tuned!

 

Links to this series of blog posts:

I2C drive (1) http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6449939.aspx
I2C drive (2) http://blog.csdn.net/jarvis_xian/archive/2011/05/27/6451168.aspx
I2C drive (3) http://blog.csdn.net/jarvis_xian/archive/2011/05/28/6452431.aspx
I2C drive (4) http://blog.csdn.net/jarvis_xian/archive/2011/05/30/6455697.aspx

 

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.