4949# define VSF_USBH_WIFI_AIC8800D_CFG_TX_BUFSIZE 2048
5050#endif
5151
52+ /* Bus-level logging. The RX URB completion path is extremely hot: the AIC
53+ * firmware emits ZLP keepalives on the bulk IN endpoint at ~1kHz, so per-URB
54+ * traces are debug-level, gated by VSF_WIFI_CFG_BUS_AIC8800D_LOG_LEVEL
55+ * (vsf_wifi_cfg.h falls it back to VSF_WIFI_CFG_LOG_LEVEL). */
56+ #if VSF_WIFI_CFG_BUS_AIC8800D_LOG_LEVEL >= 1
57+ # define __usbh_aic8800d_trace_error (...) vsf_trace_error(__VA_ARGS__)
58+ #else
59+ # define __usbh_aic8800d_trace_error (...) ((void)0)
60+ #endif
61+ #if VSF_WIFI_CFG_BUS_AIC8800D_LOG_LEVEL >= 2
62+ # define __usbh_aic8800d_trace_info (...) vsf_trace_info(__VA_ARGS__)
63+ #else
64+ # define __usbh_aic8800d_trace_info (...) ((void)0)
65+ #endif
66+ #if VSF_WIFI_CFG_BUS_AIC8800D_LOG_LEVEL >= 4
67+ # define __usbh_aic8800d_trace_debug (...) vsf_trace_info(__VA_ARGS__)
68+ #else
69+ # define __usbh_aic8800d_trace_debug (...) ((void)0)
70+ #endif
71+
5272#define AIC8800D_USB_VID 0xA69C
5373#define AIC8800D_V2_VID 0x368B
5474#define AIC8800D_TP_VID 0x2357
@@ -91,18 +111,21 @@ typedef struct vk_usbh_wifi_aic8800d_t {
91111 vsf_wifi_aic8800d_bus_ops_t bus_ops ;
92112
93113 uint8_t ep_in ;
94- uint8_t ep_out ;
114+ uint8_t ep_out_cmd ; /* command OUT endpoint (ep 0x02) */
115+ uint8_t ep_out_data ; /* data OUT endpoint (ep 0x01) */
95116 uint16_t ep_in_mps ;
96- uint16_t ep_out_mps ;
117+ uint16_t ep_out_cmd_mps ;
118+ uint16_t ep_out_data_mps ;
97119
98120 /* Bulk RX/TX iocb pool */
99121 union {
100122 struct {
101123 vk_usbh_wifi_aic8800d_iocb_t rx_icb [VSF_USBH_WIFI_AIC8800D_CFG_RX_NUM ];
102- vk_usbh_wifi_aic8800d_iocb_t tx_ocb [VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ];
124+ vk_usbh_wifi_aic8800d_iocb_t tx_cmd_ocb [VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ];
125+ vk_usbh_wifi_aic8800d_iocb_t tx_data_ocb [VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ];
103126 };
104127 vk_usbh_wifi_aic8800d_iocb_t iocb [VSF_USBH_WIFI_AIC8800D_CFG_RX_NUM
105- + VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ];
128+ + 2 * VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ];
106129 };
107130} vk_usbh_wifi_aic8800d_t ;
108131
@@ -197,14 +220,32 @@ static vsf_err_t __vk_usbh_wifi_aic8800d_send(vsf_wifi_t *wifi,
197220
198221 if (len > VSF_USBH_WIFI_AIC8800D_CFG_TX_BUFSIZE ) return VSF_ERR_NOT_SUPPORT ;
199222
223+ /* LMAC commands carry the 0x11 command type at offset 2; data frames use
224+ * the data type (0x01). Route commands to ep 0x02 and data to ep 0x01,
225+ * matching the firmware's expectations. */
226+ bool is_cmd = (len >= 8 ) && (data [2 ] == 0x11 ) && (data [3 ] == 0x00 );
227+
228+ /* Preferred pool by message type (cmd -> ep 0x02, data -> ep 0x01), but
229+ * fall back to the other pool: the boot-ROM device exposes only a single
230+ * bulk OUT (ep 0x01) used for the firmware-download commands, while the
231+ * runtime device has both. */
232+ vk_usbh_wifi_aic8800d_iocb_t * pools [2 ] = {
233+ is_cmd ? aic -> tx_cmd_ocb : aic -> tx_data_ocb ,
234+ is_cmd ? aic -> tx_data_ocb : aic -> tx_cmd_ocb ,
235+ };
200236 vk_usbh_wifi_aic8800d_iocb_t * ocb = NULL ;
201- for (int i = 0 ; i < VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ; i ++ ) {
202- if (aic -> tx_ocb [i ].is_supported && !aic -> tx_ocb [i ].is_busy ) {
203- ocb = & aic -> tx_ocb [i ];
204- break ;
237+ for (int pass = 0 ; pass < 2 && ocb == NULL ; pass ++ ) {
238+ for (int i = 0 ; i < VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ; i ++ ) {
239+ if (pools [pass ][i ].is_supported && !pools [pass ][i ].is_busy ) {
240+ ocb = & pools [pass ][i ];
241+ break ;
242+ }
205243 }
206244 }
207- if (NULL == ocb ) return VSF_ERR_NOT_AVAILABLE ;
245+ if (NULL == ocb ) {
246+ vsf_wifi_aic8800d_trace_error ("aic8800d_usb: send no free ocb (cmd=%d)" VSF_TRACE_CFG_LINEEND , is_cmd );
247+ return VSF_ERR_NOT_AVAILABLE ;
248+ }
208249
209250 uint8_t * buf = vk_usbh_urb_peek_buffer (& ocb -> urb );
210251 if (NULL == buf ) {
@@ -226,9 +267,13 @@ static bool __vk_usbh_wifi_aic8800d_can_send(vsf_wifi_t *wifi)
226267{
227268 vk_usbh_wifi_aic8800d_t * aic = vsf_container_of (wifi , vk_usbh_wifi_aic8800d_t , wifi );
228269 if (!wifi -> is_ready ) return false;
270+ /* send() falls back to the other pool when the preferred one is full, so
271+ * a free slot in either pool means TX is possible. */
229272 for (int i = 0 ; i < VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ; i ++ ) {
230- vk_usbh_wifi_aic8800d_iocb_t * ocb = & aic -> tx_ocb [i ];
231- if (ocb -> is_supported && !ocb -> is_busy ) return true;
273+ if ((aic -> tx_cmd_ocb [i ].is_supported && !aic -> tx_cmd_ocb [i ].is_busy )
274+ || (aic -> tx_data_ocb [i ].is_supported && !aic -> tx_data_ocb [i ].is_busy )) {
275+ return true;
276+ }
232277 }
233278 return false;
234279}
@@ -259,21 +304,21 @@ static void __vk_usbh_wifi_aic8800d_evthandler(vsf_eda_t *eda, vsf_evt_t evt)
259304{
260305 vk_usbh_wifi_aic8800d_t * aic = __this_aic (eda );
261306
262- vsf_trace_info ("aic8800d_usb: evthandler evt=%d disconnecting=%d INIT=%d"
307+ __usbh_aic8800d_trace_debug ("aic8800d_usb: evthandler evt=%d disconnecting=%d INIT=%d"
263308 VSF_TRACE_CFG_LINEEND , (int )evt , (int )aic -> wifi .disconnecting , (int )VSF_EVT_INIT );
264309
265310 if (aic -> wifi .disconnecting ) return ;
266311
267312 switch (evt ) {
268313 case VSF_EVT_INIT :
269- vsf_trace_info ("aic8800d_usb: VSF_EVT_INIT matched, calling wifi_start"
314+ __usbh_aic8800d_trace_info ("aic8800d_usb: VSF_EVT_INIT matched, calling wifi_start"
270315 VSF_TRACE_CFG_LINEEND );
271316 if (!__vk_usbh_wifi_aic8800d_start_rx (aic )) {
272317 vk_usbh_remove_interface (aic -> usbh , aic -> dev , aic -> ifs );
273318 return ;
274319 }
275320 vsf_wifi_start (& aic -> wifi );
276- vsf_trace_info ("aic8800d_usb: wifi_start returned" VSF_TRACE_CFG_LINEEND );
321+ __usbh_aic8800d_trace_info ("aic8800d_usb: wifi_start returned" VSF_TRACE_CFG_LINEEND );
277322 return ;
278323
279324 case VSF_WIFI_EVT_SCAN_HOP :
@@ -299,7 +344,7 @@ static void __vk_usbh_wifi_aic8800d_evthandler(vsf_eda_t *eda, vsf_evt_t evt)
299344 int st = vk_usbh_urb_get_status (& iocb -> urb );
300345 uint32_t len = (URB_OK == st )
301346 ? vk_usbh_urb_get_actual_length (& iocb -> urb ) : 0 ;
302- vsf_trace_info ("aic8800d_usb: rx done st=%d len=%u" VSF_TRACE_CFG_LINEEND ,
347+ __usbh_aic8800d_trace_debug ("aic8800d_usb: rx done st=%d len=%u" VSF_TRACE_CFG_LINEEND ,
303348 st , (unsigned )len );
304349 if (URB_OK == st && len > 0 ) {
305350 uint8_t * frame = vk_usbh_urb_peek_buffer (& iocb -> urb );
@@ -311,7 +356,7 @@ static void __vk_usbh_wifi_aic8800d_evthandler(vsf_eda_t *eda, vsf_evt_t evt)
311356 }
312357 } else {
313358 int st = vk_usbh_urb_get_status (& iocb -> urb );
314- vsf_trace_info ("aic8800d_usb: tx done st=%d" VSF_TRACE_CFG_LINEEND , st );
359+ __usbh_aic8800d_trace_debug ("aic8800d_usb: tx done st=%d" VSF_TRACE_CFG_LINEEND , st );
315360 iocb -> is_busy = false;
316361 }
317362 return ;
@@ -377,19 +422,39 @@ static void * __vk_usbh_wifi_aic8800d_probe(vk_usbh_t *usbh, vk_usbh_dev_t *dev,
377422 aic -> rx_icb [j ].urb .urb_hcd -> timeout = 5000 ;
378423 }
379424 } else {
380- aic -> ep_out = epaddr ;
381- aic -> ep_out_mps = desc_ep -> wMaxPacketSize ;
425+ /* The runtime interface has two bulk OUT endpoints: ep 0x01 is
426+ * the data path (EAPOL/business frames), ep 0x02 carries LMAC
427+ * commands. Assign by endpoint number. */
428+ uint8_t epnum = epaddr & 0x0F ;
382429 has_tx = true;
383- for (int j = 0 ; j < VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ; j ++ ) {
384- vk_usbh_urb_prepare (& aic -> tx_ocb [j ].urb , dev , desc_ep );
385- aic -> tx_ocb [j ].is_rx = false;
386- aic -> tx_ocb [j ].is_supported = true;
387- if (VSF_ERR_NONE != vk_usbh_alloc_urb (usbh , dev , & aic -> tx_ocb [j ].urb ))
388- goto free_all ;
389- if (NULL == vk_usbh_urb_alloc_buffer (& aic -> tx_ocb [j ].urb ,
390- VSF_USBH_WIFI_AIC8800D_CFG_TX_BUFSIZE ))
391- goto free_all ;
392- aic -> tx_ocb [j ].urb .urb_hcd -> timeout = 5000 ;
430+ if (epnum == 1 ) {
431+ aic -> ep_out_data = epaddr ;
432+ aic -> ep_out_data_mps = desc_ep -> wMaxPacketSize ;
433+ for (int j = 0 ; j < VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ; j ++ ) {
434+ vk_usbh_urb_prepare (& aic -> tx_data_ocb [j ].urb , dev , desc_ep );
435+ aic -> tx_data_ocb [j ].is_rx = false;
436+ aic -> tx_data_ocb [j ].is_supported = true;
437+ if (VSF_ERR_NONE != vk_usbh_alloc_urb (usbh , dev , & aic -> tx_data_ocb [j ].urb ))
438+ goto free_all ;
439+ if (NULL == vk_usbh_urb_alloc_buffer (& aic -> tx_data_ocb [j ].urb ,
440+ VSF_USBH_WIFI_AIC8800D_CFG_TX_BUFSIZE ))
441+ goto free_all ;
442+ aic -> tx_data_ocb [j ].urb .urb_hcd -> timeout = 5000 ;
443+ }
444+ } else {
445+ aic -> ep_out_cmd = epaddr ;
446+ aic -> ep_out_cmd_mps = desc_ep -> wMaxPacketSize ;
447+ for (int j = 0 ; j < VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ; j ++ ) {
448+ vk_usbh_urb_prepare (& aic -> tx_cmd_ocb [j ].urb , dev , desc_ep );
449+ aic -> tx_cmd_ocb [j ].is_rx = false;
450+ aic -> tx_cmd_ocb [j ].is_supported = true;
451+ if (VSF_ERR_NONE != vk_usbh_alloc_urb (usbh , dev , & aic -> tx_cmd_ocb [j ].urb ))
452+ goto free_all ;
453+ if (NULL == vk_usbh_urb_alloc_buffer (& aic -> tx_cmd_ocb [j ].urb ,
454+ VSF_USBH_WIFI_AIC8800D_CFG_TX_BUFSIZE ))
455+ goto free_all ;
456+ aic -> tx_cmd_ocb [j ].urb .urb_hcd -> timeout = 5000 ;
457+ }
393458 }
394459 }
395460 break ;
@@ -409,7 +474,8 @@ static void * __vk_usbh_wifi_aic8800d_probe(vk_usbh_t *usbh, vk_usbh_dev_t *dev,
409474 aic -> rx_icb [j ].rx_retry_timer .on_timer = __vk_usbh_wifi_aic8800d_rx_retry_cb ;
410475 }
411476 for (int j = 0 ; j < VSF_USBH_WIFI_AIC8800D_CFG_TX_NUM ; j ++ ) {
412- aic -> tx_ocb [j ].aic = aic ;
477+ aic -> tx_cmd_ocb [j ].aic = aic ;
478+ aic -> tx_data_ocb [j ].aic = aic ;
413479 }
414480
415481 /* Initialise per-instance bus ops from the template and decide whether
0 commit comments