字符驅(qū)動(dòng)設(shè)備的另一種寫法

學(xué)號(hào):19021211263

一、register_chrdev的缺點(diǎn)

之前注冊(cè)字符設(shè)備用的如下函數(shù)注冊(cè)字符設(shè)備驅(qū)動(dòng):

register_chrdev(unsigned int major, const char *name,const struct file_operations *fops);

但其實(shí)這個(gè)函數(shù)是linux版本2.4之前的注冊(cè)方式,它的原理是:

  • (1)確定一個(gè)主設(shè)備號(hào)

  • (2)構(gòu)造一個(gè)file_operations結(jié)構(gòu)體, 然后放在chrdevs數(shù)組中

  • (3)注冊(cè):register_chrdev

然后當(dāng)讀寫字符設(shè)備的時(shí)候,就會(huì)根據(jù)主設(shè)備號(hào)從chrdevs數(shù)組中取出相應(yīng)的結(jié)構(gòu)體,并調(diào)用相應(yīng)的處理函數(shù)。

它會(huì)有個(gè)很大的缺點(diǎn):

  • 每注冊(cè)個(gè)字符設(shè)備,還會(huì)連續(xù)注冊(cè)0~255個(gè)次設(shè)備號(hào),使它們綁定在同一個(gè)file_operations操作方法結(jié)構(gòu)體上,在大多數(shù)情況下,都只用極少的次設(shè)備號(hào),所以會(huì)浪費(fèi)很多資源。

  • 最多只能有255個(gè)設(shè)備

改進(jìn)后的優(yōu)點(diǎn):

  • 通過一定區(qū)間長度的次設(shè)備號(hào)將file_operations操作方法結(jié)構(gòu)體的數(shù)量限制住
  • 通過主設(shè)備號(hào)和次設(shè)備號(hào)來查找設(shè)備,其中次設(shè)備號(hào)20位,主設(shè)備號(hào)12位,理論上最大支持4G個(gè)驅(qū)動(dòng)程序

二、全新字符驅(qū)動(dòng)設(shè)備的注冊(cè)方法

在2.4版本后,內(nèi)核里就加入了以下幾個(gè)函數(shù)也可以來實(shí)現(xiàn)注冊(cè)字符設(shè)備:

1、register_chrdev_region

/*指定設(shè)備編號(hào)來靜態(tài)注冊(cè)一個(gè)字符設(shè)備*/
int register_chrdev_region(dev_t from, unsigned count, const char *name);   

from: 注冊(cè)的指定起始設(shè)備編號(hào),比如:MKDEV(100, 0),表示起始主設(shè)備號(hào)100, 起始次設(shè)備號(hào)為0

count:需要連續(xù)注冊(cè)的次設(shè)備編號(hào)個(gè)數(shù),比如: 起始次設(shè)備號(hào)為0,count=100,表示0~99的次設(shè)備號(hào)都要綁定在同一個(gè)file_operations操作方法結(jié)構(gòu)體上

name:字符設(shè)備名稱

當(dāng)返回值小于0,表示注冊(cè)失敗

2、alloc_chrdev_region

/*動(dòng)態(tài)分配一個(gè)字符設(shè)備,注冊(cè)成功并將分配到的主次設(shè)備號(hào)放入*dev里*/
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,const char *name);

dev: 存放起始設(shè)備編號(hào)的指針,當(dāng)注冊(cè)成功, *dev就會(huì)等于分配到的起始設(shè)備編號(hào),可以通過MAJOR()和MINNOR()函數(shù)來提取主次設(shè)備號(hào)

baseminor:次設(shè)備號(hào)基地址,也就是起始次設(shè)備號(hào)

count:需要連續(xù)注冊(cè)的次設(shè)備編號(hào)個(gè)數(shù),比如: 起始次設(shè)備號(hào)(baseminor)為0,baseminor=2,表示0~1的此設(shè)備號(hào)都要綁定在同一個(gè)file_operations操作方法結(jié)構(gòu)體上

name:字符設(shè)備名稱

當(dāng)返回值小于0,表示注冊(cè)失敗

3、cdev_init

 /*初始化cdev結(jié)構(gòu)體,并將file_operations結(jié)構(gòu)體放入cdev-> ops 里*/
void cdev_init(struct cdev *cdev, const struct file_operations *fops);

其中cdev結(jié)構(gòu)體的成員,如下所示:

struct cdev {
    struct kobject    kobj;            // 內(nèi)嵌的kobject對(duì)象 
    struct module   *owner;            //所屬模塊
    const struct file_operations  *ops;//操作方法結(jié)構(gòu)體
    struct list_head  list;          //與 cdev 對(duì)應(yīng)的字符設(shè)備文件的 inode->i_devices 的鏈表頭
    dev_t dev;                  //起始設(shè)備編號(hào),可以通過MAJOR(),MINOR()來提取主次設(shè)備號(hào)
    unsigned int count;                //連續(xù)注冊(cè)的次設(shè)備號(hào)個(gè)數(shù)
};

其中可以通過MAJOR(cdev->dev),MINOR(cdev->dev)來提取主次設(shè)備號(hào),亦可以通過MKDEV(major,minor)來將主設(shè)備號(hào)major和次設(shè)備號(hào)minor轉(zhuǎn)換成dev_t類型變量dev

4、cdev_add

/*將cdev結(jié)構(gòu)體添加到系統(tǒng)中,并將dev(注冊(cè)好的設(shè)備編號(hào))放入cdev-> dev里,  count(次設(shè)備編號(hào)個(gè)數(shù))放入cdev->count里*/
int cdev_add(struct cdev *p, dev_t dev, unsigned count);

5、cdev_del

/*將系統(tǒng)中的cdev結(jié)構(gòu)體刪除掉*/
void cdev_del(struct cdev *p);

6、unregister_chrdev_region

 /*注銷字符設(shè)備*/
void unregister_chrdev_region(dev_t from, unsigned count);

from: 注銷的指定起始設(shè)備編號(hào),比如:MKDEV(100, 0),表示起始主設(shè)備號(hào)100, 起始次設(shè)備號(hào)為0

count:需要連續(xù)注銷的次設(shè)備編號(hào)個(gè)數(shù),比如: 起始次設(shè)備號(hào)為0,baseminor=100,表示注銷掉0~99的次設(shè)備號(hào)

三、編寫全新的字符驅(qū)動(dòng)設(shè)備

通過調(diào)用上面的函數(shù),構(gòu)造兩個(gè)不同的file_operations操作結(jié)構(gòu)體,

次設(shè)備號(hào)0~1對(duì)應(yīng)第一個(gè)file_operations,

次設(shè)備號(hào)2~3對(duì)應(yīng)第二個(gè)file_operations,

然后在/dev/下,通過次設(shè)備號(hào)(0~4)創(chuàng)建5個(gè)設(shè)備節(jié)點(diǎn), 利用應(yīng)用程序打開這5個(gè)文件,看有什么現(xiàn)象

驅(qū)動(dòng)代碼

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

static int hello_fops1_open(struct inode *inode, struct file *file)
{
    printk("open_hello1!!!\n");
    return 0;
}

static int hello_fops2_open (struct inode *inode, struct file *file)
{
    printk("open_hello2!!!\n");
    return 0;
}


/*  操作結(jié)構(gòu)體1   */
static struct file_operations hello1_fops={
    .owner=THIS_MODULE,
    .open =hello_fops1_open,
};

/*  操作結(jié)構(gòu)體2   */
static struct file_operations hello2_fops={
    .owner=THIS_MODULE,
    .open =hello_fops2_open,
};


static int major;              //主設(shè)備
static struct cdev hello1_cdev;//保存 hello1_fops操作結(jié)構(gòu)體的字符設(shè)備 
static struct cdev hello2_cdev;//保存 hello2_fops操作結(jié)構(gòu)體的字符設(shè)備 
static struct class *cls;

static int chrdev_ragion_init(void)
{
    dev_t  devid;  

    alloc_chrdev_region(&devid, 0, 4,"hello");//動(dòng)態(tài)分配字符設(shè)備: (major,0)、(major,1)、(major,2)、(major,3)

    major=MAJOR(devid);

    cdev_init(&hello1_cdev, &hello1_fops);
    cdev_add(&hello1_cdev, MKDEV(major,0), 2);//(major,0) (major,1)


    cdev_init(&hello2_cdev, &hello2_fops);
    cdev_add(&hello2_cdev,MKDEV(major,2), 2);//(major,2) (major,3)     

    cls=class_create(THIS_MODULE, "hello");
    /*創(chuàng)建字符設(shè)備節(jié)點(diǎn)*/
    class_device_create(cls,0, MKDEV(major,0), 0, "hello0");//對(duì)應(yīng)hello_fops1操作結(jié)構(gòu)體
    class_device_create(cls,0, MKDEV(major,1), 0, "hello1");//對(duì)應(yīng)hello_fops1操作結(jié)構(gòu)體
    class_device_create(cls,0, MKDEV(major,2), 0, "hello2");//對(duì)應(yīng)hello_fops2操作結(jié)構(gòu)體
    class_device_create(cls,0, MKDEV(major,3), 0, "hello3");//對(duì)應(yīng)hello_fops2操作結(jié)構(gòu)體
    class_device_create(cls,0, MKDEV(major,4), 0, "hello4");//對(duì)應(yīng)空
    return 0;
}

void chrdev_ragion_exit(void)
{
    class_device_destroy(cls, MKDEV(major,4));
    class_device_destroy(cls, MKDEV(major,3));
    class_device_destroy(cls, MKDEV(major,2));
    class_device_destroy(cls, MKDEV(major,1));
    class_device_destroy(cls, MKDEV(major,0));

    class_destroy(cls);


    cdev_del(&hello1_cdev);     
    cdev_del(&hello2_cdev); 
    unregister_chrdev_region(MKDEV(major,0), 4);//注銷(major,0)~(major,3)
}

module_init(chrdev_ragion_init);
module_exit(chrdev_ragion_exit);
MODULE_LICENSE("GPL");

測試代碼

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//打印使用幫助信息
void print_useg(char arg[])
{
    printf("useg:  \n");
    printf("%s   [dev]\n",arg);
}

int main(int argc,char **argv)
{
    int fd;
    if(argc!=2)
    {
        print_useg(argv[0]);
        return -1;
    }

    fd=open(argv[1],O_RDWR);
    if(fd<0)
        printf("can't open %s \n",argv[1]);
    else
        printf("can open %s \n",argv[1]);
    return 0;
}

運(yùn)行測試

如下所示,掛載驅(qū)動(dòng)后,通過 ls /dev/hello* -l ,看到創(chuàng)建了5個(gè)字符設(shè)備節(jié)點(diǎn)

# ls /dev/hello* -l
crw-rw----    1 0     0     252, 0 Jan 1 00: 06/dev/hello0
crw-rw----    1 0     0     252, 1 Jan 1 00: 06/dev/hello1
crw-rw----    1 0     0     252, 2 Jan 1 00: 06/dev/hello2
crw-rw----    1 0     0     252, 3 Jan 1 00: 06/dev/hello3
crw-rw----    1 0     0     252, 4 Jan 1 00: 06/dev/hello4

接下來開始測試驅(qū)動(dòng),如下圖所示,

打開/dev/hello0時(shí),調(diào)用的是驅(qū)動(dòng)代碼的操作結(jié)構(gòu)體hello1_fops里的.open(),

打開/dev/hello2時(shí),調(diào)用的是驅(qū)動(dòng)代碼的操作結(jié)構(gòu)體hello2_fops里的.open(),

打開/dev/hello4時(shí),打開無效,因?yàn)樵隍?qū)動(dòng)代碼里沒有分配次設(shè)備號(hào)4的操作結(jié)構(gòu)體,

# ./20th_chrdev_region_test /dev/hello0
open_hello1!!!
can open /dev/hello0
#
#
# ./20th_chrdev_region_test /dev/hello2
open_hello2!!!
can open /dev/hello2
#
#
# ./20th_chrdev_region_test /dev/hello4
can't open /dev/hello4

總結(jié)

使用register_chrdev_region()等函數(shù)來注冊(cè)字符設(shè)備,里面可以存放多個(gè)不同的file_oprations操作結(jié)構(gòu)體,實(shí)現(xiàn)各種不同的功能

更多有趣內(nèi)容歡迎訪問我的個(gè)人博客。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容