pwc-ctrl.c 27.5 KB
Newer Older
L
Linus Torvalds 已提交
1 2 3 4
/* Driver for Philips webcam
   Functions that send various control messages to the webcam, including
   video modes.
   (C) 1999-2003 Nemosoft Unv.
5
   (C) 2004-2006 Luc Saillard (luc@saillard.org)
6
   (C) 2011 Hans de Goede <hdegoede@redhat.com>
L
Linus Torvalds 已提交
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34

   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
   driver and thus may have bugs that are not present in the original version.
   Please send bug reports and support requests to <luc@saillard.org>.

   NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
   driver and thus may have bugs that are not present in the original version.
   Please send bug reports and support requests to <luc@saillard.org>.
   The decompression routines have been implemented by reverse-engineering the
   Nemosoft binary pwcx module. Caveat emptor.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/*
   Changes
35 36
   2001/08/03  Alvarado   Added methods for changing white balance and
			  red/green gains
L
Linus Torvalds 已提交
37 38 39 40 41
 */

/* Control functions for the cam; brightness, contrast, video mode, etc. */

#ifdef __KERNEL__
42
#include <asm/uaccess.h>
L
Linus Torvalds 已提交
43 44
#endif
#include <asm/errno.h>
45

L
Linus Torvalds 已提交
46 47 48
#include "pwc.h"
#include "pwc-kiara.h"
#include "pwc-timon.h"
49 50
#include "pwc-dec1.h"
#include "pwc-dec23.h"
L
Linus Torvalds 已提交
51

52
/* Selectors for status controls used only in this file */
53
#define GET_STATUS_B00				0x0B00
L
Linus Torvalds 已提交
54
#define SENSOR_TYPE_FORMATTER1			0x0C00
55
#define GET_STATUS_3000				0x3000
L
Linus Torvalds 已提交
56 57 58 59
#define READ_RAW_Y_MEAN_FORMATTER		0x3100
#define SET_POWER_SAVE_MODE_FORMATTER		0x3200
#define MIRROR_IMAGE_FORMATTER			0x3300
#define LED_FORMATTER				0x3400
60 61
#define LOWLIGHT				0x3500
#define GET_STATUS_3600				0x3600
L
Linus Torvalds 已提交
62
#define SENSOR_TYPE_FORMATTER2			0x3700
63 64 65 66
#define GET_STATUS_3800				0x3800
#define GET_STATUS_4000				0x4000
#define GET_STATUS_4100				0x4100	/* Get */
#define CTL_STATUS_4200				0x4200	/* [GS] 1 */
L
Linus Torvalds 已提交
67 68 69 70

/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
#define VIDEO_OUTPUT_CONTROL_FORMATTER		0x0100

71
static const char *size2name[PSZ_MAX] =
L
Linus Torvalds 已提交
72 73 74 75 76 77 78
{
	"subQCIF",
	"QSIF",
	"QCIF",
	"SIF",
	"CIF",
	"VGA",
79
};
L
Linus Torvalds 已提交
80 81 82

/********/

83
/* Entries for the Nala (645/646) camera; the Nala doesn't have compression
L
Linus Torvalds 已提交
84
   preferences, so you either get compressed or non-compressed streams.
85

L
Linus Torvalds 已提交
86 87 88
   An alternate value of 0 means this mode is not available at all.
 */

89 90
#define PWC_FPS_MAX_NALA 8

L
Linus Torvalds 已提交
91 92 93 94 95 96 97
struct Nala_table_entry {
	char alternate;			/* USB alternate setting */
	int compressed;			/* Compressed yes/no */

	unsigned char mode[3];		/* precomputed mode table */
};

98 99 100
static unsigned int Nala_fps_vector[PWC_FPS_MAX_NALA] = { 4, 5, 7, 10, 12, 15, 20, 24 };

static struct Nala_table_entry Nala_table[PSZ_MAX][PWC_FPS_MAX_NALA] =
L
Linus Torvalds 已提交
101 102 103 104
{
#include "pwc-nala.h"
};

105
static void pwc_set_image_buffer_size(struct pwc_device *pdev);
L
Linus Torvalds 已提交
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 113 114 115
{
	int rc;
	void *kbuf = NULL;

	if (buflen) {
116
		kbuf = kmemdup(buf, buflen, GFP_KERNEL); /* not allowed on stack */
117 118 119
		if (kbuf == NULL)
			return -ENOMEM;
	}
L
Linus Torvalds 已提交
120

121 122 123 124 125
	rc = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
		request,
		USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
		value,
		index,
126
		kbuf, buflen, USB_CTRL_SET_TIMEOUT);
L
Linus Torvalds 已提交
127

128 129 130
	kfree(kbuf);
	return rc;
}
L
Linus Torvalds 已提交
131

132 133 134 135 136 137 138 139 140 141 142 143 144 145
static int recv_control_msg(struct pwc_device *pdev,
	u8 request, u16 value, void *buf, int buflen)
{
	int rc;
	void *kbuf = kmalloc(buflen, GFP_KERNEL); /* not allowed on stack */

	if (kbuf == NULL)
		return -ENOMEM;

	rc = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
		request,
		USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
		value,
		pdev->vcinterface,
146
		kbuf, buflen, USB_CTRL_GET_TIMEOUT);
147 148
	memcpy(buf, kbuf, buflen);
	kfree(kbuf);
149 150 151 152

	if (rc < 0)
		PWC_ERROR("recv_control_msg error %d req %02x val %04x\n",
			  rc, request, value);
153 154
	return rc;
}
L
Linus Torvalds 已提交
155

156 157
static inline int send_video_command(struct pwc_device *pdev,
	int index, void *buf, int buflen)
L
Linus Torvalds 已提交
158
{
159
	return _send_control_msg(pdev,
L
Linus Torvalds 已提交
160 161 162
		SET_EP_STREAM_CTL,
		VIDEO_OUTPUT_CONTROL_FORMATTER,
		index,
163
		buf, buflen);
L
Linus Torvalds 已提交
164 165
}

