嵌入式之Linux驱动(八)

姓名:郑煜烁  学号:19029100010   学院:电子工程学院

转自:https://blog.csdn.net/u012142460/article/details/78988741

【嵌牛导读】介绍linux系统中如何动态建立设备号和设备节点

【嵌牛鼻子】动态建立设备号

【嵌牛提问】动态建立设备号和设备节点的基本步骤是什么

【嵌牛正文】

在Linux驱动(三)字符设备驱动框架中,我们简要介绍了如何编写一个简单的驱动框架,并总结了步骤

1、生成设备号

2、向内核注册该设备号

3、初始化设备对象,完成操作方法集

4、向内核注册该设备对象

5、生成设备文件,供用户层调用。

我们之前的步骤都是静态的,

1、静态生成设备号,这样必须确保要申请的设备号是没有被占用的。我们可以让内核帮我们动态申请设备号,可以确保生成的设备号是没有被占用的。

2、设备对象空间也可以由内核来分配。

3、设备文件我们之前通过mknod来手动创建,我们现在在驱动程序中直接完成设备文件的创建。

我们来看看相关的函数

1、自动生成设备号并向内核注册alloc_chrdev_region

函数原型:int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,

                                  const char *name)

参数:

          dev: 要生成的设备号

          baseminor:次设备号

          count:生成的设备号数量

          name:设备号名称

返回值:成功返回0,错误返回错误码

举例:生成10个设备号。

dev_t devno;

int minor = 0;

int count = 10;

alloc_chrdev_region(&devno,minor,count, "xxx");

2、分配设备对象空间cdev_alloc

函数原型:struct cdev *cdev_alloc(void)

参数:        无

返回值:成功返回创建的设备对象地址,错误返回NULL

3、自动生成设备节点

      (1)device_create创建设备节点

      函数原型:struct device *device_create(struct class *class, struct device *parent,

                                                  dev_t devt, void *drvdata, const char *fmt, ...)

      函数功能:生成一个设备节点

      参数:  class 设备类(看下一个函数)

                    parent:父类,没有的话就用NULL

                    devt:设备号

                    drvdata:私有数据

                    fmt:设备节点名称

                    .....:类似printf的格式

        返回值:成功返回一个设备节点地址

        创建会对应销毁设备节点函数

        (2)销毁一个设备节点

          函数原型:void device_destroy(struct class *class, dev_t devt)

          函数功能:销毁一个设备节点

          参数:  classs:设备类  devt:设备号

      (3)创建设备类

      函数原型:class_create(owner, name)

      函数功能:创建一个设备类

      参数:

                  owner  直接赋值为THIS_MODULE

                  name    类名称

        返回值:成功返回一个类的地址

      创建设备类对应是销毁一个设备类

        (4)销毁设备类

        函数原型:void class_destroy(struct class *cls)

        函数功能:销毁一个设备类

        参数:cls 设备类

          创建设备类和创建设备的返回值得错误判断需要用到专门的函数IS_ERR

          IS_ERR的原型 static inline long __must_check IS_ERR(const void *ptr) 参数是一个地址,将创建设备类或创建设备节点的返回值作为IS_ERR参数,为真表示错误。

#include

#include

#include

#include

#include

#include

#include

MODULE_LICENSE("GPL");

dev_t devno;

int major = 0;

int minor = 0;

int count = 1;

struct cdev *pdev;

struct class * pclass;

struct device * pdevice;

int demo_open(struct inode * inodep, struct file * filep)

{

printk("%s,%d\n", __func__, __LINE__);

return 0;

}

int demo_release(struct inode *inodep, struct file *filep)

{

printk("%s,%d\n", __func__, __LINE__);

return 0;

}

struct file_operations  fops = {

.owner =THIS_MODULE,

.open = demo_open,

.release = demo_release,

};

static int __init demo_init(void)

{

int ret = 0;

printk("%s,%d\n", __func__, __LINE__);

ret = alloc_chrdev_region(&devno,minor,count, "xxx");

if(ret)

{

printk("Failed to alloc_chrdev_region.\n");

return ret;

}

printk("devno:%d , major:%d  minor:%d\n", devno, MAJOR(devno), MINOR(devno));

pdev = cdev_alloc();

if(pdev == NULL)

{

printk("Failed to cdev_alloc.\n");

goto err1;

}

cdev_init(pdev, &fops);

ret = cdev_add(pdev, devno, count);

if(ret < 0)

{

    printk("Failed to cdev_add.");

goto err2;

}

pclass = class_create(THIS_MODULE, "myclass");

if(IS_ERR(pclass))

{

printk("Failed to class_create.\n");

ret = PTR_ERR(pclass);

goto err3;

}

pdevice = device_create(pclass, NULL, devno, NULL, "hello");

if(IS_ERR(pdevice))

{

printk("Failed to device_create.\n");

ret = PTR_ERR(pdevice);

goto err4;

}

return 0;

err4:

class_destroy(pclass);

err3:

cdev_del(pdev);

err2:

kfree(pdev);

err1:

unregister_chrdev_region(devno, count);

return ret;

}

