• 驱动_Linux驱动框架


    应用实现策略,驱动实现机制

    驱动框架


    驱动框架 (字符设备 /  块设备 /  网络设备)

    #include <linux/init.h>

    #include <linux/module.h>

    加载 {

        1.申请设备编号 (动静与主副)

        2.创建设备节点 (自动与手动) 

        3.硬件的初始化 (映射与中断)

        4.硬件接口函数 (实现f o p s)

        error :

    }

    卸载{

      释放空间

    }

               认证声明


    模块加载函数:安装模块时被系统自动调用的函数,通过module_init宏来指定。

    模块卸载函数:卸载模块时被系统自动调用的函数,通过module_exit宏来指定。

    模块可选信息

    许可证申明:MODULE_LICENSE("GPL");

          用来告知内核该模块带有一个许可证。有效的许可证有“GPL”,“GPLv2”等。

    作者申明:MODULE_AUTHOR(“LuckY”);

    模块描述:MODULE_DESCRIPTION(“hello world module”);

    模块版本:MODULE_VERSION(“V1.0”);

    模块别名:MODULE_ALIAS(“simple module”);

    常用函数


    ①:申请设备编号

        内核提供了三个函数来注册一组字符设备编号,这三个函数分别是:

      1.     static inline int register_chrdev(unsigned int major, const char *name,const struct file_operations *fops)    动静申请主设备号 

               //参数1 ---- major不为0,表示要申请的主设备号,该设备号由我们自己指定,这种叫静态申请;

                    major为0,这时register_chrdev会返回主设备号,由系统自动分配,这种叫动态申请

                 //参数2 ---- 字符串,表示驱动的描述信息,自定义

                 //参数3 ---- 文件操作对象的指针

                 //返回值:major不为0,成功---返回0,失败---返回错误码; major为0,成功----主设备号,失败--返回错误码 

      2.  静:  int register_chrdev_region(dev_t from, unsigned count, const char *name)

              //参数1 ---- dev_t 静态指定的设备号
              //参数2 ---- 设备的个数
              //参数3 ---- 字符串,描述驱动的名称,自定义
              //返回值-----成功:0,失败---错误码

        

         动:  int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name)

              //参数1 ---- 存放设备号的变量的地址
              //参数2 ---- 次设备号
              //参数3 ---- 设备的个数
              //参数4 ---- 字符串,描述驱动的信息,自定义
              //返回值-----成功:0,失败---错误码   

              struct cdev {

              struct kobject kobj;
              struct module *owner;//填充时,值要为 THIS_MODULE,表示模块
              const struct file_operations *ops;//这个file_operations结构体,注册驱动的关键,要填充成这个结构体变量
              struct list_head list;
              dev_t dev;//设备号,主设备号+次设备号
              unsigned int count;//次设备号个数
              };

              struct cdev *cdev_alloc(void)                   // 申请cdev的空间

              void cdev_init(struct cdev *cdev, const struct file_operations *fops) // 初始化cdev的成员

              int cdev_add(struct cdev *p, dev_t dev, unsigned count)                    // 将cdev加入到内核中----链表(只有注册到内核,内核才能统一管理)

              cdev_del                               // 从内核中注销掉一个驱动注销驱动
                        

    ②:创建设备节点

      1. 手动创建:mknod  设备节点名称  类型  主设备号  次设备号 (mknod    /dev/hellow   c   254   0 )

      2. 自动创建:

      struct class * class_create(struct module *owner, const char *name) 

         //参数1 ---- 当前模块 THIS_MODULE用这个宏代替

         //参数2 ---- 字符串,类的描述信息,自定义

         //返回值:成功-----struct class结构体的地址,失败----NULL

       struct device *device_create(struct class *cls, struct device *parent,dev_t devt, void *drvdata,const char *fmt, ...)

        //参数1 ---- struct class * 类型的指针

        //参数2 ---- 父类,一般为NULL

        //参数3 ---- 设备号:dev_t
          #define MINORBITS 20
          #define MINORMASK ((1U << MINORBITS) - 1)

          #define MAJOR(dev) ((unsigned int) ((dev) >> MINORBITS))
          #define MINOR(dev) ((unsigned int) ((dev) & MINORMASK))
          #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
           //参数4 ---- 私有数据,一般为NULL

           //参数5 ---- 设备文件的名称

        //变参----与参数5一起使用,用来定义设备文件的名称

        //返回值: 成功---struct device 结构体的地址,失败-----NULL

     

    ③:硬件的初始化

       1. 地址映射:

           

        gpco_conf = ioremap(0x1F02C04,8);
        gpco_data = ioremap(0x1F02C10,8);

       

        *gpco_conf &= ~(0x07<<8);   //8--10清0
        *gpco_conf |= 0x01<<8;         //8---10赋值:00010001

        代码示例https://www.cnblogs.com/panda-w/p/10966500.html

            

        

      2. 中断申请:

        (1).申请

        unsigned int irqno  = IRQ_EINT(1);

        static inlineint gpio_to_irq(unsigned gpio)

        返回中断编号传给request_irq()和free_irq()

        static inline int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,const char *name, void *dev)    

          //参数1 ----- 中断号
          //参数2 ----- 中断处理函数:irq_handler_t 等价 typedef irqreturn_t (*irq_handler_t)(int, void *)
          //参数3 ----- 中断触发方式:
          #define IRQF_TRIGGER_NONE 0x00000000 //内部中断触发
          #define IRQF_TRIGGER_RISING 0x00000001 //上升沿触发
          #define IRQF_TRIGGER_FALLING 0x00000002 //下降沿触发
          #define IRQF_TRIGGER_HIGH 0x00000004 //高电平触发
          #define IRQF_TRIGGER_LOW 0x00000008 //低电平触发
          //参数4 ----- 字符串,描述信息,自定义
          //参数5 ----- 传给中断处理函数的参数
          //返回值 ----- 成功:0,失败:错误码

        (2).处理    

        irqreturn_t xxx_irq_svc(int irqno, void *dev){

        ........

        return IRQ_HANDLED;
        }

        (3).释放

        void free_irq(unsigned int irq, void *dev_id)
          //参数1 ----- 中断号
          //参数2 ----- 必须与request_irq的最后一个参数保持一致

    ④:硬件接口函数

      struct file_operations {
        ① struct module *owner;

        ② int (*open) (struct inode *, struct file *);

        ③ int (*release) (struct inode *, struct file *);

        ④ ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);

        ⑤ ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);

        ⑥ loff_t (*llseek) (struct file *, loff_t, int);

        ⑦ long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);

        ⑧ int (*mmap) (struct file *, struct vm_area_struct *);

        ⑨ unsigned int (*poll) (struct file *, struct poll_table_struct *);

        ⑩ int (*flock) (struct file *, int, struct file_lock *);    

        ⑪ int (*flush) (struct file *, fl_owner_t id);

        };

      int (*ioctl) (struct inode * node, struct file *filp, unsigned int cmd, unsigned long arg);

        通过发送命令的方式,来控制设备

      

    重要结构体


    常用机制


    ①:中断申请

      代码示例:https://www.cnblogs.com/panda-w/p/10991402.html   

      中断下半部

      代码示例:https://www.cnblogs.com/panda-w/p/10991450.html

    ③:poll多路复用    和        轮询

      应用空间:
        #include <poll.h>

        int poll(struct pollfd *fds, nfds_t nfds, int timeout);
        struct pollfd {
        int fd; /* file descriptor */
        short events; /* requested events */
        short revents; /* returned events */
        };

      ----------------------------------------------------

       内核驱动:  

        static inline void poll_wait(struct file * filp, wait_queue_head_t * wait_address, poll_table *p)
        //参数1-----file结构体指针
        //参数2-----等待队列头
        //参数3-----与等待队列相关联的表

      代码示例:https://www.cnblogs.com/panda-w/p/10991424.html

    ②:mmap应用

      应用空间:
       #include <sys/mman.h>
       void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
       //参数1 ----- 指定映射之后的虚拟空间的的位置,一般为NULL,表示由系统自动分配映射的虚拟空间
       //参数2 ----- 映射的空间长度
       //参数3 ----- 对内存的操作权限:PROT_EXEC PROT_READ PROT_WRITE PROT_NONE
       //参数4 ----- 是否允许其他进程映射这块内存:MAP_SHARED MAP_PRIVATE
       //参数5 ----- 打开的文件的描述符
       //参数6 ----- 从(物理)内存的偏移多少字节的位置开始映射
       //返回值:成功----映射后的虚拟空间的起始地址,失败----NULL
      ------------------------------------------------------------------------------------------
      内核驱动:
       int remap_pfn_range(struct vm_area_struct *vma, unsigned long addr,unsigned long pfn, unsigned long size, pgprot_t prot)
       //参数1 ---- 表示映射空间的相关信息
       //参数2 ---- 映射到应用空间的起始地址
       //参数3 ---- 被映射的物理内存的页地址
       //参数4 ---- 映射的空间大小
       //参数5 ---- 映射的空间的权限

      代码示例:https://www.cnblogs.com/panda-w/p/10991438.html

    ⑤:非/阻塞IO

      1,阻塞IO的实现:(在应用空间中,有很多函数默认就是阻塞IO:scanf(), accept(),connect(),recv(),read()等 )  

        1》 需要初始化一个等待队列头  ------------ 驱动的加载函数中,中断申请之后调用

          init_waitqueue_head(wait_queue_head_t *q)

        2》根据条件决定是否让进程入休眠状态 ---------- 在xxx_read()中调用

          wait_event_interruptible(wait_queue_head_t wq,int condition)

            //参数1 ---- 等待队列头 

            //参数2 ---- 一个条件变量: 0-----休眠,1-----不休眠

        3》当资源可用时,必须唤醒阻塞的进程 ----------------- 在中断处理函数中唤醒进程

          wake_up_interruptible(wait_queue_head_t * x)

        代码示例:https://www.cnblogs.com/panda-w/p/10991359.html

          

      2,非阻塞IO的实现:

        应用程序中:
          fd = open("/dev/button",O_RDWR|O_NONBLOCK);

          read() ------- 有数据,则读出数据,没有数据,则直接返回,返回一个错误码-EAGAIN

          -------------------------------------------------------------------------
        内核驱动中:
          ssize_t xxx_read(struct file *filp , char __user *buf , size_t size, loff_t *flags)
          {
           int ret;
           printk("--------^_^ %s------------ ",__FUNCTION__);
           //判读open时,有没有设置flags为NONBLOCK
           if(filp->f_flags & O_NONBLOCK && !button_dev->have_data)
           return -EAGAIN;

           ...........
          }

        代码示例:https://www.cnblogs.com/panda-w/p/10991411.html

    ⑥:数据传递

     1. static inline long copy_to_user(void __user *to,const void *from, unsigned long n)

       //内核空间传递数据给应用空间  ---------- 实现read接口

       //参数1 ----- 用户空间的地址

       //参数2 ----- 内核空间数据的地址

       //参数3 ----- 传递的数据的长度

          //返回值------ 成功:0,失败:未传递的数据的字节数

     2.  static inline long copy_from_user(void *to, const void __user * from, unsigned long n)

       //应用空间传递数据给内核空间  ---------- 实现wirte接口

       //参数1 ----- 内核的空间地址

       //参数2 ----- 用户空间数据的地址

       //参数3 ----- 传递的数据的长度

       //返回值------ 成功:0,失败:未传递的数据的字节数

       代码示例:https://www.cnblogs.com/panda-w/p/10991322.html

    ⑦:GPIO函数

      7,将某个gpio口配置为特定功能
      int s3c_gpio_cfgpin(unsigned int pin, unsigned int config)

      8,将某个gpio口内部上拉或者下拉
      int s3c_gpio_setpull(unsigned int pin, s3c_gpio_pull_t pull)   

    ⑧:ioctl应用

       通过发送命令的方式,来控制设备

       long xxx_ioctl(struct file *filp, unsigned int cmd , unsigned long args)

       代码示例:https://www.cnblogs.com/panda-w/p/10991312.html

      


    <笔记>

    1. 在linux中,设备号用32位的整数表示:
       分两部分:
       主设备号: 表示一类设备 ------ 高12位
       次设备号: 表示某一类设备中的某个具体设备----低20位

    2. 查看设备编号:cat /proc/devices

    3. 查看设备节点:ls   /dev

    4. 常用头文件 : 

      #include <linux/×××.h> 平台无关
      #include <asm/×××.h> CPU体系结构
      #include <plat/×××.h> IC公司相关
      #include <mach/×××.h> 开发板相关

    5. 驱动查看位置:/sys/module

    6. 主次设备号表示:不同类别设备与同一类别的不同设备

    7. PCI :外围器件互联

    8. ioctl:是应用层的API  

    9. boot  0x******  从开始运行

    10.  驱动设备的寄存器,完成设备的轮询、中断处理、DMA通信,最终让通信设备可以收发数据

    11.  cat /proc/devices         lsmod    查看设备信息

    12.  驱动是异步机制

    13.  

    Stay hungry, stay foolish 待续。。。
  • 相关阅读:
    镜像的上传和下载
    ps 命令
    过滤不合格数据
    云计算5-3-2法则
    Python Django初入门
    python web框架
    BootStrap、EasyUI、JQueryUI
    JS正则
    ngonx FastCGI 相关参数调优
    Windows10远程连接错误-出现身份验证错误
  • 原文地址:https://www.cnblogs.com/panda-w/p/10922316.html
Copyright © 2020-2023  润新知