Example #1
0
int
linux_ioctl_fdio(struct proc *p, struct linux_sys_ioctl_args *uap,
		 register_t *retval)
{
	struct filedesc *fdp;
	struct file *fp;
	int error;
	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *);
	u_long com;
	struct fd_type fparams;
	struct linux_floppy_struct lflop;
	struct linux_floppy_drive_struct ldrive;

	com = (u_long)SCARG(uap, data);

	fdp = p->p_fd;
	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
		return (EBADF);

	FREF(fp);
	com = SCARG(uap, com);
	ioctlf = fp->f_ops->fo_ioctl;

	retval[0] = error = 0;

	switch (com) {
	case LINUX_FDMSGON:
	case LINUX_FDMSGOFF:
	case LINUX_FDTWADDLE:
	case LINUX_FDCLRPRM:
		/* whatever you say */
		break;
	case LINUX_FDPOLLDRVSTAT:
		/*
		 * Just fill in some innocent defaults.
		 */
		memset(&ldrive, 0, sizeof ldrive);
		ldrive.fd_ref = 1;
		ldrive.maxblock = 2;
		ldrive.maxtrack = ldrive.track = 1;
		ldrive.flags = LINUX_FD_DISK_WRITABLE;
		error = copyout(&ldrive, SCARG(uap, data), sizeof ldrive);
		break;
	case LINUX_FDGETPRM:
		error = ioctlf(fp, FD_GTYPE, (caddr_t)&fparams, p);
		if (error != 0)
			break;
		lflop.size = fparams.heads * fparams.sectrac * fparams.tracks;
		lflop.sect = fparams.sectrac;
		lflop.head = fparams.heads;
		lflop.track = fparams.tracks;
		lflop.stretch = fparams.step == 2 ? 1 : 0;
		lflop.spec1 = fparams.steprate;
		lflop.gap = fparams.gap1;
		lflop.fmt_gap = fparams.gap2;
		lflop.rate = fparams.rate;

		error = copyout(&lflop, SCARG(uap, data), sizeof lflop);
		break;
	case LINUX_FDSETPRM:
		/*
		 * Should use FDIOCSETFORMAT here, iff its interface
		 * is extended.
		 */
	case LINUX_FDDEFPRM:
	case LINUX_FDFMTBEG:
	case LINUX_FDFMTTRK:
	case LINUX_FDFMTEND:
	case LINUX_FDSETEMSGTRESH:
	case LINUX_FDFLUSH:
	case LINUX_FDSETMAXERRS:
	case LINUX_FDGETMAXERRS:
	case LINUX_FDGETDRVTYP:
	case LINUX_FDSETDRVPRM:
	case LINUX_FDGETDRVPRM:
	case LINUX_FDGETDRVSTAT:
	case LINUX_FDRESET:
	case LINUX_FDGETFDCSTAT:
	case LINUX_FDWERRORCLR:
	case LINUX_FDWERRORGET:
	case LINUX_FDRAWCMD:
	case LINUX_FDEJECT:
	default:
		error = EINVAL;
	}

	FRELE(fp, p);
	return 0;
}
Example #2
0
/*
 * We come here in a last attempt to satisfy a Linux ioctl() call
 */
int
linux_machdepioctl(struct lwp *l, const struct linux_sys_ioctl_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
		syscallarg(u_long) com;
		syscallarg(void *) data;
	} */
	struct sys_ioctl_args bia;
	u_long com;
	int error, error1;
#if (NWSDISPLAY > 0)
	struct vt_mode lvt;
	struct kbentry kbe;
#endif
	struct linux_hd_geometry hdg;
	struct linux_hd_big_geometry hdg_big;
	struct biosdisk_info *bip;
	file_t *fp;
	int fd;
	struct disklabel label, *labp;
	struct partinfo partp;
	int (*ioctlf)(struct file *, u_long, void *);
	u_long start, biostotal, realtotal;
	u_char heads, sectors;
	u_int cylinders;
	struct ioctl_pt pt;

	fd = SCARG(uap, fd);
	SCARG(&bia, fd) = fd;
	SCARG(&bia, data) = SCARG(uap, data);
	com = SCARG(uap, com);

	if ((fp = fd_getfile(fd)) == NULL)
		return (EBADF);

	switch (com) {
#if (NWSDISPLAY > 0)
	case LINUX_KDGKBMODE:
		com = KDGKBMODE;
		break;
	case LINUX_KDSKBMODE:
		com = KDSKBMODE;
		if ((unsigned)SCARG(uap, data) == LINUX_K_MEDIUMRAW)
			SCARG(&bia, data) = (void *)K_RAW;
		break;
	case LINUX_KIOCSOUND:
		SCARG(&bia, data) =
		    (void *)(((unsigned long)SCARG(&bia, data)) & 0xffff);
		/* fall through */
	case LINUX_KDMKTONE:
		com = KDMKTONE;
		break;
	case LINUX_KDSETMODE:
		com = KDSETMODE;
		break;
	case LINUX_KDGETMODE:
		/* KD_* values are equal to the wscons numbers */
		com = WSDISPLAYIO_GMODE;
		break;
	case LINUX_KDENABIO:
		com = KDENABIO;
		break;
	case LINUX_KDDISABIO:
		com = KDDISABIO;
		break;
	case LINUX_KDGETLED:
		com = KDGETLED;
		break;
	case LINUX_KDSETLED:
		com = KDSETLED;
		break;
	case LINUX_VT_OPENQRY:
		com = VT_OPENQRY;
		break;
	case LINUX_VT_GETMODE:
		error = fp->f_ops->fo_ioctl(fp, VT_GETMODE, &lvt);
		if (error != 0)
			goto out;
		lvt.relsig = native_to_linux_signo[lvt.relsig];
		lvt.acqsig = native_to_linux_signo[lvt.acqsig];
		lvt.frsig = native_to_linux_signo[lvt.frsig];
		error = copyout(&lvt, SCARG(uap, data), sizeof (lvt));
		goto out;
	case LINUX_VT_SETMODE:
		error = copyin(SCARG(uap, data), &lvt, sizeof (lvt));
		if (error != 0)
			goto out;
		lvt.relsig = linux_to_native_signo[lvt.relsig];
		lvt.acqsig = linux_to_native_signo[lvt.acqsig];
		lvt.frsig = linux_to_native_signo[lvt.frsig];
		error = fp->f_ops->fo_ioctl(fp, VT_SETMODE, &lvt);
		goto out;
	case LINUX_VT_DISALLOCATE:
		/* XXX should use WSDISPLAYIO_DELSCREEN */
		error = 0;
		goto out;
	case LINUX_VT_RELDISP:
		com = VT_RELDISP;
		break;
	case LINUX_VT_ACTIVATE:
		com = VT_ACTIVATE;
		break;
	case LINUX_VT_WAITACTIVE:
		com = VT_WAITACTIVE;
		break;
	case LINUX_VT_GETSTATE:
		com = VT_GETSTATE;
		break;
	case LINUX_KDGKBTYPE:
	    {
		static const u_int8_t kb101 = KB_101;

		/* This is what Linux does. */
		error = copyout(&kb101, SCARG(uap, data), 1);
		goto out;
	    }
	case LINUX_KDGKBENT:
		/*
		 * The Linux KDGKBENT ioctl is different from the
		 * SYSV original. So we handle it in machdep code.
		 * XXX We should use keyboard mapping information
		 * from wsdisplay, but this would be expensive.
		 */
		if ((error = copyin(SCARG(uap, data), &kbe,
				    sizeof(struct kbentry))))
			goto out;
		if (kbe.kb_table >= sizeof(linux_keytabs) / sizeof(u_short *)
		    || kbe.kb_index >= NR_KEYS) {
			error = EINVAL;
			goto out;
		}
		kbe.kb_value = linux_keytabs[kbe.kb_table][kbe.kb_index];
		error = copyout(&kbe, SCARG(uap, data),
				sizeof(struct kbentry));
		goto out;
#endif
	case LINUX_HDIO_GETGEO:
	case LINUX_HDIO_GETGEO_BIG:
		/*
		 * Try to mimic Linux behaviour: return the BIOS geometry
		 * if possible (extending its # of cylinders if it's beyond
		 * the 1023 limit), fall back to the MI geometry (i.e.
		 * the real geometry) if not found, by returning an
		 * error. See common/linux_hdio.c
		 */
		bip = fd2biosinfo(curproc, fp);
		ioctlf = fp->f_ops->fo_ioctl;
		error = ioctlf(fp, DIOCGDEFLABEL, (void *)&label);
		error1 = ioctlf(fp, DIOCGPART, (void *)&partp);
		if (error != 0 && error1 != 0) {
			error = error1;
			goto out;
		}
		labp = error != 0 ? &label : partp.disklab;
		start = error1 != 0 ? partp.part->p_offset : 0;
		if (bip != NULL && bip->bi_head != 0 && bip->bi_sec != 0
		    && bip->bi_cyl != 0) {
			heads = bip->bi_head;
			sectors = bip->bi_sec;
			cylinders = bip->bi_cyl;
			biostotal = heads * sectors * cylinders;
			realtotal = labp->d_ntracks * labp->d_nsectors *
			    labp->d_ncylinders;
			if (realtotal > biostotal)
				cylinders = realtotal / (heads * sectors);
		} else {
			heads = labp->d_ntracks;
			cylinders = labp->d_ncylinders;
			sectors = labp->d_nsectors;
		}
		if (com == LINUX_HDIO_GETGEO) {
			hdg.start = start;
			hdg.heads = heads;
			hdg.cylinders = cylinders;
			hdg.sectors = sectors;
			error = copyout(&hdg, SCARG(uap, data), sizeof hdg);
			goto out;
		} else {
			hdg_big.start = start;
			hdg_big.heads = heads;
			hdg_big.cylinders = cylinders;
			hdg_big.sectors = sectors;
			error = copyout(&hdg_big, SCARG(uap, data),
			    sizeof hdg_big);
			goto out;
		}

	default:
		/*
		 * Unknown to us. If it's on a device, just pass it through
		 * using PTIOCLINUX, the device itself might be able to
		 * make some sense of it.
		 * XXX hack: if the function returns EJUSTRETURN,
		 * it has stuffed a sysctl return value in pt.data.
		 */
		ioctlf = fp->f_ops->fo_ioctl;
		pt.com = SCARG(uap, com);
		pt.data = SCARG(uap, data);
		error = ioctlf(fp, PTIOCLINUX, &pt);
		if (error == EJUSTRETURN) {
			retval[0] = (register_t)pt.data;
			error = 0;
		}

		if (error == ENOTTY) {
			DPRINTF(("linux_machdepioctl: invalid ioctl %08lx\n",
			    com));
		}
		goto out;
	}
	SCARG(&bia, com) = com;
	error = sys_ioctl(curlwp, &bia, retval);
out:
	fd_putfile(fd);
	return error;
}
Example #3
0
/*
 * We come here in a last attempt to satisfy a Linux ioctl() call
 */
int
linux_machdepioctl(struct proc *p, void *v, register_t *retval)
{
	struct linux_sys_ioctl_args /* {
		syscallarg(int) fd;
		syscallarg(u_long) com;
		syscallarg(caddr_t) data;
	} */ *uap = v;
	struct sys_ioctl_args bia;
	u_long com;
	int error;
#if (NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL))
	struct vt_mode lvt;
	caddr_t bvtp, sg;
#endif
	struct filedesc *fdp;
	struct file *fp;
	int fd;
	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *);
	struct ioctl_pt pt;

	fd = SCARG(uap, fd);
	SCARG(&bia, fd) = SCARG(uap, fd);
	SCARG(&bia, data) = SCARG(uap, data);
	com = SCARG(uap, com);

	fdp = p->p_fd;
	if ((fp = fd_getfile(fdp, fd)) == NULL)
		return (EBADF);

	switch (com) {
#if (NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL))
	case LINUX_KDGKBMODE:
		com = KDGKBMODE;
		break;
	case LINUX_KDSKBMODE:
		com = KDSKBMODE;
		if ((unsigned)SCARG(uap, data) == LINUX_K_MEDIUMRAW)
			SCARG(&bia, data) = (caddr_t)K_RAW;
		break;
	case LINUX_KIOCSOUND:
		SCARG(&bia, data) =
			(caddr_t)(((unsigned long)SCARG(&bia, data)) & 0xffff);
		/* FALLTHROUGH */
	case LINUX_KDMKTONE:
		com = KDMKTONE;
		break;
	case LINUX_KDSETMODE:
		com = KDSETMODE;
		break;
	case LINUX_KDGETMODE:
#if NWSDISPLAY > 0 && defined(WSDISPLAY_COMPAT_USL)
		com = WSDISPLAYIO_GMODE;
#else
		com = KDGETMODE;
#endif
		break;
	case LINUX_KDENABIO:
		com = KDENABIO;
		break;
	case LINUX_KDDISABIO:
		com = KDDISABIO;
		break;
	case LINUX_KDGETLED:
		com = KDGETLED;
		break;
	case LINUX_KDSETLED:
		com = KDSETLED;
		break;
	case LINUX_VT_OPENQRY:
		com = VT_OPENQRY;
		break;
	case LINUX_VT_GETMODE: {
		int sig;

		SCARG(&bia, com) = VT_GETMODE;
		if ((error = sys_ioctl(p, &bia, retval)))
			return error;
		if ((error = copyin(SCARG(uap, data), (caddr_t)&lvt,
		    sizeof (struct vt_mode))))
			return error;
		/* We need to bounds check here in case there
		   is a race with another thread */
		if ((error = bsd_to_linux_signal(lvt.relsig, &sig)))
			return error;
		lvt.relsig = sig;

		if ((error = bsd_to_linux_signal(lvt.acqsig, &sig)))
			return error;
		lvt.acqsig = sig;
		
		if ((error = bsd_to_linux_signal(lvt.frsig, &sig)))
			return error;
		lvt.frsig = sig;

		return copyout((caddr_t)&lvt, SCARG(uap, data),
		    sizeof (struct vt_mode));
	}
	case LINUX_VT_SETMODE: {
		int sig;

		com = VT_SETMODE;
		if ((error = copyin(SCARG(uap, data), (caddr_t)&lvt,
		    sizeof (struct vt_mode))))
			return error;
		if ((error = linux_to_bsd_signal(lvt.relsig, &sig)))
			return error;
		lvt.relsig = sig;

		if ((error = linux_to_bsd_signal(lvt.acqsig, &sig)))
			return error;
		lvt.acqsig = sig;

		if ((error = linux_to_bsd_signal(lvt.frsig, &sig)))
			return error;
		lvt.frsig = sig;

		sg = stackgap_init(p->p_emul);
		bvtp = stackgap_alloc(&sg, sizeof (struct vt_mode));
		if ((error = copyout(&lvt, bvtp, sizeof (struct vt_mode))))
			return error;
		SCARG(&bia, data) = bvtp;
		break;
	}
	case LINUX_VT_DISALLOCATE:
		/* XXX should use WSDISPLAYIO_DELSCREEN */
		return 0;
	case LINUX_VT_RELDISP:
		com = VT_RELDISP;
		break;
	case LINUX_VT_ACTIVATE:
		com = VT_ACTIVATE;
		break;
	case LINUX_VT_WAITACTIVE:
		com = VT_WAITACTIVE;
		break;
	case LINUX_VT_GETSTATE:
		com = VT_GETSTATE;
		break;
	case LINUX_KDGKBTYPE:
	{
		char tmp = KB_101;

		/* This is what Linux does */
		return copyout(&tmp, SCARG(uap, data), sizeof(char));
	}
#endif
	default:
		/*
		 * Unknown to us. If it's on a device, just pass it through
		 * using PTIOCLINUX, the device itself might be able to
		 * make some sense of it.
		 * XXX hack: if the function returns EJUSTRETURN,
		 * it has stuffed a sysctl return value in pt.data.
		 */
		FREF(fp);
		ioctlf = fp->f_ops->fo_ioctl;
		pt.com = SCARG(uap, com);
		pt.data = SCARG(uap, data);
		error = ioctlf(fp, PTIOCLINUX, (caddr_t)&pt, p);
		FRELE(fp);
		if (error == EJUSTRETURN) {
			retval[0] = (register_t)pt.data;
			error = 0;
		}

		if (error == ENOTTY)
			printf("linux_machdepioctl: invalid ioctl %08lx\n",
			    com);
		return (error);
	}
	SCARG(&bia, com) = com;
	return sys_ioctl(p, &bia, retval);
}
Example #4
0
int
linux_ioctl_hdio(struct proc *p, struct linux_sys_ioctl_args *uap,
		 register_t *retval)
{
	u_long com;
	int error, error1;
	caddr_t sg;
	struct filedesc *fdp;
	struct file *fp;
	int (*ioctlf)(struct file *, u_long, caddr_t, struct proc *);
	struct ataparams *atap, ata;
	struct atareq req;
	struct disklabel label, *labp;
	struct partinfo partp;
	struct linux_hd_geometry hdg;
	struct linux_hd_big_geometry hdg_big;

	fdp = p->p_fd;
	if ((fp = fd_getfile(fdp, SCARG(uap, fd))) == NULL)
		return (EBADF);

	FREF(fp);
	com = SCARG(uap, com);
	ioctlf = fp->f_ops->fo_ioctl;
	retval[0] = error = 0;

	com = SCARG(uap, com);

	switch (com) {
	case LINUX_HDIO_OBSOLETE_IDENTITY:
	case LINUX_HDIO_GET_IDENTITY:
		sg = stackgap_init(p->p_emul);
		atap = stackgap_alloc(&sg, DEV_BSIZE);
		if (atap == NULL) {
			error = ENOMEM;
			break;
		}

		req.flags = ATACMD_READ;
		req.command = WDCC_IDENTIFY;
		req.databuf = (caddr_t)atap;
		req.datalen = DEV_BSIZE;
		req.timeout = 1000;
		error = ioctlf(fp, ATAIOCCOMMAND, (caddr_t)&req, p);
		if (error != 0)
			break;
		if (req.retsts != ATACMD_OK) {
			error = EIO;
			break;
		}
		error = copyin(atap, &ata, sizeof ata);
		if (error != 0)
			break;
		/*
		 * 142 is the size of the old structure used by Linux,
		 * which doesn't seem to be defined anywhere anymore.
		 */
		error = copyout(&ata, SCARG(uap, data),
		    com == LINUX_HDIO_GET_IDENTITY ? sizeof ata : 142);
		break;
	case LINUX_HDIO_GETGEO:
		error = linux_machdepioctl(p, uap, retval);
		if (error == 0)
			break;
		error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p);
		error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p);
		if (error != 0 && error1 != 0) {
			error = error1;
			break;
		}
		labp = error != 0 ? &label : partp.disklab;
		hdg.start = error1 != 0 ? partp.part->p_offset : 0;
		hdg.heads = labp->d_ntracks;
		hdg.cylinders = labp->d_ncylinders;
		hdg.sectors = labp->d_nsectors;
		error = copyout(&hdg, SCARG(uap, data), sizeof hdg);
		break;
	case LINUX_HDIO_GETGEO_BIG:
		error = linux_machdepioctl(p, uap, retval);
		if (error == 0)
			break;
	case LINUX_HDIO_GETGEO_BIG_RAW:
		error = ioctlf(fp, DIOCGDINFO, (caddr_t)&label, p);
		error1 = ioctlf(fp, DIOCGPART, (caddr_t)&partp, p);
		if (error != 0 && error1 != 0) {
			error = error1;
			break;
		}
		labp = error != 0 ? &label : partp.disklab;
		hdg_big.start = error1 != 0 ? partp.part->p_offset : 0;
		hdg_big.heads = labp->d_ntracks;
		hdg_big.cylinders = labp->d_ncylinders;
		hdg_big.sectors = labp->d_nsectors;
		error = copyout(&hdg_big, SCARG(uap, data), sizeof hdg_big);
		break;
	case LINUX_HDIO_GET_UNMASKINTR:
	case LINUX_HDIO_GET_MULTCOUNT:
	case LINUX_HDIO_GET_KEEPSETTINGS:
	case LINUX_HDIO_GET_32BIT:
	case LINUX_HDIO_GET_NOWERR:
	case LINUX_HDIO_GET_DMA:
	case LINUX_HDIO_GET_NICE:
	case LINUX_HDIO_DRIVE_RESET:
	case LINUX_HDIO_TRISTATE_HWIF:
	case LINUX_HDIO_DRIVE_TASK:
	case LINUX_HDIO_DRIVE_CMD:
	case LINUX_HDIO_SET_MULTCOUNT:
	case LINUX_HDIO_SET_UNMASKINTR:
	case LINUX_HDIO_SET_KEEPSETTINGS:
	case LINUX_HDIO_SET_32BIT:
	case LINUX_HDIO_SET_NOWERR:
	case LINUX_HDIO_SET_DMA:
	case LINUX_HDIO_SET_PIO_MODE:
	case LINUX_HDIO_SCAN_HWIF:
	case LINUX_HDIO_SET_NICE:
	case LINUX_HDIO_UNREGISTER_HWIF:
		error = EINVAL;
	}

	FRELE(fp);
	return error;
}
Example #5
0
int
oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
		syscallarg(u_long) com;
		syscallarg(void *) data;
	} */
	file_t *fp;
	u_long com;
	struct audiodevinfo *di;
	mixer_ctrl_t mc;
	struct oss_mixer_info omi;
	struct audio_device adev;
	int idat;
	int i;
	int error;
	int l, r, n, e;
	int (*ioctlf)(file_t *, u_long, void *);

	if ((fp = fd_getfile(SCARG(uap, fd))) == NULL)
		return (EBADF);

	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
		error = EBADF;
		goto out;
	}

	com = SCARG(uap, com);
	DPRINTF(("oss_ioctl_mixer: com=%08lx\n", com));

	retval[0] = 0;

	di = getdevinfo(fp);
	if (di == 0) {
		error = EINVAL;
		goto out;
	}

	ioctlf = fp->f_ops->fo_ioctl;
	switch (com) {
	case OSS_GET_VERSION:
		idat = OSS_SOUND_VERSION;
		break;
	case OSS_SOUND_MIXER_INFO:
	case OSS_SOUND_OLD_MIXER_INFO:
		error = ioctlf(fp, AUDIO_GETDEV, &adev);
		if (error)
			goto out;
		omi.modify_counter = 1;
		strncpy(omi.id, adev.name, sizeof omi.id);
		strncpy(omi.name, adev.name, sizeof omi.name);
		error = copyout(&omi, SCARG(uap, data), OSS_IOCTL_SIZE(com));
		goto out;
	case OSS_SOUND_MIXER_READ_RECSRC:
		if (di->source == -1) {
			error = EINVAL;
			goto out;
		}
		mc.dev = di->source;
		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
			mc.type = AUDIO_MIXER_ENUM;
			error = ioctlf(fp, AUDIO_MIXER_READ, &mc);
			if (error)
				goto out;
			e = opaque_to_enum(di, NULL, mc.un.ord);
			if (e >= 0)
				idat = 1 << di->rdevmap[e];
		} else {
			mc.type = AUDIO_MIXER_SET;
			error = ioctlf(fp, AUDIO_MIXER_READ, &mc);
			if (error)
				goto out;
			e = opaque_to_enum(di, NULL, mc.un.mask);
			if (e >= 0)
				idat = 1 << di->rdevmap[e];
		}
		break;
	case OSS_SOUND_MIXER_READ_DEVMASK:
		idat = di->devmask;
		break;
	case OSS_SOUND_MIXER_READ_RECMASK:
		idat = di->recmask;
		break;
	case OSS_SOUND_MIXER_READ_STEREODEVS:
		idat = di->stereomask;
		break;
	case OSS_SOUND_MIXER_READ_CAPS:
		idat = di->caps;
		break;
	case OSS_SOUND_MIXER_WRITE_RECSRC:
	case OSS_SOUND_MIXER_WRITE_R_RECSRC:
		if (di->source == -1) {
			error = EINVAL;
			goto out;
		}
		mc.dev = di->source;
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
			mc.type = AUDIO_MIXER_ENUM;
			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
				if (idat & (1 << i))
					break;
			if (i >= OSS_SOUND_MIXER_NRDEVICES ||
			    di->devmap[i] == -1) {
				error = EINVAL;
				goto out;
			}
			mc.un.ord = enum_to_ord(di, di->devmap[i]);
		} else {
			mc.type = AUDIO_MIXER_SET;
			mc.un.mask = 0;
			for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) {
				if (idat & (1 << i)) {
					if (di->devmap[i] == -1) {
						error = EINVAL;
						goto out;
					}
					mc.un.mask |= enum_to_mask(di, di->devmap[i]);
				}
			}
		}
		error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc);
		goto out;
	default:
		if (OSS_MIXER_READ(OSS_SOUND_MIXER_FIRST) <= com &&
		    com < OSS_MIXER_READ(OSS_SOUND_MIXER_NRDEVICES)) {
			n = OSS_GET_DEV(com);
			if (di->devmap[n] == -1) {
				error = EINVAL;
				goto out;
			}
		    doread:
			mc.dev = di->devmap[n];
			mc.type = AUDIO_MIXER_VALUE;
			mc.un.value.num_channels = di->stereomask & (1<<n) ? 2 : 1;
			error = ioctlf(fp, AUDIO_MIXER_READ, &mc);
			if (error)
				goto out;
			if (mc.un.value.num_channels != 2) {
				l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
			} else {
				l = mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT];
				r = mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT];
			}
			idat = TO_OSSVOL(l) | (TO_OSSVOL(r) << 8);
			DPRINTF(("OSS_MIXER_READ  n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
				 n, di->devmap[n], l, r, idat));
			break;
		} else if ((OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_FIRST) <= com &&
			   com < OSS_MIXER_WRITE_R(OSS_SOUND_MIXER_NRDEVICES)) ||
			   (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES))) {
			n = OSS_GET_DEV(com);
			if (di->devmap[n] == -1) {
				error = EINVAL;
				goto out;
			}
			error = copyin(SCARG(uap, data), &idat, sizeof idat);
			if (error)
				goto out;
			l = FROM_OSSVOL( idat       & 0xff);
			r = FROM_OSSVOL((idat >> 8) & 0xff);
			mc.dev = di->devmap[n];
			mc.type = AUDIO_MIXER_VALUE;
			if (di->stereomask & (1<<n)) {
				mc.un.value.num_channels = 2;
				mc.un.value.level[AUDIO_MIXER_LEVEL_LEFT] = l;
				mc.un.value.level[AUDIO_MIXER_LEVEL_RIGHT] = r;
			} else {
				mc.un.value.num_channels = 1;
				mc.un.value.level[AUDIO_MIXER_LEVEL_MONO] = (l+r)/2;
			}
			DPRINTF(("OSS_MIXER_WRITE n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
				 n, di->devmap[n], l, r, idat));
			error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc);
			if (error)
				goto out;
			if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
			   com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
				error = 0;
				goto out;
			}
			goto doread;
		} else {
Example #6
0
int
oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t *retval)
{
	/* {
		syscallarg(int) fd;
		syscallarg(u_long) com;
		syscallarg(void *) data;
	} */
	file_t *fp;
	u_long com;
	struct audio_info tmpinfo;
	struct audio_offset tmpoffs;
	struct oss_audio_buf_info bufinfo;
	struct oss_count_info cntinfo;
	struct audio_encoding tmpenc;
	u_int u;
	int idat, idata;
	int error = 0;
	int (*ioctlf)(file_t *, u_long, void *);

	if ((fp = fd_getfile(SCARG(uap, fd))) == NULL)
		return (EBADF);

	if ((fp->f_flag & (FREAD | FWRITE)) == 0) {
		error = EBADF;
		goto out;
	}

	com = SCARG(uap, com);
	DPRINTF(("oss_ioctl_audio: com=%08lx\n", com));

	retval[0] = 0;

	ioctlf = fp->f_ops->fo_ioctl;
	switch (com) {
	case OSS_SNDCTL_DSP_RESET:
		error = ioctlf(fp, AUDIO_FLUSH, NULL);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_SYNC:
		error = ioctlf(fp, AUDIO_DRAIN, NULL);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_POST:
		/* This call is merely advisory, and may be a nop. */
		break;
	case OSS_SNDCTL_DSP_SPEED:
		AUDIO_INITINFO(&tmpinfo);
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		tmpinfo.play.sample_rate =
		tmpinfo.record.sample_rate = idat;
		error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_SPEED %d = %d\n",
			 idat, error));
		if (error)
			goto out;
		/* fall into ... */
	case OSS_SOUND_PCM_READ_RATE:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		idat = tmpinfo.play.sample_rate;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_STEREO:
		AUDIO_INITINFO(&tmpinfo);
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		tmpinfo.play.channels =
		tmpinfo.record.channels = idat ? 2 : 1;
		(void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		idat = tmpinfo.play.channels - 1;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_GETBLKSIZE:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		setblocksize(fp, &tmpinfo);
		idat = tmpinfo.blocksize;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_SETFMT:
		AUDIO_INITINFO(&tmpinfo);
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		switch (idat) {
		case OSS_AFMT_MU_LAW:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 8;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_ULAW;
			break;
		case OSS_AFMT_A_LAW:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 8;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_ALAW;
			break;
		case OSS_AFMT_U8:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 8;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR;
			break;
		case OSS_AFMT_S8:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 8;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR;
			break;
		case OSS_AFMT_S16_LE:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 16;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_LE;
			break;
		case OSS_AFMT_S16_BE:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 16;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_SLINEAR_BE;
			break;
		case OSS_AFMT_U16_LE:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 16;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_LE;
			break;
		case OSS_AFMT_U16_BE:
			tmpinfo.play.precision =
			tmpinfo.record.precision = 16;
			tmpinfo.play.encoding =
			tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
			break;
		default:
			error = EINVAL;
			goto out;
		}
		(void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
		/* fall into ... */
	case OSS_SOUND_PCM_READ_BITS:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		switch (tmpinfo.play.encoding) {
		case AUDIO_ENCODING_ULAW:
			idat = OSS_AFMT_MU_LAW;
			break;
		case AUDIO_ENCODING_ALAW:
			idat = OSS_AFMT_A_LAW;
			break;
		case AUDIO_ENCODING_SLINEAR_LE:
			if (tmpinfo.play.precision == 16)
				idat = OSS_AFMT_S16_LE;
			else
				idat = OSS_AFMT_S8;
			break;
		case AUDIO_ENCODING_SLINEAR_BE:
			if (tmpinfo.play.precision == 16)
				idat = OSS_AFMT_S16_BE;
			else
				idat = OSS_AFMT_S8;
			break;
		case AUDIO_ENCODING_ULINEAR_LE:
			if (tmpinfo.play.precision == 16)
				idat = OSS_AFMT_U16_LE;
			else
				idat = OSS_AFMT_U8;
			break;
		case AUDIO_ENCODING_ULINEAR_BE:
			if (tmpinfo.play.precision == 16)
				idat = OSS_AFMT_U16_BE;
			else
				idat = OSS_AFMT_U8;
			break;
		case AUDIO_ENCODING_ADPCM:
			idat = OSS_AFMT_IMA_ADPCM;
			break;
		}
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_CHANNELS:
		AUDIO_INITINFO(&tmpinfo);
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		tmpinfo.play.channels =
		tmpinfo.record.channels = idat;
		(void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
		/* fall into ... */
	case OSS_SOUND_PCM_READ_CHANNELS:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		idat = tmpinfo.play.channels;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SOUND_PCM_WRITE_FILTER:
	case OSS_SOUND_PCM_READ_FILTER:
		error = EINVAL; /* XXX unimplemented */
		goto out;
	case OSS_SNDCTL_DSP_SUBDIVIDE:
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		setblocksize(fp, &tmpinfo);
		if (error)
			goto out;
		if (idat == 0)
			idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
		idat = (tmpinfo.play.buffer_size / idat) & -4;
		AUDIO_INITINFO(&tmpinfo);
		tmpinfo.blocksize = idat;
		error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
		if (error)
			goto out;
		idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_SETFRAGMENT:
		AUDIO_INITINFO(&tmpinfo);
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) {
			error = EINVAL;
			goto out;
		}
		tmpinfo.blocksize = 1 << (idat & 0xffff);
		tmpinfo.hiwat = (idat >> 16) & 0x7fff;
		DPRINTF(("oss_audio: SETFRAGMENT blksize=%d, hiwat=%d\n",
			 tmpinfo.blocksize, tmpinfo.hiwat));
		if (tmpinfo.hiwat == 0)	/* 0 means set to max */
			tmpinfo.hiwat = 65536;
		(void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		u = tmpinfo.blocksize;
		for(idat = 0; u > 1; idat++, u >>= 1)
			;
		idat |= (tmpinfo.hiwat & 0x7fff) << 16;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_GETFMTS:
		for(idat = 0, tmpenc.index = 0;
		    ioctlf(fp, AUDIO_GETENC, &tmpenc) == 0;
		    tmpenc.index++) {
			switch(tmpenc.encoding) {
			case AUDIO_ENCODING_ULAW:
				idat |= OSS_AFMT_MU_LAW;
				break;
			case AUDIO_ENCODING_ALAW:
				idat |= OSS_AFMT_A_LAW;
				break;
			case AUDIO_ENCODING_SLINEAR:
				idat |= OSS_AFMT_S8;
				break;
			case AUDIO_ENCODING_SLINEAR_LE:
				if (tmpenc.precision == 16)
					idat |= OSS_AFMT_S16_LE;
				else
					idat |= OSS_AFMT_S8;
				break;
			case AUDIO_ENCODING_SLINEAR_BE:
				if (tmpenc.precision == 16)
					idat |= OSS_AFMT_S16_BE;
				else
					idat |= OSS_AFMT_S8;
				break;
			case AUDIO_ENCODING_ULINEAR:
				idat |= OSS_AFMT_U8;
				break;
			case AUDIO_ENCODING_ULINEAR_LE:
				if (tmpenc.precision == 16)
					idat |= OSS_AFMT_U16_LE;
				else
					idat |= OSS_AFMT_U8;
				break;
			case AUDIO_ENCODING_ULINEAR_BE:
				if (tmpenc.precision == 16)
					idat |= OSS_AFMT_U16_BE;
				else
					idat |= OSS_AFMT_U8;
				break;
			case AUDIO_ENCODING_ADPCM:
				idat |= OSS_AFMT_IMA_ADPCM;
				break;
			default:
				break;
			}
		}
		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETFMTS = %x\n", idat));
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_GETOSPACE:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		setblocksize(fp, &tmpinfo);
		bufinfo.fragsize = tmpinfo.blocksize;
		bufinfo.fragments = tmpinfo.hiwat -
		    (tmpinfo.play.seek + tmpinfo.blocksize - 1) /
		    tmpinfo.blocksize;
		bufinfo.fragstotal = tmpinfo.hiwat;
		bufinfo.bytes =
		    tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek;
		error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_GETISPACE:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		setblocksize(fp, &tmpinfo);
		bufinfo.fragsize = tmpinfo.blocksize;
		bufinfo.fragments = tmpinfo.hiwat -
		    (tmpinfo.record.seek + tmpinfo.blocksize - 1) /
		    tmpinfo.blocksize;
                bufinfo.fragstotal = tmpinfo.hiwat;
		bufinfo.bytes =
		    tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.record.seek;
		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETxSPACE = %d %d %d %d\n",
			 bufinfo.fragsize, bufinfo.fragments,
			 bufinfo.fragstotal, bufinfo.bytes));
		error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_NONBLOCK:
		idat = 1;
		error = ioctlf(fp, FIONBIO, &idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_GETCAPS:
		error = ioctlf(fp, AUDIO_GETPROPS, &idata);
		if (error)
			goto out;
		idat = OSS_DSP_CAP_TRIGGER; /* pretend we have trigger */
		if (idata & AUDIO_PROP_FULLDUPLEX)
			idat |= OSS_DSP_CAP_DUPLEX;
		if (idata & AUDIO_PROP_MMAP)
			idat |= OSS_DSP_CAP_MMAP;
		DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETCAPS = %x\n", idat));
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
#if 0
	case OSS_SNDCTL_DSP_GETTRIGGER:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		idat = (tmpinfo.play.pause ? 0 : OSS_PCM_ENABLE_OUTPUT) |
		       (tmpinfo.record.pause ? 0 : OSS_PCM_ENABLE_INPUT);
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_SETTRIGGER:
		(void) ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo, p);
		error = copyin(SCARG(uap, data), &idat, sizeof idat);
		if (error)
			goto out;
		tmpinfo.play.pause = (idat & OSS_PCM_ENABLE_OUTPUT) == 0;
		tmpinfo.record.pause = (idat & OSS_PCM_ENABLE_INPUT) == 0;
		(void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
#else
	case OSS_SNDCTL_DSP_GETTRIGGER:
	case OSS_SNDCTL_DSP_SETTRIGGER:
		/* XXX Do nothing for now. */
		idat = OSS_PCM_ENABLE_OUTPUT;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		goto out;
#endif
	case OSS_SNDCTL_DSP_GETIPTR:
		error = ioctlf(fp, AUDIO_GETIOFFS, &tmpoffs);
		if (error)
			goto out;
		cntinfo.bytes = tmpoffs.samples;
		cntinfo.blocks = tmpoffs.deltablks;
		cntinfo.ptr = tmpoffs.offset;
		error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_GETOPTR:
		error = ioctlf(fp, AUDIO_GETOOFFS, &tmpoffs);
		if (error)
			goto out;
		cntinfo.bytes = tmpoffs.samples;
		cntinfo.blocks = tmpoffs.deltablks;
		cntinfo.ptr = tmpoffs.offset;
		error = copyout(&cntinfo, SCARG(uap, data), sizeof cntinfo);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_SETDUPLEX:
		idat = 1;
		error = ioctlf(fp, AUDIO_SETFD, &idat);
		goto out;
	case OSS_SNDCTL_DSP_MAPINBUF:
	case OSS_SNDCTL_DSP_MAPOUTBUF:
	case OSS_SNDCTL_DSP_SETSYNCRO:
		error = EINVAL;
		goto out;
	case OSS_SNDCTL_DSP_GETODELAY:
		error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
		if (error)
			goto out;
		idat = tmpinfo.play.seek + tmpinfo.blocksize / 2;
		error = copyout(&idat, SCARG(uap, data), sizeof idat);
		if (error)
			goto out;
		break;
	case OSS_SNDCTL_DSP_PROFILE:
		/* This gives just a hint to the driver,
		 * implementing it as a NOP is ok
		 */
		break;
	default:
		error = EINVAL;
		goto out;
	}

 out:
 	fd_putfile(SCARG(uap, fd));
	return error;
}
Example #7
0
/*
 * Collect the audio device information to allow faster
 * emulation of the Linux mixer ioctls.  Cache the information
 * to eliminate the overhead of repeating all the ioctls needed
 * to collect the information.
 */
