From 7df3e0bf12605de8a58dd4f3f7c0f6835e6a861b Mon Sep 17 00:00:00 2001 From: "mila@ringwald.ch" Date: Thu, 21 Nov 2013 15:15:15 +0000 Subject: [PATCH] ble client: alow and guard exclusive connection establishment, parallel disconnects, and scanning --- example/libusb/ble_client.c | 620 ++++++++++++++++++++++-------------- example/libusb/ble_client.h | 20 +- 2 files changed, 405 insertions(+), 235 deletions(-) diff --git a/example/libusb/ble_client.c b/example/libusb/ble_client.c index fb65108e0..bbd017a08 100644 --- a/example/libusb/ble_client.c +++ b/example/libusb/ble_client.c @@ -46,6 +46,7 @@ #include #include #include +#include #include "config.h" @@ -65,24 +66,22 @@ typedef enum { IDLE, // START_SCAN, - W4_SCAN_ACTIVE, + W4_SCANNING, // - SCAN_ACTIVE, + SCANNING, // STOP_SCAN, - W4_SCAN_STOPPED, - // IDLE, - W4_CONNECTED, - CONNECTED, - // - DISCONNECT, - W4_DISCONNECTED + W4_SCAN_STOPPED } state_t; static state_t state = W4_ON; static linked_list_t le_connections = NULL; +void le_central_init(){ + state = W4_ON; + le_connections = NULL; +} void (*le_central_callback)(le_central_event_t * event); @@ -108,48 +107,14 @@ void le_central_register_handler(void (*le_callback)(le_central_event_t* event)) } } -static void hexdump2(void *data, int size){ - int i; - for (i=0; istate = P_IDLE; -} - -void le_central_connect(le_peripheral_t *context, uint8_t addr_type, bd_addr_t addr){ - le_peripheral_init(context); - context->state = P_W2_CONNECT; - context->address_type = addr_type; - memcpy (context->address, addr, 6); - linked_list_add(&le_connections, (linked_item_t *) context); - - if (state == SCAN_ACTIVE){ - le_central_stop_scan(); - } - gatt_client_run(); +static void send_gatt_connection_complete_event(le_peripheral_t * peripheral, uint8_t status){ + le_peripheral_event_t event; + event.type = GATT_CONNECTION_COMPLETE; + event.device = peripheral; + event.status = status; + (*le_central_callback)((le_central_event_t*)&event); } static le_peripheral_t * get_peripheral_for_handle(uint16_t handle){ @@ -178,6 +143,10 @@ static inline le_peripheral_t * get_peripheral_w4_disconnected(){ return get_peripheral_with_state(P_W4_DISCONNECTED); } +static inline le_peripheral_t * get_peripheral_w4_connect_cancelled(){ + return get_peripheral_with_state(P_W4_CONNECT_CANCELLED); +} + static inline le_peripheral_t * get_peripheral_w2_connect(){ return get_peripheral_with_state(P_W2_CONNECT); } @@ -190,84 +159,292 @@ static inline le_peripheral_t * get_peripheral_w2_exchange_MTU(){ return get_peripheral_with_state(P_W2_EXCHANGE_MTU); } - -void le_central_cancel_connect(le_peripheral_t *context){ - if (context->state == P_IDLE) return; - - if (context->state == P_W2_CONNECT){ - context->state = P_IDLE; - return; +static le_peripheral_t * get_peripheral_with_address(uint8_t addr_type, bd_addr_t addr){ + linked_item_t *it; + for (it = (linked_item_t *) le_connections; it ; it = it->next){ + le_peripheral_t * peripheral = (le_peripheral_t *) it; + if (BD_ADDR_CMP(addr, peripheral->address) == 0 && peripheral->address_type == addr_type){ + return peripheral; + } } - state = DISCONNECT; + return 0; +} + +static void handle_advertising_packet(uint8_t *packet){ + int num_reports = packet[3]; + int i; + int total_data_length = 0; + int data_offset = 0; + + for (i=0; inext){ + le_peripheral_t * peripheral = (le_peripheral_t *) it; + + switch (peripheral->state){ + case P_W2_CONNECT: + peripheral->state = P_W4_CONNECTED; + hci_send_cmd(&hci_le_create_connection, + 1000, // scan interval: 625 ms + 1000, // scan interval: 625 ms + 0, // don't use whitelist + 0, // peer address type: public + peripheral->address, // remote bd addr + peripheral->address_type, // random or public + 80, // conn interval min + 80, // conn interval max (3200 * 0.625) + 0, // conn latency + 2000, // supervision timeout + 0, // min ce length + 1000 // max ce length + ); + return; + + case P_W2_CANCEL_CONNECT: + peripheral->state = P_W4_CONNECT_CANCELLED; + hci_send_cmd(&hci_le_create_connection_cancel); + return; + case P_W2_EXCHANGE_MTU: + { + peripheral->state = P_W4_EXCHANGE_MTU; + uint16_t mtu = l2cap_max_mtu_for_handle(peripheral->handle); + uint8_t request[3]; + request[0] = ATT_EXCHANGE_MTU_REQUEST; + bt_store_16(request, 1, mtu); + l2cap_send_connectionless(peripheral->handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, request, sizeof(request)); + return; + } + + case P_W2_DISCONNECT: + peripheral->state = P_W4_DISCONNECTED; + hci_send_cmd(&hci_disconnect, peripheral->handle,0x13); + return; + + default: + break; + } + + } + +} + + +void le_central_start_scan(){ + if (state != IDLE) return; + state = START_SCAN; + gatt_client_run(); +} + +void le_central_stop_scan(){ + if (state != SCANNING) return; + state = STOP_SCAN; + gatt_client_run(); +} + +static void le_peripheral_init(le_peripheral_t *context, uint8_t addr_type, bd_addr_t addr){ + memset(context, 0, sizeof(le_peripheral_t)); + context->state = P_W2_CONNECT; + context->address_type = addr_type; + memcpy (context->address, addr, 6); +} + +le_command_status_t le_central_connect(le_peripheral_t *context, uint8_t addr_type, bd_addr_t addr){ + //TODO: align with hci connection list capacity + le_peripheral_t * peripheral = get_peripheral_with_address(addr_type, addr); + if (!peripheral) { + le_peripheral_init(context, addr_type, addr); + linked_list_add(&le_connections, (linked_item_t *) context); + } else if (peripheral == context) { + if (context->state != P_W2_CONNECT) return BLE_PERIPHERAL_IN_WRONG_STATE; + } else { + return BLE_PERIPHERAL_DIFFERENT_CONTEXT_FOR_ADDRESS_ALREADY_EXISTS; + } + + gatt_client_run(); + return BLE_PERIPHERAL_OK; +} + +le_command_status_t le_central_disconnect(le_peripheral_t *context){ + // printf("*** le_central_disconnect::CALLED DISCONNECT \n"); + + le_peripheral_t * peripheral = get_peripheral_with_address(context->address_type, context->address); + if (!peripheral || (peripheral && peripheral != context)){ + return BLE_PERIPHERAL_DIFFERENT_CONTEXT_FOR_ADDRESS_ALREADY_EXISTS; + } + + switch(context->state){ + case P_W2_CONNECT: + linked_list_remove(&le_connections, (linked_item_t *) context); + send_gatt_connection_complete_event(context, 0); + break; + case P_W4_CONNECTED: + case P_W2_CANCEL_CONNECT: + // trigger cancel connect + context->state = P_W2_CANCEL_CONNECT; + break; + case P_W2_EXCHANGE_MTU: + case P_W4_EXCHANGE_MTU: + case P_CONNECTED: + case P_W2_DISCONNECT: + // trigger disconnect + context->state = P_W2_DISCONNECT; + break; + case P_W4_DISCONNECTED: + case P_W4_CONNECT_CANCELLED: + return BLE_PERIPHERAL_IN_WRONG_STATE; + } + gatt_client_run(); + return BLE_PERIPHERAL_OK; } static void gatt_client_run(){ + if (state == W4_ON) return; + + handle_peripheral_list(); + + // check if command is send if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return; if (!l2cap_can_send_conectionless_packet_now()) return; - - le_peripheral_t * peripheral = NULL; - // hadle peripherals list + switch(state){ - case IDLE: - if (! (peripheral = get_peripheral_w2_connect()) ) break; - - peripheral->state = P_W4_CONNECTED; - state = W4_CONNECTED; - - hci_send_cmd(&hci_le_create_connection, - 1000, // scan interval: 625 ms - 1000, // scan interval: 625 ms - 0, // don't use whitelist - 0, // peer address type: public - peripheral->address, // remote bd addr - peripheral->address_type, // random or public - 80, // conn interval min - 80, // conn interval max (3200 * 0.625) - 0, // conn latency - 2000, // supervision timeout - 0, // min ce length - 1000 // max ce length - ); - - break; - case START_SCAN: - state = W4_SCAN_ACTIVE; + state = W4_SCANNING; hci_send_cmd(&hci_le_set_scan_enable, 1, 0); - break; + return; + case STOP_SCAN: state = W4_SCAN_STOPPED; hci_send_cmd(&hci_le_set_scan_enable, 0, 0); - break; - case CONNECTED:{ - le_peripheral_t * peripheral = get_peripheral_w2_exchange_MTU(); - - if (!peripheral) break; + return; - peripheral->state = P_W4_EXCHANGE_MTU; - uint16_t mtu = l2cap_max_mtu_for_handle(peripheral->handle); - - uint8_t request[3]; - request[0] = ATT_EXCHANGE_MTU_REQUEST; - bt_store_16(request, 1, mtu); - l2cap_send_connectionless(peripheral->handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, request, sizeof(request)); - break; - } - case DISCONNECT: - peripheral->state = P_W4_DISCONNECTED; - state = W4_DISCONNECTED; - hci_send_cmd(&hci_le_create_connection_cancel); - break; default: break; } } -static void dump_ad_event(ad_event_t e){ - printf("evt-type %u, addr-type %u, addr %s, rssi %u, length adv %u, data: ", e.event_type, - e.address_type, bd_addr_to_str(e.address), e.rssi, e.length); - hexdump2( e.data, e.length); + +static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + + if (packet_type != HCI_EVENT_PACKET) return; + switch (packet[0]) { + case BTSTACK_EVENT_STATE: + // BTstack activated, get started + if (packet[2] == HCI_STATE_WORKING) { + printf("BTstack activated, get started!\n"); + state = IDLE; + } + break; + + case HCI_EVENT_COMMAND_COMPLETE: + if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_scan_enable)){ + switch(state){ + case W4_SCANNING: + state = SCANNING; + break; + case W4_SCAN_STOPPED: + state = IDLE; + break; + default: + break; + } + return; + } + + if (COMMAND_COMPLETE_EVENT(packet, hci_le_create_connection_cancel)){ + // printf("packet_handler:: hci_le_create_connection_cancel: cancel connect\n"); + if (packet[3] != 0x0B) break; + + // cancel connection failed, as connection already established + le_peripheral_t * peripheral = get_peripheral_w4_connect_cancelled(); + peripheral->state = P_W2_DISCONNECT; + break; + } + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + { + uint16_t handle = READ_BT_16(packet,3); + le_peripheral_t * peripheral = get_peripheral_for_handle(handle); + if (!peripheral) break; + + peripheral->state = P_W2_CONNECT; + linked_list_remove(&le_connections, (linked_item_t *) peripheral); + send_gatt_connection_complete_event(peripheral, packet[5]); + // printf("Peripheral disconnected, and removed from list\n"); + break; + } + + case HCI_EVENT_LE_META: + switch (packet[2]) { + case HCI_SUBEVENT_LE_ADVERTISING_REPORT: + if (state != SCANNING) break; + handle_advertising_packet(packet); + break; + + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: { + // deal with conn cancel, conn fail, conn success + le_peripheral_t * peripheral; + + // conn success/error? + peripheral = get_peripheral_w4_connected(); + if (peripheral){ + if (packet[3]){ + // error + linked_list_remove(&le_connections, (linked_item_t *) peripheral); + } else { + // success + peripheral->state = P_W2_EXCHANGE_MTU; + peripheral->handle = READ_BT_16(packet, 4); + } + send_gatt_connection_complete_event(peripheral, packet[3]); + break; + } + + // cancel success? + peripheral = get_peripheral_w4_connect_cancelled(); + if (!peripheral) break; + + linked_list_remove(&le_connections, (linked_item_t *) peripheral); + send_gatt_connection_complete_event(peripheral, packet[3]); + break; + } + default: + break; + } + break; + + default: + break; + } + gatt_client_run(); } @@ -278,138 +455,20 @@ static void att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *pa case ATT_EXCHANGE_MTU_RESPONSE: { le_peripheral_t * peripheral = get_peripheral_for_handle(handle); - + if (!peripheral) return; + uint16_t remote_rx_mtu = READ_BT_16(packet, 1); uint16_t local_rx_mtu = l2cap_max_mtu_for_handle(handle); peripheral->mtu = remote_rx_mtu < local_rx_mtu ? remote_rx_mtu : local_rx_mtu; - le_peripheral_event_t p_connected_event; - p_connected_event.type = GATT_CONNECTION_COMPLETE; - p_connected_event.device = peripheral; - p_connected_event.status = 0; - (*le_central_callback)((le_central_event_t*)&p_connected_event); - + send_gatt_connection_complete_event(peripheral, 0); + peripheral->state = P_CONNECTED; break; } default: break; } -} - - -static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (packet[0]) { - - case BTSTACK_EVENT_STATE: - // bt stack activated, get started - if (packet[2] == HCI_STATE_WORKING) { - printf("Working!\n"); - state = IDLE; - } - break; - - case HCI_EVENT_COMMAND_COMPLETE: - if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_scan_enable)){ - switch(state){ - case W4_SCAN_ACTIVE: - state = SCAN_ACTIVE; - break; - case W4_SCAN_STOPPED: - state = IDLE; - break; - default: - break; - } - } - break; - - case HCI_EVENT_LE_META: - switch (packet[2]) { - case HCI_SUBEVENT_LE_ADVERTISING_REPORT: - if (state != SCAN_ACTIVE) break; - { - int num_reports = packet[3]; - int i; - int total_data_length = 0; - int data_offset = 0; - - for (i=0; istate = P_W2_EXCHANGE_MTU; - - peripheral->handle = READ_BT_16(packet, 4); - - printf("Connected: handle 0x%04x!\n", peripheral->handle); - break; - } - - case W4_DISCONNECTED: { - le_peripheral_t * peripheral = get_peripheral_w4_disconnected(); - state = IDLE; - peripheral->state = P_IDLE; - - le_peripheral_event_t p_disconnected_event; - p_disconnected_event.type = GATT_CONNECTION_COMPLETE; - p_disconnected_event.device = peripheral; - p_disconnected_event.status = packet[3]; - - (*le_central_callback)((le_central_event_t*)&p_disconnected_event); - - break; - } - default: - break; - } - break; - } - - default: - break; - } - break; - - default: - break; - } - } - gatt_client_run(); } @@ -435,11 +494,17 @@ void setup(void){ l2cap_register_packet_handler(packet_handler); } + // main == setup int main(void) { setup(); - + + /* test + le_central_init(); + le_central_register_handler(handle_le_central_event); + */ + // turn on! hci_power_control(HCI_POWER_ON); @@ -449,3 +514,96 @@ int main(void) // happy compiler! return 0; } + +/* test + +static void dump_peripheral_state(peripheral_state_t p_state){ + switch(p_state) { + case P_W2_CONNECT: printf("P_W2_CONNECT"); break; + case P_W4_CONNECTED: printf("P_W4_CONNECTED"); break; + case P_W2_EXCHANGE_MTU: printf("P_W2_EXCHANGE_MTU"); break; + case P_W4_EXCHANGE_MTU: printf("P_W4_EXCHANGE_MTU"); break; + case P_CONNECTED: printf("P_CONNECTED"); break; + case P_W2_CANCEL_CONNECT: printf("P_W2_CANCEL_CONNECT"); break; + case P_W4_CONNECT_CANCELLED: printf("P_W4_CONNECT_CANCELLED"); break; + case P_W2_DISCONNECT: printf("P_W2_DISCONNECT"); break; + case P_W4_DISCONNECTED: printf("P_W4_DISCONNECTED"); break; + }; + printf("\n"); +} + +static void dump_state(){ + switch(state){ + case W4_ON: printf("W4_ON"); break; + case IDLE: printf("IDLE"); break; + case START_SCAN: printf("START_SCAN"); break; + case W4_SCANNING: printf("W4_SCANNING"); break; + case SCANNING: printf("SCANNING"); break; + case STOP_SCAN: printf("STOP_SCAN"); break; + case W4_SCAN_STOPPED: printf("W4_SCAN_STOPPED"); break; + }; + printf("\n"); +} + +static void hexdump2(void *data, int size){ + int i; + for (i=0; ievent_type, + e->address_type, bd_addr_to_str(e->address), e->rssi, e->length); + hexdump2( e->data, e->length); +} + +le_peripheral_t test_device; + +static bd_addr_t test_device_addr = {0x1c, 0xba, 0x8c, 0x20, 0xc7, 0xf6}; + + +void test_client(){ + static int i = 0; + + switch(i){ + case 0: + le_central_start_scan(); + printf("--- test_client::calling start scan \n"); + break; + case 20: + printf("--- test_client::calling connect peripheral: status %d\n", le_central_connect(&test_device, 0, test_device_addr)); + break; + case 40: + i=-1; + // le_central_stop_scan(); + printf("--- test_client::calling dissconnect peripheral: status %d\n", le_central_disconnect(&test_device)); + break; + default: + break; + } + i++; +} + +static void handle_le_central_event(le_central_event_t * event){ + ad_event_t * advertisement_event; + le_peripheral_event_t * peripheral_event; + switch (event->type){ + case GATT_ADVERTISEMENT: + advertisement_event = (ad_event_t*) event; + dump_ad_event(advertisement_event); + break; + case GATT_CONNECTION_COMPLETE: + peripheral_event = (le_peripheral_event_t *) event; + if (peripheral_event->status == 0){ + printf("handle_le_central_event::device is connected\n"); + } else { + printf("handle_le_central_event::disconnected with status %02x \n", peripheral_event->status); + } + default: + break; + } +}*/ + + diff --git a/example/libusb/ble_client.h b/example/libusb/ble_client.h index 6b5b8b863..9bbf82d8a 100644 --- a/example/libusb/ble_client.h +++ b/example/libusb/ble_client.h @@ -66,15 +66,27 @@ typedef struct ad_event { } ad_event_t; typedef enum { - P_IDLE, P_W2_CONNECT, P_W4_CONNECTED, + P_W2_EXCHANGE_MTU, P_W4_EXCHANGE_MTU, + P_CONNECTED, - P_W4_DISCONNECTED + + P_W2_CANCEL_CONNECT, + P_W4_CONNECT_CANCELLED, + P_W2_DISCONNECT, + P_W4_DISCONNECTED, } peripheral_state_t; +typedef enum { + BLE_PERIPHERAL_OK = 0, + BLE_PERIPHERAL_IN_WRONG_STATE, + BLE_PERIPHERAL_DIFFERENT_CONTEXT_FOR_ADDRESS_ALREADY_EXISTS +} le_command_status_t; + + typedef struct le_peripheral{ linked_item_t item; @@ -108,8 +120,8 @@ void le_central_start_scan(); // { type (8), addr_type (8), addr(48), rssi(8), ad_len(8), ad_data(ad_len*8) } void le_central_stop_scan(); -void le_central_connect(le_peripheral_t *context, uint8_t addr_type, bd_addr_t addr); -void le_central_cancel_connect(le_peripheral_t *context); +le_command_status_t le_central_connect(le_peripheral_t *context, uint8_t addr_type, bd_addr_t addr); +le_command_status_t le_central_disconnect(le_peripheral_t *context); void le_central_get_services(le_peripheral_t *context); void le_central_get_services_with_uuid16(le_peripheral_t *context, uint16_t uuid16);