diff --git a/example/embedded/Makefile.inc b/example/embedded/Makefile.inc index aad391311..dfdbd4b46 100644 --- a/example/embedded/Makefile.inc +++ b/example/embedded/Makefile.inc @@ -9,48 +9,48 @@ CFLAGS += -I${BTSTACK_ROOT}/include CFLAGS += -I${BTSTACK_ROOT}/src CORE += \ - btstack_memory.c \ - linked_list.c \ - memory_pool.c \ - run_loop.c \ + btstack_memory.c \ + linked_list.c \ + memory_pool.c \ + run_loop.c \ COMMON += \ - hci.c \ - hci_cmds.c \ - hci_dump.c \ - l2cap.c \ - l2cap_signaling.c \ + hci.c \ + hci_cmds.c \ + hci_dump.c \ + l2cap.c \ + l2cap_signaling.c \ remote_device_db_memory.c \ - sdp_util.c \ - utils.c \ - rfcomm.c \ - bnep.c \ - sdp.c \ + sdp_util.c \ + utils.c \ + rfcomm.c \ + bnep.c \ + sdp.c \ SDP_CLIENT += \ - sdp_client.o \ - sdp_parser.o \ - sdp_query_util.o \ + sdp_client.o \ + sdp_parser.o \ + sdp_query_util.o \ sdp_query_rfcomm.o \ ATT += \ - att_dispatch.c \ + att_dispatch.c \ GATT_SERVER += \ - att.c \ - att_server.c \ + att.c \ + att_server.c \ GATT_CLIENT += \ - ad_parser.c \ - gatt_client.c \ + ad_parser.c \ + gatt_client.c \ SM_REAL += \ sm.c \ - le_device_db_memory.c \ + le_device_db_memory.c \ SM_MINIMAL += \ sm_minimal.c \ - le_device_db_dummy.c \ + le_device_db_dummy.c \ PAN += \ pan.c \ @@ -71,8 +71,9 @@ EXAMPLES = \ spp_and_le_counter \ spp_counter \ spp_streamer \ - gap_le_advertisements \ - hsp_hs_demo \ + gap_le_advertisements \ + hsp_hs_demo \ + hsp_ag_demo \ EXAMPLES_USING_LE = \ ancs_client \ @@ -81,7 +82,7 @@ EXAMPLES_USING_LE = \ le_counter \ le_streamer \ spp_and_le_counter \ - gap_le_advertisements \ + gap_le_advertisements \ # .o for .c CORE_OBJ = $(CORE:.c=.o) @@ -171,6 +172,9 @@ gap_le_advertisements: ${CORE_OBJ} ${COMMON_OBJ} ad_parser.c gap_le_advertisemen hsp_hs_demo: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hsp_hs.o hsp_hs_demo.c ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ +hsp_ag_demo: ${CORE_OBJ} ${COMMON_OBJ} ${SDP_CLIENT} hsp_ag.o hsp_ag_demo.c + ${CC} $^ ${CFLAGS} ${LDFLAGS} -o $@ + clean: rm -f ${EXAMPLES} rm -f *.o *.out *.hex *.exe diff --git a/example/embedded/hsp_ag_demo.c b/example/embedded/hsp_ag_demo.c new file mode 100644 index 000000000..f8210e0ef --- /dev/null +++ b/example/embedded/hsp_ag_demo.c @@ -0,0 +1,364 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + +/* + * hsp_ag_demo.c + */ + +// ***************************************************************************** +/* EXAMPLE_START(hsp_ag_demo): HSP Audio Gateway Demo + * + * @text This example implements a HSP Audio Gateway device that sends and receives + * audio signal over HCI SCO. It demonstrates how to receive + * an output from a remote headset (HS), and, + * if HAVE_STDIO is defined, how to control the HS. + */ +// ***************************************************************************** + + +#include "btstack-config.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "sdp.h" +#include "hsp_ag.h" +#include "stdin_support.h" + +#include "hci.h" +#include "l2cap.h" +#include "rfcomm.h" +#include "debug.h" + +#define SCO_REPORT_PERIOD 255 + +static uint32_t hsp_service_buffer[150/4]; // implicit alignment to 4-byte memory address +static const uint8_t rfcomm_channel_nr = 1; +static const char hsp_ag_service_name[] = "Audio Gateway Test"; +static uint16_t sco_handle = 0; + +static char hs_cmd_buffer[100]; +// static bd_addr_t current_addr = {0x04,0x0C,0xCE,0xE4,0x85,0xD3}; +static bd_addr_t current_addr = {0x00,0x1b,0xDC,0x07,0x32,0xEF}; + +static int phase = 0; + +// input signal: pre-computed sine wave, 160 Hz +static const uint8_t sine[] = { + 0, 15, 31, 46, 61, 74, 86, 97, 107, 114, + 120, 124, 126, 126, 124, 120, 114, 107, 97, 86, + 74, 61, 46, 31, 15, 0, 241, 225, 210, 195, + 182, 170, 159, 149, 142, 136, 132, 130, 130, 132, + 136, 142, 149, 159, 170, 182, 195, 210, 225, 241, +}; + +/* @section Audio Transfer Setup + * + * @text A pre-computed sine wave (160Hz) is used as the input audio signal. 160 Hz. + * To send and receive an audio signal, HAVE_SCO_OVER_HCI has to be defined. + * + * Tested working setups: + * - Ubuntu 14 64-bit, CC2564B connected via FTDI USB-2-UART adapter, 921600 baud + * - Ubuntu 14 64-bit, CSR USB dongle + * - OS X 10.11, CSR USB dongle + * + * Broken setups: + * - OS X 10.11, CC2564B connected via FDTI USB-2-UART adapter, 921600 baud + * - select(..) blocks > 400 ms -> num completed is received to late -> gaps between audio + * - looks like bug in select->FTDI driver as it works correct on Linux + * + * SCO not routed over HCI yet: + * - CSR UART dongle + * - Broadcom USB dongle + * - Broadcom UART chipset + * - .. + * + */ + + +static void show_usage(void){ + uint8_t iut_address_type; + bd_addr_t iut_address; + hci_le_advertisement_address(&iut_address_type, iut_address); + + printf("\n--- Bluetooth HSP Audio Gateway Test Console %s ---\n", bd_addr_to_str(iut_address)); + + printf("---\n"); + printf("c - Connect to %s\n", bd_addr_to_str(current_addr)); + printf("C - Disconnect\n"); + printf("a - establish audio connection\n"); + printf("A - release audio connection\n"); + printf("m - set microphone gain 8\n"); + printf("M - set microphone gain 15\n"); + printf("o - set speaker gain 0\n"); + printf("s - set speaker gain 8\n"); + printf("S - set speaker gain 15\n"); + printf("r - start ringing\n"); + printf("t - stop ringing\n"); + printf("---\n"); + printf("Ctrl-c - exit\n"); + printf("---\n"); +} + + +static int stdin_process(struct data_source *ds){ + char buffer; + read(ds->fd, &buffer, 1); + + switch (buffer){ + case 'c': + printf("Connect to %s\n", bd_addr_to_str(current_addr)); + hsp_ag_connect(current_addr); + break; + case 'C': + printf("Disconnect.\n"); + hsp_ag_disconnect(); + break; + case 'a': + printf("Establish audio connection\n"); + hsp_ag_establish_audio_connection(); + break; + case 'A': + printf("Release audio connection\n"); + hsp_ag_release_audio_connection(); + break; + case 'm': + printf("Setting microphone gain 8\n"); + hsp_ag_set_microphone_gain(8); + break; + case 'M': + printf("Setting microphone gain 15\n"); + hsp_ag_set_microphone_gain(15); + break; + case 'o': + printf("Setting speaker gain 0\n"); + hsp_ag_set_speaker_gain(0); + break; + case 's': + printf("Setting speaker gain 8\n"); + hsp_ag_set_speaker_gain(8); + break; + case 'S': + printf("Setting speaker gain 15\n"); + hsp_ag_set_speaker_gain(15); + break; + case 'r': + printf("Start ringing\n"); + hsp_ag_start_ringing(); + break; + case 't': + printf("Stop ringing\n"); + hsp_ag_stop_ringing(); + break; + default: + show_usage(); + break; + + } + return 0; +} + +static void try_send_sco(void){ + return; + if (!sco_handle) return; + if (!hci_can_send_sco_packet_now(sco_handle)) { + // printf("try_send_sco, cannot send now\n"); + return; + } + + const int sco_packet_length = hci_get_sco_packet_length(); + const int sco_payload_length = sco_packet_length - 3; + const int frames_per_packet = sco_payload_length; // for 8-bit data. for 16-bit data it's /2 + + hci_reserve_packet_buffer(); + uint8_t * sco_packet = hci_get_outgoing_packet_buffer(); + // set handle + flags + bt_store_16(sco_packet, 0, sco_handle); + // set len + sco_packet[2] = sco_payload_length; + int i; + for (i=0;i= sizeof(sine)) phase = 0; + } + hci_send_sco_packet_buffer(sco_packet_length); + static int count = 0; + count++; + if ((count & SCO_REPORT_PERIOD) == 0) printf("Sent %u\n", count); +} + +static void sco_packet_handler(uint8_t packet_type, uint8_t * packet, uint16_t size){ + return; + static int count = 0; + count++; + if ((count & SCO_REPORT_PERIOD)) return; + printf("SCO packets %u\n", count); + hexdumpf(packet, size); +} + +static void packet_handler(uint8_t * event, uint16_t event_size){ + switch (event[0]) { + case BTSTACK_EVENT_STATE: + if (event[2] != HCI_STATE_WORKING) break; + printf("HCI_STATE_WORKING\n"); + show_usage(); + break; + case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS: + case DAEMON_EVENT_HCI_PACKET_SENT: + try_send_sco(); + break; + case HCI_EVENT_HSP_META: + switch (event[2]) { + case HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE: + if (event[3] == 0){ + printf("RFCOMM connection established.\n"); + } else { + printf("RFCOMM connection establishement failed.\n"); + } + break; + case HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE: + if (event[3] == 0){ + printf("RFCOMM disconnected.\n"); + } else { + printf("RFCOMM disconnection failed.\n"); + } + break; + case HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE: + if (event[3] == 0){ + sco_handle = READ_BT_16(event, 4); + printf("Audio connection established with SCO handle 0x%04x.\n", sco_handle); + try_send_sco(); + } else { + printf("Audio connection establishment failed with status %u\n", event[3]); + sco_handle = 0; + } + break; + case HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE: + if (event[3] == 0){ + printf("Audio connection released.\n\n"); + sco_handle = 0; + } else { + printf("Audio connection releasing failed with status %u\n", event[3]); + } + break; + case HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED: + printf("Received microphone gain change %d\n", event[3]); + break; + case HSP_SUBEVENT_SPEAKER_GAIN_CHANGED: + printf("Received speaker gain change %d\n", event[3]); + break; + case HSP_SUBEVENT_HS_COMMAND:{ + memset(hs_cmd_buffer, 0, sizeof(hs_cmd_buffer)); + int size = event[3] <= sizeof(hs_cmd_buffer)? event[3] : sizeof(hs_cmd_buffer); + memcpy(hs_cmd_buffer, &event[4], size - 1); + printf("Received custom command: \"%s\". \nExit code or call hsp_ag_send_result.\n", hs_cmd_buffer); + break; + } + default: + printf("event not handled %u\n", event[2]); + break; + } + break; + default: + break; + } +} + +/* @section Main Application Setup + * + * @text Listing MainConfiguration shows main application code. + * To run a HSP Audio Gateway service you need to initialize the SDP, and to create and register HSP AG record with it. + * In this example, the SCO over HCI is used to receive and send an audio signal. + * + * Two packet handlers are registered: + * - The HCI SCO packet handler receives audio data. + * - The HSP AG packet handler is used to trigger sending of audio data and commands to the HS. It also receives the AG's answers. + * + * The stdin_process callback allows for sending commands to the AG. + * At the end the Bluetooth stack is started. + */ + +/* LISTING_START(MainConfiguration): Setup packet handlers and audio data channel for HSP Audio Gateway */ +int btstack_main(int argc, const char * argv[]); +int btstack_main(int argc, const char * argv[]){ + l2cap_init(); + rfcomm_init(); + sdp_init(); + + memset((uint8_t *)hsp_service_buffer, 0, sizeof(hsp_service_buffer)); + +/* LISTING_PAUSE */ +#ifdef EMBEDDED +/* LISTING_RESUME */ + service_record_item_t * service_record_item = (service_record_item_t *) hsp_service_buffer; + hsp_ag_create_sdp_record((uint8_t*) &service_record_item->service_record, rfcomm_channel_nr, hsp_ag_service_name); + printf("SDP service buffer size: %u\n", (uint16_t) (sizeof(service_record_item_t) + de_get_len((uint8_t*) &service_record_item->service_record))); + sdp_register_service_internal(NULL, service_record_item); +/* LISTING_PAUSE */ +#else + hsp_ag_create_sdp_record((uint8_t *) hsp_service_buffer, rfcomm_channel_nr, hsp_ag_service_name); + printf("SDP service record size: %u\n", de_get_len((uint8_t*) hsp_service_buffer)); + sdp_register_service_internal(NULL, (uint8_t*)hsp_service_buffer); +#endif +/* LISTING_RESUME */ + hci_register_sco_packet_handler(&sco_packet_handler); + + hsp_ag_init(rfcomm_channel_nr); + + hsp_ag_register_packet_handler(packet_handler); + + btstack_stdin_setup(stdin_process); + + gap_set_local_name("BTstack HSP AG"); + hci_discoverable_control(1); + hci_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO); + // turn on! + hci_set_class_of_device(0x400204); + + hci_power_control(HCI_POWER_ON); + + return 0; +} diff --git a/platforms/libusb/.gitignore b/platforms/libusb/.gitignore index 05c3cc477..5cbdf001c 100644 --- a/platforms/libusb/.gitignore +++ b/platforms/libusb/.gitignore @@ -9,8 +9,8 @@ gap_inquiry gap_inquiry_and_bond gatt_battery_query gatt_browser -hsp_ag_test -hsp_hs_test +hsp_ag_demo +hsp_hs_demo l2cap_test le_counter le_counter.h @@ -27,3 +27,4 @@ le_streamer.h gap_le_advertisements le_streamer spp_streamer + diff --git a/src/hsp_ag.c b/src/hsp_ag.c index 95c0039c0..dd9ef40c3 100644 --- a/src/hsp_ag.c +++ b/src/hsp_ag.c @@ -90,18 +90,28 @@ static uint8_t ag_send_error = 0; static uint8_t ag_num_button_press_received = 0; static uint8_t ag_support_custom_commands = 0; +static uint8_t hsp_disconnect_rfcomm = 0; +static uint8_t hsp_establish_audio_connection = 0; +static uint8_t hsp_release_audio_connection = 0; + typedef enum { HSP_IDLE, HSP_SDP_QUERY_RFCOMM_CHANNEL, HSP_W4_SDP_QUERY_COMPLETE, HSP_W4_RFCOMM_CONNECTED, + + HSP_RFCOMM_CONNECTION_ESTABLISHED, + HSP_W4_RING_ANSWER, HSP_W4_USER_ACTION, HSP_W2_CONNECT_SCO, HSP_W4_SCO_CONNECTED, - HSP_ACTIVE, + + HSP_AUDIO_CONNECTION_ESTABLISHED, + HSP_W2_DISCONNECT_SCO, HSP_W4_SCO_DISCONNECTED, + HSP_W2_DISCONNECT_RFCOMM, HSP_W4_RFCOMM_DISCONNECTED, HSP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN @@ -214,7 +224,6 @@ void hsp_ag_create_sdp_record(uint8_t * service, int rfcomm_channel_nr, const ch } static int hsp_ag_send_str_over_rfcomm(uint16_t cid, char * command){ - if (!rfcomm_can_send_packet_now(cid)) return 1; int err = rfcomm_send_internal(cid, (uint8_t*) command, strlen(command)); if (err){ log_error("rfcomm_send_internal -> error 0X%02x", err); @@ -249,14 +258,16 @@ static void hsp_ag_reset_state(void){ ag_microphone_gain = -1; ag_speaker_gain = -1; + + hsp_disconnect_rfcomm = 0; + hsp_establish_audio_connection = 0; + hsp_release_audio_connection = 0; } void hsp_ag_init(uint8_t rfcomm_channel_nr){ // init L2CAP - l2cap_init(); l2cap_register_packet_handler(packet_handler); - rfcomm_init(); rfcomm_register_packet_handler(packet_handler); rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 0xffff); // reserved channel, mtu limited by l2cap @@ -273,24 +284,48 @@ void hsp_ag_connect(bd_addr_t bd_addr){ } void hsp_ag_disconnect(void){ - switch (hsp_state){ - case HSP_ACTIVE: - hsp_state = HSP_W2_DISCONNECT_SCO; - break; - case HSP_W2_CONNECT_SCO: - hsp_state = HSP_W2_DISCONNECT_RFCOMM; - break; + hsp_ag_release_audio_connection(); + printf(" rfcomm diconnect %d\n", hsp_state); + if (hsp_state < HSP_W4_RFCOMM_CONNECTED){ + hsp_state = HSP_IDLE; + return; + } + if (hsp_state == HSP_W4_RFCOMM_CONNECTED){ + hsp_state = HSP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN; + return; + } + hsp_disconnect_rfcomm = 1; + hsp_run(); +} + +void hsp_ag_establish_audio_connection(void){ + printf("hsp_ag_establish_audio_connection state %d\n", hsp_state); + + switch (hsp_state){ + case HSP_RFCOMM_CONNECTION_ESTABLISHED: + printf("set flag hsp_establish_audio_connection\n"); + hsp_establish_audio_connection = 1; + hsp_state = HSP_W4_SCO_CONNECTED; + break; case HSP_W4_RFCOMM_CONNECTED: - case HSP_W4_SCO_CONNECTED: hsp_state = HSP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN; break; default: - return; + break; } hsp_run(); } +void hsp_ag_release_audio_connection(void){ + if (hsp_state >= HSP_W2_DISCONNECT_SCO) return; + if (hsp_state < HSP_AUDIO_CONNECTION_ESTABLISHED) return; + + hsp_release_audio_connection = 1; + hsp_run(); +} + + void hsp_ag_set_microphone_gain(uint8_t gain){ if (gain < 0 || gain >15) { log_error("Gain must be in interval [0..15], it is given %d", gain); @@ -342,6 +377,7 @@ void hsp_ag_stop_ringing(void){ } static void hsp_run(void){ + if (!rfcomm_can_send_packet_now(rfcomm_cid)) return; int err; if (ag_send_ok){ @@ -362,10 +398,28 @@ static void hsp_run(void){ return; } + if (hsp_establish_audio_connection){ + hsp_establish_audio_connection = 0; + hci_send_cmd(&hci_setup_synchronous_connection, rfcomm_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F); + return; + } + + if (hsp_release_audio_connection){ + hsp_release_audio_connection = 0; + gap_disconnect(sco_handle); + return; + } + + if (hsp_disconnect_rfcomm){ + hsp_disconnect_rfcomm = 0; + hsp_establish_audio_connection = 0; + rfcomm_disconnect_internal(rfcomm_cid); + return; + } + switch (hsp_state){ case HSP_SDP_QUERY_RFCOMM_CHANNEL: hsp_state = HSP_W4_SDP_QUERY_COMPLETE; - log_info("Start SDP query %s, 0x%02x", bd_addr_to_str(remote), SDP_HSP); sdp_query_rfcomm_channel_and_name_for_uuid(remote, SDP_HSP); break; @@ -391,8 +445,8 @@ static void hsp_run(void){ ag_num_button_press_received = 1; } break; + case HSP_W2_CONNECT_SCO: - if (!hci_can_send_command_packet_now()) break; hsp_state = HSP_W4_SCO_CONNECTED; hci_send_cmd(&hci_setup_synchronous_connection, rfcomm_handle, 8000, 8000, 0xFFFF, hci_get_sco_voice_setting(), 0xFF, 0x003F); break; @@ -404,11 +458,12 @@ static void hsp_run(void){ gap_disconnect(sco_handle); break; - case HSP_W2_DISCONNECT_RFCOMM: - hsp_state = HSP_W4_RFCOMM_DISCONNECTED; + case HSP_W4_RFCOMM_DISCONNECTED: rfcomm_disconnect_internal(rfcomm_cid); break; - case HSP_ACTIVE: + + case HSP_AUDIO_CONNECTION_ESTABLISHED: + case HSP_RFCOMM_CONNECTION_ESTABLISHED: if (ag_microphone_gain >= 0){ int gain = ag_microphone_gain; @@ -450,12 +505,17 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha if (strncmp((char *)packet, HSP_HS_BUTTON_PRESS, strlen(HSP_HS_BUTTON_PRESS)) == 0){ log_info("Received button press %s", HSP_HS_BUTTON_PRESS); - ag_num_button_press_received++; ag_send_ok = 1; - if (hsp_state == HSP_ACTIVE && ag_num_button_press_received >=2){ - ag_num_button_press_received = 0; - hsp_state = HSP_W2_DISCONNECT_SCO; - } + switch (hsp_state){ + case HSP_AUDIO_CONNECTION_ESTABLISHED: + hsp_release_audio_connection = 1; + break; + case HSP_RFCOMM_CONNECTION_ESTABLISHED: + hsp_establish_audio_connection = 1; + break; + default: + break; + } } else if (strncmp((char *)packet, HSP_HS_MICROPHONE_GAIN, strlen(HSP_HS_MICROPHONE_GAIN)) == 0){ uint8_t gain = (uint8_t)atoi((char*)&packet[strlen(HSP_HS_MICROPHONE_GAIN)]); ag_send_ok = 1; @@ -534,7 +594,7 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha break; } - hsp_state = HSP_ACTIVE; + hsp_state = HSP_AUDIO_CONNECTION_ESTABLISHED; emit_event_audio_connected(status, sco_handle); break; } @@ -546,9 +606,8 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha bt_flip_addr(event_addr, &packet[2]); rfcomm_cid = READ_BT_16(packet, 9); log_info("RFCOMM channel %u requested for %s", packet[8], bd_addr_to_str(event_addr)); - rfcomm_accept_connection_internal(rfcomm_cid); - hsp_state = HSP_W4_RFCOMM_CONNECTED; + rfcomm_accept_connection_internal(rfcomm_cid); break; case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: @@ -557,45 +616,44 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha if (packet[2]) { log_info("RFCOMM channel open failed, status %u§", packet[2]); hsp_ag_reset_state(); - emit_event(HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE, packet[2]); + hsp_state = HSP_IDLE; } else { // data: event(8) , len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16) rfcomm_handle = READ_BT_16(packet, 9); rfcomm_cid = READ_BT_16(packet, 12); mtu = READ_BT_16(packet, 14); log_info("RFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u, state %d", rfcomm_cid, mtu, hsp_state); - - switch (hsp_state){ - case HSP_W4_RFCOMM_CONNECTED: - ag_num_button_press_received = 0; - hsp_state = HSP_W2_CONNECT_SCO; - break; - case HSP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN: - hsp_state = HSP_W2_DISCONNECT_RFCOMM; - break; - default: - log_error("no valid state"); - break; - } + hsp_state = HSP_RFCOMM_CONNECTION_ESTABLISHED; } + emit_event(HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE, packet[2]); break; + case BTSTACK_EVENT_STATE: case DAEMON_EVENT_HCI_PACKET_SENT: + case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS: case RFCOMM_EVENT_CREDITS: + hsp_ag_callback(packet, size); break; case HCI_EVENT_DISCONNECTION_COMPLETE: handle = READ_BT_16(packet,3); if (handle == sco_handle){ - log_info("SCO disconnected, w2 disconnect RFCOMM"); sco_handle = 0; - hsp_state = HSP_W2_DISCONNECT_RFCOMM; + hsp_state = HSP_RFCOMM_CONNECTION_ESTABLISHED; + emit_event(HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE,0); break; + } + if (handle == rfcomm_handle) { + rfcomm_handle = 0; + hsp_state = HSP_IDLE; + hsp_ag_reset_state(); + emit_event(HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE,0); } break; case RFCOMM_EVENT_CHANNEL_CLOSED: - log_info("RFCOMM channel closed"); + rfcomm_handle = 0; + hsp_state = HSP_IDLE; hsp_ag_reset_state(); - emit_event(HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE,0); + emit_event(HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE,0); break; default: break; diff --git a/src/hsp_ag.h b/src/hsp_ag.h index 072240254..7954efd09 100644 --- a/src/hsp_ag.h +++ b/src/hsp_ag.h @@ -58,6 +58,8 @@ extern "C" { * @brief Packet handler for HSP Audio Gateway (AG) events. * * The HSP AG event has type HCI_EVENT_HSP_META with following subtypes: + * - HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE + * - HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE * - HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE * - HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE * - HSP_SUBEVENT_MICROPHONE_GAIN_CHANGED @@ -90,26 +92,45 @@ void hsp_ag_create_sdp_record(uint8_t * service, int rfcomm_channel_nr, const ch void hsp_ag_register_packet_handler(hsp_ag_callback_t callback); /** - * @brief Connect to HSP Headset + * @brief Connect to HSP Headset. * * Perform SDP query for an RFCOMM service on a remote device, - * and establish an RFCOMM connection if such service is found. The reception of the - * HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE or - * HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE event - * indicate if the connection is successfully established or not. + * and establish an RFCOMM connection if such service is found. Reception of the + * HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE with status 0 + * indicates if the connection is successfully established. * * @param bd_addr */ void hsp_ag_connect(bd_addr_t bd_addr); /** - * @brief Disconnect from HSP Headset. + * @brief Disconnect from HSP Headset * - * Releases the RFCOMM channel. + * Reception of the HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE with status 0 + * indicates if the connection is successfully released. * @param bd_addr */ void hsp_ag_disconnect(void); + +/** + * @brief Establish audio connection. + * + * Reception of the HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE with status 0 + * indicates if the audio connection is successfully established. + * @param bd_addr + */ +void hsp_ag_establish_audio_connection(void); + +/** + * @brief Release audio connection. + * + * Reception of the HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE with status 0 + * indicates if the connection is successfully released. + * @param bd_addr + */ +void hsp_ag_release_audio_connection(void); + /** * @brief Set microphone gain. * @param gain Valid range: [0,15] diff --git a/src/hsp_hs.c b/src/hsp_hs.c index 6b61076bf..0e6cb0549 100644 --- a/src/hsp_hs.c +++ b/src/hsp_hs.c @@ -112,8 +112,7 @@ typedef enum { HSP_W4_SCO_DISCONNECTED, HSP_W2_DISCONNECT_RFCOMM, - HSP_W4_RFCOMM_DISCONNECTED, - HSP_W4_RFCOMM_DISCONNECTED_AND_RESTART, + HSP_W4_RFCOMM_DISCONNECTED, HSP_W4_CONNECTION_ESTABLISHED_TO_SHUTDOWN } hsp_state_t; @@ -264,6 +263,10 @@ static void hsp_hs_reset_state(void){ hs_send_button_press = 0; wait_ok = 0; hs_support_custom_indications = 0; + + hsp_disconnect_rfcomm = 0; + hsp_establish_audio_connection = 0; + hsp_release_audio_connection = 0; } void hsp_hs_init(uint8_t rfcomm_channel_nr){ @@ -304,10 +307,6 @@ void hsp_hs_disconnect(void){ return; } - if (hsp_state < HSP_W4_SCO_DISCONNECTED){ - hsp_state = HSP_W2_DISCONNECT_SCO; - return; - } hsp_disconnect_rfcomm = 1; hsp_run(); } @@ -394,7 +393,7 @@ static void hsp_run(void){ break; case HSP_AUDIO_CONNECTION_ESTABLISHED: - case HSP_RFCOMM_CONNECTION_ESTABLISHED : + case HSP_RFCOMM_CONNECTION_ESTABLISHED: if (hs_microphone_gain >= 0){ char buffer[20]; @@ -558,15 +557,14 @@ static void packet_handler (void * connection, uint8_t packet_type, uint16_t cha handle = READ_BT_16(packet,3); if (handle == sco_handle){ sco_handle = 0; - hsp_state = HSP_RFCOMM_CONNECTION_ESTABLISHED ; - hsp_hs_reset_state(); + hsp_state = HSP_RFCOMM_CONNECTION_ESTABLISHED; emit_event(HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE,0); break; } if (handle == rfcomm_handle) { rfcomm_handle = 0; hsp_state = HSP_IDLE; - hsp_hs_callback(packet, size); + emit_event(HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE,0); hsp_hs_reset_state(); } break; diff --git a/src/hsp_hs.h b/src/hsp_hs.h index 3ee2954f3..bd0eea37e 100644 --- a/src/hsp_hs.h +++ b/src/hsp_hs.h @@ -58,6 +58,8 @@ extern "C" { * @brief Packet handler for HSP Headset (HS) events. * * The HSP HS event has type HCI_EVENT_HSP_META with following subtypes: + * - HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE + * - HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE * - HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE * - HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE * - HSP_SUBEVENT_RING @@ -95,10 +97,9 @@ void hsp_hs_register_packet_handler(hsp_hs_callback_t callback); * @brief Connect to HSP Audio Gateway. * * Perform SDP query for an RFCOMM service on a remote device, - * and establish an RFCOMM connection if such service is found. The reception of the - * HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE or - * HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE event - * indicate if the connection is successfully established or not. + * and establish an RFCOMM connection if such service is found. Reception of the + * HSP_SUBEVENT_RFCOMM_CONNECTION_COMPLETE with status 0 + * indicates if the connection is successfully established. * * @param bd_addr */ @@ -107,7 +108,9 @@ void hsp_hs_connect(bd_addr_t bd_addr); /** * @brief Disconnect from HSP Audio Gateway * - * Releases the RFCOMM channel. + * Releases the RFCOMM channel. Reception of the + * HSP_SUBEVENT_RFCOMM_DISCONNECTION_COMPLETE with status 0 + * indicates if the connection is successfully released. * @param bd_addr */ void hsp_hs_disconnect(void); @@ -120,7 +123,9 @@ void hsp_hs_send_button_press(void); /** * @brief Triger establishing audio connection. - * + * + * Reception of the HSP_SUBEVENT_AUDIO_CONNECTION_COMPLETE with status 0 + * indicates if the audio connection is successfully established. * @param bd_addr */ void hsp_hs_establish_audio_connection(void); @@ -128,6 +133,8 @@ void hsp_hs_establish_audio_connection(void); /** * @brief Trigger releasing audio connection. * + * Reception of the HSP_SUBEVENT_AUDIO_DISCONNECTION_COMPLETE with status 0 + * indicates if the connection is successfully released. * @param bd_addr */ void hsp_hs_release_audio_connection(void);