Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9a1b64ca authored by Takashi Iwai's avatar Takashi Iwai
Browse files

ALSA: rawmidi - Refactor rawmidi open/close codes



Refactor rawmidi open/close code messes.

Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent f9d20283
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -42,7 +42,6 @@
#define SNDRV_RAWMIDI_LFLG_INPUT	(1<<1)
#define SNDRV_RAWMIDI_LFLG_OPEN		(3<<0)
#define SNDRV_RAWMIDI_LFLG_APPEND	(1<<2)
#define	SNDRV_RAWMIDI_LFLG_NOOPENLOCK	(1<<3)

struct snd_rawmidi;
struct snd_rawmidi_substream;
+194 −183
Original line number Diff line number Diff line
@@ -224,155 +224,142 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
	return 0;
}

int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
			    int mode, struct snd_rawmidi_file * rfile)
/* look for an available substream for the given stream direction;
 * if a specific subdevice is given, try to assign it
 */
static int assign_substream(struct snd_rawmidi *rmidi, int subdevice,
			    int stream, int mode,
			    struct snd_rawmidi_substream **sub_ret)
{
	struct snd_rawmidi *rmidi;
	struct list_head *list1, *list2;
	struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
	struct snd_rawmidi_runtime *input = NULL, *output = NULL;
	int err;
	struct snd_rawmidi_substream *substream;
	struct snd_rawmidi_str *s = &rmidi->streams[stream];
	static unsigned int info_flags[2] = {
		[SNDRV_RAWMIDI_STREAM_OUTPUT] = SNDRV_RAWMIDI_INFO_OUTPUT,
		[SNDRV_RAWMIDI_STREAM_INPUT] = SNDRV_RAWMIDI_INFO_INPUT,
	};

	if (rfile)
		rfile->input = rfile->output = NULL;
	mutex_lock(&register_mutex);
	rmidi = snd_rawmidi_search(card, device);
	if (rmidi == NULL) {
		mutex_unlock(&register_mutex);
		return -ENODEV;
	}
	if (!try_module_get(rmidi->card->module)) {
		mutex_unlock(&register_mutex);
	if (!(rmidi->info_flags & info_flags[stream]))
		return -ENXIO;
	}
	mutex_unlock(&register_mutex);
	if (subdevice >= 0 && subdevice >= s->substream_count)
		return -ENODEV;
	if (s->substream_opened >= s->substream_count)
		return -EAGAIN;

	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
		mutex_lock(&rmidi->open_mutex);
	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
		if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) {
			err = -ENXIO;
			goto __error;
		}
		if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
			err = -ENODEV;
			goto __error;
		}
		if (rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened >=
		    rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_count) {
			err = -EAGAIN;
			goto __error;
		}
	list_for_each_entry(substream, &s->substreams, list) {
		if (substream->opened) {
			if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
			    !(mode & SNDRV_RAWMIDI_LFLG_APPEND))
				continue;
		}
	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
		if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) {
			err = -ENXIO;
			goto __error;
		if (subdevice < 0 || subdevice == substream->number) {
			*sub_ret = substream;
			return 0;
		}
		if (subdevice >= 0 && (unsigned int)subdevice >= rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
			err = -ENODEV;
			goto __error;
	}
		if (rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened >=
		    rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_count) {
			err = -EAGAIN;
			goto __error;
	return -EAGAIN;
}

/* open and do ref-counting for the given substream */
static int open_substream(struct snd_rawmidi *rmidi,
			  struct snd_rawmidi_substream *substream,
			  int mode)
{
	int err;

	err = snd_rawmidi_runtime_create(substream);
	if (err < 0)
		return err;
	err = substream->ops->open(substream);
	if (err < 0)
		return err;
	substream->opened = 1;
	if (substream->use_count++ == 0)
		substream->active_sensing = 1;
	if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
		substream->append = 1;
	rmidi->streams[substream->stream].substream_opened++;
	return 0;
}
	list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;
	while (1) {
		if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
			sinput = NULL;

static void close_substream(struct snd_rawmidi *rmidi,
			    struct snd_rawmidi_substream *substream,
			    int cleanup);

static int rawmidi_open_priv(struct snd_rawmidi *rmidi, int subdevice, int mode,
			     struct snd_rawmidi_file *rfile)
{
	struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
	int err;

	rfile->input = rfile->output = NULL;
	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
				err = -EAGAIN;
				goto __error;
			}
			break;
		}
		sinput = list_entry(list1, struct snd_rawmidi_substream, list);
		if ((mode & SNDRV_RAWMIDI_LFLG_INPUT) && sinput->opened)
			goto __nexti;
		if (subdevice < 0 || (subdevice >= 0 && subdevice == sinput->number))
			break;
	      __nexti:
		list1 = list1->next;
	}
	list2 = rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams.next;
	while (1) {
		if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) {
			soutput = NULL;
			if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
				err = -EAGAIN;
		err = assign_substream(rmidi, subdevice,
				       SNDRV_RAWMIDI_STREAM_INPUT,
				       mode, &sinput);
		if (err < 0)
			goto __error;
	}
			break;
		}
		soutput = list_entry(list2, struct snd_rawmidi_substream, list);
	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
			if (mode & SNDRV_RAWMIDI_LFLG_APPEND) {
				if (soutput->opened && !soutput->append)
					goto __nexto;
			} else {
				if (soutput->opened)
					goto __nexto;
			}
		}
		if (subdevice < 0 || (subdevice >= 0 && subdevice == soutput->number))
			break;
	      __nexto:
		list2 = list2->next;
	}
	if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
		if ((err = snd_rawmidi_runtime_create(sinput)) < 0)
			goto __error;
		input = sinput->runtime;
		if ((err = sinput->ops->open(sinput)) < 0)
		err = assign_substream(rmidi, subdevice,
				       SNDRV_RAWMIDI_STREAM_OUTPUT,
				       mode, &soutput);
		if (err < 0)
			goto __error;
		sinput->opened = 1;
		rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;
	} else {
		sinput = NULL;
	}
	if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
		if (soutput->opened)
			goto __skip_output;
		if ((err = snd_rawmidi_runtime_create(soutput)) < 0) {
			if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
				sinput->ops->close(sinput);

	if (sinput) {
		err = open_substream(rmidi, sinput, mode);
		if (err < 0)
			goto __error;
	}
		output = soutput->runtime;
		if ((err = soutput->ops->open(soutput)) < 0) {
			if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
				sinput->ops->close(sinput);
	if (soutput) {
		err = open_substream(rmidi, soutput, mode);
		if (err < 0) {
			if (sinput)
				close_substream(rmidi, sinput, 0);
			goto __error;
		}
	      __skip_output:
		soutput->opened = 1;
		if (mode & SNDRV_RAWMIDI_LFLG_APPEND)
			soutput->append = 1;
	      	if (soutput->use_count++ == 0)
			soutput->active_sensing = 1;
		rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened++;
	} else {
		soutput = NULL;
	}
	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
		mutex_unlock(&rmidi->open_mutex);
	if (rfile) {

	rfile->rmidi = rmidi;
	rfile->input = sinput;
	rfile->output = soutput;
	}
	return 0;

      __error:
	if (input != NULL)
	if (sinput && sinput->runtime)
		snd_rawmidi_runtime_free(sinput);
	if (output != NULL)
	if (soutput && soutput->runtime)
		snd_rawmidi_runtime_free(soutput);
	if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
	return err;
}

/* called from sound/core/seq/seq_midi.c */
int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice,
			    int mode, struct snd_rawmidi_file * rfile)
{
	struct snd_rawmidi *rmidi;
	int err;

	if (snd_BUG_ON(!rfile))
		return -EINVAL;

	mutex_lock(&register_mutex);
	rmidi = snd_rawmidi_search(card, device);
	if (rmidi == NULL) {
		mutex_unlock(&register_mutex);
		return -ENODEV;
	}
	if (!try_module_get(rmidi->card->module)) {
		mutex_unlock(&register_mutex);
		return -ENXIO;
	}
	mutex_unlock(&register_mutex);

	mutex_lock(&rmidi->open_mutex);
	err = rawmidi_open_priv(rmidi, subdevice, mode, rfile);
	mutex_unlock(&rmidi->open_mutex);
	if (err < 0)
		module_put(rmidi->card->module);
	return err;
}
@@ -385,10 +372,13 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
	unsigned short fflags;
	int err;
	struct snd_rawmidi *rmidi;
	struct snd_rawmidi_file *rawmidi_file;
	struct snd_rawmidi_file *rawmidi_file = NULL;
	wait_queue_t wait;
	struct snd_ctl_file *kctl;

	if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) 
		return -EINVAL;		/* invalid combination */

	if (maj == snd_major) {
		rmidi = snd_lookup_minor_data(iminor(inode),
					      SNDRV_DEVICE_TYPE_RAWMIDI);
@@ -402,24 +392,25 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)

	if (rmidi == NULL)
		return -ENODEV;
	if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK)) 
		return -EINVAL;		/* invalid combination */

	if (!try_module_get(rmidi->card->module))
		return -ENXIO;

	mutex_lock(&rmidi->open_mutex);
	card = rmidi->card;
	err = snd_card_file_add(card, file);
	if (err < 0)
		return -ENODEV;
		goto __error_card;
	fflags = snd_rawmidi_file_flags(file);
	if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */
		fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
	fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK;
	rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
	if (rawmidi_file == NULL) {
		snd_card_file_remove(card, file);
		return -ENOMEM;
		err = -ENOMEM;
		goto __error;
	}
	init_waitqueue_entry(&wait, current);
	add_wait_queue(&rmidi->open_wait, &wait);
	mutex_lock(&rmidi->open_mutex);
	while (1) {
		subdevice = -1;
		read_lock(&card->ctl_files_rwlock);
@@ -431,8 +422,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
			}
		}
		read_unlock(&card->ctl_files_rwlock);
		err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device,
					      subdevice, fflags, rawmidi_file);
		err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
		if (err >= 0)
			break;
		if (err == -EAGAIN) {
@@ -451,67 +441,89 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
			break;
		}
	}
	remove_wait_queue(&rmidi->open_wait, &wait);
	if (err < 0) {
		kfree(rawmidi_file);
		goto __error;
	}
#ifdef CONFIG_SND_OSSEMUL
	if (rawmidi_file->input && rawmidi_file->input->runtime)
		rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
	if (rawmidi_file->output && rawmidi_file->output->runtime)
		rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
#endif
	remove_wait_queue(&rmidi->open_wait, &wait);
	if (err >= 0) {
	file->private_data = rawmidi_file;
	} else {
	mutex_unlock(&rmidi->open_mutex);
	return 0;

 __error:
	snd_card_file_remove(card, file);
		kfree(rawmidi_file);
	}
 __error_card:
	mutex_unlock(&rmidi->open_mutex);
	module_put(rmidi->card->module);
	return err;
}

int snd_rawmidi_kernel_release(struct snd_rawmidi_file * rfile)
static void close_substream(struct snd_rawmidi *rmidi,
			    struct snd_rawmidi_substream *substream,
			    int cleanup)
{
	struct snd_rawmidi *rmidi;
	struct snd_rawmidi_substream *substream;
	struct snd_rawmidi_runtime *runtime;
	rmidi->streams[substream->stream].substream_opened--;
	if (--substream->use_count)
		return;

	if (snd_BUG_ON(!rfile))
		return -ENXIO;
	rmidi = rfile->rmidi;
	mutex_lock(&rmidi->open_mutex);
	if (rfile->input != NULL) {
		substream = rfile->input;
		rfile->input = NULL;
		runtime = substream->runtime;
	if (cleanup) {
		if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
			snd_rawmidi_input_trigger(substream, 0);
		substream->ops->close(substream);
		if (runtime->private_free != NULL)
			runtime->private_free(substream);
		snd_rawmidi_runtime_free(substream);
		substream->opened = 0;
		rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened--;
	}
	if (rfile->output != NULL) {
		substream = rfile->output;
		rfile->output = NULL;
		if (--substream->use_count == 0) {
			runtime = substream->runtime;
		else {
			if (substream->active_sensing) {
				unsigned char buf = 0xfe;
				/* sending single active sensing message to shut the device up */
				/* sending single active sensing message
				 * to shut the device up
				 */
				snd_rawmidi_kernel_write(substream, &buf, 1);
			}
			if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
				snd_rawmidi_output_trigger(substream, 0);
		}
	}
	substream->ops->close(substream);
			if (runtime->private_free != NULL)
				runtime->private_free(substream);
	if (substream->runtime->private_free)
		substream->runtime->private_free(substream);
	snd_rawmidi_runtime_free(substream);
	substream->opened = 0;
	substream->append = 0;
}
		rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--;

static void rawmidi_release_priv(struct snd_rawmidi_file *rfile)
{
	struct snd_rawmidi *rmidi;

	rmidi = rfile->rmidi;
	mutex_lock(&rmidi->open_mutex);
	if (rfile->input) {
		close_substream(rmidi, rfile->input, 1);
		rfile->input = NULL;
	}
	if (rfile->output) {
		close_substream(rmidi, rfile->output, 1);
		rfile->output = NULL;
	}
	rfile->rmidi = NULL;
	mutex_unlock(&rmidi->open_mutex);
	wake_up(&rmidi->open_wait);
}

/* called from sound/core/seq/seq_midi.c */
int snd_rawmidi_kernel_release(struct snd_rawmidi_file *rfile)
{
	struct snd_rawmidi *rmidi;

	if (snd_BUG_ON(!rfile))
		return -ENXIO;
	
	rmidi = rfile->rmidi;
	rawmidi_release_priv(rfile);
	module_put(rmidi->card->module);
	return 0;
}
@@ -520,15 +532,14 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file)
{
	struct snd_rawmidi_file *rfile;
	struct snd_rawmidi *rmidi;
	int err;

	rfile = file->private_data;
	err = snd_rawmidi_kernel_release(rfile);
	rmidi = rfile->rmidi;
	wake_up(&rmidi->open_wait);
	rawmidi_release_priv(rfile);
	kfree(rfile);
	snd_card_file_remove(rmidi->card, file);
	return err;
	module_put(rmidi->card->module);
	return 0;
}

static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,