ALSA: firewire-lib, firewire-speakers: handle packet queueing errors
authorClemens Ladisch <clemens@ladisch.de>
Tue, 15 Mar 2011 06:57:24 +0000 (07:57 +0100)
committerTakashi Iwai <tiwai@suse.de>
Tue, 15 Mar 2011 07:42:30 +0000 (08:42 +0100)
Add an AMDTP stream error state that occurs when we fail to queue
another packet.  In this case, the stream is stopped, and the error can
be reported when the application tries to restart the PCM stream.

Signed-off-by: Clemens Ladisch <clemens@ladisch.de>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
sound/firewire/amdtp.c
sound/firewire/amdtp.h
sound/firewire/speakers.c

index 046007ddbcaed09a95c6d6688593bbd479feed88..b18140ff2b93c48eb2a0dea6074e866a8340b56a 100644 (file)
@@ -47,6 +47,7 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
        s->flags = flags;
        s->context = ERR_PTR(-1);
        mutex_init(&s->mutex);
+       s->packet_index = 0;
 
        return 0;
 }
@@ -316,15 +317,19 @@ static void amdtp_fill_midi(struct amdtp_out_stream *s,
 static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
 {
        __be32 *buffer;
-       unsigned int data_blocks, syt, ptr;
+       unsigned int index, data_blocks, syt, ptr;
        struct snd_pcm_substream *pcm;
        struct fw_iso_packet packet;
        int err;
 
+       if (s->packet_index < 0)
+               return;
+       index = s->packet_index;
+
        data_blocks = calculate_data_blocks(s);
        syt = calculate_syt(s, cycle);
 
-       buffer = s->buffer.packets[s->packet_counter].buffer;
+       buffer = s->buffer.packets[index].buffer;
        buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
                                (s->data_block_quadlets << 16) |
                                s->data_block_counter);
@@ -343,20 +348,24 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
        s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
 
        packet.payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
-       packet.interrupt = IS_ALIGNED(s->packet_counter + 1,
-                                     INTERRUPT_INTERVAL);
+       packet.interrupt = IS_ALIGNED(index + 1, INTERRUPT_INTERVAL);
        packet.skip = 0;
        packet.tag = TAG_CIP;
        packet.sy = 0;
        packet.header_length = 0;
 
        err = fw_iso_context_queue(s->context, &packet, &s->buffer.iso_buffer,
-                                  s->buffer.packets[s->packet_counter].offset);
-       if (err < 0)
+                                  s->buffer.packets[index].offset);
+       if (err < 0) {
                dev_err(&s->unit->device, "queueing error: %d\n", err);
+               s->packet_index = -1;
+               amdtp_out_stream_pcm_abort(s);
+               return;
+       }
 
-       if (++s->packet_counter >= QUEUE_LENGTH)
-               s->packet_counter = 0;
+       if (++index >= QUEUE_LENGTH)
+               index = 0;
+       s->packet_index = index;
 
        if (pcm) {
                ptr = s->pcm_buffer_pointer + data_blocks;
@@ -398,13 +407,13 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
        int err;
 
        for (i = 0; i < QUEUE_LENGTH; ++i) {
-               skip_packet.interrupt = IS_ALIGNED(s->packet_counter + 1,
+               skip_packet.interrupt = IS_ALIGNED(s->packet_index + 1,
                                                   INTERRUPT_INTERVAL);
                err = fw_iso_context_queue(s->context, &skip_packet, NULL, 0);
                if (err < 0)
                        return err;
-               if (++s->packet_counter >= QUEUE_LENGTH)
-                       s->packet_counter = 0;
+               if (++s->packet_index >= QUEUE_LENGTH)
+                       s->packet_index = 0;
        }
 
        return 0;
@@ -469,7 +478,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
 
        amdtp_out_stream_update(s);
 
-       s->packet_counter = 0;
+       s->packet_index = 0;
        s->data_block_counter = 0;
        err = queue_initial_skip_packets(s);
        if (err < 0)
index 02dc1a664b55de77c5abb67eeb10d8c8f7bd7ce6..537a9cb83581b1b469b83914f39d3158413beb4f 100644 (file)
@@ -56,7 +56,7 @@ struct amdtp_out_stream {
 
        struct snd_pcm_substream *pcm;
 
-       unsigned int packet_counter;
+       int packet_index;
        unsigned int data_block_counter;
 
        unsigned int data_block_state;
@@ -110,6 +110,18 @@ static inline void amdtp_out_stream_set_midi(struct amdtp_out_stream *s,
        s->midi_ports = midi_ports;
 }
 
+/**
+ * amdtp_out_streaming_error - check for streaming error
+ * @s: the AMDTP output stream
+ *
+ * If this function returns true, the stream's packet queue has stopped due to
+ * an asynchronous error.
+ */
+static inline bool amdtp_out_streaming_error(struct amdtp_out_stream *s)
+{
+       return s->packet_index < 0;
+}
+
 /**
  * amdtp_out_stream_pcm_prepare - prepare PCM device for running
  * @s: the AMDTP output stream
index f6b095ef075a729ab5010d0020fa1827d02abaac..0fce9218abb1676bf8e259be50bfd6d5d65090d2 100644 (file)
@@ -283,6 +283,9 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
 
        mutex_lock(&fwspk->mutex);
 
+       if (amdtp_out_streaming_error(&fwspk->stream))
+               fwspk_stop_stream(fwspk);
+
        if (!fwspk->stream_running) {
                err = cmp_connection_establish(&fwspk->connection,
                        amdtp_out_stream_get_max_payload(&fwspk->stream));