Enscript Output

extractedLnx/linux-2.6.9/sound/oss/ali5455.c_ali_ioctl.c

static int ali_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
	struct ali_state *state = (struct ali_state *) file->private_data;
	struct ali_channel *c = NULL;
	struct dmabuf *dmabuf = &state->dmabuf;
	unsigned long flags;
	audio_buf_info abinfo;
	count_info cinfo;
	unsigned int i_scr;
	int val = 0, ret;
	struct ac97_codec *codec = state->card->ac97_codec[0];
	void __user *argp = (void __user *)arg;
	int __user *p = argp;

#ifdef DEBUG
	printk("ali_audio: ali_ioctl, arg=0x%x, cmd=",
	       arg ? *p : 0);
#endif
	switch (cmd) {
	case OSS_GETVERSION:
#ifdef DEBUG
		printk("OSS_GETVERSION\n");
#endif
		return put_user(SOUND_VERSION, p);
	case SNDCTL_DSP_RESET:
#ifdef DEBUG
		printk("SNDCTL_DSP_RESET\n");
#endif
		spin_lock_irqsave(&state->card->lock, flags);
		if (dmabuf->enable == DAC_RUNNING) {
			c = dmabuf->write_channel;
			__stop_dac(state);
		}
		if (dmabuf->enable == ADC_RUNNING) {
			c = dmabuf->read_channel;
			__stop_adc(state);
		}
		if (dmabuf->enable == CODEC_SPDIFOUT_RUNNING) {
			c = dmabuf->codec_spdifout_channel;
			__stop_spdifout(state);
		}
		if (dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) {
			c = dmabuf->controller_spdifout_channel;
			__stop_spdifout(state);
		}
		if (c != NULL) {
			outb(2, state->card->iobase + c->port + OFF_CR);	/* reset DMA machine */
			outl(virt_to_bus(&c->sg[0]),
			     state->card->iobase + c->port + OFF_BDBAR);
			outb(0, state->card->iobase + c->port + OFF_CIV);
			outb(0, state->card->iobase + c->port + OFF_LVI);
		}

		spin_unlock_irqrestore(&state->card->lock, flags);
		synchronize_irq(state->card->pci_dev->irq);
		dmabuf->ready = 0;
		dmabuf->swptr = dmabuf->hwptr = 0;
		dmabuf->count = dmabuf->total_bytes = 0;
		return 0;
	case SNDCTL_DSP_SYNC:
#ifdef DEBUG
		printk("SNDCTL_DSP_SYNC\n");
#endif
		if (codec_independent_spdif_locked > 0) {
			if (dmabuf->enable != CODEC_SPDIFOUT_RUNNING
			    || file->f_flags & O_NONBLOCK)
				return 0;
			if ((val = drain_spdifout(state, 1)))
				return val;
		} else {
			if (controller_independent_spdif_locked > 0) {
				if (dmabuf->enable !=
				    CONTROLLER_SPDIFOUT_RUNNING
				    || file->f_flags & O_NONBLOCK)
					return 0;
				if ((val = drain_spdifout(state, 1)))
					return val;
			} else {
				if (dmabuf->enable != DAC_RUNNING
				    || file->f_flags & O_NONBLOCK)
					return 0;
				if ((val = drain_dac(state, 1)))
					return val;
			}
		}
		dmabuf->total_bytes = 0;
		return 0;
	case SNDCTL_DSP_SPEED:	/* set smaple rate */
#ifdef DEBUG
		printk("SNDCTL_DSP_SPEED\n");
#endif
		if (get_user(val, p))
			return -EFAULT;
		if (val >= 0) {
			if (file->f_mode & FMODE_WRITE) {
				if ((state->card->ac97_status & SPDIF_ON)) {	/* S/PDIF Enabled */
					/* RELTEK ALC650 only support 48000, need to check that */
					if (ali_valid_spdif_rate(codec, val)) {
						if (codec_independent_spdif_locked > 0) {
							ali_set_spdif_output(state, -1, 0);
							stop_spdifout(state);
							dmabuf->ready = 0;
							/* I add test codec independent spdif out */
							spin_lock_irqsave(&state->card->lock, flags);
							ali_set_codecspdifout_rate(state, val);	// I modified
							spin_unlock_irqrestore(&state->card->lock, flags);
							/* Set S/PDIF transmitter rate. */
							i_scr = inl(state->card->iobase + ALI_SCR);
							if ((i_scr & 0x00300000) == 0x00100000) {
								ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
							} else {
								if ((i_scr&0x00300000)  == 0x00200000)
								{
									ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked);
								} else {
									if ((i_scr & 0x00300000) == 0x00300000) {
										ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked);
									} else {
										ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
									}
								}
							}

							if (!(state->card->ac97_status & SPDIF_ON)) {
								val = dmabuf->rate;
							}
						} else {
							if (controller_independent_spdif_locked > 0) 
							{
								stop_spdifout(state);
								dmabuf->ready = 0;
								spin_lock_irqsave(&state->card->lock, flags);
								ali_set_spdifout_rate(state, controller_independent_spdif_locked);
								spin_unlock_irqrestore(&state->card->lock, flags);
							} else {
								/* Set DAC rate */
								ali_set_spdif_output(state, -1, 0);
								stop_dac(state);
								dmabuf->ready = 0;
								spin_lock_irqsave(&state->card->lock, flags);
								ali_set_dac_rate(state, val);
								spin_unlock_irqrestore(&state->card->lock, flags);
								/* Set S/PDIF transmitter rate. */
								ali_set_spdif_output(state, AC97_EA_SPSA_3_4, val);
								if (!(state->card->ac97_status & SPDIF_ON))
								{
									val = dmabuf->rate;
								}
							}
						}
					} else {	/* Not a valid rate for S/PDIF, ignore it */
						val = dmabuf->rate;
					}
				} else {
					stop_dac(state);
					dmabuf->ready = 0;
					spin_lock_irqsave(&state->card->lock, flags);
					ali_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);
				ali_set_adc_rate(state, val);
				spin_unlock_irqrestore(&state->card->lock, flags);
			}
		}
		return put_user(dmabuf->rate, p);
	case SNDCTL_DSP_STEREO:	/* set stereo or mono channel */
