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