166
int send_control_msg(struct pwc_device *pdev,
167 168 169
	u8 request, u16 value, void *buf, int buflen)
{
	return _send_control_msg(pdev,
170
		request, value, pdev->vcinterface, buf, buflen);
171 172
}

173
static int set_video_mode_Nala(struct pwc_device *pdev, int size, int frames)
L
Linus Torvalds 已提交
174 175 176 177 178 179 180 181
{
	unsigned char buf[3];
	int ret, fps;
	struct Nala_table_entry *pEntry;
	int frames2frames[31] =
	{ /* closest match of framerate */
	   0,  0,  0,  0,  4,  /*  0-4  */
	   5,  5,  7,  7, 10,  /*  5-9  */
182 183 184 185 186
	  10, 10, 12, 12, 15,  /* 10-14 */
	  15, 15, 15, 20, 20,  /* 15-19 */
	  20, 20, 20, 24, 24,  /* 20-24 */
	  24, 24, 24, 24, 24,  /* 25-29 */
	  24                   /* 30    */
L
Linus Torvalds 已提交
187
	};
188
	int frames2table[31] =
L
Linus Torvalds 已提交
189 190 191 192 193 194 195 196
	{ 0, 0, 0, 0, 0, /*  0-4  */
	  1, 1, 1, 2, 2, /*  5-9  */
	  3, 3, 4, 4, 4, /* 10-14 */
	  5, 5, 5, 5, 5, /* 15-19 */
	  6, 6, 6, 6, 7, /* 20-24 */
	  7, 7, 7, 7, 7, /* 25-29 */
	  7              /* 30    */
	};
197

L
Linus Torvalds 已提交
198 199 200 201 202 203 204 205
	if (size < 0 || size > PSZ_CIF || frames < 4 || frames > 25)
		return -EINVAL;
	frames = frames2frames[frames];
	fps = frames2table[frames];
	pEntry = &Nala_table[size][fps];
	if (pEntry->alternate == 0)
		return -EINVAL;

206
	memcpy(buf, pEntry->mode, 3);
207
	ret = send_video_command(pdev, pdev->vendpoint, buf, 3);
L
Linus Torvalds 已提交
208
	if (ret < 0) {
209
		PWC_DEBUG_MODULE("Failed to send video command... %d\n", ret);
L
Linus Torvalds 已提交
210 211
		return ret;
	}
212 213 214 215 216
	if (pEntry->compressed && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
		ret = pwc_dec1_init(pdev, pdev->type, pdev->release, buf);
		if (ret < 0)
			return ret;
	}
217

L
Linus Torvalds 已提交
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
	pdev->cmd_len = 3;
	memcpy(pdev->cmd_buf, buf, 3);

	/* Set various parameters */
	pdev->vframes = frames;
	pdev->vsize = size;
	pdev->valternate = pEntry->alternate;
	pdev->image = pwc_image_sizes[size];
	pdev->frame_size = (pdev->image.x * pdev->image.y * 3) / 2;
	if (pEntry->compressed) {
		if (pdev->release < 5) { /* 4 fold compression */
			pdev->vbandlength = 528;
			pdev->frame_size /= 4;
		}
		else {
			pdev->vbandlength = 704;
			pdev->frame_size /= 3;
		}
	}
	else
		pdev->vbandlength = 0;
	return 0;
}


243
static int set_video_mode_Timon(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
L
Linus Torvalds 已提交
244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
{
	unsigned char buf[13];
	const struct Timon_table_entry *pChoose;
	int ret, fps;

	if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
		return -EINVAL;
	if (size == PSZ_VGA && frames > 15)
		return -EINVAL;
	fps = (frames / 5) - 1;

	/* Find a supported framerate with progressively higher compression ratios
	   if the preferred ratio is not available.
	*/
	pChoose = NULL;
	while (compression <= 3) {
	   pChoose = &Timon_table[size][fps][compression];
	   if (pChoose->alternate != 0)
	     break;
	   compression++;
	}
	if (pChoose == NULL || pChoose->alternate == 0)
		return -ENOENT; /* Not supported. */

	memcpy(buf, pChoose->mode, 13);
	if (snapshot)
		buf[0] |= 0x80;
271
	ret = send_video_command(pdev, pdev->vendpoint, buf, 13);
L
Linus Torvalds 已提交
272 273 274
	if (ret < 0)
		return ret;

275 276 277 278 279
	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
		ret = pwc_dec23_init(pdev, pdev->type, buf);
		if (ret < 0)
			return ret;
	}
L
Linus Torvalds 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298

	pdev->cmd_len = 13;
	memcpy(pdev->cmd_buf, buf, 13);

	/* Set various parameters */
	pdev->vframes = frames;
	pdev->vsize = size;
	pdev->vsnapshot = snapshot;
	pdev->valternate = pChoose->alternate;
	pdev->image = pwc_image_sizes[size];
	pdev->vbandlength = pChoose->bandlength;
	if (pChoose->bandlength > 0)
		pdev->frame_size = (pChoose->bandlength * pdev->image.y) / 4;
	else
		pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
	return 0;
}


