]> git.sesse.net Git - vlc/blob - modules/access/v4l2/controls.c
v4l2: fix mmap() call
[vlc] / modules / access / v4l2 / controls.c
1 /*****************************************************************************
2  * controls.c : Video4Linux2 device controls for vlc
3  *****************************************************************************
4  * Copyright (C) 2002-2011 the VideoLAN team
5  *
6  * Authors: Benjamin Pracht <bigben at videolan dot org>
7  *          Richard Hosking <richard at hovis dot net>
8  *          Antoine Cellerier <dionoea at videolan d.t org>
9  *          Dennis Lou <dlou99 at yahoo dot com>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <ctype.h>
31 #include <assert.h>
32 #include <sys/ioctl.h>
33
34 #include <vlc_common.h>
35
36 #include "v4l2.h"
37
38 typedef struct vlc_v4l2_ctrl_name
39 {
40     const char name[28];
41     uint32_t cid;
42 } vlc_v4l2_ctrl_name_t;
43
44 /* NOTE: must be sorted by ID */
45 static const vlc_v4l2_ctrl_name_t controls[] =
46 {
47     { "brightness", V4L2_CID_BRIGHTNESS },
48     { "contrast", V4L2_CID_CONTRAST },
49     { "saturation", V4L2_CID_SATURATION },
50     { "hue", V4L2_CID_HUE },
51     { "audio-volume", V4L2_CID_AUDIO_VOLUME },
52     { "audio-balance", V4L2_CID_AUDIO_BALANCE },
53     { "audio-bass", V4L2_CID_AUDIO_BASS },
54     { "audio-treble", V4L2_CID_AUDIO_TREBLE },
55     { "audio-mute", V4L2_CID_AUDIO_MUTE },
56     { "audio-loudness", V4L2_CID_AUDIO_LOUDNESS },
57     { "auto-white-balance", V4L2_CID_AUTO_WHITE_BALANCE },
58     { "do-white-balance", V4L2_CID_DO_WHITE_BALANCE },
59     { "red-balance", V4L2_CID_RED_BALANCE },
60     { "blue-balance", V4L2_CID_BLUE_BALANCE },
61     { "gamma", V4L2_CID_GAMMA },
62     { "autogain", V4L2_CID_AUTOGAIN },
63     { "gain", V4L2_CID_GAIN },
64     { "hflip", V4L2_CID_HFLIP },
65     { "vflip", V4L2_CID_VFLIP },
66     { "power-line-frequency", V4L2_CID_POWER_LINE_FREQUENCY },
67     { "hue-auto", V4L2_CID_HUE_AUTO },
68     { "white-balance-temperature", V4L2_CID_WHITE_BALANCE_TEMPERATURE },
69     { "sharpness", V4L2_CID_SHARPNESS },
70     { "backlight-compensation", V4L2_CID_BACKLIGHT_COMPENSATION },
71     { "chroma-gain-auto", V4L2_CID_CHROMA_AGC },
72     { "color-killer", V4L2_CID_COLOR_KILLER },
73     { "color-effect", V4L2_CID_COLORFX },
74     { "rotate", V4L2_CID_ROTATE },
75     { "bg-color", V4L2_CID_BG_COLOR }, // NOTE: output only
76     { "chroma-gain", V4L2_CID_CHROMA_GAIN },
77     { "brightness-auto", V4L2_CID_AUTOBRIGHTNESS },
78     { "band-stop-filter", V4L2_CID_BAND_STOP_FILTER },
79
80     { "illuminators-1", V4L2_CID_ILLUMINATORS_1 }, // NOTE: don't care?
81     { "illuminators-2", V4L2_CID_ILLUMINATORS_2 },
82 #define CTRL_CID_KNOWN(cid) \
83     ((((uint32_t)cid) - V4L2_CID_BRIGHTNESS) \
84         <= (V4L2_CID_BAND_STOP_FILTER - V4L2_CID_BRIGHTNESS))
85 };
86
87 struct vlc_v4l2_ctrl
88 {
89     int                   fd;
90     uint32_t              id;
91     uint8_t               type;
92     char                  name[32];
93     int32_t               default_value;
94     struct vlc_v4l2_ctrl *next;
95 };
96
97 static int ControlSet (const vlc_v4l2_ctrl_t *c, int_fast32_t value)
98 {
99     struct v4l2_control ctrl = {
100         .id = c->id,
101         .value = value,
102     };
103     if (v4l2_ioctl (c->fd, VIDIOC_S_CTRL, &ctrl) < 0)
104         return -1;
105     return 0;
106 }
107
108 static int ControlSet64 (const vlc_v4l2_ctrl_t *c, int64_t value)
109 {
110     struct v4l2_ext_control ext_ctrl = {
111         .id = c->id,
112         .size = 0,
113     };
114     ext_ctrl.value64 = value;
115     struct v4l2_ext_controls ext_ctrls = {
116         .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
117         .count = 1,
118         .error_idx = 0,
119         .controls = &ext_ctrl,
120     };
121
122     if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
123         return -1;
124     return 0;
125 }
126
127 static int ControlSetStr (const vlc_v4l2_ctrl_t *c, const char *restrict value)
128 {
129     struct v4l2_ext_control ext_ctrl = {
130         .id = c->id,
131         .size = strlen (value) + 1,
132     };
133     ext_ctrl.string = (char *)value;
134     struct v4l2_ext_controls ext_ctrls = {
135         .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
136         .count = 1,
137         .error_idx = 0,
138         .controls = &ext_ctrl,
139     };
140
141     if (v4l2_ioctl (c->fd, VIDIOC_S_EXT_CTRLS, &ext_ctrls) < 0)
142         return -1;
143     return 0;
144 }
145
146 static int ControlSetCallback (vlc_object_t *obj, const char *var,
147                                vlc_value_t old, vlc_value_t cur, void *data)
148 {
149     const vlc_v4l2_ctrl_t *ctrl = data;
150     int ret;
151
152     switch (ctrl->type)
153     {
154         case V4L2_CTRL_TYPE_INTEGER:
155         case V4L2_CTRL_TYPE_MENU:
156 #if HAVE_DECL_V4L2_CTRL_TYPE_BITMASK
157         case V4L2_CTRL_TYPE_BITMASK:
158 #endif
159             ret = ControlSet (ctrl, cur.i_int);
160             break;
161         case V4L2_CTRL_TYPE_BOOLEAN:
162             ret = ControlSet (ctrl, cur.b_bool);
163             break;
164         case V4L2_CTRL_TYPE_BUTTON:
165             ret = ControlSet (ctrl, 0);
166             break;
167         case V4L2_CTRL_TYPE_INTEGER64:
168             ret = ControlSet64 (ctrl, cur.i_int);
169             break;
170         case V4L2_CTRL_TYPE_STRING:
171             ret = ControlSetStr (ctrl, cur.psz_string);
172             break;
173         default:
174             assert (0);
175     }
176
177     if (ret)
178     {
179         msg_Err (obj, "cannot set control %s: %m", var);
180         return VLC_EGENERIC;
181     }
182     (void) old;
183     return VLC_SUCCESS;
184 }
185
186 static void ControlsReset (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
187 {
188     while (list != NULL)
189     {
190         switch (list->type)
191         {
192             case V4L2_CTRL_TYPE_INTEGER:
193             case V4L2_CTRL_TYPE_MENU:
194                 var_SetInteger (obj, list->name, list->default_value);
195                 break;
196             case V4L2_CTRL_TYPE_BOOLEAN:
197                 var_SetBool (obj, list->name, list->default_value);
198                 break;
199             default:;
200         }
201         list = list->next;
202     }
203 }
204
205 static int ControlsResetCallback (vlc_object_t *obj, const char *var,
206                                   vlc_value_t old, vlc_value_t cur, void *data)
207 {
208     ControlsReset (obj, data);
209     (void) var; (void) old; (void) cur;
210     return VLC_SUCCESS;
211 }
212
213 static void ControlsSetFromString (vlc_object_t *obj,
214                                    const vlc_v4l2_ctrl_t *list)
215 {
216     char *buf = var_InheritString (obj, CFG_PREFIX"set-ctrls");
217     if (buf == NULL)
218         return;
219
220     char *p = buf;
221     if (*p == '{')
222         p++;
223
224     char *end = strchr (p, '}');
225     if (end != NULL)
226         *end = '\0';
227 next:
228     while (p != NULL && *p)
229     {
230         const char *name, *value;
231
232         p += strspn (p, ", ");
233         name = p;
234         end = strchr (p, ',');
235         if (end != NULL)
236             *(end++) = '\0';
237         p = end; /* next name/value pair */
238
239         end = strchr (name, '=');
240         if (end == NULL)
241         {
242             /* TODO? support button controls that way? */
243             msg_Err (obj, "syntax error in \"%s\": missing '='", name);
244             continue;
245         }
246         *(end++) = '\0';
247         value = end;
248
249         for (const vlc_v4l2_ctrl_t *c = list; c != NULL; c = c->next)
250             if (!strcasecmp (name, c->name))
251                 switch (c->type)
252                 {
253                     case V4L2_CTRL_TYPE_INTEGER:
254                     case V4L2_CTRL_TYPE_BOOLEAN:
255                     case V4L2_CTRL_TYPE_MENU:
256                     {
257                         long val = strtol (value, &end, 0);
258                         if (*end)
259                         {
260                             msg_Err (obj, "syntax error in \"%s\": "
261                                      " not an integer", value);
262                             goto next;
263                         }
264                         ControlSet (c, val);
265                         break;
266                     }
267
268                     case V4L2_CTRL_TYPE_INTEGER64:
269                     {
270                         long long val = strtoll (value, &end, 0);
271                         if (*end)
272                         {
273                             msg_Err (obj, "syntax error in \"%s\": "
274                                      " not an integer", value);
275                             goto next;
276                         }
277                         ControlSet64 (c, val);
278                         break;
279                     }
280
281                     case V4L2_CTRL_TYPE_STRING:
282                         ControlSetStr (c, value);
283                         break;
284 #if HAVE_DECL_V4L2_CTRL_TYPE_BITMASK
285                     case V4L2_CTRL_TYPE_BITMASK:
286                     {
287                         unsigned long val = strtoul (value, &end, 0);
288                         if (*end)
289                         {
290                             msg_Err (obj, "syntax error in \"%s\": "
291                                      " not an integer", value);
292                             goto next;
293                         }
294                         ControlSet (c, val);
295                         break;
296                     }
297 #endif
298                     default:
299                         msg_Err (obj, "setting \"%s\" not supported", name);
300                         goto next;
301                 }
302
303         msg_Err (obj, "control \"%s\" not available", name);
304     }
305     free (buf);
306 }
307
308 static int cidcmp (const void *a, const void *b)
309 {
310     const uint32_t *id = a;
311     const vlc_v4l2_ctrl_name_t *name = b;
312
313     return (int32_t)(*id - name->cid);
314 }
315
316 /**
317  * Creates a VLC-V4L2 control structure:
318  * In particular, determines a name suitable for a VLC object variable.
319  * \param query V4L2 control query structure [IN]
320  * \return NULL on error
321  */
322 static vlc_v4l2_ctrl_t *ControlCreate (int fd,
323                                        const struct v4l2_queryctrl *query)
324 {
325     vlc_v4l2_ctrl_t *ctrl = malloc (sizeof (*ctrl));
326     if (unlikely(ctrl == NULL))
327         return NULL;
328
329     ctrl->fd = fd;
330     ctrl->id = query->id;
331     ctrl->type = query->type;
332
333     /* Search for a well-known control */
334     const vlc_v4l2_ctrl_name_t *known;
335     known = bsearch (&query->id, controls, sizeof (controls) / sizeof (*known),
336                      sizeof (*known), cidcmp);
337     if (known != NULL)
338         strcpy (ctrl->name, known->name);
339     else
340     /* Fallback to automatically-generated control name */
341     {
342         size_t i;
343         for (i = 0; query->name[i]; i++)
344         {
345             unsigned char c = query->name[i];
346             if (c == ' ' || c == ',')
347                 c = '_';
348             if (c < 128)
349                 c = tolower (c);
350             ctrl->name[i] = c;
351         }
352         ctrl->name[i] = '\0';
353     }
354
355     ctrl->default_value = query->default_value;
356     return ctrl;
357 }
358
359
360 #define CTRL_FLAGS_IGNORE \
361     (V4L2_CTRL_FLAG_DISABLED /* not implemented at all */ \
362     |V4L2_CTRL_FLAG_READ_ONLY /* value is constant */ \
363     |V4L2_CTRL_FLAG_VOLATILE /* value is (variable but) read-only */)
364
365 static vlc_v4l2_ctrl_t *ControlAddInteger (vlc_object_t *obj, int fd,
366                                            const struct v4l2_queryctrl *query)
367 {
368     msg_Dbg (obj, " integer  %s (%08"PRIX32")", query->name, query->id);
369     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
370         return NULL;
371
372     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
373     if (unlikely(c == NULL))
374         return NULL;
375
376     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
377     {
378         free (c);
379         return NULL;
380     }
381
382     vlc_value_t val;
383     struct v4l2_control ctrl = { .id = query->id };
384
385     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
386     {
387         msg_Dbg (obj, "  current: %3"PRId32", default: %3"PRId32,
388                  ctrl.value, query->default_value);
389         val.i_int = ctrl.value;
390         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
391     }
392     val.i_int = query->minimum;
393     var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
394     val.i_int = query->maximum;
395     var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
396     if (query->step != 1)
397     {
398         val.i_int = query->step;
399         var_Change (obj, c->name, VLC_VAR_SETSTEP, &val, NULL);
400     }
401     val.i_int = query->default_value;
402     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
403     return c;
404 }
405
406 static vlc_v4l2_ctrl_t *ControlAddBoolean (vlc_object_t *obj, int fd,
407                                            const struct v4l2_queryctrl *query)
408 {
409     msg_Dbg (obj, " boolean  %s (%08"PRIX32")", query->name, query->id);
410     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
411         return NULL;
412
413     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
414     if (unlikely(c == NULL))
415         return NULL;
416
417     if (var_Create (obj, c->name, VLC_VAR_BOOL | VLC_VAR_ISCOMMAND))
418     {
419         free (c);
420         return NULL;
421     }
422
423     vlc_value_t val;
424     struct v4l2_control ctrl = { .id = query->id };
425
426     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
427     {
428         msg_Dbg (obj, "  current: %s, default: %s",
429                  ctrl.value ? " true" : "false",
430                  query->default_value ? " true" : "false");
431         val.b_bool = ctrl.value;
432         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
433     }
434     val.b_bool = query->default_value;
435     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
436     return c;
437 }
438
439 static vlc_v4l2_ctrl_t *ControlAddMenu (vlc_object_t *obj, int fd,
440                                         const struct v4l2_queryctrl *query)
441 {
442     msg_Dbg (obj, " menu     %s (%08"PRIX32")", query->name, query->id);
443     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
444         return NULL;
445
446     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
447     if (unlikely(c == NULL))
448         return NULL;
449
450     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_HASCHOICE
451                                                   | VLC_VAR_ISCOMMAND))
452     {
453         free (c);
454         return NULL;
455     }
456
457     vlc_value_t val;
458     struct v4l2_control ctrl = { .id = query->id };
459
460     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
461     {
462         msg_Dbg (obj, "  current: %"PRId32", default: %"PRId32,
463                  ctrl.value, query->default_value);
464         val.i_int = ctrl.value;
465         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
466     }
467     val.b_bool = query->default_value;
468     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
469     val.i_int = query->minimum;
470     var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
471     val.i_int = query->maximum;
472     var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
473
474     /* Import menu choices */
475     for (uint_fast32_t idx = query->minimum;
476          idx <= (uint_fast32_t)query->maximum;
477          idx++)
478     {
479         struct v4l2_querymenu menu = { .id = query->id, .index = idx };
480
481         if (v4l2_ioctl (fd, VIDIOC_QUERYMENU, &menu) < 0)
482             continue;
483         msg_Dbg (obj, "  choice %"PRIu32") %s", menu.index, menu.name);
484
485         vlc_value_t text;
486         val.i_int = menu.index;
487         text.psz_string = (char *)menu.name;
488         var_Change (obj, c->name, VLC_VAR_ADDCHOICE, &val, &text);
489     }
490     return c;
491 }
492
493 static vlc_v4l2_ctrl_t *ControlAddButton (vlc_object_t *obj, int fd,
494                                           const struct v4l2_queryctrl *query)
495 {
496     msg_Dbg (obj, " button   %s (%08"PRIX32")", query->name, query->id);
497     if (query->flags & CTRL_FLAGS_IGNORE)
498         return NULL;
499
500     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
501     if (unlikely(c == NULL))
502         return NULL;
503
504     if (var_Create (obj, c->name, VLC_VAR_VOID | VLC_VAR_ISCOMMAND))
505     {
506         free (c);
507         return NULL;
508     }
509     return c;
510 }
511
512 static vlc_v4l2_ctrl_t *ControlAddInteger64 (vlc_object_t *obj, int fd,
513                                             const struct v4l2_queryctrl *query)
514 {
515     msg_Dbg (obj, " 64-bits  %s (%08"PRIX32")", query->name, query->id);
516     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
517         return NULL;
518
519     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
520     if (unlikely(c == NULL))
521         return NULL;
522
523     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
524     {
525         free (c);
526         return NULL;
527     }
528
529     struct v4l2_ext_control ext_ctrl = { .id = c->id, .size = 0, };
530     struct v4l2_ext_controls ext_ctrls = {
531         .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
532         .count = 1,
533         .error_idx = 0,
534         .controls = &ext_ctrl,
535     };
536
537     if (v4l2_ioctl (c->fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) >= 0)
538     {
539         vlc_value_t val = { .i_int = ext_ctrl.value64 };
540
541         msg_Dbg (obj, "  current: %"PRId64, val.i_int);
542         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
543     }
544
545     return c;
546 }
547
548 static vlc_v4l2_ctrl_t *ControlAddClass (vlc_object_t *obj, int fd,
549                                          const struct v4l2_queryctrl *query)
550 {
551     msg_Dbg (obj, "control class %s:", query->name);
552     (void) fd;
553     return NULL;
554 }
555
556 static vlc_v4l2_ctrl_t *ControlAddString (vlc_object_t *obj, int fd,
557                                           const struct v4l2_queryctrl *query)
558 {
559     msg_Dbg (obj, " string   %s (%08"PRIX32")", query->name, query->id);
560     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY)
561      || query->maximum > 65535)
562         return NULL;
563
564     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
565     if (unlikely(c == NULL))
566         return NULL;
567
568     if (var_Create (obj, c->name, VLC_VAR_STRING | VLC_VAR_ISCOMMAND))
569     {
570         free (c);
571         return NULL;
572     }
573
574     /* Get current value */
575     char *buf = malloc (query->maximum + 1);
576     if (likely(buf != NULL))
577     {
578         struct v4l2_ext_control ext_ctrl = {
579             .id = c->id,
580             .size = query->maximum + 1,
581         };
582         ext_ctrl.string = buf;
583         struct v4l2_ext_controls ext_ctrls = {
584             .ctrl_class = V4L2_CTRL_ID2CLASS(c->id),
585             .count = 1,
586             .error_idx = 0,
587             .controls = &ext_ctrl,
588         };
589
590         if (v4l2_ioctl (c->fd, VIDIOC_G_EXT_CTRLS, &ext_ctrls) >= 0)
591         {
592             vlc_value_t val = { .psz_string = buf };
593
594             msg_Dbg (obj, "  current: \"%s\"", buf);
595             var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
596         }
597         free (buf);
598     }
599
600     return c;
601 }
602
603 #if HAVE_DECL_V4L2_CTRL_TYPE_BITMASK
604 static vlc_v4l2_ctrl_t *ControlAddBitMask (vlc_object_t *obj, int fd,
605                                            const struct v4l2_queryctrl *query)
606 {
607     msg_Dbg (obj, " bit mask %s (%08"PRIX32")", query->name, query->id);
608     if (query->flags & (CTRL_FLAGS_IGNORE | V4L2_CTRL_FLAG_WRITE_ONLY))
609         return NULL;
610
611     vlc_v4l2_ctrl_t *c = ControlCreate (fd, query);
612     if (unlikely(c == NULL))
613         return NULL;
614
615     if (var_Create (obj, c->name, VLC_VAR_INTEGER | VLC_VAR_ISCOMMAND))
616     {
617         free (c);
618         return NULL;
619     }
620
621     vlc_value_t val;
622     struct v4l2_control ctrl = { .id = query->id };
623
624     if (v4l2_ioctl (fd, VIDIOC_G_CTRL, &ctrl) >= 0)
625     {
626         msg_Dbg (obj, "  current: 0x%08"PRIX32", default: 0x%08"PRIX32,
627                  ctrl.value, query->default_value);
628         val.i_int = ctrl.value;
629         var_Change (obj, c->name, VLC_VAR_SETVALUE, &val, NULL);
630     }
631     val.i_int = 0;
632     var_Change (obj, c->name, VLC_VAR_SETMIN, &val, NULL);
633     val.i_int = (uint32_t)query->maximum;
634     var_Change (obj, c->name, VLC_VAR_SETMAX, &val, NULL);
635     val.i_int = query->default_value;
636     var_Change (obj, c->name, VLC_VAR_SETDEFAULT, &val, NULL);
637     return c;
638 }
639 #endif
640
641 static vlc_v4l2_ctrl_t *ControlAddUnknown (vlc_object_t *obj, int fd,
642                                            const struct v4l2_queryctrl *query)
643 {
644     msg_Dbg (obj, " unknown %s (%08"PRIX32")", query->name, query->id);
645     msg_Warn (obj, "  unknown control type %u", (unsigned)query->type);
646     (void) fd;
647     return NULL;
648 }
649
650 typedef vlc_v4l2_ctrl_t *(*ctrl_type_cb) (vlc_object_t *, int,
651                                           const struct v4l2_queryctrl *);
652
653 /**
654  * Lists all user-class v4l2 controls, sets them to the user specified
655  * value and create the relevant variables to enable run-time changes.
656  */
657 vlc_v4l2_ctrl_t *ControlsInit (vlc_object_t *obj, int fd)
658 {
659     /* A list of controls that can be modified at run-time is stored in the
660      * "controls" variable. The V4L2 controls dialog can be built from this. */
661     var_Create (obj, "controls", VLC_VAR_INTEGER | VLC_VAR_HASCHOICE);
662
663     static const ctrl_type_cb handlers[] =
664     {
665         [V4L2_CTRL_TYPE_INTEGER] = ControlAddInteger,
666         [V4L2_CTRL_TYPE_BOOLEAN] = ControlAddBoolean,
667         [V4L2_CTRL_TYPE_MENU] = ControlAddMenu,
668         [V4L2_CTRL_TYPE_BUTTON] = ControlAddButton,
669         [V4L2_CTRL_TYPE_INTEGER64] = ControlAddInteger64,
670         [V4L2_CTRL_TYPE_CTRL_CLASS] = ControlAddClass,
671         [V4L2_CTRL_TYPE_STRING] = ControlAddString,
672 #if HAVE_DECL_V4L2_CTRL_TYPE_BITMASK
673         [V4L2_CTRL_TYPE_BITMASK] = ControlAddBitMask,
674 #endif
675     };
676
677     vlc_v4l2_ctrl_t *list = NULL;
678     struct v4l2_queryctrl query;
679
680     query.id = V4L2_CTRL_FLAG_NEXT_CTRL;
681     while (v4l2_ioctl (fd, VIDIOC_QUERYCTRL, &query) >= 0)
682     {
683         ctrl_type_cb handler = NULL;
684         if (query.type < (sizeof (handlers) / sizeof (handlers[0])))
685             handler = handlers[query.type];
686         if (handler == NULL)
687             handler = ControlAddUnknown;
688
689         vlc_v4l2_ctrl_t *c = handler (obj, fd, &query);
690         if (c != NULL)
691         {
692             vlc_value_t val, text;
693
694             var_AddCallback (obj, c->name, ControlSetCallback, c);
695             text.psz_string = (char *)query.name;
696             var_Change (obj, c->name, VLC_VAR_SETTEXT, &text, NULL);
697             val.i_int = query.id;
698             text.psz_string = (char *)c->name;
699             var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
700
701             c->next = list;
702             list = c;
703         }
704         query.id |= V4L2_CTRL_FLAG_NEXT_CTRL;
705     }
706
707     /* Set well-known controls from VLC configuration */
708     for (vlc_v4l2_ctrl_t *ctrl = list; ctrl != NULL; ctrl = ctrl->next)
709     {
710         if (!CTRL_CID_KNOWN (ctrl->id))
711             continue;
712
713         char varname[sizeof (CFG_PREFIX) + sizeof (ctrl->name) - 1];
714         sprintf (varname, CFG_PREFIX"%s", ctrl->name);
715
716         int64_t val = var_InheritInteger (obj, varname);
717         if (val == -1)
718             continue; /* the VLC default value: "do not modify" */
719         ControlSet (ctrl, val); /* NOTE: all known are integers or booleans */
720     }
721
722     /* Set any control from the VLC configuration control string */
723     ControlsSetFromString (obj, list);
724
725     /* Add a control to reset all controls to their default values */
726     {
727         vlc_value_t val, text;
728
729         var_Create (obj, "reset", VLC_VAR_VOID | VLC_VAR_ISCOMMAND);
730         val.psz_string = _("Reset defaults");
731         var_Change (obj, "reset", VLC_VAR_SETTEXT, &val, NULL);
732         val.i_int = -1;
733
734         text.psz_string = (char *)"reset";
735         var_Change (obj, "controls", VLC_VAR_ADDCHOICE, &val, &text);
736         var_AddCallback (obj, "reset", ControlsResetCallback, list);
737     }
738     if (var_InheritBool (obj, CFG_PREFIX"controls-reset"))
739         ControlsReset (obj, list);
740
741     return list;
742 }
743
744 void ControlsDeinit (vlc_object_t *obj, vlc_v4l2_ctrl_t *list)
745 {
746     var_DelCallback (obj, "reset", ControlsResetCallback, list);
747     var_Destroy (obj, "reset");
748
749     while (list != NULL)
750     {
751         vlc_v4l2_ctrl_t *next = list->next;
752
753         var_DelCallback (obj, list->name, ControlSetCallback, list);
754         var_Destroy (obj, list->name);
755         free (list);
756         list = next;
757     }
758
759     var_Destroy (obj, "controls");
760 }