前言
????????misc 的意思是混合、雜項的,因此 MISC 驅動也叫做雜項驅動。也就是當我們板子上的某些外設無法進行分類的時候就可以使用 MISC 驅動。 MISC 驅動其實就是最簡單的字符設備驅動,通常嵌套在 platform 總線驅動中,實現復雜的驅動,接下來就來講下一下MISC的使用,其實總結就是一句話,用MISC 設備驅動來簡化字符設備驅動的編寫,也就是替代我們之前注冊字符設備的那一堆操作。
一.MISC設備驅動簡介
? ? ? ? 所有的 MISC 設備驅動的主設備號都為 10,不同的設備使用不同的從設備號。隨著 Linux字符設備驅動的不斷增加,設備號變得越來越緊張,尤其是主設備號, MISC 設備驅動就用于解決此問題。
???????? MISC 設備會自動創建 cdev,不需要像我們以前那樣手動創建,因此采用 MISC 設備驅動可以簡化字符設備驅動的編寫。
1.miscdevice 設備結構體:
同樣的,我們要使用MISC,就需要向 Linux 注冊一個 miscdevice 設備, miscdevice是一個結構體,定義在文件 include/linux/miscdevice.h 中,內容如下:
struct miscdevice {int minor;const char *name;const struct file_operations *fops;struct list_head list;struct device *parent;struct device *this_device;const struct attribute_group **groups;const char *nodename;umode_t mode;
};
? ? 這個結構體需要我們填入的參數有:minor、 name 和 fops 這三個成員變量。
<1>minor:
????????minor 表示子設備號, MISC 設備的主設備號為 10,這個是固定的,需要用戶指定子設備號, Linux 系統已經預定義了一些 MISC 設備的子設備號,這些預定義的子設備號定義在include/linux/miscdevice.h 文件中,如下所示:
#define PSMOUSE_MINOR 1
#define MS_BUSMOUSE_MINOR 2 /* unused */
#define ATIXL_BUSMOUSE_MINOR 3 /* unused */
/*#define AMIGAMOUSE_MINOR 4 FIXME OBSOLETE */
#define ATARIMOUSE_MINOR 5 /* unused */
#define SUN_MOUSE_MINOR 6 /* unused */
#define APOLLO_MOUSE_MINOR 7 /* unused */
#define PC110PAD_MINOR 9 /* unused */
......
#define VHOST_VSOCK_MINOR 241
#define RFKILL_MINOR 242
#define MISC_DYNAMIC_MINOR 255
我們在使用的時候可以從這些預定義的子設備號中挑選一個,當然也可以自己定義,只要這個子設備號沒有被其他設備使用接口。
<2>name :
????????name 就是此 MISC 設備名字,當此設備注冊成功以后就會在/dev 目錄下生成一個名為 name的設備文件。
<3>fops :
fops 是字符設備的操作函數集合, MISC 設備驅動最終是需要使用用戶提供的 fops操作集合。也就是之前我們自己手動注冊字符設備的操作函數:
/* 設備操作函數結構體 */
static const struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = gpioled_open,.read = gpioled_read,.write = gpioled_write,.release = gpioled_release
};
2.miscdevice 注冊函數:
函數原型如下:
int misc_register(struct miscdevice * misc)函數參數和返回值含義如下:
misc:要注冊的 MISC 設備。
返回值: 負數,失敗; 0,成功。
以前我們需要自己調用一堆的函數去創建設備,比如在以前的字符設備驅動中我們會使用如下幾個函數完成設備創建過程:
/* 傳統的創建設備過程 */
alloc_chrdev_region(); /* 申請設備號 */
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 創建類 */
device_create(); /* 創建設備 */
?現在我們可以直接使用 misc_register 一個函數來完成上面的傳統的創建設備過程中的這些步驟。如下所示:
ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}
3.misc_deregister卸載函數:
當我們卸載設備驅動模塊的時候需要調用 misc_deregister 函數來注銷掉 MISC 設備,函數原型如下:
int misc_deregister(struct miscdevice *misc)函數參數和返回值含義如下:
misc:要注銷的 MISC 設備。
返回值: 負數,失敗; 0,成功。
以前注銷設備驅動的時候,我們需要調用一堆的函數去刪除此前創建的 cdev、設備等等內容,如下所示:
/* 傳統的刪除設備的過程 */
cdev_del(); /* 刪除 cdev */
unregister_chrdev_region(); /* 注銷設備號 */
device_destroy(); /* 刪除設備 */
class_destroy(); /* 刪除類 */
?現在我們只需要一個 misc_deregister 函數即可完成傳統的刪除設備的過程中的這些工作。關于MISC 設備驅動就講解到這里,接下來我們就使用 platform 加 MISC 驅動框架來編寫 led驅動。如下所示:
misc_deregister(&led_miscdev);
二、設備樹的修改:
在設備樹下的根節點添加以下結點:
gpioled{#address-cells = <1>;#size-cells = <1>;compatible = "led-gpio";pinctrl-names = "default";pinctrl-0 = <&pinctrl_led>;gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;state = "okay";};
在iomux節點下添加;
pinctrl_led:ledgrp{fsl,pin=</* 配置 GPIO1_IO03 的 IO 屬性 *bit 16:0 HYS 關閉 *bit [15:14]: 00 默認下拉 *bit [13]: 0 kepper 功能 *bit [12]: 1 pull/keeper 使能 *bit [11]: 0 關閉開路輸出 *bit [7:6]: 10 速度 100Mhz *bit [5:3]: 110 R0/6 驅動能力 *bit [0]: 0 低轉換率 */MX6UL_PAD_GPIO1_IO03__GPIO1_IO03 0x10B0>;
三、驅動代碼的編寫
1.引入框架
????????框架就使用我們之前的platfrom-led的框架不過在peobe函數里面,我們要把關于那些字符設備注冊初始化的部分刪除了:
static int led_probe(struct platform_device *dev)
{myled_init(&gpioled);return 0;
}
?剩下gpio初始化部分就可以了。
2.創建miscdevice結構體
#define DEVICE_NAME "miscled"
#define DEVICE_MINOR 145 /* 子設備號 *//* MISC設備結構體 */
static struct miscdevice led_miscdev = {.minor = DEVICE_MINOR,.name = DEVICE_NAME,.fops = &gpioled_fops,
};
3.使用.miscdevice 注冊函數
/*當誰列表的設備和驅動匹配上后執行的peobe函數*/
static int led_probe(struct platform_device *dev)
{int ret = 0;myled_init(&gpioled);ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}
4.misc_deregister卸載函數
static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);misc_deregister(&led_miscdev);printk("gpioled exit!\r\n");return 0;
}
完整代碼:
/**************頭文件區域*********************************************************/
#include <linux/ide.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/errno.h>#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/irq.h>
#include <linux/poll.h>
#include <linux/platform_device.h>
#include <linux/fcntl.h>
#include <linux/miscdevice.h>#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <linux/io.h>
/**********************************************************************************//************************函數定義-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file);
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr);
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr);
static int gpioled_open(struct inode *inode , struct file *file);
static int led_probe(struct platform_device *dev);
static int led_remove(struct platform_device *dev);
/************************函數定義-end********************************************//************************宏定義-begin***********************************************/
#define DEVICE_NAME "miscled"
#define DEVICE_MINOR 145 /* 子設備號 */
#define DEVICE_CNT 1
#define BEEP_ON 1
#define BEEP_OFF 0
/************************宏定義-end********************************************//************************結構體定義-begin***********************************************/
/* dtsled設備信息結構體 */
struct dts_dev
{dev_t devid; /* 設備號 */struct cdev cdev; /* cdev */struct class *class; /* 類 */struct device *device; /* 設備 */struct device_node *nd; /* 設備節點 */int led_gpio; /* led 所使用的 GPIO 編號 */
};
struct dts_dev gpioled; /* led設備 *//* 設備操作函數結構體 */
static const struct file_operations gpioled_fops = {.owner = THIS_MODULE,.open = gpioled_open,.read = gpioled_read,.write = gpioled_write,.release = gpioled_release
};/* MISC設備結構體 */
static struct miscdevice led_miscdev = {.minor = DEVICE_MINOR,.name = DEVICE_NAME,.fops = &gpioled_fops,
};/* 匹配列表 */
static const struct of_device_id led_of_match[] = {{ .compatible = "led-gpio" },{ /* Sentinel */ }
};static struct platform_driver led_driver = {.driver = {.name = "imx6ul-led",.of_match_table = led_of_match,},.probe = led_probe,.remove = led_remove,
};
/************************結構體定義-end***********************************************//************************file_operations操作函數-begin***********************************************/
static int gpioled_release(struct inode *inode, struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_read(struct file *file, char __user *buf, size_t size, loff_t *ptr)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);return 0;
}
static ssize_t gpioled_write(struct file *file, const char __user *buf, size_t size, loff_t *ptr)
{int ret;unsigned char databuf[1];unsigned char ledstate;struct dts_dev *dev = file->private_data;ret = __copy_from_user(databuf,buf,size);if(ret < 0){printk("kernel write failed!\r\n");return -EFAULT;}ledstate = databuf[0];if(ledstate == BEEP_OFF){ gpio_set_value(dev->led_gpio,1);}else if(ledstate == BEEP_ON){gpio_set_value(dev->led_gpio,0);}return 0;
}
static int gpioled_open(struct inode *inode , struct file *file)
{printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);file->private_data = &gpioled; /* 設置私有數據 */ return 0;
}
/************************file_operations操作函數-end***********************************************//*****************led初始化函數************************/
static int myled_init(struct dts_dev *dev)
{int ret = 0;/* 1、設置 led 所使用的 GPIO */ dev->nd = of_find_node_by_path("/gpioled");if(dev->nd == NULL){printk("gpioled node cant not found!\r\n");return -EINVAL;}else{printk("gpioled node hase been found!\r\n");}/* 2、 獲取設備樹中的 gpio 屬性,得到 led 所使用的 led 編號 */ dev->led_gpio = of_get_named_gpio(dev->nd,"gpios",0);if(dev->led_gpio < 0){printk("can't get led-gpio\r\n");return -EINVAL;}printk("led-gpio num = %d\r\n", dev->led_gpio); /* 3、設置 GPIO1_IO03 為輸出,并且輸出高電平,默認關閉 led 燈 */ ret = gpio_request(dev->led_gpio,"led");if(ret < 0){printk("led-gpio request fail\r\n"); return -EINVAL;}ret = gpio_direction_output(dev->led_gpio,1);if(ret < 0) {printk("can't set gpio!\r\n");}return ret;
}/************************platfrom操作函數-begin***********************************************/
/*當誰列表的設備和驅動匹配上后執行的peobe函數*/
static int led_probe(struct platform_device *dev)
{int ret = 0;myled_init(&gpioled);ret = misc_register(&led_miscdev);if(ret < 0){printk("misc device register failed!\r\n");return -EFAULT;}return 0;
}
static int led_remove(struct platform_device *dev)
{gpio_set_value(gpioled.led_gpio,1);gpio_free(gpioled.led_gpio);misc_deregister(&led_miscdev);printk("gpioled exit!\r\n");return 0;
}
/************************platfrom操作函數-endn***********************************************/static int __init gpioled_init(void)
{return platform_driver_register(&led_driver);
}static void __exit gpioled_exit(void)
{platform_driver_unregister(&led_driver);
}module_init(gpioled_init);
module_exit(gpioled_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("oudafa");
四、編寫測試 APP
這里的測試APP和之前得沒什么區別,不用改:
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"/** @description : main主程序* @param - argc : argv數組元素個數* @param - argv : 具體參數* @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];/* 打開led驅動 */fd = open(filename, O_RDWR);if(fd < 0){printf("file %s open failed!\r\n", argv[1]);return -1;}databuf[0] = atoi(argv[2]); /* 要執行的操作:打開或關閉 *//* 向/dev/led文件寫入數據 */retvalue = write(fd, databuf, sizeof(databuf));if(retvalue < 0){printf("LED Control Failed!\r\n");close(fd);return -1;}retvalue = close(fd); /* 關閉文件 */if(retvalue < 0){printf("file %s close failed!\r\n", argv[1]);return -1;}return 0;
}
五、運行測試
1.編寫makefile
并且使用make命令得到.ko文件和APP文件:
KERN_DIR = /home/odf/linux-imx/linux-imxall:clearmake -C $(KERN_DIR) M=`pwd` modules $(CROSS_COMPILE)gcc -o miscledApp miscledApp.c clean:clearmake -C $(KERN_DIR) M=`pwd` modules cleanrm -rf modules.orderrm -f miscledAppobj-m += miscled.o
2.加載模塊
將編譯出來 .ko文件 和 app文件 這兩個文件拷貝到 rootfs/lib/modules/4.1.15 目錄中目錄中,重啟開發板,進入到目錄 rootfs/lib/modules/4.1.15 目錄中中,輸入如下命令加載 驅動模塊:
insmod miscled.ko
所有的 misc 設備都屬于同一個類, /sys/class/misc 目錄下就是 misc 這個類的所有設備,每個設備對應一個子目錄。
????????驅動與設備匹配成功以后就會生成/dev/miscled這個設備驅動文件,輸入如下命令查看這個文件的主次設備號:
ls -l /sys/class/misc
結果如下所示:?
從上面可以看出, /dev/misc_beep 這個設備的主設備號為 10,次設備號為 144,和我們驅動程序里面設置的一致。
3.測試代碼
輸入如下命令打開 led:
./misc_beep_app /dev/miscled 1
輸入如下命令關閉?led:
./misc_beep_app /dev/miscled 0