1.2 设备驱动基础

  • 驱动基本结构

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    // hello_drv.c
    #include <linux/init.h>
    #include <linux/module.h>
    
    static int __init hello_drv_init(void)
    {
            printk(KERN_ERR"-----------from hello.ko : %s  %s---------\n",   __FILE__, __FUNCTION__);
            return 0;
    }
    
    static void __exit hello_drv_exit(void)
    {
            printk("-----------from hello.ko : %s  %s---------\n", __FILE__, __FUNCTION__);
    }
    
    module_init(hello_drv_init);
    module_exit(hello_drv_exit);
    MODULE_LICENSE("GPL");
    
  • 基本Makefile结构

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    
    # 内核源码路径
    KERNEL_DIR = 
    # 驱动源码所在目录
    CUR_DIR = $(shell pwd)
    
    # 指定编译哪些源文件
    obj-m = hello_drv.o
    
    all :
    	make -C $(KERNEL_DIR) M=$(CUR_DIR) modules
    clean 
    	make -C $(KERNEL_DIR) M=$(CUR_DIR) clean
    
  • 运行/装载驱动模块

    1
    2
    3
    
    inmod hello_drv.ko #插入内核模块
    rmmod hello_drv  #移除内核模块
    lsmod  #查看当前已经插入的内核模块
    
  • 其他特性

    1. 模块传参

      1
      2
      
      // $insmod hello.ko number=250 name="ruhua"
      module_param(参数名, 参数类型, 权限) //例: module_param(number, int, 666)
      
    2. 模块符号导出

      1. EXPORT_SYMBOL(符号名)
      2. 装载的时候先装载被调用的模块
  • 一个真正的设备驱动需要一些元素

    • 设备号

      1
      2
      3
      4
      5
      6
      7
      8
      
      register_chrdev(unsigned int major, const char * name , const struct file_operations * fops);
      /*
      * 参数1 --- 指定一个主设备号,填0表示由系统自动分配
      * 参数2 --- 字符串, 描述设备驱动信息,自定义
      * 参数3 --- 文件操作对象
      * 如果参数1为0,则返回值是一个int型的主设备号,否则返回负数为错误号, generic
      */
      unregister_chrdev(unsigned int major, const char * name)  //--- 释放已经注册的主设备号
      
    • 设备文件

      1. 手动创建: 每次重启都得手动创建

        mknod /dev/hello c 254 0

      2. 自动创建

         1
         2
         3
         4
         5
         6
         7
         8
         9
        10
        11
        12
        13
        14
        15
        16
        17
        18
        
        #include <linux/device.h>
        struct class * class_create(THIS_MODULE, led_cls)
           /*
           * 参数一: THIS_MODULE
           * 参数二 : 字符串,表示类的名字
           * 返回值 struct class 指针类型
           */
        struct device * device_create(struct class * cls, struct device * parent, dev_t devt, void * drvdata, const char * fmt, ...)
          /*
           * 参数一: class_create()返回的指针
           * 参数二: 该设备的父类
           * 参数三: 设备号, 包含主设备号和次设备号,MKDEV(major, minor)
           * 参数四: 私有数据指针,可以为NULL
           * 参数五: 设备节点名字
          * 返回值: struct device * 
          */
        void device_destory(struct device * dev, dev_t devt);
        void class_destory(struct class * cls);
        
    • 设备的操作方法

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      
      /*
       * NOTE:
       * all file operations except setlease can be called without
       * the big kernel lock held in all filesystems.
       */
      struct file_operations {
          struct module *owner;
          loff_t (*llseek) (struct file *, loff_t, int);
          ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
          ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
      
          unsigned int (*poll) (struct file *, struct poll_table_struct *);
          long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
          int (*mmap) (struct file *, struct vm_area_struct *);
          int (*open) (struct inode *, struct file *);
          int (*flush) (struct file *, fl_owner_t id);
          int (*release) (struct inode *, struct file *);
      	/*省略其他不常用的*/
      };
      /*
       * 设备操作命令如何定义: 由程序员定义,一定是一个整数
      * 1. 直接用整数 --- 有可能会和系统中已经有的命令冲突
        *  #define LED_ALL_ON  0x2222
        * 2. 用内核提供的接口来定义
           #include <asm/ioctl.h>
       _IO(type, nr) //参数一  :魔幻数---字符,参数二:唯一的数字
           __IOW(type, nr, size)
           __IOR(type, nr, size)
           __IOWR(type, nr, size)
       */
      /* 原型如下 */
      #define _IO(type,nr)        _IOC(_IOC_NONE,(type),(nr),0)
      #define _IOR(type,nr,size)  _IOC(_IOC_READ,(type),(nr),sizeof(size))
      #define _IOW(type,nr,size)  _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
      #define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
       /* ------------------ 例 ----------------*/
      #define LED_NUM_ON _IOW('L', 0x3456, int)
      #define LED_NUM_OFF _IOW('L', 0x3457, int)
      #define LED_ALL_ON _IOW('L', 0x3458)
      
      xxx_ioctl(int fd, int cmd, unsigned long args)
      {
           int num = args;
           switch(cmd)
           {
               case LED_NUM_ON
                   xxx
                   break;
               case LED_NUM_OFF
                   xxx
                   break;
               case LED_ALL_ON:
                   xxx
                       break;
           }
      }
      
    • 内存映射与映射

       1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      
      #include <linux/slab.h>
      /**
        * kzalloc - allocate memory. The memory is set to zero.
        * @size: how many bytes of memory are required.
        * @flags: the type of memory to allocate (see kmalloc).
        */
      static inline void *kzalloc(size_t size, gfp_t flags)
          //flags: GFP_KERNEL 如果当前暂时没有内存,会尝试等待(阻塞)
      kfree()
      
      #include <asm/io.h>
      #define ioremap(cookie, size)		__uc32_ioremap(cookie, size)
           /*
            * 参数一:物理地址
            * 参数二:映射的长度
            * 返回值:映射之后的虚拟地址
            */  
      /* ------------------ 例 ----------------*/
      gpc0_conf = ioremap(0xE020060, 8);
      gpc0_data = gpc0_conf+1;
       //配置输出
      *gpc0_conf &= ~(0xff<<12); //清零
      *gpc0_cong |= (0x11<<12);  //置1
      //亮灯
      *gpc0_data |= 0x3<<3;
      //灭灯
      *gpc0_data &= ~(0x3<<3);
      
    • 数据在内核空间和用户空间拷贝的方法

      1
      2
      3
      
      #include <asm/uaccess.h> 
      unsigned long copy_from_user(void * to, void __user * from, unsigned long n);
      unsigned long copy_to_user(void __user * to, const void * from, unsigned long n);