📄 pwc-if.c
字号:
pdev->fill_image = 0; spin_unlock_irqrestore(&pdev->ptrlock, flags);}/** \brief Do all the handling for getting one frame: get pointer, decompress, advance pointers. */static 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 */ Err("Huh? Read frame still in use?\n"); } else { if (pdev->full_frames == NULL) { Err("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) {#if PWC_DEBUG Trace(TRACE_SEQUENCE, "Decompressing frame %d\n", pdev->read_frame->sequence);#endif /* 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) */static inline void pwc_next_image(struct pwc_device *pdev){ pdev->image_used[pdev->fill_image] = 0; pdev->fill_image = (pdev->fill_image + 1) % default_mbufs;}/* 2001-10-14: The YUV420 is still there, but you can only set it from within a program (YUV420P being the default) */static int pwc_set_palette(struct pwc_device *pdev, int pal){ if ( pal == VIDEO_PALETTE_YUV420 || pal == VIDEO_PALETTE_YUV420P#if PWC_DEBUG || pal == VIDEO_PALETTE_RAW#endif ) { pdev->vpalette = pal; pwc_set_image_buffer_size(pdev); return 0; } Trace(TRACE_READ, "Palette %d not supported.\n", pal); return -1;}/* 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, *iso_buf; pdev = (struct pwc_device *)urb->context; if (pdev == NULL) { Err("isoc_handler() called with NULL device?!\n"); return; }#ifdef PWC_MAGIC if (pdev->magic != PWC_MAGIC) { Err("isoc_handler() called with bad magic!\n"); return; }#endif if (urb->status == -ENOENT || urb->status == -ECONNRESET) { Trace(TRACE_OPEN, "pwc_isoc_handler(): URB unlinked.\n"); return; } if (urb->status != -EINPROGRESS && urb->status != 0) { 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"; break; case -ETIMEDOUT: errmsg = "NAK (device does not respond)"; break; } Trace(TRACE_FLOW, "pwc_isoc_handler() called with status %d [%s].\n", urb->status, errmsg); return; } fbuf = pdev->fill_frame; if (fbuf == NULL) { Err("pwc_isoc_handler without valid fill frame.\n"); wake_up_interruptible(&pdev->frameq); return; } fillptr = fbuf->data + fbuf->filled; awake = 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_size) { Trace(TRACE_FLOW, "Frame buffer overflow (flen = %d, frame_size = %d).\n", flen, pdev->frame_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) { /* 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) {#if PWC_DEBUG Debug("Hyundai CMOS sensor bug. Dropping frame %d.\n", fbuf->sequence);#endif pdev->drop_frames = 2; pdev->vframes_error++; } /* 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++; } /* 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) pdev->drop_frames--; else { /* Check for underflow first */ if (fbuf->filled < pdev->frame_size) { Trace(TRACE_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)) { pdev->vframes_dumped++; if ((pdev->vframe_count > FRAME_LOWMARK) && (pwc_trace & TRACE_FLOW)) { if (pdev->vframes_dumped < 20) Trace(TRACE_FLOW, "Dumping frame %d.\n", pdev->vframe_count); if (pdev->vframes_dumped == 20) Trace(TRACE_FLOW, "Dumping frame %d (last message).\n", pdev->vframe_count); } } fbuf = pdev->fill_frame; } } /* !drop_frames */ pdev->vframe_count++; } fbuf->filled = 0; fillptr = fbuf->data; pdev->vsync = 1; } /* .. flen < last_packet_size */ pdev->vlast_packet_size = flen; } /* ..status == 0 */#ifdef PWC_DEBUG /* This is normally not interesting to the user, unless you are really debugging something */ else Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);#endif } if (awake) wake_up_interruptible(&pdev->frameq);}static int pwc_isoc_init(struct pwc_device *pdev){ struct usb_device *udev; struct urb *urb; int i, j, ret; struct usb_interface_descriptor *idesc; int cur_alt; 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; cur_alt = udev->actconfig->interface[0].act_altsetting; idesc = &udev->actconfig->interface[0].altsetting[cur_alt]; if (!idesc) return -EFAULT; /* Search video endpoint */ pdev->vmax_packet_size = -1; for (i = 0; i < idesc->bNumEndpoints; i++) if ((idesc->endpoint[i].bEndpointAddress & 0xF) == pdev->vendpoint) { pdev->vmax_packet_size = idesc->endpoint[i].wMaxPacketSize; break; } if (pdev->vmax_packet_size < 0) { Err("Failed to find packet size for video endpoint in current alternate setting.\n"); return -ENFILE; /* Odd error, that should be noticable */ } ret = 0; for (i = 0; i < MAX_ISO_BUFS; i++) { urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); if (urb == NULL) { Err("Failed to allocate urb %d\n", i); ret = -ENOMEM; break; } pdev->sbuf[i].urb = urb; } if (ret) { /* De-allocate in reverse order */ while (i >= 0) { 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, GFP_KERNEL); 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 inode *inode, struct file *file){ int i; struct video_device *vdev = video_devdata(file); struct pwc_device *pdev; Trace(TRACE_OPEN, "video_open called(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"); /* 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);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -