diff --git a/libctru/include/3ds/services/pxidev.h b/libctru/include/3ds/services/pxidev.h new file mode 100644 index 0000000..96144e8 --- /dev/null +++ b/libctru/include/3ds/services/pxidev.h @@ -0,0 +1,79 @@ +/** + * @file pxidev.h + * @brief Gamecard PXI service. + */ +#pragma once + +#include <3ds/services/fs.h> + +/// Card SPI wait operation type. +typedef enum { + WAIT_NONE = 0, ///< Do not wait. + WAIT_SLEEP = 1, ///< Sleep for the specified number of nanoseconds. + WAIT_IREQ_RETURN = 2, ///< Wait for IREQ, return if timeout. + WAIT_IREQ_CONTINUE = 3 ///< Wait for IREQ, continue if timeout. +} PXIDEV_WaitType; + +/// Card SPI register deassertion type. +typedef enum { + DEASSERT_NONE = 0, ///< Do not deassert. + DEASSERT_BEFORE_WAIT = 1, ///< Deassert before waiting. + DEASSERT_AFTER_WAIT = 2 ///< Deassert after waiting. +} PXIDEV_DeassertType; + +/// Card SPI transfer buffer. +typedef struct { + void* ptr; ///< Data pointer. + u32 size; ///< Data size. + u8 transferOption; ///< Transfer options. See @ref pxiDevMakeTransferOption + u64 waitOperation; ///< Wait operation. See @ref pxiDevMakeWaitOperation +} PXIDEV_SPIBuffer; + +/// Initializes pxi:dev. +Result pxiDevInit(void); + +/// Shuts down pxi:dev. +void pxiDevExit(void); + +/** + * @brief Creates a packed card SPI transfer option value. + * @param baudRate Baud rate to use when transferring. + * @param busMode Bus mode to use when transferring. + * @return A packed card SPI transfer option value. + */ +static inline u8 pxiDevMakeTransferOption(FS_CardSpiBaudRate baudRate, FS_CardSpiBusMode busMode) +{ + return (baudRate & 0x3F) | ((busMode & 0x3) << 6); +} + +/** + * @brief Creates a packed card SPI wait operation value. + * @param waitType Type of wait to perform. + * @param deassertType Type of register deassertion to perform. + * @param timeout Timeout, in nanoseconds, to wait, if applicable. + * @return A packed card SPI wait operation value. + */ +static inline u64 pxiDevMakeWaitOperation(PXIDEV_WaitType waitType, PXIDEV_DeassertType deassertType, u64 timeout) +{ + return (waitType & 0xF) | ((deassertType & 0xF) << 4) | ((timeout & 0xFFFFFFFFFFFFFF) << 8); +} + +/** + * @brief Performs multiple card SPI writes and reads. + * @param header Header to lead the transfers with. Must be, at most, 8 bytes in size. + * @param writeBuffer1 Buffer to make first transfer from. + * @param readBuffer1 Buffer to receive first response to. + * @param writeBuffer2 Buffer to make second transfer from. + * @param readBuffer2 Buffer to receive second response to. + * @param footer Footer to follow the transfers with. Must be, at most, 8 bytes in size. Wait operation is unused. + */ +Result PXIDEV_SPIMultiWriteRead(PXIDEV_SPIBuffer* header, PXIDEV_SPIBuffer* writeBuffer1, PXIDEV_SPIBuffer* readBuffer1, PXIDEV_SPIBuffer* writeBuffer2, PXIDEV_SPIBuffer* readBuffer2, PXIDEV_SPIBuffer* footer); + +/** + * @brief Performs a single card SPI write and read. + * @param bytesRead Pointer to output the number of bytes received to. + * @param initialWaitOperation Wait operation to perform before transferring data. + * @param writeBuffer Buffer to transfer data from. + * @param readBuffer Buffer to receive data to. + */ +Result PXIDEV_SPIWriteRead(u32* bytesRead, u64 initialWaitOperation, PXIDEV_SPIBuffer* writeBuffer, PXIDEV_SPIBuffer* readBuffer); diff --git a/libctru/source/services/pxidev.c b/libctru/source/services/pxidev.c new file mode 100644 index 0000000..55d608c --- /dev/null +++ b/libctru/source/services/pxidev.c @@ -0,0 +1,101 @@ +#include +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/srv.h> +#include <3ds/synchronization.h> +#include <3ds/services/pxidev.h> +#include <3ds/ipc.h> + +static Handle pxiDevHandle; +static int pxiDevRefCount; + +Result pxiDevInit(void) +{ + Result ret; + + if (AtomicPostIncrement(&pxiDevRefCount)) return 0; + + ret = srvGetServiceHandle(&pxiDevHandle, "pxi:dev"); + if (R_FAILED(ret)) AtomicDecrement(&pxiDevRefCount); + + return ret; +} + +void pxiDevExit(void) +{ + if (AtomicDecrement(&pxiDevRefCount)) return; + svcCloseHandle(pxiDevHandle); +} + +Result PXIDEV_SPIMultiWriteRead(PXIDEV_SPIBuffer* header, PXIDEV_SPIBuffer* writeBuffer1, PXIDEV_SPIBuffer* readBuffer1, PXIDEV_SPIBuffer* writeBuffer2, PXIDEV_SPIBuffer* readBuffer2, PXIDEV_SPIBuffer* footer) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xD,26,8); // 0x000D0688 + memcpy(&cmdbuf[1], header->ptr, header->size); + cmdbuf[3] = header->size; + cmdbuf[4] = header->transferOption; + cmdbuf[5] = (u32) (header->waitOperation & 0xFFFFFFFF); + cmdbuf[6] = (u32) ((header->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[7] = writeBuffer1->size; + cmdbuf[8] = writeBuffer1->transferOption; + cmdbuf[9] = (u32) (writeBuffer1->waitOperation & 0xFFFFFFFF); + cmdbuf[10] = (u32) ((writeBuffer1->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[11] = readBuffer1->size; + cmdbuf[12] = readBuffer1->transferOption; + cmdbuf[13] = (u32) (readBuffer1->waitOperation & 0xFFFFFFFF); + cmdbuf[14] = (u32) ((readBuffer1->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[15] = writeBuffer2->size; + cmdbuf[16] = writeBuffer2->transferOption; + cmdbuf[17] = (u32) (writeBuffer2->waitOperation & 0xFFFFFFFF); + cmdbuf[18] = (u32) ((writeBuffer2->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[19] = readBuffer2->size; + cmdbuf[20] = readBuffer2->transferOption; + cmdbuf[21] = (u32) (readBuffer2->waitOperation & 0xFFFFFFFF); + cmdbuf[22] = (u32) ((readBuffer2->waitOperation >> 32) & 0xFFFFFFFF); + memcpy(&cmdbuf[23], footer->ptr, footer->size); + cmdbuf[25] = footer->size; + cmdbuf[26] = footer->transferOption; + cmdbuf[27] = IPC_Desc_PXIBuffer(writeBuffer1->size, 0, true); + cmdbuf[28] = (u32) writeBuffer1->ptr; + cmdbuf[29] = IPC_Desc_PXIBuffer(writeBuffer2->size, 1, true); + cmdbuf[30] = (u32) writeBuffer2->ptr; + cmdbuf[31] = IPC_Desc_PXIBuffer(readBuffer1->size, 2, false); + cmdbuf[32] = (u32) readBuffer1->ptr; + cmdbuf[33] = IPC_Desc_PXIBuffer(readBuffer2->size, 3, false); + cmdbuf[34] = (u32) readBuffer2->ptr; + + if (R_FAILED(ret = svcSendSyncRequest(pxiDevHandle))) return ret; + + return (Result)cmdbuf[1]; +} + +Result PXIDEV_SPIWriteRead(u32* bytesRead, u64 initialWaitOperation, PXIDEV_SPIBuffer* writeBuffer, PXIDEV_SPIBuffer* readBuffer) +{ + Result ret=0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xE,10,4); // 0x000E0284 + cmdbuf[1] = (u32) (initialWaitOperation & 0xFFFFFFFF); + cmdbuf[2] = (u32) ((initialWaitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[3] = writeBuffer->size; + cmdbuf[4] = writeBuffer->transferOption; + cmdbuf[5] = (u32) (writeBuffer->waitOperation & 0xFFFFFFFF); + cmdbuf[6] = (u32) ((writeBuffer->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[7] = readBuffer->size; + cmdbuf[8] = readBuffer->transferOption; + cmdbuf[9] = (u32) (readBuffer->waitOperation & 0xFFFFFFFF); + cmdbuf[10] = (u32) ((readBuffer->waitOperation >> 32) & 0xFFFFFFFF); + cmdbuf[11] = IPC_Desc_PXIBuffer(writeBuffer->size, 0, true); + cmdbuf[12] = (u32) writeBuffer->ptr; + cmdbuf[13] = IPC_Desc_PXIBuffer(readBuffer->size, 1, false); + cmdbuf[14] = (u32) readBuffer->ptr; + + if (R_FAILED(ret = svcSendSyncRequest(pxiDevHandle))) return ret; + + if (bytesRead) *bytesRead = cmdbuf[2]; + + return (Result)cmdbuf[1]; +}