嵌入式之Linux驅(qū)動(三)

姓名:鄭煜爍? 學(xué)號:19029100010? 學(xué)院:電子工程學(xué)院

轉(zhuǎn)自:https://blog.csdn.net/u012142460/article/details/78906576

【嵌牛導(dǎo)讀】簡單介紹字符設(shè)備以及字符設(shè)備驅(qū)動等

【嵌牛鼻子】字符設(shè)備驅(qū)動框架

【嵌牛提問】何為字符設(shè)備驅(qū)動

【嵌牛正文】

前面我們講了linux驅(qū)動框架linux驅(qū)動(一)驅(qū)動框架虎锚,對驅(qū)動的基本框架有了了解∈υ妫現(xiàn)在我們來說一說字符設(shè)備驅(qū)動,我們一般講驅(qū)動分為三類芒填,字符設(shè)備、塊設(shè)備栓撞、網(wǎng)絡(luò)設(shè)備絮蒿。字符設(shè)備和塊設(shè)備是按照傳輸時的基本單位來劃分的,字符設(shè)備就是傳輸時是按字符來傳輸?shù)耐迓热绱凇PIO砌梆、SPI等卵蛉。字符設(shè)備如硬盤等按照塊傳輸?shù)脑O(shè)備,塊設(shè)備和網(wǎng)絡(luò)設(shè)備的驅(qū)動我們跟多是做移植的工作么库,字符設(shè)備種類繁多且不算復(fù)雜傻丝,所以就會自己來寫。

? ? ? ? 一 設(shè)備號

? ? ? ? 這么多設(shè)備如何區(qū)分诉儒,這就是設(shè)備號的作用葡缰,設(shè)備號又分為主設(shè)備號和次設(shè)備號。主設(shè)備號表征設(shè)備屬于哪一類設(shè)備忱反,比如串口設(shè)備泛释。次設(shè)備號表示主設(shè)備號下的具體哪個設(shè)備。比如說串口1温算、串口2怜校、串口3等等。

? 用4字節(jié)來表示設(shè)備號注竿,其中主設(shè)備號占用 高 12位茄茁,次設(shè)備號占用 低 20位。

? ? ? ? (1) linux提供了一組宏來生成設(shè)備號

? ? ? ? ? #define MINORBITS20

? #define MINORMASK((1U << MINORBITS) - 1)

? #define MAJOR(dev)((unsigned int) ((dev) >> MINORBITS))? ? 獲取主設(shè)備號

? #define MINOR(dev)((unsigned int) ((dev) & MINORMASK))? ? 獲取次設(shè)備號

? #define MKDEV(ma,mi)(((ma) << MINORBITS) | (mi))? // ma << 20 | mi? 生成設(shè)備號

用起來還是很簡單的巩割。

int major = 250;

int minor = 0;

int devno;

devno = MKDEV(major, minor);

? ? ? ? (2) 申請注冊設(shè)備號

? ? ? ? 上面生成設(shè)備號之后裙顽,我們要將這個設(shè)備號注冊到內(nèi)核當(dāng)中,確保沒有注冊過即可宣谈。這是靜態(tài)注冊愈犹,還有動態(tài)注冊。

? ? ? ? ? int register_chrdev_region(dev_t from, unsigned count, const char *name)

參數(shù):from? 設(shè)備號

count? 次設(shè)備的數(shù)量

name? 設(shè)備號的名稱

返回值:成功 0闻丑, 出錯負(fù)數(shù)的錯誤碼

? ? ? 這個注冊函數(shù)可以注冊多個設(shè)備漩怎,在count處設(shè)置即可勋颖。設(shè)備號的名稱是讓內(nèi)核用的,一會兒我們說到創(chuàng)建設(shè)備文件名稱是讓用戶看的勋锤,這兩者不要混淆牙言。

? ? ? 有注冊就會有取消注冊

? ? ? ? ? void unregister_chrdev_region(dev_t from, unsigned count)

? 參數(shù):from? 設(shè)備號

? ? ? ? count? 次設(shè)備的數(shù)量

? ? ? ? ? 設(shè)備注冊之后那里可以查看呢?看這里 proc/devcies文件

來源 CSDN
來源 CSDN

這里分了字符設(shè)備和塊設(shè)備怪得。前面表示的就是設(shè)備號

二 字符設(shè)備對象

? ? ? C語言是面向過程的語言,其他高級語言如c++卑硫,java等都是面向?qū)ο蟮耐搅担嫦驅(qū)ο蟮暮锰幉谎远鳎@里不說了欢伏。c語言中也可以利用結(jié)構(gòu)體來實現(xiàn)一個面向?qū)ο蟮倪^程入挣。上面我們向內(nèi)核注冊了一個設(shè)備號,那這個設(shè)備號用來干嘛呢硝拧?我們對應(yīng)這個設(shè)備號就要生成并向內(nèi)核注冊一個字符設(shè)備對象径筏,來表明這個設(shè)備實現(xiàn)的功能。

? ? ? ? 一個字符設(shè)備對象如下

? struct cdev {

struct kobject kobj;? // 設(shè)備對象的基類

struct module *owner;? // 直接賦值為THIS_MODULE? 模塊的擁有者

const struct file_operations *ops; // 文件操作集合

struct list_head list;? // 包含此結(jié)構(gòu)體的成員障陶,都是內(nèi)核循環(huán)雙鏈表節(jié)點

dev_t dev;? // 設(shè)備號

unsigned int count;? // 次設(shè)備的數(shù)量

};

? ? ? ? 我們主要關(guān)注ops和dev就行滋恬,dev就是我們的設(shè)備號,每一個設(shè)備號對應(yīng)一個設(shè)備對象抱究。ops中就是一堆的操作方法恢氯。對象中必須得有方法,設(shè)備對象的方法在哪里鼓寺?全部都在ops當(dāng)中勋拟。我們來看看ops到底都有啥

struct file_operations {

struct module *owner;? ? //直接賦值為THIS_MODULE? 模塊的擁有者

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 *);

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 *, loff_t, loff_t, 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 (*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 **);

long (*fallocate)(struct file *file, int mode, loff_t offset,

loff_t len);

};

? ? ? ? ops中有好多操作方法,我們不用全部實現(xiàn)妈候。我們重點關(guān)注標(biāo)紅的那幾個即可敢靡,我們想想在應(yīng)用層操作一個文件時常用的有那幾個呢?open close read write苦银,就是對應(yīng)我們這里的open release read write啸胧,操作設(shè)備時我們還經(jīng)常會用ioctl,比如說串口設(shè)置波特率幔虏,對應(yīng)我們這里的unlocked_ioctl吓揪。

? ? ? ? 該實現(xiàn)的方法都實現(xiàn)后,我們將實現(xiàn)的方法和對象綁定到一起所计,其實就是設(shè)備對象的方法集合初始化柠辞。

? void cdev_init(struct cdev *cdev, const struct file_operations *fops)

? 參數(shù):cdev? 字符設(shè)備對象

? ? ? ? ? ? fops? 文件操作集合

? ? ? 使用時候的框架如下

dev_t? devno;

int major = 250;

int minor = 0;

int count = 1;

struct cdev cdev;? //設(shè)備對象

int? demo_open(struct inode *inodep, struct file * filep) // 打開設(shè)備

{

? ? return 0;

}

int demo_release(struct inode * inodep, struct file * filep) // 關(guān)閉設(shè)備

{

? ? return 0;

}

ssize_t demo_read(struct file * filep, char __user * buffer, size_t size, loff_t * offlen)

{

? ? return size;

}

ssize_t demo_write(struct file *filep, const char __user *buffer, size_t size, loff_t * offlen)

{

return size;

}

long demo_ioctl(struct file * filep, unsigned int cmd, unsigned long arg)

{

? return 0;

}

struct file_operations fops = {

.owner = THIS_MODULE,

.open = demo_open,

.release = demo_release,

.read = demo_read,

.write = demo_write,

.unlocked_ioctl = demo_ioctl,

};

好,設(shè)備對象的初始化也完成了主胧,我們還得把這個設(shè)備對象告訴內(nèi)核叭首,內(nèi)核才知道有這個東東习勤。這就是設(shè)備對象的注冊,或者叫做設(shè)備對象的添加焙格。? ?

? int cdev_add(struct cdev *p, dev_t dev, unsigned count)

? 參數(shù):p? 字符設(shè)備對象

? ? ? ? dev 設(shè)備號

? ? ? ? count? 次設(shè)備的數(shù)量

? 返回值:成功 0图毕,出錯 負(fù)數(shù)的錯誤碼

有注冊就會有取消注冊

? void cdev_del(struct cdev *p)

這里稍微注意注冊設(shè)備號和注冊設(shè)備對象的順序,先注冊設(shè)備號眷唉,再注冊設(shè)備對象予颤。很好理解嘛,注冊設(shè)備對象時都得有設(shè)備號的參數(shù)了冬阳,內(nèi)核要把設(shè)備對象綁定到設(shè)備號上了蛤虐,你不得先向內(nèi)核注冊設(shè)備號嗎?

取消注冊的時候順序反過來肝陪,先取消注冊設(shè)備對象驳庭,再取消注冊設(shè)備號。

? ? ? 上面講的都是內(nèi)核層面氯窍,我們在應(yīng)用層使用open時的第一個參數(shù)是啥饲常,是要打開文件的路徑,詳細(xì)情況參考linux學(xué)習(xí)(十六):文件IO狼讨。那怎么和我們之前說的一大堆對應(yīng)上呢贝淤?我們知道linux中,一切皆文件政供。驅(qū)動設(shè)備也一樣霹娄。驅(qū)動設(shè)備文件在哪里,我們查看/dev/,那里就是設(shè)備文件統(tǒng)一聚集地鲫骗。這里的文件就是和應(yīng)用層交互用的犬耻。

來源 CSDN

我們怎么創(chuàng)建一個設(shè)備文件呢?我們來看看手動方式执泰,執(zhí)行shell命令

mknod name c 主設(shè)備號 次設(shè)備號

name表示設(shè)備文件名稱枕磁,注意和之前說的注冊設(shè)備號名稱區(qū)別,那個是給內(nèi)核用的术吝,這兩者名稱不必相同计济。

c 表示設(shè)備類型為字符設(shè)備

后面為 主設(shè)備號和次設(shè)備號

執(zhí)行這條指令之后,linux變會將創(chuàng)建出來的設(shè)備文件和之前注冊的設(shè)備號和設(shè)備對象綁定在一起排苍。具體的linux如何從應(yīng)用層到底層沦寂,我們下一篇再來詳細(xì)描述一下。

總結(jié)一下上面字符設(shè)備驅(qū)動的一個基本過程:

1淘衙、生成設(shè)備號

2传藏、向內(nèi)核注冊該設(shè)備號

3、初始化設(shè)備對象,完成操作方法集

4毯侦、向內(nèi)核注冊該設(shè)備對象

5哭靖、生成設(shè)備文件,供用戶層調(diào)用侈离。

百聞不如一見试幽,看一個簡單的例子

驅(qū)動層:

#include <linux/init.h>

#include <linux/module.h>

#include <linux/kernel.h>

#include <linux/cdev.h>

#include <linux/fs.h>

MODULE_LICENSE("GPL");

dev_t? devno;

int major = 250;

int minor = 0;

int count = 1;

struct cdev cdev;

int? demo_open(struct inode *inodep, struct file * filep) // 打開設(shè)備

{

printk("%s,%d\n", __func__, __LINE__);

return 0;

}

int demo_release(struct inode * inodep, struct file * filep)? // 關(guān)閉設(shè)備

{

printk("%s,%d\n", __func__, __LINE__);

return 0;

}

struct file_operations? fops = {

.owner = THIS_MODULE,

.open = demo_open,

.release = demo_release,

};

static int __init demo_init(void)

{

int ret = 0;

printk("%s,%d\n", __func__, __LINE__);

devno = MKDEV(major, minor);

printk("devno:%d\n", devno);

ret = register_chrdev_region(devno, count, "xxx");

if(ret)

{

printk("Failed to register_chrdev_region.\n");

return ret;

}

cdev_init(&cdev, &fops);

cdev.owner = THIS_MODULE;

ret = cdev_add(&cdev, devno, count);

if(ret)

{

printk("Failed to cdev_add.\n");

unregister_chrdev_region(devno, count);

return ret;

}

return 0;

}

static void __exit demo_exit(void)

{

printk("%s,%d\n", __func__, __LINE__);

cdev_del(&cdev);

unregister_chrdev_region(devno, count);

}

module_init(demo_init);

module_exit(demo_exit);

這里只實現(xiàn)了簡單的open close,框架嘛對吧 其余的根據(jù)功能實現(xiàn)就是了卦碾。insmod之后铺坞,可以看到/proc/devices下有了個名字為xxx的設(shè)備號

來源 CSDN

看一看應(yīng)用層

#include <stdio.h>

#include <sys/stat.h>

#include <sys/types.h>

#include <fcntl.h>

int main(int argc, const char *argv[])

{

int fd;

fd = open("/dev/hello", O_RDWR);

if(fd < 0)

{

perror("Failed to open.");

return -1;

}

else

{

printf("open success.\n");

}

getchar();

close(fd);

return 0;

}

編譯后看一下執(zhí)行結(jié)果

來源 CSDN

應(yīng)用層成功打開了該設(shè)備文件。按下回車鍵后就關(guān)閉了文件洲胖。

我們看看內(nèi)核層執(zhí)行的結(jié)果

來源 CSDN

成功的執(zhí)行了open和release函數(shù)济榨。

————————————————

版權(quán)聲明:本文為CSDN博主「念念有余」的原創(chuàng)文章,遵循CC 4.0 BY-SA版權(quán)協(xié)議宾濒,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/u012142460/article/details/78906576

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屏箍,一起剝皮案震驚了整個濱河市绘梦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌赴魁,老刑警劉巖卸奉,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異颖御,居然都是意外死亡榄棵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進店門潘拱,熙熙樓的掌柜王于貴愁眉苦臉地迎上來疹鳄,“玉大人,你說我怎么就攤上這事芦岂”窆” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵禽最,是天一觀的道長腺怯。 經(jīng)常有香客問我,道長川无,這世上最難降的妖魔是什么呛占? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮懦趋,結(jié)果婚禮上晾虑,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好走贪,可當(dāng)我...
    茶點故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布佛猛。 她就那樣靜靜地躺著,像睡著了一般坠狡。 火紅的嫁衣襯著肌膚如雪继找。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天逃沿,我揣著相機與錄音婴渡,去河邊找鬼。 笑死凯亮,一個胖子當(dāng)著我的面吹牛边臼,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播假消,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼柠并,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了富拗?” 一聲冷哼從身側(cè)響起臼予,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎啃沪,沒想到半個月后粘拾,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡创千,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年缰雇,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片追驴。...
    茶點故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡械哟,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出殿雪,到底是詐尸還是另有隱情戒良,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布冠摄,位于F島的核電站糯崎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏河泳。R本人自食惡果不足惜沃呢,卻給世界環(huán)境...
    茶點故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望拆挥。 院中可真熱鬧薄霜,春花似錦某抓、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至崎坊,卻和暖如春备禀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背奈揍。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工曲尸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人男翰。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓另患,卻偏偏與公主長得像,于是被迫代替她去往敵國和親蛾绎。 傳聞我的和親對象是個殘疾皇子昆箕,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容