diff options
author | Takashi Sakamoto <o-takashi@sakamocchi.jp> | 2018-11-23 07:13:07 +0300 |
---|---|---|
committer | Takashi Iwai <tiwai@suse.de> | 2018-11-23 17:31:15 +0300 |
commit | a8c0d13267a4151b2ff124cde6331ec28ed0d55e (patch) | |
tree | fa16772e9c438408d90c4239b7cfe3be4cc67c04 /sound/firewire/tascam/tascam-hwdep.c | |
parent | afb8e2daa031b39900488515a73c0998491ac4b8 (diff) | |
download | linux-a8c0d13267a4151b2ff124cde6331ec28ed0d55e.tar.xz |
ALSA: firewire-tascam: notify events of change of state for userspace applications
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>
Diffstat (limited to 'sound/firewire/tascam/tascam-hwdep.c')
-rw-r--r-- | sound/firewire/tascam/tascam-hwdep.c | 65 |
1 files changed, 63 insertions, 2 deletions
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c index 8f34cede2e9f..0414abf5daa8 100644 --- a/sound/firewire/tascam/tascam-hwdep.c +++ b/sound/firewire/tascam/tascam-hwdep.c @@ -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; |