extractedLnx/linux-2.5.9/drivers/sbus/audio/audio.c_sparcaudio_ioctl.c
static int sparcaudio_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
int retval = 0, i, j, k;
int minor = minor(inode->i_rdev);
struct audio_info ainfo;
audio_buf_info binfo;
count_info cinfo;
struct sparcaudio_driver *drv =
drivers[(minor >> SPARCAUDIO_DEVICE_SHIFT)];
switch (minor & 0xf) {
case SPARCAUDIO_MIXER_MINOR:
return sparcaudio_mixer_ioctl(inode, file, cmd, (unsigned int *)arg);
case SPARCAUDIO_DSP16_MINOR:
case SPARCAUDIO_DSP_MINOR:
case SPARCAUDIO_AUDIO_MINOR:
case SPARCAUDIO_AUDIOCTL_MINOR:
/* According to the OSS prog int, you can mixer ioctl /dev/dsp */
if (_IOC_TYPE(cmd) == 'M')
return sparcaudio_mixer_ioctl(inode,
file, cmd, (unsigned int *)arg);
switch (cmd) {
case I_GETSIG:
case I_GETSIG_SOLARIS:
j = (int) lis_get_elist_ent(drv->sd_siglist,current->pid);
put_user(j, (int *)arg);
retval = drv->input_count;
break;
case I_SETSIG:
case I_SETSIG_SOLARIS:
if ((minor & 0xf) == SPARCAUDIO_AUDIOCTL_MINOR) {
if (!arg) {
if (lis_del_from_elist(&(drv->sd_siglist),
current->pid,S_ALL)) {
retval = -EINVAL;
} else if (!drv->sd_siglist) {
drv->sd_sigflags=0;
}
} else if (lis_add_to_elist(&(drv->sd_siglist),
current->pid,
(short)arg)) {
retval = -EAGAIN;
} else {
((drv->sd_sigflags) |= (arg));
}
}
break;
case I_NREAD:
case I_NREAD_SOLARIS:
/* According to the Solaris man page, this copies out
* the size of the first streams buffer and returns
* the number of streams messages on the read queue as
* as its retval. (streamio(7I)) This should work.
*/
j = (drv->input_count > 0) ? drv->input_buffer_size : 0;
put_user(j, (int *)arg);
retval = drv->input_count;
break;
/* A poor substitute until we do true resizable buffers. */
case SNDCTL_DSP_GETISPACE:
binfo.fragstotal = drv->num_input_buffers;
binfo.fragments = drv->num_input_buffers -
(drv->input_count + drv->recording_count);
binfo.fragsize = drv->input_buffer_size;
binfo.bytes = binfo.fragments*binfo.fragsize;
retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(binfo));
if (retval)
break;
copy_to_user(&((char *)arg)[0], (char *)&binfo, sizeof(binfo));
break;
case SNDCTL_DSP_GETOSPACE:
binfo.fragstotal = drv->num_output_buffers;
binfo.fragments = drv->num_output_buffers -
(drv->output_count + drv->playing_count +
(drv->output_offset ? 1 : 0));
binfo.fragsize = drv->output_buffer_size;
binfo.bytes = binfo.fragments*binfo.fragsize +
(drv->output_buffer_size - drv->output_offset);
retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(binfo));
if (retval)
break;
copy_to_user(&((char *)arg)[0], (char *)&binfo, sizeof(binfo));
break;
case SNDCTL_DSP_GETIPTR:
case SNDCTL_DSP_GETOPTR:
/* int bytes (number of bytes read/written since last)
* int blocks (number of frags read/wrote since last call)
* int ptr (current position of dma in buffer)
*/
retval = 0;
cinfo.bytes = 0;
cinfo.ptr = 0;
cinfo.blocks = 0;
cinfo.bytes += cinfo.ptr;
retval = verify_area(VERIFY_WRITE, (int *)arg, sizeof(cinfo));
if (retval)
break;
copy_to_user(&((char *)arg)[0], (char *)&cinfo, sizeof(cinfo));
break;
case SNDCTL_DSP_SETFRAGMENT:
/* XXX Small hack to get ESD/Enlightenment to work. --DaveM */
retval = 0;
break;
case SNDCTL_DSP_SUBDIVIDE:
/* I don't understand what I need to do yet. */
retval = -EINVAL;
break;
case SNDCTL_DSP_SETTRIGGER:
/* This may not be 100% correct */
if ((arg & PCM_ENABLE_INPUT) && drv->ops->get_input_pause &&
drv->ops->set_input_pause) {
if (drv->ops->get_input_pause(drv))
drv->ops->set_input_pause(drv, 0);
} else {
if (!drv->ops->get_input_pause(drv))
drv->ops->set_input_pause(drv, 1);
}
if ((arg & PCM_ENABLE_OUTPUT) && drv->ops->get_output_pause &&
drv->ops->set_output_pause) {
if (drv->ops->get_output_pause(drv))
drv->ops->set_output_pause(drv, 0);
} else {
if (!drv->ops->get_output_pause(drv))
drv->ops->set_output_pause(drv, 1);
}
break;
case SNDCTL_DSP_GETTRIGGER:
j = 0;
if (drv->ops->get_input_pause) {
if (drv->ops->get_input_pause(drv))
j = PCM_ENABLE_INPUT;
}
if (drv->ops->get_output_pause) {
if (drv->ops->get_output_pause(drv))
j |= PCM_ENABLE_OUTPUT;
}
put_user(j, (int *)arg);
break;
case SNDCTL_DSP_GETBLKSIZE:
j = drv->input_buffer_size;
put_user(j, (int *)arg);
break;
case SNDCTL_DSP_SPEED:
if ((!drv->ops->set_output_rate) &&
(!drv->ops->set_input_rate)) {
retval = -EINVAL;
break;
}
get_user(i, (int *)arg)
tprintk(("setting speed to %d\n", i));
drv->ops->set_input_rate(drv, i);
drv->ops->set_output_rate(drv, i);
j = drv->ops->get_output_rate(drv);
put_user(j, (int *)arg);
break;
case SNDCTL_DSP_GETCAPS:
/* All Sparc audio hardware is full duplex.
* 4231 supports DMA pointer reading, 7930 is byte at a time.
* Pause functionality emulates trigger
*/
j = DSP_CAP_DUPLEX | DSP_CAP_TRIGGER | DSP_CAP_REALTIME;
put_user(j, (int *)arg);
break;
case SNDCTL_DSP_GETFMTS:
if (drv->ops->get_formats) {
j = drv->ops->get_formats(drv);
put_user(j, (int *)arg);
} else {
retval = -EINVAL;
}
break;
case SNDCTL_DSP_SETFMT:
/* need to decode into encoding, precision */
get_user(i, (int *)arg);
/* handle special case here */
if (i == AFMT_QUERY) {
j = drv->ops->get_output_encoding(drv);
k = drv->ops->get_output_precision(drv);
if (j == AUDIO_ENCODING_DVI) {
i = AFMT_IMA_ADPCM;
} else if (k == 8) {
switch (j) {
case AUDIO_ENCODING_ULAW:
i = AFMT_MU_LAW;
break;
case AUDIO_ENCODING_ALAW:
i = AFMT_A_LAW;
break;
case AUDIO_ENCODING_LINEAR8:
i = AFMT_U8;
break;
};
} else if (k == 16) {
switch (j) {
case AUDIO_ENCODING_LINEAR:
i = AFMT_S16_BE;
break;
case AUDIO_ENCODING_LINEARLE:
i = AFMT_S16_LE;
break;
};
}
put_user(i, (int *)arg);
break;
}
/* Without these there's no point in trying */
if (!drv->ops->set_input_precision ||
!drv->ops->set_input_encoding ||
!drv->ops->set_output_precision ||
!drv->ops->set_output_encoding) {
eprintk(("missing set routines: failed\n"));
retval = -EINVAL;
break;
}
if (drv->ops->get_formats) {
if (!(drv->ops->get_formats(drv) & i)) {
dprintk(("format not supported\n"));
return -EINVAL;
}
}
switch (i) {
case AFMT_S16_LE:
ainfo.record.precision = ainfo.play.precision = 16;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_LINEARLE;
break;
case AFMT_S16_BE:
ainfo.record.precision = ainfo.play.precision = 16;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_LINEAR;
break;
case AFMT_MU_LAW:
ainfo.record.precision = ainfo.play.precision = 8;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_ULAW;
break;
case AFMT_A_LAW:
ainfo.record.precision = ainfo.play.precision = 8;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_ALAW;
break;
case AFMT_U8:
ainfo.record.precision = ainfo.play.precision = 8;
ainfo.record.encoding = ainfo.play.encoding =
AUDIO_ENCODING_LINEAR8;
break;
};
tprintk(("setting fmt to enc %d pr %d\n",
ainfo.play.encoding,
ainfo.play.precision));
if ((drv->ops->set_input_precision(drv,
ainfo.record.precision)
< 0) ||
(drv->ops->set_output_precision(drv,
ainfo.play.precision)
< 0) ||
(drv->ops->set_input_encoding(drv,
ainfo.record.encoding)
< 0) ||
(drv->ops->set_output_encoding(drv,
ainfo.play.encoding)
< 0)) {
dprintk(("setting format: failed\n"));
return -EINVAL;
}
put_user(i, (int *)arg);
break;
case SNDCTL_DSP_CHANNELS:
if ((!drv->ops->set_output_channels) &&
(!drv->ops->set_input_channels)) {
retval = -EINVAL;
break;
}
get_user(i, (int *)arg);
drv->ops->set_input_channels(drv, i);
drv->ops->set_output_channels(drv, i);
i = drv->ops->get_output_channels(drv);
put_user(i, (int *)arg);
break;
case SNDCTL_DSP_STEREO:
if ((!drv->ops->set_output_channels) &&
(!drv->ops->set_input_channels)) {
retval = -EINVAL;
break;
}
get_user(i, (int *)arg);
drv->ops->set_input_channels(drv, (i + 1));
drv->ops->set_output_channels(drv, (i + 1));
i = ((drv->ops->get_output_channels(drv)) - 1);
put_user(i, (int *)arg);
break;
case SNDCTL_DSP_POST:
case SNDCTL_DSP_SYNC:
case AUDIO_DRAIN:
/* Deal with weirdness so we can fill buffers */
if (drv->output_offset) {
drv->output_offset = 0;
drv->output_rear = (drv->output_rear + 1)
% drv->num_output_buffers;
drv->output_count++;
}
if (drv->output_count > 0) {
sparcaudio_sync_output(drv);
/* Only pause for DRAIN/SYNC, not POST */
if (cmd != SNDCTL_DSP_POST) {
interruptible_sleep_on(&drv->output_drain_wait);
retval = (signal_pending(current)) ? -EINTR : 0;
}
}
break;
case I_FLUSH:
case I_FLUSH_SOLARIS:
if (((unsigned int)arg == FLUSHW) ||
((unsigned int)arg == FLUSHRW)) {
if (file->f_mode & FMODE_WRITE) {
sparcaudio_sync_output(drv);
if (drv->output_active) {
wake_up_interruptible(&drv->output_write_wait);
drv->ops->stop_output(drv);
}
drv->output_offset = 0;
drv->output_active = 0;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_size = 0;
drv->playing_count = 0;
drv->output_eof = 0;
}
}
if (((unsigned int)arg == FLUSHR) ||
((unsigned int)arg == FLUSHRW)) {
if (drv->input_active && (file->f_mode & FMODE_READ)) {
wake_up_interruptible(&drv->input_read_wait);
drv->ops->stop_input(drv);
drv->input_active = 0;
drv->input_front = 0;
drv->input_rear = 0;
drv->input_count = 0;
drv->input_size = 0;
drv->input_offset = 0;
drv->recording_count = 0;
}
if ((file->f_mode & FMODE_READ) &&
(drv->flags & SDF_OPEN_READ)) {
if (drv->duplex == 2)
drv->input_count = drv->output_count;
drv->ops->start_input(drv,
drv->input_buffers[drv->input_front],
drv->input_buffer_size);
drv->input_active = 1;
}
}
if (((unsigned int)arg == FLUSHW) ||
((unsigned int)arg == FLUSHRW)) {
if ((file->f_mode & FMODE_WRITE) &&
!(drv->flags & SDF_OPEN_WRITE)) {
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
sparcaudio_sync_output(drv);
}
}
break;
case SNDCTL_DSP_RESET:
case AUDIO_FLUSH:
if (drv->output_active && (file->f_mode & FMODE_WRITE)) {
wake_up_interruptible(&drv->output_write_wait);
drv->ops->stop_output(drv);
drv->output_active = 0;
drv->output_front = 0;
drv->output_rear = 0;
drv->output_count = 0;
drv->output_size = 0;
drv->playing_count = 0;
drv->output_offset = 0;
drv->output_eof = 0;
}
if (drv->input_active && (file->f_mode & FMODE_READ)) {
wake_up_interruptible(&drv->input_read_wait);
drv->ops->stop_input(drv);
drv->input_active = 0;
drv->input_front = 0;
drv->input_rear = 0;
drv->input_count = 0;
drv->input_size = 0;
drv->input_offset = 0;
drv->recording_count = 0;
}
if ((file->f_mode & FMODE_READ) &&
!(drv->flags & SDF_OPEN_READ)) {
drv->ops->start_input(drv,
drv->input_buffers[drv->input_front],
drv->input_buffer_size);
drv->input_active = 1;
}
if ((file->f_mode & FMODE_WRITE) &&
!(drv->flags & SDF_OPEN_WRITE)) {
sparcaudio_sync_output(drv);
}
break;
case AUDIO_GETDEV:
if (drv->ops->sunaudio_getdev) {
audio_device_t tmp;
retval = verify_area(VERIFY_WRITE, (void *)arg,
sizeof(audio_device_t));
if (!retval)
drv->ops->sunaudio_getdev(drv, &tmp);
copy_to_user((audio_device_t *)arg, &tmp, sizeof(tmp));
} else {
retval = -EINVAL;
}
break;
case AUDIO_GETDEV_SUNOS:
if (drv->ops->sunaudio_getdev_sunos) {
int tmp = drv->ops->sunaudio_getdev_sunos(drv);
retval = verify_area(VERIFY_WRITE, (void *)arg, sizeof(int));
if (!retval)
copy_to_user((int *)arg, &tmp, sizeof(tmp));
} else {
retval = -EINVAL;
}
break;
case AUDIO_GETINFO:
AUDIO_INITINFO(&ainfo);
if (drv->ops->get_input_rate)
ainfo.record.sample_rate =
drv->ops->get_input_rate(drv);
else
ainfo.record.sample_rate = (8000);
if (drv->ops->get_input_channels)
ainfo.record.channels =
drv->ops->get_input_channels(drv);
else
ainfo.record.channels = (1);
if (drv->ops->get_input_precision)
ainfo.record.precision =
drv->ops->get_input_precision(drv);
else
ainfo.record.precision = (8);
if (drv->ops->get_input_encoding)
ainfo.record.encoding =
drv->ops->get_input_encoding(drv);
else
ainfo.record.encoding = (AUDIO_ENCODING_ULAW);
if (drv->ops->get_input_volume)
ainfo.record.gain =
drv->ops->get_input_volume(drv);
else
ainfo.record.gain = (0);
if (drv->ops->get_input_port)
ainfo.record.port =
drv->ops->get_input_port(drv);
else
ainfo.record.port = (0);
if (drv->ops->get_input_ports)
ainfo.record.avail_ports =
drv->ops->get_input_ports(drv);
else
ainfo.record.avail_ports = (0);
/* To make e.g. vat happy, we let them think they control this */
ainfo.record.buffer_size = drv->buffer_size;
if (drv->ops->get_input_samples)
ainfo.record.samples = drv->ops->get_input_samples(drv);
else
ainfo.record.samples = 0;
/* This is undefined in the record context in Solaris */
ainfo.record.eof = 0;
if (drv->ops->get_input_pause)
ainfo.record.pause =
drv->ops->get_input_pause(drv);
else
ainfo.record.pause = 0;
if (drv->ops->get_input_error)
ainfo.record.error =
(unsigned char) drv->ops->get_input_error(drv);
else
ainfo.record.error = 0;
ainfo.record.waiting = 0;
if (drv->ops->get_input_balance)
ainfo.record.balance =
(unsigned char) drv->ops->get_input_balance(drv);
else
ainfo.record.balance = (unsigned char)(AUDIO_MID_BALANCE);
ainfo.record.minordev = 4 + (minor << SPARCAUDIO_DEVICE_SHIFT);
ainfo.record.open = (drv->flags & SDF_OPEN_READ);
ainfo.record.active = 0;
if (drv->ops->get_output_rate)
ainfo.play.sample_rate =
drv->ops->get_output_rate(drv);
else
ainfo.play.sample_rate = (8000);
if (drv->ops->get_output_channels)
ainfo.play.channels =
drv->ops->get_output_channels(drv);
else
ainfo.play.channels = (1);
if (drv->ops->get_output_precision)
ainfo.play.precision =
drv->ops->get_output_precision(drv);
else
ainfo.play.precision = (8);
if (drv->ops->get_output_encoding)
ainfo.play.encoding =
drv->ops->get_output_encoding(drv);
else
ainfo.play.encoding = (AUDIO_ENCODING_ULAW);
if (drv->ops->get_output_volume)
ainfo.play.gain =
drv->ops->get_output_volume(drv);
else
ainfo.play.gain = (0);
if (drv->ops->get_output_port)
ainfo.play.port =
drv->ops->get_output_port(drv);
else
ainfo.play.port = (0);
if (drv->ops->get_output_ports)
ainfo.play.avail_ports =
drv->ops->get_output_ports(drv);
else
ainfo.play.avail_ports = (0);
/* This is not defined in the play context in Solaris */
ainfo.play.buffer_size = 0;
if (drv->ops->get_output_samples)
ainfo.play.samples = drv->ops->get_output_samples(drv);
else
ainfo.play.samples = 0;
ainfo.play.eof = drv->output_eof;
if (drv->ops->get_output_pause)
ainfo.play.pause =
drv->ops->get_output_pause(drv);
else
ainfo.play.pause = 0;
if (drv->ops->get_output_error)
ainfo.play.error =
(unsigned char)drv->ops->get_output_error(drv);
else
ainfo.play.error = 0;
ainfo.play.waiting = waitqueue_active(&drv->open_wait);
if (drv->ops->get_output_balance)
ainfo.play.balance =
(unsigned char)drv->ops->get_output_balance(drv);
else
ainfo.play.balance = (unsigned char)(AUDIO_MID_BALANCE);
ainfo.play.minordev = 4 + (minor << SPARCAUDIO_DEVICE_SHIFT);
ainfo.play.open = (drv->flags & SDF_OPEN_WRITE);
ainfo.play.active = drv->output_active;
if (drv->ops->get_monitor_volume)
ainfo.monitor_gain =
drv->ops->get_monitor_volume(drv);
else
ainfo.monitor_gain = (0);
if (drv->ops->get_output_muted)
ainfo.output_muted =
(unsigned char)drv->ops->get_output_muted(drv);
else
ainfo.output_muted = (unsigned char)(0);
retval = verify_area(VERIFY_WRITE, (void *)arg,
sizeof(struct audio_info));
if (retval < 0)
break;
copy_to_user((struct audio_info *)arg, &ainfo, sizeof(ainfo));
break;
case AUDIO_SETINFO:
{
audio_info_t curinfo, newinfo;
if (verify_area(VERIFY_READ, (audio_info_t *)arg,
sizeof(audio_info_t))) {
dprintk(("verify_area failed\n"));
return -EINVAL;
}
copy_from_user(&ainfo, (audio_info_t *)arg, sizeof(audio_info_t));
/* Without these there's no point in trying */
if (!drv->ops->get_input_precision ||
!drv->ops->get_input_channels ||
!drv->ops->get_input_rate ||
!drv->ops->get_input_encoding ||
!drv->ops->get_output_precision ||
!drv->ops->get_output_channels ||
!drv->ops->get_output_rate ||
!drv->ops->get_output_encoding) {
eprintk(("missing get routines: failed\n"));
retval = -EINVAL;
break;
}
/* Do bounds checking for things which always apply.
* Follow with enforcement of basic tenets of certain
* encodings. Everything over and above generic is
* enforced by the driver, which can assume that
* Martian cases are taken care of here.
*/
if (Modify(ainfo.play.gain) &&
((ainfo.play.gain > AUDIO_MAX_GAIN) ||
(ainfo.play.gain < AUDIO_MIN_GAIN))) {
/* Need to differentiate this from e.g. the above error */
eprintk(("play gain bounds: failed %d\n", ainfo.play.gain));
retval = -EINVAL;
break;
}
if (Modify(ainfo.record.gain) &&
((ainfo.record.gain > AUDIO_MAX_GAIN) ||
(ainfo.record.gain < AUDIO_MIN_GAIN))) {
eprintk(("rec gain bounds: failed %d\n", ainfo.record.gain));
retval = -EINVAL;
break;
}
if (Modify(ainfo.monitor_gain) &&
((ainfo.monitor_gain > AUDIO_MAX_GAIN) ||
(ainfo.monitor_gain < AUDIO_MIN_GAIN))) {
eprintk(("monitor gain bounds: failed\n"));
retval = -EINVAL;
break;
}
/* Don't need to check less than zero on these */
if (Modifyc(ainfo.play.balance) &&
(ainfo.play.balance > AUDIO_RIGHT_BALANCE)) {
eprintk(("play balance bounds: %d failed\n",
(int)ainfo.play.balance));
retval = -EINVAL;
break;
}
if (Modifyc(ainfo.record.balance) &&
(ainfo.record.balance > AUDIO_RIGHT_BALANCE)) {
eprintk(("rec balance bounds: failed\n"));
retval = -EINVAL;
break;
}
/* If any of these changed, record them all, then make
* changes atomically. If something fails, back it all out.
*/
if (Modify(ainfo.record.precision) ||
Modify(ainfo.record.sample_rate) ||
Modify(ainfo.record.channels) ||
Modify(ainfo.record.encoding) ||
Modify(ainfo.play.precision) ||
Modify(ainfo.play.sample_rate) ||
Modify(ainfo.play.channels) ||
Modify(ainfo.play.encoding)) {
/* If they're trying to change something we
* have no routine for, they lose.
*/
if ((!drv->ops->set_input_encoding &&
Modify(ainfo.record.encoding)) ||
(!drv->ops->set_input_rate &&
Modify(ainfo.record.sample_rate)) ||
(!drv->ops->set_input_precision &&
Modify(ainfo.record.precision)) ||
(!drv->ops->set_input_channels &&
Modify(ainfo.record.channels))) {
eprintk(("rec set no routines: failed\n"));
retval = -EINVAL;
break;
}
curinfo.record.encoding =
drv->ops->get_input_encoding(drv);
curinfo.record.sample_rate =
drv->ops->get_input_rate(drv);
curinfo.record.precision =
drv->ops->get_input_precision(drv);
curinfo.record.channels =
drv->ops->get_input_channels(drv);
newinfo.record.encoding =
Modify(ainfo.record.encoding) ?
ainfo.record.encoding :
curinfo.record.encoding;
newinfo.record.sample_rate =
Modify(ainfo.record.sample_rate) ?
ainfo.record.sample_rate :
curinfo.record.sample_rate;
newinfo.record.precision =
Modify(ainfo.record.precision) ?
ainfo.record.precision :
curinfo.record.precision;
newinfo.record.channels =
Modify(ainfo.record.channels) ?
ainfo.record.channels :
curinfo.record.channels;
switch (newinfo.record.encoding) {
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_ULAW:
if (newinfo.record.precision != 8) {
eprintk(("rec law precision bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1) {
eprintk(("rec law channel bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR:
case AUDIO_ENCODING_LINEARLE:
if (newinfo.record.precision != 16) {
eprintk(("rec lin precision bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1 &&
newinfo.record.channels != 2) {
eprintk(("rec lin channel bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR8:
if (newinfo.record.precision != 8) {
eprintk(("rec lin8 precision bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.record.channels != 1 &&
newinfo.record.channels != 2) {
eprintk(("rec lin8 channel bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
};
if (retval < 0)
break;
/* If they're trying to change something we
* have no routine for, they lose.
*/
if ((!drv->ops->set_output_encoding &&
Modify(ainfo.play.encoding)) ||
(!drv->ops->set_output_rate &&
Modify(ainfo.play.sample_rate)) ||
(!drv->ops->set_output_precision &&
Modify(ainfo.play.precision)) ||
(!drv->ops->set_output_channels &&
Modify(ainfo.play.channels))) {
eprintk(("play set no routine: failed\n"));
retval = -EINVAL;
break;
}
curinfo.play.encoding =
drv->ops->get_output_encoding(drv);
curinfo.play.sample_rate =
drv->ops->get_output_rate(drv);
curinfo.play.precision =
drv->ops->get_output_precision(drv);
curinfo.play.channels =
drv->ops->get_output_channels(drv);
newinfo.play.encoding =
Modify(ainfo.play.encoding) ?
ainfo.play.encoding :
curinfo.play.encoding;
newinfo.play.sample_rate =
Modify(ainfo.play.sample_rate) ?
ainfo.play.sample_rate :
curinfo.play.sample_rate;
newinfo.play.precision =
Modify(ainfo.play.precision) ?
ainfo.play.precision :
curinfo.play.precision;
newinfo.play.channels =
Modify(ainfo.play.channels) ?
ainfo.play.channels :
curinfo.play.channels;
switch (newinfo.play.encoding) {
case AUDIO_ENCODING_ALAW:
case AUDIO_ENCODING_ULAW:
if (newinfo.play.precision != 8) {
eprintk(("play law precision bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1) {
eprintk(("play law channel bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR:
case AUDIO_ENCODING_LINEARLE:
if (newinfo.play.precision != 16) {
eprintk(("play lin precision bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1 &&
newinfo.play.channels != 2) {
eprintk(("play lin channel bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
break;
case AUDIO_ENCODING_LINEAR8:
if (newinfo.play.precision != 8) {
eprintk(("play lin8 precision bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
if (newinfo.play.channels != 1 &&
newinfo.play.channels != 2) {
eprintk(("play lin8 channel bounds: "
"failed\n"));
retval = -EINVAL;
break;
}
};
if (retval < 0)
break;
/* If we got this far, we're at least sane with
* respect to generics. Try the changes.
*/
if ((drv->ops->set_input_channels &&
(drv->ops->set_input_channels(drv,
newinfo.record.channels)
< 0)) ||
(drv->ops->set_output_channels &&
(drv->ops->set_output_channels(drv,
newinfo.play.channels)
< 0)) ||
(drv->ops->set_input_rate &&
(drv->ops->set_input_rate(drv,
newinfo.record.sample_rate)
< 0)) ||
(drv->ops->set_output_rate &&
(drv->ops->set_output_rate(drv,
newinfo.play.sample_rate)
< 0)) ||
(drv->ops->set_input_precision &&
(drv->ops->set_input_precision(drv,
newinfo.record.precision)
< 0)) ||
(drv->ops->set_output_precision &&
(drv->ops->set_output_precision(drv,
newinfo.play.precision)
< 0)) ||
(drv->ops->set_input_encoding &&
(drv->ops->set_input_encoding(drv,
newinfo.record.encoding)
< 0)) ||
(drv->ops->set_output_encoding &&
(drv->ops->set_output_encoding(drv,
newinfo.play.encoding)
< 0)))
{
dprintk(("setting format: failed\n"));
/* Pray we can set it all back. If not, uh... */
if (drv->ops->set_input_channels)
drv->ops->set_input_channels(drv,
curinfo.record.channels);
if (drv->ops->set_output_channels)
drv->ops->set_output_channels(drv,
curinfo.play.channels);
if (drv->ops->set_input_rate)
drv->ops->set_input_rate(drv,
curinfo.record.sample_rate);
if (drv->ops->set_output_rate)
drv->ops->set_output_rate(drv,
curinfo.play.sample_rate);
if (drv->ops->set_input_precision)
drv->ops->set_input_precision(drv,
curinfo.record.precision);
if (drv->ops->set_output_precision)
drv->ops->set_output_precision(drv,
curinfo.play.precision);
if (drv->ops->set_input_encoding)
drv->ops->set_input_encoding(drv,
curinfo.record.encoding);
if (drv->ops->set_output_encoding)
drv->ops->set_output_encoding(drv,
curinfo.play.encoding);
retval = -EINVAL;
break;
}
}
if (retval < 0)
break;
newinfo.record.balance =
__sparcaudio_if_setc_do(drv,
drv->ops->set_input_balance,
drv->ops->get_input_balance,
ainfo.record.balance);
newinfo.play.balance =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_balance,
drv->ops->get_output_balance,
ainfo.play.balance);
newinfo.record.error =
__sparcaudio_if_setc_do(drv,
drv->ops->set_input_error,
drv->ops->get_input_error,
ainfo.record.error);
newinfo.play.error =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_error,
drv->ops->get_output_error,
ainfo.play.error);
newinfo.output_muted =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_muted,
drv->ops->get_output_muted,
ainfo.output_muted);
newinfo.record.gain =
__sparcaudio_if_set_do(drv,
drv->ops->set_input_volume,
drv->ops->get_input_volume,
ainfo.record.gain);
newinfo.play.gain =
__sparcaudio_if_set_do(drv,
drv->ops->set_output_volume,
drv->ops->get_output_volume,
ainfo.play.gain);
newinfo.record.port =
__sparcaudio_if_set_do(drv,
drv->ops->set_input_port,
drv->ops->get_input_port,
ainfo.record.port);
newinfo.play.port =
__sparcaudio_if_set_do(drv,
drv->ops->set_output_port,
drv->ops->get_output_port,
ainfo.play.port);
newinfo.record.samples =
__sparcaudio_if_set_do(drv,
drv->ops->set_input_samples,
drv->ops->get_input_samples,
ainfo.record.samples);
newinfo.play.samples =
__sparcaudio_if_set_do(drv,
drv->ops->set_output_samples,
drv->ops->get_output_samples,
ainfo.play.samples);
newinfo.monitor_gain =
__sparcaudio_if_set_do(drv,
drv->ops->set_monitor_volume,
drv->ops->get_monitor_volume,
ainfo.monitor_gain);
if (Modify(ainfo.record.buffer_size)) {
/* Should sanity check this */
newinfo.record.buffer_size = ainfo.record.buffer_size;
drv->buffer_size = ainfo.record.buffer_size;
} else {
newinfo.record.buffer_size = drv->buffer_size;
}
if (Modify(ainfo.play.eof)) {
ainfo.play.eof = newinfo.play.eof;
newinfo.play.eof = drv->output_eof;
drv->output_eof = ainfo.play.eof;
} else {
newinfo.play.eof = drv->output_eof;
}
if (drv->flags & SDF_OPEN_READ) {
newinfo.record.pause =
__sparcaudio_if_setc_do(drv,
drv->ops->set_input_pause,
drv->ops->get_input_pause,
ainfo.record.pause);
} else if (drv->ops->get_input_pause) {
newinfo.record.pause = drv->ops->get_input_pause(drv);
} else {
newinfo.record.pause = 0;
}
if (drv->flags & SDF_OPEN_WRITE) {
newinfo.play.pause =
__sparcaudio_if_setc_do(drv,
drv->ops->set_output_pause,
drv->ops->get_output_pause,
ainfo.play.pause);
} else if (drv->ops->get_output_pause) {
newinfo.play.pause = drv->ops->get_output_pause(drv);
} else {
newinfo.play.pause = 0;
}
retval = verify_area(VERIFY_WRITE, (void *)arg,
sizeof(struct audio_info));
/* Even if we fail, if we made changes let's try notification */
if (!retval)
copy_to_user((struct audio_info *)arg, &newinfo,
sizeof(newinfo));
#ifdef REAL_AUDIO_SIGNALS
kill_procs(drv->sd_siglist,SIGPOLL,S_MSG);
#endif
break;
}
default:
if (drv->ops->ioctl)
retval = drv->ops->ioctl(inode,file,cmd,arg,drv);
else
retval = -EINVAL;
};
break;
case SPARCAUDIO_STATUS_MINOR:
eprintk(("status minor not yet implemented\n"));
retval = -EINVAL;
default:
eprintk(("unknown minor device number\n"));
retval = -EINVAL;
}
return retval;
}
Generated by GNU enscript 1.6.4.