📄 pwc-if.c
字号:
/* Linux driver for Philips webcam USB and Video4Linux interface part. (C) 1999-2004 Nemosoft Unv. (C) 2004-2006 Luc Saillard (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*//* 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 * 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*/#include <linux/errno.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/module.h>#include <linux/poll.h>#include <linux/slab.h>#include <linux/vmalloc.h>#include <linux/version.h>#include <asm/io.h>#include <linux/moduleparam.h>#include "pwc.h"#include "pwc-ioctl.h"#include "pwc-kiara.h"#include "pwc-timon.h"#include "pwc-dec23.h"#include "pwc-dec1.h"#include "pwc-uncompress.h"/* Function prototypes and driver templates *//* hotplug device table support */static const struct usb_device_id pwc_device_table [] = { { USB_DEVICE(0x0471, 0x0302) }, /* Philips models */ { USB_DEVICE(0x0471, 0x0303) }, { USB_DEVICE(0x0471, 0x0304) }, { USB_DEVICE(0x0471, 0x0307) }, { USB_DEVICE(0x0471, 0x0308) }, { USB_DEVICE(0x0471, 0x030C) }, { USB_DEVICE(0x0471, 0x0310) }, { USB_DEVICE(0x0471, 0x0311) }, /* Philips ToUcam PRO II */ { USB_DEVICE(0x0471, 0x0312) }, { USB_DEVICE(0x0471, 0x0313) }, /* the 'new' 720K */ { USB_DEVICE(0x0471, 0x0329) }, /* Philips SPC 900NC PC Camera */ { USB_DEVICE(0x069A, 0x0001) }, /* Askey */ { USB_DEVICE(0x046D, 0x08B0) }, /* Logitech QuickCam Pro 3000 */ { USB_DEVICE(0x046D, 0x08B1) }, /* Logitech QuickCam Notebook Pro */ { USB_DEVICE(0x046D, 0x08B2) }, /* Logitech QuickCam Pro 4000 */ { USB_DEVICE(0x046D, 0x08B3) }, /* Logitech QuickCam Zoom (old model) */ { USB_DEVICE(0x046D, 0x08B4) }, /* Logitech QuickCam Zoom (new model) */ { USB_DEVICE(0x046D, 0x08B5) }, /* Logitech QuickCam Orbit/Sphere */ { USB_DEVICE(0x046D, 0x08B6) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B7) }, /* Logitech (reserved) */ { USB_DEVICE(0x046D, 0x08B8) }, /* Logitech (reserved) */ { USB_DEVICE(0x055D, 0x9000) }, /* Samsung MPC-C10 */ { USB_DEVICE(0x055D, 0x9001) }, /* Samsung MPC-C30 */ { USB_DEVICE(0x055D, 0x9002) }, /* Samsung SNC-35E (Ver3.0) */ { USB_DEVICE(0x041E, 0x400C) }, /* Creative Webcam 5 */ { USB_DEVICE(0x041E, 0x4011) }, /* Creative Webcam Pro Ex */ { USB_DEVICE(0x04CC, 0x8116) }, /* Afina Eye */ { USB_DEVICE(0x06BE, 0x8116) }, /* new Afina Eye */ { USB_DEVICE(0x0d81, 0x1910) }, /* Visionite */ { USB_DEVICE(0x0d81, 0x1900) }, { }};MODULE_DEVICE_TABLE(usb, pwc_device_table);static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id);static void usb_pwc_disconnect(struct usb_interface *intf);static struct usb_driver pwc_driver = { .name = "Philips webcam", /* name */ .id_table = pwc_device_table, .probe = usb_pwc_probe, /* probe() */ .disconnect = usb_pwc_disconnect, /* disconnect() */};#define MAX_DEV_HINTS 20#define MAX_ISOC_ERRORS 20static int default_size = PSZ_QCIF;static int default_fps = 10;static int default_fbufs = 3; /* Default number of frame buffers */ int pwc_mbufs = 2; /* Default number of mmap() buffers */#if CONFIG_PWC_DEBUG int pwc_trace = PWC_DEBUG_LEVEL;#endifstatic int power_save = 0;static int led_on = 100, led_off = 0; /* defaults to LED that is on while in use */ int pwc_preferred_compression = 1; /* 0..3 = uncompressed..high */static struct { int type; char serial_number[30]; int device_node; struct pwc_device *pdev;} device_hint[MAX_DEV_HINTS];/***/static int pwc_video_open(struct inode *inode, struct file *file);static int pwc_video_close(struct inode *inode, struct file *file);static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos);static unsigned int pwc_video_poll(struct file *file, poll_table *wait);static int pwc_video_ioctl(struct inode *inode, struct file *file, unsigned int ioctlnr, unsigned long arg);static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma);static struct file_operations pwc_fops = { .owner = THIS_MODULE, .open = pwc_video_open, .release = pwc_video_close, .read = pwc_video_read, .poll = pwc_video_poll, .mmap = pwc_video_mmap, .ioctl = pwc_video_ioctl, .llseek = no_llseek,};static struct video_device pwc_template = { .owner = THIS_MODULE, .name = "Philips Webcam", /* Filled in later */ .type = VID_TYPE_CAPTURE, .hardware = VID_HARDWARE_PWC, .release = video_device_release, .fops = &pwc_fops, .minor = -1,};/***************************************************************************//* Okay, this is some magic that I worked out and the reasoning behind it... The biggest problem with any USB device is of course: "what to do when the user unplugs the device while it is in use by an application?" We have several options: 1) Curse them with the 7 plagues when they do (requires divine intervention) 2) Tell them not to (won't work: they'll do it anyway) 3) Oops the kernel (this will have a negative effect on a user's uptime) 4) Do something sensible. Of course, we go for option 4. It happens that this device will be linked to two times, once from usb_device and once from the video_device in their respective 'private' pointers. This is done when the device is probed() and all initialization succeeded. The pwc_device struct links back to both structures. When a device is unplugged while in use it will be removed from the list of known USB devices; I also de-register it as a V4L device, but unfortunately I can't free the memory since the struct is still in use by the file descriptor. This free-ing is then deferend until the first opportunity. Crude, but it works. A small 'advantage' is that if a user unplugs the cam and plugs it back in, it should get assigned the same video device minor, but unfortunately it's non-trivial to re-link the cam back to the video device... (that would surely be magic! :))*//***************************************************************************//* Private functions *//* Here we want the physical address of the memory. * This is used when initializing the contents of the area. */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,10)static unsigned long kvirt_to_pa(unsigned long adr) { unsigned long kva, ret; kva = (unsigned long) page_address(vmalloc_to_page((void *)adr)); kva |= adr & (PAGE_SIZE-1); /* restore the offset */ ret = __pa(kva); return ret;}#endif#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,14)/** * kzalloc - allocate memory. The memory is set to zero. * @size: how many bytes of memory are required. * @flags: the type of memory to allocate. */void *kzalloc(size_t size, int flags){ void *ret = kmalloc(size, flags); if (ret) memset(ret, 0, size); return ret;}#endifstatic void *pwc_rvmalloc(unsigned long size){ void * mem; unsigned long adr; mem=vmalloc_32(size); if (!mem) return NULL; memset(mem, 0, size); /* Clear the ram out, no junk to the user */ adr=(unsigned long) mem; while (size > 0) { SetPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } return mem;}static void pwc_rvfree(void * mem, unsigned long size){ unsigned long adr; if (!mem) return; adr=(unsigned long) mem; while ((long) size > 0) { ClearPageReserved(vmalloc_to_page((void *)adr)); adr += PAGE_SIZE; size -= PAGE_SIZE; } vfree(mem);}static int pwc_allocate_buffers(struct pwc_device *pdev){ int i, err; void *kbuf; PWC_DEBUG_MEMORY(">> pwc_allocate_buffers(pdev = 0x%p)\n", pdev); if (pdev == NULL) return -ENXIO; /* Allocate Isochronuous pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) { if (pdev->sbuf[i].data == NULL) { kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); if (kbuf == NULL) { PWC_ERROR("Failed to allocate iso buffer %d.\n", i); return -ENOMEM; } PWC_DEBUG_MEMORY("Allocated iso buffer at %p.\n", kbuf); pdev->sbuf[i].data = kbuf; } } /* Allocate frame buffer structure */ if (pdev->fbuf == NULL) { kbuf = kzalloc(default_fbufs * sizeof(struct pwc_frame_buf), GFP_KERNEL); if (kbuf == NULL) { PWC_ERROR("Failed to allocate frame buffer structure.\n"); return -ENOMEM; } PWC_DEBUG_MEMORY("Allocated frame buffer structure at %p.\n", kbuf); pdev->fbuf = kbuf; } /* create frame buffers, and make circular ring */ for (i = 0; i < default_fbufs; i++) { if (pdev->fbuf[i].data == NULL) { kbuf = vmalloc(PWC_FRAME_SIZE); /* need vmalloc since frame buffer > 128K */ if (kbuf == NULL) { PWC_ERROR("Failed to allocate frame buffer %d.\n", i); return -ENOMEM; } PWC_DEBUG_MEMORY("Allocated frame buffer %d at %p.\n", i, kbuf); pdev->fbuf[i].data = kbuf; memset(kbuf, 0, PWC_FRAME_SIZE); } } /* Allocate decompressor table space */ if (DEVICE_USE_CODEC1(pdev->type)) err = pwc_dec1_alloc(pdev); else err = pwc_dec23_alloc(pdev); if (err) { PWC_ERROR("Failed to allocate decompress table.\n"); return err; } /* Allocate image buffer; double buffer for mmap() */ kbuf = pwc_rvmalloc(pwc_mbufs * pdev->len_per_image); if (kbuf == NULL) { PWC_ERROR("Failed to allocate image buffer(s). needed (%d)\n", pwc_mbufs * pdev->len_per_image); return -ENOMEM; } PWC_DEBUG_MEMORY("Allocated image buffer at %p.\n", kbuf); pdev->image_data = kbuf; for (i = 0; i < pwc_mbufs; i++) { pdev->images[i].offset = i * pdev->len_per_image; pdev->images[i].vma_use_count = 0; } for (; i < MAX_IMAGES; i++) { pdev->images[i].offset = 0; } kbuf = NULL; PWC_DEBUG_MEMORY("<< pwc_allocate_buffers()\n"); return 0;}static void pwc_free_buffers(struct pwc_device *pdev){ int i; PWC_DEBUG_MEMORY("Entering free_buffers(%p).\n", pdev); if (pdev == NULL) return; /* Release Iso-pipe buffers */ for (i = 0; i < MAX_ISO_BUFS; i++) if (pdev->sbuf[i].data != NULL) { PWC_DEBUG_MEMORY("Freeing ISO buffer at %p.\n", pdev->sbuf[i].data); kfree(pdev->sbuf[i].data); pdev->sbuf[i].data = NULL; } /* The same for frame buffers */ if (pdev->fbuf != NULL) { for (i = 0; i < default_fbufs; i++) { if (pdev->fbuf[i].data != NULL) { PWC_DEBUG_MEMORY("Freeing frame buffer %d at %p.\n", i, pdev->fbuf[i].data); vfree(pdev->fbuf[i].data); pdev->fbuf[i].data = NULL; } } kfree(pdev->fbuf); pdev->fbuf = NULL; } /* Intermediate decompression buffer & tables */ if (pdev->decompress_data != NULL) { PWC_DEBUG_MEMORY("Freeing decompression buffer at %p.\n", pdev->decompress_data); kfree(pdev->decompress_data); pdev->decompress_data = NULL; } /* Release image buffers */ if (pdev->image_data != NULL) { PWC_DEBUG_MEMORY("Freeing image buffer at %p.\n", pdev->image_data); pwc_rvfree(pdev->image_data, pwc_mbufs * pdev->len_per_image); } pdev->image_data = NULL; PWC_DEBUG_MEMORY("Leaving free_buffers().\n");}/* The frame & image buffer mess.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -