Commit 3b196c39 authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai

ALSA: firewire-lib: add no-header packet processing

As long as investigating Fireface 400, IEC 61883-1/6 is not applied to
its packet streaming protocol. Remarks of the specific protocol are:
 * Each packet doesn't include CIP headers.
 * 64,0 and 128,0 kHz are supported.
 * The device doesn't necessarily transmit 8,000 packets per second.
 * 0, 1, 2, 3 are used as tag for rx isochronous packets, however 0 is
   used for tx isochronous packets.

On the other hand, there's a common feature. The number of data blocks
transferred in a second is the same as sampling transmission frequency.
Current ALSA IEC 61883-1/6 engine already has a method to calculate it and
this driver can utilize it for rx packets, as well as tx packets.

This commit adds support for the transferring protocol. CIP_NO_HEADERS
flag is newly added. When this flag is set:
 * Both of 0 (without CIP header) and 1 (with CIP header) are used as tag
   to handle incoming isochronous packet.
 * 0 (without CIP header) is used as tag to transfer outgoing isochronous
   packet.
 * Skip CIP header evaluation.
 * Use unique way to calculate the quadlets of isochronous packet payload.

In ALSA PCM interface, 128.0 kHz is not supported, and the ALSA
IEC 61883-1/6 engine doesn't support 64.0 kHz. These modes are dropped.

The sequence of rx packet has a remarkable quirk about tag. This will be
described in later commits.
Signed-off-by: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent ff0fb5aa
...@@ -27,6 +27,7 @@ ...@@ -27,6 +27,7 @@
/* isochronous header parameters */ /* isochronous header parameters */
#define ISO_DATA_LENGTH_SHIFT 16 #define ISO_DATA_LENGTH_SHIFT 16
#define TAG_NO_CIP_HEADER 0
#define TAG_CIP 1 #define TAG_CIP 1
/* common isochronous packet header parameters */ /* common isochronous packet header parameters */
...@@ -234,11 +235,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters); ...@@ -234,11 +235,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s) unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{ {
unsigned int multiplier = 1; unsigned int multiplier = 1;
unsigned int header_size = 0;
if (s->flags & CIP_JUMBO_PAYLOAD) if (s->flags & CIP_JUMBO_PAYLOAD)
multiplier = 5; multiplier = 5;
if (!(s->flags & CIP_NO_HEADER))
header_size = 8;
return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier; return header_size +
s->syt_interval * s->data_block_quadlets * 4 * multiplier;
} }
EXPORT_SYMBOL(amdtp_stream_get_max_payload); EXPORT_SYMBOL(amdtp_stream_get_max_payload);
...@@ -380,7 +385,7 @@ static int queue_packet(struct amdtp_stream *s, unsigned int header_length, ...@@ -380,7 +385,7 @@ static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
goto end; goto end;
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL); p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
p.tag = TAG_CIP; p.tag = s->tag;
p.header_length = header_length; p.header_length = header_length;
if (payload_length > 0) if (payload_length > 0)
p.payload_length = payload_length; p.payload_length = payload_length;
...@@ -457,6 +462,34 @@ static int handle_out_packet(struct amdtp_stream *s, ...@@ -457,6 +462,34 @@ static int handle_out_packet(struct amdtp_stream *s,
return 0; return 0;
} }
static int handle_out_packet_without_header(struct amdtp_stream *s,
unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
unsigned int syt;
unsigned int data_blocks;
unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
buffer = s->buffer.packets[s->packet_index].buffer;
syt = calculate_syt(s, cycle);
data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer, data_blocks, &syt);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
payload_length = data_blocks * 4 * s->data_block_quadlets;
if (queue_out_packet(s, payload_length) < 0)
return -EIO;
pcm = ACCESS_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
/* No need to return the number of handled data blocks. */
return 0;
}
static int handle_in_packet(struct amdtp_stream *s, static int handle_in_packet(struct amdtp_stream *s,
unsigned int payload_length, unsigned int cycle, unsigned int payload_length, unsigned int cycle,
unsigned int index) unsigned int index)
...@@ -573,6 +606,30 @@ static int handle_in_packet(struct amdtp_stream *s, ...@@ -573,6 +606,30 @@ static int handle_in_packet(struct amdtp_stream *s,
return 0; return 0;
} }
static int handle_in_packet_without_header(struct amdtp_stream *s,
unsigned int payload_quadlets, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
unsigned int data_blocks;
struct snd_pcm_substream *pcm;
unsigned int pcm_frames;
buffer = s->buffer.packets[s->packet_index].buffer;
data_blocks = payload_quadlets / s->data_block_quadlets;
pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL);
s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
if (queue_in_packet(s) < 0)
return -EIO;
pcm = ACCESS_ONCE(s->pcm);
if (pcm && pcm_frames > 0)
update_pcm_pointers(s, pcm, pcm_frames);
return 0;
}
/* /*
* In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On * In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
* the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent * the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
...@@ -616,7 +673,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -616,7 +673,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
for (i = 0; i < packets; ++i) { for (i = 0; i < packets; ++i) {
cycle = increment_cycle_count(cycle, 1); cycle = increment_cycle_count(cycle, 1);
if (handle_out_packet(s, 0, cycle, i) < 0) { if (s->handle_packet(s, 0, cycle, i) < 0) {
s->packet_index = -1; s->packet_index = -1;
amdtp_stream_pcm_abort(s); amdtp_stream_pcm_abort(s);
return; return;
...@@ -663,7 +720,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp, ...@@ -663,7 +720,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
break; break;
} }
if (handle_in_packet(s, payload_length, cycle, i) < 0) if (s->handle_packet(s, payload_length, cycle, i) < 0)
break; break;
} }
...@@ -699,10 +756,18 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context, ...@@ -699,10 +756,18 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
packets = header_length / IN_PACKET_HEADER_SIZE; packets = header_length / IN_PACKET_HEADER_SIZE;
cycle = decrement_cycle_count(cycle, packets); cycle = decrement_cycle_count(cycle, packets);
context->callback.sc = in_stream_callback; context->callback.sc = in_stream_callback;
if (s->flags & CIP_NO_HEADER)
s->handle_packet = handle_in_packet_without_header;
else
s->handle_packet = handle_in_packet;
} else { } else {
packets = header_length / 4; packets = header_length / 4;
cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets); cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
context->callback.sc = out_stream_callback; context->callback.sc = out_stream_callback;
if (s->flags & CIP_NO_HEADER)
s->handle_packet = handle_out_packet_without_header;
else
s->handle_packet = handle_out_packet;
} }
s->start_cycle = cycle; s->start_cycle = cycle;
...@@ -782,6 +847,11 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) ...@@ -782,6 +847,11 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
amdtp_stream_update(s); amdtp_stream_update(s);
if (s->flags & CIP_NO_HEADER)
s->tag = TAG_NO_CIP_HEADER;
else
s->tag = TAG_CIP;
s->packet_index = 0; s->packet_index = 0;
do { do {
if (s->direction == AMDTP_IN_STREAM) if (s->direction == AMDTP_IN_STREAM)
...@@ -794,7 +864,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed) ...@@ -794,7 +864,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
/* NOTE: TAG1 matches CIP. This just affects in stream. */ /* NOTE: TAG1 matches CIP. This just affects in stream. */
tag = FW_ISO_CONTEXT_MATCH_TAG1; tag = FW_ISO_CONTEXT_MATCH_TAG1;
if (s->flags & CIP_EMPTY_WITH_TAG0) if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
tag |= FW_ISO_CONTEXT_MATCH_TAG0; tag |= FW_ISO_CONTEXT_MATCH_TAG0;
s->callbacked = false; s->callbacked = false;
......
...@@ -31,6 +31,7 @@ ...@@ -31,6 +31,7 @@
* allows 5 times as large as IEC 61883-6 defines. * allows 5 times as large as IEC 61883-6 defines.
* @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
* valid EOH. * valid EOH.
* @CIP_NO_HEADERS: a lack of headers in packets
*/ */
enum cip_flags { enum cip_flags {
CIP_NONBLOCKING = 0x00, CIP_NONBLOCKING = 0x00,
...@@ -42,6 +43,7 @@ enum cip_flags { ...@@ -42,6 +43,7 @@ enum cip_flags {
CIP_EMPTY_HAS_WRONG_DBC = 0x20, CIP_EMPTY_HAS_WRONG_DBC = 0x20,
CIP_JUMBO_PAYLOAD = 0x40, CIP_JUMBO_PAYLOAD = 0x40,
CIP_HEADER_WITHOUT_EOH = 0x80, CIP_HEADER_WITHOUT_EOH = 0x80,
CIP_NO_HEADER = 0x100,
}; };
/** /**
...@@ -104,6 +106,10 @@ struct amdtp_stream { ...@@ -104,6 +106,10 @@ struct amdtp_stream {
struct fw_iso_context *context; struct fw_iso_context *context;
struct iso_packets_buffer buffer; struct iso_packets_buffer buffer;
int packet_index; int packet_index;
int tag;
int (*handle_packet)(struct amdtp_stream *s,
unsigned int payload_quadlets, unsigned int cycle,
unsigned int index);
/* For CIP headers. */ /* For CIP headers. */
unsigned int source_node_id_field; unsigned int source_node_id_field;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment