summaryrefslogtreecommitdiff
path: root/sound/firewire/bebob
diff options
context:
space:
mode:
Diffstat (limited to 'sound/firewire/bebob')
-rw-r--r--sound/firewire/bebob/bebob.h4
-rw-r--r--sound/firewire/bebob/bebob_stream.c52
2 files changed, 50 insertions, 6 deletions
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);
}