if (result < 0) goto _unlock; 
if (result < 0) { snd_power_unlock(card); return result; } 
static int snd_pcm_drain(snd_pcm_substream_t *substream){	snd_card_t *card;	snd_pcm_runtime_t *runtime;	struct list_head *pos;	int result = 0;	int i, num_drecs;	struct drain_rec *drec, drec_tmp, *d;	snd_assert(substream != NULL, return -ENXIO);	card = substream->pcm->card;	runtime = substream->runtime;	if (runtime->status->state == SNDRV_PCM_STATE_OPEN)		return -EBADFD;	down_read(&snd_pcm_link_rwsem);	snd_power_lock(card);	if (runtime->status->state == SNDRV_PCM_STATE_SUSPENDED) {		result = snd_power_wait(card, SNDRV_CTL_POWER_D0, substream->ffile);		if (result < 0)			goto _unlock;	}	/* allocate temporary record for drain sync */	if (snd_pcm_stream_linked(substream)) {		drec = kmalloc(substream->group->count * sizeof(*drec), GFP_KERNEL);		if (! drec) {			result = -ENOMEM;			goto _unlock;		}	} else		drec = &drec_tmp;	snd_pcm_stream_lock_irq(substream);	/* resume pause */	if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)		snd_pcm_pause(substream, 0);	/* pre-start/stop - all running streams are changed to DRAINING state */	result = snd_pcm_action(&snd_pcm_action_drain_init, substream, 0);	if (result < 0)		goto _end;	/* check streams with PLAYBACK & DRAINING */	num_drecs = 0;	snd_pcm_group_for_each(pos, substream) {		snd_pcm_substream_t *s = snd_pcm_group_substream_entry(pos);		runtime = s->runtime;		if (runtime->status->state != SNDRV_PCM_STATE_DRAINING) {			runtime->status->state = SNDRV_PCM_STATE_SETUP;			continue;		}		if (s->stream == SNDRV_PCM_STREAM_PLAYBACK) {			d = &drec[num_drecs++];			d->substream = s;			init_waitqueue_entry(&d->wait, current);			add_wait_queue(&runtime->sleep, &d->wait);			/* stop_threshold fixup to avoid endless loop when			 * stop_threshold > buffer_size			 */			d->stop_threshold = runtime->stop_threshold;			if (runtime->stop_threshold > runtime->buffer_size)				runtime->stop_threshold = runtime->buffer_size;		}	}	if (! num_drecs)		goto _end;	for (;;) {		long tout;		if (signal_pending(current)) {			result = -ERESTARTSYS;			break;		}		set_current_state(TASK_INTERRUPTIBLE);		snd_pcm_stream_unlock_irq(substream);		snd_power_unlock(card);		tout = schedule_timeout(10 * HZ);		snd_power_lock(card);		snd_pcm_stream_lock_irq(substream);		if (tout == 0) {			if (substream->runtime->status->state == SNDRV_PCM_STATE_SUSPENDED)				result = -ESTRPIPE;			else {				snd_printd("playback drain error (DMA or IRQ trouble?)\n");				snd_pcm_stop(substream, SNDRV_PCM_STATE_SETUP);				result = -EIO;			}			break;		}		/* all finished? */		for (i = 0; i < num_drecs; i++) {			runtime = drec[i].substream->runtime;			if (runtime->status->state == SNDRV_PCM_STATE_DRAINING)				break;		}		if (i == num_drecs)			break;	}	for (i = 0; i < num_drecs; i++) {		d = &drec[i];		runtime = d->substream->runtime;		remove_wait_queue(&runtime->sleep, &d->wait);		runtime->stop_threshold = d->stop_threshold;	} _end:	snd_pcm_stream_unlock_irq(substream);	if (drec != &drec_tmp)		kfree(drec); _unlock:	snd_power_unlock(card);	up_read(&snd_pcm_link_rwsem);	return result;}
4520	/local/tlutelli/issta_data/temp/c/2005_temp/2005/4520/21cb2a2ec5818cbba01bcb7f24388670322c77f9/pcm_native.c/clean/sound/core/pcm_native.c