diff --git a/test/auto-pts/.gitignore b/test/auto-pts/.gitignore new file mode 100644 index 000000000..946ab0d67 --- /dev/null +++ b/test/auto-pts/.gitignore @@ -0,0 +1,2 @@ +build +xcode diff --git a/test/auto-pts/CMakeLists.txt b/test/auto-pts/CMakeLists.txt index ebcceba3c..c0b6ad693 100644 --- a/test/auto-pts/CMakeLists.txt +++ b/test/auto-pts/CMakeLists.txt @@ -58,17 +58,15 @@ set(SOURCES ${SOURCES_HXCMOD} ${SOURCES_ZEPHYR} ) -list(SORT SOURCES) # create static lib add_library(btstack STATIC ${SOURCES}) -# create targets for all examples -file(GLOB EXAMPLE_FILE btpclient.c) +# target +set (EXAMPLE btpclient) # create targets -get_filename_component(EXAMPLE ${EXAMPLE_FILE} NAME_WE) -set (SOURCE_FILES ${EXAMPLE_FILE}) +set (SOURCE_FILES ${EXAMPLE}.c btp_socket.c) add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.h @@ -76,5 +74,6 @@ add_custom_command( ARGS ${CMAKE_SOURCE_DIR}/${EXAMPLE}.gatt ${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.h ) list(APPEND SOURCE_FILES ${CMAKE_CURRENT_BINARY_DIR}/${EXAMPLE}.h) + add_executable(${EXAMPLE} ${SOURCE_FILES} ) target_link_libraries(${EXAMPLE} btstack) diff --git a/test/auto-pts/btp_socket.c b/test/auto-pts/btp_socket.c new file mode 100644 index 000000000..857223f16 --- /dev/null +++ b/test/auto-pts/btp_socket.c @@ -0,0 +1,228 @@ +/* + * 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__ "btp_connection.c" + +/* + * btp_connection.c + * + * Handles btp communication with auto-pts client via unix domain socket + * + */ + +#include "btp_socket.h" + +#include "hci.h" +#include "btstack_debug.h" +#include "btstack_config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define BTP_PAYLOAD_LEN_MAX 255 +#define BTP_HEADER_LEN 5 + +/** prototypes */ +static void btp_socket_hci_process(btstack_data_source_t *ds, btstack_data_source_callback_type_t callback_type); + +/** globals */ + +typedef enum { + SOCKET_W4_HEADER, + SOCKET_W4_DATA +} SOCKET_STATE; + +static btstack_data_source_t socket_ds; +static uint8_t buffer[6+BTP_PAYLOAD_LEN_MAX]; +static SOCKET_STATE state; +static uint16_t bytes_read; +static uint16_t bytes_to_read; + +/** client packet handler */ + +static void (*btp_socket_packet_callback)(uint8_t service_id, uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t * data); + +static void btp_socket_free_connection(){ + // remove from run_loop + btstack_run_loop_remove_data_source(&socket_ds); + socket_ds.source.fd = 0; +} + +static void btp_socket_init_statemachine(void){ + // wait for next packet + state = SOCKET_W4_HEADER; + bytes_read = 0; + bytes_to_read = BTP_HEADER_LEN; +} + +static void btp_socket_emit_connection_closed(void){ + const uint8_t status = BTP_ERROR_NOT_READY; + (*btp_socket_packet_callback)(BTP_CORE_SERVICE, BTP_OP_ERROR, BTP_INDEX_NON_CONTROLLER, 1, &status); +} + +void btp_socket_hci_process(btstack_data_source_t *socket_ds, btstack_data_source_callback_type_t callback_type) { + // get socket_fd + int socket_fd = socket_ds->source.fd; + + // read from socket + int bytes_read = read(socket_fd, &buffer[bytes_read], bytes_to_read); + + if (bytes_read <= 0){ + // free connection + btp_socket_free_connection(); + + // connection broken (no particular channel, no date yet) + btp_socket_emit_connection_closed(); + return; + } + + bytes_read += bytes_read; + bytes_to_read -= bytes_read; + if (bytes_to_read > 0) return; + + bool dispatch = false; + switch (state){ + case SOCKET_W4_HEADER: + state = SOCKET_W4_DATA; + bytes_to_read = little_endian_read_16( buffer, 3); + if (bytes_to_read == 0){ + dispatch = true; + } + break; + case SOCKET_W4_DATA: + dispatch = true; + break; + default: + break; + } + + if (dispatch){ + // dispatch packet !!! connection, type, channel, data, size + (*btp_socket_packet_callback)(buffer[0], buffer[1], buffer[2], little_endian_read_16( buffer, 3), &buffer[BTP_HEADER_LEN]); + + // reset state machine + btp_socket_init_statemachine(); + } +} + + +/** + * send BTP packet + */ +void btp_socket_send_packet(uint16_t service_id, uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t *data){ + if (socket_ds.source.fd == 0) return; + + uint8_t header[BTP_HEADER_LEN]; + header[0] = service_id; + header[1] = opcode; + header[2] = controller_index; + little_endian_store_16(header, 3, length); + int res; + res = write(socket_ds.source.fd, header, sizeof(header)); + res = write(socket_ds.source.fd, data, length); + UNUSED(res); +} + +/** + * create socket connection to auto-pts client + */ +bool btp_socket_open_unix(const char * socket_name){ + int btp_socket = socket(AF_UNIX, SOCK_STREAM, 0); + if(btp_socket == -1){ + return false; + } + + struct sockaddr_un server; + memset(&server, 0, sizeof(server)); + server.sun_family = AF_UNIX; + strcpy(server.sun_path, socket_name); + if (connect(btp_socket, (struct sockaddr *)&server, sizeof (server)) == -1){ + return false; + }; + + btstack_run_loop_set_data_source_handler(&socket_ds, &btp_socket_hci_process); + btstack_run_loop_set_data_source_fd(&socket_ds, btp_socket); + btstack_run_loop_enable_data_source_callbacks(&socket_ds, DATA_SOURCE_CALLBACK_READ); + + // prepare state machine and + btp_socket_init_statemachine(); + + // add this socket to the run_loop + btstack_run_loop_add_data_source( &socket_ds ); + + return true; +} + + +/** + * close socket connection to auto-pts client + */ +bool btp_socket_close_unix(void){ + if (socket_ds.source.fd == 0) return false; + shutdown(socket_ds.source.fd, SHUT_RDWR); + btp_socket_free_connection(); + return true; +} + +/** + * Init socket connection module + */ +void btp_socket_init(void){ + // just ignore broken sockets - NO_SO_SIGPIPE + sig_t result = signal(SIGPIPE, SIG_IGN); + if (result){ + log_error("btp_socket_init: failed to ignore SIGPIPE, error: %s", strerror(errno)); + } +} + +/** + * set packet handler + */ +void btp_socket_register_packet_handler( void (*packet_handler)(uint8_t service_id, uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t *data) ){ + btp_socket_packet_callback = packet_handler; +} + diff --git a/test/auto-pts/btp_socket.h b/test/auto-pts/btp_socket.h new file mode 100644 index 000000000..1ffc54163 --- /dev/null +++ b/test/auto-pts/btp_socket.h @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2014 BlueKitchen GmbH + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the names of + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * 4. Any redistribution, use, or modification is done solely for + * personal benefit and not for any commercial purpose or for + * monetary gain. + * + * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL MATTHIAS + * RINGWALD OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF + * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * Please inquire about commercial licensing options at + * contact@bluekitchen-gmbh.com + * + */ + + +/* + * btp_socket.h + */ + +#ifndef BTP_SOCKET_H +#define BTP_SOCKET_H + +#include "btstack_run_loop.h" +#include "btstack_defines.h" + +#include + +#if defined __cplusplus +extern "C" { +#endif + +// BTP Defines +#define BTP_INDEX_NON_CONTROLLER 0xff + +#define BTP_ERROR_FAIL 0x01 +#define BTP_ERROR_UNKNOWN_CMD 0x02 +#define BTP_ERROR_NOT_READY 0x03 +#define BTP_ERROR_INVALID_INDEX 0x04 + +#define BTP_CORE_SERVICE 0 +#define BTP_GAP_SERVICE 1 +#define BTP_GATT_SERVICE 2 +#define BTP_L2CAP_SERVICE 3 +#define BTP_MESH_NODE_SERVICE 4 + +/** + * @format 1 + * @param status + */ +#define BTP_OP_ERROR 0x00 + +#define BTP_OP_CORE_READ_SUPPORTED_COMMANDS 0x01 +#define BTP_OP_CORE_READ_SUPPORTED_SERVICES 0x02 +#define BTP_OP_CORE_REGISTER 0x03 +#define BTP_OP_CORE_UNREGISTER 0x04 + +#define BTP_EV_CORE_READY 0x80 + +/** + * Init socket connection module + */ +void btp_socket_init(void); + +/** + * create unix socket connection to auto-pts client + * @returns true if successful + */ +bool btp_socket_open_unix(const char * socket_name); + +/** + * close unix connection to auto-pts client + * @returns true if successful + */ +bool btp_socket_close_unix(void); + +/** + * set packet handler + */ +void btp_socket_register_packet_handler( void (*packet_handler)(uint8_t service_id, uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t *data) ); + +/** + * send HCI packet to single connection + */ +void btp_socket_send_packet(uint16_t service_id, uint8_t opcode, uint8_t controller_index, uint16_t length, const uint8_t * data); + +#if defined __cplusplus +} +#endif + +#endif // BTP_CONNECTION_H diff --git a/test/auto-pts/btpclient.c b/test/auto-pts/btpclient.c index 64f64adaf..53290f152 100644 --- a/test/auto-pts/btpclient.c +++ b/test/auto-pts/btpclient.c @@ -48,64 +48,73 @@ #include #include "btstack.h" +#include "btp_socket.h" + +#define AUTOPTS_SOCKET_NAME "/tmp/bt-stack-tester" int btstack_main(int argc, const char * argv[]); + static btstack_packet_callback_registration_t hci_event_callback_registration; -static void packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ +static void btstack_packet_handler (uint8_t packet_type, uint16_t channel, uint8_t *packet, uint16_t size){ UNUSED(channel); - - bd_addr_t event_addr; - uint8_t rfcomm_channel_nr; - switch (packet_type) { case HCI_EVENT_PACKET: switch (hci_event_packet_get_type(packet)) { - - case HCI_EVENT_PIN_CODE_REQUEST: - // inform about pin code request - printf("Pin code request - using '0000'\n"); - hci_event_pin_code_request_get_bd_addr(packet, event_addr); - gap_pin_code_response(event_addr, "0000"); - break; - - case HCI_EVENT_USER_CONFIRMATION_REQUEST: - // inform about user confirmation request - printf("SSP User Confirmation Request with numeric value '%06"PRIu32"'\n", little_endian_read_32(packet, 8)); - printf("SSP User Confirmation Auto accept\n"); - break; - + case BTSTACK_EVENT_STATE: + if (btstack_event_state_get_state(packet) != HCI_STATE_WORKING) return; + btp_socket_open_unix(AUTOPTS_SOCKET_NAME); + log_info("BTP_CORE_SERVICE/BTP_EV_CORE_READY/BTP_INDEX_NON_CONTROLLER()"); + btp_socket_send_packet(BTP_CORE_SERVICE, BTP_EV_CORE_READY, BTP_INDEX_NON_CONTROLLER, 0, NULL); default: break; } break; - 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){ + uint8_t status; + switch (service_id){ + case BTP_CORE_SERVICE: + switch (opcode){ + case BTP_OP_ERROR: + status = data[0]; + if (status == BTP_ERROR_NOT_READY){ + // connection stopped, abort + exit(10); + } + break; + } + break; + default: + break; + } +} + int btstack_main(int argc, const char * argv[]) { (void)argc; (void)argv; l2cap_init(); - rfcomm_init(); - - // init SDP, create record for SPP and register with SDP sdp_init(); // register for HCI events - hci_event_callback_registration.callback = &packet_handler; + hci_event_callback_registration.callback = &btstack_packet_handler; hci_add_event_handler(&hci_event_callback_registration); gap_ssp_set_io_capability(SSP_IO_CAPABILITY_DISPLAY_YES_NO); gap_set_local_name("iut 00:00:00:00:00:00"); gap_discoverable_control(1); + btp_socket_register_packet_handler(&btp_packet_handler); + printf("auto-pts iut-btp-client started\n"); + // turn on! hci_power_control(HCI_POWER_ON);