aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/firewire/fwisound_pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/dev/firewire/fwisound_pcm.c')
-rw-r--r--sys/dev/firewire/fwisound_pcm.c233
1 files changed, 233 insertions, 0 deletions
diff --git a/sys/dev/firewire/fwisound_pcm.c b/sys/dev/firewire/fwisound_pcm.c
new file mode 100644
index 000000000000..a7dd6f1c1a01
--- /dev/null
+++ b/sys/dev/firewire/fwisound_pcm.c
@@ -0,0 +1,233 @@
+/*
+ * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
+ *
+ * SPDX-License-Identifier: BSD-2-Clause
+ */
+
+/* newpcm channel integration for fwisound(4). */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/bus.h>
+#include <sys/malloc.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/taskqueue.h>
+
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/fwisound.h>
+
+#include <dev/sound/pcm/sound.h>
+
+#define FWISOUND_BUFSZ 65536
+
+
+static void *
+fwisound_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b,
+ struct pcm_channel *c, int dir)
+{
+ struct fwisound_softc *sc = devinfo;
+ struct fwisound_chan *chan;
+ void *buf;
+ unsigned int bufsz;
+
+ if (dir != PCMDIR_REC)
+ return (NULL);
+
+ chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO);
+ chan->sc = sc;
+ chan->pcm_chan = c;
+ chan->buf = b;
+
+ chan->fmts[0] = SND_FORMAT(AFMT_S16_BE, 2, 0);
+ chan->fmts[1] = 0;
+
+ chan->caps.minspeed = 48000;
+ chan->caps.maxspeed = 48000;
+ chan->caps.fmtlist = chan->fmts;
+ chan->caps.caps = 0;
+
+ bufsz = pcm_getbuffersize(sc->pcm_dev, 4096, FWISOUND_BUFSZ, 131072);
+ buf = malloc(bufsz, M_DEVBUF, M_WAITOK | M_ZERO);
+ if (sndbuf_setup(b, buf, bufsz) != 0) {
+ free(buf, M_DEVBUF);
+ free(chan, M_DEVBUF);
+ return (NULL);
+ }
+
+ FWISOUND_LOCK(sc);
+ sc->chan = chan;
+ FWISOUND_UNLOCK(sc);
+
+ return (chan);
+}
+
+static int
+fwisound_chan_free(kobj_t obj, void *data)
+{
+ struct fwisound_chan *chan = data;
+ struct fwisound_softc *sc = chan->sc;
+ void *buf;
+
+ if (chan->running) {
+ taskqueue_enqueue(taskqueue_thread, &sc->stop_task);
+ taskqueue_drain(taskqueue_thread, &sc->stop_task);
+ }
+
+ FWISOUND_LOCK(sc);
+ sc->chan = NULL;
+ FWISOUND_UNLOCK(sc);
+
+ buf = sndbuf_getbufofs(chan->buf, 0);
+ if (buf != NULL)
+ free(buf, M_DEVBUF);
+
+ free(chan, M_DEVBUF);
+ return (0);
+}
+
+static int
+fwisound_chan_setformat(kobj_t obj, void *data, uint32_t fmt)
+{
+
+ if (fmt != SND_FORMAT(AFMT_S16_BE, 2, 0))
+ return (EINVAL);
+ return (0);
+}
+
+static uint32_t
+fwisound_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
+{
+
+ return (48000);
+}
+
+static uint32_t
+fwisound_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz)
+{
+ struct fwisound_chan *chan = data;
+
+ blksz = rounddown(blksz, 4);
+ if (blksz < 4)
+ blksz = 4;
+
+ sndbuf_resize(chan->buf, chan->buf->bufsize / blksz, blksz);
+
+ return (chan->buf->blksz);
+}
+
+static int
+fwisound_chan_trigger(kobj_t obj, void *data, int go)
+{
+ struct fwisound_chan *chan = data;
+ struct fwisound_softc *sc = chan->sc;
+
+ switch (go) {
+ case PCMTRIG_START:
+ chan->hw_ptr = 0;
+ sc->dropped = 0;
+ sc->sample_total = 0;
+ chan->running = 1;
+ taskqueue_enqueue(taskqueue_thread, &sc->start_task);
+ return (0);
+
+ case PCMTRIG_STOP:
+ case PCMTRIG_ABORT:
+ chan->running = 0;
+ taskqueue_drain(taskqueue_thread, &sc->start_task);
+ taskqueue_enqueue(taskqueue_thread, &sc->stop_task);
+ return (0);
+
+ default:
+ return (0);
+ }
+}
+
+static uint32_t
+fwisound_chan_getptr(kobj_t obj, void *data)
+{
+ struct fwisound_chan *chan = data;
+
+ return (chan->hw_ptr % chan->buf->bufsize);
+}
+
+static struct pcmchan_caps *
+fwisound_chan_getcaps(kobj_t obj, void *data)
+{
+ struct fwisound_chan *chan = data;
+
+ return (&chan->caps);
+}
+
+static kobj_method_t fwisound_chan_methods[] = {
+ KOBJMETHOD(channel_init, fwisound_chan_init),
+ KOBJMETHOD(channel_free, fwisound_chan_free),
+ KOBJMETHOD(channel_setformat, fwisound_chan_setformat),
+ KOBJMETHOD(channel_setspeed, fwisound_chan_setspeed),
+ KOBJMETHOD(channel_setblocksize, fwisound_chan_setblocksize),
+ KOBJMETHOD(channel_trigger, fwisound_chan_trigger),
+ KOBJMETHOD(channel_getptr, fwisound_chan_getptr),
+ KOBJMETHOD(channel_getcaps, fwisound_chan_getcaps),
+ KOBJMETHOD_END
+};
+
+CHANNEL_DECLARE(fwisound_chan);
+
+static int
+fwisound_pcm_probe(device_t dev)
+{
+
+ device_set_desc(dev, "Apple FireWire audio");
+ return (0);
+}
+
+static int
+fwisound_pcm_attach(device_t dev)
+{
+ struct fwisound_softc *sc;
+ char status[SND_STATUSLEN];
+ int err;
+
+ sc = device_get_softc(device_get_parent(dev));
+ sc->pcm_dev = dev;
+
+ pcm_init(dev, sc);
+ pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
+
+ err = pcm_addchan(dev, PCMDIR_REC, &fwisound_chan_class, sc);
+ if (err != 0) {
+ device_printf(dev, "pcm_addchan failed: %d\n", err);
+ return (err);
+ }
+
+ snprintf(status, sizeof(status), "Apple FireWire audio at %s",
+ device_get_nameunit(device_get_parent(dev)));
+ return (pcm_register(dev, status));
+}
+
+static int
+fwisound_pcm_detach(device_t dev)
+{
+
+ return (pcm_unregister(dev));
+}
+
+static device_method_t fwisound_pcm_methods[] = {
+ DEVMETHOD(device_probe, fwisound_pcm_probe),
+ DEVMETHOD(device_attach, fwisound_pcm_attach),
+ DEVMETHOD(device_detach, fwisound_pcm_detach),
+
+ DEVMETHOD_END
+};
+
+static driver_t fwisound_pcm_driver = {
+ "pcm",
+ fwisound_pcm_methods,
+ PCM_SOFTC_SIZE,
+};
+
+DRIVER_MODULE(fwisound_pcm, fwisound, fwisound_pcm_driver, 0, 0);
+MODULE_DEPEND(fwisound_pcm, fwisound, 1, 1, 1);
+MODULE_DEPEND(fwisound_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);