📄 gspca.c
字号:
PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); if (v4l2_buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; index = v4l2_buf->index; if ((unsigned) index >= gspca_dev->nframes) { PDEBUG(D_FRAM, "qbuf idx %d >= %d", index, gspca_dev->nframes); ret = -EINVAL; goto out; } if (v4l2_buf->memory != gspca_dev->memory) { PDEBUG(D_FRAM, "qbuf bad memory type"); ret = -EINVAL; goto out; } frame = &gspca_dev->frame[index]; if (frame->v4l2_buf.flags & BUF_ALL_FLAGS) { PDEBUG(D_FRAM, "qbuf bad state"); ret = -EINVAL; goto out; } frame->v4l2_buf.flags |= V4L2_BUF_FLAG_QUEUED; if (frame->v4l2_buf.memory == V4L2_MEMORY_USERPTR) { frame->v4l2_buf.m.userptr = v4l2_buf->m.userptr; frame->v4l2_buf.length = v4l2_buf->length; } /* put the buffer in the 'queued' queue */ i = gspca_dev->fr_q; gspca_dev->fr_queue[i] = index; gspca_dev->fr_q = (i + 1) % gspca_dev->nframes; PDEBUG(D_FRAM, "qbuf q:%d i:%d o:%d", gspca_dev->fr_q, gspca_dev->fr_i, gspca_dev->fr_o); v4l2_buf->flags |= V4L2_BUF_FLAG_QUEUED; v4l2_buf->flags &= ~V4L2_BUF_FLAG_DONE; ret = 0;out: mutex_unlock(&gspca_dev->queue_lock); return ret;}/* * allocate the resources for read() */static int read_alloc(struct gspca_dev *gspca_dev, struct file *file){ struct v4l2_buffer v4l2_buf; int i, ret; PDEBUG(D_STREAM, "read alloc"); if (gspca_dev->nframes == 0) { struct v4l2_requestbuffers rb; memset(&rb, 0, sizeof rb); rb.count = gspca_dev->nbufread; rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; rb.memory = GSPCA_MEMORY_READ; ret = vidioc_reqbufs(file, gspca_dev, &rb); if (ret != 0) { PDEBUG(D_STREAM, "read reqbuf err %d", ret); return ret; } memset(&v4l2_buf, 0, sizeof v4l2_buf); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_buf.memory = GSPCA_MEMORY_READ; for (i = 0; i < gspca_dev->nbufread; i++) { v4l2_buf.index = i; ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); if (ret != 0) { PDEBUG(D_STREAM, "read qbuf err: %d", ret); return ret; } } gspca_dev->memory = GSPCA_MEMORY_READ; } /* start streaming */ ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); if (ret != 0) PDEBUG(D_STREAM, "read streamon err %d", ret); return ret;}static unsigned int dev_poll(struct file *file, poll_table *wait){ struct gspca_dev *gspca_dev = file->private_data; int i, ret; PDEBUG(D_FRAM, "poll"); poll_wait(file, &gspca_dev->wq, wait); if (!gspca_dev->present) return POLLERR; /* if reqbufs is not done, the user would use read() */ if (gspca_dev->nframes == 0) { if (gspca_dev->memory != GSPCA_MEMORY_NO) return POLLERR; /* not the 1st time */ ret = read_alloc(gspca_dev, file); if (ret != 0) return POLLERR; } if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) return POLLERR; if (!gspca_dev->present) { ret = POLLERR; goto out; } /* check the next incoming buffer */ i = gspca_dev->fr_o; i = gspca_dev->fr_queue[i]; if (gspca_dev->frame[i].v4l2_buf.flags & V4L2_BUF_FLAG_DONE) ret = POLLIN | POLLRDNORM; /* something to read */ else ret = 0;out: mutex_unlock(&gspca_dev->queue_lock); return ret;}static ssize_t dev_read(struct file *file, char __user *data, size_t count, loff_t *ppos){ struct gspca_dev *gspca_dev = file->private_data; struct gspca_frame *frame; struct v4l2_buffer v4l2_buf; struct timeval timestamp; int n, ret, ret2; PDEBUG(D_FRAM, "read (%zd)", count); if (!gspca_dev->present) return -ENODEV; switch (gspca_dev->memory) { case GSPCA_MEMORY_NO: /* first time */ ret = read_alloc(gspca_dev, file); if (ret != 0) return ret; break; case GSPCA_MEMORY_READ: if (gspca_dev->capt_file == file) break; /* fall thru */ default: return -EINVAL; } /* get a frame */ jiffies_to_timeval(get_jiffies_64(), ×tamp); timestamp.tv_sec--; n = 2; for (;;) { memset(&v4l2_buf, 0, sizeof v4l2_buf); v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; v4l2_buf.memory = GSPCA_MEMORY_READ; ret = vidioc_dqbuf(file, gspca_dev, &v4l2_buf); if (ret != 0) { PDEBUG(D_STREAM, "read dqbuf err %d", ret); return ret; } /* if the process slept for more than 1 second, * get a newer frame */ frame = &gspca_dev->frame[v4l2_buf.index]; if (--n < 0) break; /* avoid infinite loop */ if (frame->v4l2_buf.timestamp.tv_sec >= timestamp.tv_sec) break; ret = vidioc_qbuf(file, gspca_dev, &v4l2_buf); if (ret != 0) { PDEBUG(D_STREAM, "read qbuf err %d", ret); return ret; } } /* copy the frame */ if (count > frame->v4l2_buf.bytesused) count = frame->v4l2_buf.bytesused; ret = copy_to_user(data, frame->data, count); if (ret != 0) { PDEBUG(D_ERR|D_STREAM, "read cp to user lack %d / %zd", ret, count); ret = -EFAULT; goto out; } ret = count;out: /* in each case, requeue the buffer */ ret2 = vidioc_qbuf(file, gspca_dev, &v4l2_buf); if (ret2 != 0) return ret2; return ret;}static void dev_release(struct video_device *vfd){ /* nothing */}static struct file_operations dev_fops = { .owner = THIS_MODULE, .open = dev_open, .release = dev_close, .read = dev_read, .mmap = dev_mmap, .ioctl = video_ioctl2,#ifdef CONFIG_COMPAT .compat_ioctl = v4l_compat_ioctl32,#endif .llseek = no_llseek, .poll = dev_poll,};static const struct v4l2_ioctl_ops dev_ioctl_ops = { .vidioc_querycap = vidioc_querycap, .vidioc_dqbuf = vidioc_dqbuf, .vidioc_qbuf = vidioc_qbuf, .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, .vidioc_streamon = vidioc_streamon, .vidioc_queryctrl = vidioc_queryctrl, .vidioc_g_ctrl = vidioc_g_ctrl, .vidioc_s_ctrl = vidioc_s_ctrl, .vidioc_querymenu = vidioc_querymenu, .vidioc_enum_input = vidioc_enum_input, .vidioc_g_input = vidioc_g_input, .vidioc_s_input = vidioc_s_input, .vidioc_reqbufs = vidioc_reqbufs, .vidioc_querybuf = vidioc_querybuf, .vidioc_streamoff = vidioc_streamoff, .vidioc_g_jpegcomp = vidioc_g_jpegcomp, .vidioc_s_jpegcomp = vidioc_s_jpegcomp, .vidioc_g_parm = vidioc_g_parm, .vidioc_s_parm = vidioc_s_parm, .vidioc_s_std = vidioc_s_std,#ifdef CONFIG_VIDEO_V4L1_COMPAT .vidiocgmbuf = vidiocgmbuf,#endif};static struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, .release = dev_release, /* mandatory */ .minor = -1,};/* * probe and create a new gspca device * * This function must be called by the sub-driver when it is * called for probing a new device. */int gspca_dev_probe(struct usb_interface *intf, const struct usb_device_id *id, const struct sd_desc *sd_desc, int dev_size, struct module *module){ struct usb_interface_descriptor *interface; struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); int ret; PDEBUG(D_PROBE, "probing %04x:%04x", id->idVendor, id->idProduct); /* we don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) return -ENODEV; interface = &intf->cur_altsetting->desc; if (interface->bInterfaceNumber > 0) return -ENODEV; /* create the device */ if (dev_size < sizeof *gspca_dev) dev_size = sizeof *gspca_dev; gspca_dev = kzalloc(dev_size, GFP_KERNEL); if (!gspca_dev) { err("couldn't kzalloc gspca struct"); return -ENOMEM; } kref_init(&gspca_dev->kref); gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { err("out of memory"); ret = -ENOMEM; goto out; } gspca_dev->dev = dev; gspca_dev->iface = interface->bInterfaceNumber; gspca_dev->nbalt = intf->num_altsetting; gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; /* configure the subdriver and initialize the USB device */ ret = gspca_dev->sd_desc->config(gspca_dev, id); if (ret < 0) goto out; ret = gspca_dev->sd_desc->init(gspca_dev); if (ret < 0) goto out; ret = gspca_set_alt0(gspca_dev); if (ret < 0) goto out; gspca_set_default_mode(gspca_dev); mutex_init(&gspca_dev->usb_lock); mutex_init(&gspca_dev->read_lock); mutex_init(&gspca_dev->queue_lock); init_waitqueue_head(&gspca_dev->wq); /* init video stuff */ memcpy(&gspca_dev->vdev, &gspca_template, sizeof gspca_template); gspca_dev->vdev.parent = &dev->dev; memcpy(&gspca_dev->fops, &dev_fops, sizeof gspca_dev->fops); gspca_dev->vdev.fops = &gspca_dev->fops; gspca_dev->fops.owner = module; /* module protection */ gspca_dev->present = 1; ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, video_nr); if (ret < 0) { err("video_register_device err %d", ret); goto out; } usb_set_intfdata(intf, gspca_dev); PDEBUG(D_PROBE, "probe ok"); return 0;out: kref_put(&gspca_dev->kref, gspca_delete); return ret;}EXPORT_SYMBOL(gspca_dev_probe);/* * USB disconnection * * This function must be called by the sub-driver * when the device disconnects, after the specific resources are freed. */void gspca_disconnect(struct usb_interface *intf){ struct gspca_dev *gspca_dev = usb_get_intfdata(intf); usb_set_intfdata(intf, NULL);/* We don't want people trying to open up the device */ video_unregister_device(&gspca_dev->vdev); gspca_dev->present = 0; gspca_dev->streaming = 0; kref_put(&gspca_dev->kref, gspca_delete); PDEBUG(D_PROBE, "disconnect complete");}EXPORT_SYMBOL(gspca_disconnect);#ifdef CONFIG_PMint gspca_suspend(struct usb_interface *intf, pm_message_t message){ struct gspca_dev *gspca_dev = usb_get_intfdata(intf); if (!gspca_dev->streaming) return 0; gspca_dev->frozen = 1; /* avoid urb error messages */ if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); gspca_set_alt0(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); return 0;}EXPORT_SYMBOL(gspca_suspend);int gspca_resume(struct usb_interface *intf){ struct gspca_dev *gspca_dev = usb_get_intfdata(intf); gspca_dev->frozen = 0; gspca_dev->sd_desc->init(gspca_dev); if (gspca_dev->streaming) return gspca_init_transfer(gspca_dev); return 0;}EXPORT_SYMBOL(gspca_resume);#endif/* -- cam driver utility functions -- *//* auto gain and exposure algorithm based on the knee algorithm described here: http://ytse.tricolour.net/docs/LowLightOptimization.html Returns 0 if no changes were made, 1 if the gain and or exposure settings where changed. */int gspca_auto_gain_n_exposure(struct gspca_dev *gspca_dev, int avg_lum, int desired_avg_lum, int deadzone, int gain_knee, int exposure_knee){ int i, steps, gain, orig_gain, exposure, orig_exposure, autogain; const struct ctrl *gain_ctrl = NULL; const struct ctrl *exposure_ctrl = NULL; const struct ctrl *autogain_ctrl = NULL; int retval = 0; for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_GAIN) gain_ctrl = &gspca_dev->sd_desc->ctrls[i]; if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_EXPOSURE) exposure_ctrl = &gspca_dev->sd_desc->ctrls[i]; if (gspca_dev->sd_desc->ctrls[i].qctrl.id == V4L2_CID_AUTOGAIN) autogain_ctrl = &gspca_dev->sd_desc->ctrls[i]; } if (!gain_ctrl || !exposure_ctrl || !autogain_ctrl) { PDEBUG(D_ERR, "Error: gspca_auto_gain_n_exposure called " "on cam without (auto)gain/exposure"); return 0; } if (gain_ctrl->get(gspca_dev, &gain) || exposure_ctrl->get(gspca_dev, &exposure) || autogain_ctrl->get(gspca_dev, &autogain) || !autogain) return 0; orig_gain = gain; orig_exposure = exposure; /* If we are of a multiple of deadzone, do multiple steps to reach the desired lumination fast (with the risc of a slight overshoot) */ steps = abs(desired_avg_lum - avg_lum) / deadzone; PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d\n", avg_lum, desired_avg_lum, steps); for (i = 0; i < steps; i++) { if (avg_lum > desired_avg_lum) { if (gain > gain_knee) gain--; else if (exposure > exposure_knee) exposure--; else if (gain > gain_ctrl->qctrl.default_value) gain--; else if (exposure > exposure_ctrl->qctrl.minimum) exposure--; else if (gain > gain_ctrl->qctrl.minimum) gain--; else break; } else { if (gain < gain_ctrl->qctrl.default_value) gain++; else if (exposure < exposure_knee) exposure++; else if (gain < gain_knee) gain++; else if (exposure < exposure_ctrl->qctrl.maximum) exposure++; else if (gain < gain_ctrl->qctrl.maximum) gain++; else break; } } if (gain != orig_gain) { gain_ctrl->set(gspca_dev, gain); retval = 1; } if (exposure != orig_exposure) { exposure_ctrl->set(gspca_dev, exposure); retval = 1; } return retval;}EXPORT_SYMBOL(gspca_auto_gain_n_exposure);/* -- module insert / remove -- */static int __init gspca_init(void){ info("main v%d.%d.%d registered", (DRIVER_VERSION_NUMBER >> 16) & 0xff, (DRIVER_VERSION_NUMBER >> 8) & 0xff, DRIVER_VERSION_NUMBER & 0xff); return 0;}static void __exit gspca_exit(void){ info("main deregistered");}module_init(gspca_init);module_exit(gspca_exit);#ifdef GSPCA_DEBUGmodule_param_named(debug, gspca_debug, int, 0644);MODULE_PARM_DESC(debug, "Debug (bit) 0x01:error 0x02:probe 0x04:config" " 0x08:stream 0x10:frame 0x20:packet 0x40:USBin 0x80:USBout" " 0x0100: v4l2");#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -