Added initial bluetooth support.

This commit is contained in:
yellows8 2020-07-15 16:15:42 -04:00
parent bcc0f86aa3
commit a5e8572019
5 changed files with 645 additions and 0 deletions

View File

@ -93,6 +93,8 @@ extern "C" {
#include "switch/services/set.h"
#include "switch/services/ssl.h"
#include "switch/services/lr.h"
#include "switch/services/bt.h"
#include "switch/services/btdrv.h"
#include "switch/services/spl.h"
#include "switch/services/ncm.h"
#include "switch/services/psc.h"

View File

@ -0,0 +1,131 @@
/**
* @file bt.h
* @brief Bluetooth user (bt) service IPC wrapper.
* @author yellows8
* @copyright libnx Authors
*/
#pragma once
#include "../types.h"
#include "../kernel/event.h"
#include "../services/btdrv.h"
#include "../sf/service.h"
/// Initialize bt. Only available on [5.0.0+].
Result btInitialize(void);
/// Exit bt.
void btExit(void);
/// Gets the Service object for the actual bt service session.
Service* btGetServiceSession(void);
/**
* @brief LeClientReadCharacteristic
* @note This is essentially the same as \ref btdrvReadGattCharacteristic.
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
*/
Result btLeClientReadCharacteristic(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1);
/**
* @brief LeClientReadDescriptor
* @note This is essentially the same as \ref btdrvReadGattDescriptor.
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
* @param[in] id2 \ref BtdrvGattId
*/
Result btLeClientReadDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2);
/**
* @brief LeClientWriteCharacteristic
* @note This is essentially the same as \ref btdrvWriteGattCharacteristic.
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] flag2 Flag
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
* @param[in] buffer Input buffer.
* @param[in] size Input buffer size.
*/
Result btLeClientWriteCharacteristic(bool flag, u8 unk, bool flag2, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const void* buffer, size_t size);
/**
* @brief LeClientWriteDescriptor
* @note This is essentially the same as \ref btdrvWriteGattDescriptor.
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
* @param[in] id2 \ref BtdrvGattId
* @param[in] buffer Input buffer.
* @param[in] size Input buffer size.
*/
Result btLeClientWriteDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2, const void* buffer, size_t size);
/**
* @brief LeClientRegisterNotification
* @note This is essentially the same as \ref btdrvRegisterGattNotification.
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
*/
Result btLeClientRegisterNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1);
/**
* @brief LeClientDeregisterNotification
* @note This is essentially the same as \ref btdrvUnregisterGattNotification.
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
*/
Result btLeClientDeregisterNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1);
/**
* @brief SetLeResponse
* @param[in] unk Unknown
* @param[in] uuid0 \ref BtdrvGattAttributeUuid
* @param[in] uuid1 \ref BtdrvGattAttributeUuid
* @param[in] buffer Input buffer.
* @param[in] size Input buffer size.
*/
Result btSetLeResponse(u8 unk, const BtdrvGattAttributeUuid *uuid0, const BtdrvGattAttributeUuid *uuid1, const void* buffer, size_t size);
/**
* @brief LeSendIndication
* @param[in] unk Unknown
* @param[in] flag Flag
* @param[in] uuid0 \ref BtdrvGattAttributeUuid
* @param[in] uuid1 \ref BtdrvGattAttributeUuid
* @param[in] buffer Input buffer.
* @param[in] size Input buffer size.
*/
Result btLeSendIndication(u8 unk, bool flag, const BtdrvGattAttributeUuid *uuid0, const BtdrvGattAttributeUuid *uuid1, const void* buffer, size_t size);
/**
* @brief GetLeEventInfo
* @note This is identical to \ref btdrvGetLeEventInfo except different state is used.
* @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[in] size Output buffer size.
* @oaram[out] type Output BleEventType.
*/
Result btGetLeEventInfo(void* buffer, size_t size, u32 *type);
/**
* @brief RegisterBleEvent
* @note This is identical to \ref btdrvRegisterBleHidEvent except different state is used.
* @note The Event must be closed by the user once finished with it.
* @param[out] out_event Output Event with autoclear=true.
*/
Result btRegisterBleEvent(Event* out_event);

View File

@ -0,0 +1,181 @@
/**
* @file btdrv.h
* @brief Bluetooth driver (btdrv) service IPC wrapper.
* @author yellows8
* @copyright libnx Authors
*/
#pragma once
#include "../types.h"
#include "../kernel/event.h"
#include "../sf/service.h"
/// Address
typedef struct {
u8 address[0x6]; ///< Address
} BtdrvAddress;
/// AdapterProperty
typedef struct {
u8 unk_x0[0x103]; ///< Unknown
} BtdrvAdapterProperty;
/// BluetoothPinCode
typedef struct {
char code[0x10]; ///< PinCode
} BtdrvBluetoothPinCode;
/// HidData, for pre-9.0.0.
typedef struct {
u16 size; ///< Size of data.
u8 data[0x280]; ///< Data
} BtdrvHidData;
/// HidReport, for [9.0.0+].
typedef struct {
u16 size; ///< Size of data.
u8 data[0x2BC]; ///< Data
} BtdrvHidReport;
/// PlrStatistics
typedef struct {
u8 unk_x0[0x84]; ///< Unknown
} BtdrvPlrStatistics;
/// PlrList
typedef struct {
u8 unk_x0[0xA4]; ///< Unknown
} BtdrvPlrList;
/// ChannelMapList
typedef struct {
u8 unk_x0[0x88]; ///< Unknown
} BtdrvChannelMapList;
/// LeConnectionParams
typedef struct {
u8 unk_x0[0x14]; ///< Unknown
} BtdrvLeConnectionParams;
/// BleConnectionParameter
typedef struct {
u8 unk_x0[0xC]; ///< Unknown
} BtdrvBleConnectionParameter;
/// BleAdvertisePacketData
typedef struct {
u8 unk_x0[0xCC]; ///< Unknown
} BtdrvBleAdvertisePacketData;
/// BleAdvertiseFilter
typedef struct {
u8 unk_x0[0x3E]; ///< Unknown
} BtdrvBleAdvertiseFilter;
/// GattAttributeUuid
typedef struct {
u8 unk_x0[0x14]; ///< Unknown
} BtdrvGattAttributeUuid;
/// GattId
typedef struct {
u8 unk_x0[0x18]; ///< Unknown
} BtdrvGattId;
/// Initialize btdrv.
Result btdrvInitialize(void);
/// Exit btdrv.
void btdrvExit(void);
/// Gets the Service object for the actual btdrv service session.
Service* btdrvGetServiceSession(void);
/**
* @brief ReadGattCharacteristic
* @note Only available on [5.0.0+].
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
*/
Result btdrvReadGattCharacteristic(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1);
/**
* @brief ReadGattDescriptor
* @note Only available on [5.0.0+].
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
* @param[in] id2 \ref BtdrvGattId
*/
Result btdrvReadGattDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2);
/**
* @brief WriteGattCharacteristic
* @note Only available on [5.0.0+].
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] flag2 Flag
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
* @param[in] buffer Input buffer.
* @param[in] size Input buffer size.
*/
Result btdrvWriteGattCharacteristic(bool flag, u8 unk, bool flag2, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const void* buffer, size_t size);
/**
* @brief WriteGattDescriptor
* @note Only available on [5.0.0+].
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] unk2 Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
* @param[in] id2 \ref BtdrvGattId
* @param[in] buffer Input buffer.
* @param[in] size Input buffer size.
*/
Result btdrvWriteGattDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2, const void* buffer, size_t size);
/**
* @brief RegisterGattNotification
* @note Only available on [5.0.0+].
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
*/
Result btdrvRegisterGattNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1);
/**
* @brief UnregisterGattNotification
* @note Only available on [5.0.0+].
* @param[in] flag Flag
* @param[in] unk Unknown
* @param[in] id0 \ref BtdrvGattId
* @param[in] id1 \ref BtdrvGattId
*/
Result btdrvUnregisterGattNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1);
/**
* @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[in] size Output buffer size.
* @oaram[out] type Output BleEventType.
*/
Result btdrvGetLeEventInfo(void* buffer, size_t size, u32 *type);
/**
* @brief RegisterBleHidEvent
* @note Only available on [5.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 btdrvRegisterBleHidEvent(Event* out_event);

178
nx/source/services/bt.c Normal file
View File

@ -0,0 +1,178 @@
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include <string.h>
#include "service_guard.h"
#include "runtime/hosversion.h"
#include "services/bt.h"
#include "services/applet.h"
static Service g_btSrv;
NX_GENERATE_SERVICE_GUARD(bt);
Result _btInitialize(void) {
if (hosversionBefore(5,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return smGetService(&g_btSrv, "bt");
}
void _btCleanup(void) {
serviceClose(&g_btSrv);
}
Service* btGetServiceSession(void) {
return &g_btSrv;
}
Result btLeClientReadCharacteristic(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1) {
const struct {
u8 flag;
u8 unk;
u8 pad[2];
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
u64 AppletResourceUserId;
} in = { flag!=0, unk, {0}, unk2, *id0, *id1, appletGetAppletResourceUserId() };
return serviceDispatchIn(&g_btSrv, 0, in,
.in_send_pid = true,
);
}
Result btLeClientReadDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2) {
const struct {
u8 flag;
u8 unk;
u8 pad[2];
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
BtdrvGattId id2;
u64 AppletResourceUserId;
} in = { flag!=0, unk, {0}, unk2, *id0, *id1, *id2, appletGetAppletResourceUserId() };
return serviceDispatchIn(&g_btSrv, 1, in,
.in_send_pid = true,
);
}
Result btLeClientWriteCharacteristic(bool flag, u8 unk, bool flag2, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const void* buffer, size_t size) {
const struct {
u8 flag;
u8 unk;
u8 flag2;
u8 pad;
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
u64 AppletResourceUserId;
} in = { flag!=0, unk, flag2!=0, 0, unk2, *id0, *id1, appletGetAppletResourceUserId() };
return serviceDispatchIn(&g_btSrv, 2, in,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { buffer, size } },
.in_send_pid = true,
);
}
Result btLeClientWriteDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2, const void* buffer, size_t size) {
const struct {
u8 flag;
u8 unk;
u8 pad[2];
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
BtdrvGattId id2;
u64 AppletResourceUserId;
} in = { flag!=0, unk, {0}, unk2, *id0, *id1, *id2, appletGetAppletResourceUserId() };
return serviceDispatchIn(&g_btSrv, 3, in,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { buffer, size } },
.in_send_pid = true,
);
}
static Result _btLeClientNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1, u32 cmd_id) {
const struct {
u8 flag;
u8 pad[3];
u32 unk;
BtdrvGattId id0;
BtdrvGattId id1;
u64 AppletResourceUserId;
} in = { flag!=0, {0}, unk, *id0, *id1, appletGetAppletResourceUserId() };
return serviceDispatchIn(&g_btSrv, cmd_id, in,
.in_send_pid = true,
);
}
Result btLeClientRegisterNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1) {
return _btLeClientNotification(flag, unk, id0, id1, 4);
}
Result btLeClientDeregisterNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1) {
return _btLeClientNotification(flag, unk, id0, id1, 5);
}
Result btSetLeResponse(u8 unk, const BtdrvGattAttributeUuid *uuid0, const BtdrvGattAttributeUuid *uuid1, const void* buffer, size_t size) {
const struct {
u8 unk;
u8 pad[3];
BtdrvGattAttributeUuid uuid0;
BtdrvGattAttributeUuid uuid1;
u8 pad2[4];
u64 AppletResourceUserId;
} in = { unk, {0}, *uuid0, *uuid1, {0}, appletGetAppletResourceUserId() };
return serviceDispatchIn(&g_btSrv, 6, in,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { buffer, size } },
.in_send_pid = true,
);
}
Result btLeSendIndication(u8 unk, bool flag, const BtdrvGattAttributeUuid *uuid0, const BtdrvGattAttributeUuid *uuid1, const void* buffer, size_t size) {
const struct {
u8 unk;
u8 flag;
u8 pad[2];
BtdrvGattAttributeUuid uuid0;
BtdrvGattAttributeUuid uuid1;
u8 pad2[4];
u64 AppletResourceUserId;
} in = { unk, flag!=0, {0}, *uuid0, *uuid1, {0}, appletGetAppletResourceUserId() };
return serviceDispatchIn(&g_btSrv, 7, in,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { buffer, size } },
.in_send_pid = true,
);
}
Result btGetLeEventInfo(void* buffer, size_t size, u32 *type) {
u64 AppletResourceUserId = appletGetAppletResourceUserId();
return serviceDispatchInOut(&g_btSrv, 8, AppletResourceUserId, *type,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_Out },
.buffers = { { buffer, size } },
.in_send_pid = true,
);
}
Result btRegisterBleEvent(Event* out_event) {
Handle tmp_handle = INVALID_HANDLE;
Result rc = 0;
u64 AppletResourceUserId = appletGetAppletResourceUserId();
rc = serviceDispatchIn(&g_btSrv, 9, AppletResourceUserId,
.in_send_pid = true,
.out_handle_attrs = { SfOutHandleAttr_HipcCopy },
.out_handles = &tmp_handle,
);
if (R_SUCCEEDED(rc)) eventLoadRemote(out_event, tmp_handle, true);
return rc;
}

153
nx/source/services/btdrv.c Normal file
View File

@ -0,0 +1,153 @@
#define NX_SERVICE_ASSUME_NON_DOMAIN
#include <string.h>
#include "service_guard.h"
#include "runtime/hosversion.h"
#include "services/btdrv.h"
static Service g_btdrvSrv;
NX_GENERATE_SERVICE_GUARD(btdrv);
Result _btdrvInitialize(void) {
return smGetService(&g_btdrvSrv, "btdrv");
}
void _btdrvCleanup(void) {
serviceClose(&g_btdrvSrv);
}
Service* btdrvGetServiceSession(void) {
return &g_btdrvSrv;
}
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);
u32 cmd_id = hosversionBefore(6,0,0) ? 89 : 90;
const struct {
u8 flag;
u8 unk;
u8 pad[2];
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
} in = { flag!=0, unk, {0}, unk2, *id0, *id1};
return serviceDispatchIn(&g_btdrvSrv, cmd_id, in);
}
Result btdrvReadGattDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2) {
if (hosversionBefore(5,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
u32 cmd_id = hosversionBefore(6,0,0) ? 90 : 91;
const struct {
u8 flag;
u8 unk;
u8 pad[2];
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
BtdrvGattId id2;
} in = { flag!=0, unk, {0}, unk2, *id0, *id1, *id2 };
return serviceDispatchIn(&g_btdrvSrv, cmd_id, in);
}
Result btdrvWriteGattCharacteristic(bool flag, u8 unk, bool flag2, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const void* buffer, size_t size) {
if (hosversionBefore(5,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
u32 cmd_id = hosversionBefore(6,0,0) ? 91 : 92;
const struct {
u8 flag;
u8 unk;
u8 flag2;
u8 pad;
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
} in = { flag!=0, unk, flag2!=0, 0, unk2, *id0, *id1 };
return serviceDispatchIn(&g_btdrvSrv, cmd_id, in,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { buffer, size } },
);
}
Result btdrvWriteGattDescriptor(bool flag, u8 unk, u32 unk2, const BtdrvGattId *id0, const BtdrvGattId *id1, const BtdrvGattId *id2, const void* buffer, size_t size) {
if (hosversionBefore(5,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
u32 cmd_id = hosversionBefore(6,0,0) ? 92 : 93;
const struct {
u8 flag;
u8 unk;
u8 pad[2];
u32 unk2;
BtdrvGattId id0;
BtdrvGattId id1;
BtdrvGattId id2;
} in = { flag!=0, unk, {0}, unk2, *id0, *id1, *id2 };
return serviceDispatchIn(&g_btdrvSrv, cmd_id, in,
.buffer_attrs = { SfBufferAttr_HipcPointer | SfBufferAttr_In },
.buffers = { { buffer, size } },
);
}
static Result _btdrvGattNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1, u32 cmd_id) {
const struct {
u8 flag;
u8 pad[3];
u32 unk;
BtdrvGattId id0;
BtdrvGattId id1;
} in = { flag!=0, {0}, unk, *id0, *id1 };
return serviceDispatchIn(&g_btdrvSrv, cmd_id, in);
}
Result btdrvRegisterGattNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1) {
if (hosversionBefore(5,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
return _btdrvGattNotification(flag, unk, id0, id1, 94);
}
Result btdrvUnregisterGattNotification(bool flag, u32 unk, const BtdrvGattId *id0, const BtdrvGattId *id1) {
if (hosversionBefore(5,0,0))
return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);
u32 cmd_id = hosversionBefore(6,0,0) ? 93 : 95;
return _btdrvGattNotification(flag, unk, id0, id1, cmd_id);
}
Result btdrvGetLeEventInfo(void* buffer, size_t size, u32 *type) {
if (hosversionBefore(5,0,0))
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 } },
);
}
Result btdrvRegisterBleHidEvent(Event* out_event) {
if (hosversionBefore(5,0,0))
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;
}