day10

来源:互联网 发布:sql 时间区间条件查询 编辑:程序博客网 时间:2024/04/29 10:19
回顾:
1.linux内核等待队列机制编程方法
  产生根本目的:慢
  功能:休眠
  方法1:
          休眠+唤醒=九步骤
  方法2:
          休眠+唤醒=三步骤
      唤醒之前置真
      唤醒以后置假  
 
2.linux内核实现按键去抖动的原理图
  内核软件定时器
   
3.如何将外设的物理地址映射到内核虚拟地址上
  ioremap
 
4.如果将外设的物理地址映射到用户虚拟地址上
  mmap
  注意:
  1.利用mmap对GPIO进行输入和输出操作时,一定要将Cache缓存功能禁止掉
    CPU下发的操作数据如果缓存在Cache,可能不会立即影响到具体的硬件寄存器
    建议:关闭cache功能
    vma->vm_page_prot =  
                    pgprot_noncached(vma->vm_page_prot); //数据访问不再需要Cache
      
      mmap(0, 1, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
      实际操作系统给你强制分配了4KB的虚拟内存!
      
  2.mmap在地址映射时,已知的用户起始虚拟地址vm_start(保证的)
      和已知的物理地址一定要是页面大小(4KB=0x1000)的整数倍   
      例如:物理地址:0xC001C000(合法)    
            remap_pfn_range(vma,vma->vm_start,0xC001C000>>12,...);//成功
            物理地址: 0xC001C004(不合法)
            remap_pfn_range(vma,vma->vm_start,0xC001C004>>12,...);//失败
   
  3.利用mmap对硬件设备的访问效率要高,mmap特别适合用于
    操作的数据量比较大的硬件外设(摄像头,声卡,显卡,LCD显示屏)
    缺点是分配的用用户虚拟内存都是4KB的整数倍,所以将来操作的
    数据量是4KB的整数倍,mmap最好使用(数据只需一次拷贝)
    而read,write,ioctl对数据的操作都是经过两次拷贝完成
    如果操作的数据量比较小,对系统性能的影响几乎可以忽略不计
    如果操作的数据量比较答,势必不考虑使用read,write,ioctl
 
*********************************************************
2.linux内核分离思想,platform机制
  2.1.分析案例:将LED1对应的GPIOC12更换成GPIOE6
      观察ioremap实现的LED驱动需要做哪些地方的改动
      结论:
      0.一个完成的硬件设备驱动必然包含两个部分:
        纯硬件相关内容:寄存器的起始地址,GPIO编号等
        纯软件相关内容:if...else,混杂设备,接口等
        纯软件用来操作纯硬件         
      1.如果仅仅是硬件GPIO编号进行了改动,驱动的改动
        仅仅也只是修改硬件相关(寄存器的起始地址和
        GPIO编号)的修改,软件无需改动
      2.并且发现昨天的代码的改动量相当之大,如果让驱动代码的
        改动量小,移植性好,首先相当利用宏替换驱动代码中的
        硬件相关的内容
      3.但是寄存器的起始地址,GPIO编号他们同样是“事物”同样
        具有额外的其他属性,利用宏不能完整描述特性,势必
        要考虑到结构体进行描述
        问:linux内核如何做到既可以让驱动的可移植性变得好
            并且对硬件信息的描述更加的丰富呢?
        答:利用linux内核的分离思想,platform机制
        分离思想:就是将驱动的纯硬件信息和纯软件信息进行
                  彻底分开,一旦将来软件写好,就无需改动,将来驱动
                  开发者的重心放在硬件信息即可
                  本质目的就是让驱动代码的可移植性变得非常好
        问:如何实现内核的分离思想呢?
        答:利用platform机制
         
        问:内核的platform机制如何实现的呢?
        答:ftp://DRV/ESD_DRV_09.ppt
    
             总结:驱动如果采用platform机制实现,只需关注以下两个数据结构:
                struct platform_device
                struct platform_driver
    
   2.2.struct platform_device如何使用
       struct platform_device {
           const char    * name;
                int        id;
                struct resource    * resource;
                u32        num_resources;
                struct device    dev;
                        .platform_data
                ...
       };   
       功能:用于描述一个驱动中纯硬件信息
       成员说明:
       name:硬件节点的名称,将来用于匹配,必须初始化
       id:硬件节点的编号,如果dev链表上,仅有一个名称为name
          的硬件节点,id=-1即可,如果dev链表上,有多个名称为name(同名)
          通过id进行编号(0,1,2,...)
       resource:装载纯硬件信息相关内容
                           struct resource {
                               unsigned long start;
                               unsigned long end;
                               unsigned long flags;
                               ...
                           };    
                           功能:用于描述硬件信息
                           成员:
                           start:起始硬件信息
                                       例如:寄存器的起始地址
                           end:结束硬件信息
                                       例如:寄存器的结束地址
                           flags:硬件信息的标志           
                                       IORESOURCE_MEM:用于指示此硬件信息为地址信息
                                                                    0xC001C000这个物理地址可以用此标志修饰
                                       IORESOURCE_IRQ:用于指示此硬件信息为GPIO信息
                                                                    PAD_GPIO_C+12这个GPIO可以用此标志进行修饰
           num_resources:用来指示用struct resource描述的硬件信息的个数                                                          
        dev:此结构体变量中重点关注其中的void *platform_data字段
             platform_data字段可以用来装载驱动开发者自定义的硬件信息
             例如:自定义的硬件信息
                          struct led_resource
                                      .gpio
                                      .name
                          struct btn_resource
                                      .gpio
                                      .name
                                      .code
       总结:platform_device装载硬件信息的方法有三种:
       1.单独使用struct resource进行描述
       2.单独使用自定义的描述硬件信息的数据结构来描述
       3.可以同时使用struct resource和自定义的描述硬件
         信息的数据结构来描述硬件信息
        
       两个配套函数:
       platform_device_register(&硬件节点对象)
       向内核dev链表添加注册硬件节点对象
       一旦添加完毕,什么遍历drv链表,什么匹配,什么调用probe
       函数,什么给probe函数传递硬件节点的首地址这些都是由
       内核完成!如果一旦匹配成功,硬件节点对象的首地址
       会传递给匹配成功的软件节点的probe函数
        
       platform_device_unregister(&硬件节点对象)  
       从内核的dev链表删除硬件节点对象
       内核会调用软件节点的remove函数
        
       案例:利用platform机制优化LED驱动程序
       实施步骤:
       上位机执行:
       mkdir /opt/drivers/day10/1.0 -p
       cd /opt/drivers/day10/1.0
       touch led_dev.c led_drv.c  
       vim led_dev.c
       vim Makefile
       make
       cp *.ko /opt/rootfs/home/drivers/
        
       下位机执行:
       cd /home/drivers
       insmod led_dev.ko
        
2.3.struct platform_driver如何使用   
        struct platform_driver {
            int (*probe)(struct platform_device *pdev);
            int (*remove)(struct platform_device *pdev);
            struct device_driver driver;
            ...
        };
        功能:描述一个驱动中纯软件相关内容
        成员说明:
        probe: 一旦纯硬件节点和纯软件节点匹配成功,内核就会
                     调用此函数,也就是说此函数是否被调用,至关重要
                     probe函数被调用,说明硬件软件匹配成功,一个完整的
                     硬件设备驱动诞生,这样纯软件才可以踏踏实实访问
                     纯硬件
                     形参pdev指针指向匹配成功的硬件节点对象  
       
      remove:从dev链表删除硬件节点或者从drv链表删除
             软件节点,不管删除哪一个,都代表这个驱动不再完整
             此时内核就会调用remove函数
             remove函数和probe函数是一对死对头!
             形参pdev指针指向匹配成功的硬件节点对象
      driver:重点关注其中的name字段,此字段将来用于匹配    
       
      配套函数:
      platform_driver_register(&软件节点对象)
      向内核drv链表添加软件节点对象
      内核会帮你遍历dev链表,帮你匹配,如果匹配成功,帮你
      调用probe函数,帮你把匹配成功的硬件节点传递给probe函数
       
      platform_driver_unregister(&软件节点对象)  
      从内核drv链表删除软件节点
      内核会调用软件节点的remove函数  
       
      案例:利用platform机制优化LED驱动程序
       实施步骤:
       上位机执行:
       mkdir /opt/drivers/day10/1.0 -p
       cd /opt/drivers/day10/1.0
       touch led_dev.c led_drv.c  
       vim led_drv.c
       vim Makefile
       make
       cp *.ko /opt/rootfs/home/drivers/
        
       下位机执行:
       cd /home/drivers
       insmod led_dev.ko  
       insmod led_drv.ko //查看probe函数是否被调用
       rmmod led_drv //查看remove函数是否被调用
       rmmod led_dev                  
        
       insmod led_drv.ko
       insmod led_dev.ko //查看probe函数是否被调用
       rmmod led_dev //查看remove函数是否被调用
       rmmod led_drv
 
2.4.问:probe函数做哪些工作呢?  
        答:明确:probe函数被调用预示着一个完整的驱动诞生
            预示着纯软件可以操作访问纯硬件
            probe函数一般做如下工作:
            1.通过形参pdev指针获取匹配成功的纯硬件信息(寄存器物理地址,编号等)
            2.要处理获取到的纯硬件信息(各种该)
              该地址映射的地址映射
              该申请资源的申请资源
              该注册的注册
              该初始化的初始化
            3.注册混杂设备或者字符设备给用户提供访问操作硬件的接口
            总结:2,3两个步骤之前都是在驱动的入口函数中完成
                  而现在统一放在probe函数中完成
                        remove和probe对着干!
          
         问:probe函数通过形参pdev指针如何获取到纯硬件信息呢(resource描述的硬件信息)
         答:用以下函数即可获取
                  struct resource *platform_get_resource(
                                                      struct platform_device *pdev,
                                                      unsigned long flags,
                                                      int index
                                                      )
                  函数功能:probe函数或者remove函数通过形参pdev
                  指针来获取到匹配成功的resource描述的硬件信息
                   
                  参数:
                  pdev:传递匹配成功的硬件节点的首地址
                  flags:要获取的硬件信息的类型
                              要不是IORESOURCE_MEM要不是IORESOURCE_IRQ
                  index:同类型资源的偏移量
                   
                  返回值:返回获取的resource描述的硬件信息对象首地址
                   
         案例:最终完成LED驱动
 
2.5.用自定义的数据结构来描述硬件信息
    具体参见:ftp://day10/4.0源码
              
                   
                            
                    
原创粉丝点击