static void __exit demo_exit(void)

{

printk("%s,%d\n", __func__, __LINE__);

device_destroy(pclass, devno);

class_destroy(pclass);

cdev_del(pdev);

kfree(pdev);

unregister_chrdev_region(devno, count);

}

module_init(demo_init);

module_exit(demo_exit);

利用之前的makefile,make之后生成demo.ko文件。insmod demo.ko后就能生成设备

来源 CSDN

我们还可以查看一下/sys/class下

来源 CSDN

在class下生成了“myclass”类名,进入myclass文件夹下,在myclass下也有hello这个设备节点

来源 CSDN

这里我们来介绍一下/sys这个文件夹

        Linux2.6以后的内核所支持的sysfs文件系统被映射到此目录上。linux设备驱动模型中的总线、设备、驱动都可以在sysfs文件系统中找到对应的节点。

        sysfs把连接在系统上的设备和总线组织成为一个分级的文件。sysfs的一个目的就是展示设备驱动模型中各组件的层次关系。目录下包括如下部分

来源 CSDN

blcok:所有的块设备

bus:所有的总线

class:系统的设备类

devices:设备

Linux2.6以后的设备模型是总线、设备、驱动模式。在sys目录下也有体现

来源 CSDN

类中的设备和总线下的设备实际上都是设备文件夹具体文件的链接

下图是平台总线下的设备,都是链接于devices/platform下

来源 CSDN
来源 CSDN

上图是在我们创建的myclass类中的设备文件,链接于devices/virtual/myclass目录下

我们可以看到在/dev中有很多tty设备,我们也可以一下子生成多个设备,在上个程序的基础上稍加修改即可。

#include

#include

#include

#include

#include

#include

#include

MODULE_LICENSE("GPL");

dev_t devno;

int major = 0;

int minor = 0;

int count = 10;

struct cdev *pdev;

struct class * pclass;

struct device * pdevice;

int demo_open(struct inode * inodep, struct file * filep)

{

printk("%s,%d\n", __func__, __LINE__);

return 0;

}

int demo_release(struct inode *inodep, struct file *filep)

{

printk("%s,%d\n", __func__, __LINE__);

return 0;

}

struct file_operations  fops = {

.owner =THIS_MODULE,

.open = demo_open,

.release = demo_release,

};

static int __init demo_init(void)

{

int ret = 0;

int i = 0;

int j = 0;

printk("%s,%d\n", __func__, __LINE__);

ret = alloc_chrdev_region(&devno,minor,count, "xxx");

if(ret)

{

printk("Failed to alloc_chrdev_region.\n");

return ret;

}

printk("devno:%d , major:%d  minor:%d\n", devno, MAJOR(devno), MINOR(devno));

pdev = cdev_alloc();

if(pdev == NULL)

{

printk("Failed to cdev_alloc.\n");

goto err1;

}

cdev_init(pdev, &fops);

ret = cdev_add(pdev, devno, count);

if(ret < 0)

{

    printk("Failed to cdev_add.");

goto err2;

}

pclass = class_create(THIS_MODULE, "myclass");

if(IS_ERR(pclass))

{

printk("Failed to class_create.\n");

ret = PTR_ERR(pclass);

goto err3;

}

for(i = 0; i < count; i++)

{

pdevice = device_create(pclass, NULL, MKDEV(MAJOR(devno), MINOR(devno)+i), NULL, "hello%d", i);

if(IS_ERR(pdevice))

{

printk("Failed to device_create.\n");

ret = PTR_ERR(pdevice);

goto err4;

}

}

return 0;

err4:

for(j = 0; j < i; j++)

{

device_destroy(pclass, MKDEV(MAJOR(devno), MINOR(devno)+j));

}

class_destroy(pclass);

err3:

cdev_del(pdev);

err2:

kfree(pdev);

err1:

unregister_chrdev_region(devno, count);

return ret;

}

static void __exit demo_exit(void)

{

int i = 0;

printk("%s,%d\n", __func__, __LINE__);

for(i = 0; i < count; i++)

{

device_destroy(pclass, MKDEV(MAJOR(devno), MINOR(devno)+i));

}

class_destroy(pclass);

cdev_del(pdev);

kfree(pdev);

unregister_chrdev_region(devno, count);

}

module_init(demo_init);

module_exit(demo_exit);

把count值修改为10,我们要创建10个设备,创建设备时,连续创建10次,注意次设备号要不同,并起不同的名字。另外注意出错处理时,要不之前创建的设备都要销毁掉。加载之后我们看一下生成了几个设备

来源 CSDN

我们看到生成了hello0--hello9 十个设备。这十个设备是在一个类“myclass”下,我们可以查看/sys/class/myclass下

来源 CSDN

————————————————

版权声明:本文为CSDN博主「念念有余」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/u012142460/article/details/78988741

你可能感兴趣的:(嵌入式之Linux驱动(八))