@@ -122,6 +122,7 @@ mtwn_usb_alloc_cmd_list(struct mtwn_usb_softc *uc)
122122 TAILQ_INIT (& uc -> uc_cmd_pending );
123123 TAILQ_INIT (& uc -> uc_cmd_waiting );
124124 TAILQ_INIT (& uc -> uc_cmd_inactive );
125+ TAILQ_INIT (& uc -> uc_cmd_completed );
125126
126127 ret = mtwn_usb_cmd_alloc_list_array (uc , uc -> uc_cmd ,
127128 MTWN_USB_CMD_LIST_COUNT , MTWN_USB_CMDBUFSZ );
@@ -145,6 +146,7 @@ mtwn_usb_free_cmd_list(struct mtwn_usb_softc *uc)
145146 TAILQ_INIT (& uc -> uc_cmd_pending );
146147 TAILQ_INIT (& uc -> uc_cmd_waiting );
147148 TAILQ_INIT (& uc -> uc_cmd_inactive );
149+ TAILQ_INIT (& uc -> uc_cmd_completed );
148150}
149151
150152/**
@@ -156,6 +158,9 @@ mtwn_usb_free_cmd_list(struct mtwn_usb_softc *uc)
156158 * but that way we don't need to worry about the sending thread timing
157159 * out - the mtwn_cmd response buffer will always be available.
158160 *
161+ * This must only be called on buffers that are in the ALLOCED state;
162+ * ie they're not on any lists.
163+ *
159164 * @param uc usb_softc
160165 * @param cmd command to set completion info for
161166 * @param buf payload buffer to copy from
@@ -166,6 +171,12 @@ void
166171mtwn_usb_cmd_copyin_response (struct mtwn_usb_softc * uc , struct mtwn_cmd * cmd ,
167172 const char * buf , int len )
168173{
174+ struct mtwn_softc * sc = & uc -> uc_sc ;
175+
176+ if (cmd -> state != MTWN_CMD_STATE_ALLOCED )
177+ MTWN_WARN_PRINTF (sc , "%s: cmd %p has the wrong state! (%d)\n" ,
178+ __func__ , cmd , cmd -> state );
179+
169180 /*
170181 * If we get an error or zero-size response then don't copy, but do set
171182 * completion.
@@ -190,17 +201,24 @@ mtwn_usb_cmd_copyin_response(struct mtwn_usb_softc *uc, struct mtwn_cmd *cmd,
190201}
191202
192203/**
193- * @brief Complete the given command, re-insert it on the inactive list.
204+ * @brief Return the given buffer to the inactive list.
205+ *
206+ * This must only be called on a cmd buffer that isn't on any list,
207+ * and is in the ALLOCED state.
194208 */
195- void
196- mtwn_usb_cmd_complete (struct mtwn_usb_softc * uc , struct mtwn_cmd * cmd )
209+ static void
210+ mtwn_usb_cmd_return_inactive (struct mtwn_usb_softc * uc ,
211+ struct mtwn_cmd * cmd )
197212{
198213 struct mtwn_softc * sc = & uc -> uc_sc ;
199214 MTWN_LOCK_ASSERT (sc , MA_OWNED );
200215
201- MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: cmd=%p; completing\n" ,
216+ if (cmd -> state != MTWN_CMD_STATE_ALLOCED )
217+ MTWN_WARN_PRINTF (sc , "%s: cmd %p has the wrong state! (%d)\n" ,
218+ __func__ , cmd , cmd -> state );
219+
220+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: cmd=%p; returning to inactive\n" ,
202221 __func__ , cmd );
203- wakeup (cmd );
204222
205223 /* Free response buffer contents */
206224 if (cmd -> resp .buf != NULL )
@@ -211,10 +229,53 @@ mtwn_usb_cmd_complete(struct mtwn_usb_softc *uc, struct mtwn_cmd *cmd)
211229 cmd -> state = MTWN_CMD_STATE_INACTIVE ;
212230}
213231
232+ /**
233+ * @brief Complete the given command, re-insert it on the inactive list.
234+ *
235+ * The command must be in the ALLOCED state and not on any list.
236+ *
237+ * If the buffer is 'wait', then move it to the completed list before
238+ * waking up. If not, just free it.
239+ */
240+ void
241+ mtwn_usb_cmd_complete (struct mtwn_usb_softc * uc , struct mtwn_cmd * cmd )
242+ {
243+ struct mtwn_softc * sc = & uc -> uc_sc ;
244+ MTWN_LOCK_ASSERT (sc , MA_OWNED );
245+
246+ if (cmd -> state != MTWN_CMD_STATE_ALLOCED )
247+ MTWN_WARN_PRINTF (sc , "%s: cmd %p has the wrong state! (%d)\n" ,
248+ __func__ , cmd , cmd -> state );
249+
250+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: cmd=%p; completing\n" ,
251+ __func__ , cmd );
252+
253+ if (cmd -> flags .do_wait == true) {
254+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD ,
255+ "%s: cmd=%p; moving to COMPLETED queue\n" , __func__ , cmd );
256+ /* Move the buffer to the completed state */
257+ TAILQ_INSERT_TAIL (& uc -> uc_cmd_completed , cmd , next );
258+ cmd -> state = MTWN_CMD_STATE_COMPLETED ;
259+
260+ /* Signal any waiter that the command has completed */
261+ wakeup (cmd );
262+ } else {
263+ /* Signal any waiter that the command has completed */
264+ wakeup (cmd );
265+
266+ /* Return the buffer to the inactive list */
267+ mtwn_usb_cmd_return_inactive (uc , cmd );
268+ }
269+ }
270+
214271/*
215- * Handle completion of the given command buffer.
272+ * @brief Handle completion of the given command buffer.
273+ *
274+ * The command buffer must not be on any active lists, and must be
275+ * in the ALLOCED state.
216276 *
217- * Note the caller still needs to shuffle it to the inactive list.
277+ * If we're required to wait for a follow-up RX notification (do_wait=true)
278+ * then move it to WAITING, else immediately complete it.
218279 */
219280static void
220281mtwn_usb_cmd_eof (struct mtwn_usb_softc * uc , struct mtwn_cmd * cmd )
@@ -223,20 +284,24 @@ mtwn_usb_cmd_eof(struct mtwn_usb_softc *uc, struct mtwn_cmd *cmd)
223284
224285 MTWN_LOCK_ASSERT (sc , MA_OWNED );
225286
226- MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: completed, cmd=%p, do_wait=%d\n" ,
227- __func__ , cmd , cmd -> flags .do_wait );
287+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: completed, cmd=%p, do_wait=%d, state=%d\n" ,
288+ __func__ , cmd , cmd -> flags .do_wait , cmd -> state );
289+
290+ if (cmd -> state != MTWN_CMD_STATE_ALLOCED )
291+ MTWN_WARN_PRINTF (sc , "%s: cmd %p has the wrong state! (%d)\n" ,
292+ __func__ , cmd , cmd -> state );
228293
229294 if (cmd -> flags .do_wait == true) {
230- cmd -> state = MTWN_CMD_STATE_WAITING ;
231295 TAILQ_INSERT_HEAD (& uc -> uc_cmd_waiting , cmd , next );
296+ cmd -> state = MTWN_CMD_STATE_WAITING ;
232297 } else
233298 mtwn_usb_cmd_complete (uc , cmd );
234299}
235300
236301/**
237302 * @brief wait for the command buffer in question to complete.
238303 */
239- int
304+ static int
240305mtwn_usb_cmd_wait (struct mtwn_usb_softc * uc , struct mtwn_cmd * cmd , int timeout )
241306{
242307 struct mtwn_softc * sc = & uc -> uc_sc ;
@@ -305,6 +370,8 @@ mtwn_usb_cmd_get(struct mtwn_usb_softc *uc, int size, int rx_size)
305370 TAILQ_REMOVE_HEAD (& uc -> uc_cmd_inactive , next );
306371 cmd -> state = MTWN_CMD_STATE_ALLOCED ;
307372
373+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: cmd=%p\n" , __func__ , cmd );
374+
308375 return (cmd );
309376}
310377
@@ -319,14 +386,9 @@ mtwn_usb_cmd_return(struct mtwn_usb_softc *uc, struct mtwn_cmd *cmd)
319386 struct mtwn_softc * sc = & uc -> uc_sc ;
320387
321388 MTWN_LOCK_ASSERT (sc , MA_OWNED );
322-
323- /* Free response buffer contents */
324- if (cmd -> resp .buf != NULL )
325- free (cmd -> resp .buf , M_USBDEV );
326- bzero (& cmd -> resp , sizeof (cmd -> resp ));
327-
328- TAILQ_INSERT_TAIL (& uc -> uc_cmd_inactive , cmd , next );
329- cmd -> state = MTWN_CMD_STATE_INACTIVE ;
389+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: cmd=%p, state=%d\n" , __func__ , cmd , state );
390+ /* Return the buffer to the inactive list */
391+ mtwn_usb_cmd_return_inactive (uc , cmd );
330392}
331393
332394/**
@@ -340,6 +402,9 @@ mtwn_usb_cmd_queue(struct mtwn_usb_softc *uc, struct mtwn_cmd *cmd)
340402 struct mtwn_softc * sc = & uc -> uc_sc ;
341403 MTWN_LOCK_ASSERT (sc , MA_OWNED );
342404
405+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD ,
406+ "%s: cmd=%p\n" , __func__ , cmd );
407+
343408 TAILQ_INSERT_TAIL (& uc -> uc_cmd_pending , cmd , next );
344409 cmd -> state = MTWN_CMD_STATE_PENDING ;
345410 usbd_transfer_start (uc -> uc_xfer [MTWN_BULK_TX_INBAND_CMD ]);
@@ -352,8 +417,8 @@ mtwn_usb_cmd_queue(struct mtwn_usb_softc *uc, struct mtwn_cmd *cmd)
352417 * Note to the caller that once this is called, the buffer is no
353418 * longer owned by the caller.
354419 *
355- * Also note this isn't going to wait for the RESPONSE, only the
356- * transfer completed .
420+ * This will either wait for the TX completion (do_wait == false);
421+ * or TX + RX completion (do_wait == true) .
357422 */
358423int
359424mtwn_usb_cmd_queue_wait (struct mtwn_usb_softc * uc , struct mtwn_cmd * cmd ,
@@ -364,15 +429,100 @@ mtwn_usb_cmd_queue_wait(struct mtwn_usb_softc *uc, struct mtwn_cmd *cmd,
364429
365430 MTWN_LOCK_ASSERT (sc , MA_OWNED );
366431
432+ MTWN_DPRINTF (sc , MTWN_DEBUG_CMD ,
433+ "%s: cmd=%p; timeout=%d, wait_resp=%d\n" , __func__ , cmd ,
434+ timeout , wait_resp );
435+
367436 /* Wait for completion, not just transmit */
368437 cmd -> flags .do_wait = wait_resp ;
369438
439+ /* Enqueue */
370440 TAILQ_INSERT_TAIL (& uc -> uc_cmd_pending , cmd , next );
371441 cmd -> state = MTWN_CMD_STATE_PENDING ;
372442 usbd_transfer_start (uc -> uc_xfer [MTWN_BULK_TX_INBAND_CMD ]);
373443
444+ /* Wait for TX completion, optional RX completion */
374445 ret = mtwn_usb_cmd_wait (uc , cmd , timeout );
375- return (ret );
446+
447+ /*
448+ * If it's a timeout, then mark it as no longer waiting so it'll
449+ * be immediately freed, or free it here if we have to.
450+ */
451+ if (ret != 0 ) {
452+ /* error path */
453+ MTWN_WARN_PRINTF (sc , "%s: cmd %p (state %d) timeout\n" , __func__ , cmd , cmd -> state );
454+ cmd -> flags .do_wait = false;
455+
456+ /* TODO: refactor this out */
457+ switch (cmd -> state ) {
458+ case MTWN_CMD_STATE_ACTIVE :
459+ case MTWN_CMD_STATE_PENDING :
460+ /* These two will complete w/ do_wait and go straight to inactive */
461+ break ;
462+ case MTWN_CMD_STATE_WAITING :
463+ /*
464+ * Don't wait until the next firmware RX notification
465+ * to run through the waiting list and free this; just
466+ * free it now.
467+ */
468+ TAILQ_REMOVE (& uc -> uc_cmd_waiting , cmd , next );
469+ cmd -> state = MTWN_CMD_STATE_ALLOCED ;
470+ mtwn_usb_cmd_return_inactive (uc , cmd );
471+ break ;
472+ case MTWN_CMD_STATE_COMPLETED :
473+ MTWN_WARN_PRINTF (sc ,
474+ "%s: cmd %p timeout but state=COMPLETED?\n" ,
475+ __func__ , cmd );
476+ /*
477+ * This is a weird situation to be in, but at least
478+ * handle it.
479+ *
480+ * This may actually be OK and we may want to just
481+ * jump to the normal completion path / response
482+ * path below somehow.
483+ */
484+ TAILQ_REMOVE (& uc -> uc_cmd_completed , cmd , next );
485+ cmd -> state = MTWN_CMD_STATE_ALLOCED ;
486+ mtwn_usb_cmd_return_inactive (uc , cmd );
487+ break ;
488+ default :
489+ /* TODO: actually figure out what list to remove it from, do so, etc */
490+ MTWN_WARN_PRINTF (sc , "%s: cmd %p (state %d) invalid/unhandled state!\n" , __func__ , cmd , cmd -> state );
491+ break ;
492+ }
493+
494+ return (ret );
495+ }
496+ MTWN_DEBUG_PRINTF (sc , "%s: completion, cmd=%p, state=%d, resp=%d, wait=%d\n" , __func__ , cmd , state , cmd -> flags .resp_set , cmd -> flags .do_wait );
497+ /* Completion path */
498+
499+ /* handle response */
500+ if (cmd -> flags .resp_set == true) {
501+ MTWN_TODO_PRINTF (sc ,
502+ "%s: TODO: **** COMPLETION COPYOUT TIME (%p) ****\n" ,
503+ __func__ , cmd );
504+ /* XXX TODO: copy the RX specific payload out */
505+ }
506+
507+ /*
508+ * if we're waiting for the response on this command buffer then
509+ * we need to also clean it up.
510+ */
511+ if (cmd -> flags .do_wait == true) {
512+ /* XXX refactor this out */
513+ if (cmd -> state != MTWN_CMD_STATE_COMPLETED )
514+ MTWN_WARN_PRINTF (sc , "%s: cmd %p in wrong state (%d)\n" ,
515+ __func__ , cmd , cmd -> state );
516+
517+ /* Remove from the completed list */
518+ TAILQ_REMOVE (& uc -> uc_cmd_completed , cmd , next );
519+ cmd -> state = MTWN_CMD_STATE_ALLOCED ;
520+
521+ /* Return the buffer to the inactive list */
522+ mtwn_usb_cmd_return_inactive (uc , cmd );
523+ }
524+
525+ return (0 );
376526}
377527
378528/*
@@ -383,7 +533,7 @@ mtwn_bulk_tx_inband_cmd_callback(struct usb_xfer *xfer, usb_error_t error)
383533{
384534 struct mtwn_usb_softc * uc = usbd_xfer_softc (xfer );
385535 struct mtwn_softc * sc = & uc -> uc_sc ;
386- struct mtwn_cmd * cmd ;
536+ struct mtwn_cmd * cmd , * c ;
387537
388538 MTWN_DPRINTF (sc , MTWN_DEBUG_CMD , "%s: called\n" , __func__ );
389539
@@ -397,6 +547,14 @@ mtwn_bulk_tx_inband_cmd_callback(struct usb_xfer *xfer, usb_error_t error)
397547 TAILQ_REMOVE_HEAD (& uc -> uc_cmd_active , next );
398548 cmd -> state = MTWN_CMD_STATE_ALLOCED ;
399549
550+ /*
551+ * TODO: usbd_xfer_get_priv() to fetch cmd, verify against
552+ * uc_cmd_active head!
553+ */
554+ c = usbd_xfer_get_priv (xfer );
555+ if (c != cmd )
556+ MTWN_WARN_PRINTF (sc ,
557+ "%s: mismatch between xfer and cmd\n" , __func__ );
400558 /* TX completed */
401559 mtwn_usb_cmd_eof (uc , cmd );
402560 /* FALLTHROUGH */
@@ -412,6 +570,7 @@ mtwn_bulk_tx_inband_cmd_callback(struct usb_xfer *xfer, usb_error_t error)
412570 cmd -> state = MTWN_CMD_STATE_ACTIVE ;
413571
414572 usbd_xfer_set_frame_data (xfer , 0 , cmd -> buf , cmd -> buflen );
573+ usbd_xfer_set_priv (xfer , cmd );
415574 usbd_transfer_submit (xfer );
416575 break ;
417576 default :
0 commit comments