MISC 驱动其实就是最简单的字符设备驱动,所有的 MISC 设备驱动的主设备号都为 10,不同的设备使用不同的从设备号。
MISC 设备会自动创建 cdev,不需要像我们以前那样手动创建,因此采用 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;
};
MISC 设备的主设备号为 10,这个是固定的,需要用户指定子设备号, Linux 系统已经预定义了一些 MISC 设备的子设备号,这些预定义的子设备号定义在include/linux/miscdevice.h 文件中,如下所示:
#define PSMOUSE_MINOR 1 #define MS_BUSMOUSE_MINOR 2 #define ATIXL_BUSMOUSE_MINOR 3 #define ATARIMOUSE_MINOR 5 #define SUN_MOUSE_MINOR 6 #define APOLLO_MOUSE_MINOR 7 #define PC110PAD_MINOR 9 #define WATCHDOG_MINOR 130 #define TEMP_MINOR 131 #define RTC_MINOR 135 #define EFI_RTC_MINOR 136 #define VHCI_MINOR 137 #define SUN_OPENPROM_MINOR 139 #define DMAPI_MINOR 140 #define NVRAM_MINOR 144 #define SGI_MMTIMER 153 #define STORE_QUEUE_MINOR 155 #define I2O_MINOR 166 #define MICROCODE_MINOR 184 #define VFIO_MINOR 196 #define TUN_MINOR 200 #define CUSE_MINOR 203 #define MWAVE_MINOR 219 #define MPT_MINOR 220 #define MPT2SAS_MINOR 221 #define MPT3SAS_MINOR 222 #define UINPUT_MINOR 223 #define MISC_MCELOG_MINOR 227 #define HPET_MINOR 228 #define FUSE_MINOR 229 #define KVM_MINOR 232 #define BTRFS_MINOR 234 #define AUTOFS_MINOR 235 #define MAPPER_CTRL_MINOR 236 #define LOOP_CTRL_MINOR 237 #define VHOST_NET_MINOR 238 #define UHID_MINOR 239 #define USERIO_MINOR 240 #define MISC_DYNAMIC_MINOR 255
miscdevice 结构体中的name 就是此 MISC 设备名字,当此设备注册成功以后就会在/dev 目录下生成一个名为 name的设备文件(后面驱动实例我们会看到这一点)。 fops 就是字符设备的操作集合, MISC 设备驱动最终是需要使用用户提供的 fops操作集合。
当设置好 miscdevice 以后就需要使用 misc_register 函数向系统中注册一个 MISC 设备,此函数原型如下:
int misc_register(struct miscdevice * misc)
字符设备驱动中我们常常会使用如下几个函数完成设备创建过程:
1 alloc_chrdev_region(); 2 cdev_init(); 3 cdev_add(); 4 class_create(); 5 device_create();
现在我们可以直接使用 misc_register 一个函数来完成上面代码中的这些步骤。
二、MISC驱动实例这个驱动代码是由之前写过的一个led点灯代码改造而来,我们主要看下一led_gpio_probe() 函数中misc_register() 的使用,它简化了我们创建设备所需要的一系列步骤。
#include#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static struct gpio_desc *led_gpio; #define MISCLED_NAME "miscled" #define MISCLED_MINOR 145 static int led_drv_open(struct inode *node, struct file *file) { printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); gpiod_direction_output(led_gpio, 0); return 0; } static int led_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offset) { int status = 0; int result = 0; printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); status = gpiod_get_value(led_gpio); result = copy_to_user(buf, &status, 1); return 1; } static int led_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset) { int result; char status; printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); result = copy_from_user(&status, buf, 1); gpiod_set_value(led_gpio, status); return 1; } static int led_drv_release(struct inode *node, struct file *file) { printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); return 0; } static struct file_operations led_drv = { .owner = THIS_MODULE, .open = led_drv_open, .read = led_drv_read, .write = led_drv_write, .release = led_drv_release, }; static struct miscdevice led_miscdev = { .minor = MISCLED_MINOR, .name = MISCLED_NAME, .fops = &led_drv, }; static int led_gpio_probe(struct platform_device *pdev) { int ret =0; printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); led_gpio = gpiod_get(&pdev->dev, "led", 0); if(IS_ERR(led_gpio)) { printk(KERN_ERR "gpiod_get is errrn"); return -1; } ret = misc_register(&led_miscdev); if(ret < 0) { printk("misc device register failed!rn"); return -EFAULT; } return 0; } static int led_gpio_remove(struct platform_device *pdev) { printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); misc_deregister(&led_miscdev); gpiod_put(led_gpio); return 0; } static const struct of_device_id my_led[] = { {.compatible = "my,led_driver"}, {}, }; static struct platform_driver led_gpio_driver = { .probe = led_gpio_probe, .remove = led_gpio_remove, .driver = { .name = "led_gpio", .of_match_table = my_led, }, }; static int __init led_init(void) { int result; printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); result = platform_driver_register(&led_gpio_driver); return result; } static void __exit led_exit(void) { printk(KERN_INFO "%s %s line is %d rn", __FILE__, __FUNCTION__, __LINE__); platform_driver_unregister(&led_gpio_driver); } module_init(led_init); module_exit(led_exit); MODULE_LICENSE("GPL");
驱动加载并且与设备中的device匹配之后(),我们在/dev目录下面看到一个名为"miscled"的设备,主设备号为10,此设备号为145,与我们代码设置的一致。
编写一个简单的测试程序,write()和read()设备节点,进而触发驱动fops文件操作函数。
#include#include #include #include #include #include int main(int argc, char **argv) { int fd; char status; if (argc != 3) { printf("Usage: %s n", argv[0]); return -1; } fd = open(argv[1], O_RDWR | O_NONBLOCK); if (fd == -1) { printf("can not open file %sn", argv[1]); return -1; } if (0 == strcmp(argv[2], "on")) { status = 1; write(fd, &status, 1); } else { status = 0; write(fd, &status, 1); } read(fd, &status, 1); printf("status is %dn", status); close(fd); return 0; }
在开发板上进行测试,打印如下:
[root@Joy:/dev]# /mnt/misc_test /dev/miscled on [ 1133.332943] /home/book/code/test/misc_drv.c led_drv_open line is 27 [ 1133.340139] /home/book/code/test/misc_drv.c led_drv_write line is 46 [ 1133.352154] /home/book/code/test/misc_drv.c led_drv_read line is 36 status is 1 [ 1133.360455] /home/book/code/test/misc_drv.c led_drv_release line is 54 [root@100ask:/dev]# /mnt/misc_test /dev/miscled off [ 1146.420773] /home/book/code/test/misc_drv.c led_drv_open line is 27 [ 1146.428629] /home/book/code/test/misc_drv.c led_drv_write line is 46 [ 1146.436893] /home/book/code/test/misc_drv.c led_drv_read line is 36 status is 0 [ 1146.445773] /home/book/code/test/misc_drv.c led_drv_release line is 54
可以看到fops对应的操作函数得到执行,比起普通的字符设备驱动MISC更加精简一些。
/面朝大海0902/



