diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 9aeb842..141c6cc 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -79,6 +79,7 @@ extern "C" { #include <3ds/services/loader.h> #include <3ds/services/y2r.h> #include <3ds/services/mcuhwc.h> +#include <3ds/services/cdcchk.h> #include <3ds/gpu/gx.h> #include <3ds/gpu/gpu.h> diff --git a/libctru/include/3ds/services/cdcchk.h b/libctru/include/3ds/services/cdcchk.h new file mode 100644 index 0000000..8917f89 --- /dev/null +++ b/libctru/include/3ds/services/cdcchk.h @@ -0,0 +1,90 @@ +/** + * @file cdcchk.h + * @brief CODEC Hardware Check service. + */ +#pragma once + +#include <3ds/types.h> + +/// I2S line enumeration +typedef enum { + CODEC_I2S_LINE_1, ///< Primary I2S line, used by DSP/Mic (configurable)/GBA sound controller. + CODEC_I2S_LINE_2, ///< Secondary I2S line, used by CSND hardware. +} CodecI2sLine; + +/// Initializes CDCCHK. +Result cdcChkInit(void); + +/// Exits CDCCHK. +void cdcChkExit(void); + +/** + * @brief Gets a pointer to the current cdc:CHK session handle. + * @return A pointer to the current cdc:CHK session handle. + */ +Handle *cdcChkGetSessionHandle(void); + +/** + * @brief Reads multiple registers from the CODEC, using the old + * SPI hardware interface and a 4MHz baudrate. + * @param pageId CODEC Page ID. + * @param initialRegAddr Address of the CODEC register to start with. + * @param[out] outData Where to write the read data to. + * @param size Number of registers to read (bytes to read, max. 64). + */ +Result CDCCHK_ReadRegisters1(u8 pageId, u8 initialRegAddr, void *outData, size_t size); + +/** + * @brief Reads multiple registers from the CODEC, using the new + * SPI hardware interface and a 16MHz baudrate. + * @param pageId CODEC Page ID. + * @param initialRegAddr Address of the CODEC register to start with. + * @param[out] outData Where to read the data to. + * @param size Number of registers to read (bytes to read, max. 64). + */ +Result CDCCHK_ReadRegisters2(u8 pageId, u8 initialRegAddr, void *outData, size_t size); + +/** + * @brief Writes multiple registers to the CODEC, using the old + * SPI hardware interface and a 4MHz baudrate. + * @param pageId CODEC Page ID. + * @param initialRegAddr Address of the CODEC register to start with. + * @param data Where to read the data to write from. + * @param size Number of registers to write (bytes to read, max. 64). + */ +Result CDCCHK_WriteRegisters1(u8 pageId, u8 initialRegAddr, const void *data, size_t size); + +/** + * @brief Writes multiple registers to the CODEC, using the new + * SPI hardware interface and a 16MHz baudrate. + * @param pageId CODEC Page ID. + * @param initialRegAddr Address of the CODEC register to start with. + * @param data Where to read the data to write from. + * @param size Number of registers to write (bytes to read, max. 64). + */ +Result CDCCHK_WriteRegisters2(u8 pageId, u8 initialRegAddr, const void *data, size_t size); + +/** + * @brief Reads a single register from the NTR PMIC. + * @param[out] outData Where to read the data to (1 byte). + * @param regAddr Register address. + * @note The NTR PMIC is emulated by the CODEC hardware and sends + * IRQs to the MCU when relevant. + */ +Result CDCCHK_ReadNtrPmicRegister(u8 *outData, u8 regAddr); + +/** + * @brief Writes a single register from the NTR PMIC. + * @param regAddr Register address. + * @param data Data to write (1 byte). + * @note The NTR PMIC is emulated by the CODEC hardware and sends + * IRQs to the MCU when relevant. + */ +Result CDCCHK_WriteNtrPmicRegister(u8 regAddr, u8 data); + +/** + * @brief Sets the DAC volume level for the specified I2S line. + * @param i2sLine I2S line to set the volume for. + * @param volume Volume level (-128 to 0). +*/ +Result CDCCHK_SetI2sVolume(CodecI2sLine i2sLine, s8 volume); diff --git a/libctru/source/services/cdcchk.c b/libctru/source/services/cdcchk.c new file mode 100644 index 0000000..5491058 --- /dev/null +++ b/libctru/source/services/cdcchk.c @@ -0,0 +1,162 @@ +#include +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/srv.h> +#include <3ds/synchronization.h> +#include <3ds/ipc.h> +#include <3ds/services/cdcchk.h> + +static Handle cdcChkHandle; +static int cdcChkRefCount; + +Result cdcChkInit(void) +{ + if (AtomicPostIncrement(&cdcChkRefCount)) + return 0; + + Result res = srvGetServiceHandle(&cdcChkHandle, "cdc:CHK"); + if (R_FAILED(res)) + AtomicDecrement(&cdcChkRefCount); + return res; +} + +void cdcChkExit(void) +{ + if (AtomicDecrement(&cdcChkRefCount)) + return; + svcCloseHandle(cdcChkHandle); +} + +Handle *cdcChkGetSessionHandle(void) +{ + return &cdcChkHandle; +} + +Result CDCCHK_ReadRegisters1(u8 pageId, u8 initialRegAddr, void *outData, size_t size) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + u32 *staticbufs = getThreadStaticBuffers(); + + u32 staticbufBackup[2]; + memcpy(staticbufBackup, staticbufs, 8); + + staticbufs[0] = IPC_Desc_StaticBuffer(size, 0); + staticbufs[1] = (u32)outData; + + cmdbuf[0] = IPC_MakeHeader(1, 3, 0); // 0x100C0 + cmdbuf[1] = pageId; + cmdbuf[2] = initialRegAddr; + cmdbuf[3] = size; + + ret = svcSendSyncRequest(cdcChkHandle); + + memcpy(staticbufs, staticbufBackup, 8); + + return R_SUCCEEDED(ret) ? cmdbuf[1] : ret; +} + +Result CDCCHK_ReadRegisters2(u8 pageId, u8 initialRegAddr, void *outData, size_t size) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + u32 *staticbufs = getThreadStaticBuffers(); + + u32 staticbufBackup[2]; + memcpy(staticbufBackup, staticbufs, 8); + + staticbufs[0] = IPC_Desc_StaticBuffer(size, 0); + staticbufs[1] = (u32)outData; + + cmdbuf[0] = IPC_MakeHeader(2, 3, 0); // 0x200C0 + cmdbuf[1] = pageId; + cmdbuf[2] = initialRegAddr; + cmdbuf[3] = size; + + ret = svcSendSyncRequest(cdcChkHandle); + + memcpy(staticbufs, staticbufBackup, 8); + + return R_SUCCEEDED(ret) ? cmdbuf[1] : ret; +} + +Result CDCCHK_WriteRegisters1(u8 pageId, u8 initialRegAddr, const void *data, size_t size) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(3, 3, 2); // 0x300C2 + cmdbuf[1] = pageId; + cmdbuf[2] = initialRegAddr; + cmdbuf[3] = size; + cmdbuf[4] = IPC_Desc_StaticBuffer(size, 0); + cmdbuf[5] = (u32)data; + + ret = svcSendSyncRequest(cdcChkHandle); + + return R_SUCCEEDED(ret) ? cmdbuf[1] : ret; +} + +Result CDCCHK_WriteRegisters2(u8 pageId, u8 initialRegAddr, const void *data, size_t size) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(4, 3, 2); // 0x400C2 + cmdbuf[1] = pageId; + cmdbuf[2] = initialRegAddr; + cmdbuf[3] = size; + cmdbuf[4] = IPC_Desc_StaticBuffer(size, 0); + cmdbuf[5] = (u32)data; + + ret = svcSendSyncRequest(cdcChkHandle); + + return R_SUCCEEDED(ret) ? cmdbuf[1] : ret; +} + +Result CDCCHK_ReadNtrPmicRegister(u8 *outData, u8 regAddr) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(5, 1, 0); // 0x50040 + cmdbuf[1] = regAddr; + + ret = svcSendSyncRequest(cdcChkHandle); + if (R_SUCCEEDED(ret)) + { + *outData = (u8)cmdbuf[2]; + ret = cmdbuf[1]; + } + + return ret; +} + +Result CDCCHK_WriteNtrPmicRegister(u8 regAddr, u8 data) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(6, 2, 0); // 0x60080 + cmdbuf[1] = regAddr; + cmdbuf[2] = data; + + ret = svcSendSyncRequest(cdcChkHandle); + + return R_SUCCEEDED(ret) ? cmdbuf[1] : ret; +} + +Result CDCCHK_SetI2sVolume(CodecI2sLine i2sLine, s8 volume) +{ + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(7, 2, 0); // 0x70080 + cmdbuf[1] = (u32)i2sLine; + cmdbuf[2] = volume; + + ret = svcSendSyncRequest(cdcChkHandle); + + return R_SUCCEEDED(ret) ? cmdbuf[1] : ret; +}