字符設(shè)備驅(qū)動(dòng)開發(fā)的基本步驟可以看上一篇,本節(jié)就以 chrdevbase 這個(gè)虛擬設(shè)備為例,完整的編寫一個(gè)字符設(shè)備驅(qū)動(dòng)模塊。chrdevbase 不是實(shí)際存在的一個(gè)設(shè)備,方便講解字符設(shè)備的開發(fā)而引入的一個(gè)虛擬設(shè)備。chrdevbase 設(shè)備有兩個(gè)緩沖區(qū),一個(gè)為讀緩沖區(qū),一個(gè)為寫緩沖區(qū),這兩個(gè)緩沖區(qū)的大小都為 100 字節(jié)。在應(yīng)用程序中可以向 chrdevbase 設(shè)備的寫緩沖區(qū)中寫入數(shù)據(jù),從讀緩沖區(qū)中讀取數(shù)據(jù)。chrdevbase 這個(gè)虛擬設(shè)備的功能很簡(jiǎn)單,但是它包含了字符設(shè)備的最基本功能。
|需求目標(biāo)
應(yīng)用程序調(diào)用 open 函數(shù)打開 chrdevbase 這個(gè)設(shè)備,打開以后可以使用 write 函數(shù)向chrdevbase 的寫緩沖區(qū) writebuf 中寫入數(shù)據(jù)(不超過 100 個(gè)字節(jié)),也可以使用 read 函數(shù)讀取讀緩沖區(qū) readbuf 中的數(shù)據(jù)操作,操作完成以后應(yīng)用程序使用 close 函數(shù)關(guān)閉 chrdevbase 設(shè)備。
|實(shí)現(xiàn)過程
1.新建文件一個(gè)nxp文件夾,然后把原廠內(nèi)核文件復(fù)制過來

2、創(chuàng)建Linux_Drivers用于存放驅(qū)動(dòng)文件,創(chuàng)建01_chrdevbase用于存放chrdevbase實(shí)驗(yàn)文件,chrdevbase.c是底層驅(qū)動(dòng)代碼,chrdevbaseApp.c是應(yīng)用代碼,Makefile編譯底層驅(qū)動(dòng);

