xref: /freebsd/sys/dev/sound/pcm/vchan.c (revision 5ee30e277a97679dd1cbd4a1746339a5f14546aa)
1285648f9SCameron Grant /*
23f225978SCameron Grant  * Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
3285648f9SCameron Grant  * All rights reserved.
4285648f9SCameron Grant  *
5285648f9SCameron Grant  * Redistribution and use in source and binary forms, with or without
6285648f9SCameron Grant  * modification, are permitted provided that the following conditions
7285648f9SCameron Grant  * are met:
8285648f9SCameron Grant  * 1. Redistributions of source code must retain the above copyright
9285648f9SCameron Grant  *    notice, this list of conditions and the following disclaimer.
10285648f9SCameron Grant  * 2. Redistributions in binary form must reproduce the above copyright
11285648f9SCameron Grant  *    notice, this list of conditions and the following disclaimer in the
12285648f9SCameron Grant  *    documentation and/or other materials provided with the distribution.
13285648f9SCameron Grant  *
14285648f9SCameron Grant  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15285648f9SCameron Grant  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16285648f9SCameron Grant  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17285648f9SCameron Grant  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18285648f9SCameron Grant  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19285648f9SCameron Grant  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20285648f9SCameron Grant  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21285648f9SCameron Grant  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22285648f9SCameron Grant  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23285648f9SCameron Grant  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24285648f9SCameron Grant  * SUCH DAMAGE.
25285648f9SCameron Grant  */
26285648f9SCameron Grant 
27285648f9SCameron Grant #include <dev/sound/pcm/sound.h>
28285648f9SCameron Grant #include <dev/sound/pcm/vchan.h>
29285648f9SCameron Grant #include "feeder_if.h"
30285648f9SCameron Grant 
3167b1dce3SCameron Grant SND_DECLARE_FILE("$FreeBSD$");
3267b1dce3SCameron Grant 
33285648f9SCameron Grant struct vchinfo {
3449c5e6e2SCameron Grant 	u_int32_t spd, fmt, blksz, bps, run;
35285648f9SCameron Grant 	struct pcm_channel *channel, *parent;
36285648f9SCameron Grant 	struct pcmchan_caps caps;
37285648f9SCameron Grant };
38285648f9SCameron Grant 
39285648f9SCameron Grant static u_int32_t vchan_fmt[] = {
40285648f9SCameron Grant 	AFMT_STEREO | AFMT_S16_LE,
41285648f9SCameron Grant 	0
42285648f9SCameron Grant };
43285648f9SCameron Grant 
44285648f9SCameron Grant static int
45285648f9SCameron Grant vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count)
46285648f9SCameron Grant {
47285648f9SCameron Grant 	/*
48285648f9SCameron Grant 	 * to is the output buffer, tmp is the input buffer
49285648f9SCameron Grant 	 * count is the number of 16bit samples to mix
50285648f9SCameron Grant 	 */
51285648f9SCameron Grant 	int i;
52285648f9SCameron Grant 	int x;
53285648f9SCameron Grant 
54285648f9SCameron Grant 	for(i = 0; i < count; i++) {
55285648f9SCameron Grant 		x = to[i];
56285648f9SCameron Grant 		x += tmp[i];
57285648f9SCameron Grant 		if (x < -32768) {
58285648f9SCameron Grant 			/* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
59285648f9SCameron Grant 			x = -32768;
60285648f9SCameron Grant 		}
61285648f9SCameron Grant 		if (x > 32767) {
62285648f9SCameron Grant 			/* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
63285648f9SCameron Grant 			x = 32767;
64285648f9SCameron Grant 		}
65285648f9SCameron Grant 		to[i] = x & 0x0000ffff;
66285648f9SCameron Grant 	}
67285648f9SCameron Grant 	return 0;
68285648f9SCameron Grant }
69285648f9SCameron Grant 
70285648f9SCameron Grant static int
71285648f9SCameron Grant feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
72285648f9SCameron Grant {
73285648f9SCameron Grant 	/* we're going to abuse things a bit */
74285648f9SCameron Grant 	struct snd_dbuf *src = source;
75285648f9SCameron Grant 	struct pcmchan_children *cce;
76285648f9SCameron Grant 	struct pcm_channel *ch;
77285648f9SCameron Grant 	int16_t *tmp, *dst;
78285648f9SCameron Grant 	unsigned int cnt;
79285648f9SCameron Grant 
80285648f9SCameron Grant 	KASSERT(sndbuf_getsize(src) >= count, ("bad bufsize"));
81285648f9SCameron Grant 	count &= ~1;
82285648f9SCameron Grant 	bzero(b, count);
83285648f9SCameron Grant 
84285648f9SCameron Grant 	/*
85285648f9SCameron Grant 	 * we are going to use our source as a temporary buffer since it's
86285648f9SCameron Grant 	 * got no other purpose.  we obtain our data by traversing the channel
87285648f9SCameron Grant 	 * list of children and calling vchan_mix_* to mix count bytes from each
88285648f9SCameron Grant 	 * into our destination buffer, b
89285648f9SCameron Grant 	 */
90285648f9SCameron Grant 	dst = (int16_t *)b;
91285648f9SCameron Grant 	tmp = (int16_t *)sndbuf_getbuf(src);
92285648f9SCameron Grant 	bzero(tmp, count);
93285648f9SCameron Grant 	SLIST_FOREACH(cce, &c->children, link) {
94285648f9SCameron Grant 		ch = cce->channel;
9549c5e6e2SCameron Grant 		if (ch->flags & CHN_F_TRIGGERED) {
96193d5719SCameron Grant 			if (ch->flags & CHN_F_MAPPED)
97193d5719SCameron Grant 				sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
98285648f9SCameron Grant 			cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
99285648f9SCameron Grant 			vchan_mix_s16(dst, tmp, cnt / 2);
100285648f9SCameron Grant 		}
10149c5e6e2SCameron Grant 	}
102285648f9SCameron Grant 
103285648f9SCameron Grant 	return count;
104285648f9SCameron Grant }
105285648f9SCameron Grant 
106285648f9SCameron Grant static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
107285648f9SCameron Grant 	{FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
108285648f9SCameron Grant 	{0},
109285648f9SCameron Grant };
110285648f9SCameron Grant static kobj_method_t feeder_vchan_s16_methods[] = {
111285648f9SCameron Grant     	KOBJMETHOD(feeder_feed,		feed_vchan_s16),
112285648f9SCameron Grant 	{ 0, 0 }
113285648f9SCameron Grant };
114285648f9SCameron Grant FEEDER_DECLARE(feeder_vchan_s16, 2, NULL);
115285648f9SCameron Grant 
116285648f9SCameron Grant /************************************************************/
117285648f9SCameron Grant 
118285648f9SCameron Grant static void *
119285648f9SCameron Grant vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
120285648f9SCameron Grant {
121285648f9SCameron Grant 	struct vchinfo *ch;
122285648f9SCameron Grant 	struct pcm_channel *parent = devinfo;
123285648f9SCameron Grant 
124285648f9SCameron Grant 	KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
125a163d034SWarner Losh 	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
126285648f9SCameron Grant 	ch->parent = parent;
127285648f9SCameron Grant 	ch->channel = c;
128285648f9SCameron Grant 	ch->fmt = AFMT_U8;
129285648f9SCameron Grant 	ch->spd = DSP_DEFAULT_SPEED;
130285648f9SCameron Grant 	ch->blksz = 2048;
131285648f9SCameron Grant 
132285648f9SCameron Grant 	c->flags |= CHN_F_VIRTUAL;
133285648f9SCameron Grant 
134285648f9SCameron Grant 	return ch;
135285648f9SCameron Grant }
136285648f9SCameron Grant 
137285648f9SCameron Grant static int
138285648f9SCameron Grant vchan_free(kobj_t obj, void *data)
139285648f9SCameron Grant {
140285648f9SCameron Grant 	return 0;
141285648f9SCameron Grant }
142285648f9SCameron Grant 
143285648f9SCameron Grant static int
144285648f9SCameron Grant vchan_setformat(kobj_t obj, void *data, u_int32_t format)
145285648f9SCameron Grant {
146285648f9SCameron Grant 	struct vchinfo *ch = data;
147285648f9SCameron Grant 	struct pcm_channel *parent = ch->parent;
148285648f9SCameron Grant 
149285648f9SCameron Grant 	ch->fmt = format;
15049c5e6e2SCameron Grant 	ch->bps = 1;
15149c5e6e2SCameron Grant 	ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
15249c5e6e2SCameron Grant 	ch->bps <<= (ch->fmt & AFMT_16BIT)? 1 : 0;
15349c5e6e2SCameron Grant 	ch->bps <<= (ch->fmt & AFMT_32BIT)? 2 : 0;
154285648f9SCameron Grant 	chn_notify(parent, CHN_N_FORMAT);
155285648f9SCameron Grant 	return 0;
156285648f9SCameron Grant }
157285648f9SCameron Grant 
158285648f9SCameron Grant static int
159285648f9SCameron Grant vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
160285648f9SCameron Grant {
161285648f9SCameron Grant 	struct vchinfo *ch = data;
162285648f9SCameron Grant 	struct pcm_channel *parent = ch->parent;
163285648f9SCameron Grant 
164285648f9SCameron Grant 	ch->spd = speed;
165285648f9SCameron Grant 	chn_notify(parent, CHN_N_RATE);
166285648f9SCameron Grant 	return speed;
167285648f9SCameron Grant }
168285648f9SCameron Grant 
169285648f9SCameron Grant static int
170285648f9SCameron Grant vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
171285648f9SCameron Grant {
172285648f9SCameron Grant 	struct vchinfo *ch = data;
173285648f9SCameron Grant 	struct pcm_channel *parent = ch->parent;
17449c5e6e2SCameron Grant 	int prate, crate;
175285648f9SCameron Grant 
176285648f9SCameron Grant 	ch->blksz = blocksize;
177285648f9SCameron Grant 	chn_notify(parent, CHN_N_BLOCKSIZE);
17849c5e6e2SCameron Grant 
17949c5e6e2SCameron Grant 	crate = ch->spd * ch->bps;
18049c5e6e2SCameron Grant 	prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard);
18149c5e6e2SCameron Grant 	blocksize = sndbuf_getblksz(parent->bufhard);
18249c5e6e2SCameron Grant 	blocksize *= prate;
18349c5e6e2SCameron Grant 	blocksize /= crate;
18449c5e6e2SCameron Grant 
185285648f9SCameron Grant 	return blocksize;
186285648f9SCameron Grant }
187285648f9SCameron Grant 
188285648f9SCameron Grant static int
189285648f9SCameron Grant vchan_trigger(kobj_t obj, void *data, int go)
190285648f9SCameron Grant {
191285648f9SCameron Grant 	struct vchinfo *ch = data;
192285648f9SCameron Grant 	struct pcm_channel *parent = ch->parent;
193285648f9SCameron Grant 
194285648f9SCameron Grant 	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
195285648f9SCameron Grant 		return 0;
196285648f9SCameron Grant 
197285648f9SCameron Grant 	ch->run = (go == PCMTRIG_START)? 1 : 0;
198285648f9SCameron Grant 	chn_notify(parent, CHN_N_TRIGGER);
199285648f9SCameron Grant 
200285648f9SCameron Grant 	return 0;
201285648f9SCameron Grant }
202285648f9SCameron Grant 
203285648f9SCameron Grant static struct pcmchan_caps *
204285648f9SCameron Grant vchan_getcaps(kobj_t obj, void *data)
205285648f9SCameron Grant {
206285648f9SCameron Grant 	struct vchinfo *ch = data;
207285648f9SCameron Grant 
208285648f9SCameron Grant 	ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard);
209285648f9SCameron Grant 	ch->caps.maxspeed = ch->caps.minspeed;
210285648f9SCameron Grant 	ch->caps.fmtlist = vchan_fmt;
211285648f9SCameron Grant 	ch->caps.caps = 0;
212285648f9SCameron Grant 
213285648f9SCameron Grant 	return &ch->caps;
214285648f9SCameron Grant }
215285648f9SCameron Grant 
216285648f9SCameron Grant static kobj_method_t vchan_methods[] = {
217285648f9SCameron Grant     	KOBJMETHOD(channel_init,		vchan_init),
218285648f9SCameron Grant     	KOBJMETHOD(channel_free,		vchan_free),
219285648f9SCameron Grant     	KOBJMETHOD(channel_setformat,		vchan_setformat),
220285648f9SCameron Grant     	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
221285648f9SCameron Grant     	KOBJMETHOD(channel_setblocksize,	vchan_setblocksize),
222285648f9SCameron Grant     	KOBJMETHOD(channel_trigger,		vchan_trigger),
223285648f9SCameron Grant     	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
224285648f9SCameron Grant 	{ 0, 0 }
225285648f9SCameron Grant };
226285648f9SCameron Grant CHANNEL_DECLARE(vchan);
227285648f9SCameron Grant 
228285648f9SCameron Grant /* virtual channel interface */
229285648f9SCameron Grant 
230285648f9SCameron Grant int
231285648f9SCameron Grant vchan_create(struct pcm_channel *parent)
232285648f9SCameron Grant {
233285648f9SCameron Grant     	struct snddev_info *d = parent->parentsnddev;
234285648f9SCameron Grant 	struct pcmchan_children *pce;
235285648f9SCameron Grant 	struct pcm_channel *child;
236285648f9SCameron Grant 	int err, first;
237285648f9SCameron Grant 
238a163d034SWarner Losh 	pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
23949c5e6e2SCameron Grant 	if (!pce) {
240285648f9SCameron Grant 		return ENOMEM;
24149c5e6e2SCameron Grant 	}
242285648f9SCameron Grant 
243285648f9SCameron Grant 	/* create a new playback channel */
244285648f9SCameron Grant 	child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
245285648f9SCameron Grant 	if (!child) {
246285648f9SCameron Grant 		free(pce, M_DEVBUF);
247285648f9SCameron Grant 		return ENODEV;
248285648f9SCameron Grant 	}
249285648f9SCameron Grant 
25067beb5a5SCameron Grant    	CHN_LOCK(parent);
25167beb5a5SCameron Grant 	if (!(parent->flags & CHN_F_BUSY)) {
25267beb5a5SCameron Grant 		CHN_UNLOCK(parent);
25367beb5a5SCameron Grant 		return EBUSY;
25467beb5a5SCameron Grant 	}
25567beb5a5SCameron Grant 
256285648f9SCameron Grant 	first = SLIST_EMPTY(&parent->children);
257285648f9SCameron Grant 	/* add us to our parent channel's children */
258285648f9SCameron Grant 	pce->channel = child;
259285648f9SCameron Grant 	SLIST_INSERT_HEAD(&parent->children, pce, link);
26049c5e6e2SCameron Grant 	CHN_UNLOCK(parent);
261285648f9SCameron Grant 
262285648f9SCameron Grant 	/* add us to our grandparent's channel list */
2635ee30e27SMathew Kanner 	/*
2645ee30e27SMathew Kanner 	 * XXX maybe we shouldn't always add the dev_t
2655ee30e27SMathew Kanner  	 */
2665ee30e27SMathew Kanner 	err = pcm_chn_add(d, child);
267285648f9SCameron Grant 	if (err) {
268285648f9SCameron Grant 		pcm_chn_destroy(child);
269285648f9SCameron Grant 		free(pce, M_DEVBUF);
270285648f9SCameron Grant 	}
271285648f9SCameron Grant 
27267beb5a5SCameron Grant 	/* XXX gross ugly hack, murder death kill */
273285648f9SCameron Grant 	if (first && !err) {
274285648f9SCameron Grant 		err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
275285648f9SCameron Grant 		if (err)
276285648f9SCameron Grant 			printf("chn_reset: %d\n", err);
277285648f9SCameron Grant 		err = chn_setspeed(parent, 44100);
278285648f9SCameron Grant 		if (err)
279285648f9SCameron Grant 			printf("chn_setspeed: %d\n", err);
280285648f9SCameron Grant 	}
281285648f9SCameron Grant 
282285648f9SCameron Grant 	return err;
283285648f9SCameron Grant }
284285648f9SCameron Grant 
285285648f9SCameron Grant int
286285648f9SCameron Grant vchan_destroy(struct pcm_channel *c)
287285648f9SCameron Grant {
288285648f9SCameron Grant 	struct pcm_channel *parent = c->parentchannel;
289285648f9SCameron Grant     	struct snddev_info *d = parent->parentsnddev;
290285648f9SCameron Grant 	struct pcmchan_children *pce;
291f637a36cSCameron Grant 	int err, last;
292285648f9SCameron Grant 
29349c5e6e2SCameron Grant 	CHN_LOCK(parent);
29449c5e6e2SCameron Grant 	if (!(parent->flags & CHN_F_BUSY)) {
29549c5e6e2SCameron Grant 		CHN_UNLOCK(parent);
29649c5e6e2SCameron Grant 		return EBUSY;
29749c5e6e2SCameron Grant 	}
29849c5e6e2SCameron Grant 	if (SLIST_EMPTY(&parent->children)) {
29949c5e6e2SCameron Grant 		CHN_UNLOCK(parent);
30049c5e6e2SCameron Grant 		return EINVAL;
30149c5e6e2SCameron Grant 	}
30249c5e6e2SCameron Grant 
303285648f9SCameron Grant 	/* remove us from our parent's children list */
304285648f9SCameron Grant 	SLIST_FOREACH(pce, &parent->children, link) {
305285648f9SCameron Grant 		if (pce->channel == c)
306285648f9SCameron Grant 			goto gotch;
307285648f9SCameron Grant 	}
30849c5e6e2SCameron Grant 	CHN_UNLOCK(parent);
309285648f9SCameron Grant 	return EINVAL;
310285648f9SCameron Grant gotch:
311285648f9SCameron Grant 	SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
312285648f9SCameron Grant 	free(pce, M_DEVBUF);
313285648f9SCameron Grant 
314f637a36cSCameron Grant 	last = SLIST_EMPTY(&parent->children);
315f637a36cSCameron Grant 	if (last)
316285648f9SCameron Grant 		parent->flags &= ~CHN_F_BUSY;
317f637a36cSCameron Grant 
31845550658SPoul-Henning Kamp 	/* remove us from our grandparent's channel list */
3195ee30e27SMathew Kanner 	err = pcm_chn_remove(d, c);
320f637a36cSCameron Grant 	if (err)
321f637a36cSCameron Grant 		return err;
322f637a36cSCameron Grant 
32349c5e6e2SCameron Grant 	CHN_UNLOCK(parent);
324285648f9SCameron Grant 	/* destroy ourselves */
325285648f9SCameron Grant 	err = pcm_chn_destroy(c);
326285648f9SCameron Grant 
327285648f9SCameron Grant 	return err;
328285648f9SCameron Grant }
329285648f9SCameron Grant 
330285648f9SCameron Grant int
33167b1dce3SCameron Grant vchan_initsys(device_t dev)
332285648f9SCameron Grant {
333285648f9SCameron Grant #ifdef SND_DYNSYSCTL
33467b1dce3SCameron Grant 	struct snddev_info *d;
33567b1dce3SCameron Grant 
33667b1dce3SCameron Grant     	d = device_get_softc(dev);
33767b1dce3SCameron Grant 	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
338285648f9SCameron Grant             OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
33964d85ef7SMark Murray 	    sysctl_hw_snd_vchans, "I", "");
340285648f9SCameron Grant #endif
34167b1dce3SCameron Grant 
342285648f9SCameron Grant 	return 0;
343285648f9SCameron Grant }
344285648f9SCameron Grant 
345285648f9SCameron Grant 
346