/** * UNPUBLISHED PROPRIETARY SOURCE CODE * Copyright (c) 2016 MXCHIP Inc. * * The contents of this file may not be disclosed to third parties, copied or * duplicated in any form, in whole or in part, without the prior written * permission of MXCHIP Corporation. * */ /** @file * */ #include "mico.h" #include "StringUtils.h" #include "mico_bt_smartbridge.h" #include "mico_bt_gatt.h" #include "mico_bt_ble.h" #include "mico_bt_cfg.h" #include "mico_bt.h" #include "bt_smartbridge_socket_manager.h" #include "bt_smartbridge_att_cache_manager.h" #include "bt_smartbridge_helper.h" #include "bt_smartbridge_stack_interface.h" /****************************************************** * Macros ******************************************************/ /****************************************************** * Constants ******************************************************/ #define SOCKET_INVALID_CONNECTION_HANDLE ( 0xFFFF ) #define MAX_CONNECTION_TIMEOUT ( 10000 ) #define MAX_AUTO_CONNECTION_SEMA_TIMEOUT ( 200 ) #define SMARTBRIDGE_AUTO_CONN_SCAN_INTERVAL ( 600 ) /****************************************************** * Enumerations ******************************************************/ /* Auto-connection precedure */ typedef enum { /* A device is scanned and ready to report to user. */ SMARTBRIDGE_AUTO_CONN_STATE_SCANED = 1, /* A device is reported to wait user action */ SMARTBRIDGE_AUTO_CONN_STATE_REPORTED, /* A device is not allowed connecting to by user and it will * be translate to FREE state. */ SMARTBRIDGE_AUTO_CONN_STATE_IDLE, /* A device is alloed connecting to by user. */ SMARTBRIDGE_AUTO_CONN_STATE_CONNING, /* A device is free and it can be reused by SCAN procedure. */ SMARTBRIDGE_AUTO_CONN_STATE_FREE, } smartbridge_auto_conn_state_t; /****************************************************** * Type Definitions ******************************************************/ typedef struct { uint8_t bdaddr[6]; uint8_t name[16]; uint8_t adv_data[31]; uint8_t length; uint8_t handle; } smartbridge_auto_conn_dev_info_t; /* Local auto connection settings */ typedef struct { mico_bt_smartbridge_socket_t *socket; /**< A socket associated with the auto connection */ mico_bt_smart_connection_settings_t conn_settings; /**< The connection settings associated with a socket */ smartbridge_auto_conn_dev_info_t device_info; /**< The scanned device information */ } smartbridge_auto_conn_entity_t; /* Device info ready to connect */ typedef struct { linked_list_node_t this_node; /* Linked-list node of this device */ mico_bt_smart_advertising_report_t report; /* Remote BT device */ smartbridge_auto_conn_state_t state; /* Auto-connection state */ } smartbridge_auto_conn_report_t; /* Device info ready to connect */ typedef struct { linked_list_t list; mico_mutex_t mutex; } smartbridge_auto_conn_list_t; /****************************************************** * Structures ******************************************************/ /****************************************************** * Static Function Declarations ******************************************************/ static OSStatus smartbridge_app_notification_handler ( void* arg ); static OSStatus smartbridge_app_disconnection_handler ( void* arg ); static void smartbridge_gatt_auto_conn_handler ( mico_bt_device_address_t bdaddr, uint16_t connection_handle ); static mico_bool_t smartbridge_gap_auto_conn_user_cfg ( const mico_bt_device_address_t device_address, const uint8_t *device_name, const uint8_t *p_data, uint8_t length ); static OSStatus smartbridge_gap_auto_conn_asyn_event_handler ( void *arg ); static OSStatus smartbridge_gap_auto_conn_scan_report_handler ( const mico_bt_smart_advertising_report_t *report ); static OSStatus smartbridge_gap_auto_conn_scan_cmpl_handler ( void *arg ); static OSStatus smartbridge_gap_auto_conn_cryption ( void* arg ); static OSStatus smartbridge_auto_conn_user_parms ( void* arg ); static int8_t smartbridge_auto_conn_dev_alloc ( void ); static int8_t smartbridge_auto_conn_find_dev_by_addr ( const mico_bt_device_address_t device_address ); static OSStatus smartbridge_auto_conn_list_init ( void ); static OSStatus smartbridge_auto_conn_list_deinit ( void ); static OSStatus smartbridge_auto_conn_list_add ( const mico_bt_smart_advertising_report_t *report ); static OSStatus smartbridge_auto_conn_list_remove ( mico_bt_device_address_t bdaddr ); static OSStatus smartbridge_auto_conn_list_clear ( void ); static OSStatus smartbridge_auto_conn_list_remove_by_state ( smartbridge_auto_conn_state_t state ); static OSStatus smartbridge_auto_conn_list_set_state ( const mico_bt_device_address_t bdaddr, smartbridge_auto_conn_state_t state ); static OSStatus smartbridge_auto_conn_list_get_by_state ( mico_bt_smart_advertising_report_t **report, smartbridge_auto_conn_state_t state ); /****************************************************** * Variable Definitions ******************************************************/ mico_bt_smartbridge_socket_t* connecting_socket = NULL; static mico_bool_t initialised = MICO_FALSE; extern mico_bool_t bt_initialised; extern mico_bt_dev_ble_io_caps_req_t local_io_caps_ble; extern gatt_subprocedure_t smartbridge_subprocedure; mico_bt_gatt_char_declaration_t current_characteristic; /** Auto Connection Establishment Procedure */ static mico_semaphore_t g_auto_conn_sem = NULL; static smartbridge_auto_conn_entity_t g_auto_conn_dev_table[BTM_BLE_MAX_BG_CONN_DEV_NUM]; static mico_bt_smartbridge_auto_connection_parms_cback_t g_auto_conn_cback = NULL; static mico_bt_smart_scan_settings_t g_auto_conn_scan_cfg; static uint16_t g_auto_conn_scan_duration = 0; static smartbridge_auto_conn_list_t g_auto_conn_list; /****************************************************** * Function Definitions ******************************************************/ static void smartbridge_gatt_connection_handler( uint16_t connection_handle ) { bt_smartbridge_log( "GATT connection was SUCCESS" ); /* Update connection handle and state of the socket */ connecting_socket->state = SOCKET_STATE_LINK_CONNECTED; connecting_socket->connection_handle = connection_handle; /* Add socket to the connected list */ bt_smartbridge_socket_manager_insert_socket( connecting_socket ); /* Notify app thread that link is connected */ mico_rtos_set_semaphore( &connecting_socket->semaphore ); } static void smartbridge_gatt_auto_conn_handler( mico_bt_device_address_t bdaddr, uint16_t connection_handle ) { int8_t idx = 0; mico_bt_smartbridge_socket_t *p_socket; bt_smartbridge_log("Auto connection [%02x:%02x:%02x:%02x:%02x:%02x] established.", bdaddr[0], bdaddr[1], bdaddr[2], bdaddr[3], bdaddr[4], bdaddr[5]); /* Check connection object */ idx = smartbridge_auto_conn_find_dev_by_addr(bdaddr); if (idx < 0) { smartbridge_bt_interface_disconnect(connection_handle); bt_smartbridge_log("There is not device in the white list."); return; } /* Check connection state. */ p_socket = g_auto_conn_dev_table[idx].socket; if (p_socket->state == SOCKET_STATE_LINK_CONNECTED || p_socket->state == SOCKET_STATE_LINK_ENCRYPTED) { bt_smartbridge_log("Incredible error"); return; } p_socket->state = SOCKET_STATE_LINK_CONNECTED; /* Manage the socket state. */ smartbridge_helper_socket_clear_actions(p_socket, SOCKET_ACTION_ENCRYPT_USING_BOND_INFO | SOCKET_ACTION_INITIATE_PAIRING); p_socket->connection_handle = connection_handle; bt_smartbridge_socket_manager_insert_socket(p_socket); /* Tell smartbridge_gap_auto_conn_asyn_event_handler a connection is ready. */ mico_rtos_set_semaphore(&g_auto_conn_sem); } static void smartbridge_gatt_disconnection_handler( uint16_t connection_handle ) { bt_smartbridge_log( "GATT disconnection" ); mico_bt_smartbridge_socket_t* removed_socket = NULL; /* Remove socket from the connected list */ if ( bt_smartbridge_socket_manager_remove_socket( connection_handle, &removed_socket ) == MICO_BT_SUCCESS ) { /* Reset connection handle to invalid value */ removed_socket->connection_handle = SOCKET_INVALID_CONNECTION_HANDLE; /* Reset socket state */ removed_socket->state = SOCKET_STATE_DISCONNECTED; /* Mark att cache as inactive and reset reference to cache */ bt_smartbridge_att_cache_set_active_state( (bt_smartbridge_att_cache_t*)removed_socket->att_cache, MICO_FALSE ); removed_socket->att_cache = NULL; /* Check if disconnection is from host or remote device */ if ( smartbridge_helper_socket_check_actions_enabled( removed_socket, SOCKET_ACTION_HOST_DISCONNECT ) == MICO_TRUE ) { /* Disconnection is originated from the host. Notify app thread that disconnection is complete */ mico_rtos_set_semaphore( &removed_socket->semaphore ); } else { /* Notify app that connection is disconnected by the remote device */ if ( removed_socket->disconnection_callback != NULL ) { mico_rtos_send_asynchronous_event( MICO_BT_EVT_WORKER_THREAD, smartbridge_app_disconnection_handler, (void*)removed_socket ); } /* If disconnection happens when connection is still being established. Notify app */ if ( connecting_socket == removed_socket ) { mico_rtos_set_semaphore( &connecting_socket->semaphore ); } } } else { /* If disconnection happens when connection is still being established. Notify app */ if ( connecting_socket != NULL ) { mico_rtos_set_semaphore( &connecting_socket->semaphore ); } } } static void smartbridge_gatt_read_operation_complete_handler( mico_bt_gatt_data_t* response_data ) { mico_bt_smart_attribute_t* attr; uint8_t *data = NULL; /* Create a new attribute */ mico_bt_smart_attribute_create( &attr, MICO_ATTRIBUTE_TYPE_CHARACTERISTIC_VALUE, response_data->len ); if ( attr != NULL ) { attr->next = NULL; attr->handle = smartbridge_subprocedure.start_handle; attr->type.len = smartbridge_subprocedure.uuid.len; data = response_data->p_data; attr->value_length = response_data->len; if( smartbridge_subprocedure.uuid.len == UUID_16BIT ) { attr->type.uu.uuid16 = smartbridge_subprocedure.uuid.uu.uuid16; } else if( smartbridge_subprocedure.uuid.len == UUID_128BIT ) { memcpy( attr->type.uu.uuid128, smartbridge_subprocedure.uuid.uu.uuid128, UUID_128BIT ); } if( smartbridge_subprocedure.subprocedure == GATT_READ_CHARACTERISTIC_VALUE ) { memcpy( attr->value.characteristic_value.value, data, attr->value_length ); } else if ( smartbridge_subprocedure.subprocedure == GATT_READ_CHARACTERISTIC_DESCRIPTORS) { memcpy( &attr->value, data, attr->value_length ); } //memcpy( &attr->type, &subprocedure.uuid, sizeof(mico_bt_uuid_t)); /* Update temporary variables */ if ( smartbridge_subprocedure.attr_head == NULL ) { smartbridge_subprocedure.attr_head = attr; } smartbridge_subprocedure.result = MICO_BT_SUCCESS; char *debug_str = NULL; if( smartbridge_subprocedure.subprocedure == GATT_READ_CHARACTERISTIC_VALUE ) { debug_str = DataToHexStringWithColons( attr->value.characteristic_value.value, attr->value_length ); bt_smartbridge_log( "Read value: UUID:%x length:%d: [%s]", attr->type.uu.uuid16, attr->type.len, debug_str ); free( debug_str ); } else if ( smartbridge_subprocedure.subprocedure == GATT_READ_CHARACTERISTIC_DESCRIPTORS) { debug_str = DataToHexStringWithColons( attr->value.value, attr->value_length ); bt_smartbridge_log( "Read value: UUID:%x length:%d: [%s]", attr->type.uu.uuid16, attr->type.len, debug_str ); free( debug_str ); } } else { smartbridge_subprocedure.result = MICO_BT_OUT_OF_HEAP_SPACE; } } static OSStatus smartbridge_gatt_notification_indication_handler( mico_bt_gatt_operation_complete_t* operation_complete ) { mico_bt_smartbridge_socket_t* socket; uint16_t connection_handle = operation_complete->conn_id; uint16_t attribute_handle = operation_complete->response_data.att_value.handle; uint8_t* data = operation_complete->response_data.att_value.p_data; uint8_t length = operation_complete->response_data.att_value.len; bt_smartbridge_log("Process peer device[0x%04x] %s...", connection_handle, operation_complete->op == GATTC_OPTYPE_INDICATION ? "INDICATION" : "NOTIFICATION"); /* Send Indication ACK to peer device. */ if( operation_complete->op == GATTC_OPTYPE_INDICATION ) { bt_smartbridge_log("Confirm peer device's INDICATION"); if ( mico_bt_gatt_send_indication_confirm( connection_handle, attribute_handle ) != 0 ) { bt_smartbridge_log("Send INDICATION confirm failed!!!"); } } /* Search for socket with indicated connection handle in the connected list */ if ( bt_smartbridge_socket_manager_find_socket_by_handle( connection_handle, &socket ) == MICO_BT_SUCCESS ) { if ( bt_smartbridge_att_cache_is_enabled() == MICO_TRUE && socket->att_cache != NULL ) { bt_smartbridge_att_cache_t* cache = (bt_smartbridge_att_cache_t*)socket->att_cache; mico_bt_smart_attribute_list_t* att_cache_list = NULL; mico_bt_smart_attribute_t* att = NULL; bt_smartbridge_att_cache_get_list( cache, &att_cache_list ); /* Socket found. lock mutex for protected access */ bt_smartbridge_att_cache_lock( cache ); /* Search for att in the socket's att list */ if ( mico_bt_smart_attribute_search_list_by_handle( att_cache_list, attribute_handle, &att ) == MICO_BT_SUCCESS ) { mico_bt_uuid_t uuid = att->type; mico_bool_t is_new_att = MICO_FALSE; /* Check if existing att memory length is sufficient */ if ( length > att->value_length ) { /* length isn't sufficient. Remove existing from the list */ mico_bt_smart_attribute_remove_from_list( att_cache_list, attribute_handle ); att = NULL; /* Create a new one and marked as new */ mico_bt_smart_attribute_create( &att, MICO_ATTRIBUTE_TYPE_CHARACTERISTIC_VALUE, length ); is_new_att = MICO_TRUE; } /* Copy new value to the att */ att->handle = attribute_handle; att->type = uuid; att->value_length = length; memcpy( att->value.value, data, length ); if ( is_new_att == MICO_TRUE ) { /* Add newly created att to the list */ mico_bt_smart_attribute_add_to_list( att_cache_list, att ); } } /* Socket found. lock mutex for protected access */ bt_smartbridge_att_cache_unlock( cache ); } socket->last_notified_attribute_handle = attribute_handle; /* Notification callback is called regardless of att cache is enabled or not */ if ( socket->notification_callback != NULL ) { if( kNoErr != mico_rtos_send_asynchronous_event( MICO_BT_EVT_WORKER_THREAD, smartbridge_app_notification_handler, (void*)socket ) ) { bt_smartbridge_log("Send asynchronous event for INDICATION/NOTIFICATION handler failed!!!"); return MICO_BT_ERROR; } } return MICO_BT_SUCCESS; } else { bt_smartbridge_log("Unknown connection handle: 0x%04x", connection_handle); } return MICO_BT_ERROR; } static void smartbridge_gatt_discover_characteristic_descriptor_result( mico_bt_gatt_event_data_t *p_event_data ) { /* Create attribute(s) based on information included in the response PDU */ mico_bt_smart_attribute_t* attr; mico_bt_smart_attribute_create( &attr, MICO_ATTRIBUTE_TYPE_NO_VALUE, 0 ); if ( attr != NULL ) { attr->next = NULL; attr->handle = GATT_DISCOVERY_RESULT_CHARACTERISTIC_DESCRIPTOR_VALUE_HANDLE(p_event_data); attr->type.len = GATT_DISCOVERY_RESULT_CHARACTERISTIC_DESCRIPTOR_UUID_LEN(p_event_data); if( attr->type.len == UUID_16BIT ) { attr->type.uu.uuid16 = GATT_DISCOVERY_RESULT_CHARACTERISTIC_DESCRIPTOR_UUID16(p_event_data); } else if ( attr->type.len == UUID_32BIT ) { attr->type.uu.uuid32 = GATT_DISCOVERY_RESULT_CHARACTERISTIC_DESCRIPTOR_UUID32(p_event_data); } else if ( attr->type.len == UUID_128BIT ) { memcpy( (uint8_t *)attr->type.uu.uuid128, &(GATT_DISCOVERY_RESULT_CHARACTERISTIC_DESCRIPTOR_UUID32(p_event_data)), UUID_128BIT ); } if ( smartbridge_subprocedure.attr_head == NULL ) { smartbridge_subprocedure.attr_head = attr; } if ( smartbridge_subprocedure.attr_tail != NULL ) { smartbridge_subprocedure.attr_tail->next = attr; } smartbridge_subprocedure.attr_tail = attr; smartbridge_subprocedure.attr_count++; bt_smartbridge_log( "Characteristic Descriptor handle:%x uuid:%x list-count:%u", attr->handle, attr->type.uu.uuid16, (unsigned int)smartbridge_subprocedure.attr_count ); } } static void smartbridge_gatt_discover_services_result( mico_bt_gatt_event_data_t *p_event_data ) { /* Create attribute(s) based on information included in the response PDU */ mico_bt_smart_attribute_t* attr; mico_bt_smart_attribute_create( &attr, MICO_ATTRIBUTE_TYPE_PRIMARY_SERVICE, 0 ); if ( attr != NULL ) { attr->next = NULL; attr->type.len = UUID_16BIT; attr->type.uu.uuid16 = GATT_UUID_PRI_SERVICE; attr->value_length = 2; attr->handle = p_event_data->discovery_result.discovery_data.group_value.s_handle; attr->value.service.start_handle = attr->handle; attr->value.service.end_handle = p_event_data->discovery_result.discovery_data.group_value.e_handle; memcpy( &attr->value.service.uuid, &p_event_data->discovery_result.discovery_data.group_value.service_type, sizeof(mico_bt_uuid_t) ); if ( smartbridge_subprocedure.attr_head == NULL ) { smartbridge_subprocedure.attr_head = attr; } if ( smartbridge_subprocedure.attr_tail != NULL ) { smartbridge_subprocedure.attr_tail->next = attr; } smartbridge_subprocedure.attr_tail = attr; smartbridge_subprocedure.attr_count++; bt_smartbridge_log( "Service [Start %x - End %x] uuid:%x len:%d list-count:%u", attr->handle, attr->value.service.end_handle, attr->value.service.uuid.uu.uuid16, attr->value.service.uuid.len, (unsigned int)smartbridge_subprocedure.attr_count ); } } static void smartbridge_gatt_discover_included_services_result( mico_bt_gatt_event_data_t *p_event_data ) { /* Create attribute(s) based on information included in the response PDU */ mico_bt_smart_attribute_t* attr; //uint16_t start_handle = 0x0; mico_bt_smart_attribute_create( &attr, MICO_ATTRIBUTE_TYPE_INCLUDE, 0 ); if ( attr != NULL ) { attr->next = NULL; attr->type.len = UUID_16BIT; attr->type.uu.uuid16 = GATT_UUID_INCLUDE_SERVICE; attr->value_length = 2; attr->handle = p_event_data->discovery_result.discovery_data.included_service.handle; attr->value.include.included_service_handle = p_event_data->discovery_result.discovery_data.included_service.handle; attr->value.include.end_group_handle = p_event_data->discovery_result.discovery_data.included_service.e_handle; //start_handle = p_event_data->discovery_result.discovery_data.included_service.s_handle; memcpy( &attr->value.include.uuid, &p_event_data->discovery_result.discovery_data.included_service.service_type, sizeof(mico_bt_uuid_t) ); if ( smartbridge_subprocedure.attr_head == NULL ) { smartbridge_subprocedure.attr_head = attr; } if ( smartbridge_subprocedure.attr_tail != NULL ) { smartbridge_subprocedure.attr_tail->next = attr; } smartbridge_subprocedure.attr_tail = attr; smartbridge_subprocedure.attr_count++; //bt_smartbridge_log( "Included Service handle:%x [Start %x - End %x] uuid:%x", attr->handle, start_handle, attr->value.include.end_group_handle, attr->value.service.uuid.uu.uuid16 ); } } static void smartbridge_gatt_discover_characteristic_result( mico_bt_gatt_event_data_t *p_event_data ) { /* Create attribute(s) based on information included in the response PDU */ mico_bt_smart_attribute_t* attr; mico_bt_smart_attribute_create( &attr, MICO_ATTRIBUTE_TYPE_CHARACTERISTIC, 0 ); memcpy( ¤t_characteristic, &p_event_data->discovery_result.discovery_data.characteristic_declaration, sizeof( current_characteristic ) ); if ( attr != NULL ) { attr->next = NULL; attr->handle = current_characteristic.handle; attr->type.len = UUID_16BIT; attr->type.uu.uuid16 = GATT_UUID_CHAR_DECLARE; attr->value_length = 2; attr->value.characteristic.properties = current_characteristic.characteristic_properties; attr->value.characteristic.value_handle = current_characteristic.val_handle; attr->value.characteristic.descriptor_start_handle = attr->value.characteristic.value_handle + 1; /* FIXME: descriptor_end_handle need to be calculated correclty */ attr->value.characteristic.descriptor_end_handle = attr->value.characteristic.descriptor_start_handle; memcpy( &attr->value.characteristic.uuid, ¤t_characteristic.char_uuid, sizeof(mico_bt_uuid_t) ); if ( smartbridge_subprocedure.attr_head == NULL ) { smartbridge_subprocedure.attr_head = attr; } if ( smartbridge_subprocedure.attr_tail != NULL ) { smartbridge_subprocedure.attr_tail->next = attr; } smartbridge_subprocedure.attr_tail = attr; smartbridge_subprocedure.attr_count++; bt_smartbridge_log( "Characteristic value_handle:%x handle:%x uuid:%x list-count:%u properties:%u",current_characteristic.val_handle, current_characteristic.handle,current_characteristic.char_uuid.uu.uuid16, (unsigned int)smartbridge_subprocedure.attr_count, (int)attr->value.characteristic.properties ); } } static void smartbridge_gatt_discovery_complete_handler( mico_bt_gatt_event_data_t *p_event_data ) { uint16_t discovery_complete_type = p_event_data->discovery_complete.disc_type; switch( discovery_complete_type ) { case GATT_DISCOVER_SERVICES_ALL: case GATT_DISCOVER_SERVICES_BY_UUID: case GATT_DISCOVER_INCLUDED_SERVICES: case GATT_DISCOVER_CHARACTERISTICS: case GATT_DISCOVER_CHARACTERISTIC_DESCRIPTORS: if ( smartbridge_subprocedure.attr_count != 0 ) { smartbridge_subprocedure.result = MICO_BT_SUCCESS; } else { smartbridge_subprocedure.result = MICO_BT_ITEM_NOT_IN_LIST; } bt_smartbridge_log( "Discovery Completed ( result:%d type:%d )\r\n", smartbridge_subprocedure.result, discovery_complete_type ); subprocedure_notify_complete( &smartbridge_subprocedure ); break; default: bt_smartbridge_log( "Unhandled Discovery Completed ( type:%d )", discovery_complete_type ); break; } } mico_bt_gatt_status_t smartbridge_gatt_callback( mico_bt_gatt_evt_t event, mico_bt_gatt_event_data_t *p_event_data ) { mico_bt_gatt_status_t status = MICO_BT_GATT_SUCCESS; switch(event) { case GATT_CONNECTION_STATUS_EVT: { if( p_event_data->connection_status.link_role == BT_SMART_LINK_ROLE_MASTER) { /* Connection */ if ( p_event_data->connection_status.connected == MICO_TRUE ) { if( connecting_socket && memcmp(connecting_socket->remote_device.address, p_event_data->connection_status.bd_addr, BD_ADDR_LEN) == 0) { smartbridge_gatt_connection_handler( p_event_data->connection_status.conn_id ); } else { smartbridge_gatt_auto_conn_handler( p_event_data->connection_status.bd_addr, p_event_data->connection_status.conn_id ); } } else { /* Auto connection: drop a connecton object. */ int8_t idx = smartbridge_auto_conn_find_dev_by_addr(p_event_data->connection_status.bd_addr); if (idx >= 0) { g_auto_conn_dev_table[idx].socket = 0; } /* Common connection drop */ smartbridge_gatt_disconnection_handler( p_event_data->connection_status.conn_id ); } } break; } case GATT_ATTRIBUTE_REQUEST_EVT: { bt_smartbridge_log( "Gatt attribute status event" ); break; } case GATT_OPERATION_CPLT_EVT: { if(p_event_data != NULL ) { if ( p_event_data->operation_complete.op == GATTC_OPTYPE_READ ) { smartbridge_gatt_read_operation_complete_handler( &p_event_data->operation_complete.response_data.att_value ); subprocedure_notify_complete( &smartbridge_subprocedure ); } else if ( p_event_data->operation_complete.op == GATTC_OPTYPE_WRITE ) { bt_smartbridge_log( "Write-Callback event for handle:%x status:%d", p_event_data->operation_complete.response_data.handle, (unsigned int)p_event_data->operation_complete.status ); subprocedure_notify_complete( &smartbridge_subprocedure ); } else if ( p_event_data->operation_complete.op == GATTC_OPTYPE_NOTIFICATION || p_event_data->operation_complete.op == GATTC_OPTYPE_INDICATION ) { bt_smartbridge_log( "Notification/Indication Event"); smartbridge_gatt_notification_indication_handler( &p_event_data->operation_complete ); } } break; } case GATT_DISCOVERY_RESULT_EVT: { if( p_event_data != NULL ) { if ( p_event_data->discovery_result.discovery_type == GATT_DISCOVER_SERVICES_ALL || p_event_data->discovery_result.discovery_type == GATT_DISCOVER_SERVICES_BY_UUID ) { smartbridge_gatt_discover_services_result( p_event_data ); } else if ( p_event_data->discovery_result.discovery_type == GATT_DISCOVER_INCLUDED_SERVICES ) { smartbridge_gatt_discover_included_services_result( p_event_data ); } else if ( p_event_data->discovery_result.discovery_type == GATT_DISCOVER_CHARACTERISTICS ) { smartbridge_gatt_discover_characteristic_result( p_event_data); } else if ( p_event_data->discovery_result.discovery_type == GATT_DISCOVER_CHARACTERISTIC_DESCRIPTORS ) { smartbridge_gatt_discover_characteristic_descriptor_result( p_event_data ); } } break; } case GATT_DISCOVERY_CPLT_EVT: { if ( ( p_event_data != NULL ) ) { smartbridge_gatt_discovery_complete_handler( p_event_data ); } break; } default: { bt_smartbridge_log( "Gatt callback event:%d", event ); break; } } return status; } OSStatus mico_bt_smartbridge_init( uint8_t count ) { OSStatus result; if ( initialised == MICO_TRUE ) { return MICO_BT_SUCCESS; } bt_smartbridge_log( "Initialising MICO SmartBridge ..." ); /* Initialise internal settings for Auto Connection. */ memset((void *)g_auto_conn_dev_table, 0, sizeof(g_auto_conn_dev_table)); smartbridge_auto_conn_list_init(); result = mico_rtos_init_semaphore(&g_auto_conn_sem, 1); if (result != kNoErr) { bt_smartbridge_log( "Error initialising a semaphore used to Auto connection" ); return result; } /* Initialise SmartBridge Socket Manager */ result = bt_smartbridge_socket_manager_init(); if ( result != MICO_BT_SUCCESS ) { bt_smartbridge_log( "Error initialising SmartBridge Socket Manager" ); return result; } /* Set socket manager's connections limit */ bt_smartbridge_socket_manager_set_max_concurrent_connections( count ); /* Initialise bt stack operation interface */ smartbridge_bt_interface_initialize(); initialised = MICO_TRUE; return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_deinit( void ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SUCCESS; } /* Disable Attribute Cache (The function checks if it's enabled) */ bt_smartbridge_att_cache_disable(); /* Deinitialise socket manager */ bt_smartbridge_socket_manager_deinit(); /* Deinitialise GATT */ smartbridge_bt_interface_deinitialize(); /* Delete semaphore */ mico_rtos_deinit_semaphore(&g_auto_conn_sem); g_auto_conn_sem = NULL; /* Delete Auto-connection list. */ smartbridge_auto_conn_list_deinit(); initialised = MICO_FALSE; return MICO_BT_SUCCESS; } mico_bool_t mico_bt_smartbridge_is_scanning( void ) { return ( initialised == MICO_TRUE ) ? smartbridge_bt_interface_is_scanning( ) : MICO_FALSE; } mico_bool_t mico_bt_smartbridge_is_ready_to_connect( void ) { return ( initialised == MICO_FALSE || connecting_socket != NULL ) ? MICO_FALSE : MICO_TRUE; } OSStatus mico_bt_smartbridge_start_scan( const mico_bt_smart_scan_settings_t* settings, mico_bt_smart_scan_complete_callback_t complete_callback, mico_bt_smart_advertising_report_callback_t advertising_report_callback ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } return smartbridge_bt_interface_start_scan( settings, complete_callback, advertising_report_callback ); } OSStatus mico_bt_smartbridge_stop_scan( void ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } return smartbridge_bt_interface_stop_scan(); } OSStatus mico_bt_smartbridge_get_scan_result_list( mico_bt_smart_scan_result_t** result_list, uint32_t* count ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } return smartbridge_helper_get_scan_results( result_list, count ); } OSStatus mico_bt_smartbridge_get_background_connection_devices_size( uint8_t *size ) { if ( initialised == MICO_FALSE ) { return kNotInitializedErr; } if ( size == (uint8_t *)0 ) { return kGeneralErr; } return smartbridge_bt_interface_get_background_connection_device_size( size ); } OSStatus mico_bt_smartbridge_set_auto_connection_action( mico_bool_t start_stop, const mico_bt_smart_scan_settings_t *scan_settings, mico_bt_smartbridge_auto_connection_parms_cback_t p_auto_conn_cback ) { OSStatus err = kNoErr; uint32_t duration; if (start_stop && !scan_settings) return kParamErr; if (initialised == MICO_FALSE) return kNotInitializedErr; if (connecting_socket != NULL) return kInProgressErr; if (start_stop && g_auto_conn_cback) return kInProgressErr; /* Clear connection list */ smartbridge_auto_conn_list_clear(); if (start_stop) { /* Start a scanning procedure */ duration = scan_settings->duration_second; memcpy(&g_auto_conn_scan_cfg, scan_settings, sizeof(mico_bt_smart_scan_settings_t)); g_auto_conn_scan_cfg.filter_duplicates = DUPLICATES_FILTER_DISABLED; g_auto_conn_scan_cfg.filter_policy = FILTER_POLICY_WHITE_LIST; g_auto_conn_scan_cfg.duration_second = MIN(SMARTBRIDGE_AUTO_CONN_SCAN_INTERVAL, duration); err = mico_bt_smartbridge_start_scan(&g_auto_conn_scan_cfg, smartbridge_gap_auto_conn_scan_cmpl_handler, smartbridge_gap_auto_conn_scan_report_handler); if (err == MICO_BT_PENDING) { if (scan_settings->duration_second < (uint16_t)(-1)) { /* An limited timeout */ g_auto_conn_scan_duration = duration - MIN(SMARTBRIDGE_AUTO_CONN_SCAN_INTERVAL, duration); } else { /* Forever until user request to stop it. */ g_auto_conn_scan_duration = (uint16_t)(-1); } /* Selective Procedure Callback */ if (p_auto_conn_cback) g_auto_conn_cback = p_auto_conn_cback; err = kNoErr; } else { bt_smartbridge_log("%s: start scanning failed", __FUNCTION__); } } else { g_auto_conn_cback = NULL; memset(&g_auto_conn_scan_cfg, 0, sizeof(mico_bt_smart_scan_settings_t)); err = mico_bt_smartbridge_stop_scan(); } return err; } OSStatus mico_bt_smartbridge_create_socket( mico_bt_smartbridge_socket_t* socket ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Reset socket fields */ memset( socket, 0, sizeof( *socket ) ); socket->connection_handle = SOCKET_INVALID_CONNECTION_HANDLE; /* Point node data to socket */ socket->node.data = (void*)socket; /* Initialise socket semaphore */ return mico_rtos_init_semaphore( &socket->semaphore, 1 ); } OSStatus mico_bt_smartbridge_delete_socket( mico_bt_smartbridge_socket_t* socket ) { OSStatus result; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } result = mico_rtos_deinit_semaphore( &socket->semaphore ); if ( result != MICO_BT_SUCCESS ) { return result; } memset( socket, 0, sizeof( *socket ) ); socket->connection_handle = SOCKET_INVALID_CONNECTION_HANDLE; return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_get_socket_status( mico_bt_smartbridge_socket_t* socket, mico_bt_smartbridge_socket_status_t* status ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( socket->state == SOCKET_STATE_LINK_ENCRYPTED ) { *status = SMARTBRIDGE_SOCKET_CONNECTED; } else if ( socket->state == SOCKET_STATE_LINK_CONNECTED ) { /* Status is connected if socket does not have loaded bond info and does not initiate pairing */ if ( smartbridge_helper_socket_check_actions_disabled( socket, SOCKET_ACTION_ENCRYPT_USING_BOND_INFO | SOCKET_ACTION_INITIATE_PAIRING ) == MICO_TRUE ) { *status = SMARTBRIDGE_SOCKET_CONNECTED; } else { *status = SMARTBRIDGE_SOCKET_CONNECTING; } } else if ( socket->state == SOCKET_STATE_LINK_CONNECTING ) { *status = SMARTBRIDGE_SOCKET_CONNECTING; } else { *status = SMARTBRIDGE_SOCKET_DISCONNECTED; } return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_connect( mico_bt_smartbridge_socket_t* socket, const mico_bt_smart_device_t* remote_device, const mico_bt_smart_connection_settings_t* settings, mico_bt_smartbridge_disconnection_callback_t disconnection_callback, mico_bt_smartbridge_notification_callback_t notification_callback ) { mico_bt_smartbridge_socket_t* found_socket; OSStatus result = MICO_BT_SUCCESS; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( connecting_socket != NULL ) { /* Only 1 connecting socket is allowed */ return MICO_BT_CONNECT_IN_PROGRESS; } if ( bt_smartbridge_socket_manager_is_full() == MICO_TRUE ) { return MICO_BT_MAX_CONNECTIONS_REACHED; } if ( bt_smartbridge_socket_manager_find_socket_by_address( &remote_device->address, &found_socket ) == MICO_BT_SUCCESS ) { /* device is already connected */ return MICO_BT_SOCKET_IN_USE; } bt_smartbridge_log( "connect()...socket things are okay" ); /* Clean-up accidentally set semaphores */ while( mico_rtos_get_semaphore( &socket->semaphore, MICO_NO_WAIT ) == MICO_BT_SUCCESS ) { } /* Store socket pointer in a temporary global variable so it can be referenced in smartbridge_gap_connection_handler */ connecting_socket = socket; /* Store connection settings */ memcpy( &socket->connection_settings, settings, sizeof( *settings ) ); /* Store remote device information */ memcpy( &socket->remote_device, remote_device, sizeof( *remote_device ) ); /* Set callback functions */ socket->disconnection_callback = disconnection_callback; socket->notification_callback = notification_callback; /* Reset connection handle to invalid value */ socket->connection_handle = SOCKET_INVALID_CONNECTION_HANDLE; /* Reset state */ socket->state = SOCKET_STATE_DISCONNECTED; /* Set socket action to connecting */ smartbridge_helper_socket_set_actions( socket, SOCKET_ACTION_HOST_CONNECT ); smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_HOST_DISCONNECT ); /* Set attribute protocol timeout */ smartbridge_bt_interface_set_attribute_timeout( settings->attribute_protocol_timeout_ms ); if ( smartbridge_helper_socket_check_actions_enabled( socket, SOCKET_ACTION_INITIATE_PAIRING ) == MICO_TRUE ) { /* Tell GAP to initiate pairing on the next connection attempt */ mico_bt_start_pairing( socket->remote_device.address, socket->remote_device.address_type, &socket->security_settings ); } else { /* Tell GAP to not send pairing request on the next connection attempt */ mico_bt_stop_pairing( socket->remote_device.address ); } smartbridge_bt_interface_connect( remote_device, settings, disconnection_callback, notification_callback ); /* Wait for connection */ mico_rtos_get_semaphore( &socket->semaphore, socket->connection_settings.timeout_second * 1000 ); /* Check if link is connected. Otherwise, return error */ if ( socket->state == SOCKET_STATE_LINK_CONNECTED ) { if ( smartbridge_helper_socket_check_actions_enabled( socket, SOCKET_ACTION_INITIATE_PAIRING ) == MICO_TRUE ) { /* Wait until pairing is complete */ mico_rtos_get_semaphore( &socket->semaphore, MICO_NEVER_TIMEOUT ); } /* Check if encryption is required */ if ( smartbridge_helper_socket_check_actions_enabled( socket, SOCKET_ACTION_INITIATE_PAIRING ) == MICO_TRUE || smartbridge_helper_socket_check_actions_enabled( socket, SOCKET_ACTION_ENCRYPT_USING_BOND_INFO ) == MICO_TRUE ) { mico_bt_start_encryption( &socket->remote_device.address ); /* Wait until link is encrypted */ mico_rtos_get_semaphore( &socket->semaphore, MICO_NEVER_TIMEOUT ); if ( socket->state != SOCKET_STATE_LINK_ENCRYPTED ) { result = MICO_BT_ENCRYPTION_FAILED; mico_bt_dev_delete_bonded_device( (uint8_t *)remote_device->address ); goto error; } } } else { result = MICO_BT_SOCKET_NOT_CONNECTED; goto error; } /* Successful */ if ( bt_smartbridge_att_cache_is_enabled() == MICO_TRUE ) { bt_smartbridge_att_cache_t* cache = NULL; result = bt_smartbridge_att_cache_find( remote_device, &cache ); if ( result == MICO_BT_SUCCESS ) { bt_smartbridge_log( "USING ATT CACHE ..." ); } else { bt_smartbridge_log( "GENERATING ATT CACHE ..." ); result = bt_smartbridge_att_cache_generate( remote_device, socket->connection_handle, &cache ); if ( result != MICO_BT_SUCCESS ) { bt_smartbridge_log( "Error Generating Cache result:%d", result ); goto error; } } /* Successful. Mark cache as active and store reference in socket */ bt_smartbridge_att_cache_set_active_state( cache, MICO_TRUE ); socket->att_cache = (void*)cache; } /* Add this device to the white list. */ if (connecting_socket->connection_settings.filter_policy == FILTER_POLICY_WHITE_LIST) { if (connecting_socket->remote_device.address_type == BT_SMART_ADDR_TYPE_PUBLIC) { result = smartbridge_bt_interface_update_background_connection_device(TRUE, connecting_socket->remote_device.address); if (result != kNoErr) { bt_smartbridge_log("Add device to white list unsuccessfully, status: %d", result); } } else { bt_smartbridge_log("Cannot add device to white list: random address type"); } } /* Clear connect action as it's no longer needed */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_HOST_CONNECT ); /* Reset connecting socket pointer */ connecting_socket = NULL; /* Link is connected. Return success */ return result; #if 1 error: /* Link is not connected nor encrypted. Issue disconnection attempt to clean-up */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_INITIATE_PAIRING|SOCKET_ACTION_ENCRYPT_USING_BOND_INFO ); mico_rtos_delay_milliseconds(200); //A quick disconnection from an established connection may cause a duplicated gatt connected callback, by william mico_bt_smartbridge_disconnect( socket, TRUE ); /* Clear connect action as it's no longer needed */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_HOST_CONNECT ); /* Reset connecting socket pointer */ connecting_socket = NULL; /* Link is not connected. Return error */ return result; #endif } OSStatus mico_bt_smartbridge_disconnect( mico_bt_smartbridge_socket_t* socket, mico_bool_t remove_it_from_whitelist ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Mark disconnection flag that it's coming from the host */ smartbridge_helper_socket_set_actions( socket, SOCKET_ACTION_HOST_DISCONNECT ); /* Clean-up accidentally set semaphores */ while( mico_rtos_get_semaphore( &socket->semaphore, MICO_NO_WAIT ) == MICO_BT_SUCCESS ) { } /* Check if either link is encrypted or connected */ if ( socket->state >= SOCKET_STATE_LINK_CONNECTED ) { smartbridge_bt_interface_disconnect( socket->connection_handle ); /* Wait for disconnection */ mico_rtos_get_semaphore( &socket->semaphore, socket->connection_settings.timeout_second * 1000 ); } else { /* Link is not yet connected. Cancel last */ smartbridge_bt_interface_cancel_last_connect( socket->remote_device.address ); } /* Clear socket disconnect action */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_HOST_DISCONNECT ); /* Proper clean-up if socket isn't properly disconnected */ if ( socket->state != SOCKET_STATE_DISCONNECTED ) { mico_bt_smartbridge_socket_t* removed_socket; bt_smartbridge_socket_manager_remove_socket( socket->connection_handle, &removed_socket ); /* Reset connection handle to invalid value */ socket->connection_handle = SOCKET_INVALID_CONNECTION_HANDLE; /* Clear socket state */ socket->state = SOCKET_STATE_DISCONNECTED; /* Mark att cache as inactive and reset reference to cache */ bt_smartbridge_att_cache_set_active_state( (bt_smartbridge_att_cache_t*)socket->att_cache, MICO_FALSE ); socket->att_cache = NULL; } /* Remove this device from the white list */ if (remove_it_from_whitelist) { smartbridge_bt_interface_update_background_connection_device(FALSE, socket->remote_device.address); } return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_set_transmit_power( mico_bt_smartbridge_socket_t* socket, int8_t transmit_power_dbm ) { if ( initialised == MICO_FALSE || socket->state == SOCKET_STATE_DISCONNECTED ) { return MICO_BT_SMART_APPL_UNINITIALISED; } return smartbridge_bt_interface_set_connection_tx_power( socket->connection_handle, transmit_power_dbm ); } OSStatus mico_bt_smartbridge_set_bond_info( mico_bt_smartbridge_socket_t* socket, const mico_bt_smart_security_settings_t* settings, const mico_bt_smart_bond_info_t* bond_info ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Set local copies of security settings and bond info */ memcpy( &socket->bond_info, bond_info, sizeof( *bond_info ) ); memcpy( &socket->security_settings, settings, sizeof( *settings ) ); /* Clear socket action to initiate pairing request */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_INITIATE_PAIRING ); /* Set socket action to encrypt using loaded bond info */ smartbridge_helper_socket_set_actions( socket, SOCKET_ACTION_ENCRYPT_USING_BOND_INFO ); return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_clear_bond_info( mico_bt_smartbridge_socket_t* socket ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Reset bond info */ memset( &socket->bond_info, 0, sizeof( socket->bond_info ) ); /* Clear socket action to encrypt using loaded bond info */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_ENCRYPT_USING_BOND_INFO ); return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_enable_pairing( mico_bt_smartbridge_socket_t* socket, const mico_bt_smart_security_settings_t* settings, mico_bt_smart_bonding_callback_t bonding_callback ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Store security settings in local copy */ memcpy( &socket->security_settings, settings, sizeof( *settings ) ); /* Reset bond info */ memset( &socket->bond_info, 0, sizeof( socket->bond_info ) ); /* Set pairing callback */ socket->bonding_callback = bonding_callback; /* Clear socket action to encrypt using loaded bond info */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_ENCRYPT_USING_BOND_INFO ); /* Set socket action to iniatiate pairing request */ smartbridge_helper_socket_set_actions( socket, SOCKET_ACTION_INITIATE_PAIRING ); return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_disable_pairing( mico_bt_smartbridge_socket_t* socket ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Clear socket action to iniatiate pairing request */ smartbridge_helper_socket_clear_actions( socket, SOCKET_ACTION_INITIATE_PAIRING ); return MICO_BT_SUCCESS; } OSStatus mico_bt_smartbridge_enable_attribute_cache( uint32_t cache_count, mico_bt_uuid_t cache_services[], uint32_t service_count ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Call internal function */ return bt_smartbridge_att_cache_enable( cache_count, cache_services, service_count ); } OSStatus mico_bt_smartbridge_disable_attribute_cache( void ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Call internal function */ return bt_smartbridge_att_cache_disable(); } OSStatus mico_bt_smartbridge_remove_attribute_cache( mico_bt_smartbridge_socket_t* socket ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } /* Call internal function */ return bt_smartbridge_att_cache_release( socket->att_cache ); } OSStatus mico_bt_smartbridge_enable_attribute_cache_notification( mico_bt_smartbridge_socket_t* socket, mico_bool_t is_notification_or_indication ) { mico_bt_smart_attribute_list_t* list; mico_bt_smart_attribute_t* iterator; bt_smartbridge_att_cache_t* cache; OSStatus result; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( bt_smartbridge_att_cache_is_enabled( ) == MICO_FALSE ) { return MICO_BT_ATT_CACHE_UNINITIALISED; } if ( socket == NULL || socket->att_cache == NULL ) { return MICO_BT_BADARG; } cache = (bt_smartbridge_att_cache_t*)socket->att_cache; if ( bt_smartbridge_att_cache_is_discovering( cache ) == MICO_TRUE ) { return MICO_BT_DISCOVER_IN_PROGRESS; } result = bt_smartbridge_att_cache_get_list( cache, &list ); if ( result != MICO_BT_SUCCESS ) { return result; } result = mico_bt_smart_attribute_get_list_head( list, &iterator ); if ( result != MICO_BT_SUCCESS ) { return result; } while( iterator != NULL ) { if ( iterator->type.uu.uuid16 == 0x2902 ) { /* Switch on notification from server */ bt_smartbridge_att_cache_lock( cache ); if (is_notification_or_indication) iterator->value.client_config.config_bits = 0x0001; // Notification else iterator->value.client_config.config_bits = 0x0002; // Indication bt_smartbridge_att_cache_unlock( cache ); result = smartbridge_bt_interface_write_characteristic_descriptor( socket->connection_handle, iterator ); } iterator = iterator->next; } return result; } OSStatus mico_bt_smartbridge_disable_attribute_cache_notification( mico_bt_smartbridge_socket_t* socket ) { mico_bt_smart_attribute_list_t* list; mico_bt_smart_attribute_t* iterator; bt_smartbridge_att_cache_t* cache; OSStatus result; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( bt_smartbridge_att_cache_is_enabled( ) == MICO_FALSE ) { return MICO_BT_ATT_CACHE_UNINITIALISED; } if ( socket == NULL || socket->att_cache == NULL ) { return MICO_BT_BADARG; } cache = (bt_smartbridge_att_cache_t*)socket->att_cache; if ( bt_smartbridge_att_cache_is_discovering( cache ) == MICO_TRUE ) { return MICO_BT_DISCOVER_IN_PROGRESS; } result = bt_smartbridge_att_cache_get_list( cache, &list ); if ( result != MICO_BT_SUCCESS ) { return result; } result = mico_bt_smart_attribute_get_list_head( list, &iterator ); if ( result != MICO_BT_SUCCESS ) { return result; } while( iterator != NULL ) { if ( iterator->type.uu.uuid16 == 0x2902 ) { /* Swith on notification from server */ bt_smartbridge_att_cache_lock( cache ); iterator->value.client_config.config_bits = 0; bt_smartbridge_att_cache_unlock( cache ); result = smartbridge_bt_interface_write_characteristic_descriptor( socket->connection_handle, iterator ); } iterator = iterator->next; } return result; } OSStatus mico_bt_smartbridge_get_attribute_cache_list( mico_bt_smartbridge_socket_t* socket, mico_bt_smart_attribute_list_t** att_cache_list ) { if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( bt_smartbridge_att_cache_is_enabled( ) == MICO_FALSE ) { return MICO_BT_ATT_CACHE_UNINITIALISED; } if ( socket == NULL || socket->att_cache == NULL || att_cache_list == NULL ) { return MICO_BT_BADARG; } if ( bt_smartbridge_att_cache_is_discovering( (bt_smartbridge_att_cache_t*)socket->att_cache ) == MICO_TRUE ) { return MICO_BT_DISCOVER_IN_PROGRESS; } /* Call internal function */ return bt_smartbridge_att_cache_get_list( (bt_smartbridge_att_cache_t*)socket->att_cache, att_cache_list ); } OSStatus mico_bt_smartbridge_get_attribute_cache_by_handle( mico_bt_smartbridge_socket_t* socket, uint16_t handle, mico_bt_smart_attribute_t* attribute, uint16_t size ) { bt_smartbridge_att_cache_t* cache = NULL; mico_bt_smart_attribute_list_t* att_cache_list = NULL; mico_bt_smart_attribute_t* att = NULL; OSStatus result; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( bt_smartbridge_att_cache_is_enabled( ) == MICO_FALSE ) { return MICO_BT_ATT_CACHE_UNINITIALISED; } if ( socket == NULL || socket->att_cache == NULL || attribute == NULL ) { return MICO_BT_BADARG; } cache = (bt_smartbridge_att_cache_t*)socket->att_cache; if ( bt_smartbridge_att_cache_is_discovering( cache ) == MICO_TRUE ) { return MICO_BT_DISCOVER_IN_PROGRESS; } bt_smartbridge_att_cache_get_list( cache, &att_cache_list ); bt_smartbridge_att_cache_lock( cache ); result = mico_bt_smart_attribute_search_list_by_handle( att_cache_list, handle, &att ); if ( result == MICO_BT_SUCCESS ) { if ( att->value_struct_size + ATTR_COMMON_FIELDS_SIZE > size ) { result = MICO_BT_ATTRIBUTE_VALUE_TOO_LONG; } else { memcpy( attribute, att, att->value_struct_size + ATTR_COMMON_FIELDS_SIZE ); } } bt_smartbridge_att_cache_unlock( cache ); return result; } OSStatus mico_bt_smartbridge_get_attribute_cache_by_uuid( mico_bt_smartbridge_socket_t* socket, const mico_bt_uuid_t* uuid, uint16_t starting_handle, uint16_t ending_handle, mico_bt_smart_attribute_t* attribute, uint32_t size ) { bt_smartbridge_att_cache_t* cache = NULL; mico_bt_smart_attribute_list_t* att_cache_list = NULL; mico_bt_smart_attribute_t* att = NULL; OSStatus result; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( bt_smartbridge_att_cache_is_enabled( ) == MICO_FALSE ) { return MICO_BT_ATT_CACHE_UNINITIALISED; } if ( socket == NULL || socket->att_cache == NULL || uuid == NULL || attribute == NULL ) { return MICO_BT_BADARG; } cache = (bt_smartbridge_att_cache_t*)socket->att_cache; if ( bt_smartbridge_att_cache_is_discovering( cache ) == MICO_TRUE ) { return MICO_BT_DISCOVER_IN_PROGRESS; } bt_smartbridge_att_cache_get_list( cache, &att_cache_list ); bt_smartbridge_att_cache_lock( cache ); result = mico_bt_smart_attribute_search_list_by_uuid( att_cache_list, uuid, starting_handle, ending_handle, &att ); if ( result == MICO_BT_SUCCESS ) { if ( att->value_struct_size + ATTR_COMMON_FIELDS_SIZE > size ) { result = MICO_BT_ATTRIBUTE_VALUE_TOO_LONG; } else { memcpy( attribute, att, att->value_struct_size + ATTR_COMMON_FIELDS_SIZE ); } } bt_smartbridge_att_cache_unlock( cache ); return result; } OSStatus mico_bt_smartbridge_get_service_from_attribute_cache_by_uuid( mico_bt_smartbridge_socket_t* socket, const mico_bt_uuid_t* uuid, uint16_t starting_handle, uint16_t ending_handle, mico_bt_smart_attribute_t* attribute, uint32_t size ) { OSStatus result; uint16_t _starting_handle = starting_handle; uint16_t _ending_handle = ending_handle; mico_bt_uuid_t service_uuid = { .len = LEN_UUID_16, .uu.uuid16 = GATT_UUID_PRI_SERVICE }; /* Find service */ while( ( result = mico_bt_smartbridge_get_attribute_cache_by_uuid( socket, &service_uuid, _starting_handle, _ending_handle, attribute, size ) )== MICO_BT_SUCCESS ) { if( memcmp( (void *)&attribute->value.service.uuid.uu, &uuid->uu, uuid->len ) == 0 ) { return MICO_BT_SUCCESS; } /* Fixed a bug for a service without characteristics. */ if (attribute->value.service.end_handle > attribute->value.service.start_handle) { _starting_handle = attribute->value.service.end_handle; } else { _starting_handle = attribute->handle + 1; } } return result; } OSStatus mico_bt_smartbridge_get_characteritics_from_attribute_cache_by_uuid( mico_bt_smartbridge_socket_t* socket, const mico_bt_uuid_t* uuid, uint16_t starting_handle, uint16_t ending_handle, mico_bt_smart_attribute_t* attribute, uint32_t size ) { OSStatus result; uint16_t _starting_handle = starting_handle; uint16_t _ending_handle = ending_handle; mico_bt_uuid_t characteristic_uuid = { .len = LEN_UUID_16, .uu.uuid16 = GATT_UUID_CHAR_DECLARE }; /* Find service */ while( ( result = mico_bt_smartbridge_get_attribute_cache_by_uuid( socket, &characteristic_uuid, _starting_handle, _ending_handle, attribute, size ) )== MICO_BT_SUCCESS ) { if( memcmp( (void *)&attribute->value.characteristic.uuid.uu, &uuid->uu, uuid->len ) == 0 ) { return MICO_BT_SUCCESS; } _starting_handle = attribute->handle + 1; } return result; } OSStatus mico_bt_smartbridge_refresh_attribute_cache_characteristic_value( mico_bt_smartbridge_socket_t* socket, uint16_t handle ) { bt_smartbridge_att_cache_t* cache = NULL; mico_bt_smart_attribute_list_t* att_cache_list = NULL; mico_bt_smart_attribute_t* current_att = NULL; mico_bt_smart_attribute_t* refreshed_att = NULL; OSStatus result; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( bt_smartbridge_att_cache_is_enabled( ) == MICO_FALSE ) { return MICO_BT_ATT_CACHE_UNINITIALISED; } if ( socket == NULL || socket->att_cache == NULL ) { return MICO_BT_BADARG; } cache = (bt_smartbridge_att_cache_t*)socket->att_cache; if ( bt_smartbridge_att_cache_is_discovering( cache ) == MICO_TRUE ) { return MICO_BT_DISCOVER_IN_PROGRESS; } bt_smartbridge_att_cache_get_list( cache, &att_cache_list ); bt_smartbridge_att_cache_lock( cache ); result = mico_bt_smart_attribute_search_list_by_handle( att_cache_list, handle, ¤t_att ); if ( result == MICO_BT_SUCCESS ) { /* Check if length is longer than what read characteristic value can handle * If longer, use read long characteristic value */ if ( current_att->value_length <= ATT_STANDARD_VALUE_LENGTH ) { result = smartbridge_bt_interface_read_characteristic_value( socket->connection_handle, current_att->handle, ¤t_att->type, &refreshed_att ); } else { result = smartbridge_bt_interface_read_long_characteristic_value( socket->connection_handle, current_att->handle, ¤t_att->type, &refreshed_att ); } /* If read is successful, replace attribute with the refreshed one */ if ( result == MICO_BT_SUCCESS ) { /* This function removes and also deletes the attribute with handle specified */ result = mico_bt_smart_attribute_remove_from_list( att_cache_list, current_att->handle ); if ( result == MICO_BT_SUCCESS ) { result = mico_bt_smart_attribute_add_to_list( att_cache_list, refreshed_att ); } } } bt_smartbridge_att_cache_unlock( cache ); return result; } OSStatus mico_bt_smartbridge_write_attribute_cache_characteristic_value( mico_bt_smartbridge_socket_t* socket, const mico_bt_smart_attribute_t* char_value ) { bt_smartbridge_att_cache_t* cache = NULL; mico_bt_smart_attribute_list_t* att_cache_list = NULL; mico_bt_smart_attribute_t* att = NULL; OSStatus result; if ( initialised == MICO_FALSE ) { return MICO_BT_SMART_APPL_UNINITIALISED; } if ( bt_smartbridge_att_cache_is_enabled( ) == MICO_FALSE ) { return MICO_BT_ATT_CACHE_UNINITIALISED; } if ( socket == NULL || socket->att_cache == NULL || char_value == NULL ) { return MICO_BT_BADARG; } cache = (bt_smartbridge_att_cache_t*)socket->att_cache; if ( bt_smartbridge_att_cache_is_discovering( cache ) == MICO_TRUE ) { return MICO_BT_DISCOVER_IN_PROGRESS; } bt_smartbridge_att_cache_get_list( cache, &att_cache_list ); if ( char_value->value_length <= ATT_STANDARD_VALUE_LENGTH ) { result = smartbridge_bt_interface_write_characteristic_value( socket->connection_handle, (mico_bt_smart_attribute_t*)char_value ); } else { result = smartbridge_bt_interface_write_long_characteristic_value( socket->connection_handle, (mico_bt_smart_attribute_t*)char_value ); } if ( result != MICO_BT_SUCCESS ) { return result; } bt_smartbridge_att_cache_lock( cache ); /* Find characteristic value in local attribute list. Add to the list if not found */ result = mico_bt_smart_attribute_search_list_by_handle( att_cache_list, char_value->handle, &att ); if ( result == MICO_BT_SUCCESS ) { /* Found. Compare lengths first. * If new length is not equal old length, replace old attribute with new one. * If equal, copy content directly. */ if ( char_value->value_length != att->value_length ) { result = mico_bt_smart_attribute_remove_from_list( att_cache_list, att->handle ); if ( result != MICO_BT_SUCCESS ) { goto exit; } att = NULL; /* Reuse attribute pointer */ if ( result != MICO_BT_SUCCESS ) { goto exit; } result = mico_bt_smart_attribute_create( &att, MICO_ATTRIBUTE_TYPE_CHARACTERISTIC_VALUE, char_value->value_length ); if ( result != MICO_BT_SUCCESS ) { goto exit; } att->handle = char_value->handle; att->type = char_value->type; att->value_length = char_value->value_length; att->value_struct_size = char_value->value_struct_size; memcpy( att->value.value, char_value->value.value, char_value->value_length ); result = mico_bt_smart_attribute_add_to_list( att_cache_list, att ); } else { memcpy( att->value.value, char_value->value.value, char_value->value_length ); } } else if ( result == MICO_BT_ITEM_NOT_IN_LIST ) { /* Not found. Create new one and add attribute to the list */ result = mico_bt_smart_attribute_create( &att, MICO_ATTRIBUTE_TYPE_CHARACTERISTIC_VALUE, char_value->value_length ); if ( result != MICO_BT_SUCCESS ) { goto exit; } att->handle = char_value->handle; att->type = char_value->type; att->value_length = char_value->value_length; att->value_struct_size = char_value->value_struct_size; memcpy( att->value.value, char_value->value.value, char_value->value_length ); result = mico_bt_smart_attribute_add_to_list( att_cache_list, att ); } exit: bt_smartbridge_att_cache_unlock( cache ); return result; } /****************************************************** * Callback Definitions ******************************************************/ static OSStatus smartbridge_app_notification_handler( void* arg ) { mico_bt_smartbridge_socket_t* socket = (mico_bt_smartbridge_socket_t*)arg; if ( socket != NULL && socket->notification_callback != NULL ) { socket->notification_callback( socket, socket->last_notified_attribute_handle ); return MICO_BT_SUCCESS; } return MICO_BT_ERROR; } static OSStatus smartbridge_app_disconnection_handler( void* arg ) { mico_bt_smartbridge_socket_t* socket = (mico_bt_smartbridge_socket_t*)arg; if ( socket != NULL && socket->disconnection_callback != NULL ) { socket->disconnection_callback( socket ); return MICO_BT_SUCCESS; } return MICO_BT_ERROR; } // OSStatus mico_bt_smartbridge_bond_info_update( mico_bt_device_link_keys_t paired_device_keys ) // { // mico_bt_smart_bond_info_t bond_info; // mico_bt_ble_keys_t *le_security_keys; // mico_bt_device_sec_keys_t *security_keys; // OSStatus result = MICO_BT_ERROR; // memcpy(bond_info.peer_address, paired_device_keys.bd_addr, BD_ADDR_LEN ); // security_keys = (mico_bt_device_sec_keys_t*)&paired_device_keys.key_data; // if( security_keys == NULL ) // { // WPRINT_LIB_ERROR( ("[SmartBridge] Security Keys is NULL\n")); // return MICO_BT_ERROR; // } // le_security_keys = ( mico_bt_ble_keys_t* )&security_keys->le_keys; // if ( le_security_keys == NULL ) // { // WPRINT_LIB_ERROR( ("[SmartBridge] Security LE-Keys is NULL\n")); // return MICO_BT_ERROR; // } // bond_info.address_type = (mico_bt_smart_address_type_t)security_keys->ble_addr_type; // bond_info.ediv = le_security_keys->ediv; // /* fill bond_info structure as expected by the application */ // memcpy( bond_info.irk, le_security_keys->irk, 16 ); // memcpy( bond_info.csrk, le_security_keys->pcsrk, 16 ); // memcpy( bond_info.ltk, le_security_keys->pltk, 16 ); // memcpy( bond_info.rand, le_security_keys->rand, 8 ); // result = smartbridge_gap_bonding_handler( connecting_socket->connection_handle, &bond_info ); // WPRINT_LIB_INFO( ( "[SmartBridge] Bond-Info updated result: %u\n", (unsigned int) result ) ); // return result; // } // static OSStatus smartbridge_gap_bonding_handler( uint16_t connection_handle, const mico_bt_smart_bond_info_t* bond_info ) // { // mico_bt_smartbridge_socket_t* socket; // if ( bt_smartbridge_socket_manager_find_socket_by_handle( connection_handle, &socket ) == MICO_BT_SUCCESS ) // { // /* Bonding successful. Update socket's bond info and post callback to MICO_BT_EVT_WORKER_THREAD */ // memcpy( &socket->bond_info, bond_info, sizeof( *bond_info ) ); // if ( socket != NULL && socket->bonding_callback != NULL ) // { // mico_rtos_send_asynchronous_event( MICO_BT_EVT_WORKER_THREAD, smartbridge_app_pairing_handler, (void*)socket ); // } // return MICO_BT_SUCCESS; // } // return MICO_BT_ERROR; // } // static OSStatus smartbridge_app_pairing_handler( void* arg ) // { // mico_bt_smartbridge_socket_t* socket = (mico_bt_smartbridge_socket_t*)arg; // if ( socket != NULL && socket->bonding_callback != NULL ) // { // socket->bonding_callback( socket, &socket->bond_info ); // return MICO_BT_SUCCESS; // } // return MICO_BT_ERROR; // } /* Auto-Connection Connecting Event Handler */ static OSStatus smartbridge_gap_auto_conn_asyn_event_handler(void *arg) { OSStatus err = kNoErr; mico_bt_gatt_status_t gatt_status; mico_bt_smart_advertising_report_t *report = NULL; /* Find a scanned device to create a connection. */ err = smartbridge_auto_conn_list_get_by_state(&report, SMARTBRIDGE_AUTO_CONN_STATE_SCANED); if (err == kNoErr) { /* Update current state */ smartbridge_auto_conn_list_set_state(report->remote_device.address, SMARTBRIDGE_AUTO_CONN_STATE_REPORTED); /* Check user configuration */ if (smartbridge_gap_auto_conn_user_cfg(report->remote_device.address, (uint8_t *)report->remote_device.name, report->eir_data, report->eir_data_length)) { /* Get socket */ int8_t idx = smartbridge_auto_conn_find_dev_by_addr(report->remote_device.address); mico_bt_smartbridge_socket_t *p_socket = g_auto_conn_dev_table[idx].socket; /* Clear semaphore */ while (mico_rtos_get_semaphore(&g_auto_conn_sem, MICO_NO_WAIT) == kNoErr); /* Update current state */ smartbridge_auto_conn_list_set_state(report->remote_device.address, SMARTBRIDGE_AUTO_CONN_STATE_CONNING); /* Update Socket State */ p_socket->state = SOCKET_STATE_LINK_CONNECTING; smartbridge_helper_socket_clear_actions(p_socket, SOCKET_ACTION_HOST_DISCONNECT); smartbridge_helper_socket_clear_actions(p_socket, SOCKET_ACTION_HOST_CONNECT); /* Start to create a connection */ gatt_status = mico_bt_gatt_le_connect((uint8_t *)report->remote_device.address, report->remote_device.address_type, BLE_CONN_MODE_HIGH_DUTY, MICO_TRUE); if (!gatt_status) { bt_smartbridge_log("%s: Create LE Connection failed", __FUNCTION__); err = kConnectionErr; goto err_exit; } /* Wait for completion */ err = mico_rtos_get_semaphore(&g_auto_conn_sem, 5000); if (err != kNoErr) { bt_smartbridge_log("%s: Create LE Connection failed: %s", __FUNCTION__, err == kTimeoutErr ? "Timeout" : "Unknown Error"); err_exit: p_socket->state = SOCKET_STATE_DISCONNECTED; smartbridge_auto_conn_list_set_state(report->remote_device.address, SMARTBRIDGE_AUTO_CONN_STATE_FREE); mico_rtos_send_asynchronous_event(MICO_BT_EVT_WORKER_THREAD, (event_handler_t)p_socket->auto_connection_callback, (void *)p_socket); } else { bt_smartbridge_log("%s: Craete LE Connection succesfully", __FUNCTION__); /* Start encryption and cache */ smartbridge_gap_auto_conn_cryption((void*)p_socket); /* Create connection successfully and remove it from the list. */ smartbridge_auto_conn_list_remove(report->remote_device.address); /* Notify user */ mico_rtos_send_asynchronous_event(MICO_BT_EVT_WORKER_THREAD, (event_handler_t)p_socket->auto_connection_callback, (void *)p_socket); } /* Next Connection */ mico_rtos_send_asynchronous_event(MICO_BT_WORKER_THREAD, smartbridge_gap_auto_conn_asyn_event_handler, NULL); } else { /* Update current state */ smartbridge_auto_conn_list_set_state(report->remote_device.address, SMARTBRIDGE_AUTO_CONN_STATE_IDLE); } } return err; } /* Auto-Connection Scanning Procedure Report Handler */ static OSStatus smartbridge_gap_auto_conn_scan_report_handler(const mico_bt_smart_advertising_report_t *report) { OSStatus err = kNoErr; mico_bt_smart_advertising_report_t *match_report = NULL; bt_smartbridge_log("%s: scan result: %s[%02x:%02x:%02x:%02x:%02x:%02x]", __FUNCTION__, report->remote_device.name, report->remote_device.address[0], report->remote_device.address[1], report->remote_device.address[2], report->remote_device.address[3], report->remote_device.address[4], report->remote_device.address[5]); /* Add it to the list. */ err = smartbridge_auto_conn_list_add(report); if (err != kNoErr) { //bt_smartbridge_log("%s: add list failed", __FUNCTION__); goto exit; } smartbridge_auto_conn_list_set_state(report->remote_device.address, SMARTBRIDGE_AUTO_CONN_STATE_SCANED); /* Check current state. */ err = smartbridge_auto_conn_list_get_by_state(&match_report, SMARTBRIDGE_AUTO_CONN_STATE_CONNING); if (err == kNoErr) { bt_smartbridge_log("%s: A connection is busy!", __FUNCTION__); /* It is busy for Auto-Connection Procedure and we should wait. */ } else { /* Create connection */ mico_rtos_send_asynchronous_event(MICO_BT_WORKER_THREAD, smartbridge_gap_auto_conn_asyn_event_handler, NULL); } exit: return err; } /* Auto-Connection Scanning Procedure Completion Handler */ static OSStatus smartbridge_gap_auto_conn_scan_cmpl_handler(void *arg) { OSStatus err = kNoErr; UNUSED_PARAMETER(arg); /* Clear some IDLE report. */ smartbridge_auto_conn_list_remove_by_state(SMARTBRIDGE_AUTO_CONN_STATE_IDLE); /* Restart scanning procedure... */ if (g_auto_conn_scan_duration > 0) { bt_smartbridge_log("restart scanning for auto-connection"); g_auto_conn_scan_cfg.duration_second = MIN(SMARTBRIDGE_AUTO_CONN_SCAN_INTERVAL, g_auto_conn_scan_duration); err = mico_bt_smartbridge_start_scan(&g_auto_conn_scan_cfg, smartbridge_gap_auto_conn_scan_cmpl_handler, smartbridge_gap_auto_conn_scan_report_handler); /* Update remain duration */ if (err == MICO_BT_PENDING && g_auto_conn_scan_duration < (uint16_t)(-1)) { g_auto_conn_scan_duration -= MIN(SMARTBRIDGE_AUTO_CONN_SCAN_INTERVAL, g_auto_conn_scan_duration); } } else { bt_smartbridge_log("scanning for auto-connection finished"); } return err; } static OSStatus smartbridge_gap_auto_conn_cryption( void *arg ) { OSStatus result = kNoErr; mico_bt_smartbridge_socket_t *socket = (mico_bt_smartbridge_socket_t *)arg; if (socket != NULL && socket->auto_connection_callback != NULL) { if (socket->security_settings.authentication_requirements != BT_SMART_AUTH_REQ_NONE) { /* Encryption */ bt_smartbridge_log("Encryption started."); mico_bt_start_encryption( &socket->remote_device.address ); mico_rtos_get_semaphore(&g_auto_conn_sem, MICO_NEVER_TIMEOUT); socket->state = SOCKET_STATE_LINK_ENCRYPTED; bt_smartbridge_log("Encryption completed."); } /* Cache if enabled. */ if (bt_smartbridge_att_cache_is_enabled() == MICO_TRUE) { bt_smartbridge_log("ATT Cache for an auto connection."); bt_smartbridge_att_cache_t* cache = NULL; result = bt_smartbridge_att_cache_find(&socket->remote_device, &cache); if (result == MICO_BT_SUCCESS) { bt_smartbridge_log("USING ATT CACHE ..."); } else { bt_smartbridge_log("GENERATING ATT CACHE ..."); result = bt_smartbridge_att_cache_generate(&socket->remote_device, socket->connection_handle, &cache); if (result != MICO_BT_SUCCESS) { bt_smartbridge_log("Error Generating Cache result:%d", result ); goto error; } } /* Successful. Mark cache as active and store reference in socket */ bt_smartbridge_att_cache_set_active_state(cache, MICO_TRUE); socket->att_cache = (void*)cache; } /* Notify user */ return result; } error: return MICO_BT_ERROR; } static mico_bool_t smartbridge_gap_auto_conn_user_cfg( const mico_bt_device_address_t device_address, const uint8_t *device_name, const uint8_t *p_data, uint8_t length ) { extern mico_bt_cfg_settings_t mico_bt_cfg_settings; int8_t idx; smartbridge_auto_conn_dev_info_t *device_info = NULL; /* Allocate an entity for storing this connection object. */ if ((idx = smartbridge_auto_conn_find_dev_by_addr(device_address)) < 0) { bt_smartbridge_log("Device is not Existed!!"); idx = smartbridge_auto_conn_dev_alloc(); require_string(idx >= 0, exit, "No resource for the auto connection"); } /* Prepare Device Information Structure */ device_info = &g_auto_conn_dev_table[idx].device_info; memset(device_info, 0, sizeof(smartbridge_auto_conn_dev_info_t)); device_info->handle = (uint8_t)idx; device_info->length = length; memcpy(device_info->bdaddr, device_address, 6); memcpy(device_info->name, device_name, MIN(strlen((const char *)device_name), 16 - 1)); memcpy(device_info->adv_data, p_data, length); /* Request socket information and connection configuration. */ smartbridge_auto_conn_user_parms((void *)device_info); /* Assign connection configuration parameters for auto connection procedure. */ require_string(g_auto_conn_dev_table[idx].socket != NULL, exit, "User not allow this auto connection"); mico_bt_cfg_settings.ble_scan_cfg.conn_min_interval = g_auto_conn_dev_table[idx].conn_settings.interval_min; mico_bt_cfg_settings.ble_scan_cfg.conn_max_interval = g_auto_conn_dev_table[idx].conn_settings.interval_max; mico_bt_cfg_settings.ble_scan_cfg.conn_latency = g_auto_conn_dev_table[idx].conn_settings.latency; mico_bt_cfg_settings.ble_scan_cfg.conn_supervision_timeout = g_auto_conn_dev_table[idx].conn_settings.supervision_timeout; return MICO_TRUE; exit: return MICO_FALSE; } static OSStatus smartbridge_auto_conn_user_parms( void *arg ) { OSStatus err = kNoErr; mico_bt_smartbridge_auto_conn_cback_parms_t parm; smartbridge_auto_conn_dev_info_t *p_dev_info = (smartbridge_auto_conn_dev_info_t *) arg; smartbridge_auto_conn_entity_t *p_entity; /* Request socket information and connection configuration. */ p_entity = &g_auto_conn_dev_table[p_dev_info->handle]; /* Release the socket resource */ if (p_entity->socket != NULL) { p_entity->socket->state = SOCKET_STATE_DISCONNECTED; } /* Get a free socket and connection configuration */ if ( (err = g_auto_conn_cback(p_dev_info->bdaddr, (char *) p_dev_info->name, p_dev_info->adv_data, p_dev_info->length, &parm) ) != kNoErr ) { /* Clear */ p_entity->socket = (void *) 0; memset( (void *) &p_entity->conn_settings, 0, sizeof(mico_bt_smart_connection_settings_t) ); bt_smartbridge_log(" Not allow this auto connection"); } else { /* Socket */ p_entity->socket = parm.socket; p_entity->socket->state = SOCKET_STATE_LINK_CONNECTING; p_entity->socket->auto_connection_callback = parm.auto_connection_callback; p_entity->socket->disconnection_callback = parm.auto_disconn_callback; p_entity->socket->notification_callback = parm.notification_callback; /* Connection settings */ memcpy( (void *) &p_entity->conn_settings, (void *) &parm.conn_settings, sizeof(mico_bt_smart_connection_settings_t) ); /* Security settings */ memcpy( (void *) &p_entity->socket->security_settings, (void *) &parm.security_settings, sizeof(mico_bt_smart_security_settings_t) ); } return err; } /* Find a valid auto connection entity by address. */ static int8_t smartbridge_auto_conn_find_dev_by_addr(const mico_bt_device_address_t device_address) { int8_t idx = 0; mico_bt_smartbridge_socket_t *temp_socket; for (idx = 0; idx < sizeof(g_auto_conn_dev_table)/sizeof(g_auto_conn_dev_table[0]); idx++) { temp_socket = g_auto_conn_dev_table[idx].socket; if ((temp_socket != (void *)0) && (memcmp((void *)temp_socket->remote_device.address, (void *)device_address, sizeof(mico_bt_device_address_t)) == 0)) { return idx; } } return -1; } /* Allocate a free auto connection entity. */ static int8_t smartbridge_auto_conn_dev_alloc(void) { int8_t idx = 0; for (idx = 0; idx < sizeof(g_auto_conn_dev_table)/sizeof(g_auto_conn_dev_table[0]); idx++) { if (g_auto_conn_dev_table[idx].socket == (void *)0) { memset((void *)&g_auto_conn_dev_table[idx].conn_settings, 0, sizeof(mico_bt_smart_connection_settings_t)); return idx; } } return -1; } void smartbridge_auto_connection_encryption_check(const mico_bt_dev_pairing_cplt_t *p_pairing_cplt) { if (p_pairing_cplt == NULL) { bt_smartbridge_log("Check auto connection encryption: invalid parameters"); return; } int idx = smartbridge_auto_conn_find_dev_by_addr(p_pairing_cplt->bd_addr); if (idx >= 0) { if (g_auto_conn_dev_table[idx].socket->security_settings.authentication_requirements != BT_SMART_AUTH_REQ_NONE) mico_rtos_set_semaphore(&g_auto_conn_sem); } } static OSStatus smartbridge_auto_conn_list_init(void) { linked_list_init(&g_auto_conn_list.list); return mico_rtos_init_mutex(&g_auto_conn_list.mutex); } static OSStatus smartbridge_auto_conn_list_deinit(void) { return mico_rtos_deinit_mutex(&g_auto_conn_list.mutex); } static mico_bool_t smartbridge_auto_conn_compare_bdaddr(linked_list_node_t* node_to_compare, void* user_data) { smartbridge_auto_conn_report_t* report = (smartbridge_auto_conn_report_t *)node_to_compare; mico_bt_device_address_t* device_address = (mico_bt_device_address_t *)user_data; if (memcmp(report->report.remote_device.address, device_address, BD_ADDR_LEN) == 0) { return TRUE; } else { return FALSE; } } static mico_bool_t smartbridge_auto_conn_compare_state(linked_list_node_t *node_to_compare, void *user_data) { smartbridge_auto_conn_report_t *report = (smartbridge_auto_conn_report_t *)node_to_compare; smartbridge_auto_conn_state_t state = *(smartbridge_auto_conn_state_t *)user_data; if (state != report->state) { return FALSE; } else { return TRUE; } } static OSStatus smartbridge_auto_conn_list_add(const mico_bt_smart_advertising_report_t *report) { OSStatus err = kNoErr; smartbridge_auto_conn_report_t *report_found = NULL, *new_report = NULL; uint32_t count = 0; smartbridge_auto_conn_state_t state = SMARTBRIDGE_AUTO_CONN_STATE_FREE; require_action(report != NULL, exit, err = kParamErr); /* Check if this report is existed. */ mico_rtos_lock_mutex(&g_auto_conn_list.mutex); err = linked_list_find_node(&g_auto_conn_list.list, (linked_list_compare_callback_t)smartbridge_auto_conn_compare_bdaddr, (void *)report->remote_device.address, (linked_list_node_t**)&report_found); /* If already existed, we should update its EIR Data. */ if (err == kNoErr) { bt_smartbridge_log("%s: already existed, state[%d], event[%d]", __FUNCTION__, report_found->state, report_found->report.event); if (report_found->state == SMARTBRIDGE_AUTO_CONN_STATE_FREE || (report_found->state == SMARTBRIDGE_AUTO_CONN_STATE_SCANED && report_found->report.event != report->event)) { /* Remove it from the list. */ linked_list_remove_node(&g_auto_conn_list.list, &report_found->this_node); /* Already existed and if its state is FREE or SCANED we should update it to reuse. */ report_found->report.signal_strength = report->signal_strength; report_found->report.event = report->event; report_found->report.eir_data_length = report->eir_data_length; memcpy(&report_found->report.eir_data, report->eir_data, report->eir_data_length); /* Add it to the list rear. */ err = linked_list_insert_node_at_rear(&g_auto_conn_list.list, &report_found->this_node); } else { /* Already existed and we may release it after next scaning procedure * in smartbridge_auto_conn_scan_cmpl_handler. */ err = kAlreadyInUseErr; } mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); return err; } else { linked_list_get_count(&g_auto_conn_list.list, &count); bt_smartbridge_log("%s: not existed, count[%lu]", __FUNCTION__, count); if (count > 0) { /* Are there a free report ? */ err = linked_list_find_node(&g_auto_conn_list.list, (linked_list_compare_callback_t)smartbridge_auto_conn_compare_state, (void *)&state, (linked_list_node_t**)&report_found); if (err == kNoErr) { /* Yeah, we should reuse it. */ linked_list_remove_node(&g_auto_conn_list.list, &report_found->this_node); new_report = report_found; } } } mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); /* Not existed. */ if (new_report == NULL) { new_report = malloc(sizeof(smartbridge_auto_conn_report_t)); if (!new_report) { bt_smartbridge_log("%s: malloc failed", __FUNCTION__); return kNoMemoryErr; } } memset(new_report, 0, sizeof(smartbridge_auto_conn_report_t)); new_report->state = SMARTBRIDGE_AUTO_CONN_STATE_SCANED; new_report->this_node.data = new_report; memcpy(&new_report->report, report, sizeof(mico_bt_smart_advertising_report_t)); mico_rtos_lock_mutex(&g_auto_conn_list.mutex); err = linked_list_insert_node_at_rear(&g_auto_conn_list.list, &new_report->this_node); mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); exit: return err; } static OSStatus smartbridge_auto_conn_list_get_by_state(mico_bt_smart_advertising_report_t **report, smartbridge_auto_conn_state_t state) { OSStatus err = kNoErr; smartbridge_auto_conn_report_t *current_report; require_action(report != NULL, exit, err = kParamErr); mico_rtos_lock_mutex(&g_auto_conn_list.mutex); err = linked_list_find_node(&g_auto_conn_list.list, (linked_list_compare_callback_t)smartbridge_auto_conn_compare_state, (void *)&state, (linked_list_node_t**)¤t_report); *report = ¤t_report->report; mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); exit: return err; } static OSStatus smartbridge_auto_conn_list_set_state(const mico_bt_device_address_t bdaddr, smartbridge_auto_conn_state_t state) { OSStatus err = kNoErr; smartbridge_auto_conn_report_t *report_found; require_action(bdaddr != NULL, exit, err = kParamErr); mico_rtos_lock_mutex(&g_auto_conn_list.mutex); err = linked_list_find_node(&g_auto_conn_list.list, (linked_list_compare_callback_t)smartbridge_auto_conn_compare_bdaddr, (void *)bdaddr, (linked_list_node_t **)&report_found); if (err == kNoErr) { report_found->state = state; } mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); exit: return err; } static OSStatus smartbridge_auto_conn_list_remove(mico_bt_device_address_t bdaddr) { OSStatus err = kNoErr; smartbridge_auto_conn_report_t* current_report; mico_rtos_lock_mutex(&g_auto_conn_list.mutex); err = linked_list_find_node(&g_auto_conn_list.list, (linked_list_compare_callback_t)smartbridge_auto_conn_compare_bdaddr, bdaddr, (linked_list_node_t**)¤t_report); if (err != kNoErr) goto exit; current_report->state = SMARTBRIDGE_AUTO_CONN_STATE_FREE; exit: mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); return err; } static OSStatus smartbridge_auto_conn_list_remove_by_state(smartbridge_auto_conn_state_t state) { OSStatus err = kNoErr; smartbridge_auto_conn_report_t* current_report; mico_rtos_lock_mutex(&g_auto_conn_list.mutex); do { err = linked_list_find_node(&g_auto_conn_list.list, (linked_list_compare_callback_t)smartbridge_auto_conn_compare_state, &state, (linked_list_node_t**)¤t_report); if (err != kNoErr) break; current_report->state = SMARTBRIDGE_AUTO_CONN_STATE_FREE; } while (1); mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); return kNoErr; } static OSStatus smartbridge_auto_conn_list_clear(void) { OSStatus err = kNoErr; smartbridge_auto_conn_report_t* current_report = NULL; mico_rtos_lock_mutex(&g_auto_conn_list.mutex); do { err = linked_list_get_front_node(&g_auto_conn_list.list, (linked_list_node_t **)¤t_report); if (err != kNoErr) break; linked_list_remove_node(&g_auto_conn_list.list, ¤t_report->this_node); free(current_report); } while (1); mico_rtos_unlock_mutex(&g_auto_conn_list.mutex); return kNoErr; }