From 733ac1e89f3bf7a997f023020bc29323054bc91c Mon Sep 17 00:00:00 2001 From: Milanka Ringwald Date: Tue, 6 Nov 2018 15:13:10 +0100 Subject: [PATCH] ublox_spp_service_server: Implementation of the ublox SPP-like profile --- example/Makefile.inc | 10 +- example/ublox_spp_le_counter.c | 210 ++++++++++++++ example/ublox_spp_le_counter.gatt | 4 + port/libusb/.gitignore | 13 +- src/ble/gatt-service/ublox_spp_service.gatt | 9 + .../gatt-service/ublox_spp_service_server.c | 271 ++++++++++++++++++ .../gatt-service/ublox_spp_service_server.h | 87 ++++++ 7 files changed, 598 insertions(+), 6 deletions(-) create mode 100644 example/ublox_spp_le_counter.c create mode 100644 example/ublox_spp_le_counter.gatt create mode 100644 src/ble/gatt-service/ublox_spp_service.gatt create mode 100644 src/ble/gatt-service/ublox_spp_service_server.c create mode 100644 src/ble/gatt-service/ublox_spp_service_server.h diff --git a/example/Makefile.inc b/example/Makefile.inc index 325b71ec4..5249fc08b 100644 --- a/example/Makefile.inc +++ b/example/Makefile.inc @@ -122,7 +122,7 @@ EXAMPLES = \ gap_inquiry \ gap_le_advertisements \ gap_link_keys \ - gatt_battery_query \ + gatt_battery_query \ gatt_browser \ gatt_heart_rate_client \ hfp_ag_demo \ @@ -141,6 +141,8 @@ EXAMPLES = \ le_streamer_client \ led_counter \ mod_player \ + nordic_spp_le_counter \ + nordic_spp_le_streamer \ pbap_client_demo \ sdp_bnep_query \ sdp_general_query \ @@ -153,6 +155,7 @@ EXAMPLES = \ spp_counter \ spp_streamer \ spp_streamer_client \ + ublox_spp_le_counter \ EXAMPLES_USING_LE = \ ancs_client_demo \ @@ -171,6 +174,7 @@ EXAMPLES_USING_LE = \ sm_pairing_central \ sm_pairing_peripheral \ spp_and_le_counter \ + ublox_spp_le_counter \ # .o for .c CORE_OBJ = $(CORE:.c=.o) @@ -332,6 +336,9 @@ nordic_spp_le_counter: nordic_spp_le_counter.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_O nordic_spp_le_streamer: nordic_spp_le_streamer.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} nordic_spp_service_server.o nordic_spp_le_streamer.c ${CC} $(filter-out nordic_spp_le_streamer.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ +ublox_spp_le_counter: ublox_spp_le_counter.h ${CORE_OBJ} ${COMMON_OBJ} ${ATT_OBJ} ${GATT_SERVER_OBJ} ${SM_OBJ} ublox_spp_service_server.o ublox_spp_le_counter.c + ${CC} $(filter-out ublox_spp_le_counter.h,$^) ${CFLAGS} ${LDFLAGS} -o $@ + clean: rm -f ${EXAMPLES} *_demo rm -f *.o *.out *.hex *.exe *.wav *.sbc @@ -339,6 +346,7 @@ clean: rm -f gatt_battery_query.h gatt_browser.h sm_pairing_peripheral.h spp_and_le_streamer.h rm -f le_data_channel_server.h sm_pairing_central.h rm -f nordic_spp_le_counter.h nordic_spp_le_streamer.h nordic_spp_le_counter nordic_spp_le_streamer + rm -f ublox_spp_le_counter.h ublox_spp_le_counter rm -f le_streamer_and_counter_client.h le_streamer_and_counter_client rm -f sco_output.msbc sco_input.msbc rm -rf *.dSYM diff --git a/example/ublox_spp_le_counter.c b/example/ublox_spp_le_counter.c new file mode 100644 index 000000000..5972ec14c --- /dev/null +++ b/example/ublox_spp_le_counter.c @@ -0,0 +1,210 @@ +/* + * 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 + * + */ + +#define __BTSTACK_FILE__ "ublox_le_counter.c" + +// ***************************************************************************** +/* EXAMPLE_START(ublox_le_counter): LE Peripheral - Nordic SPP-like profile + * + */ + // ***************************************************************************** + +#include +#include +#include +#include + +#include "ublox_spp_le_counter.h" +#include "btstack.h" +#include "ble/gatt-service/ublox_spp_service_server.h" + +#define HEARTBEAT_PERIOD_MS 1000 + +/* @section Main Application Setup + * + * @text Listing MainConfiguration shows main application code. + * It initializes L2CAP, the Security Manager and configures the ATT Server with the pre-compiled + * ATT Database generated from $ublox_le_counter.gatt$. + * Additionally, it enables the Battery Service Server with the current battery level. + * Finally, it configures the advertisements + * and the heartbeat handler and boots the Bluetooth stack. + * In this example, the Advertisement contains the Flags attribute and the device name. + * The flag 0x06 indicates: LE General Discoverable Mode and BR/EDR not supported. + */ + +/* LISTING_START(MainConfiguration): Init L2CAP SM ATT Server and start heartbeat timer */ +static btstack_timer_source_t heartbeat; +static hci_con_handle_t con_handle = HCI_CON_HANDLE_INVALID; +static btstack_context_callback_registration_t send_request; +static btstack_packet_callback_registration_t hci_event_callback_registration; + +const uint8_t adv_data[] = { + // Flags general discoverable, BR/EDR not supported + 2, BLUETOOTH_DATA_TYPE_FLAGS, 0x06, + // Name + 9, BLUETOOTH_DATA_TYPE_COMPLETE_LOCAL_NAME, 'u', 'B', 'l','o', 'x', 'S', 'P', 'P', + // UUID ... + 17, BLUETOOTH_DATA_TYPE_COMPLETE_LIST_OF_128_BIT_SERVICE_CLASS_UUIDS, 0x1, 0xd7, 0xe9, 0x1, 0x4f, 0xf3, 0x44, 0xe7, 0x83, 0x8f, 0xe2, 0x26, 0xb9, 0xe1, 0x56, 0x24, +}; + +const uint8_t adv_data_len = sizeof(adv_data); + +/* LISTING_END */ + +/* + * @section Heartbeat Handler + * + * @text The heartbeat handler updates the value of the single Characteristic provided in this example, + * and request a ATT_EVENT_CAN_SEND_NOW to send a notification if enabled see Listing heartbeat. + */ + + /* LISTING_START(heartbeat): Hearbeat Handler */ +static int counter = 0; +static char counter_string[30]; +static int counter_string_len; + +static void beat(void){ + counter++; + counter_string_len = sprintf(counter_string, "BTstack counter %03u", counter); +} + +static void ublox_can_send(void * context){ + UNUSED(context); + beat(); + printf("SEND: %s\n", counter_string); + ublox_spp_service_server_send(con_handle, (uint8_t*) counter_string, counter_string_len); +} + +static void heartbeat_handler(struct btstack_timer_source *ts){ + if (con_handle != HCI_CON_HANDLE_INVALID) { + send_request.callback = &ublox_can_send; + ublox_spp_service_server_request_can_send_now(&send_request, con_handle); + } + btstack_run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(ts); +} +/* LISTING_END */ + +static void ublox_data(hci_con_handle_t handle, const uint8_t * data, uint16_t size){ + if (size == 0 && con_handle == HCI_CON_HANDLE_INVALID ){ + con_handle = handle; + printf("Connected with handle 0x%04x\n", con_handle); + } else { + printf("RECV: "); + printf_hexdump(data, size); + } +} + +static void ublox_flow_control(hci_con_handle_t handle, uint16_t credits){ + if (con_handle == HCI_CON_HANDLE_INVALID ){ + con_handle = handle; + printf("Connected with handle 0x%04x\n", handle); + } + printf("credits %d\n", credits); +} +/* + * @section Packet Handler + * + * @text The packet handler is used to: + * - stop the counter after a disconnect + */ + +/* LISTING_START(packetHandler): Packet Handler */ +static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + UNUSED(channel); + UNUSED(size); + + switch (packet_type) { + case HCI_EVENT_PACKET: + switch (hci_event_packet_get_type(packet)) { + case HCI_EVENT_DISCONNECTION_COMPLETE: + con_handle = HCI_CON_HANDLE_INVALID; + break; + default: + break; + } + break; + } +} +/* LISTING_END */ + + +int btstack_main(void); +int btstack_main(void) +{ + // register for HCI events + hci_event_callback_registration.callback = &packet_handler; + hci_add_event_handler(&hci_event_callback_registration); + + l2cap_init(); + + // setup LE device DB + le_device_db_init(); + + // setup SM: Display only + sm_init(); + + // setup ATT server + att_server_init(profile_data, NULL, NULL); + + // setup Nordic SPP service + ublox_spp_service_server_init(&ublox_data, &ublox_flow_control); + + // setup advertisements + uint16_t adv_int_min = 0x0030; + uint16_t adv_int_max = 0x0030; + uint8_t adv_type = 0; + bd_addr_t null_addr; + memset(null_addr, 0, 6); + gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00); + gap_advertisements_set_data(adv_data_len, (uint8_t*) adv_data); + gap_advertisements_enable(1); + + // set one-shot timer + heartbeat.process = &heartbeat_handler; + btstack_run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + btstack_run_loop_add_timer(&heartbeat); + + // beat once + beat(); + + // turn on! + hci_power_control(HCI_POWER_ON); + + return 0; +} +/* EXAMPLE_END */ diff --git a/example/ublox_spp_le_counter.gatt b/example/ublox_spp_le_counter.gatt new file mode 100644 index 000000000..5f750f68e --- /dev/null +++ b/example/ublox_spp_le_counter.gatt @@ -0,0 +1,4 @@ +PRIMARY_SERVICE, GAP_SERVICE +CHARACTERISTIC, GAP_DEVICE_NAME, READ, "uBlox SPP Counter" + +#import \ No newline at end of file diff --git a/port/libusb/.gitignore b/port/libusb/.gitignore index 6c44856f3..f0a763237 100644 --- a/port/libusb/.gitignore +++ b/port/libusb/.gitignore @@ -16,6 +16,7 @@ ble_peripheral_test bnep_test btstack_link_key_db_fs_a.c btstack_link_key_db_fs_b.c +build* classic_test csr_set_bd_addr dut_mode_classic @@ -57,6 +58,10 @@ le_streamer_and_counter_client.h le_streamer_client led_counter mod_player +nordic_spp_le_counter +nordic_spp_le_counter.h +nordic_spp_le_streamer +nordic_spp_le_streamer.h panu_demo pbap_client_demo profile.h @@ -65,6 +70,7 @@ sco_output* sdp_bnep_query sdp_general_query sdp_rfcomm_query +sine_player sm_pairing_central sm_pairing_central.h sm_pairing_peripheral @@ -76,8 +82,5 @@ spp_and_le_streamer.h spp_counter spp_streamer spp_streamer_client -build* -nordic_spp_le_counter -nordic_spp_le_counter.h -nordic_spp_le_streamer -nordic_spp_le_streamer.h \ No newline at end of file +ublox_spp_le_counter +ublox_spp_le_counter.h \ No newline at end of file diff --git a/src/ble/gatt-service/ublox_spp_service.gatt b/src/ble/gatt-service/ublox_spp_service.gatt new file mode 100644 index 000000000..dac3a8702 --- /dev/null +++ b/src/ble/gatt-service/ublox_spp_service.gatt @@ -0,0 +1,9 @@ +// Specification ublox SPP-like profile + +// ublox SPP-like +PRIMARY_SERVICE, 2456E1B9-26E2-8F83-E744-F34F01E9D701 +// FIFO +CHARACTERISTIC, 2456E1B9-26E2-8F83-E744-F34F01E9D703, DYNAMIC | NOTIFY | INDICATE | WRITE | WRITE_WITHOUT_RESPONSE, +// Credits +CHARACTERISTIC, 2456E1B9-26E2-8F83-E744-F34F01E9D704, DYNAMIC | NOTIFY | INDICATE | WRITE | WRITE_WITHOUT_RESPONSE, + diff --git a/src/ble/gatt-service/ublox_spp_service_server.c b/src/ble/gatt-service/ublox_spp_service_server.c new file mode 100644 index 000000000..7a512d891 --- /dev/null +++ b/src/ble/gatt-service/ublox_spp_service_server.c @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2018 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 + * + */ + +#define __BTSTACK_FILE__ "ublox_spp_service_server.c" + +/** + * Implementation of the ublox SPP-like profile + * + * To use with your application, add '#import credits_client_configuration_descriptor_value; +} + +static ublox_spp_service_t * ublox_get_instance_for_con_handle(hci_con_handle_t con_handle){ + ublox_spp_service_t * instance = &ublox_spp; + if (con_handle == HCI_CON_HANDLE_INVALID) return NULL; + instance->con_handle = con_handle; + return instance; +} + +static inline void ublox_spp_service_init_credits(ublox_spp_service_t * instance){ + instance->incoming_credits = 0; + instance->outgoing_credits = 0; + instance->delta_credits = UBLOX_SPP_MAX_CREDITS; +} + +static uint16_t ublox_spp_service_read_callback(hci_con_handle_t con_handle, uint16_t attribute_handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ + UNUSED(offset); + UNUSED(buffer_size); + ublox_spp_service_t * instance = ublox_get_instance_for_con_handle(con_handle); + if (!instance) return 0; + + if (attribute_handle == instance->fifo_client_configuration_descriptor_handle){ + if (buffer){ + little_endian_store_16(buffer, 0, instance->fifo_client_configuration_descriptor_value); + } + return 2; + } + + if (attribute_handle == instance->credits_client_configuration_descriptor_handle){ + if (buffer){ + little_endian_store_16(buffer, 0, instance->credits_client_configuration_descriptor_value); + } + return 2; + } + return 0; +} + + +static int ublox_spp_service_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(transaction_mode); + UNUSED(offset); + ublox_spp_service_t * instance = ublox_get_instance_for_con_handle(con_handle); + if (!instance) return 0; + + if (attribute_handle == instance->fifo_value_handle){ + instance->client_data_callback(con_handle, &buffer[0], buffer_size); + if (!ublox_spp_service_flow_control_enabled(instance)) return 0; + if (!instance->incoming_credits) return 0; + instance->incoming_credits--; + if (instance->incoming_credits < UBLOX_SPP_CREDITS_THRESHOLD){ + att_server_request_to_send_notification(&instance->credits_callback, instance->con_handle); + } + } + + if (attribute_handle == instance->fifo_client_configuration_descriptor_handle){ + if (buffer_size < 2){ + return ATT_ERROR_INVALID_OFFSET; + } + instance->fifo_client_configuration_descriptor_value = little_endian_read_16(buffer, 0); + instance->client_data_callback(con_handle, NULL, 0); + } + + if (attribute_handle == instance->credits_value_handle){ + if (!ublox_spp_service_flow_control_enabled(instance)) return 0; + int8_t credits = (int8_t)buffer[0]; + if (credits <= 0) return 0; + instance->outgoing_credits += credits; + if (instance->request){ + btstack_context_callback_registration_t * request = instance->request; + instance->request = NULL; + att_server_request_to_send_notification(request, instance->con_handle); + } + } + + if (attribute_handle == instance->credits_client_configuration_descriptor_handle){ + if (buffer_size < 2){ + return ATT_ERROR_INVALID_OFFSET; + } + instance->credits_client_configuration_descriptor_value = little_endian_read_16(buffer, 0); + + ublox_spp_service_init_credits(instance); + if (instance->credits_client_configuration_descriptor_value){ + att_server_request_to_send_notification(&instance->credits_callback, instance->con_handle); + } + } + + return 0; +} + +static void ublox_spp_credits_callback(void * context){ + ublox_spp_service_t * instance = (ublox_spp_service_t *) context; + if (!instance) return; + + instance->delta_credits = UBLOX_SPP_MAX_CREDITS - instance->incoming_credits; + if (instance->delta_credits){ + instance->incoming_credits = UBLOX_SPP_MAX_CREDITS; + att_server_notify(instance->con_handle, instance->credits_value_handle, &instance->delta_credits, 1); + } +} +/** + * @brief Init ublox SPP Service Server with ATT DB + * @param callback for tx data from peer + */ +void ublox_spp_service_server_init(void (*client_data_callback)(hci_con_handle_t con_handle, const uint8_t * data, uint16_t size), + void (*client_credits_callback)(hci_con_handle_t con_handle, uint16_t credits)){ + ublox_spp_service_t * instance = &ublox_spp; + instance->client_data_callback = client_data_callback; + instance->client_credits_callback = client_credits_callback; + + instance->credits_callback.callback = ublox_spp_credits_callback; + instance->credits_callback.context = instance; + ublox_spp_service_init_credits(instance); + + + // get service handle range + uint16_t start_handle = 0; + uint16_t end_handle = 0xfff; + int service_found = gatt_server_get_get_handle_range_for_service_with_uuid128(ublox_spp_profile_uuid128, &start_handle, &end_handle); + if (!service_found) return; + + // get characteristic value handle and client configuration handle + // FIFO + instance->fifo_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(start_handle, end_handle, ublox_spp_fifo_uuid128); + instance->fifo_client_configuration_descriptor_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(start_handle, end_handle, ublox_spp_fifo_uuid128); + // Credits + instance->credits_value_handle = gatt_server_get_value_handle_for_characteristic_with_uuid128(start_handle, end_handle, ublox_spp_credits_uuid128); + instance->credits_client_configuration_descriptor_handle = gatt_server_get_client_configuration_handle_for_characteristic_with_uuid128(start_handle, end_handle, ublox_spp_credits_uuid128); + + log_info("FIFO value handle 0x%02x", instance->fifo_value_handle); + log_info("FIFO CCC value handle 0x%02x", instance->fifo_client_configuration_descriptor_handle); + log_info("Credits value handle 0x%02x", instance->credits_value_handle); + log_info("Credits CCC value handle 0x%02x", instance->credits_client_configuration_descriptor_handle); + + // register service with ATT Server + ublox_spp_service.start_handle = start_handle; + ublox_spp_service.end_handle = end_handle; + ublox_spp_service.read_callback = &ublox_spp_service_read_callback; + ublox_spp_service.write_callback = &ublox_spp_service_write_callback; + att_server_register_service_handler(&ublox_spp_service); +} + +/** + * @brief Queue send request. When called, one packet can be send via ublox_spp_service_send below + * @param request + * @param con_handle + */ +void ublox_spp_service_server_request_can_send_now(btstack_context_callback_registration_t * request, hci_con_handle_t con_handle){ + ublox_spp_service_t * instance = &ublox_spp; + if (!ublox_spp_service_flow_control_enabled(instance) || instance->outgoing_credits) { + att_server_request_to_send_notification(request, con_handle); + return; + } + instance->request = request; +} + +/** + * @brief Send data + * @param con_handle + * @param data + * @param size + */ +int ublox_spp_service_server_send(hci_con_handle_t con_handle, const uint8_t * data, uint16_t size){ + ublox_spp_service_t * instance = &ublox_spp; + if (ublox_spp_service_flow_control_enabled(instance)){ + if (instance->outgoing_credits > 0){ + instance->outgoing_credits--; + } + } + return att_server_notify(con_handle, instance->fifo_value_handle, &data[0], size); +} + diff --git a/src/ble/gatt-service/ublox_spp_service_server.h b/src/ble/gatt-service/ublox_spp_service_server.h new file mode 100644 index 000000000..3b068bb9c --- /dev/null +++ b/src/ble/gatt-service/ublox_spp_service_server.h @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2018 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 + * + */ +#ifndef __UBLOX_SPP_H +#define __UBLOX_SPP_H + +#include +#include "bluetooth.h" +#include "btstack_defines.h" + +#if defined __cplusplus +extern "C" { +#endif + +/* API_START */ + +/** + * Implementation of the ublox SPP-like profile + * + * To use with your application, add '#import