/* * This file is part of Adafruit for EFR32 project * * The MIT License (MIT) * * Copyright 2023 Silicon Laboratories Inc. www.silabs.com * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include #include "py/runtime.h" #include "shared-bindings/_bleio/__init__.h" #include "shared-bindings/_bleio/Adapter.h" #include "shared-bindings/_bleio/Characteristic.h" #include "shared-bindings/_bleio/CharacteristicBuffer.h" #include "shared-bindings/_bleio/PacketBuffer.h" #include "shared-bindings/_bleio/Connection.h" #include "shared-bindings/_bleio/Descriptor.h" #include "shared-bindings/_bleio/Service.h" #include "shared-bindings/_bleio/UUID.h" #include "supervisor/shared/bluetooth/bluetooth.h" #include "common-hal/_bleio/__init__.h" STATIC conn_state_t conn_state; osMutexId_t bluetooth_connection_mutex_id; bleio_adapter_obj_t common_hal_bleio_adapter_obj; uint8_t ble_bonding_handle = 0xFF; __ALIGNED(4) static uint8_t bluetooth_connection_mutex_cb[osMutexCbSize]; const osMutexAttr_t bluetooth_connection_mutex_attr = { .name = "Bluetooth Mutex", .attr_bits = osMutexRecursive | osMutexPrioInherit, .cb_mem = bluetooth_connection_mutex_cb, .cb_size = osMutexCbSize }; void bleio_user_reset() { // Stop any user scanning or advertising. common_hal_bleio_adapter_stop_scan(&common_hal_bleio_adapter_obj); common_hal_bleio_adapter_stop_advertising(&common_hal_bleio_adapter_obj); // Maybe start advertising the BLE workflow. supervisor_bluetooth_background(); } void bleio_reset() { reset_dynamic_service(); reset_packet_buffer_list(); reset_characteristic_buffer_list(); bleio_adapter_reset(&common_hal_bleio_adapter_obj); // Set this explicitly to save data. common_hal_bleio_adapter_obj.base.type = &bleio_adapter_type; if (!common_hal_bleio_adapter_get_enabled(&common_hal_bleio_adapter_obj)) { return; } supervisor_stop_bluetooth(); common_hal_bleio_adapter_set_enabled(&common_hal_bleio_adapter_obj, false); supervisor_start_bluetooth(); } void bleio_background(void) { } void common_hal_bleio_gc_collect(void) { bleio_adapter_gc_collect(&common_hal_bleio_adapter_obj); } void check_ble_error(int error_code) { if (error_code == SL_STATUS_OK) { return; } switch (error_code) { case SL_STATUS_TIMEOUT: mp_raise_msg(&mp_type_TimeoutError, NULL); return; default: mp_raise_bleio_BluetoothError( translate("Unknown BLE error: %d"), error_code); break; } } void common_hal_bleio_check_connected(uint16_t conn_handle) { if (conn_handle == BLEIO_HANDLE_INVALID) { mp_raise_ConnectionError(translate("Not connected")); } } // Bluetooth stack event handler. void sl_bt_on_event(sl_bt_msg_t *evt) { bd_addr address; uint8_t address_type = 0; STATIC uint8_t serv_idx = 0; STATIC uint8_t device_name[16]; bleio_connection_internal_t *connection; bleio_service_obj_t *service; bleio_characteristic_obj_t *characteristic; bleio_uuid_obj_t *uuid; bleio_characteristic_properties_t props; switch (SL_BT_MSG_ID(evt->header)) { case sl_bt_evt_system_boot_id: sl_bt_system_get_identity_address(&address, &address_type); snprintf((char *)device_name, 14 + 1, "CIRCUITPY-%X%X",address.addr[1], address.addr[0]); sl_bt_gatt_server_write_attribute_value(gattdb_device_name, 0,14,device_name); sl_bt_sm_store_bonding_configuration(5,2); sl_bt_sm_configure(0x00,sl_bt_sm_io_capability_noinputnooutput); sl_bt_sm_set_bondable_mode(1); break; // This event indicates that a new connection was opened. case sl_bt_evt_connection_opened_id: serv_idx = 0; osMutexAcquire(bluetooth_connection_mutex_id, osWaitForever); // Device role is Peripheral if (evt->data.evt_connection_opened.master == 0) { bleio_connections[0].conn_handle = evt->data.evt_connection_opened.connection; bleio_connections[0].connection_obj = mp_const_none; bleio_connections[0].pair_status = PAIR_PAIRED; bleio_connections[0].is_central = false; bleio_connections[0].mtu = 0; } ble_bonding_handle = evt->data.evt_connection_opened.bonding; osMutexRelease(bluetooth_connection_mutex_id); break; case sl_bt_evt_scanner_legacy_advertisement_report_id: set_scan_device_info_on_ble_evt( evt->data.evt_scanner_legacy_advertisement_report.address, evt->data.evt_scanner_legacy_advertisement_report.address_type, evt->data.evt_scanner_legacy_advertisement_report.rssi, &evt->data.evt_scanner_legacy_advertisement_report.data); if (xscan_event != NULL) { xEventGroupSetBits(xscan_event,1 << 0); } break; case sl_bt_evt_connection_closed_id: common_hal_bleio_adapter_remove_connection( evt->data.evt_connection_closed.connection); // reset bonding handle variable to avoid deleting wrong bonding info ble_bonding_handle = 0xFF; break; case sl_bt_evt_system_external_signal_id: if (evt->data.evt_system_external_signal.extsignals & 1) { sl_bt_external_signal(0); } break; case sl_bt_evt_gatt_service_id: osMutexAcquire(bluetooth_connection_mutex_id, osWaitForever); connection = bleio_conn_handle_to_connection( evt->data.evt_gatt_service.connection); service = m_new_obj(bleio_service_obj_t); if (NULL == service) { mp_raise_bleio_BluetoothError( translate("Create new service obj fail")); } service->base.type = &bleio_service_type; bleio_service_from_connection(service, bleio_connection_new_from_internal(connection)); service->is_remote = true; service->handle = evt->data.evt_gatt_service.service; uuid = m_new_obj(bleio_uuid_obj_t); if (NULL == uuid) { osMutexRelease(bluetooth_connection_mutex_id); mp_raise_bleio_BluetoothError( translate("Create new service uuid obj fail")); break; } uuid->base.type = &bleio_uuid_type; if (UUID16_LEN == evt->data.evt_gatt_service.uuid.len) { uuid->efr_ble_uuid.uuid16.value &= 0x0000; uuid->efr_ble_uuid.uuid16.value |= evt->data.evt_gatt_service.uuid.data[1]; uuid->efr_ble_uuid.uuid16.value = (uuid->efr_ble_uuid.uuid16.value << 8) | evt->data.evt_gatt_service.uuid.data[0]; uuid->efr_ble_uuid.uuid.type = BLE_UUID_TYPE_16; } else if (UUID128_LEN == evt->data.evt_gatt_service.uuid.len) { memcpy(uuid->efr_ble_uuid.uuid128.value, evt->data.evt_gatt_service.uuid.data, UUID128_LEN); uuid->efr_ble_uuid.uuid.type = BLE_UUID_TYPE_128; } service->uuid = uuid; mp_obj_list_append(MP_OBJ_FROM_PTR(connection->remote_service_list), MP_OBJ_FROM_PTR(service)); conn_state = DISCOVER_SERVICES; osMutexRelease(bluetooth_connection_mutex_id); break; case sl_bt_evt_gatt_characteristic_id: osMutexAcquire(bluetooth_connection_mutex_id, osWaitForever); connection = bleio_conn_handle_to_connection( evt->data.evt_gatt_characteristic.connection); service = MP_OBJ_TO_PTR(connection->remote_service_list->items[serv_idx - 1]); characteristic = m_new_obj(bleio_characteristic_obj_t); if (characteristic == NULL) { mp_raise_bleio_BluetoothError( translate("Create new characteristic obj fail.")); } characteristic->base.type = &bleio_characteristic_type; uuid = m_new_obj(bleio_uuid_obj_t); if (uuid == NULL) { mp_raise_bleio_BluetoothError( translate("Create new characteristic uuid obj fail.")); break; } uuid->base.type = &bleio_uuid_type; if (UUID16_LEN == evt->data.evt_gatt_characteristic.uuid.len) { uuid->efr_ble_uuid.uuid16.value &= 0x0000; uuid->efr_ble_uuid.uuid16.value |= evt->data.evt_gatt_characteristic.uuid.data[1]; uuid->efr_ble_uuid.uuid16.value = (uuid->efr_ble_uuid.uuid16.value << 8) | evt->data.evt_gatt_characteristic.uuid.data[0]; uuid->efr_ble_uuid.uuid.type = BLE_UUID_TYPE_16; } else if ( UUID128_LEN == evt->data.evt_gatt_characteristic.uuid.len) { memcpy(uuid->efr_ble_uuid.uuid128.value, evt->data.evt_gatt_characteristic.uuid.data, UUID128_LEN); uuid->efr_ble_uuid.uuid.type = BLE_UUID_TYPE_128; } props = evt->data.evt_gatt_characteristic.properties; characteristic->handle = evt->data.evt_gatt_characteristic.characteristic; characteristic->props = props; common_hal_bleio_characteristic_construct( characteristic, service, evt->data.evt_gatt_characteristic.characteristic, uuid, props, SECURITY_MODE_OPEN, SECURITY_MODE_OPEN, 0, false, mp_const_empty_bytes, NULL); mp_obj_list_append(MP_OBJ_FROM_PTR(service->characteristic_list), MP_OBJ_FROM_PTR(characteristic)); osMutexRelease(bluetooth_connection_mutex_id); break; case sl_bt_evt_gatt_procedure_completed_id: if (conn_state == DISCOVER_SERVICES || conn_state == DISCOVER_CHARACTERISTICS) { connection = MP_OBJ_TO_PTR( bleio_conn_handle_to_connection( evt->data.evt_gatt_procedure_completed.connection)); if (connection != NULL && serv_idx < connection->remote_service_list->len) { service = connection->remote_service_list->items[serv_idx]; sl_bt_gatt_discover_characteristics( evt->data.evt_gatt_procedure_completed.connection, service->handle); conn_state = DISCOVER_CHARACTERISTICS; serv_idx++; } else { conn_state = RUNNING; serv_idx = 0; if (xdiscovery_event != NULL) { xEventGroupSetBits(xdiscovery_event,1 << 0); } } } break; case sl_bt_evt_gatt_characteristic_value_id: if (characteristic_buffer_on_ble_evt( evt->data.evt_gatt_characteristic_value.characteristic, evt->data.evt_gatt_characteristic_value.value.data, evt->data.evt_gatt_characteristic_value.value.len)) { } else if (packet_buffer_on_ble_evt( evt->data.evt_gatt_characteristic_value.characteristic, evt->data.evt_gatt_characteristic_value.value.data, evt->data.evt_gatt_characteristic_value.value.len)) { } else { set_characteristic_value_on_ble_evt( evt->data.evt_gatt_characteristic_value.connection, evt->data.evt_gatt_characteristic_value.characteristic, evt->data.evt_gatt_characteristic_value.value.data, evt->data.evt_gatt_characteristic_value.value.len); } sl_bt_gatt_send_characteristic_confirmation( evt->data.evt_gatt_characteristic_value.connection); break; case sl_bt_evt_gatt_server_attribute_value_id: if (characteristic_buffer_on_ble_evt( evt->data.evt_gatt_server_attribute_value.attribute, evt->data.evt_gatt_server_attribute_value.value.data, evt->data.evt_gatt_server_attribute_value.value.len)) { } else { packet_buffer_on_ble_evt( evt->data.evt_gatt_server_attribute_value.attribute, evt->data.evt_gatt_server_attribute_value.value.data, evt->data.evt_gatt_server_attribute_value.value.len); } break; case sl_bt_evt_gatt_server_characteristic_status_id: break; case sl_bt_evt_sm_passkey_display_id: break; case sl_bt_evt_sm_confirm_bonding_id: sl_bt_sm_bonding_confirm(evt->data.evt_sm_confirm_bonding.connection,1); break; case sl_bt_evt_sm_bonded_id: break; case sl_bt_evt_sm_bonding_failed_id: if (ble_bonding_handle != 0xFF) { sl_bt_sm_delete_bonding(ble_bonding_handle); ble_bonding_handle = 0xFF; } break; case sl_bt_evt_connection_parameters_id: break; default: break; } }