299
static int set_video_mode_Kiara(struct pwc_device *pdev, int size, int frames, int compression, int snapshot)
L
Linus Torvalds 已提交
300 301 302 303 304 305 306 307 308 309 310 311 312
{
	const struct Kiara_table_entry *pChoose = NULL;
	int fps, ret;
	unsigned char buf[12];
	struct Kiara_table_entry RawEntry = {6, 773, 1272, {0xAD, 0xF4, 0x10, 0x27, 0xB6, 0x24, 0x96, 0x02, 0x30, 0x05, 0x03, 0x80}};

	if (size >= PSZ_MAX || frames < 5 || frames > 30 || compression < 0 || compression > 3)
		return -EINVAL;
	if (size == PSZ_VGA && frames > 15)
		return -EINVAL;
	fps = (frames / 5) - 1;

	/* special case: VGA @ 5 fps and snapshot is raw bayer mode */
313
	if (size == PSZ_VGA && frames == 5 && snapshot && pdev->pixfmt != V4L2_PIX_FMT_YUV420)
L
Linus Torvalds 已提交
314
	{
315 316 317
		/* Only available in case the raw palette is selected or
		   we have the decompressor available. This mode is
		   only available in compressed form
L
Linus Torvalds 已提交
318
		*/
319 320
		PWC_DEBUG_SIZE("Choosing VGA/5 BAYER mode.\n");
		pChoose = &RawEntry;
L
Linus Torvalds 已提交
321 322 323
	}
	else
	{
324
		/* Find a supported framerate with progressively higher compression ratios
L
Linus Torvalds 已提交
325
		   if the preferred ratio is not available.
326
		   Skip this step when using RAW modes.
L
Linus Torvalds 已提交
327
		*/
328
		snapshot = 0;
L
Linus Torvalds 已提交
329 330 331 332 333 334 335 336 337 338
		while (compression <= 3) {
			pChoose = &Kiara_table[size][fps][compression];
			if (pChoose->alternate != 0)
				break;
			compression++;
		}
	}
	if (pChoose == NULL || pChoose->alternate == 0)
		return -ENOENT; /* Not supported. */

339
	PWC_TRACE("Using alternate setting %d.\n", pChoose->alternate);
340

L
Linus Torvalds 已提交
341 342 343 344 345 346
	/* usb_control_msg won't take staticly allocated arrays as argument?? */
	memcpy(buf, pChoose->mode, 12);
	if (snapshot)
		buf[0] |= 0x80;

	/* Firmware bug: video endpoint is 5, but commands are sent to endpoint 4 */
347
	ret = send_video_command(pdev, 4 /* pdev->vendpoint */, buf, 12);
L
Linus Torvalds 已提交
348 349 350
	if (ret < 0)
		return ret;

351 352 353 354 355
	if (pChoose->bandlength > 0 && pdev->pixfmt == V4L2_PIX_FMT_YUV420) {
		ret = pwc_dec23_init(pdev, pdev->type, buf);
		if (ret < 0)
			return ret;
	}
L
Linus Torvalds 已提交
356 357 358 359 360 361 362 363 364 365 366 367 368 369

	pdev->cmd_len = 12;
	memcpy(pdev->cmd_buf, buf, 12);
	/* All set and go */
	pdev->vframes = frames;
	pdev->vsize = size;
	pdev->vsnapshot = snapshot;
	pdev->valternate = pChoose->alternate;
	pdev->image = pwc_image_sizes[size];
	pdev->vbandlength = pChoose->bandlength;
	if (pdev->vbandlength > 0)
		pdev->frame_size = (pdev->vbandlength * pdev->image.y) / 4;
	else
		pdev->frame_size = (pdev->image.x * pdev->image.y * 12) / 8;
370 371
	PWC_TRACE("frame_size=%d, vframes=%d, vsize=%d, vsnapshot=%d, vbandlength=%d\n",
	    pdev->frame_size,pdev->vframes,pdev->vsize,pdev->vsnapshot,pdev->vbandlength);
L
Linus Torvalds 已提交
372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
	return 0;
}



/**
   @pdev: device structure
   @width: viewport width
   @height: viewport height
   @frame: framerate, in fps
   @compression: preferred compression ratio
   @snapshot: snapshot mode or streaming
 */
int pwc_set_video_mode(struct pwc_device *pdev, int width, int height, int frames, int compression, int snapshot)
{
387
	int ret, size;
L
Linus Torvalds 已提交
388

389
	PWC_DEBUG_FLOW("set_video_mode(%dx%d @ %d, pixfmt %08x).\n", width, height, frames, pdev->pixfmt);
L
Linus Torvalds 已提交
390 391
	size = pwc_decode_size(pdev, width, height);
	if (size < 0) {
392
		PWC_DEBUG_MODULE("Could not find suitable size.\n");
L
Linus Torvalds 已提交
393 394
		return -ERANGE;
	}
395
	PWC_TRACE("decode_size = %d.\n", size);
L
Linus Torvalds 已提交
396

397
	if (DEVICE_USE_CODEC1(pdev->type)) {
L
Linus Torvalds 已提交
398
		ret = set_video_mode_Nala(pdev, size, frames);
399

400
	} else if (DEVICE_USE_CODEC3(pdev->type)) {
L
Linus Torvalds 已提交
401
		ret = set_video_mode_Kiara(pdev, size, frames, compression, snapshot);
402 403 404

	} else {
		ret = set_video_mode_Timon(pdev, size, frames, compression, snapshot);
L
Linus Torvalds 已提交
405 406
	}
	if (ret < 0) {
407
		PWC_ERROR("Failed to set video mode %s@%d fps; return code = %d\n", size2name[size], frames, ret);
L
Linus Torvalds 已提交
408 409 410 411
		return ret;
	}
	pdev->view.x = width;
	pdev->view.y = height;
412
	pdev->vcompression = compression;
L
Linus Torvalds 已提交
413 414
	pdev->frame_total_size = pdev->frame_size + pdev->frame_header_size + pdev->frame_trailer_size;
	pwc_set_image_buffer_size(pdev);
415
	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);
L
Linus Torvalds 已提交
416 417 418
	return 0;
}

419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
static unsigned int pwc_get_fps_Nala(struct pwc_device *pdev, unsigned int index, unsigned int size)
{
	unsigned int i;

	for (i = 0; i < PWC_FPS_MAX_NALA; i++) {
		if (Nala_table[size][i].alternate) {
			if (index--==0) return Nala_fps_vector[i];
		}
	}
	return 0;
}

