新聞中心

EEPW首頁(yè) > 嵌入式系統(tǒng) > 設(shè)計(jì)應(yīng)用 > 驅(qū)動(dòng)學(xué)習(xí)1

驅(qū)動(dòng)學(xué)習(xí)1

作者: 時(shí)間:2016-12-01 來(lái)源:網(wǎng)絡(luò) 收藏
看了好長(zhǎng)一段時(shí)間的書(shū),開(kāi)始做驅(qū)動(dòng),從簡(jiǎn)單的開(kāi)始學(xué)習(xí),實(shí)現(xiàn)最簡(jiǎn)單的字符設(shè)備驅(qū)動(dòng),實(shí)現(xiàn)的功能是對(duì)兩塊內(nèi)存空間(物理內(nèi)存)進(jìn)行控制,實(shí)現(xiàn)讀寫操作。
基本的實(shí)現(xiàn)與很多學(xué)習(xí)驅(qū)動(dòng)程序設(shè)計(jì)的方式一樣,跟著別人的程序跑,自己弄明白,搞清楚其中的道理。具體的實(shí)現(xiàn)過(guò)程,結(jié)合注釋可以看清楚。主要總結(jié)第一次寫驅(qū)動(dòng)過(guò)程中存在的問(wèn)題,以及相應(yīng)的解決方法。
字符設(shè)備驅(qū)動(dòng)的基本流程:
首先,驅(qū)動(dòng)函數(shù)的初始化,以及清除函數(shù)(這部分主要是按著一定的模式編程)
1、 申請(qǐng)主設(shè)備號(hào),其中可以采用靜態(tài)或者動(dòng)態(tài)的方式申請(qǐng)。
2、 創(chuàng)建字符設(shè)備,其中包括初始化字符設(shè)備(分配設(shè)備空間),綁定相關(guān)的操作(相應(yīng)的操作),最后是添加字符設(shè)備(實(shí)現(xiàn)所申請(qǐng)的設(shè)備號(hào)與設(shè)備的綁定)。
3、 設(shè)備的具體實(shí)現(xiàn)以及錯(cuò)誤的處理問(wèn)題。
4、 清除函數(shù)主要包含分配物理空間的釋放以及設(shè)備號(hào)的釋放,字符設(shè)備的注銷等。
第二,主要是設(shè)備相關(guān)操作的具體實(shí)現(xiàn)過(guò)程。(這部分主要是涉及內(nèi)核的一些知識(shí))包含具體的文件打開(kāi),關(guān)閉,以及讀寫,定位操作。具體的參看源碼。
第三,Makefile的基本實(shí)現(xiàn)(也是一定的模塊,但是能夠了解其中的道理)或者直接編譯到內(nèi)核中。
出現(xiàn)的問(wèn)題主要是Makefile文檔的設(shè)計(jì)以及加載問(wèn)題。
問(wèn)題一,是kmalloc函數(shù)等的運(yùn)用需要相應(yīng)的頭文件,kmalloc的頭文件是linux/slab.h,關(guān)于錯(cuò)誤(errno.h)是與CPU體系密切相關(guān)的,因此通常是asm/errno.h,這個(gè)也是常見(jiàn)的問(wèn)題。
問(wèn)題二,是加載出錯(cuò)的問(wèn)題。出現(xiàn)下面的錯(cuò)誤,
insmod:error inserting /home/gong/program/cprogram/module/memdev/memdev.ko: -1 Device or resource busy
這個(gè)錯(cuò)誤產(chǎn)生的原因主要是因?yàn)橹髟O(shè)備號(hào)真正被別的驅(qū)動(dòng)程序使用導(dǎo)致的??梢酝ㄟ^(guò)cat /proc/devices查看具體的情況
...
251 hidraw
252 usbmon
253 bsg
254 rtc
...
需要重新定義主設(shè)備號(hào)。然后重新編譯,即可加載成功。
問(wèn)題三,Makefile文件的設(shè)計(jì)
編譯驅(qū)動(dòng)有兩種方法,可以采用直接內(nèi)核編譯的方式,這種方式主要通過(guò)修改源碼中的Makefile和Kconfig實(shí)現(xiàn)。這種方法比較直觀,但是會(huì)破壞源碼的完整性。
通常采用自己編寫Makefile的方式實(shí)現(xiàn)驅(qū)動(dòng)的編譯。這種方式需要了解Makefile的一些語(yǔ)法,同時(shí)需要了解自己的源碼結(jié)構(gòu),但是這種模式具有很大的相似性,都是基于模塊的設(shè)計(jì)方式。
常用的Makefile模塊如下所示:
ifneq ($(KERNELRELEASE),)
obj-m := memdev.o
else
KDIR :=/opt/LinuxKernel/linux-2.6.38.1
PWD :=$(shell pwd)
default:
make -C $(KDIR) M=$(PWD) modules
clean:
rm -f *.ko *.o *.mod.o *.mod.c *.symvers
endif
具體的分析我說(shuō)說(shuō)自己的理解吧。首先第一句ifneq ($(KERNELRELEASE),)是實(shí)現(xiàn)兩個(gè)變量的比較。比較兩個(gè)參量是否不相等。如果不相等則執(zhí)行下面的語(yǔ)句:obj-m := memdev.o,如果相等就執(zhí)行else后面的語(yǔ)句。
具體的過(guò)程如下:
如果驅(qū)動(dòng)代碼是在內(nèi)核源碼中實(shí)現(xiàn),那么KERNELRELEASE就是一個(gè)已知的值,具體的值是:
# Read KERNELRELEASE from include/config/kernel.release (if it exists)
KERNELRELEASE = $(shell cat include/config/kernel.release 2> /dev/null)
KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
但是另一個(gè)值為空,這樣兩個(gè)值就是不相等的,這樣就執(zhí)行obj-m := memdev.o。
但是如果不是在內(nèi)核源碼中實(shí)現(xiàn),那么KERNELRELEASE就是一個(gè)空值,這樣兩個(gè)參數(shù)就是相等的。執(zhí)行else以后的語(yǔ)句。
KDIR :=/opt/LinuxKernel/linux-2.6.38.1
PWD :=$(shell pwd)
上面的兩句是定義兩個(gè)變量,其中的KDIR是內(nèi)核源碼的根目錄,PWD則是當(dāng)前驅(qū)動(dòng)代碼所在的目錄。
然后執(zhí)行
default:
make -C $(KDIR) M=$(PWD) modules
default是一個(gè)默認(rèn)的目標(biāo),沒(méi)有相關(guān)的依賴,規(guī)則是make -C $(KDIR) M=$(PWD) modules
其中參數(shù)-C和M=都有各自的意義,具體的可以參看Makefile的相關(guān)資料。
我的理解就是:
-C 選項(xiàng)的作用是指將當(dāng)前工作目錄轉(zhuǎn)移到你所指定的位置(頁(yè)就是內(nèi)核源碼的根目錄)
“M=dir”,內(nèi)核程序會(huì)自動(dòng)到你所指定的dir目錄中查找模塊源碼,將其編譯,生成.KO文件。實(shí)現(xiàn)驅(qū)動(dòng)程序的編譯。
具體的注意事項(xiàng):KERNELRELEASE這個(gè)變量不要寫錯(cuò),這個(gè)比較容易出錯(cuò),還容易檢查出來(lái)。我第一次寫代碼就出現(xiàn)了相應(yīng)的錯(cuò)誤。
需要注意的是該makefile被執(zhí)行了兩次,其中第一次是由于沒(méi)有定義KERNELRELEASE對(duì)象,當(dāng)進(jìn)入到內(nèi)核源碼以后,這時(shí)KERNELRELEASE對(duì)象已經(jīng)被定義好了,然后返回到當(dāng)前的文件夾下,這時(shí)候直接執(zhí)行第一句obj-m := memdev.o。這樣就實(shí)現(xiàn)了內(nèi)核的編譯。
具體的應(yīng)用程序代碼我都是按著別人的代碼修改調(diào)試的。
下面是我的源碼:
第一個(gè)代碼是頭文件:
#ifndef _MEMDEV_H_H_
#define _MEMDEV_H_H_
#ifndef MEMDEV_MAJOR
#define MEMDEV_MAJOR 555
#endif
#ifndef MEMDEV_NR_DEVS
#define MEMDEV_NR_DEVS 2
#endif
#ifndef MEMDEV_SIZE
#define MEMDEV_SIZE 2048
#endif
struct mem_dev
{
char *data;
unsigned long size;
};
#endif
第二個(gè)是C文件:
#include
#include
#include
//#include
#include
#include
#include
/*該頭文件主要實(shí)現(xiàn)內(nèi)存的控制,包括kmalloc等函數(shù)*/
#include
#include
#include
#include
/*錯(cuò)誤的不同,需要具體的類型*/
#include
/*自己定義的頭文件*/
#include"memdev.h"
static mem_major = MEMDEV_MAJOR;
module_param(mem_major,int,S_IRUGO);
module_param(mem_major,int,S_IRUGO);
struct mem_dev *mem_devp;
struct cdev cdev;
/*函數(shù)的聲明*/
static int memdev_init(void);
static void memdev_exit(void);
int mem_open(struct inode *inode,struct file *filp);
int mem_release(struct inode *inode, struct file *flip);
static ssize_t mem_read(struct file *flip,char __user *buf, size_t size,loff_t *ppos);
static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos);
static loff_t mem_llseek(struct file *filp,loff_t offset, int whence);
/*添加該模塊的基本文件操作支持*/
static const struct file_operations mem_fops =
{
.owner = THIS_MODULE,
.llseek = mem_llseek,
.read = mem_read,
.write = mem_write,
.open = mem_open,
.release = mem_release,
};
/*初始化函數(shù)*/
static int memdev_init(void)
{
int result;
int i;
/*創(chuàng)建一個(gè)設(shè)備號(hào)*/
dev_t devno = MKDEV(mem_major,0);
/*注冊(cè)一個(gè)設(shè)備號(hào)*/
/*如果定義了主設(shè)備號(hào)采用靜態(tài)申請(qǐng)的方式*/
if(mem_major)
result = register_chrdev_region(devno,2,"mem_dev");
else/*動(dòng)態(tài)申請(qǐng)?jiān)O(shè)備號(hào)*/
{
result = alloc_chrdev_region(&devno,0,2,"mem_dev");
mem_major = MAJOR(result);
}
/*錯(cuò)誤處理*/
if(result < 0)
return result;
/*創(chuàng)建一個(gè)設(shè)備*/
/*初始化cdev,并將相關(guān)的文件操作添加進(jìn)來(lái)*/
cdev_init(&cdev,&mem_fops);
cdev.owner = THIS_MODULE;
cdev.ops = &mem_fops;
/*注冊(cè)字符設(shè)備*/
cdev_add(&cdev,MKDEV(mem_major,0),MEMDEV_NR_DEVS);
/*分配兩個(gè)內(nèi)存空間,此處是在物理內(nèi)存上實(shí)現(xiàn)分配,實(shí)質(zhì)是創(chuàng)建兩個(gè)設(shè)備*/
mem_devp = kmalloc(MEMDEV_NR_DEVS * sizeof(struct mem_dev),GFP_KERNEL);
if(!mem_devp)/*出錯(cuò)的相應(yīng)操作*/
{
result = -ENOMEM;
/*錯(cuò)誤處理,采用典型的goto語(yǔ)句*/
goto fail_malloc;
}
上一頁(yè) 1 2 下一頁(yè)

關(guān)鍵詞: 驅(qū)動(dòng)學(xué)

評(píng)論


技術(shù)專區(qū)

關(guān)閉