cregit-Linux how code gets into the kernel

Release 4.7 drivers/media/usb/gspca/gspca.c

/*
 * Main USB camera driver
 *
 * Copyright (C) 2008-2011 Jean-François Moine <http://moinejf.free.fr>
 *
 * Camera button input handling by Márton Németh
 * Copyright (C) 2009-2010 Márton Németh <nm127@freemail.hu>
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or (at your
 * option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt


#define GSPCA_VERSION	"2.14.0"

#include <linux/init.h>
#include <linux/fs.h>
#include <linux/vmalloc.h>
#include <linux/sched.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/pagemap.h>
#include <linux/io.h>
#include <asm/page.h>
#include <linux/uaccess.h>
#include <linux/ktime.h>
#include <media/v4l2-ioctl.h>
#include <media/v4l2-ctrls.h>
#include <media/v4l2-fh.h>
#include <media/v4l2-event.h>

#include "gspca.h"

#if IS_ENABLED(CONFIG_INPUT)
#include <linux/input.h>
#include <linux/usb/input.h>
#endif

/* global values */

#define DEF_NURBS 3		
/* default number of URBs */
#if DEF_NURBS > MAX_NURBS
#error "DEF_NURBS too big"
#endif

MODULE_AUTHOR("Jean-François Moine <http://moinejf.free.fr>");
MODULE_DESCRIPTION("GSPCA USB Camera Driver");
MODULE_LICENSE("GPL");

MODULE_VERSION(GSPCA_VERSION);


int gspca_debug;

EXPORT_SYMBOL(gspca_debug);


static void PDEBUG_MODE(struct gspca_dev *gspca_dev, int debug, char *txt, __u32 pixfmt, int w, int h) { if ((pixfmt >> 24) >= '0' && (pixfmt >> 24) <= 'z') { PDEBUG(debug, "%s %c%c%c%c %dx%d", txt, pixfmt & 0xff, (pixfmt >> 8) & 0xff, (pixfmt >> 16) & 0xff, pixfmt >> 24, w, h); } else { PDEBUG(debug, "%s 0x%08x %dx%d", txt, pixfmt, w, h); } }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine7371.57%133.33%
marton nemethmarton nemeth1918.63%133.33%
theodore kilgoretheodore kilgore109.80%133.33%
Total102100.00%3100.00%

/* specific memory types - !! should be different from V4L2_MEMORY_xxx */ #define GSPCA_MEMORY_NO 0 /* V4L2_MEMORY_xxx starts from 1 */ #define GSPCA_MEMORY_READ 7 #define BUF_ALL_FLAGS (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE) /* * VMA operations. */
static void gspca_vm_open(struct vm_area_struct *vma) { struct gspca_frame *frame = vma->vm_private_data; frame->vma_use_count++; frame->v4l2_buf.flags |= V4L2_BUF_FLAG_MAPPED; }

Contributors

PersonTokensPropCommitsCommitProp
marton nemethmarton nemeth33100.00%1100.00%
Total33100.00%1100.00%


static void gspca_vm_close(struct vm_area_struct *vma) { struct gspca_frame *frame = vma->vm_private_data; if (--frame->vma_use_count <= 0) frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_MAPPED; }

Contributors

PersonTokensPropCommitsCommitProp
marton nemethmarton nemeth38100.00%1100.00%
Total38100.00%1100.00%

static const struct vm_operations_struct gspca_vm_ops = { .open = gspca_vm_open, .close = gspca_vm_close, }; /* * Input and interrupt endpoint handling functions */ #if IS_ENABLED(CONFIG_INPUT)
static void int_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; int ret; ret = urb->status; switch (ret) { case 0: if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev, urb->transfer_buffer, urb->actual_length) < 0) { PERR("Unknown packet received"); } break; case -ENOENT: case -ECONNRESET: case -ENODEV: case -ESHUTDOWN: /* Stop is requested either by software or hardware is gone, * keep the ret value non-zero and don't resubmit later. */ break; default: PERR("URB error %i, resubmitting", urb->status); urb->status = 0; ret = 0; } if (ret == 0) { ret = usb_submit_urb(urb, GFP_ATOMIC); if (ret < 0) pr_err("Resubmit URB failed with error %i\n", ret); } }

Contributors

PersonTokensPropCommitsCommitProp
marton nemethmarton nemeth13697.14%133.33%
joe perchesjoe perches21.43%133.33%
theodore kilgoretheodore kilgore21.43%133.33%
Total140100.00%3100.00%


static int gspca_input_connect(struct gspca_dev *dev) { struct input_dev *input_dev; int err = 0; dev->input_dev = NULL; if (dev->sd_desc->int_pkt_scan || dev->sd_desc->other_input) { input_dev = input_allocate_device(); if (!input_dev) return -ENOMEM; usb_make_path(dev->dev, dev->phys, sizeof(dev->phys)); strlcat(dev->phys, "/input0", sizeof(dev->phys)); input_dev->name = dev->sd_desc->name; input_dev->phys = dev->phys; usb_to_input_id(dev->dev, &input_dev->id); input_dev->evbit[0] = BIT_MASK(EV_KEY); input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA); input_dev->dev.parent = &dev->dev->dev; err = input_register_device(input_dev); if (err) { pr_err("Input device registration failed with error %i\n", err); input_dev->dev.parent = NULL; input_free_device(input_dev); } else { dev->input_dev = input_dev; } } return err; }

Contributors

PersonTokensPropCommitsCommitProp
marton nemethmarton nemeth19896.12%133.33%
hans de goedehans de goede62.91%133.33%
joe perchesjoe perches20.97%133.33%
Total206100.00%3100.00%


static int alloc_and_submit_int_urb(struct gspca_dev *gspca_dev, struct usb_endpoint_descriptor *ep) { unsigned int buffer_len; int interval; struct urb *urb; struct usb_device *dev; void *buffer = NULL; int ret = -EINVAL; buffer_len = le16_to_cpu(ep->wMaxPacketSize); interval = ep->bInterval; PDEBUG(D_CONF, "found int in endpoint: 0x%x, " "buffer_len=%u, interval=%u", ep->bEndpointAddress, buffer_len, interval); dev = gspca_dev->dev; urb = usb_alloc_urb(0, GFP_KERNEL); if (!urb) { ret = -ENOMEM; goto error; } buffer = usb_alloc_coherent(dev, buffer_len, GFP_KERNEL, &urb->transfer_dma); if (!buffer) { ret = -ENOMEM; goto error_buffer; } usb_fill_int_urb(urb, dev, usb_rcvintpipe(dev, ep->bEndpointAddress), buffer, buffer_len, int_irq, (void *)gspca_dev, interval); urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) { PERR("submit int URB failed with error %i", ret); goto error_submit; } gspca_dev->int_urb = urb; return ret; error_submit: usb_free_coherent(dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); error_buffer: usb_free_urb(urb); error: return ret; }

Contributors

PersonTokensPropCommitsCommitProp
marton nemethmarton nemeth21991.25%114.29%
hans de goedehans de goede62.50%114.29%
jason wangjason wang62.50%114.29%
jean-francois moinejean-francois moine62.50%228.57%
daniel mackdaniel mack20.83%114.29%
theodore kilgoretheodore kilgore10.42%114.29%
Total240100.00%7100.00%


static void gspca_input_create_urb(struct gspca_dev *gspca_dev) { struct usb_interface *intf; struct usb_host_interface *intf_desc; struct usb_endpoint_descriptor *ep; int i; if (gspca_dev->sd_desc->int_pkt_scan) { intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); intf_desc = intf->cur_altsetting; for (i = 0; i < intf_desc->desc.bNumEndpoints; i++) { ep = &intf_desc->endpoint[i].desc; if (usb_endpoint_dir_in(ep) && usb_endpoint_xfer_int(ep)) { alloc_and_submit_int_urb(gspca_dev, ep); break; } } } }

Contributors

PersonTokensPropCommitsCommitProp
marton nemethmarton nemeth11099.10%150.00%
hans de goedehans de goede10.90%150.00%
Total111100.00%2100.00%


static void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) { struct urb *urb; urb = gspca_dev->int_urb; if (urb) { gspca_dev->int_urb = NULL; usb_kill_urb(urb); usb_free_coherent(gspca_dev->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); usb_free_urb(urb); } }

Contributors

PersonTokensPropCommitsCommitProp
marton nemethmarton nemeth5485.71%120.00%
jean-francois moinejean-francois moine812.70%360.00%
daniel mackdaniel mack11.59%120.00%
Total63100.00%5100.00%

