字符设备驱动高级篇6——内核提供的读写寄存器接口
2017-07-20 09:30
363 查看
以下内容源于朱有鹏《物联网大讲堂》课程的学习整理,如有侵权,请告知删除。
(1)行不行?sure!
(2)好不好?不好,因为ARM体系中内存和IO统一编址的,但有其他体系(如X86)不是统一编址的,因此不具有可移植性!
(1)writel写寄存器,readl读寄存器
(2)iowrite32和ioread32
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]; // 内核空间的buf static 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") b6a9 ; 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"); // 描述模块的别名信息
相关文章推荐
- 内核提供的读写寄存器接口实现可移植性
- linux高级字符设备驱动之 二 内核等待队列
- 利用linux 内核所提供的input子系统编写字符设备驱动的步骤
- 字符设备驱动高级篇5——静态映射表、动态映射结构体方式操作寄存器
- 字符设备驱动基础篇4——字符设备驱动读写接口的操作实践
- 高级字符设备驱动-内核等待队列笔记
- 设备驱动学习之字符设备驱动内核代码分析(一)——设备号申请接口
- linux内核字符设备驱动之发送命令接口
- 字符设备驱动内核框架小结(一)
- 【Linux高级驱动】平台设备驱动机制的编程流程与编译进内核
- 使用内核定时器的second字符设备驱动及测试代码
- Linux高级字符设备驱动
- 高级字符设备驱动--中断下半部机制之tasklet(一)
- Linux 字符设备驱动开发--内存读写操作
- 字符设备驱动内核框架小结
- Linux OS内核 作业三:设备驱动与读写信号量
- 字符设备驱动内核框架小结(一)
- linux 高级字符设备驱动 ioctl操作介绍 例程分析实现
- 高级字符设备驱动--中断下半部机制之workqueue(二)
- 简单的LINUX字符设备驱动及编译进Linux内核…