ALSA: firewire-tascam: notify events of change of state for userspace applications
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Fri, 23 Nov 2018 04:13:07 +0000 (13:13 +0900)
committerTakashi Iwai <tiwai@suse.de>
Fri, 23 Nov 2018 14:31:15 +0000 (15:31 +0100)
In former commits, ALSA firewire-tascam driver queues events to notify
change of state of control surface to userspace via ALSA hwdep
interface.

This commit implements actual notification of the events. The events are
not governed by real time, thus no need to care underrun.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/uapi/sound/firewire.h
sound/firewire/tascam/amdtp-tascam.c
sound/firewire/tascam/tascam-hwdep.c

index bb067efb09c68d05cc4da46d5c9778ee6dc495bd..ae12826ed641be27a613ca78c0ca0276fb101766 100644 (file)
@@ -12,6 +12,7 @@
 #define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE      0x4e617475
 #define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE   0x746e736c
 #define SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION 0x64776479
+#define SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL    0x7473636d
 
 struct snd_firewire_event_common {
        unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -59,12 +60,18 @@ struct snd_firewire_tascam_change {
        __be32 after;
 };
 
+struct snd_firewire_event_tascam_control {
+       unsigned int type;
+       struct snd_firewire_tascam_change changes[0];
+};
+
 union snd_firewire_event {
        struct snd_firewire_event_common            common;
        struct snd_firewire_event_lock_status       lock_status;
        struct snd_firewire_event_dice_notification dice_notification;
        struct snd_firewire_event_efw_response      efw_response;
        struct snd_firewire_event_digi00x_message   digi00x_message;
+       struct snd_firewire_event_tascam_control    tascam_control;
        struct snd_firewire_event_motu_notification motu_notification;
 };
 
index 0e8088c9ada93aa2b9cd44ddbedd08d16e2b5efd..a52d1f76c610fbed8003e8195d60a61251e8dc45 100644 (file)
@@ -156,6 +156,8 @@ static void read_status_messages(struct amdtp_stream *s,
                                if (++tscm->push_pos >= SND_TSCM_QUEUE_COUNT)
                                        tscm->push_pos = 0;
                                spin_unlock_irq(&tscm->lock);
+
+                               wake_up(&tscm->hwdep_wait);
                        }
                }
 
index 8f34cede2e9f57c0b84f73e16fdd94450b6181e8..0414abf5daa835e3a393e06bedf84ff5e4d09e62 100644 (file)
@@ -35,6 +35,65 @@ static long tscm_hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
        return count;
 }
 
+static long tscm_hwdep_read_queue(struct snd_tscm *tscm, char __user *buf,
+                                 long remained, loff_t *offset)
+{
+       char __user *pos = buf;
+       unsigned int type = SNDRV_FIREWIRE_EVENT_TASCAM_CONTROL;
+       struct snd_firewire_tascam_change *entries = tscm->queue;
+       long count;
+
+       // At least, one control event can be copied.
+       if (remained < sizeof(type) + sizeof(*entries)) {
+               spin_unlock_irq(&tscm->lock);
+               return -EINVAL;
+       }
+
+       // Copy the type field later.
+       count = sizeof(type);
+       remained -= sizeof(type);
+       pos += sizeof(type);
+
+       while (true) {
+               unsigned int head_pos;
+               unsigned int tail_pos;
+               unsigned int length;
+
+               if (tscm->pull_pos == tscm->push_pos)
+                       break;
+               else if (tscm->pull_pos < tscm->push_pos)
+                       tail_pos = tscm->push_pos;
+               else
+                       tail_pos = SND_TSCM_QUEUE_COUNT;
+               head_pos = tscm->pull_pos;
+
+               length = (tail_pos - head_pos) * sizeof(*entries);
+               if (remained < length)
+                       length = rounddown(remained, sizeof(*entries));
+               if (length == 0)
+                       break;
+
+               spin_unlock_irq(&tscm->lock);
+               if (copy_to_user(pos, &entries[head_pos], length))
+                       return -EFAULT;
+
+               spin_lock_irq(&tscm->lock);
+
+               tscm->pull_pos = tail_pos % SND_TSCM_QUEUE_COUNT;
+
+               count += length;
+               remained -= length;
+               pos += length;
+       }
+
+       spin_unlock_irq(&tscm->lock);
+
+       if (copy_to_user(buf, &type, sizeof(type)))
+               return -EFAULT;
+
+       return count;
+}
+
 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
                       loff_t *offset)
 {
@@ -43,7 +102,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 
        spin_lock_irq(&tscm->lock);
 
-       while (!tscm->dev_lock_changed) {
+       while (!tscm->dev_lock_changed && tscm->push_pos == tscm->pull_pos) {
                prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
                spin_unlock_irq(&tscm->lock);
                schedule();
@@ -56,6 +115,8 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
        // NOTE: The acquired lock should be released in callee side.
        if (tscm->dev_lock_changed) {
                count = tscm_hwdep_read_locked(tscm, buf, count, offset);
+       } else if (tscm->push_pos != tscm->pull_pos) {
+               count = tscm_hwdep_read_queue(tscm, buf, count, offset);
        } else {
                spin_unlock_irq(&tscm->lock);
                count = 0;
@@ -73,7 +134,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
        poll_wait(file, &tscm->hwdep_wait, wait);
 
        spin_lock_irq(&tscm->lock);
-       if (tscm->dev_lock_changed)
+       if (tscm->dev_lock_changed || tscm->push_pos != tscm->pull_pos)
                events = EPOLLIN | EPOLLRDNORM;
        else
                events = 0;