diff options
Diffstat (limited to 'drivers/media/video/pwc/pwc-ctrl.c')
-rw-r--r-- | drivers/media/video/pwc/pwc-ctrl.c | 1220 |
1 files changed, 1220 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 00000000000..3977addf3ba --- /dev/null +++ b/drivers/media/video/pwc/pwc-ctrl.c | |||
@@ -0,0 +1,1220 @@ | |||
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-2006 Luc Saillard (luc@saillard.org) | ||
6 | (C) 2011 Hans de Goede <hdegoede@redhat.com> | ||
7 | |||
8 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx | ||
9 | driver and thus may have bugs that are not present in the original version. | ||
10 | Please send bug reports and support requests to <luc@saillard.org>. | ||
11 | |||
12 | NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx | ||
13 | driver and thus may have bugs that are not present in the original version. | ||
14 | Please send bug reports and support requests to <luc@saillard.org>. | ||
15 | The decompression routines have been implemented by reverse-engineering the | ||
16 | Nemosoft binary pwcx module. Caveat emptor. | ||
17 | |||
18 | This program is free software; you can redistribute it and/or modify | ||
19 | it under the terms of the GNU General Public License as published by | ||
20 | the Free Software Foundation; either version 2 of the License, or | ||
21 | (at your option) any later version. | ||
22 | |||
23 | This program is distributed in the hope that it will be useful, | ||
24 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
25 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
26 | GNU General Public License for more details. | ||
27 | |||
28 | You should have received a copy of the GNU General Public License | ||
29 | along with this program; if not, write to the Free Software | ||
30 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
31 | */ | ||
32 | |||
33 | /* | ||
34 | Changes | ||
35 | 2001/08/03 Alvarado Added methods for changing white balance and | ||
36 | red/green gains | ||
37 | */ | ||
38 | |||
39 | /* Control functions for the cam; brightness, contrast, video mode, etc. */ | ||
40 | |||
41 | #ifdef __KERNEL__ | ||
42 | #include <asm/uaccess.h> | ||
43 | #endif | ||
44 | #include <asm/errno.h> | ||
45 | |||
46 | #include "pwc.h" | ||
47 | #include "pwc-kiara.h" | ||
48 | #include "pwc-timon.h" | ||
49 | #include "pwc-dec1.h" | ||
50 | #include "pwc-dec23.h" | ||
51 | |||
52 | /* Selectors for status controls used only in this file */ | ||
53 | #define GET_STATUS_B00 0x0B00 | ||
54 | #define SENSOR_TYPE_FORMATTER1 0x0C00 | ||
55 | #define GET_STATUS_3000 0x3000 | ||
56 | #define READ_RAW_Y_MEAN_FORMATTER 0x3100 | ||
57 | #define SET_POWER_SAVE_MODE_FORMATTER 0x3200 | ||
58 | #define MIRROR_IMAGE_FORMATTER 0x3300 | ||
59 | #define LED_FORMATTER 0x3400 | ||
60 | #define LOWLIGHT 0x3500 | ||
61 | #define GET_STATUS_3600 0x3600 | ||
62 | #define SENSOR_TYPE_FORMATTER2 0x3700 | ||
63 | #define GET_STATUS_3800 0x3800 | ||
64 | #define GET_STATUS_4000 0x4000 | ||
65 | #define GET_STATUS_4100 0x4100 /* Get */ | ||
66 | #define CTL_STATUS_4200 0x4200 /* [GS] 1 */ | ||
67 | |||
68 | /* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */ | ||
69 | #define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100 | ||
70 | |||
71 | static const char *size2name[PSZ_MAX] = | ||
72 | { | ||
73 | "subQCIF", | ||
74 | "QSIF", | ||
75 | "QCIF", | ||
76 | "SIF", | ||
77 | "CIF", | ||
78 | "VGA", | ||
79 | }; | ||
80 | |||
81 | /********/ | ||
82 | |||
83 | /* Entries for the Nala (645/646) camera; the Nala doesn't have compression | ||
84 | preferences, so you either get compressed or non-compressed streams. | ||
85 | |||
86 | An alternate value of 0 means this mode is not available at all. | ||
87 | */ | ||
88 | |||
89 | #define PWC_FPS_MAX_NALA 8 | ||
90 | |||
91 | struct Nala_table_entry { | ||
92 | char alternate; /* USB alternate setting */ | ||
93 | int compressed; /* Compressed yes/no */ | ||
94 | |||
95 | unsigned char mode[3]; /* precomputed mode table */ | ||
96 | }; | ||
97 | |||
98 | static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 }; | ||
99 | |||
100 | static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] = | ||
101 | { | ||
102 | #include "pwc-nala.h" | ||
103 | }; | ||
104 | |||
105 | static void pwc_set_image_buffer_size(struct pwc_device *pdev); | ||
106 | |||
107 | /****************************************************************************/ | ||
108 | |||
109 | static int _send_control_msg(struct pwc_device *pdev, | ||
110 | u8 request, u16 value, int index, void *buf, int buflen) | ||
111 | { | ||
112 | int rc; | ||
113 | void *kbuf = NULL; | ||
114 | |||
115 | if (buflen) { | ||
116 | kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */ | ||
117 | if (kbuf == NULL) | ||
118 | return -ENOMEM; | ||
119 | memcpy(kbuf, buf, buflen); | ||
120 | } | ||
121 | |||
122 | rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), | ||
123 | request, | ||
124 | USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
125 | value, | ||
126 | index, | ||
127 | kbuf, buflen, USB_CTRL_SET_TIMEOUT); | ||
128 | |||
129 | kfree(kbuf); | ||
130 | return rc; | ||
131 | } | ||
132 | |||
133 | static int recv_control_msg(struct pwc_device *pdev, | ||
134 | u8 request, u16 value, void *buf, int buflen) | ||
135 | { | ||
136 | int rc; | ||
137 | void *kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */ | ||
138 | |||
139 | if (kbuf == NULL) | ||
140 | return -ENOMEM; | ||
141 | |||
142 | rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), | ||
143 | request, | ||
144 | USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, | ||
145 | value, | ||
146 | pdev->vcinterface, | ||
147 | kbuf, buflen, USB_CTRL_GET_TIMEOUT); | ||
148 | memcpy(buf, kbuf, buflen); | ||
149 | kfree(kbuf); | ||
150 | |||
151 | if (rc < 0) | ||
152 | PWC_ERROR("recv_control_msg error %d req %02x val %04x\n", | ||
153 | rc, request, value); | ||
154 | return rc; | ||
155 | } | ||
156 | |||
157 | static inline int send_video_command(struct pwc_device *pdev, | ||
158 | int index, void *buf, int buflen) | ||
159 | { | ||
160 | return _send_control_msg(pdev, | ||
161 | SET_EP_STREAM_CTL, | ||
162 | VIDEO_OUTPUT_CONTROL_FORMATTER, | ||
163 | index, | ||
164 | buf, buflen); | ||
165 | } | ||
166 | |||
167 | int send_control_msg(struct pwc_device *pdev, | ||
168 | u8 request, u16 value, void *buf, int buflen) | ||
169 | { | ||
170 | return _send_control_msg(pdev, | ||
171 | request, value, pdev->vcinterface, buf, buflen); | ||
172 | } | ||
173 | |||
174 | static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames) | ||
175 | { | ||
176 | unsigned char buf[3]; | ||
177 | int ret, fps; | ||
178 | struct Nala_table_entry *pEntry; | ||
179 | int frames2frames[31] = | ||
180 | { /* closest match of framerate */ | ||
181 | 0, 0, 0, 0, 4, /* 0-4 */ | ||
182 | 5, 5, 7, 7, 10, /* 5-9 */ | ||
183 | 10, 10, 12, 12, 15, /* 10-14 */ | ||
184 | 15, 15, 15, 20, 20, /* 15-19 */ | ||
185 | 20, 20, 20, 24, 24, /* 20-24 */ | ||
186 | 24, 24, 24, 24, 24, /* 25-29 */ | ||
187 | 24 /* 30 */ | ||
188 | }; | ||
189 | int frames2table[31] = | ||
190 | { 0, 0, 0, 0, 0, /* 0-4 */ | ||
191 | 1, 1, 1, 2, 2, /* 5-9 */ | ||
192 | 3, 3, 4, 4, 4, /* 10-14 */ | ||
193 | 5, 5, 5, 5, 5, /* 15-19 */ | ||
194 | 6, 6, 6, 6, 7, /* 20-24 */ | ||
195 | 7, 7, 7, 7, 7, /* 25-29 */ | ||
196 | 7 /* 30 */ | ||
197 | }; | ||
198 | |||
199 | if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25) | ||
200 | return -EINVAL; | ||
201 | frames = frames2frames[frames]; | ||
202 | fps = frames2table[frames]; | ||
203 | pEntry = &Nala_table[size][fps]; | ||
204 | if (pEntry->alternate == 0) | ||
205 | return -EINVAL; | ||
206 | |||
207 | memcpy(buf, pEntry->mode, 3); | ||
208 | ret = send_video_command(pdev, pdev->vendpoint, buf, 3); | ||
209 | if (ret < 0) { | ||
210 | PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret); | ||
211 | return ret; | ||
212 | } | ||
213 | if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { | ||
214 | ret = pwc_dec1_init(pdev, pdev->type, pdev->release, buf); | ||
215 | if (ret < 0) | ||
216 | return ret; | ||
217 | } | ||
218 | |||
219 | pdev->cmd_len = 3; | ||
220 | memcpy(pdev->cmd_buf, buf, 3); | ||
221 | |||
222 | /* Set various parameters */ | ||
223 | pdev->vframes = frames; | ||
224 | pdev->vsize = size; | ||
225 | pdev->valternate = pEntry->alternate; | ||
226 | pdev->image = pwc_image_sizes[size]; | ||
227 | pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2; | ||
228 | if (pEntry->compressed) { | ||
229 | if (pdev->release < 5) { /* 4 fold compression */ | ||
230 | pdev->vbandlength = 528; | ||
231 | pdev->frame_size /= 4; | ||
232 | } | ||
233 | else { | ||
234 | pdev->vbandlength = 704; | ||
235 | pdev->frame_size /= 3; | ||
236 | } | ||
237 | } | ||
238 | else | ||
239 | pdev->vbandlength = 0; | ||
240 | return 0; | ||
241 | } | ||
242 | |||
243 | |||
244 | static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) | ||
245 | { | ||
246 | unsigned char buf[13]; | ||
247 | const struct Timon_table_entry *pChoose; | ||
248 | int ret, fps; | ||
249 | |||
250 | if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) | ||
251 | return -EINVAL; | ||
252 | if (size == PSZ_VGA && frames > 15) | ||
253 | return -EINVAL; | ||
254 | fps = (frames / 5) - 1; | ||
255 | |||
256 | /* Find a supported framerate with progressively higher compression ratios | ||
257 | if the preferred ratio is not available. | ||
258 | */ | ||
259 | pChoose = NULL; | ||
260 | while (compression <= 3) { | ||
261 | pChoose = &Timon_table[size][fps][compression]; | ||
262 | if (pChoose->alternate != 0) | ||
263 | break; | ||
264 | compression++; | ||
265 | } | ||
266 | if (pChoose == NULL || pChoose->alternate == 0) | ||
267 | return -ENOENT; /* Not supported. */ | ||
268 | |||
269 | memcpy(buf, pChoose->mode, 13); | ||
270 | if (snapshot) | ||
271 | buf[0] |= 0x80; | ||
272 | ret = send_video_command(pdev, pdev->vendpoint, buf, 13); | ||
273 | if (ret < 0) | ||
274 | return ret; | ||
275 | |||
276 | if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { | ||
277 | ret = pwc_dec23_init(pdev, pdev->type, buf); | ||
278 | if (ret < 0) | ||
279 | return ret; | ||
280 | } | ||
281 | |||
282 | pdev->cmd_len = 13; | ||
283 | memcpy(pdev->cmd_buf, buf, 13); | ||
284 | |||
285 | /* Set various parameters */ | ||
286 | pdev->vframes = frames; | ||
287 | pdev->vsize = size; | ||
288 | pdev->vsnapshot = snapshot; | ||
289 | pdev->valternate = pChoose->alternate; | ||
290 | pdev->image = pwc_image_sizes[size]; | ||
291 | pdev->vbandlength = pChoose->bandlength; | ||
292 | if (pChoose->bandlength > 0) | ||
293 | pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4; | ||
294 | else | ||
295 | pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; | ||
296 | return 0; | ||
297 | } | ||
298 | |||
299 | |||
300 | static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot) | ||
301 | { | ||
302 | const struct Kiara_table_entry *pChoose = NULL; | ||
303 | int fps, ret; | ||
304 | unsigned char buf[12]; | ||
305 | struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}}; | ||
306 | |||
307 | if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3) | ||
308 | return -EINVAL; | ||
309 | if (size == PSZ_VGA && frames > 15) | ||
310 | return -EINVAL; | ||
311 | fps = (frames / 5) - 1; | ||
312 | |||
313 | /* special case: VGA @ 5 fps and snapshot is raw bayer mode */ | ||
314 | if (size == PSZ_VGA && frames == 5 && snapshot && pdev->pixfmt != V4L2_PIX_FMT_YUV420) | ||
315 | { | ||
316 | /* Only available in case the raw palette is selected or | ||
317 | we have the decompressor available. This mode is | ||
318 | only available in compressed form | ||
319 | */ | ||
320 | PWC_DEBUG_SIZE("Choosing VGA/5 BAYER mode.\n"); | ||
321 | pChoose = &RawEntry; | ||
322 | } | ||
323 | else | ||
324 | { | ||
325 | /* Find a supported framerate with progressively higher compression ratios | ||
326 | if the preferred ratio is not available. | ||
327 | Skip this step when using RAW modes. | ||
328 | */ | ||
329 | snapshot = 0; | ||
330 | while (compression <= 3) { | ||
331 | pChoose = &Kiara_table[size][fps][compression]; | ||
332 | if (pChoose->alternate != 0) | ||
333 | break; | ||
334 | compression++; | ||
335 | } | ||
336 | } | ||
337 | if (pChoose == NULL || pChoose->alternate == 0) | ||
338 | return -ENOENT; /* Not supported. */ | ||
339 | |||
340 | PWC_TRACE("Using alternate setting %d.\n", pChoose->alternate); | ||
341 | |||
342 | /* usb_control_msg won't take staticly allocated arrays as argument?? */ | ||
343 | memcpy(buf, pChoose->mode, 12); | ||
344 | if (snapshot) | ||
345 | buf[0] |= 0x80; | ||
346 | |||
347 | /* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */ | ||
348 | ret = send_video_command(pdev, 4 /* pdev->vendpoint */, buf, 12); | ||
349 | if (ret < 0) | ||
350 | return ret; | ||
351 | |||
352 | if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) { | ||
353 | ret = pwc_dec23_init(pdev, pdev->type, buf); | ||
354 | if (ret < 0) | ||
355 | return ret; | ||
356 | } | ||
357 | |||
358 | pdev->cmd_len = 12; | ||
359 | memcpy(pdev->cmd_buf, buf, 12); | ||
360 | /* All set and go */ | ||
361 | pdev->vframes = frames; | ||
362 | pdev->vsize = size; | ||
363 | pdev->vsnapshot = snapshot; | ||
364 | pdev->valternate = pChoose->alternate; | ||
365 | pdev->image = pwc_image_sizes[size]; | ||
366 | pdev->vbandlength = pChoose->bandlength; | ||
367 | if (pdev->vbandlength > 0) | ||
368 | pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4; | ||
369 | else | ||
370 | pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8; | ||
371 | PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vsnapshot=%d, vbandlength=%d\n", | ||
372 | pdev->frame_size,pdev->vframes,pdev->vsize,pdev->vsnapshot,pdev->vbandlength); | ||
373 | return 0; | ||
374 | } | ||
375 | |||
376 | |||
377 | |||
378 | /** | ||
379 | @pdev: device structure | ||
380 | @width: viewport width | ||
381 | @height: viewport height | ||
382 | @frame: framerate, in fps | ||
383 | @compression: preferred compression ratio | ||
384 | @snapshot: snapshot mode or streaming | ||
385 | */ | ||
386 | int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot) | ||
387 | { | ||
388 | int ret, size; | ||
389 | |||
390 | PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", width, height, frames, pdev->pixfmt); | ||
391 | size = pwc_decode_size(pdev, width, height); | ||
392 | if (size < 0) { | ||
393 | PWC_DEBUG_MODULE("Could not find suitable size.\n"); | ||
394 | return -ERANGE; | ||
395 | } | ||
396 | PWC_TRACE("decode_size = %d.\n", size); | ||
397 | |||
398 | if (DEVICE_USE_CODEC1(pdev->type)) { | ||
399 | ret = set_video_mode_Nala(pdev, size, frames); | ||
400 | |||
401 | } else if (DEVICE_USE_CODEC3(pdev->type)) { | ||
402 | ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot); | ||
403 | |||
404 | } else { | ||
405 | ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot); | ||
406 | } | ||
407 | if (ret < 0) { | ||
408 | PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret); | ||
409 | return ret; | ||
410 | } | ||
411 | pdev->view.x = width; | ||
412 | pdev->view.y = height; | ||
413 | pdev->vcompression = compression; | ||
414 | pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size; | ||
415 | pwc_set_image_buffer_size(pdev); | ||
416 | PWC_DEBUG_SIZE("Set viewport to %dx%d, image size is %dx%d.\n", width, height, pwc_image_sizes[size].x, pwc_image_sizes[size].y); | ||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size) | ||
421 | { | ||
422 | unsigned int i; | ||
423 | |||
424 | for (i = 0; i < PWC_FPS_MAX_NALA; i++) { | ||
425 | if (Nala_table[size][i].alternate) { | ||
426 | if (index--==0) return Nala_fps_vector[i]; | ||
427 | } | ||
428 | } | ||
429 | return 0; | ||
430 | } | ||
431 | |||
432 | static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size) | ||
433 | { | ||
434 | unsigned int i; | ||
435 | |||
436 | for (i = 0; i < PWC_FPS_MAX_KIARA; i++) { | ||
437 | if (Kiara_table[size][i][3].alternate) { | ||
438 | if (index--==0) return Kiara_fps_vector[i]; | ||
439 | } | ||
440 | } | ||
441 | return 0; | ||
442 | } | ||
443 | |||
444 | static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size) | ||
445 | { | ||
446 | unsigned int i; | ||
447 | |||
448 | for (i=0; i < PWC_FPS_MAX_TIMON; i++) { | ||
449 | if (Timon_table[size][i][3].alternate) { | ||
450 | if (index--==0) return Timon_fps_vector[i]; | ||
451 | } | ||
452 | } | ||
453 | return 0; | ||
454 | } | ||
455 | |||
456 | unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size) | ||
457 | { | ||
458 | unsigned int ret; | ||
459 | |||
460 | if (DEVICE_USE_CODEC1(pdev->type)) { | ||
461 | ret = pwc_get_fps_Nala(pdev, index, size); | ||
462 | |||
463 | } else if (DEVICE_USE_CODEC3(pdev->type)) { | ||
464 | ret = pwc_get_fps_Kiara(pdev, index, size); | ||
465 | |||
466 | } else { | ||
467 | ret = pwc_get_fps_Timon(pdev, index, size); | ||
468 | } | ||
469 | |||
470 | return ret; | ||
471 | } | ||
472 | |||
473 | static void pwc_set_image_buffer_size(struct pwc_device *pdev) | ||
474 | { | ||
475 | int factor = 0; | ||
476 | |||
477 | /* for V4L2_PIX_FMT_YUV420 */ | ||
478 | switch (pdev->pixfmt) { | ||
479 | case V4L2_PIX_FMT_YUV420: | ||
480 | factor = 6; | ||
481 | break; | ||
482 | case V4L2_PIX_FMT_PWC1: | ||
483 | case V4L2_PIX_FMT_PWC2: | ||
484 | factor = 6; /* can be uncompressed YUV420P */ | ||
485 | break; | ||
486 | } | ||
487 | |||
488 | /* Set sizes in bytes */ | ||
489 | pdev->image.size = pdev->image.x * pdev->image.y * factor / 4; | ||
490 | pdev->view.size = pdev->view.x * pdev->view.y * factor / 4; | ||
491 | |||
492 | /* Align offset, or you'll get some very weird results in | ||
493 | YUV420 mode... x must be multiple of 4 (to get the Y's in | ||
494 | place), and y even (or you'll mixup U & V). This is less of a | ||
495 | problem for YUV420P. | ||
496 | */ | ||
497 | pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC; | ||
498 | pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE; | ||
499 | } | ||
500 | |||
501 | int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) | ||
502 | { | ||
503 | int ret; | ||
504 | u8 buf; | ||
505 | |||
506 | ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); | ||
507 | if (ret < 0) | ||
508 | return ret; | ||
509 | |||
510 | *data = buf; | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data) | ||
515 | { | ||
516 | int ret; | ||
517 | |||
518 | ret = send_control_msg(pdev, request, value, &data, sizeof(data)); | ||
519 | if (ret < 0) | ||
520 | return ret; | ||
521 | |||
522 | return 0; | ||
523 | } | ||
524 | |||
525 | int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) | ||
526 | { | ||
527 | int ret; | ||
528 | s8 buf; | ||
529 | |||
530 | ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf)); | ||
531 | if (ret < 0) | ||
532 | return ret; | ||
533 | |||
534 | *data = buf; | ||
535 | return 0; | ||
536 | } | ||
537 | |||
538 | int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data) | ||
539 | { | ||
540 | int ret; | ||
541 | u8 buf[2]; | ||
542 | |||
543 | ret = recv_control_msg(pdev, request, value, buf, sizeof(buf)); | ||
544 | if (ret < 0) | ||
545 | return ret; | ||
546 | |||
547 | *data = (buf[1] << 8) | buf[0]; | ||
548 | return 0; | ||
549 | } | ||
550 | |||
551 | int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data) | ||
552 | { | ||
553 | int ret; | ||
554 | u8 buf[2]; | ||
555 | |||
556 | buf[0] = data & 0xff; | ||
557 | buf[1] = data >> 8; | ||
558 | ret = send_control_msg(pdev, request, value, buf, sizeof(buf)); | ||
559 | if (ret < 0) | ||
560 | return ret; | ||
561 | |||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | int pwc_button_ctrl(struct pwc_device *pdev, u16 value) | ||
566 | { | ||
567 | int ret; | ||
568 | |||
569 | ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0); | ||
570 | if (ret < 0) | ||
571 | return ret; | ||
572 | |||
573 | return 0; | ||
574 | } | ||
575 | |||
576 | /* POWER */ | ||
577 | void pwc_camera_power(struct pwc_device *pdev, int power) | ||
578 | { | ||
579 | char buf; | ||
580 | int r; | ||
581 | |||
582 | if (!pdev->power_save) | ||
583 | return; | ||
584 | |||
585 | if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6)) | ||
586 | return; /* Not supported by Nala or Timon < release 6 */ | ||
587 | |||
588 | if (power) | ||
589 | buf = 0x00; /* active */ | ||
590 | else | ||
591 | buf = 0xFF; /* power save */ | ||
592 | r = send_control_msg(pdev, | ||
593 | SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER, | ||
594 | &buf, sizeof(buf)); | ||
595 | |||
596 | if (r < 0) | ||
597 | PWC_ERROR("Failed to power %s camera (%d)\n", | ||
598 | power ? "on" : "off", r); | ||
599 | } | ||
600 | |||
601 | static int pwc_set_wb_speed(struct pwc_device *pdev, int speed) | ||
602 | { | ||
603 | unsigned char buf; | ||
604 | |||
605 | /* useful range is 0x01..0x20 */ | ||
606 | buf = speed / 0x7f0; | ||
607 | return send_control_msg(pdev, | ||
608 | SET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, &buf, sizeof(buf)); | ||
609 | } | ||
610 | |||
611 | static int pwc_get_wb_speed(struct pwc_device *pdev, int *value) | ||
612 | { | ||
613 | unsigned char buf; | ||
614 | int ret; | ||
615 | |||
616 | ret = recv_control_msg(pdev, | ||
617 | GET_CHROM_CTL, AWB_CONTROL_SPEED_FORMATTER, &buf, sizeof(buf)); | ||
618 | if (ret < 0) | ||
619 | return ret; | ||
620 | *value = buf * 0x7f0; | ||
621 | return 0; | ||
622 | } | ||
623 | |||
624 | |||
625 | static int pwc_set_wb_delay(struct pwc_device *pdev, int delay) | ||
626 | { | ||
627 | unsigned char buf; | ||
628 | |||
629 | /* useful range is 0x01..0x3F */ | ||
630 | buf = (delay >> 10); | ||
631 | return send_control_msg(pdev, | ||
632 | SET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, &buf, sizeof(buf)); | ||
633 | } | ||
634 | |||
635 | static int pwc_get_wb_delay(struct pwc_device *pdev, int *value) | ||
636 | { | ||
637 | unsigned char buf; | ||
638 | int ret; | ||
639 | |||
640 | ret = recv_control_msg(pdev, | ||
641 | GET_CHROM_CTL, AWB_CONTROL_DELAY_FORMATTER, &buf, sizeof(buf)); | ||
642 | if (ret < 0) | ||
643 | return ret; | ||
644 | *value = buf << 10; | ||
645 | return 0; | ||
646 | } | ||
647 | |||
648 | |||
649 | int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value) | ||
650 | { | ||
651 | unsigned char buf[2]; | ||
652 | int r; | ||
653 | |||
654 | if (pdev->type < 730) | ||
655 | return 0; | ||
656 | on_value /= 100; | ||
657 | off_value /= 100; | ||
658 | if (on_value < 0) | ||
659 | on_value = 0; | ||
660 | if (on_value > 0xff) | ||
661 | on_value = 0xff; | ||
662 | if (off_value < 0) | ||
663 | off_value = 0; | ||
664 | if (off_value > 0xff) | ||
665 | off_value = 0xff; | ||
666 | |||
667 | buf[0] = on_value; | ||
668 | buf[1] = off_value; | ||
669 | |||
670 | r = send_control_msg(pdev, | ||
671 | SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf)); | ||
672 | if (r < 0) | ||
673 | PWC_ERROR("Failed to set LED on/off time (%d)\n", r); | ||
674 | |||
675 | return r; | ||
676 | } | ||
677 | |||
678 | static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value) | ||
679 | { | ||
680 | unsigned char buf[2]; | ||
681 | int ret; | ||
682 | |||
683 | if (pdev->type < 730) { | ||
684 | *on_value = -1; | ||
685 | *off_value = -1; | ||
686 | return 0; | ||
687 | } | ||
688 | |||
689 | ret = recv_control_msg(pdev, | ||
690 | GET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf)); | ||
691 | if (ret < 0) | ||
692 | return ret; | ||
693 | *on_value = buf[0] * 100; | ||
694 | *off_value = buf[1] * 100; | ||
695 | return 0; | ||
696 | } | ||
697 | |||
698 | static int _pwc_mpt_reset(struct pwc_device *pdev, int flags) | ||
699 | { | ||
700 | unsigned char buf; | ||
701 | |||
702 | buf = flags & 0x03; // only lower two bits are currently used | ||
703 | return send_control_msg(pdev, | ||
704 | SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, &buf, sizeof(buf)); | ||
705 | } | ||
706 | |||
707 | int pwc_mpt_reset(struct pwc_device *pdev, int flags) | ||
708 | { | ||
709 | int ret; | ||
710 | ret = _pwc_mpt_reset(pdev, flags); | ||
711 | if (ret >= 0) { | ||
712 | pdev->pan_angle = 0; | ||
713 | pdev->tilt_angle = 0; | ||
714 | } | ||
715 | return ret; | ||
716 | } | ||
717 | |||
718 | static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) | ||
719 | { | ||
720 | unsigned char buf[4]; | ||
721 | |||
722 | /* set new relative angle; angles are expressed in degrees * 100, | ||
723 | but cam as .5 degree resolution, hence divide by 200. Also | ||
724 | the angle must be multiplied by 64 before it's send to | ||
725 | the cam (??) | ||
726 | */ | ||
727 | pan = 64 * pan / 100; | ||
728 | tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */ | ||
729 | buf[0] = pan & 0xFF; | ||
730 | buf[1] = (pan >> 8) & 0xFF; | ||
731 | buf[2] = tilt & 0xFF; | ||
732 | buf[3] = (tilt >> 8) & 0xFF; | ||
733 | return send_control_msg(pdev, | ||
734 | SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, &buf, sizeof(buf)); | ||
735 | } | ||
736 | |||
737 | int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt) | ||
738 | { | ||
739 | int ret; | ||
740 | |||
741 | /* check absolute ranges */ | ||
742 | if (pan < pdev->angle_range.pan_min || | ||
743 | pan > pdev->angle_range.pan_max || | ||
744 | tilt < pdev->angle_range.tilt_min || | ||
745 | tilt > pdev->angle_range.tilt_max) | ||
746 | return -ERANGE; | ||
747 | |||
748 | /* go to relative range, check again */ | ||
749 | pan -= pdev->pan_angle; | ||
750 | tilt -= pdev->tilt_angle; | ||
751 | /* angles are specified in degrees * 100, thus the limit = 36000 */ | ||
752 | if (pan < -36000 || pan > 36000 || tilt < -36000 || tilt > 36000) | ||
753 | return -ERANGE; | ||
754 | |||
755 | ret = _pwc_mpt_set_angle(pdev, pan, tilt); | ||
756 | if (ret >= 0) { | ||
757 | pdev->pan_angle += pan; | ||
758 | pdev->tilt_angle += tilt; | ||
759 | } | ||
760 | if (ret == -EPIPE) /* stall -> out of range */ | ||
761 | ret = -ERANGE; | ||
762 | return ret; | ||
763 | } | ||
764 | |||
765 | static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status) | ||
766 | { | ||
767 | int ret; | ||
768 | unsigned char buf[5]; | ||
769 | |||
770 | ret = recv_control_msg(pdev, | ||
771 | GET_MPT_CTL, PT_STATUS_FORMATTER, &buf, sizeof(buf)); | ||
772 | if (ret < 0) | ||
773 | return ret; | ||
774 | status->status = buf[0] & 0x7; // 3 bits are used for reporting | ||
775 | status->time_pan = (buf[1] << 8) + buf[2]; | ||
776 | status->time_tilt = (buf[3] << 8) + buf[4]; | ||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | #ifdef CONFIG_USB_PWC_DEBUG | ||
781 | int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor) | ||
782 | { | ||
783 | unsigned char buf; | ||
784 | int ret = -1, request; | ||
785 | |||
786 | if (pdev->type < 675) | ||
787 | request = SENSOR_TYPE_FORMATTER1; | ||
788 | else if (pdev->type < 730) | ||
789 | return -1; /* The Vesta series doesn't have this call */ | ||
790 | else | ||
791 | request = SENSOR_TYPE_FORMATTER2; | ||
792 | |||
793 | ret = recv_control_msg(pdev, | ||
794 | GET_STATUS_CTL, request, &buf, sizeof(buf)); | ||
795 | if (ret < 0) | ||
796 | return ret; | ||
797 | if (pdev->type < 675) | ||
798 | *sensor = buf | 0x100; | ||
799 | else | ||
800 | *sensor = buf; | ||
801 | return 0; | ||
802 | } | ||
803 | #endif | ||
804 | |||
805 | /* End of Add-Ons */ | ||
806 | /* ************************************************* */ | ||
807 | |||
808 | /* Linux 2.5.something and 2.6 pass direct pointers to arguments of | ||
809 | ioctl() calls. With 2.4, you have to do tedious copy_from_user() | ||
810 | and copy_to_user() calls. With these macros we circumvent this, | ||
811 | and let me maintain only one source file. The functionality is | ||
812 | exactly the same otherwise. | ||
813 | */ | ||
814 | |||
815 | /* define local variable for arg */ | ||
816 | #define ARG_DEF(ARG_type, ARG_name)\ | ||
817 | ARG_type *ARG_name = arg; | ||
818 | /* copy arg to local variable */ | ||
819 | #define ARG_IN(ARG_name) /* nothing */ | ||
820 | /* argument itself (referenced) */ | ||
821 | #define ARGR(ARG_name) (*ARG_name) | ||
822 | /* argument address */ | ||
823 | #define ARGA(ARG_name) ARG_name | ||
824 | /* copy local variable to arg */ | ||
825 | #define ARG_OUT(ARG_name) /* nothing */ | ||
826 | |||
827 | /* | ||
828 | * Our ctrls use native values, but the old custom pwc ioctl interface expects | ||
829 | * values from 0 - 65535, define 2 helper functions to scale things. */ | ||
830 | static int pwc_ioctl_g_ctrl(struct v4l2_ctrl *ctrl) | ||
831 | { | ||
832 | return v4l2_ctrl_g_ctrl(ctrl) * 65535 / ctrl->maximum; | ||
833 | } | ||
834 | |||
835 | static int pwc_ioctl_s_ctrl(struct v4l2_ctrl *ctrl, int val) | ||
836 | { | ||
837 | return v4l2_ctrl_s_ctrl(ctrl, val * ctrl->maximum / 65535); | ||
838 | } | ||
839 | |||
840 | long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg) | ||
841 | { | ||
842 | long ret = 0; | ||
843 | |||
844 | switch(cmd) { | ||
845 | case VIDIOCPWCRUSER: | ||
846 | ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER); | ||
847 | break; | ||
848 | |||
849 | case VIDIOCPWCSUSER: | ||
850 | ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER); | ||
851 | break; | ||
852 | |||
853 | case VIDIOCPWCFACTORY: | ||
854 | ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER); | ||
855 | break; | ||
856 | |||
857 | case VIDIOCPWCSCQUAL: | ||
858 | { | ||
859 | ARG_DEF(int, qual) | ||
860 | |||
861 | if (vb2_is_streaming(&pdev->vb_queue)) { | ||
862 | ret = -EBUSY; | ||
863 | break; | ||
864 | } | ||
865 | |||
866 | ARG_IN(qual) | ||
867 | if (ARGR(qual) < 0 || ARGR(qual) > 3) | ||
868 | ret = -EINVAL; | ||
869 | else | ||
870 | ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot); | ||
871 | break; | ||
872 | } | ||
873 | |||
874 | case VIDIOCPWCGCQUAL: | ||
875 | { | ||
876 | ARG_DEF(int, qual) | ||
877 | |||
878 | ARGR(qual) = pdev->vcompression; | ||
879 | ARG_OUT(qual) | ||
880 | break; | ||
881 | } | ||
882 | |||
883 | case VIDIOCPWCPROBE: | ||
884 | { | ||
885 | ARG_DEF(struct pwc_probe, probe) | ||
886 | |||
887 | strcpy(ARGR(probe).name, pdev->vdev.name); | ||
888 | ARGR(probe).type = pdev->type; | ||
889 | ARG_OUT(probe) | ||
890 | break; | ||
891 | } | ||
892 | |||
893 | case VIDIOCPWCGSERIAL: | ||
894 | { | ||
895 | ARG_DEF(struct pwc_serial, serial) | ||
896 | |||
897 | strcpy(ARGR(serial).serial, pdev->serial); | ||
898 | ARG_OUT(serial) | ||
899 | break; | ||
900 | } | ||
901 | |||
902 | case VIDIOCPWCSAGC: | ||
903 | { | ||
904 | ARG_DEF(int, agc) | ||
905 | ARG_IN(agc) | ||
906 | ret = v4l2_ctrl_s_ctrl(pdev->autogain, ARGR(agc) < 0); | ||
907 | if (ret == 0 && ARGR(agc) >= 0) | ||
908 | ret = pwc_ioctl_s_ctrl(pdev->gain, ARGR(agc)); | ||
909 | break; | ||
910 | } | ||
911 | |||
912 | case VIDIOCPWCGAGC: | ||
913 | { | ||
914 | ARG_DEF(int, agc) | ||
915 | if (v4l2_ctrl_g_ctrl(pdev->autogain)) | ||
916 | ARGR(agc) = -1; | ||
917 | else | ||
918 | ARGR(agc) = pwc_ioctl_g_ctrl(pdev->gain); | ||
919 | ARG_OUT(agc) | ||
920 | break; | ||
921 | } | ||
922 | |||
923 | case VIDIOCPWCSSHUTTER: | ||
924 | { | ||
925 | ARG_DEF(int, shutter) | ||
926 | ARG_IN(shutter) | ||
927 | ret = v4l2_ctrl_s_ctrl(pdev->exposure_auto, | ||
928 | /* Menu idx 0 = auto, idx 1 = manual */ | ||
929 | ARGR(shutter) >= 0); | ||
930 | if (ret == 0 && ARGR(shutter) >= 0) | ||
931 | ret = pwc_ioctl_s_ctrl(pdev->exposure, ARGR(shutter)); | ||
932 | break; | ||
933 | } | ||
934 | |||
935 | case VIDIOCPWCSAWB: | ||
936 | { | ||
937 | ARG_DEF(struct pwc_whitebalance, wb) | ||
938 | ARG_IN(wb) | ||
939 | ret = v4l2_ctrl_s_ctrl(pdev->auto_white_balance, | ||
940 | ARGR(wb).mode); | ||
941 | if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL) | ||
942 | ret = pwc_ioctl_s_ctrl(pdev->red_balance, | ||
943 | ARGR(wb).manual_red); | ||
944 | if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL) | ||
945 | ret = pwc_ioctl_s_ctrl(pdev->blue_balance, | ||
946 | ARGR(wb).manual_blue); | ||
947 | break; | ||
948 | } | ||
949 | |||
950 | case VIDIOCPWCGAWB: | ||
951 | { | ||
952 | ARG_DEF(struct pwc_whitebalance, wb) | ||
953 | ARGR(wb).mode = v4l2_ctrl_g_ctrl(pdev->auto_white_balance); | ||
954 | ARGR(wb).manual_red = ARGR(wb).read_red = | ||
955 | pwc_ioctl_g_ctrl(pdev->red_balance); | ||
956 | ARGR(wb).manual_blue = ARGR(wb).read_blue = | ||
957 | pwc_ioctl_g_ctrl(pdev->blue_balance); | ||
958 | ARG_OUT(wb) | ||
959 | break; | ||
960 | } | ||
961 | |||
962 | case VIDIOCPWCSAWBSPEED: | ||
963 | { | ||
964 | ARG_DEF(struct pwc_wb_speed, wbs) | ||
965 | |||
966 | if (ARGR(wbs).control_speed > 0) { | ||
967 | ret = pwc_set_wb_speed(pdev, ARGR(wbs).control_speed); | ||
968 | } | ||
969 | if (ARGR(wbs).control_delay > 0) { | ||
970 | ret = pwc_set_wb_delay(pdev, ARGR(wbs).control_delay); | ||
971 | } | ||
972 | break; | ||
973 | } | ||
974 | |||
975 | case VIDIOCPWCGAWBSPEED: | ||
976 | { | ||
977 | ARG_DEF(struct pwc_wb_speed, wbs) | ||
978 | |||
979 | ret = pwc_get_wb_speed(pdev, &ARGR(wbs).control_speed); | ||
980 | if (ret < 0) | ||
981 | break; | ||
982 | ret = pwc_get_wb_delay(pdev, &ARGR(wbs).control_delay); | ||
983 | if (ret < 0) | ||
984 | break; | ||
985 | ARG_OUT(wbs) | ||
986 | break; | ||
987 | } | ||
988 | |||
989 | case VIDIOCPWCSLED: | ||
990 | { | ||
991 | ARG_DEF(struct pwc_leds, leds) | ||
992 | |||
993 | ARG_IN(leds) | ||
994 | ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off); | ||
995 | break; | ||
996 | } | ||
997 | |||
998 | |||
999 | case VIDIOCPWCGLED: | ||
1000 | { | ||
1001 | ARG_DEF(struct pwc_leds, leds) | ||
1002 | |||
1003 | ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off); | ||
1004 | ARG_OUT(leds) | ||
1005 | break; | ||
1006 | } | ||
1007 | |||
1008 | case VIDIOCPWCSCONTOUR: | ||
1009 | { | ||
1010 | ARG_DEF(int, contour) | ||
1011 | ARG_IN(contour) | ||
1012 | ret = v4l2_ctrl_s_ctrl(pdev->autocontour, ARGR(contour) < 0); | ||
1013 | if (ret == 0 && ARGR(contour) >= 0) | ||
1014 | ret = pwc_ioctl_s_ctrl(pdev->contour, ARGR(contour)); | ||
1015 | break; | ||
1016 | } | ||
1017 | |||
1018 | case VIDIOCPWCGCONTOUR: | ||
1019 | { | ||
1020 | ARG_DEF(int, contour) | ||
1021 | if (v4l2_ctrl_g_ctrl(pdev->autocontour)) | ||
1022 | ARGR(contour) = -1; | ||
1023 | else | ||
1024 | ARGR(contour) = pwc_ioctl_g_ctrl(pdev->contour); | ||
1025 | ARG_OUT(contour) | ||
1026 | break; | ||
1027 | } | ||
1028 | |||
1029 | case VIDIOCPWCSBACKLIGHT: | ||
1030 | { | ||
1031 | ARG_DEF(int, backlight) | ||
1032 | ARG_IN(backlight) | ||
1033 | ret = v4l2_ctrl_s_ctrl(pdev->backlight, ARGR(backlight)); | ||
1034 | break; | ||
1035 | } | ||
1036 | |||
1037 | case VIDIOCPWCGBACKLIGHT: | ||
1038 | { | ||
1039 | ARG_DEF(int, backlight) | ||
1040 | ARGR(backlight) = v4l2_ctrl_g_ctrl(pdev->backlight); | ||
1041 | ARG_OUT(backlight) | ||
1042 | break; | ||
1043 | } | ||
1044 | |||
1045 | case VIDIOCPWCSFLICKER: | ||
1046 | { | ||
1047 | ARG_DEF(int, flicker) | ||
1048 | ARG_IN(flicker) | ||
1049 | ret = v4l2_ctrl_s_ctrl(pdev->flicker, ARGR(flicker)); | ||
1050 | break; | ||
1051 | } | ||
1052 | |||
1053 | case VIDIOCPWCGFLICKER: | ||
1054 | { | ||
1055 | ARG_DEF(int, flicker) | ||
1056 | ARGR(flicker) = v4l2_ctrl_g_ctrl(pdev->flicker); | ||
1057 | ARG_OUT(flicker) | ||
1058 | break; | ||
1059 | } | ||
1060 | |||
1061 | case VIDIOCPWCSDYNNOISE: | ||
1062 | { | ||
1063 | ARG_DEF(int, dynnoise) | ||
1064 | ARG_IN(dynnoise) | ||
1065 | ret = v4l2_ctrl_s_ctrl(pdev->noise_reduction, ARGR(dynnoise)); | ||
1066 | break; | ||
1067 | } | ||
1068 | |||
1069 | case VIDIOCPWCGDYNNOISE: | ||
1070 | { | ||
1071 | ARG_DEF(int, dynnoise) | ||
1072 | ARGR(dynnoise) = v4l2_ctrl_g_ctrl(pdev->noise_reduction); | ||
1073 | ARG_OUT(dynnoise); | ||
1074 | break; | ||
1075 | } | ||
1076 | |||
1077 | case VIDIOCPWCGREALSIZE: | ||
1078 | { | ||
1079 | ARG_DEF(struct pwc_imagesize, size) | ||
1080 | |||
1081 | ARGR(size).width = pdev->image.x; | ||
1082 | ARGR(size).height = pdev->image.y; | ||
1083 | ARG_OUT(size) | ||
1084 | break; | ||
1085 | } | ||
1086 | |||
1087 | case VIDIOCPWCMPTRESET: | ||
1088 | { | ||
1089 | if (pdev->features & FEATURE_MOTOR_PANTILT) | ||
1090 | { | ||
1091 | ARG_DEF(int, flags) | ||
1092 | |||
1093 | ARG_IN(flags) | ||
1094 | ret = pwc_mpt_reset(pdev, ARGR(flags)); | ||
1095 | } | ||
1096 | else | ||
1097 | { | ||
1098 | ret = -ENXIO; | ||
1099 | } | ||
1100 | break; | ||
1101 | } | ||
1102 | |||
1103 | case VIDIOCPWCMPTGRANGE: | ||
1104 | { | ||
1105 | if (pdev->features & FEATURE_MOTOR_PANTILT) | ||
1106 | { | ||
1107 | ARG_DEF(struct pwc_mpt_range, range) | ||
1108 | |||
1109 | ARGR(range) = pdev->angle_range; | ||
1110 | ARG_OUT(range) | ||
1111 | } | ||
1112 | else | ||
1113 | { | ||
1114 | ret = -ENXIO; | ||
1115 | } | ||
1116 | break; | ||
1117 | } | ||
1118 | |||
1119 | case VIDIOCPWCMPTSANGLE: | ||
1120 | { | ||
1121 | int new_pan, new_tilt; | ||
1122 | |||
1123 | if (pdev->features & FEATURE_MOTOR_PANTILT) | ||
1124 | { | ||
1125 | ARG_DEF(struct pwc_mpt_angles, angles) | ||
1126 | |||
1127 | ARG_IN(angles) | ||
1128 | /* The camera can only set relative angles, so | ||
1129 | do some calculations when getting an absolute angle . | ||
1130 | */ | ||
1131 | if (ARGR(angles).absolute) | ||
1132 | { | ||
1133 | new_pan = ARGR(angles).pan; | ||
1134 | new_tilt = ARGR(angles).tilt; | ||
1135 | } | ||
1136 | else | ||
1137 | { | ||
1138 | new_pan = pdev->pan_angle + ARGR(angles).pan; | ||
1139 | new_tilt = pdev->tilt_angle + ARGR(angles).tilt; | ||
1140 | } | ||
1141 | ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt); | ||
1142 | } | ||
1143 | else | ||
1144 | { | ||
1145 | ret = -ENXIO; | ||
1146 | } | ||
1147 | break; | ||
1148 | } | ||
1149 | |||
1150 | case VIDIOCPWCMPTGANGLE: | ||
1151 | { | ||
1152 | |||
1153 | if (pdev->features & FEATURE_MOTOR_PANTILT) | ||
1154 | { | ||
1155 | ARG_DEF(struct pwc_mpt_angles, angles) | ||
1156 | |||
1157 | ARGR(angles).absolute = 1; | ||
1158 | ARGR(angles).pan = pdev->pan_angle; | ||
1159 | ARGR(angles).tilt = pdev->tilt_angle; | ||
1160 | ARG_OUT(angles) | ||
1161 | } | ||
1162 | else | ||
1163 | { | ||
1164 | ret = -ENXIO; | ||
1165 | } | ||
1166 | break; | ||
1167 | } | ||
1168 | |||
1169 | case VIDIOCPWCMPTSTATUS: | ||
1170 | { | ||
1171 | if (pdev->features & FEATURE_MOTOR_PANTILT) | ||
1172 | { | ||
1173 | ARG_DEF(struct pwc_mpt_status, status) | ||
1174 | |||
1175 | ret = pwc_mpt_get_status(pdev, ARGA(status)); | ||
1176 | ARG_OUT(status) | ||
1177 | } | ||
1178 | else | ||
1179 | { | ||
1180 | ret = -ENXIO; | ||
1181 | } | ||
1182 | break; | ||
1183 | } | ||
1184 | |||
1185 | case VIDIOCPWCGVIDCMD: | ||
1186 | { | ||
1187 | ARG_DEF(struct pwc_video_command, vcmd); | ||
1188 | |||
1189 | ARGR(vcmd).type = pdev->type; | ||
1190 | ARGR(vcmd).release = pdev->release; | ||
1191 | ARGR(vcmd).command_len = pdev->cmd_len; | ||
1192 | memcpy(&ARGR(vcmd).command_buf, pdev->cmd_buf, pdev->cmd_len); | ||
1193 | ARGR(vcmd).bandlength = pdev->vbandlength; | ||
1194 | ARGR(vcmd).frame_size = pdev->frame_size; | ||
1195 | ARG_OUT(vcmd) | ||
1196 | break; | ||
1197 | } | ||
1198 | /* | ||
1199 | case VIDIOCPWCGVIDTABLE: | ||
1200 | { | ||
1201 | ARG_DEF(struct pwc_table_init_buffer, table); | ||
1202 | ARGR(table).len = pdev->cmd_len; | ||
1203 | memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size); | ||
1204 | ARG_OUT(table) | ||
1205 | break; | ||
1206 | } | ||
1207 | */ | ||
1208 | |||
1209 | default: | ||
1210 | ret = -ENOIOCTLCMD; | ||
1211 | break; | ||
1212 | } | ||
1213 | |||
1214 | if (ret > 0) | ||
1215 | return 0; | ||
1216 | return ret; | ||
1217 | } | ||
1218 | |||
1219 | |||
1220 | /* vim: set cinoptions= formatoptions=croql cindent shiftwidth=8 tabstop=8: */ | ||