Commit c944a93d authored by Takashi Iwai's avatar Takashi Iwai

Merge branch 'topic/rawmidi-fix' into for-linus

parents 65b3864b 9a1b64ca
...@@ -42,7 +42,6 @@ ...@@ -42,7 +42,6 @@
#define SNDRV_RAWMIDI_LFLG_INPUT (1<<1) #define SNDRV_RAWMIDI_LFLG_INPUT (1<<1)
#define SNDRV_RAWMIDI_LFLG_OPEN (3<<0) #define SNDRV_RAWMIDI_LFLG_OPEN (3<<0)
#define SNDRV_RAWMIDI_LFLG_APPEND (1<<2) #define SNDRV_RAWMIDI_LFLG_APPEND (1<<2)
#define SNDRV_RAWMIDI_LFLG_NOOPENLOCK (1<<3)
struct snd_rawmidi; struct snd_rawmidi;
struct snd_rawmidi_substream; struct snd_rawmidi_substream;
......
...@@ -224,156 +224,143 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream) ...@@ -224,156 +224,143 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream)
return 0; return 0;
} }
int snd_rawmidi_kernel_open(struct snd_card *card, int device, int subdevice, /* look for an available substream for the given stream direction;
int mode, struct snd_rawmidi_file * rfile) * 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_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 (!(rmidi->info_flags & info_flags[stream]))
return -ENXIO;
if (subdevice >= 0 && subdevice >= s->substream_count)
return -ENODEV;
if (s->substream_opened >= s->substream_count)
return -EAGAIN;
list_for_each_entry(substream, &s->substreams, list) {
if (substream->opened) {
if (stream == SNDRV_RAWMIDI_STREAM_INPUT ||
!(mode & SNDRV_RAWMIDI_LFLG_APPEND))
continue;
}
if (subdevice < 0 || subdevice == substream->number) {
*sub_ret = substream;
return 0;
}
}
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;
}
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 *rmidi;
struct list_head *list1, *list2;
struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL; struct snd_rawmidi_substream *sinput = NULL, *soutput = NULL;
struct snd_rawmidi_runtime *input = NULL, *output = NULL;
int err; int err;
if (rfile) rfile->input = rfile->output = NULL;
rfile->input = rfile->output = NULL;
mutex_lock(&register_mutex);
rmidi = snd_rawmidi_search(card, device);
mutex_unlock(&register_mutex);
if (rmidi == NULL) {
err = -ENODEV;
goto __error1;
}
if (!try_module_get(rmidi->card->module)) {
err = -EFAULT;
goto __error1;
}
if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK))
mutex_lock(&rmidi->open_mutex);
if (mode & SNDRV_RAWMIDI_LFLG_INPUT) { if (mode & SNDRV_RAWMIDI_LFLG_INPUT) {
if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_INPUT)) { err = assign_substream(rmidi, subdevice,
err = -ENXIO; SNDRV_RAWMIDI_STREAM_INPUT,
goto __error; mode, &sinput);
} if (err < 0)
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; goto __error;
}
} }
if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
if (!(rmidi->info_flags & SNDRV_RAWMIDI_INFO_OUTPUT)) { err = assign_substream(rmidi, subdevice,
err = -ENXIO; SNDRV_RAWMIDI_STREAM_OUTPUT,
goto __error; mode, &soutput);
} if (err < 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; goto __error;
}
}
list1 = rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams.next;
while (1) {
if (list1 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substreams) {
sinput = 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 (sinput) {
if (list2 == &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substreams) { err = open_substream(rmidi, sinput, mode);
soutput = NULL; if (err < 0)
if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) {
err = -EAGAIN;
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)
goto __error; goto __error;
sinput->opened = 1;
rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT].substream_opened++;
} else {
sinput = NULL;
} }
if (mode & SNDRV_RAWMIDI_LFLG_OUTPUT) { if (soutput) {
if (soutput->opened) err = open_substream(rmidi, soutput, mode);
goto __skip_output; if (err < 0) {
if ((err = snd_rawmidi_runtime_create(soutput)) < 0) { if (sinput)
if (mode & SNDRV_RAWMIDI_LFLG_INPUT) close_substream(rmidi, sinput, 0);
sinput->ops->close(sinput);
goto __error;
}
output = soutput->runtime;
if ((err = soutput->ops->open(soutput)) < 0) {
if (mode & SNDRV_RAWMIDI_LFLG_INPUT)
sinput->ops->close(sinput);
goto __error; 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;
} }
rfile->rmidi = rmidi;
rfile->input = sinput;
rfile->output = soutput;
return 0; return 0;
__error: __error:
if (input != NULL) if (sinput && sinput->runtime)
snd_rawmidi_runtime_free(sinput); snd_rawmidi_runtime_free(sinput);
if (output != NULL) if (soutput && soutput->runtime)
snd_rawmidi_runtime_free(soutput); snd_rawmidi_runtime_free(soutput);
module_put(rmidi->card->module); return err;
if (!(mode & SNDRV_RAWMIDI_LFLG_NOOPENLOCK)) }
mutex_unlock(&rmidi->open_mutex);
__error1: /* 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; return err;
} }
...@@ -385,10 +372,13 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) ...@@ -385,10 +372,13 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
unsigned short fflags; unsigned short fflags;
int err; int err;
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
struct snd_rawmidi_file *rawmidi_file; struct snd_rawmidi_file *rawmidi_file = NULL;
wait_queue_t wait; wait_queue_t wait;
struct snd_ctl_file *kctl; struct snd_ctl_file *kctl;
if ((file->f_flags & O_APPEND) && !(file->f_flags & O_NONBLOCK))
return -EINVAL; /* invalid combination */
if (maj == snd_major) { if (maj == snd_major) {
rmidi = snd_lookup_minor_data(iminor(inode), rmidi = snd_lookup_minor_data(iminor(inode),
SNDRV_DEVICE_TYPE_RAWMIDI); SNDRV_DEVICE_TYPE_RAWMIDI);
...@@ -402,24 +392,25 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) ...@@ -402,24 +392,25 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
if (rmidi == NULL) if (rmidi == NULL)
return -ENODEV; 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; card = rmidi->card;
err = snd_card_file_add(card, file); err = snd_card_file_add(card, file);
if (err < 0) if (err < 0)
return -ENODEV; goto __error_card;
fflags = snd_rawmidi_file_flags(file); fflags = snd_rawmidi_file_flags(file);
if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */ if ((file->f_flags & O_APPEND) || maj == SOUND_MAJOR) /* OSS emul? */
fflags |= SNDRV_RAWMIDI_LFLG_APPEND; fflags |= SNDRV_RAWMIDI_LFLG_APPEND;
fflags |= SNDRV_RAWMIDI_LFLG_NOOPENLOCK;
rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL); rawmidi_file = kmalloc(sizeof(*rawmidi_file), GFP_KERNEL);
if (rawmidi_file == NULL) { if (rawmidi_file == NULL) {
snd_card_file_remove(card, file); err = -ENOMEM;
return -ENOMEM; goto __error;
} }
init_waitqueue_entry(&wait, current); init_waitqueue_entry(&wait, current);
add_wait_queue(&rmidi->open_wait, &wait); add_wait_queue(&rmidi->open_wait, &wait);
mutex_lock(&rmidi->open_mutex);
while (1) { while (1) {
subdevice = -1; subdevice = -1;
read_lock(&card->ctl_files_rwlock); read_lock(&card->ctl_files_rwlock);
...@@ -431,8 +422,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) ...@@ -431,8 +422,7 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
} }
} }
read_unlock(&card->ctl_files_rwlock); read_unlock(&card->ctl_files_rwlock);
err = snd_rawmidi_kernel_open(rmidi->card, rmidi->device, err = rawmidi_open_priv(rmidi, subdevice, fflags, rawmidi_file);
subdevice, fflags, rawmidi_file);
if (err >= 0) if (err >= 0)
break; break;
if (err == -EAGAIN) { if (err == -EAGAIN) {
...@@ -451,67 +441,89 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file) ...@@ -451,67 +441,89 @@ static int snd_rawmidi_open(struct inode *inode, struct file *file)
break; break;
} }
} }
remove_wait_queue(&rmidi->open_wait, &wait);
if (err < 0) {
kfree(rawmidi_file);
goto __error;
}
#ifdef CONFIG_SND_OSSEMUL #ifdef CONFIG_SND_OSSEMUL
if (rawmidi_file->input && rawmidi_file->input->runtime) if (rawmidi_file->input && rawmidi_file->input->runtime)
rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR); rawmidi_file->input->runtime->oss = (maj == SOUND_MAJOR);
if (rawmidi_file->output && rawmidi_file->output->runtime) if (rawmidi_file->output && rawmidi_file->output->runtime)
rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR); rawmidi_file->output->runtime->oss = (maj == SOUND_MAJOR);
#endif #endif
remove_wait_queue(&rmidi->open_wait, &wait); file->private_data = rawmidi_file;
if (err >= 0) { mutex_unlock(&rmidi->open_mutex);
file->private_data = rawmidi_file; return 0;
} else {
snd_card_file_remove(card, file); __error:
kfree(rawmidi_file); snd_card_file_remove(card, file);
} __error_card:
mutex_unlock(&rmidi->open_mutex); mutex_unlock(&rmidi->open_mutex);
module_put(rmidi->card->module);
return err; 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; rmidi->streams[substream->stream].substream_opened--;
struct snd_rawmidi_substream *substream; if (--substream->use_count)
struct snd_rawmidi_runtime *runtime; return;
if (snd_BUG_ON(!rfile)) if (cleanup) {
return -ENXIO; if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
rmidi = rfile->rmidi; snd_rawmidi_input_trigger(substream, 0);
mutex_lock(&rmidi->open_mutex); else {
if (rfile->input != NULL) {
substream = rfile->input;
rfile->input = NULL;
runtime = substream->runtime;
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;
if (substream->active_sensing) { if (substream->active_sensing) {
unsigned char buf = 0xfe; 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); snd_rawmidi_kernel_write(substream, &buf, 1);
} }
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS) if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
snd_rawmidi_output_trigger(substream, 0); snd_rawmidi_output_trigger(substream, 0);
substream->ops->close(substream);
if (runtime->private_free != NULL)
runtime->private_free(substream);
snd_rawmidi_runtime_free(substream);
substream->opened = 0;
substream->append = 0;
} }
rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT].substream_opened--;
} }
substream->ops->close(substream);
if (substream->runtime->private_free)
substream->runtime->private_free(substream);
snd_rawmidi_runtime_free(substream);
substream->opened = 0;
substream->append = 0;
}
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); 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); module_put(rmidi->card->module);
return 0; return 0;
} }
...@@ -520,15 +532,14 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file) ...@@ -520,15 +532,14 @@ static int snd_rawmidi_release(struct inode *inode, struct file *file)
{ {
struct snd_rawmidi_file *rfile; struct snd_rawmidi_file *rfile;
struct snd_rawmidi *rmidi; struct snd_rawmidi *rmidi;
int err;
rfile = file->private_data; rfile = file->private_data;
err = snd_rawmidi_kernel_release(rfile);
rmidi = rfile->rmidi; rmidi = rfile->rmidi;
wake_up(&rmidi->open_wait); rawmidi_release_priv(rfile);
kfree(rfile); kfree(rfile);
snd_card_file_remove(rmidi->card, file); 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, static int snd_rawmidi_info(struct snd_rawmidi_substream *substream,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment