mirror of
https://github.com/switchbrew/libnx.git
synced 2025-06-21 12:32:40 +02:00
btdrv: Added HidReportEventInfo support.
This commit is contained in:
parent
2878b97f3a
commit
b18912980e
@ -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);
|
||||
|
||||
///@}
|
||||
|
||||
|
@ -1,11 +1,18 @@
|
||||
#define NX_SERVICE_ASSUME_NON_DOMAIN
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#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;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user