xref: /freebsd/sys/dev/firewire/fw_helpers.h (revision af20f017ec0b75456d9031059bc79be01f141c3b)
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