usbh_bl616.c 16 KB

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