1.驱动中如何描述一个对象
由结构体描述
struct cdev { struct kobject kobj; //kobject kset 文件系统 ---sysfs文件系统 struct module *owner; //THIS_MODULE const struct file_operations *ops; //操作方法集 struct list_head list; //链表,用来管理字符设备 dev_t dev; //设备号 typedef unsigned long u_long; 32位 ------主设备号 + 次设备号 unsigned int count; // //隶属于同一主设备号的次设备号的个数 12 20 };
应用层和驱动层的逻辑关系如图
在驱动中通过函数指针实现对硬件的操作
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 *); //写 long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long); //ioctl long (*compat_ioctl) (struct file *, unsigned int, unsigned long); //向下兼容linux内核 int (*mmap) (struct file *, struct vm_area_struct *); //显示屏 - 音视频驱动的比较多 int (*open) (struct inode *, struct file *); //打开 int (*release) (struct inode *, struct file *); //关闭 }; const struct file_operations *ops; //操作方法集
1.定义一个字符设备 struct cdev* cdev;
2.结构体的指针并没有实际分配内存,所以需要对cdev分配内存,在驱动中使用kzalloc或者直接调用cdev_alloc分配内存空间
3.初始化字符设备void cdev_init(struct cdev *cdev, const struct file_operations *fops),第二个参数可自己定义,结构体中的方法可不全部使用,使用.xxx的方式来使用
在linux中设备号是唯一用来区分设备是干什么的,相当于设备的身份证,设备好号分为主设备号和次设备号,主设备号表示一类设备,次设备号表示某个设备
在驱动中使用dev_t dev 获取主设备号使用 MAJOR(dev) 获取次设备号MINOR(dev) 制作设备号使用MKDEV(ma,mi)
---------写代码的时候,以框架为主题,然后框架当中你的接口需要什么类型,你就给他什么类型。
有注册一定要有注销
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name) 函数的功能;动态获取设备号 参数: @dev : 设备号:函数返回之后会将设备号保存到dev中 @baseminor :最小的次设备号的基地址 0 @count :设备的个数 3 @name :自己填充,最好见名知意 返回值:成功返回0 失败返回错误码
例子
静态注册设备号: int major = 249; MKDEV(major,0); int register_chrdev_region(dev_t from, unsigned count, const char *name) 函数的功能;静态获取设备号 参数: @from : 指定的主设备号: @count : 设备的个数 3 @name :自己填充,最好见名知意 返回值:成功返回0 失败返回错误码
void unregister_chrdev_region(dev_t from, unsigned count) 功能:注销设备号 参数: * @from: 被注销的第一个设备值,就是你指定的主设备号的值 * @count: 设备数量
添加进系统是我们最初的梦想,现在想来cdev_add需要 cdev我们定义了cdev--》分配内存--》cdev初始化 ,第二个参数需要dev_t 我们动态/静态注册设备号
int cdev_add(struct cdev *p, dev_t dev, unsigned count) 函数的功能;添加一个字符设备到系统当中去 参数: @p :字符设备 @dev :设备号 @count :个数 返回值:失败返回负值 成功返回0
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/fs.h> #include <linux/cdev.h> //定义一个字符设备对象 struct cdev *cdev; dev_t dev; unsigned int baseminor = 0; unsigned int count = 1; const char * name = "demochar"; //向上层提供的接口 //向下层操作硬件----在接口里去操作硬件 const struct file_operations fops = { }; static int __init demo_init(void) { int retval; printk("----%s---%d.\n",__func__,__LINE__); //1、为字符设备分配空间 cdev = cdev_alloc(); if(cdev == NULL){ printk("cdev_alloc faild.\n"); return -ENOMEM; } //2、初始化字符设备 cdev_init(cdev,&fops); //3、动态申请设备号 retval = alloc_chrdev_region(&dev, baseminor, count, name); if(retval){ printk("alloc_chrdev_region faild.\n"); goto err0; } printk("major :%d.\n",MAJOR(dev)); //4、添加到内核 retval = cdev_add(cdev, dev, count); if(retval){ printk("cdev_add failed.\n"); goto err1; } //5、硬件初始化 return 0; err1: unregister_chrdev_region(dev, count); err0: return retval; } static void __exit demo_exit(void) { printk("----%s---%d.\n",__func__,__LINE__); cdev_del(cdev); unregister_chrdev_region(dev, count); } //入口函数 module_init(demo_init); //出口函数 module_exit(demo_exit); //GPL许可协议 MODULE_LICENSE("GPL");
我们上面说的注册流程有点啰嗦,聪明的人,将其封装成一个接口供我们使用,但是我们知其然还要知道其所以然否则以后出错都不知道怎么debug
register_chrdev(unsigned int major, const char *name, const struct file_operations *fops) 功能: 创建并注册一个字符设备 参数: major :主设备号 major == 0; 动态分配 0 < major < 254 ;静态分配 name :设备的名字 fops :操作方法集 返回值: * If @major == 0 返回主设备号major * If @major > 0 成功返回0 失败返回错误码
13、释放设备号:
static inline void unregister_chrdev(unsigned int major, const char *name)
@major :主设备号
@name :名字
```
太简短了。。。
#include <linux/module.h> #include <linux/moduleparam.h> #include <linux/fs.h> #include <linux/cdev.h> //定义一个字符设备对象 struct cdev *cdev; unsigned int major = 0; const char * name = "demochar"; //向上层提供的接口 //向下层操作硬件----在接口里去操作硬件 const struct file_operations fops = { }; static int __init demo_init(void) { printk("----%s---%d.\n",__func__,__LINE__); major = register_chrdev(0, name, &fops); if(major <= 0){ printk("register_chrdev failed.\n"); return major; } printk("major :%d.\n",major); //5、硬件初始化 return 0; } static void __exit demo_exit(void) { printk("----%s---%d.\n",__func__,__LINE__); unregister_chrdev(major, name); } //入口函数 module_init(demo_init); //出口函数 module_exit(demo_exit); //GPL许可协议 MODULE_LICENSE("GPL");
原文:https://www.cnblogs.com/zhuzbyin/p/ghst_zbya.html