18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * bebob_command.c - driver for BeBoB based devices
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (c) 2013-2014 Takashi Sakamoto
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include "./bebob.h"
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ciint avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
118c2ecf20Sopenharmony_ci			   unsigned int fb_id, unsigned int num)
128c2ecf20Sopenharmony_ci{
138c2ecf20Sopenharmony_ci	u8 *buf;
148c2ecf20Sopenharmony_ci	int err;
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci	buf = kzalloc(12, GFP_KERNEL);
178c2ecf20Sopenharmony_ci	if (buf == NULL)
188c2ecf20Sopenharmony_ci		return -ENOMEM;
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_ci	buf[0]  = 0x00;		/* AV/C CONTROL */
218c2ecf20Sopenharmony_ci	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
228c2ecf20Sopenharmony_ci	buf[2]  = 0xb8;		/* FUNCTION BLOCK  */
238c2ecf20Sopenharmony_ci	buf[3]  = 0x80;		/* type is 'selector'*/
248c2ecf20Sopenharmony_ci	buf[4]  = 0xff & fb_id;	/* function block id */
258c2ecf20Sopenharmony_ci	buf[5]  = 0x10;		/* control attribute is CURRENT */
268c2ecf20Sopenharmony_ci	buf[6]  = 0x02;		/* selector length is 2 */
278c2ecf20Sopenharmony_ci	buf[7]  = 0xff & num;	/* input function block plug number */
288c2ecf20Sopenharmony_ci	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
318c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
328c2ecf20Sopenharmony_ci				  BIT(6) | BIT(7) | BIT(8));
338c2ecf20Sopenharmony_ci	if (err < 0)
348c2ecf20Sopenharmony_ci		;
358c2ecf20Sopenharmony_ci	else if (err < 9)
368c2ecf20Sopenharmony_ci		err = -EIO;
378c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
388c2ecf20Sopenharmony_ci		err = -ENOSYS;
398c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a) /* REJECTED */
408c2ecf20Sopenharmony_ci		err = -EINVAL;
418c2ecf20Sopenharmony_ci	else
428c2ecf20Sopenharmony_ci		err = 0;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	kfree(buf);
458c2ecf20Sopenharmony_ci	return err;
468c2ecf20Sopenharmony_ci}
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ciint avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
498c2ecf20Sopenharmony_ci			   unsigned int fb_id, unsigned int *num)
508c2ecf20Sopenharmony_ci{
518c2ecf20Sopenharmony_ci	u8 *buf;
528c2ecf20Sopenharmony_ci	int err;
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	buf = kzalloc(12, GFP_KERNEL);
558c2ecf20Sopenharmony_ci	if (buf == NULL)
568c2ecf20Sopenharmony_ci		return -ENOMEM;
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci	buf[0]  = 0x01;		/* AV/C STATUS */
598c2ecf20Sopenharmony_ci	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
608c2ecf20Sopenharmony_ci	buf[2]  = 0xb8;		/* FUNCTION BLOCK */
618c2ecf20Sopenharmony_ci	buf[3]  = 0x80;		/* type is 'selector'*/
628c2ecf20Sopenharmony_ci	buf[4]  = 0xff & fb_id;	/* function block id */
638c2ecf20Sopenharmony_ci	buf[5]  = 0x10;		/* control attribute is CURRENT */
648c2ecf20Sopenharmony_ci	buf[6]  = 0x02;		/* selector length is 2 */
658c2ecf20Sopenharmony_ci	buf[7]  = 0xff;		/* input function block plug number */
668c2ecf20Sopenharmony_ci	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
698c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
708c2ecf20Sopenharmony_ci				  BIT(6) | BIT(8));
718c2ecf20Sopenharmony_ci	if (err < 0)
728c2ecf20Sopenharmony_ci		;
738c2ecf20Sopenharmony_ci	else if (err < 9)
748c2ecf20Sopenharmony_ci		err = -EIO;
758c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
768c2ecf20Sopenharmony_ci		err = -ENOSYS;
778c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a) /* REJECTED */
788c2ecf20Sopenharmony_ci		err = -EINVAL;
798c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0b) /* IN TRANSITION */
808c2ecf20Sopenharmony_ci		err = -EAGAIN;
818c2ecf20Sopenharmony_ci	if (err < 0)
828c2ecf20Sopenharmony_ci		goto end;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	*num = buf[7];
858c2ecf20Sopenharmony_ci	err = 0;
868c2ecf20Sopenharmony_ciend:
878c2ecf20Sopenharmony_ci	kfree(buf);
888c2ecf20Sopenharmony_ci	return err;
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic inline void
928c2ecf20Sopenharmony_ciavc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
938c2ecf20Sopenharmony_ci{
948c2ecf20Sopenharmony_ci	buf[1] = addr[0];
958c2ecf20Sopenharmony_ci	memcpy(buf + 4, addr + 1, 5);
968c2ecf20Sopenharmony_ci}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_cistatic inline void
998c2ecf20Sopenharmony_ciavc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
1008c2ecf20Sopenharmony_ci					      unsigned int itype)
1018c2ecf20Sopenharmony_ci{
1028c2ecf20Sopenharmony_ci	buf[0] = 0x01;	/* AV/C STATUS */
1038c2ecf20Sopenharmony_ci	buf[2] = 0x02;	/* AV/C GENERAL PLUG INFO */
1048c2ecf20Sopenharmony_ci	buf[3] = 0xc0;	/* BridgeCo extension */
1058c2ecf20Sopenharmony_ci	avc_bridgeco_fill_extension_addr(buf, addr);
1068c2ecf20Sopenharmony_ci	buf[9] = itype;	/* info type */
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_ciint avc_bridgeco_get_plug_type(struct fw_unit *unit,
1108c2ecf20Sopenharmony_ci			       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
1118c2ecf20Sopenharmony_ci			       enum avc_bridgeco_plug_type *type)
1128c2ecf20Sopenharmony_ci{
1138c2ecf20Sopenharmony_ci	u8 *buf;
1148c2ecf20Sopenharmony_ci	int err;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	buf = kzalloc(12, GFP_KERNEL);
1178c2ecf20Sopenharmony_ci	if (buf == NULL)
1188c2ecf20Sopenharmony_ci		return -ENOMEM;
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	/* Info type is 'plug type'. */
1218c2ecf20Sopenharmony_ci	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
1248c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
1258c2ecf20Sopenharmony_ci				  BIT(6) | BIT(7) | BIT(9));
1268c2ecf20Sopenharmony_ci	if (err < 0)
1278c2ecf20Sopenharmony_ci		;
1288c2ecf20Sopenharmony_ci	else if (err < 11)
1298c2ecf20Sopenharmony_ci		err = -EIO;
1308c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
1318c2ecf20Sopenharmony_ci		err = -ENOSYS;
1328c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a) /* REJECTED */
1338c2ecf20Sopenharmony_ci		err = -EINVAL;
1348c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0b) /* IN TRANSITION */
1358c2ecf20Sopenharmony_ci		err = -EAGAIN;
1368c2ecf20Sopenharmony_ci	if (err < 0)
1378c2ecf20Sopenharmony_ci		goto end;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	*type = buf[10];
1408c2ecf20Sopenharmony_ci	err = 0;
1418c2ecf20Sopenharmony_ciend:
1428c2ecf20Sopenharmony_ci	kfree(buf);
1438c2ecf20Sopenharmony_ci	return err;
1448c2ecf20Sopenharmony_ci}
1458c2ecf20Sopenharmony_ci
1468c2ecf20Sopenharmony_ciint avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
1478c2ecf20Sopenharmony_ci				 u8 addr[AVC_BRIDGECO_ADDR_BYTES],
1488c2ecf20Sopenharmony_ci				 u8 *buf, unsigned int len)
1498c2ecf20Sopenharmony_ci{
1508c2ecf20Sopenharmony_ci	int err;
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	/* Info type is 'channel position'. */
1538c2ecf20Sopenharmony_ci	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(unit, buf, 12, buf, 256,
1568c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) |
1578c2ecf20Sopenharmony_ci				  BIT(5) | BIT(6) | BIT(7) | BIT(9));
1588c2ecf20Sopenharmony_ci	if (err < 0)
1598c2ecf20Sopenharmony_ci		;
1608c2ecf20Sopenharmony_ci	else if (err < 11)
1618c2ecf20Sopenharmony_ci		err = -EIO;
1628c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
1638c2ecf20Sopenharmony_ci		err = -ENOSYS;
1648c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a) /* REJECTED */
1658c2ecf20Sopenharmony_ci		err = -EINVAL;
1668c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0b) /* IN TRANSITION */
1678c2ecf20Sopenharmony_ci		err = -EAGAIN;
1688c2ecf20Sopenharmony_ci	if (err < 0)
1698c2ecf20Sopenharmony_ci		goto end;
1708c2ecf20Sopenharmony_ci
1718c2ecf20Sopenharmony_ci	/* Pick up specific data. */
1728c2ecf20Sopenharmony_ci	memmove(buf, buf + 10, err - 10);
1738c2ecf20Sopenharmony_ci	err = 0;
1748c2ecf20Sopenharmony_ciend:
1758c2ecf20Sopenharmony_ci	return err;
1768c2ecf20Sopenharmony_ci}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ciint avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
1798c2ecf20Sopenharmony_ci				       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
1808c2ecf20Sopenharmony_ci				       unsigned int id, u8 *type)
1818c2ecf20Sopenharmony_ci{
1828c2ecf20Sopenharmony_ci	u8 *buf;
1838c2ecf20Sopenharmony_ci	int err;
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci	/* section info includes charactors but this module don't need it */
1868c2ecf20Sopenharmony_ci	buf = kzalloc(12, GFP_KERNEL);
1878c2ecf20Sopenharmony_ci	if (buf == NULL)
1888c2ecf20Sopenharmony_ci		return -ENOMEM;
1898c2ecf20Sopenharmony_ci
1908c2ecf20Sopenharmony_ci	/* Info type is 'section info'. */
1918c2ecf20Sopenharmony_ci	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
1928c2ecf20Sopenharmony_ci	buf[10] = 0xff & ++id;	/* section id */
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
1958c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
1968c2ecf20Sopenharmony_ci				  BIT(6) | BIT(7) | BIT(9) | BIT(10));
1978c2ecf20Sopenharmony_ci	if (err < 0)
1988c2ecf20Sopenharmony_ci		;
1998c2ecf20Sopenharmony_ci	else if (err < 12)
2008c2ecf20Sopenharmony_ci		err = -EIO;
2018c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
2028c2ecf20Sopenharmony_ci		err = -ENOSYS;
2038c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a) /* REJECTED */
2048c2ecf20Sopenharmony_ci		err = -EINVAL;
2058c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0b) /* IN TRANSITION */
2068c2ecf20Sopenharmony_ci		err = -EAGAIN;
2078c2ecf20Sopenharmony_ci	if (err < 0)
2088c2ecf20Sopenharmony_ci		goto end;
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	*type = buf[11];
2118c2ecf20Sopenharmony_ci	err = 0;
2128c2ecf20Sopenharmony_ciend:
2138c2ecf20Sopenharmony_ci	kfree(buf);
2148c2ecf20Sopenharmony_ci	return err;
2158c2ecf20Sopenharmony_ci}
2168c2ecf20Sopenharmony_ci
2178c2ecf20Sopenharmony_ciint avc_bridgeco_get_plug_input(struct fw_unit *unit,
2188c2ecf20Sopenharmony_ci				u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
2198c2ecf20Sopenharmony_ci{
2208c2ecf20Sopenharmony_ci	int err;
2218c2ecf20Sopenharmony_ci	u8 *buf;
2228c2ecf20Sopenharmony_ci
2238c2ecf20Sopenharmony_ci	buf = kzalloc(18, GFP_KERNEL);
2248c2ecf20Sopenharmony_ci	if (buf == NULL)
2258c2ecf20Sopenharmony_ci		return -ENOMEM;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	/* Info type is 'plug input'. */
2288c2ecf20Sopenharmony_ci	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(unit, buf, 16, buf, 16,
2318c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
2328c2ecf20Sopenharmony_ci				  BIT(6) | BIT(7));
2338c2ecf20Sopenharmony_ci	if (err < 0)
2348c2ecf20Sopenharmony_ci		;
2358c2ecf20Sopenharmony_ci	else if (err < 16)
2368c2ecf20Sopenharmony_ci		err = -EIO;
2378c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
2388c2ecf20Sopenharmony_ci		err = -ENOSYS;
2398c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a) /* REJECTED */
2408c2ecf20Sopenharmony_ci		err = -EINVAL;
2418c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0b) /* IN TRANSITION */
2428c2ecf20Sopenharmony_ci		err = -EAGAIN;
2438c2ecf20Sopenharmony_ci	if (err < 0)
2448c2ecf20Sopenharmony_ci		goto end;
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci	memcpy(input, buf + 10, 5);
2478c2ecf20Sopenharmony_ci	err = 0;
2488c2ecf20Sopenharmony_ciend:
2498c2ecf20Sopenharmony_ci	kfree(buf);
2508c2ecf20Sopenharmony_ci	return err;
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ciint avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
2548c2ecf20Sopenharmony_ci				   u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
2558c2ecf20Sopenharmony_ci				   unsigned int *len, unsigned int eid)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	int err;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	/* check given buffer */
2608c2ecf20Sopenharmony_ci	if ((buf == NULL) || (*len < 12)) {
2618c2ecf20Sopenharmony_ci		err = -EINVAL;
2628c2ecf20Sopenharmony_ci		goto end;
2638c2ecf20Sopenharmony_ci	}
2648c2ecf20Sopenharmony_ci
2658c2ecf20Sopenharmony_ci	buf[0] = 0x01;	/* AV/C STATUS */
2668c2ecf20Sopenharmony_ci	buf[2] = 0x2f;	/* AV/C STREAM FORMAT SUPPORT */
2678c2ecf20Sopenharmony_ci	buf[3] = 0xc1;	/* Bridgeco extension - List Request */
2688c2ecf20Sopenharmony_ci	avc_bridgeco_fill_extension_addr(buf, addr);
2698c2ecf20Sopenharmony_ci	buf[10] = 0xff & eid;	/* Entry ID */
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	err = fcp_avc_transaction(unit, buf, 12, buf, *len,
2728c2ecf20Sopenharmony_ci				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
2738c2ecf20Sopenharmony_ci				  BIT(6) | BIT(7) | BIT(10));
2748c2ecf20Sopenharmony_ci	if (err < 0)
2758c2ecf20Sopenharmony_ci		;
2768c2ecf20Sopenharmony_ci	else if (err < 12)
2778c2ecf20Sopenharmony_ci		err = -EIO;
2788c2ecf20Sopenharmony_ci	else if (buf[0] == 0x08)        /* NOT IMPLEMENTED */
2798c2ecf20Sopenharmony_ci		err = -ENOSYS;
2808c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0a)        /* REJECTED */
2818c2ecf20Sopenharmony_ci		err = -EINVAL;
2828c2ecf20Sopenharmony_ci	else if (buf[0] == 0x0b)        /* IN TRANSITION */
2838c2ecf20Sopenharmony_ci		err = -EAGAIN;
2848c2ecf20Sopenharmony_ci	else if (buf[10] != eid)
2858c2ecf20Sopenharmony_ci		err = -EIO;
2868c2ecf20Sopenharmony_ci	if (err < 0)
2878c2ecf20Sopenharmony_ci		goto end;
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci	/* Pick up 'stream format info'. */
2908c2ecf20Sopenharmony_ci	memmove(buf, buf + 11, err - 11);
2918c2ecf20Sopenharmony_ci	*len = err - 11;
2928c2ecf20Sopenharmony_ci	err = 0;
2938c2ecf20Sopenharmony_ciend:
2948c2ecf20Sopenharmony_ci	return err;
2958c2ecf20Sopenharmony_ci}
296