ALSA: firewire-digi00x: add support for asynchronous messaging
authorTakashi Sakamoto <o-takashi@sakamocchi.jp>
Wed, 30 Sep 2015 00:39:22 +0000 (09:39 +0900)
committerTakashi Iwai <tiwai@suse.de>
Wed, 30 Sep 2015 13:37:55 +0000 (15:37 +0200)
Digi 002/003 family uses asynchronous transaction for messaging.
The address to transmit this message is stored on a certain register.

This commit allocates a range of address on OHCI 1394 host controller
to handle the messaging. As long as I know, the purpose of this message
seems to notify lost of synchronization. While, the meaning of content
of the message is not clear.

Actual examples of this messaging:
 * When clock source is set as internal:
 - 0x00007051
 - 0x00007052
 - 0x00007054
 - 0x00007057
 - 0x00007058
 * When clock source is set as somewhat external:
 - 0x00009000
 - 0x00009010
 - 0x00009020
 - 0x00009021
 - 0x00009022

The lost often occurs when using internal clock source. In this case,
users hear sounds with quite short gap every several minutes. In fact,
the lost is recovered temporarily.

When using with external clock source, the lost seems not to occur. The
mechanism is not clear yet.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
include/uapi/sound/firewire.h
sound/firewire/digi00x/Makefile
sound/firewire/digi00x/digi00x-hwdep.c
sound/firewire/digi00x/digi00x-transaction.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x.c
sound/firewire/digi00x/digi00x.h

index f67d228f731b16b4386707f37ad7fb724cf1836b..deb041cb9af05723fe2da245c35f370e401097f6 100644 (file)
@@ -9,6 +9,7 @@
 #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS       0x000010cc
 #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
 #define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE      0x4e617475
+#define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE   0x746e736c
 
 struct snd_firewire_event_common {
        unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -40,11 +41,17 @@ struct snd_firewire_event_efw_response {
        __be32 response[0];     /* some responses */
 };
 
+struct snd_firewire_event_digi00x_message {
+       unsigned int type;
+       __u32 message;  /* Digi00x-specific message */
+};
+
 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;
 };
 
 
index 5bc3c7780a5118e58bde102b2be29c1030d51466..28e3d137ef57f740977ee7ba07956237d3d023ad 100644 (file)
@@ -1,3 +1,4 @@
 snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
-                            digi00x-pcm.o digi00x-hwdep.o digi00x.o
+                            digi00x-pcm.o digi00x-hwdep.o \
+                            digi00x-transaction.o digi00x.o
 obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
index d629e415dc8b3e9f7de01ba2001f1b4888649dd0..f188e4758fd295ecb9a3db341c1c6606d40d06c4 100644 (file)
@@ -12,6 +12,7 @@
  * 1.get firewire node information
  * 2.get notification about starting/stopping stream
  * 3.lock/unlock stream
+ * 4.get asynchronous messaging
  */
 
 #include "digi00x.h"
@@ -25,7 +26,7 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
 
        spin_lock_irq(&dg00x->lock);
 
-       while (!dg00x->dev_lock_changed) {
+       while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
                prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
                spin_unlock_irq(&dg00x->lock);
                schedule();
@@ -42,6 +43,13 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
                dg00x->dev_lock_changed = false;
 
                count = min_t(long, count, sizeof(event.lock_status));
+       } else {
+               event.digi00x_message.type =
+                                       SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+               event.digi00x_message.message = dg00x->msg;
+               dg00x->msg = 0;
+
+               count = min_t(long, count, sizeof(event.digi00x_message));
        }
 
        spin_unlock_irq(&dg00x->lock);
@@ -61,7 +69,7 @@ static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
        poll_wait(file, &dg00x->hwdep_wait, wait);
 
        spin_lock_irq(&dg00x->lock);
-       if (dg00x->dev_lock_changed)
+       if (dg00x->dev_lock_changed || dg00x->msg)
                events = POLLIN | POLLRDNORM;
        else
                events = 0;
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644 (file)
index 0000000..4937290
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+                                  unsigned long long offset, __be32 *buf)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+       dg00x->msg = be32_to_cpu(*buf);
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+
+       wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+                          int tcode, int destination, int source,
+                          int generation, unsigned long long offset,
+                          void *data, size_t length, void *callback_data)
+{
+       struct snd_dg00x *dg00x = callback_data;
+       __be32 *buf = (__be32 *)data;
+
+       if (offset == dg00x->async_handler.offset)
+               handle_unknown_message(dg00x, offset, buf);
+
+       fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+       struct fw_device *device = fw_parent_device(dg00x->unit);
+       __be32 data[2];
+
+       /* Unknown. 4bytes. */
+       data[0] = cpu_to_be32((device->card->node_id << 16) |
+                             (dg00x->async_handler.offset >> 32));
+       data[1] = cpu_to_be32(dg00x->async_handler.offset);
+       return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+                                  DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+                                  &data, sizeof(data), 0);
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+       static const struct fw_address_region resp_register_region = {
+               .start  = 0xffffe0000000ull,
+               .end    = 0xffffe000ffffull,
+       };
+       int err;
+
+       dg00x->async_handler.length = 4;
+       dg00x->async_handler.address_callback = handle_message;
+       dg00x->async_handler.callback_data = dg00x;
+
+       err = fw_core_add_address_handler(&dg00x->async_handler,
+                                         &resp_register_region);
+       if (err < 0)
+               return err;
+
+       err = snd_dg00x_transaction_reregister(dg00x);
+       if (err < 0) {
+               fw_core_remove_address_handler(&dg00x->async_handler);
+               dg00x->async_handler.address_callback = NULL;
+       }
+
+       return err;
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+       fw_core_remove_address_handler(&dg00x->async_handler);
+}
index cd93030df1cdbf1991f51bcb770b08fbc484df04..34937a26c1986463088fda63d3e20e92b55a4ed8 100644 (file)
@@ -46,6 +46,7 @@ static void dg00x_card_free(struct snd_card *card)
        struct snd_dg00x *dg00x = card->private_data;
 
        snd_dg00x_stream_destroy_duplex(dg00x);
+       snd_dg00x_transaction_unregister(dg00x);
 
        fw_unit_put(dg00x->unit);
 
@@ -93,6 +94,10 @@ static int snd_dg00x_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
+       err = snd_dg00x_transaction_register(dg00x);
+       if (err < 0)
+               goto error;
+
        err = snd_card_register(card);
        if (err < 0)
                goto error;
@@ -109,6 +114,8 @@ static void snd_dg00x_update(struct fw_unit *unit)
 {
        struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
 
+       snd_dg00x_transaction_reregister(dg00x);
+
        mutex_lock(&dg00x->mutex);
        snd_dg00x_stream_update_duplex(dg00x);
        mutex_unlock(&dg00x->mutex);
index 5ba5318062623121c16ec0a9eae6e5251474a210..b960c868b59b5058656672ed6fc5c0a0ece9621b 100644 (file)
@@ -49,6 +49,9 @@ struct snd_dg00x {
        bool dev_lock_changed;
        wait_queue_head_t hwdep_wait;
 
+       /* For asynchronous messages. */
+       struct fw_address_handler async_handler;
+       u32 msg;
 };
 
 #define DG00X_ADDR_BASE                0xffffe0000000ull
@@ -110,6 +113,10 @@ int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
                                     struct snd_pcm_runtime *runtime);
 void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
 
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
 extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
 extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
 int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,