📄 pwc-if.c
字号:
{ struct pwc_device *pdev = video_get_drvdata(vdev); int rc; rc = video_device_create_file(vdev, &dev_attr_button); if (rc) goto err; if (pdev->features & FEATURE_MOTOR_PANTILT) { rc = video_device_create_file(vdev, &dev_attr_pan_tilt); if (rc) goto err_button; } return 0;err_button: video_device_remove_file(vdev, &dev_attr_button);err: return rc;}static void pwc_remove_sysfs_files(struct video_device *vdev){ struct pwc_device *pdev = video_get_drvdata(vdev); if (pdev->features & FEATURE_MOTOR_PANTILT) video_device_remove_file(vdev, &dev_attr_pan_tilt); video_device_remove_file(vdev, &dev_attr_button);}#ifdef CONFIG_USB_PWC_DEBUGstatic const char *pwc_sensor_type_to_string(unsigned int sensor_type){ switch(sensor_type) { case 0x00: return "Hyundai CMOS sensor"; case 0x20: return "Sony CCD sensor + TDA8787"; case 0x2E: return "Sony CCD sensor + Exas 98L59"; case 0x2F: return "Sony CCD sensor + ADI 9804"; case 0x30: return "Sharp CCD sensor + TDA8787"; case 0x3E: return "Sharp CCD sensor + Exas 98L59"; case 0x3F: return "Sharp CCD sensor + ADI 9804"; case 0x40: return "UPA 1021 sensor"; case 0x100: return "VGA sensor"; case 0x101: return "PAL MR sensor"; default: return "unknown type of sensor"; }}#endif/***************************************************************************//* Video4Linux functions */static int pwc_video_open(struct inode *inode, struct file *file){ int i, ret; struct video_device *vdev = video_devdata(file); struct pwc_device *pdev; PWC_DEBUG_OPEN(">> video_open called(vdev = 0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; BUG_ON(!pdev); if (pdev->vopen) { PWC_DEBUG_OPEN("I'm busy, someone is using the device.\n"); return -EBUSY; } mutex_lock(&pdev->modlock); if (!pdev->usb_init) { PWC_DEBUG_OPEN("Doing first time initialization.\n"); pdev->usb_init = 1; /* Query sensor type */ ret = pwc_get_cmos_sensor(pdev, &i); if (ret >= 0) { PWC_DEBUG_OPEN("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, pwc_sensor_type_to_string(i), i); } } /* Turn on camera */ if (power_save) { i = pwc_camera_power(pdev, 1); if (i < 0) PWC_DEBUG_OPEN("Failed to restore power to the camera! (%d)\n", i); } /* Set LED on/off time */ if (pwc_set_leds(pdev, led_on, led_off) < 0) PWC_DEBUG_OPEN("Failed to set LED on/off time.\n"); pwc_construct(pdev); /* set min/max sizes correct */ /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); if (i < 0) { PWC_DEBUG_OPEN("Failed to allocate buffers memory.\n"); pwc_free_buffers(pdev); mutex_unlock(&pdev->modlock); return i; } /* Reset buffers & parameters */ pwc_reset_buffers(pdev); for (i = 0; i < pwc_mbufs; i++) pdev->image_used[i] = 0; pdev->vframe_count = 0; pdev->vframes_dumped = 0; pdev->vframes_error = 0; pdev->visoc_errors = 0; pdev->error_status = 0; pwc_construct(pdev); /* set min/max sizes correct */ /* Set some defaults */ pdev->vsnapshot = 0; /* Start iso pipe for video; first try the last used video size (or the default one); if that fails try QCIF/10 or QSIF/10; it that fails too, give up. */ i = pwc_set_video_mode(pdev, pwc_image_sizes[pdev->vsize].x, pwc_image_sizes[pdev->vsize].y, pdev->vframes, pdev->vcompression, 0); if (i) { unsigned int default_resolution; PWC_DEBUG_OPEN("First attempt at set_video_mode failed.\n"); if (pdev->type>= 730) default_resolution = PSZ_QSIF; else default_resolution = PSZ_QCIF; i = pwc_set_video_mode(pdev, pwc_image_sizes[default_resolution].x, pwc_image_sizes[default_resolution].y, 10, pdev->vcompression, 0); } if (i) { PWC_DEBUG_OPEN("Second attempt at set_video_mode failed.\n"); pwc_free_buffers(pdev); mutex_unlock(&pdev->modlock); return i; } i = pwc_isoc_init(pdev); if (i) { PWC_DEBUG_OPEN("Failed to init ISOC stuff = %d.\n", i); pwc_isoc_cleanup(pdev); pwc_free_buffers(pdev); mutex_unlock(&pdev->modlock); return i; } /* Initialize the webcam to sane value */ pwc_set_brightness(pdev, 0x7fff); pwc_set_agc(pdev, 1, 0); pdev->vopen++; file->private_data = vdev; mutex_unlock(&pdev->modlock); PWC_DEBUG_OPEN("<< video_open() returns 0.\n"); return 0;}static void pwc_cleanup(struct pwc_device *pdev){ pwc_remove_sysfs_files(pdev->vdev); video_unregister_device(pdev->vdev);}/* Note that all cleanup is done in the reverse order as in _open */static int pwc_video_close(struct inode *inode, struct file *file){ struct video_device *vdev = file->private_data; struct pwc_device *pdev; int i, hint; PWC_DEBUG_OPEN(">> video_close called(vdev = 0x%p).\n", vdev); lock_kernel(); pdev = (struct pwc_device *)vdev->priv; if (pdev->vopen == 0) PWC_DEBUG_MODULE("video_close() called on closed device?\n"); /* Dump statistics, but only if a reasonable amount of frames were processed (to prevent endless log-entries in case of snap-shot programs) */ if (pdev->vframe_count > 20) PWC_DEBUG_MODULE("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); if (DEVICE_USE_CODEC1(pdev->type)) pwc_dec1_exit(); else pwc_dec23_exit(); pwc_isoc_cleanup(pdev); pwc_free_buffers(pdev); /* Turn off LEDS and power down camera, but only when not unplugged */ if (!pdev->unplugged) { /* Turn LEDs off */ if (pwc_set_leds(pdev, 0, 0) < 0) PWC_DEBUG_MODULE("Failed to set LED on/off time.\n"); if (power_save) { i = pwc_camera_power(pdev, 0); if (i < 0) PWC_ERROR("Failed to power down camera (%d)\n", i); } pdev->vopen--; PWC_DEBUG_OPEN("<< video_close() vopen=%d\n", pdev->vopen); } else { pwc_cleanup(pdev); /* Free memory (don't set pdev to 0 just yet) */ kfree(pdev); /* search device_hint[] table if we occupy a slot, by any chance */ for (hint = 0; hint < MAX_DEV_HINTS; hint++) if (device_hint[hint].pdev == pdev) device_hint[hint].pdev = NULL; } unlock_kernel(); return 0;}/* * FIXME: what about two parallel reads ???? * ANSWER: Not supported. You can't open the device more than once, despite what the V4L1 interface says. First, I don't see the need, second there's no mechanism of alerting the 2nd/3rd/... process of events like changing image size. And I don't see the point of blocking that for the 2nd/3rd/... process. In multi-threaded environments reading parallel from any device is tricky anyhow. */static ssize_t pwc_video_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ struct video_device *vdev = file->private_data; struct pwc_device *pdev; int noblock = file->f_flags & O_NONBLOCK; DECLARE_WAITQUEUE(wait, current); int bytes_to_read, rv = 0; void *image_buffer_addr; PWC_DEBUG_READ("pwc_video_read(vdev=0x%p, buf=%p, count=%zd) called.\n", vdev, buf, count); if (vdev == NULL) return -EFAULT; pdev = vdev->priv; if (pdev == NULL) return -EFAULT; mutex_lock(&pdev->modlock); if (pdev->error_status) { rv = -pdev->error_status; /* Something happened, report what. */ goto err_out; } /* In case we're doing partial reads, we don't have to wait for a frame */ if (pdev->image_read_pos == 0) { /* Do wait queueing according to the (doc)book */ add_wait_queue(&pdev->frameq, &wait); while (pdev->full_frames == NULL) { /* Check for unplugged/etc. here */ if (pdev->error_status) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); rv = -pdev->error_status ; goto err_out; } if (noblock) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); rv = -EWOULDBLOCK; goto err_out; } if (signal_pending(current)) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); rv = -ERESTARTSYS; goto err_out; } schedule(); set_current_state(TASK_INTERRUPTIBLE); } remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); /* Decompress and release frame */ if (pwc_handle_frame(pdev)) { rv = -EFAULT; goto err_out; } } PWC_DEBUG_READ("Copying data to user space.\n"); if (pdev->vpalette == VIDEO_PALETTE_RAW) bytes_to_read = pdev->frame_size + sizeof(struct pwc_raw_frame); else bytes_to_read = pdev->view.size; /* copy bytes to user space; we allow for partial reads */ if (count + pdev->image_read_pos > bytes_to_read) count = bytes_to_read - pdev->image_read_pos; image_buffer_addr = pdev->image_data; image_buffer_addr += pdev->images[pdev->fill_image].offset; image_buffer_addr += pdev->image_read_pos; if (copy_to_user(buf, image_buffer_addr, count)) { rv = -EFAULT; goto err_out; } pdev->image_read_pos += count; if (pdev->image_read_pos >= bytes_to_read) { /* All data has been read */ pdev->image_read_pos = 0; pwc_next_image(pdev); } mutex_unlock(&pdev->modlock); return count;err_out: mutex_unlock(&pdev->modlock); return rv;}static unsigned int pwc_video_poll(struct file *file, poll_table *wait){ struct video_device *vdev = file->private_data; struct pwc_device *pdev; if (vdev == NULL) return -EFAULT; pdev = vdev->priv; if (pdev == NULL) return -EFAULT; poll_wait(file, &pdev->frameq, wait); if (pdev->error_status) return POLLERR; if (pdev->full_frames != NULL) /* we have frames waiting */ return (POLLIN | POLLRDNORM); return 0;}static int pwc_video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ struct video_device *vdev = file->private_data; struct pwc_device *pdev; int r = -ENODEV; if (!vdev) goto out; pdev = vdev->priv; mutex_lock(&pdev->modlock); if (!pdev->unplugged) r = video_usercopy(inode, file, cmd, arg, pwc_video_do_ioctl); mutex_unlock(&pdev->modlock);out: return r;}static int pwc_video_mmap(struct file *file, struct vm_area_struct *vma){ struct video_device *vdev = file->private_data; struct pwc_device *pdev; unsigned long start; unsigned long size; unsigned long page, pos = 0; int index; PWC_DEBUG_MEMORY(">> %s\n", __FUNCTION__); pdev = vdev->priv; size = vma->vm_end - vma->vm_start; start = vma->vm_start; /* Find the idx buffer for this mapping */ for (index = 0; index < pwc_mbufs; index++) { pos = pdev->images[index].offset; if ((pos>>PAGE_SHIFT) == vma->vm_pgoff) break; } if (index == MAX_IMAGES) return -EINVAL; if (index == 0) { /* * Special case for v4l1. In v4l1, we map only one big buffer, * but in v4l2 each buffer is mapped */ unsigned long total_size; total_size = pwc_mbufs * pdev->len_per_image; if (size != pdev->len_per_image && size != total_size) { PWC_ERROR("Wrong size (%lu) needed to be len_per_image=%d or total_size=%lu\n", size, pdev->len_per_image, total_size); return -EINVAL; } } else if (size > pdev->len_per_image) return -EINVAL; vma->vm_flags |= VM_IO; /* from 2.6.9-acX */ pos += (unsigned long)pdev->image_data; while (size > 0) { page = vmalloc_to_pfn((void *)pos); if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) return -EAGAIN; start += PAGE_SIZE; pos += PAGE_SIZE; if (size > PAGE_SIZE) size -= PAGE_SIZE; else size = 0; } return 0;}/***************************************************************************//* USB functions *//* This function gets called when a new device is plugged in or the usb core * is loaded. */static int usb_pwc_probe(struct usb_interface *intf, const struct usb_device_id *id){ struct usb_device *udev = interface_to_usbdev(intf); struct pwc_device *pdev = NULL; int vendor_id, product_id, type_id; int i, hint, rc; int features = 0; int video_nr = -1; /* default: use next available device */ char serial_number[30], *name; vendor_id = le16_to_cpu(udev->descriptor.idVendor); product_id = le16_to_cpu(udev->descriptor.idProduct); /* 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; default: return -ENODEV; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -