应用层对i2c通用驱动接口与eeprom的i2c驱动接口从应用层到内核层调用区别和联系

来源:互联网 发布:imac软件 编辑:程序博客网 时间:2024/05/01 01:43

一:

i2c通用接口调用过程:

i2c_dev_init--》register_chrdev(I2C_MAJOR, "i2c", &i2cdev_fops)//I2c-dev.c (drivers\i2c)

static const struct file_operations i2cdev_fops = {
.owner = THIS_MODULE,
.llseek= no_llseek,
.read = i2cdev_read,
.write = i2cdev_write,
.unlocked_ioctl= i2cdev_ioctl,
.open = i2cdev_open,
.release = i2cdev_release,

};

对一个设备操作过程如下:

应用层调用write()函数后首先进入的是i2c类设备的write函数,即i2cdev_fops中的write方法。

此处的i2cdev_fops对应的是系统中所有i2c类设备的操作。也就是说系统中所有i2c adapter read()

write() open() close() ioctl()等操作,首先调用的是i2ci2cdev_fops中的方法,通过i2c类中的方法

再去寻找adapter 对应的算法i2c_algorithm,此处s3c2440对应的为s3c24xx_i2c_algorithm

i2c的操作方法

1.首先open

2.ioctl设置at24c02的地址

3.write()

1.open设备/dev/i2c-0

open通过系统调用最后调用到fopsi2cdev_open函数。


static int i2cdev_open(struct inode *inode, struct file *file)  

{  

    。。。 。。。  

    adap = i2c_get_adapter(i2c_dev->adap->nr);  

    if (!adap)  

        return -ENODEV;  

  

    。。。 。。。  

    client = kzalloc(sizeof(*client), GFP_KERNEL);  

10     if (!client) {  

11         i2c_put_adapter(adap);  

12         return -ENOMEM;  

13     }  

14     snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr);  

15     client->driver = &i2cdev_driver;  

16   

17     client->adapter = adap;  

18     file->private_data = client;  

19   

20     return 0;  

21 }  

可以发现此函数的作用是根据/dev/i2c-0的设备号找到对应的adapter,然后将其保存到新建的client中。

需要注意的是,此处的client与驱动中的client不同,这里的client并不会注册到总线上,和i2c驱动模型的代码无关。

此处的client只是用来保存client地址信息等。

最后将这个clietn保存到file->private_data中,供ioctl() write() open()等操作使用。

2. ioctl

应用层调用ioctl后会调用到i2cdev_ioctl()函数,此处使用的是I2C_SLAVE_FORCE,用于设置at24c02的地址。

3.write

write通过系统调用最后执行fops这中的i2cdev_write函数

[cpp] view plain

22 static ssize_t i2cdev_write(struct file *file, const char __user *buf,  

23         size_t count, loff_t *offset)  

24 {  

25     。。。 。。。  

26     struct i2c_client *client = file->private_data;  

27         。。。 。。。  

28     tmp = memdup_user(buf, count);  

29     。。。 。。。  

30     ret = i2c_master_send(client, tmp, count);  

31     。。。 。。。  

32 }  


可以发现,在write函数中首先做的就是将在open操作中保存到file>private_data中的client取出然后通过memdup_user函数将用户空间的缓冲区拷贝到内核空间。最后调用函数i2c_master_send()

[cpp] view plaincopy

33 int i2c_master_send(struct i2c_client *client, const char *buf, int count)  

34 {  

35     。。。 。。。   

36     ret = i2c_transfer(adap, &msg, 1);  

37         。。。 。。。  

38 }  
i2c_mastr_send函数中首先初始化msg结构体,将client的地址、当前需要拷贝的数据长度等信息填充到msg中。最后将此msg作为形参传递给i2c_transfer函数。i2c的读写过程中,发送的信息都是

通过msg来完成的,除了device address之外。device address信息单独发送,其余的通过msg.buf来完成

并且可以发现,此处i2c_transfer中的第三个参数为1,这个参数是告诉驱动每次发送的msg个数,这里设置为1

表示每次只能发送一则msg

i2c_transfer()函数如下:

[cpp] view plaincopy

39 int i2c_transfer(struct i2c_adapter *adap, struct i2c_msg *msgs, int num)  

40 {  

41                  。。。 。。。  

42                orig_jiffies = jiffies;  

43                for (ret = 0, try = 0; try <= adap->retries; try++) {  

44             ret = adap->algo->master_xfer(adap, msgs, num);  

45             if (ret != -EAGAIN)  

46                 break;  

47             if (time_after(jiffies, orig_jiffies + adap->timeout))  

48                 break;  

49         }  

50          。。。 。。。  

51 }  

在此函数做完相关处理后直接调用adapteralgorithm来发送数据,此处即i2c-s3c2440文件中s3c24xx_i2c_probe总注册的算法

[cpp] view plaincopy

52 i2c->adap.algo    = &s3c24xx_i2c_algorithm;  


s3c24xx_i2c_algorithm算法具体如下:[cpp] view plaincopy

53 static const struct i2c_algorithm s3c24xx_i2c_algorithm = {  

54     .master_xfer        = s3c24xx_i2c_xfer,  

55     .functionality      = s3c24xx_i2c_func,  

56 };  


因此相当于直接调用了函数s3c24xx_i2c_xfers3c24xx_i2c_xfer只是对s3c24xx_i2c_doxfer的简单封装,实际的处理

都在函数s3c24xx_i2c_doxfer中。下面重点分析这个s3c24xx_i2c_doxfer函数

[cpp] view plaincopy

57 static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c,  

58                   struct i2c_msg *msgs, int num)  

59 {  

60     。。。 。。。  

61     ret = s3c24xx_i2c_set_master(i2c);  

62     。。。 。。。  

63         i2c->msg     = msgs;  

64         i2c->msg_num = num;  

65         i2c->msg_ptr = 0;  

66         i2c->msg_idx = 0;  

67         i2c->state   = STATE_START;  

68           

69         s3c24xx_i2c_enable_irq(i2c);  

70         s3c24xx_i2c_message_start(i2c, msgs);  

71     spin_unlock_irq(&i2c->lock);  

72   

73     timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);  

74         。。。 。。。  

75   

76 }  

在此函数中首先调用的是s3c24xx_i2c_set_master函数,查询master(adapter)是否

处于忙的状态。忙则休眠1ms后再次查询,总共查询400次,相当于在400ms之后i2c还处于忙状态则放弃。

master空闲后,做些相关初始化的操作。初始化操作中需要注意的是i2c->state = STATE_START,通过这个状态

位来标记i2c当前是起始状态、写状态还是读状态。

接着通关函数

[cpp] view plaincopy

77 s3c24xx_i2c_enable_irq(i2c);  

打开中断。然后调用函数[cpp] view plaincopy

78 s3c24xx_i2c_message_start(i2c, msgs);  


来发送第一个字节,即device address。当第一个字节发送完毕后,s3c2440i2c控制器会产生中断。s3c2440i2c中断发生在1.完成1字节的发送或者接收2.广播呼叫或者从地址匹配时3.总线仲裁失败。

并且当第一个字节device address发送完毕后,函数通过

[cpp] view plaincopy

79 timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);  

将当前进程进入等待状态,等待处理器将msg.buf发送完毕(i2c-msg_num==0表示发送完毕),假如在HZ*5(5秒钟)内没有没有将msg发送完毕,则发送超时。中断处理函数在s3c24xx_i2c_probe函数注册,为s3c24xx_i2c_irq()

s3c24xx_i2c_irq的操作很简单,最重要的一步就是调用函数i2s_s3c_irq_nextbyte。在

i2s_s3c_irq_nextbyte函数中

[cpp] view plaincopy

80 static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)  

