应用层对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()等操作,首先调用的是i2c类i2cdev_fops中的方法,通过i2c类中的方法
再去寻找adapter 对应的算法i2c_algorithm,此处s3c2440对应的为s3c24xx_i2c_algorithm。
对i2c的操作方法
1.首先open
2.ioctl设置at24c02的地址
3.write()
1.open设备/dev/i2c-0
open通过系统调用最后调用到fops的i2cdev_open函数。
1 static int i2cdev_open(struct inode *inode, struct file *file)
2 {
3 。。。 。。。
4 adap = i2c_get_adapter(i2c_dev->adap->nr);
5 if (!adap)
6 return -ENODEV;
7
8 。。。 。。。
9 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 }
在此函数做完相关处理后直接调用adapter的algorithm来发送数据,此处即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。当第一个字节发送完毕后,s3c2440的i2c控制器会产生中断。s3c2440的i2c中断发生在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);
写到s3c2440的IICDS寄存器发送,接着又会产生中断,接着就又执行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->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系统的二进制接口文件,访问设备。
- 应用层对i2c通用驱动接口与eeprom的i2c驱动接口从应用层到内核层调用区别和联系
- Linux I2C驱动分析(三)----i2c_dev通用接口驱动和应用层分析
- Linux I2C read eeprom 从应用层看系统
- Linux I2C read eeprom 从应用层看系统
- 应用层到驱动层
- 从应用层的mmap到驱动层的mmap
- linux 内核与应用层的接口
- 驱动层-3 I2C驱动
- 驱动接口层&通用逻辑层
- 应用调用驱动层
- Android开发之应用层到驱动层的接口实现(一)
- Android开发之应用层到驱动层的接口实现(二)
- linux驱动层到应用层的重要接口sys文件系统---/sys目录详解
- 应用层(用户模式)I2C驱动支持
- 6410 背光驱动及应用层接口
- Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析
- Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析
- Linux I2C驱动分析(三)----i2c_dev驱动和应用层分析
- 拦截组合键
- 特殊回文数
- APT 高级漏洞利用技术
- raspberry-gpio-python更新到0.5.10,新增树莓派2代支持
- 第9 10讲项目6 贪财的富翁
- 应用层对i2c通用驱动接口与eeprom的i2c驱动接口从应用层到内核层调用区别和联系
- 算法训练 区间k大数查询
- oracle ebs 请求 请求组 岗位
- 简单 RPM 包制作
- 6.app架构基础
- 限定 edittext 的 输入内容
- 如何使用Java、Servlet创建二维码
- 如何解析CSV文件的示例代码
- js文本框输入限制