BLE demo for mbed Ported RunningElectronics's SBDBT firmware for BLE. It can communicate with iOS
Dependencies: FatFileSystem mbed
Fork of BTstack by
Revision 1:6078e430af82, committed 2013-02-07
- Comitter:
- todotani
- Date:
- Thu Feb 07 14:01:01 2013 +0000
- Parent:
- 0:1ed23ab1345f
- Child:
- 2:24b33d92e086
- Commit message:
- BLE demo for mbed; Ported SBDBT (RunningElectronics) firmware for BLE to mbed. It can communicate with iOS
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BLE_demo.cpp Thu Feb 07 14:01:01 2013 +0000 @@ -0,0 +1,302 @@ +/* + * BLE_demo + */ + +// mbed specific +#include "mbed.h" +Serial pc(USBTX, USBRX); +DigitalOut led1(LED1), led2(LED2), led3(LED3); + +#define HEARTBEAT_PERIOD_MS 500 + +// from btstack ble_server.c +#include "global.h" +#include "debug.h" +#include "btstack/btstack.h" +#include "btstack/hci_cmds.h" +#include "btstack/run_loop.h" + +#include "hci.h" +#include "l2cap.h" +#include "btstack_memory.h" +#include "remote_device_db.h" +#include "config.h" + +#include "att.h" + +unsigned timer_counter=0; +uint8_t startup_state=0; + + +hci_transport_t * hci_transport_picusb_instance(); + +static att_connection_t att_connection; +static uint16_t att_response_handle = 0; +static uint16_t att_response_size = 0; +static uint8_t att_response_buffer[28]; + +uint8_t connection_status=0; + +void hexdump2(void *data, int size){ + int i; + for (i=0; i<size;i++){ + log_info("%02X ", ((uint8_t *)data)[i]); + } + log_info("\n"); +} + +static void att_try_respond(void){ + if (!att_response_size) return; + if (!att_response_handle) return; + if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)) return; + + // update state before sending packet + uint16_t size = att_response_size; + att_response_size = 0; + l2cap_send_connectionless(att_response_handle, L2CAP_CID_ATTRIBUTE_PROTOCOL, att_response_buffer, size); +} + + +static void att_packet_handler(uint8_t packet_type, uint16_t handle, uint8_t *packet, uint16_t size){ + if (packet_type != ATT_DATA_PACKET) return; + + att_response_handle = handle; + att_response_size = att_handle_request(&att_connection, packet, size, att_response_buffer); + att_try_respond(); +} + + +// enable LE, setup ADV data +static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ + static bd_addr_t addr; +// uint8_t adv_data[] = { 02, 01, 05, 03, 02, 0xf0, 0xff }; + const uint8_t adv_data[31]="\x02\x01\x05" "\x06\x09SBDBT"; + switch (packet_type) { + + case HCI_EVENT_PACKET: + switch (packet[0]) { + + case BTSTACK_EVENT_STATE: + // bt stack activated, get started - set local name + if (packet[2] == HCI_STATE_WORKING) { + log_info("Working!\n"); + hci_send_cmd(&hci_read_local_supported_features); + } + break; + + case DAEMON_EVENT_HCI_PACKET_SENT: + att_try_respond(); + break; + + case HCI_EVENT_LE_META: + switch (packet[2]) { + case HCI_SUBEVENT_LE_CONNECTION_COMPLETE: + // reset connection MTU + att_connection.mtu = 23; + break; + default: + break; + } + break; + + case BTSTACK_EVENT_NR_CONNECTIONS_CHANGED: + if (packet[2]) { + connection_status=1; + log_info("CONNECTED\n"); + } else { + connection_status=0; + log_info("NOT CONNECTED\n"); + } + break; + + case HCI_EVENT_DISCONNECTION_COMPLETE: + att_response_handle =0; + att_response_size = 0; + + // restart advertising + hci_send_cmd(&hci_le_set_advertise_enable, 1); + break; + + case HCI_EVENT_COMMAND_COMPLETE: + if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){ + bt_flip_addr(addr, &packet[6]); + log_info("BD ADDR: %s\n", bd_addr_to_str(addr)); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_read_local_supported_features)){ + log_info("Local supported features: %04lX%04lX\n", READ_BT_32(packet, 10), READ_BT_32(packet, 6)); + hci_send_cmd(&hci_set_event_mask, 0xffffffff, 0x20001fff); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_set_event_mask)){ + hci_send_cmd(&hci_write_le_host_supported, 1, 1); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_write_le_host_supported)){ + hci_send_cmd(&hci_le_set_event_mask, 0xffffffff, 0xffffffff); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_event_mask)){ + hci_send_cmd(&hci_le_read_buffer_size); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_le_read_buffer_size)){ + log_info("LE buffer size: %u, count %u\n", READ_BT_16(packet,6), packet[8]); + hci_send_cmd(&hci_le_read_supported_states); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_le_read_supported_states)){ + hci_send_cmd(&hci_le_set_advertising_parameters, 0x0400, 0x0800, 0, 0, 0, &addr, 0x07, 0); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_advertising_parameters)){ + hci_send_cmd(&hci_le_set_advertising_data, sizeof(adv_data), adv_data); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_advertising_data)){ + hci_send_cmd(&hci_le_set_scan_response_data, 10, adv_data); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_scan_response_data)){ + hci_send_cmd(&hci_le_set_advertise_enable, 1); + break; + } + if (COMMAND_COMPLETE_EVENT(packet, hci_le_set_advertise_enable)){ + hci_discoverable_control(1); + log_info("startup_state=1\n"); + startup_state=1; + led1 = 0; + break; + } + + } + } +} + +// test profile +#include "profile.h" + +static uint8_t strbuf[80]; +static uint8_t ledvalue; + +// read requests +static uint16_t att_read_callback(uint16_t handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ + uint16_t ret=0,val; + + if(buffer){ + log_info("READ Callback, handle %04x\n", handle); + led1 = 1; + } + switch(handle){ + case 0x000b: + ret=strlen((char*)strbuf); + if(buffer && ret<=buffer_size){ + log_info("Read text: %s\n", strbuf); + memcpy(buffer,strbuf,ret); + } + break; + case 0x000d: + if(buffer && buffer_size){ + log_info("Read value: %u\n", buffer[0]); + buffer[0]=ledvalue; + } + ret=1; + break; + case 0x000f: + if(buffer && buffer_size>=2){ + val=timer_counter; + log_info("Read value: %u\n", val); + buffer[0]=val&0xff; + buffer[1]=val>>8; + } + ret=2; + break; + } + return ret; +} + +// write requests +static void att_write_callback(uint16_t handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size, signature_t * signature){ + log_info("WRITE Callback, handle %04x\n", handle); + led1 = 1; + switch(handle){ + case 0x000b: + buffer[buffer_size]=0; + if(sizeof(strbuf)>buffer_size){ + strcpy((char*)strbuf,(char*)buffer); + } + log_info("New text: %s\n", buffer); + break; + case 0x000d: + log_info("New value: %u\n", buffer[0]); + ledvalue=buffer[0]; + if (buffer[0]){ + led1 = 1; + } else { + led1 = 0; + } + break; + } +} + + +static void heartbeat_handler(struct timer *ts){ + run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); + run_loop_add_timer(ts); + led2 = !led2; +} + + +// main +int main(void) +{ + pc.baud(115200); + log_info("%s\n", __FILE__); + + // init LEDs + led1 = led2 = led3 = 1; + + /// GET STARTED with BTstack /// + btstack_memory_init(); + run_loop_init(RUN_LOOP_EMBEDDED); + + // init HCI + // use BlueUSB + hci_transport_t* transport = hci_transport_usb_instance(); + bt_control_t * control = NULL; + hci_uart_config_t * config = NULL; + remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory; + hci_init(transport, config, control, remote_db); + + // use eHCILL + // bt_control_cc256x_enable_ehcill(1); + + // set up l2cap_le + l2cap_init(); + l2cap_register_fixed_channel(att_packet_handler, L2CAP_CID_ATTRIBUTE_PROTOCOL); + l2cap_register_packet_handler(packet_handler); + + // set up ATT + att_set_db(profile_data); + att_set_write_callback(att_write_callback); + att_set_read_callback(att_read_callback); + att_dump_attributes(); + att_connection.mtu = 27; + + // set one-shot timer + timer_source_t heartbeat; + heartbeat.process = &heartbeat_handler; + run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); + run_loop_add_timer(&heartbeat); + + log_info("Run...\n\r"); + + // turn on! + hci_power_control(HCI_POWER_ON); + + // go! + run_loop_execute(); + + // happy compiler! + return 0; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/att.c Thu Feb 07 14:01:01 2013 +0000 @@ -0,0 +1,913 @@ +/* + * Copyright (C) 2011-2012 by Matthias Ringwald + * + * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch + * + */ + +#include <stdio.h> +#include <string.h> + +#include "att.h" + +// from src/utils. +#define READ_BT_16( buffer, pos) ( ((uint16_t) buffer[pos]) | (((uint16_t)buffer[pos+1]) << 8)) + +// Buetooth Base UUID 00000000-0000-1000-8000-00805F9B34FB in little endian +static const uint8_t bluetooth_base_uuid[] = { 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +static void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){ + buffer[pos++] = value; + buffer[pos++] = value >> 8; +} + +static void hexdump2(void const *data, int size){ + int i; + for (i=0; i<size;i++){ + printf("%02X ", ((uint8_t *)data)[i]); + } + printf("\n"); +} + +static void printUUID128(const uint8_t * uuid){ + int i; + for (i=15; i >= 0 ; i--){ + printf("%02X", uuid[i]); + switch (i){ + case 4: + case 6: + case 8: + case 10: + printf("-"); + break; + default: + break; + } + } +} + +static int is_Bluetooth_Base_UUID(uint8_t const *uuid){ + if (memcmp(&uuid[0], &bluetooth_base_uuid[0], 12)) return 0; + if (memcmp(&uuid[14], &bluetooth_base_uuid[14], 2)) return 0; + return 1; + +} + +// ATT Database +static uint8_t const * att_db = NULL; +static att_read_callback_t att_read_callback = NULL; +static att_write_callback_t att_write_callback = NULL; + +// new java-style iterator +typedef struct att_iterator { + // private + uint8_t const * att_ptr; + // public + uint16_t size; + uint16_t flags; + uint16_t handle; + uint8_t const * uuid; + uint16_t value_len; + uint8_t const * value; +} att_iterator_t; + +void att_iterator_init(att_iterator_t *it){ + it->att_ptr = att_db; +} + +int att_iterator_has_next(att_iterator_t *it){ + return it->att_ptr != NULL; +} + +void att_iterator_fetch_next(att_iterator_t *it){ + it->size = READ_BT_16(it->att_ptr, 0); + if (it->size == 0){ + it->flags = 0; + it->handle = 0; + it->uuid = NULL; + it->value_len = 0; + it->value = NULL; + it->att_ptr = NULL; + return; + } + it->flags = READ_BT_16(it->att_ptr, 2); + it->handle = READ_BT_16(it->att_ptr, 4); + it->uuid = &it->att_ptr[6]; + // handle 128 bit UUIDs + if (it->flags & ATT_PROPERTY_UUID128){ + it->value_len = it->size - 22; + it->value = &it->att_ptr[22]; + } else { + it->value_len = it->size - 8; + it->value = &it->att_ptr[8]; + } + // advance AFTER setting values + it->att_ptr += it->size; +} + +int att_iterator_match_uuid16(att_iterator_t *it, uint16_t uuid){ + if (it->handle == 0) return 0; + if (it->flags & ATT_PROPERTY_UUID128){ + if (!is_Bluetooth_Base_UUID(it->uuid)) return 0; + return READ_BT_16(it->uuid, 12) == uuid; + } + return READ_BT_16(it->uuid, 0) == uuid; +} + +int att_iterator_match_uuid(att_iterator_t *it, uint8_t *uuid, uint16_t uuid_len){ + if (it->handle == 0) return 0; + // input: UUID16 + if (uuid_len == 2) { + return att_iterator_match_uuid16(it, READ_BT_16(uuid, 0)); + } + // input and db: UUID128 + if (it->flags & ATT_PROPERTY_UUID128){ + return memcmp(it->uuid, uuid, 16) == 0; + } + // input: UUID128, db: UUID16 + if (!is_Bluetooth_Base_UUID(uuid)) return 0; + return READ_BT_16(uuid, 12) == READ_BT_16(it->uuid, 0); +} + + +int att_find_handle(att_iterator_t *it, uint16_t handle){ + att_iterator_init(it); + while (att_iterator_has_next(it)){ + att_iterator_fetch_next(it); + if (it->handle != handle) continue; + return 1; + } + return 0; +} + +static void att_update_value_len(att_iterator_t *it){ + if ((it->flags & ATT_PROPERTY_DYNAMIC) == 0 || !att_read_callback) return; + it->value_len = (*att_read_callback)(it->handle, 0, NULL, 0); + return; +} + +static int att_copy_value(att_iterator_t *it, uint16_t offset, uint8_t * buffer, uint16_t buffer_size){ + + // DYNAMIC + if ((it->flags & ATT_PROPERTY_DYNAMIC) && att_read_callback) { + return (*att_read_callback)(it->handle, offset, buffer, buffer_size); + } + + // STATIC + uint16_t bytes_to_copy = it->value_len; + if (bytes_to_copy > buffer_size){ + bytes_to_copy = buffer_size; + } + memcpy(buffer, it->value, bytes_to_copy); + return bytes_to_copy; +} + +void att_set_db(uint8_t const * db){ + att_db = db; +} + +void att_set_read_callback(att_read_callback_t callback){ + att_read_callback = callback; +} + +void att_set_write_callback(att_write_callback_t callback){ + att_write_callback = callback; +} + +void att_dump_attributes(void){ + att_iterator_t it; + att_iterator_init(&it); + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + if (it.handle == 0) { + printf("Handle: END\n"); + return; + } + printf("Handle: 0x%04x, flags: 0x%04x, uuid: ", it.handle, it.flags); + if (it.flags & ATT_PROPERTY_UUID128){ + printUUID128(it.uuid); + } else { + printf("%04x", READ_BT_16(it.uuid, 0)); + } + printf(", value_len: %u, value: ", it.value_len); + hexdump2(it.value, it.value_len); + } +} + +static uint16_t setup_error(uint8_t * response_buffer, uint16_t request, uint16_t handle, uint8_t error_code){ + response_buffer[0] = ATT_ERROR_RESPONSE; + response_buffer[1] = request; + bt_store_16(response_buffer, 2, handle); + response_buffer[4] = error_code; + return 5; +} + +static uint16_t setup_error_atribute_not_found(uint8_t * response_buffer, uint16_t request, uint16_t start_handle){ + return setup_error(response_buffer, request, start_handle, ATT_ERROR_ATTRIBUTE_NOT_FOUND); +} + +static uint16_t setup_error_invalid_handle(uint8_t * response_buffer, uint16_t request, uint16_t handle){ + return setup_error(response_buffer, request, handle, ATT_ERROR_ATTRIBUTE_INVALID); +} + +static uint16_t setup_error_invalid_offset(uint8_t * response_buffer, uint16_t request, uint16_t handle){ + return setup_error(response_buffer, request, handle, ATT_ERROR_INVALID_OFFSET); +} + +// +// MARK: ATT_EXCHANGE_MTU_REQUEST +// +static uint16_t handle_exchange_mtu_request(att_connection_t * att_connection, uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer){ + + uint16_t client_rx_mtu = READ_BT_16(request_buffer, 1); + if (client_rx_mtu < att_connection->mtu){ + att_connection->mtu = client_rx_mtu; + } + + response_buffer[0] = ATT_EXCHANGE_MTU_RESPONSE; + bt_store_16(response_buffer, 1, att_connection->mtu); + return 3; +} + + +// +// MARK: ATT_FIND_INFORMATION_REQUEST +// +// TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID +// +static uint16_t handle_find_information_request2(uint8_t * response_buffer, uint16_t response_buffer_size, + uint16_t start_handle, uint16_t end_handle){ + + printf("ATT_FIND_INFORMATION_REQUEST: from %04X to %04X\n", start_handle, end_handle); + + uint16_t offset = 1; + uint16_t pair_len = 0; + + att_iterator_t it; + att_iterator_init(&it); + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + if (!it.handle) break; + if (it.handle > end_handle) break; + if (it.handle < start_handle) continue; + + att_update_value_len(&it); + + // printf("Handle 0x%04x\n", it.handle); + + // check if value has same len as last one + uint16_t this_pair_len = 2 + it.value_len; + if (offset > 1){ + if (pair_len != this_pair_len) { + break; + } + } + + // first + if (offset == 1) { + pair_len = this_pair_len; + if (it.value_len == 2) { + response_buffer[offset] = 0x01; // format + } else { + response_buffer[offset] = 0x02; + } + offset++; + } + + // space? + if (offset + pair_len > response_buffer_size) { + if (offset > 2) break; + it.value_len = response_buffer_size - 4; + } + + // store + bt_store_16(response_buffer, offset, it.handle); + offset += 2; + uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); + offset += bytes_copied; + } + + if (offset == 1){ + return setup_error_atribute_not_found(response_buffer, ATT_FIND_INFORMATION_REQUEST, start_handle); + } + + response_buffer[0] = ATT_FIND_INFORMATION_REPLY; + return offset; +} + +static uint16_t handle_find_information_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + return handle_find_information_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3)); +} + +// +// MARK: ATT_FIND_BY_TYPE_VALUE +// +// "Only attributes with attribute handles between and including the Starting Handle parameter +// and the Ending Handle parameter that match the requested attri- bute type and the attribute +// value that have sufficient permissions to allow reading will be returned" -> (1) +// +// TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID +// +// NOTE: doesn't handle DYNAMIC values +// NOTE: only supports 16 bit UUIDs +// +static uint16_t handle_find_by_type_value_request2(uint8_t * response_buffer, uint16_t response_buffer_size, + uint16_t start_handle, uint16_t end_handle, + uint16_t attribute_type, uint16_t attribute_len, uint8_t* attribute_value){ + + printf("ATT_FIND_BY_TYPE_VALUE_REQUEST: from %04X to %04X, type %04X, value: ", start_handle, end_handle, attribute_type); + hexdump2(attribute_value, attribute_len); + + uint16_t offset = 1; + uint16_t in_group = 0; + uint16_t prev_handle = 0; + + att_iterator_t it; + att_iterator_init(&it); + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + + if (it.handle && it.handle < start_handle) continue; + if (it.handle > end_handle) break; // (1) + + // close current tag, if within a group and a new service definition starts or we reach end of att db + if (in_group && + (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){ + + printf("End of group, handle 0x%04x\n", prev_handle); + bt_store_16(response_buffer, offset, prev_handle); + offset += 2; + in_group = 0; + + // check if space for another handle pair available + if (offset + 4 > response_buffer_size){ + break; + } + } + + // keep track of previous handle + prev_handle = it.handle; + + // does current attribute match + if (it.handle && att_iterator_match_uuid16(&it, attribute_type) && attribute_len == it.value_len && memcmp(attribute_value, it.value, it.value_len) == 0){ + printf("Begin of group, handle 0x%04x\n", it.handle); + bt_store_16(response_buffer, offset, it.handle); + offset += 2; + in_group = 1; + } + } + + if (offset == 1){ + return setup_error_atribute_not_found(response_buffer, ATT_FIND_BY_TYPE_VALUE_REQUEST, start_handle); + } + + response_buffer[0] = ATT_FIND_BY_TYPE_VALUE_RESPONSE; + return offset; +} + +static uint16_t handle_find_by_type_value_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + int attribute_len = request_len - 7; + return handle_find_by_type_value_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), + READ_BT_16(request_buffer, 3), READ_BT_16(request_buffer, 5), attribute_len, &request_buffer[7]); +} + +// +// MARK: ATT_READ_BY_TYPE_REQUEST +// +static uint16_t handle_read_by_type_request2(uint8_t * response_buffer, uint16_t response_buffer_size, + uint16_t start_handle, uint16_t end_handle, + uint16_t attribute_type_len, uint8_t * attribute_type){ + + printf("ATT_READ_BY_TYPE_REQUEST: from %04X to %04X, type: ", start_handle, end_handle); + hexdump2(attribute_type, attribute_type_len); + + uint16_t offset = 1; + uint16_t pair_len = 0; + + att_iterator_t it; + att_iterator_init(&it); + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + + if (!it.handle) break; + if (it.handle < start_handle) continue; + if (it.handle > end_handle) break; // (1) + + // does current attribute match + if (!att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) continue; + + att_update_value_len(&it); + + // check if value has same len as last one + uint16_t this_pair_len = 2 + it.value_len; + if (offset > 1){ + if (pair_len != this_pair_len) { + break; + } + } + + // first + if (offset == 1) { + pair_len = this_pair_len; + response_buffer[offset] = pair_len; + offset++; + } + + // space? + if (offset + pair_len > response_buffer_size) { + if (offset > 2) break; + it.value_len = response_buffer_size - 4; + } + + // store + bt_store_16(response_buffer, offset, it.handle); + offset += 2; + uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); + offset += bytes_copied; + } + + if (offset == 1){ + return setup_error_atribute_not_found(response_buffer, ATT_READ_BY_TYPE_REQUEST, start_handle); + } + + response_buffer[0] = ATT_READ_BY_TYPE_RESPONSE; + return offset; +} + +static uint16_t handle_read_by_type_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + int attribute_type_len; + if (request_len <= 7){ + attribute_type_len = 2; + } else { + attribute_type_len = 16; + } + return handle_read_by_type_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3), attribute_type_len, &request_buffer[5]); +} + +// +// MARK: ATT_READ_BY_TYPE_REQUEST +// +static uint16_t handle_read_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle){ + + printf("ATT_READ_REQUEST: handle %04x\n", handle); + + att_iterator_t it; + int ok = att_find_handle(&it, handle); + if (!ok){ + return setup_error_atribute_not_found(response_buffer, ATT_READ_REQUEST, handle); + } + + att_update_value_len(&it); + + uint16_t offset = 1; + // limit data + if (offset + it.value_len > response_buffer_size) { + it.value_len = response_buffer_size - 1; + } + + // store + uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); + offset += bytes_copied; + + response_buffer[0] = ATT_READ_RESPONSE; + return offset; +} + +static uint16_t handle_read_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + return handle_read_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1)); +} + +// +// MARK: ATT_READ_BLOB_REQUEST 0x0c +// +static uint16_t handle_read_blob_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t handle, uint16_t value_offset){ + printf("ATT_READ_BLOB_REQUEST: handle %04x, offset %u\n", handle, value_offset); + + att_iterator_t it; + int ok = att_find_handle(&it, handle); + if (!ok){ + return setup_error_atribute_not_found(response_buffer, ATT_READ_BLOB_REQUEST, handle); + } + + att_update_value_len(&it); + + if (value_offset >= it.value_len){ + return setup_error_invalid_offset(response_buffer, ATT_READ_BLOB_REQUEST, handle); + } + + // limit data + uint16_t offset = 1; + if (offset + it.value_len - value_offset > response_buffer_size) { + it.value_len = response_buffer_size - 1 + value_offset; + } + + // store + uint16_t bytes_copied = att_copy_value(&it, value_offset, response_buffer + offset, it.value_len - value_offset); + offset += bytes_copied; + + response_buffer[0] = ATT_READ_BLOB_RESPONSE; + return offset; +} + +uint16_t handle_read_blob_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + return handle_read_blob_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3)); +} + +// +// MARK: ATT_READ_MULTIPLE_REQUEST 0x0e +// +static uint16_t handle_read_multiple_request2(uint8_t * response_buffer, uint16_t response_buffer_size, uint16_t num_handles, uint16_t * handles){ + printf("ATT_READ_MULTIPLE_REQUEST: num handles %u\n", num_handles); + + uint16_t offset = 1; + + int i; + for (i=0;i<num_handles;i++){ + uint16_t handle = handles[i]; + + if (handle == 0){ + return setup_error_invalid_handle(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle); + } + + att_iterator_t it; + + int ok = att_find_handle(&it, handle); + if (!ok){ + return setup_error_invalid_handle(response_buffer, ATT_READ_MULTIPLE_REQUEST, handle); + } + + att_update_value_len(&it); + + // limit data + if (offset + it.value_len > response_buffer_size) { + it.value_len = response_buffer_size - 1; + } + + // store + uint16_t bytes_copied = att_copy_value(&it, 0, response_buffer + offset, it.value_len); + offset += bytes_copied; + } + + response_buffer[0] = ATT_READ_MULTIPLE_RESPONSE; + return offset; +} +uint16_t handle_read_multiple_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + int num_handles = (request_len - 1) >> 1; + return handle_read_multiple_request2(response_buffer, response_buffer_size, num_handles, (uint16_t*) &request_buffer[1]); +} + +// +// MARK: ATT_READ_BY_GROUP_TYPE_REQUEST 0x10 +// +// TODO: handle other types then GATT_PRIMARY_SERVICE_UUID and GATT_SECONDARY_SERVICE_UUID +// +// NOTE: doesn't handle DYNAMIC values +// +static uint16_t handle_read_by_group_type_request2(uint8_t * response_buffer, uint16_t response_buffer_size, + uint16_t start_handle, uint16_t end_handle, + uint16_t attribute_type_len, uint8_t * attribute_type){ + + printf("ATT_READ_BY_GROUP_TYPE_REQUEST: from %04X to %04X, buffer size %u, type: ", start_handle, end_handle, response_buffer_size); + hexdump2(attribute_type, attribute_type_len); + + uint16_t offset = 1; + uint16_t pair_len = 0; + uint16_t in_group = 0; + uint16_t group_start_handle = 0; + uint8_t const * group_start_value = NULL; + uint16_t prev_handle = 0; + + att_iterator_t it; + att_iterator_init(&it); + while (att_iterator_has_next(&it)){ + att_iterator_fetch_next(&it); + + if (it.handle && it.handle < start_handle) continue; + if (it.handle > end_handle) break; // (1) + + // close current tag, if within a group and a new service definition starts or we reach end of att db + if (in_group && + (it.handle == 0 || att_iterator_match_uuid16(&it, GATT_PRIMARY_SERVICE_UUID) || att_iterator_match_uuid16(&it, GATT_SECONDARY_SERVICE_UUID))){ + // TODO: check if handle is included in start/end range + // printf("End of group, handle 0x%04x, val_len: %u\n", prev_handle, pair_len - 4); + + bt_store_16(response_buffer, offset, group_start_handle); + offset += 2; + bt_store_16(response_buffer, offset, prev_handle); + offset += 2; + memcpy(response_buffer + offset, group_start_value, pair_len - 4); + offset += pair_len - 4; + in_group = 0; + + // check if space for another handle pair available + if (offset + pair_len > response_buffer_size){ + break; + } + } + + // keep track of previous handle + prev_handle = it.handle; + + // does current attribute match + // printf("compare: %04x == %04x\n", *(uint16_t*) context->attribute_type, *(uint16_t*) uuid); + if (it.handle && att_iterator_match_uuid(&it, attribute_type, attribute_type_len)) { + + // check if value has same len as last one + uint16_t this_pair_len = 4 + it.value_len; + if (offset > 1){ + if (this_pair_len != pair_len) { + break; + } + } + + // printf("Begin of group, handle 0x%04x\n", it.handle); + + // first + if (offset == 1) { + pair_len = this_pair_len; + response_buffer[offset] = this_pair_len; + offset++; + } + + group_start_handle = it.handle; + group_start_value = it.value; + in_group = 1; + } + } + + if (offset == 1){ + return setup_error_atribute_not_found(response_buffer, ATT_READ_BY_GROUP_TYPE_REQUEST, start_handle); + } + + response_buffer[0] = ATT_READ_BY_GROUP_TYPE_RESPONSE; + return offset; +} +uint16_t handle_read_by_group_type_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + int attribute_type_len; + if (request_len <= 7){ + attribute_type_len = 2; + } else { + attribute_type_len = 16; + } + return handle_read_by_group_type_request2(response_buffer, response_buffer_size, READ_BT_16(request_buffer, 1), READ_BT_16(request_buffer, 3), attribute_type_len, &request_buffer[5]); +} + +// +// MARK: ATT_WRITE_REQUEST 0x12 +static uint16_t handle_write_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + uint16_t handle = READ_BT_16(request_buffer, 1); + if (!att_write_callback) { + // TODO: Use "Write Not Permitted" + return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); + } + att_iterator_t it; + int ok = att_find_handle(&it, handle); + if (!ok) { + return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); + } + if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) { + // TODO: Use "Write Not Permitted" + return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); + } + (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3, NULL); + response_buffer[0] = ATT_WRITE_RESPONSE; + return 1; +} + +// +// MARK: ATT_PREPARE_WRITE_REQUEST 0x16 +static uint16_t handle_prepare_write_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + uint16_t handle = READ_BT_16(request_buffer, 1); + if (!att_write_callback) { + // TODO: Use "Write Not Permitted" + return setup_error_atribute_not_found(response_buffer, ATT_PREPARE_WRITE_REQUEST, handle); + } + att_iterator_t it; + int ok = att_find_handle(&it, handle); + if (!ok) { + return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); + } + if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) { + // TODO: Use "Write Not Permitted" + return setup_error_atribute_not_found(response_buffer, ATT_WRITE_REQUEST, handle); + } + (*att_write_callback)(handle, ATT_TRANSACTION_MODE_ACTIVE, 0, request_buffer + 3, request_len - 3, NULL); + + // response: echo request + memcpy(response_buffer, request_buffer, request_len); + response_buffer[0] = ATT_PREPARE_WRITE_RESPONSE; + return request_len; +} + +// MARK: ATT_EXECUTE_WRITE_REQUEST 0x18 +static uint16_t handle_execute_write_request(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + if (!att_write_callback) { + // TODO: Use "Write Not Permitted" + return setup_error_atribute_not_found(response_buffer, ATT_EXECUTE_WRITE_REQUEST, 0); + } + if (request_buffer[1]) { + (*att_write_callback)(0, ATT_TRANSACTION_MODE_EXECUTE, 0, request_buffer + 3, request_len - 3, NULL); + } else { + (*att_write_callback)(0, ATT_TRANSACTION_MODE_CANCEL, 0, request_buffer + 3, request_len - 3, NULL); + } + response_buffer[0] = ATT_EXECUTE_WRITE_RESPONSE; + return 1; +} + +// MARK: ATT_WRITE_COMMAND 0x52 +static void handle_write_command(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + if (!att_write_callback) return; + uint16_t handle = READ_BT_16(request_buffer, 1); + att_iterator_t it; + int ok = att_find_handle(&it, handle); + if (!ok) return; + if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return; + (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3, NULL); +} + +// MARK: ATT_SIGNED_WRITE_COMAND 0xD2 +static void handle_signed_write_command(uint8_t * request_buffer, uint16_t request_len, + uint8_t * response_buffer, uint16_t response_buffer_size){ + + if (request_len < 15) return; + if (!att_write_callback) return; + uint16_t handle = READ_BT_16(request_buffer, 1); + att_iterator_t it; + int ok = att_find_handle(&it, handle); + if (!ok) return; + if ((it.flags & ATT_PROPERTY_DYNAMIC) == 0) return; + (*att_write_callback)(handle, ATT_TRANSACTION_MODE_NONE, 0, request_buffer + 3, request_len - 3 - 12, (signature_t *) request_buffer + request_len - 12); +} + +// MARK: helper for ATT_HANDLE_VALUE_NOTIFICATION and ATT_HANDLE_VALUE_INDICATION +static uint16_t prepare_handle_value(att_connection_t * att_connection, + uint16_t handle, + uint8_t *value, + uint16_t value_len, + uint8_t * response_buffer){ + bt_store_16(response_buffer, 1, handle); + if (value_len > att_connection->mtu - 3){ + value_len = att_connection->mtu - 3; + } + memcpy(&response_buffer[3], value, value_len); + return value_len + 3; +} + +// MARK: ATT_HANDLE_VALUE_NOTIFICATION 0x1b +uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection, + uint16_t handle, + uint8_t *value, + uint16_t value_len, + uint8_t * response_buffer){ + + response_buffer[0] = ATT_HANDLE_VALUE_NOTIFICATION; + return prepare_handle_value(att_connection, handle, value, value_len, response_buffer); +} + +// MARK: ATT_HANDLE_VALUE_INDICATION 0x1d +uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection, + uint16_t handle, + uint8_t *value, + uint16_t value_len, + uint8_t * response_buffer){ + + response_buffer[0] = ATT_HANDLE_VALUE_INDICATION; + return prepare_handle_value(att_connection, handle, value, value_len, response_buffer); +} + +// MARK: Dispatcher +uint16_t att_handle_request(att_connection_t * att_connection, + uint8_t * request_buffer, + uint16_t request_len, + uint8_t * response_buffer){ + uint16_t response_len = 0; + uint16_t response_buffer_size = att_connection->mtu; + + switch (request_buffer[0]){ + case ATT_EXCHANGE_MTU_REQUEST: + response_len = handle_exchange_mtu_request(att_connection, request_buffer, request_len, response_buffer); + break; + case ATT_FIND_INFORMATION_REQUEST: + response_len = handle_find_information_request(request_buffer, request_len,response_buffer, response_buffer_size); + break; + case ATT_FIND_BY_TYPE_VALUE_REQUEST: + response_len = handle_find_by_type_value_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_READ_BY_TYPE_REQUEST: + response_len = handle_read_by_type_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_READ_REQUEST: + response_len = handle_read_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_READ_BLOB_REQUEST: + response_len = handle_read_blob_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_READ_MULTIPLE_REQUEST: + response_len = handle_read_multiple_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_READ_BY_GROUP_TYPE_REQUEST: + response_len = handle_read_by_group_type_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_WRITE_REQUEST: + response_len = handle_write_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_PREPARE_WRITE_REQUEST: + response_len = handle_prepare_write_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_EXECUTE_WRITE_REQUEST: + response_len = handle_execute_write_request(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_WRITE_COMMAND: + handle_write_command(request_buffer, request_len, response_buffer, response_buffer_size); + break; + case ATT_SIGNED_WRITE_COMAND: + handle_signed_write_command(request_buffer, request_len, response_buffer, response_buffer_size); + break; + default: + printf("Unhandled ATT Command: %02X, DATA: ", request_buffer[0]); + hexdump2(&request_buffer[9], request_len-9); + break; + } + return response_len; +} + +#if 0 + +// test profile +#include "profile.h" + +int main(){ + int acl_buffer_size; + uint8_t acl_buffer[27]; + att_set_db(profile_data); + att_dump_attributes(); + + uint8_t uuid_1[] = { 0x00, 0x18}; + acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1); + hexdump2(acl_buffer, acl_buffer_size); + + uint8_t uuid_3[] = { 0x00, 0x2a}; + acl_buffer_size = handle_read_by_type_request2(acl_buffer, 19, 0, 0xffff, 2, (uint8_t *) &uuid_3); + hexdump2(acl_buffer, acl_buffer_size); + + acl_buffer_size = handle_find_by_type_value_request2(acl_buffer, 19, 0, 0xffff, 0x2800, 2, (uint8_t *) &uuid_1); + hexdump2(acl_buffer, acl_buffer_size); + + uint8_t uuid_4[] = { 0x00, 0x28}; + acl_buffer_size = handle_read_by_group_type_request2(acl_buffer, 20, 0, 0xffff, 2, (uint8_t *) &uuid_4); + hexdump2(acl_buffer, acl_buffer_size); + + acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 0, 0xffff); + hexdump2(acl_buffer, acl_buffer_size); + acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 3, 0xffff); + hexdump2(acl_buffer, acl_buffer_size); + acl_buffer_size = handle_find_information_request2(acl_buffer, 20, 5, 0xffff); + hexdump2(acl_buffer, acl_buffer_size); + + acl_buffer_size = handle_read_request2(acl_buffer, 19, 0x0003); + hexdump2(acl_buffer, acl_buffer_size); + + return 0; +} +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/att.h Thu Feb 07 14:01:01 2013 +0000 @@ -0,0 +1,164 @@ +/* + * Copyright (C) 2011-2012 by Matthias Ringwald + * + * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch + * + */ + +#pragma once + +#include <stdint.h> + +// MARK: Attribute PDU Opcodes +#define ATT_ERROR_RESPONSE 0x01 + +#define ATT_EXCHANGE_MTU_REQUEST 0x02 +#define ATT_EXCHANGE_MTU_RESPONSE 0x03 + +#define ATT_FIND_INFORMATION_REQUEST 0x04 +#define ATT_FIND_INFORMATION_REPLY 0x05 +#define ATT_FIND_BY_TYPE_VALUE_REQUEST 0x06 +#define ATT_FIND_BY_TYPE_VALUE_RESPONSE 0x07 + +#define ATT_READ_BY_TYPE_REQUEST 0x08 +#define ATT_READ_BY_TYPE_RESPONSE 0x09 +#define ATT_READ_REQUEST 0x0a +#define ATT_READ_RESPONSE 0x0b +#define ATT_READ_BLOB_REQUEST 0x0c +#define ATT_READ_BLOB_RESPONSE 0x0d +#define ATT_READ_MULTIPLE_REQUEST 0x0e +#define ATT_READ_MULTIPLE_RESPONSE 0x0f +#define ATT_READ_BY_GROUP_TYPE_REQUEST 0x10 +#define ATT_READ_BY_GROUP_TYPE_RESPONSE 0x11 + +#define ATT_WRITE_REQUEST 0x12 +#define ATT_WRITE_RESPONSE 0x13 + +#define ATT_PREPARE_WRITE_REQUEST 0x16 +#define ATT_PREPARE_WRITE_RESPONSE 0x17 +#define ATT_EXECUTE_WRITE_REQUEST 0x18 +#define ATT_EXECUTE_WRITE_RESPONSE 0x19 + +#define ATT_HANDLE_VALUE_NOTIFICATION 0x1b +#define ATT_HANDLE_VALUE_CONFIRMATION 0x1c +#define ATT_HANDLE_VALUE_INDICATION 0x1d + + +#define ATT_WRITE_COMMAND 0x52 +#define ATT_SIGNED_WRITE_COMAND 0xD2 + +// MARK: ATT Error Codes +#define ATT_ERROR_ATTRIBUTE_INVALID 0x01 +#define ATT_ERROR_INVALID_OFFSET 0x07 +#define ATT_ERROR_ATTRIBUTE_NOT_FOUND 0x0a +#define ATT_ERROR_UNSUPPORTED_GROUP_TYPE 0x10 + +// MARK: Attribute Property Flags +#define ATT_PROPERTY_BROADCAST 0x01 +#define ATT_PROPERTY_READ 0x02 +#define ATT_PROPERTY_WRITE_WITHOUT_RESPONSE 0x04 +#define ATT_PROPERTY_WRITE 0x08 +#define ATT_PROPERTY_NOTIFY 0x10 +#define ATT_PROPERTY_INDICATE 0x20 +#define ATT_PROPERTY_AUTHENTICATED_SIGNED_WRITE 0x40 +#define ATT_PROPERTY_EXTENDED_PROPERTIES 0x80 + +// MARK: Attribute Property Flag, BTstack extension +// value is asked from client +#define ATT_PROPERTY_DYNAMIC 0x100 +// 128 bit UUID used +#define ATT_PROPERTY_UUID128 0x200 + +// MARK: GATT UUIDs +#define GATT_PRIMARY_SERVICE_UUID 0x2800 +#define GATT_SECONDARY_SERVICE_UUID 0x2801 +#define GATT_CHARACTERISTICS_UUID 0x2803 + +#define GAP_SERVICE_UUID 0x1800 +#define GAP_DEVICE_NAME_UUID 0x2a00 + +#define ATT_TRANSACTION_MODE_NONE 0x0 +#define ATT_TRANSACTION_MODE_ACTIVE 0x1 +#define ATT_TRANSACTION_MODE_EXECUTE 0x2 +#define ATT_TRANSACTION_MODE_CANCEL 0x3 + +typedef struct att_connection { + uint16_t mtu; +} att_connection_t; + +typedef uint8_t signature_t[12]; + +// ATT Client Read Callback for Dynamic Data +// - if buffer == NULL, don't copy data, just return size of value +// - if buffer != NULL, copy data and return number bytes copied +// @param offset defines start of attribute value +typedef uint16_t (*att_read_callback_t)(uint16_t handle, uint16_t offset, uint8_t * buffer, uint16_t buffer_size); + +// ATT Client Write Callback for Dynamic Data +// @param handle to be written +// @param transaction - ATT_TRANSACTION_MODE_NONE for regular writes, ATT_TRANSACTION_MODE_ACTIVE for prepared writes and ATT_TRANSACTION_MODE_EXECUTE +// @param offset into the value - used for queued writes and long attributes +// @param buffer +// @param buffer_size +// @Param signature used for signed write commmands +typedef void (*att_write_callback_t)(uint16_t handle, uint16_t transaction_mode, uint16_t offset, uint8_t *buffer, uint16_t buffer_size, signature_t * signature); + +// MARK: ATT Operations + +void att_set_db(uint8_t const * db); + +void att_set_read_callback(att_read_callback_t callback); + +void att_set_write_callback(att_write_callback_t callback); + +void att_dump_attributes(void); + +// response buffer size = att_connection->mtu +uint16_t att_handle_request(att_connection_t * att_connection, + uint8_t * request_buffer, + uint16_t request_len, + uint8_t * response_buffer); + +uint16_t att_prepare_handle_value_notification(att_connection_t * att_connection, + uint16_t handle, + uint8_t *value, + uint16_t value_len, + uint8_t * response_buffer); + +uint16_t att_prepare_handle_value_indication(att_connection_t * att_connection, + uint16_t handle, + uint8_t *value, + uint16_t value_len, + uint8_t * response_buffer); + + + \ No newline at end of file
--- a/BTstack/btstack/run_loop.h Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/btstack/run_loop.h Thu Feb 07 14:01:01 2013 +0000 @@ -39,7 +39,7 @@ #include "config.h" -#include <btstack/linked_list.h> +#include "btstack/linked_list.h" #include <stdint.h> @@ -50,11 +50,11 @@ #if defined __cplusplus extern "C" { #endif - + typedef enum { - RUN_LOOP_POSIX = 1, - RUN_LOOP_COCOA, - RUN_LOOP_EMBEDDED + RUN_LOOP_POSIX = 1, + RUN_LOOP_COCOA, + RUN_LOOP_EMBEDDED } RUN_LOOP_TYPE; typedef struct data_source {
--- a/BTstack/btstack/sdp_util.h Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/btstack/sdp_util.h Thu Feb 07 14:01:01 2013 +0000 @@ -1,138 +1,138 @@ -/* - * Copyright (C) 2010 by Matthias Ringwald - * - * 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. - * - * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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. - * - */ - -/* - * sdp_util.h - */ - -#pragma once - -#include <stdint.h> - -#if defined __cplusplus -extern "C" { -#endif - -typedef enum { - DE_NIL = 0, - DE_UINT, - DE_INT, - DE_UUID, - DE_STRING, - DE_BOOL, - DE_DES, - DE_DEA, - DE_URL -} de_type_t; - -typedef enum { - DE_SIZE_8 = 0, - DE_SIZE_16, - DE_SIZE_32, - DE_SIZE_64, - DE_SIZE_128, - DE_SIZE_VAR_8, - DE_SIZE_VAR_16, - DE_SIZE_VAR_32 -} de_size_t; - -// UNIVERSAL ATTRIBUTE DEFINITIONS -#define SDP_ServiceRecordHandle 0x0000 -#define SDP_ServiceClassIDList 0x0001 -#define SDP_ServiceRecordState 0x0002 -#define SDP_ServiceID 0x0003 -#define SDP_ProtocolDescriptorList 0x0004 -#define SDP_BrowseGroupList 0x0005 -#define SDP_LanguageBaseAttributeIDList 0x0006 -#define SDP_ServiceInfoTimeToLive 0x0007 -#define SDP_ServiceAvailability 0x0008 -#define SDP_BluetoothProfileDescriptorList 0x0009 -#define SDP_DocumentationURL 0x000a -#define SDP_ClientExecutableURL 0x000b -#define SDP_IconURL 0x000c -#define SDP_AdditionalProtocolDescriptorList 0x000d -#define SDP_SupportedFormatsList 0x0303 - -// SERVICE CLASSES -#define SDP_OBEXObjectPush 0x1105 -#define SDP_OBEXFileTransfer 0x1106 -#define SDP_PublicBrowseGroup 0x1002 - -// PROTOCOLS -#define SDP_SDPProtocol 0x0001 -#define SDP_UDPProtocol 0x0002 -#define SDP_RFCOMMProtocol 0x0003 -#define SDP_OBEXProtocol 0x0008 -#define SDP_L2CAPProtocol 0x0100 - -// OFFSETS FOR LOCALIZED ATTRIBUTES - SDP_LanguageBaseAttributeIDList -#define SDP_Offest_ServiceName 0x0000 -#define SDP_Offest_ServiceDescription 0x0001 -#define SDP_Offest_ProviderName 0x0002 - -// OBEX -#define SDP_vCard_2_1 0x01 -#define SDP_vCard_3_0 0x02 -#define SDP_vCal_1_0 0x03 -#define SDP_iCal_2_0 0x04 -#define SDP_vNote 0x05 -#define SDP_vMessage 0x06 -#define SDP_OBEXFileTypeAny 0xFF - -// MARK: DateElement -void de_dump_data_element(uint8_t * record); -int de_get_len(uint8_t *header); -de_size_t de_get_size_type(uint8_t *header); -de_type_t de_get_element_type(uint8_t *header); -int de_get_header_size(uint8_t * header); -void de_create_sequence(uint8_t *header); -void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len); -uint8_t * de_push_sequence(uint8_t *header); -void de_pop_sequence(uint8_t * parent, uint8_t * child); -void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value); -void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data); - -int de_get_data_size(uint8_t * header); -void de_add_uuid128(uint8_t * seq, uint8_t * uuid); - -// MARK: SDP -uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer); -uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID); -uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value); -int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern); -int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList); -int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer); - -void sdp_create_spp_service(uint8_t *service, int service_id, const char *name); - -#if defined __cplusplus -} -#endif +/* + * Copyright (C) 2010 by Matthias Ringwald + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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. + * + */ + +/* + * sdp_util.h + */ + +#pragma once + +#include <stdint.h> + +#if defined __cplusplus +extern "C" { +#endif + +typedef enum { + DE_NIL = 0, + DE_UINT, + DE_INT, + DE_UUID, + DE_STRING, + DE_BOOL, + DE_DES, + DE_DEA, + DE_URL +} de_type_t; + +typedef enum { + DE_SIZE_8 = 0, + DE_SIZE_16, + DE_SIZE_32, + DE_SIZE_64, + DE_SIZE_128, + DE_SIZE_VAR_8, + DE_SIZE_VAR_16, + DE_SIZE_VAR_32 +} de_size_t; + +// UNIVERSAL ATTRIBUTE DEFINITIONS +#define SDP_ServiceRecordHandle 0x0000 +#define SDP_ServiceClassIDList 0x0001 +#define SDP_ServiceRecordState 0x0002 +#define SDP_ServiceID 0x0003 +#define SDP_ProtocolDescriptorList 0x0004 +#define SDP_BrowseGroupList 0x0005 +#define SDP_LanguageBaseAttributeIDList 0x0006 +#define SDP_ServiceInfoTimeToLive 0x0007 +#define SDP_ServiceAvailability 0x0008 +#define SDP_BluetoothProfileDescriptorList 0x0009 +#define SDP_DocumentationURL 0x000a +#define SDP_ClientExecutableURL 0x000b +#define SDP_IconURL 0x000c +#define SDP_AdditionalProtocolDescriptorList 0x000d +#define SDP_SupportedFormatsList 0x0303 + +// SERVICE CLASSES +#define SDP_OBEXObjectPush 0x1105 +#define SDP_OBEXFileTransfer 0x1106 +#define SDP_PublicBrowseGroup 0x1002 + +// PROTOCOLS +#define SDP_SDPProtocol 0x0001 +#define SDP_UDPProtocol 0x0002 +#define SDP_RFCOMMProtocol 0x0003 +#define SDP_OBEXProtocol 0x0008 +#define SDP_L2CAPProtocol 0x0100 + +// OFFSETS FOR LOCALIZED ATTRIBUTES - SDP_LanguageBaseAttributeIDList +#define SDP_Offest_ServiceName 0x0000 +#define SDP_Offest_ServiceDescription 0x0001 +#define SDP_Offest_ProviderName 0x0002 + +// OBEX +#define SDP_vCard_2_1 0x01 +#define SDP_vCard_3_0 0x02 +#define SDP_vCal_1_0 0x03 +#define SDP_iCal_2_0 0x04 +#define SDP_vNote 0x05 +#define SDP_vMessage 0x06 +#define SDP_OBEXFileTypeAny 0xFF + +// MARK: DateElement +void de_dump_data_element(uint8_t * record); +int de_get_len(uint8_t *header); +de_size_t de_get_size_type(uint8_t *header); +de_type_t de_get_element_type(uint8_t *header); +int de_get_header_size(uint8_t * header); +void de_create_sequence(uint8_t *header); +void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len); +uint8_t * de_push_sequence(uint8_t *header); +void de_pop_sequence(uint8_t * parent, uint8_t * child); +void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value); +void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data); + +int de_get_data_size(uint8_t * header); +void de_add_uuid128(uint8_t * seq, uint8_t * uuid); + +// MARK: SDP +uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer); +uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID); +uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value); +int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern); +int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList); +int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer); + +void sdp_create_spp_service(uint8_t *service, int service_id, const char *name); + +#if defined __cplusplus +} +#endif
--- a/BTstack/btstack_memory.c Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/btstack_memory.c Thu Feb 07 14:01:01 2013 +0000 @@ -1,336 +1,336 @@ -/* - * Copyright (C) 2009-2012 by Matthias Ringwald - * - * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch - * - */ - -/* - * btstsack_memory.h - * - * @brief BTstack memory management via configurable memory pools - * - * @note code semi-atuomatically generated by btstack_memory_generator.py - * - */ - -#include "btstack_memory.h" -#include <btstack/memory_pool.h> - -#include <stdlib.h> - -#include "config.h" -#include "hci.h" -#include "l2cap.h" -#include "rfcomm.h" - -// MARK: hci_connection_t -#ifdef MAX_NO_HCI_CONNECTIONS -#if MAX_NO_HCI_CONNECTIONS > 0 -static hci_connection_t hci_connection_storage[MAX_NO_HCI_CONNECTIONS]; -static memory_pool_t hci_connection_pool; -void * btstack_memory_hci_connection_get(void){ - return memory_pool_get(&hci_connection_pool); -} -void btstack_memory_hci_connection_free(void *hci_connection){ - memory_pool_free(&hci_connection_pool, hci_connection); -} -#else -void * btstack_memory_hci_connection_get(void){ - return NULL; -} -void btstack_memory_hci_connection_free(void *hci_connection){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_hci_connection_get(void){ - return malloc(sizeof(hci_connection_t)); -} -void btstack_memory_hci_connection_free(void *hci_connection){ - free(hci_connection); -} -#endif - - -// MARK: l2cap_service_t -#ifdef MAX_NO_L2CAP_SERVICES -#if MAX_NO_L2CAP_SERVICES > 0 -static l2cap_service_t l2cap_service_storage[MAX_NO_L2CAP_SERVICES]; -static memory_pool_t l2cap_service_pool; -void * btstack_memory_l2cap_service_get(void){ - return memory_pool_get(&l2cap_service_pool); -} -void btstack_memory_l2cap_service_free(void *l2cap_service){ - memory_pool_free(&l2cap_service_pool, l2cap_service); -} -#else -void * btstack_memory_l2cap_service_get(void){ - return NULL; -} -void btstack_memory_l2cap_service_free(void *l2cap_service){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_l2cap_service_get(void){ - return malloc(sizeof(l2cap_service_t)); -} -void btstack_memory_l2cap_service_free(void *l2cap_service){ - free(l2cap_service); -} -#endif - - -// MARK: l2cap_channel_t -#ifdef MAX_NO_L2CAP_CHANNELS -#if MAX_NO_L2CAP_CHANNELS > 0 -static l2cap_channel_t l2cap_channel_storage[MAX_NO_L2CAP_CHANNELS]; -static memory_pool_t l2cap_channel_pool; -void * btstack_memory_l2cap_channel_get(void){ - return memory_pool_get(&l2cap_channel_pool); -} -void btstack_memory_l2cap_channel_free(void *l2cap_channel){ - memory_pool_free(&l2cap_channel_pool, l2cap_channel); -} -#else -void * btstack_memory_l2cap_channel_get(void){ - return NULL; -} -void btstack_memory_l2cap_channel_free(void *l2cap_channel){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_l2cap_channel_get(void){ - return malloc(sizeof(l2cap_channel_t)); -} -void btstack_memory_l2cap_channel_free(void *l2cap_channel){ - free(l2cap_channel); -} -#endif - - -// MARK: rfcomm_multiplexer_t -#ifdef MAX_NO_RFCOMM_MULTIPLEXERS -#if MAX_NO_RFCOMM_MULTIPLEXERS > 0 -static rfcomm_multiplexer_t rfcomm_multiplexer_storage[MAX_NO_RFCOMM_MULTIPLEXERS]; -static memory_pool_t rfcomm_multiplexer_pool; -void * btstack_memory_rfcomm_multiplexer_get(void){ - return memory_pool_get(&rfcomm_multiplexer_pool); -} -void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ - memory_pool_free(&rfcomm_multiplexer_pool, rfcomm_multiplexer); -} -#else -void * btstack_memory_rfcomm_multiplexer_get(void){ - return NULL; -} -void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_rfcomm_multiplexer_get(void){ - return malloc(sizeof(rfcomm_multiplexer_t)); -} -void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ - free(rfcomm_multiplexer); -} -#endif - - -// MARK: rfcomm_service_t -#ifdef MAX_NO_RFCOMM_SERVICES -#if MAX_NO_RFCOMM_SERVICES > 0 -static rfcomm_service_t rfcomm_service_storage[MAX_NO_RFCOMM_SERVICES]; -static memory_pool_t rfcomm_service_pool; -void * btstack_memory_rfcomm_service_get(void){ - return memory_pool_get(&rfcomm_service_pool); -} -void btstack_memory_rfcomm_service_free(void *rfcomm_service){ - memory_pool_free(&rfcomm_service_pool, rfcomm_service); -} -#else -void * btstack_memory_rfcomm_service_get(void){ - return NULL; -} -void btstack_memory_rfcomm_service_free(void *rfcomm_service){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_rfcomm_service_get(void){ - return malloc(sizeof(rfcomm_service_t)); -} -void btstack_memory_rfcomm_service_free(void *rfcomm_service){ - free(rfcomm_service); -} -#endif - - -// MARK: rfcomm_channel_t -#ifdef MAX_NO_RFCOMM_CHANNELS -#if MAX_NO_RFCOMM_CHANNELS > 0 -static rfcomm_channel_t rfcomm_channel_storage[MAX_NO_RFCOMM_CHANNELS]; -static memory_pool_t rfcomm_channel_pool; -void * btstack_memory_rfcomm_channel_get(void){ - return memory_pool_get(&rfcomm_channel_pool); -} -void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ - memory_pool_free(&rfcomm_channel_pool, rfcomm_channel); -} -#else -void * btstack_memory_rfcomm_channel_get(void){ - return NULL; -} -void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_rfcomm_channel_get(void){ - return malloc(sizeof(rfcomm_channel_t)); -} -void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ - free(rfcomm_channel); -} -#endif - - -// MARK: db_mem_device_name_t -#ifdef MAX_NO_DB_MEM_DEVICE_NAMES -#if MAX_NO_DB_MEM_DEVICE_NAMES > 0 -static db_mem_device_name_t db_mem_device_name_storage[MAX_NO_DB_MEM_DEVICE_NAMES]; -static memory_pool_t db_mem_device_name_pool; -void * btstack_memory_db_mem_device_name_get(void){ - return memory_pool_get(&db_mem_device_name_pool); -} -void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ - memory_pool_free(&db_mem_device_name_pool, db_mem_device_name); -} -#else -void * btstack_memory_db_mem_device_name_get(void){ - return NULL; -} -void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_db_mem_device_name_get(void){ - return malloc(sizeof(db_mem_device_name_t)); -} -void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ - free(db_mem_device_name); -} -#endif - - -// MARK: db_mem_device_link_key_t -#ifdef MAX_NO_DB_MEM_DEVICE_LINK_KEYS -#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0 -static db_mem_device_link_key_t db_mem_device_link_key_storage[MAX_NO_DB_MEM_DEVICE_LINK_KEYS]; -static memory_pool_t db_mem_device_link_key_pool; -void * btstack_memory_db_mem_device_link_key_get(void){ - return memory_pool_get(&db_mem_device_link_key_pool); -} -void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ - memory_pool_free(&db_mem_device_link_key_pool, db_mem_device_link_key); -} -#else -void * btstack_memory_db_mem_device_link_key_get(void){ - return NULL; -} -void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_db_mem_device_link_key_get(void){ - return malloc(sizeof(db_mem_device_link_key_t)); -} -void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ - free(db_mem_device_link_key); -} -#endif - - -// MARK: db_mem_service_t -#ifdef MAX_NO_DB_MEM_SERVICES -#if MAX_NO_DB_MEM_SERVICES > 0 -static db_mem_service_t db_mem_service_storage[MAX_NO_DB_MEM_SERVICES]; -static memory_pool_t db_mem_service_pool; -void * btstack_memory_db_mem_service_get(void){ - return memory_pool_get(&db_mem_service_pool); -} -void btstack_memory_db_mem_service_free(void *db_mem_service){ - memory_pool_free(&db_mem_service_pool, db_mem_service); -} -#else -void * btstack_memory_db_mem_service_get(void){ - return NULL; -} -void btstack_memory_db_mem_service_free(void *db_mem_service){ -}; -#endif -#elif defined(HAVE_MALLOC) -void * btstack_memory_db_mem_service_get(void){ - return malloc(sizeof(db_mem_service_t)); -} -void btstack_memory_db_mem_service_free(void *db_mem_service){ - free(db_mem_service); -} -#endif - -// init -void btstack_memory_init(void){ -#if MAX_NO_HCI_CONNECTIONS > 0 - memory_pool_create(&hci_connection_pool, hci_connection_storage, MAX_NO_HCI_CONNECTIONS, sizeof(hci_connection_t)); -#endif -#if MAX_NO_L2CAP_SERVICES > 0 - memory_pool_create(&l2cap_service_pool, l2cap_service_storage, MAX_NO_L2CAP_SERVICES, sizeof(l2cap_service_t)); -#endif -#if MAX_NO_L2CAP_CHANNELS > 0 - memory_pool_create(&l2cap_channel_pool, l2cap_channel_storage, MAX_NO_L2CAP_CHANNELS, sizeof(l2cap_channel_t)); -#endif -#if MAX_NO_RFCOMM_MULTIPLEXERS > 0 - memory_pool_create(&rfcomm_multiplexer_pool, rfcomm_multiplexer_storage, MAX_NO_RFCOMM_MULTIPLEXERS, sizeof(rfcomm_multiplexer_t)); -#endif -#if MAX_NO_RFCOMM_SERVICES > 0 - memory_pool_create(&rfcomm_service_pool, rfcomm_service_storage, MAX_NO_RFCOMM_SERVICES, sizeof(rfcomm_service_t)); -#endif -#if MAX_NO_RFCOMM_CHANNELS > 0 - memory_pool_create(&rfcomm_channel_pool, rfcomm_channel_storage, MAX_NO_RFCOMM_CHANNELS, sizeof(rfcomm_channel_t)); -#endif -#if MAX_NO_DB_MEM_DEVICE_NAMES > 0 - memory_pool_create(&db_mem_device_name_pool, db_mem_device_name_storage, MAX_NO_DB_MEM_DEVICE_NAMES, sizeof(db_mem_device_name_t)); -#endif -#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0 - memory_pool_create(&db_mem_device_link_key_pool, db_mem_device_link_key_storage, MAX_NO_DB_MEM_DEVICE_LINK_KEYS, sizeof(db_mem_device_link_key_t)); -#endif -#if MAX_NO_DB_MEM_SERVICES > 0 - memory_pool_create(&db_mem_service_pool, db_mem_service_storage, MAX_NO_DB_MEM_SERVICES, sizeof(db_mem_service_t)); -#endif -} +/* + * Copyright (C) 2009-2012 by Matthias Ringwald + * + * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch + * + */ + +/* + * btstsack_memory.h + * + * @brief BTstack memory management via configurable memory pools + * + * @note code semi-atuomatically generated by btstack_memory_generator.py + * + */ + +#include "btstack_memory.h" +#include "btstack/memory_pool.h" + +#include <stdlib.h> + +#include "config.h" +#include "hci.h" +#include "l2cap.h" +#include "rfcomm.h" + +// MARK: hci_connection_t +#ifdef MAX_NO_HCI_CONNECTIONS +#if MAX_NO_HCI_CONNECTIONS > 0 +static hci_connection_t hci_connection_storage[MAX_NO_HCI_CONNECTIONS]; +static memory_pool_t hci_connection_pool; +void * btstack_memory_hci_connection_get(void){ + return memory_pool_get(&hci_connection_pool); +} +void btstack_memory_hci_connection_free(void *hci_connection){ + memory_pool_free(&hci_connection_pool, hci_connection); +} +#else +void * btstack_memory_hci_connection_get(void){ + return NULL; +} +void btstack_memory_hci_connection_free(void *hci_connection){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_hci_connection_get(void){ + return malloc(sizeof(hci_connection_t)); +} +void btstack_memory_hci_connection_free(void *hci_connection){ + free(hci_connection); +} +#endif + + +// MARK: l2cap_service_t +#ifdef MAX_NO_L2CAP_SERVICES +#if MAX_NO_L2CAP_SERVICES > 0 +static l2cap_service_t l2cap_service_storage[MAX_NO_L2CAP_SERVICES]; +static memory_pool_t l2cap_service_pool; +void * btstack_memory_l2cap_service_get(void){ + return memory_pool_get(&l2cap_service_pool); +} +void btstack_memory_l2cap_service_free(void *l2cap_service){ + memory_pool_free(&l2cap_service_pool, l2cap_service); +} +#else +void * btstack_memory_l2cap_service_get(void){ + return NULL; +} +void btstack_memory_l2cap_service_free(void *l2cap_service){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_l2cap_service_get(void){ + return malloc(sizeof(l2cap_service_t)); +} +void btstack_memory_l2cap_service_free(void *l2cap_service){ + free(l2cap_service); +} +#endif + + +// MARK: l2cap_channel_t +#ifdef MAX_NO_L2CAP_CHANNELS +#if MAX_NO_L2CAP_CHANNELS > 0 +static l2cap_channel_t l2cap_channel_storage[MAX_NO_L2CAP_CHANNELS]; +static memory_pool_t l2cap_channel_pool; +void * btstack_memory_l2cap_channel_get(void){ + return memory_pool_get(&l2cap_channel_pool); +} +void btstack_memory_l2cap_channel_free(void *l2cap_channel){ + memory_pool_free(&l2cap_channel_pool, l2cap_channel); +} +#else +void * btstack_memory_l2cap_channel_get(void){ + return NULL; +} +void btstack_memory_l2cap_channel_free(void *l2cap_channel){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_l2cap_channel_get(void){ + return malloc(sizeof(l2cap_channel_t)); +} +void btstack_memory_l2cap_channel_free(void *l2cap_channel){ + free(l2cap_channel); +} +#endif + + +// MARK: rfcomm_multiplexer_t +#ifdef MAX_NO_RFCOMM_MULTIPLEXERS +#if MAX_NO_RFCOMM_MULTIPLEXERS > 0 +static rfcomm_multiplexer_t rfcomm_multiplexer_storage[MAX_NO_RFCOMM_MULTIPLEXERS]; +static memory_pool_t rfcomm_multiplexer_pool; +void * btstack_memory_rfcomm_multiplexer_get(void){ + return memory_pool_get(&rfcomm_multiplexer_pool); +} +void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ + memory_pool_free(&rfcomm_multiplexer_pool, rfcomm_multiplexer); +} +#else +void * btstack_memory_rfcomm_multiplexer_get(void){ + return NULL; +} +void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_rfcomm_multiplexer_get(void){ + return malloc(sizeof(rfcomm_multiplexer_t)); +} +void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer){ + free(rfcomm_multiplexer); +} +#endif + + +// MARK: rfcomm_service_t +#ifdef MAX_NO_RFCOMM_SERVICES +#if MAX_NO_RFCOMM_SERVICES > 0 +static rfcomm_service_t rfcomm_service_storage[MAX_NO_RFCOMM_SERVICES]; +static memory_pool_t rfcomm_service_pool; +void * btstack_memory_rfcomm_service_get(void){ + return memory_pool_get(&rfcomm_service_pool); +} +void btstack_memory_rfcomm_service_free(void *rfcomm_service){ + memory_pool_free(&rfcomm_service_pool, rfcomm_service); +} +#else +void * btstack_memory_rfcomm_service_get(void){ + return NULL; +} +void btstack_memory_rfcomm_service_free(void *rfcomm_service){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_rfcomm_service_get(void){ + return malloc(sizeof(rfcomm_service_t)); +} +void btstack_memory_rfcomm_service_free(void *rfcomm_service){ + free(rfcomm_service); +} +#endif + + +// MARK: rfcomm_channel_t +#ifdef MAX_NO_RFCOMM_CHANNELS +#if MAX_NO_RFCOMM_CHANNELS > 0 +static rfcomm_channel_t rfcomm_channel_storage[MAX_NO_RFCOMM_CHANNELS]; +static memory_pool_t rfcomm_channel_pool; +void * btstack_memory_rfcomm_channel_get(void){ + return memory_pool_get(&rfcomm_channel_pool); +} +void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ + memory_pool_free(&rfcomm_channel_pool, rfcomm_channel); +} +#else +void * btstack_memory_rfcomm_channel_get(void){ + return NULL; +} +void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_rfcomm_channel_get(void){ + return malloc(sizeof(rfcomm_channel_t)); +} +void btstack_memory_rfcomm_channel_free(void *rfcomm_channel){ + free(rfcomm_channel); +} +#endif + + +// MARK: db_mem_device_name_t +#ifdef MAX_NO_DB_MEM_DEVICE_NAMES +#if MAX_NO_DB_MEM_DEVICE_NAMES > 0 +static db_mem_device_name_t db_mem_device_name_storage[MAX_NO_DB_MEM_DEVICE_NAMES]; +static memory_pool_t db_mem_device_name_pool; +void * btstack_memory_db_mem_device_name_get(void){ + return memory_pool_get(&db_mem_device_name_pool); +} +void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ + memory_pool_free(&db_mem_device_name_pool, db_mem_device_name); +} +#else +void * btstack_memory_db_mem_device_name_get(void){ + return NULL; +} +void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_db_mem_device_name_get(void){ + return malloc(sizeof(db_mem_device_name_t)); +} +void btstack_memory_db_mem_device_name_free(void *db_mem_device_name){ + free(db_mem_device_name); +} +#endif + + +// MARK: db_mem_device_link_key_t +#ifdef MAX_NO_DB_MEM_DEVICE_LINK_KEYS +#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0 +static db_mem_device_link_key_t db_mem_device_link_key_storage[MAX_NO_DB_MEM_DEVICE_LINK_KEYS]; +static memory_pool_t db_mem_device_link_key_pool; +void * btstack_memory_db_mem_device_link_key_get(void){ + return memory_pool_get(&db_mem_device_link_key_pool); +} +void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ + memory_pool_free(&db_mem_device_link_key_pool, db_mem_device_link_key); +} +#else +void * btstack_memory_db_mem_device_link_key_get(void){ + return NULL; +} +void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_db_mem_device_link_key_get(void){ + return malloc(sizeof(db_mem_device_link_key_t)); +} +void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key){ + free(db_mem_device_link_key); +} +#endif + + +// MARK: db_mem_service_t +#ifdef MAX_NO_DB_MEM_SERVICES +#if MAX_NO_DB_MEM_SERVICES > 0 +static db_mem_service_t db_mem_service_storage[MAX_NO_DB_MEM_SERVICES]; +static memory_pool_t db_mem_service_pool; +void * btstack_memory_db_mem_service_get(void){ + return memory_pool_get(&db_mem_service_pool); +} +void btstack_memory_db_mem_service_free(void *db_mem_service){ + memory_pool_free(&db_mem_service_pool, db_mem_service); +} +#else +void * btstack_memory_db_mem_service_get(void){ + return NULL; +} +void btstack_memory_db_mem_service_free(void *db_mem_service){ +}; +#endif +#elif defined(HAVE_MALLOC) +void * btstack_memory_db_mem_service_get(void){ + return malloc(sizeof(db_mem_service_t)); +} +void btstack_memory_db_mem_service_free(void *db_mem_service){ + free(db_mem_service); +} +#endif + +// init +void btstack_memory_init(void){ +#if MAX_NO_HCI_CONNECTIONS > 0 + memory_pool_create(&hci_connection_pool, hci_connection_storage, MAX_NO_HCI_CONNECTIONS, sizeof(hci_connection_t)); +#endif +#if MAX_NO_L2CAP_SERVICES > 0 + memory_pool_create(&l2cap_service_pool, l2cap_service_storage, MAX_NO_L2CAP_SERVICES, sizeof(l2cap_service_t)); +#endif +#if MAX_NO_L2CAP_CHANNELS > 0 + memory_pool_create(&l2cap_channel_pool, l2cap_channel_storage, MAX_NO_L2CAP_CHANNELS, sizeof(l2cap_channel_t)); +#endif +#if MAX_NO_RFCOMM_MULTIPLEXERS > 0 + memory_pool_create(&rfcomm_multiplexer_pool, rfcomm_multiplexer_storage, MAX_NO_RFCOMM_MULTIPLEXERS, sizeof(rfcomm_multiplexer_t)); +#endif +#if MAX_NO_RFCOMM_SERVICES > 0 + memory_pool_create(&rfcomm_service_pool, rfcomm_service_storage, MAX_NO_RFCOMM_SERVICES, sizeof(rfcomm_service_t)); +#endif +#if MAX_NO_RFCOMM_CHANNELS > 0 + memory_pool_create(&rfcomm_channel_pool, rfcomm_channel_storage, MAX_NO_RFCOMM_CHANNELS, sizeof(rfcomm_channel_t)); +#endif +#if MAX_NO_DB_MEM_DEVICE_NAMES > 0 + memory_pool_create(&db_mem_device_name_pool, db_mem_device_name_storage, MAX_NO_DB_MEM_DEVICE_NAMES, sizeof(db_mem_device_name_t)); +#endif +#if MAX_NO_DB_MEM_DEVICE_LINK_KEYS > 0 + memory_pool_create(&db_mem_device_link_key_pool, db_mem_device_link_key_storage, MAX_NO_DB_MEM_DEVICE_LINK_KEYS, sizeof(db_mem_device_link_key_t)); +#endif +#if MAX_NO_DB_MEM_SERVICES > 0 + memory_pool_create(&db_mem_service_pool, db_mem_service_storage, MAX_NO_DB_MEM_SERVICES, sizeof(db_mem_service_t)); +#endif +}
--- a/BTstack/config.h Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/config.h Thu Feb 07 14:01:01 2013 +0000 @@ -1,24 +1,29 @@ +// Replaced by LE version + #define EMBEDDED -#define HAVE_TICK + +#define ENABLE_LOG_DEBUG +#define ENABLE_LOG_INFO +#define ENABLE_LOG_ERROR //#define HAVE_INIT_SCRIPT -//#define HAVE_BZERO -//#define HAVE_EHCILL +#define HAVE_TICK -#define ENABLE_LOG_INFO -#define ENABLE_LOG_ERROR +//#define HAVE_EHCILL +#define HAVE_BLE + +#define ASYNC_BUFFERS 1 + #define HCI_ACL_PAYLOAD_SIZE 52 -#define HAVE_MALLOC - -//#define MAX_SPP_CONNECTIONS 1 -//#define MAX_NO_HCI_CONNECTIONS MAX_SPP_CONNECTIONS -//#define MAX_NO_L2CAP_SERVICES 2 -//#define MAX_NO_L2CAP_CHANNELS (1+MAX_SPP_CONNECTIONS) -//#define MAX_NO_RFCOMM_MULTIPLEXERS MAX_SPP_CONNECTIONS -//#define MAX_NO_RFCOMM_SERVICES 1 -//#define MAX_NO_RFCOMM_CHANNELS MAX_SPP_CONNECTIONS -//#define MAX_NO_DB_MEM_DEVICE_LINK_KEYS 2 -//#define MAX_NO_DB_MEM_DEVICE_NAMES 0 -//#define MAX_NO_DB_MEM_SERVICES 1 +// +#define MAX_NO_HCI_CONNECTIONS 1 +#define MAX_NO_L2CAP_SERVICES 0 +#define MAX_NO_L2CAP_CHANNELS 0 +#define MAX_NO_RFCOMM_MULTIPLEXERS 0 +#define MAX_NO_RFCOMM_SERVICES 0 +#define MAX_NO_RFCOMM_CHANNELS 0 +#define MAX_NO_DB_MEM_DEVICE_LINK_KEYS 2 +#define MAX_NO_DB_MEM_DEVICE_NAMES 0 +#define MAX_NO_DB_MEM_SERVICES 0
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/global.h Thu Feb 07 14:01:01 2013 +0000 @@ -0,0 +1,10 @@ +#ifndef _GLOBAL_H_ +#define _GLOBAL_H_ + +extern uint8_t startup_state; +extern uint8_t connection_status; +extern unsigned led1_on_count; +extern uint8_t led1_on_state; +extern unsigned timer_counter; + +#endif
--- a/BTstack/hci.c Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/hci.c Thu Feb 07 14:01:01 2013 +0000 @@ -58,7 +58,7 @@ #include "debug.h" #include "hci_dump.h" -#include <btstack/hci_cmds.h> +#include "btstack/hci_cmds.h" #define HCI_CONNECTION_TIMEOUT_MS 10000 @@ -677,9 +677,9 @@ } hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size); - - // execute main loop - hci_run(); + + // execute main loop + hci_run(); } void packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){ @@ -983,11 +983,11 @@ } // create internal event - hci_emit_state(); + hci_emit_state(); - // trigger next/first action - hci_run(); - + // trigger next/first action + hci_run(); + return 0; } @@ -1109,18 +1109,18 @@ log_info("hci_run: init script done\n\r"); } // otherwise continue - hci_send_cmd(&hci_read_bd_addr); - break; - case 4: - hci_send_cmd(&hci_read_buffer_size); - break; + hci_send_cmd(&hci_read_bd_addr); + break; + case 4: + hci_send_cmd(&hci_read_buffer_size); + break; case 5: // ca. 15 sec hci_send_cmd(&hci_write_page_timeout, 0x6000); break; - case 6: - hci_send_cmd(&hci_write_scan_enable, (hci_stack.connectable << 1) | hci_stack.discoverable); // page scan - break; + case 6: + hci_send_cmd(&hci_write_scan_enable, (hci_stack.connectable << 1) | hci_stack.discoverable); // page scan + break; case 7: #ifndef EMBEDDED {
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/memory_pool.c Thu Feb 07 14:01:01 2013 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright (C) 2011 by Matthias Ringwald + * + * 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. + * + * THIS SOFTWARE IS PROVIDED BY MATTHIAS RINGWALD 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. + * + */ + +/* + * memory_pool.c + * + * Fixed-size block allocation + * + * Free blocks are kept in singly linked list + * + */ + +#include "btstack/memory_pool.h" +#include <stddef.h> + +typedef struct node { + struct node * next; +} node_t; + +void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size){ + node_t *free_blocks = (node_t*) pool; + char *mem_ptr = (char *) storage; + int i; + + // create singly linked list of all available blocks + free_blocks->next = NULL; + for (i = 0 ; i < count ; i++){ + memory_pool_free(pool, mem_ptr); + mem_ptr += block_size; + } +} + +void * memory_pool_get(memory_pool_t *pool){ + node_t *free_blocks = (node_t*) pool; + + if (!free_blocks->next) return NULL; + + // remove first + node_t *node = free_blocks->next; + free_blocks->next = node->next; + + return (void*) node; +} + +void memory_pool_free(memory_pool_t *pool, void * block){ + node_t *free_blocks = (node_t*) pool; + node_t *node = (node_t*) block; + // add block as node to list + node->next = free_blocks->next; + free_blocks->next = node; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/profile.h Thu Feb 07 14:01:01 2013 +0000 @@ -0,0 +1,42 @@ +// profile.h generated from profile.gatt for BTstack + +// binary representation +// attribute size in bytes (16), flags(16), handle (16), uuid (16/128), value(...) + +#include <stdint.h> + +const uint8_t profile_data[] = +{ + // 0x0001 PRIMARY_SERVICE-GAP_SERVICE + 0x0a, 0x00, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28, 0x00, 0x18, + // 0x0002 CHARACTERISTIC-GAP_DEVICE_NAME-READ + 0x0d, 0x00, 0x02, 0x00, 0x02, 0x00, 0x03, 0x28, 0x02, 0x03, 0x00, 0x00, 0x2a, + // 0x0003 VALUE-GAP_DEVICE_NAME-READ-"mbed BLE TEST" + 0x15, 0x00, 0x02, 0x00, 0x03, 0x00, 0x00, 0x2a, 0x6d, 0x62, 0x65, 0x64, 0x20, 0x42, 0x4c, 0x45, 0x20, 0x54, 0x45, 0x53, 0x54, + // 0x0004 CHARACTERISTIC-GAP_APPEARANCE-READ + 0x0d, 0x00, 0x02, 0x00, 0x04, 0x00, 0x03, 0x28, 0x02, 0x05, 0x00, 0x01, 0x2a, + // 0x0005 VALUE-GAP_APPEARANCE-READ-00 00 + 0x0a, 0x00, 0x02, 0x00, 0x05, 0x00, 0x01, 0x2a, 0x00, 0x00, + // 0x0006 PRIMARY_SERVICE-GATT_SERVICE + 0x0a, 0x00, 0x02, 0x00, 0x06, 0x00, 0x00, 0x28, 0x01, 0x18, + // 0x0007 CHARACTERISTIC-GATT_SERVICE_CHANGED-READ + 0x0d, 0x00, 0x02, 0x00, 0x07, 0x00, 0x03, 0x28, 0x02, 0x08, 0x00, 0x05, 0x2a, + // 0x0008 VALUE-GATT_SERVICE_CHANGED-READ- + 0x08, 0x00, 0x02, 0x00, 0x08, 0x00, 0x05, 0x2a, + // 0x0009 PRIMARY_SERVICE-FFF0 + 0x0a, 0x00, 0x02, 0x00, 0x09, 0x00, 0x00, 0x28, 0xf0, 0xff, + // 0x000a CHARACTERISTIC-FFF1-READ | WRITE | DYNAMIC + 0x0d, 0x00, 0x02, 0x00, 0x0a, 0x00, 0x03, 0x28, 0x0a, 0x0b, 0x00, 0xf1, 0xff, + // 0x000b VALUE-FFF1-READ | WRITE | DYNAMIC- + 0x08, 0x00, 0x0a, 0x01, 0x0b, 0x00, 0xf1, 0xff, + // 0x000c CHARACTERISTIC-FFF2-READ | WRITE | DYNAMIC + 0x0d, 0x00, 0x02, 0x00, 0x0c, 0x00, 0x03, 0x28, 0x0a, 0x0d, 0x00, 0xf2, 0xff, + // 0x000d VALUE-FFF2-READ | WRITE | DYNAMIC- + 0x08, 0x00, 0x0a, 0x01, 0x0d, 0x00, 0xf2, 0xff, + // 0x000e CHARACTERISTIC-00001234-0000-1000-8000-00805F9B34FB-READ | WRITE | DYNAMIC + 0x1b, 0x00, 0x02, 0x00, 0x0e, 0x00, 0x03, 0x28, 0x0a, 0x0f, 0x00, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, + // 0x000f VALUE-00001234-0000-1000-8000-00805F9B34FB-READ | WRITE | DYNAMIC- + 0x16, 0x00, 0x0a, 0x03, 0x0f, 0x00, 0xfb, 0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00, 0x80, 0x00, 0x10, 0x00, 0x00, 0x34, 0x12, 0x00, 0x00, + // END + 0x00, 0x00, +}; // total size 124 bytes
--- a/BTstack/rfcomm.c Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,1869 +0,0 @@ -/* - * Copyright (C) 2009-2012 by Matthias Ringwald - * - * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch - * - */ - -/* - * rfcomm.c - */ - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> // memcpy -#include <stdint.h> - -#include <btstack/btstack.h> -#include <btstack/hci_cmds.h> -#include <btstack/utils.h> - -#include <btstack/utils.h> -#include "btstack_memory.h" -#include "hci.h" -#include "hci_dump.h" -#include "debug.h" -#include "rfcomm.h" - -// workaround for missing PRIxPTR on mspgcc (16/20-bit MCU) -#ifndef PRIxPTR -#if defined(__MSP430X__) && defined(__MSP430X_LARGE__) -#define PRIxPTR "lx" -#else -#define PRIxPTR "x" -#endif -#endif - - -// Control field values bit no. 1 2 3 4 PF 6 7 8 -#define BT_RFCOMM_SABM 0x3F // 1 1 1 1 1 1 0 0 -#define BT_RFCOMM_UA 0x73 // 1 1 0 0 1 1 1 0 -#define BT_RFCOMM_DM 0x0F // 1 1 1 1 0 0 0 0 -#define BT_RFCOMM_DM_PF 0x1F // 1 1 1 1 1 0 0 0 -#define BT_RFCOMM_DISC 0x53 // 1 1 0 0 1 0 1 0 -#define BT_RFCOMM_UIH 0xEF // 1 1 1 1 0 1 1 1 -#define BT_RFCOMM_UIH_PF 0xFF // 1 1 1 1 0 1 1 1 - -// Multiplexer message types -#define BT_RFCOMM_CLD_CMD 0xC3 -#define BT_RFCOMM_FCON_CMD 0xA3 -#define BT_RFCOMM_FCON_RSP 0xA1 -#define BT_RFCOMM_FCOFF_CMD 0x63 -#define BT_RFCOMM_FCOFF_RSP 0x61 -#define BT_RFCOMM_MSC_CMD 0xE3 -#define BT_RFCOMM_MSC_RSP 0xE1 -#define BT_RFCOMM_NSC_RSP 0x11 -#define BT_RFCOMM_PN_CMD 0x83 -#define BT_RFCOMM_PN_RSP 0x81 -#define BT_RFCOMM_RLS_CMD 0x53 -#define BT_RFCOMM_RLS_RSP 0x51 -#define BT_RFCOMM_RPN_CMD 0x93 -#define BT_RFCOMM_RPN_RSP 0x91 -#define BT_RFCOMM_TEST_CMD 0x23 -#define BT_RFCOMM_TEST_RSP 0x21 - -#define RFCOMM_MULIPLEXER_TIMEOUT_MS 60000 - -// FCS calc -#define BT_RFCOMM_CODE_WORD 0xE0 // pol = x8+x2+x1+1 -#define BT_RFCOMM_CRC_CHECK_LEN 3 -#define BT_RFCOMM_UIHCRC_CHECK_LEN 2 - -#include "l2cap.h" - -// used for debugging -// #define RFCOMM_LOG_CREDITS - -// global rfcomm data -static uint16_t rfcomm_client_cid_generator; // used for client channel IDs - -// linked lists for all -static linked_list_t rfcomm_multiplexers = NULL; -static linked_list_t rfcomm_channels = NULL; -static linked_list_t rfcomm_services = NULL; - -static void (*app_packet_handler)(void * connection, uint8_t packet_type, - uint16_t channel, uint8_t *packet, uint16_t size); - -static void rfcomm_run(void); -static void rfcomm_hand_out_credits(void); -static void rfcomm_channel_state_machine(rfcomm_channel_t *channel, rfcomm_channel_event_t *event); -static void rfcomm_channel_state_machine_2(rfcomm_multiplexer_t * multiplexer, uint8_t dlci, rfcomm_channel_event_t *event); -static int rfcomm_channel_ready_for_open(rfcomm_channel_t *channel); -static void rfcomm_multiplexer_state_machine(rfcomm_multiplexer_t * multiplexer, RFCOMM_MULTIPLEXER_EVENT event); - - -// MARK: RFCOMM CLIENT EVENTS - -// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) -static void rfcomm_emit_connection_request(rfcomm_channel_t *channel) { - uint8_t event[11]; - event[0] = RFCOMM_EVENT_INCOMING_CONNECTION; - event[1] = sizeof(event) - 2; - bt_flip_addr(&event[2], channel->multiplexer->remote_addr); - event[8] = channel->dlci >> 1; - bt_store_16(event, 9, channel->rfcomm_cid); - hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); -} - -// API Change: BTstack-0.3.50x uses -// data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) -// next Cydia release will use SVN version of this -// data: event(8), len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16) -static void rfcomm_emit_channel_opened(rfcomm_channel_t *channel, uint8_t status) { - uint8_t event[16]; - uint8_t pos = 0; - event[pos++] = RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE; - event[pos++] = sizeof(event) - 2; - event[pos++] = status; - bt_flip_addr(&event[pos], channel->multiplexer->remote_addr); pos += 6; - bt_store_16(event, pos, channel->multiplexer->con_handle); pos += 2; - event[pos++] = channel->dlci >> 1; - bt_store_16(event, pos, channel->rfcomm_cid); pos += 2; // channel ID - bt_store_16(event, pos, channel->max_frame_size); pos += 2; // max frame size - hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, pos); -} - -static void rfcomm_emit_channel_open_failed_outgoing_memory(void * connection, bd_addr_t *addr, uint8_t server_channel){ - uint8_t event[16]; - uint8_t pos = 0; - event[pos++] = RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE; - event[pos++] = sizeof(event) - 2; - event[pos++] = BTSTACK_MEMORY_ALLOC_FAILED; - bt_flip_addr(&event[pos], *addr); pos += 6; - bt_store_16(event, pos, 0); pos += 2; - event[pos++] = server_channel; - bt_store_16(event, pos, 0); pos += 2; // channel ID - bt_store_16(event, pos, 0); pos += 2; // max frame size - hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, pos); -} - -// data: event(8), len(8), creidts incoming(8), new credits incoming(8), credits outgoing(8) -static inline void rfcomm_emit_credit_status(rfcomm_channel_t * channel) { -#ifdef RFCOMM_LOG_CREDITS - uint8_t event[5]; - event[0] = 0x88; - event[1] = sizeof(event) - 2; - event[2] = channel->credits_incoming; - event[3] = channel->new_credits_incoming; - event[4] = channel->credits_outgoing; - hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); -#endif -} - -// data: event(8), len(8), rfcomm_cid(16) -static void rfcomm_emit_channel_closed(rfcomm_channel_t * channel) { - uint8_t event[4]; - event[0] = RFCOMM_EVENT_CHANNEL_CLOSED; - event[1] = sizeof(event) - 2; - bt_store_16(event, 2, channel->rfcomm_cid); - hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); -} - -static void rfcomm_emit_credits(rfcomm_channel_t * channel, uint8_t credits) { - uint8_t event[5]; - event[0] = RFCOMM_EVENT_CREDITS; - event[1] = sizeof(event) - 2; - bt_store_16(event, 2, channel->rfcomm_cid); - event[4] = credits; - hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - (*app_packet_handler)(channel->connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); -} - -static void rfcomm_emit_service_registered(void *connection, uint8_t status, uint8_t channel){ - uint8_t event[4]; - event[0] = RFCOMM_EVENT_SERVICE_REGISTERED; - event[1] = sizeof(event) - 2; - event[2] = status; - event[3] = channel; - hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event)); - (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); -} - -// MARK: RFCOMM MULTIPLEXER HELPER - -static uint16_t rfcomm_max_frame_size_for_l2cap_mtu(uint16_t l2cap_mtu){ - - // Assume RFCOMM header with credits and single byte length field - uint16_t max_frame_size = l2cap_mtu - 5; - - // single byte can denote len up to 127 - if (max_frame_size > 127) { - max_frame_size--; - } - - log_info("rfcomm_max_frame_size_for_l2cap_mtu: %u -> %u\n", l2cap_mtu, max_frame_size); - return max_frame_size; -} - -static void rfcomm_multiplexer_initialize(rfcomm_multiplexer_t *multiplexer){ - - memset(multiplexer, 0, sizeof(rfcomm_multiplexer_t)); - - multiplexer->state = RFCOMM_MULTIPLEXER_CLOSED; - multiplexer->l2cap_credits = 0; - multiplexer->send_dm_for_dlci = 0; - multiplexer->max_frame_size = rfcomm_max_frame_size_for_l2cap_mtu(l2cap_max_mtu()); -} - -static rfcomm_multiplexer_t * rfcomm_multiplexer_create_for_addr(bd_addr_t *addr){ - - // alloc structure - rfcomm_multiplexer_t * multiplexer = (rfcomm_multiplexer_t*)btstack_memory_rfcomm_multiplexer_get(); - if (!multiplexer) return NULL; - - // fill in - rfcomm_multiplexer_initialize(multiplexer); - BD_ADDR_COPY(&multiplexer->remote_addr, addr); - - // add to services list - linked_list_add(&rfcomm_multiplexers, (linked_item_t *) multiplexer); - - return multiplexer; -} - -static rfcomm_multiplexer_t * rfcomm_multiplexer_for_addr(bd_addr_t *addr){ - linked_item_t *it; - for (it = (linked_item_t *) rfcomm_multiplexers; it ; it = it->next){ - rfcomm_multiplexer_t * multiplexer = ((rfcomm_multiplexer_t *) it); - if (BD_ADDR_CMP(addr, multiplexer->remote_addr) == 0) { - return multiplexer; - }; - } - return NULL; -} - -static rfcomm_multiplexer_t * rfcomm_multiplexer_for_l2cap_cid(uint16_t l2cap_cid) { - linked_item_t *it; - for (it = (linked_item_t *) rfcomm_multiplexers; it ; it = it->next){ - rfcomm_multiplexer_t * multiplexer = ((rfcomm_multiplexer_t *) it); - if (multiplexer->l2cap_cid == l2cap_cid) { - return multiplexer; - }; - } - return NULL; -} - -static int rfcomm_multiplexer_has_channels(rfcomm_multiplexer_t * multiplexer){ - linked_item_t *it; - for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ - rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); - if (channel->multiplexer == multiplexer) { - return 1; - } - } - return 0; -} - -// MARK: RFCOMM CHANNEL HELPER - -static void rfcomm_dump_channels(void){ -#ifndef EMBEDDED - linked_item_t * it; - int channels = 0; - for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ - rfcomm_channel_t * channel = (rfcomm_channel_t *) it; - log_info("Channel #%u: addr %p, state %u\n", channels, channel, channel->state); - channels++; - } -#endif -} - -static void rfcomm_channel_initialize(rfcomm_channel_t *channel, rfcomm_multiplexer_t *multiplexer, - rfcomm_service_t *service, uint8_t server_channel){ - - // don't use 0 as channel id - if (rfcomm_client_cid_generator == 0) ++rfcomm_client_cid_generator; - - // setup channel - memset(channel, 0, sizeof(rfcomm_channel_t)); - - channel->state = RFCOMM_CHANNEL_CLOSED; - channel->state_var = RFCOMM_CHANNEL_STATE_VAR_NONE; - - channel->multiplexer = multiplexer; - channel->service = service; - channel->rfcomm_cid = rfcomm_client_cid_generator++; - channel->max_frame_size = multiplexer->max_frame_size; - - channel->credits_incoming = 0; - channel->credits_outgoing = 0; - channel->packets_granted = 0; - - // incoming flow control not active - channel->new_credits_incoming = 0x30; - channel->incoming_flow_control = 0; - - if (service) { - // incoming connection - channel->outgoing = 0; - channel->dlci = (server_channel << 1) | multiplexer->outgoing; - if (channel->max_frame_size > service->max_frame_size) { - channel->max_frame_size = service->max_frame_size; - } - channel->incoming_flow_control = service->incoming_flow_control; - channel->new_credits_incoming = service->incoming_initial_credits; - } else { - // outgoing connection - channel->outgoing = 1; - channel->dlci = (server_channel << 1) | (multiplexer->outgoing ^ 1); - } -} - -// service == NULL -> outgoing channel -static rfcomm_channel_t * rfcomm_channel_create(rfcomm_multiplexer_t * multiplexer, - rfcomm_service_t * service, uint8_t server_channel){ - - log_info("rfcomm_channel_create for service %p, channel %u --- begin\n", service, server_channel); - rfcomm_dump_channels(); - - // alloc structure - rfcomm_channel_t * channel = (rfcomm_channel_t*)btstack_memory_rfcomm_channel_get(); - if (!channel) return NULL; - - // fill in - rfcomm_channel_initialize(channel, multiplexer, service, server_channel); - - // add to services list - linked_list_add(&rfcomm_channels, (linked_item_t *) channel); - - return channel; -} - -static rfcomm_channel_t * rfcomm_channel_for_rfcomm_cid(uint16_t rfcomm_cid){ - linked_item_t *it; - for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ - rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); - if (channel->rfcomm_cid == rfcomm_cid) { - return channel; - }; - } - return NULL; -} - -static rfcomm_channel_t * rfcomm_channel_for_multiplexer_and_dlci(rfcomm_multiplexer_t * multiplexer, uint8_t dlci){ - linked_item_t *it; - for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ - rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); - if (channel->dlci == dlci && channel->multiplexer == multiplexer) { - return channel; - }; - } - return NULL; -} - -static rfcomm_service_t * rfcomm_service_for_channel(uint8_t server_channel){ - linked_item_t *it; - for (it = (linked_item_t *) rfcomm_services; it ; it = it->next){ - rfcomm_service_t * service = ((rfcomm_service_t *) it); - if ( service->server_channel == server_channel){ - return service; - }; - } - return NULL; -} - -// MARK: RFCOMM SEND - -/** - * @param credits - only used for RFCOMM flow control in UIH wiht P/F = 1 - */ -static int rfcomm_send_packet_for_multiplexer(rfcomm_multiplexer_t *multiplexer, uint8_t address, uint8_t control, uint8_t credits, uint8_t *data, uint16_t len){ - - if (!l2cap_can_send_packet_now(multiplexer->l2cap_cid)) return BTSTACK_ACL_BUFFERS_FULL; - - uint8_t * rfcomm_out_buffer = l2cap_get_outgoing_buffer(); - - uint16_t pos = 0; - uint8_t crc_fields = 3; - - rfcomm_out_buffer[pos++] = address; - rfcomm_out_buffer[pos++] = control; - - // length field can be 1 or 2 octets - if (len < 128){ - rfcomm_out_buffer[pos++] = (len << 1)| 1; // bits 0-6 - } else { - rfcomm_out_buffer[pos++] = (len & 0x7f) << 1; // bits 0-6 - rfcomm_out_buffer[pos++] = len >> 7; // bits 7-14 - crc_fields++; - } - - // add credits for UIH frames when PF bit is set - if (control == BT_RFCOMM_UIH_PF){ - rfcomm_out_buffer[pos++] = credits; - } - - // copy actual data - if (len) { - memcpy(&rfcomm_out_buffer[pos], data, len); - pos += len; - } - - // UIH frames only calc FCS over address + control (5.1.1) - if ((control & 0xef) == BT_RFCOMM_UIH){ - crc_fields = 2; - } - rfcomm_out_buffer[pos++] = crc8_calc(rfcomm_out_buffer, crc_fields); // calc fcs - - int credits_taken = 0; - if (multiplexer->l2cap_credits){ - credits_taken++; - multiplexer->l2cap_credits--; - } else { - log_info( "rfcomm_send_packet addr %02x, ctrl %02x size %u without l2cap credits\n", address, control, pos); - } - - int err = l2cap_send_prepared(multiplexer->l2cap_cid, pos); - - if (err) { - // undo credit counting - multiplexer->l2cap_credits += credits_taken; - } - return err; -} - -// C/R Flag in Address -// - terms: initiator = station that creates multiplexer with SABM -// - terms: responder = station that responds to multiplexer setup with UA -// "For SABM, UA, DM and DISC frames C/R bit is set according to Table 1 in GSM 07.10, section 5.2.1.2" -// - command initiator = 1 /response responder = 1 -// - command responder = 0 /response initiator = 0 -// "For UIH frames, the C/R bit is always set according to section 5.4.3.1 in GSM 07.10. -// This applies independently of what is contained wthin the UIH frames, either data or control messages." -// - c/r = 1 for frames by initiating station, 0 = for frames by responding station - -// C/R Flag in Message -// "In the message level, the C/R bit in the command type field is set as stated in section 5.4.6.2 in GSM 07.10." -// - If the C/R bit is set to 1 the message is a command -// - if it is set to 0 the message is a response. - -// temp/old messge construction - -// new object oriented version -static int rfcomm_send_sabm(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); // command - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_SABM, 0, NULL, 0); -} - -static int rfcomm_send_disc(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); // command - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_DISC, 0, NULL, 0); -} - -static int rfcomm_send_ua(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ - uint8_t address = (1 << 0) | ((multiplexer->outgoing ^ 1) << 1) | (dlci << 2); // response - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UA, 0, NULL, 0); -} - -static int rfcomm_send_dm_pf(rfcomm_multiplexer_t *multiplexer, uint8_t dlci){ - uint8_t address = (1 << 0) | ((multiplexer->outgoing ^ 1) << 1) | (dlci << 2); // response - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_DM_PF, 0, NULL, 0); -} - -static int rfcomm_send_uih_msc_cmd(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t signals) { - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); - uint8_t payload[4]; - uint8_t pos = 0; - payload[pos++] = BT_RFCOMM_MSC_CMD; - payload[pos++] = 2 << 1 | 1; // len - payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); // CMD => C/R = 1 - payload[pos++] = signals; - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); -} - -static int rfcomm_send_uih_msc_rsp(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t signals) { - uint8_t address = (1 << 0) | (multiplexer->outgoing<< 1); - uint8_t payload[4]; - uint8_t pos = 0; - payload[pos++] = BT_RFCOMM_MSC_RSP; - payload[pos++] = 2 << 1 | 1; // len - payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); // CMD => C/R = 1 - payload[pos++] = signals; - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); -} - -static int rfcomm_send_uih_pn_command(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint16_t max_frame_size){ - uint8_t payload[10]; - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); - uint8_t pos = 0; - payload[pos++] = BT_RFCOMM_PN_CMD; - payload[pos++] = 8 << 1 | 1; // len - payload[pos++] = dlci; - payload[pos++] = 0xf0; // pre-defined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM - payload[pos++] = 0; // priority - payload[pos++] = 0; // max 60 seconds ack - payload[pos++] = max_frame_size & 0xff; // max framesize low - payload[pos++] = max_frame_size >> 8; // max framesize high - payload[pos++] = 0x00; // number of retransmissions - payload[pos++] = 0x00; // (unused error recovery window) initial number of credits - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); -} - -// "The response may not change the DLCI, the priority, the convergence layer, or the timer value." RFCOMM-tutorial.pdf -static int rfcomm_send_uih_pn_response(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, - uint8_t priority, uint16_t max_frame_size){ - uint8_t payload[10]; - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); - uint8_t pos = 0; - payload[pos++] = BT_RFCOMM_PN_RSP; - payload[pos++] = 8 << 1 | 1; // len - payload[pos++] = dlci; - payload[pos++] = 0xe0; // pre defined for Bluetooth, see 5.5.3 of TS 07.10 Adaption for RFCOMM - payload[pos++] = priority; // priority - payload[pos++] = 0; // max 60 seconds ack - payload[pos++] = max_frame_size & 0xff; // max framesize low - payload[pos++] = max_frame_size >> 8; // max framesize high - payload[pos++] = 0x00; // number of retransmissions - payload[pos++] = 0x00; // (unused error recovery window) initial number of credits - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); -} - -static int rfcomm_send_uih_rpn_rsp(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, rfcomm_rpn_data_t *rpn_data) { - uint8_t payload[10]; - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1); - uint8_t pos = 0; - payload[pos++] = BT_RFCOMM_RPN_RSP; - payload[pos++] = 8 << 1 | 1; // len - payload[pos++] = (1 << 0) | (1 << 1) | (dlci << 2); // CMD => C/R = 1 - payload[pos++] = rpn_data->baud_rate; - payload[pos++] = rpn_data->flags; - payload[pos++] = rpn_data->flow_control; - payload[pos++] = rpn_data->xon; - payload[pos++] = rpn_data->xoff; - payload[pos++] = rpn_data->parameter_mask_0; - payload[pos++] = rpn_data->parameter_mask_1; - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, (uint8_t *) payload, pos); -} - -static int rfcomm_send_uih_data(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t *data, uint16_t len){ - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); - return rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH, 0, data, len); -} - -static void rfcomm_send_uih_credits(rfcomm_multiplexer_t *multiplexer, uint8_t dlci, uint8_t credits){ - uint8_t address = (1 << 0) | (multiplexer->outgoing << 1) | (dlci << 2); - rfcomm_send_packet_for_multiplexer(multiplexer, address, BT_RFCOMM_UIH_PF, credits, NULL, 0); -} - -// MARK: RFCOMM MULTIPLEXER - -static void rfcomm_multiplexer_finalize(rfcomm_multiplexer_t * multiplexer){ - - // remove (potential) timer - if (multiplexer->timer_active) { - run_loop_remove_timer(&multiplexer->timer); - multiplexer->timer_active = 0; - } - - // close and remove all channels - linked_item_t *it = (linked_item_t *) &rfcomm_channels; - while (it->next){ - rfcomm_channel_t * channel = (rfcomm_channel_t *) it->next; - if (channel->multiplexer == multiplexer) { - // emit appropriate events - if (channel->state == RFCOMM_CHANNEL_OPEN) { - rfcomm_emit_channel_closed(channel); - } else { - rfcomm_emit_channel_opened(channel, RFCOMM_MULTIPLEXER_STOPPED); - } - // remove from list - it->next = it->next->next; - // free channel struct - btstack_memory_rfcomm_channel_free(channel); - } else { - it = it->next; - } - } - - // keep reference to l2cap channel - uint16_t l2cap_cid = multiplexer->l2cap_cid; - - // remove mutliplexer - linked_list_remove( &rfcomm_multiplexers, (linked_item_t *) multiplexer); - btstack_memory_rfcomm_multiplexer_free(multiplexer); - - // close l2cap multiplexer channel, too - l2cap_disconnect_internal(l2cap_cid, 0x13); -} - -static void rfcomm_multiplexer_timer_handler(timer_source_t *timer){ - rfcomm_multiplexer_t * multiplexer = (rfcomm_multiplexer_t *) linked_item_get_user( (linked_item_t *) timer); - if (!rfcomm_multiplexer_has_channels(multiplexer)){ - log_info( "rfcomm_multiplexer_timer_handler timeout: shutting down multiplexer!\n"); - rfcomm_multiplexer_finalize(multiplexer); - } -} - -static void rfcomm_multiplexer_prepare_idle_timer(rfcomm_multiplexer_t * multiplexer){ - if (multiplexer->timer_active) { - run_loop_remove_timer(&multiplexer->timer); - multiplexer->timer_active = 0; - } - if (!rfcomm_multiplexer_has_channels(multiplexer)){ - // start timer for multiplexer timeout check - run_loop_set_timer(&multiplexer->timer, RFCOMM_MULIPLEXER_TIMEOUT_MS); - multiplexer->timer.process = rfcomm_multiplexer_timer_handler; - linked_item_set_user((linked_item_t*) &multiplexer->timer, multiplexer); - run_loop_add_timer(&multiplexer->timer); - multiplexer->timer_active = 1; - } -} - -static void rfcomm_multiplexer_opened(rfcomm_multiplexer_t *multiplexer){ - log_info("Multiplexer up and running\n"); - multiplexer->state = RFCOMM_MULTIPLEXER_OPEN; - - rfcomm_channel_event_t event = { CH_EVT_MULTIPLEXER_READY }; - - // transition of channels that wait for multiplexer - linked_item_t *it; - for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ - rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); - if (channel->multiplexer != multiplexer) continue; - rfcomm_channel_state_machine(channel, &event); - } - - rfcomm_run(); - rfcomm_multiplexer_prepare_idle_timer(multiplexer); -} - - -/** - * @return handled packet - */ -static int rfcomm_multiplexer_hci_event_handler(uint8_t *packet, uint16_t size){ - bd_addr_t event_addr; - uint16_t psm; - uint16_t l2cap_cid; - hci_con_handle_t con_handle; - rfcomm_multiplexer_t *multiplexer = NULL; - switch (packet[0]) { - - // accept incoming PSM_RFCOMM connection if no multiplexer exists yet - case L2CAP_EVENT_INCOMING_CONNECTION: - // data: event(8), len(8), address(48), handle (16), psm (16), source cid(16) dest cid(16) - bt_flip_addr(event_addr, &packet[2]); - con_handle = READ_BT_16(packet, 8); - psm = READ_BT_16(packet, 10); - l2cap_cid = READ_BT_16(packet, 12); - - if (psm != PSM_RFCOMM) break; - - multiplexer = rfcomm_multiplexer_for_addr(&event_addr); - - if (multiplexer) { - log_info("INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_RFCOMM => decline - multiplexer already exists", l2cap_cid); - l2cap_decline_connection_internal(l2cap_cid, 0x04); // no resources available - return 1; - } - - // create and inititialize new multiplexer instance (incoming) - multiplexer = rfcomm_multiplexer_create_for_addr(&event_addr); - if (!multiplexer){ - log_info("INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_RFCOMM => decline - no memory left", l2cap_cid); - l2cap_decline_connection_internal(l2cap_cid, 0x04); // no resources available - return 1; - } - - multiplexer->con_handle = con_handle; - multiplexer->l2cap_cid = l2cap_cid; - multiplexer->state = RFCOMM_MULTIPLEXER_W4_SABM_0; - - log_info("L2CAP_EVENT_INCOMING_CONNECTION (l2cap_cid 0x%02x) for PSM_RFCOMM => accept", l2cap_cid); - l2cap_accept_connection_internal(l2cap_cid); - return 1; - - // l2cap connection opened -> store l2cap_cid, remote_addr - case L2CAP_EVENT_CHANNEL_OPENED: - if (READ_BT_16(packet, 11) != PSM_RFCOMM) break; - log_info("L2CAP_EVENT_CHANNEL_OPENED for PSM_RFCOMM\n"); - // get multiplexer for remote addr - con_handle = READ_BT_16(packet, 9); - l2cap_cid = READ_BT_16(packet, 13); - bt_flip_addr(event_addr, &packet[3]); - multiplexer = rfcomm_multiplexer_for_addr(&event_addr); - if (!multiplexer) { - log_error("L2CAP_EVENT_CHANNEL_OPENED but no multiplexer prepared\n"); - return 1; - } - if (multiplexer->state == RFCOMM_MULTIPLEXER_W4_CONNECT) { - log_info("L2CAP_EVENT_CHANNEL_OPENED: outgoing connection\n"); - // wrong remote addr - if (BD_ADDR_CMP(event_addr, multiplexer->remote_addr)) break; - multiplexer->l2cap_cid = l2cap_cid; - multiplexer->con_handle = con_handle; - // send SABM #0 - multiplexer->state = RFCOMM_MULTIPLEXER_SEND_SABM_0; - } else { // multiplexer->state == RFCOMM_MULTIPLEXER_W4_SABM_0 - - // set max frame size based on l2cap MTU - multiplexer->max_frame_size = rfcomm_max_frame_size_for_l2cap_mtu(READ_BT_16(packet, 17)); - } - return 1; - - // l2cap disconnect -> state = RFCOMM_MULTIPLEXER_CLOSED; - - case L2CAP_EVENT_CREDITS: - // data: event(8), len(8), local_cid(16), credits(8) - l2cap_cid = READ_BT_16(packet, 2); - multiplexer = rfcomm_multiplexer_for_l2cap_cid(l2cap_cid); - if (!multiplexer) break; - multiplexer->l2cap_credits += packet[4]; - - // log_info("L2CAP_EVENT_CREDITS: %u (now %u)\n", packet[4], multiplexer->l2cap_credits); - - // new credits, continue with signaling - rfcomm_run(); - - if (multiplexer->state != RFCOMM_MULTIPLEXER_OPEN) break; - rfcomm_hand_out_credits(); - return 1; - - case DAEMON_EVENT_HCI_PACKET_SENT: - // testing DMA done code - rfcomm_run(); - break; - - case L2CAP_EVENT_CHANNEL_CLOSED: - // data: event (8), len(8), channel (16) - l2cap_cid = READ_BT_16(packet, 2); - multiplexer = rfcomm_multiplexer_for_l2cap_cid(l2cap_cid); - if (!multiplexer) break; - switch (multiplexer->state) { - case RFCOMM_MULTIPLEXER_W4_SABM_0: - case RFCOMM_MULTIPLEXER_W4_UA_0: - case RFCOMM_MULTIPLEXER_OPEN: - rfcomm_multiplexer_finalize(multiplexer); - return 1; - default: - break; - } - break; - default: - break; - } - return 0; -} - -static int rfcomm_multiplexer_l2cap_packet_handler(uint16_t channel, uint8_t *packet, uint16_t size){ - - // get or create a multiplexer for a certain device - rfcomm_multiplexer_t *multiplexer = rfcomm_multiplexer_for_l2cap_cid(channel); - if (!multiplexer) return 0; - - // but only care for multiplexer control channel - uint8_t frame_dlci = packet[0] >> 2; - if (frame_dlci) return 0; - const uint8_t length_offset = (packet[2] & 1) ^ 1; // to be used for pos >= 3 - const uint8_t credit_offset = ((packet[1] & BT_RFCOMM_UIH_PF) == BT_RFCOMM_UIH_PF) ? 1 : 0; // credits for uih_pf frames - const uint8_t payload_offset = 3 + length_offset + credit_offset; - switch (packet[1]){ - - case BT_RFCOMM_SABM: - if (multiplexer->state == RFCOMM_MULTIPLEXER_W4_SABM_0){ - log_info("Received SABM #0\n"); - multiplexer->outgoing = 0; - multiplexer->state = RFCOMM_MULTIPLEXER_SEND_UA_0; - return 1; - } - break; - - case BT_RFCOMM_UA: - if (multiplexer->state == RFCOMM_MULTIPLEXER_W4_UA_0) { - // UA #0 -> send UA #0, state = RFCOMM_MULTIPLEXER_OPEN - log_info("Received UA #0 \n"); - rfcomm_multiplexer_opened(multiplexer); - return 1; - } - break; - - case BT_RFCOMM_DISC: - // DISC #0 -> send UA #0, close multiplexer - log_info("Received DISC #0, (ougoing = %u)\n", multiplexer->outgoing); - multiplexer->state = RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC; - return 1; - - case BT_RFCOMM_DM: - // DM #0 - we shouldn't get this, just give up - log_info("Received DM #0\n"); - log_info("-> Closing down multiplexer\n"); - rfcomm_multiplexer_finalize(multiplexer); - return 1; - - case BT_RFCOMM_UIH: - if (packet[payload_offset] == BT_RFCOMM_CLD_CMD){ - // Multiplexer close down (CLD) -> close mutliplexer - log_info("Received Multiplexer close down command\n"); - log_info("-> Closing down multiplexer\n"); - rfcomm_multiplexer_finalize(multiplexer); - return 1; - } - break; - - default: - break; - - } - return 0; -} - -static void rfcomm_multiplexer_state_machine(rfcomm_multiplexer_t * multiplexer, RFCOMM_MULTIPLEXER_EVENT event){ - - // process stored DM responses - if (multiplexer->send_dm_for_dlci){ - rfcomm_send_dm_pf(multiplexer, multiplexer->send_dm_for_dlci); - multiplexer->send_dm_for_dlci = 0; - } - - switch (multiplexer->state) { - case RFCOMM_MULTIPLEXER_SEND_SABM_0: - switch (event) { - case MULT_EV_READY_TO_SEND: - log_info("Sending SABM #0 - (multi 0x%p)\n", multiplexer); - multiplexer->state = RFCOMM_MULTIPLEXER_W4_UA_0; - rfcomm_send_sabm(multiplexer, 0); - break; - default: - break; - } - break; - case RFCOMM_MULTIPLEXER_SEND_UA_0: - switch (event) { - case MULT_EV_READY_TO_SEND: - log_info("Sending UA #0\n"); - multiplexer->state = RFCOMM_MULTIPLEXER_OPEN; - rfcomm_send_ua(multiplexer, 0); - rfcomm_multiplexer_opened(multiplexer); - break; - default: - break; - } - break; - case RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC: - switch (event) { - case MULT_EV_READY_TO_SEND: - log_info("Sending UA #0\n"); - log_info("Closing down multiplexer\n"); - multiplexer->state = RFCOMM_MULTIPLEXER_CLOSED; - rfcomm_send_ua(multiplexer, 0); - rfcomm_multiplexer_finalize(multiplexer); - // try to detect authentication errors: drop link key if multiplexer closed before first channel got opened - if (!multiplexer->at_least_one_connection){ - log_info("TODO: no connections established - delete link key prophylactically\n"); - // hci_send_cmd(&hci_delete_stored_link_key, multiplexer->remote_addr); - } - default: - break; - } - break; - default: - break; - } -} - -// MARK: RFCOMM CHANNEL - -static void rfcomm_hand_out_credits(void){ - linked_item_t * it; - for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ - rfcomm_channel_t * channel = (rfcomm_channel_t *) it; - if (channel->state != RFCOMM_CHANNEL_OPEN) { - // log_info("RFCOMM_EVENT_CREDITS: multiplexer not open\n"); - continue; - } - if (channel->packets_granted) { - // log_info("RFCOMM_EVENT_CREDITS: already packets granted\n"); - continue; - } - if (!channel->credits_outgoing) { - // log_info("RFCOMM_EVENT_CREDITS: no outgoing credits\n"); - continue; - } - if (!channel->multiplexer->l2cap_credits){ - // log_info("RFCOMM_EVENT_CREDITS: no l2cap credits\n"); - continue; - } - // channel open, multiplexer has l2cap credits and we didn't hand out credit before -> go! - // log_info("RFCOMM_EVENT_CREDITS: 1\n"); - channel->packets_granted += 1; - rfcomm_emit_credits(channel, 1); - } -} - -static void rfcomm_channel_send_credits(rfcomm_channel_t *channel, uint8_t credits){ - rfcomm_send_uih_credits(channel->multiplexer, channel->dlci, credits); - channel->credits_incoming += credits; - - rfcomm_emit_credit_status(channel); -} - -static void rfcomm_channel_opened(rfcomm_channel_t *rfChannel){ - - log_info("rfcomm_channel_opened!\n"); - - rfChannel->state = RFCOMM_CHANNEL_OPEN; - rfcomm_emit_channel_opened(rfChannel, 0); - rfcomm_hand_out_credits(); - - // remove (potential) timer - rfcomm_multiplexer_t *multiplexer = rfChannel->multiplexer; - if (multiplexer->timer_active) { - run_loop_remove_timer(&multiplexer->timer); - multiplexer->timer_active = 0; - } - // hack for problem detecting authentication failure - multiplexer->at_least_one_connection = 1; - - // start next connection request if pending - rfcomm_run(); -} - -static void rfcomm_channel_packet_handler_uih(rfcomm_multiplexer_t *multiplexer, uint8_t * packet, uint16_t size){ - const uint8_t frame_dlci = packet[0] >> 2; - const uint8_t length_offset = (packet[2] & 1) ^ 1; // to be used for pos >= 3 - const uint8_t credit_offset = ((packet[1] & BT_RFCOMM_UIH_PF) == BT_RFCOMM_UIH_PF) ? 1 : 0; // credits for uih_pf frames - const uint8_t payload_offset = 3 + length_offset + credit_offset; - - rfcomm_channel_t * channel = rfcomm_channel_for_multiplexer_and_dlci(multiplexer, frame_dlci); - if (!channel) return; - - // handle new outgoing credits - if (packet[1] == BT_RFCOMM_UIH_PF) { - - // add them - uint16_t new_credits = packet[3+length_offset]; - channel->credits_outgoing += new_credits; - log_info( "RFCOMM data UIH_PF, new credits: %u, now %u\n", new_credits, channel->credits_outgoing); - - // notify channel statemachine - rfcomm_channel_event_t channel_event = { CH_EVT_RCVD_CREDITS }; - rfcomm_channel_state_machine(channel, &channel_event); - } - - // contains payload? - if (size - 1 > payload_offset){ - - // log_info( "RFCOMM data UIH_PF, size %u, channel %p\n", size-payload_offset-1, rfChannel->connection); - - // decrease incoming credit counter - if (channel->credits_incoming > 0){ - channel->credits_incoming--; - } - - // deliver payload - (*app_packet_handler)(channel->connection, RFCOMM_DATA_PACKET, channel->rfcomm_cid, - &packet[payload_offset], size-payload_offset-1); - } - - // automatically provide new credits to remote device, if no incoming flow control - if (!channel->incoming_flow_control && channel->credits_incoming < 5){ - channel->new_credits_incoming = 0x30; - } - - rfcomm_emit_credit_status(channel); - - // we received new RFCOMM credits, hand them out if possible - rfcomm_hand_out_credits(); -} - -static void rfcomm_channel_accept_pn(rfcomm_channel_t *channel, rfcomm_channel_event_pn_t *event){ - // priority of client request - channel->pn_priority = event->priority; - - // new credits - channel->credits_outgoing = event->credits_outgoing; - - // negotiate max frame size - if (channel->max_frame_size > channel->multiplexer->max_frame_size) { - channel->max_frame_size = channel->multiplexer->max_frame_size; - } - if (channel->max_frame_size > event->max_frame_size) { - channel->max_frame_size = event->max_frame_size; - } - -} - -static void rfcomm_channel_finalize(rfcomm_channel_t *channel){ - - rfcomm_multiplexer_t *multiplexer = channel->multiplexer; - - // remove from list - linked_list_remove( &rfcomm_channels, (linked_item_t *) channel); - - // free channel - btstack_memory_rfcomm_channel_free(channel); - - // update multiplexer timeout after channel was removed from list - rfcomm_multiplexer_prepare_idle_timer(multiplexer); -} - -static void rfcomm_channel_state_machine_2(rfcomm_multiplexer_t * multiplexer, uint8_t dlci, rfcomm_channel_event_t *event){ - - // TODO: if client max frame size is smaller than RFCOMM_DEFAULT_SIZE, send PN - - - // lookup existing channel - rfcomm_channel_t * channel = rfcomm_channel_for_multiplexer_and_dlci(multiplexer, dlci); - - // log_info("rfcomm_channel_state_machine_2 lookup dlci #%u = 0x%08x - event %u\n", dlci, (int) channel, event->type); - - if (channel) { - rfcomm_channel_state_machine(channel, event); - return; - } - - // service registered? - rfcomm_service_t * service = rfcomm_service_for_channel(dlci >> 1); - // log_info("rfcomm_channel_state_machine_2 service dlci #%u = 0x%08x\n", dlci, (int) service); - if (!service) { - // discard request by sending disconnected mode - multiplexer->send_dm_for_dlci = dlci; - return; - } - - // create channel for some events - switch (event->type) { - case CH_EVT_RCVD_SABM: - case CH_EVT_RCVD_PN: - case CH_EVT_RCVD_RPN_REQ: - case CH_EVT_RCVD_RPN_CMD: - // setup incoming channel - channel = rfcomm_channel_create(multiplexer, service, dlci >> 1); - if (!channel){ - // discard request by sending disconnected mode - multiplexer->send_dm_for_dlci = dlci; - } - break; - default: - break; - } - - if (!channel) { - // discard request by sending disconnected mode - multiplexer->send_dm_for_dlci = dlci; - return; - } - channel->connection = service->connection; - rfcomm_channel_state_machine(channel, event); -} - -void rfcomm_channel_packet_handler(rfcomm_multiplexer_t * multiplexer, uint8_t *packet, uint16_t size){ - - // rfcomm: (0) addr [76543 server channel] [2 direction: initiator uses 1] [1 C/R: CMD by initiator = 1] [0 EA=1] - const uint8_t frame_dlci = packet[0] >> 2; - uint8_t message_dlci; // used by commands in UIH(_PF) packets - uint8_t message_len; // " - - // rfcomm: (1) command/control - // -- credits_offset = 1 if command == BT_RFCOMM_UIH_PF - const uint8_t credit_offset = ((packet[1] & BT_RFCOMM_UIH_PF) == BT_RFCOMM_UIH_PF) ? 1 : 0; // credits for uih_pf frames - // rfcomm: (2) length. if bit 0 is cleared, 2 byte length is used. (little endian) - const uint8_t length_offset = (packet[2] & 1) ^ 1; // to be used for pos >= 3 - // rfcomm: (3+length_offset) credits if credits_offset == 1 - // rfcomm: (3+length_offest+credits_offset) - const uint8_t payload_offset = 3 + length_offset + credit_offset; - - rfcomm_channel_event_t event; - rfcomm_channel_event_pn_t event_pn; - rfcomm_channel_event_rpn_t event_rpn; - - // switch by rfcomm message type - switch(packet[1]) { - - case BT_RFCOMM_SABM: - event.type = CH_EVT_RCVD_SABM; - log_info("Received SABM #%u\n", frame_dlci); - rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); - break; - - case BT_RFCOMM_UA: - event.type = CH_EVT_RCVD_UA; - log_info("Received UA #%u - channel opened\n",frame_dlci); - rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); - break; - - case BT_RFCOMM_DISC: - event.type = CH_EVT_RCVD_DISC; - rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); - break; - - case BT_RFCOMM_DM: - case BT_RFCOMM_DM_PF: - event.type = CH_EVT_RCVD_DM; - rfcomm_channel_state_machine_2(multiplexer, frame_dlci, &event); - break; - - case BT_RFCOMM_UIH_PF: - case BT_RFCOMM_UIH: - - message_len = packet[payload_offset+1] >> 1; - - switch (packet[payload_offset]) { - case BT_RFCOMM_PN_CMD: - message_dlci = packet[payload_offset+2]; - event_pn.super.type = CH_EVT_RCVD_PN; - event_pn.priority = packet[payload_offset+4]; - event_pn.max_frame_size = READ_BT_16(packet, payload_offset+6); - event_pn.credits_outgoing = packet[payload_offset+9]; - log_info("Received UIH Parameter Negotiation Command for #%u\n", message_dlci); - rfcomm_channel_state_machine_2(multiplexer, message_dlci, (rfcomm_channel_event_t*) &event_pn); - break; - - case BT_RFCOMM_PN_RSP: - message_dlci = packet[payload_offset+2]; - event_pn.super.type = CH_EVT_RCVD_PN_RSP; - event_pn.priority = packet[payload_offset+4]; - event_pn.max_frame_size = READ_BT_16(packet, payload_offset+6); - event_pn.credits_outgoing = packet[payload_offset+9]; - log_info("UIH Parameter Negotiation Response max frame %u, credits %u\n", - event_pn.max_frame_size, event_pn.credits_outgoing); - rfcomm_channel_state_machine_2(multiplexer, message_dlci, (rfcomm_channel_event_t*) &event_pn); - break; - - case BT_RFCOMM_MSC_CMD: - message_dlci = packet[payload_offset+2] >> 2; - event.type = CH_EVT_RCVD_MSC_CMD; - log_info("Received MSC CMD for #%u, \n", message_dlci); - rfcomm_channel_state_machine_2(multiplexer, message_dlci, &event); - break; - - case BT_RFCOMM_MSC_RSP: - message_dlci = packet[payload_offset+2] >> 2; - event.type = CH_EVT_RCVD_MSC_RSP; - log_info("Received MSC RSP for #%u\n", message_dlci); - rfcomm_channel_state_machine_2(multiplexer, message_dlci, &event); - break; - - case BT_RFCOMM_RPN_CMD: - message_dlci = packet[payload_offset+2] >> 2; - switch (message_len){ - case 1: - log_info("Received Remote Port Negotiation for #%u\n", message_dlci); - event.type = CH_EVT_RCVD_RPN_REQ; - rfcomm_channel_state_machine_2(multiplexer, message_dlci, &event); - break; - case 8: - log_info("Received Remote Port Negotiation (Info) for #%u\n", message_dlci); - event_rpn.super.type = CH_EVT_RCVD_RPN_CMD; - event_rpn.data.baud_rate = packet[payload_offset+3]; - event_rpn.data.flags = packet[payload_offset+4]; - event_rpn.data.flow_control = packet[payload_offset+5]; - event_rpn.data.xon = packet[payload_offset+6]; - event_rpn.data.xoff = packet[payload_offset+7]; - event_rpn.data.parameter_mask_0 = packet[payload_offset+8]; - event_rpn.data.parameter_mask_1 = packet[payload_offset+9]; - rfcomm_channel_state_machine_2(multiplexer, message_dlci, (rfcomm_channel_event_t*) &event_rpn); - break; - default: - break; - } - break; - - default: - log_error("Received unknown UIH packet - 0x%02x\n", packet[payload_offset]); - break; - } - break; - - default: - log_error("Received unknown RFCOMM message type %x\n", packet[1]); - break; - } - - // trigger next action - example W4_PN_RSP: transition to SEND_SABM which only depends on "can send" - rfcomm_run(); -} - -void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - - // multiplexer handler - int handled = 0; - switch (packet_type) { - case HCI_EVENT_PACKET: - handled = rfcomm_multiplexer_hci_event_handler(packet, size); - break; - case L2CAP_DATA_PACKET: - handled = rfcomm_multiplexer_l2cap_packet_handler(channel, packet, size); - break; - default: - break; - } - - if (handled) { - rfcomm_run(); - return; - } - - // we only handle l2cap packet over open multiplexer channel now - if (packet_type != L2CAP_DATA_PACKET) { - (*app_packet_handler)(NULL, packet_type, channel, packet, size); - return; - } - rfcomm_multiplexer_t * multiplexer = rfcomm_multiplexer_for_l2cap_cid(channel); - if (!multiplexer || multiplexer->state != RFCOMM_MULTIPLEXER_OPEN) { - (*app_packet_handler)(NULL, packet_type, channel, packet, size); - return; - } - - // channel data ? - // rfcomm: (0) addr [76543 server channel] [2 direction: initiator uses 1] [1 C/R: CMD by initiator = 1] [0 EA=1] - const uint8_t frame_dlci = packet[0] >> 2; - - if (frame_dlci && (packet[1] == BT_RFCOMM_UIH || packet[1] == BT_RFCOMM_UIH_PF)) { - rfcomm_channel_packet_handler_uih(multiplexer, packet, size); - rfcomm_run(); - return; - } - - rfcomm_channel_packet_handler(multiplexer, packet, size); -} - -static int rfcomm_channel_ready_for_open(rfcomm_channel_t *channel){ - // log_info("rfcomm_channel_ready_for_open state %u, flags needed %04x, current %04x, rf credits %u, l2cap credits %u \n", channel->state, RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP|RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP|RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS, channel->state_var, channel->credits_outgoing, channel->multiplexer->l2cap_credits); - if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP) == 0) return 0; - if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP) == 0) return 0; - if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS) == 0) return 0; - if (channel->credits_outgoing == 0) return 0; - - return 1; -} - -static void rfcomm_channel_state_machine(rfcomm_channel_t *channel, rfcomm_channel_event_t *event){ - - // log_info("rfcomm_channel_state_machine: state %u, state_var %04x, event %u\n", channel->state, channel->state_var ,event->type); - - rfcomm_multiplexer_t *multiplexer = channel->multiplexer; - - // TODO: integrate in common switch - if (event->type == CH_EVT_RCVD_DISC){ - rfcomm_emit_channel_closed(channel); - channel->state = RFCOMM_CHANNEL_SEND_UA_AFTER_DISC; - return; - } - - // TODO: integrate in common switch - if (event->type == CH_EVT_RCVD_DM){ - log_info("Received DM message for #%u\n", channel->dlci); - log_info("-> Closing channel locally for #%u\n", channel->dlci); - rfcomm_emit_channel_closed(channel); - rfcomm_channel_finalize(channel); - return; - } - - // remote port negotiation command - just accept everything for now - // - // "The RPN command can be used before a new DLC is opened and should be used whenever the port settings change." - // "The RPN command is specified as optional in TS 07.10, but it is mandatory to recognize and respond to it in RFCOMM. - // (Although the handling of individual settings are implementation-dependent.)" - // - - // TODO: integrate in common switch - if (event->type == CH_EVT_RCVD_RPN_CMD){ - // control port parameters - rfcomm_channel_event_rpn_t *event_rpn = (rfcomm_channel_event_rpn_t*) event; - memcpy(&channel->rpn_data, &event_rpn->data, sizeof(rfcomm_rpn_data_t)); - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP; - return; - } - - // TODO: integrate in common switch - if (event->type == CH_EVT_RCVD_RPN_REQ){ - // default rpn rsp - rfcomm_rpn_data_t rpn_data; - rpn_data.baud_rate = 0xa0; /* 9600 bps */ - rpn_data.flags = 0x03; /* 8-n-1 */ - rpn_data.flow_control = 0; /* no flow control */ - rpn_data.xon = 0xd1; /* XON */ - rpn_data.xoff = 0xd3; /* XOFF */ - rpn_data.parameter_mask_0 = 0x7f; /* parameter mask, all values set */ - rpn_data.parameter_mask_1 = 0x3f; /* parameter mask, all values set */ - memcpy(&channel->rpn_data, &rpn_data, sizeof(rfcomm_rpn_data_t)); - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP; - return; - } - - // TODO: integrate in common swich - if (event->type == CH_EVT_READY_TO_SEND){ - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP){ - log_info("Sending Remote Port Negotiation RSP for #%u\n", channel->dlci); - channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP; - rfcomm_send_uih_rpn_rsp(multiplexer, channel->dlci, &channel->rpn_data); - return; - } - } - - rfcomm_channel_event_pn_t * event_pn = (rfcomm_channel_event_pn_t*) event; - - switch (channel->state) { - case RFCOMM_CHANNEL_CLOSED: - switch (event->type){ - case CH_EVT_RCVD_SABM: - log_info("-> Inform app\n"); - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM; - channel->state = RFCOMM_CHANNEL_INCOMING_SETUP; - rfcomm_emit_connection_request(channel); - break; - case CH_EVT_RCVD_PN: - rfcomm_channel_accept_pn(channel, event_pn); - log_info("-> Inform app\n"); - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_PN; - channel->state = RFCOMM_CHANNEL_INCOMING_SETUP; - rfcomm_emit_connection_request(channel); - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_INCOMING_SETUP: - switch (event->type){ - case CH_EVT_RCVD_SABM: - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM; - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED) { - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_UA; - } - break; - case CH_EVT_RCVD_PN: - rfcomm_channel_accept_pn(channel, event_pn); - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_PN; - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED) { - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP; - } - break; - case CH_EVT_READY_TO_SEND: - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP){ - log_info("Sending UIH Parameter Negotiation Respond for #%u\n", channel->dlci); - channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP; - rfcomm_send_uih_pn_response(multiplexer, channel->dlci, channel->pn_priority, channel->max_frame_size); - } - else if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_UA){ - log_info("Sending UA #%u\n", channel->dlci); - channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_UA; - rfcomm_send_ua(multiplexer, channel->dlci); - } - if ((channel->state_var & RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED) && (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM)) { - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD; - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS; - channel->state = RFCOMM_CHANNEL_DLC_SETUP; - } - break; - - default: - break; - } - break; - - case RFCOMM_CHANNEL_W4_MULTIPLEXER: - switch (event->type) { - case CH_EVT_MULTIPLEXER_READY: - log_info("Muliplexer opened, sending UIH PN next\n"); - channel->state = RFCOMM_CHANNEL_SEND_UIH_PN; - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_SEND_UIH_PN: - switch (event->type) { - case CH_EVT_READY_TO_SEND: - log_info("Sending UIH Parameter Negotiation Command for #%u (channel 0x%p)\n", channel->dlci, channel ); - channel->state = RFCOMM_CHANNEL_W4_PN_RSP; - rfcomm_send_uih_pn_command(multiplexer, channel->dlci, channel->max_frame_size); - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_W4_PN_RSP: - switch (event->type){ - case CH_EVT_RCVD_PN_RSP: - // update max frame size - if (channel->max_frame_size > event_pn->max_frame_size) { - channel->max_frame_size = event_pn->max_frame_size; - } - // new credits - channel->credits_outgoing = event_pn->credits_outgoing; - channel->state = RFCOMM_CHANNEL_SEND_SABM_W4_UA; - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_SEND_SABM_W4_UA: - switch (event->type) { - case CH_EVT_READY_TO_SEND: - log_info("Sending SABM #%u\n", channel->dlci); - channel->state = RFCOMM_CHANNEL_W4_UA; - rfcomm_send_sabm(multiplexer, channel->dlci); - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_W4_UA: - switch (event->type){ - case CH_EVT_RCVD_UA: - channel->state = RFCOMM_CHANNEL_DLC_SETUP; - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD; - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS; - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_DLC_SETUP: - switch (event->type){ - case CH_EVT_RCVD_MSC_CMD: - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD; - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; - break; - case CH_EVT_RCVD_MSC_RSP: - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP; - break; - - case CH_EVT_READY_TO_SEND: - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD){ - log_info("Sending MSC CMD for #%u\n", channel->dlci); - channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD; - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD; - rfcomm_send_uih_msc_cmd(multiplexer, channel->dlci , 0x8d); // ea=1,fc=0,rtc=1,rtr=1,ic=0,dv=1 - break; - } - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP){ - log_info("Sending MSC RSP for #%u\n", channel->dlci); - channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP; - rfcomm_send_uih_msc_rsp(multiplexer, channel->dlci, 0x8d); // ea=1,fc=0,rtc=1,rtr=1,ic=0,dv=1 - break; - } - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS){ - log_info("Providing credits for #%u\n", channel->dlci); - channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS; - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS; - if (channel->new_credits_incoming) { - uint8_t new_credits = channel->new_credits_incoming; - channel->new_credits_incoming = 0; - rfcomm_channel_send_credits(channel, new_credits); - } - break; - - } - break; - default: - break; - } - // finally done? - if (rfcomm_channel_ready_for_open(channel)){ - channel->state = RFCOMM_CHANNEL_OPEN; - rfcomm_channel_opened(channel); - } - break; - - case RFCOMM_CHANNEL_OPEN: - switch (event->type){ - case CH_EVT_RCVD_MSC_CMD: - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; - break; - case CH_EVT_READY_TO_SEND: - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP){ - log_info("Sending MSC RSP for #%u\n", channel->dlci); - channel->state_var &= ~RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP; - rfcomm_send_uih_msc_rsp(multiplexer, channel->dlci, 0x8d); // ea=1,fc=0,rtc=1,rtr=1,ic=0,dv=1 - break; - } - if (channel->new_credits_incoming) { - uint8_t new_credits = channel->new_credits_incoming; - channel->new_credits_incoming = 0; - rfcomm_channel_send_credits(channel, new_credits); - break; - } - break; - case CH_EVT_RCVD_CREDITS: { - // notify daemon -> might trigger re-try of parked connections - uint8_t event[1] = { DAEMON_EVENT_NEW_RFCOMM_CREDITS }; - (*app_packet_handler)(channel->connection, DAEMON_EVENT_PACKET, channel->rfcomm_cid, event, sizeof(event)); - break; - } - default: - break; - } - break; - - case RFCOMM_CHANNEL_SEND_DM: - switch (event->type) { - case CH_EVT_READY_TO_SEND: - log_info("Sending DM_PF for #%u\n", channel->dlci); - // don't emit channel closed - channel was never open - channel->state = RFCOMM_CHANNEL_CLOSED; - rfcomm_send_dm_pf(multiplexer, channel->dlci); - rfcomm_channel_finalize(channel); - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_SEND_DISC: - switch (event->type) { - case CH_EVT_READY_TO_SEND: - channel->state = RFCOMM_CHANNEL_CLOSED; - rfcomm_send_disc(multiplexer, channel->dlci); - rfcomm_emit_channel_closed(channel); - rfcomm_channel_finalize(channel); - break; - default: - break; - } - break; - - case RFCOMM_CHANNEL_SEND_UA_AFTER_DISC: - switch (event->type) { - case CH_EVT_READY_TO_SEND: - log_info("Sending UA after DISC for #%u\n", channel->dlci); - channel->state = RFCOMM_CHANNEL_CLOSED; - rfcomm_send_ua(multiplexer, channel->dlci); - rfcomm_channel_finalize(channel); - break; - default: - break; - } - break; - - default: - break; - } -} - - -// MARK: RFCOMM RUN -// process outstanding signaling tasks -static void rfcomm_run(void){ - - linked_item_t *it; - linked_item_t *next; - - for (it = (linked_item_t *) rfcomm_multiplexers; it ; it = next){ - - next = it->next; // be prepared for removal of channel in state machine - - rfcomm_multiplexer_t * multiplexer = ((rfcomm_multiplexer_t *) it); - - if (!l2cap_can_send_packet_now(multiplexer->l2cap_cid)) { - // log_info("rfcomm_run cannot send l2cap packet for #%u, credits %u\n", multiplexer->l2cap_cid, multiplexer->l2cap_credits); - continue; - } - // log_info("rfcomm_run: multi 0x%08x, state %u\n", (int) multiplexer, multiplexer->state); - - rfcomm_multiplexer_state_machine(multiplexer, MULT_EV_READY_TO_SEND); - } - - for (it = (linked_item_t *) rfcomm_channels; it ; it = next){ - - next = it->next; // be prepared for removal of channel in state machine - - rfcomm_channel_t * channel = ((rfcomm_channel_t *) it); - rfcomm_multiplexer_t * multiplexer = channel->multiplexer; - - if (!l2cap_can_send_packet_now(multiplexer->l2cap_cid)) continue; - - rfcomm_channel_event_t event = { CH_EVT_READY_TO_SEND }; - rfcomm_channel_state_machine(channel, &event); - } -} - -// MARK: RFCOMM BTstack API - -void rfcomm_init(void){ - rfcomm_client_cid_generator = 0; - rfcomm_multiplexers = NULL; - rfcomm_services = NULL; - rfcomm_channels = NULL; -} - -// register packet handler -void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, - uint16_t channel, uint8_t *packet, uint16_t size)){ - app_packet_handler = handler; -} - -// send packet over specific channel -int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len){ - - rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); - if (!channel){ - log_error("rfcomm_send_internal cid %u doesn't exist!\n", rfcomm_cid); - return 0; - } - - if (!channel->credits_outgoing){ - log_info("rfcomm_send_internal cid %u, no rfcomm outgoing credits!\n", rfcomm_cid); - return RFCOMM_NO_OUTGOING_CREDITS; - } - - if (!channel->packets_granted){ - log_info("rfcomm_send_internal cid %u, no rfcomm credits granted!\n", rfcomm_cid); - return RFCOMM_NO_OUTGOING_CREDITS; - } - - // log_info("rfcomm_send_internal: len %u... outgoing credits %u, l2cap credit %us, granted %u\n", - // len, channel->credits_outgoing, channel->multiplexer->l2cap_credits, channel->packets_granted); - - - // send might cause l2cap to emit new credits, update counters first - channel->credits_outgoing--; - int packets_granted_decreased = 0; - if (channel->packets_granted) { - channel->packets_granted--; - packets_granted_decreased++; - } - - int result = rfcomm_send_uih_data(channel->multiplexer, channel->dlci, data, len); - - if (result != 0) { - channel->credits_outgoing++; - channel->packets_granted += packets_granted_decreased; - log_info("rfcomm_send_internal: error %d\n", result); - return result; - } - - // log_info("rfcomm_send_internal: now outgoing credits %u, l2cap credit %us, granted %u\n", - // channel->credits_outgoing, channel->multiplexer->l2cap_credits, channel->packets_granted); - - rfcomm_hand_out_credits(); - - return result; -} - -void rfcomm_create_channel2(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t incoming_flow_control, uint8_t initial_credits){ - log_info("rfcomm_create_channel_internal to %s, at channel #%02x, flow control %u, init credits %u\n", bd_addr_to_str(*addr), server_channel, - incoming_flow_control, initial_credits); - - // create new multiplexer if necessary - rfcomm_multiplexer_t * multiplexer = rfcomm_multiplexer_for_addr(addr); - if (!multiplexer) { - multiplexer = rfcomm_multiplexer_create_for_addr(addr); - if (!multiplexer){ - rfcomm_emit_channel_open_failed_outgoing_memory(connection, addr, server_channel); - return; - } - multiplexer->outgoing = 1; - multiplexer->state = RFCOMM_MULTIPLEXER_W4_CONNECT; - } - - // prepare channel - rfcomm_channel_t * channel = rfcomm_channel_create(multiplexer, NULL, server_channel); - if (!channel){ - rfcomm_emit_channel_open_failed_outgoing_memory(connection, addr, server_channel); - return; - } - channel->connection = connection; - channel->incoming_flow_control = incoming_flow_control; - channel->new_credits_incoming = initial_credits; - - // start multiplexer setup - if (multiplexer->state != RFCOMM_MULTIPLEXER_OPEN) { - - channel->state = RFCOMM_CHANNEL_W4_MULTIPLEXER; - - l2cap_create_channel_internal(connection, rfcomm_packet_handler, *addr, PSM_RFCOMM, l2cap_max_mtu()); - - return; - } - - channel->state = RFCOMM_CHANNEL_SEND_UIH_PN; - - // start connecting, if multiplexer is already up and running - rfcomm_run(); -} - -void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t initial_credits){ - rfcomm_create_channel2(connection, addr, server_channel, 1, initial_credits); -} - -void rfcomm_create_channel_internal(void * connection, bd_addr_t *addr, uint8_t server_channel){ - rfcomm_create_channel2(connection, addr, server_channel, 0, 0x30); -} - -void rfcomm_disconnect_internal(uint16_t rfcomm_cid){ - rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); - if (channel) { - channel->state = RFCOMM_CHANNEL_SEND_DISC; - } - - // process - rfcomm_run(); -} - - -void rfcomm_register_service2(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t incoming_flow_control, uint8_t initial_credits){ - // check if already registered - rfcomm_service_t * service = rfcomm_service_for_channel(channel); - if (service){ - rfcomm_emit_service_registered(connection, RFCOMM_CHANNEL_ALREADY_REGISTERED, channel); - return; - } - - // alloc structure - service = (rfcomm_service_t*)btstack_memory_rfcomm_service_get(); - if (!service) { - rfcomm_emit_service_registered(connection, BTSTACK_MEMORY_ALLOC_FAILED, channel); - return; - } - - // register with l2cap if not registered before, max MTU - if (linked_list_empty(&rfcomm_services)){ - l2cap_register_service_internal(NULL, rfcomm_packet_handler, PSM_RFCOMM, 0xffff); - } - - // fill in - service->connection = connection; - service->server_channel = channel; - service->max_frame_size = max_frame_size; - service->incoming_flow_control = incoming_flow_control; - service->incoming_initial_credits = initial_credits; - - // add to services list - linked_list_add(&rfcomm_services, (linked_item_t *) service); - - // done - rfcomm_emit_service_registered(connection, 0, channel); -} - -void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits){ - rfcomm_register_service2(connection, channel, max_frame_size, 1, initial_credits); -} - -void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size){ - rfcomm_register_service2(connection, channel, max_frame_size, 0, 0x30); -} - -void rfcomm_unregister_service_internal(uint8_t service_channel){ - rfcomm_service_t *service = rfcomm_service_for_channel(service_channel); - if (!service) return; - linked_list_remove(&rfcomm_services, (linked_item_t *) service); - btstack_memory_rfcomm_service_free(service); - - // unregister if no services active - if (linked_list_empty(&rfcomm_services)){ - // bt_send_cmd(&l2cap_unregister_service, PSM_RFCOMM); - l2cap_unregister_service_internal(NULL, PSM_RFCOMM); - } -} - -void rfcomm_accept_connection_internal(uint16_t rfcomm_cid){ - log_info("Received Accept Connction\n"); - rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); - if (!channel) return; - switch (channel->state) { - case RFCOMM_CHANNEL_INCOMING_SETUP: - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED; - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_PN){ - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP; - } - if (channel->state_var & RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM){ - channel->state_var |= RFCOMM_CHANNEL_STATE_VAR_SEND_UA; - } - break; - default: - break; - } - - // process - rfcomm_run(); -} - -void rfcomm_decline_connection_internal(uint16_t rfcomm_cid){ - log_info("Received Decline Connction\n"); - rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); - if (!channel) return; - switch (channel->state) { - case RFCOMM_CHANNEL_INCOMING_SETUP: - channel->state = RFCOMM_CHANNEL_SEND_DM; - break; - default: - break; - } - - // process - rfcomm_run(); -} - -void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits){ - rfcomm_channel_t * channel = rfcomm_channel_for_rfcomm_cid(rfcomm_cid); - if (!channel) return; - if (!channel->incoming_flow_control) return; - channel->new_credits_incoming += credits; - - // process - rfcomm_run(); -} - -// -void rfcomm_close_connection(void *connection){ - linked_item_t *it; - - // close open channels - for (it = (linked_item_t *) rfcomm_channels; it ; it = it->next){ - rfcomm_channel_t * channel = (rfcomm_channel_t *)it; - if (channel->connection != connection) continue; - channel->state = RFCOMM_CHANNEL_SEND_DISC; - } - - // unregister services - it = (linked_item_t *) &rfcomm_services; - while (it->next) { - rfcomm_service_t * service = (rfcomm_service_t *) it->next; - if (service->connection == connection){ - it->next = it->next->next; - btstack_memory_rfcomm_service_free(service); - } else { - it = it->next; - } - } - - // process - rfcomm_run(); -} - - - -
--- a/BTstack/rfcomm.h Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/rfcomm.h Thu Feb 07 14:01:01 2013 +0000 @@ -102,7 +102,6 @@ } RFCOMM_CHANNEL_STATE; -/* typedef enum { RFCOMM_CHANNEL_STATE_VAR_NONE = 0, RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0, @@ -125,31 +124,6 @@ RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP = 1 << 14, RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS = 1 << 15, } RFCOMM_CHANNEL_STATE_VAR; -*/ - -enum { - RFCOMM_CHANNEL_STATE_VAR_NONE = 0, - RFCOMM_CHANNEL_STATE_VAR_CLIENT_ACCEPTED = 1 << 0, - RFCOMM_CHANNEL_STATE_VAR_RCVD_PN = 1 << 1, - RFCOMM_CHANNEL_STATE_VAR_RCVD_RPN = 1 << 2, - RFCOMM_CHANNEL_STATE_VAR_RCVD_SABM = 1 << 3, - - RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_CMD = 1 << 4, - RFCOMM_CHANNEL_STATE_VAR_RCVD_MSC_RSP = 1 << 5, - RFCOMM_CHANNEL_STATE_VAR_SEND_PN_RSP = 1 << 6, - RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_INFO = 1 << 7, - - RFCOMM_CHANNEL_STATE_VAR_SEND_RPN_RSP = 1 << 8, - RFCOMM_CHANNEL_STATE_VAR_SEND_UA = 1 << 9, - RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_CMD = 1 << 10, - RFCOMM_CHANNEL_STATE_VAR_SEND_MSC_RSP = 1 << 11, - - RFCOMM_CHANNEL_STATE_VAR_SEND_CREDITS = 1 << 12, - RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_CMD = 1 << 13, - RFCOMM_CHANNEL_STATE_VAR_SENT_MSC_RSP = 1 << 14, - RFCOMM_CHANNEL_STATE_VAR_SENT_CREDITS = 1 << 15, -}; -typedef uint16_t RFCOMM_CHANNEL_STATE_VAR; typedef enum { CH_EVT_RCVD_SABM = 1, @@ -227,7 +201,7 @@ timer_source_t timer; int timer_active; - RFCOMM_MULTIPLEXER_STATE state; + RFCOMM_MULTIPLEXER_STATE state; uint16_t l2cap_cid; uint8_t l2cap_credits;
--- a/BTstack/run_loop_embedded.c Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/run_loop_embedded.c Thu Feb 07 14:01:01 2013 +0000 @@ -52,10 +52,10 @@ */ -#include <btstack/run_loop.h> -#include <btstack/linked_list.h> -#include <btstack/hal_tick.h> -#include <btstack/hal_cpu.h> +#include "btstack/run_loop.h" +#include "btstack/linked_list.h" +#include "btstack/hal_tick.h" +#include "btstack/hal_cpu.h" #include "run_loop_private.h" #include "debug.h"
--- a/BTstack/run_loop_private.h Tue Jun 26 14:27:45 2012 +0000 +++ b/BTstack/run_loop_private.h Thu Feb 07 14:01:01 2013 +0000 @@ -42,7 +42,7 @@ #pragma once -#include <btstack/run_loop.h> +#include "btstack/run_loop.h" #ifdef HAVE_TIME #include <sys/time.h> @@ -58,11 +58,11 @@ // internal use only typedef struct { - void (*init)(void); - void (*add_data_source)(data_source_t *dataSource); - int (*remove_data_source)(data_source_t *dataSource); - void (*add_timer)(timer_source_t *timer); - int (*remove_timer)(timer_source_t *timer); - void (*execute)(void); - void (*dump_timer)(void); + void (*init)(void); + void (*add_data_source)(data_source_t *dataSource); + int (*remove_data_source)(data_source_t *dataSource); + void (*add_timer)(timer_source_t *timer); + int (*remove_timer)(timer_source_t *timer); + void (*execute)(void); + void (*dump_timer)(void); } run_loop_t;
--- a/BTstack/sdp.c Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,662 +0,0 @@ -/* - * Copyright (C) 2009-2012 by Matthias Ringwald - * - * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch - * - */ - -/* - * Implementation of the Service Discovery Protocol Server - */ - -#include "sdp.h" - - -#include <stdio.h> -#include <string.h> - -#include <btstack/sdp_util.h> - -#include "hci_dump.h" -#include "l2cap.h" - -#include "debug.h" - -// max reserved ServiceRecordHandle -#define maxReservedServiceRecordHandle 0xffff - -// max SDP response -#define SDP_RESPONSE_BUFFER_SIZE (HCI_ACL_BUFFER_SIZE-HCI_ACL_HEADER_SIZE) - -static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size); - -// registered service records -static linked_list_t sdp_service_records = NULL; - -// our handles start after the reserved range -static uint32_t sdp_next_service_record_handle = maxReservedServiceRecordHandle + 2; - -static uint8_t sdp_response_buffer[SDP_RESPONSE_BUFFER_SIZE]; - -static void (*app_packet_handler)(void * connection, uint8_t packet_type, - uint16_t channel, uint8_t *packet, uint16_t size) = NULL; - -static uint16_t l2cap_cid = 0; -static uint16_t sdp_response_size = 0; - -void sdp_init(){ - // register with l2cap psm sevices - max MTU - l2cap_register_service_internal(NULL, sdp_packet_handler, PSM_SDP, 0xffff); -} - -// register packet handler -void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, - uint16_t channel, uint8_t *packet, uint16_t size)){ - app_packet_handler = handler; - l2cap_cid = 0; -} - -uint32_t sdp_get_service_record_handle(uint8_t * record){ - uint8_t * serviceRecordHandleAttribute = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle); - if (!serviceRecordHandleAttribute) return 0; - if (de_get_element_type(serviceRecordHandleAttribute) != DE_UINT) return 0; - if (de_get_size_type(serviceRecordHandleAttribute) != DE_SIZE_32) return 0; - return READ_NET_32(serviceRecordHandleAttribute, 1); -} - -// data: event(8), len(8), status(8), service_record_handle(32) -static void sdp_emit_service_registered(void *connection, uint32_t handle, uint8_t status) { - if (!app_packet_handler) return; - uint8_t event[7]; - event[0] = SDP_SERVICE_REGISTERED; - event[1] = sizeof(event) - 2; - event[2] = status; - bt_store_32(event, 3, handle); - hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event)); - (*app_packet_handler)(connection, HCI_EVENT_PACKET, 0, (uint8_t *) event, sizeof(event)); -} - -service_record_item_t * sdp_get_record_for_handle(uint32_t handle){ - linked_item_t *it; - for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){ - service_record_item_t * item = (service_record_item_t *) it; - if (item->service_record_handle == handle){ - return item; - } - } - return NULL; -} - -// get next free, unregistered service record handle -uint32_t sdp_create_service_record_handle(void){ - uint32_t handle = 0; - do { - handle = sdp_next_service_record_handle++; - if (sdp_get_record_for_handle(handle)) handle = 0; - } while (handle == 0); - return handle; -} - -#ifdef EMBEDDED - -// register service record internally - this special version doesn't copy the record, it should not be freeed -// pre: AttributeIDs are in ascending order -// pre: ServiceRecordHandle is first attribute and valid -// pre: record -// @returns ServiceRecordHandle or 0 if registration failed -uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item){ - // get user record handle - uint32_t record_handle = record_item->service_record_handle; - // get actual record - uint8_t *record = record_item->service_record; - - // check for ServiceRecordHandle attribute, returns pointer or null - uint8_t * req_record_handle = sdp_get_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle); - if (!req_record_handle) { - log_error("SDP Error - record does not contain ServiceRecordHandle attribute\n"); - return 0; - } - - // validate service record handle is not in reserved range - if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0; - - // check if already in use - if (record_handle) { - if (sdp_get_record_for_handle(record_handle)) { - record_handle = 0; - } - } - - // create new handle if needed - if (!record_handle){ - record_handle = sdp_create_service_record_handle(); - // Write the handle back into the record too - record_item->service_record_handle = record_handle; - sdp_set_attribute_value_for_attribute_id(record, SDP_ServiceRecordHandle, record_handle); - } - - // add to linked list - linked_list_add(&sdp_service_records, (linked_item_t *) record_item); - - sdp_emit_service_registered(connection, 0, record_item->service_record_handle); - - return record_handle; -} - -#else - -// AttributeIDList used to remove ServiceRecordHandle -static const uint8_t removeServiceRecordHandleAttributeIDList[] = { 0x36, 0x00, 0x05, 0x0A, 0x00, 0x01, 0xFF, 0xFF }; - -// register service record internally - the normal version creates a copy of the record -// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present -// @returns ServiceRecordHandle or 0 if registration failed -uint32_t sdp_register_service_internal(void *connection, uint8_t * record){ - - // dump for now - // printf("Register service record\n"); - // de_dump_data_element(record); - - // get user record handle - uint32_t record_handle = sdp_get_service_record_handle(record); - - // validate service record handle is not in reserved range - if (record_handle <= maxReservedServiceRecordHandle) record_handle = 0; - - // check if already in use - if (record_handle) { - if (sdp_get_record_for_handle(record_handle)) { - record_handle = 0; - } - } - - // create new handle if needed - if (!record_handle){ - record_handle = sdp_create_service_record_handle(); - } - - // calculate size of new service record: DES (2 byte len) - // + ServiceRecordHandle attribute (UINT16 UINT32) + size of existing attributes - uint16_t recordSize = 3 + (3 + 5) + de_get_data_size(record); - - // alloc memory for new service_record_item - service_record_item_t * newRecordItem = (service_record_item_t *) malloc(recordSize + sizeof(service_record_item_t)); - if (!newRecordItem) { - sdp_emit_service_registered(connection, 0, BTSTACK_MEMORY_ALLOC_FAILED); - return 0; - } - // link new service item to client connection - newRecordItem->connection = connection; - - // set new handle - newRecordItem->service_record_handle = record_handle; - - // create updated service record - uint8_t * newRecord = (uint8_t *) &(newRecordItem->service_record); - - // create DES for new record - de_create_sequence(newRecord); - - // set service record handle - de_add_number(newRecord, DE_UINT, DE_SIZE_16, 0); - de_add_number(newRecord, DE_UINT, DE_SIZE_32, record_handle); - - // add other attributes - sdp_append_attributes_in_attributeIDList(record, (uint8_t *) removeServiceRecordHandleAttributeIDList, 0, recordSize, newRecord); - - // dump for now - // de_dump_data_element(newRecord); - // printf("reserved size %u, actual size %u\n", recordSize, de_get_len(newRecord)); - - // add to linked list - linked_list_add(&sdp_service_records, (linked_item_t *) newRecordItem); - - sdp_emit_service_registered(connection, 0, newRecordItem->service_record_handle); - - return record_handle; -} - -#endif - -// unregister service record internally -// -// makes sure one client cannot remove service records of other clients -// -void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle){ - service_record_item_t * record_item = sdp_get_record_for_handle(service_record_handle); - if (record_item && record_item->connection == connection) { - linked_list_remove(&sdp_service_records, (linked_item_t *) record_item); - } -} - -// remove all service record for a client connection -void sdp_unregister_services_for_connection(void *connection){ - linked_item_t *it = (linked_item_t *) &sdp_service_records; - while (it->next){ - service_record_item_t *record_item = (service_record_item_t *) it->next; - if (record_item->connection == connection){ - it->next = it->next->next; -#ifndef EMBEDDED - free(record_item); -#endif - } else { - it = it->next; - } - } -} - -// PDU -// PDU ID (1), Transaction ID (2), Param Length (2), Param 1, Param 2, .. - -int sdp_create_error_response(uint16_t transaction_id, uint16_t error_code){ - sdp_response_buffer[0] = SDP_ErrorResponse; - net_store_16(sdp_response_buffer, 1, transaction_id); - net_store_16(sdp_response_buffer, 3, 2); - net_store_16(sdp_response_buffer, 5, error_code); // invalid syntax - return 7; -} - -int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu){ - - // get request details - uint16_t transaction_id = READ_NET_16(packet, 1); - // not used yet - uint16_t param_len = READ_NET_16(packet, 3); - uint8_t * serviceSearchPattern = &packet[5]; - uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); - uint16_t maximumServiceRecordCount = READ_NET_16(packet, 5 + serviceSearchPatternLen); - uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2]; - - // calc maxumumServiceRecordCount based on remote MTU - uint16_t maxNrServiceRecordsPerResponse = (remote_mtu - (9+3))/4; - - // continuation state contains index of next service record to examine - int continuation = 0; - uint16_t continuation_index = 0; - if (continuationState[0] == 2){ - continuation_index = READ_NET_16(continuationState, 1); - } - - // get and limit total count - linked_item_t *it; - uint16_t total_service_count = 0; - for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){ - service_record_item_t * item = (service_record_item_t *) it; - if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; - total_service_count++; - } - if (total_service_count > maximumServiceRecordCount){ - total_service_count = maximumServiceRecordCount; - } - - // ServiceRecordHandleList at 9 - uint16_t pos = 9; - uint16_t current_service_count = 0; - uint16_t current_service_index = 0; - uint16_t matching_service_count = 0; - for (it = (linked_item_t *) sdp_service_records; it ; it = it->next, ++current_service_index){ - service_record_item_t * item = (service_record_item_t *) it; - - if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; - matching_service_count++; - - if (current_service_index < continuation_index) continue; - - net_store_32(sdp_response_buffer, pos, item->service_record_handle); - pos += 4; - current_service_count++; - - if (matching_service_count >= total_service_count) break; - - if (current_service_count >= maxNrServiceRecordsPerResponse){ - continuation = 1; - continuation_index = current_service_index + 1; - break; - } - } - - // Store continuation state - if (continuation) { - sdp_response_buffer[pos++] = 2; - net_store_16(sdp_response_buffer, pos, continuation_index); - pos += 2; - } else { - sdp_response_buffer[pos++] = 0; - } - - // header - sdp_response_buffer[0] = SDP_ServiceSearchResponse; - net_store_16(sdp_response_buffer, 1, transaction_id); - net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload - net_store_16(sdp_response_buffer, 5, total_service_count); - net_store_16(sdp_response_buffer, 7, current_service_count); - - return pos; -} - -int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu){ - - // get request details - uint16_t transaction_id = READ_NET_16(packet, 1); - // not used yet - uint16_t param_len = READ_NET_16(packet, 3); - uint32_t serviceRecordHandle = READ_NET_32(packet, 5); - uint16_t maximumAttributeByteCount = READ_NET_16(packet, 9); - uint8_t * attributeIDList = &packet[11]; - uint16_t attributeIDListLen = de_get_len(attributeIDList); - uint8_t * continuationState = &packet[11+attributeIDListLen]; - - // calc maximumAttributeByteCount based on remote MTU - uint16_t maximumAttributeByteCount2 = remote_mtu - (7+3); - if (maximumAttributeByteCount2 < maximumAttributeByteCount) { - maximumAttributeByteCount = maximumAttributeByteCount2; - } - - // continuation state contains the offset into the complete response - uint16_t continuation_offset = 0; - if (continuationState[0] == 2){ - continuation_offset = READ_NET_16(continuationState, 1); - } - - // get service record - service_record_item_t * item = sdp_get_record_for_handle(serviceRecordHandle); - if (!item){ - // service record handle doesn't exist - return sdp_create_error_response(transaction_id, 0x0002); /// invalid Service Record Handle - } - - - // AttributeList - starts at offset 7 - uint16_t pos = 7; - - if (continuation_offset == 0){ - - // get size of this record - uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList); - - // store DES - de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); - maximumAttributeByteCount -= 3; - pos += 3; - } - - // copy maximumAttributeByteCount from record - uint16_t bytes_used; - int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); - pos += bytes_used; - - uint16_t attributeListByteCount = pos - 7; - - if (complete) { - sdp_response_buffer[pos++] = 0; - } else { - continuation_offset += bytes_used; - sdp_response_buffer[pos++] = 2; - net_store_16(sdp_response_buffer, pos, continuation_offset); - pos += 2; - } - - // header - sdp_response_buffer[0] = SDP_ServiceAttributeResponse; - net_store_16(sdp_response_buffer, 1, transaction_id); - net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload - net_store_16(sdp_response_buffer, 5, attributeListByteCount); - - return pos; -} - -static uint16_t sdp_get_size_for_service_search_attribute_response(uint8_t * serviceSearchPattern, uint8_t * attributeIDList){ - uint16_t total_response_size = 0; - linked_item_t *it; - for (it = (linked_item_t *) sdp_service_records; it ; it = it->next){ - service_record_item_t * item = (service_record_item_t *) it; - - if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; - - // for all service records that match - total_response_size += 3 + spd_get_filtered_size(item->service_record, attributeIDList); - } - return total_response_size; -} - -int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu){ - - // SDP header before attribute sevice list: 7 - // Continuation, worst case: 5 - - // get request details - uint16_t transaction_id = READ_NET_16(packet, 1); - // not used yet - uint16_t param_len = READ_NET_16(packet, 3); - uint8_t * serviceSearchPattern = &packet[5]; - uint16_t serviceSearchPatternLen = de_get_len(serviceSearchPattern); - uint16_t maximumAttributeByteCount = READ_NET_16(packet, 5 + serviceSearchPatternLen); - uint8_t * attributeIDList = &packet[5+serviceSearchPatternLen+2]; - uint16_t attributeIDListLen = de_get_len(attributeIDList); - uint8_t * continuationState = &packet[5+serviceSearchPatternLen+2+attributeIDListLen]; - - // calc maximumAttributeByteCount based on remote MTU, SDP header and reserved Continuation block - uint16_t maximumAttributeByteCount2 = remote_mtu - 12; - if (maximumAttributeByteCount2 < maximumAttributeByteCount) { - maximumAttributeByteCount = maximumAttributeByteCount2; - } - - // continuation state contains: index of next service record to examine - // continuation state contains: byte offset into this service record - uint16_t continuation_service_index = 0; - uint16_t continuation_offset = 0; - if (continuationState[0] == 4){ - continuation_service_index = READ_NET_16(continuationState, 1); - continuation_offset = READ_NET_16(continuationState, 3); - } - - // printf("--> sdp_handle_service_search_attribute_request, cont %u/%u, max %u\n", continuation_service_index, continuation_offset, maximumAttributeByteCount); - - // AttributeLists - starts at offset 7 - uint16_t pos = 7; - - // add DES with total size for first request - if (continuation_service_index == 0 && continuation_offset == 0){ - uint16_t total_response_size = sdp_get_size_for_service_search_attribute_response(serviceSearchPattern, attributeIDList); - de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, total_response_size); - // log_info("total response size %u\n", total_response_size); - pos += 3; - maximumAttributeByteCount -= 3; - } - - // create attribute list - int first_answer = 1; - int continuation = 0; - uint16_t current_service_index = 0; - linked_item_t *it = (linked_item_t *) sdp_service_records; - for ( ; it ; it = it->next, ++current_service_index){ - service_record_item_t * item = (service_record_item_t *) it; - - if (current_service_index < continuation_service_index ) continue; - if (!sdp_record_matches_service_search_pattern(item->service_record, serviceSearchPattern)) continue; - - if (continuation_offset == 0){ - - // get size of this record - uint16_t filtered_attributes_size = spd_get_filtered_size(item->service_record, attributeIDList); - - // stop if complete record doesn't fits into response but we already have a partial response - if ((filtered_attributes_size + 3 > maximumAttributeByteCount) && !first_answer) { - continuation = 1; - break; - } - - // store DES - de_store_descriptor_with_len(&sdp_response_buffer[pos], DE_DES, DE_SIZE_VAR_16, filtered_attributes_size); - pos += 3; - maximumAttributeByteCount -= 3; - } - - first_answer = 0; - - // copy maximumAttributeByteCount from record - uint16_t bytes_used; - int complete = sdp_filter_attributes_in_attributeIDList(item->service_record, attributeIDList, continuation_offset, maximumAttributeByteCount, &bytes_used, &sdp_response_buffer[pos]); - pos += bytes_used; - maximumAttributeByteCount -= bytes_used; - - if (complete) { - continuation_offset = 0; - continue; - } - - continuation = 1; - continuation_offset += bytes_used; - break; - } - - uint16_t attributeListsByteCount = pos - 7; - - // Continuation State - if (continuation){ - sdp_response_buffer[pos++] = 4; - net_store_16(sdp_response_buffer, pos, (uint16_t) current_service_index); - pos += 2; - net_store_16(sdp_response_buffer, pos, continuation_offset); - pos += 2; - } else { - // complete - sdp_response_buffer[pos++] = 0; - } - - // create SDP header - sdp_response_buffer[0] = SDP_ServiceSearchAttributeResponse; - net_store_16(sdp_response_buffer, 1, transaction_id); - net_store_16(sdp_response_buffer, 3, pos - 5); // size of variable payload - net_store_16(sdp_response_buffer, 5, attributeListsByteCount); - - return pos; -} - -static void sdp_try_respond(void){ - if (!sdp_response_size ) return; - if (!l2cap_cid) return; - if (!l2cap_can_send_packet_now(l2cap_cid)) return; - - // update state before sending packet (avoid getting called when new l2cap credit gets emitted) - uint16_t size = sdp_response_size; - sdp_response_size = 0; - l2cap_send_internal(l2cap_cid, sdp_response_buffer, size); -} - -// we assume that we don't get two requests in a row -static void sdp_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - uint16_t transaction_id; - SDP_PDU_ID_t pdu_id; - uint16_t remote_mtu; - // uint16_t param_len; - - switch (packet_type) { - - case L2CAP_DATA_PACKET: - pdu_id = (SDP_PDU_ID_t) packet[0]; - transaction_id = READ_NET_16(packet, 1); - // param_len = READ_NET_16(packet, 3); - remote_mtu = l2cap_get_remote_mtu_for_local_cid(channel); - // account for our buffer - if (remote_mtu > SDP_RESPONSE_BUFFER_SIZE){ - remote_mtu = SDP_RESPONSE_BUFFER_SIZE; - } - - // printf("SDP Request: type %u, transaction id %u, len %u, mtu %u\n", pdu_id, transaction_id, param_len, remote_mtu); - switch (pdu_id){ - - case SDP_ServiceSearchRequest: - sdp_response_size = sdp_handle_service_search_request(packet, remote_mtu); - break; - - case SDP_ServiceAttributeRequest: - sdp_response_size = sdp_handle_service_attribute_request(packet, remote_mtu); - break; - - case SDP_ServiceSearchAttributeRequest: - sdp_response_size = sdp_handle_service_search_attribute_request(packet, remote_mtu); - break; - - default: - sdp_response_size = sdp_create_error_response(transaction_id, 0x0003); // invalid syntax - break; - } - - sdp_try_respond(); - - break; - - case HCI_EVENT_PACKET: - - switch (packet[0]) { - - case L2CAP_EVENT_INCOMING_CONNECTION: - if (l2cap_cid) { - // CONNECTION REJECTED DUE TO LIMITED RESOURCES - l2cap_decline_connection_internal(channel, 0x0d); - break; - } - // accept - l2cap_cid = channel; - sdp_response_size = 0; - l2cap_accept_connection_internal(channel); - break; - - case L2CAP_EVENT_CHANNEL_OPENED: - if (packet[2]) { - // open failed -> reset - l2cap_cid = 0; - } - break; - - case L2CAP_EVENT_CREDITS: - case DAEMON_EVENT_HCI_PACKET_SENT: - sdp_try_respond(); - break; - - case L2CAP_EVENT_CHANNEL_CLOSED: - if (channel == l2cap_cid){ - // reset - l2cap_cid = 0; - } - break; - - default: - // other event - break; - } - break; - - default: - // other packet type - break; - } -} -
--- a/BTstack/sdp.h Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,96 +0,0 @@ -/* - * Copyright (C) 2009-2012 by Matthias Ringwald - * - * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch - * - */ -#pragma once - -#include <stdint.h> -#include <btstack/linked_list.h> - -#include "config.h" - -typedef enum { - SDP_ErrorResponse = 1, - SDP_ServiceSearchRequest, - SDP_ServiceSearchResponse, - SDP_ServiceAttributeRequest, - SDP_ServiceAttributeResponse, - SDP_ServiceSearchAttributeRequest, - SDP_ServiceSearchAttributeResponse -} SDP_PDU_ID_t; - -// service record -// -- uses user_data field for actual -typedef struct { - // linked list - assert: first field - linked_item_t item; - - // client connection - void * connection; - - // data is contained in same memory - uint32_t service_record_handle; - uint8_t service_record[0]; -} service_record_item_t; - - -void sdp_init(void); - -void sdp_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, - uint16_t channel, uint8_t *packet, uint16_t size)); - -#ifdef EMBEDDED -// register service record internally - the normal version creates a copy of the record -// pre: AttributeIDs are in ascending order => ServiceRecordHandle is first attribute if present -// @returns ServiceRecordHandle or 0 if registration failed -uint32_t sdp_register_service_internal(void *connection, service_record_item_t * record_item); -#else -// register service record internally - this special version doesn't copy the record, it cannot be freeed -// pre: AttributeIDs are in ascending order -// pre: ServiceRecordHandle is first attribute and valid -// pre: record -// @returns ServiceRecordHandle or 0 if registration failed -uint32_t sdp_register_service_internal(void *connection, uint8_t * service_record); -#endif - -// unregister service record internally -void sdp_unregister_service_internal(void *connection, uint32_t service_record_handle); - -// -void sdp_unregister_services_for_connection(void *connection); - -// -int sdp_handle_service_search_request(uint8_t * packet, uint16_t remote_mtu); -int sdp_handle_service_attribute_request(uint8_t * packet, uint16_t remote_mtu); -int sdp_handle_service_search_attribute_request(uint8_t * packet, uint16_t remote_mtu);
--- a/BTstack/sdp_util.c Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,698 +0,0 @@ -/* - * Copyright (C) 2009-2012 by Matthias Ringwald - * - * 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 MATTHIAS RINGWALD 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 btstack@ringwald.ch - * - */ - -/* - * sdp_util.c - */ - -#include <btstack/sdp_util.h> -#include <btstack/utils.h> - -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <stdint.h> -#include <inttypes.h> // PRIx32 - -// workaround for missing PRIx32 on mspgcc (16-bit MCU) -#ifndef PRIx32 -#warning Using own: #define PRIx32 "lx" -#define PRIx32 "lx" -#endif - -// date element type names -const char * const type_names[] = { "NIL", "UINT", "INT", "UUID", "STRING", "BOOL", "DES", "DEA", "URL"}; - -// Bluetooth Base UUID: 00000000-0000-1000-8000- 00805F9B34FB -const uint8_t sdp_bluetooth_base_uuid[] = { 0x00, 0x00, 0x00, 0x00, /* - */ 0x00, 0x00, /* - */ 0x10, 0x00, /* - */ - 0x80, 0x00, /* - */ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB }; - -void sdp_normalize_uuid(uint8_t *uuid, uint32_t shortUUID){ - memcpy(uuid, sdp_bluetooth_base_uuid, 16); - net_store_32(uuid, 0, shortUUID); -} - -// MARK: DataElement getter -de_size_t de_get_size_type(uint8_t *header){ - return (de_size_t) (header[0] & 7); -} - -de_type_t de_get_element_type(uint8_t *header){ - return (de_type_t) (header[0] >> 3); -} - -int de_get_header_size(uint8_t * header){ - de_size_t de_size = de_get_size_type(header); - if (de_size <= DE_SIZE_128) { - return 1; - } - return 1 + (1 << (de_size-DE_SIZE_VAR_8)); -} - -int de_get_data_size(uint8_t * header){ - uint32_t result = 0; - de_type_t de_type = de_get_element_type(header); - de_size_t de_size = de_get_size_type(header); - switch (de_size){ - case DE_SIZE_VAR_8: - result = header[1]; - break; - case DE_SIZE_VAR_16: - result = READ_NET_16(header,1); - break; - case DE_SIZE_VAR_32: - result = READ_NET_32(header,1); - break; - default: - // case DE_SIZE_8: - // case DE_SIZE_16: - // case DE_SIZE_32: - // case DE_SIZE_64: - // case DE_SIZE_128: - if (de_type == DE_NIL) return 0; - return 1 << de_size; - } - return result; -} - -int de_get_len(uint8_t *header){ - return de_get_header_size(header) + de_get_data_size(header); -} - -// @returns: element is valid UUID -int de_get_normalized_uuid(uint8_t *uuid128, uint8_t *element){ - de_type_t uuidType = de_get_element_type(element); - de_size_t uuidSize = de_get_size_type(element); - if (uuidType != DE_UUID) return 0; - uint32_t shortUUID; - switch (uuidSize){ - case DE_SIZE_16: - shortUUID = READ_NET_16(element, 1); - break; - case DE_SIZE_32: - shortUUID = READ_NET_32(element, 1); - break; - case DE_SIZE_128: - memcpy(uuid128, element+1, 16); - return 1; - default: - return 0; - } - sdp_normalize_uuid(uuid128, shortUUID); - return 1; -} - -// functions to create record -static void de_store_descriptor(uint8_t * header, de_type_t type, de_size_t size){ - header[0] = (type << 3) | size; -} - -void de_store_descriptor_with_len(uint8_t * header, de_type_t type, de_size_t size, uint32_t len){ - header[0] = (type << 3) | size; - switch (size){ - case DE_SIZE_VAR_8: - header[1] = len; - break; - case DE_SIZE_VAR_16: - net_store_16(header, 1, len); - break; - case DE_SIZE_VAR_32: - net_store_32(header, 1, len); - break; - default: - break; - } -} - -// MARK: DataElement creation - -/* starts a new sequence in empty buffer - first call */ -void de_create_sequence(uint8_t *header){ - de_store_descriptor_with_len( header, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length -}; - -/* starts a sub-sequence, @returns handle for sub-sequence */ -uint8_t * de_push_sequence(uint8_t *header){ - int element_len = de_get_len(header); - de_store_descriptor_with_len(header+element_len, DE_DES, DE_SIZE_VAR_16, 0); // DES, 2 Byte Length - return header + element_len; -} - -/* closes the current sequence and updates the parent sequence */ -void de_pop_sequence(uint8_t * parent, uint8_t * child){ - int child_len = de_get_len(child); - int data_size_parent = READ_NET_16(parent,1); - net_store_16(parent, 1, data_size_parent + child_len); -} - -/* adds a single number value and 16+32 bit UUID to the sequence */ -void de_add_number(uint8_t *seq, de_type_t type, de_size_t size, uint32_t value){ - int data_size = READ_NET_16(seq,1); - int element_size = 1; // e.g. for DE_TYPE_NIL - de_store_descriptor(seq+3+data_size, type, size); - switch (size){ - case DE_SIZE_8: - if (type != DE_NIL){ - seq[4+data_size] = value; - element_size = 2; - } - break; - case DE_SIZE_16: - net_store_16(seq, 4+data_size, value); - element_size = 3; - break; - case DE_SIZE_32: - net_store_32(seq, 4+data_size, value); - element_size = 5; - break; - default: - break; - } - net_store_16(seq, 1, data_size+element_size); -} - -/* add a single block of data, e.g. as DE_STRING, DE_URL */ -void de_add_data( uint8_t *seq, de_type_t type, uint16_t size, uint8_t *data){ - int data_size = READ_NET_16(seq,1); - if (size > 0xff) { - // use 16-bit lengh information (3 byte header) - de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_16, size); - data_size += 3; - } else { - // use 8-bit lengh information (2 byte header) - de_store_descriptor_with_len(seq+3+data_size, type, DE_SIZE_VAR_8, size); - data_size += 2; - } - memcpy( seq + 3 + data_size, data, size); - data_size += size; - net_store_16(seq, 1, data_size); -} - -void de_add_uuid128(uint8_t * seq, uint8_t * uuid){ - int data_size = READ_NET_16(seq,1); - de_store_descriptor(seq+3+data_size, DE_UUID, DE_SIZE_128); - memcpy( seq + 4 + data_size, uuid, 16); - net_store_16(seq, 1, data_size+1+16); -} - -void sdp_add_attribute(uint8_t *seq, uint16_t attributeID, uint8_t attributeValue){ -} - -// MARK: DataElementSequence traversal -typedef int (*de_traversal_callback_t)(uint8_t * element, de_type_t type, de_size_t size, void *context); -static void de_traverse_sequence(uint8_t * element, de_traversal_callback_t handler, void *context){ - de_type_t type = de_get_element_type(element); - if (type != DE_DES) return; - int pos = de_get_header_size(element); - int end_pos = de_get_len(element); - while (pos < end_pos){ - de_type_t elemType = de_get_element_type(element + pos); - de_size_t elemSize = de_get_size_type(element + pos); - uint8_t done = (*handler)(element + pos, elemType, elemSize, context); - if (done) break; - pos += de_get_len(element + pos); - } -} - -// MARK: AttributeList traversal -typedef int (*sdp_attribute_list_traversal_callback_t)(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *context); -static void sdp_attribute_list_traverse_sequence(uint8_t * element, sdp_attribute_list_traversal_callback_t handler, void *context){ - de_type_t type = de_get_element_type(element); - if (type != DE_DES) return; - int pos = de_get_header_size(element); - int end_pos = de_get_len(element); - while (pos < end_pos){ - de_type_t idType = de_get_element_type(element + pos); - de_size_t idSize = de_get_size_type(element + pos); - if (idType != DE_UINT || idSize != DE_SIZE_16) break; // wrong type - uint16_t attribute_id = READ_NET_16(element, pos + 1); - pos += 3; - if (pos >= end_pos) break; // array out of bounds - de_type_t valueType = de_get_element_type(element + pos); - de_size_t valueSize = de_get_size_type(element + pos); - uint8_t done = (*handler)(attribute_id, element + pos, valueType, valueSize, context); - if (done) break; - pos += de_get_len(element + pos); - } -} - -// MARK: AttributeID in AttributeIDList -// attribute ID in AttributeIDList -// context { result, attributeID } -struct sdp_context_attributeID_search { - int result; - uint16_t attributeID; -}; -static int sdp_traversal_attributeID_search(uint8_t * element, de_type_t type, de_size_t size, void *my_context){ - struct sdp_context_attributeID_search * context = (struct sdp_context_attributeID_search *) my_context; - if (type != DE_UINT) return 0; - switch (size) { - case DE_SIZE_16: - if (READ_NET_16(element, 1) == context->attributeID) { - context->result = 1; - return 1; - } - break; - case DE_SIZE_32: - if (READ_NET_16(element, 1) <= context->attributeID - && context->attributeID <= READ_NET_16(element, 3)) { - context->result = 1; - return 1; - } - break; - default: - break; - } - return 0; -} -int sdp_attribute_list_constains_id(uint8_t *attributeIDList, uint16_t attributeID){ - struct sdp_context_attributeID_search attributeID_search; - attributeID_search.result = 0; - attributeID_search.attributeID = attributeID; - de_traverse_sequence(attributeIDList, sdp_traversal_attributeID_search, &attributeID_search); - return attributeID_search.result; -} - -// MARK: Append Attributes for AttributeIDList -// pre: buffer contains DES with 2 byte length field -struct sdp_context_append_attributes { - uint8_t * buffer; - uint16_t startOffset; // offset of when to start copying - uint16_t maxBytes; - uint16_t usedBytes; - uint8_t *attributeIDList; -}; - -static int sdp_traversal_append_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){ - struct sdp_context_append_attributes * context = (struct sdp_context_append_attributes *) my_context; - if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) { - // DES_HEADER(3) + DES_DATA + (UINT16(3) + attribute) - uint16_t data_size = READ_NET_16(context->buffer, 1); - int attribute_len = de_get_len(attributeValue); - if (3 + data_size + (3 + attribute_len) <= context->maxBytes) { - // copy Attribute - de_add_number(context->buffer, DE_UINT, DE_SIZE_16, attributeID); - data_size += 3; // 3 bytes - memcpy(context->buffer + 3 + data_size, attributeValue, attribute_len); - net_store_16(context->buffer,1,data_size+attribute_len); - } else { - // not enought space left -> continue with previous element - return 1; - } - } - return 0; -} - -// maxBytes: maximal size of data element sequence -uint16_t sdp_append_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint8_t *buffer){ - struct sdp_context_append_attributes context; - context.buffer = buffer; - context.maxBytes = maxBytes; - context.usedBytes = 0; - context.startOffset = startOffset; - context.attributeIDList = attributeIDList; - sdp_attribute_list_traverse_sequence(record, sdp_traversal_append_attributes, &context); - return context.usedBytes; -} - -// MARK: Filter attributes that match attribute list from startOffset and a max nr bytes -struct sdp_context_filter_attributes { - uint8_t * buffer; - uint16_t startOffset; // offset of when to start copying - uint16_t maxBytes; - uint16_t usedBytes; - uint8_t *attributeIDList; - int complete; -}; - -// copy data with given start offset and max bytes, returns OK if all data has been copied -static int spd_append_range(struct sdp_context_filter_attributes* context, uint16_t len, uint8_t *data){ - int ok = 1; - uint16_t remainder_len = len - context->startOffset; - if (context->maxBytes < remainder_len){ - remainder_len = context->maxBytes; - ok = 0; - } - memcpy(context->buffer, &data[context->startOffset], remainder_len); - context->usedBytes += remainder_len; - context->buffer += remainder_len; - context->maxBytes -= remainder_len; - context->startOffset = 0; - return ok; -} - -static int sdp_traversal_filter_attributes(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){ - struct sdp_context_filter_attributes * context = (struct sdp_context_filter_attributes *) my_context; - - if (!sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) return 0; - - // { Attribute ID (Descriptor, big endian 16-bit ID), AttributeValue (data)} - - // handle Attribute ID - if (context->startOffset >= 3){ - context->startOffset -= 3; - } else { - uint8_t idBuffer[3]; - de_store_descriptor(idBuffer, DE_UINT, DE_SIZE_16); - net_store_16(idBuffer,1,attributeID); - - int ok = spd_append_range(context, 3, idBuffer); - if (!ok) { - context->complete = 0; - return 1; - } - } - - // handle Attribute Value - int attribute_len = de_get_len(attributeValue); - if (context->startOffset >= attribute_len) { - context->startOffset -= attribute_len; - return 0; - } - - int ok = spd_append_range(context, attribute_len, attributeValue); - if (!ok) { - context->complete = 0; - return 1; - } - return 0; -} - -int sdp_filter_attributes_in_attributeIDList(uint8_t *record, uint8_t *attributeIDList, uint16_t startOffset, uint16_t maxBytes, uint16_t *usedBytes, uint8_t *buffer){ - - struct sdp_context_filter_attributes context; - context.buffer = buffer; - context.maxBytes = maxBytes; - context.usedBytes = 0; - context.startOffset = startOffset; - context.attributeIDList = attributeIDList; - context.complete = 1; - - sdp_attribute_list_traverse_sequence(record, sdp_traversal_filter_attributes, &context); - - *usedBytes = context.usedBytes; - return context.complete; -} - -// MARK: Get sum of attributes matching attribute list -struct sdp_context_get_filtered_size { - uint8_t *attributeIDList; - uint16_t size; -}; - -static int sdp_traversal_get_filtered_size(uint16_t attributeID, uint8_t * attributeValue, de_type_t type, de_size_t size, void *my_context){ - struct sdp_context_get_filtered_size * context = (struct sdp_context_get_filtered_size *) my_context; - if (sdp_attribute_list_constains_id(context->attributeIDList, attributeID)) { - context->size += 3 + de_get_len(attributeValue); - } - return 0; -} - -int spd_get_filtered_size(uint8_t *record, uint8_t *attributeIDList){ - struct sdp_context_get_filtered_size context; - context.size = 0; - context.attributeIDList = attributeIDList; - sdp_attribute_list_traverse_sequence(record, sdp_traversal_get_filtered_size, &context); - return context.size; -} - -// MARK: Get AttributeValue for AttributeID -// find attribute (ELEMENT) by ID -struct sdp_context_attribute_by_id { - uint16_t attributeID; - uint8_t * attributeValue; -}; -static int sdp_traversal_attribute_by_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){ - struct sdp_context_attribute_by_id * context = (struct sdp_context_attribute_by_id *) my_context; - if (attributeID == context->attributeID) { - context->attributeValue = attributeValue; - return 1; - } - return 0; -} - -uint8_t * sdp_get_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID){ - struct sdp_context_attribute_by_id context; - context.attributeValue = NULL; - context.attributeID = attributeID; - sdp_attribute_list_traverse_sequence(record, sdp_traversal_attribute_by_id, &context); - return context.attributeValue; -} - -// MARK: Set AttributeValue for AttributeID -struct sdp_context_set_attribute_for_id { - uint16_t attributeID; - uint32_t attributeValue; - uint8_t attributeFound; -}; -static int sdp_traversal_set_attribute_for_id(uint16_t attributeID, uint8_t * attributeValue, de_type_t attributeType, de_size_t size, void *my_context){ - struct sdp_context_set_attribute_for_id * context = (struct sdp_context_set_attribute_for_id *) my_context; - if (attributeID == context->attributeID) { - context->attributeFound = 1; - switch (size){ - case DE_SIZE_8: - if (attributeType != DE_NIL){ - attributeValue[1] = context->attributeValue; - } - break; - case DE_SIZE_16: - net_store_16(attributeValue, 1, context->attributeValue); - break; - case DE_SIZE_32: - net_store_32(attributeValue, 1, context->attributeValue); - break; - // Might want to support STRINGS to, copy upto original length - default: - break; - } - return 1; - } - return 0; -} -uint8_t sdp_set_attribute_value_for_attribute_id(uint8_t * record, uint16_t attributeID, uint32_t value){ - struct sdp_context_set_attribute_for_id context; - context.attributeID = attributeID; - context.attributeValue = value; - context.attributeFound = 0; - sdp_attribute_list_traverse_sequence(record, sdp_traversal_set_attribute_for_id, &context); - return context.attributeFound; -} - -// MARK: ServiceRecord contains UUID -// service record contains UUID -// context { normalizedUUID } -struct sdp_context_contains_uuid128 { - uint8_t * uuid128; - int result; -}; -int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128); -static int sdp_traversal_contains_UUID128(uint8_t * element, de_type_t type, de_size_t size, void *my_context){ - struct sdp_context_contains_uuid128 * context = (struct sdp_context_contains_uuid128 *) my_context; - uint8_t normalizedUUID[16]; - if (type == DE_UUID){ - uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element); - context->result = uuidOK && memcmp(context->uuid128, normalizedUUID, 16) == 0; - } - if (type == DE_DES){ - context->result = sdp_record_contains_UUID128(element, context->uuid128); - } - return context->result; -} -int sdp_record_contains_UUID128(uint8_t *record, uint8_t *uuid128){ - struct sdp_context_contains_uuid128 context; - context.uuid128 = uuid128; - context.result = 0; - de_traverse_sequence(record, sdp_traversal_contains_UUID128, &context); - return context.result; -} - -// MARK: ServiceRecord matches SearchServicePattern -// if UUID in searchServicePattern is not found in record => false -// context { result, record } -struct sdp_context_match_pattern { - uint8_t * record; - int result; -}; -int sdp_traversal_match_pattern(uint8_t * element, de_type_t attributeType, de_size_t size, void *my_context){ - struct sdp_context_match_pattern * context = (struct sdp_context_match_pattern *) my_context; - uint8_t normalizedUUID[16]; - uint8_t uuidOK = de_get_normalized_uuid(normalizedUUID, element); - if (!uuidOK || !sdp_record_contains_UUID128(context->record, normalizedUUID)){ - context->result = 0; - return 1; - } - return 0; -} -int sdp_record_matches_service_search_pattern(uint8_t *record, uint8_t *serviceSearchPattern){ - struct sdp_context_match_pattern context; - context.record = record; - context.result = 1; - de_traverse_sequence(serviceSearchPattern, sdp_traversal_match_pattern, &context); - return context.result; -} - -// MARK: Dump DataElement -// context { indent } -static int de_traversal_dump_data(uint8_t * element, de_type_t de_type, de_size_t de_size, void *my_context){ - int indent = *(int*) my_context; - int i; - for (i=0; i<indent;i++) printf(" "); - int pos = de_get_header_size(element); - int end_pos = de_get_len(element); - printf("type %5s (%u), element len %2u ", type_names[de_type], de_type, end_pos); - if (de_type == DE_DES) { - printf("\n"); - indent++; - de_traverse_sequence(element, de_traversal_dump_data, (void *)&indent); - } else if (de_type == DE_UUID && de_size == DE_SIZE_128) { - printf(", value: "); - printUUID(element+1); - printf("\n"); - } else if (de_type == DE_STRING) { - int len = 0; - switch (de_size){ - case DE_SIZE_VAR_8: - len = element[1]; - break; - case DE_SIZE_VAR_16: - len = READ_NET_16(element, 1); - break; - default: - break; - } - printf("len %u (0x%02x)\n", len, len); - hexdump(&element[pos], len); - } else { - uint32_t value = 0; - switch (de_size) { - case DE_SIZE_8: - if (de_type != DE_NIL){ - value = element[pos]; - } - break; - case DE_SIZE_16: - value = READ_NET_16(element,pos); - break; - case DE_SIZE_32: - value = READ_NET_32(element,pos); - break; - default: - break; - } - printf(", value: 0x%08" PRIx32 "\n", value); - } - return 0; -} - -void de_dump_data_element(uint8_t * record){ - int indent = 0; - // hack to get root DES, too. - de_type_t type = de_get_element_type(record); - de_size_t size = de_get_size_type(record); - de_traversal_dump_data(record, type, size, (void*) &indent); -} - -void sdp_create_spp_service(uint8_t *service, int service_id, const char *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, 0x10001); - - // 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, 0x1101 ); - } - 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, 0x0100); - } - de_pop_sequence(attribute, l2cpProtocol); - - uint8_t* rfcomm = de_push_sequence(attribute); - { - de_add_number(rfcomm, DE_UUID, DE_SIZE_16, 0x0003); // rfcomm_service - de_add_number(rfcomm, DE_UINT, DE_SIZE_8, service_id); // rfcomm channel - } - de_pop_sequence(attribute, rfcomm); - } - 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, 0x1002 ); - } - de_pop_sequence(service, attribute); - - // 0x0006 - de_add_number(service, DE_UINT, DE_SIZE_16, SDP_LanguageBaseAttributeIDList); - attribute = de_push_sequence(service); - { - de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x656e); - de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x006a); - de_add_number(attribute, DE_UINT, DE_SIZE_16, 0x0100); - } - 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 *sppProfile = de_push_sequence(attribute); - { - de_add_number(sppProfile, DE_UUID, DE_SIZE_16, 0x1101); - de_add_number(sppProfile, DE_UINT, DE_SIZE_16, 0x0100); - } - de_pop_sequence(attribute, sppProfile); - } - de_pop_sequence(service, attribute); - - // 0x0100 "ServiceName" - de_add_number(service, DE_UINT, DE_SIZE_16, 0x0100); - de_add_data(service, DE_STRING, strlen(name), (uint8_t *) name); -}
--- a/led_counter.cpp Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,56 +0,0 @@ -#if 0 -//***************************************************************************** -// -// led_counter demo - uses the BTstack run loop to blink an LED -// -//***************************************************************************** -#include "mbed.h" -#include <btstack/run_loop.h> - -Serial pc(USBTX, USBRX); -DigitalOut led1(LED1), led2(LED2); - -#define HEARTBEAT_PERIOD_MS 1000 - -static void heartbeat_handler(struct timer *ts){ - - // increment counter - static int counter = 0; - char lineBuffer[30]; - sprintf(lineBuffer, "BTstack counter %04u\n\r", ++counter); - printf(lineBuffer); - - // toggle LED - led2 = !led2; - - run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(ts); -} - -// main -int main(void) -{ - pc.baud(921600); - printf("%s\n", __FILE__); - - // init LEDs - led1 = led2 = 1; - - /// GET STARTED with BTstack /// - run_loop_init(RUN_LOOP_EMBEDDED); - - // set one-shot timer - timer_source_t heartbeat; - heartbeat.process = &heartbeat_handler; - run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(&heartbeat); - - printf("Run...\n\r"); - - // go! - run_loop_execute(); - - // happy compiler! - return 0; -} -#endif \ No newline at end of file
--- a/mouse_demo.cpp Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,118 +0,0 @@ -#if 0 -/* mouse_demo.cpp */ -#include "mbed.h" -#include <btstack/run_loop.h> -#include <btstack/hci_cmds.h> -#include "hci.h" -#include "l2cap.h" -#include "debug.h" -#include "bd_addr.h" // class bd_addr -Serial pc(USBTX, USBRX); -DigitalOut led1(LED1), led2(LED2), led3(LED3); - -#define INQUIRY_INTERVAL 15 - -bd_addr addr; - -static void hid_process_packet(uint8_t* report, int size) -{ - if (report[0] == 0xa1 && report[1] == 0x02) { - led1 = report[2] & 0x01; // left - led2 = report[2] & 0x02; // right - led3 = report[2] & 0x04; // center - } - hexdump(report, size); -} - -static void l2cap_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - - if (packet_type == HCI_EVENT_PACKET && packet[0] == L2CAP_EVENT_CHANNEL_OPENED){ - if (packet[2]) { - log_info("Connection failed\n"); - return; - } - log_info("Connected\n"); - } - if (packet_type == L2CAP_DATA_PACKET){ - // handle input - log_info("HID report, size %u\n", size); - hid_process_packet(packet, size); - } -} - -static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - if (packet_type == HCI_EVENT_PACKET) { - switch (packet[0]) { - case BTSTACK_EVENT_STATE: - // bt stack activated, get started - set local name - if (packet[2] == HCI_STATE_WORKING) { - hci_send_cmd(&hci_write_authentication_enable, 1); - } - break; - - case HCI_EVENT_INQUIRY_RESULT: - // ignore none mouses - if ((packet[12] & 0x80) != 0x80 || packet[13] != 0x25) break; - addr.data(&packet[3]); - log_info("Mouse addr: %s\n", addr.to_str()); - hci_send_cmd(&hci_inquiry_cancel); - break; - - case HCI_EVENT_INQUIRY_COMPLETE: - log_info("No mouse found :(\n"); - break; - - case HCI_EVENT_LINK_KEY_REQUEST: - // deny link key request - hci_send_cmd(&hci_link_key_request_negative_reply, addr.data()); - break; - - case HCI_EVENT_PIN_CODE_REQUEST: - // inform about pin code request - log_info("Enter 0000\n"); - hci_send_cmd(&hci_pin_code_request_reply, addr.data(), 4, "0000"); - break; - - case HCI_EVENT_COMMAND_COMPLETE: - if (COMMAND_COMPLETE_EVENT(packet, hci_write_authentication_enable)){ - log_info("Inquiry\n"); - hci_send_cmd(&hci_inquiry, HCI_INQUIRY_LAP, INQUIRY_INTERVAL, 0); - } - if (COMMAND_COMPLETE_EVENT(packet, hci_inquiry_cancel) ) { - // inq successfully cancelled - log_info("Connecting\n"); - l2cap_create_channel_internal(NULL, l2cap_packet_handler, addr.data(), PSM_HID_INTERRUPT, 150); - } - break; - } - } -} - -int main(void){ - pc.baud(921600); - log_info("%s\n", __FILE__); - - // init LEDs - led1 = led2 = led3 = 1; - - // GET STARTED - run_loop_init(RUN_LOOP_EMBEDDED); - - // init HCI - hci_transport_t* transport = hci_transport_usb_instance(); - remote_device_db_t* remote_db = (remote_device_db_t *)&remote_device_db_memory; - hci_init(transport, NULL, NULL, remote_db); - - // init L2CAP - l2cap_init(); - l2cap_register_packet_handler(packet_handler); - - // turn on!, send RESET command - hci_power_control(HCI_POWER_ON); - - // go! - run_loop_execute(); - - return 0; -} -#endif \ No newline at end of file
--- a/spp_counter.cpp Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,182 +0,0 @@ -#if 0 -//***************************************************************************** -// -// spp_counter demo - it provides a SPP and sends a counter every second -// -// it doesn't use the LCD to get down to a minimal memory footpring -// -//***************************************************************************** -#include "mbed.h" -#include <btstack/hci_cmds.h> -#include <btstack/run_loop.h> -#include <btstack/sdp_util.h> - -#include "hci.h" -#include "l2cap.h" -#include "btstack_memory.h" -#include "remote_device_db.h" -#include "rfcomm.h" -#include "sdp.h" -#include "config.h" -#include "debug.h" -#include "bd_addr.h" // class bd_addr - -Serial pc(USBTX, USBRX); -DigitalOut led1(LED1), led2(LED2); - -#define HEARTBEAT_PERIOD_MS 1000 - -static uint8_t rfcomm_channel_nr = 1; -static uint16_t rfcomm_channel_id = 0; -static uint8_t spp_service_buffer[128]; - -// Bluetooth logic -static void packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - bd_addr_t event_addr; - uint8_t rfcomm_channel_nr; - uint16_t mtu; - - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (packet[0]) { - - case BTSTACK_EVENT_STATE: - // bt stack activated, get started - set local name - if (packet[2] == HCI_STATE_WORKING) { - hci_send_cmd(&hci_write_local_name, "mbed-Demo"); - } - break; - - case HCI_EVENT_COMMAND_COMPLETE: - if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){ - bt_flip_addr(event_addr, &packet[6]); - log_info("BD-ADDR: %s\n\r", bd_addr_to_str(event_addr)); - break; - } - if (COMMAND_COMPLETE_EVENT(packet, hci_write_local_name)){ - hci_discoverable_control(1); - break; - } - break; - - case HCI_EVENT_LINK_KEY_REQUEST: - // deny link key request - log_info("Link key request\n\r"); - bt_flip_addr(event_addr, &packet[2]); - hci_send_cmd(&hci_link_key_request_negative_reply, &event_addr); - break; - - case HCI_EVENT_PIN_CODE_REQUEST: - // inform about pin code request - log_info("Pin code request - using '0000'\n\r"); - bt_flip_addr(event_addr, &packet[2]); - hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); - break; - - case RFCOMM_EVENT_INCOMING_CONNECTION: - // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) - bt_flip_addr(event_addr, &packet[2]); - rfcomm_channel_nr = packet[8]; - rfcomm_channel_id = READ_BT_16(packet, 9); - log_info("RFCOMM channel %u requested for %s\n\r", rfcomm_channel_nr, bd_addr_to_str(event_addr)); - rfcomm_accept_connection_internal(rfcomm_channel_id); - break; - - case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: - // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) - if (packet[2]) { - log_info("RFCOMM channel open failed, status %u\n\r", packet[2]); - } else { - rfcomm_channel_id = READ_BT_16(packet, 12); - mtu = READ_BT_16(packet, 14); - log_info("\n\rRFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n\r", rfcomm_channel_id, mtu); - } - break; - - case RFCOMM_EVENT_CHANNEL_CLOSED: - rfcomm_channel_id = 0; - break; - - default: - break; - } - break; - - default: - break; - } -} - -static void heartbeat_handler(struct timer *ts){ - - if (rfcomm_channel_id){ - static int counter = 0; - char lineBuffer[32]; - snprintf(lineBuffer, sizeof(lineBuffer), "counter %04u\n\r", ++counter); - log_info("%s\n", lineBuffer); - int err = rfcomm_send_internal(rfcomm_channel_id, (uint8_t*) lineBuffer, strlen(lineBuffer)); - if (err) { - log_info("rfcomm_send_internal -> error %d", err); - } - } - - run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(ts); - led2 = !led2; -} - -// main -int main(void) -{ - pc.baud(921600); - log_info("%s\n", __FILE__); - - - // init LEDs - led1 = led2 = 1; - - /// GET STARTED with BTstack /// - btstack_memory_init(); - run_loop_init(RUN_LOOP_EMBEDDED); - - // init HCI - hci_transport_t * transport = hci_transport_usb_instance(); - remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory; - hci_init(transport, NULL, NULL, remote_db); - - // init L2CAP - l2cap_init(); - l2cap_register_packet_handler(packet_handler); - - // init RFCOMM - rfcomm_init(); - rfcomm_register_packet_handler(packet_handler); - rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 100); // reserved channel, mtu=100 - - // init SDP, create record for SPP and register with SDP - sdp_init(); - memset(spp_service_buffer, 0, sizeof(spp_service_buffer)); - service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer; - sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, 1, "SPP Counter"); - log_info("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); - - // set one-shot timer - timer_source_t heartbeat; - heartbeat.process = &heartbeat_handler; - run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(&heartbeat); - - log_info("Run...\n\r"); - - // turn on! - hci_power_control(HCI_POWER_ON); - - // go! - run_loop_execute(); - - // happy compiler! - return 0; -} - -#endif \ No newline at end of file
--- a/spp_demo.cpp Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,172 +0,0 @@ -#if 1 -/* - * spp_demo - */ -#include "mbed.h" -#include <btstack/hci_cmds.h> -#include <btstack/run_loop.h> -#include <btstack/sdp_util.h> -#include "hci.h" -#include "l2cap.h" -#include "btstack_memory.h" -#include "remote_device_db.h" -#include "rfcomm.h" -#include "sdp.h" -#include "config.h" -#include "debug.h" -#include "bd_addr.h" // class bd_addr - -Serial pc(USBTX, USBRX); -DigitalOut led1(LED1), led2(LED2), led3(LED3); - -#define HEARTBEAT_PERIOD_MS 500 - -static uint8_t rfcomm_channel_nr = 1; -static uint16_t rfcomm_channel_id = 0; -static uint8_t spp_service_buffer[128]; - -// Bluetooth logic -static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - bd_addr_t event_addr; - uint8_t rfcomm_channel_nr; - uint16_t mtu; - int err; - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (packet[0]) { - - case BTSTACK_EVENT_STATE: - // bt stack activated, get started - set local name - if (packet[2] == HCI_STATE_WORKING) { - hci_send_cmd(&hci_write_local_name, "mbed"); - } - break; - - case HCI_EVENT_COMMAND_COMPLETE: - if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){ - bt_flip_addr(event_addr, &packet[6]); - log_info("BD-ADDR: %s\n\r", bd_addr_to_str(event_addr)); - break; - } - if (COMMAND_COMPLETE_EVENT(packet, hci_write_local_name)){ - hci_discoverable_control(1); - break; - } - break; - - case HCI_EVENT_LINK_KEY_REQUEST: - // deny link key request - log_info("Link key request\n\r"); - bt_flip_addr(event_addr, &packet[2]); - hci_send_cmd(&hci_link_key_request_negative_reply, &event_addr); - break; - - case HCI_EVENT_PIN_CODE_REQUEST: - // inform about pin code request - log_info("Pin code request - using '0000'\n\r"); - bt_flip_addr(event_addr, &packet[2]); - hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); - break; - - case RFCOMM_EVENT_INCOMING_CONNECTION: - // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) - bt_flip_addr(event_addr, &packet[2]); - rfcomm_channel_nr = packet[8]; - rfcomm_channel_id = READ_BT_16(packet, 9); - log_info("RFCOMM channel %u requested for %s\n\r", rfcomm_channel_nr, bd_addr_to_str(event_addr)); - rfcomm_accept_connection_internal(rfcomm_channel_id); - break; - - case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: - // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) - if (packet[2]) { - log_info("RFCOMM channel open failed, status %u\n\r", packet[2]); - } else { - rfcomm_channel_id = READ_BT_16(packet, 12); - mtu = READ_BT_16(packet, 14); - log_info("\n\rRFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n\r", rfcomm_channel_id, mtu); - } - break; - - case RFCOMM_EVENT_CHANNEL_CLOSED: - rfcomm_channel_id = 0; - break; - - default: - break; - } - break; - - case RFCOMM_DATA_PACKET: - // loopback - if (rfcomm_channel_id) { - err = rfcomm_send_internal(rfcomm_channel_id, packet, size); - if (err) { - log_info("rfcomm_send_internal -> error %d", err); - } - } - led3 = !led3; - break; - default: - break; - } -} - -static void heartbeat_handler(struct timer *ts){ - run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(ts); - led2 = !led2; -} - -// main -int main(void) -{ - pc.baud(921600); - log_info("%s\n", __FILE__); - - // init LEDs - led1 = led2 = led3 = 1; - - /// GET STARTED with BTstack /// - btstack_memory_init(); - run_loop_init(RUN_LOOP_EMBEDDED); - - // init HCI - hci_transport_t* transport = hci_transport_usb_instance(); - remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory; - hci_init(transport, NULL, NULL, remote_db); - - // init L2CAP - l2cap_init(); - l2cap_register_packet_handler(packet_handler); - - // init RFCOMM - rfcomm_init(); - rfcomm_register_packet_handler(packet_handler); - rfcomm_register_service_internal(NULL, rfcomm_channel_nr, 100); // reserved channel, mtu=100 - - // init SDP, create record for SPP and register with SDP - sdp_init(); - memset(spp_service_buffer, 0, sizeof(spp_service_buffer)); - service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer; - sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, 1, "loopback"); - log_info("SDP service buffer size: %u\n\r", (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); - - // set one-shot timer - timer_source_t heartbeat; - heartbeat.process = &heartbeat_handler; - run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(&heartbeat); - - log_info("SPP loopback demo...\n\r"); - - // turn on! - hci_power_control(HCI_POWER_ON); - - // go! - run_loop_execute(); - - return 0; -} -#endif
--- a/spp_flowcontrol.cpp Tue Jun 26 14:27:45 2012 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,178 +0,0 @@ -#if 0 -//***************************************************************************** -// -// spp_counter demo - it provides a SPP and sends a counter every second -// -// it doesn't use the LCD to get down to a minimal memory footpring -// -//***************************************************************************** -#include "mbed.h" -#include <btstack/hci_cmds.h> -#include <btstack/run_loop.h> -#include <btstack/sdp_util.h> -#include "hci.h" -#include "l2cap.h" -#include "btstack_memory.h" -#include "remote_device_db.h" -#include "rfcomm.h" -#include "sdp.h" -#include "config.h" -#include "debug.h" -#include "bd_addr.h" // class bd_addr - -Serial pc(USBTX, USBRX); -DigitalOut led1(LED1), led2(LED2); - -#define HEARTBEAT_PERIOD_MS 500 - -static uint8_t rfcomm_channel_nr = 1; -static uint16_t rfcomm_channel_id = 0; -static uint8_t rfcomm_send_credit = 0; -static uint8_t spp_service_buffer[128]; - -// Bluetooth logic -static void packet_handler (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ - bd_addr_t event_addr; - uint8_t rfcomm_channel_nr; - uint16_t mtu; - - switch (packet_type) { - case HCI_EVENT_PACKET: - switch (packet[0]) { - - case BTSTACK_EVENT_STATE: - // bt stack activated, get started - set local name - if (packet[2] == HCI_STATE_WORKING) { - hci_send_cmd(&hci_write_local_name, "mbed-Demo"); - } - break; - - case HCI_EVENT_COMMAND_COMPLETE: - if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)){ - bt_flip_addr(event_addr, &packet[6]); - log_info("BD-ADDR: %s\n\r", bd_addr_to_str(event_addr)); - break; - } - if (COMMAND_COMPLETE_EVENT(packet, hci_write_local_name)){ - hci_discoverable_control(1); - break; - } - break; - - case HCI_EVENT_LINK_KEY_REQUEST: - // deny link key request - log_info("Link key request\n\r"); - bt_flip_addr(event_addr, &packet[2]); - hci_send_cmd(&hci_link_key_request_negative_reply, &event_addr); - break; - - case HCI_EVENT_PIN_CODE_REQUEST: - // inform about pin code request - log_info("Pin code request - using '0000'\n\r"); - bt_flip_addr(event_addr, &packet[2]); - hci_send_cmd(&hci_pin_code_request_reply, &event_addr, 4, "0000"); - break; - - case RFCOMM_EVENT_INCOMING_CONNECTION: - // data: event (8), len(8), address(48), channel (8), rfcomm_cid (16) - bt_flip_addr(event_addr, &packet[2]); - rfcomm_channel_nr = packet[8]; - rfcomm_channel_id = READ_BT_16(packet, 9); - log_info("RFCOMM channel %u requested for %s\n\r", rfcomm_channel_nr, bd_addr_to_str(event_addr)); - rfcomm_accept_connection_internal(rfcomm_channel_id); - break; - - case RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE: - // data: event(8), len(8), status (8), address (48), server channel(8), rfcomm_cid(16), max frame size(16) - if (packet[2]) { - log_info("RFCOMM channel open failed, status %u\n\r", packet[2]); - } else { - rfcomm_channel_id = READ_BT_16(packet, 12); - mtu = READ_BT_16(packet, 14); - log_info("\n\rRFCOMM channel open succeeded. New RFCOMM Channel ID %u, max frame size %u\n\r", rfcomm_channel_id, mtu); - } - break; - - case RFCOMM_EVENT_CHANNEL_CLOSED: - rfcomm_channel_id = 0; - break; - - default: - break; - } - break; - - case RFCOMM_DATA_PACKET: - // hack: truncate data (we know that the packet is at least on byte bigger - packet[size] = 0; - puts( (const char *) packet); - rfcomm_send_credit = 1; - default: - break; - } -} - -static void heartbeat_handler(struct timer *ts){ - if (rfcomm_send_credit){ - rfcomm_grant_credits(rfcomm_channel_id, 1); - rfcomm_send_credit = 0; - } - run_loop_set_timer(ts, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(ts); - led2 = !led2; -} - -// main -int main(void) -{ - pc.baud(921600); - log_info("%s\n", __FILE__); - - // init LEDs - led1 = led2 = 1; - - /// GET STARTED with BTstack /// - btstack_memory_init(); - run_loop_init(RUN_LOOP_EMBEDDED); - - // init HCI - hci_transport_t* transport = hci_transport_usb_instance(); - remote_device_db_t * remote_db = (remote_device_db_t *) &remote_device_db_memory; - hci_init(transport, NULL, NULL, remote_db); - - // init L2CAP - l2cap_init(); - l2cap_register_packet_handler(packet_handler); - - // init RFCOMM - rfcomm_init(); - rfcomm_register_packet_handler(packet_handler); - rfcomm_register_service_with_initial_credits_internal(NULL, rfcomm_channel_nr, 100, 1); // reserved channel, mtu=100, 1 credit - - // init SDP, create record for SPP and register with SDP - sdp_init(); - memset(spp_service_buffer, 0, sizeof(spp_service_buffer)); - service_record_item_t * service_record_item = (service_record_item_t *) spp_service_buffer; - sdp_create_spp_service( (uint8_t*) &service_record_item->service_record, 1, "SPP Counter"); - log_info("SDP service buffer size: %u\n\r", (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); - - // set one-shot timer - timer_source_t heartbeat; - heartbeat.process = &heartbeat_handler; - run_loop_set_timer(&heartbeat, HEARTBEAT_PERIOD_MS); - run_loop_add_timer(&heartbeat); - - - log_info("SPP FlowControl Demo: simulates processing on received data...\n\r"); - - // turn on! - hci_power_control(HCI_POWER_ON); - - // go! - run_loop_execute(); - - // happy compiler! - return 0; -} -#endif