📄 pwc-if.c
字号:
list, unless that list is empty, in which case we take the buffer at the head of the 'full' list. * When our fill buffer has been filled, it is appended to the 'full' list. * If a frame is needed by read() or mmap(), it is taken from the head of the 'full' list, handled, and then appended to the 'empty' list. If no buffer is present on the 'full' list, we wait. The advantage is that the buffer that is currently being decompressed/ converted, is on neither list, and thus not in our way (any other scheme I tried had the problem of old data lingering in the queue). Whatever strategy you choose, it always remains a tradeoff: with more frame buffers the chances of a missed frame are reduced. On the other hand, on slower machines it introduces lag because the queue will always be full. *//** \brief Find next frame buffer to fill. Take from empty or full list, whichever comes first. */static int pwc_next_fill_frame(struct pwc_device *pdev){ int ret; unsigned long flags; ret = 0; spin_lock_irqsave(&pdev->ptrlock, flags); if (pdev->fill_frame != NULL) { /* append to 'full' list */ if (pdev->full_frames == NULL) { pdev->full_frames = pdev->fill_frame; pdev->full_frames_tail = pdev->full_frames; } else { pdev->full_frames_tail->next = pdev->fill_frame; pdev->full_frames_tail = pdev->fill_frame; } } if (pdev->empty_frames != NULL) { /* We have empty frames available. That's easy */ pdev->fill_frame = pdev->empty_frames; pdev->empty_frames = pdev->empty_frames->next; } else { /* Hmm. Take it from the full list */ /* sanity check */ if (pdev->full_frames == NULL) { PWC_ERROR("Neither empty or full frames available!\n"); spin_unlock_irqrestore(&pdev->ptrlock, flags); return -EINVAL; } pdev->fill_frame = pdev->full_frames; pdev->full_frames = pdev->full_frames->next; ret = 1; } pdev->fill_frame->next = NULL; spin_unlock_irqrestore(&pdev->ptrlock, flags); return ret;}/** \brief Reset all buffers, pointers and lists, except for the image_used[] buffer. If the image_used[] buffer is cleared too, mmap()/VIDIOCSYNC will run into trouble. */static void pwc_reset_buffers(struct pwc_device *pdev){ int i; unsigned long flags; PWC_DEBUG_MEMORY(">> %s __enter__\n", __func__); spin_lock_irqsave(&pdev->ptrlock, flags); pdev->full_frames = NULL; pdev->full_frames_tail = NULL; for (i = 0; i < default_fbufs; i++) { pdev->fbuf[i].filled = 0; if (i > 0) pdev->fbuf[i].next = &pdev->fbuf[i - 1]; else pdev->fbuf->next = NULL; } pdev->empty_frames = &pdev->fbuf[default_fbufs - 1]; pdev->empty_frames_tail = pdev->fbuf; pdev->read_frame = NULL; pdev->fill_frame = pdev->empty_frames; pdev->empty_frames = pdev->empty_frames->next; pdev->image_read_pos = 0; pdev->fill_image = 0; spin_unlock_irqrestore(&pdev->ptrlock, flags); PWC_DEBUG_MEMORY("<< %s __leaving__\n", __func__);}/** \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. */int pwc_handle_frame(struct pwc_device *pdev){ int ret = 0; unsigned long flags; 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 lengthy 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. */#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19)static void pwc_isoc_handler(struct urb *urb, struct pt_regs *regs)#elsestatic void pwc_isoc_handler(struct urb *urb)#endif{ 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, default = 0 */ static int iso_error; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -