国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
十一、Linux驅動程序開發(fā)(2) - 字符設備驅動(1)
十一、Linux驅動程序開發(fā)(2) - 字符設備驅動(1)

Linux驅動程序開發(fā)(2 - 字符設備驅動

Linux下的大部分驅動程序都是字符設備驅動程序,通過下面的學習我們將會了解到字符設備是如何注冊到系統(tǒng)中的,應用程序是如何訪問驅動程序的數(shù)據(jù)的,及字符驅動程序是如何工作的。

設備號
通過前面的學習我們知道應用程序是通過設備節(jié)點來訪問驅動程序及設備的,其根本是通過設備節(jié)點的設備號(主設備號及從設備號)來關聯(lián)驅動程序及設備的,字符設備也不例外(其實字符設備只能這樣訪問)。這里我們詳細討論Linux內部如何管理設備號的。

·        設備號類型

Linux內核里用dev_t來表示設備號,它是一個32位的無符號數(shù),其高12位用來表示主設備號,低20位用來表示從設備號。它被定義在<linux/types.h>頭文件里。內核里提供了操作“dev_t”的函數(shù),驅動程序中通過這些函數(shù)(其實是宏,定義在<linux/kdev_t.h>文件中)來操作設備號。

#define MINORBITS    20
#define MINORMASK    ((1U << MINORBITS) - 1)
#define MAJOR(dev)    ((unsigned int) ((dev) >> MINORBITS))
#define MINOR(dev)    ((unsigned int) ((dev) & MINORMASK))
#define MKDEV(ma,mi)    (((ma) << MINORBITS) | (mi))


MAJOR
dev)用于獲取主設備號,MINORdev)用于獲取從設備號,而MKDEVmami)用于通過主設備號和從設備號構造"dev_t"數(shù)據(jù)。
另一點需要說明的是,dev_t數(shù)據(jù)類型支持2^12個主設備號,每個主設備號(通常是一個設備驅動)可以支持2^20個設備,目前來說這已經(jīng)足夠大了,但誰又能說將來還能滿足要求呢?一個良好的編程習慣是不要依賴dev_t這個數(shù)據(jù)類型,切記必須使用內核提供的操作設備號的函數(shù)。

·        字符設備號注冊

內核提供了字符設備號管理的函數(shù)接口,作為一個良好的編程習慣,字符設備驅動程序應該通過這些函數(shù)向系統(tǒng)注冊或注銷字符設備號。

int register_chrdev_region(dev_t from, unsigned count, const char *name)
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count,
            const char *name)
void unregister_chrdev_region(dev_t from, unsigned count)


register_chrdev_region
用于向內核注冊已知可用的設備號(次設備號通常是0)范圍。由于歷史的原因一些設備的設備號是固定的,你可以在內核源代碼樹的Documentation/devices.txt文件中找到這些靜態(tài)分配的設備號。

alloc_chrdev_region用于動態(tài)分配的設備號并注冊到內核中,分配的設備號通過dev參數(shù)返回。作為一個良好的內核開發(fā)習慣,我們推薦你使用動態(tài)分配的方式來生成設備號。
unregister_chrdev_region
用于注銷一個不用的設備號區(qū)域,通常這個函數(shù)在驅動程序卸載時被調用。


字符設備
Linux2.6
內核使用“struct cdev”來記錄字符設備的信息,內核也提供了相關的函數(shù)來操作“struct cdev”對象,他們定義在<linux/cdev.h>頭文件中??梢娮址O備及其操作函數(shù)接口定義的很簡單。

struct cdev {
    struct kobject kobj;
    struct module *owner;
    const struct file_operations *ops;
    struct list_head list;
    dev_t dev;
    unsigned int count;
};

void cdev_init(struct cdev *, const struct file_operations *);
struct cdev *cdev_alloc(void);
void cdev_put(struct cdev *p);
int cdev_add(struct cdev *, dev_t, unsigned);
void cdev_del(struct cdev *);


對于Linux 2.6內核來說,struct cdev是內核字符設備的基礎結構,用來表示一個字符設備,包含了字符設備需要的全部信息。

·        kobjstruct kobject對象數(shù)據(jù),用來描述設備的引用計數(shù),是Linux設備模型的基礎結構。我們在后面的“Linux設備模型在做詳細的介紹。

·        ownerstruct module對象數(shù)據(jù),描述了模塊的屬主,指向擁有這個結構的模塊的指針,顯然它只有對編譯為模塊方式的驅動才由意義。一般賦值位“THIS_MODULE”

·        opsstruct file_operations對象數(shù)據(jù),描述了字符設備的操作函數(shù)指針。對于設備驅動來說,這是一個很重要的數(shù)據(jù)成員,幾乎所有的驅動都要用到這個對象,我們會在下面做詳細介紹。

·        devdev_t對象數(shù)據(jù),描述了字符設備的設備號。

內核提供了操作字符設備對象“struct cdev”的函數(shù),我們只能通過這些函數(shù)來操作字符設備,例如:初始化、注冊、添加、移除字符設備。

·        cdev_alloc:用于動態(tài)分配一個新的字符設備 cdev 對象,并對其進行初始化。采用cdev_alloc分配的cdev對象需要顯示的初始化ownerops對象。

// 參考drivers/scsi/st.c:st_probe 函數(shù)
struct cdev *cdev = NULL;
cdev = cdev_alloc();
// Error Processing
cdev->owner = THIS_MODULE;
cdev->ops = &st_fops;

 

·        cdev_init:用于初始化一個靜態(tài)分配的cdev對象,一般這個對象會嵌入到其他的對象中。cdev_init會自動初始化ops數(shù)據(jù),因此應用程序只需要顯示的給owner對象賦值。cdev_init的功能與cdev_alloc基本相同,唯一的區(qū)別是cdev_init初始化一個已經(jīng)存在的cdev對象,并且這個初始化會影響到字符設備刪除函數(shù)(cdev_del)的行為,請參考cdev_del函數(shù)。

·        cdev_add:向內核系統(tǒng)中添加一個新的字符設備cdev,并且使它立即可用。

·        cdev_del:從內核系統(tǒng)中移除cdev字符設備。如果字符設備是由cdev_alloc動態(tài)分配的,則會釋放分配的內存。

·        cdev_put:減少模塊的引用計數(shù),一般很少會有驅動程序直接調用這個函數(shù)。

文件操作對象
Linux
中的所有設備都是文件,內核中用“struct file”結構來表示一個文件。盡管我們的驅動不會直接使用這個結構中的大部分對象,其中的一些數(shù)據(jù)成員還是很重要的,我們有必要在這里做一些介紹,具體的內容請參考內核源代碼樹<linux/fs.h>頭文件。

// struct file 中的一些重要數(shù)據(jù)成員
const struct file_operations    *f_op;
unsigned int         f_flags;
mode_t            f_mode;
loff_t            f_pos;
struct address_space    *f_mapping;


這里我們不對struct file做過多的介紹,另一篇structfile做詳細介紹。這個結構中的f_ops成員是我們的驅動所關心的,它是一個struct file_operations結構。Linux里的struct file_operations結構描述了一個文件操作需要的所有函數(shù),它定義在<linux/fs.h>頭文件中。

struct file_operations {
    struct module *owner;
    loff_t (*llseek) (struct file *, loff_t, int);
    ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
    ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
    ssize_t (*aio_read) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    ssize_t (*aio_write) (struct kiocb *, const struct iovec *, unsigned long, loff_t);
    int (*readdir) (struct file *, void *, filldir_t);
    unsigned int (*poll) (struct file *, struct poll_table_struct *);
    int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
    long (*unlocked_ioctl) (struct file *, unsigned int, unsigned long);
    long (*compat_ioctl) (struct file *, unsigned int, unsigned long);
    int (*mmap) (struct file *, struct vm_area_struct *);
    int (*open) (struct inode *, struct file *);
    int (*flush) (struct file *, fl_owner_t id);
    int (*release) (struct inode *, struct file *);
    int (*fsync) (struct file *, struct dentry *, int datasync);
    int (*aio_fsync) (struct kiocb *, int datasync);
    int (*fasync) (int, struct file *, int);
    int (*lock) (struct file *, int, struct file_lock *);
    ssize_t (*sendpage) (struct file *, struct page *, int, size_t, loff_t *, int);
    unsigned long (*get_unmapped_area)(struct file *, unsigned long, unsigned long, unsigned long, unsigned long);
    int (*check_flags)(int);
    int (*dir_notify)(struct file *filp, unsigned long arg);
    int (*flock) (struct file *, int, struct file_lock *);
    ssize_t (*splice_write)(struct pipe_inode_info *, struct file *, loff_t *, size_t, unsigned int);
    ssize_t (*splice_read)(struct file *, loff_t *, struct pipe_inode_info *, size_t, unsigned int);
    int (*setlease)(struct file *, long, struct file_lock **);
};


這是一個很大的結構,包含了所有的設備操作函數(shù)指針。當然,對于一個驅動,不是所有的接口都需要來實現(xiàn)的。對于一個字符設備來說,一般實現(xiàn)openrelease、readwrite、mmap、ioctl這幾個函數(shù)就足夠了。
這里需要指出的是,openrelease函數(shù)的第一個參數(shù)是一個struct inode對象。這是一個內核文件系統(tǒng)索引節(jié)點對象,它包含了內核在操作文件或目錄是需要的全部信息。對于字符設備驅動來說,我們關心的是從structinode對象中獲取設備號(inodei_rdev成員)內核提供了兩個函數(shù)來做這件事。

static inline unsigned iminor(const struct inode *inode)
{
    return MINOR(inode->i_rdev);
}
static inline unsigned imajor(const struct inode *inode)
{
    return MAJOR(inode->i_rdev);
}


盡管我們可以直接從inode->i_rdev獲取設備號,但是盡量不要這樣做。我們推薦你調用內核提供的函數(shù)來獲取設備號,這樣即使將來inode->i_rdev有所變化,我們的程序也會工作的很好。

 

字符設備驅動可以參考Linux 設備驅動程序第三版和linux設備驅動開發(fā)詳解,其中linux設備驅動程序第三版中講的:

主次編號

一些重要數(shù)據(jù)結構

字符設備注冊

Openrelease

讀和寫

一些頭文件和結構體;

都非常經(jīng)典,都理解字符驅動設備很重要,很值得參考!
本站僅提供存儲服務,所有內容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權內容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
Linux驅動開發(fā)庖丁解牛之三——揭開字符設備驅動程序的面紗
Linux設備驅動開發(fā)詳解小結(一)之字符設備驅動結構
第三章 字符設備驅動程序|Linux.CN
深入淺出:Linux設備驅動之字符設備驅動 - 博客 - 伯樂在線
linux驅動程序學習2
Linux內核開發(fā)之簡單字符設備驅動(上)
更多類似文章 >>
生活服務
分享 收藏 導長圖 關注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服