In packet-oriented drivers such as USB Audio Device class or IEC 61883-1/6, data transmission can fail due to data bus issue. In this case, retries of transmission should be performed as long as any MIDI messages are in time of their presentation time.
When the retries fails, the channel should be broken. In this case, any messages are lost. Userspace applications should close the channel because logical transmission channel lost its reliability, then re-open the channel to recover. Although, Current implementation of ALSA rawmidi core has no mechanism to promote the applications to handle the situation.
This commit is for this idea. A structure for rawmidi runtime get a member to describe error state. Once the runtime lapses into the situation, the member should be set. Then any I/O operations from userspace returns EPIPE and masks from poll includes POLLERR.
Signed-off-by: Takashi Sakamoto o-takashi@sakamocchi.jp --- include/sound/rawmidi.h | 1 + sound/core/rawmidi.c | 61 +++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 57 insertions(+), 5 deletions(-)
diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h index f730b91..da1e93f 100644 --- a/include/sound/rawmidi.h +++ b/include/sound/rawmidi.h @@ -76,6 +76,7 @@ struct snd_rawmidi_runtime { size_t avail_min; /* min avail for wakeup */ size_t avail; /* max used buffer for wakeup */ size_t xruns; /* over/underruns counter */ + bool error; /* Transmission channel is under disorder. */ /* misc */ spinlock_t lock; wait_queue_head_t sleep; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c index 795437b..1ca820e 100644 --- a/sound/core/rawmidi.c +++ b/sound/core/rawmidi.c @@ -129,6 +129,7 @@ static int snd_rawmidi_runtime_create(struct snd_rawmidi_substream *substream) return -ENOMEM; } runtime->appl_ptr = runtime->hw_ptr = 0; + runtime->error = false; substream->runtime = runtime; return 0; } @@ -191,7 +192,10 @@ int snd_rawmidi_drain_output(struct snd_rawmidi_substream *substream) rmidi_warn(substream->rmidi, "rawmidi drain error (avail = %li, buffer_size = %li)\n", (long)runtime->avail, (long)runtime->buffer_size); - err = -EIO; + if (runtime->error) + err = -EPIPE; + else + err = -EIO; } runtime->drain = 0; if (err != -ERESTARTSYS) { @@ -210,14 +214,19 @@ int snd_rawmidi_drain_input(struct snd_rawmidi_substream *substream) { unsigned long flags; struct snd_rawmidi_runtime *runtime = substream->runtime; + int err;
snd_rawmidi_input_trigger(substream, 0); runtime->drain = 0; spin_lock_irqsave(&runtime->lock, flags); runtime->appl_ptr = runtime->hw_ptr = 0; runtime->avail = 0; + if (runtime->error) + err = -EPIPE; + else + err = 0; spin_unlock_irqrestore(&runtime->lock, flags); - return 0; + return err; } EXPORT_SYMBOL(snd_rawmidi_drain_input);
@@ -628,10 +637,13 @@ int snd_rawmidi_output_params(struct snd_rawmidi_substream *substream, { char *newbuf; struct snd_rawmidi_runtime *runtime = substream->runtime; + int err; if (substream->append && substream->use_count > 1) return -EBUSY; - snd_rawmidi_drain_output(substream); + err = snd_rawmidi_drain_output(substream); + if (err < 0) + return err; if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { return -EINVAL; } @@ -658,8 +670,11 @@ int snd_rawmidi_input_params(struct snd_rawmidi_substream *substream, { char *newbuf; struct snd_rawmidi_runtime *runtime = substream->runtime; + int err;
- snd_rawmidi_drain_input(substream); + err = snd_rawmidi_drain_input(substream); + if (err < 0) + return err; if (params->buffer_size < 32 || params->buffer_size > 1024L * 1024L) { return -EINVAL; } @@ -946,6 +961,10 @@ static long snd_rawmidi_kernel_read1(struct snd_rawmidi_substream *substream,
spin_lock_irqsave(&runtime->lock, flags); while (count > 0 && runtime->avail) { + if (runtime->error) { + spin_lock_irqrestore(&runtime->lock, flags); + return -EPIPE; + } count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) count1 = count; @@ -1017,6 +1036,8 @@ static ssize_t snd_rawmidi_read(struct file *file, char __user *buf, size_t coun return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; + if (runtime->error) + return -EPIPE; if (!runtime->avail) return result > 0 ? result : -EIO; spin_lock_irq(&runtime->lock); @@ -1244,6 +1265,10 @@ static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, } } while (count > 0 && runtime->avail > 0) { + if (runtime->error) { + result = -EPIPE; + goto _end; + } count1 = runtime->buffer_size - runtime->appl_ptr; if (count1 > count) count1 = count; @@ -1321,6 +1346,8 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, return -ENODEV; if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; + if (runtime->error) + return -EPIPE; if (!runtime->avail && !timeout) return result > 0 ? result : -EIO; spin_lock_irq(&runtime->lock); @@ -1348,6 +1375,8 @@ static ssize_t snd_rawmidi_write(struct file *file, const char __user *buf, remove_wait_queue(&runtime->sleep, &wait); if (signal_pending(current)) return result > 0 ? result : -ERESTARTSYS; + if (runtime->error) + return -EPIPE; if (runtime->avail == last_avail && !timeout) return result > 0 ? result : -EIO; spin_lock_irq(&runtime->lock); @@ -1375,11 +1404,33 @@ static unsigned int snd_rawmidi_poll(struct file *file, poll_table * wait) } mask = 0; if (rfile->input != NULL) { + runtime = rfile->input->runtime; + + /* Logical channel for communication is broken. */ + spin_lock_irq(&runtime->lock); + if (runtime->error) + mask |= POLLERR; + spin_unlock_irq(&runtime->lock); + + /* + * ALSA rawmidi character devices are available for + * bi-directional communication, thus it's impossible to use + * POLLHUP/POLLRDHUP due to logical contradiction with POLLOUT, + * even if any drivers detects peers' disconnection. + */ if (snd_rawmidi_ready(rfile->input)) mask |= POLLIN | POLLRDNORM; } if (rfile->output != NULL) { - if (snd_rawmidi_ready(rfile->output)) + runtime = rfile->output->runtime; + + /* Logical channel for communication is broken. */ + spin_lock_irq(&runtime->lock); + if (runtime->error) + mask |= POLLERR; + spin_unlock_irq(&runtime->lock); + + if (!(mask & POLLERR) && snd_rawmidi_ready(rfile->output)) mask |= POLLOUT | POLLWRNORM; } return mask;