Linux字符設備驅動開發模板中介紹了舊版本的驅動開發模板,其需要手動分配設備號后,再進行注冊,驅動加載成功后還需要手動創建設備節點,比較麻煩。 目前Linux內核推薦的新字符設備驅動API函數,可以自動分配設備號、創建設備節點,使得驅動的使用更加方便
1. 新字符設備驅動原理
1.1 分配和釋放設備號
舊字符設備驅動開發中使用register_chrdev函數注冊字符設備時,需要事先確定好主設備號,并且注冊成功后,會將該設備號下的所有次設備號都使用掉
而新字符設備驅動API函數很好的解決了這個問題,使用設備號時再向內核申請,需要幾個就申請幾個,由內核分配設備可以使用的設備號
設備號申請函數:沒有指定設備號
int alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)
//dev:保存申請到的設備號
//baseminor:次設備號起始地址,一般為0
//count:要申請的設備號數量
//name:設備名字
設備號申請函數:指定了主次設備號
int register_chrdev_region(dev_t from, unsigned count, const char *name)
//from:要申請的起始設備號
//count:要申請的設備號數量
//name:設備名字
設備號釋放函數:統一使用下面函數釋放
void unregister_chrdev_region(dev_t from, unsigned count)
//from:要釋放的設備號
//count:表示從from開始,要釋放的設備號數量
因此新字符設備驅動中,設備號分配代碼通常按如下示例編寫:
int major; /* 主設備號 */
int minor; /* 次設備號 */
dev_t devid; /* 設備號 */
if (major) { /* 定義了主設備號 */
devid = MKDEV(major, 0); /*大部分驅動次設備號都選擇0*/
register_chrdev_region(devid, 1, "test");
} else { /* 沒有定義設備號 */
alloc_chrdev_region(&devid, 0, 1, "test"); /*申請設備號*/
major = MAJOR(devid); /* 獲取分配號的主設備號 */
minor = MINOR(devid); /* 獲取分配號的次設備號 */
}
1.2 注冊字符設備
Linux中使用cdev表示字符設備,在include/linux/cdev.h中定義:
struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops; //文件操作函數集合
struct list_head list;
dev_t dev; //設備號
unsigned int count;
};
//編寫字符設備驅動之前需要定義一個cdev結構體變量
cdev_init函數:定義好cdev變量后,用該函數進行初始化
cdev_add函數:向系統添加字符設備(cdev結構體變量)
struct cdev testcdev;
/* 設備操作函數 */
static struct file_operations test_fops = {
.owner = THIS_MODULE,
/* 其他具體的初始項 */
};
testcdev.owner = THIS_MODULE;
cdev_init(&testcdev, &test_fops); /* 初始化cdev結構體變量 */
cdev_add(&testcdev, devid, 1); /* 添加字符設備 */
cdev_del函數:卸載驅動時從內核中刪除相應的字符設備
void cdev_del(struct cdev *p)
//p:要刪除的字符設備
1.3 自動創建設備節點
舊字符設備驅動開發中,驅動程序加載成功后還需要使用mknod命令手動創建設備節點,十分麻煩。
而新字符設備驅動開發中,Linux通過udev用戶程序來實現設備文件的自動創建與刪除。 udev會檢測系統中硬件設備狀態,并根據硬件設備狀態來創建或者刪除設備文件。
使用busybox構建根文件系統時,busybox會創建一個udev的簡化版本mdev。 因此,在嵌入式開發中使用mdev來實現設備節點文件的自動創建與刪除。 Linux系統中的熱插拔事件也由mdev管理,在/etc/init.d/rcS文件中有如下語句:
echo /sbin/mdev > /proc/sys/kernel/hotplug
創建類:自動創建設備節點的工作是在驅動程序入口函數中完成的,一般在cdev_add之后添加相關代碼
struct class *class_create (struct module *owner, const char *name)
//owner 一般為 THIS_MODULE
//name 是類名字
//返回值是個指向結構體class的指針,也就是創建的類
刪除類:卸載驅動程序時需要刪除類
void class_destroy(struct class *cls)
//cls 就是要刪除的類
創建設備:類創建好后還不能實現自動創建設備節點,還需要在該類下創建一個設備
struct device *device_create(struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt, ...)
//class 設備創建在哪個類下
//parent 父設備,一般為NULL
//devt 設備號
//drvdata 設備可能會使用的數據,一般NULL
//fmt 設備名字
//返回值是創建好的設備
刪除設備:卸載驅動時需要刪除創建的設備
void device_destroy(struct class *class, dev_t devt)
//class 是要刪除的設備所處的類
//devt 是要刪除的設備號
1.4 設置文件私有數據
每個硬件設備都有一些屬性,比如主設備號、類、設備、開關狀態等等,在編寫驅動時可將這些屬性封裝成一個結構體。 并在編寫驅動open函數時將設備結構體作為私有數據添加到設備文件中:
/*設備結構體*/
struct test_dev{
dev_t devid; /*設備號*/
struct cdev cdev; /*cdev*/
struct class *class; /*類*/
struct device *device; /*設備*/
int major; /*主設備號*/
int minor; /*次設備號*/
};
struct test_dev testdev;
/*open函數*/
static int test_open(struct inode *inode, struct file *filp)
{
filp->private_data = &testdev; /*設置私有數據*/
return 0;
}
綜上所述,新字符設備驅動開發流程如下圖所示:
2. 新字符設備驅動開發實驗
新字符設備驅動開發實驗是在Linux字符設備驅動開發模板一文的基礎上進行修改,只更改了驅動的編寫方式,與應用程序無關,因此只修改驅動程序即可
2.1 驅動程序編寫
添加定義:宏及字符設備定義
#define CHRDEVBASE_CNT 1 //設備號個數
#define CHRDEVBASE_NAME "chrdevbase" //名字
/*newchr設備結構體 */
struct newchr_dev{
dev_t devid; //設備號
struct cdev cdev; //cdev
struct class *class; //類
struct device *device; //設備
int major; //主設備號
int minor; //次設備號
};
struct newchr_dev chrdevbase; //自定義字符設備
修改open函數:設置私有數據
static int chrdevbase_open(struct inode *inode, struct file *filp)
{
printk("chrdevbase open!\\r\\n");
filp->private_data = &chrdevbase; //設置私有數據
return 0;
}
修改init函數
static int __init chrdevbase_init(void)
{
/* 注冊字符設備驅動 */
//1、創建設備號
if (chrdevbase.major) /* 定義了設備號 */
{
chrdevbase.devid = MKDEV(chrdevbase.major, 0);
register_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT, CHRDEVBASE_NAME);
}
else /* 沒有定義設備號 */
{
alloc_chrdev_region(&chrdevbase.devid, 0, CHRDEVBASE_CNT, CHRDEVBASE_NAME); /* 申請設備號 */
chrdevbase.major = MAJOR(chrdevbase.devid); /* 獲取分配號的主設備號 */
chrdevbase.minor = MINOR(chrdevbase.devid); /* 獲取分配號的次設備號 */
}
printk("chrdevbase major=%d,minor=%d\\r\\n",chrdevbase.major, chrdevbase.minor);
//2、初始化cdev
chrdevbase.cdev.owner = THIS_MODULE;
cdev_init(&chrdevbase.cdev, &chrdevbase_fops);
//3、添加一個cdev
cdev_add(&chrdevbase.cdev, chrdevbase.devid, CHRDEVBASE_CNT);
//4、創建類
chrdevbase.class = class_create(THIS_MODULE, CHRDEVBASE_NAME);
if (IS_ERR(chrdevbase.class))
{
return PTR_ERR(chrdevbase.class);
}
//5、創建設備
chrdevbase.device = device_create(chrdevbase.class, NULL, chrdevbase.devid, NULL, CHRDEVBASE_NAME);
if (IS_ERR(chrdevbase.device))
{
return PTR_ERR(chrdevbase.device);
}
printk("chrdevbase init done!\\r\\n");
return 0;
}
修改exit函數
static void __exit chrdevbase_exit(void)
{
/* 注銷字符設備驅動 */
cdev_del(&chrdevbase.cdev);/* 刪除cdev */
unregister_chrdev_region(chrdevbase.devid, CHRDEVBASE_CNT); /* 注銷設備號 */
device_destroy(chrdevbase.class, chrdevbase.devid);
class_destroy(chrdevbase.class);
printk("chrdevbase exit done!\\r\\n");
}
2.2 程序編譯
程序編譯包括驅動程序和應用程序編譯兩個部分:
驅動程序編譯:創建Makefile文件,使用make命令,編譯驅動程序
KERNELDIR := /home/andyxi/linux/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_andyxi
CURRENT_PATH := $(shell pwd)
obj-m := newchrdev.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
應用程序編譯:無需內核參與,直接編譯即可
arm-linux-gnueabihf-gcc newchrdevApp.c -o newchrdevApp
2.3 運行測試
為了方便,選擇通過TFTP從網絡啟動,并使用NFS掛載網絡根文件系統。 確保開發板能正常啟動,在Ubuntu中將驅動和測試文件復制到modules/4.1.15目錄中
在開發板中輸入如下指令加載驅動模塊
depmod //第一次加載驅動的時候需要運行此命令
modprobe newchrdev.ko //加載驅動
驅動加載成功后,可以看到自動申請到的主設備號和次設備號
使用ls /dev/chrdevbase -l命令驗證該設備節點文件是否存在,而舊驅動方式需要額外使用mknod指令來手動創建該設備節點文件
驅動加載成功后,測試APP程序,如下
測試完使用rmmod指令卸載驅動
以上可見Linux新字符設備驅動開發方式可以自動分配設備號、創建設備節點,使得驅動的使用更加方便、便捷。
-
Linux
+關注
關注
87文章
11351瀏覽量
210498 -
API
+關注
關注
2文章
1518瀏覽量
62448 -
字符
+關注
關注
0文章
234瀏覽量
25266 -
函數
+關注
關注
3文章
4346瀏覽量
63012 -
驅動開發
+關注
關注
0文章
130瀏覽量
12120
發布評論請先 登錄
相關推薦
「正點原子Linux連載」第四十二章新字符設備驅動實驗
嵌入式Linux字符設備驅動的設計與應用
嵌入式Linux字符設備驅動的設計與應用
基于PXA255開發板外圍字符設備的嵌入式Linux字符設備驅動設計與應用
![基于PXA255<b class='flag-5'>開發</b>板外圍<b class='flag-5'>字符</b><b class='flag-5'>設備</b>的嵌入式<b class='flag-5'>Linux</b><b class='flag-5'>字符</b><b class='flag-5'>設備</b><b class='flag-5'>驅動</b>設計與應用](https://file.elecfans.com/web1/M00/5D/7A/pIYBAFt7dyuAIrviAABrjNONzPE062.png)
評論