1/*
2 * bebob_command.c - driver for BeBoB based devices
3 *
4 * Copyright (c) 2013-2014 Takashi Sakamoto
5 *
6 * Licensed under the terms of the GNU General Public License, version 2.
7 */
8
9#include "./bebob.h"
10
11int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
12			   unsigned int fb_id, unsigned int num)
13{
14	u8 *buf;
15	int err;
16
17	buf = kzalloc(12, GFP_KERNEL);
18	if (buf == NULL)
19		return -ENOMEM;
20
21	buf[0]  = 0x00;		/* AV/C CONTROL */
22	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
23	buf[2]  = 0xb8;		/* FUNCTION BLOCK  */
24	buf[3]  = 0x80;		/* type is 'selector'*/
25	buf[4]  = 0xff & fb_id;	/* function block id */
26	buf[5]  = 0x10;		/* control attribute is CURRENT */
27	buf[6]  = 0x02;		/* selector length is 2 */
28	buf[7]  = 0xff & num;	/* input function block plug number */
29	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
30
31	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
32				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
33				  BIT(6) | BIT(7) | BIT(8));
34	if (err > 0 && err < 9)
35		err = -EIO;
36	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
37		err = -ENOSYS;
38	else if (buf[0] == 0x0a) /* REJECTED */
39		err = -EINVAL;
40	else if (err > 0)
41		err = 0;
42
43	kfree(buf);
44	return err;
45}
46
47int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
48			   unsigned int fb_id, unsigned int *num)
49{
50	u8 *buf;
51	int err;
52
53	buf = kzalloc(12, GFP_KERNEL);
54	if (buf == NULL)
55		return -ENOMEM;
56
57	buf[0]  = 0x01;		/* AV/C STATUS */
58	buf[1]  = 0x08 | (0x07 & subunit_id);	/* AUDIO SUBUNIT ID */
59	buf[2]  = 0xb8;		/* FUNCTION BLOCK */
60	buf[3]  = 0x80;		/* type is 'selector'*/
61	buf[4]  = 0xff & fb_id;	/* function block id */
62	buf[5]  = 0x10;		/* control attribute is CURRENT */
63	buf[6]  = 0x02;		/* selector length is 2 */
64	buf[7]  = 0xff;		/* input function block plug number */
65	buf[8]  = 0x01;		/* control selector is SELECTOR_CONTROL */
66
67	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
68				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
69				  BIT(6) | BIT(8));
70	if (err > 0 && err < 9)
71		err = -EIO;
72	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
73		err = -ENOSYS;
74	else if (buf[0] == 0x0a) /* REJECTED */
75		err = -EINVAL;
76	else if (buf[0] == 0x0b) /* IN TRANSITION */
77		err = -EAGAIN;
78	if (err < 0)
79		goto end;
80
81	*num = buf[7];
82	err = 0;
83end:
84	kfree(buf);
85	return err;
86}
87
88static inline void
89avc_bridgeco_fill_extension_addr(u8 *buf, u8 *addr)
90{
91	buf[1] = addr[0];
92	memcpy(buf + 4, addr + 1, 5);
93}
94
95static inline void
96avc_bridgeco_fill_plug_info_extension_command(u8 *buf, u8 *addr,
97					      unsigned int itype)
98{
99	buf[0] = 0x01;	/* AV/C STATUS */
100	buf[2] = 0x02;	/* AV/C GENERAL PLUG INFO */
101	buf[3] = 0xc0;	/* BridgeCo extension */
102	avc_bridgeco_fill_extension_addr(buf, addr);
103	buf[9] = itype;	/* info type */
104}
105
106int avc_bridgeco_get_plug_type(struct fw_unit *unit,
107			       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
108			       enum avc_bridgeco_plug_type *type)
109{
110	u8 *buf;
111	int err;
112
113	buf = kzalloc(12, GFP_KERNEL);
114	if (buf == NULL)
115		return -ENOMEM;
116
117	/* Info type is 'plug type'. */
118	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x00);
119
120	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
121				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
122				  BIT(6) | BIT(7) | BIT(9));
123	if ((err >= 0) && (err < 8))
124		err = -EIO;
125	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
126		err = -ENOSYS;
127	else if (buf[0] == 0x0a) /* REJECTED */
128		err = -EINVAL;
129	else if (buf[0] == 0x0b) /* IN TRANSITION */
130		err = -EAGAIN;
131	if (err < 0)
132		goto end;
133
134	*type = buf[10];
135	err = 0;
136end:
137	kfree(buf);
138	return err;
139}
140
141int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
142				 u8 addr[AVC_BRIDGECO_ADDR_BYTES],
143				 u8 *buf, unsigned int len)
144{
145	int err;
146
147	/* Info type is 'channel position'. */
148	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x03);
149
150	err = fcp_avc_transaction(unit, buf, 12, buf, 256,
151				  BIT(1) | BIT(2) | BIT(3) | BIT(4) |
152				  BIT(5) | BIT(6) | BIT(7) | BIT(9));
153	if ((err >= 0) && (err < 8))
154		err = -EIO;
155	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
156		err = -ENOSYS;
157	else if (buf[0] == 0x0a) /* REJECTED */
158		err = -EINVAL;
159	else if (buf[0] == 0x0b) /* IN TRANSITION */
160		err = -EAGAIN;
161	if (err < 0)
162		goto end;
163
164	/* Pick up specific data. */
165	memmove(buf, buf + 10, err - 10);
166	err = 0;
167end:
168	return err;
169}
170
171int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
172				       u8 addr[AVC_BRIDGECO_ADDR_BYTES],
173				       unsigned int id, u8 *type)
174{
175	u8 *buf;
176	int err;
177
178	/* section info includes charactors but this module don't need it */
179	buf = kzalloc(12, GFP_KERNEL);
180	if (buf == NULL)
181		return -ENOMEM;
182
183	/* Info type is 'section info'. */
184	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x07);
185	buf[10] = 0xff & ++id;	/* section id */
186
187	err = fcp_avc_transaction(unit, buf, 12, buf, 12,
188				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
189				  BIT(6) | BIT(7) | BIT(9) | BIT(10));
190	if ((err >= 0) && (err < 8))
191		err = -EIO;
192	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
193		err = -ENOSYS;
194	else if (buf[0] == 0x0a) /* REJECTED */
195		err = -EINVAL;
196	else if (buf[0] == 0x0b) /* IN TRANSITION */
197		err = -EAGAIN;
198	if (err < 0)
199		goto end;
200
201	*type = buf[11];
202	err = 0;
203end:
204	kfree(buf);
205	return err;
206}
207
208int avc_bridgeco_get_plug_input(struct fw_unit *unit,
209				u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 input[7])
210{
211	int err;
212	u8 *buf;
213
214	buf = kzalloc(18, GFP_KERNEL);
215	if (buf == NULL)
216		return -ENOMEM;
217
218	/* Info type is 'plug input'. */
219	avc_bridgeco_fill_plug_info_extension_command(buf, addr, 0x05);
220
221	err = fcp_avc_transaction(unit, buf, 16, buf, 16,
222				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
223				  BIT(6) | BIT(7));
224	if ((err >= 0) && (err < 8))
225		err = -EIO;
226	else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
227		err = -ENOSYS;
228	else if (buf[0] == 0x0a) /* REJECTED */
229		err = -EINVAL;
230	else if (buf[0] == 0x0b) /* IN TRANSITION */
231		err = -EAGAIN;
232	if (err < 0)
233		goto end;
234
235	memcpy(input, buf + 10, 5);
236	err = 0;
237end:
238	kfree(buf);
239	return err;
240}
241
242int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
243				   u8 addr[AVC_BRIDGECO_ADDR_BYTES], u8 *buf,
244				   unsigned int *len, unsigned int eid)
245{
246	int err;
247
248	/* check given buffer */
249	if ((buf == NULL) || (*len < 12)) {
250		err = -EINVAL;
251		goto end;
252	}
253
254	buf[0] = 0x01;	/* AV/C STATUS */
255	buf[2] = 0x2f;	/* AV/C STREAM FORMAT SUPPORT */
256	buf[3] = 0xc1;	/* Bridgeco extension - List Request */
257	avc_bridgeco_fill_extension_addr(buf, addr);
258	buf[10] = 0xff & eid;	/* Entry ID */
259
260	err = fcp_avc_transaction(unit, buf, 12, buf, *len,
261				  BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
262				  BIT(6) | BIT(7) | BIT(10));
263	if ((err >= 0) && (err < 12))
264		err = -EIO;
265	else if (buf[0] == 0x08)        /* NOT IMPLEMENTED */
266		err = -ENOSYS;
267	else if (buf[0] == 0x0a)        /* REJECTED */
268		err = -EINVAL;
269	else if (buf[0] == 0x0b)        /* IN TRANSITION */
270		err = -EAGAIN;
271	else if (buf[10] != eid)
272		err = -EIO;
273	if (err < 0)
274		goto end;
275
276	/* Pick up 'stream format info'. */
277	memmove(buf, buf + 11, err - 11);
278	*len = err - 11;
279	err = 0;
280end:
281	return err;
282}
283