⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ymfpci.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
				if ((swptr += cnt) >= dmabuf->dmasize) {					swptr -= dmabuf->dmasize;				}			}			dmabuf->swptr = swptr;		} else {			/*			 * XXX This is not right if dmabuf->count is small -			 * about 2*x frame size or less. We cannot count on			 * on appending and not causing an artefact.			 * Should use a variation of the count==0 case above.			 */			swptr = dmabuf->swptr;		}		cnt = dmabuf->dmasize - swptr;		if (dmabuf->count + cnt > dmabuf->dmasize - redzone)			cnt = (dmabuf->dmasize - redzone) - dmabuf->count;		spin_unlock_irqrestore(&state->unit->reg_lock, flags);		if (cnt > count)			cnt = count;		if (cnt <= 0) {/* P3 */ /* printk("ymf_write: full, count %d swptr %d\n",  dmabuf->count, dmabuf->swptr); */			/*			 * buffer is full, start the dma machine and			 * wait for data to be played			 */			spin_lock_irqsave(&state->unit->reg_lock, flags);			if (!state->ypcm.running) {				ymf_playback_trigger(state->unit, &state->ypcm, 1);			}			spin_unlock_irqrestore(&state->unit->reg_lock, flags);			if (file->f_flags & O_NONBLOCK) {				if (!ret) ret = -EAGAIN;				break;			}			set_current_state(TASK_INTERRUPTIBLE);			schedule();			if (signal_pending(current)) {				if (!ret) ret = -ERESTARTSYS;				break;			}			continue;		}		if (copy_from_user(dmabuf->rawbuf + swptr, buffer, cnt)) {			if (!ret) ret = -EFAULT;			break;		}		if ((swptr += cnt) >= dmabuf->dmasize) {			swptr -= dmabuf->dmasize;		}		spin_lock_irqsave(&state->unit->reg_lock, flags);		dmabuf->swptr = swptr;		dmabuf->count += cnt;		/*		 * Start here is a bad idea - may cause startup click		 * in /bin/play when dmabuf is not full yet.		 * However, some broken applications do not make		 * any use of SNDCTL_DSP_SYNC (Doom is the worst).		 * One frame is about 5.3ms, Doom write size is 46ms.		 */		delay = state->format.rate / 20;	/* 50ms */		delay <<= state->format.shift;		if (dmabuf->count >= delay && !state->ypcm.running) {			ymf_playback_trigger(state->unit, &state->ypcm, 1);		}		spin_unlock_irqrestore(&state->unit->reg_lock, flags);		count -= cnt;		buffer += cnt;		ret += cnt;	}	set_current_state(TASK_RUNNING);	remove_wait_queue(&dmabuf->wait, &waita);/* P3 */ /* printk("ymf_write: dmabuf.count %d\n", dmabuf->count); */	return ret;}static unsigned int ymf_poll(struct file *file, struct poll_table_struct *wait){	struct ymf_state *state = (struct ymf_state *)file->private_data;	struct ymf_dmabuf *dmabuf = &state->dmabuf;	unsigned long flags;	unsigned int mask = 0;	if (file->f_mode & (FMODE_WRITE | FMODE_READ))		poll_wait(file, &dmabuf->wait, wait);	spin_lock_irqsave(&state->unit->reg_lock, flags);	if (file->f_mode & FMODE_READ) {		if (dmabuf->count >= (signed)dmabuf->fragsize)			mask |= POLLIN | POLLRDNORM;	}	if (file->f_mode & FMODE_WRITE) {		if (dmabuf->mapped) {			if (dmabuf->count >= (signed)dmabuf->fragsize)				mask |= POLLOUT | POLLWRNORM;		} else {			if ((signed)dmabuf->dmasize >= dmabuf->count + (signed)dmabuf->fragsize)				mask |= POLLOUT | POLLWRNORM;		}	}	spin_unlock_irqrestore(&state->unit->reg_lock, flags);	return mask;}static int ymf_mmap(struct file *file, struct vm_area_struct *vma){	struct ymf_state *state = (struct ymf_state *)file->private_data;	struct ymf_dmabuf *dmabuf = &state->dmabuf;	int ret;	unsigned long size;		if (vma->vm_flags & VM_WRITE) {		if ((ret = prog_dmabuf(state, 0)) != 0)			return ret;	} else if (vma->vm_flags & VM_READ) {		if ((ret = prog_dmabuf(state, 1)) != 0)			return ret;	} else 		return -EINVAL;	if (vma->vm_pgoff != 0)		return -EINVAL;	size = vma->vm_end - vma->vm_start;	if (size > (PAGE_SIZE << dmabuf->buforder))		return -EINVAL;	if (remap_page_range(vma->vm_start, virt_to_phys(dmabuf->rawbuf),			     size, vma->vm_page_prot))		return -EAGAIN;	dmabuf->mapped = 1;	return 0;}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 = &state->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);			spin_lock_irqsave(&state->unit->reg_lock, flags);			dmabuf->ready = 0;			dmabuf->swptr = dmabuf->hwptr = 0;			dmabuf->count = dmabuf->total_bytes = 0;			spin_unlock_irqrestore(&state->unit->reg_lock, flags);		}#if HAVE_RECORD		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;		}#endif		return 0;	case SNDCTL_DSP_SYNC:		if (file->f_mode & FMODE_WRITE) {			if (file->f_flags & O_NONBLOCK) {				spin_lock_irqsave(&state->unit->reg_lock, flags);				if (dmabuf->count != 0 && !state->ypcm.running) {					ymf_start_dac(state);				}				spin_unlock_irqrestore(&state->unit->reg_lock, flags);			} else {				ymf_wait_dac(state);			}		}		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);			}#if HAVE_RECORD			if (file->f_mode & FMODE_READ) {				stop_adc(state);			}#endif			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 uses it. I wonder, why Michael Hipp uses 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); 			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 HAVE_RECORD		if (file->f_mode & FMODE_READ) {			/* stop_adc(state); */			dmabuf->ready = 0;			if(val)				dmabuf->fmt |= CS_FMT_STEREO;			else				dmabuf->fmt &= ~CS_FMT_STEREO;		}#endif		return 0;	case SNDCTL_DSP_GETBLKSIZE:		if (file->f_mode & FMODE_WRITE) {			if ((val = prog_dmabuf(state, 0)))				return val;			return put_user(dmabuf->fragsize, (int *)arg);		}		if (file->f_mode & FMODE_READ) {			if ((val = prog_dmabuf(state, 1)))				return val;			return put_user(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);			}#if HAVE_RECORD			if (file->f_mode & FMODE_READ) {				stop_adc(state);			}#endif			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->ready = 0;					state->format.voices = val;					ymf_pcm_update_shift(&state->format);					spin_unlock_irqrestore(&state->unit->reg_lock, flags);				}			}#if HAVE_RECORD			if (file->f_mode & FMODE_READ) {				spin_lock_irqsave(&state->unit->reg_lock, flags);				stop_adc(state);				dmabuf->ready = 0;				spin_unlock_irqrestore(&state->unit->reg_lock, flags);			}#endif		}		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.		 */	/* P3 */ /* printk("ymfpci: ioctl SNDCTL_DSP_POST\n"); */		spin_lock_irqsave(&state->unit->reg_lock, flags);		if (dmabuf->count != 0 && !state->ypcm.running) {			ymf_start_dac(state);		}		spin_unlock_irqrestore(&state->unit->reg_lock, flags);		return 0;	case SNDCTL_DSP_SUBDIVIDE:		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;	case SNDCTL_DSP_SETFRAGMENT:		if (get_user(val, (int *)arg))			return -EFAULT;	/* P3: these frags are for Doom. Amasingly, it sets [2,2**11]. */	/* P3 */ // printk("ymfpci: ioctl SNDCTL_DSP_SETFRAGMENT 0x%x\n", val);		dmabuf->ossfragshift = val & 0xffff;		dmabuf->ossmaxfrags = (val >> 16) & 0xffff;		switch (dmabuf->ossmaxfrags) {		case 1:			dmabuf->ossfragshift = 12;			return 0;		default:			/* Fragments must be 2K long */			dmabuf->ossfragshift = 11;			dmabuf->ossmaxfrags = 2;		}		return 0;	case SNDCTL_DSP_GETOSPACE:		if (!(file->f_mode & FMODE_WRITE))			return -EINVAL;		if (!dmabuf->ready && (val = prog_dmabuf(state, 0)) != 0)			return val;		spin_lock_irqsave(&state->unit->reg_lock, flags);		/* cs_update_ptr(state); */  /* XXX Always up to date? */		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;#if HAVE_RECORD	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);		cs_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);		return copy_to_user((void *)arg, &abinfo, sizeof(abinfo)) ? -EFAULT : 0;#endif	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 /* old */	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;		return put_user(val, (int *)arg);	case SNDCTL_DSP_SETTRIGGER:		if (get_user(val, (int *)arg))			return -EFAULT;		if (file->f_mode & FMODE_READ) {			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) {			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#if HAVE_RECORD	case SNDCTL_DSP_GETIPTR:		if (!(file->f_mode & FMODE_READ))			return -EINVAL;		spin_lock_irqsave(&state->unit->reg_lock, flags);		cs_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->unit->reg_lock, flags);		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));#endif	case SNDCTL_DSP_GETOPTR:		if (!(file->f_mode & FMODE_WRITE))			return -EINVAL;		spin_lock_irqsave(&state->unit->reg_lock, flags);		/* cs_update_ptr(state); */  /* Always up to date */		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->unit->reg_lock, flags);		return copy_to_user((void *)arg, &cinfo, sizeof(cinfo));	case SNDCTL_DSP_SETDUPLEX:	/* XXX TODO */		return -EINVAL;#if 0 /* old */	case SNDCTL_DSP_GETODELAY:		if (!(file->f_mode & FMODE_WRITE))			return -EINVAL;		spin_lock_irqsave(&state->unit->reg_lock, flags);		cs_update_ptr(state);		val = dmabuf->count;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -