summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sound/firewire/amdtp.c9
-rw-r--r--sound/firewire/amdtp.h3
-rw-r--r--sound/firewire/bebob/bebob.h4
-rw-r--r--sound/firewire/bebob/bebob_stream.c52
4 files changed, 60 insertions, 8 deletions
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 28ee3d86164d..690c60828872 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -665,7 +665,8 @@ static void handle_in_packet(struct amdtp_stream *s,
/* Check data block counter continuity */
data_block_counter = cip_header[0] & AMDTP_DBC_MASK;
- if ((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && data_block_counter == 0) {
+ if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) && data_block_counter == 0) ||
+ (s->data_block_counter == UINT_MAX)) {
lost = false;
} else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
lost = data_block_counter != s->data_block_counter;
@@ -850,7 +851,11 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
goto err_unlock;
}
- s->data_block_counter = 0;
+ if (s->direction == AMDTP_IN_STREAM &&
+ s->flags & CIP_SKIP_INIT_DBC_CHECK)
+ s->data_block_counter = UINT_MAX;
+ else
+ s->data_block_counter = 0;
s->data_block_state = initial_state[s->sfc].data_block;
s->syt_offset_state = initial_state[s->sfc].syt_offset;
s->last_syt_offset = TICKS_PER_CYCLE;
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index fb5934cc01aa..c79f058f1621 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -25,6 +25,8 @@
* The value of data_block_quadlets is used instead of reported value.
* @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.
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -34,6 +36,7 @@ enum cip_flags {
CIP_DBC_IS_END_EVENT = 0x08,
CIP_WRONG_DBS = 0x10,
CIP_SKIP_DBC_ZERO_CHECK = 0x20,
+ CIP_SKIP_INIT_DBC_CHECK = 0x40,
};
/**
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index a195c16da41e..031ca78096d2 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -53,6 +53,10 @@ struct snd_bebob {
unsigned int midi_input_ports;
unsigned int midi_output_ports;
+ /* for bus reset quirk */
+ struct completion bus_reset;
+ bool connected;
+
struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index c868c17c6fc4..46b056c8f2a8 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -9,6 +9,7 @@
#include "./bebob.h"
#define CALLBACK_TIMEOUT 1000
+#define FW_ISO_RESOURCE_DELAY 1000
/*
* NOTE;
@@ -325,7 +326,10 @@ check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
static int
make_both_connections(struct snd_bebob *bebob, unsigned int rate)
{
- int index, pcm_channels, midi_channels, err;
+ int index, pcm_channels, midi_channels, err = 0;
+
+ if (bebob->connected)
+ goto end;
/* confirm params for both streams */
index = get_formation_index(rate);
@@ -345,8 +349,12 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
goto end;
err = cmp_connection_establish(&bebob->in_conn,
amdtp_stream_get_max_payload(&bebob->rx_stream));
- if (err < 0)
+ if (err < 0) {
cmp_connection_break(&bebob->out_conn);
+ goto end;
+ }
+
+ bebob->connected = true;
end:
return err;
}
@@ -356,6 +364,8 @@ break_both_connections(struct snd_bebob *bebob)
{
cmp_connection_break(&bebob->in_conn);
cmp_connection_break(&bebob->out_conn);
+
+ bebob->connected = false;
}
static void
@@ -415,6 +425,9 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
destroy_both_connections(bebob);
goto end;
}
+ /* See comments in next function */
+ init_completion(&bebob->bus_reset);
+ bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
AMDTP_OUT_STREAM, CIP_BLOCKING);
@@ -433,8 +446,25 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, int rate)
atomic_t *slave_substreams;
enum cip_flags sync_mode;
unsigned int curr_rate;
+ bool updated = false;
int err = 0;
+ /*
+ * Normal BeBoB firmware has a quirk at bus reset to transmits packets
+ * with discontinuous value in dbc field.
+ *
+ * This 'struct completion' is used to call .update() at first to update
+ * connections/streams. Next following codes handle streaming error.
+ */
+ if (amdtp_streaming_error(&bebob->tx_stream)) {
+ if (completion_done(&bebob->bus_reset))
+ reinit_completion(&bebob->bus_reset);
+
+ updated = (wait_for_completion_interruptible_timeout(
+ &bebob->bus_reset,
+ msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0);
+ }
+
mutex_lock(&bebob->mutex);
/* Need no substreams */
@@ -463,13 +493,19 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, int rate)
if (err < 0)
goto end;
- /* packet queueing error */
- if (amdtp_streaming_error(master)) {
+ /*
+ * packet queueing error or detecting discontinuity
+ *
+ * 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);
- amdtp_stream_stop(slave);
- }
if (amdtp_streaming_error(slave))
amdtp_stream_stop(slave);
+ if (!updated &&
+ !amdtp_stream_running(master) && !amdtp_stream_running(slave))
+ break_both_connections(bebob);
/* stop streams if rate is different */
err = snd_bebob_stream_get_rate(bebob, &curr_rate);
@@ -599,6 +635,10 @@ void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
amdtp_stream_update(&bebob->tx_stream);
}
+ /* wake up stream_start_duplex() */
+ if (!completion_done(&bebob->bus_reset))
+ complete_all(&bebob->bus_reset);
+
mutex_unlock(&bebob->mutex);
}