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