IO静态映射和动态映射
1:靜態映射方法的特點:
? 內核移植時以代碼的形式硬編碼,如果要更改必須改源代碼后重新編譯內核在內核啟動時建立靜態映射表,到內核關機時銷毀,中間一直有效對于移植好的內核,你用不用他都在那里
?
2:動態映射方法的特點:
? 驅動程序根據需要隨時動態的建立映射、使用、銷毀映射映射是短期臨時的
?
3:如何選擇虛擬地址映射方法
? (1)2種映射并不排他,可以同時使用
? (2)靜態映射類似于C語言中全局變量,動態方式類似于C語言中malloc堆內存
? (3)靜態映射的好處是執行效率高,壞處是始終占用虛擬地址空間;動態映射的 好處是按需使用虛擬地址空間,壞處是每次使用前后都需要代碼去建立映射&銷毀映射(還得學會使用那些內核函數的使用)
?
4:靜態映射表
? 不同版本的內核,其靜態映射表的文件名以及文件路徑不一定相同,但是一般都在arch/arm/xxx/map_xx.h文件中
? (1)s5pv210的主映射表位于:arch/arm/plat-s5p/include/plat/map-s5p.h。
CPU在安排寄存器地址時不是隨意亂序分布的,而是按照模塊去區分的。每一個模塊內部的很多個寄存器的地址是連續的。所以內核在定義寄存器地址時都是先找到基地址,然后再用基地址+偏移量來尋找具體的一個寄存器。這個文件夾下面的虛擬地址基地址是不全的,原因是三星在移植的時候只是移植了自己需要的部分,將來我們需要添加其他基地址的時候直接添加就行了。map-s5p.h中定義的就是要用到的幾個模塊的寄存器基地址(虛擬地址)。
? (2)虛擬地址基地址定義在:arch/arm/plat-samsung/include/plat/map-base.h
#define S3C_ADDR_BASE (0xFD000000) //?三星移植時確定的靜態映射表的基地址,表中的所有虛擬地址都是以這個地址+偏移量來指定的。
? (3)GPIO相關的主映射表位于:arch/arm/machs5pv210/include/mach/regs-gpio.h
表中是GPIO的各個端口的基地址的定義
? (4)GPIO的具體寄存器定義位于:arch/arm/mach-s5pv210/include/mach/gpio-bank.h
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <mach/regs-gpio.h> #include <mach/gpio-bank.h> #include <linux/io.h> #include <asm/uaccess.h>#define GPJ0CON S5PV210_GPJ0CON #define GPJ0DAT S5PV210_GPJ0DAT//#define MYMAJOR 200 #define MYCNT 1 #define MYNAME "testchar"char kbuf[100];static dev_t mydev; static struct cdev test_cdev; static struct class *test_class;static int test_chrdev_open(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_open...\n");/* 配置IO為輸出 */writel((1 << 12) | (1 << 16) | (1 << 20), GPJ0CON);writel((1 << 3) | (1 << 4) | (1 << 5), GPJ0DAT);return 0; }static int test_chrdev_release(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_release...\n");/* 關閉所有LED */writel((1 << 3) | (1 << 4) | (1 << 5), GPJ0DAT);return 0; }ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) {printk(KERN_INFO "test_chrdev_read...\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");/* 清空內存 */memset(kbuf, 0, sizeof(kbuf));/* 將數據從用戶空間拷貝到內核空間 */ret = copy_from_user(kbuf, ubuf, count);if (ret){printk(KERN_ERR "copy_from_user fail\n");return -EINVAL;}/* 判斷LED打開還是關閉 */if (kbuf[0] == '1')writel((0 << 3) | (0 << 4) | (0 << 5), GPJ0DAT);else if (kbuf[0] == '0')writel((1 << 3) | (1 << 4) | (1 << 5), GPJ0DAT);return 0; }static const struct file_operations test_fops = {.owner = THIS_MODULE,.open = test_chrdev_open,.release = test_chrdev_release,.write = test_chrdev_write,.read = test_chrdev_read, };static int __init chrdev_init(void) { int retval;printk(KERN_INFO "chrdev_init...\n");/* 分配主次設備號 */ /*mydev = MKDEV(MYMAJOR, 0);retval = register_chrdev_region(mydev, MYCNT, MYNAME);if (retval) {printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);return -EINVAL;} */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));/* 注冊字符設備驅動 */cdev_init(&test_cdev, &test_fops);retval = cdev_add(&test_cdev, mydev, MYCNT);if (retval) {printk(KERN_ERR "Unable to cdev_add\n");goto flag2;}printk(KERN_INFO "cdev_add success\n");/* 創建設備類 */test_class = class_create(THIS_MODULE, "test_class");if (IS_ERR(test_class)) {printk(KERN_ERR "Unable to class_create\n");goto flag3;}/* 創建設備節點 */device_create(test_class, NULL, mydev, NULL, "test");return 0;flag3:cdev_del(&test_cdev);flag2:unregister_chrdev_region(mydev, MYCNT); flag1: return -EINVAL; }static void __exit chrdev_exit(void) {printk(KERN_INFO "chrdev_exit...\n");/* 銷毀設備類節點 */device_destroy(test_class, mydev);class_destroy(test_class);/* 注銷字符設備驅動 */cdev_del(&test_cdev);/* 注銷申請的主次設備號 */unregister_chrdev_region(mydev, MYCNT); }module_init(chrdev_init); module_exit(chrdev_exit);MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("lsm"); // 描述模塊的作者 MODULE_DESCRIPTION("module test"); // 描述模塊的介紹信息 MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息?
5:動態映射操作LED
5.1:如何建立動態映射
? (1)request_mem_region:向內核申請(報告)需要映射的內存資源。
? (2)ioremap:真正用來實現映射,傳給他物理地址他返回給你一個虛擬地址
? 返回值:成功返回一個虛擬地址(或者是一段虛擬地址的首地址,具體取決于我們需要映射的字節長度),失敗返回0
5.2:如何銷毀動態映射
? (1)iounmap:解除映射
? (2)release_mem_region:釋放映射
#include <linux/module.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/cdev.h> #include <linux/device.h> #include <linux/io.h> #include <asm/uaccess.h>#define GPJ0CON_PA 0xe0200240 #define GPJ0DAT_PA 0xe0200244unsigned int *pGPJ0CON; unsigned int *pGPJ0DAT;//#define MYMAJOR 200 #define MYCNT 1 #define MYNAME "testchar"char kbuf[100];static dev_t mydev; static struct cdev test_cdev; static struct class *test_class;static int test_chrdev_open(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_open...\n");/* 配置IO為輸出 */writel((1 << 12) | (1 << 16) | (1 << 20), pGPJ0CON);writel((1 << 3) | (1 << 4) | (1 << 5), pGPJ0DAT);return 0; }static int test_chrdev_release(struct inode *inode, struct file *file) {printk(KERN_INFO "test_chrdev_release...\n");/* 關閉所有LED */writel((1 << 3) | (1 << 4) | (1 << 5), pGPJ0DAT);return 0; }ssize_t test_chrdev_read(struct file *file, char __user *ubuf, size_t count, loff_t *ppos) {printk(KERN_INFO "test_chrdev_read...\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");/* 清空內存 */memset(kbuf, 0, sizeof(kbuf));/* 將數據從用戶空間拷貝到內核空間 */ret = copy_from_user(kbuf, ubuf, count);if (ret){printk(KERN_ERR "copy_from_user fail\n");return -EINVAL;}/* 判斷LED打開還是關閉 */if (kbuf[0] == '1')writel((0 << 3) | (0 << 4) | (0 << 5), pGPJ0DAT);else if (kbuf[0] == '0')writel((1 << 3) | (1 << 4) | (1 << 5), pGPJ0DAT);return 0; }static const struct file_operations test_fops = {.owner = THIS_MODULE,.open = test_chrdev_open,.release = test_chrdev_release,.write = test_chrdev_write,.read = test_chrdev_read, };static int __init chrdev_init(void) { int retval;printk(KERN_INFO "chrdev_init...\n");/* 分配主次設備號 */ /*mydev = MKDEV(MYMAJOR, 0);retval = register_chrdev_region(mydev, MYCNT, MYNAME);if (retval) {printk(KERN_ERR "Unable to register minors for %s\n", MYNAME);return -EINVAL;} */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));/* 注冊字符設備驅動 */cdev_init(&test_cdev, &test_fops);retval = cdev_add(&test_cdev, mydev, MYCNT);if (retval) {printk(KERN_ERR "Unable to cdev_add\n");goto flag2;}printk(KERN_INFO "cdev_add success\n");/* 創建設備類 */test_class = class_create(THIS_MODULE, "test_class");if (IS_ERR(test_class)) {printk(KERN_ERR "Unable to class_create\n");goto flag3;}/* 創建設備節點 */device_create(test_class, NULL, mydev, NULL, "test");/* 申請內存 */if (!request_mem_region(GPJ0CON_PA, 4, "GPJ0CON"))goto err4;if (!request_mem_region(GPJ0DAT_PA, 4, "GPJ0DAT"))goto err5; /* 內存映射 */pGPJ0CON = ioremap(GPJ0CON_PA, 4);pGPJ0DAT = ioremap(GPJ0DAT_PA, 4);return 0;err5:release_mem_region(GPJ0CON_PA, 4); err4:device_destroy(test_class, mydev);class_destroy(test_class);flag3:cdev_del(&test_cdev);flag2:unregister_chrdev_region(mydev, MYCNT); flag1: return -EINVAL; }static void __exit chrdev_exit(void) {printk(KERN_INFO "chrdev_exit...\n");iounmap(pGPJ0CON);iounmap(pGPJ0DAT);release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);/* 銷毀設備類節點 */device_destroy(test_class, mydev);class_destroy(test_class);/* 注銷字符設備驅動 */cdev_del(&test_cdev);/* 注銷申請的主次設備號 */unregister_chrdev_region(mydev, MYCNT); }module_init(chrdev_init); module_exit(chrdev_exit);MODULE_LICENSE("GPL"); // 描述模塊的許可證 MODULE_AUTHOR("lsm"); // 描述模塊的作者 MODULE_DESCRIPTION("module test"); // 描述模塊的介紹信息 MODULE_ALIAS("alias xxx"); // 描述模塊的別名信息?
總結
以上是生活随笔為你收集整理的IO静态映射和动态映射的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 区块链究竟是什么?看完这篇秒懂!
- 下一篇: 含有耦合电感的电路