3、編寫代碼
chrdevbase.c文件
#include#include #include #include #include #include #define CHRDEVBASE_MAJOR 200 /* 主設(shè)備號(hào) */ #define CHRDEVBASE_NAME "chrdevbase" /* 設(shè)備名 */ static char readbuf[100]; /* 讀緩沖區(qū) */ static char writebuf[100]; /* 寫緩沖區(qū) */ static char kerneldata[] = {"kernel data!"}; /* * @description : 打開設(shè)備 * @param - inode : 傳遞給驅(qū)動(dòng)的inode * @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個(gè)叫做private_data的成員變量 * 一般在open的時(shí)候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。 * @return : 0 成功;其他 失敗 */ static int chrdevbase_open(struct inode *inode, struct file *filp) { //printk("chrdevbase open! "); return 0; } /* * @description : 從設(shè)備讀取數(shù)據(jù) * @param - filp : 要打開的設(shè)備文件(文件描述符) * @param - buf : 返回給用戶空間的數(shù)據(jù)緩沖區(qū) * @param - cnt : 要讀取的數(shù)據(jù)長(zhǎng)度 * @param - offt : 相對(duì)于文件首地址的偏移 * @return : 讀取的字節(jié)數(shù),如果為負(fù)值,表示讀取失敗 */ static ssize_t chrdevbase_read(struct file *filp, char __user *buf, size_t cnt, loff_t *offt) { int retvalue = 0; /* 向用戶空間發(fā)送數(shù)據(jù) */ memcpy(readbuf, kerneldata, sizeof(kerneldata)); retvalue = copy_to_user(buf, readbuf, cnt); if(retvalue == 0){ printk("kernel senddata ok! "); }else{ printk("kernel senddata failed! "); } //printk("chrdevbase read! "); return 0; } /* * @description : 向設(shè)備寫數(shù)據(jù) * @param - filp : 設(shè)備文件,表示打開的文件描述符 * @param - buf : 要寫給設(shè)備寫入的數(shù)據(jù) * @param - cnt : 要寫入的數(shù)據(jù)長(zhǎng)度 * @param - offt : 相對(duì)于文件首地址的偏移 * @return : 寫入的字節(jié)數(shù),如果為負(fù)值,表示寫入失敗 */ static ssize_t chrdevbase_write(struct file *filp, const char __user *buf, size_t cnt, loff_t *offt) { int retvalue = 0; /* 接收用戶空間傳遞給內(nèi)核的數(shù)據(jù)并且打印出來 */ retvalue = copy_from_user(writebuf, buf, cnt); if(retvalue == 0){ printk("kernel recevdata:%s ", writebuf); }else{ printk("kernel recevdata failed! "); } //printk("chrdevbase write! "); return 0; } /* * @description : 關(guān)閉/釋放設(shè)備 * @param - filp : 要關(guān)閉的設(shè)備文件(文件描述符) * @return : 0 成功;其他 失敗 */ static int chrdevbase_release(struct inode *inode, struct file *filp) { //printk("chrdevbase release! "); return 0; } /* * 設(shè)備操作函數(shù)結(jié)構(gòu)體 */ static struct file_operations chrdevbase_fops = { .owner = THIS_MODULE, .open = chrdevbase_open, .read = chrdevbase_read, .write = chrdevbase_write, .release = chrdevbase_release, }; /* * @description : 驅(qū)動(dòng)入口函數(shù) * @param : 無 * @return : 0 成功;其他 失敗 */ static int __init chrdevbase_init(void) { int retvalue = 0; /* 注冊(cè)字符設(shè)備驅(qū)動(dòng) */ retvalue = register_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME, &chrdevbase_fops); if(retvalue < 0){ printk("chrdevbase driver register failed "); } printk("chrdevbase init! "); return 0; } /* * @description : 驅(qū)動(dòng)出口函數(shù) * @param : 無 * @return : 無 */ static void __exit chrdevbase_exit(void) { /* 注銷字符設(shè)備驅(qū)動(dòng) */ unregister_chrdev(CHRDEVBASE_MAJOR, CHRDEVBASE_NAME); printk("chrdevbase exit! "); } /* * 將上面兩個(gè)函數(shù)指定為驅(qū)動(dòng)的入口和出口函數(shù) */ module_init(chrdevbase_init); module_exit(chrdevbase_exit); /* * LICENSE和作者信息 */ MODULE_LICENSE("GPL"); MODULE_AUTHOR("zuozhongkai");
chrdevbaseApp.c文件
#include "stdio.h"
#include "unistd.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
#include "stdlib.h"
#include "string.h"
static char usrdata[] = {"usr data!"};
/*
* @description : main主程序
* @param - argc : argv數(shù)組元素個(gè)數(shù)
* @param - argv : 具體參數(shù)
* @return : 0 成功;其他 失敗
*/
int main(int argc, char *argv[])
{
int fd, retvalue;
char *filename;
char readbuf[100], writebuf[100];
if(argc != 3){
printf("Error Usage!
");
return -1;
}
filename = argv[1];
/* 打開驅(qū)動(dòng)文件 */
fd = open(filename, O_RDWR);
if(fd < 0){
printf("Can't open file %s
", filename);
return -1;
}
if(atoi(argv[2]) == 1){
/* 從驅(qū)動(dòng)文件讀取數(shù)據(jù) */
retvalue = read(fd, readbuf, 50);
if(retvalue < 0){
printf("read file %s failed!
", filename);
}else{
/* 讀取成功,打印出讀取成功的數(shù)據(jù) */
printf("read data:%s
",readbuf);
}
}
if(atoi(argv[2]) == 2){
/* 向設(shè)備驅(qū)動(dòng)寫數(shù)據(jù) */
memcpy(writebuf, usrdata, sizeof(usrdata));
retvalue = write(fd, writebuf, 50);
if(retvalue < 0){
printf("write file %s failed!
", filename);
}
}
/* 關(guān)閉設(shè)備 */
retvalue = close(fd);
if(retvalue < 0){
printf("Can't close file %s
", filename);
return -1;
}
return 0;
}
Makefile文件
KERNELDIR := /home/noah/linux/nxp/linux-imx-rel_imx_4.1.15_2.1.0_ga CURRENT_PATH := $(shell pwd) obj-m := chrdevbase.o build: kernel_modules kernel_modules: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules clean: $(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
相關(guān)解析:
第 1 行,KERNELDIR 表示開發(fā)板所使用的 Linux 內(nèi)核源碼目錄,使用絕對(duì)路徑,大家根據(jù)自己的實(shí)際情況填寫即可。 第2行,CURRENT_PATH表示當(dāng)前路徑,直接通過運(yùn)行“pwd”命令來獲取當(dāng)前所處路徑。 第 3 行,obj-m 表示將 chrdevbase.c 這個(gè)文件編譯為 chrdevbase.ko 模塊。 第 8 行,具體的編譯命令,后面的 modules 表示編譯模塊,-C 表示將當(dāng)前的工作目錄切換到指定目錄中,也就是 KERNERLDIR 目錄。M 表示模塊源碼目錄,“make modules”命令中加入 M=dir 以后程序會(huì)自動(dòng)到指定的 dir 目錄中讀取模塊的源碼并將其編譯為.ko 文件。
4、編譯驅(qū)動(dòng)
直接使用make命令會(huì)報(bào)錯(cuò)的,因?yàn)閗ernel中沒有指定編譯器和架構(gòu),使用了默認(rèn)的x86平臺(tái)編譯報(bào)錯(cuò)。

5、修改內(nèi)核的Makefile文件
直接定義ARCH和CROSS_COMPILE 這兩個(gè)的變量值為 arm 和 arm-linux-gnueabihf-


6、再次編譯驅(qū)動(dòng)
編譯通過,會(huì)生成不少編譯文件;

7、編譯應(yīng)用程序
應(yīng)用程序只有一個(gè)文件,在ubuntu對(duì)應(yīng)文件夾,直接輸入指令進(jìn)行編譯:
arm-linux-gnueabihf-gcc chrdevbaseApp.c -o chrdevbaseApp
8、SD卡啟動(dòng)系統(tǒng)
插入SD卡,把撥碼開關(guān)調(diào)到SD卡啟動(dòng),然后ping一下網(wǎng)關(guān),確認(rèn)網(wǎng)絡(luò)通暢才能繼續(xù)的進(jìn)行,如果ping不通就請(qǐng)看看前幾節(jié);

9、啟動(dòng)內(nèi)核
在uboot界面輸入下面指令啟動(dòng)系統(tǒng),
tftp80800000zImage tftp 83000000 imx6ull-14x14-evk.dtb bootz 80800000 - 83000000
10、創(chuàng)建目錄并復(fù)制驅(qū)動(dòng)文件
檢查開發(fā)板根文件系統(tǒng)中有沒有“/lib/modules/4.1.15”這個(gè)目錄,如果沒有的話自行創(chuàng)建。注意,“/lib/modules/4.1.15”這個(gè)目錄用來存放驅(qū)動(dòng)模塊,使用modprobe 命令加載驅(qū)動(dòng)模塊的時(shí)候,驅(qū)動(dòng)模塊要存放在此目錄下?!?lib/modules”是通用的,不管你用的什么板子、什么內(nèi)核,這部分是一樣的。
將 chrdevbase.ko 和 chrdevbaseAPP 復(fù)制到 rootfs/lib/modules/4.1.15 目錄中:

11、加載設(shè)備驅(qū)動(dòng)
自制的根文件系統(tǒng),有些命令是不支持的;
//加載驅(qū)動(dòng) insmodchrdevbase.ko // 查看驅(qū)動(dòng) lsmod // 指令查看devices信息 cat/proc/devices
效果如下圖:

12、創(chuàng)建設(shè)備節(jié)點(diǎn)文件
驅(qū)動(dòng)加載成功需要在/dev 目錄下創(chuàng)建一個(gè)與之對(duì)應(yīng)的設(shè)備節(jié)點(diǎn)文件,應(yīng)用程序就是通過操作這個(gè)設(shè)備節(jié)點(diǎn)文件來完成對(duì)具體設(shè)備的操作。輸入如下命令創(chuàng)建/dev/chrdevbase 這個(gè)設(shè)備節(jié)點(diǎn)文件:
mknod /dev/chrdevbase c 200 0其中“mknod”是創(chuàng)建節(jié)點(diǎn)命令,“/dev/chrdevbase”是要?jiǎng)?chuàng)建的節(jié)點(diǎn)文件,“c”表示這是個(gè)字符設(shè)備,“200”是設(shè)備的主設(shè)備號(hào),“0”是設(shè)備的次設(shè)備號(hào)。創(chuàng)建完成以后就會(huì)存在/dev/chrdevbase 這個(gè)文件,可以使用“l(fā)s /dev/chrdevbase -l”命令查看,結(jié)果如圖:

13、驗(yàn)證讀寫
使用 chrdevbaseApp 軟件操作 chrdevbase 這個(gè)設(shè)備,看看讀寫是否正常,首先進(jìn)行讀操作,輸入如下命令:
//讀 ./chrdevbaseApp /dev/chrdevbase 1 // 寫 ./chrdevbaseApp /dev/chrdevbase 2
相關(guān)解析:
三個(gè)參數(shù)“./chrdevbaseApp”、“/dev/chrdevbase”和“1”,這三個(gè)參數(shù)分別對(duì)應(yīng) argv[0]、argv[1]和 argv[2]。
第一個(gè)參數(shù)表示運(yùn)行 chrdevbaseAPP 這個(gè)軟件,
第二個(gè)參數(shù)表示測(cè)試APP要打開/dev/chrdevbase這個(gè)設(shè)備。
第三個(gè)參數(shù)就是要執(zhí)行的操作,1表示從chrdevbase中讀取數(shù)據(jù),2 表示向 chrdevbase 寫數(shù)據(jù)。

效果如下:

14、卸載驅(qū)動(dòng)模塊
如果不再使用某個(gè)設(shè)備的話可以將其驅(qū)動(dòng)卸載掉,命令如下:
// 卸載chrdevbase.ko rmmod chrdevbase.ko // 查看驅(qū)動(dòng) lsmod
至此,chrdevbase 這個(gè)設(shè)備的整個(gè)驅(qū)動(dòng)就驗(yàn)證完成了,驅(qū)動(dòng)工作正常。
審核編輯:湯梓紅
-
Linux
+關(guān)注
關(guān)注
88文章
11758瀏覽量
219009 -
函數(shù)
+關(guān)注
關(guān)注
3文章
4417瀏覽量
67502 -
驅(qū)動(dòng)開發(fā)
+關(guān)注
關(guān)注
0文章
140瀏覽量
12638
原文標(biāo)題:i.MX6ULL|字符設(shè)備驅(qū)動(dòng)開發(fā)實(shí)踐
文章出處:【微信號(hào):玩轉(zhuǎn)單片機(jī),微信公眾號(hào):玩轉(zhuǎn)單片機(jī)】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
i.MX6ULL 驅(qū)動(dòng)開發(fā)7—按鍵輸入捕獲與GPIO輸入配置與高低電平讀取
i.MX6ULL嵌入式Linux開發(fā)1-uboot移植初探
使用i.MX6ULL開發(fā)板進(jìn)行Linux根文件系統(tǒng)的完善
移植NXP官方linux 5.4內(nèi)核到i.MX6ULL開發(fā)板
如何在i.MX6ULL睡眠時(shí)停止刷新LCD?
I.MX6ULL終結(jié)者開發(fā)板裸機(jī)仿真jlink調(diào)試
i.MX6ULL開發(fā)板硬件資源
初識(shí) i.MX6ULL 寄存器
I.MX6ULL無法枚舉USB2514是為什么?
飛凌i.MX6ULL開發(fā)板的評(píng)測(cè),再次進(jìn)階擁有更高的性價(jià)比
基于NXP i.MX6ULL處理器的FETMX6ULL-C核心板
【i.MX6ULL】驅(qū)動(dòng)開發(fā)4——點(diǎn)亮LED(寄存器版)
基于i.MX6ULL的掉電檢測(cè)設(shè)計(jì)與軟件測(cè)試
i.MX6ULL|字符設(shè)備驅(qū)動(dòng)開發(fā)實(shí)踐
評(píng)論