初级驱动DAY5

(一)多路复用-----poll()

应用空间:

       #include <poll.h>
       int poll(struct pollfd *fds, nfds_t nfds, int timeout);
        //参数1---一般为数组
        //参数2---监控的路数
        //参数3---阻塞的时间   -1
      struct pollfd {
               int   fd;         /* file descriptor */  文件描述符          fd1
               short events;     /* requested events */  你要监控的事件     POLLIN
               short revents;    /* returned events */  这个事件是否发生    POLLIN
           };
    //利用poll监控键盘是否有数据可读,并且监控开发板上是否有按键输入
        struct pollfd fds[2];
        
        fds[0].fd   =0;  //标准输入的文件描述符
        fds[0].events =POLLIN;   //监控是否有数据输入
        
        fds[1].fd   =fd;  //我们自己写的按键的文件描述符
        fds[1].events =POLLIN;//监控是否有数据输入
        
        poll();
        
        if(fds[0].revents&POLLIN){
            fgets
            //从键盘获取数据
        }
        if(fds[1].revents&POLLIN){
            
            //读取数据
        }
        
    内核驱动:
        unsigned int drv_key_poll (struct file *, struct poll_table_struct *)
        {
            int mask=0;
            1,将等待队列头注册到VFS中
            // 将复用处理的等待队列追加内核管理的进程 poll_table上去
            poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table * p)
            
            2,如果触发了中断----有数据可读,返回POLLIN 如果没有数据返回0

            if(have_data==1){
                mask |=POLLIN;
            }
            return mask
        }
        
        unsigned int drv_key_poll(struct file *filp, struct poll_table_struct *ptbs)
        {
            unsigned int Mask=0;
            printk("------------%s--------------\n",__FUNCTION__);

            // 1,将等待队列头注册到VFS中
            poll_wait(filp, &drv_key->wq,ptbs);

            // 2,如果触发了中断----有数据可读,返回POLLIN 如果没有数据返回0
            if(drv_key->have_data){
                Mask |=POLLIN;
            }
            return Mask;
        }
        
