Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* Driver for Philips webcam |
2 | Functions that send various control messages to the webcam, including | |
3 | video modes. | |
4 | (C) 1999-2003 Nemosoft Unv. | |
5 | (C) 2004 Luc Saillard (luc@saillard.org) | |
6 | ||
7 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx | |
8 | driver and thus may have bugs that are not present in the original version. | |
9 | Please send bug reports and support requests to <luc@saillard.org>. | |
10 | ||
11 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx | |
12 | driver and thus may have bugs that are not present in the original version. | |
13 | Please send bug reports and support requests to <luc@saillard.org>. | |
14 | The decompression routines have been implemented by reverse-engineering the | |
15 | Nemosoft binary pwcx module. Caveat emptor. | |
16 | ||
17 | This program is free software; you can redistribute it and/or modify | |
18 | it under the terms of the GNU General Public License as published by | |
19 | the Free Software Foundation; either version 2 of the License, or | |
20 | (at your option) any later version. | |
21 | ||
22 | This program is distributed in the hope that it will be useful, | |
23 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | GNU General Public License for more details. | |
26 | ||
27 | You should have received a copy of the GNU General Public License | |
28 | along with this program; if not, write to the Free Software | |
29 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
30 | */ | |
31 | ||
32 | /* | |
33 | Changes | |
34 | 2001/08/03 Alvarado Added methods for changing white balance and | |
35 | red/green gains | |
36 | */ | |
37 | ||
38 | /* Control functions for the cam; brightness, contrast, video mode, etc. */ | |
39 | ||
40 | #ifdef __KERNEL__ | |
41 | #include <asm/uaccess.h> | |
42 | #endif | |
43 | #include <asm/errno.h> | |
44 | #include <linux/version.h> | |
45 | ||
46 | #include "pwc.h" | |
47 | #include "pwc-ioctl.h" | |
48 | #include "pwc-uncompress.h" | |
49 | #include "pwc-kiara.h" | |
50 | #include "pwc-timon.h" | |
51 | #include "pwc-dec1.h" | |
52 | #include "pwc-dec23.h" | |
53 | ||
54 | /* Request types: video */ | |
55 | #define SET_LUM_CTL 0x01 | |
56 | #define GET_LUM_CTL 0x02 | |
57 | #define SET_CHROM_CTL 0x03 | |
58 | #define GET_CHROM_CTL 0x04 | |
59 | #define SET_STATUS_CTL 0x05 | |
60 | #define GET_STATUS_CTL 0x06 | |
61 | #define SET_EP_STREAM_CTL 0x07 | |
62 | #define GET_EP_STREAM_CTL 0x08 | |
63 | #define SET_MPT_CTL 0x0D | |
64 | #define GET_MPT_CTL 0x0E | |
65 | ||
66 | /* Selectors for the Luminance controls [GS]ET_LUM_CTL */ | |
67 | #define AGC_MODE_FORMATTER 0x2000 | |
68 | #define PRESET_AGC_FORMATTER 0x2100 | |
69 | #define SHUTTER_MODE_FORMATTER 0x2200 | |
70 | #define PRESET_SHUTTER_FORMATTER 0x2300 | |
71 | #define PRESET_CONTOUR_FORMATTER 0x2400 | |
72 | #define AUTO_CONTOUR_FORMATTER 0x2500 | |
73 | #define BACK_LIGHT_COMPENSATION_FORMATTER 0x2600 | |
74 | #define CONTRAST_FORMATTER 0x2700 | |
75 | #define DYNAMIC_NOISE_CONTROL_FORMATTER 0x2800 | |
76 | #define FLICKERLESS_MODE_FORMATTER 0x2900 | |
77 | #define AE_CONTROL_SPEED 0x2A00 | |
78 | #define BRIGHTNESS_FORMATTER 0x2B00 | |
79 | #define GAMMA_FORMATTER 0x2C00 | |
80 | ||
81 | /* Selectors for the Chrominance controls [GS]ET_CHROM_CTL */ | |
82 | #define WB_MODE_FORMATTER 0x1000 | |
83 | #define AWB_CONTROL_SPEED_FORMATTER 0x1100 | |
84 | #define AWB_CONTROL_DELAY_FORMATTER 0x1200 | |
85 | #define PRESET_MANUAL_RED_GAIN_FORMATTER 0x1300 | |
86 | #define PRESET_MANUAL_BLUE_GAIN_FORMATTER 0x1400 | |
87 | #define COLOUR_MODE_FORMATTER 0x1500 | |
88 | #define SATURATION_MODE_FORMATTER1 0x1600 | |
89 | #define SATURATION_MODE_FORMATTER2 0x1700 | |
90 | ||
91 | /* Selectors for the Status controls [GS]ET_STATUS_CTL */ | |
92 | #define SAVE_USER_DEFAULTS_FORMATTER 0x0200 | |
93 | #define RESTORE_USER_DEFAULTS_FORMATTER 0x0300 | |
94 | #define RESTORE_FACTORY_DEFAULTS_FORMATTER 0x0400 | |
95 | #define READ_AGC_FORMATTER 0x0500 | |
96 | #define READ_SHUTTER_FORMATTER 0x0600 | |
97 | #define READ_RED_GAIN_FORMATTER 0x0700 | |
98 | #define READ_BLUE_GAIN_FORMATTER 0x0800 | |
99 | #define SENSOR_TYPE_FORMATTER1 0x0C00 | |
100 | #define READ_RAW_Y_MEAN_FORMATTER 0x3100 | |
101 | #define SET_POWER_SAVE_MODE_FORMATTER 0x3200 | |
102 | #define MIRROR_IMAGE_FORMATTER 0x3300 | |
103 | #define LED_FORMATTER 0x3400 | |
104 | #define SENSOR_TYPE_FORMATTER2 0x3700 | |
105 | ||
106 | /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ | |
107 | #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 | |
108 | ||
109 | /* Formatters for the motorized pan & tilt [GS]ET_MPT_CTL */ | |
110 | #define PT_RELATIVE_CONTROL_FORMATTER 0x01 | |
111 | #define PT_RESET_CONTROL_FORMATTER 0x02 | |
112 | #define PT_STATUS_FORMATTER 0x03 | |
113 | ||
114 | static char *size2name[PSZ_MAX] = | |
115 | { | |
116 | "subQCIF", | |
117 | "QSIF", | |
118 | "QCIF", | |
119 | "SIF", | |
120 | "CIF", | |
121 | "VGA", | |
122 | }; | |
123 | ||
124 | /********/ | |
125 | ||
126 | /* Entries for the Nala (645/646) camera; the Nala doesn't have compression | |
127 | preferences, so you either get compressed or non-compressed streams. | |
128 | ||
129 | An alternate value of 0 means this mode is not available at all. | |
130 | */ | |
131 | ||
132 | struct Nala_table_entry { | |
133 | char alternate; /* USB alternate setting */ | |
134 | int compressed; /* Compressed yes/no */ | |
135 | ||
136 | unsigned char mode[3]; /* precomputed mode table */ | |
137 | }; | |
138 | ||
139 | static struct Nala_table_entry Nala_table[PSZ_MAX][8] = | |
140 | { | |
141 | #include "pwc-nala.h" | |
142 | }; | |
143 | ||
144 | ||
145 | /****************************************************************************/ | |
146 | ||
147 | ||
148 | #define SendControlMsg(request, value, buflen) \ | |
149 | usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \ | |
150 | request, \ | |
151 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ | |
152 | value, \ | |
153 | pdev->vcinterface, \ | |
154 | &buf, buflen, 500) | |
155 | ||
156 | #define RecvControlMsg(request, value, buflen) \ | |
157 | usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \ | |
158 | request, \ | |
159 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \ | |
160 | value, \ | |
161 | pdev->vcinterface, \ | |
162 | &buf, buflen, 500) | |
163 | ||
164 | ||
165 | #if PWC_DEBUG | |
166 | void pwc_hexdump(void *p, int len) | |
167 | { | |
168 | int i; | |
169 | unsigned char *s; | |
170 | char buf[100], *d; | |
171 | ||
172 | s = (unsigned char *)p; | |
173 | d = buf; | |
174 | *d = '\0'; | |
175 | Debug("Doing hexdump @ %p, %d bytes.\n", p, len); | |
176 | for (i = 0; i < len; i++) { | |
177 | d += sprintf(d, "%02X ", *s++); | |
178 | if ((i & 0xF) == 0xF) { | |
179 | Debug("%s\n", buf); | |
180 | d = buf; | |
181 | *d = '\0'; | |
182 | } | |
183 | } | |
184 | if ((i & 0xF) != 0) | |
185 | Debug("%s\n", buf); | |
186 | } | |
187 | #endif | |
188 | ||
189 | static inline int send_video_command(struct usb_device *udev, int index, void *buf, int buflen) | |
190 | { | |
191 | return usb_control_msg(udev, | |
192 | usb_sndctrlpipe(udev, 0), | |
193 | SET_EP_STREAM_CTL, | |
194 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | |
195 | VIDEO_OUTPUT_CONTROL_FORMATTER, | |
196 | index, | |
197 | buf, buflen, 1000); | |
198 | } | |
199 | ||
200 | ||
201 | ||
202 | static inline int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) | |
203 | { | |
204 | unsigned char buf[3]; | |
205 | int ret, fps; | |
206 | struct Nala_table_entry *pEntry; | |
207 | int frames2frames[31] = | |
208 | { /* closest match of framerate */ | |
209 | 0, 0, 0, 0, 4, /* 0-4 */ | |
210 | 5, 5, 7, 7, 10, /* 5-9 */ | |
211 | 10, 10, 12, 12, 15, /* 10-14 */ | |
212 | 15, 15, 15, 20, 20, /* 15-19 */ | |
213 | 20, 20, 20, 24, 24, /* 20-24 */ | |
214 | 24, 24, 24, 24, 24, /* 25-29 */ | |
215 | 24 /* 30 */ | |
216 | }; | |
217 | int frames2table[31] = | |
218 | { 0, 0, 0, 0, 0, /* 0-4 */ | |
219 | 1, 1, 1, 2, 2, /* 5-9 */ | |
220 | 3, 3, 4, 4, 4, /* 10-14 */ | |
221 | 5, 5, 5, 5, 5, /* 15-19 */ | |
222 | 6, 6, 6, 6, 7, /* 20-24 */ | |
223 | 7, 7, 7, 7, 7, /* 25-29 */ | |
224 | 7 /* 30 */ | |
225 | }; | |
226 | ||
227 | if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) | |
228 | return -EINVAL; | |
229 | frames = frames2frames[frames]; | |
230 | fps = frames2table[frames]; | |
231 | pEntry = &Nala_table[size][fps]; | |
232 | if (pEntry->alternate == 0) | |
233 | return -EINVAL; | |
234 | ||
235 | if (pEntry->compressed) | |
236 | return -ENOENT; /* Not supported. */ | |
237 | ||
238 | memcpy(buf, pEntry->mode, 3); | |
239 | ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3); | |
240 | if (ret < 0) { | |
241 | Debug("Failed to send video command... %d\n", ret); | |
242 | return ret; | |
243 | } | |
244 | if (pEntry->compressed && pdev->vpalette != VIDEO_PALETTE_RAW) | |
245 | { | |
246 | switch(pdev->type) { | |
247 | case 645: | |
248 | case 646: | |
249 | pwc_dec1_init(pdev->type, pdev->release, buf, pdev->decompress_data); | |
250 | break; | |
251 | ||
252 | case 675: | |
253 | case 680: | |
254 | case 690: | |
255 | case 720: | |
256 | case 730: | |
257 | case 740: | |
258 | case 750: | |
259 | pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); | |
260 | break; | |
261 | } | |
262 | } | |
263 | ||
264 | pdev->cmd_len = 3; | |
265 | memcpy(pdev->cmd_buf, buf, 3); | |
266 | ||
267 | /* Set various parameters */ | |
268 | pdev->vframes = frames; | |
269 | pdev->vsize = size; | |
270 | pdev->valternate = pEntry->alternate; | |
271 | pdev->image = pwc_image_sizes[size]; | |
272 | pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; | |
273 | if (pEntry->compressed) { | |
274 | if (pdev->release < 5) { /* 4 fold compression */ | |
275 | pdev->vbandlength = 528; | |
276 | pdev->frame_size /= 4; | |
277 | } | |
278 | else { | |
279 | pdev->vbandlength = 704; | |
280 | pdev->frame_size /= 3; | |
281 | } | |
282 | } | |
283 | else | |
284 | pdev->vbandlength = 0; | |
285 | return 0; | |
286 | } | |
287 | ||
288 | ||
289 | static inline int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) | |
290 | { | |
291 | unsigned char buf[13]; | |
292 | const struct Timon_table_entry *pChoose; | |
293 | int ret, fps; | |
294 | ||
295 | if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) | |
296 | return -EINVAL; | |
297 | if (size == PSZ_VGA && frames > 15) | |
298 | return -EINVAL; | |
299 | fps = (frames / 5) - 1; | |
300 | ||
301 | /* Find a supported framerate with progressively higher compression ratios | |
302 | if the preferred ratio is not available. | |
303 | */ | |
304 | pChoose = NULL; | |
305 | while (compression <= 3) { | |
306 | pChoose = &Timon_table[size][fps][compression]; | |
307 | if (pChoose->alternate != 0) | |
308 | break; | |
309 | compression++; | |
310 | } | |
311 | if (pChoose == NULL || pChoose->alternate == 0) | |
312 | return -ENOENT; /* Not supported. */ | |
313 | ||
314 | memcpy(buf, pChoose->mode, 13); | |
315 | if (snapshot) | |
316 | buf[0] |= 0x80; | |
317 | ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 13); | |
318 | if (ret < 0) | |
319 | return ret; | |
320 | ||
321 | if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) | |
322 | pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); | |
323 | ||
324 | pdev->cmd_len = 13; | |
325 | memcpy(pdev->cmd_buf, buf, 13); | |
326 | ||
327 | /* Set various parameters */ | |
328 | pdev->vframes = frames; | |
329 | pdev->vsize = size; | |
330 | pdev->vsnapshot = snapshot; | |
331 | pdev->valternate = pChoose->alternate; | |
332 | pdev->image = pwc_image_sizes[size]; | |
333 | pdev->vbandlength = pChoose->bandlength; | |
334 | if (pChoose->bandlength > 0) | |
335 | pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; | |
336 | else | |
337 | pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; | |
338 | return 0; | |
339 | } | |
340 | ||
341 | ||
342 | static inline int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) | |
343 | { | |
344 | const struct Kiara_table_entry *pChoose = NULL; | |
345 | int fps, ret; | |
346 | unsigned char buf[12]; | |
347 | struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; | |
348 | ||
349 | if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) | |
350 | return -EINVAL; | |
351 | if (size == PSZ_VGA && frames > 15) | |
352 | return -EINVAL; | |
353 | fps = (frames / 5) - 1; | |
354 | ||
355 | /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ | |
356 | if (size == PSZ_VGA && frames == 5 && snapshot) | |
357 | { | |
358 | /* Only available in case the raw palette is selected or | |
359 | we have the decompressor available. This mode is | |
360 | only available in compressed form | |
361 | */ | |
362 | if (pdev->vpalette == VIDEO_PALETTE_RAW) | |
363 | { | |
364 | Info("Choosing VGA/5 BAYER mode (%d).\n", pdev->vpalette); | |
365 | pChoose = &RawEntry; | |
366 | } | |
367 | else | |
368 | { | |
369 | Info("VGA/5 BAYER mode _must_ have a decompressor available, or use RAW palette.\n"); | |
370 | } | |
371 | } | |
372 | else | |
373 | { | |
374 | /* Find a supported framerate with progressively higher compression ratios | |
375 | if the preferred ratio is not available. | |
376 | Skip this step when using RAW modes. | |
377 | */ | |
378 | while (compression <= 3) { | |
379 | pChoose = &Kiara_table[size][fps][compression]; | |
380 | if (pChoose->alternate != 0) | |
381 | break; | |
382 | compression++; | |
383 | } | |
384 | } | |
385 | if (pChoose == NULL || pChoose->alternate == 0) | |
386 | return -ENOENT; /* Not supported. */ | |
387 | ||
388 | Debug("Using alternate setting %d.\n", pChoose->alternate); | |
389 | ||
390 | /* usb_control_msg won't take staticly allocated arrays as argument?? */ | |
391 | memcpy(buf, pChoose->mode, 12); | |
392 | if (snapshot) | |
393 | buf[0] |= 0x80; | |
394 | ||
395 | /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ | |
396 | ret = send_video_command(pdev->udev, 4 /* pdev->vendpoint */, buf, 12); | |
397 | if (ret < 0) | |
398 | return ret; | |
399 | ||
400 | if (pChoose->bandlength > 0 && pdev->vpalette != VIDEO_PALETTE_RAW) | |
401 | pwc_dec23_init(pdev->type, pdev->release, buf, pdev->decompress_data); | |
402 | ||
403 | pdev->cmd_len = 12; | |
404 | memcpy(pdev->cmd_buf, buf, 12); | |
405 | /* All set and go */ | |
406 | pdev->vframes = frames; | |
407 | pdev->vsize = size; | |
408 | pdev->vsnapshot = snapshot; | |
409 | pdev->valternate = pChoose->alternate; | |
410 | pdev->image = pwc_image_sizes[size]; | |
411 | pdev->vbandlength = pChoose->bandlength; | |
412 | if (pdev->vbandlength > 0) | |
413 | pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; | |
414 | else | |
415 | pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; | |
416 | return 0; | |
417 | } | |
418 | ||
419 | ||
420 | ||
2c47e7f3 AB |
421 | static void pwc_set_image_buffer_size(struct pwc_device *pdev) |
422 | { | |
423 | int i, factor = 0, filler = 0; | |
424 | ||
425 | /* for PALETTE_YUV420P */ | |
426 | switch(pdev->vpalette) | |
427 | { | |
428 | case VIDEO_PALETTE_YUV420P: | |
429 | factor = 6; | |
430 | filler = 128; | |
431 | break; | |
432 | case VIDEO_PALETTE_RAW: | |
433 | factor = 6; /* can be uncompressed YUV420P */ | |
434 | filler = 0; | |
435 | break; | |
436 | } | |
437 | ||
438 | /* Set sizes in bytes */ | |
439 | pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; | |
440 | pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; | |
441 | ||
442 | /* Align offset, or you'll get some very weird results in | |
443 | YUV420 mode... x must be multiple of 4 (to get the Y's in | |
444 | place), and y even (or you'll mixup U & V). This is less of a | |
445 | problem for YUV420P. | |
446 | */ | |
447 | pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; | |
448 | pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; | |
449 | ||
450 | /* Fill buffers with gray or black */ | |
451 | for (i = 0; i < MAX_IMAGES; i++) { | |
452 | if (pdev->image_ptr[i] != NULL) | |
453 | memset(pdev->image_ptr[i], filler, pdev->view.size); | |
454 | } | |
455 | } | |
456 | ||
457 | ||
458 | ||
1da177e4 LT |
459 | /** |
460 | @pdev: device structure | |
461 | @width: viewport width | |
462 | @height: viewport height | |
463 | @frame: framerate, in fps | |
464 | @compression: preferred compression ratio | |
465 | @snapshot: snapshot mode or streaming | |
466 | */ | |
467 | int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) | |
468 | { | |
469 | int ret, size; | |
470 | ||
471 | Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); | |
472 | size = pwc_decode_size(pdev, width, height); | |
473 | if (size < 0) { | |
474 | Debug("Could not find suitable size.\n"); | |
475 | return -ERANGE; | |
476 | } | |
477 | Debug("decode_size = %d.\n", size); | |
478 | ||
479 | ret = -EINVAL; | |
480 | switch(pdev->type) { | |
481 | case 645: | |
482 | case 646: | |
483 | ret = set_video_mode_Nala(pdev, size, frames); | |
484 | break; | |
485 | ||
486 | case 675: | |
487 | case 680: | |
488 | case 690: | |
489 | ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); | |
490 | break; | |
491 | ||
492 | case 720: | |
493 | case 730: | |
494 | case 740: | |
495 | case 750: | |
496 | ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); | |
497 | break; | |
498 | } | |
499 | if (ret < 0) { | |
500 | if (ret == -ENOENT) | |
501 | Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); | |
502 | else { | |
503 | Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); | |
504 | } | |
505 | return ret; | |
506 | } | |
507 | pdev->view.x = width; | |
508 | pdev->view.y = height; | |
509 | pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; | |
510 | pwc_set_image_buffer_size(pdev); | |
511 | Trace(TRACE_SIZE, "Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); | |
512 | return 0; | |
513 | } | |
514 | ||
515 | ||
1da177e4 LT |
516 | /* BRIGHTNESS */ |
517 | ||
518 | int pwc_get_brightness(struct pwc_device *pdev) | |
519 | { | |
520 | char buf; | |
521 | int ret; | |
522 | ||
523 | ret = RecvControlMsg(GET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); | |
524 | if (ret < 0) | |
525 | return ret; | |
526 | return buf << 9; | |
527 | } | |
528 | ||
529 | int pwc_set_brightness(struct pwc_device *pdev, int value) | |
530 | { | |
531 | char buf; | |
532 | ||
533 | if (value < 0) | |
534 | value = 0; | |
535 | if (value > 0xffff) | |
536 | value = 0xffff; | |
537 | buf = (value >> 9) & 0x7f; | |
538 | return SendControlMsg(SET_LUM_CTL, BRIGHTNESS_FORMATTER, 1); | |
539 | } | |
540 | ||
541 | /* CONTRAST */ | |
542 | ||
543 | int pwc_get_contrast(struct pwc_device *pdev) | |
544 | { | |
545 | char buf; | |
546 | int ret; | |
547 | ||
548 | ret = RecvControlMsg(GET_LUM_CTL, CONTRAST_FORMATTER, 1); | |
549 | if (ret < 0) | |
550 | return ret; | |
551 | return buf << 10; | |
552 | } | |
553 | ||
554 | int pwc_set_contrast(struct pwc_device *pdev, int value) | |
555 | { | |
556 | char buf; | |
557 | ||
558 | if (value < 0) | |
559 | value = 0; | |
560 | if (value > 0xffff) | |
561 | value = 0xffff; | |
562 | buf = (value >> 10) & 0x3f; | |
563 | return SendControlMsg(SET_LUM_CTL, CONTRAST_FORMATTER, 1); | |
564 | } | |
565 | ||
566 | /* GAMMA */ | |
567 | ||
568 | int pwc_get_gamma(struct pwc_device *pdev) | |
569 | { | |
570 | char buf; | |
571 | int ret; | |
572 | ||
573 | ret = RecvControlMsg(GET_LUM_CTL, GAMMA_FORMATTER, 1); | |
574 | if (ret < 0) | |
575 | return ret; | |
576 | return buf << 11; | |
577 | } | |
578 | ||
579 | int pwc_set_gamma(struct pwc_device *pdev, int value) | |
580 | { | |
581 | char buf; | |
582 | ||
583 | if (value < 0) | |
584 | value = 0; | |
585 | if (value > 0xffff) | |
586 | value = 0xffff; | |
587 | buf = (value >> 11) & 0x1f; | |
588 | return SendControlMsg(SET_LUM_CTL, GAMMA_FORMATTER, 1); | |
589 | } | |
590 | ||
591 | ||
592 | /* SATURATION */ | |
593 | ||
594 | int pwc_get_saturation(struct pwc_device *pdev) | |
595 | { | |
596 | char buf; | |
597 | int ret; | |
598 | ||
599 | if (pdev->type < 675) | |
600 | return -1; | |
601 | ret = RecvControlMsg(GET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); | |
602 | if (ret < 0) | |
603 | return ret; | |
604 | return 32768 + buf * 327; | |
605 | } | |
606 | ||
607 | int pwc_set_saturation(struct pwc_device *pdev, int value) | |
608 | { | |
609 | char buf; | |
610 | ||
611 | if (pdev->type < 675) | |
612 | return -EINVAL; | |
613 | if (value < 0) | |
614 | value = 0; | |
615 | if (value > 0xffff) | |
616 | value = 0xffff; | |
617 | /* saturation ranges from -100 to +100 */ | |
618 | buf = (value - 32768) / 327; | |
619 | return SendControlMsg(SET_CHROM_CTL, pdev->type < 730 ? SATURATION_MODE_FORMATTER2 : SATURATION_MODE_FORMATTER1, 1); | |
620 | } | |
621 | ||
622 | /* AGC */ | |
623 | ||
624 | static inline int pwc_set_agc(struct pwc_device *pdev, int mode, int value) | |
625 | { | |
626 | char buf; | |
627 | int ret; | |
628 | ||
629 | if (mode) | |
630 | buf = 0x0; /* auto */ | |
631 | else | |
632 | buf = 0xff; /* fixed */ | |
633 | ||
634 | ret = SendControlMsg(SET_LUM_CTL, AGC_MODE_FORMATTER, 1); | |
635 | ||
636 | if (!mode && ret >= 0) { | |
637 | if (value < 0) | |
638 | value = 0; | |
639 | if (value > 0xffff) | |
640 | value = 0xffff; | |
641 | buf = (value >> 10) & 0x3F; | |
642 | ret = SendControlMsg(SET_LUM_CTL, PRESET_AGC_FORMATTER, 1); | |
643 | } | |
644 | if (ret < 0) | |
645 | return ret; | |
646 | return 0; | |
647 | } | |
648 | ||
649 | static inline int pwc_get_agc(struct pwc_device *pdev, int *value) | |
650 | { | |
651 | unsigned char buf; | |
652 | int ret; | |
653 | ||
654 | ret = RecvControlMsg(GET_LUM_CTL, AGC_MODE_FORMATTER, 1); | |
655 | if (ret < 0) | |
656 | return ret; | |
657 | ||
658 | if (buf != 0) { /* fixed */ | |
659 | ret = RecvControlMsg(GET_LUM_CTL, PRESET_AGC_FORMATTER, 1); | |
660 | if (ret < 0) | |
661 | return ret; | |
662 | if (buf > 0x3F) | |
663 | buf = 0x3F; | |
664 | *value = (buf << 10); | |
665 | } | |
666 | else { /* auto */ | |
667 | ret = RecvControlMsg(GET_STATUS_CTL, READ_AGC_FORMATTER, 1); | |
668 | if (ret < 0) | |
669 | return ret; | |
670 | /* Gah... this value ranges from 0x00 ... 0x9F */ | |
671 | if (buf > 0x9F) | |
672 | buf = 0x9F; | |
673 | *value = -(48 + buf * 409); | |
674 | } | |
675 | ||
676 | return 0; | |
677 | } | |
678 | ||
679 | static inline int pwc_set_shutter_speed(struct pwc_device *pdev, int mode, int value) | |
680 | { | |
681 | char buf[2]; | |
682 | int speed, ret; | |
683 | ||
684 | ||
685 | if (mode) | |
686 | buf[0] = 0x0; /* auto */ | |
687 | else | |
688 | buf[0] = 0xff; /* fixed */ | |
689 | ||
690 | ret = SendControlMsg(SET_LUM_CTL, SHUTTER_MODE_FORMATTER, 1); | |
691 | ||
692 | if (!mode && ret >= 0) { | |
693 | if (value < 0) | |
694 | value = 0; | |
695 | if (value > 0xffff) | |
696 | value = 0xffff; | |
697 | switch(pdev->type) { | |
698 | case 675: | |
699 | case 680: | |
700 | case 690: | |
701 | /* speed ranges from 0x0 to 0x290 (656) */ | |
702 | speed = (value / 100); | |
703 | buf[1] = speed >> 8; | |
704 | buf[0] = speed & 0xff; | |
705 | break; | |
706 | case 720: | |
707 | case 730: | |
708 | case 740: | |
709 | case 750: | |
710 | /* speed seems to range from 0x0 to 0xff */ | |
711 | buf[1] = 0; | |
712 | buf[0] = value >> 8; | |
713 | break; | |
714 | } | |
715 | ||
716 | ret = SendControlMsg(SET_LUM_CTL, PRESET_SHUTTER_FORMATTER, 2); | |
717 | } | |
718 | return ret; | |
719 | } | |
720 | ||
721 | ||
722 | /* POWER */ | |
723 | ||
724 | int pwc_camera_power(struct pwc_device *pdev, int power) | |
725 | { | |
726 | char buf; | |
727 | ||
728 | if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) | |
729 | return 0; /* Not supported by Nala or Timon < release 6 */ | |
730 | ||
731 | if (power) | |
732 | buf = 0x00; /* active */ | |
733 | else | |
734 | buf = 0xFF; /* power save */ | |
735 | return SendControlMsg(SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, 1); | |
736 | } | |
737 | ||
738 | ||
739 | ||
740 | /* private calls */ | |
741 | ||
742 | static inline int pwc_restore_user(struct pwc_device *pdev) | |
743 | { | |
744 | char buf; /* dummy */ | |
745 | return SendControlMsg(SET_STATUS_CTL, RESTORE_USER_DEFAULTS_FORMATTER, 0); | |
746 | } | |
747 | ||
748 | static inline int pwc_save_user(struct pwc_device *pdev) | |
749 | { | |
750 | char buf; /* dummy */ | |
751 | return SendControlMsg(SET_STATUS_CTL, SAVE_USER_DEFAULTS_FORMATTER, 0); | |
752 | } | |
753 | ||
754 | static inline int pwc_restore_factory(struct pwc_device *pdev) | |
755 | { | |
756 | char buf; /* dummy */ | |
757 | return SendControlMsg(SET_STATUS_CTL, RESTORE_FACTORY_DEFAULTS_FORMATTER, 0); | |
758 | } | |
759 | ||
760 | /* ************************************************* */ | |
761 | /* Patch by Alvarado: (not in the original version */ | |
762 | ||
763 | /* | |
764 | * the camera recognizes modes from 0 to 4: | |
765 | * | |
766 | * 00: indoor (incandescant lighting) | |
767 | * 01: outdoor (sunlight) | |
768 | * 02: fluorescent lighting | |
769 | * 03: manual | |
770 | * 04: auto | |
771 | */ | |
772 | static inline int pwc_set_awb(struct pwc_device *pdev, int mode) | |
773 | { | |
774 | char buf; | |
775 | int ret; | |
776 | ||
777 | if (mode < 0) | |
778 | mode = 0; | |
779 | ||
780 | if (mode > 4) | |
781 | mode = 4; | |
782 | ||
783 | buf = mode & 0x07; /* just the lowest three bits */ | |
784 | ||
785 | ret = SendControlMsg(SET_CHROM_CTL, WB_MODE_FORMATTER, 1); | |
786 | ||
787 | if (ret < 0) | |
788 | return ret; | |
789 | return 0; | |
790 | } | |
791 | ||
792 | static inline int pwc_get_awb(struct pwc_device *pdev) | |
793 | { | |
794 | unsigned char buf; | |
795 | int ret; | |
796 | ||
797 | ret = RecvControlMsg(GET_CHROM_CTL, WB_MODE_FORMATTER, 1); | |
798 | ||
799 | if (ret < 0) | |
800 | return ret; | |
801 | return buf; | |
802 | } | |
803 | ||
804 | static inline int pwc_set_red_gain(struct pwc_device *pdev, int value) | |
805 | { | |
806 | unsigned char buf; | |
807 | ||
808 | if (value < 0) | |
809 | value = 0; | |
810 | if (value > 0xffff) | |
811 | value = 0xffff; | |
812 | /* only the msb is considered */ | |
813 | buf = value >> 8; | |
814 | return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); | |
815 | } | |
816 | ||
817 | static inline int pwc_get_red_gain(struct pwc_device *pdev, int *value) | |
818 | { | |
819 | unsigned char buf; | |
820 | int ret; | |
821 | ||
822 | ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_RED_GAIN_FORMATTER, 1); | |
823 | if (ret < 0) | |
824 | return ret; | |
825 | *value = buf << 8; | |
826 | return 0; | |
827 | } | |
828 | ||
829 | ||
830 | static inline int pwc_set_blue_gain(struct pwc_device *pdev, int value) | |
831 | { | |
832 | unsigned char buf; | |
833 | ||
834 | if (value < 0) | |
835 | value = 0; | |
836 | if (value > 0xffff) | |
837 | value = 0xffff; | |
838 | /* only the msb is considered */ | |
839 | buf = value >> 8; | |
840 | return SendControlMsg(SET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); | |
841 | } | |
842 | ||
843 | static inline int pwc_get_blue_gain(struct pwc_device *pdev, int *value) | |
844 | { | |
845 | unsigned char buf; | |
846 | int ret; | |
847 | ||
848 | ret = RecvControlMsg(GET_CHROM_CTL, PRESET_MANUAL_BLUE_GAIN_FORMATTER, 1); | |
849 | if (ret < 0) | |
850 | return ret; | |
851 | *value = buf << 8; | |
852 | return 0; | |
853 | } | |
854 | ||
855 | ||
856 | /* The following two functions are different, since they only read the | |
857 | internal red/blue gains, which may be different from the manual | |
858 | gains set or read above. | |
859 | */ | |
860 | static inline int pwc_read_red_gain(struct pwc_device *pdev, int *value) | |
861 | { | |
862 | unsigned char buf; | |
863 | int ret; | |
864 | ||
865 | ret = RecvControlMsg(GET_STATUS_CTL, READ_RED_GAIN_FORMATTER, 1); | |
866 | if (ret < 0) | |
867 | return ret; | |
868 | *value = buf << 8; | |
869 | return 0; | |
870 | } | |
871 | ||
872 | static inline int pwc_read_blue_gain(struct pwc_device *pdev, int *value) | |
873 | { | |
874 | unsigned char buf; | |
875 | int ret; | |
876 | ||
877 | ret = RecvControlMsg(GET_STATUS_CTL, READ_BLUE_GAIN_FORMATTER, 1); | |
878 | if (ret < 0) | |
879 | return ret; | |
880 | *value = buf << 8; | |
881 | return 0; | |
882 | } | |
883 | ||
884 | ||
885 | static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed) | |
886 | { | |
887 | unsigned char buf; | |
888 | ||
889 | /* useful range is 0x01..0x20 */ | |
890 | buf = speed / 0x7f0; | |
891 | return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); | |
892 | } | |
893 | ||
894 | static inline int pwc_get_wb_speed(struct pwc_device *pdev, int *value) | |
895 | { | |
896 | unsigned char buf; | |
897 | int ret; | |
898 | ||
899 | ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, 1); | |
900 | if (ret < 0) | |
901 | return ret; | |
902 | *value = buf * 0x7f0; | |
903 | return 0; | |
904 | } | |
905 | ||
906 | ||
907 | static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay) | |
908 | { | |
909 | unsigned char buf; | |
910 | ||
911 | /* useful range is 0x01..0x3F */ | |
912 | buf = (delay >> 10); | |
913 | return SendControlMsg(SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); | |
914 | } | |
915 | ||
916 | static inline int pwc_get_wb_delay(struct pwc_device *pdev, int *value) | |
917 | { | |
918 | unsigned char buf; | |
919 | int ret; | |
920 | ||
921 | ret = RecvControlMsg(GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, 1); | |
922 | if (ret < 0) | |
923 | return ret; | |
924 | *value = buf << 10; | |
925 | return 0; | |
926 | } | |
927 | ||
928 | ||
929 | int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) | |
930 | { | |
931 | unsigned char buf[2]; | |
932 | ||
933 | if (pdev->type < 730) | |
934 | return 0; | |
935 | on_value /= 100; | |
936 | off_value /= 100; | |
937 | if (on_value < 0) | |
938 | on_value = 0; | |
939 | if (on_value > 0xff) | |
940 | on_value = 0xff; | |
941 | if (off_value < 0) | |
942 | off_value = 0; | |
943 | if (off_value > 0xff) | |
944 | off_value = 0xff; | |
945 | ||
946 | buf[0] = on_value; | |
947 | buf[1] = off_value; | |
948 | ||
949 | return SendControlMsg(SET_STATUS_CTL, LED_FORMATTER, 2); | |
950 | } | |
951 | ||
2c47e7f3 | 952 | static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) |
1da177e4 LT |
953 | { |
954 | unsigned char buf[2]; | |
955 | int ret; | |
956 | ||
957 | if (pdev->type < 730) { | |
958 | *on_value = -1; | |
959 | *off_value = -1; | |
960 | return 0; | |
961 | } | |
962 | ||
963 | ret = RecvControlMsg(GET_STATUS_CTL, LED_FORMATTER, 2); | |
964 | if (ret < 0) | |
965 | return ret; | |
966 | *on_value = buf[0] * 100; | |
967 | *off_value = buf[1] * 100; | |
968 | return 0; | |
969 | } | |
970 | ||
971 | static inline int pwc_set_contour(struct pwc_device *pdev, int contour) | |
972 | { | |
973 | unsigned char buf; | |
974 | int ret; | |
975 | ||
976 | if (contour < 0) | |
977 | buf = 0xff; /* auto contour on */ | |
978 | else | |
979 | buf = 0x0; /* auto contour off */ | |
980 | ret = SendControlMsg(SET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); | |
981 | if (ret < 0) | |
982 | return ret; | |
983 | ||
984 | if (contour < 0) | |
985 | return 0; | |
986 | if (contour > 0xffff) | |
987 | contour = 0xffff; | |
988 | ||
989 | buf = (contour >> 10); /* contour preset is [0..3f] */ | |
990 | ret = SendControlMsg(SET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); | |
991 | if (ret < 0) | |
992 | return ret; | |
993 | return 0; | |
994 | } | |
995 | ||
996 | static inline int pwc_get_contour(struct pwc_device *pdev, int *contour) | |
997 | { | |
998 | unsigned char buf; | |
999 | int ret; | |
1000 | ||
1001 | ret = RecvControlMsg(GET_LUM_CTL, AUTO_CONTOUR_FORMATTER, 1); | |
1002 | if (ret < 0) | |
1003 | return ret; | |
1004 | ||
1005 | if (buf == 0) { | |
1006 | /* auto mode off, query current preset value */ | |
1007 | ret = RecvControlMsg(GET_LUM_CTL, PRESET_CONTOUR_FORMATTER, 1); | |
1008 | if (ret < 0) | |
1009 | return ret; | |
1010 | *contour = buf << 10; | |
1011 | } | |
1012 | else | |
1013 | *contour = -1; | |
1014 | return 0; | |
1015 | } | |
1016 | ||
1017 | ||
1018 | static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight) | |
1019 | { | |
1020 | unsigned char buf; | |
1021 | ||
1022 | if (backlight) | |
1023 | buf = 0xff; | |
1024 | else | |
1025 | buf = 0x0; | |
1026 | return SendControlMsg(SET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); | |
1027 | } | |
1028 | ||
1029 | static inline int pwc_get_backlight(struct pwc_device *pdev, int *backlight) | |
1030 | { | |
1031 | int ret; | |
1032 | unsigned char buf; | |
1033 | ||
1034 | ret = RecvControlMsg(GET_LUM_CTL, BACK_LIGHT_COMPENSATION_FORMATTER, 1); | |
1035 | if (ret < 0) | |
1036 | return ret; | |
1037 | *backlight = buf; | |
1038 | return 0; | |
1039 | } | |
1040 | ||
1041 | ||
1042 | static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker) | |
1043 | { | |
1044 | unsigned char buf; | |
1045 | ||
1046 | if (flicker) | |
1047 | buf = 0xff; | |
1048 | else | |
1049 | buf = 0x0; | |
1050 | return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); | |
1051 | } | |
1052 | ||
1053 | static inline int pwc_get_flicker(struct pwc_device *pdev, int *flicker) | |
1054 | { | |
1055 | int ret; | |
1056 | unsigned char buf; | |
1057 | ||
1058 | ret = RecvControlMsg(GET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1); | |
1059 | if (ret < 0) | |
1060 | return ret; | |
1061 | *flicker = buf; | |
1062 | return 0; | |
1063 | } | |
1064 | ||
1065 | ||
1066 | static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise) | |
1067 | { | |
1068 | unsigned char buf; | |
1069 | ||
1070 | if (noise < 0) | |
1071 | noise = 0; | |
1072 | if (noise > 3) | |
1073 | noise = 3; | |
1074 | buf = noise; | |
1075 | return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); | |
1076 | } | |
1077 | ||
1078 | static inline int pwc_get_dynamic_noise(struct pwc_device *pdev, int *noise) | |
1079 | { | |
1080 | int ret; | |
1081 | unsigned char buf; | |
1082 | ||
1083 | ret = RecvControlMsg(GET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1); | |
1084 | if (ret < 0) | |
1085 | return ret; | |
1086 | *noise = buf; | |
1087 | return 0; | |
1088 | } | |
1089 | ||
1090 | static int pwc_mpt_reset(struct pwc_device *pdev, int flags) | |
1091 | { | |
1092 | unsigned char buf; | |
1093 | ||
1094 | buf = flags & 0x03; // only lower two bits are currently used | |
1095 | return SendControlMsg(SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, 1); | |
1096 | } | |
1097 | ||
1098 | static inline int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) | |
1099 | { | |
1100 | unsigned char buf[4]; | |
1101 | ||
1102 | /* set new relative angle; angles are expressed in degrees * 100, | |
093cf723 | 1103 | but cam as .5 degree resolution, hence divide by 200. Also |
1da177e4 LT |
1104 | the angle must be multiplied by 64 before it's send to |
1105 | the cam (??) | |
1106 | */ | |
1107 | pan = 64 * pan / 100; | |
1108 | tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ | |
1109 | buf[0] = pan & 0xFF; | |
1110 | buf[1] = (pan >> 8) & 0xFF; | |
1111 | buf[2] = tilt & 0xFF; | |
1112 | buf[3] = (tilt >> 8) & 0xFF; | |
1113 | return SendControlMsg(SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, 4); | |
1114 | } | |
1115 | ||
1116 | static inline int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) | |
1117 | { | |
1118 | int ret; | |
1119 | unsigned char buf[5]; | |
1120 | ||
1121 | ret = RecvControlMsg(GET_MPT_CTL, PT_STATUS_FORMATTER, 5); | |
1122 | if (ret < 0) | |
1123 | return ret; | |
1124 | status->status = buf[0] & 0x7; // 3 bits are used for reporting | |
1125 | status->time_pan = (buf[1] << 8) + buf[2]; | |
1126 | status->time_tilt = (buf[3] << 8) + buf[4]; | |
1127 | return 0; | |
1128 | } | |
1129 | ||
1130 | ||
1131 | int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) | |
1132 | { | |
1133 | unsigned char buf; | |
1134 | int ret = -1, request; | |
1135 | ||
1136 | if (pdev->type < 675) | |
1137 | request = SENSOR_TYPE_FORMATTER1; | |
1138 | else if (pdev->type < 730) | |
1139 | return -1; /* The Vesta series doesn't have this call */ | |
1140 | else | |
1141 | request = SENSOR_TYPE_FORMATTER2; | |
1142 | ||
1143 | ret = RecvControlMsg(GET_STATUS_CTL, request, 1); | |
1144 | if (ret < 0) | |
1145 | return ret; | |
1146 | if (pdev->type < 675) | |
1147 | *sensor = buf | 0x100; | |
1148 | else | |
1149 | *sensor = buf; | |
1150 | return 0; | |
1151 | } | |
1152 | ||
1153 | ||
1154 | /* End of Add-Ons */ | |
1155 | /* ************************************************* */ | |
1156 | ||
1157 | /* Linux 2.5.something and 2.6 pass direct pointers to arguments of | |
1158 | ioctl() calls. With 2.4, you have to do tedious copy_from_user() | |
1159 | and copy_to_user() calls. With these macros we circumvent this, | |
1160 | and let me maintain only one source file. The functionality is | |
1161 | exactly the same otherwise. | |
1162 | */ | |
1163 | ||
1164 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 0) | |
1165 | ||
1166 | /* define local variable for arg */ | |
1167 | #define ARG_DEF(ARG_type, ARG_name)\ | |
1168 | ARG_type *ARG_name = arg; | |
1169 | /* copy arg to local variable */ | |
1170 | #define ARG_IN(ARG_name) /* nothing */ | |
1171 | /* argument itself (referenced) */ | |
1172 | #define ARGR(ARG_name) (*ARG_name) | |
1173 | /* argument address */ | |
1174 | #define ARGA(ARG_name) ARG_name | |
1175 | /* copy local variable to arg */ | |
1176 | #define ARG_OUT(ARG_name) /* nothing */ | |
1177 | ||
1178 | #else | |
1179 | ||
1180 | #define ARG_DEF(ARG_type, ARG_name)\ | |
1181 | ARG_type ARG_name; | |
1182 | #define ARG_IN(ARG_name)\ | |
1183 | if (copy_from_user(&ARG_name, arg, sizeof(ARG_name))) {\ | |
1184 | ret = -EFAULT;\ | |
1185 | break;\ | |
1186 | } | |
1187 | #define ARGR(ARG_name) ARG_name | |
1188 | #define ARGA(ARG_name) &ARG_name | |
1189 | #define ARG_OUT(ARG_name)\ | |
1190 | if (copy_to_user(arg, &ARG_name, sizeof(ARG_name))) {\ | |
1191 | ret = -EFAULT;\ | |
1192 | break;\ | |
1193 | } | |
1194 | ||
1195 | #endif | |
1196 | ||
1197 | int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) | |
1198 | { | |
1199 | int ret = 0; | |
1200 | ||
1201 | switch(cmd) { | |
1202 | case VIDIOCPWCRUSER: | |
1203 | { | |
1204 | if (pwc_restore_user(pdev)) | |
1205 | ret = -EINVAL; | |
1206 | break; | |
1207 | } | |
1208 | ||
1209 | case VIDIOCPWCSUSER: | |
1210 | { | |
1211 | if (pwc_save_user(pdev)) | |
1212 | ret = -EINVAL; | |
1213 | break; | |
1214 | } | |
1215 | ||
1216 | case VIDIOCPWCFACTORY: | |
1217 | { | |
1218 | if (pwc_restore_factory(pdev)) | |
1219 | ret = -EINVAL; | |
1220 | break; | |
1221 | } | |
1222 | ||
1223 | case VIDIOCPWCSCQUAL: | |
1224 | { | |
1225 | ARG_DEF(int, qual) | |
1226 | ||
1227 | ARG_IN(qual) | |
1228 | if (ARGR(qual) < 0 || ARGR(qual) > 3) | |
1229 | ret = -EINVAL; | |
1230 | else | |
1231 | ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); | |
1232 | if (ret >= 0) | |
1233 | pdev->vcompression = ARGR(qual); | |
1234 | break; | |
1235 | } | |
1236 | ||
1237 | case VIDIOCPWCGCQUAL: | |
1238 | { | |
1239 | ARG_DEF(int, qual) | |
1240 | ||
1241 | ARGR(qual) = pdev->vcompression; | |
1242 | ARG_OUT(qual) | |
1243 | break; | |
1244 | } | |
1245 | ||
1246 | case VIDIOCPWCPROBE: | |
1247 | { | |
1248 | ARG_DEF(struct pwc_probe, probe) | |
1249 | ||
1250 | strcpy(ARGR(probe).name, pdev->vdev->name); | |
1251 | ARGR(probe).type = pdev->type; | |
1252 | ARG_OUT(probe) | |
1253 | break; | |
1254 | } | |
1255 | ||
1256 | case VIDIOCPWCGSERIAL: | |
1257 | { | |
1258 | ARG_DEF(struct pwc_serial, serial) | |
1259 | ||
1260 | strcpy(ARGR(serial).serial, pdev->serial); | |
1261 | ARG_OUT(serial) | |
1262 | break; | |
1263 | } | |
1264 | ||
1265 | case VIDIOCPWCSAGC: | |
1266 | { | |
1267 | ARG_DEF(int, agc) | |
1268 | ||
1269 | ARG_IN(agc) | |
1270 | if (pwc_set_agc(pdev, ARGR(agc) < 0 ? 1 : 0, ARGR(agc))) | |
1271 | ret = -EINVAL; | |
1272 | break; | |
1273 | } | |
1274 | ||
1275 | case VIDIOCPWCGAGC: | |
1276 | { | |
1277 | ARG_DEF(int, agc) | |
1278 | ||
1279 | if (pwc_get_agc(pdev, ARGA(agc))) | |
1280 | ret = -EINVAL; | |
1281 | ARG_OUT(agc) | |
1282 | break; | |
1283 | } | |
1284 | ||
1285 | case VIDIOCPWCSSHUTTER: | |
1286 | { | |
1287 | ARG_DEF(int, shutter_speed) | |
1288 | ||
1289 | ARG_IN(shutter_speed) | |
1290 | ret = pwc_set_shutter_speed(pdev, ARGR(shutter_speed) < 0 ? 1 : 0, ARGR(shutter_speed)); | |
1291 | break; | |
1292 | } | |
1293 | ||
1294 | case VIDIOCPWCSAWB: | |
1295 | { | |
1296 | ARG_DEF(struct pwc_whitebalance, wb) | |
1297 | ||
1298 | ARG_IN(wb) | |
1299 | ret = pwc_set_awb(pdev, ARGR(wb).mode); | |
1300 | if (ret >= 0 && ARGR(wb).mode == PWC_WB_MANUAL) { | |
1301 | pwc_set_red_gain(pdev, ARGR(wb).manual_red); | |
1302 | pwc_set_blue_gain(pdev, ARGR(wb).manual_blue); | |
1303 | } | |
1304 | break; | |
1305 | } | |
1306 | ||
1307 | case VIDIOCPWCGAWB: | |
1308 | { | |
1309 | ARG_DEF(struct pwc_whitebalance, wb) | |
1310 | ||
1311 | memset(ARGA(wb), 0, sizeof(struct pwc_whitebalance)); | |
1312 | ARGR(wb).mode = pwc_get_awb(pdev); | |
1313 | if (ARGR(wb).mode < 0) | |
1314 | ret = -EINVAL; | |
1315 | else { | |
1316 | if (ARGR(wb).mode == PWC_WB_MANUAL) { | |
1317 | ret = pwc_get_red_gain(pdev, &ARGR(wb).manual_red); | |
1318 | if (ret < 0) | |
1319 | break; | |
1320 | ret = pwc_get_blue_gain(pdev, &ARGR(wb).manual_blue); | |
1321 | if (ret < 0) | |
1322 | break; | |
1323 | } | |
1324 | if (ARGR(wb).mode == PWC_WB_AUTO) { | |
1325 | ret = pwc_read_red_gain(pdev, &ARGR(wb).read_red); | |
1326 | if (ret < 0) | |
1327 | break; | |
1328 | ret =pwc_read_blue_gain(pdev, &ARGR(wb).read_blue); | |
1329 | if (ret < 0) | |
1330 | break; | |
1331 | } | |
1332 | } | |
1333 | ARG_OUT(wb) | |
1334 | break; | |
1335 | } | |
1336 | ||
1337 | case VIDIOCPWCSAWBSPEED: | |
1338 | { | |
1339 | ARG_DEF(struct pwc_wb_speed, wbs) | |
1340 | ||
1341 | if (ARGR(wbs).control_speed > 0) { | |
1342 | ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); | |
1343 | } | |
1344 | if (ARGR(wbs).control_delay > 0) { | |
1345 | ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); | |
1346 | } | |
1347 | break; | |
1348 | } | |
1349 | ||
1350 | case VIDIOCPWCGAWBSPEED: | |
1351 | { | |
1352 | ARG_DEF(struct pwc_wb_speed, wbs) | |
1353 | ||
1354 | ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); | |
1355 | if (ret < 0) | |
1356 | break; | |
1357 | ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); | |
1358 | if (ret < 0) | |
1359 | break; | |
1360 | ARG_OUT(wbs) | |
1361 | break; | |
1362 | } | |
1363 | ||
1364 | case VIDIOCPWCSLED: | |
1365 | { | |
1366 | ARG_DEF(struct pwc_leds, leds) | |
1367 | ||
1368 | ARG_IN(leds) | |
1369 | ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); | |
1370 | break; | |
1371 | } | |
1372 | ||
1373 | ||
1374 | case VIDIOCPWCGLED: | |
1375 | { | |
1376 | ARG_DEF(struct pwc_leds, leds) | |
1377 | ||
1378 | ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); | |
1379 | ARG_OUT(leds) | |
1380 | break; | |
1381 | } | |
1382 | ||
1383 | case VIDIOCPWCSCONTOUR: | |
1384 | { | |
1385 | ARG_DEF(int, contour) | |
1386 | ||
1387 | ARG_IN(contour) | |
1388 | ret = pwc_set_contour(pdev, ARGR(contour)); | |
1389 | break; | |
1390 | } | |
1391 | ||
1392 | case VIDIOCPWCGCONTOUR: | |
1393 | { | |
1394 | ARG_DEF(int, contour) | |
1395 | ||
1396 | ret = pwc_get_contour(pdev, ARGA(contour)); | |
1397 | ARG_OUT(contour) | |
1398 | break; | |
1399 | } | |
1400 | ||
1401 | case VIDIOCPWCSBACKLIGHT: | |
1402 | { | |
1403 | ARG_DEF(int, backlight) | |
1404 | ||
1405 | ARG_IN(backlight) | |
1406 | ret = pwc_set_backlight(pdev, ARGR(backlight)); | |
1407 | break; | |
1408 | } | |
1409 | ||
1410 | case VIDIOCPWCGBACKLIGHT: | |
1411 | { | |
1412 | ARG_DEF(int, backlight) | |
1413 | ||
1414 | ret = pwc_get_backlight(pdev, ARGA(backlight)); | |
1415 | ARG_OUT(backlight) | |
1416 | break; | |
1417 | } | |
1418 | ||
1419 | case VIDIOCPWCSFLICKER: | |
1420 | { | |
1421 | ARG_DEF(int, flicker) | |
1422 | ||
1423 | ARG_IN(flicker) | |
1424 | ret = pwc_set_flicker(pdev, ARGR(flicker)); | |
1425 | break; | |
1426 | } | |
1427 | ||
1428 | case VIDIOCPWCGFLICKER: | |
1429 | { | |
1430 | ARG_DEF(int, flicker) | |
1431 | ||
1432 | ret = pwc_get_flicker(pdev, ARGA(flicker)); | |
1433 | ARG_OUT(flicker) | |
1434 | break; | |
1435 | } | |
1436 | ||
1437 | case VIDIOCPWCSDYNNOISE: | |
1438 | { | |
1439 | ARG_DEF(int, dynnoise) | |
1440 | ||
1441 | ARG_IN(dynnoise) | |
1442 | ret = pwc_set_dynamic_noise(pdev, ARGR(dynnoise)); | |
1443 | break; | |
1444 | } | |
1445 | ||
1446 | case VIDIOCPWCGDYNNOISE: | |
1447 | { | |
1448 | ARG_DEF(int, dynnoise) | |
1449 | ||
1450 | ret = pwc_get_dynamic_noise(pdev, ARGA(dynnoise)); | |
1451 | ARG_OUT(dynnoise); | |
1452 | break; | |
1453 | } | |
1454 | ||
1455 | case VIDIOCPWCGREALSIZE: | |
1456 | { | |
1457 | ARG_DEF(struct pwc_imagesize, size) | |
1458 | ||
1459 | ARGR(size).width = pdev->image.x; | |
1460 | ARGR(size).height = pdev->image.y; | |
1461 | ARG_OUT(size) | |
1462 | break; | |
1463 | } | |
1464 | ||
1465 | case VIDIOCPWCMPTRESET: | |
1466 | { | |
1467 | if (pdev->features & FEATURE_MOTOR_PANTILT) | |
1468 | { | |
1469 | ARG_DEF(int, flags) | |
1470 | ||
1471 | ARG_IN(flags) | |
1472 | ret = pwc_mpt_reset(pdev, ARGR(flags)); | |
1473 | if (ret >= 0) | |
1474 | { | |
1475 | pdev->pan_angle = 0; | |
1476 | pdev->tilt_angle = 0; | |
1477 | } | |
1478 | } | |
1479 | else | |
1480 | { | |
1481 | ret = -ENXIO; | |
1482 | } | |
1483 | break; | |
1484 | } | |
1485 | ||
1486 | case VIDIOCPWCMPTGRANGE: | |
1487 | { | |
1488 | if (pdev->features & FEATURE_MOTOR_PANTILT) | |
1489 | { | |
1490 | ARG_DEF(struct pwc_mpt_range, range) | |
1491 | ||
1492 | ARGR(range) = pdev->angle_range; | |
1493 | ARG_OUT(range) | |
1494 | } | |
1495 | else | |
1496 | { | |
1497 | ret = -ENXIO; | |
1498 | } | |
1499 | break; | |
1500 | } | |
1501 | ||
1502 | case VIDIOCPWCMPTSANGLE: | |
1503 | { | |
1504 | int new_pan, new_tilt; | |
1505 | ||
1506 | if (pdev->features & FEATURE_MOTOR_PANTILT) | |
1507 | { | |
1508 | ARG_DEF(struct pwc_mpt_angles, angles) | |
1509 | ||
1510 | ARG_IN(angles) | |
1511 | /* The camera can only set relative angles, so | |
1512 | do some calculations when getting an absolute angle . | |
1513 | */ | |
1514 | if (ARGR(angles).absolute) | |
1515 | { | |
1516 | new_pan = ARGR(angles).pan; | |
1517 | new_tilt = ARGR(angles).tilt; | |
1518 | } | |
1519 | else | |
1520 | { | |
1521 | new_pan = pdev->pan_angle + ARGR(angles).pan; | |
1522 | new_tilt = pdev->tilt_angle + ARGR(angles).tilt; | |
1523 | } | |
1524 | /* check absolute ranges */ | |
1525 | if (new_pan < pdev->angle_range.pan_min || | |
1526 | new_pan > pdev->angle_range.pan_max || | |
1527 | new_tilt < pdev->angle_range.tilt_min || | |
1528 | new_tilt > pdev->angle_range.tilt_max) | |
1529 | { | |
1530 | ret = -ERANGE; | |
1531 | } | |
1532 | else | |
1533 | { | |
1534 | /* go to relative range, check again */ | |
1535 | new_pan -= pdev->pan_angle; | |
1536 | new_tilt -= pdev->tilt_angle; | |
1537 | /* angles are specified in degrees * 100, thus the limit = 36000 */ | |
1538 | if (new_pan < -36000 || new_pan > 36000 || new_tilt < -36000 || new_tilt > 36000) | |
1539 | ret = -ERANGE; | |
1540 | } | |
1541 | if (ret == 0) /* no errors so far */ | |
1542 | { | |
1543 | ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); | |
1544 | if (ret >= 0) | |
1545 | { | |
1546 | pdev->pan_angle += new_pan; | |
1547 | pdev->tilt_angle += new_tilt; | |
1548 | } | |
1549 | if (ret == -EPIPE) /* stall -> out of range */ | |
1550 | ret = -ERANGE; | |
1551 | } | |
1552 | } | |
1553 | else | |
1554 | { | |
1555 | ret = -ENXIO; | |
1556 | } | |
1557 | break; | |
1558 | } | |
1559 | ||
1560 | case VIDIOCPWCMPTGANGLE: | |
1561 | { | |
1562 | ||
1563 | if (pdev->features & FEATURE_MOTOR_PANTILT) | |
1564 | { | |
1565 | ARG_DEF(struct pwc_mpt_angles, angles) | |
1566 | ||
1567 | ARGR(angles).absolute = 1; | |
1568 | ARGR(angles).pan = pdev->pan_angle; | |
1569 | ARGR(angles).tilt = pdev->tilt_angle; | |
1570 | ARG_OUT(angles) | |
1571 | } | |
1572 | else | |
1573 | { | |
1574 | ret = -ENXIO; | |
1575 | } | |
1576 | break; | |
1577 | } | |
1578 | ||
1579 | case VIDIOCPWCMPTSTATUS: | |
1580 | { | |
1581 | if (pdev->features & FEATURE_MOTOR_PANTILT) | |
1582 | { | |
1583 | ARG_DEF(struct pwc_mpt_status, status) | |
1584 | ||
1585 | ret = pwc_mpt_get_status(pdev, ARGA(status)); | |
1586 | ARG_OUT(status) | |
1587 | } | |
1588 | else | |
1589 | { | |
1590 | ret = -ENXIO; | |
1591 | } | |
1592 | break; | |
1593 | } | |
1594 | ||
1595 | case VIDIOCPWCGVIDCMD: | |
1596 | { | |
1597 | ARG_DEF(struct pwc_video_command, cmd); | |
1598 | ||
1599 | ARGR(cmd).type = pdev->type; | |
1600 | ARGR(cmd).release = pdev->release; | |
1601 | ARGR(cmd).command_len = pdev->cmd_len; | |
1602 | memcpy(&ARGR(cmd).command_buf, pdev->cmd_buf, pdev->cmd_len); | |
1603 | ARGR(cmd).bandlength = pdev->vbandlength; | |
1604 | ARGR(cmd).frame_size = pdev->frame_size; | |
1605 | ARG_OUT(cmd) | |
1606 | break; | |
1607 | } | |
1608 | /* | |
1609 | case VIDIOCPWCGVIDTABLE: | |
1610 | { | |
1611 | ARG_DEF(struct pwc_table_init_buffer, table); | |
1612 | ARGR(table).len = pdev->cmd_len; | |
1613 | memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); | |
1614 | ARG_OUT(table) | |
1615 | break; | |
1616 | } | |
1617 | */ | |
1618 | ||
1619 | default: | |
1620 | ret = -ENOIOCTLCMD; | |
1621 | break; | |
1622 | } | |
1623 | ||
1624 | if (ret > 0) | |
1625 | return 0; | |
1626 | return ret; | |
1627 | } | |
1628 | ||
1629 | ||
1630 |