static unsigned int pwc_get_fps_Kiara(struct pwc_device *pdev, unsigned int index, unsigned int size)
{
	unsigned int i;

	for (i = 0; i < PWC_FPS_MAX_KIARA; i++) {
		if (Kiara_table[size][i][3].alternate) {
			if (index--==0) return Kiara_fps_vector[i];
		}
	}
	return 0;
}

static unsigned int pwc_get_fps_Timon(struct pwc_device *pdev, unsigned int index, unsigned int size)
{
	unsigned int i;

	for (i=0; i < PWC_FPS_MAX_TIMON; i++) {
		if (Timon_table[size][i][3].alternate) {
			if (index--==0) return Timon_fps_vector[i];
		}
	}
	return 0;
}

unsigned int pwc_get_fps(struct pwc_device *pdev, unsigned int index, unsigned int size)
{
	unsigned int ret;

	if (DEVICE_USE_CODEC1(pdev->type)) {
		ret = pwc_get_fps_Nala(pdev, index, size);

	} else if (DEVICE_USE_CODEC3(pdev->type)) {
		ret = pwc_get_fps_Kiara(pdev, index, size);

	} else {
		ret = pwc_get_fps_Timon(pdev, index, size);
	}

	return ret;
}

472 473
static void pwc_set_image_buffer_size(struct pwc_device *pdev)
{
474
	int factor = 0;
475

476 477 478
	/* for V4L2_PIX_FMT_YUV420 */
	switch (pdev->pixfmt) {
	case V4L2_PIX_FMT_YUV420:
479 480
		factor = 6;
		break;
481 482
	case V4L2_PIX_FMT_PWC1:
	case V4L2_PIX_FMT_PWC2:
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
		factor = 6; /* can be uncompressed YUV420P */
		break;
	}

	/* Set sizes in bytes */
	pdev->image.size = pdev->image.x * pdev->image.y * factor / 4;
	pdev->view.size  = pdev->view.x  * pdev->view.y  * factor / 4;

	/* Align offset, or you'll get some very weird results in
	   YUV420 mode... x must be multiple of 4 (to get the Y's in
	   place), and y even (or you'll mixup U & V). This is less of a
	   problem for YUV420P.
	 */
	pdev->offset.x = ((pdev->view.x - pdev->image.x) / 2) & 0xFFFC;
	pdev->offset.y = ((pdev->view.y - pdev->image.y) / 2) & 0xFFFE;
}

500
int pwc_get_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
L
Linus Torvalds 已提交
501 502
{
	int ret;
503
	u8 buf;
L
Linus Torvalds 已提交
504

505
	ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
L
Linus Torvalds 已提交
506 507 508
	if (ret < 0)
		return ret;

509 510
	*data = buf;
	return 0;
L
Linus Torvalds 已提交
511 512
}

513
int pwc_set_u8_ctrl(struct pwc_device *pdev, u8 request, u16 value, u8 data)
L
Linus Torvalds 已提交
514 515 516
{
	int ret;

517
	ret = send_control_msg(pdev, request, value, &data, sizeof(data));
L
Linus Torvalds 已提交
518 519 520
	if (ret < 0)
		return ret;

521
	return 0;
L
Linus Torvalds 已提交
522 523
}

524
int pwc_get_s8_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
L
Linus Torvalds 已提交
525 526
{
	int ret;
527
	s8 buf;
528

529
	ret = recv_control_msg(pdev, request, value, &buf, sizeof(buf));
L
Linus Torvalds 已提交
530 531 532
	if (ret < 0)
		return ret;

533
	*data = buf;
534
	return 0;
L
Linus Torvalds 已提交
535 536
}

537
int pwc_get_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, int *data)
L
Linus Torvalds 已提交
538 539
{
	int ret;
540
	u8 buf[2];
541

542
	ret = recv_control_msg(pdev, request, value, buf, sizeof(buf));
L
Linus Torvalds 已提交
543 544
	if (ret < 0)
		return ret;
545 546

	*data = (buf[1] << 8) | buf[0];
L
Linus Torvalds 已提交
547 548 549
	return 0;
}

550
int pwc_set_u16_ctrl(struct pwc_device *pdev, u8 request, u16 value, u16 data)
L
Linus Torvalds 已提交
551 552
{
	int ret;
553
	u8 buf[2];
554

555 556 557
	buf[0] = data & 0xff;
	buf[1] = data >> 8;
	ret = send_control_msg(pdev, request, value, buf, sizeof(buf));
L
Linus Torvalds 已提交
558 559 560 561 562 563
	if (ret < 0)
		return ret;

	return 0;
}

564
int pwc_button_ctrl(struct pwc_device *pdev, u16 value)
L
Linus Torvalds 已提交
565
{
566 567
	int ret;

568
	ret = send_control_msg(pdev, SET_STATUS_CTL, value, NULL, 0);
569 570
	if (ret < 0)
		return ret;
571

572 573 574
	return 0;
}

L
Linus Torvalds 已提交
575
/* POWER */
576
void pwc_camera_power(struct pwc_device *pdev, int power)
L
Linus Torvalds 已提交
577 578
{
	char buf;
579 580 581 582
	int r;

	if (!pdev->power_save)
		return;
L
Linus Torvalds 已提交
583 584

	if (pdev->type < 675 || (pdev->type < 730 && pdev->release < 6))
585
		return;	/* Not supported by Nala or Timon < release 6 */
L
Linus Torvalds 已提交
586 587 588 589 590

	if (power)
		buf = 0x00; /* active */
	else
		buf = 0xFF; /* power save */
591
	r = send_control_msg(pdev,
592 593
		SET_STATUS_CTL, SET_POWER_SAVE_MODE_FORMATTER,
		&buf, sizeof(buf));
L
Linus Torvalds 已提交
594

595 596 597 598
	if (r < 0)
		PWC_ERROR("Failed to power %s camera (%d)\n",
			  power ? "on" : "off", r);
}
L
Linus Torvalds 已提交
599 600 601 602

