ALSA: bebob: detect the number of available MIDI ports

Current implementation counts the number of input/output plugs for MIDI
type and uses the count as the number of physical MIDI ports. However,
the number of channels of the port represents the count.

This commit fixes the bug by additional vendor-specific AVC command
extension.

Signed-off-by: Takashi Sakamoto <o-takashi@sakamocchi.jp>
Link: https://lore.kernel.org/r/20210321032831.340278-3-o-takashi@sakamocchi.jp
Signed-off-by: Takashi Iwai <tiwai@suse.de>
This commit is contained in:
Takashi Sakamoto 2021-03-21 12:28:30 +09:00 committed by Takashi Iwai
parent caa2715106
commit 5c6ea94f2b
3 changed files with 89 additions and 32 deletions

View File

@ -200,6 +200,8 @@ int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
int avc_bridgeco_get_plug_type(struct fw_unit *unit, int avc_bridgeco_get_plug_type(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 addr[AVC_BRIDGECO_ADDR_BYTES],
enum avc_bridgeco_plug_type *type); enum avc_bridgeco_plug_type *type);
int avc_bridgeco_get_plug_ch_count(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
unsigned int *ch_count);
int avc_bridgeco_get_plug_section_type(struct fw_unit *unit, int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 addr[AVC_BRIDGECO_ADDR_BYTES],
unsigned int id, u8 *type); unsigned int id, u8 *type);

View File

@ -143,6 +143,42 @@ end:
return err; return err;
} }
int avc_bridgeco_get_plug_ch_count(struct fw_unit *unit, u8 addr[AVC_BRIDGECO_ADDR_BYTES],
unsigned int *ch_count)
{
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, 0x02);
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)
;
else if (err < 11)
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;
*ch_count = buf[10];
err = 0;
end:
kfree(buf);
return err;
}
int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit, int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 addr[AVC_BRIDGECO_ADDR_BYTES],
u8 *buf, unsigned int len) u8 *buf, unsigned int len)

View File

@ -844,6 +844,49 @@ static int fill_stream_formations(struct snd_bebob *bebob, u8 addr[AVC_BRIDGECO_
return err; return err;
} }
static int detect_midi_ports(struct snd_bebob *bebob,
const struct snd_bebob_stream_formation *formats,
u8 addr[AVC_BRIDGECO_ADDR_BYTES], enum avc_bridgeco_plug_dir plug_dir,
unsigned int plug_count, unsigned int *midi_ports)
{
int i;
int err = 0;
*midi_ports = 0;
/// Detect the number of available MIDI ports when packet has MIDI conformant data channel.
for (i = 0; i < SND_BEBOB_STRM_FMT_ENTRIES; ++i) {
if (formats[i].midi > 0)
break;
}
if (i >= SND_BEBOB_STRM_FMT_ENTRIES)
return 0;
for (i = 0; i < plug_count; ++i) {
enum avc_bridgeco_plug_type plug_type;
unsigned int ch_count;
avc_bridgeco_fill_unit_addr(addr, plug_dir, AVC_BRIDGECO_PLUG_UNIT_EXT, i);
err = avc_bridgeco_get_plug_type(bebob->unit, addr, &plug_type);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get type for external %d plug %d: %d\n",
plug_dir, i, err);
break;
} else if (plug_type != AVC_BRIDGECO_PLUG_TYPE_MIDI) {
continue;
}
err = avc_bridgeco_get_plug_ch_count(bebob->unit, addr, &ch_count);
if (err < 0)
break;
*midi_ports += ch_count;
}
return err;
}
static int static int
seek_msu_sync_input_plug(struct snd_bebob *bebob) seek_msu_sync_input_plug(struct snd_bebob *bebob)
{ {
@ -886,8 +929,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob)
{ {
const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock; const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES]; u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
enum avc_bridgeco_plug_type type;
unsigned int i;
int err; int err;
/* the number of plugs for isoc in/out, ext in/out */ /* the number of plugs for isoc in/out, ext in/out */
@ -918,37 +959,15 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob)
if (err < 0) if (err < 0)
goto end; goto end;
/* count external input plugs for MIDI */ err = detect_midi_ports(bebob, bebob->rx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_IN,
bebob->midi_input_ports = 0; plugs[2], &bebob->midi_input_ports);
for (i = 0; i < plugs[2]; i++) { if (err < 0)
avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_IN, goto end;
AVC_BRIDGECO_PLUG_UNIT_EXT, i);
err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get type for external in plug %d: %d\n",
i, err);
goto end;
} else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
bebob->midi_input_ports++;
}
}
/* count external output plugs for MIDI */ err = detect_midi_ports(bebob, bebob->tx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_OUT,
bebob->midi_output_ports = 0; plugs[3], &bebob->midi_output_ports);
for (i = 0; i < plugs[3]; i++) { if (err < 0)
avc_bridgeco_fill_unit_addr(addr, AVC_BRIDGECO_PLUG_DIR_OUT, goto end;
AVC_BRIDGECO_PLUG_UNIT_EXT, i);
err = avc_bridgeco_get_plug_type(bebob->unit, addr, &type);
if (err < 0) {
dev_err(&bebob->unit->device,
"fail to get type for external out plug %d: %d\n",
i, err);
goto end;
} else if (type == AVC_BRIDGECO_PLUG_TYPE_MIDI) {
bebob->midi_output_ports++;
}
}
/* for check source of clock later */ /* for check source of clock later */
if (!clk_spec) if (!clk_spec)