目录
2. class_create,device_create 函数说明
杂项设备在安装模块时候会自动在/dev 目录下创建设备文件,可以分析杂项设备注册函数的源码,仿照它实现早期经典模型和 Linux 2.6 模型实现自动创建设备文件的功能。
1.杂项设备模型自动创建设备文件原理
1. 创建一个设备类
int misc_register(struct miscdevice * misc)
{
……
上图这是linux3.5的
上图这是linux2.6.22的
……
}
从函数名可以大概猜出,就是该函数实现/dev/目录下文件自动功能,接下来就要弄清楚他的参数,就可以仿照。
第一个参数: misc_class 分析
在同一个文件中定义: static struct class *misc_class;
搜索哪里给这个变量初始化了。
上图是linux3.5
上图是linux2.6.22
结论:
1. 要定义一个 struct class 指针
2. 使用 class_create(THIS_MODULE, "类名");创建一个类,保存在上一步定义的 struct class 指针变量中
第二个参数分析:
struct device *parent , 杂项设备注册函数中实际参数是 misc->parent,实际就是用户定义的杂项设备核心结构中的 parent 成员。如果没有,则为 NULL.
第三个参数分析:
dev_t devt
杂项设备注册函数中实际参数是 dev, dev 值:
dev = MKDEV(MISC_MAJOR, misc->minor); //把主设备号的次设备号合成设备号
结论:
需要把设备号传递进去,因为生成设备文件需要使用到这个信息。
第四个参数分析
void *drvdata
这个是驱动数据,传递给设备回调使用,一般情况下没有,为 NULL.
第 5 个参数分析:
const char *fmt,
/dev/目录下设备名
... 表示可变参数 ,你 printf 语句一样的使用方式,可以使用%d,%s 这种方式格式一个字符串。
2. class_create,device_create 函数说明
1.class_create 函数
Device.h include\Linux
owner:类的所有者, 固定是 THIS_MODULE 。
name:类名,随便,能有含义最好, 不是 /dev/ 下设备的名字。
这个名字决定了/sys/class/name 。
当创建一个 class 后会在 /sys/class/ 目录生成一个名为 name 的目录。
对于这个宏,也要做错误检测:
misc_class = class_create(THIS_MODULE, "misc");//这个函数返回的是指针
err = PTR_ERR(misc_class); //把执行结果转换成整数错误码,
if (IS_ERR(misc_class)) //判断 class_create 是否执行成功,真表示创建失败
goto fail_remove;
和这个函数对应的还有一个相反的函数:用于销毁一个 class.
void class_destroy(struct class *cls);
以上是linux3.5的
和这个函数对应的还有一个相反的函数:用于销毁一个 class.
void class_destroy(struct class *cls);
以上是linux2.6.22
2.device_create 函数
Device.h include\Linux
struct device *device_create(
struct class *class, /*已经创建好的类指针*/
struct device *parent, /* 指向父设备的指针, 如果没有可以为 NULL*/
dev_t devt, /* 32 位的设备号 */
void *drvdata, /* 驱动数据,也可以是 NULL,一般也为 NULL*/
const char *fmt, ...) /*可变参数, 用来生成/dev/目录下的设备文件名*/
返回值:
成功: 返回 有效 struct device * 指针;
失败:返回错误指针, 可以使用 ERR_PTR()宏转换成错误代码。
返回的指针是否合法是使用 IS_ERR( )宏判断的。
错误检测:
misc->this_device = device_create(misc_class, misc->parent, dev,misc, "%s", misc->name);
if (IS_ERR(misc->this_device)) {
……
err = PTR_ERR(misc->this_device);
goto out;
}
这个函数相反的一个函数是:
void device_destroy(struct class *class, dev_t devt)
功能是删除一个设备文件,会 删除/dev/目录下指定设备号的文件。
以上是linux3.5
3. class_device_create
这个函数相反的一个函数是:
void class_device_unregister(struct class_device *class_dev)
功能是删除一个设备文件,会 删除/dev/目录下指定设备号的文件
以上是linux2.6.22
3.自动创建设备文件示例代码片段:
根据前面所讲内容,修改以前写的 Linux2.6 设备驱动代码,让它具有自动创建设备文件的功能。驱动代码(模块加载、卸载函数):(演示了创建两个设备文件)
static struct class * myclass; //定义一个类指针
static struct device * this_device;
static int __init linux26_device_init(void)
{
int ret = -1;
//1. 使用 cdev_alloc(void) 分配 cdev 结构空间
pcdev = cdev_alloc();
if(pcdev == NULL) {
printk(KERN_EMERG" cdev_alloc error\n");
ret = -ENOMEM; /* 分配失败一般是由于内存不足导致的 */
goto err_cdev_alloc;
}
//2. 申请设备号:动态或静态
ret = alloc_chrdev_region(&dev_no, 0 , 2, MY_DEVICE_NAME);
if(ret < 0 ) {
printk(KERN_EMERG"alloc_chrdev_region error\n");
goto err_alloc_chrdev_region;
}
//3. 初始化 cdev 结构
cdev_init(pcdev, &mymisc_fops); /* 字符设备的文件操作方法*/
//4. 注册已经初始化好的 cdev 结构
ret = cdev_add(pcdev, dev_no, 2) ;
if(ret < 0 ) {
printk(KERN_EMERG"alloc_chrdev_region error\n");
goto err_cdev_add;
}
major = MAJOR(dev_no); /* 从设备号中提取主设备号 */
printk(KERN_EMERG"cdev_add ok\n");
printk(KERN_EMERG"major:%d \n", major); /*输出主设备号*/
//创建一个类
myclass = class_create(THIS_MODULE, "myclass");
ret = PTR_ERR(myclass); //把执行结果转换成整数错误码,
if (IS_ERR(myclass)) { //判断 class_create 是否执行成功,真表示创建失败
goto err_class_create;
}
//创建第一个设备
this_device = device_create(myclass, NULL, dev_no, NULL, "mydev%d", 0); //设备名:mydev0
if (IS_ERR(this_device)) {
ret = PTR_ERR(this_device); //把指针转换成错误码
goto err_device_create;
}
//创建第二个设备
this_device = device_create(myclass, NULL, dev_no+1, NULL, "mydev%d", 1); //设备名:mydev0
if (IS_ERR(this_device)) {
ret = PTR_ERR(this_device); //把指针转换成错误码
goto err_device_create2;
}
return 0; //注意,这一条语句 不要少写了。如果少写了,驱动将异常
err_device_create2:
device_destroy(myclass, dev_no+1);
err_device_create:
class_destroy(myclass); /* 销毁已经创建的类 */
err_class_create:
cdev_del(pcdev); // 注销 cdev 结构
err_cdev_add:
//释放前面成功的资源
unregister_chrdev_region(dev_no, 2); /* 3.释放前面申请的调和号*/
err_alloc_chrdev_region:
//释放前面成功的资源
kfree(pcdev); /* 3.释放 cdev 结构空间 */
err_cdev_alloc:
return ret;
}
static void __exit linux26_device_exit(void)
{
//删除设备文件
device_destroy(myclass, dev_no+1); //第一个设备 文件
device_destroy(myclass, dev_no); //第二个设备 文件
// 销毁已经创建的类
class_destroy(myclass);
//1. 注销 cdev 结构
cdev_del(pcdev);
//2. 释放设备号
unregister_chrdev_region(dev_no, /* 起始设备号(主、次)*/
2); /* 连续的次设备号数量*/
//3. 释放 cdev 结构空间
kfree(pcdev);
printk(KERN_EMERG"cdev_del ok\n");
}
测试程序
同前面的程序,复制了一个 app2.c ,用 app 和 app2 来测试生成 的两个设备 文件 。
测试过程:
[root@ChenZhiFa home]#
[root@ChenZhiFa home]# ls /dev/my* -l
ls: /dev/my*: No such file or directory
[root@ChenZhiFa home]#
[root@ChenZhiFa home]# insmod linux26_module_auto.ko
[ 346.780000] cdev_add ok
[ 346.780000] major:250
[root@ChenZhiFa home]# ls /dev/my*
/dev/mydev0 /dev/mydev1
[root@ChenZhiFa home]# ./app
/dev/mydev0 open
[ 359.530000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 359.530000] line:16, xxx_open is call
[ 359.530000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 359.530000] line:24, xxx_read is call
[ 359.535000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 359.535000] line:31, xxx_write is call
[ 359.550000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 359.550000] line:38, xxx_llseek is call
[ 359.565000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 359.565000] line:55, xxx_unlocked_ioctl is call
[ 359.575000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 359.575000] line:46, xxx_release is call
fd=3
[root@ChenZhiFa home]# ./app2
/dev/mydev1 open
[ 364.325000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 364.325000] line:16, xxx_open is call
[ 364.325000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 364.325000] line:24, xxx_read is call
[ 364.330000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 364.330000] line:31, xxx_write is call
[ 364.345000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 364.345000] line:38, xxx_llseek is call
[ 364.355000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 364.355000] line:55, xxx_unlocked_ioctl is call
[ 364.370000]
file:/mnt/hgfs/share/20160909device_drvier/06_linux26_device_auto_mknode/linux26_module_auto.c
[ 364.370000] line:46, xxx_release is call
fd=3
[root@ChenZhiFa home]#
补充说明:
上面的代码,在安装后,会在 /sys/class/ 目录生成 myclass 目录,这个目录中生成 和/dev/目录下设备名同名的文件夹,其中每个文件夹中存放有一个 dev 文件,这个文件中内容是: 主设备号:次设备号
[root@ChenZhiFa home]# ls /sys/class/
android_usb gpio leds ppp sound video4linux
bdi graphics mem rfkill spi_master vtconsole
block hwmon misc rtc s witch watchdog
bluetooth i2c-adapter mmc_host scsi_device thermal xt_idletimer
devfreq i2c-dev myclass scsi_disk tty
dma input net scsi_generic udc
firmware lcd power_supply scsi_host vc
[root@ChenZhiFa home]# cat /sys/class/myclass/mydev0/dev
250:0
[root@ChenZhiFa home]# cat /sys/class/myclass/mydev1/dev
250:1
[root@ChenZhiFa home]#