├── .gitignore ├── README.md ├── platform └── esp32 │ └── example │ ├── advertise │ ├── .gitignore │ ├── Makefile │ ├── main │ │ ├── component.mk │ │ ├── hci_transport.c │ │ └── main.c │ └── sdkconfig │ └── scan │ ├── .gitignore │ ├── Makefile │ ├── main │ ├── component.mk │ ├── hci_transport.c │ └── main.c │ └── sdkconfig └── stack ├── hci.c ├── inc ├── hci.h ├── hci_defs.h ├── hci_error.h ├── hci_transport.h ├── l2cap.h ├── l2cap_link.h └── toy_ble.h ├── l2cap.c ├── l2cap_link.c └── toy_ble.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Libraries 12 | *.lib 13 | *.a 14 | *.la 15 | *.lo 16 | 17 | # Shared objects (inc. Windows DLLs) 18 | *.dll 19 | *.so 20 | *.so.* 21 | *.dylib 22 | 23 | # Executables 24 | *.exe 25 | *.out 26 | *.app 27 | *.i*86 28 | *.x86_64 29 | *.hex 30 | 31 | # Debug files 32 | *.dSYM/ 33 | *.su 34 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Toy BLE Stack 2 | A toy Bluetooth Low Energy (BLE) stack for learning purposes. 3 | 4 | The goal of this project is to get a very basic BLE-only Host stack up and running in order to learn more about BLE internals. 5 | 6 | # Contributing 7 | 8 | Please do! I have very limited time to work on this but would love to keep it going. 9 | 10 | Things to know: 11 | 12 | - This project mostly follows the [Google C++ Style Guide](https://google.github.io/styleguide/cppguide.html), modified for C. 13 | - A major goal of this project is readable, easy to understand code. Concepts should be well-documented within the code itself, with references to external documentation such as the BLE specification. 14 | 15 | # Other Resources 16 | 17 | Check out these other great resources: 18 | 19 | - [chrisc11/ble-guides](https://github.com/chrisc11/ble-guides) - Excellent, in-depth guides for solving real-world problems such as achieving high throughput 20 | -------------------------------------------------------------------------------- /platform/esp32/example/advertise/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | sdkconfig.old 3 | -------------------------------------------------------------------------------- /platform/esp32/example/advertise/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := advertise 7 | 8 | include $(IDF_PATH)/make/project.mk 9 | 10 | -------------------------------------------------------------------------------- /platform/esp32/example/advertise/main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Main component makefile. 3 | # 4 | # This Makefile can be left empty. By default, it will take the sources in the 5 | # src/ directory, compile them and link them into lib(subdirectory_name).a 6 | # in the build directory. This behaviour is entirely configurable, 7 | # please read the ESP-IDF documents if you need to do this. 8 | # 9 | TBLE_ROOT := ../../../../.. 10 | 11 | COMPONENT_ADD_INCLUDEDIRS := $(TBLE_ROOT)/stack/inc . 12 | 13 | COMPONENT_SRCDIRS := $(TBLE_ROOT)/stack . 14 | -------------------------------------------------------------------------------- /platform/esp32/example/advertise/main/hci_transport.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Implements hci_transport.h for the esp32. 3 | */ 4 | #include "hci_transport.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "bt.h" // esp-idf VHCI definitions 13 | 14 | // @TODO currently always true 15 | static bool _can_send_packet = true; 16 | 17 | TBLE_PacketReceivedCallback packet_received_cb = NULL; 18 | 19 | static void _HostSendPacketAvailableCb(void) 20 | { 21 | printf("HostSendPacketAvailableCb\n"); 22 | _can_send_packet = true; 23 | } 24 | 25 | static int _HostReceivedPacketCb(uint8_t *data, uint16_t len) 26 | { 27 | if (packet_received_cb != NULL) { 28 | packet_received_cb(data, len); 29 | } 30 | 31 | return 0; 32 | } 33 | 34 | static const esp_vhci_host_callback_t _vhci_host_cb = { 35 | .notify_host_send_available = _HostSendPacketAvailableCb, 36 | .notify_host_recv = _HostReceivedPacketCb, 37 | }; 38 | 39 | int TBLE_Dep_HciTransportInit(void) 40 | { 41 | printf("TBLE_Dep_HciTransportInit\n"); 42 | esp_bt_controller_init(); 43 | esp_vhci_host_register_callback(&_vhci_host_cb); 44 | 45 | return 0; 46 | } 47 | 48 | 49 | int TBLE_Dep_HciTransportSendPacket(const void *packet, size_t len) 50 | { 51 | printf("TBLE_Dep_HciTransportSendPacket: [ "); 52 | for (size_t i = 0; i < len; i++) { 53 | printf("%02X ", ((uint8_t*)packet)[i]); 54 | } 55 | printf("] (%zu bytes)\n", len); 56 | 57 | esp_vhci_host_send_packet((uint8_t*)packet, len); 58 | 59 | return 0; 60 | } 61 | 62 | /** 63 | * Check if the transport is able to send a packet now. 64 | * 65 | * @return true if transport can send a packet, send false 66 | */ 67 | bool TBLE_Dep_HciTransportCanSendPacketNow(void) 68 | { 69 | return _can_send_packet; 70 | } 71 | 72 | /** 73 | * Register the packet received callback. 74 | */ 75 | void TBLE_Dep_HciTransportRegisterPacketReceivedCallback(TBLE_PacketReceivedCallback cb) 76 | { 77 | packet_received_cb = cb; 78 | } 79 | -------------------------------------------------------------------------------- /platform/esp32/example/advertise/main/main.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @TODO header 3 | * @TODO use GAP rather than direct HCI 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hci.h" 10 | #include "toy_ble.h" 11 | 12 | const uint8_t adv_data[] = { 13 | 0x02, 0x01, 0x06, // General Discoverable, BR/EDR unsupported 14 | 0x0b, 0x09, 'T', 'o', 'y', ' ', 'B', 'L', 'E' // Complete Local Name 15 | }; 16 | const uint8_t adv_data_len = sizeof(adv_data); 17 | 18 | void app_main(void) 19 | { 20 | if (TBLE_Init() != 0) { 21 | printf("ERROR: TBLE_Init failed\n"); 22 | return; 23 | } 24 | 25 | printf("HciSendReset\n"); 26 | HciSendReset(); 27 | 28 | printf("HciSendSetAdvertisingParams\n"); 29 | HciSendSetAdvertisingParams(256, // 160ms 30 | 256, // 160ms 31 | 0, // Connectable undirected (ADV_IND) 32 | 0, // Public BD_ADDR 33 | 0, // Public BD_ADDR 34 | NULL, 35 | 0x07, // 37, 38, 39 36 | 0); // Process All Conn and Scan 37 | 38 | printf("HciSendSetAdvertisingData\n"); 39 | HciSendSetAdvertisingData(adv_data_len, adv_data); 40 | 41 | printf("HciSendAdvertiseEnable\n"); 42 | HciSendSetAdvertiseEnable(true); 43 | 44 | while (1) { 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /platform/esp32/example/advertise/sdkconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # Espressif IoT Development Framework Configuration 4 | # 5 | 6 | # 7 | # SDK tool configuration 8 | # 9 | CONFIG_TOOLPREFIX="xtensa-esp32-elf-" 10 | CONFIG_PYTHON="python" 11 | 12 | # 13 | # Bootloader config 14 | # 15 | # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set 16 | # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set 17 | CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y 18 | # CONFIG_LOG_BOOTLOADER_LEVEL_INFO is not set 19 | # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set 20 | # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set 21 | CONFIG_LOG_BOOTLOADER_LEVEL=2 22 | 23 | # 24 | # Security features 25 | # 26 | # CONFIG_SECURE_BOOT_ENABLED is not set 27 | # CONFIG_FLASH_ENCRYPTION_ENABLED is not set 28 | 29 | # 30 | # Serial flasher config 31 | # 32 | CONFIG_ESPTOOLPY_PORT="/dev/tty.usbserial-00001014B" 33 | # CONFIG_ESPTOOLPY_BAUD_115200B is not set 34 | # CONFIG_ESPTOOLPY_BAUD_230400B is not set 35 | # CONFIG_ESPTOOLPY_BAUD_921600B is not set 36 | CONFIG_ESPTOOLPY_BAUD_2MB=y 37 | # CONFIG_ESPTOOLPY_BAUD_OTHER is not set 38 | CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 39 | CONFIG_ESPTOOLPY_BAUD=2000000 40 | # CONFIG_ESPTOOLPY_COMPRESSED is not set 41 | # CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set 42 | # CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set 43 | CONFIG_ESPTOOLPY_FLASHMODE_DIO=y 44 | # CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set 45 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 46 | # CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set 47 | CONFIG_ESPTOOLPY_FLASHFREQ_40M=y 48 | # CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set 49 | # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set 50 | CONFIG_ESPTOOLPY_FLASHFREQ="40m" 51 | # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set 52 | CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y 53 | # CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set 54 | # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set 55 | # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set 56 | CONFIG_ESPTOOLPY_FLASHSIZE="2MB" 57 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 58 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 59 | # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set 60 | # CONFIG_ESPTOOLPY_BEFORE_ESP32R0 is not set 61 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 62 | CONFIG_ESPTOOLPY_AFTER_RESET=y 63 | # CONFIG_ESPTOOLPY_AFTER_NORESET is not set 64 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 65 | # CONFIG_MONITOR_BAUD_9600B is not set 66 | # CONFIG_MONITOR_BAUD_57600B is not set 67 | CONFIG_MONITOR_BAUD_115200B=y 68 | # CONFIG_MONITOR_BAUD_230400B is not set 69 | # CONFIG_MONITOR_BAUD_921600B is not set 70 | # CONFIG_MONITOR_BAUD_2MB is not set 71 | # CONFIG_MONITOR_BAUD_OTHER is not set 72 | CONFIG_MONITOR_BAUD_OTHER_VAL=115200 73 | CONFIG_MONITOR_BAUD=115200 74 | 75 | # 76 | # Partition Table 77 | # 78 | CONFIG_PARTITION_TABLE_SINGLE_APP=y 79 | # CONFIG_PARTITION_TABLE_TWO_OTA is not set 80 | # CONFIG_PARTITION_TABLE_CUSTOM is not set 81 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 82 | CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 83 | CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" 84 | CONFIG_APP_OFFSET=0x10000 85 | CONFIG_PHY_DATA_OFFSET=0xf000 86 | CONFIG_OPTIMIZATION_LEVEL_DEBUG=y 87 | # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set 88 | 89 | # 90 | # Component config 91 | # 92 | CONFIG_BT_ENABLED=y 93 | CONFIG_BTC_TASK_STACK_SIZE=3072 94 | # CONFIG_BLUEDROID_MEM_DEBUG is not set 95 | CONFIG_BT_RESERVE_DRAM=0x10000 96 | 97 | # 98 | # ESP32-specific 99 | # 100 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set 101 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set 102 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y 103 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 104 | CONFIG_MEMMAP_SMP=y 105 | # CONFIG_MEMMAP_TRACEMEM is not set 106 | CONFIG_TRACEMEM_RESERVE_DRAM=0x0 107 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set 108 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set 109 | CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y 110 | # CONFIG_ESP32_ENABLE_COREDUMP is not set 111 | CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 112 | CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 113 | CONFIG_MAIN_TASK_STACK_SIZE=4096 114 | CONFIG_NEWLIB_STDOUT_ADDCR=y 115 | # CONFIG_NEWLIB_NANO_FORMAT is not set 116 | CONFIG_CONSOLE_UART_DEFAULT=y 117 | # CONFIG_CONSOLE_UART_CUSTOM is not set 118 | # CONFIG_CONSOLE_UART_NONE is not set 119 | CONFIG_CONSOLE_UART_NUM=0 120 | CONFIG_CONSOLE_UART_BAUDRATE=115200 121 | # CONFIG_ULP_COPROC_ENABLED is not set 122 | CONFIG_ULP_COPROC_RESERVE_MEM=0 123 | # CONFIG_ESP32_PANIC_PRINT_HALT is not set 124 | CONFIG_ESP32_PANIC_PRINT_REBOOT=y 125 | # CONFIG_ESP32_PANIC_SILENT_REBOOT is not set 126 | # CONFIG_ESP32_PANIC_GDBSTUB is not set 127 | CONFIG_ESP32_DEBUG_OCDAWARE=y 128 | CONFIG_INT_WDT=y 129 | CONFIG_INT_WDT_TIMEOUT_MS=300 130 | CONFIG_TASK_WDT=y 131 | # CONFIG_TASK_WDT_PANIC is not set 132 | CONFIG_TASK_WDT_TIMEOUT_S=5 133 | CONFIG_TASK_WDT_CHECK_IDLE_TASK=y 134 | # CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set 135 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y 136 | # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set 137 | # CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set 138 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y 139 | CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=0 140 | # CONFIG_WIFI_ENABLED is not set 141 | CONFIG_PHY_ENABLED=y 142 | 143 | # 144 | # PHY 145 | # 146 | CONFIG_ESP32_PHY_AUTO_INIT=y 147 | # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set 148 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 149 | # CONFIG_ETHERNET is not set 150 | 151 | # 152 | # FreeRTOS 153 | # 154 | CONFIG_FREERTOS_UNICORE=y 155 | CONFIG_FREERTOS_CORETIMER_0=y 156 | # CONFIG_FREERTOS_CORETIMER_1 is not set 157 | CONFIG_FREERTOS_HZ=1000 158 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y 159 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set 160 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y 161 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY is not set 162 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 163 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y 164 | # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set 165 | # CONFIG_FREERTOS_ASSERT_DISABLE is not set 166 | CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG=y 167 | # CONFIG_ENABLE_MEMORY_DEBUG is not set 168 | CONFIG_FREERTOS_ISR_STACKSIZE=1536 169 | # CONFIG_FREERTOS_LEGACY_HOOKS is not set 170 | # CONFIG_FREERTOS_DEBUG_INTERNALS is not set 171 | 172 | # 173 | # Log output 174 | # 175 | # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set 176 | # CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set 177 | # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set 178 | CONFIG_LOG_DEFAULT_LEVEL_INFO=y 179 | # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set 180 | # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set 181 | CONFIG_LOG_DEFAULT_LEVEL=3 182 | CONFIG_LOG_COLORS=y 183 | 184 | # 185 | # LWIP 186 | # 187 | # CONFIG_L2_TO_L3_COPY is not set 188 | CONFIG_LWIP_MAX_SOCKETS=4 189 | CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX=0 190 | # CONFIG_LWIP_SO_REUSE is not set 191 | # CONFIG_LWIP_SO_RCVBUF is not set 192 | CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 193 | # CONFIG_LWIP_IP_FRAG is not set 194 | # CONFIG_LWIP_IP_REASSEMBLY is not set 195 | CONFIG_TCP_MAXRTX=12 196 | CONFIG_TCP_SYNMAXRTX=6 197 | # CONFIG_LWIP_DHCP_DOES_ARP_CHECK is not set 198 | 199 | # 200 | # mbedTLS 201 | # 202 | CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 203 | # CONFIG_MBEDTLS_DEBUG is not set 204 | CONFIG_MBEDTLS_HARDWARE_AES=y 205 | CONFIG_MBEDTLS_HARDWARE_MPI=y 206 | CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y 207 | CONFIG_MBEDTLS_HARDWARE_SHA=y 208 | CONFIG_MBEDTLS_HAVE_TIME=y 209 | # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set 210 | 211 | # 212 | # OpenSSL 213 | # 214 | # CONFIG_OPENSSL_DEBUG is not set 215 | CONFIG_OPENSSL_ASSERT_DO_NOTHING=y 216 | # CONFIG_OPENSSL_ASSERT_EXIT is not set 217 | 218 | # 219 | # SPI Flash driver 220 | # 221 | # CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set 222 | -------------------------------------------------------------------------------- /platform/esp32/example/scan/.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | sdkconfig.old 3 | -------------------------------------------------------------------------------- /platform/esp32/example/scan/Makefile: -------------------------------------------------------------------------------- 1 | # 2 | # This is a project Makefile. It is assumed the directory this Makefile resides in is a 3 | # project subdirectory. 4 | # 5 | 6 | PROJECT_NAME := advertise 7 | 8 | include $(IDF_PATH)/make/project.mk 9 | 10 | -------------------------------------------------------------------------------- /platform/esp32/example/scan/main/component.mk: -------------------------------------------------------------------------------- 1 | # 2 | # Main component makefile. 3 | # 4 | # This Makefile can be left empty. By default, it will take the sources in the 5 | # src/ directory, compile them and link them into lib(subdirectory_name).a 6 | # in the build directory. This behaviour is entirely configurable, 7 | # please read the ESP-IDF documents if you need to do this. 8 | # 9 | TBLE_ROOT := ../../../../.. 10 | 11 | COMPONENT_ADD_INCLUDEDIRS := $(TBLE_ROOT)/stack/inc . 12 | 13 | COMPONENT_SRCDIRS := $(TBLE_ROOT)/stack . 14 | -------------------------------------------------------------------------------- /platform/esp32/example/scan/main/hci_transport.c: -------------------------------------------------------------------------------- 1 | /** 2 | * Implements hci_transport.h for the esp32. 3 | */ 4 | #include "hci_transport.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "bt.h" // esp-idf VHCI definitions 13 | 14 | // @TODO currently always true 15 | static bool _can_send_packet = true; 16 | 17 | TBLE_PacketReceivedCallback packet_received_cb = NULL; 18 | 19 | static void _HostSendPacketAvailableCb(void) 20 | { 21 | printf("HostSendPacketAvailableCb\n"); 22 | _can_send_packet = true; 23 | } 24 | 25 | static int _HostReceivedPacketCb(uint8_t *data, uint16_t len) 26 | { 27 | if (packet_received_cb != NULL) { 28 | packet_received_cb(data, len); 29 | } 30 | 31 | return 0; 32 | } 33 | 34 | static const esp_vhci_host_callback_t _vhci_host_cb = { 35 | .notify_host_send_available = _HostSendPacketAvailableCb, 36 | .notify_host_recv = _HostReceivedPacketCb, 37 | }; 38 | 39 | int TBLE_Dep_HciTransportInit(void) 40 | { 41 | printf("TBLE_Dep_HciTransportInit\n"); 42 | esp_bt_controller_init(); 43 | esp_vhci_host_register_callback(&_vhci_host_cb); 44 | 45 | return 0; 46 | } 47 | 48 | 49 | int TBLE_Dep_HciTransportSendPacket(const void *packet, size_t len) 50 | { 51 | printf("TBLE_Dep_HciTransportSendPacket: [ "); 52 | for (size_t i = 0; i < len; i++) { 53 | printf("%02X ", ((uint8_t*)packet)[i]); 54 | } 55 | printf("] (%zu bytes)\n", len); 56 | 57 | esp_vhci_host_send_packet((uint8_t*)packet, len); 58 | 59 | return 0; 60 | } 61 | 62 | /** 63 | * Check if the transport is able to send a packet now. 64 | * 65 | * @return true if transport can send a packet, send false 66 | */ 67 | bool TBLE_Dep_HciTransportCanSendPacketNow(void) 68 | { 69 | return _can_send_packet; 70 | } 71 | 72 | /** 73 | * Register the packet received callback. 74 | */ 75 | void TBLE_Dep_HciTransportRegisterPacketReceivedCallback(TBLE_PacketReceivedCallback cb) 76 | { 77 | packet_received_cb = cb; 78 | } 79 | -------------------------------------------------------------------------------- /platform/esp32/example/scan/main/main.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @TODO header 3 | * @TODO use GAP rather than direct HCI 4 | */ 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hci.h" 10 | #include "toy_ble.h" 11 | 12 | void app_main(void) 13 | { 14 | if (TBLE_Init() != 0) { 15 | printf("ERROR: TBLE_Init failed\n"); 16 | return; 17 | } 18 | 19 | HciSendReset(); 20 | 21 | // Note that we set the scan interval equal to the scan window, 22 | // so scanning should run continuously. 23 | HciSendSetScanParams(0x01, // Active scanning 24 | 0x0004, // Min scan interval 25 | 0x0004, // Min scan window 26 | 0x0, // Public address type 27 | 0x0); // Accept everything except directed adv not addressed to us 28 | 29 | HciSendSetScanEnable(true, // Enable scan 30 | false); // Don't filter duplicates 31 | 32 | while (1) { 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /platform/esp32/example/scan/sdkconfig: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically generated file; DO NOT EDIT. 3 | # Espressif IoT Development Framework Configuration 4 | # 5 | 6 | # 7 | # SDK tool configuration 8 | # 9 | CONFIG_TOOLPREFIX="xtensa-esp32-elf-" 10 | CONFIG_PYTHON="python" 11 | 12 | # 13 | # Bootloader config 14 | # 15 | # CONFIG_LOG_BOOTLOADER_LEVEL_NONE is not set 16 | # CONFIG_LOG_BOOTLOADER_LEVEL_ERROR is not set 17 | CONFIG_LOG_BOOTLOADER_LEVEL_WARN=y 18 | # CONFIG_LOG_BOOTLOADER_LEVEL_INFO is not set 19 | # CONFIG_LOG_BOOTLOADER_LEVEL_DEBUG is not set 20 | # CONFIG_LOG_BOOTLOADER_LEVEL_VERBOSE is not set 21 | CONFIG_LOG_BOOTLOADER_LEVEL=2 22 | 23 | # 24 | # Security features 25 | # 26 | # CONFIG_SECURE_BOOT_ENABLED is not set 27 | # CONFIG_FLASH_ENCRYPTION_ENABLED is not set 28 | 29 | # 30 | # Serial flasher config 31 | # 32 | CONFIG_ESPTOOLPY_PORT="/dev/tty.usbserial-00001014B" 33 | # CONFIG_ESPTOOLPY_BAUD_115200B is not set 34 | # CONFIG_ESPTOOLPY_BAUD_230400B is not set 35 | # CONFIG_ESPTOOLPY_BAUD_921600B is not set 36 | CONFIG_ESPTOOLPY_BAUD_2MB=y 37 | # CONFIG_ESPTOOLPY_BAUD_OTHER is not set 38 | CONFIG_ESPTOOLPY_BAUD_OTHER_VAL=115200 39 | CONFIG_ESPTOOLPY_BAUD=2000000 40 | # CONFIG_ESPTOOLPY_COMPRESSED is not set 41 | # CONFIG_ESPTOOLPY_FLASHMODE_QIO is not set 42 | # CONFIG_ESPTOOLPY_FLASHMODE_QOUT is not set 43 | CONFIG_ESPTOOLPY_FLASHMODE_DIO=y 44 | # CONFIG_ESPTOOLPY_FLASHMODE_DOUT is not set 45 | CONFIG_ESPTOOLPY_FLASHMODE="dio" 46 | # CONFIG_ESPTOOLPY_FLASHFREQ_80M is not set 47 | CONFIG_ESPTOOLPY_FLASHFREQ_40M=y 48 | # CONFIG_ESPTOOLPY_FLASHFREQ_26M is not set 49 | # CONFIG_ESPTOOLPY_FLASHFREQ_20M is not set 50 | CONFIG_ESPTOOLPY_FLASHFREQ="40m" 51 | # CONFIG_ESPTOOLPY_FLASHSIZE_1MB is not set 52 | CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y 53 | # CONFIG_ESPTOOLPY_FLASHSIZE_4MB is not set 54 | # CONFIG_ESPTOOLPY_FLASHSIZE_8MB is not set 55 | # CONFIG_ESPTOOLPY_FLASHSIZE_16MB is not set 56 | CONFIG_ESPTOOLPY_FLASHSIZE="2MB" 57 | CONFIG_ESPTOOLPY_FLASHSIZE_DETECT=y 58 | CONFIG_ESPTOOLPY_BEFORE_RESET=y 59 | # CONFIG_ESPTOOLPY_BEFORE_NORESET is not set 60 | # CONFIG_ESPTOOLPY_BEFORE_ESP32R0 is not set 61 | CONFIG_ESPTOOLPY_BEFORE="default_reset" 62 | CONFIG_ESPTOOLPY_AFTER_RESET=y 63 | # CONFIG_ESPTOOLPY_AFTER_NORESET is not set 64 | CONFIG_ESPTOOLPY_AFTER="hard_reset" 65 | # CONFIG_MONITOR_BAUD_9600B is not set 66 | # CONFIG_MONITOR_BAUD_57600B is not set 67 | CONFIG_MONITOR_BAUD_115200B=y 68 | # CONFIG_MONITOR_BAUD_230400B is not set 69 | # CONFIG_MONITOR_BAUD_921600B is not set 70 | # CONFIG_MONITOR_BAUD_2MB is not set 71 | # CONFIG_MONITOR_BAUD_OTHER is not set 72 | CONFIG_MONITOR_BAUD_OTHER_VAL=115200 73 | CONFIG_MONITOR_BAUD=115200 74 | 75 | # 76 | # Partition Table 77 | # 78 | CONFIG_PARTITION_TABLE_SINGLE_APP=y 79 | # CONFIG_PARTITION_TABLE_TWO_OTA is not set 80 | # CONFIG_PARTITION_TABLE_CUSTOM is not set 81 | CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv" 82 | CONFIG_PARTITION_TABLE_CUSTOM_APP_BIN_OFFSET=0x10000 83 | CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv" 84 | CONFIG_APP_OFFSET=0x10000 85 | CONFIG_PHY_DATA_OFFSET=0xf000 86 | CONFIG_OPTIMIZATION_LEVEL_DEBUG=y 87 | # CONFIG_OPTIMIZATION_LEVEL_RELEASE is not set 88 | 89 | # 90 | # Component config 91 | # 92 | CONFIG_BT_ENABLED=y 93 | CONFIG_BTC_TASK_STACK_SIZE=3072 94 | # CONFIG_BLUEDROID_MEM_DEBUG is not set 95 | CONFIG_BT_RESERVE_DRAM=0x10000 96 | 97 | # 98 | # ESP32-specific 99 | # 100 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_80 is not set 101 | # CONFIG_ESP32_DEFAULT_CPU_FREQ_160 is not set 102 | CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y 103 | CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240 104 | CONFIG_MEMMAP_SMP=y 105 | # CONFIG_MEMMAP_TRACEMEM is not set 106 | CONFIG_TRACEMEM_RESERVE_DRAM=0x0 107 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_FLASH is not set 108 | # CONFIG_ESP32_ENABLE_COREDUMP_TO_UART is not set 109 | CONFIG_ESP32_ENABLE_COREDUMP_TO_NONE=y 110 | # CONFIG_ESP32_ENABLE_COREDUMP is not set 111 | CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 112 | CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 113 | CONFIG_MAIN_TASK_STACK_SIZE=4096 114 | CONFIG_NEWLIB_STDOUT_ADDCR=y 115 | # CONFIG_NEWLIB_NANO_FORMAT is not set 116 | CONFIG_CONSOLE_UART_DEFAULT=y 117 | # CONFIG_CONSOLE_UART_CUSTOM is not set 118 | # CONFIG_CONSOLE_UART_NONE is not set 119 | CONFIG_CONSOLE_UART_NUM=0 120 | CONFIG_CONSOLE_UART_BAUDRATE=115200 121 | # CONFIG_ULP_COPROC_ENABLED is not set 122 | CONFIG_ULP_COPROC_RESERVE_MEM=0 123 | # CONFIG_ESP32_PANIC_PRINT_HALT is not set 124 | CONFIG_ESP32_PANIC_PRINT_REBOOT=y 125 | # CONFIG_ESP32_PANIC_SILENT_REBOOT is not set 126 | # CONFIG_ESP32_PANIC_GDBSTUB is not set 127 | CONFIG_ESP32_DEBUG_OCDAWARE=y 128 | CONFIG_INT_WDT=y 129 | CONFIG_INT_WDT_TIMEOUT_MS=300 130 | CONFIG_TASK_WDT=y 131 | # CONFIG_TASK_WDT_PANIC is not set 132 | CONFIG_TASK_WDT_TIMEOUT_S=5 133 | CONFIG_TASK_WDT_CHECK_IDLE_TASK=y 134 | # CONFIG_ESP32_TIME_SYSCALL_USE_RTC is not set 135 | CONFIG_ESP32_TIME_SYSCALL_USE_RTC_FRC1=y 136 | # CONFIG_ESP32_TIME_SYSCALL_USE_FRC1 is not set 137 | # CONFIG_ESP32_TIME_SYSCALL_USE_NONE is not set 138 | CONFIG_ESP32_RTC_CLOCK_SOURCE_INTERNAL_RC=y 139 | CONFIG_ESP32_DEEP_SLEEP_WAKEUP_DELAY=0 140 | # CONFIG_WIFI_ENABLED is not set 141 | CONFIG_PHY_ENABLED=y 142 | 143 | # 144 | # PHY 145 | # 146 | CONFIG_ESP32_PHY_AUTO_INIT=y 147 | # CONFIG_ESP32_PHY_INIT_DATA_IN_PARTITION is not set 148 | CONFIG_ESP32_PHY_MAX_TX_POWER=20 149 | # CONFIG_ETHERNET is not set 150 | 151 | # 152 | # FreeRTOS 153 | # 154 | CONFIG_FREERTOS_UNICORE=y 155 | CONFIG_FREERTOS_CORETIMER_0=y 156 | # CONFIG_FREERTOS_CORETIMER_1 is not set 157 | CONFIG_FREERTOS_HZ=1000 158 | CONFIG_FREERTOS_ASSERT_ON_UNTESTED_FUNCTION=y 159 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_NONE is not set 160 | CONFIG_FREERTOS_CHECK_STACKOVERFLOW_PTRVAL=y 161 | # CONFIG_FREERTOS_CHECK_STACKOVERFLOW_CANARY is not set 162 | CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=3 163 | CONFIG_FREERTOS_ASSERT_FAIL_ABORT=y 164 | # CONFIG_FREERTOS_ASSERT_FAIL_PRINT_CONTINUE is not set 165 | # CONFIG_FREERTOS_ASSERT_DISABLE is not set 166 | CONFIG_FREERTOS_BREAK_ON_SCHEDULER_START_JTAG=y 167 | # CONFIG_ENABLE_MEMORY_DEBUG is not set 168 | CONFIG_FREERTOS_ISR_STACKSIZE=1536 169 | # CONFIG_FREERTOS_LEGACY_HOOKS is not set 170 | # CONFIG_FREERTOS_DEBUG_INTERNALS is not set 171 | 172 | # 173 | # Log output 174 | # 175 | # CONFIG_LOG_DEFAULT_LEVEL_NONE is not set 176 | # CONFIG_LOG_DEFAULT_LEVEL_ERROR is not set 177 | # CONFIG_LOG_DEFAULT_LEVEL_WARN is not set 178 | CONFIG_LOG_DEFAULT_LEVEL_INFO=y 179 | # CONFIG_LOG_DEFAULT_LEVEL_DEBUG is not set 180 | # CONFIG_LOG_DEFAULT_LEVEL_VERBOSE is not set 181 | CONFIG_LOG_DEFAULT_LEVEL=3 182 | CONFIG_LOG_COLORS=y 183 | 184 | # 185 | # LWIP 186 | # 187 | # CONFIG_L2_TO_L3_COPY is not set 188 | CONFIG_LWIP_MAX_SOCKETS=4 189 | CONFIG_LWIP_THREAD_LOCAL_STORAGE_INDEX=0 190 | # CONFIG_LWIP_SO_REUSE is not set 191 | # CONFIG_LWIP_SO_RCVBUF is not set 192 | CONFIG_LWIP_DHCP_MAX_NTP_SERVERS=1 193 | # CONFIG_LWIP_IP_FRAG is not set 194 | # CONFIG_LWIP_IP_REASSEMBLY is not set 195 | CONFIG_TCP_MAXRTX=12 196 | CONFIG_TCP_SYNMAXRTX=6 197 | # CONFIG_LWIP_DHCP_DOES_ARP_CHECK is not set 198 | 199 | # 200 | # mbedTLS 201 | # 202 | CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384 203 | # CONFIG_MBEDTLS_DEBUG is not set 204 | CONFIG_MBEDTLS_HARDWARE_AES=y 205 | CONFIG_MBEDTLS_HARDWARE_MPI=y 206 | CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y 207 | CONFIG_MBEDTLS_HARDWARE_SHA=y 208 | CONFIG_MBEDTLS_HAVE_TIME=y 209 | # CONFIG_MBEDTLS_HAVE_TIME_DATE is not set 210 | 211 | # 212 | # OpenSSL 213 | # 214 | # CONFIG_OPENSSL_DEBUG is not set 215 | CONFIG_OPENSSL_ASSERT_DO_NOTHING=y 216 | # CONFIG_OPENSSL_ASSERT_EXIT is not set 217 | 218 | # 219 | # SPI Flash driver 220 | # 221 | # CONFIG_SPI_FLASH_ENABLE_COUNTERS is not set 222 | -------------------------------------------------------------------------------- /stack/hci.c: -------------------------------------------------------------------------------- 1 | #include "hci.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "hci_defs.h" 8 | #include "hci_transport.h" 9 | #include "l2cap.h" 10 | #include "toy_ble.h" 11 | 12 | // @TODO real logging 13 | #include 14 | #include 15 | 16 | /** 17 | * The buffer to be used for all outgoing 18 | * HCI commands. 19 | */ 20 | static uint8_t _hci_cmd_buf[HCI_MAX_CMD_LEN]; 21 | 22 | /** 23 | * @brief Initialize a new HCI Command. 24 | * 25 | * @param[in] ogf The OGF of the command 26 | * @param[in] ocf The OCF of the command 27 | * 28 | * @return Pointer to command that caller should fill 29 | */ 30 | static inline void *_InitHciCmd(uint16_t ogf, uint16_t ocf) 31 | { 32 | _hci_cmd_buf[0] = kHciPacketTypeCommand; 33 | *((uint16_t*)&_hci_cmd_buf[1]) = HCI_OPCODE(ogf, ocf); 34 | return &_hci_cmd_buf[1]; 35 | } 36 | 37 | /** 38 | * @brief Send an HCI Command. 39 | * 40 | * @param[in] len Length of the command, excluding packet type byte 41 | */ 42 | static inline int _SendHciCmd(size_t len) 43 | { 44 | return TBLE_Dep_HciTransportSendPacket(_hci_cmd_buf, len + 1); 45 | } 46 | 47 | int HciSendReset(void) 48 | { 49 | HciCmdReset *const pkt = 50 | _InitHciCmd(kHciGroupControllerBaseband, kHciCmdControllerReset); 51 | pkt->hdr.param_len = sizeof(*pkt) - sizeof(pkt->hdr); 52 | 53 | return _SendHciCmd(sizeof(*pkt)); 54 | } 55 | 56 | // @TODO enums etc. 57 | int HciSendSetAdvertisingParams(uint16_t adv_interval_min, 58 | uint16_t adv_interval_max, 59 | uint8_t adv_type, 60 | uint8_t own_addr_type, 61 | uint8_t peer_addr_type, 62 | BdAddr peer_addr, 63 | uint8_t adv_chan_map, 64 | uint8_t adv_filter_policy) 65 | { 66 | HciCmdSetAdvertisingParams *const pkt = 67 | _InitHciCmd(kHciGroupLeController, kHciCmdLeSetAdvertisingParams); 68 | pkt->hdr.param_len = sizeof(*pkt) - sizeof(pkt->hdr); 69 | 70 | pkt->adv_interval_min = adv_interval_min; 71 | pkt->adv_interval_max = adv_interval_max; 72 | pkt->adv_type = adv_type; 73 | pkt->own_addr_type = own_addr_type; 74 | pkt->peer_addr_type = peer_addr_type; 75 | pkt->adv_chan_map = adv_chan_map; 76 | pkt->adv_filter_policy = adv_filter_policy; 77 | 78 | if (peer_addr != NULL) { 79 | // @TODO probably needs byte reversing 80 | memcpy(pkt->peer_addr, peer_addr, BD_ADDR_LEN); 81 | } 82 | 83 | return _SendHciCmd(sizeof(*pkt)); 84 | } 85 | 86 | int HciSendSetAdvertisingData(uint8_t adv_data_len, const uint8_t *adv_data) 87 | { 88 | // @TODO bounds checking 89 | 90 | HciCmdSetAdvertisingData *const pkt = 91 | _InitHciCmd(kHciGroupLeController, kHciCmdLeSetAdvertisingData); 92 | pkt->hdr.param_len = sizeof(*pkt) - sizeof(pkt->hdr); 93 | 94 | pkt->adv_data_len = adv_data_len; 95 | memcpy(pkt->adv_data, adv_data, adv_data_len); 96 | 97 | // Unused bytes must be zero 98 | if (adv_data_len < sizeof(pkt->adv_data)) { 99 | memset(pkt->adv_data + adv_data_len, 0, sizeof(pkt->adv_data) - adv_data_len); 100 | } 101 | 102 | return _SendHciCmd(sizeof(*pkt)); 103 | } 104 | 105 | int HciSendSetAdvertiseEnable(bool enable) 106 | { 107 | HciCmdSetAdvertiseEnable *const pkt = 108 | _InitHciCmd(kHciGroupLeController, kHciCmdLeSetAdvertiseEnable); 109 | pkt->hdr.param_len = sizeof(*pkt) - sizeof(pkt->hdr); 110 | 111 | pkt->enable = enable; 112 | 113 | return _SendHciCmd(sizeof(*pkt)); 114 | } 115 | 116 | // @TODO enums etc. 117 | int HciSendSetScanParams(uint8_t le_scan_type, 118 | uint16_t le_scan_interval, 119 | uint16_t le_scan_window, 120 | uint8_t own_address_type, 121 | uint8_t scanning_filter_policy) 122 | { 123 | HciCmdSetScanParams *const pkt = 124 | _InitHciCmd(kHciGroupLeController, kHciCmdLeSetScanParams); 125 | pkt->hdr.param_len = sizeof(*pkt) - sizeof(pkt->hdr); 126 | 127 | pkt->le_scan_type = le_scan_type; 128 | pkt->le_scan_interval = le_scan_interval; 129 | pkt->le_scan_window = le_scan_window; 130 | pkt->own_address_type = own_address_type; 131 | pkt->scanning_filter_policy = scanning_filter_policy; 132 | 133 | return _SendHciCmd(sizeof(*pkt)); 134 | } 135 | 136 | int HciSendSetScanEnable(bool enable, bool filter_duplicates) 137 | { 138 | HciCmdSetScanEnable *const pkt = 139 | _InitHciCmd(kHciGroupLeController, kHciCmdLeSetScanEnable); 140 | pkt->hdr.param_len = sizeof(*pkt) - sizeof(pkt->hdr); 141 | 142 | pkt->enable = enable; 143 | pkt->filter_duplicates = filter_duplicates; 144 | 145 | return _SendHciCmd(sizeof(*pkt)); 146 | } 147 | 148 | static int _HandleAclDataReceived(const void *data, size_t len) 149 | { 150 | printf("HCI ACL In: [ "); 151 | for (size_t i = 0; i < len; i++) { 152 | printf("%02X ", ((uint8_t*)data)[i]); 153 | } 154 | printf("] (%" PRIu16 " bytes)\n", len); 155 | 156 | const HciAclDataPkt *const pkt = data; 157 | 158 | printf(" --> Conn handle: %u\n", pkt->hdr.conn_handle); 159 | printf(" --> PB Flag: %u\n", pkt->hdr.pkt_boundary_flag); 160 | printf(" --> BC Flag: %u\n", pkt->hdr.broadcast_flag); 161 | printf(" --> Data Len: %" PRIu16 "\n", pkt->hdr.data_len); 162 | 163 | // @TODO dependency function for upper layer, etc. 164 | return L2capHandleAclDataReceived(pkt->data, pkt->hdr.data_len, pkt->hdr.pkt_boundary_flag); 165 | } 166 | 167 | static int _HandleEventReceived(const void *data, size_t len) 168 | { 169 | printf("HCI Event In: [ "); 170 | for (size_t i = 0; i < len; i++) { 171 | printf("%02X ", ((uint8_t*)data)[i]); 172 | } 173 | printf("] (%" PRIu16 " bytes)\n", len); 174 | 175 | return 0; 176 | } 177 | 178 | int HciHandleDataReceived(void *data, size_t len) 179 | { 180 | int status = 0; 181 | 182 | const HciPkt *const pkt = data; 183 | 184 | switch (pkt->type) { 185 | case kHciPacketTypeAclData: 186 | status = _HandleAclDataReceived(pkt->data, len - sizeof(pkt->type)); 187 | break; 188 | 189 | case kHciPacketTypeEventData: 190 | status = _HandleEventReceived(pkt->data, len - sizeof(pkt->type)); 191 | break; 192 | 193 | default: 194 | printf("ERROR: Unknown packet type received: type = %" PRIx8 "\n", pkt->type); 195 | status = -1; 196 | break; 197 | } 198 | 199 | return status; 200 | } 201 | -------------------------------------------------------------------------------- /stack/inc/hci.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file defines the Host Controller Interface (HCI) layer. 3 | * 4 | * In the BLE stack, the HCI layer exists directly below L2CAP and directly 5 | * above the physical bus driver to the Controller (e.g. UART or USB). 6 | * 7 | * If the Host and Controller are integrated, then the HCI layer is not required. 8 | * In this case, L2CAP would exist directly above the LMP layer of the Controller 9 | * stack. 10 | * 11 | * Responsibilities: 12 | * - Standardizes interface between (and decouples) Host and Controller 13 | * - Manages flow control between Host and Controller 14 | * 15 | * Services Provided: 16 | * - Exposes functionality to allow Host to: 17 | * - Discover, add, and manage devices on the piconet 18 | * - Configure Controller properties 19 | * - Set up, manage, and release logical transports and links 20 | * - Write directly to the baseband's buffer for SCO and eSCO streaming 21 | * 22 | * References: 23 | * - BT Spec: Vol 2, Part E - HCI Functional Specification 24 | * - BT Spec: Vol 2, Part E, Section 3.19 - HCI Requirements for LE Controller 25 | */ 26 | #pragma once 27 | 28 | #include 29 | #include 30 | #include 31 | 32 | #include "toy_ble.h" 33 | 34 | /** 35 | * Send an HCI Reset command to the Controller. 36 | */ 37 | int HciSendReset(void); 38 | 39 | /** 40 | * Send an HCI Set Advertising Parameters command to the Controller. 41 | */ 42 | // @TODO enums etc. 43 | int HciSendSetAdvertisingParams(uint16_t adv_interval_min, 44 | uint16_t adv_interval_max, 45 | uint8_t adv_type, 46 | uint8_t own_addr_type, 47 | uint8_t peer_addr_type, 48 | BdAddr peer_addr, 49 | uint8_t adv_chan_map, 50 | uint8_t adv_filter_policy); 51 | 52 | /** 53 | * Send an HCI Advertise Enable command to the Controller. 54 | */ 55 | int HciSendSetAdvertiseEnable(bool enable); 56 | 57 | /** 58 | * Send an HCI Set Advertising Data command to the Controller. 59 | */ 60 | int HciSendSetAdvertisingData(uint8_t adv_data_len, const uint8_t *adv_data); 61 | 62 | /** 63 | * Send an HCI Set Scan Parameters command to the Controller. 64 | */ 65 | // @TODO enums etc. 66 | int HciSendSetScanParams(uint8_t le_scan_type, 67 | uint16_t le_scan_interval, 68 | uint16_t le_scan_window, 69 | uint8_t own_address_type, 70 | uint8_t scanning_filter_policy); 71 | 72 | /** 73 | * Send an HCI Set Scan Enable command to the Controller. 74 | */ 75 | int HciSendSetScanEnable(bool enable, bool filter_duplicates); 76 | 77 | /** 78 | * Handle generic data received by the HCI Transport. 79 | */ 80 | int HciHandleDataReceived(void *data, size_t len); 81 | -------------------------------------------------------------------------------- /stack/inc/hci_defs.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Definitions required by the HCI layer 3 | */ 4 | #pragma once 5 | 6 | #include "hci_error.h" 7 | #include "toy_ble.h" 8 | 9 | typedef uint16_t HciOpcode; 10 | #define HCI_OPCODE(ogf, ocf) (HciOpcode)((ocf | ogf << 10)) 11 | 12 | #define HCI_ADVERTISING_DATA_LEN 31 13 | 14 | /** 15 | * HCI Packet Types 16 | */ 17 | typedef enum { 18 | kHciPacketTypeCommand = 0x01, 19 | kHciPacketTypeAclData = 0x02, 20 | kHciPacketTypeSyncData = 0x03, 21 | kHciPacketTypeEventData = 0x04, 22 | } HciPacketType; 23 | 24 | /** 25 | * HCI Group Types (OGFs) 26 | */ 27 | typedef enum { 28 | kHciGroupLinkControl = 0x01, 29 | kHciGroupLinkPolicy = 0x02, 30 | kHciGroupControllerBaseband = 0x03, 31 | kHciGroupInformationalParams = 0x04, 32 | kHciGroupStatusParams = 0x05, 33 | kHciGroupTest = 0x06, 34 | kHciGroupLeController = 0x08, 35 | kHciGroupVendorSpecific = 0x3F, 36 | } HciGroup; 37 | 38 | /** 39 | * Link Control Commands (OGF 0x01) 40 | * BT Spec: Vol 2, Part E, Section 7.1 41 | */ 42 | typedef enum { 43 | kHciCmdLinkControlInquiry = 0x01, 44 | kHciCmdLinkControlInquiryCancel = 0x02, 45 | kHciCmdLinkControlPeriodicInquiryMode = 0x03, 46 | kHciCmdLinkControlExitPeriodicInquiryMode = 0x04, 47 | kHciCmdLinkControlCreateConnection = 0x05, 48 | kHciCmdLinkControlDisconnect = 0x06, 49 | kHciCmdLinkControlAddSCOConnection = 0x07, 50 | kHciCmdLinkControlAcceptConnectionRequest = 0x09, 51 | kHciCmdLinkControlRejectConnectionRequest = 0x0A, 52 | kHciCmdLinkControlLinkKeyRequestReply = 0x0B, 53 | kHciCmdLinkControlLinkKeyRequestNegativeReply = 0x0C, 54 | kHciCmdLinkControlPINRequestReply = 0x0D, 55 | kHciCmdLinkControlPINRequestNegativeReply = 0x0E, 56 | kHciCmdLinkControlChangeConnectionPacketType = 0x0F, 57 | kHciCmdLinkControlAuthenticationRequested = 0x11, 58 | kHciCmdLinkControlSetConnectionEncryption = 0x13, 59 | kHciCmdLinkControlChangeConnectionLinkKey = 0x15, 60 | kHciCmdLinkControlMasterLinkKey = 0x17, 61 | kHciCmdLinkControlRemoteNameRequest = 0x19, 62 | kHciCmdLinkControlReadRemoteSupportedFeatures = 0x1B, 63 | kHciCmdLinkControlReadRemoteVersionInfo = 0x1D, 64 | kHciCmdLinkControlReadClockOffset = 0x1F, 65 | } HciCmdLinkControl; 66 | 67 | /** 68 | * Link Policy Commands (OGF 0x02) 69 | * BT Spec: Vol 2, Part E, Section 7.2 70 | */ 71 | typedef enum { 72 | kHciCmdLinkPolicyHoldMode = 0x01, 73 | kHciCmdLinkPolicySniffMode = 0x03, 74 | kHciCmdLinkPolicyExitSniffMode = 0x04, 75 | kHciCmdLinkPolicyParkMode = 0x05, 76 | kHciCmdLinkPolicyExitParkMode = 0x06, 77 | kHciCmdLinkPolicyQoSSetup = 0x07, 78 | kHciCmdLinkPolicyRoleDiscovery = 0x09, 79 | kHciCmdLinkPolicySwitchRole = 0x0B, 80 | kHciCmdLinkPolicyReadSettings = 0x0C, 81 | kHciCmdLinkPolicyWriteSettings = 0x0D, 82 | } HciCmdLinkPolicy; 83 | 84 | /** 85 | * Controller and Baseband Commands (OGF 0x03) 86 | * BT Spec: Vol 2, Part E, Section 7.1 87 | */ 88 | typedef enum { 89 | kHciCmdControllerSetEventMask = 0x01, 90 | kHciCmdControllerReset = 0x03, 91 | kHciCmdControllerSetEventFilter = 0x05, 92 | kHciCmdControllerFlush = 0x08, 93 | kHciCmdControllerReadPINType = 0x09, 94 | kHciCmdControllerWritePINType = 0x0A, 95 | kHciCmdControllerCreateNewUnitKey = 0x0B, 96 | kHciCmdControllerReadStoredLinkKey = 0x0D, 97 | kHciCmdControllerWriteStoredLinkKey = 0x11, 98 | kHciCmdControllerDeleteStoredLinkKey = 0x12, 99 | kHciCmdControllerChangeLocalName = 0x13, 100 | kHciCmdControllerReadLocalName = 0x14, 101 | kHciCmdControllerReadConnectionAcceptTimeout = 0x15, 102 | kHciCmdControllerWriteConnectionAcceptTimeout = 0x16, 103 | kHciCmdControllerReadPageTimeout = 0x17, 104 | kHciCmdControllerWritePageTimeout = 0x18, 105 | kHciCmdControllerReadScanEnable = 0x19, 106 | kHciCmdControllerWriteScanEnable = 0x1A, 107 | kHciCmdControllerReadPageScanActivity = 0x1B, 108 | kHciCmdControllerWritePageScanActivity = 0x1C, 109 | kHciCmdControllerReadInquiryScanActivity = 0x1D, 110 | kHciCmdControllerWriteInquiryScanActivity = 0x1E, 111 | kHciCmdControllerReadAuthEnable = 0x1F, 112 | kHciCmdControllerWriteAuthEnable = 0x20, 113 | kHciCmdControllerReadEncryptionMode = 0x21, 114 | kHciCmdControllerWriteEncryptionMode = 0x22, 115 | kHciCmdControllerReadClassOfDevice = 0x23, 116 | kHciCmdControllerWriteClassOfDevice = 0x24, 117 | kHciCmdControllerReadVoiceSetting = 0x25, 118 | kHciCmdControllerWriteVoiceSetting = 0x26, 119 | kHciCmdControllerReadAutoFlushTimeout = 0x27, 120 | kHciCmdControllerWriteAutoFlushTimeout = 0x28, 121 | kHciCmdControllerReadNumBroadcastRetrans = 0x29, 122 | kHciCmdControllerWriteNumBroadcastRetrans = 0x2A, 123 | kHciCmdControllerReadHoldModeActivity = 0x2B, 124 | kHciCmdControllerWriteHoldModeActivity = 0x2C, 125 | kHciCmdControllerReadTransmitPowerLevel = 0x2D, 126 | kHciCmdControllerReadSCOFlowControlEnable = 0x2E, 127 | kHciCmdControllerWriteSCOFlowControlEnable = 0x2F, 128 | kHciCmdControllerSetControllerToHostFlowControl = 0x31, 129 | kHciCmdControllerHostBufferSize = 0x33, 130 | kHciCmdControllerHostNumCompletedPackets = 0x35, 131 | kHciCmdControllerReadLinkSupervisionTimeout = 0x36, 132 | kHciCmdControllerWriteLinkSupervisionTimeout = 0x37, 133 | kHciCmdControllerReadNumSupportedIAC = 0x38, 134 | kHciCmdControllerReadCurrentIAC_LAP = 0x39, 135 | kHciCmdControllerWriteCurrentIAC_LAP = 0x3A, 136 | kHciCmdControllerReadPageScanPeriodMode = 0x3B, 137 | kHciCmdControllerWritePageScanPeriodMode = 0x3C, 138 | kHciCmdControllerReadPageScanMode = 0x3D, 139 | kHciCmdControllerWritePageScanMode = 0x3E, 140 | } HciCmdControllerBaseband; 141 | 142 | /** 143 | * LE Controller Commands (OGF 0x08) 144 | * BT Spec: Vol 2, Part E, Section 7.8 145 | */ 146 | typedef enum { 147 | kHciCmdLeSetEventMask = 0x01, 148 | kHciCmdLeReadBufferSize = 0x02, 149 | kHciCmdLeReadLocalSupportedFeatures = 0x03, 150 | kHciCmdLeSetRandomAddr = 0x05, 151 | kHciCmdLeSetAdvertisingParams = 0x06, 152 | kHciCmdLeReadAdvertisingChannelTxPower = 0x07, 153 | kHciCmdLeSetAdvertisingData = 0x08, 154 | kHciCmdLeSetScanResponseData = 0x09, 155 | kHciCmdLeSetAdvertiseEnable = 0x0A, 156 | kHciCmdLeSetScanParams = 0x0B, 157 | kHciCmdLeSetScanEnable = 0x0C, 158 | kHciCmdLeCreateConnection = 0x0D, 159 | kHciCmdLeCreateConnectionCancel = 0x0E, 160 | kHciCmdLeReadWhiteListSize = 0x0F, 161 | kHciCmdLeClearWhiteList = 0x10, 162 | kHciCmdLeAddDeviceToWhiteList = 0x11, 163 | kHciCmdLeRemoveDeviceFromWhiteList = 0x12, 164 | kHciCmdLeConnectionUpdate = 0x13, 165 | kHciCmdLeSetHostChannelClassification = 0x14, 166 | kHciCmdLeReadChannelMap = 0x15, 167 | kHciCmdLeReadRemoteUsedFeatures = 0x16, 168 | kHciCmdLeEncrypt = 0x17, 169 | kHciCmdLeRand = 0x18, 170 | kHciCmdLeStartEncryption = 0x19, 171 | kHciCmdLeLongTermKeyRequestReply = 0x1A, 172 | kHciCmdLeLongTermKeyRequestNegativeReply = 0x1B, 173 | kHciCmdLeReadSupportedStates = 0x1C, 174 | kHciCmdLeReceiverTest = 0x1D, 175 | kHciCmdLeTransmitterTest = 0x1E, 176 | kHciCmdLeTestEnd = 0x1F, 177 | kHciCmdLeRemoteConnectionParamReqReply = 0x20, 178 | kHciCmdLeRemoteConnectionParamReqNegativeReply = 0x21, 179 | kHciCmdLeSetDataLength = 0x22, 180 | kHciCmdLeReadSuggestedDefaultDataLength = 0x23, 181 | kHciCmdLeWriteSuggestedDefaultDataLength = 0x24, 182 | kHciCmdLeReadLocalP256PublicKey = 0x25, 183 | kHciCmdLeGenerateDHKey = 0x26, 184 | kHciCmdLeAddDeviceToResolvingList = 0x27, 185 | kHciCmdLeRemoveDeviceFromResolvingList = 0x28, 186 | kHciCmdLeClearResolvingList = 0x29, 187 | kHciCmdLeReadResolvingListSize = 0x2A, 188 | kHciCmdLeReadPeerResolvableAddr = 0x2B, 189 | kHciCmdLeReadLocalResolvableAddr = 0x2C, 190 | kHciCmdLeSetAddrResolutionEnable = 0x2D, 191 | kHciCmdLeSetResolvablePrivateAddrTimeout = 0x2E, 192 | kHciCmdLeReadMaximumDataLength = 0x2F, 193 | } HciCmdLeController; 194 | 195 | /** 196 | * HCI ACL Packet Boundary Flags 197 | */ 198 | typedef enum { 199 | kHciAclPbFlagFirstNonFlushable = 0x0, 200 | kHciAclPbFlagContinuingFragment = 0x1, 201 | kHciAclPbFlagFirstFlushable = 0x2, 202 | kHciAclPbFlagCompleteL2capPdu = 0x3, 203 | } HciAclPacketBoundaryFlag; 204 | 205 | #pragma pack(1) 206 | typedef struct { 207 | uint8_t type; 208 | uint8_t data[]; 209 | } HciPkt; 210 | #pragma pack() 211 | 212 | #pragma pack(1) 213 | typedef struct { 214 | uint16_t opcode; 215 | uint8_t param_len; 216 | } HciCmdHdr; 217 | #pragma pack() 218 | 219 | // @TODO might not want to use bitfields here 220 | #pragma pack(1) 221 | typedef struct { 222 | uint16_t conn_handle : 12; 223 | uint16_t pkt_boundary_flag : 2; 224 | uint16_t broadcast_flag : 2; 225 | uint16_t data_len; 226 | } HciAclDataHdr; 227 | #pragma pack() 228 | 229 | #pragma pack(1) 230 | typedef struct { 231 | HciAclDataHdr hdr; 232 | uint8_t data[]; 233 | } HciAclDataPkt; 234 | #pragma pack() 235 | 236 | #pragma pack(1) 237 | typedef struct { 238 | HciCmdHdr hdr; 239 | uint8_t params[]; 240 | } HciCmd; 241 | #pragma pack() 242 | 243 | #pragma pack(1) 244 | typedef struct { 245 | HciCmdHdr hdr; 246 | } HciCmdReset; 247 | #pragma pack() 248 | 249 | #pragma pack(1) 250 | typedef struct { 251 | HciCmdHdr hdr; 252 | uint16_t adv_interval_min; 253 | uint16_t adv_interval_max; 254 | uint8_t adv_type; 255 | uint8_t own_addr_type; 256 | uint8_t peer_addr_type; 257 | uint8_t peer_addr[BD_ADDR_LEN]; 258 | uint8_t adv_chan_map; 259 | uint8_t adv_filter_policy; 260 | } HciCmdSetAdvertisingParams; 261 | #pragma pack() 262 | 263 | #pragma pack(1) 264 | typedef struct { 265 | HciCmdHdr hdr; 266 | uint8_t adv_data_len; 267 | uint8_t adv_data[HCI_ADVERTISING_DATA_LEN]; 268 | } HciCmdSetAdvertisingData; 269 | #pragma pack() 270 | 271 | #pragma pack(1) 272 | typedef struct { 273 | HciCmdHdr hdr; 274 | uint8_t enable; 275 | } HciCmdSetAdvertiseEnable; 276 | #pragma pack() 277 | 278 | #pragma pack(1) 279 | typedef struct { 280 | HciCmdHdr hdr; 281 | uint8_t le_scan_type; 282 | uint16_t le_scan_interval; 283 | uint16_t le_scan_window; 284 | uint8_t own_address_type; 285 | uint8_t scanning_filter_policy; 286 | } HciCmdSetScanParams; 287 | #pragma pack() 288 | 289 | #pragma pack(1) 290 | typedef struct { 291 | HciCmdHdr hdr; 292 | uint8_t enable; 293 | uint8_t filter_duplicates; 294 | } HciCmdSetScanEnable; 295 | #pragma pack() 296 | 297 | #define HCI_MAX_PARAM_LEN (UINT8_MAX) 298 | #define HCI_MAX_CMD_LEN (HCI_MAX_PARAM_LEN + sizeof(HciCmdHdr)) 299 | -------------------------------------------------------------------------------- /stack/inc/hci_error.h: -------------------------------------------------------------------------------- 1 | /** 2 | * HCI error codes 3 | * 4 | * BT Spec: Vol 2, Part D, Section 1.3 5 | */ 6 | 7 | typedef enum { 8 | kHciErrorCodeSuccess = 0x00, // Success 9 | kHciErrorCodeUnknownCmd = 0x01, // Unknown HCI Command 10 | kHciErrorCodeUnknownConn = 0x02, // Unknown Connection Identifier 11 | kHciErrorCodeHardwareFailure = 0x03, // Hardware Failure 12 | kHciErrorCodePageTimeout = 0x04, // Page Timeout 13 | kHciErrorCodeAuthFailure = 0x05, // Authentication Failure 14 | kHciErrorCodeKeyMissing = 0x06, // PIN or Key Missing 15 | kHciErrorCodeMemCapacityExceeded = 0x07, // Memory Capacity Exceeded 16 | kHciErrorCodeConnTimeout = 0x08, // Connection Timeout 17 | kHciErrorCodeConnLimitExceeded = 0x09, // Connection Limit Exceeded 18 | kHciErrorCodeSyncConnLimitExceeded = 0x0A, // Synchronous Connection Limit To A Device Exceeded 19 | kHciErrorCodeACLConnectionExists = 0x0B, // ACL Connection Already Exists 20 | kHciErrorCodeCmdDisallowed = 0x0C, // Command Disallowed 21 | kHciErrorCodeConnRejectedLimitedResources = 0x0D, // Connection Rejected due to Limited Resources 22 | kHciErrorCodeConnRejectedSecurityReason = 0x0E, // Connection Rejected Due To Security Reasons 23 | kHciErrorCodeConnRejectedBadAddr = 0x0F, // Connection Rejected due to Unacceptable BD_ADDR 24 | kHciErrorCodeConnAcceptTimeout = 0x10, // Connection Accept Timeout Exceeded 25 | kHciErrorCodeUnsupported = 0x11, // Unsupported Feature or Parameter Value 26 | kHciErrorCodeInvalidParam = 0x12, // Invalid HCI Command Parameters 27 | kHciErrorCodeRemoteUserTerminated = 0x13, // Remote User Terminated Connection 28 | kHciErrorCodeRemoveDeviceLowResource = 0x14, // Remote Device Terminated Connection due to Low Resources 29 | kHciErrorCodeRemoteDevicePowerOff = 0x15, // Remote Device Terminated Connection due to Power Off 30 | kHciErrorCodeLocalHostTerminated = 0x16, // Connection Terminated By Local Host 31 | kHciErrorCodeRepeatedAttempts = 0x17, // Repeated Attempts 32 | kHciErrorCodePairingDisallowed = 0x18, // Pairing Not Allowed 33 | kHciErrorCodeUnknownPDU = 0x19, // Unknown LMP PDU 34 | kHciErrorCodeUnsupportedRemoteFeature = 0x1A, // Unsupported Remote Feature / Unsupported LMP Feature 35 | kHciErrorCodeSCOOffsetRejected = 0x1B, // SCO Offset Rejected 36 | kHciErrorCodeSCOIntervalRejected = 0x1C, // SCO Interval Rejected 37 | kHciErrorCodeSCOAirModeRejected = 0x1D, // SCO Air Mode Rejected 38 | kHciErrorCodeInvalidLMP_LLParams = 0x1E, // Invalid LMP Parameters / Invalid LL Parameters 39 | kHciErrorCodeUnspecified = 0x1F, // Unspecified Error 40 | kHciErrorCodeUnspecifiedLMP_LLParam = 0x20, // Unsupported LMP Parameter Value / Unsupported LL Parameter Value 41 | kHciErrorCodeRoleChangeDisallowed = 0x21, // Role Change Not Allowed 42 | kHciErrorCodeLMPResponseTimeout = 0x22, // LMP Response Timeout / LL Response Timeout 43 | kHciErrorCodeLMPCollision = 0x23, // LMP Error Transaction Collision 44 | kHciErrorCodeLMP_PDUDisallowed = 0x24, // LMP PDU Not Allowed 45 | kHciErrorCodeEncryptionMode = 0x25, // Encryption Mode Not Acceptable 46 | kHciErrorCodeLinkKey = 0x26, // Link Key cannot be Changed 47 | kHciErrorCodeQoSUnsupported = 0x27, // Requested QoS Not Supported 48 | kHciErrorCodeInstantPassed = 0x28, // Instant Passed 49 | kHciErrorCodeUnitKeyUnsupported = 0x29, // Pairing With Unit Key Not Supported 50 | kHciErrorCodeDifferentTransactionCollision = 0x2A, // Different Transaction Collision 51 | kHciErrorCodeReserved2B = 0x2B, // Reserved 52 | kHciErrorCodeQoSBadParam = 0x2C, // QoS Unacceptable Parameter 53 | kHciErrorCodeQoSRejected = 0x2D, // QoS Rejected 54 | kHciErrorCodeChannelClassUnsupported = 0x2E, // Channel Classification Not Supported 55 | kHciErrorCodeInsufficientSecurity = 0x2F, // Insufficient Security 56 | kHciErrorCodeParamaterOutOfRange = 0x30, // Parameter Out Of Mandatory Range 57 | kHciErrorCodeReserved31 = 0x31, // Reserved 58 | kHciErrorCodeRoleSwitchPending = 0x32, // Role Switch Pending 59 | kHciErrorCodeReserved33 = 0x33, // Reserved 60 | kHciErrorCodeReservedSlotViolation = 0x34, // Reserved Slot Violation 61 | kHciErrorCodeRoleSwitchFailed = 0x35, // Role Switch Failed 62 | kHciErrorCodeEIRTooLarge = 0x36, // Extended Inquiry Response Too Large 63 | kHciErrorCodeSSPUnsupported = 0x37, // Secure Simple Pairing Not Supported By Host 64 | kHciErrorCodeHostBusyPairing = 0x38, // Host Busy - Pairing 65 | kHciErrorCodeNoSuitableChannel = 0x39, // Connection Rejected due to No Suitable Channel Found 66 | kHciErrorCodeControllerBusy = 0x3A, // Controller Busy 67 | kHciErrorCodeBadConnectionParams = 0x3B, // Unacceptable Connection Parameters 68 | kHciErrorCodeDirectedAdvTimeout = 0x3C, // Directed Advertising Timeout 69 | kHciErrorCodeMICFailure = 0x3D, // Connection Terminated due to MIC Failure 70 | kHciErrorCodeConnectionFailed = 0x3E, // Connection Failed to be Established 71 | kHciErrorCodeMACConnectionFailed = 0x3F, // MAC Connection Failed 72 | kHciErrorCodeCourseClockAdjustRejected = 0x40, // Coarse Clock Adjustment Rejected but Will Try to Adjust Using Clock Dragging 73 | } HciErrorCode; 74 | -------------------------------------------------------------------------------- /stack/inc/hci_transport.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file defines a generic HCI transport interface. 3 | * 4 | * Users must provide implementations for the functions defined in this file. 5 | */ 6 | #pragma once 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | /** 13 | * Initialize this HCI transport. 14 | * 15 | * @TODO define error codes 16 | * @return 0 for success, else error code 17 | */ 18 | int TBLE_Dep_HciTransportInit(void); 19 | 20 | /** 21 | * Send a packet over the HCI transport. 22 | * 23 | * @param[in] packet The packet data to send 24 | * @param[in] len The length of packet 25 | * 26 | * @TODO define error codes 27 | * @return 0 for success, else error code 28 | */ 29 | int TBLE_Dep_HciTransportSendPacket(const void *packet, size_t len); 30 | 31 | /** 32 | * Check if the transport is able to send a packet now. 33 | * 34 | * @return true if transport can send a packet, send false 35 | */ 36 | bool TBLE_Dep_HciTransportCanSendPacketNow(void); 37 | 38 | /** 39 | * Callback to be called when a new incoming packet is received over the transport 40 | * 41 | * @param[in] pkt The received packet 42 | * @param[in] len The length of the received packet 43 | */ 44 | typedef int(*TBLE_PacketReceivedCallback)(void *pkt, size_t len); 45 | 46 | /** 47 | * Register the packet received callback. 48 | */ 49 | void TBLE_Dep_HciTransportRegisterPacketReceivedCallback(TBLE_PacketReceivedCallback cb); 50 | -------------------------------------------------------------------------------- /stack/inc/l2cap.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file defines the Logical Link Control and Adaptation Protocol (L2CAP) layer. 3 | * 4 | * In the BLE stack, the L2CAP layer exists directly below ATT and directly above HCI. 5 | * 6 | * Responsibilities: 7 | * - Multiplexes logical channels (called L2CAP channels) over one or more logical links 8 | * 9 | * Services Provided: 10 | * - Connection-oriented and connectionless data services 11 | * - Protocol multiplexing capability 12 | * - Segmentation and reassembly operation 13 | * 14 | * References: 15 | * - BT Spec: Vol 3, Part A - L2CAP Specification 16 | */ 17 | #pragma once 18 | 19 | #include 20 | #include 21 | 22 | #include "hci_defs.h" 23 | 24 | // @TODO should probably be user-configurable 25 | #define L2CAP_ACL_MAX_PAYLOAD_LEN 512 26 | 27 | #define L2CAP_ACL_RECEIVE_BUFFER_LEN (sizeof(HciAclDataHdr) + L2CAP_ACL_MAX_PAYLOAD_LEN) 28 | 29 | /** 30 | * State definitions for the ACL Receive State Machine 31 | */ 32 | typedef enum { 33 | kL2capAclReceiveStateIdle, // No data expected currently 34 | kL2capAclReceiveStateReceiving, // Receiving data 35 | kL2capAclReceiveStateReceivedAllData, // Received all data 36 | } L2capAclReceiveState; 37 | 38 | /** 39 | * ACL Receive State Machine 40 | * 41 | * Handles reassembly of fragmented ACL data received over HCI. 42 | */ 43 | typedef struct { 44 | L2capAclReceiveState state; 45 | size_t num_bytes_expected; 46 | size_t buffer_pos; 47 | uint8_t buffer[L2CAP_ACL_RECEIVE_BUFFER_LEN]; 48 | } L2capAclReceiveStateMachine; 49 | 50 | /** 51 | * @TODO 52 | */ 53 | int L2capHandleAclDataReceived(const void *data, size_t len, HciAclPacketBoundaryFlag pb_flag); 54 | -------------------------------------------------------------------------------- /stack/inc/l2cap_link.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @TODO header 3 | * @TODO make a generic connection database 4 | * @TODO linked list perhaps 5 | */ 6 | #pragma once 7 | 8 | #include 9 | 10 | #include "hci_defs.h" 11 | #include "l2cap.h" 12 | 13 | typedef uint16_t L2capLinkHandle; 14 | 15 | // @TODO should probably be user-configurable 16 | #define L2CAP_MAX_NUM_LINKS 2 17 | 18 | typedef struct { 19 | bool is_init; 20 | 21 | L2capLinkHandle handle; 22 | 23 | L2capAclReceiveStateMachine acl_recv_fsm; 24 | } L2capLink; 25 | 26 | /** 27 | * @TODO 28 | */ 29 | L2capLink *L2capLinkForHandle(L2capLinkHandle handle); 30 | 31 | /** 32 | * @TODO 33 | */ 34 | L2capLink *L2capLinkInitNew(L2capLinkHandle handle); 35 | -------------------------------------------------------------------------------- /stack/inc/toy_ble.h: -------------------------------------------------------------------------------- 1 | /** 2 | * This file defines the main public interface to the Toy BLE Stack. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | #define BD_ADDR_LEN 6 9 | 10 | typedef uint8_t BdAddr[BD_ADDR_LEN]; 11 | 12 | /** 13 | * Initialize the stack. Must be called before any other TBLE_* functions are called. 14 | * 15 | * @TODO define error codes 16 | * @return 0 for success, else error code 17 | */ 18 | int TBLE_Init(void); 19 | -------------------------------------------------------------------------------- /stack/l2cap.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @TODO header 3 | */ 4 | #include "l2cap.h" 5 | 6 | #include 7 | #include 8 | 9 | #include "hci_defs.h" 10 | #include "l2cap_link.h" 11 | 12 | 13 | int L2capHandleAclDataReceived(const void *data, size_t len, HciAclPacketBoundaryFlag pb_flag) 14 | { 15 | /* 16 | L2capLink *const link = L2capLinkForHandle(pkt->hdr.conn_handle); 17 | 18 | // LE links are created automatically, so just create one 19 | // if it doesn't exist 20 | if (link == NULL) { 21 | link = L2capLinkInitNew(handle); 22 | 23 | if (link == NULL) { 24 | // @TODO what to do here? 25 | printf("ERROR: out of L2CAP links\n"); 26 | return -1; 27 | } 28 | } 29 | 30 | HciAclReceiveStateMachine *const fsm = link->acl_recv_fsm; 31 | switch (pb_flag) 32 | { 33 | case kL2capAclPbFlagFirstFlushable: 34 | // @TODO 35 | break; 36 | 37 | case kL2capAclPbFlagFirstNonFlushable: 38 | case kL2capAclPbFlagContinuingFragment: 39 | case kL2capAclPbFlagCompleteL2capPdu: 40 | default: 41 | // @TODO some of these 42 | printf("ERROR: ACL PB flag %u not yet supported\n", pb_flag); 43 | status = -1; 44 | break; 45 | } 46 | */ 47 | 48 | return 0; 49 | } 50 | -------------------------------------------------------------------------------- /stack/l2cap_link.c: -------------------------------------------------------------------------------- 1 | /** 2 | * @TODO header 3 | */ 4 | #include "l2cap_link.h" 5 | 6 | #include 7 | #include 8 | 9 | /** 10 | * L2CAP Link database. 11 | */ 12 | static L2capLink _link_db[L2CAP_MAX_NUM_LINKS]; 13 | 14 | static inline void _ResetLink(L2capLink *link) 15 | { 16 | link->is_init = false; 17 | 18 | // Reset ACL Receive State Machine 19 | link->acl_recv_fsm.state = kL2capAclReceiveStateIdle; 20 | link->acl_recv_fsm.num_bytes_expected = 0; 21 | link->acl_recv_fsm.buffer_pos = 0; 22 | } 23 | 24 | /** 25 | * @TODO 26 | */ 27 | static L2capLink *_GetFreeLink(void) 28 | { 29 | for (size_t i = 0; i < L2CAP_MAX_NUM_LINKS; i++) { 30 | if (_link_db[i].is_init == false) { 31 | return &_link_db[i]; 32 | } 33 | } 34 | 35 | return NULL; 36 | } 37 | 38 | /** 39 | * @TODO 40 | */ 41 | L2capLink *L2capLinkForHandle(L2capLinkHandle handle) 42 | { 43 | for (size_t i = 0; i < L2CAP_MAX_NUM_LINKS; i++) { 44 | if ((_link_db[i].is_init == false) 45 | && (_link_db[i].handle == handle)) { 46 | return &_link_db[i]; 47 | } 48 | } 49 | 50 | return NULL; 51 | } 52 | 53 | L2capLink *L2capLinkInitNew(L2capLinkHandle handle) 54 | { 55 | L2capLink *const link = _GetFreeLink(); 56 | 57 | if (link != NULL) { 58 | _ResetLink(link); 59 | link->is_init = true; 60 | link->handle = handle; 61 | } 62 | 63 | return link; 64 | } 65 | -------------------------------------------------------------------------------- /stack/toy_ble.c: -------------------------------------------------------------------------------- 1 | #include "toy_ble.h" 2 | 3 | #include "hci.h" 4 | #include "hci_transport.h" 5 | 6 | // @TODO real logging 7 | #include 8 | #include 9 | 10 | 11 | int TBLE_Init(void) 12 | { 13 | int ret = 0; 14 | 15 | ret = TBLE_Dep_HciTransportInit(); 16 | if (ret != 0) { 17 | printf("ERROR: TBLE_Dep_HciTransportInit failed\n"); 18 | return ret; 19 | } 20 | 21 | printf("Transport init success\n"); 22 | 23 | TBLE_Dep_HciTransportRegisterPacketReceivedCallback(HciHandleDataReceived); 24 | 25 | return 0; 26 | } 27 | --------------------------------------------------------------------------------