二,MMAP的实现
    当在应用空间中需要使用物理内存时,就可以通过mmap实现映射物理内存
    
    1,实现映射物理内存的好处
        1》是一个文件IO接口,可以在驱动中实现
        2》可以在应用空间和内存空间之间快速的传递数据,效率更高
        3》使用非常方便,可以在应用空间调用mmap实现映射
        
        
        应用空间:
          #include <sys/mman.h>
       void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
       //参数1:指定要映射到用户空间的哪个地址,一般NULL
       //参数2:要映射的空间长度 PAGE_SIZE
       //参数3: 对内存的操作权限 PROT_READ PROT_WRITE
       //参数4:是否允许其他的进程映射该内存  MAP_SHARED 允许  MAP_PRIVATE 建立一个私有的映射
       //参数5:文件描述符
       //参数6:从物理内存起始位置多少开始映射
       
       //返回值:成功:返回映射到用户空间的地址,失败:NULL
       
       
       
       内核空间:
          // 1,申请一块4k的虚拟空间------一般写在加载函数中
          返回值:申请的虚拟空间的地址=kzalloc(PAGE_SIZE,GFP_KERNEL);
        int (*mmap) (struct file *filp, struct vm_area_struct *vma(映射空间的信息))
        {
            // 2,根据申请到的虚拟空间找到一块物理空间
            static __inline__ unsigned long virt_to_phys(volatile void *address)
            //参数: 虚拟空间地址
            //返回值:找到的物理空间的地址
            
            // 3,将物理空间映射给用户
            int io_remap_pfn_range(struct vm_area_struct *vma, unsigned long from,
               unsigned long pfn, unsigned long size, pgprot_t prot)
               
               //参数1:表示映射空间的相关信息
               //参数2:是映射的应用空间的起始地址  ----vma->vm_start
               //参数3:要映射的物理内存的页地址   -----addr(物理地址) >> PAGE_SHIFT
               //参数4:要映射的空间的大小   ----PAGE_SIZE  (4k)
               //参数5:映射空间的权限
               //返回值: =0成功,不为0,失败 
               
        }
        
        
        mmap编程步骤:
            内核空间:-----实现映射
            1,申请一块4k的虚拟空间------一般写在加载函数中
                    drv_key->virt=kzalloc(PAGE_SIZE,GFP_KERNEL);
                        if(IS_ERR(drv_key->virt)){
                            printk("4k virt kzalloc is error\n");
                            ret=PTR_ERR(drv_key->virt);
                            goto device_err;
                        }
            2,实现内核的操作接口函数mmap
                2.1 根据申请到的虚拟空间找到一块对应物理空间
                    phys_addr=virt_to_phys(drv_key->virt);
                2.2 将对应的物理空间映射到用户空间
                        if (io_remap_pfn_range(vma, vma->vm_start,phys_addr>> PAGE_SHIFT,
                                        PAGE_SIZE, vma->vm_page_prot)) {
                            printk(KERN_ERR "%s: io_remap_pfn_range failed\n",
                                __func__);
                            return -EAGAIN;
                        }
            
            内核空间:----验证映射是否准确(将虚拟空间中的数据通过ioctl发送到应用层)
                3,实现ioctl的函数接口
                    3.1 定义一个结构体,和一个结构体变量----用来存放虚拟空间的数据
                    struct mmap_data{
                            char buf[128];
                        };
                        struct mmap_data mamp_data;
                    3.2 定义命令这个命令_IOR来定义(用_IOR来定义决定ioctl数据传输方向是内核到应用)
                        #define ioctl_mmap_data  _IOR('K',0x2321,struct mmap_data)
                    3.3 在ioctl的函数中用switch语句
                    switch(cmd){
                        case  ioctl_mmap_data://命令就是3.2定义的命令
                            memset(mamp_data.buf,0,sizeof(mamp_data.buf)); //清除buf结构体变量
                            //将虚拟空间的数据复制到buf结构体变量
                            memcpy(mamp_data.buf,drv_key->virt,sizeof(mamp_data.buf));
                            //将buf中的数据转化成应用空间的数据
                            ret=copy_to_user(gprs,&mamp_data,sizeof(mamp_data));
                            if(ret!=0){
                                printk("ioctl_mmap copy_to_user is error\n ");
                                return ret;
                            }
                            
            应用空间:
                1,定义一个指针,指向映射号的空间 
                    char*mmap_buf
                2,实现mmap内存映射
                      mmap_buf=mmap(NULL,PAGE_SIZE,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
                      if(mmap_buf==NULL){
                          perror("mmap");
                          exit(1);
                      }
                 
                3,在键盘上获取数据,并且放入实现了mmap内存映射的mmap_buf地址中
                    fgets(buf,sizeof(buf),stdin);  //从键盘上获取数据
                    memcpy(mmap_buf,buf,sizeof(buf));//将键盘上获取的数据放入mmap_buf地址
                4,延时1s
                5,通过ioctl将drv_key->virt的数据读出来,读出以后打印
                    ioctl(fd,ioctl_mmap_data,&mamp_data.buf);
                    printf("app:ioctl_mmap_data=%s\n",mamp_data.buf);

三,中断下半部
 实现中断下半部方式:
        1》tasklet
        2》工作队列
        3》软中断
        
    1>tasklet的实现
    struct tasklet_struct
    {
        struct tasklet_struct *next;
        unsigned long state;
        atomic_t count;
        void (*func)(unsigned long);
        unsigned long data;
    };

    1,定义一个tasklet对象 ,初始化对象-------加载函数硬件初始化的位置初始化
    void tasklet_init(struct tasklet_struct *t,  void (*func)(unsigned long), unsigned long data)
    //参数1:tasklet对象的地址
    //参数2:中断下半部的执行函数
    //参数3:传给中断下半部的执行函数的参数 
    
    2,中断下半部的执行函数
     void (*func)(unsigned long)
     {
         //中断下半部执行代码
     }
     
    3,启动中断下半部------//在中断处理函数中启动中断下半部
        static inline void tasklet_schedule(struct tasklet_struct *t)
    
    
    经验:
        1>任务对时间敏感,放在上半部
        2>任务和硬件有直接关系,放在上半部
        3>需要保证任务不被其他任务打断,放在上半部
        
        其他全部下半部
                    
    2》工作队列
        struct work_struct {
        atomic_long_t data;
        struct list_head entry;
        work_func_t func;
    #ifdef CONFIG_LOCKDEP
        struct lockdep_map lockdep_map;
    #endif
    };
    1>定义下半部的结构体对象
    struct work_struct work;
    2>初始化工作队列-------加载函数硬件初始化的位置初始化
    INIT_WORK(_work, _func)
    //参数一:定义的工作队列对象
    //参数二:工作队列的函数名
    3>启动下半部------//在中断处理函数中启动中断下半部
    schedule_work(struct work_struct * work)
    
    中断下半部的执行函数 
    typedef void (*work_func_t)(struct work_struct *work);

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

__Lewis

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值