void l2cap_signaling_handler_channel(l2cap_channel_t *channel, uint8_t *command){ uint8_t code = command[L2CAP_SIGNALING_COMMAND_CODE_OFFSET]; uint8_t identifier = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET]; uint16_t result = 0; log_info("L2CAP signaling handler code %u, state %u\n", code, channel->state); // handle DISCONNECT REQUESTS seperately if (code == DISCONNECTION_REQUEST){ switch (channel->state){ case L2CAP_STATE_CONFIG: case L2CAP_STATE_OPEN: case L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST: case L2CAP_STATE_WAIT_DISCONNECT: l2cap_handle_disconnect_request(channel, identifier); break; default: // ignore in other states break; } return; } // @STATEMACHINE(l2cap) switch (channel->state) { case L2CAP_STATE_WAIT_CONNECT_RSP: switch (code){ case CONNECTION_RESPONSE: result = READ_BT_16 (command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+4); switch (result) { case 0: // successful connection channel->remote_cid = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET); channel->state = L2CAP_STATE_CONFIG; channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ); break; case 1: // connection pending. get some coffee break; default: // channel closed channel->state = L2CAP_STATE_CLOSED; // map l2cap connection response result to BTstack status enumeration l2cap_emit_channel_opened(channel, L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL + result); // drop link key if security block if (L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL + result == L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY){ hci_drop_link_key_for_bd_addr(&channel->address); } // discard channel linked_list_remove(&l2cap_channels, (linked_item_t *) channel); btstack_memory_l2cap_channel_free(channel); break; } break; default: //@TODO: implement other signaling packets break; } break; case L2CAP_STATE_CONFIG: switch (code) { case CONFIGURE_REQUEST: channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ); channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP); l2cap_signaling_handle_configure_request(channel, command); break; case CONFIGURE_RESPONSE: channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP); break; default: break; } if (l2cap_channel_ready_for_open(channel)){ // for open: channel->state = L2CAP_STATE_OPEN; l2cap_emit_channel_opened(channel, 0); l2cap_emit_credits(channel, 1); } break; case L2CAP_STATE_WAIT_DISCONNECT: switch (code) { case DISCONNECTION_RESPONSE: l2cap_finialize_channel_close(channel); break; default: //@TODO: implement other signaling packets break; } break; case L2CAP_STATE_CLOSED: // @TODO handle incoming requests break; case L2CAP_STATE_OPEN: //@TODO: implement other signaling packets, e.g. re-configure break; default: break; } // log_info("new state %u\n", channel->state); }
static void event_handler(uint8_t *packet, int size){ bd_addr_t addr; uint8_t link_type; hci_con_handle_t handle; hci_connection_t * conn; int i; switch (packet[0]) { case HCI_EVENT_COMMAND_COMPLETE: // get num cmd packets // log_info("HCI_EVENT_COMMAND_COMPLETE cmds old %u - new %u\n", hci_stack.num_cmd_packets, packet[2]); hci_stack.num_cmd_packets = packet[2]; if (COMMAND_COMPLETE_EVENT(packet, hci_read_buffer_size)){ // from offset 5 // status // "The HC_ACL_Data_Packet_Length return parameter will be used to determine the size of the L2CAP segments contained in ACL Data Packets" hci_stack.acl_data_packet_length = READ_BT_16(packet, 6); // ignore: SCO data packet len (8) hci_stack.total_num_acl_packets = packet[9]; // ignore: total num SCO packets if (hci_stack.state == HCI_STATE_INITIALIZING){ // determine usable ACL payload size if (HCI_ACL_PAYLOAD_SIZE < hci_stack.acl_data_packet_length){ hci_stack.acl_data_packet_length = HCI_ACL_PAYLOAD_SIZE; } // determine usable ACL packet types hci_stack.packet_types = hci_acl_packet_types_for_buffer_size(hci_stack.acl_data_packet_length); log_error("hci_read_buffer_size: used size %u, count %u, packet types %04x\n", hci_stack.acl_data_packet_length, hci_stack.total_num_acl_packets, hci_stack.packet_types); } } if (COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){ hci_emit_discoverable_enabled(hci_stack.discoverable); } break; case HCI_EVENT_COMMAND_STATUS: // get num cmd packets // log_info("HCI_EVENT_COMMAND_STATUS cmds - old %u - new %u\n", hci_stack.num_cmd_packets, packet[3]); hci_stack.num_cmd_packets = packet[3]; break; case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS: for (i=0; i<packet[2];i++){ handle = READ_BT_16(packet, 3 + 2*i); uint16_t num_packets = READ_BT_16(packet, 3 + packet[2]*2 + 2*i); conn = connection_for_handle(handle); if (!conn){ log_error("hci_number_completed_packet lists unused con handle %u\n", handle); continue; } conn->num_acl_packets_sent -= num_packets; // log_info("hci_number_completed_packet %u processed for handle %u, outstanding %u\n", num_packets, handle, conn->num_acl_packets_sent); } break; case HCI_EVENT_CONNECTION_REQUEST: bt_flip_addr(addr, &packet[2]); // TODO: eval COD 8-10 link_type = packet[11]; log_info("Connection_incoming: %s, type %u\n", bd_addr_to_str(addr), link_type); if (link_type == 1) { // ACL conn = connection_for_address(addr); if (!conn) { conn = create_connection_for_addr(addr); } if (!conn) { // CONNECTION REJECTED DUE TO LIMITED RESOURCES (0X0D) hci_stack.decline_reason = 0x0d; BD_ADDR_COPY(hci_stack.decline_addr, addr); break; } conn->state = RECEIVED_CONNECTION_REQUEST; hci_run(); } else { // SYNCHRONOUS CONNECTION LIMIT TO A DEVICE EXCEEDED (0X0A) hci_stack.decline_reason = 0x0a; BD_ADDR_COPY(hci_stack.decline_addr, addr); } break; case HCI_EVENT_CONNECTION_COMPLETE: // Connection management bt_flip_addr(addr, &packet[5]); log_info("Connection_complete (status=%u) %s\n", packet[2], bd_addr_to_str(addr)); conn = connection_for_address(addr); if (conn) { if (!packet[2]){ conn->state = OPEN; conn->con_handle = READ_BT_16(packet, 3); #ifdef HAVE_TICK // restart timer run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS); run_loop_add_timer(&conn->timeout); #endif log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address)); hci_emit_nr_connections_changed(); } else { // connection failed, remove entry linked_list_remove(&hci_stack.connections, (linked_item_t *) conn); btstack_memory_hci_connection_free( conn ); // if authentication error, also delete link key if (packet[2] == 0x05) { hci_drop_link_key_for_bd_addr(&addr); } } } break; case HCI_EVENT_LINK_KEY_REQUEST: log_info("HCI_EVENT_LINK_KEY_REQUEST\n"); hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_REQUEST); if (!hci_stack.remote_device_db) break; hci_add_connection_flags_for_flipped_bd_addr(&packet[2], HANDLE_LINK_KEY_REQUEST); hci_run(); // request already answered return; case HCI_EVENT_LINK_KEY_NOTIFICATION: hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_NOTIFICATION); if (!hci_stack.remote_device_db) break; bt_flip_addr(addr, &packet[2]); hci_stack.remote_device_db->put_link_key(&addr, (link_key_t *) &packet[8]); // still forward event to allow dismiss of pairing dialog break; case HCI_EVENT_PIN_CODE_REQUEST: hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_PIN_CODE_REQUEST); break; #ifndef EMBEDDED case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE: if (!hci_stack.remote_device_db) break; if (packet[2]) break; // status not ok bt_flip_addr(addr, &packet[3]); // fix for invalid remote names - terminate on 0xff for (i=0; i<248;i++){ if (packet[9+i] == 0xff){ packet[9+i] = 0; break; } } memset(&device_name, 0, sizeof(device_name_t)); strncpy((char*) device_name, (char*) &packet[9], 248); hci_stack.remote_device_db->put_name(&addr, &device_name); break; case HCI_EVENT_INQUIRY_RESULT: case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI: if (!hci_stack.remote_device_db) break; // first send inq result packet hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size); // then send cached remote names for (i=0; i<packet[2];i++){ bt_flip_addr(addr, &packet[3+i*6]); if (hci_stack.remote_device_db->get_name(&addr, &device_name)){ hci_emit_remote_name_cached(&addr, &device_name); } } return; #endif case HCI_EVENT_DISCONNECTION_COMPLETE: if (!packet[2]){ handle = READ_BT_16(packet, 3); hci_connection_t * conn = connection_for_handle(handle); if (conn) { hci_shutdown_connection(conn); } } break; case HCI_EVENT_HARDWARE_ERROR: if(hci_stack.control->hw_error){ (*hci_stack.control->hw_error)(); } break; #ifdef HAVE_BLE case HCI_EVENT_LE_META: switch (packet[2]) { case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: // Connection management bt_flip_addr(addr, &packet[8]); log_info("LE Connection_complete (status=%u) %s\n", packet[3], bd_addr_to_str(addr)); // LE connections are auto-accepted, so just create a connection if there isn't one already conn = connection_for_address(addr); if (packet[3]){ if (conn){ // outgoing connection failed, remove entry linked_list_remove(&hci_stack.connections, (linked_item_t *) conn); btstack_memory_hci_connection_free( conn ); } // if authentication error, also delete link key if (packet[3] == 0x05) { hci_drop_link_key_for_bd_addr(&addr); } break; } if (!conn){ conn = create_connection_for_addr(addr); } if (!conn){ // no memory break; } conn->state = OPEN; conn->con_handle = READ_BT_16(packet, 4); // TODO: store - role, peer address type, conn_interval, conn_latency, supervision timeout, master clock // restart timer // run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS); // run_loop_add_timer(&conn->timeout); log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address)); hci_emit_nr_connections_changed(); break; default: break; } break; #endif default: break; } // handle BT initialization if (hci_stack.state == HCI_STATE_INITIALIZING){ // handle H4 synchronization loss on restart // if (hci_stack.substate == 1 && packet[0] == HCI_EVENT_HARDWARE_ERROR){ // hci_stack.substate = 0; // } // handle normal init sequence if (hci_stack.substate % 2){ // odd: waiting for event if (packet[0] == HCI_EVENT_COMMAND_COMPLETE){ hci_stack.substate++; } } } // help with BT sleep if (hci_stack.state == HCI_STATE_FALLING_ASLEEP && hci_stack.substate == 1 && COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){ hci_stack.substate++; } hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size); // execute main loop hci_run(); }
int stdin_process(struct data_source *ds){ char buffer; read(ds->fd, &buffer, 1); // passkey input if (ui_digits_for_passkey){ if (buffer < '0' || buffer > '9') return 0; printf("%c", buffer); fflush(stdout); ui_passkey = ui_passkey * 10 + buffer - '0'; ui_digits_for_passkey--; if (ui_digits_for_passkey == 0){ printf("\nSending Passkey '%06u'\n", ui_passkey); hci_send_cmd(&hci_user_passkey_request_reply, remote, ui_passkey); } return 0; } if (ui_chars_for_pin){ printf("%c", buffer); fflush(stdout); if (buffer == '\n'){ printf("\nSending Pin '%s'\n", ui_pin); hci_send_cmd(&hci_pin_code_request_reply, remote, ui_pin_offset, ui_pin); } else { ui_pin[ui_pin_offset++] = buffer; } return 0; } switch (buffer){ case 'c': gap_connectable = 0; hci_connectable_control(0); show_usage(); break; case 'C': gap_connectable = 1; hci_connectable_control(1); show_usage(); break; case 'd': gap_discoverable = 0; hci_discoverable_control(0); show_usage(); break; case 'D': gap_discoverable = 1; hci_discoverable_control(1); show_usage(); break; case 'b': gap_bondable = 0; // gap_set_bondable_mode(0); update_auth_req(); show_usage(); break; case 'B': gap_bondable = 1; // gap_set_bondable_mode(1); update_auth_req(); show_usage(); break; case 'm': gap_mitm_protection = 0; update_auth_req(); show_usage(); break; case 'M': gap_mitm_protection = 1; update_auth_req(); show_usage(); break; case '<': gap_dedicated_bonding_mode = 0; update_auth_req(); show_usage(); break; case '>': gap_dedicated_bonding_mode = 1; update_auth_req(); show_usage(); break; case 'e': gap_io_capabilities = "IO_CAPABILITY_DISPLAY_ONLY"; hci_ssp_set_io_capability(IO_CAPABILITY_DISPLAY_ONLY); show_usage(); break; case 'f': gap_io_capabilities = "IO_CAPABILITY_DISPLAY_YES_NO"; hci_ssp_set_io_capability(IO_CAPABILITY_DISPLAY_YES_NO); show_usage(); break; case 'g': gap_io_capabilities = "IO_CAPABILITY_NO_INPUT_NO_OUTPUT"; hci_ssp_set_io_capability(IO_CAPABILITY_NO_INPUT_NO_OUTPUT); show_usage(); break; case 'h': gap_io_capabilities = "IO_CAPABILITY_KEYBOARD_ONLY"; hci_ssp_set_io_capability(IO_CAPABILITY_KEYBOARD_ONLY); show_usage(); break; case 'i': start_scan(); break; case 'j': printf("Start dedicated bonding to %s using MITM %u\n", bd_addr_to_str(remote), gap_mitm_protection); gap_dedicated_bonding(remote, gap_mitm_protection); break; case 'z': printf("Start dedicated bonding to %s using legacy pairing\n", bd_addr_to_str(remote)); gap_dedicated_bonding(remote, gap_mitm_protection); break; case 'y': printf("Disabling SSP for this session\n"); hci_send_cmd(&hci_write_simple_pairing_mode, 0); break; case 'k': printf("Start SDP query for SPP service\n"); sdp_query_rfcomm_channel_and_name_for_uuid(remote_rfcomm, 0x1101); break; case 't': printf("Terminate connection with handle 0x%04x\n", handle); hci_send_cmd(&hci_disconnect, handle, 0x13); // remote closed connection break; case 'p': printf("Creating HCI Connection to %s\n", bd_addr_to_str(remote)); hci_send_cmd(&hci_create_connection, remote, hci_usable_acl_packet_types(), 0, 0, 0, 1); break; // printf("Creating L2CAP Connection to %s, PSM SDP\n", bd_addr_to_str(remote)); // l2cap_create_channel_internal(NULL, packet_handler, remote, PSM_SDP, 100); // break; // case 'u': // printf("Creating L2CAP Connection to %s, PSM 3\n", bd_addr_to_str(remote)); // l2cap_create_channel_internal(NULL, packet_handler, remote, 3, 100); // break; case 'q': printf("Send L2CAP Data\n"); l2cap_send_internal(local_cid, (uint8_t *) "0123456789", 10); break; case 'r': printf("Send L2CAP ECHO Request\n"); l2cap_send_echo_request(handle, (uint8_t *) "Hello World!", 13); break; case 's': printf("L2CAP Channel Closed\n"); l2cap_disconnect_internal(local_cid, 0); break; case 'x': printf("Outgoing L2CAP Channels to SDP will also require SSP\n"); l2cap_require_security_level_2_for_outgoing_sdp(); break; case 'l': printf("Creating RFCOMM Channel to %s #%u\n", bd_addr_to_str(remote_rfcomm), rfcomm_channel_nr); rfcomm_create_channel_internal(NULL, remote_rfcomm, rfcomm_channel_nr); break; case 'n': printf("Send RFCOMM Data\n"); // mtu < 60 rfcomm_send_internal(rfcomm_channel_id, (uint8_t *) "012345678901234567890123456789012345678901234567890123456789", mtu); break; case 'u': printf("Sending RLS indicating framing error\n"); // mtu < 60 rfcomm_send_local_line_status(rfcomm_channel_id, 9); break; case 'v': printf("Sending RPN CMD to select 115200 baud\n"); // mtu < 60 rfcomm_send_port_configuration(rfcomm_channel_id, RPN_BAUD_115200, RPN_DATA_BITS_8, RPN_STOP_BITS_1_0, RPN_PARITY_NONE, 0); break; case 'w': printf("Sending RPN REQ to query remote port settings\n"); // mtu < 60 rfcomm_query_port_configuration(rfcomm_channel_id); break; case 'o': printf("RFCOMM Channel Closed\n"); rfcomm_disconnect_internal(rfcomm_channel_id); rfcomm_channel_id = 0; break; case '+': printf("Initiate SSP on current connection\n"); gap_request_security_level(handle, LEVEL_2); break; case '*': printf("Sending SSP User Confirmation for %s\n", bd_addr_to_str(remote)); hci_send_cmd(&hci_user_confirmation_request_reply, remote); break; case '=': printf("Deleting Link Key for %s\n", bd_addr_to_str(remote)); hci_drop_link_key_for_bd_addr(remote); break; case 'U': printf("Sending UCD data on handle 0x%04x\n", handle); send_ucd_packet(); break; case 'Q': printf("Closing HCI Connection to handle 0x%04x\n", handle); gap_disconnect(handle); break; default: show_usage(); break; } return 0; }