亚洲综合图片区自拍_思思91精品国产综合在线观看_一区二区三区欧美_欧美黑人又粗又大_亚洲人成精品久久久久桥本

Linux系統(tǒng)中多個同類設(shè)備共用一套驅(qū)動

2023-08-30 10:06:34 來源:嵌入式開發(fā)愛好者

1. 應用場景

比如我們的設(shè)備上有很多一樣的usb接口,這些usb接口都需要有驅(qū)動才能工作,那么是每個usb都一套單獨的驅(qū)動程序么?顯然不是的,這些usb接口屬于同一類設(shè)備,用戶對他們的操作方法完全一致,只不過不是同一個設(shè)備,所以他們可以復用同一套驅(qū)動代碼,在代碼中去判斷用戶要操作哪個設(shè)備,然后去open/read/write這個設(shè)備。


【資料圖】

2. 如何區(qū)分不同的設(shè)備

前面說過,每個設(shè)備都有一個唯一的標識符–設(shè)備號,那么對于同一類設(shè)備,它們的主設(shè)備號是一樣的,次設(shè)備號是不一樣的,用來區(qū)分它們,當用戶想要操作哪個具體的設(shè)備,就會打開這個設(shè)備對應的設(shè)備文件(inode結(jié)構(gòu)體),并自動在內(nèi)核中創(chuàng)建對應的file結(jié)構(gòu)體,這個file結(jié)構(gòu)體中就保存了用戶操作的所有信息,最終會傳給我們的內(nèi)核驅(qū)動,驅(qū)動再根據(jù)這個file結(jié)構(gòu)體和inode結(jié)構(gòu)體來判斷用戶具體要操作的哪個設(shè)備,然后去read/write這個具體的設(shè)備。

案例:

hanp@hanp:/dev/usb$ ls -lcrw------- 1 root root 180, 0  3月 11 17:29 hiddev0crw-------1rootroot180,13月1117:29hiddev1

我的主機下面的兩個usb設(shè)備,他們共用了一套usb驅(qū)動,但是他們的設(shè)備號是不一樣的(180,0)和(180,1),主設(shè)備號都是180表示都屬于同一類設(shè)備(usb設(shè)備),次設(shè)備號分別是0和1,表示這是兩個不同的設(shè)備。

3. 代碼實現(xiàn)

#include #include #include #include #include #include #define NUM_OF_DEVICES    2int major= 255;/* 兩個設(shè)備,所以有兩套結(jié)構(gòu)體 *//* 設(shè)備0對應的設(shè)備結(jié)構(gòu)體是hello_dev[0], 設(shè)備1對應的設(shè)備結(jié)構(gòu)體是hello_dev[1] */struct hello_device {    dev_t devno;    struct cdev cdev;    char data[128];    char name[16];}hello_dev[NUM_OF_DEVICES];struct class * hello_class;const char DEVNAME[] = "hello_device";int hello_open(struct inode * ip, struct file * fp){    printk("%s : %d", __func__, __LINE__);        /* 獲取用戶打開的設(shè)備對應的設(shè)備結(jié)構(gòu)體 hello_dev[0] 或者 hello_dev[1] */    struct hello_device * dev = container_of(ip->i_cdev, struct hello_device, cdev);        /* open的時候,通過container_of能夠獲取到用戶要打開的那個設(shè)備的設(shè)備結(jié)構(gòu)體,所有需要把這個結(jié)構(gòu)體通過file指針的     * private_data參數(shù)傳遞給read/write */    fp->private_data = dev;        /* 一般用來做初始化設(shè)備的操作 */    /* ... */        return 0;}int hello_close(struct inode * ip, struct file * fp){    printk("%s : %d", __func__, __LINE__);        /* 一般用來做和open相反的操作,open申請資源,close釋放資源 */    /* ... */        return 0;}ssize_t hello_read(struct file * fp, char __user * buf, size_t count, loff_t * loff){    int ret;        /* 通過file指針,獲取到用戶要操作的設(shè)備對應的設(shè)備結(jié)構(gòu)體 */    struct hello_device * dev = fp->private_data;        /* 將用戶需要的數(shù)據(jù)從內(nèi)核空間copy到用戶空間(buf) */    printk("%s : %d", __func__, __LINE__);    if (count <=0 || count > 128)        count = 128;    if ((ret = copy_to_user(buf, dev->data, count)))    {        printk("copy_to_user err");        return -1;    }        return count;}ssize_t hello_write(struct file * fp, const char __user * buf, size_t count, loff_t * loff){    int ret;    struct hello_device * dev = fp->private_data;        /* 將用戶需要的數(shù)據(jù)從內(nèi)核空間copy到用戶空間(buf) */    printk("%s : %d", __func__, __LINE__);    if (count <=0 || count > 128)        count = 128;    if ((ret = copy_from_user(dev->data, buf, count)))    {        printk("copy_from_user err");        return -1;    }        return count;}/* 2. 分配file_operations結(jié)構(gòu)體 */struct file_operations hello_fops = {    .owner = THIS_MODULE,    .open  = hello_open,    .release = hello_close,    .read = hello_read,    .write = hello_write};struct cdev cdev;staticint hello_init(void){    int i;    printk("%s : %d", __func__, __LINE__);        /* 1. 生成并注冊兩個設(shè)備的設(shè)備號 */    /* 3. 分配、設(shè)置、注冊兩套cdev結(jié)構(gòu)體 */    for (i = 0; i < NUM_OF_DEVICES; i++)    {        hello_dev[i].devno = MKDEV(major, i);        sprintf(hello_dev[i].name, "%s%d", DEVNAME, i);        register_chrdev_region(hello_dev[i].devno, 1, hello_dev[i].name);                hello_dev[i].cdev.owner = THIS_MODULE;        cdev_add(&hello_dev[i].cdev, hello_dev[i].devno, 1);        cdev_init(&hello_dev[i].cdev, &hello_fops);                /* 初始化兩個設(shè)備各自的存儲空間 */        sprintf(hello_dev[i].data, "Hi, I am hello device %d", i);    }        /* 在/sys/class目錄下創(chuàng)建hello類,并在這個類下面創(chuàng)建hello_device0和hello_device1 */    hello_class = class_create(THIS_MODULE, DEVNAME);    for (i = 0; i < NUM_OF_DEVICES; i++)    {        device_create(hello_class, NULL, hello_dev[i].devno, NULL, "%s%d", DEVNAME, i);        printk("success!");    }    return 0;}static void hello_exit(void){    int i;    printk("%s : %d", __func__, __LINE__);        /* 釋放資源 */    for (i = 0; i < NUM_OF_DEVICES; i++)    {        device_destroy(hello_class, hello_dev[i].devno);        cdev_del(&hello_dev[i].cdev);        unregister_chrdev_region(hello_dev[i].devno, 1);    }    class_destroy(hello_class);}MODULE_LICENSE("GPL");module_init(hello_init);module_exit(hello_exit);

