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@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,