I2C子系统之ioctl()


根据前一篇的文章介绍 at24c02的读写方式有很多种,

写有两种1.写一字节数据到word address处2.从指定的word address处开始写一页数据,此word address需要页对齐!

读有三种1.从at24c02当前的word address读一字节数据2.从指定的word address 读数据3.从当前的word address地址开始读一串数据

根据驱动中write() read()的实现方法可以发现,当msg发送完毕时才发送stop信号,而msg之间是是连续发送的不会插入stop信号。

但是,write() read()每次都固定只能发送一则msg!这对at24c02的写以及current read、sequential read来说没问题,可以通过write()和read()函数直接实现这四种操作。

但是at24c02的random read就不能直接通过write() read()来实现了。因为random read需要先写word address,写完之后不能直接发送stop信号,

而是要接着发送start信号开始发送device address。而驱动中的write() read()只能一次发送一则msg,并且发送完毕就发送stop信号,所以这种时序不符合random read的操作。

不过系统通过ioctl操作,可以一次发送多则msg,而在msg之间是不会发送stop信号的。

所以at24c02的random read操作可以通过发送两则msg的方式来实现,第一则msg是写的,并且写的内容是word address,第二则msg为读。

下面在分析下ioctl 的驱动实现函数i2cdev_ioctl_rdrw()

  1. static noinline int i2cdev_ioctl_rdrw(struct i2c_client *client,  
  2.         unsigned long arg)  
  3. {  
  4.     struct i2c_rdwr_ioctl_data rdwr_arg;  
  5.     struct i2c_msg *rdwr_pa;  
  6.     u8 __user **data_ptrs;  
  7.     int i, res;  
  8.   
  9.     if (copy_from_user(&rdwr_arg,  
  10.                (struct i2c_rdwr_ioctl_data __user *)arg,  
  11.                sizeof(rdwr_arg)))  
  12.         return -EFAULT;  
  13.   
  14.     /* Put an arbitrary limit on the number of messages that can 
  15.      * be sent at once */  
  16.     if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)  
  17.         return -EINVAL;  
  18.   
  19.     rdwr_pa = kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg), GFP_KERNEL);  
  20.     if (!rdwr_pa)  
  21.         return -ENOMEM;  
  22.   
  23.     if (copy_from_user(rdwr_pa, rdwr_arg.msgs,  
  24.                rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {  
  25.         kfree(rdwr_pa);  
  26.         return -EFAULT;  
  27.     }  
  28.   
  29.     data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);  
  30.     if (data_ptrs == NULL) {  
  31.         kfree(rdwr_pa);  
  32.         return -ENOMEM;  
  33.     }  
  34.   
  35.     res = 0;  
  36.     for (i = 0; i < rdwr_arg.nmsgs; i++) {  
  37.         /* Limit the size of the message to a sane amount; 
  38.          * and don't let length change either. */  
  39.         if ((rdwr_pa[i].len > 8192) ||  
  40.             (rdwr_pa[i].flags & I2C_M_RECV_LEN)) {  
  41.             res = -EINVAL;  
  42.             break;  
  43.         }  
  44.         data_ptrs[i] = (u8 __user *)rdwr_pa[i].buf;  
  45.         rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);  
  46.         if (IS_ERR(rdwr_pa[i].buf)) {  
  47.             res = PTR_ERR(rdwr_pa[i].buf);  
  48.             break;  
  49.         }  
  50.     }  
  51.     if (res < 0) {  
  52.         int j;  
  53.         for (j = 0; j < i; ++j)  
  54.             kfree(rdwr_pa[j].buf);  
  55.         kfree(data_ptrs);  
  56.         kfree(rdwr_pa);  
  57.         return res;  
  58.     }  
  59.   
  60.     res = i2c_transfer(client->adapter, rdwr_pa, rdwr_arg.nmsgs);  
  61.     while (i-- > 0) {  
  62.         if (res >= 0 && (rdwr_pa[i].flags & I2C_M_RD)) {  
  63.             if (copy_to_user(data_ptrs[i], rdwr_pa[i].buf,  
  64.                      rdwr_pa[i].len))  
  65.                 res = -EFAULT;  
  66.         }  
  67.         kfree(rdwr_pa[i].buf);  
  68.     }  
  69.     kfree(data_ptrs);  
  70.     kfree(rdwr_pa);  
  71.     return res;  
  72. }  
首先函数通过copy_from_user函数将用户空间的数据拷贝到内核。data_ptrs这个变量相当于指针数组了,相当于数组里面存放的是指针,并且这些指针指向的是需要发送的数据buf的首地址。

其中有一句比较难理解的是

  1. rdwr_pa[i].buf = memdup_user(data_ptrs[i], rdwr_pa[i].len);  
这句话其实实现一下内容:

1.在内核空间申请一块buf,

2.将data_ptrs[i] (其值和目前的rdwr_pa[i].buf是一致的)指向的用户空间的buf中的数据拷贝到刚申请的内核buf中。

3.将内核空间的buf地址返回,并且覆盖rdwr_pa[i].buf中的数值,使其值由原来用户空间的buf地址变为内核空间的buf地址。

然后调用函数i2c_transfer开始发送信息。发送时序可以通过

i2s_s3c_irq_nextbyte()函数中的流程来判断,此处不具体分析了.

相关阅读:

I2C子系统之at24c02读写测试
I2C子系统之ioctl() 
I2C子系统之at24c02简介
I2C子系统之总结
I2C子系统之内核中I2C子系统的结构
I2C子系统之I2C bus初始化——I2C_init()
I2C子系统之platfor_device初始化——smdk2440_machine_init()
I2C子系统之platform_driver初始化——I2C_adap_s3c_init()
I2C子系统之I2C总线时钟频率设置
I2C子系统之adapter device和client device注册——I2C_add_number_adapter()
I2C子系统之__I2C_first_dynamic_bus_num变量的相关分析
I2C子系统之 adapter driver注册——I2C_dev_init()
I2C子系统之write()

相关内容