Home | History | Annotate | Line # | Download | only in ossaudio
ossaudio.c revision 1.8
      1 /*	$NetBSD: ossaudio.c,v 1.8 1997/05/07 18:51:37 augustss Exp $	*/
      2 #include <sys/param.h>
      3 #include <sys/proc.h>
      4 #include <sys/systm.h>
      5 #include <sys/file.h>
      6 #include <sys/vnode.h>
      7 #include <sys/filedesc.h>
      8 #include <sys/ioctl.h>
      9 #include <sys/mount.h>
     10 #include <sys/audioio.h>
     11 
     12 #include <sys/syscallargs.h>
     13 
     14 #include <compat/ossaudio/ossaudio.h>
     15 #include <compat/ossaudio/ossaudiovar.h>
     16 
     17 static struct audiodevinfo *getdevinfo __P((struct file *, struct proc *));
     18 
     19 int
     20 oss_ioctl_audio(p, uap, retval)
     21 	register struct proc *p;
     22 	register struct oss_sys_ioctl_args /* {
     23 		syscallarg(int) fd;
     24 		syscallarg(u_long) com;
     25 		syscallarg(caddr_t) data;
     26 	} */ *uap;
     27 	register_t *retval;
     28 {
     29 	register struct file *fp;
     30 	register struct filedesc *fdp;
     31 	u_long com;
     32 	struct audio_info tmpinfo;
     33 	int idat;
     34 	int error;
     35 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
     36 
     37 	fdp = p->p_fd;
     38 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
     39 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
     40 		return (EBADF);
     41 
     42 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
     43 		return (EBADF);
     44 
     45 	ioctlf = fp->f_ops->fo_ioctl;
     46 
     47 	com = SCARG(uap, com);
     48 	retval[0] = 0;
     49 
     50 	switch (com) {
     51 	case OSS_SNDCTL_DSP_RESET:
     52 		error = ioctlf(fp, AUDIO_FLUSH, (caddr_t)0, p);
     53 		if (error)
     54 			return error;
     55 		break;
     56 	case OSS_SNDCTL_DSP_SYNC:
     57 	case OSS_SNDCTL_DSP_POST:
     58 		error = ioctlf(fp, AUDIO_DRAIN, (caddr_t)0, p);
     59 		if (error)
     60 			return error;
     61 		break;
     62 	case OSS_SNDCTL_DSP_SPEED:
     63 		AUDIO_INITINFO(&tmpinfo);
     64 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
     65 		if (error)
     66 			return error;
     67 		tmpinfo.play.sample_rate =
     68 		tmpinfo.record.sample_rate = idat;
     69 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
     70 		/* fall into ... */
     71 	case OSS_SOUND_PCM_READ_RATE:
     72 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
     73 		if (error)
     74 			return error;
     75 		idat = tmpinfo.play.sample_rate;
     76 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
     77 		if (error)
     78 			return error;
     79 		break;
     80 	case OSS_SNDCTL_DSP_STEREO:
     81 		AUDIO_INITINFO(&tmpinfo);
     82 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
     83 		if (error)
     84 			return error;
     85 		tmpinfo.play.channels =
     86 		tmpinfo.record.channels = idat ? 2 : 1;
     87 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
     88 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
     89 		if (error)
     90 			return error;
     91 		idat = tmpinfo.play.channels - 1;
     92 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
     93 		if (error)
     94 			return error;
     95 		break;
     96 	case OSS_SNDCTL_DSP_GETBLKSIZE:
     97 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
     98 		if (error)
     99 			return error;
    100 		idat = tmpinfo.blocksize;
    101 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    102 		if (error)
    103 			return error;
    104 		break;
    105 	case OSS_SNDCTL_DSP_SETFMT:
    106 		AUDIO_INITINFO(&tmpinfo);
    107 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    108 		if (error)
    109 			return error;
    110 		switch (idat) {
    111 		case OSS_AFMT_MU_LAW:
    112 			tmpinfo.play.precision =
    113 			tmpinfo.record.precision = 8;
    114 			tmpinfo.play.encoding =
    115 			tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
    116 			break;
    117 		case OSS_AFMT_A_LAW:
    118 			tmpinfo.play.precision =
    119 			tmpinfo.record.precision = 8;
    120 			tmpinfo.play.encoding =
    121 			tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
    122 			break;
    123 		case OSS_AFMT_U8:
    124 			tmpinfo.play.precision =
    125 			tmpinfo.record.precision = 8;
    126 			tmpinfo.play.encoding =
    127 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR;
    128 			break;
    129 		case OSS_AFMT_S8:
    130 			tmpinfo.play.precision =
    131 			tmpinfo.record.precision = 8;
    132 			tmpinfo.play.encoding =
    133 			tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR;
    134 			break;
    135 		case OSS_AFMT_S16_LE:
    136 			tmpinfo.play.precision =
    137 			tmpinfo.record.precision = 16;
    138 			tmpinfo.play.encoding =
    139 			tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR_LE;
    140 			break;
    141 		case OSS_AFMT_S16_BE:
    142 			tmpinfo.play.precision =
    143 			tmpinfo.record.precision = 16;
    144 			tmpinfo.play.encoding =
    145 			tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR_BE;
    146 			break;
    147 		case OSS_AFMT_U16_LE:
    148 			tmpinfo.play.precision =
    149 			tmpinfo.record.precision = 16;
    150 			tmpinfo.play.encoding =
    151 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE;
    152 			break;
    153 		case OSS_AFMT_U16_BE:
    154 			tmpinfo.play.precision =
    155 			tmpinfo.record.precision = 16;
    156 			tmpinfo.play.encoding =
    157 			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
    158 			break;
    159 		default:
    160 			return EINVAL;
    161 		}
    162 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    163 		/* fall into ... */
    164 	case OSS_SOUND_PCM_READ_BITS:
    165 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    166 		if (error)
    167 			return error;
    168 		switch (tmpinfo.play.encoding) {
    169 		case AUDIO_ENCODING_ULAW:
    170 			idat = OSS_AFMT_MU_LAW;
    171 			break;
    172 		case AUDIO_ENCODING_ALAW:
    173 			idat = OSS_AFMT_A_LAW;
    174 			break;
    175 		case AUDIO_ENCODING_LINEAR_LE:
    176 			if (tmpinfo.play.precision == 16)
    177 				idat = OSS_AFMT_S16_LE;
    178 			else
    179 				idat = OSS_AFMT_S8;
    180 			break;
    181 		case AUDIO_ENCODING_LINEAR_BE:
    182 			if (tmpinfo.play.precision == 16)
    183 				idat = OSS_AFMT_S16_BE;
    184 			else
    185 				idat = OSS_AFMT_S8;
    186 			break;
    187 		case AUDIO_ENCODING_ULINEAR_LE:
    188 			if (tmpinfo.play.precision == 16)
    189 				idat = OSS_AFMT_U16_LE;
    190 			else
    191 				idat = OSS_AFMT_U8;
    192 			break;
    193 		case AUDIO_ENCODING_ULINEAR_BE:
    194 			if (tmpinfo.play.precision == 16)
    195 				idat = OSS_AFMT_U16_BE;
    196 			else
    197 				idat = OSS_AFMT_U8;
    198 			break;
    199 		case AUDIO_ENCODING_ADPCM:
    200 			idat = OSS_AFMT_IMA_ADPCM;
    201 			break;
    202 		}
    203 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    204 		if (error)
    205 			return error;
    206 		break;
    207 	case OSS_SNDCTL_DSP_CHANNELS:
    208 		AUDIO_INITINFO(&tmpinfo);
    209 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    210 		if (error)
    211 			return error;
    212 		tmpinfo.play.channels =
    213 		tmpinfo.record.channels = idat;
    214 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    215 		/* fall into ... */
    216 	case OSS_SOUND_PCM_READ_CHANNELS:
    217 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    218 		if (error)
    219 			return error;
    220 		idat = tmpinfo.play.channels;
    221 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    222 		if (error)
    223 			return error;
    224 		break;
    225 	case OSS_SOUND_PCM_WRITE_FILTER:
    226 	case OSS_SOUND_PCM_READ_FILTER:
    227 		return EINVAL; /* XXX unimplemented */
    228 	case OSS_SNDCTL_DSP_SUBDIVIDE:
    229 		return EINVAL; /* XXX unimplemented */
    230 	case OSS_SNDCTL_DSP_SETFRAGMENT:
    231 		AUDIO_INITINFO(&tmpinfo);
    232 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    233 		if (error)
    234 			return error;
    235 		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17)
    236 			return EINVAL;
    237 		tmpinfo.blocksize = 1 << (idat & 0xffff);
    238 		tmpinfo.hiwat = (idat >> 16) & 0xffff;
    239 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    240 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    241 		if (error)
    242 			return error;
    243 		idat = tmpinfo.blocksize;
    244 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    245 		if (error)
    246 			return error;
    247 		break;
    248 	case OSS_SNDCTL_DSP_GETFMTS:
    249 		idat = OSS_AFMT_MU_LAW | OSS_AFMT_U8 | OSS_AFMT_S16_LE;
    250 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    251 		if (error)
    252 			return error;
    253 		break;
    254 	case OSS_SNDCTL_DSP_GETOSPACE:
    255 	case OSS_SNDCTL_DSP_GETISPACE:
    256 	case OSS_SNDCTL_DSP_NONBLOCK:
    257 		return EINVAL; /* XXX unimplemented */
    258 	case OSS_SNDCTL_DSP_GETCAPS:
    259 		idat = 0; /* XXX full duplex info should be set */
    260 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    261 		if (error)
    262 			return error;
    263 		break;
    264 	case OSS_SNDCTL_DSP_GETTRIGGER:
    265 	case OSS_SNDCTL_DSP_SETTRIGGER:
    266 	case OSS_SNDCTL_DSP_GETIPTR:
    267 	case OSS_SNDCTL_DSP_GETOPTR:
    268 	case OSS_SNDCTL_DSP_MAPINBUF:
    269 	case OSS_SNDCTL_DSP_MAPOUTBUF:
    270 	case OSS_SNDCTL_DSP_SETSYNCRO:
    271 	case OSS_SNDCTL_DSP_SETDUPLEX:
    272 	case OSS_SNDCTL_DSP_PROFILE:
    273 		return EINVAL; /* XXX unimplemented */
    274 	default:
    275 		return EINVAL;
    276 	}
    277 
    278 	return 0;
    279 }
    280 
    281 /* If the NetBSD mixer device should have more than 32 devices
    282  * some will not be available to Linux */
    283 #define NETBSD_MAXDEVS 32
    284 struct audiodevinfo {
    285 	int done;
    286 	dev_t dev;
    287 	int16_t devmap[OSS_SOUND_MIXER_NRDEVICES],
    288 	        rdevmap[NETBSD_MAXDEVS];
    289         u_long devmask, recmask, stereomask;
    290 	u_long caps, source;
    291 };
    292 
    293 /*
    294  * Collect the audio device information to allow faster
    295  * emulation of the Linux mixer ioctls.  Cache the information
    296  * to eliminate the overhead of repeating all the ioctls needed
    297  * to collect the information.
    298  */
    299 static struct audiodevinfo *
    300 getdevinfo(fp, p)
    301 	struct file *fp;
    302 	struct proc *p;
    303 {
    304 	mixer_devinfo_t mi;
    305 	int i;
    306 	static struct {
    307 		char *name;
    308 		int code;
    309 	} *dp, devs[] = {
    310 		{ AudioNmicrophone,	OSS_SOUND_MIXER_MIC },
    311 		{ AudioNline,		OSS_SOUND_MIXER_LINE },
    312 		{ AudioNcd,		OSS_SOUND_MIXER_CD },
    313 		{ AudioNdac,		OSS_SOUND_MIXER_PCM },
    314 		{ AudioNrecord,		OSS_SOUND_MIXER_IMIX },
    315 		{ AudioNvolume,		OSS_SOUND_MIXER_VOLUME },
    316 		{ AudioNtreble,		OSS_SOUND_MIXER_TREBLE },
    317 		{ AudioNbass,		OSS_SOUND_MIXER_BASS },
    318 		{ AudioNspeaker,	OSS_SOUND_MIXER_SPEAKER },
    319 /*		{ AudioNheadphone,	?? },*/
    320 		{ AudioNoutput,		OSS_SOUND_MIXER_OGAIN },
    321 		{ AudioNinput,		OSS_SOUND_MIXER_IGAIN },
    322 /*		{ AudioNmaster,		OSS_SOUND_MIXER_MASTER },*/
    323 /*		{ AudioNstereo,		?? },*/
    324 /*		{ AudioNmono,		?? },*/
    325 /*		{ AudioNsource,		?? },*/
    326 		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
    327 /*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
    328 /*		{ AudioNmidi,		?? },*/
    329 /*		{ AudioNmixerout,	?? },*/
    330 		{ 0, -1 }
    331 	};
    332 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) =
    333 	    fp->f_ops->fo_ioctl;
    334 	struct vnode *vp;
    335 	struct vattr va;
    336 	static struct audiodevinfo devcache = { 0 };
    337 	struct audiodevinfo *di = &devcache;
    338 
    339 	/* Figure out what device it is so we can check if the
    340 	 * cached data is valid.
    341 	 */
    342 	vp = (struct vnode *)fp->f_data;
    343 	if (vp->v_type != VCHR)
    344 		return 0;
    345 	if (VOP_GETATTR(vp, &va, p->p_ucred, p))
    346 		return 0;
    347 	if (di->done && di->dev == va.va_rdev)
    348 		return di;
    349 
    350 	di->done = 1;
    351 	di->dev = va.va_rdev;
    352 	di->devmask = 0;
    353 	di->recmask = 0;
    354 	di->stereomask = 0;
    355 	di->source = -1;
    356 	di->caps = 0;
    357 	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    358 		di->devmap[i] = -1;
    359 	for(i = 0; i < NETBSD_MAXDEVS; i++)
    360 		di->rdevmap[i] = -1;
    361 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
    362 		mi.index = i;
    363 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
    364 			break;
    365 		switch(mi.type) {
    366 		case AUDIO_MIXER_VALUE:
    367 			for(dp = devs; dp->name; dp++)
    368 		    		if (strcmp(dp->name, mi.label.name) == 0)
    369 					break;
    370 			if (dp->code >= 0) {
    371 				di->devmap[dp->code] = i;
    372 				di->rdevmap[i] = dp->code;
    373 				di->devmask |= 1 << dp->code;
    374 				if (mi.un.v.num_channels == 2)
    375 					di->stereomask |= 1 << dp->code;
    376 			}
    377 			break;
    378 		case AUDIO_MIXER_ENUM:
    379 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    380 				int j;
    381 				di->source = i;
    382 				for(j = 0; j < mi.un.e.num_mem; j++) {
    383 					di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord];
    384 				}
    385 				di->caps = OSS_SOUND_CAP_EXCL_INPUT;
    386 			}
    387 			break;
    388 		case AUDIO_MIXER_SET:
    389 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    390 				int j;
    391 				di->source = i;
    392 				for(j = 0; j < mi.un.s.num_mem; j++) {
    393 					int k, mask = mi.un.s.member[j].mask;
    394 					if (mask) {
    395 						for(k = 0; !(mask & (1<<k)); k++)
    396 							;
    397 						di->recmask |= 1 << di->rdevmap[k];
    398 					}
    399 				}
    400 			}
    401 			break;
    402 		}
    403 	}
    404 	return di;
    405 }
    406 
    407 int
    408 oss_ioctl_mixer(p, uap, retval)
    409 	register struct proc *p;
    410 	register struct oss_sys_ioctl_args /* {
    411 		syscallarg(int) fd;
    412 		syscallarg(u_long) com;
    413 		syscallarg(caddr_t) data;
    414 	} */ *uap;
    415 	register_t *retval;
    416 {
    417 	struct file *fp;
    418 	struct filedesc *fdp;
    419 	u_long com;
    420 	struct audiodevinfo *di;
    421 	mixer_ctrl_t mc;
    422 	int idat;
    423 	int i;
    424 	int error;
    425 	int l, r, n;
    426 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
    427 
    428 	fdp = p->p_fd;
    429 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
    430 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
    431 		return (EBADF);
    432 
    433 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
    434 		return (EBADF);
    435 
    436 	com = SCARG(uap, com);
    437 	retval[0] = 0;
    438 
    439 	di = getdevinfo(fp, p);
    440 	if (di == 0)
    441 		return EINVAL;
    442 
    443 	ioctlf = fp->f_ops->fo_ioctl;
    444 	switch (com) {
    445 	case OSS_SOUND_MIXER_READ_RECSRC:
    446 		if (di->source == -1)
    447 			return EINVAL;
    448 		mc.dev = di->source;
    449 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
    450 			mc.type = AUDIO_MIXER_ENUM;
    451 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    452 			if (error)
    453 				return error;
    454 			idat = 1 << di->rdevmap[mc.un.ord];
    455 		} else {
    456 			int k;
    457 			unsigned int mask;
    458 			mc.type = AUDIO_MIXER_SET;
    459 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    460 			if (error)
    461 				return error;
    462 			idat = 0;
    463 			for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++)
    464 				idat |= 1 << di->rdevmap[k];
    465 		}
    466 		break;
    467 	case OSS_SOUND_MIXER_READ_DEVMASK:
    468 		idat = di->devmask;
    469 		break;
    470 	case OSS_SOUND_MIXER_READ_RECMASK:
    471 		idat = di->recmask;
    472 		break;
    473 	case OSS_SOUND_MIXER_READ_STEREODEVS:
    474 		idat = di->stereomask;
    475 		break;
    476 	case OSS_SOUND_MIXER_READ_CAPS:
    477 		idat = di->caps;
    478 		break;
    479 	case OSS_SOUND_MIXER_WRITE_RECSRC:
    480 		if (di->source == -1)
    481 			return EINVAL;
    482 		mc.dev = di->source;
    483 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    484 		if (error)
    485 			return error;
    486 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
    487 			mc.type = AUDIO_MIXER_ENUM;
    488 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    489 				if (idat & (1 << i))
    490 					break;
    491 			if (i >= OSS_SOUND_MIXER_NRDEVICES ||
    492 			    di->devmap[i] == -1)
    493 				return EINVAL;
    494 			mc.un.ord = di->devmap[i];
    495 		} else {
    496 			mc.type = AUDIO_MIXER_SET;
    497 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    498 				if (idat & (1 << i)) {
    499 					if (di->devmap[i] == -1)
    500 						return EINVAL;
    501 					mc.un.mask |= 1 << di->devmap[i];
    502 				}
    503 		}
    504 		return ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
    505 	default:
    506 		if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com &&
    507 		    com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) {
    508 			n = OSS_GET_DEV(com);
    509 			if (di->devmap[n] == -1)
    510 				return EINVAL;
    511 			mc.dev = di->devmap[n];
    512 			mc.type = AUDIO_MIXER_VALUE;
    513 		    doread:
    514 			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
    515 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    516 			if (error)
    517 				return error;
    518 			if (mc.type != AUDIO_MIXER_VALUE)
    519 				return EINVAL;
    520 			if (mc.un.value.num_channels != 2) {
    521 				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
    522 			} else {
    523 				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    524 				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    525 			}
    526 			l = l * 100 / 255;
    527 			r = r * 100 / 255;
    528 			idat = l | (r << 8);
    529 			break;
    530 		} else if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
    531 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
    532 			n = OSS_GET_DEV(com);
    533 			if (di->devmap[n] == -1)
    534 				return EINVAL;
    535 			error = copyin(SCARG(uap, data), &idat, sizeof idat);
    536 			if (error)
    537 				return error;
    538 			l = (idat & 0xff) * 255 / 100;
    539 			r = (idat >> 8) * 255 / 100;
    540 			mc.dev = di->devmap[n];
    541 			mc.type = AUDIO_MIXER_VALUE;
    542 			if (di->stereomask & (1<<n)) {
    543 				mc.un.value.num_channels = 2;
    544 				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
    545 				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
    546 			} else {
    547 				mc.un.value.num_channels = 1;
    548 				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
    549 			}
    550 			error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
    551 			if (error)
    552 				return error;
    553 			goto doread;
    554 		} else
    555 			return EINVAL;
    556 	}
    557 	return copyout(&idat, SCARG(uap, data), sizeof idat);
    558 }
    559 
    560 /* XXX hook for sequencer emulation */
    561 int
    562 oss_ioctl_sequencer(p, uap, retval)
    563 	register struct proc *p;
    564 	register struct oss_sys_ioctl_args /* {
    565 		syscallarg(int) fd;
    566 		syscallarg(u_long) com;
    567 		syscallarg(caddr_t) data;
    568 	} */ *uap;
    569 	register_t *retval;
    570 {
    571 	register struct file *fp;
    572 	register struct filedesc *fdp;
    573 #if 0
    574 	u_long com;
    575 	int idat;
    576 	int error;
    577 #endif
    578 
    579 	fdp = p->p_fd;
    580 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
    581 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
    582 		return (EBADF);
    583 
    584 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
    585 		return (EBADF);
    586 
    587 #if 0
    588 	com = SCARG(uap, com);
    589 #endif
    590 	retval[0] = 0;
    591 
    592 	return EINVAL;
    593 }
    594