The branch main has been updated by adrian:

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

commit af20f017ec0b75456d9031059bc79be01f141c3b
Author:     Abdelkader Boudih <[email protected]>
AuthorDate: 2026-06-25 02:18:52 +0000
Commit:     Adrian Chadd <[email protected]>
CommitDate: 2026-06-25 02:18:55 +0000

    firewire: add shared helpers for ISO receive drivers
    
    Add fw_helpers.h with common static inline helpers for FireWire ISO
    receive drivers: async xfer wait with timeout and tlabel cleanup,
    quadlet read/write, and ISO mbuf management.
    
    Reviewed by:    adrian
    Differential Revision:  https://reviews.freebsd.org/D57684
---
 sys/dev/firewire/fw_helpers.h | 290 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 290 insertions(+)

diff --git a/sys/dev/firewire/fw_helpers.h b/sys/dev/firewire/fw_helpers.h
new file mode 100644
index 000000000000..863b06441217
--- /dev/null
+++ b/sys/dev/firewire/fw_helpers.h
@@ -0,0 +1,290 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <[email protected]>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+#ifndef _DEV_FIREWIRE_FW_HELPERS_H_
+#define _DEV_FIREWIRE_FW_HELPERS_H_
+
+static __inline int
+fw_xfer_timeout_cancel(struct fw_xfer *xfer)
+{
+       struct firewire_comm *fc = xfer->fc;
+       struct fw_xfer *txfer;
+       int can_cancel_sent, cancelled, remove_tlabel;
+
+       can_cancel_sent = 0;
+       cancelled = 0;
+       remove_tlabel = 0;
+
+       /*
+        * FWXF_INQ xfers are still on the software AT queue and can be
+        * removed safely.  FWXF_SENT xfers have left the controller and are
+        * waiting only for a split response, so removing their tlabel is enough
+        * to make a late response miss this xfer.  FWXF_START xfers are still
+        * owned by the controller descriptor ring and must not be cancelled.
+        */
+       FW_GLOCK(fc);
+       if ((xfer->flag & FWXF_INQ) != 0) {
+               STAILQ_REMOVE(&xfer->q->q, xfer, fw_xfer, link);
+               xfer->flag &= ~FWXF_INQ;
+               cancelled = 1;
+               remove_tlabel = 1;
+       } else if ((xfer->flag & FWXF_SENT) != 0) {
+               can_cancel_sent = 1;
+       }
+       FW_GUNLOCK(fc);
+
+       if (remove_tlabel || can_cancel_sent) {
+               mtx_lock(&fc->tlabel_lock);
+               if (xfer->tl >= 0) {
+                       STAILQ_FOREACH(txfer, &fc->tlabels[xfer->tl], tlabel) {
+                               if (txfer == xfer)
+                                       break;
+                       }
+                       if (txfer == xfer) {
+                               STAILQ_REMOVE(&fc->tlabels[xfer->tl], xfer,
+                                   fw_xfer, tlabel);
+                               cancelled = 1;
+                       }
+                       xfer->tl = -1;
+               }
+               mtx_unlock(&fc->tlabel_lock);
+       }
+
+       if (cancelled) {
+               mtx_lock(&fc->wait_lock);
+               xfer->resp = ETIMEDOUT;
+               xfer->flag |= FWXF_WAKE;
+               mtx_unlock(&fc->wait_lock);
+               wakeup(xfer);
+       }
+
+       return (cancelled);
+}
+
+/*
+ * Wait for an async transfer to complete, with timeout.
+ *
+ * If the timeout fires while the controller still owns the transfer, keep the
+ * xfer alive and continue waiting for the core completion path.  Returning
+ * early in that state lets callers free an xfer still referenced by the
+ * controller descriptor ring.
+ */
+static __inline int
+fw_xferwait_timo(struct fw_xfer *xfer, int timo)
+{
+       struct firewire_comm *fc = xfer->fc;
+       struct mtx *lock = &fc->wait_lock;
+       int err = 0;
+       int timedout = 0;
+
+       mtx_lock(lock);
+       while ((xfer->flag & FWXF_WAKE) == 0) {
+               err = msleep(xfer, lock, PWAIT, "fwxfer", timo);
+               if (err == EWOULDBLOCK) {
+                       mtx_unlock(lock);
+                       if (fw_xfer_timeout_cancel(xfer))
+                               return (ETIMEDOUT);
+                       timedout = 1;
+                       mtx_lock(lock);
+                       continue;
+               }
+               if (err) {
+                       mtx_unlock(lock);
+                       return (err);
+               }
+       }
+       mtx_unlock(lock);
+       if (timedout && xfer->resp == ETIMEDOUT)
+               return (ETIMEDOUT);
+       return (0);
+}
+
+/*
+ * Submit an async request and wait for completion with timeout.
+ */
+static __inline int
+fw_xfer_request_wait(struct firewire_comm *fc, struct fw_xfer *xfer, int timo)
+{
+       int err;
+
+       err = fw_asyreq(fc, -1, xfer);
+       if (err != 0)
+               return (err);
+       return (fw_xferwait_timo(xfer, timo));
+}
+
+static __inline int
+fw_read_quadlet(struct firewire_comm *fc, struct malloc_type *mtype,
+    uint16_t dst, uint8_t spd, uint16_t addr_hi, uint32_t addr_lo,
+    uint32_t *val)
+{
+       struct fw_xfer *xfer;
+       struct fw_pkt *fp;
+       int err;
+
+       xfer = fw_xfer_alloc_buf(mtype, 0, 4);
+       if (xfer == NULL)
+               return (ENOMEM);
+
+       xfer->send.spd = spd;
+       xfer->fc = fc;
+       xfer->hand = fw_xferwake;
+
+       fp = &xfer->send.hdr;
+       fp->mode.rreqq.tcode = FWTCODE_RREQQ;
+       fp->mode.rreqq.dst = dst;
+       fp->mode.rreqq.dest_hi = addr_hi;
+       fp->mode.rreqq.dest_lo = addr_lo;
+
+       err = fw_xfer_request_wait(fc, xfer, 2 * hz);
+       if (err != 0)
+               goto out;
+
+       if (xfer->resp == 0 &&
+           xfer->recv.hdr.mode.rresq.rtcode == FWRCODE_COMPLETE)
+               *val = ntohl(xfer->recv.hdr.mode.rresq.data);
+       else
+               err = EIO;
+out:
+       fw_xfer_free_buf(xfer);
+       return (err);
+}
+
+static __inline int
+fw_write_quadlet(struct firewire_comm *fc, struct malloc_type *mtype,
+    uint16_t dst, uint8_t spd, uint16_t addr_hi, uint32_t addr_lo,
+    uint32_t val)
+{
+       struct fw_xfer *xfer;
+       struct fw_pkt *fp;
+       int err;
+
+       xfer = fw_xfer_alloc_buf(mtype, 0, 0);
+       if (xfer == NULL)
+               return (ENOMEM);
+
+       xfer->send.spd = spd;
+       xfer->fc = fc;
+       xfer->hand = fw_xferwake;
+
+       fp = &xfer->send.hdr;
+       fp->mode.wreqq.tcode = FWTCODE_WREQQ;
+       fp->mode.wreqq.dst = dst;
+       fp->mode.wreqq.dest_hi = addr_hi;
+       fp->mode.wreqq.dest_lo = addr_lo;
+       fp->mode.wreqq.data = htonl(val);
+
+       err = fw_xfer_request_wait(fc, xfer, 2 * hz);
+       if (err != 0)
+               goto out;
+
+       if (xfer->resp != 0 ||
+           xfer->recv.hdr.mode.wres.rtcode != FWRCODE_COMPLETE)
+               err = EIO;
+out:
+       fw_xfer_free_buf(xfer);
+       return (err);
+}
+
+static __inline void
+fw_iso_init_chunks(struct fw_xferq *xferq)
+{
+       struct mbuf *m;
+       int i;
+
+       STAILQ_INIT(&xferq->stvalid);
+       STAILQ_INIT(&xferq->stfree);
+       STAILQ_INIT(&xferq->stdma);
+       xferq->stproc = NULL;
+
+       for (i = 0; i < xferq->bnchunk; i++) {
+               m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
+               xferq->bulkxfer[i].mbuf = m;
+               m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
+               STAILQ_INSERT_TAIL(&xferq->stfree, &xferq->bulkxfer[i], link);
+       }
+}
+
+static __inline void
+fw_iso_free_chunks(struct fw_xferq *xferq, struct malloc_type *mtype)
+{
+       int i;
+
+       for (i = 0; i < xferq->bnchunk; i++) {
+               if (xferq->bulkxfer[i].mbuf != NULL)
+                       m_freem(xferq->bulkxfer[i].mbuf);
+       }
+       free(xferq->bulkxfer, mtype);
+       xferq->bulkxfer = NULL;
+}
+
+/*
+ * Dequeue an ISO receive mbuf, replace with a fresh one.
+ * Returns the consumed mbuf, or NULL on error/allocation failure.
+ */
+static __inline struct mbuf *
+fw_iso_dequeue(struct fw_xferq *xferq, struct fw_bulkxfer *sxfer,
+    struct firewire_comm *fc)
+{
+       struct fw_pkt *fp;
+       struct mbuf *m, *m0;
+
+       fp = mtod(sxfer->mbuf, struct fw_pkt *);
+       if (fc->irx_post != NULL)
+               fc->irx_post(fc, fp->mode.ld);
+
+       m = sxfer->mbuf;
+
+       m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
+       if (m0 == NULL) {
+               /* Allocation failed; recycle the original mbuf. */
+               STAILQ_INSERT_TAIL(&xferq->stfree, sxfer, link);
+               return (NULL);
+       }
+
+       m0->m_len = m0->m_pkthdr.len = m0->m_ext.ext_size;
+       sxfer->mbuf = m0;
+       STAILQ_INSERT_TAIL(&xferq->stfree, sxfer, link);
+
+       if (sxfer->resp != 0) {
+               m_freem(m);
+               return (NULL);
+       }
+
+       return (m);
+}
+
+static __inline void
+fw_iso_rearm(struct fw_xferq *xferq, struct firewire_comm *fc, int dma_ch)
+{
+
+       if (STAILQ_FIRST(&xferq->stfree) != NULL && dma_ch >= 0)
+               fc->irx_enable(fc, dma_ch);
+}
+
+static __inline void
+fw_iso_wait_inactive_locked(struct mtx *mtx, int *active, const char *wmesg)
+{
+
+       mtx_assert(mtx, MA_OWNED);
+       while (*active)
+               msleep(active, mtx, PWAIT, wmesg, hz);
+}
+
+static __inline void
+fw_iso_rearm_done(struct fw_xferq *xferq, struct firewire_comm *fc,
+    struct mtx *mtx, int *active, int *cur_dma_ch, int dma_ch)
+{
+
+       mtx_lock(mtx);
+       if (*cur_dma_ch == dma_ch)
+               fw_iso_rearm(xferq, fc, dma_ch);
+       *active = 0;
+       wakeup(active);
+       mtx_unlock(mtx);
+}
+
+#endif /* _DEV_FIREWIRE_FW_HELPERS_H_ */

Reply via email to