Chinaunix首页 | 论坛 | 博客
  • 博客访问: 505285
  • 博文数量: 157
  • 博客积分: 3010
  • 博客等级: 中校
  • 技术积分: 1608
  • 用 户 组: 普通用户
  • 注册时间: 2008-08-16 09:30
文章存档

2010年(155)

2008年(2)

我的朋友

分类:

2010-04-07 10:46:25

15.4.3 Linux I2C设备驱动文件操作接口
作 为一种字符类设备,Linux I2C设备驱动文件操作接口与普通的设备驱动是完全一致的,但是在其中要使用i2c_client、i2c_driver、i2c_adapter和 i2c_algorithm结构体和I2C核心,并且对设备的读写和控制需要借助体系结构中各组成部分的协同合作。代码清单15.20给出一个I2C设备 写函数的例子。
代码清单15.20 I2C设备文件接口写函数范例
1   static ssize_t yyy_write(struct file *file, char *buf, size_t count, loff_t off)
2   {
3 struct i2c_client *client = (struct i2c_client*)file->private_data;
4 i2c_msg msg[1];
5 char *tmp;
6 int ret;
7  
8 tmp = kmalloc(count, GFP_KERNEL);
9 if (tmp == NULL)
10     return   - ENOMEM;
11 if (copy_from_user(tmp, buf, count))
12 {
13     kfree(tmp);
14     return   - EFAULT;
15 }
16
17 msg[0].addr = client->addr;//地址
18 msg[0].flags = 0;    //0为写
19 msg[0].len = count;     //要写的字节数
20 msg[0].buf = tmp;    //要写的数据
21 ret = i2c_transfer(client->adapter, msg, 1);   //传输i2c消息
22 return (ret == 1) ? count : ret;
23 }
上述程序给出的仅仅是一个写函数的例子,具体的写操作与设备密切相关。我们通过这个例来仔细分析I2C设备读写过程中数据的流向和函数的调用关系。I2C设备的写操作经历了如下几个步骤:
① 从用户空间到字符设备驱动写函数接口,写函数构造I2C消息数组。
② 写函数把构造的I2C消息数组传递给I2C核心的传输函数i2c_transfer()。
③ I2C核心的传输函数i2c_transfer()找到对应适配器algorithm的通信方法函数master_xfer()去最终完成I2C消息的处理。
图15.6描述了从用户空间发起读写操作到algorithm进行消息传输的流程。

图15.6 I2C设备读写完整流程
通 常,如果I2C设备不是一个输入输出设备或存储设备,就并不需要给I2C设备提供读写函数。许多I2C设备只是需要被设置以某种方式工作,而不是被读写。 另外,I2C设备驱动的文件操作接口也不是必需的,甚至极少被需要。大多数情况下,我们只需要通过i2c-dev.c文件提供的I2C适配器设备文件接口 就可完成对I2C设备的读写。
15.4.4 Linux i2c-dev.c文件分析
i2c-dev.c文件完全可以被看作一个I2C设 备驱动,其结构与15.4.1~15.4.3节的描述是基本一致的,不过,它实现的一个i2c_client是虚拟的、临时的,随着设备文件的打开而产 生,并随设备文件的关闭而撤销,并没有被添加到i2c_adapter的clients链表中。i2c-dev.c针对每个I2C适配器生成一个主设备为 89的设备文件,实现了i2c_driver的成员函数以及文件操作接口,所以i2c-dev.c的主体是“i2c_driver成员函数 + 字符设备驱动”。
i2c-dev.c中提供i2cdev_read()、i2cdev_write()函数来对应用户空间要使用的read()和 write()文件操作接口,这两个函数分别调用I2C核心的i2c_master_recv()和i2c_master_send()函数来构造1条 I2C消息并引发适配器algorithm通信函数的调用,完成消息的传输,对应于图15.7所示的时序。但是,很遗憾,大多数稍微复杂一点I2C设备的 读写流程并不对应于1条消息,往往需要2条甚至更多的消息来进行一次读写周期(即如图15.8所示的重复开始位RepStart模式),这种情况下,在应 用层仍然调用read()、write()文件API来读写I2C设备,将不能正确地读写。许多工程师碰到过类似的问题,往往经过相当长时间的调试都没法 解决I2C设备的读写,连错误的原因也无法找到,显然是对i2cdev_read()和i2cdev_write()函数的作用有所误解。

图15.7 i2cdev_read和i2cdev_write函数对应时序
图15.8 RepStart模式
鉴 于上述原因,i2c-dev.c中i2cdev_read()和i2cdev_write()函数不具备太强的通用性,没有太大的实用价值,只能适用于非 RepStart模式的情况。对于2条以上消息组成的读写,在用户空间需要组织i2c_msg消息数组并调用I2C_RDWR IOCTL命令。代码清单15.21给出了i2cdev_ioctl()函数的框架,其中详细列出了I2C_RDWR命令的处理过程。
代码清单15.21 i2c-dev.c中的i2cdev_ioctl函数
1   static int i2cdev_ioctl(struct inode *inode, struct file *file,
2 unsigned int cmd, unsigned long arg)
3   {
4 struct i2c_client *client = (struct i2c_client *)file->private_data;
5 ...  
6 switch ( cmd ) {
7 case I2C_SLAVE:
8 case I2C_SLAVE_FORCE:
9 ... /*设置从设备地址*/
10   case I2C_TENBIT:
11 ...
12   case I2C_PEC:
13 ...
14   case I2C_FUNCS:
15 ...  
16   case I2C_RDWR:
17 if (copy_from_user(&rdwr_arg,
18        (struct i2c_rdwr_ioctl_data __user *)arg,
19        sizeof(rdwr_arg)))
20 return -EFAULT;
21 /* 一次传入的消息太多 */
22 if (rdwr_arg.nmsgs > I2C_RDRW_IOCTL_MAX_MSGS)
23 return -EINVAL;
24 /*获得用户空间传入的消息数组
25 rdwr_pa = (struct i2c_msg *)
26 kmalloc(rdwr_arg.nmsgs * sizeof(struct i2c_msg),
27 GFP_KERNEL);
28 if (rdwr_pa == NULL) return -ENOMEM;
29 if (copy_from_user(rdwr_pa, rdwr_arg.msgs,
30        rdwr_arg.nmsgs * sizeof(struct i2c_msg))) {
31 kfree(rdwr_pa);
32 return -EFAULT;
33 }
34 data_ptrs = kmalloc(rdwr_arg.nmsgs * sizeof(u8 __user *), GFP_KERNEL);
35 if (data_ptrs == NULL) {
36 kfree(rdwr_pa);
37 return -ENOMEM;
38 }
39 res = 0;
40 for( i=0; i 8192) {
43     res = -EINVAL;
44     break;
45 }
46 data_ptrs = (u8 __user *)rdwr_pa.buf;
47 rdwr_pa.buf = kmalloc(rdwr_pa.len, GFP_KERNEL);
48 if(rdwr_pa.buf == NULL) {
49     res = -ENOMEM;
50     break;
51 }
52 if(copy_from_user(rdwr_pa.buf,
53     data_ptrs,
54     rdwr_pa.len)) {
55    ++i; /* Needs to be kfreed too */
56    res = -EFAULT;
57     break;
58 }
59 }
60 if (res adapter,
70 rdwr_pa,
71 rdwr_arg.nmsgs);
72 while(i-- > 0) { /*如果是读消息,把值拷贝到用户空间*/
73 if( res>=0 && (rdwr_pa.flags & I2C_M_RD)) {
74     if(copy_to_user(
75    data_ptrs,
76    rdwr_pa.buf,
77    rdwr_pa.len)) {
78    res = -EFAULT;
79     }
80 }
81 kfree(rdwr_pa.buf);
82 }
83 kfree(data_ptrs);
84 kfree(rdwr_pa);
85 return res;
86   case I2C_SMBUS:
87 ...
88   default:
89 return i2c_control(client,cmd,arg);
90   }
91   return 0;
92 }
常用的IOCTL包括I2C_SLAVE(设置从设备地址)、I2C_RETRIES(没有收到设备ACK情况下的重试次数,缺省为1)、I2C_TIMEOU(超时)以及I2C_RDWR。
由第17~19行可以看出,应用程序需要通过i2c_rdwr_ioctl_data结构体来给内核传递I2C消息,这个结构体定义如代码清单15.22,i2c_msg数组指针及消息数量就被包含在i2c_rdwr_ioctl_data结构体中。
代码清单15.22 i2c_rdwr_ioctl_data结构体
1 struct i2c_rdwr_ioctl_data {
2   struct i2c_msg __user *msgs; /* I2C消息指针 */
3   __u32 nmsgs; /* I2C消息数量 */
4 };
代码清单15.23和代码清单15.24分别演示了直接通过read()、write()接口和O_RDWR IOCTL读写I2C设备的例子。
代码清单15.23 直接通过read()/write()读写I2C设备
1   #include
2   #include
3   #include
4   #include
5   #include
6   #include
7   #include
8  
9   #define I2C_RETRIES     0x0701
10 #define I2C_TIMEOUT     0x0702
11 #define I2C_SLAVE    0x0703
12
13 int main(int argc, char **argv)
14 {
15 unsigned int fd;
16 unsigned short mem_addr;
17 unsigned short size;
18 unsigned short idx;
19 #define BUFF_SIZE 32
20 char buf[BUFF_SIZE];
21 char cswap;
22 union
23 {
24     unsigned short addr;
25     char bytes[2];
26 } tmp;
27
28 if (argc   BUFF_SIZE)
37     size = BUFF_SIZE;
38
39 fd = open(argv[1], O_RDWR);
40
41 if (!fd)
42 {
43     printf("Error on opening the device file\n");
44     return 0;
45 }
46
47 ioctl(fd, I2C_SLAVE, 0x50); /* 设置eeprom地址 */
48 ioctl(fd, I2C_TIMEOUT, 1); /* 设置超时 */
49 ioctl(fd, I2C_RETRIES, 1); /* 设置重试次数 */
50
51 for (idx = 0; idx
2   #include
3   #include
4   #include
5   #include
6   #include
7   #include
8   #include
9   #include
10 #include
11
12 #define MAX_I2C_MSG       2
13
14 #define I2C_RETRIES     0x0701
15 #define I2C_TIMEOUT     0x0702
16 #define I2C_RDWR        0x0707
17
18 struct i2c_msg
19 {
20 __u16 addr; /* 从地址 */
21 __u16 flags;
22 #define I2C_M_RD        0x01
23 __u8 *buf; /* 消息数据指针 */
24 };
25 struct i2c_rdwr_ioctl_data
26 {
27 struct i2c_msg *msgs; /* i2c_msg[]指针 */
28 int nmsgs; /* i2c_msg数量 */
29 };
30
31 int main(int argc, char **argv)
32 {
33 struct i2c_rdwr_ioctl_data work_queue;
34 unsigned int idx;
35 unsigned int fd;
36 unsigned short start_address;
37 int ret;
38
39 if (argc 编写的S3C2410的I2C总线驱动,在Internet上我们还可以获得其他版本的S3C2410 I2C总线驱动源代码,如Steve Hein为三星SMDK2410参考板编写的i2c-s3c2410.c、i2c-algo-s3c2410.c文件,对比发现,它与Ben Dooks版本的驱动实现风格迥异。
这 里分析内核中自带的Ben Dooks版本驱动,它同时支持S3C2410和S3C2440。图15.8给出了S3C2410驱动中的主要函数与15.3节模板函数的对应关系,由于 实现通信方法的方式不一样,模板的一个函数可能对应于S3C2410 I2C总线驱动的多个函数。

图15.8 I2C总线驱动模板与S3C2410 I2C总线驱动的映射
15.5.3 S3C2410 I2C适配器驱动模块加载与卸载
I2C适配器驱动被作为一个单独的模块被加载进内核,在模块的加载和卸载函数中,只需注册和注销一个platform_driver结构体,如代码清单15.25。
代码清单15.25 S3C2410 I2C总线驱动模块加载与卸载
1   static int __init i2c_adap_s3c_init(void)
2   {
3 int ret;
4  
5 ret = platform_driver_register(&s3c2410_i2c_driver);
6 if (ret == 0) {
7 ret = platform_driver_register(&s3c2440_i2c_driver);
8 if (ret)
9     platform_driver_unregister(&s3c2410_i2c_driver);
10   }
11
12   return ret;
13 }
14
15 static void __exit i2c_adap_s3c_exit(void)
16 {
17   platform_driver_unregister(&s3c2410_i2c_driver);
18   platform_driver_unregister(&s3c2440_i2c_driver);
19 }
20 module_init(i2c_adap_s3c_init);
21 module_exit(i2c_adap_s3c_exit);
platform_driver结构体包含了具体适配器的probe()函数、remove()函数、resume()函数指针等信息,它需要被定义和赋值,如代码清单15.26。
代码清单15.26 platform_driver结构体
1 static struct platform_driver s3c2410_i2c_driver = {
2   .probe   = s3c24xx_i2c_probe,
3   .remove   = s3c24xx_i2c_remove,
4   .resume   = s3c24xx_i2c_resume,
5   .driver   = {
6 .owner = THIS_MODULE,
7 .name = "s3c2410-i2c",
8   },
9 };
当 通过Linux内核源代码/drivers/base/platform.c文件中定义platform_driver_unregister()函数注 册platform_driver结构体时,其中probe指针指向的s3c24xx_i2c_probe()函数将被调用以初始化适配器硬件,如代码清 单15.27。
代码清单15.27 S3C2410 I2C总线驱动中的s3c24xx_i2c_probe函数
1   static int s3c24xx_i2c_probe(struct platform_device *pdev)
2   {
3 struct s3c24xx_i2c *i2c = &s3c24xx_i2c;
4 struct resource *res;
5 int ret;
6  
7 /* 使能I2C的时钟 */
8 i2c->dev = &pdev->dev;
9 i2c->clk = clk_get(&pdev->dev, "i2c");
10   if (IS_ERR(i2c->clk)) {
11 dev_err(&pdev->dev, "cannot get clock\n");
12 ret = -ENOENT;
13 goto out;
14   }
15   clk_enable(i2c->clk);
16
17   /* 映射寄存器 */
18   res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
19   if (res == NULL) {
20 dev_err(&pdev->dev, "cannot find IO resource\n");
21 ret = -ENOENT;
22 goto out;
23   }
24   i2c->ioarea = request_mem_region(res->start, (res->end-res->start)+1,
25    pdev->name);
26   if (i2c->ioarea == NULL) {
27 dev_err(&pdev->dev, "cannot request IO\n");
28 ret = -ENXIO;
29 goto out;
30   }
31
32   i2c->regs = ioremap(res->start, (res->end-res->start)+1);
33   if (i2c->regs == NULL) {
34 dev_err(&pdev->dev, "cannot map IO\n");
35 ret = -ENXIO;
36 goto out;
37   }
38
39   /* 设置I2C的信息块 */
40   i2c->adap.algo_data = i2c;
41   i2c->adap.dev.parent = &pdev->dev;
42
43   /* 初始化I2C控制器 */
44   ret = s3c24xx_i2c_init(i2c);
45   if (ret != 0)
46 goto out;
47
48   /* 申请中断 */
49   res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
50   if (res == NULL) {
51 ret = -ENOENT;
52 goto out;
53   }
54   ret = request_irq(res->start, s3c24xx_i2c_irq, SA_INTERRUPT,
55    pdev->name, i2c);
56   if (ret != 0) {
57 goto out;
58   }
59   i2c->irq = res;
60
61   ret = i2c_add_adapter(&i2c->adap); /*添加i2c_adapter*/
62   if (ret algo_data;
5 int retry;
6 int ret;
7  
8 for (retry = 0; retry retries; retry++) {
9 ret = s3c24xx_i2c_doxfer(i2c, msgs, num);
10 if (ret != -EAGAIN)
11 return ret;
12 udelay(100);
13   }
14  
15   return -EREMOTEIO;
16 }
17
18 static int s3c24xx_i2c_doxfer(struct s3c24xx_i2c *i2c, struct i2c_msg *msgs, int num)
19 {
20   unsigned long timeout;
21   int ret;
22
23   ret = s3c24xx_i2c_set_master(i2c);
24   if (ret != 0) {
25 ret = -EAGAIN;
26 goto out;
27   }
28
29   spin_lock_irq(&i2c->lock);
30   i2c->msg     = msgs;
31   i2c->msg_num = num;
32   i2c->msg_ptr = 0;
33   i2c->msg_idx = 0;
34   i2c->state = STATE_START;
35   s3c24xx_i2c_enable_irq(i2c);
36   s3c24xx_i2c_message_start(i2c, msgs);
37   spin_unlock_irq(&i2c->lock);
38  
39   timeout = wait_event_timeout(i2c->wait, i2c->msg_num == 0, HZ * 5);
40
41   ret = i2c->msg_idx;
42
43   if (timeout == 0)
44 dev_dbg(i2c->dev, "timeout\n");
45   else if (ret != num)
46 dev_dbg(i2c->dev, "incomplete xfer (%d)\n", ret);
47
48   msleep(1);/* 确保停止位已经被传递 */
49
50   out:
51   return ret;
52 }
53
54 static void s3c24xx_i2c_message_start(struct s3c24xx_i2c *i2c,
55           struct i2c_msg *msg)
56 {
57   unsigned int addr = (msg->addr & 0x7f) flags & I2C_M_RD) {
65 stat |= S3C2410_IICSTAT_MASTER_RX;
66 addr |= 1;
67   } else
68 stat |= S3C2410_IICSTAT_MASTER_TX;
69   if (msg->flags & I2C_M_REV_DIR_ADDR)
70 addr ^= 1;
71
72   s3c24xx_i2c_enable_ack(i2c);/*如果要使能ACK,则使能*/
73
74   iiccon = readl(i2c->regs + S3C2410_IICCON);
75   writel(stat, i2c->regs + S3C2410_IICSTAT);
76   writeb(addr, i2c->regs + S3C2410_IICDS);
77
78   udelay(1);/*在发送新的开始位前延迟1位*/
79   writel(iiccon, i2c->regs + S3C2410_IICCON);
80   stat |=   S3C2410_IICSTAT_START;
81   writel(stat, i2c->regs + S3C2410_IICSTAT);
82 }
s3c24xx_i2c_xfer()函数调用s3c24xx_i2c_doxfer()函数传输I2C消息,第8行的循环意味着最多可以重试adap->retries次。
s3c24xx_i2c_doxfer()首先将S3C2410的I2C适配器设置为I2C主设备,其后初始化s3c24xx_i2c结构体,使能I2C中断,并调用s3c24xx_i2c_message_start()函数启动I2C消息的传输。
s3c24xx_i2c_message_start()函数写S3C2410适配器对应的控制寄存器向I2C从设备传递开始位和从设备地址。
上 述代码只是启动了I2C消息数组的传输周期,并没有完整实现图15.3中给出的algorithm master_xfer流程。这个流程的完整实现需要借助I2C适配器上的中断来步步推进。代码清单15.32给出了S3C2410 I2C适配器中断处理函数以及其依赖的i2s_s3c_irq_nextbyte()函数的源代码。
代码清单15.32 S3C2410 I2C适配器中断处理函数
1 static irqreturn_t s3c24xx_i2c_irq(int irqno, void *dev_id,
2       struct pt_regs *regs)
3 {
4 struct s3c24xx_i2c *i2c = dev_id;
5 unsigned long status;
6 unsigned long tmp;
7
8 status = readl(i2c->regs + S3C2410_IICSTAT);
9 if (status & S3C2410_IICSTAT_ARBITR) {
10 ...  
11 }
12  
13 if (i2c->state == STATE_IDLE) {
14 tmp = readl(i2c->regs + S3C2410_IICCON);
15 tmp &= ~S3C2410_IICCON_IRQPEND;
16 writel(tmp, i2c->regs +   S3C2410_IICCON);
17 goto out;
18 }
19
20 i2s_s3c_irq_nextbyte(i2c, status);/* 把传输工作进一步推进 */
21  
22 out:
23 return IRQ_HANDLED;
24   }
25  
26   static int i2s_s3c_irq_nextbyte(struct s3c24xx_i2c *i2c, unsigned long iicstat)
27   {
28 unsigned long tmp;
29 unsigned char byte;
30 int ret = 0;
31  
32 switch (i2c->state) {
33 case STATE_IDLE:
34 goto out;
35 break;
36 case STATE_STOP:
37 s3c24xx_i2c_disable_irq(i2c);  
38 goto out_ack;
39 case STATE_START:
40 /* 我们最近做的一件事是启动一个新I2C消息*/
41 if (iicstat   & S3C2410_IICSTAT_LASTBIT &&
42        !(i2c->msg->flags & I2C_M_IGNORE_NAK)) {
43     /* 没有收到ACK */
44     s3c24xx_i2c_stop(i2c, -EREMOTEIO);
45     goto out_ack;
46 }
47  
48 if (i2c->msg->flags & I2C_M_RD)
49     i2c->state = STATE_READ;
50 else
51     i2c->state = STATE_WRITE;
52  
53 /* 仅一条消息,而且长度为0(主要用于适配器探测),发送停止位*/
54 if (is_lastmsg(i2c) && i2c->msg->len == 0) {
55     s3c24xx_i2c_stop(i2c, 0);
56     goto out_ack;
57 }
58  
59 if (i2c->state == STATE_READ)
60     goto prepare_read;
61 /* 进入写状态 */
62 case STATE_WRITE:
63 retry_write:
64 if (!is_msgend(i2c)) {
65     byte = i2c->msg->buf[i2c->msg_ptr++];
66     writeb(byte, i2c->regs + S3C2410_IICDS);
67    
68 } else if (!is_lastmsg(i2c)) {
69     /* 推进到下一条消息 */
70     i2c->msg_ptr = 0;
71     i2c->msg_idx ++;
72     i2c->msg++;
73    
74     /* 检查是否要为该消息产生开始位 */
75     if (i2c->msg->flags & I2C_M_NOSTART) {
76    if (i2c->msg->flags & I2C_M_RD) {
77    s3c24xx_i2c_stop(i2c, -EINVAL);
78    }
79    goto retry_write;
80     } else {
81    /* 发送新的开始位 */
82    s3c24xx_i2c_message_start(i2c, i2c->msg);
83    i2c->state = STATE_START;
84     }
85 } else {
86     s3c24xx_i2c_stop(i2c, 0);/* send stop */
87 }
88 break;
89 case STATE_READ:
90 /* 有一个字节可读,看是否还有消息要处理   */
91 if (!(i2c->msg->flags & I2C_M_IGNORE_NAK) &&
92        !(is_msglast(i2c) && is_lastmsg(i2c))) {
93  
94     if (iicstat & S3C2410_IICSTAT_LASTBIT) {
95    dev_dbg(i2c->dev, "READ: No Ack\n");
96  
97    s3c24xx_i2c_stop(i2c, -ECONNREFUSED);
98    goto out_ack;
99     }
100 }
101 byte = readb(i2c->regs + S3C2410_IICDS);
102 i2c->msg->buf[i2c->msg_ptr++] = byte;
103
104   prepare_read:
105 if (is_msglast(i2c)) {/* last byte of buffer */
106 if (is_lastmsg(i2c))
107     s3c24xx_i2c_disable_ack(i2c);
108
109 } else if (is_msgend(i2c)) {
110 /* 还有消息要处理吗? */
111 if (is_lastmsg(i2c)) {
112     s3c24xx_i2c_stop(i2c, 0);/* last message, send stop and complete */
113 } else {
114     /* 推进到下一条消息 */
115     i2c->msg_ptr = 0;
116     i2c->msg_idx++;
117     i2c->msg++;
118 }
119 }
120 break;
121   }
122
123   /* irq清除 */
124   out_ack:
125   tmp = readl(i2c->regs + S3C2410_IICCON);
126   tmp &= ~S3C2410_IICCON_IRQPEND;
127   writel(tmp, i2c->regs + S3C2410_IICCON);
128   out:
129   return ret;
130 }
中 断处理函数s3c24xx_i2c_irq()主要通过调用i2s_s3c_irq_nextbyte()函数进行传输工作的进一步推进。 i2s_s3c_irq_nextbyte()函数通过switch(i2c->state)语句分成i2c->state的不同状态进行处 理,在每种状态下,先检查i2c->state的状态与硬件寄存器应该处于的状态是否一致,如果不一致,则证明有误,直接返回。当I2C处于读状态 STATE_READ或写状态STATE_WRITE时,通过is_lastmsg()函数判断是否传输的是最后一条I2C消息,如果是,则产生停止位, 否则通过i2c->msg_idx++、i2c->msg++推进到下一条消息。
15.6 实例:SAA7113H视频AD芯片I2C设备驱动
15.6.1 SAA7113H视频AD芯片硬件描述
如图15.9,SAA7113H是飞利浦半导体推出的9位视频AD芯片,它可以选择4路视频输入中的1路,并采样为9位的数字信号。

图15.9 SAA7113H输入、输出与I2C接口
对SAA7113H 输入通道的选择以及采样方式的设置都需通过其I2C接口进行,以0x4A地址可读SAA7113H寄存器,以0x4B可写SAA7113H寄存器。 SAA7113H的I2C接口连接在S3C2410的I2C控制器上,作为S3C2410 I2C控制器的从设备。SAA7113H的写时序与图15.7给出的写时序是一致的,但是读时序则需通过2条I2C消息解决,在第1条消息中应该给 SAA7113H写入寄存器子地址,如图15.10。

图15.10 SAA7113H读时序
15.6.2 SAA7113H视频AD芯片驱动模块加载与卸载
由于不需要给SAA7113H实现文件操作接口,在设备驱动模块的加载和卸载函数中均不再需要注册和注销字符设备的操作,代码清单15.33给出了SAA7113H设备驱动模块加载和卸载的源代码。
代码清单15.33 SAA7113H设备驱动模块加载和卸载函数
1   static int __init saa711x_init (void)
2   {
3 return i2c_add_driver(&i2c_driver_saa711x); //注册i2c_driver
4   }
5  
6   static void __exit saa711x_exit (void)
7   {
8 i2c_del_driver(&i2c_driver_saa711x); //注销i2c_driver
9   }
10
11 module_init(saa711x_init);
12 module_exit(saa711x_exit);
15.6.3 SAA7113H设备驱动i2c_driver成员函数
如 代码清单15.34,SAA7113H设备驱动模块加载和卸载中添加和删除的i2c_driver_saa711x结构体的attach_adapter 指针被赋值为saa711x_attach_adapter,detach_client指针被赋值为saa711x_detach_client,而 command指针被赋值为saa711x_command,代码清单15.35给出了这3个函数的实现。
代码清单15.34 SAA7113H设备驱动i2c_driver结构体
1 static struct i2c_driver i2c_driver_saa711x = {
2   .driver = {
3   .name = "saa711x",
4   },
5   .id = I2C_DRIVERID_SAA711X,
6   /* 成员函数 */
7   .attach_adapter = saa711x_attach_adapter,
8   .detach_client = saa711x_detach_client,
9   .command = saa711x_command,
10 };
代码清单15.35 SAA7113H设备驱动i2c_driver成员函数
1   saa711x_attach_adapter (struct i2c_adapter *adapter)
2   {
3 dprintk(1, KERN_INFO "saa711x.c: starting probe for adapter %s (0x%x)\n",
4 I2C_NAME(adapter), adapter->id);
5
6 return i2c_probe(adapter, &addr_data, &saa711x_detect_client);
7 //saa711x_detect_client会调用i2c_set_clientdata()、i2c_attach_client()
8   }
9  
10 static int saa711x_detach_client (struct i2c_client *client)
11 {
12   struct saa711x *decoder = i2c_get_clientdata(client);
13   int err;
14
15   err = i2c_detach_client(client);
16   if (err) {
17 return err;
18   }
19
20   kfree(decoder);
21   kfree(client);
22
23   return 0;
24 }
25
26 static int saa711x_command(struct i2c_client *client, unsigned int cmd, void
27 *arg)
28 {
29 struct saa711x *decoder = i2c_get_clientdata(client);
30
31 switch (cmd)   //处理不同的命令
32 {
33     case 0:
34     case DECODER_INIT:
35    ...
36     case DECODER_DUMP:
37    ...
38     case DECODER_GET_CAPABILITIES:
39    ...
40     case DECODER_GET_STATUS:
41    ...
42     case DECODER_SET_VBI_BYPASS:
43    ...
44     case DECODER_SET_NORM:
45    ...
46     case DECODER_SET_INPUT:
47    {
48       int *iarg = arg;
49       if (*iarg   9)
50       {
51           return   - EINVAL;
52       }
53       if (decoder->input !=   *iarg)
54       {
55           decoder->input =   *iarg;
56           saa711x_write(client, 0x02, (decoder->reg[0x02] &0xf0) | decoder
57          ->input);
58           saa711x_write(client, 0x09, (decoder->reg[0x09] &0x7f) | ((decoder
59          ->input > 3) ? 0x80 : 0));
60       }
61    }
62    break;
63
64     case DECODER_SET_OUTPUT:
65    ...
66     case DECODER_ENABLE_OUTPUT:
67    ...
68     case DECODER_SET_PICTURE:
69    ...
70     default:
71    return   - EINVAL;
72 }
73
74 return 0;
75 }
15.8本章小结
Linux I2C驱动体系结构有相当的复杂度,它主要由3部分组成,即I2C核心、I2C总线驱动和I2C设备驱动。I2C核心是I2C总线驱动和I2C设备驱动的 中间枢纽,它以通用的、与平台无关的接口实现了I2C中设备与适配器的沟通。I2C总线驱动填充i2c_adapter和i2c_algorithm结构 体,I2C设备驱动填充i2c_driver和i2c_client结构体。
另外,系统中i2c-dev.c文件定义的主设备号为89的设备可以方便地给应用程序提供读写I2C设备寄存器的能力,使得工程师大多数时候并不需要为具体的I2C设备驱动定义文件操作接口。
最后,工程师在设计I2C设备驱动的程序,并不是一定要遵守Linux I2C驱动的体系结构,完全可以把它当作一个普通的字符设备处理,如果不考虑融入Linux现有的I2C框架的话。

图15.4 I2C设备驱动模块加载连锁反应图15.5 I2C设备驱动模块卸载连锁反应 图15.6 I2C设备读写完整流程 图15.7 i2cdev_read和i2cdev_write函数对应时序 图15.8 RepStart模式 SAA7113H输入、输出与I2C接口

阅读(1435) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~