git: e8018942942e - main - apple_bce: initiate IN data phase for control transfers

From: Abdelkader Boudih <seuros_at_FreeBSD.org>
Date: Sun, 06 Sep 2026 19:48:34 UTC
The branch main has been updated by seuros:

URL: https://cgit.FreeBSD.org/src/commit/?id=e8018942942e212ba5a664f0265f1101e6c48930

commit e8018942942e212ba5a664f0265f1101e6c48930
Author:     Abdelkader Boudih <seuros@FreeBSD.org>
AuthorDate: 2026-09-06 19:47:34 +0000
Commit:     Abdelkader Boudih <seuros@FreeBSD.org>
CommitDate: 2026-09-06 19:47:44 +0000

    apple_bce: initiate IN data phase for control transfers
    
    For IN control transfers, the firmware does
    not send TRANSFER_REQUEST for the data phase
    the host must send BCE_VHCI_CMD_TRANSFER_REQUEST
    with an IN DMA buffer once the setup phase completes.
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D58872
---
 sys/dev/apple_bce/apple_bce_vhci.c | 70 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 70 insertions(+)

diff --git a/sys/dev/apple_bce/apple_bce_vhci.c b/sys/dev/apple_bce/apple_bce_vhci.c
index 56d519e715c1..e8bef8566cd6 100644
--- a/sys/dev/apple_bce/apple_bce_vhci.c
+++ b/sys/dev/apple_bce/apple_bce_vhci.c
@@ -176,6 +176,8 @@ static void bce_vhci_handle_transfer_request(struct bce_vhci_softc *vhci,
     struct bce_vhci_message *msg);
 static void bce_vhci_complete_ctrl_locked(struct bce_vhci_softc *vhci,
     struct bce_vhci_transfer_queue *tq, struct bce_vhci_message *msg);
+static void bce_vhci_ctrl_start_in_data(struct bce_vhci_softc *vhci,
+    struct bce_vhci_transfer_queue *tq);
 static void bce_vhci_handle_ctrl_status(struct bce_vhci_softc *vhci,
     struct bce_vhci_message *msg);
 static uint16_t bce_vhci_handle_endpoint_req_state(struct bce_vhci_softc *vhci,
@@ -1643,6 +1645,71 @@ bce_vhci_submit_pending_out(struct bce_vhci_softc *vhci,
 	}
 }
 
+static void
+bce_vhci_ctrl_start_in_data(struct bce_vhci_softc *vhci,
+    struct bce_vhci_transfer_queue *tq)
+{
+	struct bce_vhci_message treq;
+	struct bce_qe_submission *si;
+	struct usb_xfer *xfer;
+	uint32_t dlen;
+
+	dlen = tq->ctrl_data_len;
+	if (dlen > BCE_VHCI_XFER_BUFSZ)
+		dlen = BCE_VHCI_XFER_BUFSZ;
+
+	xfer = tq->active_xfer;
+	tq->ctrl_state = BCE_VHCI_CTRL_STATUS;
+	USB_BUS_UNLOCK(&vhci->sc_bus);
+
+	memset(&treq, 0, sizeof(treq));
+	treq.cmd = BCE_VHCI_CMD_TRANSFER_REQUEST;
+	treq.param1 = ((uint32_t)tq->endp_addr << 8) | tq->dev_addr;
+	treq.param2 = dlen;
+
+	bus_dmamap_sync(tq->dma_tag, tq->dma_map, BUS_DMASYNC_PREREAD);
+
+	mtx_lock_spin(&vhci->sc_async_lock);
+	if (bce_reserve_submission(vhci->msg_asynchronous.sq) != 0) {
+		mtx_unlock_spin(&vhci->sc_async_lock);
+		goto fail;
+	}
+	mtx_unlock_spin(&vhci->sc_async_lock);
+
+	mtx_lock_spin(&tq->lock);
+	if (bce_reserve_submission(tq->sq_in) != 0) {
+		mtx_unlock_spin(&tq->lock);
+		mtx_lock_spin(&vhci->sc_async_lock);
+		atomic_add_int(
+		    &vhci->msg_asynchronous.sq->available_commands, 1);
+		mtx_unlock_spin(&vhci->sc_async_lock);
+		goto fail;
+	}
+	si = bce_next_submission(tq->sq_in);
+	si->addr = tq->dma_addr;
+	si->length = dlen;
+	si->segl_addr = 0;
+	si->segl_length = 0;
+	bce_submit_to_device(vhci->sc_bce, tq->sq_in);
+	mtx_unlock_spin(&tq->lock);
+
+	mtx_lock_spin(&vhci->sc_async_lock);
+	bce_vhci_msg_queue_write(vhci, &vhci->msg_asynchronous, &treq);
+	mtx_unlock_spin(&vhci->sc_async_lock);
+
+	USB_BUS_LOCK(&vhci->sc_bus);
+	return;
+
+fail:
+	USB_BUS_LOCK(&vhci->sc_bus);
+	if (tq->active_xfer == xfer) {
+		tq->active_xfer = NULL;
+		tq->dma_inflight = 0;
+		tq->ctrl_state = BCE_VHCI_CTRL_IDLE;
+		usbd_transfer_done(xfer, USB_ERR_IOERROR);
+	}
+}
+
 /*
  * Transfer queue DMA completion callback.  Fires when the firmware
  * has consumed (OUT) or filled (IN) a DMA buffer we submitted.
@@ -1791,6 +1858,9 @@ bce_vhci_tq_completion(struct bce_queue_sq *sq)
 					    vhci, tq,
 					    &tq->ctrl_status_msg);
 				}
+				if (tq->ctrl_state == BCE_VHCI_CTRL_DATA &&
+				    tq->ctrl_dir == UE_DIR_IN)
+					bce_vhci_ctrl_start_in_data(vhci, tq);
 			} else if (tq->ctrl_state ==
 			    BCE_VHCI_CTRL_STATUS &&
 			    tq->ctrl_dir == UE_DIR_OUT) {