vchan.c revision 151456
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 151456 2005-10-18 21:33:51Z ariff $");
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_LOCK(parent);
200	speed = sndbuf_getspd(parent->bufsoft);
201	CHN_UNLOCK(parent);
202	CHN_LOCK(channel);
203	return speed;
204}
205
206static int
207vchan_setblocksize(kobj_t obj, void *data, u_int32_t blocksize)
208{
209	struct vchinfo *ch = data;
210	struct pcm_channel *channel = ch->channel;
211	struct pcm_channel *parent = ch->parent;
212	/* struct pcm_channel *channel = ch->channel; */
213	int prate, crate;
214
215	ch->blksz = blocksize;
216   	/* CHN_UNLOCK(channel); */
217	sndbuf_setblksz(channel->bufhard, blocksize);
218	chn_notify(parent, CHN_N_BLOCKSIZE);
219   	CHN_LOCK(parent);
220   	/* CHN_LOCK(channel); */
221
222	crate = ch->spd * ch->bps;
223	prate = sndbuf_getspd(parent->bufsoft) * sndbuf_getbps(parent->bufsoft);
224	blocksize = sndbuf_getblksz(parent->bufsoft);
225   	CHN_UNLOCK(parent);
226	blocksize *= prate;
227	blocksize /= crate;
228
229	return blocksize;
230}
231
232static int
233vchan_trigger(kobj_t obj, void *data, int go)
234{
235	struct vchinfo *ch = data;
236	struct pcm_channel *parent = ch->parent;
237	struct pcm_channel *channel = ch->channel;
238
239	if (go == PCMTRIG_EMLDMAWR || go == PCMTRIG_EMLDMARD)
240		return 0;
241
242	ch->run = (go == PCMTRIG_START)? 1 : 0;
243   	CHN_UNLOCK(channel);
244	chn_notify(parent, CHN_N_TRIGGER);
245   	CHN_LOCK(channel);
246
247	return 0;
248}
249
250static struct pcmchan_caps *
251vchan_getcaps(kobj_t obj, void *data)
252{
253	struct vchinfo *ch = data;
254
255	ch->caps.minspeed = sndbuf_getspd(ch->parent->bufsoft);
256	ch->caps.maxspeed = ch->caps.minspeed;
257	ch->caps.fmtlist = vchan_fmt;
258	ch->caps.caps = 0;
259
260	return &ch->caps;
261}
262
263static kobj_method_t vchan_methods[] = {
264    	KOBJMETHOD(channel_init,		vchan_init),
265    	KOBJMETHOD(channel_free,		vchan_free),
266    	KOBJMETHOD(channel_setformat,		vchan_setformat),
267    	KOBJMETHOD(channel_setspeed,		vchan_setspeed),
268    	KOBJMETHOD(channel_setblocksize,	vchan_setblocksize),
269    	KOBJMETHOD(channel_trigger,		vchan_trigger),
270    	KOBJMETHOD(channel_getcaps,		vchan_getcaps),
271	{ 0, 0 }
272};
273CHANNEL_DECLARE(vchan);
274
275/*
276 * On the fly vchan rate settings
277 */
278#ifdef SND_DYNSYSCTL
279static int
280sysctl_hw_snd_vchanrate(SYSCTL_HANDLER_ARGS)
281{
282	struct snddev_info *d;
283    	struct snddev_channel *sce;
284	struct pcm_channel *c, *ch = NULL, *fake;
285	struct pcmchan_caps *caps;
286	int err = 0;
287	int newspd = 0;
288
289	d = oidp->oid_arg1;
290	if (!(d->flags & SD_F_AUTOVCHAN) || d->vchancount < 1)
291		return EINVAL;
292	SLIST_FOREACH(sce, &d->channels, link) {
293		c = sce->channel;
294		CHN_LOCK(c);
295		if (c->direction == PCMDIR_PLAY) {
296			if (c->flags & CHN_F_VIRTUAL) {
297				if (req->newptr != NULL &&
298						(c->flags & CHN_F_BUSY)) {
299					CHN_UNLOCK(c);
300					return EBUSY;
301				}
302				if (ch == NULL)
303					ch = c->parentchannel;
304			}
305		}
306		CHN_UNLOCK(c);
307	}
308	if (ch != NULL) {
309		CHN_LOCK(ch);
310		newspd = ch->speed;
311		CHN_UNLOCK(ch);
312	}
313	err = sysctl_handle_int(oidp, &newspd, sizeof(newspd), req);
314	if (err == 0 && req->newptr != NULL) {
315		if (ch == NULL || newspd < 1 ||
316				newspd < feeder_rate_ratemin ||
317				newspd > feeder_rate_ratemax)
318			return EINVAL;
319		if (pcm_inprog(d, 1) != 1) {
320			pcm_inprog(d, -1);
321			return EINPROGRESS;
322		}
323		CHN_LOCK(ch);
324		caps = chn_getcaps(ch);
325		if (caps == NULL || newspd < caps->minspeed ||
326				newspd > caps->maxspeed) {
327			CHN_UNLOCK(ch);
328			pcm_inprog(d, -1);
329			return EINVAL;
330		}
331		if (newspd != ch->speed) {
332			err = chn_setspeed(ch, newspd);
333			/*
334			 * Try to avoid FEEDER_RATE on parent channel if the
335			 * requested value is not supported by the hardware.
336			 */
337			if (!err && (ch->feederflags & (1 << FEEDER_RATE))) {
338				newspd = sndbuf_getspd(ch->bufhard);
339				err = chn_setspeed(ch, newspd);
340			}
341			CHN_UNLOCK(ch);
342			if (err == 0) {
343				fake = pcm_getfakechan(d);
344				if (fake != NULL) {
345					CHN_LOCK(fake);
346					fake->speed = newspd;
347					CHN_UNLOCK(fake);
348				}
349			}
350		} else
351			CHN_UNLOCK(ch);
352		pcm_inprog(d, -1);
353	}
354	return err;
355}
356#endif
357
358/* virtual channel interface */
359
360int
361vchan_create(struct pcm_channel *parent)
362{
363    	struct snddev_info *d = parent->parentsnddev;
364	struct pcmchan_children *pce;
365	struct pcm_channel *child, *fake;
366	struct pcmchan_caps *parent_caps;
367	int err, first, speed = 0;
368
369	if (!(parent->flags & CHN_F_BUSY))
370		return EBUSY;
371
372
373	CHN_UNLOCK(parent);
374
375	pce = malloc(sizeof(*pce), M_DEVBUF, M_WAITOK | M_ZERO);
376	if (!pce) {
377   		CHN_LOCK(parent);
378		return ENOMEM;
379	}
380
381	/* create a new playback channel */
382	child = pcm_chn_create(d, parent, &vchan_class, PCMDIR_VIRTUAL, parent);
383	if (!child) {
384		free(pce, M_DEVBUF);
385   		CHN_LOCK(parent);
386		return ENODEV;
387	}
388	pce->channel = child;
389
390	/* add us to our grandparent's channel list */
391	/*
392	 * XXX maybe we shouldn't always add the dev_t
393 	 */
394	err = pcm_chn_add(d, child);
395	if (err) {
396		pcm_chn_destroy(child);
397		free(pce, M_DEVBUF);
398		CHN_LOCK(parent);
399		return err;
400	}
401
402   	CHN_LOCK(parent);
403	/* add us to our parent channel's children */
404	first = SLIST_EMPTY(&parent->children);
405	SLIST_INSERT_HEAD(&parent->children, pce, link);
406	parent->flags |= CHN_F_HAS_VCHAN;
407
408	if (first) {
409		parent_caps = chn_getcaps(parent);
410		if (parent_caps == NULL)
411			err = EINVAL;
412
413		if (!err)
414			err = chn_reset(parent, AFMT_STEREO | AFMT_S16_LE);
415
416		if (!err) {
417			fake = pcm_getfakechan(d);
418			if (fake != NULL) {
419				/*
420				 * Avoid querying kernel hint, use saved value
421				 * from fake channel.
422				 */
423				CHN_UNLOCK(parent);
424				CHN_LOCK(fake);
425				speed = fake->speed;
426				CHN_UNLOCK(fake);
427				CHN_LOCK(parent);
428			}
429
430			/*
431			 * This is very sad. Few soundcards advertised as being
432			 * able to do (insanely) higher/lower speed, but in
433			 * reality, they simply can't. At least, we give user chance
434			 * to set sane value via kernel hints or sysctl.
435			 */
436			if (speed < 1) {
437				int r;
438				CHN_UNLOCK(parent);
439				r = resource_int_value(device_get_name(parent->dev),
440							device_get_unit(parent->dev),
441								"vchanrate", &speed);
442				CHN_LOCK(parent);
443				if (r != 0)
444					speed = VCHAN_DEFAULT_SPEED;
445			}
446
447			/*
448			 * Limit speed based on driver caps.
449			 * This is supposed to help fixed rate, non-VRA
450			 * AC97 cards, but.. (see below)
451			 */
452			if (speed < parent_caps->minspeed)
453				speed = parent_caps->minspeed;
454			if (speed > parent_caps->maxspeed)
455				speed = parent_caps->maxspeed;
456
457			/*
458			 * We still need to limit the speed between
459			 * feeder_rate_ratemin <-> feeder_rate_ratemax. This is
460			 * just an escape goat if all of the above failed
461			 * miserably.
462			 */
463			if (speed < feeder_rate_ratemin)
464				speed = feeder_rate_ratemin;
465			if (speed > feeder_rate_ratemax)
466				speed = feeder_rate_ratemax;
467
468			err = chn_setspeed(parent, speed);
469			/*
470			 * Try to avoid FEEDER_RATE on parent channel if the
471			 * requested value is not supported by the hardware.
472			 */
473			if (!err && (parent->feederflags & (1 << FEEDER_RATE))) {
474				speed = sndbuf_getspd(parent->bufhard);
475				err = chn_setspeed(parent, speed);
476			}
477
478			if (!err && fake != NULL) {
479				/*
480				 * Save new value to fake channel.
481				 */
482				CHN_UNLOCK(parent);
483				CHN_LOCK(fake);
484				fake->speed = speed;
485				CHN_UNLOCK(fake);
486				CHN_LOCK(parent);
487			}
488		}
489
490		if (err) {
491			SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
492			parent->flags &= ~CHN_F_HAS_VCHAN;
493			CHN_UNLOCK(parent);
494			free(pce, M_DEVBUF);
495			pcm_chn_remove(d, child);
496			pcm_chn_destroy(child);
497			CHN_LOCK(parent);
498			return err;
499		}
500	}
501
502	return 0;
503}
504
505int
506vchan_destroy(struct pcm_channel *c)
507{
508	struct pcm_channel *parent = c->parentchannel;
509    	struct snddev_info *d = parent->parentsnddev;
510	struct pcmchan_children *pce;
511	struct snddev_channel *sce;
512	int err, last;
513
514	CHN_LOCK(parent);
515	if (!(parent->flags & CHN_F_BUSY)) {
516		CHN_UNLOCK(parent);
517		return EBUSY;
518	}
519	if (SLIST_EMPTY(&parent->children)) {
520		CHN_UNLOCK(parent);
521		return EINVAL;
522	}
523
524	/* remove us from our parent's children list */
525	SLIST_FOREACH(pce, &parent->children, link) {
526		if (pce->channel == c)
527			goto gotch;
528	}
529	CHN_UNLOCK(parent);
530	return EINVAL;
531gotch:
532	SLIST_FOREACH(sce, &d->channels, link) {
533		if (sce->channel == c) {
534			if (sce->dsp_devt)
535				destroy_dev(sce->dsp_devt);
536			if (sce->dspW_devt)
537				destroy_dev(sce->dspW_devt);
538			if (sce->audio_devt)
539				destroy_dev(sce->audio_devt);
540			if (sce->dspr_devt)
541				destroy_dev(sce->dspr_devt);
542			break;
543		}
544	}
545	SLIST_REMOVE(&parent->children, pce, pcmchan_children, link);
546	free(pce, M_DEVBUF);
547
548	last = SLIST_EMPTY(&parent->children);
549	if (last) {
550		parent->flags &= ~CHN_F_BUSY;
551		parent->flags &= ~CHN_F_HAS_VCHAN;
552	}
553
554	/* remove us from our grandparent's channel list */
555	err = pcm_chn_remove(d, c);
556
557	CHN_UNLOCK(parent);
558	/* destroy ourselves */
559	if (!err)
560		err = pcm_chn_destroy(c);
561
562#if 0
563	if (!err && last) {
564		CHN_LOCK(parent);
565		chn_reset(parent, chn_getcaps(parent)->fmtlist[0]);
566		chn_setspeed(parent, chn_getcaps(parent)->minspeed);
567		CHN_UNLOCK(parent);
568	}
569#endif
570
571	return err;
572}
573
574int
575vchan_initsys(device_t dev)
576{
577#ifdef SND_DYNSYSCTL
578	struct snddev_info *d;
579
580    	d = device_get_softc(dev);
581	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
582            OID_AUTO, "vchans", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
583	    sysctl_hw_snd_vchans, "I", "");
584	SYSCTL_ADD_PROC(snd_sysctl_tree(dev), SYSCTL_CHILDREN(snd_sysctl_tree_top(dev)),
585	    OID_AUTO, "vchanrate", CTLTYPE_INT | CTLFLAG_RW, d, sizeof(d),
586	    sysctl_hw_snd_vchanrate, "I", "");
587#endif
588
589	return 0;
590}
591