From b18912980e0076160dbdfab4cc6d99028ec884fd Mon Sep 17 00:00:00 2001 From: yellows8 Date: Tue, 21 Jul 2020 11:52:14 -0400 Subject: [PATCH] btdrv: Added HidReportEventInfo support. --- nx/include/switch/services/btdrv.h | 162 ++++++++++++++++++++++++++- nx/source/services/btdrv.c | 174 ++++++++++++++++++++++++++--- 2 files changed, 322 insertions(+), 14 deletions(-) diff --git a/nx/include/switch/services/btdrv.h b/nx/include/switch/services/btdrv.h index a54cb4ee..bdd59211 100644 --- a/nx/include/switch/services/btdrv.h +++ b/nx/include/switch/services/btdrv.h @@ -18,6 +18,13 @@ typedef enum { BtdrvBluetoothPropertyType_Unknown6 = 6, ///< Unknown. 1-byte. The default is value 0x68. } BtdrvBluetoothPropertyType; +/// HidEventType +typedef enum { + BtdrvHidEventType_Unknown4 = 4, ///< Unknown. + BtdrvHidEventType_Unknown8 = 8, ///< Unknown. + BtdrvHidEventType_Unknown9 = 9, ///< Unknown. +} BtdrvHidEventType; + /// Address typedef struct { u8 address[0x6]; ///< Address @@ -48,6 +55,106 @@ typedef struct { u8 data[0x2BC]; ///< Data } BtdrvHidReport; +/// Data for \ref btdrvGetHidReportEventInfo. The data stored here depends on the \ref BtdrvHidEventType. +typedef struct { + union { + u8 data[0x480]; ///< Raw data. + + struct { + u32 unk_x0; ///< Always 0. + u8 unk_x4; ///< Always 0. + BtdrvAddress addr; ///< \ref BtdrvAddress + u8 pad; ///< Padding + u16 size; ///< Size of the below data. + u8 data[]; ///< Data. + } type4; ///< ::BtdrvHidEventType_Unknown4 + + struct { + union { + u8 data[0xC]; ///< Raw data. + + struct { + u32 res; ///< 0 = success, non-zero = error. + BtdrvAddress addr; ///< \ref BtdrvAddress + u8 pad[2]; ///< Padding + }; + }; + } type8; ///< ::BtdrvHidEventType_Unknown8 + + struct { + union { + union { + u8 rawdata[0x290]; ///< Raw data. + + struct { + BtdrvAddress addr; ///< \ref BtdrvAddress + u8 pad[2]; ///< Padding + u32 unk_x0; ///< Unknown. hid-sysmodule only uses the below data when this field is 0. + BtdrvHidData data; ///< \ref BtdrvHidData + u8 pad2[2]; ///< Padding + }; + } hid_data; ///< Pre-9.0.0 + + union { + u8 rawdata[0x2C8]; ///< Raw data. + + struct { + u32 unk_x0; ///< Unknown. hid-sysmodule only uses the below report when this field is 0. + BtdrvAddress addr; ///< \ref BtdrvAddress + BtdrvHidReport report; ///< \ref BtdrvHidReport + }; + } hid_report; ///< [9.0.0+] + }; + } type9; ///< ::BtdrvHidEventType_Unknown9 + }; +} BtdrvHidReportEventInfo; + +/// The raw sharedmem data for HidReportEventInfo. +typedef struct { + struct { + u8 type; ///< \ref BtdrvHidEventType + u8 pad[7]; + u64 tick; + u64 size; + } hdr; + + union { + struct { + struct { + u8 unused[0x3]; ///< Unused + BtdrvAddress addr; ///< \ref BtdrvAddress + u8 unused2[0x3]; ///< Unused + u16 size; ///< Size of the below data. + u8 data[]; ///< Data. + } v1; ///< Pre-9.0.0 + + struct { + u8 unused[0x5]; ///< Unused + BtdrvAddress addr; ///< \ref BtdrvAddress + u8 pad; ///< Padding + u16 size; ///< Size of the below data. + u8 data[]; ///< Data. + } v9; ///< [9.0.0+] + } type4; ///< ::BtdrvHidEventType_Unknown4 + + struct { + u8 data[0xC]; ///< Raw data. + } type8; ///< ::BtdrvHidEventType_Unknown8 + + struct { + union { + struct { + u8 rawdata[0x290]; ///< Raw data. + } hid_data; ///< Pre-9.0.0 + + struct { + u8 rawdata[0x2C8]; ///< Raw data. + } hid_report; ///< [9.0.0+] + }; + } type9; ///< ::BtdrvHidEventType_Unknown9 + } data; +} BtdrvHidReportEventInfoBufferData; + /// PlrStatistics typedef struct { u8 unk_x0[0x84]; ///< Unknown @@ -93,6 +200,18 @@ typedef struct { u8 unk_x0[0x18]; ///< Unknown } BtdrvGattId; +/// CircularBuffer +typedef struct { + Mutex mutex; + void* event_type; ///< Not set with sharedmem. + u8 data[0x2710]; + s32 write_offset; + s32 read_offset; + u64 utilization; + char name[0x11]; + u8 initialized; +} BtdrvCircularBuffer; + /// Initialize btdrv. Result btdrvInitialize(void); @@ -155,6 +274,28 @@ Result btdrvSetHidReport(BtdrvAddress addr, u32 type, BtdrvHidReport *buffer); */ Result btdrvGetHidReport(BtdrvAddress addr, u8 unk, u32 type); +/** + * @brief RegisterHidReportEvent + * @note This also does sharedmem init/handling if needed, on [7.0.0+]. + * @note The Event must be closed by the user once finished with it. + * @param[out] out_event Output Event with autoclear=true. + */ +Result btdrvRegisterHidReportEvent(Event* out_event); + +/** + * @brief GetHidReportEventInfo + * @note \ref btdrvRegisterHidReportEvent must be used before this, on [7.0.0+]. + * @note This is used by hid-sysmodule. When used by other processes, hid/user-process will conflict. No events will be received by that user-process, or it will be corrupted, etc. + * @note [7.0.0+] When data isn't available, the type is set to ::BtdrvHidEventType_Unknown4, with the buffer cleared to all-zero. + * @param[out] buffer Output buffer, see \ref BtdrvHidReportEventInfo. + * @param[in] size Output buffer size. + * @oaram[out] type \ref BtdrvHidEventType + */ +Result btdrvGetHidReportEventInfo(void* buffer, size_t size, BtdrvHidEventType *type); + +/// Gets the SharedMemory addr for HidReportEventInfo (\ref BtdrvCircularBuffer), only valid when \ref btdrvRegisterHidReportEvent was previously used, on [7.0.0+]. +void* btdrvGetHidReportEventInfoSharedmemAddr(void); + /** * @brief ReadGattCharacteristic * @note Only available on [5.0.0+]. @@ -230,7 +371,7 @@ Result btdrvUnregisterGattNotification(bool flag, u32 unk, const BtdrvGattId *id * @brief GetLeEventInfo * @note Only available on [5.0.0+]. * @note The state used by this is reset after writing the data to output. - * @param[in] buffer Output buffer. 0x400-bytes from state is written here. + * @param[out] buffer Output buffer. 0x400-bytes from state is written here. * @param[in] size Output buffer size. * @oaram[out] type Output BleEventType. */ @@ -244,3 +385,22 @@ Result btdrvGetLeEventInfo(void* buffer, size_t size, u32 *type); */ Result btdrvRegisterBleHidEvent(Event* out_event); +///@name CircularBuffer +///@{ + +/** + * @brief Read + * @note Used by \ref btdrvGetHidReportEventInfo on [7.0.0+]. + * @param c \ref BtdrvCircularBuffer + */ +void* btdrvCircularBufferRead(BtdrvCircularBuffer *c); + +/** + * @brief Free + * @note Used by \ref btdrvGetHidReportEventInfo on [7.0.0+]. + * @param c \ref BtdrvCircularBuffer + */ +bool btdrvCircularBufferFree(BtdrvCircularBuffer *c); + +///@} + diff --git a/nx/source/services/btdrv.c b/nx/source/services/btdrv.c index fa365cb0..beea9801 100644 --- a/nx/source/services/btdrv.c +++ b/nx/source/services/btdrv.c @@ -1,11 +1,18 @@ #define NX_SERVICE_ASSUME_NON_DOMAIN #include +#include #include "service_guard.h" +#include "arm/counter.h" +#include "kernel/shmem.h" #include "runtime/hosversion.h" #include "services/btdrv.h" static Service g_btdrvSrv; +static bool g_btdrvSharedmemInitialized; +static SharedMemory g_btdrvSharedmem; +static BtdrvCircularBuffer *g_btdrvCircularBuffer; + static Result _btdrvCmdNoIO(u32 cmd_id); NX_GENERATE_SERVICE_GUARD(btdrv); @@ -19,6 +26,8 @@ Result _btdrvInitialize(void) { } void _btdrvCleanup(void) { + g_btdrvCircularBuffer = NULL; + shmemClose(&g_btdrvSharedmem); serviceClose(&g_btdrvSrv); } @@ -30,6 +39,29 @@ static Result _btdrvCmdNoIO(u32 cmd_id) { return serviceDispatch(&g_btdrvSrv, cmd_id); } +static Result _btdrvCmdGetHandle(Handle* handle_out, u32 cmd_id) { + return serviceDispatch(&g_btdrvSrv, cmd_id, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = handle_out, + ); +} + +static Result _btdrvCmdGetEvent(Event* out_event, bool autoclear, u32 cmd_id) { + Handle tmp_handle = INVALID_HANDLE; + Result rc = 0; + + rc = _btdrvCmdGetHandle(&tmp_handle, cmd_id); + if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, autoclear); + return rc; +} + +static Result _btdrvCmdOutU32OutBuf(void* buffer, size_t size, u32 *out, u32 cmd_id) { + return serviceDispatchOut(&g_btdrvSrv, cmd_id, *out, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out }, + .buffers = { { buffer, size } }, + ); +} + Result btdrvGetAdapterProperties(BtdrvAdapterProperty *property) { return serviceDispatch(&g_btdrvSrv, 5, .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out | SfBufferAttr_FixedSize }, @@ -93,6 +125,84 @@ Result btdrvGetHidReport(BtdrvAddress addr, u8 unk, u32 type) { return serviceDispatchIn(&g_btdrvSrv, 22, in); } +Result btdrvRegisterHidReportEvent(Event* out_event) { + Result rc=0; + Handle tmphandle=0; + + if (hosversionAtLeast(7,0,0)) { + if (!g_btdrvSharedmemInitialized) { + rc = _btdrvCmdGetHandle(&tmphandle, 38); // GetHidReportEventInfo + + if (R_SUCCEEDED(rc)) { + shmemLoadRemote(&g_btdrvSharedmem, tmphandle, 0x3000, Perm_Rw); + + rc = shmemMap(&g_btdrvSharedmem); + } + + if (R_SUCCEEDED(rc)) { + g_btdrvSharedmemInitialized = true; + g_btdrvCircularBuffer = shmemGetAddr(&g_btdrvSharedmem); + } + else shmemClose(&g_btdrvSharedmem); + } + else { + while (btdrvCircularBufferRead(g_btdrvCircularBuffer)) btdrvCircularBufferFree(g_btdrvCircularBuffer); + } + } + + if (R_SUCCEEDED(rc)) rc = _btdrvCmdGetEvent(out_event, true, 37); // RegisterHidReportEvent + return rc; +} + +Result btdrvGetHidReportEventInfo(void* buffer, size_t size, BtdrvHidEventType *type) { + if (hosversionAtLeast(7,0,0)) { + BtdrvHidReportEventInfo *info = (BtdrvHidReportEventInfo*)buffer; + BtdrvHidReportEventInfoBufferData *data_ptr = NULL; + memset(buffer, 0, size); + if (g_btdrvCircularBuffer==NULL) return MAKERESULT(Module_Libnx, LibnxError_NotInitialized); + for (; (data_ptr = btdrvCircularBufferRead(g_btdrvCircularBuffer)); btdrvCircularBufferFree(g_btdrvCircularBuffer)) { + *type = data_ptr->hdr.type; + if (*type == BtdrvHidEventType_Unknown4) { + if (armTicksToNs(armGetSystemTick() - data_ptr->hdr.tick) >= 100000001) continue; + } + break; + } + if (data_ptr == NULL) { + *type = BtdrvHidEventType_Unknown4; + return 0; + } + if (*type == BtdrvHidEventType_Unknown9) { + if (hosversionBefore(9,0,0)) memcpy(info->type9.hid_data.rawdata, data_ptr->data.type9.hid_data.rawdata, sizeof(info->type9.hid_data.rawdata)); + else memcpy(info->type9.hid_report.rawdata, data_ptr->data.type9.hid_report.rawdata, sizeof(info->type9.hid_report.rawdata)); + } + else if (*type == BtdrvHidEventType_Unknown8) memcpy(info->type8.data, data_ptr->data.type8.data, sizeof(info->type8.data)); + else if (*type == BtdrvHidEventType_Unknown4) { + u16 tmpsize = hosversionBefore(9,0,0) ? data_ptr->data.type4.v1.size : data_ptr->data.type4.v9.size; + if (size < 0xE) return MAKERESULT(Module_Libnx, LibnxError_BadInput); + if (tmpsize > size-0xE) tmpsize = size-0xE; + info->type4.unk_x0 = 0; + info->type4.size = tmpsize; + if (hosversionBefore(9,0,0)) memcpy(info->type4.data, data_ptr->data.type4.v1.data, tmpsize); + else memcpy(info->type4.data, data_ptr->data.type4.v9.data, tmpsize); + + if (hosversionBefore(9,0,0)) memcpy(&info->type4.addr, &data_ptr->data.type4.v1.addr, sizeof(BtdrvAddress)); + else memcpy(&info->type4.addr, &data_ptr->data.type4.v9.addr, sizeof(BtdrvAddress)); + } + else return MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen); // sdknso would Abort here. + btdrvCircularBufferFree(g_btdrvCircularBuffer); + return 0; + } + + u32 tmp_type=0; + Result rc = _btdrvCmdOutU32OutBuf(buffer, size, &tmp_type, 38); + if (R_SUCCEEDED(rc) && type) *type = tmp_type; + return rc; +} + +void* btdrvGetHidReportEventInfoSharedmemAddr(void) { + return g_btdrvCircularBuffer; +} + Result btdrvReadGattCharacteristic(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1) { if (hosversionBefore(5,0,0)) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); @@ -202,10 +312,7 @@ Result btdrvGetLeEventInfo(void* buffer, size_t size, u32 *type) { return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); u32 cmd_id = hosversionBefore(6,0,0) ? 95 : 96; - return serviceDispatchOut(&g_btdrvSrv, cmd_id, *type, - .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out }, - .buffers = { { buffer, size } }, - ); + return _btdrvCmdOutU32OutBuf(buffer, size, type, cmd_id); } Result btdrvRegisterBleHidEvent(Event* out_event) { @@ -213,14 +320,55 @@ Result btdrvRegisterBleHidEvent(Event* out_event) { return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); u32 cmd_id = hosversionBefore(6,0,0) ? 96 : 97; - Handle tmp_handle = INVALID_HANDLE; - Result rc = 0; - - rc = serviceDispatch(&g_btdrvSrv, cmd_id, - .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, - .out_handles = &tmp_handle, - ); - if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, true); - return rc; + return _btdrvCmdGetEvent(out_event, true, cmd_id); +} + +// CircularBuffer + +void* btdrvCircularBufferRead(BtdrvCircularBuffer *c) { + if (c==NULL || !c->initialized) return NULL; + + do { + s32 read_offset = atomic_load_explicit(&c->read_offset, memory_order_acquire); + s32 write_offset = atomic_load_explicit(&c->write_offset, memory_order_acquire); + if (read_offset == write_offset) return NULL; + + u8 *data_ptr = &c->data[read_offset]; + if (read_offset >= sizeof(c->data)) return NULL; + + if (*data_ptr != 0xFF) return data_ptr; + else { + if (!c->initialized) return NULL; + read_offset = atomic_load_explicit(&c->read_offset, memory_order_acquire); + write_offset = atomic_load_explicit(&c->write_offset, memory_order_acquire); + if (read_offset == write_offset) continue; + + data_ptr = &c->data[read_offset]; + u64 tmpsize = read_offset + 0x18; + if (tmpsize < sizeof(c->data)) tmpsize += *((u64*)&data_ptr[0x10]); + if (tmpsize >= sizeof(c->data)) tmpsize = 0; + atomic_store_explicit(&c->read_offset, tmpsize, memory_order_release); + } + } while (c->initialized); + + return NULL; +} + +bool btdrvCircularBufferFree(BtdrvCircularBuffer *c) { + if (c==NULL || !c->initialized) return false; + + s32 read_offset = atomic_load_explicit(&c->read_offset, memory_order_acquire); + s32 write_offset = atomic_load_explicit(&c->write_offset, memory_order_acquire); + if (read_offset == write_offset) return false; + + u8 *data_ptr = &c->data[read_offset]; + if (read_offset >= sizeof(c->data)) false; + + u64 tmpsize = read_offset + 0x18; + if (tmpsize < sizeof(c->data)) tmpsize += *((u64*)&data_ptr[0x10]); + if (tmpsize >= sizeof(c->data)) tmpsize = 0; + atomic_store_explicit(&c->read_offset, tmpsize, memory_order_release); + + return true; }