xref: /btstack/port/stm32-f4discovery-usb/port/usbh_bluetooth.c (revision 2fca4dad957cd7b88f4657ed51e89c12615dda72)
1 /*
2  * Copyright (C) 2020 BlueKitchen GmbH
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  * 3. Neither the name of the copyright holders nor the names of
14  *    contributors may be used to endorse or promote products derived
15  *    from this software without specific prior written permission.
16  * 4. Any redistribution, use, or modification is done solely for
17  *    personal benefit and not for any commercial purpose or for
18  *    monetary gain.
19  *
20  * THIS SOFTWARE IS PROVIDED BY BLUEKITCHEN GMBH AND CONTRIBUTORS
21  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
23  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BLUEKITCHEN
24  * GMBH OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
25  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
26  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
27  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
28  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
30  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  *
33  * Please inquire about commercial licensing options at
34  * [email protected]
35  *
36  */
37 
38 #define BTSTACK_FILE__ "usbh_bluetooth.c"
39 
40 #include "usbh_bluetooth.h"
41 #include "btstack_debug.h"
42 #include "hci.h"
43 #include "btstack_util.h"
44 #include "bluetooth.h"
45 
46 typedef struct {
47     uint8_t acl_in_ep;
48     uint8_t acl_in_pipe;
49     uint16_t acl_in_len;
50     uint32_t acl_in_frame;
51     uint8_t acl_out_ep;
52     uint8_t acl_out_pipe;
53     uint16_t acl_out_len;
54     uint8_t event_in_ep;
55     uint8_t event_in_pipe;
56     uint16_t event_in_len;
57     uint32_t event_in_frame;
58 } USB_Bluetooth_t;
59 
60 static enum {
61     USBH_OUT_OFF,
62     USBH_OUT_IDLE,
63     USBH_OUT_CMD,
64     USBH_OUT_ACL_SEND,
65     USBH_OUT_ACL_POLL,
66 } usbh_out_state;
67 
68 static enum {
69     USBH_IN_OFF,
70     USBH_IN_SUBMIT_REQUEST,
71     USBH_IN_POLL,
72 } usbh_in_state;
73 
74 // higher-layer callbacks
75 static void (*usbh_packet_sent)(void);
76 static void (*usbh_packet_received)(uint8_t packet_type, uint8_t * packet, uint16_t size);
77 
78 // class state
79 static USB_Bluetooth_t usb_bluetooth;
80 
81 // outgoing
82 static const uint8_t * cmd_packet;
83 static uint16_t        cmd_len;
84 
85 static const uint8_t * acl_packet;
86 static uint16_t        acl_len;
87 
88 // incoming
89 static uint16_t hci_event_offset;
90 static uint8_t hci_event[258];
91 
92 static uint16_t hci_acl_in_offset;
93 static uint8_t  hci_acl_in_buffer[HCI_INCOMING_PRE_BUFFER_SIZE + HCI_ACL_BUFFER_SIZE];
94 static uint8_t  * hci_acl_in_packet = &hci_acl_in_buffer[HCI_INCOMING_PRE_BUFFER_SIZE];
95 
96 
usbh_bluetooth_start_acl_in_transfer(USBH_HandleTypeDef * phost,USB_Bluetooth_t * usb)97 USBH_StatusTypeDef usbh_bluetooth_start_acl_in_transfer(USBH_HandleTypeDef *phost, USB_Bluetooth_t * usb){
98     uint16_t acl_in_transfer_size = btstack_min(usb->acl_in_len, HCI_ACL_BUFFER_SIZE - hci_acl_in_offset);
99 	usb->acl_in_frame = phost->Timer;
100     return USBH_BulkReceiveData(phost, &hci_acl_in_packet[hci_acl_in_offset], acl_in_transfer_size, usb->acl_in_pipe);
101 }
102 
USBH_Bluetooth_InterfaceInit(USBH_HandleTypeDef * phost)103 USBH_StatusTypeDef USBH_Bluetooth_InterfaceInit(USBH_HandleTypeDef *phost){
104     log_info("USBH_Bluetooth_InterfaceInit");
105 
106     // dump everything
107     uint8_t interface_index = 0;
108     USBH_InterfaceDescTypeDef * interface = &phost->device.CfgDesc.Itf_Desc[interface_index];
109     uint8_t num_endpoints = interface->bNumEndpoints;
110     uint8_t ep_index;
111     int16_t acl_in   = -1;
112     int16_t acl_out  = -1;
113     int16_t event_in = -1;
114     for (ep_index=0;ep_index<num_endpoints;ep_index++){
115         USBH_EpDescTypeDef * ep_desc = &interface->Ep_Desc[ep_index];
116         printf("Interface %u, endpoint #%u: address 0x%02x, attributes 0x%02x, packet size %u, poll %u\n",
117                interface_index, ep_index, ep_desc->bEndpointAddress, ep_desc->bmAttributes, ep_desc->wMaxPacketSize, ep_desc->bInterval);
118         // type interrupt, direction incoming
119         if  (((ep_desc->bEndpointAddress & USB_EP_DIR_MSK) == USB_EP_DIR_MSK) && (ep_desc->bmAttributes == USB_EP_TYPE_INTR)){
120             event_in = ep_index;
121             puts("-> HCI Event");
122         }
123         // type bulk, direction incoming
124         if  (((ep_desc->bEndpointAddress & USB_EP_DIR_MSK) == USB_EP_DIR_MSK) && (ep_desc->bmAttributes == USB_EP_TYPE_BULK)){
125             acl_in = ep_index;
126             puts("-> HCI ACL IN");
127         }
128         // type bulk, direction incoming
129         if  (((ep_desc->bEndpointAddress & USB_EP_DIR_MSK) == 0) && (ep_desc->bmAttributes == USB_EP_TYPE_BULK)){
130             acl_out = ep_index;
131             puts("-> HCI ACL OUT");
132         }
133     }
134 
135     // all found
136     if ((acl_in < 0) && (acl_out < 0) && (event_in < 0)) {
137         log_info("Could not find all endpoints");
138         return USBH_FAIL;
139     }
140 
141     // setup
142     memset(&usb_bluetooth, 0, sizeof(USB_Bluetooth_t));
143     phost->pActiveClass->pData = (void*) &usb_bluetooth;
144 
145     // Command
146     usbh_out_state = USBH_OUT_OFF;
147 
148     // Event In
149     USB_Bluetooth_t * usb = &usb_bluetooth;
150     usb->event_in_ep =   interface->Ep_Desc[event_in].bEndpointAddress;
151     usb->event_in_len =  interface->Ep_Desc[event_in].wMaxPacketSize;
152     usb->event_in_pipe = USBH_AllocPipe(phost, usb->event_in_ep);
153     usbh_in_state = USBH_IN_OFF;
154 
155     /* Open pipe for IN endpoint */
156     USBH_OpenPipe(phost, usb->event_in_pipe, usb->event_in_ep, phost->device.address,
157                   phost->device.speed, USB_EP_TYPE_INTR, interface->Ep_Desc[event_in].wMaxPacketSize);
158 
159     USBH_LL_SetToggle(phost, usb->event_in_ep, 0U);
160 
161     // ACL In
162     usb->acl_in_ep  =  interface->Ep_Desc[acl_in].bEndpointAddress;
163     usb->acl_in_len =  interface->Ep_Desc[acl_in].wMaxPacketSize;
164     usb->acl_in_pipe = USBH_AllocPipe(phost, usb->acl_in_ep);
165     USBH_OpenPipe(phost, usb->acl_in_pipe, usb->acl_in_ep, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, usb->acl_in_len);
166     USBH_LL_SetToggle(phost, usb->acl_in_pipe, 0U);
167     hci_acl_in_offset = 0;
168     usbh_bluetooth_start_acl_in_transfer(phost, usb);
169 
170     // ACL Out
171     usb->acl_out_ep  =  interface->Ep_Desc[acl_out].bEndpointAddress;
172     usb->acl_out_len =  interface->Ep_Desc[acl_out].wMaxPacketSize;
173     usb->acl_out_pipe = USBH_AllocPipe(phost, usb->acl_out_ep);
174     USBH_OpenPipe(phost, usb->acl_out_pipe, usb->acl_out_ep, phost->device.address, phost->device.speed, USB_EP_TYPE_BULK, usb->acl_out_len);
175     USBH_LL_SetToggle(phost, usb->acl_out_pipe, 0U);
176 
177     return USBH_OK;
178 }
179 
USBH_Bluetooth_InterfaceDeInit(USBH_HandleTypeDef * phost)180 USBH_StatusTypeDef USBH_Bluetooth_InterfaceDeInit(USBH_HandleTypeDef *phost){
181     log_info("USBH_Bluetooth_InterfaceDeInit");
182     usbh_out_state = USBH_OUT_OFF;
183     usbh_in_state = USBH_IN_OFF;
184     return USBH_OK;
185 }
186 
USBH_Bluetooth_ClassRequest(USBH_HandleTypeDef * phost)187 USBH_StatusTypeDef USBH_Bluetooth_ClassRequest(USBH_HandleTypeDef *phost){
188     // ready!
189     usbh_out_state = USBH_OUT_IDLE;
190     usbh_in_state = USBH_IN_SUBMIT_REQUEST;
191     // notify host stack
192     (*usbh_packet_sent)();
193     return USBH_OK;
194 }
195 
USBH_Bluetooth_Process(USBH_HandleTypeDef * phost)196 USBH_StatusTypeDef USBH_Bluetooth_Process(USBH_HandleTypeDef *phost){
197     USBH_StatusTypeDef status;
198     USBH_URBStateTypeDef urb_state;
199     USB_Bluetooth_t * usb = (USB_Bluetooth_t *) phost->pActiveClass->pData;
200     uint16_t transfer_size;
201     switch (usbh_out_state){
202         case USBH_OUT_CMD:
203             // just send HCI Reset naively
204             phost->Control.setup.b.bmRequestType = USB_H2D | USB_REQ_RECIPIENT_INTERFACE | USB_REQ_TYPE_CLASS;
205             phost->Control.setup.b.bRequest = 0;
206             phost->Control.setup.b.wValue.w = 0;
207             phost->Control.setup.b.wIndex.w = 0U;
208             phost->Control.setup.b.wLength.w = cmd_len;
209             status = USBH_CtlReq(phost, (uint8_t *) cmd_packet, cmd_len);
210             if (status == USBH_OK) {
211                 usbh_out_state = USBH_OUT_IDLE;
212                 // notify host stack
213                 (*usbh_packet_sent)();
214             }
215             break;
216         case USBH_OUT_ACL_SEND:
217             transfer_size = btstack_min(usb->acl_out_len, acl_len);
218             USBH_BulkSendData(phost, (uint8_t *) acl_packet, transfer_size, usb->acl_out_pipe, 1);
219             usbh_out_state = USBH_OUT_ACL_POLL;
220             break;
221         case USBH_OUT_ACL_POLL:
222             urb_state = USBH_LL_GetURBState(phost, usb->acl_out_pipe);
223             switch (urb_state){
224                 case USBH_URB_IDLE:
225                     break;
226                 case USBH_URB_NOTREADY:
227                     usbh_out_state = USBH_OUT_ACL_SEND;
228                     break;
229                 case USBH_URB_DONE:
230                     transfer_size = btstack_min(usb->acl_out_len, acl_len);
231                     acl_len -= transfer_size;
232                     if (acl_len == 0){
233                         usbh_out_state = USBH_OUT_IDLE;
234                         // notify host stack
235                         (*usbh_packet_sent)();
236                     } else {
237                         acl_packet += transfer_size;
238                         usbh_out_state = USBH_OUT_ACL_SEND;
239                     }
240                     break;
241                 default:
242                     log_info("URB State ACL Out: %02x", urb_state);
243                     break;
244             }
245             break;
246         default:
247             break;
248     }
249 
250     uint8_t  event_transfer_size;
251     uint16_t event_size;
252     switch (usbh_in_state){
253         case USBH_IN_SUBMIT_REQUEST:
254             event_transfer_size = btstack_min( usb->event_in_len, sizeof(hci_event) - hci_event_offset);
255             USBH_InterruptReceiveData(phost, &hci_event[hci_event_offset], event_transfer_size, usb->event_in_pipe);
256             usb->event_in_frame = phost->Timer;
257             usbh_in_state = USBH_IN_POLL;
258             break;
259         case USBH_IN_POLL:
260             urb_state = USBH_LL_GetURBState(phost, usb->event_in_pipe);
261             switch (urb_state){
262                 case USBH_URB_IDLE:
263                     break;
264                 case USBH_URB_DONE:
265                     usbh_in_state = USBH_IN_SUBMIT_REQUEST;
266                     event_transfer_size = USBH_LL_GetLastXferSize(phost, usb->event_in_pipe);
267                     hci_event_offset += event_transfer_size;
268                     if (hci_event_offset < 2) break;
269                     event_size = 2 + hci_event[1];
270                     // event complete
271                     if (hci_event_offset >= event_size){
272                         hci_event_offset = 0;
273                         (*usbh_packet_received)(HCI_EVENT_PACKET, hci_event, event_size);
274                     }
275                     break;
276                 default:
277                     log_info("URB State Event: %02x", urb_state);
278                     break;
279             }
280             if ((phost->Timer - usb->event_in_frame) > 2){
281                 usbh_in_state = USBH_IN_SUBMIT_REQUEST;
282             }
283             break;
284         default:
285             break;
286     }
287 
288     // ACL In
289     uint16_t acl_transfer_size;
290     uint16_t acl_size;
291     urb_state = USBH_LL_GetURBState(phost, usb->acl_in_pipe);
292     switch (urb_state){
293         case USBH_URB_IDLE:
294             // If state stays IDLE for longer than a full frame, something went wrong with submitting the request,
295             // just re-submits the request
296             if ((phost->Timer - usb->acl_in_frame) > 2){
297                 status = usbh_bluetooth_start_acl_in_transfer(phost, usb);
298                 btstack_assert(status == USBH_OK);
299             }
300             break;
301         case USBH_URB_NOTREADY:
302             // The original USB Host code re-submits the request when it receives a NAK, resulting in about 80% MCU load
303             // With our patch, NOTREADY is returned, which allows to re-submit the request in the next frame.
304             if (phost->Timer != usb->acl_in_frame){
305                 status = usbh_bluetooth_start_acl_in_transfer(phost, usb);
306                 btstack_assert(status == USBH_OK);
307             }            break;
308         case USBH_URB_DONE:
309             acl_transfer_size = USBH_LL_GetLastXferSize(phost, usb->acl_in_pipe);
310             hci_acl_in_offset += acl_transfer_size;
311             if (hci_acl_in_offset < 4) break;
312             acl_size = 4 + little_endian_read_16(hci_acl_in_packet, 2);
313             // acl complete
314             if (hci_acl_in_offset > acl_size){
315                 printf("Extra HCI EVENT!\n");
316             }
317             if (hci_acl_in_offset >= acl_size){
318                 (*usbh_packet_received)(HCI_ACL_DATA_PACKET, hci_acl_in_packet, acl_size);
319                 hci_acl_in_offset = 0;
320             }
321             usbh_bluetooth_start_acl_in_transfer(phost, usb);
322             break;
323         default:
324             log_info("URB State Event: %02x", urb_state);
325             break;
326     }
327 
328     return USBH_OK;
329 }
330 
USBH_Bluetooth_SOFProcess(USBH_HandleTypeDef * phost)331 USBH_StatusTypeDef USBH_Bluetooth_SOFProcess(USBH_HandleTypeDef *phost){
332     return USBH_OK;
333 }
334 
usbh_bluetooth_set_packet_sent(void (* callback)(void))335 void usbh_bluetooth_set_packet_sent(void (*callback)(void)){
336     usbh_packet_sent = callback;
337 }
338 
339 
usbh_bluetooth_set_packet_received(void (* callback)(uint8_t packet_type,uint8_t * packet,uint16_t size))340 void usbh_bluetooth_set_packet_received(void (*callback)(uint8_t packet_type, uint8_t * packet, uint16_t size)){
341     usbh_packet_received = callback;
342 }
343 
usbh_bluetooth_can_send_now(void)344 bool usbh_bluetooth_can_send_now(void){
345     return usbh_out_state == USBH_OUT_IDLE;;
346 }
347 
usbh_bluetooth_send_cmd(const uint8_t * packet,uint16_t len)348 void usbh_bluetooth_send_cmd(const uint8_t * packet, uint16_t len){
349     btstack_assert(usbh_out_state == USBH_OUT_IDLE);
350     cmd_packet = packet;
351     cmd_len    = len;
352     usbh_out_state = USBH_OUT_CMD;
353 }
354 
usbh_bluetooth_send_acl(const uint8_t * packet,uint16_t len)355 void usbh_bluetooth_send_acl(const uint8_t * packet, uint16_t len){
356     btstack_assert(usbh_out_state == USBH_OUT_IDLE);
357     acl_packet = packet;
358     acl_len    = len;
359     usbh_out_state = USBH_OUT_ACL_SEND;
360 }
361 
362 USBH_ClassTypeDef  Bluetooth_Class = {
363     "Bluetooth",
364     USB_BLUETOOTH_CLASS,
365     USBH_Bluetooth_InterfaceInit,
366     USBH_Bluetooth_InterfaceDeInit,
367     USBH_Bluetooth_ClassRequest,
368     USBH_Bluetooth_Process,
369     USBH_Bluetooth_SOFProcess,
370     NULL,
371 };
372 
373