[alsa-devel] [PATCH 16/17] firewire-lib: Add some AV/C general commands
Takashi Sakamoto
o-takashi at sakamocchi.jp
Sat Nov 23 07:08:03 CET 2013
This commit add three commands. I think many devices use these commands.
These commands are defined in 'AV/C Digital Interface Command Set General
Specification Version 4.2 (2004006, 1394TA)'.
1. INPUT PLUG SIGNAL FORMAT command
2. OUTPUT PLUG SIGNAL FORMAT command
3. PLUG INFO command
By the command 1 and 2, the drivers can get/set sampling frequency.
By the command 3, the drivers can get the number of each plugs.
Signed-off-by: Takashi Sakamoto <o-takashi at sakamocchi.jp>
---
sound/firewire/amdtp.c | 24 ++++----
sound/firewire/amdtp.h | 1 +
sound/firewire/fcp.c | 149 ++++++++++++++++++++++++++++++++++++++++++++++
sound/firewire/fcp.h | 16 +++++
sound/firewire/speakers.c | 44 +++-----------
5 files changed, 186 insertions(+), 48 deletions(-)
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
index 9c7aee4..cb74ade 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);
@@ -148,8 +150,8 @@ void amdtp_stream_set_parameters(struct amdtp_stream *s,
WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
return;
- for (sfc = 0; sfc < sizeof(rates); ++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 539c007..02ff8de 100644
--- a/sound/firewire/amdtp.h
+++ b/sound/firewire/amdtp.h
@@ -142,6 +142,7 @@ void amdtp_stream_pcm_abort(struct amdtp_stream *s);
bool amdtp_stream_wait_callback(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..ccdd0de 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,153 @@
#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 short bus_plugs[AVC_GENERAL_PLUG_DIR_COUNT],
+ unsigned short ext_plugs[AVC_GENERAL_PLUG_DIR_COUNT])
+{
+ u8 *buf;
+ int err;
+
+ buf = kzalloc(8, GFP_KERNEL);
+ if (buf == NULL)
+ return -ENOMEM;
+
+ buf[0] = 0x01; /* AV/C STATUS */
+ buf[1] = 0xff; /* UNIT */
+ buf[2] = 0x02; /* PLUG INFO */
+
+ 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;
+ }
+
+ bus_plugs[AVC_GENERAL_PLUG_DIR_IN] = buf[4];
+ bus_plugs[AVC_GENERAL_PLUG_DIR_OUT] = buf[5];
+ ext_plugs[AVC_GENERAL_PLUG_DIR_IN] = buf[6];
+ ext_plugs[AVC_GENERAL_PLUG_DIR_OUT] = 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..84791e0 100644
--- a/sound/firewire/fcp.h
+++ b/sound/firewire/fcp.h
@@ -3,6 +3,22 @@
struct fw_unit;
+/* AV/C Digital Interface Command Set General Specification 4.2 (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 short bus_plugs[AVC_GENERAL_PLUG_DIR_COUNT],
+ unsigned short ext_plugs[AVC_GENERAL_PLUG_DIR_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 d548376..5fb3e2c 100644
--- a/sound/firewire/speakers.c
+++ b/sound/firewire/speakers.c
@@ -194,42 +194,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)
{
@@ -253,9 +217,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
More information about the Alsa-devel
mailing list