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 *
fwisound_chan_init(kobj_t obj,void * devinfo,struct snd_dbuf * b,struct pcm_channel * c,int dir)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
fwisound_chan_free(kobj_t obj,void * data)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
fwisound_chan_setformat(kobj_t obj,void * data,uint32_t fmt)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
fwisound_chan_setspeed(kobj_t obj,void * data,uint32_t speed)101 fwisound_chan_setspeed(kobj_t obj, void *data, uint32_t speed)
102 {
103
104 return (48000);
105 }
106
107 static uint32_t
fwisound_chan_setblocksize(kobj_t obj,void * data,uint32_t blksz)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 sndbuf_resize(chan->buf, chan->buf->bufsize / blksz, blksz);
117
118 return (chan->buf->blksz);
119 }
120
121 static int
fwisound_chan_trigger(kobj_t obj,void * data,int go)122 fwisound_chan_trigger(kobj_t obj, void *data, int go)
123 {
124 struct fwisound_chan *chan = data;
125 struct fwisound_softc *sc = chan->sc;
126
127 switch (go) {
128 case PCMTRIG_START:
129 chan->hw_ptr = 0;
130 sc->dropped = 0;
131 sc->sample_total = 0;
132 chan->running = 1;
133 taskqueue_enqueue(taskqueue_thread, &sc->start_task);
134 return (0);
135
136 case PCMTRIG_STOP:
137 case PCMTRIG_ABORT:
138 chan->running = 0;
139 taskqueue_drain(taskqueue_thread, &sc->start_task);
140 taskqueue_enqueue(taskqueue_thread, &sc->stop_task);
141 return (0);
142
143 default:
144 return (0);
145 }
146 }
147
148 static uint32_t
fwisound_chan_getptr(kobj_t obj,void * data)149 fwisound_chan_getptr(kobj_t obj, void *data)
150 {
151 struct fwisound_chan *chan = data;
152
153 return (chan->hw_ptr % chan->buf->bufsize);
154 }
155
156 static struct pcmchan_caps *
fwisound_chan_getcaps(kobj_t obj,void * data)157 fwisound_chan_getcaps(kobj_t obj, void *data)
158 {
159 struct fwisound_chan *chan = data;
160
161 return (&chan->caps);
162 }
163
164 static kobj_method_t fwisound_chan_methods[] = {
165 KOBJMETHOD(channel_init, fwisound_chan_init),
166 KOBJMETHOD(channel_free, fwisound_chan_free),
167 KOBJMETHOD(channel_setformat, fwisound_chan_setformat),
168 KOBJMETHOD(channel_setspeed, fwisound_chan_setspeed),
169 KOBJMETHOD(channel_setblocksize, fwisound_chan_setblocksize),
170 KOBJMETHOD(channel_trigger, fwisound_chan_trigger),
171 KOBJMETHOD(channel_getptr, fwisound_chan_getptr),
172 KOBJMETHOD(channel_getcaps, fwisound_chan_getcaps),
173 KOBJMETHOD_END
174 };
175
176 CHANNEL_DECLARE(fwisound_chan);
177
178 static int
fwisound_pcm_probe(device_t dev)179 fwisound_pcm_probe(device_t dev)
180 {
181
182 device_set_desc(dev, "Apple FireWire audio");
183 return (0);
184 }
185
186 static int
fwisound_pcm_attach(device_t dev)187 fwisound_pcm_attach(device_t dev)
188 {
189 struct fwisound_softc *sc;
190 char status[SND_STATUSLEN];
191 int err;
192
193 sc = device_get_softc(device_get_parent(dev));
194 sc->pcm_dev = dev;
195
196 pcm_init(dev, sc);
197 pcm_setflags(dev, pcm_getflags(dev) | SD_F_MPSAFE);
198
199 err = pcm_addchan(dev, PCMDIR_REC, &fwisound_chan_class, sc);
200 if (err != 0) {
201 device_printf(dev, "pcm_addchan failed: %d\n", err);
202 return (err);
203 }
204
205 snprintf(status, sizeof(status), "Apple FireWire audio at %s",
206 device_get_nameunit(device_get_parent(dev)));
207 return (pcm_register(dev, status));
208 }
209
210 static int
fwisound_pcm_detach(device_t dev)211 fwisound_pcm_detach(device_t dev)
212 {
213
214 return (pcm_unregister(dev));
215 }
216
217 static device_method_t fwisound_pcm_methods[] = {
218 DEVMETHOD(device_probe, fwisound_pcm_probe),
219 DEVMETHOD(device_attach, fwisound_pcm_attach),
220 DEVMETHOD(device_detach, fwisound_pcm_detach),
221
222 DEVMETHOD_END
223 };
224
225 static driver_t fwisound_pcm_driver = {
226 "pcm",
227 fwisound_pcm_methods,
228 PCM_SOFTC_SIZE,
229 };
230
231 DRIVER_MODULE(fwisound_pcm, fwisound, fwisound_pcm_driver, 0, 0);
232 MODULE_DEPEND(fwisound_pcm, fwisound, 1, 1, 1);
233 MODULE_DEPEND(fwisound_pcm, sound, SOUND_MINVER, SOUND_PREFVER, SOUND_MAXVER);
234