--- title: Firmadyne MIPS kernel-v2.6.32 devfs_stubs.c tags: firmadyne lang: zh_tw --- --- # Firmadyne MIPS kernel-v2.6.32 devfs_stubs.c Source Code: https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/drivers/firmadyne/devfs_stubs.c 紀錄追 Code 過程 --- **目錄:** [TOC] ## devfs_stubs.c Line 13 定義了 Macro STUB_ENTRIES 裡面含有更多個 Macro DEVICE ```c=13 #define STUB_ENTRIES \ DEVICE(acos_nat_cli, 100, 0, open, read, write, close, acos_ioctl) \ ... \ DEVICE(nvram, 111, 0, open, read, write, close, ioctl) \ ... \ DEVICE(zybtnio, 220, 0, open, read, write, close, ioctl) ``` --- Line 121 ~ 136 ```c=121 #define DEVICE(a, b, c, d, e, f, g, h) \ static dev_t a##_devno = MKDEV(b, c); \ static struct cdev a##_cdev; \ static struct class *a##_class; \ static struct device *a##_dev; \ static struct file_operations a##_fops = { \ .owner = THIS_MODULE, \ .open = d, \ .read = e, \ .write = f, \ .release = g, \ .unlocked_ioctl = h, \ }; STUB_ENTRIES #undef DEVICE ``` 以 `DEVICE(nvram, 111, 0, open, read, write, close, ioctl)` 舉例 會被展開成: ```c static dev_t nvram_devno = MKDEV(111, 0); \ static struct cdev nvram_cdev; \ static struct class *nvram_class; \ static struct device *nvram_dev; \ static struct file_operations nvram_fops = { \ .owner = THIS_MODULE, \ .open = open, \ .read = read, \ .write = write, \ .release = close, \ .unlocked_ioctl = ioctl, \ }; ``` 定義了各種 `dev_t`, `struct cdev`, `struct class`, `struct device`, `struct file_operations` 的變數 --- 在 Line 138 中定義了 `register_devfs_stubs` Line 141 中的 extern devfs 初始化在 [/drivers/firmadyne/firmadyne.c](https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/drivers/firmadyne/firmadyne.c),為 1 ```c=141 if (!devfs) { return ret; } ``` 所以不會直接 return 掉 --- ```c=145 #define DEVICE(a, b, c, d, e, f, g, h) \ if ((ret = register_chrdev_region(a##_devno, 1, #a)) < 0) { \ printk(KERN_WARNING MODULE_NAME": Cannot register character device: %s, 0x%x, 0x%x!\n", #a, MAJOR(a##_devno), MINOR(a##_devno)); \ goto a##_out; \ } \ \ if (IS_ERR(a##_class = class_create(THIS_MODULE, #a))) { \ printk(KERN_WARNING MODULE_NAME": Cannot create device class: %s!\n", #a); \ unregister_chrdev_region(a##_devno, 1); \ ret = PTR_ERR(a##_class); \ goto a##_out; \ } \ a##_class->dev_uevent = acl; \ \ cdev_init(&a##_cdev, &a##_fops); \ \ if ((ret = cdev_add(&a##_cdev, a##_devno, 1)) < 0) { \ printk(KERN_WARNING MODULE_NAME": Cannot add class device: %s!\n", #a); \ class_destroy(a##_class); \ unregister_chrdev_region(a##_devno, 1); \ goto a##_out; \ } \ \ if (IS_ERR(a##_dev = device_create(a##_class, NULL, a##_devno, NULL, #a))) { \ printk(KERN_WARNING MODULE_NAME": Cannot create device: %s!\n", #a); \ cdev_del(&a##_cdev); \ class_destroy(a##_class); \ unregister_chrdev_region(a##_devno, 1); \ ret = PTR_ERR(a##_dev); \ } \ a##_out: STUB_ENTRIES #undef DEVICE ``` 以 `DEVICE(nvram, 111, 0, open, read, write, close, ioctl)` 舉例 會被展開成: ```c if ((ret = register_chrdev_region(nvram_devno, 1, "nvram")) < 0) { \ printk(KERN_WARNING MODULE_NAME": Cannot register character device: %s, 0x%x, 0x%x!\n", "nvram", MAJOR(nvram_devno), MINOR(nvram_devno)); goto nvram_out; } ``` 註冊設備,編號為 `nvram_devno`,設備數量 1 個,名稱為 "nvram" ```c if (IS_ERR(nvram_class = class_create(THIS_MODULE, "nvram"))) { printk(KERN_WARNING MODULE_NAME": Cannot create device class: %s!\n", "nvram"); unregister_chrdev_region(nvram_devno, 1); ret = PTR_ERR(nvram_class); goto nvram_out; } nvram_class->dev_uevent = acl; ``` 引用 [linux中class_create和class_register说明](https://www.cnblogs.com/skywang12345/archive/2013/05/15/driver_class.html) > 内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。 ```c cdev_init(&nvram_cdev, &nvram_fops); if ((ret = cdev_add(&nvram_cdev, nvram_devno, 1)) < 0) { printk(KERN_WARNING MODULE_NAME": Cannot add class device: %s!\n", "nvram"); class_destroy(nvram_class); unregister_chrdev_region(nvram_devno, 1); goto nvram_out; } ``` `cdev_init` 設定了該device 的 file operations `cdev_add` 將此驅動程式註冊到系統中 ```c if (IS_ERR(nvram_dev = device_create(nvram_class, NULL, nvram_devno, NULL, "nvram"))) { printk(KERN_WARNING MODULE_NAME": Cannot create device: %s!\n", "nvram"); cdev_del(&nvram_cdev); class_destroy(nvram_class); unregister_chrdev_region(nvram_devno, 1); ret = PTR_ERR(nvram_dev); } nvram_out: ``` --- ```c=183 void unregister_devfs_stubs(void) { if (!devfs) { return; } #define DEVICE(a, b, c, d, e, f, g, h) \ device_destroy(a##_class, a##_devno); \ cdev_del(&a##_cdev); \ class_destroy(a##_class); \ unregister_chrdev_region(a##_devno, 1); STUB_ENTRIES #undef DEVICE } ``` 將`register_devfs_stubs`創的、註冊的東西都刪掉 ### dev_t 在 [/include/linux/cdev.h](https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/include/linux/cdev.h) -> [/include/linux/kobject.h](https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/include/linux/kobject.h) -> [/include/linux/types.h](https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/include/linux/types.h) ```c=18 typedef __u32 __kernel_dev_t; ``` ```c=21 typedef __kernel_dev_t dev_t; ``` dev_t 是個 unsigned 32bit 的咚咚 估狗了一下,有人中文翻譯成`設備號` #### MKDEV 在 [/include/linux/kdev_t.h](https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/include/linux/kdev_t.h) ```c=4 #define MINORBITS 20 ``` ```c=9 #define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi)) ``` 前 20 bits 為 major number,後 12 bits 為 minor number ### cdev 直接把 `cdev` 這個字拿去餵狗,會發現中文叫他`字元裝置`、`字符設備`之類的 在 [/include/linux/cdev.h](https://github.com/firmadyne/kernel-v2.6.32/blob/firmadyne-v2.6.32.70/include/linux/cdev.h) ```c=12 struct cdev { struct kobject kobj; struct module *owner; const struct file_operations *ops; struct list_head list; dev_t dev; unsigned int count; }; ``` ### file_operations 就看這篇吧: [裝置驅動程式的基本知識](https://hackmd.io/NY0zJGwRR8KbCfkYdmTUsg?view) ## 總結 `DEVICE(devicename, 111, 0, open, read, write, close, ioctl)` 會創名為 devicename 的裝置(在 /dev 底下),主裝置號為 111,副裝置號為 0 file operations 分別為 open、read、write、close、ioctl