Enscript Output

extractedLnx/linux/drivers/sound/ymfpci.c_ymf_ioctl.c

static int ymf_ioctl(struct inode *inode, struct file *file,
    unsigned int cmd, unsigned long arg)
{
	struct ymf_state *state = (struct ymf_state *)file->private_data;
	struct ymf_dmabuf *dmabuf;
	unsigned long flags;
	audio_buf_info abinfo;
	count_info cinfo;
	int val;

	switch (cmd) {
	case OSS_GETVERSION:
		return put_user(SOUND_VERSION, (int *)arg);

	case SNDCTL_DSP_RESET:
		if (file->f_mode & FMODE_WRITE) {
			ymf_wait_dac(state);
			dmabuf = &state->wpcm.dmabuf;
			spin_lock_irqsave(&state->unit->reg_lock, flags);
			dmabuf->ready = 0;
			dmabuf->swptr = dmabuf->hwptr;
			dmabuf->count = dmabuf->total_bytes = 0;
			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		}
		if (file->f_mode & FMODE_READ) {
			ymf_stop_adc(state);
			dmabuf = &state->rpcm.dmabuf;
			spin_lock_irqsave(&state->unit->reg_lock, flags);
			dmabuf->ready = 0;
			dmabuf->swptr = dmabuf->hwptr;
			dmabuf->count = dmabuf->total_bytes = 0;
			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		}
		return 0;

	case SNDCTL_DSP_SYNC:
		if (file->f_mode & FMODE_WRITE) {
			dmabuf = &state->wpcm.dmabuf;
			if (file->f_flags & O_NONBLOCK) {
				spin_lock_irqsave(&state->unit->reg_lock, flags);
				if (dmabuf->count != 0 && !state->wpcm.running) {
					ymf_start_dac(state);
				}
				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
			} else {
				ymf_wait_dac(state);
			}
		}
		/* XXX What does this do for reading? dmabuf->count=0; ? */
		return 0;

	case SNDCTL_DSP_SPEED: /* set smaple rate */
		if (get_user(val, (int *)arg))
			return -EFAULT;
		if (val >= 8000 && val <= 48000) {
			if (file->f_mode & FMODE_WRITE) {
				ymf_wait_dac(state);
				dmabuf = &state->wpcm.dmabuf;
				spin_lock_irqsave(&state->unit->reg_lock, flags);
				dmabuf->ready = 0;
				state->format.rate = val;
				ymf_pcm_update_shift(&state->format);
				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
			}
			if (file->f_mode & FMODE_READ) {
				ymf_stop_adc(state);
				dmabuf = &state->rpcm.dmabuf;
				spin_lock_irqsave(&state->unit->reg_lock, flags);
				dmabuf->ready = 0;
				state->format.rate = val;
				ymf_pcm_update_shift(&state->format);
				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
			}
		}
		return put_user(state->format.rate, (int *)arg);

	/*
	 * OSS manual does not mention SNDCTL_DSP_STEREO at all.
	 * All channels are mono and if you want stereo, you
	 * play into two channels with SNDCTL_DSP_CHANNELS.
	 * However, mpg123 calls it. I wonder, why Michael Hipp used it.
	 */
	case SNDCTL_DSP_STEREO: /* set stereo or mono channel */
		if (get_user(val, (int *)arg))
			return -EFAULT;
		if (file->f_mode & FMODE_WRITE) {
			ymf_wait_dac(state); 
			dmabuf = &state->wpcm.dmabuf;
			spin_lock_irqsave(&state->unit->reg_lock, flags);
			dmabuf->ready = 0;
			state->format.voices = val ? 2 : 1;
			ymf_pcm_update_shift(&state->format);
			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		}
		if (file->f_mode & FMODE_READ) {
			ymf_stop_adc(state);
			dmabuf = &state->rpcm.dmabuf;
			spin_lock_irqsave(&state->unit->reg_lock, flags);
			dmabuf->ready = 0;
			state->format.voices = val ? 2 : 1;
			ymf_pcm_update_shift(&state->format);
			spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		}
		return 0;

	case SNDCTL_DSP_GETBLKSIZE:
		if (file->f_mode & FMODE_WRITE) {
			if ((val = prog_dmabuf(state, 0)))
				return val;
			return put_user(state->wpcm.dmabuf.fragsize, (int *)arg);
		}
		if (file->f_mode & FMODE_READ) {
			if ((val = prog_dmabuf(state, 1)))
				return val;
			return put_user(state->rpcm.dmabuf.fragsize, (int *)arg);
		}
		return -EINVAL;

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

	case SNDCTL_DSP_SETFMT: /* Select sample format */
		if (get_user(val, (int *)arg))
			return -EFAULT;
		if (val == AFMT_S16_LE || val == AFMT_U8) {
			if (file->f_mode & FMODE_WRITE) {
				ymf_wait_dac(state);
				dmabuf = &state->wpcm.dmabuf;
				spin_lock_irqsave(&state->unit->reg_lock, flags);
				dmabuf->ready = 0;
				state->format.format = val;
				ymf_pcm_update_shift(&state->format);
				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
			}
			if (file->f_mode & FMODE_READ) {
				ymf_stop_adc(state);
				dmabuf = &state->rpcm.dmabuf;
				spin_lock_irqsave(&state->unit->reg_lock, flags);
				dmabuf->ready = 0;
				state->format.format = val;
				ymf_pcm_update_shift(&state->format);
				spin_unlock_irqrestore(&state->unit->reg_lock, flags);
			}
		}
		return put_user(state->format.format, (int *)arg);

	case SNDCTL_DSP_CHANNELS:
		if (get_user(val, (int *)arg))
			return -EFAULT;
	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_CHANNELS 0x%x\n", val); */
		if (val != 0) {
			if (file->f_mode & FMODE_WRITE) {
				ymf_wait_dac(state);
				if (val == 1 || val == 2) {
					spin_lock_irqsave(&state->unit->reg_lock, flags);
					dmabuf = &state->wpcm.dmabuf;
					dmabuf->ready = 0;
					state->format.voices = val;
					ymf_pcm_update_shift(&state->format);
					spin_unlock_irqrestore(&state->unit->reg_lock, flags);
				}
			}
			if (file->f_mode & FMODE_READ) {
				ymf_stop_adc(state);
				if (val == 1 || val == 2) {
					spin_lock_irqsave(&state->unit->reg_lock, flags);
					dmabuf = &state->rpcm.dmabuf;
					dmabuf->ready = 0;
					state->format.voices = val;
					ymf_pcm_update_shift(&state->format);
					spin_unlock_irqrestore(&state->unit->reg_lock, flags);
				}
			}
		}
		return put_user(state->format.voices, (int *)arg);

	case SNDCTL_DSP_POST:
		/*
		 * Quoting OSS PG:
		 *    The ioctl SNDCTL_DSP_POST is a lightweight version of
		 *    SNDCTL_DSP_SYNC. It just tells to the driver that there
		 *    is likely to be a pause in the output. This makes it
		 *    possible for the device to handle the pause more
		 *    intelligently. This ioctl doesn't block the application.
		 *
		 * The paragraph above is a clumsy way to say "flush ioctl".
		 * This ioctl is used by mpg123.
		 */
		spin_lock_irqsave(&state->unit->reg_lock, flags);
		if (state->wpcm.dmabuf.count != 0 && !state->wpcm.running) {
			ymf_start_dac(state);
		}
		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		return 0;

#if 0 /* XXX Was dummy implementation anyways. Make sense of this. */
	case SNDCTL_DSP_SUBDIVIDE:
		dmabuf = &state->wpcm.dmabuf;
		if (dmabuf->subdivision)
			return -EINVAL;
		if (get_user(val, (int *)arg))
			return -EFAULT;
		if (val != 1 && val != 2)
			return -EINVAL;
		dmabuf->subdivision = val;
		return 0;
#endif

	case SNDCTL_DSP_SETFRAGMENT:
		if (get_user(val, (int *)arg))
			return -EFAULT;
		dmabuf = &state->wpcm.dmabuf;
		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;
		return 0;

	case SNDCTL_DSP_GETOSPACE:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		dmabuf = &state->wpcm.dmabuf;
		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
			return val;
		spin_lock_irqsave(&state->unit->reg_lock, flags);
		abinfo.fragsize = dmabuf->fragsize;
		abinfo.bytes = dmabuf->dmasize - dmabuf->count;
		abinfo.fragstotal = dmabuf->numfrag;
		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;

	case SNDCTL_DSP_GETISPACE:
		if (!(file->f_mode & FMODE_READ))
			return -EINVAL;
		dmabuf = &state->rpcm.dmabuf;
		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
			return val;
		spin_lock_irqsave(&state->unit->reg_lock, flags);
		abinfo.fragsize = dmabuf->fragsize;
		abinfo.bytes = dmabuf->count;
		abinfo.fragstotal = dmabuf->numfrag;
		abinfo.fragments = abinfo.bytes >> dmabuf->fragshift;
		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;

	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,
			    (int *)arg); */
		return put_user(0, (int *)arg);

#if 0 /* not implememnted, yet? */
	case SNDCTL_DSP_GETTRIGGER:
		val = 0;
		dmabuf = &state->wpcm.dmabuf;
		if (file->f_mode & FMODE_READ && dmabuf->enable)
			val |= PCM_ENABLE_INPUT;
		if (file->f_mode & FMODE_WRITE && dmabuf->enable)
			val |= PCM_ENABLE_OUTPUT;
		return put_user(val, (int *)arg);

	case SNDCTL_DSP_SETTRIGGER:
		if (get_user(val, (int *)arg))
			return -EFAULT;
		if (file->f_mode & FMODE_READ) {
			dmabuf = &state->rpcm.dmabuf;
			if (val & PCM_ENABLE_INPUT) {
				if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
					return ret;
				start_adc(state);
			} else
				stop_adc(state);
		}
		if (file->f_mode & FMODE_WRITE) {
			dmabuf = &state->wpcm.dmabuf;
			if (val & PCM_ENABLE_OUTPUT) {
				if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
					return ret;
				start_dac(state);  // sure?
			} else
				stop_dac(state);
		}
		return 0;
#endif

	case SNDCTL_DSP_GETIPTR:
		if (!(file->f_mode & FMODE_READ))
			return -EINVAL;
		dmabuf = &state->rpcm.dmabuf;
		spin_lock_irqsave(&state->unit->reg_lock, flags);
		cinfo.bytes = dmabuf->total_bytes;
		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
		cinfo.ptr = dmabuf->hwptr;
		/* XXX fishy - breaks invariant  count=hwptr-swptr */
		if (dmabuf->mapped)
			dmabuf->count &= dmabuf->fragsize-1;
		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;

	case SNDCTL_DSP_GETOPTR:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		dmabuf = &state->wpcm.dmabuf;
		spin_lock_irqsave(&state->unit->reg_lock, flags);
		cinfo.bytes = dmabuf->total_bytes;
		cinfo.blocks = dmabuf->count >> dmabuf->fragshift;
		cinfo.ptr = dmabuf->hwptr;
		/* XXX fishy - breaks invariant  count=swptr-hwptr */
		if (dmabuf->mapped)
			dmabuf->count &= dmabuf->fragsize-1;
		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo)) ? -EFAULT : 0;

	case SNDCTL_DSP_SETDUPLEX:	/* XXX TODO */
		return -EINVAL;

