// SPDX-License-Identifier: GPL-2.0-or-later /* Linux driver for Philips webcam USB and Video4Linux interface part. (C) 1999-2004 Nemosoft Unv. (C) 2004-2006 Luc Saillard (luc@saillard.org) (C) 2011 Hans de Goede <hdegoede@redhat.com>
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 code forms the interface between the USB layers and the Philips specific stuff. Some adanved stuff of the driver falls under an NDA, signed between me and Philips B.V., Eindhoven, the Netherlands, and is thus not distributed in source form. The binary pwcx.o module contains the code that falls under the NDA.
In case you're wondering: 'pwc' stands for "Philips WebCam", but I really didn't want to type 'philips_web_cam' every time (I'm lazy as any Linux kernel hacker, but I don't like uncomprehensible abbreviations without explanation).
Oh yes, convention: to disctinguish between all the various pointers to device-structures, I use these names for the pointer variables: udev: struct usb_device * vdev: struct video_device (member of pwc_dev) pdev: struct pwc_devive *
*/
/* Contributors: - Alvarado: adding whitebalance code - Alistar Moire: QuickCam 3000 Pro device/product ID - Tony Hoyle: Creative Labs Webcam 5 device/product ID - Mark Burazin: solving hang in VIDIOCSYNC when camera gets unplugged - Jk Fang: Sotec Afina Eye ID - Xavier Roche: QuickCam Pro 4000 ID - Jens Knudsen: QuickCam Zoom ID - J. Debert: QuickCam for Notebooks ID - Pham Thanh Nam: webcam snapshot button as an event input device
*/
/* The ToUCam Fun CMOS sensor causes the firmware to send 2 or 3 bogus frames on the USB wire after an exposure change. This conditition is however detected in the cam and a bit is set in the header.
*/ if (pdev->type == 730) { unsignedchar *ptr = (unsignedchar *)fbuf->data;
if (ptr[1] == 1 && ptr[0] & 0x10) {
PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n");
pdev->drop_frames += 2;
} if ((ptr[0] ^ pdev->vmirror) & 0x01) {
pwc_snapshot_button(pdev, ptr[0] & 0x01);
} if ((ptr[0] ^ pdev->vmirror) & 0x02) { if (ptr[0] & 0x02)
PWC_TRACE("Image is mirrored.\n"); else
PWC_TRACE("Image is normal.\n");
}
pdev->vmirror = ptr[0] & 0x03; /* Sometimes the trailer of the 730 is still sent as a 4 byte packet after a short frame; this condition is filtered out specifically. A 4 byte frame doesn't make sense anyway. So we get either this sequence: drop_bit set -> 4 byte frame -> short frame -> good frame Or this one: drop_bit set -> short frame -> good frame So we drop either 3 or 2 frames in all!
*/ if (fbuf->filled == 4)
pdev->drop_frames++;
} elseif (pdev->type == 740 || pdev->type == 720) { unsignedchar *ptr = (unsignedchar *)fbuf->data; if ((ptr[0] ^ pdev->vmirror) & 0x01) {
pwc_snapshot_button(pdev, ptr[0] & 0x01);
}
pdev->vmirror = ptr[0] & 0x03;
}
/* In case we were instructed to drop the frame, do so silently. */ if (pdev->drop_frames > 0) {
pdev->drop_frames--;
} else { /* Check for underflow first */ if (fbuf->filled < pdev->frame_total_size) {
PWC_DEBUG_FLOW("Frame buffer underflow (%d bytes); discarded.\n",
fbuf->filled);
} else {
fbuf->vb.field = V4L2_FIELD_NONE;
fbuf->vb.sequence = pdev->vframe_count;
vb2_buffer_done(&fbuf->vb.vb2_buf, VB2_BUF_STATE_DONE);
pdev->fill_buf = NULL;
pdev->vsync = 0;
}
} /* !drop_frames */
pdev->vframe_count++;
}
/* This gets called for the Isochronous pipe (video). This is done in * interrupt time, so it has to be fast, not crash, and not stall. Neat.
*/ staticvoid pwc_isoc_handler(struct urb *urb)
{ struct pwc_device *pdev = (struct pwc_device *)urb->context; struct device *dmadev = urb->dev->bus->sysdev; int i, fst, flen; unsignedchar *iso_buf = NULL;
if (pdev->fill_buf == NULL)
pdev->fill_buf = pwc_get_next_fill_buf(pdev);
if (urb->status != 0) { constchar *errmsg;
errmsg = "Unknown"; switch(urb->status) { case -ENOSR: errmsg = "Buffer error (overrun)"; break; case -EPIPE: errmsg = "Stalled (device not responding)"; break; case -EOVERFLOW: errmsg = "Babble (bad cable?)"; break; case -EPROTO: errmsg = "Bit-stuff error (bad cable?)"; break; case -EILSEQ: errmsg = "CRC/Timeout (could be anything)"; break; case -ETIME: errmsg = "Device does not respond"; break;
}
PWC_ERROR("pwc_isoc_handler() called with status %d [%s].\n",
urb->status, errmsg); /* Give up after a number of contiguous errors */ if (++pdev->visoc_errors > MAX_ISOC_ERRORS)
{
PWC_ERROR("Too many ISOC errors, bailing out.\n"); if (pdev->fill_buf) {
vb2_buffer_done(&pdev->fill_buf->vb.vb2_buf,
VB2_BUF_STATE_ERROR);
pdev->fill_buf = NULL;
}
}
pdev->vsync = 0; /* Drop the current frame */ goto handler_end;
}
/* Reset ISOC error counter. We did get here, after all. */
pdev->visoc_errors = 0;
i = usb_submit_urb(urb, GFP_ATOMIC); if (i != 0)
PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);
}
/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ staticint pwc_isoc_init(struct pwc_device *pdev)
{ struct usb_device *udev; struct urb *urb; int i, j, ret; struct usb_interface *intf; struct usb_host_interface *idesc = NULL; int compression = 0; /* 0..3 = uncompressed..high */
retry: /* We first try with low compression and then retry with a higher
compression setting if there is not enough bandwidth. */
ret = pwc_set_video_mode(pdev, pdev->width, pdev->height, pdev->pixfmt,
pdev->vframes, &compression, 1);
/* Get the current alternate interface, adjust packet size */
intf = usb_ifnum_to_if(udev, 0); if (intf)
idesc = usb_altnum_to_altsetting(intf, pdev->valternate); if (!idesc) return -EIO;
/* Search video endpoint */
pdev->vmax_packet_size = -1; for (i = 0; i < idesc->desc.bNumEndpoints; i++) { if ((idesc->endpoint[i].desc.bEndpointAddress & 0xF) == pdev->vendpoint) {
pdev->vmax_packet_size = le16_to_cpu(idesc->endpoint[i].desc.wMaxPacketSize); break;
}
}
if (pdev->vmax_packet_size < 0 || pdev->vmax_packet_size > ISO_MAX_FRAME_SIZE) {
PWC_ERROR("Failed to find packet size for video endpoint in current alternate setting.\n"); return -ENFILE; /* Odd error, that should be noticeable */
}
/* Set alternate interface */
PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate);
ret = usb_set_interface(pdev->udev, 0, pdev->valternate); if (ret == -ENOSPC && compression < 3) {
compression++; goto retry;
} if (ret < 0) return ret;
/* Allocate and init Isochronuous urbs */ for (i = 0; i < MAX_ISO_BUFS; i++) {
urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) {
pwc_isoc_cleanup(pdev); return -ENOMEM;
}
pdev->urbs[i] = urb;
PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb);
/* link */ for (i = 0; i < MAX_ISO_BUFS; i++) {
ret = usb_submit_urb(pdev->urbs[i], GFP_KERNEL); if (ret == -ENOSPC && compression < 3) {
compression++;
pwc_isoc_cleanup(pdev); goto retry;
} if (ret) {
PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret);
pwc_isoc_cleanup(pdev); return ret;
}
PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->urbs[i]);
}
/* All is done... */
PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); return 0;
}
staticvoid pwc_iso_stop(struct pwc_device *pdev)
{ int i;
/* Unlinking ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (pdev->urbs[i]) {
PWC_DEBUG_MEMORY("Unlinking URB %p\n", pdev->urbs[i]);
usb_kill_urb(pdev->urbs[i]);
}
}
}
staticvoid pwc_iso_free(struct pwc_device *pdev)
{ int i;
/* Freeing ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { struct urb *urb = pdev->urbs[i];
if (urb) {
PWC_DEBUG_MEMORY("Freeing URB\n"); if (urb->transfer_buffer)
pwc_free_urb_buffer(urb->dev,
urb->transfer_buffer_length,
urb->transfer_buffer,
urb->transfer_dma);
usb_free_urb(urb);
pdev->urbs[i] = NULL;
}
}
}
/* Both v4l2_lock and vb_queue_lock should be locked when calling this */ staticvoid pwc_isoc_cleanup(struct pwc_device *pdev)
{
PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n");
/* Must be called with vb_queue_lock hold */ staticvoid pwc_cleanup_queued_bufs(struct pwc_device *pdev, enum vb2_buffer_state state)
{ unsignedlong flags = 0;
spin_lock_irqsave(&pdev->queued_bufs_lock, flags); while (!list_empty(&pdev->queued_bufs)) { struct pwc_frame_buf *buf;
if (vb->state == VB2_BUF_STATE_DONE) { /* * Application has called dqbuf and is getting back a buffer * we've filled, take the pwc data we've stored in buf->data * and decompress it into a usable format, storing the result * in the vb2_buffer.
*/
pwc_decompress(pdev, buf);
}
}
if (mutex_lock_interruptible(&pdev->v4l2_lock)) return -ERESTARTSYS; /* Turn on camera and set LEDS on */
pwc_camera_power(pdev, 1);
pwc_set_leds(pdev, leds[0], leds[1]);
r = pwc_isoc_init(pdev); if (r) { /* If we failed turn camera and LEDS back off */
pwc_set_leds(pdev, 0, 0);
pwc_camera_power(pdev, 0); /* And cleanup any queued bufs!! */
pwc_cleanup_queued_bufs(pdev, VB2_BUF_STATE_QUEUED);
}
mutex_unlock(&pdev->v4l2_lock);
/* Check if we can handle this device */
PWC_DEBUG_PROBE("probe() called [%04X %04X], if %d\n",
vendor_id, product_id,
intf->altsetting->desc.bInterfaceNumber);
/* the interfaces are probed one by one. We are only interested in the video interface (0) now. Interface 1 is the Audio Control, and interface 2 Audio itself.
*/ if (intf->altsetting->desc.bInterfaceNumber > 0) return -ENODEV;
if (vendor_id == 0x0471) { switch (product_id) { case 0x0302:
PWC_INFO("Philips PCA645VC USB webcam detected.\n");
name = "Philips 645 webcam";
type_id = 645; break; case 0x0303:
PWC_INFO("Philips PCA646VC USB webcam detected.\n");
name = "Philips 646 webcam";
type_id = 646; break; case 0x0304:
PWC_INFO("Askey VC010 type 2 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 646; break; case 0x0307:
PWC_INFO("Philips PCVC675K (Vesta) USB webcam detected.\n");
name = "Philips 675 webcam";
type_id = 675; break; case 0x0308:
PWC_INFO("Philips PCVC680K (Vesta Pro) USB webcam detected.\n");
name = "Philips 680 webcam";
type_id = 680; break; case 0x030C:
PWC_INFO("Philips PCVC690K (Vesta Pro Scan) USB webcam detected.\n");
name = "Philips 690 webcam";
type_id = 690; break; case 0x0310:
PWC_INFO("Philips PCVC730K (ToUCam Fun)/PCVC830 (ToUCam II) USB webcam detected.\n");
name = "Philips 730 webcam";
type_id = 730; break; case 0x0311:
PWC_INFO("Philips PCVC740K (ToUCam Pro)/PCVC840 (ToUCam II) USB webcam detected.\n");
name = "Philips 740 webcam";
type_id = 740; break; case 0x0312:
PWC_INFO("Philips PCVC750K (ToUCam Pro Scan) USB webcam detected.\n");
name = "Philips 750 webcam";
type_id = 750; break; case 0x0313:
PWC_INFO("Philips PCVC720K/40 (ToUCam XS) USB webcam detected.\n");
name = "Philips 720K/40 webcam";
type_id = 720; break; case 0x0329:
PWC_INFO("Philips SPC 900NC USB webcam detected.\n");
name = "Philips SPC 900NC webcam";
type_id = 740; break; case 0x032C:
PWC_INFO("Philips SPC 880NC USB webcam detected.\n");
name = "Philips SPC 880NC webcam";
type_id = 740; break; default: return -ENODEV;
}
} elseif (vendor_id == 0x069A) { switch(product_id) { case 0x0001:
PWC_INFO("Askey VC010 type 1 USB webcam detected.\n");
name = "Askey VC010 webcam";
type_id = 645; break; default: return -ENODEV;
}
} elseif (vendor_id == 0x046d) { switch(product_id) { case 0x08b0:
PWC_INFO("Logitech QuickCam Pro 3000 USB webcam detected.\n");
name = "Logitech QuickCam Pro 3000";
type_id = 740; /* CCD sensor */ break; case 0x08b1:
PWC_INFO("Logitech QuickCam Notebook Pro USB webcam detected.\n");
name = "Logitech QuickCam Notebook Pro";
type_id = 740; /* CCD sensor */ break; case 0x08b2:
PWC_INFO("Logitech QuickCam 4000 Pro USB webcam detected.\n");
name = "Logitech QuickCam Pro 4000";
type_id = 740; /* CCD sensor */ if (my_power_save == -1)
my_power_save = 1; break; case 0x08b3:
PWC_INFO("Logitech QuickCam Zoom USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */ break; case 0x08B4:
PWC_INFO("Logitech QuickCam Zoom (new model) USB webcam detected.\n");
name = "Logitech QuickCam Zoom";
type_id = 740; /* CCD sensor */ if (my_power_save == -1)
my_power_save = 1; break; case 0x08b5:
PWC_INFO("Logitech QuickCam Orbit/Sphere USB webcam detected.\n");
name = "Logitech QuickCam Orbit";
type_id = 740; /* CCD sensor */ if (my_power_save == -1)
my_power_save = 1;
features |= FEATURE_MOTOR_PANTILT; break; case 0x08b6:
PWC_INFO("Logitech/Cisco VT Camera webcam detected.\n");
name = "Cisco VT Camera";
type_id = 740; /* CCD sensor */ break; case 0x08b7:
PWC_INFO("Logitech ViewPort AV 100 webcam detected.\n");
name = "Logitech ViewPort AV 100";
type_id = 740; /* CCD sensor */ break; case 0x08b8: /* Where this released? */
PWC_INFO("Logitech QuickCam detected (reserved ID).\n");
name = "Logitech QuickCam (res.)";
type_id = 730; /* Assuming CMOS */ break; default: return -ENODEV;
}
} elseif (vendor_id == 0x055d) { /* I don't know the difference between the C10 and the C30; I suppose the difference is the sensor, but both cameras work equally well with a type_id of 675
*/ switch(product_id) { case 0x9000:
PWC_INFO("Samsung MPC-C10 USB webcam detected.\n");
name = "Samsung MPC-C10";
type_id = 675; break; case 0x9001:
PWC_INFO("Samsung MPC-C30 USB webcam detected.\n");
name = "Samsung MPC-C30";
type_id = 675; break; case 0x9002:
PWC_INFO("Samsung SNC-35E (v3.0) USB webcam detected.\n");
name = "Samsung MPC-C30";
type_id = 740; break; default: return -ENODEV;
}
} elseif (vendor_id == 0x041e) { switch(product_id) { case 0x400c:
PWC_INFO("Creative Labs Webcam 5 detected.\n");
name = "Creative Labs Webcam 5";
type_id = 730; if (my_power_save == -1)
my_power_save = 1; break; case 0x4011:
PWC_INFO("Creative Labs Webcam Pro Ex detected.\n");
name = "Creative Labs Webcam Pro Ex";
type_id = 740; break; default: return -ENODEV;
}
} elseif (vendor_id == 0x04cc) { switch(product_id) { case 0x8116:
PWC_INFO("Sotec Afina Eye USB webcam detected.\n");
name = "Sotec Afina Eye";
type_id = 730; break; default: return -ENODEV;
}
} elseif (vendor_id == 0x06be) { switch(product_id) { case 0x8116: /* This is essentially the same cam as the Sotec Afina Eye */
PWC_INFO("AME Co. Afina Eye USB webcam detected.\n");
name = "AME Co. Afina Eye";
type_id = 750; break; default: return -ENODEV;
}
} elseif (vendor_id == 0x0d81) { switch(product_id) { case 0x1900:
PWC_INFO("Visionite VCS-UC300 USB webcam detected.\n");
name = "Visionite VCS-UC300";
type_id = 740; /* CCD sensor */ break; case 0x1910:
PWC_INFO("Visionite VCS-UM100 USB webcam detected.\n");
name = "Visionite VCS-UM100";
type_id = 730; /* CMOS sensor */ break; default: return -ENODEV;
}
} else return -ENODEV; /* Not any of the know types; but the list keeps growing. */
if (my_power_save == -1)
my_power_save = 0;
memset(serial_number, 0, 30);
usb_string(udev, udev->descriptor.iSerialNumber, serial_number, 29);
PWC_DEBUG_PROBE("Device serial number is %s\n", serial_number);
if (udev->descriptor.bNumConfigurations > 1)
PWC_WARNING("Warning: more than 1 configuration available.\n");
/* Allocate structure, initialize pointers, mutexes, etc. and link it to the usb_device */
pdev = kzalloc(sizeof(struct pwc_device), GFP_KERNEL); if (pdev == NULL) {
PWC_ERROR("Oops, could not allocate memory for pwc_device.\n"); return -ENOMEM;
}
pdev->type = type_id;
pdev->features = features;
pwc_construct(pdev); /* set min/max sizes correct */
/* Allocate USB command buffers */
pdev->ctrl_buf = kmalloc(sizeof(pdev->cmd_buf), GFP_KERNEL); if (!pdev->ctrl_buf) {
PWC_ERROR("Oops, could not allocate memory for pwc_device.\n");
rc = -ENOMEM; goto err_free_mem;
}
#ifdef CONFIG_USB_PWC_DEBUG /* Query sensor type */ if (pwc_get_cmos_sensor(pdev, &rc) >= 0) {
PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n",
pdev->vdev.name,
pwc_sensor_type_to_string(rc), rc);
} #endif
rc = video_register_device(&pdev->vdev, VFL_TYPE_VIDEO, -1); if (rc < 0) {
PWC_ERROR("Failed to register as video device (%d).\n", rc); goto err_unregister_v4l2_dev;
}
PWC_INFO("Registered as %s.\n", video_device_node_name(&pdev->vdev));
/* The user yanked out the cable... */ staticvoid usb_pwc_disconnect(struct usb_interface *intf)
{ struct v4l2_device *v = usb_get_intfdata(intf); struct pwc_device *pdev = container_of(v, struct pwc_device, v4l2_dev);
mutex_lock(&pdev->vb_queue_lock);
mutex_lock(&pdev->v4l2_lock); /* No need to keep the urbs around after disconnection */ if (pdev->vb_queue.streaming)
pwc_isoc_cleanup(pdev);
pdev->udev = NULL;
#ifdef CONFIG_USB_PWC_DEBUG
MODULE_PARM_DESC(trace, "For debugging purposes"); #endif
MODULE_PARM_DESC(power_save, "Turn power saving for new cameras on or off");
MODULE_PARM_DESC(leds, "LED on,off time in milliseconds");
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.