summaryrefslogtreecommitdiff
path: root/sound/firewire
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire')
-rw-r--r--sound/firewire/Kconfig1
-rw-r--r--sound/firewire/Makefile3
-rw-r--r--sound/firewire/amdtp-stream-trace.h110
-rw-r--r--sound/firewire/amdtp-stream.c210
-rw-r--r--sound/firewire/amdtp-stream.h37
-rw-r--r--sound/firewire/bebob/bebob.c217
-rw-r--r--sound/firewire/bebob/bebob.h6
-rw-r--r--sound/firewire/bebob/bebob_stream.c101
-rw-r--r--sound/firewire/dice/dice.c41
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c2
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c7
-rw-r--r--sound/firewire/digi00x/digi00x.c107
-rw-r--r--sound/firewire/digi00x/digi00x.h3
-rw-r--r--sound/firewire/fireworks/fireworks.c168
-rw-r--r--sound/firewire/fireworks/fireworks.h4
-rw-r--r--sound/firewire/fireworks/fireworks_stream.c84
-rw-r--r--sound/firewire/lib.c32
-rw-r--r--sound/firewire/lib.h3
-rw-r--r--sound/firewire/oxfw/oxfw-stream.c3
-rw-r--r--sound/firewire/oxfw/oxfw.c151
-rw-r--r--sound/firewire/oxfw/oxfw.h4
-rw-r--r--sound/firewire/tascam/tascam-stream.c26
-rw-r--r--sound/firewire/tascam/tascam.c118
-rw-r--r--sound/firewire/tascam/tascam.h2
24 files changed, 859 insertions, 581 deletions
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 2a779c2f6..ab894ed1f 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -134,6 +134,7 @@ config SND_FIREWIRE_TASCAM
Say Y here to include support for TASCAM.
* FW-1884
* FW-1082
+ * FW-1804
To compile this driver as a module, choose M here: the module
will be called snd-firewire-tascam.
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 003c09029..0ee1fb115 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -1,3 +1,6 @@
+# To find a header included by define_trace.h.
+CFLAGS_amdtp-stream.o := -I$(src)
+
snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
fcp.o cmp.o amdtp-stream.o amdtp-am824.o
snd-isight-objs := isight.o
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
new file mode 100644
index 000000000..9c04faf20
--- /dev/null
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -0,0 +1,110 @@
+/*
+ * amdtp-stream-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2016 Takashi Sakamoto
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_firewire_lib
+
+#if !defined(_AMDTP_STREAM_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _AMDTP_STREAM_TRACE_H
+
+#include <linux/tracepoint.h>
+
+TRACE_EVENT(in_packet,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index),
+ TP_ARGS(s, cycles, cip_header, payload_quadlets, index),
+ TP_STRUCT__entry(
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __field(u32, cip_header0)
+ __field(u32, cip_header1)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dest = fw_parent_device(s->unit)->card->node_id;
+ __entry->cip_header0 = cip_header[0];
+ __entry->cip_header1 = cip_header[1];
+ __entry->payload_quadlets = payload_quadlets;
+ __entry->packet_index = s->packet_index;
+ __entry->irq = !!in_interrupt();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->cip_header0,
+ __entry->cip_header1,
+ __entry->payload_quadlets,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index)
+);
+
+TRACE_EVENT(out_packet,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, __be32 *cip_header, unsigned int payload_length, unsigned int index),
+ TP_ARGS(s, cycles, cip_header, payload_length, index),
+ TP_STRUCT__entry(
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __field(u32, cip_header0)
+ __field(u32, cip_header1)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dest = fw_parent_device(s->unit)->node_id;
+ __entry->cip_header0 = be32_to_cpu(cip_header[0]);
+ __entry->cip_header1 = be32_to_cpu(cip_header[1]);
+ __entry->payload_quadlets = payload_length / 4;
+ __entry->packet_index = s->packet_index;
+ __entry->irq = !!in_interrupt();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%02u %04u %04x %04x %02d %08x %08x %03u %02u %01u %02u",
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->cip_header0,
+ __entry->cip_header1,
+ __entry->payload_quadlets,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index)
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE amdtp-stream-trace
+#include <trace/define_trace.h>
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index ed2902609..00060c4a9 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -19,6 +19,10 @@
#define CYCLES_PER_SECOND 8000
#define TICKS_PER_SECOND (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+/* Always support Linux tracing subsystem. */
+#define CREATE_TRACE_POINTS
+#include "amdtp-stream-trace.h"
+
#define TRANSFER_DELAY_TICKS 0x2e00 /* 479.17 microseconds */
/* isochronous header parameters */
@@ -87,7 +91,6 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
init_waitqueue_head(&s->callback_wait);
s->callbacked = false;
- s->sync_slave = NULL;
s->fmt = fmt;
s->process_data_blocks = process_data_blocks;
@@ -102,6 +105,10 @@ EXPORT_SYMBOL(amdtp_stream_init);
*/
void amdtp_stream_destroy(struct amdtp_stream *s)
{
+ /* Not initialized. */
+ if (s->protocol == NULL)
+ return;
+
WARN_ON(amdtp_stream_running(s));
kfree(s->protocol);
mutex_destroy(&s->mutex);
@@ -244,7 +251,6 @@ void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
tasklet_kill(&s->period_tasklet);
s->pcm_buffer_pointer = 0;
s->pcm_period_pointer = 0;
- s->pointer_flush = true;
}
EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
@@ -349,7 +355,6 @@ static void update_pcm_pointers(struct amdtp_stream *s,
s->pcm_period_pointer += frames;
if (s->pcm_period_pointer >= pcm->runtime->period_size) {
s->pcm_period_pointer -= pcm->runtime->period_size;
- s->pointer_flush = false;
tasklet_hi_schedule(&s->period_tasklet);
}
}
@@ -363,9 +368,8 @@ static void pcm_period_tasklet(unsigned long data)
snd_pcm_period_elapsed(pcm);
}
-static int queue_packet(struct amdtp_stream *s,
- unsigned int header_length,
- unsigned int payload_length, bool skip)
+static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
+ unsigned int payload_length)
{
struct fw_iso_packet p = {0};
int err = 0;
@@ -376,8 +380,10 @@ static int queue_packet(struct amdtp_stream *s,
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
p.tag = TAG_CIP;
p.header_length = header_length;
- p.payload_length = (!skip) ? payload_length : 0;
- p.skip = skip;
+ if (payload_length > 0)
+ p.payload_length = payload_length;
+ else
+ p.skip = true;
err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
s->buffer.packets[s->packet_index].offset);
if (err < 0) {
@@ -392,27 +398,30 @@ end:
}
static inline int queue_out_packet(struct amdtp_stream *s,
- unsigned int payload_length, bool skip)
+ unsigned int payload_length)
{
- return queue_packet(s, OUT_PACKET_HEADER_SIZE,
- payload_length, skip);
+ return queue_packet(s, OUT_PACKET_HEADER_SIZE, payload_length);
}
static inline int queue_in_packet(struct amdtp_stream *s)
{
return queue_packet(s, IN_PACKET_HEADER_SIZE,
- amdtp_stream_get_max_payload(s), false);
+ amdtp_stream_get_max_payload(s));
}
-static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
- unsigned int syt)
+static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
+ unsigned int index)
{
__be32 *buffer;
+ unsigned int syt;
+ unsigned int data_blocks;
unsigned int payload_length;
unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
buffer = s->buffer.packets[s->packet_index].buffer;
+ syt = calculate_syt(s, cycle);
+ data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
@@ -424,9 +433,11 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
(syt & CIP_SYT_MASK));
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
-
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
- if (queue_out_packet(s, payload_length, false) < 0)
+
+ trace_out_packet(s, cycle, buffer, payload_length, index);
+
+ if (queue_out_packet(s, payload_length) < 0)
return -EIO;
pcm = ACCESS_ONCE(s->pcm);
@@ -438,19 +449,24 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
}
static int handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_quadlets, __be32 *buffer,
- unsigned int *data_blocks, unsigned int syt)
+ unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int index)
{
+ __be32 *buffer;
u32 cip_header[2];
- unsigned int fmt, fdf;
+ unsigned int fmt, fdf, syt;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
+ unsigned int data_blocks;
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
bool lost;
+ buffer = s->buffer.packets[s->packet_index].buffer;
cip_header[0] = be32_to_cpu(buffer[0]);
cip_header[1] = be32_to_cpu(buffer[1]);
+ trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
+
/*
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not.
@@ -460,7 +476,7 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
- *data_blocks = 0;
+ data_blocks = 0;
pcm_frames = 0;
goto end;
}
@@ -471,7 +487,7 @@ static int handle_in_packet(struct amdtp_stream *s,
dev_info_ratelimited(&s->unit->device,
"Detect unexpected protocol: %08x %08x\n",
cip_header[0], cip_header[1]);
- *data_blocks = 0;
+ data_blocks = 0;
pcm_frames = 0;
goto end;
}
@@ -480,7 +496,7 @@ static int handle_in_packet(struct amdtp_stream *s,
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
if (payload_quadlets < 3 ||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
- *data_blocks = 0;
+ data_blocks = 0;
} else {
data_block_quadlets =
(cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
@@ -494,12 +510,12 @@ static int handle_in_packet(struct amdtp_stream *s,
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
- *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+ data_blocks = (payload_quadlets - 2) / data_block_quadlets;
}
/* Check data block counter continuity */
data_block_counter = cip_header[0] & CIP_DBC_MASK;
- if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+ if (data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
s->data_block_counter != UINT_MAX)
data_block_counter = s->data_block_counter;
@@ -510,10 +526,10 @@ static int handle_in_packet(struct amdtp_stream *s,
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = data_block_counter != s->data_block_counter;
} else {
- if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
+ if (data_blocks > 0 && s->tx_dbc_interval > 0)
dbc_interval = s->tx_dbc_interval;
else
- dbc_interval = *data_blocks;
+ dbc_interval = data_blocks;
lost = data_block_counter !=
((s->data_block_counter + dbc_interval) & 0xff);
@@ -526,13 +542,14 @@ static int handle_in_packet(struct amdtp_stream *s,
return -EIO;
}
- pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
+ syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+ pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
if (s->flags & CIP_DBC_IS_END_EVENT)
s->data_block_counter = data_block_counter;
else
s->data_block_counter =
- (data_block_counter + *data_blocks) & 0xff;
+ (data_block_counter + data_blocks) & 0xff;
end:
if (queue_in_packet(s) < 0)
return -EIO;
@@ -544,29 +561,50 @@ end:
return 0;
}
-static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+/*
+ * In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
+ * the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
+ * it. Thus, via Linux firewire subsystem, we can get the 3 bits for second.
+ */
+static inline u32 compute_cycle_count(u32 tstamp)
+{
+ return (((tstamp >> 13) & 0x07) * 8000) + (tstamp & 0x1fff);
+}
+
+static inline u32 increment_cycle_count(u32 cycle, unsigned int addend)
+{
+ cycle += addend;
+ if (cycle >= 8 * CYCLES_PER_SECOND)
+ cycle -= 8 * CYCLES_PER_SECOND;
+ return cycle;
+}
+
+static inline u32 decrement_cycle_count(u32 cycle, unsigned int subtrahend)
+{
+ if (cycle < subtrahend)
+ cycle += 8 * CYCLES_PER_SECOND;
+ return cycle - subtrahend;
+}
+
+static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int i, syt, packets = header_length / 4;
- unsigned int data_blocks;
+ unsigned int i, packets = header_length / 4;
+ u32 cycle;
if (s->packet_index < 0)
return;
- /*
- * Compute the cycle of the last queued packet.
- * (We need only the four lowest bits for the SYT, so we can ignore
- * that bits 0-11 must wrap around at 3072.)
- */
- cycle += QUEUE_LENGTH - packets;
+ cycle = compute_cycle_count(tstamp);
- for (i = 0; i < packets; ++i) {
- syt = calculate_syt(s, ++cycle);
- data_blocks = calculate_data_blocks(s, syt);
+ /* Align to actual cycle count for the last packet. */
+ cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
- if (handle_out_packet(s, data_blocks, syt) < 0) {
+ for (i = 0; i < packets; ++i) {
+ cycle = increment_cycle_count(cycle, 1);
+ if (handle_out_packet(s, cycle, i) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
@@ -576,15 +614,15 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
fw_iso_context_queue_flush(s->context);
}
-static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
size_t header_length, void *header,
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int p, syt, packets;
+ unsigned int i, packets;
unsigned int payload_quadlets, max_payload_quadlets;
- unsigned int data_blocks;
- __be32 *buffer, *headers = header;
+ __be32 *headers = header;
+ u32 cycle;
if (s->packet_index < 0)
return;
@@ -592,70 +630,44 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
/* The number of packets in buffer */
packets = header_length / IN_PACKET_HEADER_SIZE;
+ cycle = compute_cycle_count(tstamp);
+
+ /* Align to actual cycle count for the last packet. */
+ cycle = decrement_cycle_count(cycle, packets);
+
/* For buffer-over-run prevention. */
max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
- for (p = 0; p < packets; p++) {
- buffer = s->buffer.packets[s->packet_index].buffer;
+ for (i = 0; i < packets; i++) {
+ cycle = increment_cycle_count(cycle, 1);
/* The number of quadlets in this packet */
payload_quadlets =
- (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+ (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
if (payload_quadlets > max_payload_quadlets) {
dev_err(&s->unit->device,
"Detect jumbo payload: %02x %02x\n",
payload_quadlets, max_payload_quadlets);
- s->packet_index = -1;
break;
}
- syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
- if (handle_in_packet(s, payload_quadlets, buffer,
- &data_blocks, syt) < 0) {
- s->packet_index = -1;
+ if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
break;
- }
-
- /* Process sync slave stream */
- if (s->sync_slave && s->sync_slave->callbacked) {
- if (handle_out_packet(s->sync_slave,
- data_blocks, syt) < 0) {
- s->packet_index = -1;
- break;
- }
- }
}
- /* Queueing error or detecting discontinuity */
- if (s->packet_index < 0) {
+ /* Queueing error or detecting invalid payload. */
+ if (i < packets) {
+ s->packet_index = -1;
amdtp_stream_pcm_abort(s);
-
- /* Abort sync slave. */
- if (s->sync_slave) {
- s->sync_slave->packet_index = -1;
- amdtp_stream_pcm_abort(s->sync_slave);
- }
return;
}
- /* when sync to device, flush the packets for slave stream */
- if (s->sync_slave && s->sync_slave->callbacked)
- fw_iso_context_queue_flush(s->sync_slave->context);
-
fw_iso_context_queue_flush(s->context);
}
-/* processing is done by master callback */
-static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
- size_t header_length, void *header,
- void *private_data)
-{
- return;
-}
-
/* this is executed one time */
static void amdtp_stream_first_callback(struct fw_iso_context *context,
- u32 cycle, size_t header_length,
+ u32 tstamp, size_t header_length,
void *header, void *private_data)
{
struct amdtp_stream *s = private_data;
@@ -669,12 +681,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
if (s->direction == AMDTP_IN_STREAM)
context->callback.sc = in_stream_callback;
- else if (s->flags & CIP_SYNC_TO_DEVICE)
- context->callback.sc = slave_stream_callback;
else
context->callback.sc = out_stream_callback;
- context->callback.sc(context, cycle, header_length, header, s);
+ context->callback.sc(context, tstamp, header_length, header, s);
}
/**
@@ -713,8 +723,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
goto err_unlock;
}
- if (s->direction == AMDTP_IN_STREAM &&
- s->flags & CIP_SKIP_INIT_DBC_CHECK)
+ if (s->direction == AMDTP_IN_STREAM)
s->data_block_counter = UINT_MAX;
else
s->data_block_counter = 0;
@@ -755,7 +764,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
if (s->direction == AMDTP_IN_STREAM)
err = queue_in_packet(s);
else
- err = queue_out_packet(s, 0, true);
+ err = queue_out_packet(s, 0);
if (err < 0)
goto err_context;
} while (s->packet_index > 0);
@@ -794,11 +803,24 @@ EXPORT_SYMBOL(amdtp_stream_start);
*/
unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
{
- /* this optimization is allowed to be racy */
- if (s->pointer_flush && amdtp_stream_running(s))
+ /*
+ * This function is called in software IRQ context of period_tasklet or
+ * process context.
+ *
+ * When the software IRQ context was scheduled by software IRQ context
+ * of IR/IT contexts, queued packets were already handled. Therefore,
+ * no need to flush the queue in buffer anymore.
+ *
+ * When the process context reach here, some packets will be already
+ * queued in the buffer. These packets should be handled immediately
+ * to keep better granularity of PCM pointer.
+ *
+ * Later, the process context will sometimes schedules software IRQ
+ * context of the period_tasklet. Then, no need to flush the queue by
+ * the same reason as described for IR/IT contexts.
+ */
+ if (!in_interrupt() && amdtp_stream_running(s))
fw_iso_context_flush_completions(s->context);
- else
- s->pointer_flush = true;
return ACCESS_ONCE(s->pcm_buffer_pointer);
}
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index 8775704a3..c1bc7fad0 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -17,8 +17,6 @@
* @CIP_BLOCKING: In blocking mode, each packet contains either zero or
* SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right.
- * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
- * generated by in packets. Defaultly this driver generates timestamp.
* @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
* @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
* corresponds to the end of event in the packet. Out of IEC 61883.
@@ -26,8 +24,6 @@
* The value of data_block_quadlets is used instead of reported value.
* @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
* skipped for detecting discontinuity.
- * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
- * packet is not continuous from an initial value.
* @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
* packet is wrong but the others are correct.
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
@@ -37,14 +33,12 @@
enum cip_flags {
CIP_NONBLOCKING = 0x00,
CIP_BLOCKING = 0x01,
- CIP_SYNC_TO_DEVICE = 0x02,
- CIP_EMPTY_WITH_TAG0 = 0x04,
- CIP_DBC_IS_END_EVENT = 0x08,
- CIP_WRONG_DBS = 0x10,
- CIP_SKIP_DBC_ZERO_CHECK = 0x20,
- CIP_SKIP_INIT_DBC_CHECK = 0x40,
- CIP_EMPTY_HAS_WRONG_DBC = 0x80,
- CIP_JUMBO_PAYLOAD = 0x100,
+ CIP_EMPTY_WITH_TAG0 = 0x02,
+ CIP_DBC_IS_END_EVENT = 0x04,
+ CIP_WRONG_DBS = 0x08,
+ CIP_SKIP_DBC_ZERO_CHECK = 0x10,
+ CIP_EMPTY_HAS_WRONG_DBC = 0x20,
+ CIP_JUMBO_PAYLOAD = 0x40,
};
/**
@@ -132,12 +126,10 @@ struct amdtp_stream {
struct tasklet_struct period_tasklet;
unsigned int pcm_buffer_pointer;
unsigned int pcm_period_pointer;
- bool pointer_flush;
/* To wait for first packet. */
bool callbacked;
wait_queue_head_t callback_wait;
- struct amdtp_stream *sync_slave;
/* For backends to process data blocks. */
void *protocol;
@@ -223,23 +215,6 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
return sfc & 1;
}
-static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
- struct amdtp_stream *master,
- struct amdtp_stream *slave)
-{
- if (sync_mode == CIP_SYNC_TO_DEVICE) {
- master->flags |= CIP_SYNC_TO_DEVICE;
- slave->flags |= CIP_SYNC_TO_DEVICE;
- master->sync_slave = slave;
- } else {
- master->flags &= ~CIP_SYNC_TO_DEVICE;
- slave->flags &= ~CIP_SYNC_TO_DEVICE;
- master->sync_slave = NULL;
- }
-
- slave->sync_slave = NULL;
-}
-
/**
* amdtp_stream_wait_callback - sleep till callbacked or timeout
* @s: the AMDTP stream
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 3e4e0756e..f7e2cbd2a 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -67,7 +67,7 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
#define MODEL_MAUDIO_PROJECTMIX 0x00010091
static int
-name_device(struct snd_bebob *bebob, unsigned int vendor_id)
+name_device(struct snd_bebob *bebob)
{
struct fw_device *fw_dev = fw_parent_device(bebob->unit);
char vendor[24] = {0};
@@ -126,6 +126,17 @@ end:
return err;
}
+static void bebob_free(struct snd_bebob *bebob)
+{
+ snd_bebob_stream_destroy_duplex(bebob);
+ fw_unit_put(bebob->unit);
+
+ kfree(bebob->maudio_special_quirk);
+
+ mutex_destroy(&bebob->mutex);
+ kfree(bebob);
+}
+
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
@@ -137,18 +148,11 @@ bebob_card_free(struct snd_card *card)
{
struct snd_bebob *bebob = card->private_data;
- snd_bebob_stream_destroy_duplex(bebob);
- fw_unit_put(bebob->unit);
-
- kfree(bebob->maudio_special_quirk);
-
- if (bebob->card_index >= 0) {
- mutex_lock(&devices_mutex);
- clear_bit(bebob->card_index, devices_used);
- mutex_unlock(&devices_mutex);
- }
+ mutex_lock(&devices_mutex);
+ clear_bit(bebob->card_index, devices_used);
+ mutex_unlock(&devices_mutex);
- mutex_destroy(&bebob->mutex);
+ bebob_free(card->private_data);
}
static const struct snd_bebob_spec *
@@ -176,16 +180,17 @@ check_audiophile_booted(struct fw_unit *unit)
return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
}
-static int
-bebob_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_bebob *bebob;
- const struct snd_bebob_spec *spec;
+ struct snd_bebob *bebob =
+ container_of(work, struct snd_bebob, dwork.work);
unsigned int card_index;
int err;
+ if (bebob->registered)
+ return;
+
mutex_lock(&devices_mutex);
for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
@@ -193,64 +198,39 @@ bebob_probe(struct fw_unit *unit,
break;
}
if (card_index >= SNDRV_CARDS) {
- err = -ENOENT;
- goto end;
+ mutex_unlock(&devices_mutex);
+ return;
}
- if ((entry->vendor_id == VEN_FOCUSRITE) &&
- (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
- spec = get_saffire_spec(unit);
- else if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
- !check_audiophile_booted(unit))
- spec = NULL;
- else
- spec = (const struct snd_bebob_spec *)entry->driver_data;
-
- if (spec == NULL) {
- if ((entry->vendor_id == VEN_MAUDIO1) ||
- (entry->vendor_id == VEN_MAUDIO2))
- err = snd_bebob_maudio_load_firmware(unit);
- else
- err = -ENOSYS;
- goto end;
+ err = snd_card_new(&bebob->unit->device, index[card_index],
+ id[card_index], THIS_MODULE, 0, &bebob->card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return;
}
- err = snd_card_new(&unit->device, index[card_index], id[card_index],
- THIS_MODULE, sizeof(struct snd_bebob), &card);
+ err = name_device(bebob);
if (err < 0)
- goto end;
- bebob = card->private_data;
- bebob->card_index = card_index;
- set_bit(card_index, devices_used);
- card->private_free = bebob_card_free;
-
- bebob->card = card;
- bebob->unit = fw_unit_get(unit);
- bebob->spec = spec;
- mutex_init(&bebob->mutex);
- spin_lock_init(&bebob->lock);
- init_waitqueue_head(&bebob->hwdep_wait);
+ goto error;
- err = name_device(bebob, entry->vendor_id);
+ if (bebob->spec == &maudio_special_spec) {
+ if (bebob->entry->model_id == MODEL_MAUDIO_FW1814)
+ err = snd_bebob_maudio_special_discover(bebob, true);
+ else
+ err = snd_bebob_maudio_special_discover(bebob, false);
+ } else {
+ err = snd_bebob_stream_discover(bebob);
+ }
if (err < 0)
goto error;
- if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_FW1814))
- err = snd_bebob_maudio_special_discover(bebob, true);
- else if ((entry->vendor_id == VEN_MAUDIO1) &&
- (entry->model_id == MODEL_MAUDIO_PROJECTMIX))
- err = snd_bebob_maudio_special_discover(bebob, false);
- else
- err = snd_bebob_stream_discover(bebob);
+ err = snd_bebob_stream_init_duplex(bebob);
if (err < 0)
goto error;
snd_bebob_proc_init(bebob);
- if ((bebob->midi_input_ports > 0) ||
- (bebob->midi_output_ports > 0)) {
+ if (bebob->midi_input_ports > 0 || bebob->midi_output_ports > 0) {
err = snd_bebob_create_midi_devices(bebob);
if (err < 0)
goto error;
@@ -264,16 +244,75 @@ bebob_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_bebob_stream_init_duplex(bebob);
+ err = snd_card_register(bebob->card);
if (err < 0)
goto error;
- if (!bebob->maudio_special_quirk) {
- err = snd_card_register(card);
- if (err < 0) {
- snd_bebob_stream_destroy_duplex(bebob);
- goto error;
- }
+ set_bit(card_index, devices_used);
+ mutex_unlock(&devices_mutex);
+
+ /*
+ * After registered, bebob instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ bebob->card->private_free = bebob_card_free;
+ bebob->card->private_data = bebob;
+ bebob->registered = true;
+
+ return;
+error:
+ mutex_unlock(&devices_mutex);
+ snd_bebob_stream_destroy_duplex(bebob);
+ snd_card_free(bebob->card);
+ dev_info(&bebob->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int
+bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_bebob *bebob;
+ const struct snd_bebob_spec *spec;
+
+ if (entry->vendor_id == VEN_FOCUSRITE &&
+ entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)
+ spec = get_saffire_spec(unit);
+ else if (entry->vendor_id == VEN_MAUDIO1 &&
+ entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH &&
+ !check_audiophile_booted(unit))
+ spec = NULL;
+ else
+ spec = (const struct snd_bebob_spec *)entry->driver_data;
+
+ if (spec == NULL) {
+ if (entry->vendor_id == VEN_MAUDIO1 ||
+ entry->vendor_id == VEN_MAUDIO2)
+ return snd_bebob_maudio_load_firmware(unit);
+ else
+ return -ENODEV;
+ }
+
+ /* Allocate this independent of sound card instance. */
+ bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL);
+ if (bebob == NULL)
+ return -ENOMEM;
+
+ bebob->unit = fw_unit_get(unit);
+ bebob->entry = entry;
+ bebob->spec = spec;
+ dev_set_drvdata(&unit->device, bebob);
+
+ mutex_init(&bebob->mutex);
+ spin_lock_init(&bebob->lock);
+ init_waitqueue_head(&bebob->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&bebob->dwork, do_registration);
+
+ if (entry->vendor_id != VEN_MAUDIO1 ||
+ (entry->model_id != MODEL_MAUDIO_FW1814 &&
+ entry->model_id != MODEL_MAUDIO_PROJECTMIX)) {
+ snd_fw_schedule_registration(unit, &bebob->dwork);
} else {
/*
* This is a workaround. This bus reset seems to have an effect
@@ -285,19 +324,11 @@ bebob_probe(struct fw_unit *unit,
* signals from dbus and starts I/Os. To avoid I/Os till the
* future bus reset, registration is done in next update().
*/
- bebob->deferred_registration = true;
fw_schedule_bus_reset(fw_parent_device(bebob->unit)->card,
false, true);
}
- dev_set_drvdata(&unit->device, bebob);
-end:
- mutex_unlock(&devices_mutex);
- return err;
-error:
- mutex_unlock(&devices_mutex);
- snd_card_free(card);
- return err;
+ return 0;
}
/*
@@ -324,15 +355,11 @@ bebob_update(struct fw_unit *unit)
if (bebob == NULL)
return;
- fcp_bus_reset(bebob->unit);
-
- if (bebob->deferred_registration) {
- if (snd_card_register(bebob->card) < 0) {
- snd_bebob_stream_destroy_duplex(bebob);
- snd_card_free(bebob->card);
- }
- bebob->deferred_registration = false;
- }
+ /* Postpone a workqueue for deferred registration. */
+ if (!bebob->registered)
+ snd_fw_schedule_registration(unit, &bebob->dwork);
+ else
+ fcp_bus_reset(bebob->unit);
}
static void bebob_remove(struct fw_unit *unit)
@@ -342,8 +369,20 @@ static void bebob_remove(struct fw_unit *unit)
if (bebob == NULL)
return;
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(bebob->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&bebob->dwork);
+
+ if (bebob->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(bebob->card);
+ } else {
+ /* Don't forget this case. */
+ bebob_free(bebob);
+ }
}
static const struct snd_bebob_rate_spec normal_rate_spec = {
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index b50bb33d9..e7f1bb925 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -83,6 +83,10 @@ struct snd_bebob {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
+ const struct ieee1394_device_id *entry;
const struct snd_bebob_spec *spec;
unsigned int midi_input_ports;
@@ -90,7 +94,6 @@ struct snd_bebob {
bool connected;
- struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
@@ -111,7 +114,6 @@ struct snd_bebob {
/* for M-Audio special devices */
void *maudio_special_quirk;
- bool deferred_registration;
/* For BeBoB version quirk. */
unsigned int version;
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 77cbb02bf..4d3034a68 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -484,30 +484,6 @@ destroy_both_connections(struct snd_bebob *bebob)
}
static int
-get_sync_mode(struct snd_bebob *bebob, enum cip_flags *sync_mode)
-{
- enum snd_bebob_clock_type src;
- int err;
-
- err = snd_bebob_stream_get_clock_src(bebob, &src);
- if (err < 0)
- return err;
-
- switch (src) {
- case SND_BEBOB_CLOCK_TYPE_INTERNAL:
- case SND_BEBOB_CLOCK_TYPE_EXTERNAL:
- *sync_mode = CIP_SYNC_TO_DEVICE;
- break;
- default:
- case SND_BEBOB_CLOCK_TYPE_SYT:
- *sync_mode = 0;
- break;
- }
-
- return 0;
-}
-
-static int
start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
unsigned int rate)
{
@@ -550,8 +526,6 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
goto end;
}
- bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
-
/*
* BeBoB v3 transfers packets with these qurks:
* - In the beginning of streaming, the value of dbc is incremented
@@ -584,8 +558,6 @@ end:
int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
{
const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
- struct amdtp_stream *master, *slave;
- enum cip_flags sync_mode;
unsigned int curr_rate;
int err = 0;
@@ -593,22 +565,11 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (bebob->substreams_counter == 0)
goto end;
- err = get_sync_mode(bebob, &sync_mode);
- if (err < 0)
- goto end;
- if (sync_mode == CIP_SYNC_TO_DEVICE) {
- master = &bebob->tx_stream;
- slave = &bebob->rx_stream;
- } else {
- master = &bebob->rx_stream;
- slave = &bebob->tx_stream;
- }
-
/*
* Considering JACK/FFADO streaming:
* TODO: This can be removed hwdep functionality becomes popular.
*/
- err = check_connection_used_by_others(bebob, master);
+ err = check_connection_used_by_others(bebob, &bebob->rx_stream);
if (err < 0)
goto end;
@@ -618,11 +579,12 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
* At bus reset, connections should not be broken here. So streams need
* to be re-started. This is a reason to use SKIP_INIT_DBC_CHECK flag.
*/
- if (amdtp_streaming_error(master))
- amdtp_stream_stop(master);
- if (amdtp_streaming_error(slave))
- amdtp_stream_stop(slave);
- if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
+ if (amdtp_streaming_error(&bebob->rx_stream))
+ amdtp_stream_stop(&bebob->rx_stream);
+ if (amdtp_streaming_error(&bebob->tx_stream))
+ amdtp_stream_stop(&bebob->tx_stream);
+ if (!amdtp_stream_running(&bebob->rx_stream) &&
+ !amdtp_stream_running(&bebob->tx_stream))
break_both_connections(bebob);
/* stop streams if rate is different */
@@ -635,16 +597,13 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
- amdtp_stream_stop(master);
- amdtp_stream_stop(slave);
+ amdtp_stream_stop(&bebob->rx_stream);
+ amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob);
}
/* master should be always running */
- if (!amdtp_stream_running(master)) {
- amdtp_stream_set_sync(sync_mode, master, slave);
- bebob->master = master;
-
+ if (!amdtp_stream_running(&bebob->rx_stream)) {
/*
* NOTE:
* If establishing connections at first, Yamaha GO46
@@ -666,7 +625,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
if (err < 0)
goto end;
- err = start_stream(bebob, master, rate);
+ err = start_stream(bebob, &bebob->rx_stream, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to run AMDTP master stream:%d\n", err);
@@ -685,15 +644,16 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
dev_err(&bebob->unit->device,
"fail to ensure sampling rate: %d\n",
err);
- amdtp_stream_stop(master);
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
goto end;
}
}
/* wait first callback */
- if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(master);
+ if (!amdtp_stream_wait_callback(&bebob->rx_stream,
+ CALLBACK_TIMEOUT)) {
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
err = -ETIMEDOUT;
goto end;
@@ -701,20 +661,21 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
}
/* start slave if needed */
- if (!amdtp_stream_running(slave)) {
- err = start_stream(bebob, slave, rate);
+ if (!amdtp_stream_running(&bebob->tx_stream)) {
+ err = start_stream(bebob, &bebob->tx_stream, rate);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to run AMDTP slave stream:%d\n", err);
- amdtp_stream_stop(master);
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
goto end;
}
/* wait first callback */
- if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
- amdtp_stream_stop(slave);
- amdtp_stream_stop(master);
+ if (!amdtp_stream_wait_callback(&bebob->tx_stream,
+ CALLBACK_TIMEOUT)) {
+ amdtp_stream_stop(&bebob->tx_stream);
+ amdtp_stream_stop(&bebob->rx_stream);
break_both_connections(bebob);
err = -ETIMEDOUT;
}
@@ -725,22 +686,12 @@ end:
void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
{
- struct amdtp_stream *master, *slave;
-
- if (bebob->master == &bebob->rx_stream) {
- slave = &bebob->tx_stream;
- master = &bebob->rx_stream;
- } else {
- slave = &bebob->rx_stream;
- master = &bebob->tx_stream;
- }
-
if (bebob->substreams_counter == 0) {
- amdtp_stream_pcm_abort(master);
- amdtp_stream_stop(master);
+ amdtp_stream_pcm_abort(&bebob->rx_stream);
+ amdtp_stream_stop(&bebob->rx_stream);
- amdtp_stream_pcm_abort(slave);
- amdtp_stream_stop(slave);
+ amdtp_stream_pcm_abort(&bebob->tx_stream);
+ amdtp_stream_stop(&bebob->tx_stream);
break_both_connections(bebob);
}
diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c
index 8b64aef31..25e9f7727 100644
--- a/sound/firewire/dice/dice.c
+++ b/sound/firewire/dice/dice.c
@@ -20,8 +20,6 @@ MODULE_LICENSE("GPL v2");
#define WEISS_CATEGORY_ID 0x00
#define LOUD_CATEGORY_ID 0x10
-#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
-
/*
* Some models support several isochronous channels, while these streams are not
* always available. In this case, add the model name to this list.
@@ -201,6 +199,10 @@ static void do_registration(struct work_struct *work)
dice_card_strings(dice);
+ err = snd_dice_stream_init_duplex(dice);
+ if (err < 0)
+ goto error;
+
snd_dice_create_proc(dice);
err = snd_dice_create_pcm(dice);
@@ -229,28 +231,14 @@ static void do_registration(struct work_struct *work)
return;
error:
+ snd_dice_stream_destroy_duplex(dice);
snd_dice_transaction_destroy(dice);
+ snd_dice_stream_destroy_duplex(dice);
snd_card_free(dice->card);
dev_info(&dice->unit->device,
"Sound card registration failed: %d\n", err);
}
-static void schedule_registration(struct snd_dice *dice)
-{
- struct fw_card *fw_card = fw_parent_device(dice->unit)->card;
- u64 now, delay;
-
- now = get_jiffies_64();
- delay = fw_card->reset_jiffies + msecs_to_jiffies(PROBE_DELAY_MS);
-
- if (time_after64(delay, now))
- delay -= now;
- else
- delay = 0;
-
- mod_delayed_work(system_wq, &dice->dwork, delay);
-}
-
static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
{
struct snd_dice *dice;
@@ -273,15 +261,9 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
init_completion(&dice->clock_accepted);
init_waitqueue_head(&dice->hwdep_wait);
- err = snd_dice_stream_init_duplex(dice);
- if (err < 0) {
- dice_free(dice);
- return err;
- }
-
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&dice->dwork, do_registration);
- schedule_registration(dice);
+ snd_fw_schedule_registration(unit, &dice->dwork);
return 0;
}
@@ -312,7 +294,7 @@ static void dice_bus_reset(struct fw_unit *unit)
/* Postpone a workqueue for deferred registration. */
if (!dice->registered)
- schedule_registration(dice);
+ snd_fw_schedule_registration(unit, &dice->dwork);
/* The handler address register becomes initialized. */
snd_dice_transaction_reinit(dice);
@@ -335,6 +317,13 @@ static const struct ieee1394_device_id dice_id_table[] = {
.match_flags = IEEE1394_MATCH_VERSION,
.version = DICE_INTERFACE,
},
+ /* M-Audio Profire 610/2626 has a different value in version field. */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID,
+ .vendor_id = 0x000d6c,
+ .specifier_id = 0x000d6c,
+ },
{ }
};
MODULE_DEVICE_TABLE(ieee1394, dice_id_table);
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index 0ac92aba5..b3cffd01a 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -421,7 +421,7 @@ int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
/* Use different mode between incoming/outgoing. */
if (dir == AMDTP_IN_STREAM) {
- flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+ flags = CIP_NONBLOCKING;
process_data_blocks = process_tx_data_blocks;
} else {
flags = CIP_BLOCKING;
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
index 554324d8c..735d35640 100644
--- a/sound/firewire/digi00x/digi00x-transaction.c
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -126,12 +126,17 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
return err;
error:
fw_core_remove_address_handler(&dg00x->async_handler);
- dg00x->async_handler.address_callback = NULL;
+ dg00x->async_handler.callback_data = NULL;
return err;
}
void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
{
+ if (dg00x->async_handler.callback_data == NULL)
+ return;
+
snd_fw_async_midi_port_destroy(&dg00x->out_control);
fw_core_remove_address_handler(&dg00x->async_handler);
+
+ dg00x->async_handler.callback_data = NULL;
}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index 1f33b7a1f..cc4776c6d 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -40,10 +40,8 @@ static int name_card(struct snd_dg00x *dg00x)
return 0;
}
-static void dg00x_card_free(struct snd_card *card)
+static void dg00x_free(struct snd_dg00x *dg00x)
{
- struct snd_dg00x *dg00x = card->private_data;
-
snd_dg00x_stream_destroy_duplex(dg00x);
snd_dg00x_transaction_unregister(dg00x);
@@ -52,28 +50,24 @@ static void dg00x_card_free(struct snd_card *card)
mutex_destroy(&dg00x->mutex);
}
-static int snd_dg00x_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void dg00x_card_free(struct snd_card *card)
{
- struct snd_card *card;
- struct snd_dg00x *dg00x;
- int err;
+ dg00x_free(card->private_data);
+}
- /* create card */
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(struct snd_dg00x), &card);
- if (err < 0)
- return err;
- card->private_free = dg00x_card_free;
+static void do_registration(struct work_struct *work)
+{
+ struct snd_dg00x *dg00x =
+ container_of(work, struct snd_dg00x, dwork.work);
+ int err;
- /* initialize myself */
- dg00x = card->private_data;
- dg00x->card = card;
- dg00x->unit = fw_unit_get(unit);
+ if (dg00x->registered)
+ return;
- mutex_init(&dg00x->mutex);
- spin_lock_init(&dg00x->lock);
- init_waitqueue_head(&dg00x->hwdep_wait);
+ err = snd_card_new(&dg00x->unit->device, -1, NULL, THIS_MODULE, 0,
+ &dg00x->card);
+ if (err < 0)
+ return;
err = name_card(dg00x);
if (err < 0)
@@ -101,35 +95,86 @@ static int snd_dg00x_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_card_register(card);
+ err = snd_card_register(dg00x->card);
if (err < 0)
goto error;
- dev_set_drvdata(&unit->device, dg00x);
+ dg00x->card->private_free = dg00x_card_free;
+ dg00x->card->private_data = dg00x;
+ dg00x->registered = true;
- return err;
+ return;
error:
- snd_card_free(card);
- return err;
+ snd_dg00x_transaction_unregister(dg00x);
+ snd_dg00x_stream_destroy_duplex(dg00x);
+ snd_card_free(dg00x->card);
+ dev_info(&dg00x->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_dg00x *dg00x;
+
+ /* Allocate this independent of sound card instance. */
+ dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL);
+ if (dg00x == NULL)
+ return -ENOMEM;
+
+ dg00x->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, dg00x);
+
+ mutex_init(&dg00x->mutex);
+ spin_lock_init(&dg00x->lock);
+ init_waitqueue_head(&dg00x->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &dg00x->dwork);
+
+ return 0;
}
static void snd_dg00x_update(struct fw_unit *unit)
{
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+ /* Postpone a workqueue for deferred registration. */
+ if (!dg00x->registered)
+ snd_fw_schedule_registration(unit, &dg00x->dwork);
+
snd_dg00x_transaction_reregister(dg00x);
- mutex_lock(&dg00x->mutex);
- snd_dg00x_stream_update_duplex(dg00x);
- mutex_unlock(&dg00x->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * code block works fine.
+ */
+ if (dg00x->registered) {
+ mutex_lock(&dg00x->mutex);
+ snd_dg00x_stream_update_duplex(dg00x);
+ mutex_unlock(&dg00x->mutex);
+ }
}
static void snd_dg00x_remove(struct fw_unit *unit)
{
struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(dg00x->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&dg00x->dwork);
+
+ if (dg00x->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(dg00x->card);
+ } else {
+ /* Don't forget this case. */
+ dg00x_free(dg00x);
+ }
}
static const struct ieee1394_device_id snd_dg00x_id_table[] = {
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 907e73993..2cd465c0c 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -37,6 +37,9 @@ struct snd_dg00x {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
struct amdtp_stream tx_stream;
struct fw_iso_resources tx_resources;
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 8f27b6750..71a0613d3 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -168,11 +168,34 @@ get_hardware_info(struct snd_efw *efw)
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_in_grp_count);
memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
sizeof(struct snd_efw_phys_grp) * hwinfo->phys_out_grp_count);
+
+ /* AudioFire8 (since 2009) and AudioFirePre8 */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_9)
+ efw->is_af9 = true;
+ /* These models uses the same firmware. */
+ if (hwinfo->type == MODEL_ECHO_AUDIOFIRE_2 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_4 ||
+ hwinfo->type == MODEL_ECHO_AUDIOFIRE_9 ||
+ hwinfo->type == MODEL_GIBSON_RIP ||
+ hwinfo->type == MODEL_GIBSON_GOLDTOP)
+ efw->is_fireworks3 = true;
end:
kfree(hwinfo);
return err;
}
+static void efw_free(struct snd_efw *efw)
+{
+ snd_efw_stream_destroy_duplex(efw);
+ snd_efw_transaction_remove_instance(efw);
+ fw_unit_put(efw->unit);
+
+ kfree(efw->resp_buf);
+
+ mutex_destroy(&efw->mutex);
+ kfree(efw);
+}
+
/*
* This module releases the FireWire unit data after all ALSA character devices
* are released by applications. This is for releasing stream data or finishing
@@ -184,28 +207,24 @@ efw_card_free(struct snd_card *card)
{
struct snd_efw *efw = card->private_data;
- snd_efw_stream_destroy_duplex(efw);
- snd_efw_transaction_remove_instance(efw);
- fw_unit_put(efw->unit);
-
- kfree(efw->resp_buf);
-
if (efw->card_index >= 0) {
mutex_lock(&devices_mutex);
clear_bit(efw->card_index, devices_used);
mutex_unlock(&devices_mutex);
}
- mutex_destroy(&efw->mutex);
+ efw_free(card->private_data);
}
-static int
-efw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void
+do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_efw *efw;
- int card_index, err;
+ struct snd_efw *efw = container_of(work, struct snd_efw, dwork.work);
+ unsigned int card_index;
+ int err;
+
+ if (efw->registered)
+ return;
mutex_lock(&devices_mutex);
@@ -215,24 +234,16 @@ efw_probe(struct fw_unit *unit,
break;
}
if (card_index >= SNDRV_CARDS) {
- err = -ENOENT;
- goto end;
+ mutex_unlock(&devices_mutex);
+ return;
}
- err = snd_card_new(&unit->device, index[card_index], id[card_index],
- THIS_MODULE, sizeof(struct snd_efw), &card);
- if (err < 0)
- goto end;
- efw = card->private_data;
- efw->card_index = card_index;
- set_bit(card_index, devices_used);
- card->private_free = efw_card_free;
-
- efw->card = card;
- efw->unit = fw_unit_get(unit);
- mutex_init(&efw->mutex);
- spin_lock_init(&efw->lock);
- init_waitqueue_head(&efw->hwdep_wait);
+ err = snd_card_new(&efw->unit->device, index[card_index],
+ id[card_index], THIS_MODULE, 0, &efw->card);
+ if (err < 0) {
+ mutex_unlock(&devices_mutex);
+ return;
+ }
/* prepare response buffer */
snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size,
@@ -248,16 +259,10 @@ efw_probe(struct fw_unit *unit,
err = get_hardware_info(efw);
if (err < 0)
goto error;
- /* AudioFire8 (since 2009) and AudioFirePre8 */
- if (entry->model_id == MODEL_ECHO_AUDIOFIRE_9)
- efw->is_af9 = true;
- /* These models uses the same firmware. */
- if (entry->model_id == MODEL_ECHO_AUDIOFIRE_2 ||
- entry->model_id == MODEL_ECHO_AUDIOFIRE_4 ||
- entry->model_id == MODEL_ECHO_AUDIOFIRE_9 ||
- entry->model_id == MODEL_GIBSON_RIP ||
- entry->model_id == MODEL_GIBSON_GOLDTOP)
- efw->is_fireworks3 = true;
+
+ err = snd_efw_stream_init_duplex(efw);
+ if (err < 0)
+ goto error;
snd_efw_proc_init(efw);
@@ -275,44 +280,93 @@ efw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_efw_stream_init_duplex(efw);
+ err = snd_card_register(efw->card);
if (err < 0)
goto error;
- err = snd_card_register(card);
- if (err < 0) {
- snd_efw_stream_destroy_duplex(efw);
- goto error;
- }
-
- dev_set_drvdata(&unit->device, efw);
-end:
+ set_bit(card_index, devices_used);
mutex_unlock(&devices_mutex);
- return err;
+
+ /*
+ * After registered, efw instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ efw->card->private_free = efw_card_free;
+ efw->card->private_data = efw;
+ efw->registered = true;
+
+ return;
error:
- snd_efw_transaction_remove_instance(efw);
mutex_unlock(&devices_mutex);
- snd_card_free(card);
- return err;
+ snd_efw_transaction_remove_instance(efw);
+ snd_efw_stream_destroy_duplex(efw);
+ snd_card_free(efw->card);
+ dev_info(&efw->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int
+efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry)
+{
+ struct snd_efw *efw;
+
+ efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL);
+ if (efw == NULL)
+ return -ENOMEM;
+
+ efw->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, efw);
+
+ mutex_init(&efw->mutex);
+ spin_lock_init(&efw->lock);
+ init_waitqueue_head(&efw->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&efw->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &efw->dwork);
+
+ return 0;
}
static void efw_update(struct fw_unit *unit)
{
struct snd_efw *efw = dev_get_drvdata(&unit->device);
+ /* Postpone a workqueue for deferred registration. */
+ if (!efw->registered)
+ snd_fw_schedule_registration(unit, &efw->dwork);
+
snd_efw_transaction_bus_reset(efw->unit);
- mutex_lock(&efw->mutex);
- snd_efw_stream_update_duplex(efw);
- mutex_unlock(&efw->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * code block works fine.
+ */
+ if (efw->registered) {
+ mutex_lock(&efw->mutex);
+ snd_efw_stream_update_duplex(efw);
+ mutex_unlock(&efw->mutex);
+ }
}
static void efw_remove(struct fw_unit *unit)
{
struct snd_efw *efw = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(efw->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&efw->dwork);
+
+ if (efw->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(efw->card);
+ } else {
+ /* Don't forget this case. */
+ efw_free(efw);
+ }
}
static const struct ieee1394_device_id efw_id_table[] = {
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 96c4e0c6a..03ed35237 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -65,6 +65,9 @@ struct snd_efw {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
/* for transaction */
u32 seqnum;
bool resp_addr_changable;
@@ -81,7 +84,6 @@ struct snd_efw {
unsigned int pcm_capture_channels[SND_EFW_MULTIPLIER_MODES];
unsigned int pcm_playback_channels[SND_EFW_MULTIPLIER_MODES];
- struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 425db8d88..ee47924ae 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -121,23 +121,6 @@ destroy_stream(struct snd_efw *efw, struct amdtp_stream *stream)
}
static int
-get_sync_mode(struct snd_efw *efw, enum cip_flags *sync_mode)
-{
- enum snd_efw_clock_source clock_source;
- int err;
-
- err = snd_efw_command_get_clock_source(efw, &clock_source);
- if (err < 0)
- return err;
-
- if (clock_source == SND_EFW_CLOCK_SOURCE_SYTMATCH)
- return -ENOSYS;
-
- *sync_mode = CIP_SYNC_TO_DEVICE;
- return 0;
-}
-
-static int
check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
{
struct cmp_connection *conn;
@@ -208,9 +191,6 @@ end:
int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
{
- struct amdtp_stream *master, *slave;
- unsigned int slave_substreams;
- enum cip_flags sync_mode;
unsigned int curr_rate;
int err = 0;
@@ -218,32 +198,19 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
if (efw->playback_substreams == 0 && efw->capture_substreams == 0)
goto end;
- err = get_sync_mode(efw, &sync_mode);
- if (err < 0)
- goto end;
- if (sync_mode == CIP_SYNC_TO_DEVICE) {
- master = &efw->tx_stream;
- slave = &efw->rx_stream;
- slave_substreams = efw->playback_substreams;
- } else {
- master = &efw->rx_stream;
- slave = &efw->tx_stream;
- slave_substreams = efw->capture_substreams;
- }
-
/*
* Considering JACK/FFADO streaming:
* TODO: This can be removed hwdep functionality becomes popular.
*/
- err = check_connection_used_by_others(efw, master);
+ err = check_connection_used_by_others(efw, &efw->rx_stream);
if (err < 0)
goto end;
/* packet queueing error */
- if (amdtp_streaming_error(slave))
- stop_stream(efw, slave);
- if (amdtp_streaming_error(master))
- stop_stream(efw, master);
+ if (amdtp_streaming_error(&efw->tx_stream))
+ stop_stream(efw, &efw->tx_stream);
+ if (amdtp_streaming_error(&efw->rx_stream))
+ stop_stream(efw, &efw->rx_stream);
/* stop streams if rate is different */
err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
@@ -252,20 +219,17 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
if (rate == 0)
rate = curr_rate;
if (rate != curr_rate) {
- stop_stream(efw, slave);
- stop_stream(efw, master);
+ stop_stream(efw, &efw->tx_stream);
+ stop_stream(efw, &efw->rx_stream);
}
/* master should be always running */
- if (!amdtp_stream_running(master)) {
- amdtp_stream_set_sync(sync_mode, master, slave);
- efw->master = master;
-
+ if (!amdtp_stream_running(&efw->rx_stream)) {
err = snd_efw_command_set_sampling_rate(efw, rate);
if (err < 0)
goto end;
- err = start_stream(efw, master, rate);
+ err = start_stream(efw, &efw->rx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP master stream:%d\n", err);
@@ -274,12 +238,13 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw, unsigned int rate)
}
/* start slave if needed */
- if (slave_substreams > 0 && !amdtp_stream_running(slave)) {
- err = start_stream(efw, slave, rate);
+ if (efw->capture_substreams > 0 &&
+ !amdtp_stream_running(&efw->tx_stream)) {
+ err = start_stream(efw, &efw->tx_stream, rate);
if (err < 0) {
dev_err(&efw->unit->device,
"fail to start AMDTP slave stream:%d\n", err);
- stop_stream(efw, master);
+ stop_stream(efw, &efw->rx_stream);
}
}
end:
@@ -288,26 +253,11 @@ end:
void snd_efw_stream_stop_duplex(struct snd_efw *efw)
{
- struct amdtp_stream *master, *slave;
- unsigned int master_substreams, slave_substreams;
-
- if (efw->master == &efw->rx_stream) {
- slave = &efw->tx_stream;
- master = &efw->rx_stream;
- slave_substreams = efw->capture_substreams;
- master_substreams = efw->playback_substreams;
- } else {
- slave = &efw->rx_stream;
- master = &efw->tx_stream;
- slave_substreams = efw->playback_substreams;
- master_substreams = efw->capture_substreams;
- }
-
- if (slave_substreams == 0) {
- stop_stream(efw, slave);
+ if (efw->capture_substreams == 0) {
+ stop_stream(efw, &efw->tx_stream);
- if (master_substreams == 0)
- stop_stream(efw, master);
+ if (efw->playback_substreams == 0)
+ stop_stream(efw, &efw->rx_stream);
}
}
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index f80aafa44..ca4dfcf43 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -67,6 +67,38 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
}
EXPORT_SYMBOL(snd_fw_transaction);
+#define PROBE_DELAY_MS (2 * MSEC_PER_SEC)
+
+/**
+ * snd_fw_schedule_registration - schedule work for sound card registration
+ * @unit: an instance for unit on IEEE 1394 bus
+ * @dwork: delayed work with callback function
+ *
+ * This function is not designed for general purposes. When new unit is
+ * connected to IEEE 1394 bus, the bus is under bus-reset state because of
+ * topological change. In this state, units tend to fail both of asynchronous
+ * and isochronous communication. To avoid this problem, this function is used
+ * to postpone sound card registration after the state. The callers must
+ * set up instance of delayed work in advance.
+ */
+void snd_fw_schedule_registration(struct fw_unit *unit,
+ struct delayed_work *dwork)
+{
+ u64 now, delay;
+
+ now = get_jiffies_64();
+ delay = fw_parent_device(unit)->card->reset_jiffies
+ + msecs_to_jiffies(PROBE_DELAY_MS);
+
+ if (time_after64(delay, now))
+ delay -= now;
+ else
+ delay = 0;
+
+ mod_delayed_work(system_wq, dwork, delay);
+}
+EXPORT_SYMBOL(snd_fw_schedule_registration);
+
static void async_midi_port_callback(struct fw_card *card, int rcode,
void *data, size_t length,
void *callback_data)
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index f3f6f84c4..f6769312e 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -22,6 +22,9 @@ static inline bool rcode_is_permanent_error(int rcode)
return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
}
+void snd_fw_schedule_registration(struct fw_unit *unit,
+ struct delayed_work *dwork);
+
struct snd_fw_async_midi_port;
typedef int (*snd_fw_async_midi_port_fill)(
struct snd_rawmidi_substream *substream,
diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c
index 7cb5743c0..d9361f352 100644
--- a/sound/firewire/oxfw/oxfw-stream.c
+++ b/sound/firewire/oxfw/oxfw-stream.c
@@ -242,8 +242,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
* blocks than IEC 61883-6 defines.
*/
if (stream == &oxfw->tx_stream) {
- oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
- CIP_JUMBO_PAYLOAD;
+ oxfw->tx_stream.flags |= CIP_JUMBO_PAYLOAD;
if (oxfw->wrong_dbs)
oxfw->tx_stream.flags |= CIP_WRONG_DBS;
}
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index abedc2207..e629b88f7 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -118,15 +118,8 @@ end:
return err;
}
-/*
- * This module releases the FireWire unit data after all ALSA character devices
- * are released by applications. This is for releasing stream data or finishing
- * transactions safely. Thus at returning from .remove(), this module still keep
- * references for the unit.
- */
-static void oxfw_card_free(struct snd_card *card)
+static void oxfw_free(struct snd_oxfw *oxfw)
{
- struct snd_oxfw *oxfw = card->private_data;
unsigned int i;
snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
@@ -144,6 +137,17 @@ static void oxfw_card_free(struct snd_card *card)
mutex_destroy(&oxfw->mutex);
}
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
+static void oxfw_card_free(struct snd_card *card)
+{
+ oxfw_free(card->private_data);
+}
+
static int detect_quirks(struct snd_oxfw *oxfw)
{
struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
@@ -205,41 +209,39 @@ static int detect_quirks(struct snd_oxfw *oxfw)
return 0;
}
-static int oxfw_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void do_registration(struct work_struct *work)
{
- struct snd_card *card;
- struct snd_oxfw *oxfw;
+ struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work);
int err;
- if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
- return -ENODEV;
+ if (oxfw->registered)
+ return;
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(*oxfw), &card);
+ err = snd_card_new(&oxfw->unit->device, -1, NULL, THIS_MODULE, 0,
+ &oxfw->card);
if (err < 0)
- return err;
+ return;
- card->private_free = oxfw_card_free;
- oxfw = card->private_data;
- oxfw->card = card;
- mutex_init(&oxfw->mutex);
- oxfw->unit = fw_unit_get(unit);
- oxfw->entry = entry;
- spin_lock_init(&oxfw->lock);
- init_waitqueue_head(&oxfw->hwdep_wait);
+ err = name_card(oxfw);
+ if (err < 0)
+ goto error;
- err = snd_oxfw_stream_discover(oxfw);
+ err = detect_quirks(oxfw);
if (err < 0)
goto error;
- err = name_card(oxfw);
+ err = snd_oxfw_stream_discover(oxfw);
if (err < 0)
goto error;
- err = detect_quirks(oxfw);
+ err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
if (err < 0)
goto error;
+ if (oxfw->has_output) {
+ err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
+ if (err < 0)
+ goto error;
+ }
err = snd_oxfw_create_pcm(oxfw);
if (err < 0)
@@ -255,54 +257,97 @@ static int oxfw_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->rx_stream);
+ err = snd_card_register(oxfw->card);
if (err < 0)
goto error;
- if (oxfw->has_output) {
- err = snd_oxfw_stream_init_simplex(oxfw, &oxfw->tx_stream);
- if (err < 0)
- goto error;
- }
- err = snd_card_register(card);
- if (err < 0) {
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
- goto error;
- }
+ /*
+ * After registered, oxfw instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ oxfw->card->private_free = oxfw_card_free;
+ oxfw->card->private_data = oxfw;
+ oxfw->registered = true;
+
+ return;
+error:
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream);
+ snd_card_free(oxfw->card);
+ dev_info(&oxfw->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int oxfw_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_oxfw *oxfw;
+
+ if (entry->vendor_id == VENDOR_LOUD && !detect_loud_models(unit))
+ return -ENODEV;
+
+ /* Allocate this independent of sound card instance. */
+ oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL);
+ if (oxfw == NULL)
+ return -ENOMEM;
+
+ oxfw->entry = entry;
+ oxfw->unit = fw_unit_get(unit);
dev_set_drvdata(&unit->device, oxfw);
+ mutex_init(&oxfw->mutex);
+ spin_lock_init(&oxfw->lock);
+ init_waitqueue_head(&oxfw->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&oxfw->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &oxfw->dwork);
+
return 0;
-error:
- snd_card_free(card);
- return err;
}
static void oxfw_bus_reset(struct fw_unit *unit)
{
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
+ if (!oxfw->registered)
+ snd_fw_schedule_registration(unit, &oxfw->dwork);
+
fcp_bus_reset(oxfw->unit);
- mutex_lock(&oxfw->mutex);
+ if (oxfw->registered) {
+ mutex_lock(&oxfw->mutex);
- snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
- if (oxfw->has_output)
- snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->rx_stream);
+ if (oxfw->has_output)
+ snd_oxfw_stream_update_simplex(oxfw, &oxfw->tx_stream);
- mutex_unlock(&oxfw->mutex);
+ mutex_unlock(&oxfw->mutex);
- if (oxfw->entry->vendor_id == OUI_STANTON)
- snd_oxfw_scs1x_update(oxfw);
+ if (oxfw->entry->vendor_id == OUI_STANTON)
+ snd_oxfw_scs1x_update(oxfw);
+ }
}
static void oxfw_remove(struct fw_unit *unit)
{
struct snd_oxfw *oxfw = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(oxfw->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&oxfw->dwork);
+
+ if (oxfw->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(oxfw->card);
+ } else {
+ /* Don't forget this case. */
+ oxfw_free(oxfw);
+ }
}
static const struct compat_info griffin_firewave = {
diff --git a/sound/firewire/oxfw/oxfw.h b/sound/firewire/oxfw/oxfw.h
index 9beecc214..2047dcb27 100644
--- a/sound/firewire/oxfw/oxfw.h
+++ b/sound/firewire/oxfw/oxfw.h
@@ -36,10 +36,12 @@
struct snd_oxfw {
struct snd_card *card;
struct fw_unit *unit;
- const struct device_info *device_info;
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
+
bool wrong_dbs;
bool has_output;
u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
index 0e6dd5c61..4ad3bd7fd 100644
--- a/sound/firewire/tascam/tascam-stream.c
+++ b/sound/firewire/tascam/tascam-stream.c
@@ -381,19 +381,17 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
return err;
if (curr_rate != rate ||
- amdtp_streaming_error(&tscm->tx_stream) ||
- amdtp_streaming_error(&tscm->rx_stream)) {
+ amdtp_streaming_error(&tscm->rx_stream) ||
+ amdtp_streaming_error(&tscm->tx_stream)) {
finish_session(tscm);
- amdtp_stream_stop(&tscm->tx_stream);
amdtp_stream_stop(&tscm->rx_stream);
+ amdtp_stream_stop(&tscm->tx_stream);
release_resources(tscm);
}
- if (!amdtp_stream_running(&tscm->tx_stream)) {
- amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
- &tscm->tx_stream, &tscm->rx_stream);
+ if (!amdtp_stream_running(&tscm->rx_stream)) {
err = keep_resources(tscm, rate);
if (err < 0)
goto error;
@@ -406,27 +404,27 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
if (err < 0)
goto error;
- err = amdtp_stream_start(&tscm->tx_stream,
- tscm->tx_resources.channel,
+ err = amdtp_stream_start(&tscm->rx_stream,
+ tscm->rx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+ if (!amdtp_stream_wait_callback(&tscm->rx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
}
}
- if (!amdtp_stream_running(&tscm->rx_stream)) {
- err = amdtp_stream_start(&tscm->rx_stream,
- tscm->rx_resources.channel,
+ if (!amdtp_stream_running(&tscm->tx_stream)) {
+ err = amdtp_stream_start(&tscm->tx_stream,
+ tscm->tx_resources.channel,
fw_parent_device(tscm->unit)->max_speed);
if (err < 0)
goto error;
- if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+ if (!amdtp_stream_wait_callback(&tscm->tx_stream,
CALLBACK_TIMEOUT)) {
err = -ETIMEDOUT;
goto error;
@@ -435,8 +433,8 @@ int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
return 0;
error:
- amdtp_stream_stop(&tscm->tx_stream);
amdtp_stream_stop(&tscm->rx_stream);
+ amdtp_stream_stop(&tscm->tx_stream);
finish_session(tscm);
release_resources(tscm);
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
index e281c338e..9dc93a7eb 100644
--- a/sound/firewire/tascam/tascam.c
+++ b/sound/firewire/tascam/tascam.c
@@ -85,10 +85,8 @@ static int identify_model(struct snd_tscm *tscm)
return 0;
}
-static void tscm_card_free(struct snd_card *card)
+static void tscm_free(struct snd_tscm *tscm)
{
- struct snd_tscm *tscm = card->private_data;
-
snd_tscm_transaction_unregister(tscm);
snd_tscm_stream_destroy_duplex(tscm);
@@ -97,44 +95,36 @@ static void tscm_card_free(struct snd_card *card)
mutex_destroy(&tscm->mutex);
}
-static int snd_tscm_probe(struct fw_unit *unit,
- const struct ieee1394_device_id *entry)
+static void tscm_card_free(struct snd_card *card)
{
- struct snd_card *card;
- struct snd_tscm *tscm;
+ tscm_free(card->private_data);
+}
+
+static void do_registration(struct work_struct *work)
+{
+ struct snd_tscm *tscm = container_of(work, struct snd_tscm, dwork.work);
int err;
- /* create card */
- err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
- sizeof(struct snd_tscm), &card);
+ err = snd_card_new(&tscm->unit->device, -1, NULL, THIS_MODULE, 0,
+ &tscm->card);
if (err < 0)
- return err;
- card->private_free = tscm_card_free;
-
- /* initialize myself */
- tscm = card->private_data;
- tscm->card = card;
- tscm->unit = fw_unit_get(unit);
-
- mutex_init(&tscm->mutex);
- spin_lock_init(&tscm->lock);
- init_waitqueue_head(&tscm->hwdep_wait);
+ return;
err = identify_model(tscm);
if (err < 0)
goto error;
- snd_tscm_proc_init(tscm);
-
- err = snd_tscm_stream_init_duplex(tscm);
+ err = snd_tscm_transaction_register(tscm);
if (err < 0)
goto error;
- err = snd_tscm_create_pcm_devices(tscm);
+ err = snd_tscm_stream_init_duplex(tscm);
if (err < 0)
goto error;
- err = snd_tscm_transaction_register(tscm);
+ snd_tscm_proc_init(tscm);
+
+ err = snd_tscm_create_pcm_devices(tscm);
if (err < 0)
goto error;
@@ -146,35 +136,91 @@ static int snd_tscm_probe(struct fw_unit *unit,
if (err < 0)
goto error;
- err = snd_card_register(card);
+ err = snd_card_register(tscm->card);
if (err < 0)
goto error;
- dev_set_drvdata(&unit->device, tscm);
+ /*
+ * After registered, tscm instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ tscm->card->private_free = tscm_card_free;
+ tscm->card->private_data = tscm;
+ tscm->registered = true;
- return err;
+ return;
error:
- snd_card_free(card);
- return err;
+ snd_tscm_transaction_unregister(tscm);
+ snd_tscm_stream_destroy_duplex(tscm);
+ snd_card_free(tscm->card);
+ dev_info(&tscm->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_tscm *tscm;
+
+ /* Allocate this independent of sound card instance. */
+ tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL);
+ if (tscm == NULL)
+ return -ENOMEM;
+
+ /* initialize myself */
+ tscm->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, tscm);
+
+ mutex_init(&tscm->mutex);
+ spin_lock_init(&tscm->lock);
+ init_waitqueue_head(&tscm->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&tscm->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &tscm->dwork);
+
+ return 0;
}
static void snd_tscm_update(struct fw_unit *unit)
{
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+ /* Postpone a workqueue for deferred registration. */
+ if (!tscm->registered)
+ snd_fw_schedule_registration(unit, &tscm->dwork);
+
snd_tscm_transaction_reregister(tscm);
- mutex_lock(&tscm->mutex);
- snd_tscm_stream_update_duplex(tscm);
- mutex_unlock(&tscm->mutex);
+ /*
+ * After registration, userspace can start packet streaming, then this
+ * code block works fine.
+ */
+ if (tscm->registered) {
+ mutex_lock(&tscm->mutex);
+ snd_tscm_stream_update_duplex(tscm);
+ mutex_unlock(&tscm->mutex);
+ }
}
static void snd_tscm_remove(struct fw_unit *unit)
{
struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
- /* No need to wait for releasing card object in this context. */
- snd_card_free_when_closed(tscm->card);
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&tscm->dwork);
+
+ if (tscm->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(tscm->card);
+ } else {
+ /* Don't forget this case. */
+ tscm_free(tscm);
+ }
}
static const struct ieee1394_device_id snd_tscm_id_table[] = {
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index 30ab77e92..1f6101157 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -51,6 +51,8 @@ struct snd_tscm {
struct mutex mutex;
spinlock_t lock;
+ bool registered;
+ struct delayed_work dwork;
const struct snd_tscm_spec *spec;
struct fw_iso_resources tx_resources;