Merge pull request #413 from kasjer/kasjer/ble-over-usb
Bluetooth HCI transport over USB
This commit is contained in:
		
							
								
								
									
										252
									
								
								src/class/bth/bth_device.c
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										252
									
								
								src/class/bth/bth_device.c
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,252 @@ | |||||||
|  | /* | ||||||
|  |  * The MIT License (MIT) | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2020 Jerzy Kasenberg | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  * | ||||||
|  |  * This file is part of the TinyUSB stack. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #include "tusb_option.h" | ||||||
|  |  | ||||||
|  | #if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_BTH) | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // INCLUDE | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | #include "bth_device.h" | ||||||
|  | #include <common/tusb_types.h> | ||||||
|  | #include <device/usbd_pvt.h> | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // MACRO CONSTANT TYPEDEF | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | typedef struct | ||||||
|  | { | ||||||
|  |   uint8_t itf_num; | ||||||
|  |   uint8_t ep_ev; | ||||||
|  |   uint8_t ep_acl_in; | ||||||
|  |   uint8_t ep_acl_out; | ||||||
|  |   uint8_t ep_voice[2];  // Not used yet | ||||||
|  |   uint8_t ep_voice_size[2][CFG_TUD_BTH_ISO_ALT_COUNT]; | ||||||
|  |  | ||||||
|  |   // Endpoint Transfer buffer | ||||||
|  |   CFG_TUSB_MEM_ALIGN bt_hci_cmd_t hci_cmd; | ||||||
|  |   CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_BTH_DATA_EPSIZE]; | ||||||
|  |  | ||||||
|  | } btd_interface_t; | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // INTERNAL OBJECT & FUNCTION DECLARATION | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | CFG_TUSB_MEM_SECTION btd_interface_t _btd_itf; | ||||||
|  |  | ||||||
|  | static bool bt_tx_data(uint8_t ep, void *data, uint16_t len) | ||||||
|  | { | ||||||
|  |   // skip if previous transfer not complete | ||||||
|  |   TU_VERIFY(!usbd_edpt_busy(TUD_OPT_RHPORT, ep)); | ||||||
|  |  | ||||||
|  |   TU_ASSERT(usbd_edpt_xfer(TUD_OPT_RHPORT, ep, data, len)); | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // READ API | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  |  | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // WRITE API | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  |  | ||||||
|  | bool tud_bt_event_send(void *event, uint16_t event_len) | ||||||
|  | { | ||||||
|  |   return bt_tx_data(_btd_itf.ep_ev, event, event_len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool tud_bt_acl_data_send(void *event, uint16_t event_len) | ||||||
|  | { | ||||||
|  |   return bt_tx_data(_btd_itf.ep_acl_in, event, event_len); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // USBD Driver API | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | void btd_init(void) | ||||||
|  | { | ||||||
|  |   tu_memclr(&_btd_itf, sizeof(_btd_itf)); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | void btd_reset(uint8_t rhport) | ||||||
|  | { | ||||||
|  |   (void)rhport; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | uint16_t btd_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len) | ||||||
|  | { | ||||||
|  |   tusb_desc_endpoint_t const *desc_ep; | ||||||
|  |   uint16_t drv_len = 0; | ||||||
|  |   // Size of single alternative of ISO interface | ||||||
|  |   const uint16_t iso_alt_itf_size = sizeof(tusb_desc_interface_t) + 2 * sizeof(tusb_desc_endpoint_t); | ||||||
|  |   // Size of hci interface | ||||||
|  |   const uint16_t hci_itf_size = sizeof(tusb_desc_interface_t) + 3 * sizeof(tusb_desc_endpoint_t); | ||||||
|  |   // Ensure this is BT Primary Controller | ||||||
|  |   TU_VERIFY(TUSB_CLASS_WIRELESS_CONTROLLER == itf_desc->bInterfaceClass && | ||||||
|  |             TUD_BT_APP_SUBCLASS == itf_desc->bInterfaceSubClass && | ||||||
|  |             TUD_BT_PROTOCOL_PRIMARY_CONTROLLER == itf_desc->bInterfaceProtocol, 0); | ||||||
|  |  | ||||||
|  |   // Distinguish interface by number of endpoints, as both interface have same class, subclass and protocol | ||||||
|  |   if (itf_desc->bNumEndpoints == 3 && max_len >= hci_itf_size) | ||||||
|  |   { | ||||||
|  |     _btd_itf.itf_num = itf_desc->bInterfaceNumber; | ||||||
|  |  | ||||||
|  |     desc_ep = (tusb_desc_endpoint_t const *) tu_desc_next(itf_desc); | ||||||
|  |  | ||||||
|  |     TU_ASSERT(TUSB_DESC_ENDPOINT == desc_ep->bDescriptorType && TUSB_XFER_INTERRUPT == desc_ep->bmAttributes.xfer, 0); | ||||||
|  |     TU_ASSERT(dcd_edpt_open(rhport, desc_ep), 0); | ||||||
|  |     _btd_itf.ep_ev = desc_ep->bEndpointAddress; | ||||||
|  |  | ||||||
|  |     // Open endpoint pair | ||||||
|  |     TU_ASSERT(usbd_open_edpt_pair(rhport, tu_desc_next(desc_ep), 2, TUSB_XFER_BULK, &_btd_itf.ep_acl_out, | ||||||
|  |                                   &_btd_itf.ep_acl_in), 0); | ||||||
|  |  | ||||||
|  |     // Prepare for incoming data from host | ||||||
|  |     TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE), 0); | ||||||
|  |  | ||||||
|  |     drv_len = hci_itf_size; | ||||||
|  |   } | ||||||
|  |   else if (itf_desc->bNumEndpoints == 2 && max_len >= iso_alt_itf_size) | ||||||
|  |   { | ||||||
|  |     uint8_t dir; | ||||||
|  |  | ||||||
|  |     desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc); | ||||||
|  |     TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0); | ||||||
|  |     TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0); | ||||||
|  |     dir = tu_edpt_dir(desc_ep->bEndpointAddress); | ||||||
|  |     _btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress; | ||||||
|  |     // Store endpoint size for alternative | ||||||
|  |     _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size; | ||||||
|  |  | ||||||
|  |     desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep); | ||||||
|  |     TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT, 0); | ||||||
|  |     dir = tu_edpt_dir(desc_ep->bEndpointAddress); | ||||||
|  |     _btd_itf.ep_voice[dir] = desc_ep->bEndpointAddress; | ||||||
|  |     // Store endpoint size for alternative | ||||||
|  |     _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size; | ||||||
|  |     drv_len += iso_alt_itf_size; | ||||||
|  |  | ||||||
|  |     for (int i = 1; i < CFG_TUD_BTH_ISO_ALT_COUNT && drv_len + iso_alt_itf_size <= max_len; ++i) { | ||||||
|  |       // Make sure rest of alternatives matches | ||||||
|  |       itf_desc = (tusb_desc_interface_t const *)tu_desc_next(desc_ep); | ||||||
|  |       if (itf_desc->bDescriptorType != TUSB_DESC_INTERFACE || | ||||||
|  |           TUSB_CLASS_WIRELESS_CONTROLLER != itf_desc->bInterfaceClass || | ||||||
|  |           TUD_BT_APP_SUBCLASS != itf_desc->bInterfaceSubClass || | ||||||
|  |           TUD_BT_PROTOCOL_PRIMARY_CONTROLLER != itf_desc->bInterfaceProtocol) | ||||||
|  |       { | ||||||
|  |         // Not an Iso interface instance | ||||||
|  |         break; | ||||||
|  |       } | ||||||
|  |       TU_ASSERT(itf_desc->bAlternateSetting < CFG_TUD_BTH_ISO_ALT_COUNT, 0); | ||||||
|  |  | ||||||
|  |       desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(itf_desc); | ||||||
|  |       dir = tu_edpt_dir(desc_ep->bEndpointAddress); | ||||||
|  |       // Verify that alternative endpoint are same as first ones | ||||||
|  |       TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT && | ||||||
|  |                 _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0); | ||||||
|  |       _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size; | ||||||
|  |  | ||||||
|  |       desc_ep = (tusb_desc_endpoint_t const *)tu_desc_next(desc_ep); | ||||||
|  |       dir = tu_edpt_dir(desc_ep->bEndpointAddress); | ||||||
|  |       // Verify that alternative endpoint are same as first ones | ||||||
|  |       TU_ASSERT(desc_ep->bDescriptorType == TUSB_DESC_ENDPOINT && | ||||||
|  |                 _btd_itf.ep_voice[dir] == desc_ep->bEndpointAddress, 0); | ||||||
|  |       _btd_itf.ep_voice_size[dir][itf_desc->bAlternateSetting] = (uint8_t)desc_ep->wMaxPacketSize.size; | ||||||
|  |       drv_len += iso_alt_itf_size; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return drv_len; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool btd_control_complete(uint8_t rhport, tusb_control_request_t const *request) | ||||||
|  | { | ||||||
|  |   (void)rhport; | ||||||
|  |  | ||||||
|  |   // Handle class request only | ||||||
|  |   TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS); | ||||||
|  |  | ||||||
|  |   if (tud_bt_hci_cmd_cb) tud_bt_hci_cmd_cb(&_btd_itf.hci_cmd, request->wLength); | ||||||
|  |  | ||||||
|  |   return true; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool btd_control_request(uint8_t rhport, tusb_control_request_t const *request) | ||||||
|  | { | ||||||
|  |   (void)rhport; | ||||||
|  |  | ||||||
|  |   if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS && | ||||||
|  |       request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_DEVICE) | ||||||
|  |   { | ||||||
|  |     // HCI command packet addressing for single function Primary Controllers | ||||||
|  |     TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == 0); | ||||||
|  |   } | ||||||
|  |   else if (request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE) | ||||||
|  |   { | ||||||
|  |     if (request->bRequest == TUSB_REQ_SET_INTERFACE && _btd_itf.itf_num + 1 == request->wIndex) | ||||||
|  |     { | ||||||
|  |       // TODO: Set interface it would involve changing size of endpoint size | ||||||
|  |     } | ||||||
|  |     else | ||||||
|  |     { | ||||||
|  |       // HCI command packet for Primary Controller function in a composite device | ||||||
|  |       TU_VERIFY(request->bRequest == 0 && request->wValue == 0 && request->wIndex == _btd_itf.itf_num); | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  |   else return false; | ||||||
|  |  | ||||||
|  |   return tud_control_xfer(rhport, request, &_btd_itf.hci_cmd, request->wLength); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | bool btd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) | ||||||
|  | { | ||||||
|  |   (void)result; | ||||||
|  |  | ||||||
|  |   // received new data from host | ||||||
|  |   if (ep_addr == _btd_itf.ep_acl_out) | ||||||
|  |   { | ||||||
|  |     if (tud_bt_acl_data_received_cb) tud_bt_acl_data_received_cb(_btd_itf.epout_buf, xferred_bytes); | ||||||
|  |  | ||||||
|  |     // prepare for next data | ||||||
|  |     TU_ASSERT(usbd_edpt_xfer(rhport, _btd_itf.ep_acl_out, _btd_itf.epout_buf, CFG_TUD_BTH_DATA_EPSIZE)); | ||||||
|  |   } | ||||||
|  |   else if (ep_addr == _btd_itf.ep_ev) | ||||||
|  |   { | ||||||
|  |     if (tud_bt_event_sent_cb) tud_bt_event_sent_cb((uint16_t)xferred_bytes); | ||||||
|  |   } | ||||||
|  |   else if (ep_addr == _btd_itf.ep_acl_in) | ||||||
|  |   { | ||||||
|  |     if (tud_bt_acl_data_sent_cb) tud_bt_acl_data_sent_cb((uint16_t)xferred_bytes); | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   return TUSB_ERROR_NONE; | ||||||
|  | } | ||||||
|  |  | ||||||
|  | #endif | ||||||
							
								
								
									
										110
									
								
								src/class/bth/bth_device.h
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										110
									
								
								src/class/bth/bth_device.h
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,110 @@ | |||||||
|  | /*  | ||||||
|  |  * The MIT License (MIT) | ||||||
|  |  * | ||||||
|  |  * Copyright (c) 2020 Jerzy Kasenberg | ||||||
|  |  * | ||||||
|  |  * Permission is hereby granted, free of charge, to any person obtaining a copy | ||||||
|  |  * of this software and associated documentation files (the "Software"), to deal | ||||||
|  |  * in the Software without restriction, including without limitation the rights | ||||||
|  |  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||||||
|  |  * copies of the Software, and to permit persons to whom the Software is | ||||||
|  |  * furnished to do so, subject to the following conditions: | ||||||
|  |  * | ||||||
|  |  * The above copyright notice and this permission notice shall be included in | ||||||
|  |  * all copies or substantial portions of the Software. | ||||||
|  |  * | ||||||
|  |  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||||||
|  |  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||||||
|  |  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||||||
|  |  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||||||
|  |  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||||||
|  |  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||||||
|  |  * THE SOFTWARE. | ||||||
|  |  * | ||||||
|  |  * This file is part of the TinyUSB stack. | ||||||
|  |  */ | ||||||
|  |  | ||||||
|  | #ifndef _TUSB_BTH_DEVICE_H_ | ||||||
|  | #define _TUSB_BTH_DEVICE_H_ | ||||||
|  |  | ||||||
|  | #include <common/tusb_common.h> | ||||||
|  | #include <device/usbd.h> | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // Class Driver Configuration | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | #ifndef CFG_TUD_BTH_EVENT_EPSIZE | ||||||
|  | #define CFG_TUD_BTH_EVENT_EPSIZE     16 | ||||||
|  | #endif | ||||||
|  | #ifndef CFG_TUD_BTH_DATA_EPSIZE | ||||||
|  | #define CFG_TUD_BTH_DATA_EPSIZE      64 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | typedef struct TU_ATTR_PACKED | ||||||
|  | { | ||||||
|  |   uint16_t op_code; | ||||||
|  |   uint8_t param_length; | ||||||
|  |   uint8_t param[255]; | ||||||
|  | } bt_hci_cmd_t; | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  |  extern "C" { | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // Application Callback API (weak is optional) | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  |  | ||||||
|  | // Invoked when HCI command was received over USB from Bluetooth host. | ||||||
|  | // Detailed format is described in Bluetooth core specification Vol 2, | ||||||
|  | // Part E, 5.4.1. | ||||||
|  | // Length of the command is from 3 bytes (2 bytes for OpCode, | ||||||
|  | // 1 byte for parameter total length) to 258. | ||||||
|  | TU_ATTR_WEAK void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len); | ||||||
|  |  | ||||||
|  | // Invoked when ACL data was received over USB from Bluetooth host. | ||||||
|  | // Detailed format is described in Bluetooth core specification Vol 2, | ||||||
|  | // Part E, 5.4.2. | ||||||
|  | // Length is from 4 bytes, (12 bits for Handle, 4 bits for flags | ||||||
|  | // and 16 bits for data total length) to endpoint size. | ||||||
|  | TU_ATTR_WEAK void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len); | ||||||
|  |  | ||||||
|  | // Called when event sent with tud_bt_event_send() was delivered to BT stack. | ||||||
|  | // Controller can release/reuse buffer with Event packet at this point. | ||||||
|  | TU_ATTR_WEAK void tud_bt_event_sent_cb(uint16_t sent_bytes); | ||||||
|  |  | ||||||
|  | // Called when ACL data that was sent with tud_bt_acl_data_send() | ||||||
|  | // was delivered to BT stack. | ||||||
|  | // Controller can release/reuse buffer with ACL packet at this point. | ||||||
|  | TU_ATTR_WEAK void tud_bt_acl_data_sent_cb(uint16_t sent_bytes); | ||||||
|  |  | ||||||
|  | // Bluetooth controller calls this function when it wants to send even packet | ||||||
|  | // as described in Bluetooth core specification Vol 2, Part E, 5.4.4. | ||||||
|  | // Event has at least 2 bytes, first is Event code second contains parameter | ||||||
|  | // total length. Controller can release/reuse event memory after | ||||||
|  | // tud_bt_event_sent_cb() is called. | ||||||
|  | bool tud_bt_event_send(void *event, uint16_t event_len); | ||||||
|  |  | ||||||
|  | // Bluetooth controller calls this to send ACL data packet | ||||||
|  | // as described in Bluetooth core specification Vol 2, Part E, 5.4.2 | ||||||
|  | // Minimum length is 4 bytes, (12 bits for Handle, 4 bits for flags | ||||||
|  | // and 16 bits for data total length). Upper limit is not limited | ||||||
|  | // to endpoint size since buffer is allocate by controller | ||||||
|  | // and must not be reused till tud_bt_acl_data_sent_cb() is called. | ||||||
|  | bool tud_bt_acl_data_send(void *acl_data, uint16_t data_len); | ||||||
|  |  | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | // Internal Class Driver API | ||||||
|  | //--------------------------------------------------------------------+ | ||||||
|  | void     btd_init             (void); | ||||||
|  | void     btd_reset            (uint8_t rhport); | ||||||
|  | uint16_t btd_open             (uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len); | ||||||
|  | bool     btd_control_request  (uint8_t rhport, tusb_control_request_t const * request); | ||||||
|  | bool     btd_control_complete (uint8_t rhport, tusb_control_request_t const * request); | ||||||
|  | bool     btd_xfer_cb          (uint8_t rhport, uint8_t edpt_addr, xfer_result_t result, uint32_t xferred_bytes); | ||||||
|  |  | ||||||
|  | #ifdef __cplusplus | ||||||
|  |  } | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | #endif /* _TUSB_BTH_DEVICE_H_ */ | ||||||
| @@ -199,6 +199,19 @@ static usbd_class_driver_t const _usbd_driver[] = | |||||||
|       .sof              = NULL, |       .sof              = NULL, | ||||||
|   }, |   }, | ||||||
|   #endif |   #endif | ||||||
|  |  | ||||||
|  |   #if CFG_TUD_BTH | ||||||
|  |   { | ||||||
|  |       DRIVER_NAME("BTH") | ||||||
|  |       .init             = btd_init, | ||||||
|  |       .reset            = btd_reset, | ||||||
|  |       .open             = btd_open, | ||||||
|  |       .control_request  = btd_control_request, | ||||||
|  |       .control_complete = btd_control_complete, | ||||||
|  |       .xfer_cb          = btd_xfer_cb, | ||||||
|  |       .sof              = NULL | ||||||
|  |   }, | ||||||
|  |   #endif | ||||||
| }; | }; | ||||||
|  |  | ||||||
| enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; | enum { USBD_CLASS_DRIVER_COUNT = TU_ARRAY_SIZE(_usbd_driver) }; | ||||||
| @@ -507,6 +520,18 @@ static bool process_control_request(uint8_t rhport, tusb_control_request_t const | |||||||
|   { |   { | ||||||
|     //------------- Device Requests e.g in enumeration -------------// |     //------------- Device Requests e.g in enumeration -------------// | ||||||
|     case TUSB_REQ_RCPT_DEVICE: |     case TUSB_REQ_RCPT_DEVICE: | ||||||
|  |       if ( TUSB_REQ_TYPE_CLASS == p_request->bmRequestType_bit.type ) | ||||||
|  |       { | ||||||
|  |           uint8_t const itf = tu_u16_low(p_request->wIndex); | ||||||
|  |           TU_VERIFY(itf < TU_ARRAY_SIZE(_usbd_dev.itf2drv)); | ||||||
|  |  | ||||||
|  |           uint8_t const drvid = _usbd_dev.itf2drv[itf]; | ||||||
|  |           TU_VERIFY(drvid < USBD_CLASS_DRIVER_COUNT); | ||||||
|  |  | ||||||
|  |           // forward to class driver: "non-STD request to Interface" | ||||||
|  |           TU_VERIFY(invoke_class_control(rhport, drvid, p_request)); | ||||||
|  |           return true; | ||||||
|  |       } | ||||||
|       if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) |       if ( TUSB_REQ_TYPE_STANDARD != p_request->bmRequestType_bit.type ) | ||||||
|       { |       { | ||||||
|         // Non standard request is not supported |         // Non standard request is not supported | ||||||
|   | |||||||
| @@ -448,6 +448,60 @@ TU_ATTR_WEAK bool tud_vendor_control_complete_cb(uint8_t rhport, tusb_control_re | |||||||
|   /* Endpoint Out */\ |   /* Endpoint Out */\ | ||||||
|   7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 |   7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0 | ||||||
|  |  | ||||||
|  | //------------- BT Radio -------------// | ||||||
|  | #define TUD_BT_APP_CLASS                    (TUSB_CLASS_WIRELESS_CONTROLLER) | ||||||
|  | #define TUD_BT_APP_SUBCLASS                 0x01 | ||||||
|  | #define TUD_BT_PROTOCOL_PRIMARY_CONTROLLER  0x01 | ||||||
|  | #define TUD_BT_PROTOCOL_AMP_CONTROLLER      0x02 | ||||||
|  |  | ||||||
|  | #ifndef CFG_TUD_BTH_ISO_ALT_COUNT | ||||||
|  | #define CFG_TUD_BTH_ISO_ALT_COUNT 0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
|  | // Length of template descriptor: 30 bytes + number of ISO alternatives * 23 | ||||||
|  | #define TUD_BTH_DESC_LEN (9 + 7 + 7 + 7 + (CFG_TUD_BTH_ISO_ALT_COUNT) * (9 + 7 + 7)) | ||||||
|  |  | ||||||
|  | /* Primary Interface */ | ||||||
|  | #define TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \ | ||||||
|  |   9, TUSB_DESC_INTERFACE, _itfnum, _stridx, 3, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \ | ||||||
|  |   /* Endpoint In for events */ \ | ||||||
|  |   7, TUSB_DESC_ENDPOINT, _ep_evt, TUSB_XFER_INTERRUPT, U16_TO_U8S_LE(_ep_evt_size), _ep_evt_interval, \ | ||||||
|  |   /* Endpoint In for ACL data */ \ | ||||||
|  |   7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1, \ | ||||||
|  |   /* Endpoint Out for ACL data */ \ | ||||||
|  |   7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_BULK, U16_TO_U8S_LE(_ep_size), 1 | ||||||
|  |  | ||||||
|  | #define TUD_BTH_ISO_ITF(_itfnum, _alt, _ep_in, _ep_out, _n) ,\ | ||||||
|  |   /* Interface with 2 endpoints */ \ | ||||||
|  |   9, TUSB_DESC_INTERFACE, _itfnum, _alt, 2, TUD_BT_APP_CLASS, TUD_BT_APP_SUBCLASS, TUD_BT_PROTOCOL_PRIMARY_CONTROLLER, 0, \ | ||||||
|  |   /* Isochronous endpoints */ \ | ||||||
|  |   7, TUSB_DESC_ENDPOINT, _ep_in, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1, \ | ||||||
|  |   7, TUSB_DESC_ENDPOINT, _ep_out, TUSB_XFER_ISOCHRONOUS, U16_TO_U8S_LE(_n), 1 | ||||||
|  |  | ||||||
|  | #define _FIRST(a, ...) a | ||||||
|  | #define _REST(a, ...) __VA_ARGS__ | ||||||
|  |  | ||||||
|  | #define TUD_BTH_ISO_ITF_0(_itfnum, ...) | ||||||
|  | #define TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 1, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) | ||||||
|  | #define TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 2, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ | ||||||
|  | 	TUD_BTH_ISO_ITF_1(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) | ||||||
|  | #define TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 3, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ | ||||||
|  | 	TUD_BTH_ISO_ITF_2(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) | ||||||
|  | #define TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 4, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ | ||||||
|  | 	TUD_BTH_ISO_ITF_3(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) | ||||||
|  | #define TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 5, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ | ||||||
|  | 	TUD_BTH_ISO_ITF_4(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) | ||||||
|  | #define TUD_BTH_ISO_ITF_6(_itfnum, _ep_in, _ep_out, ...) TUD_BTH_ISO_ITF(_itfnum, (CFG_TUD_BTH_ISO_ALT_COUNT) - 6, _ep_in, _ep_out, _FIRST(__VA_ARGS__)) \ | ||||||
|  | 	TUD_BTH_ISO_ITF_5(_itfnum, _ep_in, _ep_out, _REST(__VA_ARGS__)) | ||||||
|  |  | ||||||
|  | #define TUD_BTH_ISO_ITFS(_itfnum, _ep_in, _ep_out, ...) \ | ||||||
|  | 	TU_XSTRCAT(TUD_BTH_ISO_ITF_, CFG_TUD_BTH_ISO_ALT_COUNT)(_itfnum, _ep_in, _ep_out, __VA_ARGS__) | ||||||
|  |  | ||||||
|  | // BT Primary controller descriptor | ||||||
|  | // Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes | ||||||
|  | #define TUD_BTH_DESCRIPTOR(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size,...) \ | ||||||
|  |   TUD_BTH_PRI_ITF(_itfnum, _stridx, _ep_evt, _ep_evt_size, _ep_evt_interval, _ep_in, _ep_out, _ep_size) \ | ||||||
|  |   TUD_BTH_ISO_ITFS(_itfnum + 1, _ep_in + 1, _ep_out + 1, __VA_ARGS__) | ||||||
|  |  | ||||||
| #ifdef __cplusplus | #ifdef __cplusplus | ||||||
|  } |  } | ||||||
|   | |||||||
| @@ -95,6 +95,10 @@ | |||||||
|   #if CFG_TUD_NET |   #if CFG_TUD_NET | ||||||
|     #include "class/net/net_device.h" |     #include "class/net/net_device.h" | ||||||
|   #endif |   #endif | ||||||
|  |  | ||||||
|  |   #if CFG_TUD_BTH | ||||||
|  |     #include "class/bth/bth_device.h" | ||||||
|  |   #endif | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
| @@ -219,6 +219,10 @@ | |||||||
|   #define CFG_TUD_NET             0 |   #define CFG_TUD_NET             0 | ||||||
| #endif | #endif | ||||||
|  |  | ||||||
|  | #ifndef CFG_TUD_BTH | ||||||
|  |   #define CFG_TUD_BTH             0 | ||||||
|  | #endif | ||||||
|  |  | ||||||
| //-------------------------------------------------------------------- | //-------------------------------------------------------------------- | ||||||
| // HOST OPTIONS | // HOST OPTIONS | ||||||
| //-------------------------------------------------------------------- | //-------------------------------------------------------------------- | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Ha Thach
					Ha Thach