关于驱动和设备的一点所思所想(未完待续,根据以后的学习再补充)

来源:互联网 发布:港台电视直播软件app 编辑:程序博客网 时间:2024/05/17 03:43

       我所在的公司是做车载多媒体导航方案供应商,有一天,有一个同事问了一个我们大家都觉得很奇怪的问题:市场的车载导航性能基本一致,为什么不做成这样一块板,可以兼容大家的需求,大家要哪一种功能,我们就提供给客户某种功能,这样就不用避免客户为了修改需求哪怕是一个接口功能定义,都要动到整块板子了。

      我们的公司某领导回答说:市场上的结构都不一致,结构不同导致每个PCB板都不一样,所以,即便原理图相同,PCB也需要重新做。

      那么,如果我们结构相同呢???结构相同,我们还需要结构的接口定义一样,接口定义还需要全面,能兼容所有的功能需求,这样还不够,还需要原理图一样,这样我们就可以什么都不需要改动了。此处的原理图就好比我们的驱动信息,接口就好比我们的驱动接口,

     

     

      现在回到我们软件的思路上来,其实生活中的道理基本一致,软件也有相同的问题,针对形形色色的驱动程序,如果我们大家在驱动上在抽象一层,这一层包括每个驱动的基本信息,驱动的基本操作包括读写控制等以及操作权限等等,这样我们的驱动信息与驱动结构便分开了。如果后续我要更改信息,驱动结构部分我就不需要跟着变动了。

     这一层仅仅只是针对驱动层的一个抽像,这个抽象屏蔽了器件驱动(包括同类型和不同类型器件)的差异性,但是对于linux来说,这个还不够,因为他没有屏蔽普通文件和设备文件的差异性,为此,我们又在此抽象了一层,这一层包括文件结构和文件信息(包括读写权限、操作控制等)。

     所以,我们有了如下两部分:

     文件结构和文件信息;

     驱动结构和驱动信息;

 

   

     设备信息----------由设备号来代表(主设备号代表驱动程序;次设备号代表相同的驱动程序中不同的器件);

      因为设备信息即驱动也是系统的一部分,你要想系统使用他,必须先要系统知道它是一个什么东西,否则就算是在系统里,它也不会认识你。

      所以,我们必须要像申请QQ号码一样,申请一个号码,这个号码代表你自己(驱动信息)。你必须按照腾讯的申请规范填写相关的资料信息,系统识别到这些信息符合要求后,就会把这个申请记录在一个地方,然后会分配一个QQ号码给你,这个号码就是代表你自己。

      依此来看:首先要向系统申请一个或者多个设备号,这个号可以你自己填写,但是不难理解,自己填写的设备号,在设备不多的情况下,确实可以如此,但是如果设备数量较多,不难会出现重名的情况,于是系统自动分配设备号便出现了。


      

      号码申请OK后,只是简单地将驱动和设备号链接起来,但是对于外在社会对于朋友圈而言,他们就是看到这个设备号,他们依旧不知道你。要解决这个问题,我们有两种办法:一种是直接告诉朋友,我是某某驱动程序,请你加我,设备一听,哎,我认识你,我们就匹配上了,哎,我不认识你,不好意思,我不认识你。当然,长得漂亮且看起来没有其他企图的除外。这种方式比较笨,需要花费大量的时间,而且也不及时,因为你不知道啥时候朋友们突然申请了一个QQ号,所以需要大量的时间占用大量的资源去扫描。

      很明显,这种方式不可取,那么如果我们主动向总线注册并填写其它总线上所有外设都能识别的共性出来,那么我就能识别到你了。这些共性有哪些呢?

      1、首先,你得告诉别人,你的QQ号是什么,也就是说,你必须告诉总线,你是什么设备号?(设备号就是唯一,他不像我们告诉名字以外,人家还不相信,因为名字有重复的,但是就算你告诉别人QQ号,人家也不认识你,所以还需要更多的信息-------QQ号并不是让人家认识你,QQ号只是代表你这个个人,是你这个人的抽象和虚拟);

      2、其次,你得告诉别人,你可以做什么?譬如我可以聊天,我可以打游戏等等;(现实生活中,你是什么德行朋友们当然知道,因为先认识嘛,但是驱动事先不认识,这就是驱动麻烦的地方);

      3、就因为事先不认识,所以有了更多的麻烦。除了告诉别人你可以做什么外,还不够,你还得让别人认识你,即让你们搭上桥。这时候,链表出现了。链表就好比,别人搜索QQ好友,然后把符合要求(这个要求是这个“别人”知道的)的人直接指派给他,然后你们就匹配上了。然后就可以对话了。

     4、为了不错误混淆导致对方误解,你还必须主动告诉人家,这个QQ号有几个人共用?

    

     同理,搜索你的那个别人就是所谓的设备,也需要向系统注册。把相关信息标注清晰。


     所谓的匹配,就是注册的设备和驱动 一定有吻合的地方,只是这个吻合的地方我暂时还不知道而已

