Home | History | Annotate | Line # | Download | only in ossaudio
ossaudio.c revision 1.6
      1 /*	$NetBSD: ossaudio.c,v 1.6 1997/04/04 15:36:01 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_LINEAR;
    128 			break;
    129 		case OSS_AFMT_S16_LE:
    130 			tmpinfo.play.precision =
    131 			tmpinfo.record.precision = 16;
    132 			tmpinfo.play.encoding =
    133 			tmpinfo.record.encoding = AUDIO_ENCODING_LINEAR;
    134 			break;
    135 		default:
    136 			return EINVAL;
    137 		}
    138 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    139 		/* fall into ... */
    140 	case OSS_SOUND_PCM_READ_BITS:
    141 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    142 		if (error)
    143 			return error;
    144 		switch (tmpinfo.play.encoding) {
    145 		case AUDIO_ENCODING_ULAW:
    146 			idat = OSS_AFMT_MU_LAW;
    147 			break;
    148 		case AUDIO_ENCODING_ALAW:
    149 			idat = OSS_AFMT_A_LAW;
    150 			break;
    151 		case AUDIO_ENCODING_PCM16:
    152 			idat = OSS_AFMT_S16_LE;
    153 			break;
    154 		case AUDIO_ENCODING_PCM8:
    155 			idat = OSS_AFMT_U8;
    156 			break;
    157 		case AUDIO_ENCODING_ADPCM:
    158 			idat = OSS_AFMT_IMA_ADPCM;
    159 			break;
    160 		}
    161 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    162 		if (error)
    163 			return error;
    164 		break;
    165 	case OSS_SNDCTL_DSP_CHANNELS:
    166 		AUDIO_INITINFO(&tmpinfo);
    167 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    168 		if (error)
    169 			return error;
    170 		tmpinfo.play.channels =
    171 		tmpinfo.record.channels = idat;
    172 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    173 		/* fall into ... */
    174 	case OSS_SOUND_PCM_READ_CHANNELS:
    175 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    176 		if (error)
    177 			return error;
    178 		idat = tmpinfo.play.channels;
    179 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    180 		if (error)
    181 			return error;
    182 		break;
    183 	case OSS_SOUND_PCM_WRITE_FILTER:
    184 	case OSS_SOUND_PCM_READ_FILTER:
    185 		return EINVAL; /* XXX unimplemented */
    186 	case OSS_SNDCTL_DSP_SUBDIVIDE:
    187 		return EINVAL; /* XXX unimplemented */
    188 	case OSS_SNDCTL_DSP_SETFRAGMENT:
    189 		AUDIO_INITINFO(&tmpinfo);
    190 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    191 		if (error)
    192 			return error;
    193 		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17)
    194 			return EINVAL;
    195 		tmpinfo.blocksize = 1 << (idat & 0xffff);
    196 		tmpinfo.hiwat = (idat >> 16) & 0xffff;
    197 		(void) ioctlf(fp, AUDIO_SETINFO, (caddr_t)&tmpinfo, p);
    198 		error = ioctlf(fp, AUDIO_GETINFO, (caddr_t)&tmpinfo, p);
    199 		if (error)
    200 			return error;
    201 		idat = tmpinfo.blocksize;
    202 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    203 		if (error)
    204 			return error;
    205 		break;
    206 	case OSS_SNDCTL_DSP_GETFMTS:
    207 		idat = OSS_AFMT_MU_LAW | OSS_AFMT_U8 | OSS_AFMT_S16_LE;
    208 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    209 		if (error)
    210 			return error;
    211 		break;
    212 	case OSS_SNDCTL_DSP_GETOSPACE:
    213 	case OSS_SNDCTL_DSP_GETISPACE:
    214 	case OSS_SNDCTL_DSP_NONBLOCK:
    215 		return EINVAL; /* XXX unimplemented */
    216 	case OSS_SNDCTL_DSP_GETCAPS:
    217 		idat = 0; /* XXX full duplex info should be set */
    218 		error = copyout(&idat, SCARG(uap, data), sizeof idat);
    219 		if (error)
    220 			return error;
    221 		break;
    222 	case OSS_SNDCTL_DSP_GETTRIGGER:
    223 	case OSS_SNDCTL_DSP_SETTRIGGER:
    224 	case OSS_SNDCTL_DSP_GETIPTR:
    225 	case OSS_SNDCTL_DSP_GETOPTR:
    226 	case OSS_SNDCTL_DSP_MAPINBUF:
    227 	case OSS_SNDCTL_DSP_MAPOUTBUF:
    228 	case OSS_SNDCTL_DSP_SETSYNCRO:
    229 	case OSS_SNDCTL_DSP_SETDUPLEX:
    230 	case OSS_SNDCTL_DSP_PROFILE:
    231 		return EINVAL; /* XXX unimplemented */
    232 	default:
    233 		return EINVAL;
    234 	}
    235 
    236 	return 0;
    237 }
    238 
    239 /* If the NetBSD mixer device should have more than 32 devices
    240  * some will not be available to Linux */
    241 #define NETBSD_MAXDEVS 32
    242 struct audiodevinfo {
    243 	int done;
    244 	dev_t dev;
    245 	u_int16_t devmap[OSS_SOUND_MIXER_NRDEVICES],
    246 	          rdevmap[NETBSD_MAXDEVS];
    247         u_long devmask, recmask, stereomask;
    248 	u_long caps, source;
    249 };
    250 
    251 /*
    252  * Collect the audio device information to allow faster
    253  * emulation of the Linux mixer ioctls.  Cache the information
    254  * to eliminate the overhead of repeating all the ioctls needed
    255  * to collect the information.
    256  */
    257 static struct audiodevinfo *
    258 getdevinfo(fp, p)
    259 	struct file *fp;
    260 	struct proc *p;
    261 {
    262 	mixer_devinfo_t mi;
    263 	int i;
    264 	static struct {
    265 		char *name;
    266 		int code;
    267 	} *dp, devs[] = {
    268 		{ AudioNmicrophone,	OSS_SOUND_MIXER_MIC },
    269 		{ AudioNline,		OSS_SOUND_MIXER_LINE },
    270 		{ AudioNcd,		OSS_SOUND_MIXER_CD },
    271 		{ AudioNdac,		OSS_SOUND_MIXER_PCM },
    272 		{ AudioNrecord,		OSS_SOUND_MIXER_IMIX },
    273 		{ AudioNvolume,		OSS_SOUND_MIXER_VOLUME },
    274 		{ AudioNtreble,		OSS_SOUND_MIXER_TREBLE },
    275 		{ AudioNbass,		OSS_SOUND_MIXER_BASS },
    276 		{ AudioNspeaker,	OSS_SOUND_MIXER_SPEAKER },
    277 /*		{ AudioNheadphone,	?? },*/
    278 		{ AudioNoutput,		OSS_SOUND_MIXER_OGAIN },
    279 		{ AudioNinput,		OSS_SOUND_MIXER_IGAIN },
    280 /*		{ AudioNmaster,		OSS_SOUND_MIXER_MASTER },*/
    281 /*		{ AudioNstereo,		?? },*/
    282 /*		{ AudioNmono,		?? },*/
    283 /*		{ AudioNsource,		?? },*/
    284 		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
    285 /*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
    286 /*		{ AudioNmidi,		?? },*/
    287 /*		{ AudioNmixerout,	?? },*/
    288 		{ 0, -1 }
    289 	};
    290 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *)) =
    291 	    fp->f_ops->fo_ioctl;
    292 	struct vnode *vp;
    293 	struct vattr va;
    294 	static struct audiodevinfo devcache = { 0 };
    295 	struct audiodevinfo *di = &devcache;
    296 
    297 	/* Figure out what device it is so we can check if the
    298 	 * cached data is valid.
    299 	 */
    300 	vp = (struct vnode *)fp->f_data;
    301 	if (vp->v_type != VCHR)
    302 		return 0;
    303 	if (VOP_GETATTR(vp, &va, p->p_ucred, p))
    304 		return 0;
    305 	if (di->done && di->dev == va.va_rdev)
    306 		return di;
    307 
    308 	di->done = 1;
    309 	di->dev = va.va_rdev;
    310 	di->devmask = 0;
    311 	di->recmask = 0;
    312 	di->stereomask = 0;
    313 	di->source = -1;
    314 	di->caps = 0;
    315 	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    316 		di->devmap[i] = -1;
    317 	for(i = 0; i < NETBSD_MAXDEVS; i++)
    318 		di->rdevmap[i] = -1;
    319 	for(i = 0; i < NETBSD_MAXDEVS; i++) {
    320 		mi.index = i;
    321 		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, (caddr_t)&mi, p) < 0)
    322 			break;
    323 		switch(mi.type) {
    324 		case AUDIO_MIXER_VALUE:
    325 			for(dp = devs; dp->name; dp++)
    326 		    		if (strcmp(dp->name, mi.label.name) == 0)
    327 					break;
    328 			if (dp->code >= 0) {
    329 				di->devmap[dp->code] = i;
    330 				di->rdevmap[i] = dp->code;
    331 				di->devmask |= 1 << dp->code;
    332 				if (mi.un.v.num_channels == 2)
    333 					di->stereomask |= 1 << dp->code;
    334 			}
    335 			break;
    336 		case AUDIO_MIXER_ENUM:
    337 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    338 				int j;
    339 				di->source = i;
    340 				for(j = 0; j < mi.un.e.num_mem; j++) {
    341 					di->recmask |= 1 << di->rdevmap[mi.un.e.member[j].ord];
    342 				}
    343 				di->caps = OSS_SOUND_CAP_EXCL_INPUT;
    344 			}
    345 			break;
    346 		case AUDIO_MIXER_SET:
    347 			if (strcmp(mi.label.name, AudioNsource) == 0) {
    348 				int j;
    349 				di->source = i;
    350 				for(j = 0; j < mi.un.s.num_mem; j++) {
    351 					int k, mask = mi.un.s.member[j].mask;
    352 					if (mask) {
    353 						for(k = 0; !(mask & (1<<k)); k++)
    354 							;
    355 						di->recmask |= 1 << di->rdevmap[k];
    356 					}
    357 				}
    358 			}
    359 			break;
    360 		}
    361 	}
    362 	return di;
    363 }
    364 
    365 int
    366 oss_ioctl_mixer(p, uap, retval)
    367 	register struct proc *p;
    368 	register struct oss_sys_ioctl_args /* {
    369 		syscallarg(int) fd;
    370 		syscallarg(u_long) com;
    371 		syscallarg(caddr_t) data;
    372 	} */ *uap;
    373 	register_t *retval;
    374 {
    375 	struct file *fp;
    376 	struct filedesc *fdp;
    377 	u_long com;
    378 	struct audiodevinfo *di;
    379 	mixer_ctrl_t mc;
    380 	int idat;
    381 	int i;
    382 	int error;
    383 	int l, r, n;
    384 	int (*ioctlf) __P((struct file *, u_long, caddr_t, struct proc *));
    385 
    386 	fdp = p->p_fd;
    387 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
    388 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
    389 		return (EBADF);
    390 
    391 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
    392 		return (EBADF);
    393 
    394 	com = SCARG(uap, com);
    395 	retval[0] = 0;
    396 
    397 	di = getdevinfo(fp, p);
    398 	if (di == 0)
    399 		return EINVAL;
    400 
    401 	ioctlf = fp->f_ops->fo_ioctl;
    402 	switch (com) {
    403 	case OSS_SOUND_MIXER_READ_RECSRC:
    404 		if (di->source == -1)
    405 			return EINVAL;
    406 		mc.dev = di->source;
    407 		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
    408 			mc.type = AUDIO_MIXER_ENUM;
    409 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    410 			if (error)
    411 				return error;
    412 			idat = 1 << di->rdevmap[mc.un.ord];
    413 		} else {
    414 			int k;
    415 			unsigned int mask;
    416 			mc.type = AUDIO_MIXER_SET;
    417 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    418 			if (error)
    419 				return error;
    420 			idat = 0;
    421 			for(mask = mc.un.mask, k = 0; mask; mask >>= 1, k++)
    422 				idat |= 1 << di->rdevmap[k];
    423 		}
    424 		break;
    425 	case OSS_SOUND_MIXER_READ_DEVMASK:
    426 		idat = di->devmask;
    427 		break;
    428 	case OSS_SOUND_MIXER_READ_RECMASK:
    429 		idat = di->recmask;
    430 		break;
    431 	case OSS_SOUND_MIXER_READ_STEREODEVS:
    432 		idat = di->stereomask;
    433 		break;
    434 	case OSS_SOUND_MIXER_READ_CAPS:
    435 		idat = di->caps;
    436 		break;
    437 	case OSS_SOUND_MIXER_WRITE_RECSRC:
    438 		if (di->source == -1)
    439 			return EINVAL;
    440 		mc.dev = di->source;
    441 		mc.type = AUDIO_MIXER_ENUM;
    442 		error = copyin(SCARG(uap, data), &idat, sizeof idat);
    443 		if (error)
    444 			return error;
    445 		for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
    446 			if (idat & (1 << i))
    447 				mc.un.ord = di->devmap[i];
    448 		return ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    449 	default:
    450 		if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com &&
    451 		    com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) {
    452 			n = OSS_GET_DEV(com);
    453 			mc.dev = di->devmap[n];
    454 			mc.type = AUDIO_MIXER_VALUE;
    455 		    doread:
    456 			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
    457 			error = ioctlf(fp, AUDIO_MIXER_READ, (caddr_t)&mc, p);
    458 			if (error)
    459 				return error;
    460 			if (mc.type != AUDIO_MIXER_VALUE)
    461 				return EINVAL;
    462 			if (mc.un.value.num_channels != 2) {
    463 				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
    464 			} else {
    465 				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
    466 				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
    467 			}
    468 			l = l * 100 / 255;
    469 			r = r * 100 / 255;
    470 			idat = l | (r << 8);
    471 			break;
    472 		} else if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
    473 			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
    474 			n = OSS_GET_DEV(com);
    475 			error = copyin(SCARG(uap, data), &idat, sizeof idat);
    476 			if (error)
    477 				return error;
    478 			l = (idat & 0xff) * 255 / 100;
    479 			r = (idat >> 8) * 255 / 100;
    480 			mc.dev = di->devmap[n];
    481 			mc.type = AUDIO_MIXER_VALUE;
    482 			if (di->stereomask & (1<<n)) {
    483 				mc.un.value.num_channels = 2;
    484 				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
    485 				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
    486 			} else {
    487 				mc.un.value.num_channels = 1;
    488 				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
    489 			}
    490 			error = ioctlf(fp, AUDIO_MIXER_WRITE, (caddr_t)&mc, p);
    491 			if (error)
    492 				return error;
    493 			goto doread;
    494 		} else
    495 			return EINVAL;
    496 	}
    497 	return copyout(&idat, SCARG(uap, data), sizeof idat);
    498 }
    499 
    500 /* XXX hook for sequencer emulation */
    501 int
    502 oss_ioctl_sequencer(p, uap, retval)
    503 	register struct proc *p;
    504 	register struct oss_sys_ioctl_args /* {
    505 		syscallarg(int) fd;
    506 		syscallarg(u_long) com;
    507 		syscallarg(caddr_t) data;
    508 	} */ *uap;
    509 	register_t *retval;
    510 {
    511 	register struct file *fp;
    512 	register struct filedesc *fdp;
    513 #if 0
    514 	u_long com;
    515 	int idat;
    516 	int error;
    517 #endif
    518 
    519 	fdp = p->p_fd;
    520 	if ((u_int)SCARG(uap, fd) >= fdp->fd_nfiles ||
    521 	    (fp = fdp->fd_ofiles[SCARG(uap, fd)]) == NULL)
    522 		return (EBADF);
    523 
    524 	if ((fp->f_flag & (FREAD | FWRITE)) == 0)
    525 		return (EBADF);
    526 
    527 #if 0
    528 	com = SCARG(uap, com);
    529 #endif
    530 	retval[0] = 0;
    531 
    532 	return EINVAL;
    533 }
    534