1 /* 2 * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 /* 8 * fwisound(4) - Apple FireWire audio driver 9 * 10 * Protocol reverse-engineered by Clemens Ladisch (Linux isight_audio). 11 */ 12 13 #include <sys/param.h> 14 #include <sys/systm.h> 15 #include <sys/module.h> 16 #include <sys/bus.h> 17 #include <sys/kernel.h> 18 #include <sys/malloc.h> 19 #include <sys/lock.h> 20 #include <sys/mutex.h> 21 #include <sys/sysctl.h> 22 #include <sys/taskqueue.h> 23 #include <sys/mbuf.h> 24 25 #include <dev/firewire/firewire.h> 26 #include <dev/firewire/firewirereg.h> 27 #include <dev/firewire/iec13213.h> 28 #include <dev/firewire/fwisound.h> 29 #include <dev/firewire/fw_helpers.h> 30 31 static MALLOC_DEFINE(M_FWISOUND, "fwisound", "Apple FireWire Audio"); 32 33 static int debug = 0; 34 static int iso_channel = FWISOUND_ISO_CHANNEL; 35 SYSCTL_DECL(_hw_firewire); 36 static SYSCTL_NODE(_hw_firewire, OID_AUTO, fwisound, 37 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "Apple FireWire Audio"); 38 SYSCTL_INT(_hw_firewire_fwisound, OID_AUTO, debug, CTLFLAG_RWTUN, &debug, 0, 39 "fwisound debug level"); 40 SYSCTL_INT(_hw_firewire_fwisound, OID_AUTO, iso_channel, CTLFLAG_RWTUN, 41 &iso_channel, 0, "ISO channel for isochronous receive (default 1)"); 42 43 #define FWISOUND_DEBUG(lev, fmt, ...) \ 44 do { \ 45 if (debug >= (lev)) \ 46 printf("fwisound: " fmt, ## __VA_ARGS__); \ 47 } while (0) 48 49 static int fwisound_probe(device_t); 50 static int fwisound_attach(device_t); 51 static int fwisound_detach(device_t); 52 static void fwisound_probe_task(void *, int); 53 static void fwisound_start_task(void *, int); 54 static void fwisound_stop_task(void *, int); 55 static void fwisound_iso_input(struct fw_xferq *); 56 57 static int 58 fwisound_read_quadlet(struct fwisound_softc *sc, uint32_t offset, uint32_t *val) 59 { 60 uint16_t dst; 61 uint8_t spd; 62 int err; 63 64 FWISOUND_LOCK(sc); 65 if (sc->fwdev == NULL) { 66 FWISOUND_UNLOCK(sc); 67 return (ENXIO); 68 } 69 dst = FWLOCALBUS | sc->fwdev->dst; 70 spd = min(sc->fwdev->speed, FWSPD_S400); 71 FWISOUND_UNLOCK(sc); 72 73 err = fw_read_quadlet(sc->fd.fc, M_FWISOUND, dst, spd, 74 sc->cmd_hi, sc->cmd_lo + offset, val); 75 if (err) 76 FWISOUND_DEBUG(1, "read_quadlet: offset=0x%x err=%d\n", 77 offset, err); 78 return (err); 79 } 80 81 static int 82 fwisound_write_quadlet(struct fwisound_softc *sc, uint32_t offset, uint32_t val) 83 { 84 uint16_t dst; 85 uint8_t spd; 86 int err; 87 88 FWISOUND_LOCK(sc); 89 if (sc->fwdev == NULL) { 90 FWISOUND_UNLOCK(sc); 91 return (ENXIO); 92 } 93 dst = FWLOCALBUS | sc->fwdev->dst; 94 spd = min(sc->fwdev->speed, FWSPD_S400); 95 FWISOUND_UNLOCK(sc); 96 97 err = fw_write_quadlet(sc->fd.fc, M_FWISOUND, dst, spd, 98 sc->cmd_hi, sc->cmd_lo + offset, val); 99 if (err) 100 FWISOUND_DEBUG(1, "write_quadlet: offset=0x%x err=%d\n", 101 offset, err); 102 return (err); 103 } 104 105 /* 106 * Extract the audio command base register (key 0x40) from the unit directory. 107 */ 108 static uint32_t 109 fwisound_find_audio_base(struct fw_unit *unit) 110 { 111 struct fw_device *fwdev = unit->fwdev; 112 struct csrdirectory *udir; 113 struct csrreg *reg; 114 uint32_t udir_qoff, udir_len, rom_quads; 115 int i; 116 117 rom_quads = CSRROMSIZE / 4; 118 udir_qoff = unit->dir_offset / 4; 119 if (udir_qoff >= rom_quads) 120 return (0); 121 122 udir = (struct csrdirectory *)&fwdev->csrrom[udir_qoff]; 123 udir_len = udir->crc_len; 124 if (udir_qoff + 1 + udir_len > rom_quads) 125 return (0); 126 127 for (i = 0; i < (int)udir_len; i++) { 128 if (udir_qoff + 1 + i >= rom_quads) 129 break; 130 reg = &udir->entry[i]; 131 if (reg->key == IIDC_CROM_CMD_BASE && reg->val != 0) 132 return (reg->val); 133 } 134 return (0); 135 } 136 137 static void 138 fwisound_probe_task(void *arg, int pending __unused) 139 { 140 struct fwisound_softc *sc = (struct fwisound_softc *)arg; 141 uint32_t val; 142 int err; 143 144 if (sc->state == FWISOUND_STATE_DETACHING || sc->fwdev == NULL) 145 return; 146 147 err = fwisound_write_quadlet(sc, FWISOUND_REG_SAMPLE_RATE, 148 FWISOUND_RATE_48000); 149 if (err) { 150 device_printf(sc->dev, 151 "failed to set sample rate: %d\n", err); 152 return; 153 } 154 155 if (fwisound_read_quadlet(sc, FWISOUND_REG_GAIN_RAW_START, &val) == 0) 156 sc->gain_raw_min = (int32_t)val; 157 if (fwisound_read_quadlet(sc, FWISOUND_REG_GAIN_RAW_END, &val) == 0) 158 sc->gain_raw_max = (int32_t)val; 159 160 FWISOUND_LOCK(sc); 161 if (sc->state == FWISOUND_STATE_DETACHING) { 162 FWISOUND_UNLOCK(sc); 163 return; 164 } 165 sc->state = FWISOUND_STATE_PROBED; 166 FWISOUND_UNLOCK(sc); 167 } 168 169 static void 170 fwisound_start_task(void *arg, int pending __unused) 171 { 172 struct fwisound_softc *sc = (struct fwisound_softc *)arg; 173 174 if (sc->state == FWISOUND_STATE_DETACHING) 175 return; 176 177 fwisound_iso_start(sc); 178 } 179 180 static void 181 fwisound_stop_task(void *arg, int pending __unused) 182 { 183 struct fwisound_softc *sc = (struct fwisound_softc *)arg; 184 185 fwisound_iso_stop(sc); 186 187 FWISOUND_LOCK(sc); 188 if (sc->state != FWISOUND_STATE_DETACHING) 189 sc->state = FWISOUND_STATE_PROBED; 190 FWISOUND_UNLOCK(sc); 191 } 192 193 int 194 fwisound_iso_start(struct fwisound_softc *sc) 195 { 196 struct firewire_comm *fc = sc->fd.fc; 197 struct fw_xferq *xferq; 198 uint32_t val; 199 uint8_t speed; 200 int dma_ch, err; 201 202 mtx_assert(&sc->mtx, MA_NOTOWNED); 203 204 FWISOUND_LOCK(sc); 205 if (sc->dma_ch >= 0 || sc->state == FWISOUND_STATE_STREAMING) { 206 FWISOUND_UNLOCK(sc); 207 return (0); 208 } 209 if (sc->state == FWISOUND_STATE_DETACHING || sc->fwdev == NULL) { 210 FWISOUND_UNLOCK(sc); 211 return (ENXIO); 212 } 213 speed = min(sc->fwdev->speed, FWSPD_S400); 214 FWISOUND_UNLOCK(sc); 215 216 dma_ch = fw_open_isodma(fc, 0); 217 if (dma_ch < 0) { 218 device_printf(sc->dev, "no IR DMA channel available\n"); 219 return (EBUSY); 220 } 221 222 FWISOUND_LOCK(sc); 223 if (sc->dma_ch >= 0) { 224 FWISOUND_UNLOCK(sc); 225 fc->ir[dma_ch]->flag &= ~FWXFERQ_OPEN; 226 return (0); 227 } 228 FWISOUND_UNLOCK(sc); 229 230 xferq = fc->ir[dma_ch]; 231 xferq->flag |= FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_STREAM; 232 233 xferq->flag &= ~FWXFERQ_CHTAGMASK; 234 xferq->flag |= sc->iso_channel & FWXFERQ_CHTAGMASK; 235 236 xferq->sc = (caddr_t)sc; 237 xferq->hand = fwisound_iso_input; 238 xferq->bnchunk = FWISOUND_ISO_NCHUNK; 239 xferq->bnpacket = 1; 240 xferq->psize = FWISOUND_ISO_PKTSIZE; 241 xferq->queued = 0; 242 xferq->buf = NULL; 243 244 xferq->bulkxfer = malloc( 245 sizeof(struct fw_bulkxfer) * xferq->bnchunk, 246 M_FWISOUND, M_WAITOK | M_ZERO); 247 248 fw_iso_init_chunks(xferq); 249 250 sc->sample_total = 0; 251 252 val = sc->iso_channel | ((uint32_t)speed << 16); 253 err = fwisound_write_quadlet(sc, FWISOUND_REG_ISO_TX_CONFIG, val); 254 if (err) { 255 device_printf(sc->dev, 256 "failed to set ISO_TX_CONFIG: %d\n", err); 257 goto fail; 258 } 259 260 err = fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 261 FWISOUND_AUDIO_ENABLE); 262 if (err) { 263 device_printf(sc->dev, 264 "failed to enable audio: %d\n", err); 265 goto fail; 266 } 267 268 err = fc->irx_enable(fc, dma_ch); 269 if (err) { 270 device_printf(sc->dev, "failed to start IR DMA: %d\n", err); 271 fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 0); 272 goto fail; 273 } 274 275 FWISOUND_LOCK(sc); 276 if (sc->state == FWISOUND_STATE_DETACHING) { 277 FWISOUND_UNLOCK(sc); 278 fc->irx_disable(fc, dma_ch); 279 fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 0); 280 err = ENXIO; 281 goto fail; 282 } 283 sc->dma_ch = dma_ch; 284 sc->state = FWISOUND_STATE_STREAMING; 285 FWISOUND_UNLOCK(sc); 286 287 return (0); 288 289 fail: 290 fw_iso_free_chunks(xferq, M_FWISOUND); 291 xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM | 292 FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK); 293 xferq->hand = NULL; 294 sc->dma_ch = -1; 295 return (err); 296 } 297 298 void 299 fwisound_iso_stop(struct fwisound_softc *sc) 300 { 301 struct firewire_comm *fc = sc->fd.fc; 302 struct fw_xferq *xferq; 303 int dma_ch; 304 305 FWISOUND_LOCK(sc); 306 dma_ch = sc->dma_ch; 307 if (dma_ch < 0) { 308 FWISOUND_UNLOCK(sc); 309 return; 310 } 311 sc->dma_ch = -1; 312 FWISOUND_UNLOCK(sc); 313 314 xferq = fc->ir[dma_ch]; 315 316 xferq->flag &= ~FWXFERQ_OPEN; 317 xferq->hand = NULL; 318 319 if (xferq->flag & FWXFERQ_RUNNING) 320 fc->irx_disable(fc, dma_ch); 321 322 if (sc->fwdev != NULL) 323 fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 0); 324 325 FWISOUND_LOCK(sc); 326 fw_iso_wait_inactive_locked(&sc->mtx, &sc->iso_active, "fwisis"); 327 FWISOUND_UNLOCK(sc); 328 329 xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_STREAM | 330 FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK); 331 332 fw_iso_free_chunks(xferq, M_FWISOUND); 333 } 334 335 static void 336 fwisound_iso_input(struct fw_xferq *xferq) 337 { 338 struct fwisound_softc *sc = (struct fwisound_softc *)xferq->sc; 339 struct fwisound_chan *chan; 340 struct fw_bulkxfer *sxfer; 341 struct fw_pkt *fp; 342 struct fwisound_payload *pay; 343 struct mbuf *m; 344 uint8_t *ring; 345 uint32_t plen, nbytes, sample_count, ring_size, pos, chunk, old_ptr; 346 int dma_ch, need_intr; 347 348 FWISOUND_LOCK(sc); 349 dma_ch = sc->dma_ch; 350 if (dma_ch < 0) { 351 FWISOUND_UNLOCK(sc); 352 return; 353 } 354 sc->iso_active = 1; 355 chan = sc->chan; 356 FWISOUND_UNLOCK(sc); 357 358 while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) { 359 STAILQ_REMOVE_HEAD(&xferq->stvalid, link); 360 361 m = fw_iso_dequeue(xferq, sxfer, sc->fd.fc); 362 if (m == NULL) 363 continue; 364 365 fp = mtod(m, struct fw_pkt *); 366 plen = fp->mode.stream.len; 367 if (plen < 20 || (uint32_t)m->m_len < 4 + plen) { 368 m_freem(m); 369 continue; 370 } 371 372 pay = (struct fwisound_payload *)(mtod(m, uint8_t *) + 4); 373 if (ntohl(pay->signature) != FWISOUND_SIGNATURE) { 374 m_freem(m); 375 continue; 376 } 377 378 sample_count = ntohl(pay->sample_count); 379 if (sample_count == 0 || sample_count > 475) { 380 m_freem(m); 381 continue; 382 } 383 384 nbytes = sample_count * 4; 385 if (plen < 16 + nbytes) { 386 m_freem(m); 387 continue; 388 } 389 390 if (sc->sample_total != 0 && 391 ntohl(pay->sample_total) != sc->sample_total) 392 sc->dropped++; 393 sc->sample_total = ntohl(pay->sample_total) + sample_count; 394 395 need_intr = 0; 396 if (chan != NULL && chan->running && 397 chan->buf != NULL && chan->pcm_chan != NULL) { 398 ring = sndbuf_getbufofs(chan->buf, 0); 399 ring_size = chan->buf->bufsize; 400 old_ptr = chan->hw_ptr; 401 402 uint8_t *src = (uint8_t *)pay->samples; 403 uint32_t rem = nbytes; 404 405 while (rem > 0) { 406 pos = chan->hw_ptr % ring_size; 407 chunk = MIN(rem, ring_size - pos); 408 memcpy(ring + pos, src, chunk); 409 chan->hw_ptr += chunk; 410 src += chunk; 411 rem -= chunk; 412 } 413 414 if (chan->buf->blksz == 0 || 415 (old_ptr / chan->buf->blksz) != 416 (chan->hw_ptr / chan->buf->blksz)) 417 need_intr = 1; 418 } 419 420 m_freem(m); 421 422 if (need_intr) { 423 chn_intr(chan->pcm_chan); 424 } 425 } 426 427 fw_iso_rearm_done(xferq, sc->fd.fc, &sc->mtx, &sc->iso_active, 428 &sc->dma_ch, dma_ch); 429 } 430 431 432 static int 433 fwisound_probe(device_t dev) 434 { 435 struct fw_unit *unit; 436 437 unit = fw_get_unit(dev); 438 if (unit == NULL) 439 return (ENXIO); 440 441 if (unit->spec_id != FWISOUND_SPEC_ID || 442 unit->sw_version != FWISOUND_VERSION) 443 return (ENXIO); 444 445 device_set_desc(dev, "Apple FireWire Audio"); 446 return (BUS_PROBE_DEFAULT); 447 } 448 449 static int 450 fwisound_attach(device_t dev) 451 { 452 struct fwisound_softc *sc; 453 struct fw_unit *unit; 454 uint32_t audio_base; 455 456 unit = fw_get_unit(dev); 457 if (unit == NULL || unit->fwdev == NULL) 458 return (ENXIO); 459 460 audio_base = fwisound_find_audio_base(unit); 461 if (audio_base == 0) { 462 device_printf(dev, "no audio base in unit directory\n"); 463 return (ENXIO); 464 } 465 466 sc = device_get_softc(dev); 467 sc->fd.dev = dev; 468 sc->fd.fc = fw_get_comm(dev); 469 sc->dev = dev; 470 mtx_init(&sc->mtx, "fwisound", NULL, MTX_DEF); 471 472 sc->fwdev = unit->fwdev; 473 sc->cmd_hi = 0xffff; 474 sc->cmd_lo = 0xf0000000 | (audio_base << 2); 475 sc->state = FWISOUND_STATE_IDLE; 476 sc->dma_ch = -1; 477 sc->iso_active = 0; 478 sc->iso_channel = (uint8_t)(iso_channel & FWXFERQ_CHTAGMASK); 479 sc->chan = NULL; 480 TASK_INIT(&sc->probe_task, 0, fwisound_probe_task, sc); 481 TASK_INIT(&sc->start_task, 0, fwisound_start_task, sc); 482 TASK_INIT(&sc->stop_task, 0, fwisound_stop_task, sc); 483 484 sc->pcm_dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY); 485 if (sc->pcm_dev == NULL) { 486 device_printf(dev, "failed to add pcm child\n"); 487 mtx_destroy(&sc->mtx); 488 return (ENXIO); 489 } 490 491 bus_attach_children(dev); 492 493 if (taskqueue_enqueue(taskqueue_thread, &sc->probe_task) != 0) 494 device_printf(dev, "failed to enqueue probe task\n"); 495 496 return (0); 497 } 498 499 static int 500 fwisound_detach(device_t dev) 501 { 502 struct fwisound_softc *sc; 503 504 sc = device_get_softc(dev); 505 506 FWISOUND_LOCK(sc); 507 sc->state = FWISOUND_STATE_DETACHING; 508 FWISOUND_UNLOCK(sc); 509 510 taskqueue_drain(taskqueue_thread, &sc->probe_task); 511 taskqueue_drain(taskqueue_thread, &sc->start_task); 512 taskqueue_drain(taskqueue_thread, &sc->stop_task); 513 fwisound_iso_stop(sc); 514 515 if (sc->pcm_dev != NULL) 516 device_delete_children(dev); 517 518 FWISOUND_LOCK(sc); 519 sc->fwdev = NULL; 520 FWISOUND_UNLOCK(sc); 521 522 mtx_destroy(&sc->mtx); 523 return (0); 524 } 525 526 static device_method_t fwisound_methods[] = { 527 DEVMETHOD(device_probe, fwisound_probe), 528 DEVMETHOD(device_attach, fwisound_attach), 529 DEVMETHOD(device_detach, fwisound_detach), 530 531 DEVMETHOD_END 532 }; 533 534 static driver_t fwisound_driver = { 535 "fwisound", 536 fwisound_methods, 537 sizeof(struct fwisound_softc), 538 }; 539 540 DRIVER_MODULE(fwisound, firewire, fwisound_driver, 0, 0); 541 MODULE_VERSION(fwisound, 1); 542 MODULE_DEPEND(fwisound, firewire, 1, 1, 1); 543 MODULE_DEPEND(fwisound, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 544