Home | History | Annotate | Line # | Download | only in ossaudio
ossaudio.c revision 1.10
      1 /*	$NetBSD: ossaudio.c,v 1.10 1997/05/19 17:29:12 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_SPEAKER },
    341 /*		{ AudioNstereo,		?? },*/
    342 /*		{ AudioNmono,		?? },*/
    343 		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
    344 /*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
    345 		{ AudioNmidi,		OSS_SOUND_MIXER_SYNTH },
    346 /*		{ AudioNmixerout,	?? },*/
    347 		{ 0, -1 }
    348 	};
    349 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) =
    350 	    fp->f_ops->fo_ioctl;
    351 	struct vnode *vp;
    352 	struct vattr va;
    353 	static struct audiodevinfo devcache = { 0 };
    354 	struct audiodevinfo *di = &devcache;
    355 
    356 	/* Figure out what device it is so we can check if the
    357 	 * cached data is valid.
    358 	 */
    359 	vp = (struct vnode *)fp->f_data;
    360 	if (vp->v_type != VCHR)
    361 		return 0;
    362 	if (VOP_GETATTR(vp, &va, p->p_ucred, p))
    363 		return 0;
    364 	if (di->done && di->dev == va.va_rdev)
    365 		return di;
    366 
    367 	di->done = 1;
    368 	di->dev = va.va_rdev;
    369 	di->devmask = 0;
    370 	di->recmask = 0;
    371 	di->stereomask = 0;
    372 	di->source = -1;
    373 	di->caps = 0;
    374 	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    375 		di->devmap[i] = -1;
    376 	for(i = 0; i < NETBSD_MAXDEVS; i++)
    377 		di->rdevmap[i] = -1;
    378 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
    379 		mi.index = i;
    380 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
    381 			break;
    382 		switch(mi.type) {
    383 		case AUDIO_MIXER_VALUE:
    384 			for(dp = devs; dp->name; dp++)
    385 		    		if (strcmp(dp->name, mi.label.name) == 0)
    386 					break;
    387 			if (dp->code >= 0) {
    388 				di->devmap[dp->code] = i;
    389 				di->rdevmap[i] = dp->code;
    390 				di->devmask |= 1 << dp->code;
    391 				if (mi.un.v.num_channels == 2)
    392 					di->stereomask |= 1 << dp->code;
    393 			}
    394 			break;
    395 		case AUDIO_MIXER_ENUM:
    396 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    397 				int j;
    398 				di->source = i;
    399 				for(j = 0; j < mi.un.e.num_mem; j++)
    400 					di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord];
    401 				di->caps = OSS_SOUND_CAP_EXCL_INPUT;
    402 			}
    403 			break;
    404 		case AUDIO_MIXER_SET:
    405 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    406 				int j;
    407 				di->source = i;
    408 				for(j = 0; j < mi.un.s.num_mem; j++) {
    409 					int k, mask = mi.un.s.member[j].mask;
    410 					if (mask) {
    411 						for(k = 0; !(mask & 1); mask >>= 1, k++)
    412 							;
    413 						di->recmask |= 1 << di->rdevmap[k];
    414 					}
    415 				}
    416 			}
    417 			break;
    418 		}
    419 	}
    420 	return di;
    421 }
    422 
    423 int
    424 oss_ioctl_mixer(p, uap, retval)
    425 	register struct proc *p;
    426 	register struct oss_sys_ioctl_args /* {
    427 		syscallarg(int) fd;
    428 		syscallarg(u_long) com;
    429 		syscallarg(caddr_t) data;
    430 	} */ *uap;
    431 	register_t *retval;
    432 {
    433 	struct file *fp;
    434 	struct filedesc *fdp;
    435 	u_long com;
    436 	struct audiodevinfo *di;
    437 	mixer_ctrl_t mc;
    438 	int idat;
    439 	int i;
    440 	int error;
    441 	int l, r, n;
    442 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
    443 
    444 	fdp = p->p_fd;
    445 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
    446 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
    447 		return (EBADF);
    448 
    449 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
    450 		return (EBADF);
    451 
    452 	com = SCARG(uap, com);
    453 	retval[0] = 0;
    454 
    455 	di = getdevinfo(fp, p);
    456 	if (di == 0)
    457 		return EINVAL;
    458 
    459 	ioctlf = fp->f_ops->fo_ioctl;
    460 	switch (com) {
    461 	case OSS_SOUND_MIXER_READ_RECSRC:
    462 		if (di->source == -1)
    463 			return EINVAL;
    464 		mc.dev = di->source;
    465 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
    466 			mc.type = AUDIO_MIXER_ENUM;
    467 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    468 			if (error)
    469 				return error;
    470 			idat = 1 << di->rdevmap[mc.un.ord];
    471 		} else {
    472 			int k;
    473 			unsigned int mask;
    474 			mc.type = AUDIO_MIXER_SET;
    475 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    476 			if (error)
    477 				return error;
    478 			idat = 0;
    479 			for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++)
    480 				if (mask & 1)
    481 					idat |= 1 << di->rdevmap[k];
    482 		}
    483 		break;
    484 	case OSS_SOUND_MIXER_READ_DEVMASK:
    485 		idat = di->devmask;
    486 		break;
    487 	case OSS_SOUND_MIXER_READ_RECMASK:
    488 		idat = di->recmask;
    489 		break;
    490 	case OSS_SOUND_MIXER_READ_STEREODEVS:
    491 		idat = di->stereomask;
    492 		break;
    493 	case OSS_SOUND_MIXER_READ_CAPS:
    494 		idat = di->caps;
    495 		break;
    496 	case OSS_SOUND_MIXER_WRITE_RECSRC:
    497 		if (di->source == -1)
    498 			return EINVAL;
    499 		mc.dev = di->source;
    500 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    501 		if (error)
    502 			return error;
    503 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
    504 			mc.type = AUDIO_MIXER_ENUM;
    505 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    506 				if (idat & (1 << i))
    507 					break;
    508 			if (i >= OSS_SOUND_MIXER_NRDEVICES ||
    509 			    di->devmap[i] == -1)
    510 				return EINVAL;
    511 			mc.un.ord = di->devmap[i];
    512 		} else {
    513 			mc.type = AUDIO_MIXER_SET;
    514 			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) {
    515 				if (idat & (1 << i)) {
    516 					if (di->devmap[i] == -1)
    517 						return EINVAL;
    518 					mc.un.mask |= 1 << di->devmap[i];
    519 				}
    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