#if 0 /* XXX implement when an app found that uses it. */
	case SNDCTL_DSP_GETODELAY:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		spin_lock_irqsave(&state->unit->reg_lock, flags);
		cs_update_ptr(state);
		dmabuf = &state->wpcm.dmabuf;
		val = dmabuf->count;
		spin_unlock_irqrestore(&state->unit->reg_lock, flags);
		return put_user(val, (int *)arg);
#endif

	case SOUND_PCM_READ_RATE:
		return put_user(state->format.rate, (int *)arg);

	case SOUND_PCM_READ_CHANNELS:
		return put_user(state->format.voices, (int *)arg);

	case SOUND_PCM_READ_BITS:
		return put_user(AFMT_S16_LE, (int *)arg);

	case SNDCTL_DSP_MAPINBUF:
	case SNDCTL_DSP_MAPOUTBUF:
	case SNDCTL_DSP_SETSYNCRO:
	case SOUND_PCM_WRITE_FILTER:
	case SOUND_PCM_READ_FILTER:
		return -ENOTTY;

	default:
		/*
		 * Some programs mix up audio devices and ioctls
		 * or perhaps they expect "universal" ioctls,
		 * for instance we get SNDCTL_TMR_CONTINUE here.
		 * XXX Is there sound_generic_ioctl() around?
		 */
	}
	return -ENOTTY;
}

Generated by GNU enscript 1.6.4.