📄 dmabuf.c
字号:
/* * sound/dmabuf.c * * The DMA buffer manager for digitized voice applications *//* * 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"#if defined(CONFIG_AUDIO) || defined(CONFIG_GUS)static wait_handle *in_sleeper[MAX_AUDIO_DEV] ={NULL};static volatile struct snd_wait in_sleep_flag[MAX_AUDIO_DEV] ={ {0}};static wait_handle *out_sleeper[MAX_AUDIO_DEV] ={NULL};static volatile struct snd_wait out_sleep_flag[MAX_AUDIO_DEV] ={ {0}};#define NEUTRAL8 0x80#define NEUTRAL16 0x00static int ndmaps = 0;#define MAX_DMAP (MAX_AUDIO_DEV*2)static struct dma_buffparms dmaps[MAX_DMAP] ={ {0}};static int space_in_queue (int dev);static void dma_reset_output (int dev);static void dma_reset_input (int dev);static int dma_set_fragment (int dev, struct dma_buffparms *dmap, caddr_t arg, int fact);static voidreorganize_buffers (int dev, struct dma_buffparms *dmap, int recording){ /* * This routine breaks the physical device buffers to logical ones. */ struct audio_operations *dsp_dev = audio_devs[dev]; unsigned i, n; unsigned sr, nc, sz, bsz; if (dmap->fragment_size == 0) { /* Compute the fragment size using the default algorithm */ sr = dsp_dev->d->set_speed (dev, 0); nc = dsp_dev->d->set_channels (dev, 0); sz = dsp_dev->d->set_bits (dev, 0); if (sz == 8) dmap->neutral_byte = NEUTRAL8; else dmap->neutral_byte = NEUTRAL16; if (sr < 1 || nc < 1 || sz < 1) { printk ("Warning: Invalid PCM parameters[%d] sr=%d, nc=%d, sz=%d\n", dev, sr, nc, sz); sr = DSP_DEFAULT_SPEED; nc = 1; sz = 8; } sz = sr * nc * sz; sz /= 8; /* #bits -> #bytes */ /* * Compute a buffer size for time not exceeding 1 second. * Usually this algorithm gives a buffer size for 0.5 to 1.0 seconds * of sound (using the current speed, sample size and #channels). */ bsz = dsp_dev->buffsize; while (bsz > sz) bsz /= 2; if (bsz == dsp_dev->buffsize) bsz /= 2; /* Needs at least 2 buffers *//* * Split the computed fragment to smaller parts. After 3.5a9 * the default subdivision is 4 which should give better * results when recording. */ if (dmap->subdivision == 0) /* Not already set */ { dmap->subdivision = 1; /* Init to the default value */#ifndef V35A9_COMPATIBLE if (recording) dmap->subdivision = 4; /* Use shorter fragments when recording */#endif } bsz /= dmap->subdivision; if (bsz < 16) bsz = 16; /* Just a sanity check */ dmap->fragment_size = bsz; } else { /* * The process has specified the buffer size with SNDCTL_DSP_SETFRAGMENT or * the buffer size computation has already been done. */ if (dmap->fragment_size > (audio_devs[dev]->buffsize / 2)) dmap->fragment_size = (audio_devs[dev]->buffsize / 2); bsz = dmap->fragment_size; } bsz &= ~0x03; /* Force size which is multiple of 4 bytes */#ifdef OS_DMA_ALIGN_CHECK OS_DMA_ALIGN_CHECK (bsz);#endif n = dsp_dev->buffsize / bsz; if (n > MAX_SUB_BUFFERS) n = MAX_SUB_BUFFERS; if (n > dmap->max_fragments) n = dmap->max_fragments; dmap->nbufs = n; dmap->bytes_in_use = n * bsz; if (dmap->raw_buf) memset (dmap->raw_buf, dmap->neutral_byte, dmap->bytes_in_use); for (i = 0; i < dmap->nbufs; i++) { dmap->counts[i] = 0; } dmap->flags |= DMA_ALLOC_DONE | DMA_EMPTY;}static voiddma_init_buffers (int dev, struct dma_buffparms *dmap){ if (dmap == audio_devs[dev]->dmap_out) { out_sleep_flag[dev].flags = WK_NONE; } else { in_sleep_flag[dev].flags = WK_NONE; } dmap->flags = DMA_BUSY; /* Other flags off */ dmap->qlen = dmap->qhead = dmap->qtail = 0; dmap->nbufs = 1; dmap->bytes_in_use = audio_devs[dev]->buffsize; dmap->dma_mode = DMODE_NONE; dmap->mapping_flags = 0; dmap->neutral_byte = NEUTRAL8; dmap->cfrag = -1; dmap->closing = 0;}static intopen_dmap (int dev, int mode, struct dma_buffparms *dmap, int chan){ if (dmap->flags & DMA_BUSY) return -(EBUSY); { int err; if ((err = sound_alloc_dmap (dev, dmap, chan)) < 0) return err; } if (dmap->raw_buf == NULL) return -(ENOSPC); /* Memory allocation failed during boot */ if (sound_open_dma (chan, audio_devs[dev]->name)) { printk ("Unable to grab(2) DMA%d for the audio driver\n", chan); return -(EBUSY); } dmap->open_mode = mode; dmap->subdivision = dmap->underrun_count = 0; dmap->fragment_size = 0; dmap->max_fragments = 65536; /* Just a large value */ dmap->byte_counter = 0; dma_init_buffers (dev, dmap); return 0;}static voidclose_dmap (int dev, struct dma_buffparms *dmap, int chan){ sound_close_dma (chan); if (dmap->flags & DMA_BUSY) dmap->dma_mode = DMODE_NONE; dmap->flags &= ~DMA_BUSY; disable_dma (chan); sound_free_dmap (dev, dmap);}static unsigned intdefault_set_bits (int dev, unsigned int bits){ return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SETFMT, (caddr_t) (long) bits, 1);}static intdefault_set_speed (int dev, int speed){ return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_SPEED, (caddr_t) (long) speed, 1);}static shortdefault_set_channels (int dev, short channels){ int c = channels; return audio_devs[dev]->d->ioctl (dev, SNDCTL_DSP_CHANNELS, (caddr_t) (long) c, 1);}static voidcheck_driver (struct audio_driver *d){ if (d->set_speed == NULL) d->set_speed = default_set_speed; if (d->set_bits == NULL) d->set_bits = default_set_bits; if (d->set_channels == NULL) d->set_channels = default_set_channels;}intDMAbuf_open (int dev, int mode){ int retval; struct dma_buffparms *dmap_in = NULL; struct dma_buffparms *dmap_out = NULL; if (dev >= num_audiodevs) { /* printk ("PCM device %d not installed.\n", dev); */ return -(ENXIO); } if (!audio_devs[dev]) { /* printk ("PCM device %d not initialized\n", dev); */ return -(ENXIO); } if (!(audio_devs[dev]->flags & DMA_DUPLEX)) { audio_devs[dev]->dmap_in = audio_devs[dev]->dmap_out; audio_devs[dev]->dmachan2 = audio_devs[dev]->dmachan1; } if ((retval = audio_devs[dev]->d->open (dev, mode)) < 0) return retval; check_driver (audio_devs[dev]->d); dmap_out = audio_devs[dev]->dmap_out; dmap_in = audio_devs[dev]->dmap_in; if ((retval = open_dmap (dev, mode, dmap_out, audio_devs[dev]->dmachan1)) < 0) { audio_devs[dev]->d->close (dev); return retval; } audio_devs[dev]->enable_bits = mode; if (mode & OPEN_READ && audio_devs[dev]->flags & DMA_DUPLEX && dmap_out != dmap_in) if ((retval = open_dmap (dev, mode, dmap_in, audio_devs[dev]->dmachan2)) < 0) { audio_devs[dev]->d->close (dev); close_dmap (dev, dmap_out, audio_devs[dev]->dmachan1); return retval; } audio_devs[dev]->open_mode = mode; audio_devs[dev]->go = 1; in_sleep_flag[dev].flags = WK_NONE; out_sleep_flag[dev].flags = WK_NONE; audio_devs[dev]->d->set_bits (dev, 8); audio_devs[dev]->d->set_channels (dev, 1); audio_devs[dev]->d->set_speed (dev, DSP_DEFAULT_SPEED); return 0;}static voiddma_reset (int dev){ unsigned long flags; save_flags (flags); cli (); audio_devs[dev]->d->reset (dev); restore_flags (flags); dma_reset_output (dev); if (audio_devs[dev]->flags & DMA_DUPLEX && audio_devs[dev]->open_mode & OPEN_READ) dma_reset_input (dev);}static voiddma_reset_output (int dev){ unsigned long flags; save_flags (flags); cli (); if (!(audio_devs[dev]->flags & DMA_DUPLEX) || !audio_devs[dev]->d->halt_output) audio_devs[dev]->d->reset (dev); else audio_devs[dev]->d->halt_output (dev); restore_flags (flags); dma_init_buffers (dev, audio_devs[dev]->dmap_out); reorganize_buffers (dev, audio_devs[dev]->dmap_out, 0);}static voiddma_reset_input (int dev){ unsigned long flags; save_flags (flags); cli (); if (!(audio_devs[dev]->flags & DMA_DUPLEX) || !audio_devs[dev]->d->halt_input) audio_devs[dev]->d->reset (dev); else audio_devs[dev]->d->halt_input (dev); restore_flags (flags); dma_init_buffers (dev, audio_devs[dev]->dmap_in); reorganize_buffers (dev, audio_devs[dev]->dmap_in, 1);}static intdma_sync (int dev){ unsigned long flags; if (!audio_devs[dev]->go && (!audio_devs[dev]->enable_bits & PCM_ENABLE_OUTPUT)) return 0; if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) { save_flags (flags); cli (); audio_devs[dev]->dmap_out->flags |= DMA_SYNCING; audio_devs[dev]->dmap_out->underrun_count = 0; while (!current_got_fatal_signal () && audio_devs[dev]->dmap_out->qlen && audio_devs[dev]->dmap_out->underrun_count == 0) { { unsigned long tlimit; if (HZ) current_set_timeout (tlimit = jiffies + (HZ)); else tlimit = (unsigned long) -1; out_sleep_flag[dev].flags = WK_SLEEP; module_interruptible_sleep_on (&out_sleeper[dev]); if (!(out_sleep_flag[dev].flags & WK_WAKEUP)) { if (jiffies >= tlimit) out_sleep_flag[dev].flags |= WK_TIMEOUT; } out_sleep_flag[dev].flags &= ~WK_SLEEP; }; if ((out_sleep_flag[dev].flags & WK_TIMEOUT)) { audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING; restore_flags (flags); return audio_devs[dev]->dmap_out->qlen; } } audio_devs[dev]->dmap_out->flags &= ~DMA_SYNCING; restore_flags (flags); /* * Some devices such as GUS have huge amount of on board RAM for the * audio data. We have to wait until the device has finished playing. */ save_flags (flags); cli (); if (audio_devs[dev]->d->local_qlen) /* Device has hidden buffers */ { while (!(current_got_fatal_signal ()) && audio_devs[dev]->d->local_qlen (dev)) { { unsigned long tlimit; if (HZ) current_set_timeout (tlimit = jiffies + (HZ)); else tlimit = (unsigned long) -1; out_sleep_flag[dev].flags = WK_SLEEP; module_interruptible_sleep_on (&out_sleeper[dev]); if (!(out_sleep_flag[dev].flags & WK_WAKEUP)) { if (jiffies >= tlimit) out_sleep_flag[dev].flags |= WK_TIMEOUT; } out_sleep_flag[dev].flags &= ~WK_SLEEP; }; } } restore_flags (flags); } return audio_devs[dev]->dmap_out->qlen;}intDMAbuf_release (int dev, int mode){ unsigned long flags; audio_devs[dev]->dmap_out->closing = 1; audio_devs[dev]->dmap_in->closing = 1; if (!(current_got_fatal_signal ()) && (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT)) { dma_sync (dev); } if (audio_devs[dev]->dmap_out->dma_mode == DMODE_OUTPUT) memset (audio_devs[dev]->dmap_out->raw_buf, audio_devs[dev]->dmap_out->neutral_byte, audio_devs[dev]->dmap_out->bytes_in_use); save_flags (flags); cli (); audio_devs[dev]->d->halt_xfer (dev); audio_devs[dev]->d->close (dev); close_dmap (dev, audio_devs[dev]->dmap_out, audio_devs[dev]->dmachan1); if (audio_devs[dev]->open_mode & OPEN_READ && audio_devs[dev]->flags & DMA_DUPLEX) close_dmap (dev, audio_devs[dev]->dmap_in, audio_devs[dev]->dmachan2); audio_devs[dev]->open_mode = 0; restore_flags (flags); return 0;}static intactivate_recording (int dev, struct dma_buffparms *dmap){ int prepare = 0; if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT)) return 0; if (dmap->flags & DMA_RESTART) { dma_reset_input (dev); dmap->flags &= ~DMA_RESTART; prepare = 1; } if (dmap->dma_mode == DMODE_OUTPUT) /* Direction change */ { dma_sync (dev); dma_reset (dev); dmap->dma_mode = DMODE_NONE; } if (!(dmap->flags & DMA_ALLOC_DONE)) reorganize_buffers (dev, dmap, 1); if (prepare || !dmap->dma_mode) { int err; if ((err = audio_devs[dev]->d->prepare_for_input (dev, dmap->fragment_size, dmap->nbufs)) < 0) { return err; } dmap->dma_mode = DMODE_INPUT; } if (!(dmap->flags & DMA_ACTIVE)) { audio_devs[dev]->d->start_input (dev, dmap->raw_buf_phys + dmap->qtail * dmap->fragment_size, dmap->fragment_size, 0, !(audio_devs[dev]->flags & DMA_AUTOMODE) || !(dmap->flags & DMA_STARTED)); dmap->flags |= DMA_ACTIVE | DMA_STARTED; if (audio_devs[dev]->d->trigger) audio_devs[dev]->d->trigger (dev, audio_devs[dev]->enable_bits * audio_devs[dev]->go); } return 0;}intDMAbuf_getrdbuffer (int dev, char **buf, int *len, int dontblock){ unsigned long flags; int err = EIO; struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; save_flags (flags); cli (); if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) { printk ("Sound: Can't read from mmapped device (1)\n"); return -(EINVAL); } else if (!dmap->qlen) { int tmout; if ((err = activate_recording (dev, dmap)) < 0) { restore_flags (flags); return err; } /* Wait for the next block */ if (dontblock) { restore_flags (flags); return -(EAGAIN); } if (!(audio_devs[dev]->enable_bits & PCM_ENABLE_INPUT) & audio_devs[dev]->go) { restore_flags (flags); return -(EAGAIN); } if (!audio_devs[dev]->go) tmout = 0; else tmout = 10 * HZ; { unsigned long tlimit; if (tmout) current_set_timeout (tlimit = jiffies + (tmout)); else tlimit = (unsigned long) -1; in_sleep_flag[dev].flags = WK_SLEEP; module_interruptible_sleep_on (&in_sleeper[dev]); if (!(in_sleep_flag[dev].flags & WK_WAKEUP)) { if (jiffies >= tlimit) in_sleep_flag[dev].flags |= WK_TIMEOUT; } in_sleep_flag[dev].flags &= ~WK_SLEEP; }; if ((in_sleep_flag[dev].flags & WK_TIMEOUT)) { printk ("Sound: DMA (input) timed out - IRQ/DRQ config error?\n"); err = EIO; audio_devs[dev]->d->reset (dev); ; } else err = EINTR; } restore_flags (flags); if (!dmap->qlen) return -(err); *buf = &dmap->raw_buf[dmap->qhead * dmap->fragment_size + dmap->counts[dmap->qhead]]; *len = dmap->fragment_size - dmap->counts[dmap->qhead]; return dmap->qhead;}intDMAbuf_rmchars (int dev, int buff_no, int c){ struct dma_buffparms *dmap = audio_devs[dev]->dmap_in; int p = dmap->counts[dmap->qhead] + c; if (audio_devs[dev]->dmap_in->mapping_flags & DMA_MAP_MAPPED) { printk ("Sound: Can't read from mmapped device (2)\n"); return -(EINVAL); } else if (p >= dmap->fragment_size) { /* This buffer is completely empty */ dmap->counts[dmap->qhead] = 0; if (dmap->qlen <= 0 || dmap->qlen > dmap->nbufs) printk ("\nSound: Audio queue1 corrupted for dev%d (%d/%d)\n", dev, dmap->qlen, dmap->nbufs);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -