diff options
Diffstat (limited to 'sound/firewire/bebob/bebob_command.c')
| -rw-r--r-- | sound/firewire/bebob/bebob_command.c | 282 | 
1 files changed, 282 insertions, 0 deletions
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c new file mode 100644 index 00000000000..9402cc15dbc --- /dev/null +++ b/sound/firewire/bebob/bebob_command.c @@ -0,0 +1,282 @@ +/* + * 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" + +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 && err < 9) +		err = -EIO; +	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */ +		err = -ENOSYS; +	else if (buf[0] == 0x0a) /* REJECTED */ +		err = -EINVAL; +	else if (err > 0) +		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 && 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) && (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; +}  | 
