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