#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 parameter_id, BtdrvBleAdvertisePacketParameter *out) { return btmuGetBleScanFilterParameter(parameter_id, out); } Result btdevGetBleScanParameter2(u16 parameter_id, BtdrvGattAttributeUuid *out) { return btmuGetBleScanFilterParameter2(parameter_id, 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 connection_handle) { return btmuBleDisconnect(connection_handle); } 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(connection_handle, mtu); } Result btdevGetBleMtu(u32 connection_handle, u16 *out) { return btmuGetBleMtu(connection_handle, 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(btdevGattAttributeGetConnectionHandle(&c->attr), btdevGattServiceIsPrimaryService(&tmpservice), &gattid0, &gattid1, 0); } 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(btdevGattAttributeGetConnectionHandle(&c->attr), btdevGattServiceIsPrimaryService(&tmpservice), &gattid0, &gattid1, value, value_size, 0, (prop & BIT(3)) != 0); } 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 primary_service = btdevGattServiceIsPrimaryService(&tmpservice); if (flag) rc = btLeClientRegisterNotification(connection_handle, primary_service, &gattid0, &gattid1); else rc = btLeClientDeregisterNotification(connection_handle, primary_service, &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(btdevGattAttributeGetConnectionHandle(&d->attr), btdevGattServiceIsPrimaryService(&tmpservice), &gattid0, &gattid1, &gattid2, 0); } 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(btdevGattAttributeGetConnectionHandle(&d->attr), btdevGattServiceIsPrimaryService(&tmpservice), &gattid0, &gattid1, &gattid2, value, value_size, 0); } 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 *characteristics, 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; }