static struct audiodevinfo *
getdevinfo(file_t *fp)
{
	mixer_devinfo_t mi;
	int i, j, e;
	static const struct {
		const char *name;
		int code;
	} *dp, devs[] = {
		{ AudioNmicrophone,	OSS_SOUND_MIXER_MIC },
		{ AudioNline,		OSS_SOUND_MIXER_LINE },
		{ AudioNcd,		OSS_SOUND_MIXER_CD },
		{ AudioNdac,		OSS_SOUND_MIXER_PCM },
		{ AudioNaux,		OSS_SOUND_MIXER_LINE1 },
		{ AudioNrecord,		OSS_SOUND_MIXER_IMIX },
		{ AudioNmaster,		OSS_SOUND_MIXER_VOLUME },
		{ AudioNtreble,		OSS_SOUND_MIXER_TREBLE },
		{ AudioNbass,		OSS_SOUND_MIXER_BASS },
		{ AudioNspeaker,	OSS_SOUND_MIXER_SPEAKER },
/*		{ AudioNheadphone,	?? },*/
		{ AudioNoutput,		OSS_SOUND_MIXER_OGAIN },
		{ AudioNinput,		OSS_SOUND_MIXER_IGAIN },
/*		{ AudioNmaster,		OSS_SOUND_MIXER_SPEAKER },*/
/*		{ AudioNstereo,		?? },*/
/*		{ AudioNmono,		?? },*/
		{ AudioNfmsynth,	OSS_SOUND_MIXER_SYNTH },
/*		{ AudioNwave,		OSS_SOUND_MIXER_PCM },*/
		{ AudioNmidi,		OSS_SOUND_MIXER_SYNTH },
/*		{ AudioNmixerout,	?? },*/
		{ 0, -1 }
	};
	int (*ioctlf)(file_t *, u_long, void *) = fp->f_ops->fo_ioctl;
	struct vnode *vp;
	struct vattr va;
	static struct audiodevinfo devcache;
	struct audiodevinfo *di = &devcache;
	int mlen, dlen;

	/*
	 * Figure out what device it is so we can check if the
	 * cached data is valid.
	 */
	vp = fp->f_data;
	if (vp->v_type != VCHR)
		return 0;
	if (VOP_GETATTR(vp, &va, kauth_cred_get()))
		return 0;
	if (di->done && di->dev == va.va_rdev)
		return di;

	di->done = 1;
	di->dev = va.va_rdev;
	di->devmask = 0;
	di->recmask = 0;
	di->stereomask = 0;
	di->source = ~0;
	di->caps = 0;
	for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++)
		di->devmap[i] = -1;
	for(i = 0; i < NETBSD_MAXDEVS; i++) {
		di->rdevmap[i] = -1;
		di->names[i][0] = '\0';
		di->enum2opaque[i] = -1;
	}
	for(i = 0; i < NETBSD_MAXDEVS; i++) {
		mi.index = i;
		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0)
			break;
		switch(mi.type) {
		case AUDIO_MIXER_VALUE:
			for(dp = devs; dp->name; dp++) {
				if (strcmp(dp->name, mi.label.name) == 0)
					break;
				dlen = strlen(dp->name);
				mlen = strlen(mi.label.name);
				if (dlen < mlen
				    && mi.label.name[mlen-dlen-1] == '.'
				    && strcmp(dp->name, mi.label.name + mlen - dlen) == 0)
					break;
			}
			if (dp->code >= 0) {
				di->devmap[dp->code] = i;
				di->rdevmap[i] = dp->code;
				di->devmask |= 1 << dp->code;
				if (mi.un.v.num_channels == 2)
					di->stereomask |= 1 << dp->code;
				strncpy(di->names[i], mi.label.name,
					sizeof di->names[i]);
			}
			break;
		}
	}
	for(i = 0; i < NETBSD_MAXDEVS; i++) {
		mi.index = i;
		if (ioctlf(fp, AUDIO_MIXER_DEVINFO, &mi) < 0)
			break;
		if (strcmp(mi.label.name, AudioNsource) != 0)
			continue;
		di->source = i;
		switch(mi.type) {
		case AUDIO_MIXER_ENUM:
			for(j = 0; j < mi.un.e.num_mem; j++) {
				e = opaque_to_enum(di,
						   &mi.un.e.member[j].label,
						   mi.un.e.member[j].ord);
				if (e >= 0)
					di->recmask |= 1 << di->rdevmap[e];
			}
			di->caps = OSS_SOUND_CAP_EXCL_INPUT;
			break;
		case AUDIO_MIXER_SET:
			for(j = 0; j < mi.un.s.num_mem; j++) {
				e = opaque_to_enum(di,
						   &mi.un.s.member[j].label,
						   mi.un.s.member[j].mask);
				if (e >= 0)
					di->recmask |= 1 << di->rdevmap[e];
			}
			break;
		}
	}
	return di;
}