1*af20f017SAbdelkader Boudih /*
2*af20f017SAbdelkader Boudih * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3*af20f017SAbdelkader Boudih *
4*af20f017SAbdelkader Boudih * SPDX-License-Identifier: BSD-2-Clause
5*af20f017SAbdelkader Boudih */
6*af20f017SAbdelkader Boudih
7*af20f017SAbdelkader Boudih #ifndef _DEV_FIREWIRE_FW_HELPERS_H_
8*af20f017SAbdelkader Boudih #define _DEV_FIREWIRE_FW_HELPERS_H_
9*af20f017SAbdelkader Boudih
10*af20f017SAbdelkader Boudih static __inline int
fw_xfer_timeout_cancel(struct fw_xfer * xfer)11*af20f017SAbdelkader Boudih fw_xfer_timeout_cancel(struct fw_xfer *xfer)
12*af20f017SAbdelkader Boudih {
13*af20f017SAbdelkader Boudih struct firewire_comm *fc = xfer->fc;
14*af20f017SAbdelkader Boudih struct fw_xfer *txfer;
15*af20f017SAbdelkader Boudih int can_cancel_sent, cancelled, remove_tlabel;
16*af20f017SAbdelkader Boudih
17*af20f017SAbdelkader Boudih can_cancel_sent = 0;
18*af20f017SAbdelkader Boudih cancelled = 0;
19*af20f017SAbdelkader Boudih remove_tlabel = 0;
20*af20f017SAbdelkader Boudih
21*af20f017SAbdelkader Boudih /*
22*af20f017SAbdelkader Boudih * FWXF_INQ xfers are still on the software AT queue and can be
23*af20f017SAbdelkader Boudih * removed safely. FWXF_SENT xfers have left the controller and are
24*af20f017SAbdelkader Boudih * waiting only for a split response, so removing their tlabel is enough
25*af20f017SAbdelkader Boudih * to make a late response miss this xfer. FWXF_START xfers are still
26*af20f017SAbdelkader Boudih * owned by the controller descriptor ring and must not be cancelled.
27*af20f017SAbdelkader Boudih */
28*af20f017SAbdelkader Boudih FW_GLOCK(fc);
29*af20f017SAbdelkader Boudih if ((xfer->flag & FWXF_INQ) != 0) {
30*af20f017SAbdelkader Boudih STAILQ_REMOVE(&xfer->q->q, xfer, fw_xfer, link);
31*af20f017SAbdelkader Boudih xfer->flag &= ~FWXF_INQ;
32*af20f017SAbdelkader Boudih cancelled = 1;
33*af20f017SAbdelkader Boudih remove_tlabel = 1;
34*af20f017SAbdelkader Boudih } else if ((xfer->flag & FWXF_SENT) != 0) {
35*af20f017SAbdelkader Boudih can_cancel_sent = 1;
36*af20f017SAbdelkader Boudih }
37*af20f017SAbdelkader Boudih FW_GUNLOCK(fc);
38*af20f017SAbdelkader Boudih
39*af20f017SAbdelkader Boudih if (remove_tlabel || can_cancel_sent) {
40*af20f017SAbdelkader Boudih mtx_lock(&fc->tlabel_lock);
41*af20f017SAbdelkader Boudih if (xfer->tl >= 0) {
42*af20f017SAbdelkader Boudih STAILQ_FOREACH(txfer, &fc->tlabels[xfer->tl], tlabel) {
43*af20f017SAbdelkader Boudih if (txfer == xfer)
44*af20f017SAbdelkader Boudih break;
45*af20f017SAbdelkader Boudih }
46*af20f017SAbdelkader Boudih if (txfer == xfer) {
47*af20f017SAbdelkader Boudih STAILQ_REMOVE(&fc->tlabels[xfer->tl], xfer,
48*af20f017SAbdelkader Boudih fw_xfer, tlabel);
49*af20f017SAbdelkader Boudih cancelled = 1;
50*af20f017SAbdelkader Boudih }
51*af20f017SAbdelkader Boudih xfer->tl = -1;
52*af20f017SAbdelkader Boudih }
53*af20f017SAbdelkader Boudih mtx_unlock(&fc->tlabel_lock);
54*af20f017SAbdelkader Boudih }
55*af20f017SAbdelkader Boudih
56*af20f017SAbdelkader Boudih if (cancelled) {
57*af20f017SAbdelkader Boudih mtx_lock(&fc->wait_lock);
58*af20f017SAbdelkader Boudih xfer->resp = ETIMEDOUT;
59*af20f017SAbdelkader Boudih xfer->flag |= FWXF_WAKE;
60*af20f017SAbdelkader Boudih mtx_unlock(&fc->wait_lock);
61*af20f017SAbdelkader Boudih wakeup(xfer);
62*af20f017SAbdelkader Boudih }
63*af20f017SAbdelkader Boudih
64*af20f017SAbdelkader Boudih return (cancelled);
65*af20f017SAbdelkader Boudih }
66*af20f017SAbdelkader Boudih
67*af20f017SAbdelkader Boudih /*
68*af20f017SAbdelkader Boudih * Wait for an async transfer to complete, with timeout.
69*af20f017SAbdelkader Boudih *
70*af20f017SAbdelkader Boudih * If the timeout fires while the controller still owns the transfer, keep the
71*af20f017SAbdelkader Boudih * xfer alive and continue waiting for the core completion path. Returning
72*af20f017SAbdelkader Boudih * early in that state lets callers free an xfer still referenced by the
73*af20f017SAbdelkader Boudih * controller descriptor ring.
74*af20f017SAbdelkader Boudih */
75*af20f017SAbdelkader Boudih static __inline int
fw_xferwait_timo(struct fw_xfer * xfer,int timo)76*af20f017SAbdelkader Boudih fw_xferwait_timo(struct fw_xfer *xfer, int timo)
77*af20f017SAbdelkader Boudih {
78*af20f017SAbdelkader Boudih struct firewire_comm *fc = xfer->fc;
79*af20f017SAbdelkader Boudih struct mtx *lock = &fc->wait_lock;
80*af20f017SAbdelkader Boudih int err = 0;
81*af20f017SAbdelkader Boudih int timedout = 0;
82*af20f017SAbdelkader Boudih
83*af20f017SAbdelkader Boudih mtx_lock(lock);
84*af20f017SAbdelkader Boudih while ((xfer->flag & FWXF_WAKE) == 0) {
85*af20f017SAbdelkader Boudih err = msleep(xfer, lock, PWAIT, "fwxfer", timo);
86*af20f017SAbdelkader Boudih if (err == EWOULDBLOCK) {
87*af20f017SAbdelkader Boudih mtx_unlock(lock);
88*af20f017SAbdelkader Boudih if (fw_xfer_timeout_cancel(xfer))
89*af20f017SAbdelkader Boudih return (ETIMEDOUT);
90*af20f017SAbdelkader Boudih timedout = 1;
91*af20f017SAbdelkader Boudih mtx_lock(lock);
92*af20f017SAbdelkader Boudih continue;
93*af20f017SAbdelkader Boudih }
94*af20f017SAbdelkader Boudih if (err) {
95*af20f017SAbdelkader Boudih mtx_unlock(lock);
96*af20f017SAbdelkader Boudih return (err);
97*af20f017SAbdelkader Boudih }
98*af20f017SAbdelkader Boudih }
99*af20f017SAbdelkader Boudih mtx_unlock(lock);
100*af20f017SAbdelkader Boudih if (timedout && xfer->resp == ETIMEDOUT)
101*af20f017SAbdelkader Boudih return (ETIMEDOUT);
102*af20f017SAbdelkader Boudih return (0);
103*af20f017SAbdelkader Boudih }
104*af20f017SAbdelkader Boudih
105*af20f017SAbdelkader Boudih /*
106*af20f017SAbdelkader Boudih * Submit an async request and wait for completion with timeout.
107*af20f017SAbdelkader Boudih */
108*af20f017SAbdelkader Boudih static __inline int
fw_xfer_request_wait(struct firewire_comm * fc,struct fw_xfer * xfer,int timo)109*af20f017SAbdelkader Boudih fw_xfer_request_wait(struct firewire_comm *fc, struct fw_xfer *xfer, int timo)
110*af20f017SAbdelkader Boudih {
111*af20f017SAbdelkader Boudih int err;
112*af20f017SAbdelkader Boudih
113*af20f017SAbdelkader Boudih err = fw_asyreq(fc, -1, xfer);
114*af20f017SAbdelkader Boudih if (err != 0)
115*af20f017SAbdelkader Boudih return (err);
116*af20f017SAbdelkader Boudih return (fw_xferwait_timo(xfer, timo));
117*af20f017SAbdelkader Boudih }
118*af20f017SAbdelkader Boudih
119*af20f017SAbdelkader Boudih 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)120*af20f017SAbdelkader Boudih fw_read_quadlet(struct firewire_comm *fc, struct malloc_type *mtype,
121*af20f017SAbdelkader Boudih uint16_t dst, uint8_t spd, uint16_t addr_hi, uint32_t addr_lo,
122*af20f017SAbdelkader Boudih uint32_t *val)
123*af20f017SAbdelkader Boudih {
124*af20f017SAbdelkader Boudih struct fw_xfer *xfer;
125*af20f017SAbdelkader Boudih struct fw_pkt *fp;
126*af20f017SAbdelkader Boudih int err;
127*af20f017SAbdelkader Boudih
128*af20f017SAbdelkader Boudih xfer = fw_xfer_alloc_buf(mtype, 0, 4);
129*af20f017SAbdelkader Boudih if (xfer == NULL)
130*af20f017SAbdelkader Boudih return (ENOMEM);
131*af20f017SAbdelkader Boudih
132*af20f017SAbdelkader Boudih xfer->send.spd = spd;
133*af20f017SAbdelkader Boudih xfer->fc = fc;
134*af20f017SAbdelkader Boudih xfer->hand = fw_xferwake;
135*af20f017SAbdelkader Boudih
136*af20f017SAbdelkader Boudih fp = &xfer->send.hdr;
137*af20f017SAbdelkader Boudih fp->mode.rreqq.tcode = FWTCODE_RREQQ;
138*af20f017SAbdelkader Boudih fp->mode.rreqq.dst = dst;
139*af20f017SAbdelkader Boudih fp->mode.rreqq.dest_hi = addr_hi;
140*af20f017SAbdelkader Boudih fp->mode.rreqq.dest_lo = addr_lo;
141*af20f017SAbdelkader Boudih
142*af20f017SAbdelkader Boudih err = fw_xfer_request_wait(fc, xfer, 2 * hz);
143*af20f017SAbdelkader Boudih if (err != 0)
144*af20f017SAbdelkader Boudih goto out;
145*af20f017SAbdelkader Boudih
146*af20f017SAbdelkader Boudih if (xfer->resp == 0 &&
147*af20f017SAbdelkader Boudih xfer->recv.hdr.mode.rresq.rtcode == FWRCODE_COMPLETE)
148*af20f017SAbdelkader Boudih *val = ntohl(xfer->recv.hdr.mode.rresq.data);
149*af20f017SAbdelkader Boudih else
150*af20f017SAbdelkader Boudih err = EIO;
151*af20f017SAbdelkader Boudih out:
152*af20f017SAbdelkader Boudih fw_xfer_free_buf(xfer);
153*af20f017SAbdelkader Boudih return (err);
154*af20f017SAbdelkader Boudih }
155*af20f017SAbdelkader Boudih
156*af20f017SAbdelkader Boudih 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)157*af20f017SAbdelkader Boudih fw_write_quadlet(struct firewire_comm *fc, struct malloc_type *mtype,
158*af20f017SAbdelkader Boudih uint16_t dst, uint8_t spd, uint16_t addr_hi, uint32_t addr_lo,
159*af20f017SAbdelkader Boudih uint32_t val)
160*af20f017SAbdelkader Boudih {
161*af20f017SAbdelkader Boudih struct fw_xfer *xfer;
162*af20f017SAbdelkader Boudih struct fw_pkt *fp;
163*af20f017SAbdelkader Boudih int err;
164*af20f017SAbdelkader Boudih
165*af20f017SAbdelkader Boudih xfer = fw_xfer_alloc_buf(mtype, 0, 0);
166*af20f017SAbdelkader Boudih if (xfer == NULL)
167*af20f017SAbdelkader Boudih return (ENOMEM);
168*af20f017SAbdelkader Boudih
169*af20f017SAbdelkader Boudih xfer->send.spd = spd;
170*af20f017SAbdelkader Boudih xfer->fc = fc;
171*af20f017SAbdelkader Boudih xfer->hand = fw_xferwake;
172*af20f017SAbdelkader Boudih
173*af20f017SAbdelkader Boudih fp = &xfer->send.hdr;
174*af20f017SAbdelkader Boudih fp->mode.wreqq.tcode = FWTCODE_WREQQ;
175*af20f017SAbdelkader Boudih fp->mode.wreqq.dst = dst;
176*af20f017SAbdelkader Boudih fp->mode.wreqq.dest_hi = addr_hi;
177*af20f017SAbdelkader Boudih fp->mode.wreqq.dest_lo = addr_lo;
178*af20f017SAbdelkader Boudih fp->mode.wreqq.data = htonl(val);
179*af20f017SAbdelkader Boudih
180*af20f017SAbdelkader Boudih err = fw_xfer_request_wait(fc, xfer, 2 * hz);
181*af20f017SAbdelkader Boudih if (err != 0)
182*af20f017SAbdelkader Boudih goto out;
183*af20f017SAbdelkader Boudih
184*af20f017SAbdelkader Boudih if (xfer->resp != 0 ||
185*af20f017SAbdelkader Boudih xfer->recv.hdr.mode.wres.rtcode != FWRCODE_COMPLETE)
186*af20f017SAbdelkader Boudih err = EIO;
187*af20f017SAbdelkader Boudih out:
188*af20f017SAbdelkader Boudih fw_xfer_free_buf(xfer);
189*af20f017SAbdelkader Boudih return (err);
190*af20f017SAbdelkader Boudih }
191*af20f017SAbdelkader Boudih
192*af20f017SAbdelkader Boudih static __inline void
fw_iso_init_chunks(struct fw_xferq * xferq)193*af20f017SAbdelkader Boudih fw_iso_init_chunks(struct fw_xferq *xferq)
194*af20f017SAbdelkader Boudih {
195*af20f017SAbdelkader Boudih struct mbuf *m;
196*af20f017SAbdelkader Boudih int i;
197*af20f017SAbdelkader Boudih
198*af20f017SAbdelkader Boudih STAILQ_INIT(&xferq->stvalid);
199*af20f017SAbdelkader Boudih STAILQ_INIT(&xferq->stfree);
200*af20f017SAbdelkader Boudih STAILQ_INIT(&xferq->stdma);
201*af20f017SAbdelkader Boudih xferq->stproc = NULL;
202*af20f017SAbdelkader Boudih
203*af20f017SAbdelkader Boudih for (i = 0; i < xferq->bnchunk; i++) {
204*af20f017SAbdelkader Boudih m = m_getcl(M_WAITOK, MT_DATA, M_PKTHDR);
205*af20f017SAbdelkader Boudih xferq->bulkxfer[i].mbuf = m;
206*af20f017SAbdelkader Boudih m->m_len = m->m_pkthdr.len = m->m_ext.ext_size;
207*af20f017SAbdelkader Boudih STAILQ_INSERT_TAIL(&xferq->stfree, &xferq->bulkxfer[i], link);
208*af20f017SAbdelkader Boudih }
209*af20f017SAbdelkader Boudih }
210*af20f017SAbdelkader Boudih
211*af20f017SAbdelkader Boudih static __inline void
fw_iso_free_chunks(struct fw_xferq * xferq,struct malloc_type * mtype)212*af20f017SAbdelkader Boudih fw_iso_free_chunks(struct fw_xferq *xferq, struct malloc_type *mtype)
213*af20f017SAbdelkader Boudih {
214*af20f017SAbdelkader Boudih int i;
215*af20f017SAbdelkader Boudih
216*af20f017SAbdelkader Boudih for (i = 0; i < xferq->bnchunk; i++) {
217*af20f017SAbdelkader Boudih if (xferq->bulkxfer[i].mbuf != NULL)
218*af20f017SAbdelkader Boudih m_freem(xferq->bulkxfer[i].mbuf);
219*af20f017SAbdelkader Boudih }
220*af20f017SAbdelkader Boudih free(xferq->bulkxfer, mtype);
221*af20f017SAbdelkader Boudih xferq->bulkxfer = NULL;
222*af20f017SAbdelkader Boudih }
223*af20f017SAbdelkader Boudih
224*af20f017SAbdelkader Boudih /*
225*af20f017SAbdelkader Boudih * Dequeue an ISO receive mbuf, replace with a fresh one.
226*af20f017SAbdelkader Boudih * Returns the consumed mbuf, or NULL on error/allocation failure.
227*af20f017SAbdelkader Boudih */
228*af20f017SAbdelkader Boudih static __inline struct mbuf *
fw_iso_dequeue(struct fw_xferq * xferq,struct fw_bulkxfer * sxfer,struct firewire_comm * fc)229*af20f017SAbdelkader Boudih fw_iso_dequeue(struct fw_xferq *xferq, struct fw_bulkxfer *sxfer,
230*af20f017SAbdelkader Boudih struct firewire_comm *fc)
231*af20f017SAbdelkader Boudih {
232*af20f017SAbdelkader Boudih struct fw_pkt *fp;
233*af20f017SAbdelkader Boudih struct mbuf *m, *m0;
234*af20f017SAbdelkader Boudih
235*af20f017SAbdelkader Boudih fp = mtod(sxfer->mbuf, struct fw_pkt *);
236*af20f017SAbdelkader Boudih if (fc->irx_post != NULL)
237*af20f017SAbdelkader Boudih fc->irx_post(fc, fp->mode.ld);
238*af20f017SAbdelkader Boudih
239*af20f017SAbdelkader Boudih m = sxfer->mbuf;
240*af20f017SAbdelkader Boudih
241*af20f017SAbdelkader Boudih m0 = m_getcl(M_NOWAIT, MT_DATA, M_PKTHDR);
242*af20f017SAbdelkader Boudih if (m0 == NULL) {
243*af20f017SAbdelkader Boudih /* Allocation failed; recycle the original mbuf. */
244*af20f017SAbdelkader Boudih STAILQ_INSERT_TAIL(&xferq->stfree, sxfer, link);
245*af20f017SAbdelkader Boudih return (NULL);
246*af20f017SAbdelkader Boudih }
247*af20f017SAbdelkader Boudih
248*af20f017SAbdelkader Boudih m0->m_len = m0->m_pkthdr.len = m0->m_ext.ext_size;
249*af20f017SAbdelkader Boudih sxfer->mbuf = m0;
250*af20f017SAbdelkader Boudih STAILQ_INSERT_TAIL(&xferq->stfree, sxfer, link);
251*af20f017SAbdelkader Boudih
252*af20f017SAbdelkader Boudih if (sxfer->resp != 0) {
253*af20f017SAbdelkader Boudih m_freem(m);
254*af20f017SAbdelkader Boudih return (NULL);
255*af20f017SAbdelkader Boudih }
256*af20f017SAbdelkader Boudih
257*af20f017SAbdelkader Boudih return (m);
258*af20f017SAbdelkader Boudih }
259*af20f017SAbdelkader Boudih
260*af20f017SAbdelkader Boudih static __inline void
fw_iso_rearm(struct fw_xferq * xferq,struct firewire_comm * fc,int dma_ch)261*af20f017SAbdelkader Boudih fw_iso_rearm(struct fw_xferq *xferq, struct firewire_comm *fc, int dma_ch)
262*af20f017SAbdelkader Boudih {
263*af20f017SAbdelkader Boudih
264*af20f017SAbdelkader Boudih if (STAILQ_FIRST(&xferq->stfree) != NULL && dma_ch >= 0)
265*af20f017SAbdelkader Boudih fc->irx_enable(fc, dma_ch);
266*af20f017SAbdelkader Boudih }
267*af20f017SAbdelkader Boudih
268*af20f017SAbdelkader Boudih static __inline void
fw_iso_wait_inactive_locked(struct mtx * mtx,int * active,const char * wmesg)269*af20f017SAbdelkader Boudih fw_iso_wait_inactive_locked(struct mtx *mtx, int *active, const char *wmesg)
270*af20f017SAbdelkader Boudih {
271*af20f017SAbdelkader Boudih
272*af20f017SAbdelkader Boudih mtx_assert(mtx, MA_OWNED);
273*af20f017SAbdelkader Boudih while (*active)
274*af20f017SAbdelkader Boudih msleep(active, mtx, PWAIT, wmesg, hz);
275*af20f017SAbdelkader Boudih }
276*af20f017SAbdelkader Boudih
277*af20f017SAbdelkader Boudih 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)278*af20f017SAbdelkader Boudih fw_iso_rearm_done(struct fw_xferq *xferq, struct firewire_comm *fc,
279*af20f017SAbdelkader Boudih struct mtx *mtx, int *active, int *cur_dma_ch, int dma_ch)
280*af20f017SAbdelkader Boudih {
281*af20f017SAbdelkader Boudih
282*af20f017SAbdelkader Boudih mtx_lock(mtx);
283*af20f017SAbdelkader Boudih if (*cur_dma_ch == dma_ch)
284*af20f017SAbdelkader Boudih fw_iso_rearm(xferq, fc, dma_ch);
285*af20f017SAbdelkader Boudih *active = 0;
286*af20f017SAbdelkader Boudih wakeup(active);
287*af20f017SAbdelkader Boudih mtx_unlock(mtx);
288*af20f017SAbdelkader Boudih }
289*af20f017SAbdelkader Boudih
290*af20f017SAbdelkader Boudih #endif /* _DEV_FIREWIRE_FW_HELPERS_H_ */
291