#else
static inline void gspca_input_destroy_urb(struct gspca_dev *gspca_dev) { }

Contributors

PersonTokensPropCommitsCommitProp
randy dunlaprandy dunlap11100.00%1100.00%
Total11100.00%1100.00%


static inline void gspca_input_create_urb(struct gspca_dev *gspca_dev) { }

Contributors

PersonTokensPropCommitsCommitProp
randy dunlaprandy dunlap11100.00%1100.00%
Total11100.00%1100.00%


static inline int gspca_input_connect(struct gspca_dev *dev) { return 0; }

Contributors

PersonTokensPropCommitsCommitProp
randy dunlaprandy dunlap15100.00%1100.00%
Total15100.00%1100.00%

#endif /* * fill a video frame from an URB and resubmit */
static void fill_frame(struct gspca_dev *gspca_dev, struct urb *urb) { u8 *data; /* address of data in the iso message */ int i, len, st; cam_pkt_op pkt_scan; if (urb->status != 0) { if (urb->status == -ESHUTDOWN) return; /* disconnection */ #ifdef CONFIG_PM if (gspca_dev->frozen) return; #endif PERR("urb status: %d", urb->status); urb->status = 0; goto resubmit; } pkt_scan = gspca_dev->sd_desc->pkt_scan; for (i = 0; i < urb->number_of_packets; i++) { len = urb->iso_frame_desc[i].actual_length; /* check the packet status and length */ st = urb->iso_frame_desc[i].status; if (st) { pr_err("ISOC data error: [%d] len=%d, status=%d\n", i, len, st); gspca_dev->last_packet_type = DISCARD_PACKET; continue; } if (len == 0) { if (gspca_dev->empty_packet == 0) gspca_dev->empty_packet = 1; continue; } /* let the packet be analyzed by the subdriver */ PDEBUG(D_PACK, "packet [%d] o:%d l:%d", i, urb->iso_frame_desc[i].offset, len); data = (u8 *) urb->transfer_buffer + urb->iso_frame_desc[i].offset; pkt_scan(gspca_dev, data, len); } resubmit: /* resubmit the URB */ st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) pr_err("usb_submit_urb() ret %d\n", st); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine22991.24%1066.67%
mauro carvalho chehabmauro carvalho chehab114.38%16.67%
hans de goedehans de goede62.39%213.33%
joe perchesjoe perches41.59%16.67%
theodore kilgoretheodore kilgore10.40%16.67%
Total251100.00%15100.00%

/* * ISOC message interrupt from the USB device * * Analyse each packet and call the subdriver for copy to the frame buffer. */
static void isoc_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; PDEBUG(D_PACK, "isoc irq"); if (!gspca_dev->streaming) return; fill_frame(gspca_dev, urb); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4697.87%266.67%
hans de goedehans de goede12.13%133.33%
Total47100.00%3100.00%

/* * bulk message interrupt from the USB device */
static void bulk_irq(struct urb *urb) { struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; int st; PDEBUG(D_PACK, "bulk irq"); if (!gspca_dev->streaming) return; switch (urb->status) { case 0: break; case -ESHUTDOWN: return; /* disconnection */ default: #ifdef CONFIG_PM if (gspca_dev->frozen) return; #endif PERR("urb status: %d", urb->status); urb->status = 0; goto resubmit; } PDEBUG(D_PACK, "packet l:%d", urb->actual_length); gspca_dev->sd_desc->pkt_scan(gspca_dev, urb->transfer_buffer, urb->actual_length); resubmit: /* resubmit the URB */ if (gspca_dev->cam.bulk_nurbs != 0) { st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0) pr_err("usb_submit_urb() ret %d\n", st); } }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine11271.34%444.44%
antonio ospiteantonio ospite3622.93%111.11%
hans de goedehans de goede63.82%222.22%
joe perchesjoe perches21.27%111.11%
theodore kilgoretheodore kilgore10.64%111.11%
Total157100.00%9100.00%

/* * add data to the current frame * * This function is called by the subdrivers at interrupt level. * * To build a frame, these ones must add * - one FIRST_PACKET * - 0 or many INTER_PACKETs * - one LAST_PACKET * DISCARD_PACKET invalidates the whole frame. */
void gspca_frame_add(struct gspca_dev *gspca_dev, enum gspca_packet_type packet_type, const u8 *data, int len) { struct gspca_frame *frame; int i, j; PDEBUG(D_PACK, "add t:%d l:%d", packet_type, len); if (packet_type == FIRST_PACKET) { i = atomic_read(&gspca_dev->fr_i); /* if there are no queued buffer, discard the whole frame */ if (i == atomic_read(&gspca_dev->fr_q)) { gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence++; return; } j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; v4l2_get_timestamp(&frame->v4l2_buf.timestamp); frame->v4l2_buf.sequence = gspca_dev->sequence++; gspca_dev->image = frame->data; gspca_dev->image_len = 0; } else { switch (gspca_dev->last_packet_type) { case DISCARD_PACKET: if (packet_type == LAST_PACKET) { gspca_dev->last_packet_type = packet_type; gspca_dev->image = NULL; gspca_dev->image_len = 0; } return; case LAST_PACKET: return; } } /* append the packet to the frame buffer */ if (len > 0) { if (gspca_dev->image_len + len > gspca_dev->frsz) { PERR("frame overflow %d > %d", gspca_dev->image_len + len, gspca_dev->frsz); packet_type = DISCARD_PACKET; } else { /* !! image is NULL only when last pkt is LAST or DISCARD if (gspca_dev->image == NULL) { pr_err("gspca_frame_add() image == NULL\n"); return; } */ memcpy(gspca_dev->image + gspca_dev->image_len, data, len); gspca_dev->image_len += len; } } gspca_dev->last_packet_type = packet_type; /* if last packet, invalidate packet concatenation until * next first packet, wake up the application and advance * in the queue */ if (packet_type == LAST_PACKET) { i = atomic_read(&gspca_dev->fr_i); j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; frame->v4l2_buf.bytesused = gspca_dev->image_len; frame->v4l2_buf.flags = (frame->v4l2_buf.flags | V4L2_BUF_FLAG_DONE) & ~V4L2_BUF_FLAG_QUEUED; i = (i + 1) % GSPCA_MAX_FRAMES; atomic_set(&gspca_dev->fr_i, i); wake_up_interruptible(&gspca_dev->wq); /* event = new frame */ PDEBUG(D_FRAM, "frame complete len:%d", frame->v4l2_buf.bytesused); gspca_dev->image = NULL; gspca_dev->image_len = 0; } }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine35994.23%1266.67%
hans de goedehans de goede153.94%211.11%
arnd bergmannarnd bergmann30.79%15.56%
erik andrenerik andren20.52%15.56%
theodore kilgoretheodore kilgore10.26%15.56%
joe perchesjoe perches10.26%15.56%
Total381100.00%18100.00%

EXPORT_SYMBOL(gspca_frame_add);
static int frame_alloc(struct gspca_dev *gspca_dev, struct file *file, enum v4l2_memory memory, unsigned int count) { struct gspca_frame *frame; unsigned int frsz; int i; frsz = gspca_dev->pixfmt.sizeimage; PDEBUG(D_STREAM, "frame alloc frsz: %d", frsz); frsz = PAGE_ALIGN(frsz); if (count >= GSPCA_MAX_FRAMES) count = GSPCA_MAX_FRAMES - 1; gspca_dev->frbuf = vmalloc_32(frsz * count); if (!gspca_dev->frbuf) { pr_err("frame alloc failed\n"); return -ENOMEM; } gspca_dev->capt_file = file; gspca_dev->memory = memory; gspca_dev->frsz = frsz; gspca_dev->nframes = count; for (i = 0; i < count; i++) { frame = &gspca_dev->frame[i]; frame->v4l2_buf.index = i; frame->v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; frame->v4l2_buf.flags = 0; frame->v4l2_buf.field = V4L2_FIELD_NONE; frame->v4l2_buf.length = frsz; frame->v4l2_buf.memory = memory; frame->v4l2_buf.sequence = 0; frame->data = gspca_dev->frbuf + i * frsz; frame->v4l2_buf.m.offset = i * frsz; } atomic_set(&gspca_dev->fr_q, 0); atomic_set(&gspca_dev->fr_i, 0); gspca_dev->fr_o = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine22988.42%666.67%
hans de goedehans de goede2710.42%111.11%
joe perchesjoe perches20.77%111.11%
ondrej zaryondrej zary10.39%111.11%
Total259100.00%9100.00%


static void frame_free(struct gspca_dev *gspca_dev) { int i; PDEBUG(D_STREAM, "frame free"); if (gspca_dev->frbuf != NULL) { vfree(gspca_dev->frbuf); gspca_dev->frbuf = NULL; for (i = 0; i < gspca_dev->nframes; i++) gspca_dev->frame[i].data = NULL; } gspca_dev->nframes = 0; gspca_dev->frsz = 0; gspca_dev->capt_file = NULL; gspca_dev->memory = GSPCA_MEMORY_NO; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine7579.79%250.00%
hans de goedehans de goede1819.15%125.00%
harvey harrisonharvey harrison11.06%125.00%
Total94100.00%4100.00%


static void destroy_urbs(struct gspca_dev *gspca_dev) { struct urb *urb; unsigned int i; PDEBUG(D_STREAM, "kill transfer"); for (i = 0; i < MAX_NURBS; i++) { urb = gspca_dev->urb[i]; if (urb == NULL) break; gspca_dev->urb[i] = NULL; usb_kill_urb(urb); usb_free_coherent(gspca_dev->dev, urb->transfer_buffer_length, urb->transfer_buffer, urb->transfer_dma); usb_free_urb(urb); } }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine9497.92%360.00%
erik andrenerik andren11.04%120.00%
daniel mackdaniel mack11.04%120.00%
Total96100.00%5100.00%


static int gspca_set_alt0(struct gspca_dev *gspca_dev) { int ret; if (gspca_dev->alt == 0) return 0; ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, 0); if (ret < 0) pr_err("set alt 0 err %d\n", ret); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine5496.43%150.00%
joe perchesjoe perches23.57%150.00%
Total56100.00%2100.00%

/* 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; gspca_dev->usb_err = 0; if (gspca_dev->sd_desc->stopN) gspca_dev->sd_desc->stopN(gspca_dev); destroy_urbs(gspca_dev); gspca_input_destroy_urb(gspca_dev); gspca_set_alt0(gspca_dev); gspca_input_create_urb(gspca_dev); if (gspca_dev->sd_desc->stop0) gspca_dev->sd_desc->stop0(gspca_dev); PDEBUG(D_STREAM, "stream off OK"); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine7892.86%150.00%
hans de goedehans de goede67.14%150.00%
Total84100.00%2100.00%

/* * look for an input transfer endpoint in an alternate setting. * * If xfer_ep is invalid, return the first valid ep found, otherwise * look for exactly the ep with address equal to xfer_ep. */
static struct usb_host_endpoint *alt_xfer(struct usb_host_interface *alt, int xfer, int xfer_ep) { struct usb_host_endpoint *ep; int i, attr; for (i = 0; i < alt->desc.bNumEndpoints; i++) { ep = &alt->endpoint[i]; attr = ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK; if (attr == xfer && ep->desc.wMaxPacketSize != 0 && usb_endpoint_dir_in(&ep->desc) && (xfer_ep < 0 || ep->desc.bEndpointAddress == xfer_ep)) return ep; } return NULL; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine8577.27%466.67%
antonio ospiteantonio ospite1715.45%116.67%
patrice chotardpatrice chotard87.27%116.67%
Total110100.00%6100.00%

/* compute the minimum bandwidth for the current transfer */
static u32 which_bandwidth(struct gspca_dev *gspca_dev) { u32 bandwidth; /* get the (max) image size */ bandwidth = gspca_dev->pixfmt.sizeimage; /* if the image is compressed, estimate its mean size */ if (!gspca_dev->cam.needs_full_bandwidth && bandwidth < gspca_dev->pixfmt.width * gspca_dev->pixfmt.height) bandwidth = bandwidth * 3 / 8; /* 0.375 */ /* estimate the frame rate */ if (gspca_dev->sd_desc->get_streamparm) { struct v4l2_streamparm parm; gspca_dev->sd_desc->get_streamparm(gspca_dev, &parm); bandwidth *= parm.parm.capture.timeperframe.denominator; bandwidth /= parm.parm.capture.timeperframe.numerator; } else { /* don't hope more than 15 fps with USB 1.1 and * image resolution >= 640x480 */ if (gspca_dev->pixfmt.width >= 640 && gspca_dev->dev->speed == USB_SPEED_FULL) bandwidth *= 15; /* 15 fps */ else bandwidth *= 30; /* 30 fps */ } PDEBUG(D_STREAM, "min bandwidth: %d", bandwidth); return bandwidth; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine11173.03%444.44%
hans de goedehans de goede3523.03%333.33%
ondrej zaryondrej zary53.29%111.11%
adrian bunkadrian bunk10.66%111.11%
Total152100.00%9100.00%

/* endpoint table */ #define MAX_ALT 16 struct ep_tb_s { u32 alt; u32 bandwidth; }; /* * build the table of the endpoints * and compute the minimum bandwidth for the image transfer */
static int build_isoc_ep_tb(struct gspca_dev *gspca_dev, struct usb_interface *intf, struct ep_tb_s *ep_tb) { struct usb_host_endpoint *ep; int i, j, nbalt, psize, found; u32 bandwidth, last_bw; nbalt = intf->num_altsetting; if (nbalt > MAX_ALT) nbalt = MAX_ALT; /* fixme: should warn */ /* build the endpoint table */ i = 0; last_bw = 0; for (;;) { ep_tb->bandwidth = 2000 * 2000 * 120; found = 0; for (j = 0; j < nbalt; j++) { ep = alt_xfer(&intf->altsetting[j], USB_ENDPOINT_XFER_ISOC, gspca_dev->xfer_ep); if (ep == NULL) continue; if (ep->desc.bInterval == 0) { pr_err("alt %d iso endp with 0 interval\n", j); continue; } psize = le16_to_cpu(ep->desc.wMaxPacketSize); psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); bandwidth = psize * 1000; if (gspca_dev->dev->speed == USB_SPEED_HIGH || gspca_dev->dev->speed == USB_SPEED_SUPER) bandwidth *= 8; bandwidth /= 1 << (ep->desc.bInterval - 1); if (bandwidth <= last_bw) continue; if (bandwidth < ep_tb->bandwidth) { ep_tb->bandwidth = bandwidth; ep_tb->alt = j; found = 1; } } if (!found) break; PDEBUG(D_STREAM, "alt %d bandwidth %d", ep_tb->alt, ep_tb->bandwidth); last_bw = ep_tb->bandwidth; i++; ep_tb++; } /* * If the camera: * has a usb audio class interface (a built in usb mic); and * is a usb 1 full speed device; and * uses the max full speed iso bandwidth; and * and has more than 1 alt setting * then skip the highest alt setting to spare bandwidth for the mic */ if (gspca_dev->audio && gspca_dev->dev->speed == USB_SPEED_FULL && last_bw >= 1000000 && i > 1) { PDEBUG(D_STREAM, "dev has usb audio, skipping highest alt"); i--; ep_tb--; } /* get the requested bandwidth and start at the highest atlsetting */ bandwidth = which_bandwidth(gspca_dev); ep_tb--; while (i > 1) { ep_tb--; if (ep_tb->bandwidth < bandwidth) break; i--; } return i; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine28577.87%857.14%
hans de goedehans de goede7520.49%428.57%
antonio ospiteantonio ospite41.09%17.14%
marton nemethmarton nemeth20.55%17.14%
Total366100.00%14100.00%

/* * create the URBs for image transfer */
static int create_urbs(struct gspca_dev *gspca_dev, struct usb_host_endpoint *ep) { struct urb *urb; int n, nurbs, i, psize, npkt, bsize; /* calculate the packet size and the number of packets */ psize = le16_to_cpu(ep->desc.wMaxPacketSize); if (!gspca_dev->cam.bulk) { /* isoc */ /* See paragraph 5.9 / table 5-11 of the usb 2.0 spec. */ if (gspca_dev->pkt_size == 0) psize = (psize & 0x07ff) * (1 + ((psize >> 11) & 3)); else psize = gspca_dev->pkt_size; npkt = gspca_dev->cam.npkt; if (npkt == 0) npkt = 32; /* default value */ 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); if (gspca_dev->cam.bulk_nurbs != 0) nurbs = gspca_dev->cam.bulk_nurbs; else nurbs = 1; } for (n = 0; n < nurbs; n++) { urb = usb_alloc_urb(npkt, GFP_KERNEL); if (!urb) { pr_err("usb_alloc_urb failed\n"); return -ENOMEM; } gspca_dev->urb[n] = urb; urb->transfer_buffer = usb_alloc_coherent(gspca_dev->dev, bsize, GFP_KERNEL, &urb->transfer_dma); if (urb->transfer_buffer == NULL) { pr_err("usb_alloc_coherent failed\n"); return -ENOMEM; } 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 = 1 << (ep->desc.bInterval - 1); 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; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine40992.74%1578.95%
antonio ospiteantonio ospite194.31%15.26%
frank zagofrank zago81.81%15.26%
joe perchesjoe perches40.91%15.26%
daniel mackdaniel mack10.23%15.26%
Total441100.00%19100.00%

/* * start the USB transfer */
static int gspca_init_transfer(struct gspca_dev *gspca_dev) { struct usb_interface *intf; struct usb_host_endpoint *ep; struct urb *urb; struct ep_tb_s ep_tb[MAX_ALT]; int n, ret, xfer, alt, alt_idx; /* reset the streaming variables */ gspca_dev->image = NULL; gspca_dev->image_len = 0; gspca_dev->last_packet_type = DISCARD_PACKET; gspca_dev->sequence = 0; gspca_dev->usb_err = 0; /* do the specific subdriver stuff before endpoint selection */ intf = usb_ifnum_to_if(gspca_dev->dev, gspca_dev->iface); gspca_dev->alt = gspca_dev->cam.bulk ? intf->num_altsetting : 0; if (gspca_dev->sd_desc->isoc_init) { ret = gspca_dev->sd_desc->isoc_init(gspca_dev); if (ret < 0) return ret; } xfer = gspca_dev->cam.bulk ? USB_ENDPOINT_XFER_BULK : USB_ENDPOINT_XFER_ISOC; /* if bulk or the subdriver forced an altsetting, get the endpoint */ if (gspca_dev->alt != 0) { gspca_dev->alt--; /* (previous version compatibility) */ ep = alt_xfer(&intf->altsetting[gspca_dev->alt], xfer, gspca_dev->xfer_ep); if (ep == NULL) { pr_err("bad altsetting %d\n", gspca_dev->alt); return -EIO; } ep_tb[0].alt = gspca_dev->alt; alt_idx = 1; } else { /* else, compute the minimum bandwidth * and build the endpoint table */ alt_idx = build_isoc_ep_tb(gspca_dev, intf, ep_tb); if (alt_idx <= 0) { pr_err("no transfer endpoint found\n"); return -EIO; } } /* set the highest alternate setting and * loop until urb submit succeeds */ gspca_input_destroy_urb(gspca_dev); gspca_dev->alt = ep_tb[--alt_idx].alt; alt = -1; for (;;) { if (alt != gspca_dev->alt) { alt = gspca_dev->alt; if (intf->num_altsetting > 1) { ret = usb_set_interface(gspca_dev->dev, gspca_dev->iface, alt); if (ret < 0) { if (ret == -ENOSPC) goto retry; /*fixme: ugly*/ pr_err("set alt %d err %d\n", alt, ret); goto out; } } } if (!gspca_dev->cam.no_urb_create) { PDEBUG(D_STREAM, "init transfer alt %d", alt); ret = create_urbs(gspca_dev, alt_xfer(&intf->altsetting[alt], xfer, gspca_dev->xfer_ep)); if (ret < 0) { destroy_urbs(gspca_dev); goto out; } } /* clear the bulk endpoint */ if (gspca_dev->cam.bulk) usb_clear_halt(gspca_dev->dev, gspca_dev->urb[0]->pipe); /* start the cam */ ret = gspca_dev->sd_desc->start(gspca_dev); if (ret < 0) { destroy_urbs(gspca_dev); goto out; } gspca_dev->streaming = 1; v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); /* some bulk transfers are started by the subdriver */ if (gspca_dev->cam.bulk && gspca_dev->cam.bulk_nurbs == 0) break; /* submit the URBs */ for (n = 0; n < MAX_NURBS; n++) { urb = gspca_dev->urb[n]; if (urb == NULL) break; ret = usb_submit_urb(urb, GFP_KERNEL); if (ret < 0) break; } if (ret >= 0) break; /* transfer is started */ /* something when wrong * stop the webcam and free the transfer resources */ gspca_stream_off(gspca_dev); if (ret != -ENOSPC) { pr_err("usb_submit_urb alt %d err %d\n", gspca_dev->alt, ret); goto out; } /* the bandwidth is not wide enough * negotiate or try a lower alternate setting */ retry: PERR("alt %d - bandwidth not wide enough, trying again", alt); msleep(20); /* wait for kill complete */ if (gspca_dev->sd_desc->isoc_nego) { ret = gspca_dev->sd_desc->isoc_nego(gspca_dev); if (ret < 0) goto out; } else { if (alt_idx <= 0) { pr_err("no transfer endpoint found\n"); ret = -EIO; goto out; } gspca_dev->alt = ep_tb[--alt_idx].alt; } } out: gspca_input_create_urb(gspca_dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine60188.64%2060.61%
hans de goedehans de goede365.31%412.12%
antonio ospiteantonio ospite182.65%39.09%
hans verkuilhans verkuil162.36%26.06%
mauro carvalho chehabmauro carvalho chehab20.29%13.03%
joe perchesjoe perches20.29%13.03%
theodore kilgoretheodore kilgore20.29%13.03%
lucas de marchilucas de marchi10.15%13.03%
Total678100.00%33100.00%


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->pixfmt = gspca_dev->cam.cam_mode[i]; /* does nothing if ctrl_handler == NULL */ v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4481.48%266.67%
hans verkuilhans verkuil1018.52%133.33%
Total54100.00%3100.00%


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; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine70100.00%2100.00%
Total70100.00%2100.00%

/* * 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; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine108100.00%2100.00%
Total108100.00%2100.00%

#ifdef CONFIG_VIDEO_ADV_DEBUG
static int vidioc_g_chip_info(struct file *file, void *priv, struct v4l2_dbg_chip_info *chip) { struct gspca_dev *gspca_dev = video_drvdata(file); gspca_dev->usb_err = 0; if (gspca_dev->sd_desc->get_chip_info) return gspca_dev->sd_desc->get_chip_info(gspca_dev, chip); return chip->match.addr ? -EINVAL : 0; }

Contributors

PersonTokensPropCommitsCommitProp
brian johnsonbrian johnson3247.06%120.00%
hans verkuilhans verkuil3044.12%360.00%
jean-francois moinejean-francois moine68.82%120.00%
Total68100.00%5100.00%


static int vidioc_g_register(struct file *file, void *priv, struct v4l2_dbg_register *reg) { struct gspca_dev *gspca_dev = video_drvdata(file); gspca_dev->usb_err = 0; return gspca_dev->sd_desc->get_register(gspca_dev, reg); }

Contributors

PersonTokensPropCommitsCommitProp
brian johnsonbrian johnson3572.92%120.00%
hans verkuilhans verkuil714.58%360.00%
jean-francois moinejean-francois moine612.50%120.00%
Total48100.00%5100.00%


static int vidioc_s_register(struct file *file, void *priv, const struct v4l2_dbg_register *reg) { struct gspca_dev *gspca_dev = video_drvdata(file); gspca_dev->usb_err = 0; return gspca_dev->sd_desc->set_register(gspca_dev, reg); }

Contributors

PersonTokensPropCommitsCommitProp
brian johnsonbrian johnson3265.31%120.00%
hans verkuilhans verkuil1122.45%360.00%
jean-francois moinejean-francois moine612.24%120.00%
Total49100.00%5100.00%

#endif
static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, struct v4l2_fmtdesc *fmtdesc) { struct gspca_dev *gspca_dev = video_drvdata(file); 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 >= ARRAY_SIZE(fmt_tb)) return -EINVAL; } } if (i < 0) return -EINVAL; /* no more format */ fmtdesc->pixelformat = fmt_tb[index]; if (gspca_dev->cam.cam_mode[i].sizeimage < gspca_dev->cam.cam_mode[i].width * gspca_dev->cam.cam_mode[i].height) fmtdesc->flags = V4L2_FMT_FLAG_COMPRESSED; 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; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine27297.14%562.50%
hans verkuilhans verkuil41.43%112.50%
julia lawalljulia lawall31.07%112.50%
mauro carvalho chehabmauro carvalho chehab10.36%112.50%
Total280100.00%8100.00%


static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct gspca_dev *gspca_dev = video_drvdata(file); fmt->fmt.pix = gspca_dev->pixfmt; /* some drivers use priv internally, zero it before giving it back to the core */ fmt->fmt.pix.priv = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4175.93%444.44%
hans verkuilhans verkuil1018.52%222.22%
ondrej zaryondrej zary11.85%111.11%
laurent pinchartlaurent pinchart11.85%111.11%
mauro carvalho chehabmauro carvalho chehab11.85%111.11%
Total54100.00%9100.00%


static int try_fmt_vid_cap(struct gspca_dev *gspca_dev, struct v4l2_format *fmt) { int w, h, mode, mode2; w = fmt->fmt.pix.width; h = fmt->fmt.pix.height; PDEBUG_MODE(gspca_dev, D_CONF, "try fmt cap", fmt->fmt.pix.pixelformat, w, h); /* 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; } fmt->fmt.pix = gspca_dev->cam.cam_mode[mode]; if (gspca_dev->sd_desc->try_fmt) { /* pass original resolution to subdriver try_fmt */ fmt->fmt.pix.width = w; fmt->fmt.pix.height = h; gspca_dev->sd_desc->try_fmt(gspca_dev, fmt); } /* some drivers use priv internally, zero it before giving it back to the core */ fmt->fmt.pix.priv = 0; return mode; /* used when s_fmt */ }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine14873.27%444.44%
ondrej zaryondrej zary4220.79%111.11%
hans verkuilhans verkuil62.97%111.11%
theodore kilgoretheodore kilgore41.98%111.11%
laurent pinchartlaurent pinchart10.50%111.11%
mauro carvalho chehabmauro carvalho chehab10.50%111.11%
Total202100.00%9100.00%


static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct gspca_dev *gspca_dev = video_drvdata(file); int ret; ret = try_fmt_vid_cap(gspca_dev, fmt); if (ret < 0) return ret; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4888.89%250.00%
hans verkuilhans verkuil47.41%125.00%
mauro carvalho chehabmauro carvalho chehab23.70%125.00%
Total54100.00%4100.00%


static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, struct v4l2_format *fmt) { struct gspca_dev *gspca_dev = video_drvdata(file); 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 (gspca_dev->streaming) { ret = -EBUSY; goto out; } gspca_dev->curr_mode = ret; if (gspca_dev->sd_desc->try_fmt) /* subdriver try_fmt can modify format parameters */ gspca_dev->pixfmt = fmt->fmt.pix; else gspca_dev->pixfmt = gspca_dev->cam.cam_mode[ret]; ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine11468.26%337.50%
ondrej zaryondrej zary2716.17%225.00%
hans de goedehans de goede2011.98%112.50%
hans verkuilhans verkuil42.40%112.50%
mauro carvalho chehabmauro carvalho chehab21.20%112.50%
Total167100.00%8100.00%


static int vidioc_enum_framesizes(struct file *file, void *priv, struct v4l2_frmsizeenum *fsize) { struct gspca_dev *gspca_dev = video_drvdata(file); int i; __u32 index = 0; if (gspca_dev->sd_desc->enum_framesizes) return gspca_dev->sd_desc->enum_framesizes(gspca_dev, fsize); for (i = 0; i < gspca_dev->cam.nmodes; i++) { if (fsize->pixel_format != gspca_dev->cam.cam_mode[i].pixelformat) continue; if (fsize->index == index) { fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; fsize->discrete.width = gspca_dev->cam.cam_mode[i].width; fsize->discrete.height = gspca_dev->cam.cam_mode[i].height; return 0; } index++; } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
hans de goedehans de goede13184.52%133.33%
ondrej zaryondrej zary2012.90%133.33%
hans verkuilhans verkuil42.58%133.33%
Total155100.00%3100.00%


static int vidioc_enum_frameintervals(struct file *filp, void *priv, struct v4l2_frmivalenum *fival) { struct gspca_dev *gspca_dev = video_drvdata(filp); int mode = wxh_to_mode(gspca_dev, fival->width, fival->height); __u32 i; if (gspca_dev->cam.mode_framerates == NULL || gspca_dev->cam.mode_framerates[mode].nrates == 0) return -EINVAL; if (fival->pixel_format != gspca_dev->cam.cam_mode[mode].pixelformat) return -EINVAL; for (i = 0; i < gspca_dev->cam.mode_framerates[mode].nrates; i++) { if (fival->index == i) { fival->type = V4L2_FRMSIZE_TYPE_DISCRETE; fival->discrete.numerator = 1; fival->discrete.denominator = gspca_dev->cam.mode_framerates[mode].rates[i]; return 0; } } return -EINVAL; }

Contributors

PersonTokensPropCommitsCommitProp
antonio ospiteantonio ospite16897.67%150.00%
hans verkuilhans verkuil42.33%150.00%
Total172100.00%2100.00%


static void gspca_release(struct v4l2_device *v4l2_device) { struct gspca_dev *gspca_dev = container_of(v4l2_device, struct gspca_dev, v4l2_dev); v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); v4l2_device_unregister(&gspca_dev->v4l2_dev); kfree(gspca_dev->usb_buf); kfree(gspca_dev); }

Contributors

PersonTokensPropCommitsCommitProp
frank zagofrank zago3360.00%120.00%
hans verkuilhans verkuil2138.18%360.00%
hans de goedehans de goede11.82%120.00%
Total55100.00%5100.00%


static int dev_open(struct file *file) { struct gspca_dev *gspca_dev = video_drvdata(file); int ret; PDEBUG(D_STREAM, "[%s] open", current->comm); /* protect the subdriver against rmmod */ if (!try_module_get(gspca_dev->module)) return -ENODEV; ret = v4l2_fh_open(file); if (ret) module_put(gspca_dev->module); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4157.75%450.00%
alexey khoroshilovalexey khoroshilov1926.76%112.50%
hans verkuilhans verkuil912.68%225.00%
hans de goedehans de goede22.82%112.50%
Total71100.00%8100.00%


static int dev_close(struct file *file) { struct gspca_dev *gspca_dev = video_drvdata(file); PDEBUG(D_STREAM, "[%s] close", current->comm); /* Needed for gspca_stream_off, always lock before queue_lock! */ if (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) { mutex_unlock(&gspca_dev->usb_lock); return -ERESTARTSYS; } /* if the file did the capture, free the streaming resources */ if (gspca_dev->capt_file == file) { if (gspca_dev->streaming) gspca_stream_off(gspca_dev); frame_free(gspca_dev); } module_put(gspca_dev->module); mutex_unlock(&gspca_dev->queue_lock); mutex_unlock(&gspca_dev->usb_lock); PDEBUG(D_STREAM, "close done"); return v4l2_fh_release(file); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine8261.19%866.67%
hans verkuilhans verkuil3626.87%216.67%
hans de goedehans de goede1611.94%216.67%
Total134100.00%12100.00%


static int vidioc_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { struct gspca_dev *gspca_dev = video_drvdata(file); strlcpy((char *) cap->driver, gspca_dev->sd_desc->name, sizeof cap->driver); if (gspca_dev->dev->product != NULL) { strlcpy((char *) cap->card, gspca_dev->dev->product, sizeof cap->card); } else { snprintf((char *) 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)); } usb_make_path(gspca_dev->dev, (char *) cap->bus_info, sizeof(cap->bus_info)); cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE; cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hans de goedehans de goede13376.44%133.33%
jean-francois moinejean-francois moine3721.26%133.33%
hans verkuilhans verkuil42.30%133.33%
Total174100.00%3100.00%


static int vidioc_enum_input(struct file *file, void *priv, struct v4l2_input *input) { struct gspca_dev *gspca_dev = video_drvdata(file); if (input->index != 0) return -EINVAL; input->type = V4L2_INPUT_TYPE_CAMERA; input->status = gspca_dev->cam.input_flags; strlcpy(input->name, gspca_dev->sd_desc->name, sizeof input->name); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine6481.01%125.00%
adam bakeradam baker1012.66%125.00%
hans verkuilhans verkuil45.06%125.00%
hans de goedehans de goede11.27%125.00%
Total79100.00%4100.00%


static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) { *i = 0; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine28100.00%1100.00%
Total28100.00%1100.00%


static int vidioc_s_input(struct file *file, void *priv, unsigned int i) { if (i > 0) return -EINVAL; return (0); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine34100.00%1100.00%
Total34100.00%1100.00%


static int vidioc_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *rb) { struct gspca_dev *gspca_dev = video_drvdata(file); int i, ret = 0, streaming; i = rb->memory; /* (avoid compilation warning) */ switch (i) { case GSPCA_MEMORY_READ: /* (internal call) */ case V4L2_MEMORY_MMAP: case V4L2_MEMORY_USERPTR: break; default: return -EINVAL; } if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (gspca_dev->memory != GSPCA_MEMORY_NO && gspca_dev->memory != GSPCA_MEMORY_READ && gspca_dev->memory != rb->memory) { ret = -EBUSY; goto out; } /* only one file may do the capture */ if (gspca_dev->capt_file != NULL && gspca_dev->capt_file != file) { ret = -EBUSY; goto out; } /* if allocated, the buffers must not be mapped */ for (i = 0; i < gspca_dev->nframes; i++) { if (gspca_dev->frame[i].vma_use_count) { ret = -EBUSY; goto out; } } /* stop streaming */ streaming = gspca_dev->streaming; if (streaming) { gspca_stream_off(gspca_dev); /* Don't restart the stream when switching from read * to mmap mode */ if (gspca_dev->memory == GSPCA_MEMORY_READ) streaming = 0; } /* free the previous allocated buffers, if any */ if (gspca_dev->nframes != 0) frame_free(gspca_dev); if (rb->count == 0) /* unrequest */ goto out; ret = frame_alloc(gspca_dev, file, rb->memory, rb->count); if (ret == 0) { rb->count = gspca_dev->nframes; if (streaming) ret = gspca_init_transfer(gspca_dev); } out: mutex_unlock(&gspca_dev->queue_lock); PDEBUG(D_STREAM, "reqbufs st:%d c:%d", ret, rb->count); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine27389.51%866.67%
hans de goedehans de goede289.18%325.00%
hans verkuilhans verkuil41.31%18.33%
Total305100.00%12100.00%


static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; if (v4l2_buf->index >= gspca_dev->nframes) return -EINVAL; frame = &gspca_dev->frame[v4l2_buf->index]; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine7494.87%150.00%
hans verkuilhans verkuil45.13%150.00%
Total78100.00%2100.00%


static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type buf_type) { struct gspca_dev *gspca_dev = video_drvdata(file); int ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; /* check the capture file */ if (gspca_dev->capt_file != file) { ret = -EBUSY; goto out; } if (gspca_dev->nframes == 0 || !(gspca_dev->frame[0].v4l2_buf.flags & V4L2_BUF_FLAG_QUEUED)) { ret = -EINVAL; goto out; } if (!gspca_dev->streaming) { ret = gspca_init_transfer(gspca_dev); if (ret < 0) goto out; } PDEBUG_MODE(gspca_dev, D_STREAM, "stream on OK", gspca_dev->pixfmt.pixelformat, gspca_dev->pixfmt.width, gspca_dev->pixfmt.height); ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine15687.64%342.86%
hans de goedehans de goede84.49%114.29%
ondrej zaryondrej zary63.37%114.29%
hans verkuilhans verkuil42.25%114.29%
theodore kilgoretheodore kilgore42.25%114.29%
Total178100.00%7100.00%


static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type buf_type) { struct gspca_dev *gspca_dev = video_drvdata(file); int i, ret; if (buf_type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (!gspca_dev->streaming) { ret = 0; goto out; } /* check the capture file */ if (gspca_dev->capt_file != file) { ret = -EBUSY; goto out; } /* stop streaming */ gspca_stream_off(gspca_dev); /* In case another thread is waiting in dqbuf */ wake_up_interruptible(&gspca_dev->wq); /* empty the transfer queues */ for (i = 0; i < gspca_dev->nframes; i++) gspca_dev->frame[i].v4l2_buf.flags &= ~BUF_ALL_FLAGS; atomic_set(&gspca_dev->fr_q, 0); atomic_set(&gspca_dev->fr_i, 0); gspca_dev->fr_o = 0; ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine11060.77%550.00%
hans de goedehans de goede6737.02%440.00%
hans verkuilhans verkuil42.21%110.00%
Total181100.00%10100.00%


static int vidioc_g_jpegcomp(struct file *file, void *priv, struct v4l2_jpegcompression *jpegcomp) { struct gspca_dev *gspca_dev = video_drvdata(file); gspca_dev->usb_err = 0; return gspca_dev->sd_desc->get_jcomp(gspca_dev, jpegcomp); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4389.58%250.00%
hans verkuilhans verkuil510.42%250.00%
Total48100.00%4100.00%


static int vidioc_s_jpegcomp(struct file *file, void *priv, const struct v4l2_jpegcompression *jpegcomp) { struct gspca_dev *gspca_dev = video_drvdata(file); gspca_dev->usb_err = 0; return gspca_dev->sd_desc->set_jcomp(gspca_dev, jpegcomp); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4387.76%240.00%
hans verkuilhans verkuil612.24%360.00%
Total49100.00%5100.00%


static int vidioc_g_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { struct gspca_dev *gspca_dev = video_drvdata(filp); parm->parm.capture.readbuffers = gspca_dev->nbufread; if (gspca_dev->sd_desc->get_streamparm) { gspca_dev->usb_err = 0; gspca_dev->sd_desc->get_streamparm(gspca_dev, parm); return gspca_dev->usb_err; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4963.64%240.00%
jim parisjim paris2329.87%120.00%
hans verkuilhans verkuil56.49%240.00%
Total77100.00%5100.00%


static int vidioc_s_parm(struct file *filp, void *priv, struct v4l2_streamparm *parm) { struct gspca_dev *gspca_dev = video_drvdata(filp); unsigned int n; n = parm->parm.capture.readbuffers; if (n == 0 || n >= GSPCA_MAX_FRAMES) parm->parm.capture.readbuffers = gspca_dev->nbufread; else gspca_dev->nbufread = n; if (gspca_dev->sd_desc->set_streamparm) { gspca_dev->usb_err = 0; gspca_dev->sd_desc->set_streamparm(gspca_dev, parm); return gspca_dev->usb_err; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine7973.15%342.86%
jim parisjim paris2321.30%114.29%
hans verkuilhans verkuil54.63%228.57%
hans de goedehans de goede10.93%114.29%
Total108100.00%7100.00%


static int dev_mmap(struct file *file, struct vm_area_struct *vma) { struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; struct page *page; unsigned long addr, start, size; int i, ret; start = vma->vm_start; size = vma->vm_end - vma->vm_start; PDEBUG(D_STREAM, "mmap start:%08x size:%d", (int) start, (int) size); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; if (gspca_dev->capt_file != file) { ret = -EINVAL; goto out; } frame = NULL; for (i = 0; i < gspca_dev->nframes; ++i) { if (gspca_dev->frame[i].v4l2_buf.memory != V4L2_MEMORY_MMAP) { PDEBUG(D_STREAM, "mmap bad memory type"); break; } if ((gspca_dev->frame[i].v4l2_buf.m.offset >> PAGE_SHIFT) == vma->vm_pgoff) { frame = &gspca_dev->frame[i]; break; } } if (frame == NULL) { PDEBUG(D_STREAM, "mmap no frame buffer found"); ret = -EINVAL; goto out; } if (size != frame->v4l2_buf.length) { PDEBUG(D_STREAM, "mmap bad size"); ret = -EINVAL; goto out; } /* * - VM_IO marks the area as being a 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) frame->data; while (size > 0) { page = vmalloc_to_page((void *) addr); ret = vm_insert_page(vma, start, page); if (ret < 0) goto out; start += PAGE_SIZE; addr += PAGE_SIZE; size -= PAGE_SIZE; } vma->vm_ops = &gspca_vm_ops; vma->vm_private_data = frame; gspca_vm_open(vma); ret = 0; out: mutex_unlock(&gspca_dev->queue_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine33395.42%240.00%
hans de goedehans de goede82.29%120.00%
harvey harrisonharvey harrison51.43%120.00%
hans verkuilhans verkuil30.86%120.00%
Total349100.00%5100.00%


static int frame_ready_nolock(struct gspca_dev *gspca_dev, struct file *file, enum v4l2_memory memory) { if (!gspca_dev->present) return -ENODEV; if (gspca_dev->capt_file != file || gspca_dev->memory != memory || !gspca_dev->streaming) return -EINVAL; /* check if a frame is ready */ return gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i); }

Contributors

PersonTokensPropCommitsCommitProp
hans de goedehans de goede3957.35%350.00%
jean-francois moinejean-francois moine2942.65%350.00%
Total68100.00%6100.00%


static int frame_ready(struct gspca_dev *gspca_dev, struct file *file, enum v4l2_memory memory) { int ret; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; ret = frame_ready_nolock(gspca_dev, file, memory); mutex_unlock(&gspca_dev->queue_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
hans de goedehans de goede4372.88%233.33%
jean-francois moinejean-francois moine1627.12%466.67%
Total59100.00%6100.00%

/* * dequeue a video buffer * * If nonblock_ing is false, block until a buffer is available. */
static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, j, ret; PDEBUG(D_FRAM, "dqbuf"); if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; for (;;) { ret = frame_ready_nolock(gspca_dev, file, v4l2_buf->memory); if (ret < 0) goto out; if (ret > 0) break; mutex_unlock(&gspca_dev->queue_lock); if (file->f_flags & O_NONBLOCK) return -EAGAIN; /* wait till a frame is ready */ ret = wait_event_interruptible_timeout(gspca_dev->wq, frame_ready(gspca_dev, file, v4l2_buf->memory), msecs_to_jiffies(3000)); if (ret < 0) return ret; if (ret == 0) return -EIO; if (mutex_lock_interruptible(&gspca_dev->queue_lock)) return -ERESTARTSYS; } i = gspca_dev->fr_o; j = gspca_dev->fr_queue[i]; frame = &gspca_dev->frame[j]; gspca_dev->fr_o = (i + 1) % GSPCA_MAX_FRAMES; frame->v4l2_buf.flags &= ~V4L2_BUF_FLAG_DONE; memcpy(v4l2_buf, &frame->v4l2_buf, sizeof *v4l2_buf); PDEBUG(D_FRAM, "dqbuf %d", j); ret = 0; if (gspca_dev->memory == V4L2_MEMORY_USERPTR) { if (copy_to_user((__u8 __user *) frame->v4l2_buf.m.userptr, frame->data, frame->v4l2_buf.bytesused)) { PERR("dqbuf cp to user failed"); ret = -EFAULT; } } out: mutex_unlock(&gspca_dev->queue_lock); if (ret == 0 && gspca_dev->sd_desc->dq_callback) { mutex_lock(&gspca_dev->usb_lock); gspca_dev->usb_err = 0; if (gspca_dev->present) gspca_dev->sd_desc->dq_callback(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); } return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine15341.92%545.45%
hans de goedehans de goede13336.44%218.18%
hans verkuilhans verkuil7821.37%327.27%
theodore kilgoretheodore kilgore10.27%19.09%
Total365100.00%11100.00%

/* * queue a video buffer * * Attempting to queue a buffer that has already been * queued will return -EINVAL. */
static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *v4l2_buf) { struct gspca_dev *gspca_dev = video_drvdata(file); struct gspca_frame *frame; int i, index, ret; PDEBUG(D_FRAM, "qbuf %d", v4l2_buf->index); 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 = atomic_read(&gspca_dev->fr_q); gspca_dev->fr_queue[i] = index; atomic_set(&gspca_dev->fr_q, (i + 1) % GSPCA_MAX_FRAMES); 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; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine27798.58%480.00%
hans verkuilhans verkuil41.42%120.00%
Total281100.00%5100.00%

/* * 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 (mutex_lock_interruptible(&gspca_dev->usb_lock)) return -ERESTARTSYS; 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); goto out; } 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); goto out; } } } /* start streaming */ ret = vidioc_streamon(file, gspca_dev, V4L2_BUF_TYPE_VIDEO_CAPTURE); if (ret != 0) PDEBUG(D_STREAM, "read streamon err %d", ret); out: mutex_unlock(&gspca_dev->usb_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine21187.55%375.00%
hans verkuilhans verkuil3012.45%125.00%
Total241100.00%4100.00%


static unsigned int dev_poll(struct file *file, poll_table *wait) { struct gspca_dev *gspca_dev = video_drvdata(file); unsigned long req_events = poll_requested_events(wait); int ret = 0; PDEBUG(D_FRAM, "poll"); if (req_events & POLLPRI) ret |= v4l2_ctrl_poll(file, wait); if (req_events & (POLLIN | POLLRDNORM)) { /* if reqbufs is not done, the user would use read() */ if (gspca_dev->memory == GSPCA_MEMORY_NO) { if (read_alloc(gspca_dev, file) != 0) { ret |= POLLERR; goto out; } } poll_wait(file, &gspca_dev->wq, wait); /* check if an image has been received */ if (mutex_lock_interruptible(&gspca_dev->queue_lock) != 0) { ret |= POLLERR; goto out; } if (gspca_dev->fr_o != atomic_read(&gspca_dev->fr_i)) ret |= POLLIN | POLLRDNORM; mutex_unlock(&gspca_dev->queue_lock); } out: if (!gspca_dev->present) ret |= POLLHUP; return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine10959.56%562.50%
hans de goedehans de goede7138.80%225.00%
hans verkuilhans verkuil31.64%112.50%
Total183100.00%8100.00%


static ssize_t dev_read(struct file *file, char __user *data, size_t count, loff_t *ppos) { struct gspca_dev *gspca_dev = video_drvdata(file); 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->memory == GSPCA_MEMORY_NO) { /* first time ? */ ret = read_alloc(gspca_dev, file); if (ret != 0) return ret; } /* get a frame */ v4l2_get_timestamp(&timestamp); 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) { PERR("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; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine31396.01%545.45%
hans verkuilhans verkuil30.92%19.09%
arnd bergmannarnd bergmann30.92%19.09%
hans de goedehans de goede30.92%19.09%
mauro carvalho chehabmauro carvalho chehab20.61%19.09%
theodore kilgoretheodore kilgore10.31%19.09%
frank zagofrank zago10.31%19.09%
Total326100.00%11100.00%

static struct v4l2_file_operations dev_fops = { .owner = THIS_MODULE, .open = dev_open, .release = dev_close, .read = dev_read, .mmap = dev_mmap, .unlocked_ioctl = video_ioctl2, .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_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_enum_framesizes = vidioc_enum_framesizes, .vidioc_enum_frameintervals = vidioc_enum_frameintervals, #ifdef CONFIG_VIDEO_ADV_DEBUG .vidioc_g_chip_info = vidioc_g_chip_info, .vidioc_g_register = vidioc_g_register, .vidioc_s_register = vidioc_s_register, #endif .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, .vidioc_unsubscribe_event = v4l2_event_unsubscribe, }; static const struct video_device gspca_template = { .name = "gspca main driver", .fops = &dev_fops, .ioctl_ops = &dev_ioctl_ops, .release = video_device_release_empty, /* We use v4l2_dev.release */ }; /* * 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_probe2(struct usb_interface *intf, const struct usb_device_id *id, const struct sd_desc *sd_desc, int dev_size, struct module *module) { struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); int ret; pr_info("%s-" GSPCA_VERSION " probing %04x:%04x\n", sd_desc->name, id->idVendor, id->idProduct); /* create the device */ if (dev_size < sizeof *gspca_dev) dev_size = sizeof *gspca_dev; gspca_dev = kzalloc(dev_size, GFP_KERNEL); if (!gspca_dev) { pr_err("couldn't kzalloc gspca struct\n"); return -ENOMEM; } gspca_dev->usb_buf = kmalloc(USB_BUF_SZ, GFP_KERNEL); if (!gspca_dev->usb_buf) { pr_err("out of memory\n"); ret = -ENOMEM; goto out; } gspca_dev->dev = dev; gspca_dev->iface = intf->cur_altsetting->desc.bInterfaceNumber; gspca_dev->xfer_ep = -1; /* check if any audio device */ if (dev->actconfig->desc.bNumInterfaces != 1) { int i; struct usb_interface *intf2; for (i = 0; i < dev->actconfig->desc.bNumInterfaces; i++) { intf2 = dev->actconfig->interface[i]; if (intf2 != NULL && intf2->altsetting != NULL && intf2->altsetting->desc.bInterfaceClass == USB_CLASS_AUDIO) { gspca_dev->audio = 1; break; } } } gspca_dev->v4l2_dev.release = gspca_release; ret = v4l2_device_register(&intf->dev, &gspca_dev->v4l2_dev); if (ret) goto out; gspca_dev->sd_desc = sd_desc; gspca_dev->nbufread = 2; gspca_dev->empty_packet = -1; /* don't check the empty packets */ gspca_dev->vdev = gspca_template; gspca_dev->vdev.v4l2_dev = &gspca_dev->v4l2_dev; video_set_drvdata(&gspca_dev->vdev, gspca_dev); gspca_dev->module = module; gspca_dev->present = 1; mutex_init(&gspca_dev->usb_lock); gspca_dev->vdev.lock = &gspca_dev->usb_lock; mutex_init(&gspca_dev->queue_lock); init_waitqueue_head(&gspca_dev->wq); /* configure the subdriver and initialize the USB device */ ret = sd_desc->config(gspca_dev, id); if (ret < 0) goto out; ret = sd_desc->init(gspca_dev); if (ret < 0) goto out; if (sd_desc->init_controls) ret = sd_desc->init_controls(gspca_dev); if (ret < 0) goto out; gspca_set_default_mode(gspca_dev); ret = gspca_input_connect(gspca_dev); if (ret) goto out; /* * Don't take usb_lock for these ioctls. This improves latency if * usb_lock is taken for a long time, e.g. when changing a control * value, and a new frame is ready to be dequeued. */ v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_DQBUF); v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QBUF); v4l2_disable_ioctl_locking(&gspca_dev->vdev, VIDIOC_QUERYBUF); #ifdef CONFIG_VIDEO_ADV_DEBUG if (!gspca_dev->sd_desc->get_register) v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_G_REGISTER); if (!gspca_dev->sd_desc->set_register) v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_DBG_S_REGISTER); #endif if (!gspca_dev->sd_desc->get_jcomp) v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_G_JPEGCOMP); if (!gspca_dev->sd_desc->set_jcomp) v4l2_disable_ioctl(&gspca_dev->vdev, VIDIOC_S_JPEGCOMP); /* init video stuff */ ret = video_register_device(&gspca_dev->vdev, VFL_TYPE_GRABBER, -1); if (ret < 0) { pr_err("video_register_device err %d\n", ret); goto out; } usb_set_intfdata(intf, gspca_dev); PDEBUG(D_PROBE, "%s created", video_device_node_name(&gspca_dev->vdev)); gspca_input_create_urb(gspca_dev); return 0; out: #if IS_ENABLED(CONFIG_INPUT) if (gspca_dev->input_dev) input_unregister_device(gspca_dev->input_dev); #endif v4l2_ctrl_handler_free(gspca_dev->vdev.ctrl_handler); kfree(gspca_dev->usb_buf); kfree(gspca_dev); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine36553.68%1344.83%
hans verkuilhans verkuil24736.32%620.69%
hans de goedehans de goede314.56%310.34%
marton nemethmarton nemeth111.62%13.45%
antonio ospiteantonio ospite71.03%13.45%
joe perchesjoe perches60.88%13.45%
laurent pinchartlaurent pinchart50.74%13.45%
randy dunlaprandy dunlap40.59%13.45%
erik andrenerik andren30.44%13.45%
peter senna tschudinpeter senna tschudin10.15%13.45%
Total680100.00%29100.00%

EXPORT_SYMBOL(gspca_dev_probe2); /* same function as the previous one, but check the interface */
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_device *dev = interface_to_usbdev(intf); /* we don't handle multi-config cameras */ if (dev->descriptor.bNumConfigurations != 1) { pr_err("%04x:%04x too many config\n", id->idVendor, id->idProduct); return -ENODEV; } /* the USB video interface must be the first one */ if (dev->actconfig->desc.bNumInterfaces != 1 && intf->cur_altsetting->desc.bInterfaceNumber != 0) return -ENODEV; return gspca_dev_probe2(intf, id, sd_desc, dev_size, module); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine10897.30%133.33%
joe perchesjoe perches21.80%133.33%
hans de goedehans de goede10.90%133.33%
Total111100.00%3100.00%

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); #if IS_ENABLED(CONFIG_INPUT) struct input_dev *input_dev; #endif PDEBUG(D_PROBE, "%s disconnect", video_device_node_name(&gspca_dev->vdev)); mutex_lock(&gspca_dev->usb_lock); gspca_dev->present = 0; destroy_urbs(gspca_dev); #if IS_ENABLED(CONFIG_INPUT) gspca_input_destroy_urb(gspca_dev); input_dev = gspca_dev->input_dev; if (input_dev) { gspca_dev->input_dev = NULL; input_unregister_device(input_dev); } #endif /* Free subdriver's streaming resources / stop sd workqueue(s) */ if (gspca_dev->sd_desc->stop0 && gspca_dev->streaming) gspca_dev->sd_desc->stop0(gspca_dev); gspca_dev->streaming = 0; gspca_dev->dev = NULL; wake_up_interruptible(&gspca_dev->wq); v4l2_device_disconnect(&gspca_dev->v4l2_dev); video_unregister_device(&gspca_dev->vdev); mutex_unlock(&gspca_dev->usb_lock); /* (this will call gspca_release() immediately or on last close) */ v4l2_device_put(&gspca_dev->v4l2_dev); }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine5430.34%633.33%
hans de goedehans de goede4726.40%316.67%
marton nemethmarton nemeth3318.54%15.56%
hans verkuilhans verkuil179.55%211.11%
randy dunlaprandy dunlap84.49%15.56%
adam bakeradam baker84.49%15.56%
laurent pinchartlaurent pinchart52.81%15.56%
frank zagofrank zago31.69%15.56%
peter senna tschudinpeter senna tschudin21.12%15.56%
lucas de marchilucas de marchi10.56%15.56%
Total178100.00%18100.00%

EXPORT_SYMBOL(gspca_disconnect); #ifdef CONFIG_PM
int gspca_suspend(struct usb_interface *intf, pm_message_t message) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); gspca_input_destroy_urb(gspca_dev); if (!gspca_dev->streaming) return 0; mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 1; /* avoid urb error messages */ gspca_dev->usb_err = 0; 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); mutex_unlock(&gspca_dev->usb_lock); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine8776.32%233.33%
hans verkuilhans verkuil1614.04%233.33%
hans de goedehans de goede119.65%233.33%
Total114100.00%6100.00%

EXPORT_SYMBOL(gspca_suspend);
int gspca_resume(struct usb_interface *intf) { struct gspca_dev *gspca_dev = usb_get_intfdata(intf); int streaming, ret = 0; mutex_lock(&gspca_dev->usb_lock); gspca_dev->frozen = 0; gspca_dev->usb_err = 0; gspca_dev->sd_desc->init(gspca_dev); /* * Most subdrivers send all ctrl values on sd_start and thus * only write to the device registers on s_ctrl when streaming -> * Clear streaming to avoid setting all ctrls twice. */ streaming = gspca_dev->streaming; gspca_dev->streaming = 0; if (streaming) ret = gspca_init_transfer(gspca_dev); else gspca_input_create_urb(gspca_dev); mutex_unlock(&gspca_dev->usb_lock); return ret; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine4344.33%337.50%
hans verkuilhans verkuil4243.30%337.50%
hans de goedehans de goede1212.37%225.00%
Total97100.00%8100.00%

EXPORT_SYMBOL(gspca_resume); #endif /* -- module insert / remove -- */
static int __init gspca_init(void) { pr_info("v" GSPCA_VERSION " registered\n"); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine1684.21%250.00%
joe perchesjoe perches210.53%125.00%
mauro carvalho chehabmauro carvalho chehab15.26%125.00%
Total19100.00%4100.00%


static void __exit gspca_exit(void) { }

Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine8100.00%2100.00%
Total8100.00%2100.00%

module_init(gspca_init); module_exit(gspca_exit); module_param_named(debug, gspca_debug, int, 0644); MODULE_PARM_DESC(debug, "1:probe 2:config 3:stream 4:frame 5:packet 6:usbi 7:usbo");

Overall Contributors

PersonTokensPropCommitsCommitProp
jean-francois moinejean-francois moine726467.38%8548.30%
hans de goedehans de goede10509.74%3922.16%
marton nemethmarton nemeth8968.31%10.57%
hans verkuilhans verkuil7276.74%158.52%
antonio ospiteantonio ospite2752.55%42.27%
brian johnsonbrian johnson1161.08%10.57%
ondrej zaryondrej zary1020.95%21.14%
randy dunlaprandy dunlap590.55%10.57%
jim parisjim paris460.43%10.57%
frank zagofrank zago450.42%31.70%
joe perchesjoe perches380.35%10.57%
mauro carvalho chehabmauro carvalho chehab350.32%52.84%
theodore kilgoretheodore kilgore290.27%10.57%
alexey khoroshilovalexey khoroshilov190.18%10.57%
adam bakeradam baker180.17%21.14%
laurent pinchartlaurent pinchart120.11%21.14%
patrice chotardpatrice chotard80.07%10.57%
jason wangjason wang60.06%10.57%
arnd bergmannarnd bergmann60.06%10.57%
harvey harrisonharvey harrison60.06%10.57%
erik andrenerik andren60.06%21.14%
peter senna tschudinpeter senna tschudin50.05%10.57%
daniel mackdaniel mack50.05%10.57%
julia lawalljulia lawall30.03%10.57%
lucas de marchilucas de marchi20.02%10.57%
paulo assispaulo assis10.01%10.57%
adrian bunkadrian bunk10.01%10.57%
Total10780100.00%176100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}