Enscript Output

extractedLnx/linux-2.6.9/sound/oss/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;
	void __user *argp = (void __user *)arg;
	int __user *p = argp;

	VALIDATE_STATE(state);


	mapped = ((file->f_mode & (FMODE_WRITE | FMODE_READ)) && dmabuf->mapped);

	pr_debug("trident: trident_ioctl, command = %2d, arg = 0x%08x\n",
		 _IOC_NR(cmd), arg ? *p : 0);

	switch (cmd) {
	case OSS_GETVERSION:
		ret = put_user(SOUND_VERSION, p);
		break;

	case SNDCTL_DSP_RESET:
		/* FIXME: spin_lock ? */
		if (file->f_mode & FMODE_WRITE) {
			stop_dac(state);
			synchronize_irq(card->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(card->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, p)) {
			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, p);
		break;

	case SNDCTL_DSP_STEREO:	/* set stereo or mono channel */
		if (get_user(val, p)) {
			ret = -EFAULT;
			break;
		}
		if ((ret = lock_set_fmt(state)) < 0)
			return ret;

		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_playback(state)))
				ret = val;
			else
				ret = put_user(dmabuf->fragsize, p);
			break;
		}
		if (file->f_mode & FMODE_READ) {
			if ((val = prog_dmabuf_record(state)))
				ret = val;
			else
				ret = put_user(dmabuf->fragsize, p);
			break;
		}
		/* neither READ nor WRITE? is this even possible? */
		ret = -EINVAL;
		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, p);
		break;

	case SNDCTL_DSP_SETFMT:	/* Select sample format */
		if (get_user(val, p)) {
			ret = -EFAULT;
			break;
		}
		if ((ret = lock_set_fmt(state)) < 0)
			return ret;

		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, p);
		break;

	case SNDCTL_DSP_CHANNELS:
		if (get_user(val, p)) {
			ret = -EFAULT;
			break;
		}
		if (val != 0) {
			if ((ret = lock_set_fmt(state)) < 0)
				return ret;

			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, p);
		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, p)) {
			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, p)) {
			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_playback(state)) != 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(argp, &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_record(state)) != 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(argp, &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, p);
		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, p);
		break;

	case SNDCTL_DSP_SETTRIGGER:
		if (get_user(val, p)) {
			ret = -EFAULT;
			break;
		}
		if (file->f_mode & FMODE_READ) {
			if (val & PCM_ENABLE_INPUT) {
				if (!dmabuf->ready && 
				    (ret = prog_dmabuf_record(state)))
					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_playback(state)))
					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_record(state))
		    != 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(argp, &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_playback(state))
		    != 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(argp, &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_playback(state)) != 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, p);
		break;

	case SOUND_PCM_READ_RATE:
		ret = put_user(dmabuf->rate, p);
		break;

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

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

	case SNDCTL_DSP_GETCHANNELMASK:
		ret = put_user(DSP_BIND_FRONT | DSP_BIND_SURR | 
			       DSP_BIND_CENTER_LFE,  p);
		break;

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

		if (get_user(val, p)) {
			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, p);
		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.