> For the complete documentation index, see [llms.txt](https://open-rt.gitbook.io/open-surfacert/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://open-rt.gitbook.io/open-surfacert/surface-rt/hardware/gpios.md).

# GPIOs

## How to identify GPIOs using ACPI

![ACPI-SSDT0000.ASL - Surface RT](https://2395005476-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJQb34R_xPz0ll0scbM%2F-MM_RL3ukk4dE7ey433u%2F-MM_TXCsLOd0FoI7gNn-%2Fimage.png?alt=media\&token=7bdf87ce-bf01-4e1f-b875-e96e5003bbee)

if we take a look at the ACPI table we see several GpioIO functions.

## Identifying the Volume Keys

Identifying the Volume keys is easy. We can dump the GPIO-Controller from UEFI shell while a volume key is pressed or released.

![Left: Vol-Down pressed; right Vol-Down not pressed ](https://2395005476-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJQb34R_xPz0ll0scbM%2F-MM_RL3ukk4dE7ey433u%2F-MM_U_13G6Go5hFyvl97%2Fimage.png?alt=media\&token=a0a2c8d0-a267-491c-b016-59f572ce67c7)

We see that we changed something with a button press. Nice!\
We see that 1 Byte has changed from 0xC0 to 0x80 at address 0x438\
In binary: 1100 0000 -> 1000 0000 => Bit 6 has changed.

Lets take a look at 0x438

![0x438 is located on the 5th GPIO Controller and is the input register. check.](https://2395005476-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJQb34R_xPz0ll0scbM%2F-MM_RL3ukk4dE7ey433u%2F-MM_VJFPBPG2-MPtjpMK%2Fimage.png?alt=media\&token=e293e659-12fe-410d-8f00-bc2b3064db79)

Now we know that we changed Pin6 in Port S\
\=> Vol-Down is connected to PS6

### Match it to ACPI

S is the 19th letter of the Alphabet counting from 1 (A=1, B=2, C=3,...)\
S is the 18th letter of the Alphabet counting from 0 (A=0, B=1, C=2,...)\
[TRM](/open-surfacert/surface-rt/hardware/tegra3-technical-reference-manual.md) tells us that every port has 8 pins.\
So lets multiply 18 by 8 which equals 144 and add our PinOffset: 6\
144+6 = 150\
Convert 150 from dec to hex: 0x96

![There is a Gpio with the number 0x96 it belongs to TEV2/MSHW0003](https://2395005476-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJQb34R_xPz0ll0scbM%2F-MM_ZcY0OhK9nKzMW81e%2F-MM_ZxNOFpg-LLo_gjtL%2Fimage.png?alt=media\&token=b38a2bc1-2ae9-49cf-bb1f-57d8459fc7bd)

![Windows device-manager screenshot](https://2395005476-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJQb34R_xPz0ll0scbM%2F-MM__C9AlIe4IGp4Ux5M%2F-MM_cV29eMb5nJFqJeU9%2FHomeButtonDriver.png?alt=media\&token=ea251d30-0680-4a94-9a37-fd17e2f03ba1)

We can match the 4 GPIOs to the SurfaceHomeButtonDriver. We can see that Windows knows 4 Interrupts. At the moment we dont know how to map the WindowsIRQ to ACPI-IRQ or LinuxIRQ

### Match it to Linux

Linux enumerates GPIO in the same way as ACPI:\
`portNumber * 8 + pinOffset (+ gpioControllerOffset)`\
(gpioControllerOffset is 0 on tegra 3)

We know that we deal with:\
`Vol-Down: 0x96 / 150d`\
`Vol-Up:   0x97 / 151d`\
We guess 0x97 because it is a normal shared interrupt.\
The other 2 interrupts are WakeUp Interrupts which should belong to the Power/WindowsButton

#### Lets use it the easy way

include `libgpiod` in your root filesystem\
type gpiomon \<BUS> \<Gpio-Number>\
Press the button a few times you should see the gpio chaning in realtime

![](https://2395005476-files.gitbook.io/~/files/v0/b/gitbook-legacy-files/o/assets%2F-MJQb34R_xPz0ll0scbM%2F-MMb5iVU4_WcGJqjmPX_%2F-MMb6DF_ELZ5BTMJFSIM%2Fimage.png?alt=media\&token=9a443cd3-d4dd-4474-8628-e3df816781a4)

#### Lets use it the hard way

first we must set the pins to GPIO mode. Tegra standard is SFIO mode.\
0x6000 d000 is the GPIO Controller base\
0x0000 0408 is PortS CNF register.\
We want to set pin 6/7 to GPIO mode -> 0b1100 0000 -> 0xC0

we can write memory with devmem\
`devmem 0x6000d408 8 0xC0`\
Now the GPIO Controller treats PS6/7 as GPIO

`cd /sys/class/gpio`\
`ls ./` should show `export, gpiochip0, unexport`\
gpiochip0 is the gpio controller; 0 tells the offset\
\
we can export a pin to userspace\
`echo 150 > export`\
`echo 151 > export`\
Now we can use PS6/7\
\
check the direction with\
`cat gpio150/direction`\
it should show `"in"`\
but you can set it with `echo in > gpio150/direction`\
\
Now we can read the value\
`cat gpio150/value`\
this should return `1`\
if you press VOL-Down it should return `0`\
This tells us that the pin is pulled up and active\_low.

Now remove the gpio from userspace that it can be used by drivers again\
`echo 150 > unexport`\
`echo 151 > unexport`
