• 正文
    • 在mydevice.c源碼的基礎(chǔ)上進行添加,重命名為mydevice-auto.c
    • mydevice-auto.c完整示例源碼
    • 編譯
    • 編寫測試應(yīng)用源碼device-auto_app.c
    • 測試
  • 相關(guān)推薦
申請入駐 產(chǎn)業(yè)圖譜

飛凌嵌入式ElfBoard ELF 1板卡-字符驅(qū)動之自動創(chuàng)建節(jié)點

03/18 09:50
512
加入交流群
掃碼加入
獲取工程師必備禮包
參與熱點資訊討論

例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅(qū)動例程源碼2_字符驅(qū)動mydevice-auto

上一節(jié)寫的驅(qū)動加載后,需要手動使用mknod命令在/dev下創(chuàng)建設(shè)備節(jié)點,本節(jié)講解如何讓驅(qū)動自動在/dev目錄下生成設(shè)備節(jié)點。

想要實現(xiàn)自動創(chuàng)建節(jié)點,首先需要創(chuàng)建設(shè)備類,然后創(chuàng)建設(shè)備節(jié)點并關(guān)聯(lián)到設(shè)備類。

mydevice.c源碼的基礎(chǔ)上進行添加,重命名為mydevice-auto.c

(一)添加頭文件

#include <linux/device.h>

(二)聲明變量

static struct class *my_class;

static struct device *my_device;

(三)在mydevice_init(void)函數(shù)中創(chuàng)建類和設(shè)備節(jié)點

static int __init mydevice_init(void)

{

int ret;

// 在這里執(zhí)行驅(qū)動程序的初始化操作

// 注冊字符設(shè)備驅(qū)動程序

ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

if (ret < 0) {

printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

return ret;

}

major=MAJOR(dev_num); ??//獲取主設(shè)備號

minor=MINOR(dev_num); ??//獲取次設(shè)備號

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

my_cdev.owner = THIS_MODULE;

cdev_init(&my_cdev,&fops); ??//初始化字符設(shè)備結(jié)構(gòu)體

cdev_add(&my_cdev,dev_num,1); ??//將字符設(shè)備添加到內(nèi)核中

// 創(chuàng)建設(shè)備類

my_class = class_create(THIS_MODULE, "my_class");

if (IS_ERR(my_class)) {

pr_err("Failed to create classn");

return PTR_ERR(my_class);

}

// 創(chuàng)建設(shè)備節(jié)點并關(guān)聯(lián)到設(shè)備類

my_device = device_create(my_class, NULL, MKDEV(major, minor), NULL, DEVICE_NAME);

if (IS_ERR(my_device)) {

pr_err("Failed to create devicen");

class_destroy(my_class);

return PTR_ERR(my_device);

}

printk(KERN_INFO "Device registered successfully.n");

return 0;

}

class_create()函數(shù)的原型:

struct class *class_create(struct module *owner, const char *name);

owner:指向擁有該設(shè)備類的內(nèi)核模塊的指針。通常使用THIS_MODULE來指定當前驅(qū)動模塊作為擁有者;

name:指定設(shè)備類的名稱。該名稱將用于在sysfs文件系統(tǒng)中創(chuàng)建對應(yīng)的類目錄;

class_create()函數(shù)返回一個指向新創(chuàng)建的設(shè)備類的struct class指針。如果創(chuàng)建失敗,它將返回一個指向NULL的指針;

設(shè)備類的創(chuàng)建是設(shè)備驅(qū)動程序中重要的一步,它為設(shè)備提供了一個統(tǒng)一的命名空間和屬性集合,并將相關(guān)設(shè)備節(jié)點組織在一起。創(chuàng)建設(shè)備類后,驅(qū)動程序可以使用device_create()函數(shù)創(chuàng)建設(shè)備節(jié)點,并將其關(guān)聯(lián)到該設(shè)備類下。

device_create()函數(shù)的原型:

struct device *device_create(struct class *class, struct device *parent,

dev_t dev, void *drvdata, const char *fmt, ...);

class:指向設(shè)備節(jié)點所屬的設(shè)備類的struct class指針;

