alsa-devel.alsa-project.org archive mirror
 help / color / mirror / Atom feed
* [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices
@ 2014-02-28  3:27 Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 01/39] firewire-lib: Rename functions, structure, member for AMDTP Takashi Sakamoto
                   ` (39 more replies)
  0 siblings, 40 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This is a pull-request of my 39 patches to enhance support for Firewire
devices. These patches extend firewire-lib, add new drivers (fireworks/bebob).

These drivers can handle playback/capture of PCM samples/MIDI messages and
can support 70-80 models totally.

These drivers are designed not to interrupt FFADO (user-land driver project)
functionality except for playbacking/capturing.

These drivers basically give no way to control device's internal mixer. For
this purpose, users need to use applications such like
ffado-dbus-server/ffado-mixer, or develop software to execute AV/C commands
and device specific commands.


Updates since RFC v3:
[alsa-devel] [RFC v3] [PATCH 00/52] Enhancement for support of firewire devices
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-January/071820.html 

firewire-lib:
 - improve operation for oPCR
 - add fallbacks at transaction timeout for device's quirk

fireworks:
 - add counter to maintain streams
 - use memdup_user() for hwdep interface

bebob:
 - add counter to maintain streams
 - fix failure to handle bus reset

----------------------------------------------------------------

The following changes since commit dde9c38779c1f30253485a160facc211b20ca222:

  Merge branch 'for-linus' (2014-02-27 12:46:40 +0100)

are available in the git repository at:


  https://github.com/takaswie/sound.git request

for you to fetch changes up to 0ed07c65fef2f80fcfc6223b0d689fca56fa6323:

  bebob: Add support for M-Audio special Firewire series (2014-02-28 11:30:52 +0900)

----------------------------------------------------------------
Takashi Sakamoto (39):
      firewire-lib: Rename functions, structure, member for AMDTP
      firewire-lib: Add macros instead of fixed value for AMDTP
      firewire-lib: Add 'direction' member to 'amdtp_stream' structure
      firewire-lib: Split some codes into functions to reuse for both streams
      firewire-lib: Add support for AMDTP in-stream and PCM capture
      firewire-lib: Add support for MIDI capture/playback
      firewire-lib: Give syt value as parameter to handle_out_packet()
      firewire-lib: Add support for duplex streams synchronization in blocking mode
      firewire-lib: Add sort function for transmitted packet
      firewire-lib: Add transfer delay to synchronized duplex streams
      firewire-lib: Add support for channel mapping
      firewire-lib: Rename macros, variables and functions for CMP
      firewire-lib: Add 'direction' member to 'cmp_connection' structure
      firewire-lib: Add handling output connection by CMP
      firewire-lib: Add a new function to check others' connection
      firewire-lib: Add some AV/C general commands
      firewire-lib: Add quirks for Fireworks
      firewire-lib: Add a fallback at RCODE_CANCELLED
      fireworks: Add skelton for Fireworks based devices
      fireworks: Add transaction and some commands
      fireworks: Add connection and stream management
      fireworks: Add proc interface for debugging purpose
      fireworks: Add MIDI interface
      fireworks: Add PCM interface
      fireworks: Add hwdep interface
      fireworks: Add command/response functionality into hwdep interface
      bebob: Add skelton for BeBoB based devices
      bebob: Add commands and connections/streams management
      bebob: Add proc interface for debugging purpose
      bebob: Add MIDI interface
      bebob: Add PCM interface
      bebob: Add hwdep interface
      bebob: Prepare for device specific operations
      bebob: Add support for Terratec PHASE, EWS series and Aureon
      bebob: Add support for Yamaha GO series
      bebob: Add support for Focusrite Saffire/SaffirePro series
      bebob: Add support for M-Audio usual Firewire series
      bebob: Send a cue to load firmware for M-Audio Firewire series
      bebob: Add support for M-Audio special Firewire series

 include/uapi/sound/asound.h                      |   4 +-
 include/uapi/sound/firewire.h                    |  22 +-
 sound/firewire/Kconfig                           |  59 ++
 sound/firewire/Makefile                          |   2 +
 sound/firewire/amdtp.c                           | 891 ++++++++++++++++------
 sound/firewire/amdtp.h                           | 175 ++++-
 sound/firewire/bebob/Makefile                    |   4 +
 sound/firewire/bebob/bebob.c                     | 450 +++++++++++
 sound/firewire/bebob/bebob.h                     | 262 +++++++
 sound/firewire/bebob/bebob_command.c             | 354 +++++++++
 sound/firewire/bebob/bebob_focusrite.c           | 289 +++++++
 sound/firewire/bebob/bebob_hwdep.c               | 197 +++++
 sound/firewire/bebob/bebob_maudio.c              | 922 +++++++++++++++++++++++
 sound/firewire/bebob/bebob_midi.c                | 170 +++++
 sound/firewire/bebob/bebob_pcm.c                 | 451 +++++++++++
 sound/firewire/bebob/bebob_proc.c                | 179 +++++
 sound/firewire/bebob/bebob_stream.c              | 869 +++++++++++++++++++++
 sound/firewire/bebob/bebob_terratec.c            |  68 ++
 sound/firewire/bebob/bebob_yamaha.c              |  50 ++
 sound/firewire/cmp.c                             | 229 ++++--
 sound/firewire/cmp.h                             |  14 +-
 sound/firewire/dice.c                            |  46 +-
 sound/firewire/fcp.c                             | 160 +++-
 sound/firewire/fcp.h                             |  21 +
 sound/firewire/fireworks/Makefile                |   4 +
 sound/firewire/fireworks/fireworks.c             | 324 ++++++++
 sound/firewire/fireworks/fireworks.h             | 223 ++++++
 sound/firewire/fireworks/fireworks_command.c     | 397 ++++++++++
 sound/firewire/fireworks/fireworks_hwdep.c       | 294 ++++++++
 sound/firewire/fireworks/fireworks_midi.c        | 179 +++++
 sound/firewire/fireworks/fireworks_pcm.c         | 477 ++++++++++++
 sound/firewire/fireworks/fireworks_proc.c        | 208 +++++
 sound/firewire/fireworks/fireworks_stream.c      | 337 +++++++++
 sound/firewire/fireworks/fireworks_transaction.c | 327 ++++++++
 sound/firewire/lib.c                             |   6 +-
 sound/firewire/lib.h                             |   1 +
 sound/firewire/speakers.c                        |  93 +--
 37 files changed, 8356 insertions(+), 402 deletions(-)
 create mode 100644 sound/firewire/bebob/Makefile
 create mode 100644 sound/firewire/bebob/bebob.c
 create mode 100644 sound/firewire/bebob/bebob.h
 create mode 100644 sound/firewire/bebob/bebob_command.c
 create mode 100644 sound/firewire/bebob/bebob_focusrite.c
 create mode 100644 sound/firewire/bebob/bebob_hwdep.c
 create mode 100644 sound/firewire/bebob/bebob_maudio.c
 create mode 100644 sound/firewire/bebob/bebob_midi.c
 create mode 100644 sound/firewire/bebob/bebob_pcm.c
 create mode 100644 sound/firewire/bebob/bebob_proc.c
 create mode 100644 sound/firewire/bebob/bebob_stream.c
 create mode 100644 sound/firewire/bebob/bebob_terratec.c
 create mode 100644 sound/firewire/bebob/bebob_yamaha.c
 create mode 100644 sound/firewire/fireworks/Makefile
 create mode 100644 sound/firewire/fireworks/fireworks.c
 create mode 100644 sound/firewire/fireworks/fireworks.h
 create mode 100644 sound/firewire/fireworks/fireworks_command.c
 create mode 100644 sound/firewire/fireworks/fireworks_hwdep.c
 create mode 100644 sound/firewire/fireworks/fireworks_midi.c
 create mode 100644 sound/firewire/fireworks/fireworks_pcm.c
 create mode 100644 sound/firewire/fireworks/fireworks_proc.c
 create mode 100644 sound/firewire/fireworks/fireworks_stream.c
 create mode 100644 sound/firewire/fireworks/fireworks_transaction.c
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 01/39] firewire-lib: Rename functions, structure, member for AMDTP
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 02/39] firewire-lib: Add macros instead of fixed value " Takashi Sakamoto
                   ` (38 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This patch renames some functions, structure and member to reuse them in both
AMDTP in/out stream.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c    | 154 +++++++++++++++++++++++-----------------------
 sound/firewire/amdtp.h    |  64 ++++++++++---------
 sound/firewire/dice.c     |  46 +++++++-------
 sound/firewire/speakers.c |  46 +++++++-------
 4 files changed, 158 insertions(+), 152 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 9048777..16fe858 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -34,13 +34,13 @@
 static void pcm_period_tasklet(unsigned long data);
 
 /**
- * amdtp_out_stream_init - initialize an AMDTP output stream structure
- * @s: the AMDTP output stream to initialize
+ * amdtp_stream_init - initialize an AMDTP stream structure
+ * @s: the AMDTP stream to initialize
  * @unit: the target of the stream
  * @flags: the packet transmission method to use
  */
-int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
-			  enum cip_out_flags flags)
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+		      enum cip_flags flags)
 {
 	s->unit = fw_unit_get(unit);
 	s->flags = flags;
@@ -51,19 +51,19 @@ int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
 
 	return 0;
 }
-EXPORT_SYMBOL(amdtp_out_stream_init);
+EXPORT_SYMBOL(amdtp_stream_init);
 
 /**
- * amdtp_out_stream_destroy - free stream resources
- * @s: the AMDTP output stream to destroy
+ * amdtp_stream_destroy - free stream resources
+ * @s: the AMDTP stream to destroy
  */
-void amdtp_out_stream_destroy(struct amdtp_out_stream *s)
+void amdtp_stream_destroy(struct amdtp_stream *s)
 {
-	WARN_ON(amdtp_out_stream_running(s));
+	WARN_ON(amdtp_stream_running(s));
 	mutex_destroy(&s->mutex);
 	fw_unit_put(s->unit);
 }
-EXPORT_SYMBOL(amdtp_out_stream_destroy);
+EXPORT_SYMBOL(amdtp_stream_destroy);
 
 const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
 	[CIP_SFC_32000]  =  8,
@@ -77,8 +77,8 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
 EXPORT_SYMBOL(amdtp_syt_intervals);
 
 /**
- * amdtp_out_stream_set_parameters - set stream parameters
- * @s: the AMDTP output stream to configure
+ * amdtp_stream_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
  * @rate: the sample rate
  * @pcm_channels: the number of PCM samples in each data block, to be encoded
  *                as AM824 multi-bit linear audio
@@ -87,10 +87,10 @@ EXPORT_SYMBOL(amdtp_syt_intervals);
  * The parameters must be set before the stream is started, and must not be
  * changed while the stream is running.
  */
-void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
-				     unsigned int rate,
-				     unsigned int pcm_channels,
-				     unsigned int midi_ports)
+void amdtp_stream_set_parameters(struct amdtp_stream *s,
+				 unsigned int rate,
+				 unsigned int pcm_channels,
+				 unsigned int midi_ports)
 {
 	static const unsigned int rates[] = {
 		[CIP_SFC_32000]  =  32000,
@@ -103,7 +103,7 @@ void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
 	};
 	unsigned int sfc;
 
-	if (WARN_ON(amdtp_out_stream_running(s)))
+	if (WARN_ON(amdtp_stream_running(s)))
 		return;
 
 	for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
@@ -132,47 +132,47 @@ sfc_found:
 		/* additional buffering needed to adjust for no-data packets */
 		s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
 }
-EXPORT_SYMBOL(amdtp_out_stream_set_parameters);
+EXPORT_SYMBOL(amdtp_stream_set_parameters);
 
 /**
- * amdtp_out_stream_get_max_payload - get the stream's packet size
- * @s: the AMDTP output stream
+ * amdtp_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP stream
  *
  * This function must not be called before the stream has been configured
- * with amdtp_out_stream_set_parameters().
+ * with amdtp_stream_set_parameters().
  */
-unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s)
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
 {
 	return 8 + s->syt_interval * s->data_block_quadlets * 4;
 }
-EXPORT_SYMBOL(amdtp_out_stream_get_max_payload);
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
 
-static void amdtp_write_s16(struct amdtp_out_stream *s,
+static void amdtp_write_s16(struct amdtp_stream *s,
 			    struct snd_pcm_substream *pcm,
 			    __be32 *buffer, unsigned int frames);
-static void amdtp_write_s32(struct amdtp_out_stream *s,
+static void amdtp_write_s32(struct amdtp_stream *s,
 			    struct snd_pcm_substream *pcm,
 			    __be32 *buffer, unsigned int frames);
-static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
+static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
 				     struct snd_pcm_substream *pcm,
 				     __be32 *buffer, unsigned int frames);
-static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
+static void amdtp_write_s32_dualwire(struct amdtp_stream *s,
 				     struct snd_pcm_substream *pcm,
 				     __be32 *buffer, unsigned int frames);
 
 /**
- * amdtp_out_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP output stream to configure
+ * amdtp_stream_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
  * @format: the format of the ALSA PCM device
  *
  * The sample format must be set after the other paramters (rate/PCM channels/
  * MIDI) and before the stream is started, and must not be changed while the
  * stream is running.
  */
-void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
-				     snd_pcm_format_t format)
+void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
+				 snd_pcm_format_t format)
 {
-	if (WARN_ON(amdtp_out_stream_running(s)))
+	if (WARN_ON(amdtp_stream_running(s)))
 		return;
 
 	switch (format) {
@@ -193,24 +193,24 @@ void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
 		break;
 	}
 }
-EXPORT_SYMBOL(amdtp_out_stream_set_pcm_format);
+EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
 
 /**
- * amdtp_out_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP output stream
+ * amdtp_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP stream
  *
  * This function should be called from the PCM device's .prepare callback.
  */
-void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s)
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
 {
 	tasklet_kill(&s->period_tasklet);
 	s->pcm_buffer_pointer = 0;
 	s->pcm_period_pointer = 0;
 	s->pointer_flush = true;
 }
-EXPORT_SYMBOL(amdtp_out_stream_pcm_prepare);
+EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
 
-static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
+static unsigned int calculate_data_blocks(struct amdtp_stream *s)
 {
 	unsigned int phase, data_blocks;
 
@@ -243,7 +243,7 @@ static unsigned int calculate_data_blocks(struct amdtp_out_stream *s)
 	return data_blocks;
 }
 
-static unsigned int calculate_syt(struct amdtp_out_stream *s,
+static unsigned int calculate_syt(struct amdtp_stream *s,
 				  unsigned int cycle)
 {
 	unsigned int syt_offset, phase, index, syt;
@@ -286,7 +286,7 @@ static unsigned int calculate_syt(struct amdtp_out_stream *s,
 	}
 }
 
-static void amdtp_write_s32(struct amdtp_out_stream *s,
+static void amdtp_write_s32(struct amdtp_stream *s,
 			    struct snd_pcm_substream *pcm,
 			    __be32 *buffer, unsigned int frames)
 {
@@ -312,7 +312,7 @@ static void amdtp_write_s32(struct amdtp_out_stream *s,
 	}
 }
 
-static void amdtp_write_s16(struct amdtp_out_stream *s,
+static void amdtp_write_s16(struct amdtp_stream *s,
 			    struct snd_pcm_substream *pcm,
 			    __be32 *buffer, unsigned int frames)
 {
@@ -338,7 +338,7 @@ static void amdtp_write_s16(struct amdtp_out_stream *s,
 	}
 }
 
-static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
+static void amdtp_write_s32_dualwire(struct amdtp_stream *s,
 				     struct snd_pcm_substream *pcm,
 				     __be32 *buffer, unsigned int frames)
 {
@@ -369,7 +369,7 @@ static void amdtp_write_s32_dualwire(struct amdtp_out_stream *s,
 	}
 }
 
-static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
+static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
 				     struct snd_pcm_substream *pcm,
 				     __be32 *buffer, unsigned int frames)
 {
@@ -400,7 +400,7 @@ static void amdtp_write_s16_dualwire(struct amdtp_out_stream *s,
 	}
 }
 
-static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
+static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
 				   __be32 *buffer, unsigned int frames)
 {
 	unsigned int i, c;
@@ -412,7 +412,7 @@ static void amdtp_fill_pcm_silence(struct amdtp_out_stream *s,
 	}
 }
 
-static void amdtp_fill_midi(struct amdtp_out_stream *s,
+static void amdtp_fill_midi(struct amdtp_stream *s,
 			    __be32 *buffer, unsigned int frames)
 {
 	unsigned int i;
@@ -422,7 +422,7 @@ static void amdtp_fill_midi(struct amdtp_out_stream *s,
 						cpu_to_be32(0x80000000);
 }
 
-static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
+static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle)
 {
 	__be32 *buffer;
 	unsigned int index, data_blocks, syt, ptr;
@@ -473,7 +473,7 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
 	if (err < 0) {
 		dev_err(&s->unit->device, "queueing error: %d\n", err);
 		s->packet_index = -1;
-		amdtp_out_stream_pcm_abort(s);
+		amdtp_stream_pcm_abort(s);
 		return;
 	}
 
@@ -501,7 +501,7 @@ static void queue_out_packet(struct amdtp_out_stream *s, unsigned int cycle)
 
 static void pcm_period_tasklet(unsigned long data)
 {
-	struct amdtp_out_stream *s = (void *)data;
+	struct amdtp_stream *s = (void *)data;
 	struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
 
 	if (pcm)
@@ -509,9 +509,9 @@ static void pcm_period_tasklet(unsigned long data)
 }
 
 static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
-				size_t header_length, void *header, void *data)
+			size_t header_length, void *header, void *private_data)
 {
-	struct amdtp_out_stream *s = data;
+	struct amdtp_stream *s = private_data;
 	unsigned int i, packets = header_length / 4;
 
 	/*
@@ -526,7 +526,7 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
 	fw_iso_context_queue_flush(s->context);
 }
 
-static int queue_initial_skip_packets(struct amdtp_out_stream *s)
+static int queue_initial_skip_packets(struct amdtp_stream *s)
 {
 	struct fw_iso_packet skip_packet = {
 		.skip = 1,
@@ -548,16 +548,16 @@ static int queue_initial_skip_packets(struct amdtp_out_stream *s)
 }
 
 /**
- * amdtp_out_stream_start - start sending packets
- * @s: the AMDTP output stream to start
+ * amdtp_stream_start - start transferring packets
+ * @s: the AMDTP stream to start
  * @channel: the isochronous channel on the bus
  * @speed: firewire speed code
  *
  * The stream cannot be started until it has been configured with
- * amdtp_out_stream_set_parameters() and amdtp_out_stream_set_pcm_format(),
- * and it must be started before any PCM or MIDI device can be started.
+ * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
+ * device can be started.
  */
-int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 {
 	static const struct {
 		unsigned int data_block;
@@ -575,7 +575,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
 
 	mutex_lock(&s->mutex);
 
-	if (WARN_ON(amdtp_out_stream_running(s) ||
+	if (WARN_ON(amdtp_stream_running(s) ||
 		    (!s->pcm_channels && !s->midi_ports))) {
 		err = -EBADFD;
 		goto err_unlock;
@@ -586,7 +586,7 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
 	s->last_syt_offset = TICKS_PER_CYCLE;
 
 	err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
-				      amdtp_out_stream_get_max_payload(s),
+				      amdtp_stream_get_max_payload(s),
 				      DMA_TO_DEVICE);
 	if (err < 0)
 		goto err_unlock;
@@ -599,11 +599,11 @@ int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed)
 		err = PTR_ERR(s->context);
 		if (err == -EBUSY)
 			dev_err(&s->unit->device,
-				"no free output stream on this controller\n");
+				"no free stream on this controller\n");
 		goto err_buffer;
 	}
 
-	amdtp_out_stream_update(s);
+	amdtp_stream_update(s);
 
 	s->packet_index = 0;
 	s->data_block_counter = 0;
@@ -629,15 +629,15 @@ err_unlock:
 
 	return err;
 }
-EXPORT_SYMBOL(amdtp_out_stream_start);
+EXPORT_SYMBOL(amdtp_stream_start);
 
 /**
- * amdtp_out_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP output stream that transports the PCM data
+ * amdtp_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP stream that transports the PCM data
  *
  * Returns the current buffer position, in frames.
  */
-unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
 {
 	/* this optimization is allowed to be racy */
 	if (s->pointer_flush)
@@ -647,31 +647,31 @@ unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s)
 
 	return ACCESS_ONCE(s->pcm_buffer_pointer);
 }
-EXPORT_SYMBOL(amdtp_out_stream_pcm_pointer);
+EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
 
 /**
- * amdtp_out_stream_update - update the stream after a bus reset
- * @s: the AMDTP output stream
+ * amdtp_stream_update - update the stream after a bus reset
+ * @s: the AMDTP stream
  */
-void amdtp_out_stream_update(struct amdtp_out_stream *s)
+void amdtp_stream_update(struct amdtp_stream *s)
 {
 	ACCESS_ONCE(s->source_node_id_field) =
 		(fw_parent_device(s->unit)->card->node_id & 0x3f) << 24;
 }
-EXPORT_SYMBOL(amdtp_out_stream_update);
+EXPORT_SYMBOL(amdtp_stream_update);
 
 /**
- * amdtp_out_stream_stop - stop sending packets
- * @s: the AMDTP output stream to stop
+ * amdtp_stream_stop - stop sending packets
+ * @s: the AMDTP stream to stop
  *
  * All PCM and MIDI devices of the stream must be stopped before the stream
  * itself can be stopped.
  */
-void amdtp_out_stream_stop(struct amdtp_out_stream *s)
+void amdtp_stream_stop(struct amdtp_stream *s)
 {
 	mutex_lock(&s->mutex);
 
-	if (!amdtp_out_stream_running(s)) {
+	if (!amdtp_stream_running(s)) {
 		mutex_unlock(&s->mutex);
 		return;
 	}
@@ -684,16 +684,16 @@ void amdtp_out_stream_stop(struct amdtp_out_stream *s)
 
 	mutex_unlock(&s->mutex);
 }
-EXPORT_SYMBOL(amdtp_out_stream_stop);
+EXPORT_SYMBOL(amdtp_stream_stop);
 
 /**
- * amdtp_out_stream_pcm_abort - abort the running PCM device
+ * amdtp_stream_pcm_abort - abort the running PCM device
  * @s: the AMDTP stream about to be stopped
  *
  * If the isochronous stream needs to be stopped asynchronously, call this
  * function first to stop the PCM device.
  */
-void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s)
+void amdtp_stream_pcm_abort(struct amdtp_stream *s)
 {
 	struct snd_pcm_substream *pcm;
 
@@ -705,4 +705,4 @@ void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s)
 		snd_pcm_stream_unlock_irq(pcm);
 	}
 }
-EXPORT_SYMBOL(amdtp_out_stream_pcm_abort);
+EXPORT_SYMBOL(amdtp_stream_pcm_abort);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 2746ecd..60028c7 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -8,7 +8,7 @@
 #include "packets-buffer.h"
 
 /**
- * enum cip_out_flags - describes details of the streaming protocol
+ * enum cip_flags - describes details of the streaming protocol
  * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
  *	sample_rate/8000 samples, with rounding up or down to adjust
  *	for clock skew and left-over fractional samples.  This should
@@ -21,7 +21,7 @@
  *	two samples of a channel are stored consecutively in the packet.
  *	Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
  */
-enum cip_out_flags {
+enum cip_flags {
 	CIP_NONBLOCKING	= 0x00,
 	CIP_BLOCKING	= 0x01,
 	CIP_HI_DUALWIRE	= 0x02,
@@ -48,9 +48,9 @@ struct fw_unit;
 struct fw_iso_context;
 struct snd_pcm_substream;
 
-struct amdtp_out_stream {
+struct amdtp_stream {
 	struct fw_unit *unit;
-	enum cip_out_flags flags;
+	enum cip_flags flags;
 	struct fw_iso_context *context;
 	struct mutex mutex;
 
@@ -59,7 +59,7 @@ struct amdtp_out_stream {
 	unsigned int data_block_quadlets;
 	unsigned int pcm_channels;
 	unsigned int midi_ports;
-	void (*transfer_samples)(struct amdtp_out_stream *s,
+	void (*transfer_samples)(struct amdtp_stream *s,
 				 struct snd_pcm_substream *pcm,
 				 __be32 *buffer, unsigned int frames);
 
@@ -84,56 +84,62 @@ struct amdtp_out_stream {
 	bool pointer_flush;
 };
 
-int amdtp_out_stream_init(struct amdtp_out_stream *s, struct fw_unit *unit,
-			  enum cip_out_flags flags);
-void amdtp_out_stream_destroy(struct amdtp_out_stream *s);
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+		      enum cip_flags flags);
+void amdtp_stream_destroy(struct amdtp_stream *s);
 
-void amdtp_out_stream_set_parameters(struct amdtp_out_stream *s,
-				     unsigned int rate,
-				     unsigned int pcm_channels,
-				     unsigned int midi_ports);
-unsigned int amdtp_out_stream_get_max_payload(struct amdtp_out_stream *s);
+void amdtp_stream_set_parameters(struct amdtp_stream *s,
+				 unsigned int rate,
+				 unsigned int pcm_channels,
+				 unsigned int midi_ports);
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
 
-int amdtp_out_stream_start(struct amdtp_out_stream *s, int channel, int speed);
-void amdtp_out_stream_update(struct amdtp_out_stream *s);
-void amdtp_out_stream_stop(struct amdtp_out_stream *s);
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
+void amdtp_stream_update(struct amdtp_stream *s);
+void amdtp_stream_stop(struct amdtp_stream *s);
 
-void amdtp_out_stream_set_pcm_format(struct amdtp_out_stream *s,
-				     snd_pcm_format_t format);
-void amdtp_out_stream_pcm_prepare(struct amdtp_out_stream *s);
-unsigned long amdtp_out_stream_pcm_pointer(struct amdtp_out_stream *s);
-void amdtp_out_stream_pcm_abort(struct amdtp_out_stream *s);
+void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
+				 snd_pcm_format_t format);
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
+void amdtp_stream_pcm_abort(struct amdtp_stream *s);
 
 extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
 
-static inline bool amdtp_out_stream_running(struct amdtp_out_stream *s)
+/**
+ * amdtp_stream_running - check stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream is running.
+ */
+static inline bool amdtp_stream_running(struct amdtp_stream *s)
 {
 	return !IS_ERR(s->context);
 }
 
 /**
- * amdtp_out_streaming_error - check for streaming error
- * @s: the AMDTP output stream
+ * amdtp_streaming_error - check for streaming error
+ * @s: the AMDTP 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)
+static inline bool amdtp_streaming_error(struct amdtp_stream *s)
 {
 	return s->packet_index < 0;
 }
 
 /**
- * amdtp_out_stream_pcm_trigger - start/stop playback from a PCM device
- * @s: the AMDTP output stream
+ * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP stream
  * @pcm: the PCM device to be started, or %NULL to stop the current device
  *
  * Call this function on a running isochronous stream to enable the actual
  * transmission of PCM data.  This function should be called from the PCM
  * device's .trigger callback.
  */
-static inline void amdtp_out_stream_pcm_trigger(struct amdtp_out_stream *s,
-						struct snd_pcm_substream *pcm)
+static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
+					    struct snd_pcm_substream *pcm)
 {
 	ACCESS_ONCE(s->pcm) = pcm;
 }
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index 0c39486..ca12fcb 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -51,7 +51,7 @@ struct dice {
 	wait_queue_head_t hwdep_wait;
 	u32 notification_bits;
 	struct fw_iso_resources resources;
-	struct amdtp_out_stream stream;
+	struct amdtp_stream stream;
 };
 
 MODULE_DESCRIPTION("DICE driver");
@@ -460,17 +460,17 @@ static int dice_stream_start_packets(struct dice *dice)
 {
 	int err;
 
-	if (amdtp_out_stream_running(&dice->stream))
+	if (amdtp_stream_running(&dice->stream))
 		return 0;
 
-	err = amdtp_out_stream_start(&dice->stream, dice->resources.channel,
-				     fw_parent_device(dice->unit)->max_speed);
+	err = amdtp_stream_start(&dice->stream, dice->resources.channel,
+				 fw_parent_device(dice->unit)->max_speed);
 	if (err < 0)
 		return err;
 
 	err = dice_enable_set(dice);
 	if (err < 0) {
-		amdtp_out_stream_stop(&dice->stream);
+		amdtp_stream_stop(&dice->stream);
 		return err;
 	}
 
@@ -484,7 +484,7 @@ static int dice_stream_start(struct dice *dice)
 
 	if (!dice->resources.allocated) {
 		err = fw_iso_resources_allocate(&dice->resources,
-				amdtp_out_stream_get_max_payload(&dice->stream),
+				amdtp_stream_get_max_payload(&dice->stream),
 				fw_parent_device(dice->unit)->max_speed);
 		if (err < 0)
 			goto error;
@@ -516,9 +516,9 @@ error:
 
 static void dice_stream_stop_packets(struct dice *dice)
 {
-	if (amdtp_out_stream_running(&dice->stream)) {
+	if (amdtp_stream_running(&dice->stream)) {
 		dice_enable_clear(dice);
-		amdtp_out_stream_stop(&dice->stream);
+		amdtp_stream_stop(&dice->stream);
 	}
 }
 
@@ -581,12 +581,12 @@ static int dice_hw_params(struct snd_pcm_substream *substream,
 		return err;
 
 	mode = rate_index_to_mode(rate_index);
-	amdtp_out_stream_set_parameters(&dice->stream,
-					params_rate(hw_params),
-					params_channels(hw_params),
-					dice->rx_midi_ports[mode]);
-	amdtp_out_stream_set_pcm_format(&dice->stream,
-					params_format(hw_params));
+	amdtp_stream_set_parameters(&dice->stream,
+				    params_rate(hw_params),
+				    params_channels(hw_params),
+				    dice->rx_midi_ports[mode]);
+	amdtp_stream_set_pcm_format(&dice->stream,
+				    params_format(hw_params));
 
 	return 0;
 }
@@ -609,7 +609,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
 
 	mutex_lock(&dice->mutex);
 
-	if (amdtp_out_streaming_error(&dice->stream))
+	if (amdtp_streaming_error(&dice->stream))
 		dice_stream_stop_packets(dice);
 
 	err = dice_stream_start(dice);
@@ -620,7 +620,7 @@ static int dice_prepare(struct snd_pcm_substream *substream)
 
 	mutex_unlock(&dice->mutex);
 
-	amdtp_out_stream_pcm_prepare(&dice->stream);
+	amdtp_stream_pcm_prepare(&dice->stream);
 
 	return 0;
 }
@@ -640,7 +640,7 @@ static int dice_trigger(struct snd_pcm_substream *substream, int cmd)
 	default:
 		return -EINVAL;
 	}
-	amdtp_out_stream_pcm_trigger(&dice->stream, pcm);
+	amdtp_stream_pcm_trigger(&dice->stream, pcm);
 
 	return 0;
 }
@@ -649,7 +649,7 @@ static snd_pcm_uframes_t dice_pointer(struct snd_pcm_substream *substream)
 {
 	struct dice *dice = substream->private_data;
 
-	return amdtp_out_stream_pcm_pointer(&dice->stream);
+	return amdtp_stream_pcm_pointer(&dice->stream);
 }
 
 static int dice_create_pcm(struct dice *dice)
@@ -1104,7 +1104,7 @@ static void dice_card_free(struct snd_card *card)
 {
 	struct dice *dice = card->private_data;
 
-	amdtp_out_stream_destroy(&dice->stream);
+	amdtp_stream_destroy(&dice->stream);
 	fw_core_remove_address_handler(&dice->notification_handler);
 	mutex_destroy(&dice->mutex);
 }
@@ -1360,8 +1360,8 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 		goto err_owner;
 	dice->resources.channels_mask = 0x00000000ffffffffuLL;
 
-	err = amdtp_out_stream_init(&dice->stream, unit,
-				    CIP_BLOCKING | CIP_HI_DUALWIRE);
+	err = amdtp_stream_init(&dice->stream, unit,
+				CIP_BLOCKING | CIP_HI_DUALWIRE);
 	if (err < 0)
 		goto err_resources;
 
@@ -1417,7 +1417,7 @@ static void dice_remove(struct fw_unit *unit)
 {
 	struct dice *dice = dev_get_drvdata(&unit->device);
 
-	amdtp_out_stream_pcm_abort(&dice->stream);
+	amdtp_stream_pcm_abort(&dice->stream);
 
 	snd_card_disconnect(dice->card);
 
@@ -1443,7 +1443,7 @@ static void dice_bus_reset(struct fw_unit *unit)
 	 * to stop so that the application can restart them in an orderly
 	 * manner.
 	 */
-	amdtp_out_stream_pcm_abort(&dice->stream);
+	amdtp_stream_pcm_abort(&dice->stream);
 
 	mutex_lock(&dice->mutex);
 
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 9f7ef21..2096ad7 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -51,7 +51,7 @@ struct fwspk {
 	const struct device_info *device_info;
 	struct mutex mutex;
 	struct cmp_connection connection;
-	struct amdtp_out_stream stream;
+	struct amdtp_stream stream;
 	bool mute;
 	s16 volume[6];
 	s16 volume_min;
@@ -187,8 +187,8 @@ static int fwspk_close(struct snd_pcm_substream *substream)
 
 static void fwspk_stop_stream(struct fwspk *fwspk)
 {
-	if (amdtp_out_stream_running(&fwspk->stream)) {
-		amdtp_out_stream_stop(&fwspk->stream);
+	if (amdtp_stream_running(&fwspk->stream)) {
+		amdtp_stream_stop(&fwspk->stream);
 		cmp_connection_break(&fwspk->connection);
 	}
 }
@@ -244,13 +244,13 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
 	if (err < 0)
 		goto error;
 
-	amdtp_out_stream_set_parameters(&fwspk->stream,
-					params_rate(hw_params),
-					params_channels(hw_params),
-					0);
+	amdtp_stream_set_parameters(&fwspk->stream,
+				    params_rate(hw_params),
+				    params_channels(hw_params),
+				    0);
 
-	amdtp_out_stream_set_pcm_format(&fwspk->stream,
-					params_format(hw_params));
+	amdtp_stream_set_pcm_format(&fwspk->stream,
+				    params_format(hw_params));
 
 	err = fwspk_set_rate(fwspk, fwspk->stream.sfc);
 	if (err < 0)
@@ -282,25 +282,25 @@ static int fwspk_prepare(struct snd_pcm_substream *substream)
 
 	mutex_lock(&fwspk->mutex);
 
-	if (amdtp_out_streaming_error(&fwspk->stream))
+	if (amdtp_streaming_error(&fwspk->stream))
 		fwspk_stop_stream(fwspk);
 
-	if (!amdtp_out_stream_running(&fwspk->stream)) {
+	if (!amdtp_stream_running(&fwspk->stream)) {
 		err = cmp_connection_establish(&fwspk->connection,
-			amdtp_out_stream_get_max_payload(&fwspk->stream));
+			amdtp_stream_get_max_payload(&fwspk->stream));
 		if (err < 0)
 			goto err_mutex;
 
-		err = amdtp_out_stream_start(&fwspk->stream,
-					fwspk->connection.resources.channel,
-					fwspk->connection.speed);
+		err = amdtp_stream_start(&fwspk->stream,
+					 fwspk->connection.resources.channel,
+					 fwspk->connection.speed);
 		if (err < 0)
 			goto err_connection;
 	}
 
 	mutex_unlock(&fwspk->mutex);
 
-	amdtp_out_stream_pcm_prepare(&fwspk->stream);
+	amdtp_stream_pcm_prepare(&fwspk->stream);
 
 	return 0;
 
@@ -327,7 +327,7 @@ static int fwspk_trigger(struct snd_pcm_substream *substream, int cmd)
 	default:
 		return -EINVAL;
 	}
-	amdtp_out_stream_pcm_trigger(&fwspk->stream, pcm);
+	amdtp_stream_pcm_trigger(&fwspk->stream, pcm);
 	return 0;
 }
 
@@ -335,7 +335,7 @@ static snd_pcm_uframes_t fwspk_pointer(struct snd_pcm_substream *substream)
 {
 	struct fwspk *fwspk = substream->private_data;
 
-	return amdtp_out_stream_pcm_pointer(&fwspk->stream);
+	return amdtp_stream_pcm_pointer(&fwspk->stream);
 }
 
 static int fwspk_create_pcm(struct fwspk *fwspk)
@@ -653,7 +653,7 @@ static void fwspk_card_free(struct snd_card *card)
 {
 	struct fwspk *fwspk = card->private_data;
 
-	amdtp_out_stream_destroy(&fwspk->stream);
+	amdtp_stream_destroy(&fwspk->stream);
 	cmp_connection_destroy(&fwspk->connection);
 	fw_unit_put(fwspk->unit);
 	mutex_destroy(&fwspk->mutex);
@@ -683,7 +683,7 @@ static int fwspk_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto err_unit;
 
-	err = amdtp_out_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING);
+	err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING);
 	if (err < 0)
 		goto err_connection;
 
@@ -733,21 +733,21 @@ static void fwspk_bus_reset(struct fw_unit *unit)
 	fcp_bus_reset(fwspk->unit);
 
 	if (cmp_connection_update(&fwspk->connection) < 0) {
-		amdtp_out_stream_pcm_abort(&fwspk->stream);
+		amdtp_stream_pcm_abort(&fwspk->stream);
 		mutex_lock(&fwspk->mutex);
 		fwspk_stop_stream(fwspk);
 		mutex_unlock(&fwspk->mutex);
 		return;
 	}
 
-	amdtp_out_stream_update(&fwspk->stream);
+	amdtp_stream_update(&fwspk->stream);
 }
 
 static void fwspk_remove(struct fw_unit *unit)
 {
 	struct fwspk *fwspk = dev_get_drvdata(&unit->device);
 
-	amdtp_out_stream_pcm_abort(&fwspk->stream);
+	amdtp_stream_pcm_abort(&fwspk->stream);
 	snd_card_disconnect(fwspk->card);
 
 	mutex_lock(&fwspk->mutex);
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 02/39] firewire-lib: Add macros instead of fixed value for AMDTP
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 01/39] firewire-lib: Rename functions, structure, member for AMDTP Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 03/39] firewire-lib: Add 'direction' member to 'amdtp_stream' structure Takashi Sakamoto
                   ` (37 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This patch adds some macros instead of fixed value for AMDTP in IEC 61883-6.
These macros will also be used by followed patches.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 30 +++++++++++++++++++++++-------
 1 file changed, 23 insertions(+), 7 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 16fe858..38013f9 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -20,12 +20,28 @@
 
 #define TRANSFER_DELAY_TICKS	0x2e00 /* 479.17 µs */
 
+/* isochronous header parameters */
+#define ISO_DATA_LENGTH_SHIFT	16
 #define TAG_CIP			1
 
+/* common isochronous packet header parameters */
 #define CIP_EOH			(1u << 31)
+#define CIP_EOH_MASK		0x80000000
 #define CIP_FMT_AM		(0x10 << 24)
-#define AMDTP_FDF_AM824		(0 << 19)
-#define AMDTP_FDF_SFC_SHIFT	16
+#define CIP_FMT_MASK		0x3f000000
+#define CIP_SYT_MASK		0x0000ffff
+#define CIP_SYT_NO_INFO		0xffff
+#define CIP_FDF_MASK		0x00ff0000
+#define CIP_FDF_SFC_SHIFT	16
+
+/*
+ * Audio and Music transfer protocol specific parameters
+ * only "Clock-based rate control mode" is supported
+ */
+#define AMDTP_FDF_AM824		(0 << (CIP_FDF_SFC_SHIFT + 3))
+#define AMDTP_DBS_MASK		0x00ff0000
+#define AMDTP_DBS_SHIFT		16
+#define AMDTP_DBC_MASK		0x000000ff
 
 /* TODO: make these configurable */
 #define INTERRUPT_INTERVAL	16
@@ -280,9 +296,9 @@ static unsigned int calculate_syt(struct amdtp_stream *s,
 		syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
 		syt += syt_offset % TICKS_PER_CYCLE;
 
-		return syt & 0xffff;
+		return syt & CIP_SYT_MASK;
 	} else {
-		return 0xffff; /* no info */
+		return CIP_SYT_NO_INFO;
 	}
 }
 
@@ -438,17 +454,17 @@ static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle)
 	syt = calculate_syt(s, cycle);
 	if (!(s->flags & CIP_BLOCKING))
 		data_blocks = calculate_data_blocks(s);
-	else if (syt != 0xffff)
+	else if (syt != CIP_SYT_NO_INFO)
 		data_blocks = s->syt_interval;
 	else
 		data_blocks = 0;
 
 	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_quadlets << AMDTP_DBS_SHIFT) |
 				s->data_block_counter);
 	buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
-				(s->sfc << AMDTP_FDF_SFC_SHIFT) | syt);
+				(s->sfc << CIP_FDF_SFC_SHIFT) | syt);
 	buffer += 2;
 
 	pcm = ACCESS_ONCE(s->pcm);
-- 
1.8.3.2

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 03/39] firewire-lib: Add 'direction' member to 'amdtp_stream' structure
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 01/39] firewire-lib: Rename functions, structure, member for AMDTP Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 02/39] firewire-lib: Add macros instead of fixed value " Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 04/39] firewire-lib: Split some codes into functions to reuse for both streams Takashi Sakamoto
                   ` (36 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This patch adds 'direction' member to amdtp_stream structure to indicate its
direction. This patch also adds 'direction' argument to amdtp_stream_init()
function to determine its direction.

The amdtp_stream_init() function is exported and used by firewire-speakers and
dice so this patch also affects them.

This patch just add them. Actual implementation will be done by followed
patches.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c    | 4 +++-
 sound/firewire/amdtp.h    | 7 +++++++
 sound/firewire/dice.c     | 2 +-
 sound/firewire/speakers.c | 3 ++-
 4 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 38013f9..95c5a15 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -53,12 +53,14 @@ static void pcm_period_tasklet(unsigned long data);
  * amdtp_stream_init - initialize an AMDTP stream structure
  * @s: the AMDTP stream to initialize
  * @unit: the target of the stream
+ * @dir: the direction of stream
  * @flags: the packet transmission method to use
  */
 int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
-		      enum cip_flags flags)
+		      enum amdtp_stream_direction dir, enum cip_flags flags)
 {
 	s->unit = fw_unit_get(unit);
+	s->direction = dir;
 	s->flags = flags;
 	s->context = ERR_PTR(-1);
 	mutex_init(&s->mutex);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 60028c7..019134e 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -48,9 +48,15 @@ struct fw_unit;
 struct fw_iso_context;
 struct snd_pcm_substream;
 
+enum amdtp_stream_direction {
+	AMDTP_OUT_STREAM = 0,
+	AMDTP_IN_STREAM
+};
+
 struct amdtp_stream {
 	struct fw_unit *unit;
 	enum cip_flags flags;
+	enum amdtp_stream_direction direction;
 	struct fw_iso_context *context;
 	struct mutex mutex;
 
@@ -85,6 +91,7 @@ struct amdtp_stream {
 };
 
 int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+		      enum amdtp_stream_direction dir,
 		      enum cip_flags flags);
 void amdtp_stream_destroy(struct amdtp_stream *s);
 
diff --git a/sound/firewire/dice.c b/sound/firewire/dice.c
index ca12fcb..26b2158 100644
--- a/sound/firewire/dice.c
+++ b/sound/firewire/dice.c
@@ -1360,7 +1360,7 @@ static int dice_probe(struct fw_unit *unit, const struct ieee1394_device_id *id)
 		goto err_owner;
 	dice->resources.channels_mask = 0x00000000ffffffffuLL;
 
-	err = amdtp_stream_init(&dice->stream, unit,
+	err = amdtp_stream_init(&dice->stream, unit, AMDTP_OUT_STREAM,
 				CIP_BLOCKING | CIP_HI_DUALWIRE);
 	if (err < 0)
 		goto err_resources;
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 2096ad7..c07e7cd 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -683,7 +683,8 @@ static int fwspk_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto err_unit;
 
-	err = amdtp_stream_init(&fwspk->stream, unit, CIP_NONBLOCKING);
+	err = amdtp_stream_init(&fwspk->stream, unit, AMDTP_OUT_STREAM,
+				CIP_NONBLOCKING);
 	if (err < 0)
 		goto err_connection;
 
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 04/39] firewire-lib: Split some codes into functions to reuse for both streams
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (2 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 03/39] firewire-lib: Add 'direction' member to 'amdtp_stream' structure Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 05/39] firewire-lib: Add support for AMDTP in-stream and PCM capture Takashi Sakamoto
                   ` (35 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Some codes can be reused to handle in stream. This commit adds new functions.
This commit also renames some functions to keep naming consistency.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 156 ++++++++++++++++++++++++++-----------------------
 1 file changed, 82 insertions(+), 74 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 95c5a15..ca79cb4 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -47,6 +47,8 @@
 #define INTERRUPT_INTERVAL	16
 #define QUEUE_LENGTH		48
 
+#define OUT_PACKET_HEADER_SIZE	0
+
 static void pcm_period_tasklet(unsigned long data);
 
 /**
@@ -440,13 +442,74 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
 						cpu_to_be32(0x80000000);
 }
 
-static void queue_out_packet(struct amdtp_stream *s, unsigned int cycle)
+static void update_pcm_pointers(struct amdtp_stream *s,
+				struct snd_pcm_substream *pcm,
+				unsigned int frames)
+{	unsigned int ptr;
+
+	if (s->dual_wire)
+		frames *= 2;
+
+	ptr = s->pcm_buffer_pointer + frames;
+	if (ptr >= pcm->runtime->buffer_size)
+		ptr -= pcm->runtime->buffer_size;
+	ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
+
+	s->pcm_period_pointer += frames;
+	if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+		s->pcm_period_pointer -= pcm->runtime->period_size;
+		s->pointer_flush = false;
+		tasklet_hi_schedule(&s->period_tasklet);
+	}
+}
+
+static void pcm_period_tasklet(unsigned long data)
+{
+	struct amdtp_stream *s = (void *)data;
+	struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+
+	if (pcm)
+		snd_pcm_period_elapsed(pcm);
+}
+
+static int queue_packet(struct amdtp_stream *s,
+			unsigned int header_length,
+			unsigned int payload_length, bool skip)
+{
+	struct fw_iso_packet p = {0};
+	int err;
+
+	p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
+	p.tag = TAG_CIP;
+	p.header_length = header_length;
+	p.payload_length = (!skip) ? payload_length : 0;
+	p.skip = skip;
+	err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
+				   s->buffer.packets[s->packet_index].offset);
+	if (err < 0) {
+		dev_err(&s->unit->device, "queueing error: %d\n", err);
+		s->packet_index = -1;
+		goto end;
+	}
+
+	if (++s->packet_index >= QUEUE_LENGTH)
+		s->packet_index = 0;
+end:
+	return err;
+}
+
+static inline int queue_out_packet(struct amdtp_stream *s,
+				   unsigned int payload_length, bool skip)
+{
+	return queue_packet(s, OUT_PACKET_HEADER_SIZE,
+			    payload_length, skip);
+}
+
+static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle)
 {
 	__be32 *buffer;
-	unsigned int index, data_blocks, syt, ptr;
+	unsigned int index, data_blocks, syt, payload_length;
 	struct snd_pcm_substream *pcm;
-	struct fw_iso_packet packet;
-	int err;
 
 	if (s->packet_index < 0)
 		return;
@@ -479,55 +542,19 @@ static void queue_out_packet(struct amdtp_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(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[index].offset);
-	if (err < 0) {
-		dev_err(&s->unit->device, "queueing error: %d\n", err);
-		s->packet_index = -1;
+	payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
+	if (queue_out_packet(s, payload_length, false) < 0) {
 		amdtp_stream_pcm_abort(s);
 		return;
 	}
 
-	if (++index >= QUEUE_LENGTH)
-		index = 0;
-	s->packet_index = index;
-
-	if (pcm) {
-		if (s->dual_wire)
-			data_blocks *= 2;
-
-		ptr = s->pcm_buffer_pointer + data_blocks;
-		if (ptr >= pcm->runtime->buffer_size)
-			ptr -= pcm->runtime->buffer_size;
-		ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
-
-		s->pcm_period_pointer += data_blocks;
-		if (s->pcm_period_pointer >= pcm->runtime->period_size) {
-			s->pcm_period_pointer -= pcm->runtime->period_size;
-			s->pointer_flush = false;
-			tasklet_hi_schedule(&s->period_tasklet);
-		}
-	}
-}
-
-static void pcm_period_tasklet(unsigned long data)
-{
-	struct amdtp_stream *s = (void *)data;
-	struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
-
 	if (pcm)
-		snd_pcm_period_elapsed(pcm);
+		update_pcm_pointers(s, pcm, data_blocks);
 }
 
-static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
-			size_t header_length, void *header, void *private_data)
+static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+				size_t header_length, void *header,
+				void *private_data)
 {
 	struct amdtp_stream *s = private_data;
 	unsigned int i, packets = header_length / 4;
@@ -540,31 +567,10 @@ static void out_packet_callback(struct fw_iso_context *context, u32 cycle,
 	cycle += QUEUE_LENGTH - packets;
 
 	for (i = 0; i < packets; ++i)
-		queue_out_packet(s, ++cycle);
+		handle_out_packet(s, ++cycle);
 	fw_iso_context_queue_flush(s->context);
 }
 
-static int queue_initial_skip_packets(struct amdtp_stream *s)
-{
-	struct fw_iso_packet skip_packet = {
-		.skip = 1,
-	};
-	unsigned int i;
-	int err;
-
-	for (i = 0; i < QUEUE_LENGTH; ++i) {
-		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_index >= QUEUE_LENGTH)
-			s->packet_index = 0;
-	}
-
-	return 0;
-}
-
 /**
  * amdtp_stream_start - start transferring packets
  * @s: the AMDTP stream to start
@@ -594,7 +600,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 	mutex_lock(&s->mutex);
 
 	if (WARN_ON(amdtp_stream_running(s) ||
-		    (!s->pcm_channels && !s->midi_ports))) {
+		    (s->data_block_quadlets < 1))) {
 		err = -EBADFD;
 		goto err_unlock;
 	}
@@ -612,7 +618,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 	s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
 					   FW_ISO_CONTEXT_TRANSMIT,
 					   channel, speed, 0,
-					   out_packet_callback, s);
+					   out_stream_callback, s);
 	if (IS_ERR(s->context)) {
 		err = PTR_ERR(s->context);
 		if (err == -EBUSY)
@@ -624,11 +630,13 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 	amdtp_stream_update(s);
 
 	s->packet_index = 0;
-	s->data_block_counter = 0;
-	err = queue_initial_skip_packets(s);
-	if (err < 0)
-		goto err_context;
+	do {
+		err = queue_out_packet(s, 0, true);
+		if (err < 0)
+			goto err_context;
+	} while (s->packet_index > 0);
 
+	s->data_block_counter = 0;
 	err = fw_iso_context_start(s->context, -1, 0, 0);
 	if (err < 0)
 		goto err_context;
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 05/39] firewire-lib: Add support for AMDTP in-stream and PCM capture
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (3 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 04/39] firewire-lib: Split some codes into functions to reuse for both streams Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 06/39] firewire-lib: Add support for MIDI capture/playback Takashi Sakamoto
                   ` (34 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

For capturing PCM, this commit adds the functionality to handle in-stream.
This is also applied for dual-wire mode.

Currently, capturing 32bit samples are supported.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 218 +++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 201 insertions(+), 17 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index ca79cb4..4ebfd67 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -39,6 +39,7 @@
  * only "Clock-based rate control mode" is supported
  */
 #define AMDTP_FDF_AM824		(0 << (CIP_FDF_SFC_SHIFT + 3))
+#define AMDTP_FDF_NO_DATA	0xff
 #define AMDTP_DBS_MASK		0x00ff0000
 #define AMDTP_DBS_SHIFT		16
 #define AMDTP_DBC_MASK		0x000000ff
@@ -47,6 +48,7 @@
 #define INTERRUPT_INTERVAL	16
 #define QUEUE_LENGTH		48
 
+#define IN_PACKET_HEADER_SIZE	4
 #define OUT_PACKET_HEADER_SIZE	0
 
 static void pcm_period_tasklet(unsigned long data);
@@ -179,6 +181,12 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
 static void amdtp_write_s32_dualwire(struct amdtp_stream *s,
 				     struct snd_pcm_substream *pcm,
 				     __be32 *buffer, unsigned int frames);
+static void amdtp_read_s32(struct amdtp_stream *s,
+			   struct snd_pcm_substream *pcm,
+			   __be32 *buffer, unsigned int frames);
+static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
+				    struct snd_pcm_substream *pcm,
+				    __be32 *buffer, unsigned int frames);
 
 /**
  * amdtp_stream_set_pcm_format - set the PCM format
@@ -200,16 +208,26 @@ void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
 		WARN_ON(1);
 		/* fall through */
 	case SNDRV_PCM_FORMAT_S16:
-		if (s->dual_wire)
-			s->transfer_samples = amdtp_write_s16_dualwire;
-		else
-			s->transfer_samples = amdtp_write_s16;
-		break;
+		if (s->direction == AMDTP_OUT_STREAM) {
+			if (s->dual_wire)
+				s->transfer_samples = amdtp_write_s16_dualwire;
+			else
+				s->transfer_samples = amdtp_write_s16;
+			break;
+		}
+		WARN_ON(1);
+		/* fall through */
 	case SNDRV_PCM_FORMAT_S32:
-		if (s->dual_wire)
-			s->transfer_samples = amdtp_write_s32_dualwire;
-		else
-			s->transfer_samples = amdtp_write_s32;
+		if (s->direction == AMDTP_OUT_STREAM) {
+			if (s->dual_wire)
+				s->transfer_samples = amdtp_write_s32_dualwire;
+			else
+				s->transfer_samples = amdtp_write_s32;
+		} else if (s->dual_wire) {
+			s->transfer_samples = amdtp_read_s32_dualwire;
+		} else {
+			s->transfer_samples = amdtp_read_s32;
+		}
 		break;
 	}
 }
@@ -420,6 +438,58 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
 	}
 }
 
+static void amdtp_read_s32(struct amdtp_stream *s,
+			   struct snd_pcm_substream *pcm,
+			   __be32 *buffer, unsigned int frames)
+{
+	struct snd_pcm_runtime *runtime = pcm->runtime;
+	unsigned int remaining_frames, i, c;
+	u32 *dst;
+
+	dst  = (void *)runtime->dma_area +
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+	for (i = 0; i < frames; ++i) {
+		for (c = 0; c < s->pcm_channels; ++c) {
+			*dst = be32_to_cpu(buffer[c]) << 8;
+			dst++;
+		}
+		buffer += s->data_block_quadlets;
+		if (--remaining_frames == 0)
+			dst = (void *)runtime->dma_area;
+	}
+}
+
+static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
+				    struct snd_pcm_substream *pcm,
+				    __be32 *buffer, unsigned int frames)
+{
+	struct snd_pcm_runtime *runtime = pcm->runtime;
+	unsigned int channels, remaining_frames, i, c;
+	u32 *dst;
+
+	dst = (void *)runtime->dma_area +
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+	channels = s->pcm_channels / 2;
+
+	for (i = 0; i < frames; ++i) {
+		for (c = 0; c < channels; ++c) {
+			*dst = be32_to_cpu(buffer[c * 2]) << 8;
+			dst++;
+		}
+		buffer += 1;
+		for (c = 0; c < channels; ++c) {
+			*dst = be32_to_cpu(buffer[c * 2]) << 8;
+			dst++;
+		}
+		buffer += s->data_block_quadlets - 1;
+		if (--remaining_frames == 0)
+			dst = (void *)runtime->dma_area;
+	}
+}
+
 static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
 				   __be32 *buffer, unsigned int frames)
 {
@@ -505,6 +575,12 @@ static inline int queue_out_packet(struct amdtp_stream *s,
 			    payload_length, skip);
 }
 
+static inline int queue_in_packet(struct amdtp_stream *s)
+{
+	return queue_packet(s, IN_PACKET_HEADER_SIZE,
+			    amdtp_stream_get_max_payload(s), false);
+}
+
 static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle)
 {
 	__be32 *buffer;
@@ -552,6 +628,67 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle)
 		update_pcm_pointers(s, pcm, data_blocks);
 }
 
+static void handle_in_packet(struct amdtp_stream *s,
+			     unsigned int payload_quadlets,
+			     __be32 *buffer)
+{
+	u32 cip_header[2];
+	unsigned int data_block_quadlets, data_blocks, data_block_counter;
+	struct snd_pcm_substream *pcm;
+
+	cip_header[0] = be32_to_cpu(buffer[0]);
+	cip_header[1] = be32_to_cpu(buffer[1]);
+
+	/*
+	 * This module supports 'Two-quadlet CIP header with SYT field'.
+	 * For convinience, also check FMT field is AM824 or not.
+	 */
+	if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+	    ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
+	    ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
+		dev_info_ratelimited(&s->unit->device,
+				"Invalid CIP header for AMDTP: %08X:%08X\n",
+				cip_header[0], cip_header[1]);
+		return;
+	}
+
+	/* ignore empty CIP packet or NO-DATA AMDTP packet */
+	if ((payload_quadlets < 3) ||
+	    (((cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SFC_SHIFT) ==
+							AMDTP_FDF_NO_DATA))
+		return;
+
+	data_block_quadlets =
+			(cip_header[1] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
+	/* avoid division by zero */
+	if (data_block_quadlets == 0) {
+		dev_info_ratelimited(&s->unit->device,
+				 "Detect invalid value in dbs field: %08X\n",
+				 data_block_quadlets);
+		return;
+	}
+
+	data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+	data_block_counter = cip_header[1] & AMDTP_DBC_MASK;
+
+	/* check packet continuity */
+	s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+	if (s->data_block_counter != data_block_counter) {
+		dev_info_ratelimited(&s->unit->device,
+				     "Detect uncontinuity of CIP packets\n");
+		s->data_block_counter = data_block_counter;
+		return;
+	}
+
+	buffer += 2;
+
+	pcm = ACCESS_ONCE(s->pcm);
+	if (pcm) {
+		s->transfer_samples(s, pcm, buffer, data_blocks);
+		update_pcm_pointers(s, pcm, data_blocks);
+	}
+}
+
 static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
 				size_t header_length, void *header,
 				void *private_data)
@@ -571,6 +708,35 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
 	fw_iso_context_queue_flush(s->context);
 }
 
+static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+			       size_t header_length, void *header,
+			       void *private_data)
+{
+	struct amdtp_stream *s = private_data;
+	unsigned int p, packets, data_quadlets;
+	__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;
+
+		/* The number of quadlets in this packet */
+		data_quadlets =
+			(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+		/* handle each packet data */
+		handle_in_packet(s, data_quadlets, buffer);
+
+		if (queue_in_packet(s) < 0) {
+			amdtp_stream_pcm_abort(s);
+			return;
+		}
+	}
+
+	fw_iso_context_queue_flush(s->context);
+}
+
 /**
  * amdtp_stream_start - start transferring packets
  * @s: the AMDTP stream to start
@@ -595,7 +761,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 		[CIP_SFC_88200]  = {  0,   67 },
 		[CIP_SFC_176400] = {  0,   67 },
 	};
-	int err;
+	unsigned int header_size;
+	enum dma_data_direction dir;
+	fw_iso_callback_t cb;
+	int type, err;
 
 	mutex_lock(&s->mutex);
 
@@ -609,16 +778,26 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 	s->syt_offset_state = initial_state[s->sfc].syt_offset;
 	s->last_syt_offset = TICKS_PER_CYCLE;
 
+	/* initialize packet buffer */
+	if (s->direction == AMDTP_IN_STREAM) {
+		dir = DMA_FROM_DEVICE;
+		type = FW_ISO_CONTEXT_RECEIVE;
+		header_size = IN_PACKET_HEADER_SIZE;
+		cb = in_stream_callback;
+	} else {
+		dir = DMA_TO_DEVICE;
+		type = FW_ISO_CONTEXT_TRANSMIT;
+		header_size = OUT_PACKET_HEADER_SIZE;
+		cb = out_stream_callback;
+	}
 	err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
-				      amdtp_stream_get_max_payload(s),
-				      DMA_TO_DEVICE);
+				      amdtp_stream_get_max_payload(s), dir);
 	if (err < 0)
 		goto err_unlock;
 
 	s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
-					   FW_ISO_CONTEXT_TRANSMIT,
-					   channel, speed, 0,
-					   out_stream_callback, s);
+					   type, channel, speed, header_size,
+					   cb, s);
 	if (IS_ERR(s->context)) {
 		err = PTR_ERR(s->context);
 		if (err == -EBUSY)
@@ -631,13 +810,18 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 
 	s->packet_index = 0;
 	do {
-		err = queue_out_packet(s, 0, true);
+		if (s->direction == AMDTP_IN_STREAM)
+			err = queue_in_packet(s);
+		else
+			err = queue_out_packet(s, 0, true);
 		if (err < 0)
 			goto err_context;
 	} while (s->packet_index > 0);
 
+	/* NOTE: TAG1 matches CIP. This just affects in stream */
 	s->data_block_counter = 0;
-	err = fw_iso_context_start(s->context, -1, 0, 0);
+	err = fw_iso_context_start(s->context, -1, 0,
+				   FW_ISO_CONTEXT_MATCH_TAG1);
 	if (err < 0)
 		goto err_context;
 
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 06/39] firewire-lib: Add support for MIDI capture/playback
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (4 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 05/39] firewire-lib: Add support for AMDTP in-stream and PCM capture Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 07/39] firewire-lib: Give syt value as parameter to handle_out_packet() Takashi Sakamoto
                   ` (33 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

For capturing/playbacking MIDI messages, this commit adds one MIDI conformant
data channel. This channel supports 8 MPX-MIDI data streams then maximum number
of supported MIDI ports is limited by 8.

And this commit allows to set PCM format even if AMDTP stream already starts.
I suppose the case that PCM stream is going to be joined into AMDTP streams after
AMDTP stream is already started by MIDI. Each driver must count how many MIDI
substreams use AMDTP stream to stop AMDTP stream.

IEC 61883-6 refers to AMEI/MMA RP-027 for implementation of MIDI conformant
data. In the specification, 'MIDI1.0-2x/3x-SPEED' mode are described with
'negotiation procedure' and 'encapsulation details'. But there is no
specifications to define them. This module implement 'MIDI1.0-1x-SPEED' mode.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 63 ++++++++++++++++++++++++++++++++++++++++++--------
 sound/firewire/amdtp.h | 43 ++++++++++++++++++++++++++++++++++
 2 files changed, 96 insertions(+), 10 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 4ebfd67..ad3521f 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <sound/pcm.h>
+#include <sound/rawmidi.h>
 #include "amdtp.h"
 
 #define TICKS_PER_CYCLE		3072
@@ -123,9 +124,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
 		[CIP_SFC_176400] = 176400,
 		[CIP_SFC_192000] = 192000,
 	};
-	unsigned int sfc;
+	unsigned int sfc, midi_channels;
 
-	if (WARN_ON(amdtp_stream_running(s)))
+	midi_channels = DIV_ROUND_UP(midi_ports, 8);
+
+	if (WARN_ON(amdtp_stream_running(s)) ||
+	    WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
 		return;
 
 	for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
@@ -142,7 +146,7 @@ sfc_found:
 		pcm_channels *= 2;
 	}
 	s->sfc = sfc;
-	s->data_block_quadlets = pcm_channels + DIV_ROUND_UP(midi_ports, 8);
+	s->data_block_quadlets = pcm_channels + midi_channels;
 	s->pcm_channels = pcm_channels;
 	s->midi_ports = midi_ports;
 
@@ -200,7 +204,7 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
 void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
 				 snd_pcm_format_t format)
 {
-	if (WARN_ON(amdtp_stream_running(s)))
+	if (WARN_ON(amdtp_stream_pcm_running(s)))
 		return;
 
 	switch (format) {
@@ -505,11 +509,46 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
 static void amdtp_fill_midi(struct amdtp_stream *s,
 			    __be32 *buffer, unsigned int frames)
 {
-	unsigned int i;
+	unsigned int f, port;
+	u8 *b;
+
+	for (f = 0; f < frames; f++) {
+		buffer[s->pcm_channels + 1] = 0x00;
+		b = (u8 *)&buffer[s->pcm_channels + 1];
+
+		port = (s->data_block_counter + f) % 8;
+		if ((s->midi[port] == NULL) ||
+		    (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0)) {
+			b[0] = 0x80;
+			b[1] = 0x00;	/* confirm to be zero */
+		} else {
+			b[0] = 0x81;
+		}
+		buffer += s->data_block_quadlets;
+	}
+}
+
+static void amdtp_pull_midi(struct amdtp_stream *s,
+			    __be32 *buffer, unsigned int frames)
+{
+	unsigned int f, port;
+	int len;
+	u8 *b;
+
+	for (f = 0; f < frames; f++) {
+		port = (s->data_block_counter + f) % 8;
+		b = (u8 *)&buffer[s->pcm_channels + 1];
+
+		len = b[0] - 0x80;
+		if (len < 1 || 3 < len)
+			continue;
 
-	for (i = 0; i < frames; ++i)
-		buffer[s->pcm_channels + i * s->data_block_quadlets] =
-						cpu_to_be32(0x80000000);
+		if (s->midi[port] == NULL)
+			continue;
+
+		snd_rawmidi_receive(s->midi[port], b + 1, len);
+		buffer += s->data_block_quadlets;
+	}
 }
 
 static void update_pcm_pointers(struct amdtp_stream *s,
@@ -683,10 +722,14 @@ static void handle_in_packet(struct amdtp_stream *s,
 	buffer += 2;
 
 	pcm = ACCESS_ONCE(s->pcm);
-	if (pcm) {
+	if (pcm)
 		s->transfer_samples(s, pcm, buffer, data_blocks);
+
+	if (s->midi_ports)
+		amdtp_pull_midi(s, buffer, data_blocks);
+
+	if (pcm)
 		update_pcm_pointers(s, pcm, data_blocks);
-	}
 }
 
 static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 019134e..96f25f9 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -44,9 +44,21 @@ enum cip_sfc {
 #define AMDTP_OUT_PCM_FORMAT_BITS	(SNDRV_PCM_FMTBIT_S16 | \
 					 SNDRV_PCM_FMTBIT_S32)
 
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AMDTP_MAX_CHANNELS_FOR_MIDI	1
+
 struct fw_unit;
 struct fw_iso_context;
 struct snd_pcm_substream;
+struct snd_rawmidi_substream;
 
 enum amdtp_stream_direction {
 	AMDTP_OUT_STREAM = 0,
@@ -88,6 +100,8 @@ struct amdtp_stream {
 	unsigned int pcm_buffer_pointer;
 	unsigned int pcm_period_pointer;
 	bool pointer_flush;
+
+	struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
 };
 
 int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
@@ -137,6 +151,17 @@ static inline bool amdtp_streaming_error(struct amdtp_stream *s)
 }
 
 /**
+ * amdtp_stream_pcm_running - check PCM stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, PCM stream in the stream is running.
+ */
+static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
+{
+	return !IS_ERR_OR_NULL(s->pcm);
+}
+
+/**
  * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
  * @s: the AMDTP stream
  * @pcm: the PCM device to be started, or %NULL to stop the current device
@@ -151,6 +176,24 @@ static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
 	ACCESS_ONCE(s->pcm) = pcm;
 }
 
+/**
+ * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data.  This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
+					     unsigned int port,
+					     struct snd_rawmidi_substream *midi)
+{
+	if (port < s->midi_ports)
+		ACCESS_ONCE(s->midi[port]) = midi;
+}
+
 static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
 {
 	return sfc & 1;
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 07/39] firewire-lib: Give syt value as parameter to handle_out_packet()
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (5 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 06/39] firewire-lib: Add support for MIDI capture/playback Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 08/39] firewire-lib: Add support for duplex streams synchronization in blocking mode Takashi Sakamoto
                   ` (32 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

For duplex streams with synchronization, drivers should pick up
'presentation timestamp' from in-packets and use the timestamp for
out-packets. This commit is preparation for this.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 24 ++++++++++++------------
 1 file changed, 12 insertions(+), 12 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index ad3521f..56b01c8 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -256,7 +256,9 @@ static unsigned int calculate_data_blocks(struct amdtp_stream *s)
 {
 	unsigned int phase, data_blocks;
 
-	if (!cip_sfc_is_base_44100(s->sfc)) {
+	if (s->flags & CIP_BLOCKING)
+		data_blocks = s->syt_interval;
+	else if (!cip_sfc_is_base_44100(s->sfc)) {
 		/* Sample_rate / 8000 is an integer, and precomputed. */
 		data_blocks = s->data_block_state;
 	} else {
@@ -620,26 +622,22 @@ static inline int queue_in_packet(struct amdtp_stream *s)
 			    amdtp_stream_get_max_payload(s), false);
 }
 
-static void handle_out_packet(struct amdtp_stream *s, unsigned int cycle)
+static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
 {
 	__be32 *buffer;
-	unsigned int index, data_blocks, syt, payload_length;
+	unsigned int data_blocks, payload_length;
 	struct snd_pcm_substream *pcm;
 
 	if (s->packet_index < 0)
 		return;
-	index = s->packet_index;
 
 	/* this module generate empty packet for 'no data' */
-	syt = calculate_syt(s, cycle);
-	if (!(s->flags & CIP_BLOCKING))
+	if (!(s->flags & CIP_BLOCKING) || (syt != CIP_SYT_NO_INFO))
 		data_blocks = calculate_data_blocks(s);
-	else if (syt != CIP_SYT_NO_INFO)
-		data_blocks = s->syt_interval;
 	else
 		data_blocks = 0;
 
-	buffer = s->buffer.packets[index].buffer;
+	buffer = s->buffer.packets[s->packet_index].buffer;
 	buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
 				(s->data_block_quadlets << AMDTP_DBS_SHIFT) |
 				s->data_block_counter);
@@ -737,7 +735,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
 				void *private_data)
 {
 	struct amdtp_stream *s = private_data;
-	unsigned int i, packets = header_length / 4;
+	unsigned int i, syt, packets = header_length / 4;
 
 	/*
 	 * Compute the cycle of the last queued packet.
@@ -746,8 +744,10 @@ static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
 	 */
 	cycle += QUEUE_LENGTH - packets;
 
-	for (i = 0; i < packets; ++i)
-		handle_out_packet(s, ++cycle);
+	for (i = 0; i < packets; ++i) {
+		syt = calculate_syt(s, ++cycle);
+		handle_out_packet(s, syt);
+	}
 	fw_iso_context_queue_flush(s->context);
 }
 
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 08/39] firewire-lib: Add support for duplex streams synchronization in blocking mode
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (6 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 07/39] firewire-lib: Give syt value as parameter to handle_out_packet() Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 09/39] firewire-lib: Add sort function for transmitted packet Takashi Sakamoto
                   ` (31 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Generally, the devices can synchronize to handle 'presentation timestamp'
in CIP packets. This commit adds functionality to pick up this timestamp from
in-packets transmitted by the device, then use it for out packets.

In current implementation, this module generated the timestamp by itself. This
is 'SYT Match' mode. Then this drive acts as synchronization master. This
commit allows this module to act as synchronization slave.

This commit restricts this mechanism is only available in blocking mode because
handling the timestamp in non-blocking mode is more complicated than in
blocking mode.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 65 +++++++++++++++++++++++++++++++++++++++++++++-----
 sound/firewire/amdtp.h | 46 ++++++++++++++++++++++++++++++++---
 2 files changed, 102 insertions(+), 9 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 56b01c8..9415992 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -11,6 +11,7 @@
 #include <linux/firewire.h>
 #include <linux/module.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 #include <sound/pcm.h>
 #include <sound/rawmidi.h>
 #include "amdtp.h"
@@ -72,6 +73,10 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
 	tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
 	s->packet_index = 0;
 
+	init_waitqueue_head(&s->callback_wait);
+	s->callbacked = false;
+	s->sync_slave = ERR_PTR(-1);
+
 	return 0;
 }
 EXPORT_SYMBOL(amdtp_stream_init);
@@ -626,7 +631,7 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
 {
 	__be32 *buffer;
 	unsigned int data_blocks, payload_length;
-	struct snd_pcm_substream *pcm;
+	struct snd_pcm_substream *pcm = NULL;
 
 	if (s->packet_index < 0)
 		return;
@@ -756,7 +761,7 @@ 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, data_quadlets;
+	unsigned int p, packets, syt, data_quadlets;
 	__be32 *buffer, *headers = header;
 
 	/* The number of packets in buffer */
@@ -768,6 +773,15 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
 		/* The number of quadlets in this packet */
 		data_quadlets =
 			(be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+
+		/* 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 each packet data */
 		handle_in_packet(s, data_quadlets, buffer);
 
@@ -777,9 +791,48 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
 		}
 	}
 
+	/* when sync to device, flush the packets for slave stream */
+	if ((s->flags & CIP_BLOCKING) &&
+	    (s->flags & CIP_SYNC_TO_DEVICE) && s->sync_slave->callbacked)
+		fw_iso_context_queue_flush(s->sync_slave->context);
+
 	fw_iso_context_queue_flush(s->context);
 }
 
+/* processing is done by master callback */
+static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
+				  size_t header_length, void *header,
+				  void *private_data)
+{
+	return;
+}
+
+/* this is executed one time */
+static void amdtp_stream_callback(struct fw_iso_context *context, u32 cycle,
+				  size_t header_length, void *header,
+				  void *private_data)
+{
+	struct amdtp_stream *s = private_data;
+
+	/*
+	 * For in-stream, first packet has come.
+	 * For out-stream, prepared to transmit first packet
+	 */
+	s->callbacked = true;
+	wake_up(&s->callback_wait);
+
+	if (s->direction == AMDTP_IN_STREAM)
+		context->callback.sc = in_stream_callback;
+	else if ((s->flags & CIP_BLOCKING) && (s->flags & CIP_SYNC_TO_DEVICE))
+		context->callback.sc = slave_stream_callback;
+	else
+		context->callback.sc = out_stream_callback;
+
+	context->callback.sc(context, cycle, header_length, header, s);
+
+	return;
+}
+
 /**
  * amdtp_stream_start - start transferring packets
  * @s: the AMDTP stream to start
@@ -806,7 +859,6 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 	};
 	unsigned int header_size;
 	enum dma_data_direction dir;
-	fw_iso_callback_t cb;
 	int type, err;
 
 	mutex_lock(&s->mutex);
@@ -826,12 +878,10 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 		dir = DMA_FROM_DEVICE;
 		type = FW_ISO_CONTEXT_RECEIVE;
 		header_size = IN_PACKET_HEADER_SIZE;
-		cb = in_stream_callback;
 	} else {
 		dir = DMA_TO_DEVICE;
 		type = FW_ISO_CONTEXT_TRANSMIT;
 		header_size = OUT_PACKET_HEADER_SIZE;
-		cb = out_stream_callback;
 	}
 	err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
 				      amdtp_stream_get_max_payload(s), dir);
@@ -840,7 +890,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 
 	s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
 					   type, channel, speed, header_size,
-					   cb, s);
+					   amdtp_stream_callback, s);
 	if (IS_ERR(s->context)) {
 		err = PTR_ERR(s->context);
 		if (err == -EBUSY)
@@ -863,6 +913,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 
 	/* NOTE: TAG1 matches CIP. This just affects in stream */
 	s->data_block_counter = 0;
+	s->callbacked = false;
 	err = fw_iso_context_start(s->context, -1, 0,
 				   FW_ISO_CONTEXT_MATCH_TAG1);
 	if (err < 0)
@@ -935,6 +986,8 @@ void amdtp_stream_stop(struct amdtp_stream *s)
 	s->context = ERR_PTR(-1);
 	iso_packets_buffer_destroy(&s->buffer, s->unit);
 
+	s->callbacked = false;
+
 	mutex_unlock(&s->mutex);
 }
 EXPORT_SYMBOL(amdtp_stream_stop);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 96f25f9..efa2d25 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -20,11 +20,14 @@
  *	at half the actual sample rate with twice the number of channels;
  *	two samples of a channel are stored consecutively in the packet.
  *	Requires blocking mode and SYT_INTERVAL-aligned PCM buffer size.
+ * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
+ *	generated by in packets. Defaultly this driver generates timestamp.
  */
 enum cip_flags {
-	CIP_NONBLOCKING	= 0x00,
-	CIP_BLOCKING	= 0x01,
-	CIP_HI_DUALWIRE	= 0x02,
+	CIP_NONBLOCKING		= 0x00,
+	CIP_BLOCKING		= 0x01,
+	CIP_HI_DUALWIRE		= 0x02,
+	CIP_SYNC_TO_DEVICE	= 0x04
 };
 
 /**
@@ -102,6 +105,10 @@ struct amdtp_stream {
 	bool pointer_flush;
 
 	struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
+
+	bool callbacked;
+	wait_queue_head_t callback_wait;
+	struct amdtp_stream *sync_slave;
 };
 
 int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
@@ -199,4 +206,37 @@ static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
 	return sfc & 1;
 }
 
+static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
+					 struct amdtp_stream *master,
+					 struct amdtp_stream *slave)
+{
+	/* clear sync flag */
+	master->flags &= ~CIP_SYNC_TO_DEVICE;
+	slave->flags &= ~CIP_SYNC_TO_DEVICE;
+
+	if (sync_mode == CIP_SYNC_TO_DEVICE) {
+		master->flags |= CIP_SYNC_TO_DEVICE;
+		slave->flags |= CIP_SYNC_TO_DEVICE;
+		master->sync_slave = slave;
+	} else
+		master->sync_slave = ERR_PTR(-1);
+
+	slave->sync_slave = ERR_PTR(-1);
+}
+
+/**
+ * amdtp_stream_wait_callback - sleep till callbacked or timeout
+ * @s: the AMDTP stream
+ * @timeout: msec till timeout
+ *
+ * If this function return false, the AMDTP stream should be stopped.
+ */
+static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
+					      unsigned int timeout)
+{
+	return wait_event_timeout(s->callback_wait,
+				  s->callbacked == true,
+				  msecs_to_jiffies(timeout)) > 0;
+}
+
 #endif
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 09/39] firewire-lib: Add sort function for transmitted packet
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (7 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 08/39] firewire-lib: Add support for duplex streams synchronization in blocking mode Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  6:40   ` Takashi Iwai
  2014-02-28  3:27 ` [PATCH 10/39] firewire-lib: Add transfer delay to synchronized duplex streams Takashi Sakamoto
                   ` (30 subsequent siblings)
  39 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

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@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;
+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);
+}
+
 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);
+	}
+
 	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

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 10/39] firewire-lib: Add transfer delay to synchronized duplex streams
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (8 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 09/39] firewire-lib: Add sort function for transmitted packet Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 11/39] firewire-lib: Add support for channel mapping Takashi Sakamoto
                   ` (29 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Currently, in duplex streams with synchronization mode, this module just pass
'presentation timestamp' from in-packets to out-packets. This is enough to
handle actual device but logically the timestamp should include
'transfer delay' and 'processing time'. This means the timestamp in out-packet
should be at future.

To be simple, this commit add only 'transfer delay'.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 3d943d1..43daa32 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -784,6 +784,15 @@ static void packet_sort(struct sort_table *tbl, unsigned int len)
 	} while (i < len);
 }
 
+static inline void add_transfer_delay(struct amdtp_stream *s, unsigned int *syt)
+{
+	if (*syt != CIP_SYT_NO_INFO) {
+		*syt += (s->transfer_delay / TICKS_PER_CYCLE) << 12;
+		*syt += s->transfer_delay % TICKS_PER_CYCLE;
+		*syt &= CIP_SYT_MASK;
+	}
+}
+
 static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
 				size_t header_length, void *header,
 				void *private_data)
@@ -856,6 +865,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
 			    (s->flags & CIP_SYNC_TO_DEVICE) &&
 			    s->sync_slave->callbacked) {
 				syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+				add_transfer_delay(s, &syt);
 				handle_out_packet(s->sync_slave, syt);
 			}
 			handle_in_packet(s, tbl[i].payload_size / 4, buffer);
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 11/39] firewire-lib: Add support for channel mapping
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (9 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 10/39] firewire-lib: Add transfer delay to synchronized duplex streams Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 12/39] firewire-lib: Rename macros, variables and functions for CMP Takashi Sakamoto
                   ` (28 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Some devices arrange the position of PCM/MIDI data channel in AMDTP packet.
This commit allows drivers to set channel mapping.

To be simple, the mapping table is an array with fixed length. Then the number
of channels for PCM is restricted by 64 channels.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 122 +++++++++++++++++++++++++++++--------------------
 sound/firewire/amdtp.h |   8 ++++
 2 files changed, 80 insertions(+), 50 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 43daa32..eb59eb7 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -140,11 +140,12 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
 		[CIP_SFC_176400] = 176400,
 		[CIP_SFC_192000] = 192000,
 	};
-	unsigned int sfc, midi_channels;
+	unsigned int i, sfc, midi_channels;
 
 	midi_channels = DIV_ROUND_UP(midi_ports, 8);
 
-	if (WARN_ON(amdtp_stream_running(s)) ||
+	if (WARN_ON(amdtp_stream_running(s)) |
+	    WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
 	    WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
 		return;
 
@@ -159,11 +160,12 @@ sfc_found:
 	if (s->dual_wire) {
 		sfc -= 2;
 		rate /= 2;
-		pcm_channels *= 2;
+		s->pcm_channels = pcm_channels * 2;
+	} else {
+		s->pcm_channels = pcm_channels;
 	}
 	s->sfc = sfc;
-	s->data_block_quadlets = pcm_channels + midi_channels;
-	s->pcm_channels = pcm_channels;
+	s->data_block_quadlets = s->pcm_channels + midi_channels;
 	s->midi_ports = midi_ports;
 
 	s->syt_interval = amdtp_syt_intervals[sfc];
@@ -173,6 +175,11 @@ sfc_found:
 	if (s->flags & CIP_BLOCKING)
 		/* additional buffering needed to adjust for no-data packets */
 		s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+
+	/* init the position map for PCM and MIDI channels */
+	for (i = 0; i < pcm_channels; i++)
+		s->pcm_positions[i] = i;
+	s->midi_position = s->pcm_channels;
 }
 EXPORT_SYMBOL(amdtp_stream_set_parameters);
 
@@ -351,22 +358,20 @@ static void amdtp_write_s32(struct amdtp_stream *s,
 			    __be32 *buffer, unsigned int frames)
 {
 	struct snd_pcm_runtime *runtime = pcm->runtime;
-	unsigned int channels, remaining_frames, frame_step, i, c;
+	unsigned int remaining_frames, i, c;
 	const u32 *src;
 
-	channels = s->pcm_channels;
 	src = (void *)runtime->dma_area +
 			frames_to_bytes(runtime, s->pcm_buffer_pointer);
 	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-	frame_step = s->data_block_quadlets - channels;
 
 	for (i = 0; i < frames; ++i) {
-		for (c = 0; c < channels; ++c) {
-			*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+		for (c = 0; c < s->pcm_channels; ++c) {
+			buffer[s->pcm_positions[c]] =
+					cpu_to_be32((*src >> 8) | 0x40000000);
 			src++;
-			buffer++;
 		}
-		buffer += frame_step;
+		buffer += s->data_block_quadlets;
 		if (--remaining_frames == 0)
 			src = (void *)runtime->dma_area;
 	}
@@ -377,22 +382,20 @@ static void amdtp_write_s16(struct amdtp_stream *s,
 			    __be32 *buffer, unsigned int frames)
 {
 	struct snd_pcm_runtime *runtime = pcm->runtime;
-	unsigned int channels, remaining_frames, frame_step, i, c;
+	unsigned int remaining_frames, i, c;
 	const u16 *src;
 
-	channels = s->pcm_channels;
 	src = (void *)runtime->dma_area +
 			frames_to_bytes(runtime, s->pcm_buffer_pointer);
 	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-	frame_step = s->data_block_quadlets - channels;
 
 	for (i = 0; i < frames; ++i) {
-		for (c = 0; c < channels; ++c) {
-			*buffer = cpu_to_be32((*src << 8) | 0x40000000);
+		for (c = 0; c < s->pcm_channels; ++c) {
+			buffer[s->pcm_positions[c]] =
+					cpu_to_be32((*src << 8) | 0x40000000);
 			src++;
-			buffer++;
 		}
-		buffer += frame_step;
+		buffer += s->data_block_quadlets;
 		if (--remaining_frames == 0)
 			src = (void *)runtime->dma_area;
 	}
@@ -403,29 +406,29 @@ static void amdtp_write_s32_dualwire(struct amdtp_stream *s,
 				     __be32 *buffer, unsigned int frames)
 {
 	struct snd_pcm_runtime *runtime = pcm->runtime;
-	unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
+	unsigned int channels, remaining_frames, i, c;
 	const u32 *src;
 
-	channels = s->pcm_channels;
 	src = (void *)runtime->dma_area +
-			s->pcm_buffer_pointer * (runtime->frame_bits / 8);
-	frame_adjust_1 = channels - 1;
-	frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+	channels = s->pcm_channels / 2;
 
-	channels /= 2;
 	for (i = 0; i < frames; ++i) {
 		for (c = 0; c < channels; ++c) {
-			*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+			buffer[s->pcm_positions[c] * 2] =
+					cpu_to_be32((*src >> 8) | 0x40000000);
 			src++;
-			buffer += 2;
 		}
-		buffer -= frame_adjust_1;
+		buffer += 1;
 		for (c = 0; c < channels; ++c) {
-			*buffer = cpu_to_be32((*src >> 8) | 0x40000000);
+			buffer[s->pcm_positions[c] * 2] =
+					cpu_to_be32((*src >> 8) | 0x40000000);
 			src++;
-			buffer += 2;
 		}
-		buffer -= frame_adjust_2;
+		buffer += s->data_block_quadlets - 1;
+		if (--remaining_frames == 0)
+			src = (void *)runtime->dma_area;
 	}
 }
 
@@ -434,29 +437,29 @@ static void amdtp_write_s16_dualwire(struct amdtp_stream *s,
 				     __be32 *buffer, unsigned int frames)
 {
 	struct snd_pcm_runtime *runtime = pcm->runtime;
-	unsigned int channels, frame_adjust_1, frame_adjust_2, i, c;
+	unsigned int channels, remaining_frames, i, c;
 	const u16 *src;
 
-	channels = s->pcm_channels;
 	src = (void *)runtime->dma_area +
-			s->pcm_buffer_pointer * (runtime->frame_bits / 8);
-	frame_adjust_1 = channels - 1;
-	frame_adjust_2 = 1 - (s->data_block_quadlets - channels);
+			frames_to_bytes(runtime, s->pcm_buffer_pointer);
+	remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+	channels = s->pcm_channels / 2;
 
-	channels /= 2;
 	for (i = 0; i < frames; ++i) {
 		for (c = 0; c < channels; ++c) {
-			*buffer = cpu_to_be32((*src << 8) | 0x40000000);
+			buffer[s->pcm_positions[c] * 2] =
+					cpu_to_be32((*src << 8) | 0x40000000);
 			src++;
-			buffer += 2;
 		}
-		buffer -= frame_adjust_1;
+		buffer += 1;
 		for (c = 0; c < channels; ++c) {
-			*buffer = cpu_to_be32((*src << 8) | 0x40000000);
+			buffer[s->pcm_positions[c] * 2] =
+					cpu_to_be32((*src << 8) | 0x40000000);
 			src++;
-			buffer += 2;
 		}
-		buffer -= frame_adjust_2;
+		buffer += s->data_block_quadlets - 1;
+		if (--remaining_frames == 0)
+			src = (void *)runtime->dma_area;
 	}
 }
 
@@ -474,7 +477,7 @@ static void amdtp_read_s32(struct amdtp_stream *s,
 
 	for (i = 0; i < frames; ++i) {
 		for (c = 0; c < s->pcm_channels; ++c) {
-			*dst = be32_to_cpu(buffer[c]) << 8;
+			*dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
 			dst++;
 		}
 		buffer += s->data_block_quadlets;
@@ -498,12 +501,14 @@ static void amdtp_read_s32_dualwire(struct amdtp_stream *s,
 
 	for (i = 0; i < frames; ++i) {
 		for (c = 0; c < channels; ++c) {
-			*dst = be32_to_cpu(buffer[c * 2]) << 8;
+			*dst =
+			     be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8;
 			dst++;
 		}
 		buffer += 1;
 		for (c = 0; c < channels; ++c) {
-			*dst = be32_to_cpu(buffer[c * 2]) << 8;
+			*dst =
+			     be32_to_cpu(buffer[s->pcm_positions[c] * 2]) << 8;
 			dst++;
 		}
 		buffer += s->data_block_quadlets - 1;
@@ -519,11 +524,26 @@ static void amdtp_fill_pcm_silence(struct amdtp_stream *s,
 
 	for (i = 0; i < frames; ++i) {
 		for (c = 0; c < s->pcm_channels; ++c)
-			buffer[c] = cpu_to_be32(0x40000000);
+			buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
 		buffer += s->data_block_quadlets;
 	}
 }
 
+static void amdtp_fill_pcm_silence_dualwire(struct amdtp_stream *s,
+					   __be32 *buffer, unsigned int frames)
+{
+	unsigned int i, c, channels;
+
+	channels = s->pcm_channels / 2;
+	for (i = 0; i < frames; ++i) {
+		for (c = 0; c < channels; ++c) {
+			buffer[s->pcm_positions[c] * 2] =
+			buffer[s->pcm_positions[c] * 2 + 1] =
+						cpu_to_be32(0x40000000);
+		}
+		buffer += s->data_block_quadlets;
+	}
+}
 static void amdtp_fill_midi(struct amdtp_stream *s,
 			    __be32 *buffer, unsigned int frames)
 {
@@ -531,8 +551,8 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
 	u8 *b;
 
 	for (f = 0; f < frames; f++) {
-		buffer[s->pcm_channels + 1] = 0x00;
-		b = (u8 *)&buffer[s->pcm_channels + 1];
+		buffer[s->midi_position] = 0x00;
+		b = (u8 *)&buffer[s->midi_position];
 
 		port = (s->data_block_counter + f) % 8;
 		if ((s->midi[port] == NULL) ||
@@ -555,7 +575,7 @@ static void amdtp_pull_midi(struct amdtp_stream *s,
 
 	for (f = 0; f < frames; f++) {
 		port = (s->data_block_counter + f) % 8;
-		b = (u8 *)&buffer[s->pcm_channels + 1];
+		b = (u8 *)&buffer[s->midi_position];
 
 		len = b[0] - 0x80;
 		if (len < 1 || 3 < len)
@@ -664,6 +684,8 @@ static void handle_out_packet(struct amdtp_stream *s, unsigned int syt)
 	pcm = ACCESS_ONCE(s->pcm);
 	if (pcm)
 		s->transfer_samples(s, pcm, buffer, data_blocks);
+	else if (s->dual_wire)
+		amdtp_fill_pcm_silence_dualwire(s, buffer, data_blocks);
 	else
 		amdtp_fill_pcm_silence(s, buffer, data_blocks);
 	if (s->midi_ports)
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index d502646..2fb5175 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -49,6 +49,12 @@ enum cip_sfc {
 
 
 /*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convinience.
+ */
+#define AMDTP_MAX_CHANNELS_FOR_PCM	64
+
+/*
  * AMDTP packet can include channels for MIDI conformant data.
  * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
  * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
@@ -83,6 +89,8 @@ struct amdtp_stream {
 	void (*transfer_samples)(struct amdtp_stream *s,
 				 struct snd_pcm_substream *pcm,
 				 __be32 *buffer, unsigned int frames);
+	u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
+	u8 midi_position;
 
 	unsigned int syt_interval;
 	unsigned int transfer_delay;
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 12/39] firewire-lib: Rename macros, variables and functions for CMP
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (10 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 11/39] firewire-lib: Add support for channel mapping Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 13/39] firewire-lib: Add 'direction' member to 'cmp_connection' structure Takashi Sakamoto
                   ` (27 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Referring to IEC 61883-1, oMPR and iMPR, oPCR and iPCR have some fields with
the same role in the same position. This patch renames some macros, variables
and function arguments with "i" in its prefix to reuse them between oMPR and
iMPR, oPCR and iPCR.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/cmp.c | 91 ++++++++++++++++++++++++++--------------------------
 sound/firewire/cmp.h |  6 ++--
 2 files changed, 49 insertions(+), 48 deletions(-)

diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index efdbf58..17ad4f2 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -14,18 +14,20 @@
 #include "iso-resources.h"
 #include "cmp.h"
 
-#define IMPR_SPEED_MASK		0xc0000000
-#define IMPR_SPEED_SHIFT	30
-#define IMPR_XSPEED_MASK	0x00000060
-#define IMPR_XSPEED_SHIFT	5
-#define IMPR_PLUGS_MASK		0x0000001f
-
-#define IPCR_ONLINE		0x80000000
-#define IPCR_BCAST_CONN		0x40000000
-#define IPCR_P2P_CONN_MASK	0x3f000000
-#define IPCR_P2P_CONN_SHIFT	24
-#define IPCR_CHANNEL_MASK	0x003f0000
-#define IPCR_CHANNEL_SHIFT	16
+/* MPR common fields */
+#define MPR_SPEED_MASK		0xc0000000
+#define MPR_SPEED_SHIFT		30
+#define MPR_XSPEED_MASK		0x00000060
+#define MPR_XSPEED_SHIFT	5
+#define MPR_PLUGS_MASK		0x0000001f
+
+/* PCR common fields */
+#define PCR_ONLINE		0x80000000
+#define PCR_BCAST_CONN		0x40000000
+#define PCR_P2P_CONN_MASK	0x3f000000
+#define PCR_P2P_CONN_SHIFT	24
+#define PCR_CHANNEL_MASK	0x003f0000
+#define PCR_CHANNEL_SHIFT	16
 
 enum bus_reset_handling {
 	ABORT_ON_BUS_RESET,
@@ -88,24 +90,24 @@ static int pcr_modify(struct cmp_connection *c,
  * cmp_connection_init - initializes a connection manager
  * @c: the connection manager to initialize
  * @unit: a unit of the target device
- * @ipcr_index: the index of the iPCR on the target device
+ * @pcr_index: the index of the iPCR/oPCR on the target device
  */
 int cmp_connection_init(struct cmp_connection *c,
 			struct fw_unit *unit,
-			unsigned int ipcr_index)
+			unsigned int pcr_index)
 {
-	__be32 impr_be;
-	u32 impr;
+	__be32 mpr_be;
+	u32 mpr;
 	int err;
 
 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
 				 CSR_REGISTER_BASE + CSR_IMPR,
-				 &impr_be, 4, 0);
+				 &mpr_be, 4, 0);
 	if (err < 0)
 		return err;
-	impr = be32_to_cpu(impr_be);
+	mpr = be32_to_cpu(mpr_be);
 
-	if (ipcr_index >= (impr & IMPR_PLUGS_MASK))
+	if (pcr_index >= (mpr & MPR_PLUGS_MASK))
 		return -EINVAL;
 
 	err = fw_iso_resources_init(&c->resources, unit);
@@ -115,10 +117,10 @@ int cmp_connection_init(struct cmp_connection *c,
 	c->connected = false;
 	mutex_init(&c->mutex);
 	c->last_pcr_value = cpu_to_be32(0x80000000);
-	c->pcr_index = ipcr_index;
-	c->max_speed = (impr & IMPR_SPEED_MASK) >> IMPR_SPEED_SHIFT;
+	c->pcr_index = pcr_index;
+	c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
 	if (c->max_speed == SCODE_BETA)
-		c->max_speed += (impr & IMPR_XSPEED_MASK) >> IMPR_XSPEED_SHIFT;
+		c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
 
 	return 0;
 }
@@ -139,23 +141,23 @@ EXPORT_SYMBOL(cmp_connection_destroy);
 
 static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 {
-	ipcr &= ~cpu_to_be32(IPCR_BCAST_CONN |
-			     IPCR_P2P_CONN_MASK |
-			     IPCR_CHANNEL_MASK);
-	ipcr |= cpu_to_be32(1 << IPCR_P2P_CONN_SHIFT);
-	ipcr |= cpu_to_be32(c->resources.channel << IPCR_CHANNEL_SHIFT);
+	ipcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+			     PCR_P2P_CONN_MASK |
+			     PCR_CHANNEL_MASK);
+	ipcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+	ipcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
 
 	return ipcr;
 }
 
-static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
+static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
 {
-	if (ipcr & cpu_to_be32(IPCR_BCAST_CONN |
-			       IPCR_P2P_CONN_MASK)) {
+	if (pcr & cpu_to_be32(PCR_BCAST_CONN |
+			       PCR_P2P_CONN_MASK)) {
 		cmp_error(c, "plug is already in use\n");
 		return -EBUSY;
 	}
-	if (!(ipcr & cpu_to_be32(IPCR_ONLINE))) {
+	if (!(pcr & cpu_to_be32(PCR_ONLINE))) {
 		cmp_error(c, "plug is not on-line\n");
 		return -ECONNREFUSED;
 	}
@@ -170,9 +172,9 @@ static int ipcr_set_check(struct cmp_connection *c, __be32 ipcr)
  *
  * This function establishes a point-to-point connection from the local
  * computer to the target by allocating isochronous resources (channel and
- * bandwidth) and setting the target's input plug control register.  When this
- * function succeeds, the caller is responsible for starting transmitting
- * packets.
+ * bandwidth) and setting the target's input/output plug control register.
+ * When this function succeeds, the caller is responsible for starting
+ * transmitting packets.
  */
 int cmp_connection_establish(struct cmp_connection *c,
 			     unsigned int max_payload_bytes)
@@ -193,7 +195,7 @@ retry_after_bus_reset:
 	if (err < 0)
 		goto err_mutex;
 
-	err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
+	err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 			 ABORT_ON_BUS_RESET);
 	if (err == -EAGAIN) {
 		fw_iso_resources_free(&c->resources);
@@ -221,8 +223,8 @@ EXPORT_SYMBOL(cmp_connection_establish);
  * cmp_connection_update - update the connection after a bus reset
  * @c: the connection manager
  *
- * This function must be called from the driver's .update handler to reestablish
- * any connection that might have been active.
+ * This function must be called from the driver's .update handler to
+ * reestablish any connection that might have been active.
  *
  * Returns zero on success, or a negative error code.  On an error, the
  * connection is broken and the caller must stop transmitting iso packets.
@@ -242,7 +244,7 @@ int cmp_connection_update(struct cmp_connection *c)
 	if (err < 0)
 		goto err_unconnect;
 
-	err = pcr_modify(c, ipcr_set_modify, ipcr_set_check,
+	err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
 			 SUCCEED_ON_BUS_RESET);
 	if (err < 0)
 		goto err_resources;
@@ -261,19 +263,18 @@ err_unconnect:
 }
 EXPORT_SYMBOL(cmp_connection_update);
 
-
-static __be32 ipcr_break_modify(struct cmp_connection *c, __be32 ipcr)
+static __be32 pcr_break_modify(struct cmp_connection *c, __be32 pcr)
 {
-	return ipcr & ~cpu_to_be32(IPCR_BCAST_CONN | IPCR_P2P_CONN_MASK);
+	return pcr & ~cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK);
 }
 
 /**
  * cmp_connection_break - break the connection to the target
  * @c: the connection manager
  *
- * This function deactives the connection in the target's input plug control
- * register, and frees the isochronous resources of the connection.  Before
- * calling this function, the caller should cease transmitting packets.
+ * This function deactives the connection in the target's input/output plug
+ * control register, and frees the isochronous resources of the connection.
+ * Before calling this function, the caller should cease transmitting packets.
  */
 void cmp_connection_break(struct cmp_connection *c)
 {
@@ -286,7 +287,7 @@ void cmp_connection_break(struct cmp_connection *c)
 		return;
 	}
 
-	err = pcr_modify(c, ipcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
+	err = pcr_modify(c, pcr_break_modify, NULL, SUCCEED_ON_BUS_RESET);
 	if (err < 0)
 		cmp_error(c, "plug is still connected\n");
 
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
index f47de08..2320cd4 100644
--- a/sound/firewire/cmp.h
+++ b/sound/firewire/cmp.h
@@ -11,8 +11,8 @@ struct fw_unit;
  * struct cmp_connection - manages an isochronous connection to a device
  * @speed: the connection's actual speed
  *
- * This structure manages (using CMP) an isochronous stream from the local
- * computer to a device's input plug (iPCR).
+ * This structure manages (using CMP) an isochronous stream between the local
+ * computer and a device's input plug (iPCR) and output plug (oPCR).
  *
  * There is no corresponding oPCR created on the local computer, so it is not
  * possible to overlay connections on top of this one.
@@ -30,7 +30,7 @@ struct cmp_connection {
 
 int cmp_connection_init(struct cmp_connection *connection,
 			struct fw_unit *unit,
-			unsigned int ipcr_index);
+			unsigned int pcr_index);
 void cmp_connection_destroy(struct cmp_connection *connection);
 
 int cmp_connection_establish(struct cmp_connection *connection,
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 13/39] firewire-lib: Add 'direction' member to 'cmp_connection' structure
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (11 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 12/39] firewire-lib: Rename macros, variables and functions for CMP Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 14/39] firewire-lib: Add handling output connection by CMP Takashi Sakamoto
                   ` (26 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This patch adds 'direction' member to 'cmp_connection' structure to indicate
the direction of connection. This patch also adds 'direction' argument to
cmp_connection_init() function to determine the direction.

The cmp_connection_init() function is exported and used in snd-firewire-speakers
so this patch also affect it.

This patch just add them. Actual implementation will be done by followed
patches.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/cmp.c      | 3 ++-
 sound/firewire/cmp.h      | 7 +++++++
 sound/firewire/speakers.c | 2 +-
 3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index 17ad4f2..c7f7a31 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -94,6 +94,7 @@ static int pcr_modify(struct cmp_connection *c,
  */
 int cmp_connection_init(struct cmp_connection *c,
 			struct fw_unit *unit,
+			enum cmp_direction direction,
 			unsigned int pcr_index)
 {
 	__be32 mpr_be;
@@ -153,7 +154,7 @@ static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
 {
 	if (pcr & cpu_to_be32(PCR_BCAST_CONN |
-			       PCR_P2P_CONN_MASK)) {
+			      PCR_P2P_CONN_MASK)) {
 		cmp_error(c, "plug is already in use\n");
 		return -EBUSY;
 	}
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
index 2320cd4..9b58448 100644
--- a/sound/firewire/cmp.h
+++ b/sound/firewire/cmp.h
@@ -7,6 +7,11 @@
 
 struct fw_unit;
 
+enum cmp_direction {
+	CMP_INPUT = 0,
+	CMP_OUTPUT,
+};
+
 /**
  * struct cmp_connection - manages an isochronous connection to a device
  * @speed: the connection's actual speed
@@ -26,10 +31,12 @@ struct cmp_connection {
 	__be32 last_pcr_value;
 	unsigned int pcr_index;
 	unsigned int max_speed;
+	enum cmp_direction direction;
 };
 
 int cmp_connection_init(struct cmp_connection *connection,
 			struct fw_unit *unit,
+			enum cmp_direction direction,
 			unsigned int pcr_index);
 void cmp_connection_destroy(struct cmp_connection *connection);
 
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index c07e7cd..4a9b48b 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -679,7 +679,7 @@ static int fwspk_probe(struct fw_unit *unit,
 	fwspk->unit = fw_unit_get(unit);
 	fwspk->device_info = (const struct device_info *)id->driver_data;
 
-	err = cmp_connection_init(&fwspk->connection, unit, 0);
+	err = cmp_connection_init(&fwspk->connection, unit, CMP_INPUT, 0);
 	if (err < 0)
 		goto err_unit;
 
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 14/39] firewire-lib: Add handling output connection by CMP
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (12 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 13/39] firewire-lib: Add 'direction' member to 'cmp_connection' structure Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 15/39] firewire-lib: Add a new function to check others' connection Takashi Sakamoto
                   ` (25 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This patch adds some macros, codes with condition of direction and new functions
to handle output connection. Once cmp_connection_init() is executed with its
direction, CMP input and output connection can be handled by the same way.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/cmp.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 93 insertions(+), 9 deletions(-)

diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index c7f7a31..60d2e17 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -29,6 +29,14 @@
 #define PCR_CHANNEL_MASK	0x003f0000
 #define PCR_CHANNEL_SHIFT	16
 
+/* oPCR specific fields */
+#define OPCR_XSPEED_MASK	0x00C00000
+#define OPCR_XSPEED_SHIFT	22
+#define OPCR_SPEED_MASK		0x0000C000
+#define OPCR_SPEED_SHIFT	14
+#define OPCR_OVERHEAD_ID_MASK	0x00003C00
+#define OPCR_OVERHEAD_ID_SHIFT	10
+
 enum bus_reset_handling {
 	ABORT_ON_BUS_RESET,
 	SUCCEED_ON_BUS_RESET,
@@ -41,10 +49,30 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...)
 
 	va_start(va, fmt);
 	dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
-		'i', c->pcr_index, &(struct va_format){ fmt, &va });
+		(c->direction == CMP_INPUT) ? 'i' : 'o',
+		c->pcr_index, &(struct va_format){ fmt, &va });
 	va_end(va);
 }
 
+static unsigned long long get_offset(struct cmp_connection *c, bool master)
+{
+	unsigned long long offset = CSR_REGISTER_BASE;
+
+	if (!master) {
+		if (c->direction == CMP_INPUT)
+			offset += CSR_IPCR(c->pcr_index);
+		else
+			offset += CSR_OPCR(c->pcr_index);
+	} else {
+		if (c->direction == CMP_INPUT)
+			offset += CSR_IMPR;
+		else
+			offset += CSR_OMPR;
+	}
+
+	return offset;
+}
+
 static int pcr_modify(struct cmp_connection *c,
 		      __be32 (*modify)(struct cmp_connection *c, __be32 old),
 		      int (*check)(struct cmp_connection *c, __be32 pcr),
@@ -60,8 +88,7 @@ static int pcr_modify(struct cmp_connection *c,
 
 		err = snd_fw_transaction(
 				c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
-				CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
-				buffer, 8,
+				get_offset(c, false), buffer, 8,
 				FW_FIXED_GENERATION | c->resources.generation);
 
 		if (err < 0) {
@@ -102,8 +129,7 @@ int cmp_connection_init(struct cmp_connection *c,
 	int err;
 
 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
-				 CSR_REGISTER_BASE + CSR_IMPR,
-				 &mpr_be, 4, 0);
+				 get_offset(c, true), &mpr_be, 4, 0);
 	if (err < 0)
 		return err;
 	mpr = be32_to_cpu(mpr_be);
@@ -122,6 +148,7 @@ int cmp_connection_init(struct cmp_connection *c,
 	c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
 	if (c->max_speed == SCODE_BETA)
 		c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
+	c->direction = direction;
 
 	return 0;
 }
@@ -151,6 +178,53 @@ static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 	return ipcr;
 }
 
+static int get_overhead_id(struct cmp_connection *c)
+{
+	int id;
+
+	/*
+	 * apply "oPCR overhead ID encoding"
+	 * the encoding table can convert up to 512.
+	 * here the value over 512 is converted as the same way as 512.
+	 */
+	for (id = 1; id < 16; id++) {
+		if (c->resources.bandwidth_overhead < (id << 5))
+			break;
+	}
+	if (id == 16)
+		id = 0;
+
+	return id;
+}
+
+static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
+{
+	unsigned int spd, xspd;
+
+	/* generate speed and extended speed field value */
+	if (c->speed > SCODE_400) {
+		spd  = SCODE_800;
+		xspd = c->speed - SCODE_800;
+	} else {
+		spd = c->speed;
+		xspd = 0;
+	}
+
+	opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+			     PCR_P2P_CONN_MASK |
+			     OPCR_XSPEED_MASK |
+			     PCR_CHANNEL_MASK |
+			     OPCR_SPEED_MASK |
+			     OPCR_OVERHEAD_ID_MASK);
+	opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+	opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
+	opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+	opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
+	opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
+
+	return opcr;
+}
+
 static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
 {
 	if (pcr & cpu_to_be32(PCR_BCAST_CONN |
@@ -196,8 +270,13 @@ retry_after_bus_reset:
 	if (err < 0)
 		goto err_mutex;
 
-	err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
-			 ABORT_ON_BUS_RESET);
+	if (c->direction == CMP_OUTPUT)
+		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+				 ABORT_ON_BUS_RESET);
+	else
+		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+				 ABORT_ON_BUS_RESET);
+
 	if (err == -EAGAIN) {
 		fw_iso_resources_free(&c->resources);
 		goto retry_after_bus_reset;
@@ -245,8 +324,13 @@ int cmp_connection_update(struct cmp_connection *c)
 	if (err < 0)
 		goto err_unconnect;
 
-	err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
-			 SUCCEED_ON_BUS_RESET);
+	if (c->direction == CMP_OUTPUT)
+		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+				 SUCCEED_ON_BUS_RESET);
+	else
+		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+				 SUCCEED_ON_BUS_RESET);
+
 	if (err < 0)
 		goto err_resources;
 
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 15/39] firewire-lib: Add a new function to check others' connection
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (13 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 14/39] firewire-lib: Add handling output connection by CMP Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 16/39] firewire-lib: Add some AV/C general commands Takashi Sakamoto
                   ` (24 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Plug Control Registers have two fields related to the number of established
connections, one is 'Broadcast connection counter' and another is
'Point-to-point connection counter'. The driver can know there are established
connections or not to check these fields.

This is considering for JACK/FFADO streaming. Currently, when JACK/FFADO starts
its streaming to the device, cmp_connection_establish() is failed expectedly.
This seems to be enough but there are some devices which needs to change
samplling frequency before trying to establish connections. For such devices,
this functionality is needed.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/cmp.c | 18 ++++++++++++++++++
 sound/firewire/cmp.h |  1 +
 2 files changed, 19 insertions(+)

diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index 60d2e17..a8199fd 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -155,6 +155,24 @@ int cmp_connection_init(struct cmp_connection *c,
 EXPORT_SYMBOL(cmp_connection_init);
 
 /**
+ * cmp_connection_check_used - check connection is already esablished or not
+ * @c: the connection manager to be checked
+ */
+int cmp_connection_check_used(struct cmp_connection *c, bool *used)
+{
+	__be32 pcr;
+	int err;
+
+	err = snd_fw_transaction(
+			c->resources.unit, TCODE_READ_QUADLET_REQUEST,
+			get_offset(c, false), &pcr, 4, 0);
+	if (err >= 0)
+		*used = (pcr & cpu_to_be32(PCR_BCAST_CONN | PCR_P2P_CONN_MASK));
+	return err;
+}
+EXPORT_SYMBOL(cmp_connection_check_used);
+
+/**
  * cmp_connection_destroy - free connection manager resources
  * @c: the connection manager
  */
diff --git a/sound/firewire/cmp.h b/sound/firewire/cmp.h
index 9b58448..ebcb484 100644
--- a/sound/firewire/cmp.h
+++ b/sound/firewire/cmp.h
@@ -38,6 +38,7 @@ int cmp_connection_init(struct cmp_connection *connection,
 			struct fw_unit *unit,
 			enum cmp_direction direction,
 			unsigned int pcr_index);
+int cmp_connection_check_used(struct cmp_connection *connection, bool *used);
 void cmp_connection_destroy(struct cmp_connection *connection);
 
 int cmp_connection_establish(struct cmp_connection *connection,
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 16/39] firewire-lib: Add some AV/C general commands
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (14 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 15/39] firewire-lib: Add a new function to check others' connection Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 17/39] firewire-lib: Add quirks for Fireworks Takashi Sakamoto
                   ` (23 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds three commands, which may be used by some firewire device
drivers. These commands are defined in 'AV/C Digital Interface Command Set
General Specification Version 4.2 (2004006, 1394TA)'.

1. PLUG INFO command (10.1)
2. INPUT PLUG SIGNAL FORMAT command (10.10)
3. OUTPUT PLUG SIGNAL FORMAT command (10.11)

By the command 1, the drivers can get the number of plugs for AV/C unit or
subunit.
By the command 2 and 3, the drivers can get/set sampling frequency.

The 'firewire-speakers' already uses INPUT PLUG SIGNAL FORMAT command to set
sampling rate. So this commit also affects the driver.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c    |  24 +++----
 sound/firewire/amdtp.h    |   1 +
 sound/firewire/fcp.c      | 155 ++++++++++++++++++++++++++++++++++++++++++++++
 sound/firewire/fcp.h      |  21 +++++++
 sound/firewire/speakers.c |  44 +++----------
 5 files changed, 197 insertions(+), 48 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index eb59eb7..9b45382 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -115,6 +115,17 @@ const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
 };
 EXPORT_SYMBOL(amdtp_syt_intervals);
 
+const unsigned int amdtp_rate_table[] = {
+	[CIP_SFC_32000]  =  32000,
+	[CIP_SFC_44100]  =  44100,
+	[CIP_SFC_48000]  =  48000,
+	[CIP_SFC_88200]  =  88200,
+	[CIP_SFC_96000]  =  96000,
+	[CIP_SFC_176400] = 176400,
+	[CIP_SFC_192000] = 192000,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
 /**
  * amdtp_stream_set_parameters - set stream parameters
  * @s: the AMDTP stream to configure
@@ -131,15 +142,6 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
 				 unsigned int pcm_channels,
 				 unsigned int midi_ports)
 {
-	static const unsigned int rates[] = {
-		[CIP_SFC_32000]  =  32000,
-		[CIP_SFC_44100]  =  44100,
-		[CIP_SFC_48000]  =  48000,
-		[CIP_SFC_88200]  =  88200,
-		[CIP_SFC_96000]  =  96000,
-		[CIP_SFC_176400] = 176400,
-		[CIP_SFC_192000] = 192000,
-	};
 	unsigned int i, sfc, midi_channels;
 
 	midi_channels = DIV_ROUND_UP(midi_ports, 8);
@@ -149,8 +151,8 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
 	    WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
 		return;
 
-	for (sfc = 0; sfc < CIP_SFC_COUNT; ++sfc)
-		if (rates[sfc] == rate)
+	for (sfc = 0; sfc < sizeof(amdtp_rate_table); ++sfc)
+		if (amdtp_rate_table[sfc] == rate)
 			goto sfc_found;
 	WARN_ON(1);
 	return;
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 2fb5175..861f509 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -145,6 +145,7 @@ unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
 void amdtp_stream_pcm_abort(struct amdtp_stream *s);
 
 extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
 
 /**
  * amdtp_stream_running - check stream is running or not
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index 860c080..e903ddb 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -10,12 +10,14 @@
 #include <linux/firewire-constants.h>
 #include <linux/list.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
 #include <linux/wait.h>
 #include <linux/delay.h>
 #include "fcp.h"
 #include "lib.h"
+#include "amdtp.h"
 
 #define CTS_AVC 0x00
 
@@ -23,6 +25,159 @@
 #define ERROR_DELAY_MS	5
 #define FCP_TIMEOUT_MS	125
 
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+			    enum avc_general_plug_dir dir,
+			    unsigned short pid)
+{
+	unsigned int sfc;
+	u8 *buf;
+	bool flag;
+	int err;
+
+	flag = false;
+	for (sfc = 0; sfc < CIP_SFC_COUNT; sfc++) {
+		if (amdtp_rate_table[sfc] == rate) {
+			flag = true;
+			break;
+		}
+	}
+	if (!flag)
+		return -EINVAL;
+
+	buf = kzalloc(8, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0] = 0x00;		/* AV/C CONTROL */
+	buf[1] = 0xff;		/* UNIT */
+	if (dir == AVC_GENERAL_PLUG_DIR_IN)
+		buf[2] = 0x19;	/* INPUT PLUG SIGNAL FORMAT */
+	else
+		buf[2] = 0x18;	/* OUTPUT PLUG SIGNAL FORMAT */
+	buf[3] = 0xff & pid;	/* plug id */
+	buf[4] = 0x90;		/* EOH_1, Form_1, FMT. AM824 */
+	buf[5] = 0x07 & sfc;	/* FDF-hi. AM824, frequency */
+	buf[6] = 0xff;		/* FDF-mid. AM824, SYT hi (not used)*/
+	buf[7] = 0xff;		/* FDF-low. AM824, SYT lo (not used) */
+
+	/* do transaction and check buf[1-5] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
+	if (err < 0)
+		goto end;
+
+	/* check length */
+	if (err != 8) {
+		dev_err(&unit->device, "failed to set sample rate\n");
+		err = -EIO;
+		goto end;
+	}
+
+	/* return response code */
+	err = buf[0];
+end:
+	kfree(buf);
+	return err;
+}
+EXPORT_SYMBOL(avc_general_set_sig_fmt);
+
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+			    enum avc_general_plug_dir dir,
+			    unsigned short pid)
+{
+	unsigned int sfc;
+	u8 *buf;
+	int err;
+
+	buf = kzalloc(8, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0] = 0x01;		/* AV/C STATUS */
+	buf[1] = 0xff;		/* Unit */
+	if (dir == AVC_GENERAL_PLUG_DIR_IN)
+		buf[2] = 0x19;	/* INPUT PLUG SIGNAL FORMAT */
+	else
+		buf[2] = 0x18;	/* OUTPUT PLUG SIGNAL FORMAT */
+	buf[3] = 0xff & pid;	/* plug id */
+	buf[4] = 0x90;		/* EOH_1, Form_1, FMT. AM824 */
+	buf[5] = 0xff;		/* FDF-hi. AM824, frequency */
+	buf[6] = 0xff;		/* FDF-mid. AM824, SYT hi (not used) */
+	buf[7] = 0xff;		/* FDF-low. AM824, SYT lo (not used) */
+
+	/* do transaction and check buf[1-4] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 8, buf, 8,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4));
+	if (err < 0)
+		goto end;
+
+	/* check length */
+	if (err != 8) {
+		dev_err(&unit->device, "failed to get sample rate\n");
+		err = -EIO;
+		goto end;
+	}
+
+	/* check sfc field and pick up rate */
+	sfc = 0x07 & buf[5];
+	if (sfc >= CIP_SFC_COUNT) {
+		err = -EINVAL;
+		goto end;
+	}
+	*rate = amdtp_rate_table[sfc];
+
+	/* return response code */
+	err = buf[0];
+end:
+	kfree(buf);
+	return err;
+}
+EXPORT_SYMBOL(avc_general_get_sig_fmt);
+
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+			      unsigned int subunit_id, unsigned int subfunction,
+			      u8 info[AVC_PLUG_INFO_BUF_COUNT])
+{
+	u8 *buf;
+	int err;
+
+	/* extended subunit in spec.4.2 is not supported */
+	if ((subunit_type == 0x1E) || (subunit_id == 5))
+		return -EINVAL;
+
+	buf = kzalloc(8, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0] = 0x01;	/* AV/C STATUS */
+	/* UNIT or Subunit, Functionblock */
+	buf[1] = ((subunit_type & 0x1f) << 3) | (subunit_id & 0x7);
+	buf[2] = 0x02;	/* PLUG INFO */
+	buf[3] = 0xff & subfunction;
+
+	err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
+	if (err < 0)
+		goto end;
+
+	/* check length */
+	if (err != 8) {
+		err = -EIO;
+		goto end;
+	}
+
+	info[0] = buf[4];
+	info[1] = buf[5];
+	info[2] = buf[6];
+	info[3] = buf[7];
+
+	/* return response code */
+	err = buf[0];
+end:
+	kfree(buf);
+	return err;
+}
+EXPORT_SYMBOL(avc_general_get_plug_info);
+
 static DEFINE_SPINLOCK(transactions_lock);
 static LIST_HEAD(transactions);
 
diff --git a/sound/firewire/fcp.h b/sound/firewire/fcp.h
index 8659568..cb62eb6 100644
--- a/sound/firewire/fcp.h
+++ b/sound/firewire/fcp.h
@@ -1,8 +1,29 @@
 #ifndef SOUND_FIREWIRE_FCP_H_INCLUDED
 #define SOUND_FIREWIRE_FCP_H_INCLUDED
 
+#define	AVC_PLUG_INFO_BUF_COUNT	4
+
 struct fw_unit;
 
+/*
+ * AV/C Digital Interface Command Set General Specification 4.2
+ * (Sep 2004, 1394TA)
+ */
+enum avc_general_plug_dir {
+	AVC_GENERAL_PLUG_DIR_IN		= 0,
+	AVC_GENERAL_PLUG_DIR_OUT	= 1,
+	AVC_GENERAL_PLUG_DIR_COUNT
+};
+int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
+			    enum avc_general_plug_dir dir,
+			    unsigned short plug);
+int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
+			    enum avc_general_plug_dir dir,
+			    unsigned short plug);
+int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
+			      unsigned int subunit_id, unsigned int subfunction,
+			      u8 info[AVC_PLUG_INFO_BUF_COUNT]);
+
 int fcp_avc_transaction(struct fw_unit *unit,
 			const void *command, unsigned int command_size,
 			void *response, unsigned int response_size,
diff --git a/sound/firewire/speakers.c b/sound/firewire/speakers.c
index 4a9b48b..152b159 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -193,42 +193,6 @@ static void fwspk_stop_stream(struct fwspk *fwspk)
 	}
 }
 
-static int fwspk_set_rate(struct fwspk *fwspk, unsigned int sfc)
-{
-	u8 *buf;
-	int err;
-
-	buf = kmalloc(8, GFP_KERNEL);
-	if (!buf)
-		return -ENOMEM;
-
-	buf[0] = 0x00;		/* AV/C, CONTROL */
-	buf[1] = 0xff;		/* unit */
-	buf[2] = 0x19;		/* INPUT PLUG SIGNAL FORMAT */
-	buf[3] = 0x00;		/* plug 0 */
-	buf[4] = 0x90;		/* format: audio */
-	buf[5] = 0x00 | sfc;	/* AM824, frequency */
-	buf[6] = 0xff;		/* SYT (not used) */
-	buf[7] = 0xff;
-
-	err = fcp_avc_transaction(fwspk->unit, buf, 8, buf, 8,
-				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
-	if (err < 0)
-		goto error;
-	if (err < 6 || buf[0] != 0x09 /* ACCEPTED */) {
-		dev_err(&fwspk->unit->device, "failed to set sample rate\n");
-		err = -EIO;
-		goto error;
-	}
-
-	err = 0;
-
-error:
-	kfree(buf);
-
-	return err;
-}
-
 static int fwspk_hw_params(struct snd_pcm_substream *substream,
 			   struct snd_pcm_hw_params *hw_params)
 {
@@ -252,9 +216,15 @@ static int fwspk_hw_params(struct snd_pcm_substream *substream,
 	amdtp_stream_set_pcm_format(&fwspk->stream,
 				    params_format(hw_params));
 
-	err = fwspk_set_rate(fwspk, fwspk->stream.sfc);
+	err = avc_general_set_sig_fmt(fwspk->unit, params_rate(hw_params),
+				      AVC_GENERAL_PLUG_DIR_IN, 0);
 	if (err < 0)
 		goto err_buffer;
+	if (err != 0x09 /* ACCEPTED */) {
+		dev_err(&fwspk->unit->device, "failed to set sample rate\n");
+		err = -EIO;
+		goto error;
+	}
 
 	return 0;
 
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 17/39] firewire-lib: Add quirks for Fireworks
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (15 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 16/39] firewire-lib: Add some AV/C general commands Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED Takashi Sakamoto
                   ` (22 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds three modifications for Echo Audio's Fireworks quirks.

1.Making this module to support both TAG0 and TAG1
In IEC 61883-1, each common isochronous packet (CIP) has 'tag' field in its
packet header. The value is generally 0x01 (CIP header is included = TAG1).
But Fireworks in 'IEC 61883 compliant' mode transmits 'no data' packet with
0x00 (No CIP header is included = TAG0) but the packet includes CIP structure.
To process 'presentation timestamp' for duplex streams synchronization
correctly, this packets needs to be handled.

2.Making this module to calculate data block size and not to check continuity
One of Fireworks device, AudioFirePre8, always reports 8 data block size in
in-packets and the data block counter is incremented by 8. But actual value is
different.

3.Making this module to have the numebr of first data blocks for MIDI messages
in out-packet
Fireworks can pick up MIDI messages in first 8 data blocks and ignore in other
data blocks.

I confirm these quirks in firmware version between 4.8 and 5.8 with Windows.
I didn't investigate for the other versions. But according to contact person
in Echo Audio, there is no differences between firmwares installed by Windows
or OS X.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/amdtp.c | 50 +++++++++++++++++++++++++-------------------------
 sound/firewire/amdtp.h |  2 ++
 2 files changed, 27 insertions(+), 25 deletions(-)

diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 9b45382..a2b293a 100644
--- a/sound/firewire/amdtp.c
+++ b/sound/firewire/amdtp.c
@@ -88,6 +88,8 @@ int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
 	s->sort_table = NULL;
 	s->left_packets = NULL;
 
+	s->blocks_for_midi = UINT_MAX;
+
 	return 0;
 }
 EXPORT_SYMBOL(amdtp_stream_init);
@@ -556,8 +558,14 @@ static void amdtp_fill_midi(struct amdtp_stream *s,
 		buffer[s->midi_position] = 0x00;
 		b = (u8 *)&buffer[s->midi_position];
 
+		/*
+		 * NOTE:
+		 * Fireworks ignores midi messages in more than first 8
+		 * data blocks of an packet.
+		 */
 		port = (s->data_block_counter + f) % 8;
-		if ((s->midi[port] == NULL) ||
+		if ((f >= s->blocks_for_midi) ||
+		    (s->midi[port] == NULL) ||
 		    (snd_rawmidi_transmit(s->midi[port], b + 1, 1) <= 0)) {
 			b[0] = 0x80;
 			b[1] = 0x00;	/* confirm to be zero */
@@ -710,7 +718,7 @@ static void handle_in_packet(struct amdtp_stream *s,
 			     __be32 *buffer)
 {
 	u32 cip_header[2];
-	unsigned int data_block_quadlets, data_blocks, data_block_counter;
+	unsigned int data_blocks;
 	struct snd_pcm_substream *pcm;
 
 	cip_header[0] = be32_to_cpu(buffer[0]);
@@ -735,27 +743,16 @@ static void handle_in_packet(struct amdtp_stream *s,
 							AMDTP_FDF_NO_DATA))
 		return;
 
-	data_block_quadlets =
-			(cip_header[1] & AMDTP_DBS_MASK) >> AMDTP_DBS_SHIFT;
-	/* avoid division by zero */
-	if (data_block_quadlets == 0) {
-		dev_info_ratelimited(&s->unit->device,
-				 "Detect invalid value in dbs field: %08X\n",
-				 data_block_quadlets);
-		return;
-	}
-
-	data_blocks = (payload_quadlets - 2) / data_block_quadlets;
-	data_block_counter = cip_header[1] & AMDTP_DBC_MASK;
-
-	/* check packet continuity */
-	s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
-	if (s->data_block_counter != data_block_counter) {
-		dev_info_ratelimited(&s->unit->device,
-				     "Detect uncontinuity of CIP packets\n");
-		s->data_block_counter = data_block_counter;
-		return;
-	}
+	/*
+	 * This module don't use the value of dbs and dbc beceause Echo
+	 * AudioFirePre8 reports inappropriate value.
+	 *
+	 * This model always reports a fixed value "8" as data block size at
+	 * any sampling rates but actually data block size is different.
+	 * Additionally the value of data block count always incremented by
+	 * "8" at any sampling rates but actually it's different.
+	 */
+	data_blocks = (payload_quadlets - 2) / s->data_block_quadlets;
 
 	buffer += 2;
 
@@ -1041,11 +1038,14 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
 			goto err_context;
 	} while (s->packet_index > 0);
 
-	/* NOTE: TAG1 matches CIP. This just affects in stream */
+	/*
+	 * NOTE: TAG1 matches CIP. This just affects in stream.
+	 * Fireworks transmits NODATA packets with TAG0.
+	 */
 	s->data_block_counter = 0;
 	s->callbacked = false;
 	err = fw_iso_context_start(s->context, -1, 0,
-				   FW_ISO_CONTEXT_MATCH_TAG1);
+			FW_ISO_CONTEXT_MATCH_TAG0 | FW_ISO_CONTEXT_MATCH_TAG1);
 	if (err < 0)
 		goto err_context;
 
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
index 861f509..dce3903 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -113,6 +113,8 @@ struct amdtp_stream {
 	bool pointer_flush;
 
 	struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
+	/* quirk: the first count of data blocks in an AMDTP packet for MIDI */
+	unsigned int blocks_for_midi;
 
 	bool callbacked;
 	wait_queue_head_t callback_wait;
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (16 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 17/39] firewire-lib: Add quirks for Fireworks Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28 20:25   ` Stefan Richter
  2014-03-01 10:34   ` Stefan Richter
  2014-02-28  3:27 ` [PATCH 19/39] fireworks: Add skelton for Fireworks based devices Takashi Sakamoto
                   ` (21 subsequent siblings)
  39 siblings, 2 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

I confirm that some devices often send no response against driver's
request even if the devices handle the request. In this case, drivers
should not retry the request because it's not clear whether the devices
already handled an operation of the request or not. The second request
may bring errors or make no sence if the request was handled.

This patch adds a flag, FW_RETURN_TIMEOUT, for snd_fw_transaction().
With this flag, this function returns -ETIMEOUT when fw_run_transaction()
returns RCODE_CALCELLED.

For FCP, this patch modifies fcp_avc_transaction() with this flag. The
-ETIMEDOUT is handled as success. Linux Firewire Subsystem currently gives
200msec for default timeout (See DEFAULT_SPLIT_TIMEOUT in core-card.c).
firewire-lib gives 125msec for wait_event_timeout(). As a result, in worst
case that all of three retries are timeout and devices don't send responses,
fcp_avc_transaction() returns 1sec later ((200 + 125) msec * 3 times). But
this is rare.

For CMP, this patch modifies pcr_modify() with this flag. The -ETIMEOUT
generates another read transaction to check current value of PCR. If this
read transaction returns errors, then pcr_modify() returns the error code.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/cmp.c | 23 +++++++++++++++++++++--
 sound/firewire/fcp.c |  5 +++--
 sound/firewire/lib.c |  6 +++++-
 sound/firewire/lib.h |  1 +
 4 files changed, 30 insertions(+), 5 deletions(-)

diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index a8199fd..73f5f26 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -89,13 +89,32 @@ static int pcr_modify(struct cmp_connection *c,
 		err = snd_fw_transaction(
 				c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
 				get_offset(c, false), buffer, 8,
-				FW_FIXED_GENERATION | c->resources.generation);
+				FW_FIXED_GENERATION | c->resources.generation |
+				FW_RETURN_TIMEOUT);
 
 		if (err < 0) {
 			if (err == -EAGAIN &&
 			    bus_reset_handling == SUCCEED_ON_BUS_RESET)
 				err = 0;
-			return err;
+
+			if (err != -ETIMEDOUT)
+				return err;
+
+			/* Check current PCR. */
+			err = snd_fw_transaction(
+				c->resources.unit, TCODE_READ_QUADLET_REQUEST,
+				get_offset(c, false), buffer, 4,
+				FW_FIXED_GENERATION | c->resources.generation);
+			if (err < 0)
+				return err;
+
+			/* The lock transaction may be failed, retry */
+			if (buffer[0] != buffer[1]) {
+				buffer[0] = c->last_pcr_value;
+				continue;
+			}
+
+			break;
 		}
 
 		if (buffer[0] == old_arg) /* success? */
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index e903ddb..6fc813f 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -245,8 +245,9 @@ int fcp_avc_transaction(struct fw_unit *unit,
 					  : TCODE_WRITE_BLOCK_REQUEST;
 		ret = snd_fw_transaction(t.unit, tcode,
 					 CSR_REGISTER_BASE + CSR_FCP_COMMAND,
-					 (void *)command, command_size, 0);
-		if (ret < 0)
+					 (void *)command, command_size,
+					 FW_RETURN_TIMEOUT);
+		if ((ret < 0) && (ret != -ETIMEDOUT))
 			break;
 
 		wait_event_timeout(t.wait, t.state != STATE_PENDING,
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 7409edb..6c3278b 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -22,7 +22,7 @@
  * @length: length of @buffer
  * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the
  *         request only in that generation; use %FW_QUIET to suppress error
- *         messages
+ *         messages: use %FW_RETURN_TIMEOUT to avoid retry at RCODE_CANCELLED
  *
  * Submits an asynchronous request to the target device, and waits for the
  * response.  The node ID and the current generation are derived from @unit.
@@ -53,6 +53,10 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
 		if (rcode == RCODE_GENERATION && (flags & FW_FIXED_GENERATION))
 			return -EAGAIN;
 
+		/* timeout */
+		if ((rcode == RCODE_CANCELLED) && (flags & FW_RETURN_TIMEOUT))
+			return -ETIMEDOUT;
+
 		if (rcode_is_permanent_error(rcode) || ++tries >= 3) {
 			if (!(flags & FW_QUIET))
 				dev_err(&unit->device,
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index 02cfabc..b8ea90b 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -9,6 +9,7 @@ struct fw_unit;
 #define FW_GENERATION_MASK	0x00ff
 #define FW_FIXED_GENERATION	0x0100
 #define FW_QUIET		0x0200
+#define FW_RETURN_TIMEOUT	0x0400
 
 int snd_fw_transaction(struct fw_unit *unit, int tcode,
 		       u64 offset, void *buffer, size_t length,
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 19/39] fireworks: Add skelton for Fireworks based devices
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (17 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  6:51   ` Takashi Iwai
  2014-02-28  3:27 ` [PATCH 20/39] fireworks: Add transaction and some commands Takashi Sakamoto
                   ` (20 subsequent siblings)
  39 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds a new driver with no functionality. This driver just
creates/removes card instance according to callbacks.

Fireworks is a board module which Echo Audio produced. This module consists
of three chipsets:
 - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
 - DSP or/and FPGA for signal processing
 - Flash Memory to store firmwares

Current supported devices:
 - Mackie Onyx 400F/1200F
 - Echo AudioFire12/8(until 2009 July)
 - Echo AudioFire2/4/Pre8/8(since 2009 July)
 - Echo Fireworks 8/HDMI
 - Gibson Robot Interface pack/GoldTop

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig               |  14 +++
 sound/firewire/Makefile              |   1 +
 sound/firewire/fireworks/Makefile    |   2 +
 sound/firewire/fireworks/fireworks.c | 180 +++++++++++++++++++++++++++++++++++
 sound/firewire/fireworks/fireworks.h |  41 ++++++++
 5 files changed, 238 insertions(+)
 create mode 100644 sound/firewire/fireworks/Makefile
 create mode 100644 sound/firewire/fireworks/fireworks.c
 create mode 100644 sound/firewire/fireworks/fireworks.h

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index b3e274f..8cd4f1f 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -61,4 +61,18 @@ config SND_SCS1X
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-scs1x.
 
+config SND_FIREWORKS
+	tristate "Echo Fireworks board module support"
+	help
+	  Say Y here to include support for FireWire devices based
+	  on Echo Digital Audio Fireworks board:
+	   * Mackie Onyx 400F/1200F
+	   * Echo AudioFire12/8(until 2009 July)
+	   * Echo AudioFire2/4/Pre8/8(since 2009 July)
+	   * Echo Fireworks 8/HDMI
+	   * Gibson Robot Interface Pack/GoldTop
+
+	  To compile this driver as a module, choose M here: the module
+	  will be called snd-fireworks.
+
 endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 5099550..5cd39dc 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -10,3 +10,4 @@ obj-$(CONFIG_SND_DICE) += snd-dice.o
 obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
+obj-$(CONFIG_SND_FIREWORKS) += fireworks/
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
new file mode 100644
index 0000000..99f6fc3
--- /dev/null
+++ b/sound/firewire/fireworks/Makefile
@@ -0,0 +1,2 @@
+snd-fireworks-objs := fireworks.o
+obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
new file mode 100644
index 0000000..4a3f79e
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.c
@@ -0,0 +1,180 @@
+/*
+ * fireworks.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * Fireworks is a board module which Echo Audio produced. This module consists
+ * of three chipsets:
+ *  - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
+ *  - DSP or/and FPGA for signal processing
+ *  - Flash Memory to store firmwares
+ */
+
+#include "fireworks.h"
+
+MODULE_DESCRIPTION("Echo Fireworks driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS]	= SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS]	= SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS]	= SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable Fireworks sound card");
+
+static DEFINE_MUTEX(devices_mutex);
+static unsigned int devices_used;
+
+#define VENDOR_LOUD			0x000ff2
+#define  MODEL_MACKIE_400F		0x00400f
+#define  MODEL_MACKIE_1200F		0x01200f
+
+#define VENDOR_ECHO			0x001486
+#define  MODEL_ECHO_AUDIOFIRE_12	0x00af12
+#define  MODEL_ECHO_AUDIOFIRE_12HD	0x0af12d
+#define  MODEL_ECHO_AUDIOFIRE_12_APPLE	0x0af12a
+/* This is applied for AudioFire8 (until 2009 July) */
+#define  MODEL_ECHO_AUDIOFIRE_8		0x000af8
+#define  MODEL_ECHO_AUDIOFIRE_2		0x000af2
+#define  MODEL_ECHO_AUDIOFIRE_4		0x000af4
+/* AudioFire9 is applied for AudioFire8(since 2009 July) and AudioFirePre8 */
+#define  MODEL_ECHO_AUDIOFIRE_9		0x000af9
+/* unknown as product */
+#define  MODEL_ECHO_FIREWORKS_8		0x0000f8
+#define  MODEL_ECHO_FIREWORKS_HDMI	0x00afd1
+
+#define VENDOR_GIBSON			0x00075b
+/* for Robot Interface Pack of Dark Fire, Dusk Tiger, Les Paul Standard 2010 */
+#define  MODEL_GIBSON_RIP		0x00afb2
+/* unknown as product */
+#define  MODEL_GIBSON_GOLDTOP		0x00afb9
+
+static void
+efw_card_free(struct snd_card *card)
+{
+	struct snd_efw *efw = card->private_data;
+
+	if (efw->card_index >= 0) {
+		mutex_lock(&devices_mutex);
+		devices_used &= ~BIT(efw->card_index);
+		mutex_unlock(&devices_mutex);
+	}
+
+	mutex_destroy(&efw->mutex);
+}
+
+static int
+efw_probe(struct fw_unit *unit,
+	  const struct ieee1394_device_id *entry)
+{
+	struct snd_card *card;
+	struct snd_efw *efw;
+	int card_index, err;
+
+	mutex_lock(&devices_mutex);
+
+	/* check registered cards */
+	for (card_index = 0; card_index < SNDRV_CARDS; ++card_index)
+		if (!(devices_used & BIT(card_index)) && enable[card_index])
+			break;
+	if (card_index >= SNDRV_CARDS) {
+		err = -ENOENT;
+		goto end;
+	}
+
+	err = snd_card_new(&unit->device, index[card_index], id[card_index],
+			   THIS_MODULE, sizeof(struct snd_efw), &card);
+	if (err < 0)
+		goto end;
+	card->private_free = efw_card_free;
+
+	efw = card->private_data;
+	efw->card = card;
+	efw->unit = unit;
+	efw->card_index = -1;
+	mutex_init(&efw->mutex);
+	spin_lock_init(&efw->lock);
+
+	strcpy(efw->card->driver, "Fireworks");
+	strcpy(efw->card->shortname, "Fireworks");
+	strcpy(efw->card->longname, "Fireworks");
+	strcpy(efw->card->mixername, "Fireworks");
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		goto end;
+	}
+	dev_set_drvdata(&unit->device, efw);
+	devices_used |= BIT(card_index);
+	efw->card_index = card_index;
+end:
+	mutex_unlock(&devices_mutex);
+	return err;
+}
+
+static void efw_update(struct fw_unit *unit)
+{
+	return;
+}
+
+static void efw_remove(struct fw_unit *unit)
+{
+	struct snd_efw *efw = dev_get_drvdata(&unit->device);
+	snd_card_disconnect(efw->card);
+	snd_card_free_when_closed(efw->card);
+}
+
+static const struct ieee1394_device_id efw_id_table[] = {
+	SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_400F),
+	SND_EFW_DEV_ENTRY(VENDOR_LOUD, MODEL_MACKIE_1200F),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_8),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12HD),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_12_APPLE),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_2),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_4),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_AUDIOFIRE_9),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_8),
+	SND_EFW_DEV_ENTRY(VENDOR_ECHO, MODEL_ECHO_FIREWORKS_HDMI),
+	SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_RIP),
+	SND_EFW_DEV_ENTRY(VENDOR_GIBSON, MODEL_GIBSON_GOLDTOP),
+	{}
+};
+MODULE_DEVICE_TABLE(ieee1394, efw_id_table);
+
+static struct fw_driver efw_driver = {
+	.driver = {
+		.owner = THIS_MODULE,
+		.name = "snd-fireworks",
+		.bus = &fw_bus_type,
+	},
+	.probe    = efw_probe,
+	.update   = efw_update,
+	.remove   = efw_remove,
+	.id_table = efw_id_table,
+};
+
+static int __init snd_efw_init(void)
+{
+	return driver_register(&efw_driver.driver);
+}
+
+static void __exit snd_efw_exit(void)
+{
+	driver_unregister(&efw_driver.driver);
+	mutex_destroy(&devices_mutex);
+}
+
+module_init(snd_efw_init);
+module_exit(snd_efw_exit);
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
new file mode 100644
index 0000000..9e29ee4
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks.h
@@ -0,0 +1,41 @@
+/*
+ * fireworks.h - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#ifndef SOUND_FIREWORKS_H_INCLUDED
+#define SOUND_FIREWORKS_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+struct snd_efw {
+	struct snd_card *card;
+	struct fw_unit *unit;
+	int card_index;
+
+	struct mutex mutex;
+	spinlock_t lock;
+};
+
+#define SND_EFW_DEV_ENTRY(vendor, model) \
+{ \
+	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
+			  IEEE1394_MATCH_MODEL_ID, \
+	.vendor_id	= vendor,\
+	.model_id	= model \
+}
+
+#endif
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 20/39] fireworks: Add transaction and some commands
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (18 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 19/39] fireworks: Add skelton for Fireworks based devices Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 21/39] fireworks: Add connection and stream management Takashi Sakamoto
                   ` (19 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Fireworks uses own command and response. This commit adds functionality to
transact and adds some commands required for sound card instance and kernel
streaming.

There are two ways to deliver substance of this transaction:
1.AV/C vendor dependent command for command/response
2.Async transaction to specific addresses for command/response

By way 1, I confirm AudioFire12 cannot correctly response to some commands with
firmware version 5.0 or later. This is also confirmed by FFADO. So this driver
implement way 2.

The address for response gives an issue. When this driver allocate own callback
function into the address, then no one can allocate its own callback function.
This situation is not good for applications in user-land. This issue is solved
in later commit.

I note there is a command to change the address for response if the device
supports. But this driver uses default value. So users should not execute this
command as long as hoping this driver works correctly.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                           |   1 +
 sound/firewire/fireworks/Makefile                |   2 +-
 sound/firewire/fireworks/fireworks.c             |  74 ++++-
 sound/firewire/fireworks/fireworks.h             | 120 +++++++
 sound/firewire/fireworks/fireworks_command.c     | 395 +++++++++++++++++++++++
 sound/firewire/fireworks/fireworks_transaction.c | 191 +++++++++++
 6 files changed, 773 insertions(+), 10 deletions(-)
 create mode 100644 sound/firewire/fireworks/fireworks_command.c
 create mode 100644 sound/firewire/fireworks/fireworks_transaction.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 8cd4f1f..0b85ebd 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -63,6 +63,7 @@ config SND_SCS1X
 
 config SND_FIREWORKS
 	tristate "Echo Fireworks board module support"
+	select SND_FIREWIRE_LIB
 	help
 	  Say Y here to include support for FireWire devices based
 	  on Echo Digital Audio Fireworks board:
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index 99f6fc3..a6ce214 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,2 +1,2 @@
-snd-fireworks-objs := fireworks.o
+snd-fireworks-objs := fireworks_transaction.o fireworks_command.o fireworks.o
 obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 4a3f79e..db063eb 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -59,6 +59,46 @@ static unsigned int devices_used;
 /* unknown as product */
 #define  MODEL_GIBSON_GOLDTOP		0x00afb9
 
+/* part of hardware capability flags */
+#define FLAG_RESP_ADDR_CHANGABLE	0
+
+static int
+get_hardware_info(struct snd_efw *efw)
+{
+	struct fw_device *fw_dev = fw_parent_device(efw->unit);
+	struct snd_efw_hwinfo *hwinfo;
+	char version[12] = {0};
+	int err;
+
+	hwinfo = kzalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+	if (hwinfo == NULL)
+		return -ENOMEM;
+
+	err = snd_efw_command_get_hwinfo(efw, hwinfo);
+	if (err < 0)
+		goto end;
+
+	/* firmware version for communication chipset */
+	err = sprintf(version, "%u.%u",
+		      (hwinfo->arm_version >> 24) & 0xff,
+		      (hwinfo->arm_version >> 16) & 0xff);
+
+	strcpy(efw->card->driver, "Fireworks");
+	strcpy(efw->card->shortname, hwinfo->model_name);
+	snprintf(efw->card->longname, sizeof(efw->card->longname),
+		 "%s %s v%s, GUID %08x%08x at %s, S%d",
+		 hwinfo->vendor_name, hwinfo->model_name, version,
+		 hwinfo->guid_hi, hwinfo->guid_lo,
+		 dev_name(&efw->unit->device), 100 << fw_dev->max_speed);
+	strcpy(efw->card->mixername, hwinfo->model_name);
+
+	if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
+		efw->resp_addr_changable = true;
+end:
+	kfree(hwinfo);
+	return err;
+}
+
 static void
 efw_card_free(struct snd_card *card)
 {
@@ -105,26 +145,30 @@ efw_probe(struct fw_unit *unit,
 	mutex_init(&efw->mutex);
 	spin_lock_init(&efw->lock);
 
-	strcpy(efw->card->driver, "Fireworks");
-	strcpy(efw->card->shortname, "Fireworks");
-	strcpy(efw->card->longname, "Fireworks");
-	strcpy(efw->card->mixername, "Fireworks");
+	err = get_hardware_info(efw);
+	if (err < 0)
+		goto error;
 
 	err = snd_card_register(card);
-	if (err < 0) {
-		snd_card_free(card);
-		goto end;
-	}
+	if (err < 0)
+		goto error;
+
 	dev_set_drvdata(&unit->device, efw);
 	devices_used |= BIT(card_index);
 	efw->card_index = card_index;
 end:
 	mutex_unlock(&devices_mutex);
 	return err;
+error:
+	snd_card_free(card);
+	mutex_unlock(&devices_mutex);
+	return err;
 }
 
 static void efw_update(struct fw_unit *unit)
 {
+	struct snd_efw *efw = dev_get_drvdata(&unit->device);
+	snd_efw_transaction_bus_reset(efw->unit);
 	return;
 }
 
@@ -167,11 +211,23 @@ static struct fw_driver efw_driver = {
 
 static int __init snd_efw_init(void)
 {
-	return driver_register(&efw_driver.driver);
+	int err;
+
+	err = snd_efw_transaction_register();
+	if (err < 0)
+		goto end;
+
+	err = driver_register(&efw_driver.driver);
+	if (err < 0)
+		snd_efw_transaction_unregister();
+
+end:
+	return err;
 }
 
 static void __exit snd_efw_exit(void)
 {
+	snd_efw_transaction_unregister();
 	driver_unregister(&efw_driver.driver);
 	mutex_destroy(&devices_mutex);
 }
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 9e29ee4..751302d 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -20,6 +20,19 @@
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/pcm.h>
+
+#include "../cmp.h"
+#include "../lib.h"
+
+#define SND_EFW_MUITIPLIER_MODES	3
+#define HWINFO_NAME_SIZE_BYTES		32
+#define HWINFO_MAX_CAPS_GROUPS		8
+
+struct snd_efw_phys_grp {
+	u8 type;	/* see enum snd_efw_grp_type */
+	u8 count;
+} __packed;
 
 struct snd_efw {
 	struct snd_card *card;
@@ -28,7 +41,114 @@ struct snd_efw {
 
 	struct mutex mutex;
 	spinlock_t lock;
+
+	/* for transaction */
+	u32 seqnum;
+	bool resp_addr_changable;
+};
+
+struct snd_efw_transaction {
+	u32 length;
+	u32 version;
+	u32 seqnum;
+	u32 category;
+	u32 command;
+	u32 status;
+	u32 params[0];
+};
+int snd_efw_transaction_run(struct fw_unit *unit,
+			    const void *cmd, unsigned int cmd_size,
+			    void *resp, unsigned int resp_size, u32 seqnum);
+int snd_efw_transaction_register(void);
+void snd_efw_transaction_unregister(void);
+void snd_efw_transaction_bus_reset(struct fw_unit *unit);
+
+struct snd_efw_hwinfo {
+	u32 flags;
+	u32 guid_hi;
+	u32 guid_lo;
+	u32 type;
+	u32 version;
+	char vendor_name[HWINFO_NAME_SIZE_BYTES];
+	char model_name[HWINFO_NAME_SIZE_BYTES];
+	u32 supported_clocks;
+	u32 amdtp_rx_pcm_channels;
+	u32 amdtp_tx_pcm_channels;
+	u32 phys_out;
+	u32 phys_in;
+	u32 phys_out_grp_count;
+	struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+	u32 phys_in_grp_count;
+	struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+	u32 midi_out_ports;
+	u32 midi_in_ports;
+	u32 max_sample_rate;
+	u32 min_sample_rate;
+	u32 dsp_version;
+	u32 arm_version;
+	u32 mixer_playback_channels;
+	u32 mixer_capture_channels;
+	u32 fpga_version;
+	u32 amdtp_rx_pcm_channels_2x;
+	u32 amdtp_tx_pcm_channels_2x;
+	u32 amdtp_rx_pcm_channels_4x;
+	u32 amdtp_tx_pcm_channels_4x;
+	u32 reserved[16];
+} __packed;
+enum snd_efw_grp_type {
+	SND_EFW_CH_TYPE_ANALOG			= 0,
+	SND_EFW_CH_TYPE_SPDIF			= 1,
+	SND_EFW_CH_TYPE_ADAT			= 2,
+	SND_EFW_CH_TYPE_SPDIF_OR_ADAT		= 3,
+	SND_EFW_CH_TYPE_ANALOG_MIRRORING	= 4,
+	SND_EFW_CH_TYPE_HEADPHONES		= 5,
+	SND_EFW_CH_TYPE_I2S			= 6,
+	SND_EFW_CH_TYPE_GUITAR			= 7,
+	SND_EFW_CH_TYPE_PIEZO_GUITAR		= 8,
+	SND_EFW_CH_TYPE_GUITAR_STRING		= 9,
+	SND_EFW_CH_TYPE_VIRTUAL			= 0x10000,
+	SND_EFW_CH_TYPE_DUMMY
+};
+struct snd_efw_phys_meters {
+	u32 status;	/* guitar state/midi signal/clock input detect */
+	u32 reserved0;
+	u32 reserved1;
+	u32 reserved2;
+	u32 reserved3;
+	u32 out_meters;
+	u32 in_meters;
+	u32 reserved4;
+	u32 reserved5;
+	u32 values[0];
+} __packed;
+enum snd_efw_clock_source {
+	SND_EFW_CLOCK_SOURCE_INTERNAL	= 0,
+	SND_EFW_CLOCK_SOURCE_SYTMATCH	= 1,
+	SND_EFW_CLOCK_SOURCE_WORDCLOCK	= 2,
+	SND_EFW_CLOCK_SOURCE_SPDIF	= 3,
+	SND_EFW_CLOCK_SOURCE_ADAT_1	= 4,
+	SND_EFW_CLOCK_SOURCE_ADAT_2	= 5,
+	SND_EFW_CLOCK_SOURCE_CONTINUOUS	= 6	/* internal variable clock */
+};
+enum snd_efw_transport_mode {
+	SND_EFW_TRANSPORT_MODE_WINDOWS	= 0,
+	SND_EFW_TRANSPORT_MODE_IEC61883	= 1,
 };
+int snd_efw_command_identify(struct snd_efw *efw);
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+				  u16 addr_high, u32 addr_low);
+int snd_efw_command_set_tx_mode(struct snd_efw *efw, unsigned int mode);
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+			       struct snd_efw_hwinfo *hwinfo);
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+				    struct snd_efw_phys_meters *meters,
+				    unsigned int len);
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+				     enum snd_efw_clock_source *source);
+int snd_efw_command_set_clock_source(struct snd_efw *efw,
+				     enum snd_efw_clock_source source);
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
+int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
 
 #define SND_EFW_DEV_ENTRY(vendor, model) \
 { \
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
new file mode 100644
index 0000000..9b19e3e
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -0,0 +1,395 @@
+/*
+ * fireworks_command.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto <o-takashi@sakmocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./fireworks.h"
+
+/*
+ * This driver uses transaction version 1 or later to use extended hardware
+ * information. Then too old devices are not available.
+ *
+ * Each commands are not required to have continuous sequence numbers. This
+ * number is just used to match command and response.
+ *
+ * This module support a part of commands. Please see FFADO if you want to see
+ * whole commands. But there are some commands which FFADO don't implement.
+ *
+ * Fireworks also supports AV/C general commands and AV/C Stream Format
+ * Information commands. But this module don't use them.
+ */
+
+#define EFW_TRANSACTION_SEQNUM_MAX	((u32)~0)
+
+/* for clock source and sampling rate */
+struct efc_clock {
+	u32 source;
+	u32 sampling_rate;
+	u32 index;
+};
+
+/* command categories */
+enum efc_category {
+	EFC_CAT_HWINFO		= 0,
+	EFC_CAT_TRANSPORT	= 2,
+	EFC_CAT_HWCTL		= 3,
+};
+
+/* hardware info category commands */
+enum efc_cmd_hwinfo {
+	EFC_CMD_HWINFO_GET_CAPS		= 0,
+	EFC_CMD_HWINFO_GET_POLLED	= 1,
+	EFC_CMD_HWINFO_SET_RESP_ADDR	= 2
+};
+
+enum efc_cmd_transport {
+	EFC_CMD_TRANSPORT_SET_TX_MODE	= 0
+};
+
+/* hardware control category commands */
+enum efc_cmd_hwctl {
+	EFC_CMD_HWCTL_SET_CLOCK		= 0,
+	EFC_CMD_HWCTL_GET_CLOCK		= 1,
+	EFC_CMD_HWCTL_IDENTIFY		= 5
+};
+
+/* return values in response */
+enum efr_status {
+	EFC_RETVAL_OK			= 0,
+	EFC_RETVAL_BAD			= 1,
+	EFC_RETVAL_BAD_COMMAND		= 2,
+	EFC_RETVAL_COMM_ERR		= 3,
+	EFC_RETVAL_BAD_QUAD_COUNT	= 4,
+	EFC_RETVAL_UNSUPPORTED		= 5,
+	EFC_RETVAL_1394_TIMEOUT		= 6,
+	EFC_RETVAL_DSP_TIMEOUT		= 7,
+	EFC_RETVAL_BAD_RATE		= 8,
+	EFC_RETVAL_BAD_CLOCK		= 9,
+	EFC_RETVAL_BAD_CHANNEL		= 10,
+	EFC_RETVAL_BAD_PAN		= 11,
+	EFC_RETVAL_FLASH_BUSY		= 12,
+	EFC_RETVAL_BAD_MIRROR		= 13,
+	EFC_RETVAL_BAD_LED		= 14,
+	EFC_RETVAL_BAD_PARAMETER	= 15,
+	EFC_RETVAL_INCOMPLETE		= 0x80000000
+};
+
+static const char *const efr_status_names[] = {
+	[EFC_RETVAL_OK]			= "OK",
+	[EFC_RETVAL_BAD]		= "bad",
+	[EFC_RETVAL_BAD_COMMAND]	= "bad command",
+	[EFC_RETVAL_COMM_ERR]		= "comm err",
+	[EFC_RETVAL_BAD_QUAD_COUNT]	= "bad quad count",
+	[EFC_RETVAL_UNSUPPORTED]	= "unsupported",
+	[EFC_RETVAL_1394_TIMEOUT]	= "1394 timeout",
+	[EFC_RETVAL_DSP_TIMEOUT]	= "DSP timeout",
+	[EFC_RETVAL_BAD_RATE]		= "bad rate",
+	[EFC_RETVAL_BAD_CLOCK]		= "bad clock",
+	[EFC_RETVAL_BAD_CHANNEL]	= "bad channel",
+	[EFC_RETVAL_BAD_PAN]		= "bad pan",
+	[EFC_RETVAL_FLASH_BUSY]		= "flash busy",
+	[EFC_RETVAL_BAD_MIRROR]		= "bad mirror",
+	[EFC_RETVAL_BAD_LED]		= "bad LED",
+	[EFC_RETVAL_BAD_PARAMETER]	= "bad parameter",
+	[EFC_RETVAL_BAD_PARAMETER + 1]	= "incomplete"
+};
+
+static int
+efw_transaction(struct snd_efw *efw, unsigned int category,
+		unsigned int command,
+		const __be32 *params, unsigned int param_quads,
+		const __be32 *resp, unsigned int resp_quads)
+{
+	struct snd_efw_transaction *header;
+	__be32 *buf;
+	u32 seqnum;
+	unsigned int i, buf_bytes, cmd_bytes;
+	int err;
+
+	/* calculate buffer size*/
+	buf_bytes = sizeof(struct snd_efw_transaction) +
+		    max(param_quads, resp_quads) * sizeof(u32);
+
+	/* keep buffer */
+	buf = kzalloc(buf_bytes, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	/* to keep consistency of sequence number */
+	spin_lock(&efw->lock);
+	if (efw->seqnum + 1 >= EFW_TRANSACTION_SEQNUM_MAX)
+		efw->seqnum = 0;
+	else
+		efw->seqnum += 2;
+	seqnum = efw->seqnum;
+	spin_unlock(&efw->lock);
+
+	/* fill transaction header fields */
+	cmd_bytes = sizeof(struct snd_efw_transaction) +
+		    param_quads * sizeof(u32);
+	header = (struct snd_efw_transaction *)buf;
+	header->length	 = cmd_bytes / sizeof(u32);
+	header->version	 = 1;
+	header->seqnum	 = seqnum;
+	header->category = category;
+	header->command	 = command;
+	header->status	 = 0;
+	for (i = 0; i < sizeof(struct snd_efw_transaction) / sizeof(u32); i++)
+		cpu_to_be32s(&buf[i]);
+
+	/* fill transaction command parameters */
+	for (i = 0; i < param_quads; i++)
+		header->params[i] = params[i];
+
+	err = snd_efw_transaction_run(efw->unit, buf, cmd_bytes,
+				      buf, buf_bytes, seqnum);
+	if (err < 0)
+		goto end;
+
+	/* check transaction header fields */
+	for (i = 0; i < sizeof(struct snd_efw_transaction) / sizeof(u32); i++)
+		be32_to_cpus(&buf[i]);
+	if ((header->version  < 1) ||
+	    (header->category != category) ||
+	    (header->command  != command) ||
+	    (header->status   != EFC_RETVAL_OK)) {
+		dev_err(&efw->unit->device, "EFC failed [%u/%u]: %s\n",
+			header->category, header->command,
+			efr_status_names[header->status]);
+		err = -EIO;
+		goto end;
+	}
+
+	/* fill transaction response parameters */
+	if (resp != NULL) {
+		memset((void *)resp, 0, resp_quads * sizeof(u32));
+		resp_quads = min_t(unsigned int, resp_quads,
+				   header->length -
+				    sizeof(struct snd_efw_transaction) /
+				    sizeof(u32));
+		memcpy((void *)resp, &buf[6], resp_quads * sizeof(u32));
+	}
+end:
+	kfree(buf);
+	return err;
+}
+
+/* just blink LEDs on the device */
+int snd_efw_command_identify(struct snd_efw *efw)
+{
+	return efw_transaction(efw, EFC_CAT_HWCTL,
+			       EFC_CMD_HWCTL_IDENTIFY,
+			       NULL, 0, NULL, 0);
+}
+
+/*
+ * The address in host system for EFC response is changable when the device
+ * supports. struct hwinfo.flags includes its flag. The default is
+ * INITIAL_MEMORY_SPACE_EFC_RESPONSE
+ */
+int snd_efw_command_set_resp_addr(struct snd_efw *efw,
+				  u16 addr_high, u32 addr_low)
+{
+	__be32 addr[2];
+
+	addr[0] = cpu_to_be32(addr_high);
+	addr[1] = cpu_to_be32(addr_low);
+
+	if (!efw->resp_addr_changable)
+		return -ENOSYS;
+
+	return efw_transaction(efw, EFC_CAT_HWCTL,
+			       EFC_CMD_HWINFO_SET_RESP_ADDR,
+			       addr, 2, NULL, 0);
+}
+
+/*
+ * This is for timestamp processing. In Windows mode, all 32bit fields of second
+ * CIP header in AMDTP transmit packet is used for 'presentation timestamp'. In
+ * 'no data' packet the value of this field is 0x90ffffff.
+ */
+int snd_efw_command_set_tx_mode(struct snd_efw *efw,
+				enum snd_efw_transport_mode mode)
+{
+	__be32 param = cpu_to_be32(mode);
+	return efw_transaction(efw, EFC_CAT_TRANSPORT,
+			       EFC_CMD_TRANSPORT_SET_TX_MODE,
+			       &param, 1, NULL, 0);
+}
+
+int snd_efw_command_get_hwinfo(struct snd_efw *efw,
+			       struct snd_efw_hwinfo *hwinfo)
+{
+	int err;
+
+	err  = efw_transaction(efw, EFC_CAT_HWINFO,
+			       EFC_CMD_HWINFO_GET_CAPS,
+			       NULL, 0, (__be32 *)hwinfo,
+			       sizeof(*hwinfo) / sizeof(u32));
+	if (err < 0)
+		goto end;
+
+	be32_to_cpus(&hwinfo->flags);
+	be32_to_cpus(&hwinfo->guid_hi);
+	be32_to_cpus(&hwinfo->guid_lo);
+	be32_to_cpus(&hwinfo->type);
+	be32_to_cpus(&hwinfo->version);
+	be32_to_cpus(&hwinfo->supported_clocks);
+	be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels);
+	be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels);
+	be32_to_cpus(&hwinfo->phys_out);
+	be32_to_cpus(&hwinfo->phys_in);
+	be32_to_cpus(&hwinfo->phys_out_grp_count);
+	be32_to_cpus(&hwinfo->phys_in_grp_count);
+	be32_to_cpus(&hwinfo->midi_out_ports);
+	be32_to_cpus(&hwinfo->midi_in_ports);
+	be32_to_cpus(&hwinfo->max_sample_rate);
+	be32_to_cpus(&hwinfo->min_sample_rate);
+	be32_to_cpus(&hwinfo->dsp_version);
+	be32_to_cpus(&hwinfo->arm_version);
+	be32_to_cpus(&hwinfo->mixer_playback_channels);
+	be32_to_cpus(&hwinfo->mixer_capture_channels);
+	be32_to_cpus(&hwinfo->fpga_version);
+	be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_2x);
+	be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_2x);
+	be32_to_cpus(&hwinfo->amdtp_rx_pcm_channels_4x);
+	be32_to_cpus(&hwinfo->amdtp_tx_pcm_channels_4x);
+
+	/* ensure terminated */
+	hwinfo->vendor_name[HWINFO_NAME_SIZE_BYTES - 1] = '\0';
+	hwinfo->model_name[HWINFO_NAME_SIZE_BYTES  - 1] = '\0';
+end:
+	return err;
+}
+
+int snd_efw_command_get_phys_meters(struct snd_efw *efw,
+				    struct snd_efw_phys_meters *meters,
+				    unsigned int len)
+{
+	__be32 *buf = (__be32 *)meters;
+	unsigned int i;
+	int err;
+
+	err = efw_transaction(efw, EFC_CAT_HWINFO,
+			      EFC_CMD_HWINFO_GET_POLLED,
+			      NULL, 0, (__be32 *)meters, len / sizeof(u32));
+	if (err >= 0)
+		for (i = 0; i < len / sizeof(u32); i++)
+			be32_to_cpus(&buf[i]);
+
+	return err;
+}
+
+static int
+command_get_clock(struct snd_efw *efw, struct efc_clock *clock)
+{
+	int err;
+
+	err = efw_transaction(efw, EFC_CAT_HWCTL,
+			      EFC_CMD_HWCTL_GET_CLOCK,
+			      NULL, 0,
+			      (__be32 *)clock,
+			      sizeof(struct efc_clock) / sizeof(u32));
+	if (err >= 0) {
+		be32_to_cpus(&clock->source);
+		be32_to_cpus(&clock->sampling_rate);
+		be32_to_cpus(&clock->index);
+	}
+
+	return err;
+}
+
+/* give UINT_MAX if set nothing */
+static int
+command_set_clock(struct snd_efw *efw,
+		  unsigned int source, unsigned int rate)
+{
+	struct efc_clock clock = {0};
+	int err;
+
+	/* check arguments */
+	if ((source == UINT_MAX) && (rate == UINT_MAX)) {
+		err = -EINVAL;
+		goto end;
+	}
+
+	/* get current status */
+	err = command_get_clock(efw, &clock);
+	if (err < 0)
+		goto end;
+
+	/* no need */
+	if ((clock.source == source) && (clock.sampling_rate == rate))
+		goto end;
+
+	/* set params */
+	if ((source != UINT_MAX) && (clock.source != source))
+		clock.source = source;
+	if ((rate != UINT_MAX) && (clock.sampling_rate != rate))
+		clock.sampling_rate = rate;
+	clock.index = 0;
+
+	cpu_to_be32s(&clock.source);
+	cpu_to_be32s(&clock.sampling_rate);
+	cpu_to_be32s(&clock.index);
+
+	err = efw_transaction(efw, EFC_CAT_HWCTL,
+			      EFC_CMD_HWCTL_SET_CLOCK,
+			      (__be32 *)&clock,
+			      sizeof(struct efc_clock) / sizeof(u32),
+			      NULL, 0);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * With firmware version 5.8, just after changing clock state, these
+	 * parameters are not immediately retrieved by get command. In my
+	 * trial, there needs to be 100msec to get changed parameters.
+	 */
+	msleep(150);
+end:
+	return err;
+}
+
+int snd_efw_command_get_clock_source(struct snd_efw *efw,
+				     enum snd_efw_clock_source *source)
+{
+	int err;
+	struct efc_clock clock = {0};
+
+	err = command_get_clock(efw, &clock);
+	if (err >= 0)
+		*source = clock.source;
+
+	return err;
+}
+
+int snd_efw_command_set_clock_source(struct snd_efw *efw,
+				     enum snd_efw_clock_source source)
+{
+	return command_set_clock(efw, source, UINT_MAX);
+}
+
+int snd_efw_command_get_sampling_rate(struct snd_efw *efw,
+				      unsigned int *rate)
+{
+	int err;
+	struct efc_clock clock = {0};
+
+	err = command_get_clock(efw, &clock);
+	if (err >= 0)
+		*rate = clock.sampling_rate;
+
+	return err;
+}
+
+int
+snd_efw_command_set_sampling_rate(struct snd_efw *efw,
+				  unsigned int rate)
+{
+	return command_set_clock(efw, UINT_MAX, rate);
+}
+
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
new file mode 100644
index 0000000..c2f499e
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -0,0 +1,191 @@
+/*
+ * fireworks_transaction.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto <o-takashi@sakmocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * Fireworks have its own transaction. The transaction can be delivered by AV/C
+ * Vendor Specific command. But at least Windows driver and firmware version 5.5
+ * or later don't use it.
+ *
+ * Transaction substance:
+ *  At first, 6 data exist. Following to the 6 data, parameters for each
+ *  commands exists. All of parameters are 32 bit alighed to big endian.
+ *   data[0]:	Length of transaction substance
+ *   data[1]:	Transaction version
+ *   data[2]:	Sequence number. This is incremented by the device
+ *   data[3]:	transaction category
+ *   data[4]:	transaction command
+ *   data[5]:	return value in response.
+ *   data[6-]:	parameters
+ *
+ * Transaction address:
+ *  command:	0xecc000000000
+ *  response:	0xecc080000000 (default)
+ *
+ * I note that the address for response can be changed by command. But this
+ * module uses the default address.
+ */
+#include "./fireworks.h"
+
+#define MEMORY_SPACE_EFW_COMMAND	0xecc000000000
+#define MEMORY_SPACE_EFW_RESPONSE	0xecc080000000
+/* this is for juju convinience? */
+#define MEMORY_SPACE_EFW_END		0xecc080000200
+
+#define ERROR_RETRIES 3
+#define ERROR_DELAY_MS 5
+#define EFC_TIMEOUT_MS 125
+
+static DEFINE_SPINLOCK(transaction_queues_lock);
+static LIST_HEAD(transaction_queues);
+
+enum transaction_queue_state {
+	STATE_PENDING,
+	STATE_BUS_RESET,
+	STATE_COMPLETE
+};
+
+struct transaction_queue {
+	struct list_head list;
+	struct fw_unit *unit;
+	void *buf;
+	unsigned int size;
+	u32 seqnum;
+	enum transaction_queue_state state;
+	wait_queue_head_t wait;
+};
+
+int snd_efw_transaction_run(struct fw_unit *unit,
+			    const void *cmd, unsigned int cmd_size,
+			    void *resp, unsigned int resp_size, u32 seqnum)
+{
+	struct transaction_queue t;
+	unsigned int tries;
+	int ret;
+
+	t.unit = unit;
+	t.buf = resp;
+	t.size = resp_size;
+	t.seqnum = seqnum + 1;
+	t.state = STATE_PENDING;
+	init_waitqueue_head(&t.wait);
+
+	spin_lock_irq(&transaction_queues_lock);
+	list_add_tail(&t.list, &transaction_queues);
+	spin_unlock_irq(&transaction_queues_lock);
+
+	tries = 0;
+	do {
+		ret = snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
+					 MEMORY_SPACE_EFW_COMMAND,
+					 (void *)cmd, cmd_size, 0);
+		if (ret < 0)
+			break;
+
+		wait_event_timeout(t.wait, t.state != STATE_PENDING,
+				   msecs_to_jiffies(EFC_TIMEOUT_MS));
+
+		if (t.state == STATE_COMPLETE) {
+			ret = t.size;
+			break;
+		} else if (t.state == STATE_BUS_RESET) {
+			msleep(ERROR_DELAY_MS);
+		} else if (++tries >= ERROR_RETRIES) {
+			dev_err(&t.unit->device, "EFC command timed out\n");
+			ret = -EIO;
+			break;
+		}
+	} while (1);
+
+	spin_lock_irq(&transaction_queues_lock);
+	list_del(&t.list);
+	spin_unlock_irq(&transaction_queues_lock);
+
+	return ret;
+}
+
+static void
+efw_response(struct fw_card *card, struct fw_request *request,
+	     int tcode, int destination, int source,
+	     int generation, unsigned long long offset,
+	     void *data, size_t length, void *callback_data)
+{
+	struct fw_device *device;
+	struct transaction_queue *t;
+	unsigned long flags;
+	int rcode;
+	u32 seqnum;
+
+	rcode = RCODE_TYPE_ERROR;
+	if (length < sizeof(struct snd_efw_transaction)) {
+		rcode = RCODE_DATA_ERROR;
+		goto end;
+	} else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
+		rcode = RCODE_ADDRESS_ERROR;
+		goto end;
+	}
+
+	seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+
+	spin_lock_irqsave(&transaction_queues_lock, flags);
+	list_for_each_entry(t, &transaction_queues, list) {
+		device = fw_parent_device(t->unit);
+		if ((device->card != card) ||
+		    (device->generation != generation))
+			continue;
+		smp_rmb();	/* node_id vs. generation */
+		if (device->node_id != source)
+			continue;
+
+		if ((t->state == STATE_PENDING) && (t->seqnum == seqnum)) {
+			t->state = STATE_COMPLETE;
+			t->size = min_t(unsigned int, length, t->size);
+			memcpy(t->buf, data, t->size);
+			wake_up(&t->wait);
+			rcode = RCODE_COMPLETE;
+		}
+	}
+	spin_unlock_irqrestore(&transaction_queues_lock, flags);
+end:
+	fw_send_response(card, request, rcode);
+}
+
+void snd_efw_transaction_bus_reset(struct fw_unit *unit)
+{
+	struct transaction_queue *t;
+
+	spin_lock_irq(&transaction_queues_lock);
+	list_for_each_entry(t, &transaction_queues, list) {
+		if ((t->unit == unit) &&
+		    (t->state == STATE_PENDING)) {
+			t->state = STATE_BUS_RESET;
+			wake_up(&t->wait);
+		}
+	}
+	spin_unlock_irq(&transaction_queues_lock);
+}
+
+static struct fw_address_handler resp_register_handler = {
+	.length = MEMORY_SPACE_EFW_END - MEMORY_SPACE_EFW_RESPONSE,
+	.address_callback = efw_response
+};
+
+int snd_efw_transaction_register(void)
+{
+	static const struct fw_address_region resp_register_region = {
+		.start	= MEMORY_SPACE_EFW_RESPONSE,
+		.end	= MEMORY_SPACE_EFW_END
+	};
+	return fw_core_add_address_handler(&resp_register_handler,
+					   &resp_register_region);
+}
+
+void snd_efw_transaction_unregister(void)
+{
+	WARN_ON(!list_empty(&transaction_queues));
+	fw_core_remove_address_handler(&resp_register_handler);
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 21/39] fireworks: Add connection and stream management
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (19 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 20/39] fireworks: Add transaction and some commands Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 22/39] fireworks: Add proc interface for debugging purpose Takashi Sakamoto
                   ` (18 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Fireworks manages connections by CMP and can transmit/receive AMDTP streams
with a few quirks. This commit adds functionality to start/stop the streams.

Major Fireworks products don't support 'SYT-Match' modes, except for
AudioFire12/8(till 2009 July) with firmware version 1.0. Already in previous
commit, this driver don't support such old firmwares. So this commit adds
support for non 'SYT-Match' modes.

I note that this driver has a short gap for MIDI streams when starting PCM
stream. When AMDTP streams are running only for MIDI data and PCM data is
going to be joined at different sampling rate, then AMDTP streams are
stopped once and started again after changing sampling rate.

Fireworks quirks:

1.Fireworks transmits packets with both TAG0 and TAG1
In IEC 61883-1, each common isochronous packet (CIP) has 'tag' field in its
packet header. The value is generally 0x01 ('CIP header is included' = TAG1).
But Fireworks in 'IEC 61883 compliant mode' transmits 'no data' packet with 0x00
('No CIP header is included' = TAG0) but actually the packet includes CIP
structure. To handle 'presentation timestamp' for duplex streams
synchronization, this packets must be handled.

2.Fireworks transmits 'no data' packet out of specification
Fireworks transmits 'no data' packet with 'NO-DATA' FDF and no dummy data. It
includes CIP headers only. In IEC 61883-6, 'no data' packet is generated by two
ways. One is empty CIP packet defined in IEC 61883-1. Another is
'special non-empty packet' defined in IEC 61883-6. Fireworks uses strange
combination of them.

3.Fireworks transmits packets with a bit disorder
A sequence of DBC field in packets transmitted by Fireworks includes a bit
disorder. See logs in the end of this commit.

4.Fireworks can handle MIDI messages in the first 8 data blocks of out-packet
Fireworks ignores MIDI messages in other data blocks.

5.AudioFirePre8 transmits packets with wrong data block size
One of Fireworks device, AudioFirePre8, always reports 8 data block size in
transmitted packets and increments the data block counter by 8. But this is not
actual value.

I confirm them with firmware version 4.8 or later with Windows. According to
a contact persion in Echo Audio, there is no difference between firmwares
installed by Windows/OS X.

Here, an example of a sequence of packets transmitted by Fireworks:

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/fireworks/Makefile           |   3 +-
 sound/firewire/fireworks/fireworks.c        |  46 ++++
 sound/firewire/fireworks/fireworks.h        |  28 +++
 sound/firewire/fireworks/fireworks_stream.c | 331 ++++++++++++++++++++++++++++
 4 files changed, 407 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/fireworks/fireworks_stream.c

diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index a6ce214..1bccb65 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,2 +1,3 @@
-snd-fireworks-objs := fireworks_transaction.o fireworks_command.o fireworks.o
+snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
+		      fireworks_stream.o fireworks.o
 obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index db063eb..6d7de19 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -94,6 +94,42 @@ get_hardware_info(struct snd_efw *efw)
 
 	if (hwinfo->flags & BIT(FLAG_RESP_ADDR_CHANGABLE))
 		efw->resp_addr_changable = true;
+
+	efw->supported_sampling_rate = 0;
+	if ((hwinfo->min_sample_rate <= 22050)
+	 && (22050 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_22050;
+	if ((hwinfo->min_sample_rate <= 32000)
+	 && (32000 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_32000;
+	if ((hwinfo->min_sample_rate <= 44100)
+	 && (44100 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_44100;
+	if ((hwinfo->min_sample_rate <= 48000)
+	 && (48000 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_48000;
+	if ((hwinfo->min_sample_rate <= 88200)
+	 && (88200 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_88200;
+	if ((hwinfo->min_sample_rate <= 96000)
+	 && (96000 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_96000;
+	if ((hwinfo->min_sample_rate <= 176400)
+	 && (176400 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_176400;
+	if ((hwinfo->min_sample_rate <= 192000)
+	 && (192000 <= hwinfo->max_sample_rate))
+		efw->supported_sampling_rate |= SNDRV_PCM_RATE_192000;
+
+	efw->midi_out_ports = hwinfo->midi_out_ports;
+	efw->midi_in_ports = hwinfo->midi_in_ports;
+
+	efw->pcm_capture_channels[0] = hwinfo->amdtp_tx_pcm_channels;
+	efw->pcm_capture_channels[1] = hwinfo->amdtp_tx_pcm_channels_2x;
+	efw->pcm_capture_channels[2] = hwinfo->amdtp_tx_pcm_channels_4x;
+	efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
+	efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
+	efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
 end:
 	kfree(hwinfo);
 	return err;
@@ -149,6 +185,10 @@ efw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	err = snd_efw_stream_init_duplex(efw);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;
@@ -168,13 +208,19 @@ error:
 static void efw_update(struct fw_unit *unit)
 {
 	struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
 	snd_efw_transaction_bus_reset(efw->unit);
+	snd_efw_stream_update_duplex(efw);
+
 	return;
 }
 
 static void efw_remove(struct fw_unit *unit)
 {
 	struct snd_efw *efw = dev_get_drvdata(&unit->device);
+
+	snd_efw_stream_destroy_duplex(efw);
+
 	snd_card_disconnect(efw->card);
 	snd_card_free_when_closed(efw->card);
 }
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 751302d..c022c71 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -22,9 +22,15 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
 #include "../cmp.h"
 #include "../lib.h"
 
+#define SND_EFW_MAX_MIDI_OUT_PORTS	2
+#define SND_EFW_MAX_MIDI_IN_PORTS	2
+
 #define SND_EFW_MUITIPLIER_MODES	3
 #define HWINFO_NAME_SIZE_BYTES		32
 #define HWINFO_MAX_CAPS_GROUPS		8
@@ -45,6 +51,20 @@ struct snd_efw {
 	/* for transaction */
 	u32 seqnum;
 	bool resp_addr_changable;
+
+	unsigned int midi_in_ports;
+	unsigned int midi_out_ports;
+
+	unsigned int supported_sampling_rate;
+	unsigned int pcm_capture_channels[SND_EFW_MUITIPLIER_MODES];
+	unsigned int pcm_playback_channels[SND_EFW_MUITIPLIER_MODES];
+
+	struct amdtp_stream tx_stream;
+	struct amdtp_stream rx_stream;
+	struct cmp_connection out_conn;
+	struct cmp_connection in_conn;
+	unsigned int capture_substreams;
+	unsigned int playback_substreams;
 };
 
 struct snd_efw_transaction {
@@ -150,6 +170,14 @@ int snd_efw_command_set_clock_source(struct snd_efw *efw,
 int snd_efw_command_get_sampling_rate(struct snd_efw *efw, unsigned int *rate);
 int snd_efw_command_set_sampling_rate(struct snd_efw *efw, unsigned int rate);
 
+int snd_efw_stream_init_duplex(struct snd_efw *efw);
+int snd_efw_stream_start_duplex(struct snd_efw *efw,
+				struct amdtp_stream *request,
+				int sampling_rate);
+int snd_efw_stream_stop_duplex(struct snd_efw *efw);
+void snd_efw_stream_update_duplex(struct snd_efw *efw);
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
+
 #define SND_EFW_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
new file mode 100644
index 0000000..f41aee4
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -0,0 +1,331 @@
+/*
+ * fireworks_stream.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "./fireworks.h"
+
+#define CALLBACK_TIMEOUT	100
+
+static unsigned int freq_table[] = {
+	/* multiplier mode 0 */
+	[0] = 32000,
+	[1] = 44100,
+	[2] = 48000,
+	/* multiplier mode 1 */
+	[3] = 88200,
+	[4] = 96000,
+	/* multiplier mode 2 */
+	[5] = 176400,
+	[6] = 192000,
+};
+
+static inline unsigned int
+get_multiplier_mode_with_index(unsigned int index)
+{
+	return ((int)index - 1) / 2;
+}
+
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(freq_table); i++) {
+		if (freq_table[i] == sampling_rate) {
+			*mode = get_multiplier_mode_with_index(i);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int
+init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+	struct cmp_connection *conn;
+	enum cmp_direction c_dir;
+	enum amdtp_stream_direction s_dir;
+	int err;
+
+	if (stream == &efw->tx_stream) {
+		conn = &efw->out_conn;
+		c_dir = CMP_OUTPUT;
+		s_dir = AMDTP_IN_STREAM;
+	} else {
+		conn = &efw->in_conn;
+		c_dir = CMP_INPUT;
+		s_dir = AMDTP_OUT_STREAM;
+	}
+
+	err = cmp_connection_init(conn, efw->unit, c_dir, 0);
+	if (err < 0)
+		goto end;
+
+	err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+	if (err < 0)
+		cmp_connection_destroy(conn);
+end:
+	return err;
+}
+
+static void
+stop_stream(struct snd_efw *efw, struct amdtp_stream *stream)
+{
+	amdtp_stream_stop(stream);
+
+	if (stream == &efw->tx_stream)
+		cmp_connection_break(&efw->out_conn);
+	else
+		cmp_connection_break(&efw->in_conn);
+}
+
+static int
+start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
+	     unsigned int sampling_rate)
+{
+	struct cmp_connection *conn;
+	unsigned int mode, pcm_channels, midi_ports;
+	int err;
+
+	err = snd_efw_get_multiplier_mode(sampling_rate, &mode);
+	if (err < 0)
+		goto end;
+	if (stream == &efw->tx_stream) {
+		conn = &efw->out_conn;
+		pcm_channels = efw->pcm_capture_channels[mode];
+		midi_ports = efw->midi_out_ports;
+	} else {
+		conn = &efw->in_conn;
+		pcm_channels = efw->pcm_playback_channels[mode];
+		midi_ports = efw->midi_in_ports;
+	}
+
+	amdtp_stream_set_parameters(stream, sampling_rate,
+				    pcm_channels, midi_ports);
+
+	/*  establish connection via CMP */
+	err = cmp_connection_establish(conn,
+				amdtp_stream_get_max_payload(stream));
+	if (err < 0)
+		goto end;
+
+	/* start amdtp stream */
+	err = amdtp_stream_start(stream,
+				 conn->resources.channel,
+				 conn->speed);
+	if (err < 0)
+		stop_stream(efw, stream);
+
+	/* wait first callback */
+	if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
+		stop_stream(efw, stream);
+		err = -ETIMEDOUT;
+	}
+end:
+	return err;
+}
+
+static int
+get_roles(struct snd_efw *efw, enum cip_flags *sync_mode,
+	  struct amdtp_stream **master, struct amdtp_stream **slave)
+{
+	enum snd_efw_clock_source clock_source;
+	int err;
+
+	err = snd_efw_command_get_clock_source(efw, &clock_source);
+	if (err < 0)
+		goto end;
+
+	if (clock_source != SND_EFW_CLOCK_SOURCE_SYTMATCH) {
+		*master = &efw->tx_stream;
+		*slave = &efw->rx_stream;
+		*sync_mode = CIP_SYNC_TO_DEVICE;
+	} else {
+		err = -ENOSYS;
+	}
+end:
+	return err;
+}
+
+static int
+check_connection_used_by_others(struct snd_efw *efw, struct amdtp_stream *s)
+{
+	struct cmp_connection *conn;
+	bool used;
+	int err;
+
+	if (s == &efw->tx_stream)
+		conn = &efw->out_conn;
+	else
+		conn = &efw->in_conn;
+
+	err = cmp_connection_check_used(conn, &used);
+	if ((err >= 0) && used && !amdtp_stream_running(s)) {
+		dev_err(&efw->unit->device,
+			"Connection established by others: %cPCR[%d]\n",
+			(conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+			conn->pcr_index);
+		err = -EBUSY;
+	}
+
+	return err;
+}
+
+int snd_efw_stream_init_duplex(struct snd_efw *efw)
+{
+	int err;
+
+	err = init_stream(efw, &efw->tx_stream);
+	if (err < 0)
+		goto end;
+
+	err = init_stream(efw, &efw->rx_stream);
+	if (err < 0)
+		goto end;
+
+	/* set IEC61883 compliant mode */
+	err = snd_efw_command_set_tx_mode(efw, SND_EFW_TRANSPORT_MODE_IEC61883);
+end:
+	return err;
+}
+
+int snd_efw_stream_start_duplex(struct snd_efw *efw,
+				struct amdtp_stream *request,
+				int rate)
+{
+	struct amdtp_stream *master, *slave;
+	enum cip_flags sync_mode;
+	unsigned int curr_rate;
+	bool slave_flag;
+	int err;
+
+	mutex_lock(&efw->mutex);
+
+	err = get_roles(efw, &sync_mode, &master, &slave);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * Considering JACK/FFADO streaming:
+	 * TODO: This can be removed hwdep functionality becomes popular.
+	 */
+	err = check_connection_used_by_others(efw, master);
+	if (err < 0)
+		goto end;
+
+	/* need to touch slave stream */
+	slave_flag = (request == slave) || amdtp_stream_running(slave);
+
+	/* packet queueing error */
+	if (amdtp_streaming_error(slave))
+		stop_stream(efw, slave);
+	if (amdtp_streaming_error(master))
+		stop_stream(efw, master);
+
+	/* stop streams if rate is different */
+	err = snd_efw_command_get_sampling_rate(efw, &curr_rate);
+	if (err < 0)
+		goto end;
+	if (rate == 0)
+		rate = curr_rate;
+	if (rate != curr_rate) {
+		stop_stream(efw, slave);
+		stop_stream(efw, master);
+	}
+
+	/* master should be always running */
+	if (!amdtp_stream_running(master)) {
+		amdtp_stream_set_sync(sync_mode, master, slave);
+
+		err = snd_efw_command_set_sampling_rate(efw, rate);
+		if (err < 0)
+			goto end;
+
+		err = start_stream(efw, master, rate);
+		if (err < 0) {
+			dev_err(&efw->unit->device,
+				"fail to start AMDTP master stream:%d\n", err);
+			goto end;
+		}
+	}
+
+	/* start slave if needed */
+	if (slave_flag && !amdtp_stream_running(slave)) {
+		err = start_stream(efw, slave, rate);
+		if (err < 0) {
+			dev_err(&efw->unit->device,
+				"fail to start AMDTP slave stream:%d\n", err);
+			stop_stream(efw, master);
+		}
+	}
+end:
+	mutex_unlock(&efw->mutex);
+	return err;
+}
+
+int snd_efw_stream_stop_duplex(struct snd_efw *efw)
+{
+	struct amdtp_stream *master, *slave;
+	enum cip_flags sync_mode;
+	unsigned int slave_substreams;
+	int err;
+
+	mutex_lock(&efw->mutex);
+
+	err = get_roles(efw, &sync_mode, &master, &slave);
+	if (err < 0)
+		goto end;
+
+	if (slave == &efw->tx_stream)
+		slave_substreams = efw->capture_substreams;
+	else
+		slave_substreams = efw->playback_substreams;
+
+	if (slave_substreams > 0)
+		goto end;
+
+	stop_stream(efw, slave);
+
+	if ((efw->capture_substreams > 0) || (efw->playback_substreams > 0))
+		goto end;
+
+	stop_stream(efw, master);
+end:
+	mutex_unlock(&efw->mutex);
+	return err;
+}
+
+void snd_efw_stream_update_duplex(struct snd_efw *efw)
+{
+	if ((cmp_connection_update(&efw->out_conn) < 0) ||
+	    (cmp_connection_update(&efw->in_conn) < 0)) {
+		amdtp_stream_pcm_abort(&efw->rx_stream);
+		amdtp_stream_pcm_abort(&efw->tx_stream);
+		mutex_lock(&efw->mutex);
+		stop_stream(efw, &efw->rx_stream);
+		stop_stream(efw, &efw->tx_stream);
+		mutex_unlock(&efw->mutex);
+	} else {
+		amdtp_stream_update(&efw->rx_stream);
+		amdtp_stream_update(&efw->tx_stream);
+	}
+}
+
+void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
+{
+	mutex_lock(&efw->mutex);
+
+	amdtp_stream_pcm_abort(&efw->rx_stream);
+	amdtp_stream_pcm_abort(&efw->tx_stream);
+
+	stop_stream(efw, &efw->tx_stream);
+	cmp_connection_destroy(&efw->out_conn);
+
+	stop_stream(efw, &efw->rx_stream);
+	cmp_connection_destroy(&efw->in_conn);
+
+	mutex_unlock(&efw->mutex);
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 22/39] fireworks: Add proc interface for debugging purpose
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (20 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 21/39] fireworks: Add connection and stream management Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  6:54   ` Takashi Iwai
  2014-02-28  3:27 ` [PATCH 23/39] fireworks: Add MIDI interface Takashi Sakamoto
                   ` (17 subsequent siblings)
  39 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds proc interface to output infomation for debugging.
 - firmware information
 - sampling rate and clock source
 - physical metering (linear value)

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/fireworks/Makefile         |   2 +-
 sound/firewire/fireworks/fireworks.c      |  14 ++-
 sound/firewire/fireworks/fireworks.h      |  11 ++
 sound/firewire/fireworks/fireworks_proc.c | 189 ++++++++++++++++++++++++++++++
 4 files changed, 213 insertions(+), 3 deletions(-)
 create mode 100644 sound/firewire/fireworks/fireworks_proc.c

diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index 1bccb65..52bd15e 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,3 +1,3 @@
 snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
-		      fireworks_stream.o fireworks.o
+		      fireworks_stream.o fireworks_proc.o fireworks.o
 obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 6d7de19..e370204 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -130,6 +130,16 @@ get_hardware_info(struct snd_efw *efw)
 	efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
 	efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
 	efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
+
+	/* hardware metering */
+	efw->phys_out = hwinfo->phys_out;
+	efw->phys_in = hwinfo->phys_in;
+	efw->phys_out_grp_count = hwinfo->phys_out_grp_count;
+	efw->phys_in_grp_count = hwinfo->phys_in_grp_count;
+	memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
+	       sizeof(struct snd_efw_phys_grp) * HWINFO_MAX_CAPS_GROUPS);
+	memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps,
+	       sizeof(struct snd_efw_phys_grp) * HWINFO_MAX_CAPS_GROUPS);
 end:
 	kfree(hwinfo);
 	return err;
@@ -189,6 +199,8 @@ efw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	snd_efw_proc_init(efw);
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;
@@ -211,8 +223,6 @@ static void efw_update(struct fw_unit *unit)
 
 	snd_efw_transaction_bus_reset(efw->unit);
 	snd_efw_stream_update_duplex(efw);
-
-	return;
 }
 
 static void efw_remove(struct fw_unit *unit)
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index c022c71..4d69c0d 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -21,6 +21,7 @@
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
+#include <sound/info.h>
 
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
@@ -65,6 +66,14 @@ struct snd_efw {
 	struct cmp_connection in_conn;
 	unsigned int capture_substreams;
 	unsigned int playback_substreams;
+
+	/* hardware metering parameters */
+	unsigned int phys_out;
+	unsigned int phys_in;
+	unsigned int phys_out_grp_count;
+	unsigned int phys_in_grp_count;
+	struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
+	struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
 };
 
 struct snd_efw_transaction {
@@ -178,6 +187,8 @@ int snd_efw_stream_stop_duplex(struct snd_efw *efw);
 void snd_efw_stream_update_duplex(struct snd_efw *efw);
 void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
 
+void snd_efw_proc_init(struct snd_efw *efw);
+
 #define SND_EFW_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
new file mode 100644
index 0000000..62f758b
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -0,0 +1,189 @@
+/*
+ * fireworks_proc.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./fireworks.h"
+
+static inline const char*
+get_phys_name(struct snd_efw_phys_grp *grp)
+{
+	const char *ch_type[] = {
+		"Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT",
+		"Mirroring", "Headphones", "I2S", "Guitar",
+		"Pirzo Guitar", "Guitar String", "Virtual", "Dummy"
+	};
+
+	if (grp->type < 10)
+		return ch_type[grp->type];
+	else if (grp->type == 0x10000)
+		return ch_type[10];
+	else
+		return ch_type[11];
+}
+
+static void
+proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+	struct snd_efw *efw = entry->private_data;
+	unsigned short i;
+	struct snd_efw_hwinfo *hwinfo;
+
+	hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
+	if (hwinfo == NULL)
+		return;
+
+	if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
+		goto end;
+
+	snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
+	snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
+	snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
+	snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
+	snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
+	snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
+
+	snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
+	snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
+	snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
+
+	snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
+
+	snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
+	snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
+	snd_iprintf(buffer, "supported_clock: 0x%X\n",
+		    hwinfo->supported_clocks);
+
+	snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
+	snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
+
+	snd_iprintf(buffer, "phys in grps: 0x%X\n",
+		    hwinfo->phys_in_grp_count);
+	for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
+		snd_iprintf(buffer,
+			    "phys in grp[0x%d]: type 0x%d, count 0x%d\n",
+			    i, hwinfo->phys_out_grps[i].type,
+			    hwinfo->phys_out_grps[i].count);
+	}
+
+	snd_iprintf(buffer, "phys out grps: 0x%X\n",
+		    hwinfo->phys_out_grp_count);
+	for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
+		snd_iprintf(buffer,
+			    "phys out grps[0x%d]: type 0x%d, count 0x%d\n",
+			    i, hwinfo->phys_out_grps[i].type,
+			    hwinfo->phys_out_grps[i].count);
+	}
+
+	snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
+		    hwinfo->amdtp_rx_pcm_channels);
+	snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
+		    hwinfo->amdtp_tx_pcm_channels);
+	snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
+		    hwinfo->amdtp_rx_pcm_channels_2x);
+	snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
+		    hwinfo->amdtp_tx_pcm_channels_2x);
+	snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
+		    hwinfo->amdtp_rx_pcm_channels_4x);
+	snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
+		    hwinfo->amdtp_tx_pcm_channels_4x);
+
+	snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
+	snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
+
+	snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
+		    hwinfo->mixer_playback_channels);
+	snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
+		    hwinfo->mixer_capture_channels);
+end:
+	kfree(hwinfo);
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
+{
+	struct snd_efw *efw = entry->private_data;
+	enum snd_efw_clock_source clock_source;
+	unsigned int sampling_rate;
+
+	if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
+		return;
+
+	if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
+		return;
+
+	snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
+	snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
+}
+
+/*
+ * NOTE:
+ *  dB = 20 * log10(linear / 0x01000000)
+ *  -144.0 dB when linear is 0
+ */
+static void
+proc_read_phys_meters(struct snd_info_entry *entry,
+		      struct snd_info_buffer *buffer)
+{
+	struct snd_efw *efw = entry->private_data;
+	struct snd_efw_phys_meters *meters;
+	unsigned int g, c, m, max, size;
+	const char *name;
+	u32 *linear;
+	int err;
+
+	size = sizeof(struct snd_efw_phys_meters) +
+	       (efw->phys_in + efw->phys_out) * sizeof(u32);
+	meters = kzalloc(size, GFP_KERNEL);
+	if (meters == NULL)
+		return;
+
+	err = snd_efw_command_get_phys_meters(efw, meters, size);
+	if (err < 0)
+		goto end;
+
+	snd_iprintf(buffer, "Physical Meters:\n");
+
+	m = 0;
+	max = min(efw->phys_out, meters->out_meters);
+	linear = meters->values;
+	snd_iprintf(buffer, " %d Outputs:\n", max);
+	for (g = 0; g < efw->phys_out_grp_count; g++) {
+		name = get_phys_name(&efw->phys_out_grps[g]);
+		for (c = 0; c < efw->phys_out_grps[g].count; c++) {
+			if (m < max)
+				snd_iprintf(buffer, "\t%s [%d]: %d\n",
+					    name, c, linear[m++]);
+		}
+	}
+
+	m = 0;
+	max = min(efw->phys_in, meters->in_meters);
+	linear = meters->values + meters->out_meters;
+	snd_iprintf(buffer, " %d Inputs:\n", max);
+	for (g = 0; g < efw->phys_in_grp_count; g++) {
+		name = get_phys_name(&efw->phys_in_grps[g]);
+		for (c = 0; c < efw->phys_in_grps[g].count; c++)
+			if (m < max)
+				snd_iprintf(buffer, "\t%s [%d]: %d\n",
+					    name, c, linear[m++]);
+	}
+end:
+	kfree(meters);
+}
+
+void snd_efw_proc_init(struct snd_efw *efw)
+{
+	struct snd_info_entry *entry;
+
+	if (!snd_card_proc_new(efw->card, "#firmware", &entry))
+		snd_info_set_text_ops(entry, efw, proc_read_hwinfo);
+	if (!snd_card_proc_new(efw->card, "#clock", &entry))
+		snd_info_set_text_ops(entry, efw, proc_read_clock);
+	if (!snd_card_proc_new(efw->card, "#meters", &entry))
+		snd_info_set_text_ops(entry, efw, proc_read_phys_meters);
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 23/39] fireworks: Add MIDI interface
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (21 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 22/39] fireworks: Add proc interface for debugging purpose Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 24/39] fireworks: Add PCM interface Takashi Sakamoto
                   ` (16 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds a functionality to capture/playback MIDI messages.

When no AMDTP streams are running, this driver starts AMDTP stream for MIDI
stream at current sampling rate.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                    |   1 +
 sound/firewire/fireworks/Makefile         |   3 +-
 sound/firewire/fireworks/fireworks.c      |   6 ++
 sound/firewire/fireworks/fireworks.h      |   3 +
 sound/firewire/fireworks/fireworks_midi.c | 158 ++++++++++++++++++++++++++++++
 5 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/fireworks/fireworks_midi.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 0b85ebd..36a1212 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -64,6 +64,7 @@ config SND_SCS1X
 config SND_FIREWORKS
 	tristate "Echo Fireworks board module support"
 	select SND_FIREWIRE_LIB
+	select SND_RAWMIDI
 	help
 	  Say Y here to include support for FireWire devices based
 	  on Echo Digital Audio Fireworks board:
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index 52bd15e..a2cecc6 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,3 +1,4 @@
 snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
-		      fireworks_stream.o fireworks_proc.o fireworks.o
+		      fireworks_stream.o fireworks_proc.o fireworks_midi.o \
+		      fireworks.o
 obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index e370204..5537773 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -201,6 +201,12 @@ efw_probe(struct fw_unit *unit,
 
 	snd_efw_proc_init(efw);
 
+	if (efw->midi_out_ports || efw->midi_in_ports) {
+		err = snd_efw_create_midi_devices(efw);
+		if (err < 0)
+			goto error;
+	}
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 4d69c0d..0dfed97 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -22,6 +22,7 @@
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/info.h>
+#include <sound/rawmidi.h>
 
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
@@ -189,6 +190,8 @@ void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
 
 void snd_efw_proc_init(struct snd_efw *efw);
 
+int snd_efw_create_midi_devices(struct snd_efw *efw);
+
 #define SND_EFW_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
new file mode 100644
index 0000000..a950fd1
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -0,0 +1,158 @@
+/*
+ * fireworks_midi.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "fireworks.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_efw *efw = substream->rmidi->private_data;
+
+	efw->capture_substreams++;
+	return snd_efw_stream_start_duplex(efw, &efw->tx_stream, 0);
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_efw *efw = substream->rmidi->private_data;
+
+	efw->playback_substreams++;
+	return snd_efw_stream_start_duplex(efw, &efw->rx_stream, 0);
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_efw *efw = substream->rmidi->private_data;
+
+	efw->capture_substreams--;
+	snd_efw_stream_stop_duplex(efw);
+
+	return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_efw *efw = substream->rmidi->private_data;
+
+	efw->playback_substreams--;
+	snd_efw_stream_stop_duplex(efw);
+
+	return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_efw *efw = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&efw->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&efw->tx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&efw->tx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_efw *efw = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&efw->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&efw->rx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&efw->rx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&efw->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+	.open		= midi_capture_open,
+	.close		= midi_capture_close,
+	.trigger	= midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+	.open		= midi_playback_open,
+	.close		= midi_playback_close,
+	.trigger	= midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_efw *efw,
+				     struct snd_rawmidi_str *str)
+{
+	struct snd_rawmidi_substream *subs;
+
+	list_for_each_entry(subs, &str->substreams, list) {
+		snprintf(subs->name, sizeof(subs->name),
+			 "%s MIDI %d", efw->card->shortname, subs->number + 1);
+	}
+}
+
+int snd_efw_create_midi_devices(struct snd_efw *efw)
+{
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_str *str;
+	int err;
+
+	/* check the number of midi stream */
+	if ((efw->midi_in_ports > SND_EFW_MAX_MIDI_IN_PORTS) |
+	    (efw->midi_out_ports > SND_EFW_MAX_MIDI_OUT_PORTS))
+		return -EIO;
+
+	/* create midi ports */
+	err = snd_rawmidi_new(efw->card, efw->card->driver, 0,
+			      efw->midi_out_ports, efw->midi_in_ports,
+			      &rmidi);
+	if (err < 0)
+		return err;
+
+	snprintf(rmidi->name, sizeof(rmidi->name),
+			"%s MIDI", efw->card->shortname);
+	rmidi->private_data = efw;
+
+	if (efw->midi_in_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+					&midi_capture_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+		set_midi_substream_names(efw, str);
+	}
+
+	if (efw->midi_out_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+					&midi_playback_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+		set_midi_substream_names(efw, str);
+
+		/*
+		 * Fireworks ignores MIDI messages in greater than first 8 data
+		 * blocks of an AMDTP packet.
+		 */
+		efw->rx_stream.blocks_for_midi = 8;
+	}
+
+	if ((efw->midi_out_ports > 0) && (efw->midi_in_ports > 0))
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	return 0;
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 24/39] fireworks: Add PCM interface
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (22 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 23/39] fireworks: Add MIDI interface Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 25/39] fireworks: Add hwdep interface Takashi Sakamoto
                   ` (15 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds a functionality to capture/playback PCM samples.

When AMDTP stream is already running for PCM or the source of clock is not
internal, available sampling rate is limited at current one.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                      |   1 +
 sound/firewire/fireworks/Makefile           |   2 +-
 sound/firewire/fireworks/fireworks.c        |   4 +
 sound/firewire/fireworks/fireworks.h        |   4 +
 sound/firewire/fireworks/fireworks_pcm.c    | 468 ++++++++++++++++++++++++++++
 sound/firewire/fireworks/fireworks_stream.c |  33 --
 6 files changed, 478 insertions(+), 34 deletions(-)
 create mode 100644 sound/firewire/fireworks/fireworks_pcm.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 36a1212..fb39f9c 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -65,6 +65,7 @@ config SND_FIREWORKS
 	tristate "Echo Fireworks board module support"
 	select SND_FIREWIRE_LIB
 	select SND_RAWMIDI
+	select SND_PCM
 	help
 	  Say Y here to include support for FireWire devices based
 	  on Echo Digital Audio Fireworks board:
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index a2cecc6..d7ebf83 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,4 +1,4 @@
 snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
 		      fireworks_stream.o fireworks_proc.o fireworks_midi.o \
-		      fireworks.o
+		      fireworks_pcm.o fireworks.o
 obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 5537773..3034744 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -207,6 +207,10 @@ efw_probe(struct fw_unit *unit,
 			goto error;
 	}
 
+	err = snd_efw_create_pcm_devices(efw);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 0dfed97..11d42cc 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -23,6 +23,7 @@
 #include <sound/pcm.h>
 #include <sound/info.h>
 #include <sound/rawmidi.h>
+#include <sound/pcm_params.h>
 
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
@@ -192,6 +193,9 @@ void snd_efw_proc_init(struct snd_efw *efw);
 
 int snd_efw_create_midi_devices(struct snd_efw *efw);
 
+int snd_efw_create_pcm_devices(struct snd_efw *efw);
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
+
 #define SND_EFW_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
new file mode 100644
index 0000000..88c1d53
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -0,0 +1,468 @@
+/*
+ * fireworks_pcm.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2009-2010 Clemens Ladisch
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "./fireworks.h"
+
+/*
+ * NOTE:
+ * Fireworks changes its AMDTP channels for PCM data according to its sampling
+ * rate. There are three modes. Here _XX is either _rx or _tx.
+ *  0:  32.0- 48.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels applied
+ *  1:  88.2- 96.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_2x applied
+ *  2: 176.4-192.0 kHz then snd_efw_hwinfo.amdtp_XX_pcm_channels_4x applied
+ *
+ * The number of PCM channels for analog input and output are always fixed but
+ * the number of PCM channels for digital input and output are differed.
+ *
+ * Additionally, according to "AudioFire Owner's Manual Version 2.2", in some
+ * model, the number of PCM channels for digital input has more restriction
+ * depending on which digital interface is selected.
+ *  - S/PDIF coaxial and optical	: use input 1-2
+ *  - ADAT optical at 32.0-48.0 kHz	: use input 1-8
+ *  - ADAT optical at 88.2-96.0 kHz	: use input 1-4 (S/MUX format)
+ *
+ * The data in AMDTP channels for blank PCM channels are zero.
+ */
+static unsigned int freq_table[] = {
+	/* multiplier mode 0 */
+	[0] = 32000,
+	[1] = 44100,
+	[2] = 48000,
+	/* multiplier mode 1 */
+	[3] = 88200,
+	[4] = 96000,
+	/* multiplier mode 2 */
+	[5] = 176400,
+	[6] = 192000,
+};
+
+static inline unsigned int
+get_multiplier_mode_with_index(unsigned int index)
+{
+	return ((int)index - 1) / 2;
+}
+
+int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(freq_table); i++) {
+		if (freq_table[i] == sampling_rate) {
+			*mode = get_multiplier_mode_with_index(i);
+			return 0;
+		}
+	}
+
+	return -EINVAL;
+}
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params,
+	     struct snd_pcm_hw_rule *rule,
+	     struct snd_efw *efw, unsigned int *channels)
+{
+	struct snd_interval *r =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	const struct snd_interval *c =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval t = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, mode, rate_bit;
+
+	for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+		/* skip unsupported sampling rate */
+		rate_bit = snd_pcm_rate_to_rate_bit(freq_table[i]);
+		if (!(efw->supported_sampling_rate & rate_bit))
+			continue;
+
+		mode = get_multiplier_mode_with_index(i);
+		if (!snd_interval_test(c, channels[mode]))
+			continue;
+
+		t.min = min(t.min, freq_table[i]);
+		t.max = max(t.max, freq_table[i]);
+
+	}
+
+	return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params,
+		 struct snd_pcm_hw_rule *rule,
+		 struct snd_efw *efw, unsigned int *channels)
+{
+	struct snd_interval *c =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	const struct snd_interval *r =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval t = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i, mode, rate_bit;
+
+	for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+		/* skip unsupported sampling rate */
+		rate_bit = snd_pcm_rate_to_rate_bit(freq_table[i]);
+		if (!(efw->supported_sampling_rate & rate_bit))
+			continue;
+
+		mode = get_multiplier_mode_with_index(i);
+		if (!snd_interval_test(r, freq_table[i]))
+			continue;
+
+		t.min = min(t.min, channels[mode]);
+		t.max = max(t.max, channels[mode]);
+
+	}
+
+	return snd_interval_refine(c, &t);
+}
+
+static inline int
+hw_rule_capture_rate(struct snd_pcm_hw_params *params,
+		     struct snd_pcm_hw_rule *rule)
+{
+	struct snd_efw *efw = rule->private;
+	return hw_rule_rate(params, rule, efw,
+				efw->pcm_capture_channels);
+}
+
+static inline int
+hw_rule_playback_rate(struct snd_pcm_hw_params *params,
+		      struct snd_pcm_hw_rule *rule)
+{
+	struct snd_efw *efw = rule->private;
+	return hw_rule_rate(params, rule, efw,
+				efw->pcm_playback_channels);
+}
+
+static inline int
+hw_rule_capture_channels(struct snd_pcm_hw_params *params,
+			 struct snd_pcm_hw_rule *rule)
+{
+	struct snd_efw *efw = rule->private;
+	return hw_rule_channels(params, rule, efw,
+				efw->pcm_capture_channels);
+}
+
+static inline int
+hw_rule_playback_channels(struct snd_pcm_hw_params *params,
+			  struct snd_pcm_hw_rule *rule)
+{
+	struct snd_efw *efw = rule->private;
+	return hw_rule_channels(params, rule, efw,
+				efw->pcm_playback_channels);
+}
+
+static int
+pcm_init_hw_params(struct snd_efw *efw,
+		   struct snd_pcm_substream *substream)
+{
+	unsigned int i, *pcm_channels, rate_bit, mode;
+	int err;
+
+	struct snd_pcm_hardware hardware = {
+		.info = SNDRV_PCM_INFO_BATCH |
+			SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_JOINT_DUPLEX |
+			SNDRV_PCM_INFO_MMAP |
+			SNDRV_PCM_INFO_MMAP_VALID,
+		.rates = efw->supported_sampling_rate,
+		/* set up later */
+		.rate_min = UINT_MAX,
+		.rate_max = 0,
+		/* set up later */
+		.channels_min = UINT_MAX,
+		.channels_max = 0,
+		.buffer_bytes_max = 4 * 34 * 2048 * 2,
+		.period_bytes_min = 1,
+		.period_bytes_max = 4 * 34 * 2048,
+		.periods_min = 2,
+		.periods_max = UINT_MAX,
+	};
+
+	substream->runtime->hw = hardware;
+
+	/* add rule between channels and sampling rate */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_CHANNELS,
+				    hw_rule_capture_channels, efw,
+				    SNDRV_PCM_HW_PARAM_RATE, -1);
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_RATE,
+				    hw_rule_capture_rate, efw,
+				    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+		pcm_channels = efw->pcm_capture_channels;
+	} else {
+		substream->runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_CHANNELS,
+				    hw_rule_playback_channels, efw,
+				    SNDRV_PCM_HW_PARAM_RATE, -1);
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_RATE,
+				    hw_rule_playback_rate, efw,
+				    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+		pcm_channels = efw->pcm_playback_channels;
+	}
+
+	/* limitation for min/max sampling rate */
+	snd_pcm_limit_hw_rates(substream->runtime);
+
+	/* limitation for the number of channels */
+	for (i = 0; i < ARRAY_SIZE(freq_table); i++) {
+		/* skip unsupported sampling rate */
+		rate_bit = snd_pcm_rate_to_rate_bit(freq_table[i]);
+		if (!(efw->supported_sampling_rate & rate_bit))
+			continue;
+
+		mode = get_multiplier_mode_with_index(i);
+		if (pcm_channels[mode] == 0)
+			continue;
+		substream->runtime->hw.channels_min =
+				min(substream->runtime->hw.channels_min,
+				    pcm_channels[mode]);
+		substream->runtime->hw.channels_max =
+				max(substream->runtime->hw.channels_max,
+				    pcm_channels[mode]);
+	}
+
+	/* AM824 in IEC 61883-6 can deliver 24bit data */
+	err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * AMDTP functionality in firewire-lib require periods to be aligned to
+	 * 16 bit, or 24bit inner 32bit.
+	 */
+	err = snd_pcm_hw_constraint_step(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	if (err < 0)
+		goto end;
+	err = snd_pcm_hw_constraint_step(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * Currently INTERRUPT_INTERVAL in amdtp.c is 16.
+	 * So snd_pcm_period_elapsed() can be called every 2m sec.
+	 */
+	err = snd_pcm_hw_constraint_minmax(substream->runtime,
+					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+					   2000, UINT_MAX);
+end:
+	return err;
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_efw *efw = substream->private_data;
+	int sampling_rate;
+	unsigned int clock_source;
+	int err;
+
+	err = pcm_init_hw_params(efw, substream);
+	if (err < 0)
+		goto end;
+
+	err = snd_efw_command_get_clock_source(efw, &clock_source);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * When source of clock is not internal or any PCM streams are running,
+	 * available sampling rate is limited at current sampling rate.
+	 */
+	if ((clock_source != SND_EFW_CLOCK_SOURCE_INTERNAL) ||
+	    amdtp_stream_pcm_running(&efw->tx_stream) ||
+	    amdtp_stream_pcm_running(&efw->rx_stream)) {
+		err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
+		if (err < 0)
+			goto end;
+		substream->runtime->hw.rate_min = sampling_rate;
+		substream->runtime->hw.rate_max = sampling_rate;
+	}
+
+	snd_pcm_set_sync(substream);
+end:
+	return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+				 struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_efw *efw = substream->private_data;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		efw->capture_substreams++;
+	amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
+}
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+				  struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_efw *efw = substream->private_data;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		efw->playback_substreams++;
+	amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_efw *efw = substream->private_data;
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		efw->capture_substreams--;
+
+	snd_efw_stream_stop_duplex(efw);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_efw *efw = substream->private_data;
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		efw->playback_substreams--;
+
+	snd_efw_stream_stop_duplex(efw);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_efw *efw = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	err = snd_efw_stream_start_duplex(efw, &efw->tx_stream, runtime->rate);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&efw->tx_stream);
+
+	return err;
+}
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_efw *efw = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	err = snd_efw_stream_start_duplex(efw, &efw->rx_stream, runtime->rate);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&efw->rx_stream);
+
+	return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_efw *efw = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&efw->tx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&efw->tx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_efw *efw = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&efw->rx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&efw->rx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+	struct snd_efw *efw = sbstrm->private_data;
+	return amdtp_stream_pcm_pointer(&efw->tx_stream);
+}
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+	struct snd_efw *efw = sbstrm->private_data;
+	return amdtp_stream_pcm_pointer(&efw->rx_stream);
+}
+
+static const struct snd_pcm_ops pcm_capture_ops = {
+	.open		= pcm_open,
+	.close		= pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= pcm_capture_hw_params,
+	.hw_free	= pcm_capture_hw_free,
+	.prepare	= pcm_capture_prepare,
+	.trigger	= pcm_capture_trigger,
+	.pointer	= pcm_capture_pointer,
+	.page		= snd_pcm_lib_get_vmalloc_page,
+};
+
+static const struct snd_pcm_ops pcm_playback_ops = {
+	.open		= pcm_open,
+	.close		= pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= pcm_playback_hw_params,
+	.hw_free	= pcm_playback_hw_free,
+	.prepare	= pcm_playback_prepare,
+	.trigger	= pcm_playback_trigger,
+	.pointer	= pcm_playback_pointer,
+	.page		= snd_pcm_lib_get_vmalloc_page,
+	.mmap		= snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_efw_create_pcm_devices(struct snd_efw *efw)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(efw->card, efw->card->driver, 0, 1, 1, &pcm);
+	if (err < 0)
+		goto end;
+
+	pcm->private_data = efw;
+	snprintf(pcm->name, sizeof(pcm->name), "%s PCM", efw->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+end:
+	return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index f41aee4..7ff4406 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -9,39 +9,6 @@
 
 #define CALLBACK_TIMEOUT	100
 
-static unsigned int freq_table[] = {
-	/* multiplier mode 0 */
-	[0] = 32000,
-	[1] = 44100,
-	[2] = 48000,
-	/* multiplier mode 1 */
-	[3] = 88200,
-	[4] = 96000,
-	/* multiplier mode 2 */
-	[5] = 176400,
-	[6] = 192000,
-};
-
-static inline unsigned int
-get_multiplier_mode_with_index(unsigned int index)
-{
-	return ((int)index - 1) / 2;
-}
-
-int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode)
-{
-	unsigned int i;
-
-	for (i = 0; i < sizeof(freq_table); i++) {
-		if (freq_table[i] == sampling_rate) {
-			*mode = get_multiplier_mode_with_index(i);
-			return 0;
-		}
-	}
-
-	return -EINVAL;
-}
-
 static int
 init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
 {
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 25/39] fireworks: Add hwdep interface
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (23 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 24/39] fireworks: Add PCM interface Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 26/39] fireworks: Add command/response functionality into " Takashi Sakamoto
                   ` (14 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This interface is designed for mixer/control application. To use hwdep
interface, the application can get information about firewire node, can
lock/unlock kernel streaming and can get notification at starting/stopping
kernel streaming.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/uapi/sound/asound.h                 |   3 +-
 include/uapi/sound/firewire.h               |   3 +-
 sound/firewire/Kconfig                      |   1 +
 sound/firewire/fireworks/Makefile           |   2 +-
 sound/firewire/fireworks/fireworks.c        |   5 +
 sound/firewire/fireworks/fireworks.h        |  12 ++
 sound/firewire/fireworks/fireworks_hwdep.c  | 197 ++++++++++++++++++++++++++++
 sound/firewire/fireworks/fireworks_midi.c   |  25 +++-
 sound/firewire/fireworks/fireworks_pcm.c    |  15 ++-
 sound/firewire/fireworks/fireworks_stream.c |  39 ++++++
 10 files changed, 294 insertions(+), 8 deletions(-)
 create mode 100644 sound/firewire/fireworks/fireworks_hwdep.c

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 9fc6219..5dfbfdf 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -94,9 +94,10 @@ enum {
 	SNDRV_HWDEP_IFACE_HDA,		/* HD-audio */
 	SNDRV_HWDEP_IFACE_USB_STREAM,	/* direct access to usb stream */
 	SNDRV_HWDEP_IFACE_FW_DICE,	/* TC DICE FireWire device */
+	SNDRV_HWDEP_IFACE_FW_FIREWORKS,	/* Echo Audio Fireworks based device */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_DICE
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREWORKS
 };
 
 struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index 59f5961..fc9afb2 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -34,7 +34,8 @@ union snd_firewire_event {
 #define SNDRV_FIREWIRE_IOCTL_UNLOCK    _IO('H', 0xfa)
 
 #define SNDRV_FIREWIRE_TYPE_DICE	1
-/* Fireworks, AV/C, RME, MOTU, ... */
+#define SNDRV_FIREWIRE_TYPE_FIREWORKS	2
+/* AV/C, RME, MOTU, ... */
 
 struct snd_firewire_get_info {
 	unsigned int type; /* SNDRV_FIREWIRE_TYPE_xxx */
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index fb39f9c..875af02 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -66,6 +66,7 @@ config SND_FIREWORKS
 	select SND_FIREWIRE_LIB
 	select SND_RAWMIDI
 	select SND_PCM
+	select SND_HWDEP
 	help
 	  Say Y here to include support for FireWire devices based
 	  on Echo Digital Audio Fireworks board:
diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
index d7ebf83..0c74408 100644
--- a/sound/firewire/fireworks/Makefile
+++ b/sound/firewire/fireworks/Makefile
@@ -1,4 +1,4 @@
 snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
 		      fireworks_stream.o fireworks_proc.o fireworks_midi.o \
-		      fireworks_pcm.o fireworks.o
+		      fireworks_pcm.o fireworks_hwdep.o fireworks.o
 obj-m += snd-fireworks.o
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 3034744..38986c0 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -190,6 +190,7 @@ efw_probe(struct fw_unit *unit,
 	efw->card_index = -1;
 	mutex_init(&efw->mutex);
 	spin_lock_init(&efw->lock);
+	init_waitqueue_head(&efw->hwdep_wait);
 
 	err = get_hardware_info(efw);
 	if (err < 0)
@@ -211,6 +212,10 @@ efw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	err = snd_efw_create_hwdep_device(efw);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(card);
 	if (err < 0)
 		goto error;
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 11d42cc..4ab4887 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -24,6 +24,8 @@
 #include <sound/info.h>
 #include <sound/rawmidi.h>
 #include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
 
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
@@ -76,6 +78,11 @@ struct snd_efw {
 	unsigned int phys_in_grp_count;
 	struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
 	struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
+
+	/* for uapi */
+	int dev_lock_count;
+	bool dev_lock_changed;
+	wait_queue_head_t hwdep_wait;
 };
 
 struct snd_efw_transaction {
@@ -188,6 +195,9 @@ int snd_efw_stream_start_duplex(struct snd_efw *efw,
 int snd_efw_stream_stop_duplex(struct snd_efw *efw);
 void snd_efw_stream_update_duplex(struct snd_efw *efw);
 void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
+void snd_efw_stream_lock_changed(struct snd_efw *efw);
+int snd_efw_stream_lock_try(struct snd_efw *efw);
+void snd_efw_stream_lock_release(struct snd_efw *efw);
 
 void snd_efw_proc_init(struct snd_efw *efw);
 
@@ -196,6 +206,8 @@ int snd_efw_create_midi_devices(struct snd_efw *efw);
 int snd_efw_create_pcm_devices(struct snd_efw *efw);
 int snd_efw_get_multiplier_mode(unsigned int sampling_rate, unsigned int *mode);
 
+int snd_efw_create_hwdep_device(struct snd_efw *efw);
+
 #define SND_EFW_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
new file mode 100644
index 0000000..4c28b6c
--- /dev/null
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -0,0 +1,197 @@
+/*
+ * fireworks_hwdep.c - a part of driver for Fireworks based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes have three functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ */
+
+#include "fireworks.h"
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
+	   loff_t *offset)
+{
+	struct snd_efw *efw = hwdep->private_data;
+	DEFINE_WAIT(wait);
+	union snd_firewire_event event;
+
+	spin_lock_irq(&efw->lock);
+
+	while (!efw->dev_lock_changed) {
+		prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+		spin_unlock_irq(&efw->lock);
+		schedule();
+		finish_wait(&efw->hwdep_wait, &wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		spin_lock_irq(&efw->lock);
+	}
+
+	memset(&event, 0, sizeof(event));
+	if (efw->dev_lock_changed) {
+		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+		event.lock_status.status = (efw->dev_lock_count > 0);
+		efw->dev_lock_changed = false;
+
+		count = min_t(long, count, sizeof(event.lock_status));
+	}
+
+	spin_unlock_irq(&efw->lock);
+
+	if (copy_to_user(buf, &event, count))
+		return -EFAULT;
+
+	return count;
+}
+
+static unsigned int
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+	struct snd_efw *efw = hwdep->private_data;
+	unsigned int events;
+
+	poll_wait(file, &efw->hwdep_wait, wait);
+
+	spin_lock_irq(&efw->lock);
+	if (efw->dev_lock_changed)
+		events = POLLIN | POLLRDNORM;
+	else
+		events = 0;
+	spin_unlock_irq(&efw->lock);
+
+	return events;
+}
+
+static int
+hwdep_get_info(struct snd_efw *efw, void __user *arg)
+{
+	struct fw_device *dev = fw_parent_device(efw->unit);
+	struct snd_firewire_get_info info;
+
+	memset(&info, 0, sizeof(info));
+	info.type = SNDRV_FIREWIRE_TYPE_FIREWORKS;
+	info.card = dev->card->index;
+	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+	strlcpy(info.device_name, dev_name(&dev->device),
+		sizeof(info.device_name));
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int
+hwdep_lock(struct snd_efw *efw)
+{
+	int err;
+
+	spin_lock_irq(&efw->lock);
+
+	if (efw->dev_lock_count == 0) {
+		efw->dev_lock_count = -1;
+		err = 0;
+	} else
+		err = -EBUSY;
+
+	spin_unlock_irq(&efw->lock);
+
+	return err;
+}
+
+static int
+hwdep_unlock(struct snd_efw *efw)
+{
+	int err;
+
+	spin_lock_irq(&efw->lock);
+
+	if (efw->dev_lock_count == -1) {
+		efw->dev_lock_count = 0;
+		err = 0;
+	} else
+		err = -EBADFD;
+
+	spin_unlock_irq(&efw->lock);
+
+	return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+	struct snd_efw *efw = hwdep->private_data;
+
+	spin_lock_irq(&efw->lock);
+	if (efw->dev_lock_count == -1)
+		efw->dev_lock_count = 0;
+	spin_unlock_irq(&efw->lock);
+
+	return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+	    unsigned int cmd, unsigned long arg)
+{
+	struct snd_efw *efw = hwdep->private_data;
+
+	switch (cmd) {
+	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+		return hwdep_get_info(efw, (void __user *)arg);
+	case SNDRV_FIREWIRE_IOCTL_LOCK:
+		return hwdep_lock(efw);
+	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+		return hwdep_unlock(efw);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+		   unsigned int cmd, unsigned long arg)
+{
+	return hwdep_ioctl(hwdep, file, cmd,
+			   (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+	.read		= hwdep_read,
+	.release	= hwdep_release,
+	.poll		= hwdep_poll,
+	.ioctl		= hwdep_ioctl,
+	.ioctl_compat	= hwdep_compat_ioctl,
+};
+
+int snd_efw_create_hwdep_device(struct snd_efw *efw)
+{
+	struct snd_hwdep *hwdep;
+	int err;
+
+	err = snd_hwdep_new(efw->card, "Fireworks", 0, &hwdep);
+	if (err < 0)
+		goto end;
+	strcpy(hwdep->name, "Fireworks");
+	hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREWORKS;
+	hwdep->ops = hwdep_ops;
+	hwdep->private_data = efw;
+	hwdep->exclusive = true;
+end:
+	return err;
+}
+
diff --git a/sound/firewire/fireworks/fireworks_midi.c b/sound/firewire/fireworks/fireworks_midi.c
index a950fd1..2924599 100644
--- a/sound/firewire/fireworks/fireworks_midi.c
+++ b/sound/firewire/fireworks/fireworks_midi.c
@@ -11,17 +11,36 @@
 static int midi_capture_open(struct snd_rawmidi_substream *substream)
 {
 	struct snd_efw *efw = substream->rmidi->private_data;
+	int err;
+
+	err = snd_efw_stream_lock_try(efw);
+	if (err < 0)
+		goto end;
 
 	efw->capture_substreams++;
-	return snd_efw_stream_start_duplex(efw, &efw->tx_stream, 0);
+	err = snd_efw_stream_start_duplex(efw, &efw->tx_stream, 0);
+	if (err < 0)
+		snd_efw_stream_lock_release(efw);
+
+end:
+	return err;
 }
 
 static int midi_playback_open(struct snd_rawmidi_substream *substream)
 {
 	struct snd_efw *efw = substream->rmidi->private_data;
+	int err;
+
+	err = snd_efw_stream_lock_try(efw);
+	if (err < 0)
+		goto end;
 
 	efw->playback_substreams++;
-	return snd_efw_stream_start_duplex(efw, &efw->rx_stream, 0);
+	err = snd_efw_stream_start_duplex(efw, &efw->rx_stream, 0);
+	if (err < 0)
+		snd_efw_stream_lock_release(efw);
+end:
+	return err;
 }
 
 static int midi_capture_close(struct snd_rawmidi_substream *substream)
@@ -31,6 +50,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 	efw->capture_substreams--;
 	snd_efw_stream_stop_duplex(efw);
 
+	snd_efw_stream_lock_release(efw);
 	return 0;
 }
 
@@ -41,6 +61,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 	efw->playback_substreams--;
 	snd_efw_stream_stop_duplex(efw);
 
+	snd_efw_stream_lock_release(efw);
 	return 0;
 }
 
diff --git a/sound/firewire/fireworks/fireworks_pcm.c b/sound/firewire/fireworks/fireworks_pcm.c
index 88c1d53..5ea33b9 100644
--- a/sound/firewire/fireworks/fireworks_pcm.c
+++ b/sound/firewire/fireworks/fireworks_pcm.c
@@ -273,13 +273,17 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	unsigned int clock_source;
 	int err;
 
-	err = pcm_init_hw_params(efw, substream);
+	err = snd_efw_stream_lock_try(efw);
 	if (err < 0)
 		goto end;
 
+	err = pcm_init_hw_params(efw, substream);
+	if (err < 0)
+		goto err_locked;
+
 	err = snd_efw_command_get_clock_source(efw, &clock_source);
 	if (err < 0)
-		goto end;
+		goto err_locked;
 
 	/*
 	 * When source of clock is not internal or any PCM streams are running,
@@ -290,7 +294,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	    amdtp_stream_pcm_running(&efw->rx_stream)) {
 		err = snd_efw_command_get_sampling_rate(efw, &sampling_rate);
 		if (err < 0)
-			goto end;
+			goto err_locked;
 		substream->runtime->hw.rate_min = sampling_rate;
 		substream->runtime->hw.rate_max = sampling_rate;
 	}
@@ -298,10 +302,15 @@ static int pcm_open(struct snd_pcm_substream *substream)
 	snd_pcm_set_sync(substream);
 end:
 	return err;
+err_locked:
+	snd_efw_stream_lock_release(efw);
+	return err;
 }
 
 static int pcm_close(struct snd_pcm_substream *substream)
 {
+	struct snd_efw *efw = substream->private_data;
+	snd_efw_stream_lock_release(efw);
 	return 0;
 }
 
diff --git a/sound/firewire/fireworks/fireworks_stream.c b/sound/firewire/fireworks/fireworks_stream.c
index 7ff4406..b07ea1e 100644
--- a/sound/firewire/fireworks/fireworks_stream.c
+++ b/sound/firewire/fireworks/fireworks_stream.c
@@ -296,3 +296,42 @@ void snd_efw_stream_destroy_duplex(struct snd_efw *efw)
 
 	mutex_unlock(&efw->mutex);
 }
+
+void snd_efw_stream_lock_changed(struct snd_efw *efw)
+{
+	efw->dev_lock_changed = true;
+	wake_up(&efw->hwdep_wait);
+}
+
+int snd_efw_stream_lock_try(struct snd_efw *efw)
+{
+	int err;
+
+	spin_lock_irq(&efw->lock);
+
+	/* user land lock this */
+	if (efw->dev_lock_count < 0) {
+		err = -EBUSY;
+		goto end;
+	}
+
+	/* this is the first time */
+	if (efw->dev_lock_count++ == 0)
+		snd_efw_stream_lock_changed(efw);
+	err = 0;
+end:
+	spin_unlock_irq(&efw->lock);
+	return err;
+}
+
+void snd_efw_stream_lock_release(struct snd_efw *efw)
+{
+	spin_lock_irq(&efw->lock);
+
+	if (WARN_ON(efw->dev_lock_count <= 0))
+		goto end;
+	if (--efw->dev_lock_count == 0)
+		snd_efw_stream_lock_changed(efw);
+end:
+	spin_unlock_irq(&efw->lock);
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 26/39] fireworks: Add command/response functionality into hwdep interface
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (24 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 25/39] fireworks: Add hwdep interface Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  6:58   ` Takashi Iwai
  2014-02-28  3:27 ` [PATCH 27/39] bebob: Add skelton for BeBoB based devices Takashi Sakamoto
                   ` (13 subsequent siblings)
  39 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds two functionality for hwdep interface, adds two parameters for
this driver, add a node for proc interface.

To receive responses from devices, this driver already allocate own callback
into private address area in host controller. This means no one can allocate
its own callback to the address. So this driver must give a way for user
applications to receive responses.

This commit adds a functionality to receive responses via hwdep interface. The
application can receive responses to read from this interface. To achieve this,
this commit adds a buffer to queue responses. The default size of this buffer is
1024 bytes. This size can be changed to give preferrable size to
'resp_buf_size' parameter for this driver. The application should notice rest
of space in this buffer because this driver don't push responses when this
buffer has no space.

Additionaly, this commit adds a functionality to transmit commands via hwdep
interface. The application can transmit commands to write into this interface.
I note that the application can transmit one command at once, but can receive
as many responses as possible untill the user-buffer is full.

When using these interfaces, the application must keep maximum number of
sequence number in command within the number in firewire.h because this driver
uses this number to distinguish the response is against the command by the
application or this driver.

Usually responses against commands which the application transmits are pushed
into this buffer. But to enable 'resp_buf_debug' parameter for this driver, all
responses are pushed into the buffer. When using this mode, I reccomend to
expand the size of buffer.

Finally this commit adds a new node into proc interface to output status of the
buffer.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/uapi/sound/firewire.h                    |  18 +++
 sound/firewire/fireworks/fireworks.c             |  17 +++
 sound/firewire/fireworks/fireworks.h             |  22 +--
 sound/firewire/fireworks/fireworks_command.c     |   6 +-
 sound/firewire/fireworks/fireworks_hwdep.c       | 129 ++++++++++++++---
 sound/firewire/fireworks/fireworks_proc.c        |  19 +++
 sound/firewire/fireworks/fireworks_transaction.c | 176 ++++++++++++++++++++---
 7 files changed, 340 insertions(+), 47 deletions(-)

diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index fc9afb2..7f4c419 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -7,6 +7,7 @@
 
 #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS	0x000010cc
 #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION	0xd1ce004e
+#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE	0x4e617475
 
 struct snd_firewire_event_common {
 	unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -22,10 +23,27 @@ struct snd_firewire_event_dice_notification {
 	unsigned int notification; /* DICE-specific bits */
 };
 
+#define SND_EFW_TRANSACTION_SEQNUM_MAX	((uint32_t)(BIT(28) - 1))
+/* each field should be in big endian */
+struct snd_efw_transaction {
+	uint32_t length;
+	uint32_t version;
+	uint32_t seqnum;
+	uint32_t category;
+	uint32_t command;
+	uint32_t status;
+	uint32_t params[0];
+};
+struct snd_firewire_event_efw_response {
+	unsigned int type;
+	uint32_t response[0];	/* some responses */
+};
+
 union snd_firewire_event {
 	struct snd_firewire_event_common            common;
 	struct snd_firewire_event_lock_status       lock_status;
 	struct snd_firewire_event_dice_notification dice_notification;
+	struct snd_firewire_event_efw_response      efw_response;
 };
 
 
diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
index 38986c0..7846285 100644
--- a/sound/firewire/fireworks/fireworks.c
+++ b/sound/firewire/fireworks/fireworks.c
@@ -24,6 +24,8 @@ MODULE_LICENSE("GPL v2");
 static int index[SNDRV_CARDS]	= SNDRV_DEFAULT_IDX;
 static char *id[SNDRV_CARDS]	= SNDRV_DEFAULT_STR;
 static bool enable[SNDRV_CARDS]	= SNDRV_DEFAULT_ENABLE_PNP;
+unsigned int resp_buf_size	= 1024;
+bool resp_buf_debug		= false;
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "card index");
@@ -31,6 +33,10 @@ module_param_array(id, charp, NULL, 0444);
 MODULE_PARM_DESC(id, "ID string");
 module_param_array(enable, bool, NULL, 0444);
 MODULE_PARM_DESC(enable, "enable Fireworks sound card");
+module_param(resp_buf_size, uint, 0444);
+MODULE_PARM_DESC(resp_buf_size, "response buffer size (default 1024)");
+module_param(resp_buf_debug, bool, 0444);
+MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
 
 static DEFINE_MUTEX(devices_mutex);
 static unsigned int devices_used;
@@ -165,6 +171,7 @@ efw_probe(struct fw_unit *unit,
 {
 	struct snd_card *card;
 	struct snd_efw *efw;
+	void *resp_buf;
 	int card_index, err;
 
 	mutex_lock(&devices_mutex);
@@ -178,6 +185,13 @@ efw_probe(struct fw_unit *unit,
 		goto end;
 	}
 
+	/* prepare response buffer */
+	resp_buf = kzalloc(resp_buf_size, GFP_KERNEL);
+	if (resp_buf == NULL) {
+		err = -ENOMEM;
+		goto end;
+	}
+
 	err = snd_card_new(&unit->device, index[card_index], id[card_index],
 			   THIS_MODULE, sizeof(struct snd_efw), &card);
 	if (err < 0)
@@ -191,6 +205,7 @@ efw_probe(struct fw_unit *unit,
 	mutex_init(&efw->mutex);
 	spin_lock_init(&efw->lock);
 	init_waitqueue_head(&efw->hwdep_wait);
+	efw->resp_buf = efw->pull_ptr = efw->push_ptr = resp_buf;
 
 	err = get_hardware_info(efw);
 	if (err < 0)
@@ -212,6 +227,7 @@ efw_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	snd_efw_transaction_add_instance(efw);
 	err = snd_efw_create_hwdep_device(efw);
 	if (err < 0)
 		goto error;
@@ -245,6 +261,7 @@ static void efw_remove(struct fw_unit *unit)
 	struct snd_efw *efw = dev_get_drvdata(&unit->device);
 
 	snd_efw_stream_destroy_duplex(efw);
+	snd_efw_transaction_remove_instance(efw);
 
 	snd_card_disconnect(efw->card);
 	snd_card_free_when_closed(efw->card);
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 4ab4887..283e316 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -40,6 +40,9 @@
 #define HWINFO_NAME_SIZE_BYTES		32
 #define HWINFO_MAX_CAPS_GROUPS		8
 
+extern unsigned int resp_buf_size;
+extern bool resp_buf_debug;
+
 struct snd_efw_phys_grp {
 	u8 type;	/* see enum snd_efw_grp_type */
 	u8 count;
@@ -83,23 +86,24 @@ struct snd_efw {
 	int dev_lock_count;
 	bool dev_lock_changed;
 	wait_queue_head_t hwdep_wait;
-};
 
-struct snd_efw_transaction {
-	u32 length;
-	u32 version;
-	u32 seqnum;
-	u32 category;
-	u32 command;
-	u32 status;
-	u32 params[0];
+	/* response queue */
+	u8 *resp_buf;
+	u8 *pull_ptr;
+	u8 *push_ptr;
+	unsigned int resp_queues;
 };
+
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+			    const void *cmd, unsigned int size);
 int snd_efw_transaction_run(struct fw_unit *unit,
 			    const void *cmd, unsigned int cmd_size,
 			    void *resp, unsigned int resp_size, u32 seqnum);
 int snd_efw_transaction_register(void);
 void snd_efw_transaction_unregister(void);
 void snd_efw_transaction_bus_reset(struct fw_unit *unit);
+void snd_efw_transaction_add_instance(struct snd_efw *efw);
+void snd_efw_transaction_remove_instance(struct snd_efw *efw);
 
 struct snd_efw_hwinfo {
 	u32 flags;
diff --git a/sound/firewire/fireworks/fireworks_command.c b/sound/firewire/fireworks/fireworks_command.c
index 9b19e3e..92f3325 100644
--- a/sound/firewire/fireworks/fireworks_command.c
+++ b/sound/firewire/fireworks/fireworks_command.c
@@ -22,6 +22,7 @@
  * Information commands. But this module don't use them.
  */
 
+#define EFW_TRANSACTION_SEQNUM_MIN	(SND_EFW_TRANSACTION_SEQNUM_MAX + 1)
 #define EFW_TRANSACTION_SEQNUM_MAX	((u32)~0)
 
 /* for clock source and sampling rate */
@@ -120,8 +121,9 @@ efw_transaction(struct snd_efw *efw, unsigned int category,
 
 	/* to keep consistency of sequence number */
 	spin_lock(&efw->lock);
-	if (efw->seqnum + 1 >= EFW_TRANSACTION_SEQNUM_MAX)
-		efw->seqnum = 0;
+	if ((efw->seqnum < EFW_TRANSACTION_SEQNUM_MIN) ||
+	    (efw->seqnum >= EFW_TRANSACTION_SEQNUM_MAX - 2))
+		efw->seqnum = EFW_TRANSACTION_SEQNUM_MIN;
 	else
 		efw->seqnum += 2;
 	seqnum = efw->seqnum;
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
index 4c28b6c..19a470a 100644
--- a/sound/firewire/fireworks/fireworks_hwdep.c
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -7,26 +7,100 @@
  */
 
 /*
- * This codes have three functionalities.
+ * This codes have five functionalities.
  *
  * 1.get information about firewire node
  * 2.get notification about starting/stopping stream
  * 3.lock/unlock streaming
+ * 4.transmit command of EFW transaction
+ * 5.receive response of EFW transaction
+ *
  */
 
 #include "fireworks.h"
 
 static long
-hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
+hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
+		    loff_t *offset)
+{
+	unsigned int length, till_end, type;
+	struct snd_efw_transaction *t;
+	long count = 0;
+
+	if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
+		return -ENOSPC;
+
+	/* data type is SNDRV_FIREWIRE_EVENT_EFW_RESPONSE */
+	type = SNDRV_FIREWIRE_EVENT_EFW_RESPONSE;
+	if (copy_to_user(buf, &type, sizeof(type)))
+		return -EFAULT;
+	remained -= sizeof(type);
+	buf += sizeof(type);
+
+	/* write into buffer as many responses as possible */
+	while (efw->resp_queues > 0) {
+		t = (struct snd_efw_transaction *)(efw->pull_ptr);
+		length = be32_to_cpu(t->length) * sizeof(u32);
+
+		/* confirm enough space for this response */
+		if (remained < length)
+			break;
+
+		/* copy from ring buffer to user buffer */
+		while (length > 0) {
+			till_end = resp_buf_size -
+				(unsigned int)(efw->pull_ptr - efw->resp_buf);
+			till_end = min_t(unsigned int, length, till_end);
+
+			if (copy_to_user(buf, efw->pull_ptr, till_end))
+				return -EFAULT;
+
+			efw->pull_ptr += till_end;
+			if (efw->pull_ptr >= efw->resp_buf + resp_buf_size)
+				efw->pull_ptr = efw->resp_buf;
+
+			length -= till_end;
+			buf += till_end;
+			count += till_end;
+			remained -= till_end;
+		}
+
+		efw->resp_queues--;
+	}
+
+	return count;
+}
+
+static long
+hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
+		  loff_t *offset)
+{
+	union snd_firewire_event event;
+
+	memset(&event, 0, sizeof(event));
+
+	event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+	event.lock_status.status = (efw->dev_lock_count > 0);
+	efw->dev_lock_changed = false;
+
+	count = min_t(long, count, sizeof(event.lock_status));
+
+	if (copy_to_user(buf, &event, count))
+		return -EFAULT;
+
+	return count;
+}
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
 	   loff_t *offset)
 {
 	struct snd_efw *efw = hwdep->private_data;
 	DEFINE_WAIT(wait);
-	union snd_firewire_event event;
 
 	spin_lock_irq(&efw->lock);
 
-	while (!efw->dev_lock_changed) {
+	while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) {
 		prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
 		spin_unlock_irq(&efw->lock);
 		schedule();
@@ -36,20 +110,42 @@ hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
 		spin_lock_irq(&efw->lock);
 	}
 
-	memset(&event, 0, sizeof(event));
-	if (efw->dev_lock_changed) {
-		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
-		event.lock_status.status = (efw->dev_lock_count > 0);
-		efw->dev_lock_changed = false;
-
-		count = min_t(long, count, sizeof(event.lock_status));
-	}
+	if (efw->dev_lock_changed)
+		count = hwdep_read_locked(efw, buf, count, offset);
+	else if (efw->resp_queues > 0)
+		count = hwdep_read_resp_buf(efw, buf, count, offset);
 
 	spin_unlock_irq(&efw->lock);
 
-	if (copy_to_user(buf, &event, count))
-		return -EFAULT;
+	return count;
+}
 
+static long
+hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+	    loff_t *offset)
+{
+	struct snd_efw *efw = hwdep->private_data;
+	u32 seqnum;
+	u8 *buf;
+
+	if (count < sizeof(struct snd_efw_transaction))
+		return -EINVAL;
+
+	buf = memdup_user(data, count);
+	if (IS_ERR(buf))
+		return PTR_ERR(data);
+
+	/* check seqnum is not for kernel-land */
+	seqnum = ((struct snd_efw_transaction *)buf)->seqnum;
+	if (seqnum + 2 > SND_EFW_TRANSACTION_SEQNUM_MAX) {
+		count = -EINVAL;
+		goto end;
+	}
+
+	if (snd_efw_transaction_cmd(efw->unit, buf, count) < 0)
+		count = -EIO;
+end:
+	kfree(buf);
 	return count;
 }
 
@@ -62,13 +158,13 @@ hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
 	poll_wait(file, &efw->hwdep_wait, wait);
 
 	spin_lock_irq(&efw->lock);
-	if (efw->dev_lock_changed)
+	if (efw->dev_lock_changed || (efw->resp_queues > 0))
 		events = POLLIN | POLLRDNORM;
 	else
 		events = 0;
 	spin_unlock_irq(&efw->lock);
 
-	return events;
+	return events | POLLOUT;
 }
 
 static int
@@ -172,6 +268,7 @@ hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
 
 static const struct snd_hwdep_ops hwdep_ops = {
 	.read		= hwdep_read,
+	.write		= hwdep_write,
 	.release	= hwdep_release,
 	.poll		= hwdep_poll,
 	.ioctl		= hwdep_ioctl,
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
index 62f758b..3204d69 100644
--- a/sound/firewire/fireworks/fireworks_proc.c
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -176,12 +176,31 @@ end:
 	kfree(meters);
 }
 
+static void
+proc_read_queues_state(struct snd_info_entry *entry,
+		       struct snd_info_buffer *buffer)
+{
+	struct snd_efw *efw = entry->private_data;
+	unsigned int consumed;
+
+	if (efw->pull_ptr > efw->push_ptr)
+		consumed = resp_buf_size -
+			   (unsigned int)(efw->pull_ptr - efw->push_ptr);
+	else
+		consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+	snd_iprintf(buffer, "%d %d/%d\n",
+		    efw->resp_queues, consumed, resp_buf_size);
+}
+
 void snd_efw_proc_init(struct snd_efw *efw)
 {
 	struct snd_info_entry *entry;
 
 	if (!snd_card_proc_new(efw->card, "#firmware", &entry))
 		snd_info_set_text_ops(entry, efw, proc_read_hwinfo);
+	if (!snd_card_proc_new(efw->card, "#queues", &entry))
+		snd_info_set_text_ops(entry, efw, proc_read_queues_state);
 	if (!snd_card_proc_new(efw->card, "#clock", &entry))
 		snd_info_set_text_ops(entry, efw, proc_read_clock);
 	if (!snd_card_proc_new(efw->card, "#meters", &entry))
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
index c2f499e..a310958 100644
--- a/sound/firewire/fireworks/fireworks_transaction.c
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -40,6 +40,9 @@
 #define ERROR_DELAY_MS 5
 #define EFC_TIMEOUT_MS 125
 
+static DEFINE_SPINLOCK(instances_lock);
+static struct snd_efw *instances[SNDRV_CARDS] = SNDRV_DEFAULT_PTR;
+
 static DEFINE_SPINLOCK(transaction_queues_lock);
 static LIST_HEAD(transaction_queues);
 
@@ -59,6 +62,14 @@ struct transaction_queue {
 	wait_queue_head_t wait;
 };
 
+int snd_efw_transaction_cmd(struct fw_unit *unit,
+			    const void *cmd, unsigned int size)
+{
+	return snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
+				  MEMORY_SPACE_EFW_COMMAND,
+				  (void *)cmd, size, 0);
+}
+
 int snd_efw_transaction_run(struct fw_unit *unit,
 			    const void *cmd, unsigned int cmd_size,
 			    void *resp, unsigned int resp_size, u32 seqnum)
@@ -80,9 +91,7 @@ int snd_efw_transaction_run(struct fw_unit *unit,
 
 	tries = 0;
 	do {
-		ret = snd_fw_transaction(unit, TCODE_WRITE_BLOCK_REQUEST,
-					 MEMORY_SPACE_EFW_COMMAND,
-					 (void *)cmd, cmd_size, 0);
+		ret = snd_efw_transaction_cmd(t.unit, (void *)cmd, cmd_size);
 		if (ret < 0)
 			break;
 
@@ -109,27 +118,92 @@ int snd_efw_transaction_run(struct fw_unit *unit,
 }
 
 static void
-efw_response(struct fw_card *card, struct fw_request *request,
-	     int tcode, int destination, int source,
-	     int generation, unsigned long long offset,
-	     void *data, size_t length, void *callback_data)
+copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
 {
-	struct fw_device *device;
-	struct transaction_queue *t;
-	unsigned long flags;
-	int rcode;
-	u32 seqnum;
+	size_t capacity, till_end;
+	struct snd_efw_transaction *t;
 
-	rcode = RCODE_TYPE_ERROR;
-	if (length < sizeof(struct snd_efw_transaction)) {
-		rcode = RCODE_DATA_ERROR;
-		goto end;
-	} else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
-		rcode = RCODE_ADDRESS_ERROR;
+	spin_lock_irq(&efw->lock);
+
+	t = (struct snd_efw_transaction *)data;
+	length = min_t(size_t, t->length * sizeof(t->length), length);
+
+	if (efw->push_ptr < efw->pull_ptr)
+		capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
+	else
+		capacity = resp_buf_size -
+			   (unsigned int)(efw->push_ptr - efw->pull_ptr);
+
+	/* confirm enough space for this response */
+	if (capacity < length) {
+		*rcode = RCODE_CONFLICT_ERROR;
 		goto end;
 	}
 
-	seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+	/* copy to ring buffer */
+	while (length > 0) {
+		till_end = resp_buf_size -
+			   (unsigned int)(efw->push_ptr - efw->resp_buf);
+		till_end = min_t(unsigned int, length, till_end);
+
+		memcpy(efw->push_ptr, data, till_end);
+
+		efw->push_ptr += till_end;
+		if (efw->push_ptr >= efw->resp_buf + resp_buf_size)
+			efw->push_ptr = efw->resp_buf;
+
+		length -= till_end;
+		data += till_end;
+	}
+
+	/* for hwdep */
+	efw->resp_queues++;
+	wake_up(&efw->hwdep_wait);
+
+	*rcode = RCODE_COMPLETE;
+end:
+	spin_unlock_irq(&efw->lock);
+}
+
+static void
+handle_resp_for_user(struct fw_card *card, int generation, int source,
+		     void *data, size_t length, int *rcode)
+{
+	struct fw_device *device;
+	struct snd_efw *efw;
+	unsigned int i;
+
+	spin_lock_irq(&instances_lock);
+
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		efw = instances[i];
+		if (efw == NULL)
+			continue;
+		device = fw_parent_device(efw->unit);
+		if ((device->card != card) ||
+		    (device->generation != generation))
+			continue;
+		smp_rmb();	/* node id vs. generation */
+		if (device->node_id != source)
+			continue;
+
+		break;
+	}
+	if (i == SNDRV_CARDS)
+		goto end;
+
+	copy_resp_to_buf(efw, data, length, rcode);
+end:
+	spin_unlock_irq(&instances_lock);
+}
+
+static void
+handle_resp_for_kernel(struct fw_card *card, int generation, int source,
+		       void *data, size_t length, int *rcode, u32 seqnum)
+{
+	struct fw_device *device;
+	struct transaction_queue *t;
+	unsigned long flags;
 
 	spin_lock_irqsave(&transaction_queues_lock, flags);
 	list_for_each_entry(t, &transaction_queues, list) {
@@ -146,14 +220,76 @@ efw_response(struct fw_card *card, struct fw_request *request,
 			t->size = min_t(unsigned int, length, t->size);
 			memcpy(t->buf, data, t->size);
 			wake_up(&t->wait);
-			rcode = RCODE_COMPLETE;
+			*rcode = RCODE_COMPLETE;
 		}
 	}
 	spin_unlock_irqrestore(&transaction_queues_lock, flags);
+}
+
+static void
+efw_response(struct fw_card *card, struct fw_request *request,
+	     int tcode, int destination, int source,
+	     int generation, unsigned long long offset,
+	     void *data, size_t length, void *callback_data)
+{
+	int rcode, dummy;
+	u32 seqnum;
+
+	rcode = RCODE_TYPE_ERROR;
+	if (length < sizeof(struct snd_efw_transaction)) {
+		rcode = RCODE_DATA_ERROR;
+		goto end;
+	} else if (offset != MEMORY_SPACE_EFW_RESPONSE) {
+		rcode = RCODE_ADDRESS_ERROR;
+		goto end;
+	}
+
+	seqnum = be32_to_cpu(((struct snd_efw_transaction *)data)->seqnum);
+	if (seqnum > SND_EFW_TRANSACTION_SEQNUM_MAX) {
+		handle_resp_for_kernel(card, generation, source,
+				       data, length, &rcode, seqnum);
+		if (resp_buf_debug)
+			handle_resp_for_user(card, generation, source,
+					     data, length, &dummy);
+	} else {
+		handle_resp_for_user(card, generation, source,
+				     data, length, &rcode);
+	}
 end:
 	fw_send_response(card, request, rcode);
 }
 
+void snd_efw_transaction_add_instance(struct snd_efw *efw)
+{
+	unsigned int i;
+
+	spin_lock_irq(&instances_lock);
+
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (instances[i] != NULL)
+			continue;
+		instances[i] = efw;
+		break;
+	}
+
+	spin_unlock_irq(&instances_lock);
+}
+
+void snd_efw_transaction_remove_instance(struct snd_efw *efw)
+{
+	unsigned int i;
+
+	spin_lock_irq(&instances_lock);
+
+	for (i = 0; i < SNDRV_CARDS; i++) {
+		if (instances[i] != efw)
+			continue;
+		instances[i] = NULL;
+	}
+
+	spin_unlock_irq(&instances_lock);
+}
+
 void snd_efw_transaction_bus_reset(struct fw_unit *unit)
 {
 	struct transaction_queue *t;
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 27/39] bebob: Add skelton for BeBoB based devices
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (25 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 26/39] fireworks: Add command/response functionality into " Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 28/39] bebob: Add commands and connections/streams management Takashi Sakamoto
                   ` (12 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit add a new driver for BeBoB based devices with no functionality. This driver just create/remove
card instance according to callbacks.

BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire devices with
DM1000/1000E/1100/1500 chipset. It gives common way for host system to handle BeBoB based devices.

Current supported devices:
 - Edirol FA-66/FA-101
 - PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
 - BridgeCo RDAudio1/Audio5
 - Mackie Onyx 1220/1620/1640 (Firewire I/O Card)
 - Mackie d.2 (Firewire Option)
 - Stanton FinalScratch 2 (ScratchAmp)
 - Tascam IF-FW DM
 - Behringer XENIX UFX 1204/1604
 - Behringer Digital Mixer X32 series (X-UF Card)
 - Apogee Rosetta 200/Rosetta 400 (X-FireWire card)
 - Apogee DA-16X/AD-16X/DD-16X (X-FireWire card)
 - Apogee Ensemble
 - ESI Quotafire610
 - AcousticReality eARMasterOne
 - CME MatrixKFW
 - Phonix Helix Board 12 MkII/18 MkII/24 MkII
 - Phonic Helix Board 12 Universal/18 Universal/24 Universal
 - Lynx Aurora 8/16 (LT-FW)
 - ICON FireXon
 - PrismSound Orpheus/ADA-8XR

Devices possible to be supported if identifying IDs:
 - Apogee Mini-Me Firewire/Mini-DAC Firewire
 - Behringer F-Control Audio 610/1616
 - Cakewalk Sonar Power Studio 66
 - CME UF400e
 - ESI Quotafire XL
 - Infrasonic DewX/Windy6
 - Mackie Digital X Bus x.200/400
 - Phonic Helix Board 12/18/24
 - Phonic FireFly 202/302
 - Rolf Spuler Firewire Guitar

Tested-by: Maximilian Engelhardt <maxi@daemonizer.de>
Tested-by: David Henningsson <david.henningsson@canonical.com>
Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig        |  30 +++++
 sound/firewire/Makefile       |   1 +
 sound/firewire/bebob/Makefile |   2 +
 sound/firewire/bebob/bebob.c  | 296 ++++++++++++++++++++++++++++++++++++++++++
 sound/firewire/bebob/bebob.h  |  64 +++++++++
 5 files changed, 393 insertions(+)
 create mode 100644 sound/firewire/bebob/Makefile
 create mode 100644 sound/firewire/bebob/bebob.c
 create mode 100644 sound/firewire/bebob/bebob.h

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 875af02..9027b23 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -79,4 +79,34 @@ config SND_FIREWORKS
 	  To compile this driver as a module, choose M here: the module
 	  will be called snd-fireworks.
 
+config SND_BEBOB
+	tristate "BridgeCo DM1000/1500 with BeBoB firmware"
+	select SND_FIREWIRE_LIB
+        help
+	 Say Y here to include support for FireWire devices based
+	 on BridgeCo DM1000/1500 with BeBoB firmware:
+	  * Edirol FA-66/FA-101
+	  * PreSonus FIREBOX/FIREPOD/FP10/Inspire1394
+	  * BridgeCo RDAudio1/Audio5
+	  * Mackie Onyx 1220/1620/1640 (Firewire I/O Card)
+	  * Mackie d.2 (Firewire Option)
+	  * Stanton FinalScratch 2 (ScratchAmp)
+	  * Tascam IF-FW/DM
+	  * Behringer XENIX UFX 1204/1604
+	  * Behringer Digital Mixer X32 series (X-UF Card)
+	  * Apogee Rosetta 200/400 (X-FireWire card)
+	  * Apogee DA/AD/DD-16X (X-FireWire card)
+	  * Apogee Ensemble
+	  * ESI Quotafire610
+	  * AcousticReality eARMasterOne
+	  * CME MatrixKFW
+	  * Phonic Helix Board 12 MkII/18 MkII/24 MkII
+	  * Phonic Helix Board 12 Universal/18 Universal/24 Universal
+	  * Lynx Aurora 8/16 (LT-FW)
+	  * ICON FireXon
+	  * PrismSound Orpheus/ADA-8XR
+
+          To compile this driver as a module, choose M here: the module
+          will be called snd-bebob.
+
 endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 5cd39dc..fad8d49 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
 obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
 obj-$(CONFIG_SND_FIREWORKS) += fireworks/
+obj-$(CONFIG_SND_BEBOB) += bebob/
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
new file mode 100644
index 0000000..c6f0141
--- /dev/null
+++ b/sound/firewire/bebob/Makefile
@@ -0,0 +1,2 @@
+snd-bebob-objs := bebob.o
+obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
new file mode 100644
index 0000000..68301a7
--- /dev/null
+++ b/sound/firewire/bebob/bebob.c
@@ -0,0 +1,296 @@
+/*
+ * bebob.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * BeBoB is 'BridgeCo enhanced Breakout Box'. This is installed to firewire
+ * devices with DM1000/1000E/1100/1500 chipset. It gives common way for host
+ * system to handle BeBoB based devices.
+ */
+
+#include "bebob.h"
+
+MODULE_DESCRIPTION("BridgeCo BeBoB driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static int index[SNDRV_CARDS]	= SNDRV_DEFAULT_IDX;
+static char *id[SNDRV_CARDS]	= SNDRV_DEFAULT_STR;
+static bool enable[SNDRV_CARDS]	= SNDRV_DEFAULT_ENABLE_PNP;
+
+module_param_array(index, int, NULL, 0444);
+MODULE_PARM_DESC(index, "card index");
+module_param_array(id, charp, NULL, 0444);
+MODULE_PARM_DESC(id, "ID string");
+module_param_array(enable, bool, NULL, 0444);
+MODULE_PARM_DESC(enable, "enable BeBoB sound card");
+
+static DEFINE_MUTEX(devices_mutex);
+static unsigned int devices_used;
+
+/* Offsets from information register. */
+#define INFO_OFFSET_GUID		0x10
+#define INFO_OFFSET_HW_MODEL_ID		0x18
+#define INFO_OFFSET_HW_MODEL_REVISION	0x1c
+
+#define VEN_EDIROL	0x000040ab
+#define VEN_PRESONUS	0x00000a92
+#define VEN_BRIDGECO	0x000007f5
+#define VEN_MACKIE	0x0000000f
+#define VEN_STANTON	0x00001260
+#define VEN_TASCAM	0x0000022e
+#define VEN_BEHRINGER	0x00001564
+#define VEN_APOGEE	0x000003db
+#define VEN_ESI		0x00000f1b
+#define VEN_ACOUSTIC	0x00000002
+#define VEN_CME		0x0000000a
+#define VEN_PHONIC	0x00001496
+#define VEN_LYNX	0x000019e5
+#define VEN_ICON	0x00001a9e
+#define VEN_PRISMSOUND	0x00001198
+
+static int
+name_device(struct snd_bebob *bebob, unsigned int vendor_id)
+{
+	struct fw_device *fw_dev = fw_parent_device(bebob->unit);
+	char vendor[24] = {0};
+	char model[32] = {0};
+	u32 id;
+	u32 data[2] = {0};
+	u32 revision;
+	int err;
+
+	/* get vendor name from root directory */
+	err = fw_csr_string(fw_dev->config_rom + 5, CSR_VENDOR,
+			    vendor, sizeof(vendor));
+	if (err < 0)
+		goto end;
+
+	/* get model name from unit directory */
+	err = fw_csr_string(bebob->unit->directory, CSR_MODEL,
+			    model, sizeof(model));
+	if (err < 0)
+		goto end;
+
+	/* get hardware id */
+	err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_ID,
+				  &id);
+	if (err < 0)
+		goto end;
+
+	/* get hardware revision */
+	err = snd_bebob_read_quad(bebob->unit, INFO_OFFSET_HW_MODEL_REVISION,
+				  &revision);
+	if (err < 0)
+		goto end;
+
+	/* get GUID */
+	err = snd_bebob_read_block(bebob->unit, INFO_OFFSET_GUID,
+				   data, sizeof(data));
+	if (err < 0)
+		goto end;
+
+	strcpy(bebob->card->driver, "BeBoB");
+	strcpy(bebob->card->shortname, model);
+	snprintf(bebob->card->longname, sizeof(bebob->card->longname),
+		 "%s %s (id:%d, rev:%d), GUID %08x%08x at %s, S%d",
+		 vendor, model, id, revision,
+		 data[0], data[1], dev_name(&bebob->unit->device),
+		 100 << fw_dev->max_speed);
+	strcpy(bebob->card->mixername, model);
+end:
+	return err;
+}
+
+static void
+bebob_card_free(struct snd_card *card)
+{
+	struct snd_bebob *bebob = card->private_data;
+
+	if (bebob->card_index >= 0) {
+		mutex_lock(&devices_mutex);
+		devices_used &= ~BIT(bebob->card_index);
+		mutex_unlock(&devices_mutex);
+	}
+
+	mutex_destroy(&bebob->mutex);
+}
+
+static int
+bebob_probe(struct fw_unit *unit,
+	    const struct ieee1394_device_id *entry)
+{
+	struct snd_card *card;
+	struct snd_bebob *bebob;
+	unsigned int card_index;
+	int err;
+
+	mutex_lock(&devices_mutex);
+
+	for (card_index = 0; card_index < SNDRV_CARDS; card_index++) {
+		if (!(devices_used & BIT(card_index)) && enable[card_index])
+			break;
+	}
+	if (card_index >= SNDRV_CARDS) {
+		err = -ENOENT;
+		goto end;
+	}
+
+	err = snd_card_new(&unit->device, index[card_index], id[card_index],
+			   THIS_MODULE, sizeof(struct snd_bebob), &card);
+	if (err < 0)
+		goto end;
+	card->private_free = bebob_card_free;
+
+	bebob = card->private_data;
+	bebob->card = card;
+	bebob->unit = unit;
+	bebob->card_index = -1;
+	mutex_init(&bebob->mutex);
+	spin_lock_init(&bebob->lock);
+
+	err = name_device(bebob, entry->vendor_id);
+	if (err < 0)
+		goto error;
+
+	err = snd_card_register(card);
+	if (err < 0) {
+		snd_card_free(card);
+		goto error;
+	}
+	dev_set_drvdata(&unit->device, bebob);
+	devices_used |= BIT(card_index);
+	bebob->card_index = card_index;
+end:
+	mutex_unlock(&devices_mutex);
+	return err;
+error:
+	snd_card_free(card);
+	mutex_unlock(&devices_mutex);
+	return err;
+}
+
+static void
+bebob_update(struct fw_unit *unit)
+{
+	struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+	fcp_bus_reset(bebob->unit);
+}
+
+
+static void bebob_remove(struct fw_unit *unit)
+{
+	struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+	snd_card_disconnect(bebob->card);
+	snd_card_free_when_closed(bebob->card);
+}
+
+static const struct ieee1394_device_id bebob_id_table[] = {
+	/* Edirol, FA-66 */
+	SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049),
+	/* Edirol, FA-101 */
+	SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048),
+	/* Presonus, FIREBOX */
+	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000),
+	/* PreSonus, FIREPOD/FP10 */
+	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066),
+	/* PreSonus, Inspire1394 */
+	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001),
+	/* BridgeCo, RDAudio1 */
+	SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048),
+	/* BridgeCo, Audio5 */
+	SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049),
+	/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
+	SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065),
+	/* Mackie, d.2 (Firewire Option) */
+	SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067),
+	/* Stanton, ScratchAmp */
+	SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001),
+	/* Tascam, IF-FW DM */
+	SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067),
+	/* Behringer, XENIX UFX 1204 */
+	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204),
+	/* Behringer, XENIX UFX 1604 */
+	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604),
+	/* Behringer, Digital Mixer X32 series (X-UF Card) */
+	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006),
+	/* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
+	/* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
+	SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048),
+	/* Apogee Electronics, Ensemble */
+	SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee),
+	/* ESI, Quatafire610 */
+	SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064),
+	/* AcousticReality, eARMasterOne */
+	SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002),
+	/* CME, MatrixKFW */
+	SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000),
+	/* Phonic, Helix Board 12 MkII */
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000),
+	/* Phonic, Helix Board 18 MkII */
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000),
+	/* Phonic, Helix Board 24 MkII */
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000),
+	/* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000),
+	/* Lynx, Aurora 8/16 (LT-FW) */
+	SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001),
+	/* ICON, FireXon */
+	SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001),
+	/* PrismSound, Orpheus */
+	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048),
+	/* PrismSound, ADA-8XR */
+	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8),
+	/* IDs are unknown but able to be supported */
+	/*  Apogee, Mini-ME Firewire */
+	/*  Apogee, Mini-DAC Firewire */
+	/*  Behringer, F-Control Audio 1616 */
+	/*  Behringer, F-Control Audio 610 */
+	/*  Cakawalk, Sonar Power Studio 66 */
+	/*  CME, UF400e */
+	/*  ESI, Quotafire XL */
+	/*  Infrasonic, DewX */
+	/*  Infrasonic, Windy6 */
+	/*  Mackie, Digital X Bus x.200 */
+	/*  Mackie, Digital X Bus x.400 */
+	/*  Phonic, HB 12 */
+	/*  Phonic, HB 24 */
+	/*  Phonic, HB 18 */
+	/*  Phonic, FireFly 202 */
+	/*  Phonic, FireFly 302 */
+	/*  Rolf Spuler, Firewire Guitar */
+	{}
+};
+MODULE_DEVICE_TABLE(ieee1394, bebob_id_table);
+
+static struct fw_driver bebob_driver = {
+	.driver = {
+		.owner	= THIS_MODULE,
+		.name	= "snd-bebob",
+		.bus	= &fw_bus_type,
+	},
+	.probe    = bebob_probe,
+	.update	  = bebob_update,
+	.remove   = bebob_remove,
+	.id_table = bebob_id_table,
+};
+
+static int __init
+snd_bebob_init(void)
+{
+	return driver_register(&bebob_driver.driver);
+}
+
+static void __exit
+snd_bebob_exit(void)
+{
+	driver_unregister(&bebob_driver.driver);
+	mutex_destroy(&devices_mutex);
+}
+
+module_init(snd_bebob_init);
+module_exit(snd_bebob_exit);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
new file mode 100644
index 0000000..173f4558
--- /dev/null
+++ b/sound/firewire/bebob/bebob.h
@@ -0,0 +1,64 @@
+/*
+ * bebob.h - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_BEBOB_H_INCLUDED
+#define SOUND_BEBOB_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+
+#include "../lib.h"
+#include "../fcp.h"
+
+/* basic register addresses on DM1000 */
+#define BEBOB_ADDR_REG_INFO	0xffffc8020000
+#define BEBOB_ADDR_REG_REQ	0xffffc8021000
+
+struct snd_bebob {
+	struct snd_card *card;
+	struct fw_unit *unit;
+	int card_index;
+
+	struct mutex mutex;
+	spinlock_t lock;
+};
+
+static inline int
+snd_bebob_read_block(struct fw_unit *unit, u64 addr, void *buf, int size)
+{
+	return snd_fw_transaction(unit, TCODE_READ_BLOCK_REQUEST,
+				  BEBOB_ADDR_REG_INFO + addr,
+				  buf, size, 0);
+}
+
+static inline int
+snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
+{
+	return snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
+				  BEBOB_ADDR_REG_INFO + addr,
+				  (void *)buf, sizeof(u32), 0);
+}
+
+#define SND_BEBOB_DEV_ENTRY(vendor, model) \
+{ \
+	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
+			  IEEE1394_MATCH_MODEL_ID, \
+	.vendor_id	= vendor, \
+	.model_id	= model, \
+}
+
+#endif
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 28/39] bebob: Add commands and connections/streams management
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (26 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 27/39] bebob: Add skelton for BeBoB based devices Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 29/39] bebob: Add proc interface for debugging purpose Takashi Sakamoto
                   ` (11 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds management functionality for connections and streams.
BeBoB uses CMP to manage connections and uses AMDTP for streams.

This commit also adds some BridgeCo's AV/C extension commands. There are many
BridgeCo's AV/C extension commands but this commit adds below commands to get
device's capability and status:

 1.Extended Plug Info commands
  - Plug Channel Position Specific Data
  - Plug Type Specific Data
  - Cluster(Section) Info Specific Data
  - Plug Input Specific Data
 2.Extended Stream Format Information commands
  - Extended Stream Format Information Command - Single Request

For Extended Plug Info commands for Cluster Info Specific Data, I pick up
'section' instead of 'cluster' from document to prevent from misunderstanding
because 'cluster' is also used in IEC 61883-6.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/bebob/Makefile        |   2 +-
 sound/firewire/bebob/bebob.c         |  10 +
 sound/firewire/bebob/bebob.h         | 122 ++++++
 sound/firewire/bebob/bebob_command.c | 273 ++++++++++++
 sound/firewire/bebob/bebob_stream.c  | 784 +++++++++++++++++++++++++++++++++++
 5 files changed, 1190 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/bebob/bebob_command.c
 create mode 100644 sound/firewire/bebob/bebob_stream.c

diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index c6f0141..5cece62 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,2 +1,2 @@
-snd-bebob-objs := bebob.o
+snd-bebob-objs := bebob_command.o bebob_stream.o bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 68301a7..a33f162 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -157,6 +157,14 @@ bebob_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	err = snd_bebob_stream_discover(bebob);
+	if (err < 0)
+		goto error;
+
+	err = snd_bebob_stream_init_duplex(bebob);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
@@ -179,12 +187,14 @@ bebob_update(struct fw_unit *unit)
 {
 	struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
 	fcp_bus_reset(bebob->unit);
+	snd_bebob_stream_update_duplex(bebob);
 }
 
 
 static void bebob_remove(struct fw_unit *unit)
 {
 	struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+	snd_bebob_stream_destroy_duplex(bebob);
 	snd_card_disconnect(bebob->card);
 	snd_card_free_when_closed(bebob->card);
 }
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 173f4558..b3a641c 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -23,11 +23,25 @@
 
 #include "../lib.h"
 #include "../fcp.h"
+#include "../packets-buffer.h"
+#include "../iso-resources.h"
+#include "../amdtp.h"
+#include "../cmp.h"
 
 /* basic register addresses on DM1000 */
 #define BEBOB_ADDR_REG_INFO	0xffffc8020000
 #define BEBOB_ADDR_REG_REQ	0xffffc8021000
 
+struct snd_bebob;
+
+#define SND_BEBOB_STRM_FMT_ENTRIES	7
+struct snd_bebob_stream_formation {
+	unsigned int pcm;
+	unsigned int midi;
+};
+/* this is a lookup table for index of stream formations */
+extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
+
 struct snd_bebob {
 	struct snd_card *card;
 	struct fw_unit *unit;
@@ -35,6 +49,23 @@ struct snd_bebob {
 
 	struct mutex mutex;
 	spinlock_t lock;
+
+	unsigned int midi_input_ports;
+	unsigned int midi_output_ports;
+
+	struct cmp_connection out_conn;
+	struct amdtp_stream tx_stream;
+	struct cmp_connection in_conn;
+	struct amdtp_stream rx_stream;
+	unsigned int capture_substreams;
+	unsigned int playback_substreams;
+
+	struct snd_bebob_stream_formation
+		tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+	struct snd_bebob_stream_formation
+		rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
+
+	int sync_input_plug;
 };
 
 static inline int
@@ -53,6 +84,97 @@ snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
 				  (void *)buf, sizeof(u32), 0);
 }
 
+/*
+ * AVC command extensions, AV/C Unit and Subunit, Revision 17
+ * (Nov 2003, BridgeCo)
+ */
+#define	AVC_BRIDGECO_ADDR_BYTES	6
+enum avc_bridgeco_plug_dir {
+	AVC_BRIDGECO_PLUG_DIR_IN	= 0x00,
+	AVC_BRIDGECO_PLUG_DIR_OUT	= 0x01
+};
+enum avc_bridgeco_plug_mode {
+	AVC_BRIDGECO_PLUG_MODE_UNIT		= 0x00,
+	AVC_BRIDGECO_PLUG_MODE_SUBUNIT		= 0x01,
+	AVC_BRIDGECO_PLUG_MODE_FUNCTION_BLOCK	= 0x02
+};
+enum avc_bridgeco_plug_unit {
+	AVC_BRIDGECO_PLUG_UNIT_ISOC	= 0x00,
+	AVC_BRIDGECO_PLUG_UNIT_EXT	= 0x01,
+	AVC_BRIDGECO_PLUG_UNIT_ASYNC	= 0x02
+};
+enum avc_bridgeco_plug_type {
+	AVC_BRIDGECO_PLUG_TYPE_ISOC	= 0x00,
+	AVC_BRIDGECO_PLUG_TYPE_ASYNC	= 0x01,
+	AVC_BRIDGECO_PLUG_TYPE_MIDI	= 0x02,
+	AVC_BRIDGECO_PLUG_TYPE_SYNC	= 0x03,
+	AVC_BRIDGECO_PLUG_TYPE_ANA	= 0x04,
+	AVC_BRIDGECO_PLUG_TYPE_DIG	= 0x05
+};
+static inline void
+avc_bridgeco_fill_unit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+			    enum avc_bridgeco_plug_dir dir,
+			    enum avc_bridgeco_plug_unit unit,
+			    unsigned int pid)
+{
+	buf[0] = 0xff;	/* Unit */
+	buf[1] = dir;
+	buf[2] = AVC_BRIDGECO_PLUG_MODE_UNIT;
+	buf[3] = unit;
+	buf[4] = 0xff & pid;
+	buf[5] = 0xff;	/* reserved */
+}
+static inline void
+avc_bridgeco_fill_subunit_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
+			       unsigned int mode,
+			       enum avc_bridgeco_plug_dir dir,
+			       unsigned int pid)
+{
+	buf[0] = 0xff & mode;	/* Subunit */
+	buf[1] = dir;
+	buf[2] = AVC_BRIDGECO_PLUG_MODE_SUBUNIT;
+	buf[3] = 0xff & pid;
+	buf[4] = 0xff;	/* reserved */
+	buf[5] = 0xff;	/* reserved */
+}
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+				 u8 add[AVC_BRIDGECO_ADDR_BYTES],
+				 u8 *buf, unsigned int len);
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+			       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+			       enum avc_bridgeco_plug_type *type);
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+				       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+				       unsigned int section_id, u8 *ctype);
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+				u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+				u8 input[7]);
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+				   u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+				   unsigned int entryid, u8 *buf,
+				   unsigned int *len);
+
+int snd_bebob_get_rate(struct snd_bebob *bebob, unsigned int *rate,
+		       enum avc_general_plug_dir dir);
+int snd_bebob_set_rate(struct snd_bebob *bebob, unsigned int rate,
+		       enum avc_general_plug_dir dir);
+
+/* for AMDTP streaming */
+int snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *rate);
+int snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate);
+int snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob,
+					  bool *internal);
+int snd_bebob_stream_discover(struct snd_bebob *bebob);
+int snd_bebob_stream_map(struct snd_bebob *bebob,
+			 struct amdtp_stream *stream);
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob,
+				  struct amdtp_stream *stream,
+				   unsigned int sampling_rate);
+int snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
+
 #define SND_BEBOB_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
new file mode 100644
index 0000000..22a86d0
--- /dev/null
+++ b/sound/firewire/bebob/bebob_command.c
@@ -0,0 +1,273 @@
+/*
+ * bebob_command.c - driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+#define BEBOB_COMMAND_MAX_TRIAL	3
+#define BEBOB_COMMAND_WAIT_MSEC	100
+
+static inline void
+avc_bridgeco_fill_command_base(u8 *buf, unsigned int ctype, unsigned int opcode,
+			       unsigned int subfunction,
+			       u8 addr[AVC_BRIDGECO_ADDR_BYTES])
+{
+	buf[0] = 0x7 & ctype;
+	buf[1] = addr[0];
+	buf[2] = 0xff & opcode;
+	buf[3] = 0xff & subfunction;
+	buf[4] = addr[1];
+	buf[5] = addr[2];
+	buf[6] = addr[3];
+	buf[7] = addr[4];
+	buf[8] = addr[5];
+}
+
+int avc_bridgeco_get_plug_type(struct fw_unit *unit,
+			       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+			       enum avc_bridgeco_plug_type *type)
+{
+	u8 *buf;
+	int err;
+
+	buf = kzalloc(12, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	/* status for plug info with bridgeco extension */
+	avc_bridgeco_fill_command_base(buf, 0x01, 0x02, 0xc0, addr);
+	buf[9]  = 0x00;		/* info type is 'plug type' */
+	buf[10] = 0xff;		/* plug type in response */
+
+	/* do transaction and check buf[1-7,9] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(7) | BIT(9));
+	if (err < 0)
+		goto end;
+	/* IMPLEMENTED/STABLE is OK */
+	else if ((err < 6) || (buf[0] != 0x0c)) {
+		err = -EIO;
+		goto end;
+	}
+
+	*type = buf[10];
+	err = 0;
+end:
+	kfree(buf);
+	return err;
+}
+
+int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
+				 u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+				 u8 *buf, unsigned int len)
+{
+	unsigned int trial;
+	int err;
+
+	/* check given buffer */
+	if ((buf == NULL) || (len < 256)) {
+		err = -EINVAL;
+		goto end;
+	}
+
+	/* status for plug info with bridgeco extension */
+	avc_bridgeco_fill_command_base(buf, 0x01, 0x02, 0xc0, addr);
+
+	/* info type is 'channel position' */
+	buf[9] = 0x03;
+
+	/*
+	 * NOTE:
+	 * M-Audio Firewire 410 returns 0x09 (ACCEPTED) just after changing
+	 * signal format even if this command asks STATE. This is not in
+	 * AV/C command specification.
+	 */
+	for (trial = 0; trial < BEBOB_COMMAND_MAX_TRIAL; trial++) {
+		/* do transaction and check buf[1-7,9] are the same */
+		err = fcp_avc_transaction(unit, buf, 12, buf, 256,
+					  BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+					  BIT(5) | BIT(6) | BIT(7) | BIT(9));
+		if (err < 0)
+			goto end;
+		else if (err < 6) {
+			err = -EIO;
+			goto end;
+		} else if (buf[0] == 0x0c)
+			break;
+		else if (trial < BEBOB_COMMAND_MAX_TRIAL)
+			msleep(BEBOB_COMMAND_WAIT_MSEC);
+		else {
+			err = -EIO;
+			goto end;
+		}
+	}
+
+	/* strip command header */
+	memmove(buf, buf + 10, err - 10);
+	err = 0;
+end:
+	return err;
+}
+
+int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
+				       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+				       unsigned int section_id, u8 *type)
+{
+	u8 *buf;
+	int err;
+
+	/* section info includes charactors but this module don't need it */
+	buf = kzalloc(12, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	/* status for plug info with bridgeco extension */
+	avc_bridgeco_fill_command_base(buf, 0x01, 0x02, 0xc0, addr);
+
+	buf[9] = 0x07;		/* info type is 'section info' */
+	buf[10] = 0xff & (section_id + 1);	/* section id */
+	buf[11] = 0x00;		/* type in response */
+
+	/* do transaction and check buf[1-7,9,10] are the same */
+	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(7) | BIT(9) | BIT(10));
+	if (err < 0)
+		goto end;
+	else if ((err < 12) && (buf[0] != 0x0c)) {
+		err = -EIO;
+		goto end;
+	}
+
+	*type = buf[11];
+	err = 0;
+end:
+	kfree(buf);
+	return err;
+}
+
+int avc_bridgeco_get_plug_input(struct fw_unit *unit,
+				u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
+{
+	int err;
+	u8 *buf;
+
+	buf = kzalloc(18, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	/* status for plug info with bridgeco extension */
+	avc_bridgeco_fill_command_base(buf, 0x01, 0x02, 0xc0, addr);
+
+	/* info type is 'Plug Input Specific Data' */
+	buf[9] = 0x05;
+
+	/* do transaction and check buf[1-7] are the same */
+	err = fcp_avc_transaction(unit, buf, 16, buf, 16,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(7));
+	if (err < 0)
+		goto end;
+	else if ((err < 18) && (buf[0] != 0x0c)) {
+		err = -EIO;
+		goto end;
+	}
+
+	memcpy(input, buf + 10, 5);
+end:
+	kfree(buf);
+	return err;
+}
+
+int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
+				   u8 addr[AVC_BRIDGECO_ADDR_BYTES],
+				   unsigned int entryid, u8 *buf,
+				   unsigned int *len)
+{
+	int err;
+
+	/* check given buffer */
+	if ((buf == NULL) || (*len < 12)) {
+		err = -EINVAL;
+		goto end;
+	}
+
+	/* status for plug info with bridgeco extension */
+	avc_bridgeco_fill_command_base(buf, 0x01, 0x2f, 0xc1, addr);
+
+	buf[9] = 0xff;			/* stream status in response */
+	buf[10] = 0xff & entryid;	/* entry ID */
+
+	/* do transaction and check buf[1-7,10] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 12, buf, *len,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(7) | BIT(10));
+	if (err < 0)
+		goto end;
+	/* reach the end of entries */
+	else if (buf[0] == 0x0a) {
+		err = 0;
+		*len = 0;
+		goto end;
+	} else if (buf[0] != 0x0c) {
+		err = -EINVAL;
+		goto end;
+	/* the header of this command is 11 bytes */
+	} else if (err < 12) {
+		err = -EIO;
+		goto end;
+	} else if (buf[10] != entryid) {
+		err = -EIO;
+		goto end;
+	}
+
+	/* strip command header */
+	memmove(buf, buf + 11, err - 11);
+	*len = err - 11;
+	err = 0;
+end:
+	return err;
+}
+
+int snd_bebob_get_rate(struct snd_bebob *bebob, unsigned int *rate,
+		       enum avc_general_plug_dir dir)
+{
+	int err;
+
+	err = avc_general_get_sig_fmt(bebob->unit, rate, dir, 0);
+	if (err < 0)
+		goto end;
+
+	/* IMPLEMENTED/STABLE is OK */
+	if (err != 0x0c) {
+		dev_err(&bebob->unit->device,
+			"failed to get sampling rate\n");
+		err = -EIO;
+	}
+end:
+	return err;
+}
+
+int snd_bebob_set_rate(struct snd_bebob *bebob, unsigned int rate,
+		       enum avc_general_plug_dir dir)
+{
+	int err;
+
+	err = avc_general_set_sig_fmt(bebob->unit, rate, dir, 0);
+	if (err < 0)
+		goto end;
+
+	/* ACCEPTED or INTERIM is OK */
+	if ((err != 0x0f) && (err != 0x09)) {
+		dev_err(&bebob->unit->device,
+			"failed to set sampling rate\n");
+		err = -EIO;
+	}
+end:
+	return err;
+}
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
new file mode 100644
index 0000000..497d1c6
--- /dev/null
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -0,0 +1,784 @@
+/*
+ * bebob_stream.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+#define CALLBACK_TIMEOUT	1000
+
+/*
+ * NOTE;
+ * For BeBoB streams, Both of input and output CMP connection are important.
+ *
+ * For most devices, each CMP connection starts to transmit/receive a
+ * corresponding streams. But for a few devices, both of CMP connection needs
+ * to start transmitting stream. An example is 'M-Audio Firewire 410'.
+ */
+
+/* 128 is an arbitrary length but it seems to be enough */
+#define FORMAT_MAXIMUM_LENGTH 128
+
+const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES] = {
+	[0] = 32000,
+	[1] = 44100,
+	[2] = 48000,
+	[3] = 88200,
+	[4] = 96000,
+	[5] = 176400,
+	[6] = 192000,
+};
+
+/*
+ * See: Table 51: Extended Stream Format Info ‘Sampling Frequency’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ */
+static const unsigned int bridgeco_freq_table[] = {
+	[0] = 0x02,
+	[1] = 0x03,
+	[2] = 0x04,
+	[3] = 0x0a,
+	[4] = 0x05,
+	[5] = 0x06,
+	[6] = 0x07,
+};
+
+static unsigned int
+get_formation_index(unsigned int rate)
+{
+	unsigned int i;
+
+	for (i = 0; i < sizeof(snd_bebob_rate_table); i++) {
+		if (snd_bebob_rate_table[i] == rate)
+			return i;
+	}
+	return -1;
+}
+
+int
+snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate)
+{
+	unsigned int tx_rate, rx_rate;
+	int err;
+
+	err = snd_bebob_get_rate(bebob, &tx_rate, AVC_GENERAL_PLUG_DIR_OUT);
+	if (err < 0)
+		goto end;
+
+	err = snd_bebob_get_rate(bebob, &rx_rate, AVC_GENERAL_PLUG_DIR_IN);
+	if (err < 0)
+		goto end;
+
+	*curr_rate = rx_rate;
+	if (rx_rate == tx_rate)
+		goto end;
+
+	/* synchronize receive stream rate to transmit stream rate */
+	err = snd_bebob_set_rate(bebob, rx_rate, AVC_GENERAL_PLUG_DIR_IN);
+end:
+	return err;
+}
+
+int
+snd_bebob_stream_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+	int err;
+
+	err = snd_bebob_set_rate(bebob, rate, AVC_GENERAL_PLUG_DIR_OUT);
+	if (err < 0)
+		goto end;
+
+	err = snd_bebob_set_rate(bebob, rate, AVC_GENERAL_PLUG_DIR_IN);
+end:
+	return err;
+}
+
+int
+snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
+{
+	u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
+	int err = 0;
+
+	*internal = false;
+
+	/*
+	 * 1.The device don't support for switching source of clock
+	 *   then assumed to use internal clock always
+	 */
+	if (bebob->sync_input_plug < 0) {
+		*internal = true;
+		goto end;
+	}
+
+	/*
+	 * 2.The device supports to switch source of clock by an usual way.
+	 *   Let's check input for 'Music Sub Unit Sync Input' plug.
+	 */
+	avc_bridgeco_fill_subunit_addr(addr, 0x60, AVC_BRIDGECO_PLUG_DIR_IN,
+				       bebob->sync_input_plug);
+	err = avc_bridgeco_get_plug_input(bebob->unit, addr, input);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * If there are no input plugs, all of fields are 0xff.
+	 * Here check the first field. This field is used for direction.
+	 */
+	if (input[0] == 0xff) {
+		*internal = true;
+		goto end;
+	}
+
+	/*
+	 * If source of clock is internal CSR, Music Sub Unit Sync Input is
+	 * a destination of Music Sub Unit Sync Output.
+	 */
+	*internal = ((input[0] == AVC_BRIDGECO_PLUG_DIR_OUT) &&
+		     (input[1] == AVC_BRIDGECO_PLUG_MODE_SUBUNIT) &&
+		     (input[2] == 0x0c) &&
+		     (input[3] == 0x00));
+end:
+	return err;
+}
+
+static unsigned int
+map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+	unsigned int sec, sections, ch, channels;
+	unsigned int pcm, midi, location;
+	unsigned int stm_pos, sec_loc, pos;
+	u8 *buf, addr[AVC_BRIDGECO_ADDR_BYTES], type;
+	enum avc_bridgeco_plug_dir dir;
+	int err;
+
+	/*
+	 * The length of return value of this command cannot be expected. Here
+	 * use the maximum length of FCP.
+	 */
+	buf = kzalloc(256, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	if (s == &bebob->tx_stream)
+		dir = AVC_BRIDGECO_PLUG_DIR_OUT;
+	else
+		dir = AVC_BRIDGECO_PLUG_DIR_IN;
+
+	avc_bridgeco_fill_unit_addr(addr, dir, AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+	err = avc_bridgeco_get_plug_ch_pos(bebob->unit, addr, buf, 256);
+	if (err < 0)
+		goto end;
+	pos = 0;
+
+	/* positions in I/O buffer */
+	pcm = 0;
+	midi = 0;
+
+	/* the number of sections in AMDTP packet */
+	sections = buf[pos++];
+
+	for (sec = 0; sec < sections; sec++) {
+		/* type of this section */
+		avc_bridgeco_fill_unit_addr(addr, dir,
+					    AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+		err = avc_bridgeco_get_plug_section_type(bebob->unit, addr,
+							 sec, &type);
+		if (err < 0)
+			goto end;
+		/* NoType */
+		if (type == 0xff) {
+			err = -ENOSYS;
+			goto end;
+		}
+
+		/* the number of channels in this section */
+		channels = buf[pos++];
+
+		for (ch = 0; ch < channels; ch++) {
+			/* position of this channel in AMDTP packet */
+			stm_pos = buf[pos++] - 1;
+			/* location of this channel in this section */
+			sec_loc = buf[pos++] - 1;
+
+			switch (type) {
+			/* for MIDI conformant data channel */
+			case 0x0a:
+				/* AMDTP_MAX_CHANNELS_FOR_MIDI is 1. */
+				if ((midi > 0) && (stm_pos != midi)) {
+					err = -ENOSYS;
+					goto end;
+				}
+				s->midi_position = stm_pos;
+				midi = stm_pos;
+				break;
+			/* for PCM data channel */
+			case 0x01:	/* Headphone */
+			case 0x02:	/* Microphone */
+			case 0x03:	/* Line */
+			case 0x04:	/* SPDIF */
+			case 0x05:	/* ADAT */
+			case 0x06:	/* TDIF */
+			case 0x07:	/* MADI */
+			/* for undefined/changeable signal  */
+			case 0x08:	/* Analog */
+			case 0x09:	/* Digital */
+			default:
+				location = pcm + sec_loc;
+				if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+					err = -ENOSYS;
+					goto end;
+				}
+				s->pcm_positions[location] = stm_pos;
+				break;
+			}
+		}
+
+		if (type != 0x0a)
+			pcm += channels;
+		else
+			midi += channels;
+	}
+end:
+	kfree(buf);
+	return err;
+}
+
+static int
+init_both_connections(struct snd_bebob *bebob)
+{
+	int err;
+
+	err = cmp_connection_init(&bebob->in_conn,
+				  bebob->unit, CMP_INPUT, 0);
+	if (err < 0)
+		goto end;
+
+	err = cmp_connection_init(&bebob->out_conn,
+				  bebob->unit, CMP_OUTPUT, 0);
+	if (err < 0)
+		cmp_connection_destroy(&bebob->in_conn);
+end:
+	return err;
+}
+
+static int
+check_connection_used_by_others(struct snd_bebob *bebob, struct amdtp_stream *s)
+{
+	struct cmp_connection *conn;
+	bool used;
+	int err;
+
+	if (s == &bebob->tx_stream)
+		conn = &bebob->out_conn;
+	else
+		conn = &bebob->in_conn;
+
+	err = cmp_connection_check_used(conn, &used);
+	if ((err >= 0) && used && !amdtp_stream_running(s)) {
+		dev_err(&bebob->unit->device,
+			"Connection established by others: %cPCR[%d]\n",
+			(conn->direction == CMP_OUTPUT) ? 'o' : 'i',
+			conn->pcr_index);
+		err = -EBUSY;
+	}
+
+	return err;
+}
+
+static int
+make_both_connections(struct snd_bebob *bebob, unsigned int rate)
+{
+	int index, pcm_channels, midi_channels, err;
+
+	/* confirm params for both streams */
+	index = get_formation_index(rate);
+	pcm_channels = bebob->tx_stream_formations[index].pcm;
+	midi_channels = bebob->tx_stream_formations[index].midi;
+	amdtp_stream_set_parameters(&bebob->tx_stream,
+				    rate, pcm_channels, midi_channels * 8);
+	pcm_channels = bebob->rx_stream_formations[index].pcm;
+	midi_channels = bebob->rx_stream_formations[index].midi;
+	amdtp_stream_set_parameters(&bebob->rx_stream,
+				    rate, pcm_channels, midi_channels * 8);
+
+	/* establish connections for both streams */
+	err = cmp_connection_establish(&bebob->out_conn,
+			amdtp_stream_get_max_payload(&bebob->tx_stream));
+	if (err < 0)
+		goto end;
+	err = cmp_connection_establish(&bebob->in_conn,
+			amdtp_stream_get_max_payload(&bebob->rx_stream));
+	if (err < 0)
+		cmp_connection_break(&bebob->out_conn);
+end:
+	return err;
+}
+
+static void
+break_both_connections(struct snd_bebob *bebob)
+{
+	cmp_connection_break(&bebob->in_conn);
+	cmp_connection_break(&bebob->out_conn);
+	return;
+}
+
+static void
+destroy_both_connections(struct snd_bebob *bebob)
+{
+	break_both_connections(bebob);
+
+	cmp_connection_destroy(&bebob->in_conn);
+	cmp_connection_destroy(&bebob->out_conn);
+}
+
+static int
+get_roles(struct snd_bebob *bebob, enum cip_flags *sync_mode,
+	  struct amdtp_stream **master, struct amdtp_stream **slave)
+{
+	/* currently this module doesn't support SYT-Match mode */
+	*sync_mode = CIP_SYNC_TO_DEVICE;
+	*master = &bebob->tx_stream;
+	*slave = &bebob->rx_stream;
+
+	return 0;
+}
+
+static int
+start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
+	     unsigned int rate)
+{
+	struct cmp_connection *conn;
+	int err = 0;
+
+	if (stream == &bebob->rx_stream)
+		conn = &bebob->in_conn;
+	else
+		conn = &bebob->out_conn;
+
+	/* channel mapping */
+	err = map_data_channels(bebob, stream);
+	if (err < 0)
+		goto end;
+
+	/* start amdtp stream */
+	err = amdtp_stream_start(stream,
+				 conn->resources.channel,
+				 conn->speed);
+end:
+	return err;
+}
+
+int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
+{
+	int err;
+
+	err = init_both_connections(bebob);
+	if (err < 0)
+		goto end;
+
+	err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
+				AMDTP_IN_STREAM, CIP_BLOCKING);
+	if (err < 0) {
+		destroy_both_connections(bebob);
+		goto end;
+	}
+
+	err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
+				AMDTP_OUT_STREAM, CIP_BLOCKING);
+	if (err < 0) {
+		amdtp_stream_destroy(&bebob->tx_stream);
+		destroy_both_connections(bebob);
+	}
+end:
+	return err;
+}
+
+int snd_bebob_stream_start_duplex(struct snd_bebob *bebob,
+				  struct amdtp_stream *request,
+				  unsigned int rate)
+{
+	struct amdtp_stream *master, *slave;
+	enum cip_flags sync_mode;
+	unsigned int curr_rate;
+	bool slave_flag;
+	int err;
+
+	mutex_lock(&bebob->mutex);
+
+	err = get_roles(bebob, &sync_mode, &master, &slave);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * Considering JACK/FFADO streaming:
+	 * TODO: This can be removed hwdep functionality becomes popular.
+	 */
+	err = check_connection_used_by_others(bebob, master);
+	if (err < 0)
+		goto end;
+
+	/* need to touch slave stream */
+	slave_flag = (request == slave) || amdtp_stream_running(slave);
+
+	/* packet queueing error */
+	if (amdtp_streaming_error(slave))
+		amdtp_stream_stop(slave);
+	if (amdtp_streaming_error(master))
+		amdtp_stream_stop(master);
+
+	/* stop streams if rate is different */
+	err = snd_bebob_stream_get_rate(bebob, &curr_rate);
+	if (err < 0)
+		goto end;
+	if (rate == 0)
+		rate = curr_rate;
+	if (rate != curr_rate) {
+		amdtp_stream_stop(slave);
+		amdtp_stream_stop(master);
+		break_both_connections(bebob);
+	}
+
+	/* master should be always running */
+	if (!amdtp_stream_running(master)) {
+		amdtp_stream_set_sync(sync_mode, master, slave);
+
+		/*
+		 * NOTE:
+		 * If establishing connections at first, Yamaha GO46
+		 * (and maybe Terratec X24) don't generate sound.
+		 */
+		err = snd_bebob_stream_set_rate(bebob, rate);
+		if (err < 0)
+			goto end;
+
+		err = make_both_connections(bebob, rate);
+		if (err < 0)
+			goto end;
+
+		err = start_stream(bebob, master, rate);
+		if (err < 0) {
+			dev_err(&bebob->unit->device,
+				"fail to run AMDTP master stream:%d\n", err);
+			break_both_connections(bebob);
+			goto end;
+		}
+
+		/* wait first callback */
+		if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
+			amdtp_stream_stop(master);
+			break_both_connections(bebob);
+			err = -ETIMEDOUT;
+			goto end;
+		}
+	}
+
+	/* start slave if needed */
+	if (slave_flag && !amdtp_stream_running(slave)) {
+		err = start_stream(bebob, slave, rate);
+		if (err < 0) {
+			dev_err(&bebob->unit->device,
+				"fail to run AMDTP slave stream:%d\n", err);
+			amdtp_stream_stop(master);
+			break_both_connections(bebob);
+			goto end;
+		}
+
+		/* wait first callback */
+		if (!amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
+			amdtp_stream_stop(slave);
+			amdtp_stream_stop(master);
+			break_both_connections(bebob);
+			err = -ETIMEDOUT;
+		}
+	}
+end:
+	mutex_unlock(&bebob->mutex);
+	return err;
+}
+
+int snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
+{
+	struct amdtp_stream *master, *slave;
+	enum cip_flags sync_mode;
+	unsigned int slave_substreams;
+	int err;
+
+	mutex_lock(&bebob->mutex);
+
+	err = get_roles(bebob, &sync_mode, &master, &slave);
+	if (err < 0)
+		goto end;
+
+	if (slave == &bebob->tx_stream)
+		slave_substreams = bebob->capture_substreams;
+	else
+		slave_substreams = bebob->playback_substreams;
+
+	if (slave_substreams > 0)
+		goto end;
+
+	amdtp_stream_stop(slave);
+
+	if ((bebob->capture_substreams > 0) || (bebob->playback_substreams > 0))
+		goto end;
+
+	amdtp_stream_stop(master);
+	break_both_connections(bebob);
+end:
+	mutex_unlock(&bebob->mutex);
+	return err;
+}
+
+void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
+{
+	if ((cmp_connection_update(&bebob->in_conn) < 0) ||
+	    (cmp_connection_update(&bebob->out_conn) < 0)) {
+		amdtp_stream_pcm_abort(&bebob->rx_stream);
+		amdtp_stream_pcm_abort(&bebob->tx_stream);
+		mutex_lock(&bebob->mutex);
+		amdtp_stream_stop(&bebob->rx_stream);
+		amdtp_stream_stop(&bebob->tx_stream);
+		break_both_connections(bebob);
+		mutex_unlock(&bebob->mutex);
+	} else {
+		amdtp_stream_update(&bebob->rx_stream);
+		amdtp_stream_update(&bebob->tx_stream);
+	}
+}
+
+void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob)
+{
+	mutex_lock(&bebob->mutex);
+
+	amdtp_stream_pcm_abort(&bebob->rx_stream);
+	amdtp_stream_pcm_abort(&bebob->tx_stream);
+
+	amdtp_stream_stop(&bebob->rx_stream);
+	amdtp_stream_stop(&bebob->tx_stream);
+	destroy_both_connections(bebob);
+
+	mutex_unlock(&bebob->mutex);
+}
+
+/*
+ * See: Table 50: Extended Stream Format Info ‘Format Hierarchy Level 2’
+ * in Additional AVC commands (Nov 2003, BridgeCo)
+ */
+static int
+parse_stream_formation(u8 *buf, unsigned int len,
+		       struct snd_bebob_stream_formation *formation)
+{
+	unsigned int i, e, channels, format;
+
+	/*
+	 * this module can support a hierarchy combination that:
+	 *  Root:	Audio and Music (0x90)
+	 *  Level 1:	AM824 Compound  (0x40)
+	 */
+	if ((buf[0] != 0x90) || (buf[1] != 0x40))
+		return -ENOSYS;
+
+	/* check sampling rate */
+	for (i = 0; i < sizeof(bridgeco_freq_table); i++) {
+		if (buf[2] == bridgeco_freq_table[i])
+			break;
+	}
+	if (i == sizeof(bridgeco_freq_table))
+		return -ENOSYS;
+
+	for (e = 0; e < buf[4]; e++) {
+		channels = buf[5 + e * 2];
+		format = buf[6 + e * 2];
+
+		switch (format) {
+		/* PCM for IEC 60958-3 */
+		case 0x00:
+		/* PCM for Multi bit linear audio (raw) */
+		case 0x06:
+			formation[i].pcm += channels;
+			break;
+		/* MIDI comformant (MMA/AMEI RP-027) */
+		case 0x0d:
+			formation[i].midi += channels;
+			break;
+		/* PCM for Multi bit linear audio (DVD-audio) */
+		case 0x07:
+		/* IEC 61937-3 to 7 */
+		case 0x01:
+		case 0x02:
+		case 0x03:
+		case 0x04:
+		case 0x05:
+		/* One Bit Audio */
+		case 0x08:	/* (Plain) Raw */
+		case 0x09:	/* (Plain) SACD */
+		case 0x0a:	/* (Encoded) Raw */
+		case 0x0b:	/* (ENcoded) SACD */
+		/* High precision Multi-bit Linear Audio */
+		case 0x0c:
+		/* Synchronization Stream (Stereo Raw audio) */
+		case 0x40:
+		/* Don't care */
+		case 0xff:
+		default:
+			return -ENOSYS;	/* not supported */
+		}
+	}
+
+	return 0;
+}
+
+static int
+fill_stream_formations(struct snd_bebob *bebob, enum avc_bridgeco_plug_dir dir,
+		       unsigned short pid)
+{
+	u8 *buf;
+	struct snd_bebob_stream_formation *formations;
+	unsigned int len, eid;
+	u8 addr[AVC_BRIDGECO_ADDR_BYTES];
+	int err;
+
+	buf = kmalloc(FORMAT_MAXIMUM_LENGTH, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	if (dir == AVC_BRIDGECO_PLUG_DIR_IN)
+		formations = bebob->rx_stream_formations;
+	else
+		formations = bebob->tx_stream_formations;
+
+	for (eid = 0; eid < SND_BEBOB_STRM_FMT_ENTRIES; eid++) {
+		len = FORMAT_MAXIMUM_LENGTH;
+
+		memset(buf, 0, len);
+		avc_bridgeco_fill_unit_addr(addr, dir,
+					    AVC_BRIDGECO_PLUG_UNIT_ISOC, pid);
+		err = avc_bridgeco_get_plug_strm_fmt(bebob->unit, addr,
+						     eid, buf, &len);
+		if (err < 0)
+			goto end;
+		else if (len < 3)
+			break;
+
+		/* parse and set stream formation */
+		err = parse_stream_formation(buf, len, formations);
+		if (err < 0)
+			goto end;
+	}
+end:
+	kfree(buf);
+	return err;
+}
+
+static int
+seek_msu_sync_input_plug(struct snd_bebob *bebob)
+{
+	u8 plugs[AVC_PLUG_INFO_BUF_COUNT], addr[AVC_BRIDGECO_ADDR_BYTES];
+	unsigned int i, type;
+	int err;
+
+	/* get information about Music Sub Unit */
+	err = avc_general_get_plug_info(bebob->unit, 0x0c, 0x00, 0x00, plugs);
+	if (err < 0)
+		goto end;
+
+	/* seek destination plugs for 'MSU sync input' */
+	bebob->sync_input_plug = -1;
+	for (i = 0; i < plugs[0]; i++) {
+		avc_bridgeco_fill_subunit_addr(addr, 0x60,
+					       AVC_BRIDGECO_PLUG_DIR_IN, i);
+		err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+		if (err < 0)
+			goto end;
+
+		if (type == AVC_BRIDGECO_PLUG_TYPE_SYNC)
+			bebob->sync_input_plug = i;
+	}
+end:
+	return err;
+}
+
+/* In this function, 2 means input and output */
+int snd_bebob_stream_discover(struct snd_bebob *bebob)
+{
+	u8 plugs[AVC_PLUG_INFO_BUF_COUNT], addr[AVC_BRIDGECO_ADDR_BYTES];
+	enum avc_bridgeco_plug_type type;
+	unsigned int i;
+	int err;
+
+	/* the number of plugs for isoc in/out, ext in/out  */
+	err = avc_general_get_plug_info(bebob->unit, 0x1f, 0x07, 0x00, plugs);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * This module supports one ISOC input plug and one ISOC output plug
+	 * then ignores the others.
+	 */
+	if (plugs[0] == 0) {
+		err = -EIO;
+		goto end;
+	}
+	avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+				    AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+	err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+	if (err < 0)
+		goto end;
+	else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
+		err = -EIO;
+		goto end;
+	}
+
+	if (plugs[1] == 0) {
+		err = -EIO;
+		goto end;
+	}
+	avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+				    AVC_BRIDGECO_PLUG_UNIT_ISOC, 0);
+	err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+	if (err < 0)
+		goto end;
+	else if (type != AVC_BRIDGECO_PLUG_TYPE_ISOC) {
+		err = -EIO;
+		goto end;
+	}
+
+	/* store formations */
+	for (i = 0; i < 2; i++) {
+		err = fill_stream_formations(bebob, i, 0);
+		if (err < 0)
+			goto end;
+	}
+
+	/* count external input plugs for MIDI */
+	bebob->midi_input_ports = 0;
+	for (i = 0; i < plugs[2]; i++) {
+		avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN,
+					    AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+		err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+		if (err < 0)
+			goto end;
+		else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI)
+			bebob->midi_input_ports++;
+	}
+
+	/* count external output plugs for MIDI */
+	bebob->midi_output_ports = 0;
+	for (i = 0; i < plugs[3]; i++) {
+		avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT,
+					    AVC_BRIDGECO_PLUG_UNIT_EXT, i);
+		err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
+		if (err < 0)
+			goto end;
+		else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI)
+			bebob->midi_output_ports++;
+	}
+
+	/* for check source of clock later */
+	err = seek_msu_sync_input_plug(bebob);
+end:
+	return err;
+}
-- 
1.8.3.2

_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 29/39] bebob: Add proc interface for debugging purpose
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (27 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 28/39] bebob: Add commands and connections/streams management Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 30/39] bebob: Add MIDI interface Takashi Sakamoto
                   ` (10 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds proc interface to get information for debugging.
 - firmware information
 - stream formation
 - current clock source and sampling rate

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/bebob/Makefile     |   2 +-
 sound/firewire/bebob/bebob.c      |   2 +
 sound/firewire/bebob/bebob.h      |   3 +
 sound/firewire/bebob/bebob_proc.c | 131 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 137 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/bebob/bebob_proc.c

diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 5cece62..757c40e 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,2 +1,2 @@
-snd-bebob-objs := bebob_command.o bebob_stream.o bebob.o
+snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index a33f162..81d41b1 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -165,6 +165,8 @@ bebob_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	snd_bebob_proc_init(bebob);
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index b3a641c..83ee15a 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -20,6 +20,7 @@
 
 #include <sound/core.h>
 #include <sound/initval.h>
+#include <sound/info.h>
 
 #include "../lib.h"
 #include "../fcp.h"
@@ -175,6 +176,8 @@ int snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
 
+void snd_bebob_proc_init(struct snd_bebob *bebob);
+
 #define SND_BEBOB_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
new file mode 100644
index 0000000..07bb6ca
--- /dev/null
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -0,0 +1,131 @@
+/*
+ * bebob_proc.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+/* contents of information register */
+struct hw_info {
+	u64 manufacturer;
+	u32 protocol_ver;
+	u32 bld_ver;
+	u32 guid[2];
+	u32 model_id;
+	u32 model_rev;
+	u64 fw_date;
+	u64 fw_time;
+	u32 fw_id;
+	u32 fw_ver;
+	u32 base_addr;
+	u32 max_size;
+	u64 bld_date;
+	u64 bld_time;
+/* may not used in product
+	u64 dbg_date;
+	u64 dbg_time;
+	u32 dbg_id;
+	u32 dbg_version;
+*/
+} __packed;
+
+static void
+proc_read_hw_info(struct snd_info_entry *entry,
+		  struct snd_info_buffer *buffer)
+{
+	struct snd_bebob *bebob = entry->private_data;
+	struct hw_info *info;
+	int err;
+
+	info = kzalloc(sizeof(struct hw_info), GFP_KERNEL);
+	if (info == NULL)
+		return;
+
+	err = snd_bebob_read_block(bebob->unit, 0,
+				   info, sizeof(struct hw_info));
+	if (err < 0)
+		goto end;
+
+	snd_iprintf(buffer, "Manufacturer:\t%.8s\n",
+		    (char *)&info->manufacturer);
+	snd_iprintf(buffer, "Protocol Ver:\t%d\n", info->protocol_ver);
+	snd_iprintf(buffer, "Build Ver:\t%d\n", info->bld_ver);
+	snd_iprintf(buffer, "GUID:\t\t0x%.8X%.8X\n",
+		    info->guid[0], info->guid[1]);
+	snd_iprintf(buffer, "Model ID:\t0x%02X\n", info->model_id);
+	snd_iprintf(buffer, "Model Rev:\t%d\n", info->model_rev);
+	snd_iprintf(buffer, "Firmware Date:\t%.8s\n", (char *)&info->fw_date);
+	snd_iprintf(buffer, "Firmware Time:\t%.8s\n", (char *)&info->fw_time);
+	snd_iprintf(buffer, "Firmware ID:\t0x%X\n", info->fw_id);
+	snd_iprintf(buffer, "Firmware Ver:\t%d\n", info->fw_ver);
+	snd_iprintf(buffer, "Base Addr:\t0x%X\n", info->base_addr);
+	snd_iprintf(buffer, "Max Size:\t%d\n", info->max_size);
+	snd_iprintf(buffer, "Loader Date:\t%.8s\n", (char *)&info->bld_date);
+	snd_iprintf(buffer, "Loader Time:\t%.8s\n", (char *)&info->bld_time);
+
+end:
+	kfree(info);
+}
+
+static void
+proc_read_formation(struct snd_info_entry *entry,
+		struct snd_info_buffer *buffer)
+{
+	struct snd_bebob *bebob = entry->private_data;
+	struct snd_bebob_stream_formation *formation;
+	unsigned int i;
+
+	snd_iprintf(buffer, "Output Stream from device:\n");
+	snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+	formation = bebob->tx_stream_formations;
+	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+		snd_iprintf(buffer,
+			"\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+			formation[i].pcm, formation[i].midi);
+	}
+
+	snd_iprintf(buffer, "Input Stream to device:\n");
+	snd_iprintf(buffer, "\tRate\tPCM\tMIDI\n");
+	formation = bebob->rx_stream_formations;
+	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+		snd_iprintf(buffer,
+			"\t%d\t%d\t%d\n", snd_bebob_rate_table[i],
+			formation[i].pcm, formation[i].midi);
+	}
+}
+
+static void
+proc_read_clock(struct snd_info_entry *entry,
+		struct snd_info_buffer *buffer)
+{
+	struct snd_bebob *bebob = entry->private_data;
+	unsigned int rate;
+	bool internal;
+
+	if (snd_bebob_stream_get_rate(bebob, &rate) < 0)
+		return;
+	if (snd_bebob_stream_check_internal_clock(bebob, &internal) < 0)
+		return;
+
+	snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)",
+		    (internal) ? "Internal" : "External",
+		    bebob->sync_input_plug);
+	snd_iprintf(buffer, "Sampling rate: %d\n", rate);
+}
+
+void snd_bebob_proc_init(struct snd_bebob *bebob)
+{
+	struct snd_info_entry *entry;
+
+	if (!snd_card_proc_new(bebob->card, "#firmware", &entry))
+		snd_info_set_text_ops(entry, bebob, proc_read_hw_info);
+
+	if (!snd_card_proc_new(bebob->card, "#formation", &entry))
+		snd_info_set_text_ops(entry, bebob, proc_read_formation);
+
+	if (!snd_card_proc_new(bebob->card, "#clock", &entry))
+		snd_info_set_text_ops(entry, bebob, proc_read_clock);
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 30/39] bebob: Add MIDI interface
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (28 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 29/39] bebob: Add proc interface for debugging purpose Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 31/39] bebob: Add PCM interface Takashi Sakamoto
                   ` (9 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds a functionality to capture/playback MIDI messages.

When no AMDTP streams are running, this module starts AMDTP stream at current
sampling rate for MIDI stream.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig            |   1 +
 sound/firewire/bebob/Makefile     |   3 +-
 sound/firewire/bebob/bebob.c      |   7 ++
 sound/firewire/bebob/bebob.h      |   3 +
 sound/firewire/bebob/bebob_midi.c | 148 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 161 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/bebob/bebob_midi.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 9027b23..8081ca1 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -82,6 +82,7 @@ config SND_FIREWORKS
 config SND_BEBOB
 	tristate "BridgeCo DM1000/1500 with BeBoB firmware"
 	select SND_FIREWIRE_LIB
+	select SND_RAWMIDI
         help
 	 Say Y here to include support for FireWire devices based
 	 on BridgeCo DM1000/1500 with BeBoB firmware:
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 757c40e..1e39e59 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,2 +1,3 @@
-snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob.o
+snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+		  bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 81d41b1..dbfbe89 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -167,6 +167,13 @@ bebob_probe(struct fw_unit *unit,
 
 	snd_bebob_proc_init(bebob);
 
+	if ((bebob->midi_input_ports > 0) ||
+	    (bebob->midi_output_ports > 0)) {
+		err = snd_bebob_create_midi_devices(bebob);
+		if (err < 0)
+			goto error;
+	}
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 83ee15a..1e1db6c 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -21,6 +21,7 @@
 #include <sound/core.h>
 #include <sound/initval.h>
 #include <sound/info.h>
+#include <sound/rawmidi.h>
 
 #include "../lib.h"
 #include "../fcp.h"
@@ -178,6 +179,8 @@ void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
 
 void snd_bebob_proc_init(struct snd_bebob *bebob);
 
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
+
 #define SND_BEBOB_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
new file mode 100644
index 0000000..9312b34
--- /dev/null
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -0,0 +1,148 @@
+/*
+ * bebob_midi.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "bebob.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_bebob *bebob = substream->rmidi->private_data;
+
+	bebob->capture_substreams++;
+	return snd_bebob_stream_start_duplex(bebob, &bebob->tx_stream, 0);
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+	struct snd_bebob *bebob = substream->rmidi->private_data;
+
+	bebob->playback_substreams++;
+	return snd_bebob_stream_start_duplex(bebob, &bebob->rx_stream, 0);
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_bebob *bebob = substream->rmidi->private_data;
+
+	bebob->capture_substreams--;
+	snd_bebob_stream_stop_duplex(bebob);
+
+	return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+	struct snd_bebob *bebob = substream->rmidi->private_data;
+
+	bebob->playback_substreams--;
+	snd_bebob_stream_stop_duplex(bebob);
+
+	return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_bebob *bebob = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bebob->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&bebob->tx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&bebob->tx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+	struct snd_bebob *bebob = substrm->rmidi->private_data;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bebob->lock, flags);
+
+	if (up)
+		amdtp_stream_midi_trigger(&bebob->rx_stream,
+					  substrm->number, substrm);
+	else
+		amdtp_stream_midi_trigger(&bebob->rx_stream,
+					  substrm->number, NULL);
+
+	spin_unlock_irqrestore(&bebob->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+	.open		= midi_capture_open,
+	.close		= midi_capture_close,
+	.trigger	= midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+	.open		= midi_playback_open,
+	.close		= midi_playback_close,
+	.trigger	= midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_bebob *bebob,
+				     struct snd_rawmidi_str *str)
+{
+	struct snd_rawmidi_substream *subs;
+
+	list_for_each_entry(subs, &str->substreams, list) {
+		snprintf(subs->name, sizeof(subs->name),
+			 "%s MIDI %d",
+			 bebob->card->shortname, subs->number + 1);
+	}
+}
+
+int snd_bebob_create_midi_devices(struct snd_bebob *bebob)
+{
+	struct snd_rawmidi *rmidi;
+	struct snd_rawmidi_str *str;
+	int err;
+
+	/* create midi ports */
+	err = snd_rawmidi_new(bebob->card, bebob->card->driver, 0,
+			      bebob->midi_output_ports, bebob->midi_input_ports,
+			      &rmidi);
+	if (err < 0)
+		return err;
+
+	snprintf(rmidi->name, sizeof(rmidi->name),
+			"%s MIDI", bebob->card->shortname);
+	rmidi->private_data = bebob;
+
+	if (bebob->midi_input_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+					&midi_capture_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+		set_midi_substream_names(bebob, str);
+	}
+
+	if (bebob->midi_output_ports > 0) {
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+
+		snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+					&midi_playback_ops);
+
+		str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+		set_midi_substream_names(bebob, str);
+	}
+
+	if ((bebob->midi_output_ports > 0) && (bebob->midi_input_ports > 0))
+		rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+	return 0;
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 31/39] bebob: Add PCM interface
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (29 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 30/39] bebob: Add MIDI interface Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 32/39] bebob: Add hwdep interface Takashi Sakamoto
                   ` (8 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit adds a functionality to capture/playback PCM samples.

When AMDTP stream is already running for PCM or the source of clock is not
internal, available sampling rate is limited at current one.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig           |   1 +
 sound/firewire/bebob/Makefile    |   1 +
 sound/firewire/bebob/bebob.c     |   4 +
 sound/firewire/bebob/bebob.h     |   4 +
 sound/firewire/bebob/bebob_pcm.c | 442 +++++++++++++++++++++++++++++++++++++++
 5 files changed, 452 insertions(+)
 create mode 100644 sound/firewire/bebob/bebob_pcm.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 8081ca1..022fee0 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -83,6 +83,7 @@ config SND_BEBOB
 	tristate "BridgeCo DM1000/1500 with BeBoB firmware"
 	select SND_FIREWIRE_LIB
 	select SND_RAWMIDI
+	select SND_PCM
         help
 	 Say Y here to include support for FireWire devices based
 	 on BridgeCo DM1000/1500 with BeBoB firmware:
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 1e39e59..533718a 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,3 +1,4 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
+		  bebob_pcm.o \
 		  bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index dbfbe89..777ccfc 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -174,6 +174,10 @@ bebob_probe(struct fw_unit *unit,
 			goto error;
 	}
 
+	err = snd_bebob_create_pcm_devices(bebob);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 1e1db6c..b310c85 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -22,6 +22,8 @@
 #include <sound/initval.h>
 #include <sound/info.h>
 #include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
 
 #include "../lib.h"
 #include "../fcp.h"
@@ -181,6 +183,8 @@ void snd_bebob_proc_init(struct snd_bebob *bebob);
 
 int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
 
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
+
 #define SND_BEBOB_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
new file mode 100644
index 0000000..db9c87f
--- /dev/null
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -0,0 +1,442 @@
+/*
+ * bebob_pcm.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+static int
+hw_rule_rate(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule,
+	     struct snd_bebob *bebob,
+	     struct snd_bebob_stream_formation *formations)
+{
+	struct snd_interval *r =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+	const struct snd_interval *c =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	struct snd_interval t = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+	unsigned int i;
+
+	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+		/* entry is invalid */
+		if (formations[i].pcm == 0)
+			continue;
+
+		if (!snd_interval_test(c, formations[i].pcm))
+			continue;
+
+		t.min = min(t.min, snd_bebob_rate_table[i]);
+		t.max = max(t.max, snd_bebob_rate_table[i]);
+
+	}
+	return snd_interval_refine(r, &t);
+}
+
+static int
+hw_rule_channels(struct snd_pcm_hw_params *params, struct snd_pcm_hw_rule *rule,
+		 struct snd_bebob *bebob,
+		 struct snd_bebob_stream_formation *formations)
+{
+	struct snd_interval *c =
+		hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+	const struct snd_interval *r =
+		hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+	struct snd_interval t = {
+		.min = UINT_MAX, .max = 0, .integer = 1
+	};
+
+	unsigned int i;
+
+	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+		/* entry is invalid */
+		if (formations[i].pcm == 0)
+			continue;
+
+		if (!snd_interval_test(r, snd_bebob_rate_table[i]))
+			continue;
+
+		t.min = min(t.min, formations[i].pcm);
+		t.max = max(t.max, formations[i].pcm);
+	}
+
+	return snd_interval_refine(c, &t);
+}
+
+static inline int
+hw_rule_capture_rate(struct snd_pcm_hw_params *params,
+				struct snd_pcm_hw_rule *rule)
+{
+	struct snd_bebob *bebob = rule->private;
+	return hw_rule_rate(params, rule, bebob,
+				bebob->tx_stream_formations);
+}
+
+static inline int
+hw_rule_playback_rate(struct snd_pcm_hw_params *params,
+				struct snd_pcm_hw_rule *rule)
+{
+	struct snd_bebob *bebob = rule->private;
+	return hw_rule_rate(params, rule, bebob,
+				bebob->rx_stream_formations);
+}
+
+static inline int
+hw_rule_capture_channels(struct snd_pcm_hw_params *params,
+				struct snd_pcm_hw_rule *rule)
+{
+	struct snd_bebob *bebob = rule->private;
+	return hw_rule_channels(params, rule, bebob,
+				bebob->tx_stream_formations);
+}
+
+static inline int
+hw_rule_playback_channels(struct snd_pcm_hw_params *params,
+				struct snd_pcm_hw_rule *rule)
+{
+	struct snd_bebob *bebob = rule->private;
+	return hw_rule_channels(params, rule, bebob,
+				bebob->rx_stream_formations);
+}
+
+static void
+prepare_channels(struct snd_pcm_hardware *hw,
+	  struct snd_bebob_stream_formation *formations)
+{
+	unsigned int i;
+
+	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+		/* entry has no PCM channels */
+		if (formations[i].pcm == 0)
+			continue;
+
+		hw->channels_min = min(hw->channels_min, formations[i].pcm);
+		hw->channels_max = max(hw->channels_max, formations[i].pcm);
+	}
+}
+
+static void
+prepare_rates(struct snd_pcm_hardware *hw,
+	  struct snd_bebob_stream_formation *formations)
+{
+	unsigned int i;
+
+	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+		/* entry has no PCM channels */
+		if (formations[i].pcm == 0)
+			continue;
+
+		hw->rate_min = min(hw->rate_min, snd_bebob_rate_table[i]);
+		hw->rate_max = max(hw->rate_max, snd_bebob_rate_table[i]);
+		hw->rates |= snd_pcm_rate_to_rate_bit(snd_bebob_rate_table[i]);
+	}
+}
+
+static int
+pcm_init_hw_params(struct snd_bebob *bebob,
+			struct snd_pcm_substream *substream)
+{
+	int err;
+
+	static const struct snd_pcm_hardware hw = {
+		.info = SNDRV_PCM_INFO_BATCH |
+			SNDRV_PCM_INFO_BLOCK_TRANSFER |
+			SNDRV_PCM_INFO_INTERLEAVED |
+			SNDRV_PCM_INFO_JOINT_DUPLEX |
+			SNDRV_PCM_INFO_MMAP |
+			SNDRV_PCM_INFO_MMAP_VALID,
+		/* set up later */
+		.rates = 0,
+		.rate_min = UINT_MAX,
+		.rate_max = 0,
+		/* set up later */
+		.channels_min = UINT_MAX,
+		.channels_max = 0,
+		.buffer_bytes_max = 4 * 16 * 2048 * 2,
+		.period_bytes_min = 1,
+		.period_bytes_max = 4 * 16 * 2048,
+		.periods_min = 2,
+		.periods_max = UINT_MAX,
+	};
+
+	substream->runtime->hw = hw;
+
+	/* add rule between channels and sampling rate */
+	if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+		prepare_rates(&substream->runtime->hw,
+			      bebob->tx_stream_formations);
+		prepare_channels(&substream->runtime->hw,
+				 bebob->tx_stream_formations);
+		substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32_LE;
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_CHANNELS,
+				    hw_rule_capture_channels, bebob,
+				    SNDRV_PCM_HW_PARAM_RATE, -1);
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_RATE,
+				    hw_rule_capture_rate, bebob,
+				    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	} else {
+		prepare_rates(&substream->runtime->hw,
+			      bebob->rx_stream_formations);
+		prepare_channels(&substream->runtime->hw,
+				 bebob->rx_stream_formations);
+		substream->runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_CHANNELS,
+				    hw_rule_playback_channels, bebob,
+				    SNDRV_PCM_HW_PARAM_RATE, -1);
+		snd_pcm_hw_rule_add(substream->runtime, 0,
+				    SNDRV_PCM_HW_PARAM_RATE,
+				    hw_rule_playback_rate, bebob,
+				    SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+	}
+
+	/* AM824 in IEC 61883-6 can deliver 24bit data */
+	err = snd_pcm_hw_constraint_msbits(substream->runtime, 0, 32, 24);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * AMDTP functionality in firewire-lib require periods to be aligned to
+	 * 16 bit, or 24bit inner 32bit.
+	 */
+	err = snd_pcm_hw_constraint_step(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
+	if (err < 0)
+		goto end;
+	err = snd_pcm_hw_constraint_step(substream->runtime, 0,
+					 SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * Currently INTERRUPT_INTERVAL in amdtp.c is 16.
+	 * So snd_pcm_period_elapsed() can be called every 2m sec.
+	 */
+	err = snd_pcm_hw_constraint_minmax(substream->runtime,
+					   SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+					   2000, UINT_MAX);
+end:
+	return err;
+}
+
+static int
+pcm_open(struct snd_pcm_substream *substream)
+{
+	struct snd_bebob *bebob = substream->private_data;
+	unsigned int sampling_rate;
+	bool internal;
+	int err;
+
+	err = pcm_init_hw_params(bebob, substream);
+	if (err < 0)
+		goto end;
+
+	err = snd_bebob_stream_check_internal_clock(bebob, &internal);
+	if (err < 0)
+		goto end;
+
+	/*
+	 * When source of clock is internal or any PCM stream are running,
+	 * the available sampling rate is limited at current sampling rate.
+	 */
+	if (!internal ||
+	    amdtp_stream_pcm_running(&bebob->tx_stream) ||
+	    amdtp_stream_pcm_running(&bebob->rx_stream)) {
+		err = snd_bebob_stream_get_rate(bebob, &sampling_rate);
+		if (err < 0)
+			goto end;
+
+		substream->runtime->hw.rate_min = sampling_rate;
+		substream->runtime->hw.rate_max = sampling_rate;
+	}
+
+	snd_pcm_set_sync(substream);
+
+end:
+	return err;
+}
+
+static int
+pcm_close(struct snd_pcm_substream *substream)
+{
+	return 0;
+}
+
+static int
+pcm_capture_hw_params(struct snd_pcm_substream *substream,
+		      struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_bebob *bebob = substream->private_data;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		bebob->capture_substreams++;
+	amdtp_stream_set_pcm_format(&bebob->tx_stream,
+				    params_format(hw_params));
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
+}
+static int
+pcm_playback_hw_params(struct snd_pcm_substream *substream,
+		       struct snd_pcm_hw_params *hw_params)
+{
+	struct snd_bebob *bebob = substream->private_data;
+
+	if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
+		bebob->playback_substreams++;
+	amdtp_stream_set_pcm_format(&bebob->rx_stream,
+				    params_format(hw_params));
+	return snd_pcm_lib_alloc_vmalloc_buffer(substream,
+						params_buffer_bytes(hw_params));
+}
+
+static int
+pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_bebob *bebob = substream->private_data;
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		bebob->capture_substreams--;
+
+	snd_bebob_stream_stop_duplex(bebob);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+static int
+pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+	struct snd_bebob *bebob = substream->private_data;
+
+	if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+		bebob->playback_substreams--;
+
+	snd_bebob_stream_stop_duplex(bebob);
+
+	return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int
+pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_bebob *bebob = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	err = snd_bebob_stream_start_duplex(bebob, &bebob->tx_stream,
+					    runtime->rate);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&bebob->tx_stream);
+
+	return err;
+}
+static int
+pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+	struct snd_bebob *bebob = substream->private_data;
+	struct snd_pcm_runtime *runtime = substream->runtime;
+	int err;
+
+	err = snd_bebob_stream_start_duplex(bebob, &bebob->rx_stream,
+					    runtime->rate);
+	if (err >= 0)
+		amdtp_stream_pcm_prepare(&bebob->rx_stream);
+
+	return err;
+}
+
+static int
+pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_bebob *bebob = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&bebob->tx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&bebob->tx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+static int
+pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+	struct snd_bebob *bebob = substream->private_data;
+
+	switch (cmd) {
+	case SNDRV_PCM_TRIGGER_START:
+		amdtp_stream_pcm_trigger(&bebob->rx_stream, substream);
+		break;
+	case SNDRV_PCM_TRIGGER_STOP:
+		amdtp_stream_pcm_trigger(&bebob->rx_stream, NULL);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static snd_pcm_uframes_t
+pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+	struct snd_bebob *bebob = sbstrm->private_data;
+	return amdtp_stream_pcm_pointer(&bebob->tx_stream);
+}
+static snd_pcm_uframes_t
+pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+	struct snd_bebob *bebob = sbstrm->private_data;
+	return amdtp_stream_pcm_pointer(&bebob->rx_stream);
+}
+
+static const struct snd_pcm_ops pcm_capture_ops = {
+	.open		= pcm_open,
+	.close		= pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= pcm_capture_hw_params,
+	.hw_free	= pcm_capture_hw_free,
+	.prepare	= pcm_capture_prepare,
+	.trigger	= pcm_capture_trigger,
+	.pointer	= pcm_capture_pointer,
+	.page		= snd_pcm_lib_get_vmalloc_page,
+};
+static const struct snd_pcm_ops pcm_playback_ops = {
+	.open		= pcm_open,
+	.close		= pcm_close,
+	.ioctl		= snd_pcm_lib_ioctl,
+	.hw_params	= pcm_playback_hw_params,
+	.hw_free	= pcm_playback_hw_free,
+	.prepare	= pcm_playback_prepare,
+	.trigger	= pcm_playback_trigger,
+	.pointer	= pcm_playback_pointer,
+	.page		= snd_pcm_lib_get_vmalloc_page,
+	.mmap		= snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_bebob_create_pcm_devices(struct snd_bebob *bebob)
+{
+	struct snd_pcm *pcm;
+	int err;
+
+	err = snd_pcm_new(bebob->card, bebob->card->driver, 0, 1, 1, &pcm);
+	if (err < 0)
+		goto end;
+
+	pcm->private_data = bebob;
+	snprintf(pcm->name, sizeof(pcm->name),
+		 "%s PCM", bebob->card->shortname);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+	snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+end:
+	return err;
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 32/39] bebob: Add hwdep interface
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (30 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 31/39] bebob: Add PCM interface Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 33/39] bebob: Prepare for device specific operations Takashi Sakamoto
                   ` (7 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This interface is designed for mixer/control application. To use hwdep
interface, the application can get information about firewire node, can
lock/unlock kernel streaming and can get notification at starting/stopping
kernel streaming.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 include/uapi/sound/asound.h         |   3 +-
 include/uapi/sound/firewire.h       |   1 +
 sound/firewire/Kconfig              |   1 +
 sound/firewire/bebob/Makefile       |   2 +-
 sound/firewire/bebob/bebob.c        |   5 +
 sound/firewire/bebob/bebob.h        |  13 +++
 sound/firewire/bebob/bebob_hwdep.c  | 197 ++++++++++++++++++++++++++++++++++++
 sound/firewire/bebob/bebob_midi.c   |  26 ++++-
 sound/firewire/bebob/bebob_pcm.c    |  16 ++-
 sound/firewire/bebob/bebob_stream.c |  39 +++++++
 10 files changed, 295 insertions(+), 8 deletions(-)
 create mode 100644 sound/firewire/bebob/bebob_hwdep.c

diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h
index 5dfbfdf..2249483 100644
--- a/include/uapi/sound/asound.h
+++ b/include/uapi/sound/asound.h
@@ -95,9 +95,10 @@ enum {
 	SNDRV_HWDEP_IFACE_USB_STREAM,	/* direct access to usb stream */
 	SNDRV_HWDEP_IFACE_FW_DICE,	/* TC DICE FireWire device */
 	SNDRV_HWDEP_IFACE_FW_FIREWORKS,	/* Echo Audio Fireworks based device */
+	SNDRV_HWDEP_IFACE_FW_BEBOB,	/* BridgeCo BeBoB based device */
 
 	/* Don't forget to change the following: */
-	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_FIREWORKS
+	SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_BEBOB
 };
 
 struct snd_hwdep_info {
diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
index 7f4c419..d69c0b6 100644
--- a/include/uapi/sound/firewire.h
+++ b/include/uapi/sound/firewire.h
@@ -53,6 +53,7 @@ union snd_firewire_event {
 
 #define SNDRV_FIREWIRE_TYPE_DICE	1
 #define SNDRV_FIREWIRE_TYPE_FIREWORKS	2
+#define SNDRV_FIREWIRE_TYPE_BEBOB	3
 /* AV/C, RME, MOTU, ... */
 
 struct snd_firewire_get_info {
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 022fee0..060fe7e 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -84,6 +84,7 @@ config SND_BEBOB
 	select SND_FIREWIRE_LIB
 	select SND_RAWMIDI
 	select SND_PCM
+	select SND_HWDEP
         help
 	 Say Y here to include support for FireWire devices based
 	 on BridgeCo DM1000/1500 with BeBoB firmware:
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 533718a..7808777 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,4 +1,4 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
-		  bebob_pcm.o \
+		  bebob_pcm.o bebob_hwdep.o \
 		  bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 777ccfc..355bb5b 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -152,6 +152,7 @@ bebob_probe(struct fw_unit *unit,
 	bebob->card_index = -1;
 	mutex_init(&bebob->mutex);
 	spin_lock_init(&bebob->lock);
+	init_waitqueue_head(&bebob->hwdep_wait);
 
 	err = name_device(bebob, entry->vendor_id);
 	if (err < 0)
@@ -178,6 +179,10 @@ bebob_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
+	err = snd_bebob_create_hwdep_device(bebob);
+	if (err < 0)
+		goto error;
+
 	err = snd_card_register(card);
 	if (err < 0) {
 		snd_card_free(card);
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index b310c85..933e7e6 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -24,6 +24,8 @@
 #include <sound/rawmidi.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
 
 #include "../lib.h"
 #include "../fcp.h"
@@ -70,6 +72,11 @@ struct snd_bebob {
 		rx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
 
 	int sync_input_plug;
+
+	/* for uapi */
+	int dev_lock_count;
+	bool dev_lock_changed;
+	wait_queue_head_t hwdep_wait;
 };
 
 static inline int
@@ -179,12 +186,18 @@ int snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
 
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob);
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob);
+
 void snd_bebob_proc_init(struct snd_bebob *bebob);
 
 int snd_bebob_create_midi_devices(struct snd_bebob *bebob);
 
 int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
 
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
+
 #define SND_BEBOB_DEV_ENTRY(vendor, model) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/bebob/bebob_hwdep.c b/sound/firewire/bebob/bebob_hwdep.c
new file mode 100644
index 0000000..54937c0
--- /dev/null
+++ b/sound/firewire/bebob/bebob_hwdep.c
@@ -0,0 +1,197 @@
+/*
+ * bebob_hwdep.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node infomation
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "bebob.h"
+
+static long
+hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
+	   loff_t *offset)
+{
+	struct snd_bebob *bebob = hwdep->private_data;
+	DEFINE_WAIT(wait);
+	union snd_firewire_event event;
+
+	spin_lock_irq(&bebob->lock);
+
+	while (!bebob->dev_lock_changed) {
+		prepare_to_wait(&bebob->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+		spin_unlock_irq(&bebob->lock);
+		schedule();
+		finish_wait(&bebob->hwdep_wait, &wait);
+		if (signal_pending(current))
+			return -ERESTARTSYS;
+		spin_lock_irq(&bebob->lock);
+	}
+
+	memset(&event, 0, sizeof(event));
+	if (bebob->dev_lock_changed) {
+		event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+		event.lock_status.status = (bebob->dev_lock_count > 0);
+		bebob->dev_lock_changed = false;
+
+		count = min_t(long, count, sizeof(event.lock_status));
+	}
+
+	spin_unlock_irq(&bebob->lock);
+
+	if (copy_to_user(buf, &event, count))
+		return -EFAULT;
+
+	return count;
+}
+
+static unsigned int
+hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
+{
+	struct snd_bebob *bebob = hwdep->private_data;
+	unsigned int events;
+
+	poll_wait(file, &bebob->hwdep_wait, wait);
+
+	spin_lock_irq(&bebob->lock);
+	if (bebob->dev_lock_changed)
+		events = POLLIN | POLLRDNORM;
+	else
+		events = 0;
+	spin_unlock_irq(&bebob->lock);
+
+	return events;
+}
+
+static int
+hwdep_get_info(struct snd_bebob *bebob, void __user *arg)
+{
+	struct fw_device *dev = fw_parent_device(bebob->unit);
+	struct snd_firewire_get_info info;
+
+	memset(&info, 0, sizeof(info));
+	info.type = SNDRV_FIREWIRE_TYPE_BEBOB;
+	info.card = dev->card->index;
+	*(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+	*(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+	strlcpy(info.device_name, dev_name(&dev->device),
+		sizeof(info.device_name));
+
+	if (copy_to_user(arg, &info, sizeof(info)))
+		return -EFAULT;
+
+	return 0;
+}
+
+static int
+hwdep_lock(struct snd_bebob *bebob)
+{
+	int err;
+
+	spin_lock_irq(&bebob->lock);
+
+	if (bebob->dev_lock_count == 0) {
+		bebob->dev_lock_count = -1;
+		err = 0;
+	} else
+		err = -EBUSY;
+
+	spin_unlock_irq(&bebob->lock);
+
+	return err;
+}
+
+static int
+hwdep_unlock(struct snd_bebob *bebob)
+{
+	int err;
+
+	spin_lock_irq(&bebob->lock);
+
+	if (bebob->dev_lock_count == -1) {
+		bebob->dev_lock_count = 0;
+		err = 0;
+	} else
+		err = -EBADFD;
+
+	spin_unlock_irq(&bebob->lock);
+
+	return err;
+}
+
+static int
+hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+	struct snd_bebob *bebob = hwdep->private_data;
+
+	spin_lock_irq(&bebob->lock);
+	if (bebob->dev_lock_count == -1)
+		bebob->dev_lock_count = 0;
+	spin_unlock_irq(&bebob->lock);
+
+	return 0;
+}
+
+static int
+hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+	    unsigned int cmd, unsigned long arg)
+{
+	struct snd_bebob *bebob = hwdep->private_data;
+
+	switch (cmd) {
+	case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+		return hwdep_get_info(bebob, (void __user *)arg);
+	case SNDRV_FIREWIRE_IOCTL_LOCK:
+		return hwdep_lock(bebob);
+	case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+		return hwdep_unlock(bebob);
+	default:
+		return -ENOIOCTLCMD;
+	}
+}
+
+#ifdef CONFIG_COMPAT
+static int
+hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+		   unsigned int cmd, unsigned long arg)
+{
+	return hwdep_ioctl(hwdep, file, cmd,
+			   (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+	.read		= hwdep_read,
+	.release	= hwdep_release,
+	.poll		= hwdep_poll,
+	.ioctl		= hwdep_ioctl,
+	.ioctl_compat	= hwdep_compat_ioctl,
+};
+
+int snd_bebob_create_hwdep_device(struct snd_bebob *bebob)
+{
+	struct snd_hwdep *hwdep;
+	int err;
+
+	err = snd_hwdep_new(bebob->card, "BeBoB", 0, &hwdep);
+	if (err < 0)
+		goto end;
+	strcpy(hwdep->name, "BeBoB");
+	hwdep->iface = SNDRV_HWDEP_IFACE_FW_BEBOB;
+	hwdep->ops = hwdep_ops;
+	hwdep->private_data = bebob;
+	hwdep->exclusive = true;
+end:
+	return err;
+}
+
diff --git a/sound/firewire/bebob/bebob_midi.c b/sound/firewire/bebob/bebob_midi.c
index 9312b34..8943565 100644
--- a/sound/firewire/bebob/bebob_midi.c
+++ b/sound/firewire/bebob/bebob_midi.c
@@ -11,17 +11,37 @@
 static int midi_capture_open(struct snd_rawmidi_substream *substream)
 {
 	struct snd_bebob *bebob = substream->rmidi->private_data;
+	int err;
+
+	err = snd_bebob_stream_lock_try(bebob);
+	if (err < 0)
+		goto end;
 
 	bebob->capture_substreams++;
-	return snd_bebob_stream_start_duplex(bebob, &bebob->tx_stream, 0);
+	err = snd_bebob_stream_start_duplex(bebob,
+					    &bebob->tx_stream, 0);
+	if (err < 0)
+		snd_bebob_stream_lock_release(bebob);
+end:
+	return err;
 }
 
 static int midi_playback_open(struct snd_rawmidi_substream *substream)
 {
 	struct snd_bebob *bebob = substream->rmidi->private_data;
+	int err;
+
+	err = snd_bebob_stream_lock_try(bebob);
+	if (err < 0)
+		goto end;
 
 	bebob->playback_substreams++;
-	return snd_bebob_stream_start_duplex(bebob, &bebob->rx_stream, 0);
+	err = snd_bebob_stream_start_duplex(bebob,
+					    &bebob->rx_stream, 0);
+	if (err < 0)
+		snd_bebob_stream_lock_release(bebob);
+end:
+	return err;
 }
 
 static int midi_capture_close(struct snd_rawmidi_substream *substream)
@@ -31,6 +51,7 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 	bebob->capture_substreams--;
 	snd_bebob_stream_stop_duplex(bebob);
 
+	snd_bebob_stream_lock_release(bebob);
 	return 0;
 }
 
@@ -41,6 +62,7 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 	bebob->playback_substreams--;
 	snd_bebob_stream_stop_duplex(bebob);
 
+	snd_bebob_stream_lock_release(bebob);
 	return 0;
 }
 
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index db9c87f..9e0b12c 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -233,13 +233,17 @@ pcm_open(struct snd_pcm_substream *substream)
 	bool internal;
 	int err;
 
-	err = pcm_init_hw_params(bebob, substream);
+	err = snd_bebob_stream_lock_try(bebob);
 	if (err < 0)
 		goto end;
 
+	err = pcm_init_hw_params(bebob, substream);
+	if (err < 0)
+		goto err_locked;
+
 	err = snd_bebob_stream_check_internal_clock(bebob, &internal);
 	if (err < 0)
-		goto end;
+		goto err_locked;
 
 	/*
 	 * When source of clock is internal or any PCM stream are running,
@@ -250,21 +254,25 @@ pcm_open(struct snd_pcm_substream *substream)
 	    amdtp_stream_pcm_running(&bebob->rx_stream)) {
 		err = snd_bebob_stream_get_rate(bebob, &sampling_rate);
 		if (err < 0)
-			goto end;
+			goto err_locked;
 
 		substream->runtime->hw.rate_min = sampling_rate;
 		substream->runtime->hw.rate_max = sampling_rate;
 	}
 
 	snd_pcm_set_sync(substream);
-
 end:
 	return err;
+err_locked:
+	snd_bebob_stream_lock_release(bebob);
+	return err;
 }
 
 static int
 pcm_close(struct snd_pcm_substream *substream)
 {
+	struct snd_bebob *bebob = substream->private_data;
+	snd_bebob_stream_lock_release(bebob);
 	return 0;
 }
 
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 497d1c6..797fcb0 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -782,3 +782,42 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob)
 end:
 	return err;
 }
+
+void snd_bebob_stream_lock_changed(struct snd_bebob *bebob)
+{
+	bebob->dev_lock_changed = true;
+	wake_up(&bebob->hwdep_wait);
+}
+
+int snd_bebob_stream_lock_try(struct snd_bebob *bebob)
+{
+	int err;
+
+	spin_lock_irq(&bebob->lock);
+
+	/* user land lock this */
+	if (bebob->dev_lock_count < 0) {
+		err = -EBUSY;
+		goto end;
+	}
+
+	/* this is the first time */
+	if (bebob->dev_lock_count++ == 0)
+		snd_bebob_stream_lock_changed(bebob);
+	err = 0;
+end:
+	spin_unlock_irq(&bebob->lock);
+	return err;
+}
+
+void snd_bebob_stream_lock_release(struct snd_bebob *bebob)
+{
+	spin_lock_irq(&bebob->lock);
+
+	if (WARN_ON(bebob->dev_lock_count <= 0))
+		goto end;
+	if (--bebob->dev_lock_count == 0)
+		snd_bebob_stream_lock_changed(bebob);
+end:
+	spin_unlock_irq(&bebob->lock);
+}
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 33/39] bebob: Prepare for device specific operations
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (31 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 32/39] bebob: Add hwdep interface Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 34/39] bebob: Add support for Terratec PHASE, EWS series and Aureon Takashi Sakamoto
                   ` (6 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit is for some devices which have its own operations or quirks.

Many functionality should be implemented in user land. Then this commit adds
functionality related to stream such as sampling frequency or clock source. For
help to debug, this commit adds the functionality to get metering information
if it's available.

To help these functionalities, this commit adds some AV/C commands defined in
'AV/C Audio Subunit Specification (1394TA).

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/bebob/Makefile        |  3 +-
 sound/firewire/bebob/bebob.c         | 72 ++++++++++++++++++++------------
 sound/firewire/bebob/bebob.h         | 33 ++++++++++++++-
 sound/firewire/bebob/bebob_command.c | 79 ++++++++++++++++++++++++++++++++++++
 sound/firewire/bebob/bebob_pcm.c     |  3 +-
 sound/firewire/bebob/bebob_proc.c    | 62 ++++++++++++++++++++++++----
 sound/firewire/bebob/bebob_stream.c  | 26 +++++++++---
 7 files changed, 235 insertions(+), 43 deletions(-)

diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 7808777..e4b08e3 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,4 +1,3 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
-		  bebob_pcm.o bebob_hwdep.o \
-		  bebob.o
+		  bebob_pcm.o bebob_hwdep.o bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 355bb5b..581a694 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -126,6 +126,7 @@ bebob_probe(struct fw_unit *unit,
 {
 	struct snd_card *card;
 	struct snd_bebob *bebob;
+	const struct snd_bebob_spec *spec;
 	unsigned int card_index;
 	int err;
 
@@ -140,6 +141,12 @@ bebob_probe(struct fw_unit *unit,
 		goto end;
 	}
 
+	spec = (const struct snd_bebob_spec *)entry->driver_data;
+	if (spec == NULL) {
+		err = -ENOSYS;
+		goto end;
+	}
+
 	err = snd_card_new(&unit->device, index[card_index], id[card_index],
 			   THIS_MODULE, sizeof(struct snd_bebob), &card);
 	if (err < 0)
@@ -150,6 +157,7 @@ bebob_probe(struct fw_unit *unit,
 	bebob->card = card;
 	bebob->unit = unit;
 	bebob->card_index = -1;
+	bebob->spec = spec;
 	mutex_init(&bebob->mutex);
 	spin_lock_init(&bebob->lock);
 	init_waitqueue_head(&bebob->hwdep_wait);
@@ -217,62 +225,72 @@ static void bebob_remove(struct fw_unit *unit)
 	snd_card_free_when_closed(bebob->card);
 }
 
+struct snd_bebob_rate_spec normal_rate_spec = {
+	.get	= &snd_bebob_stream_get_rate,
+	.set	= &snd_bebob_stream_set_rate
+};
+static const struct snd_bebob_spec spec_normal = {
+	.clock	= NULL,
+	.rate	= &normal_rate_spec,
+	.meter	= NULL
+};
+
 static const struct ieee1394_device_id bebob_id_table[] = {
 	/* Edirol, FA-66 */
-	SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049),
+	SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010049, &spec_normal),
 	/* Edirol, FA-101 */
-	SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048),
+	SND_BEBOB_DEV_ENTRY(VEN_EDIROL, 0x00010048, &spec_normal),
 	/* Presonus, FIREBOX */
-	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000),
+	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010000, &spec_normal),
 	/* PreSonus, FIREPOD/FP10 */
-	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066),
+	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010066, &spec_normal),
 	/* PreSonus, Inspire1394 */
-	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001),
+	SND_BEBOB_DEV_ENTRY(VEN_PRESONUS, 0x00010001, &spec_normal),
 	/* BridgeCo, RDAudio1 */
-	SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048),
+	SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010048, &spec_normal),
 	/* BridgeCo, Audio5 */
-	SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049),
+	SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
 	/* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
-	SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065),
+	SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
 	/* Mackie, d.2 (Firewire Option) */
-	SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067),
+	SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
 	/* Stanton, ScratchAmp */
-	SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001),
+	SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
 	/* Tascam, IF-FW DM */
-	SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067),
+	SND_BEBOB_DEV_ENTRY(VEN_TASCAM, 0x00010067, &spec_normal),
 	/* Behringer, XENIX UFX 1204 */
-	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204),
+	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001204, &spec_normal),
 	/* Behringer, XENIX UFX 1604 */
-	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604),
+	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00001604, &spec_normal),
 	/* Behringer, Digital Mixer X32 series (X-UF Card) */
-	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006),
+	SND_BEBOB_DEV_ENTRY(VEN_BEHRINGER, 0x00000006, &spec_normal),
 	/* Apogee Electronics, Rosetta 200/400 (X-FireWire card) */
 	/* Apogee Electronics, DA/AD/DD-16X (X-FireWire card) */
-	SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048),
+	SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00010048, &spec_normal),
 	/* Apogee Electronics, Ensemble */
-	SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee),
+	SND_BEBOB_DEV_ENTRY(VEN_APOGEE, 0x00001eee, &spec_normal),
 	/* ESI, Quatafire610 */
-	SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064),
+	SND_BEBOB_DEV_ENTRY(VEN_ESI, 0x00010064, &spec_normal),
 	/* AcousticReality, eARMasterOne */
-	SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002),
+	SND_BEBOB_DEV_ENTRY(VEN_ACOUSTIC, 0x00000002, &spec_normal),
 	/* CME, MatrixKFW */
-	SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000),
+	SND_BEBOB_DEV_ENTRY(VEN_CME, 0x00030000, &spec_normal),
 	/* Phonic, Helix Board 12 MkII */
-	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000),
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00050000, &spec_normal),
 	/* Phonic, Helix Board 18 MkII */
-	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000),
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00060000, &spec_normal),
 	/* Phonic, Helix Board 24 MkII */
-	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000),
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00070000, &spec_normal),
 	/* Phonic, Helix Board 12 Universal/18 Universal/24 Universal */
-	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000),
+	SND_BEBOB_DEV_ENTRY(VEN_PHONIC, 0x00000000, &spec_normal),
 	/* Lynx, Aurora 8/16 (LT-FW) */
-	SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001),
+	SND_BEBOB_DEV_ENTRY(VEN_LYNX, 0x00000001, &spec_normal),
 	/* ICON, FireXon */
-	SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001),
+	SND_BEBOB_DEV_ENTRY(VEN_ICON, 0x00000001, &spec_normal),
 	/* PrismSound, Orpheus */
-	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048),
+	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal),
 	/* PrismSound, ADA-8XR */
-	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8),
+	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal),
 	/* IDs are unknown but able to be supported */
 	/*  Apogee, Mini-ME Firewire */
 	/*  Apogee, Mini-DAC Firewire */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 933e7e6..6976097 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -48,6 +48,28 @@ struct snd_bebob_stream_formation {
 /* this is a lookup table for index of stream formations */
 extern const unsigned int snd_bebob_rate_table[SND_BEBOB_STRM_FMT_ENTRIES];
 
+/* device specific operations */
+#define SND_BEBOB_CLOCK_INTERNAL	"Internal"
+struct snd_bebob_clock_spec {
+	unsigned int num;
+	char **labels;
+	int (*get)(struct snd_bebob *bebob, unsigned int *id);
+};
+struct snd_bebob_rate_spec {
+	int (*get)(struct snd_bebob *bebob, unsigned int *rate);
+	int (*set)(struct snd_bebob *bebob, unsigned int rate);
+};
+struct snd_bebob_meter_spec {
+	unsigned int num;
+	char **labels;
+	int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
+};
+struct snd_bebob_spec {
+	struct snd_bebob_clock_spec *clock;
+	struct snd_bebob_rate_spec *rate;
+	struct snd_bebob_meter_spec *meter;
+};
+
 struct snd_bebob {
 	struct snd_card *card;
 	struct fw_unit *unit;
@@ -56,6 +78,8 @@ struct snd_bebob {
 	struct mutex mutex;
 	spinlock_t lock;
 
+	const struct snd_bebob_spec *spec;
+
 	unsigned int midi_input_ports;
 	unsigned int midi_output_ports;
 
@@ -95,6 +119,12 @@ snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
 				  (void *)buf, sizeof(u32), 0);
 }
 
+/* AV/C Audio Subunit Specification 1.0 (Oct 2000, 1394TA) */
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+			   unsigned int fb_id, unsigned int num);
+int avc_audio_get_selector(struct fw_unit *unit, unsigned  int subunit_id,
+			   unsigned int fb_id, unsigned int *num);
+
 /*
  * AVC command extensions, AV/C Unit and Subunit, Revision 17
  * (Nov 2003, BridgeCo)
@@ -198,12 +228,13 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
 
 int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
 
-#define SND_BEBOB_DEV_ENTRY(vendor, model) \
+#define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
 			  IEEE1394_MATCH_MODEL_ID, \
 	.vendor_id	= vendor, \
 	.model_id	= model, \
+	.driver_data	= (kernel_ulong_t)data \
 }
 
 #endif
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
index 22a86d0..c63c6d8 100644
--- a/sound/firewire/bebob/bebob_command.c
+++ b/sound/firewire/bebob/bebob_command.c
@@ -11,6 +11,85 @@
 #define BEBOB_COMMAND_MAX_TRIAL	3
 #define BEBOB_COMMAND_WAIT_MSEC	100
 
+int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
+			   unsigned int fb_id, unsigned int num)
+{
+	u8 *buf;
+	int err;
+
+	buf = kzalloc(12, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0]  = 0x00;		/* AV/C CONTROL */
+	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
+	buf[2]  = 0xb8;		/* FUNCTION BLOCK  */
+	buf[3]  = 0x80;		/* type is 'selector'*/
+	buf[4]  = 0xff & fb_id;	/* function block id */
+	buf[5]  = 0x10;		/* control attribute is CURRENT */
+	buf[6]  = 0x02;		/* selector length is 2 */
+	buf[7]  = 0xff & num;	/* input function block plug number */
+	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
+
+	/* do transaction and check buf[1-8] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(7) | BIT(8));
+	if (err < 0)
+		goto end;
+	if ((err < 6) || (buf[0] != 0x09)) {
+		dev_err(&unit->device,
+			"failed to set selector %d: 0x%02X\n",
+			fb_id, buf[0]);
+		err = -EIO;
+		goto end;
+	}
+end:
+	kfree(buf);
+	return err;
+}
+
+int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
+			   unsigned int fb_id, unsigned int *num)
+{
+	u8 *buf;
+	int err;
+
+	buf = kzalloc(12, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0]  = 0x01;		/* AV/C STATUS */
+	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
+	buf[2]  = 0xb8;		/* FUNCTION BLOCK */
+	buf[3]  = 0x80;		/* type is 'selector'*/
+	buf[4]  = 0xff & fb_id;	/* function block id */
+	buf[5]  = 0x10;		/* control attribute is CURRENT */
+	buf[6]  = 0x02;		/* selector length is 2 */
+	buf[7]  = 0xff;		/* input function block plug number */
+	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
+
+	/* do transaction and check buf[1-6,8] are the same against command */
+	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
+				  BIT(6) | BIT(8));
+	if (err < 0)
+		goto end;
+	if ((err < 6) || (buf[0] != 0x0c)) {
+		dev_err(&unit->device,
+			"failed to get selector %d: 0x%02X\n",
+			fb_id, buf[0]);
+		err = -EIO;
+		goto end;
+	}
+
+	*num = buf[7];
+	err = 0;
+end:
+	kfree(buf);
+	return err;
+}
+
 static inline void
 avc_bridgeco_fill_command_base(u8 *buf, unsigned int ctype, unsigned int opcode,
 			       unsigned int subfunction,
diff --git a/sound/firewire/bebob/bebob_pcm.c b/sound/firewire/bebob/bebob_pcm.c
index 9e0b12c..32ce990 100644
--- a/sound/firewire/bebob/bebob_pcm.c
+++ b/sound/firewire/bebob/bebob_pcm.c
@@ -229,6 +229,7 @@ static int
 pcm_open(struct snd_pcm_substream *substream)
 {
 	struct snd_bebob *bebob = substream->private_data;
+	struct snd_bebob_rate_spec *spec = bebob->spec->rate;
 	unsigned int sampling_rate;
 	bool internal;
 	int err;
@@ -252,7 +253,7 @@ pcm_open(struct snd_pcm_substream *substream)
 	if (!internal ||
 	    amdtp_stream_pcm_running(&bebob->tx_stream) ||
 	    amdtp_stream_pcm_running(&bebob->rx_stream)) {
-		err = snd_bebob_stream_get_rate(bebob, &sampling_rate);
+		err = spec->get(bebob, &sampling_rate);
 		if (err < 0)
 			goto err_locked;
 
diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c
index 07bb6ca..b8b6acc 100644
--- a/sound/firewire/bebob/bebob_proc.c
+++ b/sound/firewire/bebob/bebob_proc.c
@@ -71,6 +71,41 @@ end:
 }
 
 static void
+proc_read_meters(struct snd_info_entry *entry,
+		 struct snd_info_buffer *buffer)
+{
+	struct snd_bebob *bebob = entry->private_data;
+	struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+	u32 *buf;
+	unsigned int i, c, channels, size;
+	int err;
+
+	if (spec == NULL)
+		return;
+
+	channels = spec->num * 2;
+	size = channels * sizeof(u32);
+	buf = kmalloc(size, GFP_KERNEL);
+	if (buf == NULL)
+		return;
+
+	err = spec->get(bebob, buf, size);
+	if (err < 0)
+		goto end;
+
+	for (i = 0, c = 1; i < channels; i++) {
+		snd_iprintf(buffer, "%s %d:\t%d\n",
+			    spec->labels[i / 2], c++, buf[i]);
+		if ((i + 1 < channels - 1) &&
+		    (strcmp(spec->labels[i / 2],
+			    spec->labels[(i + 1) / 2]) != 0))
+			c = 1;
+	}
+end:
+	kfree(buf);
+}
+
+static void
 proc_read_formation(struct snd_info_entry *entry,
 		struct snd_info_buffer *buffer)
 {
@@ -102,18 +137,26 @@ proc_read_clock(struct snd_info_entry *entry,
 		struct snd_info_buffer *buffer)
 {
 	struct snd_bebob *bebob = entry->private_data;
+	struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+	struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
 	unsigned int rate;
 	bool internal;
+	unsigned int id;
 
-	if (snd_bebob_stream_get_rate(bebob, &rate) < 0)
-		return;
-	if (snd_bebob_stream_check_internal_clock(bebob, &internal) < 0)
+	if (rate_spec->get(bebob, &rate) < 0)
 		return;
-
-	snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)",
-		    (internal) ? "Internal" : "External",
-		    bebob->sync_input_plug);
 	snd_iprintf(buffer, "Sampling rate: %d\n", rate);
+
+	if (clk_spec) {
+		if (clk_spec->get(bebob, &id) >= 0)
+			snd_iprintf(buffer, "Clock Source: %s\n",
+				    clk_spec->labels[id]);
+	} else if (snd_bebob_stream_check_internal_clock(bebob,
+							 &internal) >= 0) {
+		snd_iprintf(buffer, "Clock Source: %s (MSU-dest: %d)\n",
+			    (internal) ? "Internal" : "External",
+			    bebob->sync_input_plug);
+	}
 }
 
 void snd_bebob_proc_init(struct snd_bebob *bebob)
@@ -128,4 +171,9 @@ void snd_bebob_proc_init(struct snd_bebob *bebob)
 
 	if (!snd_card_proc_new(bebob->card, "#clock", &entry))
 		snd_info_set_text_ops(entry, bebob, proc_read_clock);
+
+	if (bebob->spec->meter != NULL) {
+		if (!snd_card_proc_new(bebob->card, "#meter", &entry))
+			snd_info_set_text_ops(entry, bebob, proc_read_meters);
+	}
 }
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 797fcb0..de80756 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -99,13 +99,26 @@ end:
 int
 snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
 {
+	struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
 	u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
+	unsigned int id;
 	int err = 0;
 
 	*internal = false;
 
+	/* 1.The device has its own operation to switch source of clock */
+	if (clk_spec) {
+		err = clk_spec->get(bebob, &id);
+		if (err < 0)
+			goto end;
+		if (strncmp(clk_spec->labels[id], SND_BEBOB_CLOCK_INTERNAL,
+			    strlen(SND_BEBOB_CLOCK_INTERNAL)) == 0)
+			*internal = true;
+		goto end;
+	}
+
 	/*
-	 * 1.The device don't support for switching source of clock
+	 * 2.The device don't support for switching source of clock
 	 *   then assumed to use internal clock always
 	 */
 	if (bebob->sync_input_plug < 0) {
@@ -114,7 +127,7 @@ snd_bebob_stream_check_internal_clock(struct snd_bebob *bebob, bool *internal)
 	}
 
 	/*
-	 * 2.The device supports to switch source of clock by an usual way.
+	 * 3.The device supports to switch source of clock by an usual way.
 	 *   Let's check input for 'Music Sub Unit Sync Input' plug.
 	 */
 	avc_bridgeco_fill_subunit_addr(addr, 0x60, AVC_BRIDGECO_PLUG_DIR_IN,
@@ -400,6 +413,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob,
 				  struct amdtp_stream *request,
 				  unsigned int rate)
 {
+	struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
 	struct amdtp_stream *master, *slave;
 	enum cip_flags sync_mode;
 	unsigned int curr_rate;
@@ -430,7 +444,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob,
 		amdtp_stream_stop(master);
 
 	/* stop streams if rate is different */
-	err = snd_bebob_stream_get_rate(bebob, &curr_rate);
+	err = rate_spec->get(bebob, &curr_rate);
 	if (err < 0)
 		goto end;
 	if (rate == 0)
@@ -450,7 +464,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob,
 		 * If establishing connections at first, Yamaha GO46
 		 * (and maybe Terratec X24) don't generate sound.
 		 */
-		err = snd_bebob_stream_set_rate(bebob, rate);
+		err = rate_spec->set(bebob, rate);
 		if (err < 0)
 			goto end;
 
@@ -705,6 +719,7 @@ end:
 int snd_bebob_stream_discover(struct snd_bebob *bebob)
 {
 	u8 plugs[AVC_PLUG_INFO_BUF_COUNT], addr[AVC_BRIDGECO_ADDR_BYTES];
+	struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
 	enum avc_bridgeco_plug_type type;
 	unsigned int i;
 	int err;
@@ -778,7 +793,8 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob)
 	}
 
 	/* for check source of clock later */
-	err = seek_msu_sync_input_plug(bebob);
+	if (!clk_spec)
+		err = seek_msu_sync_input_plug(bebob);
 end:
 	return err;
 }
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 34/39] bebob: Add support for Terratec PHASE, EWS series and Aureon
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (32 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 33/39] bebob: Prepare for device specific operations Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 35/39] bebob: Add support for Yamaha GO series Takashi Sakamoto
                   ` (5 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit allows this driver to support all of models which Terratec produced
with DM1000/BeBoB. They are:
 - PHASE 24 FW
 - PHASE X24 FW
 - PHASE 88 Rack FW
 - EWS MIC2
 - EWS MIC4
 - Aureon 7.1 Firewire

For Phase series, this commit adds a Terratec specific operation. To get source
of clock. AV/C Audio Subunit command is used.

For EWS series and Aureon, this module uses normal operations.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                |  3 ++
 sound/firewire/bebob/Makefile         |  2 +-
 sound/firewire/bebob/bebob.c          | 11 ++++++
 sound/firewire/bebob/bebob.h          |  4 +++
 sound/firewire/bebob/bebob_terratec.c | 68 +++++++++++++++++++++++++++++++++++
 5 files changed, 87 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/bebob/bebob_terratec.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 060fe7e..0a7918a 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -108,6 +108,9 @@ config SND_BEBOB
 	  * Lynx Aurora 8/16 (LT-FW)
 	  * ICON FireXon
 	  * PrismSound Orpheus/ADA-8XR
+	  * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW
+	  * Terratec EWS MIC2/EWS MIC4
+	  * Terratec Aureon 7.1 Firewire
 
           To compile this driver as a module, choose M here: the module
           will be called snd-bebob.
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index e4b08e3..cb38dd1 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,3 +1,3 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
-		  bebob_pcm.o bebob_hwdep.o bebob.o
+		  bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 581a694..f1bfc02 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -52,6 +52,7 @@ static unsigned int devices_used;
 #define VEN_LYNX	0x000019e5
 #define VEN_ICON	0x00001a9e
 #define VEN_PRISMSOUND	0x00001198
+#define VEN_TERRATEC	0x00000aac
 
 static int
 name_device(struct snd_bebob *bebob, unsigned int vendor_id)
@@ -291,6 +292,16 @@ static const struct ieee1394_device_id bebob_id_table[] = {
 	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x00010048, &spec_normal),
 	/* PrismSound, ADA-8XR */
 	SND_BEBOB_DEV_ENTRY(VEN_PRISMSOUND, 0x0000ada8, &spec_normal),
+	/* TerraTec Electronic GmbH, PHASE 88 Rack FW */
+	SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec),
+	/* TerraTec Electronic GmbH, PHASE 24 FW */
+	SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &phase24_series_spec),
+	/* TerraTec Electronic GmbH, Phase X24 FW */
+	SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &phase24_series_spec),
+	/* TerraTec Electronic GmbH, EWS MIC2/MIC8 */
+	SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
+	/* Terratec Electronic GmbH, Aureon 7.1 Firewire */
+	SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
 	/* IDs are unknown but able to be supported */
 	/*  Apogee, Mini-ME Firewire */
 	/*  Apogee, Mini-DAC Firewire */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 6976097..a5422ea 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -228,6 +228,10 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
 
 int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
 
+/* model specific operations */
+extern struct snd_bebob_spec phase88_rack_spec;
+extern struct snd_bebob_spec phase24_series_spec;
+
 #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
new file mode 100644
index 0000000..9c2cebf
--- /dev/null
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -0,0 +1,68 @@
+/*
+ * bebob_terratec.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+static char *phase88_rack_clk_src_labels[] = {
+	SND_BEBOB_CLOCK_INTERNAL, "Digital In", "Word Clock"
+};
+static int
+phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+	unsigned int enable_ext, enable_word;
+	int err;
+
+	err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_ext);
+	if (err < 0)
+		goto end;
+	err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_word);
+	if (err < 0)
+		goto end;
+
+	*id = (enable_ext & 0x01) || ((enable_word & 0x01) << 1);
+end:
+	return err;
+}
+
+static char *phase24_series_clk_src_labels[] = {
+	SND_BEBOB_CLOCK_INTERNAL, "Digital In"
+};
+static int
+phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+	return avc_audio_get_selector(bebob->unit, 0, 4, id);
+}
+
+struct snd_bebob_rate_spec phase_series_rate_spec = {
+	.get	= &snd_bebob_stream_get_rate,
+	.set	= &snd_bebob_stream_set_rate,
+};
+
+/* PHASE 88 Rack FW */
+struct snd_bebob_clock_spec phase88_rack_clk = {
+	.num	= ARRAY_SIZE(phase88_rack_clk_src_labels),
+	.labels	= phase88_rack_clk_src_labels,
+	.get	= &phase88_rack_clk_src_get,
+};
+struct snd_bebob_spec phase88_rack_spec = {
+	.clock	= &phase88_rack_clk,
+	.rate	= &phase_series_rate_spec,
+	.meter	= NULL
+};
+
+/* 'PHASE 24 FW' and 'PHASE X24 FW' */
+struct snd_bebob_clock_spec phase24_series_clk = {
+	.num	= ARRAY_SIZE(phase24_series_clk_src_labels),
+	.labels	= phase24_series_clk_src_labels,
+	.get	= &phase24_series_clk_src_get,
+};
+struct snd_bebob_spec phase24_series_spec = {
+	.clock	= &phase24_series_clk,
+	.rate	= &phase_series_rate_spec,
+	.meter	= NULL
+};
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 35/39] bebob: Add support for Yamaha GO series
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (33 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 34/39] bebob: Add support for Terratec PHASE, EWS series and Aureon Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 36/39] bebob: Add support for Focusrite Saffire/SaffirePro series Takashi Sakamoto
                   ` (4 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit allows this driver to support all of models which Yamaha produced
with DM1000/BeBoB. They are:
 - GO44
 - GO46

This commit adds Yamaha specific operations. To get source of clock, AV/C Audio
Subunit command is used.

I note that their appearances are similar to some models of TerraTec; 'Go44' is
similar to 'PHASE 24 FW' and 'GO46' is similar to 'PHASE X24 FW'. But their
combination of Audio/Music subunit are a bit different.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig              |  1 +
 sound/firewire/bebob/Makefile       |  3 ++-
 sound/firewire/bebob/bebob.c        |  5 ++++
 sound/firewire/bebob/bebob.h        |  1 +
 sound/firewire/bebob/bebob_yamaha.c | 50 +++++++++++++++++++++++++++++++++++++
 5 files changed, 59 insertions(+), 1 deletion(-)
 create mode 100644 sound/firewire/bebob/bebob_yamaha.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 0a7918a..e7605ff 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -111,6 +111,7 @@ config SND_BEBOB
 	  * TerraTec PHASE 24 FW/PHASE X24 FW/PHASE 88 Rack FW
 	  * Terratec EWS MIC2/EWS MIC4
 	  * Terratec Aureon 7.1 Firewire
+	  * Yamaha GO44/GO46
 
           To compile this driver as a module, choose M here: the module
           will be called snd-bebob.
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index cb38dd1..c35eee1 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,3 +1,4 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
-		  bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob.o
+		  bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
+		  bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index f1bfc02..7ec91a9 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -53,6 +53,7 @@ static unsigned int devices_used;
 #define VEN_ICON	0x00001a9e
 #define VEN_PRISMSOUND	0x00001198
 #define VEN_TERRATEC	0x00000aac
+#define VEN_YAMAHA	0x0000a0de
 
 static int
 name_device(struct snd_bebob *bebob, unsigned int vendor_id)
@@ -302,6 +303,10 @@ static const struct ieee1394_device_id bebob_id_table[] = {
 	SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
 	/* Terratec Electronic GmbH, Aureon 7.1 Firewire */
 	SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
+	/* Yamaha, GO44 */
+	SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec),
+	/* YAMAHA, GO46 */
+	SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec),
 	/* IDs are unknown but able to be supported */
 	/*  Apogee, Mini-ME Firewire */
 	/*  Apogee, Mini-DAC Firewire */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index a5422ea..8192da4 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -231,6 +231,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
 /* model specific operations */
 extern struct snd_bebob_spec phase88_rack_spec;
 extern struct snd_bebob_spec phase24_series_spec;
+extern struct snd_bebob_spec yamaha_go_spec;
 
 #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
 { \
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
new file mode 100644
index 0000000..f71af6b
--- /dev/null
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -0,0 +1,50 @@
+/*
+ * bebob_yamaha.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+/*
+ * NOTE:
+ * Yamaha GO44 is not designed to be used as stand-alone mixer. So any streams
+ * must be accompanied. If changing the state, a LED on the device starts to
+ * blink and its sync status is false. In this state, the device sounds nothing
+ * even if streaming. To start streaming at the current sampling rate is only
+ * way to revocer this state. GO46 is better for stand-alone mixer.
+ *
+ * Both of them have a capability to change its sampling rate up to 192.0kHz.
+ * At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out.
+ * But Yamaha's driver reduce 2 PCM-in, 1 MIDI-in, 2 PCM-out, 1 MIDI-out to use
+ * 'Extended Stream Format Information Command - Single Request' in 'Additional
+ * AVC commands' defined by BridgeCo.
+ * This ALSA driver don't do this because a bit tiresome. Then isochronous
+ * streaming with many asynchronous transactions brings sounds with noises.
+ * Unfortunately current 'ffado-mixer' generated many asynchronous transaction
+ * to observe device's state, mainly check cmp connection and signal format. I
+ * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
+ */
+
+static char *clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
+static int
+clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+	return avc_audio_get_selector(bebob->unit, 0, 4, id);
+}
+static struct snd_bebob_clock_spec clock_spec = {
+	.num	= ARRAY_SIZE(clk_src_labels),
+	.labels	= clk_src_labels,
+	.get	= &clk_src_get,
+};
+static struct snd_bebob_rate_spec rate_spec = {
+	.get	= &snd_bebob_stream_get_rate,
+	.set	= &snd_bebob_stream_set_rate,
+};
+struct snd_bebob_spec yamaha_go_spec = {
+	.clock	= &clock_spec,
+	.rate	= &rate_spec,
+	.meter	= NULL
+};
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 36/39] bebob: Add support for Focusrite Saffire/SaffirePro series
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (34 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 35/39] bebob: Add support for Yamaha GO series Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 37/39] bebob: Add support for M-Audio usual Firewire series Takashi Sakamoto
                   ` (3 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit allows this driver to support all of models which Focusrite
produces with DM1000/BeBoB. They are:
 - Saffire
 - Saffire LE
 - SaffirePro 10 I/O
 - SaffirePro 26 I/O

This commit adds Focusrite specific operations:
1. Get source of clock
2. Get/Set sampling frequency
3. Get metering information

The driver uses these functionalities to read/write specific address by async
transaction.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                 |   1 +
 sound/firewire/bebob/Makefile          |   2 +-
 sound/firewire/bebob/bebob.c           |  30 +++-
 sound/firewire/bebob/bebob.h           |   4 +
 sound/firewire/bebob/bebob_focusrite.c | 289 +++++++++++++++++++++++++++++++++
 5 files changed, 324 insertions(+), 2 deletions(-)
 create mode 100644 sound/firewire/bebob/bebob_focusrite.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index e7605ff..14ae19c 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -112,6 +112,7 @@ config SND_BEBOB
 	  * Terratec EWS MIC2/EWS MIC4
 	  * Terratec Aureon 7.1 Firewire
 	  * Yamaha GO44/GO46
+	  * Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
 
           To compile this driver as a module, choose M here: the module
           will be called snd-bebob.
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index c35eee1..6565420 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,4 +1,4 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
 		  bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
-		  bebob.o
+		  bebob_focusrite.o bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 7ec91a9..22d4d4e 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -54,6 +54,9 @@ static unsigned int devices_used;
 #define VEN_PRISMSOUND	0x00001198
 #define VEN_TERRATEC	0x00000aac
 #define VEN_YAMAHA	0x0000a0de
+#define VEN_FOCUSRITE	0x0000130e
+
+#define MODEL_FOCUSRITE_SAFFIRE_BOTH	0x00000000
 
 static int
 name_device(struct snd_bebob *bebob, unsigned int vendor_id)
@@ -122,6 +125,20 @@ bebob_card_free(struct snd_card *card)
 	mutex_destroy(&bebob->mutex);
 }
 
+static const struct snd_bebob_spec *
+get_saffire_spec(struct fw_unit *unit)
+{
+	char name[24] = {0};
+
+	if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+		return NULL;
+
+	if (strcmp(name, "SaffireLE") == 0)
+		return &saffire_le_spec;
+	else
+		return &saffire_spec;
+}
+
 static int
 bebob_probe(struct fw_unit *unit,
 	    const struct ieee1394_device_id *entry)
@@ -143,7 +160,11 @@ bebob_probe(struct fw_unit *unit,
 		goto end;
 	}
 
-	spec = (const struct snd_bebob_spec *)entry->driver_data;
+	if ((entry->vendor_id == VEN_FOCUSRITE) &&
+	    (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
+		spec = get_saffire_spec(unit);
+	else
+		spec = (const struct snd_bebob_spec *)entry->driver_data;
 	if (spec == NULL) {
 		err = -ENOSYS;
 		goto end;
@@ -307,6 +328,13 @@ static const struct ieee1394_device_id bebob_id_table[] = {
 	SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec),
 	/* YAMAHA, GO46 */
 	SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec),
+	/* Focusrite, SaffirePro 26 I/O */
+	SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
+	/* Focusrite, SaffirePro 10 I/O */
+	SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000006, &saffirepro_10_spec),
+	/* Focusrite, Saffire(no label and LE) */
+	SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
+			    &saffire_spec),
 	/* IDs are unknown but able to be supported */
 	/*  Apogee, Mini-ME Firewire */
 	/*  Apogee, Mini-DAC Firewire */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 8192da4..8e5b0da 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -232,6 +232,10 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
 extern struct snd_bebob_spec phase88_rack_spec;
 extern struct snd_bebob_spec phase24_series_spec;
 extern struct snd_bebob_spec yamaha_go_spec;
+extern struct snd_bebob_spec saffirepro_26_spec;
+extern struct snd_bebob_spec saffirepro_10_spec;
+extern struct snd_bebob_spec saffire_le_spec;
+extern struct snd_bebob_spec saffire_spec;
 
 #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
 { \
diff --git a/sound/firewire/bebob/bebob_focusrite.c b/sound/firewire/bebob/bebob_focusrite.c
new file mode 100644
index 0000000..01996c9
--- /dev/null
+++ b/sound/firewire/bebob/bebob_focusrite.c
@@ -0,0 +1,289 @@
+/*
+ * bebob_focusrite.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+#define ANA_IN	"Analog In"
+#define DIG_IN	"Digital In"
+#define ANA_OUT	"Analog Out"
+#define DIG_OUT	"Digital Out"
+#define STM_IN	"Stream In"
+
+#define SAFFIRE_ADDRESS_BASE			0x000100000000
+
+#define SAFFIRE_OFFSET_CLOCK_SOURCE		0x0000000000f8
+#define SAFFIREPRO_OFFSET_CLOCK_SOURCE		0x000000000174
+
+/* whether sync to external device or not */
+#define SAFFIRE_OFFSET_CLOCK_SYNC_EXT		0x00000000013c
+#define SAFFIRE_LE_OFFSET_CLOCK_SYNC_EXT	0x000000000432
+#define SAFFIREPRO_OFFSET_CLOCK_SYNC_EXT	0x000000000164
+
+#define SAFFIRE_CLOCK_SOURCE_INTERNAL		0
+#define SAFFIRE_CLOCK_SOURCE_SPDIF		1
+
+/* '1' is absent, why... */
+#define SAFFIREPRO_CLOCK_SOURCE_INTERNAL	0
+#define SAFFIREPRO_CLOCK_SOURCE_SPDIF		2
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT1		3
+#define SAFFIREPRO_CLOCK_SOURCE_ADAT2		4
+#define SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK	5
+
+/* S/PDIF, ADAT1, ADAT2 is enabled or not. three quadlets */
+#define SAFFIREPRO_ENABLE_DIG_IFACES		0x0000000001a4
+
+/* saffirepro has its own parameter for sampling frequency */
+#define SAFFIREPRO_RATE_NOREBOOT		0x0000000001cc
+/* index is the value for this register */
+static const unsigned int rates[] = {
+	[0] = 0,
+	[1] = 44100,
+	[2] = 48000,
+	[3] = 88200,
+	[4] = 96000,
+	[5] = 176400,
+	[6] = 192000
+};
+
+/* saffire(no label)/saffire LE has metering */
+#define SAFFIRE_OFFSET_METER			0x000000000100
+#define SAFFIRE_LE_OFFSET_METER			0x000000000168
+
+static inline int
+saffire_read_block(struct snd_bebob *bebob, u64 offset,
+		   u32 *buf, unsigned int size)
+{
+	unsigned int i;
+	int err;
+	__be32 *tmp = (__be32 *)buf;
+
+	err =  snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+				  SAFFIRE_ADDRESS_BASE + offset,
+				  tmp, size, 0);
+	if (err < 0)
+		goto end;
+
+	for (i = 0; i < size / sizeof(u32); i++)
+		buf[i] = be32_to_cpu(tmp[i]);
+end:
+	return err;
+}
+
+static inline int
+saffire_read_quad(struct snd_bebob *bebob, u64 offset, u32 *value)
+{
+	int err;
+	__be32 tmp;
+
+	err = snd_fw_transaction(bebob->unit, TCODE_READ_QUADLET_REQUEST,
+				 SAFFIRE_ADDRESS_BASE + offset,
+				 &tmp, sizeof(__be32), 0);
+	if (err < 0)
+		goto end;
+
+	*value = be32_to_cpu(tmp);
+end:
+	return err;
+}
+
+static inline int
+saffire_write_quad(struct snd_bebob *bebob, u64 offset, u32 value)
+{
+	__be32 data = cpu_to_be32(value);
+
+	return snd_fw_transaction(bebob->unit, TCODE_WRITE_QUADLET_REQUEST,
+				  SAFFIRE_ADDRESS_BASE + offset,
+				  &data, sizeof(__be32), 0);
+}
+
+static char *saffirepro_26_clk_src_labels[] = {
+	SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "ADAT1", "ADAT2", "Word Clock"
+};
+
+static char *saffirepro_10_clk_src_labels[] = {
+	SND_BEBOB_CLOCK_INTERNAL, "S/PDIF", "Word Clock"
+};
+static int
+saffirepro_both_clk_freq_get(struct snd_bebob *bebob, unsigned int *rate)
+{
+	u32 id;
+	int err;
+
+	err = saffire_read_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, &id);
+	if (err < 0)
+		goto end;
+	if (id >= ARRAY_SIZE(rates)) {
+		err = -EIO;
+		goto end;
+	}
+
+	*rate = rates[id];
+end:
+	return err;
+}
+static int
+saffirepro_both_clk_freq_set(struct snd_bebob *bebob, unsigned int rate)
+{
+	u32 id;
+	bool flag;
+
+	flag = false;
+	for (id = 0; id < ARRAY_SIZE(rates); id++) {
+		if (rates[id] == rate)
+			flag = true;
+	}
+	if (!flag)
+		return -EINVAL;
+
+	return saffire_write_quad(bebob, SAFFIREPRO_RATE_NOREBOOT, id);
+}
+static int
+saffirepro_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+	int err;
+	u32 value;
+
+	err = saffire_read_quad(bebob, SAFFIREPRO_OFFSET_CLOCK_SOURCE, &value);
+	if (err < 0)
+		goto end;
+
+	if (bebob->spec->clock->labels == saffirepro_10_clk_src_labels) {
+		if (value == SAFFIREPRO_CLOCK_SOURCE_WORDCLOCK)
+			*id = 2;
+		else if (value == SAFFIREPRO_CLOCK_SOURCE_SPDIF)
+			*id = 1;
+	} else if (value > 1)
+		*id = value - 1;
+end:
+	return err;
+}
+
+struct snd_bebob_spec saffire_le_spec;
+static char *saffire_both_clk_src_labels[] = {
+	SND_BEBOB_CLOCK_INTERNAL, "S/PDIF"
+};
+static int
+saffire_both_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
+{
+	int err;
+	u32 value;
+
+	err = saffire_read_quad(bebob, SAFFIRE_OFFSET_CLOCK_SOURCE, &value);
+	if (err < 0)
+		goto end;
+
+	*id = 0xff & value;
+end:
+	return err;
+};
+static char *saffire_le_meter_labels[] = {
+	ANA_IN, ANA_IN, DIG_IN,
+	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+	STM_IN, STM_IN
+};
+#define SWAP(a, b) \
+	do { \
+		tmp = a; \
+		a = b; \
+		b = tmp; \
+	} while (0)
+static int
+saffire_le_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+	int err;
+	u32 tmp;
+
+	if (size < sizeof(saffire_le_meter_labels) * sizeof(u32))
+		return -EIO;
+
+	err = saffire_read_block(bebob, SAFFIRE_LE_OFFSET_METER, buf, size);
+	if (err < 0)
+		goto end;
+
+	SWAP(buf[1], buf[3]);
+	SWAP(buf[2], buf[3]);
+	SWAP(buf[3], buf[4]);
+
+	SWAP(buf[7], buf[10]);
+	SWAP(buf[8], buf[10]);
+	SWAP(buf[9], buf[11]);
+	SWAP(buf[11], buf[12]);
+
+	SWAP(buf[15], buf[16]);
+
+end:
+	return err;
+}
+static char *saffire_meter_labels[] = {
+	ANA_IN, ANA_IN,
+	STM_IN, STM_IN, STM_IN, STM_IN, STM_IN,
+};
+static int
+saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+	return saffire_read_block(bebob, SAFFIRE_OFFSET_METER, buf, size);
+}
+
+/* Saffire Pro 26 I/O  */
+static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+	.get	= &saffirepro_both_clk_freq_get,
+	.set	= &saffirepro_both_clk_freq_set,
+};
+static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+	.num	= ARRAY_SIZE(saffirepro_26_clk_src_labels),
+	.labels	= saffirepro_26_clk_src_labels,
+	.get	= &saffirepro_both_clk_src_get,
+};
+struct snd_bebob_spec saffirepro_26_spec = {
+	.clock	= &saffirepro_26_clk_spec,
+	.rate	= &saffirepro_both_rate_spec,
+	.meter	= NULL
+};
+/* Saffire Pro 10 I/O */
+static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+	.num	= ARRAY_SIZE(saffirepro_10_clk_src_labels),
+	.labels	= saffirepro_10_clk_src_labels,
+	.get	= &saffirepro_both_clk_src_get,
+};
+struct snd_bebob_spec saffirepro_10_spec = {
+	.clock	= &saffirepro_10_clk_spec,
+	.rate	= &saffirepro_both_rate_spec,
+	.meter	= NULL
+};
+
+/* Saffire LE */
+static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+	.get	= &snd_bebob_stream_get_rate,
+	.set	= &snd_bebob_stream_set_rate,
+};
+static struct snd_bebob_clock_spec saffire_both_clk_spec = {
+	.num	= ARRAY_SIZE(saffire_both_clk_src_labels),
+	.labels	= saffire_both_clk_src_labels,
+	.get	= &saffire_both_clk_src_get,
+};
+struct snd_bebob_meter_spec saffire_le_meter_spec = {
+	.num	= ARRAY_SIZE(saffire_le_meter_labels),
+	.labels	= saffire_le_meter_labels,
+	.get	= &saffire_le_meter_get,
+};
+struct snd_bebob_spec saffire_le_spec = {
+	.clock	= &saffire_both_clk_spec,
+	.rate	= &saffire_both_rate_spec,
+	.meter	= &saffire_le_meter_spec
+};
+/* Saffire */
+struct snd_bebob_meter_spec saffire_meter_spec = {
+	.num	= ARRAY_SIZE(saffire_meter_labels),
+	.labels	= saffire_meter_labels,
+	.get	= &saffire_meter_get,
+};
+struct snd_bebob_spec saffire_spec = {
+	.clock	= &saffire_both_clk_spec,
+	.rate	= &saffire_both_rate_spec,
+	.meter	= &saffire_meter_spec
+};
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 37/39] bebob: Add support for M-Audio usual Firewire series
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (35 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 36/39] bebob: Add support for Focusrite Saffire/SaffirePro series Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 38/39] bebob: Send a cue to load firmware for M-Audio " Takashi Sakamoto
                   ` (2 subsequent siblings)
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit allows this driver to support some models which M-Audio produces
with DM1000/DM1000E with usual firmware. They are:
 - Firewire 410
 - Firewire AudioPhile
 - Firewire Solo
 - Ozonic
 - NRV10
 - FirewireLightBridge

According to person of BridgeCo, Some models are produced with 'Pre-BeBoB'. This
means that these products were released before BeBoB was officially produced. So
these models have some quirks.

M-Audio usual firmware quirks:
 - Just after powering on, 'Firewire 410' waits to download firmware. This
   state is changed when receiving cue. Then bus reset is generated and the
   device is recognized as a different model with the uploaded firmware.
 - 'Firewire Audiophile' also waits to download firmware but its
   vendor id/model id is the same as the one after loading firmware.
 - The information of channel mapping for MIDI conformant data channel is
   invalid against BridgeCo specification.

This commit adds some codes for these quirks but don't support to upload
firmware.

This commit also adds specific operations to get metering information. The
metering information also includes status of clock synchronization if the model
supports to switch source of clock.

The specification of FirewireLightBridge is unknown. So in this time, normal
operations are applied for this model.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig              |   2 +
 sound/firewire/bebob/Makefile       |   2 +-
 sound/firewire/bebob/bebob.c        |  45 +++++-
 sound/firewire/bebob/bebob.h        |   5 +
 sound/firewire/bebob/bebob_maudio.c | 274 ++++++++++++++++++++++++++++++++++++
 sound/firewire/bebob/bebob_stream.c |   9 ++
 6 files changed, 334 insertions(+), 3 deletions(-)
 create mode 100644 sound/firewire/bebob/bebob_maudio.c

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 14ae19c..f9aa176 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -113,6 +113,8 @@ config SND_BEBOB
 	  * Terratec Aureon 7.1 Firewire
 	  * Yamaha GO44/GO46
 	  * Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
+	  * M-Audio Firewire410/AudioPhile/Solo
+	  * M-Audio Ozonic/NRV10/ProfireLightBridge
 
           To compile this driver as a module, choose M here: the module
           will be called snd-bebob.
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index 6565420..6cf470c 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,4 +1,4 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
 		  bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
-		  bebob_focusrite.o bebob.o
+		  bebob_focusrite.o bebob_maudio.o bebob.o
 obj-m += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 22d4d4e..21de883 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -55,8 +55,11 @@ static unsigned int devices_used;
 #define VEN_TERRATEC	0x00000aac
 #define VEN_YAMAHA	0x0000a0de
 #define VEN_FOCUSRITE	0x0000130e
+#define VEN_MAUDIO1	0x00000d6c
+#define VEN_MAUDIO2	0x000007f5
 
 #define MODEL_FOCUSRITE_SAFFIRE_BOTH	0x00000000
+#define MODEL_MAUDIO_AUDIOPHILE_BOTH	0x00010060
 
 static int
 name_device(struct snd_bebob *bebob, unsigned int vendor_id)
@@ -139,6 +142,17 @@ get_saffire_spec(struct fw_unit *unit)
 		return &saffire_spec;
 }
 
+static bool
+check_audiophile_booted(struct fw_unit *unit)
+{
+	char name[24] = {0};
+
+	if (fw_csr_string(unit->directory, CSR_MODEL, name, sizeof(name)) < 0)
+		return false;
+
+	return strncmp(name, "FW Audiophile Bootloader", 15) != 0;
+}
+
 static int
 bebob_probe(struct fw_unit *unit,
 	    const struct ieee1394_device_id *entry)
@@ -161,10 +175,16 @@ bebob_probe(struct fw_unit *unit,
 	}
 
 	if ((entry->vendor_id == VEN_FOCUSRITE) &&
-	    (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
+	    (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)) {
 		spec = get_saffire_spec(unit);
-	else
+	} else if ((entry->vendor_id == VEN_MAUDIO1) &&
+		   (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
+		   !check_audiophile_booted(unit)) {
+			err = 0;
+			goto end;
+	} else {
 		spec = (const struct snd_bebob_spec *)entry->driver_data;
+	}
 	if (spec == NULL) {
 		err = -ENOSYS;
 		goto end;
@@ -235,6 +255,10 @@ static void
 bebob_update(struct fw_unit *unit)
 {
 	struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+	if (bebob == NULL)
+		return;
+
 	fcp_bus_reset(bebob->unit);
 	snd_bebob_stream_update_duplex(bebob);
 }
@@ -243,6 +267,10 @@ bebob_update(struct fw_unit *unit)
 static void bebob_remove(struct fw_unit *unit)
 {
 	struct snd_bebob *bebob = dev_get_drvdata(&unit->device);
+
+	if (bebob == NULL)
+		return;
+
 	snd_bebob_stream_destroy_duplex(bebob);
 	snd_card_disconnect(bebob->card);
 	snd_card_free_when_closed(bebob->card);
@@ -335,6 +363,19 @@ static const struct ieee1394_device_id bebob_id_table[] = {
 	/* Focusrite, Saffire(no label and LE) */
 	SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
 			    &saffire_spec),
+	/* M-Audio, Firewire 410 */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010046, &maudio_fw410_spec),
+	/* M-Audio, Firewire Audiophile */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_AUDIOPHILE_BOTH,
+			    &maudio_audiophile_spec),
+	/* M-Audio, Firewire Solo */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010062, &maudio_solo_spec),
+	/* M-Audio, Ozonic */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x0000000a, &maudio_ozonic_spec),
+	/* M-Audio NRV10 */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010081, &maudio_nrv10_spec),
+	/* M-Audio, ProFireLightbridge */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x000100a1, &spec_normal),
 	/* IDs are unknown but able to be supported */
 	/*  Apogee, Mini-ME Firewire */
 	/*  Apogee, Mini-DAC Firewire */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 8e5b0da..1e9af0e 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -236,6 +236,11 @@ extern struct snd_bebob_spec saffirepro_26_spec;
 extern struct snd_bebob_spec saffirepro_10_spec;
 extern struct snd_bebob_spec saffire_le_spec;
 extern struct snd_bebob_spec saffire_spec;
+extern struct snd_bebob_spec maudio_fw410_spec;
+extern struct snd_bebob_spec maudio_audiophile_spec;
+extern struct snd_bebob_spec maudio_solo_spec;
+extern struct snd_bebob_spec maudio_ozonic_spec;
+extern struct snd_bebob_spec maudio_nrv10_spec;
 
 #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
 { \
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
new file mode 100644
index 0000000..7c8f5c1
--- /dev/null
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -0,0 +1,274 @@
+/*
+ * bebob_maudio.c - a part of driver for BeBoB based devices
+ *
+ * Copyright (c) 2013 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./bebob.h"
+
+/*
+ * Just powering on, Firewire 410/Audiophile wait to
+ * download firmware blob. To enable these devices, drivers should upload
+ * firmware blob and send a command to initialize configuration to factory
+ * settings when completing uploading. Then these devices generate bus reset
+ * and are recognized as new devices with the firmware.
+ *
+ * For streaming, both of output and input streams are needed for Firewire 410
+ * and Ozonic. The single stream is OK for the other devices even if the clock
+ * source is not SYT-Match (I note no devices use SYT-Match).
+ *
+ * Without streaming, the devices except for Firewire Audiophile can mix any
+ * input and output. For this reason, Audiophile cannot be used as standalone
+ * mixer.
+ */
+
+#define MAUDIO_SPECIFIC_ADDRESS	0xffc700000000
+
+#define METER_OFFSET		0x00600000
+
+/* some device has sync info after metering data */
+#define METER_SIZE_FW410	76	/* with sync info */
+#define METER_SIZE_AUDIOPHILE	60	/* with sync info */
+#define METER_SIZE_SOLO		52	/* with sync info */
+#define METER_SIZE_OZONIC	48
+#define METER_SIZE_NRV10	80
+
+/* labels for metering */
+#define ANA_IN		"Analog In"
+#define ANA_OUT		"Analog Out"
+#define DIG_IN		"Digital In"
+#define SPDIF_IN	"S/PDIF In"
+#define ADAT_IN		"ADAT In"
+#define DIG_OUT		"Digital Out"
+#define SPDIF_OUT	"S/PDIF Out"
+#define ADAT_OUT	"ADAT Out"
+#define STRM_IN		"Stream In"
+#define AUX_OUT		"Aux Out"
+#define HP_OUT		"HP Out"
+/* for NRV */
+#define UNKNOWN_METER	"Unknown"
+
+static inline int
+get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
+{
+	return snd_fw_transaction(bebob->unit, TCODE_READ_BLOCK_REQUEST,
+				  MAUDIO_SPECIFIC_ADDRESS + METER_OFFSET,
+				  buf, size, 0);
+}
+
+/* Firewire 410 specific operation */
+static char *fw410_meter_labels[] = {
+	ANA_IN, DIG_IN,
+	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
+	HP_OUT
+};
+static int
+fw410_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+
+	unsigned int c, channels;
+	int err;
+
+	/* last 4 bytes are omitted because it's clock info. */
+	channels = ARRAY_SIZE(fw410_meter_labels) * 2;
+	if (size < channels * sizeof(u32))
+		return -EINVAL;
+
+	err = get_meter(bebob, (void *)buf, size);
+	if (err < 0)
+		goto end;
+
+	for (c = 0; c < channels; c++)
+		be32_to_cpus(&buf[c]);
+end:
+	return err;
+}
+
+/* Firewire Audiophile specific operation */
+static char *audiophile_meter_labels[] = {
+	ANA_IN, DIG_IN,
+	ANA_OUT, ANA_OUT, DIG_OUT,
+	HP_OUT, AUX_OUT,
+};
+static int
+audiophile_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+	unsigned int c, channels;
+	int err;
+
+	/* last 4 bytes are omitted because it's clock info. */
+	channels = ARRAY_SIZE(audiophile_meter_labels) * 2;
+	if (size < channels * sizeof(u32))
+		return -EINVAL;
+
+	err = get_meter(bebob, (void *)buf, size);
+	if (err < 0)
+		goto end;
+
+	for (c = 0; c < channels; c++)
+		be32_to_cpus(&buf[c]);
+end:
+	return err;
+}
+
+/* Firewire Solo specific operation */
+static char *solo_meter_labels[] = {
+	ANA_IN, DIG_IN,
+	STRM_IN, STRM_IN,
+	ANA_OUT, DIG_OUT
+};
+static int
+solo_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+	unsigned int c;
+	int err;
+	u32 tmp;
+
+	/* last 4 bytes are omitted because it's clock info. */
+	if (size < ARRAY_SIZE(solo_meter_labels) * 2 * sizeof(u32))
+		return -EINVAL;
+
+	err = get_meter(bebob, (void *)buf, size);
+	if (err < 0)
+		goto end;
+
+	c = 0;
+	do
+		buf[c] = be32_to_cpu(buf[c]);
+	while (++c < 4);
+
+	/* swap stream channels because inverted */
+	tmp = be32_to_cpu(buf[c]);
+	buf[c] = be32_to_cpu(buf[c + 2]);
+	buf[c + 2] = tmp;
+	tmp = be32_to_cpu(buf[c + 1]);
+	buf[c + 1] = be32_to_cpu(buf[c + 3]);
+	buf[c + 3] = tmp;
+
+	c += 4;
+	do
+		be32_to_cpus(&buf[c]);
+	while (++c < 12);
+end:
+	return err;
+}
+
+/* Ozonic specific operation */
+static char *ozonic_meter_labels[] = {
+	ANA_IN, ANA_IN,
+	STRM_IN, STRM_IN,
+	ANA_OUT, ANA_OUT
+};
+static int
+ozonic_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+	unsigned int c, channels;
+	int err;
+
+	channels = ARRAY_SIZE(ozonic_meter_labels) * 2;
+	if (size < channels * sizeof(u32))
+		return -EINVAL;
+
+	err = get_meter(bebob, (void *)buf, size);
+	if (err < 0)
+		goto end;
+
+	for (c = 0; c < channels; c++)
+		be32_to_cpus(&buf[c]);
+end:
+	return err;
+}
+
+/* NRV10 specific operation */
+/* TODO: need testers. these positions are based on my assumption */
+static char *nrv10_meter_labels[] = {
+	ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+	DIG_IN,
+	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT,
+	DIG_IN
+};
+static int
+nrv10_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
+{
+	unsigned int c, channels;
+	int err;
+
+	channels = ARRAY_SIZE(nrv10_meter_labels) * 2;
+	if (size < channels * sizeof(u32))
+		return -EINVAL;
+
+	err = get_meter(bebob, (void *)buf, size);
+	if (err < 0)
+		goto end;
+
+	for (c = 0; c < channels; c++)
+		be32_to_cpus(&buf[c]);
+end:
+	return err;
+}
+
+/* Firewire 410 specification */
+static struct snd_bebob_rate_spec usual_rate_spec = {
+	.get	= &snd_bebob_stream_get_rate,
+	.set	= &snd_bebob_stream_set_rate,
+};
+static struct snd_bebob_meter_spec fw410_meter_spec = {
+	.num	= ARRAY_SIZE(fw410_meter_labels),
+	.labels	= fw410_meter_labels,
+	.get	= &fw410_meter_get
+};
+struct snd_bebob_spec maudio_fw410_spec = {
+	.clock	= NULL,
+	.rate	= &usual_rate_spec,
+	.meter	= &fw410_meter_spec
+};
+
+/* Firewire Audiophile specification */
+static struct snd_bebob_meter_spec audiophile_meter_spec = {
+	.num	= ARRAY_SIZE(audiophile_meter_labels),
+	.labels	= audiophile_meter_labels,
+	.get	= &audiophile_meter_get
+};
+struct snd_bebob_spec maudio_audiophile_spec = {
+	.clock	= NULL,
+	.rate	= &usual_rate_spec,
+	.meter	= &audiophile_meter_spec
+};
+
+/* Firewire Solo specification */
+static struct snd_bebob_meter_spec solo_meter_spec = {
+	.num	= ARRAY_SIZE(solo_meter_labels),
+	.labels	= solo_meter_labels,
+	.get	= &solo_meter_get
+};
+struct snd_bebob_spec maudio_solo_spec = {
+	.clock	= NULL,
+	.rate	= &usual_rate_spec,
+	.meter	= &solo_meter_spec
+};
+
+/* Ozonic specification */
+static struct snd_bebob_meter_spec ozonic_meter_spec = {
+	.num	= ARRAY_SIZE(ozonic_meter_labels),
+	.labels	= ozonic_meter_labels,
+	.get	= &ozonic_meter_get
+};
+struct snd_bebob_spec maudio_ozonic_spec = {
+	.clock	= NULL,
+	.rate	= &usual_rate_spec,
+	.meter	= &ozonic_meter_spec
+};
+
+/* NRV10 specification */
+static struct snd_bebob_meter_spec nrv10_meter_spec = {
+	.num	= ARRAY_SIZE(nrv10_meter_labels),
+	.labels	= nrv10_meter_labels,
+	.get	= &nrv10_meter_get
+};
+struct snd_bebob_spec maudio_nrv10_spec = {
+	.clock	= NULL,
+	.rate	= &usual_rate_spec,
+	.meter	= &nrv10_meter_spec
+};
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index de80756..92e91a7 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -216,6 +216,15 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
 			/* location of this channel in this section */
 			sec_loc = buf[pos++] - 1;
 
+			/*
+			 * Basically the number of location is within the
+			 * number of channels in this section. But some models
+			 * of M-Audio don't follow this. Its location for MIDI
+			 * is the position of MIDI channels in AMDTP packet.
+			 */
+			if (sec_loc >= channels)
+				sec_loc = ch;
+
 			switch (type) {
 			/* for MIDI conformant data channel */
 			case 0x0a:
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 38/39] bebob: Send a cue to load firmware for M-Audio Firewire series
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (36 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 37/39] bebob: Add support for M-Audio usual Firewire series Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-02-28  3:27 ` [PATCH 39/39] bebob: Add support for M-Audio special " Takashi Sakamoto
  2014-02-28  6:36 ` [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Iwai
  39 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

Just powering on, these devices below wait to download firmware.
 - Firewire Audiophile
 - Firewire 410

But firmware version 5058 or later, flash memory in the device stores the
firmware. So this driver can enable these devices by sending a certain cue to
load the firmware.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/bebob/bebob.c        | 22 +++++++-----
 sound/firewire/bebob/bebob.h        |  2 ++
 sound/firewire/bebob/bebob_maudio.c | 69 +++++++++++++++++++++++++++++++++++++
 3 files changed, 84 insertions(+), 9 deletions(-)

diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index 21de883..a2104c7 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -175,18 +175,21 @@ bebob_probe(struct fw_unit *unit,
 	}
 
 	if ((entry->vendor_id == VEN_FOCUSRITE) &&
-	    (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH)) {
+	    (entry->model_id == MODEL_FOCUSRITE_SAFFIRE_BOTH))
 		spec = get_saffire_spec(unit);
-	} else if ((entry->vendor_id == VEN_MAUDIO1) &&
-		   (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
-		   !check_audiophile_booted(unit)) {
-			err = 0;
-			goto end;
-	} else {
+	else if ((entry->vendor_id == VEN_MAUDIO1) &&
+		 (entry->model_id == MODEL_MAUDIO_AUDIOPHILE_BOTH) &&
+		 !check_audiophile_booted(unit))
+		spec = NULL;
+	else
 		spec = (const struct snd_bebob_spec *)entry->driver_data;
-	}
+
 	if (spec == NULL) {
-		err = -ENOSYS;
+		if ((entry->vendor_id == VEN_MAUDIO1) ||
+		    (entry->vendor_id == VEN_MAUDIO2))
+			err = snd_bebob_maudio_load_firmware(unit);
+		else
+			err = -ENOSYS;
 		goto end;
 	}
 
@@ -364,6 +367,7 @@ static const struct ieee1394_device_id bebob_id_table[] = {
 	SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
 			    &saffire_spec),
 	/* M-Audio, Firewire 410 */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010058, NULL),	/* bootloader */
 	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO2, 0x00010046, &maudio_fw410_spec),
 	/* M-Audio, Firewire Audiophile */
 	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_AUDIOPHILE_BOTH,
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index 1e9af0e..b08cbbb 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -242,6 +242,8 @@ extern struct snd_bebob_spec maudio_solo_spec;
 extern struct snd_bebob_spec maudio_ozonic_spec;
 extern struct snd_bebob_spec maudio_nrv10_spec;
 
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
+
 #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
 { \
 	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index 7c8f5c1..e3ab42c 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -15,6 +15,15 @@
  * settings when completing uploading. Then these devices generate bus reset
  * and are recognized as new devices with the firmware.
  *
+ * But with firmware version 5058 or later, the firmware is stored to flash
+ * memory in the device and drivers can tell DM1000 to load the firmware by
+ * sending a cue. This cue must be sent one time.
+ *
+ * If the firmware blobs are in alsa-firmware package, this driver can support
+ * these devices with any firmware versions. (Then this driver need codes to
+ * upload the firmware blob.) But for this, the license of firmware blob needs
+ * to be considered.
+ *
  * For streaming, both of output and input streams are needed for Firewire 410
  * and Ozonic. The single stream is OK for the other devices even if the clock
  * source is not SYT-Match (I note no devices use SYT-Match).
@@ -24,6 +33,20 @@
  * mixer.
  */
 
+/* Offset from information register */
+#define INFO_OFFSET_SW_DATE	0x20
+
+/* Bootloader Protocol Version 1 */
+#define MAUDIO_BOOTLOADER_CUE1	0x00000001
+/*
+ * Initializing configuration to factory settings (= 0x1101), (swapped in line),
+ * Command code is zero (= 0x00),
+ * the number of operands is zero (= 0x00)(at least significant byte)
+ */
+#define MAUDIO_BOOTLOADER_CUE2	0x01110000
+/* padding */
+#define MAUDIO_BOOTLOADER_CUE3	0x00000000
+
 #define MAUDIO_SPECIFIC_ADDRESS	0xffc700000000
 
 #define METER_OFFSET		0x00600000
@@ -50,6 +73,52 @@
 /* for NRV */
 #define UNKNOWN_METER	"Unknown"
 
+/*
+ * For some M-Audio devices, this module just send cue to load firmware. After
+ * loading, the device generates bus reset and newly detected.
+ *
+ * If we make any transactions to load firmware, the operation may failed.
+ */
+int snd_bebob_maudio_load_firmware(struct fw_unit *unit)
+{
+	struct fw_device *device = fw_parent_device(unit);
+	int err, rcode;
+	u64 date;
+	__be32 cues[3] = {
+		MAUDIO_BOOTLOADER_CUE1,
+		MAUDIO_BOOTLOADER_CUE2,
+		MAUDIO_BOOTLOADER_CUE3
+	};
+
+	/* check date of software used to build */
+	err = snd_bebob_read_block(unit, INFO_OFFSET_SW_DATE,
+				   &date, sizeof(u64));
+	if (err < 0)
+		goto end;
+	/*
+	 * firmware version 5058 or later has date later than "20070401", but
+	 * 'date' is not null-terminated.
+	 */
+	if (date < 0x3230303730343031) {
+		dev_err(&unit->device,
+			"Use firmware version 5058 or later\n");
+		err = -ENOSYS;
+		goto end;
+	}
+
+	rcode = fw_run_transaction(device->card, TCODE_WRITE_BLOCK_REQUEST,
+				   device->node_id, device->generation,
+				   device->max_speed, BEBOB_ADDR_REG_REQ,
+				   cues, sizeof(cues));
+	if (rcode != RCODE_COMPLETE) {
+		dev_err(&unit->device,
+			"Failed to send a cue to load firmware\n");
+		err = -EIO;
+	}
+end:
+	return err;
+}
+
 static inline int
 get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
 {
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 39/39] bebob: Add support for M-Audio special Firewire series
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (37 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 38/39] bebob: Send a cue to load firmware for M-Audio " Takashi Sakamoto
@ 2014-02-28  3:27 ` Takashi Sakamoto
  2014-03-01 10:53   ` Stefan Richter
  2014-02-28  6:36 ` [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Iwai
  39 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28  3:27 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This commit allows this driver to support some models which M-Audio produces
with DM1000 but its firmware is special. They are:
 - Firewire 1814
 - ProjectMix I/O

They have heavily customized firmware. The usual operations can't be applied to
them. For this reason, this commit adds a model specific member in 'struct
snd_bebob' and some model specific functions. Some parameters are write-only so
this commit also adds control interface for applications.

M-Audio special firmware quirks:
 - Just after powering on, they wait to download firmware. This state is
   changed when receiving cue. Then bus reset is generated and the device is
   recognized as a different model with the uploaded firmware.
 - They don't respond against BridgeCo AV/C extension commands. So drivers
   can't get their stream formations and so on.
 - They do not start to transmit packets only by establishing connection but
   also by receiving SIGNAL FORMAT command.
 - They often handle requests without responding. In this reason, this driver
   often fail to handle them correctly.

This module don't support to upload firmware.

Here are three examples of transactions between BeBoB driver and M-Audio
Firewire 1814. An fallback at timeout in the operations for CMP was already
added by previous commit. But for isochronous resource management, no fallbacks
are added. Further works are needed for Linux Firewire Subsystem.

At releasing connections/resources for both direction:
[27200.724273] firewire_ohci 0000:0c:06.0: AT spd 2 tl 28, ffc0 -> ffc1, ack_pending , Lk req, fffff0000984 8,2
[27200.724921] firewire_ohci 0000:0c:06.0: AR spd 2 tl 28, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27200.725037] firewire_ohci 0000:0c:06.0: AT spd 0 tl 29, ffc0 -> ffc1, ack_pending , Lk req, fffff0000224 8,2
[27200.725598] firewire_ohci 0000:0c:06.0: AR spd 2 tl 29, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27200.725714] firewire_ohci 0000:0c:06.0: AT spd 0 tl 2a, ffc0 -> ffc1, ack_pending , Lk req, fffff0000220 8,2
[27202.727199] firewire_ohci 0000:0c:06.0: AT spd 0 tl 2b, ffc0 -> ffc1, ack_pending , Lk req, fffff0000220 8,2
[27202.727735] firewire_ohci 0000:0c:06.0: AR spd 2 tl 2b, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27202.727858] firewire_ohci 0000:0c:06.0: AT spd 2 tl 2c, ffc0 -> ffc1, ack_pending , Lk req, fffff0000904 8,2
[27204.750950] firewire_ohci 0000:0c:06.0: AT spd 2 tl 2d, ffc0 -> ffc1, ack_pending , Lk req, fffff0000904 8,2
[27206.777677] firewire_ohci 0000:0c:06.0: AT spd 2 tl 2e, ffc0 -> ffc1, ack_pending , Lk req, fffff0000904 8,2
[27208.779327] snd-bebob fw1.0: transaction failed: timeout
[27208.779333] snd-bebob fw1.0: oPCR0: plug is still connected
(The device handled a first operation for oPCR0 but not respond.)

At releasing connections/resources for both direction:
[27603.200092] firewire_ohci 0000:0c:06.0: AT spd 2 tl 02, ffc0 -> ffc1, ack_pending , Lk req, fffff0000904 8,2
[27603.200455] firewire_ohci 0000:0c:06.0: AR spd 2 tl 02, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27603.200629] firewire_ohci 0000:0c:06.0: AT spd 0 tl 03, ffc0 -> ffc1, ack_pending , Lk req, fffff0000224 8,2
[27605.203315] firewire_ohci 0000:0c:06.0: AT spd 0 tl 04, ffc0 -> ffc1, ack_pending , Lk req, fffff0000224 8,2
[27605.203962] firewire_ohci 0000:0c:06.0: AR spd 2 tl 04, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27605.204077] firewire_ohci 0000:0c:06.0: AT spd 0 tl 05, ffc0 -> ffc1, ack_pending , Lk req, fffff0000220 8,2
[27605.204507] firewire_ohci 0000:0c:06.0: AR spd 2 tl 05, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27605.204569] snd-bebob fw1.0: isochronous resource deallocation failed
(The device handled a first operation for CSR_CHANNELS_AVAILABLE_HI but not respond.)

At keeping resources and establising connections:
[27212.950366] firewire_ohci 0000:0c:06.0: AT spd 0 tl 35, ffc0 -> ffc1, ack_pending , Lk req, fffff0000224 8,2
[27212.950922] firewire_ohci 0000:0c:06.0: AR spd 2 tl 35, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.951060] firewire_ohci 0000:0c:06.0: AT spd 0 tl 36, ffc0 -> ffc1, ack_pending , Lk req, fffff0000224 8,2
[27212.951505] firewire_ohci 0000:0c:06.0: AR spd 2 tl 36, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.951590] firewire_ohci 0000:0c:06.0: AT spd 0 tl 37, ffc0 -> ffc1, ack_pending , Lk req, fffff0000220 8,2
[27212.952101] firewire_ohci 0000:0c:06.0: AR spd 2 tl 37, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.952220] firewire_ohci 0000:0c:06.0: AT spd 2 tl 38, ffc0 -> ffc1, ack_pending , Lk req, fffff0000904 8,2
[27212.952568] firewire_ohci 0000:0c:06.0: AR spd 2 tl 38, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.952715] firewire_ohci 0000:0c:06.0: AT spd 2 tl 39, ffc0 -> ffc1, ack_pending , Lk req, fffff0000904 8,2
[27212.953044] firewire_ohci 0000:0c:06.0: AR spd 2 tl 39, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.953178] firewire_ohci 0000:0c:06.0: AT spd 0 tl 3a, ffc0 -> ffc1, ack_pending , Lk req, fffff0000224 8,2
[27212.953504] firewire_ohci 0000:0c:06.0: AR spd 2 tl 3a, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.953588] firewire_ohci 0000:0c:06.0: AT spd 0 tl 3b, ffc0 -> ffc1, ack_pending , Lk req, fffff0000224 8,2
[27212.954017] firewire_ohci 0000:0c:06.0: AR spd 2 tl 3b, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.954077] firewire_ohci 0000:0c:06.0: AT spd 0 tl 3c, ffc0 -> ffc1, ack_pending , Lk req, fffff0000220 8,2
[27212.954364] firewire_ohci 0000:0c:06.0: AR spd 2 tl 3c, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27212.954429] firewire_ohci 0000:0c:06.0: AT spd 2 tl 3d, ffc0 -> ffc1, ack_pending , Lk req, fffff0000984 8,2
[27214.980602] firewire_ohci 0000:0c:06.0: AT spd 2 tl 3e, ffc0 -> ffc1, ack_pending , Lk req, fffff0000984 8,2
[27214.981135] firewire_ohci 0000:0c:06.0: AR spd 2 tl 3e, ffc1 -> ffc0, ack_complete, Lk resp 4,2
[27214.981195] snd-bebob fw1.0: iPCR0: plug is already in use
(The device handled a first operation for iPCR0 but not respond.)

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
---
 sound/firewire/Kconfig                |   1 +
 sound/firewire/bebob/bebob.c          |  21 +-
 sound/firewire/bebob/bebob.h          |  10 +-
 sound/firewire/bebob/bebob_command.c  |  16 +-
 sound/firewire/bebob/bebob_maudio.c   | 583 +++++++++++++++++++++++++++++++++-
 sound/firewire/bebob/bebob_stream.c   |  39 ++-
 sound/firewire/bebob/bebob_terratec.c |   6 +-
 sound/firewire/bebob/bebob_yamaha.c   |   2 +-
 8 files changed, 653 insertions(+), 25 deletions(-)

diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index f9aa176..b719ecf 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -115,6 +115,7 @@ config SND_BEBOB
 	  * Focusrite Saffire/Saffire LE/SaffirePro10 IO/SaffirePro26 IO
 	  * M-Audio Firewire410/AudioPhile/Solo
 	  * M-Audio Ozonic/NRV10/ProfireLightBridge
+	  * M-Audio Firewire 1814/ProjectMix IO
 
           To compile this driver as a module, choose M here: the module
           will be called snd-bebob.
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index a2104c7..862bd49 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -60,6 +60,8 @@ static unsigned int devices_used;
 
 #define MODEL_FOCUSRITE_SAFFIRE_BOTH	0x00000000
 #define MODEL_MAUDIO_AUDIOPHILE_BOTH	0x00010060
+#define MODEL_MAUDIO_FW1814		0x00010071
+#define MODEL_MAUDIO_PROJECTMIX		0x00010091
 
 static int
 name_device(struct snd_bebob *bebob, unsigned int vendor_id)
@@ -212,7 +214,14 @@ bebob_probe(struct fw_unit *unit,
 	if (err < 0)
 		goto error;
 
-	err = snd_bebob_stream_discover(bebob);
+	if ((entry->vendor_id == VEN_MAUDIO1) &&
+	    (entry->model_id == MODEL_MAUDIO_FW1814))
+		err = snd_bebob_maudio_special_discover(bebob, true);
+	else if ((entry->vendor_id == VEN_MAUDIO1) &&
+		 (entry->model_id == MODEL_MAUDIO_PROJECTMIX))
+		err = snd_bebob_maudio_special_discover(bebob, false);
+	else
+		err = snd_bebob_stream_discover(bebob);
 	if (err < 0)
 		goto error;
 
@@ -274,6 +283,9 @@ static void bebob_remove(struct fw_unit *unit)
 	if (bebob == NULL)
 		return;
 
+	if (bebob->maudio_special_quirk != NULL)
+		kfree(bebob->maudio_special_quirk);
+
 	snd_bebob_stream_destroy_duplex(bebob);
 	snd_card_disconnect(bebob->card);
 	snd_card_free_when_closed(bebob->card);
@@ -380,6 +392,13 @@ static const struct ieee1394_device_id bebob_id_table[] = {
 	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010081, &maudio_nrv10_spec),
 	/* M-Audio, ProFireLightbridge */
 	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x000100a1, &spec_normal),
+	/* Firewire 1814 */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, 0x00010070, NULL),	/* bootloader */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_FW1814,
+			    &maudio_special_spec),
+	/* M-Audio ProjectMix */
+	SND_BEBOB_DEV_ENTRY(VEN_MAUDIO1, MODEL_MAUDIO_PROJECTMIX,
+			    &maudio_special_spec),
 	/* IDs are unknown but able to be supported */
 	/*  Apogee, Mini-ME Firewire */
 	/*  Apogee, Mini-DAC Firewire */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index b08cbbb..394e0fb 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -101,6 +101,9 @@ struct snd_bebob {
 	int dev_lock_count;
 	bool dev_lock_changed;
 	wait_queue_head_t hwdep_wait;
+
+	/* for M-Audio special devices */
+	void *maudio_special_quirk;
 };
 
 static inline int
@@ -123,7 +126,7 @@ snd_bebob_read_quad(struct fw_unit *unit, u64 addr, u32 *buf)
 int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
 			   unsigned int fb_id, unsigned int num);
 int avc_audio_get_selector(struct fw_unit *unit, unsigned  int subunit_id,
-			   unsigned int fb_id, unsigned int *num);
+			   unsigned int fb_id, unsigned int *num, bool quiet);
 
 /*
  * AVC command extensions, AV/C Unit and Subunit, Revision 17
@@ -196,7 +199,7 @@ int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
 				   unsigned int *len);
 
 int snd_bebob_get_rate(struct snd_bebob *bebob, unsigned int *rate,
-		       enum avc_general_plug_dir dir);
+		       enum avc_general_plug_dir dir, bool quiet);
 int snd_bebob_set_rate(struct snd_bebob *bebob, unsigned int rate,
 		       enum avc_general_plug_dir dir);
 
@@ -242,6 +245,9 @@ extern struct snd_bebob_spec maudio_solo_spec;
 extern struct snd_bebob_spec maudio_ozonic_spec;
 extern struct snd_bebob_spec maudio_nrv10_spec;
 
+extern struct snd_bebob_spec maudio_special_spec;
+int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
+
 int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
 
 #define SND_BEBOB_DEV_ENTRY(vendor, model, data) \
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
index c63c6d8..ef7c21e 100644
--- a/sound/firewire/bebob/bebob_command.c
+++ b/sound/firewire/bebob/bebob_command.c
@@ -50,7 +50,7 @@ end:
 }
 
 int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
-			   unsigned int fb_id, unsigned int *num)
+			   unsigned int fb_id, unsigned int *num, bool quiet)
 {
 	u8 *buf;
 	int err;
@@ -76,9 +76,10 @@ int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
 	if (err < 0)
 		goto end;
 	if ((err < 6) || (buf[0] != 0x0c)) {
-		dev_err(&unit->device,
-			"failed to get selector %d: 0x%02X\n",
-			fb_id, buf[0]);
+		if (!quiet)
+			dev_err(&unit->device,
+				"failed to get selector %d: 0x%02X\n",
+				fb_id, buf[0]);
 		err = -EIO;
 		goto end;
 	}
@@ -314,7 +315,7 @@ end:
 }
 
 int snd_bebob_get_rate(struct snd_bebob *bebob, unsigned int *rate,
-		       enum avc_general_plug_dir dir)
+		       enum avc_general_plug_dir dir, bool quiet)
 {
 	int err;
 
@@ -324,8 +325,9 @@ int snd_bebob_get_rate(struct snd_bebob *bebob, unsigned int *rate,
 
 	/* IMPLEMENTED/STABLE is OK */
 	if (err != 0x0c) {
-		dev_err(&bebob->unit->device,
-			"failed to get sampling rate\n");
+		if (!quiet)
+			dev_err(&bebob->unit->device,
+				"failed to get sampling rate\n");
 		err = -EIO;
 	}
 end:
diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c
index e3ab42c..82f0c7d 100644
--- a/sound/firewire/bebob/bebob_maudio.c
+++ b/sound/firewire/bebob/bebob_maudio.c
@@ -7,9 +7,10 @@
  */
 
 #include "./bebob.h"
+#include <sound/control.h>
 
 /*
- * Just powering on, Firewire 410/Audiophile wait to
+ * Just powering on, Firewire 410/Audiophile/1814 and ProjectMix I/O wait to
  * download firmware blob. To enable these devices, drivers should upload
  * firmware blob and send a command to initialize configuration to factory
  * settings when completing uploading. Then these devices generate bus reset
@@ -31,8 +32,16 @@
  * Without streaming, the devices except for Firewire Audiophile can mix any
  * input and output. For this reason, Audiophile cannot be used as standalone
  * mixer.
+ *
+ * Firewire 1814 and ProjectMix I/O uses special firmware. It will be freezed
+ * when receiving any commands which the firmware can't understand. These
+ * devices utilize completely different system to control. It is some
+ * write-transaction directly into a certain address. All of addresses for mixer
+ * functionality is between 0xffc700700000 to 0xffc70070009c.
  */
 
+#define MAX_TRIALS	3
+
 /* Offset from information register */
 #define INFO_OFFSET_SW_DATE	0x20
 
@@ -52,6 +61,7 @@
 #define METER_OFFSET		0x00600000
 
 /* some device has sync info after metering data */
+#define METER_SIZE_SPECIAL	84	/* with sync info */
 #define METER_SIZE_FW410	76	/* with sync info */
 #define METER_SIZE_AUDIOPHILE	60	/* with sync info */
 #define METER_SIZE_SOLO		52	/* with sync info */
@@ -73,6 +83,16 @@
 /* for NRV */
 #define UNKNOWN_METER	"Unknown"
 
+struct special_params {
+	bool is1814;
+	unsigned int clk_src;
+	unsigned int dig_in_iface;
+	unsigned int dig_in_fmt;
+	unsigned int dig_out_fmt;
+	unsigned int clk_lock;
+	struct snd_ctl_elem_id *ctl_id_sync;
+};
+
 /*
  * For some M-Audio devices, this module just send cue to load firmware. After
  * loading, the device generates bus reset and newly detected.
@@ -127,7 +147,545 @@ get_meter(struct snd_bebob *bebob, void *buf, unsigned int size)
 				  buf, size, 0);
 }
 
-/* Firewire 410 specific operation */
+static int
+check_clk_sync(struct snd_bebob *bebob, unsigned int size, bool *sync)
+{
+	int err;
+	u8 *buf;
+
+	buf = kmalloc(size, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	err = get_meter(bebob, buf, size);
+	if (err < 0)
+		goto end;
+
+	/* if synced, this value is the same of SFC of FDF in CIP header */
+	*sync = (buf[size - 2] != 0xff);
+end:
+	kfree(buf);
+	return err;
+}
+
+/*
+ * dig_fmt: 0x00:S/PDIF, 0x01:ADAT
+ * clk_lock: 0x00:unlock, 0x01:lock
+ */
+static int
+special_clk_set_params(struct snd_bebob *bebob, unsigned int clk_src,
+		       unsigned int dig_in_fmt, unsigned int dig_out_fmt,
+		       unsigned int clk_lock)
+{
+	struct special_params *params = bebob->maudio_special_quirk;
+	int err;
+	u8 *buf;
+
+	if (amdtp_stream_running(&bebob->rx_stream) ||
+	    amdtp_stream_running(&bebob->tx_stream))
+		return -EBUSY;
+
+	buf = kmalloc(12, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	buf[0]  = 0x00;		/* CONTROL */
+	buf[1]  = 0xff;		/* UNIT */
+	buf[2]  = 0x00;		/* vendor dependent */
+	buf[3]  = 0x04;		/* company ID high */
+	buf[4]  = 0x00;		/* company ID middle */
+	buf[5]  = 0x04;		/* company ID low */
+	buf[6]  = 0xff & clk_src;	/* clock source */
+	buf[7]  = 0xff & dig_in_fmt;	/* input digital format */
+	buf[8]  = 0xff & dig_out_fmt;	/* output digital format */
+	buf[9]  = 0xff & clk_lock;	/* lock these settings */
+	buf[10] = 0x00;		/* padding  */
+	buf[11] = 0x00;		/* padding */
+
+	/* do transaction and check buf[1-9] are the same against command */
+	err = fcp_avc_transaction(bebob->unit, buf, 12, buf, 12,
+				  BIT(1) | BIT(2) | BIT(3) | BIT(4) |
+				  BIT(5) | BIT(6) | BIT(7) | BIT(8) |
+				  BIT(9));
+	if (err < 0)
+		goto end;
+	if ((err < 6) || (buf[0] != 0x09)) {
+		dev_err(&bebob->unit->device,
+			"failed to set clock params\n");
+		err = -EIO;
+		goto end;
+	}
+
+	params->clk_src		= buf[6];
+	params->dig_in_fmt	= buf[7];
+	params->dig_out_fmt	= buf[8];
+	params->clk_lock	= buf[9];
+
+	if (params->ctl_id_sync)
+		snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       params->ctl_id_sync);
+end:
+	kfree(buf);
+	return err;
+}
+static void
+special_stream_formation_set(struct snd_bebob *bebob)
+{
+	struct special_params *params = bebob->maudio_special_quirk;
+	unsigned int i;
+
+	/*
+	 * the stream formation is different depending on digital interface
+	 */
+	if (params->dig_in_fmt == 0x01) {
+		bebob->tx_stream_formations[1].pcm = 16;
+		bebob->tx_stream_formations[2].pcm = 16;
+		bebob->tx_stream_formations[3].pcm = 12;
+		bebob->tx_stream_formations[4].pcm = 12;
+		if (params->is1814) {
+			bebob->tx_stream_formations[5].pcm = 2;
+			bebob->tx_stream_formations[6].pcm = 2;
+		}
+	} else {
+		bebob->tx_stream_formations[1].pcm = 10;
+		bebob->tx_stream_formations[2].pcm = 10;
+		bebob->tx_stream_formations[3].pcm = 10;
+		bebob->tx_stream_formations[4].pcm = 10;
+		if (params->is1814) {
+			bebob->tx_stream_formations[5].pcm = 2;
+			bebob->tx_stream_formations[6].pcm = 2;
+		}
+	}
+
+	if (params->dig_out_fmt == 0x01) {
+		bebob->rx_stream_formations[1].pcm = 12;
+		bebob->rx_stream_formations[2].pcm = 12;
+		bebob->rx_stream_formations[3].pcm = 8;
+		bebob->rx_stream_formations[4].pcm = 8;
+		if (params->is1814) {
+			bebob->rx_stream_formations[5].pcm = 4;
+			bebob->rx_stream_formations[6].pcm = 4;
+		}
+	} else {
+		bebob->rx_stream_formations[1].pcm = 6;
+		bebob->rx_stream_formations[2].pcm = 6;
+		bebob->rx_stream_formations[3].pcm = 6;
+		bebob->rx_stream_formations[4].pcm = 6;
+		if (params->is1814) {
+			bebob->rx_stream_formations[5].pcm = 4;
+			bebob->rx_stream_formations[6].pcm = 4;
+		}
+	}
+
+	for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; i++) {
+		bebob->tx_stream_formations[i].midi = 1;
+		bebob->rx_stream_formations[i].midi = 1;
+		if ((i > 4) && !params->is1814)
+			break;
+	}
+}
+
+static int snd_bebob_maudio_special_add_controls(struct snd_bebob *bebob);
+int
+snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814)
+{
+	unsigned int trials;
+	struct special_params *params;
+	int err;
+
+	params = kmalloc(sizeof(struct special_params), GFP_KERNEL);
+	if (params == NULL)
+		return -ENOMEM;
+
+	mutex_lock(&bebob->mutex);
+
+	bebob->maudio_special_quirk = (void *)params;
+	params->is1814 = is1814;
+
+	/* initialize these parameters because driver is not allowed to ask */
+	bebob->rx_stream.context = ERR_PTR(-1);
+	bebob->tx_stream.context = ERR_PTR(-1);
+	trials = 0;
+	do {
+		err = special_clk_set_params(bebob, 0x03, 0x00, 0x00, 0x00);
+		if (err >= 0)
+			break;
+	} while (++trials < MAX_TRIALS);
+	if (err < 0) {
+		dev_err(&bebob->unit->device,
+			"failed to initialize clock params\n");
+		goto end;
+	}
+
+	trials = 0;
+	do {
+		err = avc_audio_get_selector(bebob->unit, 0x00, 0x04,
+					     &params->dig_in_iface, true);
+		if (err >= 0)
+			break;
+	} while (++trials < MAX_TRIALS);
+	if (err < 0) {
+		dev_err(&bebob->unit->device,
+			"failed to get current dig iface.");
+		goto end;
+	}
+
+	err = snd_bebob_maudio_special_add_controls(bebob);
+	if (err < 0)
+		goto end;
+
+	special_stream_formation_set(bebob);
+
+	if (params->is1814) {
+		bebob->midi_input_ports = 1;
+		bebob->midi_output_ports = 1;
+	} else {
+		bebob->midi_input_ports = 2;
+		bebob->midi_output_ports = 2;
+	}
+end:
+	if (err < 0) {
+		kfree(params);
+		bebob->maudio_special_quirk = NULL;
+	}
+	mutex_unlock(&bebob->mutex);
+	return err;
+}
+
+/* Input plug shows actual rate. Output plug is needless for this purpose. */
+static int special_get_rate(struct snd_bebob *bebob, unsigned int *rate)
+{
+	unsigned int trials;
+	int err;
+
+	trials = 0;
+	do {
+		err = snd_bebob_get_rate(bebob, rate, AVC_GENERAL_PLUG_DIR_IN,
+					 true);
+		if (err >= 0)
+			break;
+	} while (++trials < MAX_TRIALS);
+	if (err < 0)
+		dev_err(&bebob->unit->device,
+			"failed to get sampling rate\n");
+
+	return err;
+}
+static int special_set_rate(struct snd_bebob *bebob, unsigned int rate)
+{
+	struct special_params *params = bebob->maudio_special_quirk;
+	int err;
+
+	err = snd_bebob_set_rate(bebob, rate, AVC_GENERAL_PLUG_DIR_OUT);
+	if (err < 0)
+		goto end;
+
+	err = snd_bebob_set_rate(bebob, rate, AVC_GENERAL_PLUG_DIR_IN);
+	if (err < 0)
+		goto end;
+
+	if (params->ctl_id_sync)
+		snd_ctl_notify(bebob->card, SNDRV_CTL_EVENT_MASK_VALUE,
+			       params->ctl_id_sync);
+end:
+	return err;
+}
+
+/* Clock source control for special firmware */
+static char *special_clk_labels[] = {
+	SND_BEBOB_CLOCK_INTERNAL " with Digital Mute", "Digital",
+	"Word Clock", SND_BEBOB_CLOCK_INTERNAL};
+static int special_clk_get(struct snd_bebob *bebob, unsigned int *id)
+{
+	struct special_params *params = bebob->maudio_special_quirk;
+	*id = params->clk_src;
+	return 0;
+}
+static int special_clk_ctl_info(struct snd_kcontrol *kctl,
+				struct snd_ctl_elem_info *einf)
+{
+	einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	einf->count = 1;
+	einf->value.enumerated.items = ARRAY_SIZE(special_clk_labels);
+
+	if (einf->value.enumerated.item >= einf->value.enumerated.items)
+		einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+	strcpy(einf->value.enumerated.name,
+	       special_clk_labels[einf->value.enumerated.item]);
+
+	return 0;
+}
+static int special_clk_ctl_get(struct snd_kcontrol *kctl,
+			       struct snd_ctl_elem_value *uval)
+{
+	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+	struct special_params *params = bebob->maudio_special_quirk;
+	uval->value.enumerated.item[0] = params->clk_src;
+	return 0;
+}
+static int special_clk_ctl_put(struct snd_kcontrol *kctl,
+			       struct snd_ctl_elem_value *uval)
+{
+	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+	struct special_params *params = bebob->maudio_special_quirk;
+	int err, id;
+
+	mutex_lock(&bebob->mutex);
+
+	id = uval->value.enumerated.item[0];
+	if (id >= ARRAY_SIZE(special_clk_labels))
+		return 0;
+
+	err = special_clk_set_params(bebob, id,
+				     params->dig_in_fmt,
+				     params->dig_out_fmt,
+				     params->clk_lock);
+	mutex_unlock(&bebob->mutex);
+
+	return err >= 0;
+}
+static struct snd_kcontrol_new special_clk_ctl = {
+	.name	= "Clock Source",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info	= special_clk_ctl_info,
+	.get	= special_clk_ctl_get,
+	.put	= special_clk_ctl_put
+};
+
+/* Clock synchronization control for special firmware */
+static int special_sync_ctl_info(struct snd_kcontrol *kctl,
+				 struct snd_ctl_elem_info *einf)
+{
+	einf->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+	einf->count = 1;
+	einf->value.integer.min = 0;
+	einf->value.integer.max = 1;
+
+	return 0;
+}
+static int special_sync_ctl_get(struct snd_kcontrol *kctl,
+				struct snd_ctl_elem_value *uval)
+{
+	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+	int err;
+	bool synced = 0;
+
+	err = check_clk_sync(bebob, METER_SIZE_SPECIAL, &synced);
+	if (err >= 0)
+		uval->value.integer.value[0] = synced;
+
+	return 0;
+}
+static struct snd_kcontrol_new special_sync_ctl = {
+	.name	= "Sync Status",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READ,
+	.info	= special_sync_ctl_info,
+	.get	= special_sync_ctl_get,
+};
+
+/* Digital interface control for special firmware */
+static char *special_dig_iface_labels[] = {
+	"S/PDIF Optical", "S/PDIF Coaxial", "ADAT Optical"
+};
+static int special_dig_in_iface_ctl_info(struct snd_kcontrol *kctl,
+					 struct snd_ctl_elem_info *einf)
+{
+	einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	einf->count = 1;
+	einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels);
+
+	if (einf->value.enumerated.item >= einf->value.enumerated.items)
+		einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+	strcpy(einf->value.enumerated.name,
+	       special_dig_iface_labels[einf->value.enumerated.item]);
+
+	return 0;
+}
+static int special_dig_in_iface_ctl_get(struct snd_kcontrol *kctl,
+					struct snd_ctl_elem_value *uval)
+{
+	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+	struct special_params *params = bebob->maudio_special_quirk;
+	int val;
+
+	/* encoded id for user value */
+	val = (params->dig_in_fmt << 1) | (params->dig_in_iface & 0x01);
+
+	/* for ADAT Optical */
+	if (val > 2)
+		val = 2;
+
+	uval->value.enumerated.item[0] = val;
+
+	return 0;
+}
+static int special_dig_in_iface_ctl_set(struct snd_kcontrol *kctl,
+					struct snd_ctl_elem_value *uval)
+{
+	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+	struct special_params *params = bebob->maudio_special_quirk;
+	unsigned int id, dig_in_fmt, dig_in_iface;
+	int err;
+
+	mutex_lock(&bebob->mutex);
+
+	id = uval->value.enumerated.item[0];
+
+	/* decode user value */
+	dig_in_fmt = (id >> 1) & 0x01;
+	dig_in_iface = id & 0x01;
+
+	err = special_clk_set_params(bebob, params->clk_src, dig_in_fmt,
+				     params->dig_out_fmt, params->clk_lock);
+	if ((err < 0) || (params->dig_in_fmt > 0)) /* ADAT */
+		goto end;
+
+	err = avc_audio_set_selector(bebob->unit, 0x00, 0x04, dig_in_iface);
+	if (err < 0)
+		goto end;
+
+	params->dig_in_iface = dig_in_iface;
+end:
+	special_stream_formation_set(bebob);
+	mutex_unlock(&bebob->mutex);
+	return err;
+}
+static struct snd_kcontrol_new special_dig_in_iface_ctl = {
+	.name	= "Digital Input Interface",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info	= special_dig_in_iface_ctl_info,
+	.get	= special_dig_in_iface_ctl_get,
+	.put	= special_dig_in_iface_ctl_set
+};
+
+static int special_dig_out_iface_ctl_info(struct snd_kcontrol *kctl,
+					  struct snd_ctl_elem_info *einf)
+{
+	einf->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+	einf->count = 1;
+	einf->value.enumerated.items = ARRAY_SIZE(special_dig_iface_labels) - 1;
+
+	if (einf->value.enumerated.item >= einf->value.enumerated.items)
+		einf->value.enumerated.item = einf->value.enumerated.items - 1;
+
+	strcpy(einf->value.enumerated.name,
+	       special_dig_iface_labels[einf->value.enumerated.item + 1]);
+
+	return 0;
+}
+static int special_dig_out_iface_ctl_get(struct snd_kcontrol *kctl,
+					 struct snd_ctl_elem_value *uval)
+{
+	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+	struct special_params *params = bebob->maudio_special_quirk;
+	mutex_lock(&bebob->mutex);
+	uval->value.enumerated.item[0] = params->dig_out_fmt;
+	mutex_unlock(&bebob->mutex);
+	return 0;
+}
+static int special_dig_out_iface_ctl_set(struct snd_kcontrol *kctl,
+					 struct snd_ctl_elem_value *uval)
+{
+	struct snd_bebob *bebob = snd_kcontrol_chip(kctl);
+	struct special_params *params = bebob->maudio_special_quirk;
+	unsigned int id;
+	int err;
+
+	mutex_lock(&bebob->mutex);
+
+	id = uval->value.enumerated.item[0];
+
+	err = special_clk_set_params(bebob, params->clk_src, params->dig_in_fmt,
+				     id, params->clk_lock);
+	if (err >= 0)
+		special_stream_formation_set(bebob);
+
+	mutex_unlock(&bebob->mutex);
+	return err;
+}
+static struct snd_kcontrol_new special_dig_out_iface_ctl = {
+	.name	= "Digital Output Interface",
+	.iface	= SNDRV_CTL_ELEM_IFACE_MIXER,
+	.access	= SNDRV_CTL_ELEM_ACCESS_READWRITE,
+	.info	= special_dig_out_iface_ctl_info,
+	.get	= special_dig_out_iface_ctl_get,
+	.put	= special_dig_out_iface_ctl_set
+};
+
+static int snd_bebob_maudio_special_add_controls(struct snd_bebob *bebob)
+{
+	struct snd_kcontrol *kctl;
+	struct special_params *params = bebob->maudio_special_quirk;
+	int err;
+
+	kctl = snd_ctl_new1(&special_clk_ctl, bebob);
+	err = snd_ctl_add(bebob->card, kctl);
+	if (err < 0)
+		goto end;
+
+	kctl = snd_ctl_new1(&special_sync_ctl, bebob);
+	err = snd_ctl_add(bebob->card, kctl);
+	if (err < 0)
+		goto end;
+	params->ctl_id_sync = &kctl->id;
+
+	kctl = snd_ctl_new1(&special_dig_in_iface_ctl, bebob);
+	err = snd_ctl_add(bebob->card, kctl);
+	if (err < 0)
+		goto end;
+
+	kctl = snd_ctl_new1(&special_dig_out_iface_ctl, bebob);
+	err = snd_ctl_add(bebob->card, kctl);
+end:
+	return err;
+}
+
+/* Hardware metering for special firmware */
+static char *special_meter_labels[] = {
+	ANA_IN, ANA_IN, ANA_IN, ANA_IN,
+	SPDIF_IN,
+	ADAT_IN, ADAT_IN, ADAT_IN, ADAT_IN,
+	ANA_OUT, ANA_OUT,
+	SPDIF_OUT,
+	ADAT_OUT, ADAT_OUT, ADAT_OUT, ADAT_OUT,
+	HP_OUT, HP_OUT,
+	AUX_OUT
+};
+static int
+special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
+{
+	u16 *buf;
+	unsigned int i, c, channels;
+	int err;
+
+	channels = ARRAY_SIZE(special_meter_labels) * 2;
+	if (size < channels * sizeof(u32))
+		return -EINVAL;
+
+	/* omit last 4 bytes because it's clock info. */
+	buf = kmalloc(METER_SIZE_SPECIAL - 4, GFP_KERNEL);
+	if (buf == NULL)
+		return -ENOMEM;
+
+	err = get_meter(bebob, (void *)buf, METER_SIZE_SPECIAL - 4);
+	if (err < 0)
+		goto end;
+
+	/* some channels are not used, and format is u16 */
+	i = 0;
+	for (c = 2; c < channels + 2; c++)
+		target[i++] = be16_to_cpu(buf[c]) << 16;
+end:
+	kfree(buf);
+	return err;
+}
+
+/* Firewire 410 specific operations */
 static char *fw410_meter_labels[] = {
 	ANA_IN, DIG_IN,
 	ANA_OUT, ANA_OUT, ANA_OUT, ANA_OUT, DIG_OUT,
@@ -278,6 +836,27 @@ end:
 	return err;
 }
 
+/* for special customized devices */
+static struct snd_bebob_rate_spec special_rate_spec = {
+	.get	= &special_get_rate,
+	.set	= &special_set_rate,
+};
+static struct snd_bebob_clock_spec special_clk_spec = {
+	.num	= ARRAY_SIZE(special_clk_labels),
+	.labels	= special_clk_labels,
+	.get	= &special_clk_get,
+};
+static struct snd_bebob_meter_spec special_meter_spec = {
+	.num	= ARRAY_SIZE(special_meter_labels),
+	.labels	= special_meter_labels,
+	.get	= &special_meter_get
+};
+struct snd_bebob_spec maudio_special_spec = {
+	.clock	= &special_clk_spec,
+	.rate	= &special_rate_spec,
+	.meter	= &special_meter_spec
+};
+
 /* Firewire 410 specification */
 static struct snd_bebob_rate_spec usual_rate_spec = {
 	.get	= &snd_bebob_stream_get_rate,
diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c
index 92e91a7..1939e16 100644
--- a/sound/firewire/bebob/bebob_stream.c
+++ b/sound/firewire/bebob/bebob_stream.c
@@ -64,11 +64,13 @@ snd_bebob_stream_get_rate(struct snd_bebob *bebob, unsigned int *curr_rate)
 	unsigned int tx_rate, rx_rate;
 	int err;
 
-	err = snd_bebob_get_rate(bebob, &tx_rate, AVC_GENERAL_PLUG_DIR_OUT);
+	err = snd_bebob_get_rate(bebob, &tx_rate, AVC_GENERAL_PLUG_DIR_OUT,
+				 false);
 	if (err < 0)
 		goto end;
 
-	err = snd_bebob_get_rate(bebob, &rx_rate, AVC_GENERAL_PLUG_DIR_IN);
+	err = snd_bebob_get_rate(bebob, &rx_rate, AVC_GENERAL_PLUG_DIR_IN,
+				 false);
 	if (err < 0)
 		goto end;
 
@@ -344,7 +346,6 @@ break_both_connections(struct snd_bebob *bebob)
 {
 	cmp_connection_break(&bebob->in_conn);
 	cmp_connection_break(&bebob->out_conn);
-	return;
 }
 
 static void
@@ -381,9 +382,11 @@ start_stream(struct snd_bebob *bebob, struct amdtp_stream *stream,
 		conn = &bebob->out_conn;
 
 	/* channel mapping */
-	err = map_data_channels(bebob, stream);
-	if (err < 0)
-		goto end;
+	if (bebob->maudio_special_quirk == NULL) {
+		err = map_data_channels(bebob, stream);
+		if (err < 0)
+			goto end;
+	}
 
 	/* start amdtp stream */
 	err = amdtp_stream_start(stream,
@@ -472,10 +475,14 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob,
 		 * NOTE:
 		 * If establishing connections at first, Yamaha GO46
 		 * (and maybe Terratec X24) don't generate sound.
+		 *
+		 * For firmware customized by M-Audio, refer to next NOTE.
 		 */
-		err = rate_spec->set(bebob, rate);
-		if (err < 0)
-			goto end;
+		if (bebob->maudio_special_quirk == NULL) {
+			err = rate_spec->set(bebob, rate);
+			if (err < 0)
+				goto end;
+		}
 
 		err = make_both_connections(bebob, rate);
 		if (err < 0)
@@ -489,6 +496,20 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob,
 			goto end;
 		}
 
+		/*
+		 * NOTE:
+		 * The firmware customized by M-Audio uses these commands to
+		 * start transmitting stream. This is not usual way.
+		 */
+		if (bebob->maudio_special_quirk != NULL) {
+			err = rate_spec->set(bebob, rate);
+			if (err < 0) {
+				amdtp_stream_stop(master);
+				break_both_connections(bebob);
+				goto end;
+			}
+		}
+
 		/* wait first callback */
 		if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT)) {
 			amdtp_stream_stop(master);
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index 9c2cebf..d5da8de 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -17,10 +17,10 @@ phase88_rack_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
 	unsigned int enable_ext, enable_word;
 	int err;
 
-	err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_ext);
+	err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_ext, false);
 	if (err < 0)
 		goto end;
-	err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_word);
+	err = avc_audio_get_selector(bebob->unit, 0, 0, &enable_word, false);
 	if (err < 0)
 		goto end;
 
@@ -35,7 +35,7 @@ static char *phase24_series_clk_src_labels[] = {
 static int
 phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
 {
-	return avc_audio_get_selector(bebob->unit, 0, 4, id);
+	return avc_audio_get_selector(bebob->unit, 0, 4, id, false);
 }
 
 struct snd_bebob_rate_spec phase_series_rate_spec = {
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha.c
index f71af6b..23ca489 100644
--- a/sound/firewire/bebob/bebob_yamaha.c
+++ b/sound/firewire/bebob/bebob_yamaha.c
@@ -32,7 +32,7 @@ static char *clk_src_labels[] = {SND_BEBOB_CLOCK_INTERNAL, "SPDIF"};
 static int
 clk_src_get(struct snd_bebob *bebob, unsigned int *id)
 {
-	return avc_audio_get_selector(bebob->unit, 0, 4, id);
+	return avc_audio_get_selector(bebob->unit, 0, 4, id, false);
 }
 static struct snd_bebob_clock_spec clock_spec = {
 	.num	= ARRAY_SIZE(clk_src_labels),
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices
  2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
                   ` (38 preceding siblings ...)
  2014-02-28  3:27 ` [PATCH 39/39] bebob: Add support for M-Audio special " Takashi Sakamoto
@ 2014-02-28  6:36 ` Takashi Iwai
  2014-03-01 13:14   ` Takashi Sakamoto
  39 siblings, 1 reply; 71+ messages in thread
From: Takashi Iwai @ 2014-02-28  6:36 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens, ffado-devel

At Fri, 28 Feb 2014 12:27:13 +0900,
Takashi Sakamoto wrote:
> 
> This is a pull-request of my 39 patches to enhance support for Firewire
> devices. These patches extend firewire-lib, add new drivers (fireworks/bebob).
> 
> These drivers can handle playback/capture of PCM samples/MIDI messages and
> can support 70-80 models totally.
> 
> These drivers are designed not to interrupt FFADO (user-land driver project)
> functionality except for playbacking/capturing.
> 
> These drivers basically give no way to control device's internal mixer. For
> this purpose, users need to use applications such like
> ffado-dbus-server/ffado-mixer, or develop software to execute AV/C commands
> and device specific commands.
> 
> 
> Updates since RFC v3:
> [alsa-devel] [RFC v3] [PATCH 00/52] Enhancement for support of firewire devices
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-January/071820.html 
> 
> firewire-lib:
>  - improve operation for oPCR
>  - add fallbacks at transaction timeout for device's quirk
> 
> fireworks:
>  - add counter to maintain streams
>  - use memdup_user() for hwdep interface
> 
> bebob:
>  - add counter to maintain streams
>  - fix failure to handle bus reset
> 
> ----------------------------------------------------------------
> 
> The following changes since commit dde9c38779c1f30253485a160facc211b20ca222:
> 
>   Merge branch 'for-linus' (2014-02-27 12:46:40 +0100)

Please don't commit on top of master branch of sound git tree.
The master branch is a result of quick merges from all branches, thus
inappropriate as a development basis.  Rebase to for-next branch
instead.  The end-result patches must be identical, so you don't have
to resend them to ML, though.


thanks,

Takashi

> 
> are available in the git repository at:
> 
> 
>   https://github.com/takaswie/sound.git request
> 
> for you to fetch changes up to 0ed07c65fef2f80fcfc6223b0d689fca56fa6323:
> 
>   bebob: Add support for M-Audio special Firewire series (2014-02-28 11:30:52 +0900)
> 
> ----------------------------------------------------------------
> Takashi Sakamoto (39):
>       firewire-lib: Rename functions, structure, member for AMDTP
>       firewire-lib: Add macros instead of fixed value for AMDTP
>       firewire-lib: Add 'direction' member to 'amdtp_stream' structure
>       firewire-lib: Split some codes into functions to reuse for both streams
>       firewire-lib: Add support for AMDTP in-stream and PCM capture
>       firewire-lib: Add support for MIDI capture/playback
>       firewire-lib: Give syt value as parameter to handle_out_packet()
>       firewire-lib: Add support for duplex streams synchronization in blocking mode
>       firewire-lib: Add sort function for transmitted packet
>       firewire-lib: Add transfer delay to synchronized duplex streams
>       firewire-lib: Add support for channel mapping
>       firewire-lib: Rename macros, variables and functions for CMP
>       firewire-lib: Add 'direction' member to 'cmp_connection' structure
>       firewire-lib: Add handling output connection by CMP
>       firewire-lib: Add a new function to check others' connection
>       firewire-lib: Add some AV/C general commands
>       firewire-lib: Add quirks for Fireworks
>       firewire-lib: Add a fallback at RCODE_CANCELLED
>       fireworks: Add skelton for Fireworks based devices
>       fireworks: Add transaction and some commands
>       fireworks: Add connection and stream management
>       fireworks: Add proc interface for debugging purpose
>       fireworks: Add MIDI interface
>       fireworks: Add PCM interface
>       fireworks: Add hwdep interface
>       fireworks: Add command/response functionality into hwdep interface
>       bebob: Add skelton for BeBoB based devices
>       bebob: Add commands and connections/streams management
>       bebob: Add proc interface for debugging purpose
>       bebob: Add MIDI interface
>       bebob: Add PCM interface
>       bebob: Add hwdep interface
>       bebob: Prepare for device specific operations
>       bebob: Add support for Terratec PHASE, EWS series and Aureon
>       bebob: Add support for Yamaha GO series
>       bebob: Add support for Focusrite Saffire/SaffirePro series
>       bebob: Add support for M-Audio usual Firewire series
>       bebob: Send a cue to load firmware for M-Audio Firewire series
>       bebob: Add support for M-Audio special Firewire series
> 
>  include/uapi/sound/asound.h                      |   4 +-
>  include/uapi/sound/firewire.h                    |  22 +-
>  sound/firewire/Kconfig                           |  59 ++
>  sound/firewire/Makefile                          |   2 +
>  sound/firewire/amdtp.c                           | 891 ++++++++++++++++------
>  sound/firewire/amdtp.h                           | 175 ++++-
>  sound/firewire/bebob/Makefile                    |   4 +
>  sound/firewire/bebob/bebob.c                     | 450 +++++++++++
>  sound/firewire/bebob/bebob.h                     | 262 +++++++
>  sound/firewire/bebob/bebob_command.c             | 354 +++++++++
>  sound/firewire/bebob/bebob_focusrite.c           | 289 +++++++
>  sound/firewire/bebob/bebob_hwdep.c               | 197 +++++
>  sound/firewire/bebob/bebob_maudio.c              | 922 +++++++++++++++++++++++
>  sound/firewire/bebob/bebob_midi.c                | 170 +++++
>  sound/firewire/bebob/bebob_pcm.c                 | 451 +++++++++++
>  sound/firewire/bebob/bebob_proc.c                | 179 +++++
>  sound/firewire/bebob/bebob_stream.c              | 869 +++++++++++++++++++++
>  sound/firewire/bebob/bebob_terratec.c            |  68 ++
>  sound/firewire/bebob/bebob_yamaha.c              |  50 ++
>  sound/firewire/cmp.c                             | 229 ++++--
>  sound/firewire/cmp.h                             |  14 +-
>  sound/firewire/dice.c                            |  46 +-
>  sound/firewire/fcp.c                             | 160 +++-
>  sound/firewire/fcp.h                             |  21 +
>  sound/firewire/fireworks/Makefile                |   4 +
>  sound/firewire/fireworks/fireworks.c             | 324 ++++++++
>  sound/firewire/fireworks/fireworks.h             | 223 ++++++
>  sound/firewire/fireworks/fireworks_command.c     | 397 ++++++++++
>  sound/firewire/fireworks/fireworks_hwdep.c       | 294 ++++++++
>  sound/firewire/fireworks/fireworks_midi.c        | 179 +++++
>  sound/firewire/fireworks/fireworks_pcm.c         | 477 ++++++++++++
>  sound/firewire/fireworks/fireworks_proc.c        | 208 +++++
>  sound/firewire/fireworks/fireworks_stream.c      | 337 +++++++++
>  sound/firewire/fireworks/fireworks_transaction.c | 327 ++++++++
>  sound/firewire/lib.c                             |   6 +-
>  sound/firewire/lib.h                             |   1 +
>  sound/firewire/speakers.c                        |  93 +--
>  37 files changed, 8356 insertions(+), 402 deletions(-)
>  create mode 100644 sound/firewire/bebob/Makefile
>  create mode 100644 sound/firewire/bebob/bebob.c
>  create mode 100644 sound/firewire/bebob/bebob.h
>  create mode 100644 sound/firewire/bebob/bebob_command.c
>  create mode 100644 sound/firewire/bebob/bebob_focusrite.c
>  create mode 100644 sound/firewire/bebob/bebob_hwdep.c
>  create mode 100644 sound/firewire/bebob/bebob_maudio.c
>  create mode 100644 sound/firewire/bebob/bebob_midi.c
>  create mode 100644 sound/firewire/bebob/bebob_pcm.c
>  create mode 100644 sound/firewire/bebob/bebob_proc.c
>  create mode 100644 sound/firewire/bebob/bebob_stream.c
>  create mode 100644 sound/firewire/bebob/bebob_terratec.c
>  create mode 100644 sound/firewire/bebob/bebob_yamaha.c
>  create mode 100644 sound/firewire/fireworks/Makefile
>  create mode 100644 sound/firewire/fireworks/fireworks.c
>  create mode 100644 sound/firewire/fireworks/fireworks.h
>  create mode 100644 sound/firewire/fireworks/fireworks_command.c
>  create mode 100644 sound/firewire/fireworks/fireworks_hwdep.c
>  create mode 100644 sound/firewire/fireworks/fireworks_midi.c
>  create mode 100644 sound/firewire/fireworks/fireworks_pcm.c
>  create mode 100644 sound/firewire/fireworks/fireworks_proc.c
>  create mode 100644 sound/firewire/fireworks/fireworks_stream.c
>  create mode 100644 sound/firewire/fireworks/fireworks_transaction.c
> -- 
> 1.8.3.2
> 

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 09/39] firewire-lib: Add sort function for transmitted packet
  2014-02-28  3:27 ` [PATCH 09/39] firewire-lib: Add sort function for transmitted packet Takashi Sakamoto
@ 2014-02-28  6:40   ` Takashi Iwai
  2014-02-28 14:31     ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Iwai @ 2014-02-28  6:40 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens, ffado-devel

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

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 19/39] fireworks: Add skelton for Fireworks based devices
  2014-02-28  3:27 ` [PATCH 19/39] fireworks: Add skelton for Fireworks based devices Takashi Sakamoto
@ 2014-02-28  6:51   ` Takashi Iwai
  2014-02-28 15:05     ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Iwai @ 2014-02-28  6:51 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens, ffado-devel

At Fri, 28 Feb 2014 12:27:32 +0900,
Takashi Sakamoto wrote:
> 
> This commit adds a new driver with no functionality. This driver just
> creates/removes card instance according to callbacks.
> 
> Fireworks is a board module which Echo Audio produced. This module consists
> of three chipsets:
>  - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
>  - DSP or/and FPGA for signal processing
>  - Flash Memory to store firmwares
> 
> Current supported devices:
>  - Mackie Onyx 400F/1200F
>  - Echo AudioFire12/8(until 2009 July)
>  - Echo AudioFire2/4/Pre8/8(since 2009 July)
>  - Echo Fireworks 8/HDMI
>  - Gibson Robot Interface pack/GoldTop
> 
> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
> ---
>  sound/firewire/Kconfig               |  14 +++
>  sound/firewire/Makefile              |   1 +
>  sound/firewire/fireworks/Makefile    |   2 +
>  sound/firewire/fireworks/fireworks.c | 180 +++++++++++++++++++++++++++++++++++
>  sound/firewire/fireworks/fireworks.h |  41 ++++++++
>  5 files changed, 238 insertions(+)
>  create mode 100644 sound/firewire/fireworks/Makefile
>  create mode 100644 sound/firewire/fireworks/fireworks.c
>  create mode 100644 sound/firewire/fireworks/fireworks.h
> 
> diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
> index b3e274f..8cd4f1f 100644
> --- a/sound/firewire/Kconfig
> +++ b/sound/firewire/Kconfig
> @@ -61,4 +61,18 @@ config SND_SCS1X
>  	  To compile this driver as a module, choose M here: the module
>  	  will be called snd-scs1x.
>  
> +config SND_FIREWORKS
> +	tristate "Echo Fireworks board module support"
> +	help
> +	  Say Y here to include support for FireWire devices based
> +	  on Echo Digital Audio Fireworks board:
> +	   * Mackie Onyx 400F/1200F
> +	   * Echo AudioFire12/8(until 2009 July)
> +	   * Echo AudioFire2/4/Pre8/8(since 2009 July)
> +	   * Echo Fireworks 8/HDMI
> +	   * Gibson Robot Interface Pack/GoldTop
> +
> +	  To compile this driver as a module, choose M here: the module
> +	  will be called snd-fireworks.
> +
>  endif # SND_FIREWIRE
> diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
> index 5099550..5cd39dc 100644
> --- a/sound/firewire/Makefile
> +++ b/sound/firewire/Makefile
> @@ -10,3 +10,4 @@ obj-$(CONFIG_SND_DICE) += snd-dice.o
>  obj-$(CONFIG_SND_FIREWIRE_SPEAKERS) += snd-firewire-speakers.o
>  obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
>  obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
> +obj-$(CONFIG_SND_FIREWORKS) += fireworks/
> diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
> new file mode 100644
> index 0000000..99f6fc3
> --- /dev/null
> +++ b/sound/firewire/fireworks/Makefile
> @@ -0,0 +1,2 @@
> +snd-fireworks-objs := fireworks.o
> +obj-m += snd-fireworks.o
> diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
> new file mode 100644
> index 0000000..4a3f79e
> --- /dev/null
> +++ b/sound/firewire/fireworks/fireworks.c
> @@ -0,0 +1,180 @@
> +/*
> + * fireworks.c - a part of driver for Fireworks based devices
> + *
> + * Copyright (c) 2009-2010 Clemens Ladisch
> + * Copyright (c) 2013 Takashi Sakamoto
> + *
> + * Licensed under the terms of the GNU General Public License, version 2.
> + */
> +
> +/*
> + * Fireworks is a board module which Echo Audio produced. This module consists
> + * of three chipsets:
> + *  - Communication chipset for IEEE1394 PHY/Link and IEC 61883-1/6
> + *  - DSP or/and FPGA for signal processing
> + *  - Flash Memory to store firmwares
> + */
> +
> +#include "fireworks.h"
> +
> +MODULE_DESCRIPTION("Echo Fireworks driver");
> +MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
> +MODULE_LICENSE("GPL v2");
> +
> +static int index[SNDRV_CARDS]	= SNDRV_DEFAULT_IDX;
> +static char *id[SNDRV_CARDS]	= SNDRV_DEFAULT_STR;
> +static bool enable[SNDRV_CARDS]	= SNDRV_DEFAULT_ENABLE_PNP;
> +
> +module_param_array(index, int, NULL, 0444);
> +MODULE_PARM_DESC(index, "card index");
> +module_param_array(id, charp, NULL, 0444);
> +MODULE_PARM_DESC(id, "ID string");
> +module_param_array(enable, bool, NULL, 0444);
> +MODULE_PARM_DESC(enable, "enable Fireworks sound card");
> +
> +static DEFINE_MUTEX(devices_mutex);
> +static unsigned int devices_used;

Now we may handle more than 32 cards, depending on Kconfig.
Use bitmap instead.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 22/39] fireworks: Add proc interface for debugging purpose
  2014-02-28  3:27 ` [PATCH 22/39] fireworks: Add proc interface for debugging purpose Takashi Sakamoto
@ 2014-02-28  6:54   ` Takashi Iwai
  2014-03-01 13:17     ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Iwai @ 2014-02-28  6:54 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens, ffado-devel

At Fri, 28 Feb 2014 12:27:35 +0900,
Takashi Sakamoto wrote:
> 
> This commit adds proc interface to output infomation for debugging.
>  - firmware information
>  - sampling rate and clock source
>  - physical metering (linear value)
> 
> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
> ---
>  sound/firewire/fireworks/Makefile         |   2 +-
>  sound/firewire/fireworks/fireworks.c      |  14 ++-
>  sound/firewire/fireworks/fireworks.h      |  11 ++
>  sound/firewire/fireworks/fireworks_proc.c | 189 ++++++++++++++++++++++++++++++
>  4 files changed, 213 insertions(+), 3 deletions(-)
>  create mode 100644 sound/firewire/fireworks/fireworks_proc.c
> 
> diff --git a/sound/firewire/fireworks/Makefile b/sound/firewire/fireworks/Makefile
> index 1bccb65..52bd15e 100644
> --- a/sound/firewire/fireworks/Makefile
> +++ b/sound/firewire/fireworks/Makefile
> @@ -1,3 +1,3 @@
>  snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
> -		      fireworks_stream.o fireworks.o
> +		      fireworks_stream.o fireworks_proc.o fireworks.o
>  obj-m += snd-fireworks.o
> diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
> index 6d7de19..e370204 100644
> --- a/sound/firewire/fireworks/fireworks.c
> +++ b/sound/firewire/fireworks/fireworks.c
> @@ -130,6 +130,16 @@ get_hardware_info(struct snd_efw *efw)
>  	efw->pcm_playback_channels[0] = hwinfo->amdtp_rx_pcm_channels;
>  	efw->pcm_playback_channels[1] = hwinfo->amdtp_rx_pcm_channels_2x;
>  	efw->pcm_playback_channels[2] = hwinfo->amdtp_rx_pcm_channels_4x;
> +
> +	/* hardware metering */
> +	efw->phys_out = hwinfo->phys_out;
> +	efw->phys_in = hwinfo->phys_in;
> +	efw->phys_out_grp_count = hwinfo->phys_out_grp_count;
> +	efw->phys_in_grp_count = hwinfo->phys_in_grp_count;
> +	memcpy(&efw->phys_out_grps, hwinfo->phys_out_grps,
> +	       sizeof(struct snd_efw_phys_grp) * HWINFO_MAX_CAPS_GROUPS);
> +	memcpy(&efw->phys_in_grps, hwinfo->phys_in_grps,
> +	       sizeof(struct snd_efw_phys_grp) * HWINFO_MAX_CAPS_GROUPS);
>  end:
>  	kfree(hwinfo);
>  	return err;
> @@ -189,6 +199,8 @@ efw_probe(struct fw_unit *unit,
>  	if (err < 0)
>  		goto error;
>  
> +	snd_efw_proc_init(efw);
> +
>  	err = snd_card_register(card);
>  	if (err < 0)
>  		goto error;
> @@ -211,8 +223,6 @@ static void efw_update(struct fw_unit *unit)
>  
>  	snd_efw_transaction_bus_reset(efw->unit);
>  	snd_efw_stream_update_duplex(efw);
> -
> -	return;
>  }
>  
>  static void efw_remove(struct fw_unit *unit)
> diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
> index c022c71..4d69c0d 100644
> --- a/sound/firewire/fireworks/fireworks.h
> +++ b/sound/firewire/fireworks/fireworks.h
> @@ -21,6 +21,7 @@
>  #include <sound/core.h>
>  #include <sound/initval.h>
>  #include <sound/pcm.h>
> +#include <sound/info.h>
>  
>  #include "../packets-buffer.h"
>  #include "../iso-resources.h"
> @@ -65,6 +66,14 @@ struct snd_efw {
>  	struct cmp_connection in_conn;
>  	unsigned int capture_substreams;
>  	unsigned int playback_substreams;
> +
> +	/* hardware metering parameters */
> +	unsigned int phys_out;
> +	unsigned int phys_in;
> +	unsigned int phys_out_grp_count;
> +	unsigned int phys_in_grp_count;
> +	struct snd_efw_phys_grp phys_out_grps[HWINFO_MAX_CAPS_GROUPS];
> +	struct snd_efw_phys_grp phys_in_grps[HWINFO_MAX_CAPS_GROUPS];
>  };
>  
>  struct snd_efw_transaction {
> @@ -178,6 +187,8 @@ int snd_efw_stream_stop_duplex(struct snd_efw *efw);
>  void snd_efw_stream_update_duplex(struct snd_efw *efw);
>  void snd_efw_stream_destroy_duplex(struct snd_efw *efw);
>  
> +void snd_efw_proc_init(struct snd_efw *efw);
> +
>  #define SND_EFW_DEV_ENTRY(vendor, model) \
>  { \
>  	.match_flags	= IEEE1394_MATCH_VENDOR_ID | \
> diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
> new file mode 100644
> index 0000000..62f758b
> --- /dev/null
> +++ b/sound/firewire/fireworks/fireworks_proc.c
> @@ -0,0 +1,189 @@
> +/*
> + * fireworks_proc.c - a part of driver for Fireworks based devices
> + *
> + * Copyright (c) 2009-2010 Clemens Ladisch
> + * Copyright (c) 2013 Takashi Sakamoto
> + *
> + * Licensed under the terms of the GNU General Public License, version 2.
> + */
> +
> +#include "./fireworks.h"
> +
> +static inline const char*
> +get_phys_name(struct snd_efw_phys_grp *grp)
> +{
> +	const char *ch_type[] = {
> +		"Analog", "S/PDIF", "ADAT", "S/PDIF or ADAT",
> +		"Mirroring", "Headphones", "I2S", "Guitar",
> +		"Pirzo Guitar", "Guitar String", "Virtual", "Dummy"
> +	};
> +
> +	if (grp->type < 10)
> +		return ch_type[grp->type];
> +	else if (grp->type == 0x10000)
> +		return ch_type[10];
> +	else
> +		return ch_type[11];
> +}
> +
> +static void
> +proc_read_hwinfo(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
> +{
> +	struct snd_efw *efw = entry->private_data;
> +	unsigned short i;
> +	struct snd_efw_hwinfo *hwinfo;
> +
> +	hwinfo = kmalloc(sizeof(struct snd_efw_hwinfo), GFP_KERNEL);
> +	if (hwinfo == NULL)
> +		return;
> +
> +	if (snd_efw_command_get_hwinfo(efw, hwinfo) < 0)
> +		goto end;
> +
> +	snd_iprintf(buffer, "guid_hi: 0x%X\n", hwinfo->guid_hi);
> +	snd_iprintf(buffer, "guid_lo: 0x%X\n", hwinfo->guid_lo);
> +	snd_iprintf(buffer, "type: 0x%X\n", hwinfo->type);
> +	snd_iprintf(buffer, "version: 0x%X\n", hwinfo->version);
> +	snd_iprintf(buffer, "vendor_name: %s\n", hwinfo->vendor_name);
> +	snd_iprintf(buffer, "model_name: %s\n", hwinfo->model_name);
> +
> +	snd_iprintf(buffer, "dsp_version: 0x%X\n", hwinfo->dsp_version);
> +	snd_iprintf(buffer, "arm_version: 0x%X\n", hwinfo->arm_version);
> +	snd_iprintf(buffer, "fpga_version: 0x%X\n", hwinfo->fpga_version);
> +
> +	snd_iprintf(buffer, "flags: 0x%X\n", hwinfo->flags);
> +
> +	snd_iprintf(buffer, "max_sample_rate: 0x%X\n", hwinfo->max_sample_rate);
> +	snd_iprintf(buffer, "min_sample_rate: 0x%X\n", hwinfo->min_sample_rate);
> +	snd_iprintf(buffer, "supported_clock: 0x%X\n",
> +		    hwinfo->supported_clocks);
> +
> +	snd_iprintf(buffer, "phys out: 0x%X\n", hwinfo->phys_out);
> +	snd_iprintf(buffer, "phys in: 0x%X\n", hwinfo->phys_in);
> +
> +	snd_iprintf(buffer, "phys in grps: 0x%X\n",
> +		    hwinfo->phys_in_grp_count);
> +	for (i = 0; i < hwinfo->phys_in_grp_count; i++) {
> +		snd_iprintf(buffer,
> +			    "phys in grp[0x%d]: type 0x%d, count 0x%d\n",
> +			    i, hwinfo->phys_out_grps[i].type,
> +			    hwinfo->phys_out_grps[i].count);
> +	}
> +
> +	snd_iprintf(buffer, "phys out grps: 0x%X\n",
> +		    hwinfo->phys_out_grp_count);
> +	for (i = 0; i < hwinfo->phys_out_grp_count; i++) {
> +		snd_iprintf(buffer,
> +			    "phys out grps[0x%d]: type 0x%d, count 0x%d\n",
> +			    i, hwinfo->phys_out_grps[i].type,
> +			    hwinfo->phys_out_grps[i].count);
> +	}
> +
> +	snd_iprintf(buffer, "amdtp rx pcm channels 1x: 0x%X\n",
> +		    hwinfo->amdtp_rx_pcm_channels);
> +	snd_iprintf(buffer, "amdtp tx pcm channels 1x: 0x%X\n",
> +		    hwinfo->amdtp_tx_pcm_channels);
> +	snd_iprintf(buffer, "amdtp rx pcm channels 2x: 0x%X\n",
> +		    hwinfo->amdtp_rx_pcm_channels_2x);
> +	snd_iprintf(buffer, "amdtp tx pcm channels 2x: 0x%X\n",
> +		    hwinfo->amdtp_tx_pcm_channels_2x);
> +	snd_iprintf(buffer, "amdtp rx pcm channels 4x: 0x%X\n",
> +		    hwinfo->amdtp_rx_pcm_channels_4x);
> +	snd_iprintf(buffer, "amdtp tx pcm channels 4x: 0x%X\n",
> +		    hwinfo->amdtp_tx_pcm_channels_4x);
> +
> +	snd_iprintf(buffer, "midi out ports: 0x%X\n", hwinfo->midi_out_ports);
> +	snd_iprintf(buffer, "midi in ports: 0x%X\n", hwinfo->midi_in_ports);
> +
> +	snd_iprintf(buffer, "mixer playback channels: 0x%X\n",
> +		    hwinfo->mixer_playback_channels);
> +	snd_iprintf(buffer, "mixer capture channels: 0x%X\n",
> +		    hwinfo->mixer_capture_channels);
> +end:
> +	kfree(hwinfo);
> +}
> +
> +static void
> +proc_read_clock(struct snd_info_entry *entry, struct snd_info_buffer *buffer)
> +{
> +	struct snd_efw *efw = entry->private_data;
> +	enum snd_efw_clock_source clock_source;
> +	unsigned int sampling_rate;
> +
> +	if (snd_efw_command_get_clock_source(efw, &clock_source) < 0)
> +		return;
> +
> +	if (snd_efw_command_get_sampling_rate(efw, &sampling_rate) < 0)
> +		return;
> +
> +	snd_iprintf(buffer, "Clock Source: %d\n", clock_source);
> +	snd_iprintf(buffer, "Sampling Rate: %d\n", sampling_rate);
> +}
> +
> +/*
> + * NOTE:
> + *  dB = 20 * log10(linear / 0x01000000)
> + *  -144.0 dB when linear is 0
> + */
> +static void
> +proc_read_phys_meters(struct snd_info_entry *entry,
> +		      struct snd_info_buffer *buffer)
> +{
> +	struct snd_efw *efw = entry->private_data;
> +	struct snd_efw_phys_meters *meters;
> +	unsigned int g, c, m, max, size;
> +	const char *name;
> +	u32 *linear;
> +	int err;
> +
> +	size = sizeof(struct snd_efw_phys_meters) +
> +	       (efw->phys_in + efw->phys_out) * sizeof(u32);
> +	meters = kzalloc(size, GFP_KERNEL);
> +	if (meters == NULL)
> +		return;
> +
> +	err = snd_efw_command_get_phys_meters(efw, meters, size);
> +	if (err < 0)
> +		goto end;
> +
> +	snd_iprintf(buffer, "Physical Meters:\n");
> +
> +	m = 0;
> +	max = min(efw->phys_out, meters->out_meters);
> +	linear = meters->values;
> +	snd_iprintf(buffer, " %d Outputs:\n", max);
> +	for (g = 0; g < efw->phys_out_grp_count; g++) {
> +		name = get_phys_name(&efw->phys_out_grps[g]);
> +		for (c = 0; c < efw->phys_out_grps[g].count; c++) {
> +			if (m < max)
> +				snd_iprintf(buffer, "\t%s [%d]: %d\n",
> +					    name, c, linear[m++]);
> +		}
> +	}
> +
> +	m = 0;
> +	max = min(efw->phys_in, meters->in_meters);
> +	linear = meters->values + meters->out_meters;
> +	snd_iprintf(buffer, " %d Inputs:\n", max);
> +	for (g = 0; g < efw->phys_in_grp_count; g++) {
> +		name = get_phys_name(&efw->phys_in_grps[g]);
> +		for (c = 0; c < efw->phys_in_grps[g].count; c++)
> +			if (m < max)
> +				snd_iprintf(buffer, "\t%s [%d]: %d\n",
> +					    name, c, linear[m++]);
> +	}
> +end:
> +	kfree(meters);
> +}
> +
> +void snd_efw_proc_init(struct snd_efw *efw)
> +{
> +	struct snd_info_entry *entry;
> +
> +	if (!snd_card_proc_new(efw->card, "#firmware", &entry))
> +		snd_info_set_text_ops(entry, efw, proc_read_hwinfo);
> +	if (!snd_card_proc_new(efw->card, "#clock", &entry))
> +		snd_info_set_text_ops(entry, efw, proc_read_clock);
> +	if (!snd_card_proc_new(efw->card, "#meters", &entry))
> +		snd_info_set_text_ops(entry, efw, proc_read_phys_meters);

Why '#' is needed for each file name?


Takashi

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 26/39] fireworks: Add command/response functionality into hwdep interface
  2014-02-28  3:27 ` [PATCH 26/39] fireworks: Add command/response functionality into " Takashi Sakamoto
@ 2014-02-28  6:58   ` Takashi Iwai
  2014-03-01 13:18     ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Iwai @ 2014-02-28  6:58 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens, ffado-devel

At Fri, 28 Feb 2014 12:27:39 +0900,
Takashi Sakamoto wrote:
> 
> This commit adds two functionality for hwdep interface, adds two parameters for
> this driver, add a node for proc interface.
> 
> To receive responses from devices, this driver already allocate own callback
> into private address area in host controller. This means no one can allocate
> its own callback to the address. So this driver must give a way for user
> applications to receive responses.
> 
> This commit adds a functionality to receive responses via hwdep interface. The
> application can receive responses to read from this interface. To achieve this,
> this commit adds a buffer to queue responses. The default size of this buffer is
> 1024 bytes. This size can be changed to give preferrable size to
> 'resp_buf_size' parameter for this driver. The application should notice rest
> of space in this buffer because this driver don't push responses when this
> buffer has no space.
> 
> Additionaly, this commit adds a functionality to transmit commands via hwdep
> interface. The application can transmit commands to write into this interface.
> I note that the application can transmit one command at once, but can receive
> as many responses as possible untill the user-buffer is full.
> 
> When using these interfaces, the application must keep maximum number of
> sequence number in command within the number in firewire.h because this driver
> uses this number to distinguish the response is against the command by the
> application or this driver.
> 
> Usually responses against commands which the application transmits are pushed
> into this buffer. But to enable 'resp_buf_debug' parameter for this driver, all
> responses are pushed into the buffer. When using this mode, I reccomend to
> expand the size of buffer.
> 
> Finally this commit adds a new node into proc interface to output status of the
> buffer.
> 
> Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
> ---
>  include/uapi/sound/firewire.h                    |  18 +++
>  sound/firewire/fireworks/fireworks.c             |  17 +++
>  sound/firewire/fireworks/fireworks.h             |  22 +--
>  sound/firewire/fireworks/fireworks_command.c     |   6 +-
>  sound/firewire/fireworks/fireworks_hwdep.c       | 129 ++++++++++++++---
>  sound/firewire/fireworks/fireworks_proc.c        |  19 +++
>  sound/firewire/fireworks/fireworks_transaction.c | 176 ++++++++++++++++++++---
>  7 files changed, 340 insertions(+), 47 deletions(-)
> 
> diff --git a/include/uapi/sound/firewire.h b/include/uapi/sound/firewire.h
> index fc9afb2..7f4c419 100644
> --- a/include/uapi/sound/firewire.h
> +++ b/include/uapi/sound/firewire.h
> @@ -7,6 +7,7 @@
>  
>  #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS	0x000010cc
>  #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION	0xd1ce004e
> +#define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE	0x4e617475
>  
>  struct snd_firewire_event_common {
>  	unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
> @@ -22,10 +23,27 @@ struct snd_firewire_event_dice_notification {
>  	unsigned int notification; /* DICE-specific bits */
>  };
>  
> +#define SND_EFW_TRANSACTION_SEQNUM_MAX	((uint32_t)(BIT(28) - 1))
> +/* each field should be in big endian */
> +struct snd_efw_transaction {
> +	uint32_t length;
> +	uint32_t version;
> +	uint32_t seqnum;
> +	uint32_t category;
> +	uint32_t command;
> +	uint32_t status;
> +	uint32_t params[0];
> +};
> +struct snd_firewire_event_efw_response {
> +	unsigned int type;
> +	uint32_t response[0];	/* some responses */
> +};
> +
>  union snd_firewire_event {
>  	struct snd_firewire_event_common            common;
>  	struct snd_firewire_event_lock_status       lock_status;
>  	struct snd_firewire_event_dice_notification dice_notification;
> +	struct snd_firewire_event_efw_response      efw_response;
>  };
>  
>  
> diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c
> index 38986c0..7846285 100644
> --- a/sound/firewire/fireworks/fireworks.c
> +++ b/sound/firewire/fireworks/fireworks.c
> @@ -24,6 +24,8 @@ MODULE_LICENSE("GPL v2");
>  static int index[SNDRV_CARDS]	= SNDRV_DEFAULT_IDX;
>  static char *id[SNDRV_CARDS]	= SNDRV_DEFAULT_STR;
>  static bool enable[SNDRV_CARDS]	= SNDRV_DEFAULT_ENABLE_PNP;
> +unsigned int resp_buf_size	= 1024;
> +bool resp_buf_debug		= false;
>  
>  module_param_array(index, int, NULL, 0444);
>  MODULE_PARM_DESC(index, "card index");
> @@ -31,6 +33,10 @@ module_param_array(id, charp, NULL, 0444);
>  MODULE_PARM_DESC(id, "ID string");
>  module_param_array(enable, bool, NULL, 0444);
>  MODULE_PARM_DESC(enable, "enable Fireworks sound card");
> +module_param(resp_buf_size, uint, 0444);
> +MODULE_PARM_DESC(resp_buf_size, "response buffer size (default 1024)");
> +module_param(resp_buf_debug, bool, 0444);
> +MODULE_PARM_DESC(resp_buf_debug, "store all responses to buffer");
>  
>  static DEFINE_MUTEX(devices_mutex);
>  static unsigned int devices_used;
> @@ -165,6 +171,7 @@ efw_probe(struct fw_unit *unit,
>  {
>  	struct snd_card *card;
>  	struct snd_efw *efw;
> +	void *resp_buf;
>  	int card_index, err;
>  
>  	mutex_lock(&devices_mutex);
> @@ -178,6 +185,13 @@ efw_probe(struct fw_unit *unit,
>  		goto end;
>  	}
>  
> +	/* prepare response buffer */
> +	resp_buf = kzalloc(resp_buf_size, GFP_KERNEL);

Better to have a sanity check of resp_buf_size value.  You can't trust
a value given by a user.


Takashi

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 09/39] firewire-lib: Add sort function for transmitted packet
  2014-02-28  6:40   ` Takashi Iwai
@ 2014-02-28 14:31     ` Takashi Sakamoto
  0 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28 14:31 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, clemens, ffado-devel

Iwai-san,

Thanks for your reviewing.

(Feb 28 2014 15:40), Takashi Iwai wrote:
>> +#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?

Because I didn't know the macro... I confirm it in kernel.h. Thank you.

>> +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).

According to /lib/sort.c, it's for heap sort.

Currently I think I cannot write this algorism as heap sort because data 
consist of cyclic numbers, A simple example is:
[4, 5, 0, 2, 1, 3, 4, 0, 5, 1, 3, 2, 4, 0, 1, 5]
After sorting, this sequence of number should be:
[4, 5, 0, 1, 2, 3, 4, 5, 0, 1, 2, 3, 4, 5, 0, 1]

And we need to use stable sort algorism because there are successive 
elements with the same value.

If you want to see actual example, please see [PATCH 21/39]. A sequence 
of the last four bytes in 'CIP header 1' is data to be sort.

>> +	/* 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?

OK. I forgot it.


Thanks

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 19/39] fireworks: Add skelton for Fireworks based devices
  2014-02-28  6:51   ` Takashi Iwai
@ 2014-02-28 15:05     ` Takashi Sakamoto
  2014-02-28 15:10       ` Takashi Iwai
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28 15:05 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, clemens, ffado-devel

Iwai-san,

(Feb 28 2014 15:51), Takashi Iwai wrote:
> Now we may handle more than 32 cards, depending on Kconfig.

I can understand I should not use 'unsigned int' for bitmap of each card 
instances because in most architecture 'unsigned int' has just 32 bit width.

 > Use bitmap instead.

But I cannot understand this sentence.

I already use this 'devices_used' for bitmap. So I guess you did mean 
alternative ways like pointer array. For example:
static struct snd_fireworks *devices_used[SNDRV_CARDS];

Can I request your explaination for this?


Thanks

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 19/39] fireworks: Add skelton for Fireworks based devices
  2014-02-28 15:05     ` Takashi Sakamoto
@ 2014-02-28 15:10       ` Takashi Iwai
  2014-02-28 15:36         ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Iwai @ 2014-02-28 15:10 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens, ffado-devel

At Sat, 01 Mar 2014 00:05:42 +0900,
Takashi Sakamoto wrote:
> 
> Iwai-san,
> 
> (Feb 28 2014 15:51), Takashi Iwai wrote:
> > Now we may handle more than 32 cards, depending on Kconfig.
> 
> I can understand I should not use 'unsigned int' for bitmap of each card 
> instances because in most architecture 'unsigned int' has just 32 bit width.
> 
>  > Use bitmap instead.
> 
> But I cannot understand this sentence.
> 
> I already use this 'devices_used' for bitmap. So I guess you did mean 
> alternative ways like pointer array. For example:
> static struct snd_fireworks *devices_used[SNDRV_CARDS];
> 
> Can I request your explaination for this?

Just take a look at linux/bitmap.h :)


Takashi

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 19/39] fireworks: Add skelton for Fireworks based devices
  2014-02-28 15:10       ` Takashi Iwai
@ 2014-02-28 15:36         ` Takashi Sakamoto
  0 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-02-28 15:36 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, clemens, ffado-devel

Iwai-san,

(Mar 01 2014 00:10), Takashi Iwai wrote:
> Just take a look at linux/bitmap.h :)

OK. Now I get what you mean.

I seek some examples under /sound but there are no drivers which uses 
bitmap functions for this purpose. I guess we probably need further work 
for this, to prevent from buffer over run under CONFIG_SND_MAX_CARDS > 32.


Thanks

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-02-28  3:27 ` [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED Takashi Sakamoto
@ 2014-02-28 20:25   ` Stefan Richter
  2014-02-28 20:39     ` Stefan Richter
  2014-03-01  3:18     ` Takashi Sakamoto
  2014-03-01 10:34   ` Stefan Richter
  1 sibling, 2 replies; 71+ messages in thread
From: Stefan Richter @ 2014-02-28 20:25 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Feb 28 Takashi Sakamoto wrote:
> Linux Firewire Subsystem currently gives
> 200msec for default timeout (See DEFAULT_SPLIT_TIMEOUT in core-card.c).

It's 2000 ms actually.

And this is the IEEE 1394 split transaction timeout of course.  FCP
transactions on the other hand are transported by means of two IEEE 1394
transactions:  One in which the FCP requester is IEEE 1394 requester, and
another one in which the FCP responder is IEEE 1394 requester.

firewire-core's split transaction timeout is only relevant to the request
subaction of an FCP transaction (i.e. the first one of the two IEEE 1394
transaction), and only if this one is being performed as a split
transaction rather than a unified transaction, which depends on the FCP
responder hardware.  (I am not aware of any hardware which handles
FCP request subactions as unified transactions.)

IEC 61883-1 does not specify any FCP transaction timeout.

The 1394 TA document "AV/C Digital Interface Command Set, Rev. 1.0,
1996" defines FCP transactions (for use with AV/C command set) much more
precisely than IEC 61883-1 does:

  - To each FCP request, there may be 0..n FCP responses.

  - An AV/C target may emit a final response (one with response code <
    0xf) up to 100 ms after reception of an FCP request.

  - Or the AV/C target may emit an intermediate response (one with
    response code 0xf for interim response) up to 100 ms after reception
    of an FCP request.  The target shall emit no additional interim
    response in this transaction, but one final response.  There is no
    timeout specified for this final-after-interim response, i.e. it may
    take an undefined, possibly very long time.  (The AV/C
    specification v4.0, 2001 adds that subunit specifications may define
    such timeouts for certain commands.)

  - Or the AV/C target may still be busy processing a previous command.
    In this case, the specification accepts that the target does not send
    any response to subsequent requests at all until it is done with the
    previous work.  The requester may the retry such a failed request after
    the mentioned 100 ms timeout.  (Plus IEEE 1394 transport overhead, but
    the AV/C specification v1.0 does not mention this.  AV/C v4.0 explains
    that the requester should take certain transport dependent delays
    into account in addition to the 100 ms FCP timeout, e.g. physical layer
    repeater delays, or busy retry delays.  Furthermore, AV/C v4.0
    recommends that a busy target returns certain acknowledge codes or
    response codes to pipelined requests that exceed its capacity
    to handle concurrent FCP transactions.)

  - An "IEEE 1394.0 reset" (sic) terminates any outstanding FCP transaction.
    (I suppose a 1394 bus reset is meant by this.  AV/C v4.0 clarifies this
    to be an IEEE 1394 bus reset indeed.)

>From a quick glance, the FCP transaction specification looks unchanged
from AV/C 4.0, 2001 to v4.1, 2001 and to v4.2, 2004.
-- 
Stefan Richter
-=====-====- --=- ===--
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-02-28 20:25   ` Stefan Richter
@ 2014-02-28 20:39     ` Stefan Richter
  2014-03-01  3:18     ` Takashi Sakamoto
  1 sibling, 0 replies; 71+ messages in thread
From: Stefan Richter @ 2014-02-28 20:39 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Feb 28 Stefan Richter wrote:
>   - Or the AV/C target may still be busy processing a previous command.
>     In this case, the specification accepts that the target does not send
>     any response to subsequent requests at all until it is done with the
>     previous work.

To be more precise:  The specification accepts that the target *ignores*
incoming AV/C command frames while processing an AV/C command.  This is
indicated to the requester by lack of a response to any ignored request.

To me, this implies that the target must send a response to any request
that it did not ignore, i.e. to any command that it executed.  On the
other hand, I don't think there is any rollback behavior specified or
practical in cases when the target is unable to deliver a response to the
initiator.
-- 
Stefan Richter
-=====-====- --=- ===--
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-02-28 20:25   ` Stefan Richter
  2014-02-28 20:39     ` Stefan Richter
@ 2014-03-01  3:18     ` Takashi Sakamoto
  2014-03-01 10:10       ` Stefan Richter
  1 sibling, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-01  3:18 UTC (permalink / raw)
  To: Stefan Richter; +Cc: tiwai, alsa-devel, clemens, ffado-devel

Stefan,

At first, I introduce this is for a patch below. You can see actual logs:
[alsa-devel] [PATCH 39/39] bebob: Add support for M-Audio special 
Firewire series
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-February/073484.html

> It's 2000 ms actually.

Oops. Here I made a mistake to refer to unrelated value here. I might 
consider about the other thing when writing this commit, perhaps because 
of noon in Friday and a bit tired...

Well, yes. It's 2000 msec.

(Feb 28 2014 12:27), Takashi Sakamoto wrote:
 > As a result, in worst case that all of three retries are timeout
 > and devices don't send responses, fcp_avc_transaction() returns 1sec
 > later ((200 + 125) msec * 3 times).

So in this case, this function returns 6,375msec later. (but it's rare, 
again.)

> And this is the IEEE 1394 split transaction timeout of course.  FCP
> transactions on the other hand are transported by means of two IEEE 1394
> transactions:  One in which the FCP requester is IEEE 1394 requester, and
> another one in which the FCP responder is IEEE 1394 requester.
>
> firewire-core's split transaction timeout is only relevant to the request
> subaction of an FCP transaction (i.e. the first one of the two IEEE 1394
> transaction),

OK.

> and only if this one is being performed as a split
> transaction rather than a unified transaction, which depends on the FCP
> responder hardware.  (I am not aware of any hardware which handles
> FCP request subactions as unified transactions.)

As You explained in this message:
[alsa-devel] Echo Fireworks control protocol (was Re: Sample program for 
hwdep interface)
http://mailman.alsa-project.org/pipermail/alsa-devel/2014-February/071978.html

> IEC 61883-1 does not specify any FCP transaction timeout.

I've confirmed it.

> The 1394 TA document "AV/C Digital Interface Command Set, Rev. 1.0,
> 1996" defines FCP transactions (for use with AV/C command set) much more
> precisely than IEC 61883-1 does:
>
>    - To each FCP request, there may be 0..n FCP responses.
>
>    - An AV/C target may emit a final response (one with response code <
>      0xf) up to 100 ms after reception of an FCP request.
>
>    - Or the AV/C target may emit an intermediate response (one with
>      response code 0xf for interim response) up to 100 ms after reception
>      of an FCP request.  The target shall emit no additional interim
>      response in this transaction, but one final response.  There is no
>      timeout specified for this final-after-interim response, i.e. it may
>      take an undefined, possibly very long time.  (The AV/C
>      specification v4.0, 2001 adds that subunit specifications may define
>      such timeouts for certain commands.)
>
>    - Or the AV/C target may still be busy processing a previous command.
>      In this case, the specification accepts that the target does not send
>      any response to subsequent requests at all until it is done with the
>      previous work.  The requester may the retry such a failed request after
>      the mentioned 100 ms timeout.  (Plus IEEE 1394 transport overhead, but
>      the AV/C specification v1.0 does not mention this.  AV/C v4.0 explains
>      that the requester should take certain transport dependent delays
>      into account in addition to the 100 ms FCP timeout, e.g. physical layer
>      repeater delays, or busy retry delays.  Furthermore, AV/C v4.0
>      recommends that a busy target returns certain acknowledge codes or
>      response codes to pipelined requests that exceed its capacity
>      to handle concurrent FCP transactions.)
>
>    - An "IEEE 1394.0 reset" (sic) terminates any outstanding FCP transaction.
>      (I suppose a 1394 bus reset is meant by this.  AV/C v4.0 clarifies this
>      to be an IEEE 1394 bus reset indeed.)

Additionally, there are two types of AV/C transactions, 'immediate 
transaction' and 'deffered transaction'. Current 'firewire-lib' don't 
implement 'deffered transaction' so fcp_avc_transaction() always returns 
first AV/C response frame. This will be response=INTERIM against 
ctype=STATUS/NOTIFY.  The gap between a first response and a second 
response of 'deffered transaction' is defined as 'unspecified interval'.

But this is not related to an issue about which this patch mentions.

(Mar 01 2014 05:39), Stefan Richter wrote:
> To be more precise:  The specification accepts that the target*ignores*
> incoming AV/C command frames while processing an AV/C command.  This is
> indicated to the requester by lack of a response to any ignored request.
>
> To me, this implies that the target must send a response to any request
> that it did not ignore, i.e. to any command that it executed.  On the
> other hand, I don't think there is any rollback behavior specified or
> practical in cases when the target is unable to deliver a response to the
> initiator.
 >
>>From a quick glance, the FCP transaction specification looks unchanged
> from AV/C 4.0, 2001 to v4.1, 2001 and to v4.2, 2004.

I cannot find some sections about such ignorance in 'AV/C Digital 
Interface Command Set General Specification Version 4.2 (Sep 01, 2004, 
1394TA)' and 'IEC 61883-1 ed2'.

Can I request you any pointers about this ignorance?


Thanks

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-03-01  3:18     ` Takashi Sakamoto
@ 2014-03-01 10:10       ` Stefan Richter
  2014-03-01 12:22         ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Stefan Richter @ 2014-03-01 10:10 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Mar 01 Takashi Sakamoto wrote:
> Stefan,
> 
> At first, I introduce this is for a patch below. You can see actual logs:
> [alsa-devel] [PATCH 39/39] bebob: Add support for M-Audio special 
> Firewire series
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-February/073484.html

Right, I saw it (and your note that the iso resource management related
transactions still need a similar workaround).

> > It's 2000 ms actually.
> 
> Oops. Here I made a mistake to refer to unrelated value here. I might 
> consider about the other thing when writing this commit, perhaps because 
> of noon in Friday and a bit tired...
> 
> Well, yes. It's 2000 msec.
> 
> (Feb 28 2014 12:27), Takashi Sakamoto wrote:
>  > As a result, in worst case that all of three retries are timeout
>  > and devices don't send responses, fcp_avc_transaction() returns 1sec
>  > later ((200 + 125) msec * 3 times).
> 
> So in this case, this function returns 6,375msec later. (but it's rare, 
> again.)

This 125 ms part of the calculation made me wonder what the split
transaction timeout of FCP transactions is (I didn't remember), so I
looked it up and wrote down what I found.  So, all of the rest of my
reply was not really a direct comment to your patch but merely a loosely
related side note.

> > And this is the IEEE 1394 split transaction timeout of course.  FCP
> > transactions on the other hand are transported by means of two IEEE 1394
> > transactions:  One in which the FCP requester is IEEE 1394 requester, and
> > another one in which the FCP responder is IEEE 1394 requester.
> >
> > firewire-core's split transaction timeout is only relevant to the request
> > subaction of an FCP transaction (i.e. the first one of the two IEEE 1394
> > transaction),
> 
> OK.

(Per IEEE 1394 specification, it should in fact also be the upper bound of
our own 1394 response timing, e.g. when we respond to FCP response frame
reception.  But we haven't implemented such a time limit for outbound 1394
response packets yet.)

> > and only if this one is being performed as a split
> > transaction rather than a unified transaction, which depends on the FCP
> > responder hardware.  (I am not aware of any hardware which handles
> > FCP request subactions as unified transactions.)
> 
> As You explained in this message:
> [alsa-devel] Echo Fireworks control protocol (was Re: Sample program for 
> hwdep interface)
> http://mailman.alsa-project.org/pipermail/alsa-devel/2014-February/071978.html
> 
> > IEC 61883-1 does not specify any FCP transaction timeout.
> 
> I've confirmed it.
> 
> > The 1394 TA document "AV/C Digital Interface Command Set, Rev. 1.0,
> > 1996" defines FCP transactions (for use with AV/C command set) much more
> > precisely than IEC 61883-1 does:
> >
> >    - To each FCP request, there may be 0..n FCP responses.

Together with the rest of the spec, it's 0..2 FCP responses, precisely.

> >    - An AV/C target may emit a final response (one with response code <
> >      0xf) up to 100 ms after reception of an FCP request.
> >
> >    - Or the AV/C target may emit an intermediate response (one with
> >      response code 0xf for interim response) up to 100 ms after reception
> >      of an FCP request.  The target shall emit no additional interim
> >      response in this transaction, but one final response.  There is no
> >      timeout specified for this final-after-interim response, i.e. it may
> >      take an undefined, possibly very long time.  (The AV/C
> >      specification v4.0, 2001 adds that subunit specifications may define
> >      such timeouts for certain commands.)
> >
> >    - Or the AV/C target may still be busy processing a previous command.
> >      In this case, the specification accepts that the target does not send
> >      any response to subsequent requests at all until it is done with the
> >      previous work.  The requester may the retry such a failed request after
> >      the mentioned 100 ms timeout.  (Plus IEEE 1394 transport overhead, but
> >      the AV/C specification v1.0 does not mention this.  AV/C v4.0 explains
> >      that the requester should take certain transport dependent delays
> >      into account in addition to the 100 ms FCP timeout, e.g. physical layer
> >      repeater delays, or busy retry delays.  Furthermore, AV/C v4.0
> >      recommends that a busy target returns certain acknowledge codes or
> >      response codes to pipelined requests that exceed its capacity
> >      to handle concurrent FCP transactions.)
> >
> >    - An "IEEE 1394.0 reset" (sic) terminates any outstanding FCP transaction.
> >      (I suppose a 1394 bus reset is meant by this.  AV/C v4.0 clarifies this
> >      to be an IEEE 1394 bus reset indeed.)
> 
> Additionally, there are two types of AV/C transactions, 'immediate 
> transaction' and 'deffered transaction'.

These are the first two cases in my list above.  Put into other words, here
are all possible cases of 0, 1, or 2 responses to one and the same request:

Cases with 0 responses:
  a) The target ignored a command with reserved ctype value.
  b) The target ignored a command that it received before it finished
     processing a prior command.
  c) A bus reset happened before the target had a chance to send the first
     response.  The FCP transaction specification part of the AV/C spec
     does not say what kind of effects such an aborted transaction may
     have.  Perhaps some of the various command related definitions specify
     this, I haven't looked.
  d) The specification of a particular command defines that handling of
     this command shall (or may) not be followed up by a response.  I
     haven't looked whether such commands actually exist.
Cases with 1 response:
  e) The target sends a final response as first response.
  f) The target sent an interim response as first response, but a bus reset
     happened before it could send the final response.
Cases with 2 responses:
  g) The target sends an interim response, and then a final response.  This
     is only possible with CONTROL and NOTIFY commands.

> Current 'firewire-lib' don't 
> implement 'deffered transaction' so fcp_avc_transaction() always returns 
> first AV/C response frame. This will be response=INTERIM against 
> ctype=STATUS/NOTIFY.

Correction:  CONTROL or NOTIFY.  (In contrast, STATUS or SPECIFIC INQUIRY
or GENERAL INQUIRY commands can only be performed in immediate
transactions.  See AV/C v4.2 table 19.  Likewise, already AV/C v1.0 allows
INTERIM response only to CONTROL or NOTIFY commands.)

> The gap between a first response and a second 
> response of 'deffered transaction' is defined as 'unspecified interval'.

I wonder:  Now that firewire-lib's use cases are expanded far beyond LaCie
Speakers and Griffin FireWave, do we need to implement deferred
transactions in firewire-lib too?

As far as I can tell, FFADO simply ignores INTERIM responses, and treats
deferred transactions which are completed within 200...400 ms the same way
as if it was an immediate transaction.  (This is 200 ms from
IEEE1394SERVICE_FCP_RESPONSE_TIMEOUT_USEC and 400 ms from
IEEE1394SERVICE_FCP_POLL_TIMEOUT_MSEC in the FFADO code.)

(By the way, there is another FCP transaction layer implementation
existing in the kernel:  drivers/media/firewire/firedtv-avc.c.  This one
does in fact implement deferred transactions, but only for one particular
command, SFE_VENDOR_OPCODE_REGISTER_REMOTE_CONTROL.)

A related question:  Since FFADO applies 200 ms or more as FCP transaction
timeout, shouldn't firewire-lib's fcp.c increase FCP_TIMEOUT_MS from 125
to 200 or more as well?

> But this is not related to an issue about which this patch mentions.

Right.

> (Mar 01 2014 05:39), Stefan Richter wrote:
> > To be more precise:  The specification accepts that the target*ignores*
> > incoming AV/C command frames while processing an AV/C command.  This is
> > indicated to the requester by lack of a response to any ignored request.
> >
> > To me, this implies that the target must send a response to any request
> > that it did not ignore, i.e. to any command that it executed.  On the
> > other hand, I don't think there is any rollback behavior specified or
> > practical in cases when the target is unable to deliver a response to the
> > initiator.
>  >
> >>From a quick glance, the FCP transaction specification looks unchanged
> > from AV/C 4.0, 2001 to v4.1, 2001 and to v4.2, 2004.
> 
> I cannot find some sections about such ignorance in 'AV/C Digital 
> Interface Command Set General Specification Version 4.2 (Sep 01, 2004, 
> 1394TA)' and 'IEC 61883-1 ed2'.
> 
> Can I request you any pointers about this ignorance?

This is about case b) and, now that I think about it, also about case a)
in my list further above.

AV/C v1.0 says about case a)...

    If the AV/C command frame contains a reserved value in the ctype
    field, the target shall ignore the command and shall not generate a
    response frame.

...and about case b):

    If the target is already occupied with a previous command, it may
    ignore any AV/C command frames received. Note that the receipt of an
    AV/C command frame shall always be acknowledged by a target. The
    target ignores a command frame by a failure to return a response frame.
    [...]
    If the target is not occupied with a previous command, it shall create
    an AV/C response frame [...]

AV/C v4.2 says about case a)...

    If the AV/C command frame contains a reserved value in the ctype
    field, the target shall ignore the command and shall not generate a
    response frame.

...and about case b):

    Before sending a first response to a command, the number of AV/C
    commands that a target can process is implementation dependent.  If the
    target cannot process an additional command, it should return a 1394
    ack_busy, ack_conflict_error, or resp_conflict_error to the additional
    command.
    NOTE – Some targets that are compliant to the previous version of this
    specification may ignore the additional command in the above case.
-- 
Stefan Richter
-=====-====- --== ----=
http://arcgraph.de/sr/
_______________________________________________
Alsa-devel mailing list
Alsa-devel@alsa-project.org
http://mailman.alsa-project.org/mailman/listinfo/alsa-devel

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-02-28  3:27 ` [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED Takashi Sakamoto
  2014-02-28 20:25   ` Stefan Richter
@ 2014-03-01 10:34   ` Stefan Richter
  2014-03-01 12:23     ` Takashi Sakamoto
  1 sibling, 1 reply; 71+ messages in thread
From: Stefan Richter @ 2014-03-01 10:34 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Feb 28 Takashi Sakamoto wrote:
> --- a/sound/firewire/lib.c
> +++ b/sound/firewire/lib.c
> @@ -22,7 +22,7 @@
>   * @length: length of @buffer
>   * @flags: use %FW_FIXED_GENERATION and add the generation value to attempt the
>   *         request only in that generation; use %FW_QUIET to suppress error
> - *         messages
> + *         messages: use %FW_RETURN_TIMEOUT to avoid retry at RCODE_CANCELLED

For clarity, this should be a semicolon instead of a colon, i.e.

              messages; use %FW_RETURN_TIMEOUT to avoid retry at RCODE_CANCELLED
-- 
Stefan Richter
-=====-====- --== ----=
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 39/39] bebob: Add support for M-Audio special Firewire series
  2014-02-28  3:27 ` [PATCH 39/39] bebob: Add support for M-Audio special " Takashi Sakamoto
@ 2014-03-01 10:53   ` Stefan Richter
  2014-03-01 13:06     ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Stefan Richter @ 2014-03-01 10:53 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Feb 28 Takashi Sakamoto wrote:
> This commit allows this driver to support some models which M-Audio produces
> with DM1000 but its firmware is special. They are:
>  - Firewire 1814
>  - ProjectMix I/O
[...]
> M-Audio special firmware quirks:
[...]
>  - They often handle requests without responding. In this reason, this driver
>    often fail to handle them correctly.
[...]
> An fallback at timeout in the operations for CMP was already
> added by previous commit. But for isochronous resource management, no fallbacks
> are added. Further works are needed for Linux Firewire Subsystem.

We could add a workaround in drivers/firewire/core-iso.c which works
similar to your CMP related workaround in patch 18/39.  I.e., after a
timed-out lock request to CHANNELS_AVAILABLE or BANDWIDTH_AVAILABLE,
perform a read request to see what happened.  Like your CMP workaround,
this is fragile because it races with concurrent lock requests.

Or we could add a workaround in drivers/firewire/core-card.c which would
attempt to make the local node root node (and thereby IRM) if it detects
that an M-Audio device is currently IRM.

Not sure what's better.
-- 
Stefan Richter
-=====-====- --== ----=
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-03-01 10:10       ` Stefan Richter
@ 2014-03-01 12:22         ` Takashi Sakamoto
  2014-03-01 14:20           ` Stefan Richter
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-01 12:22 UTC (permalink / raw)
  To: Stefan Richter; +Cc: tiwai, alsa-devel, clemens, ffado-devel

Hi Stefan,

 > This 125 ms part of the calculation made me wonder what the split
 > transaction timeout of FCP transactions is (I didn't remember), so I
 > looked it up and wrote down what I found.  So, all of the rest of my
 > reply was not really a direct comment to your patch but merely a loosely
 > related side note.

I think it's not a proper action in this ML...

My intent of this comment is for a notice about time till returining 
from fcp_avc_transaction().
It might be much time against developer's expectation.


 > Correction:  CONTROL or NOTIFY.  (In contrast, STATUS or SPECIFIC INQUIRY
 > or GENERAL INQUIRY commands can only be performed in immediate
 > transactions.  See AV/C v4.2 table 19.  Likewise, already AV/C v1.0 
allows
 > INTERIM response only to CONTROL or NOTIFY commands.)

I confirm my mistake, thanks for your correction ;)


 > I wonder:  Now that firewire-lib's use cases are expanded far beyond 
LaCie
 > Speakers and Griffin FireWave, do we need to implement deferred
 > transactions in firewire-lib too?

Yes. I confirmed some devices use deffered transaction for AV/C CONTROL 
request.
But I still pending this issue, because of the way to handle the 
'unspecified interval'.

I plan to solve this issue for my future work.
This series of patch is the most I can do, now.


 > A related question:  Since FFADO applies 200 ms or more as FCP 
transaction
 > timeout, shouldn't firewire-lib's fcp.c increase FCP_TIMEOUT_MS from 125
 > to 200 or more as well?

For this developing, I've spent much time with my test devices.
But I've never experienced disadvantages under FCP_TIMEOUT_MS=125msec.
So feel no importance.

If you feel this importance, please post your patch with proper reasons.


 > This is about case b) and, now that I think about it, also about case a)
 > in my list further above.

The case a) is for ctype=reserved. Current ALSA Firewire drivers don't 
utilize it.
So I have never considered about this case.

For the case b), I'm optimistic because current fcp_avc_transaction() 
don't return
till the first AV/C response comes (or AV/C request is error). This 
means that
the driver basically don't send any AV/C transaction at the same time.

Furthermore, my drivers basically generate some AV/C transaction in driver's
probe/remove state or starting/stopping playback/capture of PCM samples/MIDI
messages. So I believe that FFADO/ALSA rarely transfer some AV/C 
transactions
at the same time.

In these reasons, I'm optimistic for device's ignorance of AV/C request.

Anyway, I appreciate your advices. Thank you to spend time for my 
developing.


Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-03-01 10:34   ` Stefan Richter
@ 2014-03-01 12:23     ` Takashi Sakamoto
  0 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-01 12:23 UTC (permalink / raw)
  To: Stefan Richter; +Cc: tiwai, alsa-devel, clemens, ffado-devel

(Mar 02 2014 19:34), Stefan Richter wrote:
>> - *         messages
>> + *         messages: use %FW_RETURN_TIMEOUT to avoid retry at RCODE_CANCELLED
>
> For clarity, this should be a semicolon instead of a colon, i.e.
>
>                messages; use %FW_RETURN_TIMEOUT to avoid retry at RCODE_CANCELLED

Exactly. Thank you.


Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 39/39] bebob: Add support for M-Audio special Firewire series
  2014-03-01 10:53   ` Stefan Richter
@ 2014-03-01 13:06     ` Takashi Sakamoto
  2014-03-01 14:32       ` Stefan Richter
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-01 13:06 UTC (permalink / raw)
  To: Stefan Richter; +Cc: tiwai, alsa-devel, clemens, ffado-devel

Stefan,

(Mar 01 2014 19:53), Stefan Richter wrote:
> We could add a workaround in drivers/firewire/core-iso.c which works
> similar to your CMP related workaround in patch 18/39.  I.e., after a
> timed-out lock request to CHANNELS_AVAILABLE or BANDWIDTH_AVAILABLE,
> perform a read request to see what happened.  Like your CMP workaround,
> this is fragile because it races with concurrent lock requests.
>
> Or we could add a workaround in drivers/firewire/core-card.c which would
> attempt to make the local node root node (and thereby IRM) if it detects
> that an M-Audio device is currently IRM.
>
> Not sure what's better.

Currently I'm also not sure. So I want it pending for my future work.

Actually, the failure of operation to CSR_CHANNELS_AVAILABLE_HI appears 
in stopping streams. When starting streams, retries in 
manage_channels()/manage_bandwidth() works fine. So users can use their 
devices as usual.

The disadvantages may appears when the driver handles this failure many 
times. Then users will see this message 'isochronous resources exhausted'.

I think the way to recover this state is just to reboot the system. But 
this state doesn't bring sudden hangup to system. Users can observer 
what happened.

With trade-off of the fact that there is no driver for these devices in 
both of user-land/kernel-land, I want to include these patches.


Regards

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices
  2014-02-28  6:36 ` [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Iwai
@ 2014-03-01 13:14   ` Takashi Sakamoto
  0 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-01 13:14 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, clemens, ffado-devel

Iwai-san,

(Feb 28 2014 15:36), Takashi Iwai wrote:
> Please don't commit on top of master branch of sound git tree.
> The master branch is a result of quick merges from all branches, thus
> inappropriate as a development basis.  Rebase to for-next branch
> instead.  The end-result patches must be identical, so you don't have
> to resend them to ML, though.

Now I rebase my master to for-next.
I'll rebase the 'request' branch at next pull-request.
Thanks for your indication.


Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 22/39] fireworks: Add proc interface for debugging purpose
  2014-02-28  6:54   ` Takashi Iwai
@ 2014-03-01 13:17     ` Takashi Sakamoto
  2014-03-03  9:01       ` Takashi Iwai
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-01 13:17 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, clemens, ffado-devel

Hi,

(Feb 28 2014 15:54), Takashi Iwai wrote:
> Why '#' is needed for each file name?

It's for my convinience to ask testers gathering output from procfs 
nodes. With this '#', I can just tell them '/proc/asound/cardX/#*' to 
get enough information.

Is it rude?


Thanks

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 26/39] fireworks: Add command/response functionality into hwdep interface
  2014-02-28  6:58   ` Takashi Iwai
@ 2014-03-01 13:18     ` Takashi Sakamoto
  0 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-01 13:18 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, clemens, ffado-devel

Iwai-san,

(Feb 28 2014 15:58), Takashi Iwai wrote:
> Better to have a sanity check of resp_buf_size value.  You can't trust
> a value given by a user.

Exactly. I add codes with clamp() to check it.


Thanks

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-03-01 12:22         ` Takashi Sakamoto
@ 2014-03-01 14:20           ` Stefan Richter
  2014-03-04  1:35             ` [FFADO-devel] " Jonathan Woithe
  0 siblings, 1 reply; 71+ messages in thread
From: Stefan Richter @ 2014-03-01 14:20 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Mar 01 Takashi Sakamoto wrote:
[...]
>> I wonder:  Now that firewire-lib's use cases are expanded far
>> beyond LaCie Speakers and Griffin FireWave, do we need to implement
>> deferred transactions in firewire-lib too?
> 
> Yes. I confirmed some devices use deffered transaction for AV/C 
> CONTROL request.
> But I still pending this issue, because of the way to handle the 
> 'unspecified interval'.

Are these devices already supported or reported to work with FFADO?
If yes, then the interval is probably typically less than FFADO's built-in
FCP transaction timeout, i.e. 200 (or 400?) ms.

> I plan to solve this issue for my future work.

OK.

>> A related question:  Since FFADO applies 200 ms or more as FCP
>> transaction timeout, shouldn't firewire-lib's fcp.c increase
>> FCP_TIMEOUT_MS from 125 to 200 or more as well?
> 
> For this developing, I've spent much time with my test devices.
> But I've never experienced disadvantages under FCP_TIMEOUT_MS=125msec.
> So feel no importance.
> 
> If you feel this importance, please post your patch with proper reasons.

This is mostly a question to the ffado-devel subscribers.  125 ms is of
course enough for devices which comply with the specification in this
regard.  The question is whether FFADO developers know of devices (or
suspect devices) which exceed the standard 100 ms and need more like 200
ms.

In any case, this question is unrelated to your patch (apart from the
unlikely worst-case timeout which you noted in the changelog).

>> This is about case b) and, now that I think about it, also about case
>> a) in my list further above.
> 
> The case a) is for ctype=reserved. Current ALSA Firewire drivers don't 
> utilize it.
> So I have never considered about this case.

Of course; the kernel is never going to emit a reserved ctype.  And
if malicious or faulty userspace does, the specified target behavior is
exactly the desired behavior.

> For the case b), I'm optimistic because current fcp_avc_transaction() 
> don't return till the first AV/C response comes (or AV/C request is
> error). This means that the driver basically don't send any AV/C
> transaction at the same time.
> 
> Furthermore, my drivers basically generate some AV/C transaction in
> driver's probe/remove state or starting/stopping playback/capture of
> PCM samples/MIDI messages. So I believe that FFADO/ALSA rarely transfer
> some AV/C transactions at the same time.
> 
> In these reasons, I'm optimistic for device's ignorance of AV/C request.

>From what I understood so far, I agree.  (And besides, the specified
behavior of ignoring the command and not sending a response is of course
better than, for example, execution of a command but failure to send
the response.)
-- 
Stefan Richter
-=====-====- --== ----=
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 39/39] bebob: Add support for M-Audio special Firewire series
  2014-03-01 13:06     ` Takashi Sakamoto
@ 2014-03-01 14:32       ` Stefan Richter
  0 siblings, 0 replies; 71+ messages in thread
From: Stefan Richter @ 2014-03-01 14:32 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, clemens, ffado-devel

On Mar 01 Takashi Sakamoto wrote:
[M-Audio devices with misbehaving IRM feature]
> Currently I'm also not sure. So I want it pending for my future work.
> 
> Actually, the failure of operation to CSR_CHANNELS_AVAILABLE_HI appears 
> in stopping streams. When starting streams, retries in 
> manage_channels()/manage_bandwidth() works fine. So users can use their 
> devices as usual.
> 
> The disadvantages may appears when the driver handles this failure many 
> times. Then users will see this message 'isochronous resources exhausted'.
> 
> I think the way to recover this state is just to reboot the system. But 
> this state doesn't bring sudden hangup to system. Users can observer 
> what happened.

Another way to recover would be a 1394 bus reset, for example initiated by
"ffadotest BusReset", or "firecontrol <<< 'br short'", or using the
gscanbus GUI.

> With trade-off of the fact that there is no driver for these devices in 
> both of user-land/kernel-land, I want to include these patches.

I agree.
-- 
Stefan Richter
-=====-====- --== ----=
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 22/39] fireworks: Add proc interface for debugging purpose
  2014-03-01 13:17     ` Takashi Sakamoto
@ 2014-03-03  9:01       ` Takashi Iwai
  2014-03-04 14:10         ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Iwai @ 2014-03-03  9:01 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: alsa-devel, clemens, ffado-devel

At Sat, 01 Mar 2014 22:17:58 +0900,
Takashi Sakamoto wrote:
> 
> Hi,
> 
> (Feb 28 2014 15:54), Takashi Iwai wrote:
> > Why '#' is needed for each file name?
> 
> It's for my convinience to ask testers gathering output from procfs 
> nodes. With this '#', I can just tell them '/proc/asound/cardX/#*' to 
> get enough information.
> 
> Is it rude?

You can use another prefix like "firewire-xxx", "firewire#xxx", etc,
which can be better understood.  Or, create a proc subdirectory
instead, and put all firewire-specific things there.


thanks,

Takashi

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [FFADO-devel] [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-03-01 14:20           ` Stefan Richter
@ 2014-03-04  1:35             ` Jonathan Woithe
  2014-03-04  8:33               ` Stefan Richter
  0 siblings, 1 reply; 71+ messages in thread
From: Jonathan Woithe @ 2014-03-04  1:35 UTC (permalink / raw)
  To: Stefan Richter; +Cc: tiwai, alsa-devel, jwoithe, ffado-devel, Takashi Sakamoto

On Sat, Mar 01, 2014 at 03:20:50PM +0100, Stefan Richter wrote:
> On Mar 01 Takashi Sakamoto wrote:
> >> A related question:  Since FFADO applies 200 ms or more as FCP
> >> transaction timeout, shouldn't firewire-lib's fcp.c increase
> >> FCP_TIMEOUT_MS from 125 to 200 or more as well?
> > 
> > For this developing, I've spent much time with my test devices.
> > But I've never experienced disadvantages under FCP_TIMEOUT_MS=125msec.
> > So feel no importance.
> > 
> > If you feel this importance, please post your patch with proper reasons.
> 
> This is mostly a question to the ffado-devel subscribers.  125 ms is of
> course enough for devices which comply with the specification in this
> regard.  The question is whether FFADO developers know of devices (or
> suspect devices) which exceed the standard 100 ms and need more like 200
> ms.

I've personally had nothing to do with devices utilising FCP transactions so
unfortunately I don't really know.  My feeling is that the authors of the
respective FFADO drivers would not have applied an FCP timeout of 200 ms if
there was no demonstrated need for it.  Therefore in the absence of other
evidence I would be assuming that there are devices which require the higher
timeout allowed for in FFADO's streaming code.  However, I don't know which
specific devices these might be.

Regards
  jonathan

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [FFADO-devel] [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-03-04  1:35             ` [FFADO-devel] " Jonathan Woithe
@ 2014-03-04  8:33               ` Stefan Richter
  2014-03-04  9:28                 ` Stefan Richter
  0 siblings, 1 reply; 71+ messages in thread
From: Stefan Richter @ 2014-03-04  8:33 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: tiwai, alsa-devel, ffado-devel, Takashi Sakamoto

On Mar 04 Jonathan Woithe wrote:
> On Sat, Mar 01, 2014 at 03:20:50PM +0100, Stefan Richter wrote:
> > >> Since FFADO applies 200 ms or more as FCP
> > >> transaction timeout, shouldn't firewire-lib's fcp.c increase
> > >> FCP_TIMEOUT_MS from 125 to 200 or more as well?
[...]
> I've personally had nothing to do with devices utilising FCP transactions so
> unfortunately I don't really know.  My feeling is that the authors of the
> respective FFADO drivers would not have applied an FCP timeout of 200 ms if
> there was no demonstrated need for it.  Therefore in the absence of other
> evidence I would be assuming that there are devices which require the higher
> timeout allowed for in FFADO's streaming code.  However, I don't know which
> specific devices these might be.

./libffado/config.h.in:
#define IEEE1394SERVICE_FCP_RESPONSE_TIMEOUT_USEC       200000

was added in this FFADO revision:
http://subversion.ffado.org/changeset/1371
Timestamp:
    10/23/08 09:00:47 (5 years ago)
Author:
    ppalmers
Message:
    * implement our own code to do FCP transactions. the code from libavc
      had too much side-effects.
    * remove libavc1394 as a dependency
    * set the SPLIT_TIMEOUT value for the host controller such that late
      responses by the DM1x00 based devices are not discarded. Should fix
      the issues with FA-101 discovery. (re: #155, #162)

I will look through the libavc1394 source history.
-- 
Stefan Richter
-=====-====- --== --=--
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [FFADO-devel] [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED
  2014-03-04  8:33               ` Stefan Richter
@ 2014-03-04  9:28                 ` Stefan Richter
  0 siblings, 0 replies; 71+ messages in thread
From: Stefan Richter @ 2014-03-04  9:28 UTC (permalink / raw)
  To: Jonathan Woithe; +Cc: tiwai, alsa-devel, ffado-devel, Takashi Sakamoto

I wrote:
>>>>> Since FFADO applies 200 ms or more as FCP
>>>>> transaction timeout, shouldn't firewire-lib's fcp.c increase
>>>>> FCP_TIMEOUT_MS from 125 to 200 or more as well?

FFADO took over the 200 ms timeout from libavc1394, where it was
introduced by this changeset:
http://sourceforge.net/p/libavc1394/code/24/

Commit [r24]
    increase timeout
Authored by: ddennedy 2002-10-01

--- a/trunk/libavc1394/libavc1394/avc1394_internal.h
+++ b/trunk/libavc1394/libavc1394/avc1394_internal.h
@@ -8,7 +8,7 @@
 #define MAX_RESPONSE_SIZE 512
 #define AVC1394_RETRY 2
 #define AVC1394_SLEEP 10000
-#define AVC1394_POLL_TIMEOUT 100
+#define AVC1394_POLL_TIMEOUT 200
 /* #define DEBUG */
 
 void htonl_block(quadlet_t *buf, int len);

So this was certainly not done particularly for audio devices.
And interestingly, the immediate parent commit r23 on 2002-09-28 corrected
the timeout from 50 ms to 100 ms.
-- 
Stefan Richter
-=====-====- --== --=--
http://arcgraph.de/sr/

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 22/39] fireworks: Add proc interface for debugging purpose
  2014-03-03  9:01       ` Takashi Iwai
@ 2014-03-04 14:10         ` Takashi Sakamoto
  0 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-04 14:10 UTC (permalink / raw)
  To: Takashi Iwai; +Cc: alsa-devel, clemens, ffado-devel

Iwai-san,

(Mar 3 2014 18:01), Takashi Iwai wrote:
> You can use another prefix like "firewire-xxx", "firewire#xxx", etc,
> which can be better understood.  Or, create a proc subdirectory
> instead, and put all firewire-specific things there.

OK. I decide to add subdirectory named "firewire" and push these nodes 
to it.


Thanks

Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 14/39] firewire-lib: Add handling output connection by CMP
  2014-03-09 21:27     ` Clemens Ladisch
@ 2014-03-10 12:30       ` Takashi Sakamoto
  0 siblings, 0 replies; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-10 12:30 UTC (permalink / raw)
  To: Clemens Ladisch; +Cc: alsa-devel, ffado-devel

(Mar 10 2014 06:27), Clemens Ladisch wrote:
> This function is always used with a constant master parameter, and the
> two branches don't really share much code.  Two functions would be simpler:
>
> static u64 mpr_address(struct cmp_connection *c)
> {
> 	if (c->direction == CMP_INPUT)
> 		return CSR_REGISTER_BASE + CSR_IMPR;
> 	else
> 		return CSR_REGISTER_BASE + CSR_OMPR;
> }
>
> static u64 pcr_address(struct cmp_connection *c)
> {
> 	if (c->direction == CMP_INPUT)
> 		return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
> 	else
> 		return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
> }

This is a good idea.


Takashi Sakamoto
o-takashi@sakamocchi.jp

^ permalink raw reply	[flat|nested] 71+ messages in thread

* Re: [PATCH 14/39] firewire-lib: Add handling output connection by CMP
  2014-03-05 10:48   ` [PATCH 14/39] firewire-lib: Add handling output connection by CMP Takashi Sakamoto
@ 2014-03-09 21:27     ` Clemens Ladisch
  2014-03-10 12:30       ` Takashi Sakamoto
  0 siblings, 1 reply; 71+ messages in thread
From: Clemens Ladisch @ 2014-03-09 21:27 UTC (permalink / raw)
  To: Takashi Sakamoto; +Cc: tiwai, alsa-devel, ffado-devel

Takashi Sakamoto wrote:
> This patch adds some macros, codes with condition of direction and new functions
> to handle output connection. Once cmp_connection_init() is executed with its
> direction, CMP input and output connection can be handled by the same way.

> +++ b/sound/firewire/cmp.c
> +static unsigned long long get_offset(struct cmp_connection *c, bool master)
> +{
> +	unsigned long long offset = CSR_REGISTER_BASE;
> +
> +	if (!master) {
> +		if (c->direction == CMP_INPUT)
> +			offset += CSR_IPCR(c->pcr_index);
> +		else
> +			offset += CSR_OPCR(c->pcr_index);
> +	} else {
> +		if (c->direction == CMP_INPUT)
> +			offset += CSR_IMPR;
> +		else
> +			offset += CSR_OMPR;
> +	}
> +
> +	return offset;
> +}

This function is always used with a constant master parameter, and the
two branches don't really share much code.  Two functions would be simpler:

static u64 mpr_address(struct cmp_connection *c)
{
	if (c->direction == CMP_INPUT)
		return CSR_REGISTER_BASE + CSR_IMPR;
	else
		return CSR_REGISTER_BASE + CSR_OMPR;
}

static u64 pcr_address(struct cmp_connection *c)
{
	if (c->direction == CMP_INPUT)
		return CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index);
	else
		return CSR_REGISTER_BASE + CSR_OPCR(c->pcr_index);
}


Regards,
Clemens

^ permalink raw reply	[flat|nested] 71+ messages in thread

* [PATCH 14/39] firewire-lib: Add handling output connection by CMP
  2014-03-05 10:47 ` [GIT PULL][PATCH 00/39 v2] " Takashi Sakamoto
@ 2014-03-05 10:48   ` Takashi Sakamoto
  2014-03-09 21:27     ` Clemens Ladisch
  0 siblings, 1 reply; 71+ messages in thread
From: Takashi Sakamoto @ 2014-03-05 10:48 UTC (permalink / raw)
  To: clemens, tiwai, perex; +Cc: alsa-devel, ffado-devel

This patch adds some macros, codes with condition of direction and new functions
to handle output connection. Once cmp_connection_init() is executed with its
direction, CMP input and output connection can be handled by the same way.
---
 sound/firewire/cmp.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 93 insertions(+), 9 deletions(-)

diff --git a/sound/firewire/cmp.c b/sound/firewire/cmp.c
index c7f7a31..60d2e17 100644
--- a/sound/firewire/cmp.c
+++ b/sound/firewire/cmp.c
@@ -29,6 +29,14 @@
 #define PCR_CHANNEL_MASK	0x003f0000
 #define PCR_CHANNEL_SHIFT	16
 
+/* oPCR specific fields */
+#define OPCR_XSPEED_MASK	0x00C00000
+#define OPCR_XSPEED_SHIFT	22
+#define OPCR_SPEED_MASK		0x0000C000
+#define OPCR_SPEED_SHIFT	14
+#define OPCR_OVERHEAD_ID_MASK	0x00003C00
+#define OPCR_OVERHEAD_ID_SHIFT	10
+
 enum bus_reset_handling {
 	ABORT_ON_BUS_RESET,
 	SUCCEED_ON_BUS_RESET,
@@ -41,10 +49,30 @@ void cmp_error(struct cmp_connection *c, const char *fmt, ...)
 
 	va_start(va, fmt);
 	dev_err(&c->resources.unit->device, "%cPCR%u: %pV",
-		'i', c->pcr_index, &(struct va_format){ fmt, &va });
+		(c->direction == CMP_INPUT) ? 'i' : 'o',
+		c->pcr_index, &(struct va_format){ fmt, &va });
 	va_end(va);
 }
 
+static unsigned long long get_offset(struct cmp_connection *c, bool master)
+{
+	unsigned long long offset = CSR_REGISTER_BASE;
+
+	if (!master) {
+		if (c->direction == CMP_INPUT)
+			offset += CSR_IPCR(c->pcr_index);
+		else
+			offset += CSR_OPCR(c->pcr_index);
+	} else {
+		if (c->direction == CMP_INPUT)
+			offset += CSR_IMPR;
+		else
+			offset += CSR_OMPR;
+	}
+
+	return offset;
+}
+
 static int pcr_modify(struct cmp_connection *c,
 		      __be32 (*modify)(struct cmp_connection *c, __be32 old),
 		      int (*check)(struct cmp_connection *c, __be32 pcr),
@@ -60,8 +88,7 @@ static int pcr_modify(struct cmp_connection *c,
 
 		err = snd_fw_transaction(
 				c->resources.unit, TCODE_LOCK_COMPARE_SWAP,
-				CSR_REGISTER_BASE + CSR_IPCR(c->pcr_index),
-				buffer, 8,
+				get_offset(c, false), buffer, 8,
 				FW_FIXED_GENERATION | c->resources.generation);
 
 		if (err < 0) {
@@ -102,8 +129,7 @@ int cmp_connection_init(struct cmp_connection *c,
 	int err;
 
 	err = snd_fw_transaction(unit, TCODE_READ_QUADLET_REQUEST,
-				 CSR_REGISTER_BASE + CSR_IMPR,
-				 &mpr_be, 4, 0);
+				 get_offset(c, true), &mpr_be, 4, 0);
 	if (err < 0)
 		return err;
 	mpr = be32_to_cpu(mpr_be);
@@ -122,6 +148,7 @@ int cmp_connection_init(struct cmp_connection *c,
 	c->max_speed = (mpr & MPR_SPEED_MASK) >> MPR_SPEED_SHIFT;
 	if (c->max_speed == SCODE_BETA)
 		c->max_speed += (mpr & MPR_XSPEED_MASK) >> MPR_XSPEED_SHIFT;
+	c->direction = direction;
 
 	return 0;
 }
@@ -151,6 +178,53 @@ static __be32 ipcr_set_modify(struct cmp_connection *c, __be32 ipcr)
 	return ipcr;
 }
 
+static int get_overhead_id(struct cmp_connection *c)
+{
+	int id;
+
+	/*
+	 * apply "oPCR overhead ID encoding"
+	 * the encoding table can convert up to 512.
+	 * here the value over 512 is converted as the same way as 512.
+	 */
+	for (id = 1; id < 16; id++) {
+		if (c->resources.bandwidth_overhead < (id << 5))
+			break;
+	}
+	if (id == 16)
+		id = 0;
+
+	return id;
+}
+
+static __be32 opcr_set_modify(struct cmp_connection *c, __be32 opcr)
+{
+	unsigned int spd, xspd;
+
+	/* generate speed and extended speed field value */
+	if (c->speed > SCODE_400) {
+		spd  = SCODE_800;
+		xspd = c->speed - SCODE_800;
+	} else {
+		spd = c->speed;
+		xspd = 0;
+	}
+
+	opcr &= ~cpu_to_be32(PCR_BCAST_CONN |
+			     PCR_P2P_CONN_MASK |
+			     OPCR_XSPEED_MASK |
+			     PCR_CHANNEL_MASK |
+			     OPCR_SPEED_MASK |
+			     OPCR_OVERHEAD_ID_MASK);
+	opcr |= cpu_to_be32(1 << PCR_P2P_CONN_SHIFT);
+	opcr |= cpu_to_be32(xspd << OPCR_XSPEED_SHIFT);
+	opcr |= cpu_to_be32(c->resources.channel << PCR_CHANNEL_SHIFT);
+	opcr |= cpu_to_be32(spd << OPCR_SPEED_SHIFT);
+	opcr |= cpu_to_be32(get_overhead_id(c) << OPCR_OVERHEAD_ID_SHIFT);
+
+	return opcr;
+}
+
 static int pcr_set_check(struct cmp_connection *c, __be32 pcr)
 {
 	if (pcr & cpu_to_be32(PCR_BCAST_CONN |
@@ -196,8 +270,13 @@ retry_after_bus_reset:
 	if (err < 0)
 		goto err_mutex;
 
-	err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
-			 ABORT_ON_BUS_RESET);
+	if (c->direction == CMP_OUTPUT)
+		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+				 ABORT_ON_BUS_RESET);
+	else
+		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+				 ABORT_ON_BUS_RESET);
+
 	if (err == -EAGAIN) {
 		fw_iso_resources_free(&c->resources);
 		goto retry_after_bus_reset;
@@ -245,8 +324,13 @@ int cmp_connection_update(struct cmp_connection *c)
 	if (err < 0)
 		goto err_unconnect;
 
-	err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
-			 SUCCEED_ON_BUS_RESET);
+	if (c->direction == CMP_OUTPUT)
+		err = pcr_modify(c, opcr_set_modify, pcr_set_check,
+				 SUCCEED_ON_BUS_RESET);
+	else
+		err = pcr_modify(c, ipcr_set_modify, pcr_set_check,
+				 SUCCEED_ON_BUS_RESET);
+
 	if (err < 0)
 		goto err_resources;
 
-- 
1.8.3.2

^ permalink raw reply	[flat|nested] 71+ messages in thread

end of thread, other threads:[~2014-03-10 12:30 UTC | newest]

Thread overview: 71+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-02-28  3:27 [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 01/39] firewire-lib: Rename functions, structure, member for AMDTP Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 02/39] firewire-lib: Add macros instead of fixed value " Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 03/39] firewire-lib: Add 'direction' member to 'amdtp_stream' structure Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 04/39] firewire-lib: Split some codes into functions to reuse for both streams Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 05/39] firewire-lib: Add support for AMDTP in-stream and PCM capture Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 06/39] firewire-lib: Add support for MIDI capture/playback Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 07/39] firewire-lib: Give syt value as parameter to handle_out_packet() Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 08/39] firewire-lib: Add support for duplex streams synchronization in blocking mode Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 09/39] firewire-lib: Add sort function for transmitted packet Takashi Sakamoto
2014-02-28  6:40   ` Takashi Iwai
2014-02-28 14:31     ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 10/39] firewire-lib: Add transfer delay to synchronized duplex streams Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 11/39] firewire-lib: Add support for channel mapping Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 12/39] firewire-lib: Rename macros, variables and functions for CMP Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 13/39] firewire-lib: Add 'direction' member to 'cmp_connection' structure Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 14/39] firewire-lib: Add handling output connection by CMP Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 15/39] firewire-lib: Add a new function to check others' connection Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 16/39] firewire-lib: Add some AV/C general commands Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 17/39] firewire-lib: Add quirks for Fireworks Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 18/39] firewire-lib: Add a fallback at RCODE_CANCELLED Takashi Sakamoto
2014-02-28 20:25   ` Stefan Richter
2014-02-28 20:39     ` Stefan Richter
2014-03-01  3:18     ` Takashi Sakamoto
2014-03-01 10:10       ` Stefan Richter
2014-03-01 12:22         ` Takashi Sakamoto
2014-03-01 14:20           ` Stefan Richter
2014-03-04  1:35             ` [FFADO-devel] " Jonathan Woithe
2014-03-04  8:33               ` Stefan Richter
2014-03-04  9:28                 ` Stefan Richter
2014-03-01 10:34   ` Stefan Richter
2014-03-01 12:23     ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 19/39] fireworks: Add skelton for Fireworks based devices Takashi Sakamoto
2014-02-28  6:51   ` Takashi Iwai
2014-02-28 15:05     ` Takashi Sakamoto
2014-02-28 15:10       ` Takashi Iwai
2014-02-28 15:36         ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 20/39] fireworks: Add transaction and some commands Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 21/39] fireworks: Add connection and stream management Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 22/39] fireworks: Add proc interface for debugging purpose Takashi Sakamoto
2014-02-28  6:54   ` Takashi Iwai
2014-03-01 13:17     ` Takashi Sakamoto
2014-03-03  9:01       ` Takashi Iwai
2014-03-04 14:10         ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 23/39] fireworks: Add MIDI interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 24/39] fireworks: Add PCM interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 25/39] fireworks: Add hwdep interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 26/39] fireworks: Add command/response functionality into " Takashi Sakamoto
2014-02-28  6:58   ` Takashi Iwai
2014-03-01 13:18     ` Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 27/39] bebob: Add skelton for BeBoB based devices Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 28/39] bebob: Add commands and connections/streams management Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 29/39] bebob: Add proc interface for debugging purpose Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 30/39] bebob: Add MIDI interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 31/39] bebob: Add PCM interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 32/39] bebob: Add hwdep interface Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 33/39] bebob: Prepare for device specific operations Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 34/39] bebob: Add support for Terratec PHASE, EWS series and Aureon Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 35/39] bebob: Add support for Yamaha GO series Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 36/39] bebob: Add support for Focusrite Saffire/SaffirePro series Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 37/39] bebob: Add support for M-Audio usual Firewire series Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 38/39] bebob: Send a cue to load firmware for M-Audio " Takashi Sakamoto
2014-02-28  3:27 ` [PATCH 39/39] bebob: Add support for M-Audio special " Takashi Sakamoto
2014-03-01 10:53   ` Stefan Richter
2014-03-01 13:06     ` Takashi Sakamoto
2014-03-01 14:32       ` Stefan Richter
2014-02-28  6:36 ` [GIT PULL][PATCH 00/39] Enhancement of support for Firewire devices Takashi Iwai
2014-03-01 13:14   ` Takashi Sakamoto
     [not found] <5316963F.1000206@sakamocchi.jp>
2014-03-05 10:47 ` [GIT PULL][PATCH 00/39 v2] " Takashi Sakamoto
2014-03-05 10:48   ` [PATCH 14/39] firewire-lib: Add handling output connection by CMP Takashi Sakamoto
2014-03-09 21:27     ` Clemens Ladisch
2014-03-10 12:30       ` Takashi Sakamoto

This is a public inbox, see mirroring instructions
on how to clone and mirror all data and code used for this inbox