From 5c6ea94f2b7c5ce4c012ac8c23fd35588f69c4c7 Mon Sep 17 00:00:00 2001 From: Takashi Sakamoto Date: Sun, 21 Mar 2021 12:28:30 +0900 Subject: [PATCH] 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 Link: https://lore.kernel.org/r/20210321032831.340278-3-o-takashi@sakamocchi.jp Signed-off-by: Takashi Iwai --- sound/firewire/bebob/bebob.h | 2 + sound/firewire/bebob/bebob_command.c | 36 ++++++++++++ sound/firewire/bebob/bebob_stream.c | 83 +++++++++++++++++----------- 3 files changed, 89 insertions(+), 32 deletions(-) diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h index d1ad9a8451bc..4e0ed84adbee 100644 --- a/sound/firewire/bebob/bebob.h +++ b/sound/firewire/bebob/bebob.h @@ -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, u8 addr[AVC_BRIDGECO_ADDR_BYTES], 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, u8 addr[AVC_BRIDGECO_ADDR_BYTES], unsigned int id, u8 *type); diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c index e276ab8f9006..022df09c68ff 100644 --- a/sound/firewire/bebob/bebob_command.c +++ b/sound/firewire/bebob/bebob_command.c @@ -143,6 +143,42 @@ end: 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, u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf, unsigned int len) diff --git a/sound/firewire/bebob/bebob_stream.c b/sound/firewire/bebob/bebob_stream.c index d96d2feb15a8..23579a73e038 100644 --- a/sound/firewire/bebob/bebob_stream.c +++ b/sound/firewire/bebob/bebob_stream.c @@ -844,6 +844,49 @@ static int fill_stream_formations(struct snd_bebob *bebob, u8 addr[AVC_BRIDGECO_ 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 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; u8 plugs[AVC_PLUG_INFO_BUF_BYTES], 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 */ @@ -918,37 +959,15 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob) 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) { - 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++; - } - } + err = detect_midi_ports(bebob, bebob->rx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_IN, + plugs[2], &bebob->midi_input_ports); + if (err < 0) + goto end; - /* 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) { - 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++; - } - } + err = detect_midi_ports(bebob, bebob->tx_stream_formations, addr, AVC_BRIDGECO_PLUG_DIR_OUT, + plugs[3], &bebob->midi_output_ports); + if (err < 0) + goto end; /* for check source of clock later */ if (!clk_spec)