vchan.c revision 149953
1/*-
2 * Copyright (c) 2001 Cameron Grant <cg@freebsd.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27#include <dev/sound/pcm/sound.h>
28#include <dev/sound/pcm/vchan.h>
29#include "feeder_if.h"
30
31SND_DECLARE_FILE("$FreeBSD: head/sys/dev/sound/pcm/vchan.c 149953 2005-09-10 18:10:31Z netchild $");
32
33/*
34 * Default speed
35 */
36#define VCHAN_DEFAULT_SPEED	48000
37
38extern int feeder_rate_ratemin;
39extern int feeder_rate_ratemax;
40
41struct vchinfo {
42	u_int32_t spd, fmt, blksz, bps, run;
43	struct pcm_channel *channel, *parent;
44	struct pcmchan_caps caps;
45};
46
47static u_int32_t vchan_fmt[] = {
48	AFMT_STEREO | AFMT_S16_LE,
49	0
50};
51
52static int
53vchan_mix_s16(int16_t *to, int16_t *tmp, unsigned int count)
54{
55	/*
56	 * to is the output buffer, tmp is the input buffer
57	 * count is the number of 16bit samples to mix
58	 */
59	int i;
60	int x;
61
62	for(i = 0; i < count; i++) {
63		x = to[i];
64		x += tmp[i];
65		if (x < -32768) {
66			/* printf("%d + %d = %d (u)\n", to[i], tmp[i], x); */
67			x = -32768;
68		}
69		if (x > 32767) {
70			/* printf("%d + %d = %d (o)\n", to[i], tmp[i], x); */
71			x = 32767;
72		}
73		to[i] = x & 0x0000ffff;
74	}
75	return 0;
76}
77
78static int
79feed_vchan_s16(struct pcm_feeder *f, struct pcm_channel *c, u_int8_t *b, u_int32_t count, void *source)
80{
81	/* we're going to abuse things a bit */
82	struct snd_dbuf *src = source;
83	struct pcmchan_children *cce;
84	struct pcm_channel *ch;
85	uint32_t sz;
86	int16_t *tmp, *dst;
87	unsigned int cnt, rcnt = 0;
88
89	#if 0
90	if (sndbuf_getsize(src) < count)
91		panic("feed_vchan_s16(%s): tmp buffer size %d < count %d, flags = 0x%x",
92		    c->name, sndbuf_getsize(src), count, c->flags);
93	#endif
94	sz = sndbuf_getsize(src);
95	if (sz < count)
96		count = sz;
97	count &= ~1;
98	if (count < 2)
99		return 0;
100	bzero(b, count);
101
102	/*
103	 * we are going to use our source as a temporary buffer since it's
104	 * got no other purpose.  we obtain our data by traversing the channel
105	 * list of children and calling vchan_mix_* to mix count bytes from each
106	 * into our destination buffer, b
107	 */
108	dst = (int16_t *)b;
109	tmp = (int16_t *)sndbuf_getbuf(src);
110	bzero(tmp, count);
111	SLIST_FOREACH(cce, &c->children, link) {
112		ch = cce->channel;
113   		CHN_LOCK(ch);
114		if (ch->flags & CHN_F_TRIGGERED) {
115			if (ch->flags & CHN_F_MAPPED)
116				sndbuf_acquire(ch->bufsoft, NULL, sndbuf_getfree(ch->bufsoft));
117			cnt = FEEDER_FEED(ch->feeder, ch, (u_int8_t *)tmp, count, ch->bufsoft);
118			vchan_mix_s16(dst, tmp, cnt >> 1);
119			if (cnt > rcnt)
120				rcnt = cnt;
121		}
122   		CHN_UNLOCK(ch);
123	}
124
125	return rcnt & ~1;
126}
127
128static struct pcm_feederdesc feeder_vchan_s16_desc[] = {
129	{FEEDER_MIXER, AFMT_S16_LE | AFMT_STEREO, AFMT_S16_LE | AFMT_STEREO, 0},
130	{0},
131};
132static kobj_method_t feeder_vchan_s16_methods[] = {
133    	KOBJMETHOD(feeder_feed,		feed_vchan_s16),
134	{ 0, 0 }
135};
136FEEDER_DECLARE(feeder_vchan_s16, 2, NULL);
137
138/************************************************************/
139
140static void *
141vchan_init(kobj_t obj, void *devinfo, struct snd_dbuf *b, struct pcm_channel *c, int dir)
142{
143	struct vchinfo *ch;
144	struct pcm_channel *parent = devinfo;
145
146	KASSERT(dir == PCMDIR_PLAY, ("vchan_init: bad direction"));
147	ch = malloc(sizeof(*ch), M_DEVBUF, M_WAITOK | M_ZERO);
148	if (!ch)
149		return NULL;
150	ch->parent = parent;
151	ch->channel = c;
152	ch->fmt = AFMT_U8;
153	ch->spd = DSP_DEFAULT_SPEED;
154	ch->blksz = 2048;
155
156	c->flags |= CHN_F_VIRTUAL;
157
158	return ch;
159}
160
161static int
162vchan_free(kobj_t obj, void *data)
163{
164	return 0;
165}
166
167static int
168vchan_setformat(kobj_t obj, void *data, u_int32_t format)
169{
170	struct vchinfo *ch = data;
171	struct pcm_channel *parent = ch->parent;
172	struct pcm_channel *channel = ch->channel;
173
174	ch->fmt = format;
175	ch->bps = 1;
176	ch->bps <<= (ch->fmt & AFMT_STEREO)? 1 : 0;
177	if (ch->fmt & AFMT_16BIT)
178		ch->bps <<= 1;
179	else if (ch->fmt & AFMT_24BIT)
180		ch->bps *= 3;
181	else if (ch->fmt & AFMT_32BIT)
182		ch->bps <<= 2;
183   	CHN_UNLOCK(channel);
184	chn_notify(parent, CHN_N_FORMAT);
185   	CHN_LOCK(channel);
186	sndbuf_setfmt(channel->bufsoft, format);
187	return 0;
188}
189
190static int
191vchan_setspeed(kobj_t obj, void *data, u_int32_t speed)
192{
193	struct vchinfo *ch = data;
194	struct pcm_channel *parent = ch->parent;
195	struct pcm_channel *channel = ch->channel;
196
197	ch->spd = speed;
198   	CHN_UNLOCK(channel);
199	chn_notify(parent, CHN_N_RATE);
200   	CHN_LOCK(channel);
201	return speed;
202}
203
204static int
205vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
206{
207	struct vchinfo *ch = data;
208	struct pcm_channel *channel = ch->channel;
209	struct pcm_channel *parent = ch->parent;
210	/* struct pcm_channel *channel = ch->channel; */
211	int prate, crate;
212
213	ch->blksz = blocksize;
214   	/* CHN_UNLOCK(channel); */
215	sndbuf_setblksz(channel->bufhard, blocksize);
216	chn_notify(parent, CHN_N_BLOCKSIZE);
217   	CHN_LOCK(parent);
218   	/* CHN_LOCK(channel); */
219
220	crate = ch->spd * ch->bps;
221	prate = sndbuf_getspd(parent->bufhard) * sndbuf_getbps(parent->bufhard);
222	blocksize = sndbuf_getblksz(parent->bufhard);
223   	CHN_UNLOCK(parent);
224	blocksize *= prate;
225	blocksize /= crate;
226
227	return blocksize;
228}
229
230static int
231vchan_trigger(kobj_t obj, void *data, int go)
232{
233	struct vchinfo *ch = data;
234	struct pcm_channel *parent = ch->parent;
235	struct pcm_channel *channel = ch->channel;
236
237	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
238		return 0;
239
240	ch->run = (go == PCMTRIG_START)? 1 : 0;
241   	CHN_UNLOCK(channel);
242	chn_notify(parent, CHN_N_TRIGGER);
243   	CHN_LOCK(channel);
244
245	return 0;
246}
247
248static struct pcmchan_caps *
249vchan_getcaps(kobj_t obj, void *data)
250{
251	struct vchinfo *ch = data;
252
253	ch->caps.minspeed = sndbuf_getspd(ch->parent->bufhard);
254	ch->caps.maxspeed = ch->caps.minspeed;
255	ch->caps.fmtlist = vchan_fmt;
256	ch->caps.caps = 0;
257
258	return &ch->caps;
259}
260
261static kobj_method_t vchan_methods[] = {
262    	KOBJMETHOD(channel_init,		vchan_init),
263    	KOBJMETHOD(channel_free,		vchan_free),
264    	KOBJMETHOD(channel_setformat,		vchan_setformat),
265    	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
266    	KOBJMETHOD(channel_setblocksize,	vchan_setblocksize),
267    	KOBJMETHOD(channel_trigger,		vchan_trigger),
268    	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
269	{ 0, 0 }
270};
271CHANNEL_DECLARE(vchan);
272
273/*
274 * On the fly vchan rate settings
275 */
276#ifdef SND_DYNSYSCTL
277static int
278sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
279{
280	struct snddev_info *d;
281    	struct snddev_channel *sce;
282	struct pcm_channel *c, *ch = NULL, *fake;
283	struct pcmchan_caps *caps;
284	int err = 0;
285	int newspd = 0;
286
287	d = oidp->oid_arg1;
288	if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
289		return EINVAL;
290	SLIST_FOREACH(sce, &d->channels, link) {
291		c = sce->channel;
292		CHN_LOCK(c);
293		if (c->direction == PCMDIR_PLAY) {
294			if (c->flags & CHN_F_VIRTUAL) {
295				if (req->newptr != NULL &&
296						(c->flags & CHN_F_BUSY)) {
297					CHN_UNLOCK(c);
298					return EBUSY;
299				}
300				if (ch == NULL)
301					ch = c->parentchannel;
302			}
303		}
304		CHN_UNLOCK(c);
305	}
306	if (ch != NULL) {
307		CHN_LOCK(ch);
308		newspd = ch->speed;
309		CHN_UNLOCK(ch);
310	}
311	err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
312	if (err == 0 && req->newptr != NULL) {
313		if (ch == NULL || newspd < 1 ||
314				newspd < feeder_rate_ratemin ||
315				newspd > feeder_rate_ratemax)
316			return EINVAL;
317		if (pcm_inprog(d, 1) != 1) {
318			pcm_inprog(d, -1);
319			return EINPROGRESS;
320		}
321		CHN_LOCK(ch);
322		caps = chn_getcaps(ch);
323		if (caps == NULL || newspd < caps->minspeed ||
324				newspd > caps->maxspeed) {
325			CHN_UNLOCK(ch);
326			pcm_inprog(d, -1);
327			return EINVAL;
328		}
329		if (newspd != ch->speed) {
330			err = chn_setspeed(ch, newspd);
331			CHN_UNLOCK(ch);
332			if (err == 0) {
333				fake = pcm_getfakechan(d);
334				if (fake != NULL) {
335					CHN_LOCK(fake);
336					fake->speed = newspd;
337					CHN_UNLOCK(fake);
338				}
339			}
340		} else
341			CHN_UNLOCK(ch);
342		pcm_inprog(d, -1);
343	}
344	return err;
345}
346#endif
347
348/* virtual channel interface */
349
350int
351vchan_create(struct pcm_channel *parent)
352{
353    	struct snddev_info *d = parent->parentsnddev;
354	struct pcmchan_children *pce;
355	struct pcm_channel *child, *fake;
356	struct pcmchan_caps *parent_caps;
357	int err, first, speed = 0;
358
359	if (!(parent->flags & CHN_F_BUSY))
360		return EBUSY;
361
362
363	CHN_UNLOCK(parent);
364
365	pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
366	if (!pce) {
367   		CHN_LOCK(parent);
368		return ENOMEM;
369	}
370
371	/* create a new playback channel */
372	child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
373	if (!child) {
374		free(pce, M_DEVBUF);
375   		CHN_LOCK(parent);
376		return ENODEV;
377	}
378	pce->channel = child;
379
380	/* add us to our grandparent's channel list */
381	/*
382	 * XXX maybe we shouldn't always add the dev_t
383 	 */
384	err = pcm_chn_add(d, child);
385	if (err) {
386		pcm_chn_destroy(child);
387		free(pce, M_DEVBUF);
388		CHN_LOCK(parent);
389		return err;
390	}
391
392   	CHN_LOCK(parent);
393	/* add us to our parent channel's children */
394	first = SLIST_EMPTY(&parent->children);
395	SLIST_INSERT_HEAD(&parent->children, pce, link);
396	parent->flags |= CHN_F_HAS_VCHAN;
397
398	if (first) {
399		parent_caps = chn_getcaps(parent);
400		if (parent_caps == NULL)
401			err = EINVAL;
402
403		if (!err)
404			err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
405
406		if (!err) {
407			fake = pcm_getfakechan(d);
408			if (fake != NULL) {
409				/*
410				 * Avoid querying kernel hint, use saved value
411				 * from fake channel.
412				 */
413				CHN_UNLOCK(parent);
414				CHN_LOCK(fake);
415				speed = fake->speed;
416				CHN_UNLOCK(fake);
417				CHN_LOCK(parent);
418			}
419
420			/*
421			 * This is very sad. Few soundcards advertised as being
422			 * able to do (insanely) higher/lower speed, but in
423			 * reality, they simply can't. At least, we give user chance
424			 * to set sane value via kernel hints or sysctl.
425			 */
426			if (speed < 1) {
427				int r;
428				CHN_UNLOCK(parent);
429				r = resource_int_value(device_get_name(parent->dev),
430							device_get_unit(parent->dev),
431								"vchanrate", &speed);
432				CHN_LOCK(parent);
433				if (r != 0)
434					speed = VCHAN_DEFAULT_SPEED;
435			}
436
437			/*
438			 * Limit speed based on driver caps.
439			 * This is supposed to help fixed rate, non-VRA
440			 * AC97 cards, but.. (see below)
441			 */
442			if (speed < parent_caps->minspeed)
443				speed = parent_caps->minspeed;
444			if (speed > parent_caps->maxspeed)
445				speed = parent_caps->maxspeed;
446
447			/*
448			 * We still need to limit the speed between
449			 * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
450			 * just an escape goat if all of the above failed
451			 * miserably.
452			 */
453			if (speed < feeder_rate_ratemin)
454				speed = feeder_rate_ratemin;
455			if (speed > feeder_rate_ratemax)
456				speed = feeder_rate_ratemax;
457
458			err = chn_setspeed(parent, speed);
459
460			if (!err && fake != NULL) {
461				/*
462				 * Save new value to fake channel.
463				 */
464				CHN_UNLOCK(parent);
465				CHN_LOCK(fake);
466				fake->speed = speed;
467				CHN_UNLOCK(fake);
468				CHN_LOCK(parent);
469			}
470		}
471
472		if (err) {
473			SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
474			parent->flags &= ~CHN_F_HAS_VCHAN;
475			CHN_UNLOCK(parent);
476			free(pce, M_DEVBUF);
477			pcm_chn_remove(d, child);
478			pcm_chn_destroy(child);
479			CHN_LOCK(parent);
480			return err;
481		}
482	}
483
484	return 0;
485}
486
487int
488vchan_destroy(struct pcm_channel *c)
489{
490	struct pcm_channel *parent = c->parentchannel;
491    	struct snddev_info *d = parent->parentsnddev;
492	struct pcmchan_children *pce;
493	struct snddev_channel *sce;
494	int err, last;
495
496	CHN_LOCK(parent);
497	if (!(parent->flags & CHN_F_BUSY)) {
498		CHN_UNLOCK(parent);
499		return EBUSY;
500	}
501	if (SLIST_EMPTY(&parent->children)) {
502		CHN_UNLOCK(parent);
503		return EINVAL;
504	}
505
506	/* remove us from our parent's children list */
507	SLIST_FOREACH(pce, &parent->children, link) {
508		if (pce->channel == c)
509			goto gotch;
510	}
511	CHN_UNLOCK(parent);
512	return EINVAL;
513gotch:
514	SLIST_FOREACH(sce, &d->channels, link) {
515		if (sce->channel == c) {
516			if (sce->dsp_devt)
517				destroy_dev(sce->dsp_devt);
518			if (sce->dspW_devt)
519				destroy_dev(sce->dspW_devt);
520			if (sce->audio_devt)
521				destroy_dev(sce->audio_devt);
522			if (sce->dspr_devt)
523				destroy_dev(sce->dspr_devt);
524			break;
525		}
526	}
527	SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
528	free(pce, M_DEVBUF);
529
530	last = SLIST_EMPTY(&parent->children);
531	if (last) {
532		parent->flags &= ~CHN_F_BUSY;
533		parent->flags &= ~CHN_F_HAS_VCHAN;
534	}
535
536	/* remove us from our grandparent's channel list */
537	err = pcm_chn_remove(d, c);
538
539	CHN_UNLOCK(parent);
540	/* destroy ourselves */
541	if (!err)
542		err = pcm_chn_destroy(c);
543
544#if 0
545	if (!err && last) {
546		CHN_LOCK(parent);
547		chn_reset(parent, chn_getcaps(parent)->fmtlist[0]);
548		chn_setspeed(parent, chn_getcaps(parent)->minspeed);
549		CHN_UNLOCK(parent);
550	}
551#endif
552
553	return err;
554}
555
556int
557vchan_initsys(device_t dev)
558{
559#ifdef SND_DYNSYSCTL
560	struct snddev_info *d;
561
562    	d = device_get_softc(dev);
563	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
564            OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
565	    sysctl_hw_snd_vchans, "I", "");
566	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
567	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
568	    sysctl_hw_snd_vchanrate, "I", "");
569#endif
570
571	return 0;
572}
573