25 mdev与驱动源码里创建设备文件的实现

前面设备驱动备加载后,都需要用命令”mknod”来创建出设备文件.
其实内核里有接口在驱动源码里实现创建出设备文件.

首先busybox作的嵌入式文件系统里有提供”mdev”命令.(有些系统移植”udev”,工作方式有区别,但功能差不多)

“mdev –help”命令执行输出的信息:

mdev -s is to be run during boot to scan /sys and populate /dev.

Bare mdev is a kernel hotplug helper. To activate it:
        echo /sbin/mdev >/proc/sys/kernel/hotplug

It uses /etc/mdev.conf with lines
        [-][ENV=regex;]...DEVNAME UID:GID PERM [>|=PATH]|[!] [@|$|*PROG]
...

可通过以上信息得知, “mdev -s”是用于在系统启动时扫描”/sys”目录下的设备信息,在”/dev”目录下产生相应的设备文件.所以在系统的启动脚本里”/etc/init.d/rcS”里加了”mdev -s”语句

“echo /sbin/mdev >/proc/sys/kernel/hotplug” 这语句同样的也加在系统启动脚本里加上了.
用于当系统的设备或设备驱动发生变化(热插拔事件)时,自动调用mdev程序来产生或移除相应的设备文件.

“/etc/mdev.conf”还可以用于指定什么设备文件产生后,自动执行什么脚本或程序。
如u盘接上后,设备文件为/dev/sda1,想执行脚本”/bin/mysh.sh”,则可以在mdev.conf里设置:
“sda1 0:0 0600 =sda1 */bin/mysh.sh”


在内核源码里创建/移除设备,产生热插拔事件的函数有:

#include <linux/device.h>

struct class *class_create(owner, name); //在"/sys/class/"目录下创建出名为name的子目录
void class_destroy(struct class *cls);  //移除目录

struct device *device_create(struct class *class, struct device *parent,
                  dev_t devt, void *drvdata, const char *fmt, ...); //在class目录下创建出设备信息和产生热插拔事件,mdev会根据设备信息创建出相应的设备文件
    // class指定在哪个class目录下创建设备信息, parent指定父设备节点(可以为NULL), devt为设备文件的设备号.  drvdata用于设备驱动用的参数(可设为NULL), 后面"const char *fmt, ..."是用printf的方式来设置设备文件的文件名. 如(..., "mydev%d", i):表示设备文件名由mydev与变量i的值组成.

void device_destroy(struct class *class, dev_t devt); //指定在class目录下移除指定设备号的设备信息. mdev就会移除相应的设备文件.

/
如修改前面一个设备驱支持多个设备文件的例子:
test.c

#include <linux/init.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/slab.h>
#include <asm/uaccess.h>
#include <linux/device.h>

#define MYMA  1234
#define MYMI  7788
#define COUNT 3 //三个设备号,每个设备号对应一个设备文件和一个数据缓冲区

dev_t devid;
struct cdev mycdev;
u8 *data[COUNT]; //驱动数据缓冲区
int dlen = 1024; //驱动数据缓冲区长度

ssize_t myread(struct file *fl, char __user *buf, size_t len, loff_t *off)
{
    int mi = MINOR(fl->f_path.dentry->d_inode->i_rdev);
    int len_copy, ret, n;

    n = mi - MYMI;
    if (n >= COUNT)
        return -ENODEV;

    if ((fl->f_pos + len) > strlen(data[n])) 
            len_copy = strlen(data[n]) - fl->f_pos;
    else
            len_copy = len; 

    ret = copy_to_user(buf, data[n]+fl->f_pos, len_copy);

    *off += len_copy - ret; 
    return len_copy - ret;
}

ssize_t mywrite(struct file *fl, const char __user *buf, size_t len, loff_t *off)
{
    int mi = MINOR(fl->f_path.dentry->d_inode->i_rdev);
    int len_copy, ret, n;

    n = mi - MYMI;
    if (n >= COUNT)
        return -ENODEV;
    if ((fl->f_pos + len) > dlen) 
        len_copy = dlen - fl->f_pos; 
    else
        len_copy = len;

    ret = copy_from_user(data[n] + fl->f_pos, buf, len_copy);

    *off += len_copy - ret;

    return len_copy - ret;
}

struct file_operations fops = {
    .owner = THIS_MODULE,
    .read = myread,
    .write = mywrite,
};

struct class *mycls;
static int __init test_init(void)
{
    int ret, i, j;

    devid = MKDEV(MYMA, MYMI);
    ret = register_chrdev_region(devid, COUNT, "mydev");
    if (ret < 0)
        goto err0;

    cdev_init(&mycdev, &fops);
    mycdev.owner = THIS_MODULE;
    ret = cdev_add(&mycdev, devid, COUNT);
    if (ret < 0)
        goto err1;

    for (i = 0; i < COUNT; i++)
    {
        data[i] = kzalloc(dlen, GFP_KERNEL); 
        for (j = 0; j < 26; j++)
            data[i][j] = 'A' + j;
    }

    mycls = class_create(THIS_MODULE, "myclass"); // /sys/clas/目录下会出现myclass子目录
    for (i = 0; i < COUNT ; i++)
        device_create(mycls, NULL, MKDEV(MYMA, MYMI+i), NULL, "mydev%d", i);
    //会在myclass子目录里创建出"mydev0, mydev1, mydev2"设备信息
    // mydev0/uevent的内容:
    // MAJOR=1234
    // MINOR=7788
    // DEVNAME=mydev0


    return 0;
err1:
    unregister_chrdev_region(devid, COUNT);
err0:
    return ret;
}

static void __exit test_exit(void)
{
    int i;

    unregister_chrdev_region(devid, COUNT);
    cdev_del(&mycdev);

    for (i = 0; i < COUNT; i++)
    {
        kfree(data[i]);
        device_destroy(mycls, MKDEV(MYMA, MYMI+i)); //在myclass目录里移除设备信息
    }
    class_destroy(mycls); //移除myclass目录
}

module_init(test_init);
module_exit(test_exit);

MODULE_LICENSE("GPL");
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值