]> Chaos Git - corbenik/ctrulib.git/commitdiff
Added DSP service commands
authorLectem <lectem@gmail.com>
Tue, 8 Sep 2015 18:07:59 +0000 (14:07 -0400)
committerLectem <lectem@gmail.com>
Tue, 8 Sep 2015 18:25:51 +0000 (14:25 -0400)
libctru/include/3ds.h
libctru/include/3ds/services/dsp.h [new file with mode: 0644]
libctru/source/services/dsp.c [new file with mode: 0644]

index c2d3974aad665e1883175ca91bf4c8547a0bf27f..5d2f6d50c9e503ed6c5e7fa117a290a889e20b9c 100644 (file)
@@ -22,6 +22,7 @@ extern "C" {
 #include <3ds/services/cfgnor.h>
 #include <3ds/services/cfgu.h>
 #include <3ds/services/csnd.h>
+#include <3ds/services/dsp.h>
 #include <3ds/services/fs.h>
 #include <3ds/services/gsp.h>
 #include <3ds/services/hid.h>
diff --git a/libctru/include/3ds/services/dsp.h b/libctru/include/3ds/services/dsp.h
new file mode 100644 (file)
index 0000000..045c88d
--- /dev/null
@@ -0,0 +1,137 @@
+/**
+ * @file dsp.h
+ * @brief DSP Service to access the DSP processor commands (sound)
+ *
+ * The DSP has access to the Linear memory region, and to the DSP memory region if allowed in the exheader.
+ */
+#pragma once
+#include <3ds/types.h>
+
+typedef enum {
+       DSP_INTERRUPT_PIPE = 2
+} DSP_InterruptType;
+
+
+typedef enum {
+       DSP_PIPE_INPUT = 0, ///< DSP to ARM
+       DSP_PIPE_OUTPUT = 1 ///< ARM to DSP
+} DSP_PipeDirection;
+
+/**
+ * @brief Initializes the dsp service.
+ *
+ * Call this before calling any DSP_* function.
+ * @note This will also unload any previously loaded DSP binary.
+ *       It is done this way since you have to provide your binary when the 3DS leaves sleep mode anyway.
+ */
+Result dspInit(void);
+
+/**
+ * @brief Closes the dsp service.
+ * @note This will also unload the DSP binary.
+ */
+Result dspExit(void);
+
+///Checks if a headphone is inserted.
+Result DSP_GetHeadphoneStatus(bool* is_inserted);
+
+
+/**
+ * @brief Flushes the cache
+ * @param address   Beginning of the memory range to flush, inside the Linear or DSP memory regions
+ * @param size      Size of the memory range to flush
+ *
+ * Flushes the cache for the specified memory range and invalidates the cache
+ */
+Result DSP_FlushDataCache(u32 address, u32 size);
+
+/**
+ * @brief Invalidates the cache
+ * @param address   Beginning of the memory range to invalidate, inside the Linear or DSP memory regions
+ * @param size      Size of the memory range to flush
+ *
+ * Invalidates the cache for the specified memory range
+ */
+Result DSP_InvalidateDataCache(u32 address, u32 size);
+
+///Retrieves the handle of the DSP semaphore
+Result DSP_GetSemaphoreHandle(Handle* semaphore);
+
+///Sets the DSP hardware semaphore value
+Result DSP_SetSemaphore(u16 value);
+
+///Masks the DSP hardware semaphore value
+Result DSP_SetSemaphoreMask(u16 mask);
+
+/**
+ * @brief Loads a DSP binary and starts the DSP
+ * @param component The program file address in memory
+ * @param size      The size of the program
+ * @param prog_mask DSP memory block related ? Default is 0xff.
+ * @param data_mask DSP memory block related ? Default is 0xff.
+ * @param is_loaded Indicates if the DSP was succesfully loaded.
+ *
+ * @note The binary must be signed (http://3dbrew.org/wiki/DSP_Binary)
+ * @note Seems to be called when the 3ds leaves the Sleep mode
+ */
+Result DSP_LoadComponent(u8 const* component,u32 size,u16 prog_mask,u16 data_mask,bool * is_loaded);
+
+///Stops the DSP by unloading the binary
+Result DSP_UnloadComponent(void);
+
+/**
+ * @brief Registers an event handle with the DSP through IPC
+ * @param interrut The type of interrupt that will trigger the event. Usual value is DSP_INTERRUPT_PIPE.
+ * @param channel The pipe channel. Usual value is 2
+ *
+ * @note It is possible that interrupt are inverted
+ */
+Result DSP_RegisterInterruptEvents(Handle handle,u32 interrupt,u32 channel);
+
+
+/**
+ * @param channel ?????? TODO usually 2
+ * @param buffer The buffer that will store the values read from the pipe
+ * @param length Length of the buffer
+ * @param length_read Number of bytes read by the command
+ */
+Result DSP_ReadPipeIfPossible(u32 channel, u8 const *buffer, u16 length, u16* length_read);
+
+/**
+ * @param channel ?????? TODO usually 2
+ * @param buffer The message to send to the DSP process
+ * @param length Length of the message
+ */
+Result DSP_WriteProcessPipe(u32 channel,u8 const* buffer,u32 length);
+
+
+///Converts a DSP memory to a virtual address usable by the process
+Result DSP_ConvertProcessAddressFromDspDram(u32 dsp_address, u32 *arm_address);
+
+/**
+ * @brief Reads a DSP register
+ * @param regNo Offset of the hardware register, base address is 0x1EC40000
+ */
+Result DSP_RecvData(u16 regNo, u16 * value);
+
+/**
+ * @brief Checks if you can read a DSP register
+ * @param regNo Offset of the hardware register, base address is 0x1EC40000
+ *
+ * @warning This call might hang if the data is not ready. See @ref DSP_SendDataIsEmpty.
+ */
+Result DSP_RecvDataIsReady(u16 regNo, bool * is_ready);
+
+/**
+ * @brief Writes to a DSP register
+ * @param regNo Offset of the hardware register, base address is 0x1EC40000
+ *
+ * @warning This call might hang if the SendData is not empty. See @ref DSP_SendDataIsEmpty.
+ */
+Result DSP_SendData(u16 regNo, u16 value);
+
+/**
+ * @brief Checks if you can write to a DSP register ?
+ * @param regNo Offset of the hardware register, base address is 0x1EC40000
+ */
+Result DSP_SendDataIsEmpty(u16 regNo, bool * is_empty);
diff --git a/libctru/source/services/dsp.c b/libctru/source/services/dsp.c
new file mode 100644 (file)
index 0000000..05e006b
--- /dev/null
@@ -0,0 +1,254 @@
+#include <3ds/types.h>
+#include <3ds/svc.h>
+#include <3ds/srv.h>
+#include <3ds/ipc.h>
+#include <3ds/services/dsp.h>
+
+Handle dspHandle = 0;
+
+
+Result dspInit(void)
+{
+       Result ret = 0;
+
+       if (dspHandle == 0)
+       {
+               ret = srvGetServiceHandle(&dspHandle, "dsp::DSP");
+               if (ret < 0) return ret;
+       }
+       if (ret < 0) return ret;
+       DSP_UnloadComponent();
+       return 0;
+}
+
+Result dspExit(void)
+{
+       Result ret = 0;
+//No need to call unload, it will be done automatically by closing the handle
+       if (dspHandle != 0)
+       {
+               ret = svcCloseHandle(dspHandle);
+               if (ret < 0) return ret;
+               dspHandle = 0;
+       }
+
+       return 0;
+}
+
+
+Result DSP_GetHeadphoneStatus(bool* is_inserted)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x001F,0,0);
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       *is_inserted = cmdbuf[2] & 0xFF;
+       return cmdbuf[1];
+}
+
+
+Result DSP_FlushDataCache(u32 address, u32 size)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x13,2,2);
+       cmdbuf[1] = address;
+       cmdbuf[2] = size;
+       cmdbuf[3] = IPC_Desc_SharedHandles(1);
+       cmdbuf[4] = CUR_PROCESS_HANDLE;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+
+Result DSP_InvalidateDataCache(u32 address, u32 size)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x14,2,2);
+       cmdbuf[1] = address;
+       cmdbuf[2] = size;
+       cmdbuf[3] = IPC_Desc_SharedHandles(1);
+       cmdbuf[4] = CUR_PROCESS_HANDLE;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+
+
+Result DSP_SetSemaphore(u16 value)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x7,1,0);
+       cmdbuf[1] = value;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+
+
+Result DSP_SetSemaphoreMask(u16 mask)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x17,1,0);
+       cmdbuf[1] = mask;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+Result DSP_GetSemaphoreHandle(Handle* semaphore)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x16,0,0);
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       *semaphore = cmdbuf[3];
+       return cmdbuf[1];
+}
+
+Result DSP_LoadComponent(u8 const* component,u32 size,u16 prog_mask,u16 data_mask,bool * is_loaded)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x11,3,2);
+       cmdbuf[1] = size;
+       cmdbuf[2] = prog_mask;
+       cmdbuf[3] = data_mask;
+       cmdbuf[4] = IPC_Desc_Buffer(size,IPC_BUFFER_R);
+       cmdbuf[5] = (u32) component;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       *is_loaded = cmdbuf[2] & 0xFF;
+       return cmdbuf[1];
+}
+
+
+
+Result DSP_UnloadComponent(void)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x12,0,0);
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+Result DSP_RegisterInterruptEvents(Handle handle, u32 interrupt, u32 channel)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x15,2,2);
+       cmdbuf[1] = interrupt;
+       cmdbuf[2] = channel;
+       cmdbuf[3] = IPC_Desc_SharedHandles(1);
+       cmdbuf[4] = handle;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+
+Result DSP_ReadPipeIfPossibleEx(u32 channel,u32 unk1, u8 const *buffer, u16 length, u16* length_read)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x10,3,0);
+       cmdbuf[1] = channel;
+       cmdbuf[2] = unk1;
+       cmdbuf[3] = length;
+
+       u32 * staticbufs = cmdbuf + 0x100;
+
+       u32 saved1 = staticbufs[0x0];
+       u32 saved2 = staticbufs[0x4];
+
+       staticbufs[0] = (length<<14) | 2;
+       staticbufs[4] = (u32)buffer;
+
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+
+       staticbufs[0] = saved1;
+       staticbufs[4] = saved2;
+
+       *length_read = cmdbuf[2] & 0xFFFF;
+       return cmdbuf[1];
+}
+
+//TODO change DSP_ReadPipeIfPossibleEx into DSP_ReadPipeIfPossible once unk1 is figured out
+//However it seems that it is always used with value 0
+Result DSP_ReadPipeIfPossible(u32 channel, u8 const *buffer, u16 length, u16* length_read)
+{
+       return DSP_ReadPipeIfPossibleEx(channel,0,buffer,length, length_read);
+}
+
+Result DSP_WriteProcessPipe(u32 channel, u8 const *buffer, u32 length)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0xd,2,2);
+       cmdbuf[1] = channel;
+       cmdbuf[2] = length;
+       cmdbuf[3] = IPC_Desc_StaticBuffer(length,1);
+       cmdbuf[4] = (u32) buffer;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+Result DSP_ConvertProcessAddressFromDspDram(u32 dsp_address, u32 *arm_address)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0xc,1,0);
+       cmdbuf[1] = dsp_address;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       *arm_address = cmdbuf[2];
+       return cmdbuf[1];
+}
+
+Result DSP_RecvData(u16 regNo, u16 * value)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x1,1,0) ;
+       cmdbuf[1] = regNo;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       *value = cmdbuf[2] & 0xFFFF;
+       return cmdbuf[1];
+}
+
+Result DSP_RecvDataIsReady(u16 regNo, bool * is_ready)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x2,1,0);
+       cmdbuf[1] = regNo;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       *is_ready = cmdbuf[2] & 0xFF;
+       return cmdbuf[1];
+}
+
+
+// Writes data to the reg regNo
+// *(_WORD *)(8 * regNo + 0x1ED03024) = value
+Result DSP_SendData(u16 regNo, u16 value)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x3,2,0);
+       cmdbuf[1] = regNo;
+       cmdbuf[1] = value;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       return cmdbuf[1];
+}
+
+Result DSP_SendDataIsEmpty(u16 regNo, bool * is_empty)
+{
+       Result ret = 0;
+       u32* cmdbuf = getThreadCommandBuffer();
+       cmdbuf[0] = IPC_MakeHeader(0x4,1,0);
+       cmdbuf[1] = regNo;
+       if ((ret = svcSendSyncRequest(dspHandle)) != 0) return ret;
+       *is_empty = cmdbuf[2] & 0xFF;
+       return cmdbuf[1];
+}
+