.. | .. |
---|
3 | 3 | * f_uac1.c -- USB Audio Class 1.0 Function (using u_audio API) |
---|
4 | 4 | * |
---|
5 | 5 | * Copyright (C) 2016 Ruslan Bilovol <ruslan.bilovol@gmail.com> |
---|
6 | | - * Copyright (C) 2017 Julian Scheel <julian@jusst.de> |
---|
| 6 | + * Copyright (C) 2021 Julian Scheel <julian@jusst.de> |
---|
7 | 7 | * |
---|
8 | 8 | * This driver doesn't expect any real Audio codec to be present |
---|
9 | 9 | * on the device - the audio streams are simply sinked to and |
---|
.. | .. |
---|
18 | 18 | #include <linux/module.h> |
---|
19 | 19 | |
---|
20 | 20 | #include "u_audio.h" |
---|
21 | | -#include "u_uac.h" |
---|
| 21 | +#include "u_uac1.h" |
---|
22 | 22 | |
---|
23 | 23 | /* UAC1 spec: 3.7.2.3 Audio Channel Cluster Format */ |
---|
24 | 24 | #define UAC1_CHANNEL_MASK 0x0FFF |
---|
| 25 | + |
---|
| 26 | +#define USB_OUT_FU_ID (out_feature_unit_desc->bUnitID) |
---|
| 27 | +#define USB_IN_FU_ID (in_feature_unit_desc->bUnitID) |
---|
| 28 | + |
---|
| 29 | +#define EPIN_EN(_opts) ((_opts)->p_chmask != 0) |
---|
| 30 | +#define EPOUT_EN(_opts) ((_opts)->c_chmask != 0) |
---|
| 31 | +#define FUIN_EN(_opts) ((_opts)->p_mute_present \ |
---|
| 32 | + || (_opts)->p_volume_present) |
---|
| 33 | +#define FUOUT_EN(_opts) ((_opts)->c_mute_present \ |
---|
| 34 | + || (_opts)->c_volume_present) |
---|
| 35 | + |
---|
| 36 | +struct f_uac1 { |
---|
| 37 | + struct g_audio g_audio; |
---|
| 38 | + u8 ac_intf, as_in_intf, as_out_intf; |
---|
| 39 | + u8 ac_alt, as_in_alt, as_out_alt; /* needed for get_alt() */ |
---|
| 40 | + |
---|
| 41 | + struct usb_ctrlrequest setup_cr; /* will be used in data stage */ |
---|
| 42 | + |
---|
| 43 | + /* Interrupt IN endpoint of AC interface */ |
---|
| 44 | + struct usb_ep *int_ep; |
---|
| 45 | + atomic_t int_count; |
---|
| 46 | + int ctl_id; /* EP id */ |
---|
| 47 | + int c_srate; /* current capture srate */ |
---|
| 48 | + int p_srate; /* current playback prate */ |
---|
| 49 | +}; |
---|
| 50 | + |
---|
| 51 | +static inline struct f_uac1 *func_to_uac1(struct usb_function *f) |
---|
| 52 | +{ |
---|
| 53 | + return container_of(f, struct f_uac1, g_audio.func); |
---|
| 54 | +} |
---|
| 55 | + |
---|
| 56 | +static inline struct f_uac1_opts *g_audio_to_uac1_opts(struct g_audio *audio) |
---|
| 57 | +{ |
---|
| 58 | + return container_of(audio->func.fi, struct f_uac1_opts, func_inst); |
---|
| 59 | +} |
---|
| 60 | + |
---|
| 61 | +static struct usb_interface_assoc_descriptor iad_desc = { |
---|
| 62 | + .bLength = sizeof(iad_desc), |
---|
| 63 | + .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, |
---|
| 64 | + /* .bFirstInterface = DYNAMIC */ |
---|
| 65 | + /* .bInterfaceCount = DYNAMIC */ |
---|
| 66 | + .bFunctionClass = USB_CLASS_AUDIO, |
---|
| 67 | + .bFunctionSubClass = USB_SUBCLASS_AUDIOSTREAMING, |
---|
| 68 | + .bFunctionProtocol = UAC_VERSION_1, |
---|
| 69 | +}; |
---|
25 | 70 | |
---|
26 | 71 | /* |
---|
27 | 72 | * DESCRIPTORS ... most are static, but strings and full |
---|
.. | .. |
---|
35 | 80 | * USB-OUT -> IT_1 -> OT_2 -> ALSA_Capture |
---|
36 | 81 | * ALSA_Playback -> IT_3 -> OT_4 -> USB-IN |
---|
37 | 82 | */ |
---|
38 | | -#define F_AUDIO_AC_INTERFACE 0 |
---|
39 | | -#define F_AUDIO_AS_OUT_INTERFACE 1 |
---|
40 | | -#define F_AUDIO_AS_IN_INTERFACE 2 |
---|
41 | | -/* Number of streaming interfaces */ |
---|
42 | | -#define F_AUDIO_NUM_INTERFACES 2 |
---|
43 | | - |
---|
44 | | -static struct usb_interface_assoc_descriptor iad_desc = { |
---|
45 | | - .bLength = sizeof(iad_desc), |
---|
46 | | - .bDescriptorType = USB_DT_INTERFACE_ASSOCIATION, |
---|
47 | | - |
---|
48 | | - .bFirstInterface = 0, |
---|
49 | | - .bFunctionClass = USB_CLASS_AUDIO, |
---|
50 | | - .bFunctionSubClass = USB_SUBCLASS_AUDIOSTREAMING, |
---|
51 | | - .bFunctionProtocol = UAC_VERSION_1, |
---|
52 | | -}; |
---|
53 | 83 | |
---|
54 | 84 | /* B.3.1 Standard AC Interface Descriptor */ |
---|
55 | 85 | static struct usb_interface_descriptor ac_interface_desc = { |
---|
56 | 86 | .bLength = USB_DT_INTERFACE_SIZE, |
---|
57 | 87 | .bDescriptorType = USB_DT_INTERFACE, |
---|
58 | | - .bNumEndpoints = 0, |
---|
| 88 | + /* .bNumEndpoints = DYNAMIC */ |
---|
59 | 89 | .bInterfaceClass = USB_CLASS_AUDIO, |
---|
60 | 90 | .bInterfaceSubClass = USB_SUBCLASS_AUDIOCONTROL, |
---|
61 | 91 | }; |
---|
62 | 92 | |
---|
63 | | -/* |
---|
64 | | - * The number of AudioStreaming and MIDIStreaming interfaces |
---|
65 | | - * in the Audio Interface Collection |
---|
66 | | - */ |
---|
67 | | -DECLARE_UAC_AC_HEADER_DESCRIPTOR(2); |
---|
68 | | - |
---|
69 | 93 | /* B.3.2 Class-Specific AC Interface Descriptor */ |
---|
70 | | -static struct uac1_ac_header_descriptor_2 ac_header_desc = { |
---|
71 | | - .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
72 | | - .bDescriptorSubtype = UAC_HEADER, |
---|
73 | | - .bcdADC = cpu_to_le16(0x0100), |
---|
74 | | - /* .baInterfaceNr[0] = DYNAMIC */ |
---|
75 | | - /* .baInterfaceNr[1] = DYNAMIC */ |
---|
76 | | -}; |
---|
| 94 | +static struct uac1_ac_header_descriptor *ac_header_desc; |
---|
77 | 95 | |
---|
78 | 96 | static struct uac_input_terminal_descriptor usb_out_it_desc = { |
---|
79 | 97 | .bLength = UAC_DT_INPUT_TERMINAL_SIZE, |
---|
80 | 98 | .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
81 | 99 | .bDescriptorSubtype = UAC_INPUT_TERMINAL, |
---|
| 100 | + /* .bTerminalID = DYNAMIC */ |
---|
82 | 101 | .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), |
---|
83 | 102 | .bAssocTerminal = 0, |
---|
84 | 103 | .wChannelConfig = cpu_to_le16(0x3), |
---|
85 | | -}; |
---|
86 | | - |
---|
87 | | -DECLARE_UAC_FEATURE_UNIT_DESCRIPTOR(0); |
---|
88 | | - |
---|
89 | | -static struct uac_feature_unit_descriptor_0 io_out_ot_fu_desc = { |
---|
90 | | - .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), |
---|
91 | | - .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
92 | | - .bDescriptorSubtype = UAC_FEATURE_UNIT, |
---|
93 | | - .bControlSize = 2, |
---|
94 | | - .bmaControls[0] = (UAC_CONTROL_BIT(UAC_FU_MUTE) | |
---|
95 | | - UAC_CONTROL_BIT(UAC_FU_VOLUME)), |
---|
96 | | -}; |
---|
97 | | - |
---|
98 | | -static struct usb_audio_control c_mute_control = { |
---|
99 | | - .list = LIST_HEAD_INIT(c_mute_control.list), |
---|
100 | | - .name = "Capture Mute", |
---|
101 | | - .type = UAC_FU_MUTE, |
---|
102 | | - .set = u_audio_fu_set_cmd, |
---|
103 | | - .get = u_audio_fu_get_cmd, |
---|
104 | | -}; |
---|
105 | | - |
---|
106 | | -static struct usb_audio_control c_volume_control = { |
---|
107 | | - .list = LIST_HEAD_INIT(c_volume_control.list), |
---|
108 | | - .name = "Capture Volume", |
---|
109 | | - .type = UAC_FU_VOLUME, |
---|
110 | | - .set = u_audio_fu_set_cmd, |
---|
111 | | - .get = u_audio_fu_get_cmd, |
---|
112 | | -}; |
---|
113 | | - |
---|
114 | | -static struct usb_audio_control_selector c_feature_unit = { |
---|
115 | | - .list = LIST_HEAD_INIT(c_feature_unit.list), |
---|
116 | | - .name = "Capture Mute & Volume Control", |
---|
117 | | - .type = UAC_FEATURE_UNIT, |
---|
118 | | - .desc = (struct usb_descriptor_header *)&io_out_ot_fu_desc, |
---|
119 | 104 | }; |
---|
120 | 105 | |
---|
121 | 106 | static struct uac1_output_terminal_descriptor io_out_ot_desc = { |
---|
122 | 107 | .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, |
---|
123 | 108 | .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
124 | 109 | .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, |
---|
| 110 | + /* .bTerminalID = DYNAMIC */ |
---|
125 | 111 | .wTerminalType = cpu_to_le16(UAC_OUTPUT_TERMINAL_SPEAKER), |
---|
126 | 112 | .bAssocTerminal = 0, |
---|
| 113 | + /* .bSourceID = DYNAMIC */ |
---|
127 | 114 | }; |
---|
128 | 115 | |
---|
129 | 116 | static struct uac_input_terminal_descriptor io_in_it_desc = { |
---|
130 | 117 | .bLength = UAC_DT_INPUT_TERMINAL_SIZE, |
---|
131 | 118 | .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
132 | 119 | .bDescriptorSubtype = UAC_INPUT_TERMINAL, |
---|
| 120 | + /* .bTerminalID = DYNAMIC */ |
---|
133 | 121 | .wTerminalType = cpu_to_le16(UAC_INPUT_TERMINAL_MICROPHONE), |
---|
134 | 122 | .bAssocTerminal = 0, |
---|
135 | 123 | .wChannelConfig = cpu_to_le16(0x3), |
---|
136 | | -}; |
---|
137 | | - |
---|
138 | | -static struct uac_feature_unit_descriptor_0 usb_in_ot_fu_desc = { |
---|
139 | | - .bLength = UAC_DT_FEATURE_UNIT_SIZE(0), |
---|
140 | | - .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
141 | | - .bDescriptorSubtype = UAC_FEATURE_UNIT, |
---|
142 | | - .bControlSize = 2, |
---|
143 | | - .bmaControls[0] = (UAC_FU_MUTE | UAC_FU_VOLUME), |
---|
144 | | -}; |
---|
145 | | - |
---|
146 | | -static struct usb_audio_control p_mute_control = { |
---|
147 | | - .list = LIST_HEAD_INIT(p_mute_control.list), |
---|
148 | | - .name = "Playback Mute", |
---|
149 | | - .type = UAC_FU_MUTE, |
---|
150 | | - .set = u_audio_fu_set_cmd, |
---|
151 | | - .get = u_audio_fu_get_cmd, |
---|
152 | | -}; |
---|
153 | | - |
---|
154 | | -static struct usb_audio_control p_volume_control = { |
---|
155 | | - .list = LIST_HEAD_INIT(p_volume_control.list), |
---|
156 | | - .name = "Playback Volume", |
---|
157 | | - .type = UAC_FU_VOLUME, |
---|
158 | | - .set = u_audio_fu_set_cmd, |
---|
159 | | - .get = u_audio_fu_get_cmd, |
---|
160 | | -}; |
---|
161 | | - |
---|
162 | | -static struct usb_audio_control_selector p_feature_unit = { |
---|
163 | | - .list = LIST_HEAD_INIT(p_feature_unit.list), |
---|
164 | | - .name = "Playback Mute & Volume Control", |
---|
165 | | - .type = UAC_FEATURE_UNIT, |
---|
166 | | - .desc = (struct usb_descriptor_header *)&usb_in_ot_fu_desc, |
---|
167 | 124 | }; |
---|
168 | 125 | |
---|
169 | 126 | static struct uac1_output_terminal_descriptor usb_in_ot_desc = { |
---|
170 | 127 | .bLength = UAC_DT_OUTPUT_TERMINAL_SIZE, |
---|
171 | 128 | .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
172 | 129 | .bDescriptorSubtype = UAC_OUTPUT_TERMINAL, |
---|
| 130 | + /* .bTerminalID = DYNAMIC */ |
---|
173 | 131 | .wTerminalType = cpu_to_le16(UAC_TERMINAL_STREAMING), |
---|
174 | 132 | .bAssocTerminal = 0, |
---|
| 133 | + /* .bSourceID = DYNAMIC */ |
---|
| 134 | +}; |
---|
| 135 | + |
---|
| 136 | +static struct uac_feature_unit_descriptor *in_feature_unit_desc; |
---|
| 137 | +static struct uac_feature_unit_descriptor *out_feature_unit_desc; |
---|
| 138 | + |
---|
| 139 | +/* AC IN Interrupt Endpoint */ |
---|
| 140 | +static struct usb_endpoint_descriptor fs_int_ep_desc = { |
---|
| 141 | + .bLength = USB_DT_ENDPOINT_SIZE, |
---|
| 142 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 143 | + .bEndpointAddress = USB_DIR_IN, |
---|
| 144 | + .bmAttributes = USB_ENDPOINT_XFER_INT, |
---|
| 145 | + .wMaxPacketSize = cpu_to_le16(2), |
---|
| 146 | + .bInterval = 1, |
---|
| 147 | +}; |
---|
| 148 | + |
---|
| 149 | +static struct usb_endpoint_descriptor ac_int_ep_desc = { |
---|
| 150 | + .bLength = USB_DT_ENDPOINT_SIZE, |
---|
| 151 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 152 | + .bEndpointAddress = USB_DIR_IN, |
---|
| 153 | + .bmAttributes = USB_ENDPOINT_XFER_INT, |
---|
| 154 | + .wMaxPacketSize = cpu_to_le16(2), |
---|
| 155 | + .bInterval = 4, |
---|
| 156 | +}; |
---|
| 157 | + |
---|
| 158 | +static struct usb_ss_ep_comp_descriptor ac_int_ep_desc_comp = { |
---|
| 159 | + .bLength = sizeof(ac_int_ep_desc_comp), |
---|
| 160 | + .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, |
---|
| 161 | + .wBytesPerInterval = cpu_to_le16(2), |
---|
175 | 162 | }; |
---|
176 | 163 | |
---|
177 | 164 | /* B.4.1 Standard AS Interface Descriptor */ |
---|
.. | .. |
---|
216 | 203 | .bLength = UAC_DT_AS_HEADER_SIZE, |
---|
217 | 204 | .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
218 | 205 | .bDescriptorSubtype = UAC_AS_GENERAL, |
---|
| 206 | + /* .bTerminalLink = DYNAMIC */ |
---|
219 | 207 | .bDelay = 1, |
---|
220 | 208 | .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), |
---|
221 | 209 | }; |
---|
.. | .. |
---|
224 | 212 | .bLength = UAC_DT_AS_HEADER_SIZE, |
---|
225 | 213 | .bDescriptorType = USB_DT_CS_INTERFACE, |
---|
226 | 214 | .bDescriptorSubtype = UAC_AS_GENERAL, |
---|
| 215 | + /* .bTerminalLink = DYNAMIC */ |
---|
227 | 216 | .bDelay = 1, |
---|
228 | 217 | .wFormatTag = cpu_to_le16(UAC_FORMAT_TYPE_I_PCM), |
---|
229 | 218 | }; |
---|
230 | 219 | |
---|
231 | 220 | DECLARE_UAC_FORMAT_TYPE_I_DISCRETE_DESC(UAC_MAX_RATES); |
---|
232 | | -#define uac_format_type_i_discrete_descriptor \ |
---|
| 221 | +#define uac_format_type_i_discrete_descriptor \ |
---|
233 | 222 | uac_format_type_i_discrete_descriptor_##UAC_MAX_RATES |
---|
234 | 223 | |
---|
235 | 224 | static struct uac_format_type_i_discrete_descriptor as_out_type_i_desc = { |
---|
.. | .. |
---|
243 | 232 | }; |
---|
244 | 233 | |
---|
245 | 234 | /* Standard ISO OUT Endpoint Descriptor */ |
---|
| 235 | +static struct usb_endpoint_descriptor fs_out_ep_desc = { |
---|
| 236 | + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, |
---|
| 237 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 238 | + .bEndpointAddress = USB_DIR_OUT, |
---|
| 239 | + .bmAttributes = USB_ENDPOINT_SYNC_ADAPTIVE |
---|
| 240 | + | USB_ENDPOINT_XFER_ISOC, |
---|
| 241 | + /* .wMaxPacketSize = DYNAMIC */ |
---|
| 242 | + .bInterval = 1, |
---|
| 243 | +}; |
---|
| 244 | + |
---|
246 | 245 | static struct usb_endpoint_descriptor as_out_ep_desc = { |
---|
247 | 246 | .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, |
---|
248 | 247 | .bDescriptorType = USB_DT_ENDPOINT, |
---|
.. | .. |
---|
253 | 252 | .bInterval = 4, |
---|
254 | 253 | }; |
---|
255 | 254 | |
---|
256 | | -static struct usb_endpoint_descriptor ss_out_ep_desc = { |
---|
257 | | - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, |
---|
258 | | - .bDescriptorType = USB_DT_ENDPOINT, |
---|
259 | | - |
---|
260 | | - .bEndpointAddress = USB_DIR_OUT, |
---|
261 | | - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ADAPTIVE, |
---|
262 | | - /* .wMaxPacketSize = DYNAMIC */ |
---|
263 | | - .bInterval = 4, |
---|
264 | | -}; |
---|
265 | | - |
---|
266 | | -static struct usb_ss_ep_comp_descriptor ss_out_ep_desc_comp = { |
---|
267 | | - .bLength = sizeof(ss_out_ep_desc_comp), |
---|
| 255 | +static struct usb_ss_ep_comp_descriptor as_out_ep_desc_comp = { |
---|
| 256 | + .bLength = sizeof(as_out_ep_desc_comp), |
---|
268 | 257 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, |
---|
269 | | - .bMaxBurst = 0, |
---|
270 | | - .bmAttributes = 0, |
---|
271 | | - /* wBytesPerInterval = DYNAMIC */ |
---|
| 258 | + .wBytesPerInterval = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), |
---|
272 | 259 | }; |
---|
273 | 260 | |
---|
274 | 261 | /* Class-specific AS ISO OUT Endpoint Descriptor */ |
---|
.. | .. |
---|
291 | 278 | .bSamFreqType = 0, /* filled on rate setup */ |
---|
292 | 279 | }; |
---|
293 | 280 | |
---|
294 | | -/* Standard ISO OUT Endpoint Descriptor */ |
---|
| 281 | +/* Standard ISO IN Endpoint Descriptor */ |
---|
| 282 | +static struct usb_endpoint_descriptor fs_in_ep_desc = { |
---|
| 283 | + .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, |
---|
| 284 | + .bDescriptorType = USB_DT_ENDPOINT, |
---|
| 285 | + .bEndpointAddress = USB_DIR_IN, |
---|
| 286 | + .bmAttributes = USB_ENDPOINT_SYNC_ASYNC |
---|
| 287 | + | USB_ENDPOINT_XFER_ISOC, |
---|
| 288 | + /* .wMaxPacketSize = DYNAMIC */ |
---|
| 289 | + .bInterval = 1, |
---|
| 290 | +}; |
---|
| 291 | + |
---|
295 | 292 | static struct usb_endpoint_descriptor as_in_ep_desc = { |
---|
296 | 293 | .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, |
---|
297 | 294 | .bDescriptorType = USB_DT_ENDPOINT, |
---|
.. | .. |
---|
302 | 299 | .bInterval = 4, |
---|
303 | 300 | }; |
---|
304 | 301 | |
---|
305 | | -static struct usb_endpoint_descriptor ss_in_ep_desc = { |
---|
306 | | - .bLength = USB_DT_ENDPOINT_AUDIO_SIZE, |
---|
307 | | - .bDescriptorType = USB_DT_ENDPOINT, |
---|
308 | | - |
---|
309 | | - .bEndpointAddress = USB_DIR_IN, |
---|
310 | | - .bmAttributes = USB_ENDPOINT_XFER_ISOC | USB_ENDPOINT_SYNC_ASYNC, |
---|
311 | | - /* .wMaxPacketSize = DYNAMIC */ |
---|
312 | | - .bInterval = 4, |
---|
313 | | -}; |
---|
314 | | - |
---|
315 | | -static struct usb_ss_ep_comp_descriptor ss_in_ep_desc_comp = { |
---|
316 | | - .bLength = sizeof(ss_in_ep_desc_comp), |
---|
| 302 | +static struct usb_ss_ep_comp_descriptor as_in_ep_desc_comp = { |
---|
| 303 | + .bLength = sizeof(as_in_ep_desc_comp), |
---|
317 | 304 | .bDescriptorType = USB_DT_SS_ENDPOINT_COMP, |
---|
318 | | - .bMaxBurst = 0, |
---|
319 | | - .bmAttributes = 0, |
---|
320 | | - /* wBytesPerInterval = DYNAMIC */ |
---|
| 305 | + .wBytesPerInterval = cpu_to_le16(UAC1_OUT_EP_MAX_PACKET_SIZE), |
---|
321 | 306 | }; |
---|
322 | 307 | |
---|
323 | 308 | /* Class-specific AS ISO OUT Endpoint Descriptor */ |
---|
.. | .. |
---|
330 | 315 | .wLockDelay = 0, |
---|
331 | 316 | }; |
---|
332 | 317 | |
---|
333 | | -static struct usb_descriptor_header *f_audio_desc[] = { |
---|
| 318 | +static struct usb_descriptor_header *fs_audio_desc[] = { |
---|
334 | 319 | (struct usb_descriptor_header *)&iad_desc, |
---|
335 | 320 | (struct usb_descriptor_header *)&ac_interface_desc, |
---|
336 | 321 | (struct usb_descriptor_header *)&ac_header_desc, |
---|
337 | 322 | |
---|
338 | 323 | (struct usb_descriptor_header *)&usb_out_it_desc, |
---|
339 | | - (struct usb_descriptor_header *)&io_out_ot_fu_desc, |
---|
340 | 324 | (struct usb_descriptor_header *)&io_out_ot_desc, |
---|
| 325 | + (struct usb_descriptor_header *)&out_feature_unit_desc, |
---|
| 326 | + |
---|
341 | 327 | (struct usb_descriptor_header *)&io_in_it_desc, |
---|
342 | | - (struct usb_descriptor_header *)&usb_in_ot_fu_desc, |
---|
343 | 328 | (struct usb_descriptor_header *)&usb_in_ot_desc, |
---|
| 329 | + (struct usb_descriptor_header *)&in_feature_unit_desc, |
---|
| 330 | + |
---|
| 331 | + (struct usb_descriptor_header *)&fs_int_ep_desc, |
---|
| 332 | + |
---|
| 333 | + (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, |
---|
| 334 | + (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, |
---|
| 335 | + (struct usb_descriptor_header *)&as_out_header_desc, |
---|
| 336 | + |
---|
| 337 | + (struct usb_descriptor_header *)&as_out_type_i_desc, |
---|
| 338 | + |
---|
| 339 | + (struct usb_descriptor_header *)&fs_out_ep_desc, |
---|
| 340 | + (struct usb_descriptor_header *)&as_iso_out_desc, |
---|
| 341 | + |
---|
| 342 | + (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, |
---|
| 343 | + (struct usb_descriptor_header *)&as_in_interface_alt_1_desc, |
---|
| 344 | + (struct usb_descriptor_header *)&as_in_header_desc, |
---|
| 345 | + |
---|
| 346 | + (struct usb_descriptor_header *)&as_in_type_i_desc, |
---|
| 347 | + |
---|
| 348 | + (struct usb_descriptor_header *)&fs_in_ep_desc, |
---|
| 349 | + (struct usb_descriptor_header *)&as_iso_in_desc, |
---|
| 350 | + NULL, |
---|
| 351 | +}; |
---|
| 352 | + |
---|
| 353 | +static struct usb_descriptor_header *hs_audio_desc[] = { |
---|
| 354 | + (struct usb_descriptor_header *)&iad_desc, |
---|
| 355 | + (struct usb_descriptor_header *)&ac_interface_desc, |
---|
| 356 | + (struct usb_descriptor_header *)&ac_header_desc, |
---|
| 357 | + |
---|
| 358 | + (struct usb_descriptor_header *)&usb_out_it_desc, |
---|
| 359 | + (struct usb_descriptor_header *)&io_out_ot_desc, |
---|
| 360 | + (struct usb_descriptor_header *)&out_feature_unit_desc, |
---|
| 361 | + |
---|
| 362 | + (struct usb_descriptor_header *)&io_in_it_desc, |
---|
| 363 | + (struct usb_descriptor_header *)&usb_in_ot_desc, |
---|
| 364 | + (struct usb_descriptor_header *)&in_feature_unit_desc, |
---|
| 365 | + |
---|
| 366 | + (struct usb_descriptor_header *)&ac_int_ep_desc, |
---|
344 | 367 | |
---|
345 | 368 | (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, |
---|
346 | 369 | (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, |
---|
.. | .. |
---|
362 | 385 | NULL, |
---|
363 | 386 | }; |
---|
364 | 387 | |
---|
365 | | -static struct usb_descriptor_header *f_ss_audio_desc[] = { |
---|
| 388 | +static struct usb_descriptor_header *ss_audio_desc[] = { |
---|
366 | 389 | (struct usb_descriptor_header *)&iad_desc, |
---|
367 | 390 | (struct usb_descriptor_header *)&ac_interface_desc, |
---|
368 | 391 | (struct usb_descriptor_header *)&ac_header_desc, |
---|
369 | 392 | |
---|
370 | 393 | (struct usb_descriptor_header *)&usb_out_it_desc, |
---|
371 | | - (struct usb_descriptor_header *)&io_out_ot_fu_desc, |
---|
372 | 394 | (struct usb_descriptor_header *)&io_out_ot_desc, |
---|
| 395 | + (struct usb_descriptor_header *)&out_feature_unit_desc, |
---|
| 396 | + |
---|
373 | 397 | (struct usb_descriptor_header *)&io_in_it_desc, |
---|
374 | | - (struct usb_descriptor_header *)&usb_in_ot_fu_desc, |
---|
375 | 398 | (struct usb_descriptor_header *)&usb_in_ot_desc, |
---|
| 399 | + (struct usb_descriptor_header *)&in_feature_unit_desc, |
---|
| 400 | + |
---|
| 401 | + (struct usb_descriptor_header *)&ac_int_ep_desc, |
---|
| 402 | + (struct usb_descriptor_header *)&ac_int_ep_desc_comp, |
---|
376 | 403 | |
---|
377 | 404 | (struct usb_descriptor_header *)&as_out_interface_alt_0_desc, |
---|
378 | 405 | (struct usb_descriptor_header *)&as_out_interface_alt_1_desc, |
---|
.. | .. |
---|
380 | 407 | |
---|
381 | 408 | (struct usb_descriptor_header *)&as_out_type_i_desc, |
---|
382 | 409 | |
---|
383 | | - (struct usb_descriptor_header *)&ss_out_ep_desc, |
---|
384 | | - (struct usb_descriptor_header *)&ss_out_ep_desc_comp, |
---|
| 410 | + (struct usb_descriptor_header *)&as_out_ep_desc, |
---|
| 411 | + (struct usb_descriptor_header *)&as_out_ep_desc_comp, |
---|
385 | 412 | (struct usb_descriptor_header *)&as_iso_out_desc, |
---|
386 | 413 | |
---|
387 | 414 | (struct usb_descriptor_header *)&as_in_interface_alt_0_desc, |
---|
.. | .. |
---|
390 | 417 | |
---|
391 | 418 | (struct usb_descriptor_header *)&as_in_type_i_desc, |
---|
392 | 419 | |
---|
393 | | - (struct usb_descriptor_header *)&ss_in_ep_desc, |
---|
394 | | - (struct usb_descriptor_header *)&ss_in_ep_desc_comp, |
---|
| 420 | + (struct usb_descriptor_header *)&as_in_ep_desc, |
---|
| 421 | + (struct usb_descriptor_header *)&as_in_ep_desc_comp, |
---|
395 | 422 | (struct usb_descriptor_header *)&as_iso_in_desc, |
---|
396 | 423 | NULL, |
---|
397 | 424 | }; |
---|
.. | .. |
---|
401 | 428 | STR_AC_IF, |
---|
402 | 429 | STR_USB_OUT_IT, |
---|
403 | 430 | STR_USB_OUT_IT_CH_NAMES, |
---|
404 | | - STR_IO_OUT_OT_FU, |
---|
405 | 431 | STR_IO_OUT_OT, |
---|
406 | 432 | STR_IO_IN_IT, |
---|
407 | 433 | STR_IO_IN_IT_CH_NAMES, |
---|
408 | | - STR_USB_IN_OT_FU, |
---|
409 | 434 | STR_USB_IN_OT, |
---|
| 435 | + STR_FU_IN, |
---|
| 436 | + STR_FU_OUT, |
---|
410 | 437 | STR_AS_OUT_IF_ALT0, |
---|
411 | 438 | STR_AS_OUT_IF_ALT1, |
---|
412 | 439 | STR_AS_IN_IF_ALT0, |
---|
.. | .. |
---|
414 | 441 | }; |
---|
415 | 442 | |
---|
416 | 443 | static struct usb_string strings_uac1[] = { |
---|
417 | | - [STR_ASSOC].s = "Source/Sink", |
---|
| 444 | + /* [STR_ASSOC].s = DYNAMIC, */ |
---|
418 | 445 | [STR_AC_IF].s = "AC Interface", |
---|
419 | 446 | [STR_USB_OUT_IT].s = "Playback Input terminal", |
---|
420 | 447 | [STR_USB_OUT_IT_CH_NAMES].s = "Playback Channels", |
---|
421 | | - [STR_IO_OUT_OT_FU].s = "Playback Feature Unit", |
---|
422 | 448 | [STR_IO_OUT_OT].s = "Playback Output terminal", |
---|
423 | 449 | [STR_IO_IN_IT].s = "Capture Input terminal", |
---|
424 | 450 | [STR_IO_IN_IT_CH_NAMES].s = "Capture Channels", |
---|
425 | | - [STR_USB_IN_OT_FU].s = "Capture Feature Unit", |
---|
426 | 451 | [STR_USB_IN_OT].s = "Capture Output terminal", |
---|
| 452 | + [STR_FU_IN].s = "Capture Volume", |
---|
| 453 | + [STR_FU_OUT].s = "Playback Volume", |
---|
427 | 454 | [STR_AS_OUT_IF_ALT0].s = "Playback Inactive", |
---|
428 | 455 | [STR_AS_OUT_IF_ALT1].s = "Playback Active", |
---|
429 | 456 | [STR_AS_IN_IF_ALT0].s = "Capture Inactive", |
---|
.. | .. |
---|
441 | 468 | NULL, |
---|
442 | 469 | }; |
---|
443 | 470 | |
---|
| 471 | +/* Use macro to overcome line length limitation */ |
---|
| 472 | +#define USBDHDR(p) ((struct usb_descriptor_header *)(p)) |
---|
| 473 | + |
---|
| 474 | +static void setup_headers(struct f_uac1_opts *opts, |
---|
| 475 | + struct usb_descriptor_header **headers, |
---|
| 476 | + enum usb_device_speed speed) |
---|
| 477 | +{ |
---|
| 478 | + struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; |
---|
| 479 | + struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; |
---|
| 480 | + struct usb_ss_ep_comp_descriptor *ep_int_desc_comp = NULL; |
---|
| 481 | + struct usb_endpoint_descriptor *epout_desc; |
---|
| 482 | + struct usb_endpoint_descriptor *epin_desc; |
---|
| 483 | + struct usb_endpoint_descriptor *ep_int_desc; |
---|
| 484 | + int i; |
---|
| 485 | + |
---|
| 486 | + switch (speed) { |
---|
| 487 | + case USB_SPEED_FULL: |
---|
| 488 | + epout_desc = &fs_out_ep_desc; |
---|
| 489 | + epin_desc = &fs_in_ep_desc; |
---|
| 490 | + ep_int_desc = &fs_int_ep_desc; |
---|
| 491 | + break; |
---|
| 492 | + case USB_SPEED_HIGH: |
---|
| 493 | + epout_desc = &as_out_ep_desc; |
---|
| 494 | + epin_desc = &as_in_ep_desc; |
---|
| 495 | + ep_int_desc = &ac_int_ep_desc; |
---|
| 496 | + break; |
---|
| 497 | + default: |
---|
| 498 | + epout_desc = &as_out_ep_desc; |
---|
| 499 | + epout_desc_comp = &as_out_ep_desc_comp; |
---|
| 500 | + epin_desc = &as_in_ep_desc; |
---|
| 501 | + epin_desc_comp = &as_in_ep_desc_comp; |
---|
| 502 | + ep_int_desc = &ac_int_ep_desc; |
---|
| 503 | + ep_int_desc_comp = &ac_int_ep_desc_comp; |
---|
| 504 | + break; |
---|
| 505 | + } |
---|
| 506 | + |
---|
| 507 | + i = 0; |
---|
| 508 | + headers[i++] = USBDHDR(&iad_desc); |
---|
| 509 | + headers[i++] = USBDHDR(&ac_interface_desc); |
---|
| 510 | + headers[i++] = USBDHDR(ac_header_desc); |
---|
| 511 | + |
---|
| 512 | + if (EPOUT_EN(opts)) { |
---|
| 513 | + headers[i++] = USBDHDR(&usb_out_it_desc); |
---|
| 514 | + headers[i++] = USBDHDR(&io_out_ot_desc); |
---|
| 515 | + if (FUOUT_EN(opts)) |
---|
| 516 | + headers[i++] = USBDHDR(out_feature_unit_desc); |
---|
| 517 | + } |
---|
| 518 | + |
---|
| 519 | + if (EPIN_EN(opts)) { |
---|
| 520 | + headers[i++] = USBDHDR(&io_in_it_desc); |
---|
| 521 | + headers[i++] = USBDHDR(&usb_in_ot_desc); |
---|
| 522 | + if (FUIN_EN(opts)) |
---|
| 523 | + headers[i++] = USBDHDR(in_feature_unit_desc); |
---|
| 524 | + } |
---|
| 525 | + |
---|
| 526 | + if (FUOUT_EN(opts) || FUIN_EN(opts)) { |
---|
| 527 | + headers[i++] = USBDHDR(ep_int_desc); |
---|
| 528 | + if (ep_int_desc_comp) |
---|
| 529 | + headers[i++] = USBDHDR(ep_int_desc_comp); |
---|
| 530 | + } |
---|
| 531 | + |
---|
| 532 | + if (EPOUT_EN(opts)) { |
---|
| 533 | + headers[i++] = USBDHDR(&as_out_interface_alt_0_desc); |
---|
| 534 | + headers[i++] = USBDHDR(&as_out_interface_alt_1_desc); |
---|
| 535 | + headers[i++] = USBDHDR(&as_out_header_desc); |
---|
| 536 | + headers[i++] = USBDHDR(&as_out_type_i_desc); |
---|
| 537 | + headers[i++] = USBDHDR(epout_desc); |
---|
| 538 | + if (epout_desc_comp) |
---|
| 539 | + headers[i++] = USBDHDR(epout_desc_comp); |
---|
| 540 | + headers[i++] = USBDHDR(&as_iso_out_desc); |
---|
| 541 | + } |
---|
| 542 | + if (EPIN_EN(opts)) { |
---|
| 543 | + headers[i++] = USBDHDR(&as_in_interface_alt_0_desc); |
---|
| 544 | + headers[i++] = USBDHDR(&as_in_interface_alt_1_desc); |
---|
| 545 | + headers[i++] = USBDHDR(&as_in_header_desc); |
---|
| 546 | + headers[i++] = USBDHDR(&as_in_type_i_desc); |
---|
| 547 | + headers[i++] = USBDHDR(epin_desc); |
---|
| 548 | + if (epin_desc_comp) |
---|
| 549 | + headers[i++] = USBDHDR(epin_desc_comp); |
---|
| 550 | + headers[i++] = USBDHDR(&as_iso_in_desc); |
---|
| 551 | + } |
---|
| 552 | + headers[i] = NULL; |
---|
| 553 | +} |
---|
| 554 | + |
---|
444 | 555 | /* |
---|
445 | 556 | * This function is an ALSA sound card following USB Audio Class Spec 1.0. |
---|
446 | 557 | */ |
---|
447 | | -static void intf_complete(struct usb_ep *ep, struct usb_request *req) |
---|
448 | | -{ |
---|
449 | | - struct f_uac *uac1 = req->context; |
---|
450 | | - int status = req->status; |
---|
451 | | - u32 data = 0; |
---|
452 | | - |
---|
453 | | - switch (status) { |
---|
454 | | - case 0: /* normal completion? */ |
---|
455 | | - if (uac1->set_con) { |
---|
456 | | - memcpy(&data, req->buf, req->length); |
---|
457 | | - uac1->set_con->set(uac1->set_con, uac1->set_cmd, |
---|
458 | | - le16_to_cpu(data)); |
---|
459 | | - uac1->set_con = NULL; |
---|
460 | | - } |
---|
461 | | - break; |
---|
462 | | - default: |
---|
463 | | - break; |
---|
464 | | - } |
---|
465 | | -} |
---|
466 | 558 | |
---|
467 | 559 | static void uac_cs_attr_sample_rate(struct usb_ep *ep, struct usb_request *req) |
---|
468 | 560 | { |
---|
469 | 561 | struct usb_function *fn = ep->driver_data; |
---|
470 | 562 | struct usb_composite_dev *cdev = fn->config->cdev; |
---|
471 | 563 | struct g_audio *agdev = func_to_g_audio(fn); |
---|
472 | | - struct f_uac *uac1 = func_to_uac(fn); |
---|
473 | | - struct f_uac_opts *opts = g_audio_to_uac_opts(agdev); |
---|
| 564 | + struct f_uac1 *uac1 = func_to_uac1(fn); |
---|
| 565 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(agdev); |
---|
474 | 566 | u8 *buf = (u8 *)req->buf; |
---|
475 | 567 | u32 val = 0; |
---|
476 | 568 | |
---|
.. | .. |
---|
480 | 572 | } |
---|
481 | 573 | |
---|
482 | 574 | val = buf[0] | (buf[1] << 8) | (buf[2] << 16); |
---|
483 | | - |
---|
484 | 575 | if (EPIN_EN(opts) && uac1->ctl_id == agdev->in_ep->address) { |
---|
485 | | - opts->p_srate_active = val; |
---|
486 | | - u_audio_set_playback_srate(agdev, opts->p_srate_active); |
---|
| 576 | + uac1->p_srate = val; |
---|
| 577 | + u_audio_set_playback_srate(agdev, uac1->p_srate); |
---|
487 | 578 | } else if (EPOUT_EN(opts) && uac1->ctl_id == agdev->out_ep->address) { |
---|
488 | | - opts->c_srate_active = val; |
---|
489 | | - u_audio_set_capture_srate(agdev, opts->c_srate_active); |
---|
| 579 | + uac1->c_srate = val; |
---|
| 580 | + u_audio_set_capture_srate(agdev, uac1->c_srate); |
---|
490 | 581 | } |
---|
491 | 582 | } |
---|
492 | 583 | |
---|
493 | | -static int audio_set_intf_req(struct usb_function *f, |
---|
494 | | - const struct usb_ctrlrequest *ctrl) |
---|
| 584 | +static void audio_notify_complete(struct usb_ep *_ep, struct usb_request *req) |
---|
495 | 585 | { |
---|
496 | | - struct f_uac *uac1 = func_to_uac(f); |
---|
497 | | - struct usb_composite_dev *cdev = f->config->cdev; |
---|
498 | | - struct usb_request *req = cdev->req; |
---|
499 | | - u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); |
---|
500 | | - u16 len = le16_to_cpu(ctrl->wLength); |
---|
501 | | - u16 w_value = le16_to_cpu(ctrl->wValue); |
---|
502 | | - u8 con_sel = (w_value >> 8) & 0xFF; |
---|
503 | | - u8 cmd = (ctrl->bRequest & 0x0F); |
---|
504 | | - struct usb_audio_control_selector *cs; |
---|
505 | | - struct usb_audio_control *con; |
---|
| 586 | + struct g_audio *audio = req->context; |
---|
| 587 | + struct f_uac1 *uac1 = func_to_uac1(&audio->func); |
---|
506 | 588 | |
---|
507 | | - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", |
---|
508 | | - ctrl->bRequest, w_value, len, id); |
---|
509 | | - |
---|
510 | | - list_for_each_entry(cs, &uac1->cs, list) { |
---|
511 | | - if (cs->id == id) { |
---|
512 | | - list_for_each_entry(con, &cs->control, list) { |
---|
513 | | - if (con->type == con_sel) { |
---|
514 | | - uac1->set_con = con; |
---|
515 | | - break; |
---|
516 | | - } |
---|
517 | | - } |
---|
518 | | - break; |
---|
519 | | - } |
---|
520 | | - } |
---|
521 | | - |
---|
522 | | - uac1->set_cmd = cmd; |
---|
523 | | - req->context = uac1; |
---|
524 | | - req->complete = intf_complete; |
---|
525 | | - |
---|
526 | | - return len; |
---|
| 589 | + atomic_dec(&uac1->int_count); |
---|
| 590 | + kfree(req->buf); |
---|
| 591 | + usb_ep_free_request(_ep, req); |
---|
527 | 592 | } |
---|
528 | 593 | |
---|
529 | | -static int audio_get_intf_req(struct usb_function *f, |
---|
530 | | - const struct usb_ctrlrequest *ctrl) |
---|
| 594 | +static int audio_notify(struct g_audio *audio, int unit_id, int cs) |
---|
531 | 595 | { |
---|
532 | | - struct f_uac *uac1 = func_to_uac(f); |
---|
533 | | - struct usb_composite_dev *cdev = f->config->cdev; |
---|
534 | | - struct usb_request *req = cdev->req; |
---|
535 | | - int value = -EOPNOTSUPP; |
---|
536 | | - u8 id = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); |
---|
537 | | - u16 len = le16_to_cpu(ctrl->wLength); |
---|
538 | | - u16 w_value = le16_to_cpu(ctrl->wValue); |
---|
539 | | - u8 con_sel = (w_value >> 8) & 0xFF; |
---|
540 | | - u8 cmd = (ctrl->bRequest & 0x0F); |
---|
541 | | - struct usb_audio_control_selector *cs; |
---|
542 | | - struct usb_audio_control *con; |
---|
| 596 | + struct f_uac1 *uac1 = func_to_uac1(&audio->func); |
---|
| 597 | + struct usb_request *req; |
---|
| 598 | + struct uac1_status_word *msg; |
---|
| 599 | + int ret; |
---|
543 | 600 | |
---|
544 | | - DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, entity %d\n", |
---|
545 | | - ctrl->bRequest, w_value, len, id); |
---|
| 601 | + if (!uac1->int_ep->enabled) |
---|
| 602 | + return 0; |
---|
546 | 603 | |
---|
547 | | - list_for_each_entry(cs, &uac1->cs, list) { |
---|
548 | | - if (cs->id == id) { |
---|
549 | | - list_for_each_entry(con, &cs->control, list) { |
---|
550 | | - if (con->type == con_sel && con->get) { |
---|
551 | | - value = con->get(con, cmd); |
---|
552 | | - break; |
---|
553 | | - } |
---|
554 | | - } |
---|
555 | | - break; |
---|
556 | | - } |
---|
| 604 | + if (atomic_inc_return(&uac1->int_count) > UAC1_DEF_INT_REQ_NUM) { |
---|
| 605 | + atomic_dec(&uac1->int_count); |
---|
| 606 | + return 0; |
---|
557 | 607 | } |
---|
558 | 608 | |
---|
559 | | - req->context = uac1; |
---|
560 | | - req->complete = intf_complete; |
---|
561 | | - len = min_t(size_t, sizeof(value), len); |
---|
562 | | - memcpy(req->buf, &value, len); |
---|
| 609 | + req = usb_ep_alloc_request(uac1->int_ep, GFP_ATOMIC); |
---|
| 610 | + if (req == NULL) { |
---|
| 611 | + ret = -ENOMEM; |
---|
| 612 | + goto err_dec_int_count; |
---|
| 613 | + } |
---|
563 | 614 | |
---|
564 | | - return len; |
---|
| 615 | + msg = kmalloc(sizeof(*msg), GFP_ATOMIC); |
---|
| 616 | + if (msg == NULL) { |
---|
| 617 | + ret = -ENOMEM; |
---|
| 618 | + goto err_free_request; |
---|
| 619 | + } |
---|
| 620 | + |
---|
| 621 | + msg->bStatusType = UAC1_STATUS_TYPE_IRQ_PENDING |
---|
| 622 | + | UAC1_STATUS_TYPE_ORIG_AUDIO_CONTROL_IF; |
---|
| 623 | + msg->bOriginator = unit_id; |
---|
| 624 | + |
---|
| 625 | + req->length = sizeof(*msg); |
---|
| 626 | + req->buf = msg; |
---|
| 627 | + req->context = audio; |
---|
| 628 | + req->complete = audio_notify_complete; |
---|
| 629 | + |
---|
| 630 | + ret = usb_ep_queue(uac1->int_ep, req, GFP_ATOMIC); |
---|
| 631 | + |
---|
| 632 | + if (ret) |
---|
| 633 | + goto err_free_msg; |
---|
| 634 | + |
---|
| 635 | + return 0; |
---|
| 636 | + |
---|
| 637 | +err_free_msg: |
---|
| 638 | + kfree(msg); |
---|
| 639 | +err_free_request: |
---|
| 640 | + usb_ep_free_request(uac1->int_ep, req); |
---|
| 641 | +err_dec_int_count: |
---|
| 642 | + atomic_dec(&uac1->int_count); |
---|
| 643 | + |
---|
| 644 | + return ret; |
---|
| 645 | +} |
---|
| 646 | + |
---|
| 647 | +static int |
---|
| 648 | +in_rq_cur(struct usb_function *fn, const struct usb_ctrlrequest *cr) |
---|
| 649 | +{ |
---|
| 650 | + struct usb_request *req = fn->config->cdev->req; |
---|
| 651 | + struct g_audio *audio = func_to_g_audio(fn); |
---|
| 652 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); |
---|
| 653 | + u16 w_length = le16_to_cpu(cr->wLength); |
---|
| 654 | + u16 w_index = le16_to_cpu(cr->wIndex); |
---|
| 655 | + u16 w_value = le16_to_cpu(cr->wValue); |
---|
| 656 | + u8 entity_id = (w_index >> 8) & 0xff; |
---|
| 657 | + u8 control_selector = w_value >> 8; |
---|
| 658 | + int value = -EOPNOTSUPP; |
---|
| 659 | + |
---|
| 660 | + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || |
---|
| 661 | + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { |
---|
| 662 | + unsigned int is_playback = 0; |
---|
| 663 | + |
---|
| 664 | + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) |
---|
| 665 | + is_playback = 1; |
---|
| 666 | + |
---|
| 667 | + if (control_selector == UAC_FU_MUTE) { |
---|
| 668 | + unsigned int mute; |
---|
| 669 | + |
---|
| 670 | + u_audio_get_mute(audio, is_playback, &mute); |
---|
| 671 | + |
---|
| 672 | + *(u8 *)req->buf = mute; |
---|
| 673 | + value = min_t(unsigned int, w_length, 1); |
---|
| 674 | + } else if (control_selector == UAC_FU_VOLUME) { |
---|
| 675 | + __le16 c; |
---|
| 676 | + s16 volume; |
---|
| 677 | + |
---|
| 678 | + u_audio_get_volume(audio, is_playback, &volume); |
---|
| 679 | + |
---|
| 680 | + c = cpu_to_le16(volume); |
---|
| 681 | + |
---|
| 682 | + value = min_t(unsigned int, w_length, sizeof(c)); |
---|
| 683 | + memcpy(req->buf, &c, value); |
---|
| 684 | + } else { |
---|
| 685 | + dev_err(&audio->gadget->dev, |
---|
| 686 | + "%s:%d control_selector=%d TODO!\n", |
---|
| 687 | + __func__, __LINE__, control_selector); |
---|
| 688 | + } |
---|
| 689 | + } else { |
---|
| 690 | + dev_err(&audio->gadget->dev, |
---|
| 691 | + "%s:%d entity_id=%d control_selector=%d TODO!\n", |
---|
| 692 | + __func__, __LINE__, entity_id, control_selector); |
---|
| 693 | + } |
---|
| 694 | + |
---|
| 695 | + return value; |
---|
| 696 | +} |
---|
| 697 | + |
---|
| 698 | +static int |
---|
| 699 | +in_rq_min(struct usb_function *fn, const struct usb_ctrlrequest *cr) |
---|
| 700 | +{ |
---|
| 701 | + struct usb_request *req = fn->config->cdev->req; |
---|
| 702 | + struct g_audio *audio = func_to_g_audio(fn); |
---|
| 703 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); |
---|
| 704 | + u16 w_length = le16_to_cpu(cr->wLength); |
---|
| 705 | + u16 w_index = le16_to_cpu(cr->wIndex); |
---|
| 706 | + u16 w_value = le16_to_cpu(cr->wValue); |
---|
| 707 | + u8 entity_id = (w_index >> 8) & 0xff; |
---|
| 708 | + u8 control_selector = w_value >> 8; |
---|
| 709 | + int value = -EOPNOTSUPP; |
---|
| 710 | + |
---|
| 711 | + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || |
---|
| 712 | + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { |
---|
| 713 | + unsigned int is_playback = 0; |
---|
| 714 | + |
---|
| 715 | + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) |
---|
| 716 | + is_playback = 1; |
---|
| 717 | + |
---|
| 718 | + if (control_selector == UAC_FU_VOLUME) { |
---|
| 719 | + __le16 r; |
---|
| 720 | + s16 min_db; |
---|
| 721 | + |
---|
| 722 | + if (is_playback) |
---|
| 723 | + min_db = opts->p_volume_min; |
---|
| 724 | + else |
---|
| 725 | + min_db = opts->c_volume_min; |
---|
| 726 | + |
---|
| 727 | + r = cpu_to_le16(min_db); |
---|
| 728 | + |
---|
| 729 | + value = min_t(unsigned int, w_length, sizeof(r)); |
---|
| 730 | + memcpy(req->buf, &r, value); |
---|
| 731 | + } else { |
---|
| 732 | + dev_err(&audio->gadget->dev, |
---|
| 733 | + "%s:%d control_selector=%d TODO!\n", |
---|
| 734 | + __func__, __LINE__, control_selector); |
---|
| 735 | + } |
---|
| 736 | + } else { |
---|
| 737 | + dev_err(&audio->gadget->dev, |
---|
| 738 | + "%s:%d entity_id=%d control_selector=%d TODO!\n", |
---|
| 739 | + __func__, __LINE__, entity_id, control_selector); |
---|
| 740 | + } |
---|
| 741 | + |
---|
| 742 | + return value; |
---|
| 743 | +} |
---|
| 744 | + |
---|
| 745 | +static int |
---|
| 746 | +in_rq_max(struct usb_function *fn, const struct usb_ctrlrequest *cr) |
---|
| 747 | +{ |
---|
| 748 | + struct usb_request *req = fn->config->cdev->req; |
---|
| 749 | + struct g_audio *audio = func_to_g_audio(fn); |
---|
| 750 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); |
---|
| 751 | + u16 w_length = le16_to_cpu(cr->wLength); |
---|
| 752 | + u16 w_index = le16_to_cpu(cr->wIndex); |
---|
| 753 | + u16 w_value = le16_to_cpu(cr->wValue); |
---|
| 754 | + u8 entity_id = (w_index >> 8) & 0xff; |
---|
| 755 | + u8 control_selector = w_value >> 8; |
---|
| 756 | + int value = -EOPNOTSUPP; |
---|
| 757 | + |
---|
| 758 | + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || |
---|
| 759 | + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { |
---|
| 760 | + unsigned int is_playback = 0; |
---|
| 761 | + |
---|
| 762 | + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) |
---|
| 763 | + is_playback = 1; |
---|
| 764 | + |
---|
| 765 | + if (control_selector == UAC_FU_VOLUME) { |
---|
| 766 | + __le16 r; |
---|
| 767 | + s16 max_db; |
---|
| 768 | + |
---|
| 769 | + if (is_playback) |
---|
| 770 | + max_db = opts->p_volume_max; |
---|
| 771 | + else |
---|
| 772 | + max_db = opts->c_volume_max; |
---|
| 773 | + |
---|
| 774 | + r = cpu_to_le16(max_db); |
---|
| 775 | + |
---|
| 776 | + value = min_t(unsigned int, w_length, sizeof(r)); |
---|
| 777 | + memcpy(req->buf, &r, value); |
---|
| 778 | + } else { |
---|
| 779 | + dev_err(&audio->gadget->dev, |
---|
| 780 | + "%s:%d control_selector=%d TODO!\n", |
---|
| 781 | + __func__, __LINE__, control_selector); |
---|
| 782 | + } |
---|
| 783 | + } else { |
---|
| 784 | + dev_err(&audio->gadget->dev, |
---|
| 785 | + "%s:%d entity_id=%d control_selector=%d TODO!\n", |
---|
| 786 | + __func__, __LINE__, entity_id, control_selector); |
---|
| 787 | + } |
---|
| 788 | + |
---|
| 789 | + return value; |
---|
| 790 | +} |
---|
| 791 | + |
---|
| 792 | +static int |
---|
| 793 | +in_rq_res(struct usb_function *fn, const struct usb_ctrlrequest *cr) |
---|
| 794 | +{ |
---|
| 795 | + struct usb_request *req = fn->config->cdev->req; |
---|
| 796 | + struct g_audio *audio = func_to_g_audio(fn); |
---|
| 797 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); |
---|
| 798 | + u16 w_length = le16_to_cpu(cr->wLength); |
---|
| 799 | + u16 w_index = le16_to_cpu(cr->wIndex); |
---|
| 800 | + u16 w_value = le16_to_cpu(cr->wValue); |
---|
| 801 | + u8 entity_id = (w_index >> 8) & 0xff; |
---|
| 802 | + u8 control_selector = w_value >> 8; |
---|
| 803 | + int value = -EOPNOTSUPP; |
---|
| 804 | + |
---|
| 805 | + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || |
---|
| 806 | + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { |
---|
| 807 | + unsigned int is_playback = 0; |
---|
| 808 | + |
---|
| 809 | + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) |
---|
| 810 | + is_playback = 1; |
---|
| 811 | + |
---|
| 812 | + if (control_selector == UAC_FU_VOLUME) { |
---|
| 813 | + __le16 r; |
---|
| 814 | + s16 res_db; |
---|
| 815 | + |
---|
| 816 | + if (is_playback) |
---|
| 817 | + res_db = opts->p_volume_res; |
---|
| 818 | + else |
---|
| 819 | + res_db = opts->c_volume_res; |
---|
| 820 | + |
---|
| 821 | + r = cpu_to_le16(res_db); |
---|
| 822 | + |
---|
| 823 | + value = min_t(unsigned int, w_length, sizeof(r)); |
---|
| 824 | + memcpy(req->buf, &r, value); |
---|
| 825 | + } else { |
---|
| 826 | + dev_err(&audio->gadget->dev, |
---|
| 827 | + "%s:%d control_selector=%d TODO!\n", |
---|
| 828 | + __func__, __LINE__, control_selector); |
---|
| 829 | + } |
---|
| 830 | + } else { |
---|
| 831 | + dev_err(&audio->gadget->dev, |
---|
| 832 | + "%s:%d entity_id=%d control_selector=%d TODO!\n", |
---|
| 833 | + __func__, __LINE__, entity_id, control_selector); |
---|
| 834 | + } |
---|
| 835 | + |
---|
| 836 | + return value; |
---|
| 837 | +} |
---|
| 838 | + |
---|
| 839 | +static void |
---|
| 840 | +out_rq_complete(struct usb_ep *ep, struct usb_request *req) |
---|
| 841 | +{ |
---|
| 842 | + struct g_audio *audio = req->context; |
---|
| 843 | + struct usb_composite_dev *cdev = audio->func.config->cdev; |
---|
| 844 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); |
---|
| 845 | + struct f_uac1 *uac1 = func_to_uac1(&audio->func); |
---|
| 846 | + struct usb_ctrlrequest *cr = &uac1->setup_cr; |
---|
| 847 | + u16 w_index = le16_to_cpu(cr->wIndex); |
---|
| 848 | + u16 w_value = le16_to_cpu(cr->wValue); |
---|
| 849 | + u8 entity_id = (w_index >> 8) & 0xff; |
---|
| 850 | + u8 control_selector = w_value >> 8; |
---|
| 851 | + |
---|
| 852 | + if (req->status != 0) { |
---|
| 853 | + dev_dbg(&cdev->gadget->dev, "completion err %d\n", req->status); |
---|
| 854 | + return; |
---|
| 855 | + } |
---|
| 856 | + |
---|
| 857 | + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || |
---|
| 858 | + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { |
---|
| 859 | + unsigned int is_playback = 0; |
---|
| 860 | + |
---|
| 861 | + if (FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) |
---|
| 862 | + is_playback = 1; |
---|
| 863 | + |
---|
| 864 | + if (control_selector == UAC_FU_MUTE) { |
---|
| 865 | + if (cr->bRequest == UAC_SET_CUR) { |
---|
| 866 | + u8 mute = *(u8 *)req->buf; |
---|
| 867 | + |
---|
| 868 | + u_audio_set_mute(audio, is_playback, mute); |
---|
| 869 | + } |
---|
| 870 | + |
---|
| 871 | + return; |
---|
| 872 | + } else if (control_selector == UAC_FU_VOLUME) { |
---|
| 873 | + __le16 *c = req->buf; |
---|
| 874 | + s16 volume; |
---|
| 875 | + |
---|
| 876 | + volume = le16_to_cpu(*c); |
---|
| 877 | + |
---|
| 878 | + switch (cr->bRequest) { |
---|
| 879 | + case UAC_SET_CUR: |
---|
| 880 | + u_audio_set_volume(audio, is_playback, volume); |
---|
| 881 | + break; |
---|
| 882 | + case UAC_SET_MIN: |
---|
| 883 | + if (is_playback) |
---|
| 884 | + opts->p_volume_min = volume; |
---|
| 885 | + else |
---|
| 886 | + opts->c_volume_min = volume; |
---|
| 887 | + break; |
---|
| 888 | + case UAC_SET_MAX: |
---|
| 889 | + if (is_playback) |
---|
| 890 | + opts->p_volume_max = volume; |
---|
| 891 | + else |
---|
| 892 | + opts->c_volume_max = volume; |
---|
| 893 | + break; |
---|
| 894 | + case UAC_SET_RES: |
---|
| 895 | + if (is_playback) |
---|
| 896 | + opts->p_volume_res = volume; |
---|
| 897 | + else |
---|
| 898 | + opts->c_volume_res = volume; |
---|
| 899 | + break; |
---|
| 900 | + case UAC_SET_MEM: |
---|
| 901 | + break; |
---|
| 902 | + default: |
---|
| 903 | + break; |
---|
| 904 | + } |
---|
| 905 | + |
---|
| 906 | + return; |
---|
| 907 | + } else { |
---|
| 908 | + dev_err(&audio->gadget->dev, |
---|
| 909 | + "%s:%d control_selector=%d TODO!\n", |
---|
| 910 | + __func__, __LINE__, control_selector); |
---|
| 911 | + usb_ep_set_halt(ep); |
---|
| 912 | + } |
---|
| 913 | + } else { |
---|
| 914 | + dev_err(&audio->gadget->dev, |
---|
| 915 | + "%s:%d entity_id=%d control_selector=%d TODO!\n", |
---|
| 916 | + __func__, __LINE__, entity_id, control_selector); |
---|
| 917 | + usb_ep_set_halt(ep); |
---|
| 918 | + |
---|
| 919 | + } |
---|
| 920 | +} |
---|
| 921 | + |
---|
| 922 | +static int |
---|
| 923 | +ac_rq_out(struct usb_function *fn, const struct usb_ctrlrequest *cr) |
---|
| 924 | +{ |
---|
| 925 | + struct usb_request *req = fn->config->cdev->req; |
---|
| 926 | + struct g_audio *audio = func_to_g_audio(fn); |
---|
| 927 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); |
---|
| 928 | + struct f_uac1 *uac1 = func_to_uac1(&audio->func); |
---|
| 929 | + u16 w_length = le16_to_cpu(cr->wLength); |
---|
| 930 | + u16 w_index = le16_to_cpu(cr->wIndex); |
---|
| 931 | + u16 w_value = le16_to_cpu(cr->wValue); |
---|
| 932 | + u8 entity_id = (w_index >> 8) & 0xff; |
---|
| 933 | + u8 control_selector = w_value >> 8; |
---|
| 934 | + |
---|
| 935 | + if ((FUIN_EN(opts) && (entity_id == USB_IN_FU_ID)) || |
---|
| 936 | + (FUOUT_EN(opts) && (entity_id == USB_OUT_FU_ID))) { |
---|
| 937 | + memcpy(&uac1->setup_cr, cr, sizeof(*cr)); |
---|
| 938 | + req->context = audio; |
---|
| 939 | + req->complete = out_rq_complete; |
---|
| 940 | + |
---|
| 941 | + return w_length; |
---|
| 942 | + } else { |
---|
| 943 | + dev_err(&audio->gadget->dev, |
---|
| 944 | + "%s:%d entity_id=%d control_selector=%d TODO!\n", |
---|
| 945 | + __func__, __LINE__, entity_id, control_selector); |
---|
| 946 | + } |
---|
| 947 | + return -EOPNOTSUPP; |
---|
| 948 | +} |
---|
| 949 | + |
---|
| 950 | +static int ac_rq_in(struct usb_function *f, |
---|
| 951 | + const struct usb_ctrlrequest *ctrl) |
---|
| 952 | +{ |
---|
| 953 | + struct usb_composite_dev *cdev = f->config->cdev; |
---|
| 954 | + int value = -EOPNOTSUPP; |
---|
| 955 | + u8 ep = ((le16_to_cpu(ctrl->wIndex) >> 8) & 0xFF); |
---|
| 956 | + u16 len = le16_to_cpu(ctrl->wLength); |
---|
| 957 | + u16 w_value = le16_to_cpu(ctrl->wValue); |
---|
| 958 | + |
---|
| 959 | + DBG(cdev, "bRequest 0x%x, w_value 0x%04x, len %d, endpoint %d\n", |
---|
| 960 | + ctrl->bRequest, w_value, len, ep); |
---|
| 961 | + |
---|
| 962 | + switch (ctrl->bRequest) { |
---|
| 963 | + case UAC_GET_CUR: |
---|
| 964 | + return in_rq_cur(f, ctrl); |
---|
| 965 | + case UAC_GET_MIN: |
---|
| 966 | + return in_rq_min(f, ctrl); |
---|
| 967 | + case UAC_GET_MAX: |
---|
| 968 | + return in_rq_max(f, ctrl); |
---|
| 969 | + case UAC_GET_RES: |
---|
| 970 | + return in_rq_res(f, ctrl); |
---|
| 971 | + case UAC_GET_MEM: |
---|
| 972 | + break; |
---|
| 973 | + case UAC_GET_STAT: |
---|
| 974 | + value = len; |
---|
| 975 | + break; |
---|
| 976 | + default: |
---|
| 977 | + break; |
---|
| 978 | + } |
---|
| 979 | + |
---|
| 980 | + return value; |
---|
565 | 981 | } |
---|
566 | 982 | |
---|
567 | 983 | static int audio_set_endpoint_req(struct usb_function *f, |
---|
.. | .. |
---|
569 | 985 | { |
---|
570 | 986 | struct usb_composite_dev *cdev = f->config->cdev; |
---|
571 | 987 | struct usb_request *req = f->config->cdev->req; |
---|
572 | | - struct f_uac *uac1 = func_to_uac(f); |
---|
| 988 | + struct f_uac1 *uac1 = func_to_uac1(f); |
---|
573 | 989 | int value = -EOPNOTSUPP; |
---|
574 | | - u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff; |
---|
| 990 | + u16 ep = le16_to_cpu(ctrl->wIndex); |
---|
575 | 991 | u16 len = le16_to_cpu(ctrl->wLength); |
---|
576 | 992 | u16 w_value = le16_to_cpu(ctrl->wValue); |
---|
577 | 993 | u8 cs = w_value >> 8; |
---|
.. | .. |
---|
588 | 1004 | } |
---|
589 | 1005 | value = len; |
---|
590 | 1006 | break; |
---|
591 | | - } |
---|
| 1007 | + } |
---|
| 1008 | + |
---|
592 | 1009 | case UAC_SET_MIN: |
---|
593 | 1010 | break; |
---|
594 | 1011 | |
---|
.. | .. |
---|
613 | 1030 | { |
---|
614 | 1031 | struct usb_composite_dev *cdev = f->config->cdev; |
---|
615 | 1032 | struct usb_request *req = f->config->cdev->req; |
---|
| 1033 | + struct f_uac1 *uac1 = func_to_uac1(f); |
---|
616 | 1034 | struct g_audio *agdev = func_to_g_audio(f); |
---|
617 | | - struct f_uac_opts *opts = g_audio_to_uac_opts(agdev); |
---|
| 1035 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(agdev); |
---|
618 | 1036 | u8 *buf = (u8 *)req->buf; |
---|
619 | 1037 | int value = -EOPNOTSUPP; |
---|
620 | | - u8 ep = le16_to_cpu(ctrl->wIndex) & 0xff; |
---|
| 1038 | + u8 ep = le16_to_cpu(ctrl->wIndex); |
---|
621 | 1039 | u16 len = le16_to_cpu(ctrl->wLength); |
---|
622 | 1040 | u16 w_value = le16_to_cpu(ctrl->wValue); |
---|
623 | 1041 | u8 cs = w_value >> 8; |
---|
.. | .. |
---|
630 | 1048 | case UAC_GET_CUR: { |
---|
631 | 1049 | if (cs == UAC_EP_CS_ATTR_SAMPLE_RATE) { |
---|
632 | 1050 | if (EPIN_EN(opts) && ep == agdev->in_ep->address) |
---|
633 | | - val = opts->p_srate_active; |
---|
| 1051 | + val = uac1->p_srate; |
---|
634 | 1052 | else if (EPOUT_EN(opts) && ep == agdev->out_ep->address) |
---|
635 | | - val = opts->c_srate_active; |
---|
| 1053 | + val = uac1->c_srate; |
---|
636 | 1054 | buf[2] = (val >> 16) & 0xff; |
---|
637 | 1055 | buf[1] = (val >> 8) & 0xff; |
---|
638 | 1056 | buf[0] = val & 0xff; |
---|
639 | 1057 | } |
---|
640 | 1058 | value = len; |
---|
641 | 1059 | break; |
---|
642 | | - } |
---|
| 1060 | + } |
---|
643 | 1061 | case UAC_GET_MIN: |
---|
644 | 1062 | case UAC_GET_MAX: |
---|
645 | 1063 | case UAC_GET_RES: |
---|
.. | .. |
---|
668 | 1086 | * activation uses set_alt(). |
---|
669 | 1087 | */ |
---|
670 | 1088 | switch (ctrl->bRequestType) { |
---|
671 | | - case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: |
---|
672 | | - value = audio_set_intf_req(f, ctrl); |
---|
673 | | - break; |
---|
674 | | - |
---|
675 | | - case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: |
---|
676 | | - value = audio_get_intf_req(f, ctrl); |
---|
677 | | - break; |
---|
678 | | - |
---|
679 | 1089 | case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: |
---|
680 | 1090 | value = audio_set_endpoint_req(f, ctrl); |
---|
681 | 1091 | break; |
---|
.. | .. |
---|
683 | 1093 | case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_ENDPOINT: |
---|
684 | 1094 | value = audio_get_endpoint_req(f, ctrl); |
---|
685 | 1095 | break; |
---|
686 | | - |
---|
| 1096 | + case USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE: |
---|
| 1097 | + value = ac_rq_out(f, ctrl); |
---|
| 1098 | + break; |
---|
| 1099 | + case USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE: |
---|
| 1100 | + value = ac_rq_in(f, ctrl); |
---|
| 1101 | + break; |
---|
687 | 1102 | default: |
---|
688 | 1103 | ERROR(cdev, "invalid control req%02x.%02x v%04x i%04x l%d\n", |
---|
689 | 1104 | ctrl->bRequestType, ctrl->bRequest, |
---|
.. | .. |
---|
711 | 1126 | struct usb_composite_dev *cdev = f->config->cdev; |
---|
712 | 1127 | struct usb_gadget *gadget = cdev->gadget; |
---|
713 | 1128 | struct device *dev = &gadget->dev; |
---|
714 | | - struct f_uac *uac1 = func_to_uac(f); |
---|
| 1129 | + struct g_audio *audio = func_to_g_audio(f); |
---|
| 1130 | + struct f_uac1 *uac1 = func_to_uac1(f); |
---|
715 | 1131 | int ret = 0; |
---|
716 | 1132 | |
---|
717 | 1133 | /* No i/f has more than 2 alt settings */ |
---|
.. | .. |
---|
726 | 1142 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
---|
727 | 1143 | return -EINVAL; |
---|
728 | 1144 | } |
---|
| 1145 | + |
---|
| 1146 | + /* restart interrupt endpoint */ |
---|
| 1147 | + if (uac1->int_ep) { |
---|
| 1148 | + usb_ep_disable(uac1->int_ep); |
---|
| 1149 | + config_ep_by_speed(gadget, &audio->func, uac1->int_ep); |
---|
| 1150 | + usb_ep_enable(uac1->int_ep); |
---|
| 1151 | + } |
---|
| 1152 | + |
---|
729 | 1153 | return 0; |
---|
730 | 1154 | } |
---|
731 | 1155 | |
---|
.. | .. |
---|
739 | 1163 | } else if (intf == uac1->as_in_intf) { |
---|
740 | 1164 | uac1->as_in_alt = alt; |
---|
741 | 1165 | |
---|
742 | | - if (alt) |
---|
743 | | - ret = u_audio_start_playback(&uac1->g_audio); |
---|
744 | | - else |
---|
745 | | - u_audio_stop_playback(&uac1->g_audio); |
---|
| 1166 | + if (alt) |
---|
| 1167 | + ret = u_audio_start_playback(&uac1->g_audio); |
---|
| 1168 | + else |
---|
| 1169 | + u_audio_stop_playback(&uac1->g_audio); |
---|
746 | 1170 | } else { |
---|
747 | 1171 | dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
---|
748 | 1172 | return -EINVAL; |
---|
.. | .. |
---|
756 | 1180 | struct usb_composite_dev *cdev = f->config->cdev; |
---|
757 | 1181 | struct usb_gadget *gadget = cdev->gadget; |
---|
758 | 1182 | struct device *dev = &gadget->dev; |
---|
759 | | - struct f_uac *uac1 = func_to_uac(f); |
---|
| 1183 | + struct f_uac1 *uac1 = func_to_uac1(f); |
---|
760 | 1184 | |
---|
761 | 1185 | if (intf == uac1->ac_intf) |
---|
762 | 1186 | return uac1->ac_alt; |
---|
.. | .. |
---|
774 | 1198 | |
---|
775 | 1199 | static void f_audio_disable(struct usb_function *f) |
---|
776 | 1200 | { |
---|
777 | | - struct f_uac *uac1 = func_to_uac(f); |
---|
| 1201 | + struct f_uac1 *uac1 = func_to_uac1(f); |
---|
778 | 1202 | |
---|
779 | 1203 | uac1->as_out_alt = 0; |
---|
780 | 1204 | uac1->as_in_alt = 0; |
---|
781 | 1205 | |
---|
782 | 1206 | u_audio_stop_playback(&uac1->g_audio); |
---|
783 | 1207 | u_audio_stop_capture(&uac1->g_audio); |
---|
| 1208 | + if (uac1->int_ep) |
---|
| 1209 | + usb_ep_disable(uac1->int_ep); |
---|
| 1210 | +} |
---|
| 1211 | + |
---|
| 1212 | +static void |
---|
| 1213 | +f_audio_suspend(struct usb_function *f) |
---|
| 1214 | +{ |
---|
| 1215 | + struct f_uac1 *uac1 = func_to_uac1(f); |
---|
| 1216 | + |
---|
| 1217 | + u_audio_suspend(&uac1->g_audio); |
---|
784 | 1218 | } |
---|
785 | 1219 | |
---|
786 | 1220 | /*-------------------------------------------------------------------------*/ |
---|
787 | | -#define USBDHDR(p) (struct usb_descriptor_header *)(p) |
---|
788 | | - |
---|
789 | | -static void setup_headers(struct f_uac_opts *opts, |
---|
790 | | - struct usb_descriptor_header **headers, |
---|
791 | | - enum usb_device_speed speed) |
---|
| 1221 | +static struct uac_feature_unit_descriptor *build_fu_desc(int chmask) |
---|
792 | 1222 | { |
---|
793 | | - struct usb_ss_ep_comp_descriptor *epout_desc_comp = NULL; |
---|
794 | | - struct usb_ss_ep_comp_descriptor *epin_desc_comp = NULL; |
---|
795 | | - struct usb_endpoint_descriptor *epout_desc; |
---|
796 | | - struct usb_endpoint_descriptor *epin_desc; |
---|
797 | | - int i; |
---|
| 1223 | + struct uac_feature_unit_descriptor *fu_desc; |
---|
| 1224 | + int channels = num_channels(chmask); |
---|
| 1225 | + int fu_desc_size = UAC_DT_FEATURE_UNIT_SIZE(channels); |
---|
798 | 1226 | |
---|
799 | | - switch (speed) { |
---|
800 | | - case USB_SPEED_FULL: |
---|
801 | | - /* fall through */ |
---|
802 | | - case USB_SPEED_HIGH: |
---|
803 | | - epout_desc = &as_out_ep_desc; |
---|
804 | | - epin_desc = &as_in_ep_desc; |
---|
805 | | - break; |
---|
806 | | - default: |
---|
807 | | - epout_desc = &ss_out_ep_desc; |
---|
808 | | - epin_desc = &ss_in_ep_desc; |
---|
809 | | - epout_desc_comp = &ss_out_ep_desc_comp; |
---|
810 | | - epin_desc_comp = &ss_in_ep_desc_comp; |
---|
811 | | - } |
---|
| 1227 | + fu_desc = kzalloc(fu_desc_size, GFP_KERNEL); |
---|
| 1228 | + if (!fu_desc) |
---|
| 1229 | + return NULL; |
---|
812 | 1230 | |
---|
813 | | - i = 0; |
---|
814 | | - headers[i++] = USBDHDR(&iad_desc); |
---|
815 | | - headers[i++] = USBDHDR(&ac_interface_desc); |
---|
816 | | - headers[i++] = USBDHDR(&ac_header_desc); |
---|
| 1231 | + fu_desc->bLength = fu_desc_size; |
---|
| 1232 | + fu_desc->bDescriptorType = USB_DT_CS_INTERFACE; |
---|
817 | 1233 | |
---|
818 | | - if (EPOUT_EN(opts)) { |
---|
819 | | - headers[i++] = USBDHDR(&usb_out_it_desc); |
---|
820 | | - if (EPOUT_FU(opts)) |
---|
821 | | - headers[i++] = USBDHDR(&io_out_ot_fu_desc); |
---|
822 | | - headers[i++] = USBDHDR(&io_out_ot_desc); |
---|
823 | | - } |
---|
| 1234 | + fu_desc->bDescriptorSubtype = UAC_FEATURE_UNIT; |
---|
| 1235 | + fu_desc->bControlSize = 2; |
---|
824 | 1236 | |
---|
825 | | - if (EPIN_EN(opts)) { |
---|
826 | | - headers[i++] = USBDHDR(&io_in_it_desc); |
---|
827 | | - if (EPIN_FU(opts)) |
---|
828 | | - headers[i++] = USBDHDR(&usb_in_ot_fu_desc); |
---|
829 | | - headers[i++] = USBDHDR(&usb_in_ot_desc); |
---|
830 | | - } |
---|
| 1237 | + /* bUnitID, bSourceID and bmaControls will be defined later */ |
---|
831 | 1238 | |
---|
832 | | - if (EPOUT_EN(opts)) { |
---|
833 | | - headers[i++] = USBDHDR(&as_out_interface_alt_0_desc); |
---|
834 | | - headers[i++] = USBDHDR(&as_out_interface_alt_1_desc); |
---|
835 | | - headers[i++] = USBDHDR(&as_out_header_desc); |
---|
836 | | - headers[i++] = USBDHDR(&as_out_type_i_desc); |
---|
837 | | - headers[i++] = USBDHDR(epout_desc); |
---|
838 | | - if (epout_desc_comp) |
---|
839 | | - headers[i++] = USBDHDR(epout_desc_comp); |
---|
840 | | - |
---|
841 | | - headers[i++] = USBDHDR(&as_iso_out_desc); |
---|
842 | | - } |
---|
843 | | - |
---|
844 | | - if (EPIN_EN(opts)) { |
---|
845 | | - headers[i++] = USBDHDR(&as_in_interface_alt_0_desc); |
---|
846 | | - headers[i++] = USBDHDR(&as_in_interface_alt_1_desc); |
---|
847 | | - headers[i++] = USBDHDR(&as_in_header_desc); |
---|
848 | | - headers[i++] = USBDHDR(&as_in_type_i_desc); |
---|
849 | | - headers[i++] = USBDHDR(epin_desc); |
---|
850 | | - if (epin_desc_comp) |
---|
851 | | - headers[i++] = USBDHDR(epin_desc_comp); |
---|
852 | | - |
---|
853 | | - headers[i++] = USBDHDR(&as_iso_in_desc); |
---|
854 | | - } |
---|
855 | | - headers[i] = NULL; |
---|
| 1239 | + return fu_desc; |
---|
856 | 1240 | } |
---|
857 | 1241 | |
---|
858 | | -static void setup_descriptor(struct f_uac_opts *opts) |
---|
| 1242 | +/* B.3.2 Class-Specific AC Interface Descriptor */ |
---|
| 1243 | +static struct |
---|
| 1244 | +uac1_ac_header_descriptor *build_ac_header_desc(struct f_uac1_opts *opts) |
---|
859 | 1245 | { |
---|
860 | | - int i = 1; |
---|
861 | | - u16 len = 0; |
---|
| 1246 | + struct uac1_ac_header_descriptor *ac_desc; |
---|
| 1247 | + int ac_header_desc_size; |
---|
| 1248 | + int num_ifaces = 0; |
---|
| 1249 | + |
---|
| 1250 | + if (EPOUT_EN(opts)) |
---|
| 1251 | + num_ifaces++; |
---|
| 1252 | + if (EPIN_EN(opts)) |
---|
| 1253 | + num_ifaces++; |
---|
| 1254 | + |
---|
| 1255 | + ac_header_desc_size = UAC_DT_AC_HEADER_SIZE(num_ifaces); |
---|
| 1256 | + |
---|
| 1257 | + ac_desc = kzalloc(ac_header_desc_size, GFP_KERNEL); |
---|
| 1258 | + if (!ac_desc) |
---|
| 1259 | + return NULL; |
---|
| 1260 | + |
---|
| 1261 | + ac_desc->bLength = ac_header_desc_size; |
---|
| 1262 | + ac_desc->bDescriptorType = USB_DT_CS_INTERFACE; |
---|
| 1263 | + ac_desc->bDescriptorSubtype = UAC_HEADER; |
---|
| 1264 | + ac_desc->bcdADC = cpu_to_le16(0x0100); |
---|
| 1265 | + ac_desc->bInCollection = num_ifaces; |
---|
| 1266 | + |
---|
| 1267 | + /* wTotalLength and baInterfaceNr will be defined later */ |
---|
| 1268 | + |
---|
| 1269 | + return ac_desc; |
---|
| 1270 | +} |
---|
| 1271 | + |
---|
| 1272 | +static void setup_descriptor(struct f_uac1_opts *opts) |
---|
| 1273 | +{ |
---|
| 1274 | + /* patch descriptors */ |
---|
| 1275 | + int i = 1; /* ID's start with 1 */ |
---|
862 | 1276 | |
---|
863 | 1277 | if (EPOUT_EN(opts)) |
---|
864 | 1278 | usb_out_it_desc.bTerminalID = i++; |
---|
865 | 1279 | if (EPIN_EN(opts)) |
---|
866 | 1280 | io_in_it_desc.bTerminalID = i++; |
---|
867 | | - if (EPOUT_EN(opts) && EPOUT_FU(opts)) |
---|
868 | | - io_out_ot_fu_desc.bUnitID = i++; |
---|
869 | | - if (EPIN_EN(opts) && EPIN_FU(opts)) |
---|
870 | | - usb_in_ot_fu_desc.bUnitID = i++; |
---|
871 | 1281 | if (EPOUT_EN(opts)) |
---|
872 | 1282 | io_out_ot_desc.bTerminalID = i++; |
---|
873 | 1283 | if (EPIN_EN(opts)) |
---|
874 | 1284 | usb_in_ot_desc.bTerminalID = i++; |
---|
| 1285 | + if (FUOUT_EN(opts)) |
---|
| 1286 | + out_feature_unit_desc->bUnitID = i++; |
---|
| 1287 | + if (FUIN_EN(opts)) |
---|
| 1288 | + in_feature_unit_desc->bUnitID = i++; |
---|
875 | 1289 | |
---|
876 | | - if (EPIN_FU(opts)) { |
---|
877 | | - usb_in_ot_desc.bSourceID = usb_in_ot_fu_desc.bUnitID; |
---|
878 | | - usb_in_ot_fu_desc.bSourceID = io_in_it_desc.bTerminalID; |
---|
879 | | - p_feature_unit.id = usb_in_ot_fu_desc.bUnitID; |
---|
| 1290 | + if (FUIN_EN(opts)) { |
---|
| 1291 | + usb_in_ot_desc.bSourceID = in_feature_unit_desc->bUnitID; |
---|
| 1292 | + in_feature_unit_desc->bSourceID = io_in_it_desc.bTerminalID; |
---|
880 | 1293 | } else { |
---|
881 | 1294 | usb_in_ot_desc.bSourceID = io_in_it_desc.bTerminalID; |
---|
882 | 1295 | } |
---|
883 | | - if (EPOUT_FU(opts)) { |
---|
884 | | - io_out_ot_desc.bSourceID = io_out_ot_fu_desc.bUnitID; |
---|
885 | | - io_out_ot_fu_desc.bSourceID = usb_out_it_desc.bTerminalID; |
---|
886 | | - c_feature_unit.id = io_out_ot_fu_desc.bUnitID; |
---|
| 1296 | + if (FUOUT_EN(opts)) { |
---|
| 1297 | + io_out_ot_desc.bSourceID = out_feature_unit_desc->bUnitID; |
---|
| 1298 | + out_feature_unit_desc->bSourceID = usb_out_it_desc.bTerminalID; |
---|
887 | 1299 | } else { |
---|
888 | 1300 | io_out_ot_desc.bSourceID = usb_out_it_desc.bTerminalID; |
---|
889 | 1301 | } |
---|
| 1302 | + |
---|
890 | 1303 | as_out_header_desc.bTerminalLink = usb_out_it_desc.bTerminalID; |
---|
891 | 1304 | as_in_header_desc.bTerminalLink = usb_in_ot_desc.bTerminalID; |
---|
892 | 1305 | |
---|
893 | 1306 | iad_desc.bInterfaceCount = 1; |
---|
894 | | - ac_header_desc.bInCollection = 0; |
---|
| 1307 | + ac_header_desc->wTotalLength = cpu_to_le16(ac_header_desc->bLength); |
---|
895 | 1308 | |
---|
896 | 1309 | if (EPIN_EN(opts)) { |
---|
897 | | - len += UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE; |
---|
898 | | - if (EPIN_FU(opts)) |
---|
899 | | - len += UAC_DT_FEATURE_UNIT_SIZE(0); |
---|
900 | | - iad_desc.bInterfaceCount++; |
---|
901 | | - ac_header_desc.bInCollection++; |
---|
902 | | - } |
---|
| 1310 | + u16 len = le16_to_cpu(ac_header_desc->wTotalLength); |
---|
903 | 1311 | |
---|
| 1312 | + len += sizeof(usb_in_ot_desc); |
---|
| 1313 | + len += sizeof(io_in_it_desc); |
---|
| 1314 | + if (FUIN_EN(opts)) |
---|
| 1315 | + len += in_feature_unit_desc->bLength; |
---|
| 1316 | + ac_header_desc->wTotalLength = cpu_to_le16(len); |
---|
| 1317 | + iad_desc.bInterfaceCount++; |
---|
| 1318 | + } |
---|
904 | 1319 | if (EPOUT_EN(opts)) { |
---|
905 | | - len += UAC_DT_INPUT_TERMINAL_SIZE + UAC_DT_OUTPUT_TERMINAL_SIZE; |
---|
906 | | - if (EPOUT_FU(opts)) |
---|
907 | | - len += UAC_DT_FEATURE_UNIT_SIZE(0); |
---|
| 1320 | + u16 len = le16_to_cpu(ac_header_desc->wTotalLength); |
---|
| 1321 | + |
---|
| 1322 | + len += sizeof(usb_out_it_desc); |
---|
| 1323 | + len += sizeof(io_out_ot_desc); |
---|
| 1324 | + if (FUOUT_EN(opts)) |
---|
| 1325 | + len += out_feature_unit_desc->bLength; |
---|
| 1326 | + ac_header_desc->wTotalLength = cpu_to_le16(len); |
---|
908 | 1327 | iad_desc.bInterfaceCount++; |
---|
909 | | - ac_header_desc.bInCollection++; |
---|
910 | | - } |
---|
911 | | - ac_header_desc.bLength = |
---|
912 | | - UAC_DT_AC_HEADER_SIZE(ac_header_desc.bInCollection); |
---|
913 | | - ac_header_desc.wTotalLength = cpu_to_le16(len + ac_header_desc.bLength); |
---|
914 | | - |
---|
915 | | - setup_headers(opts, f_audio_desc, USB_SPEED_HIGH); |
---|
916 | | - setup_headers(opts, f_ss_audio_desc, USB_SPEED_SUPER); |
---|
917 | | -} |
---|
918 | | - |
---|
919 | | -static int set_ep_max_packet_size(const struct f_uac_opts *opts, |
---|
920 | | - struct usb_endpoint_descriptor *ep_desc, |
---|
921 | | - enum usb_device_speed speed, bool is_playback) |
---|
922 | | -{ |
---|
923 | | - int chmask, srate = 0, ssize; |
---|
924 | | - u16 max_size_bw, max_size_ep; |
---|
925 | | - unsigned int factor; |
---|
926 | | - int i; |
---|
927 | | - |
---|
928 | | - switch (speed) { |
---|
929 | | - case USB_SPEED_FULL: |
---|
930 | | - max_size_ep = 1023; |
---|
931 | | - factor = 1000; |
---|
932 | | - break; |
---|
933 | | - |
---|
934 | | - case USB_SPEED_HIGH: |
---|
935 | | - /* fall through */ |
---|
936 | | - case USB_SPEED_SUPER: |
---|
937 | | - max_size_ep = 1024; |
---|
938 | | - factor = 8000; |
---|
939 | | - break; |
---|
940 | | - |
---|
941 | | - default: |
---|
942 | | - return -EINVAL; |
---|
943 | 1328 | } |
---|
944 | 1329 | |
---|
945 | | - if (is_playback) { |
---|
946 | | - chmask = opts->p_chmask; |
---|
947 | | - for (i = 0; i < UAC_MAX_RATES; i++) { |
---|
948 | | - if (opts->p_srate[i] == 0) |
---|
949 | | - break; |
---|
950 | | - if (opts->p_srate[i] > srate) |
---|
951 | | - srate = opts->p_srate[i]; |
---|
952 | | - } |
---|
953 | | - ssize = opts->p_ssize; |
---|
954 | | - } else { |
---|
955 | | - chmask = opts->c_chmask; |
---|
956 | | - for (i = 0; i < UAC_MAX_RATES; i++) { |
---|
957 | | - if (opts->c_srate[i] == 0) |
---|
958 | | - break; |
---|
959 | | - if (opts->c_srate[i] > srate) |
---|
960 | | - srate = opts->c_srate[i]; |
---|
961 | | - } |
---|
962 | | - ssize = opts->c_ssize; |
---|
963 | | - } |
---|
964 | | - |
---|
965 | | - max_size_bw = num_channels(chmask) * ssize * |
---|
966 | | - ((srate / (factor / (1 << (ep_desc->bInterval - 1)))) + 1); |
---|
967 | | - ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw, |
---|
968 | | - max_size_ep)); |
---|
969 | | - |
---|
970 | | - return 0; |
---|
| 1330 | + setup_headers(opts, fs_audio_desc, USB_SPEED_FULL); |
---|
| 1331 | + setup_headers(opts, hs_audio_desc, USB_SPEED_HIGH); |
---|
| 1332 | + setup_headers(opts, ss_audio_desc, USB_SPEED_SUPER); |
---|
971 | 1333 | } |
---|
972 | 1334 | |
---|
973 | 1335 | static int f_audio_validate_opts(struct g_audio *audio, struct device *dev) |
---|
974 | 1336 | { |
---|
975 | | - struct f_uac_opts *opts = g_audio_to_uac_opts(audio); |
---|
| 1337 | + struct f_uac1_opts *opts = g_audio_to_uac1_opts(audio); |
---|
976 | 1338 | |
---|
977 | 1339 | if (!opts->p_chmask && !opts->c_chmask) { |
---|
978 | 1340 | dev_err(dev, "Error: no playback and capture channels\n"); |
---|
.. | .. |
---|
989 | 1351 | } else if ((opts->c_ssize < 1) || (opts->c_ssize > 4)) { |
---|
990 | 1352 | dev_err(dev, "Error: incorrect capture sample size\n"); |
---|
991 | 1353 | return -EINVAL; |
---|
992 | | - } else if (!opts->p_srate) { |
---|
| 1354 | + } else if (!opts->p_srates[0]) { |
---|
993 | 1355 | dev_err(dev, "Error: incorrect playback sampling rate\n"); |
---|
994 | 1356 | return -EINVAL; |
---|
995 | | - } else if (!opts->c_srate) { |
---|
| 1357 | + } else if (!opts->c_srates[0]) { |
---|
996 | 1358 | dev_err(dev, "Error: incorrect capture sampling rate\n"); |
---|
997 | 1359 | return -EINVAL; |
---|
998 | 1360 | } |
---|
| 1361 | + |
---|
| 1362 | + if (opts->p_volume_max <= opts->p_volume_min) { |
---|
| 1363 | + dev_err(dev, "Error: incorrect playback volume max/min\n"); |
---|
| 1364 | + return -EINVAL; |
---|
| 1365 | + } else if (opts->c_volume_max <= opts->c_volume_min) { |
---|
| 1366 | + dev_err(dev, "Error: incorrect capture volume max/min\n"); |
---|
| 1367 | + return -EINVAL; |
---|
| 1368 | + } else if (opts->p_volume_res <= 0) { |
---|
| 1369 | + dev_err(dev, "Error: negative/zero playback volume resolution\n"); |
---|
| 1370 | + return -EINVAL; |
---|
| 1371 | + } else if (opts->c_volume_res <= 0) { |
---|
| 1372 | + dev_err(dev, "Error: negative/zero capture volume resolution\n"); |
---|
| 1373 | + return -EINVAL; |
---|
| 1374 | + } |
---|
| 1375 | + |
---|
| 1376 | + if ((opts->p_volume_max - opts->p_volume_min) % opts->p_volume_res) { |
---|
| 1377 | + dev_err(dev, "Error: incorrect playback volume resolution\n"); |
---|
| 1378 | + return -EINVAL; |
---|
| 1379 | + } else if ((opts->c_volume_max - opts->c_volume_min) % opts->c_volume_res) { |
---|
| 1380 | + dev_err(dev, "Error: incorrect capture volume resolution\n"); |
---|
| 1381 | + return -EINVAL; |
---|
| 1382 | + } |
---|
| 1383 | + |
---|
| 1384 | + return 0; |
---|
| 1385 | +} |
---|
| 1386 | + |
---|
| 1387 | +static int set_ep_max_packet_size(const struct f_uac1_opts *opts, |
---|
| 1388 | + struct usb_endpoint_descriptor *ep_desc, |
---|
| 1389 | + enum usb_device_speed speed, bool is_playback) |
---|
| 1390 | +{ |
---|
| 1391 | + int chmask, srate = 0, ssize; |
---|
| 1392 | + u16 max_size_bw, max_size_ep; |
---|
| 1393 | + unsigned int factor; |
---|
| 1394 | + int i; |
---|
| 1395 | + |
---|
| 1396 | + switch (speed) { |
---|
| 1397 | + case USB_SPEED_FULL: |
---|
| 1398 | + max_size_ep = 1023; |
---|
| 1399 | + factor = 1000; |
---|
| 1400 | + break; |
---|
| 1401 | + |
---|
| 1402 | + case USB_SPEED_HIGH: |
---|
| 1403 | + fallthrough; |
---|
| 1404 | + case USB_SPEED_SUPER: |
---|
| 1405 | + max_size_ep = 1024; |
---|
| 1406 | + factor = 8000; |
---|
| 1407 | + break; |
---|
| 1408 | + |
---|
| 1409 | + default: |
---|
| 1410 | + return -EINVAL; |
---|
| 1411 | + } |
---|
| 1412 | + |
---|
| 1413 | + if (is_playback) { |
---|
| 1414 | + chmask = opts->p_chmask; |
---|
| 1415 | + for (i = 0; i < UAC_MAX_RATES; i++) { |
---|
| 1416 | + if (opts->p_srates[i] == 0) |
---|
| 1417 | + break; |
---|
| 1418 | + if (opts->p_srates[i] > srate) |
---|
| 1419 | + srate = opts->p_srates[i]; |
---|
| 1420 | + } |
---|
| 1421 | + ssize = opts->p_ssize; |
---|
| 1422 | + } else { |
---|
| 1423 | + chmask = opts->c_chmask; |
---|
| 1424 | + for (i = 0; i < UAC_MAX_RATES; i++) { |
---|
| 1425 | + if (opts->c_srates[i] == 0) |
---|
| 1426 | + break; |
---|
| 1427 | + if (opts->c_srates[i] > srate) |
---|
| 1428 | + srate = opts->c_srates[i]; |
---|
| 1429 | + } |
---|
| 1430 | + ssize = opts->c_ssize; |
---|
| 1431 | + } |
---|
| 1432 | + |
---|
| 1433 | + max_size_bw = num_channels(chmask) * ssize * |
---|
| 1434 | + ((srate / (factor / (1 << (ep_desc->bInterval - 1)))) + 1); |
---|
| 1435 | + ep_desc->wMaxPacketSize = cpu_to_le16(min_t(u16, max_size_bw, |
---|
| 1436 | + max_size_ep)); |
---|
999 | 1437 | |
---|
1000 | 1438 | return 0; |
---|
1001 | 1439 | } |
---|
.. | .. |
---|
1005 | 1443 | { |
---|
1006 | 1444 | struct usb_composite_dev *cdev = c->cdev; |
---|
1007 | 1445 | struct usb_gadget *gadget = cdev->gadget; |
---|
1008 | | - struct f_uac *uac1 = func_to_uac(f); |
---|
| 1446 | + struct device *dev = &gadget->dev; |
---|
| 1447 | + struct f_uac1 *uac1 = func_to_uac1(f); |
---|
1009 | 1448 | struct g_audio *audio = func_to_g_audio(f); |
---|
1010 | | - struct f_uac_opts *audio_opts; |
---|
| 1449 | + struct f_uac1_opts *audio_opts; |
---|
1011 | 1450 | struct usb_ep *ep = NULL; |
---|
1012 | 1451 | struct usb_string *us; |
---|
1013 | | - struct device *dev = &gadget->dev; |
---|
| 1452 | + int ba_iface_id; |
---|
1014 | 1453 | int status; |
---|
1015 | 1454 | int idx, i; |
---|
1016 | 1455 | |
---|
.. | .. |
---|
1018 | 1457 | if (status) |
---|
1019 | 1458 | return status; |
---|
1020 | 1459 | |
---|
1021 | | - audio_opts = container_of(f->fi, struct f_uac_opts, func_inst); |
---|
| 1460 | + audio_opts = container_of(f->fi, struct f_uac1_opts, func_inst); |
---|
| 1461 | + |
---|
| 1462 | + strings_uac1[STR_ASSOC].s = audio_opts->function_name; |
---|
1022 | 1463 | |
---|
1023 | 1464 | us = usb_gstrings_attach(cdev, uac1_strings, ARRAY_SIZE(strings_uac1)); |
---|
1024 | 1465 | if (IS_ERR(us)) |
---|
1025 | 1466 | return PTR_ERR(us); |
---|
1026 | 1467 | |
---|
| 1468 | + ac_header_desc = build_ac_header_desc(audio_opts); |
---|
| 1469 | + if (!ac_header_desc) |
---|
| 1470 | + return -ENOMEM; |
---|
| 1471 | + |
---|
| 1472 | + if (FUOUT_EN(audio_opts)) { |
---|
| 1473 | + out_feature_unit_desc = build_fu_desc(audio_opts->c_chmask); |
---|
| 1474 | + if (!out_feature_unit_desc) { |
---|
| 1475 | + status = -ENOMEM; |
---|
| 1476 | + goto fail; |
---|
| 1477 | + } |
---|
| 1478 | + } |
---|
| 1479 | + if (FUIN_EN(audio_opts)) { |
---|
| 1480 | + in_feature_unit_desc = build_fu_desc(audio_opts->p_chmask); |
---|
| 1481 | + if (!in_feature_unit_desc) { |
---|
| 1482 | + status = -ENOMEM; |
---|
| 1483 | + goto err_free_fu; |
---|
| 1484 | + } |
---|
| 1485 | + } |
---|
| 1486 | + |
---|
1027 | 1487 | iad_desc.iFunction = us[STR_ASSOC].id; |
---|
1028 | 1488 | ac_interface_desc.iInterface = us[STR_AC_IF].id; |
---|
1029 | 1489 | usb_out_it_desc.iTerminal = us[STR_USB_OUT_IT].id; |
---|
1030 | 1490 | usb_out_it_desc.iChannelNames = us[STR_USB_OUT_IT_CH_NAMES].id; |
---|
1031 | | - io_out_ot_fu_desc.iFeature = us[STR_IO_OUT_OT_FU].id; |
---|
1032 | 1491 | io_out_ot_desc.iTerminal = us[STR_IO_OUT_OT].id; |
---|
1033 | 1492 | as_out_interface_alt_0_desc.iInterface = us[STR_AS_OUT_IF_ALT0].id; |
---|
1034 | 1493 | as_out_interface_alt_1_desc.iInterface = us[STR_AS_OUT_IF_ALT1].id; |
---|
1035 | 1494 | io_in_it_desc.iTerminal = us[STR_IO_IN_IT].id; |
---|
1036 | 1495 | io_in_it_desc.iChannelNames = us[STR_IO_IN_IT_CH_NAMES].id; |
---|
1037 | | - usb_in_ot_fu_desc.iFeature = us[STR_USB_IN_OT_FU].id; |
---|
1038 | 1496 | usb_in_ot_desc.iTerminal = us[STR_USB_IN_OT].id; |
---|
1039 | 1497 | as_in_interface_alt_0_desc.iInterface = us[STR_AS_IN_IF_ALT0].id; |
---|
1040 | 1498 | as_in_interface_alt_1_desc.iInterface = us[STR_AS_IN_IF_ALT1].id; |
---|
| 1499 | + |
---|
| 1500 | + if (FUOUT_EN(audio_opts)) { |
---|
| 1501 | + u8 *i_feature; |
---|
| 1502 | + |
---|
| 1503 | + i_feature = (u8 *)out_feature_unit_desc + |
---|
| 1504 | + out_feature_unit_desc->bLength - 1; |
---|
| 1505 | + *i_feature = us[STR_FU_OUT].id; |
---|
| 1506 | + } |
---|
| 1507 | + if (FUIN_EN(audio_opts)) { |
---|
| 1508 | + u8 *i_feature; |
---|
| 1509 | + |
---|
| 1510 | + i_feature = (u8 *)in_feature_unit_desc + |
---|
| 1511 | + in_feature_unit_desc->bLength - 1; |
---|
| 1512 | + *i_feature = us[STR_FU_IN].id; |
---|
| 1513 | + } |
---|
1041 | 1514 | |
---|
1042 | 1515 | /* Set channel numbers */ |
---|
1043 | 1516 | usb_out_it_desc.bNrChannels = num_channels(audio_opts->c_chmask); |
---|
.. | .. |
---|
1051 | 1524 | as_in_type_i_desc.bSubframeSize = audio_opts->p_ssize; |
---|
1052 | 1525 | as_in_type_i_desc.bBitResolution = audio_opts->p_ssize * 8; |
---|
1053 | 1526 | |
---|
| 1527 | + if (FUOUT_EN(audio_opts)) { |
---|
| 1528 | + __le16 *bma = (__le16 *)&out_feature_unit_desc->bmaControls[0]; |
---|
| 1529 | + u32 control = 0; |
---|
| 1530 | + |
---|
| 1531 | + if (audio_opts->c_mute_present) |
---|
| 1532 | + control |= UAC_FU_MUTE; |
---|
| 1533 | + if (audio_opts->c_volume_present) |
---|
| 1534 | + control |= UAC_FU_VOLUME; |
---|
| 1535 | + *bma = cpu_to_le16(control); |
---|
| 1536 | + } |
---|
| 1537 | + if (FUIN_EN(audio_opts)) { |
---|
| 1538 | + __le16 *bma = (__le16 *)&in_feature_unit_desc->bmaControls[0]; |
---|
| 1539 | + u32 control = 0; |
---|
| 1540 | + |
---|
| 1541 | + if (audio_opts->p_mute_present) |
---|
| 1542 | + control |= UAC_FU_MUTE; |
---|
| 1543 | + if (audio_opts->p_volume_present) |
---|
| 1544 | + control |= UAC_FU_VOLUME; |
---|
| 1545 | + *bma = cpu_to_le16(control); |
---|
| 1546 | + } |
---|
| 1547 | + |
---|
1054 | 1548 | /* Set sample rates */ |
---|
1055 | 1549 | for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) { |
---|
1056 | | - if (audio_opts->c_srate[i] == 0) |
---|
| 1550 | + if (audio_opts->c_srates[i] == 0) |
---|
1057 | 1551 | break; |
---|
1058 | 1552 | memcpy(as_out_type_i_desc.tSamFreq[idx++], |
---|
1059 | | - &audio_opts->c_srate[i], 3); |
---|
| 1553 | + &audio_opts->c_srates[i], 3); |
---|
1060 | 1554 | } |
---|
1061 | 1555 | |
---|
1062 | 1556 | /* |
---|
.. | .. |
---|
1065 | 1559 | * be compatible with larger bandwidth requirements for |
---|
1066 | 1560 | * high speed mode. |
---|
1067 | 1561 | */ |
---|
| 1562 | + status = set_ep_max_packet_size(audio_opts, &fs_out_ep_desc, |
---|
| 1563 | + USB_SPEED_FULL, false); |
---|
| 1564 | + if (status < 0) { |
---|
| 1565 | + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
---|
| 1566 | + goto fail; |
---|
| 1567 | + } |
---|
| 1568 | + |
---|
| 1569 | + status = set_ep_max_packet_size(audio_opts, &fs_in_ep_desc, |
---|
| 1570 | + USB_SPEED_FULL, true); |
---|
| 1571 | + if (status < 0) { |
---|
| 1572 | + dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
---|
| 1573 | + goto fail; |
---|
| 1574 | + } |
---|
| 1575 | + |
---|
1068 | 1576 | status = set_ep_max_packet_size(audio_opts, &as_out_ep_desc, |
---|
1069 | 1577 | USB_SPEED_HIGH, false); |
---|
1070 | 1578 | if (status < 0) { |
---|
.. | .. |
---|
1079 | 1587 | goto fail; |
---|
1080 | 1588 | } |
---|
1081 | 1589 | |
---|
1082 | | - status = set_ep_max_packet_size(audio_opts, &ss_out_ep_desc, |
---|
1083 | | - USB_SPEED_SUPER, false); |
---|
1084 | | - if (status < 0) { |
---|
1085 | | - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
---|
1086 | | - goto fail; |
---|
1087 | | - } |
---|
1088 | | - |
---|
1089 | | - ss_out_ep_desc_comp.wBytesPerInterval = ss_out_ep_desc.wMaxPacketSize; |
---|
1090 | | - |
---|
1091 | | - status = set_ep_max_packet_size(audio_opts, &ss_in_ep_desc, |
---|
1092 | | - USB_SPEED_SUPER, true); |
---|
1093 | | - if (status < 0) { |
---|
1094 | | - dev_err(dev, "%s:%d Error!\n", __func__, __LINE__); |
---|
1095 | | - goto fail; |
---|
1096 | | - } |
---|
1097 | | - |
---|
1098 | | - ss_in_ep_desc_comp.wBytesPerInterval = ss_in_ep_desc.wMaxPacketSize; |
---|
1099 | | - |
---|
| 1590 | + as_out_ep_desc_comp.wBytesPerInterval = as_out_ep_desc.wMaxPacketSize; |
---|
| 1591 | + as_in_ep_desc_comp.wBytesPerInterval = as_in_ep_desc.wMaxPacketSize; |
---|
1100 | 1592 | as_out_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx); |
---|
1101 | 1593 | as_out_type_i_desc.bSamFreqType = idx; |
---|
1102 | 1594 | |
---|
1103 | 1595 | for (i = 0, idx = 0; i < UAC_MAX_RATES; i++) { |
---|
1104 | | - if (audio_opts->p_srate[i] == 0) |
---|
| 1596 | + if (audio_opts->p_srates[i] == 0) |
---|
1105 | 1597 | break; |
---|
1106 | 1598 | memcpy(as_in_type_i_desc.tSamFreq[idx++], |
---|
1107 | | - &audio_opts->p_srate[i], 3); |
---|
| 1599 | + &audio_opts->p_srates[i], 3); |
---|
1108 | 1600 | } |
---|
1109 | 1601 | as_in_type_i_desc.bLength = UAC_FORMAT_TYPE_I_DISCRETE_DESC_SIZE(idx); |
---|
1110 | 1602 | as_in_type_i_desc.bSamFreqType = idx; |
---|
| 1603 | + uac1->p_srate = audio_opts->p_srates[0]; |
---|
| 1604 | + uac1->c_srate = audio_opts->c_srates[0]; |
---|
1111 | 1605 | |
---|
1112 | 1606 | /* allocate instance-specific interface IDs, and patch descriptors */ |
---|
1113 | 1607 | status = usb_interface_id(c, f); |
---|
1114 | 1608 | if (status < 0) |
---|
1115 | | - goto fail; |
---|
| 1609 | + goto err_free_fu; |
---|
1116 | 1610 | iad_desc.bFirstInterface = status; |
---|
1117 | 1611 | ac_interface_desc.bInterfaceNumber = status; |
---|
1118 | 1612 | uac1->ac_intf = status; |
---|
1119 | 1613 | uac1->ac_alt = 0; |
---|
1120 | | - ac_header_desc.baInterfaceNr[0] = ++status; |
---|
1121 | | - ac_header_desc.baInterfaceNr[1] = ++status; |
---|
| 1614 | + |
---|
| 1615 | + ba_iface_id = 0; |
---|
1122 | 1616 | |
---|
1123 | 1617 | if (EPOUT_EN(audio_opts)) { |
---|
1124 | 1618 | status = usb_interface_id(c, f); |
---|
1125 | 1619 | if (status < 0) |
---|
1126 | | - goto fail; |
---|
| 1620 | + goto err_free_fu; |
---|
1127 | 1621 | as_out_interface_alt_0_desc.bInterfaceNumber = status; |
---|
1128 | 1622 | as_out_interface_alt_1_desc.bInterfaceNumber = status; |
---|
| 1623 | + ac_header_desc->baInterfaceNr[ba_iface_id++] = status; |
---|
1129 | 1624 | uac1->as_out_intf = status; |
---|
1130 | 1625 | uac1->as_out_alt = 0; |
---|
1131 | 1626 | } |
---|
.. | .. |
---|
1133 | 1628 | if (EPIN_EN(audio_opts)) { |
---|
1134 | 1629 | status = usb_interface_id(c, f); |
---|
1135 | 1630 | if (status < 0) |
---|
1136 | | - goto fail; |
---|
| 1631 | + goto err_free_fu; |
---|
1137 | 1632 | as_in_interface_alt_0_desc.bInterfaceNumber = status; |
---|
1138 | 1633 | as_in_interface_alt_1_desc.bInterfaceNumber = status; |
---|
| 1634 | + ac_header_desc->baInterfaceNr[ba_iface_id++] = status; |
---|
1139 | 1635 | uac1->as_in_intf = status; |
---|
1140 | 1636 | uac1->as_in_alt = 0; |
---|
1141 | 1637 | } |
---|
.. | .. |
---|
1144 | 1640 | |
---|
1145 | 1641 | status = -ENODEV; |
---|
1146 | 1642 | |
---|
| 1643 | + ac_interface_desc.bNumEndpoints = 0; |
---|
| 1644 | + |
---|
| 1645 | + /* allocate AC interrupt endpoint */ |
---|
| 1646 | + if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) { |
---|
| 1647 | + ep = usb_ep_autoconfig(cdev->gadget, &ac_int_ep_desc); |
---|
| 1648 | + if (!ep) |
---|
| 1649 | + goto err_free_fu; |
---|
| 1650 | + uac1->int_ep = ep; |
---|
| 1651 | + |
---|
| 1652 | + ac_interface_desc.bNumEndpoints = 1; |
---|
| 1653 | + } |
---|
| 1654 | + |
---|
1147 | 1655 | /* allocate instance-specific endpoints */ |
---|
1148 | 1656 | if (EPOUT_EN(audio_opts)) { |
---|
1149 | 1657 | ep = usb_ep_autoconfig(cdev->gadget, &as_out_ep_desc); |
---|
1150 | 1658 | if (!ep) |
---|
1151 | | - goto fail; |
---|
| 1659 | + goto err_free_fu; |
---|
1152 | 1660 | audio->out_ep = ep; |
---|
1153 | | - audio->out_ep->desc = &as_out_ep_desc; |
---|
1154 | 1661 | } |
---|
1155 | 1662 | |
---|
1156 | 1663 | if (EPIN_EN(audio_opts)) { |
---|
1157 | 1664 | ep = usb_ep_autoconfig(cdev->gadget, &as_in_ep_desc); |
---|
1158 | 1665 | if (!ep) |
---|
1159 | | - goto fail; |
---|
1160 | | - ep->maxpacket = usb_endpoint_maxp(&as_in_ep_desc); |
---|
| 1666 | + goto err_free_fu; |
---|
1161 | 1667 | audio->in_ep = ep; |
---|
1162 | | - audio->in_ep->desc = &as_in_ep_desc; |
---|
1163 | 1668 | } |
---|
1164 | 1669 | |
---|
1165 | | - ss_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress; |
---|
1166 | | - ss_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress; |
---|
| 1670 | + /* FS endpoint addresses are copied from autoconfigured HS descriptors */ |
---|
| 1671 | + fs_int_ep_desc.bEndpointAddress = ac_int_ep_desc.bEndpointAddress; |
---|
| 1672 | + fs_out_ep_desc.bEndpointAddress = as_out_ep_desc.bEndpointAddress; |
---|
| 1673 | + fs_in_ep_desc.bEndpointAddress = as_in_ep_desc.bEndpointAddress; |
---|
1167 | 1674 | |
---|
1168 | 1675 | setup_descriptor(audio_opts); |
---|
1169 | | - /* copy descriptors, and track endpoint copies */ |
---|
1170 | | - status = usb_assign_descriptors(f, f_audio_desc, f_audio_desc, |
---|
1171 | | - f_ss_audio_desc, f_ss_audio_desc); |
---|
1172 | | - if (status) |
---|
1173 | | - goto fail; |
---|
1174 | 1676 | |
---|
1175 | | - audio->out_ep_maxpsize = le16_to_cpu(as_out_ep_desc.wMaxPacketSize); |
---|
1176 | | - audio->in_ep_maxpsize = le16_to_cpu(as_in_ep_desc.wMaxPacketSize); |
---|
1177 | | - audio->out_ep_maxpsize = max_t(u16, audio->out_ep_maxpsize, |
---|
1178 | | - le16_to_cpu(ss_out_ep_desc.wMaxPacketSize)); |
---|
1179 | | - audio->in_ep_maxpsize = max_t(u16, audio->in_ep_maxpsize, |
---|
1180 | | - le16_to_cpu(ss_in_ep_desc.wMaxPacketSize)); |
---|
| 1677 | + /* copy descriptors, and track endpoint copies */ |
---|
| 1678 | + status = usb_assign_descriptors(f, fs_audio_desc, hs_audio_desc, |
---|
| 1679 | + ss_audio_desc, ss_audio_desc); |
---|
| 1680 | + if (status) |
---|
| 1681 | + goto err_free_fu; |
---|
| 1682 | + |
---|
| 1683 | + audio->out_ep_maxpsize = max_t(u16, |
---|
| 1684 | + le16_to_cpu(fs_out_ep_desc.wMaxPacketSize), |
---|
| 1685 | + le16_to_cpu(as_out_ep_desc.wMaxPacketSize)); |
---|
| 1686 | + audio->in_ep_maxpsize = max_t(u16, |
---|
| 1687 | + le16_to_cpu(fs_in_ep_desc.wMaxPacketSize), |
---|
| 1688 | + le16_to_cpu(as_in_ep_desc.wMaxPacketSize)); |
---|
1181 | 1689 | audio->params.c_chmask = audio_opts->c_chmask; |
---|
1182 | | - memcpy(audio->params.c_srate, audio_opts->c_srate, |
---|
1183 | | - sizeof(audio->params.c_srate)); |
---|
1184 | | - audio->params.c_srate_active = audio_opts->c_srate_active; |
---|
| 1690 | + memcpy(audio->params.c_srates, audio_opts->c_srates, |
---|
| 1691 | + sizeof(audio->params.c_srates)); |
---|
1185 | 1692 | audio->params.c_ssize = audio_opts->c_ssize; |
---|
| 1693 | + if (FUIN_EN(audio_opts)) { |
---|
| 1694 | + audio->params.p_fu.id = USB_IN_FU_ID; |
---|
| 1695 | + audio->params.p_fu.mute_present = audio_opts->p_mute_present; |
---|
| 1696 | + audio->params.p_fu.volume_present = |
---|
| 1697 | + audio_opts->p_volume_present; |
---|
| 1698 | + audio->params.p_fu.volume_min = audio_opts->p_volume_min; |
---|
| 1699 | + audio->params.p_fu.volume_max = audio_opts->p_volume_max; |
---|
| 1700 | + audio->params.p_fu.volume_res = audio_opts->p_volume_res; |
---|
| 1701 | + } |
---|
1186 | 1702 | audio->params.p_chmask = audio_opts->p_chmask; |
---|
1187 | | - memcpy(audio->params.p_srate, audio_opts->p_srate, |
---|
1188 | | - sizeof(audio->params.p_srate)); |
---|
1189 | | - audio->params.p_srate_active = audio_opts->p_srate_active; |
---|
| 1703 | + memcpy(audio->params.p_srates, audio_opts->p_srates, |
---|
| 1704 | + sizeof(audio->params.p_srates)); |
---|
1190 | 1705 | audio->params.p_ssize = audio_opts->p_ssize; |
---|
| 1706 | + if (FUOUT_EN(audio_opts)) { |
---|
| 1707 | + audio->params.c_fu.id = USB_OUT_FU_ID; |
---|
| 1708 | + audio->params.c_fu.mute_present = audio_opts->c_mute_present; |
---|
| 1709 | + audio->params.c_fu.volume_present = |
---|
| 1710 | + audio_opts->c_volume_present; |
---|
| 1711 | + audio->params.c_fu.volume_min = audio_opts->c_volume_min; |
---|
| 1712 | + audio->params.c_fu.volume_max = audio_opts->c_volume_max; |
---|
| 1713 | + audio->params.c_fu.volume_res = audio_opts->c_volume_res; |
---|
| 1714 | + } |
---|
1191 | 1715 | audio->params.req_number = audio_opts->req_number; |
---|
| 1716 | + audio->params.fb_max = FBACK_FAST_MAX; |
---|
| 1717 | + if (FUOUT_EN(audio_opts) || FUIN_EN(audio_opts)) |
---|
| 1718 | + audio->notify = audio_notify; |
---|
1192 | 1719 | |
---|
1193 | 1720 | status = g_audio_setup(audio, "UAC1_PCM", "UAC1_Gadget"); |
---|
1194 | 1721 | if (status) |
---|
.. | .. |
---|
1198 | 1725 | |
---|
1199 | 1726 | err_card_register: |
---|
1200 | 1727 | usb_free_all_descriptors(f); |
---|
| 1728 | +err_free_fu: |
---|
| 1729 | + kfree(out_feature_unit_desc); |
---|
| 1730 | + out_feature_unit_desc = NULL; |
---|
| 1731 | + kfree(in_feature_unit_desc); |
---|
| 1732 | + in_feature_unit_desc = NULL; |
---|
1201 | 1733 | fail: |
---|
| 1734 | + kfree(ac_header_desc); |
---|
| 1735 | + ac_header_desc = NULL; |
---|
1202 | 1736 | return status; |
---|
1203 | 1737 | } |
---|
1204 | 1738 | |
---|
1205 | 1739 | /*-------------------------------------------------------------------------*/ |
---|
1206 | 1740 | |
---|
1207 | | -/* Todo: add more control selecotor dynamically */ |
---|
1208 | | -static int control_selector_init(struct f_uac *uac1) |
---|
| 1741 | +static inline struct f_uac1_opts *to_f_uac1_opts(struct config_item *item) |
---|
1209 | 1742 | { |
---|
1210 | | - INIT_LIST_HEAD(&uac1->cs); |
---|
| 1743 | + return container_of(to_config_group(item), struct f_uac1_opts, |
---|
| 1744 | + func_inst.group); |
---|
| 1745 | +} |
---|
1211 | 1746 | |
---|
1212 | | - /* playback feature unit */ |
---|
1213 | | - list_add(&p_feature_unit.list, &uac1->cs); |
---|
| 1747 | +static void f_uac1_attr_release(struct config_item *item) |
---|
| 1748 | +{ |
---|
| 1749 | + struct f_uac1_opts *opts = to_f_uac1_opts(item); |
---|
1214 | 1750 | |
---|
1215 | | - INIT_LIST_HEAD(&p_feature_unit.control); |
---|
1216 | | - list_add(&p_mute_control.list, &p_feature_unit.control); |
---|
1217 | | - list_add(&p_volume_control.list, &p_feature_unit.control); |
---|
1218 | | - |
---|
1219 | | - p_volume_control.data[UAC__CUR] = UAC_VOLUME_CUR; |
---|
1220 | | - p_volume_control.data[UAC__MIN] = UAC_VOLUME_MIN; |
---|
1221 | | - p_volume_control.data[UAC__MAX] = UAC_VOLUME_MAX; |
---|
1222 | | - p_volume_control.data[UAC__RES] = UAC_VOLUME_RES; |
---|
1223 | | - |
---|
1224 | | - p_volume_control.context = &uac1->g_audio; |
---|
1225 | | - p_mute_control.context = &uac1->g_audio; |
---|
1226 | | - |
---|
1227 | | - /* capture feature unit */ |
---|
1228 | | - list_add(&c_feature_unit.list, &uac1->cs); |
---|
1229 | | - |
---|
1230 | | - INIT_LIST_HEAD(&c_feature_unit.control); |
---|
1231 | | - list_add(&c_mute_control.list, &c_feature_unit.control); |
---|
1232 | | - list_add(&c_volume_control.list, &c_feature_unit.control); |
---|
1233 | | - |
---|
1234 | | - c_volume_control.data[UAC__CUR] = UAC_VOLUME_CUR; |
---|
1235 | | - c_volume_control.data[UAC__MIN] = UAC_VOLUME_MIN; |
---|
1236 | | - c_volume_control.data[UAC__MAX] = UAC_VOLUME_MAX; |
---|
1237 | | - c_volume_control.data[UAC__RES] = UAC_VOLUME_RES; |
---|
1238 | | - |
---|
1239 | | - c_volume_control.context = &uac1->g_audio; |
---|
1240 | | - c_mute_control.context = &uac1->g_audio; |
---|
1241 | | - |
---|
1242 | | - return 0; |
---|
| 1751 | + usb_put_function_instance(&opts->func_inst); |
---|
1243 | 1752 | } |
---|
1244 | 1753 | |
---|
1245 | 1754 | static struct configfs_item_operations f_uac1_item_ops = { |
---|
1246 | | - .release = f_uac_attr_release, |
---|
| 1755 | + .release = f_uac1_attr_release, |
---|
1247 | 1756 | }; |
---|
1248 | 1757 | |
---|
1249 | | -UAC_ATTRIBUTE(c_chmask); |
---|
1250 | | -UAC_ATTRIBUTE(c_ssize); |
---|
1251 | | -UAC_ATTRIBUTE(c_feature_unit); |
---|
1252 | | -UAC_ATTRIBUTE(p_chmask); |
---|
1253 | | -UAC_ATTRIBUTE(p_ssize); |
---|
1254 | | -UAC_ATTRIBUTE(p_feature_unit); |
---|
1255 | | -UAC_ATTRIBUTE(req_number); |
---|
| 1758 | +#define uac1_kstrtou32 kstrtou32 |
---|
| 1759 | +#define uac1_kstrtos16 kstrtos16 |
---|
| 1760 | +#define uac1_kstrtobool(s, base, res) kstrtobool((s), (res)) |
---|
1256 | 1761 | |
---|
1257 | | -UAC_RATE_ATTRIBUTE(p_srate); |
---|
1258 | | -UAC_RATE_ATTRIBUTE(c_srate); |
---|
| 1762 | +static const char *u32_fmt = "%u\n"; |
---|
| 1763 | +static const char *s16_fmt = "%hd\n"; |
---|
| 1764 | +static const char *bool_fmt = "%u\n"; |
---|
| 1765 | + |
---|
| 1766 | +#define UAC1_ATTRIBUTE(type, name) \ |
---|
| 1767 | +static ssize_t f_uac1_opts_##name##_show( \ |
---|
| 1768 | + struct config_item *item, \ |
---|
| 1769 | + char *page) \ |
---|
| 1770 | +{ \ |
---|
| 1771 | + struct f_uac1_opts *opts = to_f_uac1_opts(item); \ |
---|
| 1772 | + int result; \ |
---|
| 1773 | + \ |
---|
| 1774 | + mutex_lock(&opts->lock); \ |
---|
| 1775 | + result = sprintf(page, type##_fmt, opts->name); \ |
---|
| 1776 | + mutex_unlock(&opts->lock); \ |
---|
| 1777 | + \ |
---|
| 1778 | + return result; \ |
---|
| 1779 | +} \ |
---|
| 1780 | + \ |
---|
| 1781 | +static ssize_t f_uac1_opts_##name##_store( \ |
---|
| 1782 | + struct config_item *item, \ |
---|
| 1783 | + const char *page, size_t len) \ |
---|
| 1784 | +{ \ |
---|
| 1785 | + struct f_uac1_opts *opts = to_f_uac1_opts(item); \ |
---|
| 1786 | + int ret; \ |
---|
| 1787 | + type num; \ |
---|
| 1788 | + \ |
---|
| 1789 | + mutex_lock(&opts->lock); \ |
---|
| 1790 | + if (opts->refcnt) { \ |
---|
| 1791 | + ret = -EBUSY; \ |
---|
| 1792 | + goto end; \ |
---|
| 1793 | + } \ |
---|
| 1794 | + \ |
---|
| 1795 | + ret = uac1_kstrto##type(page, 0, &num); \ |
---|
| 1796 | + if (ret) \ |
---|
| 1797 | + goto end; \ |
---|
| 1798 | + \ |
---|
| 1799 | + opts->name = num; \ |
---|
| 1800 | + ret = len; \ |
---|
| 1801 | + \ |
---|
| 1802 | +end: \ |
---|
| 1803 | + mutex_unlock(&opts->lock); \ |
---|
| 1804 | + return ret; \ |
---|
| 1805 | +} \ |
---|
| 1806 | + \ |
---|
| 1807 | +CONFIGFS_ATTR(f_uac1_opts_, name) |
---|
| 1808 | + |
---|
| 1809 | +#define UAC1_RATE_ATTRIBUTE(name) \ |
---|
| 1810 | +static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ |
---|
| 1811 | + char *page) \ |
---|
| 1812 | +{ \ |
---|
| 1813 | + struct f_uac1_opts *opts = to_f_uac1_opts(item); \ |
---|
| 1814 | + int result = 0; \ |
---|
| 1815 | + int i; \ |
---|
| 1816 | + \ |
---|
| 1817 | + mutex_lock(&opts->lock); \ |
---|
| 1818 | + page[0] = '\0'; \ |
---|
| 1819 | + for (i = 0; i < UAC_MAX_RATES; i++) { \ |
---|
| 1820 | + if (opts->name##s[i] == 0) \ |
---|
| 1821 | + break; \ |
---|
| 1822 | + result += sprintf(page + strlen(page), "%u,", \ |
---|
| 1823 | + opts->name##s[i]); \ |
---|
| 1824 | + } \ |
---|
| 1825 | + if (strlen(page) > 0) \ |
---|
| 1826 | + page[strlen(page) - 1] = '\n'; \ |
---|
| 1827 | + mutex_unlock(&opts->lock); \ |
---|
| 1828 | + \ |
---|
| 1829 | + return result; \ |
---|
| 1830 | +} \ |
---|
| 1831 | + \ |
---|
| 1832 | +static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ |
---|
| 1833 | + const char *page, size_t len) \ |
---|
| 1834 | +{ \ |
---|
| 1835 | + struct f_uac1_opts *opts = to_f_uac1_opts(item); \ |
---|
| 1836 | + char *split_page = NULL; \ |
---|
| 1837 | + int ret = -EINVAL; \ |
---|
| 1838 | + char *token; \ |
---|
| 1839 | + u32 num; \ |
---|
| 1840 | + int i; \ |
---|
| 1841 | + \ |
---|
| 1842 | + mutex_lock(&opts->lock); \ |
---|
| 1843 | + if (opts->refcnt) { \ |
---|
| 1844 | + ret = -EBUSY; \ |
---|
| 1845 | + goto end; \ |
---|
| 1846 | + } \ |
---|
| 1847 | + \ |
---|
| 1848 | + i = 0; \ |
---|
| 1849 | + memset(opts->name##s, 0x00, sizeof(opts->name##s)); \ |
---|
| 1850 | + split_page = kstrdup(page, GFP_KERNEL); \ |
---|
| 1851 | + while ((token = strsep(&split_page, ",")) != NULL) { \ |
---|
| 1852 | + ret = kstrtou32(token, 0, &num); \ |
---|
| 1853 | + if (ret) \ |
---|
| 1854 | + goto end; \ |
---|
| 1855 | + \ |
---|
| 1856 | + opts->name##s[i++] = num; \ |
---|
| 1857 | + ret = len; \ |
---|
| 1858 | + }; \ |
---|
| 1859 | + \ |
---|
| 1860 | +end: \ |
---|
| 1861 | + kfree(split_page); \ |
---|
| 1862 | + mutex_unlock(&opts->lock); \ |
---|
| 1863 | + return ret; \ |
---|
| 1864 | +} \ |
---|
| 1865 | + \ |
---|
| 1866 | +CONFIGFS_ATTR(f_uac1_opts_, name) |
---|
| 1867 | + |
---|
| 1868 | +#define UAC1_ATTRIBUTE_STRING(name) \ |
---|
| 1869 | +static ssize_t f_uac1_opts_##name##_show(struct config_item *item, \ |
---|
| 1870 | + char *page) \ |
---|
| 1871 | +{ \ |
---|
| 1872 | + struct f_uac1_opts *opts = to_f_uac1_opts(item); \ |
---|
| 1873 | + int result; \ |
---|
| 1874 | + \ |
---|
| 1875 | + mutex_lock(&opts->lock); \ |
---|
| 1876 | + result = snprintf(page, sizeof(opts->name), "%s", opts->name); \ |
---|
| 1877 | + mutex_unlock(&opts->lock); \ |
---|
| 1878 | + \ |
---|
| 1879 | + return result; \ |
---|
| 1880 | +} \ |
---|
| 1881 | + \ |
---|
| 1882 | +static ssize_t f_uac1_opts_##name##_store(struct config_item *item, \ |
---|
| 1883 | + const char *page, size_t len) \ |
---|
| 1884 | +{ \ |
---|
| 1885 | + struct f_uac1_opts *opts = to_f_uac1_opts(item); \ |
---|
| 1886 | + int ret = 0; \ |
---|
| 1887 | + \ |
---|
| 1888 | + mutex_lock(&opts->lock); \ |
---|
| 1889 | + if (opts->refcnt) { \ |
---|
| 1890 | + ret = -EBUSY; \ |
---|
| 1891 | + goto end; \ |
---|
| 1892 | + } \ |
---|
| 1893 | + \ |
---|
| 1894 | + ret = snprintf(opts->name, min(sizeof(opts->name), len), \ |
---|
| 1895 | + "%s", page); \ |
---|
| 1896 | + \ |
---|
| 1897 | +end: \ |
---|
| 1898 | + mutex_unlock(&opts->lock); \ |
---|
| 1899 | + return ret; \ |
---|
| 1900 | +} \ |
---|
| 1901 | + \ |
---|
| 1902 | +CONFIGFS_ATTR(f_uac1_opts_, name) |
---|
| 1903 | + |
---|
| 1904 | +UAC1_ATTRIBUTE(u32, c_chmask); |
---|
| 1905 | +UAC1_RATE_ATTRIBUTE(c_srate); |
---|
| 1906 | +UAC1_ATTRIBUTE(u32, c_ssize); |
---|
| 1907 | +UAC1_ATTRIBUTE(u32, p_chmask); |
---|
| 1908 | +UAC1_RATE_ATTRIBUTE(p_srate); |
---|
| 1909 | +UAC1_ATTRIBUTE(u32, p_ssize); |
---|
| 1910 | +UAC1_ATTRIBUTE(u32, req_number); |
---|
| 1911 | + |
---|
| 1912 | +UAC1_ATTRIBUTE(bool, p_mute_present); |
---|
| 1913 | +UAC1_ATTRIBUTE(bool, p_volume_present); |
---|
| 1914 | +UAC1_ATTRIBUTE(s16, p_volume_min); |
---|
| 1915 | +UAC1_ATTRIBUTE(s16, p_volume_max); |
---|
| 1916 | +UAC1_ATTRIBUTE(s16, p_volume_res); |
---|
| 1917 | + |
---|
| 1918 | +UAC1_ATTRIBUTE(bool, c_mute_present); |
---|
| 1919 | +UAC1_ATTRIBUTE(bool, c_volume_present); |
---|
| 1920 | +UAC1_ATTRIBUTE(s16, c_volume_min); |
---|
| 1921 | +UAC1_ATTRIBUTE(s16, c_volume_max); |
---|
| 1922 | +UAC1_ATTRIBUTE(s16, c_volume_res); |
---|
| 1923 | +UAC1_ATTRIBUTE_STRING(function_name); |
---|
1259 | 1924 | |
---|
1260 | 1925 | static struct configfs_attribute *f_uac1_attrs[] = { |
---|
1261 | | - &f_uac_opts_attr_c_chmask, |
---|
1262 | | - &f_uac_opts_attr_c_srate, |
---|
1263 | | - &f_uac_opts_attr_c_ssize, |
---|
1264 | | - &f_uac_opts_attr_c_feature_unit, |
---|
1265 | | - &f_uac_opts_attr_p_chmask, |
---|
1266 | | - &f_uac_opts_attr_p_srate, |
---|
1267 | | - &f_uac_opts_attr_p_ssize, |
---|
1268 | | - &f_uac_opts_attr_p_feature_unit, |
---|
1269 | | - &f_uac_opts_attr_req_number, |
---|
| 1926 | + &f_uac1_opts_attr_c_chmask, |
---|
| 1927 | + &f_uac1_opts_attr_c_srate, |
---|
| 1928 | + &f_uac1_opts_attr_c_ssize, |
---|
| 1929 | + &f_uac1_opts_attr_p_chmask, |
---|
| 1930 | + &f_uac1_opts_attr_p_srate, |
---|
| 1931 | + &f_uac1_opts_attr_p_ssize, |
---|
| 1932 | + &f_uac1_opts_attr_req_number, |
---|
| 1933 | + |
---|
| 1934 | + &f_uac1_opts_attr_p_mute_present, |
---|
| 1935 | + &f_uac1_opts_attr_p_volume_present, |
---|
| 1936 | + &f_uac1_opts_attr_p_volume_min, |
---|
| 1937 | + &f_uac1_opts_attr_p_volume_max, |
---|
| 1938 | + &f_uac1_opts_attr_p_volume_res, |
---|
| 1939 | + |
---|
| 1940 | + &f_uac1_opts_attr_c_mute_present, |
---|
| 1941 | + &f_uac1_opts_attr_c_volume_present, |
---|
| 1942 | + &f_uac1_opts_attr_c_volume_min, |
---|
| 1943 | + &f_uac1_opts_attr_c_volume_max, |
---|
| 1944 | + &f_uac1_opts_attr_c_volume_res, |
---|
| 1945 | + |
---|
| 1946 | + &f_uac1_opts_attr_function_name, |
---|
| 1947 | + |
---|
1270 | 1948 | NULL, |
---|
1271 | 1949 | }; |
---|
1272 | 1950 | |
---|
.. | .. |
---|
1278 | 1956 | |
---|
1279 | 1957 | static void f_audio_free_inst(struct usb_function_instance *f) |
---|
1280 | 1958 | { |
---|
1281 | | - struct f_uac_opts *opts; |
---|
| 1959 | + struct f_uac1_opts *opts; |
---|
1282 | 1960 | |
---|
1283 | | - opts = container_of(f, struct f_uac_opts, func_inst); |
---|
| 1961 | + opts = container_of(f, struct f_uac1_opts, func_inst); |
---|
1284 | 1962 | kfree(opts); |
---|
1285 | 1963 | } |
---|
1286 | 1964 | |
---|
1287 | 1965 | static struct usb_function_instance *f_audio_alloc_inst(void) |
---|
1288 | 1966 | { |
---|
1289 | | - struct f_uac_opts *opts; |
---|
| 1967 | + struct f_uac1_opts *opts; |
---|
1290 | 1968 | |
---|
1291 | 1969 | opts = kzalloc(sizeof(*opts), GFP_KERNEL); |
---|
1292 | 1970 | if (!opts) |
---|
.. | .. |
---|
1298 | 1976 | config_group_init_type_name(&opts->func_inst.group, "", |
---|
1299 | 1977 | &f_uac1_func_type); |
---|
1300 | 1978 | |
---|
1301 | | - opts->c_chmask = UAC_DEF_CCHMASK; |
---|
1302 | | - opts->c_srate[0] = UAC_DEF_CSRATE; |
---|
1303 | | - opts->c_srate_active = UAC_DEF_CSRATE; |
---|
1304 | | - opts->c_ssize = UAC_DEF_CSSIZE; |
---|
1305 | | - opts->c_feature_unit = UAC_DEF_CFU; |
---|
1306 | | - opts->p_chmask = UAC_DEF_PCHMASK; |
---|
1307 | | - opts->p_srate[0] = UAC_DEF_PSRATE; |
---|
1308 | | - opts->p_srate_active = UAC_DEF_PSRATE; |
---|
1309 | | - opts->p_ssize = UAC_DEF_PSSIZE; |
---|
1310 | | - opts->p_feature_unit = UAC_DEF_PFU; |
---|
1311 | | - opts->req_number = UAC_DEF_REQ_NUM; |
---|
| 1979 | + opts->c_chmask = UAC1_DEF_CCHMASK; |
---|
| 1980 | + opts->c_srates[0] = UAC1_DEF_CSRATE; |
---|
| 1981 | + opts->c_ssize = UAC1_DEF_CSSIZE; |
---|
| 1982 | + opts->p_chmask = UAC1_DEF_PCHMASK; |
---|
| 1983 | + opts->p_srates[0] = UAC1_DEF_PSRATE; |
---|
| 1984 | + opts->p_ssize = UAC1_DEF_PSSIZE; |
---|
| 1985 | + |
---|
| 1986 | + opts->p_mute_present = UAC1_DEF_MUTE_PRESENT; |
---|
| 1987 | + opts->p_volume_present = UAC1_DEF_VOLUME_PRESENT; |
---|
| 1988 | + opts->p_volume_min = UAC1_DEF_MIN_DB; |
---|
| 1989 | + opts->p_volume_max = UAC1_DEF_MAX_DB; |
---|
| 1990 | + opts->p_volume_res = UAC1_DEF_RES_DB; |
---|
| 1991 | + |
---|
| 1992 | + opts->c_mute_present = UAC1_DEF_MUTE_PRESENT; |
---|
| 1993 | + opts->c_volume_present = UAC1_DEF_VOLUME_PRESENT; |
---|
| 1994 | + opts->c_volume_min = UAC1_DEF_MIN_DB; |
---|
| 1995 | + opts->c_volume_max = UAC1_DEF_MAX_DB; |
---|
| 1996 | + opts->c_volume_res = UAC1_DEF_RES_DB; |
---|
| 1997 | + |
---|
| 1998 | + opts->req_number = UAC1_DEF_REQ_NUM; |
---|
| 1999 | + |
---|
| 2000 | + snprintf(opts->function_name, sizeof(opts->function_name), "Source/Sink"); |
---|
| 2001 | + |
---|
1312 | 2002 | return &opts->func_inst; |
---|
1313 | 2003 | } |
---|
1314 | 2004 | |
---|
1315 | 2005 | static void f_audio_free(struct usb_function *f) |
---|
1316 | 2006 | { |
---|
1317 | 2007 | struct g_audio *audio; |
---|
1318 | | - struct f_uac_opts *opts; |
---|
| 2008 | + struct f_uac1_opts *opts; |
---|
1319 | 2009 | |
---|
1320 | 2010 | audio = func_to_g_audio(f); |
---|
1321 | | - opts = container_of(f->fi, struct f_uac_opts, func_inst); |
---|
| 2011 | + opts = container_of(f->fi, struct f_uac1_opts, func_inst); |
---|
1322 | 2012 | kfree(audio); |
---|
1323 | 2013 | mutex_lock(&opts->lock); |
---|
1324 | 2014 | --opts->refcnt; |
---|
.. | .. |
---|
1332 | 2022 | g_audio_cleanup(audio); |
---|
1333 | 2023 | usb_free_all_descriptors(f); |
---|
1334 | 2024 | |
---|
| 2025 | + kfree(out_feature_unit_desc); |
---|
| 2026 | + out_feature_unit_desc = NULL; |
---|
| 2027 | + kfree(in_feature_unit_desc); |
---|
| 2028 | + in_feature_unit_desc = NULL; |
---|
| 2029 | + |
---|
| 2030 | + kfree(ac_header_desc); |
---|
| 2031 | + ac_header_desc = NULL; |
---|
| 2032 | + |
---|
1335 | 2033 | audio->gadget = NULL; |
---|
1336 | 2034 | } |
---|
1337 | 2035 | |
---|
1338 | 2036 | static struct usb_function *f_audio_alloc(struct usb_function_instance *fi) |
---|
1339 | 2037 | { |
---|
1340 | | - struct f_uac *uac1; |
---|
1341 | | - struct f_uac_opts *opts; |
---|
| 2038 | + struct f_uac1 *uac1; |
---|
| 2039 | + struct f_uac1_opts *opts; |
---|
1342 | 2040 | |
---|
1343 | 2041 | /* allocate and initialize one new instance */ |
---|
1344 | 2042 | uac1 = kzalloc(sizeof(*uac1), GFP_KERNEL); |
---|
1345 | 2043 | if (!uac1) |
---|
1346 | 2044 | return ERR_PTR(-ENOMEM); |
---|
1347 | 2045 | |
---|
1348 | | - opts = container_of(fi, struct f_uac_opts, func_inst); |
---|
| 2046 | + opts = container_of(fi, struct f_uac1_opts, func_inst); |
---|
1349 | 2047 | mutex_lock(&opts->lock); |
---|
1350 | 2048 | ++opts->refcnt; |
---|
1351 | 2049 | mutex_unlock(&opts->lock); |
---|
.. | .. |
---|
1357 | 2055 | uac1->g_audio.func.get_alt = f_audio_get_alt; |
---|
1358 | 2056 | uac1->g_audio.func.setup = f_audio_setup; |
---|
1359 | 2057 | uac1->g_audio.func.disable = f_audio_disable; |
---|
| 2058 | + uac1->g_audio.func.suspend = f_audio_suspend; |
---|
1360 | 2059 | uac1->g_audio.func.free_func = f_audio_free; |
---|
1361 | | - |
---|
1362 | | - control_selector_init(uac1); |
---|
1363 | 2060 | |
---|
1364 | 2061 | return &uac1->g_audio.func; |
---|
1365 | 2062 | } |
---|