Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
Dependencies: F401RE-USBHost mbed
Revision 0:a05a07cd6fdf, committed 2014-06-09
- Comitter:
- va009039
- Date:
- Mon Jun 09 09:03:25 2014 +0000
- Commit message:
- first commit
Changed in this revision
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/bt_control.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,76 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * bt_control.h
+ *
+ * BT Control API -- allows BT Daemon to initialize and control differnt hardware
+ *
+ * Created by Matthias Ringwald on 5/19/09.
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+typedef enum {
+ POWER_WILL_SLEEP = 1,
+ POWER_WILL_WAKE_UP
+} POWER_NOTIFICATION_t;
+
+typedef struct {
+ int (*on) (void *config); // <-- turn BT module on and configure
+ int (*off) (void *config); // <-- turn BT module off
+ int (*sleep)(void *config); // <-- put BT module to sleep - only to be called after ON
+ int (*wake) (void *config); // <-- wake BT module from sleep - only to be called after SLEEP
+ int (*valid)(void *config); // <-- test if hardware can be supported
+ const char * (*name) (void *config); // <-- return hardware name
+
+ /** support for UART baud rate changes - cmd has to be stored in hci_cmd_buffer
+ * @return have command
+ */
+ int (*baudrate_cmd)(void * config, uint32_t baudrate, uint8_t *hci_cmd_buffer);
+
+ /** support custom init sequences after RESET command - cmd has to be stored in hci_cmd_buffer
+ * @return have command
+ */
+ int (*next_cmd)(void *config, uint8_t * hci_cmd_buffer);
+
+ void (*register_for_power_notifications)(void (*cb)(POWER_NOTIFICATION_t event));
+
+ void (*hw_error)(void);
+} bt_control_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/btstack.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ */
+
+/*
+ * btstack.h
+ *
+ * Created by Matthias Ringwald on 7/1/09.
+ *
+ * BTstack client API
+ *
+ */
+
+#pragma once
+
+#include <btstack/hci_cmds.h>
+#include <btstack/run_loop.h>
+#include <btstack/utils.h>
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+// Default TCP port for BTstack daemon
+#define BTSTACK_PORT 13333
+
+// UNIX domain socket for BTstack */
+#define BTSTACK_UNIX "/tmp/BTstack"
+
+// packet handler
+typedef void (*btstack_packet_handler_t) (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
+
+// optional: if called before bt_open, TCP socket is used instead of local unix socket
+// note: address is not copied and must be valid during bt_open
+void bt_use_tcp(const char * address, uint16_t port);
+
+// init BTstack library
+int bt_open(void);
+
+// stop using BTstack library
+int bt_close(void);
+
+// send hci cmd packet
+int bt_send_cmd(const hci_cmd_t *cmd, ...);
+
+// register packet handler -- channel only valid for l2cap and rfcomm packets
+// @returns old packet handler
+btstack_packet_handler_t bt_register_packet_handler(btstack_packet_handler_t handler);
+
+void bt_send_acl(uint8_t * data, uint16_t len);
+
+void bt_send_l2cap(uint16_t local_cid, uint8_t *data, uint16_t len);
+void bt_send_rfcomm(uint16_t rfcom_cid, uint8_t *data, uint16_t len);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/hal_cpu.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * hal_cpu.h
+ *
+ * Low power mode for MCU requires that IRQs can be first blocked
+ * and then unblocked while entering low power mode atomically
+ */
+#if defined __cplusplus
+extern "C" {
+#endif
+
+void hal_cpu_disable_irqs(void);
+void hal_cpu_enable_irqs(void);
+void hal_cpu_enable_irqs_and_sleep(void);
+
+#if defined __cplusplus
+}
+#endif
+
\ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/hal_tick.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,53 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * hal_tick.h
+ *
+ * Hardware abstraction layer for periodic ticks
+ *
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+void hal_tick_init(void);
+void hal_tick_set_handler(void (*tick_handler)(void));
+int hal_tick_get_tick_period_in_ms(void);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/hci_cmds.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,380 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ */
+
+/*
+ * hci_cmds.h
+ *
+ * Created by Matthias Ringwald on 7/23/09.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+/**
+ * packet types - used in BTstack and over the H4 UART interface
+ */
+#define HCI_COMMAND_DATA_PACKET 0x01
+#define HCI_ACL_DATA_PACKET 0x02
+#define HCI_SCO_DATA_PACKET 0x03
+#define HCI_EVENT_PACKET 0x04
+
+// extension for client/server communication
+#define DAEMON_EVENT_PACKET 0x05
+
+// L2CAP data
+#define L2CAP_DATA_PACKET 0x06
+
+// RFCOMM data
+#define RFCOMM_DATA_PACKET 0x07
+
+// Attribute protocol data
+#define ATT_DATA_PACKET 0x08
+
+// Security Manager protocol data
+#define SM_DATA_PACKET 0x09
+
+// debug log messages
+#define LOG_MESSAGE_PACKET 0xfc
+
+
+// Fixed PSM numbers
+#define PSM_SDP 0x01
+#define PSM_RFCOMM 0x03
+#define PSM_HID_CONTROL 0x11
+#define PSM_HID_INTERRUPT 0x13
+
+// Events from host controller to host
+#define HCI_EVENT_INQUIRY_COMPLETE 0x01
+#define HCI_EVENT_INQUIRY_RESULT 0x02
+#define HCI_EVENT_CONNECTION_COMPLETE 0x03
+#define HCI_EVENT_CONNECTION_REQUEST 0x04
+#define HCI_EVENT_DISCONNECTION_COMPLETE 0x05
+#define HCI_EVENT_AUTHENTICATION_COMPLETE_EVENT 0x06
+#define HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE 0x07
+#define HCI_EVENT_ENCRYPTION_CHANGE 0x08
+#define HCI_EVENT_CHANGE_CONNECTION_LINK_KEY_COMPLETE 0x09
+#define HCI_EVENT_MASTER_LINK_KEY_COMPLETE 0x0A
+#define HCI_EVENT_READ_REMOTE_SUPPORTED_FEATURES_COMPLETE 0x0B
+#define HCI_EVENT_READ_REMOTE_VERSION_INFORMATION_COMPLETE 0x0C
+#define HCI_EVENT_QOS_SETUP_COMPLETE 0x0D
+#define HCI_EVENT_COMMAND_COMPLETE 0x0E
+#define HCI_EVENT_COMMAND_STATUS 0x0F
+#define HCI_EVENT_HARDWARE_ERROR 0x10
+#define HCI_EVENT_FLUSH_OCCURED 0x11
+#define HCI_EVENT_ROLE_CHANGE 0x12
+#define HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS 0x13
+#define HCI_EVENT_MODE_CHANGE_EVENT 0x14
+#define HCI_EVENT_RETURN_LINK_KEYS 0x15
+#define HCI_EVENT_PIN_CODE_REQUEST 0x16
+#define HCI_EVENT_LINK_KEY_REQUEST 0x17
+#define HCI_EVENT_LINK_KEY_NOTIFICATION 0x18
+#define HCI_EVENT_DATA_BUFFER_OVERFLOW 0x1A
+#define HCI_EVENT_MAX_SLOTS_CHANGED 0x1B
+#define HCI_EVENT_READ_CLOCK_OFFSET_COMPLETE 0x1C
+#define HCI_EVENT_PACKET_TYPE_CHANGED 0x1D
+#define HCI_EVENT_INQUIRY_RESULT_WITH_RSSI 0x22
+#define HCI_EVENT_EXTENDED_INQUIRY_RESPONSE 0x2F
+#define HCI_EVENT_LE_META 0x3E
+#define HCI_EVENT_VENDOR_SPECIFIC 0xFF
+
+#define HCI_SUBEVENT_LE_CONNECTION_COMPLETE 0x01
+#define HCI_SUBEVENT_LE_ADVERTISING_REPORT 0x02
+#define HCI_SUBEVENT_LE_CONNECTION_UPDATE_COMPLETE 0x03
+#define HCI_SUBEVENT_LE_READ_REMOTE_USED_FEATURES_COMPLETE 0x04
+#define HCI_SUBEVENT_LE_LONG_TERM_KEY_REQUEST 0x05
+
+// last used HCI_EVENT in 2.1 is 0x3d
+
+// events 0x50-0x5f are used internally
+
+// BTSTACK DAEMON EVENTS
+
+// events from BTstack for application/client lib
+#define BTSTACK_EVENT_STATE 0x60
+
+// data: event(8), len(8), nr hci connections
+#define BTSTACK_EVENT_NR_CONNECTIONS_CHANGED 0x61
+
+// data: none
+#define BTSTACK_EVENT_POWERON_FAILED 0x62
+
+// data: majot (8), minor (8), revision(16)
+#define BTSTACK_EVENT_VERSION 0x63
+
+// data: system bluetooth on/off (bool)
+#define BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED 0x64
+
+// data: event (8), len(8), status (8) == 0, address (48), name (1984 bits = 248 bytes)
+#define BTSTACK_EVENT_REMOTE_NAME_CACHED 0x65
+
+// data: discoverable enabled (bool)
+#define BTSTACK_EVENT_DISCOVERABLE_ENABLED 0x66
+
+// L2CAP EVENTS
+
+// data: event (8), len(8), status (8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16), local_mtu(16), remote_mtu(16)
+#define L2CAP_EVENT_CHANNEL_OPENED 0x70
+
+// data: event (8), len(8), channel (16)
+#define L2CAP_EVENT_CHANNEL_CLOSED 0x71
+
+// data: event (8), len(8), address(48), handle (16), psm (16), local_cid(16), remote_cid (16)
+#define L2CAP_EVENT_INCOMING_CONNECTION 0x72
+
+// data: event(8), len(8), handle(16)
+#define L2CAP_EVENT_TIMEOUT_CHECK 0x73
+
+// data: event(8), len(8), local_cid(16), credits(8)
+#define L2CAP_EVENT_CREDITS 0x74
+
+// data: event(8), len(8), status (8), psm (16)
+#define L2CAP_EVENT_SERVICE_REGISTERED 0x75
+
+
+// RFCOMM EVENTS
+
+// data: event(8), len(8), status (8), address (48), handle (16), server channel(8), rfcomm_cid(16), max frame size(16)
+#define RFCOMM_EVENT_OPEN_CHANNEL_COMPLETE 0x80
+
+// data: event(8), len(8), rfcomm_cid(16)
+#define RFCOMM_EVENT_CHANNEL_CLOSED 0x81
+
+// data: event (8), len(8), address(48), channel (8), rfcomm_cid (16)
+#define RFCOMM_EVENT_INCOMING_CONNECTION 0x82
+
+// data: event (8), len(8), rfcommid (16), ...
+#define RFCOMM_EVENT_REMOTE_LINE_STATUS 0x83
+
+// data: event(8), len(8), rfcomm_cid(16), credits(8)
+#define RFCOMM_EVENT_CREDITS 0x84
+
+// data: event(8), len(8), status (8), rfcomm server channel id (8)
+#define RFCOMM_EVENT_SERVICE_REGISTERED 0x85
+
+// data: event(8), len(8), status (8), rfcomm server channel id (8)
+#define RFCOMM_EVENT_PERSISTENT_CHANNEL 0x86
+
+
+// data: event(8), len(8), status(8), service_record_handle(32)
+#define SDP_SERVICE_REGISTERED 0x90
+
+
+// last error code in 2.1 is 0x38 - we start with 0x50 for BTstack errors
+
+#define BTSTACK_CONNECTION_TO_BTDAEMON_FAILED 0x50
+#define BTSTACK_ACTIVATION_FAILED_SYSTEM_BLUETOOTH 0x51
+#define BTSTACK_ACTIVATION_POWERON_FAILED 0x52
+#define BTSTACK_ACTIVATION_FAILED_UNKNOWN 0x53
+#define BTSTACK_NOT_ACTIVATED 0x54
+#define BTSTACK_BUSY 0x55
+#define BTSTACK_MEMORY_ALLOC_FAILED 0x56
+#define BTSTACK_ACL_BUFFERS_FULL 0x57
+
+// l2cap errors - enumeration by the command that created them
+#define L2CAP_COMMAND_REJECT_REASON_COMMAND_NOT_UNDERSTOOD 0x60
+#define L2CAP_COMMAND_REJECT_REASON_SIGNALING_MTU_EXCEEDED 0x61
+#define L2CAP_COMMAND_REJECT_REASON_INVALID_CID_IN_REQUEST 0x62
+
+#define L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL 0x63
+#define L2CAP_CONNECTION_RESPONSE_RESULT_PENDING 0x64
+#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_PSM 0x65
+#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY 0x66
+#define L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_RESOURCES 0x65
+
+#define L2CAP_CONFIG_RESPONSE_RESULT_SUCCESSFUL 0x66
+#define L2CAP_CONFIG_RESPONSE_RESULT_UNACCEPTABLE_PARAMS 0x67
+#define L2CAP_CONFIG_RESPONSE_RESULT_REJECTED 0x68
+#define L2CAP_CONFIG_RESPONSE_RESULT_UNKNOWN_OPTIONS 0x69
+#define L2CAP_SERVICE_ALREADY_REGISTERED 0x6a
+
+#define RFCOMM_MULTIPLEXER_STOPPED 0x70
+#define RFCOMM_CHANNEL_ALREADY_REGISTERED 0x71
+#define RFCOMM_NO_OUTGOING_CREDITS 0x72
+
+#define SDP_HANDLE_ALREADY_REGISTERED 0x80
+
+/**
+ * Default INQ Mode
+ */
+#define HCI_INQUIRY_LAP 0x9E8B33L // 0x9E8B33: General/Unlimited Inquiry Access Code (GIAC)
+/**
+ * Hardware state of Bluetooth controller
+ */
+typedef enum {
+ HCI_POWER_OFF = 0,
+ HCI_POWER_ON,
+ HCI_POWER_SLEEP
+} HCI_POWER_MODE;
+
+/**
+ * State of BTstack
+ */
+typedef enum {
+ HCI_STATE_OFF = 0,
+ HCI_STATE_INITIALIZING,
+ HCI_STATE_WORKING,
+ HCI_STATE_HALTING,
+ HCI_STATE_SLEEPING,
+ HCI_STATE_FALLING_ASLEEP
+} HCI_STATE;
+
+/**
+ * compact HCI Command packet description
+ */
+ typedef struct {
+ uint16_t opcode;
+ const char *format;
+} hci_cmd_t;
+
+
+// HCI Commands - see hci_cmds.c for info on parameters
+extern const hci_cmd_t btstack_get_state;
+extern const hci_cmd_t btstack_set_power_mode;
+extern const hci_cmd_t btstack_set_acl_capture_mode;
+extern const hci_cmd_t btstack_get_version;
+extern const hci_cmd_t btstack_get_system_bluetooth_enabled;
+extern const hci_cmd_t btstack_set_system_bluetooth_enabled;
+extern const hci_cmd_t btstack_set_discoverable;
+extern const hci_cmd_t btstack_set_bluetooth_enabled; // only used by btstack config
+
+extern const hci_cmd_t hci_accept_connection_request;
+extern const hci_cmd_t hci_authentication_requested;
+extern const hci_cmd_t hci_change_connection_link_key;
+extern const hci_cmd_t hci_create_connection;
+extern const hci_cmd_t hci_create_connection_cancel;
+extern const hci_cmd_t hci_delete_stored_link_key;
+extern const hci_cmd_t hci_disconnect;
+extern const hci_cmd_t hci_host_buffer_size;
+extern const hci_cmd_t hci_inquiry;
+extern const hci_cmd_t hci_inquiry_cancel;
+extern const hci_cmd_t hci_link_key_request_negative_reply;
+extern const hci_cmd_t hci_link_key_request_reply;
+extern const hci_cmd_t hci_pin_code_request_reply;
+extern const hci_cmd_t hci_pin_code_request_negative_reply;
+extern const hci_cmd_t hci_qos_setup;
+extern const hci_cmd_t hci_read_bd_addr;
+extern const hci_cmd_t hci_read_buffer_size;
+extern const hci_cmd_t hci_read_le_host_supported;
+extern const hci_cmd_t hci_read_link_policy_settings;
+extern const hci_cmd_t hci_read_link_supervision_timeout;
+extern const hci_cmd_t hci_read_local_supported_features;
+extern const hci_cmd_t hci_read_num_broadcast_retransmissions;
+extern const hci_cmd_t hci_reject_connection_request;
+extern const hci_cmd_t hci_remote_name_request;
+extern const hci_cmd_t hci_remote_name_request_cancel;
+extern const hci_cmd_t hci_reset;
+extern const hci_cmd_t hci_role_discovery;
+extern const hci_cmd_t hci_set_event_mask;
+extern const hci_cmd_t hci_set_connection_encryption;
+extern const hci_cmd_t hci_sniff_mode;
+extern const hci_cmd_t hci_switch_role_command;
+extern const hci_cmd_t hci_write_authentication_enable;
+extern const hci_cmd_t hci_write_class_of_device;
+extern const hci_cmd_t hci_write_extended_inquiry_response;
+extern const hci_cmd_t hci_write_inquiry_mode;
+extern const hci_cmd_t hci_write_le_host_supported;
+extern const hci_cmd_t hci_write_link_policy_settings;
+extern const hci_cmd_t hci_write_link_supervision_timeout;
+extern const hci_cmd_t hci_write_local_name;
+extern const hci_cmd_t hci_write_num_broadcast_retransmissions;
+extern const hci_cmd_t hci_write_page_timeout;
+extern const hci_cmd_t hci_write_scan_enable;
+extern const hci_cmd_t hci_write_simple_pairing_mode;
+
+extern const hci_cmd_t hci_le_add_device_to_whitelist;
+extern const hci_cmd_t hci_le_clear_white_list;
+extern const hci_cmd_t hci_le_connection_update;
+extern const hci_cmd_t hci_le_create_connection;
+extern const hci_cmd_t hci_le_create_connection_cancel;
+extern const hci_cmd_t hci_le_encrypt;
+extern const hci_cmd_t hci_le_long_term_key_negative_reply;
+extern const hci_cmd_t hci_le_long_term_key_request_reply;
+extern const hci_cmd_t hci_le_rand;
+extern const hci_cmd_t hci_le_read_advertising_channel_tx_power;
+extern const hci_cmd_t hci_le_read_buffer_size ;
+extern const hci_cmd_t hci_le_read_channel_map;
+extern const hci_cmd_t hci_le_read_remote_used_features;
+extern const hci_cmd_t hci_le_read_supported_features;
+extern const hci_cmd_t hci_le_read_supported_states;
+extern const hci_cmd_t hci_le_read_white_list_size;
+extern const hci_cmd_t hci_le_receiver_test;
+extern const hci_cmd_t hci_le_remove_device_from_whitelist;
+extern const hci_cmd_t hci_le_set_advertise_enable;
+extern const hci_cmd_t hci_le_set_advertising_data;
+extern const hci_cmd_t hci_le_set_advertising_parameters;
+extern const hci_cmd_t hci_le_set_event_mask;
+extern const hci_cmd_t hci_le_set_host_channel_classification;
+extern const hci_cmd_t hci_le_set_random_address;
+extern const hci_cmd_t hci_le_set_scan_enable;
+extern const hci_cmd_t hci_le_set_scan_parameters;
+extern const hci_cmd_t hci_le_set_scan_response_data;
+extern const hci_cmd_t hci_le_start_encryption;
+extern const hci_cmd_t hci_le_test_end;
+extern const hci_cmd_t hci_le_transmitter_test;
+
+extern const hci_cmd_t l2cap_accept_connection;
+extern const hci_cmd_t l2cap_create_channel;
+extern const hci_cmd_t l2cap_create_channel_mtu;
+extern const hci_cmd_t l2cap_decline_connection;
+extern const hci_cmd_t l2cap_disconnect;
+extern const hci_cmd_t l2cap_register_service;
+extern const hci_cmd_t l2cap_unregister_service;
+
+extern const hci_cmd_t sdp_register_service_record;
+extern const hci_cmd_t sdp_unregister_service_record;
+
+// accept connection @param bd_addr(48), rfcomm_cid (16)
+extern const hci_cmd_t rfcomm_accept_connection;
+// create rfcomm channel: @param bd_addr(48), channel (8)
+extern const hci_cmd_t rfcomm_create_channel;
+// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8)
+extern const hci_cmd_t rfcomm_create_channel_with_initial_credits;
+// decline rfcomm disconnect,@param bd_addr(48), rfcomm cid (16), reason(8)
+extern const hci_cmd_t rfcomm_decline_connection;
+// disconnect rfcomm disconnect, @param rfcomm_cid(8), reason(8)
+extern const hci_cmd_t rfcomm_disconnect;
+// register rfcomm service: @param channel(8), mtu (16)
+extern const hci_cmd_t rfcomm_register_service;
+// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
+extern const hci_cmd_t rfcomm_register_service_with_initial_credits;
+// unregister rfcomm service, @param service_channel(16)
+extern const hci_cmd_t rfcomm_unregister_service;
+// request persisten rfcomm channel for service name: serive name (char*)
+extern const hci_cmd_t rfcomm_persistent_channel_for_service;
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/linked_list.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ */
+
+/*
+ * linked_list.h
+ *
+ * Created by Matthias Ringwald on 7/13/09.
+ */
+
+#pragma once
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+typedef struct linked_item {
+ struct linked_item *next; // <-- next element in list, or NULL
+ void *user_data; // <-- pointer to struct base
+} linked_item_t;
+
+typedef linked_item_t * linked_list_t;
+
+void linked_item_set_user(linked_item_t *item, void *user_data); // <-- set user data
+void * linked_item_get_user(linked_item_t *item); // <-- get user data
+int linked_list_empty(linked_list_t * list);
+void linked_list_add(linked_list_t * list, linked_item_t *item); // <-- add item to list as first element
+void linked_list_add_tail(linked_list_t * list, linked_item_t *item); // <-- add item to list as last element
+int linked_list_remove(linked_list_t * list, linked_item_t *item); // <-- remove item from list
+linked_item_t * linked_list_get_last_item(linked_list_t * list); // <-- find the last item in the list
+
+void test_linked_list(void);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/btstack/memory_pool.h Mon Jun 09 09:03:25 2014 +0000 @@ -0,0 +1,54 @@ +/* + * 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.h + * + * @Brief Fixed-size block allocation + * + * @Assumption block_size >= sizeof(void *) + * @Assumption size of storage >= count * block_size + * + * @Note minimal implementation, no error checking/handling + */ + +#pragma once + +typedef void * memory_pool_t; + +// initialize memory pool with with given storage, block size and count +void memory_pool_create(memory_pool_t *pool, void * storage, int count, int block_size); + +// get free block from pool, @returns NULL or pointer to block +void * memory_pool_get(memory_pool_t *pool); + +// return previously reserved block to memory pool +void memory_pool_free(memory_pool_t *pool, void * block);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/run_loop.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ */
+
+/*
+ * run_loop.h
+ *
+ * Created by Matthias Ringwald on 6/6/09.
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <btstack/linked_list.h>
+
+#include <stdint.h>
+
+#ifdef HAVE_TIME
+#include <sys/time.h>
+#endif
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+ RUN_LOOP_POSIX = 1,
+ RUN_LOOP_COCOA,
+ RUN_LOOP_EMBEDDED
+} RUN_LOOP_TYPE;
+
+typedef struct data_source {
+ linked_item_t item;
+ int fd; // <-- file descriptor to watch or 0
+ int (*process)(struct data_source *ds); // <-- do processing
+} data_source_t;
+
+typedef struct timer {
+ linked_item_t item;
+#ifdef HAVE_TIME
+ struct timeval timeout; // <-- next timeout
+#endif
+#ifdef HAVE_TICK
+ uint32_t timeout; // timeout in system ticks
+#endif
+ void (*process)(struct timer *ts); // <-- do processing
+} timer_source_t;
+
+
+// set timer based on current time
+void run_loop_set_timer(timer_source_t *a, uint32_t timeout_in_ms);
+
+// add/remove timer_source
+void run_loop_add_timer(timer_source_t *timer);
+int run_loop_remove_timer(timer_source_t *timer);
+
+// init must be called before any other run_loop call
+void run_loop_init(RUN_LOOP_TYPE type);
+
+// add/remove data_source
+void run_loop_add_data_source(data_source_t *dataSource);
+int run_loop_remove_data_source(data_source_t *dataSource);
+
+
+// execute configured run_loop
+void run_loop_execute(void);
+
+// hack to fix HCI timer handling
+#ifdef HAVE_TICK
+uint32_t embedded_get_ticks(void);
+uint32_t embedded_ticks_for_ms(uint32_t time_in_ms);
+#endif
+#ifdef EMBEDDED
+void embedded_trigger(void);
+#endif
+#if defined __cplusplus
+}
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/sdp_util.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack/utils.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2009 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.
+ *
+ */
+
+/*
+ * utils.h
+ *
+ * General utility functions
+ *
+ * Created by Matthias Ringwald on 7/23/09.
+ */
+
+#pragma once
+
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>
+
+/**
+ * @brief hci connection handle type
+ */
+typedef uint16_t hci_con_handle_t;
+
+/**
+ * @brief Length of a bluetooth device address.
+ */
+#define BD_ADDR_LEN 6
+typedef uint8_t bd_addr_t[BD_ADDR_LEN];
+
+/**
+ * @brief The link key type
+ */
+#define LINK_KEY_LEN 16
+typedef uint8_t link_key_t[LINK_KEY_LEN];
+
+/**
+ * @brief The device name type
+ */
+#define DEVICE_NAME_LEN 248
+typedef uint8_t device_name_t[DEVICE_NAME_LEN+1];
+
+
+// helper for BT little endian format
+#define READ_BT_16( buffer, pos) ( ((uint16_t) buffer[pos]) | (((uint16_t)buffer[pos+1]) << 8))
+#define READ_BT_24( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16))
+#define READ_BT_32( buffer, pos) ( ((uint32_t) buffer[pos]) | (((uint32_t)buffer[pos+1]) << 8) | (((uint32_t)buffer[pos+2]) << 16) | (((uint32_t) buffer[pos+3])) << 24)
+
+// helper for SDP big endian format
+#define READ_NET_16( buffer, pos) ( ((uint16_t) buffer[pos+1]) | (((uint16_t)buffer[pos ]) << 8))
+#define READ_NET_32( buffer, pos) ( ((uint32_t) buffer[pos+3]) | (((uint32_t)buffer[pos+2]) << 8) | (((uint32_t)buffer[pos+1]) << 16) | (((uint32_t) buffer[pos])) << 24)
+
+// HCI CMD OGF/OCF
+#define READ_CMD_OGF(buffer) (buffer[1] >> 2)
+#define READ_CMD_OCF(buffer) ((buffer[1] & 0x03) << 8 | buffer[0])
+
+// check if command complete event for given command
+#define COMMAND_COMPLETE_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_COMPLETE && READ_BT_16(event,3) == cmd.opcode)
+#define COMMAND_STATUS_EVENT(event,cmd) ( event[0] == HCI_EVENT_COMMAND_STATUS && READ_BT_16(event,4) == cmd.opcode)
+
+// Code+Len=2, Pkts+Opcode=3; total=5
+#define OFFSET_OF_DATA_IN_COMMAND_COMPLETE 5
+
+// ACL Packet
+#define READ_ACL_CONNECTION_HANDLE( buffer ) ( READ_BT_16(buffer,0) & 0x0fff)
+#define READ_ACL_FLAGS( buffer ) ( buffer[1] >> 4 )
+#define READ_ACL_LENGTH( buffer ) (READ_BT_16(buffer, 2))
+
+// L2CAP Packet
+#define READ_L2CAP_LENGTH(buffer) ( READ_BT_16(buffer, 4))
+#define READ_L2CAP_CHANNEL_ID(buffer) ( READ_BT_16(buffer, 6))
+
+void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value);
+void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value);
+void bt_flip_addr(bd_addr_t dest, bd_addr_t src);
+
+void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value);
+void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value);
+
+void hexdump(void *data, int size);
+void printUUID(uint8_t *uuid);
+
+// @deprecated please use more convenient bd_addr_to_str
+void print_bd_addr( bd_addr_t addr);
+char * bd_addr_to_str(bd_addr_t addr);
+
+int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr);
+
+uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum);
+uint8_t crc8_calc(uint8_t *data, uint16_t len);
+
+#define BD_ADDR_CMP(a,b) memcmp(a,b, BD_ADDR_LEN)
+#define BD_ADDR_COPY(dest,src) memcpy(dest,src,BD_ADDR_LEN)
+
+#if defined __cplusplus
+}
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack_memory.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +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
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/btstack_memory.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,73 @@
+/*
+ * 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
+ *
+ */
+
+#pragma once
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+void btstack_memory_init(void);
+
+void * btstack_memory_hci_connection_get(void);
+void btstack_memory_hci_connection_free(void *hci_connection);
+void * btstack_memory_l2cap_service_get(void);
+void btstack_memory_l2cap_service_free(void *l2cap_service);
+void * btstack_memory_l2cap_channel_get(void);
+void btstack_memory_l2cap_channel_free(void *l2cap_channel);
+void * btstack_memory_rfcomm_multiplexer_get(void);
+void btstack_memory_rfcomm_multiplexer_free(void *rfcomm_multiplexer);
+void * btstack_memory_rfcomm_service_get(void);
+void btstack_memory_rfcomm_service_free(void *rfcomm_service);
+void * btstack_memory_rfcomm_channel_get(void);
+void btstack_memory_rfcomm_channel_free(void *rfcomm_channel);
+void * btstack_memory_db_mem_device_name_get(void);
+void btstack_memory_db_mem_device_name_free(void *db_mem_device_name);
+void * btstack_memory_db_mem_device_link_key_get(void);
+void btstack_memory_db_mem_device_link_key_free(void *db_mem_device_link_key);
+void * btstack_memory_db_mem_service_get(void);
+void btstack_memory_db_mem_service_free(void *db_mem_service);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/config.h Mon Jun 09 09:03:25 2014 +0000 @@ -0,0 +1,24 @@ +#define EMBEDDED +#define HAVE_TICK + +//#define HAVE_INIT_SCRIPT +//#define HAVE_BZERO +//#define HAVE_EHCILL + +#define ENABLE_LOG_INFO +#define ENABLE_LOG_ERROR + +#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
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/BTstack/debug.h Mon Jun 09 09:03:25 2014 +0000 @@ -0,0 +1,76 @@ +/* + * 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 + * + */ + +/* + * debug.h + * + * allow to funnel debug & error messages + */ + +#include "config.h" +#include "hci_dump.h" + +#include <stdio.h> + +#ifdef ENABLE_LOG_DEBUG +#ifdef HAVE_HCI_DUMP +#define log_debug(format, ...) hci_dump_log(format, ## __VA_ARGS__) +#else +#define log_debug(format, ...) printf(format, ## __VA_ARGS__) +#endif +#else +#define log_debug(...) +#endif + +#ifdef ENABLE_LOG_INFO +#ifdef HAVE_HCI_DUMP +#define log_info(format, ...) hci_dump_log(format, ## __VA_ARGS__) +#else +#define log_info(format, ...) printf(format, ## __VA_ARGS__) +#endif +#else +#define log_info(...) +#endif + +#ifdef ENABLE_LOG_ERROR +#ifdef HAVE_HCI_DUMP +#define log_error(format, ...) hci_dump_log(format, ## __VA_ARGS__) +#else +#define log_error(format, ...) printf(format, ## __VA_ARGS__) +#endif +#else +#define log_error(...) +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hal_mbed/hal_cpu.cpp Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * hal_cpu.c
+ *
+ * Implementation for mbed
+ *
+ */
+
+#include <btstack/hal_cpu.h>
+
+void hal_cpu_disable_irqs(){
+
+}
+
+void hal_cpu_enable_irqs(){
+
+}
+
+void hal_cpu_enable_irqs_and_sleep(){
+
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hal_mbed/hal_tick.cpp Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * hal_tick.c
+ *
+ * Implementation for mbed
+ *
+ */
+
+#include <btstack/hal_tick.h>
+#include "mbed.h"
+static Ticker tick;
+
+static void dummy_handler(void){};
+
+static void (*tick_handler)(void) = &dummy_handler;
+
+void hal_tick_init(void){
+
+}
+
+void hal_tick_set_handler(void (*handler)(void)){
+ if (handler == NULL){
+ tick_handler = &dummy_handler;
+ return;
+ }
+ tick_handler = handler;
+ tick.attach(tick_handler, 0.001);
+}
+
+int hal_tick_get_tick_period_in_ms(void){
+ return 1;
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hci.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,1409 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * hci.c
+ *
+ * Created by Matthias Ringwald on 4/29/09.
+ *
+ */
+
+#include "config.h"
+
+#include "hci.h"
+
+#include <stdarg.h>
+#include <string.h>
+#include <stdio.h>
+
+#ifndef EMBEDDED
+#include <unistd.h> // gethostbyname
+#include <btstack/version.h>
+#endif
+
+#include "btstack_memory.h"
+#include "debug.h"
+#include "hci_dump.h"
+
+#include <btstack/hci_cmds.h>
+
+#define HCI_CONNECTION_TIMEOUT_MS 10000
+
+#ifdef USE_BLUETOOL
+#include "bt_control_iphone.h"
+#endif
+
+static void hci_update_scan_enable(void);
+
+// the STACK is here
+static hci_stack_t hci_stack;
+
+/**
+ * get connection for a given handle
+ *
+ * @return connection OR NULL, if not found
+ */
+hci_connection_t * connection_for_handle(hci_con_handle_t con_handle){
+ linked_item_t *it;
+ for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+ if ( ((hci_connection_t *) it)->con_handle == con_handle){
+ return (hci_connection_t *) it;
+ }
+ }
+ return NULL;
+}
+
+static void hci_connection_timeout_handler(timer_source_t *timer){
+ hci_connection_t * connection = (hci_connection_t *) linked_item_get_user(&timer->item);
+#ifdef HAVE_TIME
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+ if (tv.tv_sec >= connection->timestamp.tv_sec + HCI_CONNECTION_TIMEOUT_MS/1000) {
+ // connections might be timed out
+ hci_emit_l2cap_check_timeout(connection);
+ }
+#endif
+#ifdef HAVE_TICK
+ if (embedded_get_ticks() > connection->timestamp + embedded_ticks_for_ms(HCI_CONNECTION_TIMEOUT_MS)){
+ // connections might be timed out
+ hci_emit_l2cap_check_timeout(connection);
+ }
+#endif
+ run_loop_set_timer(timer, HCI_CONNECTION_TIMEOUT_MS);
+ run_loop_add_timer(timer);
+}
+
+static void hci_connection_timestamp(hci_connection_t *connection){
+#ifdef HAVE_TIME
+ gettimeofday(&connection->timestamp, NULL);
+#endif
+#ifdef HAVE_TICK
+ connection->timestamp = embedded_get_ticks();
+#endif
+}
+
+/**
+ * create connection for given address
+ *
+ * @return connection OR NULL, if no memory left
+ */
+static hci_connection_t * create_connection_for_addr(bd_addr_t addr){
+ hci_connection_t * conn = (hci_connection_t *) btstack_memory_hci_connection_get();
+ if (!conn) return NULL;
+ BD_ADDR_COPY(conn->address, addr);
+ conn->con_handle = 0xffff;
+ conn->authentication_flags = AUTH_FLAGS_NONE;
+ linked_item_set_user(&conn->timeout.item, conn);
+ conn->timeout.process = hci_connection_timeout_handler;
+ hci_connection_timestamp(conn);
+ conn->acl_recombination_length = 0;
+ conn->acl_recombination_pos = 0;
+ conn->num_acl_packets_sent = 0;
+ linked_list_add(&hci_stack.connections, (linked_item_t *) conn);
+ return conn;
+}
+
+/**
+ * get connection for given address
+ *
+ * @return connection OR NULL, if not found
+ */
+static hci_connection_t * connection_for_address(bd_addr_t address){
+ linked_item_t *it;
+ for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+ if ( ! BD_ADDR_CMP( ((hci_connection_t *) it)->address, address) ){
+ return (hci_connection_t *) it;
+ }
+ }
+ return NULL;
+}
+
+inline static void connectionSetAuthenticationFlags(hci_connection_t * conn, hci_authentication_flags_t flags){
+ conn->authentication_flags = (hci_authentication_flags_t)(conn->authentication_flags | flags);
+}
+
+inline static void connectionClearAuthenticationFlags(hci_connection_t * conn, hci_authentication_flags_t flags){
+ conn->authentication_flags = (hci_authentication_flags_t)(conn->authentication_flags & ~flags);
+}
+
+
+/**
+ * add authentication flags and reset timer
+ */
+static void hci_add_connection_flags_for_flipped_bd_addr(uint8_t *bd_addr, hci_authentication_flags_t flags){
+ bd_addr_t addr;
+ bt_flip_addr(addr, *(bd_addr_t *) bd_addr);
+ hci_connection_t * conn = connection_for_address(addr);
+ if (conn) {
+ connectionSetAuthenticationFlags(conn, flags);
+ hci_connection_timestamp(conn);
+ }
+}
+
+int hci_authentication_active_for_handle(hci_con_handle_t handle){
+ hci_connection_t * conn = connection_for_handle(handle);
+ if (!conn) return 0;
+ if (!conn->authentication_flags) return 0;
+ if (conn->authentication_flags & SENT_LINK_KEY_REPLY) return 0;
+ if (conn->authentication_flags & RECV_LINK_KEY_NOTIFICATION) return 0;
+ return 1;
+}
+
+void hci_drop_link_key_for_bd_addr(bd_addr_t *addr){
+ if (hci_stack.remote_device_db) {
+ hci_stack.remote_device_db->delete_link_key(addr);
+ }
+}
+
+
+/**
+ * count connections
+ */
+static int nr_hci_connections(void){
+ int count = 0;
+ linked_item_t *it;
+ for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next, count++);
+ return count;
+}
+
+/**
+ * Dummy handler called by HCI
+ */
+static void dummy_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
+}
+
+uint8_t hci_number_outgoing_packets(hci_con_handle_t handle){
+ hci_connection_t * connection = connection_for_handle(handle);
+ if (!connection) {
+ log_error("hci_number_outgoing_packets connectino for handle %u does not exist!\n", handle);
+ return 0;
+ }
+ return connection->num_acl_packets_sent;
+}
+
+uint8_t hci_number_free_acl_slots(){
+ uint8_t free_slots = hci_stack.total_num_acl_packets;
+ linked_item_t *it;
+ for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+ hci_connection_t * connection = (hci_connection_t *) it;
+ if (free_slots < connection->num_acl_packets_sent) {
+ log_error("hci_number_free_acl_slots: sum of outgoing packets > total acl packets!\n");
+ return 0;
+ }
+ free_slots -= connection->num_acl_packets_sent;
+ }
+ return free_slots;
+}
+
+int hci_can_send_packet_now(uint8_t packet_type){
+
+ // check for async hci transport implementations
+ if (hci_stack.hci_transport->can_send_packet_now){
+ if (!hci_stack.hci_transport->can_send_packet_now(packet_type)){
+ return 0;
+ }
+ }
+
+ // check regular Bluetooth flow control
+ switch (packet_type) {
+ case HCI_ACL_DATA_PACKET:
+ return hci_number_free_acl_slots();
+ case HCI_COMMAND_DATA_PACKET:
+ return hci_stack.num_cmd_packets;
+ default:
+ return 0;
+ }
+}
+
+int hci_send_acl_packet(uint8_t *packet, int size){
+
+ // check for free places on BT module
+ if (!hci_number_free_acl_slots()) return BTSTACK_ACL_BUFFERS_FULL;
+
+ hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet);
+ hci_connection_t *connection = connection_for_handle( con_handle);
+ if (!connection) return 0;
+ hci_connection_timestamp(connection);
+
+ // count packet
+ connection->num_acl_packets_sent++;
+ // log_info("hci_send_acl_packet - handle %u, sent %u\n", connection->con_handle, connection->num_acl_packets_sent);
+
+ // send packet
+ int err = hci_stack.hci_transport->send_packet(HCI_ACL_DATA_PACKET, packet, size);
+
+ return err;
+}
+
+static void acl_handler(uint8_t *packet, int size){
+
+ // get info
+ hci_con_handle_t con_handle = READ_ACL_CONNECTION_HANDLE(packet);
+ hci_connection_t *conn = connection_for_handle(con_handle);
+ uint8_t acl_flags = READ_ACL_FLAGS(packet);
+ uint16_t acl_length = READ_ACL_LENGTH(packet);
+
+ // ignore non-registered handle
+ if (!conn){
+ log_error( "hci.c: acl_handler called with non-registered handle %u!\n" , con_handle);
+ return;
+ }
+
+ // update idle timestamp
+ hci_connection_timestamp(conn);
+
+ // handle different packet types
+ switch (acl_flags & 0x03) {
+
+ case 0x01: // continuation fragment
+
+ // sanity check
+ if (conn->acl_recombination_pos == 0) {
+ log_error( "ACL Cont Fragment but no first fragment for handle 0x%02x\n", con_handle);
+ return;
+ }
+
+ // append fragment payload (header already stored)
+ memcpy(&conn->acl_recombination_buffer[conn->acl_recombination_pos], &packet[4], acl_length );
+ conn->acl_recombination_pos += acl_length;
+
+ // log_error( "ACL Cont Fragment: acl_len %u, combined_len %u, l2cap_len %u\n", acl_length,
+ // conn->acl_recombination_pos, conn->acl_recombination_length);
+
+ // forward complete L2CAP packet if complete.
+ if (conn->acl_recombination_pos >= conn->acl_recombination_length + 4 + 4){ // pos already incl. ACL header
+
+ hci_stack.packet_handler(HCI_ACL_DATA_PACKET, conn->acl_recombination_buffer, conn->acl_recombination_pos);
+ // reset recombination buffer
+ conn->acl_recombination_length = 0;
+ conn->acl_recombination_pos = 0;
+ }
+ break;
+
+ case 0x02: { // first fragment
+
+ // sanity check
+ if (conn->acl_recombination_pos) {
+ log_error( "ACL First Fragment but data in buffer for handle 0x%02x\n", con_handle);
+ return;
+ }
+
+ // peek into L2CAP packet!
+ uint16_t l2cap_length = READ_L2CAP_LENGTH( packet );
+
+ // log_error( "ACL First Fragment: acl_len %u, l2cap_len %u\n", acl_length, l2cap_length);
+
+ // compare fragment size to L2CAP packet size
+ if (acl_length >= l2cap_length + 4){
+
+ // forward fragment as L2CAP packet
+ hci_stack.packet_handler(HCI_ACL_DATA_PACKET, packet, acl_length + 4);
+
+ } else {
+ // store first fragment and tweak acl length for complete package
+ memcpy(conn->acl_recombination_buffer, packet, acl_length + 4);
+ conn->acl_recombination_pos = acl_length + 4;
+ conn->acl_recombination_length = l2cap_length;
+ bt_store_16(conn->acl_recombination_buffer, 2, l2cap_length +4);
+ }
+ break;
+
+ }
+ default:
+ log_error( "hci.c: acl_handler called with invalid packet boundary flags %u\n", acl_flags & 0x03);
+ return;
+ }
+
+ // execute main loop
+ hci_run();
+}
+
+static void hci_shutdown_connection(hci_connection_t *conn){
+ log_info("Connection closed: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address));
+
+ // cancel all l2cap connections
+ hci_emit_disconnection_complete(conn->con_handle, 0x16); // terminated by local host
+
+ run_loop_remove_timer(&conn->timeout);
+
+ linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
+ btstack_memory_hci_connection_free( conn );
+
+ // now it's gone
+ hci_emit_nr_connections_changed();
+}
+
+static const uint16_t packet_type_sizes[] = {
+ 0, HCI_ACL_2DH1_SIZE, HCI_ACL_3DH1_SIZE, HCI_ACL_DM1_SIZE,
+ HCI_ACL_DH1_SIZE, 0, 0, 0,
+ HCI_ACL_2DH3_SIZE, HCI_ACL_3DH3_SIZE, HCI_ACL_DM3_SIZE, HCI_ACL_DH3_SIZE,
+ HCI_ACL_2DH5_SIZE, HCI_ACL_3DH5_SIZE, HCI_ACL_DM5_SIZE, HCI_ACL_DH5_SIZE
+};
+
+static uint16_t hci_acl_packet_types_for_buffer_size(uint16_t buffer_size){
+ uint16_t packet_types = 0;
+ int i;
+ for (i=0;i<16;i++){
+ if (packet_type_sizes[i] == 0) continue;
+ if (packet_type_sizes[i] <= buffer_size){
+ packet_types |= 1 << i;
+ }
+ }
+ // flip bits for "may not be used"
+ packet_types ^= 0x3306;
+ return packet_types;
+}
+
+uint16_t hci_usable_acl_packet_types(void){
+ return hci_stack.packet_types;
+}
+
+uint8_t* hci_get_outgoing_acl_packet_buffer(void){
+ // hci packet buffer is >= acl data packet length
+ return hci_stack.hci_packet_buffer;
+}
+
+uint16_t hci_max_acl_data_packet_length(){
+ return hci_stack.acl_data_packet_length;
+}
+
+// avoid huge local variables
+#ifndef EMBEDDED
+static device_name_t device_name;
+#endif
+static void event_handler(uint8_t *packet, int size){
+ bd_addr_t addr;
+ uint8_t link_type;
+ hci_con_handle_t handle;
+ hci_connection_t * conn;
+ int i;
+
+ // printf("HCI:EVENT:%02x\n", packet[0]);
+
+ switch (packet[0]) {
+
+ case HCI_EVENT_COMMAND_COMPLETE:
+ // get num cmd packets
+ // log_info("HCI_EVENT_COMMAND_COMPLETE cmds old %u - new %u\n", hci_stack.num_cmd_packets, packet[2]);
+ hci_stack.num_cmd_packets = packet[2];
+
+ if (COMMAND_COMPLETE_EVENT(packet, hci_read_buffer_size)){
+ // from offset 5
+ // status
+ // "The HC_ACL_Data_Packet_Length return parameter will be used to determine the size of the L2CAP segments contained in ACL Data Packets"
+ hci_stack.acl_data_packet_length = READ_BT_16(packet, 6);
+ // ignore: SCO data packet len (8)
+ hci_stack.total_num_acl_packets = packet[9];
+ // ignore: total num SCO packets
+ if (hci_stack.state == HCI_STATE_INITIALIZING){
+ // determine usable ACL payload size
+ if (HCI_ACL_PAYLOAD_SIZE < hci_stack.acl_data_packet_length){
+ hci_stack.acl_data_packet_length = HCI_ACL_PAYLOAD_SIZE;
+ }
+ // determine usable ACL packet types
+ hci_stack.packet_types = hci_acl_packet_types_for_buffer_size(hci_stack.acl_data_packet_length);
+
+ log_error("hci_read_buffer_size: used size %u, count %u, packet types %04x\n",
+ hci_stack.acl_data_packet_length, hci_stack.total_num_acl_packets, hci_stack.packet_types);
+ }
+ }
+ // Dump local address
+ if (COMMAND_COMPLETE_EVENT(packet, hci_read_bd_addr)) {
+ bd_addr_t addr;
+ bt_flip_addr(addr, &packet[OFFSET_OF_DATA_IN_COMMAND_COMPLETE + 1]);
+ log_info("Local Address, Status: 0x%02x: Addr: %s\n",
+ packet[OFFSET_OF_DATA_IN_COMMAND_COMPLETE], bd_addr_to_str(addr));
+ }
+ if (COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){
+ hci_emit_discoverable_enabled(hci_stack.discoverable);
+ }
+ break;
+
+ case HCI_EVENT_COMMAND_STATUS:
+ // get num cmd packets
+ // log_info("HCI_EVENT_COMMAND_STATUS cmds - old %u - new %u\n", hci_stack.num_cmd_packets, packet[3]);
+ hci_stack.num_cmd_packets = packet[3];
+ break;
+
+ case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
+ for (i=0; i<packet[2];i++){
+ handle = READ_BT_16(packet, 3 + 2*i);
+ uint16_t num_packets = READ_BT_16(packet, 3 + packet[2]*2 + 2*i);
+ conn = connection_for_handle(handle);
+ if (!conn){
+ log_error("hci_number_completed_packet lists unused con handle %u\n", handle);
+ continue;
+ }
+ conn->num_acl_packets_sent -= num_packets;
+ // log_info("hci_number_completed_packet %u processed for handle %u, outstanding %u\n", num_packets, handle, conn->num_acl_packets_sent);
+ }
+ break;
+
+ case HCI_EVENT_CONNECTION_REQUEST:
+ bt_flip_addr(addr, &packet[2]);
+ // TODO: eval COD 8-10
+ link_type = packet[11];
+ log_info("Connection_incoming: %s, type %u\n", bd_addr_to_str(addr), link_type);
+ if (link_type == 1) { // ACL
+ conn = connection_for_address(addr);
+ if (!conn) {
+ conn = create_connection_for_addr(addr);
+ }
+ if (!conn) {
+ // CONNECTION REJECTED DUE TO LIMITED RESOURCES (0X0D)
+ hci_stack.decline_reason = 0x0d;
+ BD_ADDR_COPY(hci_stack.decline_addr, addr);
+ break;
+ }
+ conn->state = RECEIVED_CONNECTION_REQUEST;
+ hci_run();
+ } else {
+ // SYNCHRONOUS CONNECTION LIMIT TO A DEVICE EXCEEDED (0X0A)
+ hci_stack.decline_reason = 0x0a;
+ BD_ADDR_COPY(hci_stack.decline_addr, addr);
+ }
+ break;
+
+ case HCI_EVENT_CONNECTION_COMPLETE:
+ // Connection management
+ bt_flip_addr(addr, &packet[5]);
+ log_info("Connection_complete (status=%u) %s\n", packet[2], bd_addr_to_str(addr));
+ conn = connection_for_address(addr);
+ if (conn) {
+ if (!packet[2]){
+ conn->state = OPEN;
+ conn->con_handle = READ_BT_16(packet, 3);
+
+ // restart timer
+ run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS);
+ run_loop_add_timer(&conn->timeout);
+
+ log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address));
+
+ hci_emit_nr_connections_changed();
+ } else {
+ // connection failed, remove entry
+ linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
+ btstack_memory_hci_connection_free( conn );
+
+ // if authentication error, also delete link key
+ if (packet[2] == 0x05) {
+ hci_drop_link_key_for_bd_addr(&addr);
+ }
+ }
+ }
+ break;
+
+ case HCI_EVENT_LINK_KEY_REQUEST:
+ log_info("HCI_EVENT_LINK_KEY_REQUEST\n");
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_REQUEST);
+ if (!hci_stack.remote_device_db) break;
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[2], HANDLE_LINK_KEY_REQUEST);
+ hci_run();
+ // request handled by hci_run() as HANDLE_LINK_KEY_REQUEST gets set
+ return;
+
+ case HCI_EVENT_LINK_KEY_NOTIFICATION:
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_LINK_KEY_NOTIFICATION);
+ if (!hci_stack.remote_device_db) break;
+ bt_flip_addr(addr, &packet[2]);
+ hci_stack.remote_device_db->put_link_key(&addr, (link_key_t *) &packet[8]);
+ // still forward event to allow dismiss of pairing dialog
+ break;
+
+ case HCI_EVENT_PIN_CODE_REQUEST:
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[2], RECV_PIN_CODE_REQUEST);
+ // PIN CODE REQUEST means the link key request didn't succee -> delete stored link key
+ if (!hci_stack.remote_device_db) break;
+ bt_flip_addr(addr, &packet[2]);
+ hci_stack.remote_device_db->delete_link_key(&addr);
+ break;
+
+#ifndef EMBEDDED
+ case HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE:
+ if (!hci_stack.remote_device_db) break;
+ if (packet[2]) break; // status not ok
+ bt_flip_addr(addr, &packet[3]);
+ // fix for invalid remote names - terminate on 0xff
+ for (i=0; i<248;i++){
+ if (packet[9+i] == 0xff){
+ packet[9+i] = 0;
+ break;
+ }
+ }
+ memset(&device_name, 0, sizeof(device_name_t));
+ strncpy((char*) device_name, (char*) &packet[9], 248);
+ hci_stack.remote_device_db->put_name(&addr, &device_name);
+ break;
+
+ case HCI_EVENT_INQUIRY_RESULT:
+ case HCI_EVENT_INQUIRY_RESULT_WITH_RSSI:
+ if (!hci_stack.remote_device_db) break;
+ // first send inq result packet
+ hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size);
+ // then send cached remote names
+ for (i=0; i<packet[2];i++){
+ bt_flip_addr(addr, &packet[3+i*6]);
+ if (hci_stack.remote_device_db->get_name(&addr, &device_name)){
+ hci_emit_remote_name_cached(&addr, &device_name);
+ }
+ }
+ return;
+#endif
+
+ case HCI_EVENT_DISCONNECTION_COMPLETE:
+ if (!packet[2]){
+ handle = READ_BT_16(packet, 3);
+ hci_connection_t * conn = connection_for_handle(handle);
+ if (conn) {
+ hci_shutdown_connection(conn);
+ }
+ }
+ break;
+
+ case HCI_EVENT_HARDWARE_ERROR:
+ if(hci_stack.control->hw_error){
+ (*hci_stack.control->hw_error)();
+ }
+ break;
+
+#ifdef HAVE_BLE
+ case HCI_EVENT_LE_META:
+ switch (packet[2]) {
+ case HCI_SUBEVENT_LE_CONNECTION_COMPLETE:
+ // Connection management
+ bt_flip_addr(addr, &packet[8]);
+ log_info("LE Connection_complete (status=%u) %s\n", packet[3], bd_addr_to_str(addr));
+ // LE connections are auto-accepted, so just create a connection if there isn't one already
+ conn = connection_for_address(addr);
+ if (packet[3]){
+ if (conn){
+ // outgoing connection failed, remove entry
+ linked_list_remove(&hci_stack.connections, (linked_item_t *) conn);
+ btstack_memory_hci_connection_free( conn );
+
+ }
+ // if authentication error, also delete link key
+ if (packet[3] == 0x05) {
+ hci_drop_link_key_for_bd_addr(&addr);
+ }
+ break;
+ }
+ if (!conn){
+ conn = create_connection_for_addr(addr);
+ }
+ if (!conn){
+ // no memory
+ break;
+ }
+
+ conn->state = OPEN;
+ conn->con_handle = READ_BT_16(packet, 4);
+
+ // TODO: store - role, peer address type, conn_interval, conn_latency, supervision timeout, master clock
+
+ // restart timer
+ // run_loop_set_timer(&conn->timeout, HCI_CONNECTION_TIMEOUT_MS);
+ // run_loop_add_timer(&conn->timeout);
+
+ log_info("New connection: handle %u, %s\n", conn->con_handle, bd_addr_to_str(conn->address));
+
+ hci_emit_nr_connections_changed();
+ break;
+
+ default:
+ break;
+ }
+ break;
+#endif
+
+ default:
+ break;
+ }
+
+ // handle BT initialization
+ if (hci_stack.state == HCI_STATE_INITIALIZING){
+ // handle H4 synchronization loss on restart
+ // if (hci_stack.substate == 1 && packet[0] == HCI_EVENT_HARDWARE_ERROR){
+ // hci_stack.substate = 0;
+ // }
+ // handle normal init sequence
+ if (hci_stack.substate % 2){
+ // odd: waiting for event
+ if (packet[0] == HCI_EVENT_COMMAND_COMPLETE){
+ hci_stack.substate++;
+ }
+ }
+ }
+
+ // help with BT sleep
+ if (hci_stack.state == HCI_STATE_FALLING_ASLEEP
+ && hci_stack.substate == 1
+ && COMMAND_COMPLETE_EVENT(packet, hci_write_scan_enable)){
+ hci_stack.substate++;
+ }
+
+ hci_stack.packet_handler(HCI_EVENT_PACKET, packet, size);
+
+ // execute main loop
+ hci_run();
+}
+
+void packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
+ switch (packet_type) {
+ case HCI_EVENT_PACKET:
+ event_handler(packet, size);
+ break;
+ case HCI_ACL_DATA_PACKET:
+ acl_handler(packet, size);
+ break;
+ default:
+ break;
+ }
+}
+
+/** Register HCI packet handlers */
+void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
+ hci_stack.packet_handler = handler;
+}
+
+void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db){
+
+ // reference to use transport layer implementation
+ hci_stack.hci_transport = transport;
+
+ // references to used control implementation
+ hci_stack.control = control;
+
+ // reference to used config
+ hci_stack.config = config;
+
+ // no connections yet
+ hci_stack.connections = NULL;
+ hci_stack.discoverable = 0;
+ hci_stack.connectable = 0;
+
+ // no pending cmds
+ hci_stack.decline_reason = 0;
+ hci_stack.new_scan_enable_value = 0xff;
+
+ // higher level handler
+ hci_stack.packet_handler = dummy_handler;
+
+ // store and open remote device db
+ hci_stack.remote_device_db = remote_device_db;
+ if (hci_stack.remote_device_db) {
+ hci_stack.remote_device_db->open();
+ }
+
+ // max acl payload size defined in config.h
+ hci_stack.acl_data_packet_length = HCI_ACL_PAYLOAD_SIZE;
+
+ // register packet handlers with transport
+ transport->register_packet_handler(&packet_handler);
+
+ hci_stack.state = HCI_STATE_OFF;
+}
+
+void hci_close(){
+ // close remote device db
+ if (hci_stack.remote_device_db) {
+ hci_stack.remote_device_db->close();
+ }
+ while (hci_stack.connections) {
+ hci_shutdown_connection((hci_connection_t *) hci_stack.connections);
+}
+ hci_power_control(HCI_POWER_OFF);
+}
+
+// State-Module-Driver overview
+// state module low-level
+// HCI_STATE_OFF off close
+// HCI_STATE_INITIALIZING, on open
+// HCI_STATE_WORKING, on open
+// HCI_STATE_HALTING, on open
+// HCI_STATE_SLEEPING, off/sleep close
+// HCI_STATE_FALLING_ASLEEP on open
+
+static int hci_power_control_on(void){
+
+ // power on
+ int err = 0;
+ if (hci_stack.control && hci_stack.control->on){
+ err = (*hci_stack.control->on)(hci_stack.config);
+ }
+ if (err){
+ log_error( "POWER_ON failed\n");
+ hci_emit_hci_open_failed();
+ return err;
+ }
+
+ // open low-level device
+ err = hci_stack.hci_transport->open(hci_stack.config);
+ if (err){
+ log_error( "HCI_INIT failed, turning Bluetooth off again\n");
+ if (hci_stack.control && hci_stack.control->off){
+ (*hci_stack.control->off)(hci_stack.config);
+ }
+ hci_emit_hci_open_failed();
+ return err;
+ }
+ return 0;
+}
+
+static void hci_power_control_off(void){
+
+ log_info("hci_power_control_off\n");
+
+ // close low-level device
+ hci_stack.hci_transport->close(hci_stack.config);
+
+ log_info("hci_power_control_off - hci_transport closed\n");
+
+ // power off
+ if (hci_stack.control && hci_stack.control->off){
+ (*hci_stack.control->off)(hci_stack.config);
+ }
+
+ log_info("hci_power_control_off - control closed\n");
+
+ hci_stack.state = HCI_STATE_OFF;
+}
+
+static void hci_power_control_sleep(void){
+
+ log_info("hci_power_control_sleep\n");
+
+#if 0
+ // don't close serial port during sleep
+
+ // close low-level device
+ hci_stack.hci_transport->close(hci_stack.config);
+#endif
+
+ // sleep mode
+ if (hci_stack.control && hci_stack.control->sleep){
+ (*hci_stack.control->sleep)(hci_stack.config);
+ }
+
+ hci_stack.state = HCI_STATE_SLEEPING;
+}
+
+static int hci_power_control_wake(void){
+
+ log_info("hci_power_control_wake\n");
+
+ // wake on
+ if (hci_stack.control && hci_stack.control->wake){
+ (*hci_stack.control->wake)(hci_stack.config);
+ }
+
+#if 0
+ // open low-level device
+ int err = hci_stack.hci_transport->open(hci_stack.config);
+ if (err){
+ log_error( "HCI_INIT failed, turning Bluetooth off again\n");
+ if (hci_stack.control && hci_stack.control->off){
+ (*hci_stack.control->off)(hci_stack.config);
+ }
+ hci_emit_hci_open_failed();
+ return err;
+ }
+#endif
+
+ return 0;
+}
+
+
+int hci_power_control(HCI_POWER_MODE power_mode){
+
+ log_info("hci_power_control: %u, current mode %u\n", power_mode, hci_stack.state);
+
+ int err = 0;
+ switch (hci_stack.state){
+
+ case HCI_STATE_OFF:
+ switch (power_mode){
+ case HCI_POWER_ON:
+ err = hci_power_control_on();
+ if (err) return err;
+ // set up state machine
+ hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent
+ hci_stack.state = HCI_STATE_INITIALIZING;
+ hci_stack.substate = 0;
+ break;
+ case HCI_POWER_OFF:
+ // do nothing
+ break;
+ case HCI_POWER_SLEEP:
+ // do nothing (with SLEEP == OFF)
+ break;
+ }
+ break;
+
+ case HCI_STATE_INITIALIZING:
+ switch (power_mode){
+ case HCI_POWER_ON:
+ // do nothing
+ break;
+ case HCI_POWER_OFF:
+ // no connections yet, just turn it off
+ hci_power_control_off();
+ break;
+ case HCI_POWER_SLEEP:
+ // no connections yet, just turn it off
+ hci_power_control_sleep();
+ break;
+ }
+ break;
+
+ case HCI_STATE_WORKING:
+ switch (power_mode){
+ case HCI_POWER_ON:
+ // do nothing
+ break;
+ case HCI_POWER_OFF:
+ // see hci_run
+ hci_stack.state = HCI_STATE_HALTING;
+ break;
+ case HCI_POWER_SLEEP:
+ // see hci_run
+ hci_stack.state = HCI_STATE_FALLING_ASLEEP;
+ hci_stack.substate = 0;
+ break;
+ }
+ break;
+
+ case HCI_STATE_HALTING:
+ switch (power_mode){
+ case HCI_POWER_ON:
+ // set up state machine
+ hci_stack.state = HCI_STATE_INITIALIZING;
+ hci_stack.substate = 0;
+ break;
+ case HCI_POWER_OFF:
+ // do nothing
+ break;
+ case HCI_POWER_SLEEP:
+ // see hci_run
+ hci_stack.state = HCI_STATE_FALLING_ASLEEP;
+ hci_stack.substate = 0;
+ break;
+ }
+ break;
+
+ case HCI_STATE_FALLING_ASLEEP:
+ switch (power_mode){
+ case HCI_POWER_ON:
+
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+ // nothing to do, if H4 supports power management
+ if (bt_control_iphone_power_management_enabled()){
+ hci_stack.state = HCI_STATE_INITIALIZING;
+ hci_stack.substate = 6;
+ break;
+ }
+#endif
+ // set up state machine
+ hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent
+ hci_stack.state = HCI_STATE_INITIALIZING;
+ hci_stack.substate = 0;
+ break;
+ case HCI_POWER_OFF:
+ // see hci_run
+ hci_stack.state = HCI_STATE_HALTING;
+ break;
+ case HCI_POWER_SLEEP:
+ // do nothing
+ break;
+ }
+ break;
+
+ case HCI_STATE_SLEEPING:
+ switch (power_mode){
+ case HCI_POWER_ON:
+
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+ // nothing to do, if H4 supports power management
+ if (bt_control_iphone_power_management_enabled()){
+ hci_stack.state = HCI_STATE_INITIALIZING;
+ hci_stack.substate = 6;
+ hci_update_scan_enable();
+ break;
+ }
+#endif
+ err = hci_power_control_wake();
+ if (err) return err;
+ // set up state machine
+ hci_stack.num_cmd_packets = 1; // assume that one cmd can be sent
+ hci_stack.state = HCI_STATE_INITIALIZING;
+ hci_stack.substate = 0;
+ break;
+ case HCI_POWER_OFF:
+ hci_stack.state = HCI_STATE_HALTING;
+ break;
+ case HCI_POWER_SLEEP:
+ // do nothing
+ break;
+ }
+ break;
+ }
+
+ // create internal event
+ hci_emit_state();
+
+ // trigger next/first action
+ hci_run();
+
+ return 0;
+}
+
+static void hci_update_scan_enable(void){
+ // 2 = page scan, 1 = inq scan
+ hci_stack.new_scan_enable_value = hci_stack.connectable << 1 | hci_stack.discoverable;
+ hci_run();
+}
+
+void hci_discoverable_control(uint8_t enable){
+ if (enable) enable = 1; // normalize argument
+
+ if (hci_stack.discoverable == enable){
+ hci_emit_discoverable_enabled(hci_stack.discoverable);
+ return;
+ }
+
+ hci_stack.discoverable = enable;
+ hci_update_scan_enable();
+}
+
+void hci_connectable_control(uint8_t enable){
+ if (enable) enable = 1; // normalize argument
+
+ // don't emit event
+ if (hci_stack.connectable == enable) return;
+
+ hci_stack.connectable = enable;
+ hci_update_scan_enable();
+}
+
+void hci_run(){
+
+ hci_connection_t * connection;
+ linked_item_t * it;
+
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ // global/non-connection oriented commands
+
+ // decline incoming connections
+ if (hci_stack.decline_reason){
+ uint8_t reason = hci_stack.decline_reason;
+ hci_stack.decline_reason = 0;
+ hci_send_cmd(&hci_reject_connection_request, hci_stack.decline_addr, reason);
+ }
+
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ // send scan enable
+ if (hci_stack.new_scan_enable_value != 0xff){
+ hci_send_cmd(&hci_write_scan_enable, hci_stack.new_scan_enable_value);
+ hci_stack.new_scan_enable_value = 0xff;
+ }
+
+ // send pending HCI commands
+ for (it = (linked_item_t *) hci_stack.connections; it ; it = it->next){
+
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ connection = (hci_connection_t *) it;
+
+ if (connection->state == RECEIVED_CONNECTION_REQUEST){
+ log_info("sending hci_accept_connection_request\n");
+ hci_send_cmd(&hci_accept_connection_request, connection->address, 1);
+ connection->state = ACCEPTED_CONNECTION_REQUEST;
+ }
+
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ if (connection->authentication_flags & HANDLE_LINK_KEY_REQUEST){
+ link_key_t link_key;
+ log_info("responding to link key request\n");
+ if (hci_stack.remote_device_db->get_link_key( &connection->address, &link_key)){
+ hci_send_cmd(&hci_link_key_request_reply, connection->address, &link_key);
+ } else {
+ hci_send_cmd(&hci_link_key_request_negative_reply, connection->address);
+ }
+ connectionClearAuthenticationFlags(connection, HANDLE_LINK_KEY_REQUEST);
+ }
+ }
+
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ switch (hci_stack.state){
+ case HCI_STATE_INITIALIZING:
+ // log_info("hci_init: substate %u\n", hci_stack.substate);
+ if (hci_stack.substate % 2) {
+ // odd: waiting for command completion
+ return;
+ }
+ switch (hci_stack.substate >> 1){
+ case 0: // RESET
+ hci_send_cmd(&hci_reset);
+ if (hci_stack.config == 0 || ((hci_uart_config_t *)hci_stack.config)->baudrate_main == 0){
+ // skip baud change
+ hci_stack.substate = 4; // >> 1 = 2
+ }
+ break;
+ case 1: // SEND BAUD CHANGE
+ hci_stack.control->baudrate_cmd(hci_stack.config, ((hci_uart_config_t *)hci_stack.config)->baudrate_main, hci_stack.hci_packet_buffer);
+ hci_send_cmd_packet(hci_stack.hci_packet_buffer, 3 + hci_stack.hci_packet_buffer[2]);
+ break;
+ case 2: // LOCAL BAUD CHANGE
+ hci_stack.hci_transport->set_baudrate(((hci_uart_config_t *)hci_stack.config)->baudrate_main);
+ hci_stack.substate += 2;
+ // break missing here for fall through
+
+ case 3:
+ // custom initialization
+ if (hci_stack.control && hci_stack.control->next_cmd){
+ int valid_cmd = (*hci_stack.control->next_cmd)(hci_stack.config, hci_stack.hci_packet_buffer);
+ if (valid_cmd){
+ int size = 3 + hci_stack.hci_packet_buffer[2];
+ hci_stack.hci_transport->send_packet(HCI_COMMAND_DATA_PACKET, hci_stack.hci_packet_buffer, size);
+ hci_stack.substate = 4; // more init commands
+ break;
+ }
+ 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;
+ 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 7:
+#ifndef EMBEDDED
+ {
+ char hostname[30];
+ gethostname(hostname, 30);
+ hostname[29] = '\0';
+ hci_send_cmd(&hci_write_local_name, hostname);
+ break;
+ }
+ case 8:
+#ifdef USE_BLUETOOL
+ hci_send_cmd(&hci_write_class_of_device, 0x007a020c); // Smartphone
+ break;
+
+ case 9:
+#endif
+#endif
+ // done.
+ hci_stack.state = HCI_STATE_WORKING;
+ hci_emit_state();
+ break;
+ default:
+ break;
+ }
+ hci_stack.substate++;
+ break;
+
+ case HCI_STATE_HALTING:
+
+ log_info("HCI_STATE_HALTING\n");
+ // close all open connections
+ connection = (hci_connection_t *) hci_stack.connections;
+ if (connection){
+
+ // send disconnect
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ log_info("HCI_STATE_HALTING, connection %p, handle %u\n", connection, (uint16_t)connection->con_handle);
+ hci_send_cmd(&hci_disconnect, connection->con_handle, 0x13); // remote closed connection
+
+ // send disconnected event right away - causes higher layer connections to get closed, too.
+ hci_shutdown_connection(connection);
+ return;
+ }
+ log_info("HCI_STATE_HALTING, calling off\n");
+
+ // switch mode
+ hci_power_control_off();
+
+ log_info("HCI_STATE_HALTING, emitting state\n");
+ hci_emit_state();
+ log_info("HCI_STATE_HALTING, done\n");
+ break;
+
+ case HCI_STATE_FALLING_ASLEEP:
+ switch(hci_stack.substate) {
+ case 0:
+ log_info("HCI_STATE_FALLING_ASLEEP\n");
+ // close all open connections
+ connection = (hci_connection_t *) hci_stack.connections;
+
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+ // don't close connections, if H4 supports power management
+ if (bt_control_iphone_power_management_enabled()){
+ connection = NULL;
+ }
+#endif
+ if (connection){
+
+ // send disconnect
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ log_info("HCI_STATE_FALLING_ASLEEP, connection %p, handle %u\n", connection, (uint16_t)connection->con_handle);
+ hci_send_cmd(&hci_disconnect, connection->con_handle, 0x13); // remote closed connection
+
+ // send disconnected event right away - causes higher layer connections to get closed, too.
+ hci_shutdown_connection(connection);
+ return;
+ }
+
+ // disable page and inquiry scan
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) return;
+
+ log_info("HCI_STATE_HALTING, disabling inq cans\n");
+ hci_send_cmd(&hci_write_scan_enable, hci_stack.connectable << 1); // drop inquiry scan but keep page scan
+
+ // continue in next sub state
+ hci_stack.substate++;
+ break;
+ case 1:
+ // wait for command complete "hci_write_scan_enable" in event_handler();
+ break;
+ case 2:
+ log_info("HCI_STATE_HALTING, calling sleep\n");
+#if defined(USE_POWERMANAGEMENT) && defined(USE_BLUETOOL)
+ // don't actually go to sleep, if H4 supports power management
+ if (bt_control_iphone_power_management_enabled()){
+ // SLEEP MODE reached
+ hci_stack.state = HCI_STATE_SLEEPING;
+ hci_emit_state();
+ break;
+ }
+#endif
+ // switch mode
+ hci_power_control_sleep(); // changes hci_stack.state to SLEEP
+ hci_emit_state();
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+int hci_send_cmd_packet(uint8_t *packet, int size){
+ bd_addr_t addr;
+ hci_connection_t * conn;
+ // house-keeping
+
+ // create_connection?
+ if (IS_COMMAND(packet, hci_create_connection)){
+ bt_flip_addr(addr, &packet[3]);
+ log_info("Create_connection to %s\n", bd_addr_to_str(addr));
+ conn = connection_for_address(addr);
+ if (conn) {
+ // if connection exists
+ if (conn->state == OPEN) {
+ // and OPEN, emit connection complete command
+ hci_emit_connection_complete(conn, 0);
+ }
+ // otherwise, just ignore as it is already in the open process
+ return 0; // don't sent packet to controller
+
+ }
+ // create connection struct and register, state = SENT_CREATE_CONNECTION
+ conn = create_connection_for_addr(addr);
+ if (!conn){
+ // notify client that alloc failed
+ hci_emit_connection_complete(conn, BTSTACK_MEMORY_ALLOC_FAILED);
+ return 0; // don't sent packet to controller
+ }
+ conn->state = SENT_CREATE_CONNECTION;
+ }
+
+ if (IS_COMMAND(packet, hci_link_key_request_reply)){
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_LINK_KEY_REPLY);
+ }
+ if (IS_COMMAND(packet, hci_link_key_request_negative_reply)){
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_LINK_KEY_NEGATIVE_REQUEST);
+ }
+ if (IS_COMMAND(packet, hci_pin_code_request_reply)){
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_PIN_CODE_REPLY);
+ }
+ if (IS_COMMAND(packet, hci_pin_code_request_negative_reply)){
+ hci_add_connection_flags_for_flipped_bd_addr(&packet[3], SENT_PIN_CODE_NEGATIVE_REPLY);
+ }
+
+ if (IS_COMMAND(packet, hci_delete_stored_link_key)){
+ if (hci_stack.remote_device_db){
+ bt_flip_addr(addr, &packet[3]);
+ hci_stack.remote_device_db->delete_link_key(&addr);
+ }
+ }
+
+ hci_stack.num_cmd_packets--;
+ return hci_stack.hci_transport->send_packet(HCI_COMMAND_DATA_PACKET, packet, size);
+}
+
+/**
+ * pre: numcmds >= 0 - it's allowed to send a command to the controller
+ */
+int hci_send_cmd(const hci_cmd_t *cmd, ...){
+ va_list argptr;
+ va_start(argptr, cmd);
+ uint16_t size = hci_create_cmd_internal(hci_stack.hci_packet_buffer, cmd, argptr);
+ va_end(argptr);
+ return hci_send_cmd_packet(hci_stack.hci_packet_buffer, size);
+}
+
+// Create various non-HCI events.
+// TODO: generalize, use table similar to hci_create_command
+
+void hci_emit_state(){
+ uint8_t event[3];
+ event[0] = BTSTACK_EVENT_STATE;
+ event[1] = sizeof(event) - 2;
+ event[2] = hci_stack.state;
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status){
+ uint8_t event[13];
+ event[0] = HCI_EVENT_CONNECTION_COMPLETE;
+ event[1] = sizeof(event) - 2;
+ event[2] = status;
+ bt_store_16(event, 3, conn->con_handle);
+ bt_flip_addr(&event[5], conn->address);
+ event[11] = 1; // ACL connection
+ event[12] = 0; // encryption disabled
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason){
+ uint8_t event[6];
+ event[0] = HCI_EVENT_DISCONNECTION_COMPLETE;
+ event[1] = sizeof(event) - 2;
+ event[2] = 0; // status = OK
+ bt_store_16(event, 3, handle);
+ event[5] = reason;
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_l2cap_check_timeout(hci_connection_t *conn){
+ uint8_t event[4];
+ event[0] = L2CAP_EVENT_TIMEOUT_CHECK;
+ event[1] = sizeof(event) - 2;
+ bt_store_16(event, 2, conn->con_handle);
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_nr_connections_changed(){
+ uint8_t event[3];
+ event[0] = BTSTACK_EVENT_NR_CONNECTIONS_CHANGED;
+ event[1] = sizeof(event) - 2;
+ event[2] = nr_hci_connections();
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_hci_open_failed(){
+ uint8_t event[2];
+ event[0] = BTSTACK_EVENT_POWERON_FAILED;
+ event[1] = sizeof(event) - 2;
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+#ifndef EMBEDDED
+void hci_emit_btstack_version() {
+ uint8_t event[6];
+ event[0] = BTSTACK_EVENT_VERSION;
+ event[1] = sizeof(event) - 2;
+ event[2] = BTSTACK_MAJOR;
+ event[3] = BTSTACK_MINOR;
+ bt_store_16(event, 4, BTSTACK_REVISION);
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+#endif
+
+void hci_emit_system_bluetooth_enabled(uint8_t enabled){
+ uint8_t event[3];
+ event[0] = BTSTACK_EVENT_SYSTEM_BLUETOOTH_ENABLED;
+ event[1] = sizeof(event) - 2;
+ event[2] = enabled;
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name){
+ uint8_t event[2+1+6+248];
+ event[0] = BTSTACK_EVENT_REMOTE_NAME_CACHED;
+ event[1] = sizeof(event) - 2;
+ event[2] = 0; // just to be compatible with HCI_EVENT_REMOTE_NAME_REQUEST_COMPLETE
+ bt_flip_addr(&event[3], *addr);
+ memcpy(&event[9], name, 248);
+ hci_dump_packet(HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void hci_emit_discoverable_enabled(uint8_t enabled){
+ uint8_t event[3];
+ event[0] = BTSTACK_EVENT_DISCOVERABLE_ENABLED;
+ event[1] = sizeof(event) - 2;
+ event[2] = enabled;
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ hci_stack.packet_handler(HCI_EVENT_PACKET, event, sizeof(event));
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hci.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,359 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * hci.h
+ *
+ * Created by Matthias Ringwald on 4/29/09.
+ *
+ */
+
+#pragma once
+
+#include "config.h"
+
+#include <btstack/hci_cmds.h>
+#include <btstack/utils.h>
+#include "hci_transport.h"
+#include "bt_control.h"
+#include "remote_device_db.h"
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdarg.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+// packet header sizes
+#define HCI_CMD_HEADER_SIZE 3
+#define HCI_ACL_HEADER_SIZE 4
+#define HCI_SCO_HEADER_SIZE 3
+#define HCI_EVENT_HEADER_SIZE 2
+
+// packet sizes (max payload)
+#define HCI_ACL_DM1_SIZE 17
+#define HCI_ACL_DH1_SIZE 27
+#define HCI_ACL_2DH1_SIZE 54
+#define HCI_ACL_3DH1_SIZE 83
+#define HCI_ACL_DM3_SIZE 121
+#define HCI_ACL_DH3_SIZE 183
+#define HCI_ACL_DM5_SIZE 224
+#define HCI_ACL_DH5_SIZE 339
+#define HCI_ACL_2DH3_SIZE 367
+#define HCI_ACL_3DH3_SIZE 552
+#define HCI_ACL_2DH5_SIZE 679
+#define HCI_ACL_3DH5_SIZE 1021
+
+#define HCI_EVENT_PAYLOAD_SIZE 255
+#define HCI_CMD_PAYLOAD_SIZE 255
+
+// packet buffer sizes
+// HCI_ACL_PAYLOAD_SIZE is configurable and defined in config.h
+#define HCI_EVENT_BUFFER_SIZE (HCI_EVENT_HEADER_SIZE + HCI_EVENT_PAYLOAD_SIZE)
+#define HCI_CMD_BUFFER_SIZE (HCI_CMD_HEADER_SIZE + HCI_CMD_PAYLOAD_SIZE)
+#define HCI_ACL_BUFFER_SIZE (HCI_ACL_HEADER_SIZE + HCI_ACL_PAYLOAD_SIZE)
+
+// size of hci buffers, big enough for command, event, or acl packet without H4 packet type
+// @note cmd buffer is bigger than event buffer
+#if HCI_ACL_BUFFER_SIZE > HCI_CMD_BUFFER_SIZE
+#define HCI_PACKET_BUFFER_SIZE HCI_ACL_BUFFER_SIZE
+#else
+#define HCI_PACKET_BUFFER_SIZE HCI_CMD_BUFFER_SIZE
+#endif
+
+// OGFs
+#define OGF_LINK_CONTROL 0x01
+#define OGF_LINK_POLICY 0x02
+#define OGF_CONTROLLER_BASEBAND 0x03
+#define OGF_INFORMATIONAL_PARAMETERS 0x04
+#define OGF_LE_CONTROLLER 0x08
+#define OGF_BTSTACK 0x3d
+#define OGF_VENDOR 0x3f
+
+// cmds for BTstack
+// get state: @returns HCI_STATE
+#define BTSTACK_GET_STATE 0x01
+
+// set power mode: @param HCI_POWER_MODE
+#define BTSTACK_SET_POWER_MODE 0x02
+
+// set capture mode: @param on
+#define BTSTACK_SET_ACL_CAPTURE_MODE 0x03
+
+// get BTstack version
+#define BTSTACK_GET_VERSION 0x04
+
+// get system Bluetooth state
+#define BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED 0x05
+
+// set system Bluetooth state
+#define BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED 0x06
+
+// enable inquiry scan for this client
+#define BTSTACK_SET_DISCOVERABLE 0x07
+
+// set global Bluetooth state
+#define BTSTACK_SET_BLUETOOTH_ENABLED 0x08
+
+// create l2cap channel: @param bd_addr(48), psm (16)
+#define L2CAP_CREATE_CHANNEL 0x20
+
+// disconnect l2cap disconnect, @param channel(16), reason(8)
+#define L2CAP_DISCONNECT 0x21
+
+// register l2cap service: @param psm(16), mtu (16)
+#define L2CAP_REGISTER_SERVICE 0x22
+
+// unregister l2cap disconnect, @param psm(16)
+#define L2CAP_UNREGISTER_SERVICE 0x23
+
+// accept connection @param bd_addr(48), dest cid (16)
+#define L2CAP_ACCEPT_CONNECTION 0x24
+
+// decline l2cap disconnect,@param bd_addr(48), dest cid (16), reason(8)
+#define L2CAP_DECLINE_CONNECTION 0x25
+
+// create l2cap channel: @param bd_addr(48), psm (16), mtu (16)
+#define L2CAP_CREATE_CHANNEL_MTU 0x26
+
+// register SDP Service Record: service record (size)
+#define SDP_REGISTER_SERVICE_RECORD 0x30
+
+// unregister SDP Service Record
+#define SDP_UNREGISTER_SERVICE_RECORD 0x31
+
+// RFCOMM "HCI" Commands
+#define RFCOMM_CREATE_CHANNEL 0x40
+#define RFCOMM_DISCONNECT 0x41
+#define RFCOMM_REGISTER_SERVICE 0x42
+#define RFCOMM_UNREGISTER_SERVICE 0x43
+#define RFCOMM_ACCEPT_CONNECTION 0x44
+#define RFCOMM_DECLINE_CONNECTION 0x45
+#define RFCOMM_PERSISTENT_CHANNEL 0x46
+#define RFCOMM_CREATE_CHANNEL_WITH_CREDITS 0x47
+#define RFCOMM_REGISTER_SERVICE_WITH_CREDITS 0x48
+#define RFCOMM_GRANT_CREDITS 0x49
+
+//
+#define IS_COMMAND(packet, command) (READ_BT_16(packet,0) == command.opcode)
+
+// data: event(8)
+#define DAEMON_EVENT_CONNECTION_OPENED 0x50
+
+// data: event(8)
+#define DAEMON_EVENT_CONNECTION_CLOSED 0x51
+
+// data: event(8), nr_connections(8)
+#define DAEMON_NR_CONNECTIONS_CHANGED 0x52
+
+// data: event(8)
+#define DAEMON_EVENT_NEW_RFCOMM_CREDITS 0x53
+
+// data: event()
+#define DAEMON_EVENT_HCI_PACKET_SENT 0x54
+
+/**
+ * Connection State
+ */
+typedef enum {
+ AUTH_FLAGS_NONE = 0x00,
+ RECV_LINK_KEY_REQUEST = 0x01,
+ HANDLE_LINK_KEY_REQUEST = 0x02,
+ SENT_LINK_KEY_REPLY = 0x04,
+ SENT_LINK_KEY_NEGATIVE_REQUEST = 0x08,
+ RECV_LINK_KEY_NOTIFICATION = 0x10,
+ RECV_PIN_CODE_REQUEST = 0x20,
+ SENT_PIN_CODE_REPLY = 0x40,
+ SENT_PIN_CODE_NEGATIVE_REPLY = 0x80
+} hci_authentication_flags_t;
+
+typedef enum {
+ SENT_CREATE_CONNECTION = 1,
+ RECEIVED_CONNECTION_REQUEST,
+ ACCEPTED_CONNECTION_REQUEST,
+ REJECTED_CONNECTION_REQUEST,
+ OPEN,
+ SENT_DISCONNECT
+} CONNECTION_STATE;
+
+typedef enum {
+ BLUETOOTH_OFF = 1,
+ BLUETOOTH_ON,
+ BLUETOOTH_ACTIVE
+} BLUETOOTH_STATE;
+
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ // remote side
+ bd_addr_t address;
+
+ // module handle
+ hci_con_handle_t con_handle;
+
+ // state
+ CONNECTION_STATE state;
+
+ // errands
+ hci_authentication_flags_t authentication_flags;
+
+ timer_source_t timeout;
+
+#ifdef HAVE_TIME
+ // timer
+ struct timeval timestamp;
+#endif
+#ifdef HAVE_TICK
+ uint32_t timestamp; // timeout in system ticks
+#endif
+
+ // ACL packet recombination - ACL Header + ACL payload
+ uint8_t acl_recombination_buffer[4 + HCI_ACL_BUFFER_SIZE];
+ uint16_t acl_recombination_pos;
+ uint16_t acl_recombination_length;
+
+ // number ACL packets sent to controller
+ uint8_t num_acl_packets_sent;
+
+} hci_connection_t;
+
+/**
+ * main data structure
+ */
+typedef struct {
+ // transport component with configuration
+ hci_transport_t * hci_transport;
+ void * config;
+
+ // hardware power controller
+ bt_control_t * control;
+
+ // list of existing baseband connections
+ linked_list_t connections;
+
+ // single buffer for HCI Command assembly
+ uint8_t hci_packet_buffer[HCI_PACKET_BUFFER_SIZE]; // opcode (16), len(8)
+
+ /* host to controller flow control */
+ uint8_t num_cmd_packets;
+ // uint8_t total_num_cmd_packets;
+ uint8_t total_num_acl_packets;
+ uint16_t acl_data_packet_length;
+
+ // usable packet types given acl_data_packet_length and HCI_ACL_BUFFER_SIZE
+ uint16_t packet_types;
+
+ /* callback to L2CAP layer */
+ void (*packet_handler)(uint8_t packet_type, uint8_t *packet, uint16_t size);
+
+ /* remote device db */
+ remote_device_db_t const*remote_device_db;
+
+ /* hci state machine */
+ HCI_STATE state;
+ uint8_t substate;
+ uint8_t cmds_ready;
+
+ uint8_t discoverable;
+ uint8_t connectable;
+
+ /* buffer for scan enable cmd - 0xff no change */
+ uint8_t new_scan_enable_value;
+
+ // buffer for single connection decline
+ uint8_t decline_reason;
+ bd_addr_t decline_addr;
+
+} hci_stack_t;
+
+// create and send hci command packets based on a template and a list of parameters
+uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...);
+uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr);
+
+// set up HCI
+void hci_init(hci_transport_t *transport, void *config, bt_control_t *control, remote_device_db_t const* remote_device_db);
+void hci_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
+void hci_close(void);
+
+// power and inquriy scan control
+int hci_power_control(HCI_POWER_MODE mode);
+void hci_discoverable_control(uint8_t enable);
+void hci_connectable_control(uint8_t enable);
+
+/**
+ * run the hci control loop once
+ */
+void hci_run(void);
+
+// create and send hci command packets based on a template and a list of parameters
+int hci_send_cmd(const hci_cmd_t *cmd, ...);
+
+// send complete CMD packet
+int hci_send_cmd_packet(uint8_t *packet, int size);
+
+// send ACL packet
+int hci_send_acl_packet(uint8_t *packet, int size);
+
+// non-blocking UART driver needs
+int hci_can_send_packet_now(uint8_t packet_type);
+
+hci_connection_t * connection_for_handle(hci_con_handle_t con_handle);
+uint8_t hci_number_outgoing_packets(hci_con_handle_t handle);
+uint8_t hci_number_free_acl_slots(void);
+int hci_authentication_active_for_handle(hci_con_handle_t handle);
+void hci_drop_link_key_for_bd_addr(bd_addr_t *addr);
+uint16_t hci_max_acl_data_packet_length(void);
+uint16_t hci_usable_acl_packet_types(void);
+uint8_t* hci_get_outgoing_acl_packet_buffer(void);
+
+//
+void hci_emit_state(void);
+void hci_emit_connection_complete(hci_connection_t *conn, uint8_t status);
+void hci_emit_l2cap_check_timeout(hci_connection_t *conn);
+void hci_emit_disconnection_complete(uint16_t handle, uint8_t reason);
+void hci_emit_nr_connections_changed(void);
+void hci_emit_hci_open_failed(void);
+void hci_emit_btstack_version(void);
+void hci_emit_system_bluetooth_enabled(uint8_t enabled);
+void hci_emit_remote_name_cached(bd_addr_t *addr, device_name_t *name);
+void hci_emit_discoverable_enabled(uint8_t enabled);
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hci_cmds.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,679 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * hci_cmds.c
+ *
+ * Created by Matthias Ringwald on 7/23/09.
+ */
+
+#include <btstack/hci_cmds.h>
+
+#include <string.h>
+
+#include <btstack/sdp_util.h>
+#include "config.h"
+#include "hci.h"
+
+// calculate combined ogf/ocf value
+#define OPCODE(ogf, ocf) (ocf | ogf << 10)
+
+/**
+ * construct HCI Command based on template
+ *
+ * Format:
+ * 1,2,3,4: one to four byte value
+ * H: HCI connection handle
+ * B: Bluetooth Baseband Address (BD_ADDR)
+ * E: Extended Inquiry Result
+ * N: Name up to 248 chars, \0 terminated
+ * P: 16 byte Pairing code
+ * S: Service Record (Data Element Sequence)
+ */
+uint16_t hci_create_cmd_internal(uint8_t *hci_cmd_buffer, const hci_cmd_t *cmd, va_list argptr){
+
+ hci_cmd_buffer[0] = cmd->opcode & 0xff;
+ hci_cmd_buffer[1] = cmd->opcode >> 8;
+ int pos = 3;
+
+ const char *format = cmd->format;
+ uint16_t word;
+ uint32_t longword;
+ uint8_t * ptr;
+ while (*format) {
+ switch(*format) {
+ case '1': // 8 bit value
+ case '2': // 16 bit value
+ case 'H': // hci_handle
+ word = va_arg(argptr, int); // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
+ hci_cmd_buffer[pos++] = word & 0xff;
+ if (*format == '2') {
+ hci_cmd_buffer[pos++] = word >> 8;
+ } else if (*format == 'H') {
+ // TODO implement opaque client connection handles
+ // pass module handle for now
+ hci_cmd_buffer[pos++] = word >> 8;
+ }
+ break;
+ case '3':
+ case '4':
+ longword = va_arg(argptr, uint32_t);
+ // longword = va_arg(argptr, int);
+ hci_cmd_buffer[pos++] = longword;
+ hci_cmd_buffer[pos++] = longword >> 8;
+ hci_cmd_buffer[pos++] = longword >> 16;
+ if (*format == '4'){
+ hci_cmd_buffer[pos++] = longword >> 24;
+ }
+ break;
+ case 'B': // bt-addr
+ ptr = va_arg(argptr, uint8_t *);
+ hci_cmd_buffer[pos++] = ptr[5];
+ hci_cmd_buffer[pos++] = ptr[4];
+ hci_cmd_buffer[pos++] = ptr[3];
+ hci_cmd_buffer[pos++] = ptr[2];
+ hci_cmd_buffer[pos++] = ptr[1];
+ hci_cmd_buffer[pos++] = ptr[0];
+ break;
+ case 'E': // Extended Inquiry Information 240 octets
+ ptr = va_arg(argptr, uint8_t *);
+ memcpy(&hci_cmd_buffer[pos], ptr, 240);
+ pos += 240;
+ break;
+ case 'N': { // UTF-8 string, null terminated
+ ptr = va_arg(argptr, uint8_t *);
+ uint16_t len = strlen((const char*) ptr);
+ if (len > 248) {
+ len = 248;
+ }
+ memcpy(&hci_cmd_buffer[pos], ptr, len);
+ if (len < 248) {
+ // fill remaining space with zeroes
+ memset(&hci_cmd_buffer[pos+len], 0, 248-len);
+ }
+ pos += 248;
+ break;
+ }
+ case 'P': // 16 byte PIN code or link key
+ ptr = va_arg(argptr, uint8_t *);
+ memcpy(&hci_cmd_buffer[pos], ptr, 16);
+ pos += 16;
+ break;
+#ifdef HAVE_BLE
+ case 'A': // 31 bytes advertising data
+ ptr = va_arg(argptr, uint8_t *);
+ memcpy(&hci_cmd_buffer[pos], ptr, 31);
+ pos += 31;
+ break;
+#endif
+#ifdef HAVE_SDP
+ case 'S': { // Service Record (Data Element Sequence)
+ ptr = va_arg(argptr, uint8_t *);
+ uint16_t len = de_get_len(ptr);
+ memcpy(&hci_cmd_buffer[pos], ptr, len);
+ pos += len;
+ break;
+ }
+#endif
+ default:
+ break;
+ }
+ format++;
+ };
+ hci_cmd_buffer[2] = pos - 3;
+ return pos;
+}
+
+/**
+ * construct HCI Command based on template
+ *
+ * mainly calls hci_create_cmd_internal
+ */
+uint16_t hci_create_cmd(uint8_t *hci_cmd_buffer, hci_cmd_t *cmd, ...){
+ va_list argptr;
+ va_start(argptr, cmd);
+ uint16_t len = hci_create_cmd_internal(hci_cmd_buffer, cmd, argptr);
+ va_end(argptr);
+ return len;
+}
+
+
+/**
+ * Link Control Commands
+ */
+const hci_cmd_t hci_inquiry = {
+OPCODE(OGF_LINK_CONTROL, 0x01), "311"
+// LAP, Inquiry length, Num_responses
+};
+const hci_cmd_t hci_inquiry_cancel = {
+OPCODE(OGF_LINK_CONTROL, 0x02), ""
+// no params
+};
+const hci_cmd_t hci_create_connection = {
+OPCODE(OGF_LINK_CONTROL, 0x05), "B21121"
+// BD_ADDR, Packet_Type, Page_Scan_Repetition_Mode, Reserved, Clock_Offset, Allow_Role_Switch
+};
+const hci_cmd_t hci_disconnect = {
+OPCODE(OGF_LINK_CONTROL, 0x06), "H1"
+// Handle, Reason: 0x05, 0x13-0x15, 0x1a, 0x29
+// see Errors Codes in BT Spec Part D
+};
+const hci_cmd_t hci_create_connection_cancel = {
+OPCODE(OGF_LINK_CONTROL, 0x08), "B"
+// BD_ADDR
+};
+const hci_cmd_t hci_accept_connection_request = {
+OPCODE(OGF_LINK_CONTROL, 0x09), "B1"
+// BD_ADDR, Role: become master, stay slave
+};
+const hci_cmd_t hci_reject_connection_request = {
+OPCODE(OGF_LINK_CONTROL, 0x0a), "B1"
+// BD_ADDR, reason e.g. CONNECTION REJECTED DUE TO LIMITED RESOURCES (0x0d)
+};
+const hci_cmd_t hci_link_key_request_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0b), "BP"
+// BD_ADDR, LINK_KEY
+};
+const hci_cmd_t hci_link_key_request_negative_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0c), "B"
+// BD_ADDR
+};
+const hci_cmd_t hci_pin_code_request_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0d), "B1P"
+// BD_ADDR, pin length, PIN: c-string
+};
+const hci_cmd_t hci_pin_code_request_negative_reply = {
+OPCODE(OGF_LINK_CONTROL, 0x0e), "B"
+// BD_ADDR
+};
+const hci_cmd_t hci_authentication_requested = {
+OPCODE(OGF_LINK_CONTROL, 0x11), "H"
+// Handle
+};
+const hci_cmd_t hci_set_connection_encryption = {
+OPCODE(OGF_LINK_CONTROL, 0x13), "H1"
+// Handle, Encryption_Enable
+};
+const hci_cmd_t hci_change_connection_link_key = {
+OPCODE(OGF_LINK_CONTROL, 0x15), "H"
+// Handle
+};
+const hci_cmd_t hci_remote_name_request = {
+OPCODE(OGF_LINK_CONTROL, 0x19), "B112"
+// BD_ADDR, Page_Scan_Repetition_Mode, Reserved, Clock_Offset
+};
+const hci_cmd_t hci_remote_name_request_cancel = {
+OPCODE(OGF_LINK_CONTROL, 0x1A), "B"
+// BD_ADDR
+};
+
+/**
+ * Link Policy Commands
+ */
+const hci_cmd_t hci_sniff_mode = {
+OPCODE(OGF_LINK_POLICY, 0x03), "H2222"
+// handle, Sniff_Max_Interval, Sniff_Min_Interval, Sniff_Attempt, Sniff_Timeout:
+};
+const hci_cmd_t hci_qos_setup = {
+OPCODE(OGF_LINK_POLICY, 0x07), "H114444"
+// handle, flags, service_type, token rate (bytes/s), peak bandwith (bytes/s),
+// latency (us), delay_variation (us)
+};
+const hci_cmd_t hci_role_discovery = {
+OPCODE(OGF_LINK_POLICY, 0x09), "H"
+// handle
+};
+const hci_cmd_t hci_switch_role_command= {
+OPCODE(OGF_LINK_POLICY, 0x0b), "B1"
+// BD_ADDR, role: {0=master,1=slave}
+};
+const hci_cmd_t hci_read_link_policy_settings = {
+OPCODE(OGF_LINK_POLICY, 0x0c), "H"
+// handle
+};
+const hci_cmd_t hci_write_link_policy_settings = {
+OPCODE(OGF_LINK_POLICY, 0x0d), "H2"
+// handle, settings
+};
+
+/**
+ * Controller & Baseband Commands
+ */
+const hci_cmd_t hci_set_event_mask = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x01), "44"
+// event_mask lower 4 octets, higher 4 bytes
+};
+const hci_cmd_t hci_reset = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x03), ""
+// no params
+};
+const hci_cmd_t hci_delete_stored_link_key = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x12), "B1"
+// BD_ADDR, Delete_All_Flag
+};
+const hci_cmd_t hci_write_local_name = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x13), "N"
+// Local name (UTF-8, Null Terminated, max 248 octets)
+};
+const hci_cmd_t hci_write_page_timeout = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x18), "2"
+// Page_Timeout * 0.625 ms
+};
+const hci_cmd_t hci_write_scan_enable = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x1A), "1"
+// Scan_enable: no, inq, page, inq+page
+};
+const hci_cmd_t hci_write_authentication_enable = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x20), "1"
+// Authentication_Enable
+};
+const hci_cmd_t hci_write_class_of_device = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x24), "3"
+// Class of Device
+};
+const hci_cmd_t hci_read_num_broadcast_retransmissions = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x29), ""
+};
+const hci_cmd_t hci_write_num_broadcast_retransmissions = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x2a), "1"
+// Num broadcast retransmissions (e.g. 0 for a single broadcast)
+};
+const hci_cmd_t hci_host_buffer_size = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x33), "2122"
+// Host_ACL_Data_Packet_Length:, Host_Synchronous_Data_Packet_Length:, Host_Total_Num_ACL_Data_Packets:, Host_Total_Num_Synchronous_Data_Packets:
+};
+const hci_cmd_t hci_read_link_supervision_timeout = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x36), "H"
+// handle
+};
+const hci_cmd_t hci_write_link_supervision_timeout = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x37), "H2"
+// handle, Range for N: 0x0001 Ð 0xFFFF Time (Range: 0.625ms Ð 40.9 sec)
+};
+const hci_cmd_t hci_write_inquiry_mode = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x45), "1"
+// Inquiry mode: 0x00 = standard, 0x01 = with RSSI, 0x02 = extended
+};
+const hci_cmd_t hci_write_extended_inquiry_response = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x52), "1E"
+// FEC_Required, Exstended Inquiry Response
+};
+const hci_cmd_t hci_write_simple_pairing_mode = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x56), "1"
+// mode: 0 = off, 1 = on
+};
+const hci_cmd_t hci_read_le_host_supported = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x6c), ""
+// params: none
+// return: status, le supported host, simultaneous le host
+};
+const hci_cmd_t hci_write_le_host_supported = {
+OPCODE(OGF_CONTROLLER_BASEBAND, 0x6d), "11"
+// param: le supported host, simultaneous le host
+// return: status
+};
+
+/**
+ * Informational Parameters
+ */
+const hci_cmd_t hci_read_local_supported_features = {
+OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x03), ""
+// no params
+};
+const hci_cmd_t hci_read_buffer_size = {
+OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x05), ""
+// no params
+};
+const hci_cmd_t hci_read_bd_addr = {
+OPCODE(OGF_INFORMATIONAL_PARAMETERS, 0x09), ""
+// no params
+};
+
+#ifdef HAVE_BLE
+/**
+ * Low Energy Commands
+ */
+const hci_cmd_t hci_le_set_event_mask = {
+OPCODE(OGF_LE_CONTROLLER, 0x01), "44"
+// params: event_mask lower 4 octets, higher 4 bytes
+// return: status
+};
+const hci_cmd_t hci_le_read_buffer_size = {
+OPCODE(OGF_LE_CONTROLLER, 0x02), ""
+// params: none
+// return: status, le acl data packet len (16), total num le acl data packets(8)
+};
+const hci_cmd_t hci_le_read_supported_features = {
+OPCODE(OGF_LE_CONTROLLER, 0x03), ""
+// params: none
+// return: LE_Features See [Vol 6] Part B, Section 4.6
+};
+const hci_cmd_t hci_le_set_random_address = {
+OPCODE(OGF_LE_CONTROLLER, 0x05), "B"
+// params: random device address
+// return: status
+};
+const hci_cmd_t hci_le_set_advertising_parameters = {
+OPCODE(OGF_LE_CONTROLLER, 0x06), "22111B11"
+// param: min advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
+// param: max advertising interval, [0x0020,0x4000], default: 0x0800, unit: 0.625 msec
+// param: advertising type (enum from 0): ADV_IND, ADC_DIRECT_IND, ADV_SCAN_IND, ADV_NONCONN_IND
+// param: own address type (enum from 0): public device address, random device address
+// param: direct address type (enum from 0): public device address, random device address
+// param: direct address - public or random address of device to be connecteed
+// param: advertising channel map (flags): chan_37(1), chan_38(2), chan_39(4)
+// param: advertising filter policy (enum from 0): scan any conn any, scan whitelist, con any, scan any conn whitelist, scan whitelist, con whitelist
+// return: status
+};
+const hci_cmd_t hci_le_read_advertising_channel_tx_power = {
+OPCODE(OGF_LE_CONTROLLER, 0x07), ""
+// params: none
+// return: status, level [-20,10] signed int (8), units dBm
+};
+const hci_cmd_t hci_le_set_advertising_data= {
+OPCODE(OGF_LE_CONTROLLER, 0x08), "1A"
+// param: advertising data len
+// param: advertising data (31 bytes)
+// return: status
+};
+const hci_cmd_t hci_le_set_scan_response_data= {
+OPCODE(OGF_LE_CONTROLLER, 0x09), "1A"
+// param: scan response data len
+// param: scan response data (31 bytes)
+// return: status
+};
+const hci_cmd_t hci_le_set_advertise_enable = {
+OPCODE(OGF_LE_CONTROLLER, 0x0a), "1"
+// params: avertise enable: off (0), on (1)
+// return: status
+};
+const hci_cmd_t hci_le_set_scan_parameters = {
+OPCODE(OGF_LE_CONTROLLER, 0x0b), "12211"
+// param: le scan type: passive (0), active (1)
+// param: le scan interval [0x0004,0x4000], unit: 0.625 msec
+// param: le scan window [0x0004,0x4000], unit: 0.625 msec
+// param: own address type: public (0), random (1)
+// param: scanning filter policy: any (0), only whitelist (1)
+// return: status
+};
+const hci_cmd_t hci_le_set_scan_enable = {
+OPCODE(OGF_LE_CONTROLLER, 0x0c), "11"
+// param: le scan enable: disabled (0), enabled (1)
+// param: filter duplices: disabled (0), enabled (1)
+// return: status
+};
+const hci_cmd_t hci_le_create_connection= {
+OPCODE(OGF_LE_CONTROLLER, 0x0d), "2211B1222222"
+// param: le scan interval, [0x0004, 0x4000], unit: 0.625 msec
+// param: le scan window, [0x0004, 0x4000], unit: 0.625 msec
+// param: initiator filter policy: peer address type + peer address (0), whitelist (1)
+// param: peer address type: public (0), random (1)
+// param: peer address
+// param: own address type: public (0), random (1)
+// param: conn interval min, [0x0006, 0x0c80], unit: 1.25 msec
+// param: conn interval max, [0x0006, 0x0c80], unit: 1.25 msec
+// param: conn latency, number of connection events [0x0000, 0x01f4]
+// param: supervision timeout, [0x000a, 0x0c80], unit: 10 msec
+// param: minimum CE length, [0x0000, 0xffff], unit: 0.625 msec
+// return: none -> le create connection complete event
+};
+const hci_cmd_t hci_le_create_connection_cancel = {
+OPCODE(OGF_LE_CONTROLLER, 0x0e), ""
+// params: none
+// return: status
+};
+const hci_cmd_t hci_le_read_white_list_size = {
+OPCODE(OGF_LE_CONTROLLER, 0x0f), ""
+// params: none
+// return: status, number of entries in controller whitelist
+};
+const hci_cmd_t hci_le_clear_white_list = {
+OPCODE(OGF_LE_CONTROLLER, 0x10), ""
+// params: none
+// return: status
+};
+const hci_cmd_t hci_le_add_device_to_whitelist = {
+OPCODE(OGF_LE_CONTROLLER, 0x11), "1B"
+// param: address type: public (0), random (1)
+// param: address
+// return: status
+};
+const hci_cmd_t hci_le_remove_device_from_whitelist = {
+OPCODE(OGF_LE_CONTROLLER, 0x12), "1B"
+// param: address type: public (0), random (1)
+// param: address
+// return: status
+};
+const hci_cmd_t hci_le_connection_update = {
+OPCODE(OGF_LE_CONTROLLER, 0x13), "H222222"
+// param: conn handle
+// param: conn interval min, [0x0006,0x0c80], unit: 1.25 msec
+// param: conn interval max, [0x0006,0x0c80], unit: 1.25 msec
+// param: conn latency, [0x0000,0x03e8], number of connection events
+// param: supervision timeout, [0x000a,0x0c80], unit: 10 msec
+// param: minimum CE length, [0x0000,0xffff], unit: 0.625 msec
+// param: maximum CE length, [0x0000,0xffff], unit: 0.625 msec
+// return: none -> le connection update complete event
+};
+const hci_cmd_t hci_le_set_host_channel_classification = {
+OPCODE(OGF_LE_CONTROLLER, 0x14), "41"
+// param: channel map 37 bit, split into first 32 and higher 5 bits
+// return: status
+};
+const hci_cmd_t hci_le_read_channel_map = {
+OPCODE(OGF_LE_CONTROLLER, 0x15), "H"
+// params: connection handle
+// return: status, connection handle, channel map (5 bytes, 37 used)
+};
+const hci_cmd_t hci_le_read_remote_used_features = {
+OPCODE(OGF_LE_CONTROLLER, 0x16), "H"
+// params: connection handle
+// return: none -> le read remote used features complete event
+};
+const hci_cmd_t hci_le_encrypt = {
+OPCODE(OGF_LE_CONTROLLER, 0x17), "PP"
+// param: key (128) for AES-128
+// param: plain text (128)
+// return: status, encrypted data (128)
+};
+const hci_cmd_t hci_le_rand = {
+OPCODE(OGF_LE_CONTROLLER, 0x18), ""
+// params: none
+// return: status, random number (64)
+};
+const hci_cmd_t hci_le_start_encryption = {
+OPCODE(OGF_LE_CONTROLLER, 0x19), "H442P"
+// param: connection handle
+// param: 64 bit random number lower 32 bit
+// param: 64 bit random number higher 32 bit
+// param: encryption diversifier (16)
+// param: long term key (128)
+// return: none -> encryption changed or encryption key refresh complete event
+};
+const hci_cmd_t hci_le_long_term_key_request_reply = {
+OPCODE(OGF_LE_CONTROLLER, 0x1a), "HP"
+// param: connection handle
+// param: long term key (128)
+// return: status, connection handle
+};
+const hci_cmd_t hci_le_long_term_key_negative_reply = {
+OPCODE(OGF_LE_CONTROLLER, 0x1b), "H"
+// param: connection handle
+// return: status, connection handle
+};
+const hci_cmd_t hci_le_read_supported_states = {
+OPCODE(OGF_LE_CONTROLLER, 0x1c), "H"
+// param: none
+// return: status, LE states (64)
+};
+const hci_cmd_t hci_le_receiver_test = {
+OPCODE(OGF_LE_CONTROLLER, 0x1d), "1"
+// param: rx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
+// return: status
+};
+const hci_cmd_t hci_le_transmitter_test = {
+ OPCODE(OGF_LE_CONTROLLER, 0x1e), "111"
+ // param: tx frequency, [0x00 0x27], frequency (MHz): 2420 + N*2
+ // param: lengh of test payload [0x00,0x25]
+ // param: packet payload [0,7] different patterns
+ // return: status
+};
+const hci_cmd_t hci_le_test_end = {
+ OPCODE(OGF_LE_CONTROLLER, 0x1f), "1"
+ // params: none
+ // return: status, number of packets (8)
+};
+#endif
+
+// BTstack commands
+const hci_cmd_t btstack_get_state = {
+OPCODE(OGF_BTSTACK, BTSTACK_GET_STATE), ""
+// no params ->
+};
+
+const hci_cmd_t btstack_set_power_mode = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_POWER_MODE), "1"
+// mode: 0 = off, 1 = on
+};
+
+const hci_cmd_t btstack_set_acl_capture_mode = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_ACL_CAPTURE_MODE), "1"
+// mode: 0 = off, 1 = on
+};
+
+const hci_cmd_t btstack_get_version = {
+OPCODE(OGF_BTSTACK, BTSTACK_GET_VERSION), ""
+};
+
+const hci_cmd_t btstack_get_system_bluetooth_enabled = {
+OPCODE(OGF_BTSTACK, BTSTACK_GET_SYSTEM_BLUETOOTH_ENABLED), ""
+};
+
+const hci_cmd_t btstack_set_system_bluetooth_enabled = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_SYSTEM_BLUETOOTH_ENABLED), "1"
+};
+
+const hci_cmd_t btstack_set_discoverable = {
+OPCODE(OGF_BTSTACK, BTSTACK_SET_DISCOVERABLE), "1"
+};
+
+const hci_cmd_t btstack_set_bluetooth_enabled = {
+// only used by btstack config
+OPCODE(OGF_BTSTACK, BTSTACK_SET_BLUETOOTH_ENABLED), "1"
+};
+
+const hci_cmd_t l2cap_create_channel = {
+OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL), "B2"
+// @param bd_addr(48), psm (16)
+};
+const hci_cmd_t l2cap_create_channel_mtu = {
+OPCODE(OGF_BTSTACK, L2CAP_CREATE_CHANNEL_MTU), "B22"
+// @param bd_addr(48), psm (16), mtu (16)
+};
+const hci_cmd_t l2cap_disconnect = {
+OPCODE(OGF_BTSTACK, L2CAP_DISCONNECT), "21"
+// @param channel(16), reason(8)
+};
+const hci_cmd_t l2cap_register_service = {
+OPCODE(OGF_BTSTACK, L2CAP_REGISTER_SERVICE), "22"
+// @param psm (16), mtu (16)
+};
+const hci_cmd_t l2cap_unregister_service = {
+OPCODE(OGF_BTSTACK, L2CAP_UNREGISTER_SERVICE), "2"
+// @param psm (16)
+};
+const hci_cmd_t l2cap_accept_connection = {
+OPCODE(OGF_BTSTACK, L2CAP_ACCEPT_CONNECTION), "2"
+// @param source cid (16)
+};
+const hci_cmd_t l2cap_decline_connection = {
+OPCODE(OGF_BTSTACK, L2CAP_DECLINE_CONNECTION), "21"
+// @param source cid (16), reason(8)
+};
+const hci_cmd_t sdp_register_service_record = {
+OPCODE(OGF_BTSTACK, SDP_REGISTER_SERVICE_RECORD), "S"
+// @param service record handle (DES)
+};
+const hci_cmd_t sdp_unregister_service_record = {
+OPCODE(OGF_BTSTACK, SDP_UNREGISTER_SERVICE_RECORD), "4"
+// @param service record handle (32)
+};
+
+// create rfcomm channel: @param bd_addr(48), channel (8)
+const hci_cmd_t rfcomm_create_channel = {
+ OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL), "B1"
+};
+// create rfcomm channel: @param bd_addr(48), channel (8), mtu (16), credits (8)
+const hci_cmd_t rfcomm_create_channel_with_initial_credits = {
+ OPCODE(OGF_BTSTACK, RFCOMM_CREATE_CHANNEL_WITH_CREDITS), "B121"
+};
+// grant credits: @param rfcomm_cid(16), credits (8)
+const hci_cmd_t rfcomm_grants_credits= {
+ OPCODE(OGF_BTSTACK, RFCOMM_GRANT_CREDITS), "21"
+};
+// disconnect rfcomm disconnect, @param rfcomm_cid(16), reason(8)
+const hci_cmd_t rfcomm_disconnect = {
+ OPCODE(OGF_BTSTACK, RFCOMM_DISCONNECT), "21"
+};
+
+// register rfcomm service: @param channel(8), mtu (16)
+const hci_cmd_t rfcomm_register_service = {
+ OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE), "12"
+};
+// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
+const hci_cmd_t rfcomm_register_service_with_initial_credits = {
+ OPCODE(OGF_BTSTACK, RFCOMM_REGISTER_SERVICE_WITH_CREDITS), "121"
+};
+
+// unregister rfcomm service, @param service_channel(16)
+const hci_cmd_t rfcomm_unregister_service = {
+ OPCODE(OGF_BTSTACK, RFCOMM_UNREGISTER_SERVICE), "2"
+};
+// accept connection @param source cid (16)
+const hci_cmd_t rfcomm_accept_connection = {
+ OPCODE(OGF_BTSTACK, RFCOMM_ACCEPT_CONNECTION), "2"
+};
+// decline connection @param source cid (16)
+const hci_cmd_t rfcomm_decline_connection = {
+ OPCODE(OGF_BTSTACK, RFCOMM_DECLINE_CONNECTION), "21"
+};
+// request persisten rfcomm channel number for named service
+const hci_cmd_t rfcomm_persistent_channel_for_service = {
+ OPCODE(OGF_BTSTACK, RFCOMM_PERSISTENT_CHANNEL), "N"
+};
+
+// register rfcomm service: @param channel(8), mtu (16), initial credits (8)
+extern const hci_cmd_t rfcomm_register_service_with_initial_credits;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hci_dump.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,239 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * hci_dump.c
+ *
+ * Dump HCI trace in various formats:
+ *
+ * - BlueZ's hcidump format
+ * - Apple's PacketLogger
+ * - stdout hexdump
+ *
+ * Created by Matthias Ringwald on 5/26/09.
+ */
+
+#include "config.h"
+
+#include "hci_dump.h"
+#include "hci.h"
+#include "hci_transport.h"
+#include <btstack/hci_cmds.h>
+
+#ifndef EMBEDDED
+#include <fcntl.h> // open
+#include <arpa/inet.h> // hton..
+#include <unistd.h> // write
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h> // for timestamps
+#include <sys/stat.h> // for mode flags
+#include <stdarg.h> // for va_list
+#endif
+
+// BLUEZ hcidump
+typedef struct {
+ uint16_t len;
+ uint8_t in;
+ uint8_t pad;
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint8_t packet_type;
+}
+#ifdef __GNUC__
+__attribute__ ((packed))
+#endif
+hcidump_hdr;
+
+// APPLE PacketLogger
+typedef struct {
+ uint32_t len;
+ uint32_t ts_sec;
+ uint32_t ts_usec;
+ uint8_t type; // 0xfc for note
+}
+#ifdef __GNUC__
+__attribute__ ((packed))
+#endif
+pktlog_hdr;
+
+#ifndef EMBEDDED
+static int dump_file = -1;
+static int dump_format;
+static hcidump_hdr header_bluez;
+static pktlog_hdr header_packetlogger;
+static char time_string[40];
+static int max_nr_packets = -1;
+static int nr_packets = 0;
+static char log_message_buffer[256];
+#endif
+
+void hci_dump_open(char *filename, hci_dump_format_t format){
+#ifndef EMBEDDED
+ dump_format = format;
+ if (dump_format == HCI_DUMP_STDOUT) {
+ dump_file = fileno(stdout);
+ } else {
+ dump_file = open(filename, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ }
+#endif
+}
+
+#ifndef EMBEDDED
+void hci_dump_set_max_packets(int packets){
+ max_nr_packets = packets;
+}
+#endif
+
+void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len) {
+#ifndef EMBEDDED
+
+ if (dump_file < 0) return; // not activated yet
+
+ // don't grow bigger than max_nr_packets
+ if (dump_format != HCI_DUMP_STDOUT && max_nr_packets > 0){
+ if (nr_packets >= max_nr_packets){
+ lseek(dump_file, 0, SEEK_SET);
+ ftruncate(dump_file, 0);
+ nr_packets = 0;
+ }
+ nr_packets++;
+ }
+
+ // get time
+ struct timeval curr_time;
+ struct tm* ptm;
+ gettimeofday(&curr_time, NULL);
+
+ switch (dump_format){
+ case HCI_DUMP_STDOUT: {
+ /* Obtain the time of day, and convert it to a tm struct. */
+ ptm = localtime (&curr_time.tv_sec);
+ /* Format the date and time, down to a single second. */
+ strftime (time_string, sizeof (time_string), "[%Y-%m-%d %H:%M:%S", ptm);
+ /* Compute milliseconds from microseconds. */
+ uint16_t milliseconds = curr_time.tv_usec / 1000;
+ /* Print the formatted time, in seconds, followed by a decimal point
+ and the milliseconds. */
+ printf ("%s.%03u] ", time_string, milliseconds);
+ switch (packet_type){
+ case HCI_COMMAND_DATA_PACKET:
+ printf("CMD => ");
+ break;
+ case HCI_EVENT_PACKET:
+ printf("EVT <= ");
+ break;
+ case HCI_ACL_DATA_PACKET:
+ if (in) {
+ printf("ACL <= ");
+ } else {
+ printf("ACL => ");
+ }
+ break;
+ case LOG_MESSAGE_PACKET:
+ // assume buffer is big enough
+ packet[len] = 0;
+ printf("LOG -- %s\n", (char*) packet);
+ return;
+ default:
+ return;
+ }
+ hexdump(packet, len);
+ break;
+ }
+
+ case HCI_DUMP_BLUEZ:
+ bt_store_16( (uint8_t *) &header_bluez.len, 0, 1 + len);
+ header_bluez.in = in;
+ header_bluez.pad = 0;
+ bt_store_32( (uint8_t *) &header_bluez.ts_sec, 0, curr_time.tv_sec);
+ bt_store_32( (uint8_t *) &header_bluez.ts_usec, 0, curr_time.tv_usec);
+ header_bluez.packet_type = packet_type;
+ write (dump_file, &header_bluez, sizeof(hcidump_hdr) );
+ write (dump_file, packet, len );
+ break;
+
+ case HCI_DUMP_PACKETLOGGER:
+ header_packetlogger.len = htonl( sizeof(pktlog_hdr) - 4 + len);
+ header_packetlogger.ts_sec = htonl(curr_time.tv_sec);
+ header_packetlogger.ts_usec = htonl(curr_time.tv_usec);
+ switch (packet_type){
+ case HCI_COMMAND_DATA_PACKET:
+ header_packetlogger.type = 0x00;
+ break;
+ case HCI_ACL_DATA_PACKET:
+ if (in) {
+ header_packetlogger.type = 0x03;
+ } else {
+ header_packetlogger.type = 0x02;
+ }
+ break;
+ case HCI_EVENT_PACKET:
+ header_packetlogger.type = 0x01;
+ break;
+ case LOG_MESSAGE_PACKET:
+ header_packetlogger.type = 0xfc;
+ break;
+ default:
+ return;
+ }
+ write (dump_file, &header_packetlogger, sizeof(pktlog_hdr) );
+ write (dump_file, packet, len );
+ break;
+
+ default:
+ break;
+ }
+#endif
+}
+
+void hci_dump_log(const char * format, ...){
+#ifndef EMBEDDED
+ va_list argptr;
+ va_start(argptr, format);
+ int len = vsnprintf(log_message_buffer, sizeof(log_message_buffer), format, argptr);
+ hci_dump_packet(LOG_MESSAGE_PACKET, 0, (uint8_t*) log_message_buffer, len);
+ va_end(argptr);
+#endif
+}
+
+void hci_dump_close(){
+#ifndef EMBEDDED
+ close(dump_file);
+ dump_file = -1;
+#endif
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hci_dump.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,59 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * hci_dump.h
+ *
+ * Dump HCI trace as BlueZ's hcidump format, Apple's PacketLogger, or stdout
+ *
+ * Created by Matthias Ringwald on 5/26/09.
+ */
+
+#pragma once
+
+#include <stdint.h>
+
+typedef enum {
+ HCI_DUMP_BLUEZ = 0,
+ HCI_DUMP_PACKETLOGGER,
+ HCI_DUMP_STDOUT
+} hci_dump_format_t;
+
+void hci_dump_open(char *filename, hci_dump_format_t format);
+void hci_dump_set_max_packets(int packets); // -1 for unlimited
+void hci_dump_packet(uint8_t packet_type, uint8_t in, uint8_t *packet, uint16_t len);
+void hci_dump_log(const char * format, ...);
+void hci_dump_close(void);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hci_transport.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,88 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * hci_transport.h
+ *
+ * HCI Transport API -- allows BT Daemon to use different transport protcols
+ *
+ * Created by Matthias Ringwald on 4/29/09.
+ *
+ */
+#pragma once
+
+#include <stdint.h>
+#include <btstack/run_loop.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+/* HCI packet types */
+typedef struct {
+ int (*open)(void *transport_config);
+ int (*close)(void *transport_config);
+ int (*send_packet)(uint8_t packet_type, uint8_t *packet, int size);
+ void (*register_packet_handler)(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size));
+ const char * (*get_transport_name)(void);
+ // custom extension for UART transport implementations
+ int (*set_baudrate)(uint32_t baudrate);
+ // support async transport layers, e.g. IRQ driven without buffers
+ int (*can_send_packet_now)(uint8_t packet_type);
+} hci_transport_t;
+
+typedef struct {
+ const char *device_name;
+ uint32_t baudrate_init; // initial baud rate
+ uint32_t baudrate_main; // = 0: same as initial baudrate
+ int flowcontrol; //
+} hci_uart_config_t;
+
+
+// inline various hci_transport_X.h files
+extern hci_transport_t * hci_transport_h4_instance(void);
+extern hci_transport_t * hci_transport_h4_dma_instance(void);
+extern hci_transport_t * hci_transport_h4_iphone_instance(void);
+extern hci_transport_t * hci_transport_h5_instance(void);
+extern hci_transport_t * hci_transport_usb_instance(void);
+
+// support for "enforece wake device" in h4 - used by iOS power management
+extern void hci_transport_h4_iphone_set_enforce_wake_device(char *path);
+
+#if defined __cplusplus
+}
+#endif
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/hci_transport_usb.cpp Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,133 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * hci_transport_usb.cpp
+ *
+ * HCI Transport API implementation for USB
+ *
+ * Created by Matthias Ringwald on 7/5/09.
+ */
+
+// delock bt class 2 - csr
+// 0a12:0001 (bus 27, device 2)
+
+// Interface Number - Alternate Setting - suggested Endpoint Address - Endpoint Type - Suggested Max Packet Size
+// HCI Commands 0 0 0x00 Control 8/16/32/64
+// HCI Events 0 0 0x81 Interrupt (IN) 16
+// ACL Data 0 0 0x82 Bulk (IN) 32/64
+// ACL Data 0 0 0x02 Bulk (OUT) 32/64
+
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "debug.h"
+#include "hci.h"
+#include "hci_transport.h"
+#include "hci_dump.h"
+#include "USBHostBTstack.h"
+
+enum {
+ LIB_USB_CLOSED = 0,
+ LIB_USB_OPENED,
+ LIB_USB_DEVICE_OPENDED,
+ LIB_USB_KERNEL_DETACHED,
+ LIB_USB_INTERFACE_CLAIMED,
+ LIB_USB_TRANSFERS_ALLOCATED
+} libusb_state = LIB_USB_CLOSED;
+
+// single instance
+static hci_transport_t * hci_transport_usb = NULL;
+static USBHostBTstack* bt = NULL;
+
+static int usb_process_ds(struct data_source *ds) {
+ if (bt) {
+ bt->poll();
+ }
+ return 0;
+}
+
+static int usb_open(void *transport_config){
+ log_info("usb_open\n");
+ data_source_t *ds = (data_source_t*)malloc(sizeof(data_source_t));
+ ds->process = usb_process_ds;
+ run_loop_add_data_source(ds);
+ if (bt) {
+ return bt->open();
+ }
+ return 0;
+}
+static int usb_close(void *transport_config){
+
+ return 0;
+}
+
+static int usb_send_packet(uint8_t packet_type, uint8_t * packet, int size){
+ //log_info("usb_send_packet\n");
+ if (bt) {
+ bt->send_packet(packet_type, packet, size);
+ }
+ return 0;
+}
+
+static void usb_register_packet_handler(void (*handler)(uint8_t packet_type, uint8_t *packet, uint16_t size)){
+ log_info("registering packet handler\n");
+ if (bt) {
+ bt->register_packet_handler(handler);
+ }
+}
+
+static const char * usb_get_transport_name(void){
+ return "USB";
+}
+
+// get usb singleton
+hci_transport_t * hci_transport_usb_instance() {
+ if (!bt) {
+ bt = new USBHostBTstack;
+ }
+ if (!hci_transport_usb) {
+ hci_transport_usb = (hci_transport_t*)malloc( sizeof(hci_transport_t));
+ hci_transport_usb->open = usb_open;
+ hci_transport_usb->close = usb_close;
+ hci_transport_usb->send_packet = usb_send_packet;
+ hci_transport_usb->register_packet_handler = usb_register_packet_handler;
+ hci_transport_usb->get_transport_name = usb_get_transport_name;
+ hci_transport_usb->set_baudrate = NULL;
+ hci_transport_usb->can_send_packet_now = NULL;
+ }
+ return hci_transport_usb;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/l2cap.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,1138 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * l2cap.c
+ *
+ * Logical Link Control and Adaption Protocl (L2CAP)
+ *
+ * Created by Matthias Ringwald on 5/16/09.
+ */
+
+#include "l2cap.h"
+#include "hci.h"
+#include "hci_dump.h"
+#include "debug.h"
+#include "btstack_memory.h"
+
+#include <stdarg.h>
+#include <string.h>
+
+#include <stdio.h>
+
+// nr of buffered acl packets in outgoing queue to get max performance
+#define NR_BUFFERED_ACL_PACKETS 3
+
+// used to cache l2cap rejects, echo, and informational requests
+#define NR_PENDING_SIGNALING_RESPONSES 3
+
+// offsets for L2CAP SIGNALING COMMANDS
+#define L2CAP_SIGNALING_COMMAND_CODE_OFFSET 0
+#define L2CAP_SIGNALING_COMMAND_SIGID_OFFSET 1
+#define L2CAP_SIGNALING_COMMAND_LENGTH_OFFSET 2
+#define L2CAP_SIGNALING_COMMAND_DATA_OFFSET 4
+
+static void null_packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
+static void l2cap_packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size);
+
+// used to cache l2cap rejects, echo, and informational requests
+static l2cap_signaling_response_t signaling_responses[NR_PENDING_SIGNALING_RESPONSES];
+static int signaling_responses_pending;
+
+static linked_list_t l2cap_channels = NULL;
+static linked_list_t l2cap_services = NULL;
+static void (*packet_handler) (void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size) = null_packet_handler;
+static int new_credits_blocked = 0;
+
+static btstack_packet_handler_t attribute_protocol_packet_handler = NULL;
+static btstack_packet_handler_t security_protocol_packet_handler = NULL;
+
+// prototypes
+static void l2cap_finialize_channel_close(l2cap_channel_t *channel);
+static l2cap_service_t * l2cap_get_service(uint16_t psm);
+static void l2cap_emit_channel_opened(l2cap_channel_t *channel, uint8_t status);
+static void l2cap_emit_channel_closed(l2cap_channel_t *channel);
+static void l2cap_emit_connection_request(l2cap_channel_t *channel);
+static int l2cap_channel_ready_for_open(l2cap_channel_t *channel);
+
+
+void l2cap_init(){
+ new_credits_blocked = 0;
+ signaling_responses_pending = 0;
+
+ l2cap_channels = NULL;
+ l2cap_services = NULL;
+
+ packet_handler = null_packet_handler;
+
+ //
+ // register callback with HCI
+ //
+ hci_register_packet_handler(&l2cap_packet_handler);
+ hci_connectable_control(0); // no services yet
+}
+
+
+/** Register L2CAP packet handlers */
+static void null_packet_handler(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
+}
+void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size)){
+ packet_handler = handler;
+}
+
+// notify client/protocol handler
+void l2cap_dispatch(l2cap_channel_t *channel, uint8_t type, uint8_t * data, uint16_t size){
+ if (channel->packet_handler) {
+ (* (channel->packet_handler))(type, channel->local_cid, data, size);
+ } else {
+ (*packet_handler)(channel->connection, type, channel->local_cid, data, size);
+ }
+}
+
+void l2cap_emit_channel_opened(l2cap_channel_t *channel, uint8_t status) {
+ uint8_t event[21];
+ event[0] = L2CAP_EVENT_CHANNEL_OPENED;
+ event[1] = sizeof(event) - 2;
+ event[2] = status;
+ bt_flip_addr(&event[3], channel->address);
+ bt_store_16(event, 9, channel->handle);
+ bt_store_16(event, 11, channel->psm);
+ bt_store_16(event, 13, channel->local_cid);
+ bt_store_16(event, 15, channel->remote_cid);
+ bt_store_16(event, 17, channel->local_mtu);
+ bt_store_16(event, 19, channel->remote_mtu);
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void l2cap_emit_channel_closed(l2cap_channel_t *channel) {
+ uint8_t event[4];
+ event[0] = L2CAP_EVENT_CHANNEL_CLOSED;
+ event[1] = sizeof(event) - 2;
+ bt_store_16(event, 2, channel->local_cid);
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void l2cap_emit_connection_request(l2cap_channel_t *channel) {
+ uint8_t event[16];
+ event[0] = L2CAP_EVENT_INCOMING_CONNECTION;
+ event[1] = sizeof(event) - 2;
+ bt_flip_addr(&event[2], channel->address);
+ bt_store_16(event, 8, channel->handle);
+ bt_store_16(event, 10, channel->psm);
+ bt_store_16(event, 12, channel->local_cid);
+ bt_store_16(event, 14, channel->remote_cid);
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+static void l2cap_emit_service_registered(void *connection, uint8_t status, uint16_t psm){
+ uint8_t event[5];
+ event[0] = L2CAP_EVENT_SERVICE_REGISTERED;
+ event[1] = sizeof(event) - 2;
+ event[2] = status;
+ bt_store_16(event, 3, psm);
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ (*packet_handler)(connection, HCI_EVENT_PACKET, 0, event, sizeof(event));
+}
+
+void l2cap_emit_credits(l2cap_channel_t *channel, uint8_t credits) {
+ // track credits
+ channel->packets_granted += credits;
+ // log_info("l2cap_emit_credits for cid %u, credits given: %u (+%u)\n", channel->local_cid, channel->packets_granted, credits);
+
+ uint8_t event[5];
+ event[0] = L2CAP_EVENT_CREDITS;
+ event[1] = sizeof(event) - 2;
+ bt_store_16(event, 2, channel->local_cid);
+ event[4] = credits;
+ hci_dump_packet( HCI_EVENT_PACKET, 0, event, sizeof(event));
+ l2cap_dispatch(channel, HCI_EVENT_PACKET, event, sizeof(event));
+}
+
+void l2cap_block_new_credits(uint8_t blocked){
+ new_credits_blocked = blocked;
+}
+
+void l2cap_hand_out_credits(void){
+
+ if (new_credits_blocked) return; // we're told not to. used by daemon
+
+ linked_item_t *it;
+ for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
+ if (!hci_number_free_acl_slots()) return;
+ l2cap_channel_t * channel = (l2cap_channel_t *) it;
+ if (channel->state != L2CAP_STATE_OPEN) continue;
+ if (hci_number_outgoing_packets(channel->handle) < NR_BUFFERED_ACL_PACKETS && channel->packets_granted == 0) {
+ l2cap_emit_credits(channel, 1);
+ }
+ }
+}
+
+l2cap_channel_t * l2cap_get_channel_for_local_cid(uint16_t local_cid){
+ linked_item_t *it;
+ for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
+ l2cap_channel_t * channel = (l2cap_channel_t *) it;
+ if ( channel->local_cid == local_cid) {
+ return channel;
+ }
+ }
+ return NULL;
+}
+
+int l2cap_can_send_packet_now(uint16_t local_cid){
+ l2cap_channel_t *channel = l2cap_get_channel_for_local_cid(local_cid);
+ if (!channel) return 0;
+ if (!channel->packets_granted) return 0;
+ return hci_can_send_packet_now(HCI_ACL_DATA_PACKET);
+}
+
+uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid){
+ l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid);
+ if (channel) {
+ return channel->remote_mtu;
+ }
+ return 0;
+}
+
+int l2cap_send_signaling_packet(hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, ...){
+
+ if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){
+ log_info("l2cap_send_signaling_packet, cannot send\n");
+ return BTSTACK_ACL_BUFFERS_FULL;
+ }
+
+ // log_info("l2cap_send_signaling_packet type %u\n", cmd);
+ uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer();
+ va_list argptr;
+ va_start(argptr, identifier);
+ uint16_t len = l2cap_create_signaling_internal(acl_buffer, handle, cmd, identifier, argptr);
+ va_end(argptr);
+ // log_info("l2cap_send_signaling_packet con %u!\n", handle);
+ return hci_send_acl_packet(acl_buffer, len);
+}
+
+uint8_t *l2cap_get_outgoing_buffer(void){
+ return hci_get_outgoing_acl_packet_buffer() + COMPLETE_L2CAP_HEADER; // 8 bytes
+}
+
+int l2cap_send_prepared(uint16_t local_cid, uint16_t len){
+
+ if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){
+ log_info("l2cap_send_internal cid %u, cannot send\n", local_cid);
+ return BTSTACK_ACL_BUFFERS_FULL;
+ }
+
+ l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid);
+ if (!channel) {
+ log_error("l2cap_send_internal no channel for cid %u\n", local_cid);
+ return -1; // TODO: define error
+ }
+
+ if (channel->packets_granted == 0){
+ log_error("l2cap_send_internal cid %u, no credits!\n", local_cid);
+ return -1; // TODO: define error
+ }
+
+ --channel->packets_granted;
+
+ log_debug("l2cap_send_internal cid %u, handle %u, 1 credit used, credits left %u;\n",
+ local_cid, channel->handle, channel->packets_granted);
+
+ uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer();
+
+ // 0 - Connection handle : PB=10 : BC=00
+ bt_store_16(acl_buffer, 0, channel->handle | (2 << 12) | (0 << 14));
+ // 2 - ACL length
+ bt_store_16(acl_buffer, 2, len + 4);
+ // 4 - L2CAP packet length
+ bt_store_16(acl_buffer, 4, len + 0);
+ // 6 - L2CAP channel DEST
+ bt_store_16(acl_buffer, 6, channel->remote_cid);
+ // send
+ int err = hci_send_acl_packet(acl_buffer, len+8);
+
+ l2cap_hand_out_credits();
+
+ return err;
+}
+
+int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len){
+
+ if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){
+ log_info("l2cap_send_prepared_to_handle cid %u, cannot send\n", cid);
+ return BTSTACK_ACL_BUFFERS_FULL;
+ }
+
+ log_debug("l2cap_send_prepared_to_handle cid %u, handle %u\n", cid, handle);
+
+ uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer();
+
+ // 0 - Connection handle : PB=10 : BC=00
+ bt_store_16(acl_buffer, 0, handle | (2 << 12) | (0 << 14));
+ // 2 - ACL length
+ bt_store_16(acl_buffer, 2, len + 4);
+ // 4 - L2CAP packet length
+ bt_store_16(acl_buffer, 4, len + 0);
+ // 6 - L2CAP channel DEST
+ bt_store_16(acl_buffer, 6, cid);
+ // send
+ int err = hci_send_acl_packet(acl_buffer, len+8);
+
+ l2cap_hand_out_credits();
+
+ return err;
+}
+
+int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len){
+
+ if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){
+ log_info("l2cap_send_internal cid %u, cannot send\n", local_cid);
+ return BTSTACK_ACL_BUFFERS_FULL;
+ }
+
+ uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer();
+
+ memcpy(&acl_buffer[8], data, len);
+
+ return l2cap_send_prepared(local_cid, len);
+}
+
+int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len){
+
+ if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)){
+ log_info("l2cap_send_internal cid %u, cannot send\n", cid);
+ return BTSTACK_ACL_BUFFERS_FULL;
+ }
+
+ uint8_t *acl_buffer = hci_get_outgoing_acl_packet_buffer();
+
+ memcpy(&acl_buffer[8], data, len);
+
+ return l2cap_send_prepared_connectionless(handle, cid, len);
+}
+
+static inline void channelStateVarSetFlag(l2cap_channel_t *channel, L2CAP_CHANNEL_STATE_VAR flag){
+ channel->state_var = (L2CAP_CHANNEL_STATE_VAR) (channel->state_var | flag);
+}
+
+static inline void channelStateVarClearFlag(l2cap_channel_t *channel, L2CAP_CHANNEL_STATE_VAR flag){
+ channel->state_var = (L2CAP_CHANNEL_STATE_VAR) (channel->state_var & ~flag);
+}
+
+
+
+// MARK: L2CAP_RUN
+// process outstanding signaling tasks
+void l2cap_run(void){
+
+ // check pending signaling responses
+ while (signaling_responses_pending){
+
+ if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)) break;
+
+ hci_con_handle_t handle = signaling_responses[0].handle;
+ uint8_t sig_id = signaling_responses[0].sig_id;
+ uint16_t infoType = signaling_responses[0].data; // INFORMATION_REQUEST
+ uint16_t result = signaling_responses[0].data; // CONNECTION_REQUEST
+
+ switch (signaling_responses[0].code){
+ case CONNECTION_REQUEST:
+ l2cap_send_signaling_packet(handle, CONNECTION_RESPONSE, sig_id, 0, 0, result, 0);
+ break;
+ case ECHO_REQUEST:
+ l2cap_send_signaling_packet(handle, ECHO_RESPONSE, sig_id, 0, NULL);
+ break;
+ case INFORMATION_REQUEST:
+ if (infoType == 2) {
+ uint32_t features = 0;
+ // extended features request supported, however no features present
+ l2cap_send_signaling_packet(handle, INFORMATION_RESPONSE, sig_id, infoType, 0, 4, &features);
+ } else {
+ // all other types are not supported
+ l2cap_send_signaling_packet(handle, INFORMATION_RESPONSE, sig_id, infoType, 1, 0, NULL);
+ }
+ break;
+ default:
+ // should not happen
+ break;
+ }
+
+ // remove first item
+ signaling_responses_pending--;
+ int i;
+ for (i=0; i < signaling_responses_pending; i++){
+ memcpy(&signaling_responses[i], &signaling_responses[i+1], sizeof(l2cap_signaling_response_t));
+ }
+ }
+
+ uint8_t config_options[4];
+ linked_item_t *it;
+ linked_item_t *next;
+ for (it = (linked_item_t *) l2cap_channels; it ; it = next){
+ next = it->next; // cache next item as current item might get freed
+
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) break;
+ if (!hci_can_send_packet_now(HCI_ACL_DATA_PACKET)) break;
+
+ l2cap_channel_t * channel = (l2cap_channel_t *) it;
+
+ // log_info("l2cap_run: state %u, var 0x%02x\n", channel->state, channel->state_var);
+
+
+ switch (channel->state){
+
+ case L2CAP_STATE_WILL_SEND_CREATE_CONNECTION:
+ // send connection request - set state first
+ channel->state = L2CAP_STATE_WAIT_CONNECTION_COMPLETE;
+ // BD_ADDR, Packet_Type, Page_Scan_Repetition_Mode, Reserved, Clock_Offset, Allow_Role_Switch
+ hci_send_cmd(&hci_create_connection, channel->address, hci_usable_acl_packet_types(), 0, 0, 0, 1);
+ break;
+
+ case L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE:
+ l2cap_send_signaling_packet(channel->handle, CONNECTION_RESPONSE, channel->remote_sig_id, 0, 0, channel->reason, 0);
+ // discard channel - l2cap_finialize_channel_close without sending l2cap close event
+ linked_list_remove(&l2cap_channels, (linked_item_t *) channel); // -- remove from list
+ btstack_memory_l2cap_channel_free(channel);
+ break;
+
+ case L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT:
+ channel->state = L2CAP_STATE_CONFIG;
+ channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ);
+ l2cap_send_signaling_packet(channel->handle, CONNECTION_RESPONSE, channel->remote_sig_id, channel->local_cid, channel->remote_cid, 0, 0);
+ break;
+
+ case L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST:
+ // success, start l2cap handshake
+ channel->local_sig_id = l2cap_next_sig_id();
+ channel->state = L2CAP_STATE_WAIT_CONNECT_RSP;
+ l2cap_send_signaling_packet( channel->handle, CONNECTION_REQUEST, channel->local_sig_id, channel->psm, channel->local_cid);
+ break;
+
+ case L2CAP_STATE_CONFIG:
+ if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP){
+ channelStateVarClearFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP);
+ channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP);
+ l2cap_send_signaling_packet(channel->handle, CONFIGURE_RESPONSE, channel->remote_sig_id, channel->remote_cid, 0, 0, 0, NULL);
+ }
+ else if (channel->state_var & L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ){
+ channelStateVarClearFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ);
+ channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ);
+ channel->local_sig_id = l2cap_next_sig_id();
+ config_options[0] = 1; // MTU
+ config_options[1] = 2; // len param
+ bt_store_16( (uint8_t*)&config_options, 2, channel->local_mtu);
+ l2cap_send_signaling_packet(channel->handle, CONFIGURE_REQUEST, channel->local_sig_id, channel->remote_cid, 0, 4, &config_options);
+ }
+ if (l2cap_channel_ready_for_open(channel)){
+ channel->state = L2CAP_STATE_OPEN;
+ l2cap_emit_channel_opened(channel, 0); // success
+ l2cap_emit_credits(channel, 1);
+ }
+ break;
+
+ case L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE:
+ l2cap_send_signaling_packet( channel->handle, DISCONNECTION_RESPONSE, channel->remote_sig_id, channel->local_cid, channel->remote_cid);
+ l2cap_finialize_channel_close(channel); // -- remove from list
+ break;
+
+ case L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST:
+ channel->local_sig_id = l2cap_next_sig_id();
+ channel->state = L2CAP_STATE_WAIT_DISCONNECT;
+ l2cap_send_signaling_packet( channel->handle, DISCONNECTION_REQUEST, channel->local_sig_id, channel->remote_cid, channel->local_cid);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+uint16_t l2cap_max_mtu(void){
+ return hci_max_acl_data_packet_length() - L2CAP_HEADER_SIZE;
+}
+
+// open outgoing L2CAP channel
+void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler,
+ bd_addr_t address, uint16_t psm, uint16_t mtu){
+
+ // alloc structure
+ l2cap_channel_t * chan = (l2cap_channel_t*) btstack_memory_l2cap_channel_get();
+ if (!chan) {
+ // emit error event
+ l2cap_channel_t dummy_channel;
+ BD_ADDR_COPY(dummy_channel.address, address);
+ dummy_channel.psm = psm;
+ l2cap_emit_channel_opened(&dummy_channel, BTSTACK_MEMORY_ALLOC_FAILED);
+ return;
+ }
+ // limit local mtu to max acl packet length
+ if (mtu > l2cap_max_mtu()) {
+ mtu = l2cap_max_mtu();
+ }
+
+ // fill in
+ BD_ADDR_COPY(chan->address, address);
+ chan->psm = psm;
+ chan->handle = 0;
+ chan->connection = connection;
+ chan->packet_handler = packet_handler;
+ chan->remote_mtu = L2CAP_MINIMAL_MTU;
+ chan->local_mtu = mtu;
+ chan->packets_granted = 0;
+
+ // set initial state
+ chan->state = L2CAP_STATE_WILL_SEND_CREATE_CONNECTION;
+ chan->state_var = L2CAP_CHANNEL_STATE_VAR_NONE;
+ chan->remote_sig_id = L2CAP_SIG_ID_INVALID;
+ chan->local_sig_id = L2CAP_SIG_ID_INVALID;
+
+ // add to connections list
+ linked_list_add(&l2cap_channels, (linked_item_t *) chan);
+
+ l2cap_run();
+}
+
+void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason){
+ // find channel for local_cid
+ l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid);
+ if (channel) {
+ channel->state = L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST;
+ }
+ // process
+ l2cap_run();
+}
+
+static void l2cap_handle_connection_failed_for_addr(bd_addr_t address, uint8_t status){
+ linked_item_t *it = (linked_item_t *) &l2cap_channels;
+ while (it->next){
+ l2cap_channel_t * channel = (l2cap_channel_t *) it->next;
+ if ( ! BD_ADDR_CMP( channel->address, address) ){
+ if (channel->state == L2CAP_STATE_WAIT_CONNECTION_COMPLETE || channel->state == L2CAP_STATE_WILL_SEND_CREATE_CONNECTION) {
+ // failure, forward error code
+ l2cap_emit_channel_opened(channel, status);
+ // discard channel
+ it->next = it->next->next;
+ btstack_memory_l2cap_channel_free(channel);
+ }
+ } else {
+ it = it->next;
+ }
+ }
+}
+
+static void l2cap_handle_connection_success_for_addr(bd_addr_t address, hci_con_handle_t handle){
+ linked_item_t *it;
+ for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
+ l2cap_channel_t * channel = (l2cap_channel_t *) it;
+ if ( ! BD_ADDR_CMP( channel->address, address) ){
+ if (channel->state == L2CAP_STATE_WAIT_CONNECTION_COMPLETE || channel->state == L2CAP_STATE_WILL_SEND_CREATE_CONNECTION) {
+ // success, start l2cap handshake
+ channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST;
+ channel->handle = handle;
+ channel->local_cid = l2cap_next_local_cid();
+ }
+ }
+ }
+ // process
+ l2cap_run();
+}
+
+void l2cap_event_handler( uint8_t *packet, uint16_t size ){
+
+ bd_addr_t address;
+ hci_con_handle_t handle;
+ l2cap_channel_t * channel;
+ linked_item_t *it;
+ int hci_con_used;
+
+ switch(packet[0]){
+
+ // handle connection complete events
+ case HCI_EVENT_CONNECTION_COMPLETE:
+ bt_flip_addr(address, &packet[5]);
+ if (packet[2] == 0){
+ handle = READ_BT_16(packet, 3);
+ l2cap_handle_connection_success_for_addr(address, handle);
+ } else {
+ l2cap_handle_connection_failed_for_addr(address, packet[2]);
+ }
+ break;
+
+ // handle successful create connection cancel command
+ case HCI_EVENT_COMMAND_COMPLETE:
+ if ( COMMAND_COMPLETE_EVENT(packet, hci_create_connection_cancel) ) {
+ if (packet[5] == 0){
+ bt_flip_addr(address, &packet[6]);
+ // CONNECTION TERMINATED BY LOCAL HOST (0X16)
+ l2cap_handle_connection_failed_for_addr(address, 0x16);
+ }
+ }
+ l2cap_run(); // try sending signaling packets first
+ break;
+
+ case HCI_EVENT_COMMAND_STATUS:
+ l2cap_run(); // try sending signaling packets first
+ break;
+
+ // handle disconnection complete events
+ case HCI_EVENT_DISCONNECTION_COMPLETE:
+ // send l2cap disconnect events for all channels on this handle
+ handle = READ_BT_16(packet, 3);
+ it = (linked_item_t *) &l2cap_channels;
+ while (it->next){
+ l2cap_channel_t * channel = (l2cap_channel_t *) it->next;
+ if ( channel->handle == handle ){
+ // update prev item before free'ing next element - don't call l2cap_finalize_channel_close
+ it->next = it->next->next;
+ l2cap_emit_channel_closed(channel);
+ btstack_memory_l2cap_channel_free(channel);
+ } else {
+ it = it->next;
+ }
+ }
+ break;
+
+ case HCI_EVENT_NUMBER_OF_COMPLETED_PACKETS:
+ l2cap_run(); // try sending signaling packets first
+ l2cap_hand_out_credits();
+ break;
+
+ // HCI Connection Timeouts
+ case L2CAP_EVENT_TIMEOUT_CHECK:
+ handle = READ_BT_16(packet, 2);
+ if (hci_authentication_active_for_handle(handle)) break;
+ hci_con_used = 0;
+ for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
+ channel = (l2cap_channel_t *) it;
+ if (channel->handle == handle) {
+ hci_con_used = 1;
+ }
+ }
+ if (hci_con_used) break;
+ if (!hci_can_send_packet_now(HCI_COMMAND_DATA_PACKET)) break;
+ hci_send_cmd(&hci_disconnect, handle, 0x13); // remote closed connection
+ break;
+
+ case DAEMON_EVENT_HCI_PACKET_SENT:
+ for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
+ channel = (l2cap_channel_t *) it;
+ if (channel->packet_handler) {
+ (* (channel->packet_handler))(HCI_EVENT_PACKET, channel->local_cid, packet, size);
+ }
+ }
+ if (attribute_protocol_packet_handler) {
+ (*attribute_protocol_packet_handler)(HCI_EVENT_PACKET, 0, packet, size);
+ }
+ if (security_protocol_packet_handler) {
+ (*security_protocol_packet_handler)(HCI_EVENT_PACKET, 0, packet, size);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // pass on
+ (*packet_handler)(NULL, HCI_EVENT_PACKET, 0, packet, size);
+}
+
+static void l2cap_handle_disconnect_request(l2cap_channel_t *channel, uint16_t identifier){
+ channel->remote_sig_id = identifier;
+ channel->state = L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE;
+ l2cap_run();
+}
+
+static void l2cap_register_signaling_response(hci_con_handle_t handle, uint8_t code, uint8_t sig_id, uint16_t data){
+ // Vol 3, Part A, 4.3: "The DCID and SCID fields shall be ignored when the result field indi- cates the connection was refused."
+ if (signaling_responses_pending < NR_PENDING_SIGNALING_RESPONSES) {
+ signaling_responses[signaling_responses_pending].handle = handle;
+ signaling_responses[signaling_responses_pending].code = code;
+ signaling_responses[signaling_responses_pending].sig_id = sig_id;
+ signaling_responses[signaling_responses_pending].data = data;
+ signaling_responses_pending++;
+ l2cap_run();
+ }
+}
+
+static void l2cap_handle_connection_request(hci_con_handle_t handle, uint8_t sig_id, uint16_t psm, uint16_t source_cid){
+
+ // log_info("l2cap_handle_connection_request for handle %u, psm %u cid %u\n", handle, psm, source_cid);
+ l2cap_service_t *service = l2cap_get_service(psm);
+ if (!service) {
+ // 0x0002 PSM not supported
+ l2cap_register_signaling_response(handle, CONNECTION_REQUEST, sig_id, 0x0002);
+ return;
+ }
+
+ hci_connection_t * hci_connection = connection_for_handle( handle );
+ if (!hci_connection) {
+ //
+ log_error("no hci_connection for handle %u\n", handle);
+ return;
+ }
+ // alloc structure
+ // log_info("l2cap_handle_connection_request register channel\n");
+ l2cap_channel_t * channel = (l2cap_channel_t*) btstack_memory_l2cap_channel_get();
+ if (!channel){
+ // 0x0004 No resources available
+ l2cap_register_signaling_response(handle, CONNECTION_REQUEST, sig_id, 0x0004);
+ return;
+ }
+
+ // fill in
+ BD_ADDR_COPY(channel->address, hci_connection->address);
+ channel->psm = psm;
+ channel->handle = handle;
+ channel->connection = service->connection;
+ channel->packet_handler = service->packet_handler;
+ channel->local_cid = l2cap_next_local_cid();
+ channel->remote_cid = source_cid;
+ channel->local_mtu = service->mtu;
+ channel->remote_mtu = L2CAP_DEFAULT_MTU;
+ channel->packets_granted = 0;
+ channel->remote_sig_id = sig_id;
+
+ // limit local mtu to max acl packet length
+ if (channel->local_mtu > l2cap_max_mtu()) {
+ channel->local_mtu = l2cap_max_mtu();
+ }
+
+ // set initial state
+ channel->state = L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT;
+ channel->state_var = L2CAP_CHANNEL_STATE_VAR_NONE;
+
+ // add to connections list
+ linked_list_add(&l2cap_channels, (linked_item_t *) channel);
+
+ // emit incoming connection request
+ l2cap_emit_connection_request(channel);
+}
+
+void l2cap_accept_connection_internal(uint16_t local_cid){
+ l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(local_cid);
+ if (!channel) {
+ log_error("l2cap_accept_connection_internal called but local_cid 0x%x not found", local_cid);
+ return;
+ }
+
+ channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT;
+
+ // process
+ l2cap_run();
+}
+
+void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason){
+ l2cap_channel_t * channel = l2cap_get_channel_for_local_cid( local_cid);
+ if (!channel) {
+ log_error( "l2cap_decline_connection_internal called but local_cid 0x%x not found", local_cid);
+ return;
+ }
+ channel->state = L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE;
+ channel->reason = reason;
+ l2cap_run();
+}
+
+void l2cap_signaling_handle_configure_request(l2cap_channel_t *channel, uint8_t *command){
+
+ channel->remote_sig_id = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET];
+
+ // accept the other's configuration options
+ uint16_t end_pos = 4 + READ_BT_16(command, L2CAP_SIGNALING_COMMAND_LENGTH_OFFSET);
+ uint16_t pos = 8;
+ while (pos < end_pos){
+ uint8_t type = command[pos++];
+ uint8_t length = command[pos++];
+ // MTU { type(8): 1, len(8):2, MTU(16) }
+ if ((type & 0x7f) == 1 && length == 2){
+ channel->remote_mtu = READ_BT_16(command, pos);
+ // log_info("l2cap cid %u, remote mtu %u\n", channel->local_cid, channel->remote_mtu);
+ }
+ pos += length;
+ }
+}
+
+static int l2cap_channel_ready_for_open(l2cap_channel_t *channel){
+ // log_info("l2cap_channel_ready_for_open 0x%02x\n", channel->state_var);
+ if ((channel->state_var & L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP) == 0) return 0;
+ if ((channel->state_var & L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP) == 0) return 0;
+ return 1;
+}
+
+
+void l2cap_signaling_handler_channel(l2cap_channel_t *channel, uint8_t *command){
+
+ uint8_t code = command[L2CAP_SIGNALING_COMMAND_CODE_OFFSET];
+ uint8_t identifier = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET];
+ uint16_t result = 0;
+
+ log_info("L2CAP signaling handler code %u, state %u\n", code, channel->state);
+
+ // handle DISCONNECT REQUESTS seperately
+ if (code == DISCONNECTION_REQUEST){
+ switch (channel->state){
+ case L2CAP_STATE_CONFIG:
+ case L2CAP_STATE_OPEN:
+ case L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST:
+ case L2CAP_STATE_WAIT_DISCONNECT:
+ l2cap_handle_disconnect_request(channel, identifier);
+ break;
+
+ default:
+ // ignore in other states
+ break;
+ }
+ return;
+ }
+
+ // @STATEMACHINE(l2cap)
+ switch (channel->state) {
+
+ case L2CAP_STATE_WAIT_CONNECT_RSP:
+ switch (code){
+ case CONNECTION_RESPONSE:
+ result = READ_BT_16 (command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+4);
+ switch (result) {
+ case 0:
+ // successful connection
+ channel->remote_cid = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET);
+ channel->state = L2CAP_STATE_CONFIG;
+ channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ);
+ break;
+ case 1:
+ // connection pending. get some coffee
+ break;
+ default:
+ // channel closed
+ channel->state = L2CAP_STATE_CLOSED;
+
+ // map l2cap connection response result to BTstack status enumeration
+ l2cap_emit_channel_opened(channel, L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL + result);
+
+ // drop link key if security block
+ if (L2CAP_CONNECTION_RESPONSE_RESULT_SUCCESSFUL + result == L2CAP_CONNECTION_RESPONSE_RESULT_REFUSED_SECURITY){
+ hci_drop_link_key_for_bd_addr(&channel->address);
+ }
+
+ // discard channel
+ linked_list_remove(&l2cap_channels, (linked_item_t *) channel);
+ btstack_memory_l2cap_channel_free(channel);
+ break;
+ }
+ break;
+
+ default:
+ //@TODO: implement other signaling packets
+ break;
+ }
+ break;
+
+ case L2CAP_STATE_CONFIG:
+ switch (code) {
+ case CONFIGURE_REQUEST:
+ channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ);
+ channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP);
+ l2cap_signaling_handle_configure_request(channel, command);
+ break;
+ case CONFIGURE_RESPONSE:
+ channelStateVarSetFlag(channel, L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP);
+ break;
+ default:
+ break;
+ }
+ if (l2cap_channel_ready_for_open(channel)){
+ // for open:
+ channel->state = L2CAP_STATE_OPEN;
+ l2cap_emit_channel_opened(channel, 0);
+ l2cap_emit_credits(channel, 1);
+ }
+ break;
+
+ case L2CAP_STATE_WAIT_DISCONNECT:
+ switch (code) {
+ case DISCONNECTION_RESPONSE:
+ l2cap_finialize_channel_close(channel);
+ break;
+ default:
+ //@TODO: implement other signaling packets
+ break;
+ }
+ break;
+
+ case L2CAP_STATE_CLOSED:
+ // @TODO handle incoming requests
+ break;
+
+ case L2CAP_STATE_OPEN:
+ //@TODO: implement other signaling packets, e.g. re-configure
+ break;
+ default:
+ break;
+ }
+ // log_info("new state %u\n", channel->state);
+}
+
+
+void l2cap_signaling_handler_dispatch( hci_con_handle_t handle, uint8_t * command){
+
+ // get code, signalind identifier and command len
+ uint8_t code = command[L2CAP_SIGNALING_COMMAND_CODE_OFFSET];
+ uint8_t sig_id = command[L2CAP_SIGNALING_COMMAND_SIGID_OFFSET];
+
+ // not for a particular channel, and not CONNECTION_REQUEST, ECHO_[REQUEST|RESPONSE], INFORMATION_REQUEST
+ if (code < 1 || code == ECHO_RESPONSE || code > INFORMATION_REQUEST){
+ return;
+ }
+
+ // general commands without an assigned channel
+ switch(code) {
+
+ case CONNECTION_REQUEST: {
+ uint16_t psm = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET);
+ uint16_t source_cid = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET+2);
+ l2cap_handle_connection_request(handle, sig_id, psm, source_cid);
+ return;
+ }
+
+ case ECHO_REQUEST:
+ l2cap_register_signaling_response(handle, code, sig_id, 0);
+ return;
+
+ case INFORMATION_REQUEST: {
+ uint16_t infoType = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET);
+ l2cap_register_signaling_response(handle, code, sig_id, infoType);
+ return;
+ }
+
+ default:
+ break;
+ }
+
+
+ // Get potential destination CID
+ uint16_t dest_cid = READ_BT_16(command, L2CAP_SIGNALING_COMMAND_DATA_OFFSET);
+
+ // Find channel for this sig_id and connection handle
+ linked_item_t *it;
+ for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
+ l2cap_channel_t * channel = (l2cap_channel_t *) it;
+ if (channel->handle == handle) {
+ if (code & 1) {
+ // match odd commands (responses) by previous signaling identifier
+ if (channel->local_sig_id == sig_id) {
+ l2cap_signaling_handler_channel(channel, command);
+ break;
+ }
+ } else {
+ // match even commands (requests) by local channel id
+ if (channel->local_cid == dest_cid) {
+ l2cap_signaling_handler_channel(channel, command);
+ break;
+ }
+ }
+ }
+ }
+}
+
+void l2cap_acl_handler( uint8_t *packet, uint16_t size ){
+
+ // Get Channel ID
+ uint16_t channel_id = READ_L2CAP_CHANNEL_ID(packet);
+ hci_con_handle_t handle = READ_ACL_CONNECTION_HANDLE(packet);
+
+ switch (channel_id) {
+
+ case L2CAP_CID_SIGNALING: {
+
+ uint16_t command_offset = 8;
+ while (command_offset < size) {
+
+ // handle signaling commands
+ l2cap_signaling_handler_dispatch(handle, &packet[command_offset]);
+
+ // increment command_offset
+ command_offset += L2CAP_SIGNALING_COMMAND_DATA_OFFSET + READ_BT_16(packet, command_offset + L2CAP_SIGNALING_COMMAND_LENGTH_OFFSET);
+ }
+ break;
+ }
+
+ case L2CAP_CID_ATTRIBUTE_PROTOCOL:
+ if (attribute_protocol_packet_handler) {
+ (*attribute_protocol_packet_handler)(ATT_DATA_PACKET, handle, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER);
+ }
+ break;
+
+ case L2CAP_CID_SECURITY_MANAGER_PROTOCOL:
+ if (security_protocol_packet_handler) {
+ (*security_protocol_packet_handler)(SM_DATA_PACKET, handle, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER);
+ }
+ break;
+
+ default: {
+ // Find channel for this channel_id and connection handle
+ l2cap_channel_t * channel = l2cap_get_channel_for_local_cid(channel_id);
+ if (channel) {
+ l2cap_dispatch(channel, L2CAP_DATA_PACKET, &packet[COMPLETE_L2CAP_HEADER], size-COMPLETE_L2CAP_HEADER);
+ }
+ break;
+ }
+ }
+
+ l2cap_run();
+}
+
+static void l2cap_packet_handler(uint8_t packet_type, uint8_t *packet, uint16_t size){
+ switch (packet_type) {
+ case HCI_EVENT_PACKET:
+ l2cap_event_handler(packet, size);
+ break;
+ case HCI_ACL_DATA_PACKET:
+ l2cap_acl_handler(packet, size);
+ break;
+ default:
+ break;
+ }
+}
+
+// finalize closed channel - l2cap_handle_disconnect_request & DISCONNECTION_RESPONSE
+void l2cap_finialize_channel_close(l2cap_channel_t *channel){
+ channel->state = L2CAP_STATE_CLOSED;
+ l2cap_emit_channel_closed(channel);
+ // discard channel
+ linked_list_remove(&l2cap_channels, (linked_item_t *) channel);
+ btstack_memory_l2cap_channel_free(channel);
+}
+
+l2cap_service_t * l2cap_get_service(uint16_t psm){
+ linked_item_t *it;
+
+ // close open channels
+ for (it = (linked_item_t *) l2cap_services; it ; it = it->next){
+ l2cap_service_t * service = ((l2cap_service_t *) it);
+ if ( service->psm == psm){
+ return service;
+ };
+ }
+ return NULL;
+}
+
+void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu){
+ // check for alread registered psm
+ // TODO: emit error event
+ l2cap_service_t *service = l2cap_get_service(psm);
+ if (service) {
+ log_error("l2cap_register_service_internal: PSM %u already registered\n", psm);
+ l2cap_emit_service_registered(connection, L2CAP_SERVICE_ALREADY_REGISTERED, psm);
+ return;
+ }
+
+ // alloc structure
+ // TODO: emit error event
+ service = (l2cap_service_t *) btstack_memory_l2cap_service_get();
+ if (!service) {
+ log_error("l2cap_register_service_internal: no memory for l2cap_service_t\n");
+ l2cap_emit_service_registered(connection, BTSTACK_MEMORY_ALLOC_FAILED, psm);
+ return;
+ }
+
+ // fill in
+ service->psm = psm;
+ service->mtu = mtu;
+ service->connection = connection;
+ service->packet_handler = packet_handler;
+
+ // add to services list
+ linked_list_add(&l2cap_services, (linked_item_t *) service);
+
+ // enable page scan
+ hci_connectable_control(1);
+
+ // done
+ l2cap_emit_service_registered(connection, 0, psm);
+}
+
+void l2cap_unregister_service_internal(void *connection, uint16_t psm){
+ l2cap_service_t *service = l2cap_get_service(psm);
+ if (!service) return;
+ linked_list_remove(&l2cap_services, (linked_item_t *) service);
+ btstack_memory_l2cap_service_free(service);
+
+ // disable page scan when no services registered
+ if (!linked_list_empty(&l2cap_services)) return;
+ hci_connectable_control(0);
+}
+
+//
+void l2cap_close_connection(void *connection){
+ linked_item_t *it;
+
+ // close open channels - note to myself: no channel is freed, so no new for fancy iterator tricks
+ l2cap_channel_t * channel;
+ for (it = (linked_item_t *) l2cap_channels; it ; it = it->next){
+ channel = (l2cap_channel_t *) it;
+ if (channel->connection == connection) {
+ channel->state = L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST;
+ }
+ }
+
+ // unregister services
+ it = (linked_item_t *) &l2cap_services;
+ while (it->next) {
+ l2cap_service_t * service = (l2cap_service_t *) it->next;
+ if (service->connection == connection){
+ it->next = it->next->next;
+ btstack_memory_l2cap_service_free(service);
+ } else {
+ it = it->next;
+ }
+ }
+
+ // process
+ l2cap_run();
+}
+
+// Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol
+void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id) {
+ switch(channel_id){
+ case L2CAP_CID_ATTRIBUTE_PROTOCOL:
+ attribute_protocol_packet_handler = packet_handler;
+ break;
+ case L2CAP_CID_SECURITY_MANAGER_PROTOCOL:
+ security_protocol_packet_handler = packet_handler;
+ break;
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/l2cap.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,201 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * l2cap.h
+ *
+ * Logical Link Control and Adaption Protocl (L2CAP)
+ *
+ * Created by Matthias Ringwald on 5/16/09.
+ */
+
+#pragma once
+
+#include "hci.h"
+#include "l2cap_signaling.h"
+#include <btstack/utils.h>
+#include <btstack/btstack.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+#define L2CAP_SIG_ID_INVALID 0
+
+#define L2CAP_HEADER_SIZE 4
+
+// size of HCI ACL + L2CAP Header for regular data packets (8)
+#define COMPLETE_L2CAP_HEADER (HCI_ACL_HEADER_SIZE + L2CAP_HEADER_SIZE)
+
+// minimum signaling MTU
+#define L2CAP_MINIMAL_MTU 48
+#define L2CAP_DEFAULT_MTU 672
+
+// check L2CAP MTU
+#if (L2CAP_MINIMAL_MTU + L2CAP_HEADER_SIZE) > HCI_ACL_PAYLOAD_SIZE
+#error "HCI_ACL_PAYLOAD_SIZE too small for minimal L2CAP MTU of 48 bytes"
+#endif
+
+// L2CAP Fixed Channel IDs
+#define L2CAP_CID_SIGNALING 0x0001
+#define L2CAP_CID_CONNECTIONLESS_CHANNEL 0x0002
+#define L2CAP_CID_ATTRIBUTE_PROTOCOL 0x0004
+#define L2CAP_CID_SIGNALING_LE 0x0005
+#define L2CAP_CID_SECURITY_MANAGER_PROTOCOL 0x0006
+
+void l2cap_init(void);
+void l2cap_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size));
+void l2cap_create_channel_internal(void * connection, btstack_packet_handler_t packet_handler, bd_addr_t address, uint16_t psm, uint16_t mtu);
+void l2cap_disconnect_internal(uint16_t local_cid, uint8_t reason);
+uint16_t l2cap_get_remote_mtu_for_local_cid(uint16_t local_cid);
+uint16_t l2cap_max_mtu(void);
+
+void l2cap_block_new_credits(uint8_t blocked);
+int l2cap_can_send_packet_now(uint16_t local_cid); // non-blocking UART write
+
+// get outgoing buffer and prepare data
+uint8_t *l2cap_get_outgoing_buffer(void);
+
+int l2cap_send_prepared(uint16_t local_cid, uint16_t len);
+int l2cap_send_internal(uint16_t local_cid, uint8_t *data, uint16_t len);
+
+int l2cap_send_prepared_connectionless(uint16_t handle, uint16_t cid, uint16_t len);
+int l2cap_send_connectionless(uint16_t handle, uint16_t cid, uint8_t *data, uint16_t len);
+
+void l2cap_close_connection(void *connection);
+
+void l2cap_register_service_internal(void *connection, btstack_packet_handler_t packet_handler, uint16_t psm, uint16_t mtu);
+void l2cap_unregister_service_internal(void *connection, uint16_t psm);
+
+void l2cap_accept_connection_internal(uint16_t local_cid);
+void l2cap_decline_connection_internal(uint16_t local_cid, uint8_t reason);
+
+// Bluetooth 4.0 - allows to register handler for Attribute Protocol and Security Manager Protocol
+void l2cap_register_fixed_channel(btstack_packet_handler_t packet_handler, uint16_t channel_id);
+
+
+// private structs
+typedef enum {
+ L2CAP_STATE_CLOSED = 1, // no baseband
+ L2CAP_STATE_WILL_SEND_CREATE_CONNECTION,
+ L2CAP_STATE_WAIT_CONNECTION_COMPLETE,
+ L2CAP_STATE_WAIT_CLIENT_ACCEPT_OR_REJECT,
+ L2CAP_STATE_WAIT_CONNECT_RSP, // from peer
+ L2CAP_STATE_CONFIG,
+ L2CAP_STATE_OPEN,
+ L2CAP_STATE_WAIT_DISCONNECT, // from application
+ L2CAP_STATE_WILL_SEND_CONNECTION_REQUEST,
+ L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_DECLINE,
+ L2CAP_STATE_WILL_SEND_CONNECTION_RESPONSE_ACCEPT,
+ L2CAP_STATE_WILL_SEND_DISCONNECT_REQUEST,
+ L2CAP_STATE_WILL_SEND_DISCONNECT_RESPONSE,
+} L2CAP_STATE;
+
+typedef enum {
+ L2CAP_CHANNEL_STATE_VAR_NONE = 0,
+ L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_REQ = 1 << 0,
+ L2CAP_CHANNEL_STATE_VAR_RCVD_CONF_RSP = 1 << 1,
+ L2CAP_CHANNEL_STATE_VAR_SEND_CONF_REQ = 1 << 2,
+ L2CAP_CHANNEL_STATE_VAR_SEND_CONF_RSP = 1 << 3,
+ L2CAP_CHANNEL_STATE_VAR_SENT_CONF_REQ = 1 << 4,
+ L2CAP_CHANNEL_STATE_VAR_SENT_CONF_RSP = 1 << 5,
+} L2CAP_CHANNEL_STATE_VAR;
+
+// info regarding an actual coneection
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ L2CAP_STATE state;
+ L2CAP_CHANNEL_STATE_VAR state_var;
+
+ bd_addr_t address;
+ hci_con_handle_t handle;
+
+ uint8_t remote_sig_id; // used by other side, needed for delayed response
+ uint8_t local_sig_id; // own signaling identifier
+
+ uint16_t local_cid;
+ uint16_t remote_cid;
+
+ uint16_t local_mtu;
+ uint16_t remote_mtu;
+
+ uint16_t psm;
+
+ uint8_t packets_granted; // number of L2CAP/ACL packets client is allowed to send
+
+ uint8_t reason; // used in decline internal
+
+ // client connection
+ void * connection;
+
+ // internal connection
+ btstack_packet_handler_t packet_handler;
+
+} l2cap_channel_t;
+
+// info regarding potential connections
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ // service id
+ uint16_t psm;
+
+ // incoming MTU
+ uint16_t mtu;
+
+ // client connection
+ void *connection;
+
+ // internal connection
+ btstack_packet_handler_t packet_handler;
+
+} l2cap_service_t;
+
+
+typedef struct l2cap_signaling_response {
+ hci_con_handle_t handle;
+ uint8_t sig_id;
+ uint8_t code;
+ uint16_t data; // infoType for INFORMATION REQUEST, result for CONNECTION request
+} l2cap_signaling_response_t;
+
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/l2cap_signaling.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,128 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * l2cap_signaling.h
+ *
+ * Created by Matthias Ringwald on 7/23/09.
+ */
+
+#include "l2cap_signaling.h"
+
+#include <string.h>
+
+static char *l2cap_signaling_commands_format[] = {
+"D", // 0x01 command reject: reason {cmd not understood (0), sig MTU exceeded (2:max sig MTU), invalid CID (4:req CID)}, data len, data
+"22", // 0x02 connection request: PSM, Source CID
+"2222", // 0x03 connection response: Dest CID, Source CID, Result, Status
+"22D", // 0x04 config request: Dest CID, Flags, Configuration options
+"222D", // 0x05 config response: Source CID, Flags, Result, Configuration options
+"22", // 0x06 disconection request: Dest CID, Source CID
+"22", // 0x07 disconection response: Dest CID, Source CID
+"D", // 0x08 echo request: Data
+"D", // 0x09 echo response: Data
+"2", // 0x0a information request: InfoType {1=Connectionless MTU, 2=Extended features supported}
+"22D", // 0x0b information response: InfoType, Result, Data
+};
+
+uint8_t sig_seq_nr = 0xff;
+uint16_t source_cid = 0x40;
+
+uint8_t l2cap_next_sig_id(void){
+ if (sig_seq_nr == 0xff) {
+ sig_seq_nr = 1;
+ } else {
+ sig_seq_nr++;
+ }
+ return sig_seq_nr;
+}
+
+uint16_t l2cap_next_local_cid(void){
+ return source_cid++;
+}
+
+uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer, hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr){
+
+ // 0 - Connection handle : PB=10 : BC=00
+ bt_store_16(acl_buffer, 0, handle | (2 << 12) | (0 << 14));
+ // 6 - L2CAP channel = 1
+ bt_store_16(acl_buffer, 6, 1);
+ // 8 - Code
+ acl_buffer[8] = cmd;
+ // 9 - id (!= 0 sequentially)
+ acl_buffer[9] = identifier;
+
+ // 12 - L2CAP signaling parameters
+ uint16_t pos = 12;
+ const char *format = l2cap_signaling_commands_format[cmd-1];
+ uint16_t word;
+ uint8_t * ptr;
+ while (*format) {
+ switch(*format) {
+ case '1': // 8 bit value
+ case '2': // 16 bit value
+ word = va_arg(argptr, int);
+ // minimal va_arg is int: 2 bytes on 8+16 bit CPUs
+ acl_buffer[pos++] = word & 0xff;
+ if (*format == '2') {
+ acl_buffer[pos++] = word >> 8;
+ }
+ break;
+ case 'D': // variable data. passed: len, ptr
+ word = va_arg(argptr, int);
+ ptr = va_arg(argptr, uint8_t *);
+ memcpy(&acl_buffer[pos], ptr, word);
+ pos += word;
+ break;
+ default:
+ break;
+ }
+ format++;
+ };
+ va_end(argptr);
+
+ // Fill in various length fields: it's the number of bytes following for ACL lenght and l2cap parameter length
+ // - the l2cap payload length is counted after the following channel id (only payload)
+
+ // 2 - ACL length
+ bt_store_16(acl_buffer, 2, pos - 4);
+ // 4 - L2CAP packet length
+ bt_store_16(acl_buffer, 4, pos - 6 - 2);
+ // 10 - L2CAP signaling parameter length
+ bt_store_16(acl_buffer, 10, pos - 12);
+
+ return pos;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/l2cap_signaling.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,67 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * l2cap_signaling.h
+ *
+ * Created by Matthias Ringwald on 7/23/09.
+ */
+
+#pragma once
+
+#include <stdint.h>
+#include <stdarg.h>
+#include <btstack/utils.h>
+#include <btstack/hci_cmds.h>
+
+typedef enum {
+ COMMAND_REJECT = 1,
+ CONNECTION_REQUEST,
+ CONNECTION_RESPONSE,
+ CONFIGURE_REQUEST,
+ CONFIGURE_RESPONSE,
+ DISCONNECTION_REQUEST,
+ DISCONNECTION_RESPONSE,
+ ECHO_REQUEST,
+ ECHO_RESPONSE,
+ INFORMATION_REQUEST,
+ INFORMATION_RESPONSE
+} L2CAP_SIGNALING_COMMANDS;
+
+uint16_t l2cap_create_signaling_internal(uint8_t * acl_buffer,hci_con_handle_t handle, L2CAP_SIGNALING_COMMANDS cmd, uint8_t identifier, va_list argptr);
+uint8_t l2cap_next_sig_id(void);
+uint16_t l2cap_next_local_cid(void);
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/linked_list.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,147 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * linked_list.c
+ *
+ * Created by Matthias Ringwald on 7/13/09.
+ */
+
+#include <btstack/linked_list.h>
+#include <stdlib.h>
+/**
+ * tests if list is empty
+ */
+int linked_list_empty(linked_list_t * list){
+ return *list == (void *) 0;
+}
+
+/**
+ * linked_list_get_last_item
+ */
+linked_item_t * linked_list_get_last_item(linked_list_t * list){ // <-- find the last item in the list
+ linked_item_t *lastItem = NULL;
+ linked_item_t *it;
+ for (it = *list; it ; it = it->next){
+ if (it) {
+ lastItem = it;
+ }
+ }
+ return lastItem;
+}
+
+
+/**
+ * linked_list_add
+ */
+void linked_list_add(linked_list_t * list, linked_item_t *item){ // <-- add item to list
+ // check if already in list
+ linked_item_t *it;
+ for (it = *list; it ; it = it->next){
+ if (it == item) {
+ return;
+ }
+ }
+ // add first
+ item->next = *list;
+ *list = item;
+}
+
+void linked_list_add_tail(linked_list_t * list, linked_item_t *item){ // <-- add item to list as last element
+ // check if already in list
+ linked_item_t *it;
+ for (it = (linked_item_t *) list; it->next ; it = it->next){
+ if (it->next == item) {
+ return;
+ }
+ }
+ item->next = (linked_item_t*) 0;
+ it->next = item;
+}
+
+/**
+ * Remove data_source from run loop
+ *
+ * @note: assumes that data_source_t.next is first element in data_source
+ */
+int linked_list_remove(linked_list_t * list, linked_item_t *item){ // <-- remove item from list
+ linked_item_t *it;
+ for (it = (linked_item_t *) list; it ; it = it->next){
+ if (it->next == item){
+ it->next = item->next;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+void linked_item_set_user(linked_item_t *item, void *user_data){
+ item->next = (linked_item_t *) 0;
+ item->user_data = user_data;
+}
+
+void * linked_item_get_user(linked_item_t *item) {
+ return item->user_data;
+}
+
+#if 0
+#include <stdio.h>
+void test_linked_list(){
+ linked_list_t testList = 0;
+ linked_item_t itemA;
+ linked_item_t itemB;
+ linked_item_t itemC;
+ linked_item_set_user(&itemA, (void *) 0);
+ linked_item_set_user(&itemB, (void *) 0);
+ linked_list_add(&testList, &itemA);
+ linked_list_add(&testList, &itemB);
+ linked_list_add_tail(&testList, &itemC);
+ // linked_list_remove(&testList, &itemB);
+ linked_item_t *it;
+ for (it = (linked_item_t *) &testList; it ; it = it->next){
+ if (it->next == &itemA) printf("Item A\n");
+ if (it->next == &itemB) printf("Item B\n");
+ if (it->next == &itemC) printf("Item C\n");
+ /* if (it->next == &itemB){
+ it->next = it->next;
+ printf(" remove\n");
+ } else {
+ printf(" keep\n");
+
+ */
+ }
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/remote_device_db.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,95 @@
+/*
+ * 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
+ *
+ */
+
+/**
+ * interface to provide link key and remote name storage
+ */
+
+#pragma once
+
+#include <btstack/utils.h>
+
+typedef struct {
+
+ // management
+ void (*open)(void);
+ void (*close)(void);
+
+ // link key
+ int (*get_link_key)(bd_addr_t *bd_addr, link_key_t *link_key);
+ void (*put_link_key)(bd_addr_t *bd_addr, link_key_t *key);
+ void (*delete_link_key)(bd_addr_t *bd_addr);
+
+ // remote name
+ int (*get_name)(bd_addr_t *bd_addr, device_name_t *device_name);
+ void (*put_name)(bd_addr_t *bd_addr, device_name_t *device_name);
+ void (*delete_name)(bd_addr_t *bd_addr);
+
+ // persistent rfcomm channel
+ uint8_t (*persistent_rfcomm_channel)(char *servicename);
+
+} remote_device_db_t;
+
+extern remote_device_db_t remote_device_db_iphone;
+extern const remote_device_db_t remote_device_db_memory;
+
+// MARK: non-persisten implementation
+#include <btstack/linked_list.h>
+#define MAX_NAME_LEN 32
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ bd_addr_t bd_addr;
+} db_mem_device_t;
+
+typedef struct {
+ db_mem_device_t device;
+ link_key_t link_key;
+} db_mem_device_link_key_t;
+
+typedef struct {
+ db_mem_device_t device;
+ char device_name[MAX_NAME_LEN];
+} db_mem_device_name_t;
+
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ char service_name[MAX_NAME_LEN];
+ uint8_t channel;
+} db_mem_service_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/remote_device_db_memory.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,198 @@
+/*
+ * 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
+ *
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "remote_device_db.h"
+#include "btstack_memory.h"
+#include "debug.h"
+
+#include <btstack/utils.h>
+#include <btstack/linked_list.h>
+
+// This lists should be only accessed by tests.
+linked_list_t db_mem_link_keys = NULL;
+linked_list_t db_mem_names = NULL;
+static linked_list_t db_mem_services = NULL;
+
+// Device info
+static void db_open(void){
+}
+
+static void db_close(void){
+}
+
+static db_mem_device_t * get_item(linked_list_t list, bd_addr_t *bd_addr) {
+ linked_item_t *it;
+ for (it = (linked_item_t *) list; it ; it = it->next){
+ db_mem_device_t * item = (db_mem_device_t *) it;
+ if (BD_ADDR_CMP(item->bd_addr, *bd_addr) == 0) {
+ return item;
+ }
+ }
+ return NULL;
+}
+
+static int get_name(bd_addr_t *bd_addr, device_name_t *device_name) {
+ db_mem_device_name_t * item = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
+
+ if (!item) return 0;
+
+ strncpy((char*)device_name, item->device_name, MAX_NAME_LEN);
+
+ linked_list_remove(&db_mem_names, (linked_item_t *) item);
+ linked_list_add(&db_mem_names, (linked_item_t *) item);
+
+ return 1;
+}
+
+static int get_link_key(bd_addr_t *bd_addr, link_key_t *link_key) {
+ db_mem_device_link_key_t * item = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
+
+ if (!item) return 0;
+
+ memcpy(link_key, item->link_key, LINK_KEY_LEN);
+
+ linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
+ linked_list_add(&db_mem_link_keys, (linked_item_t *) item);
+
+ return 1;
+}
+
+static void delete_link_key(bd_addr_t *bd_addr){
+ db_mem_device_t * item = get_item(db_mem_link_keys, bd_addr);
+
+ if (!item) return;
+
+ linked_list_remove(&db_mem_link_keys, (linked_item_t *) item);
+ btstack_memory_db_mem_device_link_key_free(item);
+}
+
+
+static void put_link_key(bd_addr_t *bd_addr, link_key_t *link_key){
+ db_mem_device_link_key_t * existingRecord = (db_mem_device_link_key_t *) get_item(db_mem_link_keys, bd_addr);
+
+ if (existingRecord){
+ memcpy(existingRecord->link_key, link_key, LINK_KEY_LEN);
+ return;
+ }
+
+ // Record not found, create new one for this device
+ db_mem_device_link_key_t * newItem = (db_mem_device_link_key_t*) btstack_memory_db_mem_device_link_key_get();
+ if (!newItem){
+ newItem = (db_mem_device_link_key_t*)linked_list_get_last_item(&db_mem_link_keys);
+ }
+
+ if (!newItem) return;
+
+ memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
+ memcpy(newItem->link_key, link_key, LINK_KEY_LEN);
+ linked_list_add(&db_mem_link_keys, (linked_item_t *) newItem);
+}
+
+static void delete_name(bd_addr_t *bd_addr){
+ db_mem_device_t * item = get_item(db_mem_names, bd_addr);
+
+ if (!item) return;
+
+ linked_list_remove(&db_mem_names, (linked_item_t *) item);
+ btstack_memory_db_mem_device_name_free(item);
+}
+
+static void put_name(bd_addr_t *bd_addr, device_name_t *device_name){
+ db_mem_device_name_t * existingRecord = (db_mem_device_name_t *) get_item(db_mem_names, bd_addr);
+
+ if (existingRecord){
+ strncpy(existingRecord->device_name, (const char*) device_name, MAX_NAME_LEN);
+ return;
+ }
+
+ // Record not found, create a new one for this device
+ db_mem_device_name_t * newItem = (db_mem_device_name_t *) btstack_memory_db_mem_device_name_get();
+ if (!newItem) {
+ newItem = (db_mem_device_name_t*)linked_list_get_last_item(&db_mem_names);
+ };
+
+ if (!newItem) return;
+
+ memcpy(newItem->device.bd_addr, bd_addr, sizeof(bd_addr_t));
+ strncpy(newItem->device_name, (const char*) device_name, MAX_NAME_LEN);
+ linked_list_add(&db_mem_names, (linked_item_t *) newItem);
+}
+
+
+// MARK: PERSISTENT RFCOMM CHANNEL ALLOCATION
+
+static uint8_t persistent_rfcomm_channel(char *serviceName){
+ linked_item_t *it;
+ db_mem_service_t * item;
+ uint8_t max_channel = 1;
+
+ for (it = (linked_item_t *) db_mem_services; it ; it = it->next){
+ item = (db_mem_service_t *) it;
+ if (strncmp(item->service_name, serviceName, MAX_NAME_LEN) == 0) {
+ // Match found
+ return item->channel;
+ }
+
+ // TODO prevent overflow
+ if (item->channel >= max_channel) max_channel = item->channel + 1;
+ }
+
+ // Allocate new persistant channel
+ db_mem_service_t * newItem = (db_mem_service_t *) btstack_memory_db_mem_service_get();
+
+ if (!newItem) return 0;
+
+ strncpy(newItem->service_name, serviceName, MAX_NAME_LEN);
+ newItem->channel = max_channel;
+ linked_list_add(&db_mem_services, (linked_item_t *) newItem);
+ return max_channel;
+}
+
+
+const remote_device_db_t remote_device_db_memory = {
+ db_open,
+ db_close,
+ get_link_key,
+ put_link_key,
+ delete_link_key,
+ get_name,
+ put_name,
+ delete_name,
+ persistent_rfcomm_channel
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/rfcomm.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,1869 @@
+/*
+ * 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();
+}
+
+
+
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/rfcomm.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,304 @@
+/*
+ * 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.h
+ */
+
+#include <btstack/btstack.h>
+#include <btstack/utils.h>
+
+#include <stdint.h>
+
+#if defined __cplusplus
+extern "C" {
+#endif
+
+void rfcomm_init(void);
+
+// register packet handler
+void rfcomm_packet_handler(uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size);
+void rfcomm_register_packet_handler(void (*handler)(void * connection, uint8_t packet_type,
+ uint16_t channel, uint8_t *packet, uint16_t size));
+
+// BTstack Internal RFCOMM API
+void rfcomm_create_channel_internal(void * connection, bd_addr_t *addr, uint8_t channel);
+void rfcomm_create_channel_with_initial_credits_internal(void * connection, bd_addr_t *addr, uint8_t server_channel, uint8_t initial_credits);
+void rfcomm_disconnect_internal(uint16_t rfcomm_cid);
+void rfcomm_register_service_internal(void * connection, uint8_t channel, uint16_t max_frame_size);
+void rfcomm_register_service_with_initial_credits_internal(void * connection, uint8_t channel, uint16_t max_frame_size, uint8_t initial_credits);
+void rfcomm_unregister_service_internal(uint8_t service_channel);
+void rfcomm_accept_connection_internal(uint16_t rfcomm_cid);
+void rfcomm_decline_connection_internal(uint16_t rfcomm_cid);
+void rfcomm_grant_credits(uint16_t rfcomm_cid, uint8_t credits);
+int rfcomm_send_internal(uint16_t rfcomm_cid, uint8_t *data, uint16_t len);
+void rfcomm_close_connection(void *connection);
+
+#define UNLIMITED_INCOMING_CREDITS 0xff
+
+// private structs
+typedef enum {
+ RFCOMM_MULTIPLEXER_CLOSED = 1,
+ RFCOMM_MULTIPLEXER_W4_CONNECT, // outgoing
+ RFCOMM_MULTIPLEXER_SEND_SABM_0, // "
+ RFCOMM_MULTIPLEXER_W4_UA_0, // "
+ RFCOMM_MULTIPLEXER_W4_SABM_0, // incoming
+ RFCOMM_MULTIPLEXER_SEND_UA_0,
+ RFCOMM_MULTIPLEXER_OPEN,
+ RFCOMM_MULTIPLEXER_SEND_UA_0_AND_DISC
+} RFCOMM_MULTIPLEXER_STATE;
+
+typedef enum {
+ MULT_EV_READY_TO_SEND = 1,
+
+} RFCOMM_MULTIPLEXER_EVENT;
+
+typedef enum {
+ RFCOMM_CHANNEL_CLOSED = 1,
+ RFCOMM_CHANNEL_W4_MULTIPLEXER,
+ RFCOMM_CHANNEL_SEND_UIH_PN,
+ RFCOMM_CHANNEL_W4_PN_RSP,
+ RFCOMM_CHANNEL_SEND_SABM_W4_UA,
+ RFCOMM_CHANNEL_W4_UA,
+ RFCOMM_CHANNEL_INCOMING_SETUP,
+ RFCOMM_CHANNEL_DLC_SETUP,
+ RFCOMM_CHANNEL_OPEN,
+ RFCOMM_CHANNEL_SEND_UA_AFTER_DISC,
+ RFCOMM_CHANNEL_SEND_DISC,
+ RFCOMM_CHANNEL_SEND_DM,
+
+} RFCOMM_CHANNEL_STATE;
+
+/*
+typedef 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,
+} 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,
+ CH_EVT_RCVD_UA,
+ CH_EVT_RCVD_PN,
+ CH_EVT_RCVD_PN_RSP,
+ CH_EVT_RCVD_DISC,
+ CH_EVT_RCVD_DM,
+ CH_EVT_RCVD_MSC_CMD,
+ CH_EVT_RCVD_MSC_RSP,
+ CH_EVT_RCVD_RPN_CMD,
+ CH_EVT_RCVD_RPN_REQ,
+ CH_EVT_RCVD_CREDITS,
+ CH_EVT_MULTIPLEXER_READY,
+ CH_EVT_READY_TO_SEND,
+} RFCOMM_CHANNEL_EVENT;
+
+typedef struct rfcomm_channel_event {
+ RFCOMM_CHANNEL_EVENT type;
+} rfcomm_channel_event_t;
+
+typedef struct rfcomm_channel_event_pn {
+ rfcomm_channel_event_t super;
+ uint16_t max_frame_size;
+ uint8_t priority;
+ uint8_t credits_outgoing;
+} rfcomm_channel_event_pn_t;
+
+typedef struct rfcomm_rpn_data {
+ uint8_t baud_rate;
+ uint8_t flags;
+ uint8_t flow_control;
+ uint8_t xon;
+ uint8_t xoff;
+ uint8_t parameter_mask_0; // first byte
+ uint8_t parameter_mask_1; // second byte
+} rfcomm_rpn_data_t;
+
+typedef struct rfcomm_channel_event_rpn {
+ rfcomm_channel_event_t super;
+ rfcomm_rpn_data_t data;
+} rfcomm_channel_event_rpn_t;
+
+// info regarding potential connections
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ // server channel
+ uint8_t server_channel;
+
+ // incoming max frame size
+ uint16_t max_frame_size;
+
+ // use incoming flow control
+ uint8_t incoming_flow_control;
+
+ // initial incoming credits
+ uint8_t incoming_initial_credits;
+
+ // client connection
+ void *connection;
+
+ // internal connection
+ btstack_packet_handler_t packet_handler;
+
+} rfcomm_service_t;
+
+// info regarding multiplexer
+// note: spec mandates single multplexer per device combination
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ timer_source_t timer;
+ int timer_active;
+
+ RFCOMM_MULTIPLEXER_STATE state;
+
+ uint16_t l2cap_cid;
+ uint8_t l2cap_credits;
+
+ bd_addr_t remote_addr;
+ hci_con_handle_t con_handle;
+
+ uint8_t outgoing;
+
+ // hack to deal with authentication failure only observed by remote side
+ uint8_t at_least_one_connection;
+
+ uint16_t max_frame_size;
+
+ // send DM for DLCI != 0
+ uint8_t send_dm_for_dlci;
+
+} rfcomm_multiplexer_t;
+
+// info regarding an actual coneection
+typedef struct {
+ // linked list - assert: first field
+ linked_item_t item;
+
+ rfcomm_multiplexer_t *multiplexer;
+ uint16_t rfcomm_cid;
+ uint8_t outgoing;
+ uint8_t dlci;
+
+ // number of packets granted to client
+ uint8_t packets_granted;
+
+ // credits for outgoing traffic
+ uint8_t credits_outgoing;
+
+ // number of packets remote will be granted
+ uint8_t new_credits_incoming;
+
+ // credits for incoming traffic
+ uint8_t credits_incoming;
+
+ // use incoming flow control
+ uint8_t incoming_flow_control;
+
+ // channel state
+ RFCOMM_CHANNEL_STATE state;
+
+ // state variables used in RFCOMM_CHANNEL_INCOMING
+ RFCOMM_CHANNEL_STATE_VAR state_var;
+
+ // priority set by incoming side in PN
+ uint8_t pn_priority;
+
+ // negotiated frame size
+ uint16_t max_frame_size;
+
+ // rpn data
+ rfcomm_rpn_data_t rpn_data;
+
+ // server channel (see rfcomm_service_t) - NULL => outgoing channel
+ rfcomm_service_t * service;
+
+ // internal connection
+ btstack_packet_handler_t packet_handler;
+
+ // client connection
+ void * connection;
+
+} rfcomm_channel_t;
+
+
+#if defined __cplusplus
+}
+#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/run_loop.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,153 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * run_loop.c
+ *
+ * Created by Matthias Ringwald on 6/6/09.
+ */
+
+#include <btstack/run_loop.h>
+
+#include <stdio.h>
+#include <stdlib.h> // exit()
+
+#include "run_loop_private.h"
+
+#include "debug.h"
+#include "config.h"
+
+static run_loop_t * the_run_loop = NULL;
+
+extern const run_loop_t run_loop_embedded;
+
+#ifdef USE_POSIX_RUN_LOOP
+extern run_loop_t run_loop_posix;
+#endif
+
+#ifdef USE_COCOA_RUN_LOOP
+extern run_loop_t run_loop_cocoa;
+#endif
+
+// assert run loop initialized
+void run_loop_assert(void){
+#ifndef EMBEDDED
+ if (!the_run_loop){
+ log_error("ERROR: run_loop function called before run_loop_init!\n");
+ exit(10);
+ }
+#endif
+}
+
+/**
+ * Add data_source to run_loop
+ */
+void run_loop_add_data_source(data_source_t *ds){
+ run_loop_assert();
+ the_run_loop->add_data_source(ds);
+}
+
+/**
+ * Remove data_source from run loop
+ */
+int run_loop_remove_data_source(data_source_t *ds){
+ run_loop_assert();
+ return the_run_loop->remove_data_source(ds);
+}
+
+/**
+ * Add timer to run_loop (keep list sorted)
+ */
+void run_loop_add_timer(timer_source_t *ts){
+ run_loop_assert();
+ the_run_loop->add_timer(ts);
+}
+
+/**
+ * Remove timer from run loop
+ */
+int run_loop_remove_timer(timer_source_t *ts){
+ run_loop_assert();
+ return the_run_loop->remove_timer(ts);
+}
+
+void run_loop_timer_dump(){
+ run_loop_assert();
+ the_run_loop->dump_timer();
+}
+
+/**
+ * Execute run_loop
+ */
+void run_loop_execute() {
+ run_loop_assert();
+ the_run_loop->execute();
+}
+
+// init must be called before any other run_loop call
+void run_loop_init(RUN_LOOP_TYPE type){
+#ifndef EMBEDDED
+ if (the_run_loop){
+ log_error("ERROR: run loop initialized twice!\n");
+ exit(10);
+ }
+#endif
+ switch (type) {
+#ifdef EMBEDDED
+ case RUN_LOOP_EMBEDDED:
+ the_run_loop = (run_loop_t*) &run_loop_embedded;
+ break;
+#endif
+#ifdef USE_POSIX_RUN_LOOP
+ case RUN_LOOP_POSIX:
+ the_run_loop = &run_loop_posix;
+ break;
+#endif
+#ifdef USE_COCOA_RUN_LOOP
+ case RUN_LOOP_COCOA:
+ the_run_loop = &run_loop_cocoa;
+ break;
+#endif
+ default:
+#ifndef EMBEDDED
+ log_error("ERROR: invalid run loop type %u selected!\n", type);
+ exit(10);
+#endif
+ break;
+ }
+ the_run_loop->init();
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/run_loop_embedded.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,218 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * run_loop_embedded.c
+ *
+ * For this run loop, we assume that there's no global way to wait for a list
+ * of data sources to get ready. Instead, each data source has to queried
+ * individually. Calling ds->isReady() before calling ds->process() doesn't
+ * make sense, so we just poll each data source round robin.
+ *
+ * To support an idle state, where an MCU could go to sleep, the process function
+ * has to return if it has to called again as soon as possible
+ *
+ * After calling process() on every data source and evaluating the pending timers,
+ * the idle hook gets called if no data source did indicate that it needs to be
+ * called right away.
+ *
+ */
+
+
+#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"
+
+#include <stddef.h> // NULL
+
+// the run loop
+static linked_list_t data_sources;
+
+static linked_list_t timers;
+
+#ifdef HAVE_TICK
+static uint32_t system_ticks;
+#endif
+
+static int trigger_event_received = 0;
+
+/**
+ * trigger run loop iteration
+ */
+void embedded_trigger(void){
+ trigger_event_received = 1;
+}
+
+/**
+ * Add data_source to run_loop
+ */
+void embedded_add_data_source(data_source_t *ds){
+ linked_list_add(&data_sources, (linked_item_t *) ds);
+}
+
+/**
+ * Remove data_source from run loop
+ */
+int embedded_remove_data_source(data_source_t *ds){
+ return linked_list_remove(&data_sources, (linked_item_t *) ds);
+}
+
+/**
+ * Add timer to run_loop (keep list sorted)
+ */
+void embedded_add_timer(timer_source_t *ts){
+#ifdef HAVE_TICK
+ linked_item_t *it;
+ for (it = (linked_item_t *) &timers; it->next ; it = it->next){
+ if (ts->timeout < ((timer_source_t *) it->next)->timeout) {
+ break;
+ }
+ }
+ ts->item.next = it->next;
+ it->next = (linked_item_t *) ts;
+ // log_info("Added timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
+ // embedded_dump_timer();
+#endif
+}
+
+/**
+ * Remove timer from run loop
+ */
+int embedded_remove_timer(timer_source_t *ts){
+#ifdef HAVE_TICK
+ // log_info("Removed timer %x at %u\n", (int) ts, (unsigned int) ts->timeout.tv_sec);
+ return linked_list_remove(&timers, (linked_item_t *) ts);
+#else
+ return 0;
+#endif
+}
+
+void embedded_dump_timer(void){
+#ifdef HAVE_TICK
+#ifdef ENABLE_LOG_INFO
+ linked_item_t *it;
+ int i = 0;
+ for (it = (linked_item_t *) timers; it ; it = it->next){
+ timer_source_t *ts = (timer_source_t*) it;
+ log_info("timer %u, timeout %u\n", i, (unsigned int) ts->timeout);
+ }
+#endif
+#endif
+}
+
+/**
+ * Execute run_loop
+ */
+void embedded_execute(void) {
+ data_source_t *ds;
+
+ while (1) {
+
+ // process data sources
+ data_source_t *next;
+ for (ds = (data_source_t *) data_sources; ds != NULL ; ds = next){
+ next = (data_source_t *) ds->item.next; // cache pointer to next data_source to allow data source to remove itself
+ ds->process(ds);
+ }
+
+#ifdef HAVE_TICK
+ // process timers
+ while (timers) {
+ timer_source_t *ts = (timer_source_t *) timers;
+ if (ts->timeout > system_ticks) break;
+ run_loop_remove_timer(ts);
+ ts->process(ts);
+ }
+#endif
+
+ // disable IRQs and check if run loop iteration has been requested. if not, go to sleep
+ hal_cpu_disable_irqs();
+ if (trigger_event_received){
+ hal_cpu_enable_irqs_and_sleep();
+ continue;
+ }
+ hal_cpu_enable_irqs();
+ }
+}
+
+#ifdef HAVE_TICK
+static void embedded_tick_handler(void){
+ system_ticks++;
+ trigger_event_received = 1;
+}
+
+uint32_t embedded_get_ticks(void){
+ return system_ticks;
+}
+
+uint32_t embedded_ticks_for_ms(uint32_t time_in_ms){
+ return time_in_ms / hal_tick_get_tick_period_in_ms();
+}
+
+// set timer
+void run_loop_set_timer(timer_source_t *ts, uint32_t timeout_in_ms){
+ uint32_t ticks = embedded_ticks_for_ms(timeout_in_ms);
+ if (ticks == 0) ticks++;
+ ts->timeout = system_ticks + ticks;
+}
+#endif
+
+void embedded_init(void){
+
+ data_sources = NULL;
+
+#ifdef HAVE_TICK
+ timers = NULL;
+ system_ticks = 0;
+ hal_tick_init();
+ hal_tick_set_handler(&embedded_tick_handler);
+#endif
+}
+
+extern const run_loop_t run_loop_embedded;
+const run_loop_t run_loop_embedded = {
+ &embedded_init,
+ &embedded_add_data_source,
+ &embedded_remove_data_source,
+ &embedded_add_timer,
+ &embedded_remove_timer,
+ &embedded_execute,
+ &embedded_dump_timer
+};
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/run_loop_private.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,68 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * run_loop_private.h
+ *
+ * Created by Matthias Ringwald on 6/6/09.
+ */
+
+#pragma once
+
+#include <btstack/run_loop.h>
+
+#ifdef HAVE_TIME
+#include <sys/time.h>
+
+// compare timeval or timers - NULL is assumed to be before the Big Bang
+int run_loop_timeval_compare(struct timeval *a, struct timeval *b);
+int run_loop_timer_compare(timer_source_t *a, timer_source_t *b);
+
+#endif
+
+//
+void run_loop_timer_dump(void);
+
+// 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);
+} run_loop_t;
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/sdp.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,662 @@
+/*
+ * 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;
+ }
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/sdp.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,96 @@
+/*
+ * 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);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/sdp_util.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,698 @@
+/*
+ * 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);
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/BTstack/utils.c Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,184 @@
+/*
+ * 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
+ *
+ */
+
+/*
+ * utils.c
+ *
+ * General utility functions
+ *
+ * Created by Matthias Ringwald on 7/23/09.
+ */
+
+#include "config.h"
+#include <btstack/utils.h>
+#include <stdio.h>
+#include "debug.h"
+
+void bt_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
+ buffer[pos++] = value;
+ buffer[pos++] = value >> 8;
+}
+
+void bt_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
+ buffer[pos++] = value;
+ buffer[pos++] = value >> 8;
+ buffer[pos++] = value >> 16;
+ buffer[pos++] = value >> 24;
+}
+
+void net_store_16(uint8_t *buffer, uint16_t pos, uint16_t value){
+ buffer[pos++] = value >> 8;
+ buffer[pos++] = value;
+}
+
+void net_store_32(uint8_t *buffer, uint16_t pos, uint32_t value){
+ buffer[pos++] = value >> 24;
+ buffer[pos++] = value >> 16;
+ buffer[pos++] = value >> 8;
+ buffer[pos++] = value;
+}
+
+void bt_flip_addr(bd_addr_t dest, bd_addr_t src){
+ dest[0] = src[5];
+ dest[1] = src[4];
+ dest[2] = src[3];
+ dest[3] = src[2];
+ dest[4] = src[1];
+ dest[5] = src[0];
+}
+
+void hexdump(void *data, int size){
+ int i;
+ for (i=0; i<size;i++){
+ log_info("%02X ", ((uint8_t *)data)[i]);
+ }
+ log_info("\n");
+}
+
+void printUUID(uint8_t *uuid) {
+ log_info("%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+ uuid[0], uuid[1], uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7],
+ uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], uuid[14], uuid[15]);
+}
+
+static char bd_addr_to_str_buffer[6*3]; // 12:45:78:01:34:67\0
+char * bd_addr_to_str(bd_addr_t addr){
+ sprintf(bd_addr_to_str_buffer, "%02x:%02x:%02x:%02x:%02x:%02x", addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+ return (char *) bd_addr_to_str_buffer;
+}
+
+void print_bd_addr( bd_addr_t addr){
+ log_info("%s", bd_addr_to_str(addr));
+}
+
+#ifndef EMBEDDED
+int sscan_bd_addr(uint8_t * addr_string, bd_addr_t addr){
+ unsigned int bd_addr_buffer[BD_ADDR_LEN]; //for sscanf, integer needed
+ // reset result buffer
+ int i;
+ for (i = 0; i < BD_ADDR_LEN; i++) {
+ bd_addr_buffer[i] = 0;
+ }
+
+ // parse
+ int result = sscanf( (char *) addr_string, "%2x:%2x:%2x:%2x:%2x:%2x", &bd_addr_buffer[0], &bd_addr_buffer[1], &bd_addr_buffer[2],
+ &bd_addr_buffer[3], &bd_addr_buffer[4], &bd_addr_buffer[5]);
+ // store
+ if (result == 6){
+ for (i = 0; i < BD_ADDR_LEN; i++) {
+ addr[i] = (uint8_t) bd_addr_buffer[i];
+ }
+ }
+ return (result == 6);
+}
+#endif
+
+/*
+ * CRC (reversed crc) lookup table as calculated by the table generator in ETSI TS 101 369 V6.3.0.
+ */
+static const uint8_t crc8table[256] = { /* reversed, 8-bit, poly=0x07 */
+ 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B,
+ 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67,
+ 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43,
+ 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F,
+ 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B,
+ 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17,
+ 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33,
+ 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F,
+ 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B,
+ 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87,
+ 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3,
+ 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF,
+ 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB,
+ 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7,
+ 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3,
+ 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF
+};
+
+#define CRC8_INIT 0xFF // Initial FCS value
+#define CRC8_OK 0xCF // Good final FCS value
+/*-----------------------------------------------------------------------------------*/
+uint8_t crc8(uint8_t *data, uint16_t len)
+{
+ uint16_t count;
+ uint8_t crc = CRC8_INIT;
+ for (count = 0; count < len; count++)
+ crc = crc8table[crc ^ data[count]];
+ return crc;
+}
+
+/*-----------------------------------------------------------------------------------*/
+uint8_t crc8_check(uint8_t *data, uint16_t len, uint8_t check_sum)
+{
+ uint8_t crc;
+
+ crc = crc8(data, len);
+
+ crc = crc8table[crc ^ check_sum];
+ if (crc == CRC8_OK)
+ return 0; /* Valid */
+ else
+ return 1; /* Failed */
+
+}
+
+/*-----------------------------------------------------------------------------------*/
+uint8_t crc8_calc(uint8_t *data, uint16_t len)
+{
+ /* Ones complement */
+ return 0xFF - crc8(data, len);
+}
+
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/F401RE-USBHost.lib Mon Jun 09 09:03:25 2014 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/va009039/code/F401RE-USBHost/#b91fdea8c0a7
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHostBTstack/USBHostBTstack.cpp Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,187 @@
+// USBHostBTstack.cpp
+#include "USBHostBTstack.h"
+
+//#define BTSTACK_DEBUG
+
+#ifdef BTSTACK_DEBUG
+#define BT_DBG(x, ...) std::printf("[%s:%d]"x"\r\n", __PRETTY_FUNCTION__, __LINE__, ##__VA_ARGS__);
+#define BT_DBG_BYTES(STR,BUF,LEN) _debug_bytes(__PRETTY_FUNCTION__,__LINE__,STR,BUF,LEN)
+#else
+#define BT_DBG(...) while(0);
+#define BT_DBG_BYTES(S,BUF,LEN) while(0);
+#endif
+#define USB_INFO(...) do{fprintf(stderr,__VA_ARGS__);fprintf(stderr,"\r\n");}while(0);
+
+#define HCI_COMMAND_DATA_PACKET 0x01
+#define HCI_ACL_DATA_PACKET 0x02
+#define HCI_SCO_DATA_PACKET 0x03
+#define HCI_EVENT_PACKET 0x04
+
+USBHostBTstack::USBHostBTstack()
+{
+ host = USBHost::getHostInst();
+ init();
+ m_pCb = NULL;
+}
+
+void USBHostBTstack::init()
+{
+ BT_DBG("");
+ dev = NULL;
+ int_in = NULL;
+ bulk_in = NULL;
+ bulk_out = NULL;
+ btstack_intf = -1;
+ btstack_device_found = false;
+ dev_connected = false;
+ ep_int_in = false;
+ ep_bulk_in = false;
+ ep_bulk_out = false;
+}
+
+bool USBHostBTstack::connected()
+{
+ return dev_connected;
+}
+
+bool USBHostBTstack::connect()
+{
+ if (dev_connected) {
+ return true;
+ }
+
+ for (uint8_t i = 0; i < MAX_DEVICE_CONNECTED; i++) {
+ if ((dev = host->getDevice(i)) != NULL) {
+
+ BT_DBG("Trying to connect BTstack device\r\n");
+
+ if(host->enumerate(dev, this)) {
+ break;
+ }
+ if (btstack_device_found) {
+ int_in = dev->getEndpoint(btstack_intf, INTERRUPT_ENDPOINT, IN);
+ bulk_in = dev->getEndpoint(btstack_intf, BULK_ENDPOINT, IN);
+ bulk_out = dev->getEndpoint(btstack_intf, BULK_ENDPOINT, OUT);
+ if (!int_in || !bulk_in || !bulk_out) {
+ continue;
+ }
+ USB_INFO("New BTstack device: VID:%04x PID:%04x [dev: %p - intf: %d]", dev->getVid(), dev->getPid(), dev, btstack_intf);
+ //dev->setName("BTstack", btstack_intf);
+ //host->registerDriver(dev, btstack_intf, this, &USBHostBTstack::init);
+
+ //int_in->attach(this, &USBHostBTstack::int_rxHandler);
+ //host->interruptRead(dev, int_in, int_report, int_in->getSize(), false);
+
+ //bulk_in->attach(this, &USBHostBTstack::bulk_rxHandler);
+ //host->bulkRead(dev, bulk_in, bulk_report, bulk_in->getSize(), false);
+
+ dev_connected = true;
+ return true;
+ }
+ }
+ }
+ init();
+ return false;
+}
+
+/*virtual*/ void USBHostBTstack::setVidPid(uint16_t vid, uint16_t pid)
+{
+ BT_DBG("vid:%04x,pid:%04x", vid, pid);
+}
+
+/*virtual*/ bool USBHostBTstack::parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol) //Must return true if the interface should be parsed
+{
+ BT_DBG("intf_nb=%d,intf_class=%02X,intf_subclass=%d,intf_protocol=%d", intf_nb, intf_class, intf_subclass, intf_protocol);
+ if ((btstack_intf == -1) && intf_class == 0xe0) {
+ btstack_intf = intf_nb;
+ return true;
+ }
+ return false;
+}
+
+/*virtual*/ bool USBHostBTstack::useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir) //Must return true if the endpoint will be used
+{
+ BT_DBG("intf_nb:%d,type:%d,dir:%d",intf_nb, type, dir);
+ if (intf_nb == btstack_intf) {
+ if (ep_int_in == false && type == INTERRUPT_ENDPOINT && dir == IN) {
+ ep_int_in = true;
+ } else if (ep_bulk_in == false && type == BULK_ENDPOINT && dir == IN) {
+ ep_bulk_in = true;
+ } else if (ep_bulk_out == false && type == BULK_ENDPOINT && dir == OUT) {
+ ep_bulk_out = true;
+ } else {
+ return false;
+ }
+ if (ep_int_in && ep_bulk_in && ep_bulk_out) {
+ btstack_device_found = true;
+ }
+ return true;
+ }
+ return false;
+}
+
+int USBHostBTstack::open()
+{
+ BT_DBG("%p", this);
+ if (!connect()) {
+ error("Bluetooth not found.\n");
+ }
+ return 0;
+}
+
+int USBHostBTstack::send_packet(uint8_t packet_type, uint8_t* packet, int size)
+{
+ USB_TYPE res;
+ switch(packet_type){
+ case HCI_COMMAND_DATA_PACKET:
+ BT_DBG_BYTES("HCI_CMD:", packet, size);
+ res = host->controlWrite(dev,
+ USB_HOST_TO_DEVICE | USB_REQUEST_TYPE_CLASS | USB_RECIPIENT_DEVICE,
+ 0, 0, 0, packet, size);
+ TEST_ASSERT(res == USB_TYPE_OK);
+ break;
+ case HCI_ACL_DATA_PACKET:
+ BT_DBG_BYTES("ACL_SND:", packet, size);
+ res = host->bulkWrite(dev, bulk_out, packet, size);
+ TEST_ASSERT(res == USB_TYPE_OK);
+ break;
+ default:
+ TEST_ASSERT(0);
+ }
+ return 0;
+}
+
+void USBHostBTstack::register_packet_handler(void (*pMethod)(uint8_t, uint8_t*, uint16_t) )
+{
+ BT_DBG("pMethod: %p", pMethod);
+ m_pCb = pMethod;
+}
+
+void USBHostBTstack::poll()
+{
+ int result = host->interruptReadNB(int_in, int_report, sizeof(int_report));
+ if (result >= 0) {
+ int len = int_in->getLengthTransferred();
+ BT_DBG_BYTES("HCI_EVT:", int_report, len);
+ if (m_pCb) {
+ m_pCb(HCI_EVENT_PACKET, int_report, len);
+ }
+ }
+ result = host->bulkReadNB(bulk_in, bulk_report, sizeof(bulk_report));
+ if (result >= 0) {
+ int len = bulk_in->getLengthTransferred();
+ BT_DBG_BYTES("HCI_ACL_RECV:", bulk_report, len);
+ if (m_pCb) {
+ m_pCb(HCI_ACL_DATA_PACKET, bulk_report, len);
+ }
+ }
+}
+
+void _debug_bytes(const char* pretty, int line, const char* s, uint8_t* buf, int len)
+{
+ printf("[%s:%d]\n%s", pretty, line, s);
+ for(int i = 0; i < len; i++) {
+ printf(" %02x", buf[i]);
+ }
+ printf("\n");
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/KL46Z-USBHostBTstack/USBHostBTstack.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,62 @@
+#include "USBHostConf.h"
+#include "USBHost.h"
+#pragma once
+
+#define TEST_ASSERT(A) while(!(A)){fprintf(stderr,"\n\n%s@%d %s ASSERT!\n\n",__PRETTY_FUNCTION__,__LINE__,#A);exit(1);};
+
+/**
+ * A class to communicate a BTstack
+ */
+class USBHostBTstack : public IUSBEnumerator {
+public:
+ /**
+ * Constructor
+ *
+ */
+ USBHostBTstack();
+
+ /**
+ * Check if a BTstack device is connected
+ *
+ * @return true if a BTstack device is connected
+ */
+ bool connected();
+
+ /**
+ * Try to connect to a BTstack device
+ *
+ * @return true if connection was successful
+ */
+ bool connect();
+
+ int open();
+ int send_packet(uint8_t packet_type, uint8_t* packet, int size);
+ void register_packet_handler( void (*pMethod)(uint8_t, uint8_t*, uint16_t));
+ void poll();
+
+protected:
+ //From IUSBEnumerator
+ virtual void setVidPid(uint16_t vid, uint16_t pid);
+ virtual bool parseInterface(uint8_t intf_nb, uint8_t intf_class, uint8_t intf_subclass, uint8_t intf_protocol); //Must return true if the interface should be parsed
+ virtual bool useEndpoint(uint8_t intf_nb, ENDPOINT_TYPE type, ENDPOINT_DIRECTION dir); //Must return true if the endpoint will be used
+
+private:
+ USBHost * host;
+ USBDeviceConnected * dev;
+ bool dev_connected;
+ uint8_t int_report[64];
+ uint8_t bulk_report[64];
+ USBEndpoint * int_in;
+ USBEndpoint * bulk_in;
+ USBEndpoint * bulk_out;
+ bool ep_int_in;
+ bool ep_bulk_in;
+ bool ep_bulk_out;
+
+ bool btstack_device_found;
+ int btstack_intf;
+ void (*m_pCb)(uint8_t, uint8_t*, uint16_t);
+ void init();
+};
+
+void _debug_bytes(const char* pretty, int line, const char* s, uint8_t* buf, int len);
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/main.cpp Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,128 @@
+#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);
+
+#if defined(TARGET_NUCLEO_F401RE)
+DigitalOut led1(LED1);
+int led2 = 0;
+#define LED_ON 0
+#define LED_OFF 1
+#elif defined(TARGET_KL46Z)||defined(TARGET_KL25Z)
+DigitalOut led1(LED1), led2(LED2);
+#define LED_ON 0
+#define LED_OFF 1
+#else
+#error "target error"
+#endif
+
+#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) ? LED_ON : LED_OFF; // left
+ led2 = (report[2] & 0x02) ? LED_ON : LED_OFF; // right
+ //led2 = (report[2] & 0x04) ? LED_ON : LED_OFF; // 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 = LED_OFF;
+ led2 = LED_OFF;
+
+ // 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;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mbed.bld Mon Jun 09 09:03:25 2014 +0000 @@ -0,0 +1,1 @@ +http://mbed.org/users/mbed_official/code/mbed/builds/0b3ab51c8877 \ No newline at end of file
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usbbt/bd_addr.cpp Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,33 @@
+#include "mbed.h"
+#include "bd_addr.h"
+bd_addr::bd_addr()
+{
+
+}
+
+bd_addr::bd_addr(char* s)
+{
+ for(int i = 0; i <= 5; i++) {
+ ad[i] = strtol(s+i*3, NULL, 16);
+ }
+}
+
+uint8_t* bd_addr::data(uint8_t* addr)
+{
+ if (addr != NULL) {
+ ad[5] = addr[0];
+ ad[4] = addr[1];
+ ad[3] = addr[2];
+ ad[2] = addr[3];
+ ad[1] = addr[4];
+ ad[0] = addr[5];
+ }
+ return ad;
+}
+
+char* bd_addr::to_str()
+{
+ snprintf(ad_str, sizeof(ad_str), "%02X:%02X:%02X:%02X:%02X:%02X",
+ ad[0], ad[1], ad[2], ad[3], ad[4], ad[5]);
+ return ad_str;
+}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/usbbt/bd_addr.h Mon Jun 09 09:03:25 2014 +0000
@@ -0,0 +1,13 @@
+#ifndef BD_ADDR_H
+#define BD_ADDR_H
+class bd_addr {
+public:
+ bd_addr();
+ bd_addr(char* s);
+ uint8_t* data(uint8_t* addr = NULL);
+ char* to_str();
+private:
+ char ad_str[18];
+ uint8_t ad[6];
+};
+#endif //BD_ADDR_H