From e1c9ed29a5bece8a733559ea638aeec2c9ab24d1 Mon Sep 17 00:00:00 2001 From: syelan34 <82814680+syelan34@users.noreply.github.com> Date: Mon, 3 Mar 2025 22:05:59 -0500 Subject: [PATCH 01/26] Create iruser.c --- libctru/source/services/iruser.c | 445 +++++++++++++++++++++++++++++++ 1 file changed, 445 insertions(+) create mode 100644 libctru/source/services/iruser.c diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c new file mode 100644 index 000000000..453920416 --- /dev/null +++ b/libctru/source/services/iruser.c @@ -0,0 +1,445 @@ +/** +Copy of the IR:USER API from ctru-rs +*/ + +#include "ir_user.h" +#include "defines.h" +#include "console.h" +#include <3ds.h> +#include +#include +#include +#include + +// Misc constants +const size_t SHARED_MEM_INFO_SECTIONS_SIZE = 0x30; +const size_t SHARED_MEM_RECV_BUFFER_OFFSET = 0x20; +const size_t PAGE_SIZE = 0x1000; +const u32 IR_BITRATE = 4; +const u8 CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID = 0x10; + +static Handle iruserHandle; +static Handle iruserSharedMemHandle; +static u32 *iruserSharedMem; +static u32 iruserSharedMemSize; +static int iruserRefCount; +static u32 iruserRecvBufferSize; +static u32 iruserRecvPacketCount; + +namespace { + inline size_t round_up(size_t value, size_t multiple) { + return (value / multiple + 1) * multiple; + } +} + +Result iruserInit(size_t buffer_size, size_t packet_count) { + if(AtomicPostIncrement(&iruserRefCount)) return 0; + Result ret = srvGetServiceHandle(&iruserHandle, "ir:USER"); + if (R_FAILED(ret)) goto cleanup0; + + // Calculate the shared memory size. + // Shared memory length must be a multiple of the page size. + iruserSharedMemSize = round_up(SHARED_MEM_INFO_SECTIONS_SIZE + buffer_size + buffer_size, PAGE_SIZE); + iruserSharedMem = (u32*)memalign(iruserSharedMemSize, PAGE_SIZE); + + ret = svcCreateMemoryBlock(&iruserSharedMemHandle, (u32)iruserSharedMem, iruserSharedMemSize, MEMPERM_READ, MEMPERM_READWRITE); + if (R_FAILED(ret)) goto cleanup1; + + ret = IRUSER_InitializeIrNopShared(buffer_size, packet_count, buffer_size, packet_count, IR_BITRATE); + if (R_FAILED(ret)) goto cleanup2; + + iruserRecvBufferSize = buffer_size; + iruserRecvPacketCount = packet_count; + + cleanup2: + IRUSER_FinalizeIrNop(); + + cleanup1: + free(iruserSharedMem); + svcCloseHandle(iruserSharedMemHandle); + + cleanup0: + return ret; +} + +void iruserExit(void) { + if(AtomicDecrement(&iruserRefCount)) return; + + IRUSER_FinalizeIrNop(); + svcCloseHandle(iruserHandle); + svcCloseHandle(iruserSharedMemHandle); + + iruserHandle = 0; + iruserSharedMemHandle = 0; +} + + + +Result IRUSER_InitializeIrNop(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x1, 6, 2); // 0x00010182 + cmdbuf[1] = iruserSharedMemSize; + cmdbuf[2] = recv_buffer_size; + cmdbuf[3] = recv_packet_count; + cmdbuf[4] = send_buffer_size; + cmdbuf[5] = send_packet_count; + cmdbuf[6] = bitrate; + cmdbuf[7] = IPC_Desc_SharedHandles(1); + cmdbuf[8] = iruserSharedMemHandle; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +Result IRUSER_FinalizeIrNop() { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x2, 0, 0); // 0x00020000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +}; + +Result IRUSER_ClearReceiveBuffer() { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x3, 0, 0); // 0x00030000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +}; + +Result IRUSER_ClearSendBuffer() { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x4, 0, 0); // 0x00040000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +}; + +Result IRUSER_WaitConnection() { + // 3 params + // what could they be + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_RequireConnection(u8 device_id) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + ql::Console::log("cmdbuf: %p", cmdbuf); + + cmdbuf[0] = IPC_MakeHeader(0x6, 1, 0); // 0x00060040 + cmdbuf[1] = device_id; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +Result IRUSER_AutoConnection() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_AnyConnection() { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x8, 0, 0); // 0x00080000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +}; + +Result IRUSER_Disconnect() { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x9, 0, 0); // 0x00090000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +Result IRUSER_GetReceiveEvent(Handle* eventhandle) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xA, 0, 0); // 0x000A0000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *eventhandle = cmdbuf[3]; + + return ret; +} + +Result IRUSER_GetSendEvent(Handle* eventhandle) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xB, 0, 0); // 0x000B0000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *eventhandle = cmdbuf[3]; + + return ret; +} + +Result IRUSER_GetConnectionStatusEvent(Handle* eventhandle) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xC, 0, 0); // 0x000C0000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *eventhandle = cmdbuf[3]; + + return ret; +} + +Result IRUSER_SendIrNop(u32 size, u8* inbufptr) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xD, 1, 2); // 0x000D0042 + cmdbuf[1] = size; + cmdbuf[2] = (size << 14) | 2; + cmdbuf[3] = (u32)inbufptr; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +Result IRUSER_SendIrNopLarge(u32 size, u8* inbufptr) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0xE, 1, 2); // 0x000E0042 + cmdbuf[1] = size; + cmdbuf[2] = (size << 8) | 10; + cmdbuf[3] = (u32)inbufptr; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +Result IRUSER_ReceiveIrNop() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} +Result IRUSER_ReceiveIrNopLarge() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_GetLatestReceiveErrorResult() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_GetLatestSendErrorResult() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_GetConnectionStatus() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_GetTryingToConnectStatus() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_GetReceiveSizeFreeAndUsed() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_GetSendSizeFreeAndUsed() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_GetConnectionRole() { + return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +} + +Result IRUSER_InitializeIrNopShared(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x18, 6, 2); // 0x00180182 + cmdbuf[1] = iruserSharedMemSize; + cmdbuf[2] = recv_buffer_size; + cmdbuf[3] = recv_packet_count; + cmdbuf[4] = send_buffer_size; + cmdbuf[5] = send_packet_count; + cmdbuf[6] = bitrate; + cmdbuf[7] = IPC_Desc_SharedHandles(1); + cmdbuf[8] = iruserSharedMemHandle; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +Result IRUSER_ReleaseReceivedData(u32 packet_count) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x19, 1, 0); // 0x00190040 + cmdbuf[1] = packet_count; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +Result IRUSER_SetOwnMachineId(u8 id) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x1A, 1, 0); // 0x001A0040 + cmdbuf[1] = id; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; +} + +/// This will let you directly read the ir:USER shared memory via a callback. +void iruserProcessSharedMemory(void(*process_fn)(u8*)) { + process_fn((u8*)iruserSharedMem); +} + +/// Read and parse the ir:USER service status data from shared memory. +IrUserStatusInfo iruserGetStatusInfo() { + void* shared_mem = iruserSharedMem; + IrUserStatusInfo status_info; + // copy over data + memcpy(&status_info, shared_mem, sizeof(status_info)); + + return status_info; +} + +Result iruserGetCirclePadProState(circlePadProInputResponse* response) { + Result ret; + IrUserPacket* packet = iruserGetPackets(&ret); + if (R_FAILED(ret)) return ret; + if (packet->payload_length != 6) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_SIZE); + if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_ENUM_VALUE); + *response = circlePadProInputResponse { + .c_stick_x = (u16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)), + .c_stick_y = (u16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)), + .status_raw = packet->payload[4], + .unknown_field = packet->payload[5], + }; + free(packet); + return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS); +} + +Result iruserCirclePadProRead(circlePosition *pos) { + Result ret; + IrUserPacket* packet = iruserGetPackets(&ret); + if (R_FAILED(ret)) return ret; + if (packet->payload_length != 6) return RS_INVALIDSTATE; + if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; + *pos = circlePosition { + .dx = (s16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)), + .dy = (s16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)) + }; + free(packet); + return RS_SUCCESS; +} + +/// Read and parse the current packets received from the IR device. +IrUserPacket* iruserGetPackets(Result* res) { + void* shared_mem = iruserSharedMem; + + // Find where the packets are, and how many + u32 start_index = *(u32*)((u8*)shared_mem + 0x10); + u32 valid_packet_count = *(u32*)((u8*)shared_mem + 0x18); + + IrUserPacket* packets = (IrUserPacket*)malloc(valid_packet_count * sizeof(IrUserPacket)); + + // Parse the packets + for (size_t i = 0; i < valid_packet_count; i++) { + u32 packet_index = (i + start_index) % iruserRecvPacketCount; + u32 packet_info_offset = SHARED_MEM_RECV_BUFFER_OFFSET + (packet_index * PACKET_INFO_SIZE); + u8* packet_info = (u8*)shared_mem + packet_info_offset; + u32 offset_to_data_buffer = *(u32*)packet_info; + u32 data_length = *(u32*)(packet_info + 4); + u32 packet_info_section_size = iruserRecvPacketCount * PACKET_INFO_SIZE; + u32 header_size = SHARED_MEM_RECV_BUFFER_OFFSET + packet_info_section_size; + u32 data_buffer_size = iruserRecvBufferSize - packet_info_section_size; + auto packet_data = [=](u32 idx) -> u8 { + u32 data_buffer_offset = offset_to_data_buffer + idx; + return *(u8*)((u8*)shared_mem + header_size + data_buffer_offset % data_buffer_size); + }; + u32 payload_length, payload_offset; + if ((packet_data(2) & 0x40 )!= 0) { + // Big payload + payload_length = ((packet_data(2) & 0x3F) << 8) + packet_data(3); + payload_offset = 4; + } else { + // Small payload + payload_length = packet_data(2) & 0x3F; + payload_offset = 3; + } + if (data_length != payload_offset + payload_length + 1) { + *res = RS_INVALIDSTATE; + return NULL; + } + if (packet_data(0) != 0xA5) { + *res = RS_INVALIDSTATE; + return NULL; + } + packets[i] = IrUserPacket { + .magic_number = packet_data(0), + .destination_network_id = packet_data(1), + .payload_length = payload_length, + .payload = (u8*)payload_offset, + .checksum = packet_data(payload_offset + payload_length), + }; + } + return packets; +} + +/// Circle Pad Pro specific request. +/// +/// This will send a packet to the CPP requesting it to send back packets +/// with the current device input values. +Result iruserCPPRequestInputPolling(u8 period_ms) { + u8 ir_request[3] = { + 1, + period_ms, + static_cast((period_ms + 2) << 2) + }; + return IRUSER_SendIrNop(sizeof(ir_request), ir_request); +} + +Handle iruserGetServHandle() { + return iruserHandle; +} From 59134cf3a9ec89cd193424ad29be60c09e04a0ac Mon Sep 17 00:00:00 2001 From: syelan34 <82814680+syelan34@users.noreply.github.com> Date: Mon, 3 Mar 2025 22:06:29 -0500 Subject: [PATCH 02/26] Create iruser.h --- libctru/include/3ds/services/iruser.h | 226 ++++++++++++++++++++++++++ 1 file changed, 226 insertions(+) create mode 100644 libctru/include/3ds/services/iruser.h diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h new file mode 100644 index 000000000..f342e9919 --- /dev/null +++ b/libctru/include/3ds/services/iruser.h @@ -0,0 +1,226 @@ +#pragma once + +#include "3ds/services/hid.h" +#include "3ds/types.h" + +/// Connection status values for [`IrUserStatusInfo`]. +enum ConnectionStatus { + Disconnected = 0, /// Device is not connected + Connecting = 1, /// Waiting for device to connect + Connected = 2, /// Device is connected +}; + +/// This struct holds a parsed copy of the ir:USER service status (from shared memory). +struct IrUserStatusInfo { + /// The result of the last receive operation. + Result recv_err_result; + /// The result of the last send operation. + Result send_err_result; + /// The current connection status. + ConnectionStatus connection_status; + /// The status of the connection attempt. + u8 trying_to_connect_status; + /// The role of the device in the connection (value meaning is unknown). + u8 connection_role; + /// The machine ID of the device. + u8 machine_id; + /// Unknown field. + u8 unknown_field_1; + /// The network ID of the connection. + u8 network_id; + /// Unknown field. + u8 unknown_field_2; + /// Unknown field. + u8 unknown_field_3; +}; + +/// A packet of data sent/received to/from the IR device. +struct IrUserPacket { + /// The magic number of the packet. Should always be 0xA5. + u8 magic_number; + /// The destination network ID. + u8 destination_network_id; + /// The length of the payload. + size_t payload_length; + /// The payload data. + u8* payload; + /// The checksum of the packet. + u8 checksum; +}; + +/// Circle Pad Pro response packet holding the current device input signals and status. +struct circlePadProInputResponse { + union { + struct { + /// The X value of the C-stick. + u16 c_stick_x; + /// The Y value of the C-stick. + u16 c_stick_y; + }; + circlePosition csPos; + } cstick; + union { + u8 status_raw; + struct { + /// Whether the ZL button is pressed. + bool zl_pressed : 1; + /// Whether the ZR button is pressed. + bool zr_pressed : 1; + /// Whether the R button is pressed. + bool r_pressed : 1; + /// The battery level of the Circle Pad Pro. + u8 battery_level : 5; + } status; + }; + /// Unknown field. + u8 unknown_field; +}; + +/** + * @brief Initializes IRUSER. + * Allocates memory to use as shared_memory based on recv_buffer_size and send_buffer_size and sets it as read only. + * + */ +Result iruserInit(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count); + +/// Shuts down IRUSER. Frees shared memory. +void iruserExit(); + +/// Gets IRUSER service handle +Handle iruserGetServHandle(); + + +IrUserStatusInfo iruserGetStatusInfo(); + +/** + * @brief Circle Pad Pro specific request. + * This will send a packet to the CPP requesting it to send back packets with the current device input values. + * @param period_ms Period at which to send another packet. +*/ +Result iruserCPPRequestInputPolling(u8 period_ms); + +/// This will let you directly read the ir:USER shared memory via a callback. +void iruserProcessSharedMemory(void(*process_fn)(u8*)); + +/** + * @brief Gets circle pad pro inputs state + * @param response Pointer to write data to + */ +Result iruserGetCirclePadProState(circlePadProInputResponse* response); + +/** + * @brief Reads c-stick position from circle pad pro + * @param pos Pointer to write data to + */ +Result iruserCirclePadProCStickRead(circlePosition* pos); + + +IrUserPacket* iruserGetPackets(Result* res); + +/** + * @brief Initializes the IR session + * @brief IR uses shared memory in non-shared mode (puts less information in shared memory) + * @param recv_buffer_size Size of receiving buffer + * @param recv_packet_count + * @param send_buffer_size + * @param send_packet_count + * @param bitrate + */ +Result IRUSER_InitializeIrNop(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate); + +/// Shuts down the IR session +Result IRUSER_FinalizeIrNop(); + +/// Clears receive buffer +Result IRUSER_ClearReceiveBuffer(); + +/// Clears send buffer +Result IRUSER_ClearSendBuffer(); + +/// Unknown. +Result IRUSER_WaitConnection(); + +/** + * @brief Attempts to connect to device with given id. + * @param device_id ID of the device to connect to. + */ +Result IRUSER_RequireConnection(u8 device_id); + +/// Unknown. +Result IRUSER_AutoConnection(); + +/// Connects to any device +Result IRUSER_AnyConnection(); + +/// Closes the current IR connection. +Result IRUSER_Disconnect(); + +/** + * @brief Gets an event handle that activates when a packet is received. + * @param eventhandle Pointer to write the event handle to. + */ +Result IRUSER_GetReceiveEvent(Handle* eventhandle); + +/** + * @brief Gets an event handle that activates when a packet is sent. + * @param eventhandle Pointer to write the event handle to. + */ +Result IRUSER_GetSendEvent(Handle* eventhandle); + +/** + * @brief Gets an event handle that activates on connection status change. + * @param eventhandle Pointer to write the event handle to. + */ +Result IRUSER_GetConnectionStatusEvent(Handle* eventhandle); + +/** + * @brief Sends data to connected device + * @brief Should be used when `size <= 0xFC` + * @param size Size of the buffer + * @param inbufptr Buffer to send + */ +Result IRUSER_SendIrNop(u32 size, u8* inbufptr); + +/** + * @brief Sends data to connected device + * @brief Should be used when `size > 0xFC` + * @param size Size of the buffer + * @param inbufptr Buffer to send + */ +Result IRUSER_SendIrNopLarge(u32 size, u8* inbufptr); + + +Result IRUSER_ReceiveIrNop(); +Result IRUSER_ReceiveIrNopLarge(); + + +Result IRUSER_GetLatestReceiveErrorResult(); +Result IRUSER_GetLatestSendErrorResult(); +Result IRUSER_GetConnectionStatus(); +Result IRUSER_GetTryingToConnectStatus(); +Result IRUSER_GetReceiveSizeFreeAndUsed(); +Result IRUSER_GetSendSizeFreeAndUsed(); +Result IRUSER_GetConnectionRole(); + +/** + * @brief Initializes the IR session + * @brief IR uses shared memory in shared mode (puts more information in shared memory) + * @param recv_buffer_size Size of receiving buffer + * @param recv_packet_count + * @param send_buffer_size + * @param send_packet_count + * @param bitrate + */ +Result IRUSER_InitializeIrNopShared(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate); + +/** + * @brief Mark the last `packet_count` packets as processed, so their memory in the receive buffer can be reused. + * @param packet_count Number of packets to mark as processed + */ +Result IRUSER_ReleaseReceivedData(u32 packet_count); + +/** + * @brief Sets own ID to `id`. + * @param id ID to set + */ +Result IRUSER_SetOwnMachineId(u8 id); From 97a8dc672776229d19c639787cc1a2f64f81600e Mon Sep 17 00:00:00 2001 From: syelan34 <82814680+syelan34@users.noreply.github.com> Date: Mon, 3 Mar 2025 22:07:16 -0500 Subject: [PATCH 03/26] Update 3ds.h --- libctru/include/3ds.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libctru/include/3ds.h b/libctru/include/3ds.h index 9d7d28ce2..0a9fea973 100644 --- a/libctru/include/3ds.h +++ b/libctru/include/3ds.h @@ -59,6 +59,7 @@ extern "C" { #include <3ds/services/nim.h> #include <3ds/services/nwmext.h> #include <3ds/services/ir.h> +#include <3ds/services/iruser.h> #include <3ds/services/ns.h> #include <3ds/services/pmapp.h> #include <3ds/services/pmdbg.h> From 2100047590fd0948be982039d5ac8571ee13a4ef Mon Sep 17 00:00:00 2001 From: syelan34 Date: Tue, 4 Mar 2025 03:50:07 +0000 Subject: [PATCH 04/26] Fix all the stupid issues I had bc of translating from c++ --- libctru/include/3ds/services/iruser.h | 28 +++++++----- libctru/source/services/iruser.c | 63 ++++++++++++++------------- 2 files changed, 48 insertions(+), 43 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index f342e9919..af72f0f51 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -1,17 +1,21 @@ +/** + * @file iruser.h + * @brief IRUSER service. + */ #pragma once -#include "3ds/services/hid.h" -#include "3ds/types.h" +#include <3ds/types.h> +#include <3ds/services/hid.h> /// Connection status values for [`IrUserStatusInfo`]. -enum ConnectionStatus { +typedef enum { Disconnected = 0, /// Device is not connected Connecting = 1, /// Waiting for device to connect Connected = 2, /// Device is connected -}; +} ConnectionStatus; /// This struct holds a parsed copy of the ir:USER service status (from shared memory). -struct IrUserStatusInfo { +typedef struct { /// The result of the last receive operation. Result recv_err_result; /// The result of the last send operation. @@ -32,10 +36,10 @@ struct IrUserStatusInfo { u8 unknown_field_2; /// Unknown field. u8 unknown_field_3; -}; +} IrUserStatusInfo; /// A packet of data sent/received to/from the IR device. -struct IrUserPacket { +typedef struct { /// The magic number of the packet. Should always be 0xA5. u8 magic_number; /// The destination network ID. @@ -46,10 +50,10 @@ struct IrUserPacket { u8* payload; /// The checksum of the packet. u8 checksum; -}; +} IrUserPacket; /// Circle Pad Pro response packet holding the current device input signals and status. -struct circlePadProInputResponse { +typedef struct { union { struct { /// The X value of the C-stick. @@ -74,14 +78,14 @@ struct circlePadProInputResponse { }; /// Unknown field. u8 unknown_field; -}; +} circlePadProInputResponse; /** * @brief Initializes IRUSER. - * Allocates memory to use as shared_memory based on recv_buffer_size and send_buffer_size and sets it as read only. + * Allocates memory to use as shared_memory based on buffer_size and sets it as read only. * */ -Result iruserInit(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count); +Result iruserInit(size_t buffer_size, size_t packet_count); /// Shuts down IRUSER. Frees shared memory. void iruserExit(); diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 453920416..d95107ee0 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -2,14 +2,18 @@ Copy of the IR:USER API from ctru-rs */ -#include "ir_user.h" -#include "defines.h" -#include "console.h" -#include <3ds.h> -#include #include -#include #include +#include +#include <3ds/types.h> +#include <3ds/result.h> +#include <3ds/svc.h> +#include <3ds/srv.h> +#include <3ds/allocator/mappable.h> +#include <3ds/synchronization.h> +#include <3ds/services/iruser.h> +#include <3ds/ipc.h> +#include <3ds/env.h> // Misc constants const size_t SHARED_MEM_INFO_SECTIONS_SIZE = 0x30; @@ -17,6 +21,7 @@ const size_t SHARED_MEM_RECV_BUFFER_OFFSET = 0x20; const size_t PAGE_SIZE = 0x1000; const u32 IR_BITRATE = 4; const u8 CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID = 0x10; +const u8 PACKET_INFO_SIZE = 8; static Handle iruserHandle; static Handle iruserSharedMemHandle; @@ -26,10 +31,9 @@ static int iruserRefCount; static u32 iruserRecvBufferSize; static u32 iruserRecvPacketCount; -namespace { - inline size_t round_up(size_t value, size_t multiple) { - return (value / multiple + 1) * multiple; - } + +static size_t round_up(size_t value, size_t multiple) { + return (value / multiple + 1) * multiple; } Result iruserInit(size_t buffer_size, size_t packet_count) { @@ -140,7 +144,6 @@ Result IRUSER_WaitConnection() { Result IRUSER_RequireConnection(u8 device_id) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - ql::Console::log("cmdbuf: %p", cmdbuf); cmdbuf[0] = IPC_MakeHeader(0x6, 1, 0); // 0x00060040 cmdbuf[1] = device_id; @@ -350,12 +353,10 @@ Result iruserGetCirclePadProState(circlePadProInputResponse* response) { if (R_FAILED(ret)) return ret; if (packet->payload_length != 6) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_SIZE); if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_ENUM_VALUE); - *response = circlePadProInputResponse { - .c_stick_x = (u16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)), - .c_stick_y = (u16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)), - .status_raw = packet->payload[4], - .unknown_field = packet->payload[5], - }; + response->cstick.csPos.dx = (u16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); + response->cstick.csPos.dy = (u16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); + response->status_raw = packet->payload[4]; + response->unknown_field = packet->payload[5]; free(packet); return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS); } @@ -366,10 +367,9 @@ Result iruserCirclePadProRead(circlePosition *pos) { if (R_FAILED(ret)) return ret; if (packet->payload_length != 6) return RS_INVALIDSTATE; if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; - *pos = circlePosition { - .dx = (s16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)), - .dy = (s16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)) - }; + pos->dx = (s16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); + pos->dy = (s16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); + free(packet); return RS_SUCCESS; } @@ -394,10 +394,12 @@ IrUserPacket* iruserGetPackets(Result* res) { u32 packet_info_section_size = iruserRecvPacketCount * PACKET_INFO_SIZE; u32 header_size = SHARED_MEM_RECV_BUFFER_OFFSET + packet_info_section_size; u32 data_buffer_size = iruserRecvBufferSize - packet_info_section_size; - auto packet_data = [=](u32 idx) -> u8 { + + u8 packet_data(u32 idx) { u32 data_buffer_offset = offset_to_data_buffer + idx; return *(u8*)((u8*)shared_mem + header_size + data_buffer_offset % data_buffer_size); - }; + } + u32 payload_length, payload_offset; if ((packet_data(2) & 0x40 )!= 0) { // Big payload @@ -416,13 +418,12 @@ IrUserPacket* iruserGetPackets(Result* res) { *res = RS_INVALIDSTATE; return NULL; } - packets[i] = IrUserPacket { - .magic_number = packet_data(0), - .destination_network_id = packet_data(1), - .payload_length = payload_length, - .payload = (u8*)payload_offset, - .checksum = packet_data(payload_offset + payload_length), - }; + + packets[i].magic_number = packet_data(0); + packets[i].destination_network_id = packet_data(1); + packets[i].payload_length = payload_length; + packets[i].payload = (u8*)payload_offset; + packets[i].checksum = packet_data(payload_offset + payload_length); } return packets; } @@ -435,7 +436,7 @@ Result iruserCPPRequestInputPolling(u8 period_ms) { u8 ir_request[3] = { 1, period_ms, - static_cast((period_ms + 2) << 2) + (u8)((period_ms + 2) << 2) }; return IRUSER_SendIrNop(sizeof(ir_request), ir_request); } From 3b0bc2e50b72b29026d3d518545b0a8cb9b6dd98 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Mon, 31 Mar 2025 16:43:49 +0000 Subject: [PATCH 05/26] Move memory allocation to be handled by user, to align with the rest of libctru. --- libctru/include/3ds/services/iruser.h | 2 +- libctru/source/services/iruser.c | 22 ++++++++-------------- 2 files changed, 9 insertions(+), 15 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index af72f0f51..5f74d224c 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -85,7 +85,7 @@ typedef struct { * Allocates memory to use as shared_memory based on buffer_size and sets it as read only. * */ -Result iruserInit(size_t buffer_size, size_t packet_count); +Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, size_t packet_count); /// Shuts down IRUSER. Frees shared memory. void iruserExit(); diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index d95107ee0..41a543bdc 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -9,11 +9,9 @@ Copy of the IR:USER API from ctru-rs #include <3ds/result.h> #include <3ds/svc.h> #include <3ds/srv.h> -#include <3ds/allocator/mappable.h> #include <3ds/synchronization.h> #include <3ds/services/iruser.h> #include <3ds/ipc.h> -#include <3ds/env.h> // Misc constants const size_t SHARED_MEM_INFO_SECTIONS_SIZE = 0x30; @@ -31,20 +29,15 @@ static int iruserRefCount; static u32 iruserRecvBufferSize; static u32 iruserRecvPacketCount; - -static size_t round_up(size_t value, size_t multiple) { - return (value / multiple + 1) * multiple; -} - -Result iruserInit(size_t buffer_size, size_t packet_count) { +Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, size_t packet_count) { if(AtomicPostIncrement(&iruserRefCount)) return 0; Result ret = srvGetServiceHandle(&iruserHandle, "ir:USER"); if (R_FAILED(ret)) goto cleanup0; // Calculate the shared memory size. // Shared memory length must be a multiple of the page size. - iruserSharedMemSize = round_up(SHARED_MEM_INFO_SECTIONS_SIZE + buffer_size + buffer_size, PAGE_SIZE); - iruserSharedMem = (u32*)memalign(iruserSharedMemSize, PAGE_SIZE); + iruserSharedMemSize = sharedmem_size; + iruserSharedMem = sharedmem_addr; ret = svcCreateMemoryBlock(&iruserSharedMemHandle, (u32)iruserSharedMem, iruserSharedMemSize, MEMPERM_READ, MEMPERM_READWRITE); if (R_FAILED(ret)) goto cleanup1; @@ -55,11 +48,12 @@ Result iruserInit(size_t buffer_size, size_t packet_count) { iruserRecvBufferSize = buffer_size; iruserRecvPacketCount = packet_count; + return ret; + cleanup2: IRUSER_FinalizeIrNop(); cleanup1: - free(iruserSharedMem); svcCloseHandle(iruserSharedMemHandle); cleanup0: @@ -81,7 +75,7 @@ void iruserExit(void) { Result IRUSER_InitializeIrNop(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate) { Result ret = 0; - u32 *cmdbuf = getThreadCommandBuffer(); + u32 *cmdbuf = getThreadCommandBuffer(); cmdbuf[0] = IPC_MakeHeader(0x1, 6, 2); // 0x00010182 cmdbuf[1] = iruserSharedMemSize; @@ -378,7 +372,7 @@ Result iruserCirclePadProRead(circlePosition *pos) { IrUserPacket* iruserGetPackets(Result* res) { void* shared_mem = iruserSharedMem; - // Find where the packets are, and how many + // Find where the p1ackets are, and how many u32 start_index = *(u32*)((u8*)shared_mem + 0x10); u32 valid_packet_count = *(u32*)((u8*)shared_mem + 0x18); @@ -438,7 +432,7 @@ Result iruserCPPRequestInputPolling(u8 period_ms) { period_ms, (u8)((period_ms + 2) << 2) }; - return IRUSER_SendIrNop(sizeof(ir_request), ir_request); + return IRUSER_SendIrNop(3, ir_request); } Handle iruserGetServHandle() { From 463354acdb09348325ed4b50daa3286ebdab8b5a Mon Sep 17 00:00:00 2001 From: syelan34 <82814680+syelan34@users.noreply.github.com> Date: Sat, 12 Apr 2025 15:21:21 -0400 Subject: [PATCH 06/26] Add disconnecting status to enum --- libctru/include/3ds/services/iruser.h | 1 + 1 file changed, 1 insertion(+) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 5f74d224c..021063fb2 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -12,6 +12,7 @@ typedef enum { Disconnected = 0, /// Device is not connected Connecting = 1, /// Waiting for device to connect Connected = 2, /// Device is connected + Disconnecting = 4 /// Device is disconnecting } ConnectionStatus; /// This struct holds a parsed copy of the ir:USER service status (from shared memory). From 66708c49d5ed6f43dddfe60d621963fcd676c8f1 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Sun, 13 Apr 2025 04:25:23 +0000 Subject: [PATCH 07/26] Rename IrUserPacket, IrUserConnectionStatus, and IrUserStatusInfo to reflect libctru naming style --- libctru/include/3ds/services/iruser.h | 18 +++++++++--------- libctru/source/services/iruser.c | 13 +++++++------ 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 021063fb2..491058834 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -7,13 +7,13 @@ #include <3ds/types.h> #include <3ds/services/hid.h> -/// Connection status values for [`IrUserStatusInfo`]. +/// Connection status values for [`IRUSER_StatusInfo`]. typedef enum { Disconnected = 0, /// Device is not connected Connecting = 1, /// Waiting for device to connect Connected = 2, /// Device is connected Disconnecting = 4 /// Device is disconnecting -} ConnectionStatus; +} IRUSER_ConnectionStatus; /// This struct holds a parsed copy of the ir:USER service status (from shared memory). typedef struct { @@ -22,22 +22,22 @@ typedef struct { /// The result of the last send operation. Result send_err_result; /// The current connection status. - ConnectionStatus connection_status; + IRUSER_ConnectionStatus connection_status; /// The status of the connection attempt. u8 trying_to_connect_status; /// The role of the device in the connection (value meaning is unknown). u8 connection_role; /// The machine ID of the device. u8 machine_id; - /// Unknown field. - u8 unknown_field_1; + /// The machine ID of the target device. + u8 target_machine_id; /// The network ID of the connection. u8 network_id; /// Unknown field. u8 unknown_field_2; /// Unknown field. u8 unknown_field_3; -} IrUserStatusInfo; +} IRUSER_StatusInfo; /// A packet of data sent/received to/from the IR device. typedef struct { @@ -51,7 +51,7 @@ typedef struct { u8* payload; /// The checksum of the packet. u8 checksum; -} IrUserPacket; +} IRUSER_Packet; /// Circle Pad Pro response packet holding the current device input signals and status. typedef struct { @@ -95,7 +95,7 @@ void iruserExit(); Handle iruserGetServHandle(); -IrUserStatusInfo iruserGetStatusInfo(); +IRUSER_StatusInfo iruserGetStatusInfo(); /** * @brief Circle Pad Pro specific request. @@ -120,7 +120,7 @@ Result iruserGetCirclePadProState(circlePadProInputResponse* response); Result iruserCirclePadProCStickRead(circlePosition* pos); -IrUserPacket* iruserGetPackets(Result* res); +IRUSER_Packet* iruserGetPackets(Result* res); /** * @brief Initializes the IR session diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 41a543bdc..2784f036b 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -248,6 +248,7 @@ Result IRUSER_SendIrNopLarge(u32 size, u8* inbufptr) { Result IRUSER_ReceiveIrNop() { return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); } + Result IRUSER_ReceiveIrNopLarge() { return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); } @@ -332,9 +333,9 @@ void iruserProcessSharedMemory(void(*process_fn)(u8*)) { } /// Read and parse the ir:USER service status data from shared memory. -IrUserStatusInfo iruserGetStatusInfo() { +IRUSER_StatusInfo iruserGetStatusInfo() { void* shared_mem = iruserSharedMem; - IrUserStatusInfo status_info; + IRUSER_StatusInfo status_info; // copy over data memcpy(&status_info, shared_mem, sizeof(status_info)); @@ -343,7 +344,7 @@ IrUserStatusInfo iruserGetStatusInfo() { Result iruserGetCirclePadProState(circlePadProInputResponse* response) { Result ret; - IrUserPacket* packet = iruserGetPackets(&ret); + IRUSER_Packet* packet = iruserGetPackets(&ret); if (R_FAILED(ret)) return ret; if (packet->payload_length != 6) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_SIZE); if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_ENUM_VALUE); @@ -357,7 +358,7 @@ Result iruserGetCirclePadProState(circlePadProInputResponse* response) { Result iruserCirclePadProRead(circlePosition *pos) { Result ret; - IrUserPacket* packet = iruserGetPackets(&ret); + IRUSER_Packet* packet = iruserGetPackets(&ret); if (R_FAILED(ret)) return ret; if (packet->payload_length != 6) return RS_INVALIDSTATE; if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; @@ -369,14 +370,14 @@ Result iruserCirclePadProRead(circlePosition *pos) { } /// Read and parse the current packets received from the IR device. -IrUserPacket* iruserGetPackets(Result* res) { +IRUSER_Packet* iruserGetPackets(Result* res) { void* shared_mem = iruserSharedMem; // Find where the p1ackets are, and how many u32 start_index = *(u32*)((u8*)shared_mem + 0x10); u32 valid_packet_count = *(u32*)((u8*)shared_mem + 0x18); - IrUserPacket* packets = (IrUserPacket*)malloc(valid_packet_count * sizeof(IrUserPacket)); + IRUSER_Packet* packets = (IRUSER_Packet*)malloc(valid_packet_count * sizeof(IRUSER_Packet)); // Parse the packets for (size_t i = 0; i < valid_packet_count; i++) { From ec9438b88aca251c4a9b8e1e417db725259dc5f2 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Sun, 13 Apr 2025 04:26:48 +0000 Subject: [PATCH 08/26] Add IRUSER_WaitConnection implementation --- libctru/include/3ds/services/iruser.h | 8 ++++++-- libctru/source/services/iruser.c | 17 +++++++++++++---- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 491058834..129b1156c 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -142,8 +142,12 @@ Result IRUSER_ClearReceiveBuffer(); /// Clears send buffer Result IRUSER_ClearSendBuffer(); -/// Unknown. -Result IRUSER_WaitConnection(); +/** + * @brief Attempts to connect to device with given id and timeout. + * @param device_id ID of the device to connect to. + * @param timeout Timeout in milliseconds. + */ +Result IRUSER_WaitConnection(u8 target_id, u64 timeout); /** * @brief Attempts to connect to device with given id. diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 2784f036b..321ea570a 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -129,10 +129,19 @@ Result IRUSER_ClearSendBuffer() { return ret; }; -Result IRUSER_WaitConnection() { - // 3 params - // what could they be - return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +Result IRUSER_WaitConnection(u8 target_id, u64 timeout) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x6, 3, 0); // 0x000600C0 + cmdbuf[1] = target_id; + cmdbuf[2] = timeout & 0xFFFFFFFF; + cmdbuf[3] = (timeout >> 32) & 0xFFFFFFFF; + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + + return ret; } Result IRUSER_RequireConnection(u8 device_id) { From 6835607f52408e4507b1b97ed5bc2e7373b729ab Mon Sep 17 00:00:00 2001 From: syelan34 Date: Sun, 13 Apr 2025 04:44:10 +0000 Subject: [PATCH 09/26] Fix WaitConnection command header --- libctru/source/services/iruser.c | 136 +++++++++++++++---------------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 321ea570a..ea00c0eba 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -33,40 +33,40 @@ Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, s if(AtomicPostIncrement(&iruserRefCount)) return 0; Result ret = srvGetServiceHandle(&iruserHandle, "ir:USER"); if (R_FAILED(ret)) goto cleanup0; - + // Calculate the shared memory size. // Shared memory length must be a multiple of the page size. iruserSharedMemSize = sharedmem_size; iruserSharedMem = sharedmem_addr; - + ret = svcCreateMemoryBlock(&iruserSharedMemHandle, (u32)iruserSharedMem, iruserSharedMemSize, MEMPERM_READ, MEMPERM_READWRITE); if (R_FAILED(ret)) goto cleanup1; - + ret = IRUSER_InitializeIrNopShared(buffer_size, packet_count, buffer_size, packet_count, IR_BITRATE); if (R_FAILED(ret)) goto cleanup2; iruserRecvBufferSize = buffer_size; iruserRecvPacketCount = packet_count; - + return ret; - + cleanup2: IRUSER_FinalizeIrNop(); - + cleanup1: svcCloseHandle(iruserSharedMemHandle); - + cleanup0: return ret; } void iruserExit(void) { if(AtomicDecrement(&iruserRefCount)) return; - + IRUSER_FinalizeIrNop(); svcCloseHandle(iruserHandle); svcCloseHandle(iruserSharedMemHandle); - + iruserHandle = 0; iruserSharedMemHandle = 0; } @@ -76,7 +76,7 @@ void iruserExit(void) { Result IRUSER_InitializeIrNop(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x1, 6, 2); // 0x00010182 cmdbuf[1] = iruserSharedMemSize; cmdbuf[2] = recv_buffer_size; @@ -86,74 +86,74 @@ Result IRUSER_InitializeIrNop(size_t recv_buffer_size, size_t recv_packet_count, cmdbuf[6] = bitrate; cmdbuf[7] = IPC_Desc_SharedHandles(1); cmdbuf[8] = iruserSharedMemHandle; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } Result IRUSER_FinalizeIrNop() { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x2, 0, 0); // 0x00020000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; }; Result IRUSER_ClearReceiveBuffer() { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x3, 0, 0); // 0x00030000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; }; Result IRUSER_ClearSendBuffer() { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x4, 0, 0); // 0x00040000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; }; Result IRUSER_WaitConnection(u8 target_id, u64 timeout) { - Result ret = 0; + Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - - cmdbuf[0] = IPC_MakeHeader(0x6, 3, 0); // 0x000600C0 + + cmdbuf[0] = IPC_MakeHeader(0x5, 3, 0); // 0x000600C0 cmdbuf[1] = target_id; cmdbuf[2] = timeout & 0xFFFFFFFF; cmdbuf[3] = (timeout >> 32) & 0xFFFFFFFF; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } Result IRUSER_RequireConnection(u8 device_id) { - Result ret = 0; + Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x6, 1, 0); // 0x00060040 cmdbuf[1] = device_id; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } @@ -164,93 +164,93 @@ Result IRUSER_AutoConnection() { Result IRUSER_AnyConnection() { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x8, 0, 0); // 0x00080000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; }; Result IRUSER_Disconnect() { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x9, 0, 0); // 0x00090000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } Result IRUSER_GetReceiveEvent(Handle* eventhandle) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0xA, 0, 0); // 0x000A0000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; *eventhandle = cmdbuf[3]; - + return ret; } Result IRUSER_GetSendEvent(Handle* eventhandle) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0xB, 0, 0); // 0x000B0000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; *eventhandle = cmdbuf[3]; - + return ret; } Result IRUSER_GetConnectionStatusEvent(Handle* eventhandle) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0xC, 0, 0); // 0x000C0000 - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; *eventhandle = cmdbuf[3]; - + return ret; } Result IRUSER_SendIrNop(u32 size, u8* inbufptr) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0xD, 1, 2); // 0x000D0042 cmdbuf[1] = size; cmdbuf[2] = (size << 14) | 2; cmdbuf[3] = (u32)inbufptr; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } Result IRUSER_SendIrNopLarge(u32 size, u8* inbufptr) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0xE, 1, 2); // 0x000E0042 cmdbuf[1] = size; cmdbuf[2] = (size << 8) | 10; cmdbuf[3] = (u32)inbufptr; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } @@ -293,7 +293,7 @@ Result IRUSER_GetConnectionRole() { Result IRUSER_InitializeIrNopShared(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x18, 6, 2); // 0x00180182 cmdbuf[1] = iruserSharedMemSize; cmdbuf[2] = recv_buffer_size; @@ -303,36 +303,36 @@ Result IRUSER_InitializeIrNopShared(size_t recv_buffer_size, size_t recv_packet_ cmdbuf[6] = bitrate; cmdbuf[7] = IPC_Desc_SharedHandles(1); cmdbuf[8] = iruserSharedMemHandle; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } Result IRUSER_ReleaseReceivedData(u32 packet_count) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x19, 1, 0); // 0x00190040 cmdbuf[1] = packet_count; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } Result IRUSER_SetOwnMachineId(u8 id) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); - + cmdbuf[0] = IPC_MakeHeader(0x1A, 1, 0); // 0x001A0040 cmdbuf[1] = id; - + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; ret = (Result)cmdbuf[1]; - + return ret; } @@ -347,7 +347,7 @@ IRUSER_StatusInfo iruserGetStatusInfo() { IRUSER_StatusInfo status_info; // copy over data memcpy(&status_info, shared_mem, sizeof(status_info)); - + return status_info; } @@ -373,7 +373,7 @@ Result iruserCirclePadProRead(circlePosition *pos) { if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; pos->dx = (s16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); pos->dy = (s16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); - + free(packet); return RS_SUCCESS; } @@ -385,7 +385,7 @@ IRUSER_Packet* iruserGetPackets(Result* res) { // Find where the p1ackets are, and how many u32 start_index = *(u32*)((u8*)shared_mem + 0x10); u32 valid_packet_count = *(u32*)((u8*)shared_mem + 0x18); - + IRUSER_Packet* packets = (IRUSER_Packet*)malloc(valid_packet_count * sizeof(IRUSER_Packet)); // Parse the packets @@ -398,12 +398,12 @@ IRUSER_Packet* iruserGetPackets(Result* res) { u32 packet_info_section_size = iruserRecvPacketCount * PACKET_INFO_SIZE; u32 header_size = SHARED_MEM_RECV_BUFFER_OFFSET + packet_info_section_size; u32 data_buffer_size = iruserRecvBufferSize - packet_info_section_size; - + u8 packet_data(u32 idx) { u32 data_buffer_offset = offset_to_data_buffer + idx; return *(u8*)((u8*)shared_mem + header_size + data_buffer_offset % data_buffer_size); } - + u32 payload_length, payload_offset; if ((packet_data(2) & 0x40 )!= 0) { // Big payload @@ -422,7 +422,7 @@ IRUSER_Packet* iruserGetPackets(Result* res) { *res = RS_INVALIDSTATE; return NULL; } - + packets[i].magic_number = packet_data(0); packets[i].destination_network_id = packet_data(1); packets[i].payload_length = payload_length; @@ -438,8 +438,8 @@ IRUSER_Packet* iruserGetPackets(Result* res) { /// with the current device input values. Result iruserCPPRequestInputPolling(u8 period_ms) { u8 ir_request[3] = { - 1, - period_ms, + 1, + period_ms, (u8)((period_ms + 2) << 2) }; return IRUSER_SendIrNop(3, ir_request); From d4d6cc9610e0b636c21c8b9339fdaf0815a88879 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Sun, 13 Apr 2025 04:52:19 +0000 Subject: [PATCH 10/26] Fix disconnecting status value --- libctru/include/3ds/services/iruser.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 129b1156c..1587b386b 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -12,7 +12,8 @@ typedef enum { Disconnected = 0, /// Device is not connected Connecting = 1, /// Waiting for device to connect Connected = 2, /// Device is connected - Disconnecting = 4 /// Device is disconnecting + Disconnecting = 3, /// Device is disconnecting + Unknown = 4 // /// Unknown status value } IRUSER_ConnectionStatus; /// This struct holds a parsed copy of the ir:USER service status (from shared memory). From 83c8a498a9921b3807d098f042333414cd6e4e53 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Sun, 13 Apr 2025 05:16:57 +0000 Subject: [PATCH 11/26] Add IRUSER_GetConnectionStatus implementation --- libctru/include/3ds/services/iruser.h | 12 ++++++------ libctru/source/services/iruser.c | 13 +++++++++++-- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 1587b386b..b2e411951 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -9,11 +9,11 @@ /// Connection status values for [`IRUSER_StatusInfo`]. typedef enum { - Disconnected = 0, /// Device is not connected - Connecting = 1, /// Waiting for device to connect - Connected = 2, /// Device is connected - Disconnecting = 3, /// Device is disconnecting - Unknown = 4 // /// Unknown status value + CNSTATUS_Disconnected = 0, /// Device is not connected + CNSTATUS_Connecting = 1, /// Waiting for device to connect + CNSTATUS_Connected = 2, /// Device is connected + CNSTATUS_Disconnecting = 3, /// Device is disconnecting + CNSTATUS_Unknown = 4 // /// Unknown status value } IRUSER_ConnectionStatus; /// This struct holds a parsed copy of the ir:USER service status (from shared memory). @@ -206,7 +206,7 @@ Result IRUSER_ReceiveIrNopLarge(); Result IRUSER_GetLatestReceiveErrorResult(); Result IRUSER_GetLatestSendErrorResult(); -Result IRUSER_GetConnectionStatus(); +Result IRUSER_GetConnectionStatus(IRUSER_ConnectionStatus* status); Result IRUSER_GetTryingToConnectStatus(); Result IRUSER_GetReceiveSizeFreeAndUsed(); Result IRUSER_GetSendSizeFreeAndUsed(); diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index ea00c0eba..7b9be1260 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -270,8 +270,17 @@ Result IRUSER_GetLatestSendErrorResult() { return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); } -Result IRUSER_GetConnectionStatus() { - return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +Result IRUSER_GetConnectionStatus(IRUSER_ConnectionStatus* status) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x13, 0, 0); // 0x00130000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *status = (IRUSER_ConnectionStatus)cmdbuf[2]; + + return ret; } Result IRUSER_GetTryingToConnectStatus() { From 0c8233b26eb331d40224f504d11fd2afeea499e8 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Sun, 13 Apr 2025 05:44:54 +0000 Subject: [PATCH 12/26] Add IRUSER_GetConnectionRole implementation --- libctru/include/3ds/services/iruser.h | 7 ++++++- libctru/source/services/iruser.c | 13 +++++++++++-- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index b2e411951..cce21249e 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -16,6 +16,11 @@ typedef enum { CNSTATUS_Unknown = 4 // /// Unknown status value } IRUSER_ConnectionStatus; +typedef enum { + CNRole_1 = 1, + CNRole_2 = 2, +} IRUSER_ConnectionRole; + /// This struct holds a parsed copy of the ir:USER service status (from shared memory). typedef struct { /// The result of the last receive operation. @@ -210,7 +215,7 @@ Result IRUSER_GetConnectionStatus(IRUSER_ConnectionStatus* status); Result IRUSER_GetTryingToConnectStatus(); Result IRUSER_GetReceiveSizeFreeAndUsed(); Result IRUSER_GetSendSizeFreeAndUsed(); -Result IRUSER_GetConnectionRole(); +Result IRUSER_GetConnectionRole(IRUSER_ConnectionRole* role); /** * @brief Initializes the IR session diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 7b9be1260..928f7a4ae 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -295,8 +295,17 @@ Result IRUSER_GetSendSizeFreeAndUsed() { return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); } -Result IRUSER_GetConnectionRole() { - return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +Result IRUSER_GetConnectionRole(IRUSER_ConnectionRole* role) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x17, 0, 0); // 0x00170000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *role = (IRUSER_ConnectionRole)cmdbuf[2]; + + return ret; } Result IRUSER_InitializeIrNopShared(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate) { From c47702b6f4adff70ebec081d248f1f9c35a4c4c9 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Sun, 13 Apr 2025 22:47:45 +0000 Subject: [PATCH 13/26] Add preliminary TryingToConnectStatus implementation --- libctru/include/3ds/services/iruser.h | 10 +++++++++- libctru/source/services/iruser.c | 13 +++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index cce21249e..98108d81a 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -16,6 +16,14 @@ typedef enum { CNSTATUS_Unknown = 4 // /// Unknown status value } IRUSER_ConnectionStatus; +/// Connection status values for [`IRUSER_StatusInfo`]. +typedef enum { + Unk_1 = 1, + Unk_2 = 2, + Unk_3 = 3, + Unk_4 = 4 +} IRUSER_TryingToConnectStatus; + typedef enum { CNRole_1 = 1, CNRole_2 = 2, @@ -212,7 +220,7 @@ Result IRUSER_ReceiveIrNopLarge(); Result IRUSER_GetLatestReceiveErrorResult(); Result IRUSER_GetLatestSendErrorResult(); Result IRUSER_GetConnectionStatus(IRUSER_ConnectionStatus* status); -Result IRUSER_GetTryingToConnectStatus(); +Result IRUSER_GetTryingToConnectStatus(IRUSER_TryingToConnectStatus* status); Result IRUSER_GetReceiveSizeFreeAndUsed(); Result IRUSER_GetSendSizeFreeAndUsed(); Result IRUSER_GetConnectionRole(IRUSER_ConnectionRole* role); diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 928f7a4ae..0dd2cc0fc 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -283,8 +283,17 @@ Result IRUSER_GetConnectionStatus(IRUSER_ConnectionStatus* status) { return ret; } -Result IRUSER_GetTryingToConnectStatus() { - return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +Result IRUSER_GetTryingToConnectStatus(IRUSER_TryingToConnectStatus* status) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x14, 0, 0); // 0x00140000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *status = cmdbuf[2] & 0xFF; // value is a u8 + + return ret; } Result IRUSER_GetReceiveSizeFreeAndUsed() { From c730ee02812f4694b80929e9dc230d227d4c98d6 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Tue, 15 Apr 2025 17:12:00 +0000 Subject: [PATCH 14/26] Fix packet parsing function --- libctru/include/3ds/services/iruser.h | 7 +- libctru/source/services/iruser.c | 103 ++++++++++++++------------ 2 files changed, 62 insertions(+), 48 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 98108d81a..09f4f2e22 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -53,6 +53,11 @@ typedef struct { u8 unknown_field_3; } IRUSER_StatusInfo; +typedef struct { + u32 offset; + u32 length; +} IRUSER_PacketInfo; + /// A packet of data sent/received to/from the IR device. typedef struct { /// The magic number of the packet. Should always be 0xA5. @@ -134,7 +139,7 @@ Result iruserGetCirclePadProState(circlePadProInputResponse* response); Result iruserCirclePadProCStickRead(circlePosition* pos); -IRUSER_Packet* iruserGetPackets(Result* res); +IRUSER_Packet* iruserGetPackets(); /** * @brief Initializes the IR session diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 0dd2cc0fc..588ca73a6 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -28,6 +28,9 @@ static u32 iruserSharedMemSize; static int iruserRefCount; static u32 iruserRecvBufferSize; static u32 iruserRecvPacketCount; +static IRUSER_PacketInfo* iruserRecvPacketInfoBuffer; +static u8* iruserRecvPacketDataBuffer; +static u32 iruserRecvPacketDataBufferSize; Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, size_t packet_count) { if(AtomicPostIncrement(&iruserRefCount)) return 0; @@ -47,6 +50,9 @@ Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, s iruserRecvBufferSize = buffer_size; iruserRecvPacketCount = packet_count; + iruserRecvPacketInfoBuffer = (IRUSER_PacketInfo*)((u8*)iruserSharedMem + SHARED_MEM_RECV_BUFFER_OFFSET); + iruserRecvPacketDataBuffer = (u8*)iruserRecvPacketInfoBuffer + iruserRecvPacketCount * PACKET_INFO_SIZE; + iruserRecvPacketDataBufferSize = (u32)((u8*)iruserSharedMem - iruserRecvPacketDataBuffer); return ret; @@ -379,82 +385,85 @@ IRUSER_StatusInfo iruserGetStatusInfo() { } Result iruserGetCirclePadProState(circlePadProInputResponse* response) { - Result ret; - IRUSER_Packet* packet = iruserGetPackets(&ret); + Result ret = 0; + IRUSER_Packet* packet = iruserGetPackets(); if (R_FAILED(ret)) return ret; - if (packet->payload_length != 6) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_SIZE); - if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_ENUM_VALUE); + if (!packet->payload) return ret; + if (packet->payload_length != 6) return /*MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_SIZE)*/-1; + if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return /*MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_ENUM_VALUE)*/-2; response->cstick.csPos.dx = (u16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); response->cstick.csPos.dy = (u16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); response->status_raw = packet->payload[4]; response->unknown_field = packet->payload[5]; + free(packet->payload); free(packet); - return MAKERESULT(RL_SUCCESS, RS_SUCCESS, RM_COMMON, RD_SUCCESS); + return 0; } Result iruserCirclePadProRead(circlePosition *pos) { - Result ret; - IRUSER_Packet* packet = iruserGetPackets(&ret); + Result ret = 0; + IRUSER_Packet* packet = iruserGetPackets(); if (R_FAILED(ret)) return ret; if (packet->payload_length != 6) return RS_INVALIDSTATE; if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; pos->dx = (s16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); pos->dy = (s16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); + free(packet->payload); free(packet); - return RS_SUCCESS; + return 0; } +// since data buffer is a ring buffer, this helper macro makes it easier to access the data +#ifndef IRUSER_PACKET_DATA +#define IRUSER_PACKET_DATA(i) (iruserRecvPacketDataBuffer + (inf.offset + i) % iruserRecvPacketDataBufferSize) + +static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { + if (packet == NULL) return false; + IRUSER_PacketInfo inf = iruserRecvPacketInfoBuffer[index % iruserRecvPacketCount]; + packet->magic_number = *IRUSER_PACKET_DATA(0); + packet->destination_network_id = *IRUSER_PACKET_DATA(1); + bool large = *IRUSER_PACKET_DATA(2) & 0x40; + u32 payload_offset = 0; + if (large) { + packet->payload_length = (*IRUSER_PACKET_DATA(2) << 8) + *IRUSER_PACKET_DATA(3); + packet->payload = malloc(packet->payload_length); + payload_offset = 4; + } else { + packet->payload_length = *IRUSER_PACKET_DATA(2); + packet->payload = malloc(packet->payload_length); + payload_offset = 3; + } + for (size_t i = 0; i < packet->payload_length; i++) { + packet->payload[i] = *IRUSER_PACKET_DATA(i + payload_offset); + } + packet->checksum = *IRUSER_PACKET_DATA(packet->payload_length + payload_offset); + return true; +} + +#endif + +#undef IRUSER_PACKET_DATA + /// Read and parse the current packets received from the IR device. -IRUSER_Packet* iruserGetPackets(Result* res) { +IRUSER_Packet* iruserGetPackets() { void* shared_mem = iruserSharedMem; - // Find where the p1ackets are, and how many + // Find where the packets are, and how many u32 start_index = *(u32*)((u8*)shared_mem + 0x10); u32 valid_packet_count = *(u32*)((u8*)shared_mem + 0x18); + + if (valid_packet_count == 0) {return NULL;} IRUSER_Packet* packets = (IRUSER_Packet*)malloc(valid_packet_count * sizeof(IRUSER_Packet)); + int failed = 0; // number of bad packets // Parse the packets for (size_t i = 0; i < valid_packet_count; i++) { - u32 packet_index = (i + start_index) % iruserRecvPacketCount; - u32 packet_info_offset = SHARED_MEM_RECV_BUFFER_OFFSET + (packet_index * PACKET_INFO_SIZE); - u8* packet_info = (u8*)shared_mem + packet_info_offset; - u32 offset_to_data_buffer = *(u32*)packet_info; - u32 data_length = *(u32*)(packet_info + 4); - u32 packet_info_section_size = iruserRecvPacketCount * PACKET_INFO_SIZE; - u32 header_size = SHARED_MEM_RECV_BUFFER_OFFSET + packet_info_section_size; - u32 data_buffer_size = iruserRecvBufferSize - packet_info_section_size; - - u8 packet_data(u32 idx) { - u32 data_buffer_offset = offset_to_data_buffer + idx; - return *(u8*)((u8*)shared_mem + header_size + data_buffer_offset % data_buffer_size); - } - - u32 payload_length, payload_offset; - if ((packet_data(2) & 0x40 )!= 0) { - // Big payload - payload_length = ((packet_data(2) & 0x3F) << 8) + packet_data(3); - payload_offset = 4; - } else { - // Small payload - payload_length = packet_data(2) & 0x3F; - payload_offset = 3; + // bad packet + if (!iruserParsePacket((i + start_index) % iruserRecvPacketCount, &packets[i - failed])) { + failed++; // retry with next packet } - if (data_length != payload_offset + payload_length + 1) { - *res = RS_INVALIDSTATE; - return NULL; - } - if (packet_data(0) != 0xA5) { - *res = RS_INVALIDSTATE; - return NULL; - } - - packets[i].magic_number = packet_data(0); - packets[i].destination_network_id = packet_data(1); - packets[i].payload_length = payload_length; - packets[i].payload = (u8*)payload_offset; - packets[i].checksum = packet_data(payload_offset + payload_length); } return packets; } From 3081d37720625e7235e958123b670cd622c97837 Mon Sep 17 00:00:00 2001 From: syelan34 Date: Thu, 17 Apr 2025 02:08:06 +0000 Subject: [PATCH 15/26] Mostly fix getting packets from shared mem, plus actually check the checksum value before returning packets. --- libctru/include/3ds/services/iruser.h | 21 +++-- libctru/source/services/iruser.c | 123 ++++++++++++++++++++------ 2 files changed, 111 insertions(+), 33 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 09f4f2e22..987544ec9 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -53,6 +53,13 @@ typedef struct { u8 unknown_field_3; } IRUSER_StatusInfo; +typedef struct { + u32 start_index; + u32 end_index; + u32 valid_packet_count; + u32 unknown_field; +} IRUSER_BufferInfo; + typedef struct { u32 offset; u32 length; @@ -86,14 +93,14 @@ typedef struct { union { u8 status_raw; struct { + /// The battery level of the Circle Pad Pro. + u8 battery_level : 5; /// Whether the ZL button is pressed. bool zl_pressed : 1; /// Whether the ZR button is pressed. bool zr_pressed : 1; /// Whether the R button is pressed. bool r_pressed : 1; - /// The battery level of the Circle Pad Pro. - u8 battery_level : 5; } status; }; /// Unknown field. @@ -130,7 +137,7 @@ void iruserProcessSharedMemory(void(*process_fn)(u8*)); * @brief Gets circle pad pro inputs state * @param response Pointer to write data to */ -Result iruserGetCirclePadProState(circlePadProInputResponse* response); +Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputResponse* response); /** * @brief Reads c-stick position from circle pad pro @@ -138,8 +145,12 @@ Result iruserGetCirclePadProState(circlePadProInputResponse* response); */ Result iruserCirclePadProCStickRead(circlePosition* pos); - -IRUSER_Packet* iruserGetPackets(); +/** + * @brief Returns the packets received from the IR device. + * @param [out] numpackets Number of valid packets that were received + * @return Pointer to the packets received. The caller is responsible for freeing the memory (both the packet and its payload data). + */ +IRUSER_Packet* iruserGetPackets(u32* numpackets); /** * @brief Initializes the IR session diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 588ca73a6..776b806c3 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -32,6 +32,55 @@ static IRUSER_PacketInfo* iruserRecvPacketInfoBuffer; static u8* iruserRecvPacketDataBuffer; static u32 iruserRecvPacketDataBufferSize; +static const u8 CRC_TABLE[256] = { + 0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, + 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, + 0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, + 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D, + 0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, + 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD, + 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, + 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, + 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, + 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, + 0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, + 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, + 0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, + 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A, + 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, + 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A, + 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, + 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, + 0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, + 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, + 0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, + 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44, + 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, + 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34, + 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, + 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, + 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, + 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, + 0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, + 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83, + 0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, + 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 +}; + +u8 crc8ccitt(const void * data, size_t size) { + u8 val = 0; + + u8 * pos = (u8 *) data; + u8 * end = pos + size; + + while (pos < end) { + val = CRC_TABLE[val ^ *pos]; + pos++; + } + + return val; +} + Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, size_t packet_count) { if(AtomicPostIncrement(&iruserRefCount)) return 0; Result ret = srvGetServiceHandle(&iruserHandle, "ir:USER"); @@ -384,33 +433,34 @@ IRUSER_StatusInfo iruserGetStatusInfo() { return status_info; } -Result iruserGetCirclePadProState(circlePadProInputResponse* response) { - Result ret = 0; - IRUSER_Packet* packet = iruserGetPackets(); - if (R_FAILED(ret)) return ret; - if (!packet->payload) return ret; - if (packet->payload_length != 6) return /*MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_SIZE)*/-1; - if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return /*MAKERESULT(RL_TEMPORARY, RS_INVALIDSTATE, RM_IR, RD_INVALID_ENUM_VALUE)*/-2; +Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputResponse* response) { + if (!packet) goto failure; + if (!packet->payload) goto failure; + if (packet->payload_length != 6) goto failure; + if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) goto failure; response->cstick.csPos.dx = (u16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); response->cstick.csPos.dy = (u16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); response->status_raw = packet->payload[4]; response->unknown_field = packet->payload[5]; - free(packet->payload); - free(packet); return 0; -} - -Result iruserCirclePadProRead(circlePosition *pos) { - Result ret = 0; - IRUSER_Packet* packet = iruserGetPackets(); - if (R_FAILED(ret)) return ret; - if (packet->payload_length != 6) return RS_INVALIDSTATE; - if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; - pos->dx = (s16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); - pos->dy = (s16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); - - free(packet->payload); + failure: + memset(response, 0, sizeof(circlePadProInputResponse)); + return -1; +} + +Result iruserCirclePadProCStickRead(circlePosition *pos) { + // Result ret = 0; + u32 n = 0; + IRUSER_Packet* packet = iruserGetPackets(&n); + if (n == 0) return -1; + if (packet[n-1].payload_length != 6) return RS_INVALIDSTATE; + if (packet[n-1].payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; + pos->dx = (s16)(packet[n-1].payload[1] | ((packet[n-1].payload[2] & 0x0F) << 8)); + pos->dy = (s16)(((packet[n-1].payload[2] & 0xF0) >> 4) | ((packet[n-1].payload[3]) << 4)); + + free(packet[n-1].payload); free(packet); + IRUSER_ReleaseReceivedData(n); return 0; } @@ -438,6 +488,13 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { packet->payload[i] = *IRUSER_PACKET_DATA(i + payload_offset); } packet->checksum = *IRUSER_PACKET_DATA(packet->payload_length + payload_offset); + // check the checksum + u8 checksum = crc8ccitt(IRUSER_PACKET_DATA(0), packet->payload_length + payload_offset); + if (packet->checksum != checksum) { // bad data + free(packet->payload); + packet->payload = NULL; + return false; + } return true; } @@ -446,25 +503,35 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { #undef IRUSER_PACKET_DATA /// Read and parse the current packets received from the IR device. -IRUSER_Packet* iruserGetPackets() { +IRUSER_Packet* iruserGetPackets(u32* n) { void* shared_mem = iruserSharedMem; // Find where the packets are, and how many - u32 start_index = *(u32*)((u8*)shared_mem + 0x10); - u32 valid_packet_count = *(u32*)((u8*)shared_mem + 0x18); + IRUSER_BufferInfo buffer_info; + memcpy(&buffer_info, (u8*)shared_mem + 0x10, sizeof(buffer_info)); + + if (n) *n = buffer_info.valid_packet_count; + if (buffer_info.valid_packet_count == 0) {return NULL;} - if (valid_packet_count == 0) {return NULL;} - IRUSER_Packet* packets = (IRUSER_Packet*)malloc(valid_packet_count * sizeof(IRUSER_Packet)); + IRUSER_Packet* packets = (IRUSER_Packet*)malloc(buffer_info.valid_packet_count * sizeof(IRUSER_Packet)); int failed = 0; // number of bad packets // Parse the packets - for (size_t i = 0; i < valid_packet_count; i++) { + for (size_t i = 0; i < buffer_info.valid_packet_count; i++) { // bad packet - if (!iruserParsePacket((i + start_index) % iruserRecvPacketCount, &packets[i - failed])) { + if (!iruserParsePacket((i + buffer_info.start_index) % iruserRecvPacketCount, &packets[i - failed])) { failed++; // retry with next packet } } + + *n -= failed; // update the number of packets + + // all packets were bad + if (*n == 0) { + free(packets); + return NULL; + } return packets; } From 8eb9c7bd1227ff9317ae4b1e393fcbf86d08963a Mon Sep 17 00:00:00 2001 From: syelan34 Date: Thu, 17 Apr 2025 02:16:16 +0000 Subject: [PATCH 16/26] Add GetLatestReceive/SendErrorResult() implementation. --- libctru/source/services/iruser.c | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 776b806c3..1b1eb912a 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -126,8 +126,6 @@ void iruserExit(void) { iruserSharedMemHandle = 0; } - - Result IRUSER_InitializeIrNop(size_t recv_buffer_size, size_t recv_packet_count, size_t send_buffer_size, size_t send_packet_count, u32 bitrate) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); @@ -317,12 +315,30 @@ Result IRUSER_ReceiveIrNopLarge() { return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); } -Result IRUSER_GetLatestReceiveErrorResult() { - return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +Result IRUSER_GetLatestReceiveErrorResult(u32* result) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x11, 0, 0); // 0x00110000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *result = (IRUSER_ConnectionStatus)cmdbuf[2]; + + return ret; } -Result IRUSER_GetLatestSendErrorResult() { - return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); +Result IRUSER_GetLatestSendErrorResult(u32* result) { + Result ret = 0; + u32 *cmdbuf = getThreadCommandBuffer(); + + cmdbuf[0] = IPC_MakeHeader(0x12, 0, 0); // 0x00120000 + + if(R_FAILED(ret = svcSendSyncRequest(iruserHandle)))return ret; + ret = (Result)cmdbuf[1]; + *result = (IRUSER_ConnectionStatus)cmdbuf[2]; + + return ret; } Result IRUSER_GetConnectionStatus(IRUSER_ConnectionStatus* status) { From 2bdcec81fe1d75ac7aeabea756ebbd60d03599df Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Tue, 30 Sep 2025 12:57:16 -0400 Subject: [PATCH 17/26] Resolved issues raised by @TuxSH. - crc8 function is now static and const - checksum now calculated on the right data - iruserRecvPacketDataBufferSize now calculated correctly - iruserGetPackets now changed to iruserGetNPackets, and uses a user-provided buffer instead of dynamically allocating. - Packets themselves now simply point to the receive buffer for the payload data instead of dynamically allocating a buffer and copying the payload data into it. This data should be valid until IRUSER_ReleaseReceivedData or IRUSER_ClearReceiveBuffer are called (Though this requires further testing). --- libctru/include/3ds/services/iruser.h | 17 +++- libctru/source/services/iruser.c | 127 ++++++++++++-------------- 2 files changed, 70 insertions(+), 74 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 987544ec9..06ef53d3c 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -143,14 +143,21 @@ Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputRespon * @brief Reads c-stick position from circle pad pro * @param pos Pointer to write data to */ -Result iruserCirclePadProCStickRead(circlePosition* pos); +bool iruserCirclePadProCStickRead(circlePosition* pos); /** - * @brief Returns the packets received from the IR device. - * @param [out] numpackets Number of valid packets that were received - * @return Pointer to the packets received. The caller is responsible for freeing the memory (both the packet and its payload data). + * @brief gets the packets received from the IR device. + * @param packets Buffer to write packet data into. + * @param numpackets Size of buffer (in packets). + * @return Actual number of (valid) packets received */ -IRUSER_Packet* iruserGetPackets(u32* numpackets); +u32 iruserGetNPackets(IRUSER_Packet packets[], u32 numpackets); + +/** + * @brief Returns the first valid packet received from the IR device. + * @param [out] packet Pointer to write the packet data into. + */ +bool iruserGetFirstPacket(IRUSER_Packet* packet) /** * @brief Initializes the IR session diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 1b1eb912a..2a29d8552 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -13,9 +13,16 @@ Copy of the IR:USER API from ctru-rs #include <3ds/services/iruser.h> #include <3ds/ipc.h> +// since data buffer is a ring buffer, this helper macro makes it easier to access the data +#ifndef IRUSER_PACKET_DATA +#define IRUSER_PACKET_DATA(i) (iruserRecvPacketDataBuffer + (i) % iruserRecvPacketDataBufferSize) +#endif + // Misc constants -const size_t SHARED_MEM_INFO_SECTIONS_SIZE = 0x30; +const size_t SHARED_MEM_STATUS_INFO_OFFSET = 0x00; +const size_t SHARED_MEM_RECV_BUFFER_INFO_OFFSET = 0x10 const size_t SHARED_MEM_RECV_BUFFER_OFFSET = 0x20; +const size_t SHARED_MEM_INFO_SECTIONS_SIZE = 0x30; const size_t PAGE_SIZE = 0x1000; const u32 IR_BITRATE = 4; const u8 CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID = 0x10; @@ -28,6 +35,8 @@ static u32 iruserSharedMemSize; static int iruserRefCount; static u32 iruserRecvBufferSize; static u32 iruserRecvPacketCount; +static IRUSER_StatusInfo* iruserStatusInfo; +static IRUSER_BufferInfo* iruserRecvBufferInfo; static IRUSER_PacketInfo* iruserRecvPacketInfoBuffer; static u8* iruserRecvPacketDataBuffer; static u32 iruserRecvPacketDataBufferSize; @@ -67,14 +76,13 @@ static const u8 CRC_TABLE[256] = { 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 }; -u8 crc8ccitt(const void * data, size_t size) { +static const u8 crc8ccitt(const size_t offset, const size_t size) { u8 val = 0; - u8 * pos = (u8 *) data; - u8 * end = pos + size; + u8 pos = 0; - while (pos < end) { - val = CRC_TABLE[val ^ *pos]; + while (pos < size) { + val = CRC_TABLE[val ^ *IRUSER_PACKET_DATA(offset + pos)]; pos++; } @@ -99,9 +107,11 @@ Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, s iruserRecvBufferSize = buffer_size; iruserRecvPacketCount = packet_count; + iruserStatusInfo = (IRUSER_StatusInfo*)((u8*)iruserSharedMem + SHARED_MEM_STATUS_INFO_OFFSET); + iruserRecvBufferInfo = (IRUSER_BufferInfo*)((u8*)iruserSharedMem + SHARED_MEM_RECV_BUFFER_INFO_OFFSET); iruserRecvPacketInfoBuffer = (IRUSER_PacketInfo*)((u8*)iruserSharedMem + SHARED_MEM_RECV_BUFFER_OFFSET); iruserRecvPacketDataBuffer = (u8*)iruserRecvPacketInfoBuffer + iruserRecvPacketCount * PACKET_INFO_SIZE; - iruserRecvPacketDataBufferSize = (u32)((u8*)iruserSharedMem - iruserRecvPacketDataBuffer); + iruserRecvPacketDataBufferSize = (u32)(iruserRecvPacketDataBuffer - iruserRecPacketInfoBuffer); return ret; @@ -441,12 +451,7 @@ void iruserProcessSharedMemory(void(*process_fn)(u8*)) { /// Read and parse the ir:USER service status data from shared memory. IRUSER_StatusInfo iruserGetStatusInfo() { - void* shared_mem = iruserSharedMem; - IRUSER_StatusInfo status_info; - // copy over data - memcpy(&status_info, shared_mem, sizeof(status_info)); - - return status_info; + return *iruserStatusInfo; } Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputResponse* response) { @@ -464,91 +469,75 @@ Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputRespon return -1; } -Result iruserCirclePadProCStickRead(circlePosition *pos) { +bool iruserCirclePadProCStickRead(circlePosition *pos) { // Result ret = 0; u32 n = 0; - IRUSER_Packet* packet = iruserGetPackets(&n); - if (n == 0) return -1; - if (packet[n-1].payload_length != 6) return RS_INVALIDSTATE; - if (packet[n-1].payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return RS_INVALIDSTATE; - pos->dx = (s16)(packet[n-1].payload[1] | ((packet[n-1].payload[2] & 0x0F) << 8)); - pos->dy = (s16)(((packet[n-1].payload[2] & 0xF0) >> 4) | ((packet[n-1].payload[3]) << 4)); - - free(packet[n-1].payload); - free(packet); - IRUSER_ReleaseReceivedData(n); + IRUSER_Packet packet; + if (!iruserGetFirstPacket(&packet)) return false; + if (packet.payload_length != 6) return false; + if (packet.payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return false; + pos->dx = (s16)(packet.payload[1] | ((packet.payload[2] & 0x0F) << 8)); + pos->dy = (s16)(((packet.payload[2] & 0xF0) >> 4) | ((packet.payload[3]) << 4)); + + IRUSER_ReleaseReceivedData(1); return 0; } -// since data buffer is a ring buffer, this helper macro makes it easier to access the data -#ifndef IRUSER_PACKET_DATA -#define IRUSER_PACKET_DATA(i) (iruserRecvPacketDataBuffer + (inf.offset + i) % iruserRecvPacketDataBufferSize) - static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { if (packet == NULL) return false; + IRUSER_PacketInfo inf = iruserRecvPacketInfoBuffer[index % iruserRecvPacketCount]; - packet->magic_number = *IRUSER_PACKET_DATA(0); - packet->destination_network_id = *IRUSER_PACKET_DATA(1); - bool large = *IRUSER_PACKET_DATA(2) & 0x40; + packet->magic_number = *IRUSER_PACKET_DATA(inf.offset + 0); + packet->destination_network_id = *IRUSER_PACKET_DATA(inf.offset + 1); + bool large = *IRUSER_PACKET_DATA(inf.offset + 2) & (1 << 7); + + u32 payload_offset = 0; if (large) { - packet->payload_length = (*IRUSER_PACKET_DATA(2) << 8) + *IRUSER_PACKET_DATA(3); - packet->payload = malloc(packet->payload_length); + packet->payload_length = (*IRUSER_PACKET_DATA(inf.offset + 2) << 8) + *IRUSER_PACKET_DATA(inf.offset + 3); payload_offset = 4; } else { - packet->payload_length = *IRUSER_PACKET_DATA(2); - packet->payload = malloc(packet->payload_length); + packet->payload_length = *IRUSER_PACKET_DATA(inf.offset + 2); payload_offset = 3; } - for (size_t i = 0; i < packet->payload_length; i++) { - packet->payload[i] = *IRUSER_PACKET_DATA(i + payload_offset); - } - packet->checksum = *IRUSER_PACKET_DATA(packet->payload_length + payload_offset); + + packet->payload = IRUSER_PACKET_DATA(inf.offset + payload_offset); // Pointer into the receive buffer, so won't be valid after we release the data using IRUSER_ReleaseReceivedData + packet->checksum = *IRUSER_PACKET_DATA(inf.offset + payload_offset + packet->payload_length); + // check the checksum - u8 checksum = crc8ccitt(IRUSER_PACKET_DATA(0), packet->payload_length + payload_offset); + u8 checksum = crc8ccitt(inf.offset, payload_offset + packet->payload_length - 1); // Checksum over the entire packet, including header (https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure) if (packet->checksum != checksum) { // bad data - free(packet->payload); packet->payload = NULL; return false; } return true; } -#endif - -#undef IRUSER_PACKET_DATA - /// Read and parse the current packets received from the IR device. -IRUSER_Packet* iruserGetPackets(u32* n) { - void* shared_mem = iruserSharedMem; - - // Find where the packets are, and how many - IRUSER_BufferInfo buffer_info; - memcpy(&buffer_info, (u8*)shared_mem + 0x10, sizeof(buffer_info)); - - if (n) *n = buffer_info.valid_packet_count; - if (buffer_info.valid_packet_count == 0) {return NULL;} +u32 iruserGetNPackets(IRUSER_Packet packets[], u32 n) { + u32 num_available_packets = iruserRecvBufferInfo->valid_packet_count; + if (n < 1 || num_available_packets < 1) return 0; - - IRUSER_Packet* packets = (IRUSER_Packet*)malloc(buffer_info.valid_packet_count * sizeof(IRUSER_Packet)); + u32 num_packets = n < num_available_packets ? n : num_available_packets; int failed = 0; // number of bad packets + // Parse the packets - for (size_t i = 0; i < buffer_info.valid_packet_count; i++) { - // bad packet - if (!iruserParsePacket((i + buffer_info.start_index) % iruserRecvPacketCount, &packets[i - failed])) { - failed++; // retry with next packet - } + for (size_t i = 0; i < num_packets; i++) { + // if bad packet, increment number of failed packets + failed += !iruserParsePacket((i + iruserRecvBufferInfo->start_index) % iruserRecvPacketCount, &packets[i - failed]); } - *n -= failed; // update the number of packets + num_packets -= failed; // update the number of packets + + return num_packets; +} + +bool iruserGetFirstPacket(IRUSER_Packet* packet) { + if (!packet) return false; - // all packets were bad - if (*n == 0) { - free(packets); - return NULL; - } - return packets; + if (iruserRecvBufferInfo->valid_packet_count < 1) return false; + return iruserParsePacket(iruserRecBufferInfo->start_index, packet); } /// Circle Pad Pro specific request. @@ -566,4 +555,4 @@ Result iruserCPPRequestInputPolling(u8 period_ms) { Handle iruserGetServHandle() { return iruserHandle; -} +} \ No newline at end of file From a4931d3c8ad8eef81ac4c3fde052adb95465eae9 Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:38:47 -0400 Subject: [PATCH 18/26] Fixed all the compile errors I should have checked before. Removed some useless constants. Fixed definition of iruserRecvPacketDataBufferSize. Updated iruserParsePacket() to use a user-allocated buffer instead of a pointer into the recv buffer. Simplified logic for iruserParseNPackets() so that it properly gets the number of requested packets if possible. Removed iruserGetFirstPacket() since it's now redundant. --- libctru/include/3ds/services/iruser.h | 11 ++---- libctru/source/services/iruser.c | 48 ++++++++++----------------- 2 files changed, 20 insertions(+), 39 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 06ef53d3c..8c7843dd9 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -66,6 +66,7 @@ typedef struct { } IRUSER_PacketInfo; /// A packet of data sent/received to/from the IR device. +/// `payload` should be initialized by the user to point to a buffer of at least 0x4000 bytes (the maximum payload size) typedef struct { /// The magic number of the packet. Should always be 0xA5. u8 magic_number; @@ -153,12 +154,6 @@ bool iruserCirclePadProCStickRead(circlePosition* pos); */ u32 iruserGetNPackets(IRUSER_Packet packets[], u32 numpackets); -/** - * @brief Returns the first valid packet received from the IR device. - * @param [out] packet Pointer to write the packet data into. - */ -bool iruserGetFirstPacket(IRUSER_Packet* packet) - /** * @brief Initializes the IR session * @brief IR uses shared memory in non-shared mode (puts less information in shared memory) @@ -240,8 +235,8 @@ Result IRUSER_ReceiveIrNop(); Result IRUSER_ReceiveIrNopLarge(); -Result IRUSER_GetLatestReceiveErrorResult(); -Result IRUSER_GetLatestSendErrorResult(); +Result IRUSER_GetLatestReceiveErrorResult(Result* result); +Result IRUSER_GetLatestSendErrorResult(Result* result); Result IRUSER_GetConnectionStatus(IRUSER_ConnectionStatus* status); Result IRUSER_GetTryingToConnectStatus(IRUSER_TryingToConnectStatus* status); Result IRUSER_GetReceiveSizeFreeAndUsed(); diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 2a29d8552..e21510833 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -19,14 +19,11 @@ Copy of the IR:USER API from ctru-rs #endif // Misc constants -const size_t SHARED_MEM_STATUS_INFO_OFFSET = 0x00; -const size_t SHARED_MEM_RECV_BUFFER_INFO_OFFSET = 0x10 -const size_t SHARED_MEM_RECV_BUFFER_OFFSET = 0x20; -const size_t SHARED_MEM_INFO_SECTIONS_SIZE = 0x30; -const size_t PAGE_SIZE = 0x1000; -const u32 IR_BITRATE = 4; -const u8 CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID = 0x10; -const u8 PACKET_INFO_SIZE = 8; +static const size_t SHARED_MEM_STATUS_INFO_OFFSET = 0x00; +static const size_t SHARED_MEM_RECV_BUFFER_INFO_OFFSET = 0x10; +static const size_t SHARED_MEM_RECV_BUFFER_OFFSET = 0x20; +static const u32 IR_BITRATE = 4; +static const u8 CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID = 0x10; static Handle iruserHandle; static Handle iruserSharedMemHandle; @@ -76,7 +73,7 @@ static const u8 CRC_TABLE[256] = { 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 }; -static const u8 crc8ccitt(const size_t offset, const size_t size) { +static u8 crc8ccitt(const size_t offset, const size_t size) { u8 val = 0; u8 pos = 0; @@ -110,8 +107,8 @@ Result iruserInit(u32 *sharedmem_addr, u32 sharedmem_size, size_t buffer_size, s iruserStatusInfo = (IRUSER_StatusInfo*)((u8*)iruserSharedMem + SHARED_MEM_STATUS_INFO_OFFSET); iruserRecvBufferInfo = (IRUSER_BufferInfo*)((u8*)iruserSharedMem + SHARED_MEM_RECV_BUFFER_INFO_OFFSET); iruserRecvPacketInfoBuffer = (IRUSER_PacketInfo*)((u8*)iruserSharedMem + SHARED_MEM_RECV_BUFFER_OFFSET); - iruserRecvPacketDataBuffer = (u8*)iruserRecvPacketInfoBuffer + iruserRecvPacketCount * PACKET_INFO_SIZE; - iruserRecvPacketDataBufferSize = (u32)(iruserRecvPacketDataBuffer - iruserRecPacketInfoBuffer); + iruserRecvPacketDataBuffer = (u8*)iruserRecvPacketInfoBuffer + iruserRecvPacketCount * sizeof(IRUSER_PacketInfo); + iruserRecvPacketDataBufferSize = iruserRecvBufferSize - iruserRecvPacketCount * sizeof(IRUSER_PacketInfo); return ret; @@ -325,7 +322,7 @@ Result IRUSER_ReceiveIrNopLarge() { return MAKERESULT(RL_INFO, RS_NOTSUPPORTED, RM_IR, RD_NOT_IMPLEMENTED); } -Result IRUSER_GetLatestReceiveErrorResult(u32* result) { +Result IRUSER_GetLatestReceiveErrorResult(Result* result) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); @@ -338,7 +335,7 @@ Result IRUSER_GetLatestReceiveErrorResult(u32* result) { return ret; } -Result IRUSER_GetLatestSendErrorResult(u32* result) { +Result IRUSER_GetLatestSendErrorResult(Result* result) { Result ret = 0; u32 *cmdbuf = getThreadCommandBuffer(); @@ -471,7 +468,6 @@ Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputRespon bool iruserCirclePadProCStickRead(circlePosition *pos) { // Result ret = 0; - u32 n = 0; IRUSER_Packet packet; if (!iruserGetFirstPacket(&packet)) return false; if (packet.payload_length != 6) return false; @@ -485,6 +481,7 @@ bool iruserCirclePadProCStickRead(circlePosition *pos) { static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { if (packet == NULL) return false; + if (packet->payload == NULL) return false; IRUSER_PacketInfo inf = iruserRecvPacketInfoBuffer[index % iruserRecvPacketCount]; packet->magic_number = *IRUSER_PACKET_DATA(inf.offset + 0); @@ -501,7 +498,8 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { payload_offset = 3; } - packet->payload = IRUSER_PACKET_DATA(inf.offset + payload_offset); // Pointer into the receive buffer, so won't be valid after we release the data using IRUSER_ReleaseReceivedData + // Copy payload into buffer, respecting circular buffer access + for (int i = 0; i < packet->payload_length; i++) packet->payload[i] = *IRUSER_PACKET_DATA(inf.offset + payload_offset); packet->checksum = *IRUSER_PACKET_DATA(inf.offset + payload_offset + packet->payload_length); // check the checksum @@ -517,27 +515,15 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { u32 iruserGetNPackets(IRUSER_Packet packets[], u32 n) { u32 num_available_packets = iruserRecvBufferInfo->valid_packet_count; if (n < 1 || num_available_packets < 1) return 0; - - u32 num_packets = n < num_available_packets ? n : num_available_packets; - int failed = 0; // number of bad packets + int parsed = 0; - // Parse the packets - for (size_t i = 0; i < num_packets; i++) { + for (size_t i = 0; i < num_available_packets && parsed < n; i++) { // if bad packet, increment number of failed packets - failed += !iruserParsePacket((i + iruserRecvBufferInfo->start_index) % iruserRecvPacketCount, &packets[i - failed]); + parsed += iruserParsePacket((i + iruserRecvBufferInfo->start_index) % iruserRecvPacketCount, &packets[parsed]); } - - num_packets -= failed; // update the number of packets - return num_packets; -} - -bool iruserGetFirstPacket(IRUSER_Packet* packet) { - if (!packet) return false; - - if (iruserRecvBufferInfo->valid_packet_count < 1) return false; - return iruserParsePacket(iruserRecBufferInfo->start_index, packet); + return parsed; } /// Circle Pad Pro specific request. From d5adcb40105b3d1eee7fe63ef62c066c2c84721d Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Wed, 1 Oct 2025 11:48:23 -0400 Subject: [PATCH 19/26] Slightly edit iruserParsePacket() to do the checksum check before copying over the data, so that it doesn't do the copy for no reason. --- libctru/source/services/iruser.c | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index e21510833..9b9f35f4f 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -498,17 +498,14 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { payload_offset = 3; } - // Copy payload into buffer, respecting circular buffer access - for (int i = 0; i < packet->payload_length; i++) packet->payload[i] = *IRUSER_PACKET_DATA(inf.offset + payload_offset); packet->checksum = *IRUSER_PACKET_DATA(inf.offset + payload_offset + packet->payload_length); - - // check the checksum u8 checksum = crc8ccitt(inf.offset, payload_offset + packet->payload_length - 1); // Checksum over the entire packet, including header (https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure) - if (packet->checksum != checksum) { // bad data - packet->payload = NULL; - return false; + + if (packet->checksum = checksum) { + for (int i = 0; i < packet->payload_length; i++) packet->payload[i] = *IRUSER_PACKET_DATA(inf.offset + payload_offset); + return true; } - return true; + return false; } /// Read and parse the current packets received from the IR device. From 0fed2627c5059fbf80456a45c53539366f443f65 Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Wed, 1 Oct 2025 12:08:57 -0400 Subject: [PATCH 20/26] Fix some extra compile errors that somehow didn't show up --- libctru/source/services/iruser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 9b9f35f4f..e4ff486bc 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -469,7 +469,7 @@ Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputRespon bool iruserCirclePadProCStickRead(circlePosition *pos) { // Result ret = 0; IRUSER_Packet packet; - if (!iruserGetFirstPacket(&packet)) return false; + if (iruserGetNPackets(&packet, 1) != 1) return false; if (packet.payload_length != 6) return false; if (packet.payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return false; pos->dx = (s16)(packet.payload[1] | ((packet.payload[2] & 0x0F) << 8)); @@ -501,7 +501,7 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { packet->checksum = *IRUSER_PACKET_DATA(inf.offset + payload_offset + packet->payload_length); u8 checksum = crc8ccitt(inf.offset, payload_offset + packet->payload_length - 1); // Checksum over the entire packet, including header (https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure) - if (packet->checksum = checksum) { + if (packet->checksum == checksum) { for (int i = 0; i < packet->payload_length; i++) packet->payload[i] = *IRUSER_PACKET_DATA(inf.offset + payload_offset); return true; } From b1d12752d3fd25e4dadd974e9f1d9f2a0c23936e Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:39:21 -0400 Subject: [PATCH 21/26] Changed iruserCirclePadProCStickRead() and iruserGetCirclePadProState() to take in a packet as a parameter (which should be a packet containing Circle Pad Pro state data) --- libctru/include/3ds/services/iruser.h | 4 ++-- libctru/source/services/iruser.c | 28 ++++++++++++--------------- 2 files changed, 14 insertions(+), 18 deletions(-) diff --git a/libctru/include/3ds/services/iruser.h b/libctru/include/3ds/services/iruser.h index 8c7843dd9..0335f6d16 100644 --- a/libctru/include/3ds/services/iruser.h +++ b/libctru/include/3ds/services/iruser.h @@ -138,13 +138,13 @@ void iruserProcessSharedMemory(void(*process_fn)(u8*)); * @brief Gets circle pad pro inputs state * @param response Pointer to write data to */ -Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputResponse* response); +bool iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputResponse* response); /** * @brief Reads c-stick position from circle pad pro * @param pos Pointer to write data to */ -bool iruserCirclePadProCStickRead(circlePosition* pos); +bool iruserCirclePadProCStickRead(IRUSER_Packet* packet, circlePosition* pos); /** * @brief gets the packets received from the IR device. diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index e4ff486bc..4a3b76fda 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -451,31 +451,27 @@ IRUSER_StatusInfo iruserGetStatusInfo() { return *iruserStatusInfo; } -Result iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputResponse* response) { - if (!packet) goto failure; - if (!packet->payload) goto failure; - if (packet->payload_length != 6) goto failure; - if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) goto failure; +bool iruserGetCirclePadProState(IRUSER_Packet* packet, circlePadProInputResponse* response) { + if (!packet) return false; + if (!packet->payload) return false; + if (packet->payload_length != 6) return false; + if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return false; response->cstick.csPos.dx = (u16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); response->cstick.csPos.dy = (u16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); response->status_raw = packet->payload[4]; response->unknown_field = packet->payload[5]; - return 0; - failure: - memset(response, 0, sizeof(circlePadProInputResponse)); - return -1; + return true; + } -bool iruserCirclePadProCStickRead(circlePosition *pos) { - // Result ret = 0; - IRUSER_Packet packet; - if (iruserGetNPackets(&packet, 1) != 1) return false; - if (packet.payload_length != 6) return false; - if (packet.payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return false; +bool iruserCirclePadProCStickRead(IRUSER_Packet* packet, circlePosition *pos) { + if (!packet) return false; + if (!packet->payload) return false; + if (packet->payload_length != 6) return false; + if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return false; pos->dx = (s16)(packet.payload[1] | ((packet.payload[2] & 0x0F) << 8)); pos->dy = (s16)(((packet.payload[2] & 0xF0) >> 4) | ((packet.payload[3]) << 4)); - IRUSER_ReleaseReceivedData(1); return 0; } From 61c3c87591da662b0eaad85e4dfebeb553c94f94 Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:42:29 -0400 Subject: [PATCH 22/26] Fix accidental used of . instead of -> --- libctru/source/services/iruser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 4a3b76fda..e7aff1783 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -469,8 +469,8 @@ bool iruserCirclePadProCStickRead(IRUSER_Packet* packet, circlePosition *pos) { if (!packet->payload) return false; if (packet->payload_length != 6) return false; if (packet->payload[0] != CIRCLE_PAD_PRO_INPUT_RESPONSE_PACKET_ID) return false; - pos->dx = (s16)(packet.payload[1] | ((packet.payload[2] & 0x0F) << 8)); - pos->dy = (s16)(((packet.payload[2] & 0xF0) >> 4) | ((packet.payload[3]) << 4)); + pos->dx = (s16)(packet->payload[1] | ((packet->payload[2] & 0x0F) << 8)); + pos->dy = (s16)(((packet->payload[2] & 0xF0) >> 4) | ((packet->payload[3]) << 4)); return 0; } From 49c18c7bf3e5e2f57f69aca7cd33125559f2b401 Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:43:14 -0400 Subject: [PATCH 23/26] Fix iruserParsePacket reading payload large/small and length value incorrectly Fix iruserParsePacket only reading first byte of payload --- libctru/source/services/iruser.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index e7aff1783..685b2b685 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -482,15 +482,14 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { IRUSER_PacketInfo inf = iruserRecvPacketInfoBuffer[index % iruserRecvPacketCount]; packet->magic_number = *IRUSER_PACKET_DATA(inf.offset + 0); packet->destination_network_id = *IRUSER_PACKET_DATA(inf.offset + 1); - bool large = *IRUSER_PACKET_DATA(inf.offset + 2) & (1 << 7); - + bool large = *IRUSER_PACKET_DATA(inf.offset + 2) & (1 << 6); u32 payload_offset = 0; if (large) { - packet->payload_length = (*IRUSER_PACKET_DATA(inf.offset + 2) << 8) + *IRUSER_PACKET_DATA(inf.offset + 3); + packet->payload_length = ((*IRUSER_PACKET_DATA(inf.offset + 2) & 0x4F) << 8) + *IRUSER_PACKET_DATA(inf.offset + 3); payload_offset = 4; } else { - packet->payload_length = *IRUSER_PACKET_DATA(inf.offset + 2); + packet->payload_length = *IRUSER_PACKET_DATA(inf.offset + 2) & 0x4F; // bottom 6 bits payload_offset = 3; } @@ -498,7 +497,7 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { u8 checksum = crc8ccitt(inf.offset, payload_offset + packet->payload_length - 1); // Checksum over the entire packet, including header (https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure) if (packet->checksum == checksum) { - for (int i = 0; i < packet->payload_length; i++) packet->payload[i] = *IRUSER_PACKET_DATA(inf.offset + payload_offset); + for (int i = 0; i < packet->payload_length; i++) packet->payload[i] = *IRUSER_PACKET_DATA(inf.offset + payload_offset + i); return true; } return false; From 289f5a41fb2bc78597705a1c8ad3523d91c1a22c Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:47:45 -0400 Subject: [PATCH 24/26] Add preliminary iruserCPPRequestCalibrationData() implementation --- libctru/source/services/iruser.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 685b2b685..a76852e8c 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -531,6 +531,19 @@ Result iruserCPPRequestInputPolling(u8 period_ms) { return IRUSER_SendIrNop(3, ir_request); } +/// Circle Pad Pro specific request. +/// +/// This will send a packet to the CPP requesting it to send back a packet with the calibration data +Result iruserCPPRequestCalibrationData() { + u8 ir_request[4] = { + 2, + 0, + 0, + 0 + }; + return IRUSER_SendIrNop(4, ir_request); +} + Handle iruserGetServHandle() { return iruserHandle; } \ No newline at end of file From e44a8321676ad6eb1ab6d4cec0be3149daa73c1c Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Thu, 2 Oct 2025 10:50:57 -0400 Subject: [PATCH 25/26] Fix off-by-one error in iruserParsePacket() checksum calculation --- libctru/source/services/iruser.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index a76852e8c..73dda04e8 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -494,7 +494,7 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { } packet->checksum = *IRUSER_PACKET_DATA(inf.offset + payload_offset + packet->payload_length); - u8 checksum = crc8ccitt(inf.offset, payload_offset + packet->payload_length - 1); // Checksum over the entire packet, including header (https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure) + u8 checksum = crc8ccitt(inf.offset, payload_offset + packet->payload_length); // Checksum over the entire packet, including header (https://www.3dbrew.org/wiki/IRUSER_Shared_Memory#Packet_structure) if (packet->checksum == checksum) { for (int i = 0; i < packet->payload_length; i++) packet->payload[i] = *IRUSER_PACKET_DATA(inf.offset + payload_offset + i); From b646fc4f03c11ed57eb2e59f8678acf1602a721f Mon Sep 17 00:00:00 2001 From: me <82814680+syelan34@users.noreply.github.com> Date: Thu, 2 Oct 2025 13:04:39 -0400 Subject: [PATCH 26/26] Fixed bit mask for payload length --- libctru/source/services/iruser.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libctru/source/services/iruser.c b/libctru/source/services/iruser.c index 73dda04e8..2a8505f23 100644 --- a/libctru/source/services/iruser.c +++ b/libctru/source/services/iruser.c @@ -486,10 +486,10 @@ static bool iruserParsePacket(size_t index, IRUSER_Packet* packet) { u32 payload_offset = 0; if (large) { - packet->payload_length = ((*IRUSER_PACKET_DATA(inf.offset + 2) & 0x4F) << 8) + *IRUSER_PACKET_DATA(inf.offset + 3); + packet->payload_length = ((*IRUSER_PACKET_DATA(inf.offset + 2) & 0x3F) << 8) | *IRUSER_PACKET_DATA(inf.offset + 3); payload_offset = 4; } else { - packet->payload_length = *IRUSER_PACKET_DATA(inf.offset + 2) & 0x4F; // bottom 6 bits + packet->payload_length = *IRUSER_PACKET_DATA(inf.offset + 2) & 0x3F; // bottom 6 bits payload_offset = 3; }