From 0ff42ee69ef16b3a143cf9a20378914d0c4eb29c Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Mon, 26 Oct 2020 03:29:40 -0700 Subject: [PATCH] gpio: implement majority of the service --- nx/include/switch/services/gpio.h | 39 ++++++++- nx/source/services/gpio.c | 127 ++++++++++++++++++++++++++++++ 2 files changed, 162 insertions(+), 4 deletions(-) diff --git a/nx/include/switch/services/gpio.h b/nx/include/switch/services/gpio.h index f6129e3b..f0a80dd8 100644 --- a/nx/include/switch/services/gpio.h +++ b/nx/include/switch/services/gpio.h @@ -6,12 +6,14 @@ */ #pragma once #include "../types.h" +#include "../kernel/event.h" #include "../sf/service.h" typedef enum { - GpioPadName_AudioCodec = 1, - GpioPadName_ButtonVolUp = 25, + GpioPadName_AudioCodec = 1, + GpioPadName_ButtonVolUp = 25, GpioPadName_ButtonVolDown = 26, + GpioPadName_SdCd = 56, } GpioPadName; typedef struct { @@ -19,15 +21,28 @@ typedef struct { } GpioPadSession; typedef enum { - GpioDirection_Input = 0, + GpioDirection_Input = 0, GpioDirection_Output = 1, } GpioDirection; typedef enum { - GpioValue_Low = 0, + GpioValue_Low = 0, GpioValue_High = 1, } GpioValue; +typedef enum { + GpioInterruptMode_LowLevel = 0, + GpioInterruptMode_HighLevel = 1, + GpioInterruptMode_RisingEdge = 2, + GpioInterruptMode_FallingEdge = 3, + GpioInterruptMode_AnyEdge = 4, +} GpioInterruptMode; + +typedef enum { + GpioInterruptStatus_Inactive = 0, + GpioInterruptStatus_Active = 1, +} GpioInterruptStatus; + /// Initialize gpio. Result gpioInitialize(void); @@ -38,9 +53,25 @@ void gpioExit(void); Service* gpioGetServiceSession(void); Result gpioOpenSession(GpioPadSession *out, GpioPadName name); +Result gpioOpenSession2(GpioPadSession *out, u32 device_code, u32 access_mode); + +Result gpioIsWakeEventActive(bool *out, GpioPadName name); +Result gpioIsWakeEventActive2(bool *out, u32 device_code); Result gpioPadSetDirection(GpioPadSession *p, GpioDirection dir); Result gpioPadGetDirection(GpioPadSession *p, GpioDirection *out); +Result gpioPadSetInterruptMode(GpioPadSession *p, GpioInterruptMode mode); +Result gpioPadGetInterruptMode(GpioPadSession *p, GpioInterruptMode *out); +Result gpioPadSetInterruptEnable(GpioPadSession *p, bool en); +Result gpioPadGetInterruptEnable(GpioPadSession *p, bool *out); +Result gpioPadGetInterruptStatus(GpioPadSession *p, GpioInterruptStatus *out); +Result gpioPadClearInterruptStatus(GpioPadSession *p); Result gpioPadSetValue(GpioPadSession *p, GpioValue val); Result gpioPadGetValue(GpioPadSession *p, GpioValue *out); +Result gpioPadBindInterrupt(GpioPadSession *p, Event *out); +Result gpioPadUnbindInterrupt(GpioPadSession *p); +Result gpioPadSetDebounceEnabled(GpioPadSession *p, bool en); +Result gpioPadGetDebounceEnabled(GpioPadSession *p, bool *out); +Result gpioPadSetDebounceTime(GpioPadSession *p, s32 ms); +Result gpioPadGetDebounceTime(GpioPadSession *p, s32 *out); void gpioPadClose(GpioPadSession *p); diff --git a/nx/source/services/gpio.c b/nx/source/services/gpio.c index d8d45740..3b9d1ca2 100644 --- a/nx/source/services/gpio.c +++ b/nx/source/services/gpio.c @@ -1,6 +1,7 @@ #define NX_SERVICE_ASSUME_NON_DOMAIN #include "service_guard.h" #include "services/gpio.h" +#include "runtime/hosversion.h" static Service g_gpioSrv; @@ -18,6 +19,10 @@ Service* gpioGetServiceSession(void) { return &g_gpioSrv; } +static Result _gpioCmdNoInNoOut(Service *srv, u32 cmd_id) { + return serviceDispatch(srv, cmd_id); +} + static Result _gpioCmdInU32NoOut(Service *srv, u32 value, u32 cmd_id) { return serviceDispatchIn(srv, cmd_id, value); } @@ -26,7 +31,34 @@ static Result _gpioCmdNoInOutU32(Service *srv, u32 *out_value, u32 cmd_id) { return serviceDispatchOut(srv, cmd_id, *out_value); } +static Result _gpioCmdInBoolNoOut(Service *srv, bool value, u32 cmd_id) { + const u8 in = value; + return serviceDispatchIn(srv, cmd_id, in); +} + +static Result _gpioCmdNoInOutBool(Service *srv, bool *out_value, u32 cmd_id) { + u8 outval = 0; + Result rc = serviceDispatchOut(srv, cmd_id, outval); + if (R_SUCCEEDED(rc)) { + if (out_value) *out_value = outval & 1; + } + return rc; +} + +static Result _gpioCmdInU32OutBool(Service *srv, bool *out_value, u32 inval, u32 cmd_id) { + u8 outval = 0; + Result rc = serviceDispatchInOut(srv, cmd_id, inval, outval); + if (R_SUCCEEDED(rc)) { + if (out_value) *out_value = outval & 1; + } + return rc; +} + Result gpioOpenSession(GpioPadSession *out, GpioPadName name) { + if (hosversionAtLeast(7,0,0)) { + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + } + _Static_assert(sizeof(name) == sizeof(u32), "GpioPadName size"); return serviceDispatchIn(&g_gpioSrv, 1, name, .out_num_objects = 1, @@ -34,6 +66,39 @@ Result gpioOpenSession(GpioPadSession *out, GpioPadName name) { ); } +Result gpioOpenSession2(GpioPadSession *out, u32 device_code, u32 access_mode) { + if (hosversionBefore(7,0,0)) { + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + } + + const struct { + u32 device_code; + u32 access_mode; + } in = { device_code, access_mode }; + + return serviceDispatchIn(&g_gpioSrv, 7, in, + .out_num_objects = 1, + .out_objects = &out->s, + ); +} + +Result gpioIsWakeEventActive(bool *out, GpioPadName name) { + if (hosversionAtLeast(7,0,0)) { + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + } + + _Static_assert(sizeof(name) == sizeof(u32), "GpioPadName size"); + return _gpioCmdInU32OutBool(&g_gpioSrv, out, name, 3); +} + +Result gpioIsWakeEventActive2(bool *out, u32 device_code) { + if (hosversionBefore(7,0,0)) { + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + } + + return _gpioCmdInU32OutBool(&g_gpioSrv, out, device_code, 8); +} + Result gpioPadSetDirection(GpioPadSession *p, GpioDirection dir) { return _gpioCmdInU32NoOut(&p->s, dir, 0); } @@ -43,6 +108,33 @@ Result gpioPadGetDirection(GpioPadSession *p, GpioDirection *out) { return _gpioCmdNoInOutU32(&p->s, (u32 *)out, 1); } +Result gpioPadSetInterruptMode(GpioPadSession *p, GpioInterruptMode mode) { + return _gpioCmdInU32NoOut(&p->s, mode, 2); +} + +Result gpioPadGetInterruptMode(GpioPadSession *p, GpioInterruptMode *out) { + _Static_assert(sizeof(*out) == sizeof(u32), "GpioInterruptMode size"); + return _gpioCmdNoInOutU32(&p->s, (u32 *)out, 3); +} + +Result gpioPadSetInterruptEnable(GpioPadSession *p, bool en) { + return _gpioCmdInBoolNoOut(&p->s, en, 4); +} + +Result gpioPadGetInterruptEnable(GpioPadSession *p, bool *out) { + return _gpioCmdNoInOutBool(&p->s, out, 5); +} + +Result gpioPadGetInterruptStatus(GpioPadSession *p, GpioInterruptStatus *out) { + _Static_assert(sizeof(*out) == sizeof(u32), "GpioInterruptStatus size"); + return _gpioCmdNoInOutU32(&p->s, (u32 *)out, 6); +} + +Result gpioPadClearInterruptStatus(GpioPadSession *p) { + return _gpioCmdNoInNoOut(&p->s, 7); +} + + Result gpioPadSetValue(GpioPadSession *p, GpioValue val) { return _gpioCmdInU32NoOut(&p->s, val, 8); } @@ -52,6 +144,41 @@ Result gpioPadGetValue(GpioPadSession *p, GpioValue *out) { return _gpioCmdNoInOutU32(&p->s, (u32 *)out, 9); } +Result gpioPadBindInterrupt(GpioPadSession *p, Event *out) { + Handle evt_handle; + Result rc = serviceDispatch(&p->s, 10, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &evt_handle, + ); + + if (R_SUCCEEDED(rc)) { + eventLoadRemote(out, evt_handle, false); + } + + return rc; +} + +Result gpioPadUnbindInterrupt(GpioPadSession *p) { + return _gpioCmdNoInNoOut(&p->s, 11); +} + +Result gpioPadSetDebounceEnabled(GpioPadSession *p, bool en) { + return _gpioCmdInBoolNoOut(&p->s, en, 12); +} + +Result gpioPadGetDebounceEnabled(GpioPadSession *p, bool *out) { + return _gpioCmdNoInOutBool(&p->s, out, 13); +} + +Result gpioPadSetDebounceTime(GpioPadSession *p, s32 ms) { + return _gpioCmdInU32NoOut(&p->s, (u32)ms, 14); +} + +Result gpioPadGetDebounceTime(GpioPadSession *p, s32 *out) { + _Static_assert(sizeof(*out) == sizeof(u32), ""); + return _gpioCmdNoInOutU32(&p->s, (u32 *)out, 15); +} + void gpioPadClose(GpioPadSession *p) { serviceClose(&p->s); }