今天是母亲节,首先祝各位读者的母亲节日快乐,祝你们的母亲年轻健康。母亲节是一个亘古的话题,我本来想写个文章,但是想起来这周就一个周末,要花点时间陪下家人,昨天我们老大开会,特别说了,有时间多陪陪家人,我脑子一转,今天就一直陪着楠哥各种玩。
晚上回来看看笔记,写了这个技术文章,ioremap是一个属于被遗忘的技术知识点,可能我做Android 驱动没用上,也就疏忽了,像内核通知链一些冷门的技术,还偶尔用一下。
内存管理是一个经久不衰的话题,从段式管理到页式管理已经过去很久了,页式管理也已经有了好长好长时间了,使用Linux内核的操作系统已经从更新了很多不同的版本。
不同的CPU体系对外设IO端口物理地址的编址方式也不同,分为I/O映射方式(I/O-mapped)和内存映射方式(Memory-mapped)。
以X86为例:X86为外设专门实现有单独的地址空间,可以称为「I/O地址空间」或「I/O端口空间」,这个是独立与CPU和RAM物理地址空间,所有外设的IO端口均在这一空间。CPU通过设立专门的IN和OUT指令来访问这一空间中的地址单元(即I/O端口),这就是所谓的“I/O映射方式”(I/O-mapped)。和RAM物理地址空间相比,I/O地址空间通常都比较小,如x86 CPU的I/O空间就只有64KB(0-0xffff)。这是「I/O映射方式」的一个主要缺点,你可以通过cat /proc/ioports去查看,IO port空间的地址资源分配情况是以树状结构显示。这个源于x86平台的设计思想,目前基本不用了,获取这些资源的函数接口如request_region和ioremap。
像用户空间一样,内核也是通过页表访问内存,很多人对页表理解不是很透彻,你要是说他复杂也是非常复杂,但是你说要是简单也很简单,可以理解成一个图书的目录,有了这个目录,就可以知道什么内存地址存放了什么东西,以后要是想存新的东西进去,就在原来的目录表里面进行更新。
但是我们使用的外设,比如是一个光感传感器,我们需要控制这个光感传感器,那就需要读写这个光感传感器的I2C寄存器,如果内核代码需要像访问内存一样来访问这个光感设备呢?我们这时候就需要首先设置一个适当的内核页表映射。
这时候我们就需要使用 ioremap()这个函数,经过这个函数还衍生了其他很多变种函数,我不知道为什么要用变种这个词,如果有其他词语可以留言一起讨论下,毕竟一次好的词语可以加深我们的印象。
具体可以看代码arch/arm/mm/ioremap.c 里面有很多相近似的函数。函数参数含义
// 功能:将物理地址映射为虚拟地址
// 参数1:需要映射的物理地址
// 参数2:需要映射的地址长度
// 返回值:该函数返回映射后的内核虚拟地址(3G-4G). 接着便可以通过读写该返回的内核虚拟地址去访问之这段I/O内存资源。
简单的实例代码
void led_on(void)
{void *reg_base;unsigned int temp; reg_base = ioremap(0x1212, 4);temp = __raw_readl(reg_base); temp &= ~(0xff<<12);temp |= (0x11<<12);__raw_writel(temp, reg_base);
}
#但是内核没几个地方用这个函数
我在内核里面搜了下,这个函数在内核里面并没有几个地方用到的。特别是我们最近做MTK方案和RK方案,里面的触摸屏驱动下面没使用到。但是音频platform部分却还有使用的地方。
用了一个新的转换方式
/* ioremap to BT HW register base address */BTSYS_PKV_BASE_ADDRESS = (void *)btsys_pkv_physical_base;BTSYS_SRAM_BANK2_BASE_ADDRESS = (void *)btsys_sram_bank2_physical_base;bt_hw_REG_PACKET_R = BTSYS_PKV_BASE_ADDRESS + cvsd_mcu_read_offset;bt_hw_REG_PACKET_W = BTSYS_PKV_BASE_ADDRESS + cvsd_mcu_write_offset;bt_hw_REG_CONTROL = BTSYS_PKV_BASE_ADDRESS + cvsd_packet_indicator;pr_debug("[BTCVSD probe] BTSYS_PKV_BASE_ADDRESS = %p BTSYS_SRAM_BANK2_BASE_ADDRESS = %p\n",BTSYS_PKV_BASE_ADDRESS, BTSYS_SRAM_BANK2_BASE_ADDRESS);return snd_soc_register_platform(&pdev->dev, &mtk_btcvsd_rx_soc_platform);/*使用*/kal_int32 i;kal_uint16 pv;kal_uint8 *pSrc;kal_uint8 *pPacketBuf;unsigned long flags;unsigned long connsys_addr_rx, ap_addr_rx;LOGBT("%s(+) btsco.pRX->iPacket_w=%d\n", __func__, btsco.pRX->iPacket_w);connsys_addr_rx = *bt_hw_REG_PACKET_R;ap_addr_rx = (unsigned long)BTSYS_SRAM_BANK2_BASE_ADDRESS + (connsys_addr_rx & 0xFFFF);LOGBT("%s connsys_addr_rx=0x%lx,ap_addr_rx=0x%lx\n",__func__, connsys_addr_rx, ap_addr_rx);pSrc = (kal_uint8 *)ap_addr_rx;LOGBT("%s uPacketLength=%d,uPacketNumber=%d, btsco.uRXState=%d\n",__func__, uPacketLength, uPacketNumber, btsco.uRXState);AudDrv_BTCVSD_DataTransfer(BT_SCO_DIRECT_BT2ARM, pSrc, btsco.pRX->TempPacketBuf,uPacketLength, uPacketNumber, btsco.uRXState);LOGBT("%s AudDrv_BTCVSD_DataTransfer DONE!!!,uControl=0x%x,uLen=%d\n", __func__, uControl, uLen);
#查看系统当前的内存映射
/proc/iomem这个文件记录的是物理地址的分配情况,记得要查看需要使用root权限。这些地址范围是通过 requset_mem_region 函数申请得到的。
weiqifa0@weiqifa-System-Product-Name:~$ sudo cat /proc/iomem
[sudo] password for weiqifa0:
00000000-00000fff : Reserved
00001000-00057fff : System RAM
00058000-00058fff : Reserved
00059000-0009efff : System RAM
0009f000-000fffff : Reserved000a0000-000bffff : PCI Bus 0000:00000c0000-000cffff : Video ROM000f0000-000fffff : System ROM
00100000-b77eefff : System RAM
b77ef000-b7828fff : ACPI Tables
b7829000-b7bc9fff : System RAM
b7bca000-b7bcafff : ACPI Non-volatile Storage
b7bcb000-b7bcbfff : Reserved
b7bcc000-c492efff : System RAM
c492f000-c61b9fff : Reserved
c61ba000-c61cefff : ACPI Tables
c61cf000-c62ccfff : System RAM
c62cd000-c65f4fff : ACPI Non-volatile Storage
c65f5000-c7795fff : Reserved
c7796000-c77fefff : Unknown E820 type
c77ff000-c77fffff : System RAM
c7800000-c7ffffff : Reserved
c8000000-f7ffffff : PCI Bus 0000:00c8000000-c81fffff : PCI Bus 0000:05c8200000-c83fffff : PCI Bus 0000:05e0000000-efffffff : PCI Bus 0000:01e0000000-efffffff : 0000:01:00.0
ioports主要显示IO端口的地址范围,使用request_region( )分配I/O端口地址范围会显示在这里。
weiqifa0@weiqifa-System-Product-Name:~$ sudo cat /proc/ioports
0000-0cf7 : PCI Bus 0000:000000-001f : dma10020-0021 : pic10040-0043 : timer00050-0053 : timer10060-0060 : keyboard0064-0064 : keyboard0070-0077 : rtc00080-008f : dma page reg00a0-00a1 : pic200c0-00df : dma200f0-00ff : fpu00f0-00f0 : PNP0C04:000290-029f : pnp 00:0003f8-03ff : serial0680-069f : pnp 00:020800-087f : pnp 00:03
0cf8-0cff : PCI conf1
0d00-ffff : PCI Bus 0000:00164e-164f : pnp 00:021800-18fe : pnp 00:021800-1803 : ACPI PM1a_EVT_BLK1804-1805 : ACPI PM1a_CNT_BLK1808-180b : ACPI PM_TMR1850-1850 : ACPI PM2_CNT_BLK1854-1857 : pnp 00:051880-189f : ACPI GPE0_BLK2000-2fff : PCI Bus 0000:05d000-dfff : PCI Bus 0000:04d000-d0ff : 0000:04:00.0e000-efff : PCI Bus 0000:01e000-e0ff : 0000:01:00.0f000-f01f : 0000:00:1f.4f020-f03f : 0000:00:17.0f020-f03f : ahcif040-f043 : 0000:00:17.0f040-f043 : ahcif050-f057 : 0000:00:17.0f050-f057 : ahcife00-fefe : pnp 00:08ffff-ffff : pnp 00:02ffff-ffff : pnp 00:02ffff-ffff : pnp 00:02
weiqifa0@weiqifa-System-Product-Name:~$
#总结
ioremap 这些曾经用起来很不错,或者说是设计者设计的时候,觉得很不错的东西,现在突然被遗忘,我认为很大程度是因为设计的时候允许的地址范围太小,也就是因为太小,要加新的东西总是畏首畏尾。
回复「 篮球的大肚子」进入技术群聊
回复「1024」获取1000G学习资料
PS:想加入技术群的同学,加了我好友后,就给我发「篮球的大肚子」这句话,有可能机器人打瞌睡,可以多发几次,不要发与技术无关的消息或者推广。