#ifdef DEBUG
		printk("SNDCTL_DSP_STEREO\n");
#endif
		if (dmabuf->enable & DAC_RUNNING) {
			stop_dac(state);
		}
		if (dmabuf->enable & ADC_RUNNING) {
			stop_adc(state);
		}
		if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {
			stop_spdifout(state);
		}
		if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) {
			stop_spdifout(state);
		}
		return put_user(1, p);
	case SNDCTL_DSP_GETBLKSIZE:
		if (file->f_mode & FMODE_WRITE) {
			if (codec_independent_spdif_locked > 0) {
				if (!dmabuf->ready && (val = prog_dmabuf(state, 2)))
					return val;
			} else {
				if (controller_independent_spdif_locked > 0) {
					if (!dmabuf->ready && (val = prog_dmabuf(state, 3)))
						return val;
				} else {
					if (!dmabuf->ready && (val = prog_dmabuf(state, 0)))
						return val;
				}
			}
		}

		if (file->f_mode & FMODE_READ) {
			if (!dmabuf->ready && (val = prog_dmabuf(state, 1)))
				return val;
		}
#ifdef DEBUG
		printk("SNDCTL_DSP_GETBLKSIZE %d\n", dmabuf->userfragsize);
#endif
		return put_user(dmabuf->userfragsize, p);
	case SNDCTL_DSP_GETFMTS:	/* Returns a mask of supported sample format */
#ifdef DEBUG
		printk("SNDCTL_DSP_GETFMTS\n");
#endif
		return put_user(AFMT_S16_LE, p);
	case SNDCTL_DSP_SETFMT:	/* Select sample format */
#ifdef DEBUG
		printk("SNDCTL_DSP_SETFMT\n");
#endif
		return put_user(AFMT_S16_LE, p);
	case SNDCTL_DSP_CHANNELS:	// add support 4,6 channel 
#ifdef DEBUG
		printk("SNDCTL_DSP_CHANNELS\n");
#endif
		if (get_user(val, p))
			return -EFAULT;
		if (val > 0) {
			if (dmabuf->enable & DAC_RUNNING) {
				stop_dac(state);
			}
			if (dmabuf->enable & CODEC_SPDIFOUT_RUNNING) {
				stop_spdifout(state);
			}
			if (dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING) {
				stop_spdifout(state);
			}
			if (dmabuf->enable & ADC_RUNNING) {
				stop_adc(state);
			}
		} else {
			return put_user(state->card->channels, p);
		}

		i_scr = inl(state->card->iobase + ALI_SCR);
		/* Current # of channels enabled */
		if (i_scr & 0x00000100)
			ret = 4;
		else if (i_scr & 0x00000200)
			ret = 6;
		else
			ret = 2;
		switch (val) {
		case 2:	/* 2 channels is always supported */
			if (codec_independent_spdif_locked > 0) {
				outl(((i_scr & 0xfffffcff) | 0x00100000), (state->card->iobase + ALI_SCR));
			} else
				outl((i_scr & 0xfffffcff), (state->card->iobase + ALI_SCR));
			/* Do we need to change mixer settings????  */
			break;
		case 4:	/* Supported on some chipsets, better check first */
			if (codec_independent_spdif_locked > 0) {
				outl(((i_scr & 0xfffffcff) | 0x00000100 | 0x00200000), (state->card->iobase + ALI_SCR));
			} else
				outl(((i_scr & 0xfffffcff) | 0x00000100), (state->card->iobase + ALI_SCR));
			break;
		case 6:	/* Supported on some chipsets, better check first */
			if (codec_independent_spdif_locked > 0) {
				outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000 | 0x00300000), (state->card->iobase + ALI_SCR));
			} else
				outl(((i_scr & 0xfffffcff) | 0x00000200 | 0x00008000), (state->card->iobase + ALI_SCR));
			break;
		default:	/* nothing else is ever supported by the chipset */
			val = ret;
			break;
		}
		return put_user(val, p);
	case SNDCTL_DSP_POST:	/* the user has sent all data and is notifying us */
		/* we update the swptr to the end of the last sg segment then return */
#ifdef DEBUG
		printk("SNDCTL_DSP_POST\n");
#endif
		if (codec_independent_spdif_locked > 0) {
			if (!dmabuf->ready || (dmabuf->enable != CODEC_SPDIFOUT_RUNNING))
				return 0;
		} else {
			if (controller_independent_spdif_locked > 0) {
				if (!dmabuf->ready || (dmabuf->enable != CONTROLLER_SPDIFOUT_RUNNING))
					return 0;
			} else {
				if (!dmabuf->ready || (dmabuf->enable != DAC_RUNNING))
					return 0;
			}
		}
		if ((dmabuf->swptr % dmabuf->fragsize) != 0) {
			val = dmabuf->fragsize - (dmabuf->swptr % dmabuf->fragsize);
			dmabuf->swptr += val;
			dmabuf->count += val;
		}
		return 0;
	case SNDCTL_DSP_SUBDIVIDE:
		if (dmabuf->subdivision)
			return -EINVAL;
		if (get_user(val, p))
			return -EFAULT;
		if (val != 1 && val != 2 && val != 4)
			return -EINVAL;
#ifdef DEBUG
		printk("SNDCTL_DSP_SUBDIVIDE %d\n", val);
