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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

字符设备驱动高级篇6——内核提供的读写寄存器接口

發布時間:2023/12/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 字符设备驱动高级篇6——内核提供的读写寄存器接口 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

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


1、前面訪問寄存器的方式

通過定義指向寄存器的指針,然后解引用來對寄存器進行操作。

(1)行不行?sure!

(2)好不好?不好,因為ARM體系中內存和IO統一編址的,但有其他體系(如X86)不是統一編址的,因此不具有可移植性!


2、內核提供的寄存器讀寫接口

這些接口具有移植性,在Io.h文件中。


(1)writel寫寄存器,readl讀寄存器

(2)iowrite32和ioread32


3、代碼實踐

#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>#define MYMAJOR 200 #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 0xe0200244#define S5P_GPJ0REG(x) (x) #define S5P_GPJ0CON S5P_GPJ0REG(0) #define S5P_GPJ0DAT S5P_GPJ0REG(4)unsigned int *pGPJ0CON; unsigned int *pGPJ0DAT;static void __iomem *baseaddr; // 寄存器的虛擬地址的基地址int mymajor;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) { printk(KERN_INFO "chrdev_init helloworld init\n");// 在module_init宏調用的函數中去注冊字符設備驅動// major傳0進去表示要讓內核幫我們自動分配一個合適的空白的沒被使用的主設備號// 內核如果成功分配就會返回分配的主設備好;如果分配失敗會返回負數mymajor = register_chrdev(0, MYNAME, &test_fops);if (mymajor < 0){printk(KERN_ERR "register_chrdev fail\n");return -EINVAL;}printk(KERN_INFO "register_chrdev success... mymajor = %d.\n", mymajor);/* // 使用動態映射的方式來操作寄存器if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))return -EINVAL;if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0CON"))return -EINVAL;pGPJ0CON = ioremap(GPJ0CON_PA, 4);pGPJ0DAT = ioremap(GPJ0DAT_PA, 4); */ // *pGPJ0CON = 0x11111111; // *pGPJ0DAT = ((0<<3) | (0<<4) | (0<<5)); // 亮// 測試1:用2次ioremap得到的動態映射虛擬地址來操作,測試成功 // writel(0x11111111, pGPJ0CON); // writel(((0<<3) | (0<<4) | (0<<5)), pGPJ0DAT);// 測試2:用靜態映射的虛擬地址來操作,測試成功 // writel(0x11111111, GPJ0CON); // writel(((0<<3) | (0<<4) | (0<<5)), GPJ0DAT);// 測試3:用1次ioremap映射多個寄存器得到虛擬地址,測試成功if (!request_mem_region(GPJ0CON_PA, 8, "GPJ0BASE"))return -EINVAL;baseaddr = ioremap(GPJ0CON_PA, 8);writel(0x11111111, baseaddr + S5P_GPJ0CON);writel(((0<<3) | (0<<4) | (0<<5)), baseaddr + S5P_GPJ0DAT);return 0; }// 模塊下載函數 static void __exit chrdev_exit(void) {printk(KERN_INFO "chrdev_exit helloworld exit\n");//*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));//writel(((1<<3) | (1<<4) | (1<<5)), pGPJ0DAT); //writel(((1<<3) | (1<<4) | (1<<5)), GPJ0DAT); writel(((1<<3) | (1<<4) | (1<<5)), baseaddr + S5P_GPJ0DAT); /* // 解除映射iounmap(pGPJ0CON);iounmap(pGPJ0DAT);release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4); */iounmap(baseaddr);release_mem_region(baseaddr, 8);// 在module_exit宏調用的函數中去注銷字符設備驅動unregister_chrdev(mymajor, MYNAME);// rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); }module_init(chrdev_init); module_exit(chrdev_exit);// MODULE_xxx這種宏作用是用來添加模塊描述信息 MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("aston"); // 描述模塊的作者 MODULE_DESCRIPTION("module test"); // 描述模塊的介紹信息 MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息


總結

以上是生活随笔為你收集整理的字符设备驱动高级篇6——内核提供的读写寄存器接口的全部內容,希望文章能夠幫你解決所遇到的問題。

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