---
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