日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

字符设备驱动高级篇3——自动创建字符设备驱动的设备文件

發布時間:2023/12/20 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符设备驱动高级篇3——自动创建字符设备驱动的设备文件 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

以下內容源于朱有鵬《物聯網大講堂》課程的學習整理,如有侵權,請告知刪除。


1、問題描述

  • 使用mknod創建設備文件的缺點;
  • 能否自動生成和刪除設備文件;

2、解決方案:udev(嵌入式中用的是mdev),什么是udev?

  • 應用層的一個應用程序;
  • 內核驅動和應用層udev之間有一套信息傳輸機制(netlink協議);
  • 應用層啟用udev,內核驅動中使用相應接口;
  • 驅動注冊和注銷時信息會被傳給udev,由udev在應用層進行設備文件的創建和刪除;

3、內核驅動設備類相關函數

  • class_create
  • device_create

4、編程實踐

注冊字符設備驅動完成后,添加設備類的操作,以讓內核幫我們發信息!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ?


#include <linux/module.h> // module_init module_exit #include <linux/init.h> // __init __exit #include <linux/fs.h> #include <asm/uaccess.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> // arch/arm/mach-s5pv210/include/mach/gpio-bank.h #include <linux/string.h> #include <linux/io.h> #include <linux/ioport.h> #include <linux/cdev.h> #include <linux/device.h>//#define MYMAJOR 200 #define MYCNT 1 #define MYNAME "testchar"#define GPJ0CON S5PV210_GPJ0CON #define GPJ0DAT S5PV210_GPJ0DAT#define rGPJ0CON *((volatile unsigned int *)GPJ0CON) #define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)#define GPJ0CON_PA 0xe0200240 #define GPJ0DAT_PA 0xe0200244unsigned int *pGPJ0CON; unsigned int *pGPJ0DAT;//int mymajor; static dev_t mydev; //static struct cdev test_cdev; static struct cdev *pcdev; static struct class *test_class;char kbuf[100]; // 內核空間的bufstatic int test_chrdev_open(struct inode *inode, struct file *file) {// 這個函數中真正應該放置的是打開這個設備的硬件操作代碼部分// 但是現在暫時我們寫不了這么多,所以用一個printk打印個信息來做代表。printk(KERN_INFO "test_chrdev_open\n");rGPJ0CON = 0x11111111;rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮return 0; }static int test_chrdev_release(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_release\n");rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));return 0; }ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) {int ret = -1;printk(KERN_INFO "test_chrdev_read\n");ret = copy_to_user(ubuf, kbuf, count);if (ret){printk(KERN_ERR "copy_to_user fail\n");return -EINVAL;}printk(KERN_INFO "copy_to_user success..\n");return 0; }// 寫函數的本質就是將應用層傳遞過來的數據先復制到內核中,然后將之以正確的方式寫入硬件完成操作。 static ssize_t test_chrdev_write(struct file *file, const char __user *ubuf,size_t count, loff_t *ppos) {int ret = -1;printk(KERN_INFO "test_chrdev_write\n");// 使用該函數將應用層傳過來的ubuf中的內容拷貝到驅動空間中的一個buf中//memcpy(kbuf, ubuf); // 不行,因為2個不在一個地址空間中memset(kbuf, 0, sizeof(kbuf));ret = copy_from_user(kbuf, ubuf, count);if (ret){printk(KERN_ERR "copy_from_user fail\n");return -EINVAL;}printk(KERN_INFO "copy_from_user success..\n");if (kbuf[0] == '1'){rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));}else if (kbuf[0] == '0'){rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));}/*// 真正的驅動中,數據從應用層復制到驅動中后,我們就要根據這個數據// 去寫硬件完成硬件的操作。所以這下面就應該是操作硬件的代碼if (!strcmp(kbuf, "on")){rGPJ0DAT = ((0<<3) | (0<<4) | (0<<5));}else if (!strcmp(kbuf, "off")){rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));} */return 0; }// 自定義一個file_operations結構體變量,并且去填充 static const struct file_operations test_fops = {.owner = THIS_MODULE, // 慣例,直接寫即可.open = test_chrdev_open, // 將來應用open打開這個設備時實際調用的.release = test_chrdev_release, // 就是這個.open對應的函數.write = test_chrdev_write,.read = test_chrdev_read, };// 模塊安裝函數 static int __init chrdev_init(void) { int retval;printk(KERN_INFO "chrdev_init helloworld init\n");// 使用新的cdev接口來注冊字符設備驅動// 新的接口注冊字符設備驅動需要2步// 第1步:分配主次設備號retval = alloc_chrdev_region(&mydev, 12, MYCNT, MYNAME);if (retval < 0) {printk(KERN_ERR "Unable to alloc minors for %s\n", MYNAME);goto flag1;}printk(KERN_INFO "alloc_chrdev_region success\n");printk(KERN_INFO "major = %d, minor = %d.\n", MAJOR(mydev), MINOR(mydev));// 第2步:注冊字符設備驅動pcdev = cdev_alloc(); // 給pcdev分配內存,指針實例化//cdev_init(pcdev, &test_fops);pcdev->owner = THIS_MODULE;pcdev->ops = &test_fops;retval = cdev_add(pcdev, mydev, MYCNT);if (retval) {printk(KERN_ERR "Unable to cdev_add\n");goto flag2;}printk(KERN_INFO "cdev_add success\n");// 注冊字符設備驅動完成后,添加設備類的操作,以讓內核幫我們發信息!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!// 給udev,讓udev自動創建和刪除設備文件test_class = class_create(THIS_MODULE, "aston_class");//類名if (IS_ERR(test_class))return -EINVAL;// 最后1個參數字符串,就是我們將來要在/dev目錄下創建的設備文件的名字// 所以我們這里要的文件名是/dev/testdevice_create(test_class, NULL, mydev, NULL, "test");// 使用動態映射的方式來操作寄存器if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON")) // return -EINVAL;goto flag3;if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON")) // return -EINVAL;goto flag3;pGPJ0CON = ioremap(GPJ0CON_PA, 4);pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);*pGPJ0CON = 0x11111111;*pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮//goto flag0:return 0;// 如果第4步才出錯跳轉到這里來 release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);// 如果第3步才出錯跳轉到這里來 flag3:cdev_del(pcdev);// 如果第2步才出錯跳轉到這里來 flag2:// 在這里把第1步做成功的東西給注銷掉unregister_chrdev_region(mydev, MYCNT); // 如果第1步才出錯跳轉到這里來 flag1: return -EINVAL; //flag0: // return 0; }// 模塊下載函數 static void __exit chrdev_exit(void) {printk(KERN_INFO "chrdev_exit helloworld exit\n");*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); // 解除映射iounmap(pGPJ0CON);iounmap(pGPJ0DAT);release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);/* // 在module_exit宏調用的函數中去注銷字符設備驅動unregister_chrdev(mymajor, MYNAME); */ device_destroy(test_class, mydev);class_destroy(test_class);// 使用新的接口來注銷字符設備驅動// 注銷分2步:// 第一步真正注銷字符設備驅動用cdev_delcdev_del(pcdev);// 第二步去注銷申請的主次設備號unregister_chrdev_region(mydev, MYCNT); }module_init(chrdev_init); module_exit(chrdev_exit);// MODULE_xxx這種宏作用是用來添加模塊描述信息 MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("aston"); // 描述模塊的作者 MODULE_DESCRIPTION("module test"); // 描述模塊的介紹信息 MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息

總結

以上是生活随笔為你收集整理的字符设备驱动高级篇3——自动创建字符设备驱动的设备文件的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。