int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
{
	unsigned char buf[2];
603
	int r;
L
Linus Torvalds 已提交
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620

	if (pdev->type < 730)
		return 0;
	on_value /= 100;
	off_value /= 100;
	if (on_value < 0)
		on_value = 0;
	if (on_value > 0xff)
		on_value = 0xff;
	if (off_value < 0)
		off_value = 0;
	if (off_value > 0xff)
		off_value = 0xff;

	buf[0] = on_value;
	buf[1] = off_value;

621
	r = send_control_msg(pdev,
622
		SET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf));
623 624 625 626
	if (r < 0)
		PWC_ERROR("Failed to set LED on/off time (%d)\n", r);

	return r;
L
Linus Torvalds 已提交
627 628
}

629
static int pwc_get_leds(struct pwc_device *pdev, int *on_value, int *off_value)
L
Linus Torvalds 已提交
630 631 632
{
	unsigned char buf[2];
	int ret;
633

L
Linus Torvalds 已提交
634 635 636 637 638 639
	if (pdev->type < 730) {
		*on_value = -1;
		*off_value = -1;
		return 0;
	}

640 641
	ret = recv_control_msg(pdev,
		GET_STATUS_CTL, LED_FORMATTER, &buf, sizeof(buf));
L
Linus Torvalds 已提交
642 643 644 645 646 647 648
	if (ret < 0)
		return ret;
	*on_value = buf[0] * 100;
	*off_value = buf[1] * 100;
	return 0;
}

649
static int _pwc_mpt_reset(struct pwc_device *pdev, int flags)
L
Linus Torvalds 已提交
650 651
{
	unsigned char buf;
652

L
Linus Torvalds 已提交
653
	buf = flags & 0x03; // only lower two bits are currently used
654 655
	return send_control_msg(pdev,
		SET_MPT_CTL, PT_RESET_CONTROL_FORMATTER, &buf, sizeof(buf));
L
Linus Torvalds 已提交
656 657
}

658 659 660 661 662 663 664 665 666 667 668 669
int pwc_mpt_reset(struct pwc_device *pdev, int flags)
{
	int ret;
	ret = _pwc_mpt_reset(pdev, flags);
	if (ret >= 0) {
		pdev->pan_angle = 0;
		pdev->tilt_angle = 0;
	}
	return ret;
}

static int _pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
L
Linus Torvalds 已提交
670 671
{
	unsigned char buf[4];
672

L
Linus Torvalds 已提交
673
	/* set new relative angle; angles are expressed in degrees * 100,
674
	   but cam as .5 degree resolution, hence divide by 200. Also
L
Linus Torvalds 已提交
675 676 677 678 679 680 681 682 683
	   the angle must be multiplied by 64 before it's send to
	   the cam (??)
	 */
	pan  =  64 * pan  / 100;
	tilt = -64 * tilt / 100; /* positive tilt is down, which is not what the user would expect */
	buf[0] = pan & 0xFF;
	buf[1] = (pan >> 8) & 0xFF;
	buf[2] = tilt & 0xFF;
	buf[3] = (tilt >> 8) & 0xFF;
684 685
	return send_control_msg(pdev,
		SET_MPT_CTL, PT_RELATIVE_CONTROL_FORMATTER, &buf, sizeof(buf));
L
Linus Torvalds 已提交
686 687
}

688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716
int pwc_mpt_set_angle(struct pwc_device *pdev, int pan, int tilt)
{
	int ret;

	/* check absolute ranges */
	if (pan  < pdev->angle_range.pan_min  ||
	    pan  > pdev->angle_range.pan_max  ||
	    tilt < pdev->angle_range.tilt_min ||
	    tilt > pdev->angle_range.tilt_max)
		return -ERANGE;

	/* go to relative range, check again */
	pan  -= pdev->pan_angle;
	tilt -= pdev->tilt_angle;
	/* angles are specified in degrees * 100, thus the limit = 36000 */
	if (pan < -36000 || pan > 36000 || tilt < -36000 || tilt > 36000)
		return -ERANGE;

	ret = _pwc_mpt_set_angle(pdev, pan, tilt);
	if (ret >= 0) {
		pdev->pan_angle  += pan;
		pdev->tilt_angle += tilt;
	}
	if (ret == -EPIPE) /* stall -> out of range */
		ret = -ERANGE;
	return ret;
}

static int pwc_mpt_get_status(struct pwc_device *pdev, struct pwc_mpt_status *status)
L
Linus Torvalds 已提交
717 718 719
{
	int ret;
	unsigned char buf[5];
720

721 722
	ret = recv_control_msg(pdev,
		GET_MPT_CTL, PT_STATUS_FORMATTER, &buf, sizeof(buf));
L
Linus Torvalds 已提交
723 724 725 726 727 728 729 730
	if (ret < 0)
		return ret;
	status->status = buf[0] & 0x7; // 3 bits are used for reporting
	status->time_pan = (buf[1] << 8) + buf[2];
	status->time_tilt = (buf[3] << 8) + buf[4];
	return 0;
}

731
#ifdef CONFIG_USB_PWC_DEBUG
L
Linus Torvalds 已提交
732 733 734 735
int pwc_get_cmos_sensor(struct pwc_device *pdev, int *sensor)
{
	unsigned char buf;
	int ret = -1, request;
736

L
Linus Torvalds 已提交
737 738 739 740 741 742
	if (pdev->type < 675)
		request = SENSOR_TYPE_FORMATTER1;
	else if (pdev->type < 730)
		return -1; /* The Vesta series doesn't have this call */
	else
		request = SENSOR_TYPE_FORMATTER2;
743

744 745
	ret = recv_control_msg(pdev,
		GET_STATUS_CTL, request, &buf, sizeof(buf));
L
Linus Torvalds 已提交
746 747 748 749 750 751 752 753
	if (ret < 0)
		return ret;
	if (pdev->type < 675)
		*sensor = buf | 0x100;
	else
		*sensor = buf;
	return 0;
}
754
#endif
L
Linus Torvalds 已提交
755 756 757 758

 /* End of Add-Ons                                    */
 /* ************************************************* */

