Enscript Output

extractedLnx/linux-2.6.9/sound/oss/cs46xx.c_cs_ioctl.c

static int cs_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct cs_card *card = (struct cs_card *)file->private_data;
	struct cs_state *state;
	struct dmabuf *dmabuf=NULL;
	unsigned long flags;
	audio_buf_info abinfo;
	count_info cinfo;
	int val, valsave, mapped, ret;
	void __user *argp = (void __user *)arg;
	int __user *p = argp;

	state = (struct cs_state *)card->states[0];
	if(state)
	{
		dmabuf = &state->dmabuf;
		mapped = (file->f_mode & FMODE_READ) && dmabuf->mapped;
	}
	state = (struct cs_state *)card->states[1];
	if(state)
	{
		dmabuf = &state->dmabuf;
		mapped |= (file->f_mode & FMODE_WRITE) && dmabuf->mapped;
	}
		
#if CSDEBUG
	printioctl(cmd);
#endif

	switch (cmd) 
	{
	case OSS_GETVERSION:
		return put_user(SOUND_VERSION, p);

	case SNDCTL_DSP_RESET:
		/* FIXME: spin_lock ? */
		if (file->f_mode & FMODE_WRITE) {
			state = (struct cs_state *)card->states[1];
			if(state)
			{
				dmabuf = &state->dmabuf;
				stop_dac(state);
				synchronize_irq(card->irq);
				dmabuf->ready = 0;
				resync_dma_ptrs(state);
				dmabuf->swptr = dmabuf->hwptr = 0;
				dmabuf->count = dmabuf->total_bytes = 0;
				dmabuf->blocks = 0;
				dmabuf->SGok = 0;
			}
		}
		if (file->f_mode & FMODE_READ) {
			state = (struct cs_state *)card->states[0];
			if(state)
			{
				dmabuf = &state->dmabuf;
				stop_adc(state);
				synchronize_irq(card->irq);
				resync_dma_ptrs(state);
				dmabuf->ready = 0;
				dmabuf->swptr = dmabuf->hwptr = 0;
				dmabuf->count = dmabuf->total_bytes = 0;
				dmabuf->blocks = 0;
				dmabuf->SGok = 0;
			}
		}
		CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_RESET()-\n") );
		return 0;

	case SNDCTL_DSP_SYNC:
		if (file->f_mode & FMODE_WRITE)
			return drain_dac(state, file->f_flags & O_NONBLOCK);
		return 0;

	case SNDCTL_DSP_SPEED: /* set sample rate */
		if (get_user(val, p))
			return -EFAULT;
		if (val >= 0) {
			if (file->f_mode & FMODE_READ) {
				state = (struct cs_state *)card->states[0];
				if(state)
				{
					dmabuf = &state->dmabuf;
					stop_adc(state);
					dmabuf->ready = 0;
					dmabuf->SGok = 0;
					cs_set_adc_rate(state, val);
					cs_set_divisor(dmabuf);
				}
			}
			if (file->f_mode & FMODE_WRITE) {
				state = (struct cs_state *)card->states[1];
				if(state)
				{
					dmabuf = &state->dmabuf;
					stop_dac(state);
					dmabuf->ready = 0;
					dmabuf->SGok = 0;
					cs_set_dac_rate(state, val);
					cs_set_divisor(dmabuf);
				}
			}
			CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
			    "cs46xx: cs_ioctl() DSP_SPEED %s %s %d\n",
				file->f_mode & FMODE_WRITE ? "DAC" : "",
				file->f_mode & FMODE_READ ? "ADC" : "",
				dmabuf->rate ) );
			return put_user(dmabuf->rate, p);
		}
		return put_user(0, p);

	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
		if (get_user(val, p))
			return -EFAULT;
		if (file->f_mode & FMODE_WRITE) {
			state = (struct cs_state *)card->states[1];
			if(state)
			{
				dmabuf = &state->dmabuf;
				stop_dac(state);
				dmabuf->ready = 0;
				dmabuf->SGok = 0;
				if(val)
					dmabuf->fmt |= CS_FMT_STEREO;
				else
					dmabuf->fmt &= ~CS_FMT_STEREO;
				cs_set_divisor(dmabuf);
				CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
				    "cs46xx: DSP_STEREO() DAC %s\n",
				    (dmabuf->fmt & CS_FMT_STEREO) ?
					"STEREO":"MONO") );
			}
		}
		if (file->f_mode & FMODE_READ) {
			state = (struct cs_state *)card->states[0];
			if(state)
			{
				dmabuf = &state->dmabuf;
				stop_adc(state);
				dmabuf->ready = 0;
				dmabuf->SGok = 0;
				if(val)
					dmabuf->fmt |= CS_FMT_STEREO;
				else
					dmabuf->fmt &= ~CS_FMT_STEREO;
				cs_set_divisor(dmabuf);
				CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
				    "cs46xx: DSP_STEREO() ADC %s\n",
				    (dmabuf->fmt & CS_FMT_STEREO) ?
					"STEREO":"MONO") );
			}
		}
		return 0;

	case SNDCTL_DSP_GETBLKSIZE:
		if (file->f_mode & FMODE_WRITE) {
			state = (struct cs_state *)card->states[1];
			if(state)
			{
				dmabuf = &state->dmabuf;
				if ((val = prog_dmabuf(state)))
					return val;
				return put_user(dmabuf->fragsize, p);
			}
		}
		if (file->f_mode & FMODE_READ) {
			state = (struct cs_state *)card->states[0];
			if(state)
			{
				dmabuf = &state->dmabuf;
				if ((val = prog_dmabuf(state)))
					return val;
				return put_user(dmabuf->fragsize/dmabuf->divisor, 
						p);
			}
		}
		return put_user(0, p);

	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
		return put_user(AFMT_S16_LE | AFMT_U8, p);

	case SNDCTL_DSP_SETFMT: /* Select sample format */
		if (get_user(val, p))
			return -EFAULT;
		CS_DBGOUT(CS_IOCTL | CS_PARMS, 4, printk(
		    "cs46xx: cs_ioctl() DSP_SETFMT %s %s %s %s\n",
			file->f_mode & FMODE_WRITE ? "DAC" : "",
			file->f_mode & FMODE_READ ? "ADC" : "",
			val == AFMT_S16_LE ? "16Bit Signed" : "",
			val == AFMT_U8 ? "8Bit Unsigned" : "") );
		valsave = val;
		if (val != AFMT_QUERY) {
			if(val==AFMT_S16_LE || val==AFMT_U8)
			{
				if (file->f_mode & FMODE_WRITE) {
					state = (struct cs_state *)card->states[1];
					if(state)
					{
						dmabuf = &state->dmabuf;
						stop_dac(state);
						dmabuf->ready = 0;
						dmabuf->SGok = 0;
						if(val==AFMT_S16_LE)
							dmabuf->fmt |= CS_FMT_16BIT;
						else
							dmabuf->fmt &= ~CS_FMT_16BIT;
						cs_set_divisor(dmabuf);
						if((ret = prog_dmabuf(state)))
							return ret;
					}
				}
				if (file->f_mode & FMODE_READ) {
					val = valsave;
					state = (struct cs_state *)card->states[0];
					if(state)
					{
						dmabuf = &state->dmabuf;
						stop_adc(state);
						dmabuf->ready = 0;
						dmabuf->SGok = 0;
						if(val==AFMT_S16_LE)
							dmabuf->fmt |= CS_FMT_16BIT;
						else
							dmabuf->fmt &= ~CS_FMT_16BIT;
						cs_set_divisor(dmabuf);
						if((ret = prog_dmabuf(state)))
							return ret;
					}
				}
			}
			else
			{
				CS_DBGOUT(CS_IOCTL | CS_ERROR, 2, printk(
				    "cs46xx: DSP_SETFMT() Unsupported format (0x%x)\n",
					valsave) );
			}
		}
		else
		{
			if(file->f_mode & FMODE_WRITE)
			{
				state = (struct cs_state *)card->states[1];
				if(state)
					dmabuf = &state->dmabuf;
			}
			else if(file->f_mode & FMODE_READ)
			{
				state = (struct cs_state *)card->states[0];
				if(state)
					dmabuf = &state->dmabuf;
			}
		}
		if(dmabuf)
		{
			if(dmabuf->fmt & CS_FMT_16BIT)
				return put_user(AFMT_S16_LE, p);
			else
				return put_user(AFMT_U8, p);
		}
		return put_user(0, p);

	case SNDCTL_DSP_CHANNELS:
		if (get_user(val, p))
			return -EFAULT;
		if (val != 0) {
			if (file->f_mode & FMODE_WRITE) {
				state = (struct cs_state *)card->states[1];
				if(state)
				{
					dmabuf = &state->dmabuf;
					stop_dac(state);
					dmabuf->ready = 0;
					dmabuf->SGok = 0;
					if(val>1)
						dmabuf->fmt |= CS_FMT_STEREO;
					else
						dmabuf->fmt &= ~CS_FMT_STEREO;
					cs_set_divisor(dmabuf);
					if (prog_dmabuf(state))
						return 0;
				}
			}
			if (file->f_mode & FMODE_READ) {
				state = (struct cs_state *)card->states[0];
				if(state)
				{
					dmabuf = &state->dmabuf;
					stop_adc(state);
					dmabuf->ready = 0;
					dmabuf->SGok = 0;
					if(val>1)
						dmabuf->fmt |= CS_FMT_STEREO;
					else
						dmabuf->fmt &= ~CS_FMT_STEREO;
					cs_set_divisor(dmabuf);
					if (prog_dmabuf(state))
						return 0;
				}
			}
		}
		return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1,
				p);

	case SNDCTL_DSP_POST:
		/*
		 * There will be a longer than normal pause in the data.
		 * so... do nothing, because there is nothing that we can do.
		 */
		return 0;

	case SNDCTL_DSP_SUBDIVIDE:
		if (file->f_mode & FMODE_WRITE) {
			state = (struct cs_state *)card->states[1];
			if(state)
			{
				dmabuf = &state->dmabuf;
				if (dmabuf->subdivision)
					return -EINVAL;
				if (get_user(val, p))
					return -EFAULT;
				if (val != 1 && val != 2)
					return -EINVAL;
				dmabuf->subdivision = val;
			}
		}
		if (file->f_mode & FMODE_READ) {
			state = (struct cs_state *)card->states[0];
			if(state)
			{
				dmabuf = &state->dmabuf;
				if (dmabuf->subdivision)
					return -EINVAL;
				if (get_user(val, p))
					return -EFAULT;
				if (val != 1 && val != 2)
					return -EINVAL;
				dmabuf->subdivision = val;
			}
		}
		return 0;

	case SNDCTL_DSP_SETFRAGMENT:
		if (get_user(val, p))
			return -EFAULT;

		if (file->f_mode & FMODE_WRITE) {
			state = (struct cs_state *)card->states[1];
			if(state)
			{
				dmabuf = &state->dmabuf;
				dmabuf->ossfragshift = val & 0xffff;
				dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
			}
		}
		if (file->f_mode & FMODE_READ) {
			state = (struct cs_state *)card->states[0];
			if(state)
			{
				dmabuf = &state->dmabuf;
				dmabuf->ossfragshift = val & 0xffff;
				dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
			}
		}
		return 0;

	case SNDCTL_DSP_GETOSPACE:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		state = (struct cs_state *)card->states[1];
		if(state)
		{
			dmabuf = &state->dmabuf;
			spin_lock_irqsave(&state->card->lock, flags);
			cs_update_ptr(card, CS_TRUE);
			abinfo.fragsize = dmabuf->fragsize;
			abinfo.fragstotal = dmabuf->numfrag;
		/*
		 * for mmap we always have total space available
		 */
			if (dmabuf->mapped)
				abinfo.bytes = dmabuf->dmasize;
			else
				abinfo.bytes = dmabuf->dmasize - dmabuf->count;

			abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
			spin_unlock_irqrestore(&state->card->lock, flags);
			return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
		}
		return -ENODEV;

	case SNDCTL_DSP_GETISPACE:
		if (!(file->f_mode & FMODE_READ))
			return -EINVAL;
		state = (struct cs_state *)card->states[0];
		if(state)
		{
			dmabuf = &state->dmabuf;
			spin_lock_irqsave(&state->card->lock, flags);
			cs_update_ptr(card, CS_TRUE);
			abinfo.fragsize = dmabuf->fragsize/dmabuf->divisor;
			abinfo.bytes = dmabuf->count/dmabuf->divisor;
			abinfo.fragstotal = dmabuf->numfrag;
			abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
			spin_unlock_irqrestore(&state->card->lock, flags);
			return copy_to_user(argp, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
		}
		return -ENODEV;

	case SNDCTL_DSP_NONBLOCK:
		file->f_flags |= O_NONBLOCK;
		return 0;

	case SNDCTL_DSP_GETCAPS:
		return put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP,
			    p);

	case SNDCTL_DSP_GETTRIGGER:
		val = 0;
		CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()+\n") );
		if (file->f_mode & FMODE_WRITE)
		{
			state = (struct cs_state *)card->states[1];
			if(state)
			{
				dmabuf = &state->dmabuf;
				if(dmabuf->enable & DAC_RUNNING)
					val |= PCM_ENABLE_INPUT;
			}
		}
		if (file->f_mode & FMODE_READ)
		{
			if(state)
			{
				state = (struct cs_state *)card->states[0];
				dmabuf = &state->dmabuf;
				if(dmabuf->enable & ADC_RUNNING)
					val |= PCM_ENABLE_OUTPUT;
			}
		}
		CS_DBGOUT(CS_IOCTL, 2, printk("cs46xx: DSP_GETTRIGGER()- val=0x%x\n",val) );
		return put_user(val, p);

	case SNDCTL_DSP_SETTRIGGER:
		if (get_user(val, p))
			return -EFAULT;
		if (file->f_mode & FMODE_READ) {
			state = (struct cs_state *)card->states[0];
			if(state)
			{
				dmabuf = &state->dmabuf;
				if (val & PCM_ENABLE_INPUT) {
					if (!dmabuf->ready && (ret = prog_dmabuf(state)))
						return ret;
					start_adc(state);
				} else
					stop_adc(state);
			}
		}
		if (file->f_mode & FMODE_WRITE) {
			state = (struct cs_state *)card->states[1];
			if(state)
			{
				dmabuf = &state->dmabuf;
				if (val & PCM_ENABLE_OUTPUT) {
					if (!dmabuf->ready && (ret = prog_dmabuf(state)))
						return ret;
					start_dac(state);
				} else
					stop_dac(state);
			}
		}
		return 0;

	case SNDCTL_DSP_GETIPTR:
		if (!(file->f_mode & FMODE_READ))
			return -EINVAL;
		state = (struct cs_state *)card->states[0];
		if(state)
		{
			dmabuf = &state->dmabuf;
			spin_lock_irqsave(&state->card->lock, flags);
			cs_update_ptr(card, CS_TRUE);
			cinfo.bytes = dmabuf->total_bytes/dmabuf->divisor;
			cinfo.blocks = dmabuf->count/dmabuf->divisor >> dmabuf->fragshift;
			cinfo.ptr = dmabuf->hwptr/dmabuf->divisor;
			spin_unlock_irqrestore(&state->card->lock, flags);
			if (copy_to_user(argp, &cinfo, sizeof(cinfo)))
				return -EFAULT;
			return 0;
		}
		return -ENODEV;

	case SNDCTL_DSP_GETOPTR:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		state = (struct cs_state *)card->states[1];
		if(state)
		{
			dmabuf = &state->dmabuf;
			spin_lock_irqsave(&state->card->lock, flags);
			cs_update_ptr(card, CS_TRUE);
			cinfo.bytes = dmabuf->total_bytes;
			if (dmabuf->mapped)
			{
				cinfo.blocks = (cinfo.bytes >> dmabuf->fragshift) 
							- dmabuf->blocks;
				CS_DBGOUT(CS_PARMS, 8, 
					printk("total_bytes=%d blocks=%d dmabuf->blocks=%d\n", 
					cinfo.bytes,cinfo.blocks,dmabuf->blocks) );
				dmabuf->blocks = cinfo.bytes >> dmabuf->fragshift;
			}
			else
			{
				cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
			}
			cinfo.ptr = dmabuf->hwptr;

			CS_DBGOUT(CS_PARMS, 4, printk(
			    "cs46xx: GETOPTR bytes=%d blocks=%d ptr=%d\n",
				cinfo.bytes,cinfo.blocks,cinfo.ptr) );
			spin_unlock_irqrestore(&state->card->lock, flags);
			if (copy_to_user(argp, &cinfo, sizeof(cinfo)))
				return -EFAULT;
			return 0;
		}
		return -ENODEV;

	case SNDCTL_DSP_SETDUPLEX:
		return 0;

	case SNDCTL_DSP_GETODELAY:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		state = (struct cs_state *)card->states[1];
		if(state)
		{
			dmabuf = &state->dmabuf;
			spin_lock_irqsave(&state->card->lock, flags);
			cs_update_ptr(card, CS_TRUE);
			val = dmabuf->count;
			spin_unlock_irqrestore(&state->card->lock, flags);
		}
		else
			val = 0;
		return put_user(val, p);

	case SOUND_PCM_READ_RATE:
		if(file->f_mode & FMODE_READ)
			state = (struct cs_state *)card->states[0];
		else 
			state = (struct cs_state *)card->states[1];
		if(state)
		{
			dmabuf = &state->dmabuf;
			return put_user(dmabuf->rate, p);
		}
		return put_user(0, p);
		

	case SOUND_PCM_READ_CHANNELS:
		if(file->f_mode & FMODE_READ)
			state = (struct cs_state *)card->states[0];
		else 
			state = (struct cs_state *)card->states[1];
		if(state)
		{
			dmabuf = &state->dmabuf;
			return put_user((dmabuf->fmt & CS_FMT_STEREO) ? 2 : 1,
				p);
		}
		return put_user(0, p);

	case SOUND_PCM_READ_BITS:
		if(file->f_mode & FMODE_READ)
			state = (struct cs_state *)card->states[0];
		else 
			state = (struct cs_state *)card->states[1];
		if(state)
		{
			dmabuf = &state->dmabuf;
			return put_user((dmabuf->fmt & CS_FMT_16BIT) ? 
			  	AFMT_S16_LE : AFMT_U8, p);

		}
		return put_user(0, p);

	case SNDCTL_DSP_MAPINBUF:
	case SNDCTL_DSP_MAPOUTBUF:
	case SNDCTL_DSP_SETSYNCRO:
	case SOUND_PCM_WRITE_FILTER:
	case SOUND_PCM_READ_FILTER:
		return -EINVAL;
	}
	return -EINVAL;
}

Generated by GNU enscript 1.6.4.