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

📄 waveartist.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	my_dev = sound_install_audiodrv(AUDIO_DRIVER_VERSION, dev_name,			&waveartist_audio_driver, sizeof(struct audio_driver),			devc->audio_flags, AFMT_U8 | AFMT_S16_LE | AFMT_S8,			devc, devc->hw.dma, devc->hw.dma2);	if (my_dev < 0)		goto free;	audio_devs[my_dev]->portc = portc;	waveartist_mixer_reset(devc);	/*	 * clear any pending interrupt	 */	waveartist_iack(devc);	if (request_irq(devc->hw.irq, waveartist_intr, 0, devc->hw.name, devc) < 0) {		printk(KERN_ERR "%s: IRQ %d in use\n",			devc->hw.name, devc->hw.irq);		goto uninstall;	}	if (sound_alloc_dma(devc->hw.dma, devc->hw.name)) {		printk(KERN_ERR "%s: Can't allocate DMA%d\n",			devc->hw.name, devc->hw.dma);		goto uninstall_irq;	}	if (devc->hw.dma != devc->hw.dma2 && devc->hw.dma2 != NO_DMA)		if (sound_alloc_dma(devc->hw.dma2, devc->hw.name)) {			printk(KERN_ERR "%s: can't allocate DMA%d\n",				devc->hw.name, devc->hw.dma2);			goto uninstall_dma;		}	waveartist_set_ctlr(&devc->hw, 0, DMA1_IE | DMA0_IE);	audio_devs[my_dev]->mixer_dev =		sound_install_mixer(MIXER_DRIVER_VERSION,				dev_name,				&waveartist_mixer_operations,				sizeof(struct mixer_operations),				devc);	return my_dev;uninstall_dma:	sound_free_dma(devc->hw.dma);uninstall_irq:	free_irq(devc->hw.irq, devc);uninstall:	sound_unload_audiodev(my_dev);free:	kfree(portc);nomem:	return -1;}static int __init probe_waveartist(struct address_info *hw_config){	wavnc_info *devc = &adev_info[nr_waveartist_devs];	if (nr_waveartist_devs >= MAX_AUDIO_DEV) {		printk(KERN_WARNING "waveartist: too many audio devices\n");		return 0;	}	if (check_region(hw_config->io_base, 15))  {		printk(KERN_WARNING "WaveArtist: I/O port conflict\n");		return 0;	}	if (hw_config->irq > _ISA_IRQ(15) || hw_config->irq < _ISA_IRQ(0)) {		printk(KERN_WARNING "WaveArtist: Bad IRQ %d\n",		       hw_config->irq);		return 0;	}	if (hw_config->dma != _ISA_DMA(3)) {		printk(KERN_WARNING "WaveArtist: Bad DMA %d\n",		       hw_config->dma);		return 0;	}	hw_config->name = "WaveArtist";	devc->hw = *hw_config;	devc->open_mode = 0;	devc->chip_name = "RWA-010";	return 1;}static void __init attach_waveartist(struct address_info *hw){	wavnc_info *devc = &adev_info[nr_waveartist_devs];	/*	 * NOTE! If irq < 0, there is another driver which has allocated the	 *   IRQ so that this driver doesn't need to allocate/deallocate it.	 *   The actually used IRQ is ABS(irq).	 */	devc->hw = *hw;	devc->hw.irq = (hw->irq > 0) ? hw->irq : 0;	devc->open_mode = 0;	devc->playback_dev = 0;	devc->record_dev = 0;	devc->audio_flags = DMA_AUTOMODE;	devc->levels = levels;	if (hw->dma != hw->dma2 && hw->dma2 != NO_DMA)		devc->audio_flags |= DMA_DUPLEX;	request_region(hw->io_base, 15, devc->hw.name);	devc->dev_no = waveartist_init(devc);	if (devc->dev_no < 0)		release_region(hw->io_base, 15);	else {#ifdef CONFIG_ARCH_NETWINDER		if (machine_is_netwinder()) {			init_timer(&vnc_timer);			vnc_timer.function = vnc_slider_tick;			vnc_timer.expires  = jiffies;			vnc_timer.data     = nr_waveartist_devs;			add_timer(&vnc_timer);			vnc_configure_mixer(devc);		}#endif		nr_waveartist_devs += 1;	}}static void __exit unload_waveartist(struct address_info *hw){	wavnc_info *devc = NULL;	int i;	for (i = 0; i < nr_waveartist_devs; i++)		if (hw->io_base == adev_info[i].hw.io_base) {			devc = adev_info + i;			break;		}	if (devc != NULL) {		int mixer;#ifdef CONFIG_ARCH_NETWINDER		if (machine_is_netwinder())			del_timer(&vnc_timer);#endif		release_region(devc->hw.io_base, 15);		waveartist_set_ctlr(&devc->hw, DMA1_IE|DMA0_IE, 0);		if (devc->hw.irq >= 0)			free_irq(devc->hw.irq, devc);		sound_free_dma(devc->hw.dma);		if (devc->hw.dma != devc->hw.dma2 &&		    devc->hw.dma2 != NO_DMA)			sound_free_dma(devc->hw.dma2);		mixer = audio_devs[devc->dev_no]->mixer_dev;		if (mixer >= 0)			sound_unload_mixerdev(mixer);		if (devc->dev_no >= 0)			sound_unload_audiodev(devc->dev_no);		nr_waveartist_devs -= 1;		for (; i < nr_waveartist_devs; i++)			adev_info[i] = adev_info[i + 1];	} else		printk(KERN_WARNING "waveartist: can't find device "		       "to unload\n");}/* * Rebel.com Netwinder specifics... */#define	VNC_TIMER_PERIOD (HZ/4)	//check slider 4 times/sec#define	MIXER_PRIVATE3_RESET	0x53570000#define	MIXER_PRIVATE3_READ	0x53570001#define	MIXER_PRIVATE3_WRITE	0x53570002#define	VNC_MUTE_INTERNAL_SPKR	0x01	//the sw mute on/off control bit#define	VNC_MUTE_LINE_OUT	0x10#define VNC_PHONE_DETECT	0x20#define VNC_HANDSET_DETECT	0x40#define VNC_DISABLE_AUTOSWITCH	0x80extern spinlock_t gpio_lock;static inline voidvnc_update_spkr_mute(wavnc_info *devc){	unsigned long flags;	spin_lock_irqsave(&gpio_lock, flags);	cpld_modify(CPLD_UNMUTE, devc->spkr_mute_state ? 0 : CPLD_UNMUTE);	spin_unlock_irqrestore(&gpio_lock, flags);}static voidvnc_mute_lout(wavnc_info *devc, int mute){}static intvnc_volume_slider(wavnc_info *devc){	static signed int old_slider_volume;	unsigned long flags;	signed int volume = 255;	*CSR_TIMER1_LOAD = 0x00ffffff;	save_flags(flags);	cli();	outb(0xFF, 0x201);	*CSR_TIMER1_CNTL = TIMER_CNTL_ENABLE | TIMER_CNTL_DIV1;	while (volume && (inb(0x201) & 0x01))		volume--;	*CSR_TIMER1_CNTL = 0;	restore_flags(flags);		volume = 0x00ffffff - *CSR_TIMER1_VALUE;#ifndef REVERSE	volume = 150 - (volume >> 5);#else	volume = (volume >> 6) - 25;#endif	if (volume < 0)		volume = 0;	if (volume > 100)		volume = 100;	/*	 * slider quite often reads +-8, so debounce this random noise	 */	if (abs(volume - old_slider_volume) > 7) {		old_slider_volume = volume;		if (debug_flg & DEBUG_MIXER)			printk(KERN_DEBUG "Slider volume: %d.\n", volume);	}	return old_slider_volume;}static voidvnc_configure_mixer(wavnc_info *devc){	u_int vals[3];	if (!devc->no_autoselect) {		if (devc->handset_detect) {			devc->recmask = SOUND_MASK_LINE1;			devc->spkr_mute_state = devc->line_mute_state = 1;		} else if (devc->telephone_detect) {			devc->recmask = SOUND_MASK_PHONEIN;			devc->spkr_mute_state = devc->line_mute_state = 1;		} else {			/* unless someone has asked for LINE-IN,			 * we default to MIC			 */			if ((devc->recmask & SOUND_MASK_LINE) == 0)				devc->recmask = SOUND_MASK_MIC;			devc->spkr_mute_state = devc->line_mute_state = 0;		}		vnc_update_spkr_mute(devc);		vnc_mute_lout(devc, devc->spkr_mute_state);	}	/* Ok.  At this point, we have done the autoswitch logic, or we	 * have had a command from an ioctl.  We have a valid devc->recmask.	 * Now we have to connect up the hardware to reflect the recmask.	 */	vals[1] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x800);	vals[2] = waveartist_cmd1_r(devc, WACMD_GET_LEVEL | 0x900);	vals[1] &= ~0x3f;	switch(devc->recmask) {	case SOUND_MASK_MIC:		/* builtin mic */		waveartist_cmd1(devc, WACMD_SET_MONO | 0x100);	/* right */		vals[1] |= 0x28;		break;	case SOUND_MASK_LINE1:		/* out handset */		waveartist_cmd1(devc, WACMD_SET_MONO);		/* left */		vals[1] |= 0x05;		break;	case SOUND_MASK_PHONEIN:	/* our telephone mic */		waveartist_cmd1(devc, WACMD_SET_MONO);		/* left */		vals[1] |= 0x04;		break;	case SOUND_MASK_LINE:		/* stereo line in */		vals[1] |= 12;		break;	default:		return;	}	vals[0] = WACMD_SET_MIXER;	waveartist_cmd(devc, 3, vals, 0, NULL);	waveartist_mixer_update(devc, SOUND_MIXER_IMIX);}static intvnc_slider(wavnc_info *devc){	signed int slider_volume;	unsigned int temp, old_hs, old_td;	/*	 * read the "buttons" state.	 *  Bit 4 = handset present,	 *  Bit 5 = offhook	 */	temp = inb(0x201) & 0x30;	old_hs = devc->handset_detect;	old_td = devc->telephone_detect;	devc->handset_detect = !(temp & 0x10);	devc->telephone_detect = !!(temp & 0x20);	if (!devc->no_autoselect &&	    (old_hs != devc->handset_detect ||	     old_td != devc->telephone_detect))		vnc_configure_mixer(devc);	slider_volume = vnc_volume_slider(devc);	/*	 * If we're using software controlled volume, and	 * the slider moves by more than 20%, then we	 * switch back to slider controlled volume.	 */	if (abs(devc->slider_vol - slider_volume) > 20)		devc->use_slider = 1;	/*	 * use only left channel	 */	temp = levels[SOUND_MIXER_VOLUME] & 0xFF;	if (slider_volume != temp && devc->use_slider) {		devc->slider_vol = slider_volume;		waveartist_mixer_set(devc, SOUND_MIXER_VOLUME,			slider_volume | slider_volume << 8);		return 1;	}	return 0;}static voidvnc_slider_tick(unsigned long data){	int next_timeout;	if (vnc_slider(adev_info + data))		next_timeout = 5;	// mixer reported change	else		next_timeout = VNC_TIMER_PERIOD;	mod_timer(&vnc_timer, jiffies + next_timeout);}static intvnc_private_ioctl(int dev, unsigned int cmd, caddr_t arg){	wavnc_info *devc = (wavnc_info *)audio_devs[dev]->devc;	int val;	switch (cmd) {	case SOUND_MIXER_PRIVATE1:	{		u_int prev_spkr_mute, prev_line_mute, prev_auto_state;		int val;		if (get_user(val, (int *)arg))			return -EFAULT;		/* check if parameter is logical */		if (val & ~(VNC_MUTE_INTERNAL_SPKR |			    VNC_MUTE_LINE_OUT |			    VNC_DISABLE_AUTOSWITCH))			return -EINVAL;		prev_auto_state = devc->no_autoselect;		prev_spkr_mute  = devc->spkr_mute_state;		prev_line_mute  = devc->line_mute_state;		devc->no_autoselect   = (val & VNC_DISABLE_AUTOSWITCH) ? 1 : 0;		devc->spkr_mute_state = (val & VNC_MUTE_INTERNAL_SPKR) ? 1 : 0;		devc->line_mute_state = (val & VNC_MUTE_LINE_OUT) ? 1 : 0;		if (prev_spkr_mute != devc->spkr_mute_state)			vnc_update_spkr_mute(devc);		if (prev_line_mute != devc->line_mute_state)			vnc_mute_lout(devc, devc->line_mute_state);		if (prev_auto_state != devc->no_autoselect)			vnc_configure_mixer(devc);		return 0;	}	case SOUND_MIXER_PRIVATE2:		if (get_user(val, (int *)arg))			return -EFAULT;		switch (val) {#define VNC_SOUND_PAUSE         0x53    //to pause the DSP#define VNC_SOUND_RESUME        0x57    //to unpause the DSP		case VNC_SOUND_PAUSE:			waveartist_cmd1(devc, 0x16);			break;		case VNC_SOUND_RESUME:			waveartist_cmd1(devc, 0x18);			break;		default:			return -EINVAL;		}		return 0;	/* private ioctl to allow bulk access to waveartist */	case SOUND_MIXER_PRIVATE3:	{		unsigned long	flags;		int		mixer_reg[15], i, val;		if (get_user(val, (int *)arg))			return -EFAULT;		if (copy_from_user(mixer_reg, (void *)val, sizeof(mixer_reg)))			return -EFAULT;		switch (mixer_reg[14]) {		case MIXER_PRIVATE3_RESET:			waveartist_mixer_reset(devc);			break;		case MIXER_PRIVATE3_WRITE:			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[0], mixer_reg[4]);			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[1], mixer_reg[5]);			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[2], mixer_reg[6]);			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[3], mixer_reg[7]);			waveartist_cmd3(devc, WACMD_SET_MIXER, mixer_reg[8], mixer_reg[9]);			waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[10], mixer_reg[11]);			waveartist_cmd3(devc, WACMD_SET_LEVEL, mixer_reg[12], mixer_reg[13]);			break;		case MIXER_PRIVATE3_READ:			spin_lock_irqsave(&waveartist_lock, flags);			for (i = 0x30; i < 14 << 8; i += 1 << 8)				waveartist_cmd(devc, 1, &i, 1, mixer_reg + (i >> 8));			spin_unlock_irqrestore(&waveartist_lock, flags);			if (copy_to_user((void *)val, mixer_reg, sizeof(mixer_reg)))				return -EFAULT;			break;		default:			return -EINVAL;		}		return 0;	}	/* read back the state from PRIVATE1 */	case SOUND_MIXER_PRIVATE4:		val = (devc->spkr_mute_state  ? VNC_MUTE_INTERNAL_SPKR : 0) |		      (devc->line_mute_state  ? VNC_MUTE_LINE_OUT      : 0) |		      (devc->handset_detect   ? VNC_HANDSET_DETECT     : 0) |		      (devc->telephone_detect ? VNC_PHONE_DETECT       : 0) |		      (devc->no_autoselect    ? VNC_DISABLE_AUTOSWITCH : 0);		return put_user(val, (int *)arg) ? -EFAULT : 0;	}	if (((cmd >> 8) & 0xff) == 'M') {		if (_SIOC_DIR(cmd) & _SIOC_WRITE) {			/*			 * special case for master volume: if we			 * received this call - switch from hw			 * volume control to a software volume			 * control, till the hw volume is modified			 * to signal that user wants to be back in			 * hardware...			 */			if ((cmd & 0xff) == SOUND_MIXER_VOLUME)				devc->use_slider = 0;		} else if ((cmd & 0xff) == SOUND_MIXER_STEREODEVS) {			val = devc->supported_devices &				~(SOUND_MASK_IMIX |				  SOUND_MASK_MIC |				  SOUND_MASK_LINE1 |				  SOUND_MASK_PHONEIN |				  SOUND_MASK_PHONEOUT);			return put_user(val, (int *)arg) ? -EFAULT : 0;		}	}	return -ENOIOCTLCMD;}static struct address_info cfg;static int attached;static int __initdata io = 0;static int __initdata irq = 0;static int __initdata dma = 0;static int __initdata dma2 = 0;MODULE_PARM(io, "i");		/* IO base */MODULE_PARM(irq, "i");		/* IRQ */MODULE_PARM(dma, "i");		/* DMA */MODULE_PARM(dma2, "i");		/* DMA2 */static int __init init_waveartist(void){	if (!io && machine_is_netwinder()) {		/*		 * The NetWinder WaveArtist is at a fixed address.		 * If the user does not supply an address, use the		 * well-known parameters.		 */		io   = 0x250;		irq  = 12;		dma  = 3;		dma2 = 7;	}	cfg.io_base = io;	cfg.irq = irq;	cfg.dma = dma;	cfg.dma2 = dma2;	if (!probe_waveartist(&cfg))		return -ENODEV;	attach_waveartist(&cfg);	attached = 1;	return 0;}static void __exit cleanup_waveartist(void){	if (attached)		unload_waveartist(&cfg);}module_init(init_waveartist);module_exit(cleanup_waveartist);#ifndef MODULEstatic int __init setup_waveartist(char *str){	/* io, irq, dma, dma2 */	int ints[5];		str = get_options(str, ARRAY_SIZE(ints), ints);		io	= ints[1];	irq	= ints[2];	dma	= ints[3];	dma16	= ints[4];	return 1;}__setup("waveartist=", setup_waveartist);#endif

⌨️ 快捷键说明

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