📄 gspca.c
字号:
if (gspca_dev->alt != 0) { /* isoc */ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); npkt = ISO_MAX_SIZE / psize; if (npkt > ISO_MAX_PKT) npkt = ISO_MAX_PKT; bsize = psize * npkt; PDEBUG(D_STREAM, "isoc %d pkts size %d = bsize:%d", npkt, psize, bsize); nurbs = DEF_NURBS; } else { /* bulk */ npkt = 0; bsize = gspca_dev->cam. bulk_size; if (bsize == 0) bsize = psize; PDEBUG(D_STREAM, "bulk bsize:%d", bsize); nurbs = 1; } gspca_dev->nurbs = nurbs; for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { err("usb_alloc_urb failed"); destroy_urbs(gspca_dev); return -ENOMEM; } urb->transfer_buffer = usb_buffer_alloc(gspca_dev->dev, bsize, GFP_KERNEL, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { usb_free_urb(urb); err("usb_buffer_urb failed"); destroy_urbs(gspca_dev); return -ENOMEM; } gspca_dev->urb[n] = urb; urb->dev = gspca_dev->dev; urb->context = gspca_dev; urb->transfer_buffer_length = bsize; if (npkt != 0) { /* ISOC */ urb->pipe = usb_rcvisocpipe(gspca_dev->dev, ep->desc.bEndpointAddress); urb->transfer_flags = URB_ISO_ASAP | URB_NO_TRANSFER_DMA_MAP; urb->interval = ep->desc.bInterval; urb->complete = isoc_irq; urb->number_of_packets = npkt; for (i = 0; i < npkt; i++) { urb->iso_frame_desc[i].length = psize; urb->iso_frame_desc[i].offset = psize * i; } } else { /* bulk */ urb->pipe = usb_rcvbulkpipe(gspca_dev->dev, ep->desc.bEndpointAddress), urb->transfer_flags = URB_NO_TRANSFER_DMA_MAP; urb->complete = bulk_irq; } } return 0;}/* * start the USB transfer */static int gspca_init_transfer(struct gspca_dev *gspca_dev){ struct usb_host_endpoint *ep; int n, ret; if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; /* set the higher alternate setting and * loop until urb submit succeeds */ gspca_dev->alt = gspca_dev->nbalt; for (;;) { PDEBUG(D_STREAM, "init transfer alt %d", gspca_dev->alt); ep = get_ep(gspca_dev); if (ep == NULL) { ret = -EIO; goto out; } ret = create_urbs(gspca_dev, ep); if (ret < 0) goto out; /* start the cam */ ret = gspca_dev->sd_desc->start(gspca_dev); if (ret < 0) { destroy_urbs(gspca_dev); goto out; } gspca_dev->streaming = 1; atomic_set(&gspca_dev->nevent, 0); /* bulk transfers are started by the subdriver */ if (gspca_dev->alt == 0) break; /* submit the URBs */ for (n = 0; n < gspca_dev->nurbs; n++) { ret = usb_submit_urb(gspca_dev->urb[n], GFP_KERNEL); if (ret < 0) { PDEBUG(D_ERR|D_STREAM, "usb_submit_urb [%d] err %d", n, ret); gspca_dev->streaming = 0; destroy_urbs(gspca_dev); if (ret == -ENOSPC) break; /* try the previous alt */ goto out; } } if (ret >= 0) break; }out: mutex_unlock(&gspca_dev->usb_lock); return ret;}static int gspca_set_alt0(struct gspca_dev *gspca_dev){ int ret; ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); if (ret < 0) PDEBUG(D_ERR|D_STREAM, "set interface 0 err %d", ret); return ret;}/* Note: both the queue and the usb locks should be held when calling this */static void gspca_stream_off(struct gspca_dev *gspca_dev){ gspca_dev->streaming = 0; atomic_set(&gspca_dev->nevent, 0); if (gspca_dev->present) { 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); PDEBUG(D_STREAM, "stream off OK"); }}static void gspca_set_default_mode(struct gspca_dev *gspca_dev){ int i; i = gspca_dev->cam.nmodes - 1; /* take the highest mode */ gspca_dev->curr_mode = i; gspca_dev->width = gspca_dev->cam.cam_mode[i].width; gspca_dev->height = gspca_dev->cam.cam_mode[i].height; gspca_dev->pixfmt = gspca_dev->cam.cam_mode[i].pixelformat;}static int wxh_to_mode(struct gspca_dev *gspca_dev, int width, int height){ int i; for (i = gspca_dev->cam.nmodes; --i > 0; ) { if (width >= gspca_dev->cam.cam_mode[i].width && height >= gspca_dev->cam.cam_mode[i].height) break; } return i;}/* * search a mode with the right pixel format */static int gspca_get_mode(struct gspca_dev *gspca_dev, int mode, int pixfmt){ int modeU, modeD; modeU = modeD = mode; while ((modeU < gspca_dev->cam.nmodes) || modeD >= 0) { if (--modeD >= 0) { if (gspca_dev->cam.cam_mode[modeD].pixelformat == pixfmt) return modeD; } if (++modeU < gspca_dev->cam.nmodes) { if (gspca_dev->cam.cam_mode[modeU].pixelformat == pixfmt) return modeU; } } return -EINVAL;}static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc){ struct gspca_dev *gspca_dev = priv; int i, j, index; __u32 fmt_tb[8]; /* give an index to each format */ index = 0; j = 0; for (i = gspca_dev->cam.nmodes; --i >= 0; ) { fmt_tb[index] = gspca_dev->cam.cam_mode[i].pixelformat; j = 0; for (;;) { if (fmt_tb[j] == fmt_tb[index]) break; j++; } if (j == index) { if (fmtdesc->index == index) break; /* new format */ index++; if (index >= sizeof fmt_tb / sizeof fmt_tb[0]) return -EINVAL; } } if (i < 0) return -EINVAL; /* no more format */ fmtdesc->pixelformat = fmt_tb[index]; if (gspca_is_compressed(fmt_tb[index])) fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; fmtdesc->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fmtdesc->description[0] = fmtdesc->pixelformat & 0xff; fmtdesc->description[1] = (fmtdesc->pixelformat >> 8) & 0xff; fmtdesc->description[2] = (fmtdesc->pixelformat >> 16) & 0xff; fmtdesc->description[3] = fmtdesc->pixelformat >> 24; fmtdesc->description[4] = '\0'; return 0;}static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt){ struct gspca_dev *gspca_dev = priv; int mode; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; mode = gspca_dev->curr_mode; memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], sizeof fmt->fmt.pix); return 0;}static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, struct v4l2_format *fmt){ int w, h, mode, mode2; if (fmt->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; w = fmt->fmt.pix.width; h = fmt->fmt.pix.height;#ifdef GSPCA_DEBUG if (gspca_debug & D_CONF) PDEBUG_MODE("try fmt cap", fmt->fmt.pix.pixelformat, w, h);#endif /* search the closest mode for width and height */ mode = wxh_to_mode(gspca_dev, w, h); /* OK if right palette */ if (gspca_dev->cam.cam_mode[mode].pixelformat != fmt->fmt.pix.pixelformat) { /* else, search the closest mode with the same pixel format */ mode2 = gspca_get_mode(gspca_dev, mode, fmt->fmt.pix.pixelformat); if (mode2 >= 0) mode = mode2;/* else ; * no chance, return this mode */ } memcpy(&fmt->fmt.pix, &gspca_dev->cam.cam_mode[mode], sizeof fmt->fmt.pix); return mode; /* used when s_fmt */}static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt){ struct gspca_dev *gspca_dev = priv; int ret; ret = try_fmt_vid_cap(gspca_dev, fmt); if (ret < 0) return ret; return 0;}static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt){ struct gspca_dev *gspca_dev = priv; int ret; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; ret = try_fmt_vid_cap(gspca_dev, fmt); if (ret < 0) goto out; if (gspca_dev->nframes != 0 && fmt->fmt.pix.sizeimage > gspca_dev->frsz) { ret = -EINVAL; goto out; } if (ret == gspca_dev->curr_mode) { ret = 0; goto out; /* same mode */ } if (gspca_dev->streaming) { ret = -EBUSY; goto out; } gspca_dev->width = fmt->fmt.pix.width; gspca_dev->height = fmt->fmt.pix.height; gspca_dev->pixfmt = fmt->fmt.pix.pixelformat; gspca_dev->curr_mode = ret; ret = 0;out: mutex_unlock(&gspca_dev->queue_lock); return ret;}static void gspca_delete(struct kref *kref){ struct gspca_dev *gspca_dev = container_of(kref, struct gspca_dev, kref); PDEBUG(D_STREAM, "device deleted"); kfree(gspca_dev->usb_buf); kfree(gspca_dev);}static int dev_open(struct inode *inode, struct file *file){ struct gspca_dev *gspca_dev; int ret; PDEBUG(D_STREAM, "%s open", current->comm); gspca_dev = (struct gspca_dev *) video_devdata(file); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (!gspca_dev->present) { ret = -ENODEV; goto out; } if (gspca_dev->users > 4) { /* (arbitrary value) */ ret = -EBUSY; goto out; } gspca_dev->users++; /* one more user */ kref_get(&gspca_dev->kref); file->private_data = gspca_dev;#ifdef GSPCA_DEBUG /* activate the v4l2 debug */ if (gspca_debug & D_V4L2) gspca_dev->vdev.debug |= V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; else gspca_dev->vdev.debug &= ~(V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG);#endif ret = 0;out: mutex_unlock(&gspca_dev->queue_lock); if (ret != 0) PDEBUG(D_ERR|D_STREAM, "open failed err %d", ret); else PDEBUG(D_STREAM, "open done"); return ret;}static int dev_close(struct inode *inode, struct file *file){ struct gspca_dev *gspca_dev = file->private_data; PDEBUG(D_STREAM, "%s close", current->comm); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; gspca_dev->users--; /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { if (gspca_dev->streaming) { mutex_lock(&gspca_dev->usb_lock); gspca_stream_off(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } frame_free(gspca_dev); gspca_dev->capt_file = NULL; gspca_dev->memory = GSPCA_MEMORY_NO; } file->private_data = NULL; mutex_unlock(&gspca_dev->queue_lock); PDEBUG(D_STREAM, "close done"); kref_put(&gspca_dev->kref, gspca_delete); return 0;}static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap){ struct gspca_dev *gspca_dev = priv; memset(cap, 0, sizeof *cap); strncpy(cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); if (gspca_dev->dev->product != NULL) { strncpy(cap->card, gspca_dev->dev->product, sizeof cap->card); } else { snprintf(cap->card, sizeof cap->card, "USB Camera (%04x:%04x)", le16_to_cpu(gspca_dev->dev->descriptor.idVendor), le16_to_cpu(gspca_dev->dev->descriptor.idProduct)); } strncpy(cap->bus_info, gspca_dev->dev->bus->bus_name, sizeof cap->bus_info); cap->version = DRIVER_VERSION_NUMBER; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; return 0;}static int vidioc_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *q_ctrl){ struct gspca_dev *gspca_dev = priv; int i, ix; u32 id; ix = -1; id = q_ctrl->id; if (id & V4L2_CTRL_FLAG_NEXT_CTRL) { id &= V4L2_CTRL_ID_MASK; id++; for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { if (gspca_dev->sd_desc->ctrls[i].qctrl.id < id) continue; if (ix < 0) { ix = i; continue; } if (gspca_dev->sd_desc->ctrls[i].qctrl.id > gspca_dev->sd_desc->ctrls[ix].qctrl.id) continue; ix = i; } } for (i = 0; i < gspca_dev->sd_desc->nctrls; i++) { if (id == gspca_dev->sd_desc->ctrls[i].qctrl.id) { ix = i; break; } } if (ix < 0) return -EINVAL; memcpy(q_ctrl, &gspca_dev->sd_desc->ctrls[ix].qctrl, sizeof *q_ctrl); if (gspca_dev->ctrl_dis & (1 << ix)) q_ctrl->flags |= V4L2_CTRL_FLAG_DISABLED; return 0;}static int vidioc_s_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl){ struct gspca_dev *gspca_dev = priv; const struct ctrl *ctrls; int i, ret; for (i = 0, ctrls = gspca_dev->sd_desc->ctrls; i < gspca_dev->sd_desc->nctrls; i++, ctrls++) { if (ctrl->id != ctrls->qctrl.id) continue; if (gspca_dev->ctrl_dis & (1 << i)) return -EINVAL; if (ctrl->value < ctrls->qctrl.minimum || ctrl->value > ctrls->qctrl.maximum) return -ERANGE; PDEBUG(D_CONF, "set ctrl [%08x] = %d", ctrl->id, ctrl->value); if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; ret = ctrls->set(gspca_dev, ctrl->value); mutex_unlock(&gspca_dev->usb_lock); return ret; } return -EINVAL;}static int vidioc_g_ctrl(struct file *file, void *priv, struct v4l2_control *ctrl)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -