5.Linux 标准字符设备自动创建设备文件

目录

1.杂项设备模型自动创建设备文件原理

1. 创建一个设备类

2. class_create,device_create 函数说明

1.class_create 函数

2.device_create 函数

3. class_device_create

3.自动创建设备文件示例代码片段:


杂项设备在安装模块时候会自动在/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]#

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值