网上有很多关于怎么安装pos机驱动,字符设备驱动的安装知识,也有很多人为大家解答关于怎么安装pos机驱动的驱动驱动问题,今天乐刷官方代理商(www.zypos.cn)为大家整理了关于这方面的字符知识,让我们一起来看下吧!
1、设备怎么安装pos机驱动
字符设备驱动是安装Linux最基本的驱动,很多学习驱动的驱动驱动朋友都是从这个开始的,比如LED,字符或者按键驱动等。设备但是安装很多时候你看一些视频教程或书籍时,会发现其实不是驱动驱动很完整,特别是字符既看书又看视频教程的,会发现好像不怎么一样。设备接下来一起来总结一下!安装
字符设备结构体在Linux内核中,驱动驱动 使用cdev结构体来描述一个字符设备。字符
struct cdev { struct kobject kobj; //内嵌的kobject对象 struct module *owner;//所属模块 const struct file_operations *ops; //文件操作结构体 struct list_head list; dev_t dev; //设备号 unsigned int count;};
cdev结构体的dev_t成员定义了设备号,为32位,其中12位为主设备号,20位为次设备号。
使用下面的宏来定义dev_t
MKDEV(int major, int minor)
使用下面的宏来获取主设备号和次设备号
MAJOR(dev_t dev)MINOR(dev_t dev)字符设备API
/*函数功能:分配一些设备号参数说明: from:起始设备号,必须要包含主设备号 count: 连续分配的数量 name: 设备或驱动的名称*/int register_chrdev_region(dev_t from, unsigned count, const char *name)/*函数功能:分配一些字符设备号参数说明: 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)
/*函数功能:初始化一个cdev结构体参数说明: cdev:要初始化的cdev结构体 fops:该字符设备的file_operations (操作函数)*/void cdev_init(struct cdev *cdev, const struct file_operations *fops)/*函数功能:动态分配一个cdev结构体说明:该函数不常用,因为字符设备都会封装一个新的结构体, 然后使用kzalloc来分配内存(结构体)*/struct cdev *cdev_alloc(void)/*函数功能: 添加一个字符设备到系统参数说明: p:设备的cdev结构体 dev:第一个设备的设备号 count:连续的次设备号数*/int cdev_add(struct cdev *p, dev_t dev, unsigned count)/*函数功能: 从系统中移除一个cdev参数说明: p:要移除的cdev结构体*/void cdev_del(struct cdev *p)
把上面的API分成两段,一段是分配和释放设备号的,另一段就是分配字符设备和注册到系统中的。
register_chrdev_region() 和 alloc_chrdev_region() 的区别:
register_chrdev_region() 函数用于已知起始设备的设备号的情况,而alloc_chrdev_region() 用于设备号未知,向系统动态申请未被占用的设备号的情况。alloc_chrdev_region() 相比于 register_chrdev_region() 的优点就在于它会自动避开设备号重复的冲突。
字符设备的驱动架构下面是写一个字符设备驱动的一般步骤:
(1) 为设备定义一个设备相关的结构体(包含设备所涉及的cdev,私有数据及锁等信息)
(2) 初始化函数xxx_init的定义
1. 向系统申请设备号(register_chrdev_region()或alloc_chrdev_region())
2. 使用kzalloc申请设备内存(为(1)中定义的结构体申请存储空间)
3. 调用cdev_init()初始化cdev
4. 调用cdev_add()向系统注册设备
(3) 卸载函数xxx_exit的定义
1. 释放设备号(unregister_chrdev_region())
2. 调用cdev_del()注销设备
(4) 定义file_operations
1. 实现write()函数 (copy_to_user())
2. 实现read()函数(copy_from_user())
3. 根据需要实现其他函数....
模板程序
/*设备结构体*/struct xxx_dev_t{ struct cdev cdev; ...};struct xxx_dev_t *dev;dev_t devno;//读设备ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t* f_pos){ ... copy_to_user(buf, ..., ...);}//写设备ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t* f_pos){ ... copy_from_user(..., buf, ...);}//操作函数file_operationsstruct file_operations xxx_fops = { .owner = THIS_MODULE, .read = xxx_read, .write = xxx_write, ...};//设备驱动模块加载函数static int __init xxx_init(void){ ... devno = MKDEV(xxx_major, 0); //(1)申请设备号 if(xxx_major) { register_chrdev_region(devno, 1, "xxx_dev"); } else { alloc_chrdev_region(&devno, 0, 1, "xxx_dev"); } //(2)为设备结构体申请内存(推荐使用devm_kzalloc) dev = kzalloc(sizeof(struct xxx_dev_t), GFP_KERNEL); //(3)初始化cdev cdev_init(&dev.cdev, &xxx_fops); dev.cdev.owner = THIS_MODULE; //(4)向系统注册设备 cdev_add(dev.cdev, dev_no, 1);}module_init(xxx_init);//设备驱动模块卸载函数static void __exit xxx_exit(void){ //释放设备号 unregister_chrdev_region(dev_no, 1); //注销设备 cdev_del(&dev.cdev); ...}module_exit(xxx_exit);MODULE_LICENSE("GPL v2");简化字符设备驱动架构
每次编写初始化函数都要一步一步写,为了简化这个流程,Linux提供了一些封装的接口函数来让我们调用。
/*函数功能:注册字符设备参数说明:major:主设备号,0表示动态分配 name: 设备名 fops: 操作函数返回值: 申请的主设备号 */static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)/*函数功能:注销字符设备参数说明:major:主设备号 name: 设备名*/static inline void unregister_chrdev(unsigned int major, const char *name)
分析一下上面两个函数的源码
//register_chrdev调用了__register_chrdevint __register_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name, const struct file_operations *fops){ struct char_device_struct *cd; struct cdev *cdev; int err = -ENOMEM; //1. 申请设备号 cd = __register_chrdev_region(major, baseminor, count, name); if (IS_ERR(cd)) return PTR_ERR(cd); //2. 为设备结构体申请内存 cdev = cdev_alloc(); if (!cdev) goto out2; // 3. 初始化cdev cdev->owner = fops->owner; cdev->ops = fops; kobject_set_name(&cdev->kobj, "%s", name); //4. 向系统注册设备 err = cdev_add(cdev, MKDEV(cd->major, baseminor), count); if (err) goto out; cd->cdev = cdev; return major ? 0 : cd->major;out: kobject_put(&cdev->kobj);out2: kfree(__unregister_chrdev_region(cd->major, baseminor, count)); return err;}//unregister_chrdev调用__unregister_chrdevvoid __unregister_chrdev(unsigned int major, unsigned int baseminor, unsigned int count, const char *name){ struct char_device_struct *cd; //释放设备号 cd = __unregister_chrdev_region(major, baseminor, count); if (cd && cd->cdev) cdev_del(cd->cdev); //注销设备 kfree(cd);}
通过上面的分析发现和上面的驱动框架是一样的,只是做了个封装罢了。这时候是不是发现了两者之间的关系,是不是很多驱动都是用这两个函数来注册的。有些书就只讲前面的,而有些教程又只讲后面。所以搞得云里雾里的。
简化上面的驱动模板
/*设备结构体*/struct xxx_dev_t{ struct cdev cdev; ...};struct xxx_dev_t *dev;dev_t devno;//读设备ssize_t xxx_read(struct file *filp, char __user *buf, size_t count, loff_t* f_pos){ ... copy_to_user(buf, ..., ...);}//写设备ssize_t xxx_write(struct file *filp, const char __user *buf, size_t count, loff_t* f_pos){ ... copy_from_user(..., buf, ...);}//操作函数file_operationsstruct file_operations xxx_fops = { .owner = THIS_MODULE, .read = xxx_read, .write = xxx_write, ...};//设备驱动模块加载函数static int __init xxx_init(void){ ... //注册字符设备 xxx_major = register_chrdev(0, "xxx_dev", &xxx_fops);}module_init(xxx_init);//设备驱动模块卸载函数static void __exit xxx_exit(void){ //注销字符设备 unregister_chrdev(major, "xxx_dev"); ...}module_exit(xxx_exit);MODULE_LICENSE("GPL v2");
喜欢这篇文章,欢迎点赞,分享,关注!
更多精彩文章,欢迎关注微信公众号"嵌入式软件开发交流"
以上就是关于怎么安装pos机驱动,字符设备驱动的知识,后面我们会继续为大家整理关于怎么安装pos机驱动的知识,希望能够帮助到大家!
相关文章: