字符设备驱动(三)——自动创建设备节点

自动创建设备节点


使用udevmdev来实现自动创建设备节点。

使用mknod手动创建设备节点不够灵活,如果是动态分配的设备号怎么办,难道每次加载驱动后去查看/proc/devices文件中查看它的主设备号,利用**udev(mdev)**来实现设备节点的自动创建,**udev(mdev)**存在于应用层。
包含头文件

#include<linux/device.h>

新建一个class结构体指针

static struct class *my_class;

初始化函数中调用class_create()函数创建一个类,参数分别为模块所有者和class结构name字段,在/sys/class/下体现。在/sys/class/下创建类目录。

my_class= class_create(THIS_MODULE, "my_class");

if(IS_ERR(my_class)) 
{
    printk("Err: failed increating class.\n");
	return -1;
}

调用device_create()函数创建设备节点,参数分别为所从属类,这个设备的父设备,没有就制定NULL,设备号,设备的私有数据,最后一组参数指定设备节点名,比如这里的名为hello。在驱动模块初始化函数中实现设备节点的自动创建设备节点
函数原型:

extern struct device *device_create(struct class *cls, structdevice *parent, dev_t devt, void *drvdata,const char *fmt, ...)

例:

device_create(my_class,NULL,dev_n,NULL,"%s","hello");

或者

device_create(my_class,NULL,dev_n,NULL,"hello");

设备卸载删除类和设备节点

device_destroy(my_class,dev_n);
class_destroy(my_class);

字符驱动框架

1. 现将头文件加进来

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>

###b 2. 写open,write,read等函数

static int seconds_drv_open(struct inode *inode,struct file *file)
{
	//printk("seconds_drv_open\n");
	/* 配置gpf4,5,6引脚为输出 */
	return 0;
}

static ssize_t seconds_drv_write(struct file *file,const char __user *buf,size_t count,loff_t *ppos)
{
	//printk("seconds_drv_write\n");
	return 0;
}

3. 写file_operations结构,然后将上面的open,write函数填充到相应函数指针处

static struct file_operations seconds_drv_fops={
.owner = THIS_MODULE, 
.open = seconds_drv_open,
.write = seconds_drv_write,
};

4. 在入口函数中注册驱动程序

int char01_dev_init(void)
{
    int result;
    //自动分配主次设备号
    result = alloc_chrdev_region(&devno, 0, 1, "char01");
    if (result != 0)
    {
        printk("alloc_chrdev_region failed!/n");
        goto ERR1;
    }
   
    //设备初始化 
    cdev_init(&dev, &fops);
    dev.owner   =   THIS_MODULE;
    dev.ops     =   &fops;
	//添加设备
    result = cdev_add(&dev, devno, 1);
    if (result != 0)
    {
        printk("cdev_add failed!/n");
        goto ERR2;
    }
 	//创建类
    pclass = class_create(THIS_MODULE, "char01");
    if (IS_ERR(pclass))
    {
        printk("class_create failed!/n");
        goto ERR3;
    }
    
    //创建设备节点
    device_create(pclass, NULL, devno, NULL, "char01");
 
    return 0;
ERR3:
    cdev_del(&dev);
ERR2:
    unregister_chrdev_region(devno, 1);
ERR1:
    return -1;
}
static int __init char01_init(void)
{
    printk("module char01 init!/n");
    return char01_dev_init();
}

5. 在出口函数中卸载驱动

static void __exit char01_exit(void)
{
    printk("module char01 exit!/n");
    class_destroy(pclass);
    device_destroy(pclass,devno);
    cdev_del(&dev);
    unregister_chrdev_region(devno, 1);
}

6. 修饰入口函数和出口函数

module_init(seconds_drv_init);
module_exit(seconds_drv_exit);
MODULE_LICENSE("GPL");

7. 内核传参数,主次设备号

//内核模块传参数
//让内核传进来空闲的主、次设备号
module_param(major_num, int, S_IRUSR);

module_param(minor_num, int, S_IRUSR);
## 总结
使用`class_create()`和`device_create()`函数可以自动创建节点。
```c
    class_create                 //创建class ,在/sys/class下创建类
    class_destroy               //销毁class 
    device_create     //创建device ,在/sys/class的类下面创建设备节点
    device_destroy   //销毁device

示例程序


chrdev.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/kdev_t.h>
#include <linux/fs.h>

int char01_open(struct inode *inode, struct file *filp)
{
    printk("char01 open!/n");
    return 0;
}
 
int char01_release(struct inode *inode, struct file *filp)
{
    printk("char01 release!/n");
    return 0;
}
 
ssize_t char01_read(struct file *filp, char *buf,
        size_t count, loff_t *fpos)
{
    printk("char01 read!/n");
    return 0;
}
 
ssize_t char01_write(struct file *filp, const char *buf,
        size_t count, loff_t *fpos)
{
    printk("char01 write!/n");
    return 0;
}
 
int char01_ioctl(struct inode *inode, struct file *filp,
        unsigned int cmd, unsigned long param)
{
    printk("char01 ioctl!/n");
    printk("cmd:%d param:%ld/n", cmd, param);
    return 0;
}

struct file_operations fops = 
{
    .owner      =   THIS_MODULE,
    .open       =   char01_open,
    .release    =   char01_release,
    .read       =   char01_read,
    .write      =   char01_write,
    .ioctl      =   char01_ioctl
};
dev_t   devno;
struct class *pclass;
struct cdev dev;
 
int char01_dev_init(void)
{
    int result;
    result = alloc_chrdev_region(&devno, 0, 1, "char01");
    if (result != 0)
    {
        printk("alloc_chrdev_region failed!/n");
        goto ERR1;
    }
    
    cdev_init(&dev, &fops);
    dev.owner   =   THIS_MODULE;
    dev.ops     =   &fops;
    result = cdev_add(&dev, devno, 1);
    if (result != 0)
    {
        printk("cdev_add failed!/n");
        goto ERR2;
    }
 
    pclass = class_create(THIS_MODULE, "char01");
    if (IS_ERR(pclass))
    {
        printk("class_create failed!/n");
        goto ERR3;
    }
    
    device_create(pclass, NULL, devno, NULL, "char01");
 
    return 0;
ERR3:
    cdev_del(&dev);
ERR2:
    unregister_chrdev_region(devno, 1);
ERR1:
    return -1;
}
static int __init char01_init(void)
{
    printk("module char01 init!/n");
    return char01_dev_init();
}
 
static void __exit char01_exit(void)
{
    printk("module char01 exit!/n");
    class_destroy(pclass);
    device_destroy(pclass,devno);
    cdev_del(&dev);
    unregister_chrdev_region(devno, 1);
}

MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("Yao.GUET");
module_init(char01_init);
module_exit(char01_exit);

chrdev_test.c

#include <stdio.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/stat.h>
/////////////////////////////////////////////////
int main(int argc, char *argv)
{
    int fd;
    fd = open("/dev/char01", O_RDWR);
    if (fd<0)
    {
        printf("open /dev/char01 failed!/n");
        printf("%s/n", strerror(errno));
        return -1;
    }
    printf("open /dev/char01 ok!/n");
    ioctl(fd, 0);
    close(fd);
}

猜你喜欢

转载自blog.csdn.net/qq_44715649/article/details/88954636