📄 uvc_v4l2.c
字号:
uvc_ctrl_begin(video); for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_get(video, ctrl); if (ret < 0) { uvc_ctrl_rollback(video); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; ret = uvc_ctrl_rollback(video); } break; case VIDIOC_S_EXT_CTRLS: case VIDIOC_TRY_EXT_CTRLS: { struct v4l2_ext_controls *ctrls = (struct v4l2_ext_controls*)arg; struct v4l2_ext_control *ctrl = ctrls->controls; unsigned int i; ret = uvc_ctrl_begin(video); if (ret < 0) return ret; for (i = 0; i < ctrls->count; ++ctrl, ++i) { ret = uvc_ctrl_set(video, ctrl); if (ret < 0) { uvc_ctrl_rollback(video); ctrls->error_idx = i; return ret; } } ctrls->error_idx = 0; if (cmd == VIDIOC_S_EXT_CTRLS) ret = uvc_ctrl_commit(video); else ret = uvc_ctrl_rollback(video); } break; /* Get, Set & Enum input */ case VIDIOC_ENUMINPUT: { const struct uvc_entity *selector = video->selector; struct v4l2_input *input = arg; struct uvc_entity *iterm = NULL; u32 index = input->index; int pin = 0; if (selector == NULL) { if (index != 0) return -EINVAL; iterm = list_first_entry(&video->iterms, struct uvc_entity, chain); pin = iterm->id; } else if (pin < selector->selector.bNrInPins) { pin = selector->selector.baSourceID[index]; list_for_each_entry(iterm, video->iterms.next, chain) { if (iterm->id == pin) break; } } if (iterm == NULL || iterm->id != pin) return -EINVAL; memset(input, 0, sizeof *input); input->index = index; strncpy(input->name, iterm->name, sizeof input->name); if (UVC_ENTITY_TYPE(iterm) == ITT_CAMERA) input->type = V4L2_INPUT_TYPE_CAMERA; } break; case VIDIOC_G_INPUT: { u8 input; if (video->selector == NULL) { *(int*)arg = 0; break; } ret = uvc_query_ctrl(video->dev, GET_CUR, video->selector->id, video->dev->intfnum, SU_INPUT_SELECT_CONTROL, &input, 1); if (ret < 0) return ret; *(int*)arg = input - 1; } break; case VIDIOC_S_INPUT: { u8 input = *(u32*)arg + 1; if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; if (video->selector == NULL) { if (input != 1) return -EINVAL; break; } if (input > video->selector->selector.bNrInPins) return -EINVAL; return uvc_query_ctrl(video->dev, SET_CUR, video->selector->id, video->dev->intfnum, SU_INPUT_SELECT_CONTROL, &input, 1); } /* 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 = 0; if (format->flags & UVC_FMT_FLAG_COMPRESSED) fmt->flags |= V4L2_FMT_FLAG_COMPRESSED; strncpy(fmt->description, format->name, sizeof fmt->description); fmt->description[sizeof fmt->description - 1] = 0; fmt->pixelformat = format->fcc; } break; case VIDIOC_TRY_FMT: { struct uvc_streaming_control probe; if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; return uvc_v4l2_try_format(video, (struct v4l2_format*)arg, &probe, NULL, NULL); } case VIDIOC_S_FMT: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; return uvc_v4l2_set_format(video, (struct v4l2_format*)arg); case VIDIOC_G_FMT: return uvc_v4l2_get_format(video, (struct v4l2_format*)arg); /* Frame size enumeration */ case VIDIOC_ENUM_FRAMESIZES: { struct v4l2_frmsizeenum *fsize = arg; struct uvc_format *format = NULL; struct uvc_frame *frame; int i; /* Look for the given pixel format */ for (i = 0; i < video->streaming->nformats; i++) { if (video->streaming->format[i].fcc == fsize->pixel_format) { format = &video->streaming->format[i]; break; } } if (format == NULL) return -EINVAL; if (fsize->index >= format->nframes) return -EINVAL; frame = &format->frame[fsize->index]; fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = frame->wWidth; fsize->discrete.height = frame->wHeight; } break; /* Frame interval enumeration */ case VIDIOC_ENUM_FRAMEINTERVALS: { struct v4l2_frmivalenum *fival = arg; struct uvc_format *format; struct uvc_frame *frame = NULL; int i, j; /* Look for the given pixel format and frame size */ for (i = 0; i < video->streaming->nformats; i++) { if (video->streaming->format[i].fcc == fival->pixel_format) { format = &video->streaming->format[i]; for (j = 0; j < format->nframes; j++) { if (format->frame[j].wWidth == fival->width && format->frame[j].wHeight == fival->height) { frame = &format->frame[j]; break; } } break; /* Each format should occur only once. */ } } if (frame == NULL) return -EINVAL; if (frame->bFrameIntervalType) { if (fival->index >= frame->bFrameIntervalType) return -EINVAL; fival->type = V4L2_FRMIVAL_TYPE_DISCRETE; fival->discrete.numerator = frame->dwFrameInterval[fival->index]; fival->discrete.denominator = 10000000; uvc_simplify_fraction(&fival->discrete.numerator, &fival->discrete.denominator, 8, 333); } else { fival->type = V4L2_FRMIVAL_TYPE_STEPWISE; fival->stepwise.min.numerator = frame->dwFrameInterval[0]; fival->stepwise.min.denominator = 10000000; fival->stepwise.max.numerator = frame->dwFrameInterval[1]; fival->stepwise.max.denominator = 10000000; fival->stepwise.step.numerator = frame->dwFrameInterval[2]; fival->stepwise.step.denominator = 10000000; uvc_simplify_fraction(&fival->stepwise.min.numerator, &fival->stepwise.min.denominator, 8, 333); uvc_simplify_fraction(&fival->stepwise.max.numerator, &fival->stepwise.max.denominator, 8, 333); uvc_simplify_fraction(&fival->stepwise.step.numerator, &fival->stepwise.step.denominator, 8, 333); } } break; /* Get & Set streaming parameters */ case VIDIOC_G_PARM: return uvc_v4l2_get_streamparm(video, (struct v4l2_streamparm*)arg); case VIDIOC_S_PARM: if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; return uvc_v4l2_set_streamparm(video, (struct v4l2_streamparm*)arg); /* Cropping and scaling */ case VIDIOC_CROPCAP: { struct v4l2_cropcap *ccap = arg; struct uvc_frame *frame = video->streaming->cur_frame; if (ccap->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; ccap->bounds.left = 0; ccap->bounds.top = 0; ccap->bounds.width = frame->wWidth; ccap->bounds.height = frame->wHeight; ccap->defrect = ccap->bounds; ccap->pixelaspect.numerator = 1; ccap->pixelaspect.denominator = 1; } break; case VIDIOC_G_CROP: case VIDIOC_S_CROP: return -EINVAL; /* Buffers & streaming */ case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *rb = arg; unsigned int bufsize = video->streaming->ctrl.dwMaxVideoFrameSize; if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE || rb->memory != V4L2_MEMORY_MMAP) return -EINVAL; if ((ret = uvc_acquire_privileges(handle)) < 0) return ret; if ((ret = uvc_alloc_buffers(&video->queue, rb->count, bufsize)) < 0) return ret; rb->count = ret; ret = 0; } break; case VIDIOC_QUERYBUF: { struct v4l2_buffer *buf = arg; if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; return uvc_query_buffer(&video->queue, buf); } case VIDIOC_QBUF: if (!uvc_has_privileges(handle)) return -EBUSY; return uvc_queue_buffer(&video->queue, (struct v4l2_buffer*)arg); case VIDIOC_DQBUF: if (!uvc_has_privileges(handle)) return -EBUSY; return uvc_dequeue_buffer(&video->queue, (struct v4l2_buffer*)arg, file->f_flags & O_NONBLOCK); case VIDIOC_STREAMON: { int *type = arg; if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; if ((ret = uvc_video_enable(video, 1)) < 0) return ret; } break; case VIDIOC_STREAMOFF: { int *type = arg; if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (!uvc_has_privileges(handle)) return -EBUSY; return uvc_video_enable(video, 0); } /* Analog video standards make no sense for digital cameras. */ case VIDIOC_ENUMSTD: case VIDIOC_QUERYSTD: case VIDIOC_G_STD: case VIDIOC_S_STD: case VIDIOC_OVERLAY: case VIDIOC_ENUMAUDIO: case VIDIOC_ENUMAUDOUT: case VIDIOC_ENUMOUTPUT: uvc_trace(UVC_TRACE_IOCTL, "Unsupported ioctl 0x%08x\n", cmd); return -EINVAL; /* Dynamic controls. */ case UVCIOC_CTRL_ADD: { struct uvc_xu_control_info *xinfo = arg; struct uvc_control_info *info; info = kmalloc(sizeof *info, GFP_KERNEL); if (info == NULL) return -ENOMEM; memcpy(info->entity, xinfo->entity, sizeof info->entity); info->index = xinfo->index; info->selector = xinfo->selector; info->size = xinfo->size; info->flags = xinfo->flags; info->flags |= UVC_CONTROL_GET_MIN | UVC_CONTROL_GET_MAX | UVC_CONTROL_GET_RES | UVC_CONTROL_GET_DEF; ret = uvc_ctrl_add_info(info); if (ret < 0) kfree(info); } break; case UVCIOC_CTRL_MAP: { struct uvc_xu_control_mapping *xmap = arg; struct uvc_control_mapping *map; map = kmalloc(sizeof *map, GFP_KERNEL); if (map == NULL) return -ENOMEM; map->id = xmap->id; memcpy(map->name, xmap->name, sizeof map->name); memcpy(map->entity, xmap->entity, sizeof map->entity); map->selector = xmap->selector; map->size = xmap->size; map->offset = xmap->offset; map->v4l2_type = xmap->v4l2_type; map->data_type = xmap->data_type; ret = uvc_ctrl_add_mapping(map); if (ret < 0) kfree(map); } break; case UVCIOC_CTRL_GET: return uvc_xu_ctrl_query(video, (struct uvc_xu_control*)arg, 0); case UVCIOC_CTRL_SET: return uvc_xu_ctrl_query(video, (struct uvc_xu_control*)arg, 1); default: if ((ret = v4l_compat_translate_ioctl(inode, file, cmd, arg, uvc_v4l2_do_ioctl)) == -ENOIOCTLCMD) uvc_trace(UVC_TRACE_IOCTL, "Unknown ioctl 0x%08x\n", cmd); return ret; } return ret;}static int uvc_v4l2_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg){ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_ioctl\n"); return video_usercopy(inode, file, cmd, arg, uvc_v4l2_do_ioctl);}static ssize_t uvc_v4l2_read(struct file *file, char __user *data, size_t count, loff_t *ppos){ uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_read: not implemented.\n"); return -ENODEV;}/* * VMA operations. */static void uvc_vm_open(struct vm_area_struct *vma){ struct uvc_buffer *buffer = vma->vm_private_data; buffer->vma_use_count++;}static void uvc_vm_close(struct vm_area_struct *vma){ struct uvc_buffer *buffer = vma->vm_private_data; buffer->vma_use_count--;}static struct vm_operations_struct uvc_vm_ops = { .open = uvc_vm_open, .close = uvc_vm_close,};static int uvc_v4l2_mmap(struct file *file, struct vm_area_struct *vma){ struct video_device *vdev = video_devdata(file); struct uvc_video_device *video = video_get_drvdata(vdev); struct uvc_buffer *buffer = NULL; struct page *page; unsigned long addr, start, size; unsigned int i; int ret = 0; uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_mmap\n"); start = vma->vm_start; size = vma->vm_end - vma->vm_start; mutex_lock(&video->queue.mutex); for (i = 0; i < video->queue.count; ++i) { if ((video->queue.buffer[i].buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) { buffer = &video->queue.buffer[i]; break; } } if (buffer == NULL || size != video->queue.buf_size) { ret = -EINVAL; goto done; } /* * VM_IO marks the area as being an mmaped region for I/O to a * device. It also prevents the region from being core dumped. */ vma->vm_flags |= VM_IO; addr = (unsigned long)video->queue.mem + buffer->buf.m.offset; while (size > 0) { page = vmalloc_to_page((void*)addr); if ((ret = vm_insert_page(vma, start, page)) < 0) goto done; start += PAGE_SIZE; addr += PAGE_SIZE; size -= PAGE_SIZE; } vma->vm_ops = &uvc_vm_ops; vma->vm_private_data = buffer; uvc_vm_open(vma);done: mutex_unlock(&video->queue.mutex); return ret;}static unsigned int uvc_v4l2_poll(struct file *file, poll_table *wait){ struct video_device *vdev = video_devdata(file); struct uvc_video_device *video = video_get_drvdata(vdev); uvc_trace(UVC_TRACE_CALLS, "uvc_v4l2_poll\n"); return uvc_queue_poll(&video->queue, file, wait);}struct file_operations uvc_fops = { .owner = THIS_MODULE, .open = uvc_v4l2_open, .release = uvc_v4l2_release, .ioctl = uvc_v4l2_ioctl,#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,15) .compat_ioctl = v4l_compat_ioctl32,#endif .llseek = no_llseek, .read = uvc_v4l2_read, .mmap = uvc_v4l2_mmap, .poll = uvc_v4l2_poll,};
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -