URL
https://opencores.org/ocsvn/or1k/or1k/trunk
Subversion Repositories or1k
[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [drivers/] [sound/] [audio.c] - Rev 1765
Compare with Previous | Blame | View Log
/* * sound/audio.c * * Device file manager for /dev/audio */ /* * Copyright (C) by Hannu Savolainen 1993-1996 * * USS/Lite for Linux is distributed under the GNU GENERAL PUBLIC LICENSE (GPL) * Version 2 (June 1991). See the "COPYING" file distributed with this software * for more info. */ #include <linux/config.h> #include "sound_config.h" #ifdef CONFIG_AUDIO #include "ulaw.h" #include "coproc.h" #define ON 1 #define OFF 0 static int audio_mode[MAX_AUDIO_DEV]; static int dev_nblock[MAX_AUDIO_DEV]; /* 1 if in nonblocking mode */ #define AM_NONE 0 #define AM_WRITE 1 #define AM_READ 2 static int audio_format[MAX_AUDIO_DEV]; static int local_conversion[MAX_AUDIO_DEV]; static int set_format (int dev, long 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, (caddr_t) 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; long bits; int dev_type = dev & 0x0f; int mode = file->mode & O_ACCMODE; dev = dev >> 4; if (dev_type == SND_DEV_DSP16) bits = 16; else bits = 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); printk ("Sound: Can't access coprocessor device\n"); return ret; } local_conversion[dev] = 0; if (DMAbuf_ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) bits, 1) != bits) { printk ("audio: Can't set number of bits on device %d\n", dev); audio_release (dev, file); return -(ENXIO); } if (dev_type == SND_DEV_AUDIO) { set_format (dev, AFMT_MU_LAW); } else set_format (dev, bits); audio_mode[dev] = AM_NONE; dev_nblock[dev] = 0; return ret; } void sync_output (int dev) { int buf_no, buf_ptr, buf_size, p, i; char *dma_buf; struct dma_buffparms *dmap = audio_devs[dev]->dmap_out; if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0) { DMAbuf_start_output (dev, buf_no, buf_ptr); } /* * Clean all unused buffer fragments. */ p = dmap->qtail; for (i = dmap->qlen + 1; i < dmap->nbufs; i++) { memset (dmap->raw_buf + p * dmap->fragment_size, dmap->neutral_byte, dmap->fragment_size); p = (p + 1) % dmap->nbufs; } dmap->flags |= DMA_CLEAN; } void audio_release (int dev, struct fileinfo *file) { int mode; dev = dev >> 4; mode = file->mode & O_ACCMODE; audio_devs[dev]->dmap_out->closing = 1; audio_devs[dev]->dmap_in->closing = 1; sync_output (dev); if (audio_devs[dev]->coproc) audio_devs[dev]->coproc->close (audio_devs[dev]->coproc->devc, COPR_PCM); DMAbuf_release (dev, mode); } #if defined(NO_INLINE_ASM) || !defined(i386) static void translate_bytes (const unsigned char *table, unsigned char *buff, int n) { unsigned long i; if (n <= 0) return; for (i = 0; i < n; ++i) buff[i] = table[buff[i]]; } #else extern inline void translate_bytes (const void *table, void *buff, int n) { if (n > 0) { __asm__ ("cld\n" "1:\tlodsb\n\t" "xlatb\n\t" "stosb\n\t" "loop 1b\n\t": : "b" ((long) table), "c" (n), "D" ((long) buff), "S" ((long) buff) : "bx", "cx", "di", "si", "ax"); } } #endif int audio_write (int dev, struct fileinfo *file, const char *buf, int count) { int c, p, l, buf_no, buf_ptr, buf_size; int err; char *dma_buf; dev = dev >> 4; p = 0; c = count; if (count < 0) return -EINVAL; if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) { /* Direction change */ } if (audio_devs[dev]->flags & DMA_DUPLEX) audio_mode[dev] |= AM_WRITE; else audio_mode[dev] = AM_WRITE; if (!count) /* Flush output */ { sync_output (dev); return 0; } while (c) { if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) < 0) { if ((buf_no = DMAbuf_getwrbuffer (dev, &dma_buf, &buf_size, dev_nblock[dev])) < 0) { /* Handle nonblocking mode */ if (dev_nblock[dev] && buf_no == -(EAGAIN)) return p; /* No more space. Return # of accepted bytes */ return buf_no; } buf_ptr = 0; } l = c; if (l > (buf_size - buf_ptr)) l = (buf_size - buf_ptr); if (!audio_devs[dev]->d->copy_from_user) { /* * No device specific copy routine */ memcpy_fromfs (&dma_buf[buf_ptr], &(buf)[p], l); } else audio_devs[dev]->d->copy_from_user (dev, dma_buf, buf_ptr, buf, p, l); if (local_conversion[dev] == AFMT_MU_LAW) { /* * This just allows interrupts while the conversion is running */ sti (); translate_bytes (ulaw_dsp, (unsigned char *) &dma_buf[buf_ptr], l); } c -= l; p += l; buf_ptr += l; if (buf_ptr >= buf_size) { if ((err = DMAbuf_start_output (dev, buf_no, buf_ptr)) < 0) { return err; } } else DMAbuf_set_count (dev, buf_no, buf_ptr); } return count; } int audio_read (int dev, struct fileinfo *file, char *buf, int count) { int c, p, l; char *dmabuf; int buf_no; dev = dev >> 4; p = 0; c = count; if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) { sync_output (dev); } if (audio_devs[dev]->flags & DMA_DUPLEX) audio_mode[dev] |= AM_READ; else audio_mode[dev] = AM_READ; while (c) { if ((buf_no = DMAbuf_getrdbuffer (dev, &dmabuf, &l, dev_nblock[dev])) < 0) { /* Nonblocking mode handling. Return current # of bytes */ if (dev_nblock[dev] && buf_no == -(EAGAIN)) return p; return buf_no; } if (l > c) l = c; /* * Insert any local processing here. */ if (local_conversion[dev] == AFMT_MU_LAW) { /* * This just allows interrupts while the conversion is running */ sti (); translate_bytes (dsp_ulaw, (unsigned char *) dmabuf, l); } memcpy_tofs (&(buf)[p], dmabuf, l); DMAbuf_rmchars (dev, buf_no, l); p += l; c -= l; } return count - c; } int audio_ioctl (int dev, struct fileinfo *file, unsigned int cmd, caddr_t 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 printk ("/dev/dsp%d: No coprocessor for this device\n", dev); return -(ENXIO); } else switch (cmd) { case SNDCTL_DSP_SYNC: if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return 0; sync_output (dev); return DMAbuf_ioctl (dev, cmd, arg, 0); break; case SNDCTL_DSP_POST: if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return 0; sync_output (dev); return 0; break; case SNDCTL_DSP_RESET: audio_mode[dev] = AM_NONE; return DMAbuf_ioctl (dev, cmd, arg, 0); break; case SNDCTL_DSP_GETFMTS: return snd_ioctl_return ((int *) arg, audio_devs[dev]->format_mask | AFMT_MU_LAW); break; case SNDCTL_DSP_SETFMT: return snd_ioctl_return ((int *) arg, set_format (dev, get_user ((int *) arg))); case SNDCTL_DSP_GETISPACE: if (!(audio_devs[dev]->open_mode & OPEN_READ)) return 0; if ((audio_mode[dev] & AM_WRITE) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -(EBUSY); { audio_buf_info info; int err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1); if (err < 0) return err; memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); return 0; } case SNDCTL_DSP_GETOSPACE: if (!(audio_devs[dev]->open_mode & OPEN_WRITE)) return -EPERM; if ((audio_mode[dev] & AM_READ) && !(audio_devs[dev]->flags & DMA_DUPLEX)) return -(EBUSY); { audio_buf_info info; char *dma_buf; int buf_no, buf_ptr, buf_size; int err = DMAbuf_ioctl (dev, cmd, (caddr_t) & info, 1); if (err < 0) return err; if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0) info.bytes -= buf_ptr; memcpy_tofs (&((char *) arg)[0], (char *) &info, 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]->d->local_qlen) /* Device has hidden buffers */ info |= DSP_CAP_BATCH; if (audio_devs[dev]->d->trigger) /* Supports SETTRIGGER */ info |= DSP_CAP_TRIGGER; info |= DSP_CAP_MMAP; memcpy_tofs (&((char *) arg)[0], (char *) &info, sizeof (info)); return 0; } break; default: return DMAbuf_ioctl (dev, cmd, arg, 0); } } void audio_init (void) { /* * NOTE! This routine could be called several times during boot. */ } int audio_select (int dev, struct fileinfo *file, int sel_type, select_table_handle * wait) { char *dma_buf; int buf_no, buf_ptr, buf_size; dev = dev >> 4; switch (sel_type) { case SEL_IN: if (audio_mode[dev] & AM_WRITE && !(audio_devs[dev]->flags & DMA_DUPLEX)) { return 0; /* Not recording */ } return DMAbuf_select (dev, file, sel_type, wait); break; case SEL_OUT: if (audio_mode[dev] & AM_READ && !(audio_devs[dev]->flags & DMA_DUPLEX)) { return 0; /* Wrong direction */ } if (DMAbuf_get_curr_buffer (dev, &buf_no, &dma_buf, &buf_ptr, &buf_size) >= 0) { return 1; /* There is space in the current buffer */ } return DMAbuf_select (dev, file, sel_type, wait); break; case SEL_EX: return 0; } return 0; } #endif