例程代碼路徑:ELF 1開發(fā)板資料包3-例程源碼3-2 驅動例程源碼2_字符驅動mydevice-auto
上一節(jié)寫的驅動加載后,需要手動使用mknod命令在/dev下創(chuàng)建設備節(jié)點,本節(jié)講解如何讓驅動自動在/dev目錄下生成設備節(jié)點。
想要實現(xiàn)自動創(chuàng)建節(jié)點,首先需要創(chuàng)建設備類,然后創(chuàng)建設備節(jié)點并關聯(lián)到設備類。
在mydevice.c源碼的基礎上進行添加,重命名為mydevice-auto.c
(一)添加頭文件
#include <linux/device.h> |
(二)聲明變量
static struct class *my_class;
static struct device *my_device; |
(三)在mydevice_init(void)函數中創(chuàng)建類和設備節(jié)點
static int __init mydevice_init(void)
{ int ret; // 在這里執(zhí)行驅動程序的初始化操作 // 注冊字符設備驅動程序 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); ??//獲取主設備號 minor=MINOR(dev_num); ??//獲取次設備號 printk(KERN_INFO "major number: %dn",major); printk(KERN_INFO "minor number: %dn",minor); my_cdev.owner = THIS_MODULE; cdev_init(&my_cdev,&fops); ??//初始化字符設備結構體 cdev_add(&my_cdev,dev_num,1); ??//將字符設備添加到內核中 // 創(chuàng)建設備類 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)建設備節(jié)點并關聯(lián)到設備類 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()函數的原型:
struct class *class_create(struct module *owner, const char *name); |
owner:指向擁有該設備類的內核模塊的指針。通常使用THIS_MODULE來指定當前驅動模塊作為擁有者;
name:指定設備類的名稱。該名稱將用于在sysfs文件系統(tǒng)中創(chuàng)建對應的類目錄;
class_create()函數返回一個指向新創(chuàng)建的設備類的struct class指針。如果創(chuàng)建失敗,它將返回一個指向NULL的指針;
設備類的創(chuàng)建是設備驅動程序中重要的一步,它為設備提供了一個統(tǒng)一的命名空間和屬性集合,并將相關設備節(jié)點組織在一起。創(chuàng)建設備類后,驅動程序可以使用device_create()函數創(chuàng)建設備節(jié)點,并將其關聯(lián)到該設備類下。
device_create()函數的原型:
struct device *device_create(struct class *class, struct device *parent,
dev_t dev, void *drvdata, const char *fmt, ...); |
class:指向設備節(jié)點所屬的設備類的struct class指針;
parent:指向設備節(jié)點的父設備的struct device指針,如果沒有父設備,則可以設置為NULL;
dev:指定設備節(jié)點的設備號,使用MKDEV(major, minor)來創(chuàng)建;
drvdata:指向與設備節(jié)點相關的私有數據的指針,可以將驅動程序的特定數據與設備節(jié)點關聯(lián)起來;
fmt:設備節(jié)點名稱的格式字符串,用于在sysfs文件系統(tǒng)中創(chuàng)建設備節(jié)點。可以使用類似"mydevice%d"的格式,并使用可變參數來指定設備節(jié)點名稱的后綴;
device_create()函數返回一個指向新創(chuàng)建的設備節(jié)點的struct device指針。如果創(chuàng)建失敗,它將返回一個指向NULL的指針;
設備節(jié)點的創(chuàng)建需要先創(chuàng)建一個設備類(使用class_create()函數),然后使用device_create()函數創(chuàng)建設備節(jié)點并將其關聯(lián)到設備類下;
(四)mydevice_exit(void)函數中添加銷毀設備節(jié)點和設備類
static void __exit mydevice_exit(void)
{ // 在這里執(zhí)行驅動程序的清理操作 // 銷毀設備節(jié)點 device_destroy(my_class, MKDEV(major, minor)); // 銷毀設備類 class_destroy(my_class); // 刪除字符設備 cdev_del(&my_cdev); // 注銷字符設備驅動程序 unregister_chrdev(0, DEVICE_NAME); printk(KERN_INFO "Device unregistered.n"); } |
mydevice-auto.c完整示例源碼
#include <linux/module.h> ??????// 包含模塊相關函數的頭文件
#include <linux/fs.h> ??????????// 包含文件系統(tǒng)相關函數的頭文件 #include <linux/uaccess.h> ?????// 包含用戶空間數據訪問函數的頭文件 #include <linux/cdev.h> ????????//包含字符設備頭文件 #include <linux/device.h> #define DEVICE_NAME "mydevice" ?// 設備名稱 static dev_t dev_num; ??//分配的設備號 struct ?cdev my_cdev; ?????????//字符設備指針 int major; ?//主設備號 int minor; ?//次設備號 static struct class *my_class; static struct device *my_device; static int device_open(struct inode *inode, struct file *file) { // 在這里處理設備打開的操作 printk(KERN_INFO "This is device_open.n"); return 0; } static int device_release(struct inode *inode, struct file *file) { // 在這里處理設備關閉的操作 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) { // 在這里處理設備讀取的操作 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) { // 在這里處理設備寫入的操作 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í)行驅動程序的初始化操作 // 注冊字符設備驅動程序 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); ??//獲取主設備號 minor=MINOR(dev_num); ??//獲取次設備號 printk(KERN_INFO "major number: %dn",major); printk(KERN_INFO "minor number: %dn",minor); my_cdev.owner = THIS_MODULE; cdev_init(&my_cdev,&fops); ??//初始化字符設備結構體 cdev_add(&my_cdev,dev_num,1); ??//將字符設備添加到內核中 // 創(chuàng)建設備類 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)建設備節(jié)點并關聯(lián)到設備類 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í)行驅動程序的清理操作 // 銷毀設備節(jié)點 device_destroy(my_class, MKDEV(major, minor)); // 銷毀設備類 class_destroy(my_class); // 刪除字符設備 cdev_del(&my_cdev); // 注銷字符設備驅動程序 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_字符驅動/mydevice-auto$ make |
將生成的mydevice.ko文件拷貝到開發(fā)板中。
編寫測試應用源碼device-auto_app.c
驅動中已經給應用層提供了open、read、write的接口,接下來應用層就可以進行相應的系統(tǒng)調用。
#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; } |
設置環(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_字符驅動/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加載驅動后,在/dev下就生成了my_device節(jié)點,使用device_app測試正常。