📄 usbvideo.c
字号:
* usbvideo_v4l_open() * * This is part of Video 4 Linux API. The driver can be opened by one * client only (checks internal counter 'uvdser'). The procedure * then allocates buffers needed for video processing. * * History: * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the * camera is also initialized here (once per connect), at * expense of V4L client (it waits on open() call). * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). */static int usbvideo_v4l_open(struct inode *inode, struct file *file){ struct video_device *dev = video_devdata(file); struct uvd *uvd = (struct uvd *) dev; const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; int i, errCode = 0; if (uvd->debug > 1) info("%s($%p)", __FUNCTION__, dev); usbvideo_ClientIncModCount(uvd); down(&uvd->lock); if (uvd->user) { err("%s: Someone tried to open an already opened device!", __FUNCTION__); errCode = -EBUSY; } else { /* Clear statistics */ memset(&uvd->stats, 0, sizeof(uvd->stats)); /* Clean pointers so we know if we allocated something */ for (i=0; i < USBVIDEO_NUMSBUF; i++) uvd->sbuf[i].data = NULL; /* Allocate memory for the frame buffers */ uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); if ((uvd->fbuf == NULL) || (!RingQueue_IsAllocated(&uvd->dp))) { err("%s: Failed to allocate fbuf or dp", __FUNCTION__); errCode = -ENOMEM; } else { /* Allocate all buffers */ for (i=0; i < USBVIDEO_NUMFRAMES; i++) { uvd->frame[i].frameState = FrameState_Unused; uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); /* * Set default sizes in case IOCTL (VIDIOCMCAPTURE) * is not used (using read() instead). */ uvd->frame[i].canvas = uvd->canvas; uvd->frame[i].seqRead_Index = 0; } for (i=0; i < USBVIDEO_NUMSBUF; i++) { uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); if (uvd->sbuf[i].data == NULL) { errCode = -ENOMEM; break; } } } if (errCode != 0) { /* Have to free all that memory */ if (uvd->fbuf != NULL) { usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); uvd->fbuf = NULL; } RingQueue_Free(&uvd->dp); for (i=0; i < USBVIDEO_NUMSBUF; i++) { if (uvd->sbuf[i].data != NULL) { kfree (uvd->sbuf[i].data); uvd->sbuf[i].data = NULL; } } } } /* If so far no errors then we shall start the camera */ if (errCode == 0) { /* Start data pump if we have valid endpoint */ if (uvd->video_endp != 0) errCode = GET_CALLBACK(uvd, startDataPump)(uvd); if (errCode == 0) { if (VALID_CALLBACK(uvd, setupOnOpen)) { if (uvd->debug > 1) info("%s: setupOnOpen callback", __FUNCTION__); errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); if (errCode < 0) { err("%s: setupOnOpen callback failed (%d.).", __FUNCTION__, errCode); } else if (uvd->debug > 1) { info("%s: setupOnOpen callback successful", __FUNCTION__); } } if (errCode == 0) { uvd->settingsAdjusted = 0; if (uvd->debug > 1) info("%s: Open succeeded.", __FUNCTION__); uvd->user++; file->private_data = uvd; } } } up(&uvd->lock); if (errCode != 0) usbvideo_ClientDecModCount(uvd); if (uvd->debug > 0) info("%s: Returning %d.", __FUNCTION__, errCode); return errCode;}/* * usbvideo_v4l_close() * * This is part of Video 4 Linux API. The procedure * stops streaming and deallocates all buffers that were earlier * allocated in usbvideo_v4l_open(). * * History: * 22-Jan-2000 Moved scratch buffer deallocation here. * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. */static int usbvideo_v4l_close(struct inode *inode, struct file *file){ struct video_device *dev = file->private_data; struct uvd *uvd = (struct uvd *) dev; int i; if (uvd->debug > 1) info("%s($%p)", __FUNCTION__, dev); down(&uvd->lock); GET_CALLBACK(uvd, stopDataPump)(uvd); usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); uvd->fbuf = NULL; RingQueue_Free(&uvd->dp); for (i=0; i < USBVIDEO_NUMSBUF; i++) { kfree(uvd->sbuf[i].data); uvd->sbuf[i].data = NULL; }#if USBVIDEO_REPORT_STATS usbvideo_ReportStatistics(uvd);#endif uvd->user--; if (uvd->remove_pending) { if (uvd->debug > 0) info("usbvideo_v4l_close: Final disconnect."); usbvideo_CameraRelease(uvd); } up(&uvd->lock); usbvideo_ClientDecModCount(uvd); if (uvd->debug > 1) info("%s: Completed.", __FUNCTION__); file->private_data = NULL; return 0;}/* * usbvideo_v4l_ioctl() * * This is part of Video 4 Linux API. The procedure handles ioctl() calls. * * History: * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. */static int usbvideo_v4l_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg){ struct uvd *uvd = file->private_data; if (!CAMERA_IS_OPERATIONAL(uvd)) return -EIO; switch (cmd) { case VIDIOCGCAP: { struct video_capability *b = arg; *b = uvd->vcap; return 0; } case VIDIOCGCHAN: { struct video_channel *v = arg; *v = uvd->vchan; return 0; } case VIDIOCSCHAN: { struct video_channel *v = arg; if (v->channel != 0) return -EINVAL; return 0; } case VIDIOCGPICT: { struct video_picture *pic = arg; *pic = uvd->vpic; return 0; } case VIDIOCSPICT: { struct video_picture *pic = arg; /* * Use temporary 'video_picture' structure to preserve our * own settings (such as color depth, palette) that we * aren't allowing everyone (V4L client) to change. */ uvd->vpic.brightness = pic->brightness; uvd->vpic.hue = pic->hue; uvd->vpic.colour = pic->colour; uvd->vpic.contrast = pic->contrast; uvd->settingsAdjusted = 0; /* Will force new settings */ return 0; } case VIDIOCSWIN: { struct video_window *vw = arg; if(VALID_CALLBACK(uvd, setVideoMode)) { return GET_CALLBACK(uvd, setVideoMode)(uvd, vw); } if (vw->flags) return -EINVAL; if (vw->clipcount) return -EINVAL; if (vw->width != VIDEOSIZE_X(uvd->canvas)) return -EINVAL; if (vw->height != VIDEOSIZE_Y(uvd->canvas)) return -EINVAL; return 0; } case VIDIOCGWIN: { struct video_window *vw = arg; vw->x = 0; vw->y = 0; vw->width = VIDEOSIZE_X(uvd->videosize); vw->height = VIDEOSIZE_Y(uvd->videosize); vw->chromakey = 0; if (VALID_CALLBACK(uvd, getFPS)) vw->flags = GET_CALLBACK(uvd, getFPS)(uvd); else vw->flags = 10; /* FIXME: do better! */ return 0; } case VIDIOCGMBUF: { struct video_mbuf *vm = arg; int i; memset(vm, 0, sizeof(*vm)); vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES; vm->frames = USBVIDEO_NUMFRAMES; for(i = 0; i < USBVIDEO_NUMFRAMES; i++) vm->offsets[i] = i * uvd->max_frame_size; return 0; } case VIDIOCMCAPTURE: { struct video_mmap *vm = arg; 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 >= USBVIDEO_NUMFRAMES)) { err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1); 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 = arg; int ret; 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 = arg; memset(vb, 0, sizeof(*vb)); 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;}static int usbvideo_v4l_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ return video_usercopy(inode, file, cmd, arg, usbvideo_v4l_do_ioctl);}/* * 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). */static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *ppos){ struct uvd *uvd = file->private_data; int noblock = file->f_flags & O_NONBLOCK; int frmx = -1, i; struct usbvideo_frame *frame; if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) return -EFAULT; if (uvd->debug >= 1) info("%s: %Zd. bytes, noblock=%d.", __FUNCTION__, count, noblock); down(&uvd->lock); /* See if a frame is completed, then use it. */ for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { if ((uvd->frame[i].frameState == FrameState_Done) || (uvd->frame[i].frameState == FrameState_Done_Hold) || (uvd->frame[i].frameState == FrameState_Error)) { frmx = i; break; } } /* 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) { for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { if (uvd->frame[i].frameState == FrameState_Grabbing) { frmx = i; break; } } } /* * 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!", __FUNCTION__); 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=%Zd, new seqRead_Index=%ld", __FUNCTION__, 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 + 1) % USBVIDEO_NUMFRAMES)) { err("%s: usbvideo_NewFrame failed.", __FUNCTION__); } }read_done:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -