1 /* 2 * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com> 3 * 4 * SPDX-License-Identifier: BSD-2-Clause 5 */ 6 7 /* newpcm channel integration for fwisound(4). */ 8 9 #include <sys/param.h> 10 #include <sys/systm.h> 11 #include <sys/kernel.h> 12 #include <sys/bus.h> 13 #include <sys/malloc.h> 14 #include <sys/lock.h> 15 #include <sys/mutex.h> 16 #include <sys/taskqueue.h> 17 18 #include <dev/firewire/firewire.h> 19 #include <dev/firewire/firewirereg.h> 20 #include <dev/firewire/fwisound.h> 21 22 #include <dev/sound/pcm/sound.h> 23 24 #define FWISOUND_BUFSZ 65536 25 26 27 static void * 28 fwisound_chan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, 29 struct pcm_channel *c, int dir) 30 { 31 struct fwisound_softc *sc = devinfo; 32 struct fwisound_chan *chan; 33 void *buf; 34 unsigned int bufsz; 35 36 if (dir != PCMDIR_REC) 37 return (NULL); 38 39 chan = malloc(sizeof(*chan), M_DEVBUF, M_WAITOK | M_ZERO); 40 chan->sc = sc; 41 chan->pcm_chan = c; 42 chan->buf = b; 43 44 chan->fmts[0] = SND_FORMAT(AFMT_S16_BE, 2, 0); 45 chan->fmts[1] = 0; 46 47 chan->caps.minspeed = 48000; 48 chan->caps.maxspeed = 48000; 49 chan->caps.fmtlist = chan->fmts; 50 chan->caps.caps = 0; 51 52 bufsz = pcm_getbuffersize(sc->pcm_dev, 4096, FWISOUND_BUFSZ, 131072); 53 buf = malloc(bufsz, M_DEVBUF, M_WAITOK | M_ZERO); 54 if (sndbuf_setup(b, buf, bufsz) != 0) { 55 free(buf, M_DEVBUF); 56 free(chan, M_DEVBUF); 57 return (NULL); 58 } 59 60 FWISOUND_LOCK(sc); 61 sc->chan = chan; 62 FWISOUND_UNLOCK(sc); 63 64 return (chan); 65 } 66 67 static int 68 fwisound_chan_free(kobj_t obj, void *data) 69 { 70 struct fwisound_chan *chan = data; 71 struct fwisound_softc *sc = chan->sc; 72 void *buf; 73 74 if (chan->running) { 75 taskqueue_enqueue(taskqueue_thread, &sc->stop_task); 76 taskqueue_drain(taskqueue_thread, &sc->stop_task); 77 } 78 79 FWISOUND_LOCK(sc); 80 sc->chan = NULL; 81 FWISOUND_UNLOCK(sc); 82 83 buf = sndbuf_getbufofs(chan->buf, 0); 84 if (buf != NULL) 85 free(buf, M_DEVBUF); 86 87 free(chan, M_DEVBUF); 88 return (0); 89 } 90 91 static int 92 fwisound_chan_setformat(kobj_t obj, void *data, uint32_t fmt) 93 { 94 95 if (fmt != SND_FORMAT(AFMT_S16_BE, 2, 0)) 96 return (EINVAL); 97 return (0); 98 } 99 100 static uint32_t 101 fwisound_chan_setspeed(kobj_t obj, void *data, uint32_t speed) 102 { 103 104 return (48000); 105 } 106 107 static uint32_t 108 fwisound_chan_setblocksize(kobj_t obj, void *data, uint32_t blksz) 109 { 110 struct fwisound_chan *chan = data; 111 112 blksz = rounddown(blksz, 4); 113 if (blksz < 4) 114 blksz = 4; 115 116 if (sndbuf_resize(chan->buf, chan->buf->bufsize / blksz, blksz) != 0) 117 return (0); 118 119 return (chan->buf->blksz); 120 } 121 122 static int 123 fwisound_chan_trigger(kobj_t obj, void *data, int go) 124 { 125 struct fwisound_chan *chan = data; 126 struct fwisound_softc *sc = chan->sc; 127 128 switch (go) { 129 case PCMTRIG_START: 130 chan->hw_ptr = 0; 131 sc->dropped = 0; 132 sc->sample_total = 0; 133 chan->running = 1; 134 taskqueue_enqueue(taskqueue_thread, &sc->start_task); 135 return (0); 136 137 case PCMTRIG_STOP: 138 case PCMTRIG_ABORT: 139 chan->running = 0; 140 taskqueue_drain(taskqueue_thread, &sc->start_task); 141 taskqueue_enqueue(taskqueue_thread, &sc->stop_task); 142 return (0); 143 144 default: 145 return (0); 146 } 147 } 148 149 static uint32_t 150 fwisound_chan_getptr(kobj_t obj, void *data) 151 { 152 struct fwisound_chan *chan = data; 153 154 return (chan->hw_ptr % chan->buf->bufsize); 155 } 156 157 static struct pcmchan_caps * 158 fwisound_chan_getcaps(kobj_t obj, void *data) 159 { 160 struct fwisound_chan *chan = data; 161 162 return (&chan->caps); 163 } 164 165 static kobj_method_t fwisound_chan_methods[] = { 166 KOBJMETHOD(channel_init, fwisound_chan_init), 167 KOBJMETHOD(channel_free, fwisound_chan_free), 168 KOBJMETHOD(channel_setformat, fwisound_chan_setformat), 169 KOBJMETHOD(channel_setspeed, fwisound_chan_setspeed), 170 KOBJMETHOD(channel_setblocksize, fwisound_chan_setblocksize), 171 KOBJMETHOD(channel_trigger, fwisound_chan_trigger), 172 KOBJMETHOD(channel_getptr, fwisound_chan_getptr), 173 KOBJMETHOD(channel_getcaps, fwisound_chan_getcaps), 174 KOBJMETHOD_END 175 }; 176 177 CHANNEL_DECLARE(fwisound_chan); 178 179 static int 180 fwisound_pcm_probe(device_t dev) 181 { 182 183 device_set_desc(dev, "Apple FireWire audio"); 184 return (0); 185 } 186 187 static int 188 fwisound_pcm_attach(device_t dev) 189 { 190 struct fwisound_softc *sc; 191 char status[SND_STATUSLEN]; 192 int err; 193 194 sc = device_get_softc(device_get_parent(dev)); 195 sc->pcm_dev = dev; 196 197 pcm_init(dev, sc); 198 pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE); 199 200 err = pcm_addchan(dev, PCMDIR_REC, &fwisound_chan_class, sc); 201 if (err != 0) { 202 device_printf(dev, "pcm_addchan failed: %d\n", err); 203 return (err); 204 } 205 206 snprintf(status, sizeof(status), "Apple FireWire audio at %s", 207 device_get_nameunit(device_get_parent(dev))); 208 return (pcm_register(dev, status)); 209 } 210 211 static int 212 fwisound_pcm_detach(device_t dev) 213 { 214 215 return (pcm_unregister(dev)); 216 } 217 218 static device_method_t fwisound_pcm_methods[] = { 219 DEVMETHOD(device_probe, fwisound_pcm_probe), 220 DEVMETHOD(device_attach, fwisound_pcm_attach), 221 DEVMETHOD(device_detach, fwisound_pcm_detach), 222 223 DEVMETHOD_END 224 }; 225 226 static driver_t fwisound_pcm_driver = { 227 "pcm", 228 fwisound_pcm_methods, 229 PCM_SOFTC_SIZE, 230 }; 231 232 DRIVER_MODULE(fwisound_pcm, fwisound, fwisound_pcm_driver, 0, 0); 233 MODULE_DEPEND(fwisound_pcm, fwisound, 1, 1, 1); 234 MODULE_DEPEND(fwisound_pcm, sound, 1, 1, 1); 235