字符设备驱动高级篇5——静态映射表、动态映射结构体方式操作寄存器
生活随笔
收集整理的這篇文章主要介紹了
字符设备驱动高级篇5——静态映射表、动态映射结构体方式操作寄存器
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
以下內容源于朱有鵬《物聯網大講堂》課程的學習整理,如有侵權,請告知刪除。
一、靜態映射表建立過程分析
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 GPJ0CON S5PV210_GPJ0CON #define GPJ0DAT S5PV210_GPJ0DAT #define rGPJ0CON *((volatile unsigned int *)GPJ0CON) #define rGPJ0DAT *((volatile unsigned int *)GPJ0DAT)#define GPJ0_REGBASE 0xe0200240gpj0_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"); // 描述模塊的別名信息總結
以上是生活随笔為你收集整理的字符设备驱动高级篇5——静态映射表、动态映射结构体方式操作寄存器的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SIFT算法流程介绍
- 下一篇: CNVD、CNNVD、CICSVD等区别