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 if (!crom_crc_valid((uint32_t *)&udir->entry[0], udir_len, udir->crc)) { 127 if (firewire_debug) 128 printf("fwisound: bad CRC in unit directory at 0x%x\n", 129 udir_qoff); 130 } 131 132 for (i = 0; i < (int)udir_len; i++) { 133 if (udir_qoff + 1 + i >= rom_quads) 134 break; 135 reg = &udir->entry[i]; 136 if (reg->key == IIDC_CROM_CMD_BASE && reg->val != 0) 137 return (reg->val); 138 } 139 return (0); 140 } 141 142 static void 143 fwisound_probe_task(void *arg, int pending __unused) 144 { 145 struct fwisound_softc *sc = (struct fwisound_softc *)arg; 146 uint32_t val; 147 int err; 148 149 if (sc->state == FWISOUND_STATE_DETACHING || sc->fwdev == NULL) 150 return; 151 152 err = fwisound_write_quadlet(sc, FWISOUND_REG_SAMPLE_RATE, 153 FWISOUND_RATE_48000); 154 if (err) { 155 device_printf(sc->dev, 156 "failed to set sample rate: %d\n", err); 157 return; 158 } 159 160 if (fwisound_read_quadlet(sc, FWISOUND_REG_GAIN_RAW_START, &val) == 0) 161 sc->gain_raw_min = (int32_t)val; 162 if (fwisound_read_quadlet(sc, FWISOUND_REG_GAIN_RAW_END, &val) == 0) 163 sc->gain_raw_max = (int32_t)val; 164 165 FWISOUND_LOCK(sc); 166 if (sc->state == FWISOUND_STATE_DETACHING) { 167 FWISOUND_UNLOCK(sc); 168 return; 169 } 170 sc->state = FWISOUND_STATE_PROBED; 171 FWISOUND_UNLOCK(sc); 172 } 173 174 static void 175 fwisound_start_task(void *arg, int pending __unused) 176 { 177 struct fwisound_softc *sc = (struct fwisound_softc *)arg; 178 179 if (sc->state == FWISOUND_STATE_DETACHING) 180 return; 181 182 fwisound_iso_start(sc); 183 } 184 185 static void 186 fwisound_stop_task(void *arg, int pending __unused) 187 { 188 struct fwisound_softc *sc = (struct fwisound_softc *)arg; 189 190 fwisound_iso_stop(sc); 191 192 FWISOUND_LOCK(sc); 193 if (sc->state != FWISOUND_STATE_DETACHING) 194 sc->state = FWISOUND_STATE_PROBED; 195 FWISOUND_UNLOCK(sc); 196 } 197 198 int 199 fwisound_iso_start(struct fwisound_softc *sc) 200 { 201 struct firewire_comm *fc = sc->fd.fc; 202 struct fw_xferq *xferq; 203 uint32_t val; 204 uint8_t speed; 205 int dma_ch, err; 206 207 mtx_assert(&sc->mtx, MA_NOTOWNED); 208 209 FWISOUND_LOCK(sc); 210 if (sc->dma_ch >= 0 || sc->state == FWISOUND_STATE_STREAMING) { 211 FWISOUND_UNLOCK(sc); 212 return (0); 213 } 214 if (sc->state == FWISOUND_STATE_DETACHING || sc->fwdev == NULL) { 215 FWISOUND_UNLOCK(sc); 216 return (ENXIO); 217 } 218 speed = min(sc->fwdev->speed, FWSPD_S400); 219 FWISOUND_UNLOCK(sc); 220 221 dma_ch = fw_open_isodma(fc, 0); 222 if (dma_ch < 0) { 223 device_printf(sc->dev, "no IR DMA channel available\n"); 224 return (EBUSY); 225 } 226 227 FWISOUND_LOCK(sc); 228 if (sc->dma_ch >= 0) { 229 FWISOUND_UNLOCK(sc); 230 fc->ir[dma_ch]->flag &= ~FWXFERQ_OPEN; 231 return (0); 232 } 233 FWISOUND_UNLOCK(sc); 234 235 xferq = fc->ir[dma_ch]; 236 xferq->flag |= FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_STREAM; 237 238 xferq->flag &= ~FWXFERQ_CHTAGMASK; 239 xferq->flag |= sc->iso_channel & FWXFERQ_CHTAGMASK; 240 241 xferq->sc = (caddr_t)sc; 242 xferq->hand = fwisound_iso_input; 243 xferq->bnchunk = FWISOUND_ISO_NCHUNK; 244 xferq->bnpacket = 1; 245 xferq->psize = FWISOUND_ISO_PKTSIZE; 246 xferq->queued = 0; 247 xferq->buf = NULL; 248 249 xferq->bulkxfer = malloc( 250 sizeof(struct fw_bulkxfer) * xferq->bnchunk, 251 M_FWISOUND, M_WAITOK | M_ZERO); 252 253 fw_iso_init_chunks(xferq); 254 255 sc->sample_total = 0; 256 257 val = sc->iso_channel | ((uint32_t)speed << 16); 258 err = fwisound_write_quadlet(sc, FWISOUND_REG_ISO_TX_CONFIG, val); 259 if (err) { 260 device_printf(sc->dev, 261 "failed to set ISO_TX_CONFIG: %d\n", err); 262 goto fail; 263 } 264 265 err = fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 266 FWISOUND_AUDIO_ENABLE); 267 if (err) { 268 device_printf(sc->dev, 269 "failed to enable audio: %d\n", err); 270 goto fail; 271 } 272 273 err = fc->irx_enable(fc, dma_ch); 274 if (err) { 275 device_printf(sc->dev, "failed to start IR DMA: %d\n", err); 276 fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 0); 277 goto fail; 278 } 279 280 FWISOUND_LOCK(sc); 281 if (sc->state == FWISOUND_STATE_DETACHING) { 282 FWISOUND_UNLOCK(sc); 283 fc->irx_disable(fc, dma_ch); 284 fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 0); 285 err = ENXIO; 286 goto fail; 287 } 288 sc->dma_ch = dma_ch; 289 sc->state = FWISOUND_STATE_STREAMING; 290 FWISOUND_UNLOCK(sc); 291 292 return (0); 293 294 fail: 295 fw_iso_free_chunks(xferq, M_FWISOUND); 296 xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_OPEN | FWXFERQ_STREAM | 297 FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK); 298 xferq->hand = NULL; 299 sc->dma_ch = -1; 300 return (err); 301 } 302 303 void 304 fwisound_iso_stop(struct fwisound_softc *sc) 305 { 306 struct firewire_comm *fc = sc->fd.fc; 307 struct fw_xferq *xferq; 308 int dma_ch; 309 310 FWISOUND_LOCK(sc); 311 dma_ch = sc->dma_ch; 312 if (dma_ch < 0) { 313 FWISOUND_UNLOCK(sc); 314 return; 315 } 316 sc->dma_ch = -1; 317 FWISOUND_UNLOCK(sc); 318 319 xferq = fc->ir[dma_ch]; 320 321 xferq->flag &= ~FWXFERQ_OPEN; 322 xferq->hand = NULL; 323 324 if (xferq->flag & FWXFERQ_RUNNING) 325 fc->irx_disable(fc, dma_ch); 326 327 if (sc->fwdev != NULL) 328 fwisound_write_quadlet(sc, FWISOUND_REG_AUDIO_ENABLE, 0); 329 330 FWISOUND_LOCK(sc); 331 fw_iso_wait_inactive_locked(&sc->mtx, &sc->iso_active, "fwisis"); 332 FWISOUND_UNLOCK(sc); 333 334 xferq->flag &= ~(FWXFERQ_MODEMASK | FWXFERQ_STREAM | 335 FWXFERQ_EXTBUF | FWXFERQ_HANDLER | FWXFERQ_CHTAGMASK); 336 337 fw_iso_free_chunks(xferq, M_FWISOUND); 338 } 339 340 static void 341 fwisound_iso_input(struct fw_xferq *xferq) 342 { 343 struct fwisound_softc *sc = (struct fwisound_softc *)xferq->sc; 344 struct fwisound_chan *chan; 345 struct fw_bulkxfer *sxfer; 346 struct fw_pkt *fp; 347 struct fwisound_payload *pay; 348 struct mbuf *m; 349 uint8_t *ring; 350 uint32_t plen, nbytes, sample_count, ring_size, pos, chunk, old_ptr; 351 int dma_ch, need_intr; 352 353 FWISOUND_LOCK(sc); 354 dma_ch = sc->dma_ch; 355 if (dma_ch < 0) { 356 FWISOUND_UNLOCK(sc); 357 return; 358 } 359 sc->iso_active = 1; 360 chan = sc->chan; 361 FWISOUND_UNLOCK(sc); 362 363 while ((sxfer = STAILQ_FIRST(&xferq->stvalid)) != NULL) { 364 STAILQ_REMOVE_HEAD(&xferq->stvalid, link); 365 366 m = fw_iso_dequeue(xferq, sxfer, sc->fd.fc); 367 if (m == NULL) 368 continue; 369 370 fp = mtod(m, struct fw_pkt *); 371 plen = fp->mode.stream.len; 372 if (plen < 20 || (uint32_t)m->m_len < 4 + plen) { 373 m_freem(m); 374 continue; 375 } 376 377 pay = (struct fwisound_payload *)(mtod(m, uint8_t *) + 4); 378 if (ntohl(pay->signature) != FWISOUND_SIGNATURE) { 379 m_freem(m); 380 continue; 381 } 382 383 sample_count = ntohl(pay->sample_count); 384 if (sample_count == 0 || sample_count > FWISOUND_MAX_SAMPLES) { 385 m_freem(m); 386 continue; 387 } 388 389 nbytes = sample_count * 4; 390 if (plen < 16 + nbytes) { 391 m_freem(m); 392 continue; 393 } 394 395 if (sc->sample_total != 0 && 396 ntohl(pay->sample_total) != sc->sample_total) 397 sc->dropped++; 398 sc->sample_total = ntohl(pay->sample_total) + sample_count; 399 400 need_intr = 0; 401 if (chan != NULL && chan->running && 402 chan->buf != NULL && chan->pcm_chan != NULL) { 403 ring = sndbuf_getbufofs(chan->buf, 0); 404 ring_size = chan->buf->bufsize; 405 old_ptr = chan->hw_ptr; 406 407 uint8_t *src = (uint8_t *)pay->samples; 408 uint32_t rem = nbytes; 409 410 while (rem > 0) { 411 pos = chan->hw_ptr % ring_size; 412 chunk = MIN(rem, ring_size - pos); 413 memcpy(ring + pos, src, chunk); 414 chan->hw_ptr += chunk; 415 src += chunk; 416 rem -= chunk; 417 } 418 419 if (chan->buf->blksz == 0 || 420 (old_ptr / chan->buf->blksz) != 421 (chan->hw_ptr / chan->buf->blksz)) 422 need_intr = 1; 423 } 424 425 m_freem(m); 426 427 if (need_intr) { 428 chn_intr(chan->pcm_chan); 429 } 430 } 431 432 fw_iso_rearm_done(xferq, sc->fd.fc, &sc->mtx, &sc->iso_active, 433 &sc->dma_ch, dma_ch); 434 } 435 436 437 static int 438 fwisound_probe(device_t dev) 439 { 440 struct fw_unit *unit; 441 442 unit = fw_get_unit(dev); 443 if (unit == NULL) 444 return (ENXIO); 445 446 if (unit->spec_id != FWISOUND_SPEC_ID || 447 unit->sw_version != FWISOUND_VERSION) 448 return (ENXIO); 449 450 device_set_desc(dev, "Apple FireWire Audio"); 451 return (BUS_PROBE_DEFAULT); 452 } 453 454 static int 455 fwisound_attach(device_t dev) 456 { 457 struct fwisound_softc *sc; 458 struct fw_unit *unit; 459 uint32_t audio_base; 460 461 unit = fw_get_unit(dev); 462 if (unit == NULL || unit->fwdev == NULL) 463 return (ENXIO); 464 465 audio_base = fwisound_find_audio_base(unit); 466 if (audio_base == 0) { 467 device_printf(dev, "no audio base in unit directory\n"); 468 return (ENXIO); 469 } 470 471 sc = device_get_softc(dev); 472 sc->fd.dev = dev; 473 sc->fd.fc = fw_get_comm(dev); 474 sc->dev = dev; 475 mtx_init(&sc->mtx, "fwisound", NULL, MTX_DEF); 476 477 sc->fwdev = unit->fwdev; 478 sc->cmd_hi = 0xffff; 479 sc->cmd_lo = 0xf0000000 | (audio_base << 2); 480 sc->state = FWISOUND_STATE_IDLE; 481 sc->dma_ch = -1; 482 sc->iso_active = 0; 483 sc->iso_channel = (uint8_t)(iso_channel & FWXFERQ_CHTAGMASK); 484 sc->chan = NULL; 485 TASK_INIT(&sc->probe_task, 0, fwisound_probe_task, sc); 486 TASK_INIT(&sc->start_task, 0, fwisound_start_task, sc); 487 TASK_INIT(&sc->stop_task, 0, fwisound_stop_task, sc); 488 489 sc->pcm_dev = device_add_child(dev, "pcm", DEVICE_UNIT_ANY); 490 if (sc->pcm_dev == NULL) { 491 device_printf(dev, "failed to add pcm child\n"); 492 mtx_destroy(&sc->mtx); 493 return (ENXIO); 494 } 495 496 bus_attach_children(dev); 497 498 if (taskqueue_enqueue(taskqueue_thread, &sc->probe_task) != 0) 499 device_printf(dev, "failed to enqueue probe task\n"); 500 501 return (0); 502 } 503 504 static int 505 fwisound_detach(device_t dev) 506 { 507 struct fwisound_softc *sc; 508 509 sc = device_get_softc(dev); 510 511 FWISOUND_LOCK(sc); 512 sc->state = FWISOUND_STATE_DETACHING; 513 FWISOUND_UNLOCK(sc); 514 515 taskqueue_drain(taskqueue_thread, &sc->probe_task); 516 taskqueue_drain(taskqueue_thread, &sc->start_task); 517 taskqueue_drain(taskqueue_thread, &sc->stop_task); 518 fwisound_iso_stop(sc); 519 520 if (sc->pcm_dev != NULL) 521 device_delete_children(dev); 522 523 FWISOUND_LOCK(sc); 524 sc->fwdev = NULL; 525 FWISOUND_UNLOCK(sc); 526 527 mtx_destroy(&sc->mtx); 528 return (0); 529 } 530 531 static device_method_t fwisound_methods[] = { 532 DEVMETHOD(device_probe, fwisound_probe), 533 DEVMETHOD(device_attach, fwisound_attach), 534 DEVMETHOD(device_detach, fwisound_detach), 535 536 DEVMETHOD_END 537 }; 538 539 static driver_t fwisound_driver = { 540 "fwisound", 541 fwisound_methods, 542 sizeof(struct fwisound_softc), 543 }; 544 545 DRIVER_MODULE(fwisound, firewire, fwisound_driver, 0, 0); 546 MODULE_VERSION(fwisound, 1); 547 MODULE_DEPEND(fwisound, firewire, 1, 1, 1); 548 MODULE_DEPEND(fwisound, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER); 549