patch-2.4.19 linux-2.4.19/drivers/usb/pwc-ctrl.c
Next file: linux-2.4.19/drivers/usb/pwc-if.c
Previous file: linux-2.4.19/drivers/usb/printer.c
Back to the patch index
Back to the overall index
- Lines: 683
- Date:
Fri Aug 2 17:39:45 2002
- Orig file:
linux-2.4.18/drivers/usb/pwc-ctrl.c
- Orig date:
Mon Feb 25 11:38:07 2002
diff -urN linux-2.4.18/drivers/usb/pwc-ctrl.c linux-2.4.19/drivers/usb/pwc-ctrl.c
@@ -1,7 +1,7 @@
/* Driver for Philips webcam
Functions that send various control messages to the webcam, including
video modes.
- (C) 1999-2001 Nemosoft Unv. (webcam@smcc.demon.nl)
+ (C) 1999-2002 Nemosoft Unv. (webcam@smcc.demon.nl)
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
@@ -78,10 +78,12 @@
#define READ_SHUTTER_FORMATTER 0x0600
#define READ_RED_GAIN_FORMATTER 0x0700
#define READ_BLUE_GAIN_FORMATTER 0x0800
+#define SENSOR_TYPE_FORMATTER1 0x0C00
#define READ_RAW_Y_MEAN_FORMATTER 0x3100
#define SET_POWER_SAVE_MODE_FORMATTER 0x3200
#define MIRROR_IMAGE_FORMATTER 0x3300
#define LED_FORMATTER 0x3400
+#define SENSOR_TYPE_FORMATTER2 0x3700
/* Formatters for the Video Endpoint controls [GS]ET_EP_STREAM_CTL */
#define VIDEO_OUTPUT_CONTROL_FORMATTER 0x0100
@@ -162,6 +164,21 @@
/****************************************************************************/
+#define SendControlMsg(request, value, buflen) \
+ usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0), \
+ request, \
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
+ value, \
+ pdev->vcinterface, \
+ &buf, buflen, HZ / 2)
+
+#define RecvControlMsg(request, value, buflen) \
+ usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0), \
+ request, \
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, \
+ value, \
+ pdev->vcinterface, \
+ &buf, buflen, HZ / 2)
#if PWC_DEBUG
@@ -241,7 +258,7 @@
ret = send_video_command(pdev->udev, pdev->vendpoint, buf, 3);
if (ret < 0)
return ret;
- if (pEntry->compressed)
+ if (pEntry->compressed && pdev->decompressor != NULL)
pdev->decompressor->init(pdev->release, buf, pdev->decompress_data);
/* Set various parameters */
@@ -911,7 +928,7 @@
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
- GET_STATUS_CTL,
+ GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_RED_GAIN_FORMATTER,
pdev->vcinterface,
@@ -950,7 +967,7 @@
int ret;
ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
- GET_STATUS_CTL,
+ GET_CHROM_CTL,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
PRESET_MANUAL_BLUE_GAIN_FORMATTER,
pdev->vcinterface,
@@ -962,6 +979,7 @@
return (buf << 8);
}
+
/* The following two functions are different, since they only read the
internal red/blue gains, which may be different from the manual
gains set or read above.
@@ -997,11 +1015,74 @@
&buf, 1, HZ / 2);
if (ret < 0)
- return ret;
+ return ret;
return (buf << 8);
}
+
+static inline int pwc_set_wb_speed(struct pwc_device *pdev, int speed)
+{
+ unsigned char buf;
+
+ /* useful range is 0x01..0x20 */
+ buf = speed / 0x7f0;
+ return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+ SET_CHROM_CTL,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ AWB_CONTROL_SPEED_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+}
+
+static inline int pwc_get_wb_speed(struct pwc_device *pdev)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+ GET_CHROM_CTL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ AWB_CONTROL_SPEED_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+ return (buf * 0x7f0);
+}
+
+
+static inline int pwc_set_wb_delay(struct pwc_device *pdev, int delay)
+{
+ unsigned char buf;
+
+ /* useful range is 0x01..0x3F */
+ buf = (delay >> 10);
+ return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+ SET_CHROM_CTL,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ AWB_CONTROL_DELAY_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+}
+
+static inline int pwc_get_wb_delay(struct pwc_device *pdev)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+ GET_CHROM_CTL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ AWB_CONTROL_DELAY_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+ return (buf << 10);
+}
+
+
int pwc_set_leds(struct pwc_device *pdev, int on_value, int off_value)
{
unsigned char buf[2];
@@ -1055,53 +1136,242 @@
return 0;
}
+static inline int pwc_set_contour(struct pwc_device *pdev, int contour)
+{
+ unsigned char buf;
+ int ret;
+
+ if (contour < 0)
+ buf = 0xff; /* auto contour on */
+ else
+ buf = 0x0; /* auto contour off */
+ ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+ SET_LUM_CTL,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ AUTO_CONTOUR_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+
+ if (contour < 0)
+ return 0;
+ if (contour > 0xffff)
+ contour = 0xffff;
+
+ buf = (contour >> 10); /* contour preset is [0..3f] */
+ ret = usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+ SET_LUM_CTL,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ PRESET_CONTOUR_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+ return 0;
+}
+
+static inline int pwc_get_contour(struct pwc_device *pdev, int *contour)
+{
+ unsigned char buf;
+ int ret;
+
+ ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+ GET_LUM_CTL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ AUTO_CONTOUR_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+
+ if (buf == 0) {
+ /* auto mode off, query current preset value */
+ ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+ GET_LUM_CTL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ PRESET_CONTOUR_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+ *contour = (buf << 10);
+ }
+ else
+ *contour = -1;
+ return 0;
+}
+
+
+static inline int pwc_set_backlight(struct pwc_device *pdev, int backlight)
+{
+ unsigned char buf;
+
+ if (backlight)
+ buf = 0xff;
+ else
+ buf = 0x0;
+ return usb_control_msg(pdev->udev, usb_sndctrlpipe(pdev->udev, 0),
+ SET_LUM_CTL,
+ USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ BACK_LIGHT_COMPENSATION_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+}
+
+static inline int pwc_get_backlight(struct pwc_device *pdev)
+{
+ int ret;
+ unsigned char buf;
+
+ ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+ GET_LUM_CTL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ BACK_LIGHT_COMPENSATION_FORMATTER,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+ return buf;
+}
+
+
+static inline int pwc_set_flicker(struct pwc_device *pdev, int flicker)
+{
+ unsigned char buf;
+
+ if (flicker)
+ buf = 0xff;
+ else
+ buf = 0x0;
+ return SendControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
+}
+
+static inline int pwc_get_flicker(struct pwc_device *pdev)
+{
+ int ret;
+ unsigned char buf;
+
+ ret = RecvControlMsg(SET_LUM_CTL, FLICKERLESS_MODE_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+ return buf;
+}
+
+
+static inline int pwc_set_dynamic_noise(struct pwc_device *pdev, int noise)
+{
+ unsigned char buf;
+
+ if (noise < 0)
+ noise = 0;
+ if (noise > 3)
+ noise = 3;
+ buf = noise;
+ return SendControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
+}
+
+static inline int pwc_get_dynamic_noise(struct pwc_device *pdev)
+{
+ int ret;
+ unsigned char buf;
+
+ ret = RecvControlMsg(SET_LUM_CTL, DYNAMIC_NOISE_CONTROL_FORMATTER, 1);
+ if (ret < 0)
+ return ret;
+Debug("pwc_get_dynamic_noise = %d\n", buf);
+ return buf;
+}
+
+
+int pwc_get_cmos_sensor(struct pwc_device *pdev)
+{
+ unsigned char buf;
+ int ret = -1, request;
+
+ 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;
+
+ ret = usb_control_msg(pdev->udev, usb_rcvctrlpipe(pdev->udev, 0),
+ GET_STATUS_CTL,
+ USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+ request,
+ pdev->vcinterface,
+ &buf, 1, HZ / 2);
+ if (ret < 0)
+ return ret;
+ if (pdev->type < 675)
+ return buf | 0x100;
+ else
+ return buf;
+}
+
+
/* End of Add-Ons */
/* ************************************************* */
int pwc_ioctl(struct pwc_device *pdev, unsigned int cmd, void *arg)
{
+ int ret = 0;
+
switch(cmd) {
case VIDIOCPWCRUSER:
{
if (pwc_restore_user(pdev))
- return -EINVAL;
+ ret = -EINVAL;
break;
}
case VIDIOCPWCSUSER:
{
if (pwc_save_user(pdev))
- return -EINVAL;
+ ret = -EINVAL;
break;
}
case VIDIOCPWCFACTORY:
{
if (pwc_restore_factory(pdev))
- return -EINVAL;
+ ret = -EINVAL;
break;
}
case VIDIOCPWCSCQUAL:
{
- int qual, ret;
+ int qual;
if (copy_from_user(&qual, arg, sizeof(int)))
- return -EFAULT;
-
- if (qual < 0 || qual > 3)
- return -EINVAL;
- ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, qual, pdev->vsnapshot);
- if (ret < 0)
- return ret;
- pdev->vcompression = qual;
+ ret = -EFAULT;
+ else {
+ if (qual < 0 || qual > 3)
+ ret = -EINVAL;
+ else
+ ret = pwc_try_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, qual, pdev->vsnapshot);
+ if (ret >= 0)
+ pdev->vcompression = qual;
+ }
break;
}
case VIDIOCPWCGCQUAL:
{
if (copy_to_user(arg, &pdev->vcompression, sizeof(int)))
- return -EFAULT;
+ ret = -EFAULT;
+ break;
+ }
+
+ case VIDIOCPWCPROBE:
+ {
+ struct pwc_probe probe;
+
+ strcpy(probe.name, pdev->vdev->name);
+ probe.type = pdev->type;
+ if (copy_to_user(arg, &probe, sizeof(probe)))
+ ret = -EFAULT;
break;
}
@@ -1110,10 +1380,10 @@
int agc;
if (copy_from_user(&agc, arg, sizeof(agc)))
- return -EFAULT;
+ ret = -EFAULT;
else {
if (pwc_set_agc(pdev, agc < 0 ? 1 : 0, agc))
- return -EINVAL;
+ ret = -EINVAL;
}
break;
}
@@ -1123,42 +1393,36 @@
int agc;
if (pwc_get_agc(pdev, &agc))
- return -EINVAL;
- if (copy_to_user(arg, &agc, sizeof(agc)))
- return -EFAULT;
+ ret = -EINVAL;
+ else
+ if (copy_to_user(arg, &agc, sizeof(agc)))
+ ret = -EFAULT;
break;
}
case VIDIOCPWCSSHUTTER:
{
- int shutter_speed, ret;
+ int shutter_speed;
if (copy_from_user(&shutter_speed, arg, sizeof(shutter_speed)))
- return -EFAULT;
- else {
+ ret = -EFAULT;
+ else
ret = pwc_set_shutter_speed(pdev, shutter_speed < 0 ? 1 : 0, shutter_speed);
- if (ret < 0)
- return ret;
- }
break;
}
-
- /* ************************************************* */
- /* Begin of Add-Ons for color compensation */
-
case VIDIOCPWCSAWB:
{
struct pwc_whitebalance wb;
- int ret;
if (copy_from_user(&wb, arg, sizeof(wb)))
- return -EFAULT;
-
- ret = pwc_set_awb(pdev, wb.mode);
- if (ret >= 0 && wb.mode == PWC_WB_MANUAL) {
- pwc_set_red_gain(pdev, wb.manual_red);
- pwc_set_blue_gain(pdev, wb.manual_blue);
+ ret = -EFAULT;
+ else {
+ ret = pwc_set_awb(pdev, wb.mode);
+ if (ret >= 0 && wb.mode == PWC_WB_MANUAL) {
+ pwc_set_red_gain(pdev, wb.manual_red);
+ pwc_set_blue_gain(pdev, wb.manual_blue);
+ }
}
break;
}
@@ -1170,55 +1434,176 @@
memset(&wb, 0, sizeof(wb));
wb.mode = pwc_get_awb(pdev);
if (wb.mode < 0)
- return -EINVAL;
- wb.manual_red = pwc_get_red_gain(pdev);
- wb.manual_blue = pwc_get_blue_gain(pdev);
- if (wb.mode == PWC_WB_AUTO) {
- wb.read_red = pwc_read_red_gain(pdev);
- wb.read_blue = pwc_read_blue_gain(pdev);
+ ret = -EINVAL;
+ else {
+ if (wb.mode == PWC_WB_MANUAL) {
+ wb.manual_red = pwc_get_red_gain(pdev);
+ wb.manual_blue = pwc_get_blue_gain(pdev);
+ }
+ if (wb.mode == PWC_WB_AUTO) {
+ wb.read_red = pwc_read_red_gain(pdev);
+ wb.read_blue = pwc_read_blue_gain(pdev);
+ }
+ if (copy_to_user(arg, &wb, sizeof(wb)))
+ ret= -EFAULT;
}
- if (copy_to_user(arg, &wb, sizeof(wb)))
- return -EFAULT;
+ break;
+ }
+
+ case VIDIOCPWCSAWBSPEED:
+ {
+ struct pwc_wb_speed wbs;
+
+ if (copy_from_user(&wbs, arg, sizeof(wbs)))
+ ret = -EFAULT;
+ else {
+ if (wbs.control_speed > 0) {
+ ret = pwc_set_wb_speed(pdev, wbs.control_speed);
+ }
+ if (wbs.control_delay > 0) {
+ ret = pwc_set_wb_delay(pdev, wbs.control_delay);
+ }
+ }
+ break;
+ }
+
+ case VIDIOCPWCGAWBSPEED:
+ {
+ struct pwc_wb_speed wbs;
+
+ ret = pwc_get_wb_speed(pdev);
+ if (ret < 0)
+ break;
+ wbs.control_speed = ret;
+ ret = pwc_get_wb_delay(pdev);
+ if (ret < 0)
+ break;
+ wbs.control_delay = ret;
+ if (copy_to_user(arg, &wbs, sizeof(wbs)))
+ ret = -EFAULT;
break;
}
case VIDIOCPWCSLED:
{
- int ret;
struct pwc_leds leds;
if (copy_from_user(&leds, arg, sizeof(leds)))
- return -EFAULT;
-
- ret = pwc_set_leds(pdev, leds.led_on, leds.led_off);
- if (ret<0)
- return ret;
- break;
+ ret = -EFAULT;
+ else
+ ret = pwc_set_leds(pdev, leds.led_on, leds.led_off);
+ break;
}
-
case VIDIOCPWCGLED:
{
- int led;
struct pwc_leds leds;
- led = pwc_get_leds(pdev, &leds.led_on, &leds.led_off);
- if (led < 0)
- return -EINVAL;
+ ret = pwc_get_leds(pdev, &leds.led_on, &leds.led_off);
+ if (ret < 0)
+ break;
if (copy_to_user(arg, &leds, sizeof(leds)))
- return -EFAULT;
+ ret = -EFAULT;
break;
}
- /* End of Add-Ons */
- /* ************************************************* */
+ case VIDIOCPWCSCONTOUR:
+ {
+ int contour;
+
+ if (copy_from_user(&contour, arg, sizeof(contour)))
+ ret = -EFAULT;
+ else
+ ret = pwc_set_contour(pdev, contour);
+ break;
+ }
+
+ case VIDIOCPWCGCONTOUR:
+ {
+ int contour;
+
+ ret = pwc_get_contour(pdev, &contour);
+ if (ret < 0)
+ break;
+
+ if (copy_to_user(arg, &contour, sizeof(contour)))
+ ret = -EFAULT;
+ break;
+ }
+
+ case VIDIOCPWCSBACKLIGHT:
+ {
+ int backlight;
+
+ if (copy_from_user(&backlight, arg, sizeof(backlight)))
+ ret = -EFAULT;
+ else
+ ret = pwc_set_backlight(pdev, backlight);
+ break;
+ }
+
+ case VIDIOCPWCGBACKLIGHT:
+ {
+ ret = pwc_get_backlight(pdev);
+ if (ret < 0)
+ break;
+ if (copy_to_user(arg, &ret, sizeof(ret)))
+ ret = -EFAULT;
+ break;
+ }
+
+ case VIDIOCPWCSFLICKER:
+ {
+ int flicker;
+
+ if (copy_from_user(&flicker, arg, sizeof(flicker)))
+ ret = -EFAULT;
+ else
+ ret = pwc_set_flicker(pdev, flicker);
+ break;
+ }
+
+ case VIDIOCPWCGFLICKER:
+ {
+ ret = pwc_get_flicker(pdev);
+ if (ret < 0)
+ break;
+ if (copy_to_user(arg, &ret, sizeof(ret)))
+ ret = -EFAULT;
+ break;
+ }
+
+ case VIDIOCPWCSDYNNOISE:
+ {
+ int dynnoise;
+
+ if (copy_from_user(&dynnoise, arg, sizeof(dynnoise)))
+ ret = -EFAULT;
+ else
+ ret = pwc_set_dynamic_noise(pdev, dynnoise);
+ break;
+ }
+
+ case VIDIOCPWCGDYNNOISE:
+ {
+ ret = pwc_get_dynamic_noise(pdev);
+ if (ret < 0)
+ break;
+ if (copy_to_user(arg, &ret, sizeof(ret)))
+ ret = -EFAULT;
+ break;
+ }
+
default:
- return -ENOIOCTLCMD;
+ ret = -ENOIOCTLCMD;
break;
}
- return 0;
+
+ if (ret > 0)
+ return 0;
+ return ret;
}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)