summaryrefslogtreecommitdiff
path: root/drivers/usb
diff options
context:
space:
mode:
authorGreg Kroah-Hartman <greg@soap.kroah.net>2002-02-10 21:52:50 -0800
committerGreg Kroah-Hartman <greg@soap.kroah.net>2002-02-10 21:52:50 -0800
commit7cf654b1044525fc8ffcdb32a5fa95e06a69eb99 (patch)
tree4cbaf727b75199115f84081b30c28286821cc45e /drivers/usb
parent1a3cb06f76a79ebddcc578bc5a9a1f1f57e0d37a (diff)
patch by Simon Evans <spse@secret.org.uk> that adds a Konica USB webcam driver
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/Config.help15
-rw-r--r--drivers/usb/Config.in1
-rw-r--r--drivers/usb/Makefile1
-rw-r--r--drivers/usb/konicawc.c737
-rw-r--r--drivers/usb/usbvideo.c12
-rw-r--r--drivers/usb/usbvideo.h2
6 files changed, 764 insertions, 4 deletions
diff --git a/drivers/usb/Config.help b/drivers/usb/Config.help
index c3a8bc953eea..e2c0810d6f88 100644
--- a/drivers/usb/Config.help
+++ b/drivers/usb/Config.help
@@ -505,6 +505,21 @@ CONFIG_USB_DABUSB
The module will be called dabusb.o. If you want to compile it as a
module, say M here and read <file:Documentation/modules.txt>.
+CONFIG_USB_KONICAWC
+ Say Y here if you want support for webcams based on a Konica
+ chipset. This is known to work with the Intel YC76 webcam.
+
+ This driver uses the Video For Linux API. You must enable
+ (Y or M in config) Video For Linux (under Character Devices)
+ to use this driver. Information on this API and pointers to
+ "v4l" programs may be found on the WWW at
+ <http://roadrunner.swansea.uk.linux.org/v4l.shtml>.
+
+ This code is also available as a module ( = code which can be
+ inserted in and removed from the running kernel whenever you want).
+ The module will be called konicawc.o. If you want to compile it as
+ a module, say M here and read <file:Documentation/modules.txt>.
+
CONFIG_USB_USBNET
This driver supports network links over USB with USB "Network"
or "data transfer" cables, often used to network laptops to PCs.
diff --git a/drivers/usb/Config.in b/drivers/usb/Config.in
index 950c0a3e215d..a56f898170c4 100644
--- a/drivers/usb/Config.in
+++ b/drivers/usb/Config.in
@@ -80,6 +80,7 @@ else
dep_tristate ' USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)' CONFIG_USB_VICAM $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
dep_tristate ' D-Link USB FM radio support (EXPERIMENTAL)' CONFIG_USB_DSBR $CONFIG_USB $CONFIG_VIDEO_DEV $CONFIG_EXPERIMENTAL
dep_tristate ' DABUSB driver' CONFIG_USB_DABUSB $CONFIG_USB
+ dep_tristate ' USB Konica Webcam support' CONFIG_USB_KONICAWC $CONFIG_USB $CONFIG_VIDEO_DEV
fi
comment 'USB Network adaptors'
diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile
index 6eaecc1bfeb4..3c20853c64a4 100644
--- a/drivers/usb/Makefile
+++ b/drivers/usb/Makefile
@@ -64,6 +64,7 @@ obj-$(CONFIG_USB_ACM) += acm.o
obj-$(CONFIG_USB_PRINTER) += printer.o
obj-$(CONFIG_USB_AUDIO) += audio.o
obj-$(CONFIG_USB_IBMCAM) += ibmcam.o usbvideo.o ultracam.o
+obj-$(CONFIG_USB_KONICAWC) += konicawc.o usbvideo.o
obj-$(CONFIG_USB_PWC) += pwc.o
obj-$(CONFIG_USB_DC2XX) += dc2xx.o
obj-$(CONFIG_USB_MDC800) += mdc800.o
diff --git a/drivers/usb/konicawc.c b/drivers/usb/konicawc.c
new file mode 100644
index 000000000000..3341e831bd4b
--- /dev/null
+++ b/drivers/usb/konicawc.c
@@ -0,0 +1,737 @@
+/*
+ * $Id: konicawc.c,v 1.12 2002/02/07 23:18:53 spse Exp $
+ *
+ * konicawc.c - konica webcam driver
+ *
+ * Author: Simon Evans <spse@secret.org.uk>
+ *
+ * Copyright (C) 2002 Simon Evans
+ *
+ * Licence: GPL
+ *
+ * Driver for USB webcams based on Konica chipset. This
+ * chipset is used in Intel YC76 camera.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#define DEBUG
+
+#include "usbvideo.h"
+
+#define MAX_BRIGHTNESS 108
+#define MAX_CONTRAST 108
+#define MAX_SATURATION 108
+#define MAX_SHARPNESS 108
+#define MAX_WHITEBAL 363
+
+#define MAX_CAMERAS 1
+
+enum ctrl_req {
+ SetWhitebal = 0x01,
+ SetBrightness = 0x02,
+ SetSharpness = 0x03,
+ SetContrast = 0x04,
+ SetSaturation = 0x05,
+};
+
+
+enum frame_sizes {
+ SIZE_160X130 = 0,
+ SIZE_176X144 = 1,
+ SIZE_320X240 = 2,
+};
+
+
+static usbvideo_t *cams;
+
+/* Some default values for inital camera settings,
+ can be set by modprobe */
+
+static int debug;
+static enum frame_sizes size;
+static int brightness = MAX_BRIGHTNESS/2;
+static int contrast = MAX_CONTRAST/2;
+static int saturation = MAX_SATURATION/2;
+static int sharpness = MAX_SHARPNESS/2;
+static int whitebal = 3*(MAX_WHITEBAL/4);
+
+
+struct konicawc {
+ u8 brightness; /* camera uses 0 - 9, x11 for real value */
+ u8 contrast; /* as above */
+ u8 saturation; /* as above */
+ u8 sharpness; /* as above */
+ u8 white_bal; /* 0 - 33, x11 for real value */
+ u8 fps; /* Stored as fps * 3 */
+ u8 size; /* Frame Size */
+ int height;
+ int width;
+ struct urb *sts_urb[USBVIDEO_NUMFRAMES];
+ u8 sts_buf[USBVIDEO_NUMFRAMES][FRAMES_PER_DESC];
+ struct urb *last_data_urb;
+ int lastframe;
+};
+
+
+#define konicawc_set_misc(uvd, req, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, req, value, index, NULL, 0)
+#define konicawc_get_misc(uvd, req, value, index, buf, sz) konicawc_ctrl_msg(uvd, USB_DIR_IN, req, value, index, buf, sz)
+#define konicawc_set_value(uvd, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, 2, value, index, NULL, 0)
+
+
+static int konicawc_ctrl_msg(uvd_t *uvd, u8 dir, u8 request, u16 value, u16 index, void *buf, int len)
+{
+ int retval = usb_control_msg(uvd->dev,
+ dir ? usb_rcvctrlpipe(uvd->dev, 0) : usb_sndctrlpipe(uvd->dev, 0),
+ request, 0x40 | dir, value, index, buf, len, HZ);
+ return retval < 0 ? retval : 0;
+}
+
+
+static int konicawc_setup_on_open(uvd_t *uvd)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ konicawc_set_misc(uvd, 0x2, 0, 0x0b);
+ dbg("setting brightness to %d (%d)", cam->brightness,
+ cam->brightness*11);
+ konicawc_set_value(uvd, cam->brightness, SetBrightness);
+ dbg("setting white balance to %d (%d)", cam->white_bal,
+ cam->white_bal*11);
+ konicawc_set_value(uvd, cam->white_bal, SetWhitebal);
+ dbg("setting contrast to %d (%d)", cam->contrast,
+ cam->contrast*11);
+ konicawc_set_value(uvd, cam->brightness, SetBrightness);
+ dbg("setting saturation to %d (%d)", cam->saturation,
+ cam->saturation*11);
+ konicawc_set_value(uvd, cam->saturation, SetSaturation);
+ dbg("setting sharpness to %d (%d)", cam->sharpness,
+ cam->sharpness*11);
+ konicawc_set_value(uvd, cam->sharpness, SetSharpness);
+ dbg("setting size %d", cam->size);
+ switch(cam->size) {
+ case 0:
+ konicawc_set_misc(uvd, 0x2, 0xa, 0x08);
+ break;
+
+ case 1:
+ konicawc_set_misc(uvd, 0x2, 4, 0x08);
+ break;
+
+ case 2:
+ konicawc_set_misc(uvd, 0x2, 5, 0x08);
+ break;
+ }
+ konicawc_set_misc(uvd, 0x2, 1, 0x0b);
+ cam->lastframe = -1;
+ return 0;
+}
+
+
+static int konicawc_compress_iso(uvd_t *uvd, struct urb *dataurb, struct urb *stsurb)
+{
+ char *cdata;
+ int i, totlen = 0;
+ unsigned char *status = stsurb->transfer_buffer;
+ int keep = 0, discard = 0, bad = 0;
+ static int buttonsts = 0;
+
+ for (i = 0; i < dataurb->number_of_packets; i++) {
+ int button = buttonsts;
+ unsigned char sts;
+ int n = dataurb->iso_frame_desc[i].actual_length;
+ int st = dataurb->iso_frame_desc[i].status;
+ cdata = dataurb->transfer_buffer +
+ dataurb->iso_frame_desc[i].offset;
+
+ /* Detect and ignore errored packets */
+ if (st < 0) {
+ if (debug >= 1)
+ err("Data error: packet=%d. len=%d. status=%d.",
+ i, n, st);
+ uvd->stats.iso_err_count++;
+ continue;
+ }
+
+ /* Detect and ignore empty packets */
+ if (n <= 0) {
+ uvd->stats.iso_skip_count++;
+ continue;
+ }
+
+ /* See what the status data said about the packet */
+ sts = *(status+stsurb->iso_frame_desc[i].offset);
+
+ /* sts: 0x80-0xff: frame start with frame number (ie 0-7f)
+ * otherwise:
+ * bit 0 0:drop packet (padding data)
+ * 1 keep packet
+ *
+ * bit 4 0 button not clicked
+ * 1 button clicked
+ * button is used to `take a picture' (in software)
+ */
+
+ if(sts < 0x80) {
+ button = sts & 0x40;
+ sts &= ~0x40;
+ }
+
+ /* work out the button status, but dont do
+ anything with it for now */
+
+ if(button != buttonsts) {
+ dbg("button: %sclicked", button ? "" : "un");
+ buttonsts = button;
+ }
+
+ if(sts == 0x01) { /* drop frame */
+ discard++;
+ continue;
+ }
+
+ if((sts > 0x01) && (sts < 0x80)) {
+ info("unknown status %2.2x", sts);
+ bad++;
+ continue;
+ }
+
+ keep++;
+ if(*(status+i) & 0x80) { /* frame start */
+ unsigned char marker[] = { 0, 0xff, 0, 0x00 };
+ if(debug > 1)
+ dbg("Adding Marker packet = %d, frame = %2.2x",
+ i, *(status+i));
+ marker[3] = *(status+i) - 0x80;
+ RingQueue_Enqueue(&uvd->dp, marker, 4);
+ totlen += 4;
+ }
+ totlen += n; /* Little local accounting */
+ if(debug > 5)
+ dbg("Adding packet %d, bytes = %d", i, n);
+ RingQueue_Enqueue(&uvd->dp, cdata, n);
+
+ }
+ if(debug > 8) {
+ dbg("finished: keep = %d discard = %d bad = %d added %d bytes",
+ keep, discard, bad, totlen);
+ }
+ return totlen;
+}
+
+
+static void konicawc_isoc_irq(struct urb *urb)
+{
+ int i, len = 0;
+ uvd_t *uvd = urb->context;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ /* We don't want to do anything if we are about to be removed! */
+ if (!CAMERA_IS_OPERATIONAL(uvd))
+ return;
+
+ if (urb->actual_length > 32) {
+ cam->last_data_urb = urb;
+ return;
+
+ }
+
+ if (!uvd->streaming) {
+ if (debug >= 1)
+ info("Not streaming, but interrupt!");
+ return;
+ }
+
+ uvd->stats.urb_count++;
+ if (urb->actual_length <= 0)
+ goto urb_done_with;
+
+ /* Copy the data received into ring queue */
+ if(cam->last_data_urb) {
+ len = konicawc_compress_iso(uvd, cam->last_data_urb, urb);
+ for (i = 0; i < FRAMES_PER_DESC; i++) {
+ cam->last_data_urb->iso_frame_desc[i].status = 0;
+ cam->last_data_urb->iso_frame_desc[i].actual_length = 0;
+ }
+ cam->last_data_urb = NULL;
+ }
+ uvd->stats.urb_length = len;
+ if (len <= 0) {
+ goto urb_done_with;
+ }
+
+ /* Here we got some data */
+ uvd->stats.data_count += len;
+ RingQueue_WakeUpInterruptible(&uvd->dp);
+
+urb_done_with:
+
+ for (i = 0; i < FRAMES_PER_DESC; i++) {
+ urb->iso_frame_desc[i].status = 0;
+ urb->iso_frame_desc[i].actual_length = 0;
+ }
+ return;
+}
+
+
+static int konicawc_start_data(uvd_t *uvd)
+{
+ struct usb_device *dev = uvd->dev;
+ int i, errFlag;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ if (!CAMERA_IS_OPERATIONAL(uvd)) {
+ err("Camera is not operational");
+ return -EFAULT;
+ }
+ uvd->curframe = -1;
+
+ /* Alternate interface 1 is is the biggest frame size */
+ i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive);
+ if (i < 0) {
+ err("usb_set_interface error");
+ uvd->last_error = i;
+ return -EBUSY;
+ }
+
+ /* We double buffer the Iso lists */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ int j, k;
+ struct urb *urb = uvd->sbuf[i].urb;
+ urb->dev = dev;
+ urb->context = uvd;
+ urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
+ urb->transfer_flags = USB_ISO_ASAP;
+ urb->transfer_buffer = uvd->sbuf[i].data;
+ urb->complete = konicawc_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC;
+ for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) {
+ urb->iso_frame_desc[j].offset = k;
+ urb->iso_frame_desc[j].length = uvd->iso_packet_len;
+ }
+
+ urb = cam->sts_urb[i];
+ urb->dev = dev;
+ urb->context = uvd;
+ urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);
+ urb->transfer_flags = USB_ISO_ASAP;
+ urb->transfer_buffer = cam->sts_buf[i];
+ urb->complete = konicawc_isoc_irq;
+ urb->number_of_packets = FRAMES_PER_DESC;
+ urb->transfer_buffer_length = FRAMES_PER_DESC;
+ for (j=0; j < FRAMES_PER_DESC; j++) {
+ urb->iso_frame_desc[j].offset = j;
+ urb->iso_frame_desc[j].length = 1;
+ }
+ }
+
+
+ /* Link URBs into a ring so that they invoke each other infinitely */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ if ((i+1) < USBVIDEO_NUMSBUF) {
+ cam->sts_urb[i]->next = uvd->sbuf[i].urb;
+ uvd->sbuf[i].urb->next = cam->sts_urb[i+1];
+ } else {
+ cam->sts_urb[i]->next = uvd->sbuf[i].urb;
+ uvd->sbuf[i].urb->next = cam->sts_urb[0];
+ }
+ }
+
+ /* Submit all URBs */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
+ if (errFlag)
+ err ("usb_submit_isoc(%d) ret %d", i, errFlag);
+
+ errFlag = usb_submit_urb(cam->sts_urb[i], GFP_KERNEL);
+ if (errFlag)
+ err("usb_submit_isoc(%d) ret %d", i, errFlag);
+ }
+
+ uvd->streaming = 1;
+ if (debug > 1)
+ dbg("streaming=1 video_endp=$%02x", uvd->video_endp);
+ return 0;
+}
+
+
+static void konicawc_stop_data(uvd_t *uvd)
+{
+ int i, j;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+
+ if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
+ return;
+
+ /* Unschedule all of the iso td's */
+ for (i=0; i < USBVIDEO_NUMSBUF; i++) {
+ j = usb_unlink_urb(uvd->sbuf[i].urb);
+ if (j < 0)
+ err("usb_unlink_urb() error %d.", j);
+
+ j = usb_unlink_urb(cam->sts_urb[i]);
+ if (j < 0)
+ err("usb_unlink_urb() error %d.", j);
+ }
+
+ uvd->streaming = 0;
+
+ if (!uvd->remove_pending) {
+ /* Set packet size to 0 */
+ j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive);
+ if (j < 0) {
+ err("usb_set_interface() error %d.", j);
+ uvd->last_error = j;
+ }
+ }
+}
+
+
+static void konicawc_process_isoc(uvd_t *uvd, usbvideo_frame_t *frame)
+{
+ int n;
+ int maxline, yplanesz;
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+ assert(uvd != NULL);
+ assert(frame != NULL);
+
+ maxline = (cam->height * cam->width * 3) / (2 * 384);
+ yplanesz = cam->height * cam->width;
+ if(debug > 5)
+ dbg("maxline = %d yplanesz = %d", maxline, yplanesz);
+
+ if(debug > 3)
+ dbg("Frame state = %d", frame->scanstate);
+
+ if(frame->scanstate == ScanState_Scanning) {
+ int drop = 0;
+ int curframe;
+ int fdrops = 0;
+ if(debug > 3)
+ dbg("Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp));
+ while(RingQueue_GetLength(&uvd->dp) >= 4) {
+ if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) &&
+ (RING_QUEUE_PEEK(&uvd->dp, 3) < 0x80)) {
+ curframe = RING_QUEUE_PEEK(&uvd->dp, 3);
+ if(cam->lastframe != -1) {
+ if(curframe < cam->lastframe) {
+ fdrops = (curframe + 0x80) - cam->lastframe;
+ } else {
+ fdrops = curframe - cam->lastframe;
+ }
+ fdrops--;
+ if(fdrops)
+ info("Dropped %d frames (%d -> %d)", fdrops,
+ cam->lastframe, curframe);
+ }
+ cam->lastframe = curframe;
+ frame->curline = 0;
+ frame->scanstate = ScanState_Lines;
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4);
+ break;
+ }
+ RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
+ drop++;
+ }
+ }
+
+ if(frame->scanstate == ScanState_Scanning)
+ return;
+
+ /* Try to move data from queue into frame buffer
+ * We get data in blocks of 384 bytes made up of:
+ * 256 Y, 64 U, 64 V.
+ * This needs to be written out as a Y plane, a U plane and a V plane.
+ */
+
+ while ( frame->curline < maxline && (n = RingQueue_GetLength(&uvd->dp)) >= 384) {
+ /* Y */
+ RingQueue_Dequeue(&uvd->dp, frame->data + (frame->curline * 256), 256);
+ /* U */
+ RingQueue_Dequeue(&uvd->dp, frame->data + yplanesz + (frame->curline * 64), 64);
+ /* V */
+ RingQueue_Dequeue(&uvd->dp, frame->data + (5 * yplanesz)/4 + (frame->curline * 64), 64);
+ frame->seqRead_Length += 384;
+ frame->curline++;
+ }
+ /* See if we filled the frame */
+ if (frame->curline == maxline) {
+ if(debug > 5)
+ dbg("got whole frame");
+
+ frame->frameState = FrameState_Done_Hold;
+ frame->curline = 0;
+ uvd->curframe = -1;
+ uvd->stats.frame_num++;
+ }
+}
+
+
+static int konicawc_calculate_fps(uvd_t *uvd)
+{
+ struct konicawc *t = uvd->user_data;
+ dbg("");
+
+ return (t->fps)/3;
+}
+
+
+static void konicawc_configure_video(uvd_t *uvd)
+{
+ struct konicawc *cam = (struct konicawc *)uvd->user_data;
+ u8 buf[2];
+
+ memset(&uvd->vpic, 0, sizeof(uvd->vpic));
+ memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
+
+ RESTRICT_TO_RANGE(brightness, 0, MAX_BRIGHTNESS);
+ RESTRICT_TO_RANGE(contrast, 0, MAX_CONTRAST);
+ RESTRICT_TO_RANGE(saturation, 0, MAX_SATURATION);
+ RESTRICT_TO_RANGE(sharpness, 0, MAX_SHARPNESS);
+ RESTRICT_TO_RANGE(whitebal, 0, MAX_WHITEBAL);
+
+ cam->brightness = brightness / 11;
+ cam->contrast = contrast / 11;
+ cam->saturation = saturation / 11;
+ cam->sharpness = sharpness / 11;
+ cam->white_bal = whitebal / 11;
+
+ uvd->vpic.colour = 108;
+ uvd->vpic.hue = 108;
+ uvd->vpic.brightness = brightness;
+ uvd->vpic.contrast = contrast;
+ uvd->vpic.whiteness = whitebal;
+ uvd->vpic.depth = 6;
+ uvd->vpic.palette = VIDEO_PALETTE_YUV420P;
+
+ memset(&uvd->vcap, 0, sizeof(uvd->vcap));
+ strcpy(uvd->vcap.name, "Konica Webcam");
+ uvd->vcap.type = VID_TYPE_CAPTURE;
+ uvd->vcap.channels = 1;
+ uvd->vcap.audios = 0;
+ uvd->vcap.maxwidth = cam->width;
+ uvd->vcap.maxheight = cam->height;
+ uvd->vcap.minwidth = cam->width;
+ uvd->vcap.minheight = cam->height;
+
+ memset(&uvd->vchan, 0, sizeof(uvd->vchan));
+ uvd->vchan.flags = 0 ;
+ uvd->vchan.tuners = 0;
+ uvd->vchan.channel = 0;
+ uvd->vchan.type = VIDEO_TYPE_CAMERA;
+ strcpy(uvd->vchan.name, "Camera");
+
+ /* Talk to device */
+ dbg("device init");
+ if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2))
+ dbg("3,10 -> %2.2x %2.2x", buf[0], buf[1]);
+ if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2))
+ dbg("3,10 -> %2.2x %2.2x", buf[0], buf[1]);
+ if(konicawc_set_misc(uvd, 0x2, 0, 0xd))
+ dbg("2,0,d failed");
+ dbg("setting initial values");
+
+}
+
+
+static void *konicawc_probe(struct usb_device *dev, unsigned int ifnum ,const struct usb_device_id *devid)
+{
+ uvd_t *uvd = NULL;
+ int i, nas;
+ int actInterface=-1, inactInterface=-1, maxPS=0;
+ unsigned char video_ep = 0;
+
+ if (debug >= 1)
+ dbg("konicawc_probe(%p,%u.)", dev, ifnum);
+
+ /* We don't handle multi-config cameras */
+ if (dev->descriptor.bNumConfigurations != 1)
+ return NULL;
+
+ info("Konica Webcam (rev. 0x%04x)", dev->descriptor.bcdDevice);
+
+ /* Validate found interface: must have one ISO endpoint */
+ nas = dev->actconfig->interface[ifnum].num_altsetting;
+ if (debug > 0)
+ info("Number of alternate settings=%d.", nas);
+ if (nas < 8) {
+ err("Too few alternate settings for this camera!");
+ return NULL;
+ }
+ /* Validate all alternate settings */
+ for (i=0; i < nas; i++) {
+ const struct usb_interface_descriptor *interface;
+ const struct usb_endpoint_descriptor *endpoint;
+
+ interface = &dev->actconfig->interface[ifnum].altsetting[i];
+ if (interface->bNumEndpoints != 2) {
+ err("Interface %d. has %u. endpoints!",
+ ifnum, (unsigned)(interface->bNumEndpoints));
+ return NULL;
+ }
+ endpoint = &interface->endpoint[1];
+ dbg("found endpoint: addr: 0x%2.2x maxps = 0x%4.4x",
+ endpoint->bEndpointAddress, endpoint->wMaxPacketSize);
+ if (video_ep == 0)
+ video_ep = endpoint->bEndpointAddress;
+ else if (video_ep != endpoint->bEndpointAddress) {
+ err("Alternate settings have different endpoint addresses!");
+ return NULL;
+ }
+ if ((endpoint->bmAttributes & 0x03) != 0x01) {
+ err("Interface %d. has non-ISO endpoint!", ifnum);
+ return NULL;
+ }
+ if ((endpoint->bEndpointAddress & 0x80) == 0) {
+ err("Interface %d. has ISO OUT endpoint!", ifnum);
+ return NULL;
+ }
+ if (endpoint->wMaxPacketSize == 0) {
+ if (inactInterface < 0)
+ inactInterface = i;
+ else {
+ err("More than one inactive alt. setting!");
+ return NULL;
+ }
+ } else {
+ if (actInterface < 0) {
+ actInterface = i;
+ maxPS = endpoint->wMaxPacketSize;
+ if (debug > 0)
+ info("Active setting=%d. maxPS=%d.",
+ i, maxPS);
+ } else {
+ /* Got another active alt. setting */
+ if (maxPS < endpoint->wMaxPacketSize) {
+ /* This one is better! */
+ actInterface = i;
+ maxPS = endpoint->wMaxPacketSize;
+ if (debug > 0) {
+ info("Even better active setting=%d. maxPS=%d.",
+ i, maxPS);
+ }
+ }
+ }
+ }
+ }
+
+ /* Code below may sleep, need to lock module while we are here */
+ MOD_INC_USE_COUNT;
+ uvd = usbvideo_AllocateDevice(cams);
+ if (uvd != NULL) {
+ struct konicawc *konicawc_data = (struct konicawc *)(uvd->user_data);
+ /* Here uvd is a fully allocated uvd_t object */
+
+ for(i = 0; i < USBVIDEO_NUMSBUF; i++) {
+ konicawc_data->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC);
+ }
+
+ switch(size) {
+ case SIZE_160X130:
+ default:
+ konicawc_data->height = 136;
+ konicawc_data->width = 160;
+ konicawc_data->size = SIZE_160X130;
+ break;
+
+ case SIZE_176X144:
+ konicawc_data->height = 144;
+ konicawc_data->width = 176;
+ konicawc_data->size = SIZE_176X144;
+ break;
+
+ case SIZE_320X240:
+ konicawc_data->height = 240;
+ konicawc_data->width = 320;
+ konicawc_data->size = SIZE_320X240;
+ break;
+ }
+
+ uvd->flags = 0;
+ uvd->debug = debug;
+ uvd->dev = dev;
+ uvd->iface = ifnum;
+ uvd->ifaceAltInactive = inactInterface;
+ uvd->ifaceAltActive = actInterface;
+ uvd->video_endp = video_ep;
+ uvd->iso_packet_len = maxPS;
+ uvd->paletteBits = 1L << VIDEO_PALETTE_YUV420P;
+ uvd->defaultPalette = VIDEO_PALETTE_YUV420P;
+ uvd->canvas = VIDEOSIZE(konicawc_data->width, konicawc_data->height);
+ uvd->videosize = uvd->canvas;
+
+ /* Initialize konicawc specific data */
+ konicawc_configure_video(uvd);
+
+ i = usbvideo_RegisterVideoDevice(uvd);
+ uvd->max_frame_size = (konicawc_data->width * konicawc_data->height * 3)/2;
+ if (i != 0) {
+ err("usbvideo_RegisterVideoDevice() failed.");
+ uvd = NULL;
+ }
+ }
+ MOD_DEC_USE_COUNT;
+ return uvd;
+}
+
+
+static int __init konicawc_init(void)
+{
+ usbvideo_cb_t cbTbl;
+ memset(&cbTbl, 0, sizeof(cbTbl));
+ cbTbl.probe = konicawc_probe;
+ cbTbl.setupOnOpen = konicawc_setup_on_open;
+ cbTbl.processData = konicawc_process_isoc;
+ cbTbl.getFPS = konicawc_calculate_fps;
+ cbTbl.startDataPump = konicawc_start_data;
+ cbTbl.stopDataPump = konicawc_stop_data;
+ return usbvideo_register(
+ &cams,
+ MAX_CAMERAS,
+ sizeof(struct konicawc),
+ "konicawc",
+ &cbTbl,
+ THIS_MODULE);
+}
+
+
+static void __exit konicawc_cleanup(void)
+{
+ usbvideo_Deregister(&cams);
+}
+
+#if defined(usb_device_id_ver)
+
+static __devinitdata struct usb_device_id id_table[] = {
+ { USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */
+ { } /* Terminating entry */
+};
+
+MODULE_DEVICE_TABLE(usb, id_table);
+#endif /* defined(usb_device_id_ver) */
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
+MODULE_DESCRIPTION("Konica Webcam driver");
+MODULE_PARM(size, "i");
+MODULE_PARM_DESC(size, "Frame Size 0: 160x136 1: 176x144 2: 320x240");
+MODULE_PARM(brightness, "i");
+MODULE_PARM_DESC(brightness, "Initial brightness 0 - 108");
+MODULE_PARM(contrast, "i");
+MODULE_PARM_DESC(contrast, "Initial contrast 0 - 108");
+MODULE_PARM(saturation, "i");
+MODULE_PARM_DESC(saturation, "Initial saturation 0 - 108");
+MODULE_PARM(sharpness, "i");
+MODULE_PARM_DESC(sharpness, "Initial brightness 0 - 108");
+MODULE_PARM(whitebal, "i");
+MODULE_PARM_DESC(whitebal, "Initial white balance 0 - 363");
+MODULE_PARM(debug, "i");
+MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
+module_init(konicawc_init);
+module_exit(konicawc_cleanup);
diff --git a/drivers/usb/usbvideo.c b/drivers/usb/usbvideo.c
index a7a5fc1052af..238e7448b760 100644
--- a/drivers/usb/usbvideo.c
+++ b/drivers/usb/usbvideo.c
@@ -791,6 +791,10 @@ int usbvideo_register(
cams->cb.getFrame = usbvideo_GetFrame;
if (cams->cb.disconnect == NULL)
cams->cb.disconnect = usbvideo_Disconnect;
+ if (cams->cb.startDataPump == NULL)
+ cams->cb.startDataPump = usbvideo_StartDataPump;
+ if (cams->cb.stopDataPump == NULL)
+ cams->cb.stopDataPump = usbvideo_StopDataPump;
#if USES_PROC_FS
/*
* If both /proc fs callbacks are NULL then we assume that the driver
@@ -963,7 +967,7 @@ void usbvideo_Disconnect(struct usb_device *dev, void *ptr)
uvd->remove_pending = 1; /* Now all ISO data will be ignored */
/* At this time we ask to cancel outstanding URBs */
- usbvideo_StopDataPump(uvd);
+ GET_CALLBACK(uvd, stopDataPump)(uvd);
for (i=0; i < USBVIDEO_NUMSBUF; i++)
usb_free_urb(uvd->sbuf[i].urb);
@@ -1299,7 +1303,7 @@ int usbvideo_v4l_open(struct video_device *dev, int flags)
if (errCode == 0) {
/* Start data pump if we have valid endpoint */
if (uvd->video_endp != 0)
- errCode = usbvideo_StartDataPump(uvd);
+ errCode = GET_CALLBACK(uvd, startDataPump)(uvd);
if (errCode == 0) {
if (VALID_CALLBACK(uvd, setupOnOpen)) {
if (uvd->debug > 1)
@@ -1349,8 +1353,8 @@ void usbvideo_v4l_close(struct video_device *dev)
if (uvd->debug > 1)
info("%s($%p)", proc, dev);
- down(&uvd->lock);
- usbvideo_StopDataPump(uvd);
+ down(&uvd->lock);
+ GET_CALLBACK(uvd, stopDataPump)(uvd);
usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size);
uvd->fbuf = NULL;
RingQueue_Free(&uvd->dp);
diff --git a/drivers/usb/usbvideo.h b/drivers/usb/usbvideo.h
index 17456ed46b0d..e0775a4ac639 100644
--- a/drivers/usb/usbvideo.h
+++ b/drivers/usb/usbvideo.h
@@ -269,6 +269,8 @@ typedef struct {
int (*getFrame)(uvd_t *, int);
int (*procfs_read)(char *page,char **start,off_t off,int count,int *eof,void *data);
int (*procfs_write)(struct file *file,const char *buffer,unsigned long count,void *data);
+ int (*startDataPump)(uvd_t *uvd);
+ void (*stopDataPump)(uvd_t *uvd);
} usbvideo_cb_t;
struct s_usbvideo_t {