Enscript Output

extractedLnx/linux-2.4.37/drivers/sound/trident.c_trident_ioctl.c

static int trident_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct trident_state *state = (struct trident_state *)file->private_data;
	struct dmabuf *dmabuf = &state->dmabuf;
	unsigned long flags;
	audio_buf_info abinfo;
	count_info cinfo;
	int val, mapped, ret = 0;

	struct trident_card *card = state->card;

	VALIDATE_STATE(state);
	mapped = ((file->f_mode & FMODE_WRITE) && dmabuf->mapped) ||
		((file->f_mode & FMODE_READ) && dmabuf->mapped);
	TRDBG("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
	      _IOC_NR(cmd), arg ? *(int *)arg : 0);

	switch (cmd) 
	{
	case OSS_GETVERSION:
		ret = put_user(SOUND_VERSION, (int *)arg);
		break;
		
	case SNDCTL_DSP_RESET:
		/* FIXME: spin_lock ? */
		if (file->f_mode & FMODE_WRITE) {
			stop_dac(state);
			synchronize_irq();
			dmabuf->ready = 0;
			dmabuf->swptr = dmabuf->hwptr = 0;
			dmabuf->count = dmabuf->total_bytes = 0;
		}
		if (file->f_mode & FMODE_READ) {
			stop_adc(state);
			synchronize_irq();
			dmabuf->ready = 0;
			dmabuf->swptr = dmabuf->hwptr = 0;
			dmabuf->count = dmabuf->total_bytes = 0;
		}
		break;

	case SNDCTL_DSP_SYNC:
		if (file->f_mode & FMODE_WRITE)
			ret = drain_dac(state, file->f_flags & O_NONBLOCK);
		break;

	case SNDCTL_DSP_SPEED: /* set smaple rate */
		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}
		if (val >= 0) {
			if (file->f_mode & FMODE_WRITE) {
				stop_dac(state);
				dmabuf->ready = 0;
				spin_lock_irqsave(&state->card->lock, flags);
				trident_set_dac_rate(state, val);
				spin_unlock_irqrestore(&state->card->lock, flags);
			}
			if (file->f_mode & FMODE_READ) {
				stop_adc(state);
				dmabuf->ready = 0;
				spin_lock_irqsave(&state->card->lock, flags);
				trident_set_adc_rate(state, val);
				spin_unlock_irqrestore(&state->card->lock, flags);
			}
		}
		ret = put_user(dmabuf->rate, (int *)arg);
		break;

	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}
		lock_set_fmt(state);
		if (file->f_mode & FMODE_WRITE) {
			stop_dac(state);
			dmabuf->ready = 0;
			if (val)
				dmabuf->fmt |= TRIDENT_FMT_STEREO;
			else
				dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
		}
		if (file->f_mode & FMODE_READ) {
			stop_adc(state);
			dmabuf->ready = 0;
			if (val)
				dmabuf->fmt |= TRIDENT_FMT_STEREO;
			else
				dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
		}
		unlock_set_fmt(state);
		break;

	case SNDCTL_DSP_GETBLKSIZE:
		if (file->f_mode & FMODE_WRITE) {
			if ((val = prog_dmabuf(state, 0)))
				ret = val;
			else
				ret = put_user(dmabuf->fragsize, (int *)arg);
			break;
		}
		if (file->f_mode & FMODE_READ) {
			if ((val = prog_dmabuf(state, 1)))
				ret = val;
			else
				ret = put_user(dmabuf->fragsize, (int *)arg);
			break;
		}

	case SNDCTL_DSP_GETFMTS: /* Returns a mask of supported sample format*/
		ret = put_user(AFMT_S16_LE|AFMT_U16_LE|AFMT_S8|AFMT_U8, (int *)arg);
		break;

	case SNDCTL_DSP_SETFMT: /* Select sample format */
		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}
		lock_set_fmt(state);
		if (val != AFMT_QUERY) {
			if (file->f_mode & FMODE_WRITE) {
				stop_dac(state);
				dmabuf->ready = 0;
				if (val == AFMT_S16_LE)
					dmabuf->fmt |= TRIDENT_FMT_16BIT;
				else
					dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
			}
			if (file->f_mode & FMODE_READ) {
				stop_adc(state);
				dmabuf->ready = 0;
				if (val == AFMT_S16_LE)
					dmabuf->fmt |= TRIDENT_FMT_16BIT;
				else
					dmabuf->fmt &= ~TRIDENT_FMT_16BIT;
			}
		}
		unlock_set_fmt(state);
		ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
				AFMT_S16_LE : AFMT_U8, (int *)arg);
		break;

	case SNDCTL_DSP_CHANNELS:
		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}
		if (val != 0) {
			lock_set_fmt(state);
			if (file->f_mode & FMODE_WRITE) {
				stop_dac(state);
				dmabuf->ready = 0;
								
				//prevent from memory leak
				if ((state->chans_num > 2) && (state->chans_num != val)) {
					ali_free_other_states_resources(state);
					state->chans_num = 1;
				}
				
				if (val >= 2)
				{

					dmabuf->fmt |= TRIDENT_FMT_STEREO;
					if ((val == 6) && (state->card->pci_id == PCI_DEVICE_ID_ALI_5451)) {

						if( card->rec_channel_use_count > 0 )
						{
							printk(KERN_ERR "trident: Record is working on the card!\n");
							ret = -EBUSY;
							unlock_set_fmt(state); 
							break;
						}

						ret = ali_setup_multi_channels(state->card, 6);
						if (ret < 0) {
							unlock_set_fmt(state);
							break;
						}
						down(&state->card->open_sem);
						ret = ali_allocate_other_states_resources(state, 6);
						if (ret < 0) {
							up(&state->card->open_sem);
							unlock_set_fmt(state);
							break;
						}
						state->card->multi_channel_use_count ++;
						up(&state->card->open_sem);
					}
					else val = 2;	/*yield to 2-channels*/
				}
				else
					dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
				state->chans_num = val;
			}
			if (file->f_mode & FMODE_READ) {
				stop_adc(state);
				dmabuf->ready = 0;
				if (val >= 2) {
					if (!((file->f_mode & FMODE_WRITE) && (val == 6)))
						val = 2;
					dmabuf->fmt |= TRIDENT_FMT_STEREO;
				}
				else
					dmabuf->fmt &= ~TRIDENT_FMT_STEREO;
				state->chans_num = val;
			}
			unlock_set_fmt(state);
		}
		ret = put_user(val, (int *)arg);
		break;

	case SNDCTL_DSP_POST:
		/* Cause the working fragment to be output */
		break;

	case SNDCTL_DSP_SUBDIVIDE:
		if (dmabuf->subdivision)
		{
			ret = -EINVAL;
			break;
		}
		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}
		if (val != 1 && val != 2 && val != 4)
		{
			ret = -EINVAL;
			break;
		}
		dmabuf->subdivision = val;
		break;

	case SNDCTL_DSP_SETFRAGMENT:
		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}

		dmabuf->ossfragshift = val & 0xffff;
		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
		if (dmabuf->ossfragshift < 4)
			dmabuf->ossfragshift = 4;
		if (dmabuf->ossfragshift > 15)
			dmabuf->ossfragshift = 15;
		if (dmabuf->ossmaxfrags < 4)
			dmabuf->ossmaxfrags = 4;

		break;

	case SNDCTL_DSP_GETOSPACE:
		if (!(file->f_mode & FMODE_WRITE))
		{
			ret = -EINVAL;
			break;
		}
		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
		{ 
			ret = val;
			break;
		}
		spin_lock_irqsave(&state->card->lock, flags);
		trident_update_ptr(state);
		abinfo.fragsize = dmabuf->fragsize;
		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
		abinfo.fragstotal = dmabuf->numfrag;
		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
		spin_unlock_irqrestore(&state->card->lock, flags);
		ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
		break;

	case SNDCTL_DSP_GETISPACE:
		if (!(file->f_mode & FMODE_READ))
		{
			ret = -EINVAL;
			break;
		}
		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
		{
			ret = val;
			break;
		}
		spin_lock_irqsave(&state->card->lock, flags);
		trident_update_ptr(state);
		abinfo.fragsize = dmabuf->fragsize;
		abinfo.bytes = dmabuf->count;
		abinfo.fragstotal = dmabuf->numfrag;
		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
		spin_unlock_irqrestore(&state->card->lock, flags);
		ret = copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;
		break;

	case SNDCTL_DSP_NONBLOCK:
		file->f_flags |= O_NONBLOCK;
		break;

	case SNDCTL_DSP_GETCAPS:
		ret = put_user(DSP_CAP_REALTIME|DSP_CAP_TRIGGER|DSP_CAP_MMAP|DSP_CAP_BIND,
			    (int *)arg);
		break;

	case SNDCTL_DSP_GETTRIGGER:
		val = 0;
		if ((file->f_mode & FMODE_READ) && dmabuf->enable)
			val |= PCM_ENABLE_INPUT;
		if ((file->f_mode & FMODE_WRITE) && dmabuf->enable)
			val |= PCM_ENABLE_OUTPUT;
		ret = put_user(val, (int *)arg);
		break;

	case SNDCTL_DSP_SETTRIGGER:
		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}
		if (file->f_mode & FMODE_READ) {
			if (val & PCM_ENABLE_INPUT) {
				if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
					break;
				start_adc(state);
			} else
				stop_adc(state);
		}
		if (file->f_mode & FMODE_WRITE) {
			if (val & PCM_ENABLE_OUTPUT) {
				if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
					break;
				start_dac(state);
			} else
				stop_dac(state);
		}
		break;

	case SNDCTL_DSP_GETIPTR:
		if (!(file->f_mode & FMODE_READ))
		{
			ret = -EINVAL;
			break;
		}
		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
		{
			ret = val;
			break;
		}
		spin_lock_irqsave(&state->card->lock, flags);
		trident_update_ptr(state);
		cinfo.bytes = dmabuf->total_bytes;
		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
		cinfo.ptr = dmabuf->hwptr;
		if (dmabuf->mapped)
			dmabuf->count &= dmabuf->fragsize-1;
		spin_unlock_irqrestore(&state->card->lock, flags);
		ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0;
		break;

	case SNDCTL_DSP_GETOPTR:
		if (!(file->f_mode & FMODE_WRITE))
		{
			ret = -EINVAL;
			break;
		}
		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
		{
			ret = val;
			break;
		}
		
		spin_lock_irqsave(&state->card->lock, flags);
		trident_update_ptr(state);
		cinfo.bytes = dmabuf->total_bytes;
		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
		cinfo.ptr = dmabuf->hwptr;
		if (dmabuf->mapped)
			dmabuf->count &= dmabuf->fragsize-1;
		spin_unlock_irqrestore(&state->card->lock, flags);
		ret = copy_to_user((void *)arg, &cinfo, sizeof(cinfo))?-EFAULT:0;
		break;

	case SNDCTL_DSP_SETDUPLEX:
		ret = -EINVAL;
		break;

	case SNDCTL_DSP_GETODELAY:
		if (!(file->f_mode & FMODE_WRITE))
		{
			ret = -EINVAL;
			break;
		}
		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
		{
			ret = val;
			break;
		}
		spin_lock_irqsave(&state->card->lock, flags);
		trident_update_ptr(state);
		val = dmabuf->count;
		spin_unlock_irqrestore(&state->card->lock, flags);
		ret = put_user(val, (int *)arg);
		break;

	case SOUND_PCM_READ_RATE:
		ret = put_user(dmabuf->rate, (int *)arg);
		break;

	case SOUND_PCM_READ_CHANNELS:
		ret = put_user((dmabuf->fmt & TRIDENT_FMT_STEREO) ? 2 : 1,
				(int *)arg);
		break;

	case SOUND_PCM_READ_BITS:
		ret = put_user((dmabuf->fmt & TRIDENT_FMT_16BIT) ?
				AFMT_S16_LE : AFMT_U8, (int *)arg);
		break;

	case SNDCTL_DSP_GETCHANNELMASK:
		ret = put_user(DSP_BIND_FRONT|DSP_BIND_SURR|DSP_BIND_CENTER_LFE,
				(int *)arg);
		break;

	case SNDCTL_DSP_BIND_CHANNEL:
		if (state->card->pci_id != PCI_DEVICE_ID_SI_7018)
		{
			ret = -EINVAL;
			break;
		}

		if (get_user(val, (int *)arg))
		{
			ret = -EFAULT;
			break;
		}
		if (val == DSP_BIND_QUERY) {
			val = dmabuf->channel->attribute | 0x3c00;
			val = attr2mask[val >> 8];
		} else {
			dmabuf->ready = 0;
			if (file->f_mode & FMODE_READ)
				dmabuf->channel->attribute = (CHANNEL_REC|SRC_ENABLE);
			if (file->f_mode & FMODE_WRITE)
				dmabuf->channel->attribute = (CHANNEL_SPC_PB|SRC_ENABLE);
			dmabuf->channel->attribute |= mask2attr[ffs(val)];
		}
		ret = put_user(val, (int *)arg);
		break;

	case SNDCTL_DSP_MAPINBUF:
	case SNDCTL_DSP_MAPOUTBUF:
	case SNDCTL_DSP_SETSYNCRO:
	case SOUND_PCM_WRITE_FILTER:
	case SOUND_PCM_READ_FILTER:
	default:
		ret = -EINVAL;
		break;
		
	}
	return ret;
}

Generated by GNU enscript 1.6.4.