ALSA: fireface: add local framework to message parser
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Thu, 12 Jan 2023 12:09:53 +0000 (21:09 +0900)
committerTakashi Iwai <tiwai@suse.de>
Fri, 13 Jan 2023 08:56:37 +0000 (09:56 +0100)
This commit adds local framework to message parser. This is preparation
for future work to pass event of knob control for Fireface 400 to user
space.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20230112120954.500692-6-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/fireface/ff-hwdep.c
sound/firewire/fireface/ff-transaction.c
sound/firewire/fireface/ff.c
sound/firewire/fireface/ff.h

index ea64a2a..8a741b3 100644 (file)
 
 #include "ff.h"
 
+static bool has_msg(struct snd_ff *ff)
+{
+       if (ff->spec->protocol->has_msg)
+               return ff->spec->protocol->has_msg(ff);
+       else
+               return 0;
+}
+
 static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
                       loff_t *offset)
 {
        struct snd_ff *ff = hwdep->private_data;
        DEFINE_WAIT(wait);
-       union snd_firewire_event event;
 
        spin_lock_irq(&ff->lock);
 
-       while (!ff->dev_lock_changed) {
+       while (!ff->dev_lock_changed && !has_msg(ff)) {
                prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
                spin_unlock_irq(&ff->lock);
                schedule();
@@ -34,17 +41,29 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
                spin_lock_irq(&ff->lock);
        }
 
-       memset(&event, 0, sizeof(event));
-       event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
-       event.lock_status.status = (ff->dev_lock_count > 0);
-       ff->dev_lock_changed = false;
+       if (ff->dev_lock_changed && count >= sizeof(struct snd_firewire_event_lock_status)) {
+               struct snd_firewire_event_lock_status ev = {
+                       .type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+                       .status = (ff->dev_lock_count > 0),
+               };
 
-       count = min_t(long, count, sizeof(event.lock_status));
+               ff->dev_lock_changed = false;
 
-       spin_unlock_irq(&ff->lock);
+               spin_unlock_irq(&ff->lock);
 
-       if (copy_to_user(buf, &event, count))
-               return -EFAULT;
+               if (copy_to_user(buf, &ev, sizeof(ev)))
+                       return -EFAULT;
+               count = sizeof(ev);
+       } else if (has_msg(ff)) {
+               // NOTE: Acquired spin lock should be released before accessing to user space in the
+               // callback since the access can cause page fault.
+               count = ff->spec->protocol->copy_msg_to_user(ff, buf, count);
+               spin_unlock_irq(&ff->lock);
+       } else {
+               spin_unlock_irq(&ff->lock);
+
+               count = 0;
+       }
 
        return count;
 }
@@ -58,7 +77,7 @@ static __poll_t hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
        poll_wait(file, &ff->hwdep_wait, wait);
 
        spin_lock_irq(&ff->lock);
-       if (ff->dev_lock_changed)
+       if (ff->dev_lock_changed || has_msg(ff))
                events = EPOLLIN | EPOLLRDNORM;
        else
                events = 0;
index 79f733d..6b89e39 100644 (file)
@@ -132,11 +132,15 @@ static void handle_msg(struct fw_card *card, struct fw_request *request, int tco
        struct snd_ff *ff = callback_data;
        __le32 *buf = data;
        u32 tstamp = fw_request_get_timestamp(request);
+       unsigned long flag;
 
        fw_send_response(card, request, RCODE_COMPLETE);
 
        offset -= ff->async_handler.offset;
+
+       spin_lock_irqsave(&ff->lock, flag);
        ff->spec->protocol->handle_msg(ff, (unsigned int)offset, buf, length, tstamp);
+       spin_unlock_irqrestore(&ff->lock, flag);
 }
 
 static int allocate_own_address(struct snd_ff *ff, int i)
index 7bf51d0..448e972 100644 (file)
@@ -43,6 +43,8 @@ static void ff_card_free(struct snd_card *card)
        snd_ff_stream_destroy_duplex(ff);
        snd_ff_transaction_unregister(ff);
 
+       kfree(ff->msg_parser);
+
        mutex_destroy(&ff->mutex);
        fw_unit_put(ff->unit);
 }
@@ -94,6 +96,14 @@ static int snd_ff_probe(struct fw_unit *unit, const struct ieee1394_device_id *e
        if (err < 0)
                goto error;
 
+       if (ff->spec->protocol->msg_parser_size > 0) {
+               ff->msg_parser = kzalloc(ff->spec->protocol->msg_parser_size, GFP_KERNEL);
+               if (!ff->msg_parser) {
+                       err = -ENOMEM;
+                       goto error;
+               }
+       }
+
        err = snd_card_register(card);
        if (err < 0)
                goto error;
index f430ebe..7e42f57 100644 (file)
@@ -97,6 +97,8 @@ struct snd_ff {
        wait_queue_head_t hwdep_wait;
 
        struct amdtp_domain domain;
+
+       void *msg_parser;
 };
 
 enum snd_ff_clock_src {
@@ -110,6 +112,9 @@ enum snd_ff_clock_src {
 };
 
 struct snd_ff_protocol {
+       size_t msg_parser_size;
+       bool (*has_msg)(struct snd_ff *ff);
+       long (*copy_msg_to_user)(struct snd_ff *ff, char __user *buf, long count);
        void (*handle_msg)(struct snd_ff *ff, unsigned int offset, const __le32 *buf,
                           size_t length, u32 tstamp);
        int (*fill_midi_msg)(struct snd_ff *ff,