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

📄 pcxhr_core.c

📁 linux2.6.16版本
💻 C
📖 第 1 页 / 共 3 页
字号:
static int pcxhr_toggle_pipes(struct pcxhr_mgr *mgr, int audio_mask){	struct pcxhr_rmh rmh;	int err;	int audio = 0;	while (audio_mask) {		if (audio_mask & 1) {			pcxhr_init_rmh(&rmh, CMD_CONF_PIPE);			if (audio < PCXHR_PIPE_STATE_CAPTURE_OFFSET)				pcxhr_set_pipe_cmd_params(&rmh, 0, 0, 0, 1 << audio);			else				pcxhr_set_pipe_cmd_params(&rmh, 1, 0, 0,							  1 << (audio - PCXHR_PIPE_STATE_CAPTURE_OFFSET));			err = pcxhr_send_msg(mgr, &rmh);			if (err) {				snd_printk(KERN_ERR					   "error pipe start (CMD_CONF_PIPE) err=%x!\n",					   err);				return err;			}		}		audio_mask>>=1;		audio++;	}	/* now fire the interrupt on the card */	pcxhr_init_rmh(&rmh, CMD_SEND_IRQA);	err = pcxhr_send_msg(mgr, &rmh);	if (err) {		snd_printk(KERN_ERR "error pipe start (CMD_SEND_IRQA) err=%x!\n", err );		return err;	}	return 0;}int pcxhr_set_pipe_state(struct pcxhr_mgr *mgr, int playback_mask, int capture_mask, int start){	int state, i, err;	int audio_mask;#ifdef CONFIG_SND_DEBUG_DETECT	struct timeval my_tv1, my_tv2;	do_gettimeofday(&my_tv1);#endif	audio_mask = (playback_mask | (capture_mask << PCXHR_PIPE_STATE_CAPTURE_OFFSET));	/* current pipe state (playback + record) */	state = pcxhr_pipes_running(mgr);	snd_printdd("pcxhr_set_pipe_state %s (mask %x current %x)\n",		    start ? "START" : "STOP", audio_mask, state);	if (start) {		audio_mask &= ~state;	/* start only pipes that are not yet started */		state = audio_mask;		for (i = 0; i < MAX_WAIT_FOR_DSP; i++) {			err = pcxhr_prepair_pipe_start(mgr, state, &state);			if (err)				return err;			if (state == 0)				break;	/* success, all pipes prepaired for start */			mdelay(1);		/* otherwise wait 1 millisecond and retry */		}	} else {		audio_mask &= state;	/* stop only pipes that are started */	}	if (audio_mask == 0)		return 0;	err = pcxhr_toggle_pipes(mgr, audio_mask);	if (err)		return err;	i = 0;	while (1) {		state = pcxhr_pipes_running(mgr);		/* have all pipes the new state ? */		if ((state & audio_mask) == (start ? audio_mask : 0))			break;		if (++i >= MAX_WAIT_FOR_DSP * 100) {			snd_printk(KERN_ERR "error pipe start/stop (ED_NO_RESPONSE_AT_IRQA)\n");			return -EBUSY;		}		udelay(10);			/* wait 10 microseconds */	}	if (!start) {		err = pcxhr_stop_pipes(mgr, audio_mask);		if (err)			return err;	}#ifdef CONFIG_SND_DEBUG_DETECT	do_gettimeofday(&my_tv2);	snd_printdd("***SET PIPE STATE*** TIME = %ld (err = %x)\n",		    my_tv2.tv_usec - my_tv1.tv_usec, err);#endif	return 0;}int pcxhr_write_io_num_reg_cont(struct pcxhr_mgr *mgr, unsigned int mask,				unsigned int value, int *changed){	struct pcxhr_rmh rmh;	unsigned long flags;	int err;	spin_lock_irqsave(&mgr->msg_lock, flags);	if ((mgr->io_num_reg_cont & mask) == value) {		snd_printdd("IO_NUM_REG_CONT mask %x already is set to %x\n", mask, value);		if (changed)			*changed = 0;		spin_unlock_irqrestore(&mgr->msg_lock, flags);		return 0;	/* already programmed */	}	pcxhr_init_rmh(&rmh, CMD_ACCESS_IO_WRITE);	rmh.cmd[0] |= IO_NUM_REG_CONT;	rmh.cmd[1]  = mask;	rmh.cmd[2]  = value;	rmh.cmd_len = 3;	err = pcxhr_send_msg_nolock(mgr, &rmh);	if (err == 0) {		mgr->io_num_reg_cont &= ~mask;		mgr->io_num_reg_cont |= value;		if (changed)			*changed = 1;	}	spin_unlock_irqrestore(&mgr->msg_lock, flags);	return err;}#define PCXHR_IRQ_TIMER		0x000300#define PCXHR_IRQ_FREQ_CHANGE	0x000800#define PCXHR_IRQ_TIME_CODE	0x001000#define PCXHR_IRQ_NOTIFY	0x002000#define PCXHR_IRQ_ASYNC		0x008000#define PCXHR_IRQ_MASK		0x00bb00#define PCXHR_FATAL_DSP_ERR	0xff0000enum pcxhr_async_err_src {	PCXHR_ERR_PIPE,	PCXHR_ERR_STREAM,	PCXHR_ERR_AUDIO};static int pcxhr_handle_async_err(struct pcxhr_mgr *mgr, u32 err,				  enum pcxhr_async_err_src err_src, int pipe,				  int is_capture){#ifdef CONFIG_SND_DEBUG_DETECT	static char* err_src_name[] = {		[PCXHR_ERR_PIPE]	= "Pipe",		[PCXHR_ERR_STREAM]	= "Stream",		[PCXHR_ERR_AUDIO]	= "Audio"	};#endif	if (err & 0xfff)		err &= 0xfff;	else		err = ((err >> 12) & 0xfff);	if (!err)		return 0;	snd_printdd("CMD_ASYNC : Error %s %s Pipe %d err=%x\n", err_src_name[err_src],		    is_capture ? "Record" : "Play", pipe, err);	if (err == 0xe01)		mgr->async_err_stream_xrun++;	else if (err == 0xe10)		mgr->async_err_pipe_xrun++;	else		mgr->async_err_other_last = (int)err;	return 1;}void pcxhr_msg_tasklet(unsigned long arg){	struct pcxhr_mgr *mgr = (struct pcxhr_mgr *)(arg);	struct pcxhr_rmh *prmh = mgr->prmh;	int err;	int i, j;	if (mgr->src_it_dsp & PCXHR_IRQ_FREQ_CHANGE)		snd_printdd("TASKLET : PCXHR_IRQ_FREQ_CHANGE event occured\n");	if (mgr->src_it_dsp & PCXHR_IRQ_TIME_CODE)		snd_printdd("TASKLET : PCXHR_IRQ_TIME_CODE event occured\n");	if (mgr->src_it_dsp & PCXHR_IRQ_NOTIFY)		snd_printdd("TASKLET : PCXHR_IRQ_NOTIFY event occured\n");	if (mgr->src_it_dsp & PCXHR_IRQ_ASYNC) {		snd_printdd("TASKLET : PCXHR_IRQ_ASYNC event occured\n");		pcxhr_init_rmh(prmh, CMD_ASYNC);		prmh->cmd[0] |= 1;	/* add SEL_ASYNC_EVENTS */		/* this is the only one extra long response command */		prmh->stat_len = PCXHR_SIZE_MAX_LONG_STATUS;		err = pcxhr_send_msg(mgr, prmh);		if (err)			snd_printk(KERN_ERR "ERROR pcxhr_msg_tasklet=%x;\n", err);		i = 1;		while (i < prmh->stat_len) {			int nb_audio = (prmh->stat[i] >> FIELD_SIZE) & MASK_FIRST_FIELD;			int nb_stream = (prmh->stat[i] >> (2*FIELD_SIZE)) & MASK_FIRST_FIELD;			int pipe = prmh->stat[i] & MASK_FIRST_FIELD;			int is_capture = prmh->stat[i] & 0x400000;			u32 err;			if (prmh->stat[i] & 0x800000) {	/* if BIT_END */				snd_printdd("TASKLET : End%sPipe %d\n",					    is_capture ? "Record" : "Play", pipe);			}			i++;			err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];			if (err)				pcxhr_handle_async_err(mgr, err, PCXHR_ERR_PIPE,						       pipe, is_capture);			i += 2;			for (j = 0; j < nb_stream; j++) {				err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];				if (err)					pcxhr_handle_async_err(mgr, err, PCXHR_ERR_STREAM,							       pipe, is_capture);				i += 2;			}			for (j = 0; j < nb_audio; j++) {				err = prmh->stat[i] ? prmh->stat[i] : prmh->stat[i+1];				if (err)					pcxhr_handle_async_err(mgr, err, PCXHR_ERR_AUDIO,							       pipe, is_capture);				i += 2;			}		}	}}static u_int64_t pcxhr_stream_read_position(struct pcxhr_mgr *mgr,					    struct pcxhr_stream *stream){	u_int64_t hw_sample_count;	struct pcxhr_rmh rmh;	int err, stream_mask;	stream_mask = stream->pipe->is_capture ? 1 : 1<<stream->substream->number;	/* get sample count for one stream */	pcxhr_init_rmh(&rmh, CMD_STREAM_SAMPLE_COUNT);	pcxhr_set_pipe_cmd_params(&rmh, stream->pipe->is_capture,				  stream->pipe->first_audio, 0, stream_mask);	/* rmh.stat_len = 2; */		/* 2 resp data for each stream of the pipe */	err = pcxhr_send_msg(mgr, &rmh);	if (err)		return 0;	hw_sample_count = ((u_int64_t)rmh.stat[0]) << 24;	hw_sample_count += (u_int64_t)rmh.stat[1];	snd_printdd("stream %c%d : abs samples real(%ld) timer(%ld)\n",		    stream->pipe->is_capture ? 'C':'P', stream->substream->number,		    (long unsigned int)hw_sample_count,		    (long unsigned int)(stream->timer_abs_periods +					stream->timer_period_frag + PCXHR_GRANULARITY));	return hw_sample_count;}static void pcxhr_update_timer_pos(struct pcxhr_mgr *mgr,				   struct pcxhr_stream *stream, int samples_to_add){	if (stream->substream && (stream->status == PCXHR_STREAM_STATUS_RUNNING)) {		u_int64_t new_sample_count;		int elapsed = 0;		int hardware_read = 0;		struct snd_pcm_runtime *runtime = stream->substream->runtime;		if (samples_to_add < 0) {			stream->timer_is_synced = 0;			/* add default if no hardware_read possible */			samples_to_add = PCXHR_GRANULARITY;		}		if (!stream->timer_is_synced) {			if (stream->timer_abs_periods != 0 ||			    stream->timer_period_frag + PCXHR_GRANULARITY >=			    runtime->period_size) {				new_sample_count = pcxhr_stream_read_position(mgr, stream);				hardware_read = 1;				if (new_sample_count >= PCXHR_GRANULARITY_MIN) {					/* sub security offset because of jitter and					 * finer granularity of dsp time (MBOX4)					 */					new_sample_count -= PCXHR_GRANULARITY_MIN;					stream->timer_is_synced = 1;				}			}		}		if (!hardware_read) {			/* if we didn't try to sync the position, increment it			 * by PCXHR_GRANULARITY every timer interrupt			 */			new_sample_count = stream->timer_abs_periods +				stream->timer_period_frag + samples_to_add;		}		while (1) {			u_int64_t new_elapse_pos = stream->timer_abs_periods +				runtime->period_size;			if (new_elapse_pos > new_sample_count)				break;			elapsed = 1;			stream->timer_buf_periods++;			if (stream->timer_buf_periods >= runtime->periods)				stream->timer_buf_periods = 0;			stream->timer_abs_periods = new_elapse_pos;		}		if (new_sample_count >= stream->timer_abs_periods)			stream->timer_period_frag = (u_int32_t)(new_sample_count -								stream->timer_abs_periods);		else			snd_printk(KERN_ERR "ERROR new_sample_count too small ??? %lx\n",				   (long unsigned int)new_sample_count);		if (elapsed) {			spin_unlock(&mgr->lock);			snd_pcm_period_elapsed(stream->substream);			spin_lock(&mgr->lock);		}	}}irqreturn_t pcxhr_interrupt(int irq, void *dev_id, struct pt_regs *regs){	struct pcxhr_mgr *mgr = dev_id;	unsigned int reg;	int i, j;	struct snd_pcxhr *chip;	spin_lock(&mgr->lock);	reg = PCXHR_INPL(mgr, PCXHR_PLX_IRQCS);	if (! (reg & PCXHR_IRQCS_ACTIVE_PCIDB)) {		spin_unlock(&mgr->lock);		return IRQ_NONE;	/* this device did not cause the interrupt */	}	/* clear interrupt */	reg = PCXHR_INPL(mgr, PCXHR_PLX_L2PCIDB);	PCXHR_OUTPL(mgr, PCXHR_PLX_L2PCIDB, reg);	/* timer irq occured */	if (reg & PCXHR_IRQ_TIMER) {		int timer_toggle = reg & PCXHR_IRQ_TIMER;		/* is a 24 bit counter */		int dsp_time_new = PCXHR_INPL(mgr, PCXHR_PLX_MBOX4) & PCXHR_DSP_TIME_MASK;		int dsp_time_diff = dsp_time_new - mgr->dsp_time_last;		if (dsp_time_diff < 0 && mgr->dsp_time_last != PCXHR_DSP_TIME_INVALID) {			snd_printdd("ERROR DSP TIME old(%d) new(%d) -> "				    "resynchronize all streams\n",				    mgr->dsp_time_last, dsp_time_new);			mgr->dsp_time_err++;		}#ifdef CONFIG_SND_DEBUG_DETECT		if (dsp_time_diff == 0)			snd_printdd("ERROR DSP TIME NO DIFF time(%d)\n", dsp_time_new);		else if (dsp_time_diff >= (2*PCXHR_GRANULARITY))			snd_printdd("ERROR DSP TIME TOO BIG old(%d) add(%d)\n",				    mgr->dsp_time_last, dsp_time_new - mgr->dsp_time_last);#endif		mgr->dsp_time_last = dsp_time_new;		if (timer_toggle == mgr->timer_toggle)			snd_printk(KERN_ERR "ERROR TIMER TOGGLE\n");		mgr->timer_toggle = timer_toggle;		reg &= ~PCXHR_IRQ_TIMER;		for (i = 0; i < mgr->num_cards; i++) {			chip = mgr->chip[i];			for (j = 0; j < chip->nb_streams_capt; j++)				pcxhr_update_timer_pos(mgr, &chip->capture_stream[j],						       dsp_time_diff);		}		for (i = 0; i < mgr->num_cards; i++) {			chip = mgr->chip[i];			for (j = 0; j < chip->nb_streams_play; j++)				pcxhr_update_timer_pos(mgr, &chip->playback_stream[j],						       dsp_time_diff);		}	}	/* other irq's handled in the tasklet */	if (reg & PCXHR_IRQ_MASK) {		/* as we didn't request any notifications, some kind of xrun error		 * will probably occured		 */		/* better resynchronize all streams next interrupt : */		mgr->dsp_time_last = PCXHR_DSP_TIME_INVALID;				mgr->src_it_dsp = reg;		tasklet_hi_schedule(&mgr->msg_taskq);	}#ifdef CONFIG_SND_DEBUG_DETECT	if (reg & PCXHR_FATAL_DSP_ERR)		snd_printdd("FATAL DSP ERROR : %x\n", reg);#endif	spin_unlock(&mgr->lock);	return IRQ_HANDLED;	/* this device caused the interrupt */}

⌨️ 快捷键说明

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