add a lot more debugging and error checking. Alas, skype seems to be happy
getting back our values, but still does not work.
This commit is contained in:
parent
a05687c954
commit
84ec419140
@ -1,4 +1,4 @@
|
||||
/* $NetBSD: ossaudio.c,v 1.64 2008/11/13 10:05:52 ad Exp $ */
|
||||
/* $NetBSD: ossaudio.c,v 1.65 2009/08/22 23:31:16 christos Exp $ */
|
||||
|
||||
/*-
|
||||
* Copyright (c) 1997, 2008 The NetBSD Foundation, Inc.
|
||||
@ -27,7 +27,7 @@
|
||||
*/
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.64 2008/11/13 10:05:52 ad Exp $");
|
||||
__KERNEL_RCSID(0, "$NetBSD: ossaudio.c,v 1.65 2009/08/22 23:31:16 christos Exp $");
|
||||
|
||||
#include <sys/param.h>
|
||||
#include <sys/proc.h>
|
||||
@ -66,6 +66,88 @@ static int enum_to_mask(struct audiodevinfo *di, int enm);
|
||||
|
||||
static void setblocksize(file_t *, struct audio_info *);
|
||||
|
||||
#ifdef AUDIO_DEBUG
|
||||
static const char *
|
||||
compat_ossaudio_getcmd(u_long cmd)
|
||||
{
|
||||
static char buf[64];
|
||||
switch (cmd) {
|
||||
#define _DO(_a) \
|
||||
case _a: \
|
||||
return # _a;
|
||||
_DO(OSS_SNDCTL_DSP_RESET)
|
||||
_DO(OSS_SNDCTL_DSP_SYNC)
|
||||
_DO(OSS_SNDCTL_DSP_SPEED)
|
||||
_DO(OSS_SOUND_PCM_READ_RATE)
|
||||
_DO(OSS_SNDCTL_DSP_STEREO)
|
||||
_DO(OSS_SNDCTL_DSP_GETBLKSIZE)
|
||||
_DO(OSS_SNDCTL_DSP_SETFMT)
|
||||
_DO(OSS_SOUND_PCM_READ_BITS)
|
||||
_DO(OSS_SNDCTL_DSP_CHANNELS)
|
||||
_DO(OSS_SOUND_PCM_READ_CHANNELS)
|
||||
_DO(OSS_SOUND_PCM_WRITE_FILTER)
|
||||
_DO(OSS_SOUND_PCM_READ_FILTER)
|
||||
_DO(OSS_SNDCTL_DSP_POST)
|
||||
_DO(OSS_SNDCTL_DSP_SUBDIVIDE)
|
||||
_DO(OSS_SNDCTL_DSP_SETFRAGMENT)
|
||||
_DO(OSS_SNDCTL_DSP_GETFMTS)
|
||||
_DO(OSS_SNDCTL_DSP_GETOSPACE)
|
||||
_DO(OSS_SNDCTL_DSP_GETISPACE)
|
||||
_DO(OSS_SNDCTL_DSP_NONBLOCK)
|
||||
_DO(OSS_SNDCTL_DSP_GETCAPS)
|
||||
_DO(OSS_SNDCTL_DSP_GETTRIGGER)
|
||||
_DO(OSS_SNDCTL_DSP_SETTRIGGER)
|
||||
_DO(OSS_SNDCTL_DSP_GETIPTR)
|
||||
_DO(OSS_SNDCTL_DSP_GETOPTR)
|
||||
_DO(OSS_SNDCTL_DSP_MAPINBUF)
|
||||
_DO(OSS_SNDCTL_DSP_MAPOUTBUF)
|
||||
_DO(OSS_SNDCTL_DSP_SETSYNCRO)
|
||||
_DO(OSS_SNDCTL_DSP_SETDUPLEX)
|
||||
_DO(OSS_SNDCTL_DSP_GETODELAY)
|
||||
_DO(OSS_SNDCTL_DSP_PROFILE)
|
||||
_DO(OSS_SOUND_MIXER_INFO)
|
||||
_DO(OSS_SOUND_OLD_MIXER_INFO)
|
||||
_DO(OSS_GET_VERSION)
|
||||
_DO(OSS_SEQ_RESET)
|
||||
_DO(OSS_SEQ_SYNC)
|
||||
_DO(OSS_SYNTH_INFO)
|
||||
_DO(OSS_SEQ_CTRLRATE)
|
||||
_DO(OSS_SEQ_GETOUTCOUNT)
|
||||
_DO(OSS_SEQ_GETINCOUNT)
|
||||
_DO(OSS_SEQ_PERCMODE)
|
||||
_DO(OSS_SEQ_TESTMIDI)
|
||||
_DO(OSS_SEQ_RESETSAMPLES)
|
||||
_DO(OSS_SEQ_NRSYNTHS)
|
||||
_DO(OSS_SEQ_NRMIDIS)
|
||||
#ifdef notyet
|
||||
_DO(OSS_MIDI_INFO)
|
||||
#endif
|
||||
_DO(OSS_SEQ_THRESHOLD)
|
||||
_DO(OSS_MEMAVL)
|
||||
_DO(OSS_FM_4OP_ENABLE)
|
||||
_DO(OSS_SEQ_PANIC)
|
||||
_DO(OSS_SEQ_OUTOFBAND)
|
||||
_DO(OSS_SEQ_GETTIME)
|
||||
_DO(OSS_ID)
|
||||
_DO(OSS_CONTROL)
|
||||
_DO(OSS_REMOVESAMPLE)
|
||||
_DO(OSS_TMR_TIMEBASE)
|
||||
_DO(OSS_TMR_START)
|
||||
_DO(OSS_TMR_STOP)
|
||||
_DO(OSS_TMR_CONTINUE)
|
||||
_DO(OSS_TMR_TEMPO)
|
||||
_DO(OSS_TMR_SOURCE)
|
||||
_DO(OSS_TMR_METRONOME)
|
||||
_DO(OSS_TMR_SELECT)
|
||||
#undef _DO
|
||||
default:
|
||||
(void)snprintf(buf, sizeof(buf), "*0x%lx*", cmd);
|
||||
return buf;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static int
|
||||
compat_ossaudio_modcmd(modcmd_t cmd, void *arg)
|
||||
{
|
||||
@ -108,7 +190,7 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
}
|
||||
|
||||
com = SCARG(uap, com);
|
||||
DPRINTF(("oss_ioctl_audio: com=%08lx\n", com));
|
||||
DPRINTF(("%s: com=%s\n", __func__, compat_ossaudio_getcmd(com)));
|
||||
|
||||
retval[0] = 0;
|
||||
|
||||
@ -116,13 +198,17 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
switch (com) {
|
||||
case OSS_SNDCTL_DSP_RESET:
|
||||
error = ioctlf(fp, AUDIO_FLUSH, NULL);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_FLUSH %d\n", __func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_SYNC:
|
||||
error = ioctlf(fp, AUDIO_DRAIN, NULL);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_DRAIN %d\n", __func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_POST:
|
||||
/* This call is merely advisory, and may be a nop. */
|
||||
@ -130,56 +216,93 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
case OSS_SNDCTL_DSP_SPEED:
|
||||
AUDIO_INITINFO(&tmpinfo);
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SPEED %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
tmpinfo.play.sample_rate =
|
||||
tmpinfo.record.sample_rate = idat;
|
||||
DPRINTF(("%s: SNDCTL_DSP_SPEED > %d\n", __func__, idat));
|
||||
error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_SPEED %d = %d\n",
|
||||
idat, error));
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SPEED %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
/* fall into ... */
|
||||
case OSS_SOUND_PCM_READ_RATE:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
idat = tmpinfo.play.sample_rate;
|
||||
DPRINTF(("%s: SNDCTL_PCM_READ_RATE < %d\n", __func__, idat));
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SOUND_PCM_READ_RATE %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_STEREO:
|
||||
AUDIO_INITINFO(&tmpinfo);
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_STEREO %d\n",
|
||||
__func__, 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)
|
||||
error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_SETINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
idat = tmpinfo.play.channels - 1;
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_STEREO %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_GETBLKSIZE:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
setblocksize(fp, &tmpinfo);
|
||||
idat = tmpinfo.blocksize;
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETBLKSIZE < %d\n",
|
||||
__func__, idat));
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETBLKSIZE %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_SETFMT:
|
||||
AUDIO_INITINFO(&tmpinfo);
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SETFMT %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
switch (idat) {
|
||||
case OSS_AFMT_MU_LAW:
|
||||
tmpinfo.play.precision =
|
||||
@ -230,15 +353,27 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
tmpinfo.record.encoding = AUDIO_ENCODING_ULINEAR_BE;
|
||||
break;
|
||||
default:
|
||||
DPRINTF(("%s: SNDCTL_DSP_SETFMT bad fmt %d\n",
|
||||
__func__, idat));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
(void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
DPRINTF(("%s: SNDCTL_DSP_SETFMT > 0x%x\n",
|
||||
__func__, idat));
|
||||
error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_SETINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
/* fall into ... */
|
||||
case OSS_SOUND_PCM_READ_BITS:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
switch (tmpinfo.play.encoding) {
|
||||
case AUDIO_ENCODING_ULAW:
|
||||
idat = OSS_AFMT_MU_LAW;
|
||||
@ -273,83 +408,139 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
case AUDIO_ENCODING_ADPCM:
|
||||
idat = OSS_AFMT_IMA_ADPCM;
|
||||
break;
|
||||
}
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
default:
|
||||
DPRINTF(("%s: SOUND_PCM_READ_BITS bad encoding %d\n",
|
||||
__func__, tmpinfo.play.encoding));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
DPRINTF(("%s: SOUND_PCM_READ_BITS < 0x%x\n",
|
||||
__func__, idat));
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error) {
|
||||
DPRINTF(("%s: SOUND_PCM_READ_BITS %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_CHANNELS:
|
||||
AUDIO_INITINFO(&tmpinfo);
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_CHANNELS %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
tmpinfo.play.channels =
|
||||
tmpinfo.record.channels = idat;
|
||||
(void) ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
DPRINTF(("%s: SNDCTL_DSP_CHANNELS > %d\n", __func__, idat));
|
||||
error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_SETINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
/* fall into ... */
|
||||
case OSS_SOUND_PCM_READ_CHANNELS:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
idat = tmpinfo.play.channels;
|
||||
DPRINTF(("%s: SOUND_PCM_READ_CHANNELS < %d\n", __func__, idat));
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SOUND_PCM_READ_CHANNELS %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SOUND_PCM_WRITE_FILTER:
|
||||
case OSS_SOUND_PCM_READ_FILTER:
|
||||
error = EINVAL; /* XXX unimplemented */
|
||||
DPRINTF(("%s: SOUND_PCM_{READ,WRITE}_FILTER filter\n",
|
||||
__func__));
|
||||
goto out;
|
||||
case OSS_SNDCTL_DSP_SUBDIVIDE:
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SUBDIVIDE %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
setblocksize(fp, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
setblocksize(fp, &tmpinfo);
|
||||
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)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_SETINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
idat = tmpinfo.play.buffer_size / tmpinfo.blocksize;
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SUBDIVIDE %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_SETFRAGMENT:
|
||||
AUDIO_INITINFO(&tmpinfo);
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: DSP_SETFRAGMENT %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
if ((idat & 0xffff) < 4 || (idat & 0xffff) > 17) {
|
||||
DPRINTF(("%s: DSP_SETFRAGMENT bad ival%d\n",
|
||||
__func__, idat));
|
||||
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));
|
||||
DPRINTF(("%s: SNDCTL_DSP_SETFRAGMENT blksize=%d, "
|
||||
"hiwat=%d\n", __func__, 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)
|
||||
error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_SETINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, 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)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SETFRAGMENT %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_GETFMTS:
|
||||
for(idat = 0, tmpenc.index = 0;
|
||||
for (idat = 0, tmpenc.index = 0;
|
||||
ioctlf(fp, AUDIO_GETENC, &tmpenc) == 0;
|
||||
tmpenc.index++) {
|
||||
switch(tmpenc.encoding) {
|
||||
@ -393,18 +584,27 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
idat |= OSS_AFMT_IMA_ADPCM;
|
||||
break;
|
||||
default:
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETFMTS unknown %d\n",
|
||||
__func__, tmpenc.encoding));
|
||||
break;
|
||||
}
|
||||
}
|
||||
DPRINTF(("oss_sys_ioctl: SNDCTL_DSP_GETFMTS = %x\n", idat));
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETFMTS < 0x%x\n",
|
||||
__func__, idat));
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETFMTS = %x = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_GETOSPACE:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
setblocksize(fp, &tmpinfo);
|
||||
bufinfo.fragsize = tmpinfo.blocksize;
|
||||
bufinfo.fragments = tmpinfo.hiwat -
|
||||
@ -414,13 +614,19 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
bufinfo.bytes =
|
||||
tmpinfo.hiwat * tmpinfo.blocksize - tmpinfo.play.seek;
|
||||
error = copyout(&bufinfo, SCARG(uap, data), sizeof bufinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETOSPACE = %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_GETISPACE:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
setblocksize(fp, &tmpinfo);
|
||||
bufinfo.fragsize = tmpinfo.blocksize;
|
||||
bufinfo.fragments = tmpinfo.hiwat -
|
||||
@ -429,55 +635,89 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
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)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETISPACE %d %d %d %d = %d\n",
|
||||
__func__, bufinfo.fragsize, bufinfo.fragments,
|
||||
bufinfo.fragstotal, bufinfo.bytes, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_NONBLOCK:
|
||||
idat = 1;
|
||||
error = ioctlf(fp, FIONBIO, &idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SENDCLT_DSP_NONBLOCK %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_GETCAPS:
|
||||
error = ioctlf(fp, AUDIO_GETPROPS, &idata);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETPROPS %d\n",
|
||||
__func__, 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));
|
||||
DPRINTF(("%s: SNDCL_DSP_GETCAPS %s duplex, %smmap\n",
|
||||
__func__, (idat & OSS_DSP_CAP_DUPLEX) ? "full" : "half",
|
||||
(idat & OSS_DSP_CAP_MMAP) ? "" : "no "));
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETCAPS %x = %d\n", __func__,
|
||||
idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
#if 0
|
||||
case OSS_SNDCTL_DSP_GETTRIGGER:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, 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)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SETRIGGER %x = %d\n",
|
||||
__func__, idat, 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)
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo, p);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, 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)
|
||||
error = ioctlf(fp, AUDIO_SETINFO, &tmpinfo);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_SETINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_SETRIGGER %x = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
#else
|
||||
case OSS_SNDCTL_DSP_GETTRIGGER:
|
||||
@ -485,54 +725,94 @@ oss_ioctl_audio(struct lwp *l, const struct oss_sys_ioctl_args *uap, register_t
|
||||
/* XXX Do nothing for now. */
|
||||
idat = OSS_PCM_ENABLE_OUTPUT;
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
goto out;
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_{GET,SET}RIGGER %x = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case OSS_SNDCTL_DSP_GETIPTR:
|
||||
error = ioctlf(fp, AUDIO_GETIOFFS, &tmpoffs);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETIOFFS %d\n",
|
||||
__func__, 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)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETIPTR %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_GETOPTR:
|
||||
error = ioctlf(fp, AUDIO_GETOOFFS, &tmpoffs);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETOOFFS %d\n",
|
||||
__func__, 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)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETOPTR %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_SETDUPLEX:
|
||||
idat = 1;
|
||||
error = ioctlf(fp, AUDIO_SETFD, &idat);
|
||||
goto out;
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_SETFD %d = %d\n",
|
||||
__func__, idat, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_MAPINBUF:
|
||||
DPRINTF(("%s: Unimplemented SNDCTL_DSP_MAPINBUF\n",
|
||||
__func__));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
case OSS_SNDCTL_DSP_MAPOUTBUF:
|
||||
DPRINTF(("%s: Unimplemented SNDCTL_DSP_MAPOUTBUF\n",
|
||||
__func__));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
case OSS_SNDCTL_DSP_SETSYNCRO:
|
||||
DPRINTF(("%s: Unimplemented SNDCTL_DSP_GETSYNCHRO\n",
|
||||
__func__));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
case OSS_SNDCTL_DSP_GETODELAY:
|
||||
error = ioctlf(fp, AUDIO_GETBUFINFO, &tmpinfo);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETBUFINFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
idat = tmpinfo.play.seek + tmpinfo.blocksize / 2;
|
||||
error = copyout(&idat, SCARG(uap, data), sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: SNDCTL_DSP_GETODELAY %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SNDCTL_DSP_PROFILE:
|
||||
/* This gives just a hint to the driver,
|
||||
* implementing it as a NOP is ok
|
||||
*/
|
||||
DPRINTF(("%s: SNDCTL_DSP_PROFILE\n", __func__));
|
||||
break;
|
||||
default:
|
||||
DPRINTF(("%s: Unimplemented 0x%lx\n", __func__, com));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -760,7 +1040,7 @@ oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_
|
||||
}
|
||||
|
||||
com = SCARG(uap, com);
|
||||
DPRINTF(("oss_ioctl_mixer: com=%08lx\n", com));
|
||||
DPRINTF(("%s: com=%s\n", __func__, compat_ossaudio_getcmd(com)));
|
||||
|
||||
retval[0] = 0;
|
||||
|
||||
@ -778,15 +1058,25 @@ oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_
|
||||
case OSS_SOUND_MIXER_INFO:
|
||||
case OSS_SOUND_OLD_MIXER_INFO:
|
||||
error = ioctlf(fp, AUDIO_GETDEV, &adev);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_GETDEV %d\n",
|
||||
__func__, 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;
|
||||
if (error) {
|
||||
DPRINTF(("%s: OSS_SOUND_MIXER_INFO %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
case OSS_SOUND_MIXER_READ_RECSRC:
|
||||
if (di->source == -1) {
|
||||
DPRINTF(("%s: OSS_SOUND_MIXER_READ_RECSRC bad source\n",
|
||||
__func__));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -794,16 +1084,22 @@ oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_
|
||||
if (di->caps & OSS_SOUND_CAP_EXCL_INPUT) {
|
||||
mc.type = AUDIO_MIXER_ENUM;
|
||||
error = ioctlf(fp, AUDIO_MIXER_READ, &mc);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_MIXER_READ %d\n",
|
||||
__func__, 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)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_MIXER_READ %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
e = opaque_to_enum(di, NULL, mc.un.mask);
|
||||
if (e >= 0)
|
||||
idat = 1 << di->rdevmap[e];
|
||||
@ -824,13 +1120,18 @@ oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_
|
||||
case OSS_SOUND_MIXER_WRITE_RECSRC:
|
||||
case OSS_SOUND_MIXER_WRITE_R_RECSRC:
|
||||
if (di->source == -1) {
|
||||
DPRINTF(("%s: OSS_SOUND_MIXER_WRITE_RECSRC bad "
|
||||
"source\n", __func__));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mc.dev = di->source;
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: OSS_SOUND_MIXER_WRITE_RECSRC %d\n",
|
||||
__func__, 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++)
|
||||
@ -839,6 +1140,8 @@ oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_
|
||||
if (i >= OSS_SOUND_MIXER_NRDEVICES ||
|
||||
di->devmap[i] == -1) {
|
||||
error = EINVAL;
|
||||
DPRINTF(("%s: OSS_SOUND_MIXER_WRITE_RECSRC "
|
||||
"bad index %d\n", __func__, i));
|
||||
goto out;
|
||||
}
|
||||
mc.un.ord = enum_to_ord(di, di->devmap[i]);
|
||||
@ -848,69 +1151,94 @@ oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_
|
||||
for(i = 0; i < OSS_SOUND_MIXER_NRDEVICES; i++) {
|
||||
if (idat & (1 << i)) {
|
||||
if (di->devmap[i] == -1) {
|
||||
DPRINTF(("%s: OSS_SOUND_MIXER_"
|
||||
"WRITE_RECSRC bad devmap "
|
||||
"%d\n", __func__, i));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
mc.un.mask |= enum_to_mask(di, di->devmap[i]);
|
||||
mc.un.mask |= enum_to_mask(di,
|
||||
di->devmap[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc);
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_MIXER_WRITE %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
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) {
|
||||
DPRINTF(("%s: 0x%lx bad devmap %d\n",
|
||||
__func__, com, n));
|
||||
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;
|
||||
mc.un.value.num_channels = di->stereomask &
|
||||
(1 << n) ? 2 : 1;
|
||||
error = ioctlf(fp, AUDIO_MIXER_READ, &mc);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_MIXER_READ %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
if (mc.un.value.num_channels != 2) {
|
||||
l = r = mc.un.value.level[AUDIO_MIXER_LEVEL_MONO];
|
||||
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));
|
||||
DPRINTF(("%s: n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
|
||||
__func__, 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))) {
|
||||
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) {
|
||||
DPRINTF(("%s: 0x%lx bad devmap %d\n",
|
||||
__func__, com, n));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
error = copyin(SCARG(uap, data), &idat, sizeof idat);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: 0x%lx error %d\n",
|
||||
__func__, com, 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)) {
|
||||
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;
|
||||
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));
|
||||
DPRINTF(("%s: n=%d (dev=%d) l=%d, r=%d, idat=%04x\n",
|
||||
__func__, n, di->devmap[n], l, r, idat));
|
||||
error = ioctlf(fp, AUDIO_MIXER_WRITE, &mc);
|
||||
if (error)
|
||||
if (error) {
|
||||
DPRINTF(("%s: AUDIO_MIXER_WRITE %d\n",
|
||||
__func__, error));
|
||||
goto out;
|
||||
}
|
||||
if (OSS_MIXER_WRITE(OSS_SOUND_MIXER_FIRST) <= com &&
|
||||
com < OSS_MIXER_WRITE(OSS_SOUND_MIXER_NRDEVICES)) {
|
||||
error = 0;
|
||||
@ -918,9 +1246,8 @@ oss_ioctl_mixer(struct lwp *lwp, const struct oss_sys_ioctl_args *uap, register_
|
||||
}
|
||||
goto doread;
|
||||
} else {
|
||||
#ifdef AUDIO_DEBUG
|
||||
printf("oss_audio: unknown mixer ioctl %04lx\n", com);
|
||||
#endif
|
||||
DPRINTF(("%s: Unknown mixer ioctl 0x%lx\n", __func__,
|
||||
com));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
@ -958,7 +1285,7 @@ oss_ioctl_sequencer(struct lwp *l, const struct oss_sys_ioctl_args *uap, registe
|
||||
}
|
||||
|
||||
com = SCARG(uap, com);
|
||||
DPRINTF(("oss_ioctl_sequencer: com=%08lx\n", com));
|
||||
DPRINTF(("%s: com=%s\n", __func__, compat_ossaudio_getcmd(com)));
|
||||
|
||||
retval[0] = 0;
|
||||
|
||||
@ -1132,6 +1459,8 @@ oss_ioctl_sequencer(struct lwp *l, const struct oss_sys_ioctl_args *uap, registe
|
||||
error = ioctlf(fp, SEQUENCER_TMR_SELECT, &idat);
|
||||
goto out;
|
||||
default:
|
||||
DPRINTF(("%s: Unknown sequencer command 0x%lx\n", __func__,
|
||||
com));
|
||||
error = EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user