Home | History | Annotate | Line # | Download | only in ossaudio
ossaudio.c revision 1.9
      1 /*	$NetBSD: ossaudio.c,v 1.9 1997/05/07 19:24:30 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 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    230 		if (error)
    231 			return error;
    232 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    233 		if (error)
    234 			return error;
    235 		if (idat == 0)
    236 			idat = tmpinfo.buffersize / tmpinfo.blocksize;
    237 		idat = (tmpinfo.buffersize / idat) & -4;
    238 		AUDIO_INITINFO(&tmpinfo);
    239 		tmpinfo.blocksize = idat;
    240 		error = ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    241 		if (error)
    242 			return error;
    243 		idat = tmpinfo.buffersize / tmpinfo.blocksize;
    244 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    245 		if (error)
    246 			return error;
    247 		break;
    248 	case OSS_SNDCTL_DSP_SETFRAGMENT:
    249 		AUDIO_INITINFO(&tmpinfo);
    250 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    251 		if (error)
    252 			return error;
    253 		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17)
    254 			return EINVAL;
    255 		tmpinfo.blocksize = 1 << (idat & 0xffff);
    256 		tmpinfo.hiwat = (idat >> 16) & 0xffff;
    257 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    258 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    259 		if (error)
    260 			return error;
    261 		idat = tmpinfo.blocksize;
    262 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    263 		if (error)
    264 			return error;
    265 		break;
    266 	case OSS_SNDCTL_DSP_GETFMTS:
    267 		idat = OSS_AFMT_MU_LAW | OSS_AFMT_U8 | OSS_AFMT_S16_LE;
    268 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    269 		if (error)
    270 			return error;
    271 		break;
    272 	case OSS_SNDCTL_DSP_GETOSPACE:
    273 	case OSS_SNDCTL_DSP_GETISPACE:
    274 	case OSS_SNDCTL_DSP_NONBLOCK:
    275 		return EINVAL; /* XXX unimplemented */
    276 	case OSS_SNDCTL_DSP_GETCAPS:
    277 		idat = 0; /* XXX full duplex info should be set */
    278 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    279 		if (error)
    280 			return error;
    281 		break;
    282 	case OSS_SNDCTL_DSP_GETTRIGGER:
    283 	case OSS_SNDCTL_DSP_SETTRIGGER:
    284 	case OSS_SNDCTL_DSP_GETIPTR:
    285 	case OSS_SNDCTL_DSP_GETOPTR:
    286 	case OSS_SNDCTL_DSP_MAPINBUF:
    287 	case OSS_SNDCTL_DSP_MAPOUTBUF:
    288 	case OSS_SNDCTL_DSP_SETSYNCRO:
    289 	case OSS_SNDCTL_DSP_SETDUPLEX:
    290 	case OSS_SNDCTL_DSP_PROFILE:
    291 		return EINVAL; /* XXX unimplemented */
    292 	default:
    293 		return EINVAL;
    294 	}
    295 
    296 	return 0;
    297 }
    298 
    299 /* If the NetBSD mixer device should have more than 32 devices
    300  * some will not be available to Linux */
    301 #define NETBSD_MAXDEVS 32
    302 struct audiodevinfo {
    303 	int done;
    304 	dev_t dev;
    305 	int16_t devmap[OSS_SOUND_MIXER_NRDEVICES],
    306 	        rdevmap[NETBSD_MAXDEVS];
    307         u_long devmask, recmask, stereomask;
    308 	u_long caps, source;
    309 };
    310 
    311 /*
    312  * Collect the audio device information to allow faster
    313  * emulation of the Linux mixer ioctls.  Cache the information
    314  * to eliminate the overhead of repeating all the ioctls needed
    315  * to collect the information.
    316  */
    317 static struct audiodevinfo *
    318 getdevinfo(fp, p)
    319 	struct file *fp;
    320 	struct proc *p;
    321 {
    322 	mixer_devinfo_t mi;
    323 	int i;
    324 	static struct {
    325 		char *name;
    326 		int code;
    327 	} *dp, devs[] = {
    328 		{ AudioNmicrophone,	OSS_SOUND_MIXER_MIC },
    329 		{ AudioNline,		OSS_SOUND_MIXER_LINE },
    330 		{ AudioNcd,		OSS_SOUND_MIXER_CD },
    331 		{ AudioNdac,		OSS_SOUND_MIXER_PCM },
    332 		{ AudioNrecord,		OSS_SOUND_MIXER_IMIX },
    333 		{ AudioNvolume,		OSS_SOUND_MIXER_VOLUME },
    334 		{ AudioNtreble,		OSS_SOUND_MIXER_TREBLE },
    335 		{ AudioNbass,		OSS_SOUND_MIXER_BASS },
    336 		{ AudioNspeaker,	OSS_SOUND_MIXER_SPEAKER },
    337 /*		{ AudioNheadphone,	?? },*/
    338 		{ AudioNoutput,		OSS_SOUND_MIXER_OGAIN },
    339 		{ AudioNinput,		OSS_SOUND_MIXER_IGAIN },
    340 /*		{ AudioNmaster,		OSS_SOUND_MIXER_MASTER },*/
    341 /*		{ AudioNstereo,		?? },*/
    342 /*		{ AudioNmono,		?? },*/
    343 /*		{ AudioNsource,		?? },*/
    344 		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
    345 /*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
    346 /*		{ AudioNmidi,		?? },*/
    347 /*		{ AudioNmixerout,	?? },*/
    348 		{ 0, -1 }
    349 	};
    350 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) =
    351 	    fp->f_ops->fo_ioctl;
    352 	struct vnode *vp;
    353 	struct vattr va;
    354 	static struct audiodevinfo devcache = { 0 };
    355 	struct audiodevinfo *di = &devcache;
    356 
    357 	/* Figure out what device it is so we can check if the
    358 	 * cached data is valid.
    359 	 */
    360 	vp = (struct vnode *)fp->f_data;
    361 	if (vp->v_type != VCHR)
    362 		return 0;
    363 	if (VOP_GETATTR(vp, &va, p->p_ucred, p))
    364 		return 0;
    365 	if (di->done && di->dev == va.va_rdev)
    366 		return di;
    367 
    368 	di->done = 1;
    369 	di->dev = va.va_rdev;
    370 	di->devmask = 0;
    371 	di->recmask = 0;
    372 	di->stereomask = 0;
    373 	di->source = -1;
    374 	di->caps = 0;
    375 	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    376 		di->devmap[i] = -1;
    377 	for(i = 0; i < NETBSD_MAXDEVS; i++)
    378 		di->rdevmap[i] = -1;
    379 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
    380 		mi.index = i;
    381 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
    382 			break;
    383 		switch(mi.type) {
    384 		case AUDIO_MIXER_VALUE:
    385 			for(dp = devs; dp->name; dp++)
    386 		    		if (strcmp(dp->name, mi.label.name) == 0)
    387 					break;
    388 			if (dp->code >= 0) {
    389 				di->devmap[dp->code] = i;
    390 				di->rdevmap[i] = dp->code;
    391 				di->devmask |= 1 << dp->code;
    392 				if (mi.un.v.num_channels == 2)
    393 					di->stereomask |= 1 << dp->code;
    394 			}
    395 			break;
    396 		case AUDIO_MIXER_ENUM:
    397 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    398 				int j;
    399 				di->source = i;
    400 				for(j = 0; j < mi.un.e.num_mem; j++) {
    401 					di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord];
    402 				}
    403 				di->caps = OSS_SOUND_CAP_EXCL_INPUT;
    404 			}
    405 			break;
    406 		case AUDIO_MIXER_SET:
    407 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    408 				int j;
    409 				di->source = i;
    410 				for(j = 0; j < mi.un.s.num_mem; j++) {
    411 					int k, mask = mi.un.s.member[j].mask;
    412 					if (mask) {
    413 						for(k = 0; !(mask & (1<<k)); k++)
    414 							;
    415 						di->recmask |= 1 << di->rdevmap[k];
    416 					}
    417 				}
    418 			}
    419 			break;
    420 		}
    421 	}
    422 	return di;
    423 }
    424 
    425 int
    426 oss_ioctl_mixer(p, uap, retval)
    427 	register struct proc *p;
    428 	register struct oss_sys_ioctl_args /* {
    429 		syscallarg(int) fd;
    430 		syscallarg(u_long) com;
    431 		syscallarg(caddr_t) data;
    432 	} */ *uap;
    433 	register_t *retval;
    434 {
    435 	struct file *fp;
    436 	struct filedesc *fdp;
    437 	u_long com;
    438 	struct audiodevinfo *di;
    439 	mixer_ctrl_t mc;
    440 	int idat;
    441 	int i;
    442 	int error;
    443 	int l, r, n;
    444 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
    445 
    446 	fdp = p->p_fd;
    447 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
    448 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
    449 		return (EBADF);
    450 
    451 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
    452 		return (EBADF);
    453 
    454 	com = SCARG(uap, com);
    455 	retval[0] = 0;
    456 
    457 	di = getdevinfo(fp, p);
    458 	if (di == 0)
    459 		return EINVAL;
    460 
    461 	ioctlf = fp->f_ops->fo_ioctl;
    462 	switch (com) {
    463 	case OSS_SOUND_MIXER_READ_RECSRC:
    464 		if (di->source == -1)
    465 			return EINVAL;
    466 		mc.dev = di->source;
    467 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
    468 			mc.type = AUDIO_MIXER_ENUM;
    469 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    470 			if (error)
    471 				return error;
    472 			idat = 1 << di->rdevmap[mc.un.ord];
    473 		} else {
    474 			int k;
    475 			unsigned int mask;
    476 			mc.type = AUDIO_MIXER_SET;
    477 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    478 			if (error)
    479 				return error;
    480 			idat = 0;
    481 			for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++)
    482 				idat |= 1 << di->rdevmap[k];
    483 		}
    484 		break;
    485 	case OSS_SOUND_MIXER_READ_DEVMASK:
    486 		idat = di->devmask;
    487 		break;
    488 	case OSS_SOUND_MIXER_READ_RECMASK:
    489 		idat = di->recmask;
    490 		break;
    491 	case OSS_SOUND_MIXER_READ_STEREODEVS:
    492 		idat = di->stereomask;
    493 		break;
    494 	case OSS_SOUND_MIXER_READ_CAPS:
    495 		idat = di->caps;
    496 		break;
    497 	case OSS_SOUND_MIXER_WRITE_RECSRC:
    498 		if (di->source == -1)
    499 			return EINVAL;
    500 		mc.dev = di->source;
    501 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    502 		if (error)
    503 			return error;
    504 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
    505 			mc.type = AUDIO_MIXER_ENUM;
    506 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    507 				if (idat & (1 << i))
    508 					break;
    509 			if (i >= OSS_SOUND_MIXER_NRDEVICES ||
    510 			    di->devmap[i] == -1)
    511 				return EINVAL;
    512 			mc.un.ord = di->devmap[i];
    513 		} else {
    514 			mc.type = AUDIO_MIXER_SET;
    515 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    516 				if (idat & (1 << i)) {
    517 					if (di->devmap[i] == -1)
    518 						return EINVAL;
    519 					mc.un.mask |= 1 << di->devmap[i];
    520 				}
    521 		}
    522 		return ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
    523 	default:
    524 		if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com &&
    525 		    com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) {
    526 			n = OSS_GET_DEV(com);
    527 			if (di->devmap[n] == -1)
    528 				return EINVAL;
    529 			mc.dev = di->devmap[n];
    530 			mc.type = AUDIO_MIXER_VALUE;
    531 		    doread:
    532 			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
    533 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    534 			if (error)
    535 				return error;
    536 			if (mc.type != AUDIO_MIXER_VALUE)
    537 				return EINVAL;
    538 			if (mc.un.value.num_channels != 2) {
    539 				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
    540 			} else {
    541 				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    542 				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    543 			}
    544 			l = l * 100 / 255;
    545 			r = r * 100 / 255;
    546 			idat = l | (r << 8);
    547 			break;
    548 		} else if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
    549 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
    550 			n = OSS_GET_DEV(com);
    551 			if (di->devmap[n] == -1)
    552 				return EINVAL;
    553 			error = copyin(SCARG(uap, data), &idat, sizeof idat);
    554 			if (error)
    555 				return error;
    556 			l = (idat & 0xff) * 255 / 100;
    557 			r = (idat >> 8) * 255 / 100;
    558 			mc.dev = di->devmap[n];
    559 			mc.type = AUDIO_MIXER_VALUE;
    560 			if (di->stereomask & (1<<n)) {
    561 				mc.un.value.num_channels = 2;
    562 				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
    563 				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
    564 			} else {
    565 				mc.un.value.num_channels = 1;
    566 				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
    567 			}
    568 			error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
    569 			if (error)
    570 				return error;
    571 			goto doread;
    572 		} else
    573 			return EINVAL;
    574 	}
    575 	return copyout(&idat, SCARG(uap, data), sizeof idat);
    576 }
    577 
    578 /* XXX hook for sequencer emulation */
    579 int
    580 oss_ioctl_sequencer(p, uap, retval)
    581 	register struct proc *p;
    582 	register struct oss_sys_ioctl_args /* {
    583 		syscallarg(int) fd;
    584 		syscallarg(u_long) com;
    585 		syscallarg(caddr_t) data;
    586 	} */ *uap;
    587 	register_t *retval;
    588 {
    589 	register struct file *fp;
    590 	register struct filedesc *fdp;
    591 #if 0
    592 	u_long com;
    593 	int idat;
    594 	int error;
    595 #endif
    596 
    597 	fdp = p->p_fd;
    598 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
    599 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
    600 		return (EBADF);
    601 
    602 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
    603 		return (EBADF);
    604 
    605 #if 0
    606 	com = SCARG(uap, com);
    607 #endif
    608 	retval[0] = 0;
    609 
    610 	return EINVAL;
    611 }
    612