parent:指向設(shè)備節(jié)點的父設(shè)備的struct device指針,如果沒有父設(shè)備,則可以設(shè)置為NULL;

dev:指定設(shè)備節(jié)點的設(shè)備號,使用MKDEV(major, minor)來創(chuàng)建;

drvdata:指向與設(shè)備節(jié)點相關(guān)的私有數(shù)據(jù)的指針,可以將驅(qū)動程序的特定數(shù)據(jù)與設(shè)備節(jié)點關(guān)聯(lián)起來;

fmt:設(shè)備節(jié)點名稱的格式字符串,用于在sysfs文件系統(tǒng)中創(chuàng)建設(shè)備節(jié)點??梢允褂妙愃?mydevice%d"的格式,并使用可變參數(shù)來指定設(shè)備節(jié)點名稱的后綴;

device_create()函數(shù)返回一個指向新創(chuàng)建的設(shè)備節(jié)點的struct device指針。如果創(chuàng)建失敗,它將返回一個指向NULL的指針;

設(shè)備節(jié)點的創(chuàng)建需要先創(chuàng)建一個設(shè)備類(使用class_create()函數(shù)),然后使用device_create()函數(shù)創(chuàng)建設(shè)備節(jié)點并將其關(guān)聯(lián)到設(shè)備類下;

(四)mydevice_exit(void)函數(shù)中添加銷毀設(shè)備節(jié)點和設(shè)備類

static void __exit mydevice_exit(void)

{

// 在這里執(zhí)行驅(qū)動程序的清理操作

// 銷毀設(shè)備節(jié)點

device_destroy(my_class, MKDEV(major, minor));

// 銷毀設(shè)備類

class_destroy(my_class);

// 刪除字符設(shè)備

cdev_del(&my_cdev);

// 注銷字符設(shè)備驅(qū)動程序

unregister_chrdev(0, DEVICE_NAME);

printk(KERN_INFO "Device unregistered.n");

}

mydevice-auto.c完整示例源碼

#include <linux/module.h> ??????// 包含模塊相關(guān)函數(shù)的頭文件

#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關(guān)函數(shù)的頭文件

#include <linux/uaccess.h> ?????// 包含用戶空間數(shù)據(jù)訪問函數(shù)的頭文件

#include <linux/cdev.h> ????????//包含字符設(shè)備頭文件

#include <linux/device.h>

#define DEVICE_NAME "mydevice" ?// 設(shè)備名稱

static dev_t dev_num; ??//分配的設(shè)備號

struct ?cdev my_cdev; ?????????//字符設(shè)備指針

int major; ?//主設(shè)備號

int minor; ?//次設(shè)備號

static struct class *my_class;

static struct device *my_device;

static int device_open(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備打開的操作

printk(KERN_INFO "This is device_open.n");

return 0;

}

static int device_release(struct inode *inode, struct file *file)

{

// 在這里處理設(shè)備關(guān)閉的操作

printk(KERN_INFO "This is device_release.n");

return 0;

}

static ssize_t device_read(struct file *file, char __user *buffer, size_t length, loff_t *offset)

{

// 在這里處理設(shè)備讀取的操作

printk(KERN_INFO "This is device_read.n");

return 0;

}

static ssize_t device_write(struct file *file, const char __user *buffer, size_t length, loff_t *offset)

{

// 在這里處理設(shè)備寫入的操作

printk(KERN_INFO "This is device_write.n");

return 0;

}

static struct file_operations fops = {

.owner = THIS_MODULE,

.open = device_open,

.release = device_release,

.read = device_read,

.write = device_write,

};

static int __init mydevice_init(void)

