/* * sound/audio.c * * Device file manager for /dev/audio * * Copyright by Hannu Savolainen 1993 * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. 2. * Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ #include #ifdef CONFIG_AUDIO #include #include #define ON 1 #define OFF 0 int DMAbuf_poll(int dev, struct fileinfo * file, int events, select_table * wait); int audio_poll(int dev, struct fileinfo * file, int events, select_table * wait); static int wr_buff_no[MAX_AUDIO_DEV]; /* != -1, if there is a incomplete output block in the queue. */ static int wr_buff_size[MAX_AUDIO_DEV], wr_buff_ptr[MAX_AUDIO_DEV]; static int audio_mode[MAX_AUDIO_DEV]; static int dev_nblock[MAX_AUDIO_DEV]; /* 1 if in noblocking mode */ #define AM_NONE 0 #define AM_WRITE 1 #define AM_READ 2 static char *wr_dma_buf[MAX_AUDIO_DEV]; static int audio_format[MAX_AUDIO_DEV]; static int local_conversion[MAX_AUDIO_DEV]; static int set_format(int dev, int fmt) { if (fmt != AFMT_QUERY) { local_conversion[dev] = 0; if (!(audio_devs[dev]->format_mask & fmt)) { /* Not supported */ if (fmt == AFMT_MU_LAW) { fmt = AFMT_U8; local_conversion[dev] = AFMT_MU_LAW; } else fmt = AFMT_U8; /* This is always supported */ } audio_format[dev] = DMAbuf_ioctl(dev, SNDCTL_DSP_SETFMT, (ioctl_arg) fmt, 1); } if (local_conversion[dev]) /* This shadows the HW format */ return local_conversion[dev]; return audio_format[dev]; } int audio_open(int dev, struct fileinfo * file) { int ret; int bits; int dev_type = dev & 0x0f; int mode = file->mode & O_ACCMODE; dev = dev >> 4; bits = (dev_type == SND_DEV_DSP16) ? 16 : 8 ; if ((ret = DMAbuf_open(dev, mode)) < 0) return ret; if (audio_devs[dev]->coproc) if ((ret = audio_devs[dev]->coproc-> open(audio_devs[dev]->coproc->devc, COPR_PCM)) < 0) { audio_release(dev, file); printf("Sound: Can't access coprocessor device\n"); return ret; } local_conversion[dev] = 0; if (DMAbuf_ioctl(dev, SNDCTL_DSP_SETFMT, (ioctl_arg) bits, 1) != bits) { audio_release(dev, file); return -(ENXIO); } set_format(dev, (dev_type == SND_DEV_AUDIO) ? AFMT_MU_LAW : bits ) ; wr_buff_no[dev] = -1; audio_mode[dev] = AM_NONE; wr_buff_size[dev] = wr_buff_ptr[dev] = 0; dev_nblock[dev] = 0; return ret; } void audio_release(int dev, struct fileinfo * file) { int mode; dev = dev >> 4; mode = file->mode & O_ACCMODE; if (wr_buff_no[dev] >= 0) { DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } if (audio_devs[dev]->coproc) audio_devs[dev]->coproc->close(audio_devs[dev]->coproc->devc,COPR_PCM); DMAbuf_release(dev, mode); audio_devs[dev]->dmap_out->mapping_flags &= ~DMA_MAP_MAPPED ; } static void translate_bytes(const u_char *table, u_char *buff, int n) { u_long i; if (n <= 0) return; for (i = 0; i < n; ++i) buff[i] = table[buff[i]]; } int audio_write(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) { int c, p, l; int err; dev = dev >> 4; p = 0; c = count; if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) { /* Direction change */ wr_buff_no[dev] = -1; } if (audio_devs[dev]->flags & DMA_DUPLEX) audio_mode[dev] |= AM_WRITE; else audio_mode[dev] = AM_WRITE; if (!count) { /* Flush output */ if (wr_buff_no[dev] >= 0) { DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } return 0; } while (c) { /* Perform output blocking */ if (wr_buff_no[dev] < 0) { /* There is no incomplete buffers */ if ((wr_buff_no[dev] = DMAbuf_getwrbuffer(dev, &wr_dma_buf[dev], &wr_buff_size[dev], dev_nblock[dev])) < 0) { /* Handle nonblocking mode */ if (dev_nblock[dev] && wr_buff_no[dev] == -(EAGAIN)) return p; /* No more space. Return # of accepted bytes */ return wr_buff_no[dev]; } wr_buff_ptr[dev] = 0; } l = c; if (l > (wr_buff_size[dev] - wr_buff_ptr[dev])) l = (wr_buff_size[dev] - wr_buff_ptr[dev]); if (!audio_devs[dev]->copy_from_user) { /* No device specific * copy routine */ if (uiomove(&wr_dma_buf[dev][wr_buff_ptr[dev]], l, buf)) { printf("sb: Bad copyin()!\n"); }; } else audio_devs[dev]->copy_from_user(dev, wr_dma_buf[dev], wr_buff_ptr[dev], buf, p, l); /* * Insert local processing here */ if (local_conversion[dev] == AFMT_MU_LAW) { translate_bytes(ulaw_dsp, (u_char *) &wr_dma_buf[dev][wr_buff_ptr[dev]], l); } c -= l; p += l; wr_buff_ptr[dev] += l; if (wr_buff_ptr[dev] >= wr_buff_size[dev]) { if ((err = DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev])) < 0) { return err; } wr_buff_no[dev] = -1; } } return count; } int audio_read(int dev, struct fileinfo * file, snd_rw_buf * buf, int count) { int c, p, l; char *dmabuf; int buff_no; dev = dev >> 4; p = 0; c = count; if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) { if (wr_buff_no[dev] >= 0) { DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); if (!(audio_devs[dev]->flags & DMA_DUPLEX)) wr_buff_no[dev] = -1; } } if (audio_devs[dev]->flags & DMA_DUPLEX) audio_mode[dev] |= AM_READ; else audio_mode[dev] = AM_READ; while (c) { if ((buff_no = DMAbuf_getrdbuffer(dev, &dmabuf, &l, dev_nblock[dev])) < 0) { /* * Nonblocking mode handling. Return current # of bytes */ if (dev_nblock[dev] && buff_no == -(EAGAIN)) return p; return buff_no; } if (l > c) l = c; /* * Insert any local processing here. */ if (local_conversion[dev] == AFMT_MU_LAW) { translate_bytes(dsp_ulaw, (u_char *) dmabuf, l); } if (uiomove(dmabuf, l, buf)) { printf("sb: Bad copyout()!\n"); }; DMAbuf_rmchars(dev, buff_no, l); p += l; c -= l; } return count - c; } int audio_ioctl(int dev, struct fileinfo * file, u_int cmd, ioctl_arg arg) { dev = dev >> 4; if (((cmd >> 8) & 0xff) == 'C') { if (audio_devs[dev]->coproc) /* Coprocessor ioctl */ return audio_devs[dev]->coproc->ioctl(audio_devs[dev]->coproc->devc, cmd, arg, 0); else printf("/dev/dsp%d: No coprocessor for this device\n", dev); return -(ENXIO); } else switch (cmd) { case SNDCTL_DSP_SYNC: if (wr_buff_no[dev] >= 0) { DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } return DMAbuf_ioctl(dev, cmd, arg, 0); break; case SNDCTL_DSP_POST: if (wr_buff_no[dev] >= 0) { DMAbuf_start_output(dev, wr_buff_no[dev], wr_buff_ptr[dev]); wr_buff_no[dev] = -1; } return 0; break; case SNDCTL_DSP_RESET: wr_buff_no[dev] = -1; audio_mode[dev] = AM_NONE; return DMAbuf_ioctl(dev, cmd, arg, 0); break; case SNDCTL_DSP_GETFMTS: return *(int *) arg = audio_devs[dev]->format_mask; break; case SNDCTL_DSP_SETFMT: return *(int *) arg = set_format(dev, (*(int *) arg)); case SNDCTL_DSP_GETISPACE: if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -(EBUSY); else { audio_buf_info info; int err = DMAbuf_ioctl(dev, cmd, (ioctl_arg) & info, 1); if (err < 0) return err; bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); return 0; } case SNDCTL_DSP_GETOSPACE: if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -(EBUSY); else { audio_buf_info info; int err = DMAbuf_ioctl(dev, cmd, (ioctl_arg) & info, 1); if (err < 0) return err; if (wr_buff_no[dev] != -1) info.bytes += wr_buff_size[dev] - wr_buff_ptr[dev]; bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); return 0; } case SNDCTL_DSP_NONBLOCK: dev_nblock[dev] = 1; return 0; break; case SNDCTL_DSP_GETCAPS: { int info = 1; /* Revision level of this ioctl() */ if (audio_devs[dev]->flags & DMA_DUPLEX) info |= DSP_CAP_DUPLEX; if (audio_devs[dev]->coproc) info |= DSP_CAP_COPROC; if (audio_devs[dev]->local_qlen) /* Dev. has hidden buffs */ info |= DSP_CAP_BATCH; if (audio_devs[dev]->trigger) /* Supports SETTRIGGER */ info |= DSP_CAP_TRIGGER; info |= DSP_CAP_MMAP; bcopy((char *) &info, &(((char *) arg)[0]), sizeof(info)); return 0; } break; case FIOASYNC: return *(int *) arg = 1; case FIONBIO: return *(int *) arg = 1; default: return DMAbuf_ioctl(dev, cmd, arg, 0); } } #ifdef ALLOW_POLL /* * XXX should we use spltty() in the select calls ? - lr970714 * */ int audio_poll(int dev, struct fileinfo * file, int events, select_table * wait) { dev = dev >> 4; if (events & (POLLIN | POLLRDNORM)) { if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return 0; /* Not recording */ return (DMAbuf_poll(dev, file, events, wait)); } if (events & (POLLOUT | POLLWRNORM)) { if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return 0; /* Wrong direction */ if (wr_buff_no[dev] != -1) return 1; /* There is space in the current buffer */ return ( DMAbuf_poll(dev, file, events, wait) ); } return 0; } #endif /* ALLOW_POLL */ #endif