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

📄 cs4218_tdm.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
	case AFMT_S8:		ct_func = sound.trans_write->ct_s8;		break;	case AFMT_U8:		ct_func = sound.trans_write->ct_u8;		break;	case AFMT_S16_BE:		ct_func = sound.trans_write->ct_s16be;		break;	case AFMT_U16_BE:		ct_func = sound.trans_write->ct_u16be;		break;	case AFMT_S16_LE:		ct_func = sound.trans_write->ct_s16le;		break;	case AFMT_U16_LE:		ct_func = sound.trans_write->ct_u16le;		break;	}	if (ct_func)		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);	else		return 0;}static ssize_t sound_copy_translate_read(const u_char *userPtr,				    size_t userCount,				    u_char frame[], ssize_t *frameUsed,				    ssize_t frameLeft){	ssize_t (*ct_func)(const u_char *, size_t, u_char *, ssize_t *, ssize_t) = NULL;	switch (sound.soft.format) {	case AFMT_MU_LAW:		ct_func = sound.trans_read->ct_ulaw;		break;	case AFMT_A_LAW:		ct_func = sound.trans_read->ct_alaw;		break;	case AFMT_S8:		ct_func = sound.trans_read->ct_s8;		break;	case AFMT_U8:		ct_func = sound.trans_read->ct_u8;		break;	case AFMT_S16_BE:		ct_func = sound.trans_read->ct_s16be;		break;	case AFMT_U16_BE:		ct_func = sound.trans_read->ct_u16be;		break;	case AFMT_S16_LE:		ct_func = sound.trans_read->ct_s16le;		break;	case AFMT_U16_LE:		ct_func = sound.trans_read->ct_u16le;		break;	}	if (ct_func)		return ct_func(userPtr, userCount, frame, frameUsed, frameLeft);	else		return 0;}/* * /dev/mixer abstraction */static int mixer_open(struct inode *inode, struct file *file){	mixer.busy = 1;	return nonseekable_open(inode, file);}static int mixer_release(struct inode *inode, struct file *file){	mixer.busy = 0;	return 0;}static int mixer_ioctl(struct inode *inode, struct file *file, u_int cmd,		       u_long arg){	int data;	uint tmpcs;	if (_SIOC_DIR(cmd) & _SIOC_WRITE)	    mixer.modify_counter++;	if (cmd == OSS_GETVERSION)	    return IOCTL_OUT(arg, SOUND_VERSION);	switch (cmd) {		case SOUND_MIXER_INFO: {		    mixer_info info;		    strlcpy(info.id, "CS4218_TDM", sizeof(info.id));		    strlcpy(info.name, "CS4218_TDM", sizeof(info.name));		    info.name[sizeof(info.name)-1] = 0;		    info.modify_counter = mixer.modify_counter;		    if (copy_to_user((int *)arg, &info, sizeof(info)))		    		return -EFAULT;		    return 0;		}		case SOUND_MIXER_READ_DEVMASK:			data = SOUND_MASK_VOLUME | SOUND_MASK_LINE				| SOUND_MASK_MIC | SOUND_MASK_RECLEV				| SOUND_MASK_ALTPCM;			return IOCTL_OUT(arg, data);		case SOUND_MIXER_READ_RECMASK:			data = SOUND_MASK_LINE | SOUND_MASK_MIC;			return IOCTL_OUT(arg, data);		case SOUND_MIXER_READ_RECSRC:			if (cs4218_control & CS_DO1)				data = SOUND_MASK_LINE;			else				data = SOUND_MASK_MIC;			return IOCTL_OUT(arg, data);		case SOUND_MIXER_WRITE_RECSRC:			IOCTL_IN(arg, data);			data &= (SOUND_MASK_LINE | SOUND_MASK_MIC);			if (data & SOUND_MASK_LINE)				tmpcs = cs4218_control |						(CS_ISL | CS_ISR | CS_DO1);			if (data & SOUND_MASK_MIC)				tmpcs = cs4218_control &						~(CS_ISL | CS_ISR | CS_DO1);			if (tmpcs != cs4218_control)				cs4218_ctl_write(tmpcs);			return IOCTL_OUT(arg, data);		case SOUND_MIXER_READ_STEREODEVS:			data = SOUND_MASK_VOLUME | SOUND_MASK_RECLEV;			return IOCTL_OUT(arg, data);		case SOUND_MIXER_READ_CAPS:			return IOCTL_OUT(arg, 0);		case SOUND_MIXER_READ_VOLUME:			data = (cs4218_control & CS_MUTE)? 0:				cs_get_volume(cs4218_control);			return IOCTL_OUT(arg, data);		case SOUND_MIXER_WRITE_VOLUME:			IOCTL_IN(arg, data);			return IOCTL_OUT(arg, sound_set_volume(data));		case SOUND_MIXER_WRITE_ALTPCM:	/* really bell volume */			IOCTL_IN(arg, data);			beep_volume = data & 0xff;			/* fall through */		case SOUND_MIXER_READ_ALTPCM:			return IOCTL_OUT(arg, beep_volume);		case SOUND_MIXER_WRITE_RECLEV:			IOCTL_IN(arg, data);			data = cs_set_gain(data);			return IOCTL_OUT(arg, data);		case SOUND_MIXER_READ_RECLEV:			data = cs_get_gain(cs4218_control);			return IOCTL_OUT(arg, data);	}	return -EINVAL;}static struct file_operations mixer_fops ={	.owner =	THIS_MODULE,	.llseek =	sound_lseek,	.ioctl =	mixer_ioctl,	.open =		mixer_open,	.release =	mixer_release,};static void __init mixer_init(void){	mixer_unit = register_sound_mixer(&mixer_fops, -1);	if (mixer_unit < 0)		return;	mixer.busy = 0;	sound.treble = 0;	sound.bass = 0;	/* Set Line input, no gain, no attenuation.	*/	cs4218_control = CS_ISL | CS_ISR | CS_DO1;	cs4218_control |= CS_LGAIN_SET(0) | CS_RGAIN_SET(0);	cs4218_control |= CS_LATTEN_SET(0) | CS_RATTEN_SET(0);	cs4218_ctl_write(cs4218_control);}/* * Sound queue stuff, the heart of the driver */static int sq_allocate_buffers(void){	int i;	if (sound_buffers)		return 0;	sound_buffers = kmalloc (numBufs * sizeof(char *), GFP_KERNEL);	if (!sound_buffers)		return -ENOMEM;	for (i = 0; i < numBufs; i++) {		sound_buffers[i] = sound.mach.dma_alloc (bufSize << 10, GFP_KERNEL);		if (!sound_buffers[i]) {			while (i--)				sound.mach.dma_free (sound_buffers[i], bufSize << 10);			kfree (sound_buffers);			sound_buffers = 0;			return -ENOMEM;		}	}	return 0;}static void sq_release_buffers(void){	int i;	if (sound_buffers) {		for (i = 0; i < numBufs; i++)			sound.mach.dma_free (sound_buffers[i], bufSize << 10);		kfree (sound_buffers);		sound_buffers = 0;	}}static int sq_allocate_read_buffers(void){	int i;	if (sound_read_buffers)		return 0;	sound_read_buffers = kmalloc(numReadBufs * sizeof(char *), GFP_KERNEL);	if (!sound_read_buffers)		return -ENOMEM;	for (i = 0; i < numBufs; i++) {		sound_read_buffers[i] = sound.mach.dma_alloc (readbufSize<<10,							      GFP_KERNEL);		if (!sound_read_buffers[i]) {			while (i--)				sound.mach.dma_free (sound_read_buffers[i],						     readbufSize << 10);			kfree (sound_read_buffers);			sound_read_buffers = 0;			return -ENOMEM;		}	}	return 0;}static void sq_release_read_buffers(void){	int i;	if (sound_read_buffers) {		cpmp->cp_smc[1].smc_smcmr &= ~SMCMR_REN;		for (i = 0; i < numReadBufs; i++)			sound.mach.dma_free (sound_read_buffers[i],					     bufSize << 10);		kfree (sound_read_buffers);		sound_read_buffers = 0;	}}static void sq_setup(int numBufs, int bufSize, char **write_buffers){	int i;	volatile cbd_t *bdp;	volatile cpm8xx_t	*cp;	volatile smc_t	*sp;	/* Make sure the SMC transmit is shut down.	*/	cp = cpmp;	sp = &cpmp->cp_smc[1];	sp->smc_smcmr &= ~SMCMR_TEN;	sq.max_count = numBufs;	sq.max_active = numBufs;	sq.block_size = bufSize;	sq.buffers = write_buffers;	sq.front = sq.count = 0;	sq.rear = -1;	sq.syncing = 0;	sq.active = 0;	bdp = tx_base;	for (i=0; i<numBufs; i++) {		bdp->cbd_bufaddr = virt_to_bus(write_buffers[i]);		bdp++;	}	/* This causes the SMC to sync up with the first buffer again.	*/	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_TX) | CPM_CR_FLG;	while (cp->cp_cpcr & CPM_CR_FLG);}static void read_sq_setup(int numBufs, int bufSize, char **read_buffers){	int i;	volatile cbd_t *bdp;	volatile cpm8xx_t	*cp;	volatile smc_t	*sp;	/* Make sure the SMC receive is shut down.	*/	cp = cpmp;	sp = &cpmp->cp_smc[1];	sp->smc_smcmr &= ~SMCMR_REN;	read_sq.max_count = numBufs;	read_sq.max_active = numBufs;	read_sq.block_size = bufSize;	read_sq.buffers = read_buffers;	read_sq.front = read_sq.count = 0;	read_sq.rear = 0;	read_sq.rear_size = 0;	read_sq.syncing = 0;	read_sq.active = 0;	bdp = rx_base;	for (i=0; i<numReadBufs; i++) {		bdp->cbd_bufaddr = virt_to_bus(read_buffers[i]);		bdp->cbd_datlen = read_sq.block_size;		bdp++;	}	/* This causes the SMC to sync up with the first buffer again.	*/	cp->cp_cpcr = mk_cr_cmd(CPM_CR_CH_SMC2, CPM_CR_INIT_RX) | CPM_CR_FLG;	while (cp->cp_cpcr & CPM_CR_FLG);}static void sq_play(void){	(*sound.mach.play)();}/* ++TeSche: radically changed this one too */static ssize_t sq_write(struct file *file, const char *src, size_t uLeft,			loff_t *ppos){	ssize_t uWritten = 0;	u_char *dest;	ssize_t uUsed, bUsed, bLeft;	/* ++TeSche: Is something like this necessary?	 * Hey, that's an honest question! Or does any other part of the	 * filesystem already checks this situation? I really don't know.	 */	if (uLeft == 0)		return 0;	/* The interrupt doesn't start to play the last, incomplete frame.	 * Thus we can append to it without disabling the interrupts! (Note	 * also that sq.rear isn't affected by the interrupt.)	 */	if (sq.count > 0 && (bLeft = sq.block_size-sq.rear_size) > 0) {		dest = sq_block_address(sq.rear);		bUsed = sq.rear_size;		uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);		if (uUsed <= 0)			return uUsed;		src += uUsed;		uWritten += uUsed;		uLeft -= uUsed;		sq.rear_size = bUsed;	}	do {		while (sq.count == sq.max_active) {			sq_play();			if (NON_BLOCKING(sq.open_mode))				return uWritten > 0 ? uWritten : -EAGAIN;			SLEEP(sq.action_queue);			if (SIGNAL_RECEIVED)				return uWritten > 0 ? uWritten : -EINTR;		}		/* Here, we can avoid disabling the interrupt by first		 * copying and translating the data, and then updating		 * the sq variables. Until this is done, the interrupt		 * won't see the new frame and we can work on it		 * undisturbed.		 */		dest = sq_block_address((sq.rear+1) % sq.max_count);		bUsed = 0;		bLeft = sq.block_size;		uUsed = sound_copy_translate(src, uLeft, dest, &bUsed, bLeft);		if (uUsed <= 0)			break;		src += uUsed;		uWritten += uUsed;		uLeft -= uUsed;		if (bUsed) {			sq.rear = (sq.rear+1) % sq.max_count;			sq.rear_size = bUsed;			sq.count++;		}	} while (bUsed);   /* uUsed may have been 0 */	sq_play();	return uUsed < 0? uUsed: uWritten;}/***********//* Here is how the values are used for reading. * The value 'active' simply indicates the DMA is running.  This is * done so the driver semantics are DMA starts when the first read is * posted.  The value 'front' indicates the buffer we should next * send to the user.  The value 'rear' indicates the buffer the DMA is * currently filling.  When 'front' == 'rear' the buffer "ring" is * empty (we always have an empty available).  The 'rear_size' is used * to track partial offsets into the current buffer.  Right now, I just keep * The DMA running.  If the reader can't keep up, the interrupt tosses * the oldest buffer.  We could also shut down the DMA in this case. */static ssize_t sq_read(struct file *file, char *dst, size_t uLeft,                       loff_t *ppos){	ssize_t	uRead, bLeft, bUsed, uUsed;	if (uLeft == 0)		return 0;	if (!read_sq.active)		CS_Record();	/* Kick off the record process. */	uRead = 0;	/* Move what the user requests, depending upon other options.	*/	while (uLeft > 0) {		/* When front == rear, the DMA is not done yet.		*/		while (read_sq.front == read_sq.rear) {			if (NON_BLOCKING(read_sq.open_mode)) {			       return uRead > 0 ? uRead : -EAGAIN;			}			SLEEP(read_sq.action_queue);			if (SIGNAL_RECEIVED)				return uRead > 0 ? uRead : -EINTR;		}		/* The amount we move is either what is left in the		 * current buffer or what the user wants.		 */		bLeft = read_sq.block_size - read_sq.rear_size;		bUsed = read_sq.rear_size;		uUsed = sound_copy_translate_read(dst, uLeft,			read_sq.buffers[read_sq.front], &bUsed, bLeft);		if (uUsed <= 0)			return uUsed;		dst += uUsed;		uRead += uUsed;		uLeft -= uUsed;		read_sq.rear_size += bUsed;		if (read_sq.rear_size >= read_sq.block_size) {			read_sq.rear_size = 0;			read_sq.front++;			if (read_sq.front >= read_sq.max_active)				read_sq.front = 0;		}	}	return uRead;}static int sq_open(struct inode *inode, struct file *file){	int rc = 0;	if (file->f_mode & FMODE_WRITE) {		if (sq.busy) {			rc = -EBUSY;			if (NON_BLOCKING(file->f_flags))				goto err_out;			rc = -EINTR;			while (sq.busy) {				SLEEP(sq.open_queue);				if (SIGNAL_RECEIVED)					goto err_out;			}		}		sq.busy = 1; /* Let's play spot-the-race-condition */		if (sq_allocate_buffers()) goto err_out_nobusy;		sq_setup(numBufs, bufSize<<10,sound_buffers);		sq.open_mode = file->f_mode;	}	if (file->f_mode & FMODE_READ) {		if (read_sq.busy) {			rc = -EBUSY;			if (NON_BLOCKING(file->f_flags))				goto err_out;			rc = -EINTR;			while (read_sq.busy) {

⌨️ 快捷键说明

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