• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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