// SPDX-License-Identifier: GPL-2.0-or-later /* * 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>
*/
/* 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);
/* 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
ret = urb->status; switch (ret) { case 0: if (gspca_dev->sd_desc->int_pkt_scan(gspca_dev,
urb->transfer_buffer, urb->actual_length) < 0) {
gspca_err(gspca_dev, "Unknown packet received\n");
} 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;
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)) {
/* * fill a video frame from an URB and resubmit
*/ staticvoid 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
gspca_err(gspca_dev, "urb status: %d\n", 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) {
gspca_dbg(gspca_dev, D_PACK, "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 */
gspca_dbg(gspca_dev, D_PACK, "packet [%d] o:%d l:%d\n",
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: if (!gspca_dev->streaming) return; /* resubmit the URB */
st = usb_submit_urb(urb, GFP_ATOMIC); if (st < 0)
pr_err("usb_submit_urb() ret %d\n", st);
}
/* * ISOC message interrupt from the USB device * * Analyse each packet and call the subdriver for copy to the frame buffer.
*/ staticvoid isoc_irq(struct urb *urb)
{ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context;
gspca_dbg(gspca_dev, D_PACK, "isoc irq\n"); if (!gspca_dev->streaming) return;
fill_frame(gspca_dev, urb);
}
/* * bulk message interrupt from the USB device
*/ staticvoid bulk_irq(struct urb *urb)
{ struct gspca_dev *gspca_dev = (struct gspca_dev *) urb->context; int st;
gspca_dbg(gspca_dev, D_PACK, "bulk irq\n"); 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
gspca_err(gspca_dev, "urb status: %d\n", urb->status);
urb->status = 0; goto resubmit;
}
resubmit: if (!gspca_dev->streaming) return; /* 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);
}
}
/* * 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_buffer *buf; unsignedlong flags;
/* Killing all URBs guarantee that no URB completion * handler is running. Therefore, there shouldn't * be anyone trying to access gspca_dev->urb[i]
*/ for (i = 0; i < MAX_NURBS; i++)
usb_kill_urb(gspca_dev->urb[i]);
gspca_dbg(gspca_dev, D_STREAM, "releasing urbs\n"); for (i = 0; i < MAX_NURBS; i++) {
urb = gspca_dev->urb[i]; if (!urb) continue;
gspca_dev->urb[i] = NULL;
usb_free_coherent(gspca_dev->dev,
urb->transfer_buffer_length,
urb->transfer_buffer,
urb->transfer_dma);
usb_free_urb(urb);
}
}
staticint 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;
}
/* * 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.
*/ staticstruct 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;
}
/* 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;
/* * build the table of the endpoints * and compute the minimum bandwidth for the image transfer
*/ staticint 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;
gspca_dbg(gspca_dev, D_STREAM, "alt %d bandwidth %d\n",
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) {
gspca_dbg(gspca_dev, D_STREAM, "dev has usb audio, skipping highest alt\n");
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;
}
/* * create the URBs for image transfer
*/ staticint 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);
/* Note: both the queue and the usb locks should be held when calling this */ staticvoid gspca_stream_off(struct gspca_dev *gspca_dev)
{
gspca_dev->streaming = false;
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); if (gspca_dev->present)
gspca_input_create_urb(gspca_dev); if (gspca_dev->sd_desc->stop0)
gspca_dev->sd_desc->stop0(gspca_dev);
gspca_dbg(gspca_dev, D_STREAM, "stream off OK\n");
}
/* * start the USB transfer
*/ staticint 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;
/* 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) {
gspca_dbg(gspca_dev, D_STREAM, "init transfer alt %d\n",
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;
}
v4l2_ctrl_handler_setup(gspca_dev->vdev.ctrl_handler);
gspca_dev->streaming = true;
/* 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:
gspca_err(gspca_dev, "alt %d - bandwidth not wide enough, trying again\n",
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;
}
staticvoid 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);
}
staticint wxh_to_mode(struct gspca_dev *gspca_dev, int width, int height, u32 pixelformat)
{ int i;
for (i = 0; i < gspca_dev->cam.nmodes; i++) { if (width == gspca_dev->cam.cam_mode[i].width
&& height == gspca_dev->cam.cam_mode[i].height
&& pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) return i;
} return -EINVAL;
}
staticint wxh_to_nearest_mode(struct gspca_dev *gspca_dev, int width, int height, u32 pixelformat)
{ 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
&& pixelformat == gspca_dev->cam.cam_mode[i].pixelformat) return 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
*/ staticint 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;
}
/* give an index to each format */
index = 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 */
if (try_fmt_vid_cap(gspca_dev, fmt) < 0) return -EINVAL; /* some drivers use priv internally, so keep the original value */
fmt->fmt.pix.priv = priv; return 0;
}
if (vb2_is_busy(&gspca_dev->queue)) return -EBUSY;
mode = try_fmt_vid_cap(gspca_dev, fmt); if (mode < 0) return -EINVAL;
gspca_dev->curr_mode = mode; 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[mode]; /* some drivers use priv internally, so keep the original value */
fmt->fmt.pix.priv = priv; return 0;
}
staticconststruct 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, conststruct usb_device_id *id, conststruct sd_desc *sd_desc, int dev_size, struct module *module)
{ struct gspca_dev *gspca_dev; struct usb_device *dev = interface_to_usbdev(intf); struct vb2_queue *q; int ret;
/* 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;
#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_VIDEO,
-1); if (ret < 0) {
pr_err("video_register_device err %d\n", ret); goto out;
}
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);
v4l2_device_unregister(&gspca_dev->v4l2_dev); if (sd_desc->probe_error)
sd_desc->probe_error(gspca_dev);
kfree(gspca_dev->usb_buf);
kfree(gspca_dev); return ret;
}
EXPORT_SYMBOL(gspca_dev_probe2);
/* same function as the previous one, but check the interface */ int gspca_dev_probe(struct usb_interface *intf, conststruct usb_device_id *id, conststruct 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;
/* * 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
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 = vb2_start_streaming_called(&gspca_dev->queue); if (streaming)
ret = gspca_init_transfer(gspca_dev); else
gspca_input_create_urb(gspca_dev);
mutex_unlock(&gspca_dev->usb_lock);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.