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

📄 pwc-if.c

📁 基于S3CEB2410平台LINUX操作系统下 USB驱动源代码
💻 C
📖 第 1 页 / 共 5 页
字号:
   The frame buffer is the second scheme, and is the central element here.   It collects the data from a single frame from the camera (hence, the   name). Frames are delimited by the USB camera with a short USB packet,   so that's easy to detect. The frame buffers form a list that is filled   by the camera+USB controller and drained by the user process through    either read() or mmap().      The image buffer is the third scheme, in which frames are decompressed   and possibly converted into planar format. For mmap() there is more than   one image buffer available.   The frame buffers provide the image buffering, in case the user process   is a bit slow. This introduces lag and some undesired side-effects.   The problem arises when the frame buffer is full. I used to drop the last    frame, which makes the data in the queue stale very quickly. But dropping    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 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;}/* 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(purb_t 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;	purb_t 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);		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) {

⌨️ 快捷键说明

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