r5u870

Ricoh R5U870 Linux Driver
git clone https://logand.com/git/r5u870.git/
Log | Files | Refs | README | LICENSE

commit 3df74894a778e5c638e91d1d93d72960d07760b8
parent afb1b4a19c0aeb4d21352bc0a7ddb8541901d0c5
Author: alex <alex@022568fa-442e-4ef8-a3e8-54dcafdb011a>
Date:   Wed, 16 Jan 2008 23:51:34 +0000

* Remove these two. They accidentally didn't get removed as my local filesystem was out of sync.


git-svn-id: http://svn.mediati.org/svn/r5u870/trunk@37 022568fa-442e-4ef8-a3e8-54dcafdb011a

Diffstat:
Dusbcam.c | 3477-------------------------------------------------------------------------------
Dusbcam.h | 721-------------------------------------------------------------------------------
2 files changed, 0 insertions(+), 4198 deletions(-)

diff --git a/usbcam.c b/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"); diff --git a/usbcam.h b/usbcam.h @@ -1,721 +0,0 @@ -/* - * USBCAM abstraction library for USB webcam drivers - * Version 0.10.2 - * - * Copyright (C) 2007 Sam Revitch <samr7@cs.washington.edu> - * - * 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 - */ - -/* - * This library is intended to simplify the process of creating drivers - * for simpler USB webcam devices. It handles most V4L interactions, and - * all USB driver entry points. It provides a minidriver callback API - * for handling most common tasks. - */ - -#ifndef __USBCAM_H__ -#define __USBCAM_H__ - -#ifdef __KERNEL__ - -#include <linux/usb.h> -#include <linux/mutex.h> -#include <linux/module.h> -#include <linux/version.h> - -#include <linux/videodev2.h> -#include <media/v4l2-common.h> - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) -#include <media/video-buf.h> -#else -#include <media/videobuf-dma-sg.h> -#endif - - -/* The actual per-minidriver structure is opaque */ -typedef struct usbcam_minidrv usbcam_minidrv_t; -struct usbcam_dev; - - -/* - * Log file and debug infrastructure - */ -#define usbcam_info(UDP, FMT, ARG...) do { \ - printk(KERN_INFO "%s: " FMT "\n", \ - (UDP)->ud_dev_name, ## ARG); \ -} while (0) -#define usbcam_warn(UDP, FMT, ARG...) do { \ - printk(KERN_WARNING "%s: " FMT "\n", \ - (UDP)->ud_dev_name, ## ARG); \ -} while (0) -#define usbcam_err(UDP, FMT, ARG...) do { \ - printk(KERN_ERR "%s: " FMT "\n", \ - (UDP)->ud_dev_name, ## ARG); \ -} while (0) - - -#if defined(CONFIG_USBCAM_DEBUG) -#define usbcam_dbg(UDP, SUBSYS, FMT, ARG...) do { \ - if ((UDP)->ud_debug && \ - (*(UDP)->ud_debug & (1UL << USBCAM_DBG_ ## SUBSYS))) \ - printk(KERN_DEBUG "%s: " FMT "\n", \ - (UDP)->ud_dev_name, ## ARG); \ -} while (0) -#define usbcam_assert(expr) \ -do { \ - if (!(expr)) { \ - printk(KERN_ERR "%s:%d: assertion \"" # expr "\" " \ - "failed", __FILE__, __LINE__); \ - dump_stack(); \ - } \ -} while (0) -extern void usbcam_hexdump(struct usbcam_dev *udp, const u8 *buf, size_t len); -#else -#define usbcam_dbg(UDP, SUBSYS, FMT, ARG...) -#define usbcam_assert(expr) -#define usbcam_hexdump(udp, buf, len) -#endif - -/* - * Debug subsystem bit values - * - * The usbcam_dev.ud_debug debug subsystem mask is a pointer to a - * per-minidriver integer, which is usually a module parameter and can - * be manipulated via sysfs. - */ -enum { - USBCAM_DBG_VIDEOBUF = 0, - USBCAM_DBG_CAPTURE, - USBCAM_DBG_IOCTL_BUF, - USBCAM_DBG_IOCTL_FMT, - USBCAM_DBG_IOCTL_MISC, - USBCAM_DBG_DEV_STATE, - USBCAM_DBG_ALTSETTING, - USBCAM_DBG_ISOSTREAM, - - /* First bit available to minidrivers */ - USBCAM_DBGBIT_MD_START = 8, -}; - - - -/* - * The usbcam_pix_fmt structure is used to describe a pixel format natively - * supported by the driver to V4L clients. A minidriver may set the - * ud_fmt_array member of usbcam_dev to point to an array of these - * structures, and the array will be used to service the ENUMFMT ioctl, - * as long as the minidriver does not intercept that ioctl. - */ -struct usbcam_pix_fmt { - char description[32]; - unsigned int pixelformat; - unsigned int flags; -}; - - -/* - * The control structure is interpreted by the usbcam ioctl handler to - * answer QUERYCTRL/G_CTRL/S_CTRL/QUERYMENU requests. A minidriver may - * set the ud_ctrl_array field in usbcam_dev to point to an array of - * usbcam_ctrl structures, or larger structures that have a usbcam_ctrl - * as their first member. - * - * This mode of handling control-related ioctls is only used if the - * minidriver does not intercept and handle the appropriate ioctl - * commands in its ioctl callout. - */ -struct usbcam_ctrl { - struct v4l2_queryctrl ctrl; - - const char **menu_names; - - /* Retrieves details about the control dynamically, may be NULL */ - int (*query_fn)(struct usbcam_dev *, const struct usbcam_ctrl *, - struct v4l2_queryctrl *); - - /* Retrieves the current value of the control */ - int (*get_fn)(struct usbcam_dev *, const struct usbcam_ctrl *, - struct v4l2_control *); - - /* If set_fn is not set, the control is considered read-only. */ - int (*set_fn)(struct usbcam_dev *, const struct usbcam_ctrl *, - const struct v4l2_control *); -}; - -/* - * usbcam_dev: The per-device structure representing: - * (1) A USB device/interface - * (2) A registered V4L device - * - * Commented fields are of interest to minidrivers. - * Uncommented fields should be considered opaque. - */ - -struct usbcam_dev { - /* - * ud_vdev is the video4linux device structure. - * The minidriver may be interested in a few fields: - * ud_vdev.name: The device name displayed by applications - */ - struct video_device ud_vdev; - - /* - * ud_dev, ud_intf: The associated USB device/primary interface - * These members are read-only to all except when set during - * usbcam_dev structure initialization. - */ - struct usb_device *ud_dev; - struct usb_interface *ud_intf; - - usbcam_minidrv_t *ud_minidrv; - - /* - * ud_minidrv_data: Minidriver private data - * During structure initialization, if a minidriver structure - * size was specified to usbcam_register(), that many bytes of - * memory are allocated, the allocation is assigned here, and - * the original allocation is automatically freed with the - * usbcam_dev structure. Otherwise this member is initialized - * to NULL. - * The minidriver may set whatever policies it wants with how - * this field is managed. - */ - void *ud_minidrv_data; - - struct list_head ud_drv_links; - - /* - * ud_minidrv_id: The device's unique number among all devices - * belonging to the minidriver. Set prior to the minidriver's - * init handler being called. Read-only at all other times. - */ - int ud_minidrv_id; - - /* - * ud_lock: the mutex protecting most of this structure and all - * minidriver entry points. - */ - struct mutex ud_lock; - - /* - * ud_format: Currently configured size/format - * Protected by ud_lock - * Modified only by minidriver - * Examined by ioctl handler and framebuffer allocator - */ - struct v4l2_pix_format ud_format; - - /* - * ud_fmt_array, ud_fmt_array_elem_size, ud_fmt_array_len: - * Supported pixel formats for enumeration - * Protected by ud_lock - * Modified only by minidriver, usually set by init callout - * Examined by default ioctl handler for ENUMFMT - */ - const struct usbcam_pix_fmt *ud_fmt_array; - int ud_fmt_array_elem_size; - int ud_fmt_array_len; - - /* - * ud_ctrl_array, ud_ctrl_array_elem_size, ud_ctrl_array_len: - * Supported control knobs with get/set callouts - * Protected by ud_ctrl_lock - * Modified only by minidriver, usually set by init callout - * Examined by default ioctl handlers for: - * QUERYCTRL, G_CTRL, S_CTRL, QUERYMENU - */ - const struct usbcam_ctrl *ud_ctrl_array; - int ud_ctrl_array_elem_size; - int ud_ctrl_array_len; - - /* - * ud_capturing: Minidriver capture-in-progress flag - * Protected by ud_lock - * Modified only by minidriver, e.g. cap_start, cap_stop - * Examined by framebuffer interface and S_FMT ioctl handler - */ - unsigned int ud_capturing : 1, - - /* - * ud_disconnected: Set if the underlying USB device has been - * disconnected, just prior to invoking the minidriver disconnect - * callout, if one is provided. This field should be considered - * read-only to minidrivers. - */ - ud_disconnected : 1, - - ud_initializing : 1, - ud_suspended : 1, - ud_disconnected_primary : 1, - ud_videodev_released : 1; - - struct kref ud_kref; - struct list_head ud_interface_list; - - struct list_head ud_frame_cap_queue; - - int *ud_debug; - - int ud_work_refs; - spinlock_t ud_work_lock; - int ud_work_lockwait; - struct task_struct *ud_work_thread; - struct list_head ud_work_queue; - - struct mutex ud_open_lock; - int ud_user_refs; - unsigned int ud_autopm_ref : 1; - struct file *ud_excl_owner; - - /* - * ud_dev_name: Name of device as used for worker thread names and - * debug messages. The minidriver may set this field in its init - * callout, but should consider it read-only after that point. - */ - char ud_dev_name[32]; -}; - - -/* - * Per-device reference counting helpers - * - * When the last reference is released, the minidriver's release - * callout will be invoked. - * - * usbcam_get() may be called from any context. - * usbcam_put() can sleep, and may not be called while holding the device - * lock (see below). - */ -#define usbcam_get(UDP) kref_get(&(UDP)->ud_kref) -extern void usbcam_put(struct usbcam_dev *udp); - - -/* - * Per-Device locking helpers - * - * The minidriver callouts, which are described below in usbcam_dev_ops - * and usbcam_isostream_ops, are all invoked with the device lock held. - * Also, all usbcam work queue callouts are invoked with the device lock - * held. - * - * Minidrivers that must be entered through paths other than those - * described above may need to examine or update data structures - * protected by the device lock, and may do so using the lock - * acquire/release macros. For example, minidrivers that use - * procfs/sysfs file operations, or completion callouts unsuitable for - * the usbcam work queue will need this. - * - * Minidrivers may release and reacquire the lock inside of certain - * usbcam minidriver callouts (see - */ -#define usbcam_lock(UDP) mutex_lock(&(UDP)->ud_lock) -#define usbcam_unlock(UDP) mutex_unlock(&(UDP)->ud_lock) -#define usbcam_chklock(UDP) usbcam_assert(mutex_is_locked(&(UDP)->ud_lock)) - - -/* - * MINIDRIVER CALLOUTS - * - * Callouts invoked at various stages of the lifetime of a usbcam_dev - * device. - * - * REQUIRED: init, cap_start, cap_stop. - * - * All other callouts are optional. - * - * By default, all callouts are invoked with the device lock held. - * - * The device lock is not intended to be held for long periods of time. - * Some operations may run for a long time, and may need to sleep - * waiting for an operation to complete on the device. If these - * operations need to be able to run at the same time as capture, the - * minidriver must ensure that it does not hold the device lock while - * waiting for such long operations to complete. - * - * To support operating without the device lock, there are flags - * in the ops structure to selectively disable this behavior for the - * ioctl callout, control callouts, and power management callouts. - * The minidriver will be responsible for acquiring and releasing the - * device lock, and re-verifying the device state upon reacquisition. - * - * Certain callouts are explicitly restricted from releasing and - * reacquiring the device lock. These include: - * - * disconnect, open, close, try_format, set_format, cap_start, cap_stop - * - */ -struct usbcam_dev_ops { - int unlocked_ioctl : 1, - unlocked_ctrl : 1, - unlocked_pm : 1, - supports_autosuspend : 1, - no_workref_on_open : 1; - - /* init: invoked when a matching USB device is discovered */ - int (*init)(struct usbcam_dev *udp, const struct usb_device_id *); - - /* suspend/resume: invoked from power management paths */ - int (*suspend)(struct usbcam_dev *udp, pm_message_t message); - int (*resume)(struct usbcam_dev *udp); - - /* disconnect: invoked when a device has been disconnected */ - void (*disconnect)(struct usbcam_dev *udp); - - /* release: invoked when a usbcam_dev is about to be freed */ - void (*release)(struct usbcam_dev *udp); - - /* open: invoked on first user open of the device */ - int (*open)(struct usbcam_dev *udp); - - /* close: invoked on last user close of the device */ - void (*close)(struct usbcam_dev *udp); - - /* try_format: invoked to negotiate capture format */ - int (*try_format)(struct usbcam_dev *udp, - struct v4l2_pix_format *fmt_in_out); - - /* set_format: invoked when the capture format is being set */ - int (*set_format)(struct usbcam_dev *udp, - struct v4l2_pix_format *fmt_in_out); - - /* ioctl: ioctl call interception for most commands */ - int (*ioctl)(struct usbcam_dev *udp, int cmd, void *arg); - - /* cap_start: invoked when capture should be initiated */ - int (*cap_start)(struct usbcam_dev *udp); - - /* cap_stop: invoked when capture should be halted */ - void (*cap_stop)(struct usbcam_dev *udp); -}; - - -/* - * Minidrivers may register themselves using usbcam_register_mod(), - * although the recommended method is to use the usbcam_register() - * macro instead. - * - * The driver should keep a usbcam_minidrv structure pointer as a - * handle to the usbcam registration, as it will need it later when it - * needs to unregister itself. usbcam_register_mod() accepts a pointer - * to this structure, and fills it in on success. - * - * The minidriver must report a primary version number in KERNEL_VERSION - * format, which is used by the default VIDIOC_QUERYCAP ioctl handler. - * The minidriver may optionally report an extra version string. - * - * usbcam_register_mod() will cause usbcam to register a new USB driver - * with the given device ID table, so that it may be called when - * matching devices are discovered. - * - * When a matching device is detected, usbcam will: - * -> Allocate a usbcam_dev structure, including a minidriver-specific - * portion of a given size, attached to ud_minidrv_data - * -> Invoke the ->init() callout for the minidriver - * -> If successful, register a new video4linux device and - * start accepting V4L API requests on it - * - * usbcam_register() depends on variables defined by the - * DEFINE_USBCAM_MODPARAMS macro to function correctly. This defines - * a set of static variables and marks them as module parameters. These - * include: - * -> video_nr: Favored video4linux minor numbers - * -> debug: A pointer to the debug level variable - */ - -extern int usbcam_register_mod(usbcam_minidrv_t **driverpp, - int mdrv_version, const char *mdrv_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_unregister() will remove a minidriver registration with the - * USB subsystem, and will prepare the minidriver structure to be - * freed. It is safe to call this API in paths other than minidriver - * module unload, as long as the minidriver is registered. - */ -extern void usbcam_unregister(usbcam_minidrv_t *driverp); - -#define DEFINE_USBCAM_MODPARAMS_BASE \ - static int video_nr_len = 0; \ - static int video_nr[8]; \ - module_param_array(video_nr, int, &video_nr_len, S_IRUGO|S_IWUSR); \ - MODULE_PARM_DESC(video_nr, "=n[,n...] Force /dev/videoX IDs"); - -#if defined(CONFIG_USBCAM_DEBUG) -#ifndef USBCAM_DEBUG_DEFAULT -#define USBCAM_DEBUG_DEFAULT 0x0020 -#endif -#define DEFINE_USBCAM_MODPARAMS \ - DEFINE_USBCAM_MODPARAMS_BASE \ - static int debug = USBCAM_DEBUG_DEFAULT; \ - module_param(debug, int, S_IRUGO|S_IWUSR); \ - MODULE_PARM_DESC(debug, "Enable debug trace messages"); -#define usbcam_register(DRVPP, MDV, VERX, CB, STRUCTSIZE, IDTBL) \ - usbcam_register_mod((DRVPP), (MDV), (VERX), (CB), (STRUCTSIZE), \ - (IDTBL), video_nr, video_nr_len, &debug, \ - THIS_MODULE, KBUILD_MODNAME) -#else -#define DEFINE_USBCAM_MODPARAMS \ - DEFINE_USBCAM_MODPARAMS_BASE -#define usbcam_register(DRVPP, MDV, VERX, CB, STRUCTSIZE, IDTBL) \ - usbcam_register_mod((DRVPP), (MDV), (VERX), (CB), (STRUCTSIZE), \ - (IDTBL), video_nr, video_nr_len, NULL, \ - THIS_MODULE, KBUILD_MODNAME) -#endif - - -/* - * For minidrivers that do not need to do anything other than register - * and unregister themselves as usbcam minidrivers when their modules - * are loaded and unloaded, the DEFINE_USBCAM_MINIDRV_MODULE macro - * is offered. - */ -#define DEFINE_USBCAM_MINIDRV_MODULE(VER, VERX, OPS, STRUCTSIZE, IDTBL) \ - DEFINE_USBCAM_MODPARAMS \ - static usbcam_minidrv_t *usbcam_minidrv_driver; \ - static int __init usbcam_minidrv_init(void) \ - { \ - return usbcam_register(&usbcam_minidrv_driver, \ - (VER), (VERX), (OPS), \ - (STRUCTSIZE), (IDTBL)); \ - } \ - static void __exit usbcam_minidrv_exit(void) \ - { \ - usbcam_unregister(usbcam_minidrv_driver); \ - } \ - module_init(usbcam_minidrv_init); \ - module_exit(usbcam_minidrv_exit); - - -/* - * Function for claiming additional interfaces - * - * This may only be called from the minidriver init callout - * - * For devices with multiple interfaces that pertain to a single driver, - * a separate usbcam_dev would ordinarily be created for each interface, - * and the init callout would be invoked. With this function, - * additional pertinent interfaces can be bound to the base usbcam_dev. - * - * Additional claimed interfaces will be automatically released at - * disconnect time, or in the event of a failure from the init callout. - * - * If the subject interface is already claimed by another USB driver, - * this function will fail with -EBUSY. - */ -extern int usbcam_claim_interface(struct usbcam_dev *udp, int ifnum); - - -/* - * Alternate setting choosing helper function - * - * This function assists in choosing an alternate setting based on - * overall bandwidth and packet size requirements for a given pipe. - */ -extern 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); - - -/* - * Minidrivers need to get the address to which the current frame - * buffer is mapped, and the allocated size of the frame buffer in - * order to do their job. The below functions facilitate that. - */ -struct usbcam_curframe { - u8 *uc_base; - size_t uc_size; - enum v4l2_field uc_field; - struct timeval uc_timestamp; -}; - -extern int usbcam_curframe_get(struct usbcam_dev *udp, - struct usbcam_curframe *cf); -extern void usbcam_curframe_complete_detail(struct usbcam_dev *udp, - int is_error, - struct usbcam_curframe *cf); -#define usbcam_curframe_complete(UDP, ISERR) \ - usbcam_curframe_complete_detail(UDP, ISERR, NULL) - -extern void usbcam_curframe_abortall(struct usbcam_dev *udp); - -/* - * usbcam_curframe_fill() is a glorified memset: it fills the current - * frame buffer, starting at offs, with nrecs instances of the repeating - * byte sequence pattern/patlen. - * - * It does range checking and will issue printk warnings if the frame - * buffer or the ud_format.sizeimage is overshot. - */ -extern void usbcam_curframe_fill(struct usbcam_dev *udp, size_t offset, - const void *pattern, int patlen, int nrecs); - -/* - * usbcam_curframe_testpattern() attempts to fill the current frame - * buffer with a blue test pattern. It paves over any partial frame - * data. If you wish to display part of an incomplete or interrupted - * frame, don't use this function. It is used by default to fill in - * frames that are completed with is_error = 1. - * - * This function is aware of bytesperline and padded line formats. - */ -extern int usbcam_curframe_testpattern(struct usbcam_dev *udp); - - -/* - * WORK ITEMS AND THE PER-DEVICE WORKER THREAD - * - * So long as a usbcam video4linux device is open, there is a worker - * thread available to execute deferred minidriver tasks. The worker - * thread becomes available prior to the open callout issued to the - * minidriver, and the last close of the device will block after the - * minidriver close callout finishes, waiting for the work queue to - * drain. - * - * A work item may be queued from any context, including interrupt - * contexts and URB completion callouts. - * - * Work item callouts are invoked in the context of the worker thread - * with the device mutex held. When a work item is queued, when the - * worker thread is next idle, it will wait for the device mutex, and - * will start the work item once it acquires the device mutex. Until - * that point, the work item may be canceled and dequeued using the - * usbcam_work_cancel() API, which may only be called while holding - * the device mutex. - * - * It was decided not to use the core kernel work queue - * implementation for the usbcam work queue because: - * 1. Work item cancelation is seen as a useful feature - * 2. Multithreading is not seen as a useful feature - */ - -struct usbcam_workitem; -typedef void (*usbcam_workfunc_t)(struct usbcam_workitem *work); - -struct usbcam_workitem { - struct list_head uw_links; - struct usbcam_dev *uw_dev; - usbcam_workfunc_t uw_func; -}; - -extern void usbcam_work_init(struct usbcam_workitem *wip, - usbcam_workfunc_t func); -extern int usbcam_work_queue(struct usbcam_dev *udp, - struct usbcam_workitem *wip); -extern int usbcam_work_cancel(struct usbcam_dev *udp, - struct usbcam_workitem *wip); - -/* - * usbcam_work_ref() and usbcam_work_unref() are for managing the start - * and stop of the worker thread. - */ -extern int usbcam_work_ref(struct usbcam_dev *udp); -extern void usbcam_work_unref(struct usbcam_dev *udp); - -/* - * usbcam_work_runqueue() will run work queue items in the current context - * until the work queue becomes empty. - */ -extern void usbcam_work_runqueue(struct usbcam_dev *udp); - - -/* - * Isochronous stream helper structure - * - * Minidrivers create any number of these, assign appropriate endpoints, - * and invoke start. Completed packets are automatically processed in the - * worker thread. - * - * This infrastructure may only be used when a device is opened, as the - * worker thread is shut down at other times. - */ - -struct usbcam_isostream; - -struct usbcam_isostream_ops { - void (*packet_done)(struct usbcam_dev *, struct usbcam_isostream *, - void *pktdata, int pktlen, int pktstatus); - void (*submit_error)(struct usbcam_dev *, struct usbcam_isostream *, - int status); -}; - -struct usbcam_isostream { - struct usbcam_dev *iso_dev; - int iso_endpoint; - int iso_packet_len; - int iso_packet_count; - int iso_urb_interval; - struct usbcam_isostream_ops *iso_ops; - spinlock_t iso_lock; - int iso_streaming; - struct list_head iso_unused_list; - struct list_head iso_active_list; - struct list_head iso_complete_list; - int iso_resubmit_err; - struct usbcam_workitem iso_error_workitem; -}; - -extern 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); -extern void usbcam_isostream_cleanup(struct usbcam_isostream *); -extern int usbcam_isostream_start(struct usbcam_isostream *); -extern void usbcam_isostream_stop(struct usbcam_isostream *); - - -/* - * Backward compatibility crap for slightly older 2.6 series kernels - */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) -#if !defined(V4L2_CTRL_FLAG_NEXT_CTRL) -#define V4L2_CTRL_FLAG_NEXT_CTRL 0 -#endif -#if !defined(V4L2_CTRL_FLAG_SLIDER) -#define V4L2_CTRL_FLAG_SLIDER 0 -#endif -#if !defined(V4L2_CTRL_FLAG_INACTIVE) -#define V4L2_CTRL_FLAG_INACTIVE 0 -#endif -#if !defined(V4L2_CTRL_FLAG_UPDATE) -#define V4L2_CTRL_FLAG_UPDATE 0 -#endif -#define usb_endpoint_xfer_bulk(EPD) \ - (((EPD)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == \ - USB_ENDPOINT_XFER_BULK) -#define usb_endpoint_xfer_int(EPD) \ - (((EPD)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == \ - USB_ENDPOINT_XFER_INT) -#define usb_endpoint_xfer_isoc(EPD) \ - (((EPD)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) == \ - USB_ENDPOINT_XFER_ISOC) -#define usb_endpoint_dir_in(EPD) \ - (((EPD)->bEndpointAddress & USB_ENDPOINT_DIR_MASK) == USB_DIR_IN) -#define usb_autopm_get_interface(X) 0 -#define usb_autopm_put_interface(X) -#endif /* LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) */ - -#endif /* __KERNEL__ */ - -#endif /* __USBCAM_H__ */