title: 指定子设备号创建字符设备
tags: linux
date: 2018/12/28 19:57:24
toc: true
指定子设备号字符设备
流程
内核中设备号分为主设备号和次设备号,以前注册字符设备驱动的时候,直接占用了主设备号包含了255个子设备号,也就是内核最多支持255个设备驱动(如果主设备号占据8位)
在Linux2.6中内核可以指定主设备号和对应的子设备号给一个fileoperation
,如下图所示
流程如下:
-
确认需要得到几个子设备号使用
-
如果指定主设备号使用
register_chrdev_region
,当返回值小于0,表示注册失败dev_t devid; devid = MKDEV(major, 0); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */ register_chrdev_region(devid, HELLO_CNT, "hello");
-
如果需要系统分配主设备号,
alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello");
当返回值小于0,表示注册失败/* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */ alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello"); major = MAJOR(devid);
-
这里使用
cdev
管理驱动,并将file_operations
结构体放入cdev-> ops
里cdev_init(&hello_cdev, &hello_fops); cdev_add(&hello_cdev, devid, HELLO_CNT); struct cdev { struct kobject kobj; // 内嵌的kobject对象 struct module *owner; //所属模块 const struct file_operations *ops; //操作方法结构体 struct list_head list; //与 cdev 对应的字符设备文件inode->i_devices 的链表头 dev_t dev; //起始设备编号,可以通过MAJOR(),MINOR()来提取主次设备号 unsigned int count; //连续注册的次设备号个数 };
-
创建设备文件是与以前一样的,使用
class_create
创建类和class_device_create
创建设备文件 -
卸载
/*将系统中的cdev结构体删除掉*/ void cdev_del(struct cdev *p); /*注销字符设备*/ void unregister_chrdev_region(dev_t from, unsigned count);
实例程序
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/poll.h>
#include <linux/cdev.h>
/* 1. 确定主设备号 */
static int major;
static int hello_open(struct inode *inode, struct file *file)
{
printk("hello_open
");
return 0;
}
static int hello2_open(struct inode *inode, struct file *file)
{
printk("hello2_open
");
return 0;
}
/* 2. 构造file_operations */
static struct file_operations hello_fops = {
.owner = THIS_MODULE,
.open = hello_open,
};
static struct file_operations hello2_fops = {
.owner = THIS_MODULE,
.open = hello2_open,
};
#define HELLO_CNT 2
static struct cdev hello_cdev;
static struct cdev hello2_cdev;
static struct class *cls;
static int hello_init(void)
{
dev_t devid;
/* 3. 告诉内核 */
#if 0
major = register_chrdev(0, "hello", &hello_fops); /* (major, 0), (major, 1), ..., (major, 255)都对应hello_fops */
#else
if (major) {
devid = MKDEV(major, 0);
register_chrdev_region(devid, HELLO_CNT, "hello"); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
} else {
alloc_chrdev_region(&devid, 0, HELLO_CNT, "hello"); /* (major,0~1) 对应 hello_fops, (major, 2~255)都不对应hello_fops */
major = MAJOR(devid);
}
cdev_init(&hello_cdev, &hello_fops);
cdev_add(&hello_cdev, devid, HELLO_CNT);
devid = MKDEV(major, 2);
register_chrdev_region(devid, 1, "hello2");
cdev_init(&hello2_cdev, &hello2_fops);
cdev_add(&hello2_cdev, devid, 1);
#endif
cls = class_create(THIS_MODULE, "hello");
class_device_create(cls, NULL, MKDEV(major, 0), NULL, "hello0"); /* /dev/hello0 */
class_device_create(cls, NULL, MKDEV(major, 1), NULL, "hello1"); /* /dev/hello1 */
class_device_create(cls, NULL, MKDEV(major, 2), NULL, "hello2"); /* /dev/hello2 */
class_device_create(cls, NULL, MKDEV(major, 3), NULL, "hello3"); /* /dev/hello3 */
return 0;
}
static void hello_exit(void)
{
class_device_destroy(cls, MKDEV(major, 0));
class_device_destroy(cls, MKDEV(major, 1));
class_device_destroy(cls, MKDEV(major, 2));
class_device_destroy(cls, MKDEV(major, 3));
class_destroy(cls);
cdev_del(&hello_cdev);
unregister_chrdev_region(MKDEV(major, 0), HELLO_CNT);
cdev_del(&hello2_cdev);
unregister_chrdev_region(MKDEV(major, 2), 1);
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
测试
查看到只是用了同一个主设备号
cat /proc/devices
# cat /proc/devices
Character devices:
252 hello #同一个主设备号
252 hello2 #同一个主设备号
查看设备文件
# ls /dev/hello* -l
crw-rw---- 1 0 0 252, 0 Jan 1 04:18 /dev/hello0
crw-rw---- 1 0 0 252, 1 Jan 1 04:18 /dev/hello1
crw-rw---- 1 0 0 252, 2 Jan 1 04:18 /dev/hello2
crw-rw---- 1 0 0 252, 3 Jan 1 04:18 /dev/hello3
驱动fileopration | 次设备号 |
---|---|
hello_fops | 0,1 |
hello2_fops | 2 |
也就是对应设备文件的驱动如下
设备文件 | |
---|---|
/dev/hello0 | hello_fops |
/dev/hello1 | hello_fops |
/dev/hello2 | hello2_fops |
/dev/hello3 | 没有 |
使用测试文件去打开文件如下结果
# ./test /dev/hello0
hello_open #驱动1打开的
can open /dev/hello0
# ./test /dev/hello1
hello_open #驱动1打开的
can open /dev/hello1
# ./test /dev/hello2
hello2_open #驱动2打开的
can open /dev/hello2
# ./test /dev/hello3
can't open /dev/hello3 #无法打开
测试程序如下
//arm-linux-gcc -o test test.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
/*
* test /dev/hello0
*/
void print_usage(char *file)
{
printf("%s <dev>
", file);
}
int main(int argc, char **argv)
{
int fd;
if (argc != 2)
{
print_usage(argv[0]);
return 0;
}
fd = open(argv[1], O_RDWR);
if (fd < 0)
printf("can't open %s
", argv[1]);
else
printf("can open %s
", argv[1]);
return 0;
}