ChangeSet 1.2181.25.27, 2005/03/22 09:12:31+01:00, [EMAIL PROTECTED]
[ALSA] rawmidi - move output trigger into a tasklet
Documentation,RawMidi Midlevel
Calling the output trigger callback from another interrupt handler
can lead to unintuitive locking requirements (i.e., spin_lock_irqsave)
in the sound card interrupt handler. Moving the call to the callback
into a tasklet cures this, and has the added benefit that the callback
is called only once if more that one sequencer event has been
delivered in one timer interrupt tick.
Signed-off-by: Clemens Ladisch <[EMAIL PROTECTED]>
Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl | 9 -
include/sound/rawmidi.h | 5 -
sound/core/rawmidi.c | 55 +++++++----
3 files changed, 40 insertions(+), 29 deletions(-)
diff -Nru a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
--- a/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
2005-03-30 16:14:56 -08:00
+++ b/Documentation/sound/alsa/DocBook/writing-an-alsa-driver.tmpl
2005-03-30 16:14:56 -08:00
@@ -4604,15 +4604,6 @@
zero <parameter>up</parameter> parameter when the transmission
of data should be aborted.
</para>
-
- <para>
- The <function>trigger</function> callback with nonzero
- <paramater>up</parameter> parameter may be called from another
- hardware interrupt handler. This means that all spinlocks
- taken in the <function>trigger</function> callback must be
- taken with <function>spin_lock_irqsave</function> even when
- you are in your own interrupt handler.
- </para>
</section>
<section id="rawmidi-interface-op-trigger-in">
diff -Nru a/include/sound/rawmidi.h b/include/sound/rawmidi.h
--- a/include/sound/rawmidi.h 2005-03-30 16:14:56 -08:00
+++ b/include/sound/rawmidi.h 2005-03-30 16:14:56 -08:00
@@ -79,9 +79,10 @@
/* misc */
spinlock_t lock;
wait_queue_head_t sleep;
- /* event handler (room [output] or new bytes [input]) */
- struct tasklet_struct event_tasklet;
+ /* event handler (new bytes, input only) */
void (*event)(snd_rawmidi_substream_t *substream);
+ /* defers calls to event [input] or ops->trigger [output] */
+ struct tasklet_struct tasklet;
/* private data */
void *private_data;
void (*private_free)(snd_rawmidi_substream_t *substream);
diff -Nru a/sound/core/rawmidi.c b/sound/core/rawmidi.c
--- a/sound/core/rawmidi.c 2005-03-30 16:14:56 -08:00
+++ b/sound/core/rawmidi.c 2005-03-30 16:14:56 -08:00
@@ -85,12 +85,18 @@
(!substream->append || runtime->avail >= count);
}
-static void snd_rawmidi_event_tasklet(unsigned long data)
+static void snd_rawmidi_input_event_tasklet(unsigned long data)
{
snd_rawmidi_substream_t *substream = (snd_rawmidi_substream_t *)data;
substream->runtime->event(substream);
}
+static void snd_rawmidi_output_trigger_tasklet(unsigned long data)
+{
+ snd_rawmidi_substream_t *substream = (snd_rawmidi_substream_t *)data;
+ substream->ops->trigger(substream, 1);
+}
+
static int snd_rawmidi_runtime_create(snd_rawmidi_substream_t * substream)
{
snd_rawmidi_runtime_t *runtime;
@@ -99,8 +105,14 @@
return -ENOMEM;
spin_lock_init(&runtime->lock);
init_waitqueue_head(&runtime->sleep);
- tasklet_init(&runtime->event_tasklet, snd_rawmidi_event_tasklet,
- (unsigned long)substream);
+ if (substream->stream == SNDRV_RAWMIDI_STREAM_INPUT)
+ tasklet_init(&runtime->tasklet,
+ snd_rawmidi_input_event_tasklet,
+ (unsigned long)substream);
+ else
+ tasklet_init(&runtime->tasklet,
+ snd_rawmidi_output_trigger_tasklet,
+ (unsigned long)substream);
runtime->event = NULL;
runtime->buffer_size = PAGE_SIZE;
runtime->avail_min = 1;
@@ -127,11 +139,21 @@
return 0;
}
-static void snd_rawmidi_trigger(snd_rawmidi_substream_t * substream, int up)
+static inline void snd_rawmidi_output_trigger(snd_rawmidi_substream_t *
substream, int up)
+{
+ if (up) {
+ tasklet_hi_schedule(&substream->runtime->tasklet);
+ } else {
+ tasklet_kill(&substream->runtime->tasklet);
+ substream->ops->trigger(substream, 0);
+ }
+}
+
+static void snd_rawmidi_input_trigger(snd_rawmidi_substream_t * substream, int
up)
{
substream->ops->trigger(substream, up);
if (!up && substream->runtime->event)
- tasklet_kill(&substream->runtime->event_tasklet);
+ tasklet_kill(&substream->runtime->tasklet);
}
int snd_rawmidi_drop_output(snd_rawmidi_substream_t * substream)
@@ -139,7 +161,7 @@
unsigned long flags;
snd_rawmidi_runtime_t *runtime = substream->runtime;
- snd_rawmidi_trigger(substream, 0);
+ snd_rawmidi_output_trigger(substream, 0);
runtime->drain = 0;
spin_lock_irqsave(&runtime->lock, flags);
runtime->appl_ptr = runtime->hw_ptr = 0;
@@ -182,7 +204,7 @@
unsigned long flags;
snd_rawmidi_runtime_t *runtime = substream->runtime;
- snd_rawmidi_trigger(substream, 0);
+ snd_rawmidi_input_trigger(substream, 0);
runtime->drain = 0;
spin_lock_irqsave(&runtime->lock, flags);
runtime->appl_ptr = runtime->hw_ptr = 0;
@@ -461,7 +483,7 @@
substream = rfile->input;
rfile->input = NULL;
runtime = substream->runtime;
- snd_rawmidi_trigger(substream, 0);
+ snd_rawmidi_input_trigger(substream, 0);
substream->ops->close(substream);
if (runtime->private_free != NULL)
runtime->private_free(substream);
@@ -480,7 +502,7 @@
snd_rawmidi_kernel_write(substream, &buf, 1);
}
if (snd_rawmidi_drain_output(substream) == -ERESTARTSYS)
- snd_rawmidi_trigger(substream, 0);
+ snd_rawmidi_output_trigger(substream, 0);
substream->ops->close(substream);
if (runtime->private_free != NULL)
runtime->private_free(substream);
@@ -878,7 +900,7 @@
}
if (result > 0) {
if (runtime->event)
- tasklet_hi_schedule(&runtime->event_tasklet);
+ tasklet_hi_schedule(&runtime->tasklet);
else if (snd_rawmidi_ready(substream))
wake_up(&runtime->sleep);
}
@@ -922,7 +944,7 @@
long snd_rawmidi_kernel_read(snd_rawmidi_substream_t *substream, unsigned char
*buf, long count)
{
- snd_rawmidi_trigger(substream, 1);
+ snd_rawmidi_input_trigger(substream, 1);
return snd_rawmidi_kernel_read1(substream, buf, count, 1);
}
@@ -939,7 +961,7 @@
if (substream == NULL)
return -EIO;
runtime = substream->runtime;
- snd_rawmidi_trigger(substream, 1);
+ snd_rawmidi_input_trigger(substream, 1);
result = 0;
while (count > 0) {
spin_lock_irq(&runtime->lock);
@@ -1075,11 +1097,8 @@
runtime->avail += count;
substream->bytes += count;
if (count > 0) {
- if (runtime->drain ||
- (runtime->event == NULL && snd_rawmidi_ready(substream)))
+ if (runtime->drain || snd_rawmidi_ready(substream))
wake_up(&runtime->sleep);
- if (runtime->event)
- tasklet_hi_schedule(&runtime->event_tasklet);
}
spin_unlock_irqrestore(&runtime->lock, flags);
return count;
@@ -1149,7 +1168,7 @@
count1 = runtime->avail < runtime->buffer_size;
spin_unlock_irqrestore(&runtime->lock, flags);
if (count1)
- snd_rawmidi_trigger(substream, 1);
+ snd_rawmidi_output_trigger(substream, 1);
return result;
}
@@ -1234,7 +1253,7 @@
rfile = file->private_data;
if (rfile->input != NULL) {
runtime = rfile->input->runtime;
- snd_rawmidi_trigger(rfile->input, 1);
+ snd_rawmidi_input_trigger(rfile->input, 1);
poll_wait(file, &runtime->sleep, wait);
}
if (rfile->output != NULL) {
-
To unsubscribe from this list: send the line "unsubscribe bk-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at http://vger.kernel.org/majordomo-info.html