1 /*
2 # (C) 2008-2009 Elmar Kleijn <elmar_kleijn@hotmail.com>
3 # (C) 2008-2009 Sjoerd Piepenbrink <need4weed@gmail.com>
4 # (C) 2008-2009 Radjnies Bhansingh <radjnies@gmail.com>
5 # (C) 2008-2010 Hans de Goede <hdegoede@redhat.com>
6
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU Lesser General Public License as published by
9 # the Free Software Foundation; either version 2.1 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU Lesser General Public License for more details.
16 #
17 # You should have received a copy of the GNU Lesser General Public License
18 # along with this program; if not, write to the Free Software
19 # Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335 USA
20 */
21
22 #include <sys/types.h>
23 #ifndef __OpenBSD__
24 #include <sys/sysmacros.h>
25 #endif
26 #include <sys/mman.h>
27 #include <fcntl.h>
28 #include <sys/stat.h>
29 #include <ctype.h>
30 #include <errno.h>
31 #include <fnmatch.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <stdint.h>
35 #include <unistd.h>
36 #include <string.h>
37 #include <pwd.h>
38 #include "libv4lcontrol.h"
39 #include "libv4lcontrol-priv.h"
40 #include "../libv4lsyscall-priv.h"
41 #if defined(__OpenBSD__)
42 #include <sys/videoio.h>
43 #else
44 #include <linux/videodev2.h>
45 #endif
46
47 #define ARRAY_SIZE(x) ((int)sizeof(x) / (int)sizeof((x)[0]))
48
49 /* List of cams which need special flags */
50 static const struct v4lcontrol_flags_info v4lcontrol_flags[] = {
51 /* First: Upside down devices */
52 /* Philips SPC200NC */
53 { 0x0471, 0x0325, 0, NULL, NULL, V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
54 /* Philips SPC300NC */
55 { 0x0471, 0x0326, 0, NULL, NULL, V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
56 /* Philips SPC210NC */
57 { 0x0471, 0x032d, 0, NULL, NULL, V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
58 /* Philips SPC315NC */
59 { 0x0471, 0x032e, 0, NULL, NULL, V4LCONTROL_VFLIPPED },
60 /* Genius E-M 112 (also want whitebalance by default) */
61 { 0x093a, 0x2476, 0, NULL, NULL,
62 V4LCONTROL_HFLIPPED|V4LCONTROL_VFLIPPED | V4LCONTROL_WANTS_WB, 1500 },
63
64 /* Laptops (and all in one PC's) */
65 /* 0x0402, 0x5602 - add quirk to driver/media/video/gspca/m5602/m5602_s5k4aa.c */
66 { 0x0402, 0x5606, 0,
67 "CLEVO CO. ",
68 "M570TU ",
69 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
70 { 0x0402, 0x5606, 0, "AOpen", "i45GMt-HR R1.02 Dec.21.2009",
71 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
72 "RM plc", "RM ONE ECOQUIET 300" },
73 { 0x0402, 0x5606, 0, "Intel Corporation", "DQ45CB",
74 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
75 "RM plc", "RM EXPERT 3040" },
76 { 0x046d, 0x09b2, 0, "FUJITSU", "FJNB1C9",
77 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
78 "FUJITSU SIEMENS", "LIFEBOOK P7230" },
79 { 0x046d, 0x09b2, 0, "FUJITSU", "FJNB1C9",
80 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
81 "FUJITSU", "LifeBook P7230" },
82 /* A re-branded ASUS notebook */
83 { 0x04f2, 0xb012, 0, "Founder PC", "T14MF",
84 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
85 /* These 3 PACKARD BELL's seem to be Asus notebook in disguise */
86 { 0x04f2, 0xb012, 0, "Packard Bell BV", "T32A ",
87 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
88 { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG45",
89 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
90 { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BG46",
91 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
92 { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_BS45 ",
93 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
94 { 0x04f2, 0xb012, 0, "PACKARD BELL BV ", "EasyNote_F0945",
95 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
96 /* This Asus has its camera the right way up, so we've an entry here
97 to override the wildcard match from the upside_down table. */
98 { 0x04f2, 0xb012, 0, "ASUSTeK Computer Inc. ", "F3Sc ",
99 0 },
100 { 0x04f2, 0xb071, 0, "AXIOO", "PICO DJH Model",
101 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
102 { 0x04f2, 0xb071, 0, "PEGATRON CORPORATION", "H54",
103 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
104 { 0x04f2, 0xb071, 0, "PEGATRON CORP.", NULL,
105 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
106 "Philco", "S\202rie PHN10050" },
107 { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB206",
108 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
109 "FUJITSU", "LifeBook T4410" },
110 { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB219",
111 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
112 "FUJITSU", "LIFEBOOK T730" },
113 { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB232",
114 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
115 "FUJITSU", "LIFEBOOK T731" },
116 { 0x04f2, 0xb169, 0, "FUJITSU", "FJNB21A",
117 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
118 "FUJITSU", "LIFEBOOK TH700" },
119 { 0x04f2, 0xb16b, 0, "ASUSTeK Computer Inc. ", "U20A ",
120 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
121 /* 1 report:
122 Unknown laptop model -> System Vendor: " IDEALMAX"
123 But given that the System Vendor is "unstable" for the other
124 H34 entry, we put NULL in the dmi_system_vendor field. */
125 { 0x04f2, 0xb16b, 0, "To be filled by O.E.M.", "H34",
126 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
127 NULL, "H34" },
128 { 0x04f2, 0xb186, 0, "FUJITSU", "FJNB206",
129 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
130 "FUJITSU SIEMENS", "LifeBook T4310" },
131 { 0x04f2, 0xb186, 0, "FUJITSU", "FJNB206",
132 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
133 "FUJITSU", "LifeBook T4310" },
134 { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB11",
135 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
136 "FUJITSU", "LIFEBOOK PH521" },
137 { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB13",
138 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
139 "FUJITSU", "LIFEBOOK SH531" },
140 { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB16",
141 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
142 "FUJITSU", "LIFEBOOK LH531" },
143 { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB18",
144 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
145 "FUJITSU", "FMVN77ED" },
146 { 0x04f2, 0xb213, 0, "FUJITSU", "FJNBB19",
147 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
148 "FUJITSU", "LIFEBOOK NH751" },
149 { 0x04f2, 0xb217, 0, "LENOVO", "42982YG",
150 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
151 { 0x04f2, 0xb217, 0, "LENOVO", "42992QG",
152 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
153 { 0x04f2, 0xb27c, 0, "LENOVO", "12973MG",
154 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
155 "ThinkPad Edge E325" },
156 { 0x064e, 0xa111, 0, "Acer, Inc.", "Prespa1 ",
157 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
158 "Acer, inc.", "Aspire 5570 " },
159 /* 2 reports:
160 Unknown laptop model -> System Vendor: " IDEALMAX"
161 Síragon SL-6120 -> System Vendor: "PEGA PC"
162 So we just put NULL in the dmi_system_vendor field. */
163 { 0x064e, 0xa116, 0, "To be filled by O.E.M.", "H34",
164 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
165 NULL, "H34" },
166 { 0x064e, 0xa212, 0, "MEDIONAG", "WeTab ",
167 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
168 { 0x174f, 0x6a51, 0, NULL, "S96S",
169 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0,
170 "MicroLink", "S96S" },
171 { 0x17ef, 0x480c, 0, "LENOVO", "7449C8G",
172 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
173 "ThinkPad X200 Tablet" },
174 /* Disabled due to http://bugs.debian.org/667958
175 { 0x17ef, 0x4816, 0, "LENOVO", "0831CTO",
176 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
177 "ThinkPad X201 Tablet" }, */
178 { 0x5986, 0x0200, 0, "LENOVO", "SPEEDY ",
179 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
180 "Lenovo IdeaPad Y510" },
181 { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name",
182 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
183 "Lenovo IdeaPad U330" },
184 { 0x5986, 0x0205, 0, "LENOVO", "Base Board Product Name",
185 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED, 0, NULL, NULL, NULL,
186 "Lenovo IdeaPad Y330" },
187 { 0xeb1a, 0x2750, 0, "CLEVO", "D900K",
188 V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED },
189
190 /* Second: devices which should use some software processing by default */
191 /* jl2005bcd devices */
192 { 0x0979, 0x0227, 0, NULL, NULL, V4LCONTROL_WANTS_WB },
193 /* sn9c101 / sn9c102 based devices (sonixb) */
194 { 0x0c45, 0x6011, 0, NULL, NULL, 0, 1500 }, /* OV6650, no WB needed */
195 { 0x0c45, 0x6019, 0, NULL, NULL, 0, 1500 }, /* OV7630, no WB needed */
196 { 0x0c45, 0x608f, 0, NULL, NULL, 0, 1500 }, /* OV7630, no WB needed */
197 { 0x0c45, 0x60b0, 0, NULL, NULL, 0, 1500 }, /* OV7630, no WB needed */
198 { 0x0c45, 0x6000, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, /* other */
199 { 0x0c45, 0x6020, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, /* other */
200 { 0x0c45, 0x60af, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 }, /* PAS202 */
201 /* sn9c105 / sn9c120 based devices (sonixj) */
202 { 0x0c45, 0x60fe, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */
203 { 0x0c45, 0x610e, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */
204 { 0x0c45, 0x6128, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OM6802 */
205 { 0x0c45, 0x612e, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */
206 { 0x0c45, 0x613e, 0, NULL, NULL, V4LCONTROL_WANTS_WB }, /* OV7630 */
207 /* Pac207 based devices */
208 { 0x041e, 0x4028, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
209 { 0x093a, 0x2460, 0x1f, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
210 { 0x145f, 0x013a, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
211 { 0x2001, 0xf115, 0, NULL, NULL, V4LCONTROL_WANTS_WB, 1500 },
212 /* Pac7302 based devices */
213 { 0x093a, 0x2620, 0x0f, NULL, NULL,
214 V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 },
215 { 0x06f8, 0x3009, 0, NULL, NULL,
216 V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 },
217 { 0x06f8, 0x301b, 0, NULL, NULL,
218 V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 },
219 { 0x145f, 0x013c, 0, NULL, NULL,
220 V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 },
221 { 0x1ae7, 0x2001, 0, NULL, NULL,
222 V4LCONTROL_ROTATED_90_JPEG | V4LCONTROL_WANTS_WB, 1500 },
223 /* Pac7311 based devices */
224 { 0x093a, 0x2600, 0x0f, NULL, NULL, V4LCONTROL_WANTS_WB },
225 /* sq905 devices */
226 { 0x2770, 0x9120, 0, NULL, NULL, V4LCONTROL_WANTS_WB },
227 /* spca561 revison 12a devices */
228 { 0x041e, 0x403b, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
229 { 0x046d, 0x0928, 7, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
230 /* logitech quickcam express stv06xx 2 versions:
231 pb0100 only needs whitebalance, see software autogain code enable below
232 hdcs10xx needs both whitebalance and autogain. */
233 { 0x046d, 0x0840, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
234 /* logitech quickcam messenger variants, st6422 */
235 { 0x046d, 0x08f0, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
236 { 0x046d, 0x08f5, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
237 { 0x046d, 0x08f6, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
238 { 0x046d, 0x08da, 0, NULL, NULL, V4LCONTROL_WANTS_AUTOGAIN },
239 /* mr97310a cams, note some models do not have the necessary controls, for
240 those we will only do whitebal. see software autogain code enable below */
241 { 0x08ca, 0x0111, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
242 { 0x093a, 0x010e, 1, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN },
243 /* stv0680 based cams */
244 { 0x0553, 0x0202, 0, NULL, NULL, V4LCONTROL_WANTS_WB },
245 { 0x041e, 0x4007, 0, NULL, NULL, V4LCONTROL_WANTS_WB },
246 /* vicam based cams */
247 { 0x04c1, 0x009d, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN, 1500 },
248 { 0x0602, 0x1001, 0, NULL, NULL, V4LCONTROL_WANTS_WB_AUTOGAIN, 1500 },
249 /* se401 cams, mirrored and need wb + autogain */
250 { 0x03e8, 0x0004, 0, NULL, NULL,
251 V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
252 { 0x0471, 0x030b, 0, NULL, NULL,
253 V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
254 { 0x047d, 0x5001, 0, NULL, NULL,
255 V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
256 { 0x047d, 0x5002, 0, NULL, NULL,
257 V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
258 { 0x047d, 0x5003, 0, NULL, NULL,
259 V4LCONTROL_HFLIPPED | V4LCONTROL_WANTS_WB_AUTOGAIN },
260 /* W996xCF based cams, must use jpeglite because of funky JPEG fmt */
261 { 0x041e, 0x4003, 0, NULL, NULL, V4LCONTROL_FORCE_TINYJPEG },
262 { 0x1046, 0x9967, 0, NULL, NULL, V4LCONTROL_FORCE_TINYJPEG },
263 };
264
265 static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT];
266
267 static const char *asus_board_vendor[] = {
268 "ASUSTeK Computer Inc.",
269 "ASUSTeK Computer INC.",
270 "ASUS CORPORATION",
271 "PEGATRON CORPORATION",
272 NULL };
273
274 static const char *asus_board_name[] = {
275 "A3[A-Z]*", "A6R*", "A7J", "A7M", "A7Sn",
276 "B50[A-Z]*",
277 "F[23579][A-Z]*", "F70[A-Z]*", "F[58]2[A-Z]*",
278 "G[12][A-Z]*", "G[57]0[A-Z]*",
279 "K[4567]0[A-Z]*", "K[56]1[A-Z]*", "K52[A-Z]*", "K[45]3[A-Z]*",
280 "N[12579]0[A-Z]*", "N[56]1[A-Z]*", "N82[A-Z]*", "N[47]3[A-Z]*",
281 "P5[02][A-Z]*", "P81[A-Z]*",
282 "U6[A-Z]*", "U[28]0[A-Z]*", "U3[1356][A-Z]*", "U5[23][A-Z]*",
283 "UL[35]0[A-Z]*",
284 "X55[A-Z]*", "X58[A-Z]*", "X71[A-Z]*",
285 /* special devices */
286 "900AX",
287 "N5051Tp", "NX90Jq",
288 "T101MT", "T14C",
289 "VX3",
290 "W7S*",
291 NULL };
292
293 static const struct v4lcontrol_usb_id asus_camera_id[] = {
294 { 0x04f2, 0xb012 },
295 { 0x04f2, 0xb034 },
296 { 0x04f2, 0xb036 },
297 { 0x04f2, 0xb071 },
298 { 0x04f2, 0xb072 },
299 { 0x04f2, 0xb106 },
300 { 0x04f2, 0xb169 },
301 { 0x04f2, 0xb16b },
302 { 0x04f2, 0xb1b9 },
303 { 0x04f2, 0xb1bb },
304 { 0x04f2, 0xb1be },
305 { 0x04f2, 0xb1e5 },
306 { 0x064e, 0xa111 },
307 { 0x064e, 0xa116 },
308 { 0x064e, 0xa136 },
309 { 0x090c, 0xe370 },
310 { 0x13d3, 0x5094 },
311 { 0x13d3, 0x5111 },
312 { 0x13d3, 0x5120 },
313 { 0x13d3, 0x5122 },
314 { 0x13d3, 0x5126 },
315 { 0x13d3, 0x5130 },
316 { 0x13d3, 0x5702 },
317 { 0x174f, 0x1120 },
318 { 0x174f, 0x1408 },
319 { 0x174f, 0x5a35 },
320 { 0x174f, 0x8a31 },
321 { 0x174f, 0xa311 },
322 { 0x1d4d, 0x1002 },
323 { 0x05e1, 0x0501 },
324 { 0x0000, 0x0000 }
325 };
326
327 static const struct v4lcontrol_upside_down_table upside_down[] = {
328 { asus_board_vendor, asus_board_name, asus_camera_id },
329 };
330
v4lcontrol_get_dmi_string(const char * sysfs_prefix,const char * string,char * buf,int size)331 static void v4lcontrol_get_dmi_string(const char *sysfs_prefix, const char *string, char *buf, int size)
332 {
333 FILE *f;
334 char *s, sysfs_name[512];
335
336 snprintf(sysfs_name, sizeof(sysfs_name),
337 "%s/sys/class/dmi/id/%s", sysfs_prefix, string);
338 f = fopen(sysfs_name, "r");
339 if (!f) {
340 /* Try again with a different sysfs path, not sure if this is needed
341 but we used to look under /sys/devices/virtual/dmi/id in older
342 libv4l versions, but this did not work with some kernels */
343 snprintf(sysfs_name, sizeof(sysfs_name),
344 "%s/sys/devices/virtual/dmi/id/%s", sysfs_prefix, string);
345 f = fopen(sysfs_name, "r");
346 if (!f) {
347 buf[0] = 0;
348 return;
349 }
350 }
351
352 s = fgets(buf, size, f);
353 if (s)
354 s[strlen(s) - 1] = 0;
355 fclose(f);
356 }
357
v4lcontrol_get_usb_info(struct v4lcontrol_data * data,const char * sysfs_prefix,unsigned short * vendor_id,unsigned short * product_id,int * speed)358 static int v4lcontrol_get_usb_info(struct v4lcontrol_data *data,
359 const char *sysfs_prefix,
360 unsigned short *vendor_id, unsigned short *product_id,
361 int *speed)
362 {
363 FILE *f;
364 int i, minor_dev;
365 struct stat st;
366 char sysfs_name[512];
367 char c, *s, buf[32];
368
369 snprintf(sysfs_name, sizeof(sysfs_name),
370 "%s/sys/class/video4linux", sysfs_prefix);
371
372 /* Check for sysfs mounted before trying to search */
373 if (stat(sysfs_name, &st) != 0)
374 return 0; /* Not found, sysfs not mounted? */
375
376 if (fstat(data->fd, &st) || !S_ISCHR(st.st_mode))
377 return 0; /* Should never happen */
378
379 /* <Sigh> find ourselve in sysfs */
380 for (i = 0; i < 256; i++) {
381 snprintf(sysfs_name, sizeof(sysfs_name),
382 "%s/sys/class/video4linux/video%d/dev", sysfs_prefix, i);
383 f = fopen(sysfs_name, "r");
384 if (!f)
385 continue;
386
387 s = fgets(buf, sizeof(buf), f);
388 fclose(f);
389
390 if (s && sscanf(buf, "%*d:%d%c", &minor_dev, &c) == 2 &&
391 c == '\n' && minor_dev == minor(st.st_rdev))
392 break;
393 }
394 if (i == 256)
395 return 0; /* Not found, sysfs not mounted? */
396
397 /* Get vendor and product ID */
398 snprintf(sysfs_name, sizeof(sysfs_name),
399 "%s/sys/class/video4linux/video%d/device/modalias", sysfs_prefix, i);
400 f = fopen(sysfs_name, "r");
401 if (f) {
402 s = fgets(buf, sizeof(buf), f);
403 fclose(f);
404
405 if (!s || sscanf(s, "usb:v%4hxp%4hx%c", vendor_id, product_id,
406 &c) != 3 || c != 'd')
407 return 0; /* Not an USB device */
408
409 snprintf(sysfs_name, sizeof(sysfs_name),
410 "%s/sys/class/video4linux/video%d/device/../speed", sysfs_prefix, i);
411 } else {
412 /* Try again assuming the device link points to the usb
413 device instead of the usb interface (bug in older versions
414 of gspca) */
415
416 /* Get vendor ID */
417 snprintf(sysfs_name, sizeof(sysfs_name),
418 "%s/sys/class/video4linux/video%d/device/idVendor", sysfs_prefix, i);
419 f = fopen(sysfs_name, "r");
420 if (!f)
421 return 0; /* Not an USB device (or no sysfs) */
422
423 s = fgets(buf, sizeof(buf), f);
424 fclose(f);
425
426 if (!s || sscanf(s, "%04hx%c", vendor_id, &c) != 2 ||
427 c != '\n')
428 return 0; /* Should never happen */
429
430 /* Get product ID */
431 snprintf(sysfs_name, sizeof(sysfs_name),
432 "%s/sys/class/video4linux/video%d/device/idProduct", sysfs_prefix, i);
433 f = fopen(sysfs_name, "r");
434 if (!f)
435 return 0; /* Should never happen */
436
437 s = fgets(buf, sizeof(buf), f);
438 fclose(f);
439
440 if (!s || sscanf(s, "%04hx%c", product_id, &c) != 2 ||
441 c != '\n')
442 return 0; /* Should never happen */
443
444 snprintf(sysfs_name, sizeof(sysfs_name),
445 "%s/sys/class/video4linux/video%d/device/speed", sysfs_prefix, i);
446 }
447
448 f = fopen(sysfs_name, "r");
449 if (!f)
450 return 0; /* Should never happen */
451
452 s = fgets(buf, sizeof(buf), f);
453 fclose(f);
454
455 if (!s || sscanf(s, "%d%c", speed, &c) != 2 || (c != '\n' && c != '.'))
456 return 0; /* Should never happen */
457
458 return 1;
459 }
460
461 /*
462 * Tries to match value in NULL terminated table_entries string array
463 * aganist the space trimmed dmi_value. The upside down table entries
464 * might contain shell wildcard patterns [see glob(7)].
465 *
466 * Returns non zero value if value is found, otherwise 0.
467 */
find_dmi_string(const char ** table_entries,const char * dmi_value)468 static int find_dmi_string(const char **table_entries, const char *dmi_value)
469 {
470 const char *start = dmi_value;
471 const char **entry_ptr;
472 char *trimmed_dmi;
473 size_t n;
474 int found = 0;
475
476 if (!start) return 0;
477
478 /* trim value */
479 while (isspace(*start)) start++;
480 n = strlen(start);
481 while (n > 0 && isspace(start[n-1])) --n;
482 trimmed_dmi = strndup(start, n);
483
484 /* find trimmed value */
485 for (entry_ptr = table_entries; *entry_ptr; entry_ptr++) {
486 found = fnmatch(*entry_ptr, trimmed_dmi, 0) == 0;
487 /* fprintf(stderr, "find_dmi_string('%s', '%s'->'%s')=%i\n", *entry_ptr, dmi_value, trimmed_dmi, found); */
488 if (found)
489 break;
490 }
491
492 free(trimmed_dmi);
493
494 return found;
495 }
496
497 /*
498 * Tries to find an USB id in table_entries array
499 *
500 * Returns non zero value if value is found, otherwise 0.
501 */
find_usb_id(const struct v4lcontrol_usb_id * table_entries,unsigned short vendor_id,unsigned short product_id)502 static int find_usb_id(const struct v4lcontrol_usb_id *table_entries,
503 unsigned short vendor_id, unsigned short product_id)
504 {
505 const struct v4lcontrol_usb_id *entry_ptr;
506 for (entry_ptr = table_entries; entry_ptr->vendor_id && entry_ptr->product_id; ++entry_ptr)
507 if (entry_ptr->vendor_id == vendor_id && entry_ptr->product_id == product_id) return 1;
508 return 0;
509 }
510
v4lcontrol_get_flags_from_db(struct v4lcontrol_data * data,const char * sysfs_prefix,unsigned short vendor_id,unsigned short product_id)511 static void v4lcontrol_get_flags_from_db(struct v4lcontrol_data *data,
512 const char *sysfs_prefix,
513 unsigned short vendor_id, unsigned short product_id)
514 {
515 char dmi_system_vendor[512], dmi_system_name[512], dmi_system_version[512];
516 char dmi_board_vendor[512], dmi_board_name[512], dmi_board_version[512];
517 int i;
518
519 /* Get DMI board and system strings */
520 v4lcontrol_get_dmi_string(sysfs_prefix, "sys_vendor", dmi_system_vendor,
521 sizeof(dmi_system_vendor));
522 v4lcontrol_get_dmi_string(sysfs_prefix, "product_name", dmi_system_name,
523 sizeof(dmi_system_name));
524 v4lcontrol_get_dmi_string(sysfs_prefix, "product_version", dmi_system_version,
525 sizeof(dmi_system_version));
526
527 v4lcontrol_get_dmi_string(sysfs_prefix, "board_vendor", dmi_board_vendor,
528 sizeof(dmi_board_vendor));
529 v4lcontrol_get_dmi_string(sysfs_prefix, "board_name", dmi_board_name,
530 sizeof(dmi_board_name));
531 v4lcontrol_get_dmi_string(sysfs_prefix, "board_version", dmi_board_version,
532 sizeof(dmi_board_version));
533
534 for (i = 0; i < ARRAY_SIZE(v4lcontrol_flags); i++)
535 if (v4lcontrol_flags[i].vendor_id == vendor_id &&
536 v4lcontrol_flags[i].product_id ==
537 (product_id & ~v4lcontrol_flags[i].product_mask) &&
538
539 (v4lcontrol_flags[i].dmi_system_vendor == NULL ||
540 !strcmp(v4lcontrol_flags[i].dmi_system_vendor, dmi_system_vendor)) &&
541 (v4lcontrol_flags[i].dmi_system_name == NULL ||
542 !strcmp(v4lcontrol_flags[i].dmi_system_name, dmi_system_name)) &&
543 (v4lcontrol_flags[i].dmi_system_version == NULL ||
544 !strcmp(v4lcontrol_flags[i].dmi_system_version, dmi_system_version)) &&
545
546 (v4lcontrol_flags[i].dmi_board_vendor == NULL ||
547 !strcmp(v4lcontrol_flags[i].dmi_board_vendor, dmi_board_vendor)) &&
548 (v4lcontrol_flags[i].dmi_board_name == NULL ||
549 !strcmp(v4lcontrol_flags[i].dmi_board_name, dmi_board_name)) &&
550 (v4lcontrol_flags[i].dmi_board_version == NULL ||
551 !strcmp(v4lcontrol_flags[i].dmi_board_version, dmi_board_version))) {
552 data->flags |= v4lcontrol_flags[i].flags;
553 data->flags_info = &v4lcontrol_flags[i];
554 /* Entries in the v4lcontrol_flags table override
555 wildcard matches in the upside_down table. */
556 return;
557 }
558
559 for (i = 0; i < ARRAY_SIZE(upside_down); i++)
560 if (find_dmi_string(upside_down[i].board_vendor, dmi_board_vendor) &&
561 find_dmi_string(upside_down[i].board_name, dmi_board_name) &&
562 find_usb_id(upside_down[i].camera_id, vendor_id, product_id)) {
563 /* found entry */
564 data->flags |= V4LCONTROL_HFLIPPED | V4LCONTROL_VFLIPPED;
565 break;
566 }
567 }
568
v4lcontrol_create(int fd,void * dev_ops_priv,const struct libv4l_dev_ops * dev_ops,int always_needs_conversion)569 struct v4lcontrol_data *v4lcontrol_create(int fd, void *dev_ops_priv,
570 const struct libv4l_dev_ops *dev_ops, int always_needs_conversion)
571 {
572 int shm_fd;
573 int i, rc, got_usb_info, speed, init = 0;
574 char *s, shm_name[256], pwd_buf[1024];
575 struct v4l2_capability cap;
576 struct v4l2_queryctrl ctrl;
577 struct passwd pwd, *pwd_p;
578 unsigned short vendor_id = 0;
579 unsigned short product_id = 0;
580 struct v4l2_input input;
581
582 struct v4lcontrol_data *data = calloc(1, sizeof(struct v4lcontrol_data));
583
584 if (!data) {
585 fprintf(stderr, "libv4lcontrol: error: out of memory!\n");
586 return NULL;
587 }
588
589 data->fd = fd;
590 data->dev_ops = dev_ops;
591 data->dev_ops_priv = dev_ops_priv;
592
593 /* Check if the driver has indicated some form of flipping is needed */
594 if ((data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
595 VIDIOC_G_INPUT, &input.index) == 0) &&
596 (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
597 VIDIOC_ENUMINPUT, &input) == 0)) {
598 if (input.status & V4L2_IN_ST_HFLIP)
599 data->flags |= V4LCONTROL_HFLIPPED;
600 if (input.status & V4L2_IN_ST_VFLIP)
601 data->flags |= V4LCONTROL_VFLIPPED;
602 }
603
604 s = getenv("LIBV4LCONTROL_SYSFS_PREFIX");
605 if (!s)
606 s = "";
607
608 got_usb_info = v4lcontrol_get_usb_info(data, s, &vendor_id, &product_id,
609 &speed);
610 if (got_usb_info) {
611 v4lcontrol_get_flags_from_db(data, s, vendor_id, product_id);
612 switch (speed) {
613 case 12:
614 data->bandwidth = 1023 * 1000;
615 break;
616 case 480:
617 data->bandwidth = 3 * 1024 * 8000;
618 break;
619 case 5000:
620 data->bandwidth = 48 * 1024 * 8000;
621 break;
622 default:
623 /* heuh, low speed device, or ... ? */
624 data->bandwidth = speed / 20;
625 }
626 } else
627 data->bandwidth = 0;
628
629 /* Allow overriding through environment */
630 s = getenv("LIBV4LCONTROL_FLAGS");
631 if (s)
632 data->flags = strtol(s, NULL, 0);
633
634 ctrl.id = V4L2_CTRL_FLAG_NEXT_CTRL;
635 if (data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
636 VIDIOC_QUERYCTRL, &ctrl) == 0)
637 data->priv_flags |= V4LCONTROL_SUPPORTS_NEXT_CTRL;
638
639 /* If the device always needs conversion, we can add fake controls at no cost
640 (no cost when not activated by the user that is) */
641 if (always_needs_conversion || v4lcontrol_needs_conversion(data)) {
642 for (i = 0; i < V4LCONTROL_AUTO_ENABLE_COUNT; i++) {
643 ctrl.id = fake_controls[i].id;
644 rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
645 VIDIOC_QUERYCTRL, &ctrl);
646 if (rc == -1 ||
647 (rc == 0 &&
648 (ctrl.flags & V4L2_CTRL_FLAG_DISABLED)))
649 data->controls |= 1 << i;
650 }
651 }
652
653 /* Check if a camera does not have hardware autogain and has the necessary
654 controls, before enabling sw autogain, even if this is requested by flags.
655 This is necessary because some cameras share a USB-ID, but can have
656 different sensors with / without autogain or the necessary controls. */
657 while (data->flags & V4LCONTROL_WANTS_AUTOGAIN) {
658 ctrl.id = V4L2_CID_AUTOGAIN;
659 rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
660 VIDIOC_QUERYCTRL, &ctrl);
661 if (rc == 0 && !(ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
662 break;
663
664 ctrl.id = V4L2_CID_EXPOSURE;
665 rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
666 VIDIOC_QUERYCTRL, &ctrl);
667 if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
668 break;
669
670 ctrl.id = V4L2_CID_GAIN;
671 rc = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
672 VIDIOC_QUERYCTRL, &ctrl);
673 if (rc != 0 || (ctrl.flags & V4L2_CTRL_FLAG_DISABLED))
674 break;
675
676 data->controls |= 1 << V4LCONTROL_AUTOGAIN |
677 1 << V4LCONTROL_AUTOGAIN_TARGET;
678 break;
679 }
680
681 /* Allow overriding through environment */
682 s = getenv("LIBV4LCONTROL_CONTROLS");
683 if (s)
684 data->controls = strtol(s, NULL, 0);
685
686 if (data->controls == 0)
687 return data; /* No need to create a shared memory segment */
688
689 if (data->dev_ops->ioctl(data->dev_ops_priv, fd,
690 VIDIOC_QUERYCAP, &cap)) {
691 perror("libv4lcontrol: error querying device capabilities");
692 goto error;
693 }
694
695 if (getpwuid_r(geteuid(), &pwd, pwd_buf, sizeof(pwd_buf), &pwd_p) == 0) {
696 if (got_usb_info)
697 snprintf(shm_name, 256, "/libv4l-%s:%s:%04x:%04x:%s", pwd.pw_name,
698 cap.bus_info, (int)vendor_id, (int)product_id, cap.card);
699 else
700 snprintf(shm_name, 256, "/libv4l-%s:%s:%s", pwd.pw_name,
701 cap.bus_info, cap.card);
702 } else {
703 perror("libv4lcontrol: error getting username using uid instead");
704 if (got_usb_info)
705 snprintf(shm_name, 256, "/libv4l-%lu:%s:%04x:%04x:%s",
706 (unsigned long)geteuid(), cap.bus_info,
707 (int)vendor_id, (int)product_id, cap.card);
708 else
709 snprintf(shm_name, 256, "/libv4l-%lu:%s:%s", (unsigned long)geteuid(),
710 cap.bus_info, cap.card);
711 }
712
713 /* / is not allowed inside shm names */
714 for (i = 1; shm_name[i]; i++)
715 if (shm_name[i] == '/')
716 shm_name[i] = '-';
717
718 #ifndef ANDROID
719 /* Open the shared memory object identified by shm_name */
720 shm_fd = shm_open(shm_name, (O_CREAT | O_EXCL | O_RDWR), (S_IREAD | S_IWRITE));
721 if (shm_fd >= 0)
722 init = 1;
723 else
724 shm_fd = shm_open(shm_name, O_RDWR, (S_IREAD | S_IWRITE));
725
726 if (shm_fd >= 0) {
727 /* Set the shared memory size */
728 int ret = ftruncate(shm_fd, V4LCONTROL_SHM_SIZE);
729 if (ret) {
730 perror("libv4lcontrol: shm ftruncate failed");
731 close(shm_fd);
732 } else {
733 /* Retrieve a pointer to the shm object */
734 data->shm_values = mmap(NULL, V4LCONTROL_SHM_SIZE,
735 PROT_READ | PROT_WRITE,
736 MAP_SHARED, shm_fd, 0);
737 close(shm_fd);
738
739 if (data->shm_values == MAP_FAILED) {
740 perror("libv4lcontrol: shm mmap failed");
741 data->shm_values = NULL;
742 }
743 }
744 } else
745 perror("libv4lcontrol: error creating shm segment failed");
746 #endif
747
748 /* Fall back to malloc */
749 if (data->shm_values == NULL) {
750 fprintf(stderr,
751 "libv4lcontrol: falling back to malloc-ed memory for controls\n");
752 data->shm_values = malloc(V4LCONTROL_SHM_SIZE);
753 if (!data->shm_values) {
754 fprintf(stderr, "libv4lcontrol: error: out of memory!\n");
755 goto error;
756 }
757 init = 1;
758 data->priv_flags |= V4LCONTROL_MEMORY_IS_MALLOCED;
759 }
760
761 if (init) {
762 /* Initialize the new shm object we created */
763 memset(data->shm_values, 0, V4LCONTROL_SHM_SIZE);
764
765 for (i = 0; i < V4LCONTROL_COUNT; i++)
766 data->shm_values[i] = fake_controls[i].default_value;
767
768 if (data->flags & V4LCONTROL_WANTS_WB)
769 data->shm_values[V4LCONTROL_WHITEBALANCE] = 1;
770
771 if (data->flags_info && data->flags_info->default_gamma)
772 data->shm_values[V4LCONTROL_GAMMA] = data->flags_info->default_gamma;
773 }
774
775 return data;
776
777 error:
778 free(data);
779 return NULL;
780 }
781
v4lcontrol_destroy(struct v4lcontrol_data * data)782 void v4lcontrol_destroy(struct v4lcontrol_data *data)
783 {
784 if (data->controls) {
785 if (data->priv_flags & V4LCONTROL_MEMORY_IS_MALLOCED)
786 free(data->shm_values);
787 else
788 munmap(data->shm_values, V4LCONTROL_SHM_SIZE);
789 }
790 free(data);
791 }
792
793 static const struct v4l2_queryctrl fake_controls[V4LCONTROL_COUNT] = {
794 {
795 .id = V4L2_CID_AUTO_WHITE_BALANCE,
796 .type = V4L2_CTRL_TYPE_BOOLEAN,
797 .name = "White Balance, Automatic",
798 .minimum = 0,
799 .maximum = 1,
800 .step = 1,
801 .default_value = 0,
802 .flags = 0
803 }, {
804 .id = V4L2_CID_HFLIP,
805 .type = V4L2_CTRL_TYPE_BOOLEAN,
806 .name = "Horizontal Flip",
807 .minimum = 0,
808 .maximum = 1,
809 .step = 1,
810 .default_value = 0,
811 .flags = 0
812 }, {
813 .id = V4L2_CID_VFLIP,
814 .type = V4L2_CTRL_TYPE_BOOLEAN,
815 .name = "Vertical Flip",
816 .minimum = 0,
817 .maximum = 1,
818 .step = 1,
819 .default_value = 0,
820 .flags = 0
821 }, {
822 .id = V4L2_CID_GAMMA,
823 .type = V4L2_CTRL_TYPE_INTEGER,
824 .name = "Gamma",
825 .minimum = 500, /* == 0.5 */
826 .maximum = 3000, /* == 3.0 */
827 .step = 1,
828 .default_value = 1000, /* == 1.0 */
829 .flags = V4L2_CTRL_FLAG_SLIDER
830 }, { /* Dummy place holder for V4LCONTROL_AUTO_ENABLE_COUNT */
831 }, {
832 .id = V4L2_CID_AUTOGAIN,
833 .type = V4L2_CTRL_TYPE_BOOLEAN,
834 .name = "Gain, Automatic",
835 .minimum = 0,
836 .maximum = 1,
837 .step = 1,
838 .default_value = 1,
839 .flags = 0
840 }, {
841 .id = V4L2_CTRL_CLASS_USER + 0x2000, /* FIXME */
842 .type = V4L2_CTRL_TYPE_INTEGER,
843 .name = "Auto Gain Target",
844 .minimum = 0,
845 .maximum = 255,
846 .step = 1,
847 .default_value = 100,
848 .flags = V4L2_CTRL_FLAG_SLIDER
849 },
850 };
851
v4lcontrol_copy_queryctrl(struct v4lcontrol_data * data,struct v4l2_queryctrl * ctrl,int i)852 static void v4lcontrol_copy_queryctrl(struct v4lcontrol_data *data,
853 struct v4l2_queryctrl *ctrl, int i)
854 {
855 memcpy(ctrl, &fake_controls[i], sizeof(struct v4l2_queryctrl));
856
857 /* Hmm, not pretty */
858 if (ctrl->id == V4L2_CID_AUTO_WHITE_BALANCE &&
859 (data->flags & V4LCONTROL_WANTS_WB))
860 ctrl->default_value = 1;
861
862 if (ctrl->id == V4L2_CID_GAMMA && data->flags_info &&
863 data->flags_info->default_gamma)
864 ctrl->default_value = data->flags_info->default_gamma;
865 }
866
v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data * data,void * arg)867 int v4lcontrol_vidioc_queryctrl(struct v4lcontrol_data *data, void *arg)
868 {
869 int i;
870 struct v4l2_queryctrl *ctrl = arg;
871 int retval;
872 uint32_t orig_id = ctrl->id;
873
874 /* if we have an exact match return it */
875 for (i = 0; i < V4LCONTROL_COUNT; i++)
876 if ((data->controls & (1 << i)) &&
877 ctrl->id == fake_controls[i].id) {
878 v4lcontrol_copy_queryctrl(data, ctrl, i);
879 return 0;
880 }
881
882 /* find out what the kernel driver would respond. */
883 retval = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
884 VIDIOC_QUERYCTRL, arg);
885
886 if ((data->priv_flags & V4LCONTROL_SUPPORTS_NEXT_CTRL) &&
887 (orig_id & V4L2_CTRL_FLAG_NEXT_CTRL)) {
888 /* If the hardware has no more controls check if we still have any
889 fake controls with a higher id than the hardware's highest */
890 if (retval)
891 ctrl->id = V4L2_CTRL_ID_MASK;
892
893 /* If any of our controls have an id > orig_id but less than
894 ctrl->id then return that control instead. Note we do not
895 break when we have a match, but keep iterating, so that
896 we end up with the fake ctrl with the lowest CID > orig_id. */
897 for (i = 0; i < V4LCONTROL_COUNT; i++)
898 if ((data->controls & (1 << i)) &&
899 (fake_controls[i].id > (orig_id & ~V4L2_CTRL_FLAG_NEXT_CTRL)) &&
900 (fake_controls[i].id <= ctrl->id)) {
901 v4lcontrol_copy_queryctrl(data, ctrl, i);
902 retval = 0;
903 }
904 }
905
906 return retval;
907 }
908
v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data * data,void * arg)909 int v4lcontrol_vidioc_g_ctrl(struct v4lcontrol_data *data, void *arg)
910 {
911 int i;
912 struct v4l2_control *ctrl = arg;
913
914 for (i = 0; i < V4LCONTROL_COUNT; i++)
915 if ((data->controls & (1 << i)) &&
916 ctrl->id == fake_controls[i].id) {
917 ctrl->value = data->shm_values[i];
918 return 0;
919 }
920
921 return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
922 VIDIOC_G_CTRL, arg);
923 }
924
v4lcontrol_alloc_valid_controls(struct v4lcontrol_data * data,const struct v4l2_ext_controls * src,struct v4l2_ext_controls * dst)925 static void v4lcontrol_alloc_valid_controls(struct v4lcontrol_data *data,
926 const struct v4l2_ext_controls *src,
927 struct v4l2_ext_controls *dst)
928 {
929 struct v4l2_ext_control *ctrl;
930 unsigned i, j;
931
932 *dst = *src;
933 if (data->controls == 0)
934 return;
935 ctrl = malloc(src->count * sizeof(*ctrl));
936 if (ctrl == NULL)
937 return;
938 dst->controls = ctrl;
939 dst->count = 0;
940 for (i = 0; i < src->count; i++) {
941 for (j = 0; j < V4LCONTROL_COUNT; j++)
942 if ((data->controls & (1 << j)) &&
943 src->controls[i].id == fake_controls[j].id)
944 break;
945 if (j == V4LCONTROL_COUNT)
946 ctrl[dst->count++] = src->controls[i];
947 }
948 }
949
v4lcontrol_free_valid_controls(struct v4lcontrol_data * data,struct v4l2_ext_controls * src,struct v4l2_ext_controls * dst)950 static void v4lcontrol_free_valid_controls(struct v4lcontrol_data *data,
951 struct v4l2_ext_controls *src,
952 struct v4l2_ext_controls *dst)
953 {
954 unsigned i, j, k = 0;
955 int inc_idx;
956
957 src->error_idx = dst->error_idx;
958 if (dst->controls == src->controls)
959 return;
960
961 inc_idx = dst->error_idx < dst->count;
962 for (i = 0; i < src->count; i++) {
963 for (j = 0; j < V4LCONTROL_COUNT; j++)
964 if ((data->controls & (1 << j)) &&
965 src->controls[i].id == fake_controls[j].id)
966 break;
967 if (j == V4LCONTROL_COUNT)
968 src->controls[i] = dst->controls[k++];
969 else if (inc_idx)
970 src->error_idx++;
971 }
972 free(dst->controls);
973 }
974
v4lcontrol_vidioc_g_ext_ctrls(struct v4lcontrol_data * data,void * arg)975 int v4lcontrol_vidioc_g_ext_ctrls(struct v4lcontrol_data *data, void *arg)
976 {
977 struct v4l2_ext_controls *ctrls = arg;
978 struct v4l2_ext_controls dst;
979 int i, j;
980 int res;
981
982 v4lcontrol_alloc_valid_controls(data, ctrls, &dst);
983 res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
984 VIDIOC_G_EXT_CTRLS, &dst);
985 v4lcontrol_free_valid_controls(data, ctrls, &dst);
986 if (res)
987 return res;
988
989 for (i = 0; i < ctrls->count; i++) {
990 for (j = 0; j < V4LCONTROL_COUNT; j++)
991 if ((data->controls & (1 << j)) &&
992 ctrls->controls[i].id == fake_controls[j].id) {
993 ctrls->controls[i].value = data->shm_values[j];
994 break;
995 }
996 }
997 return 0;
998 }
999
v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data * data,void * arg)1000 int v4lcontrol_vidioc_s_ctrl(struct v4lcontrol_data *data, void *arg)
1001 {
1002 int i;
1003 struct v4l2_control *ctrl = arg;
1004
1005 for (i = 0; i < V4LCONTROL_COUNT; i++)
1006 if ((data->controls & (1 << i)) &&
1007 ctrl->id == fake_controls[i].id) {
1008 if (ctrl->value > fake_controls[i].maximum ||
1009 ctrl->value < fake_controls[i].minimum) {
1010 errno = EINVAL;
1011 return -1;
1012 }
1013
1014 data->shm_values[i] = ctrl->value;
1015 return 0;
1016 }
1017
1018 return data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
1019 VIDIOC_S_CTRL, arg);
1020 }
1021
v4lcontrol_validate_ext_ctrls(struct v4lcontrol_data * data,struct v4l2_ext_controls * ctrls)1022 static int v4lcontrol_validate_ext_ctrls(struct v4lcontrol_data *data,
1023 struct v4l2_ext_controls *ctrls)
1024 {
1025 int i, j;
1026
1027 if (data->controls == 0)
1028 return 0;
1029 for (i = 0; i < ctrls->count; i++) {
1030 for (j = 0; j < V4LCONTROL_COUNT; j++)
1031 if ((data->controls & (1 << j)) &&
1032 ctrls->controls[i].id == fake_controls[j].id) {
1033 if (ctrls->controls[i].value > fake_controls[j].maximum ||
1034 ctrls->controls[i].value < fake_controls[j].minimum) {
1035 ctrls->error_idx = i;
1036 errno = EINVAL;
1037 return -1;
1038 }
1039 }
1040 }
1041 return 0;
1042 }
1043
v4lcontrol_vidioc_try_ext_ctrls(struct v4lcontrol_data * data,void * arg)1044 int v4lcontrol_vidioc_try_ext_ctrls(struct v4lcontrol_data *data, void *arg)
1045 {
1046 struct v4l2_ext_controls *ctrls = arg;
1047 struct v4l2_ext_controls dst;
1048 int res = v4lcontrol_validate_ext_ctrls(data, ctrls);
1049
1050 if (res)
1051 return res;
1052
1053 v4lcontrol_alloc_valid_controls(data, ctrls, &dst);
1054 res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
1055 VIDIOC_TRY_EXT_CTRLS, &dst);
1056 v4lcontrol_free_valid_controls(data, ctrls, &dst);
1057 return res;
1058 }
1059
v4lcontrol_vidioc_s_ext_ctrls(struct v4lcontrol_data * data,void * arg)1060 int v4lcontrol_vidioc_s_ext_ctrls(struct v4lcontrol_data *data, void *arg)
1061 {
1062 struct v4l2_ext_controls *ctrls = arg;
1063 struct v4l2_ext_controls dst;
1064 int i, j;
1065 int res = v4lcontrol_validate_ext_ctrls(data, ctrls);
1066
1067 if (res)
1068 return res;
1069
1070 v4lcontrol_alloc_valid_controls(data, ctrls, &dst);
1071 res = data->dev_ops->ioctl(data->dev_ops_priv, data->fd,
1072 VIDIOC_S_EXT_CTRLS, &dst);
1073 v4lcontrol_free_valid_controls(data, ctrls, &dst);
1074 if (res)
1075 return res;
1076
1077 for (i = 0; i < ctrls->count; i++) {
1078 for (j = 0; j < V4LCONTROL_COUNT; j++)
1079 if ((data->controls & (1 << j)) &&
1080 ctrls->controls[i].id == fake_controls[j].id) {
1081 data->shm_values[j] = ctrls->controls[i].value;
1082 break;
1083 }
1084 }
1085 return 0;
1086 }
1087
v4lcontrol_get_bandwidth(struct v4lcontrol_data * data)1088 int v4lcontrol_get_bandwidth(struct v4lcontrol_data *data)
1089 {
1090 return data->bandwidth;
1091 }
1092
v4lcontrol_get_flags(struct v4lcontrol_data * data)1093 int v4lcontrol_get_flags(struct v4lcontrol_data *data)
1094 {
1095 return data->flags;
1096 }
1097
v4lcontrol_get_ctrl(struct v4lcontrol_data * data,int ctrl)1098 int v4lcontrol_get_ctrl(struct v4lcontrol_data *data, int ctrl)
1099 {
1100 if (data->controls & (1 << ctrl)) {
1101 /* Special case for devices with flipped input */
1102 if ((ctrl == V4LCONTROL_HFLIP && (data->flags & V4LCONTROL_HFLIPPED)) ||
1103 (ctrl == V4LCONTROL_VFLIP && (data->flags & V4LCONTROL_VFLIPPED)))
1104 return !data->shm_values[ctrl];
1105
1106 return data->shm_values[ctrl];
1107 }
1108
1109 return 0;
1110 }
1111
v4lcontrol_controls_changed(struct v4lcontrol_data * data)1112 int v4lcontrol_controls_changed(struct v4lcontrol_data *data)
1113 {
1114 int res;
1115
1116 if (!data->controls)
1117 return 0;
1118
1119 res = memcmp(data->shm_values, data->old_values,
1120 V4LCONTROL_COUNT * sizeof(unsigned int));
1121
1122 memcpy(data->old_values, data->shm_values,
1123 V4LCONTROL_COUNT * sizeof(unsigned int));
1124
1125 return res;
1126 }
1127
1128 /* See the comment about this in libv4lconvert.h */
v4lcontrol_needs_conversion(struct v4lcontrol_data * data)1129 int v4lcontrol_needs_conversion(struct v4lcontrol_data *data)
1130 {
1131 return data->flags || data->controls;
1132 }
1133