mirror of
https://github.com/torvalds/linux.git
synced 2024-11-10 22:21:40 +00:00
5c6ea94f2b
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>
332 lines
7.6 KiB
C
332 lines
7.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* bebob_command.c - driver for BeBoB based devices
|
|
*
|
|
* Copyright (c) 2013-2014 Takashi Sakamoto
|
|
*/
|
|
|
|
#include "./bebob.h"
|
|
|
|
int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
|
|
unsigned int fb_id, unsigned int num)
|
|
{
|
|
u8 *buf;
|
|
int err;
|
|
|
|
buf = kzalloc(12, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
|
|
buf[0] = 0x00; /* AV/C CONTROL */
|
|
buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
|
|
buf[2] = 0xb8; /* FUNCTION BLOCK */
|
|
buf[3] = 0x80; /* type is 'selector'*/
|
|
buf[4] = 0xff & fb_id; /* function block id */
|
|
buf[5] = 0x10; /* control attribute is CURRENT */
|
|
buf[6] = 0x02; /* selector length is 2 */
|
|
buf[7] = 0xff & num; /* input function block plug number */
|
|
buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
|
|
|
|
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
|
|
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
|
|
BIT(6) | BIT(7) | BIT(8));
|
|
if (err < 0)
|
|
;
|
|
else if (err < 9)
|
|
err = -EIO;
|
|
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
|
|
err = -ENOSYS;
|
|
else if (buf[0] == 0x0a) /* REJECTED */
|
|
err = -EINVAL;
|
|
else
|
|
err = 0;
|
|
|
|
kfree(buf);
|
|
return err;
|
|
}
|
|
|
|
int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
|
|
unsigned int fb_id, unsigned int *num)
|
|
{
|
|
u8 *buf;
|
|
int err;
|
|
|
|
buf = kzalloc(12, GFP_KERNEL);
|
|
if (buf == NULL)
|
|
return -ENOMEM;
|
|
|
|
buf[0] = 0x01; /* AV/C STATUS */
|
|
buf[1] = 0x08 | (0x07 & subunit_id); /* AUDIO SUBUNIT ID */
|
|
buf[2] = 0xb8; /* FUNCTION BLOCK */
|
|
buf[3] = 0x80; /* type is 'selector'*/
|
|
buf[4] = 0xff & fb_id; /* function block id */
|
|
buf[5] = 0x10; /* control attribute is CURRENT */
|
|
buf[6] = 0x02; /* selector length is 2 */
|
|
buf[7] = 0xff; /* input function block plug number */
|
|
buf[8] = 0x01; /* control selector is SELECTOR_CONTROL */
|
|
|
|
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
|
|
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
|
|
BIT(6) | BIT(8));
|
|
if (err < 0)
|
|
;
|
|
else if (err < 9)
|
|
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;
|
|
|
|
*num = buf[7];
|
|
err = 0;
|
|
end:
|
|
kfree(buf);
|
|
return err;
|
|
}
|
|
|
|
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)
|
|
;
|
|
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;
|
|
|
|
*type = buf[10];
|
|
err = 0;
|
|
end:
|
|
kfree(buf);
|
|
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)
|
|
{
|
|
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)
|
|
;
|
|
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;
|
|
|
|
/* 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)
|
|
;
|
|
else if (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;
|
|
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)
|
|
;
|
|
else if (err < 16)
|
|
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)
|
|
;
|
|
else if (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;
|
|
}
|