⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 pwc-if.c

📁 linux-2.6.15.6
💻 C
📖 第 1 页 / 共 5 页
字号:
   the frame at the head of the queue proved to be a litte bit more difficult.   I tried a circular linked scheme, but this introduced more problems than   it solved.   Because filling and draining are completely asynchronous processes, this   requires some fiddling with pointers and mutexes.   Eventually, I came up with a system with 2 lists: an 'empty' frame list   and a 'full' frame list:     * Initially, all frame buffers but one are on the 'empty' list; the one       remaining buffer is our initial fill frame.     * If a frame is needed for filling, we try to take it from the 'empty'        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 inline 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 */#if PWC_DEBUG		/* sanity check */		if (pdev->full_frames == NULL) {			Err("Neither empty or full frames available!\n");			spin_unlock_irqrestore(&pdev->ptrlock, flags);			return -EINVAL;		}#endif		pdev->fill_frame = pdev->full_frames;		pdev->full_frames = pdev->full_frames->next;		ret = 1;	}	pdev->fill_frame->next = NULL;#if PWC_DEBUG	Trace(TRACE_SEQUENCE, "Assigning sequence number %d.\n", pdev->sequence);	pdev->fill_frame->sequence = pdev->sequence++;#endif	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;	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);}/**  \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;}/* 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 pt_regs *regs){	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) {		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 (%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 -ETIMEDOUT:	errmsg = "NAK (device does not respond)"; break;		}		Trace(TRACE_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)		{			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) {		Err("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) {						Trace(TRACE_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) {					/* 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++;						}						if ((ptr[0] ^ pdev->vmirror) & 0x01) {							if (ptr[0] & 0x01)								Info("Snapshot button pressed.\n");							else								Info("Snapshot button released.\n");						}						if ((ptr[0] ^ pdev->vmirror) & 0x02) {							if (ptr[0] & 0x02)								Info("Image is mirrored.\n");							else								Info("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++;					}					/* 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) {							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 */#if PWC_DEBUG		/* This is normally not interesting to the user, unless you are really debugging something */		else {			static int iso_error = 0;			iso_error++;			if (iso_error < 20)				Trace(TRACE_FLOW, "Iso frame %d of USB has error %d\n", i, fst);		}#endif	}handler_end:	if (awake)		wake_up_interruptible(&pdev->frameq);	urb->dev = pdev->udev;	i = usb_submit_urb(urb, GFP_ATOMIC);	if (i != 0)		Err("Error (%d) re-submitting urb in pwc_isoc_handler.\n", i);}static 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;#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,5)	idesc = &udev->actconfig->interface[0]->altsetting[pdev->valternate];#else	intf = usb_ifnum_to_if(udev, 0);	if (intf)		idesc = usb_altnum_to_altsetting(intf, pdev->valternate);#endif			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) {		Err("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;	Trace(TRACE_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) {			Err("Failed to allocate urb %d\n", i);			ret = -ENOMEM;			break;		}		pdev->sbuf[i].urb = urb;		Trace(TRACE_MEMORY, "Allocated URB at 0x%p\n", urb);	}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -