網(wǎng)上有很多關于怎么安裝pos機驅(qū)動,字符設備驅(qū)動的知識,也有很多人為大家解答關于怎么安裝pos機驅(qū)動的問題,今天pos機之家(m.afbey.com)為大家整理了關于這方面的知識,讓我們一起來看下吧!
本文目錄一覽:
怎么安裝pos機驅(qū)動
前言字符設備驅(qū)動是Linux最基本的驅(qū)動,很多學習驅(qū)動的朋友都是從這個開始的,比如LED,或者按鍵驅(qū)動等。但是很多時候你看一些視頻教程或書籍時,會發(fā)現(xiàn)其實不是很完整,特別是既看書又看視頻教程的,會發(fā)現(xiàn)好像不怎么一樣。接下來一起來總結(jié)一下!
字符設備結(jié)構(gòu)體在Linux內(nèi)核中, 使用cdev結(jié)構(gòu)體來描述一個字符設備。
struct cdev { struct kobject kobj; //內(nèi)嵌的kobject對象 struct module *owner;//所屬模塊 const struct file_operations *ops; //文件操作結(jié)構(gòu)體 struct list_head list; dev_t dev; //設備號 unsigned int count;};
cdev結(jié)構(gòu)體的dev_t成員定義了設備號,為32位,其中12位為主設備號,20位為次設備號。
使用下面的宏來定義dev_t
MKDEV(int major, int minor)
使用下面的宏來獲取主設備號和次設備號
MAJOR(dev_t dev)MINOR(dev_t dev)字符設備API
/*函數(shù)功能:分配一些設備號參數(shù)說明: from:起始設備號,必須要包含主設備號 count: 連續(xù)分配的數(shù)量 name: 設備或驅(qū)動的名稱*/int register_chrdev_region(dev_t from, unsigned count, const char *name)/*函數(shù)功能:分配一些字符設備號參數(shù)說明: dev:第一個被分配的設備號 baseminor:起始次設備號 count:分配的次設備號數(shù)(分配的設備號數(shù)) name: 相關設備或驅(qū)動的名稱*/int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)/*函數(shù)功能: 釋放一些設備號參數(shù)說明: from:釋放的起始設備號 count: 釋放的設備號數(shù)*/ void unregister_chrdev_region(dev_t from, unsigned count)
/*函數(shù)功能:初始化一個cdev結(jié)構(gòu)體參數(shù)說明: cdev:要初始化的cdev結(jié)構(gòu)體 fops:該字符設備的file_operations (操作函數(shù))*/void cdev_init(struct cdev *cdev, const struct file_operations *fops)/*函數(shù)功能:動態(tài)分配一個cdev結(jié)構(gòu)體說明:該函數(shù)不常用,因為字符設備都會封裝一個新的結(jié)構(gòu)體, 然后使用kzalloc來分配內(nèi)存(結(jié)構(gòu)體)*/struct cdev *cdev_alloc(void)/*函數(shù)功能: 添加一個字符設備到系統(tǒng)參數(shù)說明: p:設備的cdev結(jié)構(gòu)體 dev:第一個設備的設備號 count:連續(xù)的次設備號數(shù)*/int cdev_add(struct cdev *p, dev_t dev, unsigned count)/*函數(shù)功能: 從系統(tǒng)中移除一個cdev參數(shù)說明: p:要移除的cdev結(jié)構(gòu)體*/void cdev_del(struct cdev *p)
把上面的API分成兩段,一段是分配和釋放設備號的,另一段就是分配字符設備和注冊到系統(tǒng)中的。
register_chrdev_region() 和 alloc_chrdev_region() 的區(qū)別:
register_chrdev_region() 函數(shù)用于已知起始設備的設備號的情況,而alloc_chrdev_region() 用于設備號未知,向系統(tǒng)動態(tài)申請未被占用的設備號的情況。alloc_chrdev_region() 相比于 register_chrdev_region() 的優(yōu)點就在于它會自動避開設備號重復的沖突。
字符設備的驅(qū)動架構(gòu)下面是寫一個字符設備驅(qū)動的一般步驟:
(1) 為設備定義一個設備相關的結(jié)構(gòu)體(包含設備所涉及的cdev,私有數(shù)據(jù)及鎖等信息)
(2) 初始化函數(shù)xxx_init的定義
1. 向系統(tǒng)申請設備號(register_chrdev_region()或alloc_chrdev_region())
2. 使用kzalloc申請設備內(nèi)存(為(1)中定義的結(jié)構(gòu)體申請存儲空間)
3. 調(diào)用cdev_init()初始化cdev
4. 調(diào)用cdev_add()向系統(tǒng)注冊設備
(3) 卸載函數(shù)xxx_exit的定義
1. 釋放設備號(unregister_chrdev_region())
2. 調(diào)用cdev_del()注銷設備
(4) 定義file_operations
1. 實現(xiàn)write()函數(shù) (copy_to_user())
2. 實現(xiàn)read()函數(shù)(copy_from_user())
3. 根據(jù)需要實現(xiàn)其他函數(shù)....
模板程序
/*設備結(jié)構(gòu)體*/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, ...);}//操作函數(shù)file_operationsstruct file_operations xxx_fops = { .owner = THIS_MODULE, .read = xxx_read, .write = xxx_write, ...};//設備驅(qū)動模塊加載函數(shù)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)為設備結(jié)構(gòu)體申請內(nèi)存(推薦使用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);//設備驅(qū)動模塊卸載函數(shù)static void __exit xxx_exit(void){ //釋放設備號 unregister_chrdev_region(dev_no, 1); //注銷設備 cdev_del(&dev.cdev); ...}module_exit(xxx_exit);MODULE_LICENSE("GPL v2");簡化字符設備驅(qū)動架構(gòu)
每次編寫初始化函數(shù)都要一步一步寫,為了簡化這個流程,Linux提供了一些封裝的接口函數(shù)來讓我們調(diào)用。
/*函數(shù)功能:注冊字符設備參數(shù)說明:major:主設備號,0表示動態(tài)分配 name: 設備名 fops: 操作函數(shù)返回值: 申請的主設備號 */static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)/*函數(shù)功能:注銷字符設備參數(shù)說明:major:主設備號 name: 設備名*/static inline void unregister_chrdev(unsigned int major, const char *name)
分析一下上面兩個函數(shù)的源碼
//register_chrdev調(diào)用了__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. 為設備結(jié)構(gòu)體申請內(nèi)存 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調(diào)用__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)和上面的驅(qū)動框架是一樣的,只是做了個封裝罷了。這時候是不是發(fā)現(xiàn)了兩者之間的關系,是不是很多驅(qū)動都是用這兩個函數(shù)來注冊的。有些書就只講前面的,而有些教程又只講后面。所以搞得云里霧里的。
簡化上面的驅(qū)動模板
/*設備結(jié)構(gòu)體*/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, ...);}//操作函數(shù)file_operationsstruct file_operations xxx_fops = { .owner = THIS_MODULE, .read = xxx_read, .write = xxx_write, ...};//設備驅(qū)動模塊加載函數(shù)static int __init xxx_init(void){ ... //注冊字符設備 xxx_major = register_chrdev(0, "xxx_dev", &xxx_fops);}module_init(xxx_init);//設備驅(qū)動模塊卸載函數(shù)static void __exit xxx_exit(void){ //注銷字符設備 unregister_chrdev(major, "xxx_dev"); ...}module_exit(xxx_exit);MODULE_LICENSE("GPL v2");
喜歡這篇文章,歡迎點贊,分享,關注!
更多精彩文章,歡迎關注微信公眾號"嵌入式軟件開發(fā)交流"
以上就是關于怎么安裝pos機驅(qū)動,字符設備驅(qū)動的知識,后面我們會繼續(xù)為大家整理關于怎么安裝pos機驅(qū)動的知識,希望能夠幫助到大家!
