[alsa-devel] [PATCH 09/17] firewire-lib: Add sort function for transmitted packet
Takashi Sakamoto
o-takashi at sakamocchi.jp
Sat Nov 23 07:07:56 CET 2013
The aim of this commit is to assure the continuity of timestamp in received
packets. Fireworks with firmware version 5.5 or former is a type of device
which transmits packets with a bit disorder. For example,
Index Payload CIP header 1 CIP header 2
023 210 3F1100F8 900478E9
024 002 3F1100F8 90FFFFFF
025 210 3F110000 900490E9
026 210 3F110008 9004A4E9
027 210 3F110010 9004B8E9
028 002 3F110010 90FFFFFF
029 210 3F110020 9004E4E8 <-
030 210 3F110018 9004D0E8 <-
031 210 3F110028 9004F8E8
032 002 3F110028 90FFFFFF
033 210 3F110030 900410E8
034 210 3F110038 900424E8
035 210 3F110040 900438E8
036 002 3F110040 90FFFFFF
037 210 3F110050 900464E8 <-
038 210 3F110048 900450E8 <-
039 210 3F110058 900478E8
040 002 3F110058 90FFFFFF
041 210 3F110068 9004A4E8 <-
042 210 3F110060 900490E8 <-
043 210 3F110070 9004B8E8
044 002 3F110070 90FFFFFF
045 210 3F110080 9004E4E7 <-
046 210 3F110078 9004D0E8 <-
Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
sound/firewire/amdtp.c | 130 +++++++++++++++++++++++++++++++++++++++++++------
sound/firewire/amdtp.h | 4 ++
2 files changed, 119 insertions(+), 15 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 6830dd8..d02ccac 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -44,6 +44,7 @@
#define AMDTP_DBS_MASK 0x00ff0000
#define AMDTP_DBS_SHIFT 16
#define AMDTP_DBC_MASK 0x000000ff
+#define DBC_THREADSHOULD (AMDTP_DBC_MASK / 2)
/* TODO: make these configurable */
#define INTERRUPT_INTERVAL 16
@@ -53,6 +54,13 @@
#define TRANSMIT_PACKET_HEADER_SIZE 4
#define RECEIVE_PACKET_HEADER_SIZE 0
+/* for re-ordering receive packets */
+struct sort_table {
+ unsigned int id;
+ unsigned int dbc;
+ unsigned int payload_size;
+};
+
static void pcm_period_tasklet(unsigned long data);
/**
@@ -77,6 +85,9 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
s->callbacked = false;
s->sync_slave = ERR_PTR(-1);
+ s->sort_table = NULL;
+ s->left_packets = NULL;
+
return 0;
}
EXPORT_SYMBOL(amdtp_stream_init);
@@ -807,6 +818,44 @@ static void handle_in_packet(struct amdtp_stream *s,
check_pcm_pointer(s, pcm, data_blocks);
}
+#define SWAP(tbl, m, n) \
+ t = tbl[n].id; \
+ tbl[n].id = tbl[m].id; \
+ tbl[m].id = t; \
+ t = tbl[n].dbc; \
+ tbl[n].dbc = tbl[m].dbc; \
+ tbl[m].dbc = t; \
+ t = tbl[n].payload_size; \
+ tbl[n].payload_size = tbl[m].payload_size; \
+ tbl[m].payload_size = t;
+static void packet_sort(struct sort_table *tbl, unsigned int len)
+{
+ unsigned int i, j, k, t;
+
+ i = 0;
+ do {
+ for (j = i + 1; j < len; j++) {
+ if (((tbl[i].dbc > tbl[j].dbc) &&
+ (tbl[i].dbc - tbl[j].dbc < DBC_THREADSHOULD))) {
+ SWAP(tbl, i, j);
+ } else if ((tbl[j].dbc > tbl[i].dbc) &&
+ (tbl[j].dbc - tbl[i].dbc >
+ DBC_THREADSHOULD)) {
+ for (k = i; k > 0; k--) {
+ if ((tbl[k].dbc > tbl[j].dbc) ||
+ (tbl[j].dbc - tbl[k].dbc >
+ DBC_THREADSHOULD)) {
+ SWAP(tbl, j, k);
+ }
+ break;
+ }
+ }
+ break;
+ }
+ i = j;
+ } while (i < len);
+}
+
static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
size_t header_length, void *header,
void *private_data)
@@ -833,30 +882,65 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
void *private_data)
{
struct amdtp_stream *s = private_data;
- unsigned int p, packets, syt, data_quadlets;
+ struct sort_table *entry, *tbl = s->sort_table;
+ unsigned int i, j, k, index, packets, syt, remain_packets;
__be32 *buffer, *headers = header;
/* The number of packets in buffer */
packets = header_length / TRANSMIT_PACKET_HEADER_SIZE;
- for (p = 0; p < packets; p++) {
- buffer = s->buffer.packets[s->packet_index].buffer;
+ /* Store into sort table and sort. */
+ for (i = 0; i < packets; i++) {
+ entry = &tbl[s->remain_packets + i];
+ entry->id = i;
- /* The number of quadlets in this packet */
- data_quadlets =
- (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+ index = s->packet_index + i;
+ if (index >= QUEUE_LENGTH)
+ index -= QUEUE_LENGTH;
+ buffer = s->buffer.packets[index].buffer;
+ entry->dbc = be32_to_cpu(buffer[0]) & AMDTP_DBC_MASK;
- /* Process sync slave stream */
- if ((s->flags & CIP_BLOCKING) &&
- (s->flags & CIP_SYNC_TO_DEVICE) &&
- s->sync_slave->callbacked) {
- syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
- handle_out_packet(s->sync_slave, syt);
- }
+ entry->payload_size = be32_to_cpu(headers[i]) >>
+ ISO_DATA_LENGTH_SHIFT;
+ }
+ packet_sort(tbl, packets + s->remain_packets);
- /* handle each packet data */
- handle_in_packet(s, data_quadlets, buffer);
+ /*
+ * for convinience, tbl[i].id >= QUEUE_LENGTH is a label to identify
+ * previous packets in buffer.
+ */
+ remain_packets = s->remain_packets;
+ s->remain_packets = packets / 4;
+ for (i = 0, j = 0, k = 0; i < remain_packets + packets; i++) {
+ if (tbl[i].id < QUEUE_LENGTH) {
+ index = s->packet_index + tbl[i].id;
+ if (index >= QUEUE_LENGTH)
+ index -= QUEUE_LENGTH;
+ buffer = s->buffer.packets[index].buffer;
+ } else
+ buffer = s->left_packets +
+ amdtp_stream_get_max_payload(s) * j++;
+
+ if (i < remain_packets + packets - s->remain_packets) {
+ /* Process sync slave stream */
+ if ((s->flags & CIP_BLOCKING) &&
+ (s->flags & CIP_SYNC_TO_DEVICE) &&
+ s->sync_slave->callbacked) {
+ syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+ handle_out_packet(s->sync_slave, syt);
+ }
+ handle_in_packet(s, tbl[i].payload_size / 4, buffer);
+ } else {
+ tbl[k].id = tbl[i].id + QUEUE_LENGTH;
+ tbl[k].dbc = tbl[i].dbc;
+ tbl[k].payload_size = tbl[i].payload_size;
+ memcpy(s->left_packets +
+ amdtp_stream_get_max_payload(s) * k++,
+ buffer, tbl[i].payload_size);
+ }
+ }
+ for (i = 0; i < packets; i ++) {
if (queue_in_packet(s) < 0) {
amdtp_stream_pcm_abort(s);
return;
@@ -955,6 +1039,17 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
if (err < 0)
goto err_unlock;
+ /* for sorting transmitted packets */
+ if (s->direction == AMDTP_TRANSMIT_STREAM) {
+ s->remain_packets = 0;
+ s->sort_table = kzalloc(sizeof(struct sort_table) *
+ QUEUE_LENGTH, GFP_KERNEL);
+ if (s->sort_table == NULL)
+ return -ENOMEM;
+ s->left_packets = kzalloc(amdtp_stream_get_max_payload(s) *
+ QUEUE_LENGTH / 4, GFP_KERNEL);
+ }
+
s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
type, channel, speed, header_size,
amdtp_stream_callback, s);
@@ -1052,6 +1147,11 @@ void amdtp_stream_stop(struct amdtp_stream *s)
s->context = ERR_PTR(-1);
iso_packets_buffer_destroy(&s->buffer, s->unit);
+ if (s->sort_table != NULL)
+ kfree(s->sort_table);
+ if (s->left_packets != NULL)
+ kfree(s->left_packets);
+
s->callbacked = false;
mutex_unlock(&s->mutex);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index a2a7818..0ba65bb 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -106,6 +106,10 @@ struct amdtp_stream {
bool callbacked;
wait_queue_head_t callback_wait;
struct amdtp_stream *sync_slave;
+
+ void *sort_table;
+ void *left_packets;
+ unsigned int remain_packets;
};
int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
--
1.8.3.2
More information about the Alsa-devel
mailing list