r5u870

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

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

* Remove old usbcam.c file - not used.


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

Diffstat:
MChangeLog | 4++++
Dusbcam/usbcam.c | 3477-------------------------------------------------------------------------------
2 files changed, 4 insertions(+), 3477 deletions(-)

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