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