?? dmabuf.c
字號:
/* * sound/dmabuf.c * * The DMA buffer manager for digitized voice applications *//* * Copyright (C) by Hannu Savolainen 1993-1997 * * OSS/Free 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. * * Thomas Sailer : moved several static variables into struct audio_operations * (which is grossly misnamed btw.) because they have the same * lifetime as the rest in there and dynamic allocation saves * 12k or so * Thomas Sailer : remove {in,out}_sleep_flag. It was used for the sleeper to * determine if it was woken up by the expiring timeout or by * an explicit wake_up. The return value from schedule_timeout * can be used instead; if 0, the wakeup was due to the timeout. * * Rob Riggs Added persistent DMA buffers (1998/10/17) */#define BE_CONSERVATIVE#define SAMPLE_ROUNDUP 0#include "sound_config.h"#include <linux/wrapper.h>#define DMAP_FREE_ON_CLOSE 0#define DMAP_KEEP_ON_CLOSE 1extern int sound_dmap_flag;static void dma_reset_output(int dev);static void dma_reset_input(int dev);static int local_start_dma(struct audio_operations *adev, unsigned long physaddr, int count, int dma_mode);static int debugmem = 0; /* switched off by default */static int dma_buffsize = DSP_BUFFSIZE;static long dmabuf_timeout(struct dma_buffparms *dmap){ long tmout; tmout = (dmap->fragment_size * HZ) / dmap->data_rate; tmout += HZ / 5; /* Some safety distance */ if (tmout < (HZ / 2)) tmout = HZ / 2; if (tmout > 20 * HZ) tmout = 20 * HZ; return tmout;}static int sound_alloc_dmap(struct dma_buffparms *dmap){ char *start_addr, *end_addr; int dma_pagesize; int sz, size; struct page *page; dmap->mapping_flags &= ~DMA_MAP_MAPPED; if (dmap->raw_buf != NULL) return 0; /* Already done */ if (dma_buffsize < 4096) dma_buffsize = 4096; dma_pagesize = (dmap->dma < 4) ? (64 * 1024) : (128 * 1024); /* * Now check for the Cyrix problem. */ if(isa_dma_bridge_buggy==2) dma_pagesize=32768; dmap->raw_buf = NULL; dmap->buffsize = dma_buffsize; if (dmap->buffsize > dma_pagesize) dmap->buffsize = dma_pagesize; start_addr = NULL; /* * Now loop until we get a free buffer. Try to get smaller buffer if * it fails. Don't accept smaller than 8k buffer for performance * reasons. */ while (start_addr == NULL && dmap->buffsize > PAGE_SIZE) { for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); dmap->buffsize = PAGE_SIZE * (1 << sz); start_addr = (char *) __get_free_pages(GFP_ATOMIC|GFP_DMA, sz); if (start_addr == NULL) dmap->buffsize /= 2; } if (start_addr == NULL) { printk(KERN_WARNING "Sound error: Couldn't allocate DMA buffer\n"); return -ENOMEM; } else { /* make some checks */ end_addr = start_addr + dmap->buffsize - 1; if (debugmem) printk(KERN_DEBUG "sound: start 0x%lx, end 0x%lx\n", (long) start_addr, (long) end_addr); /* now check if it fits into the same dma-pagesize */ if (((long) start_addr & ~(dma_pagesize - 1)) != ((long) end_addr & ~(dma_pagesize - 1)) || end_addr >= (char *) (MAX_DMA_ADDRESS)) { printk(KERN_ERR "sound: Got invalid address 0x%lx for %db DMA-buffer\n", (long) start_addr, dmap->buffsize); return -EFAULT; } } dmap->raw_buf = start_addr; dmap->raw_buf_phys = virt_to_bus(start_addr); for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) mem_map_reserve(page); return 0;}static void sound_free_dmap(struct dma_buffparms *dmap){ int sz, size; struct page *page; unsigned long start_addr, end_addr; if (dmap->raw_buf == NULL) return; if (dmap->mapping_flags & DMA_MAP_MAPPED) return; /* Don't free mmapped buffer. Will use it next time */ for (sz = 0, size = PAGE_SIZE; size < dmap->buffsize; sz++, size <<= 1); start_addr = (unsigned long) dmap->raw_buf; end_addr = start_addr + dmap->buffsize; for (page = virt_to_page(start_addr); page <= virt_to_page(end_addr); page++) mem_map_unreserve(page); free_pages((unsigned long) dmap->raw_buf, sz); dmap->raw_buf = NULL;}/* Intel version !!!!!!!!! */static int sound_start_dma(struct dma_buffparms *dmap, unsigned long physaddr, int count, int dma_mode){ unsigned long flags; int chan = dmap->dma; /* printk( "Start DMA%d %d, %d\n", chan, (int)(physaddr-dmap->raw_buf_phys), count); */ flags = claim_dma_lock(); disable_dma(chan); clear_dma_ff(chan); set_dma_mode(chan, dma_mode); set_dma_addr(chan, physaddr); set_dma_count(chan, count); enable_dma(chan); release_dma_lock(flags); return 0;}static void dma_init_buffers(struct dma_buffparms *dmap){ dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; dmap->byte_counter = 0; dmap->max_byte_counter = 8000 * 60 * 60; dmap->bytes_in_use = dmap->buffsize; dmap->dma_mode = DMODE_NONE; dmap->mapping_flags = 0; dmap->neutral_byte = 0x80; dmap->data_rate = 8000; dmap->cfrag = -1; dmap->closing = 0; dmap->nbufs = 1; dmap->flags = DMA_BUSY; /* Other flags off */}static int open_dmap(struct audio_operations *adev, int mode, struct dma_buffparms *dmap){ int err; if (dmap->flags & DMA_BUSY) return -EBUSY; if ((err = sound_alloc_dmap(dmap)) < 0) return err; if (dmap->raw_buf == NULL) { printk(KERN_WARNING "Sound: DMA buffers not available\n"); return -ENOSPC; /* Memory allocation failed during boot */ } if (dmap->dma >= 0 && sound_open_dma(dmap->dma, adev->name)) { printk(KERN_WARNING "Unable to grab(2) DMA%d for the audio driver\n", dmap->dma); return -EBUSY; } dma_init_buffers(dmap); 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; dmap->max_byte_counter = 8000 * 60 * 60; dmap->applic_profile = APF_NORMAL; dmap->needs_reorg = 1; dmap->audio_callback = NULL; dmap->callback_parm = 0; return 0;}static void close_dmap(struct audio_operations *adev, struct dma_buffparms *dmap){ unsigned long flags; if (dmap->dma >= 0) { sound_close_dma(dmap->dma); flags=claim_dma_lock(); disable_dma(dmap->dma); release_dma_lock(flags); } if (dmap->flags & DMA_BUSY) dmap->dma_mode = DMODE_NONE; dmap->flags &= ~DMA_BUSY; if (sound_dmap_flag == DMAP_FREE_ON_CLOSE) sound_free_dmap(dmap);}static unsigned int default_set_bits(int dev, unsigned int bits){ mm_segment_t fs = get_fs(); set_fs(get_ds()); audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SETFMT, (caddr_t)&bits); set_fs(fs); return bits;}static int default_set_speed(int dev, int speed){ mm_segment_t fs = get_fs(); set_fs(get_ds()); audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_SPEED, (caddr_t)&speed); set_fs(fs); return speed;}static short default_set_channels(int dev, short channels){ int c = channels; mm_segment_t fs = get_fs(); set_fs(get_ds()); audio_devs[dev]->d->ioctl(dev, SNDCTL_DSP_CHANNELS, (caddr_t)&c); set_fs(fs); return c;}static void check_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;}int DMAbuf_open(int dev, int mode){ struct audio_operations *adev = audio_devs[dev]; int retval; struct dma_buffparms *dmap_in = NULL; struct dma_buffparms *dmap_out = NULL; if (!adev) return -ENXIO; if (!(adev->flags & DMA_DUPLEX)) adev->dmap_in = adev->dmap_out; check_driver(adev->d); if ((retval = adev->d->open(dev, mode)) < 0) return retval; dmap_out = adev->dmap_out; dmap_in = adev->dmap_in; if (dmap_in == dmap_out) adev->flags &= ~DMA_DUPLEX; if (mode & OPEN_WRITE) { if ((retval = open_dmap(adev, mode, dmap_out)) < 0) { adev->d->close(dev); return retval; } } adev->enable_bits = mode; if (mode == OPEN_READ || (mode != OPEN_WRITE && (adev->flags & DMA_DUPLEX))) { if ((retval = open_dmap(adev, mode, dmap_in)) < 0) { adev->d->close(dev); if (mode & OPEN_WRITE) close_dmap(adev, dmap_out); return retval; } } adev->open_mode = mode; adev->go = 1; adev->d->set_bits(dev, 8); adev->d->set_channels(dev, 1); adev->d->set_speed(dev, DSP_DEFAULT_SPEED); if (adev->dmap_out->dma_mode == DMODE_OUTPUT) memset(adev->dmap_out->raw_buf, adev->dmap_out->neutral_byte, adev->dmap_out->bytes_in_use); return 0;}void DMAbuf_reset(int dev){ if (audio_devs[dev]->open_mode & OPEN_WRITE) dma_reset_output(dev); if (audio_devs[dev]->open_mode & OPEN_READ) dma_reset_input(dev);}static void dma_reset_output(int dev){ struct audio_operations *adev = audio_devs[dev]; unsigned long flags,f ; struct dma_buffparms *dmap = adev->dmap_out; if (!(dmap->flags & DMA_STARTED)) /* DMA is not active */ return; /* * First wait until the current fragment has been played completely */ save_flags(flags); cli(); adev->dmap_out->flags |= DMA_SYNCING; adev->dmap_out->underrun_count = 0; if (!signal_pending(current) && adev->dmap_out->qlen && adev->dmap_out->underrun_count == 0) interruptible_sleep_on_timeout(&adev->out_sleeper, dmabuf_timeout(dmap)); adev->dmap_out->flags &= ~(DMA_SYNCING | DMA_ACTIVE); /* * Finally shut the device off */ if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_output) adev->d->halt_io(dev); else adev->d->halt_output(dev); adev->dmap_out->flags &= ~DMA_STARTED; f=claim_dma_lock(); clear_dma_ff(dmap->dma); disable_dma(dmap->dma); release_dma_lock(f); restore_flags(flags); dmap->byte_counter = 0; reorganize_buffers(dev, adev->dmap_out, 0); dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0;}static void dma_reset_input(int dev){ struct audio_operations *adev = audio_devs[dev]; unsigned long flags; struct dma_buffparms *dmap = adev->dmap_in; save_flags(flags); cli(); if (!(adev->flags & DMA_DUPLEX) || !adev->d->halt_input) adev->d->halt_io(dev); else adev->d->halt_input(dev); adev->dmap_in->flags &= ~DMA_STARTED; restore_flags(flags); dmap->qlen = dmap->qhead = dmap->qtail = dmap->user_counter = 0; dmap->byte_counter = 0; reorganize_buffers(dev, adev->dmap_in, 1);}void DMAbuf_launch_output(int dev, struct dma_buffparms *dmap){ struct audio_operations *adev = audio_devs[dev]; if (!((adev->enable_bits * adev->go) & PCM_ENABLE_OUTPUT)) return; /* Don't start DMA yet */ dmap->dma_mode = DMODE_OUTPUT; if (!(dmap->flags & DMA_ACTIVE) || !(adev->flags & DMA_AUTOMODE) || (dmap->flags & DMA_NODMA)) { if (!(dmap->flags & DMA_STARTED)) { reorganize_buffers(dev, dmap, 0); if (adev->d->prepare_for_output(dev, dmap->fragment_size, dmap->nbufs)) return; if (!(dmap->flags & DMA_NODMA)) local_start_dma(adev, dmap->raw_buf_phys, dmap->bytes_in_use,DMA_MODE_WRITE); dmap->flags |= DMA_STARTED; } if (dmap->counts[dmap->qhead] == 0) dmap->counts[dmap->qhead] = dmap->fragment_size; dmap->dma_mode = DMODE_OUTPUT; adev->d->output_block(dev, dmap->raw_buf_phys + dmap->qhead * dmap->fragment_size, dmap->counts[dmap->qhead], 1); if (adev->d->trigger) adev->d->trigger(dev,adev->enable_bits * adev->go); } dmap->flags |= DMA_ACTIVE;}int DMAbuf_sync(int dev){ struct audio_operations *adev = audio_devs[dev]; unsigned long flags; int n = 0; struct dma_buffparms *dmap; if (!adev->go && !(adev->enable_bits & PCM_ENABLE_OUTPUT)) return 0; if (adev->dmap_out->dma_mode == DMODE_OUTPUT) {
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -