📄 pwc-if.c
字号:
spin_lock_irqsave(&pdev->ptrlock, flags); /* First grab our read_frame; this is removed from all lists, so we can release the lock after this without problems */ if (pdev->read_frame != NULL) { /* This can't theoretically happen */ PWC_ERROR("Huh? Read frame still in use?\n"); spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret; } if (pdev->full_frames == NULL) { PWC_ERROR("Woops. No frames ready.\n"); } else { pdev->read_frame = pdev->full_frames; pdev->full_frames = pdev->full_frames->next; pdev->read_frame->next = NULL; } if (pdev->read_frame != NULL) { /* Decompression is a lenghty process, so it's outside of the lock. This gives the isoc_handler the opportunity to fill more frames in the mean time. */ spin_unlock_irqrestore(&pdev->ptrlock, flags); ret = pwc_decompress(pdev); spin_lock_irqsave(&pdev->ptrlock, flags); /* We're done with read_buffer, tack it to the end of the empty buffer list */ if (pdev->empty_frames == NULL) { pdev->empty_frames = pdev->read_frame; pdev->empty_frames_tail = pdev->empty_frames; } else { pdev->empty_frames_tail->next = pdev->read_frame; pdev->empty_frames_tail = pdev->read_frame; } pdev->read_frame = NULL; } spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret;}/** \brief Advance pointers of image buffer (after each user request)*/void pwc_next_image(struct pwc_device *pdev){ pdev->image_used[pdev->fill_image] = 0; pdev->fill_image = (pdev->fill_image + 1) % pwc_mbufs;}/** * Print debug information when a frame is discarded because all of our buffer * is full */static void pwc_frame_dumped(struct pwc_device *pdev){ pdev->vframes_dumped++; if (pdev->vframe_count < FRAME_LOWMARK) return; if (pdev->vframes_dumped < 20) PWC_DEBUG_FLOW("Dumping frame %d\n", pdev->vframe_count); else if (pdev->vframes_dumped == 20) PWC_DEBUG_FLOW("Dumping frame %d (last message)\n", pdev->vframe_count);}static int pwc_rcv_short_packet(struct pwc_device *pdev, const struct pwc_frame_buf *fbuf){ int awake = 0; /* 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) { unsigned char *ptr = (unsigned char *)fbuf->data; if (ptr[1] == 1 && ptr[0] & 0x10) { PWC_TRACE("Hyundai CMOS sensor bug. Dropping frame.\n"); pdev->drop_frames += 2; pdev->vframes_error++; } if ((ptr[0] ^ pdev->vmirror) & 0x01) { if (ptr[0] & 0x01) { pdev->snapshot_button_status = 1; PWC_TRACE("Snapshot button pressed.\n"); } else { PWC_TRACE("Snapshot button released.\n"); } } 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++; } else if (pdev->type == 740 || pdev->type == 720) { unsigned char *ptr = (unsigned char *)fbuf->data; if ((ptr[0] ^ pdev->vmirror) & 0x01) { if (ptr[0] & 0x01) { pdev->snapshot_button_status = 1; PWC_TRACE("Snapshot button pressed.\n"); } else PWC_TRACE("Snapshot button released.\n"); } pdev->vmirror = ptr[0] & 0x03; } /* In case we were instructed to drop the frame, do so silently. The buffer pointers are not updated either (but the counters are reset below). */ 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); pdev->vframes_error++; } else { /* Send only once per EOF */ awake = 1; /* delay wake_ups */ /* Find our next frame to fill. This will always succeed, since we * nick a frame from either empty or full list, but if we had to * take it from the full list, it means a frame got dropped. */ if (pwc_next_fill_frame(pdev)) pwc_frame_dumped(pdev); } } /* !drop_frames */ pdev->vframe_count++; return awake;}/* 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. */static void pwc_isoc_handler(struct urb *urb){ struct pwc_device *pdev; int i, fst, flen; int awake; struct pwc_frame_buf *fbuf; unsigned char *fillptr = NULL, *iso_buf = NULL; awake = 0; pdev = (struct pwc_device *)urb->context; if (pdev == NULL) { PWC_ERROR("isoc_handler() called with NULL device?!\n"); return; } if (urb->status == -ENOENT || urb->status == -ECONNRESET) { PWC_DEBUG_OPEN("URB (%p) unlinked %ssynchronuously.\n", urb, urb->status == -ENOENT ? "" : "a"); return; } if (urb->status != -EINPROGRESS && urb->status != 0) { const char *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_DEBUG_FLOW("pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); /* Give up after a number of contiguous errors on the USB bus. Appearantly something is wrong so we simulate an unplug event. */ if (++pdev->visoc_errors > MAX_ISOC_ERRORS) { PWC_INFO("Too many ISOC errors, bailing out.\n"); pdev->error_status = EIO; awake = 1; wake_up_interruptible(&pdev->frameq); } goto handler_end; // ugly, but practical } fbuf = pdev->fill_frame; if (fbuf == NULL) { PWC_ERROR("pwc_isoc_handler without valid fill frame.\n"); awake = 1; goto handler_end; } else { fillptr = fbuf->data + fbuf->filled; } /* Reset ISOC error counter. We did get here, after all. */ pdev->visoc_errors = 0; /* vsync: 0 = don't copy data 1 = sync-hunt 2 = synched */ /* Compact data */ for (i = 0; i < urb->number_of_packets; i++) { fst = urb->iso_frame_desc[i].status; flen = urb->iso_frame_desc[i].actual_length; iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; if (fst == 0) { if (flen > 0) { /* if valid data... */ if (pdev->vsync > 0) { /* ...and we are not sync-hunting... */ pdev->vsync = 2; /* ...copy data to frame buffer, if possible */ if (flen + fbuf->filled > pdev->frame_total_size) { PWC_DEBUG_FLOW("Frame buffer overflow (flen = %d, frame_total_size = %d).\n", flen, pdev->frame_total_size); pdev->vsync = 0; /* Hmm, let's wait for an EOF (end-of-frame) */ pdev->vframes_error++; } else { memmove(fillptr, iso_buf, flen); fillptr += flen; } } fbuf->filled += flen; } /* ..flen > 0 */ if (flen < pdev->vlast_packet_size) { /* Shorter packet... We probably have the end of an image-frame; wake up read() process and let select()/poll() do something. Decompression is done in user time over there. */ if (pdev->vsync == 2) { if (pwc_rcv_short_packet(pdev, fbuf)) { awake = 1; fbuf = pdev->fill_frame; } } fbuf->filled = 0; fillptr = fbuf->data; pdev->vsync = 1; } pdev->vlast_packet_size = flen; } /* ..status == 0 */ else { /* This is normally not interesting to the user, unless * you are really debugging something */ static int iso_error = 0; iso_error++; if (iso_error < 20) PWC_DEBUG_FLOW("Iso frame %d of USB has error %d\n", i, fst); } }handler_end: if (awake) wake_up_interruptible(&pdev->frameq); urb->dev = pdev->udev; i = usb_submit_urb(urb, GFP_ATOMIC); if (i != 0) PWC_ERROR("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);}int 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; if (pdev == NULL) return -EFAULT; if (pdev->iso_init) return 0; pdev->vsync = 0; udev = pdev->udev; /* Get the current alternate interface, adjust packet size */ if (!udev->actconfig) return -EFAULT; intf = usb_ifnum_to_if(udev, 0); if (intf) idesc = usb_altnum_to_altsetting(intf, pdev->valternate); if (!idesc) return -EFAULT; /* 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 */ ret = 0; PWC_DEBUG_OPEN("Setting alternate interface %d\n", pdev->valternate); ret = usb_set_interface(pdev->udev, 0, pdev->valternate); if (ret < 0) return ret; for (i = 0; i < MAX_ISO_BUFS; i++) { urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { PWC_ERROR("Failed to allocate urb %d\n", i); ret = -ENOMEM; break; } pdev->sbuf[i].urb = urb; PWC_DEBUG_MEMORY("Allocated URB at 0x%p\n", urb); } if (ret) { /* De-allocate in reverse order */ while (i--) { usb_free_urb(pdev->sbuf[i].urb); pdev->sbuf[i].urb = NULL; } return ret; } /* init URB structure */ for (i = 0; i < MAX_ISO_BUFS; i++) { urb = pdev->sbuf[i].urb; urb->interval = 1; // devik urb->dev = udev; urb->pipe = usb_rcvisocpipe(udev, pdev->vendpoint); urb->transfer_flags = URB_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 = 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) PWC_ERROR("isoc_init() submit_urb %d failed with error %d\n", i, ret); else PWC_DEBUG_MEMORY("URB 0x%p submitted.\n", pdev->sbuf[i].urb); } /* All is done... */ pdev->iso_init = 1; PWC_DEBUG_OPEN("<< pwc_isoc_init()\n"); return 0;}static void pwc_iso_stop(struct pwc_device *pdev){ int i; /* 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) { PWC_DEBUG_MEMORY("Unlinking URB %p\n", urb); usb_kill_urb(urb); } }}static void 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; urb = pdev->sbuf[i].urb; if (urb != 0) { PWC_DEBUG_MEMORY("Freeing URB\n"); usb_free_urb(urb); pdev->sbuf[i].urb = NULL; } }}void pwc_isoc_cleanup(struct pwc_device *pdev){ PWC_DEBUG_OPEN(">> pwc_isoc_cleanup()\n"); if (pdev == NULL) return; if (pdev->iso_init == 0) return; pwc_iso_stop(pdev); pwc_iso_free(pdev); /* 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) { PWC_DEBUG_OPEN("Setting alternate interface 0.\n"); usb_set_interface(pdev->udev, 0, 0); } pdev->iso_init = 0; PWC_DEBUG_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) { PWC_DEBUG_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) { PWC_DEBUG_FLOW("pwc_set_video_mode attempt 2 failed.\n"); } } if (start == 0) { if (pwc_isoc_init(pdev) < 0) { PWC_WARNING("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 */}/********* * sysfs *********/static struct pwc_device *cd_to_pwc(struct device *cd){ struct video_device *vdev = to_video_device(cd); return video_get_drvdata(vdev);}static ssize_t show_pan_tilt(struct device *class_dev, struct device_attribute *attr, char *buf){ struct pwc_device *pdev = cd_to_pwc(class_dev); return sprintf(buf, "%d %d\n", pdev->pan_angle, pdev->tilt_angle);}static ssize_t store_pan_tilt(struct device *class_dev, struct device_attribute *attr, const char *buf, size_t count){ struct pwc_device *pdev = cd_to_pwc(class_dev); int pan, tilt; int ret = -EINVAL; if (strncmp(buf, "reset", 5) == 0) ret = pwc_mpt_reset(pdev, 0x3); else if (sscanf(buf, "%d %d", &pan, &tilt) > 0) ret = pwc_mpt_set_angle(pdev, pan, tilt); if (ret < 0) return ret; return strlen(buf);}static DEVICE_ATTR(pan_tilt, S_IRUGO | S_IWUSR, show_pan_tilt, store_pan_tilt);static ssize_t show_snapshot_button_status(struct device *class_dev, struct device_attribute *attr, char *buf){ struct pwc_device *pdev = cd_to_pwc(class_dev); int status = pdev->snapshot_button_status; pdev->snapshot_button_status = 0; return sprintf(buf, "%d\n", status);}static DEVICE_ATTR(button, S_IRUGO | S_IWUSR, show_snapshot_button_status, NULL);static int pwc_create_sysfs_files(struct video_device *vdev)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -