Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eb7b3a05 authored by Takashi Sakamoto's avatar Takashi Sakamoto Committed by Takashi Iwai
Browse files

ALSA: bebob: Add commands and connections/streams management



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: default avatarTakashi Sakamoto <o-takashi@sakamocchi.jp>
Signed-off-by: default avatarTakashi Iwai <tiwai@suse.de>
parent fd6f4b0d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
snd-bebob-objs := bebob.o
snd-bebob-objs := bebob_command.o bebob_stream.o bebob.o
obj-m += snd-bebob.o
+14 −1
Original line number Diff line number Diff line
@@ -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);
@@ -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);
}
+114 −0
Original line number Diff line number Diff line
@@ -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/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;
@@ -35,6 +49,24 @@ struct snd_bebob {

	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
@@ -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 | \
+205 −0
Original line number Diff line number Diff line
/*
 * 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;
}
+874 −0

File added.

Preview size limit exceeded, changes collapsed.