Linux MISC 驱动实验
目錄
- MISC 設(shè)備驅(qū)動簡介
- 硬件原理圖分析
- 實驗程序編寫
- 修改設(shè)備樹
- beep 驅(qū)動程序編寫
- 編寫測試APP
- 運行測試
- 編譯驅(qū)動程序和測試APP
- 運行測試
misc 的意思是混合、雜項的,因此MISC 驅(qū)動也叫做雜項驅(qū)動,也就是當我們板子上的某些外設(shè)無法進行分類的時候就可以使用MISC 驅(qū)動。MISC 驅(qū)動其實就是最簡單的字符設(shè)備驅(qū)動,通常嵌套在platform 總線驅(qū)動中,實現(xiàn)復雜的驅(qū)動,本章我們就來學習一下MISC 驅(qū)動的編寫。
MISC 設(shè)備驅(qū)動簡介
所有的MISC 設(shè)備驅(qū)動的主設(shè)備號都為10,不同的設(shè)備使用不同的從設(shè)備號。隨著Linux字符設(shè)備驅(qū)動的不斷增加,設(shè)備號變得越來越緊張,尤其是主設(shè)備號,MISC 設(shè)備驅(qū)動就用于解決此問題。MISC 設(shè)備會自動創(chuàng)建cdev,不需要像我們以前那樣手動創(chuàng)建,因此采用MISC 設(shè)備驅(qū)動可以簡化字符設(shè)備驅(qū)動的編寫。我們需要向Linux 注冊一個miscdevice 設(shè)備,miscdevice
是一個結(jié)構(gòu)體,定義在文件include/linux/miscdevice.h 中,內(nèi)容如下:
定義一個MISC 設(shè)備(miscdevice 類型)以后我們需要設(shè)置minor、name 和fops 這三個成員變量。minor 表示子設(shè)備號,MISC 設(shè)備的主設(shè)備號為10,這個是固定的,需要用戶指定子設(shè)備號,Linux 系統(tǒng)已經(jīng)預(yù)定義了一些MISC 設(shè)備的子設(shè)備號,這些預(yù)定義的子設(shè)備號定義在
include/linux/miscdevice.h 文件中,如下所示:
我們在使用的時候可以從這些預(yù)定義的子設(shè)備號中挑選一個,當然也可以自己定義,只要這個子設(shè)備號沒有被其他設(shè)備使用接口。
name 就是此MISC 設(shè)備名字,當此設(shè)備注冊成功以后就會在/dev 目錄下生成一個名為name的設(shè)備文件。fops 就是字符設(shè)備的操作集合,MISC 設(shè)備驅(qū)動最終是需要使用用戶提供的fops操作集合。
當設(shè)置好miscdevice 以后就需要使用misc_register 函數(shù)向系統(tǒng)中注冊一個MISC 設(shè)備,此函數(shù)原型如下:
函數(shù)參數(shù)和返回值含義如下:
misc:要注冊的MISC 設(shè)備。
返回值:負數(shù),失敗;0,成功。
以前我們需要自己調(diào)用一堆的函數(shù)去創(chuàng)建設(shè)備,比如在以前的字符設(shè)備驅(qū)動中我們會使用如下幾個函數(shù)完成設(shè)備創(chuàng)建過程:
1 alloc_chrdev_region(); /* 申請設(shè)備號*/ 2 cdev_init(); /* 初始化cdev */ 3 cdev_add(); /* 添加cdev */ 4 class_create(); /* 創(chuàng)建類*/ 5 device_create(); /* 創(chuàng)建設(shè)備*/現(xiàn)在我們可以直接使用misc_register 一個函數(shù)來完成示例代碼57.1.3 中的這些步驟。當我們卸載設(shè)備驅(qū)動模塊的時候需要調(diào)用misc_deregister 函數(shù)來注銷掉MISC 設(shè)備,函數(shù)原型如下:
int misc_deregister(struct miscdevice *misc)函數(shù)參數(shù)和返回值含義如下:
misc:要注銷的MISC 設(shè)備。
返回值:負數(shù),失敗;0,成功。
以前注銷設(shè)備驅(qū)動的時候,我們需要調(diào)用一堆的函數(shù)去刪除此前創(chuàng)建的cdev、設(shè)備等等內(nèi)容,如下所示:
現(xiàn)在我們只需要一個misc_deregister 函數(shù)即可完成示例代碼57.1.4 中的這些工作。關(guān)于MISC 設(shè)備驅(qū)動就講解到這里,接下來我們就使用platform 加MISC 驅(qū)動框架來編寫beep 蜂鳴器驅(qū)動。
硬件原理圖分析
本章實驗我們只使用到IMX6U-ALPHA 開發(fā)板上的BEEP 蜂鳴器,因此實驗硬件原理圖參考14.3 小節(jié)即可。
實驗程序編寫
本實驗對應(yīng)的例程路徑為:開發(fā)板光盤-> 2、Linux 驅(qū)動例程-> 17_misc。
本章實驗我們采用platform 加misc 的方式編寫beep 驅(qū)動,這也是實際的Linux 驅(qū)動中很常用的方法。采用platform 來實現(xiàn)總線、設(shè)備和驅(qū)動,misc 主要負責完成字符設(shè)備的創(chuàng)建。
修改設(shè)備樹
本章實驗我們需要用到蜂鳴器,因此需要在imx6ull-alientek-emmc.dts 文件中創(chuàng)建蜂鳴器設(shè)備節(jié)點,這里我們直接使用46.3.1 小節(jié)創(chuàng)建的beep 這個設(shè)備節(jié)點即可。
beep 驅(qū)動程序編寫
新建名為“19_miscbeep”的文件夾,然后在19_miscbeep 文件夾里面創(chuàng)建vscode 工程,工作區(qū)命名為“miscbeep。新建名為miscbeep.c 的驅(qū)動文件,在miscbeep.c 中輸入如下所示內(nèi)容:
#include <linux/types.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/ide.h> #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/gpio.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/of_gpio.h> #include <linux/platform_device.h> #include <linux/miscdevice.h> #include <asm/mach/map.h> #include <asm/uaccess.h> #include <asm/io.h> /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : miscbeep.c 作者 : 左忠凱 版本 : V1.0 描述 : 采用MISC的蜂鳴器驅(qū)動程序。 其他 : 無 論壇 : www.openedv.com 日志 : 初版V1.0 2019/8/20 左忠凱創(chuàng)建 ***************************************************************/ #define MISCBEEP_NAME "miscbeep" /* 名字 */ #define MISCBEEP_MINOR 144 /* 子設(shè)備號 */ #define BEEPOFF 0 /* 關(guān)蜂鳴器 */ #define BEEPON 1 /* 開蜂鳴器 *//* miscbeep設(shè)備結(jié)構(gòu)體 */ struct miscbeep_dev{dev_t devid; /* 設(shè)備號 */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設(shè)備 */struct device_node *nd; /* 設(shè)備節(jié)點 */int beep_gpio; /* beep所使用的GPIO編號 */ };struct miscbeep_dev miscbeep; /* beep設(shè)備 *//** @description : 打開設(shè)備* @param - inode : 傳遞給驅(qū)動的inode* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個叫做private_data的成員變量* 一般在open的時候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。* @return : 0 成功;其他 失敗*/ static int miscbeep_open(struct inode *inode, struct file *filp) {filp->private_data = &miscbeep; /* 設(shè)置私有數(shù)據(jù) */return 0; }/** @description : 向設(shè)備寫數(shù)據(jù) * @param - filp : 設(shè)備文件,表示打開的文件描述符* @param - buf : 要寫給設(shè)備寫入的數(shù)據(jù)* @param - cnt : 要寫入的數(shù)據(jù)長度* @param - offt : 相對于文件首地址的偏移* @return : 寫入的字節(jié)數(shù),如果為負值,表示寫入失敗*/ static ssize_t miscbeep_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) {int retvalue;unsigned char databuf[1];unsigned char beepstat;struct miscbeep_dev *dev = filp->private_data;retvalue = copy_from_user(databuf, buf, cnt);if(retvalue < 0) {printk("kernel write failed!\r\n");return -EFAULT;}beepstat = databuf[0]; /* 獲取狀態(tài)值 */if(beepstat == BEEPON) { gpio_set_value(dev->beep_gpio, 0); /* 打開蜂鳴器 */} else if(beepstat == BEEPOFF) {gpio_set_value(dev->beep_gpio, 1); /* 關(guān)閉蜂鳴器 */}return 0; }/* 設(shè)備操作函數(shù) */ static struct file_operations miscbeep_fops = {.owner = THIS_MODULE,.open = miscbeep_open,.write = miscbeep_write, };/* MISC設(shè)備結(jié)構(gòu)體 */ static struct miscdevice beep_miscdev = {.minor = MISCBEEP_MINOR,.name = MISCBEEP_NAME,.fops = &miscbeep_fops, };/** @description : flatform驅(qū)動的probe函數(shù),當驅(qū)動與* 設(shè)備匹配以后此函數(shù)就會執(zhí)行* @param - dev : platform設(shè)備* @return : 0,成功;其他負值,失敗*/ static int miscbeep_probe(struct platform_device *dev) {int ret = 0;printk("beep driver and device was matched!\r\n");/* 設(shè)置BEEP所使用的GPIO *//* 1、獲取設(shè)備節(jié)點:beep */miscbeep.nd = of_find_node_by_path("/beep");if(miscbeep.nd == NULL) {printk("beep node not find!\r\n");return -EINVAL;} /* 2、 獲取設(shè)備樹中的gpio屬性,得到BEEP所使用的BEEP編號 */miscbeep.beep_gpio = of_get_named_gpio(miscbeep.nd, "beep-gpio", 0);if(miscbeep.beep_gpio < 0) {printk("can't get beep-gpio");return -EINVAL;}/* 3、設(shè)置GPIO5_IO01為輸出,并且輸出高電平,默認關(guān)閉BEEP */ret = gpio_direction_output(miscbeep.beep_gpio, 1);if(ret < 0) {printk("can't set gpio!\r\n");}/* 一般情況下會注冊對應(yīng)的字符設(shè)備,但是這里我們使用MISC設(shè)備* 所以我們不需要自己注冊字符設(shè)備驅(qū)動,只需要注冊misc設(shè)備驅(qū)動即可*/ret = misc_register(&beep_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0; }/** @description : platform驅(qū)動的remove函數(shù),移除platform驅(qū)動的時候此函數(shù)會執(zhí)行* @param - dev : platform設(shè)備* @return : 0,成功;其他負值,失敗*/ static int miscbeep_remove(struct platform_device *dev) {/* 注銷設(shè)備的時候關(guān)閉LED燈 */gpio_set_value(miscbeep.beep_gpio, 1);/* 注銷misc設(shè)備 */misc_deregister(&beep_miscdev);return 0; }/* 匹配列表 */static const struct of_device_id beep_of_match[] = {{ .compatible = "atkalpha-beep" },{ /* Sentinel */ }};/* platform驅(qū)動結(jié)構(gòu)體 */ static struct platform_driver beep_driver = {.driver = {.name = "imx6ul-beep", /* 驅(qū)動名字,用于和設(shè)備匹配 */.of_match_table = beep_of_match, /* 設(shè)備樹匹配表 */},.probe = miscbeep_probe,.remove = miscbeep_remove, };/** @description : 驅(qū)動出口函數(shù)* @param : 無* @return : 無*/ static int __init miscbeep_init(void) {return platform_driver_register(&beep_driver); }/** @description : 驅(qū)動出口函數(shù)* @param : 無* @return : 無*/ static void __exit miscbeep_exit(void) {platform_driver_unregister(&beep_driver); }module_init(miscbeep_init); module_exit(miscbeep_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");第29~94 行,標準的字符設(shè)備驅(qū)動。
第97~101 行,MISC 設(shè)備beep_miscdev,第98 行設(shè)置子設(shè)備號為144,第99 行設(shè)置設(shè)備名字為“miscbeep”,這樣當系統(tǒng)啟動以后就會在/dev/目錄下存在一個名為“miscbeep”的設(shè)備文件。第100 行,設(shè)置MISC 設(shè)備的操作函數(shù)集合,為file_operations 類型。
第109~145 行,platform 框架的probe 函數(shù),當驅(qū)動與設(shè)備匹配以后此函數(shù)就會執(zhí)行,首先在此函數(shù)中初始化BEEP 所使用的IO。最后在138 行通過misc_register 函數(shù)向Linux 內(nèi)核注冊MISC 設(shè)備,也就是前面定義的beep_miscdev。
第152~160 行,platform 框架的remove 函數(shù),在此函數(shù)中調(diào)用misc_deregister 函數(shù)來注銷MISC 設(shè)備。
第163~196,標準的platform 驅(qū)動。
編寫測試APP
新建miscbeepApp.c 文件,然后在里面輸入如下所示內(nèi)容:
#include "stdio.h" #include "unistd.h" #include "sys/types.h" #include "sys/stat.h" #include "fcntl.h" #include "stdlib.h" #include "string.h" /*************************************************************** Copyright ? ALIENTEK Co., Ltd. 1998-2029. All rights reserved. 文件名 : miscbeepApp.c 作者 : 左忠凱 版本 : V1.0 描述 : MISC驅(qū)動框架下的beep測試APP。 其他 : 無 使用方法 :./miscbeepApp /dev/miscbeep 0 關(guān)閉蜂鳴器./misdcbeepApp /dev/miscbeep 1 打開蜂鳴器 論壇 : www.openedv.com 日志 : 初版V1.0 2019/8/20 左忠凱創(chuàng)建 ***************************************************************/ #define BEEPOFF 0 #define BEEPON 1/** @description : main主程序* @param - argc : argv數(shù)組元素個數(shù)* @param - argv : 具體參數(shù)* @return : 0 成功;其他 失敗*/ int main(int argc, char *argv[]) {int fd, retvalue;char *filename;unsigned char databuf[1];if(argc != 3){printf("Error Usage!\r\n");return -1;}filename = argv[1];fd = open(filename, O_RDWR); /* 打開beep驅(qū)動 */if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要執(zhí)行的操作:打開或關(guān)閉 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("BEEP Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 關(guān)閉文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0; }miscbeepApp.c 文件內(nèi)容和其他例程的測試APP 基本一致,很簡單,這里就不講解了。
運行測試
編譯驅(qū)動程序和測試APP
1、編譯驅(qū)動程序
編寫Makefile 文件,本章實驗的Makefile 文件和第四十章實驗基本一樣,只是將obj-m 變量的值改為“miscbeep.o”,Makefile 內(nèi)容如下所示:
第4 行,設(shè)置obj-m 變量的值為“miscbeep.o”。
輸入如下命令編譯出驅(qū)動模塊文件:
編譯成功以后就會生成一個名為“miscbeep.ko”的驅(qū)動模塊文件。
2、編譯測試APP
輸入如下命令編譯測試miscbeepApp.c 這個測試程序:
編譯成功以后就會生成miscbeepApp 這個應(yīng)用程序。
運行測試
將上一小節(jié)編譯出來miscbeep.ko 和miscbeepApp 這兩個文件拷貝到
rootfs/lib/modules/4.1.15 目錄中,重啟開發(fā)板,進入到目錄lib/modules/4.1.15 中,輸入如下命令加載miscbeep.ko 這個驅(qū)動模塊。
當驅(qū)動模塊加載成功以后我們可以在/sys/class/misc 這個目錄下看到一個名為“miscbeep”的子目錄,如圖57.4.2.1 所示:
所有的misc 設(shè)備都屬于同一個類,/sys/class/misc 目錄下就是misc 這個類的所有設(shè)備,每個設(shè)備對應(yīng)一個子目錄。
驅(qū)動與設(shè)備匹配成功以后就會生成/dev/miscbeep 這個設(shè)備驅(qū)動文件,輸入如下命令查看這個文件的主次設(shè)備號:
結(jié)果如圖57.4.2.2 所示:
從圖57.4.2.2 可以看出,/dev/miscbeep 這個設(shè)備的主設(shè)備號為10,次設(shè)備號為144,和我們驅(qū)動程序里面設(shè)置的一致。
輸入如下命令打開BEEP:
在輸入如下命令關(guān)閉LED 燈:
./miscbeepApp /dev/miscbeep 0 //關(guān)閉BEEP觀察一下BEEP 能否打開和關(guān)閉,如果可以的話就說明驅(qū)動工作正常,如果要卸載驅(qū)動的話輸入如下命令即可:
rmmod miscbeep.ko總結(jié)
以上是生活随笔為你收集整理的Linux MISC 驱动实验的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: lua的string.gsub初使用
- 下一篇: Linux LCD 驱动实验