📄 audio.c
字号:
/* ********************************************************************** * audio.c -- /dev/dsp interface for emu10k1 driver * Copyright 1999, 2000 Creative Labs, Inc. * ********************************************************************** * * Date Author Summary of changes * ---- ------ ------------------ * October 20, 1999 Bertrand Lee base code release * November 2, 1999 Alan Cox cleaned up types/leaks * ********************************************************************** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, * USA. * ********************************************************************** */#include <linux/module.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/bitops.h>#include <asm/io.h>#include <linux/sched.h>#include <linux/smp_lock.h>#include "hwaccess.h"#include "cardwo.h"#include "cardwi.h"#include "recmgr.h"#include "irqmgr.h"#include "audio.h"#include "8010.h"static void calculate_ofrag(struct woinst *);static void calculate_ifrag(struct wiinst *);static void emu10k1_waveout_bh(unsigned long refdata);static void emu10k1_wavein_bh(unsigned long refdata);/* Audio file operations */static ssize_t emu10k1_audio_read(struct file *file, char __user *buffer, size_t count, loff_t * ppos){ struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; struct wiinst *wiinst = wave_dev->wiinst; ssize_t ret = 0; unsigned long flags; DPD(3, "emu10k1_audio_read(), buffer=%p, count=%d\n", buffer, (u32) count); if (!access_ok(VERIFY_WRITE, buffer, count)) return -EFAULT; spin_lock_irqsave(&wiinst->lock, flags); if (wiinst->mmapped) { spin_unlock_irqrestore(&wiinst->lock, flags); return -ENXIO; } if (wiinst->state == WAVE_STATE_CLOSED) { calculate_ifrag(wiinst); while (emu10k1_wavein_open(wave_dev) < 0) { spin_unlock_irqrestore(&wiinst->lock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&wave_dev->card->open_wait); if (signal_pending(current)) return -ERESTARTSYS; spin_lock_irqsave(&wiinst->lock, flags); } } spin_unlock_irqrestore(&wiinst->lock, flags); while (count > 0) { u32 bytestocopy; spin_lock_irqsave(&wiinst->lock, flags); if (!(wiinst->state & WAVE_STATE_STARTED) && (wave_dev->enablebits & PCM_ENABLE_INPUT)) emu10k1_wavein_start(wave_dev); emu10k1_wavein_update(wave_dev->card, wiinst); emu10k1_wavein_getxfersize(wiinst, &bytestocopy); spin_unlock_irqrestore(&wiinst->lock, flags); DPD(3, "bytestocopy --> %d\n", bytestocopy); if ((bytestocopy >= wiinst->buffer.fragment_size) || (bytestocopy >= count)) { bytestocopy = min_t(u32, bytestocopy, count); emu10k1_wavein_xferdata(wiinst, (u8 __user *)buffer, &bytestocopy); count -= bytestocopy; buffer += bytestocopy; ret += bytestocopy; } if (count > 0) { if ((file->f_flags & O_NONBLOCK) || (!(wave_dev->enablebits & PCM_ENABLE_INPUT))) return (ret ? ret : -EAGAIN); interruptible_sleep_on(&wiinst->wait_queue); if (signal_pending(current)) return (ret ? ret : -ERESTARTSYS); } } DPD(3, "bytes copied -> %d\n", (u32) ret); return ret;}static ssize_t emu10k1_audio_write(struct file *file, const char __user *buffer, size_t count, loff_t * ppos){ struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; struct woinst *woinst = wave_dev->woinst; ssize_t ret; unsigned long flags; DPD(3, "emu10k1_audio_write(), buffer=%p, count=%d\n", buffer, (u32) count); if (!access_ok(VERIFY_READ, buffer, count)) return -EFAULT; spin_lock_irqsave(&woinst->lock, flags); if (woinst->mmapped) { spin_unlock_irqrestore(&woinst->lock, flags); return -ENXIO; } // This is for emu10k1 revs less than 7, we need to go through tram if (woinst->format.passthrough == 1) { int r; woinst->buffer.ossfragshift = PT_BLOCKSIZE_LOG2; woinst->buffer.numfrags = PT_BLOCKCOUNT; calculate_ofrag(woinst); r = emu10k1_pt_write(file, buffer, count); spin_unlock_irqrestore(&woinst->lock, flags); return r; } if (woinst->state == WAVE_STATE_CLOSED) { calculate_ofrag(woinst); while (emu10k1_waveout_open(wave_dev) < 0) { spin_unlock_irqrestore(&woinst->lock, flags); if (file->f_flags & O_NONBLOCK) return -EAGAIN; interruptible_sleep_on(&wave_dev->card->open_wait); if (signal_pending(current)) return -ERESTARTSYS; spin_lock_irqsave(&woinst->lock, flags); } } spin_unlock_irqrestore(&woinst->lock, flags); ret = 0; if (count % woinst->format.bytespersample) return -EINVAL; count /= woinst->num_voices; while (count > 0) { u32 bytestocopy; spin_lock_irqsave(&woinst->lock, flags); emu10k1_waveout_update(woinst); emu10k1_waveout_getxfersize(woinst, &bytestocopy); spin_unlock_irqrestore(&woinst->lock, flags); DPD(3, "bytestocopy --> %d\n", bytestocopy); if ((bytestocopy >= woinst->buffer.fragment_size) || (bytestocopy >= count)) { bytestocopy = min_t(u32, bytestocopy, count); emu10k1_waveout_xferdata(woinst, (u8 __user *) buffer, &bytestocopy); count -= bytestocopy; buffer += bytestocopy * woinst->num_voices; ret += bytestocopy * woinst->num_voices; spin_lock_irqsave(&woinst->lock, flags); woinst->total_copied += bytestocopy; if (!(woinst->state & WAVE_STATE_STARTED) && (wave_dev->enablebits & PCM_ENABLE_OUTPUT) && (woinst->total_copied >= woinst->buffer.fragment_size)) emu10k1_waveout_start(wave_dev); spin_unlock_irqrestore(&woinst->lock, flags); } if (count > 0) { if ((file->f_flags & O_NONBLOCK) || (!(wave_dev->enablebits & PCM_ENABLE_OUTPUT))) return (ret ? ret : -EAGAIN); interruptible_sleep_on(&woinst->wait_queue); if (signal_pending(current)) return (ret ? ret : -ERESTARTSYS); } } DPD(3, "bytes copied -> %d\n", (u32) ret); return ret;}static int emu10k1_audio_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct emu10k1_wavedevice *wave_dev = (struct emu10k1_wavedevice *) file->private_data; struct woinst *woinst = NULL; struct wiinst *wiinst = NULL; int val = 0; u32 bytestocopy; unsigned long flags; int __user *p = (int __user *)arg; DPF(4, "emu10k1_audio_ioctl()\n"); if (file->f_mode & FMODE_WRITE) woinst = wave_dev->woinst; if (file->f_mode & FMODE_READ) wiinst = wave_dev->wiinst; switch (cmd) { case OSS_GETVERSION: DPF(2, "OSS_GETVERSION:\n"); return put_user(SOUND_VERSION, p); case SNDCTL_DSP_RESET: DPF(2, "SNDCTL_DSP_RESET:\n"); wave_dev->enablebits = PCM_ENABLE_OUTPUT | PCM_ENABLE_INPUT; if (file->f_mode & FMODE_WRITE) { spin_lock_irqsave(&woinst->lock, flags); if (woinst->state & WAVE_STATE_OPEN) { emu10k1_waveout_close(wave_dev); } woinst->mmapped = 0; woinst->total_copied = 0; woinst->total_played = 0; woinst->blocks = 0; spin_unlock_irqrestore(&woinst->lock, flags); } if (file->f_mode & FMODE_READ) { spin_lock_irqsave(&wiinst->lock, flags); if (wiinst->state & WAVE_STATE_OPEN) { emu10k1_wavein_close(wave_dev); } wiinst->mmapped = 0; wiinst->total_recorded = 0; wiinst->blocks = 0; spin_unlock_irqrestore(&wiinst->lock, flags); } break; case SNDCTL_DSP_SYNC: DPF(2, "SNDCTL_DSP_SYNC:\n"); if (file->f_mode & FMODE_WRITE) { spin_lock_irqsave(&woinst->lock, flags); if (woinst->state & WAVE_STATE_OPEN) { if (woinst->state & WAVE_STATE_STARTED) while ((woinst->total_played < woinst->total_copied) && !signal_pending(current)) { spin_unlock_irqrestore(&woinst->lock, flags); interruptible_sleep_on(&woinst->wait_queue); spin_lock_irqsave(&woinst->lock, flags); } emu10k1_waveout_close(wave_dev); } woinst->mmapped = 0; woinst->total_copied = 0; woinst->total_played = 0; woinst->blocks = 0; spin_unlock_irqrestore(&woinst->lock, flags); } if (file->f_mode & FMODE_READ) { spin_lock_irqsave(&wiinst->lock, flags); if (wiinst->state & WAVE_STATE_OPEN) { emu10k1_wavein_close(wave_dev); } wiinst->mmapped = 0; wiinst->total_recorded = 0; wiinst->blocks = 0; spin_unlock_irqrestore(&wiinst->lock, flags); } break; case SNDCTL_DSP_SETDUPLEX: DPF(2, "SNDCTL_DSP_SETDUPLEX:\n"); break; case SNDCTL_DSP_GETCAPS: DPF(2, "SNDCTL_DSP_GETCAPS:\n"); return put_user(DSP_CAP_DUPLEX | DSP_CAP_REALTIME | DSP_CAP_TRIGGER | DSP_CAP_MMAP | DSP_CAP_COPROC| DSP_CAP_MULTI, p); case SNDCTL_DSP_SPEED: DPF(2, "SNDCTL_DSP_SPEED:\n"); if (get_user(val, p)) return -EFAULT; DPD(2, "val is %d\n", val); if (val > 0) { if (file->f_mode & FMODE_READ) { struct wave_format format; spin_lock_irqsave(&wiinst->lock, flags); format = wiinst->format; format.samplingrate = val; if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { spin_unlock_irqrestore(&wiinst->lock, flags); return -EINVAL; } val = wiinst->format.samplingrate; spin_unlock_irqrestore(&wiinst->lock, flags); DPD(2, "set recording sampling rate -> %d\n", val); } if (file->f_mode & FMODE_WRITE) { struct wave_format format; spin_lock_irqsave(&woinst->lock, flags); format = woinst->format; format.samplingrate = val; if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { spin_unlock_irqrestore(&woinst->lock, flags); return -EINVAL; } val = woinst->format.samplingrate; spin_unlock_irqrestore(&woinst->lock, flags); DPD(2, "set playback sampling rate -> %d\n", val); } return put_user(val, p); } else { if (file->f_mode & FMODE_READ) val = wiinst->format.samplingrate; else if (file->f_mode & FMODE_WRITE) val = woinst->format.samplingrate; return put_user(val, p); } break; case SNDCTL_DSP_STEREO: DPF(2, "SNDCTL_DSP_STEREO:\n"); if (get_user(val, p)) return -EFAULT; DPD(2, " val is %d\n", val); if (file->f_mode & FMODE_READ) { struct wave_format format; spin_lock_irqsave(&wiinst->lock, flags); format = wiinst->format; format.channels = val ? 2 : 1; if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { spin_unlock_irqrestore(&wiinst->lock, flags); return -EINVAL; } val = wiinst->format.channels - 1; spin_unlock_irqrestore(&wiinst->lock, flags); DPD(2, "set recording stereo -> %d\n", val); } if (file->f_mode & FMODE_WRITE) { struct wave_format format; spin_lock_irqsave(&woinst->lock, flags); format = woinst->format; format.channels = val ? 2 : 1; if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { spin_unlock_irqrestore(&woinst->lock, flags); return -EINVAL; } val = woinst->format.channels - 1; spin_unlock_irqrestore(&woinst->lock, flags); DPD(2, "set playback stereo -> %d\n", val); } return put_user(val, p); break; case SNDCTL_DSP_CHANNELS: DPF(2, "SNDCTL_DSP_CHANNELS:\n"); if (get_user(val, p)) return -EFAULT; DPD(2, " val is %d\n", val); if (val > 0) { if (file->f_mode & FMODE_READ) { struct wave_format format; spin_lock_irqsave(&wiinst->lock, flags); format = wiinst->format; format.channels = val; if (emu10k1_wavein_setformat(wave_dev, &format) < 0) { spin_unlock_irqrestore(&wiinst->lock, flags); return -EINVAL; } val = wiinst->format.channels; spin_unlock_irqrestore(&wiinst->lock, flags); DPD(2, "set recording number of channels -> %d\n", val); } if (file->f_mode & FMODE_WRITE) { struct wave_format format; spin_lock_irqsave(&woinst->lock, flags); format = woinst->format; format.channels = val; if (emu10k1_waveout_setformat(wave_dev, &format) < 0) { spin_unlock_irqrestore(&woinst->lock, flags); return -EINVAL; } val = woinst->format.channels; spin_unlock_irqrestore(&woinst->lock, flags); DPD(2, "set playback number of channels -> %d\n", val); } return put_user(val, p); } else { if (file->f_mode & FMODE_READ) val = wiinst->format.channels; else if (file->f_mode & FMODE_WRITE) val = woinst->format.channels; return put_user(val, p); } break; case SNDCTL_DSP_GETFMTS: DPF(2, "SNDCTL_DSP_GETFMTS:\n"); if (file->f_mode & FMODE_READ) val = AFMT_S16_LE; else if (file->f_mode & FMODE_WRITE) { val = AFMT_S16_LE | AFMT_U8; if (emu10k1_find_control_gpr(&wave_dev->card->mgr, wave_dev->card->pt.patch_name,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -