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

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

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

以下內(nèi)容源于朱有鵬《物聯(lián)網(wǎng)大講堂》課程的學(xué)習(xí)整理,如有侵權(quán),請(qǐng)告知?jiǎng)h除。


1、前面訪問(wèn)寄存器的方式

通過(guò)定義指向寄存器的指針,然后解引用來(lái)對(duì)寄存器進(jìn)行操作。

(1)行不行?sure!

(2)好不好?不好,因?yàn)锳RM體系中內(nèi)存和IO統(tǒng)一編址的,但有其他體系(如X86)不是統(tǒng)一編址的,因此不具有可移植性!


2、內(nèi)核提供的寄存器讀寫(xiě)接口

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


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

(2)iowrite32和ioread32


3、代碼實(shí)踐

#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]; // 內(nèi)核空間的bufstatic int test_chrdev_open(struct inode *inode, struct file *file) {// 這個(gè)函數(shù)中真正應(yīng)該放置的是打開(kāi)這個(gè)設(shè)備的硬件操作代碼部分// 但是現(xiàn)在暫時(shí)我們寫(xiě)不了這么多,所以用一個(gè)printk打印個(gè)信息來(lái)做代表。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; }// 寫(xiě)函數(shù)的本質(zhì)就是將應(yīng)用層傳遞過(guò)來(lái)的數(shù)據(jù)先復(fù)制到內(nèi)核中,然后將之以正確的方式寫(xiě)入硬件完成操作。 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");// 使用該函數(shù)將應(yīng)用層傳過(guò)來(lái)的ubuf中的內(nèi)容拷貝到驅(qū)動(dòng)空間中的一個(gè)buf中//memcpy(kbuf, ubuf); // 不行,因?yàn)?個(gè)不在一個(gè)地址空間中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));}/*// 真正的驅(qū)動(dòng)中,數(shù)據(jù)從應(yīng)用層復(fù)制到驅(qū)動(dòng)中后,我們就要根據(jù)這個(gè)數(shù)據(jù)// 去寫(xiě)硬件完成硬件的操作。所以這下面就應(yīng)該是操作硬件的代碼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; }// 自定義一個(gè)file_operations結(jié)構(gòu)體變量,并且去填充 static const struct file_operations test_fops = {.owner = THIS_MODULE, // 慣例,直接寫(xiě)即可.open = test_chrdev_open, // 將來(lái)應(yīng)用open打開(kāi)這個(gè)設(shè)備時(shí)實(shí)際調(diào)用的.release = test_chrdev_release, // 就是這個(gè).open對(duì)應(yīng)的函數(shù).write = test_chrdev_write,.read = test_chrdev_read, };// 模塊安裝函數(shù) static int __init chrdev_init(void) { printk(KERN_INFO "chrdev_init helloworld init\n");// 在module_init宏調(diào)用的函數(shù)中去注冊(cè)字符設(shè)備驅(qū)動(dòng)// major傳0進(jìn)去表示要讓內(nèi)核幫我們自動(dòng)分配一個(gè)合適的空白的沒(méi)被使用的主設(shè)備號(hào)// 內(nèi)核如果成功分配就會(huì)返回分配的主設(shè)備好;如果分配失敗會(huì)返回負(fù)數(shù)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);/* // 使用動(dòng)態(tài)映射的方式來(lái)操作寄存器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)); // 亮// 測(cè)試1:用2次ioremap得到的動(dòng)態(tài)映射虛擬地址來(lái)操作,測(cè)試成功 // writel(0x11111111, pGPJ0CON); // writel(((0<<3) | (0<<4) | (0<<5)), pGPJ0DAT);// 測(cè)試2:用靜態(tài)映射的虛擬地址來(lái)操作,測(cè)試成功 // writel(0x11111111, GPJ0CON); // writel(((0<<3) | (0<<4) | (0<<5)), GPJ0DAT);// 測(cè)試3:用1次ioremap映射多個(gè)寄存器得到虛擬地址,測(cè)試成功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; }// 模塊下載函數(shù) 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宏調(diào)用的函數(shù)中去注銷(xiāo)字符設(shè)備驅(qū)動(dòng)unregister_chrdev(mymajor, MYNAME);// rGPJ0DAT = ((1<<3) | (1<<4) | (1<<5)); }module_init(chrdev_init); module_exit(chrdev_exit);// MODULE_xxx這種宏作用是用來(lái)添加模塊描述信息 MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("aston"); // 描述模塊的作者 MODULE_DESCRIPTION("module test"); // 描述模塊的介紹信息 MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息


總結(jié)

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

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。