BTstack for Nucleo F401RE/FRDM-KL46Z example program
Dependencies: F401RE-USBHost mbed
The usage is the same as KL46Z-BTstack_example.
使い方はKL46Z-BTstack_exampleと同じです。
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