diff options
Diffstat (limited to 'drivers/usb/media/pwc/pwc-ctrl.c')
-rw-r--r-- | drivers/usb/media/pwc/pwc-ctrl.c | 1630 |
1 files changed, 1630 insertions, 0 deletions
diff --git a/drivers/usb/media/pwc/pwc-ctrl.c b/drivers/usb/media/pwc/pwc-ctrl.c new file mode 100644 index 000000000000..26aa914bc541 --- /dev/null +++ b/drivers/usb/media/pwc/pwc-ctrl.c | |||
@@ -0,0 +1,1630 @@ | |||
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 | |||
421 | /** | ||
422 | @pdev: device structure | ||
423 | @width: viewport width | ||
424 | @height: viewport height | ||
425 | @frame: framerate, in fps | ||
426 | @compression: preferred compression ratio | ||
427 | @snapshot: snapshot mode or streaming | ||
428 | */ | ||
429 | int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) | ||
430 | { | ||
431 | int ret, size; | ||
432 | |||
433 | Trace(TRACE_FLOW, "set_video_mode(%dx%d @ %d, palette %d).\n", width, height, frames, pdev->vpalette); | ||
434 | size = pwc_decode_size(pdev, width, height); | ||
435 | if (size < 0) { | ||
436 | Debug("Could not find suitable size.\n"); | ||
437 | return -ERANGE; | ||
438 | } | ||
439 | Debug("decode_size = %d.\n", size); | ||
440 | |||
441 | ret = -EINVAL; | ||
442 | switch(pdev->type) { | ||
443 | case 645: | ||
444 | case 646: | ||
445 | ret = set_video_mode_Nala(pdev, size, frames); | ||
446 | break; | ||
447 | |||
448 | case 675: | ||
449 | case 680: | ||
450 | case 690: | ||
451 | ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); | ||
452 | break; | ||
453 | |||
454 | case 720: | ||
455 | case 730: | ||
456 | case 740: | ||
457 | case 750: | ||
458 | ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); | ||
459 | break; | ||
460 | } | ||
461 | if (ret < 0) { | ||
462 | if (ret == -ENOENT) | ||
463 | Info("Video mode %s@%d fps is only supported with the decompressor module (pwcx).\n", size2name[size], frames); | ||
464 | else { | ||
465 | Err("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); | ||
466 | } | ||
467 | return ret; | ||
468 | } | ||
469 | pdev->view.x = width; | ||
470 | pdev->view.y = height; | ||
471 | pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; | ||
472 | pwc_set_image_buffer_size(pdev); | ||
473 | 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); | ||
474 | return 0; | ||
475 | } | ||
476 | |||
477 | |||
478 | void pwc_set_image_buffer_size(struct pwc_device *pdev) | ||
479 | { | ||
480 | int i, factor = 0, filler = 0; | ||
481 | |||
482 | /* for PALETTE_YUV420P */ | ||
483 | switch(pdev->vpalette) | ||
484 | { | ||
485 | case VIDEO_PALETTE_YUV420P: | ||
486 | factor = 6; | ||
487 | filler = 128; | ||
488 | break; | ||
489 | case VIDEO_PALETTE_RAW: | ||
490 | factor = 6; /* can be uncompressed YUV420P */ | ||
491 | filler = 0; | ||
492 | break; | ||
493 | } | ||
494 | |||
495 | /* Set sizes in bytes */ | ||
496 | pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; | ||
497 | pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; | ||
498 | |||
499 | /* Align offset, or you'll get some very weird results in | ||
500 | YUV420 mode... x must be multiple of 4 (to get the Y's in | ||
501 | place), and y even (or you'll mixup U & V). This is less of a | ||
502 | problem for YUV420P. | ||
503 | */ | ||
504 | pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; | ||
505 | pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; | ||
506 | |||
507 | /* Fill buffers with gray or black */ | ||
508 | for (i = 0; i < MAX_IMAGES; i++) { | ||
509 | if (pdev->image_ptr[i] != NULL) | ||
510 | memset(pdev->image_ptr[i], filler, pdev->view.size); | ||
511 | } | ||
512 | } | ||
513 | |||
514 | |||
515 | |||
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 | |||
952 | int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) | ||
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, | ||
1103 | but cam as .5 degree resolution, hence devide by 200. Also | ||
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 | |||