📄 pwc-if.c
字号:
if (pdev->sbuf[i].urb != NULL) usb_free_urb(pdev->sbuf[i].urb); pdev->sbuf[i].urb = NULL; i--; } return ret; } /* init URB structure */ for (i = 0; i < MAX_ISO_BUFS; i++) { urb = pdev->sbuf[i].urb; urb->next = pdev->sbuf[(i + 1) % MAX_ISO_BUFS].urb; urb->dev = udev; urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); urb->transfer_flags = USB_ISO_ASAP; urb->transfer_buffer = pdev->sbuf[i].data; urb->transfer_buffer_length = ISO_BUFFER_SIZE; urb->complete = pwc_isoc_handler; urb->context = pdev; urb->start_frame = 0; urb->number_of_packets = ISO_FRAMES_PER_DESC; for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; } } /* link */ for (i = 0; i < MAX_ISO_BUFS; i++) { ret = usb_submit_urb(pdev->sbuf[i].urb); if (ret) Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); else Trace(TRACE_OPEN, "pwc_isoc_init(): URB submitted.\n"); } /* data should stream in now */ pdev->iso_init = 1; return 0;}static void pwc_isoc_cleanup(struct pwc_device *pdev){ int i; if (pdev == NULL) return; if (!pdev->iso_init) return; /* Stop camera, but only if we are sure the camera is still there */ if (!pdev->unplugged) usb_set_interface(pdev->udev, 0, 0); /* Unlinking ISOC buffers one by one */ for (i = MAX_ISO_BUFS - 1; i >= 0; i--) { pdev->sbuf[i].urb->next = NULL; usb_unlink_urb(pdev->sbuf[i].urb); usb_free_urb(pdev->sbuf[i].urb); pdev->sbuf[i].urb = NULL; } pdev->iso_init = 0;}int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot){ int ret; /* Stop isoc stuff */ pwc_isoc_cleanup(pdev); /* Reset parameters */ pwc_reset_buffers(pdev); /* Try to set video mode... */ ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); if (ret) /* That failed... restore old mode (we know that worked) */ ret = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); else /* Set (new) alternate interface */ ret = usb_set_interface(pdev->udev, 0, pdev->valternate); if (!ret) ret = pwc_isoc_init(pdev); pdev->drop_frames = 1; /* try to avoid garbage during switch */ return ret;}static inline void set_mem_leak(void *ptr){ down(&mem_lock); if (mem_leak != NULL) Err("Memleak: overwriting mem_leak pointer!\n"); Trace(TRACE_MEMORY, "Setting mem_leak to 0x%p.\n", ptr); mem_leak = ptr; up(&mem_lock);}static inline void free_mem_leak(void){ down(&mem_lock); if (mem_leak != NULL) { Trace(TRACE_MEMORY, "Freeing mem_leak ptr 0x%p.\n", mem_leak); kfree(mem_leak); mem_leak = NULL; } up(&mem_lock);}/***************************************************************************//* Video4Linux functions */static int pwc_video_open(struct video_device *vdev, int mode){ int i; struct pwc_device *pdev; Trace(TRACE_OPEN, "video_open called(0x%p, 0%o).\n", vdev, mode); if (vdev == NULL) BUG(); pdev = (struct pwc_device *)vdev->priv; if (pdev == NULL) BUG(); down(&pdev->modlock); if (!pdev->usb_init) { Trace(TRACE_OPEN, "Doing first time initialization.\n"); /* Reset camera */ if (usb_set_interface(pdev->udev, 0, 0)) Info("Failed to set alternate interface to 0.\n"); pdev->usb_init = 1; } /* Turn on camera */ if (power_save) { i = pwc_camera_power(pdev, 1); if (i < 0) Info("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) Info("Failed to set LED on/off time.\n"); /* Find our decompressor, if any */ pdev->decompressor = pwc_find_decompressor(pdev->type);#if PWC_DEBUG Debug("Found decompressor for %d at 0x%p\n", pdev->type, pdev->decompressor);#endif /* So far, so good. Allocate memory. */ i = pwc_allocate_buffers(pdev); if (i < 0) { Trace(TRACE_OPEN, "Failed to allocate buffer memory.\n"); up(&pdev->modlock); return i; } /* Reset buffers & parameters */ pwc_reset_buffers(pdev); for (i = 0; i < default_mbufs; i++) pdev->image_used[i] = 0; pdev->vframe_count = 0; pdev->vframes_dumped = 0; pdev->vframes_error = 0; pdev->vpalette = default_palette;#if PWC_DEBUG pdev->sequence = 0;#endif /* Set some defaults */ pdev->vsnapshot = 0; if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) pdev->vsize = PSZ_QSIF; else pdev->vsize = PSZ_QCIF; pdev->vframes = 10; /* Start iso pipe for video; first try user-supplied size/fps, if that fails try QCIF/10 or QSIF/10 (a reasonable default), then give up */ i = pwc_set_video_mode(pdev, pwc_image_sizes[default_size].x, pwc_image_sizes[default_size].y, default_fps, pdev->vcompression, 0); if (i) { Trace(TRACE_OPEN, "First attempt at set_video_mode failed.\n"); if (pdev->type == 730 || pdev->type == 740 || pdev->type == 750) i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QSIF].x, pwc_image_sizes[PSZ_QSIF].y, 10, pdev->vcompression, 0); else i = pwc_set_video_mode(pdev, pwc_image_sizes[PSZ_QCIF].x, pwc_image_sizes[PSZ_QCIF].y, 10, pdev->vcompression, 0); } if (i) { Trace(TRACE_OPEN, "Second attempt at set_video_mode failed.\n"); up(&pdev->modlock); return i; } i = usb_set_interface(pdev->udev, 0, pdev->valternate); if (i) { Trace(TRACE_OPEN, "Failed to set alternate interface = %d.\n", i); up(&pdev->modlock); return -EINVAL; } i = pwc_isoc_init(pdev); if (i) { Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); MOD_DEC_USE_COUNT; up(&pdev->modlock); return i; } pdev->vopen++; /* lock decompressor; this has a small race condition, since we could in theory unload pwcx.o between pwc_find_decompressor() above and this call. I doubt it's ever going to be a problem. */ if (pdev->decompressor != NULL) pdev->decompressor->lock(); up(&pdev->modlock); Trace(TRACE_OPEN, "video_open() returning 0.\n"); return 0;}/* Note that all cleanup is done in the reverse order as in _open */static void pwc_video_close(struct video_device *vdev){ struct pwc_device *pdev; int i; Trace(TRACE_OPEN, "video_close called(0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; if (pdev->vopen == 0) Info("video_close() called on closed device?\n"); /* Free isoc URBs */ pwc_isoc_cleanup(pdev); /* 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) Info("Closing video device: %d frames received, dumped %d frames, %d frames with errors.\n", pdev->vframe_count, pdev->vframes_dumped, pdev->vframes_error); if (pdev->unplugged) { /* The device was unplugged or some other error occured */ /* We unregister the video_device */ Trace(TRACE_OPEN, "Delayed video device unregistered.\n"); video_unregister_device(pdev->vdev); } else { /* Normal close: stop isochronuous and interrupt endpoint */ Trace(TRACE_OPEN, "Normal close(): setting interface to 0.\n"); usb_set_interface(pdev->udev, 0, 0); /* Turn LEDs off */ if (pwc_set_leds(pdev, 0, 0) < 0) Info("Failed to set LED on/off time..\n"); /* Power down camere to save energy */ if (power_save) { i = pwc_camera_power(pdev, 0); if (i < 0) Err("Failed to power down camera (%d)\n", i); } } pdev->vopen = 0; if (pdev->decompressor != NULL) { pdev->decompressor->exit(); pdev->decompressor->unlock(); } pwc_free_buffers(pdev); /* wake up _disconnect() routine */ if (pdev->unplugged) wake_up(&pdev->remove_ok);}/* * 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 long pwc_video_read(struct video_device *vdev, char *buf, unsigned long count, int noblock){ struct pwc_device *pdev; DECLARE_WAITQUEUE(wait, current); Trace(TRACE_READ, "video_read(0x%p, %p, %ld, %d) called.\n", vdev, buf, count, noblock); if (vdev == NULL) return -EFAULT; pdev = vdev->priv; if (pdev == NULL) return -EFAULT; if (pdev->unplugged) { Info("pwc_video_read: Device got unplugged (1).\n"); return -EPIPE; /* unplugged device! */ } /* 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) { if (noblock) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); return -EWOULDBLOCK; } if (signal_pending(current)) { remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); return -ERESTARTSYS; } schedule(); set_current_state(TASK_INTERRUPTIBLE); } remove_wait_queue(&pdev->frameq, &wait); set_current_state(TASK_RUNNING); /* Decompress [, convert] and release frame */ if (pwc_handle_frame(pdev)) return -EFAULT; } Trace(TRACE_READ, "Copying data to user space.\n"); /* copy bytes to user space; we allow for partial reads */ if (count + pdev->image_read_pos > pdev->view.size) count = pdev->view.size - pdev->image_read_pos; if (copy_to_user(buf, pdev->image_ptr[pdev->fill_image] + pdev->image_read_pos, count)) return -EFAULT; pdev->image_read_pos += count; if (pdev->image_read_pos >= pdev->view.size) { /* All data has been read */ pdev->image_read_pos = 0; pwc_next_image(pdev); } return count;}static long pwc_video_write(struct video_device *vdev, const char *buf, unsigned long count, int noblock){ return -EINVAL; }static unsigned int pwc_video_poll(struct video_device *vdev, struct file *file, poll_table *wait){ 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->unplugged) { Info("pwc_video_poll: Device got unplugged.\n"); return POLLERR; } if (pdev->full_frames != NULL) /* we have frames waiting */ return (POLLIN | POLLRDNORM); return 0;} static int pwc_video_ioctl(struct video_device *vdev, unsigned int cmd, void *arg){ struct pwc_device *pdev; DECLARE_WAITQUEUE(wait, current); if (vdev == NULL) return -EFAULT; pdev = vdev->priv; if (pdev == NULL) return -EFAULT; switch (cmd) { /* Query cabapilities */ case VIDIOCGCAP: { struct video_capability caps; strcpy(caps.name, vdev->name); caps.type = VID_TYPE_CAPTURE; caps.channels = 1; caps.audios = 1; caps.minwidth = pdev->view_min.x; caps.minheight = pdev->view_min.y; caps.maxwidth = pdev->view_max.x; caps.maxheight = pdev->view_max.y; if (copy_to_user(arg, &caps, sizeof(caps))) return -EFAULT; break; } /* Channel functions (simulate 1 channel) */ case VIDIOCGCHAN: { struct video_channel v; if (copy_from_user(&v, arg, sizeof(v))) return -EFAULT; if (v.channel != 0) return -EINVAL; v.flags = 0; v.tuners = 0; v.type = VIDEO_TYPE_CAMERA; strcpy(v.name, "Webcam"); if (copy_to_user(arg, &v, sizeof(v))) return -EFAULT; return 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -