From 6ab0dc38e6769935b55692ddaaa5c307b33a417d Mon Sep 17 00:00:00 2001 From: yellows8 Date: Sun, 16 Aug 2020 18:47:40 -0400 Subject: [PATCH] Added btdev. --- nx/include/switch.h | 1 + nx/include/switch/runtime/btdev.h | 267 +++++++++++++ nx/source/runtime/btdev.c | 613 ++++++++++++++++++++++++++++++ 3 files changed, 881 insertions(+) create mode 100644 nx/include/switch/runtime/btdev.h create mode 100644 nx/source/runtime/btdev.c diff --git a/nx/include/switch.h b/nx/include/switch.h index 85167fae..9ff154ea 100644 --- a/nx/include/switch.h +++ b/nx/include/switch.h @@ -154,6 +154,7 @@ extern "C" { #include "switch/runtime/nxlink.h" #include "switch/runtime/resolver.h" #include "switch/runtime/ringcon.h" +#include "switch/runtime/btdev.h" #include "switch/runtime/util/utf.h" diff --git a/nx/include/switch/runtime/btdev.h b/nx/include/switch/runtime/btdev.h new file mode 100644 index 00000000..aea67558 --- /dev/null +++ b/nx/include/switch/runtime/btdev.h @@ -0,0 +1,267 @@ +/** + * @file btdev.h + * @brief Wrapper around the bt/btmu services for using bluetooth BLE. + * @author yellows8 + * @copyright libnx Authors + */ +#pragma once +#include "../types.h" +#include "../kernel/event.h" +#include "../services/btdrv_types.h" + +/// GattAttribute +typedef struct { + u8 type; ///< Type + BtdrvGattAttributeUuid uuid; ///< \ref BtdrvGattAttributeUuid + u16 handle; ///< Handle + u32 connection_handle; ///< ConnectionHandle +} BtdevGattAttribute; + +/// GattService +typedef struct { + BtdevGattAttribute attr; ///< \ref BtdevGattAttribute + u16 instance_id; ///< InstanceId + u16 end_group_handle; ///< EndGroupHandle + bool primary_service; ///< PrimaryService +} BtdevGattService; + +/// GattCharacteristic +typedef struct { + BtdevGattAttribute attr; ///< \ref BtdevGattAttribute + u16 instance_id; ///< InstanceId + u8 properties; ///< Properties + u64 value_size; ///< Size of value. + u8 value[0x200]; ///< Value +} BtdevGattCharacteristic; + +/// GattDescriptor +typedef struct { + BtdevGattAttribute attr; ///< \ref BtdevGattAttribute + u64 value_size; ///< Size of value. + u8 value[0x200]; ///< Value +} BtdevGattDescriptor; + +/// Initialize bt/btmu. +Result btdevInitialize(void); + +/// Exit bt/btmu. +void btdevExit(void); + +/// Compares two \ref BtdrvGattAttributeUuid, returning whether these match. +bool btdevGattAttributeUuidIsSame(const BtdrvGattAttributeUuid *a, const BtdrvGattAttributeUuid *b); + +/// Wrapper for \ref btmuAcquireBleScanEvent. +Result btdevAcquireBleScanEvent(Event* out_event); + +/// Wrapper for \ref btmuGetBleScanFilterParameter. +Result btdevGetBleScanParameter(u16 unk, BtdrvBleAdvertisePacketParameter *out); + +/// Wrapper for \ref btmuGetBleScanFilterParameter2. +Result btdevGetBleScanParameter2(u16 unk, BtdrvGattAttributeUuid *out); + +/// Wrapper for \ref btdevStartBleScanGeneral. +Result btdevStartBleScanGeneral(BtdrvBleAdvertisePacketParameter param); + +/// Wrapper for \ref btmuStopBleScanForGeneral. +Result btdevStopBleScanGeneral(void); + +/// Wrapper for \ref btmuGetBleScanResultsForGeneral and \ref btmuGetBleScanResultsForSmartDevice. +Result btdevGetBleScanResult(BtdrvBleScanResult *results, u8 count, u8 *total_out); + +/// Wrapper for \ref btmuStartBleScanForPaired. +Result btdevEnableBleAutoConnection(BtdrvBleAdvertisePacketParameter param); + +/// Wrapper for \ref btmuStopBleScanForPaired. +Result btdevDisableBleAutoConnection(void); + +/// Wrapper for \ref btmuStartBleScanForSmartDevice. +Result btdevStartBleScanSmartDevice(const BtdrvGattAttributeUuid *uuid); + +/// Wrapper for \ref btmuStopBleScanForSmartDevice. +Result btdevStopBleScanSmartDevice(void); + +/// Wrapper for \ref btmuAcquireBleConnectionEvent. +Result btdevAcquireBleConnectionStateChangedEvent(Event* out_event); + +/// Wrapper for \ref btmuBleConnect. +Result btdevConnectToGattServer(BtdrvAddress addr); + +/// Wrapper for \ref btmuBleDisconnect. +Result btdevDisconnectFromGattServer(u32 id); + +/// Wrapper for \ref btmuBleGetConnectionState. +Result btdevGetBleConnectionInfoList(BtdrvBleConnectionInfo *info, u8 count, u8 *total_out); + +/// Wrapper for \ref btmuAcquireBleServiceDiscoveryEvent. +Result btdevAcquireBleServiceDiscoveryEvent(Event* out_event); + +/// Wrapper for \ref btmuGetGattServices. +Result btdevGetGattServices(u32 connection_handle, BtdevGattService *services, u8 count, u8 *total_out); + +/// Wrapper for \ref btmuGetGattService. +Result btdevGetGattService(u32 connection_handle, const BtdrvGattAttributeUuid *uuid, BtdevGattService *service, bool *flag); + +/// Wrapper for \ref btmuAcquireBlePairingEvent. +Result btdevAcquireBlePairingEvent(Event* out_event); + +/// Wrapper for \ref btmuBlePairDevice. +Result btdevPairGattServer(BtdrvBleAdvertisePacketParameter param, u32 id); + +/// Wrapper for \ref btmuBleUnPairDevice. +Result btdevUnpairGattServer(BtdrvBleAdvertisePacketParameter param, u32 id); + +/// Wrapper for \ref btmuBleUnPairDevice2. +Result btdevUnpairGattServer2(BtdrvAddress addr, BtdrvBleAdvertisePacketParameter param); + +/// Wrapper for \ref btmuBleGetPairedDevices. +Result btdevGetPairedGattServerAddress(BtdrvBleAdvertisePacketParameter param, BtdrvAddress *addrs, u8 count, u8 *total_out); + +/// Wrapper for \ref btmuAcquireBleMtuConfigEvent. +Result btdevAcquireBleMtuConfigEvent(Event* out_event); + +/// Wrapper for \ref btmuConfigureBleMtu. mtu must be 0x18-0x200. +Result btdevConfigureBleMtu(u32 id, u16 mtu); + +/// Wrapper for \ref btmuGetBleMtu. +Result btdevGetBleMtu(u32 id, u16 *out); + +/// Wrapper for \ref btRegisterBleEvent. +Result btdevAcquireBleGattOperationEvent(Event* out_event); + +/// Wrapper for \ref btdevRegisterGattOperationNotification. +Result btdevRegisterGattOperationNotification(const BtdrvGattAttributeUuid *uuid); + +/// Wrapper for \ref btdevUnregisterGattOperationNotification. +Result btdevUnregisterGattOperationNotification(const BtdrvGattAttributeUuid *uuid); + +/// Wrapper for \ref btGetLeEventInfo. +Result btdevGetGattOperationResult(BtdrvBleClientGattOperationInfo *out); + +/// Wrapper for \ref btLeClientReadCharacteristic. +Result btdevReadGattCharacteristic(BtdevGattCharacteristic *c); + +/// Wrapper for \ref btLeClientWriteCharacteristic. +Result btdevWriteGattCharacteristic(BtdevGattCharacteristic *c); + +/// Wrapper for \ref btLeClientRegisterNotification / \ref btLeClientDeregisterNotification, flag controls which func to call. +Result btdevEnableGattCharacteristicNotification(BtdevGattCharacteristic *c, bool flag); + +/// Wrapper for \ref btLeClientReadDescriptor. +Result btdevReadGattDescriptor(BtdevGattDescriptor *d); + +/// Wrapper for \ref btLeClientWriteDescriptor. +Result btdevWriteGattDescriptor(BtdevGattDescriptor *d); + +///@name GattAttribute +///@{ + +/// Creates a \ref BtdevGattAttribute object. +void btdevGattAttributeCreate(BtdevGattAttribute *a, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle); + +/// Gets the Type. +NX_CONSTEXPR u8 btdevGattAttributeGetType(BtdevGattAttribute *a) { + return a->type; +} + +/// Gets the Uuid. +NX_CONSTEXPR void btdevGattAttributeGetUuid(BtdevGattAttribute *a, BtdrvGattAttributeUuid *out) { + *out = a->uuid; +} + +/// Gets the Handle. +NX_CONSTEXPR u16 btdevGattAttributeGetHandle(BtdevGattAttribute *a) { + return a->handle; +} + +/// Gets the ConnectionHandle. +NX_CONSTEXPR u32 btdevGattAttributeGetConnectionHandle(BtdevGattAttribute *a) { + return a->connection_handle; +} + +///@} + +///@name GattService +///@{ + +/// Creates a \ref BtdevGattService object. +void btdevGattServiceCreate(BtdevGattService *s, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle, u16 instance_id, u16 end_group_handle, bool primary_service); + +/// Gets the InstanceId. +NX_CONSTEXPR u16 btdevGattServiceGetInstanceId(BtdevGattService *s) { + return s->instance_id; +} + +/// Gets the EndGroupHandle. +NX_CONSTEXPR u16 btdevGattServiceGetEndGroupHandle(BtdevGattService *s) { + return s->end_group_handle; +} + +/// Gets whether this is the PrimaryService. +NX_CONSTEXPR u16 btdevGattServiceIsPrimaryService(BtdevGattService *s) { + return s->primary_service; +} + +/// Wrapper for \ref btmuGetGattIncludedServices. +Result btdevGattServiceGetIncludedServices(BtdevGattService *s, BtdevGattService *services, u8 count, u8 *total_out); + +/// Wrapper for \ref btmuGetGattCharacteristics. +Result btdevGattServiceGetCharacteristics(BtdevGattService *s, BtdevGattCharacteristic *characteristic, u8 count, u8 *total_out); + +/// Wrapper for \ref btmuGetGattCharacteristics. +Result btdevGattServiceGetCharacteristic(BtdevGattService *s, const BtdrvGattAttributeUuid *uuid, BtdevGattCharacteristic *characteristic, bool *flag); + +///@} + +///@name GattCharacteristic +///@{ + +/// Creates a \ref BtdevGattCharacteristic object. +void btdevGattCharacteristicCreate(BtdevGattCharacteristic *c, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle, u16 instance_id, u8 properties); + +/// Gets the InstanceId. +NX_CONSTEXPR u16 btdevGattCharacteristicGetInstanceId(BtdevGattCharacteristic *c) { + return c->instance_id; +} + +/// Gets the Properties. +NX_CONSTEXPR u8 btdevGattCharacteristicGetProperties(BtdevGattCharacteristic *c) { + return c->properties; +} + +/// Wrapper for \ref btmuGetBelongingGattService. +Result btdevGattCharacteristicGetService(BtdevGattCharacteristic *c, BtdevGattService *service); + +/// Wrapper for \ref btmuGetGattDescriptors. +Result btdevGattCharacteristicGetDescriptors(BtdevGattCharacteristic *c, BtdevGattDescriptor *descriptors, u8 count, u8 *total_out); + +/// Same as \ref btdevGattCharacteristicGetDescriptors except this only returns a \ref BtdevGattDescriptor which contains a matching \ref BtdrvGattAttributeUuid. +Result btdevGattCharacteristicGetDescriptor(BtdevGattCharacteristic *c, const BtdrvGattAttributeUuid *uuid, BtdevGattDescriptor *descriptor, bool *flag); + +/// Sets the Value in the object, max size is 0x200. +void btdevGattCharacteristicSetValue(BtdevGattCharacteristic *c, const void* buffer, size_t size); + +/// Gets the Value in the object, returns the copied value size. +u64 btdevGattCharacteristicGetValue(BtdevGattCharacteristic *c, void* buffer, size_t size); + +///@} + +///@name GattDescriptor +///@{ + +/// Creates a \ref BtdevGattDescriptor object. +void btdevGattDescriptorCreate(BtdevGattDescriptor *d, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle); + +/// Wrapper for \ref btmuGetBelongingGattService. +Result btdevGattDescriptorGetService(BtdevGattDescriptor *d, BtdevGattService *service); + +/// Wrapper for \ref btmuGetGattCharacteristics. +Result btdevGattDescriptorGetCharacteristic(BtdevGattDescriptor *d, BtdevGattCharacteristic *characteristic); + +/// Sets the Value in the object, max size is 0x200. +void btdevGattDescriptorSetValue(BtdevGattDescriptor *d, const void* buffer, size_t size); + +/// Gets the Value in the object, returns the copied value size. +u64 btdevGattDescriptorGetValue(BtdevGattDescriptor *d, void* buffer, size_t size); + +///@} + diff --git a/nx/source/runtime/btdev.c b/nx/source/runtime/btdev.c new file mode 100644 index 00000000..3bd693a6 --- /dev/null +++ b/nx/source/runtime/btdev.c @@ -0,0 +1,613 @@ +#include +#include "runtime/btdev.h" +#include "services/bt.h" +#include "services/btmu.h" + +Result btdevInitialize(void) { + Result rc=0; + + rc = btInitialize(); + if (R_SUCCEEDED(rc)) { + rc = btmuInitialize(); + if (R_FAILED(rc)) btExit(); + } + return rc; +} + +void btdevExit(void) { + btmuExit(); + btExit(); +} + +// sdknso does Result error-conversion, but we won't do so. + +bool btdevGattAttributeUuidIsSame(const BtdrvGattAttributeUuid *a, const BtdrvGattAttributeUuid *b) { + if (a->size != b->size) return false; + if (a->size != 0x2 && a->size != 0x4 && a->size != 0x10) return false; + return memcmp(a->uuid, b->uuid, a->size)==0; +} + +Result btdevAcquireBleScanEvent(Event* out_event) { + return btmuAcquireBleScanEvent(out_event); +} + +Result btdevGetBleScanParameter(u16 unk, BtdrvBleAdvertisePacketParameter *out) { + return btmuGetBleScanFilterParameter(unk, out); +} + +Result btdevGetBleScanParameter2(u16 unk, BtdrvGattAttributeUuid *out) { + return btmuGetBleScanFilterParameter2(unk, out); +} + +Result btdevStartBleScanGeneral(BtdrvBleAdvertisePacketParameter param) { + return btmuStartBleScanForGeneral(param); +} + +Result btdevStopBleScanGeneral(void) { + return btmuStopBleScanForGeneral(); +} + +Result btdevGetBleScanResult(BtdrvBleScanResult *results, u8 count, u8 *total_out) { + Result rc=0; + BtdrvBleScanResult tmpresults[2][10]={0}; + u8 tmp_total_out[2]={0}; + u8 tmpi[2]={0}; + u8 targeti=0; + + memset(results, 0, sizeof(BtdrvBleScanResult)*count); + + rc = btmuGetBleScanResultsForGeneral(tmpresults[0], 10, &tmp_total_out[0]); + if (R_FAILED(rc)) return rc; + + rc = btmuGetBleScanResultsForSmartDevice(tmpresults[1], 10, &tmp_total_out[1]); + if (R_FAILED(rc)) return rc; + + for (u8 i=0; i= tmpresults[1][tmpi[1]].unk_x144) targeti = 0; + else targeti = 1; + } + else { + if (tmpi[1] >= tmp_total_out[1] && tmpi[0] < tmp_total_out[0]) targeti = 0; + else if (tmpi[0] >= tmp_total_out[0] && tmpi[1] < tmp_total_out[1]) targeti = 1; + else break; + } + + memcpy(results, &tmpresults[targeti][tmpi[targeti]], sizeof(BtdrvBleScanResult)); + results++; + tmpi[targeti]++; + } + + if (total_out) *total_out = tmpi[0] + tmpi[1]; + + return rc; +} + +Result btdevEnableBleAutoConnection(BtdrvBleAdvertisePacketParameter param) { + return btmuStartBleScanForPaired(param); +} + +Result btdevDisableBleAutoConnection(void) { + return btmuStopBleScanForPaired(); +} + +Result btdevStartBleScanSmartDevice(const BtdrvGattAttributeUuid *uuid) { + return btmuStartBleScanForSmartDevice(uuid); +} + +Result btdevStopBleScanSmartDevice(void) { + return btmuStopBleScanForSmartDevice(); +} + +Result btdevAcquireBleConnectionStateChangedEvent(Event* out_event) { + return btmuAcquireBleConnectionEvent(out_event); +} + +Result btdevConnectToGattServer(BtdrvAddress addr) { + return btmuBleConnect(addr); +} + +Result btdevDisconnectFromGattServer(u32 id) { + return btmuBleDisconnect(id); +} + +Result btdevGetBleConnectionInfoList(BtdrvBleConnectionInfo *info, u8 count, u8 *total_out) { + return btmuBleGetConnectionState(info, count, total_out); +} + +Result btdevAcquireBleServiceDiscoveryEvent(Event* out_event) { + return btmuAcquireBleServiceDiscoveryEvent(out_event); +} + +Result btdevGetGattServices(u32 connection_handle, BtdevGattService *services, u8 count, u8 *total_out) { + Result rc=0; + u8 tmp_total_out=0; + BtmGattService tmpservices[100]={0}; + + memset(services, 0, sizeof(BtdevGattService)*count); + + rc = btmuGetGattServices(connection_handle, tmpservices, 100, &tmp_total_out); + if (R_SUCCEEDED(rc)) { + if (tmp_total_out > count) tmp_total_out = count; + for (u8 i=0; i 0x200) return MAKERESULT(Module_Libnx, LibnxError_BadInput); // sdknso would Abort here, sdknso impls the same thing in the btmu func as well (but we don't). + return btmuConfigureBleMtu(id, mtu); +} + +Result btdevGetBleMtu(u32 id, u16 *out) { + return btmuGetBleMtu(id, out); +} + +Result btdevAcquireBleGattOperationEvent(Event* out_event) { + return btRegisterBleEvent(out_event); +} + +Result btdevRegisterGattOperationNotification(const BtdrvGattAttributeUuid *uuid) { + BtmBleDataPath path = {.unk_x0 = 0, .uuid = *uuid}; + return btmuRegisterBleGattDataPath(&path); +} + +Result btdevUnregisterGattOperationNotification(const BtdrvGattAttributeUuid *uuid) { + BtmBleDataPath path = {.unk_x0 = 0, .uuid = *uuid}; + return btmuUnregisterBleGattDataPath(&path); +} + +Result btdevGetGattOperationResult(BtdrvBleClientGattOperationInfo *out) { + Result rc=0; + u32 type=0; + u8 tmpval=0; + BtdrvLeEventInfo eventinfo={0}; + + if (out==NULL) return MAKERESULT(Module_Libnx, LibnxError_BadInput); + memset(out, 0, sizeof(*out)); + + rc = btGetLeEventInfo(&eventinfo, sizeof(eventinfo), &type); + if (R_SUCCEEDED(rc) && type!=8) rc = MAKERESULT(Module_Libnx, LibnxError_ShouldNotHappen); + if (R_SUCCEEDED(rc)) { + switch(eventinfo.unk_x0) { + case 0: + tmpval=0; + break; + + case 4: + case 16: + case 21: + case 50: + tmpval=3; + break; + + case 5: + tmpval=4; + break; + + case 6: + tmpval=5; + break; + + case 7: + tmpval=6; + break; + + case 9: + tmpval=7; + break; + + case 13: + tmpval=8; + break; + + case 25: + case 52: + tmpval=2; + break; + + case 35: + tmpval=10; + break; + + case 55: + tmpval=1; + break; + + default: + tmpval=9; + break; + } + + out->unk_x0 = tmpval; + out->unk_x4 = eventinfo.unk_x4; + out->unk_x8 = eventinfo.unk_x8; + out->uuid0 = eventinfo.uuid0; + out->uuid1 = eventinfo.uuid1; + out->uuid2 = eventinfo.uuid2; + + out->size = eventinfo.size; + if (out->size > sizeof(out->data)) out->size = sizeof(out->data); + memcpy(out->data, eventinfo.data, out->size); + } + + return rc; +} + +Result btdevReadGattCharacteristic(BtdevGattCharacteristic *c) { + Result rc=0; + BtdevGattService tmpservice={0}; + BtdrvGattId gattid0={0}; + BtdrvGattId gattid1={0}; + + if ((btdevGattCharacteristicGetProperties(c) & BIT(1)) == 0) + rc = MAKERESULT(113, 523); + + if (R_SUCCEEDED(rc)) rc = btdevGattCharacteristicGetService(c, &tmpservice); + + if (R_SUCCEEDED(rc)) { + gattid0.instance_id = btdevGattServiceGetInstanceId(&tmpservice); + btdevGattAttributeGetUuid(&tmpservice.attr, &gattid0.uuid); + gattid1.instance_id = btdevGattCharacteristicGetInstanceId(c); + btdevGattAttributeGetUuid(&c->attr, &gattid1.uuid); + rc = btLeClientReadCharacteristic(btdevGattServiceIsPrimaryService(&tmpservice), 0, btdevGattAttributeGetConnectionHandle(&c->attr), &gattid0, &gattid1); + } + + return rc; +} + +Result btdevWriteGattCharacteristic(BtdevGattCharacteristic *c) { + Result rc=0; + BtdevGattService tmpservice={0}; + BtdrvGattId gattid0={0}; + BtdrvGattId gattid1={0}; + u8 prop = btdevGattCharacteristicGetProperties(c); + u64 value_size=0; + u8 value[0x200]={0}; + + if ((prop & (BIT(2) | BIT(3))) == 0) + rc = MAKERESULT(113, 523); + + if (R_SUCCEEDED(rc)) value_size = btdevGattCharacteristicGetValue(c, value, sizeof(value)); + + if (R_SUCCEEDED(rc)) rc = btdevGattCharacteristicGetService(c, &tmpservice); + + if (R_SUCCEEDED(rc)) { + gattid0.instance_id = btdevGattServiceGetInstanceId(&tmpservice); + btdevGattAttributeGetUuid(&tmpservice.attr, &gattid0.uuid); + gattid1.instance_id = btdevGattCharacteristicGetInstanceId(c); + btdevGattAttributeGetUuid(&c->attr, &gattid1.uuid); + rc = btLeClientWriteCharacteristic(btdevGattServiceIsPrimaryService(&tmpservice), 0, (prop & BIT(3)) != 0, btdevGattAttributeGetConnectionHandle(&c->attr), &gattid0, &gattid1, value, value_size); + } + + return rc; +} + +Result btdevEnableGattCharacteristicNotification(BtdevGattCharacteristic *c, bool flag) { + Result rc=0; + BtdevGattService tmpservice={0}; + BtdrvGattId gattid0={0}; + BtdrvGattId gattid1={0}; + u32 connection_handle = btdevGattAttributeGetConnectionHandle(&c->attr); + + if ((btdevGattCharacteristicGetProperties(c) & (BIT(4) | BIT(5))) == 0) + rc = MAKERESULT(113, 523); + + if (R_SUCCEEDED(rc)) rc = btdevGattCharacteristicGetService(c, &tmpservice); + + if (R_SUCCEEDED(rc)) { + gattid0.instance_id = btdevGattServiceGetInstanceId(&tmpservice); + btdevGattAttributeGetUuid(&tmpservice.attr, &gattid0.uuid); + gattid1.instance_id = btdevGattCharacteristicGetInstanceId(c); + btdevGattAttributeGetUuid(&c->attr, &gattid1.uuid); + bool is_primary = btdevGattServiceIsPrimaryService(&tmpservice); + if (flag) + rc = btLeClientRegisterNotification(is_primary, connection_handle, &gattid0, &gattid1); + else + rc = btLeClientDeregisterNotification(is_primary, connection_handle, &gattid0, &gattid1); + } + + return rc; +} + +Result btdevReadGattDescriptor(BtdevGattDescriptor *d) { + Result rc=0; + BtdevGattService tmpservice={0}; + BtdevGattCharacteristic tmpcharacteristic={0}; + BtdrvGattId gattid0={0}; + BtdrvGattId gattid1={0}; + BtdrvGattId gattid2={0}; // instance_id is left at 0. + + rc = btdevGattDescriptorGetService(d, &tmpservice); + if (R_SUCCEEDED(rc)) rc = btdevGattDescriptorGetCharacteristic(d, &tmpcharacteristic); + + if (R_SUCCEEDED(rc)) { + gattid0.instance_id = btdevGattServiceGetInstanceId(&tmpservice); + btdevGattAttributeGetUuid(&tmpservice.attr, &gattid0.uuid); + gattid1.instance_id = btdevGattCharacteristicGetInstanceId(&tmpcharacteristic); + btdevGattAttributeGetUuid(&tmpcharacteristic.attr, &gattid1.uuid); + btdevGattAttributeGetUuid(&d->attr, &gattid2.uuid); + rc = btLeClientReadDescriptor(btdevGattServiceIsPrimaryService(&tmpservice), 0, btdevGattAttributeGetConnectionHandle(&d->attr), &gattid0, &gattid1, &gattid2); + } + + return rc; +} + +Result btdevWriteGattDescriptor(BtdevGattDescriptor *d) { + Result rc=0; + BtdevGattService tmpservice={0}; + BtdevGattCharacteristic tmpcharacteristic={0}; + BtdrvGattId gattid0={0}; + BtdrvGattId gattid1={0}; + BtdrvGattId gattid2={0}; // instance_id is left at 0. + u64 value_size=0; + u8 value[0x200]={0}; + + value_size = btdevGattDescriptorGetValue(d, value, sizeof(value)); + + rc = btdevGattDescriptorGetService(d, &tmpservice); + if (R_SUCCEEDED(rc)) rc = btdevGattDescriptorGetCharacteristic(d, &tmpcharacteristic); + + if (R_SUCCEEDED(rc)) { + gattid0.instance_id = btdevGattServiceGetInstanceId(&tmpservice); + btdevGattAttributeGetUuid(&tmpservice.attr, &gattid0.uuid); + gattid1.instance_id = btdevGattCharacteristicGetInstanceId(&tmpcharacteristic); + btdevGattAttributeGetUuid(&tmpcharacteristic.attr, &gattid1.uuid); + btdevGattAttributeGetUuid(&d->attr, &gattid2.uuid); + rc = btLeClientWriteDescriptor(btdevGattServiceIsPrimaryService(&tmpservice), 0, btdevGattAttributeGetConnectionHandle(&d->attr), &gattid0, &gattid1, &gattid2, value, value_size); + } + + return rc; +} + +// GattAttribute + +void btdevGattAttributeCreate(BtdevGattAttribute *a, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle) { + memset(a, 0, sizeof(*a)); + a->type = 0xff; + a->uuid = *uuid; + a->handle = handle; + a->connection_handle = connection_handle; +} + +// GattService + +void btdevGattServiceCreate(BtdevGattService *s, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle, u16 instance_id, u16 end_group_handle, bool primary_service) { + memset(s, 0, sizeof(*s)); + btdevGattAttributeCreate(&s->attr, uuid, handle, connection_handle); + s->attr.type = 0x3; + s->instance_id = instance_id; + s->end_group_handle = end_group_handle; + s->primary_service = primary_service; +} + +Result btdevGattServiceGetIncludedServices(BtdevGattService *s, BtdevGattService *services, u8 count, u8 *total_out) { + Result rc=0; + u8 tmp_total_out=0; + BtmGattService tmpservices[100]={0}; + + memset(services, 0, sizeof(BtdevGattService)*count); + + rc = btmuGetGattIncludedServices(s->attr.connection_handle, s->attr.handle, tmpservices, 100, &tmp_total_out); + if (R_SUCCEEDED(rc)) { + if (tmp_total_out > count) tmp_total_out = count; + for (u8 i=0; iattr.connection_handle, tmpservices[i].instance_id, tmpservices[i].end_group_handle, tmpservices[i].primary_service); + } + if (total_out) *total_out = tmp_total_out; + } + return rc; +} + +Result btdevGattServiceGetCharacteristics(BtdevGattService *s, BtdevGattCharacteristic *characteristic, u8 count, u8 *total_out) { + Result rc=0; + BtmGattCharacteristic tmpcharacteristics[100]={0}; + u8 tmp_total_out=0; + + if (R_SUCCEEDED(rc)) rc = btmuGetGattCharacteristics(s->attr.connection_handle, s->attr.handle, tmpcharacteristics, 100, &tmp_total_out); + if (R_SUCCEEDED(rc)) { + if (tmp_total_out > count) tmp_total_out = count; + for (u8 i=0; iattr.connection_handle, tmpcharacteristics[i].instance_id, tmpcharacteristics[i].properties); + } + if (total_out) *total_out = tmp_total_out; + } + return rc; +} + +Result btdevGattServiceGetCharacteristic(BtdevGattService *s, const BtdrvGattAttributeUuid *uuid, BtdevGattCharacteristic *characteristic, bool *flag) { + Result rc=0; + BtmGattCharacteristic tmpcharacteristics[100]={0}; + u8 tmp_total_out=0; + bool tmp_flag=false; + + if (R_SUCCEEDED(rc)) rc = btmuGetGattCharacteristics(s->attr.connection_handle, s->attr.handle, tmpcharacteristics, 100, &tmp_total_out); + if (R_SUCCEEDED(rc)) { + if (tmp_total_out > 100) tmp_total_out = 100; + for (u8 i=0; iattr.connection_handle, tmpcharacteristics[i].instance_id, tmpcharacteristics[i].properties); + tmp_flag=true; + break; + } + } + if (flag) *flag = tmp_flag; + } + return rc; +} + +// GattCharacteristic + +void btdevGattCharacteristicCreate(BtdevGattCharacteristic *c, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle, u16 instance_id, u8 properties) { + memset(c, 0, sizeof(*c)); + btdevGattAttributeCreate(&c->attr, uuid, handle, connection_handle); + c->attr.type = 0x1; + c->instance_id = instance_id; + c->properties = properties; +} + +Result btdevGattCharacteristicGetService(BtdevGattCharacteristic *c, BtdevGattService *service) { + Result rc=0; + BtmGattService tmpservice={0}; + bool flag=0; + + rc = btmuGetBelongingGattService(c->attr.connection_handle, c->attr.handle, &tmpservice, &flag); + if (R_SUCCEEDED(rc) && !flag) rc = MAKERESULT(113, 512); + if (R_SUCCEEDED(rc)) btdevGattServiceCreate(service, &tmpservice.uuid, tmpservice.handle, c->attr.connection_handle, tmpservice.instance_id, tmpservice.end_group_handle, tmpservice.primary_service); + return rc; +} + +Result btdevGattCharacteristicGetDescriptors(BtdevGattCharacteristic *c, BtdevGattDescriptor *descriptors, u8 count, u8 *total_out) { + Result rc=0; + u8 tmp_total_out=0; + BtmGattDescriptor tmpdescriptors[100]={0}; + + memset(descriptors, 0, sizeof(BtdevGattDescriptor)*count); + + rc = btmuGetGattDescriptors(c->attr.connection_handle, c->attr.handle, tmpdescriptors, 100, &tmp_total_out); + if (R_SUCCEEDED(rc)) { + if (tmp_total_out > count) tmp_total_out = count; + for (u8 i=0; iattr.connection_handle); + } + if (total_out) *total_out = tmp_total_out; + } + return rc; +} + +Result btdevGattCharacteristicGetDescriptor(BtdevGattCharacteristic *c, const BtdrvGattAttributeUuid *uuid, BtdevGattDescriptor *descriptor, bool *flag) { + Result rc=0; + u8 tmp_total_out=0; + bool tmp_flag=0; + BtmGattDescriptor tmpdescriptors[100]={0}; + + memset(descriptor, 0, sizeof(BtdevGattDescriptor)); + + rc = btmuGetGattDescriptors(c->attr.connection_handle, c->attr.handle, tmpdescriptors, 100, &tmp_total_out); + if (R_SUCCEEDED(rc)) { + if (tmp_total_out > 100) tmp_total_out = 100; + for (u8 i=0; iattr.connection_handle); + tmp_flag = true; + break; + } + } + if (flag) *flag = tmp_flag; + } + return rc; +} + +void btdevGattCharacteristicSetValue(BtdevGattCharacteristic *c, const void* buffer, size_t size) { + if (buffer==NULL || size > sizeof(c->value)) return; + c->value_size = size; + memcpy(c->value, buffer, size); +} + +u64 btdevGattCharacteristicGetValue(BtdevGattCharacteristic *c, void* buffer, size_t size) { + if (buffer==NULL) return 0; + u64 out_size = c->value_size; + if (size < out_size) out_size = size; + if (out_size > sizeof(c->value)) out_size = sizeof(c->value); + memcpy(buffer, c->value, out_size); + return out_size; +} + +// GattDescriptor + +void btdevGattDescriptorCreate(BtdevGattDescriptor *d, const BtdrvGattAttributeUuid *uuid, u16 handle, u32 connection_handle) { + memset(d, 0, sizeof(*d)); + btdevGattAttributeCreate(&d->attr, uuid, handle, connection_handle); + d->attr.type = 0x2; +} + +Result btdevGattDescriptorGetService(BtdevGattDescriptor *d, BtdevGattService *service) { + Result rc=0; + BtmGattService tmpservice={0}; + bool flag=0; + + rc = btmuGetBelongingGattService(d->attr.connection_handle, d->attr.handle, &tmpservice, &flag); + if (R_SUCCEEDED(rc) && !flag) rc = MAKERESULT(113, 512); + if (R_SUCCEEDED(rc)) btdevGattServiceCreate(service, &tmpservice.uuid, tmpservice.handle, d->attr.connection_handle, tmpservice.instance_id, tmpservice.end_group_handle, tmpservice.primary_service); + return rc; +} + +Result btdevGattDescriptorGetCharacteristic(BtdevGattDescriptor *d, BtdevGattCharacteristic *characteristic) { + Result rc=0; + BtdevGattService tmpservice={0}; + BtmGattCharacteristic tmpcharacteristics[100]={0}; + u8 tmp_total_out=0; + + rc = btdevGattDescriptorGetService(d, &tmpservice); + + if (R_SUCCEEDED(rc)) rc = btmuGetGattCharacteristics(d->attr.connection_handle, btdevGattAttributeGetHandle(&tmpservice.attr), tmpcharacteristics, 100, &tmp_total_out); + if (R_SUCCEEDED(rc)) { + if (tmp_total_out > 100) tmp_total_out = 100; + for (u8 i=0; iattr.handle && (tmp_total_out-1 == i || d->attr.handle < tmpcharacteristics[i+1].handle)) { + btdevGattCharacteristicCreate(characteristic, &tmpcharacteristics[i].uuid, tmpcharacteristics[i].handle, d->attr.connection_handle, tmpcharacteristics[i].instance_id, tmpcharacteristics[i].properties); + return 0; + } + } + } + if (R_SUCCEEDED(rc)) rc = MAKERESULT(113, 512); + return rc; +} + +void btdevGattDescriptorSetValue(BtdevGattDescriptor *d, const void* buffer, size_t size) { + if (buffer==NULL || size > sizeof(d->value)) return; + d->value_size = size; + memcpy(d->value, buffer, size); +} + +u64 btdevGattDescriptorGetValue(BtdevGattDescriptor *d, void* buffer, size_t size) { + if (buffer==NULL) return 0; + u64 out_size = d->value_size; + if (size < out_size) out_size = size; + if (out_size > sizeof(d->value)) out_size = sizeof(d->value); + memcpy(buffer, d->value, out_size); + return out_size; +} +