759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777
/* Linux 2.5.something and 2.6 pass direct pointers to arguments of
   ioctl() calls. With 2.4, you have to do tedious copy_from_user()
   and copy_to_user() calls. With these macros we circumvent this,
   and let me maintain only one source file. The functionality is
   exactly the same otherwise.
 */

/* define local variable for arg */
#define ARG_DEF(ARG_type, ARG_name)\
	ARG_type *ARG_name = arg;
/* copy arg to local variable */
#define ARG_IN(ARG_name) /* nothing */
/* argument itself (referenced) */
#define ARGR(ARG_name) (*ARG_name)
/* argument address */
#define ARGA(ARG_name) ARG_name
/* copy local variable to arg */
#define ARG_OUT(ARG_name) /* nothing */

778 779 780 781 782 783 784 785 786 787 788 789 790
/*
 * Our ctrls use native values, but the old custom pwc ioctl interface expects
 * values from 0 - 65535, define 2 helper functions to scale things. */
static int pwc_ioctl_g_ctrl(struct v4l2_ctrl *ctrl)
{
	return v4l2_ctrl_g_ctrl(ctrl) * 65535 / ctrl->maximum;
}

static int pwc_ioctl_s_ctrl(struct v4l2_ctrl *ctrl, int val)
{
	return v4l2_ctrl_s_ctrl(ctrl, val * ctrl->maximum / 65535);
}

791
long pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
L
Linus Torvalds 已提交
792
{
793
	long ret = 0;
L
Linus Torvalds 已提交
794 795 796

	switch(cmd) {
	case VIDIOCPWCRUSER:
797
		ret = pwc_button_ctrl(pdev, RESTORE_USER_DEFAULTS_FORMATTER);
L
Linus Torvalds 已提交
798
		break;
799

L
Linus Torvalds 已提交
800
	case VIDIOCPWCSUSER:
801
		ret = pwc_button_ctrl(pdev, SAVE_USER_DEFAULTS_FORMATTER);
L
Linus Torvalds 已提交
802
		break;
803

L
Linus Torvalds 已提交
804
	case VIDIOCPWCFACTORY:
805
		ret = pwc_button_ctrl(pdev, RESTORE_FACTORY_DEFAULTS_FORMATTER);
L
Linus Torvalds 已提交
806
		break;
807

L
Linus Torvalds 已提交
808
	case VIDIOCPWCSCQUAL:
809
	{
810
		ARG_DEF(int, qual)
L
Linus Torvalds 已提交
811

812
		if (vb2_is_streaming(&pdev->vb_queue)) {
813 814 815 816
			ret = -EBUSY;
			break;
		}

817 818
		ARG_IN(qual)
		if (ARGR(qual) < 0 || ARGR(qual) > 3)
L
Linus Torvalds 已提交
819 820
			ret = -EINVAL;
		else
821
			ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, ARGR(qual), pdev->vsnapshot);
L
Linus Torvalds 已提交
822 823
		break;
	}
824

L
Linus Torvalds 已提交
825 826
	case VIDIOCPWCGCQUAL:
	{
827 828 829 830
		ARG_DEF(int, qual)

		ARGR(qual) = pdev->vcompression;
		ARG_OUT(qual)
L
Linus Torvalds 已提交
831 832
		break;
	}
833

L
Linus Torvalds 已提交
834 835
	case VIDIOCPWCPROBE:
	{
836 837
		ARG_DEF(struct pwc_probe, probe)

838
		strcpy(ARGR(probe).name, pdev->vdev.name);
839 840
		ARGR(probe).type = pdev->type;
		ARG_OUT(probe)
L
Linus Torvalds 已提交
841 842 843 844 845
		break;
	}

	case VIDIOCPWCGSERIAL:
	{
846 847 848 849
		ARG_DEF(struct pwc_serial, serial)

		strcpy(ARGR(serial).serial, pdev->serial);
		ARG_OUT(serial)
L
Linus Torvalds 已提交
850 851 852 853 854
		break;
	}

	case VIDIOCPWCSAGC:
	{
855 856
		ARG_DEF(int, agc)
		ARG_IN(agc)
857 858 859
		ret = v4l2_ctrl_s_ctrl(pdev->autogain, ARGR(agc) < 0);
		if (ret == 0 && ARGR(agc) >= 0)
			ret = pwc_ioctl_s_ctrl(pdev->gain, ARGR(agc));
L
Linus Torvalds 已提交
860 861
		break;
	}
862

L
Linus Torvalds 已提交
863 864
	case VIDIOCPWCGAGC:
	{
865
		ARG_DEF(int, agc)
866 867 868 869
		if (v4l2_ctrl_g_ctrl(pdev->autogain))
			ARGR(agc) = -1;
		else
			ARGR(agc) = pwc_ioctl_g_ctrl(pdev->gain);
870
		ARG_OUT(agc)
L
Linus Torvalds 已提交
871 872
		break;
	}
873

L
Linus Torvalds 已提交
874 875
	case VIDIOCPWCSSHUTTER:
	{
876 877 878 879 880 881 882
		ARG_DEF(int, shutter)
		ARG_IN(shutter)
		ret = v4l2_ctrl_s_ctrl(pdev->exposure_auto,
				       /* Menu idx 0 = auto, idx 1 = manual */
				       ARGR(shutter) >= 0);
		if (ret == 0 && ARGR(shutter) >= 0)
			ret = pwc_ioctl_s_ctrl(pdev->exposure, ARGR(shutter));
L
Linus Torvalds 已提交
883 884
		break;
	}
885 886

	case VIDIOCPWCSAWB:
L
Linus Torvalds 已提交
887
	{
888 889
		ARG_DEF(struct pwc_whitebalance, wb)
		ARG_IN(wb)
890 891 892 893 894 895 896 897
		ret = v4l2_ctrl_s_ctrl(pdev->auto_white_balance,
				       ARGR(wb).mode);
		if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
			ret = pwc_ioctl_s_ctrl(pdev->red_balance,
					       ARGR(wb).manual_red);
		if (ret == 0 && ARGR(wb).mode == PWC_WB_MANUAL)
			ret = pwc_ioctl_s_ctrl(pdev->blue_balance,
					       ARGR(wb).manual_blue);
L
Linus Torvalds 已提交
898 899 900 901 902
		break;
	}

	case VIDIOCPWCGAWB:
	{
903
		ARG_DEF(struct pwc_whitebalance, wb)
904 905 906 907 908
		ARGR(wb).mode = v4l2_ctrl_g_ctrl(pdev->auto_white_balance);
		ARGR(wb).manual_red = ARGR(wb).read_red =
			pwc_ioctl_g_ctrl(pdev->red_balance);
		ARGR(wb).manual_blue = ARGR(wb).read_blue =
			pwc_ioctl_g_ctrl(pdev->blue_balance);
909
		ARG_OUT(wb)
L
Linus Torvalds 已提交
910 911
		break;
	}
912

L
Linus Torvalds 已提交
913 914
	case VIDIOCPWCSAWBSPEED:
	{
915
		ARG_DEF(struct pwc_wb_speed, wbs)
916

917
		if (ARGR(wbs).control_speed > 0) {
918 919
			ret = pwc_ioctl_s_ctrl(pdev->awb_speed,
					       ARGR(wbs).control_speed);
L
Linus Torvalds 已提交
920
		}
921 922 923
		if (ret == 0 && ARGR(wbs).control_delay > 0) {
			ret = pwc_ioctl_s_ctrl(pdev->awb_delay,
					       ARGR(wbs).control_delay);
L
Linus Torvalds 已提交
924 925 926
		}
		break;
	}
927

L
Linus Torvalds 已提交
928 929
	case VIDIOCPWCGAWBSPEED:
	{
930
		ARG_DEF(struct pwc_wb_speed, wbs)
931

932 933
		ARGR(wbs).control_speed = v4l2_ctrl_g_ctrl(pdev->awb_speed);
		ARGR(wbs).control_delay = v4l2_ctrl_g_ctrl(pdev->awb_delay);
934
		ARG_OUT(wbs)
L
Linus Torvalds 已提交
935 936 937
		break;
	}

938
	case VIDIOCPWCSLED:
L
Linus Torvalds 已提交
939
	{
940 941 942 943
		ARG_DEF(struct pwc_leds, leds)

		ARG_IN(leds)
		ret = pwc_set_leds(pdev, ARGR(leds).led_on, ARGR(leds).led_off);
944
		break;
L
Linus Torvalds 已提交
945 946 947 948 949
	}


	case VIDIOCPWCGLED:
	{
950 951 952 953
		ARG_DEF(struct pwc_leds, leds)

		ret = pwc_get_leds(pdev, &ARGR(leds).led_on, &ARGR(leds).led_off);
		ARG_OUT(leds)
L
Linus Torvalds 已提交
954 955 956 957 958
		break;
	}

	case VIDIOCPWCSCONTOUR:
	{
959 960
		ARG_DEF(int, contour)
		ARG_IN(contour)
961 962 963
		ret = v4l2_ctrl_s_ctrl(pdev->autocontour, ARGR(contour) < 0);
		if (ret == 0 && ARGR(contour) >= 0)
			ret = pwc_ioctl_s_ctrl(pdev->contour, ARGR(contour));
L
Linus Torvalds 已提交
964 965
		break;
	}
966

L
Linus Torvalds 已提交
967 968
	case VIDIOCPWCGCONTOUR:
	{
969
		ARG_DEF(int, contour)
970 971 972 973
		if (v4l2_ctrl_g_ctrl(pdev->autocontour))
			ARGR(contour) = -1;
		else
			ARGR(contour) = pwc_ioctl_g_ctrl(pdev->contour);
974
		ARG_OUT(contour)
L
Linus Torvalds 已提交
975 976
		break;
	}
977

L
Linus Torvalds 已提交
978 979
	case VIDIOCPWCSBACKLIGHT:
	{
980 981
		ARG_DEF(int, backlight)
		ARG_IN(backlight)
982
		ret = v4l2_ctrl_s_ctrl(pdev->backlight, ARGR(backlight));
L
Linus Torvalds 已提交
983 984 985 986 987
		break;
	}

	case VIDIOCPWCGBACKLIGHT:
	{
988
		ARG_DEF(int, backlight)
989
		ARGR(backlight) = v4l2_ctrl_g_ctrl(pdev->backlight);
990
		ARG_OUT(backlight)
L
Linus Torvalds 已提交
991 992
		break;
	}
993

L
Linus Torvalds 已提交
994 995
	case VIDIOCPWCSFLICKER:
	{
996 997
		ARG_DEF(int, flicker)
		ARG_IN(flicker)
998
		ret = v4l2_ctrl_s_ctrl(pdev->flicker, ARGR(flicker));
L
Linus Torvalds 已提交
999 1000 1001 1002 1003
		break;
	}

	case VIDIOCPWCGFLICKER:
	{
1004
		ARG_DEF(int, flicker)
1005
		ARGR(flicker) = v4l2_ctrl_g_ctrl(pdev->flicker);
1006
		ARG_OUT(flicker)
L
Linus Torvalds 已提交
1007 1008
		break;
	}
1009

L
Linus Torvalds 已提交
1010 1011
	case VIDIOCPWCSDYNNOISE:
	{
1012 1013
		ARG_DEF(int, dynnoise)
		ARG_IN(dynnoise)
1014
		ret = v4l2_ctrl_s_ctrl(pdev->noise_reduction, ARGR(dynnoise));
L
Linus Torvalds 已提交
1015 1016
		break;
	}
1017

L
Linus Torvalds 已提交
1018 1019
	case VIDIOCPWCGDYNNOISE:
	{
1020
		ARG_DEF(int, dynnoise)
1021
		ARGR(dynnoise) = v4l2_ctrl_g_ctrl(pdev->noise_reduction);
1022
		ARG_OUT(dynnoise);
L
Linus Torvalds 已提交
1023 1024 1025 1026 1027
		break;
	}

	case VIDIOCPWCGREALSIZE:
	{
1028 1029 1030 1031 1032
		ARG_DEF(struct pwc_imagesize, size)

		ARGR(size).width = pdev->image.x;
		ARGR(size).height = pdev->image.y;
		ARG_OUT(size)
L
Linus Torvalds 已提交
1033
		break;
1034 1035 1036 1037 1038 1039
	}

	case VIDIOCPWCMPTRESET:
	{
		if (pdev->features & FEATURE_MOTOR_PANTILT)
		{
1040
			ARG_DEF(int, flags)
L
Linus Torvalds 已提交
1041

1042 1043
			ARG_IN(flags)
			ret = pwc_mpt_reset(pdev, ARGR(flags));
1044 1045 1046 1047 1048 1049 1050 1051 1052 1053 1054 1055
		}
		else
		{
			ret = -ENXIO;
		}
		break;
	}

	case VIDIOCPWCMPTGRANGE:
	{
		if (pdev->features & FEATURE_MOTOR_PANTILT)
		{
1056 1057 1058 1059
			ARG_DEF(struct pwc_mpt_range, range)

			ARGR(range) = pdev->angle_range;
			ARG_OUT(range)
1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073
		}
		else
		{
			ret = -ENXIO;
		}
		break;
	}

	case VIDIOCPWCMPTSANGLE:
	{
		int new_pan, new_tilt;

		if (pdev->features & FEATURE_MOTOR_PANTILT)
		{
1074 1075 1076
			ARG_DEF(struct pwc_mpt_angles, angles)

			ARG_IN(angles)
L
Linus Torvalds 已提交
1077 1078 1079
			/* The camera can only set relative angles, so
			   do some calculations when getting an absolute angle .
			 */
1080
			if (ARGR(angles).absolute)
L
Linus Torvalds 已提交
1081
			{
1082 1083
				new_pan  = ARGR(angles).pan;
				new_tilt = ARGR(angles).tilt;
1084 1085 1086
			}
			else
			{
1087 1088
				new_pan  = pdev->pan_angle  + ARGR(angles).pan;
				new_tilt = pdev->tilt_angle + ARGR(angles).tilt;
L
Linus Torvalds 已提交
1089
			}
1090
			ret = pwc_mpt_set_angle(pdev, new_pan, new_tilt);
1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103
		}
		else
		{
			ret = -ENXIO;
		}
		break;
	}

	case VIDIOCPWCMPTGANGLE:
	{

		if (pdev->features & FEATURE_MOTOR_PANTILT)
		{
1104
			ARG_DEF(struct pwc_mpt_angles, angles)
1105

1106 1107 1108 1109
			ARGR(angles).absolute = 1;
			ARGR(angles).pan  = pdev->pan_angle;
			ARGR(angles).tilt = pdev->tilt_angle;
			ARG_OUT(angles)
1110 1111 1112 1113 1114 1115 1116 1117 1118 1119 1120 1121
		}
		else
		{
			ret = -ENXIO;
		}
		break;
	}

	case VIDIOCPWCMPTSTATUS:
	{
		if (pdev->features & FEATURE_MOTOR_PANTILT)
		{
1122 1123 1124 1125
			ARG_DEF(struct pwc_mpt_status, status)

			ret = pwc_mpt_get_status(pdev, ARGA(status));
			ARG_OUT(status)
1126 1127 1128 1129 1130 1131
		}
		else
		{
			ret = -ENXIO;
		}
		break;
L
Linus Torvalds 已提交
1132 1133 1134 1135
	}

	case VIDIOCPWCGVIDCMD:
	{
1136 1137 1138 1139 1140 1141 1142 1143 1144
		ARG_DEF(struct pwc_video_command, vcmd);

		ARGR(vcmd).type = pdev->type;
		ARGR(vcmd).release = pdev->release;
		ARGR(vcmd).command_len = pdev->cmd_len;
		memcpy(&ARGR(vcmd).command_buf, pdev->cmd_buf, pdev->cmd_len);
		ARGR(vcmd).bandlength = pdev->vbandlength;
		ARGR(vcmd).frame_size = pdev->frame_size;
		ARG_OUT(vcmd)
L
Linus Torvalds 已提交
1145 1146
		break;
	}
1147
	/*
L
Linus Torvalds 已提交
1148 1149
	case VIDIOCPWCGVIDTABLE:
	{
1150 1151 1152 1153
		ARG_DEF(struct pwc_table_init_buffer, table);
		ARGR(table).len = pdev->cmd_len;
		memcpy(&ARGR(table).buffer, pdev->decompress_data, pdev->decompressor->table_size);
		ARG_OUT(table)
L
Linus Torvalds 已提交
1154 1155 1156 1157 1158 1159 1160 1161
		break;
	}
	*/

	default:
		ret = -ENOIOCTLCMD;
		break;
	}
1162

L
Linus Torvalds 已提交
1163 1164 1165 1166 1167 1168
	if (ret > 0)
		return 0;
	return ret;
}


1169
/* vim: set cinoptions= formatoptions=croql cindent shiftwidth=8 tabstop=8: */