From 60c84a442587ae095e2466c4da6c6c17d3f6362a Mon Sep 17 00:00:00 2001 From: "mila@ringwald.ch" Date: Sat, 9 Nov 2013 23:48:36 +0000 Subject: [PATCH] implement advertising or scan data parser --- example/libusb/ad_parser.c | 73 +++++++++++++++ example/libusb/ad_parser.h | 73 +++++++++++++++ example/libusb/ble_client.c | 95 ++----------------- example/libusb/ble_client.h | 108 ++++++++++++++++++++++ test/ble_client/Makefile | 25 +++++ test/ble_client/advertising_data_parser.c | 53 +++++++++++ 6 files changed, 339 insertions(+), 88 deletions(-) create mode 100644 example/libusb/ad_parser.c create mode 100644 example/libusb/ad_parser.h create mode 100644 example/libusb/ble_client.h create mode 100644 test/ble_client/Makefile create mode 100644 test/ble_client/advertising_data_parser.c diff --git a/example/libusb/ad_parser.c b/example/libusb/ad_parser.c new file mode 100644 index 000000000..f8e4b9873 --- /dev/null +++ b/example/libusb/ad_parser.c @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011-2013 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. This software may not be used in a commercial product + * without an explicit license granted by the copyright holder. + * + * 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. + * + */ + +//***************************************************************************** +// +// Advertising Data Parser +// +//***************************************************************************** + +#include +#include +#include +#include + +#include "ad_parser.h" + +void ad_iterator_init(ad_context_t *context, uint8_t ad_len, uint8_t * ad_data){ + context->data = ad_data; + context->length = ad_len; + context->offset = 0; +} + +int ad_iterator_has_more(ad_context_t * context){ + return context->offset < context->length; +} + +void ad_iterator_next(ad_context_t * context){ + uint8_t chunk_len = context->data[context->offset]; + context->offset += 1 + chunk_len; +} + +uint8_t ad_iterator_get_data_len(ad_context_t * context){ + return context->data[context->offset] - 1; +} + +uint8_t ad_iterator_get_data_type(ad_context_t * context){ + return context->data[context->offset + 1]; +} + +uint8_t * ad_iterator_get_data(ad_context_t * context){ + return &context->data[context->offset + 2]; +} + diff --git a/example/libusb/ad_parser.h b/example/libusb/ad_parser.h new file mode 100644 index 000000000..10d1637a0 --- /dev/null +++ b/example/libusb/ad_parser.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2011-2013 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. This software may not be used in a commercial product + * without an explicit license granted by the copyright holder. + * + * 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. + * + */ + +//***************************************************************************** +// +// Advertising Data Parser +// +//***************************************************************************** + +#pragma once + +#include "config.h" + +#include + +#if defined __cplusplus +extern "C" { +#endif + +typedef struct ad_context { + uint8_t * data; + uint8_t offset; + uint8_t length; +} ad_context_t; + +// Advertising or Scan Response data iterator +void ad_iterator_init(ad_context_t *context, uint8_t ad_len, uint8_t * ad_data); +int ad_iterator_has_more(ad_context_t * context); +void ad_iterator_next(ad_context_t * context); + +// Access functions +uint8_t ad_iterator_get_data_type(ad_context_t * context); +uint8_t ad_iterator_get_data_len(ad_context_t * context); +uint8_t * ad_iterator_get_data(ad_context_t * context); + +// convenience function on complete advertisements +int ad_data_contains_uuid16(uint8_t ad_len, uint8_t * ad_data, uint16_t uuid); +int ad_data_contains_uuid128(uint8_t ad_len, uint8_t * ad_data, uint8_t * uuid128); + + +#if defined __cplusplus +} +#endif \ No newline at end of file diff --git a/example/libusb/ble_client.c b/example/libusb/ble_client.c index 4462b5f8c..ae24c1dfe 100644 --- a/example/libusb/ble_client.c +++ b/example/libusb/ble_client.c @@ -46,7 +46,7 @@ #include #include -#include "config.h" +#include "ble_client.h" #include "debug.h" #include "btstack_memory.h" @@ -56,90 +56,17 @@ #include "l2cap.h" #include "att.h" - -// API - -typedef struct gatt_client_event { - uint8_t type; -} gatt_client_event_t; - -typedef struct ad_event { - uint8_t type; - uint8_t event_type; - uint8_t address_type; - bd_addr_t address; - uint8_t rssi; - uint8_t length; - uint8_t * data; -} ad_event_t; +#include "ad_parser.h" -void (*gatt_client_callback)(gatt_client_event_t * event); - -void gatt_client_init(); -void gatt_client_start_scan(); -// creates one event per found peripheral device -// EVENT: type (8), addr_type (8), addr(48), rssi(8), ad_len(8), ad_data(ad_len*8) -void gatt_client_stop_scan(); - -/* - -typedef struct service_uuid{ - uint8_t lenght; - uint8_t * uuid; -} service_uuid_t; - -typedef struct peripheral{ - -} peripheral_t; - -// Advertising Data Parser - -typedef struct ad_context { - uint8_t * data; - uint8_t offset; - uint8_t length; -} ad_context_t; - -// iterator -void ad_init(ad_context_t *context, uint8_t ad_len, uint8_t * ad_data); -int ad_has_more(ad_context_t * context); -void ad_next(ad_context_t * context); - -// access functions -uint8_t ad_get_data_type(ad_context_t * context); -uint8_t ad_get_data_len(ad_context_t * context); -uint8_t * ad_get_data(ad_context_t * context); - -// convenience function on complete advertisements -int ad_data_contains_uuid16(uint8_t ad_len, uint8_t * ad_data, uint16_t uuid); -int ad_data_contains_uuid128(uint8_t ad_len, uint8_t * ad_data, uint8_t * uuid128); - -// example use of Advertisment Data Parser -void test_ad_parser(){ - ad_context_t context; - for (ad_init(&context, len, data) ; ad_has_more(&context) ; ad_next(&context)){ - uint8_t data_type = ad_get_data_type(&context); - uint8_t data_len = ad_get_data_len(&context); - uint8_t * data = ad_get_data(&context); +static void hexdump2(void *data, int size){ + int i; + for (i=0; i + +#if defined __cplusplus +extern "C" { +#endif + + +typedef struct gatt_client_event { + uint8_t type; +} gatt_client_event_t; + +typedef struct ad_event { + uint8_t type; + uint8_t event_type; + uint8_t address_type; + bd_addr_t address; + uint8_t rssi; + uint8_t length; + uint8_t * data; +} ad_event_t; + + +void (*gatt_client_callback)(gatt_client_event_t * event); + +void gatt_client_init(); +void gatt_client_start_scan(); +// creates one event per found peripheral device +// EVENT: type (8), addr_type (8), addr(48), rssi(8), ad_len(8), ad_data(ad_len*8) +void gatt_client_stop_scan(); + + +/* + +typedef struct service_uuid{ + uint8_t lenght; + uint8_t * uuid; +} service_uuid_t; + +typedef struct peripheral{ + +} peripheral_t; + + + +// GATT Client API + +void gatt_client_register_handler( btstack_packet_handler_t handler); + +uint16_t gatt_client_connect(bt_addr_t *dev); +void gatt_client_cancel_connect(peripheral_id peripheral); + +void get_services_for_peripheral(peripheral_id peripheral); +// EVENT: type (8), peripheral_id (16), service_id + +void get_characteristics_for_service(peripheral_id, service_id); +// EVENT: type (8), peripheral_id (16), service_id (16), ... +*/ + + +#if defined __cplusplus +} +#endif \ No newline at end of file diff --git a/test/ble_client/Makefile b/test/ble_client/Makefile new file mode 100644 index 000000000..cba174ae4 --- /dev/null +++ b/test/ble_client/Makefile @@ -0,0 +1,25 @@ +CC = g++ + +# Requirements: http://www.cpputest.org/ should be placed in btstack/test + +BTSTACK_ROOT = ../.. +CPPUTEST_HOME = ${BTSTACK_ROOT}/test/cpputest + +CFLAGS = -g -Wall -I. -I${BTSTACK_ROOT}/example/libusb -I${BTSTACK_ROOT}/include -I$(CPPUTEST_HOME)/include +LDFLAGS += -L$(CPPUTEST_HOME) -lCppUTest -lCppUTestExt + +COMMON = \ + ${BTSTACK_ROOT}/example/libusb/ad_parser.c \ + +COMMON_OBJ = $(COMMON:.c=.o) + +all: ad_parser + +ad_parser: ${CORE_OBJ} ${COMMON_OBJ} advertising_data_parser.c + ${CC} ${CORE_OBJ} ${COMMON_OBJ} advertising_data_parser.c ${CFLAGS} ${LDFLAGS} -o $@ + +clean: + rm -f ad_parser + rm -f *.o ${BTSTACK_ROOT}/src/*.o + rm -rf *.dSYM + \ No newline at end of file diff --git a/test/ble_client/advertising_data_parser.c b/test/ble_client/advertising_data_parser.c new file mode 100644 index 000000000..62489d18f --- /dev/null +++ b/test/ble_client/advertising_data_parser.c @@ -0,0 +1,53 @@ + +//***************************************************************************** +// +// test rfcomm query tests +// +//***************************************************************************** + + +#include +#include +#include +#include + +#include "ad_parser.h" + +#include "CppUTest/TestHarness.h" +#include "CppUTest/CommandLineTestRunner.h" + +static uint8_t ad_data[] = {0x02, 0x01, 0x05, 0x03, 0x02, 0xF0, 0xFF}; + +TEST_GROUP(ADParser){ +}; + + +TEST(ADParser, Test){ + ad_context_t context; + uint8_t expected_len[] = {1, 2}; + uint8_t expected_type[] = {0x01, 0x02}; + uint8_t expected_data[][2] = {{0x05, 0x00}, {0xF0, 0xFF}}; + + int i = 0; + uint8_t ad_len = sizeof(ad_data); + + for (ad_iterator_init(&context, ad_len, ad_data) ; ad_iterator_has_more(&context) ; ad_iterator_next(&context)){ + uint8_t data_type = ad_iterator_get_data_type(&context); + uint8_t data_len = ad_iterator_get_data_len(&context); + uint8_t * data = ad_iterator_get_data(&context); + + CHECK_EQUAL(expected_len[i], data_len); + CHECK_EQUAL(expected_type[i], data_type); + + int j; + for (j = 0; j < data_len; j++){ + CHECK_EQUAL(expected_data[i][j], data[j]); + } + i++; + } +} + + +int main (int argc, const char * argv[]){ + return CommandLineTestRunner::RunAllTests(argc, argv); +}