📄 uvcvideo.c
字号:
return ret; /* Retrieve the default format and commit it. */ if ((ret = uvc_get_video_ctrl(video, probe, 1, GET_DEF)) < 0) return ret; if ((ret = uvc_set_video_ctrl(video, probe, 0)) < 0) return ret; video->streaming->cur_format = format; video->streaming->cur_frame = frame; atomic_set(&video->count, 1); return 0;}/* * Enable or disable the video stream. */static int uvc_video_enable(struct uvc_video_device *video, int enable){ struct usb_interface *intf = video->streaming->intf; struct usb_host_interface *alts; struct usb_host_endpoint *ep; int intfnum = video->streaming->intfnum; unsigned int bandwidth, psize, i; int ret; if (!enable) { uvc_uninit_isoc(video); usb_set_interface(video->dev->udev, intfnum, 0); uvc_queue_enable(&video->queue, 0); return 0; } if ((ret = uvc_queue_enable(&video->queue, 1)) < 0) return ret; /* Select the alternate setting. */ bandwidth = video->streaming->ctrl.dwMaxPayloadTransferSize; for (i = 0; i < intf->num_altsetting; ++i) { alts = &intf->altsetting[i]; ep = uvc_find_endpoint(alts, video->streaming->input.bEndpointAddress); if (ep == NULL) continue; /* Check if the bandwidth is high enough. */ psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); if (psize >= bandwidth) break; } if (i >= intf->num_altsetting) return -EIO; if ((ret = usb_set_interface(video->dev->udev, intfnum, i)) < 0 || (ret = uvc_init_isoc(video, ep)) < 0) return ret; return 0;}/* * Mapping V4L2 controls to UVC controls can be straighforward if done well. * Most of the UVC controls exist in V4L2, and can be mapped directly. Some * must be grouped (for instance the Red Balance, Blue Balance and Do White * Balance V4L2 controls use the White Balance Component UVC control) or * otherwise translated. The approach we take here is to use a translation * table for the controls which can be mapped directly, and handle the others * manually. */static int uvc_v4l2_get_ctrl(struct uvc_video_device *video, struct v4l2_control *ctrl){ switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: case V4L2_CID_DO_WHITE_BALANCE: case V4L2_CID_RED_BALANCE: case V4L2_CID_BLUE_BALANCE: return -EINVAL; default: return uvc_get_v4l2_ctrl(video, ctrl); }}static int uvc_v4l2_set_ctrl(struct uvc_video_device *video, struct v4l2_control *ctrl){ switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: case V4L2_CID_DO_WHITE_BALANCE: case V4L2_CID_RED_BALANCE: case V4L2_CID_BLUE_BALANCE: return -EINVAL; default: return uvc_set_v4l2_ctrl(video, ctrl); }}static int uvc_v4l2_query_ctrl(struct uvc_video_device *video, struct v4l2_queryctrl *ctrl){ switch (ctrl->id) { case V4L2_CID_AUTO_WHITE_BALANCE: case V4L2_CID_DO_WHITE_BALANCE: case V4L2_CID_RED_BALANCE: case V4L2_CID_BLUE_BALANCE: return -EINVAL; default: return uvc_query_v4l2_ctrl(video, ctrl); }}static int uvc_v4l2_query_menu(struct uvc_video_device *video, struct v4l2_querymenu *query_menu){ struct uvc_menu_info *menu_info; struct uvc_control_desc *ctrl; __u32 i; ctrl = uvc_find_control(video, query_menu->id, NULL); if (ctrl == NULL || ctrl-> type != V4L2_CTRL_TYPE_MENU) return -EINVAL; menu_info = ctrl->menu_info; for (i = 0; i < ctrl->menu_count; ++i, ++menu_info) { if (query_menu->index == menu_info->index) { strncpy(query_menu->name, menu_info->name, 32); return 0; } } return -EINVAL;}static int uvc_v4l2_try_format(struct uvc_video_device *video, struct v4l2_format *fmt, struct uvc_streaming_control *probe, struct uvc_format **uvc_format, struct uvc_frame **uvc_frame){ struct uvc_format *format = NULL; struct uvc_frame *frame = NULL; __u16 rw, rh; unsigned int d, maxd; unsigned int i; __u32 interval; int ret = 0; __u8 *fcc; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; fcc = (__u8*)&fmt->fmt.pix.pixelformat; uvc_trace(UVC_TRACE_FORMAT, "Trying format 0x%08x (%c%c%c%c): %ux%u.\n", fmt->fmt.pix.pixelformat, fcc[0], fcc[1], fcc[2], fcc[3], fmt->fmt.pix.width, fmt->fmt.pix.height); /* Check if the hardware supports the requested format. */ for (i = 0; i < video->streaming->nformats; ++i) { format = &video->streaming->format[i]; if (format->fcc == fmt->fmt.pix.pixelformat) break; } if (format == NULL || format->fcc != fmt->fmt.pix.pixelformat) { uvc_trace(UVC_TRACE_FORMAT, "Unsupported format 0x%08x.\n", fmt->fmt.pix.pixelformat); return -EINVAL; } /* Find the closest image size. The distance between image sizes is * the size in pixels of the non-overlapping regions between the * requested size and the frame-specified size. */ rw = fmt->fmt.pix.width; rh = fmt->fmt.pix.height; maxd = (unsigned int)-1; for (i = 0; i < format->nframes; ++i) { __u16 w = format->frame[i].wWidth; __u16 h = format->frame[i].wHeight; d = min(w, rw) * min(h, rh); d = w*h + rw*rh - 2*d; if (d < maxd) { maxd = d; frame = &format->frame[i]; } if (maxd == 0) break; } if (frame == NULL) { uvc_trace(UVC_TRACE_FORMAT, "Unsupported size %ux%u.\n", fmt->fmt.pix.width, fmt->fmt.pix.height); return -EINVAL; } /* Use the default frame interval. */ interval = frame->dwDefaultFrameInterval; uvc_trace(UVC_TRACE_FORMAT, "Using default frame interval %u.%u us " "(%u.%u fps).\n", interval/10, interval%10, 10000000/interval, (100000000/interval)%10); /* Set the format index, frame index and frame interval. */ memset(probe, 0, sizeof *probe); probe->bmHint = 1; /* dwFrameInterval */ probe->bFormatIndex = format->index; probe->bFrameIndex = frame->bFrameIndex; probe->dwFrameInterval = uvc_try_frame_interval(frame, interval); /* Probe the device */ if ((ret = uvc_probe_video(video, probe)) < 0) goto done; fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth; fmt->fmt.pix.sizeimage = probe->dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; if (uvc_format != NULL) *uvc_format = format; if (uvc_frame != NULL) *uvc_frame = frame;done: return ret;}static int uvc_v4l2_get_format(struct uvc_video_device *video, struct v4l2_format *fmt){ struct uvc_format *format = video->streaming->cur_format; struct uvc_frame *frame = video->streaming->cur_frame; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (format == NULL || frame == NULL) return -EINVAL; fmt->fmt.pix.pixelformat = format->fcc; fmt->fmt.pix.width = frame->wWidth; fmt->fmt.pix.height = frame->wHeight; fmt->fmt.pix.field = V4L2_FIELD_NONE; fmt->fmt.pix.bytesperline = format->bpp * frame->wWidth; fmt->fmt.pix.sizeimage = video->streaming->ctrl.dwMaxVideoFrameSize; fmt->fmt.pix.colorspace = format->colorspace; fmt->fmt.pix.priv = 0; return 0;}static int uvc_v4l2_set_format(struct uvc_video_device *video, struct v4l2_format *fmt){ struct uvc_streaming_control probe; struct uvc_format *format; struct uvc_frame *frame; int ret; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; ret = uvc_v4l2_try_format(video, fmt, &probe, &format, &frame); if (ret < 0) return ret; if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) return ret; memcpy(&video->streaming->ctrl, &probe, sizeof probe); video->streaming->cur_format = format; video->streaming->cur_frame = frame; return 0;}static int uvc_v4l2_get_streamparm(struct uvc_video_device *video, struct v4l2_streamparm *parm){ uint32_t numerator, denominator; if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; numerator = video->streaming->ctrl.dwFrameInterval; denominator = 10000000; uvc_simplify_fraction(&numerator, &denominator, 8, 333); memset(parm, 0, sizeof *parm); parm->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; parm->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; parm->parm.capture.capturemode = 0; parm->parm.capture.timeperframe.numerator = numerator; parm->parm.capture.timeperframe.denominator = denominator; parm->parm.capture.extendedmode = 0; parm->parm.capture.readbuffers = 0; return 0;}static int uvc_v4l2_set_streamparm(struct uvc_video_device *video, struct v4l2_streamparm *parm){ struct uvc_frame *frame = video->streaming->cur_frame; struct uvc_streaming_control probe; uint32_t interval; int ret; if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; memcpy(&probe, &video->streaming->ctrl, sizeof probe); interval = uvc_fraction_to_interval( parm->parm.capture.timeperframe.numerator, parm->parm.capture.timeperframe.denominator); uvc_trace(UVC_TRACE_FORMAT, "Setting frame interval to %u/%u (%u).\n", parm->parm.capture.timeperframe.numerator, parm->parm.capture.timeperframe.denominator, interval); probe.dwFrameInterval = uvc_try_frame_interval(frame, interval); /* Probe the device with the new settings. */ if ((ret = uvc_probe_video(video, &probe)) < 0) return ret; /* Commit the new settings. */ if ((ret = uvc_set_video_ctrl(video, &probe, 0)) < 0) return ret; memcpy(&video->streaming->ctrl, &probe, sizeof probe); /* Return the actual frame period. */ parm->parm.capture.timeperframe.numerator = probe.dwFrameInterval; parm->parm.capture.timeperframe.denominator = 10000000; uvc_simplify_fraction(&parm->parm.capture.timeperframe.numerator, &parm->parm.capture.timeperframe.denominator, 8, 333); return 0;}/* ------------------------------------------------------------------------ * V4L2 file operations */static int uvc_v4l2_open(struct inode *inode, struct file *file){ struct video_device *vdev; struct uvc_video_device *video; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_open\n"); down(&dev_sem); vdev = video_devdata(file); video = video_get_drvdata(vdev); if (video->dev->state & UVC_DEV_DISCONNECTED) { up(&dev_sem); return -ENODEV; } if (!atomic_dec_and_test(&video->count)) { atomic_inc(&video->count); up(&dev_sem); return -EBUSY; } kref_get(&video->dev->kref); up(&dev_sem); return 0;}static int uvc_v4l2_release(struct inode *inode, struct file *file){ struct video_device *vdev = video_devdata(file); struct uvc_video_device *video = video_get_drvdata(vdev); uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_release\n"); uvc_video_enable(video, 0); down(&video->queue.lock); if (uvc_free_buffers(&video->queue) < 0) uvc_printk(KERN_ERR, "uvc_v4l2_release: Unable to free buffers.\n"); up(&video->queue.lock); atomic_inc(&video->count); kref_put(&video->dev->kref, uvc_delete); return 0;}static int uvc_v4l2_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg){ struct video_device *vdev = video_devdata(file); struct uvc_video_device *video = video_get_drvdata(vdev); int ret; if (trace & UVC_TRACE_IOCTL) {#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,16) switch (_IOC_TYPE(cmd)) { case 'v': uvc_printk(KERN_DEBUG, "ioctl 0x%x (V4L1)\n", cmd); break; case 'V': uvc_printk(KERN_DEBUG, "ioctl 0x%x (%s)\n", cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); break; default: uvc_printk(KERN_DEBUG, "ioctl 0x%x (???)\n", cmd); break; }#else v4l_printk_ioctl(cmd);#endif } switch (cmd) { /* Query capabilities */ case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = arg; memset(cap, 0, sizeof *cap); strncpy(cap->driver, "uvcvideo", sizeof cap->driver); if (video->dev->udev->product) strncpy(cap->card, video->dev->udev->product, sizeof cap->card); else strncpy(cap->card, "USB Video Class device", sizeof cap->card); strncpy(cap->bus_info, video->dev->udev->bus->bus_name, sizeof cap->bus_info); cap->version = DRIVER_VERSION_NUMBER; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; } break; /* Get, Set & Query control */ case VIDIOC_QUERYCTRL: return uvc_v4l2_query_ctrl(video, (struct v4l2_queryctrl*)arg); case VIDIOC_G_CTRL: return uvc_v4l2_get_ctrl(video, (struct v4l2_control*)arg); case VIDIOC_S_CTRL: return uvc_v4l2_set_ctrl(video, (struct v4l2_control*)arg); case VIDIOC_QUERYMENU: return uvc_v4l2_query_menu(video, (struct v4l2_querymenu*)arg); /* Get, Set & Enum input */ case VIDIOC_ENUMINPUT: { struct v4l2_input *input = arg; if (input->index != 0) return -EINVAL; memset(input, 0, sizeof *input); strncpy(input->name, "Camera", sizeof input->name); input->type = V4L2_INPUT_TYPE_CAMERA; } break; case VIDIOC_G_INPUT: *(int*)arg = 0; break; case VIDIOC_S_INPUT: if (*(int*)arg != 0) return -EINVAL; break; /* Try, Get, Set & Enum format */ case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *fmt = arg; struct uvc_format *format; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || fmt->index >= video->streaming->nformats) return -EINVAL; format = &video->streaming->format[fmt->index]; fmt->flags = format->flags; strncpy(fmt->description, format->name, sizeof fmt->d
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -