📄 usbvideo.c
字号:
vw.height = VIDEOSIZE_Y(uvd->canvas); vw.chromakey = 0; if (VALID_CALLBACK(uvd, getFPS)) vw.flags = GET_CALLBACK(uvd, getFPS)(uvd); else vw.flags = 10; /* FIXME: do better! */ if (copy_to_user(arg, &vw, sizeof(vw))) return -EFAULT; return 0; } case VIDIOCGMBUF: { struct video_mbuf vm; memset(&vm, 0, sizeof(vm)); vm.size = uvd->max_frame_size * 2; vm.frames = 2; vm.offsets[0] = 0; vm.offsets[1] = uvd->max_frame_size; if (copy_to_user((void *)arg, (void *)&vm, sizeof(vm))) return -EFAULT; return 0; } case VIDIOCMCAPTURE: { struct video_mmap vm; if (copy_from_user((void *)&vm, (void *)arg, sizeof(vm))) { err("VIDIOCMCAPTURE: copy_from_user() failed."); return -EFAULT; } if (uvd->debug >= 1) { info("VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.", vm.frame, vm.width, vm.height, vm.format); } /* * Check if the requested size is supported. If the requestor * requests too big a frame then we may be tricked into accessing * outside of own preallocated frame buffer (in uvd->frame). * This will cause oops or a security hole. Theoretically, we * could only clamp the size down to acceptable bounds, but then * we'd need to figure out how to insert our smaller buffer into * larger caller's buffer... this is not an easy question. So we * here just flatly reject too large requests, assuming that the * caller will resubmit with smaller size. Callers should know * what size we support (returned by VIDIOCGCAP). However vidcat, * for one, does not care and allows to ask for any size. */ if ((vm.width > VIDEOSIZE_X(uvd->canvas)) || (vm.height > VIDEOSIZE_Y(uvd->canvas))) { if (uvd->debug > 0) { info("VIDIOCMCAPTURE: Size=%dx%d too large; " "allowed only up to %ldx%ld", vm.width, vm.height, VIDEOSIZE_X(uvd->canvas), VIDEOSIZE_Y(uvd->canvas)); } return -EINVAL; } /* Check if the palette is supported */ if (((1L << vm.format) & uvd->paletteBits) == 0) { if (uvd->debug > 0) { info("VIDIOCMCAPTURE: format=%d. not supported" " (paletteBits=$%08lx)", vm.format, uvd->paletteBits); } return -EINVAL; } if ((vm.frame != 0) && (vm.frame != 1)) { err("VIDIOCMCAPTURE: vm.frame=%d. !E [0,1]", vm.frame); return -EINVAL; } if (uvd->frame[vm.frame].frameState == FrameState_Grabbing) { /* Not an error - can happen */ } uvd->frame[vm.frame].request = VIDEOSIZE(vm.width, vm.height); uvd->frame[vm.frame].palette = vm.format; /* Mark it as ready */ uvd->frame[vm.frame].frameState = FrameState_Ready; return usbvideo_NewFrame(uvd, vm.frame); } case VIDIOCSYNC: { int frameNum, ret; if (copy_from_user((void *)&frameNum, arg, sizeof(frameNum))) { err("VIDIOCSYNC: copy_from_user() failed."); return -EFAULT; } if(frameNum < 0 || frameNum >= USBVIDEO_NUMFRAMES) return -EINVAL; if (uvd->debug >= 1) info("VIDIOCSYNC: syncing to frame %d.", frameNum); if (uvd->flags & FLAGS_NO_DECODING) ret = usbvideo_GetFrame(uvd, frameNum); else if (VALID_CALLBACK(uvd, getFrame)) { ret = GET_CALLBACK(uvd, getFrame)(uvd, frameNum); if ((ret < 0) && (uvd->debug >= 1)) { err("VIDIOCSYNC: getFrame() returned %d.", ret); } } else { err("VIDIOCSYNC: getFrame is not set"); ret = -EFAULT; } /* * The frame is in FrameState_Done_Hold state. Release it * right now because its data is already mapped into * the user space and it's up to the application to * make use of it until it asks for another frame. */ uvd->frame[frameNum].frameState = FrameState_Unused; return ret; } case VIDIOCGFBUF: { struct video_buffer vb; memset(&vb, 0, sizeof(vb)); vb.base = NULL; /* frame buffer not supported, not used */ if (copy_to_user((void *)arg, (void *)&vb, sizeof(vb))) return -EFAULT; return 0; } case VIDIOCKEY: return 0; case VIDIOCCAPTURE: return -EINVAL; case VIDIOCSFBUF: case VIDIOCGTUNER: case VIDIOCSTUNER: case VIDIOCGFREQ: case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: return -EINVAL; default: return -ENOIOCTLCMD; } return 0;}/* * usbvideo_v4l_read() * * This is mostly boring stuff. We simply ask for a frame and when it * arrives copy all the video data from it into user space. There is * no obvious need to override this method. * * History: * 20-Oct-2000 Created. * 01-Nov-2000 Added mutex (uvd->lock). */long usbvideo_v4l_read(struct video_device *dev, char *buf, unsigned long count, int noblock){ static const char proc[] = "usbvideo_v4l_read"; uvd_t *uvd = (uvd_t *) dev; int frmx = -1; usbvideo_frame_t *frame; if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) return -EFAULT; if (uvd->debug >= 1) info("%s: %ld. bytes, noblock=%d.", proc, count, noblock); down(&uvd->lock); /* See if a frame is completed, then use it. */ if ((uvd->frame[0].frameState == FrameState_Done) || (uvd->frame[0].frameState == FrameState_Done_Hold) || (uvd->frame[0].frameState == FrameState_Error)) { frmx = 0; } else if ((uvd->frame[1].frameState >= FrameState_Done) || (uvd->frame[1].frameState == FrameState_Done_Hold) || (uvd->frame[1].frameState >= FrameState_Done)) { frmx = 1; } /* FIXME: If we don't start a frame here then who ever does? */ if (noblock && (frmx == -1)) { count = -EAGAIN; goto read_done; } /* * If no FrameState_Done, look for a FrameState_Grabbing state. * See if a frame is in process (grabbing), then use it. * We will need to wait until it becomes cooked, of course. */ if (frmx == -1) { if (uvd->frame[0].frameState == FrameState_Grabbing) frmx = 0; else if (uvd->frame[1].frameState == FrameState_Grabbing) frmx = 1; } /* * If no frame is active, start one. We don't care which one * it will be, so #0 is as good as any. * In read access mode we don't have convenience of VIDIOCMCAPTURE * to specify the requested palette (video format) on per-frame * basis. This means that we have to return data in -some- format * and just hope that the client knows what to do with it. * The default format is configured in uvd->defaultPalette field * as one of VIDEO_PALETTE_xxx values. We stuff it into the new * frame and initiate the frame filling process. */ if (frmx == -1) { if (uvd->defaultPalette == 0) { err("%s: No default palette; don't know what to do!", proc); count = -EFAULT; goto read_done; } frmx = 0; /* * We have no per-frame control over video size. * Therefore we only can use whatever size was * specified as default. */ uvd->frame[frmx].request = uvd->videosize; uvd->frame[frmx].palette = uvd->defaultPalette; uvd->frame[frmx].frameState = FrameState_Ready; usbvideo_NewFrame(uvd, frmx); /* Now frame 0 is supposed to start filling... */ } /* * Get a pointer to the active frame. It is either previously * completed frame or frame in progress but not completed yet. */ frame = &uvd->frame[frmx]; /* * Sit back & wait until the frame gets filled and postprocessed. * If we fail to get the picture [in time] then return the error. * In this call we specify that we want the frame to be waited for, * postprocessed and switched into FrameState_Done_Hold state. This * state is used to hold the frame as "fully completed" between * subsequent partial reads of the same frame. */ if (frame->frameState != FrameState_Done_Hold) { long rv = -EFAULT; if (uvd->flags & FLAGS_NO_DECODING) rv = usbvideo_GetFrame(uvd, frmx); else if (VALID_CALLBACK(uvd, getFrame)) rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); else err("getFrame is not set"); if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { count = rv; goto read_done; } } /* * Copy bytes to user space. We allow for partial reads, which * means that the user application can request read less than * the full frame size. It is up to the application to issue * subsequent calls until entire frame is read. * * First things first, make sure we don't copy more than we * have - even if the application wants more. That would be * a big security embarassment! */ if ((count + frame->seqRead_Index) > frame->seqRead_Length) count = frame->seqRead_Length - frame->seqRead_Index; /* * Copy requested amount of data to user space. We start * copying from the position where we last left it, which * will be zero for a new frame (not read before). */ if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { count = -EFAULT; goto read_done; } /* Update last read position */ frame->seqRead_Index += count; if (uvd->debug >= 1) { err("%s: {copy} count used=%ld, new seqRead_Index=%ld", proc, count, frame->seqRead_Index); } /* Finally check if the frame is done with and "release" it */ if (frame->seqRead_Index >= frame->seqRead_Length) { /* All data has been read */ frame->seqRead_Index = 0; /* Mark it as available to be used again. */ uvd->frame[frmx].frameState = FrameState_Unused; if (usbvideo_NewFrame(uvd, frmx ? 0 : 1)) { err("%s: usbvideo_NewFrame failed.", proc); } }read_done: up(&uvd->lock); return count;}/* * Make all of the blocks of data contiguous */static int usbvideo_CompressIsochronous(uvd_t *uvd, urb_t *urb){ char *cdata; int i, totlen = 0; for (i = 0; i < urb->number_of_packets; i++) { int n = urb->iso_frame_desc[i].actual_length; int st = urb->iso_frame_desc[i].status; cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; /* Detect and ignore errored packets */ if (st < 0) { if (uvd->debug >= 1) err("Data error: packet=%d. len=%d. status=%d.", i, n, st); uvd->stats.iso_err_count++; continue; } /* Detect and ignore empty packets */ if (n <= 0) { uvd->stats.iso_skip_count++; continue; } totlen += n; /* Little local accounting */ RingQueue_Enqueue(&uvd->dp, cdata, n); } return totlen;}static void usbvideo_IsocIrq(struct urb *urb){ int i, len; uvd_t *uvd = urb->context; /* We don't want to do anything if we are about to be removed! */ if (!CAMERA_IS_OPERATIONAL(uvd)) return;#if 0 if (urb->actual_length > 0) { info("urb=$%p status=%d. errcount=%d. length=%d.", urb, urb->status, urb->error_count, urb->actual_length); } else { static int c = 0; if (c++ % 100 == 0) info("No Isoc data"); }#endif if (!uvd->streaming) { if (uvd->debug >= 1) info("Not streaming, but interrupt!"); return; } uvd->stats.urb_count++; if (urb->actual_length <= 0) goto urb_done_with; /* Copy the data received into ring queue */ len = usbvideo_CompressIsochronous(uvd, urb); uvd->stats.urb_length = len; if (len <= 0) goto urb_done_with; /* Here we got some data */ uvd->stats.data_count += len; RingQueue_WakeUpInterruptible(&uvd->dp);urb_done_with: for (i = 0; i < FRAMES_PER_DESC; i++) { urb->iso_frame_desc[i].status = 0; urb->iso_frame_desc[i].actual_length = 0; } return;}/* * usbvideo_StartDataPump() * * History: * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead * of hardcoded values. Simplified by using for loop, * allowed any number of URBs. */int usbvideo_StartDataPump(uvd_t *uvd){ static const char proc[] = "usbvideo_StartDataPump"; struct usb_device *dev = uvd->dev; int i, errFlag; if (uvd->debug > 1) info("%s($%p)", proc, uvd); if (!CAMERA_IS_OPERATIONAL(uvd)) { err("%s: Camera is not operational",proc); return -EFAULT; } uvd->curframe = -1; /* Alternate interface 1 is is the biggest frame size */ i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); if (i < 0) { err("%s: usb_set_interface error", proc); uvd->last_error = i; return -EBUSY; } if (VALID_CALLBACK(uvd, videoStart)) GET_CALLBACK(uvd, videoStart)(uvd); else err("%s: videoStart not set", proc); /* We double buffer the Iso lists */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { int j, k; urb_t *urb = uvd->sbuf[i].urb; urb->dev = dev; urb->context = uvd; urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); urb->transfer_flags = USB_ISO_ASAP; urb->transfer_buffer = uvd->sbuf[i].data; urb->complete = usbvideo_IsocIrq; urb->number_of_packets = FRAMES_PER_DESC; urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { urb->iso_frame_desc[j].offset = k; urb->iso_frame_desc[j].length = uvd->iso_packet_len; } } /* Link URBs into a ring so that they invoke each other infinitely */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { if ((i+1) < USBVIDEO_NUMSBUF) uvd->sbuf[i].urb->next = uvd->sbuf[i+1].urb; else uvd->sbuf[i].urb->next = uvd->sbuf[0].urb; } /* Submit all URBs */ for (i=0; i < USBVIDEO_NUMSBUF; i++) { errFlag = usb_submit_urb(uvd->sbuf[i].urb); if (errFlag) err("%s: usb_submit_isoc(%d) ret %d", proc, i, errFlag); } uvd->streaming = 1; if (uvd->debug > 1) info("%s: streaming=1 video_endp=$%02x", proc, uvd->video_endp); return 0;}/* * usbvideo_StopDataPump() * * This procedure stops streaming and deallocates URBs. Then it * activates zero-bandwidth alt. setting of the video interface. * * History: * 22-Jan-2000 Corrected order of actions to work after surprise removal. * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. */void usbvideo_StopDataPump(uvd_t *uvd){ static const char proc[] = "usbvideo_StopDataPump"; int i, j; if (uvd->debug > 1) info("%s($%p)", proc, uvd); if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -