diff options
Diffstat (limited to 'drivers/media/video/uvc/uvc_ctrl.c')
-rw-r--r-- | drivers/media/video/uvc/uvc_ctrl.c | 1256 |
1 files changed, 1256 insertions, 0 deletions
diff --git a/drivers/media/video/uvc/uvc_ctrl.c b/drivers/media/video/uvc/uvc_ctrl.c new file mode 100644 index 000000000000..f0ee46d15540 --- /dev/null +++ b/drivers/media/video/uvc/uvc_ctrl.c | |||
@@ -0,0 +1,1256 @@ | |||
1 | /* | ||
2 | * uvc_ctrl.c -- USB Video Class driver - Controls | ||
3 | * | ||
4 | * Copyright (C) 2005-2008 | ||
5 | * Laurent Pinchart (laurent.pinchart@skynet.be) | ||
6 | * | ||
7 | * This program is free software; you can redistribute it and/or modify | ||
8 | * it under the terms of the GNU General Public License as published by | ||
9 | * the Free Software Foundation; either version 2 of the License, or | ||
10 | * (at your option) any later version. | ||
11 | * | ||
12 | */ | ||
13 | |||
14 | #include <linux/kernel.h> | ||
15 | #include <linux/version.h> | ||
16 | #include <linux/list.h> | ||
17 | #include <linux/module.h> | ||
18 | #include <linux/uaccess.h> | ||
19 | #include <linux/usb.h> | ||
20 | #include <linux/videodev2.h> | ||
21 | #include <linux/vmalloc.h> | ||
22 | #include <linux/wait.h> | ||
23 | #include <asm/atomic.h> | ||
24 | |||
25 | #include "uvcvideo.h" | ||
26 | |||
27 | #define UVC_CTRL_NDATA 2 | ||
28 | #define UVC_CTRL_DATA_CURRENT 0 | ||
29 | #define UVC_CTRL_DATA_BACKUP 1 | ||
30 | |||
31 | /* ------------------------------------------------------------------------ | ||
32 | * Control, formats, ... | ||
33 | */ | ||
34 | |||
35 | static struct uvc_control_info uvc_ctrls[] = { | ||
36 | { | ||
37 | .entity = UVC_GUID_UVC_PROCESSING, | ||
38 | .selector = PU_BRIGHTNESS_CONTROL, | ||
39 | .index = 0, | ||
40 | .size = 2, | ||
41 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
42 | | UVC_CONTROL_RESTORE, | ||
43 | }, | ||
44 | { | ||
45 | .entity = UVC_GUID_UVC_PROCESSING, | ||
46 | .selector = PU_CONTRAST_CONTROL, | ||
47 | .index = 1, | ||
48 | .size = 2, | ||
49 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
50 | | UVC_CONTROL_RESTORE, | ||
51 | }, | ||
52 | { | ||
53 | .entity = UVC_GUID_UVC_PROCESSING, | ||
54 | .selector = PU_HUE_CONTROL, | ||
55 | .index = 2, | ||
56 | .size = 2, | ||
57 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
58 | | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, | ||
59 | }, | ||
60 | { | ||
61 | .entity = UVC_GUID_UVC_PROCESSING, | ||
62 | .selector = PU_SATURATION_CONTROL, | ||
63 | .index = 3, | ||
64 | .size = 2, | ||
65 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
66 | | UVC_CONTROL_RESTORE, | ||
67 | }, | ||
68 | { | ||
69 | .entity = UVC_GUID_UVC_PROCESSING, | ||
70 | .selector = PU_SHARPNESS_CONTROL, | ||
71 | .index = 4, | ||
72 | .size = 2, | ||
73 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
74 | | UVC_CONTROL_RESTORE, | ||
75 | }, | ||
76 | { | ||
77 | .entity = UVC_GUID_UVC_PROCESSING, | ||
78 | .selector = PU_GAMMA_CONTROL, | ||
79 | .index = 5, | ||
80 | .size = 2, | ||
81 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
82 | | UVC_CONTROL_RESTORE, | ||
83 | }, | ||
84 | { | ||
85 | .entity = UVC_GUID_UVC_PROCESSING, | ||
86 | .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, | ||
87 | .index = 8, | ||
88 | .size = 2, | ||
89 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
90 | | UVC_CONTROL_RESTORE, | ||
91 | }, | ||
92 | { | ||
93 | .entity = UVC_GUID_UVC_PROCESSING, | ||
94 | .selector = PU_GAIN_CONTROL, | ||
95 | .index = 9, | ||
96 | .size = 2, | ||
97 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
98 | | UVC_CONTROL_RESTORE, | ||
99 | }, | ||
100 | { | ||
101 | .entity = UVC_GUID_UVC_PROCESSING, | ||
102 | .selector = PU_POWER_LINE_FREQUENCY_CONTROL, | ||
103 | .index = 10, | ||
104 | .size = 1, | ||
105 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
106 | | UVC_CONTROL_RESTORE, | ||
107 | }, | ||
108 | { | ||
109 | .entity = UVC_GUID_UVC_PROCESSING, | ||
110 | .selector = PU_HUE_AUTO_CONTROL, | ||
111 | .index = 11, | ||
112 | .size = 1, | ||
113 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR | ||
114 | | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, | ||
115 | }, | ||
116 | { | ||
117 | .entity = UVC_GUID_UVC_CAMERA, | ||
118 | .selector = CT_AE_MODE_CONTROL, | ||
119 | .index = 1, | ||
120 | .size = 1, | ||
121 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR | ||
122 | | UVC_CONTROL_GET_DEF | UVC_CONTROL_GET_RES | ||
123 | | UVC_CONTROL_RESTORE, | ||
124 | }, | ||
125 | { | ||
126 | .entity = UVC_GUID_UVC_CAMERA, | ||
127 | .selector = CT_AE_PRIORITY_CONTROL, | ||
128 | .index = 2, | ||
129 | .size = 1, | ||
130 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR | ||
131 | | UVC_CONTROL_RESTORE, | ||
132 | }, | ||
133 | { | ||
134 | .entity = UVC_GUID_UVC_CAMERA, | ||
135 | .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, | ||
136 | .index = 3, | ||
137 | .size = 4, | ||
138 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
139 | | UVC_CONTROL_RESTORE, | ||
140 | }, | ||
141 | { | ||
142 | .entity = UVC_GUID_UVC_CAMERA, | ||
143 | .selector = CT_FOCUS_ABSOLUTE_CONTROL, | ||
144 | .index = 5, | ||
145 | .size = 2, | ||
146 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
147 | | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, | ||
148 | }, | ||
149 | { | ||
150 | .entity = UVC_GUID_UVC_CAMERA, | ||
151 | .selector = CT_FOCUS_AUTO_CONTROL, | ||
152 | .index = 17, | ||
153 | .size = 1, | ||
154 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR | ||
155 | | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, | ||
156 | }, | ||
157 | { | ||
158 | .entity = UVC_GUID_UVC_PROCESSING, | ||
159 | .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, | ||
160 | .index = 12, | ||
161 | .size = 1, | ||
162 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR | ||
163 | | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, | ||
164 | }, | ||
165 | { | ||
166 | .entity = UVC_GUID_UVC_PROCESSING, | ||
167 | .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, | ||
168 | .index = 6, | ||
169 | .size = 2, | ||
170 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
171 | | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, | ||
172 | }, | ||
173 | { | ||
174 | .entity = UVC_GUID_UVC_PROCESSING, | ||
175 | .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, | ||
176 | .index = 13, | ||
177 | .size = 1, | ||
178 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_CUR | ||
179 | | UVC_CONTROL_GET_DEF | UVC_CONTROL_RESTORE, | ||
180 | }, | ||
181 | { | ||
182 | .entity = UVC_GUID_UVC_PROCESSING, | ||
183 | .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, | ||
184 | .index = 7, | ||
185 | .size = 4, | ||
186 | .flags = UVC_CONTROL_SET_CUR | UVC_CONTROL_GET_RANGE | ||
187 | | UVC_CONTROL_RESTORE | UVC_CONTROL_AUTO_UPDATE, | ||
188 | }, | ||
189 | }; | ||
190 | |||
191 | static struct uvc_menu_info power_line_frequency_controls[] = { | ||
192 | { 0, "Disabled" }, | ||
193 | { 1, "50 Hz" }, | ||
194 | { 2, "60 Hz" }, | ||
195 | }; | ||
196 | |||
197 | static struct uvc_menu_info exposure_auto_controls[] = { | ||
198 | { 1, "Manual Mode" }, | ||
199 | { 2, "Auto Mode" }, | ||
200 | { 4, "Shutter Priority Mode" }, | ||
201 | { 8, "Aperture Priority Mode" }, | ||
202 | }; | ||
203 | |||
204 | static struct uvc_control_mapping uvc_ctrl_mappings[] = { | ||
205 | { | ||
206 | .id = V4L2_CID_BRIGHTNESS, | ||
207 | .name = "Brightness", | ||
208 | .entity = UVC_GUID_UVC_PROCESSING, | ||
209 | .selector = PU_BRIGHTNESS_CONTROL, | ||
210 | .size = 16, | ||
211 | .offset = 0, | ||
212 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
213 | .data_type = UVC_CTRL_DATA_TYPE_SIGNED, | ||
214 | }, | ||
215 | { | ||
216 | .id = V4L2_CID_CONTRAST, | ||
217 | .name = "Contrast", | ||
218 | .entity = UVC_GUID_UVC_PROCESSING, | ||
219 | .selector = PU_CONTRAST_CONTROL, | ||
220 | .size = 16, | ||
221 | .offset = 0, | ||
222 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
223 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
224 | }, | ||
225 | { | ||
226 | .id = V4L2_CID_HUE, | ||
227 | .name = "Hue", | ||
228 | .entity = UVC_GUID_UVC_PROCESSING, | ||
229 | .selector = PU_HUE_CONTROL, | ||
230 | .size = 16, | ||
231 | .offset = 0, | ||
232 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
233 | .data_type = UVC_CTRL_DATA_TYPE_SIGNED, | ||
234 | }, | ||
235 | { | ||
236 | .id = V4L2_CID_SATURATION, | ||
237 | .name = "Saturation", | ||
238 | .entity = UVC_GUID_UVC_PROCESSING, | ||
239 | .selector = PU_SATURATION_CONTROL, | ||
240 | .size = 16, | ||
241 | .offset = 0, | ||
242 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
243 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
244 | }, | ||
245 | { | ||
246 | .id = V4L2_CID_SHARPNESS, | ||
247 | .name = "Sharpness", | ||
248 | .entity = UVC_GUID_UVC_PROCESSING, | ||
249 | .selector = PU_SHARPNESS_CONTROL, | ||
250 | .size = 16, | ||
251 | .offset = 0, | ||
252 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
253 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
254 | }, | ||
255 | { | ||
256 | .id = V4L2_CID_GAMMA, | ||
257 | .name = "Gamma", | ||
258 | .entity = UVC_GUID_UVC_PROCESSING, | ||
259 | .selector = PU_GAMMA_CONTROL, | ||
260 | .size = 16, | ||
261 | .offset = 0, | ||
262 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
263 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
264 | }, | ||
265 | { | ||
266 | .id = V4L2_CID_BACKLIGHT_COMPENSATION, | ||
267 | .name = "Backlight Compensation", | ||
268 | .entity = UVC_GUID_UVC_PROCESSING, | ||
269 | .selector = PU_BACKLIGHT_COMPENSATION_CONTROL, | ||
270 | .size = 16, | ||
271 | .offset = 0, | ||
272 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
273 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
274 | }, | ||
275 | { | ||
276 | .id = V4L2_CID_GAIN, | ||
277 | .name = "Gain", | ||
278 | .entity = UVC_GUID_UVC_PROCESSING, | ||
279 | .selector = PU_GAIN_CONTROL, | ||
280 | .size = 16, | ||
281 | .offset = 0, | ||
282 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
283 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
284 | }, | ||
285 | { | ||
286 | .id = V4L2_CID_POWER_LINE_FREQUENCY, | ||
287 | .name = "Power Line Frequency", | ||
288 | .entity = UVC_GUID_UVC_PROCESSING, | ||
289 | .selector = PU_POWER_LINE_FREQUENCY_CONTROL, | ||
290 | .size = 2, | ||
291 | .offset = 0, | ||
292 | .v4l2_type = V4L2_CTRL_TYPE_MENU, | ||
293 | .data_type = UVC_CTRL_DATA_TYPE_ENUM, | ||
294 | .menu_info = power_line_frequency_controls, | ||
295 | .menu_count = ARRAY_SIZE(power_line_frequency_controls), | ||
296 | }, | ||
297 | { | ||
298 | .id = V4L2_CID_HUE_AUTO, | ||
299 | .name = "Hue, Auto", | ||
300 | .entity = UVC_GUID_UVC_PROCESSING, | ||
301 | .selector = PU_HUE_AUTO_CONTROL, | ||
302 | .size = 1, | ||
303 | .offset = 0, | ||
304 | .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, | ||
305 | .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, | ||
306 | }, | ||
307 | { | ||
308 | .id = V4L2_CID_EXPOSURE_AUTO, | ||
309 | .name = "Exposure, Auto", | ||
310 | .entity = UVC_GUID_UVC_CAMERA, | ||
311 | .selector = CT_AE_MODE_CONTROL, | ||
312 | .size = 4, | ||
313 | .offset = 0, | ||
314 | .v4l2_type = V4L2_CTRL_TYPE_MENU, | ||
315 | .data_type = UVC_CTRL_DATA_TYPE_BITMASK, | ||
316 | .menu_info = exposure_auto_controls, | ||
317 | .menu_count = ARRAY_SIZE(exposure_auto_controls), | ||
318 | }, | ||
319 | { | ||
320 | .id = V4L2_CID_EXPOSURE_AUTO_PRIORITY, | ||
321 | .name = "Exposure, Auto Priority", | ||
322 | .entity = UVC_GUID_UVC_CAMERA, | ||
323 | .selector = CT_AE_PRIORITY_CONTROL, | ||
324 | .size = 1, | ||
325 | .offset = 0, | ||
326 | .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, | ||
327 | .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, | ||
328 | }, | ||
329 | { | ||
330 | .id = V4L2_CID_EXPOSURE_ABSOLUTE, | ||
331 | .name = "Exposure (Absolute)", | ||
332 | .entity = UVC_GUID_UVC_CAMERA, | ||
333 | .selector = CT_EXPOSURE_TIME_ABSOLUTE_CONTROL, | ||
334 | .size = 32, | ||
335 | .offset = 0, | ||
336 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
337 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
338 | }, | ||
339 | { | ||
340 | .id = V4L2_CID_AUTO_WHITE_BALANCE, | ||
341 | .name = "White Balance Temperature, Auto", | ||
342 | .entity = UVC_GUID_UVC_PROCESSING, | ||
343 | .selector = PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL, | ||
344 | .size = 1, | ||
345 | .offset = 0, | ||
346 | .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, | ||
347 | .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, | ||
348 | }, | ||
349 | { | ||
350 | .id = V4L2_CID_WHITE_BALANCE_TEMPERATURE, | ||
351 | .name = "White Balance Temperature", | ||
352 | .entity = UVC_GUID_UVC_PROCESSING, | ||
353 | .selector = PU_WHITE_BALANCE_TEMPERATURE_CONTROL, | ||
354 | .size = 16, | ||
355 | .offset = 0, | ||
356 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
357 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
358 | }, | ||
359 | { | ||
360 | .id = V4L2_CID_AUTO_WHITE_BALANCE, | ||
361 | .name = "White Balance Component, Auto", | ||
362 | .entity = UVC_GUID_UVC_PROCESSING, | ||
363 | .selector = PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL, | ||
364 | .size = 1, | ||
365 | .offset = 0, | ||
366 | .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, | ||
367 | .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, | ||
368 | }, | ||
369 | { | ||
370 | .id = V4L2_CID_BLUE_BALANCE, | ||
371 | .name = "White Balance Blue Component", | ||
372 | .entity = UVC_GUID_UVC_PROCESSING, | ||
373 | .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, | ||
374 | .size = 16, | ||
375 | .offset = 0, | ||
376 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
377 | .data_type = UVC_CTRL_DATA_TYPE_SIGNED, | ||
378 | }, | ||
379 | { | ||
380 | .id = V4L2_CID_RED_BALANCE, | ||
381 | .name = "White Balance Red Component", | ||
382 | .entity = UVC_GUID_UVC_PROCESSING, | ||
383 | .selector = PU_WHITE_BALANCE_COMPONENT_CONTROL, | ||
384 | .size = 16, | ||
385 | .offset = 16, | ||
386 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
387 | .data_type = UVC_CTRL_DATA_TYPE_SIGNED, | ||
388 | }, | ||
389 | { | ||
390 | .id = V4L2_CID_FOCUS_ABSOLUTE, | ||
391 | .name = "Focus (absolute)", | ||
392 | .entity = UVC_GUID_UVC_CAMERA, | ||
393 | .selector = CT_FOCUS_ABSOLUTE_CONTROL, | ||
394 | .size = 16, | ||
395 | .offset = 0, | ||
396 | .v4l2_type = V4L2_CTRL_TYPE_INTEGER, | ||
397 | .data_type = UVC_CTRL_DATA_TYPE_UNSIGNED, | ||
398 | }, | ||
399 | { | ||
400 | .id = V4L2_CID_FOCUS_AUTO, | ||
401 | .name = "Focus, Auto", | ||
402 | .entity = UVC_GUID_UVC_CAMERA, | ||
403 | .selector = CT_FOCUS_AUTO_CONTROL, | ||
404 | .size = 1, | ||
405 | .offset = 0, | ||
406 | .v4l2_type = V4L2_CTRL_TYPE_BOOLEAN, | ||
407 | .data_type = UVC_CTRL_DATA_TYPE_BOOLEAN, | ||
408 | }, | ||
409 | }; | ||
410 | |||
411 | /* ------------------------------------------------------------------------ | ||
412 | * Utility functions | ||
413 | */ | ||
414 | |||
415 | static inline __u8 *uvc_ctrl_data(struct uvc_control *ctrl, int id) | ||
416 | { | ||
417 | return ctrl->data + id * ctrl->info->size; | ||
418 | } | ||
419 | |||
420 | static inline int uvc_get_bit(const __u8 *data, int bit) | ||
421 | { | ||
422 | return (data[bit >> 3] >> (bit & 7)) & 1; | ||
423 | } | ||
424 | |||
425 | /* Extract the bit string specified by mapping->offset and mapping->size | ||
426 | * from the little-endian data stored at 'data' and return the result as | ||
427 | * a signed 32bit integer. Sign extension will be performed if the mapping | ||
428 | * references a signed data type. | ||
429 | */ | ||
430 | static __s32 uvc_get_le_value(const __u8 *data, | ||
431 | struct uvc_control_mapping *mapping) | ||
432 | { | ||
433 | int bits = mapping->size; | ||
434 | int offset = mapping->offset; | ||
435 | __s32 value = 0; | ||
436 | __u8 mask; | ||
437 | |||
438 | data += offset / 8; | ||
439 | offset &= 7; | ||
440 | mask = ((1LL << bits) - 1) << offset; | ||
441 | |||
442 | for (; bits > 0; data++) { | ||
443 | __u8 byte = *data & mask; | ||
444 | value |= offset > 0 ? (byte >> offset) : (byte << (-offset)); | ||
445 | bits -= 8 - (offset > 0 ? offset : 0); | ||
446 | offset -= 8; | ||
447 | mask = (1 << bits) - 1; | ||
448 | } | ||
449 | |||
450 | /* Sign-extend the value if needed */ | ||
451 | if (mapping->data_type == UVC_CTRL_DATA_TYPE_SIGNED) | ||
452 | value |= -(value & (1 << (mapping->size - 1))); | ||
453 | |||
454 | return value; | ||
455 | } | ||
456 | |||
457 | /* Set the bit string specified by mapping->offset and mapping->size | ||
458 | * in the little-endian data stored at 'data' to the value 'value'. | ||
459 | */ | ||
460 | static void uvc_set_le_value(__s32 value, __u8 *data, | ||
461 | struct uvc_control_mapping *mapping) | ||
462 | { | ||
463 | int bits = mapping->size; | ||
464 | int offset = mapping->offset; | ||
465 | __u8 mask; | ||
466 | |||
467 | data += offset / 8; | ||
468 | offset &= 7; | ||
469 | |||
470 | for (; bits > 0; data++) { | ||
471 | mask = ((1LL << bits) - 1) << offset; | ||
472 | *data = (*data & ~mask) | ((value << offset) & mask); | ||
473 | value >>= offset ? offset : 8; | ||
474 | bits -= 8 - offset; | ||
475 | offset = 0; | ||
476 | } | ||
477 | } | ||
478 | |||
479 | /* ------------------------------------------------------------------------ | ||
480 | * Terminal and unit management | ||
481 | */ | ||
482 | |||
483 | static const __u8 uvc_processing_guid[16] = UVC_GUID_UVC_PROCESSING; | ||
484 | static const __u8 uvc_camera_guid[16] = UVC_GUID_UVC_CAMERA; | ||
485 | static const __u8 uvc_media_transport_input_guid[16] = | ||
486 | UVC_GUID_UVC_MEDIA_TRANSPORT_INPUT; | ||
487 | |||
488 | static int uvc_entity_match_guid(struct uvc_entity *entity, __u8 guid[16]) | ||
489 | { | ||
490 | switch (UVC_ENTITY_TYPE(entity)) { | ||
491 | case ITT_CAMERA: | ||
492 | return memcmp(uvc_camera_guid, guid, 16) == 0; | ||
493 | |||
494 | case ITT_MEDIA_TRANSPORT_INPUT: | ||
495 | return memcmp(uvc_media_transport_input_guid, guid, 16) == 0; | ||
496 | |||
497 | case VC_PROCESSING_UNIT: | ||
498 | return memcmp(uvc_processing_guid, guid, 16) == 0; | ||
499 | |||
500 | case VC_EXTENSION_UNIT: | ||
501 | return memcmp(entity->extension.guidExtensionCode, | ||
502 | guid, 16) == 0; | ||
503 | |||
504 | default: | ||
505 | return 0; | ||
506 | } | ||
507 | } | ||
508 | |||
509 | /* ------------------------------------------------------------------------ | ||
510 | * UVC Controls | ||
511 | */ | ||
512 | |||
513 | static void __uvc_find_control(struct uvc_entity *entity, __u32 v4l2_id, | ||
514 | struct uvc_control_mapping **mapping, struct uvc_control **control, | ||
515 | int next) | ||
516 | { | ||
517 | struct uvc_control *ctrl; | ||
518 | struct uvc_control_mapping *map; | ||
519 | unsigned int i; | ||
520 | |||
521 | if (entity == NULL) | ||
522 | return; | ||
523 | |||
524 | for (i = 0; i < entity->ncontrols; ++i) { | ||
525 | ctrl = &entity->controls[i]; | ||
526 | if (ctrl->info == NULL) | ||
527 | continue; | ||
528 | |||
529 | list_for_each_entry(map, &ctrl->info->mappings, list) { | ||
530 | if ((map->id == v4l2_id) && !next) { | ||
531 | *control = ctrl; | ||
532 | *mapping = map; | ||
533 | return; | ||
534 | } | ||
535 | |||
536 | if ((*mapping == NULL || (*mapping)->id > map->id) && | ||
537 | (map->id > v4l2_id) && next) { | ||
538 | *control = ctrl; | ||
539 | *mapping = map; | ||
540 | } | ||
541 | } | ||
542 | } | ||
543 | } | ||
544 | |||
545 | struct uvc_control *uvc_find_control(struct uvc_video_device *video, | ||
546 | __u32 v4l2_id, struct uvc_control_mapping **mapping) | ||
547 | { | ||
548 | struct uvc_control *ctrl = NULL; | ||
549 | struct uvc_entity *entity; | ||
550 | int next = v4l2_id & V4L2_CTRL_FLAG_NEXT_CTRL; | ||
551 | |||
552 | *mapping = NULL; | ||
553 | |||
554 | /* Mask the query flags. */ | ||
555 | v4l2_id &= V4L2_CTRL_ID_MASK; | ||
556 | |||
557 | /* Find the control. */ | ||
558 | __uvc_find_control(video->processing, v4l2_id, mapping, &ctrl, next); | ||
559 | if (ctrl && !next) | ||
560 | return ctrl; | ||
561 | |||
562 | list_for_each_entry(entity, &video->iterms, chain) { | ||
563 | __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); | ||
564 | if (ctrl && !next) | ||
565 | return ctrl; | ||
566 | } | ||
567 | |||
568 | list_for_each_entry(entity, &video->extensions, chain) { | ||
569 | __uvc_find_control(entity, v4l2_id, mapping, &ctrl, next); | ||
570 | if (ctrl && !next) | ||
571 | return ctrl; | ||
572 | } | ||
573 | |||
574 | if (ctrl == NULL && !next) | ||
575 | uvc_trace(UVC_TRACE_CONTROL, "Control 0x%08x not found.\n", | ||
576 | v4l2_id); | ||
577 | |||
578 | return ctrl; | ||
579 | } | ||
580 | |||
581 | int uvc_query_v4l2_ctrl(struct uvc_video_device *video, | ||
582 | struct v4l2_queryctrl *v4l2_ctrl) | ||
583 | { | ||
584 | struct uvc_control *ctrl; | ||
585 | struct uvc_control_mapping *mapping; | ||
586 | struct uvc_menu_info *menu; | ||
587 | unsigned int i; | ||
588 | __u8 data[8]; | ||
589 | int ret; | ||
590 | |||
591 | ctrl = uvc_find_control(video, v4l2_ctrl->id, &mapping); | ||
592 | if (ctrl == NULL) | ||
593 | return -EINVAL; | ||
594 | |||
595 | v4l2_ctrl->id = mapping->id; | ||
596 | v4l2_ctrl->type = mapping->v4l2_type; | ||
597 | strncpy(v4l2_ctrl->name, mapping->name, sizeof v4l2_ctrl->name); | ||
598 | v4l2_ctrl->flags = 0; | ||
599 | |||
600 | if (!(ctrl->info->flags & UVC_CONTROL_SET_CUR)) | ||
601 | v4l2_ctrl->flags |= V4L2_CTRL_FLAG_READ_ONLY; | ||
602 | |||
603 | if (ctrl->info->flags & UVC_CONTROL_GET_DEF) { | ||
604 | if ((ret = uvc_query_ctrl(video->dev, GET_DEF, ctrl->entity->id, | ||
605 | video->dev->intfnum, ctrl->info->selector, | ||
606 | &data, ctrl->info->size)) < 0) | ||
607 | return ret; | ||
608 | v4l2_ctrl->default_value = uvc_get_le_value(data, mapping); | ||
609 | } | ||
610 | |||
611 | if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { | ||
612 | v4l2_ctrl->minimum = 0; | ||
613 | v4l2_ctrl->maximum = mapping->menu_count - 1; | ||
614 | v4l2_ctrl->step = 1; | ||
615 | |||
616 | menu = mapping->menu_info; | ||
617 | for (i = 0; i < mapping->menu_count; ++i, ++menu) { | ||
618 | if (menu->value == v4l2_ctrl->default_value) { | ||
619 | v4l2_ctrl->default_value = i; | ||
620 | break; | ||
621 | } | ||
622 | } | ||
623 | |||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | if (ctrl->info->flags & UVC_CONTROL_GET_MIN) { | ||
628 | if ((ret = uvc_query_ctrl(video->dev, GET_MIN, ctrl->entity->id, | ||
629 | video->dev->intfnum, ctrl->info->selector, | ||
630 | &data, ctrl->info->size)) < 0) | ||
631 | return ret; | ||
632 | v4l2_ctrl->minimum = uvc_get_le_value(data, mapping); | ||
633 | } | ||
634 | if (ctrl->info->flags & UVC_CONTROL_GET_MAX) { | ||
635 | if ((ret = uvc_query_ctrl(video->dev, GET_MAX, ctrl->entity->id, | ||
636 | video->dev->intfnum, ctrl->info->selector, | ||
637 | &data, ctrl->info->size)) < 0) | ||
638 | return ret; | ||
639 | v4l2_ctrl->maximum = uvc_get_le_value(data, mapping); | ||
640 | } | ||
641 | if (ctrl->info->flags & UVC_CONTROL_GET_RES) { | ||
642 | if ((ret = uvc_query_ctrl(video->dev, GET_RES, ctrl->entity->id, | ||
643 | video->dev->intfnum, ctrl->info->selector, | ||
644 | &data, ctrl->info->size)) < 0) | ||
645 | return ret; | ||
646 | v4l2_ctrl->step = uvc_get_le_value(data, mapping); | ||
647 | } | ||
648 | |||
649 | return 0; | ||
650 | } | ||
651 | |||
652 | |||
653 | /* -------------------------------------------------------------------------- | ||
654 | * Control transactions | ||
655 | * | ||
656 | * To make extended set operations as atomic as the hardware allows, controls | ||
657 | * are handled using begin/commit/rollback operations. | ||
658 | * | ||
659 | * At the beginning of a set request, uvc_ctrl_begin should be called to | ||
660 | * initialize the request. This function acquires the control lock. | ||
661 | * | ||
662 | * When setting a control, the new value is stored in the control data field | ||
663 | * at position UVC_CTRL_DATA_CURRENT. The control is then marked as dirty for | ||
664 | * later processing. If the UVC and V4L2 control sizes differ, the current | ||
665 | * value is loaded from the hardware before storing the new value in the data | ||
666 | * field. | ||
667 | * | ||
668 | * After processing all controls in the transaction, uvc_ctrl_commit or | ||
669 | * uvc_ctrl_rollback must be called to apply the pending changes to the | ||
670 | * hardware or revert them. When applying changes, all controls marked as | ||
671 | * dirty will be modified in the UVC device, and the dirty flag will be | ||
672 | * cleared. When reverting controls, the control data field | ||
673 | * UVC_CTRL_DATA_CURRENT is reverted to its previous value | ||
674 | * (UVC_CTRL_DATA_BACKUP) for all dirty controls. Both functions release the | ||
675 | * control lock. | ||
676 | */ | ||
677 | int uvc_ctrl_begin(struct uvc_video_device *video) | ||
678 | { | ||
679 | return mutex_lock_interruptible(&video->ctrl_mutex) ? -ERESTARTSYS : 0; | ||
680 | } | ||
681 | |||
682 | static int uvc_ctrl_commit_entity(struct uvc_device *dev, | ||
683 | struct uvc_entity *entity, int rollback) | ||
684 | { | ||
685 | struct uvc_control *ctrl; | ||
686 | unsigned int i; | ||
687 | int ret; | ||
688 | |||
689 | if (entity == NULL) | ||
690 | return 0; | ||
691 | |||
692 | for (i = 0; i < entity->ncontrols; ++i) { | ||
693 | ctrl = &entity->controls[i]; | ||
694 | if (ctrl->info == NULL || !ctrl->dirty) | ||
695 | continue; | ||
696 | |||
697 | if (!rollback) | ||
698 | ret = uvc_query_ctrl(dev, SET_CUR, ctrl->entity->id, | ||
699 | dev->intfnum, ctrl->info->selector, | ||
700 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
701 | ctrl->info->size); | ||
702 | else | ||
703 | ret = 0; | ||
704 | |||
705 | if (rollback || ret < 0) | ||
706 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
707 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | ||
708 | ctrl->info->size); | ||
709 | |||
710 | if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) | ||
711 | ctrl->loaded = 0; | ||
712 | |||
713 | ctrl->dirty = 0; | ||
714 | |||
715 | if (ret < 0) | ||
716 | return ret; | ||
717 | } | ||
718 | |||
719 | return 0; | ||
720 | } | ||
721 | |||
722 | int __uvc_ctrl_commit(struct uvc_video_device *video, int rollback) | ||
723 | { | ||
724 | struct uvc_entity *entity; | ||
725 | int ret = 0; | ||
726 | |||
727 | /* Find the control. */ | ||
728 | ret = uvc_ctrl_commit_entity(video->dev, video->processing, rollback); | ||
729 | if (ret < 0) | ||
730 | goto done; | ||
731 | |||
732 | list_for_each_entry(entity, &video->iterms, chain) { | ||
733 | ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); | ||
734 | if (ret < 0) | ||
735 | goto done; | ||
736 | } | ||
737 | |||
738 | list_for_each_entry(entity, &video->extensions, chain) { | ||
739 | ret = uvc_ctrl_commit_entity(video->dev, entity, rollback); | ||
740 | if (ret < 0) | ||
741 | goto done; | ||
742 | } | ||
743 | |||
744 | done: | ||
745 | mutex_unlock(&video->ctrl_mutex); | ||
746 | return ret; | ||
747 | } | ||
748 | |||
749 | int uvc_ctrl_get(struct uvc_video_device *video, | ||
750 | struct v4l2_ext_control *xctrl) | ||
751 | { | ||
752 | struct uvc_control *ctrl; | ||
753 | struct uvc_control_mapping *mapping; | ||
754 | struct uvc_menu_info *menu; | ||
755 | unsigned int i; | ||
756 | int ret; | ||
757 | |||
758 | ctrl = uvc_find_control(video, xctrl->id, &mapping); | ||
759 | if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) | ||
760 | return -EINVAL; | ||
761 | |||
762 | if (!ctrl->loaded) { | ||
763 | ret = uvc_query_ctrl(video->dev, GET_CUR, ctrl->entity->id, | ||
764 | video->dev->intfnum, ctrl->info->selector, | ||
765 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
766 | ctrl->info->size); | ||
767 | if (ret < 0) | ||
768 | return ret; | ||
769 | |||
770 | if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) | ||
771 | ctrl->loaded = 1; | ||
772 | } | ||
773 | |||
774 | xctrl->value = uvc_get_le_value( | ||
775 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); | ||
776 | |||
777 | if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { | ||
778 | menu = mapping->menu_info; | ||
779 | for (i = 0; i < mapping->menu_count; ++i, ++menu) { | ||
780 | if (menu->value == xctrl->value) { | ||
781 | xctrl->value = i; | ||
782 | break; | ||
783 | } | ||
784 | } | ||
785 | } | ||
786 | |||
787 | return 0; | ||
788 | } | ||
789 | |||
790 | int uvc_ctrl_set(struct uvc_video_device *video, | ||
791 | struct v4l2_ext_control *xctrl) | ||
792 | { | ||
793 | struct uvc_control *ctrl; | ||
794 | struct uvc_control_mapping *mapping; | ||
795 | s32 value = xctrl->value; | ||
796 | int ret; | ||
797 | |||
798 | ctrl = uvc_find_control(video, xctrl->id, &mapping); | ||
799 | if (ctrl == NULL || (ctrl->info->flags & UVC_CONTROL_SET_CUR) == 0) | ||
800 | return -EINVAL; | ||
801 | |||
802 | if (mapping->v4l2_type == V4L2_CTRL_TYPE_MENU) { | ||
803 | if (value < 0 || value >= mapping->menu_count) | ||
804 | return -EINVAL; | ||
805 | value = mapping->menu_info[value].value; | ||
806 | } | ||
807 | |||
808 | if (!ctrl->loaded && (ctrl->info->size * 8) != mapping->size) { | ||
809 | if ((ctrl->info->flags & UVC_CONTROL_GET_CUR) == 0) { | ||
810 | memset(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
811 | 0, ctrl->info->size); | ||
812 | } else { | ||
813 | ret = uvc_query_ctrl(video->dev, GET_CUR, | ||
814 | ctrl->entity->id, video->dev->intfnum, | ||
815 | ctrl->info->selector, | ||
816 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
817 | ctrl->info->size); | ||
818 | if (ret < 0) | ||
819 | return ret; | ||
820 | } | ||
821 | |||
822 | if ((ctrl->info->flags & UVC_CONTROL_AUTO_UPDATE) == 0) | ||
823 | ctrl->loaded = 1; | ||
824 | } | ||
825 | |||
826 | if (!ctrl->dirty) { | ||
827 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | ||
828 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
829 | ctrl->info->size); | ||
830 | } | ||
831 | |||
832 | uvc_set_le_value(value, | ||
833 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), mapping); | ||
834 | |||
835 | ctrl->dirty = 1; | ||
836 | ctrl->modified = 1; | ||
837 | return 0; | ||
838 | } | ||
839 | |||
840 | /* -------------------------------------------------------------------------- | ||
841 | * Dynamic controls | ||
842 | */ | ||
843 | |||
844 | int uvc_xu_ctrl_query(struct uvc_video_device *video, | ||
845 | struct uvc_xu_control *xctrl, int set) | ||
846 | { | ||
847 | struct uvc_entity *entity; | ||
848 | struct uvc_control *ctrl = NULL; | ||
849 | unsigned int i, found = 0; | ||
850 | __u8 *data; | ||
851 | int ret; | ||
852 | |||
853 | /* Find the extension unit. */ | ||
854 | list_for_each_entry(entity, &video->extensions, chain) { | ||
855 | if (entity->id == xctrl->unit) | ||
856 | break; | ||
857 | } | ||
858 | |||
859 | if (entity->id != xctrl->unit) { | ||
860 | uvc_trace(UVC_TRACE_CONTROL, "Extension unit %u not found.\n", | ||
861 | xctrl->unit); | ||
862 | return -EINVAL; | ||
863 | } | ||
864 | |||
865 | /* Find the control. */ | ||
866 | for (i = 0; i < entity->ncontrols; ++i) { | ||
867 | ctrl = &entity->controls[i]; | ||
868 | if (ctrl->info == NULL) | ||
869 | continue; | ||
870 | |||
871 | if (ctrl->info->selector == xctrl->selector) { | ||
872 | found = 1; | ||
873 | break; | ||
874 | } | ||
875 | } | ||
876 | |||
877 | if (!found) { | ||
878 | uvc_trace(UVC_TRACE_CONTROL, | ||
879 | "Control " UVC_GUID_FORMAT "/%u not found.\n", | ||
880 | UVC_GUID_ARGS(entity->extension.guidExtensionCode), | ||
881 | xctrl->selector); | ||
882 | return -EINVAL; | ||
883 | } | ||
884 | |||
885 | /* Validate control data size. */ | ||
886 | if (ctrl->info->size != xctrl->size) | ||
887 | return -EINVAL; | ||
888 | |||
889 | if ((set && !(ctrl->info->flags & UVC_CONTROL_SET_CUR)) || | ||
890 | (!set && !(ctrl->info->flags & UVC_CONTROL_GET_CUR))) | ||
891 | return -EINVAL; | ||
892 | |||
893 | if (mutex_lock_interruptible(&video->ctrl_mutex)) | ||
894 | return -ERESTARTSYS; | ||
895 | |||
896 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | ||
897 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
898 | xctrl->size); | ||
899 | data = uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT); | ||
900 | |||
901 | if (set && copy_from_user(data, xctrl->data, xctrl->size)) { | ||
902 | ret = -EFAULT; | ||
903 | goto out; | ||
904 | } | ||
905 | |||
906 | ret = uvc_query_ctrl(video->dev, set ? SET_CUR : GET_CUR, xctrl->unit, | ||
907 | video->dev->intfnum, xctrl->selector, data, | ||
908 | xctrl->size); | ||
909 | if (ret < 0) | ||
910 | goto out; | ||
911 | |||
912 | if (!set && copy_to_user(xctrl->data, data, xctrl->size)) { | ||
913 | ret = -EFAULT; | ||
914 | goto out; | ||
915 | } | ||
916 | |||
917 | out: | ||
918 | if (ret) | ||
919 | memcpy(uvc_ctrl_data(ctrl, UVC_CTRL_DATA_CURRENT), | ||
920 | uvc_ctrl_data(ctrl, UVC_CTRL_DATA_BACKUP), | ||
921 | xctrl->size); | ||
922 | |||
923 | mutex_unlock(&video->ctrl_mutex); | ||
924 | return ret; | ||
925 | } | ||
926 | |||
927 | /* -------------------------------------------------------------------------- | ||
928 | * Suspend/resume | ||
929 | */ | ||
930 | |||
931 | /* | ||
932 | * Restore control values after resume, skipping controls that haven't been | ||
933 | * changed. | ||
934 | * | ||
935 | * TODO | ||
936 | * - Don't restore modified controls that are back to their default value. | ||
937 | * - Handle restore order (Auto-Exposure Mode should be restored before | ||
938 | * Exposure Time). | ||
939 | */ | ||
940 | int uvc_ctrl_resume_device(struct uvc_device *dev) | ||
941 | { | ||
942 | struct uvc_control *ctrl; | ||
943 | struct uvc_entity *entity; | ||
944 | unsigned int i; | ||
945 | int ret; | ||
946 | |||
947 | /* Walk the entities list and restore controls when possible. */ | ||
948 | list_for_each_entry(entity, &dev->entities, list) { | ||
949 | |||
950 | for (i = 0; i < entity->ncontrols; ++i) { | ||
951 | ctrl = &entity->controls[i]; | ||
952 | |||
953 | if (ctrl->info == NULL || !ctrl->modified || | ||
954 | (ctrl->info->flags & UVC_CONTROL_RESTORE) == 0) | ||
955 | continue; | ||
956 | |||
957 | printk(KERN_INFO "restoring control " UVC_GUID_FORMAT | ||
958 | "/%u/%u\n", UVC_GUID_ARGS(ctrl->info->entity), | ||
959 | ctrl->info->index, ctrl->info->selector); | ||
960 | ctrl->dirty = 1; | ||
961 | } | ||
962 | |||
963 | ret = uvc_ctrl_commit_entity(dev, entity, 0); | ||
964 | if (ret < 0) | ||
965 | return ret; | ||
966 | } | ||
967 | |||
968 | return 0; | ||
969 | } | ||
970 | |||
971 | /* -------------------------------------------------------------------------- | ||
972 | * Control and mapping handling | ||
973 | */ | ||
974 | |||
975 | static void uvc_ctrl_add_ctrl(struct uvc_device *dev, | ||
976 | struct uvc_control_info *info) | ||
977 | { | ||
978 | struct uvc_entity *entity; | ||
979 | struct uvc_control *ctrl = NULL; | ||
980 | int ret, found = 0; | ||
981 | unsigned int i; | ||
982 | |||
983 | list_for_each_entry(entity, &dev->entities, list) { | ||
984 | if (!uvc_entity_match_guid(entity, info->entity)) | ||
985 | continue; | ||
986 | |||
987 | for (i = 0; i < entity->ncontrols; ++i) { | ||
988 | ctrl = &entity->controls[i]; | ||
989 | if (ctrl->index == info->index) { | ||
990 | found = 1; | ||
991 | break; | ||
992 | } | ||
993 | } | ||
994 | |||
995 | if (found) | ||
996 | break; | ||
997 | } | ||
998 | |||
999 | if (!found) | ||
1000 | return; | ||
1001 | |||
1002 | if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { | ||
1003 | /* Check if the device control information and length match | ||
1004 | * the user supplied information. | ||
1005 | */ | ||
1006 | __u32 flags; | ||
1007 | __le16 size; | ||
1008 | __u8 inf; | ||
1009 | |||
1010 | if ((ret = uvc_query_ctrl(dev, GET_LEN, ctrl->entity->id, | ||
1011 | dev->intfnum, info->selector, (__u8 *)&size, 2)) < 0) { | ||
1012 | uvc_trace(UVC_TRACE_CONTROL, "GET_LEN failed on " | ||
1013 | "control " UVC_GUID_FORMAT "/%u (%d).\n", | ||
1014 | UVC_GUID_ARGS(info->entity), info->selector, | ||
1015 | ret); | ||
1016 | return; | ||
1017 | } | ||
1018 | |||
1019 | if (info->size != le16_to_cpu(size)) { | ||
1020 | uvc_trace(UVC_TRACE_CONTROL, "Control " UVC_GUID_FORMAT | ||
1021 | "/%u size doesn't match user supplied " | ||
1022 | "value.\n", UVC_GUID_ARGS(info->entity), | ||
1023 | info->selector); | ||
1024 | return; | ||
1025 | } | ||
1026 | |||
1027 | if ((ret = uvc_query_ctrl(dev, GET_INFO, ctrl->entity->id, | ||
1028 | dev->intfnum, info->selector, &inf, 1)) < 0) { | ||
1029 | uvc_trace(UVC_TRACE_CONTROL, "GET_INFO failed on " | ||
1030 | "control " UVC_GUID_FORMAT "/%u (%d).\n", | ||
1031 | UVC_GUID_ARGS(info->entity), info->selector, | ||
1032 | ret); | ||
1033 | return; | ||
1034 | } | ||
1035 | |||
1036 | flags = info->flags; | ||
1037 | if (((flags & UVC_CONTROL_GET_CUR) && !(inf & (1 << 0))) || | ||
1038 | ((flags & UVC_CONTROL_SET_CUR) && !(inf & (1 << 1)))) { | ||
1039 | uvc_trace(UVC_TRACE_CONTROL, "Control " | ||
1040 | UVC_GUID_FORMAT "/%u flags don't match " | ||
1041 | "supported operations.\n", | ||
1042 | UVC_GUID_ARGS(info->entity), info->selector); | ||
1043 | return; | ||
1044 | } | ||
1045 | } | ||
1046 | |||
1047 | ctrl->info = info; | ||
1048 | ctrl->data = kmalloc(ctrl->info->size * UVC_CTRL_NDATA, GFP_KERNEL); | ||
1049 | uvc_trace(UVC_TRACE_CONTROL, "Added control " UVC_GUID_FORMAT "/%u " | ||
1050 | "to device %s entity %u\n", UVC_GUID_ARGS(ctrl->info->entity), | ||
1051 | ctrl->info->selector, dev->udev->devpath, entity->id); | ||
1052 | } | ||
1053 | |||
1054 | /* | ||
1055 | * Add an item to the UVC control information list, and instantiate a control | ||
1056 | * structure for each device that supports the control. | ||
1057 | */ | ||
1058 | int uvc_ctrl_add_info(struct uvc_control_info *info) | ||
1059 | { | ||
1060 | struct uvc_control_info *ctrl; | ||
1061 | struct uvc_device *dev; | ||
1062 | int ret = 0; | ||
1063 | |||
1064 | /* Find matching controls by walking the devices, entities and | ||
1065 | * controls list. | ||
1066 | */ | ||
1067 | mutex_lock(&uvc_driver.ctrl_mutex); | ||
1068 | |||
1069 | /* First check if the list contains a control matching the new one. | ||
1070 | * Bail out if it does. | ||
1071 | */ | ||
1072 | list_for_each_entry(ctrl, &uvc_driver.controls, list) { | ||
1073 | if (memcmp(ctrl->entity, info->entity, 16)) | ||
1074 | continue; | ||
1075 | |||
1076 | if (ctrl->selector == info->selector) { | ||
1077 | uvc_trace(UVC_TRACE_CONTROL, "Control " | ||
1078 | UVC_GUID_FORMAT "/%u is already defined.\n", | ||
1079 | UVC_GUID_ARGS(info->entity), info->selector); | ||
1080 | ret = -EEXIST; | ||
1081 | goto end; | ||
1082 | } | ||
1083 | if (ctrl->index == info->index) { | ||
1084 | uvc_trace(UVC_TRACE_CONTROL, "Control " | ||
1085 | UVC_GUID_FORMAT "/%u would overwrite index " | ||
1086 | "%d.\n", UVC_GUID_ARGS(info->entity), | ||
1087 | info->selector, info->index); | ||
1088 | ret = -EEXIST; | ||
1089 | goto end; | ||
1090 | } | ||
1091 | } | ||
1092 | |||
1093 | list_for_each_entry(dev, &uvc_driver.devices, list) | ||
1094 | uvc_ctrl_add_ctrl(dev, info); | ||
1095 | |||
1096 | INIT_LIST_HEAD(&info->mappings); | ||
1097 | list_add_tail(&info->list, &uvc_driver.controls); | ||
1098 | end: | ||
1099 | mutex_unlock(&uvc_driver.ctrl_mutex); | ||
1100 | return ret; | ||
1101 | } | ||
1102 | |||
1103 | int uvc_ctrl_add_mapping(struct uvc_control_mapping *mapping) | ||
1104 | { | ||
1105 | struct uvc_control_info *info; | ||
1106 | struct uvc_control_mapping *map; | ||
1107 | int ret = -EINVAL; | ||
1108 | |||
1109 | if (mapping->id & ~V4L2_CTRL_ID_MASK) { | ||
1110 | uvc_trace(UVC_TRACE_CONTROL, "Can't add mapping '%s' with " | ||
1111 | "invalid control id 0x%08x\n", mapping->name, | ||
1112 | mapping->id); | ||
1113 | return -EINVAL; | ||
1114 | } | ||
1115 | |||
1116 | mutex_lock(&uvc_driver.ctrl_mutex); | ||
1117 | list_for_each_entry(info, &uvc_driver.controls, list) { | ||
1118 | if (memcmp(info->entity, mapping->entity, 16) || | ||
1119 | info->selector != mapping->selector) | ||
1120 | continue; | ||
1121 | |||
1122 | if (info->size * 8 < mapping->size + mapping->offset) { | ||
1123 | uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' would " | ||
1124 | "overflow control " UVC_GUID_FORMAT "/%u\n", | ||
1125 | mapping->name, UVC_GUID_ARGS(info->entity), | ||
1126 | info->selector); | ||
1127 | ret = -EOVERFLOW; | ||
1128 | goto end; | ||
1129 | } | ||
1130 | |||
1131 | /* Check if the list contains a mapping matching the new one. | ||
1132 | * Bail out if it does. | ||
1133 | */ | ||
1134 | list_for_each_entry(map, &info->mappings, list) { | ||
1135 | if (map->id == mapping->id) { | ||
1136 | uvc_trace(UVC_TRACE_CONTROL, "Mapping '%s' is " | ||
1137 | "already defined.\n", mapping->name); | ||
1138 | ret = -EEXIST; | ||
1139 | goto end; | ||
1140 | } | ||
1141 | } | ||
1142 | |||
1143 | mapping->ctrl = info; | ||
1144 | list_add_tail(&mapping->list, &info->mappings); | ||
1145 | uvc_trace(UVC_TRACE_CONTROL, "Adding mapping %s to control " | ||
1146 | UVC_GUID_FORMAT "/%u.\n", mapping->name, | ||
1147 | UVC_GUID_ARGS(info->entity), info->selector); | ||
1148 | |||
1149 | ret = 0; | ||
1150 | break; | ||
1151 | } | ||
1152 | end: | ||
1153 | mutex_unlock(&uvc_driver.ctrl_mutex); | ||
1154 | return ret; | ||
1155 | } | ||
1156 | |||
1157 | /* | ||
1158 | * Initialize device controls. | ||
1159 | */ | ||
1160 | int uvc_ctrl_init_device(struct uvc_device *dev) | ||
1161 | { | ||
1162 | struct uvc_control_info *info; | ||
1163 | struct uvc_control *ctrl; | ||
1164 | struct uvc_entity *entity; | ||
1165 | unsigned int i; | ||
1166 | |||
1167 | /* Walk the entities list and instantiate controls */ | ||
1168 | list_for_each_entry(entity, &dev->entities, list) { | ||
1169 | unsigned int bControlSize = 0, ncontrols = 0; | ||
1170 | __u8 *bmControls = NULL; | ||
1171 | |||
1172 | if (UVC_ENTITY_TYPE(entity) == VC_EXTENSION_UNIT) { | ||
1173 | bmControls = entity->extension.bmControls; | ||
1174 | bControlSize = entity->extension.bControlSize; | ||
1175 | } else if (UVC_ENTITY_TYPE(entity) == VC_PROCESSING_UNIT) { | ||
1176 | bmControls = entity->processing.bmControls; | ||
1177 | bControlSize = entity->processing.bControlSize; | ||
1178 | } else if (UVC_ENTITY_TYPE(entity) == ITT_CAMERA) { | ||
1179 | bmControls = entity->camera.bmControls; | ||
1180 | bControlSize = entity->camera.bControlSize; | ||
1181 | } | ||
1182 | |||
1183 | for (i = 0; i < bControlSize; ++i) | ||
1184 | ncontrols += hweight8(bmControls[i]); | ||
1185 | |||
1186 | if (ncontrols == 0) | ||
1187 | continue; | ||
1188 | |||
1189 | entity->controls = kzalloc(ncontrols*sizeof *ctrl, GFP_KERNEL); | ||
1190 | if (entity->controls == NULL) | ||
1191 | return -ENOMEM; | ||
1192 | |||
1193 | entity->ncontrols = ncontrols; | ||
1194 | |||
1195 | ctrl = entity->controls; | ||
1196 | for (i = 0; i < bControlSize * 8; ++i) { | ||
1197 | if (uvc_get_bit(bmControls, i) == 0) | ||
1198 | continue; | ||
1199 | |||
1200 | ctrl->entity = entity; | ||
1201 | ctrl->index = i; | ||
1202 | ctrl++; | ||
1203 | } | ||
1204 | } | ||
1205 | |||
1206 | /* Walk the controls info list and associate them with the device | ||
1207 | * controls, then add the device to the global device list. This has | ||
1208 | * to be done while holding the controls lock, to make sure | ||
1209 | * uvc_ctrl_add_info() will not get called in-between. | ||
1210 | */ | ||
1211 | mutex_lock(&uvc_driver.ctrl_mutex); | ||
1212 | list_for_each_entry(info, &uvc_driver.controls, list) | ||
1213 | uvc_ctrl_add_ctrl(dev, info); | ||
1214 | |||
1215 | list_add_tail(&dev->list, &uvc_driver.devices); | ||
1216 | mutex_unlock(&uvc_driver.ctrl_mutex); | ||
1217 | |||
1218 | return 0; | ||
1219 | } | ||
1220 | |||
1221 | /* | ||
1222 | * Cleanup device controls. | ||
1223 | */ | ||
1224 | void uvc_ctrl_cleanup_device(struct uvc_device *dev) | ||
1225 | { | ||
1226 | struct uvc_entity *entity; | ||
1227 | unsigned int i; | ||
1228 | |||
1229 | /* Remove the device from the global devices list */ | ||
1230 | mutex_lock(&uvc_driver.ctrl_mutex); | ||
1231 | if (dev->list.next != NULL) | ||
1232 | list_del(&dev->list); | ||
1233 | mutex_unlock(&uvc_driver.ctrl_mutex); | ||
1234 | |||
1235 | list_for_each_entry(entity, &dev->entities, list) { | ||
1236 | for (i = 0; i < entity->ncontrols; ++i) | ||
1237 | kfree(entity->controls[i].data); | ||
1238 | |||
1239 | kfree(entity->controls); | ||
1240 | } | ||
1241 | } | ||
1242 | |||
1243 | void uvc_ctrl_init(void) | ||
1244 | { | ||
1245 | struct uvc_control_info *ctrl = uvc_ctrls; | ||
1246 | struct uvc_control_info *cend = ctrl + ARRAY_SIZE(uvc_ctrls); | ||
1247 | struct uvc_control_mapping *mapping = uvc_ctrl_mappings; | ||
1248 | struct uvc_control_mapping *mend = | ||
1249 | mapping + ARRAY_SIZE(uvc_ctrl_mappings); | ||
1250 | |||
1251 | for (; ctrl < cend; ++ctrl) | ||
1252 | uvc_ctrl_add_info(ctrl); | ||
1253 | |||
1254 | for (; mapping < mend; ++mapping) | ||
1255 | uvc_ctrl_add_mapping(mapping); | ||
1256 | } | ||