字符设备驱动高级篇5——静态映射表、动态映射结构体方式操作寄存器

来源:互联网 发布:成都家政公司办公软件 编辑:程序博客网 时间:2024/06/06 20:56

以下内容源于朱有鹏《物联网大讲堂》课程的学习整理,如有侵权,请告知删除。


一、静态映射表建立过程分析

1、建立映射表的三个关键部分

(1)映射表描述

  • 具体物理地址和虚拟地址的值相关的宏定义;

(2)映射表建立函数

a、

  • 该函数负责(由(1)中的映射表(一些宏定义)来)建立linux内核的页表映射关系。
  • 该函数位于kernel/arch/arm/mach-s5pv210/mach-smdkc110.c中,即smdkc110_map_io函数。
  • smdkc110_map_io
  •       s5p_init_io
  •             iotable_init

b、


  • 经过分析,真正的静态映射表在arch/arm/plat-s5p/cpu.c中的s5p_iodesc这个变量中;
  • 本质是一个结构体数组,数组中每一个元素就是一个映射;
  • 这个映射描述了一段物理地址到虚拟地址之间的映射。
  • 这个结构体数组所记录的几个映射关系被iotable_init所使用,该函数负责将这个结构体数组格式的表建立成MMU所能识别的页表映射关系;
  • 这样在开机后可以直接使用相对应的虚拟地址来访问对应的物理地址。

(3)开机时调用映射表建立函数

  • kernel启动时,smdkc110_map_io怎样被调用的?
  • 第二阶段的start_kernel函数
  •                setup_arch函数
  •                      paging_init函数(mmu.c文件中)
  •                           devicemaps_init函数



二、动态映射结构体方式操作寄存器

1、问题描述


  • 上图是之前讲解的多次映射(分开独立映射);
  • 下面仿效真实驱动中,用结构体封装的方式来进行单次多寄存器的地址映射;

2、实践编码

typedef struct GPJ0REG{volatile unsigned int gpj0con;volatile unsigned int gpj0dat;}gpj0_reg_t;#define GPJ0CONS5PV210_GPJ0CON#define GPJ0DATS5PV210_GPJ0DAT#define rGPJ0CON*((volatile unsigned int *)GPJ0CON)#define rGPJ0DAT*((volatile unsigned int *)GPJ0DAT)#define GPJ0_REGBASE0xe0200240gpj0_reg_t *pGPJ0REG;/****省略部分代码****/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));// 亮*/// 2步完成了映射if (!request_mem_region(GPJ0_REGBASE, sizeof(gpj0_reg_t), "GPJ0REG"))return -EINVAL;pGPJ0REG = ioremap(GPJ0_REGBASE, sizeof(gpj0_reg_t));// 映射之后用指向结构体的指针来进行操作// 指针使用->结构体内元素的方式来操作各个寄存器pGPJ0REG->gpj0con = 0x11111111;pGPJ0REG->gpj0dat = ((0<<3) | (0<<4) | (0<<5));// 亮return 0;}// 模块下载函数static void __exit chrdev_exit(void){printk(KERN_INFO "chrdev_exit helloworld exit\n");//*pGPJ0DAT = ((1<<3) | (1<<4) | (1<<5));pGPJ0REG->gpj0dat = ((1<<3) | (1<<4) | (1<<5));// 解除映射/*iounmap(pGPJ0CON);iounmap(pGPJ0DAT);release_mem_region(GPJ0CON_PA, 4);release_mem_region(GPJ0DAT_PA, 4);*/iounmap(pGPJ0REG);release_mem_region(GPJ0_REGBASE, sizeof(gpj0_reg_t));// 在module_exit宏调用的函数中去注销字符设备驱动unregister_chrdev(mymajor, MYNAME);}module_init(chrdev_init);module_exit(chrdev_exit);// MODULE_xxx这种宏作用是用来添加模块描述信息MODULE_LICENSE("GPL");// 描述模块的许可证MODULE_AUTHOR("aston");// 描述模块的作者MODULE_DESCRIPTION("module test");// 描述模块的介绍信息MODULE_ALIAS("alias xxx");// 描述模块的别名信息

阅读全文
0 0