]>
Commit | Line | Data |
---|---|---|
46919a23 AP |
1 | /* |
2 | * uvc_configfs.c | |
3 | * | |
4 | * Configfs support for the uvc function. | |
5 | * | |
6 | * Copyright (c) 2014 Samsung Electronics Co., Ltd. | |
7 | * http://www.samsung.com | |
8 | * | |
9 | * Author: Andrzej Pietrasiewicz <andrzej.p@samsung.com> | |
10 | * | |
11 | * This program is free software; you can redistribute it and/or modify | |
12 | * it under the terms of the GNU General Public License version 2 as | |
13 | * published by the Free Software Foundation. | |
14 | */ | |
15 | #include "u_uvc.h" | |
16 | #include "uvc_configfs.h" | |
17 | ||
18 | #define UVCG_STREAMING_CONTROL_SIZE 1 | |
19 | ||
76e0da34 CH |
20 | #define UVC_ATTR(prefix, cname, aname) \ |
21 | static struct configfs_attribute prefix##attr_##cname = { \ | |
22 | .ca_name = __stringify(aname), \ | |
27681abc | 23 | .ca_mode = S_IRUGO | S_IWUGO, \ |
76e0da34 CH |
24 | .ca_owner = THIS_MODULE, \ |
25 | .show = prefix##cname##_show, \ | |
26 | .store = prefix##cname##_store, \ | |
27 | } | |
28 | ||
29 | #define UVC_ATTR_RO(prefix, cname, aname) \ | |
30 | static struct configfs_attribute prefix##attr_##cname = { \ | |
31 | .ca_name = __stringify(aname), \ | |
32 | .ca_mode = S_IRUGO, \ | |
33 | .ca_owner = THIS_MODULE, \ | |
34 | .show = prefix##cname##_show, \ | |
46919a23 AP |
35 | } |
36 | ||
37 | static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item); | |
38 | ||
39 | /* control/header/<NAME> */ | |
40 | DECLARE_UVC_HEADER_DESCRIPTOR(1); | |
41 | ||
42 | struct uvcg_control_header { | |
43 | struct config_item item; | |
44 | struct UVC_HEADER_DESCRIPTOR(1) desc; | |
45 | unsigned linked; | |
46 | }; | |
47 | ||
f093a2d4 | 48 | static struct uvcg_control_header *to_uvcg_control_header(struct config_item *item) |
46919a23 AP |
49 | { |
50 | return container_of(item, struct uvcg_control_header, item); | |
51 | } | |
52 | ||
46919a23 AP |
53 | #define UVCG_CTRL_HDR_ATTR(cname, aname, conv, str2u, uxx, vnoc, limit) \ |
54 | static ssize_t uvcg_control_header_##cname##_show( \ | |
76e0da34 | 55 | struct config_item *item, char *page) \ |
46919a23 | 56 | { \ |
76e0da34 | 57 | struct uvcg_control_header *ch = to_uvcg_control_header(item); \ |
46919a23 AP |
58 | struct f_uvc_opts *opts; \ |
59 | struct config_item *opts_item; \ | |
60 | struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ | |
61 | int result; \ | |
62 | \ | |
63 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
64 | \ | |
65 | opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ | |
66 | opts = to_f_uvc_opts(opts_item); \ | |
67 | \ | |
68 | mutex_lock(&opts->lock); \ | |
69 | result = sprintf(page, "%d\n", conv(ch->desc.aname)); \ | |
70 | mutex_unlock(&opts->lock); \ | |
71 | \ | |
72 | mutex_unlock(su_mutex); \ | |
73 | return result; \ | |
74 | } \ | |
75 | \ | |
76 | static ssize_t \ | |
76e0da34 | 77 | uvcg_control_header_##cname##_store(struct config_item *item, \ |
46919a23 AP |
78 | const char *page, size_t len) \ |
79 | { \ | |
76e0da34 | 80 | struct uvcg_control_header *ch = to_uvcg_control_header(item); \ |
46919a23 AP |
81 | struct f_uvc_opts *opts; \ |
82 | struct config_item *opts_item; \ | |
83 | struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex;\ | |
84 | int ret; \ | |
85 | uxx num; \ | |
86 | \ | |
87 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
88 | \ | |
89 | opts_item = ch->item.ci_parent->ci_parent->ci_parent; \ | |
90 | opts = to_f_uvc_opts(opts_item); \ | |
91 | \ | |
92 | mutex_lock(&opts->lock); \ | |
93 | if (ch->linked || opts->refcnt) { \ | |
94 | ret = -EBUSY; \ | |
95 | goto end; \ | |
96 | } \ | |
97 | \ | |
98 | ret = str2u(page, 0, &num); \ | |
99 | if (ret) \ | |
100 | goto end; \ | |
101 | \ | |
102 | if (num > limit) { \ | |
103 | ret = -EINVAL; \ | |
104 | goto end; \ | |
105 | } \ | |
106 | ch->desc.aname = vnoc(num); \ | |
107 | ret = len; \ | |
108 | end: \ | |
109 | mutex_unlock(&opts->lock); \ | |
110 | mutex_unlock(su_mutex); \ | |
111 | return ret; \ | |
112 | } \ | |
113 | \ | |
76e0da34 | 114 | UVC_ATTR(uvcg_control_header_, cname, aname) |
46919a23 AP |
115 | |
116 | UVCG_CTRL_HDR_ATTR(bcd_uvc, bcdUVC, le16_to_cpu, kstrtou16, u16, cpu_to_le16, | |
117 | 0xffff); | |
118 | ||
119 | UVCG_CTRL_HDR_ATTR(dw_clock_frequency, dwClockFrequency, le32_to_cpu, kstrtou32, | |
120 | u32, cpu_to_le32, 0x7fffffff); | |
121 | ||
122 | #undef UVCG_CTRL_HDR_ATTR | |
123 | ||
124 | static struct configfs_attribute *uvcg_control_header_attrs[] = { | |
76e0da34 CH |
125 | &uvcg_control_header_attr_bcd_uvc, |
126 | &uvcg_control_header_attr_dw_clock_frequency, | |
46919a23 AP |
127 | NULL, |
128 | }; | |
129 | ||
f093a2d4 | 130 | static struct config_item_type uvcg_control_header_type = { |
46919a23 AP |
131 | .ct_attrs = uvcg_control_header_attrs, |
132 | .ct_owner = THIS_MODULE, | |
133 | }; | |
134 | ||
135 | static struct config_item *uvcg_control_header_make(struct config_group *group, | |
136 | const char *name) | |
137 | { | |
138 | struct uvcg_control_header *h; | |
139 | ||
140 | h = kzalloc(sizeof(*h), GFP_KERNEL); | |
141 | if (!h) | |
df90f838 | 142 | return ERR_PTR(-ENOMEM); |
46919a23 AP |
143 | |
144 | h->desc.bLength = UVC_DT_HEADER_SIZE(1); | |
145 | h->desc.bDescriptorType = USB_DT_CS_INTERFACE; | |
146 | h->desc.bDescriptorSubType = UVC_VC_HEADER; | |
147 | h->desc.bcdUVC = cpu_to_le16(0x0100); | |
148 | h->desc.dwClockFrequency = cpu_to_le32(48000000); | |
149 | ||
150 | config_item_init_type_name(&h->item, name, &uvcg_control_header_type); | |
151 | ||
152 | return &h->item; | |
153 | } | |
154 | ||
f093a2d4 | 155 | static void uvcg_control_header_drop(struct config_group *group, |
46919a23 AP |
156 | struct config_item *item) |
157 | { | |
158 | struct uvcg_control_header *h = to_uvcg_control_header(item); | |
159 | ||
160 | kfree(h); | |
161 | } | |
162 | ||
163 | /* control/header */ | |
164 | static struct uvcg_control_header_grp { | |
165 | struct config_group group; | |
166 | } uvcg_control_header_grp; | |
167 | ||
168 | static struct configfs_group_operations uvcg_control_header_grp_ops = { | |
169 | .make_item = uvcg_control_header_make, | |
170 | .drop_item = uvcg_control_header_drop, | |
171 | }; | |
172 | ||
173 | static struct config_item_type uvcg_control_header_grp_type = { | |
174 | .ct_group_ops = &uvcg_control_header_grp_ops, | |
175 | .ct_owner = THIS_MODULE, | |
176 | }; | |
177 | ||
178 | /* control/processing/default */ | |
179 | static struct uvcg_default_processing { | |
180 | struct config_group group; | |
181 | } uvcg_default_processing; | |
182 | ||
183 | static inline struct uvcg_default_processing | |
184 | *to_uvcg_default_processing(struct config_item *item) | |
185 | { | |
186 | return container_of(to_config_group(item), | |
187 | struct uvcg_default_processing, group); | |
188 | } | |
189 | ||
46919a23 AP |
190 | #define UVCG_DEFAULT_PROCESSING_ATTR(cname, aname, conv) \ |
191 | static ssize_t uvcg_default_processing_##cname##_show( \ | |
76e0da34 | 192 | struct config_item *item, char *page) \ |
46919a23 | 193 | { \ |
76e0da34 | 194 | struct uvcg_default_processing *dp = to_uvcg_default_processing(item); \ |
46919a23 AP |
195 | struct f_uvc_opts *opts; \ |
196 | struct config_item *opts_item; \ | |
197 | struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; \ | |
198 | struct uvc_processing_unit_descriptor *pd; \ | |
199 | int result; \ | |
200 | \ | |
201 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
202 | \ | |
203 | opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; \ | |
204 | opts = to_f_uvc_opts(opts_item); \ | |
205 | pd = &opts->uvc_processing; \ | |
206 | \ | |
207 | mutex_lock(&opts->lock); \ | |
208 | result = sprintf(page, "%d\n", conv(pd->aname)); \ | |
209 | mutex_unlock(&opts->lock); \ | |
210 | \ | |
211 | mutex_unlock(su_mutex); \ | |
212 | return result; \ | |
213 | } \ | |
214 | \ | |
76e0da34 | 215 | UVC_ATTR_RO(uvcg_default_processing_, cname, aname) |
46919a23 AP |
216 | |
217 | #define identity_conv(x) (x) | |
218 | ||
219 | UVCG_DEFAULT_PROCESSING_ATTR(b_unit_id, bUnitID, identity_conv); | |
220 | UVCG_DEFAULT_PROCESSING_ATTR(b_source_id, bSourceID, identity_conv); | |
221 | UVCG_DEFAULT_PROCESSING_ATTR(w_max_multiplier, wMaxMultiplier, le16_to_cpu); | |
222 | UVCG_DEFAULT_PROCESSING_ATTR(i_processing, iProcessing, identity_conv); | |
223 | ||
224 | #undef identity_conv | |
225 | ||
226 | #undef UVCG_DEFAULT_PROCESSING_ATTR | |
227 | ||
228 | static ssize_t uvcg_default_processing_bm_controls_show( | |
76e0da34 | 229 | struct config_item *item, char *page) |
46919a23 | 230 | { |
76e0da34 | 231 | struct uvcg_default_processing *dp = to_uvcg_default_processing(item); |
46919a23 AP |
232 | struct f_uvc_opts *opts; |
233 | struct config_item *opts_item; | |
234 | struct mutex *su_mutex = &dp->group.cg_subsys->su_mutex; | |
235 | struct uvc_processing_unit_descriptor *pd; | |
236 | int result, i; | |
237 | char *pg = page; | |
238 | ||
239 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
240 | ||
241 | opts_item = dp->group.cg_item.ci_parent->ci_parent->ci_parent; | |
242 | opts = to_f_uvc_opts(opts_item); | |
243 | pd = &opts->uvc_processing; | |
244 | ||
245 | mutex_lock(&opts->lock); | |
246 | for (result = 0, i = 0; i < pd->bControlSize; ++i) { | |
247 | result += sprintf(pg, "%d\n", pd->bmControls[i]); | |
248 | pg = page + result; | |
249 | } | |
250 | mutex_unlock(&opts->lock); | |
251 | ||
252 | mutex_unlock(su_mutex); | |
253 | ||
254 | return result; | |
255 | } | |
256 | ||
76e0da34 | 257 | UVC_ATTR_RO(uvcg_default_processing_, bm_controls, bmControls); |
46919a23 AP |
258 | |
259 | static struct configfs_attribute *uvcg_default_processing_attrs[] = { | |
76e0da34 CH |
260 | &uvcg_default_processing_attr_b_unit_id, |
261 | &uvcg_default_processing_attr_b_source_id, | |
262 | &uvcg_default_processing_attr_w_max_multiplier, | |
263 | &uvcg_default_processing_attr_bm_controls, | |
264 | &uvcg_default_processing_attr_i_processing, | |
46919a23 AP |
265 | NULL, |
266 | }; | |
267 | ||
268 | static struct config_item_type uvcg_default_processing_type = { | |
46919a23 AP |
269 | .ct_attrs = uvcg_default_processing_attrs, |
270 | .ct_owner = THIS_MODULE, | |
271 | }; | |
272 | ||
273 | /* struct uvcg_processing {}; */ | |
274 | ||
46919a23 AP |
275 | /* control/processing */ |
276 | static struct uvcg_processing_grp { | |
277 | struct config_group group; | |
278 | } uvcg_processing_grp; | |
279 | ||
280 | static struct config_item_type uvcg_processing_grp_type = { | |
281 | .ct_owner = THIS_MODULE, | |
282 | }; | |
283 | ||
284 | /* control/terminal/camera/default */ | |
285 | static struct uvcg_default_camera { | |
286 | struct config_group group; | |
287 | } uvcg_default_camera; | |
288 | ||
289 | static inline struct uvcg_default_camera | |
290 | *to_uvcg_default_camera(struct config_item *item) | |
291 | { | |
292 | return container_of(to_config_group(item), | |
293 | struct uvcg_default_camera, group); | |
294 | } | |
295 | ||
46919a23 AP |
296 | #define UVCG_DEFAULT_CAMERA_ATTR(cname, aname, conv) \ |
297 | static ssize_t uvcg_default_camera_##cname##_show( \ | |
76e0da34 | 298 | struct config_item *item, char *page) \ |
46919a23 | 299 | { \ |
76e0da34 | 300 | struct uvcg_default_camera *dc = to_uvcg_default_camera(item); \ |
46919a23 AP |
301 | struct f_uvc_opts *opts; \ |
302 | struct config_item *opts_item; \ | |
303 | struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ | |
304 | struct uvc_camera_terminal_descriptor *cd; \ | |
305 | int result; \ | |
306 | \ | |
307 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
308 | \ | |
309 | opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> \ | |
310 | ci_parent; \ | |
311 | opts = to_f_uvc_opts(opts_item); \ | |
312 | cd = &opts->uvc_camera_terminal; \ | |
313 | \ | |
314 | mutex_lock(&opts->lock); \ | |
315 | result = sprintf(page, "%d\n", conv(cd->aname)); \ | |
316 | mutex_unlock(&opts->lock); \ | |
317 | \ | |
318 | mutex_unlock(su_mutex); \ | |
319 | \ | |
320 | return result; \ | |
321 | } \ | |
322 | \ | |
76e0da34 | 323 | UVC_ATTR_RO(uvcg_default_camera_, cname, aname) |
46919a23 AP |
324 | |
325 | #define identity_conv(x) (x) | |
326 | ||
327 | UVCG_DEFAULT_CAMERA_ATTR(b_terminal_id, bTerminalID, identity_conv); | |
328 | UVCG_DEFAULT_CAMERA_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); | |
329 | UVCG_DEFAULT_CAMERA_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); | |
330 | UVCG_DEFAULT_CAMERA_ATTR(i_terminal, iTerminal, identity_conv); | |
331 | UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_min, wObjectiveFocalLengthMin, | |
332 | le16_to_cpu); | |
333 | UVCG_DEFAULT_CAMERA_ATTR(w_objective_focal_length_max, wObjectiveFocalLengthMax, | |
334 | le16_to_cpu); | |
335 | UVCG_DEFAULT_CAMERA_ATTR(w_ocular_focal_length, wOcularFocalLength, | |
336 | le16_to_cpu); | |
337 | ||
338 | #undef identity_conv | |
339 | ||
340 | #undef UVCG_DEFAULT_CAMERA_ATTR | |
341 | ||
342 | static ssize_t uvcg_default_camera_bm_controls_show( | |
76e0da34 | 343 | struct config_item *item, char *page) |
46919a23 | 344 | { |
76e0da34 | 345 | struct uvcg_default_camera *dc = to_uvcg_default_camera(item); |
46919a23 AP |
346 | struct f_uvc_opts *opts; |
347 | struct config_item *opts_item; | |
348 | struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; | |
349 | struct uvc_camera_terminal_descriptor *cd; | |
350 | int result, i; | |
351 | char *pg = page; | |
352 | ||
353 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
354 | ||
355 | opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent-> | |
356 | ci_parent; | |
357 | opts = to_f_uvc_opts(opts_item); | |
358 | cd = &opts->uvc_camera_terminal; | |
359 | ||
360 | mutex_lock(&opts->lock); | |
361 | for (result = 0, i = 0; i < cd->bControlSize; ++i) { | |
362 | result += sprintf(pg, "%d\n", cd->bmControls[i]); | |
363 | pg = page + result; | |
364 | } | |
365 | mutex_unlock(&opts->lock); | |
366 | ||
367 | mutex_unlock(su_mutex); | |
368 | return result; | |
369 | } | |
370 | ||
76e0da34 | 371 | UVC_ATTR_RO(uvcg_default_camera_, bm_controls, bmControls); |
46919a23 AP |
372 | |
373 | static struct configfs_attribute *uvcg_default_camera_attrs[] = { | |
76e0da34 CH |
374 | &uvcg_default_camera_attr_b_terminal_id, |
375 | &uvcg_default_camera_attr_w_terminal_type, | |
376 | &uvcg_default_camera_attr_b_assoc_terminal, | |
377 | &uvcg_default_camera_attr_i_terminal, | |
378 | &uvcg_default_camera_attr_w_objective_focal_length_min, | |
379 | &uvcg_default_camera_attr_w_objective_focal_length_max, | |
380 | &uvcg_default_camera_attr_w_ocular_focal_length, | |
381 | &uvcg_default_camera_attr_bm_controls, | |
46919a23 AP |
382 | NULL, |
383 | }; | |
384 | ||
385 | static struct config_item_type uvcg_default_camera_type = { | |
46919a23 AP |
386 | .ct_attrs = uvcg_default_camera_attrs, |
387 | .ct_owner = THIS_MODULE, | |
388 | }; | |
389 | ||
390 | /* struct uvcg_camera {}; */ | |
391 | ||
46919a23 AP |
392 | /* control/terminal/camera */ |
393 | static struct uvcg_camera_grp { | |
394 | struct config_group group; | |
395 | } uvcg_camera_grp; | |
396 | ||
397 | static struct config_item_type uvcg_camera_grp_type = { | |
398 | .ct_owner = THIS_MODULE, | |
399 | }; | |
400 | ||
401 | /* control/terminal/output/default */ | |
402 | static struct uvcg_default_output { | |
403 | struct config_group group; | |
404 | } uvcg_default_output; | |
405 | ||
406 | static inline struct uvcg_default_output | |
407 | *to_uvcg_default_output(struct config_item *item) | |
408 | { | |
409 | return container_of(to_config_group(item), | |
410 | struct uvcg_default_output, group); | |
411 | } | |
412 | ||
46919a23 AP |
413 | #define UVCG_DEFAULT_OUTPUT_ATTR(cname, aname, conv) \ |
414 | static ssize_t uvcg_default_output_##cname##_show( \ | |
76e0da34 | 415 | struct config_item *item, char *page) \ |
46919a23 | 416 | { \ |
76e0da34 | 417 | struct uvcg_default_output *dout = to_uvcg_default_output(item); \ |
46919a23 AP |
418 | struct f_uvc_opts *opts; \ |
419 | struct config_item *opts_item; \ | |
420 | struct mutex *su_mutex = &dout->group.cg_subsys->su_mutex; \ | |
421 | struct uvc_output_terminal_descriptor *cd; \ | |
422 | int result; \ | |
423 | \ | |
424 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
425 | \ | |
426 | opts_item = dout->group.cg_item.ci_parent->ci_parent-> \ | |
427 | ci_parent->ci_parent; \ | |
428 | opts = to_f_uvc_opts(opts_item); \ | |
429 | cd = &opts->uvc_output_terminal; \ | |
430 | \ | |
431 | mutex_lock(&opts->lock); \ | |
432 | result = sprintf(page, "%d\n", conv(cd->aname)); \ | |
433 | mutex_unlock(&opts->lock); \ | |
434 | \ | |
435 | mutex_unlock(su_mutex); \ | |
436 | \ | |
437 | return result; \ | |
438 | } \ | |
439 | \ | |
76e0da34 | 440 | UVC_ATTR_RO(uvcg_default_output_, cname, aname) |
46919a23 AP |
441 | |
442 | #define identity_conv(x) (x) | |
443 | ||
444 | UVCG_DEFAULT_OUTPUT_ATTR(b_terminal_id, bTerminalID, identity_conv); | |
445 | UVCG_DEFAULT_OUTPUT_ATTR(w_terminal_type, wTerminalType, le16_to_cpu); | |
446 | UVCG_DEFAULT_OUTPUT_ATTR(b_assoc_terminal, bAssocTerminal, identity_conv); | |
447 | UVCG_DEFAULT_OUTPUT_ATTR(b_source_id, bSourceID, identity_conv); | |
448 | UVCG_DEFAULT_OUTPUT_ATTR(i_terminal, iTerminal, identity_conv); | |
449 | ||
450 | #undef identity_conv | |
451 | ||
452 | #undef UVCG_DEFAULT_OUTPUT_ATTR | |
453 | ||
454 | static struct configfs_attribute *uvcg_default_output_attrs[] = { | |
76e0da34 CH |
455 | &uvcg_default_output_attr_b_terminal_id, |
456 | &uvcg_default_output_attr_w_terminal_type, | |
457 | &uvcg_default_output_attr_b_assoc_terminal, | |
458 | &uvcg_default_output_attr_b_source_id, | |
459 | &uvcg_default_output_attr_i_terminal, | |
46919a23 AP |
460 | NULL, |
461 | }; | |
462 | ||
463 | static struct config_item_type uvcg_default_output_type = { | |
46919a23 AP |
464 | .ct_attrs = uvcg_default_output_attrs, |
465 | .ct_owner = THIS_MODULE, | |
466 | }; | |
467 | ||
468 | /* struct uvcg_output {}; */ | |
469 | ||
46919a23 AP |
470 | /* control/terminal/output */ |
471 | static struct uvcg_output_grp { | |
472 | struct config_group group; | |
473 | } uvcg_output_grp; | |
474 | ||
475 | static struct config_item_type uvcg_output_grp_type = { | |
476 | .ct_owner = THIS_MODULE, | |
477 | }; | |
478 | ||
46919a23 AP |
479 | /* control/terminal */ |
480 | static struct uvcg_terminal_grp { | |
481 | struct config_group group; | |
482 | } uvcg_terminal_grp; | |
483 | ||
484 | static struct config_item_type uvcg_terminal_grp_type = { | |
485 | .ct_owner = THIS_MODULE, | |
486 | }; | |
487 | ||
488 | /* control/class/{fs} */ | |
489 | static struct uvcg_control_class { | |
490 | struct config_group group; | |
491 | } uvcg_control_class_fs, uvcg_control_class_ss; | |
492 | ||
493 | ||
494 | static inline struct uvc_descriptor_header | |
495 | **uvcg_get_ctl_class_arr(struct config_item *i, struct f_uvc_opts *o) | |
496 | { | |
497 | struct uvcg_control_class *cl = container_of(to_config_group(i), | |
498 | struct uvcg_control_class, group); | |
499 | ||
500 | if (cl == &uvcg_control_class_fs) | |
501 | return o->uvc_fs_control_cls; | |
502 | ||
503 | if (cl == &uvcg_control_class_ss) | |
504 | return o->uvc_ss_control_cls; | |
505 | ||
506 | return NULL; | |
507 | } | |
508 | ||
509 | static int uvcg_control_class_allow_link(struct config_item *src, | |
510 | struct config_item *target) | |
511 | { | |
512 | struct config_item *control, *header; | |
513 | struct f_uvc_opts *opts; | |
514 | struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; | |
515 | struct uvc_descriptor_header **class_array; | |
516 | struct uvcg_control_header *target_hdr; | |
517 | int ret = -EINVAL; | |
518 | ||
519 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
520 | ||
521 | control = src->ci_parent->ci_parent; | |
522 | header = config_group_find_item(to_config_group(control), "header"); | |
523 | if (!header || target->ci_parent != header) | |
524 | goto out; | |
525 | ||
526 | opts = to_f_uvc_opts(control->ci_parent); | |
527 | ||
528 | mutex_lock(&opts->lock); | |
529 | ||
530 | class_array = uvcg_get_ctl_class_arr(src, opts); | |
531 | if (!class_array) | |
532 | goto unlock; | |
533 | if (opts->refcnt || class_array[0]) { | |
534 | ret = -EBUSY; | |
535 | goto unlock; | |
536 | } | |
537 | ||
538 | target_hdr = to_uvcg_control_header(target); | |
539 | ++target_hdr->linked; | |
540 | class_array[0] = (struct uvc_descriptor_header *)&target_hdr->desc; | |
541 | ret = 0; | |
542 | ||
543 | unlock: | |
544 | mutex_unlock(&opts->lock); | |
545 | out: | |
546 | mutex_unlock(su_mutex); | |
547 | return ret; | |
548 | } | |
549 | ||
550 | static int uvcg_control_class_drop_link(struct config_item *src, | |
551 | struct config_item *target) | |
552 | { | |
553 | struct config_item *control, *header; | |
554 | struct f_uvc_opts *opts; | |
555 | struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; | |
556 | struct uvc_descriptor_header **class_array; | |
557 | struct uvcg_control_header *target_hdr; | |
558 | int ret = -EINVAL; | |
559 | ||
560 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
561 | ||
562 | control = src->ci_parent->ci_parent; | |
563 | header = config_group_find_item(to_config_group(control), "header"); | |
564 | if (!header || target->ci_parent != header) | |
565 | goto out; | |
566 | ||
567 | opts = to_f_uvc_opts(control->ci_parent); | |
568 | ||
569 | mutex_lock(&opts->lock); | |
570 | ||
571 | class_array = uvcg_get_ctl_class_arr(src, opts); | |
572 | if (!class_array) | |
573 | goto unlock; | |
574 | if (opts->refcnt) { | |
575 | ret = -EBUSY; | |
576 | goto unlock; | |
577 | } | |
578 | ||
579 | target_hdr = to_uvcg_control_header(target); | |
580 | --target_hdr->linked; | |
581 | class_array[0] = NULL; | |
582 | ret = 0; | |
583 | ||
584 | unlock: | |
585 | mutex_unlock(&opts->lock); | |
586 | out: | |
587 | mutex_unlock(su_mutex); | |
588 | return ret; | |
589 | } | |
590 | ||
591 | static struct configfs_item_operations uvcg_control_class_item_ops = { | |
592 | .allow_link = uvcg_control_class_allow_link, | |
593 | .drop_link = uvcg_control_class_drop_link, | |
594 | }; | |
595 | ||
596 | static struct config_item_type uvcg_control_class_type = { | |
597 | .ct_item_ops = &uvcg_control_class_item_ops, | |
598 | .ct_owner = THIS_MODULE, | |
599 | }; | |
600 | ||
46919a23 AP |
601 | /* control/class */ |
602 | static struct uvcg_control_class_grp { | |
603 | struct config_group group; | |
604 | } uvcg_control_class_grp; | |
605 | ||
606 | static struct config_item_type uvcg_control_class_grp_type = { | |
607 | .ct_owner = THIS_MODULE, | |
608 | }; | |
609 | ||
46919a23 AP |
610 | /* control */ |
611 | static struct uvcg_control_grp { | |
612 | struct config_group group; | |
613 | } uvcg_control_grp; | |
614 | ||
615 | static struct config_item_type uvcg_control_grp_type = { | |
616 | .ct_owner = THIS_MODULE, | |
617 | }; | |
618 | ||
619 | /* streaming/uncompressed */ | |
620 | static struct uvcg_uncompressed_grp { | |
621 | struct config_group group; | |
622 | } uvcg_uncompressed_grp; | |
623 | ||
624 | /* streaming/mjpeg */ | |
625 | static struct uvcg_mjpeg_grp { | |
626 | struct config_group group; | |
627 | } uvcg_mjpeg_grp; | |
628 | ||
629 | static struct config_item *fmt_parent[] = { | |
630 | &uvcg_uncompressed_grp.group.cg_item, | |
631 | &uvcg_mjpeg_grp.group.cg_item, | |
632 | }; | |
633 | ||
634 | enum uvcg_format_type { | |
635 | UVCG_UNCOMPRESSED = 0, | |
636 | UVCG_MJPEG, | |
637 | }; | |
638 | ||
639 | struct uvcg_format { | |
640 | struct config_group group; | |
641 | enum uvcg_format_type type; | |
642 | unsigned linked; | |
643 | unsigned num_frames; | |
644 | __u8 bmaControls[UVCG_STREAMING_CONTROL_SIZE]; | |
645 | }; | |
646 | ||
f093a2d4 | 647 | static struct uvcg_format *to_uvcg_format(struct config_item *item) |
46919a23 AP |
648 | { |
649 | return container_of(to_config_group(item), struct uvcg_format, group); | |
650 | } | |
651 | ||
652 | static ssize_t uvcg_format_bma_controls_show(struct uvcg_format *f, char *page) | |
653 | { | |
654 | struct f_uvc_opts *opts; | |
655 | struct config_item *opts_item; | |
656 | struct mutex *su_mutex = &f->group.cg_subsys->su_mutex; | |
657 | int result, i; | |
658 | char *pg = page; | |
659 | ||
660 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
661 | ||
662 | opts_item = f->group.cg_item.ci_parent->ci_parent->ci_parent; | |
663 | opts = to_f_uvc_opts(opts_item); | |
664 | ||
665 | mutex_lock(&opts->lock); | |
666 | result = sprintf(pg, "0x"); | |
667 | pg += result; | |
668 | for (i = 0; i < UVCG_STREAMING_CONTROL_SIZE; ++i) { | |
669 | result += sprintf(pg, "%x\n", f->bmaControls[i]); | |
670 | pg = page + result; | |
671 | } | |
672 | mutex_unlock(&opts->lock); | |
673 | ||
674 | mutex_unlock(su_mutex); | |
675 | return result; | |
676 | } | |
677 | ||
678 | static ssize_t uvcg_format_bma_controls_store(struct uvcg_format *ch, | |
679 | const char *page, size_t len) | |
680 | { | |
681 | struct f_uvc_opts *opts; | |
682 | struct config_item *opts_item; | |
683 | struct mutex *su_mutex = &ch->group.cg_subsys->su_mutex; | |
684 | int ret = -EINVAL; | |
685 | ||
686 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
687 | ||
688 | opts_item = ch->group.cg_item.ci_parent->ci_parent->ci_parent; | |
689 | opts = to_f_uvc_opts(opts_item); | |
690 | ||
691 | mutex_lock(&opts->lock); | |
692 | if (ch->linked || opts->refcnt) { | |
693 | ret = -EBUSY; | |
694 | goto end; | |
695 | } | |
696 | ||
697 | if (len < 4 || *page != '0' || | |
698 | (*(page + 1) != 'x' && *(page + 1) != 'X')) | |
699 | goto end; | |
700 | ret = hex2bin(ch->bmaControls, page + 2, 1); | |
701 | if (ret < 0) | |
702 | goto end; | |
703 | ret = len; | |
704 | end: | |
705 | mutex_unlock(&opts->lock); | |
706 | mutex_unlock(su_mutex); | |
707 | return ret; | |
708 | } | |
709 | ||
710 | struct uvcg_format_ptr { | |
711 | struct uvcg_format *fmt; | |
712 | struct list_head entry; | |
713 | }; | |
714 | ||
715 | /* streaming/header/<NAME> */ | |
716 | struct uvcg_streaming_header { | |
717 | struct config_item item; | |
718 | struct uvc_input_header_descriptor desc; | |
719 | unsigned linked; | |
720 | struct list_head formats; | |
721 | unsigned num_fmt; | |
722 | }; | |
723 | ||
f093a2d4 | 724 | static struct uvcg_streaming_header *to_uvcg_streaming_header(struct config_item *item) |
46919a23 AP |
725 | { |
726 | return container_of(item, struct uvcg_streaming_header, item); | |
727 | } | |
728 | ||
46919a23 AP |
729 | static int uvcg_streaming_header_allow_link(struct config_item *src, |
730 | struct config_item *target) | |
731 | { | |
732 | struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; | |
733 | struct config_item *opts_item; | |
734 | struct f_uvc_opts *opts; | |
735 | struct uvcg_streaming_header *src_hdr; | |
736 | struct uvcg_format *target_fmt = NULL; | |
737 | struct uvcg_format_ptr *format_ptr; | |
738 | int i, ret = -EINVAL; | |
739 | ||
740 | src_hdr = to_uvcg_streaming_header(src); | |
741 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
742 | ||
743 | opts_item = src->ci_parent->ci_parent->ci_parent; | |
744 | opts = to_f_uvc_opts(opts_item); | |
745 | ||
746 | mutex_lock(&opts->lock); | |
747 | ||
748 | if (src_hdr->linked) { | |
749 | ret = -EBUSY; | |
750 | goto out; | |
751 | } | |
752 | ||
753 | for (i = 0; i < ARRAY_SIZE(fmt_parent); ++i) | |
754 | if (target->ci_parent == fmt_parent[i]) | |
755 | break; | |
756 | if (i == ARRAY_SIZE(fmt_parent)) | |
757 | goto out; | |
758 | ||
759 | target_fmt = container_of(to_config_group(target), struct uvcg_format, | |
760 | group); | |
761 | if (!target_fmt) | |
762 | goto out; | |
763 | ||
764 | format_ptr = kzalloc(sizeof(*format_ptr), GFP_KERNEL); | |
765 | if (!format_ptr) { | |
df90f838 | 766 | ret = -ENOMEM; |
46919a23 AP |
767 | goto out; |
768 | } | |
769 | ret = 0; | |
770 | format_ptr->fmt = target_fmt; | |
771 | list_add_tail(&format_ptr->entry, &src_hdr->formats); | |
772 | ++src_hdr->num_fmt; | |
773 | ||
774 | out: | |
775 | mutex_unlock(&opts->lock); | |
776 | mutex_unlock(su_mutex); | |
777 | return ret; | |
778 | } | |
779 | ||
780 | static int uvcg_streaming_header_drop_link(struct config_item *src, | |
781 | struct config_item *target) | |
782 | { | |
783 | struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; | |
784 | struct config_item *opts_item; | |
785 | struct f_uvc_opts *opts; | |
786 | struct uvcg_streaming_header *src_hdr; | |
787 | struct uvcg_format *target_fmt = NULL; | |
788 | struct uvcg_format_ptr *format_ptr, *tmp; | |
789 | int ret = -EINVAL; | |
790 | ||
791 | src_hdr = to_uvcg_streaming_header(src); | |
792 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
793 | ||
794 | opts_item = src->ci_parent->ci_parent->ci_parent; | |
795 | opts = to_f_uvc_opts(opts_item); | |
796 | ||
797 | mutex_lock(&opts->lock); | |
798 | target_fmt = container_of(to_config_group(target), struct uvcg_format, | |
799 | group); | |
800 | if (!target_fmt) | |
801 | goto out; | |
802 | ||
803 | list_for_each_entry_safe(format_ptr, tmp, &src_hdr->formats, entry) | |
804 | if (format_ptr->fmt == target_fmt) { | |
805 | list_del(&format_ptr->entry); | |
806 | kfree(format_ptr); | |
807 | --src_hdr->num_fmt; | |
808 | break; | |
809 | } | |
810 | ||
811 | out: | |
812 | mutex_unlock(&opts->lock); | |
813 | mutex_unlock(su_mutex); | |
814 | return ret; | |
815 | ||
816 | } | |
817 | ||
818 | static struct configfs_item_operations uvcg_streaming_header_item_ops = { | |
46919a23 AP |
819 | .allow_link = uvcg_streaming_header_allow_link, |
820 | .drop_link = uvcg_streaming_header_drop_link, | |
821 | }; | |
822 | ||
823 | #define UVCG_STREAMING_HEADER_ATTR(cname, aname, conv) \ | |
824 | static ssize_t uvcg_streaming_header_##cname##_show( \ | |
76e0da34 | 825 | struct config_item *item, char *page) \ |
46919a23 | 826 | { \ |
76e0da34 | 827 | struct uvcg_streaming_header *sh = to_uvcg_streaming_header(item); \ |
46919a23 AP |
828 | struct f_uvc_opts *opts; \ |
829 | struct config_item *opts_item; \ | |
830 | struct mutex *su_mutex = &sh->item.ci_group->cg_subsys->su_mutex;\ | |
831 | int result; \ | |
832 | \ | |
833 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
834 | \ | |
835 | opts_item = sh->item.ci_parent->ci_parent->ci_parent; \ | |
836 | opts = to_f_uvc_opts(opts_item); \ | |
837 | \ | |
838 | mutex_lock(&opts->lock); \ | |
839 | result = sprintf(page, "%d\n", conv(sh->desc.aname)); \ | |
840 | mutex_unlock(&opts->lock); \ | |
841 | \ | |
842 | mutex_unlock(su_mutex); \ | |
843 | return result; \ | |
844 | } \ | |
845 | \ | |
76e0da34 | 846 | UVC_ATTR_RO(uvcg_streaming_header_, cname, aname) |
46919a23 AP |
847 | |
848 | #define identity_conv(x) (x) | |
849 | ||
850 | UVCG_STREAMING_HEADER_ATTR(bm_info, bmInfo, identity_conv); | |
851 | UVCG_STREAMING_HEADER_ATTR(b_terminal_link, bTerminalLink, identity_conv); | |
852 | UVCG_STREAMING_HEADER_ATTR(b_still_capture_method, bStillCaptureMethod, | |
853 | identity_conv); | |
854 | UVCG_STREAMING_HEADER_ATTR(b_trigger_support, bTriggerSupport, identity_conv); | |
855 | UVCG_STREAMING_HEADER_ATTR(b_trigger_usage, bTriggerUsage, identity_conv); | |
856 | ||
857 | #undef identity_conv | |
858 | ||
859 | #undef UVCG_STREAMING_HEADER_ATTR | |
860 | ||
861 | static struct configfs_attribute *uvcg_streaming_header_attrs[] = { | |
76e0da34 CH |
862 | &uvcg_streaming_header_attr_bm_info, |
863 | &uvcg_streaming_header_attr_b_terminal_link, | |
864 | &uvcg_streaming_header_attr_b_still_capture_method, | |
865 | &uvcg_streaming_header_attr_b_trigger_support, | |
866 | &uvcg_streaming_header_attr_b_trigger_usage, | |
46919a23 AP |
867 | NULL, |
868 | }; | |
869 | ||
f093a2d4 | 870 | static struct config_item_type uvcg_streaming_header_type = { |
46919a23 AP |
871 | .ct_item_ops = &uvcg_streaming_header_item_ops, |
872 | .ct_attrs = uvcg_streaming_header_attrs, | |
873 | .ct_owner = THIS_MODULE, | |
874 | }; | |
875 | ||
876 | static struct config_item | |
877 | *uvcg_streaming_header_make(struct config_group *group, const char *name) | |
878 | { | |
879 | struct uvcg_streaming_header *h; | |
880 | ||
881 | h = kzalloc(sizeof(*h), GFP_KERNEL); | |
882 | if (!h) | |
df90f838 | 883 | return ERR_PTR(-ENOMEM); |
46919a23 AP |
884 | |
885 | INIT_LIST_HEAD(&h->formats); | |
886 | h->desc.bDescriptorType = USB_DT_CS_INTERFACE; | |
887 | h->desc.bDescriptorSubType = UVC_VS_INPUT_HEADER; | |
888 | h->desc.bTerminalLink = 3; | |
889 | h->desc.bControlSize = UVCG_STREAMING_CONTROL_SIZE; | |
890 | ||
891 | config_item_init_type_name(&h->item, name, &uvcg_streaming_header_type); | |
892 | ||
893 | return &h->item; | |
894 | } | |
895 | ||
f093a2d4 | 896 | static void uvcg_streaming_header_drop(struct config_group *group, |
46919a23 AP |
897 | struct config_item *item) |
898 | { | |
899 | struct uvcg_streaming_header *h = to_uvcg_streaming_header(item); | |
900 | ||
901 | kfree(h); | |
902 | } | |
903 | ||
904 | /* streaming/header */ | |
905 | static struct uvcg_streaming_header_grp { | |
906 | struct config_group group; | |
907 | } uvcg_streaming_header_grp; | |
908 | ||
909 | static struct configfs_group_operations uvcg_streaming_header_grp_ops = { | |
910 | .make_item = uvcg_streaming_header_make, | |
911 | .drop_item = uvcg_streaming_header_drop, | |
912 | }; | |
913 | ||
914 | static struct config_item_type uvcg_streaming_header_grp_type = { | |
915 | .ct_group_ops = &uvcg_streaming_header_grp_ops, | |
916 | .ct_owner = THIS_MODULE, | |
917 | }; | |
918 | ||
919 | /* streaming/<mode>/<format>/<NAME> */ | |
920 | struct uvcg_frame { | |
921 | struct { | |
922 | u8 b_length; | |
923 | u8 b_descriptor_type; | |
924 | u8 b_descriptor_subtype; | |
925 | u8 b_frame_index; | |
926 | u8 bm_capabilities; | |
927 | u16 w_width; | |
928 | u16 w_height; | |
929 | u32 dw_min_bit_rate; | |
930 | u32 dw_max_bit_rate; | |
931 | u32 dw_max_video_frame_buffer_size; | |
932 | u32 dw_default_frame_interval; | |
933 | u8 b_frame_interval_type; | |
934 | } __attribute__((packed)) frame; | |
935 | u32 *dw_frame_interval; | |
936 | enum uvcg_format_type fmt_type; | |
937 | struct config_item item; | |
938 | }; | |
939 | ||
f093a2d4 | 940 | static struct uvcg_frame *to_uvcg_frame(struct config_item *item) |
46919a23 AP |
941 | { |
942 | return container_of(item, struct uvcg_frame, item); | |
943 | } | |
944 | ||
3c4c733c | 945 | #define UVCG_FRAME_ATTR(cname, aname, to_cpu_endian, to_little_endian, bits) \ |
76e0da34 | 946 | static ssize_t uvcg_frame_##cname##_show(struct config_item *item, char *page)\ |
46919a23 | 947 | { \ |
76e0da34 | 948 | struct uvcg_frame *f = to_uvcg_frame(item); \ |
46919a23 AP |
949 | struct f_uvc_opts *opts; \ |
950 | struct config_item *opts_item; \ | |
951 | struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ | |
952 | int result; \ | |
953 | \ | |
954 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
955 | \ | |
956 | opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ | |
957 | opts = to_f_uvc_opts(opts_item); \ | |
958 | \ | |
959 | mutex_lock(&opts->lock); \ | |
3c4c733c | 960 | result = sprintf(page, "%d\n", to_cpu_endian(f->frame.cname)); \ |
46919a23 AP |
961 | mutex_unlock(&opts->lock); \ |
962 | \ | |
963 | mutex_unlock(su_mutex); \ | |
964 | return result; \ | |
965 | } \ | |
966 | \ | |
76e0da34 | 967 | static ssize_t uvcg_frame_##cname##_store(struct config_item *item, \ |
46919a23 AP |
968 | const char *page, size_t len)\ |
969 | { \ | |
76e0da34 | 970 | struct uvcg_frame *f = to_uvcg_frame(item); \ |
46919a23 AP |
971 | struct f_uvc_opts *opts; \ |
972 | struct config_item *opts_item; \ | |
973 | struct uvcg_format *fmt; \ | |
974 | struct mutex *su_mutex = &f->item.ci_group->cg_subsys->su_mutex;\ | |
975 | int ret; \ | |
3c4c733c DC |
976 | u##bits num; \ |
977 | \ | |
978 | ret = kstrtou##bits(page, 0, &num); \ | |
979 | if (ret) \ | |
980 | return ret; \ | |
46919a23 AP |
981 | \ |
982 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
983 | \ | |
984 | opts_item = f->item.ci_parent->ci_parent->ci_parent->ci_parent; \ | |
985 | opts = to_f_uvc_opts(opts_item); \ | |
986 | fmt = to_uvcg_format(f->item.ci_parent); \ | |
987 | \ | |
988 | mutex_lock(&opts->lock); \ | |
989 | if (fmt->linked || opts->refcnt) { \ | |
990 | ret = -EBUSY; \ | |
991 | goto end; \ | |
992 | } \ | |
993 | \ | |
3c4c733c | 994 | f->frame.cname = to_little_endian(num); \ |
46919a23 AP |
995 | ret = len; \ |
996 | end: \ | |
997 | mutex_unlock(&opts->lock); \ | |
998 | mutex_unlock(su_mutex); \ | |
999 | return ret; \ | |
1000 | } \ | |
1001 | \ | |
76e0da34 | 1002 | UVC_ATTR(uvcg_frame_, cname, aname); |
46919a23 | 1003 | |
3c4c733c | 1004 | #define noop_conversion(x) (x) |
46919a23 | 1005 | |
3c4c733c DC |
1006 | UVCG_FRAME_ATTR(bm_capabilities, bmCapabilities, noop_conversion, |
1007 | noop_conversion, 8); | |
1008 | UVCG_FRAME_ATTR(w_width, wWidth, le16_to_cpu, cpu_to_le16, 16); | |
1009 | UVCG_FRAME_ATTR(w_height, wHeight, le16_to_cpu, cpu_to_le16, 16); | |
1010 | UVCG_FRAME_ATTR(dw_min_bit_rate, dwMinBitRate, le32_to_cpu, cpu_to_le32, 32); | |
1011 | UVCG_FRAME_ATTR(dw_max_bit_rate, dwMaxBitRate, le32_to_cpu, cpu_to_le32, 32); | |
46919a23 | 1012 | UVCG_FRAME_ATTR(dw_max_video_frame_buffer_size, dwMaxVideoFrameBufferSize, |
3c4c733c | 1013 | le32_to_cpu, cpu_to_le32, 32); |
46919a23 | 1014 | UVCG_FRAME_ATTR(dw_default_frame_interval, dwDefaultFrameInterval, |
3c4c733c | 1015 | le32_to_cpu, cpu_to_le32, 32); |
46919a23 | 1016 | |
3c4c733c | 1017 | #undef noop_conversion |
46919a23 AP |
1018 | |
1019 | #undef UVCG_FRAME_ATTR | |
1020 | ||
76e0da34 | 1021 | static ssize_t uvcg_frame_dw_frame_interval_show(struct config_item *item, |
46919a23 AP |
1022 | char *page) |
1023 | { | |
76e0da34 | 1024 | struct uvcg_frame *frm = to_uvcg_frame(item); |
46919a23 AP |
1025 | struct f_uvc_opts *opts; |
1026 | struct config_item *opts_item; | |
1027 | struct mutex *su_mutex = &frm->item.ci_group->cg_subsys->su_mutex; | |
1028 | int result, i; | |
1029 | char *pg = page; | |
1030 | ||
1031 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
1032 | ||
1033 | opts_item = frm->item.ci_parent->ci_parent->ci_parent->ci_parent; | |
1034 | opts = to_f_uvc_opts(opts_item); | |
1035 | ||
1036 | mutex_lock(&opts->lock); | |
1037 | for (result = 0, i = 0; i < frm->frame.b_frame_interval_type; ++i) { | |
1038 | result += sprintf(pg, "%d\n", | |
1039 | le32_to_cpu(frm->dw_frame_interval[i])); | |
1040 | pg = page + result; | |
1041 | } | |
1042 | mutex_unlock(&opts->lock); | |
1043 | ||
1044 | mutex_unlock(su_mutex); | |
1045 | return result; | |
1046 | } | |
1047 | ||
1048 | static inline int __uvcg_count_frm_intrv(char *buf, void *priv) | |
1049 | { | |
1050 | ++*((int *)priv); | |
1051 | return 0; | |
1052 | } | |
1053 | ||
1054 | static inline int __uvcg_fill_frm_intrv(char *buf, void *priv) | |
1055 | { | |
1056 | u32 num, **interv; | |
1057 | int ret; | |
1058 | ||
1059 | ret = kstrtou32(buf, 0, &num); | |
1060 | if (ret) | |
1061 | return ret; | |
46919a23 AP |
1062 | |
1063 | interv = priv; | |
1064 | **interv = cpu_to_le32(num); | |
1065 | ++*interv; | |
1066 | ||
1067 | return 0; | |
1068 | } | |
1069 | ||
1070 | static int __uvcg_iter_frm_intrv(const char *page, size_t len, | |
1071 | int (*fun)(char *, void *), void *priv) | |
1072 | { | |
1073 | /* sign, base 2 representation, newline, terminator */ | |
1074 | char buf[1 + sizeof(u32) * 8 + 1 + 1]; | |
1075 | const char *pg = page; | |
1076 | int i, ret; | |
1077 | ||
1078 | if (!fun) | |
1079 | return -EINVAL; | |
1080 | ||
1081 | while (pg - page < len) { | |
1082 | i = 0; | |
1083 | while (i < sizeof(buf) && (pg - page < len) && | |
1084 | *pg != '\0' && *pg != '\n') | |
1085 | buf[i++] = *pg++; | |
1086 | if (i == sizeof(buf)) | |
1087 | return -EINVAL; | |
1088 | while ((pg - page < len) && (*pg == '\0' || *pg == '\n')) | |
1089 | ++pg; | |
1090 | buf[i] = '\0'; | |
1091 | ret = fun(buf, priv); | |
1092 | if (ret) | |
1093 | return ret; | |
1094 | } | |
1095 | ||
1096 | return 0; | |
1097 | } | |
1098 | ||
76e0da34 | 1099 | static ssize_t uvcg_frame_dw_frame_interval_store(struct config_item *item, |
46919a23 AP |
1100 | const char *page, size_t len) |
1101 | { | |
76e0da34 | 1102 | struct uvcg_frame *ch = to_uvcg_frame(item); |
46919a23 AP |
1103 | struct f_uvc_opts *opts; |
1104 | struct config_item *opts_item; | |
1105 | struct uvcg_format *fmt; | |
1106 | struct mutex *su_mutex = &ch->item.ci_group->cg_subsys->su_mutex; | |
1107 | int ret = 0, n = 0; | |
1108 | u32 *frm_intrv, *tmp; | |
1109 | ||
1110 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
1111 | ||
1112 | opts_item = ch->item.ci_parent->ci_parent->ci_parent->ci_parent; | |
1113 | opts = to_f_uvc_opts(opts_item); | |
1114 | fmt = to_uvcg_format(ch->item.ci_parent); | |
1115 | ||
1116 | mutex_lock(&opts->lock); | |
1117 | if (fmt->linked || opts->refcnt) { | |
1118 | ret = -EBUSY; | |
1119 | goto end; | |
1120 | } | |
1121 | ||
1122 | ret = __uvcg_iter_frm_intrv(page, len, __uvcg_count_frm_intrv, &n); | |
1123 | if (ret) | |
1124 | goto end; | |
1125 | ||
1126 | tmp = frm_intrv = kcalloc(n, sizeof(u32), GFP_KERNEL); | |
1127 | if (!frm_intrv) { | |
1128 | ret = -ENOMEM; | |
1129 | goto end; | |
1130 | } | |
1131 | ||
1132 | ret = __uvcg_iter_frm_intrv(page, len, __uvcg_fill_frm_intrv, &tmp); | |
1133 | if (ret) { | |
1134 | kfree(frm_intrv); | |
1135 | goto end; | |
1136 | } | |
1137 | ||
1138 | kfree(ch->dw_frame_interval); | |
1139 | ch->dw_frame_interval = frm_intrv; | |
1140 | ch->frame.b_frame_interval_type = n; | |
1141 | ret = len; | |
1142 | ||
1143 | end: | |
1144 | mutex_unlock(&opts->lock); | |
1145 | mutex_unlock(su_mutex); | |
1146 | return ret; | |
1147 | } | |
1148 | ||
76e0da34 | 1149 | UVC_ATTR(uvcg_frame_, dw_frame_interval, dwFrameInterval); |
46919a23 AP |
1150 | |
1151 | static struct configfs_attribute *uvcg_frame_attrs[] = { | |
76e0da34 CH |
1152 | &uvcg_frame_attr_bm_capabilities, |
1153 | &uvcg_frame_attr_w_width, | |
1154 | &uvcg_frame_attr_w_height, | |
1155 | &uvcg_frame_attr_dw_min_bit_rate, | |
1156 | &uvcg_frame_attr_dw_max_bit_rate, | |
1157 | &uvcg_frame_attr_dw_max_video_frame_buffer_size, | |
1158 | &uvcg_frame_attr_dw_default_frame_interval, | |
1159 | &uvcg_frame_attr_dw_frame_interval, | |
46919a23 AP |
1160 | NULL, |
1161 | }; | |
1162 | ||
f093a2d4 | 1163 | static struct config_item_type uvcg_frame_type = { |
46919a23 AP |
1164 | .ct_attrs = uvcg_frame_attrs, |
1165 | .ct_owner = THIS_MODULE, | |
1166 | }; | |
1167 | ||
1168 | static struct config_item *uvcg_frame_make(struct config_group *group, | |
1169 | const char *name) | |
1170 | { | |
1171 | struct uvcg_frame *h; | |
1172 | struct uvcg_format *fmt; | |
1173 | struct f_uvc_opts *opts; | |
1174 | struct config_item *opts_item; | |
1175 | ||
1176 | h = kzalloc(sizeof(*h), GFP_KERNEL); | |
1177 | if (!h) | |
df90f838 | 1178 | return ERR_PTR(-ENOMEM); |
46919a23 AP |
1179 | |
1180 | h->frame.b_descriptor_type = USB_DT_CS_INTERFACE; | |
1181 | h->frame.b_frame_index = 1; | |
1182 | h->frame.w_width = cpu_to_le16(640); | |
1183 | h->frame.w_height = cpu_to_le16(360); | |
1184 | h->frame.dw_min_bit_rate = cpu_to_le32(18432000); | |
1185 | h->frame.dw_max_bit_rate = cpu_to_le32(55296000); | |
1186 | h->frame.dw_max_video_frame_buffer_size = cpu_to_le32(460800); | |
1187 | h->frame.dw_default_frame_interval = cpu_to_le32(666666); | |
1188 | ||
1189 | opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; | |
1190 | opts = to_f_uvc_opts(opts_item); | |
1191 | ||
1192 | mutex_lock(&opts->lock); | |
1193 | fmt = to_uvcg_format(&group->cg_item); | |
1194 | if (fmt->type == UVCG_UNCOMPRESSED) { | |
1195 | h->frame.b_descriptor_subtype = UVC_VS_FRAME_UNCOMPRESSED; | |
1196 | h->fmt_type = UVCG_UNCOMPRESSED; | |
1197 | } else if (fmt->type == UVCG_MJPEG) { | |
1198 | h->frame.b_descriptor_subtype = UVC_VS_FRAME_MJPEG; | |
1199 | h->fmt_type = UVCG_MJPEG; | |
1200 | } else { | |
1201 | mutex_unlock(&opts->lock); | |
c5b2dc68 | 1202 | kfree(h); |
46919a23 AP |
1203 | return ERR_PTR(-EINVAL); |
1204 | } | |
1205 | ++fmt->num_frames; | |
1206 | mutex_unlock(&opts->lock); | |
1207 | ||
1208 | config_item_init_type_name(&h->item, name, &uvcg_frame_type); | |
1209 | ||
1210 | return &h->item; | |
1211 | } | |
1212 | ||
f093a2d4 | 1213 | static void uvcg_frame_drop(struct config_group *group, struct config_item *item) |
46919a23 AP |
1214 | { |
1215 | struct uvcg_frame *h = to_uvcg_frame(item); | |
1216 | struct uvcg_format *fmt; | |
1217 | struct f_uvc_opts *opts; | |
1218 | struct config_item *opts_item; | |
1219 | ||
1220 | opts_item = group->cg_item.ci_parent->ci_parent->ci_parent; | |
1221 | opts = to_f_uvc_opts(opts_item); | |
1222 | ||
1223 | mutex_lock(&opts->lock); | |
1224 | fmt = to_uvcg_format(&group->cg_item); | |
1225 | --fmt->num_frames; | |
1226 | kfree(h); | |
1227 | mutex_unlock(&opts->lock); | |
1228 | } | |
1229 | ||
1230 | /* streaming/uncompressed/<NAME> */ | |
1231 | struct uvcg_uncompressed { | |
1232 | struct uvcg_format fmt; | |
1233 | struct uvc_format_uncompressed desc; | |
1234 | }; | |
1235 | ||
f093a2d4 | 1236 | static struct uvcg_uncompressed *to_uvcg_uncompressed(struct config_item *item) |
46919a23 AP |
1237 | { |
1238 | return container_of( | |
1239 | container_of(to_config_group(item), struct uvcg_format, group), | |
1240 | struct uvcg_uncompressed, fmt); | |
1241 | } | |
1242 | ||
46919a23 AP |
1243 | static struct configfs_group_operations uvcg_uncompressed_group_ops = { |
1244 | .make_item = uvcg_frame_make, | |
1245 | .drop_item = uvcg_frame_drop, | |
1246 | }; | |
1247 | ||
76e0da34 | 1248 | static ssize_t uvcg_uncompressed_guid_format_show(struct config_item *item, |
46919a23 AP |
1249 | char *page) |
1250 | { | |
76e0da34 | 1251 | struct uvcg_uncompressed *ch = to_uvcg_uncompressed(item); |
46919a23 AP |
1252 | struct f_uvc_opts *opts; |
1253 | struct config_item *opts_item; | |
1254 | struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; | |
1255 | ||
1256 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
1257 | ||
1258 | opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; | |
1259 | opts = to_f_uvc_opts(opts_item); | |
1260 | ||
1261 | mutex_lock(&opts->lock); | |
1262 | memcpy(page, ch->desc.guidFormat, sizeof(ch->desc.guidFormat)); | |
1263 | mutex_unlock(&opts->lock); | |
1264 | ||
1265 | mutex_unlock(su_mutex); | |
1266 | ||
1267 | return sizeof(ch->desc.guidFormat); | |
1268 | } | |
1269 | ||
76e0da34 | 1270 | static ssize_t uvcg_uncompressed_guid_format_store(struct config_item *item, |
46919a23 AP |
1271 | const char *page, size_t len) |
1272 | { | |
76e0da34 | 1273 | struct uvcg_uncompressed *ch = to_uvcg_uncompressed(item); |
46919a23 AP |
1274 | struct f_uvc_opts *opts; |
1275 | struct config_item *opts_item; | |
1276 | struct mutex *su_mutex = &ch->fmt.group.cg_subsys->su_mutex; | |
1277 | int ret; | |
1278 | ||
1279 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
1280 | ||
1281 | opts_item = ch->fmt.group.cg_item.ci_parent->ci_parent->ci_parent; | |
1282 | opts = to_f_uvc_opts(opts_item); | |
1283 | ||
1284 | mutex_lock(&opts->lock); | |
1285 | if (ch->fmt.linked || opts->refcnt) { | |
1286 | ret = -EBUSY; | |
1287 | goto end; | |
1288 | } | |
1289 | ||
1290 | memcpy(ch->desc.guidFormat, page, | |
1291 | min(sizeof(ch->desc.guidFormat), len)); | |
1292 | ret = sizeof(ch->desc.guidFormat); | |
1293 | ||
1294 | end: | |
1295 | mutex_unlock(&opts->lock); | |
1296 | mutex_unlock(su_mutex); | |
1297 | return ret; | |
1298 | } | |
1299 | ||
76e0da34 | 1300 | UVC_ATTR(uvcg_uncompressed_, guid_format, guidFormat); |
46919a23 AP |
1301 | |
1302 | #define UVCG_UNCOMPRESSED_ATTR_RO(cname, aname, conv) \ | |
1303 | static ssize_t uvcg_uncompressed_##cname##_show( \ | |
76e0da34 | 1304 | struct config_item *item, char *page) \ |
46919a23 | 1305 | { \ |
76e0da34 | 1306 | struct uvcg_uncompressed *u = to_uvcg_uncompressed(item); \ |
46919a23 AP |
1307 | struct f_uvc_opts *opts; \ |
1308 | struct config_item *opts_item; \ | |
1309 | struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ | |
1310 | int result; \ | |
1311 | \ | |
1312 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
1313 | \ | |
1314 | opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ | |
1315 | opts = to_f_uvc_opts(opts_item); \ | |
1316 | \ | |
1317 | mutex_lock(&opts->lock); \ | |
1318 | result = sprintf(page, "%d\n", conv(u->desc.aname)); \ | |
1319 | mutex_unlock(&opts->lock); \ | |
1320 | \ | |
1321 | mutex_unlock(su_mutex); \ | |
1322 | return result; \ | |
1323 | } \ | |
1324 | \ | |
76e0da34 | 1325 | UVC_ATTR_RO(uvcg_uncompressed_, cname, aname); |
46919a23 AP |
1326 | |
1327 | #define UVCG_UNCOMPRESSED_ATTR(cname, aname, conv) \ | |
1328 | static ssize_t uvcg_uncompressed_##cname##_show( \ | |
76e0da34 | 1329 | struct config_item *item, char *page) \ |
46919a23 | 1330 | { \ |
76e0da34 | 1331 | struct uvcg_uncompressed *u = to_uvcg_uncompressed(item); \ |
46919a23 AP |
1332 | struct f_uvc_opts *opts; \ |
1333 | struct config_item *opts_item; \ | |
1334 | struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ | |
1335 | int result; \ | |
1336 | \ | |
1337 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
1338 | \ | |
1339 | opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ | |
1340 | opts = to_f_uvc_opts(opts_item); \ | |
1341 | \ | |
1342 | mutex_lock(&opts->lock); \ | |
1343 | result = sprintf(page, "%d\n", conv(u->desc.aname)); \ | |
1344 | mutex_unlock(&opts->lock); \ | |
1345 | \ | |
1346 | mutex_unlock(su_mutex); \ | |
1347 | return result; \ | |
1348 | } \ | |
1349 | \ | |
1350 | static ssize_t \ | |
76e0da34 | 1351 | uvcg_uncompressed_##cname##_store(struct config_item *item, \ |
46919a23 AP |
1352 | const char *page, size_t len) \ |
1353 | { \ | |
76e0da34 | 1354 | struct uvcg_uncompressed *u = to_uvcg_uncompressed(item); \ |
46919a23 AP |
1355 | struct f_uvc_opts *opts; \ |
1356 | struct config_item *opts_item; \ | |
1357 | struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ | |
1358 | int ret; \ | |
1359 | u8 num; \ | |
1360 | \ | |
1361 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
1362 | \ | |
1363 | opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ | |
1364 | opts = to_f_uvc_opts(opts_item); \ | |
1365 | \ | |
1366 | mutex_lock(&opts->lock); \ | |
1367 | if (u->fmt.linked || opts->refcnt) { \ | |
1368 | ret = -EBUSY; \ | |
1369 | goto end; \ | |
1370 | } \ | |
1371 | \ | |
1372 | ret = kstrtou8(page, 0, &num); \ | |
1373 | if (ret) \ | |
1374 | goto end; \ | |
1375 | \ | |
1376 | if (num > 255) { \ | |
1377 | ret = -EINVAL; \ | |
1378 | goto end; \ | |
1379 | } \ | |
1380 | u->desc.aname = num; \ | |
1381 | ret = len; \ | |
1382 | end: \ | |
1383 | mutex_unlock(&opts->lock); \ | |
1384 | mutex_unlock(su_mutex); \ | |
1385 | return ret; \ | |
1386 | } \ | |
1387 | \ | |
76e0da34 | 1388 | UVC_ATTR(uvcg_uncompressed_, cname, aname); |
46919a23 AP |
1389 | |
1390 | #define identity_conv(x) (x) | |
1391 | ||
1392 | UVCG_UNCOMPRESSED_ATTR(b_bits_per_pixel, bBitsPerPixel, identity_conv); | |
1393 | UVCG_UNCOMPRESSED_ATTR(b_default_frame_index, bDefaultFrameIndex, | |
1394 | identity_conv); | |
1395 | UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); | |
1396 | UVCG_UNCOMPRESSED_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); | |
1397 | UVCG_UNCOMPRESSED_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); | |
1398 | ||
1399 | #undef identity_conv | |
1400 | ||
1401 | #undef UVCG_UNCOMPRESSED_ATTR | |
1402 | #undef UVCG_UNCOMPRESSED_ATTR_RO | |
1403 | ||
1404 | static inline ssize_t | |
76e0da34 | 1405 | uvcg_uncompressed_bma_controls_show(struct config_item *item, char *page) |
46919a23 | 1406 | { |
76e0da34 | 1407 | struct uvcg_uncompressed *unc = to_uvcg_uncompressed(item); |
46919a23 AP |
1408 | return uvcg_format_bma_controls_show(&unc->fmt, page); |
1409 | } | |
1410 | ||
1411 | static inline ssize_t | |
76e0da34 | 1412 | uvcg_uncompressed_bma_controls_store(struct config_item *item, |
46919a23 AP |
1413 | const char *page, size_t len) |
1414 | { | |
76e0da34 CH |
1415 | struct uvcg_uncompressed *unc = to_uvcg_uncompressed(item); |
1416 | return uvcg_format_bma_controls_store(&unc->fmt, page, len); | |
46919a23 AP |
1417 | } |
1418 | ||
76e0da34 | 1419 | UVC_ATTR(uvcg_uncompressed_, bma_controls, bmaControls); |
46919a23 AP |
1420 | |
1421 | static struct configfs_attribute *uvcg_uncompressed_attrs[] = { | |
76e0da34 CH |
1422 | &uvcg_uncompressed_attr_guid_format, |
1423 | &uvcg_uncompressed_attr_b_bits_per_pixel, | |
1424 | &uvcg_uncompressed_attr_b_default_frame_index, | |
1425 | &uvcg_uncompressed_attr_b_aspect_ratio_x, | |
1426 | &uvcg_uncompressed_attr_b_aspect_ratio_y, | |
1427 | &uvcg_uncompressed_attr_bm_interface_flags, | |
1428 | &uvcg_uncompressed_attr_bma_controls, | |
46919a23 AP |
1429 | NULL, |
1430 | }; | |
1431 | ||
f093a2d4 | 1432 | static struct config_item_type uvcg_uncompressed_type = { |
46919a23 AP |
1433 | .ct_group_ops = &uvcg_uncompressed_group_ops, |
1434 | .ct_attrs = uvcg_uncompressed_attrs, | |
1435 | .ct_owner = THIS_MODULE, | |
1436 | }; | |
1437 | ||
1438 | static struct config_group *uvcg_uncompressed_make(struct config_group *group, | |
1439 | const char *name) | |
1440 | { | |
1441 | static char guid[] = { | |
1442 | 'Y', 'U', 'Y', '2', 0x00, 0x00, 0x10, 0x00, | |
1443 | 0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71 | |
1444 | }; | |
1445 | struct uvcg_uncompressed *h; | |
1446 | ||
1447 | h = kzalloc(sizeof(*h), GFP_KERNEL); | |
1448 | if (!h) | |
df90f838 | 1449 | return ERR_PTR(-ENOMEM); |
46919a23 AP |
1450 | |
1451 | h->desc.bLength = UVC_DT_FORMAT_UNCOMPRESSED_SIZE; | |
1452 | h->desc.bDescriptorType = USB_DT_CS_INTERFACE; | |
1453 | h->desc.bDescriptorSubType = UVC_VS_FORMAT_UNCOMPRESSED; | |
1454 | memcpy(h->desc.guidFormat, guid, sizeof(guid)); | |
1455 | h->desc.bBitsPerPixel = 16; | |
1456 | h->desc.bDefaultFrameIndex = 1; | |
1457 | h->desc.bAspectRatioX = 0; | |
1458 | h->desc.bAspectRatioY = 0; | |
1459 | h->desc.bmInterfaceFlags = 0; | |
1460 | h->desc.bCopyProtect = 0; | |
1461 | ||
1462 | h->fmt.type = UVCG_UNCOMPRESSED; | |
1463 | config_group_init_type_name(&h->fmt.group, name, | |
1464 | &uvcg_uncompressed_type); | |
1465 | ||
1466 | return &h->fmt.group; | |
1467 | } | |
1468 | ||
f093a2d4 | 1469 | static void uvcg_uncompressed_drop(struct config_group *group, |
46919a23 AP |
1470 | struct config_item *item) |
1471 | { | |
1472 | struct uvcg_uncompressed *h = to_uvcg_uncompressed(item); | |
1473 | ||
1474 | kfree(h); | |
1475 | } | |
1476 | ||
1477 | static struct configfs_group_operations uvcg_uncompressed_grp_ops = { | |
1478 | .make_group = uvcg_uncompressed_make, | |
1479 | .drop_item = uvcg_uncompressed_drop, | |
1480 | }; | |
1481 | ||
1482 | static struct config_item_type uvcg_uncompressed_grp_type = { | |
1483 | .ct_group_ops = &uvcg_uncompressed_grp_ops, | |
1484 | .ct_owner = THIS_MODULE, | |
1485 | }; | |
1486 | ||
1487 | /* streaming/mjpeg/<NAME> */ | |
1488 | struct uvcg_mjpeg { | |
1489 | struct uvcg_format fmt; | |
1490 | struct uvc_format_mjpeg desc; | |
1491 | }; | |
1492 | ||
f093a2d4 | 1493 | static struct uvcg_mjpeg *to_uvcg_mjpeg(struct config_item *item) |
46919a23 AP |
1494 | { |
1495 | return container_of( | |
1496 | container_of(to_config_group(item), struct uvcg_format, group), | |
1497 | struct uvcg_mjpeg, fmt); | |
1498 | } | |
1499 | ||
46919a23 AP |
1500 | static struct configfs_group_operations uvcg_mjpeg_group_ops = { |
1501 | .make_item = uvcg_frame_make, | |
1502 | .drop_item = uvcg_frame_drop, | |
1503 | }; | |
1504 | ||
1505 | #define UVCG_MJPEG_ATTR_RO(cname, aname, conv) \ | |
76e0da34 | 1506 | static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ |
46919a23 | 1507 | { \ |
76e0da34 | 1508 | struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ |
46919a23 AP |
1509 | struct f_uvc_opts *opts; \ |
1510 | struct config_item *opts_item; \ | |
1511 | struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ | |
1512 | int result; \ | |
1513 | \ | |
1514 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
1515 | \ | |
1516 | opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ | |
1517 | opts = to_f_uvc_opts(opts_item); \ | |
1518 | \ | |
1519 | mutex_lock(&opts->lock); \ | |
1520 | result = sprintf(page, "%d\n", conv(u->desc.aname)); \ | |
1521 | mutex_unlock(&opts->lock); \ | |
1522 | \ | |
1523 | mutex_unlock(su_mutex); \ | |
1524 | return result; \ | |
1525 | } \ | |
1526 | \ | |
76e0da34 | 1527 | UVC_ATTR_RO(uvcg_mjpeg_, cname, aname) |
46919a23 AP |
1528 | |
1529 | #define UVCG_MJPEG_ATTR(cname, aname, conv) \ | |
76e0da34 | 1530 | static ssize_t uvcg_mjpeg_##cname##_show(struct config_item *item, char *page)\ |
46919a23 | 1531 | { \ |
76e0da34 | 1532 | struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ |
46919a23 AP |
1533 | struct f_uvc_opts *opts; \ |
1534 | struct config_item *opts_item; \ | |
1535 | struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ | |
1536 | int result; \ | |
1537 | \ | |
1538 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
1539 | \ | |
1540 | opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ | |
1541 | opts = to_f_uvc_opts(opts_item); \ | |
1542 | \ | |
1543 | mutex_lock(&opts->lock); \ | |
1544 | result = sprintf(page, "%d\n", conv(u->desc.aname)); \ | |
1545 | mutex_unlock(&opts->lock); \ | |
1546 | \ | |
1547 | mutex_unlock(su_mutex); \ | |
1548 | return result; \ | |
1549 | } \ | |
1550 | \ | |
1551 | static ssize_t \ | |
76e0da34 | 1552 | uvcg_mjpeg_##cname##_store(struct config_item *item, \ |
46919a23 AP |
1553 | const char *page, size_t len) \ |
1554 | { \ | |
76e0da34 | 1555 | struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); \ |
46919a23 AP |
1556 | struct f_uvc_opts *opts; \ |
1557 | struct config_item *opts_item; \ | |
1558 | struct mutex *su_mutex = &u->fmt.group.cg_subsys->su_mutex; \ | |
1559 | int ret; \ | |
1560 | u8 num; \ | |
1561 | \ | |
1562 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
1563 | \ | |
1564 | opts_item = u->fmt.group.cg_item.ci_parent->ci_parent->ci_parent;\ | |
1565 | opts = to_f_uvc_opts(opts_item); \ | |
1566 | \ | |
1567 | mutex_lock(&opts->lock); \ | |
1568 | if (u->fmt.linked || opts->refcnt) { \ | |
1569 | ret = -EBUSY; \ | |
1570 | goto end; \ | |
1571 | } \ | |
1572 | \ | |
1573 | ret = kstrtou8(page, 0, &num); \ | |
1574 | if (ret) \ | |
1575 | goto end; \ | |
1576 | \ | |
1577 | if (num > 255) { \ | |
1578 | ret = -EINVAL; \ | |
1579 | goto end; \ | |
1580 | } \ | |
1581 | u->desc.aname = num; \ | |
1582 | ret = len; \ | |
1583 | end: \ | |
1584 | mutex_unlock(&opts->lock); \ | |
1585 | mutex_unlock(su_mutex); \ | |
1586 | return ret; \ | |
1587 | } \ | |
1588 | \ | |
76e0da34 | 1589 | UVC_ATTR(uvcg_mjpeg_, cname, aname) |
46919a23 AP |
1590 | |
1591 | #define identity_conv(x) (x) | |
1592 | ||
1593 | UVCG_MJPEG_ATTR(b_default_frame_index, bDefaultFrameIndex, | |
1594 | identity_conv); | |
1595 | UVCG_MJPEG_ATTR_RO(bm_flags, bmFlags, identity_conv); | |
1596 | UVCG_MJPEG_ATTR_RO(b_aspect_ratio_x, bAspectRatioX, identity_conv); | |
1597 | UVCG_MJPEG_ATTR_RO(b_aspect_ratio_y, bAspectRatioY, identity_conv); | |
1598 | UVCG_MJPEG_ATTR_RO(bm_interface_flags, bmInterfaceFlags, identity_conv); | |
1599 | ||
1600 | #undef identity_conv | |
1601 | ||
1602 | #undef UVCG_MJPEG_ATTR | |
1603 | #undef UVCG_MJPEG_ATTR_RO | |
1604 | ||
1605 | static inline ssize_t | |
76e0da34 | 1606 | uvcg_mjpeg_bma_controls_show(struct config_item *item, char *page) |
46919a23 | 1607 | { |
76e0da34 CH |
1608 | struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); |
1609 | return uvcg_format_bma_controls_show(&u->fmt, page); | |
46919a23 AP |
1610 | } |
1611 | ||
1612 | static inline ssize_t | |
76e0da34 | 1613 | uvcg_mjpeg_bma_controls_store(struct config_item *item, |
46919a23 AP |
1614 | const char *page, size_t len) |
1615 | { | |
76e0da34 CH |
1616 | struct uvcg_mjpeg *u = to_uvcg_mjpeg(item); |
1617 | return uvcg_format_bma_controls_store(&u->fmt, page, len); | |
46919a23 AP |
1618 | } |
1619 | ||
76e0da34 | 1620 | UVC_ATTR(uvcg_mjpeg_, bma_controls, bmaControls); |
46919a23 AP |
1621 | |
1622 | static struct configfs_attribute *uvcg_mjpeg_attrs[] = { | |
76e0da34 CH |
1623 | &uvcg_mjpeg_attr_b_default_frame_index, |
1624 | &uvcg_mjpeg_attr_bm_flags, | |
1625 | &uvcg_mjpeg_attr_b_aspect_ratio_x, | |
1626 | &uvcg_mjpeg_attr_b_aspect_ratio_y, | |
1627 | &uvcg_mjpeg_attr_bm_interface_flags, | |
1628 | &uvcg_mjpeg_attr_bma_controls, | |
46919a23 AP |
1629 | NULL, |
1630 | }; | |
1631 | ||
f093a2d4 | 1632 | static struct config_item_type uvcg_mjpeg_type = { |
46919a23 AP |
1633 | .ct_group_ops = &uvcg_mjpeg_group_ops, |
1634 | .ct_attrs = uvcg_mjpeg_attrs, | |
1635 | .ct_owner = THIS_MODULE, | |
1636 | }; | |
1637 | ||
1638 | static struct config_group *uvcg_mjpeg_make(struct config_group *group, | |
1639 | const char *name) | |
1640 | { | |
1641 | struct uvcg_mjpeg *h; | |
1642 | ||
1643 | h = kzalloc(sizeof(*h), GFP_KERNEL); | |
1644 | if (!h) | |
df90f838 | 1645 | return ERR_PTR(-ENOMEM); |
46919a23 AP |
1646 | |
1647 | h->desc.bLength = UVC_DT_FORMAT_MJPEG_SIZE; | |
1648 | h->desc.bDescriptorType = USB_DT_CS_INTERFACE; | |
1649 | h->desc.bDescriptorSubType = UVC_VS_FORMAT_MJPEG; | |
1650 | h->desc.bDefaultFrameIndex = 1; | |
1651 | h->desc.bAspectRatioX = 0; | |
1652 | h->desc.bAspectRatioY = 0; | |
1653 | h->desc.bmInterfaceFlags = 0; | |
1654 | h->desc.bCopyProtect = 0; | |
1655 | ||
1656 | h->fmt.type = UVCG_MJPEG; | |
1657 | config_group_init_type_name(&h->fmt.group, name, | |
1658 | &uvcg_mjpeg_type); | |
1659 | ||
1660 | return &h->fmt.group; | |
1661 | } | |
1662 | ||
f093a2d4 | 1663 | static void uvcg_mjpeg_drop(struct config_group *group, |
46919a23 AP |
1664 | struct config_item *item) |
1665 | { | |
1666 | struct uvcg_mjpeg *h = to_uvcg_mjpeg(item); | |
1667 | ||
1668 | kfree(h); | |
1669 | } | |
1670 | ||
1671 | static struct configfs_group_operations uvcg_mjpeg_grp_ops = { | |
1672 | .make_group = uvcg_mjpeg_make, | |
1673 | .drop_item = uvcg_mjpeg_drop, | |
1674 | }; | |
1675 | ||
1676 | static struct config_item_type uvcg_mjpeg_grp_type = { | |
1677 | .ct_group_ops = &uvcg_mjpeg_grp_ops, | |
1678 | .ct_owner = THIS_MODULE, | |
1679 | }; | |
1680 | ||
1681 | /* streaming/color_matching/default */ | |
1682 | static struct uvcg_default_color_matching { | |
1683 | struct config_group group; | |
1684 | } uvcg_default_color_matching; | |
1685 | ||
1686 | static inline struct uvcg_default_color_matching | |
1687 | *to_uvcg_default_color_matching(struct config_item *item) | |
1688 | { | |
1689 | return container_of(to_config_group(item), | |
1690 | struct uvcg_default_color_matching, group); | |
1691 | } | |
1692 | ||
46919a23 AP |
1693 | #define UVCG_DEFAULT_COLOR_MATCHING_ATTR(cname, aname, conv) \ |
1694 | static ssize_t uvcg_default_color_matching_##cname##_show( \ | |
76e0da34 | 1695 | struct config_item *item, char *page) \ |
46919a23 | 1696 | { \ |
76e0da34 CH |
1697 | struct uvcg_default_color_matching *dc = \ |
1698 | to_uvcg_default_color_matching(item); \ | |
46919a23 AP |
1699 | struct f_uvc_opts *opts; \ |
1700 | struct config_item *opts_item; \ | |
1701 | struct mutex *su_mutex = &dc->group.cg_subsys->su_mutex; \ | |
1702 | struct uvc_color_matching_descriptor *cd; \ | |
1703 | int result; \ | |
1704 | \ | |
1705 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ \ | |
1706 | \ | |
1707 | opts_item = dc->group.cg_item.ci_parent->ci_parent->ci_parent; \ | |
1708 | opts = to_f_uvc_opts(opts_item); \ | |
1709 | cd = &opts->uvc_color_matching; \ | |
1710 | \ | |
1711 | mutex_lock(&opts->lock); \ | |
1712 | result = sprintf(page, "%d\n", conv(cd->aname)); \ | |
1713 | mutex_unlock(&opts->lock); \ | |
1714 | \ | |
1715 | mutex_unlock(su_mutex); \ | |
1716 | return result; \ | |
1717 | } \ | |
1718 | \ | |
76e0da34 | 1719 | UVC_ATTR_RO(uvcg_default_color_matching_, cname, aname) |
46919a23 AP |
1720 | |
1721 | #define identity_conv(x) (x) | |
1722 | ||
1723 | UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_color_primaries, bColorPrimaries, | |
1724 | identity_conv); | |
1725 | UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_transfer_characteristics, | |
1726 | bTransferCharacteristics, identity_conv); | |
1727 | UVCG_DEFAULT_COLOR_MATCHING_ATTR(b_matrix_coefficients, bMatrixCoefficients, | |
1728 | identity_conv); | |
1729 | ||
1730 | #undef identity_conv | |
1731 | ||
1732 | #undef UVCG_DEFAULT_COLOR_MATCHING_ATTR | |
1733 | ||
1734 | static struct configfs_attribute *uvcg_default_color_matching_attrs[] = { | |
76e0da34 CH |
1735 | &uvcg_default_color_matching_attr_b_color_primaries, |
1736 | &uvcg_default_color_matching_attr_b_transfer_characteristics, | |
1737 | &uvcg_default_color_matching_attr_b_matrix_coefficients, | |
46919a23 AP |
1738 | NULL, |
1739 | }; | |
1740 | ||
1741 | static struct config_item_type uvcg_default_color_matching_type = { | |
46919a23 AP |
1742 | .ct_attrs = uvcg_default_color_matching_attrs, |
1743 | .ct_owner = THIS_MODULE, | |
1744 | }; | |
1745 | ||
1746 | /* struct uvcg_color_matching {}; */ | |
1747 | ||
46919a23 AP |
1748 | /* streaming/color_matching */ |
1749 | static struct uvcg_color_matching_grp { | |
1750 | struct config_group group; | |
1751 | } uvcg_color_matching_grp; | |
1752 | ||
1753 | static struct config_item_type uvcg_color_matching_grp_type = { | |
1754 | .ct_owner = THIS_MODULE, | |
1755 | }; | |
1756 | ||
1757 | /* streaming/class/{fs|hs|ss} */ | |
1758 | static struct uvcg_streaming_class { | |
1759 | struct config_group group; | |
1760 | } uvcg_streaming_class_fs, uvcg_streaming_class_hs, uvcg_streaming_class_ss; | |
1761 | ||
1762 | ||
1763 | static inline struct uvc_descriptor_header | |
1764 | ***__uvcg_get_stream_class_arr(struct config_item *i, struct f_uvc_opts *o) | |
1765 | { | |
1766 | struct uvcg_streaming_class *cl = container_of(to_config_group(i), | |
1767 | struct uvcg_streaming_class, group); | |
1768 | ||
1769 | if (cl == &uvcg_streaming_class_fs) | |
1770 | return &o->uvc_fs_streaming_cls; | |
1771 | ||
1772 | if (cl == &uvcg_streaming_class_hs) | |
1773 | return &o->uvc_hs_streaming_cls; | |
1774 | ||
1775 | if (cl == &uvcg_streaming_class_ss) | |
1776 | return &o->uvc_ss_streaming_cls; | |
1777 | ||
1778 | return NULL; | |
1779 | } | |
1780 | ||
1781 | enum uvcg_strm_type { | |
1782 | UVCG_HEADER = 0, | |
1783 | UVCG_FORMAT, | |
1784 | UVCG_FRAME | |
1785 | }; | |
1786 | ||
578d0b6b AP |
1787 | /* |
1788 | * Iterate over a hierarchy of streaming descriptors' config items. | |
1789 | * The items are created by the user with configfs. | |
1790 | * | |
1791 | * It "processes" the header pointed to by @priv1, then for each format | |
1792 | * that follows the header "processes" the format itself and then for | |
1793 | * each frame inside a format "processes" the frame. | |
1794 | * | |
1795 | * As a "processing" function the @fun is used. | |
1796 | * | |
1797 | * __uvcg_iter_strm_cls() is used in two context: first, to calculate | |
1798 | * the amount of memory needed for an array of streaming descriptors | |
1799 | * and second, to actually fill the array. | |
1800 | * | |
1801 | * @h: streaming header pointer | |
1802 | * @priv2: an "inout" parameter (the caller might want to see the changes to it) | |
1803 | * @priv3: an "inout" parameter (the caller might want to see the changes to it) | |
1804 | * @fun: callback function for processing each level of the hierarchy | |
1805 | */ | |
72796835 AP |
1806 | static int __uvcg_iter_strm_cls(struct uvcg_streaming_header *h, |
1807 | void *priv2, void *priv3, | |
46919a23 AP |
1808 | int (*fun)(void *, void *, void *, int, enum uvcg_strm_type type)) |
1809 | { | |
46919a23 AP |
1810 | struct uvcg_format_ptr *f; |
1811 | struct config_group *grp; | |
1812 | struct config_item *item; | |
1813 | struct uvcg_frame *frm; | |
1814 | int ret, i, j; | |
1815 | ||
1816 | if (!fun) | |
1817 | return -EINVAL; | |
1818 | ||
1819 | i = j = 0; | |
1820 | ret = fun(h, priv2, priv3, 0, UVCG_HEADER); | |
1821 | if (ret) | |
1822 | return ret; | |
1823 | list_for_each_entry(f, &h->formats, entry) { | |
1824 | ret = fun(f->fmt, priv2, priv3, i++, UVCG_FORMAT); | |
1825 | if (ret) | |
1826 | return ret; | |
1827 | grp = &f->fmt->group; | |
1828 | list_for_each_entry(item, &grp->cg_children, ci_entry) { | |
1829 | frm = to_uvcg_frame(item); | |
1830 | ret = fun(frm, priv2, priv3, j++, UVCG_FRAME); | |
1831 | if (ret) | |
1832 | return ret; | |
1833 | } | |
1834 | } | |
1835 | ||
1836 | return ret; | |
1837 | } | |
1838 | ||
578d0b6b AP |
1839 | /* |
1840 | * Count how many bytes are needed for an array of streaming descriptors. | |
1841 | * | |
1842 | * @priv1: pointer to a header, format or frame | |
1843 | * @priv2: inout parameter, accumulated size of the array | |
1844 | * @priv3: inout parameter, accumulated number of the array elements | |
1845 | * @n: unused, this function's prototype must match @fun in __uvcg_iter_strm_cls | |
1846 | */ | |
46919a23 AP |
1847 | static int __uvcg_cnt_strm(void *priv1, void *priv2, void *priv3, int n, |
1848 | enum uvcg_strm_type type) | |
1849 | { | |
1850 | size_t *size = priv2; | |
1851 | size_t *count = priv3; | |
1852 | ||
1853 | switch (type) { | |
1854 | case UVCG_HEADER: { | |
1855 | struct uvcg_streaming_header *h = priv1; | |
1856 | ||
1857 | *size += sizeof(h->desc); | |
1858 | /* bmaControls */ | |
1859 | *size += h->num_fmt * UVCG_STREAMING_CONTROL_SIZE; | |
1860 | } | |
1861 | break; | |
1862 | case UVCG_FORMAT: { | |
1863 | struct uvcg_format *fmt = priv1; | |
1864 | ||
1865 | if (fmt->type == UVCG_UNCOMPRESSED) { | |
1866 | struct uvcg_uncompressed *u = | |
1867 | container_of(fmt, struct uvcg_uncompressed, | |
1868 | fmt); | |
1869 | ||
1870 | *size += sizeof(u->desc); | |
1871 | } else if (fmt->type == UVCG_MJPEG) { | |
1872 | struct uvcg_mjpeg *m = | |
1873 | container_of(fmt, struct uvcg_mjpeg, fmt); | |
1874 | ||
1875 | *size += sizeof(m->desc); | |
1876 | } else { | |
1877 | return -EINVAL; | |
1878 | } | |
1879 | } | |
1880 | break; | |
1881 | case UVCG_FRAME: { | |
1882 | struct uvcg_frame *frm = priv1; | |
1883 | int sz = sizeof(frm->dw_frame_interval); | |
1884 | ||
1885 | *size += sizeof(frm->frame); | |
1886 | *size += frm->frame.b_frame_interval_type * sz; | |
1887 | } | |
1888 | break; | |
1889 | } | |
1890 | ||
1891 | ++*count; | |
1892 | ||
1893 | return 0; | |
1894 | } | |
1895 | ||
578d0b6b AP |
1896 | /* |
1897 | * Fill an array of streaming descriptors. | |
1898 | * | |
1899 | * @priv1: pointer to a header, format or frame | |
1900 | * @priv2: inout parameter, pointer into a block of memory | |
1901 | * @priv3: inout parameter, pointer to a 2-dimensional array | |
1902 | */ | |
46919a23 AP |
1903 | static int __uvcg_fill_strm(void *priv1, void *priv2, void *priv3, int n, |
1904 | enum uvcg_strm_type type) | |
1905 | { | |
1906 | void **dest = priv2; | |
1907 | struct uvc_descriptor_header ***array = priv3; | |
1908 | size_t sz; | |
1909 | ||
1910 | **array = *dest; | |
1911 | ++*array; | |
1912 | ||
1913 | switch (type) { | |
1914 | case UVCG_HEADER: { | |
1915 | struct uvc_input_header_descriptor *ihdr = *dest; | |
1916 | struct uvcg_streaming_header *h = priv1; | |
1917 | struct uvcg_format_ptr *f; | |
1918 | ||
1919 | memcpy(*dest, &h->desc, sizeof(h->desc)); | |
1920 | *dest += sizeof(h->desc); | |
1921 | sz = UVCG_STREAMING_CONTROL_SIZE; | |
1922 | list_for_each_entry(f, &h->formats, entry) { | |
1923 | memcpy(*dest, f->fmt->bmaControls, sz); | |
1924 | *dest += sz; | |
1925 | } | |
1926 | ihdr->bLength = sizeof(h->desc) + h->num_fmt * sz; | |
1927 | ihdr->bNumFormats = h->num_fmt; | |
1928 | } | |
1929 | break; | |
1930 | case UVCG_FORMAT: { | |
1931 | struct uvcg_format *fmt = priv1; | |
1932 | ||
1933 | if (fmt->type == UVCG_UNCOMPRESSED) { | |
1934 | struct uvc_format_uncompressed *unc = *dest; | |
1935 | struct uvcg_uncompressed *u = | |
1936 | container_of(fmt, struct uvcg_uncompressed, | |
1937 | fmt); | |
1938 | ||
1939 | memcpy(*dest, &u->desc, sizeof(u->desc)); | |
1940 | *dest += sizeof(u->desc); | |
1941 | unc->bNumFrameDescriptors = fmt->num_frames; | |
1942 | unc->bFormatIndex = n + 1; | |
1943 | } else if (fmt->type == UVCG_MJPEG) { | |
1944 | struct uvc_format_mjpeg *mjp = *dest; | |
1945 | struct uvcg_mjpeg *m = | |
1946 | container_of(fmt, struct uvcg_mjpeg, fmt); | |
1947 | ||
1948 | memcpy(*dest, &m->desc, sizeof(m->desc)); | |
1949 | *dest += sizeof(m->desc); | |
1950 | mjp->bNumFrameDescriptors = fmt->num_frames; | |
1951 | mjp->bFormatIndex = n + 1; | |
1952 | } else { | |
1953 | return -EINVAL; | |
1954 | } | |
1955 | } | |
1956 | break; | |
1957 | case UVCG_FRAME: { | |
1958 | struct uvcg_frame *frm = priv1; | |
1959 | struct uvc_descriptor_header *h = *dest; | |
1960 | ||
1961 | sz = sizeof(frm->frame); | |
1962 | memcpy(*dest, &frm->frame, sz); | |
1963 | *dest += sz; | |
1964 | sz = frm->frame.b_frame_interval_type * | |
1965 | sizeof(*frm->dw_frame_interval); | |
1966 | memcpy(*dest, frm->dw_frame_interval, sz); | |
1967 | *dest += sz; | |
1968 | if (frm->fmt_type == UVCG_UNCOMPRESSED) | |
1969 | h->bLength = UVC_DT_FRAME_UNCOMPRESSED_SIZE( | |
1970 | frm->frame.b_frame_interval_type); | |
1971 | else if (frm->fmt_type == UVCG_MJPEG) | |
1972 | h->bLength = UVC_DT_FRAME_MJPEG_SIZE( | |
1973 | frm->frame.b_frame_interval_type); | |
1974 | } | |
1975 | break; | |
1976 | } | |
1977 | ||
1978 | return 0; | |
1979 | } | |
1980 | ||
1981 | static int uvcg_streaming_class_allow_link(struct config_item *src, | |
1982 | struct config_item *target) | |
1983 | { | |
1984 | struct config_item *streaming, *header; | |
1985 | struct f_uvc_opts *opts; | |
1986 | struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; | |
1987 | struct uvc_descriptor_header ***class_array, **cl_arr; | |
1988 | struct uvcg_streaming_header *target_hdr; | |
06ab8b04 | 1989 | void *data, *data_save; |
46919a23 AP |
1990 | size_t size = 0, count = 0; |
1991 | int ret = -EINVAL; | |
1992 | ||
1993 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
1994 | ||
1995 | streaming = src->ci_parent->ci_parent; | |
1996 | header = config_group_find_item(to_config_group(streaming), "header"); | |
1997 | if (!header || target->ci_parent != header) | |
1998 | goto out; | |
1999 | ||
2000 | opts = to_f_uvc_opts(streaming->ci_parent); | |
2001 | ||
2002 | mutex_lock(&opts->lock); | |
2003 | ||
2004 | class_array = __uvcg_get_stream_class_arr(src, opts); | |
2005 | if (!class_array || *class_array || opts->refcnt) { | |
2006 | ret = -EBUSY; | |
2007 | goto unlock; | |
2008 | } | |
2009 | ||
2010 | target_hdr = to_uvcg_streaming_header(target); | |
2011 | ret = __uvcg_iter_strm_cls(target_hdr, &size, &count, __uvcg_cnt_strm); | |
2012 | if (ret) | |
2013 | goto unlock; | |
2014 | ||
2015 | count += 2; /* color_matching, NULL */ | |
2016 | *class_array = kcalloc(count, sizeof(void *), GFP_KERNEL); | |
2017 | if (!*class_array) { | |
df90f838 | 2018 | ret = -ENOMEM; |
46919a23 AP |
2019 | goto unlock; |
2020 | } | |
2021 | ||
06ab8b04 | 2022 | data = data_save = kzalloc(size, GFP_KERNEL); |
46919a23 AP |
2023 | if (!data) { |
2024 | kfree(*class_array); | |
2025 | *class_array = NULL; | |
bd610c5a | 2026 | ret = -ENOMEM; |
46919a23 AP |
2027 | goto unlock; |
2028 | } | |
2029 | cl_arr = *class_array; | |
2030 | ret = __uvcg_iter_strm_cls(target_hdr, &data, &cl_arr, | |
2031 | __uvcg_fill_strm); | |
2032 | if (ret) { | |
2033 | kfree(*class_array); | |
2034 | *class_array = NULL; | |
06ab8b04 AP |
2035 | /* |
2036 | * __uvcg_fill_strm() called from __uvcg_iter_stream_cls() | |
2037 | * might have advanced the "data", so use a backup copy | |
2038 | */ | |
2039 | kfree(data_save); | |
46919a23 AP |
2040 | goto unlock; |
2041 | } | |
2042 | *cl_arr = (struct uvc_descriptor_header *)&opts->uvc_color_matching; | |
2043 | ||
2044 | ++target_hdr->linked; | |
2045 | ret = 0; | |
2046 | ||
2047 | unlock: | |
2048 | mutex_unlock(&opts->lock); | |
2049 | out: | |
2050 | mutex_unlock(su_mutex); | |
2051 | return ret; | |
2052 | } | |
2053 | ||
2054 | static int uvcg_streaming_class_drop_link(struct config_item *src, | |
2055 | struct config_item *target) | |
2056 | { | |
2057 | struct config_item *streaming, *header; | |
2058 | struct f_uvc_opts *opts; | |
2059 | struct mutex *su_mutex = &src->ci_group->cg_subsys->su_mutex; | |
2060 | struct uvc_descriptor_header ***class_array; | |
2061 | struct uvcg_streaming_header *target_hdr; | |
2062 | int ret = -EINVAL; | |
2063 | ||
2064 | mutex_lock(su_mutex); /* for navigating configfs hierarchy */ | |
2065 | ||
2066 | streaming = src->ci_parent->ci_parent; | |
2067 | header = config_group_find_item(to_config_group(streaming), "header"); | |
2068 | if (!header || target->ci_parent != header) | |
2069 | goto out; | |
2070 | ||
2071 | opts = to_f_uvc_opts(streaming->ci_parent); | |
2072 | ||
2073 | mutex_lock(&opts->lock); | |
2074 | ||
2075 | class_array = __uvcg_get_stream_class_arr(src, opts); | |
2076 | if (!class_array || !*class_array) | |
2077 | goto unlock; | |
2078 | ||
2079 | if (opts->refcnt) { | |
2080 | ret = -EBUSY; | |
2081 | goto unlock; | |
2082 | } | |
2083 | ||
2084 | target_hdr = to_uvcg_streaming_header(target); | |
2085 | --target_hdr->linked; | |
2086 | kfree(**class_array); | |
2087 | kfree(*class_array); | |
2088 | *class_array = NULL; | |
2089 | ret = 0; | |
2090 | ||
2091 | unlock: | |
2092 | mutex_unlock(&opts->lock); | |
2093 | out: | |
2094 | mutex_unlock(su_mutex); | |
2095 | return ret; | |
2096 | } | |
2097 | ||
2098 | static struct configfs_item_operations uvcg_streaming_class_item_ops = { | |
2099 | .allow_link = uvcg_streaming_class_allow_link, | |
2100 | .drop_link = uvcg_streaming_class_drop_link, | |
2101 | }; | |
2102 | ||
2103 | static struct config_item_type uvcg_streaming_class_type = { | |
2104 | .ct_item_ops = &uvcg_streaming_class_item_ops, | |
2105 | .ct_owner = THIS_MODULE, | |
2106 | }; | |
2107 | ||
46919a23 AP |
2108 | /* streaming/class */ |
2109 | static struct uvcg_streaming_class_grp { | |
2110 | struct config_group group; | |
2111 | } uvcg_streaming_class_grp; | |
2112 | ||
2113 | static struct config_item_type uvcg_streaming_class_grp_type = { | |
2114 | .ct_owner = THIS_MODULE, | |
2115 | }; | |
2116 | ||
46919a23 AP |
2117 | /* streaming */ |
2118 | static struct uvcg_streaming_grp { | |
2119 | struct config_group group; | |
2120 | } uvcg_streaming_grp; | |
2121 | ||
2122 | static struct config_item_type uvcg_streaming_grp_type = { | |
2123 | .ct_owner = THIS_MODULE, | |
2124 | }; | |
2125 | ||
46919a23 AP |
2126 | static inline struct f_uvc_opts *to_f_uvc_opts(struct config_item *item) |
2127 | { | |
2128 | return container_of(to_config_group(item), struct f_uvc_opts, | |
2129 | func_inst.group); | |
2130 | } | |
2131 | ||
46919a23 AP |
2132 | static void uvc_attr_release(struct config_item *item) |
2133 | { | |
2134 | struct f_uvc_opts *opts = to_f_uvc_opts(item); | |
2135 | ||
2136 | usb_put_function_instance(&opts->func_inst); | |
2137 | } | |
2138 | ||
2139 | static struct configfs_item_operations uvc_item_ops = { | |
2140 | .release = uvc_attr_release, | |
46919a23 AP |
2141 | }; |
2142 | ||
2143 | #define UVCG_OPTS_ATTR(cname, conv, str2u, uxx, vnoc, limit) \ | |
2144 | static ssize_t f_uvc_opts_##cname##_show( \ | |
76e0da34 | 2145 | struct config_item *item, char *page) \ |
46919a23 | 2146 | { \ |
76e0da34 | 2147 | struct f_uvc_opts *opts = to_f_uvc_opts(item); \ |
46919a23 AP |
2148 | int result; \ |
2149 | \ | |
2150 | mutex_lock(&opts->lock); \ | |
2151 | result = sprintf(page, "%d\n", conv(opts->cname)); \ | |
2152 | mutex_unlock(&opts->lock); \ | |
2153 | \ | |
2154 | return result; \ | |
2155 | } \ | |
2156 | \ | |
2157 | static ssize_t \ | |
76e0da34 | 2158 | f_uvc_opts_##cname##_store(struct config_item *item, \ |
46919a23 AP |
2159 | const char *page, size_t len) \ |
2160 | { \ | |
76e0da34 | 2161 | struct f_uvc_opts *opts = to_f_uvc_opts(item); \ |
46919a23 AP |
2162 | int ret; \ |
2163 | uxx num; \ | |
2164 | \ | |
2165 | mutex_lock(&opts->lock); \ | |
2166 | if (opts->refcnt) { \ | |
2167 | ret = -EBUSY; \ | |
2168 | goto end; \ | |
2169 | } \ | |
2170 | \ | |
2171 | ret = str2u(page, 0, &num); \ | |
2172 | if (ret) \ | |
2173 | goto end; \ | |
2174 | \ | |
2175 | if (num > limit) { \ | |
2176 | ret = -EINVAL; \ | |
2177 | goto end; \ | |
2178 | } \ | |
2179 | opts->cname = vnoc(num); \ | |
2180 | ret = len; \ | |
2181 | end: \ | |
2182 | mutex_unlock(&opts->lock); \ | |
2183 | return ret; \ | |
2184 | } \ | |
2185 | \ | |
76e0da34 | 2186 | UVC_ATTR(f_uvc_opts_, cname, aname) |
46919a23 AP |
2187 | |
2188 | #define identity_conv(x) (x) | |
2189 | ||
2190 | UVCG_OPTS_ATTR(streaming_interval, identity_conv, kstrtou8, u8, identity_conv, | |
2191 | 16); | |
2192 | UVCG_OPTS_ATTR(streaming_maxpacket, le16_to_cpu, kstrtou16, u16, le16_to_cpu, | |
2193 | 3072); | |
2194 | UVCG_OPTS_ATTR(streaming_maxburst, identity_conv, kstrtou8, u8, identity_conv, | |
2195 | 15); | |
2196 | ||
2197 | #undef identity_conv | |
2198 | ||
2199 | #undef UVCG_OPTS_ATTR | |
2200 | ||
2201 | static struct configfs_attribute *uvc_attrs[] = { | |
76e0da34 CH |
2202 | &f_uvc_opts_attr_streaming_interval, |
2203 | &f_uvc_opts_attr_streaming_maxpacket, | |
2204 | &f_uvc_opts_attr_streaming_maxburst, | |
46919a23 AP |
2205 | NULL, |
2206 | }; | |
2207 | ||
2208 | static struct config_item_type uvc_func_type = { | |
2209 | .ct_item_ops = &uvc_item_ops, | |
2210 | .ct_attrs = uvc_attrs, | |
2211 | .ct_owner = THIS_MODULE, | |
2212 | }; | |
2213 | ||
46919a23 AP |
2214 | int uvcg_attach_configfs(struct f_uvc_opts *opts) |
2215 | { | |
2216 | config_group_init_type_name(&uvcg_control_header_grp.group, | |
2217 | "header", | |
2218 | &uvcg_control_header_grp_type); | |
1ae1602d | 2219 | |
46919a23 | 2220 | config_group_init_type_name(&uvcg_default_processing.group, |
1ae1602d CH |
2221 | "default", &uvcg_default_processing_type); |
2222 | config_group_init_type_name(&uvcg_processing_grp.group, | |
2223 | "processing", &uvcg_processing_grp_type); | |
2224 | configfs_add_default_group(&uvcg_default_processing.group, | |
2225 | &uvcg_processing_grp.group); | |
2226 | ||
46919a23 | 2227 | config_group_init_type_name(&uvcg_default_camera.group, |
1ae1602d CH |
2228 | "default", &uvcg_default_camera_type); |
2229 | config_group_init_type_name(&uvcg_camera_grp.group, | |
2230 | "camera", &uvcg_camera_grp_type); | |
2231 | configfs_add_default_group(&uvcg_default_camera.group, | |
2232 | &uvcg_camera_grp.group); | |
2233 | ||
46919a23 | 2234 | config_group_init_type_name(&uvcg_default_output.group, |
1ae1602d CH |
2235 | "default", &uvcg_default_output_type); |
2236 | config_group_init_type_name(&uvcg_output_grp.group, | |
2237 | "output", &uvcg_output_grp_type); | |
2238 | configfs_add_default_group(&uvcg_default_output.group, | |
2239 | &uvcg_output_grp.group); | |
2240 | ||
2241 | config_group_init_type_name(&uvcg_terminal_grp.group, | |
2242 | "terminal", &uvcg_terminal_grp_type); | |
2243 | configfs_add_default_group(&uvcg_camera_grp.group, | |
2244 | &uvcg_terminal_grp.group); | |
2245 | configfs_add_default_group(&uvcg_output_grp.group, | |
2246 | &uvcg_terminal_grp.group); | |
2247 | ||
46919a23 | 2248 | config_group_init_type_name(&uvcg_control_class_fs.group, |
1ae1602d | 2249 | "fs", &uvcg_control_class_type); |
46919a23 | 2250 | config_group_init_type_name(&uvcg_control_class_ss.group, |
1ae1602d CH |
2251 | "ss", &uvcg_control_class_type); |
2252 | config_group_init_type_name(&uvcg_control_class_grp.group, | |
46919a23 AP |
2253 | "class", |
2254 | &uvcg_control_class_grp_type); | |
1ae1602d CH |
2255 | configfs_add_default_group(&uvcg_control_class_fs.group, |
2256 | &uvcg_control_class_grp.group); | |
2257 | configfs_add_default_group(&uvcg_control_class_ss.group, | |
2258 | &uvcg_control_class_grp.group); | |
2259 | ||
2260 | config_group_init_type_name(&uvcg_control_grp.group, | |
46919a23 AP |
2261 | "control", |
2262 | &uvcg_control_grp_type); | |
1ae1602d CH |
2263 | configfs_add_default_group(&uvcg_control_header_grp.group, |
2264 | &uvcg_control_grp.group); | |
2265 | configfs_add_default_group(&uvcg_processing_grp.group, | |
2266 | &uvcg_control_grp.group); | |
2267 | configfs_add_default_group(&uvcg_terminal_grp.group, | |
2268 | &uvcg_control_grp.group); | |
2269 | configfs_add_default_group(&uvcg_control_class_grp.group, | |
2270 | &uvcg_control_grp.group); | |
2271 | ||
46919a23 AP |
2272 | config_group_init_type_name(&uvcg_streaming_header_grp.group, |
2273 | "header", | |
2274 | &uvcg_streaming_header_grp_type); | |
2275 | config_group_init_type_name(&uvcg_uncompressed_grp.group, | |
2276 | "uncompressed", | |
2277 | &uvcg_uncompressed_grp_type); | |
2278 | config_group_init_type_name(&uvcg_mjpeg_grp.group, | |
2279 | "mjpeg", | |
2280 | &uvcg_mjpeg_grp_type); | |
2281 | config_group_init_type_name(&uvcg_default_color_matching.group, | |
2282 | "default", | |
2283 | &uvcg_default_color_matching_type); | |
1ae1602d | 2284 | config_group_init_type_name(&uvcg_color_matching_grp.group, |
46919a23 AP |
2285 | "color_matching", |
2286 | &uvcg_color_matching_grp_type); | |
1ae1602d CH |
2287 | configfs_add_default_group(&uvcg_default_color_matching.group, |
2288 | &uvcg_color_matching_grp.group); | |
2289 | ||
46919a23 | 2290 | config_group_init_type_name(&uvcg_streaming_class_fs.group, |
1ae1602d | 2291 | "fs", &uvcg_streaming_class_type); |
46919a23 | 2292 | config_group_init_type_name(&uvcg_streaming_class_hs.group, |
1ae1602d | 2293 | "hs", &uvcg_streaming_class_type); |
46919a23 | 2294 | config_group_init_type_name(&uvcg_streaming_class_ss.group, |
1ae1602d CH |
2295 | "ss", &uvcg_streaming_class_type); |
2296 | config_group_init_type_name(&uvcg_streaming_class_grp.group, | |
2297 | "class", &uvcg_streaming_class_grp_type); | |
2298 | configfs_add_default_group(&uvcg_streaming_class_fs.group, | |
2299 | &uvcg_streaming_class_grp.group); | |
2300 | configfs_add_default_group(&uvcg_streaming_class_hs.group, | |
2301 | &uvcg_streaming_class_grp.group); | |
2302 | configfs_add_default_group(&uvcg_streaming_class_ss.group, | |
2303 | &uvcg_streaming_class_grp.group); | |
2304 | ||
2305 | config_group_init_type_name(&uvcg_streaming_grp.group, | |
2306 | "streaming", &uvcg_streaming_grp_type); | |
2307 | configfs_add_default_group(&uvcg_streaming_header_grp.group, | |
2308 | &uvcg_streaming_grp.group); | |
2309 | configfs_add_default_group(&uvcg_uncompressed_grp.group, | |
2310 | &uvcg_streaming_grp.group); | |
2311 | configfs_add_default_group(&uvcg_mjpeg_grp.group, | |
2312 | &uvcg_streaming_grp.group); | |
2313 | configfs_add_default_group(&uvcg_color_matching_grp.group, | |
2314 | &uvcg_streaming_grp.group); | |
2315 | configfs_add_default_group(&uvcg_streaming_class_grp.group, | |
2316 | &uvcg_streaming_grp.group); | |
2317 | ||
2318 | config_group_init_type_name(&opts->func_inst.group, | |
46919a23 AP |
2319 | "", |
2320 | &uvc_func_type); | |
1ae1602d CH |
2321 | configfs_add_default_group(&uvcg_control_grp.group, |
2322 | &opts->func_inst.group); | |
2323 | configfs_add_default_group(&uvcg_streaming_grp.group, | |
2324 | &opts->func_inst.group); | |
2325 | ||
46919a23 AP |
2326 | return 0; |
2327 | } |