diff --git a/nx/include/switch.h b/nx/include/switch.h index e170d43c..3cf16f0e 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -51,6 +51,7 @@ extern "C" { #include "switch/services/hwopus.h" #include "switch/services/csrng.h" #include "switch/services/lbl.h" +#include "switch/services/i2c.h" #include "switch/services/bpc.h" #include "switch/services/pcv.h" #include "switch/services/psm.h" diff --git a/nx/include/switch/services/i2c.h b/nx/include/switch/services/i2c.h new file mode 100644 index 00000000..d112f982 --- /dev/null +++ b/nx/include/switch/services/i2c.h @@ -0,0 +1,32 @@ +/** + * @file i2c.h + * @brief I2C service IPC wrapper. + * @author SciresM + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "sm.h" + +typedef enum { + I2cDevice_AudioCodec = 4, +} I2cDevice; + +typedef struct { + Service s; +} I2cSession; + +typedef enum { + I2cTransactionOption_Start = (1 << 0), + I2cTransactionOption_Stop = (1 << 1), + + I2cTransactionOption_All = I2cTransactionOption_Start | I2cTransactionOption_Stop, +} I2cTransactionOption; + +Result i2cInitialize(void); +void i2cExit(void); + +Result i2cOpenSession(I2cSession *out, I2cDevice dev); + +Result i2csessionSendAuto(I2cSession *s, void *buf, size_t size, I2cTransactionOption option); +void i2csessionClose(I2cSession *s); \ No newline at end of file diff --git a/nx/source/services/i2c.c b/nx/source/services/i2c.c new file mode 100644 index 00000000..e57685dc --- /dev/null +++ b/nx/source/services/i2c.c @@ -0,0 +1,112 @@ +#include "types.h" +#include "result.h" +#include "arm/atomics.h" +#include "kernel/ipc.h" +#include "kernel/detect.h" +#include "services/i2c.h" +#include "services/sm.h" + +static Service g_i2cSrv; +static size_t g_i2cSrvPtrBufSize; +static u64 g_refCnt; + +Result i2cInitialize(void) { + Result rc = 0; + + atomicIncrement64(&g_refCnt); + + if (serviceIsActive(&g_i2cSrv)) + return 0; + + rc = smGetService(&g_i2cSrv, "i2c"); + + if (R_SUCCEEDED(rc)) rc = ipcQueryPointerBufferSize(g_i2cSrv.handle, &g_i2cSrvPtrBufSize); + + if (R_FAILED(rc)) i2cExit(); + + return rc; +} + +void i2cExit(void) { + if (atomicDecrement64(&g_refCnt) == 0) { + g_i2cSrvPtrBufSize = 0; + serviceClose(&g_i2cSrv); + } +} + +Result i2cOpenSession(I2cSession *out, I2cDevice dev) { + IpcCommand c; + ipcInitialize(&c); + + struct { + u64 magic; + u64 cmd_id; + u32 device; + } *raw; + + raw = serviceIpcPrepareHeader(&g_i2cSrv, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 1; + raw->device = dev; + + Result rc = serviceIpcDispatch(&g_i2cSrv); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&g_i2cSrv, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc)) { + serviceCreateSubservice(&out->s, &g_i2cSrv, &r, 0); + } + } + + return rc; +} + +Result i2csessionSendAuto(I2cSession *s, void *buf, size_t size, I2cTransactionOption option) { + IpcCommand c; + ipcInitialize(&c); + ipcAddSendSmart(&c, g_i2cSrvPtrBufSize, buf, size, 0); + + struct { + u64 magic; + u64 cmd_id; + u32 option; + } *raw; + + raw = serviceIpcPrepareHeader(&s->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 10; + raw->option = option; + + Result rc = serviceIpcDispatch(&s->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + } *resp; + + serviceIpcParse(&s->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + } + + return rc; +} + +void i2csessionClose(I2cSession *s) { + serviceClose(&s->s); +}