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

📄 pcm_lib.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
			return err;	} else {		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);		snd_assert(runtime->dma_area, return -EFAULT);		if (copy_from_user(hwbuf, buf, frames_to_bytes(runtime, frames)))			return -EFAULT;	}	return 0;} typedef int (*transfer_f)(struct snd_pcm_substream *substream, unsigned int hwoff,			  unsigned long data, unsigned int off,			  snd_pcm_uframes_t size);static snd_pcm_sframes_t snd_pcm_lib_write1(struct snd_pcm_substream *substream, 					    unsigned long data,					    snd_pcm_uframes_t size,					    int nonblock,					    transfer_f transfer){	struct snd_pcm_runtime *runtime = substream->runtime;	snd_pcm_uframes_t xfer = 0;	snd_pcm_uframes_t offset = 0;	int err = 0;	if (size == 0)		return 0;	if (size > runtime->xfer_align)		size -= size % runtime->xfer_align;	snd_pcm_stream_lock_irq(substream);	switch (runtime->status->state) {	case SNDRV_PCM_STATE_PREPARED:	case SNDRV_PCM_STATE_RUNNING:	case SNDRV_PCM_STATE_PAUSED:		break;	case SNDRV_PCM_STATE_XRUN:		err = -EPIPE;		goto _end_unlock;	case SNDRV_PCM_STATE_SUSPENDED:		err = -ESTRPIPE;		goto _end_unlock;	default:		err = -EBADFD;		goto _end_unlock;	}	while (size > 0) {		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;		snd_pcm_uframes_t avail;		snd_pcm_uframes_t cont;		if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)			snd_pcm_update_hw_ptr(substream);		avail = snd_pcm_playback_avail(runtime);		if (((avail < runtime->control->avail_min && size > avail) ||		   (size >= runtime->xfer_align && avail < runtime->xfer_align))) {			wait_queue_t wait;			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;			long tout;			if (nonblock) {				err = -EAGAIN;				goto _end_unlock;			}			init_waitqueue_entry(&wait, current);			add_wait_queue(&runtime->sleep, &wait);			while (1) {				if (signal_pending(current)) {					state = SIGNALED;					break;				}				set_current_state(TASK_INTERRUPTIBLE);				snd_pcm_stream_unlock_irq(substream);				tout = schedule_timeout(10 * HZ);				snd_pcm_stream_lock_irq(substream);				if (tout == 0) {					if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&					    runtime->status->state != SNDRV_PCM_STATE_PAUSED) {						state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;						break;					}				}				switch (runtime->status->state) {				case SNDRV_PCM_STATE_XRUN:				case SNDRV_PCM_STATE_DRAINING:					state = ERROR;					goto _end_loop;				case SNDRV_PCM_STATE_SUSPENDED:					state = SUSPENDED;					goto _end_loop;				case SNDRV_PCM_STATE_SETUP:					state = DROPPED;					goto _end_loop;				default:					break;				}				avail = snd_pcm_playback_avail(runtime);				if (avail >= runtime->control->avail_min) {					state = READY;					break;				}			}		       _end_loop:			remove_wait_queue(&runtime->sleep, &wait);			switch (state) {			case ERROR:				err = -EPIPE;				goto _end_unlock;			case SUSPENDED:				err = -ESTRPIPE;				goto _end_unlock;			case SIGNALED:				err = -ERESTARTSYS;				goto _end_unlock;			case EXPIRED:				snd_printd("playback write error (DMA or IRQ trouble?)\n");				err = -EIO;				goto _end_unlock;			case DROPPED:				err = -EBADFD;				goto _end_unlock;			default:				break;			}		}		if (avail > runtime->xfer_align)			avail -= avail % runtime->xfer_align;		frames = size > avail ? avail : size;		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;		if (frames > cont)			frames = cont;		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);		appl_ptr = runtime->control->appl_ptr;		appl_ofs = appl_ptr % runtime->buffer_size;		snd_pcm_stream_unlock_irq(substream);		if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)			goto _end;		snd_pcm_stream_lock_irq(substream);		switch (runtime->status->state) {		case SNDRV_PCM_STATE_XRUN:			err = -EPIPE;			goto _end_unlock;		case SNDRV_PCM_STATE_SUSPENDED:			err = -ESTRPIPE;			goto _end_unlock;		default:			break;		}		appl_ptr += frames;		if (appl_ptr >= runtime->boundary)			appl_ptr -= runtime->boundary;		runtime->control->appl_ptr = appl_ptr;		if (substream->ops->ack)			substream->ops->ack(substream);		offset += frames;		size -= frames;		xfer += frames;		if (runtime->status->state == SNDRV_PCM_STATE_PREPARED &&		    snd_pcm_playback_hw_avail(runtime) >= (snd_pcm_sframes_t)runtime->start_threshold) {			err = snd_pcm_start(substream);			if (err < 0)				goto _end_unlock;		}		if (runtime->sleep_min &&		    runtime->status->state == SNDRV_PCM_STATE_RUNNING)			snd_pcm_tick_prepare(substream);	} _end_unlock:	snd_pcm_stream_unlock_irq(substream); _end:	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;}snd_pcm_sframes_t snd_pcm_lib_write(struct snd_pcm_substream *substream, const void __user *buf, snd_pcm_uframes_t size){	struct snd_pcm_runtime *runtime;	int nonblock;	snd_assert(substream != NULL, return -ENXIO);	runtime = substream->runtime;	snd_assert(runtime != NULL, return -ENXIO);	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)		return -EBADFD;	nonblock = !!(substream->f_flags & O_NONBLOCK);	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED &&	    runtime->channels > 1)		return -EINVAL;	return snd_pcm_lib_write1(substream, (unsigned long)buf, size, nonblock,				  snd_pcm_lib_write_transfer);}EXPORT_SYMBOL(snd_pcm_lib_write);static int snd_pcm_lib_writev_transfer(struct snd_pcm_substream *substream,				       unsigned int hwoff,				       unsigned long data, unsigned int off,				       snd_pcm_uframes_t frames){	struct snd_pcm_runtime *runtime = substream->runtime;	int err;	void __user **bufs = (void __user **)data;	int channels = runtime->channels;	int c;	if (substream->ops->copy) {		snd_assert(substream->ops->silence != NULL, return -EINVAL);		for (c = 0; c < channels; ++c, ++bufs) {			if (*bufs == NULL) {				if ((err = substream->ops->silence(substream, c, hwoff, frames)) < 0)					return err;			} else {				char __user *buf = *bufs + samples_to_bytes(runtime, off);				if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)					return err;			}		}	} else {		/* default transfer behaviour */		size_t dma_csize = runtime->dma_bytes / channels;		snd_assert(runtime->dma_area, return -EFAULT);		for (c = 0; c < channels; ++c, ++bufs) {			char *hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);			if (*bufs == NULL) {				snd_pcm_format_set_silence(runtime->format, hwbuf, frames);			} else {				char __user *buf = *bufs + samples_to_bytes(runtime, off);				if (copy_from_user(hwbuf, buf, samples_to_bytes(runtime, frames)))					return -EFAULT;			}		}	}	return 0;} snd_pcm_sframes_t snd_pcm_lib_writev(struct snd_pcm_substream *substream,				     void __user **bufs,				     snd_pcm_uframes_t frames){	struct snd_pcm_runtime *runtime;	int nonblock;	snd_assert(substream != NULL, return -ENXIO);	runtime = substream->runtime;	snd_assert(runtime != NULL, return -ENXIO);	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)		return -EBADFD;	nonblock = !!(substream->f_flags & O_NONBLOCK);	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)		return -EINVAL;	return snd_pcm_lib_write1(substream, (unsigned long)bufs, frames,				  nonblock, snd_pcm_lib_writev_transfer);}EXPORT_SYMBOL(snd_pcm_lib_writev);static int snd_pcm_lib_read_transfer(struct snd_pcm_substream *substream, 				     unsigned int hwoff,				     unsigned long data, unsigned int off,				     snd_pcm_uframes_t frames){	struct snd_pcm_runtime *runtime = substream->runtime;	int err;	char __user *buf = (char __user *) data + frames_to_bytes(runtime, off);	if (substream->ops->copy) {		if ((err = substream->ops->copy(substream, -1, hwoff, buf, frames)) < 0)			return err;	} else {		char *hwbuf = runtime->dma_area + frames_to_bytes(runtime, hwoff);		snd_assert(runtime->dma_area, return -EFAULT);		if (copy_to_user(buf, hwbuf, frames_to_bytes(runtime, frames)))			return -EFAULT;	}	return 0;}static snd_pcm_sframes_t snd_pcm_lib_read1(struct snd_pcm_substream *substream,					   unsigned long data,					   snd_pcm_uframes_t size,					   int nonblock,					   transfer_f transfer){	struct snd_pcm_runtime *runtime = substream->runtime;	snd_pcm_uframes_t xfer = 0;	snd_pcm_uframes_t offset = 0;	int err = 0;	if (size == 0)		return 0;	if (size > runtime->xfer_align)		size -= size % runtime->xfer_align;	snd_pcm_stream_lock_irq(substream);	switch (runtime->status->state) {	case SNDRV_PCM_STATE_PREPARED:		if (size >= runtime->start_threshold) {			err = snd_pcm_start(substream);			if (err < 0)				goto _end_unlock;		}		break;	case SNDRV_PCM_STATE_DRAINING:	case SNDRV_PCM_STATE_RUNNING:	case SNDRV_PCM_STATE_PAUSED:		break;	case SNDRV_PCM_STATE_XRUN:		err = -EPIPE;		goto _end_unlock;	case SNDRV_PCM_STATE_SUSPENDED:		err = -ESTRPIPE;		goto _end_unlock;	default:		err = -EBADFD;		goto _end_unlock;	}	while (size > 0) {		snd_pcm_uframes_t frames, appl_ptr, appl_ofs;		snd_pcm_uframes_t avail;		snd_pcm_uframes_t cont;		if (runtime->sleep_min == 0 && runtime->status->state == SNDRV_PCM_STATE_RUNNING)			snd_pcm_update_hw_ptr(substream);	      __draining:		avail = snd_pcm_capture_avail(runtime);		if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) {			if (avail < runtime->xfer_align) {				err = -EPIPE;				goto _end_unlock;			}		} else if ((avail < runtime->control->avail_min && size > avail) ||			   (size >= runtime->xfer_align && avail < runtime->xfer_align)) {			wait_queue_t wait;			enum { READY, SIGNALED, ERROR, SUSPENDED, EXPIRED, DROPPED } state;			long tout;			if (nonblock) {				err = -EAGAIN;				goto _end_unlock;			}			init_waitqueue_entry(&wait, current);			add_wait_queue(&runtime->sleep, &wait);			while (1) {				if (signal_pending(current)) {					state = SIGNALED;					break;				}				set_current_state(TASK_INTERRUPTIBLE);				snd_pcm_stream_unlock_irq(substream);				tout = schedule_timeout(10 * HZ);				snd_pcm_stream_lock_irq(substream);				if (tout == 0) {					if (runtime->status->state != SNDRV_PCM_STATE_PREPARED &&					    runtime->status->state != SNDRV_PCM_STATE_PAUSED) {						state = runtime->status->state == SNDRV_PCM_STATE_SUSPENDED ? SUSPENDED : EXPIRED;						break;					}				}				switch (runtime->status->state) {				case SNDRV_PCM_STATE_XRUN:					state = ERROR;					goto _end_loop;				case SNDRV_PCM_STATE_SUSPENDED:					state = SUSPENDED;					goto _end_loop;				case SNDRV_PCM_STATE_DRAINING:					goto __draining;				case SNDRV_PCM_STATE_SETUP:					state = DROPPED;					goto _end_loop;				default:					break;				}				avail = snd_pcm_capture_avail(runtime);				if (avail >= runtime->control->avail_min) {					state = READY;					break;				}			}		       _end_loop:			remove_wait_queue(&runtime->sleep, &wait);			switch (state) {			case ERROR:				err = -EPIPE;				goto _end_unlock;			case SUSPENDED:				err = -ESTRPIPE;				goto _end_unlock;			case SIGNALED:				err = -ERESTARTSYS;				goto _end_unlock;			case EXPIRED:				snd_printd("capture read error (DMA or IRQ trouble?)\n");				err = -EIO;				goto _end_unlock;			case DROPPED:				err = -EBADFD;				goto _end_unlock;			default:				break;			}		}		if (avail > runtime->xfer_align)			avail -= avail % runtime->xfer_align;		frames = size > avail ? avail : size;		cont = runtime->buffer_size - runtime->control->appl_ptr % runtime->buffer_size;		if (frames > cont)			frames = cont;		snd_assert(frames != 0, snd_pcm_stream_unlock_irq(substream); return -EINVAL);		appl_ptr = runtime->control->appl_ptr;		appl_ofs = appl_ptr % runtime->buffer_size;		snd_pcm_stream_unlock_irq(substream);		if ((err = transfer(substream, appl_ofs, data, offset, frames)) < 0)			goto _end;		snd_pcm_stream_lock_irq(substream);		switch (runtime->status->state) {		case SNDRV_PCM_STATE_XRUN:			err = -EPIPE;			goto _end_unlock;		case SNDRV_PCM_STATE_SUSPENDED:			err = -ESTRPIPE;			goto _end_unlock;		default:			break;		}		appl_ptr += frames;		if (appl_ptr >= runtime->boundary)			appl_ptr -= runtime->boundary;		runtime->control->appl_ptr = appl_ptr;		if (substream->ops->ack)			substream->ops->ack(substream);		offset += frames;		size -= frames;		xfer += frames;		if (runtime->sleep_min &&		    runtime->status->state == SNDRV_PCM_STATE_RUNNING)			snd_pcm_tick_prepare(substream);	} _end_unlock:	snd_pcm_stream_unlock_irq(substream); _end:	return xfer > 0 ? (snd_pcm_sframes_t)xfer : err;}snd_pcm_sframes_t snd_pcm_lib_read(struct snd_pcm_substream *substream, void __user *buf, snd_pcm_uframes_t size){	struct snd_pcm_runtime *runtime;	int nonblock;		snd_assert(substream != NULL, return -ENXIO);	runtime = substream->runtime;	snd_assert(runtime != NULL, return -ENXIO);	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)		return -EBADFD;	nonblock = !!(substream->f_flags & O_NONBLOCK);	if (runtime->access != SNDRV_PCM_ACCESS_RW_INTERLEAVED)		return -EINVAL;	return snd_pcm_lib_read1(substream, (unsigned long)buf, size, nonblock, snd_pcm_lib_read_transfer);}EXPORT_SYMBOL(snd_pcm_lib_read);static int snd_pcm_lib_readv_transfer(struct snd_pcm_substream *substream,				      unsigned int hwoff,				      unsigned long data, unsigned int off,				      snd_pcm_uframes_t frames){	struct snd_pcm_runtime *runtime = substream->runtime;	int err;	void __user **bufs = (void __user **)data;	int channels = runtime->channels;	int c;	if (substream->ops->copy) {		for (c = 0; c < channels; ++c, ++bufs) {			char __user *buf;			if (*bufs == NULL)				continue;			buf = *bufs + samples_to_bytes(runtime, off);			if ((err = substream->ops->copy(substream, c, hwoff, buf, frames)) < 0)				return err;		}	} else {		snd_pcm_uframes_t dma_csize = runtime->dma_bytes / channels;		snd_assert(runtime->dma_area, return -EFAULT);		for (c = 0; c < channels; ++c, ++bufs) {			char *hwbuf;			char __user *buf;			if (*bufs == NULL)				continue;			hwbuf = runtime->dma_area + (c * dma_csize) + samples_to_bytes(runtime, hwoff);			buf = *bufs + samples_to_bytes(runtime, off);			if (copy_to_user(buf, hwbuf, samples_to_bytes(runtime, frames)))				return -EFAULT;		}	}	return 0;} snd_pcm_sframes_t snd_pcm_lib_readv(struct snd_pcm_substream *substream,				    void __user **bufs,				    snd_pcm_uframes_t frames){	struct snd_pcm_runtime *runtime;	int nonblock;	snd_assert(substream != NULL, return -ENXIO);	runtime = substream->runtime;	snd_assert(runtime != NULL, return -ENXIO);	snd_assert(substream->ops->copy != NULL || runtime->dma_area != NULL, return -EINVAL);	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)		return -EBADFD;	nonblock = !!(substream->f_flags & O_NONBLOCK);	if (runtime->access != SNDRV_PCM_ACCESS_RW_NONINTERLEAVED)		return -EINVAL;	return snd_pcm_lib_read1(substream, (unsigned long)bufs, frames, nonblock, snd_pcm_lib_readv_transfer);}EXPORT_SYMBOL(snd_pcm_lib_readv);

⌨️ 快捷键说明

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