====================================================================================================================================

内核要求每次出现一个设备,就要向总线汇报,会着说注册;每次出现一个驱动,也要向总线汇报,或者叫注册。比如系统初始化时,会扫描连接了哪些设备,并为每一个设备建立一个struct device变量,并为每一个驱动程序准备一个struct device_driver结构的变量。把这些量变量加入相应的链表,形成一条设备链表和一条驱动量表。这样,总线就能通过总线找到每一个设备和每一个驱动程序。

当一个struct device诞生,总线就会去driver链表找设备对应的驱动程序。如果找到就执行设备的驱动程序,否则就等待。反之亦然。

还有一个需要注意的地方:

usb_type结构体的match函数,它的两个参数一个是驱动,另一个则是设备。这个函数就是用来进行判断,总线上的驱动程序能不能处理设备的。至于这个函数什么时候调用,怎么调用,后面会有说。

====================================================================================================================================















file_operation: struct file_operations是一个字符设备把驱动的操作和设备号联系在一起的纽带,是一系列指针的集合,每个被打开的文件都对应于一系列的操作,这就是file_operations,用来执行一系列的系统调用。

struct file代表一个打开的文件,在执行file_operation中的open操作时被创建,这里需要注意的是与用户空间inode指针的区别,一个在内核,而file指针在用户空间,由c库来定义。
    struct inode被内核用来代表一个文件,注意和struct file的区别,struct inode一个是代表文件,struct file一个是代表打开的文件,struct inode包括很重要的二个成员:
dev_t i_rdev设备文件的设备号
struct cdev *i_cdev 代表字符设备的数据结构
struct inode结构是用来在内核内部表示文件的.同一个文件可以被打开好多次,所以可以对应很多struct file,但是只对应一个struct inode.




      你向系统提交相关信息后,系统觉得符合要求,同时将这些设备信息和设备号记录在一个结构体中,同时分配给设备一个设备号用以代表驱动信息;


     文件信息----------由文件描述符代表(









      

1.申请设备号

在构建字符设备之前,首先要向系统申请一个或者多个设备号。完成该工作的函数是register_chrdev_region(),该函数在<fs/char_dev.c>中定义:


  1. int register_chrdev_region(dev_t from, unsigned count, const char *name); 

其中,from是要分配的设备号范围的起始值。一般只提供from的主设备号,from的次设备号通常被设置成0。count是需要申请的连续设备号的个数。最后name是和该范围编号关联的设备名称,该名称不能超过64字节。

和大多数内核函数一样,register_chrdev_region()函数成功时返回0。错误时,返回一个负的错误码,并且不能为字符设备分配设备号。下面是一个例子代码,其申请了CS5535_GPIO_COUNT个设备号。

  1. retval = register_chrdev_region(dev_id, CS5535_GPIO_COUNT,NAME); 

在Linux中有非常多的字符设备,在人为的为字符设备分配设备号时,很可能发生冲突。Linux内核开发者一直在努力将设备号变为动态的。可以使用alloc_chrdev_region()函数达到这个目的。

  1. int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name) 

在上面的函数中,dev作为输出参数,在函数成功返回后将保存已经分配的设备号。函数有可能申请一段连续的设备号,这是dev返回第一个设备号。baseminor表示要申请的第一个次设备号,其通常设为0。count和name与register_chrdev_region()函数的对应参数一样。count表示要申请的连续设备号个数,name表示设备的名字。下面是一个例子代码,其申请了CS5535_GPIO_COUNT个设备号。

  1. retval = alloc_chrdev_region(&dev_id, 0, CS5535_GPIO_COUNT, NAME);

2.释放设备号

使用上面两种方式申请的设备号,都应该在不使用设备时,释放设备号。设备号的释放统一使用下面的函数:

  1. void unregister_chrdev_region(dev_t from, unsigned count); 

在上面这个函数中,from表示要释放的设备号,count表示从from开始要释放的设备号个数。通常,在模块的卸载函数中调用unregister_chrdev_region()函数。