/* 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;
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;
#include "./bebob.h"
#define CALLBACK_TIMEOUT 1000
+#define FW_ISO_RESOURCE_DELAY 1000
/*
* NOTE;
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);
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;
}
{
cmp_connection_break(&bebob->in_conn);
cmp_connection_break(&bebob->out_conn);
+
+ bebob->connected = false;
}
static void
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);
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 */
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);
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);
}