Skip to content

Commit

Permalink
ALSA: bebob: Add commands and connections/streams management
Browse files Browse the repository at this point in the history
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 some
BridgeCo's AV/C extension commands but this commit just uses 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 - List 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>
Signed-off-by: Takashi Iwai <tiwai@suse.de>
  • Loading branch information
Takashi Sakamoto authored and Takashi Iwai committed May 26, 2014
1 parent fd6f4b0 commit eb7b3a0
Show file tree
Hide file tree
Showing 5 changed files with 1,208 additions and 2 deletions.
2 changes: 1 addition & 1 deletion sound/firewire/bebob/Makefile
Original file line number Diff line number Diff line change
@@ -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
15 changes: 14 additions & 1 deletion sound/firewire/bebob/bebob.c
Original file line number Diff line number Diff line change
Expand Up @@ -158,9 +158,20 @@ bebob_probe(struct fw_unit *unit,
if (err < 0)
goto error;

err = snd_card_register(card);
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_bebob_stream_destroy_duplex(bebob);
goto error;
}

dev_set_drvdata(&unit->device, bebob);
end:
mutex_unlock(&devices_mutex);
Expand All @@ -176,11 +187,13 @@ 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);
}
Expand Down
114 changes: 114 additions & 0 deletions sound/firewire/bebob/bebob.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,18 +23,50 @@

#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/DM1100/DM1500 */
#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;
int card_index;

struct mutex mutex;
spinlock_t lock;

unsigned int midi_input_ports;
unsigned int midi_output_ports;

struct amdtp_stream *master;
struct amdtp_stream tx_stream;
struct amdtp_stream rx_stream;
struct cmp_connection out_conn;
struct cmp_connection in_conn;
atomic_t capture_substreams;
atomic_t 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
Expand All @@ -53,6 +85,88 @@ 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_msu_addr(u8 buf[AVC_BRIDGECO_ADDR_BYTES],
enum avc_bridgeco_plug_dir dir,
unsigned int pid)
{
buf[0] = 0x60; /* Music 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 addr[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 id, u8 *type);
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], u8 *buf,
unsigned int *len, unsigned int eid);

/* 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, int rate);
void 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 | \
Expand Down
205 changes: 205 additions & 0 deletions sound/firewire/bebob/bebob_command.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* bebob_command.c - driver for BeBoB based devices
*
* Copyright (c) 2013-2014 Takashi Sakamoto
*
* Licensed under the terms of the GNU General Public License, version 2.
*/

#include "./bebob.h"

static inline void
avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
{
buf[1] = addr[0];
memcpy(buf + 4, addr + 1, 5);
}

static inline void
avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
unsigned int itype)
{
buf[0] = 0x01; /* AV/C STATUS */
buf[2] = 0x02; /* AV/C GENERAL PLUG INFO */
buf[3] = 0xc0; /* BridgeCo extension */
avc_bridgeco_fill_extension_addr(buf, addr);
buf[9] = itype; /* info type */
}

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;

/* Info type is 'plug type'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);

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) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
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)
{
int err;

/* Info type is 'channel position'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);

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) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;

/* Pick up specific data. */
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 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;

/* Info type is 'section info'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
buf[10] = 0xff & ++id; /* section id */

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) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
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;

/* Info type is 'plug input'. */
avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);

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) && (err < 8))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
if (err < 0)
goto end;

memcpy(input, buf + 10, 5);
err = 0;
end:
kfree(buf);
return err;
}

int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
unsigned int *len, unsigned int eid)
{
int err;

/* check given buffer */
if ((buf == NULL) || (*len < 12)) {
err = -EINVAL;
goto end;
}

buf[0] = 0x01; /* AV/C STATUS */
buf[2] = 0x2f; /* AV/C STREAM FORMAT SUPPORT */
buf[3] = 0xc1; /* Bridgeco extension - List Request */
avc_bridgeco_fill_extension_addr(buf, addr);
buf[10] = 0xff & eid; /* Entry ID */

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) && (err < 12))
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
else if (buf[0] == 0x0b) /* IN TRANSITION */
err = -EAGAIN;
else if (buf[10] != eid)
err = -EIO;
if (err < 0)
goto end;

/* Pick up 'stream format info'. */
memmove(buf, buf + 11, err - 11);
*len = err - 11;
err = 0;
end:
return err;
}
Loading

0 comments on commit eb7b3a0

Please sign in to comment.