2019-11-02 18:47:10 +01:00
|
|
|
/*
|
|
|
|
* Copyright (C) 2019 BlueKitchen GmbH
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
*
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. Neither the name of the copyright holders nor the names of
|
|
|
|
* contributors may be used to endorse or promote products derived
|
|
|
|
* from this software without specific prior written permission.
|
|
|
|
* 4. Any redistribution, use, or modification is done solely for
|
|
|
|
* personal benefit and not for any commercial purpose or for
|
|
|
|
* monetary gain.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
|
|
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
|
|
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS
|
|
|
|
* RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
|
|
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
|
|
|
|
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
|
|
|
|
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
|
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
|
|
* THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*
|
|
|
|
* Please inquire about commercial licensing options at
|
|
|
|
* contact@bluekitchen-gmbh.com
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define BTSTACK_FILE__ "btpclient.c"
|
|
|
|
|
|
|
|
/*
|
|
|
|
* btpclient.c
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <inttypes.h>
|
2019-11-06 17:31:45 +01:00
|
|
|
|
|
|
|
// TODO: only include on posix platform
|
|
|
|
#include <unistd.h>
|
|
|
|
|
2019-11-02 18:47:10 +01:00
|
|
|
#include "btstack.h"
|
2019-11-03 15:47:09 +01:00
|
|
|
#include "btp.h"
|
2019-11-02 22:05:50 +01:00
|
|
|
#include "btp_socket.h"
|
|
|
|
|
|
|
|
#define AUTOPTS_SOCKET_NAME "/tmp/bt-stack-tester"
|
2019-11-02 18:47:10 +01:00
|
|
|
|
2019-11-07 12:05:36 +01:00
|
|
|
#define BT_LE_AD_LIMITED (1U << 0)
|
|
|
|
#define BT_LE_AD_GENERAL (1U << 1)
|
|
|
|
#define BT_LE_AD_NO_BREDR (1U << 2)
|
2019-11-02 18:47:10 +01:00
|
|
|
|
2019-11-07 12:05:36 +01:00
|
|
|
int btstack_main(int argc, const char * argv[]);
|
2019-11-02 22:05:50 +01:00
|
|
|
|
2019-11-02 18:47:10 +01:00
|
|
|
static btstack_packet_callback_registration_t hci_event_callback_registration;
|
2019-11-04 16:28:55 +01:00
|
|
|
static bool gap_send_powered_state;
|
|
|
|
static char gap_name[249];
|
|
|
|
static char gap_short_name[11];
|
|
|
|
static uint32_t gap_cod;
|
2019-11-07 11:33:15 +01:00
|
|
|
|
|
|
|
// gap_adv_data_len/gap_scan_response is 16-bit to simplify bounds calculation
|
2019-11-07 12:05:36 +01:00
|
|
|
static uint8_t ad_flags;
|
2019-11-07 11:33:15 +01:00
|
|
|
static uint8_t gap_adv_data[31];
|
|
|
|
static uint16_t gap_adv_data_len;
|
|
|
|
static uint8_t gap_scan_response[31];
|
|
|
|
static uint16_t gap_scan_response_len;
|
2019-11-06 17:31:45 +01:00
|
|
|
static uint8_t gap_discovery_active;
|
2019-11-04 16:28:55 +01:00
|
|
|
|
|
|
|
static uint32_t current_settings;
|
|
|
|
|
2019-11-06 17:31:45 +01:00
|
|
|
// log/debug output
|
|
|
|
#define MESSAGE(format, ...) log_info(format, ## __VA_ARGS__); printf(format "\n", ## __VA_ARGS__)
|
|
|
|
static void MESSAGE_HEXDUMP(const uint8_t * data, uint16_t len){
|
|
|
|
log_info_hexdump(data, len);
|
|
|
|
printf_hexdump(data, len);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void btp_send(uint8_t service_id, uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t *data){
|
|
|
|
if (opcode >= 0x80){
|
|
|
|
MESSAGE("Event: service id 0x%02x, opcode 0x%02x, controller_index 0x%0x, len %u", service_id, opcode, controller_index, length);
|
|
|
|
if (length > 0) {
|
|
|
|
MESSAGE_HEXDUMP(data, length);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
MESSAGE("Response: service id 0x%02x, opcode 0x%02x, controller_index 0x%0x, len %u", service_id, opcode, controller_index, length);
|
|
|
|
if (length > 0){
|
|
|
|
MESSAGE_HEXDUMP(data, length);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
btp_socket_send_packet(service_id, opcode, controller_index, length, data);
|
|
|
|
}
|
2019-11-04 16:28:55 +01:00
|
|
|
|
|
|
|
static void btp_send_gap_settings(uint8_t opcode){
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_SETTINGS opcode %02x: %08x", opcode, current_settings);
|
2019-11-04 16:28:55 +01:00
|
|
|
uint8_t buffer[4];
|
|
|
|
little_endian_store_32(buffer, 0, current_settings);
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_send(BTP_SERVICE_ID_GAP, opcode, 0, 4, buffer);
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
2019-11-02 18:47:10 +01:00
|
|
|
|
2019-11-07 12:05:36 +01:00
|
|
|
static void reset_gap(void){
|
|
|
|
// current settings
|
|
|
|
current_settings |= BTP_GAP_SETTING_SSP;
|
|
|
|
current_settings |= BTP_GAP_SETTING_LE;
|
|
|
|
current_settings |= BTP_GAP_SETTING_PRIVACY;
|
|
|
|
#ifdef ENABLE_CLASSIC
|
|
|
|
current_settings |= BTP_GAP_SETTING_BREDR;
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_BLE
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_LE_SECURE_CONNECTIONS
|
|
|
|
current_settings |= BTP_GAP_SETTING_SC;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// TODO: check ENABLE_CLASSIC / Controller features
|
|
|
|
ad_flags = BT_LE_AD_NO_BREDR;
|
|
|
|
}
|
|
|
|
|
2019-11-02 22:05:50 +01:00
|
|
|
static void btstack_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){
|
2019-11-02 18:47:10 +01:00
|
|
|
UNUSED(channel);
|
2019-11-06 17:31:45 +01:00
|
|
|
switch (packet_type) {
|
|
|
|
case HCI_EVENT_PACKET:
|
|
|
|
switch (hci_event_packet_get_type(packet)) {
|
2019-11-02 22:05:50 +01:00
|
|
|
case BTSTACK_EVENT_STATE:
|
2019-11-03 15:47:09 +01:00
|
|
|
switch (btstack_event_state_get_state(packet)){
|
|
|
|
case HCI_STATE_WORKING:
|
2019-11-04 16:28:55 +01:00
|
|
|
if (gap_send_powered_state){
|
|
|
|
gap_send_powered_state = false;
|
|
|
|
current_settings |= BTP_GAP_SETTING_POWERED;
|
|
|
|
btp_send_gap_settings(BTP_GAP_OP_SET_POWERED);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case HCI_STATE_OFF:
|
|
|
|
if (gap_send_powered_state){
|
|
|
|
gap_send_powered_state = false;
|
2019-11-06 11:50:55 +01:00
|
|
|
// update settings
|
|
|
|
current_settings &= ~BTP_GAP_SETTING_ADVERTISING;
|
2019-11-04 16:28:55 +01:00
|
|
|
current_settings &= ~BTP_GAP_SETTING_POWERED;
|
|
|
|
btp_send_gap_settings(BTP_GAP_OP_SET_POWERED);
|
|
|
|
}
|
2019-11-03 15:47:09 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
2019-11-06 17:31:45 +01:00
|
|
|
case GAP_EVENT_ADVERTISING_REPORT:{
|
|
|
|
bd_addr_t address;
|
|
|
|
gap_event_advertising_report_get_address(packet, address);
|
|
|
|
uint8_t event_type = gap_event_advertising_report_get_advertising_event_type(packet);
|
|
|
|
uint8_t address_type = gap_event_advertising_report_get_address_type(packet);
|
|
|
|
int8_t rssi = gap_event_advertising_report_get_rssi(packet);
|
|
|
|
uint8_t length = gap_event_advertising_report_get_data_length(packet);
|
|
|
|
const uint8_t * data = gap_event_advertising_report_get_data(packet);
|
|
|
|
printf("Advertisement event: evt-type %u, addr-type %u, addr %s, rssi %d, data[%u] ", event_type,
|
|
|
|
address_type, bd_addr_to_str(address), rssi, length);
|
|
|
|
printf_hexdump(data, length);
|
|
|
|
// max 255 bytes EIR data
|
|
|
|
uint8_t buffer[11 + 255];
|
|
|
|
buffer[0] = address_type;
|
|
|
|
reverse_bd_addr(address, &buffer[1]);
|
|
|
|
buffer[7] = rssi;
|
|
|
|
buffer[8] = BTP_GAP_EV_DEVICE_FOUND_FLAG_RSSI | BTP_GAP_EV_DEVICE_FOUND_FLAG_AD | BTP_GAP_EV_DEVICE_FOUND_FLAG_SR;
|
|
|
|
little_endian_store_16(buffer, 9, 0);
|
|
|
|
// TODO: deliver AD/SD if needed
|
|
|
|
btp_send(BTP_SERVICE_ID_GAP, BTP_GAP_EV_DEVICE_FOUND, 0, 11, &buffer[0]);
|
|
|
|
break;
|
|
|
|
}
|
2019-11-02 18:47:10 +01:00
|
|
|
default:
|
|
|
|
break;
|
2019-11-06 17:31:45 +01:00
|
|
|
}
|
2019-11-02 18:47:10 +01:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
2019-11-06 17:31:45 +01:00
|
|
|
}
|
2019-11-02 18:47:10 +01:00
|
|
|
}
|
|
|
|
|
2019-11-04 16:28:55 +01:00
|
|
|
static void btp_core_handler(uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t *data){
|
2019-11-02 22:05:50 +01:00
|
|
|
uint8_t status;
|
2019-11-04 16:28:55 +01:00
|
|
|
switch (opcode){
|
|
|
|
case BTP_OP_ERROR:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_OP_ERROR");
|
2019-11-04 16:28:55 +01:00
|
|
|
status = data[0];
|
|
|
|
if (status == BTP_ERROR_NOT_READY){
|
|
|
|
// connection stopped, abort
|
|
|
|
exit(10);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_CORE_OP_READ_SUPPORTED_COMMANDS:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_CORE_OP_READ_SUPPORTED_COMMANDS");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == BTP_INDEX_NON_CONTROLLER){
|
|
|
|
uint8_t commands = 0;
|
|
|
|
commands |= (1U << BTP_CORE_OP_READ_SUPPORTED_COMMANDS);
|
|
|
|
commands |= (1U << BTP_CORE_OP_READ_SUPPORTED_SERVICES);
|
|
|
|
commands |= (1U << BTP_CORE_OP_REGISTER);
|
|
|
|
commands |= (1U << BTP_CORE_OP_UNREGISTER);
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_send(BTP_SERVICE_ID_CORE, opcode, controller_index, 1, &commands);
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_CORE_OP_READ_SUPPORTED_SERVICES:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_CORE_OP_READ_SUPPORTED_SERVICES");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == BTP_INDEX_NON_CONTROLLER){
|
|
|
|
uint8_t services = 0;
|
|
|
|
services |= (1U << BTP_SERVICE_ID_CORE);
|
|
|
|
services |= (1U << BTP_SERVICE_ID_GAP);
|
|
|
|
services |= (1U << BTP_SERVICE_ID_GATT );
|
|
|
|
services |= (1U << BTP_SERVICE_ID_L2CAP);
|
|
|
|
services |= (1U << BTP_SERVICE_ID_MESH );
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_send(BTP_SERVICE_ID_CORE, opcode, controller_index, 1, &services);
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_CORE_OP_REGISTER:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_CORE_OP_REGISTER");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == BTP_INDEX_NON_CONTROLLER){
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_send(BTP_SERVICE_ID_CORE, opcode, controller_index, 0, NULL);
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_CORE_OP_UNREGISTER:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_CORE_OP_UNREGISTER");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == BTP_INDEX_NON_CONTROLLER){
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_send(BTP_SERVICE_ID_CORE, opcode, controller_index, 0, NULL);
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void btp_gap_handler(uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t *data){
|
|
|
|
switch (opcode){
|
|
|
|
case BTP_OP_ERROR:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_OP_ERROR");
|
2019-11-04 16:28:55 +01:00
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_READ_SUPPORTED_COMMANDS:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_OP_READ_SUPPORTED_COMMANDS");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == BTP_INDEX_NON_CONTROLLER){
|
|
|
|
uint8_t commands = 0;
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_send(BTP_SERVICE_ID_GAP, opcode, controller_index, 1, &commands);
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_READ_CONTROLLER_INDEX_LIST:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_OP_READ_CONTROLLER_INDEX_LIST - not implemented");
|
2019-11-04 16:28:55 +01:00
|
|
|
break;
|
2019-11-05 17:51:21 +01:00
|
|
|
case BTP_GAP_OP_READ_CONTROLLER_INFO:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_OP_READ_CONTROLLER_INFO");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t buffer[277];
|
|
|
|
bd_addr_t local_addr;
|
|
|
|
gap_local_bd_addr( local_addr);
|
|
|
|
reverse_bd_addr(local_addr, &buffer[0]);
|
|
|
|
uint32_t supported_settings = 0;
|
|
|
|
supported_settings |= BTP_GAP_SETTING_POWERED;
|
|
|
|
supported_settings |= BTP_GAP_SETTING_CONNECTABLE;
|
|
|
|
supported_settings |= BTP_GAP_SETTING_DISCOVERABLE;
|
|
|
|
supported_settings |= BTP_GAP_SETTING_BONDABLE;
|
|
|
|
supported_settings |= BTP_GAP_SETTING_SSP;
|
|
|
|
#ifdef ENABLE_CLASSIC
|
|
|
|
supported_settings |= BTP_GAP_SETTING_BREDR;
|
|
|
|
#endif
|
|
|
|
#ifdef ENABLE_BLE
|
|
|
|
supported_settings |= BTP_GAP_SETTING_LE;
|
|
|
|
#endif
|
|
|
|
supported_settings |= BTP_GAP_SETTING_ADVERTISING;
|
|
|
|
#ifdef ENABLE_LE_SECURE_CONNECTIONS
|
|
|
|
supported_settings |= BTP_GAP_SETTING_SC;
|
|
|
|
#endif
|
|
|
|
supported_settings |= BTP_GAP_SETTING_PRIVACY;
|
|
|
|
// supported_settings |= BTP_GAP_SETTING_STATIC_ADDRESS;
|
|
|
|
little_endian_store_32(buffer, 6, supported_settings);
|
|
|
|
little_endian_store_32(buffer,10, current_settings);
|
|
|
|
little_endian_store_24(buffer, 14, gap_cod);
|
|
|
|
strncpy((char *) &buffer[17], &gap_name[0], 249);
|
|
|
|
strncpy((char *) &buffer[266], &gap_short_name[0], 11 );
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_send(BTP_SERVICE_ID_GAP, opcode, controller_index, 277, buffer);
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_RESET:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_OP_RESET - NOP");
|
2019-11-04 16:28:55 +01:00
|
|
|
// ignore
|
|
|
|
btp_send_gap_settings(opcode);
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_SET_POWERED:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_OP_SET_POWERED");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == 0){
|
2019-11-06 11:50:55 +01:00
|
|
|
gap_send_powered_state = true;
|
2019-11-04 16:28:55 +01:00
|
|
|
uint8_t powered = data[0];
|
|
|
|
if (powered){
|
|
|
|
hci_power_control(HCI_POWER_ON);
|
|
|
|
} else {
|
|
|
|
hci_power_control(HCI_POWER_OFF);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_SET_CONNECTABLE:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_OP_SET_CONNECTABLE - NOP");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t connectable = data[0];
|
|
|
|
if (connectable) {
|
|
|
|
current_settings |= BTP_GAP_SETTING_CONNECTABLE;
|
|
|
|
} else {
|
|
|
|
current_settings &= ~BTP_GAP_SETTING_CONNECTABLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
btp_send_gap_settings(opcode);
|
|
|
|
}
|
|
|
|
break;
|
2019-11-06 17:31:45 +01:00
|
|
|
case BTP_GAP_OP_SET_FAST_CONNECTABLE:
|
|
|
|
MESSAGE("BTP_GAP_OP_SET_FAST_CONNECTABLE - NOP");
|
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t connectable = data[0];
|
|
|
|
if (connectable) {
|
|
|
|
current_settings |= BTP_GAP_SETTING_FAST_CONNECTABLE;
|
|
|
|
} else {
|
|
|
|
current_settings &= ~BTP_GAP_SETTING_FAST_CONNECTABLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
btp_send_gap_settings(opcode);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_SET_DISCOVERABLE:
|
|
|
|
MESSAGE("BTP_GAP_OP_SET_DISCOVERABLE");
|
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t discoverable = data[0];
|
2019-11-07 12:05:36 +01:00
|
|
|
// Classic
|
|
|
|
gap_discoverable_control(discoverable > 0);
|
|
|
|
|
|
|
|
switch (discoverable) {
|
|
|
|
case BTP_GAP_DISCOVERABLE_NON:
|
|
|
|
ad_flags &= ~(BT_LE_AD_GENERAL | BT_LE_AD_LIMITED);
|
|
|
|
current_settings &= ~BTP_GAP_SETTING_DISCOVERABLE;
|
|
|
|
break;
|
|
|
|
case BTP_GAP_DISCOVERABLE_GENERAL:
|
|
|
|
ad_flags &= ~BT_LE_AD_LIMITED;
|
|
|
|
ad_flags |= BT_LE_AD_GENERAL;
|
|
|
|
current_settings |= BTP_GAP_SETTING_DISCOVERABLE;
|
|
|
|
break;
|
|
|
|
case BTP_GAP_DISCOVERABLE_LIMITED:
|
|
|
|
ad_flags &= ~BT_LE_AD_GENERAL;
|
|
|
|
ad_flags |= BT_LE_AD_LIMITED;
|
|
|
|
current_settings |= BTP_GAP_SETTING_DISCOVERABLE;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return;
|
2019-11-06 17:31:45 +01:00
|
|
|
}
|
|
|
|
btp_send_gap_settings(opcode);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_SET_BONDABLE:
|
|
|
|
MESSAGE("BTP_GAP_OP_SET_BONDABLE - NOP");
|
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t bondable = data[0];
|
|
|
|
// TODO:
|
|
|
|
if (bondable) {
|
|
|
|
current_settings |= BTP_GAP_SETTING_BONDABLE;
|
|
|
|
} else {
|
|
|
|
current_settings &= ~BTP_GAP_SETTING_BONDABLE;
|
|
|
|
|
|
|
|
}
|
|
|
|
btp_send_gap_settings(opcode);
|
|
|
|
}
|
|
|
|
break;
|
2019-11-04 16:28:55 +01:00
|
|
|
case BTP_GAP_OP_START_ADVERTISING:
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("BTP_GAP_OP_START_ADVERTISING");
|
2019-11-04 16:28:55 +01:00
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t adv_data_len = data[0];
|
|
|
|
uint8_t scan_response_len = data[1];
|
|
|
|
const uint8_t * adv_data = &data[2];
|
|
|
|
const uint8_t * scan_response = &data[2 + adv_data_len];
|
|
|
|
// uint32_t duration = little_endian_read_32(data, 2 + adv_data_len + scan_response_len);
|
|
|
|
bool use_own_id_address = (bool) &data[6 + adv_data_len + scan_response_len];
|
2019-11-06 11:50:55 +01:00
|
|
|
|
2019-11-07 11:33:15 +01:00
|
|
|
// prefix adv_data with flags and append rest
|
|
|
|
gap_adv_data_len = 0;
|
|
|
|
gap_adv_data[gap_adv_data_len++] = 0x02;
|
|
|
|
gap_adv_data[gap_adv_data_len++] = 0x01;
|
2019-11-07 12:05:36 +01:00
|
|
|
gap_adv_data[gap_adv_data_len++] = ad_flags;
|
2019-11-07 11:33:15 +01:00
|
|
|
|
|
|
|
uint8_t ad_pos = 0;
|
|
|
|
while ((ad_pos + 2) < adv_data_len){
|
|
|
|
uint8_t ad_type = adv_data[ad_pos++];
|
|
|
|
uint8_t ad_len = adv_data[ad_pos++];
|
|
|
|
if ((ad_type != BLUETOOTH_DATA_TYPE_FLAGS) && ((ad_pos + ad_len) < adv_data_len) && (gap_adv_data_len + 2 + ad_len < 31)) {
|
|
|
|
gap_adv_data[gap_adv_data_len++] = ad_len + 1;
|
|
|
|
gap_adv_data[gap_adv_data_len++] = ad_type;
|
|
|
|
memcpy(&gap_adv_data[gap_adv_data_len], &adv_data[ad_pos], ad_len);
|
|
|
|
gap_adv_data_len += ad_len;
|
|
|
|
}
|
|
|
|
ad_pos += ad_len;
|
|
|
|
}
|
|
|
|
|
|
|
|
// process scan data
|
|
|
|
uint8_t scan_pos = 0;
|
|
|
|
gap_scan_response_len = 0;
|
|
|
|
while ((scan_pos + 2) < scan_response_len){
|
|
|
|
uint8_t ad_type = scan_response[scan_pos++];
|
|
|
|
uint8_t ad_len = scan_response[scan_pos++];
|
|
|
|
if (((scan_pos + ad_len) < scan_response_len) && (gap_scan_response_len + 2 + ad_len < 31)) {
|
|
|
|
gap_scan_response[gap_scan_response_len++] = ad_len + 1;
|
|
|
|
gap_scan_response[gap_scan_response_len++] = ad_type;
|
|
|
|
memcpy(&gap_scan_response[gap_scan_response_len], &scan_response[scan_pos], ad_len);
|
|
|
|
gap_scan_response_len += ad_len;
|
|
|
|
}
|
|
|
|
scan_pos += ad_len;
|
|
|
|
}
|
2019-11-06 11:50:55 +01:00
|
|
|
|
2019-11-04 16:28:55 +01:00
|
|
|
// configure controller
|
|
|
|
if (use_own_id_address){
|
|
|
|
gap_random_address_set_mode(GAP_RANDOM_ADDRESS_TYPE_OFF);
|
|
|
|
} else {
|
|
|
|
gap_random_address_set_mode(GAP_RANDOM_ADDRESS_RESOLVABLE);
|
|
|
|
}
|
|
|
|
uint16_t adv_int_min = 0x0030;
|
|
|
|
uint16_t adv_int_max = 0x0030;
|
|
|
|
uint8_t adv_type;
|
|
|
|
bd_addr_t null_addr;
|
|
|
|
memset(null_addr, 0, 6);
|
|
|
|
if (current_settings & BTP_GAP_SETTING_CONNECTABLE){
|
|
|
|
adv_type = 0; // ADV_IND
|
|
|
|
} else {
|
2019-11-06 11:50:55 +01:00
|
|
|
// min advertising interval 100 ms for non-connectable advertisements (pre 5.0 controllers)
|
2019-11-04 16:28:55 +01:00
|
|
|
adv_type = 3; // ADV_NONCONN_IND
|
2019-11-06 11:50:55 +01:00
|
|
|
adv_int_min = 0xa0;
|
|
|
|
adv_int_max = 0xa0;
|
2019-11-04 16:28:55 +01:00
|
|
|
}
|
|
|
|
gap_advertisements_set_params(adv_int_min, adv_int_max, adv_type, 0, null_addr, 0x07, 0x00);
|
2019-11-06 11:50:55 +01:00
|
|
|
gap_advertisements_set_data(gap_adv_data_len, (uint8_t *) gap_adv_data);
|
2019-11-04 16:28:55 +01:00
|
|
|
gap_scan_response_set_data(scan_response_len, (uint8_t *) scan_response);
|
|
|
|
gap_advertisements_enable(1);
|
|
|
|
// update settings
|
|
|
|
current_settings |= BTP_GAP_SETTING_ADVERTISING;
|
|
|
|
btp_send_gap_settings(opcode);
|
|
|
|
}
|
|
|
|
break;
|
2019-11-06 17:31:45 +01:00
|
|
|
case BTP_GAP_OP_STOP_ADVERTISING:
|
|
|
|
MESSAGE("BTP_GAP_OP_STOP_ADVERTISING");
|
|
|
|
if (controller_index == 0){
|
|
|
|
gap_advertisements_enable(0);
|
|
|
|
// update settings
|
|
|
|
current_settings &= ~BTP_GAP_SETTING_ADVERTISING;
|
|
|
|
btp_send_gap_settings(opcode);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_START_DISCOVERY:
|
|
|
|
MESSAGE("BTP_GAP_OP_START_DISCOVERY");
|
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t flags = data[0];
|
|
|
|
if ((flags & BTP_GAP_DISCOVERY_FLAG_LE) != 0){
|
|
|
|
uint8_t scan_type;
|
|
|
|
if ((flags & BTP_GAP_DISCOVERY_FLAG_ACTIVE) != 0){
|
|
|
|
scan_type = 1;
|
|
|
|
} else {
|
|
|
|
scan_type = 0;
|
|
|
|
}
|
|
|
|
gap_set_scan_parameters(scan_type, 0x30, 0x30);
|
|
|
|
gap_start_scan();
|
|
|
|
}
|
|
|
|
if (flags & BTP_GAP_DISCOVERY_FLAG_BREDR){
|
|
|
|
gap_discovery_active = 1;
|
|
|
|
// TODO: start discovery
|
|
|
|
}
|
|
|
|
btp_send(BTP_SERVICE_ID_GAP, opcode, controller_index, 0, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_STOP_DISCOVERY:
|
|
|
|
MESSAGE("BTP_GAP_OP_STOP_DISCOVERY");
|
|
|
|
if (controller_index == 0){
|
|
|
|
gap_stop_scan();
|
|
|
|
if (gap_discovery_active){
|
|
|
|
gap_discovery_active = 0;
|
|
|
|
// TODO: stop discovery
|
|
|
|
}
|
|
|
|
btp_send(BTP_SERVICE_ID_GAP, opcode, controller_index, 0, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_CONNECT:
|
|
|
|
MESSAGE("BTP_GAP_OP_CONNECT - not implemented");
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_DISCONNECT:
|
|
|
|
MESSAGE("BTP_GAP_OP_DISCONNECT - not implemented");
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_SET_IO_CAPA:
|
|
|
|
MESSAGE("BTP_GAP_OP_SET_IO_CAPA - not implemented");
|
|
|
|
if (controller_index == 0){
|
|
|
|
uint8_t io_capabilities = data[0];
|
|
|
|
gap_ssp_set_io_capability(io_capabilities);
|
|
|
|
sm_set_io_capabilities( (io_capability_t) io_capabilities);
|
|
|
|
btp_send(BTP_SERVICE_ID_GAP, opcode, controller_index, 0, NULL);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_PAIR:
|
|
|
|
MESSAGE("BTP_GAP_OP_PAIR - not implemented");
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_UNPAIR:
|
|
|
|
MESSAGE("BTP_GAP_OP_UNPAIR - not implemented");
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_PASSKEY_ENTRY_RSP:
|
|
|
|
MESSAGE("BTP_GAP_OP_PASSKEY_ENTRY_RSP - not implemented");
|
|
|
|
break;
|
|
|
|
case BTP_GAP_OP_PASSKEY_CONFIRM_RSP:
|
|
|
|
MESSAGE("BTP_GAP_OP_PASSKEY_CONFIRM_RSP - not implemented");
|
|
|
|
break;
|
|
|
|
|
2019-11-04 16:28:55 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void btp_packet_handler(uint8_t service_id, uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t *data){
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("Command: service id 0x%02x, opcode 0x%02x, controller_index 0x%0x, len %u", service_id, opcode, controller_index, length);
|
|
|
|
if (length > 0){
|
|
|
|
MESSAGE_HEXDUMP(data, length);
|
|
|
|
}
|
2019-11-03 15:47:09 +01:00
|
|
|
|
2019-11-02 22:05:50 +01:00
|
|
|
switch (service_id){
|
2019-11-03 15:47:09 +01:00
|
|
|
case BTP_SERVICE_ID_CORE:
|
2019-11-04 16:28:55 +01:00
|
|
|
btp_core_handler(opcode, controller_index, length, data);
|
2019-11-02 22:05:50 +01:00
|
|
|
break;
|
2019-11-04 16:28:55 +01:00
|
|
|
case BTP_SERVICE_ID_GAP:
|
|
|
|
btp_gap_handler(opcode, controller_index, length, data);
|
2019-11-02 22:05:50 +01:00
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-06 17:31:45 +01:00
|
|
|
enum console_state {
|
|
|
|
CONSOLE_STATE_MAIN = 0,
|
|
|
|
CONSOLE_STATE_GAP,
|
|
|
|
} console_state;
|
|
|
|
|
|
|
|
static void usage(void){
|
|
|
|
switch (console_state){
|
|
|
|
case CONSOLE_STATE_MAIN:
|
|
|
|
printf("BTstack BTP Client for auto-pts framework: MAIN console interface\n");
|
|
|
|
printf("g - GAP Command\n");
|
|
|
|
break;
|
|
|
|
case CONSOLE_STATE_GAP:
|
|
|
|
printf("BTstack BTP Client for auto-pts framework: GAP console interface\n");
|
|
|
|
printf("s - Start active scanning\n");
|
|
|
|
printf("S - Stop discovery and scanning\n");
|
|
|
|
printf("p - Power On\n");
|
|
|
|
printf("P - Power Off\n");
|
|
|
|
printf("x - Back to main\n");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
static void stdin_process(char cmd){
|
|
|
|
const uint8_t active_le_scan = BTP_GAP_DISCOVERY_FLAG_LE | BTP_GAP_DISCOVERY_FLAG_ACTIVE;
|
|
|
|
const uint8_t value_on = 1;
|
|
|
|
const uint8_t value_off = 0;
|
|
|
|
switch (console_state){
|
|
|
|
case CONSOLE_STATE_MAIN:
|
|
|
|
switch (cmd){
|
|
|
|
case 'g':
|
|
|
|
console_state = CONSOLE_STATE_GAP;
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case CONSOLE_STATE_GAP:
|
|
|
|
switch (cmd){
|
|
|
|
case 'p':
|
|
|
|
btp_packet_handler(BTP_SERVICE_ID_GAP, BTP_GAP_OP_SET_POWERED, 0, 1, &value_on);
|
|
|
|
break;
|
|
|
|
case 'P':
|
|
|
|
btp_packet_handler(BTP_SERVICE_ID_GAP, BTP_GAP_OP_SET_POWERED, 0, 1, &value_off);
|
|
|
|
break;
|
|
|
|
case 's':
|
|
|
|
btp_packet_handler(BTP_SERVICE_ID_GAP, BTP_GAP_OP_START_DISCOVERY, 0, 1, &active_le_scan);
|
|
|
|
break;
|
|
|
|
case 'S':
|
|
|
|
btp_packet_handler(BTP_SERVICE_ID_GAP, BTP_GAP_OP_STOP_DISCOVERY, 0, 0, NULL);
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
console_state = CONSOLE_STATE_MAIN;
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
usage();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-11-02 18:47:10 +01:00
|
|
|
int btstack_main(int argc, const char * argv[])
|
|
|
|
{
|
|
|
|
(void)argc;
|
|
|
|
(void)argv;
|
|
|
|
|
|
|
|
l2cap_init();
|
|
|
|
rfcomm_init();
|
|
|
|
sdp_init();
|
|
|
|
|
2019-11-04 16:28:55 +01:00
|
|
|
le_device_db_init();
|
|
|
|
sm_init();
|
|
|
|
|
2019-11-06 18:48:47 +01:00
|
|
|
// use fixed IRK
|
|
|
|
uint8_t test_irk[16] = { 0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF,0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF };
|
|
|
|
sm_test_set_irk(test_irk);
|
|
|
|
printf("TEST IRK: ");
|
|
|
|
printf_hexdump(test_irk, 16);
|
|
|
|
|
2019-11-02 18:47:10 +01:00
|
|
|
// register for HCI events
|
2019-11-02 22:05:50 +01:00
|
|
|
hci_event_callback_registration.callback = &btstack_packet_handler;
|
2019-11-02 18:47:10 +01:00
|
|
|
hci_add_event_handler(&hci_event_callback_registration);
|
|
|
|
|
2019-11-04 16:28:55 +01:00
|
|
|
// configure GAP
|
2019-11-02 18:47:10 +01:00
|
|
|
gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO);
|
2019-11-04 16:28:55 +01:00
|
|
|
|
|
|
|
strcpy(gap_name, "iut 00:00:00:00:00:00");
|
|
|
|
gap_set_local_name(gap_name);
|
|
|
|
|
|
|
|
strcpy(gap_short_name, "iut");
|
|
|
|
|
|
|
|
gap_cod = 0x007a020c; // smartphone
|
|
|
|
gap_set_class_of_device(gap_cod);
|
2019-11-02 18:47:10 +01:00
|
|
|
|
2019-11-07 12:05:36 +01:00
|
|
|
reset_gap();
|
2019-11-04 16:28:55 +01:00
|
|
|
|
2019-11-06 17:31:45 +01:00
|
|
|
MESSAGE("auto-pts iut-btp-client started");
|
|
|
|
|
2019-11-04 16:28:55 +01:00
|
|
|
// connect to auto-pts client
|
|
|
|
btp_socket_open_unix(AUTOPTS_SOCKET_NAME);
|
2019-11-06 17:31:45 +01:00
|
|
|
btp_socket_register_packet_handler(&btp_packet_handler);
|
|
|
|
|
|
|
|
MESSAGE("BTP_CORE_SERVICE/BTP_EV_CORE_READY/BTP_INDEX_NON_CONTROLLER()");
|
|
|
|
btp_send(BTP_SERVICE_ID_CORE, BTP_CORE_EV_READY, BTP_INDEX_NON_CONTROLLER, 0, NULL);
|
|
|
|
|
|
|
|
#ifdef HAVE_POSIX_FILE_IO
|
|
|
|
#ifdef HAVE_BTSTACK_STDIN
|
|
|
|
if (isatty(fileno(stdin))){
|
|
|
|
btstack_stdin_setup(stdin_process);
|
|
|
|
usage();
|
|
|
|
} else {
|
|
|
|
MESSAGE("Terminal not interactive");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
2019-11-02 18:47:10 +01:00
|
|
|
return 0;
|
|
|
|
}
|