xref: /onnv-gate/usr/src/uts/common/io/audio/impl/audio_sun.c (revision 12165:e481916a5729)
19484Sgarrett.damore@Sun.COM /*
29484Sgarrett.damore@Sun.COM  * CDDL HEADER START
39484Sgarrett.damore@Sun.COM  *
49484Sgarrett.damore@Sun.COM  * The contents of this file are subject to the terms of the
59484Sgarrett.damore@Sun.COM  * Common Development and Distribution License (the "License").
69484Sgarrett.damore@Sun.COM  * You may not use this file except in compliance with the License.
79484Sgarrett.damore@Sun.COM  *
89484Sgarrett.damore@Sun.COM  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
99484Sgarrett.damore@Sun.COM  * or http://www.opensolaris.org/os/licensing.
109484Sgarrett.damore@Sun.COM  * See the License for the specific language governing permissions
119484Sgarrett.damore@Sun.COM  * and limitations under the License.
129484Sgarrett.damore@Sun.COM  *
139484Sgarrett.damore@Sun.COM  * When distributing Covered Code, include this CDDL HEADER in each
149484Sgarrett.damore@Sun.COM  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
159484Sgarrett.damore@Sun.COM  * If applicable, add the following below this CDDL HEADER, with the
169484Sgarrett.damore@Sun.COM  * fields enclosed by brackets "[]" replaced with your own identifying
179484Sgarrett.damore@Sun.COM  * information: Portions Copyright [yyyy] [name of copyright owner]
189484Sgarrett.damore@Sun.COM  *
199484Sgarrett.damore@Sun.COM  * CDDL HEADER END
209484Sgarrett.damore@Sun.COM  */
219484Sgarrett.damore@Sun.COM /*
22*12165Sgdamore@opensolaris.org  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
239484Sgarrett.damore@Sun.COM  */
249484Sgarrett.damore@Sun.COM 
259484Sgarrett.damore@Sun.COM /*
269484Sgarrett.damore@Sun.COM  * Sun audio(7I) and mixer(7I) personality.
279484Sgarrett.damore@Sun.COM  *
289484Sgarrett.damore@Sun.COM  * There are some "undocumented" details of how legacy Sun audio
299484Sgarrett.damore@Sun.COM  * interfaces work.  The following "rules" were derived from reading the
309484Sgarrett.damore@Sun.COM  * legacy Sun mixer code, and to the best of our knowledge are not
319484Sgarrett.damore@Sun.COM  * documented elsewhere.
329484Sgarrett.damore@Sun.COM  *
339484Sgarrett.damore@Sun.COM  * - We create a "fake" audio device, which behaves like a classic
349484Sgarrett.damore@Sun.COM  *   exclusive audio device, for each PID, as determined during open(2).
359484Sgarrett.damore@Sun.COM  *
369484Sgarrett.damore@Sun.COM  * - Different processes don't interfere with each other.  Even though
379484Sgarrett.damore@Sun.COM  *   they are running concurrently, they each think they have exclusive
389484Sgarrett.damore@Sun.COM  *   control over the audio device.
399484Sgarrett.damore@Sun.COM  *
409484Sgarrett.damore@Sun.COM  * - Read and write directions operate independent of each other.  That
419484Sgarrett.damore@Sun.COM  *   is, a device open for reading won't intefere with a future open for
429484Sgarrett.damore@Sun.COM  *   writing, and vice versa.  This is true even within the same process.
439484Sgarrett.damore@Sun.COM  *
449484Sgarrett.damore@Sun.COM  * - Because the virtualization is by PID, strange behavior may occur
459484Sgarrett.damore@Sun.COM  *   if a process tries to open an audio device at the same time it
469484Sgarrett.damore@Sun.COM  *   has already received a file descriptor from another process (such
479484Sgarrett.damore@Sun.COM  *   through inheritence via fork()).
489484Sgarrett.damore@Sun.COM  *
499484Sgarrett.damore@Sun.COM  * - The "fake" audio device has no control over physical settings.
509484Sgarrett.damore@Sun.COM  *   It sees only the software attenuation-based volumes for play and
519484Sgarrett.damore@Sun.COM  *   record, and has no support for alternate input or output ports or
529484Sgarrett.damore@Sun.COM  *   access to the monitoring features of the hardware.
539484Sgarrett.damore@Sun.COM  *
549484Sgarrett.damore@Sun.COM  * - Explicit notificaton signals (SIGPOLL) are only ever sent up the
559484Sgarrett.damore@Sun.COM  *   audioctl node -- never up a regular audio node.  (The stream head
569484Sgarrett.damore@Sun.COM  *   may still issue SIGPOLL based on readability/writability of
579484Sgarrett.damore@Sun.COM  *   course.)
589484Sgarrett.damore@Sun.COM  *
599484Sgarrett.damore@Sun.COM  * - Corollary: processes that want asynch. notifications will open
609484Sgarrett.damore@Sun.COM  *   /dev/audioctl as well as /dev/audio.
619484Sgarrett.damore@Sun.COM  *
629484Sgarrett.damore@Sun.COM  * - We don't support the MIXER mode at all.
639484Sgarrett.damore@Sun.COM  *
649484Sgarrett.damore@Sun.COM  * - By corollary, a process is only allowed to open /dev/audio once
659484Sgarrett.damore@Sun.COM  *   (in each direction.)
669484Sgarrett.damore@Sun.COM  *
679484Sgarrett.damore@Sun.COM  * - Attempts to open /dev/audio in duplex mode (O_RDWR) fail (EBUSY)
689484Sgarrett.damore@Sun.COM  *   if the device cannot support duplex operation.
699484Sgarrett.damore@Sun.COM  *
709484Sgarrett.damore@Sun.COM  * - Attempts to open a device with FREAD set fail if the device is not
719484Sgarrett.damore@Sun.COM  *   capable of recording.  (Likewise for FWRITE and playback.)
729484Sgarrett.damore@Sun.COM  *
739484Sgarrett.damore@Sun.COM  * - No data transfer is permitted for audioctl nodes.  (No actual
749484Sgarrett.damore@Sun.COM  *   record or play.)
759484Sgarrett.damore@Sun.COM  *
769484Sgarrett.damore@Sun.COM  * - Sun audio does not support any formats other than linear and
779484Sgarrett.damore@Sun.COM  *   ULAW/ALAW.  I.e. it will never support AC3 or other "opaque"
789484Sgarrett.damore@Sun.COM  *   streams which require special handling.
799484Sgarrett.damore@Sun.COM  *
809484Sgarrett.damore@Sun.COM  * - Sun audio only supports stereo or monophonic data streams.
819484Sgarrett.damore@Sun.COM  */
829484Sgarrett.damore@Sun.COM 
839484Sgarrett.damore@Sun.COM #include <sys/types.h>
849484Sgarrett.damore@Sun.COM #include <sys/open.h>
859484Sgarrett.damore@Sun.COM #include <sys/errno.h>
869484Sgarrett.damore@Sun.COM #include <sys/audio.h>
879484Sgarrett.damore@Sun.COM #include <sys/mixer.h>
889484Sgarrett.damore@Sun.COM #include <sys/file.h>
899484Sgarrett.damore@Sun.COM #include <sys/stropts.h>
909484Sgarrett.damore@Sun.COM #include <sys/strsun.h>
919484Sgarrett.damore@Sun.COM #include <sys/sysmacros.h>
929484Sgarrett.damore@Sun.COM #include <sys/list.h>
939484Sgarrett.damore@Sun.COM #include <sys/note.h>
949484Sgarrett.damore@Sun.COM #include <sys/stat.h>
959484Sgarrett.damore@Sun.COM #include <sys/ddi.h>
969484Sgarrett.damore@Sun.COM #include <sys/sunddi.h>
979484Sgarrett.damore@Sun.COM #include "audio_client.h"
989484Sgarrett.damore@Sun.COM 
9910157Sgdamore@opensolaris.org typedef struct daclient daclient_t;
10010157Sgdamore@opensolaris.org typedef struct dadev dadev_t;
10110157Sgdamore@opensolaris.org typedef struct daproc daproc_t;
1029484Sgarrett.damore@Sun.COM 
1039484Sgarrett.damore@Sun.COM /* common structure shared between both audioctl and audio nodes */
10410157Sgdamore@opensolaris.org struct daclient {
10510157Sgdamore@opensolaris.org 	daproc_t		*dc_proc;
10610157Sgdamore@opensolaris.org 	dadev_t			*dc_dev;
10710157Sgdamore@opensolaris.org 	audio_client_t		*dc_client;
10810157Sgdamore@opensolaris.org 	queue_t			*dc_wq;
10910157Sgdamore@opensolaris.org 	unsigned		dc_eof;
11010157Sgdamore@opensolaris.org 	list_t			dc_eofcnt;
11110157Sgdamore@opensolaris.org 	kmutex_t		dc_lock;
11210157Sgdamore@opensolaris.org 	mblk_t			*dc_draining;
1139484Sgarrett.damore@Sun.COM };
1149484Sgarrett.damore@Sun.COM 
1159484Sgarrett.damore@Sun.COM struct eofcnt {
1169484Sgarrett.damore@Sun.COM 	list_node_t		linkage;
1179484Sgarrett.damore@Sun.COM 	uint64_t		tail;
1189484Sgarrett.damore@Sun.COM };
1199484Sgarrett.damore@Sun.COM 
12010157Sgdamore@opensolaris.org struct dadev {
1219484Sgarrett.damore@Sun.COM 	audio_dev_t		*d_dev;
1229484Sgarrett.damore@Sun.COM 
1239484Sgarrett.damore@Sun.COM 	list_t			d_procs;
1249484Sgarrett.damore@Sun.COM 	kmutex_t		d_mx;
1259484Sgarrett.damore@Sun.COM 	kcondvar_t		d_cv;
1269484Sgarrett.damore@Sun.COM };
1279484Sgarrett.damore@Sun.COM 
12810157Sgdamore@opensolaris.org struct daproc {
1299484Sgarrett.damore@Sun.COM 	pid_t			p_id;
1309484Sgarrett.damore@Sun.COM 	struct audio_info	p_info;
1319484Sgarrett.damore@Sun.COM 	int			p_refcnt;
1329484Sgarrett.damore@Sun.COM 	int			p_oflag;
1339484Sgarrett.damore@Sun.COM 	list_node_t		p_linkage;
13410157Sgdamore@opensolaris.org 	dadev_t			*p_dev;
13510157Sgdamore@opensolaris.org 	audio_client_t		*p_writer;
13610157Sgdamore@opensolaris.org 	audio_client_t		*p_reader;
1379484Sgarrett.damore@Sun.COM };
1389484Sgarrett.damore@Sun.COM 
13910157Sgdamore@opensolaris.org int devaudio_proc_hold(audio_client_t *, int);
14010157Sgdamore@opensolaris.org void devaudio_proc_release(audio_client_t *);
14110157Sgdamore@opensolaris.org static void devaudio_proc_update(daproc_t *);
1429484Sgarrett.damore@Sun.COM 
1439484Sgarrett.damore@Sun.COM 
1449484Sgarrett.damore@Sun.COM static int
devaudio_compose_format(audio_prinfo_t * prinfo)14510157Sgdamore@opensolaris.org devaudio_compose_format(audio_prinfo_t *prinfo)
1469484Sgarrett.damore@Sun.COM {
1479484Sgarrett.damore@Sun.COM 	switch (prinfo->precision) {
1489484Sgarrett.damore@Sun.COM 	case 8:
1499484Sgarrett.damore@Sun.COM 		switch (prinfo->encoding) {
1509484Sgarrett.damore@Sun.COM 		case AUDIO_ENCODING_ULAW:
1519484Sgarrett.damore@Sun.COM 			return (AUDIO_FORMAT_ULAW);
1529484Sgarrett.damore@Sun.COM 		case AUDIO_ENCODING_ALAW:
1539484Sgarrett.damore@Sun.COM 			return (AUDIO_FORMAT_ALAW);
1549484Sgarrett.damore@Sun.COM 		case AUDIO_ENCODING_LINEAR8:
1559484Sgarrett.damore@Sun.COM 			return (AUDIO_FORMAT_U8);
1569484Sgarrett.damore@Sun.COM 		case AUDIO_ENCODING_LINEAR:
1579484Sgarrett.damore@Sun.COM 			return (AUDIO_FORMAT_S8);
1589484Sgarrett.damore@Sun.COM 		}
1599484Sgarrett.damore@Sun.COM 		break;
1609484Sgarrett.damore@Sun.COM 	case 16:
1619484Sgarrett.damore@Sun.COM 		if (prinfo->encoding == AUDIO_ENCODING_LINEAR)
1629484Sgarrett.damore@Sun.COM 			return (AUDIO_FORMAT_S16_NE);
1639484Sgarrett.damore@Sun.COM 		break;
1649484Sgarrett.damore@Sun.COM 	case 32:
1659484Sgarrett.damore@Sun.COM 		if (prinfo->encoding == AUDIO_ENCODING_LINEAR)
1669484Sgarrett.damore@Sun.COM 			return (AUDIO_FORMAT_S32_NE);
1679484Sgarrett.damore@Sun.COM 		break;
1689484Sgarrett.damore@Sun.COM 	}
1699484Sgarrett.damore@Sun.COM 	return (AUDIO_FORMAT_NONE);
1709484Sgarrett.damore@Sun.COM 
1719484Sgarrett.damore@Sun.COM }
1729484Sgarrett.damore@Sun.COM 
1739484Sgarrett.damore@Sun.COM static void
devaudio_decompose_format(audio_prinfo_t * prinfo,int afmt)17410157Sgdamore@opensolaris.org devaudio_decompose_format(audio_prinfo_t *prinfo, int afmt)
1759484Sgarrett.damore@Sun.COM {
1769484Sgarrett.damore@Sun.COM 	int	e, p;
1779484Sgarrett.damore@Sun.COM 
1789484Sgarrett.damore@Sun.COM 	/*
1799484Sgarrett.damore@Sun.COM 	 * N.B.: Even though some of the formats below can't be set by
1809484Sgarrett.damore@Sun.COM 	 * this personality, reporting them (using the closest match)
1819484Sgarrett.damore@Sun.COM 	 * allows this personality to roughly approximate settings for
1829484Sgarrett.damore@Sun.COM 	 * other streams.  It would be incredibly poor form for any
1839484Sgarrett.damore@Sun.COM 	 * personality to modify the format settings for a different
1849484Sgarrett.damore@Sun.COM 	 * personality, so we don't worry about that case.
1859484Sgarrett.damore@Sun.COM 	 */
1869484Sgarrett.damore@Sun.COM 
1879484Sgarrett.damore@Sun.COM 	switch (afmt) {
1889484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_ULAW:
1899484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_ULAW;
1909484Sgarrett.damore@Sun.COM 		p = 8;
1919484Sgarrett.damore@Sun.COM 		break;
1929484Sgarrett.damore@Sun.COM 
1939484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_ALAW:
1949484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_ALAW;
1959484Sgarrett.damore@Sun.COM 		p = 8;
1969484Sgarrett.damore@Sun.COM 		break;
1979484Sgarrett.damore@Sun.COM 
1989484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_U8:
1999484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_LINEAR8;
2009484Sgarrett.damore@Sun.COM 		p = 8;
2019484Sgarrett.damore@Sun.COM 		break;
2029484Sgarrett.damore@Sun.COM 
2039484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S8:
2049484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_LINEAR;
2059484Sgarrett.damore@Sun.COM 		p = 8;
2069484Sgarrett.damore@Sun.COM 		break;
2079484Sgarrett.damore@Sun.COM 
2089484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S16_NE:
2099484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S16_OE:
2109484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_U16_NE:
2119484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_U16_OE:
2129484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_LINEAR;
2139484Sgarrett.damore@Sun.COM 		p = 16;
2149484Sgarrett.damore@Sun.COM 		break;
2159484Sgarrett.damore@Sun.COM 
2169484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S24_NE:
2179484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S24_OE:
2189484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S24_PACKED:
2199484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_LINEAR;
2209484Sgarrett.damore@Sun.COM 		p = 24;
2219484Sgarrett.damore@Sun.COM 		break;
2229484Sgarrett.damore@Sun.COM 
2239484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S32_NE:
2249484Sgarrett.damore@Sun.COM 	case AUDIO_FORMAT_S32_OE:
2259484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_LINEAR;
2269484Sgarrett.damore@Sun.COM 		p = 32;
2279484Sgarrett.damore@Sun.COM 		break;
2289484Sgarrett.damore@Sun.COM 
2299484Sgarrett.damore@Sun.COM 	default:
2309484Sgarrett.damore@Sun.COM 		/* all other formats (e.g. AC3) are uninterpreted */
2319484Sgarrett.damore@Sun.COM 		e = AUDIO_ENCODING_NONE;
2329484Sgarrett.damore@Sun.COM 		p = 32;
2339484Sgarrett.damore@Sun.COM 		break;
2349484Sgarrett.damore@Sun.COM 	}
2359484Sgarrett.damore@Sun.COM 
2369484Sgarrett.damore@Sun.COM 	prinfo->encoding = e;
2379484Sgarrett.damore@Sun.COM 	prinfo->precision = p;
2389484Sgarrett.damore@Sun.COM }
2399484Sgarrett.damore@Sun.COM 
24010157Sgdamore@opensolaris.org static daproc_t *
devaudio_proc_alloc(audio_client_t * c)24110157Sgdamore@opensolaris.org devaudio_proc_alloc(audio_client_t *c)
2429484Sgarrett.damore@Sun.COM {
2439484Sgarrett.damore@Sun.COM 	audio_info_t	*info;
2449484Sgarrett.damore@Sun.COM 	audio_prinfo_t	*prinfo;
2459484Sgarrett.damore@Sun.COM 	uint32_t	caps;
24610157Sgdamore@opensolaris.org 	daproc_t	*proc;
2479484Sgarrett.damore@Sun.COM 
2489484Sgarrett.damore@Sun.COM 	if ((proc = kmem_zalloc(sizeof (*proc), KM_NOSLEEP)) == NULL) {
2499484Sgarrett.damore@Sun.COM 		return (NULL);
2509484Sgarrett.damore@Sun.COM 	}
2519484Sgarrett.damore@Sun.COM 	info = &proc->p_info;
2529484Sgarrett.damore@Sun.COM 
2539484Sgarrett.damore@Sun.COM 	/*
2549484Sgarrett.damore@Sun.COM 	 * audio(7I) says: Upon the initial open() of the audio
2559484Sgarrett.damore@Sun.COM 	 * device, the driver resets the data format of the device to
2569484Sgarrett.damore@Sun.COM 	 * the default state of 8-bit, 8Khz, mono u-Law data.
2579484Sgarrett.damore@Sun.COM 	 */
2589484Sgarrett.damore@Sun.COM 	prinfo = &info->play;
2599484Sgarrett.damore@Sun.COM 	prinfo->channels =	1;
2609484Sgarrett.damore@Sun.COM 	prinfo->sample_rate =	8000;
2619484Sgarrett.damore@Sun.COM 	prinfo->encoding =	AUDIO_ENCODING_ULAW;
2629484Sgarrett.damore@Sun.COM 	prinfo->precision =	8;
2639484Sgarrett.damore@Sun.COM 	prinfo->gain =		AUDIO_MAX_GAIN;
2649484Sgarrett.damore@Sun.COM 	prinfo->balance =	AUDIO_MID_BALANCE;
2659484Sgarrett.damore@Sun.COM 	prinfo->buffer_size =	8192;
2669484Sgarrett.damore@Sun.COM 	prinfo->pause =		B_FALSE;
2679484Sgarrett.damore@Sun.COM 	prinfo->waiting =	B_FALSE;
2689484Sgarrett.damore@Sun.COM 	prinfo->open =		B_FALSE;
2699484Sgarrett.damore@Sun.COM 	prinfo->active =	B_FALSE;
2709484Sgarrett.damore@Sun.COM 	prinfo->samples =	0;
2719484Sgarrett.damore@Sun.COM 	prinfo->eof =		0;
2729484Sgarrett.damore@Sun.COM 	prinfo->error =		0;
2739484Sgarrett.damore@Sun.COM 	prinfo->minordev =	0;
2749484Sgarrett.damore@Sun.COM 	prinfo->port =		AUDIO_SPEAKER;
2759484Sgarrett.damore@Sun.COM 	prinfo->avail_ports =	AUDIO_SPEAKER;
2769484Sgarrett.damore@Sun.COM 	prinfo->mod_ports =	AUDIO_NONE;
2779484Sgarrett.damore@Sun.COM 	prinfo->_xxx =		0;
2789484Sgarrett.damore@Sun.COM 
2799484Sgarrett.damore@Sun.COM 	prinfo = &info->record;
2809484Sgarrett.damore@Sun.COM 	prinfo->channels =	1;
2819484Sgarrett.damore@Sun.COM 	prinfo->sample_rate =	8000;
2829484Sgarrett.damore@Sun.COM 	prinfo->encoding =	AUDIO_ENCODING_ULAW;
2839484Sgarrett.damore@Sun.COM 	prinfo->precision =	8;
2849484Sgarrett.damore@Sun.COM 	prinfo->gain =		AUDIO_MAX_GAIN;
2859484Sgarrett.damore@Sun.COM 	prinfo->balance =	AUDIO_MID_BALANCE;
2869484Sgarrett.damore@Sun.COM 	prinfo->buffer_size =	8192;
2879484Sgarrett.damore@Sun.COM 	prinfo->waiting =	B_FALSE;
2889484Sgarrett.damore@Sun.COM 	prinfo->open =  	B_FALSE;
2899484Sgarrett.damore@Sun.COM 	prinfo->active =	B_FALSE;
2909484Sgarrett.damore@Sun.COM 	prinfo->samples =	0;
2919484Sgarrett.damore@Sun.COM 	prinfo->eof =		0;
2929484Sgarrett.damore@Sun.COM 	prinfo->error =		0;
2939484Sgarrett.damore@Sun.COM 	prinfo->minordev =	0;
2949484Sgarrett.damore@Sun.COM 	prinfo->port =		AUDIO_MICROPHONE;
2959484Sgarrett.damore@Sun.COM 	prinfo->avail_ports =	AUDIO_MICROPHONE;
2969484Sgarrett.damore@Sun.COM 	prinfo->mod_ports =	AUDIO_MICROPHONE;
2979484Sgarrett.damore@Sun.COM 
2989484Sgarrett.damore@Sun.COM 	info->output_muted =	B_FALSE;
2999484Sgarrett.damore@Sun.COM 	/* pretend we don't have a software mixer - we don't support the API */
3009484Sgarrett.damore@Sun.COM 	info->hw_features =	0;
3019484Sgarrett.damore@Sun.COM 	info->sw_features =	0;
3029484Sgarrett.damore@Sun.COM 	info->sw_features_enabled = 0;
3039484Sgarrett.damore@Sun.COM 
3049484Sgarrett.damore@Sun.COM 	caps = auclnt_get_dev_capab(auclnt_get_dev(c));
3059484Sgarrett.damore@Sun.COM 	if (caps & AUDIO_CLIENT_CAP_PLAY)
3069484Sgarrett.damore@Sun.COM 		info->hw_features |= AUDIO_HWFEATURE_PLAY;
3079484Sgarrett.damore@Sun.COM 	if (caps & AUDIO_CLIENT_CAP_RECORD)
3089484Sgarrett.damore@Sun.COM 		info->hw_features |= AUDIO_HWFEATURE_RECORD;
3099484Sgarrett.damore@Sun.COM 	if (caps & AUDIO_CLIENT_CAP_DUPLEX)
3109484Sgarrett.damore@Sun.COM 		info->hw_features |= AUDIO_HWFEATURE_DUPLEX;
3119484Sgarrett.damore@Sun.COM 
3129484Sgarrett.damore@Sun.COM 	return (proc);
3139484Sgarrett.damore@Sun.COM }
3149484Sgarrett.damore@Sun.COM 
3159484Sgarrett.damore@Sun.COM static void
devaudio_proc_free(daproc_t * proc)31610157Sgdamore@opensolaris.org devaudio_proc_free(daproc_t *proc)
3179484Sgarrett.damore@Sun.COM {
3189484Sgarrett.damore@Sun.COM 	kmem_free(proc, sizeof (*proc));
3199484Sgarrett.damore@Sun.COM }
3209484Sgarrett.damore@Sun.COM 
3219484Sgarrett.damore@Sun.COM int
devaudio_proc_hold(audio_client_t * c,int oflag)32210157Sgdamore@opensolaris.org devaudio_proc_hold(audio_client_t *c, int oflag)
3239484Sgarrett.damore@Sun.COM {
3249484Sgarrett.damore@Sun.COM 	pid_t		pid;
32510157Sgdamore@opensolaris.org 	daproc_t	*proc;
32610157Sgdamore@opensolaris.org 	dadev_t		*dev;
32710157Sgdamore@opensolaris.org 	daclient_t	*dc;
3289484Sgarrett.damore@Sun.COM 	list_t		*l;
3299484Sgarrett.damore@Sun.COM 	audio_dev_t	*adev;
3309484Sgarrett.damore@Sun.COM 	int		rv;
3319484Sgarrett.damore@Sun.COM 
3329484Sgarrett.damore@Sun.COM 	adev = auclnt_get_dev(c);
3339484Sgarrett.damore@Sun.COM 
33410157Sgdamore@opensolaris.org 	/* first allocate and initialize the daclient private data */
33510157Sgdamore@opensolaris.org 	if ((dc = kmem_zalloc(sizeof (*dc), KM_NOSLEEP)) == NULL) {
3369484Sgarrett.damore@Sun.COM 		return (ENOMEM);
3379484Sgarrett.damore@Sun.COM 	}
3389484Sgarrett.damore@Sun.COM 
33910157Sgdamore@opensolaris.org 	mutex_init(&dc->dc_lock, NULL, MUTEX_DRIVER, NULL);
34010157Sgdamore@opensolaris.org 	list_create(&dc->dc_eofcnt, sizeof (struct eofcnt),
3419484Sgarrett.damore@Sun.COM 	    offsetof(struct eofcnt, linkage));
34210157Sgdamore@opensolaris.org 	auclnt_set_private(c, dc);
3439484Sgarrett.damore@Sun.COM 
34410157Sgdamore@opensolaris.org 	dev = auclnt_get_dev_minor_data(adev, AUDIO_MINOR_DEVAUDIO);
34510157Sgdamore@opensolaris.org 	l = &dev->d_procs;
3469484Sgarrett.damore@Sun.COM 	pid = auclnt_get_pid(c);
3479484Sgarrett.damore@Sun.COM 
3489484Sgarrett.damore@Sun.COM 	/* set a couple of common fields */
34910157Sgdamore@opensolaris.org 	dc->dc_client = c;
35010157Sgdamore@opensolaris.org 	dc->dc_dev = dev;
3519484Sgarrett.damore@Sun.COM 
35210157Sgdamore@opensolaris.org 	mutex_enter(&dev->d_mx);
3539484Sgarrett.damore@Sun.COM 	for (proc = list_head(l); proc != NULL; proc = list_next(l, proc)) {
3549484Sgarrett.damore@Sun.COM 		if (proc->p_id == pid) {
3559484Sgarrett.damore@Sun.COM 			proc->p_refcnt++;
3569484Sgarrett.damore@Sun.COM 			break;
3579484Sgarrett.damore@Sun.COM 		}
3589484Sgarrett.damore@Sun.COM 	}
3599484Sgarrett.damore@Sun.COM 	if (proc == NULL) {
36010157Sgdamore@opensolaris.org 		if ((proc = devaudio_proc_alloc(c)) == NULL) {
3619484Sgarrett.damore@Sun.COM 			rv = ENOMEM;
3629484Sgarrett.damore@Sun.COM 			goto failed;
3639484Sgarrett.damore@Sun.COM 		}
3649484Sgarrett.damore@Sun.COM 		proc->p_refcnt = 1;
3659484Sgarrett.damore@Sun.COM 		proc->p_id = pid;
36610157Sgdamore@opensolaris.org 		proc->p_dev = dev;
3679484Sgarrett.damore@Sun.COM 		list_insert_tail(l, proc);
3689484Sgarrett.damore@Sun.COM 	}
3699484Sgarrett.damore@Sun.COM 
3709484Sgarrett.damore@Sun.COM 	while (proc->p_oflag & oflag) {
3719484Sgarrett.damore@Sun.COM 
3729484Sgarrett.damore@Sun.COM 		if (oflag & (FNDELAY|FNONBLOCK)) {
3739484Sgarrett.damore@Sun.COM 			rv = EBUSY;
3749484Sgarrett.damore@Sun.COM 			goto failed;
3759484Sgarrett.damore@Sun.COM 		}
3769484Sgarrett.damore@Sun.COM 		if (oflag & FWRITE)
3779484Sgarrett.damore@Sun.COM 			proc->p_info.play.waiting++;
3789484Sgarrett.damore@Sun.COM 		if (oflag & FREAD)
3799484Sgarrett.damore@Sun.COM 			proc->p_info.record.waiting++;
38010157Sgdamore@opensolaris.org 		if (cv_wait_sig(&dev->d_cv, &dev->d_mx) == 0) {
3819484Sgarrett.damore@Sun.COM 			/* interrupted! */
3829484Sgarrett.damore@Sun.COM 			if (oflag & FWRITE)
3839484Sgarrett.damore@Sun.COM 				proc->p_info.play.waiting--;
3849484Sgarrett.damore@Sun.COM 			if (oflag & FREAD)
3859484Sgarrett.damore@Sun.COM 				proc->p_info.record.waiting--;
3869484Sgarrett.damore@Sun.COM 			rv = EINTR;
3879484Sgarrett.damore@Sun.COM 			goto failed;
3889484Sgarrett.damore@Sun.COM 		}
3899484Sgarrett.damore@Sun.COM 		if (oflag & FWRITE)
3909484Sgarrett.damore@Sun.COM 			proc->p_info.play.waiting--;
3919484Sgarrett.damore@Sun.COM 		if (oflag & FREAD)
3929484Sgarrett.damore@Sun.COM 			proc->p_info.record.waiting--;
3939484Sgarrett.damore@Sun.COM 	}
3949484Sgarrett.damore@Sun.COM 
3959484Sgarrett.damore@Sun.COM 	if (oflag & FWRITE) {
3969484Sgarrett.damore@Sun.COM 		audio_prinfo_t	*play = &proc->p_info.play;
3979484Sgarrett.damore@Sun.COM 		audio_stream_t	*sp = auclnt_output_stream(c);
3989484Sgarrett.damore@Sun.COM 
3999484Sgarrett.damore@Sun.COM 		if (((rv = auclnt_set_rate(sp, 8000)) != 0) ||
4009484Sgarrett.damore@Sun.COM 		    ((rv = auclnt_set_format(sp, AUDIO_FORMAT_ULAW)) != 0) ||
4019484Sgarrett.damore@Sun.COM 		    ((rv = auclnt_set_channels(sp, 1)) != 0)) {
4029484Sgarrett.damore@Sun.COM 			goto failed;
4039484Sgarrett.damore@Sun.COM 		}
4049484Sgarrett.damore@Sun.COM 
4059484Sgarrett.damore@Sun.COM 		auclnt_set_samples(sp, 0);
4069484Sgarrett.damore@Sun.COM 		auclnt_set_errors(sp, 0);
4079484Sgarrett.damore@Sun.COM 		play->eof = 0;
4089484Sgarrett.damore@Sun.COM 		play->buffer_size = 8192;
4099484Sgarrett.damore@Sun.COM 
4109484Sgarrett.damore@Sun.COM 		auclnt_set_gain(sp, ((play->gain * 100) / AUDIO_MAX_GAIN));
4119484Sgarrett.damore@Sun.COM 		auclnt_set_muted(sp, proc->p_info.output_muted);
4129484Sgarrett.damore@Sun.COM 		play->open = B_TRUE;
41310157Sgdamore@opensolaris.org 		proc->p_writer = c;
4149484Sgarrett.damore@Sun.COM 		proc->p_oflag |= FWRITE;
4159484Sgarrett.damore@Sun.COM 	}
4169484Sgarrett.damore@Sun.COM 
4179484Sgarrett.damore@Sun.COM 	if (oflag & FREAD) {
4189484Sgarrett.damore@Sun.COM 		audio_prinfo_t	*rec = &proc->p_info.record;
4199484Sgarrett.damore@Sun.COM 		audio_stream_t	*sp = auclnt_input_stream(c);
4209484Sgarrett.damore@Sun.COM 
4219484Sgarrett.damore@Sun.COM 		if (((rv = auclnt_set_rate(sp, 8000)) != 0) ||
4229484Sgarrett.damore@Sun.COM 		    ((rv = auclnt_set_format(sp, AUDIO_FORMAT_ULAW)) != 0) ||
4239484Sgarrett.damore@Sun.COM 		    ((rv = auclnt_set_channels(sp, 1)) != 0)) {
4249484Sgarrett.damore@Sun.COM 			goto failed;
4259484Sgarrett.damore@Sun.COM 		}
4269484Sgarrett.damore@Sun.COM 
4279484Sgarrett.damore@Sun.COM 		auclnt_set_samples(sp, 0);
4289484Sgarrett.damore@Sun.COM 		auclnt_set_errors(sp, 0);
4299484Sgarrett.damore@Sun.COM 		rec->eof = 0;
4309484Sgarrett.damore@Sun.COM 		rec->buffer_size = 8192;
4319484Sgarrett.damore@Sun.COM 
4329484Sgarrett.damore@Sun.COM 		auclnt_set_gain(sp, ((rec->gain * 100) / AUDIO_MAX_GAIN));
4339484Sgarrett.damore@Sun.COM 		rec->open = B_TRUE;
43410157Sgdamore@opensolaris.org 		proc->p_reader = c;
4359484Sgarrett.damore@Sun.COM 		proc->p_oflag |= FREAD;
4369484Sgarrett.damore@Sun.COM 	}
4379484Sgarrett.damore@Sun.COM 
43810157Sgdamore@opensolaris.org 
43910157Sgdamore@opensolaris.org 	dc->dc_wq = auclnt_get_wq(c);
44010157Sgdamore@opensolaris.org 
44110016Sgdamore@opensolaris.org 	/* we update the s_proc last to avoid a race */
44210157Sgdamore@opensolaris.org 	dc->dc_proc = proc;
44310016Sgdamore@opensolaris.org 
44410157Sgdamore@opensolaris.org 	devaudio_proc_update(proc);
4459484Sgarrett.damore@Sun.COM 
44610157Sgdamore@opensolaris.org 	mutex_exit(&dev->d_mx);
4479484Sgarrett.damore@Sun.COM 
4489484Sgarrett.damore@Sun.COM 	return (0);
4499484Sgarrett.damore@Sun.COM 
4509484Sgarrett.damore@Sun.COM failed:
45110157Sgdamore@opensolaris.org 	mutex_exit(&dev->d_mx);
45210157Sgdamore@opensolaris.org 	devaudio_proc_release(c);
4539484Sgarrett.damore@Sun.COM 	return (rv);
4549484Sgarrett.damore@Sun.COM 
4559484Sgarrett.damore@Sun.COM }
4569484Sgarrett.damore@Sun.COM 
4579484Sgarrett.damore@Sun.COM static void
devaudio_clear_eof(audio_client_t * c)45810157Sgdamore@opensolaris.org devaudio_clear_eof(audio_client_t *c)
4599484Sgarrett.damore@Sun.COM {
46010157Sgdamore@opensolaris.org 	struct eofcnt	*eof;
46110157Sgdamore@opensolaris.org 	daclient_t	*dc;
46210157Sgdamore@opensolaris.org 
46310157Sgdamore@opensolaris.org 	dc = auclnt_get_private(c);
46410157Sgdamore@opensolaris.org 	mutex_enter(&dc->dc_lock);
46510157Sgdamore@opensolaris.org 	while ((eof = list_remove_head(&dc->dc_eofcnt)) != NULL) {
4669484Sgarrett.damore@Sun.COM 		kmem_free(eof, sizeof (*eof));
4679484Sgarrett.damore@Sun.COM 	}
46810157Sgdamore@opensolaris.org 	mutex_exit(&dc->dc_lock);
4699484Sgarrett.damore@Sun.COM }
4709484Sgarrett.damore@Sun.COM 
4719484Sgarrett.damore@Sun.COM void
devaudio_proc_release(audio_client_t * c)47210157Sgdamore@opensolaris.org devaudio_proc_release(audio_client_t *c)
4739484Sgarrett.damore@Sun.COM {
47410157Sgdamore@opensolaris.org 	daproc_t	*proc;
47510157Sgdamore@opensolaris.org 	dadev_t		*dev;
4769484Sgarrett.damore@Sun.COM 	mblk_t		*mp;
47710157Sgdamore@opensolaris.org 	daclient_t	*dc;
4789484Sgarrett.damore@Sun.COM 
47910157Sgdamore@opensolaris.org 	dc = auclnt_get_private(c);
48010157Sgdamore@opensolaris.org 	proc = dc->dc_proc;
48110157Sgdamore@opensolaris.org 	dev = dc->dc_dev;
48210157Sgdamore@opensolaris.org 	dc->dc_proc = NULL;
4839484Sgarrett.damore@Sun.COM 
48410157Sgdamore@opensolaris.org 	mutex_enter(&dev->d_mx);
4859484Sgarrett.damore@Sun.COM 
4869484Sgarrett.damore@Sun.COM 	if (proc != NULL) {
4879484Sgarrett.damore@Sun.COM 		proc->p_refcnt--;
4889484Sgarrett.damore@Sun.COM 		ASSERT(proc->p_refcnt >= 0);
4899484Sgarrett.damore@Sun.COM 
49010157Sgdamore@opensolaris.org 		if (c == proc->p_writer) {
4919484Sgarrett.damore@Sun.COM 			proc->p_oflag &= ~FWRITE;
4929484Sgarrett.damore@Sun.COM 			proc->p_writer = NULL;
4939484Sgarrett.damore@Sun.COM 		}
49410157Sgdamore@opensolaris.org 		if (c == proc->p_reader) {
4959484Sgarrett.damore@Sun.COM 			proc->p_oflag &= ~FREAD;
4969484Sgarrett.damore@Sun.COM 			proc->p_reader = NULL;
4979484Sgarrett.damore@Sun.COM 		}
49810157Sgdamore@opensolaris.org 		cv_broadcast(&dev->d_cv);
4999484Sgarrett.damore@Sun.COM 
5009484Sgarrett.damore@Sun.COM 		if (proc->p_refcnt == 0) {
50110157Sgdamore@opensolaris.org 			list_remove(&dev->d_procs, proc);
50210157Sgdamore@opensolaris.org 			devaudio_proc_free(proc);
5039484Sgarrett.damore@Sun.COM 		}
50410157Sgdamore@opensolaris.org 		dc->dc_proc = NULL;
5059484Sgarrett.damore@Sun.COM 	}
5069484Sgarrett.damore@Sun.COM 
50710157Sgdamore@opensolaris.org 	mutex_exit(&dev->d_mx);
5089484Sgarrett.damore@Sun.COM 
50910157Sgdamore@opensolaris.org 	devaudio_clear_eof(c);
5109484Sgarrett.damore@Sun.COM 
51110157Sgdamore@opensolaris.org 	while ((mp = dc->dc_draining) != NULL) {
51210157Sgdamore@opensolaris.org 		dc->dc_draining = mp->b_next;
5139484Sgarrett.damore@Sun.COM 		mp->b_next = NULL;
5149484Sgarrett.damore@Sun.COM 		freemsg(mp);
5159484Sgarrett.damore@Sun.COM 	}
5169484Sgarrett.damore@Sun.COM 
51710157Sgdamore@opensolaris.org 	mutex_destroy(&dc->dc_lock);
51810157Sgdamore@opensolaris.org 	list_destroy(&dc->dc_eofcnt);
51910157Sgdamore@opensolaris.org 	kmem_free(dc, sizeof (*dc));
5209484Sgarrett.damore@Sun.COM }
5219484Sgarrett.damore@Sun.COM 
5229484Sgarrett.damore@Sun.COM static void
devaudio_input(audio_client_t * c)52310157Sgdamore@opensolaris.org devaudio_input(audio_client_t *c)
5249484Sgarrett.damore@Sun.COM {
5259484Sgarrett.damore@Sun.COM 	audio_stream_t	*sp = auclnt_input_stream(c);
52610157Sgdamore@opensolaris.org 	daclient_t	*dc = auclnt_get_private(c);
5279484Sgarrett.damore@Sun.COM 	unsigned	framesz = auclnt_get_framesz(sp);
52810157Sgdamore@opensolaris.org 	queue_t		*rq = auclnt_get_rq(c);
5299484Sgarrett.damore@Sun.COM 	mblk_t		*mp;
53010157Sgdamore@opensolaris.org 	unsigned	nbytes = dc->dc_proc->p_info.record.buffer_size;
5319484Sgarrett.damore@Sun.COM 	unsigned	count = nbytes / framesz;
5329484Sgarrett.damore@Sun.COM 
5339484Sgarrett.damore@Sun.COM 	/*
5349484Sgarrett.damore@Sun.COM 	 * Potentially send a message upstream with the record data.
5359484Sgarrett.damore@Sun.COM 	 * We collect this up in chunks of the buffer size requested
5369484Sgarrett.damore@Sun.COM 	 * by the client.
5379484Sgarrett.damore@Sun.COM 	 */
5389484Sgarrett.damore@Sun.COM 
5399484Sgarrett.damore@Sun.COM 	while (auclnt_get_count(sp) >= count) {
5409484Sgarrett.damore@Sun.COM 
54110632Sgdamore@opensolaris.org 		if ((!canput(rq)) ||
5429484Sgarrett.damore@Sun.COM 		    ((mp = allocb(nbytes, BPRI_MED)) == NULL)) {
5439484Sgarrett.damore@Sun.COM 			/*
5449484Sgarrett.damore@Sun.COM 			 * This will apply back pressure to the
5459484Sgarrett.damore@Sun.COM 			 * buffer.  We haven't yet lost any data, we
5469484Sgarrett.damore@Sun.COM 			 * just can't send it up.  The point at which
5479484Sgarrett.damore@Sun.COM 			 * we have an unrecoverable overrun is in the
5489484Sgarrett.damore@Sun.COM 			 * buffer, not in the streams queue.  So, no
5499484Sgarrett.damore@Sun.COM 			 * need to do anything right now.
5509484Sgarrett.damore@Sun.COM 			 *
5519484Sgarrett.damore@Sun.COM 			 * Note that since recording is enabled, we
5529484Sgarrett.damore@Sun.COM 			 * expect that the callback routine will be
5539484Sgarrett.damore@Sun.COM 			 * called repeatedly & regularly, so we don't
5549484Sgarrett.damore@Sun.COM 			 * have to worry about leaving data orphaned
5559484Sgarrett.damore@Sun.COM 			 * in the queue.
5569484Sgarrett.damore@Sun.COM 			 */
5579484Sgarrett.damore@Sun.COM 			break;
5589484Sgarrett.damore@Sun.COM 		}
5599484Sgarrett.damore@Sun.COM 
5609484Sgarrett.damore@Sun.COM 		(void) auclnt_consume_data(sp, (caddr_t)mp->b_wptr, count);
5619484Sgarrett.damore@Sun.COM 		mp->b_wptr += nbytes;
56210632Sgdamore@opensolaris.org 		(void) putq(rq, mp);
5639484Sgarrett.damore@Sun.COM 	}
5649484Sgarrett.damore@Sun.COM }
5659484Sgarrett.damore@Sun.COM 
5669484Sgarrett.damore@Sun.COM static void
devaudio_proc_update(daproc_t * proc)56710157Sgdamore@opensolaris.org devaudio_proc_update(daproc_t *proc)
5689484Sgarrett.damore@Sun.COM {
5699484Sgarrett.damore@Sun.COM 	audio_info_t	*info;
5709484Sgarrett.damore@Sun.COM 	audio_stream_t	*sp;
57110157Sgdamore@opensolaris.org 	audio_client_t	*c;
5729484Sgarrett.damore@Sun.COM 
5739484Sgarrett.damore@Sun.COM 	info = &proc->p_info;
5749484Sgarrett.damore@Sun.COM 
57510157Sgdamore@opensolaris.org 	ASSERT(mutex_owned(&proc->p_dev->d_mx));
5769484Sgarrett.damore@Sun.COM 
57710157Sgdamore@opensolaris.org 	if ((c = proc->p_writer) != NULL) {
57810157Sgdamore@opensolaris.org 		sp = auclnt_output_stream(c);
5799484Sgarrett.damore@Sun.COM 
5809484Sgarrett.damore@Sun.COM 		info->play.sample_rate = auclnt_get_rate(sp);
5819484Sgarrett.damore@Sun.COM 		info->play.channels = auclnt_get_channels(sp);
58210157Sgdamore@opensolaris.org 		devaudio_decompose_format(&info->play, auclnt_get_format(sp));
5839484Sgarrett.damore@Sun.COM 
5849484Sgarrett.damore@Sun.COM 		info->play.gain =
5859484Sgarrett.damore@Sun.COM 		    (auclnt_get_gain(sp) * AUDIO_MAX_GAIN) / 100;
5869484Sgarrett.damore@Sun.COM 		info->play.pause = auclnt_is_paused(sp);
58710172Sgdamore@opensolaris.org 		info->play.active = auclnt_is_running(sp);
5889484Sgarrett.damore@Sun.COM 		info->play.samples = auclnt_get_samples(sp);
5899484Sgarrett.damore@Sun.COM 		info->play.error = auclnt_get_errors(sp) ? B_TRUE : B_FALSE;
5909484Sgarrett.damore@Sun.COM 		info->output_muted = auclnt_get_muted(sp);
59110172Sgdamore@opensolaris.org 	} else {
59210172Sgdamore@opensolaris.org 		info->play.encoding = AUDIO_ENCODING_NONE;
59310172Sgdamore@opensolaris.org 		info->play.precision = 0;
59410172Sgdamore@opensolaris.org 		info->play.sample_rate = 0;
59510172Sgdamore@opensolaris.org 		info->play.pause = B_FALSE;
59610172Sgdamore@opensolaris.org 		info->play.active = B_FALSE;
59710172Sgdamore@opensolaris.org 		info->play.error = B_FALSE;
59810172Sgdamore@opensolaris.org 		info->play.samples = 0;
5999484Sgarrett.damore@Sun.COM 	}
6009484Sgarrett.damore@Sun.COM 
60110157Sgdamore@opensolaris.org 	if ((c = proc->p_reader) != NULL) {
60210157Sgdamore@opensolaris.org 		sp = auclnt_input_stream(c);
6039484Sgarrett.damore@Sun.COM 
6049484Sgarrett.damore@Sun.COM 		info->record.sample_rate = auclnt_get_rate(sp);
6059484Sgarrett.damore@Sun.COM 		info->record.channels = auclnt_get_channels(sp);
60610157Sgdamore@opensolaris.org 		devaudio_decompose_format(&info->record, auclnt_get_format(sp));
6079484Sgarrett.damore@Sun.COM 
6089484Sgarrett.damore@Sun.COM 		info->record.gain =
6099484Sgarrett.damore@Sun.COM 		    (auclnt_get_gain(sp) * AUDIO_MAX_GAIN) / 100;
6109484Sgarrett.damore@Sun.COM 		info->record.pause = auclnt_is_paused(sp);
61110172Sgdamore@opensolaris.org 		info->record.active = auclnt_is_running(sp);
6129484Sgarrett.damore@Sun.COM 		info->record.samples = auclnt_get_samples(sp);
6139484Sgarrett.damore@Sun.COM 		info->record.error = auclnt_get_errors(sp) ? B_TRUE : B_FALSE;
61410172Sgdamore@opensolaris.org 	} else {
61510172Sgdamore@opensolaris.org 		info->record.encoding = AUDIO_ENCODING_NONE;
61610172Sgdamore@opensolaris.org 		info->record.precision = 0;
61710172Sgdamore@opensolaris.org 		info->record.sample_rate = 0;
61810172Sgdamore@opensolaris.org 		info->record.pause = B_FALSE;
61910172Sgdamore@opensolaris.org 		info->record.active = B_FALSE;
62010172Sgdamore@opensolaris.org 		info->record.error = B_FALSE;
62110172Sgdamore@opensolaris.org 		info->record.samples = 0;
6229484Sgarrett.damore@Sun.COM 	}
6239484Sgarrett.damore@Sun.COM }
6249484Sgarrett.damore@Sun.COM 
6259484Sgarrett.damore@Sun.COM static void
devaudio_ioc_getinfo(queue_t * wq,audio_client_t * c,mblk_t * mp)62610157Sgdamore@opensolaris.org devaudio_ioc_getinfo(queue_t *wq, audio_client_t *c, mblk_t *mp)
6279484Sgarrett.damore@Sun.COM {
62810157Sgdamore@opensolaris.org 	daclient_t	*dc = auclnt_get_private(c);
62910157Sgdamore@opensolaris.org 	daproc_t	*proc = dc->dc_proc;
63010157Sgdamore@opensolaris.org 	mblk_t		*bcont;
6319484Sgarrett.damore@Sun.COM 
63210157Sgdamore@opensolaris.org 	if ((bcont = allocb(sizeof (audio_info_t), BPRI_MED)) == NULL) {
63310157Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, ENOMEM);
63410157Sgdamore@opensolaris.org 		return;
6359484Sgarrett.damore@Sun.COM 	}
6369484Sgarrett.damore@Sun.COM 
63710157Sgdamore@opensolaris.org 	mutex_enter(&dc->dc_dev->d_mx);
63810157Sgdamore@opensolaris.org 	devaudio_proc_update(proc);
63910157Sgdamore@opensolaris.org 	bcopy(&proc->p_info, bcont->b_wptr, sizeof (audio_info_t));
64010157Sgdamore@opensolaris.org 	mutex_exit(&dc->dc_dev->d_mx);
64110157Sgdamore@opensolaris.org 
64210157Sgdamore@opensolaris.org 	bcont->b_wptr += sizeof (audio_info_t);
64310157Sgdamore@opensolaris.org 
64410157Sgdamore@opensolaris.org 	mcopyout(mp, NULL, sizeof (audio_info_t), NULL, bcont);
64510157Sgdamore@opensolaris.org 	qreply(wq, mp);
6469484Sgarrett.damore@Sun.COM }
6479484Sgarrett.damore@Sun.COM 
6489484Sgarrett.damore@Sun.COM #define	CHANGED(new, old, field)			\
6499484Sgarrett.damore@Sun.COM 	((new->field != ((uint32_t)~0)) && (new->field != old->field))
6509484Sgarrett.damore@Sun.COM #define	CHANGED8(new, old, field)			\
6519484Sgarrett.damore@Sun.COM 	((new->field != ((uint8_t)~0)) && (new->field != old->field))
6529484Sgarrett.damore@Sun.COM 
65310157Sgdamore@opensolaris.org static void
devaudio_ioc_setinfo(queue_t * wq,audio_client_t * c,mblk_t * mp)65410157Sgdamore@opensolaris.org devaudio_ioc_setinfo(queue_t *wq, audio_client_t *c, mblk_t *mp)
6559484Sgarrett.damore@Sun.COM {
65610157Sgdamore@opensolaris.org 	daclient_t	*dc;
65710157Sgdamore@opensolaris.org 	daproc_t	*proc;
65810157Sgdamore@opensolaris.org 	audio_info_t	*oinfo;
65910157Sgdamore@opensolaris.org 	audio_info_t	*ninfo;
6609484Sgarrett.damore@Sun.COM 	audio_prinfo_t	*npr;
6619484Sgarrett.damore@Sun.COM 	audio_prinfo_t	*opr;
6629484Sgarrett.damore@Sun.COM 
6639484Sgarrett.damore@Sun.COM 	int		pfmt = AUDIO_FORMAT_NONE;
6649484Sgarrett.damore@Sun.COM 	int		rfmt = AUDIO_FORMAT_NONE;
6659484Sgarrett.damore@Sun.COM 
6669484Sgarrett.damore@Sun.COM 	boolean_t	reader;
6679484Sgarrett.damore@Sun.COM 	boolean_t	writer;
6689484Sgarrett.damore@Sun.COM 	boolean_t	isctl;
6699484Sgarrett.damore@Sun.COM 	audio_stream_t	*sp;
6709484Sgarrett.damore@Sun.COM 	int		rv;
67110157Sgdamore@opensolaris.org 	caddr_t		uaddr;
67210157Sgdamore@opensolaris.org 	mblk_t		*bcont;
6739484Sgarrett.damore@Sun.COM 
67410157Sgdamore@opensolaris.org 	struct copyresp	*csp;
67510157Sgdamore@opensolaris.org 
67610157Sgdamore@opensolaris.org 	if (DB_TYPE(mp) == M_IOCTL) {
67710157Sgdamore@opensolaris.org 		/* the special value "1" indicates that this is a copyin */
67810157Sgdamore@opensolaris.org 		uaddr = *(caddr_t *)(void *)mp->b_cont->b_rptr;
67910157Sgdamore@opensolaris.org 
68010157Sgdamore@opensolaris.org 		mcopyin(mp, uaddr, sizeof (audio_info_t), NULL);
68110157Sgdamore@opensolaris.org 		qreply(wq, mp);
68210157Sgdamore@opensolaris.org 		return;
68310157Sgdamore@opensolaris.org 	}
68410157Sgdamore@opensolaris.org 
68510157Sgdamore@opensolaris.org 	ASSERT(DB_TYPE(mp) == M_IOCDATA);
68610157Sgdamore@opensolaris.org 	if (((bcont = mp->b_cont) == NULL) ||
68710157Sgdamore@opensolaris.org 	    (MBLKL(mp->b_cont) != sizeof (audio_info_t))) {
68810157Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, EINVAL);
68910157Sgdamore@opensolaris.org 		return;
69010157Sgdamore@opensolaris.org 	}
69110157Sgdamore@opensolaris.org 
69210157Sgdamore@opensolaris.org 	mp->b_cont = NULL;
69310157Sgdamore@opensolaris.org 	csp = (void *)mp->b_rptr;
69410157Sgdamore@opensolaris.org 	uaddr = (void *)csp->cp_private;
69510157Sgdamore@opensolaris.org 	dc = auclnt_get_private(c);
69610157Sgdamore@opensolaris.org 	ninfo = (void *)bcont->b_rptr;
69710157Sgdamore@opensolaris.org 
69810157Sgdamore@opensolaris.org 	mutex_enter(&dc->dc_dev->d_mx);
69910157Sgdamore@opensolaris.org 
70010157Sgdamore@opensolaris.org 	proc = dc->dc_proc;
70110157Sgdamore@opensolaris.org 	oinfo = &proc->p_info;
70210157Sgdamore@opensolaris.org 
70310157Sgdamore@opensolaris.org 	if (auclnt_get_minor_type(c) == AUDIO_MINOR_DEVAUDIOCTL) {
7049484Sgarrett.damore@Sun.COM 		/* control node can do both read and write fields */
7059484Sgarrett.damore@Sun.COM 		isctl = B_TRUE;
7069484Sgarrett.damore@Sun.COM 		reader = B_TRUE;
7079484Sgarrett.damore@Sun.COM 		writer = B_TRUE;
7089484Sgarrett.damore@Sun.COM 	} else {
7099484Sgarrett.damore@Sun.COM 		isctl = B_FALSE;
71010157Sgdamore@opensolaris.org 		writer = (c == proc->p_writer);
71110157Sgdamore@opensolaris.org 		reader = (c == proc->p_reader);
7129484Sgarrett.damore@Sun.COM 	}
7139484Sgarrett.damore@Sun.COM 
7149484Sgarrett.damore@Sun.COM 	/*
7159484Sgarrett.damore@Sun.COM 	 * Start by validating settings.
7169484Sgarrett.damore@Sun.COM 	 */
7179484Sgarrett.damore@Sun.COM 	npr = &ninfo->play;
7189484Sgarrett.damore@Sun.COM 	opr = &oinfo->play;
7199484Sgarrett.damore@Sun.COM 
7209484Sgarrett.damore@Sun.COM 	if (writer && CHANGED(npr, opr, sample_rate)) {
7219484Sgarrett.damore@Sun.COM 		if ((isctl) ||
7229484Sgarrett.damore@Sun.COM 		    (npr->sample_rate < 5500) || (npr->sample_rate > 48000)) {
72310157Sgdamore@opensolaris.org 			rv = EINVAL;
72410157Sgdamore@opensolaris.org 			goto err;
7259484Sgarrett.damore@Sun.COM 		}
7269484Sgarrett.damore@Sun.COM 	}
7279484Sgarrett.damore@Sun.COM 	if (writer && CHANGED(npr, opr, channels)) {
7289484Sgarrett.damore@Sun.COM 		if ((isctl) || (npr->channels < 1) || (npr->channels > 2)) {
72910157Sgdamore@opensolaris.org 			rv = EINVAL;
73010157Sgdamore@opensolaris.org 			goto err;
7319484Sgarrett.damore@Sun.COM 		}
7329484Sgarrett.damore@Sun.COM 	}
7339484Sgarrett.damore@Sun.COM 	if (writer &&
7349484Sgarrett.damore@Sun.COM 	    (CHANGED(npr, opr, encoding) || CHANGED(npr, opr, precision))) {
7359484Sgarrett.damore@Sun.COM 		if (npr->encoding == (uint32_t)~0)
7369484Sgarrett.damore@Sun.COM 			npr->encoding = opr->encoding;
7379484Sgarrett.damore@Sun.COM 		if (npr->precision == (uint32_t)~0)
7389484Sgarrett.damore@Sun.COM 			npr->precision = opr->precision;
73910157Sgdamore@opensolaris.org 		pfmt = devaudio_compose_format(npr);
7409484Sgarrett.damore@Sun.COM 		if ((isctl) || (pfmt == AUDIO_FORMAT_NONE)) {
74110157Sgdamore@opensolaris.org 			rv = EINVAL;
74210157Sgdamore@opensolaris.org 			goto err;
7439484Sgarrett.damore@Sun.COM 		}
7449484Sgarrett.damore@Sun.COM 	}
7459484Sgarrett.damore@Sun.COM 
7469484Sgarrett.damore@Sun.COM 	/* play fields that anyone can modify */
7479484Sgarrett.damore@Sun.COM 	if (CHANGED(npr, opr, gain)) {
7489484Sgarrett.damore@Sun.COM 		if (npr->gain > AUDIO_MAX_GAIN) {
74910157Sgdamore@opensolaris.org 			rv = EINVAL;
75010157Sgdamore@opensolaris.org 			goto err;
7519484Sgarrett.damore@Sun.COM 		}
7529484Sgarrett.damore@Sun.COM 	}
7539484Sgarrett.damore@Sun.COM 
7549484Sgarrett.damore@Sun.COM 
7559484Sgarrett.damore@Sun.COM 	npr = &ninfo->record;
7569484Sgarrett.damore@Sun.COM 	opr = &oinfo->record;
7579484Sgarrett.damore@Sun.COM 
7589484Sgarrett.damore@Sun.COM 	if (reader && CHANGED(npr, opr, sample_rate)) {
7599484Sgarrett.damore@Sun.COM 		if ((isctl) ||
76010157Sgdamore@opensolaris.org 		    (npr->sample_rate < 5500) || (npr->sample_rate > 48000)) {
76110157Sgdamore@opensolaris.org 			rv = EINVAL;
76210157Sgdamore@opensolaris.org 			goto err;
7639484Sgarrett.damore@Sun.COM 		}
7649484Sgarrett.damore@Sun.COM 	}
7659484Sgarrett.damore@Sun.COM 	if (reader && CHANGED(npr, opr, channels)) {
7669484Sgarrett.damore@Sun.COM 		if ((isctl) || (npr->channels < 1) || (npr->channels > 2)) {
76710157Sgdamore@opensolaris.org 			rv = EINVAL;
76810157Sgdamore@opensolaris.org 			goto err;
7699484Sgarrett.damore@Sun.COM 		}
7709484Sgarrett.damore@Sun.COM 	}
7719484Sgarrett.damore@Sun.COM 	if (reader &&
7729484Sgarrett.damore@Sun.COM 	    (CHANGED(npr, opr, encoding) || CHANGED(npr, opr, precision))) {
7739484Sgarrett.damore@Sun.COM 		if (npr->encoding == (uint32_t)~0)
7749484Sgarrett.damore@Sun.COM 			npr->encoding = opr->encoding;
7759484Sgarrett.damore@Sun.COM 		if (npr->precision == (uint32_t)~0)
7769484Sgarrett.damore@Sun.COM 			npr->precision = opr->precision;
77710157Sgdamore@opensolaris.org 		rfmt = devaudio_compose_format(npr);
7789484Sgarrett.damore@Sun.COM 		if ((isctl) || (rfmt == AUDIO_FORMAT_NONE)) {
77910157Sgdamore@opensolaris.org 			rv = EINVAL;
78010157Sgdamore@opensolaris.org 			goto err;
7819484Sgarrett.damore@Sun.COM 		}
7829484Sgarrett.damore@Sun.COM 	}
7839484Sgarrett.damore@Sun.COM 	if (reader && CHANGED(npr, opr, buffer_size)) {
7849484Sgarrett.damore@Sun.COM 		if (isctl) {
78510157Sgdamore@opensolaris.org 			rv = EINVAL;
78610157Sgdamore@opensolaris.org 			goto err;
7879484Sgarrett.damore@Sun.COM 		}
7889484Sgarrett.damore@Sun.COM 		/* make sure we can support 16-bit stereo samples */
7899484Sgarrett.damore@Sun.COM 		if ((npr->buffer_size % 4) != 0) {
7909484Sgarrett.damore@Sun.COM 			npr->buffer_size = (npr->buffer_size + 3) & ~3;
7919484Sgarrett.damore@Sun.COM 		}
7929484Sgarrett.damore@Sun.COM 		/* limit the maximum buffer size somewhat */
7939484Sgarrett.damore@Sun.COM 		if (npr->buffer_size > 16384) {
7949484Sgarrett.damore@Sun.COM 			npr->buffer_size = 16384;
7959484Sgarrett.damore@Sun.COM 		}
7969484Sgarrett.damore@Sun.COM 	}
7979484Sgarrett.damore@Sun.COM 
7989484Sgarrett.damore@Sun.COM 	/* record fields that anyone can modify */
7999484Sgarrett.damore@Sun.COM 	if (CHANGED(npr, opr, gain)) {
8009484Sgarrett.damore@Sun.COM 		if (npr->gain > AUDIO_MAX_GAIN) {
80110157Sgdamore@opensolaris.org 			rv = EINVAL;
80210157Sgdamore@opensolaris.org 			goto err;
8039484Sgarrett.damore@Sun.COM 		}
8049484Sgarrett.damore@Sun.COM 	}
8059484Sgarrett.damore@Sun.COM 
8069484Sgarrett.damore@Sun.COM 	/*
8079484Sgarrett.damore@Sun.COM 	 * Now apply the changes.
8089484Sgarrett.damore@Sun.COM 	 */
8099484Sgarrett.damore@Sun.COM 	if (proc->p_writer != NULL) {
81010157Sgdamore@opensolaris.org 		sp = auclnt_output_stream(proc->p_writer);
8119484Sgarrett.damore@Sun.COM 		npr = &ninfo->play;
8129484Sgarrett.damore@Sun.COM 		opr = &oinfo->play;
8139484Sgarrett.damore@Sun.COM 
8149484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, sample_rate)) {
81510157Sgdamore@opensolaris.org 			if ((rv = auclnt_set_rate(sp, npr->sample_rate)) != 0)
81610157Sgdamore@opensolaris.org 				goto err;
8179484Sgarrett.damore@Sun.COM 		}
8189484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, channels)) {
81910157Sgdamore@opensolaris.org 			if ((rv = auclnt_set_channels(sp, npr->channels)) != 0)
82010157Sgdamore@opensolaris.org 				goto err;
8219484Sgarrett.damore@Sun.COM 		}
8229484Sgarrett.damore@Sun.COM 		if (pfmt != AUDIO_FORMAT_NONE) {
82310157Sgdamore@opensolaris.org 			if ((rv = auclnt_set_format(sp, pfmt)) != 0)
82410157Sgdamore@opensolaris.org 				goto err;
8259484Sgarrett.damore@Sun.COM 		}
8269484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, samples)) {
8279484Sgarrett.damore@Sun.COM 			auclnt_set_samples(sp, npr->samples);
8289484Sgarrett.damore@Sun.COM 		}
8299484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, eof)) {
8309484Sgarrett.damore@Sun.COM 			/*
8319484Sgarrett.damore@Sun.COM 			 * This ugly special case code is required to
8329484Sgarrett.damore@Sun.COM 			 * prevent problems with realaudio.
8339484Sgarrett.damore@Sun.COM 			 */
8349484Sgarrett.damore@Sun.COM 			if (npr->eof == 0) {
83510157Sgdamore@opensolaris.org 				devaudio_clear_eof(proc->p_writer);
8369484Sgarrett.damore@Sun.COM 			}
8379484Sgarrett.damore@Sun.COM 			opr->eof = npr->eof;
8389484Sgarrett.damore@Sun.COM 		}
8399484Sgarrett.damore@Sun.COM 		if (CHANGED8(npr, opr, pause)) {
8409484Sgarrett.damore@Sun.COM 			if (npr->pause) {
8419484Sgarrett.damore@Sun.COM 				auclnt_set_paused(sp);
8429484Sgarrett.damore@Sun.COM 			} else {
8439484Sgarrett.damore@Sun.COM 				auclnt_clear_paused(sp);
84410157Sgdamore@opensolaris.org 
8459484Sgarrett.damore@Sun.COM 				/* qenable to start up the playback */
84610157Sgdamore@opensolaris.org 				qenable(auclnt_get_wq(proc->p_writer));
8479484Sgarrett.damore@Sun.COM 			}
8489484Sgarrett.damore@Sun.COM 		}
8499484Sgarrett.damore@Sun.COM 		if (CHANGED8(npr, opr, waiting) && (npr->waiting)) {
8509484Sgarrett.damore@Sun.COM 			opr->waiting = npr->waiting;
8519484Sgarrett.damore@Sun.COM 		}
8529484Sgarrett.damore@Sun.COM 		if (CHANGED8(npr, opr, error)) {
8539484Sgarrett.damore@Sun.COM 			auclnt_set_errors(sp, npr->error);
8549484Sgarrett.damore@Sun.COM 		}
8559484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, gain)) {
8569484Sgarrett.damore@Sun.COM 			auclnt_set_gain(sp, (npr->gain * 100) / AUDIO_MAX_GAIN);
8579484Sgarrett.damore@Sun.COM 		}
8589484Sgarrett.damore@Sun.COM 		if (CHANGED8(ninfo, oinfo, output_muted)) {
8599484Sgarrett.damore@Sun.COM 			auclnt_set_muted(sp, ninfo->output_muted);
8609484Sgarrett.damore@Sun.COM 		}
8619484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, buffer_size)) {
8629484Sgarrett.damore@Sun.COM 			/*
8639484Sgarrett.damore@Sun.COM 			 * No checks on the buffer size are performed
8649484Sgarrett.damore@Sun.COM 			 * for play side.  The value of the buffer size
8659484Sgarrett.damore@Sun.COM 			 * is meaningless for play side anyway.
8669484Sgarrett.damore@Sun.COM 			 */
8679484Sgarrett.damore@Sun.COM 			opr->buffer_size = npr->buffer_size;
8689484Sgarrett.damore@Sun.COM 		}
8699484Sgarrett.damore@Sun.COM 	} else {
87010172Sgdamore@opensolaris.org 		/* these values are preserved even if /dev/audio not open */
8719484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, gain)) {
8729484Sgarrett.damore@Sun.COM 			opr->gain = npr->gain;
8739484Sgarrett.damore@Sun.COM 		}
8749484Sgarrett.damore@Sun.COM 		if (CHANGED8(ninfo, oinfo, output_muted)) {
8759484Sgarrett.damore@Sun.COM 			oinfo->output_muted = ninfo->output_muted;
8769484Sgarrett.damore@Sun.COM 		}
8779484Sgarrett.damore@Sun.COM 	}
8789484Sgarrett.damore@Sun.COM 
8799484Sgarrett.damore@Sun.COM 	if (proc->p_reader != NULL) {
88010157Sgdamore@opensolaris.org 		sp = auclnt_input_stream(proc->p_reader);
8819484Sgarrett.damore@Sun.COM 		npr = &ninfo->record;
8829484Sgarrett.damore@Sun.COM 		opr = &oinfo->record;
8839484Sgarrett.damore@Sun.COM 
8849484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, sample_rate)) {
88510157Sgdamore@opensolaris.org 			if ((rv = auclnt_set_rate(sp, npr->sample_rate)) != 0)
88610157Sgdamore@opensolaris.org 				goto err;
8879484Sgarrett.damore@Sun.COM 		}
8889484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, channels)) {
88910157Sgdamore@opensolaris.org 			if ((rv = auclnt_set_channels(sp, npr->channels)) != 0)
89010157Sgdamore@opensolaris.org 				goto err;
8919484Sgarrett.damore@Sun.COM 		}
8929484Sgarrett.damore@Sun.COM 		if (rfmt != AUDIO_FORMAT_NONE) {
89310157Sgdamore@opensolaris.org 			if ((rv = auclnt_set_format(sp, rfmt)) != 0)
89410157Sgdamore@opensolaris.org 				goto err;
8959484Sgarrett.damore@Sun.COM 		}
8969484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, samples)) {
8979484Sgarrett.damore@Sun.COM 			auclnt_set_samples(sp, npr->samples);
8989484Sgarrett.damore@Sun.COM 		}
8999484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, eof)) {
9009484Sgarrett.damore@Sun.COM 			opr->eof = npr->eof;
9019484Sgarrett.damore@Sun.COM 		}
9029484Sgarrett.damore@Sun.COM 		if (CHANGED8(npr, opr, pause)) {
9039484Sgarrett.damore@Sun.COM 			if (npr->pause) {
9049484Sgarrett.damore@Sun.COM 				auclnt_set_paused(sp);
9059484Sgarrett.damore@Sun.COM 			} else {
9069484Sgarrett.damore@Sun.COM 				auclnt_clear_paused(sp);
9079484Sgarrett.damore@Sun.COM 				auclnt_start(sp);
9089484Sgarrett.damore@Sun.COM 			}
9099484Sgarrett.damore@Sun.COM 		}
9109484Sgarrett.damore@Sun.COM 		if (CHANGED8(npr, opr, waiting) && (npr->waiting)) {
9119484Sgarrett.damore@Sun.COM 			opr->waiting = npr->waiting;
9129484Sgarrett.damore@Sun.COM 		}
9139484Sgarrett.damore@Sun.COM 		if (CHANGED8(npr, opr, error)) {
9149484Sgarrett.damore@Sun.COM 			auclnt_set_errors(sp, npr->error);
9159484Sgarrett.damore@Sun.COM 		}
9169484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, buffer_size)) {
9179484Sgarrett.damore@Sun.COM 			opr->buffer_size = npr->buffer_size;
9189484Sgarrett.damore@Sun.COM 		}
9199484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, gain)) {
9209484Sgarrett.damore@Sun.COM 			auclnt_set_gain(sp, (npr->gain * 100) / AUDIO_MAX_GAIN);
9219484Sgarrett.damore@Sun.COM 		}
9229484Sgarrett.damore@Sun.COM 	} else {
9239484Sgarrett.damore@Sun.COM 		/* these values are preserved even if /dev/audio not open */
9249484Sgarrett.damore@Sun.COM 		if (CHANGED(npr, opr, gain)) {
9259484Sgarrett.damore@Sun.COM 			opr->gain = npr->gain;
9269484Sgarrett.damore@Sun.COM 		}
9279484Sgarrett.damore@Sun.COM 	}
9289484Sgarrett.damore@Sun.COM 
92910157Sgdamore@opensolaris.org 	devaudio_proc_update(dc->dc_proc);
93010157Sgdamore@opensolaris.org 	bcopy(&dc->dc_proc->p_info, ninfo, sizeof (*ninfo));
93110172Sgdamore@opensolaris.org 
93210157Sgdamore@opensolaris.org 	mutex_exit(&dc->dc_dev->d_mx);
93310157Sgdamore@opensolaris.org 	mcopyout(mp, NULL, sizeof (audio_info_t), uaddr, bcont);
93410157Sgdamore@opensolaris.org 	qreply(wq, mp);
93510157Sgdamore@opensolaris.org 	return;
93610157Sgdamore@opensolaris.org 
93710157Sgdamore@opensolaris.org err:
93810157Sgdamore@opensolaris.org 	mutex_exit(&dc->dc_dev->d_mx);
93910157Sgdamore@opensolaris.org 	miocnak(wq, mp, 0, rv);
94010157Sgdamore@opensolaris.org }
94110157Sgdamore@opensolaris.org 
94210157Sgdamore@opensolaris.org static void
devaudio_ioc_getdev(queue_t * wq,audio_client_t * c,mblk_t * mp)94310157Sgdamore@opensolaris.org devaudio_ioc_getdev(queue_t *wq, audio_client_t *c, mblk_t *mp)
94410157Sgdamore@opensolaris.org {
94510157Sgdamore@opensolaris.org 	audio_dev_t	*d = auclnt_get_dev(c);
94610157Sgdamore@opensolaris.org 	mblk_t		*bcont;
94710157Sgdamore@opensolaris.org 	audio_device_t	*a;
94810157Sgdamore@opensolaris.org 
94910157Sgdamore@opensolaris.org 	if ((bcont = allocb(sizeof (*a), BPRI_MED)) == NULL) {
95010157Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, ENOMEM);
95110157Sgdamore@opensolaris.org 		return;
95210157Sgdamore@opensolaris.org 	}
95310157Sgdamore@opensolaris.org 
95410157Sgdamore@opensolaris.org 	a = (void *)bcont->b_wptr;
95510157Sgdamore@opensolaris.org 	(void) snprintf(a->name, sizeof (a->name),
95610157Sgdamore@opensolaris.org 	    "SUNW,%s", auclnt_get_dev_name(d));
95710157Sgdamore@opensolaris.org 	(void) strlcpy(a->config,
95810157Sgdamore@opensolaris.org 	    auclnt_get_dev_description(d), sizeof (a->config));
95910157Sgdamore@opensolaris.org 	(void) strlcpy(a->version,
96010157Sgdamore@opensolaris.org 	    auclnt_get_dev_version(d),  sizeof (a->version));
96110157Sgdamore@opensolaris.org 	bcont->b_wptr += sizeof (*a);
96210157Sgdamore@opensolaris.org 
96310157Sgdamore@opensolaris.org 	mcopyout(mp, NULL, sizeof (*a), NULL, bcont);
96410157Sgdamore@opensolaris.org 	qreply(wq, mp);
96510157Sgdamore@opensolaris.org }
96610157Sgdamore@opensolaris.org 
96710157Sgdamore@opensolaris.org static int
devaudio_sigpoll(audio_client_t * c,void * arg)96810157Sgdamore@opensolaris.org devaudio_sigpoll(audio_client_t *c, void *arg)
96910157Sgdamore@opensolaris.org {
97010632Sgdamore@opensolaris.org 	pid_t		pid = (pid_t)(uintptr_t)arg;
97110157Sgdamore@opensolaris.org 
97210157Sgdamore@opensolaris.org 	if (auclnt_get_minor_type(c) == AUDIO_MINOR_DEVAUDIOCTL) {
97310157Sgdamore@opensolaris.org 		/* we only need to notify peers in our own process */
97410632Sgdamore@opensolaris.org 		if (auclnt_get_pid(c) == pid) {
97510632Sgdamore@opensolaris.org 			(void) putctl1(auclnt_get_rq(c), M_PCSIG, SIGPOLL);
97610157Sgdamore@opensolaris.org 		}
97710157Sgdamore@opensolaris.org 	}
97810157Sgdamore@opensolaris.org 	return (AUDIO_WALK_CONTINUE);
97910157Sgdamore@opensolaris.org }
98010157Sgdamore@opensolaris.org 
98110157Sgdamore@opensolaris.org static void
devaudio_drain(audio_client_t * c)98210157Sgdamore@opensolaris.org devaudio_drain(audio_client_t *c)
98310157Sgdamore@opensolaris.org {
98410157Sgdamore@opensolaris.org 	daclient_t	*dc = auclnt_get_private(c);
98510157Sgdamore@opensolaris.org 	mblk_t		*mplist, *mp;
98610157Sgdamore@opensolaris.org 
98710157Sgdamore@opensolaris.org 	mutex_enter(&dc->dc_lock);
98810157Sgdamore@opensolaris.org 	mplist = dc->dc_draining;
98910157Sgdamore@opensolaris.org 	dc->dc_draining = NULL;
99010157Sgdamore@opensolaris.org 	mutex_exit(&dc->dc_lock);
99110157Sgdamore@opensolaris.org 
99210157Sgdamore@opensolaris.org 	while ((mp = mplist) != NULL) {
99310157Sgdamore@opensolaris.org 		mplist = mp->b_next;
99410157Sgdamore@opensolaris.org 		mp->b_next = NULL;
99510632Sgdamore@opensolaris.org 		mioc2ack(mp, NULL, 0, 0);
99610632Sgdamore@opensolaris.org 		(void) putq(auclnt_get_rq(c), mp);
99710157Sgdamore@opensolaris.org 	}
99810157Sgdamore@opensolaris.org }
99910157Sgdamore@opensolaris.org 
100010157Sgdamore@opensolaris.org static void
devaudio_output(audio_client_t * c)100110157Sgdamore@opensolaris.org devaudio_output(audio_client_t *c)
100210157Sgdamore@opensolaris.org {
100310157Sgdamore@opensolaris.org 	daclient_t	*dc = auclnt_get_private(c);
100410157Sgdamore@opensolaris.org 	daproc_t	*proc = dc->dc_proc;
100510157Sgdamore@opensolaris.org 	uint64_t	tail;
100610157Sgdamore@opensolaris.org 	struct eofcnt	*eof;
100710157Sgdamore@opensolaris.org 	int		eofs = 0;
100810157Sgdamore@opensolaris.org 
100910157Sgdamore@opensolaris.org 	tail = auclnt_get_tail(auclnt_output_stream(c));
101010157Sgdamore@opensolaris.org 
101110157Sgdamore@opensolaris.org 	/* get more data! (do this early) */
101210157Sgdamore@opensolaris.org 	qenable(auclnt_get_wq(c));
101310157Sgdamore@opensolaris.org 
101410157Sgdamore@opensolaris.org 	mutex_enter(&dc->dc_lock);
101510157Sgdamore@opensolaris.org 	while (((eof = list_head(&dc->dc_eofcnt)) != NULL) &&
101610644SGarrett.Damore@Sun.COM 	    (eof->tail <= tail)) {
101710157Sgdamore@opensolaris.org 		list_remove(&dc->dc_eofcnt, eof);
101810157Sgdamore@opensolaris.org 		kmem_free(eof, sizeof (*eof));
101910157Sgdamore@opensolaris.org 		eofs++;
102010157Sgdamore@opensolaris.org 	}
102110157Sgdamore@opensolaris.org 	proc->p_info.play.eof += eofs;
102210157Sgdamore@opensolaris.org 	mutex_exit(&dc->dc_lock);
102310157Sgdamore@opensolaris.org 
102410157Sgdamore@opensolaris.org 	if (eofs) {
102510157Sgdamore@opensolaris.org 		auclnt_dev_walk_clients(auclnt_get_dev(c),
102610632Sgdamore@opensolaris.org 		    devaudio_sigpoll, (void *)(uintptr_t)auclnt_get_pid(c));
102710157Sgdamore@opensolaris.org 	}
102810157Sgdamore@opensolaris.org }
102910157Sgdamore@opensolaris.org 
103010157Sgdamore@opensolaris.org static void *
devaudio_init(audio_dev_t * adev)103110157Sgdamore@opensolaris.org devaudio_init(audio_dev_t *adev)
103210157Sgdamore@opensolaris.org {
103310157Sgdamore@opensolaris.org 	dadev_t		*dev;
103410157Sgdamore@opensolaris.org 	unsigned	cap;
103510157Sgdamore@opensolaris.org 
103610157Sgdamore@opensolaris.org 	cap = auclnt_get_dev_capab(adev);
103710157Sgdamore@opensolaris.org 	/* if not a play or record device, don't bother initializing it */
103810157Sgdamore@opensolaris.org 	if ((cap & (AUDIO_CLIENT_CAP_PLAY | AUDIO_CLIENT_CAP_RECORD)) == 0) {
103910157Sgdamore@opensolaris.org 		return (NULL);
104010157Sgdamore@opensolaris.org 	}
104110157Sgdamore@opensolaris.org 
104210157Sgdamore@opensolaris.org 	dev = kmem_zalloc(sizeof (*dev), KM_SLEEP);
104310157Sgdamore@opensolaris.org 	dev->d_dev = adev;
104410157Sgdamore@opensolaris.org 	mutex_init(&dev->d_mx, NULL, MUTEX_DRIVER, NULL);
104510157Sgdamore@opensolaris.org 	cv_init(&dev->d_cv, NULL, CV_DRIVER, NULL);
104610157Sgdamore@opensolaris.org 	list_create(&dev->d_procs, sizeof (struct daproc),
104710157Sgdamore@opensolaris.org 	    offsetof(struct daproc, p_linkage));
104810157Sgdamore@opensolaris.org 
104910157Sgdamore@opensolaris.org 	return (dev);
105010157Sgdamore@opensolaris.org }
105110157Sgdamore@opensolaris.org 
105210157Sgdamore@opensolaris.org static void
devaudio_fini(void * arg)105310157Sgdamore@opensolaris.org devaudio_fini(void *arg)
105410157Sgdamore@opensolaris.org {
105510157Sgdamore@opensolaris.org 	dadev_t	*dev = arg;
105610157Sgdamore@opensolaris.org 
105710157Sgdamore@opensolaris.org 	if (dev != NULL) {
105810157Sgdamore@opensolaris.org 
105910157Sgdamore@opensolaris.org 		mutex_destroy(&dev->d_mx);
106010157Sgdamore@opensolaris.org 		cv_destroy(&dev->d_cv);
106110157Sgdamore@opensolaris.org 		list_destroy(&dev->d_procs);
106210157Sgdamore@opensolaris.org 		kmem_free(dev, sizeof (*dev));
106310157Sgdamore@opensolaris.org 	}
106410157Sgdamore@opensolaris.org }
106510157Sgdamore@opensolaris.org 
106610157Sgdamore@opensolaris.org static int
devaudio_open(audio_client_t * c,int oflag)106710157Sgdamore@opensolaris.org devaudio_open(audio_client_t *c, int oflag)
106810157Sgdamore@opensolaris.org {
106910157Sgdamore@opensolaris.org 	int	rv;
107010157Sgdamore@opensolaris.org 
1071*12165Sgdamore@opensolaris.org 	if ((rv = auclnt_open(c, oflag)) != 0) {
107210157Sgdamore@opensolaris.org 		return (rv);
107310157Sgdamore@opensolaris.org 	}
107410157Sgdamore@opensolaris.org 
107510157Sgdamore@opensolaris.org 	if ((rv = devaudio_proc_hold(c, oflag)) != 0) {
107610157Sgdamore@opensolaris.org 		auclnt_close(c);
107710157Sgdamore@opensolaris.org 		return (rv);
107810157Sgdamore@opensolaris.org 	}
107910157Sgdamore@opensolaris.org 
108010157Sgdamore@opensolaris.org 	/* start up the input */
108110157Sgdamore@opensolaris.org 	if (oflag & FREAD) {
108210157Sgdamore@opensolaris.org 		auclnt_start(auclnt_input_stream(c));
108310157Sgdamore@opensolaris.org 	}
108410157Sgdamore@opensolaris.org 
108510157Sgdamore@opensolaris.org 	return (0);
108610157Sgdamore@opensolaris.org }
108710157Sgdamore@opensolaris.org 
108810157Sgdamore@opensolaris.org static int
devaudioctl_open(audio_client_t * c,int oflag)108910157Sgdamore@opensolaris.org devaudioctl_open(audio_client_t *c, int oflag)
109010157Sgdamore@opensolaris.org {
109110157Sgdamore@opensolaris.org 	int	rv;
109210157Sgdamore@opensolaris.org 
109310157Sgdamore@opensolaris.org 	_NOTE(ARGUNUSED(oflag));
109410157Sgdamore@opensolaris.org 
109510157Sgdamore@opensolaris.org 	oflag &= ~(FWRITE | FREAD);
109610157Sgdamore@opensolaris.org 
1097*12165Sgdamore@opensolaris.org 	if ((rv = auclnt_open(c, 0)) != 0) {
109810157Sgdamore@opensolaris.org 		return (rv);
109910157Sgdamore@opensolaris.org 	}
110010157Sgdamore@opensolaris.org 
110110157Sgdamore@opensolaris.org 	if ((rv = devaudio_proc_hold(c, oflag)) != 0) {
110210157Sgdamore@opensolaris.org 		auclnt_close(c);
110310157Sgdamore@opensolaris.org 		return (rv);
110410157Sgdamore@opensolaris.org 	}
110510157Sgdamore@opensolaris.org 
11069484Sgarrett.damore@Sun.COM 	return (0);
11079484Sgarrett.damore@Sun.COM }
11089484Sgarrett.damore@Sun.COM 
11099484Sgarrett.damore@Sun.COM static void
devaudio_close(audio_client_t * c)111010157Sgdamore@opensolaris.org devaudio_close(audio_client_t *c)
11119484Sgarrett.damore@Sun.COM {
111210157Sgdamore@opensolaris.org 	auclnt_stop(auclnt_output_stream(c));
111310157Sgdamore@opensolaris.org 	auclnt_stop(auclnt_input_stream(c));
11149484Sgarrett.damore@Sun.COM 
111510157Sgdamore@opensolaris.org 	auclnt_close(c);
111610157Sgdamore@opensolaris.org 	devaudio_proc_release(c);
111710157Sgdamore@opensolaris.org }
11189484Sgarrett.damore@Sun.COM 
111910157Sgdamore@opensolaris.org static void
devaudioctl_close(audio_client_t * c)112010157Sgdamore@opensolaris.org devaudioctl_close(audio_client_t *c)
112110157Sgdamore@opensolaris.org {
112210157Sgdamore@opensolaris.org 	auclnt_close(c);
112310157Sgdamore@opensolaris.org 	devaudio_proc_release(c);
11249484Sgarrett.damore@Sun.COM }
11259484Sgarrett.damore@Sun.COM 
11269484Sgarrett.damore@Sun.COM static void
devaudio_miocdata(audio_client_t * c,mblk_t * mp)112710157Sgdamore@opensolaris.org devaudio_miocdata(audio_client_t *c, mblk_t *mp)
11289484Sgarrett.damore@Sun.COM {
112910157Sgdamore@opensolaris.org 	struct copyresp		*csp;
113010157Sgdamore@opensolaris.org 	queue_t			*wq;
113110157Sgdamore@opensolaris.org 
113210157Sgdamore@opensolaris.org 	csp = (void *)mp->b_rptr;
113310157Sgdamore@opensolaris.org 	wq = auclnt_get_wq(c);
11349484Sgarrett.damore@Sun.COM 
113510157Sgdamore@opensolaris.org 	/*
113610157Sgdamore@opensolaris.org 	 * If a transfer error occurred, the framework already
113710157Sgdamore@opensolaris.org 	 * MIOCNAK'd it.
113810157Sgdamore@opensolaris.org 	 */
113910157Sgdamore@opensolaris.org 	if (csp->cp_rval != 0) {
114010157Sgdamore@opensolaris.org 		freemsg(mp);
114110157Sgdamore@opensolaris.org 		return;
114210157Sgdamore@opensolaris.org 	}
11439484Sgarrett.damore@Sun.COM 
114410157Sgdamore@opensolaris.org 	/*
114510157Sgdamore@opensolaris.org 	 * If no state, then this is a response to M_COPYOUT, and we
114610157Sgdamore@opensolaris.org 	 * are done.  (Audio ioctls just copyout a single structure at
114710157Sgdamore@opensolaris.org 	 * completion of work.)
114810157Sgdamore@opensolaris.org 	 */
114910157Sgdamore@opensolaris.org 	if (csp->cp_private == NULL) {
115010157Sgdamore@opensolaris.org 		miocack(wq, mp, 0, 0);
115110157Sgdamore@opensolaris.org 		return;
115210157Sgdamore@opensolaris.org 	}
115310157Sgdamore@opensolaris.org 
115410157Sgdamore@opensolaris.org 	/* now, call the handler ioctl */
115510157Sgdamore@opensolaris.org 	switch (csp->cp_cmd) {
115610157Sgdamore@opensolaris.org 	case AUDIO_SETINFO:
115710157Sgdamore@opensolaris.org 		devaudio_ioc_setinfo(wq, c, mp);
11589484Sgarrett.damore@Sun.COM 		break;
115910157Sgdamore@opensolaris.org 	default:
116010157Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, EINVAL);
11619484Sgarrett.damore@Sun.COM 		break;
11629484Sgarrett.damore@Sun.COM 	}
11639484Sgarrett.damore@Sun.COM }
11649484Sgarrett.damore@Sun.COM 
11659484Sgarrett.damore@Sun.COM static void
devaudio_mioctl(audio_client_t * c,mblk_t * mp)116610157Sgdamore@opensolaris.org devaudio_mioctl(audio_client_t *c, mblk_t *mp)
11679484Sgarrett.damore@Sun.COM {
116810157Sgdamore@opensolaris.org 	struct iocblk	*iocp = (void *)mp->b_rptr;
116910157Sgdamore@opensolaris.org 	queue_t		*wq = auclnt_get_wq(c);
117010157Sgdamore@opensolaris.org 
117110157Sgdamore@opensolaris.org 	/* BSD legacy here: we only support transparent ioctls */
117210157Sgdamore@opensolaris.org 	if (iocp->ioc_count != TRANSPARENT) {
117310157Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, EINVAL);
117410157Sgdamore@opensolaris.org 		return;
117510157Sgdamore@opensolaris.org 	}
117610157Sgdamore@opensolaris.org 
117710157Sgdamore@opensolaris.org 	switch (iocp->ioc_cmd) {
11789484Sgarrett.damore@Sun.COM 	case AUDIO_GETINFO:
117910157Sgdamore@opensolaris.org 		devaudio_ioc_getinfo(wq, c, mp);
11809484Sgarrett.damore@Sun.COM 		break;
11819484Sgarrett.damore@Sun.COM 
11829484Sgarrett.damore@Sun.COM 	case AUDIO_SETINFO:
118310157Sgdamore@opensolaris.org 		devaudio_ioc_setinfo(wq, c, mp);
11849484Sgarrett.damore@Sun.COM 		break;
11859484Sgarrett.damore@Sun.COM 
11869484Sgarrett.damore@Sun.COM 	case AUDIO_GETDEV:
118710157Sgdamore@opensolaris.org 		devaudio_ioc_getdev(wq, c, mp);
11889484Sgarrett.damore@Sun.COM 		break;
11899484Sgarrett.damore@Sun.COM 
11909484Sgarrett.damore@Sun.COM 	case AUDIO_DIAG_LOOPBACK:
11919484Sgarrett.damore@Sun.COM 		/* we don't support this one */
119210157Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, ENOTTY);
11939484Sgarrett.damore@Sun.COM 		break;
11949484Sgarrett.damore@Sun.COM 
11959484Sgarrett.damore@Sun.COM 	case AUDIO_MIXERCTL_GET_MODE:
11969484Sgarrett.damore@Sun.COM 	case AUDIO_MIXERCTL_SET_MODE:
11979484Sgarrett.damore@Sun.COM 	case AUDIO_MIXERCTL_GET_CHINFO:
11989484Sgarrett.damore@Sun.COM 	case AUDIO_MIXERCTL_SET_CHINFO:
11999484Sgarrett.damore@Sun.COM 	case AUDIO_MIXERCTL_GETINFO:
12009484Sgarrett.damore@Sun.COM 	case AUDIO_MIXERCTL_SETINFO:
12019484Sgarrett.damore@Sun.COM 	case AUDIO_GET_NUM_CHS:
12029484Sgarrett.damore@Sun.COM 	case AUDIO_GET_CH_NUMBER:
12039484Sgarrett.damore@Sun.COM 	case AUDIO_GET_CH_TYPE:
12049484Sgarrett.damore@Sun.COM 	case AUDIO_MIXER_SINGLE_OPEN:
12059484Sgarrett.damore@Sun.COM 	case AUDIO_MIXER_MULTIPLE_OPEN:
12069484Sgarrett.damore@Sun.COM 	case AUDIO_MIXER_GET_SAMPLE_RATES:
12079484Sgarrett.damore@Sun.COM 	default:
120810157Sgdamore@opensolaris.org 		miocnak(wq, mp, 0, EINVAL);
12099484Sgarrett.damore@Sun.COM 		break;
12109484Sgarrett.damore@Sun.COM 	}
12119484Sgarrett.damore@Sun.COM }
12129484Sgarrett.damore@Sun.COM 
12139484Sgarrett.damore@Sun.COM static void
devaudioctl_wput(audio_client_t * c,mblk_t * mp)121410157Sgdamore@opensolaris.org devaudioctl_wput(audio_client_t *c, mblk_t *mp)
12159484Sgarrett.damore@Sun.COM {
121610157Sgdamore@opensolaris.org 	queue_t		*wq = auclnt_get_wq(c);
12179484Sgarrett.damore@Sun.COM 
12189484Sgarrett.damore@Sun.COM 	switch (DB_TYPE(mp)) {
12199484Sgarrett.damore@Sun.COM 	case M_IOCTL:
12209484Sgarrett.damore@Sun.COM 		/* Drain ioctl needs to be handled on the service queue */
122110157Sgdamore@opensolaris.org 		devaudio_mioctl(c, mp);
122210157Sgdamore@opensolaris.org 		break;
122310157Sgdamore@opensolaris.org 
122410157Sgdamore@opensolaris.org 	case M_IOCDATA:
122510157Sgdamore@opensolaris.org 		devaudio_miocdata(c, mp);
122610157Sgdamore@opensolaris.org 		break;
122710157Sgdamore@opensolaris.org 
122810157Sgdamore@opensolaris.org 	case M_FLUSH:
122910157Sgdamore@opensolaris.org 		/*
123010157Sgdamore@opensolaris.org 		 * We don't flush the engine.  The reason is that
123110157Sgdamore@opensolaris.org 		 * other streams might be using the engine.  This is
123210157Sgdamore@opensolaris.org 		 * fundamentally no different from the case where the
123310157Sgdamore@opensolaris.org 		 * engine hardware has data buffered in an
123410157Sgdamore@opensolaris.org 		 * inaccessible FIFO.
123510157Sgdamore@opensolaris.org 		 *
123610157Sgdamore@opensolaris.org 		 * Clients that want to ensure no more data is coming
123710157Sgdamore@opensolaris.org 		 * should stop the stream before flushing.
123810157Sgdamore@opensolaris.org 		 */
123910157Sgdamore@opensolaris.org 		if (*mp->b_rptr & FLUSHW) {
124010157Sgdamore@opensolaris.org 			*mp->b_rptr &= ~FLUSHW;
124110157Sgdamore@opensolaris.org 		}
124210157Sgdamore@opensolaris.org 		if (*mp->b_rptr & FLUSHR) {
124310157Sgdamore@opensolaris.org 			qreply(wq, mp);
12449484Sgarrett.damore@Sun.COM 		} else {
124510157Sgdamore@opensolaris.org 			freemsg(mp);
124610157Sgdamore@opensolaris.org 		}
124710157Sgdamore@opensolaris.org 		break;
124810157Sgdamore@opensolaris.org 
124910157Sgdamore@opensolaris.org 	case M_DATA:
125010157Sgdamore@opensolaris.org 		/*
125110157Sgdamore@opensolaris.org 		 * No audio data on control nodes!
125210157Sgdamore@opensolaris.org 		 */
125310157Sgdamore@opensolaris.org 		freemsg(mp);
125410157Sgdamore@opensolaris.org 
125510157Sgdamore@opensolaris.org 	default:
125610157Sgdamore@opensolaris.org 		freemsg(mp);
125710157Sgdamore@opensolaris.org 		break;
125810157Sgdamore@opensolaris.org 	}
125910157Sgdamore@opensolaris.org }
126010157Sgdamore@opensolaris.org 
126110157Sgdamore@opensolaris.org static void
devaudio_wput(audio_client_t * c,mblk_t * mp)126210157Sgdamore@opensolaris.org devaudio_wput(audio_client_t *c, mblk_t *mp)
126310157Sgdamore@opensolaris.org {
126410157Sgdamore@opensolaris.org 	queue_t		*wq = auclnt_get_wq(c);
126510157Sgdamore@opensolaris.org 
126610157Sgdamore@opensolaris.org 	switch (DB_TYPE(mp)) {
126710157Sgdamore@opensolaris.org 	case M_IOCTL:
126810157Sgdamore@opensolaris.org 		/* Drain ioctl needs to be handled on the service queue */
126910157Sgdamore@opensolaris.org 		if (*(int *)(void *)mp->b_rptr == AUDIO_DRAIN) {
127010157Sgdamore@opensolaris.org 			(void) putq(wq, mp);
127110157Sgdamore@opensolaris.org 		} else {
127210157Sgdamore@opensolaris.org 			devaudio_mioctl(c, mp);
12739484Sgarrett.damore@Sun.COM 		}
12749484Sgarrett.damore@Sun.COM 		break;
12759484Sgarrett.damore@Sun.COM 
12769484Sgarrett.damore@Sun.COM 	case M_IOCDATA:
127710157Sgdamore@opensolaris.org 		devaudio_miocdata(c, mp);
12789484Sgarrett.damore@Sun.COM 		break;
12799484Sgarrett.damore@Sun.COM 
12809484Sgarrett.damore@Sun.COM 	case M_FLUSH:
12819484Sgarrett.damore@Sun.COM 		/*
12829484Sgarrett.damore@Sun.COM 		 * We don't flush the engine.  The reason is that
12839484Sgarrett.damore@Sun.COM 		 * other streams might be using the engine.  This is
12849484Sgarrett.damore@Sun.COM 		 * fundamentally no different from the case where the
12859484Sgarrett.damore@Sun.COM 		 * engine hardware has data buffered in an
12869484Sgarrett.damore@Sun.COM 		 * inaccessible FIFO.
12879484Sgarrett.damore@Sun.COM 		 *
12889484Sgarrett.damore@Sun.COM 		 * Clients that want to ensure no more data is coming
12899484Sgarrett.damore@Sun.COM 		 * should stop the stream before flushing.
12909484Sgarrett.damore@Sun.COM 		 */
12919484Sgarrett.damore@Sun.COM 		if (*mp->b_rptr & FLUSHW) {
12929484Sgarrett.damore@Sun.COM 			flushq(wq, FLUSHALL);
129310157Sgdamore@opensolaris.org 			auclnt_flush(auclnt_output_stream(c));
12949484Sgarrett.damore@Sun.COM 			*mp->b_rptr &= ~FLUSHW;
12959484Sgarrett.damore@Sun.COM 		}
12969484Sgarrett.damore@Sun.COM 		if (*mp->b_rptr & FLUSHR) {
12979484Sgarrett.damore@Sun.COM 			flushq(RD(wq), FLUSHALL);
129810157Sgdamore@opensolaris.org 			auclnt_flush(auclnt_input_stream(c));
12999484Sgarrett.damore@Sun.COM 			qreply(wq, mp);
13009484Sgarrett.damore@Sun.COM 		} else {
13019484Sgarrett.damore@Sun.COM 			freemsg(mp);
13029484Sgarrett.damore@Sun.COM 		}
13039484Sgarrett.damore@Sun.COM 		break;
13049484Sgarrett.damore@Sun.COM 
13059484Sgarrett.damore@Sun.COM 	case M_DATA:
13069484Sgarrett.damore@Sun.COM 		/*
130710157Sgdamore@opensolaris.org 		 * Defer processing to the queue.  This keeps the data
130810157Sgdamore@opensolaris.org 		 * ordered, and allows the wsrv routine to gather
130910157Sgdamore@opensolaris.org 		 * multiple mblks at once.
13109484Sgarrett.damore@Sun.COM 		 */
131110157Sgdamore@opensolaris.org 		if (mp->b_cont != NULL) {
13129484Sgarrett.damore@Sun.COM 
131310157Sgdamore@opensolaris.org 			/*
131410157Sgdamore@opensolaris.org 			 * If we need to pullup, do it here to
131510157Sgdamore@opensolaris.org 			 * simplify the rest of the processing later.
131610157Sgdamore@opensolaris.org 			 * This should rarely (if ever) be necessary.
131710157Sgdamore@opensolaris.org 			 */
131810157Sgdamore@opensolaris.org 			mblk_t	*nmp;
13199484Sgarrett.damore@Sun.COM 
132010157Sgdamore@opensolaris.org 			if ((nmp = msgpullup(mp, -1)) == NULL) {
132110157Sgdamore@opensolaris.org 				freemsg(mp);
13229484Sgarrett.damore@Sun.COM 			} else {
132310157Sgdamore@opensolaris.org 				freemsg(mp);
132410157Sgdamore@opensolaris.org 				(void) putq(wq, nmp);
13259484Sgarrett.damore@Sun.COM 			}
132610157Sgdamore@opensolaris.org 		} else {
132710157Sgdamore@opensolaris.org 			(void) putq(wq, mp);
13289484Sgarrett.damore@Sun.COM 		}
13299484Sgarrett.damore@Sun.COM 		break;
13309484Sgarrett.damore@Sun.COM 
13319484Sgarrett.damore@Sun.COM 	default:
13329484Sgarrett.damore@Sun.COM 		freemsg(mp);
13339484Sgarrett.damore@Sun.COM 		break;
13349484Sgarrett.damore@Sun.COM 	}
13359484Sgarrett.damore@Sun.COM }
13369484Sgarrett.damore@Sun.COM 
133710157Sgdamore@opensolaris.org static void
devaudio_rsrv(audio_client_t * c)133810632Sgdamore@opensolaris.org devaudio_rsrv(audio_client_t *c)
133910632Sgdamore@opensolaris.org {
134010632Sgdamore@opensolaris.org 	queue_t		*rq = auclnt_get_rq(c);
134110632Sgdamore@opensolaris.org 	mblk_t		*mp;
134210632Sgdamore@opensolaris.org 
134310632Sgdamore@opensolaris.org 	while ((mp = getq(rq)) != NULL) {
134410632Sgdamore@opensolaris.org 
134510632Sgdamore@opensolaris.org 		if ((queclass(mp) != QPCTL) && (!canputnext(rq))) {
134610644SGarrett.Damore@Sun.COM 			/*
134710644SGarrett.Damore@Sun.COM 			 * Put it back in the queue so we can apply
134810644SGarrett.Damore@Sun.COM 			 * backpressure properly.
134910644SGarrett.Damore@Sun.COM 			 */
135010644SGarrett.Damore@Sun.COM 			(void) putbq(rq, mp);
135110632Sgdamore@opensolaris.org 			return;
135210632Sgdamore@opensolaris.org 		}
135310632Sgdamore@opensolaris.org 		putnext(rq, mp);
135410632Sgdamore@opensolaris.org 	}
135510632Sgdamore@opensolaris.org }
135610632Sgdamore@opensolaris.org 
135710632Sgdamore@opensolaris.org static void
devaudio_wsrv(audio_client_t * c)135810157Sgdamore@opensolaris.org devaudio_wsrv(audio_client_t *c)
13599484Sgarrett.damore@Sun.COM {
136010157Sgdamore@opensolaris.org 	queue_t		*wq = auclnt_get_wq(c);
136110157Sgdamore@opensolaris.org 	daclient_t	*dc = auclnt_get_private(c);
13629484Sgarrett.damore@Sun.COM 	audio_stream_t	*sp;
13639484Sgarrett.damore@Sun.COM 	mblk_t		*mp;
13649484Sgarrett.damore@Sun.COM 	unsigned	framesz;
13659484Sgarrett.damore@Sun.COM 
13669484Sgarrett.damore@Sun.COM 	sp = auclnt_output_stream(c);
13679484Sgarrett.damore@Sun.COM 
13689484Sgarrett.damore@Sun.COM 	framesz = auclnt_get_framesz(sp);
13699484Sgarrett.damore@Sun.COM 
13709484Sgarrett.damore@Sun.COM 	while ((mp = getq(wq)) != NULL) {
13719484Sgarrett.damore@Sun.COM 
13729484Sgarrett.damore@Sun.COM 		unsigned	count;
13739484Sgarrett.damore@Sun.COM 
13749484Sgarrett.damore@Sun.COM 		/* got a message */
13759484Sgarrett.damore@Sun.COM 
13769484Sgarrett.damore@Sun.COM 		/* if its a drain ioctl, we need to process it here */
13779484Sgarrett.damore@Sun.COM 		if (DB_TYPE(mp) == M_IOCTL) {
13789484Sgarrett.damore@Sun.COM 			ASSERT((*(int *)(void *)mp->b_rptr) == AUDIO_DRAIN);
137910157Sgdamore@opensolaris.org 			mutex_enter(&dc->dc_lock);
138010157Sgdamore@opensolaris.org 			mp->b_next = dc->dc_draining;
138110157Sgdamore@opensolaris.org 			dc->dc_draining = mp;
138210157Sgdamore@opensolaris.org 			mutex_exit(&dc->dc_lock);
13839484Sgarrett.damore@Sun.COM 
13849484Sgarrett.damore@Sun.COM 			if (auclnt_start_drain(c) != 0) {
138510157Sgdamore@opensolaris.org 				devaudio_drain(c);
13869484Sgarrett.damore@Sun.COM 			}
13879484Sgarrett.damore@Sun.COM 			continue;
13889484Sgarrett.damore@Sun.COM 		}
13899484Sgarrett.damore@Sun.COM 
13909484Sgarrett.damore@Sun.COM 		ASSERT(DB_TYPE(mp) == M_DATA);
13919484Sgarrett.damore@Sun.COM 
13929484Sgarrett.damore@Sun.COM 		/*
13939484Sgarrett.damore@Sun.COM 		 * Empty mblk require special handling, since they
13949484Sgarrett.damore@Sun.COM 		 * indicate EOF.  We treat them separate from the main
13959484Sgarrett.damore@Sun.COM 		 * processing loop.
13969484Sgarrett.damore@Sun.COM 		 */
13979484Sgarrett.damore@Sun.COM 		if (MBLKL(mp) == 0) {
13989484Sgarrett.damore@Sun.COM 			struct eofcnt	*eof;
13999484Sgarrett.damore@Sun.COM 
14009484Sgarrett.damore@Sun.COM 			eof = kmem_zalloc(sizeof (*eof), KM_NOSLEEP);
14019484Sgarrett.damore@Sun.COM 			if (eof != NULL) {
14029484Sgarrett.damore@Sun.COM 				eof->tail = auclnt_get_head(sp);
140310157Sgdamore@opensolaris.org 				mutex_enter(&dc->dc_lock);
140410157Sgdamore@opensolaris.org 				list_insert_tail(&dc->dc_eofcnt, eof);
140510157Sgdamore@opensolaris.org 				mutex_exit(&dc->dc_lock);
14069484Sgarrett.damore@Sun.COM 			}
14079484Sgarrett.damore@Sun.COM 			freemsg(mp);
14089484Sgarrett.damore@Sun.COM 			continue;
14099484Sgarrett.damore@Sun.COM 		}
14109484Sgarrett.damore@Sun.COM 
14119484Sgarrett.damore@Sun.COM 		count = auclnt_produce_data(sp, (caddr_t)mp->b_rptr,
14129484Sgarrett.damore@Sun.COM 		    MBLKL(mp) / framesz);
14139484Sgarrett.damore@Sun.COM 
14149484Sgarrett.damore@Sun.COM 		mp->b_rptr += count * framesz;
14159484Sgarrett.damore@Sun.COM 
14169484Sgarrett.damore@Sun.COM 		if (MBLKL(mp) >= framesz) {
14179484Sgarrett.damore@Sun.COM 			(void) putbq(wq, mp);
14189484Sgarrett.damore@Sun.COM 			break;
14199484Sgarrett.damore@Sun.COM 		} else {
14209484Sgarrett.damore@Sun.COM 			freemsg(mp);
14219484Sgarrett.damore@Sun.COM 		}
14229484Sgarrett.damore@Sun.COM 	}
14239484Sgarrett.damore@Sun.COM 
14249484Sgarrett.damore@Sun.COM 	/* if the stream isn't running yet, start it up */
14259484Sgarrett.damore@Sun.COM 	if (!auclnt_is_paused(sp))
14269484Sgarrett.damore@Sun.COM 		auclnt_start(sp);
14279484Sgarrett.damore@Sun.COM }
14289484Sgarrett.damore@Sun.COM 
142910157Sgdamore@opensolaris.org static struct audio_client_ops devaudio_ops = {
143010157Sgdamore@opensolaris.org 	"sound,audio",
143110157Sgdamore@opensolaris.org 	devaudio_init,
143210157Sgdamore@opensolaris.org 	devaudio_fini,
143310157Sgdamore@opensolaris.org 	devaudio_open,
143410157Sgdamore@opensolaris.org 	devaudio_close,
143510157Sgdamore@opensolaris.org 	NULL,	/* read */
143610157Sgdamore@opensolaris.org 	NULL,	/* write */
143710157Sgdamore@opensolaris.org 	NULL,	/* ioctl */
143810157Sgdamore@opensolaris.org 	NULL,	/* chpoll */
143910157Sgdamore@opensolaris.org 	NULL,	/* mmap */
144010157Sgdamore@opensolaris.org 	devaudio_input,
144110157Sgdamore@opensolaris.org 	devaudio_output,
144210157Sgdamore@opensolaris.org 	devaudio_drain,
144310157Sgdamore@opensolaris.org 	devaudio_wput,
144410632Sgdamore@opensolaris.org 	devaudio_wsrv,
144510632Sgdamore@opensolaris.org 	devaudio_rsrv
14469484Sgarrett.damore@Sun.COM };
14479484Sgarrett.damore@Sun.COM 
144810157Sgdamore@opensolaris.org static struct audio_client_ops devaudioctl_ops = {
144910157Sgdamore@opensolaris.org 	"sound,audioctl",
145010157Sgdamore@opensolaris.org 	NULL,	/* dev_init */
145110157Sgdamore@opensolaris.org 	NULL,	/* dev_fini */
145210157Sgdamore@opensolaris.org 	devaudioctl_open,
145310157Sgdamore@opensolaris.org 	devaudioctl_close,
145410157Sgdamore@opensolaris.org 	NULL,	/* read */
145510157Sgdamore@opensolaris.org 	NULL,	/* write */
145610157Sgdamore@opensolaris.org 	NULL,	/* ioctl */
145710157Sgdamore@opensolaris.org 	NULL,	/* chpoll */
145810157Sgdamore@opensolaris.org 	NULL,	/* mmap */
145910157Sgdamore@opensolaris.org 	NULL,	/* output */
146010157Sgdamore@opensolaris.org 	NULL,	/* input */
146110157Sgdamore@opensolaris.org 	NULL,	/* drain */
146210157Sgdamore@opensolaris.org 	devaudioctl_wput,
14639484Sgarrett.damore@Sun.COM 	NULL,
146410644SGarrett.Damore@Sun.COM 	devaudio_rsrv
14659484Sgarrett.damore@Sun.COM };
14669484Sgarrett.damore@Sun.COM 
146710157Sgdamore@opensolaris.org void
auimpl_sun_init(void)146810157Sgdamore@opensolaris.org auimpl_sun_init(void)
14699484Sgarrett.damore@Sun.COM {
147010157Sgdamore@opensolaris.org 	auclnt_register_ops(AUDIO_MINOR_DEVAUDIO, &devaudio_ops);
147110157Sgdamore@opensolaris.org 	auclnt_register_ops(AUDIO_MINOR_DEVAUDIOCTL, &devaudioctl_ops);
14729484Sgarrett.damore@Sun.COM }
1473