1 /*
2  * Copyright (c) 2024, sakumisu
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include "usbh_core.h"
7 #include "usbh_bl616.h"
8 
9 #undef USB_DBG_TAG
10 #define USB_DBG_TAG "usbh_bl616"
11 #include "usb_log.h"
12 
13 #define DEV_FORMAT "/dev/wifi/bl616"
14 
15 #define MAC_FMT      "%02X:%02X:%02X:%02X:%02X:%02X"
16 #define ARR_ELE_6(e) (e)[0], (e)[1], (e)[2], (e)[3], (e)[4], (e)[5]
17 
18 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bl616_tx_buffer[2048 + 512];
19 USB_NOCACHE_RAM_SECTION USB_MEM_ALIGNX uint8_t g_bl616_rx_buffer[2048 + 512];
20 
21 static struct usbh_bl616 g_bl616_class;
22 
auth_to_str(uint8_t auth)23 static const char *auth_to_str(uint8_t auth)
24 {
25     const char *table[RNM_WIFI_AUTH_MAX] = {
26         [RNM_WIFI_AUTH_UNKNOWN] = "UNKNOWN",
27         [RNM_WIFI_AUTH_OPEN] = "OPEN",
28         [RNM_WIFI_AUTH_WEP] = "WEP",
29         [RNM_WIFI_AUTH_WPA_PSK] = "WPA-PSK",
30         [RNM_WIFI_AUTH_WPA2_PSK] = "WPA2-PSK",
31         [RNM_WIFI_AUTH_WPA_WPA2_PSK] = "WPA2-PSK/WPA-PSK",
32         [RNM_WIFI_AUTH_WPA_ENTERPRISE] = "WPA-ENT",
33         [RNM_WIFI_AUTH_WPA3_SAE] = "WPA3-SAE",
34         [RNM_WIFI_AUTH_WPA2_PSK_WPA3_SAE] = "WPA2-PSK/WPA3-SAE",
35     };
36     if (auth < RNM_WIFI_AUTH_MAX)
37         return table[auth];
38     else
39         return table[RNM_WIFI_AUTH_UNKNOWN];
40 }
41 
cipher_to_str(uint8_t cipher)42 static const char *cipher_to_str(uint8_t cipher)
43 {
44     const char *table[RNM_WIFI_CIPHER_MAX] = {
45         [RNM_WIFI_CIPHER_UNKNOWN] = "UNKNOWN",
46         [RNM_WIFI_CIPHER_NONE] = "NONE",
47         [RNM_WIFI_CIPHER_WEP] = "WEP",
48         [RNM_WIFI_CIPHER_AES] = "AES",
49         [RNM_WIFI_CIPHER_TKIP] = "TKIP",
50         [RNM_WIFI_CIPHER_TKIP_AES] = "TKIP/AES",
51     };
52     if (cipher < RNM_WIFI_CIPHER_MAX)
53         return table[cipher];
54     else
55         return table[RNM_WIFI_CIPHER_UNKNOWN];
56 }
57 
parse_get_mac_rsp_msg(struct usbh_bl616 * bl616_class,void * buf,int buf_len)58 static int parse_get_mac_rsp_msg(struct usbh_bl616 *bl616_class, void *buf, int buf_len)
59 {
60     usb_data_t *usb_hdr = buf;
61     rnm_mac_addr_ind_msg_t *rsp = buf + sizeof(usb_data_t);
62 
63     if (buf_len != sizeof(usb_data_t) + sizeof(rnm_mac_addr_ind_msg_t)) {
64         return -1;
65     }
66     if (usb_hdr->type != USBWIFI_DATA_TYPE_CMD || usb_hdr->length != sizeof(rnm_mac_addr_ind_msg_t)) {
67         return -1;
68     }
69     if (rsp->hdr.cmd != BFLB_CMD_GET_MAC_ADDR || !(rsp->hdr.flags & RNM_MSG_FLAG_ACK)) {
70         return -1;
71     }
72     memcpy(bl616_class->sta_mac, rsp->sta_mac, 6);
73     memcpy(bl616_class->ap_mac, rsp->ap_mac, 6);
74 
75     return 0;
76 }
77 
usbh_bl616_bulk_in_transfer(struct usbh_bl616 * bl616_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)78 static int usbh_bl616_bulk_in_transfer(struct usbh_bl616 *bl616_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
79 {
80     int ret;
81     struct usbh_urb *urb = &bl616_class->bulkin_urb;
82 
83     usbh_bulk_urb_fill(urb, bl616_class->hport, bl616_class->bulkin, buffer, buflen, timeout, NULL, NULL);
84     ret = usbh_submit_urb(urb);
85     if (ret == 0) {
86         ret = urb->actual_length;
87     }
88     return ret;
89 }
90 
usbh_bl616_bulk_out_transfer(struct usbh_bl616 * bl616_class,uint8_t * buffer,uint32_t buflen,uint32_t timeout)91 static int usbh_bl616_bulk_out_transfer(struct usbh_bl616 *bl616_class, uint8_t *buffer, uint32_t buflen, uint32_t timeout)
92 {
93     int ret;
94     struct usbh_urb *urb = &bl616_class->bulkout_urb;
95 
96     usbh_bulk_urb_fill(urb, bl616_class->hport, bl616_class->bulkout, buffer, buflen, timeout, NULL, NULL);
97     ret = usbh_submit_urb(urb);
98     if (ret == 0) {
99         ret = urb->actual_length;
100     }
101     return ret;
102 }
103 
usbh_bl616_get_wifi_mac(struct usbh_bl616 * bl616_class)104 static int usbh_bl616_get_wifi_mac(struct usbh_bl616 *bl616_class)
105 {
106     int ret;
107     uint32_t msg_len;
108     usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
109     rnm_base_msg_t *rnm_msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t));
110 
111     memset(usb_hdr, 0, sizeof(usb_data_t));
112     memset(rnm_msg, 0, sizeof(rnm_base_msg_t));
113 
114     usb_hdr->type = USBWIFI_DATA_TYPE_CMD;
115     usb_hdr->length = sizeof(rnm_base_msg_t);
116     usb_hdr->payload_offset = sizeof(usb_data_t);
117 
118     rnm_msg->cmd = BFLB_CMD_GET_MAC_ADDR;
119 
120     msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t);
121 
122     ret = usbh_bl616_bulk_out_transfer(bl616_class, g_bl616_tx_buffer, msg_len, 500);
123     if (ret < 0) {
124         return ret;
125     }
126     ret = usbh_bl616_bulk_in_transfer(bl616_class, g_bl616_rx_buffer, sizeof(g_bl616_rx_buffer), 500);
127     if (ret < 0) {
128         return ret;
129     }
130 
131     ret = parse_get_mac_rsp_msg(bl616_class, g_bl616_rx_buffer, ret);
132     return ret;
133 }
134 
usbh_bl616_wifi_open(struct usbh_bl616 * bl616_class)135 static int usbh_bl616_wifi_open(struct usbh_bl616 *bl616_class)
136 {
137     uint32_t msg_len;
138     usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
139     rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t));
140 
141     memset(usb_hdr, 0, sizeof(usb_data_t));
142     memset(msg, 0, sizeof(rnm_base_msg_t));
143 
144     usb_hdr->type = USBWIFI_DATA_TYPE_CMD;
145     usb_hdr->length = sizeof(rnm_base_msg_t);
146     usb_hdr->payload_offset = sizeof(usb_data_t);
147 
148     msg->cmd = BFLB_CMD_HELLO;
149 
150     msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t);
151 
152     return usbh_bl616_bulk_out_transfer(bl616_class, g_bl616_tx_buffer, msg_len, 500);
153 }
154 
usbh_bl616_wifi_close(struct usbh_bl616 * bl616_class)155 static int usbh_bl616_wifi_close(struct usbh_bl616 *bl616_class)
156 {
157     uint32_t msg_len;
158     usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
159     rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t));
160 
161     memset(usb_hdr, 0, sizeof(usb_data_t));
162     memset(msg, 0, sizeof(rnm_base_msg_t));
163 
164     usb_hdr->type = USBWIFI_DATA_TYPE_CMD;
165     usb_hdr->length = sizeof(rnm_base_msg_t);
166     usb_hdr->payload_offset = sizeof(usb_data_t);
167 
168     msg->cmd = BFLB_CMD_UNLOAD_DRV;
169 
170     msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t);
171 
172     return usbh_bl616_bulk_out_transfer(bl616_class, g_bl616_tx_buffer, msg_len, 500);
173 }
174 
usbh_bl616_wifi_sta_connect(const char * ssid,const int ssid_len,const char * password,const int pwd_len)175 int usbh_bl616_wifi_sta_connect(const char *ssid,
176                                 const int ssid_len,
177                                 const char *password,
178                                 const int pwd_len)
179 {
180     uint32_t msg_len;
181     usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
182     rnm_sta_connect_msg_t *msg = (rnm_sta_connect_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t));
183 
184     memset(usb_hdr, 0, sizeof(usb_data_t));
185     memset(msg, 0, sizeof(rnm_sta_connect_msg_t));
186 
187     usb_hdr->type = USBWIFI_DATA_TYPE_CMD;
188     usb_hdr->length = sizeof(rnm_sta_connect_msg_t);
189     usb_hdr->payload_offset = sizeof(usb_data_t);
190 
191     msg->hdr.cmd = BFLB_CMD_STA_CONNECT;
192     msg->hdr.msg_id = 0x0001;
193     msg->hdr.session_id = 0x0002;
194     msg->ssid_len = ssid_len;
195     memcpy(msg->ssid, ssid, ssid_len);
196     if (password) {
197         memcpy(msg->password, password, pwd_len);
198     }
199 
200     msg_len = sizeof(usb_data_t) + sizeof(rnm_sta_connect_msg_t);
201 
202     return usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500);
203 }
204 
usbh_bl616_wifi_sta_disconnect(void)205 int usbh_bl616_wifi_sta_disconnect(void)
206 {
207     uint32_t msg_len;
208     usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
209     rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t));
210 
211     memset(usb_hdr, 0, sizeof(usb_data_t));
212     memset(msg, 0, sizeof(rnm_base_msg_t));
213 
214     usb_hdr->type = USBWIFI_DATA_TYPE_CMD;
215     usb_hdr->length = sizeof(rnm_base_msg_t);
216     usb_hdr->payload_offset = sizeof(usb_data_t);
217 
218     msg->cmd = BFLB_CMD_STA_DISCONNECT;
219 
220     msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t);
221 
222     return usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500);
223 }
224 
usbh_bl616_get_wifi_scan_result(void)225 int usbh_bl616_get_wifi_scan_result(void)
226 {
227     uint32_t msg_len;
228     usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
229     rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t));
230 
231     memset(usb_hdr, 0, sizeof(usb_data_t));
232     memset(msg, 0, sizeof(rnm_base_msg_t));
233 
234     usb_hdr->type = USBWIFI_DATA_TYPE_CMD;
235     usb_hdr->length = sizeof(rnm_base_msg_t);
236     usb_hdr->payload_offset = sizeof(usb_data_t);
237 
238     msg->cmd = BFLB_CMD_SCAN_RESULTS;
239 
240     msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t);
241 
242     return usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500);
243 }
244 
usbh_bl616_wifi_scan(void)245 int usbh_bl616_wifi_scan(void)
246 {
247     int ret;
248     uint32_t msg_len;
249     usb_data_t *usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
250     rnm_base_msg_t *msg = (rnm_base_msg_t *)(g_bl616_tx_buffer + sizeof(usb_data_t));
251 
252     memset(usb_hdr, 0, sizeof(usb_data_t));
253     memset(msg, 0, sizeof(rnm_base_msg_t));
254 
255     usb_hdr->type = USBWIFI_DATA_TYPE_CMD;
256     usb_hdr->length = sizeof(rnm_base_msg_t);
257     usb_hdr->payload_offset = sizeof(usb_data_t);
258 
259     msg->cmd = BFLB_CMD_SCAN;
260 
261     msg_len = sizeof(usb_data_t) + sizeof(rnm_base_msg_t);
262 
263     ret = usbh_bl616_bulk_out_transfer(&g_bl616_class, g_bl616_tx_buffer, msg_len, 500);
264     if (ret < 0) {
265         return ret;
266     }
267 
268     usb_osal_msleep(500);
269     return usbh_bl616_get_wifi_scan_result();
270 }
271 
usbh_bl616_connect(struct usbh_hubport * hport,uint8_t intf)272 static int usbh_bl616_connect(struct usbh_hubport *hport, uint8_t intf)
273 {
274     struct usb_endpoint_descriptor *ep_desc;
275     int ret = 0;
276 
277     struct usbh_bl616 *bl616_class = &g_bl616_class;
278 
279     memset(bl616_class, 0, sizeof(struct usbh_bl616));
280 
281     bl616_class->hport = hport;
282     bl616_class->intf = intf;
283 
284     hport->config.intf[intf].priv = bl616_class;
285 
286     for (uint8_t i = 0; i < hport->config.intf[intf].altsetting[0].intf_desc.bNumEndpoints; i++) {
287         ep_desc = &hport->config.intf[intf].altsetting[0].ep[i].ep_desc;
288 
289         if (ep_desc->bEndpointAddress & 0x80) {
290             USBH_EP_INIT(bl616_class->bulkin, ep_desc);
291         } else {
292             USBH_EP_INIT(bl616_class->bulkout, ep_desc);
293         }
294     }
295 
296     usbh_bl616_get_wifi_mac(bl616_class);
297     usbh_bl616_wifi_close(bl616_class);
298     usbh_bl616_wifi_open(bl616_class);
299 
300     USB_LOG_INFO("BL616 WIFI STA MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
301                  bl616_class->sta_mac[0],
302                  bl616_class->sta_mac[1],
303                  bl616_class->sta_mac[2],
304                  bl616_class->sta_mac[3],
305                  bl616_class->sta_mac[4],
306                  bl616_class->sta_mac[5]);
307 
308     USB_LOG_INFO("BL616 WIFI AP MAC address %02x:%02x:%02x:%02x:%02x:%02x\r\n",
309                  bl616_class->ap_mac[0],
310                  bl616_class->ap_mac[1],
311                  bl616_class->ap_mac[2],
312                  bl616_class->ap_mac[3],
313                  bl616_class->ap_mac[4],
314                  bl616_class->ap_mac[5]);
315 
316     strncpy(hport->config.intf[intf].devname, DEV_FORMAT, CONFIG_USBHOST_DEV_NAMELEN);
317 
318     USB_LOG_INFO("Register BL616 WIFI Class:%s\r\n", hport->config.intf[intf].devname);
319 
320     usbh_bl616_run(bl616_class);
321     return ret;
322 }
323 
usbh_bl616_disconnect(struct usbh_hubport * hport,uint8_t intf)324 static int usbh_bl616_disconnect(struct usbh_hubport *hport, uint8_t intf)
325 {
326     int ret = 0;
327 
328     struct usbh_bl616 *bl616_class = (struct usbh_bl616 *)hport->config.intf[intf].priv;
329 
330     if (bl616_class) {
331         if (bl616_class->bulkin) {
332             usbh_kill_urb(&bl616_class->bulkin_urb);
333         }
334 
335         if (bl616_class->bulkout) {
336             usbh_kill_urb(&bl616_class->bulkout_urb);
337         }
338 
339         if (hport->config.intf[intf].devname[0] != '\0') {
340             usb_osal_thread_schedule_other();
341             USB_LOG_INFO("Unregister BL616 WIFI Class:%s\r\n", hport->config.intf[intf].devname);
342             usbh_bl616_stop(bl616_class);
343         }
344 
345         memset(bl616_class, 0, sizeof(struct usbh_bl616));
346     }
347 
348     return ret;
349 }
350 
usbh_bl616_rx_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)351 void usbh_bl616_rx_thread(CONFIG_USB_OSAL_THREAD_SET_ARGV)
352 {
353     int ret;
354     usb_data_t *usb_hdr;
355     rnm_base_msg_t *msg;
356     rnm_sta_ip_update_ind_msg_t *ipmsg;
357     rnm_scan_ind_msg_t *scanmsg;
358     uint8_t *data;
359 
360     (void)CONFIG_USB_OSAL_THREAD_GET_ARGV;
361     USB_LOG_INFO("Create bl616 wifi rx thread\r\n");
362 
363     while (1) {
364         ret = usbh_bl616_bulk_in_transfer(&g_bl616_class, g_bl616_rx_buffer, sizeof(g_bl616_rx_buffer), USB_OSAL_WAITING_FOREVER);
365         if (ret < 0) {
366             break;
367         }
368 
369         usb_hdr = (usb_data_t *)g_bl616_rx_buffer;
370 
371         if (usb_hdr->type == USBWIFI_DATA_TYPE_CMD) {
372             msg = (rnm_base_msg_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset);
373 
374             switch (msg->cmd) {
375                 case BFLB_CMD_STA_CONNECTED_IND:
376                     USB_LOG_INFO("AP connected\n");
377                     g_bl616_class.connect_status = true;
378                     usbh_bl616_sta_connect_callback();
379 
380                     break;
381                 case BFLB_CMD_STA_DISCONNECTED_IND:
382                     if (g_bl616_class.connect_status == true) {
383                         g_bl616_class.connect_status = false;
384                         USB_LOG_INFO("AP disconnected\n");
385                         usbh_bl616_sta_disconnect_callback();
386                     }
387                     break;
388                 case BFLB_CMD_STA_IP_UPDATE_IND:
389                     ipmsg = (rnm_sta_ip_update_ind_msg_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset);
390 
391                     USB_LOG_INFO("WIFI IP update\r\n");
392                     USB_LOG_INFO("WIFI IPv4 Address     : %d:%d:%d:%d\r\n",
393                                  ipmsg->ip4_addr[0],
394                                  ipmsg->ip4_addr[1],
395                                  ipmsg->ip4_addr[2],
396                                  ipmsg->ip4_addr[3]);
397                     USB_LOG_INFO("WIFI IPv4 Mask        : %d:%d:%d:%d\r\n",
398                                  ipmsg->ip4_mask[0],
399                                  ipmsg->ip4_mask[1],
400                                  ipmsg->ip4_mask[2],
401                                  ipmsg->ip4_mask[3]);
402                     USB_LOG_INFO("WIFI IPv4 Gateway     : %d:%d:%d:%d\r\n\r\n",
403                                  ipmsg->ip4_gw[0],
404                                  ipmsg->ip4_gw[1],
405                                  ipmsg->ip4_gw[2],
406                                  ipmsg->ip4_gw[3]);
407 
408                     g_bl616_class.mode = BL_MODE_STA;
409                     usbh_bl616_sta_update_ip(ipmsg->ip4_addr, ipmsg->ip4_mask, ipmsg->ip4_gw);
410                     break;
411                 case BFLB_CMD_SCAN_RESULTS:
412                     scanmsg = (rnm_scan_ind_msg_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset);
413                     USB_LOG_INFO("WIFI scan result:\r\n");
414                     for (uint32_t i = 0; i < scanmsg->num; ++i) {
415                         struct bf1b_wifi_scan_record *r = &scanmsg->records[i];
416                         USB_LOG_INFO("BSSID " MAC_FMT ", channel %u, rssi %d, auth %s, cipher %s, SSID %s\r\n",
417                                      ARR_ELE_6(r->bssid), r->channel, r->rssi,
418                                      auth_to_str(r->auth_mode), cipher_to_str(r->cipher), r->ssid);
419                     }
420                     break;
421                 default:
422                     break;
423             }
424         } else if (usb_hdr->type == USBWIFI_DATA_TYPE_PKT) {
425             data = (uint8_t *)(g_bl616_rx_buffer + usb_hdr->payload_offset);
426             usbh_bl616_eth_input(data, usb_hdr->length);
427         } else {
428         }
429     }
430 
431     USB_LOG_INFO("Delete bl616 wifi rx thread\r\n");
432     usb_osal_thread_delete(NULL);
433 }
434 
usbh_bl616_get_eth_txbuf(void)435 uint8_t *usbh_bl616_get_eth_txbuf(void)
436 {
437     return (g_bl616_tx_buffer + sizeof(usb_data_t));
438 }
439 
usbh_bl616_eth_output(uint32_t buflen)440 int usbh_bl616_eth_output(uint32_t buflen)
441 {
442     usb_data_t *usb_hdr;
443     uint32_t txlen;
444 
445     if (g_bl616_class.connect_status == false) {
446         return -USB_ERR_NOTCONN;
447     }
448 
449     usb_hdr = (usb_data_t *)g_bl616_tx_buffer;
450     memset(usb_hdr, 0, sizeof(usb_data_t));
451 
452     usb_hdr->type = USBWIFI_DATA_TYPE_PKT;
453     usb_hdr->length = buflen;
454     usb_hdr->payload_offset = sizeof(usb_data_t);
455 
456     txlen = buflen + sizeof(usb_data_t);
457     if (!(txlen % USB_GET_MAXPACKETSIZE(g_bl616_class.bulkout->wMaxPacketSize))) {
458         txlen += 1;
459     }
460     USB_LOG_DBG("txlen:%d\r\n", txlen);
461 
462     usbh_bulk_urb_fill(&g_bl616_class.bulkout_urb, g_bl616_class.hport, g_bl616_class.bulkout, g_bl616_tx_buffer, txlen, USB_OSAL_WAITING_FOREVER, NULL, NULL);
463     return usbh_submit_urb(&g_bl616_class.bulkout_urb);
464 }
465 
wifi_sta_connect(int argc,char ** argv)466 int wifi_sta_connect(int argc, char **argv)
467 {
468     if (argc < 3) {
469         USB_LOG_ERR("Usage: %s <ssid> <password>\r\n", argv[0]);
470         return -1;
471     }
472     usbh_bl616_wifi_sta_connect(argv[1], strlen(argv[1]), argv[2], strlen(argv[2]));
473     return 0;
474 }
475 
wifi_scan(int argc,char ** argv)476 int wifi_scan(int argc, char **argv)
477 {
478     (void)argc;
479     (void)argv;
480 
481     usbh_bl616_wifi_scan();
482     return 0;
483 }
484 
usbh_bl616_run(struct usbh_bl616 * bl616_class)485 __WEAK void usbh_bl616_run(struct usbh_bl616 *bl616_class)
486 {
487     (void)bl616_class;
488 }
489 
usbh_bl616_stop(struct usbh_bl616 * bl616_class)490 __WEAK void usbh_bl616_stop(struct usbh_bl616 *bl616_class)
491 {
492     (void)bl616_class;
493 }
494 
495 static const uint16_t bl616_id_table[][2] = {
496     { 0x349b, 0x616f },
497     { 0, 0 },
498 };
499 
500 static const struct usbh_class_driver bl616_class_driver = {
501     .driver_name = "bl616_wifi",
502     .connect = usbh_bl616_connect,
503     .disconnect = usbh_bl616_disconnect
504 };
505 
506 CLASS_INFO_DEFINE const struct usbh_class_info bl616_class_info = {
507     .match_flags = USB_CLASS_MATCH_VID_PID | USB_CLASS_MATCH_INTF_CLASS,
508     .bInterfaceClass = 0xff,
509     .bInterfaceSubClass = 0x00,
510     .bInterfaceProtocol = 0x00,
511     .id_table = bl616_id_table,
512     .class_driver = &bl616_class_driver
513 };
514