第一:自動創(chuàng)建設(shè)備節(jié)點(diǎn)方法
1、使用 class_create 函數(shù)創(chuàng)建一個類。
2、使用 device_create 函數(shù)在我們創(chuàng)建的類下面創(chuàng)建一個設(shè)備。
第二:自動創(chuàng)建設(shè)備節(jié)點(diǎn)簡介
Linux驅(qū)動實(shí)驗(yàn)中,通過使用insmod命令加載模塊后,需要通過mknod命令手動創(chuàng)建設(shè)備節(jié)點(diǎn),這樣使用起來比較麻煩,并且不可能每個設(shè)備都這樣操作, Linux 系統(tǒng)的存在就是為了方便使用, 所以我們來看一下如何實(shí)現(xiàn)自動創(chuàng)建設(shè)備節(jié)點(diǎn), 當(dāng)加載模塊時, 在/dev 目錄下自動創(chuàng)建相應(yīng)的設(shè)備文件。怎么自動創(chuàng)建一個設(shè)備節(jié)點(diǎn)呢?在嵌入式 Linux 中使用 mdev 來實(shí)現(xiàn)設(shè)備節(jié)點(diǎn)文件的自動創(chuàng)建和刪除。
udev 是一種工具, 它能夠根據(jù)系統(tǒng)中的硬件設(shè)備的狀態(tài)動態(tài)更新設(shè)備文件, 包括設(shè)備文件的創(chuàng)建, 刪除等。設(shè)備文件通常放在/dev 目錄下。使用 udev 后, 在/dev 目錄下就只包含系統(tǒng)中真正存在的設(shè)備。而mdev 是 udev 的簡化版本,是 busybox 中所帶的程序,最適合用在嵌入式系統(tǒng),而 udev 一般用在 PC 上的linux 中,相對 mdev 來說要復(fù)雜些, 所以在嵌入式 Linux 中使用 mdev 來實(shí)現(xiàn)設(shè)備節(jié)點(diǎn)文件的自動創(chuàng)建和刪除。
第三:創(chuàng)建和刪除類函數(shù)
內(nèi)核中定義了struct class結(jié)構(gòu)體,一個struct class結(jié)構(gòu)體類型變量對應(yīng)一個類,內(nèi)核同時提供了class_create用來創(chuàng)建一個類,這個類存放于 sysfs 下面, 一旦創(chuàng)建好了這個類, 再調(diào)用 device_create來在/dev 目錄下創(chuàng)建相應(yīng)的設(shè)備節(jié)點(diǎn)。這樣, 加載模塊的時候, 用戶空間中的 udev 會自動響應(yīng) device_create,去/sysfs 下尋找對應(yīng)的類從而創(chuàng)建設(shè)備節(jié)點(diǎn)。
在 Linux 驅(qū)動程序中一般通過 class_create 和 class_destroy 來完成設(shè)備節(jié)點(diǎn)的創(chuàng)建和刪除。首先要創(chuàng)建一個 class 類結(jié)構(gòu)體, class 結(jié)構(gòu)體定義在 include/linux/device.h 里面。class_create 是個宏, 宏定義如下:#define class_create(owner, name)
({
static struct lock_class_key __key;
__class_create(owner, name, &__key);
})
struct class *__class_create(struct module *owner, const char *name,struct lock_class_key *key)
class_create一共有兩個參數(shù),參數(shù)owner 一般為 THIS_MODULE, 參數(shù) name 是類名字。返回值是個指向結(jié)構(gòu)體 class 的指針, 也就是創(chuàng)建的類。
void class_destroy(struct class *cls);//參數(shù) cls 就是要刪除的類。
當(dāng)使用上節(jié)的函數(shù)創(chuàng)建完成一個類后,使用device_create 函數(shù)在這個類下創(chuàng)建一個設(shè)備。
struct device *device_create(struct class *class,
struct device *parent,
dev_t devt,
void *drvdata,
const char *fmt, ...)
device_create是個可變參數(shù)函數(shù),參數(shù)class就是設(shè)備要創(chuàng)建哪個類下面;參數(shù)parent是父設(shè)備,一般為NULL,也就是沒有父設(shè)備;參數(shù)devt是設(shè)備號;參數(shù)drvdata是設(shè)備可能會使用的一些數(shù)據(jù),一般為NULL;參數(shù)fmt是設(shè)備名字,如果設(shè)置fmt=xxx的話,就會生成/dev/xxx這個設(shè)備文件。返回值就是創(chuàng)建好的設(shè)備。同樣的, 卸載驅(qū)動的時候需要刪除掉創(chuàng)建的設(shè)備, 設(shè)備刪除函數(shù)為 device_destroy, 函數(shù)原型如下:
void device_destroy(struct class *class, dev_t devt)
第五:創(chuàng)建類函數(shù)
chrdev.c文件完整代碼如下所示:
//包含了 cdev 結(jié)構(gòu)及相關(guān)函數(shù)的定義。
static int major_num, minor_num; //定義主設(shè)備號和次設(shè)備號
struct class *class; //定義類
struct cdev cdev;//定義一個 cdev 結(jié)構(gòu)體
module_param(major_num, int, S_IRUSR); //驅(qū)動模塊傳入普通參數(shù) major_num
module_param(minor_num, int, S_IRUSR); //驅(qū)動模塊傳入普通參數(shù) minor_num
dev_t dev_num;
int chrdev_open(struct inode *inode, struct file *file)
{
printk("chrdev_open
");
return 0;
}
struct file_operations chrdev_ops = {
.owner = THIS_MODULE,
.open = chrdev_open
};
static int hello_init(void)
{
int ret; //函數(shù)返回值
if (major_num)
{
/*靜態(tài)注冊設(shè)備號*/
printk("major_num = %d
", major_num); //打印傳入進(jìn)來的主設(shè)備號
printk("minor_num = %d
", minor_num); //打印傳入進(jìn)來的次設(shè)備號
//MKDEV 將主設(shè)備號和次設(shè)備號合并為一個設(shè)備號
dev_num = MKDEV(major_num, minor_num);
ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME); //注冊設(shè)備號
if (ret < 0)
{
printk("register_chrdev_region error
");
}
printk("register_chrdev_region ok
"); //靜態(tài)注冊設(shè)備號成功
}
else
{
/*動態(tài)注冊設(shè)備號*/
ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, 1, DEVICE_ANAME);
if (ret < 0)
{
printk("alloc_chrdev_region error
");
}
printk("alloc_chrdev_region ok
"); //動態(tài)注冊設(shè)備號成功
major_num = MAJOR(dev_num); //將主設(shè)備號取出來
minor_num = MINOR(dev_num); //將次設(shè)備號取出來
printk("major_num = %d
", major_num); //打印傳入進(jìn)來的主設(shè)備號
printk("minor_num = %d
", minor_num); //打印傳入進(jìn)來的次設(shè)備號
}
cdev.owner = THIS_MODULE;
//cdev_init 函數(shù)初始化 cdev 結(jié)構(gòu)體成員變量
cdev_init(&cdev, &chrdev_ops);
//完成字符設(shè)備注冊到內(nèi)核
cdev_add(&cdev, dev_num, DEVICE_NUMBER);
//創(chuàng)建類
class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
return 0;
}
static void hello_exit(void)
{
unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);
//注銷設(shè)備號
cdev_del(&cdev);
//刪除類
class_destroy(class);
printk("gooodbye!
");
}
module_init(hello_init);
module_exit(hello_exit);
MODULE_LICENSE("GPL");
將代碼編譯成模塊,利用驅(qū)動程序里面的Makefile文件。
編譯并加載,如果創(chuàng)建類成功以后,會在開發(fā)板的/sys/class/下面生成一個名為“chrdev_class”的類?,F(xiàn)在沒有加載驅(qū)動的情況,如下圖所示:ls /sys/class
第六:創(chuàng)建設(shè)備函數(shù)
在前面代碼的基礎(chǔ)上添加創(chuàng)建設(shè)備的代碼,如下所示:
//包含了 cdev 結(jié)構(gòu)及相關(guān)函數(shù)的定義。
static int major_num, minor_num; //定義主設(shè)備號和次設(shè)備號
struct class *class; /* 類 */
struct device *device; /* 設(shè)備 */
struct cdev cdev; //定義一個 cdev 結(jié)構(gòu)體
module_param(major_num, int, S_IRUSR); //驅(qū)動模塊傳入普通參數(shù) major_num
module_param(minor_num, int, S_IRUSR); //驅(qū)動模塊傳入普通參數(shù) minor_num
dev_t dev_num; /* 設(shè)備號 */
/***
* @description: 打開設(shè)備
* @param {structinode} *inode:傳遞給驅(qū)動的 inode
* @param {structfile} *file:設(shè)備文件, file 結(jié)構(gòu)體有個叫做 private_data 的成員變量,
* 一般在 open 的時候?qū)?private_data 指向設(shè)備結(jié)構(gòu)體。
* @return: 0 成功;其他 失敗
*/
int chrdev_open(struct inode *inode, struct file *file)
{
printk("chrdev_open
");
return 0;
}
// 設(shè)備操作函數(shù)結(jié)構(gòu)體
struct file_operations chrdev_ops = {
.owner = THIS_MODULE,
.open = chrdev_open
};
/**
* @description: 驅(qū)動入口函數(shù)
* @param {*}無
* @return {*} 0 成功;其他 失敗
*/
static int hello_init(void)
{
int ret; //函數(shù)返回值
if (major_num)
{
/*靜態(tài)注冊設(shè)備號*/
printk("major_num = %d
", major_num); //打印傳入進(jìn)來的主設(shè)備號
printk("minor_num = %d
", minor_num); //打印傳入進(jìn)來的次設(shè)備號
dev_num = MKDEV(major_num, minor_num);
//MKDEV 將主設(shè)備號和次設(shè)備號合并為一個設(shè)備號
ret = register_chrdev_region(dev_num, DEVICE_NUMBER, DEVICE_SNAME); //注冊設(shè)備號
if (ret < 0)
{
printk("register_chrdev_region error
");
}
printk("register_chrdev_region ok
"); //靜態(tài)注冊設(shè)備號成功
}
else
{
/*動態(tài)注冊設(shè)備號*/
ret = alloc_chrdev_region(&dev_num, DEVICE_MINOR_NUMBER, 1, DEVICE_ANAME);
if (ret < 0)
{
printk("alloc_chrdev_region error
");
}
printk("alloc_chrdev_region ok
"); //動態(tài)注冊設(shè)備號成功
major_num = MAJOR(dev_num); //將主設(shè)備號取出來
minor_num = MINOR(dev_num); //將次設(shè)備號取出來
printk("major_num = %d
", major_num); //打印傳入進(jìn)來的主設(shè)備號
printk("minor_num = %d
", minor_num); //打印傳入進(jìn)來的次設(shè)備號
}
// 初始化 cdev
cdev.owner = THIS_MODULE;
cdev_init(&cdev, &chrdev_ops);
// 向系統(tǒng)注冊設(shè)備
cdev_add(&cdev, dev_num, DEVICE_NUMBER);
// 創(chuàng)建 class 類
class = class_create(THIS_MODULE, DEVICE_CLASS_NAME);
// 在 class 類下創(chuàng)建設(shè)備
device = device_create(class, NULL, dev_num, NULL, DEVICE_NODE_NAME);
return 0;
}
/**
* @description: 驅(qū)動出口函數(shù)
* @param {*}無
* @return {*}無
*/
static void hello_exit(void)
{
//注銷設(shè)備號
unregister_chrdev_region(MKDEV(major_num, minor_num), DEVICE_NUMBER);
//刪除設(shè)備
cdev_del(&cdev);
//注銷設(shè)備
device_destroy(class, dev_num);
//刪除類
class_destroy(class);
printk("gooodbye!
");
}
// 將上面兩個函數(shù)指定為驅(qū)動的入口和出口函數(shù)
module_init(hello_init);
module_exit(hello_exit);
// LICENSE 和作者信息
MODULE_LICENSE("GPL");
MODULE_AUTHOR("zuomu");
編寫應(yīng)用測試程序如下所示:
int main(int argc,char *argv[])
{
int fd;
char buf[64] = {0};
fd = open("/dev/chrdev_test",O_RDWR); //打開設(shè)備節(jié)點(diǎn)
if(fd < 0)
{
perror("open error
");
return fd;
}
close(fd);
return 0;
}
輸入命令編譯app.c ,利用驅(qū)動里面的Makefile文件實(shí)現(xiàn)。
第七:具體效果如下:
將前面加載的驅(qū)動卸載掉,再加載新編譯好的的驅(qū)動, 如下圖所示:
rmmod chrdev
insmod chrdev.ko
輸入以下命令查看/sys/class 下面是否生成類, 如下圖所示:ls /sys/class/chrdev_class/
輸入以下命令查看下是否生成了設(shè)備節(jié)點(diǎn)ls /dev/chrdev_test
總結(jié):利用標(biāo)準(zhǔn)字符驅(qū)動模型,自動生成設(shè)備節(jié)點(diǎn),在開發(fā)過程具有重要意義。
審核編輯:郭婷
-
嵌入式
+關(guān)注
關(guān)注
5046文章
18817瀏覽量
298538 -
Linux
+關(guān)注
關(guān)注
87文章
11123瀏覽量
207910
原文標(biāo)題:Linux系統(tǒng)下自動創(chuàng)建設(shè)備節(jié)點(diǎn)方法
文章出處:【微信號:嵌入式開發(fā)愛好者,微信公眾號:嵌入式開發(fā)愛好者】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。
發(fā)布評論請先 登錄
相關(guān)推薦
評論