1. 程式人生 > >23 mdev與驅動原始碼實現裝置檔案建立

23 mdev與驅動原始碼實現裝置檔案建立

mdev與驅動原始碼實現裝置檔案建立


前面裝置驅動備載入後,都需要用命令”mknod”來創建出裝置檔案。
其實核心裡有介面在驅動原始碼裡實現創建出裝置檔案。

busybox做的嵌入式檔案系統裡有提供”mdev”命令。


“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就會移除相應的裝置檔案。

驅動原始碼實現裝置檔案建立步驟:

1.包含標頭檔案:
    #include <linux/device.h>

2.定義全域性結構體:
    struct class *mycls;

3.在"/sys/class/"目錄下創建出名為name的子目錄:
    mycls = class_create(THIS_MODULE, "myclass");

4.在class目錄下創建出裝置資訊和產生熱插拔事件,mdev會根據裝置資訊創建出相應的裝置檔案:
    device_create(mycls, NULL, MKDEV(MYMA, MYMI), NULL, "mydev", NULL);

5.在myclass目錄裡移除裝置資訊:
    device_destroy(mycls, MKDEV(MYMA, MYMI));

6.移除myclass目錄:
    class_destroy(mycls);

一個裝置驅支援多個裝置檔案的例子(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  5500
#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=5500
        //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");