📄 pwc-if.c
字号:
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 = pdev->vmax_packet_size; } } /* link */ for (i = 0; i < MAX_ISO_BUFS; i++) { ret = usb_submit_urb(pdev->sbuf[i].urb, GFP_KERNEL); if (ret) Err("isoc_init() submit_urb %d failed with error %d\n", i, ret); else Trace(TRACE_MEMORY, "URB 0x%p submitted.\n", pdev->sbuf[i].urb); } /* All is done... */ pdev->iso_init = 1; Trace(TRACE_OPEN, "<< pwc_isoc_init()\n"); return 0;}static void pwc_isoc_cleanup(struct pwc_device *pdev){ int i; Trace(TRACE_OPEN, ">> pwc_isoc_cleanup()\n"); if (pdev == NULL) return; /* Unlinking ISOC buffers one by one */ for (i = 0; i < MAX_ISO_BUFS; i++) { struct urb *urb; urb = pdev->sbuf[i].urb; if (urb != 0) { if (pdev->iso_init) { Trace(TRACE_MEMORY, "Unlinking URB %p\n", urb); usb_unlink_urb(urb); } Trace(TRACE_MEMORY, "Freeing URB\n"); usb_free_urb(urb); pdev->sbuf[i].urb = NULL; } } /* Stop camera, but only if we are sure the camera is still there (unplug is signalled by EPIPE) */ if (pdev->error_status && pdev->error_status != EPIPE) { Trace(TRACE_OPEN, "Setting alternate interface 0.\n"); usb_set_interface(pdev->udev, 0, 0); } pdev->iso_init = 0; Trace(TRACE_OPEN, "<< pwc_isoc_cleanup()\n");}int pwc_try_video_mode(struct pwc_device *pdev, int width, int height, int new_fps, int new_compression, int new_snapshot){ int ret, start; /* Stop isoc stuff */ pwc_isoc_cleanup(pdev); /* Reset parameters */ pwc_reset_buffers(pdev); /* Try to set video mode... */ start = ret = pwc_set_video_mode(pdev, width, height, new_fps, new_compression, new_snapshot); if (ret) { Trace(TRACE_FLOW, "pwc_set_video_mode attempt 1 failed.\n"); /* That failed... restore old mode (we know that worked) */ start = pwc_set_video_mode(pdev, pdev->view.x, pdev->view.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot); if (start) { Trace(TRACE_FLOW, "pwc_set_video_mode attempt 2 failed.\n"); } } if (start == 0) { if (pwc_isoc_init(pdev) < 0) { Info("Failed to restart ISOC transfers in pwc_try_video_mode.\n"); ret = -EAGAIN; /* let's try again, who knows if it works a second time */ } } pdev->drop_frames++; /* try to avoid garbage during switch */ return ret; /* Return original error code */}/***************************************************************************//* Video4Linux functions */static int pwc_video_open(struct inode *inode, struct file *file){ int i; struct video_device *vdev = video_devdata(file); struct pwc_device *pdev; Trace(TRACE_OPEN, ">> video_open called(vdev = 0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; if (pdev == NULL) BUG(); if (pdev->vopen) return -EBUSY; down(&pdev->modlock); if (!pdev->usb_init) { Trace(TRACE_OPEN, "Doing first time initialization.\n"); pdev->usb_init = 1; if (pwc_trace & TRACE_OPEN) { /* Query sensor type */ const char *sensor_type = NULL; int ret; ret = pwc_get_cmos_sensor(pdev, &i); if (ret >= 0) { switch(i) { case 0x00: sensor_type = "Hyundai CMOS sensor"; break; case 0x20: sensor_type = "Sony CCD sensor + TDA8787"; break; case 0x2E: sensor_type = "Sony CCD sensor + Exas 98L59"; break; case 0x2F: sensor_type = "Sony CCD sensor + ADI 9804"; break; case 0x30: sensor_type = "Sharp CCD sensor + TDA8787"; break; case 0x3E: sensor_type = "Sharp CCD sensor + Exas 98L59"; break; case 0x3F: sensor_type = "Sharp CCD sensor + ADI 9804"; break; case 0x40: sensor_type = "UPA 1021 sensor"; break; case 0x100: sensor_type = "VGA sensor"; break; case 0x101: sensor_type = "PAL MR sensor"; break; default: sensor_type = "unknown type of sensor"; break; } } if (sensor_type != NULL) Info("This %s camera is equipped with a %s (%d).\n", pdev->vdev->name, sensor_type, i); } } /* 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 pwc_construct(pdev); /* set min/max sizes correct */ /* 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->visoc_errors = 0; pdev->error_status = 0;#if PWC_DEBUG pdev->sequence = 0;#endif 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) { 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 = pwc_isoc_init(pdev); if (i) { Trace(TRACE_OPEN, "Failed to init ISOC stuff = %d.\n", i); up(&pdev->modlock); return i; } pdev->vopen++; file->private_data = vdev; /* 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() returns 0.\n"); return 0;}/* 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; Trace(TRACE_OPEN, ">> video_close called(vdev = 0x%p).\n", vdev); pdev = (struct pwc_device *)vdev->priv; if (pdev->vopen == 0) Info("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) 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->decompressor != NULL) { pdev->decompressor->exit(); pdev->decompressor->unlock(); pdev->decompressor = NULL; } pwc_isoc_cleanup(pdev); pwc_free_buffers(pdev); /* Turn off LEDS and power down camera, but only when not unplugged */ if (pdev->error_status != EPIPE) { /* Turn LEDs off */ if (pwc_set_leds(pdev, 0, 0) < 0) Info("Failed to set LED on/off time.\n"); if (power_save) { i = pwc_camera_power(pdev, 0); if (i < 0) Err("Failed to power down camera (%d)\n", i); } } pdev->vopen = 0; Trace(TRACE_OPEN, "<< video_close()\n"); 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 *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; Trace(TRACE_READ, "video_read(0x%p, %p, %d) called.\n", vdev, buf, count); if (vdev == NULL) return -EFAULT; pdev = vdev->priv; if (pdev == NULL) return -EFAULT; if (pdev->error_status) return -pdev->error_status; /* Something happened, report what. */ /* 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); return -pdev->error_status ; } 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 and release frame */ if (pwc_handle_frame(pdev)) return -EFAULT; } Trace(TRACE_READ, "Copying data to user space.\n"); if (pdev->vpalette == VIDEO_PALETTE_RAW) bytes_to_read = pdev->frame_size; 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; 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 >= bytes_to_read) { /* All data has been read */ pdev->image_read_pos = 0; pwc_next_image(pdev); } return count;}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_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg){ struct video_device *vdev = file->private_data; 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 = arg; 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; break; } /* Channel functions (simulate 1 channel) */ case VIDIOCGCHAN: { struct video_channel *v = arg; if (v->channel != 0) return -EINVAL; v->flags = 0; v->tuners = 0; v->type = VIDEO_TYPE_CAMERA; strcpy(v->name, "Webcam"); return 0; } case VIDIOCSCHAN: { /* The spec says the argument is an integer, but the bttv driver uses a video_channel arg, which makes sense becasue it also has the norm flag. */ struct video_channel *v = arg; if (v->channel != 0) return -EINVAL; return 0; } /* Picture functions; contrast etc. */ case VIDIOCGPICT: { struct video_picture *p = arg; int val; val = pwc_get_brightness(pdev); if (val >= 0)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -