⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sscape.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* *   Low-level ALSA driver for the ENSONIQ SoundScape PnP *   Copyright (c) by Chris Rankin * *   This driver was written in part using information obtained from *   the OSS/Free SoundScape driver, written by Hannu Savolainen. * * *   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., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA */#include <sound/driver.h>#include <linux/init.h>#include <linux/err.h>#include <linux/isa.h>#include <linux/delay.h>#include <linux/pnp.h>#include <linux/spinlock.h>#include <linux/moduleparam.h>#include <asm/dma.h>#include <sound/core.h>#include <sound/hwdep.h>#include <sound/cs4231.h>#include <sound/mpu401.h>#include <sound/initval.h>#include <sound/sscape_ioctl.h>MODULE_AUTHOR("Chris Rankin");MODULE_DESCRIPTION("ENSONIQ SoundScape PnP driver");MODULE_LICENSE("GPL");static int index[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IDX;static char* id[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_STR;static long port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;static long wss_port[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_PORT;static int irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;static int mpu_irq[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_IRQ;static int dma[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;static int dma2[SNDRV_CARDS] __devinitdata = SNDRV_DEFAULT_DMA;module_param_array(index, int, NULL, 0444);MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");module_param_array(id, charp, NULL, 0444);MODULE_PARM_DESC(id, "Description for SoundScape card");module_param_array(port, long, NULL, 0444);MODULE_PARM_DESC(port, "Port # for SoundScape driver.");module_param_array(wss_port, long, NULL, 0444);MODULE_PARM_DESC(wss_port, "WSS Port # for SoundScape driver.");module_param_array(irq, int, NULL, 0444);MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");module_param_array(mpu_irq, int, NULL, 0444);MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");module_param_array(dma, int, NULL, 0444);MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");module_param_array(dma2, int, NULL, 0444);MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");#ifdef CONFIG_PNPstatic int isa_registered;static int pnp_registered;static struct pnp_card_device_id sscape_pnpids[] = {	{ .id = "ENS3081", .devs = { { "ENS0000" } } }, /* Soundscape PnP */	{ .id = "ENS4081", .devs = { { "ENS1011" } } },	/* VIVO90 */	{ .id = "" }	/* end */};MODULE_DEVICE_TABLE(pnp_card, sscape_pnpids);#endif#define MPU401_IO(i)     ((i) + 0)#define MIDI_DATA_IO(i)  ((i) + 0)#define MIDI_CTRL_IO(i)  ((i) + 1)#define HOST_CTRL_IO(i)  ((i) + 2)#define HOST_DATA_IO(i)  ((i) + 3)#define ODIE_ADDR_IO(i)  ((i) + 4)#define ODIE_DATA_IO(i)  ((i) + 5)#define CODEC_IO(i)      ((i) + 8)#define IC_ODIE  1#define IC_OPUS  2#define RX_READY 0x01#define TX_READY 0x02#define CMD_ACK           0x80#define CMD_SET_MIDI_VOL  0x84#define CMD_GET_MIDI_VOL  0x85#define CMD_XXX_MIDI_VOL  0x86#define CMD_SET_EXTMIDI   0x8a#define CMD_GET_EXTMIDI   0x8b#define CMD_SET_MT32      0x8c#define CMD_GET_MT32      0x8denum GA_REG {	GA_INTSTAT_REG = 0,	GA_INTENA_REG,	GA_DMAA_REG,	GA_DMAB_REG,	GA_INTCFG_REG,	GA_DMACFG_REG,	GA_CDCFG_REG,	GA_SMCFGA_REG,	GA_SMCFGB_REG,	GA_HMCTL_REG};#define DMA_8BIT  0x80#define AD1845_FREQ_SEL_MSB    0x16#define AD1845_FREQ_SEL_LSB    0x17enum card_type {	SSCAPE,	SSCAPE_PNP,	SSCAPE_VIVO,};struct soundscape {	spinlock_t lock;	unsigned io_base;	unsigned wss_base;	int codec_type;	int ic_type;	enum card_type type;	struct resource *io_res;	struct resource *wss_res;	struct snd_cs4231 *chip;	struct snd_mpu401 *mpu;	struct snd_hwdep *hw;	/*	 * The MIDI device won't work until we've loaded	 * its firmware via a hardware-dependent device IOCTL	 */	spinlock_t fwlock;	int hw_in_use;	unsigned long midi_usage;	unsigned char midi_vol;};#define INVALID_IRQ  ((unsigned)-1)static inline struct soundscape *get_card_soundscape(struct snd_card *c){	return (struct soundscape *) (c->private_data);}static inline struct soundscape *get_mpu401_soundscape(struct snd_mpu401 * mpu){	return (struct soundscape *) (mpu->private_data);}static inline struct soundscape *get_hwdep_soundscape(struct snd_hwdep * hw){	return (struct soundscape *) (hw->private_data);}/* * Allocates some kernel memory that we can use for DMA. * I think this means that the memory has to map to * contiguous pages of physical memory. */static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, unsigned long size){	if (buf) {		if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(),						 size, buf) < 0) {			snd_printk(KERN_ERR "sscape: Failed to allocate %lu bytes for DMA\n", size);			return NULL;		}	}	return buf;}/* * Release the DMA-able kernel memory ... */static void free_dmabuf(struct snd_dma_buffer *buf){	if (buf && buf->area)		snd_dma_free_pages(buf);}/* * This function writes to the SoundScape's control registers, * but doesn't do any locking. It's up to the caller to do that. * This is why this function is "unsafe" ... */static inline void sscape_write_unsafe(unsigned io_base, enum GA_REG reg, unsigned char val){	outb(reg, ODIE_ADDR_IO(io_base));	outb(val, ODIE_DATA_IO(io_base));}/* * Write to the SoundScape's control registers, and do the * necessary locking ... */static void sscape_write(struct soundscape *s, enum GA_REG reg, unsigned char val){	unsigned long flags;	spin_lock_irqsave(&s->lock, flags);	sscape_write_unsafe(s->io_base, reg, val);	spin_unlock_irqrestore(&s->lock, flags);}/* * Read from the SoundScape's control registers, but leave any * locking to the caller. This is why the function is "unsafe" ... */static inline unsigned char sscape_read_unsafe(unsigned io_base, enum GA_REG reg){	outb(reg, ODIE_ADDR_IO(io_base));	return inb(ODIE_DATA_IO(io_base));}/* * Puts the SoundScape into "host" mode, as compared to "MIDI" mode */static inline void set_host_mode_unsafe(unsigned io_base){	outb(0x0, HOST_CTRL_IO(io_base));}/* * Puts the SoundScape into "MIDI" mode, as compared to "host" mode */static inline void set_midi_mode_unsafe(unsigned io_base){	outb(0x3, HOST_CTRL_IO(io_base));}/* * Read the SoundScape's host-mode control register, but leave * any locking issues to the caller ... */static inline int host_read_unsafe(unsigned io_base){	int data = -1;	if ((inb(HOST_CTRL_IO(io_base)) & RX_READY) != 0) {		data = inb(HOST_DATA_IO(io_base));	}	return data;}/* * Read the SoundScape's host-mode control register, performing * a limited amount of busy-waiting if the register isn't ready. * Also leaves all locking-issues to the caller ... */static int host_read_ctrl_unsafe(unsigned io_base, unsigned timeout){	int data;	while (((data = host_read_unsafe(io_base)) < 0) && (timeout != 0)) {		udelay(100);		--timeout;	} /* while */	return data;}/* * Write to the SoundScape's host-mode control registers, but * leave any locking issues to the caller ... */static inline int host_write_unsafe(unsigned io_base, unsigned char data){	if ((inb(HOST_CTRL_IO(io_base)) & TX_READY) != 0) {		outb(data, HOST_DATA_IO(io_base));		return 1;	}	return 0;}/* * Write to the SoundScape's host-mode control registers, performing * a limited amount of busy-waiting if the register isn't ready. * Also leaves all locking-issues to the caller ... */static int host_write_ctrl_unsafe(unsigned io_base, unsigned char data,                                  unsigned timeout){	int err;	while (!(err = host_write_unsafe(io_base, data)) && (timeout != 0)) {		udelay(100);		--timeout;	} /* while */	return err;}/* * Check that the MIDI subsystem is operational. If it isn't, * then we will hang the computer if we try to use it ... * * NOTE: This check is based upon observation, not documentation. */static inline int verify_mpu401(const struct snd_mpu401 * mpu){	return ((inb(MIDI_CTRL_IO(mpu->port)) & 0xc0) == 0x80);}/* * This is apparently the standard way to initailise an MPU-401 */static inline void initialise_mpu401(const struct snd_mpu401 * mpu){	outb(0, MIDI_DATA_IO(mpu->port));}/* * Tell the SoundScape to activate the AD1845 chip (I think). * The AD1845 detection fails if we *don't* do this, so I * think that this is a good idea ... */static inline void activate_ad1845_unsafe(unsigned io_base){	sscape_write_unsafe(io_base, GA_HMCTL_REG, (sscape_read_unsafe(io_base, GA_HMCTL_REG) & 0xcf) | 0x10);	sscape_write_unsafe(io_base, GA_CDCFG_REG, 0x80);}/* * Do the necessary ALSA-level cleanup to deallocate our driver ... */static void soundscape_free(struct snd_card *c){	struct soundscape *sscape = get_card_soundscape(c);	release_and_free_resource(sscape->io_res);	release_and_free_resource(sscape->wss_res);	free_dma(sscape->chip->dma1);}/* * Tell the SoundScape to begin a DMA tranfer using the given channel. * All locking issues are left to the caller. */static inline void sscape_start_dma_unsafe(unsigned io_base, enum GA_REG reg){	sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) | 0x01);	sscape_write_unsafe(io_base, reg, sscape_read_unsafe(io_base, reg) & 0xfe);}/* * Wait for a DMA transfer to complete. This is a "limited busy-wait", * and all locking issues are left to the caller. */static int sscape_wait_dma_unsafe(unsigned io_base, enum GA_REG reg, unsigned timeout){	while (!(sscape_read_unsafe(io_base, reg) & 0x01) && (timeout != 0)) {		udelay(100);		--timeout;	} /* while */	return (sscape_read_unsafe(io_base, reg) & 0x01);}/* * Wait for the On-Board Processor to return its start-up * acknowledgement sequence. This wait is too long for * us to perform "busy-waiting", and so we must sleep. * This in turn means that we must not be holding any * spinlocks when we call this function. */static int obp_startup_ack(struct soundscape *s, unsigned timeout){	while (timeout != 0) {		unsigned long flags;		unsigned char x;		schedule_timeout_uninterruptible(1);		spin_lock_irqsave(&s->lock, flags);		x = inb(HOST_DATA_IO(s->io_base));		spin_unlock_irqrestore(&s->lock, flags);		if ((x & 0xfe) == 0xfe)			return 1;		--timeout;	} /* while */	return 0;}/* * Wait for the host to return its start-up acknowledgement * sequence. This wait is too long for us to perform * "busy-waiting", and so we must sleep. This in turn means * that we must not be holding any spinlocks when we call * this function. */static int host_startup_ack(struct soundscape *s, unsigned timeout){	while (timeout != 0) {		unsigned long flags;		unsigned char x;		schedule_timeout_uninterruptible(1);		spin_lock_irqsave(&s->lock, flags);		x = inb(HOST_DATA_IO(s->io_base));		spin_unlock_irqrestore(&s->lock, flags);		if (x == 0xfe)			return 1;		--timeout;	} /* while */	return 0;}/* * Upload a byte-stream into the SoundScape using DMA channel A. */static int upload_dma_data(struct soundscape *s,                           const unsigned char __user *data,                           size_t size){	unsigned long flags;	struct snd_dma_buffer dma;	int ret;	if (!get_dmabuf(&dma, PAGE_ALIGN(size)))		return -ENOMEM;	spin_lock_irqsave(&s->lock, flags);	/*	 * Reset the board ...	 */	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) & 0x3f);	/*	 * Enable the DMA channels and configure them ...	 */	sscape_write_unsafe(s->io_base, GA_DMACFG_REG, 0x50);	sscape_write_unsafe(s->io_base, GA_DMAA_REG, (s->chip->dma1 << 4) | DMA_8BIT);	sscape_write_unsafe(s->io_base, GA_DMAB_REG, 0x20);	/*	 * Take the board out of reset ...	 */	sscape_write_unsafe(s->io_base, GA_HMCTL_REG, sscape_read_unsafe(s->io_base, GA_HMCTL_REG) | 0x80);	/*	 * Upload the user's data (firmware?) to the SoundScape	 * board through the DMA channel ...	 */	while (size != 0) {		unsigned long len;		/*		 * Apparently, copying to/from userspace can sleep.		 * We are therefore forbidden from holding any		 * spinlocks while we copy ...		 */		spin_unlock_irqrestore(&s->lock, flags);		/*		 * Remember that the data that we want to DMA		 * comes from USERSPACE. We have already verified		 * the userspace pointer ...		 */		len = min(size, dma.bytes);		len -= __copy_from_user(dma.area, data, len);		data += len;		size -= len;		/*		 * Grab that spinlock again, now that we've		 * finished copying!		 */		spin_lock_irqsave(&s->lock, flags);		snd_dma_program(s->chip->dma1, dma.addr, len, DMA_MODE_WRITE);		sscape_start_dma_unsafe(s->io_base, GA_DMAA_REG);		if (!sscape_wait_dma_unsafe(s->io_base, GA_DMAA_REG, 5000)) {			/*			 * Don't forget to release this spinlock we're holding ...			 */			spin_unlock_irqrestore(&s->lock, flags);			snd_printk(KERN_ERR "sscape: DMA upload has timed out\n");			ret = -EAGAIN;			goto _release_dma;		}	} /* while */

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -