网站建设协议书 保密条款自适应网站cms
news/
2025/9/26 3:31:18/
文章来源:
网站建设协议书 保密条款,自适应网站cms,河北建设集团股份有限公司,网站网页区别是什么从上面的分析可知#xff0c;虽然I2C硬件体系结构比较简单#xff0c;但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板#xff0c;面对复杂的 Linux I2C子系统#xff0c;应该如何下手写驱动呢#xff1f;究竟有哪些是需要亲自做的#xff0c;哪些是… 从上面的分析可知虽然I2C硬件体系结构比较简单但是I2C体系结构在Linux中的实现却相当复杂。当工程师拿到实际的电路板面对复杂的 Linux I2C子系统应该如何下手写驱动呢究竟有哪些是需要亲自做的哪些是内核已经提供的呢理清这个问题非常有意义可以使我们面对具体问题时迅速地抓住重点。 一方面适配器驱动可能是Linux内核本身还不包含的。另一方面挂接在适配器上的具体设备驱动可能也是Linux不存在的。即便上述设备驱动都存在于Linux内核中其基于的平台也可能与我们的电路板不一样。因此工程师要实现的主要工作将包括 ? 提供I2C适配器的硬件驱动探测、初始化I2C适配器如申请I2C的I/O地址和中断号、驱动CPU控制的I2C适配器从硬件上产生各种信号以及处理I2C中断等。 ? 提供I2C适配器的algorithm用具体适配器的xxx_xfer()函数填充i2c_algorithm的master_xfer指针并把i2c_algorithm指针赋值给i2c_adapter的algo指针。 ? 实现I2C设备驱动与i2c_driver接口用具体设备yyy的yyy_attach_adapter()函数指针、 yyy_detach_client()函数指针和yyy_command()函数指针的赋值给i2c_driver的attach_adapter、 detach_adapter和detach_client指针。 ? 实现I2C设备驱动的文件操作接口即实现具体设备yyy的yyy_read()、yyy_write()和yyy_ioctl()函数等。 上述工作中1、2属于I2C总线驱动3、4属于I2C设备驱动做完这些工作系统会增加两个内核模块。本章第34节将详细分析这些工作的实施方法给出设计模板而5~6节将给出两个具体的实例。 15.2 Linux I2C核心 I2C核心drivers/i2c/i2c-core.c中提供了一组不依赖于硬件平台的接口函数这个文件一般不需要被工程师修改但是理解其中的主要函数非常关键因为I2C总线驱动和设备驱动之间依赖于I2C核心作为纽带。I2C核心中的主要函数包括 ? 增加/删除i2c_adapter int i2c_add_adapter(struct i2c_adapter *adap); int i2c_del_adapter(struct i2c_adapter *adap); ? 增加/删除i2c_driver int i2c_register_driver(struct module *owner, struct i2c_driver *driver); int i2c_del_driver(struct i2c_driver *driver); inline int i2c_add_driver(struct i2c_driver *driver); ? i2c_client依附/脱离 int i2c_attach_client(struct i2c_client *client); int i2c_detach_client(struct i2c_client *client); 当一个具体的client被侦测到并被关联的时候设备和sysfs文件将被注册。相反地在client被取消关联的时候sysfs文件和设备也被注销如代码清单15.6。 代码清单15.6 I2C核心client attach/detach函数 1 int i2c_attach_client(struct i2c_client *client) 2 { 3 ... 4 device_register(client-dev); 5 device_create_file(client-dev, dev_attr_client_name); 6 7 return 0; 8 } 9 10 int i2c_detach_client(struct i2c_client *client) 11 { 12 ... 13 device_remove_file(client-dev, dev_attr_client_name); 14 device_unregister(client-dev); 15 ... 16 } 4i2c传输、发送和接收 int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num); int i2c_master_send(struct i2c_client *client,const char *buf ,int count); int i2c_master_recv(struct i2c_client *client, char *buf ,int count); i2c_transfer ()函数用于进行I2C适配器和I2C设备之间的一组消息交互i2c_master_send()函数和i2c_master_recv()函数内部会调用i2c_transfer()函数分别完成一条写消息和一条读消息如代码清单15.7、15.8。 代码清单15.7 I2C核心i2c_master_send函数 1 int i2c_master_send(struct i2c_client *client,const char *buf ,int count) 2 { 3 int ret; 4 struct i2c_adapter *adapclient-adapter; 5 struct i2c_msg msg; 6 /*构造一个写消息*/ 7 msg.addr client-addr; 8 msg.flags client-flags I2C_M_TEN; 9 msg.len count; 10 msg.buf (char *)buf; 11 /*传输消息*/ 12 ret i2c_transfer(adap, msg, 1); 13 14 return (ret 1) ? count : ret; 15 } 代码清单15.8 I2C核心i 2c_master_recv函数 1 int i2c_master_recv(struct i2c_client *client, char *buf ,int count) 2 { 3 struct i2c_adapter *adapclient-adapter; 4 struct i2c_msg msg; 5 int ret; 6 /*构造一个读消息*/ 7 msg.addr client-addr; 8 msg.flags client-flags I2C_M_TEN; 9 msg.flags | I2C_M_RD; 10 msg.len count; 11 msg.buf buf; 12 /*传输消息*/ 13 ret i2c_transfer(adap, msg, 1); 14 15 /* 成功1条消息被处理 返回读的字节数 */ 16 return (ret 1) ? count : ret; 17 } i2c_transfer()函数本身不具备驱动适配器物理硬件完成消息交互的能力它只是寻找到i2c_adapter对应的i2c_algorithm并使用i2c_algorithm的master_xfer()函数真正驱动硬件流程如代码清单15.9。 代码清单15.9 I2C核心i 2c_transfer函数 1 int i2c_transfer(struct i2c_adapter * adap, struct i2c_msg *msgs, int num) 2 { 3 int ret; 4 5 if (adap-algo-master_xfer) { 6 down(adap-bus_lock); 7 ret adap-algo-master_xfer(adap,msgs,num); /* 消息传输 */ 8 up(adap-bus_lock); 9 return ret; 10 } else { 11 dev_dbg(adap-dev, I2C level transfers not supported\n); 12 return -ENOSYS; 13 } 14 } 5I2C控制命令分派 下面函数有助于将发给I2C适配器设备文件ioctl的命令分派给对应适配器的algorithm的algo_control()函数或i2c_driver的command()函数 int i2c_control(struct i2c_client *client, unsigned int cmd, unsigned long arg); void i2c_clients_command(struct i2c_adapter *adap, unsigned int cmd, void *arg); 15.3 Linux I2C总线驱动 15.3.1 I2C适配器驱动加载与卸载 I2C总线驱动模块的加载函数要完成两个工作 ? 初始化I2C适配器所使用的硬件资源申请I/O地址、中断号等。 ? 通过i2c_add_adapter()添加i2c_adapter的数据结构当然这个i2c_adapter数据结构的成员已经被xxx适配器的相应函数指针所初始化。 I2C总线驱动模块的卸载函数要完成的工作与加载函数的相反 ? 释放I2C适配器所使用的硬件资源释放I/O地址、中断号等。 ? 通过i2c_del_adapter()删除i2c_adapter的数据结构。 代码清单15.10给出了I2C适配器驱动模块加载和卸载函数的模板。 代码清单15.10 I2C总线驱动模块加载和卸载函数模板 1 static int __init i2c_adapter_xxx_init(void) 2 { 3 xxx_adpater_hw_init(); 4 i2c_add_adapter(xxx_adapter); 5 } 6 7 static void __exit i2c_adapter_xxx_exit(void) 8 { 9 xxx_adpater_hw_free(); 10 i2c_del_adapter(xxx_adapter); 11 } 上述代码中xxx_adpater_hw_init()和xxx_adpater_hw_free()函数的实现都与具体的CPU和I2C设备硬件直接相关。 15.3.2 I2C总线通信方法 我们需要为特定的I2C适配器实现其通信方法主要实现i2c_algorithm的master_xfer()函数和functionality()函数。 functionality ()函数非常简单用于返回algorithm所支持的通信协议如I2C_FUNC_I2C、I2C_FUNC_10BIT_ADDR、 I2C_FUNC_SMBUS_READ_BYTE、I2C_FUNC_SMBUS_WRITE_BYTE等。 master_xfer()函数在I2C适配器上完成传递给它的i2c_msg数组中的每个I2C消息代码清单15.11给出了xxx设备的master_xfer()函数模板。 代码清单15.11 I2C总线驱动master_xfer函数模板 1 static int i2c_adapter_xxx_xfer(struct i2c_adapter *adap, struct i2c_msg *msgs, 2 int num) 3 { 4 ... 5 for (i 0; i num; i) 6 { 7 i2c_adapter_xxx_start(); /*产生开始位*/ 8 /*是读消息*/ 9 if (msgs[i]-flags I2C_M_RD) 10 { 11 i2c_adapter_xxx_setaddr((msg-addr 1) | 1); /*发送从设备读地址*/ 12 i2c_adapter_xxx_wait_ack(); /*获得从设备的ack*/ 13 i2c_adapter_xxx_readbytes(msgs[i]-buf, msgs[i]-len); /*读取msgs[i] 14 -len长的数据到msgs[i]-buf*/ 15 } 16 else 17 /*是写消息*/ 18 { 19 i2c_adapter_xxx_setaddr(msg-addr 1); /*发送从设备写地址*/ 20 i2c_adapter_xxx_wait_ack(); /*获得从设备的ack*/ 21 i2c_adapter_xxx_writebytes(msgs[i]-buf, msgs[i]-len); /*读取msgs[i] 22 -len长的数据到msgs[i]-buf*/ 23 } 24 } 25 i2c_adapter_xxx_stop(); /*产生停止位*/ 26 } 上 述代码实际上给出了一个master_xfer()函数处理I2C消息数组的流程对于数组中的每个消息判断消息类型若为读消息则赋从设备地址为 (msg-addr 1) | 1否则为msg-addr 1。对每个消息产生1个开始位紧接着传送从设备地址然后开始数据的发送或接收对最后的消息还需产生1个停止位。图15.3描述了整个 master_xfer()完成的时序。 图15.3 algorithm中master_xfer的时序 master_xfer()函 数模板中的i2c_adapter_xxx_start()、i2c_adapter_xxx_setaddr()、 i2c_adapter_xxx_wait_ack()、i2c_adapter_xxx_readbytes()、 i2c_adapter_xxx_writebytes()和i2c_adapter_xxx_stop()函数用于完成适配器的底层硬件操作与I2C 适配器和CPU的具体硬件直接相关需要由工程师根据芯片的数据手册来实现。 i2c_adapter_xxx_readbytes()用于从从设备上接收一串数据i2c_adapter_xxx_writebytes()用于向从设备写入一串数据这两个函数的内部也会涉及到I2C总线协议中的ACK应答。 master_xfer ()函数的实现在形式上会很多样即便是Linux内核源代码中已经给出的一些I2C总线驱动的master_xfer()函数由于由不同的组织或个人 完成风格上的差别也非常大不一定能与模板完全对应如master_xfer()函数模板给出的消息处理是顺序进行的而有的驱动以中断方式来完成这 个流程第5节的实例即是如此。不管具体怎么实施流程的本质都是不变的。因为这个流程不以驱动工程师的意志为转移最终由I2C总线硬件上的通信协议 决定。 多数I2C总线驱动会定义一个xxx_i2c结构体作为i2c_adapter的algo_data类似“私有数据”其中包含 I2C消息数组指针、数组索引及I2C适配器algorithm访问控制用的自旋锁、等待队列等而master_xfer()函数完成消息数组中消息的 处理也可通过对xxx_i2c结构体相关成员的访问来控制。代码清单15.12给出了xxx_i2c结构体的定义与图15.2中的xxx_i2c是对应 的。 代码清单15.12 xxx_i2c结构体模板 1 struct xxx_i2c 2 { 3 spinlock_t lock; 4 wait_queue_head_t wait; 5 struct i2c_msg *msg; 6 unsigned int msg_num; 7 unsigned int msg_idx; 8 unsigned int msg_ptr; 9 ... 10 struct i2c_adapter adap; 11 }; 15.4 Linux I2C设备驱动 I2C 设备驱动要使用i2c_driver和i2c_client数据结构并填充其中的成员函数。i2c_client一般被包含在设备的私有信息结构体 yyy_data中而i2c_driver则适宜被定义为全局变量并初始化代码清单15.13显示了被初始化的i2c_driver。 代码清单15.13 初始化的i2c_driver 1 static struct i2c_driver yyy_driver 2 { 3 .driver 4 { 5 .name yyy, 6 } , 7 .attach_adapter yyy_attach_adapter, 8 .detach_client yyy_detach_client, 9 .command yyy_command, 10 }; 15.4.1 Linux I2C设备驱动模块加载与卸载 I2C设备驱动模块加载函数通用的方法是在I2C设备驱动模块加载函数中完成两件事 ? 通过register_chrdev()函数将I2C设备注册为一个字符设备。 ? 通过I2C核心的i2c_add_driver()函数添加i2c_driver。 在模块卸载函数中需要做相反的两件事 ? 通过I2C核心的i2c_del_driver()函数删除i2c_driver。 ? 通过unregister_chrdev()函数注销字符设备。 代码清单15.14给出了I2C设备驱动加载与卸载函数的模板。 代码清单15.14 I2C设备驱动模块加载与卸载函数模板 1 static int __init yyy_init(void) 2 { 3 int res; 4 /*注册字符设备*/ 5 res register_chrdev(YYY_MAJOR, yyy, yyy_fops); //老内核接口 6 if (res) 7 goto out; 8 /*添加i2c_driver*/ 9 res i2c_add_driver(yyy_driver); 10 if (res) 11 goto out_unreg_class; 12 return 0; 13 14 out_unreg_chrdev: unregister_chrdev(I2C_MAJOR, i2c); 15 out: printk(KERN_ERR %s: Driver Initialisation failed\n, __FILE__); 16 return res; 17 } 18 19 static void __exit yyy_exit(void) 20 { 21 i2c_del_driver(i2cdev_driver); 22 unregister_chrdev(YYY_MAJOR, yyy); 23 } 第5行代码说明注册“yyy”这个字符设备时使用的file_operations结构体为yyy_fops15.4.3节将讲解这个结构体中成员函数的实现。 15.4.2 Linux I2C设备驱动i2c_driver成员函数 i2c_add_driver (yyy_driver)的执行会引发i2c_driver结构体中yyy_attach_adapter()函数的执行我们可以在 yyy_attach_adapter()函数里探测物理设备。为了实现探测yyy_attach_adapter()函数里面也只需简单地调用I2C 核心的i2c_probe()函数如代码清单15.15。 代码清单15.15 I2C设备驱动i2c_attach_adapter函数模板 1 static int yyy_attach_adapter(struct i2c_adapter *adapter) 2 { 3 return i2c_probe(adapter, addr_data, yyy_detect); 4 } 代码第3行传递给i2c_probe()函数的第1个参数是i2c_adapter指针第2个参数是要探测的地址数据第3个参数是具体的探测函数。要探测的地址实际列表在一个16位无符号整型数组中这个数组以I2C_CLIENT_END为最后一个元素。 i2c_probe()函数会引发yyy_detect()函数的调用可以在yyy_detect()函数中初始化i2c_client如代码清单15.16。 代码清单15.16 I2C设备驱动detect函数模板 1 static int yyy_detect(struct i2c_adapter *adapter, int address, int kind) 2 { 3 struct i2c_client *new_client; 4 struct yyy_data *data; 5 int err 0; 6 7 if (!i2c_check_functionality(adapter, I2C_FUNC_XXX) 8 goto exit; 9 10 if (!(data kzalloc(sizeof(struct yyy_data), GFP_KERNEL))) 11 { 12 err - ENOMEM; 13 goto exit; 14 } 15 16 new_client data-client; 17 new_client-addr address; 18 new_client-adapter adapter; 19 new_client-driver yyy_driver; 20 new_client-flags 0; 21 22 /* 新的client将依附于adapter */ 23 if ((err i2c_attach_client(new_client))) 24 goto exit_kfree; 25 26 yyy_init_client(new_client); 27 return 0; 28 exit_kfree: kfree(data); 29 exit: return err; 30} 代 码第10行分配私有信息结构体的内存i2c_client也被创建。第1620行对新创建的i2c_client进行初始化。第23行调用内核的 i2c_attach_client()知会I2C核心系统中包含了一个新的I2C设备。第26行代码初始化i2c_client对应的I2C设备这个 函数是硬件相关的。 图15.4描述了当I2C设备驱动的模块加载函数被调用的时候引发的连锁反应的流程。 图15.4 I2C设备驱动模块加载连锁反应 I2C 设备驱动卸载函数进行i2c_del_driver(yyy_driver)调用后会引发与yyy_driver关联的每个 i2c_client与之解除关联即yyy_detach_client()函数将因此而被调用代码清单15.17给出了函数 yyy_detach_client()的设计。 代码清单15.17 I2C设备驱动i2c_detach_client函数模板 1 static int yyy_detach_client(struct i2c_client *client) 2 { 3 int err; 4 struct yyy_data *data i2c_get_clientdata(client); 5 6 if ((err i2c_detach_client(client))) 7 return err; 8 9 kfree(data); 10 return 0; 11 } 上 述函数中第4行的i2c_get_clientdata()函数用于从yyy_data私有信息结构中的i2c_client的指针获取yyy_data 的指针。第6行调用I2C核心函数i2c_detach_client()这个函数会引发i2c_adapter的client_unregister ()函数被调用。第9行代码释放yyy_data的内存。 图15.5描述了当I2C设备驱动的模块卸载函数被调用的时候引发的连锁反应的流程。 图15.5 I2C设备驱动模块卸载连锁反应 下面开始分析i2c_driver中重要函数yyy_command()的实现它实现了针对设备的控制命令。具体的控制命令是设备相关的如对于实时钟而言命令将是设置时间和获取时间而对于视频AD设备而言命令会是设置采样方式、选择通道等。 假设yyy设备接受两类命令YYY_CMD1、YYY_CMD2而处理这两个命令的函数分别为yyy_cmd1()、yyy_cmd2()代码清单15.18给出了yyy_command()函数的设计。 代码清单15.18 I2C设备驱动command函数模板 1 static int yyy_command(struct i2c_client *client, unsigned int cmd, void 2 *arg) 3 { 4 switch (cmd) 5 { 6 case YYY_CMD1: 7 return yyy_cmd1(client, arg); 8 case YYY_CMD2: 9 return yyy_cmd2(client, arg); 10 default: 11 return - EINVAL; 12 } 13 } 具体命令的实现是通过组件i2c_msg消息数组并调用I2C核心的传输、发送和接收函数由I2C核心的传输、发送和接收函数调用I2C适配器对应的algorithm相关函数来完成的。代码清单15.19给出了一个yyy_cmd1()的例子。 代码清单15.19 I2C设备具体命令处理函数模板 1 static int yyy_cmd1(struct i2c_client *client, struct rtc_time *dt) 2 { 3 struct i2c_msg msg[2]; 4 /*第一条消息是写消息*/ 5 msg[0].addr client-addr; 6 msg[0].flags 0; 7 msg[0].len 1; 8 msg[0].buf offs; 9 /*第二条消息是读消息*/ 10 msg[1].addr client-addr; 11 msg[1].flags I2C_M_RD; 12 msg[1].len sizeof(buf); 13 msg[1].buf buf[0]; 14 15 i2c_transfer(client-adapter, msg, 2); 16 ... 17 } 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设备读写完整流程
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/917819.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!