{

int ret;

// 在這里執(zhí)行驅(qū)動程序的初始化操作

// 注冊字符設(shè)備驅(qū)動程序

ret = alloc_chrdev_region(&dev_num,0,1,DEVICE_NAME);

if (ret < 0) {

printk(KERN_ALERT "Failed to allocate device number: %dn", ret);

return ret;

}

major=MAJOR(dev_num); ??//獲取主設(shè)備號

minor=MINOR(dev_num); ??//獲取次設(shè)備號

printk(KERN_INFO "major number: %dn",major);

printk(KERN_INFO "minor number: %dn",minor);

my_cdev.owner = THIS_MODULE;

cdev_init(&my_cdev,&fops); ??//初始化字符設(shè)備結(jié)構(gòu)體

cdev_add(&my_cdev,dev_num,1); ??//將字符設(shè)備添加到內(nèi)核中

// 創(chuàng)建設(shè)備類

my_class = class_create(THIS_MODULE, "my_class");

if (IS_ERR(my_class)) {

pr_err("Failed to create classn");

return PTR_ERR(my_class);

}

// 創(chuàng)建設(shè)備節(jié)點并關(guān)聯(lián)到設(shè)備類

my_device = device_create(my_class, NULL, MKDEV(major, minor), NULL, DEVICE_NAME);

if (IS_ERR(my_device)) {

pr_err("Failed to create devicen");

class_destroy(my_class);

return PTR_ERR(my_device);

}

printk(KERN_INFO "Device registered successfully.n");

return 0;

}

static void __exit mydevice_exit(void)

{

// 在這里執(zhí)行驅(qū)動程序的清理操作

// 銷毀設(shè)備節(jié)點

device_destroy(my_class, MKDEV(major, minor));

// 銷毀設(shè)備類

class_destroy(my_class);

// 刪除字符設(shè)備

cdev_del(&my_cdev);

// 注銷字符設(shè)備驅(qū)動程序

unregister_chrdev(0, DEVICE_NAME);

printk(KERN_INFO "Device unregistered.n");

}

module_init(mydevice_init);

module_exit(mydevice_exit);

MODULE_LICENSE("GPL"); ?????// 指定模塊的許可證信息

MODULE_AUTHOR("Your Name"); // 指定模塊的作者信息

MODULE_DESCRIPTION("A simple character device driver"); // 指定模塊的描述信息

編譯

Makefile文件如下:

?. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

elf@ubuntu:~/work/test/02_字符驅(qū)動/mydevice-auto$ make

將生成的mydevice.ko文件拷貝到開發(fā)板中。

編寫測試應(yīng)用源碼device-auto_app.c

驅(qū)動中已經(jīng)給應(yīng)用層提供了open、read、write的接口,接下來應(yīng)用層就可以進行相應(yīng)的系統(tǒng)調(diào)用。

#include <stdio.h>

#include <stdlib.h>

#include <unistd.h>

#include <sys/ioctl.h>

#include <errno.h>

#include <fcntl.h>

#define DEV_NAME "/dev/my_device"

int main(int argc, char *argv[])

{

int reg;

int fd = 0;

int dat = 0;

fd = open (DEV_NAME, O_RDWR);

if (fd < 0) {

perror("Open "DEV_NAME" Failed!n");

exit(1);

}

reg = read(fd, &dat, 1);

if (reg<0) {

perror("read "DEV_NAME" Failed!n");

exit(1);

}

dat = 0;

reg = write(fd, &dat, 1);

if (reg<0) {

perror("write "DEV_NAME" Failed!n");

exit(1);

}

close(fd);

return 0;

}

設(shè)置環(huán)境變量:

?. /opt/fsl-imx-x11/4.1.15-2.0.0/environment-setup-cortexa7hf-neon-poky-linux-gnueabi

編譯,將生成的device-auto_app文件拷貝到開發(fā)板中:

elf@ubuntu:~/work/test/02_字符驅(qū)動/device-auto_app$ $CC device-auto_app.c -o device-auto_app

測試

root@ELF1:~# insmod mydevice-auto.ko

major number: 245

minor number: 0

Device registered successfully.

root@ELF1:~# ls /dev/my_device

/dev/my_device

root@ELF1:~#?./device-auto_app

This is device_open.

This is device_read.

This is device_write.

This is device_release.

root@ELF1:~# rmmod mydevice-auto.ko

Device unregistered.

可以看出在使用insmod加載驅(qū)動后,在/dev下就生成了my_device節(jié)點,使用device_app測試正常。

相關(guān)推薦