From ec3ca939d3295000bc4b4c98a2a6be746ffc2e28 Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Mon, 26 Sep 2016 22:44:32 +0200 Subject: [PATCH] Makefile --- test/avdtp/avdtp.h | 86 ++++++++++ test/avdtp/avdtp_sink.c | 330 ++++++++++++++++++++++++++++++++++++++ test/avdtp/avdtp_sink.h | 118 ++++++++++++++ test/avdtp/avdtp_source.c | 134 ++++++++++++++++ test/avdtp/avdtp_source.h | 72 +++++++++ test/avdtp/avdtp_test.c | 162 +++++++++++++++++++ 6 files changed, 902 insertions(+) create mode 100644 test/avdtp/avdtp.h create mode 100644 test/avdtp/avdtp_sink.c create mode 100644 test/avdtp/avdtp_sink.h create mode 100644 test/avdtp/avdtp_source.c create mode 100644 test/avdtp/avdtp_source.h create mode 100644 test/avdtp/avdtp_test.c diff --git a/test/avdtp/avdtp.h b/test/avdtp/avdtp.h new file mode 100644 index 000000000..d3e7dc02e --- /dev/null +++ b/test/avdtp/avdtp.h @@ -0,0 +1,86 @@ +/* + * Copyright (C) 2016 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 + * + */ + +/* + * avdtp.h + * + * Audio/Video Distribution Transport Protocol + * + * This protocol defines A/V stream negotiation, establishment, and transmission + * procedures. Also specified are the message formats that are exchanged between + * such devices to transport their A/V streams in A/V distribution applications. + * + * Media packets are unidirectional, they travel downstream from AVDTP Source to AVDTP Sink. + */ + +#ifndef __AVDTP_H +#define __AVDTP_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +// protocols +#define PSM_AVCTP 0x0017 +#define PSM_AVDTP 0x0019 + +// service classes +#define AUDIO_SOURCE_GROUP 0x110A +#define AUDIO_SINK_GROUP 0x110B +#define AV_REMOTE_CONTROL_TARGET 0X110C +#define ADVANCED_AUDIO_DISTRIBUTION 0X110D +#define AV_REMOTE_CONTROL 0X110E +#define AV_REMOTE_CONTROL_CONTROLER 0X110F + +// Supported Features +#define AVDTP_SOURCE_SF_Player 0x0001 +#define AVDTP_SOURCE_SF_Microphone 0x0002 +#define AVDTP_SOURCE_SF_Tuner 0x0004 +#define AVDTP_SOURCE_SF_Mixer 0x0008 + +#define AVDTP_SINK_SF_Headphone 0x0001 +#define AVDTP_SINK_SF_Speaker 0x0002 +#define AVDTP_SINK_SF_Recorder 0x0004 +#define AVDTP_SINK_SF_Amplifier 0x0008 + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_H \ No newline at end of file diff --git a/test/avdtp/avdtp_sink.c b/test/avdtp/avdtp_sink.c new file mode 100644 index 000000000..edab58dcd --- /dev/null +++ b/test/avdtp/avdtp_sink.c @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2016 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 + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_sink.h" + +static const char * default_avdtp_sink_service_name = "BTstack AVDTP Sink Service"; +static const char * default_avdtp_sink_service_provider_name = "BTstack AVDTP Sink Service Provider"; + +static btstack_linked_list_t avdtp_sink_connections = NULL; + +static btstack_packet_handler_t avdtp_sink_callback; +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); +static void avdtp_sink_run_for_connection(avdtp_sink_connection_t *connection); + +void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); + de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, AUDIO_SINK_GROUP); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, SDP_L2CAPProtocol); + de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVDTP); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* avProtocol = de_push_sequence(attribute); + { + de_add_number(avProtocol, DE_UUID, DE_SIZE_16, PSM_AVDTP); // avProtocol_service + de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version + } + de_pop_sequence(attribute, avProtocol); + } + de_pop_sequence(service, attribute); + + // 0x0005 "Public Browse Group" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_PublicBrowseGroup); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t *a2dProfile = de_push_sequence(attribute); + { + de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, ADVANCED_AUDIO_DISTRIBUTION); + de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); + } + de_pop_sequence(attribute, a2dProfile); + } + de_pop_sequence(service, attribute); + + + // 0x0100 "Service Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + if (service_name){ + de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_name), (uint8_t *) default_avdtp_sink_service_name); + } + + // 0x0100 "Provider Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); + if (service_provider_name){ + de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_provider_name), (uint8_t *) default_avdtp_sink_service_provider_name); + } + + // 0x0311 "Supported Features" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); + de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); +} + + +static avdtp_sink_connection_t * create_avdtp_sink_connection_context(bd_addr_t bd_addr){ + avdtp_sink_connection_t * avdtp_connection = btstack_memory_avdtp_sink_connection_get(); + if (!avdtp_connection) return NULL; + printf("create_avdtp_sink_connection_context %p\n", avdtp_connection); + + memset(avdtp_connection,0, sizeof(avdtp_sink_connection_t)); + memcpy(avdtp_connection->remote_addr, bd_addr, 6); + avdtp_connection->state = AVDTP_SINK_IDLE; + + btstack_linked_list_add(&avdtp_sink_connections, (btstack_linked_item_t*)avdtp_connection); + return avdtp_connection; +} + +static avdtp_sink_connection_t * get_avdtp_sink_connection_context_for_bd_addr(bd_addr_t bd_addr){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avdtp_sink_connections); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_sink_connection_t * connection = (avdtp_sink_connection_t *)btstack_linked_list_iterator_next(&it); + if (memcmp(connection->remote_addr, bd_addr, 6) == 0) { + return connection; + } + } + return NULL; +} + +static avdtp_sink_connection_t * get_avdtp_sink_connection_context_for_l2cap_cid(uint16_t l2cap_cid){ + btstack_linked_list_iterator_t it; + btstack_linked_list_iterator_init(&it, (btstack_linked_list_t *) &avdtp_sink_connections); + while (btstack_linked_list_iterator_has_next(&it)){ + avdtp_sink_connection_t * connection = (avdtp_sink_connection_t *)btstack_linked_list_iterator_next(&it); + if (connection->l2cap_cid == l2cap_cid){ + return connection; + } + } + return NULL; +} + +static void remove_avdtp_sink_connection_context(avdtp_sink_connection_t * connection){ + btstack_linked_list_remove(&avdtp_sink_connections, (btstack_linked_item_t*) connection); +} + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + bd_addr_t event_addr; + hci_con_handle_t con_handle; + uint16_t psm; + uint16_t remote_cid; + uint16_t local_cid; + avdtp_sink_connection_t * connection = NULL; + + printf("avdtp_sink packet handler: Received packet type %02x\n", packet_type); + + switch (packet_type) { + case L2CAP_DATA_PACKET: + // just dump data for now + printf("source cid %x -- ", channel); + // printf_hexdump( packet, size ); + break; + + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case L2CAP_EVENT_INCOMING_CONNECTION: + l2cap_event_incoming_connection_get_address(packet, event_addr); + connection = get_avdtp_sink_connection_context_for_bd_addr(event_addr); + if (connection){ + log_error(""); + break; + } + + connection = create_avdtp_sink_connection_context(event_addr); + if (!connection) return; + + con_handle = l2cap_event_incoming_connection_get_handle(packet); + psm = l2cap_event_incoming_connection_get_psm(packet); + local_cid = l2cap_event_incoming_connection_get_local_cid(packet); + remote_cid = l2cap_event_incoming_connection_get_remote_cid(packet); + printf("L2CAP_EVENT_INCOMING_CONNECTION %s, handle 0x%02x, psm 0x%02x, local cid 0x%02x, remote cid 0x%02x\n", + bd_addr_to_str(event_addr), con_handle, psm, local_cid, remote_cid); + + connection->state = AVDTP_SINK_W4_L2CAP_CONNECTED; + l2cap_accept_connection(local_cid); + + break; + + case L2CAP_EVENT_CHANNEL_OPENED: + // inform about new l2cap connection + l2cap_event_channel_opened_get_address(packet, event_addr); + connection = get_avdtp_sink_connection_context_for_bd_addr(event_addr); + if (!connection || connection->state != AVDTP_SINK_W4_L2CAP_CONNECTED) return; + + if (l2cap_event_channel_opened_get_status(packet)){ + printf("L2CAP connection to device %s failed. status code %u\n", + bd_addr_to_str(event_addr), l2cap_event_channel_opened_get_status(packet)); + break; + } + psm = l2cap_event_channel_opened_get_psm(packet); + connection->l2cap_cid = l2cap_event_channel_opened_get_local_cid(packet); + connection->acl_handle = l2cap_event_channel_opened_get_handle(packet); + + 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), connection->acl_handle, psm, connection->l2cap_cid, l2cap_event_channel_opened_get_remote_cid(packet)); + + + connection->state = AVDTP_SINK_W4_CONFIGURATION_COMPLETE; + break; + + case L2CAP_EVENT_CHANNEL_CLOSED: + // data: event (8), len(8), channel (16) + local_cid = l2cap_event_channel_closed_get_local_cid(packet); + connection = get_avdtp_sink_connection_context_for_l2cap_cid(local_cid); + if (!connection || connection->state != AVDTP_SINK_W4_L2CAP_DISCONNECTED) return; + + log_info("L2CAP_EVENT_CHANNEL_CLOSED cid 0x%0x", local_cid); + remove_avdtp_sink_connection_context(connection); + connection->state = AVDTP_SINK_IDLE; + break; + + default: + // other event + break; + } + break; + + default: + // other packet type + break; + } + avdtp_sink_run_for_connection(connection); +} + +// TODO: find out which security level is needed, and replace LEVEL_0 in avdtp_sink_init +void avdtp_sink_init(void){ + l2cap_register_service(&packet_handler, PSM_AVDTP, 0xffff, LEVEL_0); +} + +void avdtp_sink_register_packet_handler(btstack_packet_handler_t callback){ + if (callback == NULL){ + log_error("avdtp_sink_register_packet_handler called with NULL callback"); + return; + } + avdtp_sink_callback = callback; +} + + +static void avdtp_sink_run_for_connection(avdtp_sink_connection_t *connection){ + if (!connection) return; + // handle disconnect request + if (connection->release_l2cap_connection){ + if (connection->state > AVDTP_SINK_W4_L2CAP_CONNECTED || + connection->state < AVDTP_SINK_W4_L2CAP_DISCONNECTED){ + connection->release_l2cap_connection = 0; + connection->state = AVDTP_SINK_W4_L2CAP_DISCONNECTED; + l2cap_disconnect(connection->l2cap_cid, 0); + return; + } + } + + // if (!l2cap_can_send_packet_now(connection->l2cap_cid)) { + // log_info("avdtp_sink_run_for_connection: request cannot send for 0x%02x", connection->l2cap_cid); + // return; + // } + +} + +void avdtp_sink_connect(bd_addr_t bd_addr){ + avdtp_sink_connection_t * connection = get_avdtp_sink_connection_context_for_bd_addr(bd_addr); + if (connection) { + log_error("..."); + return; + } + connection = create_avdtp_sink_connection_context(bd_addr); + if (!connection){ + log_error(""); + return; + } + + connection->state = AVDTP_SINK_W4_L2CAP_CONNECTED; + l2cap_create_channel(packet_handler, connection->remote_addr, PSM_AVDTP, 0xffff, NULL); +} + + +void avdtp_sink_disconnect(uint16_t l2cap_cid){ + avdtp_sink_connection_t * connection = get_avdtp_sink_connection_context_for_l2cap_cid(l2cap_cid); + if (!connection) { + log_error("avdtp_sink_disconnect Connection with l2cap_cid %d not found", l2cap_cid); + return; + } + + if (connection->state == AVDTP_SINK_IDLE) return; + if (connection->state == AVDTP_SINK_W4_L2CAP_DISCONNECTED) return; + + connection->release_l2cap_connection = 1; + avdtp_sink_run_for_connection(connection); +} diff --git a/test/avdtp/avdtp_sink.h b/test/avdtp/avdtp_sink.h new file mode 100644 index 000000000..6ef411767 --- /dev/null +++ b/test/avdtp/avdtp_sink.h @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 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 + * + */ + +/* + * avdtp_sink.h + * + * Audio/Video Distribution Transport Protocol (AVDTP) Sink + * + * AVDTP Sink is a device that accepts streamed media data. + */ + +#ifndef __AVDTP_SINK_H +#define __AVDTP_SINK_H + +#include +#include "hci.h" + +#if defined __cplusplus +extern "C" { +#endif + +typedef enum { + AVDTP_SINK_IDLE, + AVDTP_SINK_W4_L2CAP_CONNECTED, + AVDTP_SINK_W4_CONFIGURATION_COMPLETE, + + AVDTP_SINK_W4_L2CAP_DISCONNECTED +} avdtp_sink_state_t; + +typedef struct avdtp_sink_connection { + btstack_linked_item_t item; + + bd_addr_t remote_addr; + hci_con_handle_t acl_handle; + uint16_t l2cap_cid; + + avdtp_sink_state_t state; + + uint8_t release_l2cap_connection; +} avdtp_sink_connection_t; + + +/* API_START */ + +/** + * @brief AVDTP Sink service record. + * @param service + * @param service_record_handle + * @param supported_features 16-bit bitmap, see AVDTP_SINK_SF_* values in avdtp.h + * @param service_name + * @param service_provider_name + */ +void a2dp_sink_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name); + +/** + * @brief Set up AVDTP Sink device. + */ +void avdtp_sink_init(void); + +/** + * @brief Register callback for the AVDTP Sink client. + * @param callback + */ +void avdtp_sink_register_packet_handler(btstack_packet_handler_t callback); + +/** + * @brief Connect to device with a bluetooth address. (and perform configuration?) + * @param bd_addr + */ +void avdtp_sink_connect(bd_addr_t bd_addr); + +/** + * @brief Disconnect from device with connection handle. + * @param acl_handle + */ +void avdtp_sink_disconnect(uint16_t l2cap_cid); + +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_SINK_H \ No newline at end of file diff --git a/test/avdtp/avdtp_source.c b/test/avdtp/avdtp_source.c new file mode 100644 index 000000000..6242947bd --- /dev/null +++ b/test/avdtp/avdtp_source.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2016 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 + * + */ + + +#include +#include +#include +#include +#include + +#include "btstack.h" +#include "avdtp.h" +#include "avdtp_source.h" + + +static const char * default_avdtp_sink_service_name = "BTstack AVDTP Sink Service"; +static const char * default_avdtp_sink_service_provider_name = "BTstack AVDTP Sink Service Provider"; + +void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name){ + uint8_t* attribute; + de_create_sequence(service); + + // 0x0000 "Service Record Handle" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceRecordHandle); + de_add_number(service, DE_UINT, DE_SIZE_32, service_record_handle); + + // 0x0001 "Service Class ID List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ServiceClassIDList); + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, AUDIO_SOURCE_GROUP); + } + de_pop_sequence(service, attribute); + + // 0x0004 "Protocol Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_ProtocolDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t* l2cpProtocol = de_push_sequence(attribute); + { + de_add_number(l2cpProtocol, DE_UUID, DE_SIZE_16, SDP_L2CAPProtocol); + de_add_number(l2cpProtocol, DE_UINT, DE_SIZE_16, PSM_AVDTP); + } + de_pop_sequence(attribute, l2cpProtocol); + + uint8_t* avProtocol = de_push_sequence(attribute); + { + de_add_number(avProtocol, DE_UUID, DE_SIZE_16, PSM_AVDTP); // avProtocol_service + de_add_number(avProtocol, DE_UINT, DE_SIZE_16, 0x0103); // version + } + de_pop_sequence(attribute, avProtocol); + } + de_pop_sequence(service, attribute); + + // 0x0005 "Public Browse Group" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BrowseGroupList); // public browse group + attribute = de_push_sequence(service); + { + de_add_number(attribute, DE_UUID, DE_SIZE_16, SDP_PublicBrowseGroup); + } + de_pop_sequence(service, attribute); + + // 0x0009 "Bluetooth Profile Descriptor List" + de_add_number(service, DE_UINT, DE_SIZE_16, SDP_BluetoothProfileDescriptorList); + attribute = de_push_sequence(service); + { + uint8_t *a2dProfile = de_push_sequence(attribute); + { + de_add_number(a2dProfile, DE_UUID, DE_SIZE_16, ADVANCED_AUDIO_DISTRIBUTION); + de_add_number(a2dProfile, DE_UINT, DE_SIZE_16, 0x0103); + } + de_pop_sequence(attribute, a2dProfile); + } + de_pop_sequence(service, attribute); + + + // 0x0100 "Service Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); + if (service_name){ + de_add_data(service, DE_STRING, strlen(service_name), (uint8_t *) service_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_name), (uint8_t *) default_avdtp_sink_service_name); + } + + // 0x0100 "Provider Name" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0102); + if (service_provider_name){ + de_add_data(service, DE_STRING, strlen(service_provider_name), (uint8_t *) service_provider_name); + } else { + de_add_data(service, DE_STRING, strlen(default_avdtp_sink_service_provider_name), (uint8_t *) default_avdtp_sink_service_provider_name); + } + + // 0x0311 "Supported Features" + de_add_number(service, DE_UINT, DE_SIZE_16, 0x0311); + de_add_number(service, DE_UINT, DE_SIZE_16, supported_features); +} + + + + diff --git a/test/avdtp/avdtp_source.h b/test/avdtp/avdtp_source.h new file mode 100644 index 000000000..24de81549 --- /dev/null +++ b/test/avdtp/avdtp_source.h @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2016 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 + * + */ + +/* + * avdtp_source.h + * + * Audio/Video Distribution Transport Protocol Source + * + * AVDTP Source is a device that streames media data. + */ + +#ifndef __AVDTP_SOURCE_H +#define __AVDTP_SOURCE_H + +#include + +#if defined __cplusplus +extern "C" { +#endif + +/* API_START */ + +/** + * @brief AVDTP Source service record. + * @param service + * @param service_record_handle + * @param supported_features 16-bit bitmap, see AVDTP_SOURCE_SF_* values in avdtp.h + * @param service_name + * @param service_provider_name + */ +void a2dp_source_create_sdp_record(uint8_t * service, uint32_t service_record_handle, uint16_t supported_features, const char * service_name, const char * service_provider_name); +/* API_END */ + +#if defined __cplusplus +} +#endif + +#endif // __AVDTP_SOURCE_H \ No newline at end of file diff --git a/test/avdtp/avdtp_test.c b/test/avdtp/avdtp_test.c new file mode 100644 index 000000000..cf9535a01 --- /dev/null +++ b/test/avdtp/avdtp_test.c @@ -0,0 +1,162 @@ +/* + * Copyright (C) 2016 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 + * + */ + +#include "btstack_config.h" + +#include +#include +#include +#include +#include + +#include "btstack_event.h" +#include "btstack_memory.h" +#include "btstack_run_loop.h" +#include "gap.h" +#include "hci.h" +#include "hci_cmd.h" +#include "hci_dump.h" +#include "l2cap.h" +#include "stdin_support.h" +#include "avdtp_sink.h" + +static bd_addr_t remote = {0x04, 0x0C, 0xCE, 0xE4, 0x85, 0xD3}; +static uint8_t sdp_avdtp_sink_service_buffer[150]; + +uint16_t local_cid = 0; +static btstack_packet_callback_registration_t hci_event_callback_registration; + +static void packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + bd_addr_t event_addr; + + switch (packet_type) { + case L2CAP_DATA_PACKET: + // just dump data for now + printf("source cid %x -- ", channel); + // printf_hexdump( packet, size ); + break; + + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + + case BTSTACK_EVENT_STATE: + // bt stack activated, get started + if (btstack_event_state_get_state(packet) == HCI_STATE_WORKING){ + + } + break; + 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); + hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + // connection closed -> quit tes app + printf("HCI_EVENT_DISCONNECTION_COMPLETE\n"); + + // exit(0); + break; + + default: + // other event + break; + } + break; + + default: + // other packet type + break; + } +} + +static void show_usage(void){ + printf("\n--- CLI for L2CAP TEST ---\n"); + printf("c - create connection to SDP at addr %s\n", bd_addr_to_str(remote)); + printf("d - disconnect\n"); + printf("Ctrl-c - exit\n"); + printf("---\n"); +} + +static void stdin_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type){ + char buffer; + read(ds->fd, &buffer, 1); + switch (buffer){ + case 'c': + printf("Creating L2CAP Connection to %s, PSM_AVDTP\n", bd_addr_to_str(remote)); + avdtp_sink_connect(remote); + break; + case 'd': + printf("L2CAP Channel Closed\n"); + // l2cap_disconnect(local_cid, 0); + break; + case '\n': + case '\r': + break; + default: + show_usage(); + break; + + } +} + + +int btstack_main(int argc, const char * argv[]); +int btstack_main(int argc, const char * argv[]){ + + /* Register for HCI events */ + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + l2cap_init(); + // Initialize AVDTP Sink + avdtp_sink_init(); + avdtp_sink_register_packet_handler(&packet_handler); + + // Initialize SDP + sdp_init(); + memset(sdp_avdtp_sink_service_buffer, 0, sizeof(sdp_avdtp_sink_service_buffer)); + a2dp_sink_create_sdp_record(sdp_avdtp_sink_service_buffer, 0x10001, 0, NULL, NULL); + sdp_register_service(sdp_avdtp_sink_service_buffer); + + // turn on! + hci_power_control(HCI_POWER_ON); + + btstack_stdin_setup(stdin_process); + return 0; +}