GPIO字符设备驱动框架

一.字符设备和驱动
  1. 字符设备是指使用字节流进行操作的设备。
  2. 驱动是位于内核空间的,所以用户空间(应用程序)要想操作某个设备时,则需要通过系统调用(如c库函数)的方法,实现对驱动的调用,从而实现操作设备
  3. Linux驱动有两种运行方式
    • 直接编译进Linux内核,随操作系统启动而自动运行
    • 将驱动编译成模块动态地加载、卸载驱动模块
二. 当动态地加载卸载驱动模块时,需要向Linux内核注册驱动模块加载卸载的操作函数
  • module_init(xxx_init);      //注册驱动模块加载函数xxx_init
    module_exit(xxx_exit);      //注册驱动模块卸载函数xxx_exit
    
三. 实现设备的具体操作函数——驱动的实现
  1. 即实现file_operations结构体中的函数,该结构体在内核文件include/linux/fs.h中。常用的几个函数如下:

     /* 参数:
     	inode:传递给驱动的inode值
     	filp :设备文件
     	buf	 :数据缓冲区
     	cnt  :数据长度
     	offt :相对于文件首地址的偏移 */
     //打开设备
     static int chrdevbase_open(struct inode *inode, struct file *filp);
     //从设备读取数据
     static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt);
     //向设备写数据
     static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt);
     //关闭/释放设备
     static int chrdevbase_release(struct inode *inode, struct file *filp);
     
     /*定义file_operations结构体,并初始化*/
     static struct file_operations chrdevbase_fops = {  
     	.owner = THIS_MODULE,     
     	.open = chrdevbase_open,  
     	.read = chrdevbase_read,  
     	.write = chrdevbase_write,  
     	.release = chrdevbase_release
     };
    
四. 注册/注销字符设备
  1. 加载了驱动,还需要向Linux内核注册设备;同理卸载驱动时,也需要向Linux内核注销设备;Linux内核中使用cdev结构体来表示一个字符设备,结构体在文件include/linux/cdev.h中定义;相关函数如下

     /*定义好后cdev字符设备变量后,要使用cdev_init函数对其进行初始化*/
     void cdev_init(struct cdev *cdev, const struct file_operations *fops);
     
     /*使用cdev_add函数向Linux内核添加/注册 cdev变量表示的字符设备.
     	dev是设备号,count是要添加/注册的设备数量 */
     int cdev_add(struct cdev *p, dev_t dev, unsigned count);
     
     /*卸载驱动时,使用cdev_del函数从Linux内核 删除/卸载 cdev变量表示的字符设备*/
     void cdev_del(struct cdev *p);
    
  2. 设备号:Linux设备号由主设备号和次设备号组成,主设备号表示一个具体的驱动次设备号表示驱动下的设备;用一个unsigned int类型表示设备号,其中高12位为主设备号,低20位为次设备号。相关函数如下:

  /*向Linux内核申请未使用的设备号;
  	参数dev是返回的主/次设备号;	baseminor是起始次设备号;
  	count是要申请的数量;		   name是设备名字*/
  int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name);
  
  /*使用完后释放设备号;
  	参数from是注销的起始设备号;	count是注销的设备数量*/
  void unregister_chrdev_region(dev_t from, unsigned count);
  1. 设备节点文件:用户空间下操作设备的入口文件。

*在这里插入图片描述

  • 实现在驱动加载设备注册后,自动在 /dev目录下创建设备节点文件;借助class结构体实现,在include/linux/device.h文件下定义,相关函数如下:
 /* 创建class结构体变量;
     	owner参数一般为THIS_MODULE;name是变量名字;返回值为class指针*/  
     struct class *class_create (struct module *owner, const char *name);  
     
     /* 删除class结构体变量;
     	cls为要删除的class结构体*/  
     void class_destroy(struct class *cls);

     /* 创建设备节点文件;
     	参数class表示设备节点文件需要创建在哪个class结构体下面; 
       	parent表示父设备,NULL表示没有;devt表示设备号; 
       	drvdata表示需要使用到的数据,一般为NULL; 
       	fmt表示创建dev/fmt设备节点文件 */ 
     struct device *device_create(  
                       		struct class    *class,  
     		                    struct device 	*parent,  
     		                    dev_t           devt,   
     	                    	void            *drvdata,   
     	                    	const char     	*fmt, ...);
     
     /* 删除设备节点文件;
     	class表示要删除的设备节点所处的结构体,devt是指设备号 */
     void device_destroy(struct class *class, dev_t devt);
五. 添加驱动的License信息和作者信息
  • MODULE_LICENSE();	 //添加模块LICENSE 信息
    MODULE_AUTHOR();     //添加模块作者信息
    
六. 其他函数
  1. 因为用户空间不能直接操作内核空间内存,内核空间不能直接操作用户空间,所以要使用内核提供的函数完成这一操作
 /*成功返回0,失败返回负数*/
 //从内核空间拷贝数据至用户空间
 static inline long copy_to_user(void __user *to, const void *from, unsigned long n);
 //从用户空间拷贝数据至内核空间
 static inline long copy_from_user(void *to, const void __user *from, unsigned long n);

以上是我在学习过程中的总结,不当之处请在评论区指出。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值