Gpio調試方法
1:高通文檔說明
通過直接讀取gpio相應配置寄存器的值來分析對應gpio的配置情況;
寄存器地址:
上面說明的是寄存器每位代表的含義,是以msm8940為例的,對於我們的msm8940可以參考msm8940的芯片手冊《80-p4978-2x_a_msm8940_hardware_register_description.pdf》
第15 TLMM control and status registers這章節的說明
2讀寫方法:
Android 里有一個工具/system/bin/r ,這個工具只有在userdebug版本中會被編譯出來,源碼放在android/system/core/toolbox/r.c 里面。(有興趣的同學可以看下它的源碼)
這個工具會依賴於/dev/mem這個節點,可以先看一下/dev/mem這個節點是否已經存在,如果不存在需要打開kernel里的配置宏:CONFIG_DEVMEM=y,如果存在的話就不用管了。
如果上面條件都具備了,就可以用/system/bin/r 這個工具來讀寫GPIO寄存器,來控制GPIO 了。下面說一下這個工具的具體用法。
以GPIO 64為例,通過上面的高通文檔里可以看到寄存器配置:
Register – TLMM_GPIO_CFGn, n=[0..133]: 0x01000000 + 0x1000* (n)
GPIO64 ,TLMM_GPIO_CFG64: 0x01000000 +0x1000*0x40 = 0x01040000
(64換成16進制為0x40)
讀寄存器:
adb root
adb shell
#X596:/ # system/bin/r 0x1040000
01040000: 000002c3
bit[1:0] is 3, that means PULL_UP
bit[5:2] is 0, that means FUNC_0 (normal GPIO)
(FUNC_SEL在80-p4978-1b_a_msm8940_gpio_configuration_spreadsheet.xlsm可以查到):
bit[8:6] is 3, that means DRV_8_MA( 8mA drive strength)
bit[9] is 1, that means output enabled(GPIO behaves as output).
Register – TLMM_GPIO_IN_OUTn, n=[0..133]: 0x01000004 + 0x00001000* (n)
GPIO64, TLMM_GPIO_CFG64: 0x01000004+0x1000*0x40 = 0x01040004
讀寄存器:
X596:/ # system/bin/r 0x1040004
01040004: 00000003
寫寄存器:
X596:/ # system/bin/r 0x1040004 2
01040004: 00000002
Bit [1] 為 0, 表示GPIO64 輸出低,Bit [1] 為 1, 表示GPIO64 輸出高。
注意:如果要設置modem中用到的GPIO, 需要把modem中的配置去掉,用/system/bin/r命令才可以配置成功,這里有個patch, 是用GPIO108 為例的:
到此為止就是/system/bin/r 的使用方法。
下面說一下/system/bin/r 這個工具(有興趣的可以看一下,沒興趣的下面的就不用看了),源碼在android/system/core/toolbox/r.c 里面用到節點/dev/mem。這個節點應該是在android/kernel/msm-3.18/drivers/char/mem.c 里創建的,需要打開宏 CONFIG_DEVMEM。這個文件里不只創建一個節點,還有一個/dem/kmem節點。
/dev/mem: 物理內存的全鏡像。可以用來訪問物理內存。
用來訪問物理IO設備,比如X用來訪問顯卡的物理內存,或嵌入式中訪問GPIO。用法一般就是open,然后mmap,接着可以使用map之后的地址來訪問物理內存。這其實就是實現用戶空間驅動的一種方法。
Mmap的使用方法:https://baike.baidu.com/item/mmap/1322217?fr=aladdin
/dev/kmem: kernel看到的虛擬內存的全鏡像。可以用來訪問kernel的內容。
一般可以用來查看kernel的變量,或者用作rootkit之類的。
/dev/mem節點的使用方法可以查看r.c 代碼。
下面是一段/dev/kmem使用的一段代碼:
#include
#include
#include
#include
#include
#include
#include
#include
#define DEVKMEM "/dev/kmem"
#define PAGE_SIZE 0x1000
#define PAGE_MASK (~(PAGE_SIZE-1))
int main(int argc, char* argv[])
{
int fd;
char *mbase;
char read_buf[10];
unsigned int regAddr;
unsigned int varAddr;
varAddr = strtoul(argv[1], 0, 16);
unsigned int ptr = varAddr & ~(PAGE_MASK);
fd = open(DEVKMEM, O_RDONLY);
if (fd == -1) {
perror("open");
exit(-1);
}
mbase = mmap(0,PAGE_SIZE,PROT_READ,MAP_SHARED,fd, (varAddr & PAGE_MASK));
if (mbase == MAP_FAILED) {
printf("map failed %s\n",strerror(errno));
}
printf("varAddr = 0x%X \n", varAddr);
printf("mapbase = 0x%X \n", (unsigned int)mbase);
printf("value = 0x%X \n",*(unsigned int*)(mbase+ptr));
printf("char = %c%c%c%c \n",
*(char *)(mbase+ptr), *(char *)(mbase+ptr+1),
*(char *)(mbase+ptr+2), *(char *)(mbase+ptr+3));
close(fd);
munmap(mbase,PAGE_SIZE);
return 0;
}
