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

📄 ali5455.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 5 页
字号:
		/* 		 * This will make sure that our LVI is correct, that our		 * pointer is updated, and that the DAC is running.  We		 * have to force the setting of dmabuf->trigger to avoid		 * any possible deadlocks.		 */		if (!dmabuf->enable) {			dmabuf->trigger = PCM_ENABLE_OUTPUT;			ali_update_lvi(state, 0);		}		if (signal_pending(current) && signals_allowed) {			break;		}		/* It seems that we have to set the current state to		 * TASK_INTERRUPTIBLE every time to make the process		 * really go to sleep.  This also has to be *after* the		 * update_ptr() call because update_ptr is likely to		 * do a wake_up() which will unset this before we ever		 * try to sleep, resuling in a tight loop in this code		 * instead of actually sleeping and waiting for an		 * interrupt to wake us up!		 */		set_current_state(TASK_INTERRUPTIBLE);		/*		 * set the timeout to significantly longer than it *should*		 * take for the DAC to drain the DMA buffer		 */		tmo = (count * HZ) / (dmabuf->rate);		if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {			printk(KERN_ERR "ali_audio: drain_dac, dma timeout?\n");			count = 0;			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&dmabuf->wait, &wait);	if (count > 0 && signal_pending(current) && signals_allowed)		return -ERESTARTSYS;	stop_dac(state);	return 0;}static int drain_spdifout(struct ali_state *state, int signals_allowed){	DECLARE_WAITQUEUE(wait, current);	struct dmabuf *dmabuf = &state->dmabuf;	unsigned long flags;	unsigned long tmo;	int count;	if (!dmabuf->ready)		return 0;	if (dmabuf->mapped) {		stop_spdifout(state);		return 0;	}	add_wait_queue(&dmabuf->wait, &wait);	for (;;) {		spin_lock_irqsave(&state->card->lock, flags);		ali_update_ptr(state);		count = dmabuf->count;		spin_unlock_irqrestore(&state->card->lock, flags);		if (count <= 0)			break;		/* 		 * This will make sure that our LVI is correct, that our		 * pointer is updated, and that the DAC is running.  We		 * have to force the setting of dmabuf->trigger to avoid		 * any possible deadlocks.		 */		if (!dmabuf->enable) {			if (codec_independent_spdif_locked > 0) {				dmabuf->trigger = SPDIF_ENABLE_OUTPUT;				ali_update_lvi(state, 2);			} else {				if (controller_independent_spdif_locked > 0) {					dmabuf->trigger = SPDIF_ENABLE_OUTPUT;					ali_update_lvi(state, 3);				}			}		}		if (signal_pending(current) && signals_allowed) {			break;		}		/* It seems that we have to set the current state to		 * TASK_INTERRUPTIBLE every time to make the process		 * really go to sleep.  This also has to be *after* the		 * update_ptr() call because update_ptr is likely to		 * do a wake_up() which will unset this before we ever		 * try to sleep, resuling in a tight loop in this code		 * instead of actually sleeping and waiting for an		 * interrupt to wake us up!		 */		set_current_state(TASK_INTERRUPTIBLE);		/*		 * set the timeout to significantly longer than it *should*		 * take for the DAC to drain the DMA buffer		 */		tmo = (count * HZ) / (dmabuf->rate);		if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {			printk(KERN_ERR "ali_audio: drain_spdifout, dma timeout?\n");			count = 0;			break;		}	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&dmabuf->wait, &wait);	if (count > 0 && signal_pending(current) && signals_allowed)		return -ERESTARTSYS;	stop_spdifout(state);	return 0;}static void ali_channel_interrupt(struct ali_card *card){	int i, count;		for (i = 0; i < NR_HW_CH; i++) {		struct ali_state *state = card->states[i];		struct ali_channel *c = NULL;		struct dmabuf *dmabuf;		unsigned long port = card->iobase;		u16 status;		if (!state)			continue;		if (!state->dmabuf.ready)			continue;		dmabuf = &state->dmabuf;		if (codec_independent_spdif_locked > 0) {			if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {				c = dmabuf->codec_spdifout_channel;			}		} else {			if (controller_independent_spdif_locked > 0) {				if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)					c = dmabuf->controller_spdifout_channel;			} else {				if (dmabuf->enable & DAC_RUNNING) {					c = dmabuf->write_channel;				} else if (dmabuf->enable & ADC_RUNNING) {					c = dmabuf->read_channel;				} else					continue;			}		}		port += c->port;		status = inw(port + OFF_SR);		if (status & DMA_INT_COMPLETE) {			/* only wake_up() waiters if this interrupt signals			 * us being beyond a userfragsize of data open or			 * available, and ali_update_ptr() does that for			 * us			 */			ali_update_ptr(state);		}		if (status & DMA_INT_LVI) {			ali_update_ptr(state);			wake_up(&dmabuf->wait);			if (dmabuf->enable & DAC_RUNNING)				count = dmabuf->count;			else if (dmabuf->enable & ADC_RUNNING)				count = dmabuf->dmasize - dmabuf->count;			else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)				count = dmabuf->count;			else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)				count = dmabuf->count;			else count = 0;			if (count > 0) {				if (dmabuf->enable & DAC_RUNNING)					outl((1 << 1), state->card->iobase + ALI_DMACR);				else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)						outl((1 << 3), state->card->iobase + ALI_DMACR);				else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)					outl((1 << 7), state->card->iobase + ALI_DMACR);			} else {				if (dmabuf->enable & DAC_RUNNING)					__stop_dac(state);				if (dmabuf->enable & ADC_RUNNING)					__stop_adc(state);				if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)					__stop_spdifout(state);				if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)					__stop_spdifout(state);				dmabuf->enable = 0;				wake_up(&dmabuf->wait);			}		}		if (!(status & DMA_INT_DCH)) {			ali_update_ptr(state);			wake_up(&dmabuf->wait);			if (dmabuf->enable & DAC_RUNNING)				count = dmabuf->count;			else if (dmabuf->enable & ADC_RUNNING)				count = dmabuf->dmasize - dmabuf->count;			else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)				count = dmabuf->count;			else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)				count = dmabuf->count;			else				count = 0;			if (count > 0) {				if (dmabuf->enable & DAC_RUNNING)					outl((1 << 1), state->card->iobase + ALI_DMACR);				else if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)					outl((1 << 3), state->card->iobase + ALI_DMACR);				else if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)					outl((1 << 7), state->card->iobase + ALI_DMACR);			} else {				if (dmabuf->enable & DAC_RUNNING)					__stop_dac(state);				if (dmabuf->enable & ADC_RUNNING)					__stop_adc(state);				if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING)					__stop_spdifout(state);				if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)					__stop_spdifout(state);				dmabuf->enable = 0;				wake_up(&dmabuf->wait);			}		}		outw(status & DMA_INT_MASK, port + OFF_SR);	}}static irqreturn_t ali_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct ali_card *card = (struct ali_card *) dev_id;	u32 status;	u16 status2;	spin_lock(&card->lock);	status = inl(card->iobase + ALI_INTERRUPTSR);	if (!(status & INT_MASK)) {		spin_unlock(&card->lock);		return IRQ_NONE;		/* not for us */	}	if (codec_independent_spdif_locked > 0) {		if (globel == 0) {			globel += 1;			status2 = inw(card->iobase + 0x76);			outw(status2 | 0x000c, card->iobase + 0x76);		} else {			if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT))				ali_channel_interrupt(card);		}	} else {		if (status & (INT_PCMOUT | INT_PCMIN | INT_MICIN | INT_SPDIFOUT | INT_CODECSPDIFOUT))			ali_channel_interrupt(card);	}	/* clear 'em */	outl(status & INT_MASK, card->iobase + ALI_INTERRUPTSR);	spin_unlock(&card->lock);	return IRQ_HANDLED;}/* in this loop, dmabuf.count signifies the amount of data that is   waiting to be copied to the user's buffer.  It is filled by the dma   machine and drained by this loop. */static ssize_t ali_read(struct file *file, char __user *buffer,			size_t count, loff_t * ppos){	struct ali_state *state = (struct ali_state *) file->private_data;	struct ali_card *card = state ? state->card : NULL;	struct dmabuf *dmabuf = &state->dmabuf;	ssize_t ret;	unsigned long flags;	unsigned int swptr;	int cnt;	DECLARE_WAITQUEUE(waita, current);#ifdef DEBUG2	printk("ali_audio: ali_read called, count = %d\n", count);#endif	if (dmabuf->mapped)		return -ENXIO;	if (dmabuf->enable & DAC_RUNNING)		return -ENODEV;	if (!dmabuf->read_channel) {		dmabuf->ready = 0;		dmabuf->read_channel = card->alloc_rec_pcm_channel(card);		if (!dmabuf->read_channel) {			return -EBUSY;		}	}	if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))		return ret;	if (!access_ok(VERIFY_WRITE, buffer, count))		return -EFAULT;	ret = 0;	add_wait_queue(&dmabuf->wait, &waita);	while (count > 0) {		set_current_state(TASK_INTERRUPTIBLE);		spin_lock_irqsave(&card->lock, flags);		if (PM_SUSPENDED(card)) {			spin_unlock_irqrestore(&card->lock, flags);			schedule();			if (signal_pending(current)) {				if (!ret)					ret = -EAGAIN;				break;			}			continue;		}		swptr = dmabuf->swptr;		cnt = ali_get_available_read_data(state);		// this is to make the copy_to_user simpler below		if (cnt > (dmabuf->dmasize - swptr))			cnt = dmabuf->dmasize - swptr;		spin_unlock_irqrestore(&card->lock, flags);		if (cnt > count)			cnt = count;		/* Lop off the last two bits to force the code to always		 * write in full samples.  This keeps software that sets		 * O_NONBLOCK but doesn't check the return value of the		 * write call from getting things out of state where they		 * think a full 4 byte sample was written when really only		 * a portion was, resulting in odd sound and stereo		 * hysteresis.		 */		cnt &= ~0x3;		if (cnt <= 0) {			unsigned long tmo;			/*			 * Don't let us deadlock.  The ADC won't start if			 * dmabuf->trigger isn't set.  A call to SETTRIGGER			 * could have turned it off after we set it to on			 * previously.			 */			dmabuf->trigger = PCM_ENABLE_INPUT;			/*			 * This does three things.  Updates LVI to be correct,			 * makes sure the ADC is running, and updates the			 * hwptr.			 */			ali_update_lvi(state, 1);			if (file->f_flags & O_NONBLOCK) {				if (!ret)					ret = -EAGAIN;				goto done;			}			/* Set the timeout to how long it would take to fill			 * two of our buffers.  If we haven't been woke up			 * by then, then we know something is wrong.			 */			tmo = (dmabuf->dmasize * HZ * 2) / (dmabuf->rate * 4);			    			/* There are two situations when sleep_on_timeout returns, one is when			   the interrupt is serviced correctly and the process is waked up by			   ISR ON TIME. Another is when timeout is expired, which means that			   either interrupt is NOT serviced correctly (pending interrupt) or it			   is TOO LATE for the process to be scheduled to run (scheduler latency)			   which results in a (potential) buffer overrun. And worse, there is			   NOTHING we can do to prevent it. */			if (!schedule_timeout(tmo >= 2 ? tmo : 2)) {				printk(KERN_ERR				       "ali_audio: recording schedule timeout, "				       "dmasz %u fragsz %u count %i hwptr %u swptr %u\n",				       dmabuf->dmasize, dmabuf->fragsize,				       dmabuf->count, dmabuf->hwptr,				       dmabuf->swptr);				/* a buffer overrun, we delay the recovery until next time the				   while loop begin and we REALLY have space to record */			}			if (signal_pending(current)) {				ret = ret ? ret : -ERESTARTSYS;				goto done;			}			continue;		}		if (copy_to_user(buffer, dmabuf->rawbuf + swptr, cnt)) {			if (!ret)				ret = -EFAULT;			goto done;		}		swptr = (swptr + cnt) % dmabuf->dmasize;		spin_lock_irqsave(&card->lock, flags);		if (PM_SUSPENDED(card)) {			spin_unlock_irqrestore(&card->lock, flags);			continue;		}		dmabuf->swptr = swptr;		dmabuf->count -= cnt;		spin_unlock_irqrestore(&card->lock, flags);		count -= cnt;		buffer += cnt;		ret += cnt;	}done:	ali_update_lvi(state, 1);	set_current_state(TASK_RUNNING);	remove_wait_queue(&dmabuf->wait, &waita);	return ret;}/* in this loop, dmabuf.count signifies the amount of data that is waiting to be dma to   the soundcard.  it is drained by the dma machine and filled by this loop. */static ssize_t ali_write(struct file *file,			 const char __user *buffer, size_t count, loff_t * ppos){	struct ali_state *state = (struct ali_state *) file->private_data;	struct ali_card *card = state ? state->card : NULL;	struct dmabuf *dmabuf = &state->dmabuf;	ssize_t ret;	unsigned long flags;	unsigned int swptr = 0;	int cnt, x;	DECLARE_WAITQUEUE(waita, current);#ifdef DEBUG2	printk("ali_audio: ali_write called, count = %d\n", count);#endif	if (dmabuf->mapped)		return -ENXIO;	if (dmabuf->enable & ADC_RUNNING)		return -ENODEV;	if (codec_independent_spdif_locked > 0) {		if (!dmabuf->codec_spdifout_channel) {			dmabuf->ready = 0;			dmabuf->codec_spdifout_channel = card->alloc_codec_spdifout_channel(card);			if (!dmabuf->codec_spdifout_channel)				return -EBUSY;		}

⌨️ 快捷键说明

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