Commit 91a700ed authored by Jaroslav Kysela's avatar Jaroslav Kysela

[ALSA] move rawmidi event callback into tasklet

RawMidi Midlevel
Move the event callback into a tasklet instead of calling it directly
from snd_rawmidi_transmit_ack/_receive to prevent recursive calls to
the trigger callback.  This means that drivers no longer have to
check that they're called inside their own spinlock.
Signed-off-by: default avatarClemens Ladisch <clemens@ladisch.de>
parent e575374c
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
*/ */
#include <sound/asound.h> #include <sound/asound.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <asm/semaphore.h> #include <asm/semaphore.h>
...@@ -79,6 +80,7 @@ struct _snd_rawmidi_runtime { ...@@ -79,6 +80,7 @@ struct _snd_rawmidi_runtime {
spinlock_t lock; spinlock_t lock;
wait_queue_head_t sleep; wait_queue_head_t sleep;
/* event handler (room [output] or new bytes [input]) */ /* event handler (room [output] or new bytes [input]) */
struct tasklet_struct event_tasklet;
void (*event)(snd_rawmidi_substream_t *substream); void (*event)(snd_rawmidi_substream_t *substream);
/* private data */ /* private data */
void *private_data; void *private_data;
......
...@@ -85,6 +85,12 @@ static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream, ...@@ -85,6 +85,12 @@ static inline int snd_rawmidi_ready_append(snd_rawmidi_substream_t * substream,
(!substream->append || runtime->avail >= count); (!substream->append || runtime->avail >= count);
} }
static void snd_rawmidi_event_tasklet(unsigned long data)
{
snd_rawmidi_substream_t *substream = (snd_rawmidi_substream_t *)data;
substream->runtime->event(substream);
}
static int snd_rawmidi_runtime_create(snd_rawmidi_substream_t * substream) static int snd_rawmidi_runtime_create(snd_rawmidi_substream_t * substream)
{ {
snd_rawmidi_runtime_t *runtime; snd_rawmidi_runtime_t *runtime;
...@@ -93,6 +99,8 @@ static int snd_rawmidi_runtime_create(snd_rawmidi_substream_t * substream) ...@@ -93,6 +99,8 @@ static int snd_rawmidi_runtime_create(snd_rawmidi_substream_t * substream)
return -ENOMEM; return -ENOMEM;
spin_lock_init(&runtime->lock); spin_lock_init(&runtime->lock);
init_waitqueue_head(&runtime->sleep); init_waitqueue_head(&runtime->sleep);
tasklet_init(&runtime->event_tasklet, snd_rawmidi_event_tasklet,
(unsigned long)substream);
runtime->event = NULL; runtime->event = NULL;
runtime->buffer_size = PAGE_SIZE; runtime->buffer_size = PAGE_SIZE;
runtime->avail_min = 1; runtime->avail_min = 1;
...@@ -119,11 +127,18 @@ static int snd_rawmidi_runtime_free(snd_rawmidi_substream_t * substream) ...@@ -119,11 +127,18 @@ static int snd_rawmidi_runtime_free(snd_rawmidi_substream_t * substream)
return 0; return 0;
} }
static void snd_rawmidi_trigger(snd_rawmidi_substream_t * substream, int up)
{
substream->ops->trigger(substream, up);
if (!up && substream->runtime->event)
tasklet_kill(&substream->runtime->event_tasklet);
}
int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream) int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream)
{ {
snd_rawmidi_runtime_t *runtime = substream->runtime; snd_rawmidi_runtime_t *runtime = substream->runtime;
substream->ops->trigger(substream, 0); snd_rawmidi_trigger(substream, 0);
runtime->drain = 0; runtime->drain = 0;
/* interrupts are not enabled at this moment, /* interrupts are not enabled at this moment,
so spinlock is not required */ so spinlock is not required */
...@@ -165,7 +180,7 @@ int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream) ...@@ -165,7 +180,7 @@ int snd_rawmidi_drain_input(snd_rawmidi_substream_t * substream)
{ {
snd_rawmidi_runtime_t *runtime = substream->runtime; snd_rawmidi_runtime_t *runtime = substream->runtime;
substream->ops->trigger(substream, 0); snd_rawmidi_trigger(substream, 0);
runtime->drain = 0; runtime->drain = 0;
/* interrupts aren't enabled at this moment, so spinlock isn't needed */ /* interrupts aren't enabled at this moment, so spinlock isn't needed */
runtime->appl_ptr = runtime->hw_ptr = 0; runtime->appl_ptr = runtime->hw_ptr = 0;
...@@ -443,7 +458,7 @@ int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile) ...@@ -443,7 +458,7 @@ int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile)
substream = rfile->input; substream = rfile->input;
rfile->input = NULL; rfile->input = NULL;
runtime = substream->runtime; runtime = substream->runtime;
substream->ops->trigger(substream, 0); snd_rawmidi_trigger(substream, 0);
substream->ops->close(substream); substream->ops->close(substream);
if (runtime->private_free != NULL) if (runtime->private_free != NULL)
runtime->private_free(substream); runtime->private_free(substream);
...@@ -462,7 +477,7 @@ int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile) ...@@ -462,7 +477,7 @@ int snd_rawmidi_kernel_release(snd_rawmidi_file_t * rfile)
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)
substream->ops->trigger(substream, 0); snd_rawmidi_trigger(substream, 0);
substream->ops->close(substream); substream->ops->close(substream);
if (runtime->private_free != NULL) if (runtime->private_free != NULL)
runtime->private_free(substream); runtime->private_free(substream);
...@@ -858,13 +873,13 @@ int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char ...@@ -858,13 +873,13 @@ int snd_rawmidi_receive(snd_rawmidi_substream_t * substream, const unsigned char
} }
} }
} }
if (result > 0 && runtime->event == NULL) { if (result > 0) {
if (snd_rawmidi_ready(substream)) if (runtime->event)
tasklet_hi_schedule(&runtime->event_tasklet);
else if (snd_rawmidi_ready(substream))
wake_up(&runtime->sleep); wake_up(&runtime->sleep);
} }
spin_unlock_irqrestore(&runtime->lock, flags); spin_unlock_irqrestore(&runtime->lock, flags);
if (result > 0 && runtime->event)
runtime->event(substream);
return result; return result;
} }
...@@ -904,7 +919,7 @@ static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream, ...@@ -904,7 +919,7 @@ static long snd_rawmidi_kernel_read1(snd_rawmidi_substream_t *substream,
long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count) long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char *buf, long count)
{ {
substream->ops->trigger(substream, 1); snd_rawmidi_trigger(substream, 1);
return snd_rawmidi_kernel_read1(substream, buf, count, 1); return snd_rawmidi_kernel_read1(substream, buf, count, 1);
} }
...@@ -921,7 +936,7 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun ...@@ -921,7 +936,7 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun
if (substream == NULL) if (substream == NULL)
return -EIO; return -EIO;
runtime = substream->runtime; runtime = substream->runtime;
substream->ops->trigger(substream, 1); snd_rawmidi_trigger(substream, 1);
result = 0; result = 0;
while (count > 0) { while (count > 0) {
spin_lock_irq(&runtime->lock); spin_lock_irq(&runtime->lock);
...@@ -1056,15 +1071,14 @@ int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count) ...@@ -1056,15 +1071,14 @@ int snd_rawmidi_transmit_ack(snd_rawmidi_substream_t * substream, int count)
runtime->hw_ptr %= runtime->buffer_size; runtime->hw_ptr %= runtime->buffer_size;
runtime->avail += count; runtime->avail += count;
substream->bytes += count; substream->bytes += count;
if (runtime->drain) if (count > 0) {
wake_up(&runtime->sleep); if (runtime->drain ||
else (runtime->event == NULL && snd_rawmidi_ready(substream)))
if (count > 0 && runtime->event == NULL)
if (snd_rawmidi_ready(substream))
wake_up(&runtime->sleep); wake_up(&runtime->sleep);
if (runtime->event)
tasklet_hi_schedule(&runtime->event_tasklet);
}
spin_unlock_irqrestore(&runtime->lock, flags); spin_unlock_irqrestore(&runtime->lock, flags);
if (count > 0 && runtime->event)
runtime->event(substream);
return count; return count;
} }
...@@ -1132,7 +1146,7 @@ static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const ...@@ -1132,7 +1146,7 @@ static long snd_rawmidi_kernel_write1(snd_rawmidi_substream_t * substream, const
count1 = runtime->avail < runtime->buffer_size; count1 = runtime->avail < runtime->buffer_size;
spin_unlock_irqrestore(&runtime->lock, flags); spin_unlock_irqrestore(&runtime->lock, flags);
if (count1) if (count1)
substream->ops->trigger(substream, 1); snd_rawmidi_trigger(substream, 1);
return result; return result;
} }
...@@ -1217,7 +1231,7 @@ static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait) ...@@ -1217,7 +1231,7 @@ static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait)
rfile = file->private_data; rfile = file->private_data;
if (rfile->input != NULL) { if (rfile->input != NULL) {
runtime = rfile->input->runtime; runtime = rfile->input->runtime;
rfile->input->ops->trigger(rfile->input, 1); snd_rawmidi_trigger(rfile->input, 1);
poll_wait(file, &runtime->sleep, wait); poll_wait(file, &runtime->sleep, wait);
} }
if (rfile->output != NULL) { if (rfile->output != NULL) {
......
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