ZYNQ学习笔记-MISC设备驱动开发

MISC杂项设备,是Linux中用于归类ADC、蜂鸣器等不好分类的设备。其特点为共用一个主设备号10,不同设备使用不同设备号,同时会自动创建cdev和创建节点,大大简化字符设备的编写。

MISC设备简介

MISC设备由一个miscdevice结构体描述,该结构体在include/linux/miscdevice.h 中定义:

1
2
3
4
5
6
7
8
9
10
11
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变量。minor为从设备号,需要用户自己指定,在miscdevice.h中定义了一些预定的设备号,但是一般我们只需要关注一个值:

1
#define MISC_DYNAMIC_MINOR 255

将minor设置为这个值,系统将会将动态分配设备号。

name就是模块名字,注册设备后将会在dev/下生成该名字的文件。

MISC与传统字符驱动的不同

普通字符设备进行注册需要进行以下步骤:

1
2
3
4
5
alloc_chrdev_region(); /* 申请设备号 */
cdev_init(); /* 初始化 cdev */
cdev_add(); /* 添加 cdev */
class_create(); /* 创建类 */
device_create(); /* 创建设备 */

在MISC设备中,直接调用一个函数即可:

1
int misc_register(struct miscdevice * misc)

同样的,对于注销,字符设备需要执行以下步骤:

1
2
3
4
cdev_del(); /* 删除 cdev */
unregister_chrdev_region(); /* 注销设备号 */
device_destroy(); /* 删除设备 */
class_destroy(); /* 删除类 */

在MISC中,只需要一个函数:

1
int misc_deregister(struct miscdevice *misc)

可见MISC设备大大简化了字符设备的创建过程,同时还节约了设备号,不用手动创建节点。

一个例子

以PIR红外传感器驱动为例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/miscdevice.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/fs.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/time.h>
#include <asm/uaccess.h>

/* 驱动名称 */
#define DEVICE_NAME "ccd_pir"
/* gpio 寄存器虚拟地址 */
static void __iomem *gpio_add_minor_pir;
//static void __iomem *gpio_add_minor_led;

/* gpio 寄存器物理基地址 */
#define GPIO_BASE_PIR 0x41210000
//#define GPIO_BASE_LED 0x41200000

unsigned int GPIO_TRI_Offset = 0x4;
unsigned int GPIO_DATA_Offset = 0x0;

static volatile long pir[1] = {0};
void PIR(void);
int label_pir, state_pir;

/*打开函数,当打开驱动设备节点时,执行此函数,相当于初始化设备文件*/

static int pir_open(struct inode *inode,struct file *filp)
{
gpio_add_minor_pir = (unsigned int)ioremap(GPIO_BASE_PIR, 32);
//gpio_add_minor_led = (unsigned int)ioremap(GPIO_BASE_LED, 32);
return 0;
}

/* write 函数实现, 对应到 Linux 系统调用函数的 write 函数 */
static ssize_t pir_write(struct file *file_p, const char __user *buf, size_t len, loff_t *loff_t_p)
{
return 0;
}

/* release 函数实现, 对应到 Linux 系统调用函数的 close 函数 */
static int pir_release(struct inode *inode_p, struct file *file_p)
{
iounmap((unsigned int *)gpio_add_minor_pir);
//iounmap((unsigned int *)gpio_add_minor_led);
return 0;
}

/* 控 制 函 数 , 相 当 于 给 ult 发 送 控 制 命 令*/
static int pir_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
/*
if(arg == 0)
{
int state = 0;
if(ioread32(gpio_add_minor_pir))
{
mdelay(2);
if(ioread32(gpio_add_minor_pir))
state =1;
}
if(state)
{
iowrite32(4, gpio_add_minor_led);
mdelay(1000);
iowrite32(0, gpio_add_minor_led);
}
}
else
iowrite32(2, gpio_add_minor_led);
*/
return 0;
}

/* 读函数*/
static int pir_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
pir[0] = 0;
PIR();
pir[0] = state_pir;

printk(KERN_DEBUG"state_pir: %d\n", state_pir);

unsigned long err;
err = copy_to_user(buff, (const void *)pir, min(sizeof(pir), count));
return err ? -EFAULT : min(sizeof(pir), count);
}

/*struct file_operations 结构体 , 指 向 对 应 的 驱 动 操 作 函 数*/
struct file_operations pir_fops=
{
.owner = THIS_MODULE,
.open = pir_open,
.unlocked_ioctl = pir_ioctl,
.read = pir_read,
.write = pir_write,
.release = pir_release,
};
/*注册模块*/
static struct miscdevice misc =
{
.minor = MISC_DYNAMIC_MINOR,
.name = DEVICE_NAME,
.fops = &pir_fops,
};

/*led 驱 动 加 载函数*/
static int __init pir_init(void)
{
int ret;
ret = misc_register(&misc);//注册设备, 混合设备驱动类,此时不需要手动分配主从设备号
printk (DEVICE_NAME"\tinitialized\n");
return ret;
}
/*led 驱 动 卸 载 函 数*/
static void __exit pir_exit(void)
{
misc_deregister(&misc);//卸载设备
}

void PIR(void)
{
//label_pir = 0;
state_pir = 0;
if(ioread32(gpio_add_minor_pir))
{
mdelay(2);
if(ioread32(gpio_add_minor_pir))
state_pir = 1;
}
}

module_init(pir_init);
module_exit(pir_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("ccd");