commit afb1b4a19c0aeb4d21352bc0a7ddb8541901d0c5
parent f119e7474d19c64c139bbce59b0d6d6fdd08ca88
Author: alex <alex@022568fa-442e-4ef8-a3e8-54dcafdb011a>
Date: Wed, 16 Jan 2008 23:49:39 +0000
* Remove old usbcam.c file - not used.
git-svn-id: http://svn.mediati.org/svn/r5u870/trunk@36 022568fa-442e-4ef8-a3e8-54dcafdb011a
Diffstat:
M | ChangeLog | | | 4 | ++++ |
D | usbcam/usbcam.c | | | 3477 | ------------------------------------------------------------------------------- |
2 files changed, 4 insertions(+), 3477 deletions(-)
diff --git a/ChangeLog b/ChangeLog
@@ -1,4 +1,8 @@
2008-01-17 Alexander Hixon <hixon.alexander@mediati.org>
+ * usbcam/usbcam.c: Remove unnecessary file - accidentially kept post-
+ merge.
+
+2008-01-17 Alexander Hixon <hixon.alexander@mediati.org>
* r5u870.c, r5u870_1839.fw, Makefile: Added support for 1839 UVC
camera. Also not sure of which UVC camera controls this camera
completely supports, however, it appears to work. I also don't own
diff --git a/usbcam/usbcam.c b/usbcam/usbcam.c
@@ -1,3477 +0,0 @@
-/*
- * USBCAM abstraction library for USB webcam drivers
- * Version 0.10.2
- *
- * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu>
- * Copyright (c) 2008 Alexander Hixon <hixon.alexander@mediati.org>
- *
- * 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, 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- */
-
-/*
- * TODO LIST:
- * - Add debug tracing to more ioctl paths
- * - Provide a cleaner mechanism for alerting minidrvers of URB
- * underflows in the isostream component.
- */
-
-#define CONFIG_USBCAM_DEBUG
-
-#include <linux/kernel.h>
-#include <linux/kthread.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/list.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <linux/smp_lock.h>
-#include <linux/vmalloc.h>
-#include <linux/init.h>
-#include <linux/spinlock.h>
-#include <linux/videodev.h>
-
-#include "usbcam.h"
-
-/* If building integrated with a minidriver, don't export symbols */
-#undef EXPORT_SYMBOL
-#define EXPORT_SYMBOL(X)
-
-#define assert usbcam_assert
-
-#define usbcam_drvname(MDP) ((MDP)->um_owner->name)
-
-#if defined(CONFIG_USBCAM_DEBUG)
-#define usbcam_dbgm(MD, SUBSYS, FMT, ARG...) do { \
- if ((MD)->um_debug && \
- *(MD)->um_debug & (1UL << USBCAM_DBG_ ## SUBSYS)) \
- printk(KERN_INFO "%s: " FMT "\n", \
- (MD)->um_modname, ## ARG); \
-} while (0)
-#else
-#define usbcam_dbgm(MD, SUBSYS, FMT, ARG...)
-#endif /* defined(CONFIG_USBCAM_DEBUG) */
-
-#define usbcam_minidrv_op_present(UDP, CB) \
- ((UDP)->ud_minidrv->um_ops->CB ? 1 : 0)
-#define usbcam_minidrv_op(UDP, CB, ARGS...) \
- ((UDP)->ud_minidrv->um_ops->CB((UDP), ## ARGS))
-
-
-/*
- * Private data structure definitions
- */
-
-/*
- * This structure represents a registered minidriver
- */
-struct usbcam_minidrv {
- struct kref um_kref;
- struct module *um_owner;
- const char *um_modname;
- int *um_debug;
- int um_version;
- int um_dev_count;
- struct list_head um_dev_list;
- int um_dev_privsize;
- struct usb_driver um_usbdrv;
- struct mutex um_lock;
- const struct usbcam_dev_ops *um_ops;
- struct video_device um_videodev_template;
- struct file_operations um_v4l_fops;
- const int *um_video_nr_array;
- int um_video_nr_array_len;
-};
-
-/*
- * The frame structure is generally managed by the video-buf module
- * and represents some chunk of memory that the video4linux client
- * requested as a frame buffer. It might be vmalloc()'d, or it might
- * be mapped from a user address space. In either case, usbcam
- * guarantees a contiguous kernel mapping accessible to the minidriver.
- *
- * The primary reason to use video-buf in usbcam is for its
- * implementation of buffer mapping methods and "zero-copy" kernel-user
- * data movement. The V4L2 API is quite rich, and it's much easier to
- * use video-buf than to create a private full-featured implementation,
- * and much more desirable to use video-buf than to limp along with a
- * substandard implementation. The video-buf module isn't specifically
- * used for DMA functionality, as most USB devices, with the possible
- * exception of those employing bulk transfers, are unsuitable for
- * direct frame buffer DMA.
- *
- * Minidrivers can access details of the current frame using
- * usbcam_curframe_get(), and can signal completion of the current
- * frame with usbcam_curframe_complete(). It is up to the minidriver
- * to fill in the frame buffer.
- */
-struct usbcam_frame {
- struct videobuf_buffer vbb;
- struct list_head cap_links;
- void *vmap_base;
- void *vmap_sof;
-};
-
-/*
- * This structure represents an open file handle and the frame
- * buffers associated with that client
- */
-struct usbcam_fh {
- struct usbcam_dev *uf_dev;
- int uf_flags;
- struct videobuf_queue uf_vbq;
-};
-
-#define USBCAM_FH_USE_FIXED_FB 0x00000001
-
-
-/*
- * APPBUG: Some applications expect VIDIOCGMBUF to provide a buffer
- * large enough to accommodate whatever image format they choose in the
- * future. We enable fixed size buffer mode from VIDIOCGMBUF, and
- * disable it from VIDIOC_REQBUFS.
- */
-static int fixed_fbsize = 1024 * 1024;
-module_param(fixed_fbsize, int, S_IRUGO|S_IWUSR);
-MODULE_PARM_DESC(fixed_fbsize, "Size in bytes of fixed-length framebuffers");
-
-
-/*
- * Frame capture handling helpers follow
- */
-
-static inline struct usbcam_frame *
-usbcam_capture_curframe(struct usbcam_dev *udp)
-{
- return list_empty(&udp->ud_frame_cap_queue)
- ? NULL
- : list_entry(udp->ud_frame_cap_queue.next,
- struct usbcam_frame, cap_links);
-}
-
-static void usbcam_capture_abortall(struct usbcam_dev *udp)
-{
- struct usbcam_frame *framep;
-
- /* Abort all frames on the capture queue */
- while (1) {
- framep = usbcam_capture_curframe(udp);
- if (!framep)
- break;
- usbcam_dbg(udp, VIDEOBUF, "completing frame %d STATE_ERROR",
- framep->vbb.i);
- list_del_init(&framep->cap_links);
- framep->vbb.state = STATE_ERROR;
- wake_up_all(&framep->vbb.done);
- }
-}
-
-static inline void usbcam_capture_complete_frame(struct usbcam_dev *udp,
- struct usbcam_frame *framep,
- int is_error)
-{
- usbcam_chklock(udp);
- usbcam_dbg(udp, VIDEOBUF, "completing frame %d/%p %s", framep->vbb.i,
- framep, is_error ? "STATE_ERROR" : "STATE_DONE");
- list_del_init(&framep->cap_links);
- framep->vbb.state = is_error ? STATE_ERROR : STATE_DONE;
- wake_up_all(&framep->vbb.done);
-}
-
-static int usbcam_capture_start(struct usbcam_dev *udp)
-{
- int res;
-
- if (udp->ud_capturing) {
- usbcam_warn(udp, "%s: already capturing", __FUNCTION__);
- return 0;
- }
-
- if (list_empty(&udp->ud_frame_cap_queue)) {
- usbcam_warn(udp, "%s: no frames queued to capture",
- __FUNCTION__);
- return -ENOENT;
- }
-
- if (udp->ud_disconnected) {
- /*
- * We can't let any frames through if the device has
- * been disconnected
- */
- usbcam_capture_abortall(udp);
- return -ENODEV;
- }
-
- usbcam_dbg(udp, CAPTURE, "invoking minidriver cap_start");
-
- res = usbcam_minidrv_op(udp, cap_start);
- if (res) {
- usbcam_dbg(udp, CAPTURE,
- "%s: could not start capture for %s: %d",
- __FUNCTION__, usbcam_drvname(udp->ud_minidrv), res);
-
- if (udp->ud_capturing) {
- usbcam_warn(udp,
- "%s: minidriver left ud_capturing set\n",
- __FUNCTION__);
- }
-
- usbcam_capture_abortall(udp);
- return res;
- }
-
- if (!udp->ud_capturing && usbcam_capture_curframe(udp)) {
- usbcam_warn(udp, "%s: minidriver failed to set ud_capturing!",
- __FUNCTION__);
- } else {
- usbcam_dbg(udp, CAPTURE, "minidriver capture started");
- }
-
- return 0;
-}
-
-static void usbcam_capture_stop(struct usbcam_dev *udp)
-{
- if (udp->ud_capturing) {
- usbcam_dbg(udp, CAPTURE, "invoking minidriver cap_stop");
- usbcam_minidrv_op(udp, cap_stop);
-
- if (udp->ud_capturing) {
- usbcam_warn(udp, "%s: minidriver failed to clear "
- "ud_capturing!", __FUNCTION__);
- } else {
- usbcam_dbg(udp, CAPTURE, "minidriver capture stopped");
- }
- }
-}
-
-static void usbcam_capture_stop_nondestructive(struct usbcam_dev *udp)
-{
- /*
- * Only stop capturing if no frames are queued.
- *
- * We allow and encourage the minidriver to continue
- * capturing in the last requested format, and have it
- * stop autonomously when it receives its first data
- * for the next frame but finds no frame available.
- * This expedites the process for situations such as
- * S_FMT which cannot tolerate capture being in progress.
- */
- if (udp->ud_capturing && list_empty(&udp->ud_frame_cap_queue))
- usbcam_capture_stop(udp);
-}
-
-static inline struct videobuf_dmabuf* usbframe_get_dmabuf(struct videobuf_buffer *buf)
-{
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- return buf->dma;
-#else
- return videobuf_to_dma(buf);
-#endif
-}
-
-
-/*
- * External APIs for minidriver access to the frame queue
- */
-
-int usbcam_curframe_get(struct usbcam_dev *udp, struct usbcam_curframe *cf)
-{
- struct usbcam_frame *framep = usbcam_capture_curframe(udp);
- struct videobuf_dmabuf *dma;
-
- usbcam_chklock(udp);
-
- if (!framep || !&framep->vbb)
- return -ENOENT;
-
- dma = usbframe_get_dmabuf(&framep->vbb);
-
- cf->uc_base = (u8 *) (framep->vmap_sof
- ? framep->vmap_sof
- : dma->vmalloc);
- cf->uc_size = framep->vbb.size;
- cf->uc_field = framep->vbb.field;
- memset(&cf->uc_timestamp, 0, sizeof(cf->uc_timestamp));
-
- return 0;
-}
-EXPORT_SYMBOL(usbcam_curframe_get);
-
-void usbcam_curframe_complete_detail(struct usbcam_dev *udp, int is_error,
- struct usbcam_curframe *cf)
-{
- struct usbcam_frame *framep;
-
- usbcam_chklock(udp);
-
- framep = usbcam_capture_curframe(udp);
- if (!framep) {
- usbcam_warn(udp, "%s: no current frame!", __FUNCTION__);
- return;
- }
-
- if (framep->vbb.state != STATE_ACTIVE) {
- usbcam_err(udp, "%s: current frame is in unexpected state %d",
- __FUNCTION__, framep->vbb.state);
- }
-
- if (cf && !is_error) {
- framep->vbb.size = cf->uc_size;
- framep->vbb.field = cf->uc_field;
- framep->vbb.ts = cf->uc_timestamp;
-
- if (framep->vbb.bsize < cf->uc_size) {
- usbcam_warn(udp, "%s: minidriver supplied "
- "excessive size %zu",
- __FUNCTION__, cf->uc_size);
- framep->vbb.size = framep->vbb.bsize;
- }
- }
-
- else if (is_error && usbcam_curframe_testpattern(udp)) {
- /*
- * No test pattern available.
- * Punt: just memset the frame buffer to zero.
- */
- const char tp[1] = { 0 };
- usbcam_curframe_fill(udp, 0, tp, 1, udp->ud_format.sizeimage);
- }
-
- usbcam_capture_complete_frame(udp, framep, 0);
-}
-EXPORT_SYMBOL(usbcam_curframe_complete_detail);
-
-void usbcam_curframe_abortall(struct usbcam_dev *udp)
-{
- usbcam_dbg(udp, CAPTURE, "minidriver aborting all frames");
-
- usbcam_chklock(udp);
-
- if (udp->ud_capturing) {
- usbcam_warn(udp, "%s: minidriver left ud_capturing set",
- __FUNCTION__);
- }
- usbcam_capture_abortall(udp);
-}
-EXPORT_SYMBOL(usbcam_curframe_abortall);
-
-
-/*
- * Test pattern code
- */
-
-void usbcam_curframe_fill(struct usbcam_dev *udp, size_t offset,
- const void *pattern, int patlen, int nrecs)
-{
- struct usbcam_curframe cf;
- size_t end;
-
- usbcam_chklock(udp);
-
- if (usbcam_curframe_get(udp, &cf)) {
- usbcam_warn(udp, "%s: no current frame", __FUNCTION__);
- return;
- }
-
- end = offset + (patlen * nrecs);
- if (end > cf.uc_size) {
- usbcam_warn(udp, "%s(offs=%zu patlen=%d nrecs=%d) would "
- "write past end of %zu byte framebuffer",
- __FUNCTION__,
- offset, patlen, nrecs, cf.uc_size);
- if (offset > cf.uc_size)
- nrecs = 0;
- else
- nrecs = (cf.uc_size - offset) / patlen;
- }
- else if (end > udp->ud_format.sizeimage)
- usbcam_warn(udp, "%s(offs=%zu patlen=%d nrecs=%d) writing "
- "beyond %u-byte sizeimage", __FUNCTION__,
- offset, patlen, nrecs, udp->ud_format.sizeimage);
-
- if (!nrecs)
- return;
-
- if (patlen == 1) {
- memset(cf.uc_base + offset, *(char *)pattern, nrecs);
- return;
- }
-
- while (nrecs--) {
- memcpy(cf.uc_base + offset, pattern, patlen);
- offset += patlen;
- }
-}
-EXPORT_SYMBOL(usbcam_curframe_fill);
-
-void usbcam_curframe_fill_lines(struct usbcam_dev *udp,
- const char *pat, int patlen,
- int pixperpat)
-{
- int line, nrecperline, stride;
- size_t offset = 0;
-
- nrecperline = (udp->ud_format.width + pixperpat - 1) / pixperpat;
- stride = udp->ud_format.bytesperline
- ? udp->ud_format.bytesperline
- : (nrecperline * patlen);
-
- if ((patlen * nrecperline) == stride) {
- usbcam_curframe_fill(udp, 0, pat, patlen,
- nrecperline * udp->ud_format.height);
- return;
- }
-
- for (line = 0; line < udp->ud_format.height; line++) {
- usbcam_curframe_fill(udp, offset, pat, patlen, nrecperline);
- offset += stride;
- }
-}
-
-void usbcam_curframe_fill_interleaved(struct usbcam_dev *udp,
- const char *pat_even,
- const char *pat_odd,
- int patlen, int pixperpat)
-{
- int line, nrecperline, stride;
- size_t offset = 0;
-
- nrecperline = (udp->ud_format.width + pixperpat - 1) / pixperpat;
- stride = udp->ud_format.bytesperline
- ? udp->ud_format.bytesperline
- : (nrecperline * patlen);
-
- for (line = 0; line < udp->ud_format.height; line++) {
- usbcam_curframe_fill(udp, offset,
- (line & 1) ? pat_odd : pat_even,
- patlen, nrecperline);
- offset += stride;
- }
-}
-
-void usbcam_curframe_fill_planar(struct usbcam_dev *udp,
- const char *pat0, int pat0len, int pixperpat0,
- const char *pat1, int pat1len, int pixperpat1,
- const char *pat2, int pat2len, int pixperpat2)
-{
- int nrecperline;
- size_t offset = 0;
-
- if (pat0 && pat0len) {
- nrecperline = ((udp->ud_format.width + pixperpat0 - 1) /
- pixperpat0);
- usbcam_curframe_fill(udp, offset, pat0, pat0len,
- nrecperline * udp->ud_format.height);
- offset += (nrecperline * udp->ud_format.height * pat0len);
- }
- if (pat1 && pat1len) {
- nrecperline = ((udp->ud_format.width + pixperpat1 - 1) /
- pixperpat1);
- usbcam_curframe_fill(udp, offset, pat1, pat1len,
- nrecperline * udp->ud_format.height);
- offset += (nrecperline * udp->ud_format.height * pat1len);
- }
- if (pat2 && pat2len) {
- nrecperline = ((udp->ud_format.width + pixperpat2 - 1) /
- pixperpat2);
- usbcam_curframe_fill(udp, offset, pat2, pat2len,
- nrecperline * udp->ud_format.height);
- offset += (nrecperline * udp->ud_format.height * pat2len);
- }
-}
-
-/*
- * The goal is to be able to come up with a solid blue image in all
- * basic uncompressed formats. No JPEG or compressed formats, at least
- * not yet.
- */
-int usbcam_curframe_testpattern(struct usbcam_dev *udp)
-{
- usbcam_chklock(udp);
-
-#define DO_FILL(PIXPERPAT, TP...) { \
- const char tp[] = {TP}; \
- usbcam_curframe_fill_lines(udp, tp, sizeof(tp), PIXPERPAT); \
-}
- switch (udp->ud_format.pixelformat) {
- case V4L2_PIX_FMT_RGB332:
- DO_FILL(1, 0x03)
- return 0;
- case V4L2_PIX_FMT_RGB555:
- case V4L2_PIX_FMT_RGB565:
- DO_FILL(1, 0x1f, 0x00)
- return 0;
- case V4L2_PIX_FMT_RGB555X:
- case V4L2_PIX_FMT_RGB565X:
- DO_FILL(1, 0x00, 0x1f)
- return 0;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,20)
- case V4L2_PIX_FMT_RGB444:
- DO_FILL(1, 0x00, 0x0f)
- return 0;
-#endif
- case V4L2_PIX_FMT_BGR24:
- DO_FILL(1, 0xff, 0x00, 0x00);
- return 0;
- case V4L2_PIX_FMT_RGB24:
- DO_FILL(1, 0x00, 0x00, 0xff);
- return 0;
- case V4L2_PIX_FMT_BGR32:
- DO_FILL(1, 0xff, 0x00, 0x00, 0x00);
- return 0;
- case V4L2_PIX_FMT_RGB32:
- DO_FILL(1, 0x00, 0x00, 0xff, 0x00);
- return 0;
- case V4L2_PIX_FMT_GREY:
- DO_FILL(1, 0x1f);
- return 0;
- case V4L2_PIX_FMT_YUYV:
- DO_FILL(2, 0x29, 0xf0, 0x29, 0x6e);
- return 0;
- case V4L2_PIX_FMT_UYVY:
- DO_FILL(2, 0xf0, 0x29, 0x6e, 0x29);
- return 0;
- case V4L2_PIX_FMT_YYUV:
- DO_FILL(2, 0x29, 0x29, 0xf0, 0x6e);
- return 0;
- case V4L2_PIX_FMT_Y41P:
- DO_FILL(8,
- 0xf0, 0x29, 0x6e, 0x29, 0xf0, 0x29, 0x6e, 0x29,
- 0x29, 0x29, 0x29, 0x29);
- return 0;
-#undef DO_FILL
-
- case V4L2_PIX_FMT_SBGGR8: {
- const char tp0[] = { 0xff, 0x00 }, tp1[] = { 0x00, 0x00 };
- usbcam_curframe_fill_interleaved(udp, tp0, tp1, 2, 2);
- return 0;
- }
- case V4L2_PIX_FMT_YVU410: {
- const char tp0[] = {0x29}, tp1[] = {0x6e}, tp2[] = {0xf0};
- usbcam_curframe_fill_planar(udp, tp0, sizeof(tp0), 1,
- tp1, sizeof(tp1), 16,
- tp2, sizeof(tp2), 16);
- return 0;
- }
- case V4L2_PIX_FMT_YUV410: {
- const char tp0[] = {0x29}, tp1[] = {0xf0}, tp2[] = {0x6e};
- usbcam_curframe_fill_planar(udp, tp0, sizeof(tp0), 1,
- tp1, sizeof(tp1), 16,
- tp2, sizeof(tp2), 16);
- return 0;
- }
- case V4L2_PIX_FMT_YVU420: {
- const char tp0[] = {0x29}, tp1[] = {0x6e}, tp2[] = {0xf0};
- usbcam_curframe_fill_planar(udp, tp0, sizeof(tp0), 1,
- tp1, sizeof(tp1), 4,
- tp2, sizeof(tp2), 4);
- return 0;
- }
- case V4L2_PIX_FMT_YUV411P:
- case V4L2_PIX_FMT_YUV420: {
- const char tp0[] = {0x29}, tp1[] = {0xf0}, tp2[] = {0x6e};
- usbcam_curframe_fill_planar(udp, tp0, sizeof(tp0), 1,
- tp1, sizeof(tp1), 4,
- tp2, sizeof(tp2), 4);
- return 0;
- }
- case V4L2_PIX_FMT_YUV422P: {
- const char tp0[] = {0x29}, tp1[] = {0xf0}, tp2[] = {0x6e};
- usbcam_curframe_fill_planar(udp, tp0, sizeof(tp0), 1,
- tp1, sizeof(tp1), 2,
- tp2, sizeof(tp2), 2);
- return 0;
- }
- case V4L2_PIX_FMT_NV12: {
- const char tp0[] = {0x29}, tp1[] = {0xf0, 0x6e};
- usbcam_curframe_fill_planar(udp, tp0, sizeof(tp0), 1,
- tp1, sizeof(tp1), 4,
- NULL, 0, 0);
- return 0;
- }
- case V4L2_PIX_FMT_NV21: {
- const char tp0[] = {0x29}, tp1[] = {0x6e, 0xf0};
- usbcam_curframe_fill_planar(udp, tp0, sizeof(tp0), 1,
- tp1, sizeof(tp1), 4,
- NULL, 0, 0);
- return 0;
- }
- }
-
- return -EINVAL;
-}
-EXPORT_SYMBOL(usbcam_curframe_testpattern);
-
-
-/*
- * video-buf interfaces for managing frame buffers
- */
-
-static int usbcam_videobuf_setup(struct videobuf_queue *vq,
- unsigned int *count, unsigned int *size)
-{
- struct usbcam_fh *ufp = container_of(vq, struct usbcam_fh, uf_vbq);
- struct usbcam_dev *udp = ufp->uf_dev;
-
- usbcam_lock(udp);
-
- /* APPBUG: possibly request larger buffers than necessary */
- if ((ufp->uf_flags & USBCAM_FH_USE_FIXED_FB) &&
- (fixed_fbsize > udp->ud_format.sizeimage))
- *size = fixed_fbsize;
- else
- *size = udp->ud_format.sizeimage;
-
- if (!*count)
- *count = 2;
-
- usbcam_dbg(udp, VIDEOBUF, "videobuf setup: size=%u count=%u",
- *size, *count);
-
- usbcam_unlock(udp);
- return 0;
-}
-
-static void usbcam_videobuf_free(struct videobuf_queue *vq,
- struct usbcam_frame *framep)
-{
- struct videobuf_dmabuf *dma = usbframe_get_dmabuf(&framep->vbb);
-
- videobuf_waiton(&framep->vbb, 0, 0);
- videobuf_dma_unmap(vq, dma);
- videobuf_dma_free(dma);
- if (framep->vbb.state != STATE_NEEDS_INIT) {
- if (framep->vmap_base) {
- vunmap(framep->vmap_base);
- framep->vmap_base = NULL;
- framep->vmap_sof = NULL;
- }
- assert(list_empty(&framep->cap_links));
- framep->vbb.state = STATE_NEEDS_INIT;
- }
-}
-
-static int usbcam_videobuf_prepare(struct videobuf_queue *vq,
- struct videobuf_buffer *vb,
- enum v4l2_field field)
-{
- struct usbcam_fh *ufp = container_of(vq, struct usbcam_fh, uf_vbq);
- struct usbcam_dev *udp = ufp->uf_dev;
- struct usbcam_frame *framep =
- container_of(vb, struct usbcam_frame, vbb);
- struct videobuf_dmabuf *dma = usbframe_get_dmabuf(&framep->vbb);
- int res;
-
-
- framep->vbb.size = udp->ud_format.sizeimage;
- if (framep->vbb.baddr && (framep->vbb.bsize < framep->vbb.size)) {
- usbcam_warn(udp, "process %s requested capture of a frame "
- "larger than its", current->comm);
- usbcam_warn(udp, "allocated frame buffer, fix it!");
- return -EINVAL;
- }
-
- if (framep->vbb.state == STATE_NEEDS_INIT) {
- /*
- * This is the place where we initialize the rest of
- * the usbcam_frame structure.
- */
- INIT_LIST_HEAD(&framep->cap_links);
- framep->vmap_base = NULL;
- framep->vmap_sof = NULL;
-
- usbcam_dbg(udp, VIDEOBUF,
- "preparing frame %d/%p", framep->vbb.i, framep);
-
- /* We also lock down the memory that was allocated for it */
- res = videobuf_iolock(vq, &framep->vbb, NULL);
- if (res)
- goto fail;
-
- /* If there's no kernel mapping, we must create one */
- if (!dma->vmalloc) {
- framep->vmap_base = vmap(dma->pages,
- dma->nr_pages,
- VM_MAP,
- PAGE_KERNEL);
- if (!framep->vmap_base) {
- res = -ENOMEM;
- goto fail;
- }
-
- framep->vmap_sof =
- ((char *)framep->vmap_base) +
- dma->offset;
- }
- }
-
- framep->vbb.field = field;
- framep->vbb.state = STATE_PREPARED;
- return 0;
-
-fail:
- usbcam_warn(udp, "videobuf_prepare failed.");
- usbcam_videobuf_free(vq, framep);
- return res;
-}
-
-static void usbcam_videobuf_queue(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct usbcam_fh *ufp = container_of(vq, struct usbcam_fh, uf_vbq);
- struct usbcam_dev *udp = ufp->uf_dev;
- struct usbcam_frame *framep =
- container_of(vb, struct usbcam_frame, vbb);
- int was_empty = 0;
-
- assert(framep->vbb.state != STATE_NEEDS_INIT);
-
- usbcam_lock(udp);
-
- if (list_empty(&udp->ud_frame_cap_queue))
- was_empty = 1;
-
- usbcam_dbg(udp, VIDEOBUF, "queueing frame %d/%p",
- framep->vbb.i, framep);
-
- /*
- * We always set buffers to STATE_ACTIVE to prevent them from
- * being manipulated / dequeued by the videobuf code.
- */
- list_add_tail(&framep->cap_links, &udp->ud_frame_cap_queue);
- framep->vbb.state = STATE_ACTIVE;
-
- if (was_empty && !udp->ud_capturing) {
- (void) usbcam_capture_start(udp);
- }
-
- usbcam_unlock(udp);
-}
-
-static void usbcam_videobuf_release(struct videobuf_queue *vq,
- struct videobuf_buffer *vb)
-{
- struct usbcam_fh *ufp = container_of(vq, struct usbcam_fh, uf_vbq);
- struct usbcam_dev *udp = ufp->uf_dev;
- struct usbcam_frame *framep =
- container_of(vb, struct usbcam_frame, vbb);
- int stopped_capture = 0;
-
- usbcam_lock(udp);
-
- if ((framep->vbb.state != STATE_NEEDS_INIT) &&
- !list_empty(&framep->cap_links)) {
-
- usbcam_dbg(udp, VIDEOBUF,
- "aborting frame %d/%p", framep->vbb.i, framep);
-
- /*
- * An active frame is being shot down here, most
- * likely by videobuf_queue_cancel.
- */
- assert(framep->vbb.state == STATE_ACTIVE);
-
- if (udp->ud_capturing &&
- !list_empty(&udp->ud_frame_cap_queue) &&
- (framep == usbcam_capture_curframe(udp))) {
- /*
- * The current frame has been user-aborted.
- * We will stop capturing. The minidriver may complete
- * it with an error, or may leave it alone, in which
- * case we will complete it with an error.
- */
- usbcam_dbg(udp, VIDEOBUF,
- "current frame aborted, stopping capture");
-
- usbcam_capture_stop(udp);
- stopped_capture = 1;
- }
-
- if (!list_empty(&framep->cap_links))
- usbcam_capture_complete_frame(udp, framep, 1);
-
- /*
- * Ideally, if we stopped capturing, and there are frames
- * still in the queue, we would restart.
- *
- * In reality, we only take this code path if all frames
- * from the owning file handle are aborted, and restarting
- * would pointlessly slow down this process.
- */
- }
-
- usbcam_unlock(udp);
- usbcam_videobuf_free(vq, framep);
-}
-
-static struct videobuf_queue_ops usbcam_videobuf_qops = {
- .buf_setup = usbcam_videobuf_setup,
- .buf_prepare = usbcam_videobuf_prepare,
- .buf_queue = usbcam_videobuf_queue,
- .buf_release = usbcam_videobuf_release,
-};
-
-
-/*
- * Reference Counting Notes
- *
- * Each usbcam_minidrv gets:
- * - One reference for being in the registered state
- * - One reference for each outstanding usbcam_dev
- *
- * Each usbcam_dev gets:
- * - One reference for having its V4L minor registered and not released
- * - One reference for having its underlying USB device not disconnected
- * - One reference for each open file handle
- */
-
-static void usbcam_minidrv_release(struct kref *kref)
-{
- struct usbcam_minidrv *minidrv =
- container_of(kref, struct usbcam_minidrv, um_kref);
-
- assert(!minidrv->um_dev_count);
- usbcam_dbgm(minidrv, DEV_STATE, "%s: destroying minidrvier",
- __FUNCTION__);
- kfree(minidrv);
-}
-
-/*
- */
-static void usbcam_work_stop(struct usbcam_dev *udp);
-static inline void usbcam_work_maybe_stop(struct usbcam_dev *udp)
-{
- if (!udp->ud_work_refs)
- usbcam_work_stop(udp);
-}
-
-static void usbcam_dev_free(struct kref *kref)
-{
- struct usbcam_dev *udp =
- container_of(kref, struct usbcam_dev, ud_kref);
-
- if (udp->ud_work_refs) {
- usbcam_lock(udp);
- usbcam_warn(udp, "%s: work queue has %d leaked refs",
- __FUNCTION__, udp->ud_work_refs);
- while (udp->ud_work_refs)
- usbcam_work_unref(udp);
- usbcam_unlock(udp);
- }
-
- usbcam_work_maybe_stop(udp);
- assert(!udp->ud_work_thread);
-
- usb_put_intf(udp->ud_intf);
- udp->ud_intf = NULL;
-
- usb_put_dev(udp->ud_dev);
- udp->ud_dev = NULL;
-
- mutex_lock(&udp->ud_minidrv->um_lock);
-
- assert(!list_empty(&udp->ud_drv_links));
- assert(udp->ud_minidrv->um_dev_count > 0);
-
- list_del_init(&udp->ud_drv_links);
- udp->ud_minidrv->um_dev_count--;
-
- mutex_unlock(&udp->ud_minidrv->um_lock);
-
- kref_put(&udp->ud_minidrv->um_kref, usbcam_minidrv_release);
- kfree(udp);
-}
-
-static void usbcam_dev_release(struct kref *kref)
-{
- struct usbcam_dev *udp =
- container_of(kref, struct usbcam_dev, ud_kref);
-
- usbcam_dbg(udp, DEV_STATE, "%s: destroying device", __FUNCTION__);
-
- if (usbcam_minidrv_op_present(udp, release)) {
- usbcam_lock(udp);
- usbcam_minidrv_op(udp, release);
- usbcam_unlock(udp);
- }
- usbcam_dev_free(kref);
-}
-
-void usbcam_put(struct usbcam_dev *udp)
-{
- kref_put(&udp->ud_kref, usbcam_dev_release);
-}
-EXPORT_SYMBOL(usbcam_put);
-
-
-/*
- * Work item crap
- */
-
-enum {
- USBCAM_WORKSTATE_DEAD,
- USBCAM_WORKSTATE_IDLE,
- USBCAM_WORKSTATE_MUTEX_WAIT,
- USBCAM_WORKSTATE_RUNNING,
-};
-
-DECLARE_WAIT_QUEUE_HEAD(usbcam_work_idle_wait);
-
-static int usbcam_work_thread(void *arg)
-{
- struct usbcam_dev *udp = (struct usbcam_dev *) arg;
- struct usbcam_workitem *wip;
- sigset_t wakesigs;
- unsigned long flags;
- usbcam_workfunc_t fn;
- int res;
-
- current->flags |= PF_NOFREEZE;
- set_user_nice(current, -5);
-
- sigemptyset(&wakesigs);
- sigaddset(&wakesigs, SIGUSR1);
-
- while (1) {
- /* Wait for something to appear on the work queue */
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- udp->ud_work_lockwait = 0;
- if (list_empty(&udp->ud_work_queue)) {
- if (kthread_should_stop()) {
- spin_unlock_irqrestore(&udp->ud_work_lock,
- flags);
- break;
- }
-
- set_current_state(TASK_INTERRUPTIBLE);
- wake_up_all(&usbcam_work_idle_wait);
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
- schedule();
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- }
- udp->ud_work_lockwait = 1;
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-
- /* Enable the mutex wait cancelation signal */
- sigprocmask(SIG_UNBLOCK, &wakesigs, NULL);
-
- /* Re-check the queue, wait if it's still nonempty */
- res = -EINTR;
- if (!list_empty(&udp->ud_work_queue)) {
- res = mutex_lock_interruptible(&udp->ud_lock);
- }
-
- /* Disable the mutex wait cancelation signal */
- sigprocmask(SIG_BLOCK, &wakesigs, NULL);
- flush_signals(current);
-
- if (res)
- continue;
-
- wip = NULL;
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- udp->ud_work_lockwait = 0;
- if (!list_empty(&udp->ud_work_queue)) {
- wip = container_of(udp->ud_work_queue.next,
- struct usbcam_workitem,
- uw_links);
- list_del_init(&wip->uw_links);
- }
-
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-
- if (wip) {
- fn = wip->uw_func;
- fn(wip);
- }
-
- usbcam_unlock(udp);
- }
-
- return 0;
-}
-
-void usbcam_work_init(struct usbcam_workitem *wip, usbcam_workfunc_t func)
-{
- INIT_LIST_HEAD(&wip->uw_links);
- wip->uw_dev = NULL;
- wip->uw_func = func;
-}
-EXPORT_SYMBOL(usbcam_work_init);
-
-int usbcam_work_queue(struct usbcam_dev *udp, struct usbcam_workitem *wip)
-{
- unsigned long flags;
- int res;
-
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- if (!list_empty(&wip->uw_links)) {
- res = -EALREADY;
- assert(wip->uw_dev == udp);
- } else if (udp->ud_work_refs) {
- res = 0;
- wip->uw_dev = udp;
- list_add_tail(&wip->uw_links, &udp->ud_work_queue);
- if (udp->ud_work_queue.next == &wip->uw_links)
- wake_up_process(udp->ud_work_thread);
- } else {
- res = -EBUSY;
- }
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-
- return res;
-}
-EXPORT_SYMBOL(usbcam_work_queue);
-
-int usbcam_work_cancel(struct usbcam_dev *udp, struct usbcam_workitem *wip)
-{
- unsigned long flags;
- int res, wakeit = 0;
-
- usbcam_chklock(udp);
-
- res = -ENOENT;
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- if (!list_empty(&wip->uw_links)) {
- res = 0;
- assert(wip->uw_dev == udp);
- if ((udp->ud_work_queue.next == &wip->uw_links) &&
- udp->ud_work_lockwait)
- wakeit = 1;
- list_del_init(&wip->uw_links);
- if (wakeit)
- force_sig(SIGUSR1, udp->ud_work_thread);
- }
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-
- return res;
-}
-EXPORT_SYMBOL(usbcam_work_cancel);
-
-int usbcam_work_ref(struct usbcam_dev *udp)
-{
- struct task_struct *kt_new;
- unsigned long flags;
-
- usbcam_chklock(udp);
-
- /*
- * We adjust this value under the spinlock to synchronize with
- * usbcam_work_queue().
- */
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- udp->ud_work_refs++;
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-
- if (!udp->ud_work_thread) {
- kt_new = kthread_create(usbcam_work_thread, udp,
- udp->ud_dev_name);
- if (!kt_new) {
- usbcam_err(udp, "%s: could not create worker thread",
- __FUNCTION__);
- return -ENOMEM;
- }
-
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- udp->ud_work_thread = kt_new;
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
- }
-
- return 0;
-}
-EXPORT_SYMBOL(usbcam_work_ref);
-
-void usbcam_work_unref(struct usbcam_dev *udp)
-{
- unsigned long flags;
-
- usbcam_chklock(udp);
-
- if (!udp->ud_work_refs) {
- usbcam_warn(udp, "%s: work queue has zero refs", __FUNCTION__);
- return;
- }
-
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- udp->ud_work_refs--;
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-}
-EXPORT_SYMBOL(usbcam_work_unref);
-
-void usbcam_work_runqueue(struct usbcam_dev *udp)
-{
- struct usbcam_workitem *wip;
- unsigned long flags;
- usbcam_workfunc_t fn;
-
- usbcam_chklock(udp);
-
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- while (!list_empty(&udp->ud_work_queue)) {
- wip = container_of(udp->ud_work_queue.next,
- struct usbcam_workitem,
- uw_links);
- list_del_init(&wip->uw_links);
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-
- fn = wip->uw_func;
- fn(wip);
-
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- }
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
-}
-EXPORT_SYMBOL(usbcam_work_runqueue);
-
-
-static void usbcam_work_stop(struct usbcam_dev *udp)
-{
- struct task_struct *kt_stop = NULL;
- unsigned long flags;
-
- usbcam_lock(udp);
-
- if (!udp->ud_work_refs) {
- /* Prevent further tasks from being queued */
- spin_lock_irqsave(&udp->ud_work_lock, flags);
- kt_stop = udp->ud_work_thread;
- udp->ud_work_thread = NULL;
- spin_unlock_irqrestore(&udp->ud_work_lock, flags);
- }
-
- usbcam_unlock(udp);
-
- if (kt_stop) {
- /*
- * Wait for the queue to empty out, then stop the
- * thread. It might be easier to just call
- * usbcam_work_flush() and execute the remaining
- * tasks synchronously in the current thread.
- */
- wait_event(usbcam_work_idle_wait,
- list_empty(&udp->ud_work_queue));
- kthread_stop(kt_stop);
- }
-}
-
-
-
-/*
- * V4L file_operations callout implementations
- */
-
-static int usbcam_v4l_open(struct inode *inode, struct file *filp)
-{
- struct usbcam_dev *udp;
- struct usbcam_fh *ufp;
- int autopm_ref = 0;
- int work_ref = 0;
- int res = 0;
-
- /* The usbcam_dev is referenced by the videodev at this point */
- udp = container_of(video_devdata(filp), struct usbcam_dev, ud_vdev);
-
- ufp = (struct usbcam_fh *) kzalloc(sizeof(*ufp), GFP_KERNEL);
- if (!ufp)
- return -ENOMEM;
-
- ufp->uf_dev = udp;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24)
- videobuf_queue_init(&ufp->uf_vbq,
- &usbcam_videobuf_qops,
- NULL,
- NULL,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct usbcam_frame), ufp);
-#else
- videobuf_queue_pci_init(&ufp->uf_vbq,
- &usbcam_videobuf_qops,
- NULL,
- NULL,
- V4L2_BUF_TYPE_VIDEO_CAPTURE,
- V4L2_FIELD_INTERLACED,
- sizeof(struct usbcam_frame), ufp);
-#endif
-
- mutex_lock(&udp->ud_open_lock);
-
- if (!udp->ud_user_refs) {
- res = usb_autopm_get_interface(udp->ud_intf);
- if (res)
- goto bail_nolock;
- autopm_ref = 1;
- }
-
- usbcam_lock(udp);
-
- if (udp->ud_disconnected) {
- usbcam_warn(udp, "udp is disconnected; bailing");
- res = -ENODEV;
- goto bail;
- }
-
- if (!udp->ud_user_refs) {
- if (!udp->ud_minidrv->um_ops->no_workref_on_open) {
- res = usbcam_work_ref(udp);
- if (res)
- goto bail;
- work_ref = 1;
- }
-
- if (usbcam_minidrv_op_present(udp, open)) {
- res = usbcam_minidrv_op(udp, open);
- if (res) {
- /* Can release/reacquire device lock */
- if (work_ref)
- usbcam_work_unref(udp);
- assert(!udp->ud_user_refs);
- goto bail;
- }
- }
-
- /* Transfer the autopm reference */
- assert(autopm_ref);
- autopm_ref = 0;
- }
-
- udp->ud_user_refs++;
- filp->private_data = ufp;
- usbcam_get(udp);
-
-bail:
- usbcam_unlock(udp);
-bail_nolock:
- mutex_unlock(&udp->ud_open_lock);
- if (res)
- kfree(ufp);
- if (autopm_ref)
- usb_autopm_put_interface(udp->ud_intf);
- usbcam_work_maybe_stop(udp);
- return res;
-}
-
-static int usbcam_v4l_release(struct inode *inode, struct file *filp)
-{
- struct usbcam_fh *ufp = (struct usbcam_fh *) filp->private_data;
- struct usbcam_dev *udp = ufp->uf_dev;
- int autopm_ref = 0;
-
- videobuf_mmap_free(&ufp->uf_vbq);
-
- mutex_lock(&udp->ud_open_lock);
- usbcam_lock(udp);
-
- assert(udp->ud_user_refs);
- if (udp->ud_excl_owner == filp)
- udp->ud_excl_owner = NULL;
- kfree(ufp);
- filp->private_data = NULL;
-
- if (!--udp->ud_user_refs) {
- usbcam_capture_stop(udp);
-
- if (usbcam_minidrv_op_present(udp, close))
- usbcam_minidrv_op(udp, close);
- if (!udp->ud_minidrv->um_ops->no_workref_on_open)
- usbcam_work_unref(udp);
- autopm_ref = 1;
- }
-
- usbcam_unlock(udp);
- mutex_unlock(&udp->ud_open_lock);
- if (autopm_ref)
- usb_autopm_put_interface(udp->ud_intf);
- usbcam_work_maybe_stop(udp);
- usbcam_put(udp);
- return 0;
-}
-
-static ssize_t usbcam_v4l_read(struct file *filp, char __user *data,
- size_t count, loff_t *ppos)
-{
- struct usbcam_fh *ufp = (struct usbcam_fh *) filp->private_data;
- int res;
-
- res = videobuf_read_one(&ufp->uf_vbq, data, count, ppos,
- (filp->f_flags & O_NONBLOCK) ? 1 : 0);
- usbcam_work_maybe_stop(ufp->uf_dev);
- return res;
-}
-
-static unsigned int usbcam_v4l_poll(struct file *filp,
- struct poll_table_struct *wait)
-{
- struct usbcam_fh *ufp = (struct usbcam_fh *) filp->private_data;
-
- return videobuf_poll_stream(filp, &ufp->uf_vbq, wait);
-}
-
-static int usbcam_v4l_mmap(struct file *filp, struct vm_area_struct * vma)
-{
- struct usbcam_fh *ufp = (struct usbcam_fh *) filp->private_data;
-
- return videobuf_mmap_mapper(&ufp->uf_vbq, vma);
-}
-
-static void usbcam_dbg_v4l2_buffer_res(struct usbcam_dev *udp, int res,
- void *arg, const char *prefix)
-{
- struct v4l2_buffer *b __attribute__((unused)) =
- (struct v4l2_buffer *) arg;
-
- if (res) {
- usbcam_dbg(udp, IOCTL_BUF, "%s res:%d", prefix, res);
- return;
- }
-
- usbcam_dbg(udp, IOCTL_BUF, "%s out: index=%d type=%d bytesused=%d "
- "flags=0x%x field=%d memory=%d m=0x%lx length=%d",
- prefix, b->index, b->type, b->bytesused,
- b->flags, b->field, b->memory, b->m.userptr, b->length);
-}
-
-static void usbcam_dbg_v4l2_pix_format(struct usbcam_dev *udp,
- struct v4l2_pix_format *f,
- const char *prefix)
-{
- __u32 pixfmt = f->pixelformat;
- if (!pixfmt)
- pixfmt = 0x3f3f3f3f;
- usbcam_dbg(udp, IOCTL_FMT, "%s wid=%d hgt=%d fmt=%.4s field=%d "
- "bpl=%d size=%d cs=%d", prefix,
- f->width, f->height, (char *) &pixfmt, f->field,
- f->bytesperline, f->sizeimage, f->colorspace);
-}
-
-static void usbcam_dbg_v4l2_pix_format_res(struct usbcam_dev *udp, int res,
- struct v4l2_pix_format *f,
- const char *prefix)
-{
- if (res) {
- usbcam_dbg(udp, IOCTL_FMT, "%s %d", prefix, res);
- return;
- }
- usbcam_dbg_v4l2_pix_format(udp, f, prefix);
-}
-
-static int usbcam_v4l_int_ioctl(struct inode *inodep, struct file *filp,
- unsigned int cmd, void *arg)
-{
- struct usbcam_fh *ufp = (struct usbcam_fh *) filp->private_data;
- struct usbcam_dev *udp = ufp->uf_dev;
- int droplock = 0, res;
-
-#ifdef CONFIG_VIDEO_V4L1_COMPAT
- /* V4L1 API -- Handle this before v4l_compat_translate_ioctl(). */
- if (cmd == VIDIOCGMBUF) {
- struct v4l2_requestbuffers req;
- struct video_mbuf *p = (struct video_mbuf *) arg;
- unsigned int i;
-
- /*
- * APPBUG: motion keeps the first mmap, yet requests
- * larger capture sizes.
- */
- usbcam_lock(udp);
- ufp->uf_flags |= USBCAM_FH_USE_FIXED_FB;
- usbcam_unlock(udp);
-
- req.type = ufp->uf_vbq.type;
- req.count = 2;
- req.memory = V4L2_MEMORY_MMAP;
- res = videobuf_reqbufs(&ufp->uf_vbq, &req);
- if (res == -EBUSY)
- {
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOCGMBUF reqbufs failed: device was busy"
- " - closing and trying again.");
-
- res = videobuf_streamoff(&ufp->uf_vbq);
- if (res < 0)
- {
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOCGMBUF reqbufs failed:"
- "couldn't free previous buffer.");
- return -EBUSY;
- }
- else
- {
- // we freed previous reqbuf OK.
- usbcam_lock(udp);
- ufp->uf_flags |= USBCAM_FH_USE_FIXED_FB;
- usbcam_unlock(udp);
-
- req.type = ufp->uf_vbq.type;
- req.count = 2;
- req.memory = V4L2_MEMORY_MMAP;
- res = videobuf_reqbufs(&ufp->uf_vbq, &req);
- }
- }
- else if (res < 0) {
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOCGMBUF reqbufs failed: %d", res);
- return res;
- }
-
- p->frames = req.count;
- p->size = 0;
- for (i = 0; i < p->frames; i++) {
- p->offsets[i] = ufp->uf_vbq.bufs[i]->boff;
- p->size += ufp->uf_vbq.bufs[i]->bsize;
- }
-
- usbcam_dbg(udp, IOCTL_BUF, "VIDIOCGMBUF frames=%d size=%d",
- p->frames, p->size);
- return 0;
- }
- else if (cmd == VIDIOCGCAP) {
- struct video_capability *cap = (struct video_capability *) arg;
-
- usbcam_lock(udp);
-
- strlcpy(cap->name, udp->ud_vdev.name, sizeof(cap->name));
- cap->type = VID_TYPE_CAPTURE;
- cap->audios = 0;
- cap->channels = 1; /* only one input source, the camera */
-
- cap->maxwidth = udp->ud_format.width;
- cap->maxheight = udp->ud_format.height;
-
- /*
- * We lie, here. These values normally return 640x480, which is
- * actually the maximum, not the minimum. Minimum is usually
- * 160x120. It's sort of useful to lie since lots of software
- * just stick with the minimum - we want higher res for the
- * user where possible.
- */
-
- cap->minwidth = udp->ud_format.width;
- cap->minheight = udp->ud_format.height;
-
- usbcam_unlock(udp);
- return 0;
- }
- else if (cmd == VIDIOCGFBUF) {
- struct video_buffer *buf = (struct video_buffer *) arg;
-
- usbcam_lock(udp);
- buf->base = NULL; /* no physical frame buffer access */
- buf->height = udp->ud_format.height;
- buf->width = udp->ud_format.width;
-
- // graciously stolen from drivers/media/video/v4l1-compat.c
- // and modified slightly.
- switch (udp->ud_format.pixelformat) {
- case V4L2_PIX_FMT_RGB332:
- buf->depth = 8;
- break;
- case V4L2_PIX_FMT_RGB555:
- buf->depth = 15;
- break;
- case V4L2_PIX_FMT_RGB565:
- buf->depth = 16;
- break;
- case V4L2_PIX_FMT_BGR24:
- buf->depth = 24;
- break;
- case V4L2_PIX_FMT_BGR32:
- buf->depth = 32;
- break;
- default:
- buf->depth = 0;
- }
-
- if (udp->ud_format.bytesperline) {
- buf->bytesperline = udp->ud_format.bytesperline;
-
- /* typically comes out at 16 bit depth as non-rgb */
- if (!buf->depth && buf->width)
- buf->depth = ((udp->ud_format.bytesperline<<3)
- + (buf->width-1) )
- /buf->width;
- } else {
- buf->bytesperline =
- (buf->width * buf->depth + 7) & 7;
- buf->bytesperline >>= 3;
- }
-
- usbcam_unlock(udp);
- return 0;
- }
-#endif
- /* If the command is any other V4L1 API, pass it to the translator */
- if (_IOC_TYPE(cmd) == 'v')
- {
- return v4l_compat_translate_ioctl(inodep, filp, cmd, arg,
- usbcam_v4l_int_ioctl);
- }
-
- /*
- * Pass 1: filter out ioctls that we don't want to give
- * the minidriver a chance to handle.
- * These tend to be related to frame management, which is
- * none of the minidriver's business, and we do not allow the
- * minidriver to intercept.
- */
-
- switch (cmd) {
- case VIDIOC_REQBUFS: {
- struct v4l2_requestbuffers *r =
- (struct v4l2_requestbuffers *) arg;
- /* APPBUG: disable USE_FIXED_FB if we enter this path */
- usbcam_lock(udp);
- ufp->uf_flags &= ~(USBCAM_FH_USE_FIXED_FB);
- usbcam_unlock(udp);
-
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOC_REQBUFS count=%d type=%d memory=%d",
- r->count, r->type, r->memory);
- res = videobuf_reqbufs(&ufp->uf_vbq, r);
- usbcam_dbg(udp, IOCTL_BUF, "REQBUFS result=%d", res);
- usbcam_work_maybe_stop(udp);
- return res;
- }
- case VIDIOC_QUERYBUF: {
- struct v4l2_buffer *b = (struct v4l2_buffer *) arg;
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOC_QUERYBUF in: index=%d type=%d",
- b->index, b->type);
- res = videobuf_querybuf(&ufp->uf_vbq, b);
- usbcam_dbg_v4l2_buffer_res(udp, res, b, "VIDIOC_QUERYBUF");
- usbcam_work_maybe_stop(udp);
- return res;
- }
- case VIDIOC_QBUF: {
- struct v4l2_buffer *b = (struct v4l2_buffer *) arg;
- /*
- * APPBUG: ptlib / Ekiga has an issue with zeroing the
- * flags field before calling QBUF.
- *
- * Minidriver support for fast input switching is
- * unavailable for the time being.
- */
- b->flags = 0;
-
- usbcam_dbg(udp, IOCTL_BUF, "VIDIOC_QBUF in: index=%d type=%d",
- b->index, b->type);
- res = videobuf_qbuf(&ufp->uf_vbq, b);
- usbcam_dbg_v4l2_buffer_res(udp, res, b, "VIDIOC_QBUF");
- usbcam_work_maybe_stop(udp);
- return res;
- }
- case VIDIOC_DQBUF: {
- struct v4l2_buffer *b = (struct v4l2_buffer *) arg;
- res = videobuf_dqbuf(&ufp->uf_vbq, b,
- (filp->f_flags & O_NONBLOCK) ? 1 : 0);
- usbcam_dbg_v4l2_buffer_res(udp, res, b, "VIDIOC_DQBUF");
- usbcam_work_maybe_stop(udp);
- return res;
- }
- case VIDIOC_STREAMON: {
- enum v4l2_buf_type f = *(int *) arg;
-
- if (f != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOC_STREAMON: invalid buf type %d", f);
- return -EINVAL;
- }
- if (!udp->ud_excl_owner) {
- usbcam_lock(udp);
- if (!udp->ud_excl_owner)
- udp->ud_excl_owner = filp;
- usbcam_unlock(udp);
- }
- if (udp->ud_excl_owner != filp) {
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOC_STREAMON: not exclusive owner");
- return -EBUSY;
- }
- res = videobuf_streamon(&ufp->uf_vbq);
- usbcam_dbg(udp, IOCTL_BUF, "VIDIOC_STREAMON: res:%d", res);
- usbcam_work_maybe_stop(udp);
- return res;
- }
- case VIDIOC_STREAMOFF: {
- enum v4l2_buf_type f = *(int *) arg;
-
- if (f != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- usbcam_dbg(udp, IOCTL_BUF,
- "VIDIOC_STREAMOFF: invalid buf type %d", f);
- return -EINVAL;
- }
- res = videobuf_streamoff(&ufp->uf_vbq);
- usbcam_dbg(udp, IOCTL_BUF, "VIDIOC_STREAMOFF: res:%d", res);
- usbcam_work_maybe_stop(udp);
- return res;
- }
- }
-
- /* Pass the ioctl to the minidriver, see if it takes responsibility */
-
- if (usbcam_minidrv_op_present(udp, ioctl)) {
- if (!udp->ud_minidrv->um_ops->unlocked_ioctl) {
- usbcam_lock(udp);
- droplock = 1;
- }
- res = usbcam_minidrv_op(udp, ioctl, cmd, arg);
- if (droplock)
- usbcam_unlock(udp);
-
- usbcam_work_maybe_stop(udp);
-
- if (res != -ENOIOCTLCMD)
- return res;
- }
-
- /*
- * Pass 2: If the minidriver doesn't handle the ioctl, do something
- * default. The minidriver can override all of this stuff by
- * intercepting.
- */
-
- switch (cmd) {
-
- /* DEFAULT CAPABILITIES / DEVICE NAME / DRIVER NAME / BUS INFO */
- case VIDIOC_QUERYCAP: {
- struct v4l2_capability *cap = (struct v4l2_capability *) arg;
-
- usbcam_lock(udp);
- strlcpy(cap->driver,
- usbcam_drvname(udp->ud_minidrv),
- sizeof(cap->driver));
- strlcpy(cap->card, udp->ud_vdev.name, sizeof(cap->card));
- snprintf(cap->bus_info, sizeof(cap->bus_info),
- "usb:%s", udp->ud_dev->dev.bus_id);
- cap->version = udp->ud_minidrv->um_version;
- cap->capabilities = (V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_READWRITE |
- V4L2_CAP_STREAMING);
- usbcam_unlock(udp);
- return 0;
- }
-
- /* DEFAULT FORMAT HANDLING - USE MINDIRIVER CALLOUTS */
- case VIDIOC_ENUM_FMT: {
- struct v4l2_fmtdesc *f = (struct v4l2_fmtdesc *) arg;
- struct usbcam_pix_fmt *pf;
-
- usbcam_lock(udp);
-
- res = -EINVAL;
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- goto enum_fmt_done;
- if (f->index >= udp->ud_fmt_array_len)
- goto enum_fmt_done;
-
- res = 0;
- pf = (struct usbcam_pix_fmt *)
- &(((u8 *) udp->ud_fmt_array)
- [f->index * udp->ud_fmt_array_elem_size]);
- f->flags = pf->flags;
- f->pixelformat = pf->pixelformat;
- strlcpy(f->description,
- pf->description,
- sizeof(f->description));
-
- enum_fmt_done:
- usbcam_unlock(udp);
- return res;
- }
-
- case VIDIOC_G_FMT: {
- struct v4l2_format *f = (struct v4l2_format *) arg;
-
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_G_FMT: invalid buf type %d"
- "(wasn't of type VIDEO_CAPTURE: %d)",
- f->type, V4L2_BUF_TYPE_VIDEO_CAPTURE);
- return -EINVAL;
- }
-
- usbcam_lock(udp);
- f->fmt.pix = udp->ud_format;
- usbcam_unlock(udp);
- usbcam_dbg_v4l2_pix_format(udp, &f->fmt.pix,
- "VIDIOC_G_FMT: res:");
- return 0;
- }
-
- case VIDIOC_S_FMT: {
- struct v4l2_format *f = (struct v4l2_format *) arg;
-
- usbcam_lock(udp);
-
- usbcam_dbg_v4l2_pix_format(udp, &f->fmt.pix,
- "VIDIOC_S_FMT: param:");
-
- if (udp->ud_disconnected) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_S_FMT: device disconnected");
- res = -EIO;
- goto s_fmt_done;
- }
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_S_FMT: invalid buf type %d",
- f->type);
- res = -EINVAL;
- goto s_fmt_done;
- }
- if (!udp->ud_excl_owner)
- udp->ud_excl_owner = filp;
- if (!memcmp(&f->fmt.pix, &udp->ud_format,
- sizeof(f->fmt.pix))) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_S_FMT: nothing to do");
- res = 0;
- goto s_fmt_done;
- }
- if (!usbcam_minidrv_op_present(udp, set_format)) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_S_FMT: no minidriver op");
- res = -EINVAL;
- goto s_fmt_done;
- }
- if (udp->ud_excl_owner != filp) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_S_FMT: not exclusive owner");
- res = -EBUSY;
- goto s_fmt_done;
- }
- usbcam_capture_stop_nondestructive(udp);
- if (udp->ud_capturing) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_S_FMT: capture in progress");
- res = -EBUSY;
- goto s_fmt_done;
- }
- res = usbcam_minidrv_op(udp, set_format, &f->fmt.pix);
- usbcam_dbg_v4l2_pix_format_res(udp, res, &f->fmt.pix,
- "VIDIOC_S_FMT: res:");
-
- s_fmt_done:
- usbcam_unlock(udp);
- return res;
- }
-
- case VIDIOC_TRY_FMT: {
- struct v4l2_format *f = (struct v4l2_format *) arg;
-
- usbcam_lock(udp);
-
- usbcam_dbg_v4l2_pix_format(udp, &f->fmt.pix,
- "VIDIOC_TRY_FMT: param:");
-
- if (udp->ud_disconnected) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_TRY_FMT: device disconnected");
- res = -EIO;
- goto s_fmt_done;
- }
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_TRY_FMT: invalid buf type %d",
- f->type);
- res = -EINVAL;
- goto try_fmt_done;
- }
- if (!usbcam_minidrv_op_present(udp, try_format)) {
- usbcam_dbg(udp, IOCTL_FMT,
- "VIDIOC_TRY_FMT: no minidriver op");
- res = -EINVAL;
- goto try_fmt_done;
- }
-
- res = usbcam_minidrv_op(udp, try_format, &f->fmt.pix);
- usbcam_dbg_v4l2_pix_format_res(udp, res, &f->fmt.pix,
- "VIDIOC_TRY_FMT: res:");
-
- try_fmt_done:
- usbcam_unlock(udp);
- usbcam_work_maybe_stop(udp);
- return res;
- }
-
- /* DEFAULT CONTROL HANDLING - USE MINIDRIVER ARRAY / CALLOUTS */
- case VIDIOC_QUERYCTRL: {
- struct v4l2_queryctrl *a = (struct v4l2_queryctrl *) arg;
- const struct usbcam_ctrl *ctrlp, *resp;
- unsigned int targ, highest_id = 0;
- int i, res;
-
- usbcam_lock(udp);
- droplock = 1;
- targ = a->id;
-
- resp = NULL;
-
- if (targ & V4L2_CTRL_FLAG_NEXT_CTRL) {
- /*
- * Find the control with the least ID greater than or
- * equal to a->id
- */
- targ &= ~V4L2_CTRL_FLAG_NEXT_CTRL;
- for (i = 0, ctrlp = udp->ud_ctrl_array;
- i < udp->ud_ctrl_array_len;
- i++, ctrlp = (struct usbcam_ctrl *)
- (((u8 *) ctrlp) +
- udp->ud_ctrl_array_elem_size)) {
- if (ctrlp->ctrl.id <= targ) {
- if (!resp ||
- (ctrlp->ctrl.id < resp->ctrl.id))
- resp = ctrlp;
- }
- }
-
- } else {
- /* Find an exact match */
- for (i = 0, ctrlp = udp->ud_ctrl_array;
- i < udp->ud_ctrl_array_len;
- i++, ctrlp = (struct usbcam_ctrl *)
- (((u8 *) ctrlp) +
- udp->ud_ctrl_array_elem_size)) {
- if (ctrlp->ctrl.id == targ) {
- resp = ctrlp;
- break;
- }
- else if ((ctrlp->ctrl.id >=
- V4L2_CID_PRIVATE_BASE) &&
- (ctrlp->ctrl.id <
- (V4L2_CID_PRIVATE_BASE + 1024)) &&
- (ctrlp->ctrl.id > highest_id)) {
- highest_id = ctrlp->ctrl.id;
- }
- }
- }
-
- if (!resp && !highest_id)
- res = -EINVAL;
-
- else if (!resp) {
- /*
- * Deal with the private control enumeration
- * nonsense that the CTRL_FLAG_NEXT_CTRL thing
- * fixes.
- */
- memset(a, 0, sizeof(*a));
- a->id = targ;
- a->type = V4L2_CTRL_TYPE_INTEGER;
- strlcpy(a->name, "Disabled", sizeof(a->name));
- a->flags = V4L2_CTRL_FLAG_DISABLED;
- res = 0;
-
- } else {
- *a = resp->ctrl;
- res = 0;
-
- /*
- * If a query function was provided, call it to
- * postprocess the response structure, e.g. to set
- * flags.
- */
- if (resp->query_fn) {
- if (udp->ud_minidrv->um_ops->unlocked_ctrl) {
- usbcam_unlock(udp);
- droplock = 0;
- }
- res = resp->query_fn(udp, resp, a);
- }
- }
-
- if (droplock)
- usbcam_unlock(udp);
- usbcam_work_maybe_stop(udp);
- return res;
- }
-
- case VIDIOC_G_CTRL: {
- struct v4l2_control *a = (struct v4l2_control *) arg;
- const struct usbcam_ctrl *ctrlp, *resp;
- int i, res;
-
- usbcam_lock(udp);
- droplock = 1;
-
- if (udp->ud_disconnected) {
- usbcam_unlock(udp);
- return -EIO;
- }
-
- resp = NULL;
- for (i = 0, ctrlp = udp->ud_ctrl_array;
- i < udp->ud_ctrl_array_len;
- i++, ctrlp = (struct usbcam_ctrl *)
- (((u8 *) ctrlp) +
- udp->ud_ctrl_array_elem_size)) {
- if (ctrlp->ctrl.id == a->id) {
- resp = ctrlp;
- break;
- }
- }
-
- if (!resp || (resp->ctrl.type == V4L2_CTRL_TYPE_BUTTON))
- res = -EINVAL;
- else {
- if (udp->ud_minidrv->um_ops->unlocked_ctrl) {
- usbcam_unlock(udp);
- droplock = 0;
- }
- res = resp->get_fn(udp, resp, a);
- }
-
- if (droplock)
- usbcam_unlock(udp);
- usbcam_work_maybe_stop(udp);
- return res;
- }
-
- case VIDIOC_S_CTRL: {
- struct v4l2_control *a = (struct v4l2_control *) arg;
- const struct usbcam_ctrl *ctrlp, *resp;
- int i, res;
-
- usbcam_lock(udp);
- droplock = 1;
-
- if (udp->ud_disconnected) {
- usbcam_unlock(udp);
- return -EIO;
- }
-
- resp = NULL;
- for (i = 0, ctrlp = udp->ud_ctrl_array;
- i < udp->ud_ctrl_array_len;
- i++, ctrlp = (struct usbcam_ctrl *)
- (((u8 *) ctrlp) +
- udp->ud_ctrl_array_elem_size)) {
- if (ctrlp->ctrl.id == a->id) {
- resp = ctrlp;
- break;
- }
- }
-
- if (!resp) {
- res = -EINVAL;
- } else if (!resp->set_fn) {
- /* Read-only control */
- res = -EBUSY;
- } else if ((resp->ctrl.type != V4L2_CTRL_TYPE_BUTTON) &&
- ((a->value < resp->ctrl.minimum) ||
- (a->value > resp->ctrl.maximum))) {
- res = -ERANGE;
- } else {
- if (udp->ud_minidrv->um_ops->unlocked_ctrl) {
- usbcam_unlock(udp);
- droplock = 0;
- }
- res = resp->set_fn(udp, resp, a);
- }
-
- if (droplock)
- usbcam_unlock(udp);
- usbcam_work_maybe_stop(udp);
- return res;
- }
-
- case VIDIOC_QUERYMENU: {
- struct v4l2_querymenu *a = (struct v4l2_querymenu *) arg;
- const struct usbcam_ctrl *ctrlp, *resp;
- int i, res;
-
- usbcam_lock(udp);
-
- resp = NULL;
- for (i = 0, ctrlp = udp->ud_ctrl_array;
- i < udp->ud_ctrl_array_len;
- i++, ctrlp = (struct usbcam_ctrl *)
- (((u8 *) ctrlp) +
- udp->ud_ctrl_array_elem_size)) {
- if (ctrlp->ctrl.id == a->id) {
- resp = ctrlp;
- break;
- }
- }
-
- if (!resp ||
- (resp->ctrl.type != V4L2_CTRL_TYPE_MENU) ||
- (a->index > resp->ctrl.maximum)) {
- res = -EINVAL;
- goto querymenu_done;
- }
-
- strlcpy(a->name, resp->menu_names[a->index], sizeof(a->name));
- res = 0;
-
- querymenu_done:
- usbcam_unlock(udp);
- return res;
- }
-
- /* DEFAULT INPUT HANDLING -- There is one input called "Camera" */
- case VIDIOC_ENUMINPUT: {
- const struct v4l2_input dfl_input = {
- .name = "Camera",
- .type = V4L2_INPUT_TYPE_CAMERA,
- };
- struct v4l2_input *inp = (struct v4l2_input *) arg;
-
- if (inp->index > 0)
- return -EINVAL;
- *inp = dfl_input;
- return 0;
- }
-
- case VIDIOCGCHAN: {
- const struct video_channel dfl_input = {
- .name = "Camera",
- .type = VIDEO_TYPE_CAMERA
- };
- struct video_channel *inp = (struct video_channel *) arg;
-
- if (inp->channel > 0)
- return -EINVAL;
- *inp = dfl_input;
- return 0;
- }
-
- case VIDIOC_G_INPUT: {
- unsigned int *i = (unsigned int *) arg;
- *i = 0;
- return 0;
- }
-
- case VIDIOC_S_INPUT: {
- unsigned int *i = (unsigned int *) arg;
- if (*i != 0)
- return -EINVAL;
- return 0;
- }
-
- /* DEFAULT VIDEO STANDARD HANDLING */
- case VIDIOC_ENUMSTD: {
- struct v4l2_standard *s = (struct v4l2_standard *) arg;
- if (s->index > 0)
- return -EINVAL;
- v4l2_video_std_construct(s, V4L2_STD_NTSC_M, "NTSC-M");
- return 0;
- }
-
- case VIDIOC_G_STD: {
- v4l2_std_id *std = (v4l2_std_id *) arg;
- *std = V4L2_STD_NTSC_M;
- return 0;
- }
-
- case VIDIOC_S_STD:
- return 0;
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20)
- /* Workaround for v4l1_compat bug in older kernels */
- case VIDIOC_G_FBUF: {
- struct v4l2_framebuffer *f = (struct v4l2_framebuffer *) arg;
- memset(f, 0, sizeof(*f));
- return -ENOIOCTLCMD;
- }
-#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,20) */
- }
-
- /* video_usercopy() will convert this to -EINVAL. */
- return -ENOIOCTLCMD;
-}
-
-static int usbcam_v4l_ioctl(struct inode *inodep, struct file *filp,
- unsigned int cmd, unsigned long arg)
-{
- return video_usercopy(inodep, filp, cmd, arg, usbcam_v4l_int_ioctl);
-}
-
-
-/*
- * The template file_operations structure
- *
- * Each usbcam_minidrv_t contains its own copy of this, which
- * is associated with the video4linux device created for that
- * minidriver.
- *
- * In general, copies will differ only in the .owner field, which
- * will refer to the minidriver module, not usbcam.
- */
-
-static struct file_operations usbcam_v4l_fops_template = {
- .owner = THIS_MODULE,
- .open = usbcam_v4l_open,
- .release = usbcam_v4l_release,
- .read = usbcam_v4l_read,
- .poll = usbcam_v4l_poll,
- .mmap = usbcam_v4l_mmap,
- .ioctl = usbcam_v4l_ioctl,
-#ifdef CONFIG_COMPAT
- .compat_ioctl = v4l_compat_ioctl32,
-#endif
- .llseek = no_llseek,
-};
-
-
-/*
- * V4L2 videodev callout implementations
- */
-
-static void usbcam_videodev_release(struct video_device *vfd)
-{
- struct usbcam_dev *udp = container_of(vfd, struct usbcam_dev, ud_vdev);
-
- usbcam_warn(udp, "releasing videodev device.");
-
- usbcam_lock(udp);
-
- assert(!udp->ud_videodev_released);
- udp->ud_videodev_released = 1;
-
- usbcam_unlock(udp);
- usbcam_put(udp);
-}
-
-static struct video_device usbcam_videodev_template = {
- .name = "usbcam-unknown",
- .type = VFL_TYPE_GRABBER,
- .type2 = VID_TYPE_CAPTURE,
- .minor = -1,
- .release = usbcam_videodev_release,
-};
-
-
-/*
- * USB subsystem operation implementations
- */
-
-static int usbcam_check_ctrl_array(struct usbcam_dev *udp,
- const struct usbcam_ctrl *ctrlp,
- int elem_size, int count)
-{
- const struct usbcam_ctrl *startp = ctrlp, *xctrlp;
- int i, j;
- int errors = 0;
-
- for (i = 0; i < count;
- i++, ctrlp = (struct usbcam_ctrl *)
- (((u8 *) ctrlp) + elem_size)) {
- /* Verify that the ID isn't already registered */
- for (j = 0, xctrlp = startp;
- j < i;
- j++, xctrlp = (struct usbcam_ctrl *)
- (((u8 *) xctrlp) + elem_size)) {
- if (ctrlp->ctrl.id == xctrlp->ctrl.id) {
- usbcam_warn(udp, "control %d is registered "
- "more than once",
- ctrlp->ctrl.id);
- errors++;
- break;
- }
- }
-
- /* Check minimum, maximum, step, and default */
- switch (ctrlp->ctrl.type) {
- case V4L2_CTRL_TYPE_INTEGER:
- if (ctrlp->ctrl.minimum > ctrlp->ctrl.maximum) {
- usbcam_warn(udp, "control %d has "
- "minimum > maximum",
- ctrlp->ctrl.id);
- errors++;
- }
- break;
-
- case V4L2_CTRL_TYPE_BOOLEAN:
- if (ctrlp->ctrl.minimum) {
- usbcam_warn(udp, "control %d is BOOLEAN "
- "and has minimum != 0",
- ctrlp->ctrl.id);
- errors++;
- }
- if (ctrlp->ctrl.maximum != 1) {
- usbcam_warn(udp, "control %d is BOOLEAN "
- "and has maximum != 1",
- ctrlp->ctrl.id);
- errors++;
- }
- if (ctrlp->ctrl.step != 1) {
- usbcam_warn(udp, "control %d is BOOLEAN "
- "and has step != 1",
- ctrlp->ctrl.id);
- errors++;
- }
- break;
-
- case V4L2_CTRL_TYPE_MENU:
- if (ctrlp->ctrl.minimum) {
- usbcam_warn(udp, "control %d is MENU and has "
- "minimum != 0",
- ctrlp->ctrl.id);
- errors++;
- }
- if (!ctrlp->ctrl.maximum) {
- usbcam_warn(udp, "control %d is MENU and has "
- "maximum == 0",
- ctrlp->ctrl.id);
- errors++;
- }
- if (ctrlp->ctrl.step != 1) {
- usbcam_warn(udp, "control %d is MENU and has "
- "step != 1",
- ctrlp->ctrl.id);
- errors++;
- }
- if (!ctrlp->menu_names) {
- usbcam_warn(udp, "control %d is MENU and has "
- "NULL menu_names",
- ctrlp->ctrl.id);
- errors++;
- break;
- }
- break;
-
- case V4L2_CTRL_TYPE_BUTTON:
- if (ctrlp->ctrl.minimum) {
- usbcam_warn(udp, "control %d is BUTTON "
- "and has minimum != 0",
- ctrlp->ctrl.id);
- errors++;
- }
- if (ctrlp->ctrl.maximum) {
- usbcam_warn(udp, "control %d is BUTTON "
- "and has maximum != 0",
- ctrlp->ctrl.id);
- errors++;
- }
- if (ctrlp->ctrl.step) {
- usbcam_warn(udp, "control %d is BUTTON "
- "and has step != 0",
- ctrlp->ctrl.id);
- errors++;
- }
- break;
-
- default:
- usbcam_warn(udp, "control %d is of invalid type %d",
- ctrlp->ctrl.id, ctrlp->ctrl.type);
- errors++;
- continue;
- }
-
- /* Check the range */
- if ((ctrlp->ctrl.default_value < ctrlp->ctrl.minimum) ||
- (ctrlp->ctrl.default_value > ctrlp->ctrl.maximum)) {
- usbcam_warn(udp, "control %d default out of range",
- ctrlp->ctrl.id);
- errors++;
- }
-
- /* Check the get_fn callout */
- if (ctrlp->ctrl.type == V4L2_CTRL_TYPE_BUTTON) {
- if (ctrlp->get_fn) {
- usbcam_warn(udp, "control %d is BUTTON "
- "and has a get_fn callout",
- ctrlp->ctrl.id);
- errors++;
- }
- } else {
- if (!ctrlp->get_fn) {
- usbcam_warn(udp, "control %d has no "
- "get_fn callout",
- ctrlp->ctrl.id);
- errors++;
- }
- }
-
- if ((ctrlp->ctrl.type == V4L2_CTRL_TYPE_BUTTON) &&
- !ctrlp->set_fn) {
- usbcam_warn(udp, "control %d has no set_fn callout",
- ctrlp->ctrl.id);
- errors++;
- }
- }
-
- return errors;
-}
-
-struct usbcam_claimed_interface {
- struct list_head uc_links;
- struct usb_interface *uc_intf;
-};
-
-static int usbcam_usb_probe(struct usb_interface *intf,
- const struct usb_device_id *devid)
-{
- struct usb_driver *drvp;
- struct usbcam_dev *udp = NULL, *udpx;
- usbcam_minidrv_t *minidrv;
- struct usb_device *dev;
- struct list_head *listp;
- struct usbcam_claimed_interface *cip;
- int minidrv_init_failed = 0;
- int res, i;
-
- /* Locate the mini-driver */
- dev = interface_to_usbdev(intf);
- drvp = to_usb_driver(intf->dev.driver);
- minidrv = container_of(drvp, usbcam_minidrv_t, um_usbdrv);
-
- /* Allocate and initialize a device structure */
- udp = (struct usbcam_dev *) kzalloc(sizeof(*udp) +
- minidrv->um_dev_privsize,
- GFP_KERNEL);
- if (!udp)
- return -ENOMEM;
-
- mutex_init(&udp->ud_open_lock);
- mutex_init(&udp->ud_lock);
- spin_lock_init(&udp->ud_work_lock);
- INIT_LIST_HEAD(&udp->ud_work_queue);
- udp->ud_minidrv = minidrv;
- udp->ud_dev = usb_get_dev(dev);
- udp->ud_intf = usb_get_intf(intf);
- udp->ud_debug = minidrv->um_debug;
- INIT_LIST_HEAD(&udp->ud_drv_links);
- kref_init(&udp->ud_kref);
-
- INIT_LIST_HEAD(&udp->ud_interface_list);
- INIT_LIST_HEAD(&udp->ud_frame_cap_queue);
-
- if (minidrv->um_dev_privsize) {
- udp->ud_minidrv_data = &udp[1];
- }
-
- /* Set up the video4linux structure */
- udp->ud_vdev = minidrv->um_videodev_template;
-
- /* Add the device to the minidriver's list of active devices */
- usbcam_lock(udp);
-
- mutex_lock(&minidrv->um_lock);
-
- /* Inefficiently find an unused ID in the device list */
- i = 0;
- udpx = NULL;
- list_for_each(listp, &minidrv->um_dev_list) {
- udpx = list_entry(listp, struct usbcam_dev, ud_drv_links);
- if (udpx->ud_minidrv_id < 0) {
- udpx = NULL;
- continue;
- }
- if (udpx->ud_minidrv_id != i)
- break;
- udpx = NULL;
- i++;
- }
-
- udp->ud_minidrv_id = i;
- if (udpx) {
- list_add_tail(&udp->ud_drv_links, &udpx->ud_drv_links);
- } else {
- list_add_tail(&udp->ud_drv_links, &minidrv->um_dev_list);
- }
-
- minidrv->um_dev_count++;
- kref_get(&minidrv->um_kref);
-
- snprintf(udp->ud_dev_name, sizeof(udp->ud_dev_name),
- "%s-%d", usbcam_drvname(udp->ud_minidrv),
- udp->ud_minidrv_id);
-
- snprintf(udp->ud_vdev.name, sizeof(udp->ud_vdev.name),
- "%s USB Camera #%d",
- usbcam_drvname(minidrv), udp->ud_minidrv_id + 1);
-
-
- mutex_unlock(&minidrv->um_lock);
-
- /* Invoke the minidriver initialization callout */
- udp->ud_initializing = 1;
- res = usbcam_minidrv_op(udp, init, devid);
- udp->ud_initializing = 0;
- if (res) {
- usbcam_dbg(udp, DEV_STATE, "minidriver init failed: %d", res);
- minidrv_init_failed = 1;
- goto out_nodisconn;
- }
-
- /* Complain if the device isn't filled out correctly */
- if (!udp->ud_format.width || !udp->ud_format.height) {
- usbcam_warn(udp, "minidriver did not set default size");
- res = -EINVAL;
- goto out;
- }
- if (!udp->ud_format.pixelformat) {
- usbcam_warn(udp, "minidriver did not set default pixelformat");
- res = -EINVAL;
- goto out;
- }
-
- /* Check the control array */
- if (udp->ud_ctrl_array_len &&
- usbcam_check_ctrl_array(udp,
- udp->ud_ctrl_array,
- udp->ud_ctrl_array_elem_size,
- udp->ud_ctrl_array_len)) {
- usbcam_warn(udp, "minidriver supplied ctrl_array with errors");
- res = -EINVAL;
- goto out;
- }
-
- usb_set_intfdata(intf, udp);
- usbcam_unlock(udp);
-
- /*
- * Register the device with video4linux
- *
- * BUG: video_register_device() may or may not call back
- * into usbcam_videodev_release(), depending on how it fails,
- * and if it does call back, its callback may be latent.
- *
- * We will assume no callback on failure.
- */
-
- if (udp->ud_vdev.minor != -1) {
- /* Minidriver has indicated its preference for a minor */
- res = video_register_device(&udp->ud_vdev, VFL_TYPE_GRABBER,
- -1);
- if (!res)
- goto video_registered;
- }
-
- for (i = 0; i < minidrv->um_video_nr_array_len; i++) {
- res = video_register_device(&udp->ud_vdev, VFL_TYPE_GRABBER,
- minidrv->um_video_nr_array[i]);
- if (!res)
- goto video_registered;
- }
-
- res = video_register_device(&udp->ud_vdev, VFL_TYPE_GRABBER, -1);
- if (res) {
- usbcam_err(udp, "%s: video_register_device failed",
- __FUNCTION__);
- usbcam_lock(udp);
- assert(!udp->ud_videodev_released);
- udp->ud_videodev_released = 1;
- goto out;
- }
-
-video_registered:
- usbcam_get(udp);
- /*
- * There should now be at least two references on udp:
- * One for the primary USB interface in the non-disconnected state
- * One for the videodev stuff
- * One for each additional claimed interface
- */
-
- usbcam_dbg(udp, DEV_STATE, "registered as video%d",
- udp->ud_vdev.minor);
-
- usbcam_work_maybe_stop(udp);
- return 0;
-
-out:
- assert(!udp->ud_disconnected);
- udp->ud_disconnected = 1;
- if (usbcam_minidrv_op_present(udp, disconnect))
- usbcam_minidrv_op(udp, disconnect);
-
-out_nodisconn:
- while (!list_empty(&udp->ud_interface_list)) {
- cip = list_entry(udp->ud_interface_list.next,
- struct usbcam_claimed_interface,
- uc_links);
- list_del_init(&cip->uc_links);
- usb_set_intfdata(cip->uc_intf, NULL);
- usb_driver_release_interface(&minidrv->um_usbdrv,
- cip->uc_intf);
- usb_put_intf(cip->uc_intf);
- kfree(cip);
- usbcam_put(udp);
- }
-
- usbcam_unlock(udp);
-
- if (minidrv_init_failed)
- kref_put(&udp->ud_kref, usbcam_dev_free);
- else
- usbcam_put(udp);
- return res;
-}
-
-static void usbcam_usb_disconnect(struct usb_interface *intf)
-{
- struct usbcam_dev *udp = (struct usbcam_dev *) usb_get_intfdata(intf);
- struct usbcam_claimed_interface *iterp, *cip;
- int put_intf = 0;
- int put_udp = 0;
-
- if (!udp)
- return;
-
- usbcam_lock(udp);
- if (!udp->ud_disconnected) {
- udp->ud_disconnected = 1;
- usbcam_unlock(udp);
-
- usbcam_dbg(udp, DEV_STATE, "disconnected");
- video_unregister_device(&udp->ud_vdev);
-
- usbcam_dbg(udp, DEV_STATE, "unregistered from video%d",
- udp->ud_vdev.minor);
-
- usbcam_lock(udp);
- if (usbcam_minidrv_op_present(udp, disconnect))
- usbcam_minidrv_op(udp, disconnect);
-
- usbcam_capture_stop(udp);
- }
-
- if (intf == udp->ud_intf) {
- assert(!udp->ud_disconnected_primary);
- udp->ud_disconnected_primary = 1;
- put_udp = 1;
-
- } else {
- cip = NULL;
- list_for_each_entry(iterp, &udp->ud_interface_list, uc_links) {
- if (iterp->uc_intf == intf) {
- cip = iterp;
- break;
- }
- }
-
- if (cip) {
- list_del_init(&cip->uc_links);
- kfree(cip);
- put_intf = 1;
- put_udp = 1;
- } else {
- usbcam_err(udp, "interface %p is not claimed", intf);
- }
- }
-
- usb_set_intfdata(intf, NULL);
- usbcam_unlock(udp);
-
- usbcam_work_maybe_stop(udp);
-
- if (put_intf)
- usb_put_intf(intf);
- if (put_udp)
- usbcam_put(udp);
-}
-
-#if defined(CONFIG_PM)
-static int usbcam_usb_suspend(struct usb_interface *intf, pm_message_t msg)
-{
- struct usbcam_dev *udp = (struct usbcam_dev *) usb_get_intfdata(intf);
- int relock = 0, res = 0;
- if (!udp) {
- printk(KERN_WARNING "%s: no associated device\n",
- __FUNCTION__);
- return 0;
- }
-
- usbcam_lock(udp);
- if ((intf != udp->ud_intf) || udp->ud_suspended) {
- /* Do nothing */
- } else if (usbcam_minidrv_op_present(udp, suspend)) {
- usbcam_dbg(udp, DEV_STATE, "invoking minidriver suspend");
- udp->ud_suspended = 1;
- if (udp->ud_minidrv->um_ops->unlocked_pm) {
- usbcam_unlock(udp);
- relock = 1;
- }
-
- res = usbcam_minidrv_op(udp, suspend, msg);
-
- if (relock)
- usbcam_lock(udp);
- if (res)
- udp->ud_suspended = 0;
- } else {
- usbcam_dbg(udp, DEV_STATE, "no minidriver suspend method");
- udp->ud_suspended = 1;
- }
-
- usbcam_unlock(udp);
- usbcam_work_maybe_stop(udp);
- return res;
-}
-
-static int usbcam_usb_resume(struct usb_interface *intf)
-{
- struct usbcam_dev *udp = (struct usbcam_dev *) usb_get_intfdata(intf);
- int relock = 0, res = 0;
- if (!udp) {
- printk(KERN_WARNING "%s: no associated device\n",
- __FUNCTION__);
- return 0;
- }
-
- usbcam_lock(udp);
- if ((intf != udp->ud_intf) || !udp->ud_suspended) {
- /* Nothing to do! */
- } else if (usbcam_minidrv_op_present(udp, resume)) {
- usbcam_dbg(udp, DEV_STATE, "invoking minidriver resume");
- if (udp->ud_minidrv->um_ops->unlocked_pm) {
- usbcam_unlock(udp);
- relock = 1;
- }
- res = usbcam_minidrv_op(udp, resume);
- if (relock)
- usbcam_lock(udp);
- } else
- usbcam_dbg(udp, DEV_STATE, "no minidriver resume method");
-
- if (!res)
- udp->ud_suspended = 0;
-
- usbcam_unlock(udp);
- usbcam_work_maybe_stop(udp);
- return res;
-}
-#endif /* defined(CONFIG_PM) */
-
-
-static const struct usb_driver usbcam_usb_driver_template = {
- .name = "usbcam minidriver",
- .probe = usbcam_usb_probe,
- .disconnect = usbcam_usb_disconnect,
-#if defined(CONFIG_PM)
- .suspend = usbcam_usb_suspend,
- .resume = usbcam_usb_resume,
-#endif
-};
-
-
-/*
- * Minidriver registration/unregistration
- */
-
-int usbcam_register_mod(usbcam_minidrv_t **driverpp,
- int minidrv_version, const char *minidrv_verx,
- const struct usbcam_dev_ops *ops,
- const int dev_priv_size,
- const struct usb_device_id *id_table,
- const int *video_nrs, int video_nrs_len,
- int *debug, struct module *md, const char *modname)
-{
- usbcam_minidrv_t *minidrv;
- int res;
-
- printk(KERN_INFO "usbcam: registering driver %s %d.%d.%d%s\n",
- modname,
- (minidrv_version >> 16) & 0xff,
- (minidrv_version >> 8) & 0xff,
- minidrv_version & 0xff,
- minidrv_verx ? minidrv_verx : "");
-
- minidrv = (usbcam_minidrv_t *) kzalloc(sizeof(*minidrv), GFP_KERNEL);
- if (!minidrv) {
- err("%s: Failed to allocate usbcam_minidrv_t", __FUNCTION__);
- return -ENOMEM;
- }
-
- kref_init(&minidrv->um_kref);
- minidrv->um_owner = md;
- minidrv->um_modname = modname;
- minidrv->um_version = minidrv_version;
- minidrv->um_debug = debug;
- minidrv->um_dev_privsize = dev_priv_size;
- INIT_LIST_HEAD(&minidrv->um_dev_list);
- mutex_init(&minidrv->um_lock);
- minidrv->um_video_nr_array = video_nrs;
- minidrv->um_video_nr_array_len = video_nrs_len;
-
- minidrv->um_ops = ops;
-
- minidrv->um_usbdrv = usbcam_usb_driver_template;
- minidrv->um_usbdrv.name = usbcam_drvname(minidrv);
- minidrv->um_usbdrv.id_table = id_table;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
- minidrv->um_usbdrv.supports_autosuspend =
- minidrv->um_ops->supports_autosuspend;
-#endif
-
- /*
- * We have a separate fops per minidriver structure so that
- * module reference counting works without egregious hacks.
- */
- minidrv->um_v4l_fops = usbcam_v4l_fops_template;
- minidrv->um_v4l_fops.owner = minidrv->um_owner;
-
- minidrv->um_videodev_template = usbcam_videodev_template;
- minidrv->um_videodev_template.fops = &minidrv->um_v4l_fops;
-
- *driverpp = minidrv;
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21)
- res = usb_register_driver(&minidrv->um_usbdrv, minidrv->um_owner,
- minidrv->um_modname);
-#else
- res = usb_register_driver(&minidrv->um_usbdrv, minidrv->um_owner);
-#endif
- if (res) {
- kref_put(&minidrv->um_kref, usbcam_minidrv_release);
- *driverpp = NULL;
- }
-
- usbcam_dbgm(minidrv, DEV_STATE, "registered minidriver");
-
- return res;
-}
-EXPORT_SYMBOL(usbcam_register_mod);
-
-void usbcam_unregister(usbcam_minidrv_t *minidrv)
-{
- usbcam_dbgm(minidrv, DEV_STATE, "unregistering minidriver");
-
- usb_deregister(&minidrv->um_usbdrv);
-
- if (minidrv->um_dev_count) {
- err("%s: %d \"%s\" devices remain",
- __FUNCTION__, minidrv->um_dev_count,
- usbcam_drvname(minidrv));
- }
-
- kref_put(&minidrv->um_kref, usbcam_minidrv_release);
-}
-EXPORT_SYMBOL(usbcam_unregister);
-
-
-int usbcam_claim_interface(struct usbcam_dev *udp, int ifnum)
-{
- struct usb_interface *intf;
- struct usbcam_claimed_interface *cip;
- int res;
-
- usbcam_chklock(udp);
-
- if (!udp->ud_initializing) {
- usbcam_warn(udp, "%s may only be called from minidriver init",
- __FUNCTION__);
- return -EINVAL;
- }
-
- intf = usb_ifnum_to_if(udp->ud_dev, ifnum);
- if (!intf) {
- usbcam_warn(udp, "%s: interface %d does not exist",
- __FUNCTION__, ifnum);
- return -ENODEV;
- }
-
- res = usb_driver_claim_interface(&udp->ud_minidrv->um_usbdrv,
- intf, NULL);
-
- if (!res) {
- cip = kmalloc(sizeof(*cip), GFP_KERNEL);
- if (!cip) {
- usb_driver_release_interface(&udp->ud_minidrv->
- um_usbdrv, intf);
- return -ENOMEM;
- }
-
- INIT_LIST_HEAD(&cip->uc_links);
- cip->uc_intf = usb_get_intf(intf);
- usb_set_intfdata(intf, udp);
- usbcam_get(udp);
- list_add_tail(&cip->uc_links, &udp->ud_interface_list);
- }
-
- return res;
-}
-EXPORT_SYMBOL(usbcam_claim_interface);
-
-
-/*
- * Traverse the alternate setting list and find one that provides
- * the least bandwidth that satisfies the minimum requirement.
- */
-int usbcam_choose_altsetting(struct usbcam_dev *udp, int ifnum,
- int pipe, int bytes_per_sec_min,
- int pkt_min, int pkt_max,
- int *altsetting_nr)
-{
- struct usb_interface *intf;
- const struct usb_host_interface *aintf;
- const struct usb_endpoint_descriptor *epd = NULL;
- int i, j;
-
- int wmp, bw;
- int best_alt = -1, best_alt_bw = 0;
-
- usbcam_chklock(udp);
-
- if (udp->ud_disconnected) {
- usbcam_warn(udp, "%s: device is disconnected", __FUNCTION__);
- return -ENODEV;
- }
-
- if (ifnum < 0)
- ifnum = udp->ud_intf->cur_altsetting->desc.bInterfaceNumber;
-
- intf = usb_ifnum_to_if(udp->ud_dev, ifnum);
- if (!intf) {
- usbcam_warn(udp, "%s: interface %d does not exist",
- __FUNCTION__, ifnum);
- return -ENODEV;
- }
-
- if ((bytes_per_sec_min >= 0) &&
- !usb_pipeisoc(pipe) && !usb_pipeint(pipe)) {
- usbcam_warn(udp, "%s: minidriver specified bytes_per_sec_min "
- "on non-iso non-int pipe", __FUNCTION__);
- }
-
- for (i = 0; i < intf->num_altsetting; i++) {
-
- aintf = &intf->altsetting[i];
- for (j = 0; j < aintf->desc.bNumEndpoints; j++) {
- epd = &aintf->endpoint[j].desc;
- if ((epd->bEndpointAddress &
- USB_ENDPOINT_NUMBER_MASK) ==
- usb_pipeendpoint(pipe))
- break;
- }
-
- if (j == aintf->desc.bNumEndpoints) {
- /* No endpoint 6 in this descriptor, huh?? */
- usbcam_dbg(udp, ALTSETTING,
- "altsetting %d has no EP%d",
- i, usb_pipeendpoint(pipe));
- continue;
- }
-
- if (((usb_pipetype(pipe) == PIPE_ISOCHRONOUS) &&
- !usb_endpoint_xfer_isoc(epd)) ||
- ((usb_pipetype(pipe) == PIPE_INTERRUPT) &&
- !usb_endpoint_xfer_int(epd)) ||
- (usb_pipein(pipe) && !usb_endpoint_dir_in(epd)) ||
- (!usb_pipein(pipe) && usb_endpoint_dir_in(epd))) {
- /* Something is horribly wrong */
- usbcam_dbg(udp, ALTSETTING,
- "altsetting %d has unexpected EP%d",
- i, usb_pipeendpoint(pipe));
- continue;
- }
-
- bw = 0;
- wmp = le16_to_cpu(epd->wMaxPacketSize);
-
- /* Bandwidth only applies to iso & int pipes */
- if (usb_pipeisoc(pipe) || usb_pipeint(pipe)) {
- if (udp->ud_dev->speed == USB_SPEED_HIGH) {
- /* 8 uframes per regular frame */
- bw = 8000;
-
- /* high bandwidth endpoint? */
- wmp = ((wmp & 0x7ff) *
- (((wmp >> 11) & 0x3) + 1));
- } else {
- bw = 1000;
- wmp &= 0x7ff;
- }
-
- bw *= wmp;
-
- /* Divide by interval / frame skippage */
- bw = bw / (1 << (epd->bInterval - 1));
-
- usbcam_dbg(udp, ALTSETTING,
- "altsetting %d provides %d B/s bandwidth",
- i, bw);
-
- /* Check the bandwidth */
- if (bw < bytes_per_sec_min)
- continue;
-
- } else
- wmp &= 0x7ff;
-
- /* Check the packet size */
- if (((pkt_min >= 0) && (wmp < pkt_min)) ||
- ((pkt_max >= 0) && (wmp > pkt_max)))
- continue;
-
- if ((best_alt < 0) || (bw < best_alt_bw)) {
- best_alt = i;
- best_alt_bw = bw;
- }
- }
-
- if (best_alt == -1)
- return -ENODEV;
-
- *altsetting_nr = best_alt;
- return 0;
-}
-EXPORT_SYMBOL(usbcam_choose_altsetting);
-
-
-/*
- * DMA buffer helper routines
- */
-static int usbcam_urb_allocbuf(struct usbcam_dev *udp, struct urb *urbp,
- size_t nbytes)
-{
- urbp->transfer_buffer = usb_buffer_alloc(udp->ud_dev,
- nbytes,
- GFP_KERNEL,
- &urbp->transfer_dma);
- if (!urbp->transfer_buffer)
- return -ENOMEM;
-
- urbp->dev = udp->ud_dev;
- urbp->transfer_buffer_length = nbytes;
- urbp->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
- return 0;
-}
-
-static inline void usbcam_urb_freebuf(struct urb *urbp)
-{
- usb_buffer_free(urbp->dev,
- urbp->transfer_buffer_length,
- urbp->transfer_buffer,
- urbp->transfer_dma);
-}
-
-
-/*
- * Isochronous stream helper functions
- * This depends on the other usbcam stuff, but they don't depend on it,
- * and it should be considered an extension sub-library.
- */
-
-/* Default isostream parameters */
-#define USBCAM_DFL_ISO_URBS 8
-#define USBCAM_DFL_ISO_URB_PKTS 32
-
-/*
- * This structure represents one Isoc request - URB and buffer
- */
-struct usbcam_isobuf {
- struct list_head ib_links;
- struct usbcam_isostream *ib_isostream;
- struct urb *ib_urb;
- struct task_struct **ib_worker;
- struct usbcam_workitem ib_workitem;
-};
-
-/* isop->iso_lock must be held on entry */
-static void usbcam_isostream_resubmit(struct usbcam_isostream *isop,
- struct usbcam_isobuf *ibp)
-{
- int res;
-
- list_del(&ibp->ib_links);
- list_add(&ibp->ib_links, &isop->iso_active_list);
-
- res = usb_submit_urb(ibp->ib_urb, GFP_ATOMIC);
- if (res == -EL2NSYNC)
- res = usb_submit_urb(ibp->ib_urb, GFP_ATOMIC);
- if (res) {
- usbcam_dbg(isop->iso_dev, ISOSTREAM,
- "iso resubmit %p failed: %d", ibp, res);
- isop->iso_resubmit_err = res;
-
- list_del(&ibp->ib_links);
- list_add(&ibp->ib_links, &isop->iso_unused_list);
-
- (void) usbcam_work_queue(isop->iso_dev,
- &isop->iso_error_workitem);
- }
-}
-
-static void usbcam_isostream_urb_process(struct usbcam_workitem *work)
-{
- struct usbcam_isobuf *ibp = container_of(work, struct usbcam_isobuf,
- ib_workitem);
- struct usbcam_isostream *isop = ibp->ib_isostream;
- struct task_struct *me = current;
- long flags;
- int i;
-
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso processing %p", ibp);
-
- assert(!ibp->ib_worker);
- ibp->ib_worker = &me;
-
- for (i = 0; i < ibp->ib_urb->number_of_packets; i++) {
- char *buf = (((char *) ibp->ib_urb->transfer_buffer) +
- ibp->ib_urb->iso_frame_desc[i].offset);
- int len = ibp->ib_urb->iso_frame_desc[i].actual_length;
- int status = ibp->ib_urb->iso_frame_desc[i].status;
-
- ibp->ib_urb->iso_frame_desc[i].actual_length = 0;
- ibp->ib_urb->iso_frame_desc[i].status = 0;
-
- isop->iso_ops->packet_done(isop->iso_dev,
- isop, buf, len, status);
- if (!me)
- return;
- }
-
- assert(ibp->ib_worker == &me);
- ibp->ib_worker = NULL;
-
- spin_lock_irqsave(&isop->iso_lock, flags);
-
- if (isop->iso_streaming && (isop->iso_resubmit_err == -ENOSPC)) {
- /* Try to limp along with iso underflows */
-
- usbcam_isostream_resubmit(isop, ibp);
-
- if ((isop->iso_active_list.next !=
- isop->iso_active_list.prev) &&
- (isop->iso_resubmit_err == -ENOSPC))
- isop->iso_resubmit_err = 0;
-
- } else {
- list_del(&ibp->ib_links);
- list_add(&ibp->ib_links, &isop->iso_unused_list);
- }
-
- spin_unlock_irqrestore(&isop->iso_lock, flags);
-}
-
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,19)
-static void usbcam_isostream_urb_complete(struct urb *urb)
-#else
-static void usbcam_isostream_urb_complete(struct urb *urb,
- struct pt_regs *unused)
-#endif
-{
- struct usbcam_isobuf *ibp = (struct usbcam_isobuf *) urb->context;
- struct usbcam_isostream *isop = ibp->ib_isostream;
- long flags;
- int res;
-
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso urb complete: %p", ibp);
-
- spin_lock_irqsave(&isop->iso_lock, flags);
-
- if (list_empty(&ibp->ib_links)) {
- /* We are being singled out for cancelation, do nothing */
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso canceled, ignoring");
- goto done;
- }
-
- else if (!isop->iso_streaming) {
- /* Quietly and politely move this URB to the unused list */
- list_del(&ibp->ib_links);
- list_add(&ibp->ib_links, &isop->iso_unused_list);
- usbcam_dbg(isop->iso_dev, ISOSTREAM,
- "not resubmitting, streaming off");
- goto done;
- }
-
- /* Move to the done queue, submit a new URB, wake */
- list_del(&ibp->ib_links);
- list_add_tail(&ibp->ib_links, &isop->iso_complete_list);
-
- res = usbcam_work_queue(isop->iso_dev, &ibp->ib_workitem);
- if (res) {
- assert(res == -EBUSY);
- list_del(&ibp->ib_links);
- list_add_tail(&ibp->ib_links, &isop->iso_unused_list);
- goto done;
- }
-
- if (isop->iso_resubmit_err &&
- (isop->iso_resubmit_err != -ENOSPC)) {
- /* Nothing to do here... */
- usbcam_dbg(isop->iso_dev, ISOSTREAM,
- "not resubmitting, pending error");
- goto done;
- }
-
- if (list_empty(&isop->iso_unused_list)) {
- isop->iso_resubmit_err = -ENOSPC;
- usbcam_dbg(isop->iso_dev, ISOSTREAM,
- "not resubmitting, no URBs available");
- goto done;
- }
-
- ibp = list_entry(isop->iso_unused_list.next,
- struct usbcam_isobuf, ib_links);
-
- usbcam_isostream_resubmit(isop, ibp);
-
-done:
- spin_unlock_irqrestore(&isop->iso_lock, flags);
-}
-
-static void usbcam_isostream_freebuf(struct usbcam_isobuf *ibp)
-{
- assert(list_empty(&ibp->ib_links));
-
- usbcam_urb_freebuf(ibp->ib_urb);
- usb_free_urb(ibp->ib_urb);
- kfree(ibp);
-}
-
-static struct usbcam_isobuf *
-usbcam_isostream_allocbuf(struct usbcam_isostream *isop)
-{
- struct usbcam_isobuf *ibp;
- size_t nbytes;
- int i;
-
- ibp = kzalloc(sizeof(*ibp), GFP_KERNEL);
- if (!ibp)
- return NULL;
-
- INIT_LIST_HEAD(&ibp->ib_links);
- usbcam_work_init(&ibp->ib_workitem, usbcam_isostream_urb_process);
- ibp->ib_isostream = isop;
-
- ibp->ib_urb = usb_alloc_urb(isop->iso_packet_count, GFP_KERNEL);
- if (!ibp->ib_urb) {
- kfree(ibp);
- return NULL;
- }
-
- nbytes = (isop->iso_packet_len * isop->iso_packet_count);
-
- if (usbcam_urb_allocbuf(isop->iso_dev, ibp->ib_urb, nbytes)) {
- usb_free_urb(ibp->ib_urb);
- kfree(ibp);
- return NULL;
- }
-
- ibp->ib_urb->context = ibp;
- ibp->ib_urb->number_of_packets = isop->iso_packet_count;
-
-
- for (i = 0; i < isop->iso_packet_count; i++) {
- ibp->ib_urb->iso_frame_desc[i].offset =
- (i * isop->iso_packet_len);
- ibp->ib_urb->iso_frame_desc[i].length = isop->iso_packet_len;
- ibp->ib_urb->iso_frame_desc[i].actual_length = 0;
- ibp->ib_urb->iso_frame_desc[i].status = 0;
- }
-
- return ibp;
-}
-
-static void usbcam_isostream_freebufs(struct list_head *head)
-{
- struct usbcam_isobuf *ibp;
- while (!list_empty(head)) {
- ibp = list_entry(head->next, struct usbcam_isobuf, ib_links);
- list_del_init(&ibp->ib_links);
- usbcam_isostream_freebuf(ibp);
- }
-}
-
-static int usbcam_isostream_allocbufs(struct usbcam_isostream *isop,
- struct list_head *head, int count)
-{
- struct usbcam_isobuf *ibp;
- struct list_head new_bufs;
-
- INIT_LIST_HEAD(&new_bufs);
-
- while (count--) {
- ibp = usbcam_isostream_allocbuf(isop);
- if (!ibp) {
- usbcam_isostream_freebufs(&new_bufs);
- return -ENOMEM;
- }
- list_add_tail(&ibp->ib_links, &new_bufs);
- }
-
- list_splice(&new_bufs, head);
- return 0;
-}
-
-static void usbcam_isostream_error(struct usbcam_workitem *work)
-{
- struct usbcam_isostream *isop = container_of(work,
- struct usbcam_isostream,
- iso_error_workitem);
- unsigned long flags;
- int sts;
-
- spin_lock_irqsave(&isop->iso_lock, flags);
- sts = isop->iso_resubmit_err;
- isop->iso_resubmit_err = 0;
- spin_unlock_irqrestore(&isop->iso_lock, flags);
-
- if (sts && isop->iso_ops && isop->iso_ops->submit_error)
- isop->iso_ops->submit_error(isop->iso_dev, isop, sts);
-}
-
-int usbcam_isostream_init(struct usbcam_isostream *isop,
- struct usbcam_dev *udp,
- int ep, struct usbcam_isostream_ops *ops,
- int pktcount, int nurbs, int interval, int pktlen)
-{
- int res;
-
- if (!interval) {
- /* FIXME: find the appropriate interval for the endpoint */
- return -EINVAL;
- }
-
- if (!pktlen) {
- /* Choose a packet length based on the current altsetting */
- pktlen = usb_maxpacket(udp->ud_dev,
- usb_rcvisocpipe(udp->ud_dev, ep), 0);
- if (!pktlen) {
- usbcam_dbg(udp, ISOSTREAM,
- "current altsetting for ep%d has "
- "maxpacket=0", ep);
- return -EINVAL;
- }
- if (udp->ud_dev->speed == USB_SPEED_HIGH)
- pktlen = (pktlen & 0x7ff) *
- (((pktlen >> 11) & 0x3) + 1);
- else
- pktlen &= 0x7ff;
-
- usbcam_dbg(udp, ISOSTREAM, "isostream using pktlen %d",
- pktlen);
- }
-
- if (!pktcount)
- pktcount = USBCAM_DFL_ISO_URB_PKTS;
- if (!nurbs)
- nurbs = USBCAM_DFL_ISO_URBS;
- if (nurbs < 2) {
- usbcam_warn(udp, "%s: at least two iso URBs are required",
- __FUNCTION__);
- nurbs = 2;
- }
-
- memset(isop, 0, sizeof(*isop));
- isop->iso_dev = udp;
- isop->iso_endpoint = ep;
- isop->iso_packet_len = pktlen;
- isop->iso_packet_count = pktcount;
- isop->iso_urb_interval = interval;
- isop->iso_ops = ops;
- spin_lock_init(&isop->iso_lock);
- INIT_LIST_HEAD(&isop->iso_unused_list);
- INIT_LIST_HEAD(&isop->iso_active_list);
- INIT_LIST_HEAD(&isop->iso_complete_list);
- usbcam_work_init(&isop->iso_error_workitem, usbcam_isostream_error);
-
- res = usbcam_isostream_allocbufs(isop, &isop->iso_unused_list, nurbs);
- return res;
-}
-EXPORT_SYMBOL(usbcam_isostream_init);
-
-void usbcam_isostream_stop(struct usbcam_isostream *isop)
-{
- long flags;
- struct usbcam_isobuf *ibp, *prev;
- int res;
-
- usbcam_chklock(isop->iso_dev);
-
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso stream stopping");
- spin_lock_irqsave(&isop->iso_lock, flags);
-
- if (isop->iso_streaming) {
- isop->iso_streaming = 0;
-
- /* Cancel all in-flight requests */
- while (!list_empty(&isop->iso_active_list)) {
- ibp = list_entry(isop->iso_active_list.prev,
- struct usbcam_isobuf, ib_links);
- list_del_init(&ibp->ib_links);
- spin_unlock_irqrestore(&isop->iso_lock, flags);
- usb_kill_urb(ibp->ib_urb);
- spin_lock_irqsave(&isop->iso_lock, flags);
- list_add(&ibp->ib_links, &isop->iso_unused_list);
- }
-
- /* Cancel all done queue work items */
- list_for_each_entry_safe(ibp, prev,
- &isop->iso_complete_list,
- ib_links) {
-
- res = usbcam_work_cancel(isop->iso_dev,
- &ibp->ib_workitem);
-
- if (!ibp->ib_worker)
- assert(!res);
- else {
- assert(res);
- assert(*ibp->ib_worker == current);
- *ibp->ib_worker = NULL;
- ibp->ib_worker = NULL;
- }
-
- list_del(&ibp->ib_links);
- list_add_tail(&ibp->ib_links, &isop->iso_unused_list);
- }
-
- /* Cancel the error work item */
- (void) usbcam_work_cancel(isop->iso_dev,
- &isop->iso_error_workitem);
- }
-
- /* Clear the resubmission error code */
- isop->iso_resubmit_err = 0;
-
- spin_unlock_irqrestore(&isop->iso_lock, flags);
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso stream stopped");
-}
-EXPORT_SYMBOL(usbcam_isostream_stop);
-
-int usbcam_isostream_start(struct usbcam_isostream *isop)
-{
- long flags;
- struct usbcam_dev *udp;
- struct usbcam_isobuf *ibp;
- int submitted = 0, res;
-
- udp = isop->iso_dev;
-
- usbcam_chklock(udp);
-
- spin_lock_irqsave(&isop->iso_lock, flags);
- if (isop->iso_streaming) {
- spin_unlock_irqrestore(&isop->iso_lock, flags);
- usbcam_warn(udp, "%s: already streaming", __FUNCTION__);
- return -EEXIST;
- }
-
- if (list_empty(&isop->iso_unused_list) ||
- (isop->iso_unused_list.next == isop->iso_unused_list.prev)) {
- spin_unlock_irqrestore(&isop->iso_lock, flags);
- usbcam_warn(udp, "%s: not enough unused iso URBs",
- __FUNCTION__);
- return -ENOENT;
- }
-
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso stream starting");
- isop->iso_streaming = 1;
-
- /*
- * Fill out generic details of each URB.
- * The interval and endpoints can be changed after init, but
- * the packet size and URB count cannot.
- */
- list_for_each_entry(ibp, &isop->iso_unused_list, ib_links) {
- ibp->ib_urb->pipe = usb_rcvisocpipe(ibp->ib_urb->dev,
- isop->iso_endpoint);
- ibp->ib_urb->interval = isop->iso_urb_interval;
- ibp->ib_urb->transfer_flags |= URB_ISO_ASAP;
- ibp->ib_urb->complete = usbcam_isostream_urb_complete;
- ibp->ib_urb->start_frame = 0;
- }
-
- /*
- * Submit two (2) URBs.
- */
- while (1) {
- assert(!list_empty(&isop->iso_unused_list));
- ibp = list_entry(isop->iso_unused_list.next,
- struct usbcam_isobuf, ib_links);
-
- list_del(&ibp->ib_links);
- list_add_tail(&ibp->ib_links, &isop->iso_active_list);
-
- spin_unlock_irqrestore(&isop->iso_lock, flags);
-
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso submit %p", ibp);
- res = usb_submit_urb(ibp->ib_urb, GFP_KERNEL);
- if (res == -EL2NSYNC)
- res = usb_submit_urb(ibp->ib_urb, GFP_KERNEL);
-
- if (res) {
- usbcam_dbg(isop->iso_dev, ISOSTREAM,
- "%s: ISO URB submit failed: %d",
- __FUNCTION__, res);
- usbcam_isostream_stop(isop);
- return res;
- }
-
- if (++submitted == 2)
- break;
-
- spin_lock_irqsave(&isop->iso_lock, flags);
- }
-
- usbcam_dbg(isop->iso_dev, ISOSTREAM, "iso stream started");
- return 0;
-
-}
-EXPORT_SYMBOL(usbcam_isostream_start);
-
-void usbcam_isostream_cleanup(struct usbcam_isostream *isop)
-{
- usbcam_isostream_stop(isop);
- usbcam_isostream_freebufs(&isop->iso_unused_list);
- assert(list_empty(&isop->iso_active_list));
- assert(list_empty(&isop->iso_complete_list));
-}
-EXPORT_SYMBOL(usbcam_isostream_cleanup);
-
-
-#ifdef usbcam_hexdump
-#undef usbcam_hexdump
-#endif
-#define dumpable_char(X) (((X) >= ' ') && ((X) <= '~'))
-void usbcam_hexdump(struct usbcam_dev *udp, const u8 *buf, size_t len)
-{
- const int bpl = 16;
- const int cend_max = ((bpl * 4) + 1);
- static const char n2x[16] = { '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
- char x, outbuf[cend_max + 1];
- unsigned int cstart, cend, xpos, cpos, offset;
-
- offset = 0;
- cstart = (3 * bpl) + 1;
- cend = cstart + bpl;
- outbuf[cend] = '\0';
- goto beginning;
-
- while (len) {
- x = *buf++;
- outbuf[xpos++] = n2x[(x >> 4) & 0xf];
- outbuf[xpos++] = n2x[x & 0xf];
- outbuf[cpos+cstart] = dumpable_char(x) ? x : '.';
- cpos++;
- xpos++;
- len--;
-
- if (!len || (cpos == bpl)) {
- printk(KERN_DEBUG "%s: %08x %s\n",
- udp->ud_dev_name, offset, outbuf);
- offset += bpl;
-
- beginning:
- memset(outbuf, ' ', cend);
- cpos = 0;
- xpos = 0;
- }
- }
-}
-EXPORT_SYMBOL(usbcam_hexdump);
-
-
-MODULE_DESCRIPTION("Abstraction Library for USB Webcam Drivers");
-MODULE_AUTHOR("Sam Revitch <samr7@cs.washington.edu>, "
- "Alexander Hixon <hixon.alexander@mediati.org>");
-MODULE_LICENSE("GPL");