在要開發的工程的資料夾下簡歷乙個sourceinsight的資料夾(用來存放sourceinsight軟體生成的一些檔案),然後開啟sourceinsight建立乙個專案,選擇到我們需要編輯的工程資料夾下,sourceinsight可以使開發變得更加簡單。
1.下圖是驅動的基本示意圖
應用程式呼叫open,read,write等函式,open等函式是在c庫中是實現的(通過swi + value指令就會觸發乙個異常),這個異常進入核心空間,核心空間就會根據不同的值呼叫sys_open,sys_read,sys_write等函式,這些函式根據開啟的不同的檔案的屬性找到更為底層的驅動程式。
2.對於乙個最為簡單的字元型驅動程式,最簡單的做法就是讓上層的open直接對應底層的led_open,上層的read直接對應底層的led_read等,上層open函式和底層led_open函式的對應要依賴於驅動的框架來實現。
3.怎麼告訴核心
(1)定義乙個file_operations,並填充。
(2)應用程式裡面有什麼介面,file_operations裡面就有對應的成員。
(3)把這個結構告訴核心:register_chardev(major, "first_dev", &file_operations_name);
major:主裝置號。
"first_dev":裝置名。
&file_operations_name:定義的file_operations結構體的位址。
(4)register_chardev函式的呼叫者是驅動的入口函式。舉例:
int first_dev_init(void)
(5)核心怎麼知道自動去呼叫這個first_dev_init函式?
通過module_init(first_dev_init);這條語句來將這個函式進行一次修飾,module_init會定義乙個結構體,這個結構體裡面有乙個函式指標指向first_dev_init函式,當載入乙個驅動的時候,核心會自動找到這樣的乙個結構體,呼叫裡面的函式指標。
4.主裝置號
應用程式開啟乙個裝置檔案時,這個裝置有自己的屬性(比如字元型裝置,可讀可寫可執行,主裝置號次裝置號等),核心根據裝置型別+主裝置號就找到對應的裝置的file_operations結構體。可以理解為:核心中字元型有乙個陣列,陣列存的是各個主裝置號,每個主裝置號下掛著相應的鍊錶,當註冊的時候就是把對應的結構體掛到相應位置的鍊錶裡面。
5.出口函式
void first_dev_exit(void)
出口函式也需要修飾:
module_exit(firsrt_dev_exit);
6.編譯驅動程式
將第乙個字元型驅動的原始碼放置到乙個新建的資料夾,然後新建乙個makefile檔案:
kern_dir = /usr/linux-2.6.22
all:
make -c $(kern_dir) m="pwd" modules
clean:
make -c $(kern_dir) m="pwd" modules clean
rm -rf modules.order
obj-m += first_dev.o
解析:-c:後面跟乙個目錄,這樣會轉到後面的目錄,用後面目錄裡面的makefile編譯。
m="pwd":當前目錄
modules:目標
然後在這個資料夾下執行:
# make
7.載入驅動
# cat /proc/devices(檢視核心目前支援的裝置)
# insmod first_dev.ko
# cat /proc/devices(檢視是否載入成功)
8.使用乙個簡單的應用程式測試驅動
int main(int argc, char **argv)
交叉編譯這個檔案:
# arm-linux-gcc -o firsttest firsttest.c
# ./firsttest
到這裡會提示無法開啟,原因是沒有對應的裝置節點。
# mknod /dev/*** c 111 0
# ./firsttest
9.主裝置號的確定
可以自己給定,但是推薦使用:
major = register_chardev(0, "first_dev", &first_dev_fops);
這裡引數寫為0,核心就會自動找乙個空的裝置號,這個裝置號通過返回值返回。
10.自動建立裝置節點
udev機制,busybox中是mdev,註冊乙個驅動程式的時候,裝置的資訊會在sys目錄下生成,而mdev會自動的根據裝置資訊來建立節點。
(1)定義兩個全域性變數
static struct class *firstdev_class;//定義乙個類
static struct class_device *firstdev_class_dev;//類下面定義乙個裝置
在first_dev_init函式中新增:
firstdev_class = class_create(this_module, "firstdev");
if(is_err(firstdev_class))
return ptr_err(firstdev_class);
firstdev_class_dev = class_device_create(firstdev_class, null, mkdev(major, 0), null, "xyz");
if(unlikely(is_err(firstdev_class_dev))
return ptr_err(firstdev_class_dev);
新增解除安裝資訊(在first_dev_exit中):
class_device_unregister(firstdev_class_dev);
class_destroy(firstdev_class);
注:這時候載入驅動可能會提醒出錯。這個問題的原因是沒有給**授權,在程式末尾新增:
module_license("gpl");
解析:這樣修改之後,在載入驅動的時候,mdev就會自動根據資訊建立出對應的裝置節點,可以通過:
# ls -l /dev/xyz
這個命令來檢視,同時也可以檢視裝置:
# cat /proc/devices
在系統目錄的class目錄下有各種類,驅動載入時建立的類也在其中。
# cd class/
# cd firstdev/
# cd xyz/
# cat dev
能夠自動生成裝置節點,還依賴於指令碼檔案中的語句:
echo /sbin/mdev > /porc/sys/kernel/hotplug
核心一旦有裝置載入或者解除安裝,那麼就會根據/porc/sys/kernel/hotplug這個檔案所指示的應用程式去處理。
11.led驅動與裸機驅動的區別
裸機程式直接操作實體地址,而驅動中操作的是虛擬位址。
12.實際位址和虛擬位址的對映
volatile unsigned long *gpfcon = null;
volatile unsigned long *gpfdat = null;
虛擬位址的對映(建議放在first_dev_init中):
gpfcon = (volatile unsigned long *)ioremap(real_address, size);
虛擬位址去對映(建議放在first_dev_exit中):
iounmap(gpfcon);
經過對映之後,操作實體地址就可以使用類似:
*gpfcon = val;
這種方式來往對應的實體地址寫值。
13.核心空間和使用者空間資料傳輸
copy_from_user();
copy_to_user();
14.次裝置號的使用
可以在open函式中使用:
int minor = minor(inode->i_rdev);
取出裝置的次裝置號,然後在接下來根據次裝置號的不同設定很多種操作模式。次裝置號完全是驅動程式來指定,要代表的意義由程式設計師控制。
15.使用系統提供的io操作函式
s3c2410_gpio_cfgpin(s3c2410_gpf4, s3c2410_gpf4_outp);
s3c2410_gpio_setpin(s3c2410_gpf4, 0);
乙個字元驅動
實現乙個基本框架 define notice fmt,args.printk kern notice scull fmt,args define error fmt,args.printk kern err scull fmt,args static init int scull init void...
我的第乙個字元裝置驅動程式 First drv
驅動程式 include include include include include include include include include include module license gpl static struct class firstdrv class static stru...
簡單的乙個字元裝置驅動
include include include include include include include include include include include include 包含記憶體管理兩個核心函式 include memdev.h static int mem major me...