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

📄 pas2_pcm.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
字号:
/* * pas2_pcm.c Audio routines for PAS16 * * * 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   : ioctl code reworked (vmalloc/vfree removed) * Alan Cox	   : Swatted a double allocation of device bug. Made a few *		     more things module options. * Bartlomiej Zolnierkiewicz : Added __init to pas_pcm_init() */#include <linux/init.h>#include "sound_config.h"#include "pas2.h"#ifndef DEB#define DEB(WHAT)#endif#define PAS_PCM_INTRBITS (0x08)/* * Sample buffer timer interrupt enable */#define PCM_NON	0#define PCM_DAC	1#define PCM_ADC	2static unsigned long pcm_speed = 0;	/* sampling rate */static unsigned char pcm_channels = 1;	/* channels (1 or 2) */static unsigned char pcm_bits = 8;	/* bits/sample (8 or 16) */static unsigned char pcm_filter = 0;	/* filter FLAG */static unsigned char pcm_mode = PCM_NON;static unsigned long pcm_count = 0;static unsigned short pcm_bitsok = 8;	/* mask of OK bits */static int      pcm_busy = 0;int             pas_audiodev = -1;static int      open_mode = 0;static int pcm_set_speed(int arg){	int foo, tmp;	unsigned long flags;	if (arg == 0)		return pcm_speed;	if (arg > 44100)		arg = 44100;	if (arg < 5000)		arg = 5000;	if (pcm_channels & 2)	{		foo = (596590 + (arg / 2)) / arg;		arg = (596590 + (foo / 2)) / foo;	}	else	{		foo = (1193180 + (arg / 2)) / arg;		arg = (1193180 + (foo / 2)) / foo;	}	pcm_speed = arg;	tmp = pas_read(0x0B8A);	/*	 * Set anti-aliasing filters according to sample rate. You really *NEED*	 * to enable this feature for all normal recording unless you want to	 * experiment with aliasing effects.	 * These filters apply to the selected "recording" source.	 * I (pfw) don't know the encoding of these 5 bits. The values shown	 * come from the SDK found on ftp.uwp.edu:/pub/msdos/proaudio/.	 *	 * I cleared bit 5 of these values, since that bit controls the master	 * mute flag. (Olav W鰈felschneider)	 *	 */#if !defined NO_AUTO_FILTER_SET	tmp &= 0xe0;	if (pcm_speed >= 2 * 17897)		tmp |= 0x01;	else if (pcm_speed >= 2 * 15909)		tmp |= 0x02;	else if (pcm_speed >= 2 * 11931)		tmp |= 0x09;	else if (pcm_speed >= 2 * 8948)		tmp |= 0x11;	else if (pcm_speed >= 2 * 5965)		tmp |= 0x19;	else if (pcm_speed >= 2 * 2982)		tmp |= 0x04;	pcm_filter = tmp;#endif	save_flags(flags);	cli();	pas_write(tmp & ~(0x40 | 0x80), 0x0B8A);	pas_write(0x00 | 0x30 | 0x04, 0x138B);	pas_write(foo & 0xff, 0x1388);	pas_write((foo >> 8) & 0xff, 0x1388);	pas_write(tmp, 0x0B8A);	restore_flags(flags);	return pcm_speed;}static int pcm_set_channels(int arg){	if ((arg != 1) && (arg != 2))		return pcm_channels;	if (arg != pcm_channels)	{		pas_write(pas_read(0xF8A) ^ 0x20, 0xF8A);		pcm_channels = arg;		pcm_set_speed(pcm_speed);	/* The speed must be reinitialized */	}	return pcm_channels;}static int pcm_set_bits(int arg){	if (arg == 0)		return pcm_bits;	if ((arg & pcm_bitsok) != arg)		return pcm_bits;	if (arg != pcm_bits)	{		pas_write(pas_read(0x8389) ^ 0x04, 0x8389);		pcm_bits = arg;	}	return pcm_bits;}static int pas_audio_ioctl(int dev, unsigned int cmd, caddr_t arg){	int val, ret;	DEB(printk("pas2_pcm.c: static int pas_audio_ioctl(unsigned int cmd = %X, unsigned int arg = %X)\n", cmd, arg));	switch (cmd) 	{	case SOUND_PCM_WRITE_RATE:		if (get_user(val, (int *)arg)) 			return -EFAULT;		ret = pcm_set_speed(val);		break;	case SOUND_PCM_READ_RATE:		ret = pcm_speed;		break;			case SNDCTL_DSP_STEREO:		if (get_user(val, (int *)arg)) 			return -EFAULT;		ret = pcm_set_channels(val + 1) - 1;		break;	case SOUND_PCM_WRITE_CHANNELS:		if (get_user(val, (int *)arg)) 			return -EFAULT;		ret = pcm_set_channels(val);		break;	case SOUND_PCM_READ_CHANNELS:		ret = pcm_channels;		break;	case SNDCTL_DSP_SETFMT:		if (get_user(val, (int *)arg))			return -EFAULT;		ret = pcm_set_bits(val);		break;			case SOUND_PCM_READ_BITS:		ret = pcm_bits;		break;  	default:		return -EINVAL;	}	return put_user(ret, (int *)arg);}static void pas_audio_reset(int dev){	DEB(printk("pas2_pcm.c: static void pas_audio_reset(void)\n"));	pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);	/* Disable PCM */}static int pas_audio_open(int dev, int mode){	int             err;	unsigned long   flags;	DEB(printk("pas2_pcm.c: static int pas_audio_open(int mode = %X)\n", mode));	save_flags(flags);	cli();	if (pcm_busy)	{		restore_flags(flags);		return -EBUSY;	}	pcm_busy = 1;	restore_flags(flags);	if ((err = pas_set_intr(PAS_PCM_INTRBITS)) < 0)		return err;	pcm_count = 0;	open_mode = mode;	return 0;}static void pas_audio_close(int dev){	unsigned long   flags;	DEB(printk("pas2_pcm.c: static void pas_audio_close(void)\n"));	save_flags(flags);	cli();	pas_audio_reset(dev);	pas_remove_intr(PAS_PCM_INTRBITS);	pcm_mode = PCM_NON;	pcm_busy = 0;	restore_flags(flags);}static void pas_audio_output_block(int dev, unsigned long buf, int count,		       int intrflag){	unsigned long   flags, cnt;	DEB(printk("pas2_pcm.c: static void pas_audio_output_block(char *buf = %P, int count = %X)\n", buf, count));	cnt = count;	if (audio_devs[dev]->dmap_out->dma > 3)		cnt >>= 1;	if (audio_devs[dev]->flags & DMA_AUTOMODE &&	    intrflag &&	    cnt == pcm_count)		return;	save_flags(flags);	cli();	pas_write(pas_read(0xF8A) & ~0x40,		  0xF8A);	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_WRITE); */	if (audio_devs[dev]->dmap_out->dma > 3)		count >>= 1;	if (count != pcm_count)	{		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);		pas_write(0x40 | 0x30 | 0x04, 0x138B);		pas_write(count & 0xff, 0x1389);		pas_write((count >> 8) & 0xff, 0x1389);		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);		pcm_count = count;	}	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);#ifdef NO_TRIGGER	pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);#endif	pcm_mode = PCM_DAC;	restore_flags(flags);}static void pas_audio_start_input(int dev, unsigned long buf, int count,		      int intrflag){	unsigned long   flags;	int             cnt;	DEB(printk("pas2_pcm.c: static void pas_audio_start_input(char *buf = %P, int count = %X)\n", buf, count));	cnt = count;	if (audio_devs[dev]->dmap_out->dma > 3)		cnt >>= 1;	if (audio_devs[pas_audiodev]->flags & DMA_AUTOMODE &&	    intrflag &&	    cnt == pcm_count)		return;	save_flags(flags);	cli();	/* DMAbuf_start_dma (dev, buf, count, DMA_MODE_READ); */	if (audio_devs[dev]->dmap_out->dma > 3)		count >>= 1;	if (count != pcm_count)	{		pas_write(pas_read(0x0B8A) & ~0x80, 0x0B8A);		pas_write(0x40 | 0x30 | 0x04, 0x138B);		pas_write(count & 0xff, 0x1389);		pas_write((count >> 8) & 0xff, 0x1389);		pas_write(pas_read(0x0B8A) | 0x80, 0x0B8A);		pcm_count = count;	}	pas_write(pas_read(0x0B8A) | 0x80 | 0x40, 0x0B8A);#ifdef NO_TRIGGER	pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);#endif	pcm_mode = PCM_ADC;	restore_flags(flags);}#ifndef NO_TRIGGERstatic void pas_audio_trigger(int dev, int state){	unsigned long   flags;	save_flags(flags);	cli();	state &= open_mode;	if (state & PCM_ENABLE_OUTPUT)		pas_write(pas_read(0xF8A) | 0x40 | 0x10, 0xF8A);	else if (state & PCM_ENABLE_INPUT)		pas_write((pas_read(0xF8A) | 0x40) & ~0x10, 0xF8A);	else		pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);	restore_flags(flags);}#endifstatic int pas_audio_prepare_for_input(int dev, int bsize, int bcount){	pas_audio_reset(dev);	return 0;}static int pas_audio_prepare_for_output(int dev, int bsize, int bcount){	pas_audio_reset(dev);	return 0;}static struct audio_driver pas_audio_driver ={	owner:		THIS_MODULE,	open:		pas_audio_open,	close:		pas_audio_close,	output_block:	pas_audio_output_block,	start_input:	pas_audio_start_input,	ioctl:		pas_audio_ioctl,	prepare_for_input:	pas_audio_prepare_for_input,	prepare_for_output:	pas_audio_prepare_for_output,	halt_io:		pas_audio_reset,	trigger:	pas_audio_trigger};void __init pas_pcm_init(struct address_info *hw_config){	DEB(printk("pas2_pcm.c: long pas_pcm_init()\n"));	pcm_bitsok = 8;	if (pas_read(0xEF8B) & 0x08)		pcm_bitsok |= 16;	pcm_set_speed(DSP_DEFAULT_SPEED);	if ((pas_audiodev = sound_install_audiodrv(AUDIO_DRIVER_VERSION,					"Pro Audio Spectrum",					&pas_audio_driver,					sizeof(struct audio_driver),					DMA_AUTOMODE,					AFMT_U8 | AFMT_S16_LE,					NULL,					hw_config->dma,					hw_config->dma)) < 0)		printk(KERN_WARNING "PAS16: Too many PCM devices available\n");}void pas_pcm_interrupt(unsigned char status, int cause){	if (cause == 1)	{		/*		 * Halt the PCM first. Otherwise we don't have time to start a new		 * block before the PCM chip proceeds to the next sample		 */		if (!(audio_devs[pas_audiodev]->flags & DMA_AUTOMODE))			pas_write(pas_read(0xF8A) & ~0x40, 0xF8A);		switch (pcm_mode)		{			case PCM_DAC:				DMAbuf_outputintr(pas_audiodev, 1);				break;			case PCM_ADC:				DMAbuf_inputintr(pas_audiodev);				break;			default:				printk(KERN_WARNING "PAS: Unexpected PCM interrupt\n");		}	}}

⌨️ 快捷键说明

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