diff --git a/nx/include/switch.h b/nx/include/switch.h index 5af99746..f8ea0f27 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -95,6 +95,7 @@ extern "C" { #include "switch/services/lr.h" #include "switch/services/bt.h" #include "switch/services/btdrv.h" +#include "switch/services/btmu.h" #include "switch/services/spl.h" #include "switch/services/ncm.h" #include "switch/services/psc.h" diff --git a/nx/include/switch/services/btdrv.h b/nx/include/switch/services/btdrv.h index 6f889915..d10d8406 100644 --- a/nx/include/switch/services/btdrv.h +++ b/nx/include/switch/services/btdrv.h @@ -230,6 +230,21 @@ typedef struct { u8 unk_x0[0x3E]; ///< Unknown } BtdrvBleAdvertiseFilter; +/// BleAdvertisePacketParameter +typedef struct { + u8 data[0x8]; ///< Unknown +} BtdrvBleAdvertisePacketParameter; + +/// BleScanResult +typedef struct { + u8 unk_x0[0x148]; ///< Unknown +} BtdrvBleScanResult; + +/// BleConnectionInfo +typedef struct { + u8 unk_x0[0xC]; ///< Unknown +} BtdrvBleConnectionInfo; + /// GattAttributeUuid typedef struct { u8 unk_x0[0x14]; ///< Unknown diff --git a/nx/include/switch/services/btmu.h b/nx/include/switch/services/btmu.h new file mode 100644 index 00000000..e62c693b --- /dev/null +++ b/nx/include/switch/services/btmu.h @@ -0,0 +1,275 @@ +/** + * @file btmu.h + * @brief btm:u (btm user) service IPC wrapper. + * @note Only available on [5.0.0+]. + * @author yellows8 + */ +#pragma once +#include "../types.h" +#include "../kernel/event.h" +#include "../services/btdrv.h" +#include "../sf/service.h" + +/// GattService +typedef struct { + u8 unk_x0[0x24]; ///< Unknown +} BtmuGattService; + +/// GattCharacteristic +typedef struct { + u8 unk_x0[0x24]; ///< Unknown +} BtmuGattCharacteristic; + +/// GattDescriptor +typedef struct { + u8 unk_x0[0x20]; ///< Unknown +} BtmuGattDescriptor; + +/// BleDataPath +typedef struct { + u8 unk_x0[0x18]; ///< Unknown +} BtmuBleDataPath; + +/// Initialize btm:u. +Result btmuInitialize(void); + +/// Exit btm:u. +void btmuExit(void); + +/// Gets the Service object for the actual btm:u service session. This object must be closed by the user once finished using cmds with this. +Result btmuGetServiceSession(Service* srv_out); + +/// Gets the Service object for IBtmUserCore. +Service* btmuGetServiceSession_IBtmUserCore(void); + +/** + * @brief AcquireBleScanEvent + * @note The Event must be closed by the user once finished with it. + * @param[out] out_event Output Event with autoclear=true. + */ +Result btmuAcquireBleScanEvent(Event* out_event); + +/** + * @brief GetBleScanFilterParameter + * @param[in] unk Unknown + * @param[out] out \ref BtdrvBleAdvertisePacketParameter + */ +Result btmuGetBleScanFilterParameter(u16 unk, BtdrvBleAdvertisePacketParameter *out); + +/** + * @brief GetBleScanFilterParameter2 + * @param[in] unk Unknown + * @param[out] out \ref BtdrvGattAttributeUuid + */ +Result btmuGetBleScanFilterParameter2(u16 unk, BtdrvGattAttributeUuid *out); + +/** + * @brief StartBleScanForGeneral + * @param[in] param \ref BtdrvBleAdvertisePacketParameter + */ +Result btmuStartBleScanForGeneral(BtdrvBleAdvertisePacketParameter param); + +/** + * @brief StopBleScanForGeneral + */ +Result btmuStopBleScanForGeneral(void); + +/** + * @brief GetBleScanResultsForGeneral + * @param[out] results Output array of \ref BtdrvBleScanResult. + * @param[in] count Size of the results array in entries. + * @param[out] total_out Total output entries. + */ +Result btmuGetBleScanResultsForGeneral(BtdrvBleScanResult *results, u8 count, u8 *total_out); + +/** + * @brief StartBleScanForPaired + * @param[in] param \ref BtdrvBleAdvertisePacketParameter + */ +Result btmuStartBleScanForPaired(BtdrvBleAdvertisePacketParameter param); + +/** + * @brief StopBleScanForPaired + */ +Result btmuStopBleScanForPaired(void); + +/** + * @brief StartBleScanForSmartDevice + * @param[in] uuid \ref BtdrvGattAttributeUuid + */ +Result btmuStartBleScanForSmartDevice(const BtdrvGattAttributeUuid *uuid); + +/** + * @brief StopBleScanForSmartDevice + */ +Result btmuStopBleScanForSmartDevice(void); + +/** + * @brief GetBleScanResultsForSmartDevice + * @param[out] results Output array of \ref BtdrvBleScanResult. + * @param[in] count Size of the results array in entries. + * @param[out] total_out Total output entries. + */ +Result btmuGetBleScanResultsForSmartDevice(BtdrvBleScanResult *results, u8 count, u8 *total_out); + +/** + * @brief AcquireBleConnectionEvent + * @note The Event must be closed by the user once finished with it. + * @param[out] out_event Output Event with autoclear=true. + */ +Result btmuAcquireBleConnectionEvent(Event* out_event); + +/** + * @brief BleConnect + * @param[in] addr \ref BtdrvAddress + */ +Result btmuBleConnect(BtdrvAddress addr); + +/** + * @brief BleDisconnect + * @param[in] unk Unknown + */ +Result btmuBleDisconnect(u32 unk); + +/** + * @brief BleGetConnectionState + * @param[out] info Output array of \ref BtdrvBleConnectionInfo. + * @param[in] count Size of the info array in entries. + * @param[out] total_out Total output entries. + */ +Result btmuBleGetConnectionState(BtdrvBleConnectionInfo *info, u8 count, u8 *total_out); + +/** + * @brief AcquireBlePairingEvent + * @note The Event must be closed by the user once finished with it. + * @param[out] out_event Output Event with autoclear=true. + */ +Result btmuAcquireBlePairingEvent(Event* out_event); + +/** + * @brief BlePairDevice + * @param[in] param \ref BtdrvBleAdvertisePacketParameter + * @param[in] unk Unknown + */ +Result btmuBlePairDevice(BtdrvBleAdvertisePacketParameter param, u32 unk); + +/** + * @brief BleUnPairDevice + * @param[in] param \ref BtdrvBleAdvertisePacketParameter + * @param[in] unk Unknown + */ +Result btmuBleUnPairDevice(BtdrvBleAdvertisePacketParameter param, u32 unk); + +/** + * @brief BleUnPairDevice2 + * @param[in] addr \ref BtdrvAddress + * @param[in] param \ref BtdrvBleAdvertisePacketParameter + */ +Result btmuBleUnPairDevice2(BtdrvAddress addr, BtdrvBleAdvertisePacketParameter param); + +/** + * @brief BleGetPairedDevices + * @param[in] param \ref BtdrvBleAdvertisePacketParameter + * @param[out] addrs Output array of \ref BtdrvAddress. + * @param[in] count Size of the addrs array in entries. + * @param[out] total_out Total output entries. + */ +Result btmuBleGetPairedDevices(BtdrvBleAdvertisePacketParameter param, BtdrvAddress *addrs, u8 count, u8 *total_out); + +/** + * @brief AcquireBleServiceDiscoveryEvent + * @note The Event must be closed by the user once finished with it. + * @param[out] out_event Output Event with autoclear=true. + */ +Result btmuAcquireBleServiceDiscoveryEvent(Event* out_event); + +/** + * @brief GetGattServices + * @param[in] unk Unknown + * @param[out] services Output array of \ref BtmuGattService. + * @param[in] count Size of the services array in entries. + * @param[out] total_out Total output entries. + */ +Result btmuGetGattServices(u32 unk, BtmuGattService *services, u8 count, u8 *total_out); + +/** + * @brief GetGattService + * @param[in] unk Unknown + * @param[in] uuid \ref BtdrvGattAttributeUuid + * @param[out] service \ref BtmuGattService + * @param[out] total_out Total output entries. + */ +Result btmuGetGattService(u32 unk, const BtdrvGattAttributeUuid *uuid, BtmuGattService *service, u8 *total_out); + +/** + * @brief GetGattIncludedServices + * @param[in] unk0 Unknown + * @param[in] unk1 Unknown + * @param[out] services \ref BtmuGattService + * @param[in] count Size of the services array in entries. + * @param[out] out Output value. + */ +Result btmuGetGattIncludedServices(u32 unk0, u16 unk1, BtmuGattService *services, u8 count, u8 *out); + +/** + * @brief GetBelongingGattService + * @param[in] unk0 Unknown + * @param[in] unk1 Unknown + * @param[out] service \ref BtmuGattService + * @param[out] total_out Total output entries. + */ +Result btmuGetBelongingGattService(u32 unk0, u16 unk1, BtmuGattService *service, u8 *total_out); + +/** + * @brief GetGattCharacteristics + * @param[in] unk0 Unknown + * @param[in] unk1 Unknown + * @param[out] characteristics \ref BtmuGattCharacteristic + * @param[in] count Size of the characteristics array in entries. + * @param[out] total_out Total output entries. + */ +Result btmuGetGattCharacteristics(u32 unk0, u16 unk1, BtmuGattCharacteristic *characteristics, u8 count, u8 *total_out); + +/** + * @brief GetGattDescriptors + * @param[in] unk0 Unknown + * @param[in] unk1 Unknown + * @param[out] descriptors \ref BtmuGattDescriptor + * @param[in] count Size of the descriptors array in entries. + * @param[out] total_out Total output entries. + */ +Result btmuGetGattDescriptors(u32 unk0, u16 unk1, BtmuGattDescriptor *descriptors, u8 count, u8 *total_out); + +/** + * @brief AcquireBleMtuConfigEvent + * @note The Event must be closed by the user once finished with it. + * @param[out] out_event Output Event with autoclear=true. + */ +Result btmuAcquireBleMtuConfigEvent(Event* out_event); + +/** + * @brief ConfigureBleMtu + * @param[in] unk Unknown + * @param[in] mtu MTU + */ +Result btmuConfigureBleMtu(u32 unk, u16 mtu); + +/** + * @brief GetBleMtu + * @param[in] unk Unknown + * @param[out] out Output MTU. + */ +Result btmuGetBleMtu(u32 unk, u16 *out); + +/** + * @brief RegisterBleGattDataPath + * @param[in] path \ref BtmuBleDataPath + */ +Result btmuRegisterBleGattDataPath(const BtmuBleDataPath *path); + +/** + * @brief UnregisterBleGattDataPath + * @param[in] path \ref BtmuBleDataPath + */ +Result btmuUnregisterBleGattDataPath(const BtmuBleDataPath *path); + diff --git a/nx/source/services/btmu.c b/nx/source/services/btmu.c new file mode 100644 index 00000000..eff456fa --- /dev/null +++ b/nx/source/services/btmu.c @@ -0,0 +1,327 @@ +#define NX_SERVICE_ASSUME_NON_DOMAIN +#include +#include "service_guard.h" +#include "runtime/hosversion.h" +#include "services/btmu.h" +#include "services/applet.h" + +static Service g_btdrvIBtmUserCore; + +static Result _btmuGetSession(Service* srv, Service* srv_out, u32 cmd_id); + +NX_GENERATE_SERVICE_GUARD(btmu); + +Result _btmuInitialize(void) { + Result rc=0; + Service srv={0}; + + rc = btmuGetServiceSession(&srv); + if (R_SUCCEEDED(rc)) rc = _btmuGetSession(&srv, &g_btdrvIBtmUserCore, 0); // GetCore + serviceClose(&srv); + return rc; +} + +void _btmuCleanup(void) { + serviceClose(&g_btdrvIBtmUserCore); +} + +Result btmuGetServiceSession(Service* srv_out) { + if (hosversionBefore(5,0,0)) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + return smGetService(srv_out, "btm:u"); +} + +Service* btmuGetServiceSession_IBtmUserCore(void) { + return &g_btdrvIBtmUserCore; +} + +static Result _btmuGetSession(Service* srv, Service* srv_out, u32 cmd_id) { + return serviceDispatch(srv, cmd_id, + .out_num_objects = 1, + .out_objects = srv_out, + ); +} + +static Result _btmuCmdNoIO(u32 cmd_id) { + return serviceDispatch(&g_btdrvIBtmUserCore, cmd_id); +} + +static Result _btmuCmdGetEventOutFlag(Event* out_event, bool autoclear, u32 cmd_id) { + Handle tmp_handle = INVALID_HANDLE; + Result rc = 0; + u8 out=0; + + rc = serviceDispatchOut(&g_btdrvIBtmUserCore, cmd_id, out, + .out_handle_attrs = { SfOutHandleAttr_HipcCopy }, + .out_handles = &tmp_handle, + ); + if (R_SUCCEEDED(rc) && !out) rc = MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen); // sdknso would Abort here. + if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, autoclear); + return rc; +} + +static Result _btmuCmdInU32NoOut(u32 inval, u32 cmd_id) { + return serviceDispatchIn(&g_btdrvIBtmUserCore, cmd_id, inval); +} + +static Result _btmuCmdInBleAdvertisePacketParameterAruidNoOutput(BtdrvBleAdvertisePacketParameter param, u32 cmd_id) { + const struct { + BtdrvBleAdvertisePacketParameter param; + u64 AppletResourceUserId; + } in = { param, appletGetAppletResourceUserId() }; + + return serviceDispatchIn(&g_btdrvIBtmUserCore, cmd_id, in, + .in_send_pid = true, + ); +} + +static Result _btmuGetBleScanResults(BtdrvBleScanResult *results, u8 count, u8 *total_out, u32 cmd_id) { + u64 AppletResourceUserId = appletGetAppletResourceUserId(); + return serviceDispatchInOut(&g_btdrvIBtmUserCore, cmd_id, AppletResourceUserId, *total_out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { results, sizeof(BtdrvBleScanResult)*count } }, + .in_send_pid = true, + ); +} + +static Result _btmuBlePairDevice(BtdrvBleAdvertisePacketParameter param, u32 unk, u32 cmd_id) { + const struct { + BtdrvBleAdvertisePacketParameter param; + u32 unk; + } in = { param, unk }; + + return serviceDispatchIn(&g_btdrvIBtmUserCore, cmd_id, in); +} + +static Result _btmuGetGattServiceData(u32 unk0, u16 unk1, void* buffer, size_t entrysize, u8 count, u8 *out, u32 cmd_id) { + const struct { + u16 unk1; + u16 pad; + u32 unk0; + u64 AppletResourceUserId; + } in = { unk1, 0, unk0, appletGetAppletResourceUserId() }; + + return serviceDispatchInOut(&g_btdrvIBtmUserCore, cmd_id, in, *out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { buffer, entrysize*count } }, + .in_send_pid = true, + ); +} + +static Result _btmuRegisterBleGattDataPath(const BtmuBleDataPath *path, u32 cmd_id) { + const struct { + BtmuBleDataPath path; + u64 AppletResourceUserId; + } in = { *path, appletGetAppletResourceUserId() }; + + return serviceDispatchIn(&g_btdrvIBtmUserCore, cmd_id, in, + .in_send_pid = true, + ); +} + +Result btmuAcquireBleScanEvent(Event* out_event) { + return _btmuCmdGetEventOutFlag(out_event, true, 0); +} + +Result btmuGetBleScanFilterParameter(u16 unk, BtdrvBleAdvertisePacketParameter *out) { + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 1, unk, *out); +} + +Result btmuGetBleScanFilterParameter2(u16 unk, BtdrvGattAttributeUuid *out) { + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 2, unk, *out); +} + +Result btmuStartBleScanForGeneral(BtdrvBleAdvertisePacketParameter param) { + return _btmuCmdInBleAdvertisePacketParameterAruidNoOutput(param, 3); +} + +Result btmuStopBleScanForGeneral(void) { + return _btmuCmdNoIO(4); +} + +Result btmuGetBleScanResultsForGeneral(BtdrvBleScanResult *results, u8 count, u8 *total_out) { + return _btmuGetBleScanResults(results, count, total_out, 5); +} + +Result btmuStartBleScanForPaired(BtdrvBleAdvertisePacketParameter param) { + return _btmuCmdInBleAdvertisePacketParameterAruidNoOutput(param, 6); +} + +Result btmuStopBleScanForPaired(void) { + return _btmuCmdNoIO(7); +} + +Result btmuStartBleScanForSmartDevice(const BtdrvGattAttributeUuid *uuid) { + const struct { + BtdrvGattAttributeUuid uuid; + u32 pad; + u64 AppletResourceUserId; + } in = { *uuid, 0, appletGetAppletResourceUserId() }; + + return serviceDispatchIn(&g_btdrvIBtmUserCore, 8, in, + .in_send_pid = true, + ); +} + +Result btmuStopBleScanForSmartDevice(void) { + return _btmuCmdNoIO(9); +} + +Result btmuGetBleScanResultsForSmartDevice(BtdrvBleScanResult *results, u8 count, u8 *total_out) { + return _btmuGetBleScanResults(results, count, total_out, 10); +} + +Result btmuAcquireBleConnectionEvent(Event* out_event) { + return _btmuCmdGetEventOutFlag(out_event, true, 17); +} + +Result btmuBleConnect(BtdrvAddress addr) { + const struct { + BtdrvAddress addr; + u8 pad[2]; + u64 AppletResourceUserId; + } in = { addr, {0}, appletGetAppletResourceUserId() }; + + return serviceDispatchIn(&g_btdrvIBtmUserCore, 18, in, + .in_send_pid = true, + ); +} + +Result btmuBleDisconnect(u32 unk) { + return _btmuCmdInU32NoOut(unk, 19); +} + +Result btmuBleGetConnectionState(BtdrvBleConnectionInfo *info, u8 count, u8 *total_out) { + u64 AppletResourceUserId = appletGetAppletResourceUserId(); + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 20, AppletResourceUserId, *total_out, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out }, + .buffers = { { info, sizeof(BtdrvBleConnectionInfo)*count } }, + .in_send_pid = true, + ); +} + +Result btmuAcquireBlePairingEvent(Event* out_event) { + return _btmuCmdGetEventOutFlag(out_event, true, 21); +} + +Result btmuBlePairDevice(BtdrvBleAdvertisePacketParameter param, u32 unk) { + return _btmuBlePairDevice(param, unk, 22); +} + +Result btmuBleUnPairDevice(BtdrvBleAdvertisePacketParameter param, u32 unk) { + return _btmuBlePairDevice(param, unk, 23); +} + +Result btmuBleUnPairDevice2(BtdrvAddress addr, BtdrvBleAdvertisePacketParameter param) { + const struct { + BtdrvAddress addr; + BtdrvBleAdvertisePacketParameter param; + } in = { addr, param }; + + return serviceDispatchIn(&g_btdrvIBtmUserCore, 24, in); +} + +Result btmuBleGetPairedDevices(BtdrvBleAdvertisePacketParameter param, BtdrvAddress *addrs, u8 count, u8 *total_out) { + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 25, param, *total_out, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out }, + .buffers = { { addrs, sizeof(BtdrvAddress)*count } }, + ); +} + +Result btmuAcquireBleServiceDiscoveryEvent(Event* out_event) { + return _btmuCmdGetEventOutFlag(out_event, true, 26); +} + +Result btmuGetGattServices(u32 unk, BtmuGattService *services, u8 count, u8 *total_out) { + const struct { + u32 unk; + u32 pad; + u64 AppletResourceUserId; + } in = { unk, 0, appletGetAppletResourceUserId() }; + + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 27, in, *total_out, + .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, + .buffers = { { services, sizeof(BtmuGattService)*count } }, + .in_send_pid = true, + ); +} + +Result btmuGetGattService(u32 unk, const BtdrvGattAttributeUuid *uuid, BtmuGattService *service, u8 *total_out) { + const struct { + u32 unk; + BtdrvGattAttributeUuid uuid; + u64 AppletResourceUserId; + } in = { unk, *uuid, appletGetAppletResourceUserId() }; + + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 28, in, *total_out, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out | SfBufferAttr_FixedSize }, + .buffers = { { service, sizeof(*service) } }, + .in_send_pid = true, + ); +} + +Result btmuGetGattIncludedServices(u32 unk0, u16 unk1, BtmuGattService *services, u8 count, u8 *out) { + return _btmuGetGattServiceData(unk0, unk1, services, sizeof(BtmuGattService), count, out, 29); +} + +Result btmuGetBelongingGattService(u32 unk0, u16 unk1, BtmuGattService *service, u8 *total_out) { + const struct { + u16 unk1; + u16 pad; + u32 unk0; + u64 AppletResourceUserId; + } in = { unk1, 0, unk0, appletGetAppletResourceUserId() }; + + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 30, in, *total_out, + .buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out | SfBufferAttr_FixedSize }, + .buffers = { { service, sizeof(*service) } }, + .in_send_pid = true, + ); +} + +Result btmuGetGattCharacteristics(u32 unk0, u16 unk1, BtmuGattCharacteristic *characteristics, u8 count, u8 *total_out) { + return _btmuGetGattServiceData(unk0, unk1, characteristics, sizeof(BtmuGattCharacteristic), count, total_out, 31); +} + +Result btmuGetGattDescriptors(u32 unk0, u16 unk1, BtmuGattDescriptor *descriptors, u8 count, u8 *total_out) { + return _btmuGetGattServiceData(unk0, unk1, descriptors, sizeof(BtmuGattDescriptor), count, total_out, 32); +} + +Result btmuAcquireBleMtuConfigEvent(Event* out_event) { + return _btmuCmdGetEventOutFlag(out_event, true, 33); +} + +Result btmuConfigureBleMtu(u32 unk, u16 mtu) { + const struct { + u16 mtu; + u16 pad; + u32 unk; + u64 AppletResourceUserId; + } in = { mtu, 0, unk, appletGetAppletResourceUserId() }; + + return serviceDispatchIn(&g_btdrvIBtmUserCore, 34, in, + .in_send_pid = true, + ); +} + +Result btmuGetBleMtu(u32 unk, u16 *out) { + const struct { + u32 unk; + u32 pad; + u64 AppletResourceUserId; + } in = { unk, 0, appletGetAppletResourceUserId() }; + + return serviceDispatchInOut(&g_btdrvIBtmUserCore, 35, in, *out, + .in_send_pid = true, + ); +} + +Result btmuRegisterBleGattDataPath(const BtmuBleDataPath *path) { + return _btmuRegisterBleGattDataPath(path, 36); +} + +Result btmuUnregisterBleGattDataPath(const BtmuBleDataPath *path) { + return _btmuRegisterBleGattDataPath(path, 37); +} +