static void sc_local_oob_generated_callback(const uint8_t * confirm_value, const uint8_t * random_value){ printf("LOCAL_OOB_CONFIRM: "); printf_hexdump(confirm_value, 16); printf("LOCAL_OOB_RANDOM: "); printf_hexdump(random_value, 16); fflush(stdout); memcpy(sm_oob_local_random, random_value, 16); }
void CHECK_EQUAL_ARRAY(uint8_t * expected, uint8_t * actual, int size){ int i; for (i=0; i<size; i++){ if (expected[i] != actual[i]) { printf("offset %u wrong\n", i); printf("expected: "); printf_hexdump(expected, size); printf("actual: "); printf_hexdump(actual, size); } BYTES_EQUAL(expected[i], actual[i]); } }
static void stdin_process(char c){ // passkey input if (ui_digits_for_passkey && c >= '0' && c <= '9'){ printf("%c", c); fflush(stdout); ui_passkey = ui_passkey * 10 + c - '0'; ui_digits_for_passkey--; sm_keypress_notification(connection_handle, SM_KEYPRESS_PASSKEY_DIGIT_ENTERED); if (ui_digits_for_passkey == 0){ printf("\n"); fflush(stdout); sm_keypress_notification(connection_handle, SM_KEYPRESS_PASSKEY_ENTRY_COMPLETED); sm_passkey_input(connection_handle, ui_passkey); } return; } if (ui_oob_confirm){ if (c == ' ') return; ui_oob_nibble = (ui_oob_nibble << 4) | nibble_for_char(c); if ((ui_oob_pos & 1) == 1){ sm_oob_peer_confirm[ui_oob_pos >> 1] = ui_oob_nibble; ui_oob_nibble = 0; } ui_oob_pos++; if (ui_oob_pos == 32){ ui_oob_confirm = 0; printf("PEER_OOB_CONFIRM: "); printf_hexdump(sm_oob_peer_confirm, 16); fflush(stdout); } return; }
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { if (packet_type != HCI_EVENT_PACKET) return; uint8_t event = hci_event_packet_get_type(packet); switch (event) { case BTSTACK_EVENT_STATE: // bt stack activated, get started printf("- btstack state %u\n", packet[2]); if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING) { bt_send_cmd(&gap_le_set_scan_parameters, 1, 0x30, 0x30); bt_send_cmd(&gap_le_scan_start); } break; case HCI_EVENT_LE_META: switch (hci_event_le_meta_get_subevent_code(packet)) { case HCI_SUBEVENT_LE_ADVERTISING_REPORT: printf("\n- ADV: "); printf_hexdump(packet, size); break; } break; default: break; } }
static void handle_hci_event(uint8_t packet_type, 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, start scaning!\n"); le_central_set_scan_parameters(0,0x0030, 0x0030); le_central_start_scan(); } break; case GAP_LE_ADVERTISING_REPORT:{ int pos = 2; uint8_t event_type = packet[pos++]; uint8_t address_type = packet[pos++]; bd_addr_t address; bt_flip_addr(address, &packet[pos]); pos += 6; uint8_t rssi = packet[pos++]; uint8_t length = packet[pos++]; uint8_t * data = &packet[pos]; printf("Advertisement event: evt-type %u, addr-type %u, addr %s, rssi %u, data[%u] ", event_type, address_type, bd_addr_to_str(address), rssi, length); printf_hexdump(data, length); dump_advertisement_data(data, length); break; } default: break; } }
static void dump_descriptor(le_event_t * event){ le_characteristic_descriptor_event_t * devent = (le_characteristic_descriptor_event_t *) event; le_characteristic_descriptor_t descriptor = devent->characteristic_descriptor; printf(" *** descriptor *** handle 0x%02x, value ", descriptor.handle); printf_hexdump(devent->value, devent->value_length); printUUID(descriptor.uuid128, descriptor.uuid16); }
static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ if (packet_type != HCI_EVENT_PACKET) return; switch (hci_event_packet_get_type(packet)) { case BTSTACK_EVENT_STATE: // BTstack activated, get started if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ printf("Start scaning!\n"); gap_set_scan_parameters(0,0x0030, 0x0030); gap_start_scan(); } break; case GAP_EVENT_ADVERTISING_REPORT:{ bd_addr_t address; gap_event_advertising_report_get_address(packet, address); uint8_t event_type = gap_event_advertising_report_get_advertising_event_type(packet); uint8_t address_type = gap_event_advertising_report_get_address_type(packet); int8_t rssi = gap_event_advertising_report_get_rssi(packet); uint8_t length = gap_event_advertising_report_get_data_length(packet); const uint8_t * data = gap_event_advertising_report_get_data(packet); printf("Advertisement event: evt-type %u, addr-type %u, addr %s, rssi %d, data[%u] ", event_type, address_type, bd_addr_to_str(address), rssi, length); printf_hexdump(data, length); dump_advertisement_data(data, length); break; } default: break; } }
/** * @brief Process received data */ void sco_demo_receive(uint8_t * packet, uint16_t size){ int dump_data = 1; count_received++; // if ((count_received % SCO_REPORT_PERIOD) == 0) sco_report(); #if SCO_DEMO_MODE == SCO_DEMO_MODE_SINE #ifdef SCO_WAV_FILENAME if (num_samples_to_write){ const int num_samples = size - 3; const int samples_to_write = btstack_min(num_samples, num_samples_to_write); // convert 8 bit signed to 8 bit unsigned int i; for (i=0;i<samples_to_write;i++){ packet[3+i] += 128; } write_wav_data_uint8(wav_file, samples_to_write, &packet[3]); num_samples_to_write -= samples_to_write; if (num_samples_to_write == 0){ printf("SCO Demo: closing wav file\n"); fclose(wav_file); } dump_data = 0; } #endif #endif if (packet[1] & 0xf0){ printf("SCO CRC Error: %x - data: ", packet[1] >> 4); printf_hexdump(&packet[3], size-3); return; }
// write requests static int att_write_callback(hci_con_handle_t con_handle, uint16_t att_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ // ignore cancel sent for new connections if (transaction_mode == ATT_TRANSACTION_MODE_CANCEL) return 0; // find characteristic for handle switch (att_handle){ case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE: le_notification_enabled = little_endian_read_16(buffer, 0) == GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION; att_con_handle = con_handle; return 0; case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_VALUE_HANDLE: printf("Write on test characteristic: "); printf_hexdump(buffer, buffer_size); return 0; default: printf("WRITE Callback, handle %04x, mode %u, offset %u, data: ", con_handle, transaction_mode, offset); printf_hexdump(buffer, buffer_size); return 0; } }
// write requests static int att_write_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ UNUSED(con_handle); printf("WRITE Callback, handle %04x, mode %u, offset %u, data: ", attribute_handle, transaction_mode, offset); printf_hexdump(buffer, buffer_size); switch (attribute_handle){ case ATT_CHARACTERISTIC_0000FF11_0000_1000_8000_00805F9B34FB_01_CLIENT_CONFIGURATION_HANDLE: // short cut, send right away att_server_request_can_send_now_event(con_handle); break; default: break; } return 0; }
void show_usage(){ printf("\n--- CLI for LE Peripheral ---\n"); printf("GAP: discoverable %u, connectable %u, bondable %u, directed connectable %u, random addr %u, ads enabled %u, adv type %u \n", gap_discoverable, gap_connectable, gap_bondable, gap_directed_connectable, gap_random, gap_advertisements, gap_adv_type()); printf("ADV: "); printf_hexdump(adv_data, adv_data_len); printf("SM: %s, MITM protection %u, OOB data %u, key range [%u..16]\n", sm_io_capabilities, sm_mitm_protection, sm_have_oob_data, sm_min_key_size); printf("Default value: '%s'\n", att_default_value_long ? default_value_long : default_value_short); printf("Privacy %u\n", gap_privacy); printf("Device name %s\n", gap_device_name); printf("---\n"); printf("a/A - advertisements off/on\n"); printf("b/B - bondable off/on\n"); printf("c/C - connectable off\n"); printf("d/D - discoverable off\n"); printf("r/R - random address off/on\n"); printf("x/X - directed connectable off\n"); printf("y/Y - scannable on/off\n"); printf("---\n"); printf("1 - AD Manufacturer Data | 7 - AD Slave Preferred... | = - AD Public Target Address\n"); printf("2 - AD Local Name | 8 - AD Tx Power Level | / - AD Random Target Address\n"); printf("3 - AD flags | 9 - AD SM OOB | # - AD Advertising interval\n"); printf("4 - AD Service Data | 0 - AD SM TK | & - AD LE Role\n"); printf("5 - AD Service Solicitation | + - AD LE BD_ADDR\n"); printf("6 - AD Services | - - AD Appearance\n"); printf("---\n"); printf("l/L - default attribute value '%s'/'%s'\n", default_value_short, default_value_long); printf("p/P - privacy flag off\n"); printf("s - send security request\n"); printf("z - send Connection Parameter Update Request\n"); printf("t - terminate connection\n"); printf("j - create L2CAP LE connection to %s\n", bd_addr_to_str(tester_address)); printf("---\n"); printf("e - IO_CAPABILITY_DISPLAY_ONLY\n"); printf("f - IO_CAPABILITY_DISPLAY_YES_NO\n"); printf("g - IO_CAPABILITY_NO_INPUT_NO_OUTPUT\n"); printf("h - IO_CAPABILITY_KEYBOARD_ONLY\n"); printf("i - IO_CAPABILITY_KEYBOARD_DISPLAY\n"); printf("o/O - OOB data off/on ('%s')\n", sm_oob_data); printf("m/M - MITM protection off\n"); printf("k/k - encryption key range [7..16]/[16..16]\n"); printf("---\n"); printf("Ctrl-c - exit\n"); printf("---\n"); }
static void printf_packet(uint8_t packet_type, uint8_t in, uint8_t * packet, uint16_t len){ switch (packet_type){ case HCI_COMMAND_DATA_PACKET: printf("CMD => "); break; case HCI_EVENT_PACKET: printf("EVT <= "); break; case HCI_ACL_DATA_PACKET: if (in) { printf("ACL <= "); } else { printf("ACL => "); } break; case LOG_MESSAGE_PACKET: printf("LOG -- %s\n", (char*) packet); return; default: return; } printf_hexdump(packet, len); }
// Bluetooth logic static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ bd_addr_t event_addr; uint8_t rfcomm_channel_nr; uint16_t mtu; uint8_t event_code; // uint8_t channel; uint8_t message_id; switch (packet_type) { case HCI_EVENT_PACKET: switch (hci_event_packet_get_type(packet)) { case BTSTACK_EVENT_STATE: if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ printf("BTstack is up and running\n"); // start ANT init ant_send_cmd(&ant_reset); } break; case HCI_EVENT_COMMAND_COMPLETE: if (HCI_EVENT_IS_COMMAND_COMPLETE(packet, hci_read_bd_addr)){ reverse_bd_addr(&packet[6], event_addr); printf("BD-ADDR: %s\n\r", bd_addr_to_str(event_addr)); break; } break; case HCI_EVENT_LINK_KEY_REQUEST: // deny link key request printf("Link key request\n\r"); hci_event_link_key_request_get_bd_addr(packet, event_addr); hci_send_cmd(&hci_link_key_request_negative_reply, &event_addr); break; case HCI_EVENT_PIN_CODE_REQUEST: // inform about pin code request printf("Pin code request - using '0000'\n\r"); reverse_bd_addr(&packet[2], event_addr); hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); break; case RFCOMM_EVENT_INCOMING_CONNECTION: // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) rfcomm_event_incoming_connection_get_bd_addr(packet, event_addr); rfcomm_channel_nr = rfcomm_event_incoming_connection_get_server_channel(packet); rfcomm_channel_id = rfcomm_event_incoming_connection_get_rfcomm_cid(packet); printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr)); rfcomm_accept_connection(rfcomm_channel_id); break; case RFCOMM_EVENT_CHANNEL_OPENED: // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) if (rfcomm_event_channel_opened_get_status(packet)) { printf("RFCOMM channel open failed, status %u\n", rfcomm_event_channel_opened_get_status(packet)); } else { rfcomm_channel_id = rfcomm_event_channel_opened_get_rfcomm_cid(packet); mtu = rfcomm_event_channel_opened_get_max_frame_size(packet); printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu); } break; case RFCOMM_EVENT_CHANNEL_CLOSED: rfcomm_channel_id = 0; break; case 0xff: // vendor specific -> ANT // vendor specific ant message if (packet[2] != 0x00) break; if (packet[3] != 0x05) break; event_code = packet[7]; printf("ANT Event: "); printf_hexdump(packet, size); switch(event_code){ case MESG_STARTUP_MESG_ID: // 2. assign channel ant_send_cmd(&ant_assign_channel, 0, 0x00, 0); break; case MESG_RESPONSE_EVENT_ID: // channel = packet[8]; message_id = packet[9]; switch (message_id){ case MESG_ASSIGN_CHANNEL_ID: // 3. set channel ID ant_send_cmd(&ant_channel_id, 0, 33, 1, 1); break; case MESG_CHANNEL_ID_ID: // 4. open channel ant_send_cmd(&ant_open_channel, 0); } break; default: break; } break; default: break; } break; default: break; } }
static void handle_sdp_client_query_result(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { UNUSED(packet_type); UNUSED(channel); UNUSED(size); des_iterator_t attribute_list_it; des_iterator_t additional_des_it; des_iterator_t prot_it; uint8_t *des_element; uint8_t *element; uint32_t uuid; uint8_t status; switch (hci_event_packet_get_type(packet)){ case SDP_EVENT_QUERY_ATTRIBUTE_VALUE: if (sdp_event_query_attribute_byte_get_attribute_length(packet) <= attribute_value_buffer_size) { attribute_value[sdp_event_query_attribute_byte_get_data_offset(packet)] = sdp_event_query_attribute_byte_get_data(packet); if ((uint16_t)(sdp_event_query_attribute_byte_get_data_offset(packet)+1) == sdp_event_query_attribute_byte_get_attribute_length(packet)) { switch(sdp_event_query_attribute_byte_get_attribute_id(packet)) { case BLUETOOTH_ATTRIBUTE_PROTOCOL_DESCRIPTOR_LIST: for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; des_element = des_iterator_get_element(&attribute_list_it); des_iterator_init(&prot_it, des_element); element = des_iterator_get_element(&prot_it); if (de_get_element_type(element) != DE_UUID) continue; uuid = de_get_uuid32(element); switch (uuid){ case BLUETOOTH_PROTOCOL_L2CAP: if (!des_iterator_has_more(&prot_it)) continue; des_iterator_next(&prot_it); de_element_get_uint16(des_iterator_get_element(&prot_it), &hid_control_psm); printf("HID Control PSM: 0x%04x\n", (int) hid_control_psm); break; default: break; } } break; case BLUETOOTH_ATTRIBUTE_ADDITIONAL_PROTOCOL_DESCRIPTOR_LISTS: for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; des_element = des_iterator_get_element(&attribute_list_it); for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) { if (des_iterator_get_type(&additional_des_it) != DE_DES) continue; des_element = des_iterator_get_element(&additional_des_it); des_iterator_init(&prot_it, des_element); element = des_iterator_get_element(&prot_it); if (de_get_element_type(element) != DE_UUID) continue; uuid = de_get_uuid32(element); switch (uuid){ case BLUETOOTH_PROTOCOL_L2CAP: if (!des_iterator_has_more(&prot_it)) continue; des_iterator_next(&prot_it); de_element_get_uint16(des_iterator_get_element(&prot_it), &hid_interrupt_psm); printf("HID Interrupt PSM: 0x%04x\n", (int) hid_interrupt_psm); break; default: break; } } } break; case BLUETOOTH_ATTRIBUTE_HID_DESCRIPTOR_LIST: for (des_iterator_init(&attribute_list_it, attribute_value); des_iterator_has_more(&attribute_list_it); des_iterator_next(&attribute_list_it)) { if (des_iterator_get_type(&attribute_list_it) != DE_DES) continue; des_element = des_iterator_get_element(&attribute_list_it); for (des_iterator_init(&additional_des_it, des_element); des_iterator_has_more(&additional_des_it); des_iterator_next(&additional_des_it)) { if (des_iterator_get_type(&additional_des_it) != DE_STRING) continue; element = des_iterator_get_element(&additional_des_it); const uint8_t * descriptor = de_get_string(element); hid_descriptor_len = de_get_data_size(element); memcpy(hid_descriptor, descriptor, hid_descriptor_len); printf("HID Descriptor:\n"); printf_hexdump(hid_descriptor, hid_descriptor_len); } } break; default: break; } } } else { fprintf(stderr, "SDP attribute value buffer size exceeded: available %d, required %d\n", attribute_value_buffer_size, sdp_event_query_attribute_byte_get_attribute_length(packet)); } break; case SDP_EVENT_QUERY_COMPLETE: if (!hid_control_psm) { printf("HID Control PSM missing\n"); break; } if (!hid_interrupt_psm) { printf("HID Interrupt PSM missing\n"); break; } printf("Setup HID\n"); status = l2cap_create_channel(packet_handler, remote_addr, hid_control_psm, 48, &l2cap_hid_control_cid); if (status){ printf("Connecting to HID Control failed: 0x%02x\n", status); } break; } }
static void dump_characteristic_value(le_characteristic_value_event_t * event){ printf(" *** characteristic value of length %d *** ", event->blob_length ); printf_hexdump(event->blob , event->blob_length); printf("\n"); }
/* LISTING_START(GAPLEAdvDataParsing): Parsing advertising data */ static void dump_advertisement_data(const uint8_t * adv_data, uint8_t adv_size){ ad_context_t context; bd_addr_t address; uint8_t uuid_128[16]; for (ad_iterator_init(&context, adv_size, (uint8_t *)adv_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){ uint8_t data_type = ad_iterator_get_data_type(&context); uint8_t size = ad_iterator_get_data_len(&context); const uint8_t * data = ad_iterator_get_data(&context); if (data_type > 0 && data_type < 0x1B){ printf(" %s: ", ad_types[data_type]); } int i; // Assigned Numbers GAP switch (data_type){ case 0x01: // Flags // show only first octet, ignore rest for (i=0; i<8;i++){ if (data[0] & (1<<i)){ printf("%s; ", flags[i]); } } break; case 0x02: // Incomplete List of 16-bit Service Class UUIDs case 0x03: // Complete List of 16-bit Service Class UUIDs case 0x14: // List of 16-bit Service Solicitation UUIDs for (i=0; i<size;i+=2){ printf("%02X ", little_endian_read_16(data, i)); } break; case 0x04: // Incomplete List of 32-bit Service Class UUIDs case 0x05: // Complete List of 32-bit Service Class UUIDs for (i=0; i<size;i+=4){ printf("%04"PRIX32, little_endian_read_32(data, i)); } break; case 0x06: // Incomplete List of 128-bit Service Class UUIDs case 0x07: // Complete List of 128-bit Service Class UUIDs case 0x15: // List of 128-bit Service Solicitation UUIDs reverse_128(data, uuid_128); printf("%s", uuid128_to_str(uuid_128)); break; case 0x08: // Shortened Local Name case 0x09: // Complete Local Name for (i=0; i<size;i++){ printf("%c", (char)(data[i])); } break; case 0x0A: // Tx Power Level printf("%d dBm", *(int8_t*)data); break; case 0x12: // Slave Connection Interval Range printf("Connection Interval Min = %u ms, Max = %u ms", little_endian_read_16(data, 0) * 5/4, little_endian_read_16(data, 2) * 5/4); break; case 0x16: // Service Data printf_hexdump(data, size); break; case 0x17: // Public Target Address case 0x18: // Random Target Address reverse_bd_addr(data, address); printf("%s", bd_addr_to_str(address)); break; case 0x19: // Appearance // https://developer.bluetooth.org/gatt/characteristics/Pages/CharacteristicViewer.aspx?u=org.bluetooth.characteristic.gap.appearance.xml printf("%02X", little_endian_read_16(data, 0) ); break; case 0x1A: // Advertising Interval printf("%u ms", little_endian_read_16(data, 0) * 5/8 ); break; case 0x3D: // 3D Information Data printf_hexdump(data, size); break; case 0xFF: // Manufacturer Specific Data break; case 0x0D: // Class of Device (3B) case 0x0E: // Simple Pairing Hash C (16B) case 0x0F: // Simple Pairing Randomizer R (16B) case 0x10: // Device ID case 0x11: // Security Manager TK Value (16B) default: printf("Unknown Advertising Data Type"); break; } printf("\n"); } printf("\n"); }
static void dump_ad_event(ad_event_t * e){ printf(" *** adv. event *** 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); printf_hexdump(e->data, e->length); }
// Bluetooth logic static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ bd_addr_t event_addr; uint8_t rfcomm_channel_nr; uint16_t mtu; uint8_t event_code; // uint8_t channel; uint8_t message_id; switch (packet_type) { case HCI_EVENT_PACKET: switch (packet[0]) { case BTSTACK_EVENT_STATE: // bt stack activated, get started - set local name if (packet[2] == HCI_STATE_WORKING) { hci_send_cmd(&hci_write_local_name, "BlueMSP-Demo"); } break; case HCI_EVENT_COMMAND_COMPLETE: if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){ bt_flip_addr(event_addr, &packet[6]); printf("BD-ADDR: %s\n\r", bd_addr_to_str(event_addr)); break; } if (COMMAND_COMPLETE_EVENT(packet, hci_write_local_name)){ // start ANT init ant_send_cmd(&ant_reset); break; } break; case HCI_EVENT_LINK_KEY_REQUEST: // deny link key request printf("Link key request\n\r"); bt_flip_addr(event_addr, &packet[2]); hci_send_cmd(&hci_link_key_request_negative_reply, &event_addr); break; case HCI_EVENT_PIN_CODE_REQUEST: // inform about pin code request printf("Pin code request - using '0000'\n\r"); bt_flip_addr(event_addr, &packet[2]); hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); break; case RFCOMM_EVENT_INCOMING_CONNECTION: // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) bt_flip_addr(event_addr, &packet[2]); rfcomm_channel_nr = packet[8]; rfcomm_channel_id = READ_BT_16(packet, 9); printf("RFCOMM channel %u requested for %s\n\r", rfcomm_channel_nr, bd_addr_to_str(event_addr)); rfcomm_accept_connection_internal(rfcomm_channel_id); break; case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) if (packet[2]) { printf("RFCOMM channel open failed, status %u\n\r", packet[2]); } else { rfcomm_channel_id = READ_BT_16(packet, 12); mtu = READ_BT_16(packet, 14); printf("\n\rRFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n\r", rfcomm_channel_id, mtu); } break; case RFCOMM_EVENT_CHANNEL_CLOSED: rfcomm_channel_id = 0; break; case 0xff: // vendor specific -> ANT // vendor specific ant message if (packet[2] != 0x00) break; if (packet[3] != 0x05) break; event_code = packet[7]; printf("ANT Event: "); printf_hexdump(packet, size); switch(event_code){ case MESG_STARTUP_MESG_ID: // 2. assign channel ant_send_cmd(&ant_assign_channel, 0, 0x00, 0); break; case MESG_RESPONSE_EVENT_ID: // channel = packet[8]; message_id = packet[9]; switch (message_id){ case MESG_ASSIGN_CHANNEL_ID: // 3. set channel ID ant_send_cmd(&ant_channel_id, 0, 33, 1, 1); break; case MESG_CHANNEL_ID_ID: // 4. open channel ant_send_cmd(&ant_open_channel, 0); } break; default: break; } break; default: break; } break; default: break; } }
void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ bd_addr_t event_addr; uint16_t rfcomm_channel_nr; switch (packet_type) { case RFCOMM_DATA_PACKET: printf("Received RFCOMM data on channel id %u, size %u\n", channel, size); printf_hexdump(packet, size); bt_send_rfcomm(channel, packet, size); break; case HCI_EVENT_PACKET: switch (packet[0]) { case BTSTACK_EVENT_POWERON_FAILED: // handle HCI init failure printf("HCI Init failed - make sure you have turned off Bluetooth in the System Settings\n"); exit(1); break; case BTSTACK_EVENT_STATE: // bt stack activated, get started if (packet[2] == HCI_STATE_WORKING) { // get persistent RFCOMM channel printf("HCI_STATE_WORKING\n"); bt_send_cmd(&rfcomm_persistent_channel_for_service, "ch.ringwald.btstack.rfcomm-test"); } break; case RFCOMM_EVENT_PERSISTENT_CHANNEL: rfcomm_channel_nr = packet[3]; printf("RFCOMM channel %u was assigned by BTdaemon\n", rfcomm_channel_nr); bt_send_cmd(&rfcomm_register_service, rfcomm_channel_nr, 0xffff); // reserved channel, mtu limited by l2cap break; case RFCOMM_EVENT_SERVICE_REGISTERED: printf("RFCOMM_EVENT_SERVICE_REGISTERED\n"); rfcomm_channel_nr = packet[3]; // register SDP for our SPP sdp_create_spp_service( spp_service_buffer, rfcomm_channel_nr, "RFCOMM Test"); bt_send_cmd(&sdp_register_service_record, spp_service_buffer); bt_send_cmd(&btstack_set_discoverable, 1); break; case RFCOMM_EVENT_CREDITS: sprintf((char*)test_data, "\n\r\n\r-> %09u <- ", counter++); bt_send_rfcomm(rfcomm_channel_id, test_data, mtu); break; case HCI_EVENT_PIN_CODE_REQUEST: // inform about pin code request printf("Using PIN 0000\n"); bt_flip_addr(event_addr, &packet[2]); bt_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); break; case RFCOMM_EVENT_INCOMING_CONNECTION: // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) bt_flip_addr(event_addr, &packet[2]); rfcomm_channel_nr = packet[8]; rfcomm_channel_id = READ_BT_16(packet, 9); printf("RFCOMM channel %u requested for %s\n", rfcomm_channel_nr, bd_addr_to_str(event_addr)); bt_send_cmd(&rfcomm_accept_connection, rfcomm_channel_id); break; case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: // data: event(8), len(8), status (8), address (48), handle(16), server channel(8), rfcomm_cid(16), max frame size(16) if (packet[2]) { printf("RFCOMM channel open failed, status %u\n", packet[2]); } else { // data: event(8), len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16) rfcomm_channel_id = READ_BT_16(packet, 12); mtu = READ_BT_16(packet, 14); printf("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n", rfcomm_channel_id, mtu); } break; case HCI_EVENT_DISCONNECTION_COMPLETE: // connection closed -> quit test app printf("Basebank connection closed\n"); break; default: break; } break; default: break; } }
static void handle_gatt_client_event(le_event_t * event) { uint8_t * packet = (uint8_t*) event; int connection_encrypted; // handle connect / disconncet events first switch (packet[0]) { case HCI_EVENT_LE_META: switch (packet[2]) { case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: gc_handle = READ_BT_16(packet, 4); printf("Connection handle 0x%04x, request encryption\n", gc_handle); // we need to be paired to enable notifications tc_state = TC_W4_ENCRYPTED_CONNECTION; sm_send_security_request(gc_handle); break; default: break; } return; case HCI_EVENT_ENCRYPTION_CHANGE: if (gc_handle != READ_BT_16(packet, 3)) return; connection_encrypted = packet[5]; log_info("Encryption state change: %u", connection_encrypted); if (!connection_encrypted) return; if (tc_state != TC_W4_ENCRYPTED_CONNECTION) return; // let's start printf("\nANCS Client - CONNECTED, discover ANCS service\n"); tc_state = TC_W4_SERVICE_RESULT; gatt_client_discover_primary_services_by_uuid128(gc_id, gc_handle, ancs_service_uuid); return; case HCI_EVENT_DISCONNECTION_COMPLETE: notify_client(ANCS_CLIENT_DISCONNECTED); return; default: break; } le_characteristic_t characteristic; le_characteristic_value_event_t * value_event; switch(tc_state) { case TC_W4_SERVICE_RESULT: switch(event->type) { case GATT_SERVICE_QUERY_RESULT: ancs_service = ((le_service_event_t *) event)->service; ancs_service_found = 1; break; case GATT_QUERY_COMPLETE: if (!ancs_service_found) { printf("ANCS Service not found"); tc_state = TC_IDLE; break; } tc_state = TC_W4_CHARACTERISTIC_RESULT; printf("ANCS Client - Discover characteristics for ANCS SERVICE \n"); gatt_client_discover_characteristics_for_service(gc_id, gc_handle, &ancs_service); break; default: break; } break; case TC_W4_CHARACTERISTIC_RESULT: switch(event->type) { case GATT_CHARACTERISTIC_QUERY_RESULT: characteristic = ((le_characteristic_event_t *) event)->characteristic; if (memcmp(characteristic.uuid128, ancs_notification_source_uuid, 16) == 0) { printf("ANCS Notification Source Characterisic found\n"); ancs_notification_source_characteristic = characteristic; ancs_characteristcs++; break; } if (memcmp(characteristic.uuid128, ancs_control_point_uuid, 16) == 0) { printf("ANCS Control Point found\n"); ancs_control_point_characteristic = characteristic; ancs_characteristcs++; break; } if (memcmp(characteristic.uuid128, ancs_data_source_uuid, 16) == 0) { printf("ANCS Data Source Characterisic found\n"); ancs_data_source_characteristic = characteristic; ancs_characteristcs++; break; } break; case GATT_QUERY_COMPLETE: printf("ANCS Characteristcs count %u\n", ancs_characteristcs); tc_state = TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED; gatt_client_write_client_characteristic_configuration(gc_id, gc_handle, &ancs_notification_source_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); break; default: break; } break; case TC_W4_NOTIFICATION_SOURCE_SUBSCRIBED: switch(event->type) { case GATT_QUERY_COMPLETE: printf("ANCS Notification Source subscribed\n"); tc_state = TC_W4_DATA_SOURCE_SUBSCRIBED; gatt_client_write_client_characteristic_configuration(gc_id, gc_handle, &ancs_data_source_characteristic, GATT_CLIENT_CHARACTERISTICS_CONFIGURATION_NOTIFICATION); break; default: break; } break; case TC_W4_DATA_SOURCE_SUBSCRIBED: switch(event->type) { case GATT_QUERY_COMPLETE: printf("ANCS Data Source subscribed\n"); tc_state = TC_SUBSCRIBED; notify_client(ANCS_CLIENT_CONNECTED); break; default: break; } break; case TC_SUBSCRIBED: if ( event->type != GATT_NOTIFICATION && event->type != GATT_INDICATION ) break; value_event = (le_characteristic_value_event_t *) event; if (value_event->value_handle == ancs_data_source_characteristic.value_handle) { int i; for (i=0; i<value_event->blob_length; i++) { ancs_chunk_parser_handle_byte(value_event->blob[i]); } } else if (value_event->value_handle == ancs_notification_source_characteristic.value_handle) { ancs_notification_uid = READ_BT_32(value_event->blob, 4); printf("Notification received: EventID %02x, EventFlags %02x, CategoryID %02x, CategoryCount %u, UID %04x\n", value_event->blob[0], value_event->blob[1], value_event->blob[2], value_event->blob[3], (int) ancs_notification_uid); static uint8_t get_notification_attributes[] = {0, 0,0,0,0, 0, 1,32,0, 2,32,0, 3,32,0, 4, 5}; bt_store_32(get_notification_attributes, 1, ancs_notification_uid); ancs_notification_uid = 0; ancs_chunk_parser_init(); gatt_client_write_value_of_characteristic(gc_id, gc_handle, ancs_control_point_characteristic.value_handle, sizeof(get_notification_attributes), get_notification_attributes); } else { printf("Unknown Source: "); printf_hexdump(value_event->blob , value_event->blob_length); } break; default: break; } // app_run(); }
// write requests static int att_write_callback(uint16_t con_handle, uint16_t attribute_handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size){ printf("WRITE Callback, handle %04x, mode %u, offset %u, data: ", handle, transaction_mode, offset); printf_hexdump(buffer, buffer_size); uint16_t uuid16 = att_uuid_for_handle(handle); switch (uuid16){ case 0x2902: client_configuration = buffer[0]; client_configuration_handle = handle; printf("Client Configuration set to %u for handle %04x\n", client_configuration, handle); return 0; // ok case 0x2a00: memcpy(gap_device_name, buffer, buffer_size); gap_device_name[buffer_size]=0; printf("Setting device name to '%s'\n", gap_device_name); return 0; case 0x2a01: gap_appearance = READ_BT_16(buffer, 0); printf("Setting appearance to 0x%04x'\n", gap_appearance); return 0; case 0x2a02: gap_privacy = buffer[0]; printf("Setting privacy to 0x%04x'\n", gap_privacy); update_advertisements(); return 0; case 0x2A03: bt_flip_addr(gap_reconnection_address, buffer); printf("Setting Reconnection Address to %s\n", bd_addr_to_str(gap_reconnection_address)); return 0; // handle device name // handle appearance } // check transaction mode int attributes_index; int writes_index; switch (transaction_mode){ case ATT_TRANSACTION_MODE_NONE: attributes_index = att_attribute_for_handle(handle); if (attributes_index < 0){ attributes_index = att_attribute_for_handle(0); if (attributes_index < 0) return 0; // ok, but we couldn't store it (our fault) att_attributes[attributes_index].handle = handle; // not written before uint8_t * att_value; uint16_t att_value_len; if (att_default_value_long){ att_value = (uint8_t*) default_value_long; att_value_len = strlen(default_value_long); } else { att_value = (uint8_t*) default_value_short; att_value_len = strlen(default_value_short); } att_attributes[attributes_index].len = att_value_len; } if (buffer_size > att_attributes[attributes_index].len) return ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH; att_attributes[attributes_index].len = buffer_size; memcpy(att_attributes[attributes_index].value, buffer, buffer_size); break; case ATT_TRANSACTION_MODE_ACTIVE: writes_index = att_write_queue_for_handle(handle); if (writes_index < 0) return ATT_ERROR_PREPARE_QUEUE_FULL; if (offset > att_write_queues[writes_index].len) return ATT_ERROR_INVALID_OFFSET; if (buffer_size + offset > ATT_VALUE_MAX_LEN) return ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LENGTH; att_write_queues[writes_index].len = buffer_size + offset; memcpy(&(att_write_queues[writes_index].value[offset]), buffer, buffer_size); break; case ATT_TRANSACTION_MODE_EXECUTE: for (writes_index=0 ; writes_index<ATT_NUM_WRITE_QUEUES ; writes_index++){ handle = att_write_queues[writes_index].handle; if (handle == 0) continue; attributes_index = att_attribute_for_handle(handle); if (attributes_index < 0){ attributes_index = att_attribute_for_handle(0); if (attributes_index < 0) continue; att_attributes[attributes_index].handle = handle; } att_attributes[attributes_index].len = att_write_queues[writes_index].len; memcpy(att_attributes[attributes_index].value, att_write_queues[writes_index].value, att_write_queues[writes_index].len); } att_write_queue_init(); break; case ATT_TRANSACTION_MODE_CANCEL: att_write_queue_init(); break; } return 0; }
// ATT Client Read Callback for Dynamic Data // - if buffer == NULL, don't copy data, just return size of value // - if buffer != NULL, copy data and return number bytes copied // @param offset defines start of attribute value static uint16_t att_read_callback(uint16_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ printf("READ Callback, handle %04x, offset %u, buffer size %u\n", handle, offset, buffer_size); uint16_t att_value_len; uint16_t uuid16 = att_uuid_for_handle(handle); switch (uuid16){ case 0x2902: if (buffer) { buffer[0] = client_configuration; } return 1; case 0x2a00: att_value_len = strlen(gap_device_name); if (buffer) { memcpy(buffer, gap_device_name, att_value_len); } return att_value_len; case 0x2a01: if (buffer){ bt_store_16(buffer, 0, gap_appearance); } return 2; case 0x2a02: if (buffer){ buffer[0] = gap_privacy; } return 1; case 0x2A03: if (buffer) { bt_flip_addr(buffer, gap_reconnection_address); } return 6; // handle device name // handle appearance default: break; } // find attribute int index = att_attribute_for_handle(handle); uint8_t * att_value; if (index < 0){ // not written before if (att_default_value_long){ att_value = (uint8_t*) default_value_long; att_value_len = strlen(default_value_long); } else { att_value = (uint8_t*) default_value_short; att_value_len = strlen(default_value_short); } } else { att_value = att_attributes[index].value; att_value_len = att_attributes[index].len; } printf("Attribute len %u, data: ", att_value_len); printf_hexdump(att_value, att_value_len); // assert offset <= att_value_len if (offset > att_value_len) { return 0; } uint16_t bytes_to_copy = att_value_len - offset; if (!buffer) return bytes_to_copy; if (bytes_to_copy > buffer_size){ bytes_to_copy = buffer_size; } memcpy(buffer, &att_value[offset], bytes_to_copy); return bytes_to_copy; }
static void cmac_done(uint8_t * hash){ memcpy(cmac_hash, hash, 16); printf("cmac hash: "); printf_hexdump(hash, 16); cmac_hash_received = 1; }
// ATT Client Read Callback for Dynamic Data // - if buffer == NULL, don't copy data, just return size of value // - if buffer != NULL, copy data and return number bytes copied // @param offset defines start of attribute value static uint16_t att_read_callback(uint16_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ printf("READ Callback, handle %04x, offset %u, buffer size %u\n", attribute_handle, offset, buffer_size); uint16_t att_value_len; switch (attribute_handle){ case ATT_CHARACTERISTIC_GAP_DEVICE_NAME_01_VALUE_HANDLE: att_value_len = strlen(gap_device_name); if (buffer) { memcpy(buffer, gap_device_name, att_value_len); } return att_value_len; case ATT_CHARACTERISTIC_GAP_APPEARANCE_01_VALUE_HANDLE: if (buffer){ bt_store_16(buffer, 0, gap_appearance); } return 2; case ATT_CHARACTERISTIC_GAP_PERIPHERAL_PRIVACY_FLAG_01_VALUE_HANDLE: if (buffer){ buffer[0] = gap_privacy; } return 1; case ATT_CHARACTERISTIC_GAP_RECONNECTION_ADDRESS_01_VALUE_HANDLE: if (buffer) { bt_flip_addr(buffer, gap_reconnection_address); } return 6; default: break; } uint16_t uuid16 = att_uuid_for_handle(handle); if (uuid16){ printf("Resolved to UUID %04x\n", uuid16); switch (uuid16){ case 0x2902: if (buffer) { buffer[0] = client_configuration; } return 1; default: break; } } // find attribute int index = att_attribute_for_handle(handle); uint8_t * att_value; if (index < 0){ // not written before if (att_default_value_long){ att_value = (uint8_t*) default_value_long; att_value_len = strlen(default_value_long); } else { att_value = (uint8_t*) default_value_short; att_value_len = strlen(default_value_short); } } else { att_value = att_attributes[index].value; att_value_len = att_attributes[index].len; } printf("Attribute len %u, data: ", att_value_len); printf_hexdump(att_value, att_value_len); // assert offset <= att_value_len if (offset > att_value_len) { return 0; } uint16_t bytes_to_copy = att_value_len - offset; if (!buffer) return bytes_to_copy; if (bytes_to_copy > buffer_size){ bytes_to_copy = buffer_size; } memcpy(buffer, &att_value[offset], bytes_to_copy); return bytes_to_copy; }
static void dump_characteristic_value(uint8_t * blob, uint16_t blob_length){ printf(" * characteristic value of length %d *** ", blob_length ); printf_hexdump(blob , blob_length); printf("\n"); }
/* LISTING_START(packetHandler): Packet Handler */ static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) { /* LISTING_PAUSE */ uint8_t event; bd_addr_t event_addr; uint8_t status; uint16_t l2cap_cid; /* LISTING_RESUME */ switch (packet_type) { case HCI_EVENT_PACKET: event = hci_event_packet_get_type(packet); switch (event) { /* @text When BTSTACK_EVENT_STATE with state HCI_STATE_WORKING * is received and the example is started in client mode, the remote SDP HID query is started. */ case BTSTACK_EVENT_STATE: if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ printf("Start SDP HID query for remote HID Device.\n"); sdp_client_query_uuid16(&handle_sdp_client_query_result, remote_addr, BLUETOOTH_SERVICE_CLASS_HUMAN_INTERFACE_DEVICE_SERVICE); } break; /* LISTING_PAUSE */ case HCI_EVENT_PIN_CODE_REQUEST: // inform about pin code request printf("Pin code request - using '0000'\n"); hci_event_pin_code_request_get_bd_addr(packet, event_addr); gap_pin_code_response(event_addr, "0000"); break; case HCI_EVENT_USER_CONFIRMATION_REQUEST: // inform about user confirmation request printf("SSP User Confirmation Request with numeric value '%"PRIu32"'\n", little_endian_read_32(packet, 8)); printf("SSP User Confirmation Auto accept\n"); break; /* LISTING_RESUME */ case L2CAP_EVENT_CHANNEL_OPENED: status = packet[2]; if (status){ printf("L2CAP Connection failed: 0x%02x\n", status); break; } l2cap_cid = little_endian_read_16(packet, 13); if (!l2cap_cid) break; if (l2cap_cid == l2cap_hid_control_cid){ status = l2cap_create_channel(packet_handler, remote_addr, hid_interrupt_psm, 48, &l2cap_hid_interrupt_cid); if (status){ printf("Connecting to HID Control failed: 0x%02x\n", status); break; } } if (l2cap_cid == l2cap_hid_interrupt_cid){ printf("HID Connection established\n"); } break; default: break; } break; case L2CAP_DATA_PACKET: // for now, just dump incoming data if (channel == l2cap_hid_interrupt_cid){ hid_host_handle_interrupt_report(packet, size); } else if (channel == l2cap_hid_control_cid){ printf("HID Control: "); printf_hexdump(packet, size); } else { break; } default: break; } }
static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(channel); UNUSED(size); bd_addr_t event_addr; uint16_t psm; switch (packet_type){ case HCI_EVENT_PACKET: switch (packet[0]) { case BTSTACK_EVENT_STATE: // bt stack activated, get started if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ printf("BTstack L2CAP Test Ready\n"); show_usage(); } break; case HCI_EVENT_CONNECTION_COMPLETE: handle = hci_event_connection_complete_get_connection_handle(packet); break; case L2CAP_EVENT_CHANNEL_OPENED: // inform about new l2cap connection reverse_bd_addr(&packet[3], event_addr); psm = little_endian_read_16(packet, 11); local_cid = little_endian_read_16(packet, 13); handle = little_endian_read_16(packet, 9); if (packet[2] == 0) { printf("Channel successfully opened: %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n", bd_addr_to_str(event_addr), handle, psm, local_cid, little_endian_read_16(packet, 15)); } else { printf("L2CAP connection to device %s failed. status code %u\n", bd_addr_to_str(event_addr), packet[2]); } break; case L2CAP_EVENT_INCOMING_CONNECTION: { uint16_t l2cap_cid = little_endian_read_16(packet, 12); if (l2cap_ertm){ printf("L2CAP Accepting incoming connection request in ERTM\n"); l2cap_accept_ertm_connection(l2cap_cid, &ertm_config, ertm_buffer, sizeof(ertm_buffer)); } else { printf("L2CAP Accepting incoming connection request in Basic Mode\n"); l2cap_accept_connection(l2cap_cid); } break; } default: break; } break; case L2CAP_DATA_PACKET: printf_hexdump(packet, size); break; } if (packet_type != HCI_EVENT_PACKET) return; }