網上有很多關于怎么安裝pos機驅動,字符設備驅動的知識,也有很多人為大家解答關于怎么安裝pos機驅動的問題,今天pos機之家(www.afbey.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
怎么安裝pos機驅動
前言字符設備驅動是Linux最基本的驅動,很多學習驅動的朋友都是從這個開始的,比如LED,或者按鍵驅動等。但是很多時候你看一些視頻教程或書籍時,會發(fā)現(xiàn)其實不是很完整,特別是既看書又看視頻教程的,會發(fā)現(xiàn)好像不怎么一樣。接下來一起來總結一下!
字符設備結構體在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: 連續(xù)分配的數量 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)/*函數功能:動態(tài)分配一個cdev結構體說明:該函數不常用,因為字符設備都會封裝一個新的結構體, 然后使用kzalloc來分配內存(結構體)*/struct cdev *cdev_alloc(void)/*函數功能: 添加一個字符設備到系統(tǒng)參數說明: p:設備的cdev結構體 dev:第一個設備的設備號 count:連續(xù)的次設備號數*/int cdev_add(struct cdev *p, dev_t dev, unsigned count)/*函數功能: 從系統(tǒng)中移除一個cdev參數說明: p:要移除的cdev結構體*/void cdev_del(struct cdev *p)
把上面的API分成兩段,一段是分配和釋放設備號的,另一段就是分配字符設備和注冊到系統(tǒng)中的。
register_chrdev_region() 和 alloc_chrdev_region() 的區(qū)別:
register_chrdev_region() 函數用于已知起始設備的設備號的情況,而alloc_chrdev_region() 用于設備號未知,向系統(tǒng)動態(tài)申請未被占用的設備號的情況。alloc_chrdev_region() 相比于 register_chrdev_region() 的優(yōu)點就在于它會自動避開設備號重復的沖突。
字符設備的驅動架構下面是寫一個字符設備驅動的一般步驟:
(1) 為設備定義一個設備相關的結構體(包含設備所涉及的cdev,私有數據及鎖等信息)
(2) 初始化函數xxx_init的定義
1. 向系統(tǒng)申請設備號(register_chrdev_region()或alloc_chrdev_region())
2. 使用kzalloc申請設備內存(為(1)中定義的結構體申請存儲空間)
3. 調用cdev_init()初始化cdev
4. 調用cdev_add()向系統(tǒng)注冊設備
(3) 卸載函數xxx_exit的定義
1. 釋放設備號(unregister_chrdev_region())
2. 調用cdev_del()注銷設備
(4) 定義file_operations
1. 實現(xiàn)write()函數 (copy_to_user())
2. 實現(xiàn)read()函數(copy_from_user())
3. 根據需要實現(xiàn)其他函數....
模板程序
/*設備結構體*/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)向系統(tǒng)注冊設備 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表示動態(tài)分配 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. 向系統(tǒng)注冊設備 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);}
通過上面的分析發(fā)現(xiàn)和上面的驅動框架是一樣的,只是做了個封裝罷了。這時候是不是發(fā)現(xiàn)了兩者之間的關系,是不是很多驅動都是用這兩個函數來注冊的。有些書就只講前面的,而有些教程又只講后面。所以搞得云里霧里的。
簡化上面的驅動模板
/*設備結構體*/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");
喜歡這篇文章,歡迎點贊,分享,關注!
更多精彩文章,歡迎關注微信公眾號"嵌入式軟件開發(fā)交流"
以上就是關于怎么安裝pos機驅動,字符設備驅動的知識,后面我們會繼續(xù)為大家整理關于怎么安裝pos機驅動的知識,希望能夠幫助到大家!
