]>
Commit | Line | Data |
---|---|---|
132fcb46 JB |
1 | /* |
2 | * f_uac2.c -- USB Audio Class 2.0 Function | |
3 | * | |
4 | * Copyright (C) 2011 | |
5 | * Yadwinder Singh (yadi.brar01@gmail.com) | |
6 | * Jaswinder Singh (jaswinder.singh@linaro.org) | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify | |
9 | * it under the terms of the GNU General Public License as published by | |
10 | * the Free Software Foundation; either version 2 of the License, or | |
11 | * (at your option) any later version. | |
12 | */ | |
13 | ||
14 | #include <linux/usb/audio.h> | |
15 | #include <linux/usb/audio-v2.h> | |
132fcb46 JB |
16 | #include <linux/module.h> |
17 | ||
eb9fecb9 | 18 | #include "u_audio.h" |
f8f93d24 AP |
19 | #include "u_uac2.h" |
20 | ||
132fcb46 JB |
21 | /* |
22 | * The driver implements a simple UAC_2 topology. | |
23 | * USB-OUT -> IT_1 -> OT_3 -> ALSA_Capture | |
24 | * ALSA_Playback -> IT_2 -> OT_4 -> USB-IN | |
25 | * Capture and Playback sampling rates are independently | |
26 | * controlled by two clock sources : | |
27 | * CLK_5 := c_srate, and CLK_6 := p_srate | |
28 | */ | |
29 | #define USB_OUT_IT_ID 1 | |
30 | #define IO_IN_IT_ID 2 | |
31 | #define IO_OUT_OT_ID 3 | |
32 | #define USB_IN_OT_ID 4 | |
33 | #define USB_OUT_CLK_ID 5 | |
34 | #define USB_IN_CLK_ID 6 | |
35 | ||
36 | #define CONTROL_ABSENT 0 | |
37 | #define CONTROL_RDONLY 1 | |
38 | #define CONTROL_RDWR 3 | |
39 | ||
40 | #define CLK_FREQ_CTRL 0 | |
41 | #define CLK_VLD_CTRL 2 | |
42 | ||
43 | #define COPY_CTRL 0 | |
44 | #define CONN_CTRL 2 | |
45 | #define OVRLD_CTRL 4 | |
46 | #define CLSTR_CTRL 6 | |
47 | #define UNFLW_CTRL 8 | |
48 | #define OVFLW_CTRL 10 | |
49 | ||
eb9fecb9 RB |
50 | struct f_uac2 { |
51 | struct g_audio g_audio; | |
52 | u8 ac_intf, as_in_intf, as_out_intf; | |
53 | u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */ | |
132fcb46 JB |
54 | }; |
55 | ||
eb9fecb9 | 56 | static inline struct f_uac2 *func_to_uac2(struct usb_function *f) |
132fcb46 | 57 | { |
eb9fecb9 | 58 | return container_of(f, struct f_uac2, g_audio.func); |
132fcb46 JB |
59 | } |
60 | ||
254b3bf6 | 61 | static inline |
eb9fecb9 | 62 | struct f_uac2_opts *g_audio_to_uac2_opts(struct g_audio *agdev) |
254b3bf6 DM |
63 | { |
64 | return container_of(agdev->func.fi, struct f_uac2_opts, func_inst); | |
65 | } | |
66 | ||
132fcb46 JB |
67 | /* --------- USB Function Interface ------------- */ |
68 | ||
69 | enum { | |
70 | STR_ASSOC, | |
71 | STR_IF_CTRL, | |
72 | STR_CLKSRC_IN, | |
73 | STR_CLKSRC_OUT, | |
74 | STR_USB_IT, | |
75 | STR_IO_IT, | |
76 | STR_USB_OT, | |
77 | STR_IO_OT, | |
78 | STR_AS_OUT_ALT0, | |
79 | STR_AS_OUT_ALT1, | |
80 | STR_AS_IN_ALT0, | |
81 | STR_AS_IN_ALT1, | |
82 | }; | |
83 | ||
132fcb46 JB |
84 | static char clksrc_in[8]; |
85 | static char clksrc_out[8]; | |
132fcb46 JB |
86 | |
87 | static struct usb_string strings_fn[] = { | |
b36c3479 SAS |
88 | [STR_ASSOC].s = "Source/Sink", |
89 | [STR_IF_CTRL].s = "Topology Control", | |
132fcb46 JB |
90 | [STR_CLKSRC_IN].s = clksrc_in, |
91 | [STR_CLKSRC_OUT].s = clksrc_out, | |
b36c3479 SAS |
92 | [STR_USB_IT].s = "USBH Out", |
93 | [STR_IO_IT].s = "USBD Out", | |
94 | [STR_USB_OT].s = "USBH In", | |
95 | [STR_IO_OT].s = "USBD In", | |
96 | [STR_AS_OUT_ALT0].s = "Playback Inactive", | |
97 | [STR_AS_OUT_ALT1].s = "Playback Active", | |
98 | [STR_AS_IN_ALT0].s = "Capture Inactive", | |
99 | [STR_AS_IN_ALT1].s = "Capture Active", | |
132fcb46 JB |
100 | { }, |
101 | }; | |
102 | ||
103 | static struct usb_gadget_strings str_fn = { | |
104 | .language = 0x0409, /* en-us */ | |
105 | .strings = strings_fn, | |
106 | }; | |
107 | ||
108 | static struct usb_gadget_strings *fn_strings[] = { | |
109 | &str_fn, | |
110 | NULL, | |
111 | }; | |
112 | ||
132fcb46 JB |
113 | static struct usb_interface_assoc_descriptor iad_desc = { |
114 | .bLength = sizeof iad_desc, | |
115 | .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, | |
116 | ||
117 | .bFirstInterface = 0, | |
118 | .bInterfaceCount = 3, | |
119 | .bFunctionClass = USB_CLASS_AUDIO, | |
120 | .bFunctionSubClass = UAC2_FUNCTION_SUBCLASS_UNDEFINED, | |
121 | .bFunctionProtocol = UAC_VERSION_2, | |
122 | }; | |
123 | ||
124 | /* Audio Control Interface */ | |
125 | static struct usb_interface_descriptor std_ac_if_desc = { | |
126 | .bLength = sizeof std_ac_if_desc, | |
127 | .bDescriptorType = USB_DT_INTERFACE, | |
128 | ||
129 | .bAlternateSetting = 0, | |
130 | .bNumEndpoints = 0, | |
131 | .bInterfaceClass = USB_CLASS_AUDIO, | |
132 | .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, | |
133 | .bInterfaceProtocol = UAC_VERSION_2, | |
134 | }; | |
135 | ||
136 | /* Clock source for IN traffic */ | |
ef16e7c8 | 137 | static struct uac_clock_source_descriptor in_clk_src_desc = { |
132fcb46 JB |
138 | .bLength = sizeof in_clk_src_desc, |
139 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
140 | ||
141 | .bDescriptorSubtype = UAC2_CLOCK_SOURCE, | |
142 | .bClockID = USB_IN_CLK_ID, | |
143 | .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, | |
144 | .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), | |
145 | .bAssocTerminal = 0, | |
146 | }; | |
147 | ||
148 | /* Clock source for OUT traffic */ | |
ef16e7c8 | 149 | static struct uac_clock_source_descriptor out_clk_src_desc = { |
132fcb46 JB |
150 | .bLength = sizeof out_clk_src_desc, |
151 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
152 | ||
153 | .bDescriptorSubtype = UAC2_CLOCK_SOURCE, | |
154 | .bClockID = USB_OUT_CLK_ID, | |
155 | .bmAttributes = UAC_CLOCK_SOURCE_TYPE_INT_FIXED, | |
156 | .bmControls = (CONTROL_RDONLY << CLK_FREQ_CTRL), | |
157 | .bAssocTerminal = 0, | |
158 | }; | |
159 | ||
160 | /* Input Terminal for USB_OUT */ | |
ef16e7c8 | 161 | static struct uac2_input_terminal_descriptor usb_out_it_desc = { |
132fcb46 JB |
162 | .bLength = sizeof usb_out_it_desc, |
163 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
164 | ||
165 | .bDescriptorSubtype = UAC_INPUT_TERMINAL, | |
166 | .bTerminalID = USB_OUT_IT_ID, | |
167 | .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), | |
168 | .bAssocTerminal = 0, | |
169 | .bCSourceID = USB_OUT_CLK_ID, | |
170 | .iChannelNames = 0, | |
14e1d56c | 171 | .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), |
132fcb46 JB |
172 | }; |
173 | ||
174 | /* Input Terminal for I/O-In */ | |
ef16e7c8 | 175 | static struct uac2_input_terminal_descriptor io_in_it_desc = { |
132fcb46 JB |
176 | .bLength = sizeof io_in_it_desc, |
177 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
178 | ||
179 | .bDescriptorSubtype = UAC_INPUT_TERMINAL, | |
180 | .bTerminalID = IO_IN_IT_ID, | |
181 | .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_UNDEFINED), | |
182 | .bAssocTerminal = 0, | |
183 | .bCSourceID = USB_IN_CLK_ID, | |
184 | .iChannelNames = 0, | |
14e1d56c | 185 | .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), |
132fcb46 JB |
186 | }; |
187 | ||
188 | /* Ouput Terminal for USB_IN */ | |
ef16e7c8 | 189 | static struct uac2_output_terminal_descriptor usb_in_ot_desc = { |
132fcb46 JB |
190 | .bLength = sizeof usb_in_ot_desc, |
191 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
192 | ||
193 | .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, | |
194 | .bTerminalID = USB_IN_OT_ID, | |
195 | .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), | |
196 | .bAssocTerminal = 0, | |
197 | .bSourceID = IO_IN_IT_ID, | |
198 | .bCSourceID = USB_IN_CLK_ID, | |
14e1d56c | 199 | .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), |
132fcb46 JB |
200 | }; |
201 | ||
202 | /* Ouput Terminal for I/O-Out */ | |
ef16e7c8 | 203 | static struct uac2_output_terminal_descriptor io_out_ot_desc = { |
132fcb46 JB |
204 | .bLength = sizeof io_out_ot_desc, |
205 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
206 | ||
207 | .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, | |
208 | .bTerminalID = IO_OUT_OT_ID, | |
209 | .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_UNDEFINED), | |
210 | .bAssocTerminal = 0, | |
211 | .bSourceID = USB_OUT_IT_ID, | |
212 | .bCSourceID = USB_OUT_CLK_ID, | |
14e1d56c | 213 | .bmControls = cpu_to_le16(CONTROL_RDWR << COPY_CTRL), |
132fcb46 JB |
214 | }; |
215 | ||
ef16e7c8 | 216 | static struct uac2_ac_header_descriptor ac_hdr_desc = { |
132fcb46 JB |
217 | .bLength = sizeof ac_hdr_desc, |
218 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
219 | ||
220 | .bDescriptorSubtype = UAC_MS_HEADER, | |
221 | .bcdADC = cpu_to_le16(0x200), | |
222 | .bCategory = UAC2_FUNCTION_IO_BOX, | |
14e1d56c RB |
223 | .wTotalLength = cpu_to_le16(sizeof in_clk_src_desc |
224 | + sizeof out_clk_src_desc + sizeof usb_out_it_desc | |
225 | + sizeof io_in_it_desc + sizeof usb_in_ot_desc | |
226 | + sizeof io_out_ot_desc), | |
132fcb46 JB |
227 | .bmControls = 0, |
228 | }; | |
229 | ||
230 | /* Audio Streaming OUT Interface - Alt0 */ | |
231 | static struct usb_interface_descriptor std_as_out_if0_desc = { | |
232 | .bLength = sizeof std_as_out_if0_desc, | |
233 | .bDescriptorType = USB_DT_INTERFACE, | |
234 | ||
235 | .bAlternateSetting = 0, | |
236 | .bNumEndpoints = 0, | |
237 | .bInterfaceClass = USB_CLASS_AUDIO, | |
238 | .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, | |
239 | .bInterfaceProtocol = UAC_VERSION_2, | |
240 | }; | |
241 | ||
242 | /* Audio Streaming OUT Interface - Alt1 */ | |
243 | static struct usb_interface_descriptor std_as_out_if1_desc = { | |
244 | .bLength = sizeof std_as_out_if1_desc, | |
245 | .bDescriptorType = USB_DT_INTERFACE, | |
246 | ||
247 | .bAlternateSetting = 1, | |
248 | .bNumEndpoints = 1, | |
249 | .bInterfaceClass = USB_CLASS_AUDIO, | |
250 | .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, | |
251 | .bInterfaceProtocol = UAC_VERSION_2, | |
252 | }; | |
253 | ||
254 | /* Audio Stream OUT Intface Desc */ | |
ef16e7c8 | 255 | static struct uac2_as_header_descriptor as_out_hdr_desc = { |
132fcb46 JB |
256 | .bLength = sizeof as_out_hdr_desc, |
257 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
258 | ||
259 | .bDescriptorSubtype = UAC_AS_GENERAL, | |
260 | .bTerminalLink = USB_OUT_IT_ID, | |
261 | .bmControls = 0, | |
262 | .bFormatType = UAC_FORMAT_TYPE_I, | |
263 | .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), | |
264 | .iChannelNames = 0, | |
265 | }; | |
266 | ||
267 | /* Audio USB_OUT Format */ | |
ef16e7c8 | 268 | static struct uac2_format_type_i_descriptor as_out_fmt1_desc = { |
132fcb46 JB |
269 | .bLength = sizeof as_out_fmt1_desc, |
270 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
271 | .bDescriptorSubtype = UAC_FORMAT_TYPE, | |
272 | .bFormatType = UAC_FORMAT_TYPE_I, | |
273 | }; | |
274 | ||
275 | /* STD AS ISO OUT Endpoint */ | |
ef16e7c8 | 276 | static struct usb_endpoint_descriptor fs_epout_desc = { |
132fcb46 JB |
277 | .bLength = USB_DT_ENDPOINT_SIZE, |
278 | .bDescriptorType = USB_DT_ENDPOINT, | |
279 | ||
280 | .bEndpointAddress = USB_DIR_OUT, | |
281 | .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, | |
703a303c | 282 | .wMaxPacketSize = cpu_to_le16(1023), |
132fcb46 JB |
283 | .bInterval = 1, |
284 | }; | |
285 | ||
ef16e7c8 | 286 | static struct usb_endpoint_descriptor hs_epout_desc = { |
132fcb46 JB |
287 | .bLength = USB_DT_ENDPOINT_SIZE, |
288 | .bDescriptorType = USB_DT_ENDPOINT, | |
289 | ||
290 | .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, | |
703a303c | 291 | .wMaxPacketSize = cpu_to_le16(1024), |
132fcb46 JB |
292 | .bInterval = 4, |
293 | }; | |
294 | ||
295 | /* CS AS ISO OUT Endpoint */ | |
296 | static struct uac2_iso_endpoint_descriptor as_iso_out_desc = { | |
297 | .bLength = sizeof as_iso_out_desc, | |
298 | .bDescriptorType = USB_DT_CS_ENDPOINT, | |
299 | ||
300 | .bDescriptorSubtype = UAC_EP_GENERAL, | |
301 | .bmAttributes = 0, | |
302 | .bmControls = 0, | |
303 | .bLockDelayUnits = 0, | |
304 | .wLockDelay = 0, | |
305 | }; | |
306 | ||
307 | /* Audio Streaming IN Interface - Alt0 */ | |
308 | static struct usb_interface_descriptor std_as_in_if0_desc = { | |
309 | .bLength = sizeof std_as_in_if0_desc, | |
310 | .bDescriptorType = USB_DT_INTERFACE, | |
311 | ||
312 | .bAlternateSetting = 0, | |
313 | .bNumEndpoints = 0, | |
314 | .bInterfaceClass = USB_CLASS_AUDIO, | |
315 | .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, | |
316 | .bInterfaceProtocol = UAC_VERSION_2, | |
317 | }; | |
318 | ||
319 | /* Audio Streaming IN Interface - Alt1 */ | |
320 | static struct usb_interface_descriptor std_as_in_if1_desc = { | |
321 | .bLength = sizeof std_as_in_if1_desc, | |
322 | .bDescriptorType = USB_DT_INTERFACE, | |
323 | ||
324 | .bAlternateSetting = 1, | |
325 | .bNumEndpoints = 1, | |
326 | .bInterfaceClass = USB_CLASS_AUDIO, | |
327 | .bInterfaceSubClass = USB_SUBCLASS_AUDIOSTREAMING, | |
328 | .bInterfaceProtocol = UAC_VERSION_2, | |
329 | }; | |
330 | ||
331 | /* Audio Stream IN Intface Desc */ | |
ef16e7c8 | 332 | static struct uac2_as_header_descriptor as_in_hdr_desc = { |
132fcb46 JB |
333 | .bLength = sizeof as_in_hdr_desc, |
334 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
335 | ||
336 | .bDescriptorSubtype = UAC_AS_GENERAL, | |
337 | .bTerminalLink = USB_IN_OT_ID, | |
338 | .bmControls = 0, | |
339 | .bFormatType = UAC_FORMAT_TYPE_I, | |
340 | .bmFormats = cpu_to_le32(UAC_FORMAT_TYPE_I_PCM), | |
341 | .iChannelNames = 0, | |
342 | }; | |
343 | ||
344 | /* Audio USB_IN Format */ | |
ef16e7c8 | 345 | static struct uac2_format_type_i_descriptor as_in_fmt1_desc = { |
132fcb46 JB |
346 | .bLength = sizeof as_in_fmt1_desc, |
347 | .bDescriptorType = USB_DT_CS_INTERFACE, | |
348 | .bDescriptorSubtype = UAC_FORMAT_TYPE, | |
349 | .bFormatType = UAC_FORMAT_TYPE_I, | |
350 | }; | |
351 | ||
352 | /* STD AS ISO IN Endpoint */ | |
ef16e7c8 | 353 | static struct usb_endpoint_descriptor fs_epin_desc = { |
132fcb46 JB |
354 | .bLength = USB_DT_ENDPOINT_SIZE, |
355 | .bDescriptorType = USB_DT_ENDPOINT, | |
356 | ||
357 | .bEndpointAddress = USB_DIR_IN, | |
358 | .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, | |
703a303c | 359 | .wMaxPacketSize = cpu_to_le16(1023), |
132fcb46 JB |
360 | .bInterval = 1, |
361 | }; | |
362 | ||
ef16e7c8 | 363 | static struct usb_endpoint_descriptor hs_epin_desc = { |
132fcb46 JB |
364 | .bLength = USB_DT_ENDPOINT_SIZE, |
365 | .bDescriptorType = USB_DT_ENDPOINT, | |
366 | ||
367 | .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, | |
703a303c | 368 | .wMaxPacketSize = cpu_to_le16(1024), |
132fcb46 JB |
369 | .bInterval = 4, |
370 | }; | |
371 | ||
372 | /* CS AS ISO IN Endpoint */ | |
373 | static struct uac2_iso_endpoint_descriptor as_iso_in_desc = { | |
374 | .bLength = sizeof as_iso_in_desc, | |
375 | .bDescriptorType = USB_DT_CS_ENDPOINT, | |
376 | ||
377 | .bDescriptorSubtype = UAC_EP_GENERAL, | |
378 | .bmAttributes = 0, | |
379 | .bmControls = 0, | |
380 | .bLockDelayUnits = 0, | |
381 | .wLockDelay = 0, | |
382 | }; | |
383 | ||
384 | static struct usb_descriptor_header *fs_audio_desc[] = { | |
385 | (struct usb_descriptor_header *)&iad_desc, | |
386 | (struct usb_descriptor_header *)&std_ac_if_desc, | |
387 | ||
388 | (struct usb_descriptor_header *)&ac_hdr_desc, | |
389 | (struct usb_descriptor_header *)&in_clk_src_desc, | |
390 | (struct usb_descriptor_header *)&out_clk_src_desc, | |
391 | (struct usb_descriptor_header *)&usb_out_it_desc, | |
392 | (struct usb_descriptor_header *)&io_in_it_desc, | |
393 | (struct usb_descriptor_header *)&usb_in_ot_desc, | |
394 | (struct usb_descriptor_header *)&io_out_ot_desc, | |
395 | ||
396 | (struct usb_descriptor_header *)&std_as_out_if0_desc, | |
397 | (struct usb_descriptor_header *)&std_as_out_if1_desc, | |
398 | ||
399 | (struct usb_descriptor_header *)&as_out_hdr_desc, | |
400 | (struct usb_descriptor_header *)&as_out_fmt1_desc, | |
401 | (struct usb_descriptor_header *)&fs_epout_desc, | |
402 | (struct usb_descriptor_header *)&as_iso_out_desc, | |
403 | ||
404 | (struct usb_descriptor_header *)&std_as_in_if0_desc, | |
405 | (struct usb_descriptor_header *)&std_as_in_if1_desc, | |
406 | ||
407 | (struct usb_descriptor_header *)&as_in_hdr_desc, | |
408 | (struct usb_descriptor_header *)&as_in_fmt1_desc, | |
409 | (struct usb_descriptor_header *)&fs_epin_desc, | |
410 | (struct usb_descriptor_header *)&as_iso_in_desc, | |
411 | NULL, | |
412 | }; | |
413 | ||
414 | static struct usb_descriptor_header *hs_audio_desc[] = { | |
415 | (struct usb_descriptor_header *)&iad_desc, | |
416 | (struct usb_descriptor_header *)&std_ac_if_desc, | |
417 | ||
418 | (struct usb_descriptor_header *)&ac_hdr_desc, | |
419 | (struct usb_descriptor_header *)&in_clk_src_desc, | |
420 | (struct usb_descriptor_header *)&out_clk_src_desc, | |
421 | (struct usb_descriptor_header *)&usb_out_it_desc, | |
422 | (struct usb_descriptor_header *)&io_in_it_desc, | |
423 | (struct usb_descriptor_header *)&usb_in_ot_desc, | |
424 | (struct usb_descriptor_header *)&io_out_ot_desc, | |
425 | ||
426 | (struct usb_descriptor_header *)&std_as_out_if0_desc, | |
427 | (struct usb_descriptor_header *)&std_as_out_if1_desc, | |
428 | ||
429 | (struct usb_descriptor_header *)&as_out_hdr_desc, | |
430 | (struct usb_descriptor_header *)&as_out_fmt1_desc, | |
431 | (struct usb_descriptor_header *)&hs_epout_desc, | |
432 | (struct usb_descriptor_header *)&as_iso_out_desc, | |
433 | ||
434 | (struct usb_descriptor_header *)&std_as_in_if0_desc, | |
435 | (struct usb_descriptor_header *)&std_as_in_if1_desc, | |
436 | ||
437 | (struct usb_descriptor_header *)&as_in_hdr_desc, | |
438 | (struct usb_descriptor_header *)&as_in_fmt1_desc, | |
439 | (struct usb_descriptor_header *)&hs_epin_desc, | |
440 | (struct usb_descriptor_header *)&as_iso_in_desc, | |
441 | NULL, | |
442 | }; | |
443 | ||
444 | struct cntrl_cur_lay3 { | |
445 | __u32 dCUR; | |
446 | }; | |
447 | ||
448 | struct cntrl_range_lay3 { | |
449 | __u16 wNumSubRanges; | |
450 | __u32 dMIN; | |
451 | __u32 dMAX; | |
452 | __u32 dRES; | |
453 | } __packed; | |
454 | ||
913e4a90 PC |
455 | static void set_ep_max_packet_size(const struct f_uac2_opts *uac2_opts, |
456 | struct usb_endpoint_descriptor *ep_desc, | |
457 | unsigned int factor, bool is_playback) | |
458 | { | |
459 | int chmask, srate, ssize; | |
460 | u16 max_packet_size; | |
461 | ||
462 | if (is_playback) { | |
463 | chmask = uac2_opts->p_chmask; | |
464 | srate = uac2_opts->p_srate; | |
465 | ssize = uac2_opts->p_ssize; | |
466 | } else { | |
467 | chmask = uac2_opts->c_chmask; | |
468 | srate = uac2_opts->c_srate; | |
469 | ssize = uac2_opts->c_ssize; | |
470 | } | |
471 | ||
472 | max_packet_size = num_channels(chmask) * ssize * | |
473 | DIV_ROUND_UP(srate, factor / (1 << (ep_desc->bInterval - 1))); | |
0f4315a8 | 474 | ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_packet_size, |
913e4a90 PC |
475 | le16_to_cpu(ep_desc->wMaxPacketSize))); |
476 | } | |
477 | ||
f8f93d24 | 478 | static int |
132fcb46 JB |
479 | afunc_bind(struct usb_configuration *cfg, struct usb_function *fn) |
480 | { | |
eb9fecb9 RB |
481 | struct f_uac2 *uac2 = func_to_uac2(fn); |
482 | struct g_audio *agdev = func_to_g_audio(fn); | |
132fcb46 JB |
483 | struct usb_composite_dev *cdev = cfg->cdev; |
484 | struct usb_gadget *gadget = cdev->gadget; | |
7158b57a | 485 | struct device *dev = &gadget->dev; |
f8f93d24 | 486 | struct f_uac2_opts *uac2_opts; |
f408757f | 487 | struct usb_string *us; |
132fcb46 JB |
488 | int ret; |
489 | ||
f8f93d24 | 490 | uac2_opts = container_of(fn->fi, struct f_uac2_opts, func_inst); |
f8f93d24 | 491 | |
f408757f AP |
492 | us = usb_gstrings_attach(cdev, fn_strings, ARRAY_SIZE(strings_fn)); |
493 | if (IS_ERR(us)) | |
494 | return PTR_ERR(us); | |
495 | iad_desc.iFunction = us[STR_ASSOC].id; | |
496 | std_ac_if_desc.iInterface = us[STR_IF_CTRL].id; | |
497 | in_clk_src_desc.iClockSource = us[STR_CLKSRC_IN].id; | |
498 | out_clk_src_desc.iClockSource = us[STR_CLKSRC_OUT].id; | |
499 | usb_out_it_desc.iTerminal = us[STR_USB_IT].id; | |
500 | io_in_it_desc.iTerminal = us[STR_IO_IT].id; | |
501 | usb_in_ot_desc.iTerminal = us[STR_USB_OT].id; | |
502 | io_out_ot_desc.iTerminal = us[STR_IO_OT].id; | |
503 | std_as_out_if0_desc.iInterface = us[STR_AS_OUT_ALT0].id; | |
504 | std_as_out_if1_desc.iInterface = us[STR_AS_OUT_ALT1].id; | |
505 | std_as_in_if0_desc.iInterface = us[STR_AS_IN_ALT0].id; | |
506 | std_as_in_if1_desc.iInterface = us[STR_AS_IN_ALT1].id; | |
507 | ||
f8f93d24 | 508 | |
f8f93d24 AP |
509 | /* Initialize the configurable parameters */ |
510 | usb_out_it_desc.bNrChannels = num_channels(uac2_opts->c_chmask); | |
511 | usb_out_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); | |
512 | io_in_it_desc.bNrChannels = num_channels(uac2_opts->p_chmask); | |
513 | io_in_it_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); | |
514 | as_out_hdr_desc.bNrChannels = num_channels(uac2_opts->c_chmask); | |
515 | as_out_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->c_chmask); | |
516 | as_in_hdr_desc.bNrChannels = num_channels(uac2_opts->p_chmask); | |
517 | as_in_hdr_desc.bmChannelConfig = cpu_to_le32(uac2_opts->p_chmask); | |
518 | as_out_fmt1_desc.bSubslotSize = uac2_opts->c_ssize; | |
519 | as_out_fmt1_desc.bBitResolution = uac2_opts->c_ssize * 8; | |
520 | as_in_fmt1_desc.bSubslotSize = uac2_opts->p_ssize; | |
521 | as_in_fmt1_desc.bBitResolution = uac2_opts->p_ssize * 8; | |
522 | ||
523 | snprintf(clksrc_in, sizeof(clksrc_in), "%uHz", uac2_opts->p_srate); | |
524 | snprintf(clksrc_out, sizeof(clksrc_out), "%uHz", uac2_opts->c_srate); | |
f8f93d24 | 525 | |
132fcb46 JB |
526 | ret = usb_interface_id(cfg, fn); |
527 | if (ret < 0) { | |
a8147dab | 528 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
132fcb46 JB |
529 | return ret; |
530 | } | |
531 | std_ac_if_desc.bInterfaceNumber = ret; | |
eb9fecb9 RB |
532 | uac2->ac_intf = ret; |
533 | uac2->ac_alt = 0; | |
132fcb46 JB |
534 | |
535 | ret = usb_interface_id(cfg, fn); | |
536 | if (ret < 0) { | |
a8147dab | 537 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
132fcb46 JB |
538 | return ret; |
539 | } | |
540 | std_as_out_if0_desc.bInterfaceNumber = ret; | |
541 | std_as_out_if1_desc.bInterfaceNumber = ret; | |
eb9fecb9 RB |
542 | uac2->as_out_intf = ret; |
543 | uac2->as_out_alt = 0; | |
132fcb46 JB |
544 | |
545 | ret = usb_interface_id(cfg, fn); | |
546 | if (ret < 0) { | |
a8147dab | 547 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
132fcb46 JB |
548 | return ret; |
549 | } | |
550 | std_as_in_if0_desc.bInterfaceNumber = ret; | |
551 | std_as_in_if1_desc.bInterfaceNumber = ret; | |
eb9fecb9 RB |
552 | uac2->as_in_intf = ret; |
553 | uac2->as_in_alt = 0; | |
132fcb46 | 554 | |
0db56e43 SN |
555 | /* Calculate wMaxPacketSize according to audio bandwidth */ |
556 | set_ep_max_packet_size(uac2_opts, &fs_epin_desc, 1000, true); | |
557 | set_ep_max_packet_size(uac2_opts, &fs_epout_desc, 1000, false); | |
558 | set_ep_max_packet_size(uac2_opts, &hs_epin_desc, 8000, true); | |
559 | set_ep_max_packet_size(uac2_opts, &hs_epout_desc, 8000, false); | |
560 | ||
132fcb46 | 561 | agdev->out_ep = usb_ep_autoconfig(gadget, &fs_epout_desc); |
391aa852 | 562 | if (!agdev->out_ep) { |
a8147dab | 563 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
f1d3861d | 564 | return ret; |
391aa852 | 565 | } |
132fcb46 JB |
566 | |
567 | agdev->in_ep = usb_ep_autoconfig(gadget, &fs_epin_desc); | |
391aa852 | 568 | if (!agdev->in_ep) { |
a8147dab | 569 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
f1d3861d | 570 | return ret; |
391aa852 | 571 | } |
132fcb46 | 572 | |
14e1d56c RB |
573 | agdev->in_ep_maxpsize = max_t(u16, |
574 | le16_to_cpu(fs_epin_desc.wMaxPacketSize), | |
575 | le16_to_cpu(hs_epin_desc.wMaxPacketSize)); | |
576 | agdev->out_ep_maxpsize = max_t(u16, | |
577 | le16_to_cpu(fs_epout_desc.wMaxPacketSize), | |
578 | le16_to_cpu(hs_epout_desc.wMaxPacketSize)); | |
eb127cb5 | 579 | |
132fcb46 | 580 | hs_epout_desc.bEndpointAddress = fs_epout_desc.bEndpointAddress; |
132fcb46 | 581 | hs_epin_desc.bEndpointAddress = fs_epin_desc.bEndpointAddress; |
132fcb46 | 582 | |
eaef50c7 JY |
583 | ret = usb_assign_descriptors(fn, fs_audio_desc, hs_audio_desc, NULL, |
584 | NULL); | |
10287bae | 585 | if (ret) |
f1d3861d | 586 | return ret; |
132fcb46 | 587 | |
7158b57a RB |
588 | agdev->gadget = gadget; |
589 | ||
eb9fecb9 RB |
590 | agdev->params.p_chmask = uac2_opts->p_chmask; |
591 | agdev->params.p_srate = uac2_opts->p_srate; | |
592 | agdev->params.p_ssize = uac2_opts->p_ssize; | |
593 | agdev->params.c_chmask = uac2_opts->c_chmask; | |
594 | agdev->params.c_srate = uac2_opts->c_srate; | |
595 | agdev->params.c_ssize = uac2_opts->c_ssize; | |
596 | agdev->params.req_number = uac2_opts->req_number; | |
597 | ret = g_audio_setup(agdev, "UAC2 PCM", "UAC2_Gadget"); | |
391aa852 | 598 | if (ret) |
eb9fecb9 | 599 | goto err_free_descs; |
391aa852 | 600 | return 0; |
d12a8727 | 601 | |
f1d3861d PC |
602 | err_free_descs: |
603 | usb_free_all_descriptors(fn); | |
7158b57a | 604 | agdev->gadget = NULL; |
88f950a6 | 605 | return ret; |
132fcb46 JB |
606 | } |
607 | ||
132fcb46 JB |
608 | static int |
609 | afunc_set_alt(struct usb_function *fn, unsigned intf, unsigned alt) | |
610 | { | |
611 | struct usb_composite_dev *cdev = fn->config->cdev; | |
eb9fecb9 | 612 | struct f_uac2 *uac2 = func_to_uac2(fn); |
132fcb46 | 613 | struct usb_gadget *gadget = cdev->gadget; |
7158b57a | 614 | struct device *dev = &gadget->dev; |
eb9fecb9 | 615 | int ret = 0; |
132fcb46 JB |
616 | |
617 | /* No i/f has more than 2 alt settings */ | |
618 | if (alt > 1) { | |
a8147dab | 619 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
132fcb46 JB |
620 | return -EINVAL; |
621 | } | |
622 | ||
eb9fecb9 | 623 | if (intf == uac2->ac_intf) { |
132fcb46 JB |
624 | /* Control I/f has only 1 AltSetting - 0 */ |
625 | if (alt) { | |
a8147dab | 626 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
132fcb46 JB |
627 | return -EINVAL; |
628 | } | |
629 | return 0; | |
630 | } | |
631 | ||
eb9fecb9 RB |
632 | if (intf == uac2->as_out_intf) { |
633 | uac2->as_out_alt = alt; | |
9bb87f16 | 634 | |
eb9fecb9 RB |
635 | if (alt) |
636 | ret = u_audio_start_capture(&uac2->g_audio); | |
9bb87f16 | 637 | else |
eb9fecb9 RB |
638 | u_audio_stop_capture(&uac2->g_audio); |
639 | } else if (intf == uac2->as_in_intf) { | |
640 | uac2->as_in_alt = alt; | |
9bb87f16 | 641 | |
eb9fecb9 RB |
642 | if (alt) |
643 | ret = u_audio_start_playback(&uac2->g_audio); | |
644 | else | |
645 | u_audio_stop_playback(&uac2->g_audio); | |
132fcb46 | 646 | } else { |
a8147dab | 647 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
132fcb46 JB |
648 | return -EINVAL; |
649 | } | |
650 | ||
eb9fecb9 | 651 | return ret; |
132fcb46 JB |
652 | } |
653 | ||
654 | static int | |
655 | afunc_get_alt(struct usb_function *fn, unsigned intf) | |
656 | { | |
eb9fecb9 RB |
657 | struct f_uac2 *uac2 = func_to_uac2(fn); |
658 | struct g_audio *agdev = func_to_g_audio(fn); | |
659 | ||
660 | if (intf == uac2->ac_intf) | |
661 | return uac2->ac_alt; | |
662 | else if (intf == uac2->as_out_intf) | |
663 | return uac2->as_out_alt; | |
664 | else if (intf == uac2->as_in_intf) | |
665 | return uac2->as_in_alt; | |
132fcb46 | 666 | else |
7158b57a | 667 | dev_err(&agdev->gadget->dev, |
132fcb46 JB |
668 | "%s:%d Invalid Interface %d!\n", |
669 | __func__, __LINE__, intf); | |
670 | ||
671 | return -EINVAL; | |
672 | } | |
673 | ||
674 | static void | |
675 | afunc_disable(struct usb_function *fn) | |
676 | { | |
eb9fecb9 | 677 | struct f_uac2 *uac2 = func_to_uac2(fn); |
132fcb46 | 678 | |
eb9fecb9 RB |
679 | uac2->as_in_alt = 0; |
680 | uac2->as_out_alt = 0; | |
681 | u_audio_stop_capture(&uac2->g_audio); | |
682 | u_audio_stop_playback(&uac2->g_audio); | |
132fcb46 JB |
683 | } |
684 | ||
685 | static int | |
686 | in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) | |
687 | { | |
688 | struct usb_request *req = fn->config->cdev->req; | |
eb9fecb9 | 689 | struct g_audio *agdev = func_to_g_audio(fn); |
f8f93d24 | 690 | struct f_uac2_opts *opts; |
132fcb46 JB |
691 | u16 w_length = le16_to_cpu(cr->wLength); |
692 | u16 w_index = le16_to_cpu(cr->wIndex); | |
693 | u16 w_value = le16_to_cpu(cr->wValue); | |
694 | u8 entity_id = (w_index >> 8) & 0xff; | |
695 | u8 control_selector = w_value >> 8; | |
696 | int value = -EOPNOTSUPP; | |
f8f93d24 AP |
697 | int p_srate, c_srate; |
698 | ||
eb9fecb9 | 699 | opts = g_audio_to_uac2_opts(agdev); |
f8f93d24 AP |
700 | p_srate = opts->p_srate; |
701 | c_srate = opts->c_srate; | |
132fcb46 JB |
702 | |
703 | if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { | |
704 | struct cntrl_cur_lay3 c; | |
ffeee83a | 705 | memset(&c, 0, sizeof(struct cntrl_cur_lay3)); |
132fcb46 JB |
706 | |
707 | if (entity_id == USB_IN_CLK_ID) | |
708 | c.dCUR = p_srate; | |
709 | else if (entity_id == USB_OUT_CLK_ID) | |
710 | c.dCUR = c_srate; | |
711 | ||
712 | value = min_t(unsigned, w_length, sizeof c); | |
713 | memcpy(req->buf, &c, value); | |
714 | } else if (control_selector == UAC2_CS_CONTROL_CLOCK_VALID) { | |
715 | *(u8 *)req->buf = 1; | |
716 | value = min_t(unsigned, w_length, 1); | |
717 | } else { | |
7158b57a | 718 | dev_err(&agdev->gadget->dev, |
132fcb46 JB |
719 | "%s:%d control_selector=%d TODO!\n", |
720 | __func__, __LINE__, control_selector); | |
721 | } | |
722 | ||
723 | return value; | |
724 | } | |
725 | ||
726 | static int | |
727 | in_rq_range(struct usb_function *fn, const struct usb_ctrlrequest *cr) | |
728 | { | |
729 | struct usb_request *req = fn->config->cdev->req; | |
eb9fecb9 | 730 | struct g_audio *agdev = func_to_g_audio(fn); |
f8f93d24 | 731 | struct f_uac2_opts *opts; |
132fcb46 JB |
732 | u16 w_length = le16_to_cpu(cr->wLength); |
733 | u16 w_index = le16_to_cpu(cr->wIndex); | |
734 | u16 w_value = le16_to_cpu(cr->wValue); | |
735 | u8 entity_id = (w_index >> 8) & 0xff; | |
736 | u8 control_selector = w_value >> 8; | |
737 | struct cntrl_range_lay3 r; | |
738 | int value = -EOPNOTSUPP; | |
f8f93d24 AP |
739 | int p_srate, c_srate; |
740 | ||
eb9fecb9 | 741 | opts = g_audio_to_uac2_opts(agdev); |
f8f93d24 AP |
742 | p_srate = opts->p_srate; |
743 | c_srate = opts->c_srate; | |
132fcb46 JB |
744 | |
745 | if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) { | |
746 | if (entity_id == USB_IN_CLK_ID) | |
747 | r.dMIN = p_srate; | |
748 | else if (entity_id == USB_OUT_CLK_ID) | |
749 | r.dMIN = c_srate; | |
750 | else | |
751 | return -EOPNOTSUPP; | |
752 | ||
753 | r.dMAX = r.dMIN; | |
754 | r.dRES = 0; | |
755 | r.wNumSubRanges = 1; | |
756 | ||
757 | value = min_t(unsigned, w_length, sizeof r); | |
758 | memcpy(req->buf, &r, value); | |
759 | } else { | |
7158b57a | 760 | dev_err(&agdev->gadget->dev, |
132fcb46 JB |
761 | "%s:%d control_selector=%d TODO!\n", |
762 | __func__, __LINE__, control_selector); | |
763 | } | |
764 | ||
765 | return value; | |
766 | } | |
767 | ||
768 | static int | |
769 | ac_rq_in(struct usb_function *fn, const struct usb_ctrlrequest *cr) | |
770 | { | |
771 | if (cr->bRequest == UAC2_CS_CUR) | |
772 | return in_rq_cur(fn, cr); | |
773 | else if (cr->bRequest == UAC2_CS_RANGE) | |
774 | return in_rq_range(fn, cr); | |
775 | else | |
776 | return -EOPNOTSUPP; | |
777 | } | |
778 | ||
779 | static int | |
780 | out_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) | |
781 | { | |
782 | u16 w_length = le16_to_cpu(cr->wLength); | |
783 | u16 w_value = le16_to_cpu(cr->wValue); | |
784 | u8 control_selector = w_value >> 8; | |
785 | ||
786 | if (control_selector == UAC2_CS_CONTROL_SAM_FREQ) | |
787 | return w_length; | |
788 | ||
789 | return -EOPNOTSUPP; | |
790 | } | |
791 | ||
792 | static int | |
793 | setup_rq_inf(struct usb_function *fn, const struct usb_ctrlrequest *cr) | |
794 | { | |
eb9fecb9 RB |
795 | struct f_uac2 *uac2 = func_to_uac2(fn); |
796 | struct g_audio *agdev = func_to_g_audio(fn); | |
132fcb46 JB |
797 | u16 w_index = le16_to_cpu(cr->wIndex); |
798 | u8 intf = w_index & 0xff; | |
799 | ||
eb9fecb9 | 800 | if (intf != uac2->ac_intf) { |
7158b57a | 801 | dev_err(&agdev->gadget->dev, |
132fcb46 JB |
802 | "%s:%d Error!\n", __func__, __LINE__); |
803 | return -EOPNOTSUPP; | |
804 | } | |
805 | ||
806 | if (cr->bRequestType & USB_DIR_IN) | |
807 | return ac_rq_in(fn, cr); | |
808 | else if (cr->bRequest == UAC2_CS_CUR) | |
809 | return out_rq_cur(fn, cr); | |
810 | ||
811 | return -EOPNOTSUPP; | |
812 | } | |
813 | ||
814 | static int | |
815 | afunc_setup(struct usb_function *fn, const struct usb_ctrlrequest *cr) | |
816 | { | |
817 | struct usb_composite_dev *cdev = fn->config->cdev; | |
eb9fecb9 | 818 | struct g_audio *agdev = func_to_g_audio(fn); |
132fcb46 JB |
819 | struct usb_request *req = cdev->req; |
820 | u16 w_length = le16_to_cpu(cr->wLength); | |
821 | int value = -EOPNOTSUPP; | |
822 | ||
823 | /* Only Class specific requests are supposed to reach here */ | |
824 | if ((cr->bRequestType & USB_TYPE_MASK) != USB_TYPE_CLASS) | |
825 | return -EOPNOTSUPP; | |
826 | ||
827 | if ((cr->bRequestType & USB_RECIP_MASK) == USB_RECIP_INTERFACE) | |
828 | value = setup_rq_inf(fn, cr); | |
829 | else | |
7158b57a RB |
830 | dev_err(&agdev->gadget->dev, "%s:%d Error!\n", |
831 | __func__, __LINE__); | |
132fcb46 JB |
832 | |
833 | if (value >= 0) { | |
834 | req->length = value; | |
835 | req->zero = value < w_length; | |
836 | value = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); | |
837 | if (value < 0) { | |
7158b57a | 838 | dev_err(&agdev->gadget->dev, |
132fcb46 JB |
839 | "%s:%d Error!\n", __func__, __LINE__); |
840 | req->status = 0; | |
841 | } | |
842 | } | |
843 | ||
844 | return value; | |
845 | } | |
846 | ||
3aeea3c5 AP |
847 | static inline struct f_uac2_opts *to_f_uac2_opts(struct config_item *item) |
848 | { | |
849 | return container_of(to_config_group(item), struct f_uac2_opts, | |
850 | func_inst.group); | |
851 | } | |
852 | ||
3aeea3c5 AP |
853 | static void f_uac2_attr_release(struct config_item *item) |
854 | { | |
855 | struct f_uac2_opts *opts = to_f_uac2_opts(item); | |
856 | ||
857 | usb_put_function_instance(&opts->func_inst); | |
858 | } | |
859 | ||
860 | static struct configfs_item_operations f_uac2_item_ops = { | |
861 | .release = f_uac2_attr_release, | |
3aeea3c5 AP |
862 | }; |
863 | ||
864 | #define UAC2_ATTRIBUTE(name) \ | |
495702bc | 865 | static ssize_t f_uac2_opts_##name##_show(struct config_item *item, \ |
3aeea3c5 AP |
866 | char *page) \ |
867 | { \ | |
495702bc | 868 | struct f_uac2_opts *opts = to_f_uac2_opts(item); \ |
3aeea3c5 AP |
869 | int result; \ |
870 | \ | |
871 | mutex_lock(&opts->lock); \ | |
872 | result = sprintf(page, "%u\n", opts->name); \ | |
873 | mutex_unlock(&opts->lock); \ | |
874 | \ | |
875 | return result; \ | |
876 | } \ | |
877 | \ | |
495702bc | 878 | static ssize_t f_uac2_opts_##name##_store(struct config_item *item, \ |
3aeea3c5 AP |
879 | const char *page, size_t len) \ |
880 | { \ | |
495702bc | 881 | struct f_uac2_opts *opts = to_f_uac2_opts(item); \ |
3aeea3c5 AP |
882 | int ret; \ |
883 | u32 num; \ | |
884 | \ | |
885 | mutex_lock(&opts->lock); \ | |
886 | if (opts->refcnt) { \ | |
887 | ret = -EBUSY; \ | |
888 | goto end; \ | |
889 | } \ | |
890 | \ | |
891 | ret = kstrtou32(page, 0, &num); \ | |
892 | if (ret) \ | |
893 | goto end; \ | |
894 | \ | |
895 | opts->name = num; \ | |
896 | ret = len; \ | |
897 | \ | |
898 | end: \ | |
899 | mutex_unlock(&opts->lock); \ | |
900 | return ret; \ | |
901 | } \ | |
902 | \ | |
495702bc | 903 | CONFIGFS_ATTR(f_uac2_opts_, name) |
3aeea3c5 AP |
904 | |
905 | UAC2_ATTRIBUTE(p_chmask); | |
906 | UAC2_ATTRIBUTE(p_srate); | |
907 | UAC2_ATTRIBUTE(p_ssize); | |
908 | UAC2_ATTRIBUTE(c_chmask); | |
909 | UAC2_ATTRIBUTE(c_srate); | |
910 | UAC2_ATTRIBUTE(c_ssize); | |
e92b9d44 | 911 | UAC2_ATTRIBUTE(req_number); |
3aeea3c5 AP |
912 | |
913 | static struct configfs_attribute *f_uac2_attrs[] = { | |
495702bc CH |
914 | &f_uac2_opts_attr_p_chmask, |
915 | &f_uac2_opts_attr_p_srate, | |
916 | &f_uac2_opts_attr_p_ssize, | |
917 | &f_uac2_opts_attr_c_chmask, | |
918 | &f_uac2_opts_attr_c_srate, | |
919 | &f_uac2_opts_attr_c_ssize, | |
e92b9d44 | 920 | &f_uac2_opts_attr_req_number, |
3aeea3c5 AP |
921 | NULL, |
922 | }; | |
923 | ||
924 | static struct config_item_type f_uac2_func_type = { | |
925 | .ct_item_ops = &f_uac2_item_ops, | |
926 | .ct_attrs = f_uac2_attrs, | |
927 | .ct_owner = THIS_MODULE, | |
928 | }; | |
929 | ||
f8f93d24 AP |
930 | static void afunc_free_inst(struct usb_function_instance *f) |
931 | { | |
932 | struct f_uac2_opts *opts; | |
933 | ||
934 | opts = container_of(f, struct f_uac2_opts, func_inst); | |
935 | kfree(opts); | |
936 | } | |
937 | ||
938 | static struct usb_function_instance *afunc_alloc_inst(void) | |
939 | { | |
940 | struct f_uac2_opts *opts; | |
941 | ||
942 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); | |
943 | if (!opts) | |
944 | return ERR_PTR(-ENOMEM); | |
945 | ||
3aeea3c5 | 946 | mutex_init(&opts->lock); |
f8f93d24 AP |
947 | opts->func_inst.free_func_inst = afunc_free_inst; |
948 | ||
3aeea3c5 AP |
949 | config_group_init_type_name(&opts->func_inst.group, "", |
950 | &f_uac2_func_type); | |
951 | ||
952 | opts->p_chmask = UAC2_DEF_PCHMASK; | |
953 | opts->p_srate = UAC2_DEF_PSRATE; | |
954 | opts->p_ssize = UAC2_DEF_PSSIZE; | |
955 | opts->c_chmask = UAC2_DEF_CCHMASK; | |
956 | opts->c_srate = UAC2_DEF_CSRATE; | |
957 | opts->c_ssize = UAC2_DEF_CSSIZE; | |
e92b9d44 | 958 | opts->req_number = UAC2_DEF_REQ_NUM; |
f8f93d24 AP |
959 | return &opts->func_inst; |
960 | } | |
961 | ||
962 | static void afunc_free(struct usb_function *f) | |
963 | { | |
eb9fecb9 | 964 | struct g_audio *agdev; |
3aeea3c5 | 965 | struct f_uac2_opts *opts; |
f8f93d24 | 966 | |
eb9fecb9 | 967 | agdev = func_to_g_audio(f); |
3aeea3c5 | 968 | opts = container_of(f->fi, struct f_uac2_opts, func_inst); |
f8f93d24 | 969 | kfree(agdev); |
3aeea3c5 AP |
970 | mutex_lock(&opts->lock); |
971 | --opts->refcnt; | |
972 | mutex_unlock(&opts->lock); | |
f8f93d24 AP |
973 | } |
974 | ||
975 | static void afunc_unbind(struct usb_configuration *c, struct usb_function *f) | |
976 | { | |
eb9fecb9 | 977 | struct g_audio *agdev = func_to_g_audio(f); |
f8f93d24 | 978 | |
eb9fecb9 | 979 | g_audio_cleanup(agdev); |
f8f93d24 | 980 | usb_free_all_descriptors(f); |
7158b57a RB |
981 | |
982 | agdev->gadget = NULL; | |
f8f93d24 AP |
983 | } |
984 | ||
ef16e7c8 | 985 | static struct usb_function *afunc_alloc(struct usb_function_instance *fi) |
f8f93d24 | 986 | { |
eb9fecb9 | 987 | struct f_uac2 *uac2; |
f8f93d24 AP |
988 | struct f_uac2_opts *opts; |
989 | ||
eb9fecb9 RB |
990 | uac2 = kzalloc(sizeof(*uac2), GFP_KERNEL); |
991 | if (uac2 == NULL) | |
f8f93d24 AP |
992 | return ERR_PTR(-ENOMEM); |
993 | ||
994 | opts = container_of(fi, struct f_uac2_opts, func_inst); | |
3aeea3c5 AP |
995 | mutex_lock(&opts->lock); |
996 | ++opts->refcnt; | |
997 | mutex_unlock(&opts->lock); | |
f8f93d24 | 998 | |
eb9fecb9 RB |
999 | uac2->g_audio.func.name = "uac2_func"; |
1000 | uac2->g_audio.func.bind = afunc_bind; | |
1001 | uac2->g_audio.func.unbind = afunc_unbind; | |
1002 | uac2->g_audio.func.set_alt = afunc_set_alt; | |
1003 | uac2->g_audio.func.get_alt = afunc_get_alt; | |
1004 | uac2->g_audio.func.disable = afunc_disable; | |
1005 | uac2->g_audio.func.setup = afunc_setup; | |
1006 | uac2->g_audio.func.free_func = afunc_free; | |
f8f93d24 | 1007 | |
eb9fecb9 | 1008 | return &uac2->g_audio.func; |
f8f93d24 AP |
1009 | } |
1010 | ||
1011 | DECLARE_USB_FUNCTION_INIT(uac2, afunc_alloc_inst, afunc_alloc); | |
1012 | MODULE_LICENSE("GPL"); | |
1013 | MODULE_AUTHOR("Yadwinder Singh"); | |
1014 | MODULE_AUTHOR("Jaswinder Singh"); |