通过字符设备节点一,知道了如何创建一个最简单的内核模块,这章展示了怎么去真正写一个字符设备节点,手动创建字符设备文件
在Linux内核中,使用cdev结构体描述一个字符设备,cdev结构体如下:
struct cdev{
struct kobject kobj; 内嵌的kobject对象
struct moudle *owener; 所属模块
const struct file_operations * ops; 操作方法结构体
struct list_head list; 与cdev对应的字符设备文件inode->i_devices的链表头
dev_t dev; 起始设备编号
unsigned int count; 连续注册的次设备编号个数
};
file_operations定义了字符设备驱动提供给虚拟文件系统的接口函数。Linux内核提供一些函数用于操作cdev结构体:
cdev_init()初始化cdev结构体,并将file_operations结构体放入到cdev->fops里
void cdev_init(struct cdev *cdev,const struct file_operations *fops)
*cdev:cdev结构体
*fops:函数操作集
cdev_add()添加cdev到内核,让内核能把它管理起来,将cdev结构体添加到系统中,并将dev(注册好的设备编号)
放入cdev->dev里,count(次设备编号个数)放入cdev->cont里
int cdev_add(struct cdev *cdev,dev_t dev,unsigned count)
*cdev:cdev结构体
*dev:注册好的设备编号
*count:连续注册的次设备编号个数
*当返回值小于0,表示添加失败
dev_tc成员定义了设备号,为32位,12位为主设备号,20位为次设备号。使用下列宏可以从dev_t中得到主次设备号
major = MAJOR(dev_t dev);
minor = MINOR(dev_t dev);
已知主次设备号,得到设备号
dev_t dev = MKDEV(major,minor);
设备号注册分为静态注册和动态注册,当已经已知设备号时,使用静态注册register_chrdev_region(),当如果设备号已经被其他驱动注册,在挂载时会报错。动态注册,自己设定一个次设备号,可以动态生成一个设备号,主设备号由MAJOR(dev_t dev)可得。
int register_chardev_regsion(dev_t from,unsigned count,const char *name);
*from:注册的制定其实设备编号
*count:连续注册的次设备编号个数
*name:字符设备编号
*returnvalue:
* 当返回值小于0,表示注册失败
int alloc_chardev_regsion(dev_t *dev,unsigned baseminor,unsigned count,const char *name);
*(*dev):存放起始设备编号的指针,当注册成功,*dev就会等于分配到的起始设备编号,
*baseminor:次设备号基地址,也就是起始次设备号
*count:连续注册的次设备编号个数
*name:字符设备编号
*returnvalue:
*当返回值小于0,表示注册失败
当取消挂载后,我们需要释放掉我们的设备号unregister_chardev_region()和cdev结构体cdev_del()
void unregister_chardev_regsion(dev_t from,unsigned count);
*from:注册的制定其实设备编号
*count:连续注册的次设备编号个数
void cdev_del(struct cdev *p)
*cdev:cdev结构体
#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#define CHARDEVICE_NUM_COUNT 1
dev_t devno = 0;//设备号
dev_t devno_major = 0;//主设备号
dev_t devno_minor = 0;//次设备号
struct cdev chardevice_cdev;
struct file_operations * chardevice_fops = {
.owner = THIS_MODULE,
};
static int __init chardevice_init(void)
{
int ret;
if(devno){
devno = MKDEV(devno_major,devno_minor);
ret = register_chrdev_region(devno,CHARDEVICE_NUM_COUNT,"chardevice01");
}eles{
ret = alloc_chrdev_region(&devno,devno_minor,CHARDEVICE_NUM_COUNT,"chardevice01");
}
if(ret<0){
printk(KERN_ALERT"fialed to register_chrdev\n");
goto failure_register_chardev;
}
cdev_init(&chardevice_cdev,&chardevice_fops);
ret = cdev_add(&chardevice_cdev,devno,CHARDEVICE_NUM_COUNT);
if(ret<0){
printk(KERN_ALERT"fialed to cdev_init\n");
goto failure_cdev_init;
}
return 0;
failure_cdev_init:
unregister_chardev_region(devno,CHARDEVICE_NUM_COUNT);
failure_register_chardev:
return ret;
}
static void __exit chardevice_exit(void)
{
cdev_del(&chardevice_cdev);
unregister_chardev_region(devno,CHARDEVICE_NUM_COUNT);
}
module_init(chardevice_init);
module_exit(chardevice_exit);
MODULE_LICENSE("GPL v2");
编译成功后,通过insmod/modprobe挂载成功后,手动创建字符设备文件
手动创建节点()
1)查看字符设备字节是否创建成功
cat /proc/devices
如果成功 注册成功,你会看到创建的字符设备结点的名字 和 主设备号
Character devices:
238 chardevices
...
然后使用mknod命令创建一个字符设备文件,b代表块设备,c代表字符设备
mknod Name (b|c) Major Minnor
eg mknod /dev/chardevice c 238 0
查看设备结点是否创建创建成功
ls -l /dev