杂项设备驱动
在 linux 中,将无法归类的设备定义为杂项设备。
相对于字符设备来说,杂项设备的主设备号固定为 10,而字符设备不管是动态分配还是静态分配设备号,都会消耗一个主设备号,比较浪费主设备号。
杂项设备会自己调用 class_create()
和 device_create()
来自动创建设备节点。所以可以将杂项设备看成是字符设备的一种。但是比平时写的字符设备降低了难度并节约了主设备号。
杂项设备的次设备号 minor 一般使用宏 MISC_DYNAMIC_MINOR
,表示自动分配次设备号。杂项设备主要依赖次设备号来管理不同的杂项设备。
杂项设备的头文件为 #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;
};
API
int misc_register(struct miscdevice *misc);
void misc_deregister(struct miscdevice *misc);
驱动程序
#include <linux/device/class.h>
#include <linux/export.h>
#include <linux/uaccess.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/cdev.h>
#include <linux/miscdevice.h>
#define CHRDEVBASE_NAME "misc_test1" /* 设备名 */
static char write_buf[100];
static char read_buf[100];
static char *string_test = "kernel data this tyustli test";
static int miscdev_open(struct inode *inode, struct file *file)
{
printk("k: miscdev open\r\n");
return 0;
}
static ssize_t miscdev_read(struct file *file, char __user *buf, size_t count,
loff_t *ppos)
{
unsigned long ret = 0;
printk("k: miscdev read\r\n");
memcpy(read_buf, string_test, strlen(string_test));
ret = copy_to_user(buf, read_buf, count);
if (ret == 0) {
printk("k: read data success\r\n");
} else {
printk("k: read data failed ret = %ld\r\n", ret);
}
return ret;
}
static ssize_t miscdev_write(struct file *file, const char __user *buf,
size_t count, loff_t *ppos)
{
unsigned long ret = 0;
printk("k: miscdev write\r\n");
ret = copy_from_user(write_buf, buf, count);
if (ret == 0) {
printk("k: write data success write data is: %s\r\n", write_buf);
} else {
printk("k: write data failed ret = %ld\r\n", ret);
}
return count;
}
static int miscdev_release(struct inode *inode, struct file *file)
{
printk("k: miscdev release\r\n");
return 0;
}
static struct file_operations miscdev_fops = {
.owner = THIS_MODULE,
.open = miscdev_open,
.read = miscdev_read,
.write = miscdev_write,
.release = miscdev_release,
};
static struct miscdevice miscdevice_dev = {
.minor = MISC_DYNAMIC_MINOR,
.name = CHRDEVBASE_NAME,
.fops = &miscdev_fops,
};
static int __init miscdev_init(void)
{
int err = 0;
err = misc_register(&miscdevice_dev);
if (err < 0) {
printk("k: misc register err = %d\r\n", err);
goto err_misc;
}
printk("k: misc module init!\r\n");
err_misc:
return err;
}
static void __exit miscdev_exit(void)
{
misc_deregister(&miscdevice_dev);
printk("k: misc module exit!\r\n");
}
module_init(miscdev_init);
module_exit(miscdev_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("tyustli");
MODULE_INFO(intree, "Y"); /* loading out-of-tree module taints kernel */
应用程序
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdata[] = { "user data!" };
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
char readbuf[100], writebuf[100];
if (argc != 3) {
printf("u: error Usage!\r\n");
return -1;
}
filename = argv[1];
/* 打开驱动文件 */
fd = open(filename, O_RDWR);
if (fd < 0) {
printf("u: can't open file %s\r\n", filename);
return -1;
}
/* 从驱动文件读取数据 */
if (atoi(argv[2]) == 1) {
retvalue = read(fd, readbuf, 50);
if (retvalue < 0) {
printf("u: read file %s failed!\r\n", filename);
} else {
/* 读取成功,打印出读取成功的数据 */
printf("u: read data:%s\r\n", readbuf);
}
}
/* 向设备驱动写数据 */
if (atoi(argv[2]) == 2) {
memcpy(writebuf, usrdata, sizeof(usrdata));
retvalue = write(fd, writebuf, 50);
if (retvalue < 0) {
printf("u: write file %s failed!\r\n", filename);
}
}
/* 关闭设备 */
retvalue = close(fd);
if (retvalue < 0) {
printf("u: can't close file %s\r\n", filename);
return -1;
}
return 0;
}
模块使用
查看字符设备 cat /proc/devices
~ # cat /proc/devices
Character devices:
1 mem
2 pty
3 ttyp
4 /dev/vc/0
4 tty
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
29 fb
90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 ttyAMA
226 drm
249 rpmb
250 usbmon
251 ptp
252 pps
253 rtc
254 gpiochip
Block devices:
8 sd
31 mtdblock
65 sd
66 sd
67 sd
68 sd
69 sd
70 sd
71 sd
128 sd
129 sd
130 sd
131 sd
132 sd
133 sd
134 sd
135 sd
179 mmc
254 virtblk
259 blkext
~ #
查看设备节点
ls /dev
模块安装
modprobe my_module
模块使用
lib/modules/6.5.7+/my_app /dev/misc_test1 1
lib/modules/6.5.7+/my_app /dev/misc_test1 2
模块运行日志
~ # /lib/modules/6.5.7+/my_app /dev/misc_test1 1
k: miscdev open
k: miscdev read
k: read data success
u: read data:kernel data this tyustli test
k: miscdev release
~ # /lib/modules/6.5.7+/my_app /dev/misc_test1 2
k: miscdev open
k: miscdev write
k: write data success write data is: user data!
k: miscdev release