[alsa-devel] [PATCH 09/39] firewire-lib: Add sort function for transmitted packet

Takashi Iwai tiwai at suse.de
Fri Feb 28 07:40:51 CET 2014


At Fri, 28 Feb 2014 12:27:22 +0900,
Takashi Sakamoto wrote:
> 
> This commit is to assure the continuity of timestamp in in-packets
> transmitted by devices.
> 
> Fireworks with firmware version 5.5 or former is a type of device which
> transmits packets with a bit disorder.
> 
> This commit add sorting of in-packets. When callback of receive-context
> is processed, the parameters of in-packets are stored in sort table and
> sorted by its value of data block counter. The sort algorism works faster
> in ordered sequence than in messy sequence. This is convinient for this
> purpose because the sequence is assumed not to be so messy. After sorting,
> packets are processed except for a last few packets in sort table. These
> packets are stored in buffer once and used in next cycle.
> 
> Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
> ---
>  sound/firewire/amdtp.c | 129 +++++++++++++++++++++++++++++++++++++++++++------
>  sound/firewire/amdtp.h |   4 ++
>  2 files changed, 119 insertions(+), 14 deletions(-)
> 
> diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
> index 9415992..3d943d1 100644
> --- a/sound/firewire/amdtp.c
> +++ b/sound/firewire/amdtp.c
> @@ -45,6 +45,7 @@
>  #define AMDTP_DBS_MASK		0x00ff0000
>  #define AMDTP_DBS_SHIFT		16
>  #define AMDTP_DBC_MASK		0x000000ff
> +#define DBC_THRESHOLD		(AMDTP_DBC_MASK / 2)
>  
>  /* TODO: make these configurable */
>  #define INTERRUPT_INTERVAL	16
> @@ -53,6 +54,13 @@
>  #define IN_PACKET_HEADER_SIZE	4
>  #define OUT_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);
> @@ -735,6 +746,44 @@ static void handle_in_packet(struct amdtp_stream *s,
>  		update_pcm_pointers(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;

Why swap() macro can't be used instead?


> +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_THRESHOLD))) {
> +				SWAP(tbl, i, j);
> +			} else if ((tbl[j].dbc > tbl[i].dbc) &&
> +				   (tbl[j].dbc - tbl[i].dbc >
> +							DBC_THRESHOLD)) {
> +				for (k = i; k > 0; k--) {
> +					if ((tbl[k].dbc > tbl[j].dbc) ||
> +					    (tbl[j].dbc - tbl[k].dbc >
> +							DBC_THRESHOLD)) {
> +						SWAP(tbl, j, k);
> +					}
> +					break;
> +				}
> +			}
> +			break;
> +		}
> +		i = j;
> +	} while (i < len);
> +}

You can use the standard sort() function (available in linux/sort.h).


> +
>  static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
>  				size_t header_length, void *header,
>  				void *private_data)
> @@ -761,30 +810,66 @@ 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 / IN_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;
> +
> +		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;
>  
> -		/* The number of quadlets in this packet */
> -		data_quadlets =
> -			(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
> +		entry->payload_size = be32_to_cpu(headers[i]) >>
> +				      ISO_DATA_LENGTH_SHIFT;
> +	}
> +	packet_sort(tbl, 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);
> +	/*
> +	 * 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++;
>  		}
>  
> -		/* handle each packet data */
> -		handle_in_packet(s, data_quadlets, buffer);
> +		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;
> @@ -888,6 +973,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_IN_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);

NULL check missing?


thanks,

Takashi


> +	}
> +
>  	s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
>  					   type, channel, speed, header_size,
>  					   amdtp_stream_callback, s);
> @@ -986,6 +1082,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 efa2d25..d502646 100644
> --- a/sound/firewire/amdtp.h
> +++ b/sound/firewire/amdtp.h
> @@ -109,6 +109,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