#endif
		dmabuf->subdivision = val;
		dmabuf->ready = 0;
		return 0;
	case SNDCTL_DSP_SETFRAGMENT:
		if (get_user(val, p))
			return -EFAULT;
		dmabuf->ossfragsize = 1 << (val & 0xffff);
		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;
		if (!dmabuf->ossfragsize || !dmabuf->ossmaxfrags)
			return -EINVAL;
		/*
		 * Bound the frag size into our allowed range of 256 - 4096
		 */
		if (dmabuf->ossfragsize < 256)
			dmabuf->ossfragsize = 256;
		else if (dmabuf->ossfragsize > 4096)
			dmabuf->ossfragsize = 4096;
		/*
		 * The numfrags could be something reasonable, or it could
		 * be 0xffff meaning "Give me as much as possible".  So,
		 * we check the numfrags * fragsize doesn't exceed our
		 * 64k buffer limit, nor is it less than our 8k minimum.
		 * If it fails either one of these checks, then adjust the
		 * number of fragments, not the size of them.  It's OK if
		 * our number of fragments doesn't equal 32 or anything
		 * like our hardware based number now since we are using
		 * a different frag count for the hardware.  Before we get
		 * into this though, bound the maxfrags to avoid overflow
		 * issues.  A reasonable bound would be 64k / 256 since our
		 * maximum buffer size is 64k and our minimum frag size is
		 * 256.  On the other end, our minimum buffer size is 8k and
		 * our maximum frag size is 4k, so the lower bound should
		 * be 2.
		 */
		if (dmabuf->ossmaxfrags > 256)
			dmabuf->ossmaxfrags = 256;
		else if (dmabuf->ossmaxfrags < 2)
			dmabuf->ossmaxfrags = 2;
		val = dmabuf->ossfragsize * dmabuf->ossmaxfrags;
		while (val < 8192) {
			val <<= 1;
			dmabuf->ossmaxfrags <<= 1;
		}
		while (val > 65536) {
			val >>= 1;
			dmabuf->ossmaxfrags >>= 1;
		}
		dmabuf->ready = 0;
#ifdef DEBUG
		printk("SNDCTL_DSP_SETFRAGMENT 0x%x, %d, %d\n", val,
		       dmabuf->ossfragsize, dmabuf->ossmaxfrags);
#endif
		return 0;
	case SNDCTL_DSP_GETOSPACE:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		if (codec_independent_spdif_locked > 0) {
			if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0)
				return val;
		} else {
			if (controller_independent_spdif_locked > 0) {
				if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0)
					return val;
			} else {
				if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
					return val;
			}
		}
		spin_lock_irqsave(&state->card->lock, flags);
		ali_update_ptr(state);
		abinfo.fragsize = dmabuf->userfragsize;
		abinfo.fragstotal = dmabuf->userfrags;
		if (dmabuf->mapped)
			abinfo.bytes = dmabuf->dmasize;
		else
			abinfo.bytes = ali_get_free_write_space(state);
		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
		spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
		printk("SNDCTL_DSP_GETOSPACE %d, %d, %d, %d\n",
		       abinfo.bytes, abinfo.fragsize, abinfo.fragments,
		       abinfo.fragstotal);
#endif
		return copy_to_user(argp, &abinfo,
				    sizeof(abinfo)) ? -EFAULT : 0;
	case SNDCTL_DSP_GETOPTR:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		if (codec_independent_spdif_locked > 0) {
			if (!dmabuf->ready && (val = prog_dmabuf(state, 2)) != 0)
				return val;
		} else {
			if (controller_independent_spdif_locked > 0) {
				if (!dmabuf->ready && (val = prog_dmabuf(state, 3)) != 0)
					return val;
			} else {
				if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
					return val;
			}
		}
		spin_lock_irqsave(&state->card->lock, flags);
		val = ali_get_free_write_space(state);
		cinfo.bytes = dmabuf->total_bytes;
		cinfo.ptr = dmabuf->hwptr;
		cinfo.blocks = val / dmabuf->userfragsize;
		if (codec_independent_spdif_locked > 0) {
			if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
				dmabuf->count += val;
				dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
				__ali_update_lvi(state, 2);
			}
		} else {
			if (controller_independent_spdif_locked > 0) {
				if (dmabuf->mapped && (dmabuf->trigger & SPDIF_ENABLE_OUTPUT)) {
					dmabuf->count += val;
					dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
					__ali_update_lvi(state, 3);
				}
			} else {
				if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_OUTPUT)) {
					dmabuf->count += val;
					dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
					__ali_update_lvi(state, 0);
				}
			}
		}
		spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
		printk("SNDCTL_DSP_GETOPTR %d, %d, %d, %d\n", cinfo.bytes,
		       cinfo.blocks, cinfo.ptr, dmabuf->count);
#endif
		return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT : 0;
	case SNDCTL_DSP_GETISPACE:
		if (!(file->f_mode & FMODE_READ))
			return -EINVAL;
		if (!dmabuf->ready && (val = prog_dmabuf(state, 1)) != 0)
			return val;
		spin_lock_irqsave(&state->card->lock, flags);
		abinfo.bytes = ali_get_available_read_data(state);
		abinfo.fragsize = dmabuf->userfragsize;
		abinfo.fragstotal = dmabuf->userfrags;
		abinfo.fragments = abinfo.bytes / dmabuf->userfragsize;
		spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
		printk("SNDCTL_DSP_GETISPACE %d, %d, %d, %d\n",
		       abinfo.bytes, abinfo.fragsize, abinfo.fragments,
		       abinfo.fragstotal);
#endif
		return copy_to_user(argp, &abinfo,
				    sizeof(abinfo)) ? -EFAULT : 0;
	case SNDCTL_DSP_GETIPTR:
		if (!(file->f_mode & FMODE_READ))
			return -EINVAL;
		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)
			return val;
		spin_lock_irqsave(&state->card->lock, flags);
		val = ali_get_available_read_data(state);
		cinfo.bytes = dmabuf->total_bytes;
		cinfo.blocks = val / dmabuf->userfragsize;
		cinfo.ptr = dmabuf->hwptr;
		if (dmabuf->mapped && (dmabuf->trigger & PCM_ENABLE_INPUT)) {
			dmabuf->count -= val;
			dmabuf->swptr = (dmabuf->swptr + val) % dmabuf->dmasize;
			__ali_update_lvi(state, 1);
		}
		spin_unlock_irqrestore(&state->card->lock, flags);
#if defined(DEBUG) || defined(DEBUG_MMAP)
		printk("SNDCTL_DSP_GETIPTR %d, %d, %d, %d\n", cinfo.bytes,
		       cinfo.blocks, cinfo.ptr, dmabuf->count);
#endif
		return copy_to_user(argp, &cinfo, sizeof(cinfo))? -EFAULT: 0;
	case SNDCTL_DSP_NONBLOCK:
#ifdef DEBUG
		printk("SNDCTL_DSP_NONBLOCK\n");
#endif
		file->f_flags |= O_NONBLOCK;
		return 0;
	case SNDCTL_DSP_GETCAPS:
#ifdef DEBUG
		printk("SNDCTL_DSP_GETCAPS\n");
#endif
		return put_user(DSP_CAP_REALTIME | DSP_CAP_TRIGGER |
				DSP_CAP_MMAP | DSP_CAP_BIND, p);
	case SNDCTL_DSP_GETTRIGGER:
		val = 0;
#ifdef DEBUG
		printk("SNDCTL_DSP_GETTRIGGER 0x%x\n", dmabuf->trigger);
#endif
		return put_user(dmabuf->trigger, p);
	case SNDCTL_DSP_SETTRIGGER:
		if (get_user(val, p))
			return -EFAULT;
#if defined(DEBUG) || defined(DEBUG_MMAP)
		printk("SNDCTL_DSP_SETTRIGGER 0x%x\n", val);
#endif
		if (!(val & PCM_ENABLE_INPUT) && dmabuf->enable == ADC_RUNNING) {
			stop_adc(state);
		}
		if (!(val & PCM_ENABLE_OUTPUT) && dmabuf->enable == DAC_RUNNING) {
			stop_dac(state);
		}
		if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CODEC_SPDIFOUT_RUNNING) {
			stop_spdifout(state);
		}
		if (!(val & SPDIF_ENABLE_OUTPUT) && dmabuf->enable == CONTROLLER_SPDIFOUT_RUNNING) {
			stop_spdifout(state);
		}
		dmabuf->trigger = val;
		if (val & PCM_ENABLE_OUTPUT && !(dmabuf->enable & DAC_RUNNING)) {
			if (!dmabuf->write_channel) {
				dmabuf->ready = 0;
				dmabuf->write_channel = state->card->alloc_pcm_channel(state->card);
				if (!dmabuf->write_channel)
					return -EBUSY;
			}
			if (!dmabuf->ready && (ret = prog_dmabuf(state, 0)))
				return ret;
			if (dmabuf->mapped) {
				spin_lock_irqsave(&state->card->lock, flags);
				ali_update_ptr(state);
				dmabuf->count = 0;
				dmabuf->swptr = dmabuf->hwptr;
				dmabuf->count = ali_get_free_write_space(state);
				dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
				__ali_update_lvi(state, 0);
				spin_unlock_irqrestore(&state->card->lock,
						       flags);
			} else
				start_dac(state);
		}
		if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CODEC_SPDIFOUT_RUNNING)) {
			if (!dmabuf->codec_spdifout_channel) {
				dmabuf->ready = 0;
				dmabuf->codec_spdifout_channel = state->card->alloc_codec_spdifout_channel(state->card);
				if (!dmabuf->codec_spdifout_channel)
					return -EBUSY;
			}
			if (!dmabuf->ready && (ret = prog_dmabuf(state, 2)))
				return ret;
			if (dmabuf->mapped) {
				spin_lock_irqsave(&state->card->lock, flags);
				ali_update_ptr(state);
				dmabuf->count = 0;
				dmabuf->swptr = dmabuf->hwptr;
				dmabuf->count = ali_get_free_write_space(state);
				dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
				__ali_update_lvi(state, 2);
				spin_unlock_irqrestore(&state->card->lock,
						       flags);
			} else
				start_spdifout(state);
		}
		if (val & SPDIF_ENABLE_OUTPUT && !(dmabuf->enable & CONTROLLER_SPDIFOUT_RUNNING)) {
			if (!dmabuf->controller_spdifout_channel) {
				dmabuf->ready = 0;
				dmabuf->controller_spdifout_channel = state->card->alloc_controller_spdifout_channel(state->card);
				if (!dmabuf->controller_spdifout_channel)
					return -EBUSY;
			}
			if (!dmabuf->ready && (ret = prog_dmabuf(state, 3)))
				return ret;
			if (dmabuf->mapped) {
				spin_lock_irqsave(&state->card->lock, flags);
				ali_update_ptr(state);
				dmabuf->count = 0;
				dmabuf->swptr = dmabuf->hwptr;
				dmabuf->count = ali_get_free_write_space(state);
				dmabuf->swptr = (dmabuf->swptr + dmabuf->count) % dmabuf->dmasize;
				__ali_update_lvi(state, 3);
				spin_unlock_irqrestore(&state->card->lock, flags);
			} else
				start_spdifout(state);
		}
		if (val & PCM_ENABLE_INPUT && !(dmabuf->enable & ADC_RUNNING)) {
			if (!dmabuf->read_channel) {
				dmabuf->ready = 0;
				dmabuf->read_channel = state->card->alloc_rec_pcm_channel(state->card);
				if (!dmabuf->read_channel)
					return -EBUSY;
			}
			if (!dmabuf->ready && (ret = prog_dmabuf(state, 1)))
				return ret;
			if (dmabuf->mapped) {
				spin_lock_irqsave(&state->card->lock,
						  flags);
				ali_update_ptr(state);
				dmabuf->swptr = dmabuf->hwptr;
				dmabuf->count = 0;
				spin_unlock_irqrestore(&state->card->lock, flags);
			}
			ali_update_lvi(state, 1);
			start_adc(state);
		}
		return 0;
	case SNDCTL_DSP_SETDUPLEX:
#ifdef DEBUG
		printk("SNDCTL_DSP_SETDUPLEX\n");
#endif
		return -EINVAL;
	case SNDCTL_DSP_GETODELAY:
		if (!(file->f_mode & FMODE_WRITE))
			return -EINVAL;
		spin_lock_irqsave(&state->card->lock, flags);
		ali_update_ptr(state);
		val = dmabuf->count;
		spin_unlock_irqrestore(&state->card->lock, flags);
#ifdef DEBUG
		printk("SNDCTL_DSP_GETODELAY %d\n", dmabuf->count);
#endif
		return put_user(val, p);
	case SOUND_PCM_READ_RATE:
#ifdef DEBUG
		printk("SOUND_PCM_READ_RATE %d\n", dmabuf->rate);
#endif
		return put_user(dmabuf->rate, p);
	case SOUND_PCM_READ_CHANNELS:
#ifdef DEBUG
		printk("SOUND_PCM_READ_CHANNELS\n");
#endif
		return put_user(2, p);
	case SOUND_PCM_READ_BITS:
#ifdef DEBUG
		printk("SOUND_PCM_READ_BITS\n");
#endif
		return put_user(AFMT_S16_LE, p);
	case SNDCTL_DSP_SETSPDIF:	/* Set S/PDIF Control register */
#ifdef DEBUG
		printk("SNDCTL_DSP_SETSPDIF\n");
#endif
		if (get_user(val, p))
			return -EFAULT;
		/* Check to make sure the codec supports S/PDIF transmitter */
		if ((state->card->ac97_features & 4)) {
			/* mask out the transmitter speed bits so the user can't set them */
			val &= ~0x3000;
			/* Add the current transmitter speed bits to the passed value */
			ret = ali_ac97_get(codec, AC97_SPDIF_CONTROL);
			val |= (ret & 0x3000);
			ali_ac97_set(codec, AC97_SPDIF_CONTROL, val);
			if (ali_ac97_get(codec, AC97_SPDIF_CONTROL) != val) {
				printk(KERN_ERR "ali_audio: Unable to set S/PDIF configuration to 0x%04x.\n", val);
				return -EFAULT;
			}
		}
#ifdef DEBUG
		else
			printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n");
#endif
		return put_user(val, p);
	case SNDCTL_DSP_GETSPDIF:	/* Get S/PDIF Control register */
#ifdef DEBUG
		printk("SNDCTL_DSP_GETSPDIF\n");
#endif
		if (get_user(val, p))
			return -EFAULT;
		/* Check to make sure the codec supports S/PDIF transmitter */
		if (!(state->card->ac97_features & 4)) {
#ifdef DEBUG
			printk(KERN_WARNING "ali_audio: S/PDIF transmitter not avalible.\n");
#endif
			val = 0;
		} else {
			val = ali_ac97_get(codec, AC97_SPDIF_CONTROL);
		}

		return put_user(val, p);
//end add support spdif out
//add support 4,6 channel
	case SNDCTL_DSP_GETCHANNELMASK:
#ifdef DEBUG
		printk("SNDCTL_DSP_GETCHANNELMASK\n");
#endif
		if (get_user(val, p))
			return -EFAULT;
		/* Based on AC'97 DAC support, not ICH hardware */
		val = DSP_BIND_FRONT;
		if (state->card->ac97_features & 0x0004)
			val |= DSP_BIND_SPDIF;
		if (state->card->ac97_features & 0x0080)
			val |= DSP_BIND_SURR;
		if (state->card->ac97_features & 0x0140)
			val |= DSP_BIND_CENTER_LFE;
		return put_user(val, p);
	case SNDCTL_DSP_BIND_CHANNEL:
#ifdef DEBUG
		printk("SNDCTL_DSP_BIND_CHANNEL\n");
#endif
		if (get_user(val, p))
			return -EFAULT;
		if (val == DSP_BIND_QUERY) {
			val = DSP_BIND_FRONT;	/* Always report this as being enabled */
			if (state->card->ac97_status & SPDIF_ON)
				val |= DSP_BIND_SPDIF;
			else {
				if (state->card->ac97_status & SURR_ON)
					val |= DSP_BIND_SURR;
				if (state->card->
				    ac97_status & CENTER_LFE_ON)
					val |= DSP_BIND_CENTER_LFE;
			}
		} else {	/* Not a query, set it */
			if (!(file->f_mode & FMODE_WRITE))
				return -EINVAL;
			if (dmabuf->enable == DAC_RUNNING) {
				stop_dac(state);
			}
			if (val & DSP_BIND_SPDIF) {	/* Turn on SPDIF */
				/*  Ok, this should probably define what slots
				 *  to use. For now, we'll only set it to the
				 *  defaults:
				 * 
				 *   non multichannel codec maps to slots 3&4
				 *   2 channel codec maps to slots 7&8
				 *   4 channel codec maps to slots 6&9
				 *   6 channel codec maps to slots 10&11
				 *
				 *  there should be some way for the app to
				 *  select the slot assignment.
				 */
				i_scr = inl(state->card->iobase + ALI_SCR);
				if (codec_independent_spdif_locked > 0) {

					if ((i_scr & 0x00300000) == 0x00100000) {
						ali_set_spdif_output(state, AC97_EA_SPSA_7_8, codec_independent_spdif_locked);
					} else {
						if ((i_scr & 0x00300000) == 0x00200000) {
							ali_set_spdif_output(state, AC97_EA_SPSA_6_9, codec_independent_spdif_locked);
						} else {
							if ((i_scr & 0x00300000) == 0x00300000) {
								ali_set_spdif_output(state, AC97_EA_SPSA_10_11, codec_independent_spdif_locked);
							}
						}
					}
				} else {	/* codec spdif out (pcm out share ) */
					ali_set_spdif_output(state, AC97_EA_SPSA_3_4, dmabuf->rate);	//I do not modify
				}

				if (!(state->card->ac97_status & SPDIF_ON))
					val &= ~DSP_BIND_SPDIF;
			} else {
				int mask;
				int channels;
				/* Turn off S/PDIF if it was on */
				if (state->card->ac97_status & SPDIF_ON)
					ali_set_spdif_output(state, -1, 0);
				mask =
				    val & (DSP_BIND_FRONT | DSP_BIND_SURR |
					   DSP_BIND_CENTER_LFE);
				switch (mask) {
				case DSP_BIND_FRONT:
					channels = 2;
					break;
				case DSP_BIND_FRONT | DSP_BIND_SURR:
					channels = 4;
					break;
				case DSP_BIND_FRONT | DSP_BIND_SURR | DSP_BIND_CENTER_LFE:
					channels = 6;
					break;
				default:
					val = DSP_BIND_FRONT;
					channels = 2;
					break;
				}
				ali_set_dac_channels(state, channels);
				/* check that they really got turned on */
				if (!state->card->ac97_status & SURR_ON)
					val &= ~DSP_BIND_SURR;
				if (!state->card->
				    ac97_status & CENTER_LFE_ON)
					val &= ~DSP_BIND_CENTER_LFE;
			}
		}
		return put_user(val, 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.