diff --git a/nx/include/switch.h b/nx/include/switch.h index 3cf16f0e..822632d8 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -52,6 +52,7 @@ extern "C" { #include "switch/services/csrng.h" #include "switch/services/lbl.h" #include "switch/services/i2c.h" +#include "switch/services/gpio.h" #include "switch/services/bpc.h" #include "switch/services/pcv.h" #include "switch/services/psm.h" diff --git a/nx/include/switch/services/gpio.h b/nx/include/switch/services/gpio.h new file mode 100644 index 00000000..bf89952c --- /dev/null +++ b/nx/include/switch/services/gpio.h @@ -0,0 +1,38 @@ +/** + * @file i2c.h + * @brief I2C service IPC wrapper. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "sm.h" + +typedef enum { + GpioPadName_AudioCodec = 1, +} GpioPadName; + +typedef struct { + Service s; +} GpioPadSession; + +typedef enum { + GpioDirection_Input = 0, + GpioDirection_Output = 1, +} GpioDirection; + +typedef enum { + GpioValue_Low = 0, + GpioValue_High = 1, +} GpioValue; + +Result gpioInitialize(void); +void gpioExit(void); + +Result gpioOpenSession(GpioPadSession *out, GpioPadName name); + +Result gpioPadSetDirection(GpioPadSession *p, GpioDirection dir); +Result gpioPadGetDirection(GpioPadSession *p, GpioDirection *out); +Result gpioPadSetValue(GpioPadSession *p, GpioValue val); +Result gpioPadGetValue(GpioPadSession *p, GpioValue *out); +void gpioPadClose(GpioPadSession *p); \ No newline at end of file diff --git a/nx/source/services/gpio.c b/nx/source/services/gpio.c new file mode 100644 index 00000000..c0f08d84 --- /dev/null +++ b/nx/source/services/gpio.c @@ -0,0 +1,216 @@ +#include "types.h" +#include "result.h" +#include "arm/atomics.h" +#include "kernel/ipc.h" +#include "kernel/detect.h" +#include "services/gpio.h" +#include "services/sm.h" + +static Service g_gpioSrv; +static u64 g_refCnt; + +Result gpioInitialize(void) { + Result rc = 0; + + atomicIncrement64(&g_refCnt); + + if (serviceIsActive(&g_gpioSrv)) + return 0; + + rc = smGetService(&g_gpioSrv, "gpio"); + + if (R_FAILED(rc)) gpioExit(); + + return rc; +} + +void gpioExit(void) { + if (atomicDecrement64(&g_refCnt) == 0) { + serviceClose(&g_gpioSrv); + } +} + +Result gpioOpenSession(GpioPadSession *out, GpioPadName name) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 name; + } *raw; + + raw = serviceIpcPrepareHeader(&g_gpioSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + raw->name = name; + + Result rc = serviceIpcDispatch(&g_gpioSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_gpioSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreateSubservice(&out->s, &g_gpioSrv, &r, 0); + } + } + + return rc; +} + +Result gpioPadSetDirection(GpioPadSession *p, GpioDirection dir) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 dir; + } *raw; + + raw = serviceIpcPrepareHeader(&p->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 0; + raw->dir = dir; + + Result rc = serviceIpcDispatch(&p->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&p->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result gpioPadGetDirection(GpioPadSession *p, GpioDirection *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&p->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + + Result rc = serviceIpcDispatch(&p->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u8 dir; + } *resp; + + serviceIpcParse(&p->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *out = (GpioDirection)resp->dir; + } + } + + return rc; +} + +Result gpioPadSetValue(GpioPadSession *p, GpioValue val) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 val; + } *raw; + + raw = serviceIpcPrepareHeader(&p->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 8; + raw->val = val; + + Result rc = serviceIpcDispatch(&p->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u8 val; + } *resp; + + serviceIpcParse(&p->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +Result gpioPadGetValue(GpioPadSession *p, GpioValue *out) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&p->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 9; + + Result rc = serviceIpcDispatch(&p->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + u8 val; + } *resp; + + serviceIpcParse(&p->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + *out = (GpioValue)resp->val; + } + } + + return rc; +} + +void gpioPadClose(GpioPadSession *p) { + serviceClose(&p->s); +}