81 {  

82     。。。 。。。  

83     switch (i2c->state) {  

84   

85     。。。 。。。  

86   

87     case STATE_START:  

88         /* last thing we did was send a start condition on the 

89          * bus, or started a new i2c message 

90          */  

91   

92         if (iicstat & S3C2410_IICSTAT_LASTBIT &&  

93             !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {  

94             /* ack was not received... */  

95   

96             dev_dbg(i2c->dev, "ack was not received\n");  

97             s3c24xx_i2c_stop(i2c, -ENXIO);  

98             goto out_ack;  

99         }  

100   

101         if (i2c->msg->flags & I2C_M_RD)  

102             i2c->state = STATE_READ;  

103         else  

104             i2c->state = STATE_WRITE;  

105   

106         /* terminate the transfer if there is nothing to do 

107          * as this is used by the i2c probe to find devices. */  

108   

109         if (is_lastmsg(i2c) && i2c->msg->len == 0) {  

110             dev_dbg(i2c->dev, "last msg sended\n");  

111             s3c24xx_i2c_stop(i2c, 0);  

112             goto out_ack;  

113         }  

114   

115         if (i2c->state == STATE_READ)  

116             goto prepare_read;  

117   

118         /* fall through to the write state, as we will need to 

119          * send a byte as well */  

120   

121     case STATE_WRITE:  

122         /* we are writing data to the device... check for the 

123          * end of the message, and if so, work out what to do 

124          */  

125   

126         if (!(i2c->msg->flags & I2C_M_IGNORE_NAK)) {  

127             if (iicstat & S3C2410_IICSTAT_LASTBIT) {  

128                 dev_dbg(i2c->dev, "WRITE: No Ack\n");  

129   

130                 s3c24xx_i2c_stop(i2c, -ECONNREFUSED);  

131                 goto out_ack;  

132             }  

133         }  

134   

135  retry_write:  

136   

137         if (!is_msgend(i2c)) {  

138             byte = i2c->msg->buf[i2c->msg_ptr++];  

139             writeb(byte, i2c->regs + S3C2410_IICDS);  

140   

141             /* delay after writing the byte to allow the 

142              * data setup time on the bus, as writing the 

143              * data to the register causes the first bit 

144              * to appear on SDA, and SCL will change as 

145              * soon as the interrupt is acknowledged */  

146   

147             ndelay(i2c->tx_setup);  

148   

149         } else if (!is_lastmsg(i2c)) {  

150             /* we need to go to the next i2c message */  

151   

152             dev_dbg(i2c->dev, "WRITE: Next Message\n");  

153   

154             i2c->msg_ptr = 0;  

155             i2c->msg_idx++;  

156             i2c->msg++;  

157   

158             /* check to see if we need to do another message */  

159             if (i2c->msg->flags & I2C_M_NOSTART) {  

160   

161                 if (i2c->msg->flags & I2C_M_RD) {  

162                     /* cannot do this, the controller 

163                      * forces us to send a new START 

164                      * when we change direction */  

165   

166                     s3c24xx_i2c_stop(i2c, -EINVAL);  

167                 }  

168   

169                 goto retry_write;  

170             } else {  

171                 /* send the new start */  

172                 s3c24xx_i2c_message_start(i2c, i2c->msg);  

173                 i2c->state = STATE_START;  

174             }  

175   

176         } else {  

177             /* send stop */  

178   

179             s3c24xx_i2c_stop(i2c, 0);  

180         }  

181         break;  

182   

183      。。。 。。。  

184 }  


可以发现此处的函数先进入STATE_START,然后将i2c状态根据切换为读或者写状态,接着函数将需要送的字节通过代码

[cpp] view plaincopy

185                      byte = i2c->msg->buf[i2c->msg_ptr++];  

186 writeb(byte, i2c->regs + S3C2410_IICDS);  

187   

188 /* delay after writing the byte to allow the 

189  * data setup time on the bus, as writing the 

190  * data to the register causes the first bit 

191  * to appear on SDA, and SCL will change as 

192  * soon as the interrupt is acknowledged */  

193   

194 ndelay(i2c->tx_setup);  

写到s3c2440IICDS寄存器发送,接着又会产生中断,接着就又执行i2s_s3c_irq_nextbyte->i2s_s3c_irq_nextbyte直到msg.buf中所有的数据全部发送出去。

当所有数据发送完毕后,在i2s_s3c_irq_nextbyte函数中执行s3c24xx_i2c_stop()函数

[cpp] view plaincopy

195 static inline void s3c24xx_i2c_stop(struct s3c24xx_i2c *i2c, int ret)  

196 {  

197     unsigned long iicstat = readl(i2c->regs + S3C2410_IICSTAT);  

198   

199     dev_dbg(i2c->dev, "STOP\n");  

200   

201     /* stop the transfer */  

202     iicstat &= ~S3C2410_IICSTAT_START;  

203     writel(iicstat, i2c->regs + S3C2410_IICSTAT);  

204   

205     i2c->state = STATE_STOP;  

206   

207     s3c24xx_i2c_master_complete(i2c, ret);  

208     s3c24xx_i2c_disable_irq(i2c);  

209 }  


在此函数中执行一些扫尾工作,并最后调用函数s3c24xx_i2c_master_complete()[cpp] view plaincopy

210 static inline void s3c24xx_i2c_master_complete(struct s3c24xx_i2c *i2c, int ret)  

211 {  

212     dev_dbg(i2c->dev, "master_complete %d\n", ret);  

213   

214     i2c->msg_ptr = 0;  

215     i2c->msg = NULL;  

216     i2c->msg_idx++;  

217     i2c->msg_num = 0;  

218     if (ret)  

219         i2c->msg_idx = ret;  

220   

221     wake_up(&i2c->wait);  

222 }  

然后通过wake_up函数来唤醒等待在i2c队列上的其他操作。

到此数据写完毕。读过程类似。

二:eeprom特定接口读写

static struct i2c_driver at24_driver = {
.driver = {
.name = "at24",
.owner = THIS_MODULE,
},
.probe = at24_probe,
.remove = __devexit_p(at24_remove),
.id_table = at24_ids,
};

at24_init--》i2c_add_driver(&at24_driver)--》at24_probe-》(at24_bin_read/at24_bin_write

[at24->bin.write = at24_bin_write;/at24->bin.read = at24_bin_read;]

二进制的文件读写与普通属性的文件读写方式大部份都一样,所不同的是.二进制文件的读写接口分别是: sysfs_dirent ->s_bin_attr.bin_attr->read和sysfs_dirent ->s_bin_attr.bin_attr->write

通过访问sysfs_create_bin_file创建sysfs系统的二进制接口文件,访问设备


0 0
原创粉丝点击