---
# System prepended metadata

title: Firmadyne MIPS kernel-v2.6.32 devfs_stubs.c
tags: [firmadyne]

---

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