解釋:

container_of:/** * container_of - cast a member of a structure out to the containing structure * @ptr:        the pointer to the member. * @type:       the type of the container struct this is embedded in. * @member:     the name of the member within the struct. * */#define container_of(ptr, type, member)

功能:根據(jù)結(jié)構(gòu)體中某個成員的地址,從而獲取到整個結(jié)構(gòu)體的首地址

@ptr: 已知結(jié)構(gòu)體成員的地址

@type: 要獲取的結(jié)構(gòu)體的類型

@member: 已知結(jié)構(gòu)體成員的名字

我們用到的實例解析:

struct hello_device * dev = container_of(ip->i_cdev, struct hello_device, cdev);

文章最后的圖解和上篇文章中我講到file結(jié)構(gòu)體和inode結(jié)構(gòu)體的關(guān)系,其中inode結(jié)構(gòu)體和文件系統(tǒng)下的文件是一一對應的關(guān)系,里面保存了這個字符設(shè)備對應的cdev結(jié)構(gòu)體:

struct cdev * i_cdev,而這個cdev結(jié)構(gòu)體又包含在設(shè)備結(jié)構(gòu)體hello_device中,其中hello_dev[0]中包含的是設(shè)備0的cdev,hello_dev[1]中包含的是設(shè)備1的cdev,那么

container_of函數(shù)就可以根據(jù)這個cdev來判斷用戶打開的是hello_dev[0]還是hello_dev[1]并獲取到地址。

編譯安裝驅(qū)動:

sudo insmod hello.ko

hanp@hanp:/dev$ ls hello_device*hello_device0hello_device1
hanp@hanp:/dev$ cat /proc/devices | grep hello255 hello_device0255hello_device1

可以看到在/proc/devices下注冊了兩個設(shè)備hello_device0和hello_device1,這兩個設(shè)備的主設(shè)備一樣都是255,但是次設(shè)備號不一樣(cat /dev/hello_deviceX可以查看次設(shè)備號)。

4. 寫應用程序進行測試 app.c

#include #include #include #include #include #include int main(char argc, char * argv[]){    int fd;    int ret;    char buf[64];        if (argc != 2)    {        printf("Usage: %s ", argv[0]);        return -1;    }        fd = open(argv[1], O_RDWR);    if (fd < 0)    {        perror("fail to open file");        return -1;    }        /* read data */    ret = read(fd, buf, sizeof(buf));    if (ret < 0)    {        printf("read err!");        return -1;    }    printf("buf = %s", buf);        /* write data */    strcpy(buf, "write data from app!");    ret = write(fd, buf, sizeof(buf));    if (ret < 0)    {        printf("write err!");        return -1;    }    read(fd, buf, sizeof(buf));    printf("buf = %s", buf);        close(fd);    return 0;}

測試:

$ gcc app.c$ sudo ./a.out /dev/hello_device0buf = Hi, I am hello device 0buf = write data from app!$ sudo ./a.out /dev/hello_device1buf = Hi, I am hello device 1buf=writedatafromapp!

審核編輯:湯梓紅

標簽:

上一篇:談談什么是文件系統(tǒng) 文件系統(tǒng)的功能與特點
下一篇:最后一頁