驱动开发

15 篇文章 0 订阅
12 篇文章 0 订阅
字符设备: 传输字符流的设备     不允许随机访问(一般)
块设备:数据块  允许随机访问  例如硬盘,U盘
网络设备:可以使用网络的设备

字符设备驱动:
ctrl+N自动补齐
生成tags

一、模块:

组成:
1、许可证声明
  MODULE_LICENSE("GPL");
  1) 不写内核会产生抱怨
  2) 不写则内核很多函数无法使用

2、初始化函数---加载函数----insmod 时会被调用
   int init_module(void)
   {
      return 0;
   }



3、清除函数---卸载函数---rmmod 时会被调用
   void cleanup_module(void)
   {
     
   }


编译外部模块:
  make -C <path-to-kernel-src> M=`pwd`======> 使用path-to-kernel-src目录下的Makefile为主Makefile并传递参数M=`pwd`告诉内核Makefile外部模块所在位置
 
  例:make -C /home/linux/FS4412/linux-3.14-fs4412 M=`pwd`

  注意:内核源码有要求
      1、必须是配置为针对某个特定平台的内核源码
      2、内核源码必须编译过且没有被make clean

测试:
  insmod 插入模块---动态加载模块
    insmod 模块文件名

  dmesg 显示内核启动后所有打印信息
  dmesg -c 清除现有信息

  lsmod 列出当前内核中所有动态加载模块


  rmmod 删除模块----动态卸载模块
    rmmod 模块名
    注意:根文件系统中需要一个路径:/lib/modules/`uname -r`

  modinfo 查看模块信息
  modprobe  类似与insmod


module_init
#ifndef MODULE
  定义函数指针指向加载函数,且函数指针被链接到initcall段,内核启动时将被统一调用

#else
  用来检测加载函数的合法性(只有返回值为int 参数为void的函数才能作为加载函数)
  定义别名将加载函数与init_module关联

#endif


组成:
必须有的部分:
1、许可证声明
  MODULE_LICENSE("GPL");
  1) 不写内核会产生抱怨
  2) 不写则内核很多函数无法使用

2、初始化函数---加载函数----insmod 时会被调用
   初始化函数为返回值为int 参数为void的函数,并用module_init修饰



3、清除函数---卸载函数---rmmod 时会被调用
   卸载函数函数为返回值为void 参数为void的函数,并用module_exit修饰


可选部分:
4、描述类信息
5、模块参数
  定义全局变量:
  int i = 10;
 
  声明变量可以接受外部传参
  module_param(变量名,变量类型,权限)

  module_param(i, int, 0444);

  模块参数的说明
  MODULE_PARM_DESC(变量名, 描述信息)

  MODULE_PARM_DESC(i, "...");

  传递参数:
  insmod hello.ko i=15

6、符号导出


驱动
     
  1、申请设备号
       cat /proc/devices 可以查看内核设备号使用情况
      <linux/fs.h>
      dev_t 32bit数
      12(主设备号) | 20 (次设备号)
      主设备号表示设备的类型
      次设备好表示第几个这种类型的设备
 
      dev_t devno = MKDEV(主, 次);
      MAJOR(devno);
      MINOR(devno);

      静态申请
      int register_chrdev_region(dev_t from, unsigned count, const char *name);
      参数:
      from: 要申请的连续的若干个设备号中的第一个
      count:要申请多少个设备号
      name:设备或驱动的名字
     
      返回值:
      成功 0
      失败 -errno

      设备号释放
      void unregister_chrdev_region(dev_t from, unsinged count);
       参数:
      from: 要释放的连续的若干个设备号中的第一个,必须和申请的号一直
      count:要释放多少个设备号

     
      动态分配
      alloc_chrdev_region----课后作业
  1.1
    定义struct file_operations
    struct file_operations hello_fops = {
      .owner = THIS_MODULE,
    };
 


  2、注册设备
      <linux/cdev.h>
      定义:struct cdev cdev;

      初始化:void cdev_init(struct cdev *cdev, struct file_operations *ops);
            cdev_init(&cdev, &hello_fops);

      注册:int cdev_add(struct cdev *cdev, dev_t devno, int count); 将设备号devno和cdev关联,并将cdev注册到系统中去(cdev_map)
          cdev_add(&cdev, devno, 1);
  3、注销设备
       cdev_del(struct cdev *cdev);
       cdev_del(&cdev);
     
 
  4、为应用程序提供访问接口
    open/release

    open:
    
write:
    memcpy(data,buf,size);
    1,size合法性验证
    2,数据拷贝---copy_from_user(to,from,size)
    3,返回实际写入的数据大小
   return -EINVAL;//返回无效操作
   return -EFAULT;//返回错误地址

read:
    memcpy(buf,data,size);
     access_ok(buf);//判断buf是否可写
    1,size合法性验证
    2,数据拷贝---copy_to_user(to,from,size)
    3,返回实际写入的数据大小
    
注意:copy_to_user和copy_to_user是可能引起睡眠的操作
    
编译
 make -C /lib/modules/`uname -r`/build M=`pwd`
测试出错时(缺少文件或目录时)
在 /dev下新建hello
mknod /dev/hello c 250 0

 ioctl:
    应用:
    int ioctl(int fd, int cmd, void *arg)
    fd : 文件描述符
    cmd: 设备驱动所定义的命令,应用程序根据驱动对每个命令赋予的不同含义,传递不同的命令到驱动中,完成不同的操作
    arg: 当某个命令需要传递参数时,就是用这个参数
   
    驱动:
    定义命令:
    类型为int 32bit
    dir(2bit) | size(14bit) | type(8bit) | nr(8bit)
    type: 类型表示这个命令属于哪个设备
    nr:   序号表示这个这个设备的第几个命令
    dir:  数据流向
    size: 传递数据的大小

    struct A
    { int a;
      int b;
    };
    struct A a;
    #define HELLO_CMD1  _IO('H', 0)
    #define HELLO_CMD2  _IOW('H', 1, struct A)

    编码: _IO(type, nr)
           _IOW(type, nr, size)
           _IOR(type, nr, size)
           _IOWR(type, nr, size)
   
    解码: _IOC_DIR(cmd)
           _IOC_SIZE(cmd)
           _IOC_TYPE(cmd)
           _IOC_NR(cmd)
   
    file-operations ->ioctl(struct inode *indoe, struct file *file, int cmd, unsigned long arg)
                      unlocked_ioctl(struct file *file, int cmd, unsigned long arg)

      识别命令,根据驱动对不同命令的定义,实现不同的操作
        switch-case
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

qq_27205523

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

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

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

打赏作者

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

抵扣说明:

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

余额充值