1eb7b3a05STakashi Sakamoto /*
2eb7b3a05STakashi Sakamoto  * bebob_command.c - driver for BeBoB based devices
3eb7b3a05STakashi Sakamoto  *
4eb7b3a05STakashi Sakamoto  * Copyright (c) 2013-2014 Takashi Sakamoto
5eb7b3a05STakashi Sakamoto  *
6eb7b3a05STakashi Sakamoto  * Licensed under the terms of the GNU General Public License, version 2.
7eb7b3a05STakashi Sakamoto  */
8eb7b3a05STakashi Sakamoto 
9eb7b3a05STakashi Sakamoto #include "./bebob.h"
10eb7b3a05STakashi Sakamoto 
111fc9522aSTakashi Sakamoto int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
121fc9522aSTakashi Sakamoto 			   unsigned int fb_id, unsigned int num)
131fc9522aSTakashi Sakamoto {
141fc9522aSTakashi Sakamoto 	u8 *buf;
151fc9522aSTakashi Sakamoto 	int err;
161fc9522aSTakashi Sakamoto 
171fc9522aSTakashi Sakamoto 	buf = kzalloc(12, GFP_KERNEL);
181fc9522aSTakashi Sakamoto 	if (buf == NULL)
191fc9522aSTakashi Sakamoto 		return -ENOMEM;
201fc9522aSTakashi Sakamoto 
211fc9522aSTakashi Sakamoto 	buf[0]  = 0x00;		/* AV/C CONTROL */
221fc9522aSTakashi Sakamoto 	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
231fc9522aSTakashi Sakamoto 	buf[2]  = 0xb8;		/* FUNCTION BLOCK  */
241fc9522aSTakashi Sakamoto 	buf[3]  = 0x80;		/* type is 'selector'*/
251fc9522aSTakashi Sakamoto 	buf[4]  = 0xff & fb_id;	/* function block id */
261fc9522aSTakashi Sakamoto 	buf[5]  = 0x10;		/* control attribute is CURRENT */
271fc9522aSTakashi Sakamoto 	buf[6]  = 0x02;		/* selector length is 2 */
281fc9522aSTakashi Sakamoto 	buf[7]  = 0xff & num;	/* input function block plug number */
291fc9522aSTakashi Sakamoto 	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
301fc9522aSTakashi Sakamoto 
311fc9522aSTakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
321fc9522aSTakashi Sakamoto 				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
331fc9522aSTakashi Sakamoto 				  BIT(6) | BIT(7) | BIT(8));
341fc9522aSTakashi Sakamoto 	if (err > 0 && err < 9)
351fc9522aSTakashi Sakamoto 		err = -EIO;
361fc9522aSTakashi Sakamoto 	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
371fc9522aSTakashi Sakamoto 		err = -ENOSYS;
381fc9522aSTakashi Sakamoto 	else if (buf[0] == 0x0a) /* REJECTED */
391fc9522aSTakashi Sakamoto 		err = -EINVAL;
401fc9522aSTakashi Sakamoto 	else if (err > 0)
411fc9522aSTakashi Sakamoto 		err = 0;
421fc9522aSTakashi Sakamoto 
431fc9522aSTakashi Sakamoto 	kfree(buf);
441fc9522aSTakashi Sakamoto 	return err;
451fc9522aSTakashi Sakamoto }
461fc9522aSTakashi Sakamoto 
471fc9522aSTakashi Sakamoto int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
481fc9522aSTakashi Sakamoto 			   unsigned int fb_id, unsigned int *num)
491fc9522aSTakashi Sakamoto {
501fc9522aSTakashi Sakamoto 	u8 *buf;
511fc9522aSTakashi Sakamoto 	int err;
521fc9522aSTakashi Sakamoto 
531fc9522aSTakashi Sakamoto 	buf = kzalloc(12, GFP_KERNEL);
541fc9522aSTakashi Sakamoto 	if (buf == NULL)
551fc9522aSTakashi Sakamoto 		return -ENOMEM;
561fc9522aSTakashi Sakamoto 
571fc9522aSTakashi Sakamoto 	buf[0]  = 0x01;		/* AV/C STATUS */
581fc9522aSTakashi Sakamoto 	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
591fc9522aSTakashi Sakamoto 	buf[2]  = 0xb8;		/* FUNCTION BLOCK */
601fc9522aSTakashi Sakamoto 	buf[3]  = 0x80;		/* type is 'selector'*/
611fc9522aSTakashi Sakamoto 	buf[4]  = 0xff & fb_id;	/* function block id */
621fc9522aSTakashi Sakamoto 	buf[5]  = 0x10;		/* control attribute is CURRENT */
631fc9522aSTakashi Sakamoto 	buf[6]  = 0x02;		/* selector length is 2 */
641fc9522aSTakashi Sakamoto 	buf[7]  = 0xff;		/* input function block plug number */
651fc9522aSTakashi Sakamoto 	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
661fc9522aSTakashi Sakamoto 
671fc9522aSTakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
681fc9522aSTakashi Sakamoto 				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
691fc9522aSTakashi Sakamoto 				  BIT(6) | BIT(8));
701fc9522aSTakashi Sakamoto 	if (err > 0 && err < 9)
711fc9522aSTakashi Sakamoto 		err = -EIO;
721fc9522aSTakashi Sakamoto 	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
731fc9522aSTakashi Sakamoto 		err = -ENOSYS;
741fc9522aSTakashi Sakamoto 	else if (buf[0] == 0x0a) /* REJECTED */
751fc9522aSTakashi Sakamoto 		err = -EINVAL;
761fc9522aSTakashi Sakamoto 	else if (buf[0] == 0x0b) /* IN TRANSITION */
771fc9522aSTakashi Sakamoto 		err = -EAGAIN;
781fc9522aSTakashi Sakamoto 	if (err < 0)
791fc9522aSTakashi Sakamoto 		goto end;
801fc9522aSTakashi Sakamoto 
811fc9522aSTakashi Sakamoto 	*num = buf[7];
821fc9522aSTakashi Sakamoto 	err = 0;
831fc9522aSTakashi Sakamoto end:
841fc9522aSTakashi Sakamoto 	kfree(buf);
851fc9522aSTakashi Sakamoto 	return err;
861fc9522aSTakashi Sakamoto }
871fc9522aSTakashi Sakamoto 
88eb7b3a05STakashi Sakamoto static inline void
89eb7b3a05STakashi Sakamoto avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
90eb7b3a05STakashi Sakamoto {
91eb7b3a05STakashi Sakamoto 	buf[1] = addr[0];
92eb7b3a05STakashi Sakamoto 	memcpy(buf + 4, addr + 1, 5);
93eb7b3a05STakashi Sakamoto }
94eb7b3a05STakashi Sakamoto 
95eb7b3a05STakashi Sakamoto static inline void
96eb7b3a05STakashi Sakamoto avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
97eb7b3a05STakashi Sakamoto 					      unsigned int itype)
98eb7b3a05STakashi Sakamoto {
99eb7b3a05STakashi Sakamoto 	buf[0] = 0x01;	/* AV/C STATUS */
100eb7b3a05STakashi Sakamoto 	buf[2] = 0x02;	/* AV/C GENERAL PLUG INFO */
101eb7b3a05STakashi Sakamoto 	buf[3] = 0xc0;	/* BridgeCo extension */
102eb7b3a05STakashi Sakamoto 	avc_bridgeco_fill_extension_addr(buf, addr);
103eb7b3a05STakashi Sakamoto 	buf[9] = itype;	/* info type */
104eb7b3a05STakashi Sakamoto }
105eb7b3a05STakashi Sakamoto 
106eb7b3a05STakashi Sakamoto int avc_bridgeco_get_plug_type(struct fw_unit *unit,
107eb7b3a05STakashi Sakamoto 			       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
108eb7b3a05STakashi Sakamoto 			       enum avc_bridgeco_plug_type *type)
109eb7b3a05STakashi Sakamoto {
110eb7b3a05STakashi Sakamoto 	u8 *buf;
111eb7b3a05STakashi Sakamoto 	int err;
112eb7b3a05STakashi Sakamoto 
113eb7b3a05STakashi Sakamoto 	buf = kzalloc(12, GFP_KERNEL);
114eb7b3a05STakashi Sakamoto 	if (buf == NULL)
115eb7b3a05STakashi Sakamoto 		return -ENOMEM;
116eb7b3a05STakashi Sakamoto 
117eb7b3a05STakashi Sakamoto 	/* Info type is 'plug type'. */
118eb7b3a05STakashi Sakamoto 	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
119eb7b3a05STakashi Sakamoto 
120eb7b3a05STakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
121eb7b3a05STakashi Sakamoto 				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
122eb7b3a05STakashi Sakamoto 				  BIT(6) | BIT(7) | BIT(9));
123eb7b3a05STakashi Sakamoto 	if ((err >= 0) && (err < 8))
124eb7b3a05STakashi Sakamoto 		err = -EIO;
125eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
126eb7b3a05STakashi Sakamoto 		err = -ENOSYS;
127eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0a) /* REJECTED */
128eb7b3a05STakashi Sakamoto 		err = -EINVAL;
129eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0b) /* IN TRANSITION */
130eb7b3a05STakashi Sakamoto 		err = -EAGAIN;
131eb7b3a05STakashi Sakamoto 	if (err < 0)
132eb7b3a05STakashi Sakamoto 		goto end;
133eb7b3a05STakashi Sakamoto 
134eb7b3a05STakashi Sakamoto 	*type = buf[10];
135eb7b3a05STakashi Sakamoto 	err = 0;
136eb7b3a05STakashi Sakamoto end:
137eb7b3a05STakashi Sakamoto 	kfree(buf);
138eb7b3a05STakashi Sakamoto 	return err;
139eb7b3a05STakashi Sakamoto }
140eb7b3a05STakashi Sakamoto 
141eb7b3a05STakashi Sakamoto int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
142eb7b3a05STakashi Sakamoto 				 u8 addr[AVC_BRIDGECO_ADDR_BYTES],
143eb7b3a05STakashi Sakamoto 				 u8 *buf, unsigned int len)
144eb7b3a05STakashi Sakamoto {
145eb7b3a05STakashi Sakamoto 	int err;
146eb7b3a05STakashi Sakamoto 
147eb7b3a05STakashi Sakamoto 	/* Info type is 'channel position'. */
148eb7b3a05STakashi Sakamoto 	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
149eb7b3a05STakashi Sakamoto 
150eb7b3a05STakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 12, buf, 256,
151eb7b3a05STakashi Sakamoto 				  BIT(1) | BIT(2) | BIT(3) | BIT(4) |
152eb7b3a05STakashi Sakamoto 				  BIT(5) | BIT(6) | BIT(7) | BIT(9));
153eb7b3a05STakashi Sakamoto 	if ((err >= 0) && (err < 8))
154eb7b3a05STakashi Sakamoto 		err = -EIO;
155eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
156eb7b3a05STakashi Sakamoto 		err = -ENOSYS;
157eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0a) /* REJECTED */
158eb7b3a05STakashi Sakamoto 		err = -EINVAL;
159eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0b) /* IN TRANSITION */
160eb7b3a05STakashi Sakamoto 		err = -EAGAIN;
161eb7b3a05STakashi Sakamoto 	if (err < 0)
162eb7b3a05STakashi Sakamoto 		goto end;
163eb7b3a05STakashi Sakamoto 
164eb7b3a05STakashi Sakamoto 	/* Pick up specific data. */
165eb7b3a05STakashi Sakamoto 	memmove(buf, buf + 10, err - 10);
166eb7b3a05STakashi Sakamoto 	err = 0;
167eb7b3a05STakashi Sakamoto end:
168eb7b3a05STakashi Sakamoto 	return err;
169eb7b3a05STakashi Sakamoto }
170eb7b3a05STakashi Sakamoto 
171eb7b3a05STakashi Sakamoto int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
172eb7b3a05STakashi Sakamoto 				       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
173eb7b3a05STakashi Sakamoto 				       unsigned int id, u8 *type)
174eb7b3a05STakashi Sakamoto {
175eb7b3a05STakashi Sakamoto 	u8 *buf;
176eb7b3a05STakashi Sakamoto 	int err;
177eb7b3a05STakashi Sakamoto 
178eb7b3a05STakashi Sakamoto 	/* section info includes charactors but this module don't need it */
179eb7b3a05STakashi Sakamoto 	buf = kzalloc(12, GFP_KERNEL);
180eb7b3a05STakashi Sakamoto 	if (buf == NULL)
181eb7b3a05STakashi Sakamoto 		return -ENOMEM;
182eb7b3a05STakashi Sakamoto 
183eb7b3a05STakashi Sakamoto 	/* Info type is 'section info'. */
184eb7b3a05STakashi Sakamoto 	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
185eb7b3a05STakashi Sakamoto 	buf[10] = 0xff & ++id;	/* section id */
186eb7b3a05STakashi Sakamoto 
187eb7b3a05STakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
188eb7b3a05STakashi Sakamoto 				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
189eb7b3a05STakashi Sakamoto 				  BIT(6) | BIT(7) | BIT(9) | BIT(10));
190eb7b3a05STakashi Sakamoto 	if ((err >= 0) && (err < 8))
191eb7b3a05STakashi Sakamoto 		err = -EIO;
192eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
193eb7b3a05STakashi Sakamoto 		err = -ENOSYS;
194eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0a) /* REJECTED */
195eb7b3a05STakashi Sakamoto 		err = -EINVAL;
196eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0b) /* IN TRANSITION */
197eb7b3a05STakashi Sakamoto 		err = -EAGAIN;
198eb7b3a05STakashi Sakamoto 	if (err < 0)
199eb7b3a05STakashi Sakamoto 		goto end;
200eb7b3a05STakashi Sakamoto 
201eb7b3a05STakashi Sakamoto 	*type = buf[11];
202eb7b3a05STakashi Sakamoto 	err = 0;
203eb7b3a05STakashi Sakamoto end:
204eb7b3a05STakashi Sakamoto 	kfree(buf);
205eb7b3a05STakashi Sakamoto 	return err;
206eb7b3a05STakashi Sakamoto }
207eb7b3a05STakashi Sakamoto 
208eb7b3a05STakashi Sakamoto int avc_bridgeco_get_plug_input(struct fw_unit *unit,
209eb7b3a05STakashi Sakamoto 				u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
210eb7b3a05STakashi Sakamoto {
211eb7b3a05STakashi Sakamoto 	int err;
212eb7b3a05STakashi Sakamoto 	u8 *buf;
213eb7b3a05STakashi Sakamoto 
214eb7b3a05STakashi Sakamoto 	buf = kzalloc(18, GFP_KERNEL);
215eb7b3a05STakashi Sakamoto 	if (buf == NULL)
216eb7b3a05STakashi Sakamoto 		return -ENOMEM;
217eb7b3a05STakashi Sakamoto 
218eb7b3a05STakashi Sakamoto 	/* Info type is 'plug input'. */
219eb7b3a05STakashi Sakamoto 	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
220eb7b3a05STakashi Sakamoto 
221eb7b3a05STakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 16, buf, 16,
222eb7b3a05STakashi Sakamoto 				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
223eb7b3a05STakashi Sakamoto 				  BIT(6) | BIT(7));
224eb7b3a05STakashi Sakamoto 	if ((err >= 0) && (err < 8))
225eb7b3a05STakashi Sakamoto 		err = -EIO;
226eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
227eb7b3a05STakashi Sakamoto 		err = -ENOSYS;
228eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0a) /* REJECTED */
229eb7b3a05STakashi Sakamoto 		err = -EINVAL;
230eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0b) /* IN TRANSITION */
231eb7b3a05STakashi Sakamoto 		err = -EAGAIN;
232eb7b3a05STakashi Sakamoto 	if (err < 0)
233eb7b3a05STakashi Sakamoto 		goto end;
234eb7b3a05STakashi Sakamoto 
235eb7b3a05STakashi Sakamoto 	memcpy(input, buf + 10, 5);
236eb7b3a05STakashi Sakamoto 	err = 0;
237eb7b3a05STakashi Sakamoto end:
238eb7b3a05STakashi Sakamoto 	kfree(buf);
239eb7b3a05STakashi Sakamoto 	return err;
240eb7b3a05STakashi Sakamoto }
241eb7b3a05STakashi Sakamoto 
242eb7b3a05STakashi Sakamoto int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
243eb7b3a05STakashi Sakamoto 				   u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
244eb7b3a05STakashi Sakamoto 				   unsigned int *len, unsigned int eid)
245eb7b3a05STakashi Sakamoto {
246eb7b3a05STakashi Sakamoto 	int err;
247eb7b3a05STakashi Sakamoto 
248eb7b3a05STakashi Sakamoto 	/* check given buffer */
249eb7b3a05STakashi Sakamoto 	if ((buf == NULL) || (*len < 12)) {
250eb7b3a05STakashi Sakamoto 		err = -EINVAL;
251eb7b3a05STakashi Sakamoto 		goto end;
252eb7b3a05STakashi Sakamoto 	}
253eb7b3a05STakashi Sakamoto 
254eb7b3a05STakashi Sakamoto 	buf[0] = 0x01;	/* AV/C STATUS */
255eb7b3a05STakashi Sakamoto 	buf[2] = 0x2f;	/* AV/C STREAM FORMAT SUPPORT */
256eb7b3a05STakashi Sakamoto 	buf[3] = 0xc1;	/* Bridgeco extension - List Request */
257eb7b3a05STakashi Sakamoto 	avc_bridgeco_fill_extension_addr(buf, addr);
258eb7b3a05STakashi Sakamoto 	buf[10] = 0xff & eid;	/* Entry ID */
259eb7b3a05STakashi Sakamoto 
260eb7b3a05STakashi Sakamoto 	err = fcp_avc_transaction(unit, buf, 12, buf, *len,
261eb7b3a05STakashi Sakamoto 				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
262eb7b3a05STakashi Sakamoto 				  BIT(6) | BIT(7) | BIT(10));
263eb7b3a05STakashi Sakamoto 	if ((err >= 0) && (err < 12))
264eb7b3a05STakashi Sakamoto 		err = -EIO;
265eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x08)        /* NOT IMPLEMENTED */
266eb7b3a05STakashi Sakamoto 		err = -ENOSYS;
267eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0a)        /* REJECTED */
268eb7b3a05STakashi Sakamoto 		err = -EINVAL;
269eb7b3a05STakashi Sakamoto 	else if (buf[0] == 0x0b)        /* IN TRANSITION */
270eb7b3a05STakashi Sakamoto 		err = -EAGAIN;
271eb7b3a05STakashi Sakamoto 	else if (buf[10] != eid)
272eb7b3a05STakashi Sakamoto 		err = -EIO;
273eb7b3a05STakashi Sakamoto 	if (err < 0)
274eb7b3a05STakashi Sakamoto 		goto end;
275eb7b3a05STakashi Sakamoto 
276eb7b3a05STakashi Sakamoto 	/* Pick up 'stream format info'. */
277eb7b3a05STakashi Sakamoto 	memmove(buf, buf + 11, err - 11);
278eb7b3a05STakashi Sakamoto 	*len = err - 11;
279eb7b3a05STakashi Sakamoto 	err = 0;
280eb7b3a05STakashi Sakamoto end:
281eb7b3a05STakashi Sakamoto 	return err;
282eb7b3a05STakashi Sakamoto }
283