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>
#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 */
__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;
};
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
* 1.get firewire node information
* 2.get notification about starting/stopping stream
* 3.lock/unlock stream
+ * 4.get asynchronous messaging
*/
#include "digi00x.h"
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();
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);
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;
--- /dev/null
+/*
+ * 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);
+}
struct snd_dg00x *dg00x = card->private_data;
snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_dg00x_transaction_unregister(dg00x);
fw_unit_put(dg00x->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;
{
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);
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
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,