summaryrefslogtreecommitdiff
path: root/drivers/staging/media/easycap
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/staging/media/easycap')
-rw-r--r--drivers/staging/media/easycap/Kconfig30
-rw-r--r--drivers/staging/media/easycap/Makefile10
-rw-r--r--drivers/staging/media/easycap/README141
-rw-r--r--drivers/staging/media/easycap/easycap.h594
-rw-r--r--drivers/staging/media/easycap/easycap_ioctl.c2450
-rw-r--r--drivers/staging/media/easycap/easycap_low.c1129
-rw-r--r--drivers/staging/media/easycap/easycap_main.c4253
-rw-r--r--drivers/staging/media/easycap/easycap_settings.c696
-rw-r--r--drivers/staging/media/easycap/easycap_sound.c816
-rw-r--r--drivers/staging/media/easycap/easycap_testcard.c155
10 files changed, 10274 insertions, 0 deletions
diff --git a/drivers/staging/media/easycap/Kconfig b/drivers/staging/media/easycap/Kconfig
new file mode 100644
index 000000000000..a425a6f9cdca
--- /dev/null
+++ b/drivers/staging/media/easycap/Kconfig
@@ -0,0 +1,30 @@
+config EASYCAP
+ tristate "EasyCAP USB ID 05e1:0408 support"
+ depends on USB && VIDEO_DEV && SND
+ select SND_PCM
+
+ ---help---
+ This is an integrated audio/video driver for EasyCAP cards with
+ USB ID 05e1:0408. It supports two hardware variants:
+
+ * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
+ having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
+
+ * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
+ 1, 2, 3, 4 and an unlabelled input cable for a microphone.
+
+ To compile this driver as a module, choose M here: the
+ module will be called easycap
+
+config EASYCAP_DEBUG
+ bool "Enable EasyCAP driver debugging"
+ depends on EASYCAP
+
+ ---help---
+ This option enables debug printouts
+
+ To enable debug, pass the debug level to the debug module
+ parameter:
+
+ modprobe easycap debug=[0..9]
+
diff --git a/drivers/staging/media/easycap/Makefile b/drivers/staging/media/easycap/Makefile
new file mode 100644
index 000000000000..a34e75f59c18
--- /dev/null
+++ b/drivers/staging/media/easycap/Makefile
@@ -0,0 +1,10 @@
+easycap-objs := easycap_main.o
+easycap-objs += easycap_low.o
+easycap-objs += easycap_ioctl.o
+easycap-objs += easycap_settings.o
+easycap-objs += easycap_testcard.o
+easycap-objs += easycap_sound.o
+obj-$(CONFIG_EASYCAP) += easycap.o
+
+ccflags-y := -Wall
+
diff --git a/drivers/staging/media/easycap/README b/drivers/staging/media/easycap/README
new file mode 100644
index 000000000000..796b032384bd
--- /dev/null
+++ b/drivers/staging/media/easycap/README
@@ -0,0 +1,141 @@
+
+ ***********************************************************
+ * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60 *
+ * and *
+ * EasyCAP002 4-Channel USB 2.0 DVR *
+ ***********************************************************
+ Mike Thomas <rmthomas@sciolus.org>
+
+
+
+SUPPORTED HARDWARE
+------------------
+
+This driver is intended for use with hardware having USB ID 05e1:0408.
+Two kinds of EasyCAP have this USB ID, namely:
+
+ * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60,
+ having input cables labelled CVBS, S-VIDEO, AUDIO(L), AUDIO(R)
+
+ * EasyCAP002 4-Channel USB 2.0 DVR, having input cables labelled
+ 1, 2, 3, 4 and an unlabelled input cable for a microphone.
+
+
+BUILD OPTIONS AND DEPENDENCIES
+------------------------------
+
+Unless EASYCAP_DEBUG is defined during compilation it will not be possible
+to select a debug level at the time of module installation.
+
+
+KNOWN RUNTIME ISSUES
+--------------------
+
+(1) Intentionally, this driver will not stream material which is unambiguously
+identified by the hardware as copy-protected. Normal video output will be
+present for about a minute but will then freeze when this situation arises.
+
+(2) The controls for luminance, contrast, saturation, hue and volume may not
+always work properly.
+
+(3) Reduced-resolution S-Video seems to suffer from moire artefacts.
+
+
+INPUT NUMBERING
+---------------
+
+For the EasyCAP with S-VIDEO input cable the driver regards a request for
+inputs numbered 0 or 1 as referring to CVBS and a request for input
+numbered 5 as referring to S-VIDEO.
+
+For the EasyCAP with four CVBS inputs the driver expects to be asked for
+any one of inputs numbered 1,2,3,4. If input 0 is asked for, it is
+interpreted as input 1.
+
+
+MODULE PARAMETERS
+-----------------
+
+Three module parameters are defined:
+
+debug the easycap module is configured at diagnostic level n (0 to 9)
+gain audio gain level n (0 to 31, default is 16)
+bars whether to display testcard bars when incoming video signal is lost
+ 0 => no, 1 => yes (default)
+
+
+SUPPORTED TV STANDARDS AND RESOLUTIONS
+--------------------------------------
+
+The following TV standards are natively supported by the hardware and are
+usable as (for example) the "norm=" parameter in the mplayer command:
+
+ PAL_BGHIN, NTSC_N_443,
+ PAL_Nc, NTSC_N,
+ SECAM, NTSC_M, NTSC_M_JP,
+ PAL_60, NTSC_443,
+ PAL_M.
+
+In addition, the driver offers "custom" pseudo-standards with a framerate
+which is 20% of the usual framerate. These pseudo-standards are named:
+
+ PAL_BGHIN_SLOW, NTSC_N_443_SLOW,
+ PAL_Nc_SLOW, NTSC_N_SLOW,
+ SECAM_SLOW, NTSC_M_SLOW, NTSC_M_JP_SLOW,
+ PAL_60_SLOW, NTSC_443_SLOW,
+ PAL_M_SLOW.
+
+
+The available picture sizes are:
+
+ at 25 frames per second: 720x576, 704x576, 640x480, 360x288, 320x240;
+ at 30 frames per second: 720x480, 640x480, 360x240, 320x240.
+
+
+WHAT'S TESTED AND WHAT'S NOT
+----------------------------
+
+This driver is known to work with mplayer, mencoder, tvtime, zoneminder,
+xawtv, gstreamer and sufficiently recent versions of vlc. An interface
+to ffmpeg is implemented, but serious audio-video synchronization problems
+remain.
+
+The driver is designed to support all the TV standards accepted by the
+hardware, but as yet it has actually been tested on only a few of these.
+
+I have been unable to test and calibrate the S-video input myself because I
+do not possess any equipment with S-video output.
+
+
+UDEV RULES
+----------
+
+In order that the special files /dev/easycap0 and /dev/easysnd1 are created
+with conveniently relaxed permissions when the EasyCAP is plugged in, a file
+is preferably to be provided in directory /etc/udev/rules.d with content:
+
+ACTION!="add|change", GOTO="easycap_rules_end"
+ATTRS{idVendor}=="05e1", ATTRS{idProduct}=="0408", \
+ MODE="0666", OWNER="root", GROUP="root"
+LABEL="easycap_rules_end"
+
+
+MODPROBE CONFIGURATION
+----------------------
+
+The easycap module is in competition with the module snd-usb-audio for the
+EasyCAP's audio channel, and its installation can be aided by providing a
+file in directory /etc/modprobe.d with content:
+
+options easycap gain=16 bars=1
+install easycap /sbin/rmmod snd-usb-audio; /sbin/modprobe --ignore-install easycap
+
+
+ACKNOWLEGEMENTS AND REFERENCES
+------------------------------
+This driver makes use of information contained in the Syntek Semicon DC-1125
+Driver, presently maintained at http://sourceforge.net/projects/syntekdriver/
+by Nicolas Vivien. Particularly useful has been a patch to the latter driver
+provided by Ivor Hewitt in January 2009. The NTSC implementation is taken
+from the work of Ben Trask.
+
diff --git a/drivers/staging/media/easycap/easycap.h b/drivers/staging/media/easycap/easycap.h
new file mode 100644
index 000000000000..7b256a948c27
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap.h
@@ -0,0 +1,594 @@
+/*****************************************************************************
+* *
+* easycap.h *
+* *
+*****************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * THE FOLLOWING PARAMETERS ARE UNDEFINED:
+ *
+ * EASYCAP_DEBUG
+ *
+ * IF REQUIRED THEY MUST BE EXTERNALLY DEFINED, FOR EXAMPLE AS COMPILER
+ * OPTIONS.
+ */
+/*---------------------------------------------------------------------------*/
+
+#ifndef __EASYCAP_H__
+#define __EASYCAP_H__
+
+/*---------------------------------------------------------------------------*/
+/*
+ * THESE ARE NORMALLY DEFINED
+ */
+/*---------------------------------------------------------------------------*/
+#define PATIENCE 500
+#define PERSEVERE
+/*---------------------------------------------------------------------------*/
+/*
+ * THESE ARE FOR MAINTENANCE ONLY - NORMALLY UNDEFINED:
+ */
+/*---------------------------------------------------------------------------*/
+#undef EASYCAP_TESTCARD
+/*---------------------------------------------------------------------------*/
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/kref.h>
+#include <linux/usb.h>
+#include <linux/uaccess.h>
+
+#include <linux/i2c.h>
+#include <linux/workqueue.h>
+#include <linux/poll.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/types.h>
+
+#include <linux/vmalloc.h>
+#include <linux/sound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/info.h>
+#include <sound/initval.h>
+#include <sound/control.h>
+#include <media/v4l2-dev.h>
+#include <media/v4l2-device.h>
+#include <linux/videodev2.h>
+#include <linux/soundcard.h>
+
+/*---------------------------------------------------------------------------*/
+/* VENDOR, PRODUCT: Syntek Semiconductor Co., Ltd
+ *
+ * EITHER EasyCAP USB 2.0 Video Adapter with Audio, Model No. DC60
+ * with input cabling: AUDIO(L), AUDIO(R), CVBS, S-VIDEO.
+ *
+ * OR EasyCAP 4CHANNEL USB 2.0 DVR, Model No. EasyCAP002
+ * with input cabling: MICROPHONE, CVBS1, CVBS2, CVBS3, CVBS4.
+ */
+/*---------------------------------------------------------------------------*/
+#define USB_EASYCAP_VENDOR_ID 0x05e1
+#define USB_EASYCAP_PRODUCT_ID 0x0408
+
+#define EASYCAP_DRIVER_VERSION "0.9.01"
+#define EASYCAP_DRIVER_DESCRIPTION "easycapdc60"
+
+#define USB_SKEL_MINOR_BASE 192
+#define DONGLE_MANY 8
+#define INPUT_MANY 6
+/*---------------------------------------------------------------------------*/
+/*
+ * DEFAULT LUMINANCE, CONTRAST, SATURATION AND HUE
+ */
+/*---------------------------------------------------------------------------*/
+#define SAA_0A_DEFAULT 0x7F
+#define SAA_0B_DEFAULT 0x3F
+#define SAA_0C_DEFAULT 0x2F
+#define SAA_0D_DEFAULT 0x00
+/*---------------------------------------------------------------------------*/
+/*
+ * VIDEO STREAMING PARAMETERS:
+ * USB 2.0 PROVIDES FOR HIGH-BANDWIDTH ENDPOINTS WITH AN UPPER LIMIT
+ * OF 3072 BYTES PER MICROFRAME for wMaxPacketSize.
+ */
+/*---------------------------------------------------------------------------*/
+#define VIDEO_ISOC_BUFFER_MANY 16
+#define VIDEO_ISOC_ORDER 3
+#define VIDEO_ISOC_FRAMESPERDESC ((unsigned int) 1 << VIDEO_ISOC_ORDER)
+#define USB_2_0_MAXPACKETSIZE 3072
+#if (USB_2_0_MAXPACKETSIZE > PAGE_SIZE)
+#error video_isoc_buffer[.] will not be big enough
+#endif
+#define VIDEO_JUNK_TOLERATE VIDEO_ISOC_BUFFER_MANY
+#define VIDEO_LOST_TOLERATE 50
+/*---------------------------------------------------------------------------*/
+/*
+ * VIDEO BUFFERS
+ */
+/*---------------------------------------------------------------------------*/
+#define FIELD_BUFFER_SIZE (203 * PAGE_SIZE)
+#define FRAME_BUFFER_SIZE (405 * PAGE_SIZE)
+#define FIELD_BUFFER_MANY 4
+#define FRAME_BUFFER_MANY 6
+/*---------------------------------------------------------------------------*/
+/*
+ * AUDIO STREAMING PARAMETERS
+ */
+/*---------------------------------------------------------------------------*/
+#define AUDIO_ISOC_BUFFER_MANY 16
+#define AUDIO_ISOC_ORDER 1
+#define AUDIO_ISOC_FRAMESPERDESC 32
+#define AUDIO_ISOC_BUFFER_SIZE (PAGE_SIZE << AUDIO_ISOC_ORDER)
+/*---------------------------------------------------------------------------*/
+/*
+ * AUDIO BUFFERS
+ */
+/*---------------------------------------------------------------------------*/
+#define AUDIO_FRAGMENT_MANY 32
+#define PAGES_PER_AUDIO_FRAGMENT 4
+/*---------------------------------------------------------------------------*/
+/*
+ * IT IS ESSENTIAL THAT EVEN-NUMBERED STANDARDS ARE 25 FRAMES PER SECOND,
+ * ODD-NUMBERED STANDARDS ARE 30 FRAMES PER SECOND.
+ * THE NUMBERING OF STANDARDS MUST NOT BE CHANGED WITHOUT DUE CARE. NOT
+ * ONLY MUST THE PARAMETER
+ * STANDARD_MANY
+ * BE CHANGED TO CORRESPOND TO THE NEW NUMBER OF STANDARDS, BUT ALSO THE
+ * NUMBERING MUST REMAIN AN UNBROKEN ASCENDING SEQUENCE: DUMMY STANDARDS
+ * MAY NEED TO BE ADDED. APPROPRIATE CHANGES WILL ALWAYS BE REQUIRED IN
+ * ROUTINE fillin_formats() AND POSSIBLY ELSEWHERE. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+#define PAL_BGHIN 0
+#define PAL_Nc 2
+#define SECAM 4
+#define NTSC_N 6
+#define NTSC_N_443 8
+#define NTSC_M 1
+#define NTSC_443 3
+#define NTSC_M_JP 5
+#define PAL_60 7
+#define PAL_M 9
+#define PAL_BGHIN_SLOW 10
+#define PAL_Nc_SLOW 12
+#define SECAM_SLOW 14
+#define NTSC_N_SLOW 16
+#define NTSC_N_443_SLOW 18
+#define NTSC_M_SLOW 11
+#define NTSC_443_SLOW 13
+#define NTSC_M_JP_SLOW 15
+#define PAL_60_SLOW 17
+#define PAL_M_SLOW 19
+#define STANDARD_MANY 20
+/*---------------------------------------------------------------------------*/
+/*
+ * ENUMS
+ */
+/*---------------------------------------------------------------------------*/
+enum {
+ AT_720x576,
+ AT_704x576,
+ AT_640x480,
+ AT_720x480,
+ AT_360x288,
+ AT_320x240,
+ AT_360x240,
+ RESOLUTION_MANY
+};
+enum {
+ FMT_UYVY,
+ FMT_YUY2,
+ FMT_RGB24,
+ FMT_RGB32,
+ FMT_BGR24,
+ FMT_BGR32,
+ PIXELFORMAT_MANY
+};
+enum {
+ FIELD_NONE,
+ FIELD_INTERLACED,
+ INTERLACE_MANY
+};
+#define SETTINGS_MANY (STANDARD_MANY * \
+ RESOLUTION_MANY * \
+ 2 * \
+ PIXELFORMAT_MANY * \
+ INTERLACE_MANY)
+/*---------------------------------------------------------------------------*/
+/*
+ * STRUCTURE DEFINITIONS
+ */
+/*---------------------------------------------------------------------------*/
+struct easycap_dongle {
+ struct easycap *peasycap;
+ struct mutex mutex_video;
+ struct mutex mutex_audio;
+};
+/*---------------------------------------------------------------------------*/
+struct data_buffer {
+ struct list_head list_head;
+ void *pgo;
+ void *pto;
+ u16 kount;
+ u16 input;
+};
+/*---------------------------------------------------------------------------*/
+struct data_urb {
+ struct list_head list_head;
+ struct urb *purb;
+ int isbuf;
+ int length;
+};
+/*---------------------------------------------------------------------------*/
+struct easycap_standard {
+ u16 mask;
+struct v4l2_standard v4l2_standard;
+};
+struct easycap_format {
+ u16 mask;
+ char name[128];
+struct v4l2_format v4l2_format;
+};
+struct inputset {
+ int input;
+ int input_ok;
+ int standard_offset;
+ int standard_offset_ok;
+ int format_offset;
+ int format_offset_ok;
+ int brightness;
+ int brightness_ok;
+ int contrast;
+ int contrast_ok;
+ int saturation;
+ int saturation_ok;
+ int hue;
+ int hue_ok;
+};
+/*---------------------------------------------------------------------------*/
+/*
+ * easycap.ilk == 0 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=256
+ * easycap.ilk == 2 => CVBS+S-VIDEO HARDWARE, AUDIO wMaxPacketSize=9
+ * easycap.ilk == 3 => FOUR-CVBS HARDWARE, AUDIO wMaxPacketSize=9
+ */
+/*---------------------------------------------------------------------------*/
+struct easycap {
+ int isdongle;
+ int minor;
+
+ struct video_device video_device;
+ struct v4l2_device v4l2_device;
+
+ int status;
+ unsigned int audio_pages_per_fragment;
+ unsigned int audio_bytes_per_fragment;
+ unsigned int audio_buffer_page_many;
+
+#define UPSAMPLE
+#ifdef UPSAMPLE
+ s16 oldaudio;
+#endif /*UPSAMPLE*/
+
+ int ilk;
+ bool microphone;
+
+ struct usb_device *pusb_device;
+ struct usb_interface *pusb_interface;
+
+ struct kref kref;
+
+ int queued[FRAME_BUFFER_MANY];
+ int done[FRAME_BUFFER_MANY];
+
+ wait_queue_head_t wq_video;
+ wait_queue_head_t wq_audio;
+ wait_queue_head_t wq_trigger;
+
+ int input;
+ int polled;
+ int standard_offset;
+ int format_offset;
+ struct inputset inputset[INPUT_MANY];
+
+ bool ntsc;
+ int fps;
+ int usec;
+ int tolerate;
+ int skip;
+ int skipped;
+ int lost[INPUT_MANY];
+ int merit[180];
+
+ long long int dnbydt;
+
+ int video_interface;
+ int video_altsetting_on;
+ int video_altsetting_off;
+ int video_endpointnumber;
+ int video_isoc_maxframesize;
+ int video_isoc_buffer_size;
+ int video_isoc_framesperdesc;
+
+ int video_isoc_streaming;
+ int video_isoc_sequence;
+ int video_idle;
+ int video_eof;
+ int video_junk;
+
+ struct data_buffer video_isoc_buffer[VIDEO_ISOC_BUFFER_MANY];
+ struct data_buffer field_buffer[FIELD_BUFFER_MANY]
+ [(FIELD_BUFFER_SIZE/PAGE_SIZE)];
+ struct data_buffer frame_buffer[FRAME_BUFFER_MANY]
+ [(FRAME_BUFFER_SIZE/PAGE_SIZE)];
+
+ struct list_head urb_video_head;
+ struct list_head *purb_video_head;
+
+ u8 cache[8];
+ u8 *pcache;
+ int video_mt;
+ int audio_mt;
+ long long audio_bytes;
+ u32 isequence;
+
+ int vma_many;
+/*---------------------------------------------------------------------------*/
+/*
+ * BUFFER INDICATORS
+ */
+/*---------------------------------------------------------------------------*/
+ int field_fill; /* Field buffer being filled by easycap_complete(). */
+ /* Bumped only by easycap_complete(). */
+ int field_page; /* Page of field buffer page being filled by */
+ /* easycap_complete(). */
+ int field_read; /* Field buffer to be read by field2frame(). */
+ /* Bumped only by easycap_complete(). */
+ int frame_fill; /* Frame buffer being filled by field2frame(). */
+ /* Bumped only by easycap_dqbuf() when */
+ /* field2frame() has created a complete frame. */
+ int frame_read; /* Frame buffer offered to user by DQBUF. */
+ /* Set only by easycap_dqbuf() to trail frame_fill.*/
+ int frame_lock; /* Flag set to 1 by DQBUF and cleared by QBUF */
+/*---------------------------------------------------------------------------*/
+/*
+ * IMAGE PROPERTIES
+ */
+/*---------------------------------------------------------------------------*/
+ u32 pixelformat;
+ int width;
+ int height;
+ int bytesperpixel;
+ bool byteswaporder;
+ bool decimatepixel;
+ bool offerfields;
+ int frame_buffer_used;
+ int frame_buffer_many;
+ int videofieldamount;
+
+ int brightness;
+ int contrast;
+ int saturation;
+ int hue;
+
+ int allocation_video_urb;
+ int allocation_video_page;
+ int allocation_video_struct;
+ int registered_video;
+/*---------------------------------------------------------------------------*/
+/*
+ * ALSA
+ */
+/*---------------------------------------------------------------------------*/
+ struct snd_pcm_hardware alsa_hardware;
+ struct snd_card *psnd_card;
+ struct snd_pcm *psnd_pcm;
+ struct snd_pcm_substream *psubstream;
+ int dma_fill;
+ int dma_next;
+ int dma_read;
+/*---------------------------------------------------------------------------*/
+/*
+ * SOUND PROPERTIES
+ */
+/*---------------------------------------------------------------------------*/
+ int audio_interface;
+ int audio_altsetting_on;
+ int audio_altsetting_off;
+ int audio_endpointnumber;
+ int audio_isoc_maxframesize;
+ int audio_isoc_buffer_size;
+ int audio_isoc_framesperdesc;
+
+ int audio_isoc_streaming;
+ int audio_idle;
+ int audio_eof;
+ int volume;
+ int mute;
+ s8 gain;
+
+ struct data_buffer audio_isoc_buffer[AUDIO_ISOC_BUFFER_MANY];
+
+ struct list_head urb_audio_head;
+ struct list_head *purb_audio_head;
+/*---------------------------------------------------------------------------*/
+/*
+ * BUFFER INDICATORS
+ */
+/*---------------------------------------------------------------------------*/
+ int audio_fill; /* Audio buffer being filled by easycap_complete(). */
+ /* Bumped only by easycap_complete(). */
+ int audio_read; /* Audio buffer page being read by easycap_read(). */
+ /* Set by easycap_read() to trail audio_fill by */
+ /* one fragment. */
+/*---------------------------------------------------------------------------*/
+/*
+ * SOUND PROPERTIES
+ */
+/*---------------------------------------------------------------------------*/
+
+ int audio_buffer_many;
+
+ int allocation_audio_urb;
+ int allocation_audio_page;
+ int allocation_audio_struct;
+ int registered_audio;
+
+ long long int audio_sample;
+ long long int audio_niveau;
+ long long int audio_square;
+
+ struct data_buffer audio_buffer[];
+};
+/*---------------------------------------------------------------------------*/
+/*
+ * VIDEO FUNCTION PROTOTYPES
+ */
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+long easycap_unlocked_ioctl(struct file *, unsigned int, unsigned long);
+int easycap_dqbuf(struct easycap *, int);
+int submit_video_urbs(struct easycap *);
+int kill_video_urbs(struct easycap *);
+int field2frame(struct easycap *);
+int redaub(struct easycap *, void *, void *,
+ int, int, u8, u8, bool);
+void easycap_testcard(struct easycap *, int);
+int fillin_formats(void);
+int newinput(struct easycap *, int);
+int adjust_standard(struct easycap *, v4l2_std_id);
+int adjust_format(struct easycap *, u32, u32, u32,
+ int, bool);
+int adjust_brightness(struct easycap *, int);
+int adjust_contrast(struct easycap *, int);
+int adjust_saturation(struct easycap *, int);
+int adjust_hue(struct easycap *, int);
+int adjust_volume(struct easycap *, int);
+/*---------------------------------------------------------------------------*/
+/*
+ * AUDIO FUNCTION PROTOTYPES
+ */
+/*---------------------------------------------------------------------------*/
+int easycap_alsa_probe(struct easycap *);
+void easycap_alsa_complete(struct urb *);
+
+int easycap_sound_setup(struct easycap *);
+int submit_audio_urbs(struct easycap *);
+int kill_audio_urbs(struct easycap *);
+void easyoss_testtone(struct easycap *, int);
+int audio_setup(struct easycap *);
+/*---------------------------------------------------------------------------*/
+/*
+ * LOW-LEVEL FUNCTION PROTOTYPES
+ */
+/*---------------------------------------------------------------------------*/
+int audio_gainget(struct usb_device *);
+int audio_gainset(struct usb_device *, s8);
+
+int set_interface(struct usb_device *, u16);
+int wakeup_device(struct usb_device *);
+int confirm_resolution(struct usb_device *);
+int confirm_stream(struct usb_device *);
+
+int setup_stk(struct usb_device *, bool);
+int setup_saa(struct usb_device *, bool);
+int setup_vt(struct usb_device *);
+int check_stk(struct usb_device *, bool);
+int check_saa(struct usb_device *, bool);
+int ready_saa(struct usb_device *);
+int merit_saa(struct usb_device *);
+int check_vt(struct usb_device *);
+int select_input(struct usb_device *, int, int);
+int set_resolution(struct usb_device *,
+ u16, u16, u16, u16);
+
+int read_saa(struct usb_device *, u16);
+int read_stk(struct usb_device *, u32);
+int write_saa(struct usb_device *, u16, u16);
+int write_000(struct usb_device *, u16, u16);
+int start_100(struct usb_device *);
+int stop_100(struct usb_device *);
+int write_300(struct usb_device *);
+int read_vt(struct usb_device *, u16);
+int write_vt(struct usb_device *, u16, u16);
+int isdongle(struct easycap *);
+/*---------------------------------------------------------------------------*/
+
+
+/*---------------------------------------------------------------------------*/
+/*
+ * MACROS SAM(...) AND JOM(...) ALLOW DIAGNOSTIC OUTPUT TO BE TAGGED WITH
+ * THE IDENTITY OF THE DONGLE TO WHICH IT APPLIES, BUT IF INVOKED WHEN THE
+ * POINTER peasycap IS INVALID AN Oops IS LIKELY, AND ITS CAUSE MAY NOT BE
+ * IMMEDIATELY OBVIOUS FROM A CASUAL READING OF THE SOURCE CODE. BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+const char *strerror(int err);
+
+#define SAY(format, args...) do { \
+ printk(KERN_DEBUG "easycap:: %s: " \
+ format, __func__, ##args); \
+} while (0)
+#define SAM(format, args...) do { \
+ printk(KERN_DEBUG "easycap::%i%s: " \
+ format, peasycap->isdongle, __func__, ##args);\
+} while (0)
+
+#ifdef CONFIG_EASYCAP_DEBUG
+extern int easycap_debug;
+#define JOT(n, format, args...) do { \
+ if (n <= easycap_debug) { \
+ printk(KERN_DEBUG "easycap:: %s: " \
+ format, __func__, ##args);\
+ } \
+} while (0)
+#define JOM(n, format, args...) do { \
+ if (n <= easycap_debug) { \
+ printk(KERN_DEBUG "easycap::%i%s: " \
+ format, peasycap->isdongle, __func__, ##args);\
+ } \
+} while (0)
+
+#else
+#define JOT(n, format, args...) do {} while (0)
+#define JOM(n, format, args...) do {} while (0)
+#endif /* CONFIG_EASYCAP_DEBUG */
+
+/*---------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------*/
+/* globals
+ */
+/*---------------------------------------------------------------------------*/
+
+extern bool easycap_readback;
+extern const struct easycap_standard easycap_standard[];
+extern struct easycap_format easycap_format[];
+extern struct v4l2_queryctrl easycap_control[];
+extern struct usb_driver easycap_usb_driver;
+extern struct easycap_dongle easycapdc60_dongle[];
+
+#endif /* !__EASYCAP_H__ */
diff --git a/drivers/staging/media/easycap/easycap_ioctl.c b/drivers/staging/media/easycap/easycap_ioctl.c
new file mode 100644
index 000000000000..c99addfb6242
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap_ioctl.c
@@ -0,0 +1,2450 @@
+/******************************************************************************
+* *
+* easycap_ioctl.c *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include <linux/version.h>
+#include "easycap.h"
+
+/*--------------------------------------------------------------------------*/
+/*
+ * UNLESS THERE IS A PREMATURE ERROR RETURN THIS ROUTINE UPDATES THE
+ * FOLLOWING:
+ * peasycap->standard_offset
+ * peasycap->inputset[peasycap->input].standard_offset
+ * peasycap->fps
+ * peasycap->usec
+ * peasycap->tolerate
+ * peasycap->skip
+ */
+/*---------------------------------------------------------------------------*/
+int adjust_standard(struct easycap *peasycap, v4l2_std_id std_id)
+{
+ struct easycap_standard const *peasycap_standard;
+ u16 reg, set;
+ int ir, rc, need, k;
+ unsigned int itwas, isnow;
+ bool resubmit;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if (std_id == peasycap_standard->v4l2_standard.id)
+ break;
+ peasycap_standard++;
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if (std_id & peasycap_standard->v4l2_standard.id)
+ break;
+ peasycap_standard++;
+ }
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ SAM("ERROR: 0x%08X=std_id: standard not found\n",
+ (unsigned int)std_id);
+ return -EINVAL;
+ }
+ SAM("selected standard: %s\n",
+ &(peasycap_standard->v4l2_standard.name[0]));
+ if (peasycap->standard_offset == peasycap_standard - easycap_standard) {
+ SAM("requested standard already in effect\n");
+ return 0;
+ }
+ peasycap->standard_offset = peasycap_standard - easycap_standard;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].standard_offset_ok) {
+ peasycap->inputset[k].standard_offset =
+ peasycap->standard_offset;
+ }
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].standard_offset =
+ peasycap->standard_offset;
+ peasycap->inputset[peasycap->input].standard_offset_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+
+ peasycap->fps = peasycap_standard->v4l2_standard.frameperiod.denominator /
+ peasycap_standard->v4l2_standard.frameperiod.numerator;
+ switch (peasycap->fps) {
+ case 6:
+ case 30: {
+ peasycap->ntsc = true;
+ break;
+ }
+ case 5:
+ case 25: {
+ peasycap->ntsc = false;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=frames-per-second\n", peasycap->fps);
+ return -ENOENT;
+ }
+ }
+ JOM(8, "%i frames-per-second\n", peasycap->fps);
+ if (0x8000 & peasycap_standard->mask) {
+ peasycap->skip = 5;
+ peasycap->usec = 1000000 / (2 * (5 * peasycap->fps));
+ peasycap->tolerate = 1000 * (25 / (5 * peasycap->fps));
+ } else {
+ peasycap->skip = 0;
+ peasycap->usec = 1000000 / (2 * peasycap->fps);
+ peasycap->tolerate = 1000 * (25 / peasycap->fps);
+ }
+ if (peasycap->video_isoc_streaming) {
+ resubmit = true;
+ kill_video_urbs(peasycap);
+ } else
+ resubmit = false;
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 44, TABLE 42
+ */
+/*--------------------------------------------------------------------------*/
+ need = 0;
+ itwas = 0;
+ reg = 0x00;
+ set = 0x00;
+ switch (peasycap_standard->mask & 0x000F) {
+ case NTSC_M_JP: {
+ reg = 0x0A;
+ set = 0x95;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
+ else
+ itwas = (unsigned int)ir;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register "
+ "0x%02X to 0x%02X for JP standard\n", reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+
+ reg = 0x0B;
+ set = 0x48;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: cannot read SAA register 0x%02X\n", reg);
+ else
+ itwas = (unsigned int)ir;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X "
+ "for JP standard\n", reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * NOTE: NO break HERE: RUN ON TO NEXT CASE
+ */
+/*--------------------------------------------------------------------------*/
+ }
+ case NTSC_M:
+ case PAL_BGHIN: {
+ reg = 0x0E;
+ set = 0x01;
+ need = 1;
+ break;
+ }
+ case NTSC_N_443:
+ case PAL_60: {
+ reg = 0x0E;
+ set = 0x11;
+ need = 1;
+ break;
+ }
+ case NTSC_443:
+ case PAL_Nc: {
+ reg = 0x0E;
+ set = 0x21;
+ need = 1;
+ break;
+ }
+ case NTSC_N:
+ case PAL_M: {
+ reg = 0x0E;
+ set = 0x31;
+ need = 1;
+ break;
+ }
+ case SECAM: {
+ reg = 0x0E;
+ set = 0x51;
+ need = 1;
+ break;
+ }
+ default:
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+ if (need) {
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X\n", reg);
+ else
+ itwas = (unsigned int)ir;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (0 != write_saa(peasycap->pusb_device, reg, set)) {
+ SAM("ERROR: failed to set SAA register "
+ "0x%02X to 0x%02X for table 42\n", reg, set);
+ } else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 41
+ */
+/*--------------------------------------------------------------------------*/
+ reg = 0x08;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X "
+ "so cannot reset\n", reg);
+ else {
+ itwas = (unsigned int)ir;
+ if (peasycap_standard->mask & 0x0001)
+ set = itwas | 0x40 ;
+ else
+ set = itwas & ~0x40 ;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
+ reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
+ reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 51, TABLE 57
+ */
+/*---------------------------------------------------------------------------*/
+ reg = 0x40;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X "
+ "so cannot reset\n", reg);
+ else {
+ itwas = (unsigned int)ir;
+ if (peasycap_standard->mask & 0x0001)
+ set = itwas | 0x80 ;
+ else
+ set = itwas & ~0x80 ;
+ rc = write_saa(peasycap->pusb_device, reg, set);
+ if (rc)
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
+ reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed to 0x%02X\n",
+ reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAA7113H DATASHEET PAGE 53, TABLE 66
+ */
+/*--------------------------------------------------------------------------*/
+ reg = 0x5A;
+ ir = read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ SAM("ERROR: failed to read SAA register 0x%02X but continuing\n", reg);
+ itwas = (unsigned int)ir;
+ if (peasycap_standard->mask & 0x0001)
+ set = 0x0A ;
+ else
+ set = 0x07 ;
+ if (0 != write_saa(peasycap->pusb_device, reg, set))
+ SAM("ERROR: failed to set SAA register 0x%02X to 0x%02X\n",
+ reg, set);
+ else {
+ isnow = (unsigned int)read_saa(peasycap->pusb_device, reg);
+ if (0 > ir)
+ JOM(8, "SAA register 0x%02X changed "
+ "to 0x%02X\n", reg, isnow);
+ else
+ JOM(8, "SAA register 0x%02X changed "
+ "from 0x%02X to 0x%02X\n", reg, itwas, isnow);
+ }
+ if (resubmit)
+ submit_video_urbs(peasycap);
+ return 0;
+}
+/*****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * THE ALGORITHM FOR RESPONDING TO THE VIDIO_S_FMT IOCTL REQUIRES
+ * A VALID VALUE OF peasycap->standard_offset, OTHERWISE -EBUSY IS RETURNED.
+ *
+ * PROVIDED THE ARGUMENT try IS false AND THERE IS NO PREMATURE ERROR RETURN
+ * THIS ROUTINE UPDATES THE FOLLOWING:
+ * peasycap->format_offset
+ * peasycap->inputset[peasycap->input].format_offset
+ * peasycap->pixelformat
+ * peasycap->height
+ * peasycap->width
+ * peasycap->bytesperpixel
+ * peasycap->byteswaporder
+ * peasycap->decimatepixel
+ * peasycap->frame_buffer_used
+ * peasycap->videofieldamount
+ * peasycap->offerfields
+ *
+ * IF SUCCESSFUL THE FUNCTION RETURNS THE OFFSET IN easycap_format[]
+ * IDENTIFYING THE FORMAT WHICH IS TO RETURNED TO THE USER.
+ * ERRORS RETURN A NEGATIVE NUMBER.
+ */
+/*--------------------------------------------------------------------------*/
+int adjust_format(struct easycap *peasycap,
+ u32 width, u32 height, u32 pixelformat, int field, bool try)
+{
+ struct easycap_format *peasycap_format, *peasycap_best_format;
+ u16 mask;
+ struct usb_device *p;
+ int miss, multiplier, best, k;
+ char bf[5], fo[32], *pc;
+ u32 uc;
+ bool resubmit;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (0 > peasycap->standard_offset) {
+ JOM(8, "%i=peasycap->standard_offset\n", peasycap->standard_offset);
+ return -EBUSY;
+ }
+ p = peasycap->pusb_device;
+ if (!p) {
+ SAM("ERROR: peaycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ pc = &bf[0];
+ uc = pixelformat;
+ memcpy((void *)pc, (void *)(&uc), 4);
+ bf[4] = 0;
+ mask = 0xFF & easycap_standard[peasycap->standard_offset].mask;
+ SAM("sought: %ix%i,%s(0x%08X),%i=field,0x%02X=std mask\n",
+ width, height, pc, pixelformat, field, mask);
+ switch (field) {
+ case V4L2_FIELD_ANY: {
+ strcpy(&fo[0], "V4L2_FIELD_ANY ");
+ break;
+ }
+ case V4L2_FIELD_NONE: {
+ strcpy(&fo[0], "V4L2_FIELD_NONE");
+ break;
+ }
+ case V4L2_FIELD_TOP: {
+ strcpy(&fo[0], "V4L2_FIELD_TOP");
+ break;
+ }
+ case V4L2_FIELD_BOTTOM: {
+ strcpy(&fo[0], "V4L2_FIELD_BOTTOM");
+ break;
+ }
+ case V4L2_FIELD_INTERLACED: {
+ strcpy(&fo[0], "V4L2_FIELD_INTERLACED");
+ break;
+ }
+ case V4L2_FIELD_SEQ_TB: {
+ strcpy(&fo[0], "V4L2_FIELD_SEQ_TB");
+ break;
+ }
+ case V4L2_FIELD_SEQ_BT: {
+ strcpy(&fo[0], "V4L2_FIELD_SEQ_BT");
+ break;
+ }
+ case V4L2_FIELD_ALTERNATE: {
+ strcpy(&fo[0], "V4L2_FIELD_ALTERNATE");
+ break;
+ }
+ case V4L2_FIELD_INTERLACED_TB: {
+ strcpy(&fo[0], "V4L2_FIELD_INTERLACED_TB");
+ break;
+ }
+ case V4L2_FIELD_INTERLACED_BT: {
+ strcpy(&fo[0], "V4L2_FIELD_INTERLACED_BT");
+ break;
+ }
+ default: {
+ strcpy(&fo[0], "V4L2_FIELD_... UNKNOWN ");
+ break;
+ }
+ }
+ SAM("sought: %s\n", &fo[0]);
+ if (V4L2_FIELD_ANY == field) {
+ field = V4L2_FIELD_NONE;
+ SAM("prefer: V4L2_FIELD_NONE=field, was V4L2_FIELD_ANY\n");
+ }
+ peasycap_best_format = NULL;
+ peasycap_format = &easycap_format[0];
+ while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
+ JOM(16, ".> %i %i 0x%08X %ix%i\n",
+ peasycap_format->mask & 0x01,
+ peasycap_format->v4l2_format.fmt.pix.field,
+ peasycap_format->v4l2_format.fmt.pix.pixelformat,
+ peasycap_format->v4l2_format.fmt.pix.width,
+ peasycap_format->v4l2_format.fmt.pix.height);
+
+ if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
+ (peasycap_format->v4l2_format.fmt.pix.field == field) &&
+ (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat) &&
+ (peasycap_format->v4l2_format.fmt.pix.width == width) &&
+ (peasycap_format->v4l2_format.fmt.pix.height == height)) {
+
+ peasycap_best_format = peasycap_format;
+ break;
+ }
+ peasycap_format++;
+ }
+ if (0 == peasycap_format->v4l2_format.fmt.pix.width) {
+ SAM("cannot do: %ix%i with standard mask 0x%02X\n",
+ width, height, mask);
+ peasycap_format = &easycap_format[0];
+ best = -1;
+ while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
+ if (((peasycap_format->mask & 0x1F) == (mask & 0x1F)) &&
+ (peasycap_format->v4l2_format.fmt.pix.field == field) &&
+ (peasycap_format->v4l2_format.fmt.pix.pixelformat == pixelformat)) {
+
+ miss = abs(peasycap_format->v4l2_format.fmt.pix.width - width);
+ if ((best > miss) || (best < 0)) {
+ best = miss;
+ peasycap_best_format = peasycap_format;
+ if (!miss)
+ break;
+ }
+ }
+ peasycap_format++;
+ }
+ if (-1 == best) {
+ SAM("cannot do %ix... with standard mask 0x%02X\n",
+ width, mask);
+ SAM("cannot do ...x%i with standard mask 0x%02X\n",
+ height, mask);
+ SAM(" %ix%i unmatched\n", width, height);
+ return peasycap->format_offset;
+ }
+ }
+ if (!peasycap_best_format) {
+ SAM("MISTAKE: peasycap_best_format is NULL");
+ return -EINVAL;
+ }
+ peasycap_format = peasycap_best_format;
+
+/*...........................................................................*/
+ if (try)
+ return peasycap_best_format - easycap_format;
+/*...........................................................................*/
+
+ if (false != try) {
+ SAM("MISTAKE: true==try where is should be false\n");
+ return -EINVAL;
+ }
+ SAM("actioning: %ix%i %s\n",
+ peasycap_format->v4l2_format.fmt.pix.width,
+ peasycap_format->v4l2_format.fmt.pix.height,
+ &peasycap_format->name[0]);
+ peasycap->height = peasycap_format->v4l2_format.fmt.pix.height;
+ peasycap->width = peasycap_format->v4l2_format.fmt.pix.width;
+ peasycap->pixelformat = peasycap_format->v4l2_format.fmt.pix.pixelformat;
+ peasycap->format_offset = peasycap_format - easycap_format;
+
+
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].format_offset_ok) {
+ peasycap->inputset[k].format_offset =
+ peasycap->format_offset;
+ }
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].format_offset =
+ peasycap->format_offset;
+ peasycap->inputset[peasycap->input].format_offset_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+
+
+
+ peasycap->bytesperpixel = (0x00E0 & peasycap_format->mask) >> 5 ;
+ if (0x0100 & peasycap_format->mask)
+ peasycap->byteswaporder = true;
+ else
+ peasycap->byteswaporder = false;
+ if (0x0200 & peasycap_format->mask)
+ peasycap->skip = 5;
+ else
+ peasycap->skip = 0;
+ if (0x0800 & peasycap_format->mask)
+ peasycap->decimatepixel = true;
+ else
+ peasycap->decimatepixel = false;
+ if (0x1000 & peasycap_format->mask)
+ peasycap->offerfields = true;
+ else
+ peasycap->offerfields = false;
+ if (peasycap->decimatepixel)
+ multiplier = 2;
+ else
+ multiplier = 1;
+ peasycap->videofieldamount =
+ multiplier * peasycap->width * multiplier * peasycap->height;
+ peasycap->frame_buffer_used =
+ peasycap->bytesperpixel * peasycap->width * peasycap->height;
+ if (peasycap->video_isoc_streaming) {
+ resubmit = true;
+ kill_video_urbs(peasycap);
+ } else
+ resubmit = false;
+/*---------------------------------------------------------------------------*/
+/*
+ * PAL
+ */
+/*---------------------------------------------------------------------------*/
+ if (0 == (0x01 & peasycap_format->mask)) {
+ if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (576 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (288 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0000, 0x0001, 0x05A0, 0x0121)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else if ((704 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (576 == peasycap_format->v4l2_format.fmt.pix.height)) {
+ if (set_resolution(p, 0x0004, 0x0001, 0x0584, 0x0121)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0014, 0x0020, 0x0514, 0x0110)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else {
+ SAM("MISTAKE: bad format, cannot set resolution\n");
+ return -EINVAL;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * NTSC
+ */
+/*---------------------------------------------------------------------------*/
+ } else {
+ if (((720 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((360 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0000, 0x0003, 0x05A0, 0x00F3)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else if (((640 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (480 == peasycap_format->v4l2_format.fmt.pix.height)) ||
+ ((320 == peasycap_format->v4l2_format.fmt.pix.width) &&
+ (240 == peasycap_format->v4l2_format.fmt.pix.height))) {
+ if (set_resolution(p, 0x0014, 0x0003, 0x0514, 0x00F3)) {
+ SAM("ERROR: set_resolution() failed\n");
+ return -EINVAL;
+ }
+ } else {
+ SAM("MISTAKE: bad format, cannot set resolution\n");
+ return -EINVAL;
+ }
+ }
+/*---------------------------------------------------------------------------*/
+ if (resubmit)
+ submit_video_urbs(peasycap);
+
+ return peasycap_best_format - easycap_format;
+}
+/*****************************************************************************/
+int adjust_brightness(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_BRIGHTNESS == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+ if ((easycap_control[i1].minimum <= peasycap->brightness) &&
+ (easycap_control[i1].maximum >= peasycap->brightness)) {
+ if (peasycap->brightness == value) {
+ SAM("unchanged brightness at 0x%02X\n",
+ value);
+ return 0;
+ }
+ }
+ peasycap->brightness = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].brightness_ok)
+ peasycap->inputset[k].brightness =
+ peasycap->brightness;
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].brightness =
+ peasycap->brightness;
+ peasycap->inputset[peasycap->input].brightness_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+ mood = 0x00FF & (unsigned int)peasycap->brightness;
+ if (!write_saa(peasycap->pusb_device, 0x0A, mood)) {
+ SAM("adjusting brightness to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust brightness "
+ "to 0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust brightness: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_contrast(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_CONTRAST == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+
+ if ((easycap_control[i1].minimum <= peasycap->contrast) &&
+ (easycap_control[i1].maximum >= peasycap->contrast)) {
+ if (peasycap->contrast == value) {
+ SAM("unchanged contrast at 0x%02X\n", value);
+ return 0;
+ }
+ }
+ peasycap->contrast = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].contrast_ok)
+ peasycap->inputset[k].contrast = peasycap->contrast;
+ }
+
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].contrast =
+ peasycap->contrast;
+ peasycap->inputset[peasycap->input].contrast_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+
+ mood = 0x00FF & (unsigned int) (peasycap->contrast - 128);
+ if (!write_saa(peasycap->pusb_device, 0x0B, mood)) {
+ SAM("adjusting contrast to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust contrast to "
+ "0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust contrast: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_saturation(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_SATURATION == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+
+ if ((easycap_control[i1].minimum <= peasycap->saturation) &&
+ (easycap_control[i1].maximum >= peasycap->saturation)) {
+ if (peasycap->saturation == value) {
+ SAM("unchanged saturation at 0x%02X\n",
+ value);
+ return 0;
+ }
+ }
+ peasycap->saturation = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].saturation_ok)
+ peasycap->inputset[k].saturation =
+ peasycap->saturation;
+ }
+ if ((0 <= peasycap->input) && (INPUT_MANY > peasycap->input)) {
+ peasycap->inputset[peasycap->input].saturation =
+ peasycap->saturation;
+ peasycap->inputset[peasycap->input].saturation_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+ mood = 0x00FF & (unsigned int) (peasycap->saturation - 128);
+ if (!write_saa(peasycap->pusb_device, 0x0C, mood)) {
+ SAM("adjusting saturation to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust saturation to "
+ "0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust saturation: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_hue(struct easycap *peasycap, int value)
+{
+ unsigned int mood;
+ int i1, i2, k;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_HUE == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+ if ((easycap_control[i1].minimum <= peasycap->hue) &&
+ (easycap_control[i1].maximum >= peasycap->hue)) {
+ if (peasycap->hue == value) {
+ SAM("unchanged hue at 0x%02X\n", value);
+ return 0;
+ }
+ }
+ peasycap->hue = value;
+ for (k = 0; k < INPUT_MANY; k++) {
+ if (!peasycap->inputset[k].hue_ok)
+ peasycap->inputset[k].hue = peasycap->hue;
+ }
+ if (0 <= peasycap->input && INPUT_MANY > peasycap->input) {
+ peasycap->inputset[peasycap->input].hue = peasycap->hue;
+ peasycap->inputset[peasycap->input].hue_ok = 1;
+ } else
+ JOM(8, "%i=peasycap->input\n", peasycap->input);
+ i2 = peasycap->hue - 128;
+ mood = 0x00FF & ((int) i2);
+ if (!write_saa(peasycap->pusb_device, 0x0D, mood)) {
+ SAM("adjusting hue to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust hue to 0x%02X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust hue: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+int adjust_volume(struct easycap *peasycap, int value)
+{
+ s8 mood;
+ int i1;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_AUDIO_VOLUME == easycap_control[i1].id) {
+ if ((easycap_control[i1].minimum > value) ||
+ (easycap_control[i1].maximum < value))
+ value = easycap_control[i1].default_value;
+
+ if ((easycap_control[i1].minimum <= peasycap->volume) &&
+ (easycap_control[i1].maximum >= peasycap->volume)) {
+ if (peasycap->volume == value) {
+ SAM("unchanged volume at 0x%02X\n", value);
+ return 0;
+ }
+ }
+ peasycap->volume = value;
+ mood = (16 > peasycap->volume) ? 16 :
+ ((31 < peasycap->volume) ? 31 :
+ (s8) peasycap->volume);
+ if (!audio_gainset(peasycap->pusb_device, mood)) {
+ SAM("adjusting volume to 0x%02X\n", mood);
+ return 0;
+ } else {
+ SAM("WARNING: failed to adjust volume to "
+ "0x%2X\n", mood);
+ return -ENOENT;
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust volume: control not found\n");
+ return -ENOENT;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * AN ALTERNATIVE METHOD OF MUTING MIGHT SEEM TO BE:
+ * usb_set_interface(peasycap->pusb_device,
+ * peasycap->audio_interface,
+ * peasycap->audio_altsetting_off);
+ * HOWEVER, AFTER THIS COMMAND IS ISSUED ALL SUBSEQUENT URBS RECEIVE STATUS
+ * -ESHUTDOWN. THE HANDLER ROUTINE easyxxx_complete() DECLINES TO RESUBMIT
+ * THE URB AND THE PIPELINE COLLAPSES IRRETRIEVABLY. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+static int adjust_mute(struct easycap *peasycap, int value)
+{
+ int i1;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (V4L2_CID_AUDIO_MUTE == easycap_control[i1].id) {
+ peasycap->mute = value;
+ switch (peasycap->mute) {
+ case 1: {
+ peasycap->audio_idle = 1;
+ SAM("adjusting mute: %i=peasycap->audio_idle\n",
+ peasycap->audio_idle);
+ return 0;
+ }
+ default: {
+ peasycap->audio_idle = 0;
+ SAM("adjusting mute: %i=peasycap->audio_idle\n",
+ peasycap->audio_idle);
+ return 0;
+ }
+ }
+ break;
+ }
+ i1++;
+ }
+ SAM("WARNING: failed to adjust mute: control not found\n");
+ return -ENOENT;
+}
+/*---------------------------------------------------------------------------*/
+long easycap_unlocked_ioctl(struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct easycap *peasycap;
+ struct usb_device *p;
+ int kd;
+
+ if (!file) {
+ SAY("ERROR: file is NULL\n");
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -1;
+ }
+ p = peasycap->pusb_device;
+ if (!p) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ kd = isdongle(peasycap);
+ if (0 <= kd && DONGLE_MANY > kd) {
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
+ SAY("ERROR: cannot lock "
+ "easycapdc60_dongle[%i].mutex_video\n", kd);
+ return -ERESTARTSYS;
+ }
+ JOM(4, "locked easycapdc60_dongle[%i].mutex_video\n", kd);
+/*---------------------------------------------------------------------------*/
+/*
+ * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER peasycap,
+ * IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+ * IF NECESSARY, BAIL OUT.
+ */
+/*---------------------------------------------------------------------------*/
+ if (kd != isdongle(peasycap))
+ return -ERESTARTSYS;
+ if (!file) {
+ SAY("ERROR: file is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ } else {
+/*---------------------------------------------------------------------------*/
+/*
+ * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap BEFORE THE
+ * ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL HAVE FAILED. BAIL OUT.
+ */
+/*---------------------------------------------------------------------------*/
+ return -ERESTARTSYS;
+ }
+/*---------------------------------------------------------------------------*/
+ switch (cmd) {
+ case VIDIOC_QUERYCAP: {
+ struct v4l2_capability v4l2_capability;
+ char version[16], *p1, *p2;
+ int i, rc, k[3];
+ long lng;
+
+ JOM(8, "VIDIOC_QUERYCAP\n");
+
+ if (16 <= strlen(EASYCAP_DRIVER_VERSION)) {
+ SAM("ERROR: bad driver version string\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ strcpy(&version[0], EASYCAP_DRIVER_VERSION);
+ for (i = 0; i < 3; i++)
+ k[i] = 0;
+ p2 = &version[0];
+ i = 0;
+ while (*p2) {
+ p1 = p2;
+ while (*p2 && ('.' != *p2))
+ p2++;
+ if (*p2)
+ *p2++ = 0;
+ if (3 > i) {
+ rc = (int) strict_strtol(p1, 10, &lng);
+ if (rc) {
+ SAM("ERROR: %i=strict_strtol(%s,.,,)\n",
+ rc, p1);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ k[i] = (int)lng;
+ }
+ i++;
+ }
+
+ memset(&v4l2_capability, 0, sizeof(struct v4l2_capability));
+ strlcpy(&v4l2_capability.driver[0],
+ "easycap", sizeof(v4l2_capability.driver));
+
+ v4l2_capability.capabilities = V4L2_CAP_VIDEO_CAPTURE |
+ V4L2_CAP_STREAMING |
+ V4L2_CAP_AUDIO |
+ V4L2_CAP_READWRITE;
+
+ v4l2_capability.version = KERNEL_VERSION(k[0], k[1], k[2]);
+ JOM(8, "v4l2_capability.version=(%i,%i,%i)\n", k[0], k[1], k[2]);
+
+ strlcpy(&v4l2_capability.card[0],
+ "EasyCAP DC60", sizeof(v4l2_capability.card));
+
+ if (usb_make_path(peasycap->pusb_device,
+ &v4l2_capability.bus_info[0],
+ sizeof(v4l2_capability.bus_info)) < 0) {
+
+ strlcpy(&v4l2_capability.bus_info[0], "EasyCAP bus_info",
+ sizeof(v4l2_capability.bus_info));
+ JOM(8, "%s=v4l2_capability.bus_info\n",
+ &v4l2_capability.bus_info[0]);
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_capability,
+ sizeof(struct v4l2_capability))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUMINPUT: {
+ struct v4l2_input v4l2_input;
+ u32 index;
+
+ JOM(8, "VIDIOC_ENUMINPUT\n");
+
+ if (copy_from_user(&v4l2_input, (void __user *)arg,
+ sizeof(struct v4l2_input))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_input.index;
+ memset(&v4l2_input, 0, sizeof(struct v4l2_input));
+
+ switch (index) {
+ case 0: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS0");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL |
+ V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 1: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS1");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 2: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS2");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 3: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS3");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 4: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "CVBS4");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ case 5: {
+ v4l2_input.index = index;
+ strcpy(&v4l2_input.name[0], "S-VIDEO");
+ v4l2_input.type = V4L2_INPUT_TYPE_CAMERA;
+ v4l2_input.audioset = 0x01;
+ v4l2_input.tuner = 0;
+ v4l2_input.std = V4L2_STD_PAL | V4L2_STD_SECAM |
+ V4L2_STD_NTSC ;
+ v4l2_input.status = 0;
+ JOM(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts inputs\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+
+ if (copy_to_user((void __user *)arg, &v4l2_input,
+ sizeof(struct v4l2_input))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_INPUT: {
+ u32 index;
+
+ JOM(8, "VIDIOC_G_INPUT\n");
+ index = (u32)peasycap->input;
+ JOM(8, "user is told: %i\n", index);
+ if (copy_to_user((void __user *)arg, &index, sizeof(u32))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_INPUT:
+ {
+ u32 index;
+ int rc;
+
+ JOM(8, "VIDIOC_S_INPUT\n");
+
+ if (0 != copy_from_user(&index, (void __user *)arg, sizeof(u32))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ JOM(8, "user requests input %i\n", index);
+
+ if ((int)index == peasycap->input) {
+ SAM("requested input already in effect\n");
+ break;
+ }
+
+ if ((0 > index) || (INPUT_MANY <= index)) {
+ JOM(8, "ERROR: bad requested input: %i\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+
+ rc = newinput(peasycap, (int)index);
+ if (0 == rc) {
+ JOM(8, "newinput(.,%i) OK\n", (int)index);
+ } else {
+ SAM("ERROR: newinput(.,%i) returned %i\n", (int)index, rc);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUMAUDIO: {
+ JOM(8, "VIDIOC_ENUMAUDIO\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUMAUDOUT: {
+ struct v4l2_audioout v4l2_audioout;
+
+ JOM(8, "VIDIOC_ENUMAUDOUT\n");
+
+ if (copy_from_user(&v4l2_audioout, (void __user *)arg,
+ sizeof(struct v4l2_audioout))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (0 != v4l2_audioout.index) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout));
+ v4l2_audioout.index = 0;
+ strcpy(&v4l2_audioout.name[0], "Soundtrack");
+
+ if (copy_to_user((void __user *)arg, &v4l2_audioout,
+ sizeof(struct v4l2_audioout))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYCTRL: {
+ int i1;
+ struct v4l2_queryctrl v4l2_queryctrl;
+
+ JOM(8, "VIDIOC_QUERYCTRL\n");
+
+ if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg,
+ sizeof(struct v4l2_queryctrl))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ i1 = 0;
+ while (0xFFFFFFFF != easycap_control[i1].id) {
+ if (easycap_control[i1].id == v4l2_queryctrl.id) {
+ JOM(8, "VIDIOC_QUERYCTRL %s=easycap_control[%i]"
+ ".name\n", &easycap_control[i1].name[0], i1);
+ memcpy(&v4l2_queryctrl, &easycap_control[i1],
+ sizeof(struct v4l2_queryctrl));
+ break;
+ }
+ i1++;
+ }
+ if (0xFFFFFFFF == easycap_control[i1].id) {
+ JOM(8, "%i=index: exhausts controls\n", i1);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_queryctrl,
+ sizeof(struct v4l2_queryctrl))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYMENU: {
+ JOM(8, "VIDIOC_QUERYMENU unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_CTRL: {
+ struct v4l2_control *pv4l2_control;
+
+ JOM(8, "VIDIOC_G_CTRL\n");
+ pv4l2_control = memdup_user((void __user *)arg,
+ sizeof(struct v4l2_control));
+ if (IS_ERR(pv4l2_control)) {
+ SAM("ERROR: copy from user failed\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return PTR_ERR(pv4l2_control);
+ }
+
+ switch (pv4l2_control->id) {
+ case V4L2_CID_BRIGHTNESS: {
+ pv4l2_control->value = peasycap->brightness;
+ JOM(8, "user enquires brightness: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_CONTRAST: {
+ pv4l2_control->value = peasycap->contrast;
+ JOM(8, "user enquires contrast: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_SATURATION: {
+ pv4l2_control->value = peasycap->saturation;
+ JOM(8, "user enquires saturation: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_HUE: {
+ pv4l2_control->value = peasycap->hue;
+ JOM(8, "user enquires hue: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_AUDIO_VOLUME: {
+ pv4l2_control->value = peasycap->volume;
+ JOM(8, "user enquires volume: %i\n", pv4l2_control->value);
+ break;
+ }
+ case V4L2_CID_AUDIO_MUTE: {
+ if (1 == peasycap->mute)
+ pv4l2_control->value = true;
+ else
+ pv4l2_control->value = false;
+ JOM(8, "user enquires mute: %i\n", pv4l2_control->value);
+ break;
+ }
+ default: {
+ SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
+ pv4l2_control->id);
+ kfree(pv4l2_control);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ if (copy_to_user((void __user *)arg, pv4l2_control,
+ sizeof(struct v4l2_control))) {
+ kfree(pv4l2_control);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ kfree(pv4l2_control);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_CTRL: {
+ struct v4l2_control v4l2_control;
+
+ JOM(8, "VIDIOC_S_CTRL\n");
+
+ if (0 != copy_from_user(&v4l2_control, (void __user *)arg,
+ sizeof(struct v4l2_control))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ switch (v4l2_control.id) {
+ case V4L2_CID_BRIGHTNESS: {
+ JOM(8, "user requests brightness %i\n", v4l2_control.value);
+ if (0 != adjust_brightness(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_CONTRAST: {
+ JOM(8, "user requests contrast %i\n", v4l2_control.value);
+ if (0 != adjust_contrast(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_SATURATION: {
+ JOM(8, "user requests saturation %i\n", v4l2_control.value);
+ if (0 != adjust_saturation(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_HUE: {
+ JOM(8, "user requests hue %i\n", v4l2_control.value);
+ if (0 != adjust_hue(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_AUDIO_VOLUME: {
+ JOM(8, "user requests volume %i\n", v4l2_control.value);
+ if (0 != adjust_volume(peasycap, v4l2_control.value))
+ ;
+ break;
+ }
+ case V4L2_CID_AUDIO_MUTE: {
+ int mute;
+
+ JOM(8, "user requests mute %i\n", v4l2_control.value);
+ if (v4l2_control.value)
+ mute = 1;
+ else
+ mute = 0;
+
+ if (0 != adjust_mute(peasycap, mute))
+ SAM("WARNING: failed to adjust mute to %i\n", mute);
+ break;
+ }
+ default: {
+ SAM("ERROR: unknown V4L2 control: 0x%08X=id\n",
+ v4l2_control.id);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_EXT_CTRLS: {
+ JOM(8, "VIDIOC_S_EXT_CTRLS unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUM_FMT: {
+ u32 index;
+ struct v4l2_fmtdesc v4l2_fmtdesc;
+
+ JOM(8, "VIDIOC_ENUM_FMT\n");
+
+ if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg,
+ sizeof(struct v4l2_fmtdesc))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_fmtdesc.index;
+ memset(&v4l2_fmtdesc, 0, sizeof(struct v4l2_fmtdesc));
+
+ v4l2_fmtdesc.index = index;
+ v4l2_fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ switch (index) {
+ case 0: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "uyvy");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_UYVY;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 1: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "yuy2");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_YUYV;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 2: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "rgb24");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB24;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 3: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "rgb32");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_RGB32;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 4: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "bgr24");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR24;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ case 5: {
+ v4l2_fmtdesc.flags = 0;
+ strcpy(&v4l2_fmtdesc.description[0], "bgr32");
+ v4l2_fmtdesc.pixelformat = V4L2_PIX_FMT_BGR32;
+ JOM(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts formats\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_fmtdesc,
+ sizeof(struct v4l2_fmtdesc))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * THE RESPONSE TO VIDIOC_ENUM_FRAMESIZES MUST BE CONDITIONED ON THE
+ * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
+ */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUM_FRAMESIZES: {
+ u32 index;
+ struct v4l2_frmsizeenum v4l2_frmsizeenum;
+
+ JOM(8, "VIDIOC_ENUM_FRAMESIZES\n");
+
+ if (0 != copy_from_user(&v4l2_frmsizeenum, (void __user *)arg,
+ sizeof(struct v4l2_frmsizeenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_frmsizeenum.index;
+
+ v4l2_frmsizeenum.type = (u32) V4L2_FRMSIZE_TYPE_DISCRETE;
+
+ if (peasycap->ntsc) {
+ switch (index) {
+ case 0: {
+ v4l2_frmsizeenum.discrete.width = 640;
+ v4l2_frmsizeenum.discrete.height = 480;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 1: {
+ v4l2_frmsizeenum.discrete.width = 320;
+ v4l2_frmsizeenum.discrete.height = 240;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 2: {
+ v4l2_frmsizeenum.discrete.width = 720;
+ v4l2_frmsizeenum.discrete.height = 480;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 3: {
+ v4l2_frmsizeenum.discrete.width = 360;
+ v4l2_frmsizeenum.discrete.height = 240;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts framesizes\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ } else {
+ switch (index) {
+ case 0: {
+ v4l2_frmsizeenum.discrete.width = 640;
+ v4l2_frmsizeenum.discrete.height = 480;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 1: {
+ v4l2_frmsizeenum.discrete.width = 320;
+ v4l2_frmsizeenum.discrete.height = 240;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 2: {
+ v4l2_frmsizeenum.discrete.width = 704;
+ v4l2_frmsizeenum.discrete.height = 576;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 3: {
+ v4l2_frmsizeenum.discrete.width = 720;
+ v4l2_frmsizeenum.discrete.height = 576;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ case 4: {
+ v4l2_frmsizeenum.discrete.width = 360;
+ v4l2_frmsizeenum.discrete.height = 288;
+ JOM(8, "%i=index: %ix%i\n", index,
+ (int)(v4l2_frmsizeenum.
+ discrete.width),
+ (int)(v4l2_frmsizeenum.
+ discrete.height));
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts framesizes\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_frmsizeenum,
+ sizeof(struct v4l2_frmsizeenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * THE RESPONSE TO VIDIOC_ENUM_FRAMEINTERVALS MUST BE CONDITIONED ON THE
+ * THE CURRENT STANDARD, BECAUSE THAT IS WHAT gstreamer EXPECTS. BEWARE.
+ */
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_ENUM_FRAMEINTERVALS: {
+ u32 index;
+ int denominator;
+ struct v4l2_frmivalenum v4l2_frmivalenum;
+
+ JOM(8, "VIDIOC_ENUM_FRAMEINTERVALS\n");
+
+ if (peasycap->fps)
+ denominator = peasycap->fps;
+ else {
+ if (peasycap->ntsc)
+ denominator = 30;
+ else
+ denominator = 25;
+ }
+
+ if (0 != copy_from_user(&v4l2_frmivalenum, (void __user *)arg,
+ sizeof(struct v4l2_frmivalenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ index = v4l2_frmivalenum.index;
+
+ v4l2_frmivalenum.type = (u32) V4L2_FRMIVAL_TYPE_DISCRETE;
+
+ switch (index) {
+ case 0: {
+ v4l2_frmivalenum.discrete.numerator = 1;
+ v4l2_frmivalenum.discrete.denominator = denominator;
+ JOM(8, "%i=index: %i/%i\n", index,
+ (int)(v4l2_frmivalenum.discrete.numerator),
+ (int)(v4l2_frmivalenum.discrete.denominator));
+ break;
+ }
+ case 1: {
+ v4l2_frmivalenum.discrete.numerator = 1;
+ v4l2_frmivalenum.discrete.denominator = denominator/5;
+ JOM(8, "%i=index: %i/%i\n", index,
+ (int)(v4l2_frmivalenum.discrete.numerator),
+ (int)(v4l2_frmivalenum.discrete.denominator));
+ break;
+ }
+ default: {
+ JOM(8, "%i=index: exhausts frameintervals\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ }
+ if (copy_to_user((void __user *)arg, &v4l2_frmivalenum,
+ sizeof(struct v4l2_frmivalenum))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_FMT: {
+ struct v4l2_format *pv4l2_format;
+ struct v4l2_pix_format *pv4l2_pix_format;
+
+ JOM(8, "VIDIOC_G_FMT\n");
+ pv4l2_format = kzalloc(sizeof(struct v4l2_format), GFP_KERNEL);
+ if (!pv4l2_format) {
+ SAM("ERROR: out of memory\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOMEM;
+ }
+ pv4l2_pix_format = kzalloc(sizeof(struct v4l2_pix_format), GFP_KERNEL);
+ if (!pv4l2_pix_format) {
+ SAM("ERROR: out of memory\n");
+ kfree(pv4l2_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOMEM;
+ }
+ if (0 != copy_from_user(pv4l2_format, (void __user *)arg,
+ sizeof(struct v4l2_format))) {
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (pv4l2_format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+
+ memset(pv4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
+ pv4l2_format->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ memcpy(&pv4l2_format->fmt.pix,
+ &easycap_format[peasycap->format_offset]
+ .v4l2_format.fmt.pix, sizeof(struct v4l2_pix_format));
+ JOM(8, "user is told: %s\n",
+ &easycap_format[peasycap->format_offset].name[0]);
+
+ if (copy_to_user((void __user *)arg, pv4l2_format,
+ sizeof(struct v4l2_format))) {
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ kfree(pv4l2_format);
+ kfree(pv4l2_pix_format);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_TRY_FMT:
+ case VIDIOC_S_FMT: {
+ struct v4l2_format v4l2_format;
+ struct v4l2_pix_format v4l2_pix_format;
+ bool try;
+ int best_format;
+
+ if (VIDIOC_TRY_FMT == cmd) {
+ JOM(8, "VIDIOC_TRY_FMT\n");
+ try = true;
+ } else {
+ JOM(8, "VIDIOC_S_FMT\n");
+ try = false;
+ }
+
+ if (0 != copy_from_user(&v4l2_format, (void __user *)arg,
+ sizeof(struct v4l2_format))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ best_format = adjust_format(peasycap,
+ v4l2_format.fmt.pix.width,
+ v4l2_format.fmt.pix.height,
+ v4l2_format.fmt.pix.pixelformat,
+ v4l2_format.fmt.pix.field,
+ try);
+ if (0 > best_format) {
+ if (-EBUSY == best_format) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EBUSY;
+ }
+ JOM(8, "WARNING: adjust_format() returned %i\n", best_format);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOENT;
+ }
+/*...........................................................................*/
+ memset(&v4l2_pix_format, 0, sizeof(struct v4l2_pix_format));
+ v4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+
+ memcpy(&(v4l2_format.fmt.pix),
+ &(easycap_format[best_format].v4l2_format.fmt.pix),
+ sizeof(v4l2_pix_format));
+ JOM(8, "user is told: %s\n", &easycap_format[best_format].name[0]);
+
+ if (copy_to_user((void __user *)arg, &v4l2_format,
+ sizeof(struct v4l2_format))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_CROPCAP: {
+ struct v4l2_cropcap v4l2_cropcap;
+
+ JOM(8, "VIDIOC_CROPCAP\n");
+
+ if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg,
+ sizeof(struct v4l2_cropcap))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
+ JOM(8, "v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
+
+ memset(&v4l2_cropcap, 0, sizeof(struct v4l2_cropcap));
+ v4l2_cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_cropcap.bounds.left = 0;
+ v4l2_cropcap.bounds.top = 0;
+ v4l2_cropcap.bounds.width = peasycap->width;
+ v4l2_cropcap.bounds.height = peasycap->height;
+ v4l2_cropcap.defrect.left = 0;
+ v4l2_cropcap.defrect.top = 0;
+ v4l2_cropcap.defrect.width = peasycap->width;
+ v4l2_cropcap.defrect.height = peasycap->height;
+ v4l2_cropcap.pixelaspect.numerator = 1;
+ v4l2_cropcap.pixelaspect.denominator = 1;
+
+ JOM(8, "user is told: %ix%i\n", peasycap->width, peasycap->height);
+
+ if (copy_to_user((void __user *)arg, &v4l2_cropcap,
+ sizeof(struct v4l2_cropcap))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_CROP:
+ case VIDIOC_S_CROP: {
+ JOM(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYSTD: {
+ JOM(8, "VIDIOC_QUERYSTD: "
+ "EasyCAP is incapable of detecting standard\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ break;
+ }
+ /*-------------------------------------------------------------------*/
+ /*
+ * THE MANIPULATIONS INVOLVING last0,last1,last2,last3
+ * CONSTITUTE A WORKAROUND * FOR WHAT APPEARS TO BE
+ * A BUG IN 64-BIT mplayer.
+ * NOT NEEDED, BUT HOPEFULLY HARMLESS, FOR 32-BIT mplayer.
+ */
+ /*------------------------------------------------------------------*/
+ case VIDIOC_ENUMSTD: {
+ int last0 = -1, last1 = -1, last2 = -1, last3 = -1;
+ struct v4l2_standard v4l2_standard;
+ u32 index;
+ struct easycap_standard const *peasycap_standard;
+
+ JOM(8, "VIDIOC_ENUMSTD\n");
+
+ if (0 != copy_from_user(&v4l2_standard, (void __user *)arg,
+ sizeof(struct v4l2_standard))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ index = v4l2_standard.index;
+
+ last3 = last2;
+ last2 = last1;
+ last1 = last0;
+ last0 = index;
+ if ((index == last3) && (index == last2) &&
+ (index == last1) && (index == last0)) {
+ index++;
+ last3 = last2;
+ last2 = last1;
+ last1 = last0;
+ last0 = index;
+ }
+
+ memset(&v4l2_standard, 0, sizeof(struct v4l2_standard));
+
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if ((int)(peasycap_standard - &easycap_standard[0]) == index)
+ break;
+ peasycap_standard++;
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ JOM(8, "%i=index: exhausts standards\n", index);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ JOM(8, "%i=index: %s\n", index,
+ &(peasycap_standard->v4l2_standard.name[0]));
+ memcpy(&v4l2_standard, &(peasycap_standard->v4l2_standard),
+ sizeof(struct v4l2_standard));
+
+ v4l2_standard.index = index;
+
+ if (copy_to_user((void __user *)arg, &v4l2_standard,
+ sizeof(struct v4l2_standard))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_STD: {
+ v4l2_std_id std_id;
+ struct easycap_standard const *peasycap_standard;
+
+ JOM(8, "VIDIOC_G_STD\n");
+
+ if (0 > peasycap->standard_offset) {
+ JOM(8, "%i=peasycap->standard_offset\n",
+ peasycap->standard_offset);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EBUSY;
+ }
+
+ if (0 != copy_from_user(&std_id, (void __user *)arg,
+ sizeof(v4l2_std_id))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ peasycap_standard = &easycap_standard[peasycap->standard_offset];
+ std_id = peasycap_standard->v4l2_standard.id;
+
+ JOM(8, "user is told: %s\n",
+ &peasycap_standard->v4l2_standard.name[0]);
+
+ if (copy_to_user((void __user *)arg, &std_id,
+ sizeof(v4l2_std_id))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_STD: {
+ v4l2_std_id std_id;
+ int rc;
+
+ JOM(8, "VIDIOC_S_STD\n");
+
+ if (0 != copy_from_user(&std_id, (void __user *)arg,
+ sizeof(v4l2_std_id))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ JOM(8, "User requests standard: 0x%08X%08X\n",
+ (int)((std_id & (((v4l2_std_id)0xFFFFFFFF) << 32)) >> 32),
+ (int)(std_id & ((v4l2_std_id)0xFFFFFFFF)));
+
+ rc = adjust_standard(peasycap, std_id);
+ if (0 > rc) {
+ JOM(8, "WARNING: adjust_standard() returned %i\n", rc);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOENT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_REQBUFS: {
+ int nbuffers;
+ struct v4l2_requestbuffers v4l2_requestbuffers;
+
+ JOM(8, "VIDIOC_REQBUFS\n");
+
+ if (0 != copy_from_user(&v4l2_requestbuffers,
+ (void __user *)arg,
+ sizeof(struct v4l2_requestbuffers))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ nbuffers = v4l2_requestbuffers.count;
+ JOM(8, " User requests %i buffers ...\n", nbuffers);
+ if (nbuffers < 2)
+ nbuffers = 2;
+ if (nbuffers > FRAME_BUFFER_MANY)
+ nbuffers = FRAME_BUFFER_MANY;
+ if (v4l2_requestbuffers.count == nbuffers) {
+ JOM(8, " ... agree to %i buffers\n",
+ nbuffers);
+ } else {
+ JOM(8, " ... insist on %i buffers\n",
+ nbuffers);
+ v4l2_requestbuffers.count = nbuffers;
+ }
+ peasycap->frame_buffer_many = nbuffers;
+
+ if (copy_to_user((void __user *)arg, &v4l2_requestbuffers,
+ sizeof(struct v4l2_requestbuffers))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QUERYBUF: {
+ u32 index;
+ struct v4l2_buffer v4l2_buffer;
+
+ JOM(8, "VIDIOC_QUERYBUF\n");
+
+ if (peasycap->video_eof) {
+ JOM(8, "returning -EIO because %i=video_eof\n",
+ peasycap->video_eof);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+
+ if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ index = v4l2_buffer.index;
+ if (index < 0 || index >= peasycap->frame_buffer_many)
+ return -EINVAL;
+ memset(&v4l2_buffer, 0, sizeof(struct v4l2_buffer));
+ v4l2_buffer.index = index;
+ v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buffer.bytesused = peasycap->frame_buffer_used;
+ v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED |
+ peasycap->done[index] |
+ peasycap->queued[index];
+ v4l2_buffer.field = V4L2_FIELD_NONE;
+ v4l2_buffer.memory = V4L2_MEMORY_MMAP;
+ v4l2_buffer.m.offset = index * FRAME_BUFFER_SIZE;
+ v4l2_buffer.length = FRAME_BUFFER_SIZE;
+
+ JOM(16, " %10i=index\n", v4l2_buffer.index);
+ JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
+ JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
+ JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
+ JOM(16, " %10i=field\n", v4l2_buffer.field);
+ JOM(16, " %10li=timestamp.tv_usec\n",
+ (long)v4l2_buffer.timestamp.tv_usec);
+ JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
+ JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
+ JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
+ JOM(16, " %10i=length\n", v4l2_buffer.length);
+
+ if (copy_to_user((void __user *)arg, &v4l2_buffer,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_QBUF: {
+ struct v4l2_buffer v4l2_buffer;
+
+ JOM(8, "VIDIOC_QBUF\n");
+
+ if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (v4l2_buffer.memory != V4L2_MEMORY_MMAP) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ if (v4l2_buffer.index < 0 ||
+ v4l2_buffer.index >= peasycap->frame_buffer_many) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED;
+
+ peasycap->done[v4l2_buffer.index] = 0;
+ peasycap->queued[v4l2_buffer.index] = V4L2_BUF_FLAG_QUEUED;
+
+ if (copy_to_user((void __user *)arg, &v4l2_buffer,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ JOM(8, "..... user queueing frame buffer %i\n",
+ (int)v4l2_buffer.index);
+
+ peasycap->frame_lock = 0;
+
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_DQBUF:
+ {
+ struct timeval timeval, timeval2;
+ int i, j;
+ struct v4l2_buffer v4l2_buffer;
+ int rcdq;
+ u16 input;
+
+ JOM(8, "VIDIOC_DQBUF\n");
+
+ if ((peasycap->video_idle) || (peasycap->video_eof)) {
+ JOM(8, "returning -EIO because "
+ "%i=video_idle %i=video_eof\n",
+ peasycap->video_idle, peasycap->video_eof);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+
+ if (copy_from_user(&v4l2_buffer, (void __user *)arg,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+
+ if (peasycap->offerfields) {
+ /*---------------------------------------------------*/
+ /*
+ * IN ITS 50 "fps" MODE tvtime SEEMS ALWAYS TO REQUEST
+ * V4L2_FIELD_BOTTOM
+ */
+ /*---------------------------------------------------*/
+ if (V4L2_FIELD_TOP == v4l2_buffer.field)
+ JOM(8, "user wants V4L2_FIELD_TOP\n");
+ else if (V4L2_FIELD_BOTTOM == v4l2_buffer.field)
+ JOM(8, "user wants V4L2_FIELD_BOTTOM\n");
+ else if (V4L2_FIELD_ANY == v4l2_buffer.field)
+ JOM(8, "user wants V4L2_FIELD_ANY\n");
+ else
+ JOM(8, "user wants V4L2_FIELD_...UNKNOWN: %i\n",
+ v4l2_buffer.field);
+ }
+
+ if (!peasycap->video_isoc_streaming) {
+ JOM(16, "returning -EIO because video urbs not streaming\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+ /*-------------------------------------------------------------------*/
+ /*
+ * IF THE USER HAS PREVIOUSLY CALLED easycap_poll(),
+ * AS DETERMINED BY FINDING
+ * THE FLAG peasycap->polled SET, THERE MUST BE
+ * NO FURTHER WAIT HERE. IN THIS
+ * CASE, JUST CHOOSE THE FRAME INDICATED BY peasycap->frame_read
+ */
+ /*-------------------------------------------------------------------*/
+
+ if (!peasycap->polled) {
+ do {
+ rcdq = easycap_dqbuf(peasycap, 0);
+ if (-EIO == rcdq) {
+ JOM(8, "returning -EIO because "
+ "dqbuf() returned -EIO\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+ } while (0 != rcdq);
+ } else {
+ if (peasycap->video_eof) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EIO;
+ }
+ }
+ if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) {
+ JOM(8, "V4L2_BUF_FLAG_DONE != 0x%08X\n",
+ peasycap->done[peasycap->frame_read]);
+ }
+ peasycap->polled = 0;
+
+ if (!(peasycap->isequence % 10)) {
+ for (i = 0; i < 179; i++)
+ peasycap->merit[i] = peasycap->merit[i+1];
+ peasycap->merit[179] = merit_saa(peasycap->pusb_device);
+ j = 0;
+ for (i = 0; i < 180; i++)
+ j += peasycap->merit[i];
+ if (90 < j) {
+ SAM("easycap driver shutting down "
+ "on condition blue\n");
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ }
+ }
+
+ v4l2_buffer.index = peasycap->frame_read;
+ v4l2_buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ v4l2_buffer.bytesused = peasycap->frame_buffer_used;
+ v4l2_buffer.flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE;
+ if (peasycap->offerfields)
+ v4l2_buffer.field = V4L2_FIELD_BOTTOM;
+ else
+ v4l2_buffer.field = V4L2_FIELD_NONE;
+ do_gettimeofday(&timeval);
+ timeval2 = timeval;
+
+ v4l2_buffer.timestamp = timeval2;
+ v4l2_buffer.sequence = peasycap->isequence++;
+ v4l2_buffer.memory = V4L2_MEMORY_MMAP;
+ v4l2_buffer.m.offset = v4l2_buffer.index * FRAME_BUFFER_SIZE;
+ v4l2_buffer.length = FRAME_BUFFER_SIZE;
+
+ JOM(16, " %10i=index\n", v4l2_buffer.index);
+ JOM(16, " 0x%08X=type\n", v4l2_buffer.type);
+ JOM(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
+ JOM(16, " 0x%08X=flags\n", v4l2_buffer.flags);
+ JOM(16, " %10i=field\n", v4l2_buffer.field);
+ JOM(16, " %10li=timestamp.tv_sec\n",
+ (long)v4l2_buffer.timestamp.tv_sec);
+ JOM(16, " %10li=timestamp.tv_usec\n",
+ (long)v4l2_buffer.timestamp.tv_usec);
+ JOM(16, " %10i=sequence\n", v4l2_buffer.sequence);
+ JOM(16, " 0x%08X=memory\n", v4l2_buffer.memory);
+ JOM(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
+ JOM(16, " %10i=length\n", v4l2_buffer.length);
+
+ if (copy_to_user((void __user *)arg, &v4l2_buffer,
+ sizeof(struct v4l2_buffer))) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ input = peasycap->frame_buffer[peasycap->frame_read][0].input;
+ if (0x08 & input) {
+ JOM(8, "user is offered frame buffer %i, input %i\n",
+ peasycap->frame_read, (0x07 & input));
+ } else {
+ JOM(8, "user is offered frame buffer %i\n",
+ peasycap->frame_read);
+ }
+ peasycap->frame_lock = 1;
+ JOM(8, "%i=peasycap->frame_fill\n", peasycap->frame_fill);
+ if (peasycap->frame_read == peasycap->frame_fill) {
+ if (peasycap->frame_lock) {
+ JOM(8, "WORRY: filling frame buffer "
+ "while offered to user\n");
+ }
+ }
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_STREAMON: {
+ int i;
+
+ JOM(8, "VIDIOC_STREAMON\n");
+
+ peasycap->isequence = 0;
+ for (i = 0; i < 180; i++)
+ peasycap->merit[i] = 0;
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ submit_video_urbs(peasycap);
+ peasycap->video_idle = 0;
+ peasycap->audio_idle = 0;
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_STREAMOFF: {
+ JOM(8, "VIDIOC_STREAMOFF\n");
+
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+
+ peasycap->video_idle = 1;
+ peasycap->audio_idle = 1;
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO THE STREAMOFF COMMAND
+ * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(8, "calling wake_up on wq_video and wq_audio\n");
+ wake_up_interruptible(&(peasycap->wq_video));
+ if (peasycap->psubstream)
+ snd_pcm_period_elapsed(peasycap->psubstream);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_PARM: {
+ struct v4l2_streamparm *pv4l2_streamparm;
+
+ JOM(8, "VIDIOC_G_PARM\n");
+ pv4l2_streamparm = memdup_user((void __user *)arg,
+ sizeof(struct v4l2_streamparm));
+ if (IS_ERR(pv4l2_streamparm)) {
+ SAM("ERROR: copy from user failed\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return PTR_ERR(pv4l2_streamparm);
+ }
+
+ if (pv4l2_streamparm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
+ kfree(pv4l2_streamparm);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ pv4l2_streamparm->parm.capture.capability = 0;
+ pv4l2_streamparm->parm.capture.capturemode = 0;
+ pv4l2_streamparm->parm.capture.timeperframe.numerator = 1;
+
+ if (peasycap->fps) {
+ pv4l2_streamparm->parm.capture.timeperframe.
+ denominator = peasycap->fps;
+ } else {
+ if (peasycap->ntsc) {
+ pv4l2_streamparm->parm.capture.timeperframe.
+ denominator = 30;
+ } else {
+ pv4l2_streamparm->parm.capture.timeperframe.
+ denominator = 25;
+ }
+ }
+
+ pv4l2_streamparm->parm.capture.readbuffers =
+ peasycap->frame_buffer_many;
+ pv4l2_streamparm->parm.capture.extendedmode = 0;
+ if (copy_to_user((void __user *)arg,
+ pv4l2_streamparm,
+ sizeof(struct v4l2_streamparm))) {
+ kfree(pv4l2_streamparm);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EFAULT;
+ }
+ kfree(pv4l2_streamparm);
+ break;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_PARM: {
+ JOM(8, "VIDIOC_S_PARM unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_AUDIO: {
+ JOM(8, "VIDIOC_G_AUDIO unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_AUDIO: {
+ JOM(8, "VIDIOC_S_AUDIO unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_S_TUNER: {
+ JOM(8, "VIDIOC_S_TUNER unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_FBUF:
+ case VIDIOC_S_FBUF:
+ case VIDIOC_OVERLAY: {
+ JOM(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ case VIDIOC_G_TUNER: {
+ JOM(8, "VIDIOC_G_TUNER unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+ case VIDIOC_G_FREQUENCY:
+ case VIDIOC_S_FREQUENCY: {
+ JOM(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -EINVAL;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ default: {
+ JOM(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ENOIOCTLCMD;
+ }
+ }
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ JOM(4, "unlocked easycapdc60_dongle[%i].mutex_video\n", kd);
+ return 0;
+}
+/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_low.c b/drivers/staging/media/easycap/easycap_low.c
new file mode 100644
index 000000000000..0385735ac6df
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap_low.c
@@ -0,0 +1,1129 @@
+/*****************************************************************************
+* *
+* *
+* easycap_low.c *
+* *
+* *
+*****************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+/*
+ * ACKNOWLEGEMENTS AND REFERENCES
+ * ------------------------------
+ * This driver makes use of register information contained in the Syntek
+ * Semicon DC-1125 driver hosted at
+ * http://sourceforge.net/projects/syntekdriver/.
+ * Particularly useful has been a patch to the latter driver provided by
+ * Ivor Hewitt in January 2009. The NTSC implementation is taken from the
+ * work of Ben Trask.
+*/
+/****************************************************************************/
+
+#include "easycap.h"
+
+#define GET(X, Y, Z) do { \
+ int __rc; \
+ *(Z) = (u16)0; \
+ __rc = regget(X, Y, Z, sizeof(u8)); \
+ if (0 > __rc) { \
+ JOT(8, ":-(%i\n", __LINE__); return __rc; \
+ } \
+} while (0)
+
+#define SET(X, Y, Z) do { \
+ int __rc; \
+ __rc = regset(X, Y, Z); \
+ if (0 > __rc) { \
+ JOT(8, ":-(%i\n", __LINE__); return __rc; \
+ } \
+} while (0)
+
+/*--------------------------------------------------------------------------*/
+static const struct stk1160config {
+ int reg;
+ int set;
+} stk1160configPAL[256] = {
+ {0x000, 0x0098},
+ {0x002, 0x0093},
+
+ {0x001, 0x0003},
+ {0x003, 0x0080},
+ {0x00D, 0x0000},
+ {0x00F, 0x0002},
+ {0x018, 0x0010},
+ {0x019, 0x0000},
+ {0x01A, 0x0014},
+ {0x01B, 0x000E},
+ {0x01C, 0x0046},
+
+ {0x100, 0x0033},
+ {0x103, 0x0000},
+ {0x104, 0x0000},
+ {0x105, 0x0000},
+ {0x106, 0x0000},
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * RESOLUTION 640x480
+*/
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {0x110, 0x0008},
+ {0x111, 0x0000},
+ {0x112, 0x0020},
+ {0x113, 0x0000},
+ {0x114, 0x0508},
+ {0x115, 0x0005},
+ {0x116, 0x0110},
+ {0x117, 0x0001},
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+ {0x202, 0x000F},
+ {0x203, 0x004A},
+ {0x2FF, 0x0000},
+
+ {0xFFF, 0xFFFF}
+};
+/*--------------------------------------------------------------------------*/
+static const struct stk1160config stk1160configNTSC[256] = {
+ {0x000, 0x0098},
+ {0x002, 0x0093},
+
+ {0x001, 0x0003},
+ {0x003, 0x0080},
+ {0x00D, 0x0000},
+ {0x00F, 0x0002},
+ {0x018, 0x0010},
+ {0x019, 0x0000},
+ {0x01A, 0x0014},
+ {0x01B, 0x000E},
+ {0x01C, 0x0046},
+
+ {0x100, 0x0033},
+ {0x103, 0x0000},
+ {0x104, 0x0000},
+ {0x105, 0x0000},
+ {0x106, 0x0000},
+
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+/*
+ * RESOLUTION 640x480
+*/
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {0x110, 0x0008},
+ {0x111, 0x0000},
+ {0x112, 0x0003},
+ {0x113, 0x0000},
+ {0x114, 0x0508},
+ {0x115, 0x0005},
+ {0x116, 0x00F3},
+ {0x117, 0x0000},
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+
+ {0x202, 0x000F},
+ {0x203, 0x004A},
+ {0x2FF, 0x0000},
+
+ {0xFFF, 0xFFFF}
+};
+/*--------------------------------------------------------------------------*/
+static const struct saa7113config {
+ int reg;
+ int set;
+} saa7113configPAL[256] = {
+ {0x01, 0x08},
+ {0x02, 0x80},
+ {0x03, 0x33},
+ {0x04, 0x00},
+ {0x05, 0x00},
+ {0x06, 0xE9},
+ {0x07, 0x0D},
+ {0x08, 0x38},
+ {0x09, 0x00},
+ {0x0A, SAA_0A_DEFAULT},
+ {0x0B, SAA_0B_DEFAULT},
+ {0x0C, SAA_0C_DEFAULT},
+ {0x0D, SAA_0D_DEFAULT},
+ {0x0E, 0x01},
+ {0x0F, 0x36},
+ {0x10, 0x00},
+ {0x11, 0x0C},
+ {0x12, 0xE7},
+ {0x13, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x40, 0x02},
+ {0x41, 0xFF},
+ {0x42, 0xFF},
+ {0x43, 0xFF},
+ {0x44, 0xFF},
+ {0x45, 0xFF},
+ {0x46, 0xFF},
+ {0x47, 0xFF},
+ {0x48, 0xFF},
+ {0x49, 0xFF},
+ {0x4A, 0xFF},
+ {0x4B, 0xFF},
+ {0x4C, 0xFF},
+ {0x4D, 0xFF},
+ {0x4E, 0xFF},
+ {0x4F, 0xFF},
+ {0x50, 0xFF},
+ {0x51, 0xFF},
+ {0x52, 0xFF},
+ {0x53, 0xFF},
+ {0x54, 0xFF},
+ {0x55, 0xFF},
+ {0x56, 0xFF},
+ {0x57, 0xFF},
+ {0x58, 0x40},
+ {0x59, 0x54},
+ {0x5A, 0x07},
+ {0x5B, 0x83},
+
+ {0xFF, 0xFF}
+};
+/*--------------------------------------------------------------------------*/
+static const struct saa7113config saa7113configNTSC[256] = {
+ {0x01, 0x08},
+ {0x02, 0x80},
+ {0x03, 0x33},
+ {0x04, 0x00},
+ {0x05, 0x00},
+ {0x06, 0xE9},
+ {0x07, 0x0D},
+ {0x08, 0x78},
+ {0x09, 0x00},
+ {0x0A, SAA_0A_DEFAULT},
+ {0x0B, SAA_0B_DEFAULT},
+ {0x0C, SAA_0C_DEFAULT},
+ {0x0D, SAA_0D_DEFAULT},
+ {0x0E, 0x01},
+ {0x0F, 0x36},
+ {0x10, 0x00},
+ {0x11, 0x0C},
+ {0x12, 0xE7},
+ {0x13, 0x00},
+ {0x15, 0x00},
+ {0x16, 0x00},
+ {0x40, 0x82},
+ {0x41, 0xFF},
+ {0x42, 0xFF},
+ {0x43, 0xFF},
+ {0x44, 0xFF},
+ {0x45, 0xFF},
+ {0x46, 0xFF},
+ {0x47, 0xFF},
+ {0x48, 0xFF},
+ {0x49, 0xFF},
+ {0x4A, 0xFF},
+ {0x4B, 0xFF},
+ {0x4C, 0xFF},
+ {0x4D, 0xFF},
+ {0x4E, 0xFF},
+ {0x4F, 0xFF},
+ {0x50, 0xFF},
+ {0x51, 0xFF},
+ {0x52, 0xFF},
+ {0x53, 0xFF},
+ {0x54, 0xFF},
+ {0x55, 0xFF},
+ {0x56, 0xFF},
+ {0x57, 0xFF},
+ {0x58, 0x40},
+ {0x59, 0x54},
+ {0x5A, 0x0A},
+ {0x5B, 0x83},
+
+ {0xFF, 0xFF}
+};
+
+static int regget(struct usb_device *pusb_device,
+ u16 index, void *reg, int reg_size)
+{
+ int rc;
+
+ if (!pusb_device)
+ return -ENODEV;
+
+ rc = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0),
+ 0x00,
+ (USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+ 0x00,
+ index, reg, reg_size, 50000);
+
+ return rc;
+}
+
+static int regset(struct usb_device *pusb_device, u16 index, u16 value)
+{
+ int rc;
+
+ if (!pusb_device)
+ return -ENODEV;
+
+ rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
+ 0x01,
+ (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE),
+ value, index, NULL, 0, 500);
+
+ if (rc < 0)
+ return rc;
+
+ if (easycap_readback) {
+ u16 igot = 0;
+ rc = regget(pusb_device, index, &igot, sizeof(igot));
+ igot = 0xFF & igot;
+ switch (index) {
+ case 0x000:
+ case 0x500:
+ case 0x502:
+ case 0x503:
+ case 0x504:
+ case 0x506:
+ case 0x507:
+ break;
+
+ case 0x204:
+ case 0x205:
+ case 0x350:
+ case 0x351:
+ if (igot)
+ JOT(8, "unexpected 0x%02X "
+ "for STK register 0x%03X\n",
+ igot, index);
+ break;
+
+ default:
+ if ((0xFF & value) != igot)
+ JOT(8, "unexpected 0x%02X != 0x%02X "
+ "for STK register 0x%03X\n",
+ igot, value, index);
+ break;
+ }
+ }
+
+ return rc;
+}
+/*--------------------------------------------------------------------------*/
+/*
+ * FUNCTION wait_i2c() RETURNS 0 ON SUCCESS
+*/
+/*--------------------------------------------------------------------------*/
+static int wait_i2c(struct usb_device *p)
+{
+ u16 get0;
+ u8 igot;
+ const int max = 2;
+ int k;
+
+ if (!p)
+ return -ENODEV;
+
+ for (k = 0; k < max; k++) {
+ GET(p, 0x0201, &igot); get0 = igot;
+ switch (get0) {
+ case 0x04:
+ case 0x01:
+ return 0;
+ case 0x00:
+ msleep(20);
+ continue;
+ default:
+ return get0 - 1;
+ }
+ }
+ return -1;
+}
+
+/****************************************************************************/
+int confirm_resolution(struct usb_device *p)
+{
+ u8 get0, get1, get2, get3, get4, get5, get6, get7;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0110, &get0);
+ GET(p, 0x0111, &get1);
+ GET(p, 0x0112, &get2);
+ GET(p, 0x0113, &get3);
+ GET(p, 0x0114, &get4);
+ GET(p, 0x0115, &get5);
+ GET(p, 0x0116, &get6);
+ GET(p, 0x0117, &get7);
+ JOT(8, "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ get0, get1, get2, get3, get4, get5, get6, get7);
+ JOT(8, "....cf PAL_720x526: "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ 0x000, 0x000, 0x001, 0x000, 0x5A0, 0x005, 0x121, 0x001);
+ JOT(8, "....cf PAL_704x526: "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ 0x004, 0x000, 0x001, 0x000, 0x584, 0x005, 0x121, 0x001);
+ JOT(8, "....cf VGA_640x480: "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X, "
+ "0x%03X, 0x%03X\n",
+ 0x008, 0x000, 0x020, 0x000, 0x508, 0x005, 0x110, 0x001);
+ return 0;
+}
+/****************************************************************************/
+int confirm_stream(struct usb_device *p)
+{
+ u16 get2;
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0100, &igot); get2 = 0x80 & igot;
+ if (0x80 == get2)
+ JOT(8, "confirm_stream: OK\n");
+ else
+ JOT(8, "confirm_stream: STUCK\n");
+ return 0;
+}
+/****************************************************************************/
+int setup_stk(struct usb_device *p, bool ntsc)
+{
+ int i;
+ const struct stk1160config *cfg;
+ if (!p)
+ return -ENODEV;
+ cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL;
+ for (i = 0; cfg[i].reg != 0xFFF; i++)
+ SET(p, cfg[i].reg, cfg[i].set);
+
+ write_300(p);
+
+ return 0;
+}
+/****************************************************************************/
+int setup_saa(struct usb_device *p, bool ntsc)
+{
+ int i, ir;
+ const struct saa7113config *cfg;
+ if (!p)
+ return -ENODEV;
+ cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL;
+ for (i = 0; cfg[i].reg != 0xFF; i++)
+ ir = write_saa(p, cfg[i].reg, cfg[i].set);
+ return 0;
+}
+/****************************************************************************/
+int write_000(struct usb_device *p, u16 set2, u16 set0)
+{
+ u8 igot0, igot2;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0002, &igot2);
+ GET(p, 0x0000, &igot0);
+ SET(p, 0x0002, set2);
+ SET(p, 0x0000, set0);
+ return 0;
+}
+/****************************************************************************/
+int write_saa(struct usb_device *p, u16 reg0, u16 set0)
+{
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x200, 0x00);
+ SET(p, 0x204, reg0);
+ SET(p, 0x205, set0);
+ SET(p, 0x200, 0x01);
+ return wait_i2c(p);
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
+ * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
+ * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO SET
+ * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO SET
+ * REGISTER 504: TARGET ADDRESS ON VT1612A
+ */
+/*--------------------------------------------------------------------------*/
+int
+write_vt(struct usb_device *p, u16 reg0, u16 set0)
+{
+ u8 igot;
+ u16 got502, got503;
+ u16 set502, set503;
+
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x0504, reg0);
+ SET(p, 0x0500, 0x008B);
+
+ GET(p, 0x0502, &igot); got502 = (0xFF & igot);
+ GET(p, 0x0503, &igot); got503 = (0xFF & igot);
+
+ JOT(16, "write_vt(., 0x%04X, 0x%04X): was 0x%04X\n",
+ reg0, set0, ((got503 << 8) | got502));
+
+ set502 = (0x00FF & set0);
+ set503 = ((0xFF00 & set0) >> 8);
+
+ SET(p, 0x0504, reg0);
+ SET(p, 0x0502, set502);
+ SET(p, 0x0503, set503);
+ SET(p, 0x0500, 0x008C);
+
+ return 0;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * REGISTER 500: SETTING VALUE TO 0x008B READS FROM VT1612A (?)
+ * REGISTER 500: SETTING VALUE TO 0x008C WRITES TO VT1612A
+ * REGISTER 502: LEAST SIGNIFICANT BYTE OF VALUE TO GET
+ * REGISTER 503: MOST SIGNIFICANT BYTE OF VALUE TO GET
+ * REGISTER 504: TARGET ADDRESS ON VT1612A
+ */
+/*--------------------------------------------------------------------------*/
+int read_vt(struct usb_device *p, u16 reg0)
+{
+ u8 igot;
+ u16 got502, got503;
+
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x0504, reg0);
+ SET(p, 0x0500, 0x008B);
+
+ GET(p, 0x0502, &igot); got502 = (0xFF & igot);
+ GET(p, 0x0503, &igot); got503 = (0xFF & igot);
+
+ JOT(16, "read_vt(., 0x%04X): has 0x%04X\n",
+ reg0, ((got503 << 8) | got502));
+
+ return (got503 << 8) | got502;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * THESE APPEAR TO HAVE NO EFFECT ON EITHER VIDEO OR AUDIO.
+ */
+/*--------------------------------------------------------------------------*/
+int write_300(struct usb_device *p)
+{
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x300, 0x0012);
+ SET(p, 0x350, 0x002D);
+ SET(p, 0x351, 0x0001);
+ SET(p, 0x352, 0x0000);
+ SET(p, 0x353, 0x0000);
+ SET(p, 0x300, 0x0080);
+ return 0;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * NOTE: THE FOLLOWING IS NOT CHECKED:
+ * REGISTER 0x0F, WHICH IS INVOLVED IN CHROMINANCE AUTOMATIC GAIN CONTROL.
+ */
+/*--------------------------------------------------------------------------*/
+int check_saa(struct usb_device *p, bool ntsc)
+{
+ int i, ir, rc = 0;
+ struct saa7113config const *cfg;
+ if (!p)
+ return -ENODEV;
+
+ cfg = (ntsc) ? saa7113configNTSC : saa7113configPAL;
+ for (i = 0; cfg[i].reg != 0xFF; i++) {
+ if (0x0F == cfg[i].reg)
+ continue;
+ ir = read_saa(p, cfg[i].reg);
+ if (ir != cfg[i].set) {
+ SAY("SAA register 0x%02X has 0x%02X, expected 0x%02X\n",
+ cfg[i].reg, ir, cfg[i].set);
+ rc--;
+ }
+ }
+
+ return (rc < -8) ? rc : 0;
+}
+/****************************************************************************/
+int merit_saa(struct usb_device *p)
+{
+ int rc;
+
+ if (!p)
+ return -ENODEV;
+ rc = read_saa(p, 0x1F);
+ return ((0 > rc) || (0x02 & rc)) ? 1 : 0;
+}
+/****************************************************************************/
+int ready_saa(struct usb_device *p)
+{
+ int j, rc, rate;
+ const int max = 5, marktime = PATIENCE/5;
+/*--------------------------------------------------------------------------*/
+/*
+ * RETURNS 0 FOR INTERLACED 50 Hz
+ * 1 FOR NON-INTERLACED 50 Hz
+ * 2 FOR INTERLACED 60 Hz
+ * 3 FOR NON-INTERLACED 60 Hz
+*/
+/*--------------------------------------------------------------------------*/
+ if (!p)
+ return -ENODEV;
+ j = 0;
+ while (max > j) {
+ rc = read_saa(p, 0x1F);
+ if (0 <= rc) {
+ if (0 == (0x40 & rc))
+ break;
+ if (1 == (0x01 & rc))
+ break;
+ }
+ msleep(marktime);
+ j++;
+ }
+ if (max == j)
+ return -1;
+ else {
+ if (0x20 & rc) {
+ rate = 2;
+ JOT(8, "hardware detects 60 Hz\n");
+ } else {
+ rate = 0;
+ JOT(8, "hardware detects 50 Hz\n");
+ }
+ if (0x80 & rc)
+ JOT(8, "hardware detects interlacing\n");
+ else {
+ rate++;
+ JOT(8, "hardware detects no interlacing\n");
+ }
+ }
+ return 0;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * NOTE: THE FOLLOWING ARE NOT CHECKED:
+ * REGISTERS 0x000, 0x002: FUNCTIONALITY IS NOT KNOWN
+ * REGISTER 0x100: ACCEPT ALSO (0x80 | stk1160config....[.].set)
+ */
+/*--------------------------------------------------------------------------*/
+int check_stk(struct usb_device *p, bool ntsc)
+{
+ int i, ir;
+ const struct stk1160config *cfg;
+
+ if (!p)
+ return -ENODEV;
+ cfg = (ntsc) ? stk1160configNTSC : stk1160configPAL;
+
+ for (i = 0; 0xFFF != cfg[i].reg; i++) {
+ if (0x000 == cfg[i].reg || 0x002 == cfg[i].reg)
+ continue;
+
+
+ ir = read_stk(p, cfg[i].reg);
+ if (0x100 == cfg[i].reg) {
+ if ((ir != (0xFF & cfg[i].set)) &&
+ (ir != (0x80 | (0xFF & cfg[i].set))) &&
+ (0xFFFF != cfg[i].set)) {
+ SAY("STK reg[0x%03X]=0x%02X expected 0x%02X\n",
+ cfg[i].reg, ir, cfg[i].set);
+ }
+ continue;
+ }
+ if ((ir != (0xFF & cfg[i].set)) && (0xFFFF != cfg[i].set))
+ SAY("STK register 0x%03X has 0x%02X,expected 0x%02X\n",
+ cfg[i].reg, ir, cfg[i].set);
+ }
+ return 0;
+}
+/****************************************************************************/
+int read_saa(struct usb_device *p, u16 reg0)
+{
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ SET(p, 0x208, reg0);
+ SET(p, 0x200, 0x20);
+ if (0 != wait_i2c(p))
+ return -1;
+ igot = 0;
+ GET(p, 0x0209, &igot);
+ return igot;
+}
+/****************************************************************************/
+int read_stk(struct usb_device *p, u32 reg0)
+{
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ igot = 0;
+ GET(p, reg0, &igot);
+ return igot;
+}
+/****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * HARDWARE USERSPACE INPUT NUMBER PHYSICAL INPUT DRIVER input VALUE
+ *
+ * CVBS+S-VIDEO 0 or 1 CVBS 1
+ * FOUR-CVBS 0 or 1 CVBS1 1
+ * FOUR-CVBS 2 CVBS2 2
+ * FOUR-CVBS 3 CVBS3 3
+ * FOUR-CVBS 4 CVBS4 4
+ * CVBS+S-VIDEO 5 S-VIDEO 5
+ *
+ * WHEN 5==input THE ARGUMENT mode MUST ALSO BE SUPPLIED:
+ *
+ * mode 7 => GAIN TO BE SET EXPLICITLY USING REGISTER 0x05 (UNTESTED)
+ * mode 9 => USE AUTOMATIC GAIN CONTROL (DEFAULT)
+ *
+*/
+/*---------------------------------------------------------------------------*/
+int
+select_input(struct usb_device *p, int input, int mode)
+{
+ int ir;
+
+ if (!p)
+ return -ENODEV;
+ stop_100(p);
+ switch (input) {
+ case 0:
+ case 1: {
+ if (0 != write_saa(p, 0x02, 0x80))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ SET(p, 0x0000, 0x0098);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 2: {
+ if (0 != write_saa(p, 0x02, 0x80))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ SET(p, 0x0000, 0x0090);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 3: {
+ if (0 != write_saa(p, 0x02, 0x80))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ " for input %i\n", input);
+
+ SET(p, 0x0000, 0x0088);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 4: {
+ if (0 != write_saa(p, 0x02, 0x80)) {
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+ }
+ SET(p, 0x0000, 0x0080);
+ SET(p, 0x0002, 0x0078);
+ break;
+ }
+ case 5: {
+ if (9 != mode)
+ mode = 7;
+ switch (mode) {
+ case 7: {
+ if (0 != write_saa(p, 0x02, 0x87))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ if (0 != write_saa(p, 0x05, 0xFF))
+ SAY("ERROR: failed to set SAA register 0x05 "
+ "for input %i\n", input);
+
+ break;
+ }
+ case 9: {
+ if (0 != write_saa(p, 0x02, 0x89))
+ SAY("ERROR: failed to set SAA register 0x02 "
+ "for input %i\n", input);
+
+ if (0 != write_saa(p, 0x05, 0x00))
+ SAY("ERROR: failed to set SAA register 0x05 "
+ "for input %i\n", input);
+
+ break;
+ }
+ default:
+ SAY("MISTAKE: bad mode: %i\n", mode);
+ return -1;
+ }
+
+ if (0 != write_saa(p, 0x04, 0x00))
+ SAY("ERROR: failed to set SAA register 0x04 "
+ "for input %i\n", input);
+
+ if (0 != write_saa(p, 0x09, 0x80))
+ SAY("ERROR: failed to set SAA register 0x09 "
+ "for input %i\n", input);
+
+ SET(p, 0x0002, 0x0093);
+ break;
+ }
+ default:
+ SAY("ERROR: bad input: %i\n", input);
+ return -1;
+ }
+
+ ir = read_stk(p, 0x00);
+ JOT(8, "STK register 0x00 has 0x%02X\n", ir);
+ ir = read_saa(p, 0x02);
+ JOT(8, "SAA register 0x02 has 0x%02X\n", ir);
+
+ start_100(p);
+
+ return 0;
+}
+/****************************************************************************/
+int set_resolution(struct usb_device *p,
+ u16 set0, u16 set1, u16 set2, u16 set3)
+{
+ u16 u0x0111, u0x0113, u0x0115, u0x0117;
+
+ if (!p)
+ return -ENODEV;
+ u0x0111 = ((0xFF00 & set0) >> 8);
+ u0x0113 = ((0xFF00 & set1) >> 8);
+ u0x0115 = ((0xFF00 & set2) >> 8);
+ u0x0117 = ((0xFF00 & set3) >> 8);
+
+ SET(p, 0x0110, (0x00FF & set0));
+ SET(p, 0x0111, u0x0111);
+ SET(p, 0x0112, (0x00FF & set1));
+ SET(p, 0x0113, u0x0113);
+ SET(p, 0x0114, (0x00FF & set2));
+ SET(p, 0x0115, u0x0115);
+ SET(p, 0x0116, (0x00FF & set3));
+ SET(p, 0x0117, u0x0117);
+
+ return 0;
+}
+/****************************************************************************/
+int start_100(struct usb_device *p)
+{
+ u16 get116, get117, get0;
+ u8 igot116, igot117, igot;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0116, &igot116);
+ get116 = igot116;
+ GET(p, 0x0117, &igot117);
+ get117 = igot117;
+ SET(p, 0x0116, 0x0000);
+ SET(p, 0x0117, 0x0000);
+
+ GET(p, 0x0100, &igot);
+ get0 = igot;
+ SET(p, 0x0100, (0x80 | get0));
+
+ SET(p, 0x0116, get116);
+ SET(p, 0x0117, get117);
+
+ return 0;
+}
+/****************************************************************************/
+int stop_100(struct usb_device *p)
+{
+ u16 get0;
+ u8 igot;
+
+ if (!p)
+ return -ENODEV;
+ GET(p, 0x0100, &igot);
+ get0 = igot;
+ SET(p, 0x0100, (0x7F & get0));
+ return 0;
+}
+/****************************************************************************/
+/****************************************************************************/
+/*****************************************************************************/
+int wakeup_device(struct usb_device *pusb_device)
+{
+ if (!pusb_device)
+ return -ENODEV;
+ return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
+ USB_REQ_SET_FEATURE,
+ USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE,
+ USB_DEVICE_REMOTE_WAKEUP,
+ 0, NULL, 0, 50000);
+}
+/*****************************************************************************/
+int
+audio_setup(struct easycap *peasycap)
+{
+ struct usb_device *pusb_device;
+ u8 buffer[1];
+ int rc, id1, id2;
+/*---------------------------------------------------------------------------*/
+/*
+ * IMPORTANT:
+ * THE MESSAGE OF TYPE (USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE)
+ * CAUSES MUTING IF THE VALUE 0x0100 IS SENT.
+ * TO ENABLE AUDIO THE VALUE 0x0200 MUST BE SENT.
+ */
+/*---------------------------------------------------------------------------*/
+ const u8 request = 0x01;
+ const u8 requesttype = USB_DIR_OUT |
+ USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE;
+ const u16 value_unmute = 0x0200;
+ const u16 index = 0x0301;
+ const u16 length = 1;
+
+ if (!peasycap)
+ return -EFAULT;
+
+ pusb_device = peasycap->pusb_device;
+ if (!pusb_device)
+ return -ENODEV;
+
+ JOM(8, "%02X %02X %02X %02X %02X %02X %02X %02X\n",
+ requesttype, request,
+ (0x00FF & value_unmute),
+ (0xFF00 & value_unmute) >> 8,
+ (0x00FF & index),
+ (0xFF00 & index) >> 8,
+ (0x00FF & length),
+ (0xFF00 & length) >> 8);
+
+ buffer[0] = 0x01;
+
+ rc = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0),
+ request, requesttype, value_unmute,
+ index, &buffer[0], length, 50000);
+
+ JOT(8, "0x%02X=buffer\n", buffer[0]);
+ if (rc != (int)length) {
+ switch (rc) {
+ case -EPIPE:
+ SAY("usb_control_msg returned -EPIPE\n");
+ break;
+ default:
+ SAY("ERROR: usb_control_msg returned %i\n", rc);
+ break;
+ }
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * REGISTER 500: SETTING VALUE TO 0x0094 RESETS AUDIO CONFIGURATION ???
+ * REGISTER 506: ANALOGUE AUDIO ATTENTUATOR ???
+ * FOR THE CVBS+S-VIDEO HARDWARE:
+ * SETTING VALUE TO 0x0000 GIVES QUIET SOUND.
+ * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
+ * FOR THE FOUR-CVBS HARDWARE:
+ * SETTING VALUE TO 0x0000 SEEMS TO HAVE NO EFFECT.
+ * REGISTER 507: ANALOGUE AUDIO PREAMPLIFIER ON/OFF ???
+ * FOR THE CVBS-S-VIDEO HARDWARE:
+ * SETTING VALUE TO 0x0001 GIVES VERY LOUD, DISTORTED SOUND.
+ * THE UPPER BYTE SEEMS TO HAVE NO EFFECT.
+ */
+/*--------------------------------------------------------------------------*/
+ SET(pusb_device, 0x0500, 0x0094);
+ SET(pusb_device, 0x0500, 0x008C);
+ SET(pusb_device, 0x0506, 0x0001);
+ SET(pusb_device, 0x0507, 0x0000);
+ id1 = read_vt(pusb_device, 0x007C);
+ id2 = read_vt(pusb_device, 0x007E);
+ SAM("0x%04X:0x%04X is audio vendor id\n", id1, id2);
+/*---------------------------------------------------------------------------*/
+/*
+ * SELECT AUDIO SOURCE "LINE IN" AND SET THE AUDIO GAIN.
+*/
+/*---------------------------------------------------------------------------*/
+ if (0 != audio_gainset(pusb_device, peasycap->gain))
+ SAY("ERROR: audio_gainset() failed\n");
+ check_vt(pusb_device);
+ return 0;
+}
+/*****************************************************************************/
+int check_vt(struct usb_device *pusb_device)
+{
+ int igot;
+
+ if (!pusb_device)
+ return -ENODEV;
+ igot = read_vt(pusb_device, 0x0002);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x02\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x02);
+
+ igot = read_vt(pusb_device, 0x000E);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x0E\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x0E);
+
+ igot = read_vt(pusb_device, 0x0010);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x10\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x10);
+
+ igot = read_vt(pusb_device, 0x0012);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x12\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x12);
+
+ igot = read_vt(pusb_device, 0x0014);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x14\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x14);
+
+ igot = read_vt(pusb_device, 0x0016);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x16\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x16);
+
+ igot = read_vt(pusb_device, 0x0018);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x18\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x18);
+
+ igot = read_vt(pusb_device, 0x001C);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x1C\n");
+ if (0x8000 & igot)
+ SAY("register 0x%02X muted\n", 0x1C);
+
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/* NOTE: THIS DOES INCREASE THE VOLUME DRAMATICALLY:
+ * audio_gainset(pusb_device, 0x000F);
+ *
+ * loud dB register 0x10 dB register 0x1C dB total
+ * 0 -34.5 0 -34.5
+ * .. .... . ....
+ * 15 10.5 0 10.5
+ * 16 12.0 0 12.0
+ * 17 12.0 1.5 13.5
+ * .. .... .... ....
+ * 31 12.0 22.5 34.5
+*/
+/*---------------------------------------------------------------------------*/
+int audio_gainset(struct usb_device *pusb_device, s8 loud)
+{
+ int igot;
+ u8 tmp;
+ u16 mute;
+
+ if (!pusb_device)
+ return -ENODEV;
+ if (0 > loud)
+ loud = 0;
+ if (31 < loud)
+ loud = 31;
+
+ write_vt(pusb_device, 0x0002, 0x8000);
+/*---------------------------------------------------------------------------*/
+ igot = read_vt(pusb_device, 0x000E);
+ if (0 > igot) {
+ SAY("ERROR: failed to read VT1612A register 0x0E\n");
+ mute = 0x0000;
+ } else
+ mute = 0x8000 & ((unsigned int)igot);
+ mute = 0;
+
+ if (16 > loud)
+ tmp = 0x01 | (0x001F & (((u8)(15 - loud)) << 1));
+ else
+ tmp = 0;
+
+ JOT(8, "0x%04X=(mute|tmp) for VT1612A register 0x0E\n", mute | tmp);
+ write_vt(pusb_device, 0x000E, (mute | tmp));
+/*---------------------------------------------------------------------------*/
+ igot = read_vt(pusb_device, 0x0010);
+ if (0 > igot) {
+ SAY("ERROR: failed to read VT1612A register 0x10\n");
+ mute = 0x0000;
+ } else
+ mute = 0x8000 & ((unsigned int)igot);
+ mute = 0;
+
+ JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x10,...0x18\n",
+ mute | tmp | (tmp << 8));
+ write_vt(pusb_device, 0x0010, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0012, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0014, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0016, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x0018, (mute | tmp | (tmp << 8)));
+/*---------------------------------------------------------------------------*/
+ igot = read_vt(pusb_device, 0x001C);
+ if (0 > igot) {
+ SAY("ERROR: failed to read VT1612A register 0x1C\n");
+ mute = 0x0000;
+ } else
+ mute = 0x8000 & ((unsigned int)igot);
+ mute = 0;
+
+ if (16 <= loud)
+ tmp = 0x000F & (u8)(loud - 16);
+ else
+ tmp = 0;
+
+ JOT(8, "0x%04X=(mute|tmp|(tmp<<8)) for VT1612A register 0x1C\n",
+ mute | tmp | (tmp << 8));
+ write_vt(pusb_device, 0x001C, (mute | tmp | (tmp << 8)));
+ write_vt(pusb_device, 0x001A, 0x0404);
+ write_vt(pusb_device, 0x0002, 0x0000);
+ return 0;
+}
+/*****************************************************************************/
+int audio_gainget(struct usb_device *pusb_device)
+{
+ int igot;
+
+ if (!pusb_device)
+ return -ENODEV;
+ igot = read_vt(pusb_device, 0x001C);
+ if (0 > igot)
+ SAY("ERROR: failed to read VT1612A register 0x1C\n");
+ return igot;
+}
+/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_main.c b/drivers/staging/media/easycap/easycap_main.c
new file mode 100644
index 000000000000..a45c0b507067
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap_main.c
@@ -0,0 +1,4253 @@
+/******************************************************************************
+* *
+* easycap_main.c *
+* *
+* Video driver for EasyCAP USB2.0 Video Capture Device DC60 *
+* *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+#include <linux/usb/audio.h>
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("R.M. Thomas <rmthomas@sciolus.org>");
+MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION);
+MODULE_VERSION(EASYCAP_DRIVER_VERSION);
+
+#ifdef CONFIG_EASYCAP_DEBUG
+int easycap_debug;
+module_param_named(debug, easycap_debug, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(debug, "Debug level: 0(default),1,2,...,9");
+#endif /* CONFIG_EASYCAP_DEBUG */
+
+bool easycap_readback;
+module_param_named(readback, easycap_readback, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(readback, "read back written registers: (default false)");
+
+static int easycap_bars = 1;
+module_param_named(bars, easycap_bars, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(bars,
+ "Testcard bars on input signal failure: 0=>no, 1=>yes(default)");
+
+static int easycap_gain = 16;
+module_param_named(gain, easycap_gain, int, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(gain, "Audio gain: 0,...,16(default),...31");
+
+static bool easycap_ntsc;
+module_param_named(ntsc, easycap_ntsc, bool, S_IRUGO | S_IWUSR);
+MODULE_PARM_DESC(ntsc, "NTSC default encoding (default PAL)");
+
+
+
+struct easycap_dongle easycapdc60_dongle[DONGLE_MANY];
+static struct mutex mutex_dongle;
+static void easycap_complete(struct urb *purb);
+static int reset(struct easycap *peasycap);
+
+const char *strerror(int err)
+{
+#define ERRNOSTR(_e) case _e: return # _e
+ switch (err) {
+ case 0: return "OK";
+ ERRNOSTR(ENOMEM);
+ ERRNOSTR(ENODEV);
+ ERRNOSTR(ENXIO);
+ ERRNOSTR(EINVAL);
+ ERRNOSTR(EAGAIN);
+ ERRNOSTR(EFBIG);
+ ERRNOSTR(EPIPE);
+ ERRNOSTR(EMSGSIZE);
+ ERRNOSTR(ENOSPC);
+ ERRNOSTR(EINPROGRESS);
+ ERRNOSTR(ENOSR);
+ ERRNOSTR(EOVERFLOW);
+ ERRNOSTR(EPROTO);
+ ERRNOSTR(EILSEQ);
+ ERRNOSTR(ETIMEDOUT);
+ ERRNOSTR(EOPNOTSUPP);
+ ERRNOSTR(EPFNOSUPPORT);
+ ERRNOSTR(EAFNOSUPPORT);
+ ERRNOSTR(EADDRINUSE);
+ ERRNOSTR(EADDRNOTAVAIL);
+ ERRNOSTR(ENOBUFS);
+ ERRNOSTR(EISCONN);
+ ERRNOSTR(ENOTCONN);
+ ERRNOSTR(ESHUTDOWN);
+ ERRNOSTR(ENOENT);
+ ERRNOSTR(ECONNRESET);
+ ERRNOSTR(ETIME);
+ ERRNOSTR(ECOMM);
+ ERRNOSTR(EREMOTEIO);
+ ERRNOSTR(EXDEV);
+ ERRNOSTR(EPERM);
+ default: return "unknown";
+ }
+
+#undef ERRNOSTR
+}
+
+/*---------------------------------------------------------------------------*/
+/*
+ * PARAMETERS USED WHEN REGISTERING THE VIDEO INTERFACE
+ *
+ * NOTE: SOME KERNELS IGNORE usb_class_driver.minor_base, AS MENTIONED BY
+ * CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGE 253.
+ * THIS IS THE CASE FOR OpenSUSE.
+ */
+/*---------------------------------------------------------------------------*/
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * THIS ROUTINE DOES NOT DETECT DUPLICATE OCCURRENCES OF POINTER peasycap
+*/
+/*---------------------------------------------------------------------------*/
+int isdongle(struct easycap *peasycap)
+{
+ int k;
+ if (!peasycap)
+ return -2;
+ for (k = 0; k < DONGLE_MANY; k++) {
+ if (easycapdc60_dongle[k].peasycap == peasycap) {
+ peasycap->isdongle = k;
+ return k;
+ }
+ }
+ return -1;
+}
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+static int easycap_open(struct inode *inode, struct file *file)
+{
+ struct video_device *pvideo_device;
+ struct easycap *peasycap;
+ int rc;
+
+ JOT(4, "\n");
+ SAY("==========OPEN=========\n");
+
+ pvideo_device = video_devdata(file);
+ if (!pvideo_device) {
+ SAY("ERROR: pvideo_device is NULL.\n");
+ return -EFAULT;
+ }
+ peasycap = (struct easycap *)video_get_drvdata(pvideo_device);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ } else {
+ JOM(16, "peasycap->pusb_device=%p\n", peasycap->pusb_device);
+ }
+ file->private_data = peasycap;
+ rc = wakeup_device(peasycap->pusb_device);
+ if (0 == rc)
+ JOM(8, "wakeup_device() OK\n");
+ else {
+ SAM("ERROR: wakeup_device() rc = %i\n", rc);
+ if (-ENODEV == rc)
+ SAM("ERROR: wakeup_device() returned -ENODEV\n");
+ else
+ SAM("ERROR: wakeup_device() rc = %i\n", rc);
+ return rc;
+ }
+ peasycap->input = 0;
+ rc = reset(peasycap);
+ if (rc) {
+ SAM("ERROR: reset() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ return 0;
+}
+
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * RESET THE HARDWARE TO ITS REFERENCE STATE.
+ *
+ * THIS ROUTINE MAY BE CALLED REPEATEDLY IF easycap_complete() DETECTS
+ * A BAD VIDEO FRAME SIZE.
+*/
+/*---------------------------------------------------------------------------*/
+static int reset(struct easycap *peasycap)
+{
+ struct easycap_standard const *peasycap_standard;
+ int fmtidx, input, rate;
+ bool ntsc, other;
+ int rc;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ input = peasycap->input;
+
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE SAA7113H HAS ALREADY ACQUIRED SYNC, USE ITS HARDWARE-DETECTED
+ * FIELD FREQUENCY TO DISTINGUISH NTSC FROM PAL. THIS IS ESSENTIAL FOR
+ * gstreamer AND OTHER USERSPACE PROGRAMS WHICH MAY NOT ATTEMPT TO INITIATE
+ * A SWITCH BETWEEN PAL AND NTSC.
+ *
+ * FUNCTION ready_saa() MAY REQUIRE A SUBSTANTIAL FRACTION OF A SECOND TO
+ * COMPLETE, SO SHOULD NOT BE INVOKED WITHOUT GOOD REASON.
+*/
+/*---------------------------------------------------------------------------*/
+ other = false;
+ JOM(8, "peasycap->ntsc=%d\n", peasycap->ntsc);
+
+ rate = ready_saa(peasycap->pusb_device);
+ if (rate < 0) {
+ JOM(8, "not ready to capture after %i ms ...\n", PATIENCE);
+ ntsc = !peasycap->ntsc;
+ JOM(8, "... trying %s ..\n", ntsc ? "NTSC" : "PAL");
+ rc = setup_stk(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_stk() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = setup_saa(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_saa() rc = %i\n", rc);
+ return -EFAULT;
+ }
+
+ rate = ready_saa(peasycap->pusb_device);
+ if (rate < 0) {
+ JOM(8, "not ready to capture after %i ms\n", PATIENCE);
+ JOM(8, "... saa register 0x1F has 0x%02X\n",
+ read_saa(peasycap->pusb_device, 0x1F));
+ ntsc = peasycap->ntsc;
+ } else {
+ JOM(8, "... success at second try: %i=rate\n", rate);
+ ntsc = (0 < (rate/2)) ? true : false ;
+ other = true;
+ }
+ } else {
+ JOM(8, "... success at first try: %i=rate\n", rate);
+ ntsc = (0 < rate/2) ? true : false ;
+ }
+ JOM(8, "ntsc=%d\n", ntsc);
+/*---------------------------------------------------------------------------*/
+
+ rc = setup_stk(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_stk() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = setup_saa(peasycap->pusb_device, ntsc);
+ if (rc) {
+ SAM("ERROR: setup_saa() rc = %i\n", rc);
+ return -EFAULT;
+ }
+
+ memset(peasycap->merit, 0, sizeof(peasycap->merit));
+
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+/*---------------------------------------------------------------------------*/
+/*
+ * RESTORE INPUT AND FORCE REFRESH OF STANDARD, FORMAT, ETC.
+ *
+ * WHILE THIS PROCEDURE IS IN PROGRESS, SOME IOCTL COMMANDS WILL RETURN -EBUSY.
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->input = -8192;
+ peasycap->standard_offset = -8192;
+ fmtidx = ntsc ? NTSC_M : PAL_BGHIN;
+ if (other) {
+ peasycap_standard = &easycap_standard[0];
+ while (0xFFFF != peasycap_standard->mask) {
+ if (fmtidx == peasycap_standard->v4l2_standard.index) {
+ peasycap->inputset[input].standard_offset =
+ peasycap_standard - easycap_standard;
+ break;
+ }
+ peasycap_standard++;
+ }
+ if (0xFFFF == peasycap_standard->mask) {
+ SAM("ERROR: standard not found\n");
+ return -EINVAL;
+ }
+ JOM(8, "%i=peasycap->inputset[%i].standard_offset\n",
+ peasycap->inputset[input].standard_offset, input);
+ }
+ peasycap->format_offset = -8192;
+ peasycap->brightness = -8192;
+ peasycap->contrast = -8192;
+ peasycap->saturation = -8192;
+ peasycap->hue = -8192;
+
+ rc = newinput(peasycap, input);
+
+ if (rc) {
+ SAM("ERROR: newinput(.,%i) rc = %i\n", rc, input);
+ return -EFAULT;
+ }
+ JOM(4, "restored input, standard and format\n");
+
+ JOM(8, "true=peasycap->ntsc %d\n", peasycap->ntsc);
+
+ if (0 > peasycap->input) {
+ SAM("MISTAKE: %i=peasycap->input\n", peasycap->input);
+ return -ENOENT;
+ }
+ if (0 > peasycap->standard_offset) {
+ SAM("MISTAKE: %i=peasycap->standard_offset\n",
+ peasycap->standard_offset);
+ return -ENOENT;
+ }
+ if (0 > peasycap->format_offset) {
+ SAM("MISTAKE: %i=peasycap->format_offset\n",
+ peasycap->format_offset);
+ return -ENOENT;
+ }
+ if (0 > peasycap->brightness) {
+ SAM("MISTAKE: %i=peasycap->brightness\n",
+ peasycap->brightness);
+ return -ENOENT;
+ }
+ if (0 > peasycap->contrast) {
+ SAM("MISTAKE: %i=peasycap->contrast\n", peasycap->contrast);
+ return -ENOENT;
+ }
+ if (0 > peasycap->saturation) {
+ SAM("MISTAKE: %i=peasycap->saturation\n",
+ peasycap->saturation);
+ return -ENOENT;
+ }
+ if (0 > peasycap->hue) {
+ SAM("MISTAKE: %i=peasycap->hue\n", peasycap->hue);
+ return -ENOENT;
+ }
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE REQUESTED INPUT IS THE SAME AS THE EXISTING INPUT, DO NOTHING.
+ * OTHERWISE:
+ * KILL URBS, CLEAR FIELD AND FRAME BUFFERS AND RESET THEIR
+ * _read AND _fill POINTERS.
+ * SELECT THE NEW INPUT.
+ * ADJUST THE STANDARD, FORMAT, BRIGHTNESS, CONTRAST, SATURATION AND HUE
+ * ON THE BASIS OF INFORMATION IN STRUCTURE easycap.inputset[input].
+ * RESUBMIT THE URBS IF STREAMING WAS ALREADY IN PROGRESS.
+ *
+ * NOTE:
+ * THIS ROUTINE MAY BE CALLED FREQUENTLY BY ZONEMINDER VIA IOCTL,
+ * SO IT SHOULD WRITE ONLY SPARINGLY TO THE LOGFILE.
+*/
+/*---------------------------------------------------------------------------*/
+int
+newinput(struct easycap *peasycap, int input)
+{
+ int rc, k, m, mood, off;
+ int inputnow, video_idlenow, audio_idlenow;
+ bool resubmit;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ JOM(8, "%i=input sought\n", input);
+
+ if (0 > input && INPUT_MANY <= input)
+ return -ENOENT;
+ inputnow = peasycap->input;
+ if (input == inputnow)
+ return 0;
+/*---------------------------------------------------------------------------*/
+/*
+ * IF STREAMING IS IN PROGRESS THE URBS ARE KILLED AT THIS
+ * STAGE AND WILL BE RESUBMITTED PRIOR TO EXIT FROM THE ROUTINE.
+ * IF NO STREAMING IS IN PROGRESS NO URBS WILL BE SUBMITTED BY THE
+ * ROUTINE.
+*/
+/*---------------------------------------------------------------------------*/
+ video_idlenow = peasycap->video_idle;
+ audio_idlenow = peasycap->audio_idle;
+
+ peasycap->video_idle = 1;
+ peasycap->audio_idle = 1;
+ if (peasycap->video_isoc_streaming) {
+ resubmit = true;
+ kill_video_urbs(peasycap);
+ } else {
+ resubmit = false;
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ rc = usb_set_interface(peasycap->pusb_device,
+ peasycap->video_interface,
+ peasycap->video_altsetting_off);
+ if (rc) {
+ SAM("ERROR: usb_set_interface() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = stop_100(peasycap->pusb_device);
+ if (rc) {
+ SAM("ERROR: stop_100() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ for (k = 0; k < FIELD_BUFFER_MANY; k++) {
+ for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++)
+ memset(peasycap->field_buffer[k][m].pgo, 0, PAGE_SIZE);
+ }
+ for (k = 0; k < FRAME_BUFFER_MANY; k++) {
+ for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++)
+ memset(peasycap->frame_buffer[k][m].pgo, 0, PAGE_SIZE);
+ }
+ peasycap->field_page = 0;
+ peasycap->field_read = 0;
+ peasycap->field_fill = 0;
+
+ peasycap->frame_read = 0;
+ peasycap->frame_fill = 0;
+ for (k = 0; k < peasycap->input; k++) {
+ (peasycap->frame_fill)++;
+ if (peasycap->frame_buffer_many <= peasycap->frame_fill)
+ peasycap->frame_fill = 0;
+ }
+ peasycap->input = input;
+ select_input(peasycap->pusb_device, peasycap->input, 9);
+/*---------------------------------------------------------------------------*/
+ if (input == peasycap->inputset[input].input) {
+ off = peasycap->inputset[input].standard_offset;
+ if (off != peasycap->standard_offset) {
+ rc = adjust_standard(peasycap,
+ easycap_standard[off].v4l2_standard.id);
+ if (rc) {
+ SAM("ERROR: adjust_standard() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->standard_offset\n",
+ peasycap->standard_offset);
+ } else {
+ JOM(8, "%i=peasycap->standard_offset unchanged\n",
+ peasycap->standard_offset);
+ }
+ off = peasycap->inputset[input].format_offset;
+ if (off != peasycap->format_offset) {
+ struct v4l2_pix_format *pix =
+ &easycap_format[off].v4l2_format.fmt.pix;
+ rc = adjust_format(peasycap,
+ pix->width, pix->height,
+ pix->pixelformat, pix->field, false);
+ if (0 > rc) {
+ SAM("ERROR: adjust_format() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->format_offset\n",
+ peasycap->format_offset);
+ } else {
+ JOM(8, "%i=peasycap->format_offset unchanged\n",
+ peasycap->format_offset);
+ }
+ mood = peasycap->inputset[input].brightness;
+ if (mood != peasycap->brightness) {
+ rc = adjust_brightness(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_brightness rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->brightness\n",
+ peasycap->brightness);
+ }
+ mood = peasycap->inputset[input].contrast;
+ if (mood != peasycap->contrast) {
+ rc = adjust_contrast(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_contrast rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->contrast\n", peasycap->contrast);
+ }
+ mood = peasycap->inputset[input].saturation;
+ if (mood != peasycap->saturation) {
+ rc = adjust_saturation(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_saturation rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->saturation\n",
+ peasycap->saturation);
+ }
+ mood = peasycap->inputset[input].hue;
+ if (mood != peasycap->hue) {
+ rc = adjust_hue(peasycap, mood);
+ if (rc) {
+ SAM("ERROR: adjust_hue rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(8, "%i=peasycap->hue\n", peasycap->hue);
+ }
+ } else {
+ SAM("MISTAKE: easycap.inputset[%i] unpopulated\n", input);
+ return -ENOENT;
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ rc = usb_set_interface(peasycap->pusb_device,
+ peasycap->video_interface,
+ peasycap->video_altsetting_on);
+ if (rc) {
+ SAM("ERROR: usb_set_interface() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ rc = start_100(peasycap->pusb_device);
+ if (rc) {
+ SAM("ERROR: start_100() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ if (resubmit)
+ submit_video_urbs(peasycap);
+
+ peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1;
+ peasycap->video_idle = video_idlenow;
+ peasycap->audio_idle = audio_idlenow;
+ peasycap->video_junk = 0;
+
+ return 0;
+}
+/*****************************************************************************/
+int submit_video_urbs(struct easycap *peasycap)
+{
+ struct data_urb *pdata_urb;
+ struct urb *purb;
+ struct list_head *plist_head;
+ int j, isbad, nospc, m, rc;
+ int isbuf;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ if (!peasycap->purb_video_head) {
+ SAY("ERROR: peasycap->urb_video_head uninitialized\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAY("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ if (!peasycap->video_isoc_streaming) {
+ JOM(4, "submission of all video urbs\n");
+ isbad = 0; nospc = 0; m = 0;
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ purb = pdata_urb->purb;
+ isbuf = pdata_urb->isbuf;
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe =
+ usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->video_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer =
+ peasycap->video_isoc_buffer[isbuf].pgo;
+ purb->transfer_buffer_length =
+ peasycap->video_isoc_buffer_size;
+ purb->complete = easycap_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets =
+ peasycap->video_isoc_framesperdesc;
+
+ for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j]. offset =
+ j * peasycap->video_isoc_maxframesize;
+ purb->iso_frame_desc[j]. length =
+ peasycap->video_isoc_maxframesize;
+ }
+
+ rc = usb_submit_urb(purb, GFP_KERNEL);
+ if (rc) {
+ isbad++;
+ SAM("ERROR: usb_submit_urb() failed "
+ "for urb with rc:-%s\n",
+ strerror(rc));
+ if (rc == -ENOSPC)
+ nospc++;
+ } else {
+ m++;
+ }
+ } else {
+ isbad++;
+ }
+ }
+ if (nospc) {
+ SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
+ SAM("..... possibly inadequate USB bandwidth\n");
+ peasycap->video_eof = 1;
+ }
+
+ if (isbad) {
+ JOM(4, "attempting cleanup instead of submitting\n");
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ purb = pdata_urb->purb;
+ if (purb)
+ usb_kill_urb(purb);
+ }
+ }
+ peasycap->video_isoc_streaming = 0;
+ } else {
+ peasycap->video_isoc_streaming = 1;
+ JOM(4, "submitted %i video urbs\n", m);
+ }
+ } else {
+ JOM(4, "already streaming video urbs\n");
+ }
+ return 0;
+}
+/*****************************************************************************/
+int kill_video_urbs(struct easycap *peasycap)
+{
+ int m;
+ struct list_head *plist_head;
+ struct data_urb *pdata_urb;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->video_isoc_streaming) {
+ JOM(8, "%i=video_isoc_streaming, no video urbs killed\n",
+ peasycap->video_isoc_streaming);
+ return 0;
+ }
+ if (!peasycap->purb_video_head) {
+ SAM("ERROR: peasycap->purb_video_head is NULL\n");
+ return -EFAULT;
+ }
+
+ peasycap->video_isoc_streaming = 0;
+ JOM(4, "killing video urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ JOM(4, "%i video urbs killed\n", m);
+
+ return 0;
+}
+/****************************************************************************/
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/*--------------------------------------------------------------------------*/
+static int easycap_open_noinode(struct file *file)
+{
+ return easycap_open(NULL, file);
+}
+
+static int videodev_release(struct video_device *pvideo_device)
+{
+ struct easycap *peasycap;
+
+ peasycap = video_get_drvdata(pvideo_device);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ SAY("ending unsuccessfully\n");
+ return -EFAULT;
+ }
+ if (0 != kill_video_urbs(peasycap)) {
+ SAM("ERROR: kill_video_urbs() failed\n");
+ return -EFAULT;
+ }
+ JOM(4, "ending successfully\n");
+ return 0;
+}
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+/*****************************************************************************/
+/*--------------------------------------------------------------------------*/
+/*
+ * THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect() AND IS
+ * PROTECTED BY SEMAPHORES SET AND CLEARED BY easycap_usb_disconnect().
+ *
+ * BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED, SO
+ * peasycap->pusb_device IS NO LONGER VALID.
+ */
+/*---------------------------------------------------------------------------*/
+static void easycap_delete(struct kref *pkref)
+{
+ struct easycap *peasycap;
+ struct data_urb *pdata_urb;
+ struct list_head *plist_head, *plist_next;
+ int k, m, gone, kd;
+ int allocation_video_urb;
+ int allocation_video_page;
+ int allocation_video_struct;
+ int allocation_audio_urb;
+ int allocation_audio_page;
+ int allocation_audio_struct;
+ int registered_video, registered_audio;
+
+ peasycap = container_of(pkref, struct easycap, kref);
+ if (!peasycap) {
+ SAM("ERROR: peasycap is NULL: cannot perform deletions\n");
+ return;
+ }
+ kd = isdongle(peasycap);
+/*---------------------------------------------------------------------------*/
+/*
+ * FREE VIDEO.
+ */
+/*---------------------------------------------------------------------------*/
+ if (peasycap->purb_video_head) {
+ JOM(4, "freeing video urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_video_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (!pdata_urb) {
+ JOM(4, "ERROR: pdata_urb is NULL\n");
+ } else {
+ if (pdata_urb->purb) {
+ usb_free_urb(pdata_urb->purb);
+ pdata_urb->purb = NULL;
+ peasycap->allocation_video_urb -= 1;
+ m++;
+ }
+ }
+ }
+
+ JOM(4, "%i video urbs freed\n", m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video data_urb structures.\n");
+ m = 0;
+ list_for_each_safe(plist_head, plist_next,
+ peasycap->purb_video_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ peasycap->allocation_video_struct -=
+ sizeof(struct data_urb);
+ kfree(pdata_urb);
+ pdata_urb = NULL;
+ m++;
+ }
+ }
+ JOM(4, "%i video data_urb structures freed\n", m);
+ JOM(4, "setting peasycap->purb_video_head=NULL\n");
+ peasycap->purb_video_head = NULL;
+ }
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video isoc buffers.\n");
+ m = 0;
+ for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
+ if (peasycap->video_isoc_buffer[k].pgo) {
+ free_pages((unsigned long)
+ peasycap->video_isoc_buffer[k].pgo,
+ VIDEO_ISOC_ORDER);
+ peasycap->video_isoc_buffer[k].pgo = NULL;
+ peasycap->allocation_video_page -=
+ BIT(VIDEO_ISOC_ORDER);
+ m++;
+ }
+ }
+ JOM(4, "isoc video buffers freed: %i pages\n",
+ m * (0x01 << VIDEO_ISOC_ORDER));
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video field buffers.\n");
+ gone = 0;
+ for (k = 0; k < FIELD_BUFFER_MANY; k++) {
+ for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->field_buffer[k][m].pgo) {
+ free_page((unsigned long)
+ peasycap->field_buffer[k][m].pgo);
+ peasycap->field_buffer[k][m].pgo = NULL;
+ peasycap->allocation_video_page -= 1;
+ gone++;
+ }
+ }
+ }
+ JOM(4, "video field buffers freed: %i pages\n", gone);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing video frame buffers.\n");
+ gone = 0;
+ for (k = 0; k < FRAME_BUFFER_MANY; k++) {
+ for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->frame_buffer[k][m].pgo) {
+ free_page((unsigned long)
+ peasycap->frame_buffer[k][m].pgo);
+ peasycap->frame_buffer[k][m].pgo = NULL;
+ peasycap->allocation_video_page -= 1;
+ gone++;
+ }
+ }
+ }
+ JOM(4, "video frame buffers freed: %i pages\n", gone);
+/*---------------------------------------------------------------------------*/
+/*
+ * FREE AUDIO.
+ */
+/*---------------------------------------------------------------------------*/
+ if (peasycap->purb_audio_head) {
+ JOM(4, "freeing audio urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_audio_head)) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (!pdata_urb)
+ JOM(4, "ERROR: pdata_urb is NULL\n");
+ else {
+ if (pdata_urb->purb) {
+ usb_free_urb(pdata_urb->purb);
+ pdata_urb->purb = NULL;
+ peasycap->allocation_audio_urb -= 1;
+ m++;
+ }
+ }
+ }
+ JOM(4, "%i audio urbs freed\n", m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing audio data_urb structures.\n");
+ m = 0;
+ list_for_each_safe(plist_head, plist_next,
+ peasycap->purb_audio_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ peasycap->allocation_audio_struct -=
+ sizeof(struct data_urb);
+ kfree(pdata_urb);
+ pdata_urb = NULL;
+ m++;
+ }
+ }
+ JOM(4, "%i audio data_urb structures freed\n", m);
+ JOM(4, "setting peasycap->purb_audio_head=NULL\n");
+ peasycap->purb_audio_head = NULL;
+ }
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing audio isoc buffers.\n");
+ m = 0;
+ for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
+ if (peasycap->audio_isoc_buffer[k].pgo) {
+ free_pages((unsigned long)
+ (peasycap->audio_isoc_buffer[k].pgo),
+ AUDIO_ISOC_ORDER);
+ peasycap->audio_isoc_buffer[k].pgo = NULL;
+ peasycap->allocation_audio_page -=
+ BIT(AUDIO_ISOC_ORDER);
+ m++;
+ }
+ }
+ JOM(4, "easyoss_delete(): isoc audio buffers freed: %i pages\n",
+ m * (0x01 << AUDIO_ISOC_ORDER));
+/*---------------------------------------------------------------------------*/
+ JOM(4, "freeing easycap structure.\n");
+ allocation_video_urb = peasycap->allocation_video_urb;
+ allocation_video_page = peasycap->allocation_video_page;
+ allocation_video_struct = peasycap->allocation_video_struct;
+ registered_video = peasycap->registered_video;
+ allocation_audio_urb = peasycap->allocation_audio_urb;
+ allocation_audio_page = peasycap->allocation_audio_page;
+ allocation_audio_struct = peasycap->allocation_audio_struct;
+ registered_audio = peasycap->registered_audio;
+
+ if (0 <= kd && DONGLE_MANY > kd) {
+ if (mutex_lock_interruptible(&mutex_dongle)) {
+ SAY("ERROR: cannot down mutex_dongle\n");
+ } else {
+ JOM(4, "locked mutex_dongle\n");
+ easycapdc60_dongle[kd].peasycap = NULL;
+ mutex_unlock(&mutex_dongle);
+ JOM(4, "unlocked mutex_dongle\n");
+ JOT(4, " null-->dongle[%i].peasycap\n", kd);
+ allocation_video_struct -= sizeof(struct easycap);
+ }
+ } else {
+ SAY("ERROR: cannot purge dongle[].peasycap");
+ }
+
+ kfree(peasycap);
+
+/*---------------------------------------------------------------------------*/
+ SAY("%8i=video urbs after all deletions\n", allocation_video_urb);
+ SAY("%8i=video pages after all deletions\n", allocation_video_page);
+ SAY("%8i=video structs after all deletions\n", allocation_video_struct);
+ SAY("%8i=video devices after all deletions\n", registered_video);
+ SAY("%8i=audio urbs after all deletions\n", allocation_audio_urb);
+ SAY("%8i=audio pages after all deletions\n", allocation_audio_page);
+ SAY("%8i=audio structs after all deletions\n", allocation_audio_struct);
+ SAY("%8i=audio devices after all deletions\n", registered_audio);
+
+ JOT(4, "ending.\n");
+ return;
+}
+/*****************************************************************************/
+static unsigned int easycap_poll(struct file *file, poll_table *wait)
+{
+ struct easycap *peasycap;
+ int rc, kd;
+
+ JOT(8, "\n");
+
+ if (NULL == ((poll_table *)wait))
+ JOT(8, "WARNING: poll table pointer is NULL ... continuing\n");
+ if (!file) {
+ SAY("ERROR: file pointer is NULL\n");
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAY("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+ kd = isdongle(peasycap);
+ if (0 <= kd && DONGLE_MANY > kd) {
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
+ SAY("ERROR: cannot down dongle[%i].mutex_video\n", kd);
+ return -ERESTARTSYS;
+ }
+ JOM(4, "locked dongle[%i].mutex_video\n", kd);
+ /*
+ * MEANWHILE, easycap_usb_disconnect() MAY HAVE FREED POINTER
+ * peasycap, IN WHICH CASE A REPEAT CALL TO isdongle() WILL FAIL.
+ * IF NECESSARY, BAIL OUT.
+ */
+ if (kd != isdongle(peasycap)) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ if (!file) {
+ SAY("ERROR: file is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ peasycap = file->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return -ERESTARTSYS;
+ }
+ } else
+ /*
+ * IF easycap_usb_disconnect() HAS ALREADY FREED POINTER peasycap
+ * BEFORE THE ATTEMPT TO ACQUIRE THE SEMAPHORE, isdongle() WILL
+ * HAVE FAILED. BAIL OUT.
+ */
+ return -ERESTARTSYS;
+/*---------------------------------------------------------------------------*/
+ rc = easycap_dqbuf(peasycap, 0);
+ peasycap->polled = 1;
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ if (0 == rc)
+ return POLLIN | POLLRDNORM;
+ else
+ return POLLERR;
+ }
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * IF mode IS NONZERO THIS ROUTINE RETURNS -EAGAIN RATHER THAN BLOCKING.
+ */
+/*---------------------------------------------------------------------------*/
+int easycap_dqbuf(struct easycap *peasycap, int mode)
+{
+ int input, ifield, miss, rc;
+
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAY("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+ ifield = 0;
+ JOM(8, "%i=ifield\n", ifield);
+/*---------------------------------------------------------------------------*/
+/*
+ * CHECK FOR LOST INPUT SIGNAL.
+ *
+ * FOR THE FOUR-CVBS EasyCAP, THIS DOES NOT WORK AS EXPECTED.
+ * IF INPUT 0 IS PRESENT AND SYNC ACQUIRED, UNPLUGGING INPUT 4 DOES NOT
+ * RESULT IN SETTING BIT 0x40 ON REGISTER 0x1F, PRESUMABLY BECAUSE THERE
+ * IS FLYWHEELING ON INPUT 0. THE UPSHOT IS:
+ *
+ * INPUT 0 PLUGGED, INPUT 4 PLUGGED => SCREEN 0 OK, SCREEN 4 OK
+ * INPUT 0 PLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 OK, SCREEN 4 BLACK
+ * INPUT 0 UNPLUGGED, INPUT 4 PLUGGED => SCREEN 0 BARS, SCREEN 4 OK
+ * INPUT 0 UNPLUGGED, INPUT 4 UNPLUGGED => SCREEN 0 BARS, SCREEN 4 BARS
+*/
+/*---------------------------------------------------------------------------*/
+ input = peasycap->input;
+ if (0 <= input && INPUT_MANY > input) {
+ rc = read_saa(peasycap->pusb_device, 0x1F);
+ if (0 <= rc) {
+ if (rc & 0x40)
+ peasycap->lost[input] += 1;
+ else
+ peasycap->lost[input] -= 2;
+
+ if (0 > peasycap->lost[input])
+ peasycap->lost[input] = 0;
+ else if ((2 * VIDEO_LOST_TOLERATE) < peasycap->lost[input])
+ peasycap->lost[input] = (2 * VIDEO_LOST_TOLERATE);
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * WAIT FOR FIELD ifield (0 => TOP, 1 => BOTTOM)
+ */
+/*---------------------------------------------------------------------------*/
+ miss = 0;
+ while ((peasycap->field_read == peasycap->field_fill) ||
+ (0 != (0xFF00 & peasycap->field_buffer
+ [peasycap->field_read][0].kount)) ||
+ (ifield != (0x00FF & peasycap->field_buffer
+ [peasycap->field_read][0].kount))) {
+ if (mode)
+ return -EAGAIN;
+
+ JOM(8, "first wait on wq_video, %i=field_read %i=field_fill\n",
+ peasycap->field_read, peasycap->field_fill);
+
+ if (0 != (wait_event_interruptible(peasycap->wq_video,
+ (peasycap->video_idle || peasycap->video_eof ||
+ ((peasycap->field_read != peasycap->field_fill) &&
+ (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
+ (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
+ SAM("aborted by signal\n");
+ return -EIO;
+ }
+ if (peasycap->video_idle) {
+ JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
+ peasycap->video_idle);
+ return -EAGAIN;
+ }
+ if (peasycap->video_eof) {
+ JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
+ #if defined(PERSEVERE)
+ if (1 == peasycap->status) {
+ JOM(8, "persevering ...\n");
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+ if (0 != reset(peasycap)) {
+ JOM(8, " ... failed returning -EIO\n");
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ return -EIO;
+ }
+ peasycap->status = 0;
+ JOM(8, " ... OK returning -EAGAIN\n");
+ return -EAGAIN;
+ }
+ #endif /*PERSEVERE*/
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ JOM(8, "returning -EIO\n");
+ return -EIO;
+ }
+ miss++;
+ }
+ JOM(8, "first awakening on wq_video after %i waits\n", miss);
+
+ rc = field2frame(peasycap);
+ if (rc)
+ SAM("ERROR: field2frame() rc = %i\n", rc);
+/*---------------------------------------------------------------------------*/
+/*
+ * WAIT FOR THE OTHER FIELD
+ */
+/*---------------------------------------------------------------------------*/
+ if (ifield)
+ ifield = 0;
+ else
+ ifield = 1;
+ miss = 0;
+ while ((peasycap->field_read == peasycap->field_fill) ||
+ (0 != (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) ||
+ (ifield != (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))) {
+ if (mode)
+ return -EAGAIN;
+
+ JOM(8, "second wait on wq_video %i=field_read %i=field_fill\n",
+ peasycap->field_read, peasycap->field_fill);
+ if (0 != (wait_event_interruptible(peasycap->wq_video,
+ (peasycap->video_idle || peasycap->video_eof ||
+ ((peasycap->field_read != peasycap->field_fill) &&
+ (0 == (0xFF00 & peasycap->field_buffer[peasycap->field_read][0].kount)) &&
+ (ifield == (0x00FF & peasycap->field_buffer[peasycap->field_read][0].kount))))))) {
+ SAM("aborted by signal\n");
+ return -EIO;
+ }
+ if (peasycap->video_idle) {
+ JOM(8, "%i=peasycap->video_idle returning -EAGAIN\n",
+ peasycap->video_idle);
+ return -EAGAIN;
+ }
+ if (peasycap->video_eof) {
+ JOM(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
+#if defined(PERSEVERE)
+ if (1 == peasycap->status) {
+ JOM(8, "persevering ...\n");
+ peasycap->video_eof = 0;
+ peasycap->audio_eof = 0;
+ if (0 != reset(peasycap)) {
+ JOM(8, " ... failed returning -EIO\n");
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ return -EIO;
+ }
+ peasycap->status = 0;
+ JOM(8, " ... OK ... returning -EAGAIN\n");
+ return -EAGAIN;
+ }
+#endif /*PERSEVERE*/
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ kill_video_urbs(peasycap);
+ JOM(8, "returning -EIO\n");
+ return -EIO;
+ }
+ miss++;
+ }
+ JOM(8, "second awakening on wq_video after %i waits\n", miss);
+
+ rc = field2frame(peasycap);
+ if (rc)
+ SAM("ERROR: field2frame() rc = %i\n", rc);
+/*---------------------------------------------------------------------------*/
+/*
+ * WASTE THIS FRAME
+*/
+/*---------------------------------------------------------------------------*/
+ if (peasycap->skip) {
+ peasycap->skipped++;
+ if (peasycap->skip != peasycap->skipped)
+ return peasycap->skip - peasycap->skipped;
+ else
+ peasycap->skipped = 0;
+ }
+/*---------------------------------------------------------------------------*/
+ peasycap->frame_read = peasycap->frame_fill;
+ peasycap->queued[peasycap->frame_read] = 0;
+ peasycap->done[peasycap->frame_read] = V4L2_BUF_FLAG_DONE;
+
+ peasycap->frame_fill++;
+ if (peasycap->frame_buffer_many <= peasycap->frame_fill)
+ peasycap->frame_fill = 0;
+
+ if (0x01 & easycap_standard[peasycap->standard_offset].mask)
+ peasycap->frame_buffer[peasycap->frame_read][0].kount =
+ V4L2_FIELD_TOP;
+ else
+ peasycap->frame_buffer[peasycap->frame_read][0].kount =
+ V4L2_FIELD_BOTTOM;
+
+
+ JOM(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read);
+ JOM(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill);
+
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * BY DEFINITION, odd IS true FOR THE FIELD OCCUPYING LINES 1,3,5,...,479
+ * odd IS false FOR THE FIELD OCCUPYING LINES 0,2,4,...,478
+ *
+ * WHEN BOOLEAN PARAMETER decimatepixel IS true, ONLY THE FIELD FOR WHICH
+ * odd==false IS TRANSFERRED TO THE FRAME BUFFER.
+ *
+ * THE BOOLEAN PARAMETER offerfields IS true ONLY WHEN THE USER PROGRAM
+ * CHOOSES THE OPTION V4L2_FIELD_INTERLACED.
+ */
+/*---------------------------------------------------------------------------*/
+int
+field2frame(struct easycap *peasycap)
+{
+
+ void *pex, *pad;
+ int kex, kad, mex, mad, rex, rad, rad2;
+ int c2, c3, w2, w3, cz, wz;
+ int rc, bytesperpixel, multiplier;
+ int much, more, over, rump, caches, input;
+ u8 mask, margin;
+ bool odd, isuy, decimatepixel, offerfields, badinput;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ badinput = false;
+ input = 0x07 & peasycap->field_buffer[peasycap->field_read][0].input;
+
+ JOM(8, "===== parity %i, input 0x%02X, field buffer %i --> "
+ "frame buffer %i\n",
+ peasycap->field_buffer[peasycap->field_read][0].kount,
+ peasycap->field_buffer[peasycap->field_read][0].input,
+ peasycap->field_read, peasycap->frame_fill);
+ JOM(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel);
+ if (peasycap->offerfields)
+ JOM(8, "===== offerfields\n");
+
+/*---------------------------------------------------------------------------*/
+/*
+ * REJECT OR CLEAN BAD FIELDS
+ */
+/*---------------------------------------------------------------------------*/
+ if (peasycap->field_read == peasycap->field_fill) {
+ SAM("ERROR: on entry, still filling field buffer %i\n",
+ peasycap->field_read);
+ return 0;
+ }
+#ifdef EASYCAP_TESTCARD
+ easycap_testcard(peasycap, peasycap->field_read);
+#else
+ if (0 <= input && INPUT_MANY > input) {
+ if (easycap_bars && VIDEO_LOST_TOLERATE <= peasycap->lost[input])
+ easycap_testcard(peasycap, peasycap->field_read);
+ }
+#endif /*EASYCAP_TESTCARD*/
+/*---------------------------------------------------------------------------*/
+
+ offerfields = peasycap->offerfields;
+ bytesperpixel = peasycap->bytesperpixel;
+ decimatepixel = peasycap->decimatepixel;
+
+ if ((2 != bytesperpixel) &&
+ (3 != bytesperpixel) &&
+ (4 != bytesperpixel)) {
+ SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
+ return -EFAULT;
+ }
+ if (decimatepixel)
+ multiplier = 2;
+ else
+ multiplier = 1;
+
+ w2 = 2 * multiplier * (peasycap->width);
+ w3 = bytesperpixel * multiplier * (peasycap->width);
+ wz = multiplier * (peasycap->height) *
+ multiplier * (peasycap->width);
+
+ kex = peasycap->field_read; mex = 0;
+ kad = peasycap->frame_fill; mad = 0;
+
+ pex = peasycap->field_buffer[kex][0].pgo; rex = PAGE_SIZE;
+ pad = peasycap->frame_buffer[kad][0].pgo; rad = PAGE_SIZE;
+ odd = !!(peasycap->field_buffer[kex][0].kount);
+
+ if (odd && (!decimatepixel)) {
+ JOM(8, "initial skipping %4i bytes p.%4i\n",
+ w3/multiplier, mad);
+ pad += (w3 / multiplier); rad -= (w3 / multiplier);
+ }
+ isuy = true;
+ mask = 0; rump = 0; caches = 0;
+
+ cz = 0;
+ while (cz < wz) {
+ /*
+ * PROCESS ONE LINE OF FRAME AT FULL RESOLUTION:
+ * READ w2 BYTES FROM FIELD BUFFER,
+ * WRITE w3 BYTES TO FRAME BUFFER
+ */
+ if (!decimatepixel) {
+ over = w2;
+ do {
+ much = over; more = 0;
+ margin = 0; mask = 0x00;
+ if (rex < much)
+ much = rex;
+ rump = 0;
+
+ if (much % 2) {
+ SAM("MISTAKE: much is odd\n");
+ return -EFAULT;
+ }
+
+ more = (bytesperpixel *
+ much) / 2;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ if (1 < bytesperpixel) {
+ if (rad * 2 < much * bytesperpixel) {
+ /*
+ * INJUDICIOUS ALTERATION OF
+ * THIS STATEMENT BLOCK WILL
+ * CAUSE BREAKAGE. BEWARE.
+ */
+ rad2 = rad + bytesperpixel - 1;
+ much = ((((2 * rad2)/bytesperpixel)/2) * 2);
+ rump = ((bytesperpixel * much) / 2) - rad;
+ more = rad;
+ }
+ mask = (u8)rump;
+ margin = 0;
+ if (much == rex) {
+ mask |= 0x04;
+ if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
+ margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
+ else
+ mask |= 0x08;
+ }
+ } else {
+ SAM("MISTAKE: %i=bytesperpixel\n",
+ bytesperpixel);
+ return -EFAULT;
+ }
+ if (rump)
+ caches++;
+ if (badinput) {
+ JOM(8, "ERROR: 0x%02X=->field_buffer"
+ "[%i][%i].input, "
+ "0x%02X=(0x08|->input)\n",
+ peasycap->field_buffer
+ [kex][mex].input, kex, mex,
+ (0x08|peasycap->input));
+ }
+ rc = redaub(peasycap, pad, pex, much, more,
+ mask, margin, isuy);
+ if (0 > rc) {
+ SAM("ERROR: redaub() failed\n");
+ return -EFAULT;
+ }
+ if (much % 4)
+ isuy = !isuy;
+
+ over -= much; cz += much;
+ pex += much; rex -= much;
+ if (!rex) {
+ mex++;
+ pex = peasycap->field_buffer[kex][mex].pgo;
+ rex = PAGE_SIZE;
+ if (peasycap->field_buffer[kex][mex].input != (0x08|peasycap->input))
+ badinput = true;
+ }
+ pad += more;
+ rad -= more;
+ if (!rad) {
+ mad++;
+ pad = peasycap->frame_buffer[kad][mad].pgo;
+ rad = PAGE_SIZE;
+ if (rump) {
+ pad += rump;
+ rad -= rump;
+ }
+ }
+ } while (over);
+/*---------------------------------------------------------------------------*/
+/*
+ * SKIP w3 BYTES IN TARGET FRAME BUFFER,
+ * UNLESS IT IS THE LAST LINE OF AN ODD FRAME
+ */
+/*---------------------------------------------------------------------------*/
+ if (!odd || (cz != wz)) {
+ over = w3;
+ do {
+ if (!rad) {
+ mad++;
+ pad = peasycap->frame_buffer
+ [kad][mad].pgo;
+ rad = PAGE_SIZE;
+ }
+ more = over;
+ if (rad < more)
+ more = rad;
+ over -= more;
+ pad += more;
+ rad -= more;
+ } while (over);
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PROCESS ONE LINE OF FRAME AT REDUCED RESOLUTION:
+ * ONLY IF false==odd,
+ * READ w2 BYTES FROM FIELD BUFFER,
+ * WRITE w3 / 2 BYTES TO FRAME BUFFER
+ */
+/*---------------------------------------------------------------------------*/
+ } else if (!odd) {
+ over = w2;
+ do {
+ much = over; more = 0; margin = 0; mask = 0x00;
+ if (rex < much)
+ much = rex;
+ rump = 0;
+
+ if (much % 2) {
+ SAM("MISTAKE: much is odd\n");
+ return -EFAULT;
+ }
+
+ more = (bytesperpixel * much) / 4;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ if (1 < bytesperpixel) {
+ if (rad * 4 < much * bytesperpixel) {
+ /*
+ * INJUDICIOUS ALTERATION OF
+ * THIS STATEMENT BLOCK
+ * WILL CAUSE BREAKAGE.
+ * BEWARE.
+ */
+ rad2 = rad + bytesperpixel - 1;
+ much = ((((2 * rad2) / bytesperpixel) / 2) * 4);
+ rump = ((bytesperpixel * much) / 4) - rad;
+ more = rad;
+ }
+ mask = (u8)rump;
+ margin = 0;
+ if (much == rex) {
+ mask |= 0x04;
+ if ((mex + 1) < FIELD_BUFFER_SIZE / PAGE_SIZE)
+ margin = *((u8 *)(peasycap->field_buffer[kex][mex + 1].pgo));
+ else
+ mask |= 0x08;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ } else {
+ SAM("MISTAKE: %i=bytesperpixel\n",
+ bytesperpixel);
+ return -EFAULT;
+ }
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ if (rump)
+ caches++;
+
+ if (badinput) {
+ JOM(8, "ERROR: 0x%02X=->field_buffer"
+ "[%i][%i].input, "
+ "0x%02X=(0x08|->input)\n",
+ peasycap->field_buffer
+ [kex][mex].input, kex, mex,
+ (0x08|peasycap->input));
+ }
+ rc = redaub(peasycap, pad, pex, much, more,
+ mask, margin, isuy);
+ if (0 > rc) {
+ SAM("ERROR: redaub() failed\n");
+ return -EFAULT;
+ }
+ over -= much; cz += much;
+ pex += much; rex -= much;
+ if (!rex) {
+ mex++;
+ pex = peasycap->field_buffer[kex][mex].pgo;
+ rex = PAGE_SIZE;
+ if (peasycap->field_buffer[kex][mex].input !=
+ (0x08|peasycap->input))
+ badinput = true;
+ }
+ pad += more;
+ rad -= more;
+ if (!rad) {
+ mad++;
+ pad = peasycap->frame_buffer[kad][mad].pgo;
+ rad = PAGE_SIZE;
+ if (rump) {
+ pad += rump;
+ rad -= rump;
+ }
+ }
+ } while (over);
+/*---------------------------------------------------------------------------*/
+/*
+ * OTHERWISE JUST
+ * READ w2 BYTES FROM FIELD BUFFER AND DISCARD THEM
+ */
+/*---------------------------------------------------------------------------*/
+ } else {
+ over = w2;
+ do {
+ if (!rex) {
+ mex++;
+ pex = peasycap->field_buffer[kex][mex].pgo;
+ rex = PAGE_SIZE;
+ if (peasycap->field_buffer[kex][mex].input !=
+ (0x08|peasycap->input)) {
+ JOM(8, "ERROR: 0x%02X=->field_buffer"
+ "[%i][%i].input, "
+ "0x%02X=(0x08|->input)\n",
+ peasycap->field_buffer
+ [kex][mex].input, kex, mex,
+ (0x08|peasycap->input));
+ badinput = true;
+ }
+ }
+ much = over;
+ if (rex < much)
+ much = rex;
+ over -= much;
+ cz += much;
+ pex += much;
+ rex -= much;
+ } while (over);
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * SANITY CHECKS
+ */
+/*---------------------------------------------------------------------------*/
+ c2 = (mex + 1)*PAGE_SIZE - rex;
+ if (cz != c2)
+ SAM("ERROR: discrepancy %i in bytes read\n", c2 - cz);
+ c3 = (mad + 1)*PAGE_SIZE - rad;
+
+ if (!decimatepixel) {
+ if (bytesperpixel * cz != c3)
+ SAM("ERROR: discrepancy %i in bytes written\n",
+ c3 - (bytesperpixel * cz));
+ } else {
+ if (!odd) {
+ if (bytesperpixel *
+ cz != (4 * c3))
+ SAM("ERROR: discrepancy %i in bytes written\n",
+ (2*c3)-(bytesperpixel * cz));
+ } else {
+ if (0 != c3)
+ SAM("ERROR: discrepancy %i "
+ "in bytes written\n", c3);
+ }
+ }
+ if (rump)
+ SAM("WORRY: undischarged cache at end of line in frame buffer\n");
+
+ JOM(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3);
+ JOM(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad);
+
+ if (odd)
+ JOM(8, "+++++ field2frame(): frame buffer %i is full\n", kad);
+
+ if (peasycap->field_read == peasycap->field_fill)
+ SAM("WARNING: on exit, filling field buffer %i\n",
+ peasycap->field_read);
+
+ if (caches)
+ JOM(8, "%i=caches\n", caches);
+ return 0;
+}
+/*---------------------------------------------------------------------------*/
+/*
+ * DECIMATION AND COLOURSPACE CONVERSION.
+ *
+ * THIS ROUTINE REQUIRES THAT ALL THE DATA TO BE READ RESIDES ON ONE PAGE
+ * AND THAT ALL THE DATA TO BE WRITTEN RESIDES ON ONE (DIFFERENT) PAGE.
+ * THE CALLING ROUTINE MUST ENSURE THAT THIS REQUIREMENT IS MET, AND MUST
+ * ALSO ENSURE THAT much IS EVEN.
+ *
+ * much BYTES ARE READ, AT LEAST (bytesperpixel * much)/2 BYTES ARE WRITTEN
+ * IF THERE IS NO DECIMATION, HALF THIS AMOUNT IF THERE IS DECIMATION.
+ *
+ * mask IS ZERO WHEN NO SPECIAL BEHAVIOUR REQUIRED. OTHERWISE IT IS SET THUS:
+ * 0x03 & mask = number of bytes to be written to cache instead of to
+ * frame buffer
+ * 0x04 & mask => use argument margin to set the chrominance for last pixel
+ * 0x08 & mask => do not set the chrominance for last pixel
+ *
+ * YUV to RGB CONVERSION IS (OR SHOULD BE) ITU-R BT 601.
+ *
+ * THERE IS A LOT OF CODE REPETITION IN THIS ROUTINE IN ORDER TO AVOID
+ * INEFFICIENT SWITCHING INSIDE INNER LOOPS. REARRANGING THE LOGIC TO
+ * REDUCE CODE LENGTH WILL GENERALLY IMPAIR RUNTIME PERFORMANCE. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+int
+redaub(struct easycap *peasycap, void *pad, void *pex, int much, int more,
+ u8 mask, u8 margin, bool isuy)
+{
+ static s32 ay[256], bu[256], rv[256], gu[256], gv[256];
+ u8 *pcache;
+ u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr;
+ int bytesperpixel;
+ bool byteswaporder, decimatepixel, last;
+ int j, rump;
+ s32 tmp;
+
+ if (much % 2) {
+ SAM("MISTAKE: much is odd\n");
+ return -EFAULT;
+ }
+ bytesperpixel = peasycap->bytesperpixel;
+ byteswaporder = peasycap->byteswaporder;
+ decimatepixel = peasycap->decimatepixel;
+
+/*---------------------------------------------------------------------------*/
+ if (!bu[255]) {
+ for (j = 0; j < 112; j++) {
+ tmp = (0xFF00 & (453 * j)) >> 8;
+ bu[j + 128] = tmp; bu[127 - j] = -tmp;
+ tmp = (0xFF00 & (359 * j)) >> 8;
+ rv[j + 128] = tmp; rv[127 - j] = -tmp;
+ tmp = (0xFF00 & (88 * j)) >> 8;
+ gu[j + 128] = tmp; gu[127 - j] = -tmp;
+ tmp = (0xFF00 & (183 * j)) >> 8;
+ gv[j + 128] = tmp; gv[127 - j] = -tmp;
+ }
+ for (j = 0; j < 16; j++) {
+ bu[j] = bu[16]; rv[j] = rv[16];
+ gu[j] = gu[16]; gv[j] = gv[16];
+ }
+ for (j = 240; j < 256; j++) {
+ bu[j] = bu[239]; rv[j] = rv[239];
+ gu[j] = gu[239]; gv[j] = gv[239];
+ }
+ for (j = 16; j < 236; j++)
+ ay[j] = j;
+ for (j = 0; j < 16; j++)
+ ay[j] = ay[16];
+ for (j = 236; j < 256; j++)
+ ay[j] = ay[235];
+ JOM(8, "lookup tables are prepared\n");
+ }
+ pcache = peasycap->pcache;
+ if (!pcache)
+ pcache = &peasycap->cache[0];
+/*---------------------------------------------------------------------------*/
+/*
+ * TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER
+ */
+/*---------------------------------------------------------------------------*/
+ if (!pcache) {
+ SAM("MISTAKE: pcache is NULL\n");
+ return -EFAULT;
+ }
+
+ if (pcache != &peasycap->cache[0])
+ JOM(16, "cache has %i bytes\n", (int)(pcache - &peasycap->cache[0]));
+ p2 = &peasycap->cache[0];
+ p3 = (u8 *)pad - (int)(pcache - &peasycap->cache[0]);
+ while (p2 < pcache) {
+ *p3++ = *p2; p2++;
+ }
+ pcache = &peasycap->cache[0];
+ if (p3 != pad) {
+ SAM("MISTAKE: pointer misalignment\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+ rump = (int)(0x03 & mask);
+ u = 0; v = 0;
+ p2 = (u8 *)pex; pz = p2 + much; pr = p3 + more; last = false;
+ p2++;
+
+ if (isuy)
+ u = *(p2 - 1);
+ else
+ v = *(p2 - 1);
+
+ if (rump)
+ JOM(16, "%4i=much %4i=more %i=rump\n", much, more, rump);
+
+/*---------------------------------------------------------------------------*/
+ switch (bytesperpixel) {
+ case 2: {
+ if (!decimatepixel) {
+ memcpy(pad, pex, (size_t)much);
+ if (!byteswaporder) {
+ /* UYVY */
+ return 0;
+ } else {
+ /* YUYV */
+ p3 = (u8 *)pad; pz = p3 + much;
+ while (pz > p3) {
+ c = *p3;
+ *p3 = *(p3 + 1);
+ *(p3 + 1) = c;
+ p3 += 2;
+ }
+ return 0;
+ }
+ } else {
+ if (!byteswaporder) {
+ /* UYVY DECIMATED */
+ p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
+ while (pz > p2) {
+ *p3 = *p2;
+ *(p3 + 1) = *(p2 + 1);
+ *(p3 + 2) = *(p2 + 2);
+ *(p3 + 3) = *(p2 + 3);
+ p3 += 4; p2 += 8;
+ }
+ return 0;
+ } else {
+ /* YUYV DECIMATED */
+ p2 = (u8 *)pex; p3 = (u8 *)pad; pz = p2 + much;
+ while (pz > p2) {
+ *p3 = *(p2 + 1);
+ *(p3 + 1) = *p2;
+ *(p3 + 2) = *(p2 + 3);
+ *(p3 + 3) = *(p2 + 2);
+ p3 += 4; p2 += 8;
+ }
+ return 0;
+ }
+ }
+ break;
+ }
+ case 3:
+ {
+ if (!decimatepixel) {
+ if (!byteswaporder) {
+ /* RGB */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ return 0;
+ } else {
+ /* BGR */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ }
+ else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ }
+ return 0;
+ } else {
+ if (!byteswaporder) {
+ /* RGB DECIMATED */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ } else {
+ isuy = true;
+ }
+ p2 += 2;
+ }
+ return 0;
+ } else {
+ /* BGR DECIMATED */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ }
+ else
+ isuy = true;
+ p2 += 2;
+ }
+ return 0;
+ }
+ }
+ break;
+ }
+ case 4:
+ {
+ if (!decimatepixel) {
+ if (!byteswaporder) {
+ /* RGBA */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *pcache++ = 0;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *(p3 + 3) = 0;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ return 0;
+ } else {
+ /*
+ * BGRA
+ */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] - gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *pcache++ = 0;
+ break;
+ }
+ default:
+ SAM("MISTAKE: %i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *(p3 + 3) = 0;
+ }
+ p2 += 2;
+ if (isuy)
+ isuy = false;
+ else
+ isuy = true;
+ p3 += bytesperpixel;
+ }
+ }
+ return 0;
+ } else {
+ if (!byteswaporder) {
+ /*
+ * RGBA DECIMATED
+ */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = r;
+ *pcache++ = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *pcache++ = b;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *pcache++ = 0;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel -
+ rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = r;
+ *(p3 + 1) = g;
+ *(p3 + 2) = b;
+ *(p3 + 3) = 0;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ } else
+ isuy = true;
+ p2 += 2;
+ }
+ return 0;
+ } else {
+ /*
+ * BGRA DECIMATED
+ */
+ while (pz > p2) {
+ if (pr <= (p3 + bytesperpixel))
+ last = true;
+ else
+ last = false;
+ y = *p2;
+ if (last && (0x0C & mask)) {
+ if (0x04 & mask) {
+ if (isuy)
+ v = margin;
+ else
+ u = margin;
+ } else
+ if (0x08 & mask)
+ ;
+ } else {
+ if (isuy)
+ v = *(p2 + 1);
+ else
+ u = *(p2 + 1);
+ }
+
+ if (isuy) {
+ tmp = ay[(int)y] + rv[(int)v];
+ r = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] - gu[(int)u] -
+ gv[(int)v];
+ g = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+ tmp = ay[(int)y] + bu[(int)u];
+ b = (255 < tmp) ? 255 : ((0 > tmp) ?
+ 0 : (u8)tmp);
+
+ if (last && rump) {
+ pcache = &peasycap->cache[0];
+ switch (bytesperpixel - rump) {
+ case 1: {
+ *p3 = b;
+ *pcache++ = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 2: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *pcache++ = r;
+ *pcache++ = 0;
+ break;
+ }
+ case 3: {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *pcache++ = 0;
+ break;
+ }
+ default: {
+ SAM("MISTAKE: "
+ "%i=rump\n",
+ bytesperpixel - rump);
+ return -EFAULT;
+ }
+ }
+ } else {
+ *p3 = b;
+ *(p3 + 1) = g;
+ *(p3 + 2) = r;
+ *(p3 + 3) = 0;
+ }
+ isuy = false;
+ p3 += bytesperpixel;
+ } else
+ isuy = true;
+ p2 += 2;
+ }
+ return 0;
+ }
+ }
+ break;
+ }
+ default: {
+ SAM("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
+ return -EFAULT;
+ }
+ }
+ return 0;
+}
+/*****************************************************************************/
+/*
+ * SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434
+ */
+/*****************************************************************************/
+static void easycap_vma_open(struct vm_area_struct *pvma)
+{
+ struct easycap *peasycap;
+
+ peasycap = pvma->vm_private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ peasycap->vma_many++;
+ JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
+ return;
+}
+/*****************************************************************************/
+static void easycap_vma_close(struct vm_area_struct *pvma)
+{
+ struct easycap *peasycap;
+
+ peasycap = pvma->vm_private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ peasycap->vma_many--;
+ JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
+ return;
+}
+/*****************************************************************************/
+static int easycap_vma_fault(struct vm_area_struct *pvma, struct vm_fault *pvmf)
+{
+ int k, m, retcode;
+ void *pbuf;
+ struct page *page;
+ struct easycap *peasycap;
+
+ retcode = VM_FAULT_NOPAGE;
+
+ if (!pvma) {
+ SAY("pvma is NULL\n");
+ return retcode;
+ }
+ if (!pvmf) {
+ SAY("pvmf is NULL\n");
+ return retcode;
+ }
+
+ k = (pvmf->pgoff) / (FRAME_BUFFER_SIZE/PAGE_SIZE);
+ m = (pvmf->pgoff) % (FRAME_BUFFER_SIZE/PAGE_SIZE);
+
+ if (!m)
+ JOT(4, "%4i=k, %4i=m\n", k, m);
+ else
+ JOT(16, "%4i=k, %4i=m\n", k, m);
+
+ if ((0 > k) || (FRAME_BUFFER_MANY <= k)) {
+ SAY("ERROR: buffer index %i out of range\n", k);
+ return retcode;
+ }
+ if ((0 > m) || (FRAME_BUFFER_SIZE/PAGE_SIZE <= m)) {
+ SAY("ERROR: page number %i out of range\n", m);
+ return retcode;
+ }
+ peasycap = pvma->vm_private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return retcode;
+ }
+/*---------------------------------------------------------------------------*/
+ pbuf = peasycap->frame_buffer[k][m].pgo;
+ if (!pbuf) {
+ SAM("ERROR: pbuf is NULL\n");
+ return retcode;
+ }
+ page = virt_to_page(pbuf);
+ if (!page) {
+ SAM("ERROR: page is NULL\n");
+ return retcode;
+ }
+ get_page(page);
+/*---------------------------------------------------------------------------*/
+ if (!page) {
+ SAM("ERROR: page is NULL after get_page(page)\n");
+ } else {
+ pvmf->page = page;
+ retcode = VM_FAULT_MINOR;
+ }
+ return retcode;
+}
+
+static const struct vm_operations_struct easycap_vm_ops = {
+ .open = easycap_vma_open,
+ .close = easycap_vma_close,
+ .fault = easycap_vma_fault,
+};
+
+static int easycap_mmap(struct file *file, struct vm_area_struct *pvma)
+{
+ JOT(8, "\n");
+
+ pvma->vm_ops = &easycap_vm_ops;
+ pvma->vm_flags |= VM_RESERVED;
+ if (file)
+ pvma->vm_private_data = file->private_data;
+ easycap_vma_open(pvma);
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS
+ * PROVIDED peasycap->video_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
+ * IT IS RESUBMITTED PROVIDED peasycap->video_isoc_streaming IS NOT ZERO.
+ *
+ * THIS FUNCTION IS AN INTERRUPT SERVICE ROUTINE AND MUST NOT SLEEP.
+ *
+ * INFORMATION ABOUT THE VALIDITY OF THE CONTENTS OF THE FIELD BUFFER ARE
+ * STORED IN THE TWO-BYTE STATUS PARAMETER
+ * peasycap->field_buffer[peasycap->field_fill][0].kount
+ * NOTICE THAT THE INFORMATION IS STORED ONLY WITH PAGE 0 OF THE FIELD BUFFER.
+ *
+ * THE LOWER BYTE CONTAINS THE FIELD PARITY BYTE FURNISHED BY THE SAA7113H
+ * CHIP.
+ *
+ * THE UPPER BYTE IS ZERO IF NO PROBLEMS, OTHERWISE:
+ * 0 != (kount & 0x8000) => AT LEAST ONE URB COMPLETED WITH ERRORS
+ * 0 != (kount & 0x4000) => BUFFER HAS TOO MUCH DATA
+ * 0 != (kount & 0x2000) => BUFFER HAS NOT ENOUGH DATA
+ * 0 != (kount & 0x1000) => BUFFER HAS DATA FROM DISPARATE INPUTS
+ * 0 != (kount & 0x0400) => RESERVED
+ * 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED
+ * 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY?
+ */
+/*---------------------------------------------------------------------------*/
+static void easycap_complete(struct urb *purb)
+{
+ struct easycap *peasycap;
+ struct data_buffer *pfield_buffer;
+ char errbuf[16];
+ int i, more, much, leap, rc, last;
+ int videofieldamount;
+ unsigned int override, bad;
+ int framestatus, framelength, frameactual, frameoffset;
+ u8 *pu;
+
+ if (!purb) {
+ SAY("ERROR: easycap_complete(): purb is NULL\n");
+ return;
+ }
+ peasycap = purb->context;
+ if (!peasycap) {
+ SAY("ERROR: easycap_complete(): peasycap is NULL\n");
+ return;
+ }
+ if (peasycap->video_eof)
+ return;
+ for (i = 0; i < VIDEO_ISOC_BUFFER_MANY; i++)
+ if (purb->transfer_buffer == peasycap->video_isoc_buffer[i].pgo)
+ break;
+ JOM(16, "%2i=urb\n", i);
+ last = peasycap->video_isoc_sequence;
+ if ((((VIDEO_ISOC_BUFFER_MANY - 1) == last) && (0 != i)) ||
+ (((VIDEO_ISOC_BUFFER_MANY - 1) != last) && ((last + 1) != i))) {
+ JOM(16, "ERROR: out-of-order urbs %i,%i ... continuing\n",
+ last, i);
+ }
+ peasycap->video_isoc_sequence = i;
+
+ if (peasycap->video_idle) {
+ JOM(16, "%i=video_idle %i=video_isoc_streaming\n",
+ peasycap->video_idle, peasycap->video_isoc_streaming);
+ if (peasycap->video_isoc_streaming) {
+ rc = usb_submit_urb(purb, GFP_ATOMIC);
+ if (rc) {
+ SAM("%s:%d ENOMEM\n", strerror(rc), rc);
+ if (-ENODEV != rc)
+ SAM("ERROR: while %i=video_idle, "
+ "usb_submit_urb() "
+ "failed with rc:\n",
+ peasycap->video_idle);
+ }
+ }
+ return;
+ }
+ override = 0;
+/*---------------------------------------------------------------------------*/
+ if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
+ SAM("ERROR: bad peasycap->field_fill\n");
+ return;
+ }
+ if (purb->status) {
+ if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
+ JOM(8, "urb status -ESHUTDOWN or -ENOENT\n");
+ return;
+ }
+
+ (peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ;
+ SAM("ERROR: bad urb status -%s: %d\n",
+ strerror(purb->status), purb->status);
+/*---------------------------------------------------------------------------*/
+ } else {
+ for (i = 0; i < purb->number_of_packets; i++) {
+ if (0 != purb->iso_frame_desc[i].status) {
+ (peasycap->field_buffer
+ [peasycap->field_fill][0].kount) |= 0x8000 ;
+ /* FIXME: 1. missing '-' check boundaries */
+ strcpy(&errbuf[0],
+ strerror(purb->iso_frame_desc[i].status));
+ }
+ framestatus = purb->iso_frame_desc[i].status;
+ framelength = purb->iso_frame_desc[i].length;
+ frameactual = purb->iso_frame_desc[i].actual_length;
+ frameoffset = purb->iso_frame_desc[i].offset;
+
+ JOM(16, "frame[%2i]:"
+ "%4i=status "
+ "%4i=actual "
+ "%4i=length "
+ "%5i=offset\n",
+ i, framestatus, frameactual, framelength, frameoffset);
+ if (!purb->iso_frame_desc[i].status) {
+ more = purb->iso_frame_desc[i].actual_length;
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill][peasycap->field_page];
+ videofieldamount = (peasycap->field_page *
+ PAGE_SIZE) +
+ (int)(pfield_buffer->pto - pfield_buffer->pgo);
+ if (4 == more)
+ peasycap->video_mt++;
+ if (4 < more) {
+ if (peasycap->video_mt) {
+ JOM(8, "%4i empty video urb frames\n",
+ peasycap->video_mt);
+ peasycap->video_mt = 0;
+ }
+ if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
+ SAM("ERROR: bad peasycap->field_fill\n");
+ return;
+ }
+ if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
+ peasycap->field_page) {
+ SAM("ERROR: bad peasycap->field_page\n");
+ return;
+ }
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill][peasycap->field_page];
+ pu = (u8 *)(purb->transfer_buffer +
+ purb->iso_frame_desc[i].offset);
+ if (0x80 & *pu)
+ leap = 8;
+ else
+ leap = 4;
+/*--------------------------------------------------------------------------*/
+/*
+ * EIGHT-BYTE END-OF-VIDEOFIELD MARKER.
+ * NOTE: A SUCCESSION OF URB FRAMES FOLLOWING THIS ARE EMPTY,
+ * CORRESPONDING TO THE FIELD FLYBACK (VERTICAL BLANKING) PERIOD.
+ *
+ * PROVIDED THE FIELD BUFFER CONTAINS GOOD DATA AS INDICATED BY A ZERO UPPER
+ * BYTE OF
+ * peasycap->field_buffer[peasycap->field_fill][0].kount
+ * THE CONTENTS OF THE FIELD BUFFER ARE OFFERED TO dqbuf(), field_read IS
+ * UPDATED AND field_fill IS BUMPED. IF THE FIELD BUFFER CONTAINS BAD DATA
+ * NOTHING IS OFFERED TO dqbuf().
+ *
+ * THE DECISION ON WHETHER THE PARITY OF THE OFFERED FIELD BUFFER IS RIGHT
+ * RESTS WITH dqbuf().
+ */
+/*---------------------------------------------------------------------------*/
+ if ((8 == more) || override) {
+ if (videofieldamount >
+ peasycap->videofieldamount) {
+ if (2 == videofieldamount -
+ peasycap->
+ videofieldamount) {
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount) |= 0x0100;
+ peasycap->video_junk += (1 +
+ VIDEO_JUNK_TOLERATE);
+ } else
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount) |= 0x4000;
+ } else if (videofieldamount <
+ peasycap->
+ videofieldamount) {
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount) |= 0x2000;
+ }
+ bad = 0xFF00 & peasycap->field_buffer
+ [peasycap->field_fill]
+ [0].kount;
+ if (!bad) {
+ (peasycap->video_junk)--;
+ if (-VIDEO_JUNK_TOLERATE >
+ peasycap->video_junk)
+ peasycap->video_junk =
+ -VIDEO_JUNK_TOLERATE;
+ peasycap->field_read =
+ (peasycap->
+ field_fill)++;
+ if (FIELD_BUFFER_MANY <=
+ peasycap->
+ field_fill)
+ peasycap->
+ field_fill = 0;
+ peasycap->field_page = 0;
+ pfield_buffer = &peasycap->
+ field_buffer
+ [peasycap->
+ field_fill]
+ [peasycap->
+ field_page];
+ pfield_buffer->pto =
+ pfield_buffer->pgo;
+ JOM(8, "bumped to: %i="
+ "peasycap->"
+ "field_fill %i="
+ "parity\n",
+ peasycap->field_fill,
+ 0x00FF &
+ pfield_buffer->kount);
+ JOM(8, "field buffer %i has "
+ "%i bytes fit to be "
+ "read\n",
+ peasycap->field_read,
+ videofieldamount);
+ JOM(8, "wakeup call to "
+ "wq_video, "
+ "%i=field_read "
+ "%i=field_fill "
+ "%i=parity\n",
+ peasycap->field_read,
+ peasycap->field_fill,
+ 0x00FF & peasycap->
+ field_buffer
+ [peasycap->
+ field_read][0].kount);
+ wake_up_interruptible
+ (&(peasycap->
+ wq_video));
+ } else {
+ peasycap->video_junk++;
+ if (bad & 0x0010)
+ peasycap->video_junk +=
+ (1 + VIDEO_JUNK_TOLERATE/2);
+ JOM(8, "field buffer %i had %i "
+ "bytes, now discarded: "
+ "0x%04X\n",
+ peasycap->field_fill,
+ videofieldamount,
+ (0xFF00 &
+ peasycap->field_buffer
+ [peasycap->field_fill][0].
+ kount));
+ (peasycap->field_fill)++;
+
+ if (FIELD_BUFFER_MANY <=
+ peasycap->field_fill)
+ peasycap->field_fill = 0;
+ peasycap->field_page = 0;
+ pfield_buffer =
+ &peasycap->field_buffer
+ [peasycap->field_fill]
+ [peasycap->field_page];
+ pfield_buffer->pto =
+ pfield_buffer->pgo;
+
+ JOM(8, "bumped to: %i=peasycap->"
+ "field_fill %i=parity\n",
+ peasycap->field_fill,
+ 0x00FF & pfield_buffer->kount);
+ }
+ if (8 == more) {
+ JOM(8, "end-of-field: received "
+ "parity byte 0x%02X\n",
+ (0xFF & *pu));
+ if (0x40 & *pu)
+ pfield_buffer->kount = 0x0000;
+ else
+ pfield_buffer->kount = 0x0001;
+ pfield_buffer->input = 0x08 |
+ (0x07 & peasycap->input);
+ JOM(8, "end-of-field: 0x%02X=kount\n",
+ 0xFF & pfield_buffer->kount);
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * COPY more BYTES FROM ISOC BUFFER TO FIELD BUFFER
+ */
+/*---------------------------------------------------------------------------*/
+ pu += leap;
+ more -= leap;
+
+ if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
+ SAM("ERROR: bad peasycap->field_fill\n");
+ return;
+ }
+ if (FIELD_BUFFER_SIZE/PAGE_SIZE <= peasycap->field_page) {
+ SAM("ERROR: bad peasycap->field_page\n");
+ return;
+ }
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill][peasycap->field_page];
+ while (more) {
+ pfield_buffer = &peasycap->field_buffer
+ [peasycap->field_fill]
+ [peasycap->field_page];
+ if (PAGE_SIZE < (pfield_buffer->pto -
+ pfield_buffer->pgo)) {
+ SAM("ERROR: bad pfield_buffer->pto\n");
+ return;
+ }
+ if (PAGE_SIZE == (pfield_buffer->pto -
+ pfield_buffer->pgo)) {
+ (peasycap->field_page)++;
+ if (FIELD_BUFFER_SIZE/PAGE_SIZE <=
+ peasycap->field_page) {
+ JOM(16, "wrapping peasycap->"
+ "field_page\n");
+ peasycap->field_page = 0;
+ }
+ pfield_buffer = &peasycap->
+ field_buffer
+ [peasycap->field_fill]
+ [peasycap->field_page];
+ pfield_buffer->pto = pfield_buffer->pgo;
+ pfield_buffer->input = 0x08 |
+ (0x07 & peasycap->input);
+ if ((peasycap->field_buffer[peasycap->
+ field_fill][0]).
+ input !=
+ pfield_buffer->input)
+ (peasycap->field_buffer
+ [peasycap->field_fill]
+ [0]).kount |= 0x1000;
+ }
+
+ much = PAGE_SIZE -
+ (int)(pfield_buffer->pto -
+ pfield_buffer->pgo);
+
+ if (much > more)
+ much = more;
+ memcpy(pfield_buffer->pto, pu, much);
+ pu += much;
+ (pfield_buffer->pto) += much;
+ more -= much;
+ }
+ }
+ }
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * RESUBMIT THIS URB, UNLESS A SEVERE PERSISTENT ERROR CONDITION EXISTS.
+ *
+ * IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO AN ERROR CONDITION
+ * THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+ if (VIDEO_ISOC_BUFFER_MANY <= peasycap->video_junk) {
+ SAM("easycap driver shutting down on condition green\n");
+ peasycap->status = 1;
+ peasycap->video_eof = 1;
+ peasycap->video_junk = 0;
+ wake_up_interruptible(&peasycap->wq_video);
+#if !defined(PERSEVERE)
+ peasycap->audio_eof = 1;
+ wake_up_interruptible(&peasycap->wq_audio);
+#endif /*PERSEVERE*/
+ return;
+ }
+ if (peasycap->video_isoc_streaming) {
+ rc = usb_submit_urb(purb, GFP_ATOMIC);
+ if (rc) {
+ SAM("%s: %d\n", strerror(rc), rc);
+ if (-ENODEV != rc)
+ SAM("ERROR: while %i=video_idle, "
+ "usb_submit_urb() "
+ "failed with rc:\n",
+ peasycap->video_idle);
+ }
+ }
+ return;
+}
+static const struct file_operations easycap_fops = {
+ .owner = THIS_MODULE,
+ .open = easycap_open,
+ .unlocked_ioctl = easycap_unlocked_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
+ .llseek = no_llseek,
+};
+static const struct usb_class_driver easycap_class = {
+ .name = "usb/easycap%d",
+ .fops = &easycap_fops,
+ .minor_base = USB_SKEL_MINOR_BASE,
+};
+/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
+static const struct v4l2_file_operations v4l2_fops = {
+ .owner = THIS_MODULE,
+ .open = easycap_open_noinode,
+ .unlocked_ioctl = easycap_unlocked_ioctl,
+ .poll = easycap_poll,
+ .mmap = easycap_mmap,
+};
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * WHEN THE EasyCAP IS PHYSICALLY PLUGGED IN, THIS FUNCTION IS CALLED THREE
+ * TIMES, ONCE FOR EACH OF THE THREE INTERFACES. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+static int easycap_usb_probe(struct usb_interface *intf,
+ const struct usb_device_id *id)
+{
+ struct usb_device *usbdev;
+ struct usb_host_interface *alt;
+ struct usb_endpoint_descriptor *ep;
+ struct usb_interface_descriptor *interface;
+ struct urb *purb;
+ struct easycap *peasycap;
+ int ndong;
+ struct data_urb *pdata_urb;
+ int i, j, k, m, rc;
+ u8 bInterfaceNumber;
+ u8 bInterfaceClass;
+ u8 bInterfaceSubClass;
+ void *pbuf;
+ int okalt[8], isokalt;
+ int okepn[8];
+ int okmps[8];
+ int maxpacketsize;
+ u16 mask;
+ s32 value;
+ struct easycap_format *peasycap_format;
+ int fmtidx;
+ struct inputset *inputset;
+
+ usbdev = interface_to_usbdev(intf);
+
+/*---------------------------------------------------------------------------*/
+ alt = usb_altnum_to_altsetting(intf, 0);
+ if (!alt) {
+ SAY("ERROR: usb_host_interface not found\n");
+ return -EFAULT;
+ }
+ interface = &alt->desc;
+ if (!interface) {
+ SAY("ERROR: intf_descriptor is NULL\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * GET PROPERTIES OF PROBED INTERFACE
+ */
+/*---------------------------------------------------------------------------*/
+ bInterfaceNumber = interface->bInterfaceNumber;
+ bInterfaceClass = interface->bInterfaceClass;
+ bInterfaceSubClass = interface->bInterfaceSubClass;
+
+ JOT(4, "intf[%i]: num_altsetting=%i\n",
+ bInterfaceNumber, intf->num_altsetting);
+ JOT(4, "intf[%i]: cur_altsetting - altsetting=%li\n",
+ bInterfaceNumber,
+ (long int)(intf->cur_altsetting - intf->altsetting));
+ JOT(4, "intf[%i]: bInterfaceClass=0x%02X bInterfaceSubClass=0x%02X\n",
+ bInterfaceNumber, bInterfaceClass, bInterfaceSubClass);
+/*---------------------------------------------------------------------------*/
+/*
+ * A NEW struct easycap IS ALWAYS ALLOCATED WHEN INTERFACE 0 IS PROBED.
+ * IT IS NOT POSSIBLE HERE TO FREE ANY EXISTING struct easycap. THIS
+ * SHOULD HAVE BEEN DONE BY easycap_delete() WHEN THE EasyCAP WAS
+ * PHYSICALLY UNPLUGGED.
+ *
+ * THE POINTER peasycap TO THE struct easycap IS REMEMBERED WHEN
+ * INTERFACES 1 AND 2 ARE PROBED.
+*/
+/*---------------------------------------------------------------------------*/
+ if (0 == bInterfaceNumber) {
+ peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL);
+ if (!peasycap) {
+ SAY("ERROR: Could not allocate peasycap\n");
+ return -ENOMEM;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PERFORM URGENT INTIALIZATIONS ...
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->minor = -1;
+ kref_init(&peasycap->kref);
+ JOM(8, "intf[%i]: after kref_init(..._video) "
+ "%i=peasycap->kref.refcount.counter\n",
+ bInterfaceNumber, peasycap->kref.refcount.counter);
+
+ /* module params */
+ peasycap->gain = (s8)clamp(easycap_gain, 0, 31);
+
+ init_waitqueue_head(&peasycap->wq_video);
+ init_waitqueue_head(&peasycap->wq_audio);
+ init_waitqueue_head(&peasycap->wq_trigger);
+
+ if (mutex_lock_interruptible(&mutex_dongle)) {
+ SAY("ERROR: cannot down mutex_dongle\n");
+ return -ERESTARTSYS;
+ } else {
+/*---------------------------------------------------------------------------*/
+ /*
+ * FOR INTERFACES 1 AND 2 THE POINTER peasycap WILL NEED TO
+ * TO BE THE SAME AS THAT ALLOCATED NOW FOR INTERFACE 0.
+ *
+ * NORMALLY ndong WILL NOT HAVE CHANGED SINCE INTERFACE 0 WAS
+ * PROBED, BUT THIS MAY NOT BE THE CASE IF, FOR EXAMPLE, TWO
+ * EASYCAPs ARE PLUGGED IN SIMULTANEOUSLY.
+ */
+/*---------------------------------------------------------------------------*/
+ for (ndong = 0; ndong < DONGLE_MANY; ndong++) {
+ if ((!easycapdc60_dongle[ndong].peasycap) &&
+ (!mutex_is_locked(&easycapdc60_dongle
+ [ndong].mutex_video)) &&
+ (!mutex_is_locked(&easycapdc60_dongle
+ [ndong].mutex_audio))) {
+ easycapdc60_dongle[ndong].peasycap = peasycap;
+ peasycap->isdongle = ndong;
+ JOM(8, "intf[%i]: peasycap-->easycap"
+ "_dongle[%i].peasycap\n",
+ bInterfaceNumber, ndong);
+ break;
+ }
+ }
+ if (DONGLE_MANY <= ndong) {
+ SAM("ERROR: too many dongles\n");
+ mutex_unlock(&mutex_dongle);
+ return -ENOMEM;
+ }
+ mutex_unlock(&mutex_dongle);
+ }
+ peasycap->allocation_video_struct = sizeof(struct easycap);
+ peasycap->allocation_video_page = 0;
+ peasycap->allocation_video_urb = 0;
+ peasycap->allocation_audio_struct = 0;
+ peasycap->allocation_audio_page = 0;
+ peasycap->allocation_audio_urb = 0;
+
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND FURTHER INITIALIZE THE STRUCTURE
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->pusb_device = usbdev;
+ peasycap->pusb_interface = intf;
+
+ peasycap->ilk = 0;
+ peasycap->microphone = false;
+
+ peasycap->video_interface = -1;
+ peasycap->video_altsetting_on = -1;
+ peasycap->video_altsetting_off = -1;
+ peasycap->video_endpointnumber = -1;
+ peasycap->video_isoc_maxframesize = -1;
+ peasycap->video_isoc_buffer_size = -1;
+
+ peasycap->audio_interface = -1;
+ peasycap->audio_altsetting_on = -1;
+ peasycap->audio_altsetting_off = -1;
+ peasycap->audio_endpointnumber = -1;
+ peasycap->audio_isoc_maxframesize = -1;
+ peasycap->audio_isoc_buffer_size = -1;
+
+ peasycap->frame_buffer_many = FRAME_BUFFER_MANY;
+
+ for (k = 0; k < INPUT_MANY; k++)
+ peasycap->lost[k] = 0;
+ peasycap->skip = 0;
+ peasycap->skipped = 0;
+ peasycap->offerfields = 0;
+/*---------------------------------------------------------------------------*/
+/*
+ * DYNAMICALLY FILL IN THE AVAILABLE FORMATS ...
+ */
+/*---------------------------------------------------------------------------*/
+ rc = fillin_formats();
+ if (0 > rc) {
+ SAM("ERROR: fillin_formats() rc = %i\n", rc);
+ return -EFAULT;
+ }
+ JOM(4, "%i formats available\n", rc);
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND POPULATE easycap.inputset[]
+*/
+/*---------------------------------------------------------------------------*/
+ /* FIXME: maybe we just use memset 0 */
+ inputset = peasycap->inputset;
+ for (k = 0; k < INPUT_MANY; k++) {
+ inputset[k].input_ok = 0;
+ inputset[k].standard_offset_ok = 0;
+ inputset[k].format_offset_ok = 0;
+ inputset[k].brightness_ok = 0;
+ inputset[k].contrast_ok = 0;
+ inputset[k].saturation_ok = 0;
+ inputset[k].hue_ok = 0;
+ }
+
+ fmtidx = peasycap->ntsc ? NTSC_M : PAL_BGHIN;
+ m = 0;
+ mask = 0;
+ for (i = 0; 0xFFFF != easycap_standard[i].mask; i++) {
+ if (fmtidx == easycap_standard[i].v4l2_standard.index) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].standard_offset = i;
+
+ mask = easycap_standard[i].mask;
+ }
+ }
+
+ if (1 != m) {
+ SAM("ERROR: "
+ "inputset->standard_offset unpopulated, %i=m\n", m);
+ return -ENOENT;
+ }
+
+ peasycap_format = &easycap_format[0];
+ m = 0;
+ for (i = 0; peasycap_format->v4l2_format.fmt.pix.width; i++) {
+ struct v4l2_pix_format *pix =
+ &peasycap_format->v4l2_format.fmt.pix;
+ if (((peasycap_format->mask & 0x0F) == (mask & 0x0F)) &&
+ pix->field == V4L2_FIELD_NONE &&
+ pix->pixelformat == V4L2_PIX_FMT_UYVY &&
+ pix->width == 640 && pix->height == 480) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].format_offset = i;
+ break;
+ }
+ peasycap_format++;
+ }
+ if (1 != m) {
+ SAM("ERROR: inputset[]->format_offset unpopulated\n");
+ return -ENOENT;
+ }
+
+ m = 0;
+ for (i = 0; 0xFFFFFFFF != easycap_control[i].id; i++) {
+ value = easycap_control[i].default_value;
+ if (V4L2_CID_BRIGHTNESS == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].brightness = value;
+ } else if (V4L2_CID_CONTRAST == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].contrast = value;
+ } else if (V4L2_CID_SATURATION == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].saturation = value;
+ } else if (V4L2_CID_HUE == easycap_control[i].id) {
+ m++;
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].hue = value;
+ }
+ }
+
+ if (4 != m) {
+ SAM("ERROR: inputset[]->brightness underpopulated\n");
+ return -ENOENT;
+ }
+ for (k = 0; k < INPUT_MANY; k++)
+ inputset[k].input = k;
+ JOM(4, "populated inputset[]\n");
+ JOM(4, "finished initialization\n");
+ } else {
+/*---------------------------------------------------------------------------*/
+/*
+ * FIXME
+ *
+ * IDENTIFY THE APPROPRIATE POINTER peasycap FOR INTERFACES 1 AND 2.
+ * THE ADDRESS OF peasycap->pusb_device IS RELUCTANTLY USED FOR THIS PURPOSE.
+ */
+/*---------------------------------------------------------------------------*/
+ for (ndong = 0; ndong < DONGLE_MANY; ndong++) {
+ if (usbdev == easycapdc60_dongle[ndong].peasycap->
+ pusb_device) {
+ peasycap = easycapdc60_dongle[ndong].peasycap;
+ JOT(8, "intf[%i]: dongle[%i].peasycap\n",
+ bInterfaceNumber, ndong);
+ break;
+ }
+ }
+ if (DONGLE_MANY <= ndong) {
+ SAY("ERROR: peasycap is unknown when probing interface %i\n",
+ bInterfaceNumber);
+ return -ENODEV;
+ }
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL when probing interface %i\n",
+ bInterfaceNumber);
+ return -ENODEV;
+ }
+ }
+/*---------------------------------------------------------------------------*/
+ if ((USB_CLASS_VIDEO == bInterfaceClass) ||
+ (USB_CLASS_VENDOR_SPEC == bInterfaceClass)) {
+ if (-1 == peasycap->video_interface) {
+ peasycap->video_interface = bInterfaceNumber;
+ JOM(4, "setting peasycap->video_interface=%i\n",
+ peasycap->video_interface);
+ } else {
+ if (peasycap->video_interface != bInterfaceNumber) {
+ SAM("ERROR: attempting to reset "
+ "peasycap->video_interface\n");
+ SAM("...... continuing with "
+ "%i=peasycap->video_interface\n",
+ peasycap->video_interface);
+ }
+ }
+ } else if ((USB_CLASS_AUDIO == bInterfaceClass) &&
+ (USB_SUBCLASS_AUDIOSTREAMING == bInterfaceSubClass)) {
+ if (-1 == peasycap->audio_interface) {
+ peasycap->audio_interface = bInterfaceNumber;
+ JOM(4, "setting peasycap->audio_interface=%i\n",
+ peasycap->audio_interface);
+ } else {
+ if (peasycap->audio_interface != bInterfaceNumber) {
+ SAM("ERROR: attempting to reset "
+ "peasycap->audio_interface\n");
+ SAM("...... continuing with "
+ "%i=peasycap->audio_interface\n",
+ peasycap->audio_interface);
+ }
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * INVESTIGATE ALL ALTSETTINGS.
+ * DONE IN DETAIL BECAUSE USB DEVICE 05e1:0408 HAS DISPARATE INCARNATIONS.
+ */
+/*---------------------------------------------------------------------------*/
+ isokalt = 0;
+
+ for (i = 0; i < intf->num_altsetting; i++) {
+ alt = usb_altnum_to_altsetting(intf, i);
+ if (!alt) {
+ SAM("ERROR: alt is NULL\n");
+ return -EFAULT;
+ }
+ interface = &alt->desc;
+ if (!interface) {
+ SAM("ERROR: intf_descriptor is NULL\n");
+ return -EFAULT;
+ }
+
+ if (0 == interface->bNumEndpoints)
+ JOM(4, "intf[%i]alt[%i] has no endpoints\n",
+ bInterfaceNumber, i);
+/*---------------------------------------------------------------------------*/
+ for (j = 0; j < interface->bNumEndpoints; j++) {
+ ep = &alt->endpoint[j].desc;
+ if (!ep) {
+ SAM("ERROR: ep is NULL.\n");
+ SAM("...... skipping\n");
+ continue;
+ }
+
+ if (!usb_endpoint_is_isoc_in(ep)) {
+ JOM(4, "intf[%i]alt[%i]end[%i] is a %d endpoint\n",
+ bInterfaceNumber,
+ i, j, ep->bmAttributes);
+ if (usb_endpoint_dir_out(ep)) {
+ SAM("ERROR: OUT endpoint unexpected\n");
+ SAM("...... continuing\n");
+ }
+ continue;
+ }
+ switch (bInterfaceClass) {
+ case USB_CLASS_VIDEO:
+ case USB_CLASS_VENDOR_SPEC: {
+ if (ep->wMaxPacketSize) {
+ if (8 > isokalt) {
+ okalt[isokalt] = i;
+ JOM(4,
+ "%i=okalt[%i]\n",
+ okalt[isokalt],
+ isokalt);
+ okepn[isokalt] =
+ ep->
+ bEndpointAddress &
+ 0x0F;
+ JOM(4,
+ "%i=okepn[%i]\n",
+ okepn[isokalt],
+ isokalt);
+ okmps[isokalt] =
+ le16_to_cpu(ep->
+ wMaxPacketSize);
+ JOM(4,
+ "%i=okmps[%i]\n",
+ okmps[isokalt],
+ isokalt);
+ isokalt++;
+ }
+ } else {
+ if (-1 == peasycap->
+ video_altsetting_off) {
+ peasycap->
+ video_altsetting_off =
+ i;
+ JOM(4, "%i=video_"
+ "altsetting_off "
+ "<====\n",
+ peasycap->
+ video_altsetting_off);
+ } else {
+ SAM("ERROR: peasycap"
+ "->video_altsetting_"
+ "off already set\n");
+ SAM("...... "
+ "continuing with "
+ "%i=peasycap->video_"
+ "altsetting_off\n",
+ peasycap->
+ video_altsetting_off);
+ }
+ }
+ break;
+ }
+ case USB_CLASS_AUDIO: {
+ if (bInterfaceSubClass !=
+ USB_SUBCLASS_AUDIOSTREAMING)
+ break;
+ if (!peasycap) {
+ SAM("MISTAKE: "
+ "peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (ep->wMaxPacketSize) {
+ if (8 > isokalt) {
+ okalt[isokalt] = i ;
+ JOM(4,
+ "%i=okalt[%i]\n",
+ okalt[isokalt],
+ isokalt);
+ okepn[isokalt] =
+ ep->
+ bEndpointAddress &
+ 0x0F;
+ JOM(4,
+ "%i=okepn[%i]\n",
+ okepn[isokalt],
+ isokalt);
+ okmps[isokalt] =
+ le16_to_cpu(ep->
+ wMaxPacketSize);
+ JOM(4,
+ "%i=okmps[%i]\n",
+ okmps[isokalt],
+ isokalt);
+ isokalt++;
+ }
+ } else {
+ if (-1 == peasycap->
+ audio_altsetting_off) {
+ peasycap->
+ audio_altsetting_off =
+ i;
+ JOM(4, "%i=audio_"
+ "altsetting_off "
+ "<====\n",
+ peasycap->
+ audio_altsetting_off);
+ } else {
+ SAM("ERROR: peasycap"
+ "->audio_altsetting_"
+ "off already set\n");
+ SAM("...... "
+ "continuing with "
+ "%i=peasycap->"
+ "audio_altsetting_"
+ "off\n",
+ peasycap->
+ audio_altsetting_off);
+ }
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ if (0 == ep->wMaxPacketSize) {
+ JOM(4, "intf[%i]alt[%i]end[%i] "
+ "has zero packet size\n",
+ bInterfaceNumber, i, j);
+ }
+ }
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PERFORM INITIALIZATION OF THE PROBED INTERFACE
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "initialization begins for interface %i\n",
+ interface->bInterfaceNumber);
+ switch (bInterfaceNumber) {
+/*---------------------------------------------------------------------------*/
+/*
+ * INTERFACE 0 IS THE VIDEO INTERFACE
+ */
+/*---------------------------------------------------------------------------*/
+ case 0: {
+ if (!peasycap) {
+ SAM("MISTAKE: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!isokalt) {
+ SAM("ERROR: no viable video_altsetting_on\n");
+ return -ENOENT;
+ } else {
+ peasycap->video_altsetting_on = okalt[isokalt - 1];
+ JOM(4, "%i=video_altsetting_on <====\n",
+ peasycap->video_altsetting_on);
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * DECIDE THE VIDEO STREAMING PARAMETERS
+ */
+/*---------------------------------------------------------------------------*/
+ peasycap->video_endpointnumber = okepn[isokalt - 1];
+ JOM(4, "%i=video_endpointnumber\n", peasycap->video_endpointnumber);
+ maxpacketsize = okmps[isokalt - 1];
+
+ peasycap->video_isoc_maxframesize =
+ min(maxpacketsize, USB_2_0_MAXPACKETSIZE);
+ if (0 >= peasycap->video_isoc_maxframesize) {
+ SAM("ERROR: bad video_isoc_maxframesize\n");
+ SAM(" possibly because port is USB 1.1\n");
+ return -ENOENT;
+ }
+ JOM(4, "%i=video_isoc_maxframesize\n",
+ peasycap->video_isoc_maxframesize);
+
+ peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC;
+ JOM(4, "%i=video_isoc_framesperdesc\n",
+ peasycap->video_isoc_framesperdesc);
+ if (0 >= peasycap->video_isoc_framesperdesc) {
+ SAM("ERROR: bad video_isoc_framesperdesc\n");
+ return -ENOENT;
+ }
+ peasycap->video_isoc_buffer_size =
+ peasycap->video_isoc_maxframesize *
+ peasycap->video_isoc_framesperdesc;
+ JOM(4, "%i=video_isoc_buffer_size\n",
+ peasycap->video_isoc_buffer_size);
+ if ((PAGE_SIZE << VIDEO_ISOC_ORDER) <
+ peasycap->video_isoc_buffer_size) {
+ SAM("MISTAKE: peasycap->video_isoc_buffer_size too big\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+ if (-1 == peasycap->video_interface) {
+ SAM("MISTAKE: video_interface is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_altsetting_on) {
+ SAM("MISTAKE: video_altsetting_on is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_altsetting_off) {
+ SAM("MISTAKE: video_interface_off is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_endpointnumber) {
+ SAM("MISTAKE: video_endpointnumber is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_isoc_maxframesize) {
+ SAM("MISTAKE: video_isoc_maxframesize is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->video_isoc_buffer_size) {
+ SAM("MISTAKE: video_isoc_buffer_size is unset\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE MEMORY FOR VIDEO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
+ */
+/*---------------------------------------------------------------------------*/
+ INIT_LIST_HEAD(&(peasycap->urb_video_head));
+ peasycap->purb_video_head = &(peasycap->urb_video_head);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i frame buffers of size %li\n",
+ FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE);
+ JOM(4, ".... each scattered over %li pages\n",
+ FRAME_BUFFER_SIZE/PAGE_SIZE);
+
+ for (k = 0; k < FRAME_BUFFER_MANY; k++) {
+ for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->frame_buffer[k][m].pgo)
+ SAM("attempting to reallocate frame "
+ " buffers\n");
+ else {
+ pbuf = (void *)__get_free_page(GFP_KERNEL);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate frame "
+ "buffer %i page %i\n", k, m);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_page += 1;
+ peasycap->frame_buffer[k][m].pgo = pbuf;
+ }
+ peasycap->frame_buffer[k][m].pto =
+ peasycap->frame_buffer[k][m].pgo;
+ }
+ }
+
+ peasycap->frame_fill = 0;
+ peasycap->frame_read = 0;
+ JOM(4, "allocation of frame buffers done: %i pages\n", k *
+ m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i field buffers of size %li\n",
+ FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE);
+ JOM(4, ".... each scattered over %li pages\n",
+ FIELD_BUFFER_SIZE/PAGE_SIZE);
+
+ for (k = 0; k < FIELD_BUFFER_MANY; k++) {
+ for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
+ if (peasycap->field_buffer[k][m].pgo) {
+ SAM("ERROR: attempting to reallocate "
+ "field buffers\n");
+ } else {
+ pbuf = (void *) __get_free_page(GFP_KERNEL);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate field"
+ " buffer %i page %i\n", k, m);
+ return -ENOMEM;
+ }
+ else
+ peasycap->allocation_video_page += 1;
+ peasycap->field_buffer[k][m].pgo = pbuf;
+ }
+ peasycap->field_buffer[k][m].pto =
+ peasycap->field_buffer[k][m].pgo;
+ }
+ peasycap->field_buffer[k][0].kount = 0x0200;
+ }
+ peasycap->field_fill = 0;
+ peasycap->field_page = 0;
+ peasycap->field_read = 0;
+ JOM(4, "allocation of field buffers done: %i pages\n", k *
+ m);
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i isoc video buffers of size %i\n",
+ VIDEO_ISOC_BUFFER_MANY,
+ peasycap->video_isoc_buffer_size);
+ JOM(4, ".... each occupying contiguous memory pages\n");
+
+ for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
+ pbuf = (void *)__get_free_pages(GFP_KERNEL,
+ VIDEO_ISOC_ORDER);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate isoc video buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_page +=
+ BIT(VIDEO_ISOC_ORDER);
+
+ peasycap->video_isoc_buffer[k].pgo = pbuf;
+ peasycap->video_isoc_buffer[k].pto =
+ pbuf + peasycap->video_isoc_buffer_size;
+ peasycap->video_isoc_buffer[k].kount = k;
+ }
+ JOM(4, "allocation of isoc video buffers done: %i pages\n",
+ k * (0x01 << VIDEO_ISOC_ORDER));
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY);
+ JOM(4, "using %i=peasycap->video_isoc_framesperdesc\n",
+ peasycap->video_isoc_framesperdesc);
+ JOM(4, "using %i=peasycap->video_isoc_maxframesize\n",
+ peasycap->video_isoc_maxframesize);
+ JOM(4, "using %i=peasycap->video_isoc_buffer_sizen",
+ peasycap->video_isoc_buffer_size);
+
+ for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
+ purb = usb_alloc_urb(peasycap->video_isoc_framesperdesc,
+ GFP_KERNEL);
+ if (!purb) {
+ SAM("ERROR: usb_alloc_urb returned NULL for buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_urb += 1;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
+ if (!pdata_urb) {
+ SAM("ERROR: Could not allocate struct data_urb.\n");
+ return -ENOMEM;
+ } else
+ peasycap->allocation_video_struct +=
+ sizeof(struct data_urb);
+
+ pdata_urb->purb = purb;
+ pdata_urb->isbuf = k;
+ pdata_urb->length = 0;
+ list_add_tail(&(pdata_urb->list_head),
+ peasycap->purb_video_head);
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND INITIALIZE THEM
+ */
+/*---------------------------------------------------------------------------*/
+ if (!k) {
+ JOM(4, "initializing video urbs thus:\n");
+ JOM(4, " purb->interval = 1;\n");
+ JOM(4, " purb->dev = peasycap->pusb_device;\n");
+ JOM(4, " purb->pipe = usb_rcvisocpipe"
+ "(peasycap->pusb_device,%i);\n",
+ peasycap->video_endpointnumber);
+ JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
+ JOM(4, " purb->transfer_buffer = peasycap->"
+ "video_isoc_buffer[.].pgo;\n");
+ JOM(4, " purb->transfer_buffer_length = %i;\n",
+ peasycap->video_isoc_buffer_size);
+ JOM(4, " purb->complete = easycap_complete;\n");
+ JOM(4, " purb->context = peasycap;\n");
+ JOM(4, " purb->start_frame = 0;\n");
+ JOM(4, " purb->number_of_packets = %i;\n",
+ peasycap->video_isoc_framesperdesc);
+ JOM(4, " for (j = 0; j < %i; j++)\n",
+ peasycap->video_isoc_framesperdesc);
+ JOM(4, " {\n");
+ JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
+ peasycap->video_isoc_maxframesize);
+ JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
+ peasycap->video_isoc_maxframesize);
+ JOM(4, " }\n");
+ }
+
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->video_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer = peasycap->video_isoc_buffer[k].pgo;
+ purb->transfer_buffer_length =
+ peasycap->video_isoc_buffer_size;
+ purb->complete = easycap_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets = peasycap->video_isoc_framesperdesc;
+ for (j = 0; j < peasycap->video_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j].offset = j *
+ peasycap->video_isoc_maxframesize;
+ purb->iso_frame_desc[j].length =
+ peasycap->video_isoc_maxframesize;
+ }
+ }
+ JOM(4, "allocation of %i struct urb done.\n", k);
+/*--------------------------------------------------------------------------*/
+/*
+ * SAVE POINTER peasycap IN THIS INTERFACE.
+ */
+/*--------------------------------------------------------------------------*/
+ usb_set_intfdata(intf, peasycap);
+/*---------------------------------------------------------------------------*/
+/*
+ * IT IS ESSENTIAL TO INITIALIZE THE HARDWARE BEFORE, RATHER THAN AFTER,
+ * THE DEVICE IS REGISTERED, BECAUSE SOME VERSIONS OF THE videodev MODULE
+ * CALL easycap_open() IMMEDIATELY AFTER REGISTRATION, CAUSING A CLASH.
+ * BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->ntsc = easycap_ntsc;
+ JOM(8, "defaulting initially to %s\n",
+ easycap_ntsc ? "NTSC" : "PAL");
+ rc = reset(peasycap);
+ if (rc) {
+ SAM("ERROR: reset() rc = %i\n", rc);
+ return -EFAULT;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
+ */
+/*--------------------------------------------------------------------------*/
+ if (v4l2_device_register(&intf->dev, &peasycap->v4l2_device)) {
+ SAM("v4l2_device_register() failed\n");
+ return -ENODEV;
+ }
+ JOM(4, "registered device instance: %s\n",
+ peasycap->v4l2_device.name);
+/*---------------------------------------------------------------------------*/
+/*
+ * FIXME
+ *
+ *
+ * THIS IS BELIEVED TO BE HARMLESS, BUT MAY WELL BE UNNECESSARY OR WRONG:
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->video_device.v4l2_dev = NULL;
+/*---------------------------------------------------------------------------*/
+
+
+ strcpy(&peasycap->video_device.name[0], "easycapdc60");
+ peasycap->video_device.fops = &v4l2_fops;
+ peasycap->video_device.minor = -1;
+ peasycap->video_device.release = (void *)(&videodev_release);
+
+ video_set_drvdata(&(peasycap->video_device), (void *)peasycap);
+
+ if (0 != (video_register_device(&(peasycap->video_device),
+ VFL_TYPE_GRABBER, -1))) {
+ err("Not able to register with videodev");
+ videodev_release(&(peasycap->video_device));
+ return -ENODEV;
+ } else {
+ (peasycap->registered_video)++;
+ SAM("registered with videodev: %i=minor\n",
+ peasycap->video_device.minor);
+ peasycap->minor = peasycap->video_device.minor;
+ }
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * INTERFACE 1 IS THE AUDIO CONTROL INTERFACE
+ * INTERFACE 2 IS THE AUDIO STREAMING INTERFACE
+ */
+/*--------------------------------------------------------------------------*/
+ case 1: {
+ if (!peasycap) {
+ SAM("MISTAKE: peasycap is NULL\n");
+ return -EFAULT;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * SAVE POINTER peasycap IN INTERFACE 1
+ */
+/*--------------------------------------------------------------------------*/
+ usb_set_intfdata(intf, peasycap);
+ JOM(4, "no initialization required for interface %i\n",
+ interface->bInterfaceNumber);
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+ case 2: {
+ if (!peasycap) {
+ SAM("MISTAKE: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!isokalt) {
+ SAM("ERROR: no viable audio_altsetting_on\n");
+ return -ENOENT;
+ } else {
+ peasycap->audio_altsetting_on = okalt[isokalt - 1];
+ JOM(4, "%i=audio_altsetting_on <====\n",
+ peasycap->audio_altsetting_on);
+ }
+
+ peasycap->audio_endpointnumber = okepn[isokalt - 1];
+ JOM(4, "%i=audio_endpointnumber\n", peasycap->audio_endpointnumber);
+
+ peasycap->audio_isoc_maxframesize = okmps[isokalt - 1];
+ JOM(4, "%i=audio_isoc_maxframesize\n",
+ peasycap->audio_isoc_maxframesize);
+ if (0 >= peasycap->audio_isoc_maxframesize) {
+ SAM("ERROR: bad audio_isoc_maxframesize\n");
+ return -ENOENT;
+ }
+ if (9 == peasycap->audio_isoc_maxframesize) {
+ peasycap->ilk |= 0x02;
+ SAM("audio hardware is microphone\n");
+ peasycap->microphone = true;
+ peasycap->audio_pages_per_fragment =
+ PAGES_PER_AUDIO_FRAGMENT;
+ } else if (256 == peasycap->audio_isoc_maxframesize) {
+ peasycap->ilk &= ~0x02;
+ SAM("audio hardware is AC'97\n");
+ peasycap->microphone = false;
+ peasycap->audio_pages_per_fragment =
+ PAGES_PER_AUDIO_FRAGMENT;
+ } else {
+ SAM("hardware is unidentified:\n");
+ SAM("%i=audio_isoc_maxframesize\n",
+ peasycap->audio_isoc_maxframesize);
+ return -ENOENT;
+ }
+
+ peasycap->audio_bytes_per_fragment =
+ peasycap->audio_pages_per_fragment * PAGE_SIZE;
+ peasycap->audio_buffer_page_many = (AUDIO_FRAGMENT_MANY *
+ peasycap->audio_pages_per_fragment);
+
+ JOM(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY);
+ JOM(4, "%6i=audio_pages_per_fragment\n",
+ peasycap->audio_pages_per_fragment);
+ JOM(4, "%6i=audio_bytes_per_fragment\n",
+ peasycap->audio_bytes_per_fragment);
+ JOM(4, "%6i=audio_buffer_page_many\n",
+ peasycap->audio_buffer_page_many);
+
+ peasycap->audio_isoc_framesperdesc = AUDIO_ISOC_FRAMESPERDESC;
+
+ JOM(4, "%i=audio_isoc_framesperdesc\n",
+ peasycap->audio_isoc_framesperdesc);
+ if (0 >= peasycap->audio_isoc_framesperdesc) {
+ SAM("ERROR: bad audio_isoc_framesperdesc\n");
+ return -ENOENT;
+ }
+
+ peasycap->audio_isoc_buffer_size =
+ peasycap->audio_isoc_maxframesize *
+ peasycap->audio_isoc_framesperdesc;
+ JOM(4, "%i=audio_isoc_buffer_size\n",
+ peasycap->audio_isoc_buffer_size);
+ if (AUDIO_ISOC_BUFFER_SIZE < peasycap->audio_isoc_buffer_size) {
+ SAM("MISTAKE: audio_isoc_buffer_size bigger "
+ "than %li=AUDIO_ISOC_BUFFER_SIZE\n",
+ AUDIO_ISOC_BUFFER_SIZE);
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_interface) {
+ SAM("MISTAKE: audio_interface is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_altsetting_on) {
+ SAM("MISTAKE: audio_altsetting_on is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_altsetting_off) {
+ SAM("MISTAKE: audio_interface_off is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_endpointnumber) {
+ SAM("MISTAKE: audio_endpointnumber is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_isoc_maxframesize) {
+ SAM("MISTAKE: audio_isoc_maxframesize is unset\n");
+ return -EFAULT;
+ }
+ if (-1 == peasycap->audio_isoc_buffer_size) {
+ SAM("MISTAKE: audio_isoc_buffer_size is unset\n");
+ return -EFAULT;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE MEMORY FOR AUDIO BUFFERS. LISTS MUST BE INITIALIZED FIRST.
+ */
+/*---------------------------------------------------------------------------*/
+ INIT_LIST_HEAD(&(peasycap->urb_audio_head));
+ peasycap->purb_audio_head = &(peasycap->urb_audio_head);
+
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i isoc audio buffers of size %i\n",
+ AUDIO_ISOC_BUFFER_MANY,
+ peasycap->audio_isoc_buffer_size);
+ JOM(4, ".... each occupying contiguous memory pages\n");
+
+ for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
+ pbuf = (void *)__get_free_pages(GFP_KERNEL,
+ AUDIO_ISOC_ORDER);
+ if (!pbuf) {
+ SAM("ERROR: Could not allocate isoc audio buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ } else
+ peasycap->allocation_audio_page +=
+ BIT(AUDIO_ISOC_ORDER);
+
+ peasycap->audio_isoc_buffer[k].pgo = pbuf;
+ peasycap->audio_isoc_buffer[k].pto = pbuf +
+ peasycap->audio_isoc_buffer_size;
+ peasycap->audio_isoc_buffer[k].kount = k;
+ }
+ JOM(4, "allocation of isoc audio buffers done.\n");
+/*---------------------------------------------------------------------------*/
+/*
+ * ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY);
+ JOM(4, "using %i=peasycap->audio_isoc_framesperdesc\n",
+ peasycap->audio_isoc_framesperdesc);
+ JOM(4, "using %i=peasycap->audio_isoc_maxframesize\n",
+ peasycap->audio_isoc_maxframesize);
+ JOM(4, "using %i=peasycap->audio_isoc_buffer_size\n",
+ peasycap->audio_isoc_buffer_size);
+
+ for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
+ purb = usb_alloc_urb(peasycap->audio_isoc_framesperdesc,
+ GFP_KERNEL);
+ if (!purb) {
+ SAM("ERROR: usb_alloc_urb returned NULL for buffer "
+ "%i\n", k);
+ return -ENOMEM;
+ }
+ peasycap->allocation_audio_urb += 1 ;
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
+ if (!pdata_urb) {
+ SAM("ERROR: Could not allocate struct data_urb.\n");
+ return -ENOMEM;
+ }
+ peasycap->allocation_audio_struct +=
+ sizeof(struct data_urb);
+
+ pdata_urb->purb = purb;
+ pdata_urb->isbuf = k;
+ pdata_urb->length = 0;
+ list_add_tail(&(pdata_urb->list_head),
+ peasycap->purb_audio_head);
+/*---------------------------------------------------------------------------*/
+/*
+ * ... AND INITIALIZE THEM
+ */
+/*---------------------------------------------------------------------------*/
+ if (!k) {
+ JOM(4, "initializing audio urbs thus:\n");
+ JOM(4, " purb->interval = 1;\n");
+ JOM(4, " purb->dev = peasycap->pusb_device;\n");
+ JOM(4, " purb->pipe = usb_rcvisocpipe(peasycap->"
+ "pusb_device,%i);\n",
+ peasycap->audio_endpointnumber);
+ JOM(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
+ JOM(4, " purb->transfer_buffer = "
+ "peasycap->audio_isoc_buffer[.].pgo;\n");
+ JOM(4, " purb->transfer_buffer_length = %i;\n",
+ peasycap->audio_isoc_buffer_size);
+ JOM(4, " purb->complete = easycap_alsa_complete;\n");
+ JOM(4, " purb->context = peasycap;\n");
+ JOM(4, " purb->start_frame = 0;\n");
+ JOM(4, " purb->number_of_packets = %i;\n",
+ peasycap->audio_isoc_framesperdesc);
+ JOM(4, " for (j = 0; j < %i; j++)\n",
+ peasycap->audio_isoc_framesperdesc);
+ JOM(4, " {\n");
+ JOM(4, " purb->iso_frame_desc[j].offset = j*%i;\n",
+ peasycap->audio_isoc_maxframesize);
+ JOM(4, " purb->iso_frame_desc[j].length = %i;\n",
+ peasycap->audio_isoc_maxframesize);
+ JOM(4, " }\n");
+ }
+
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->audio_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer = peasycap->audio_isoc_buffer[k].pgo;
+ purb->transfer_buffer_length =
+ peasycap->audio_isoc_buffer_size;
+ purb->complete = easycap_alsa_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
+ for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j].offset = j *
+ peasycap->audio_isoc_maxframesize;
+ purb->iso_frame_desc[j].length =
+ peasycap->audio_isoc_maxframesize;
+ }
+ }
+ JOM(4, "allocation of %i struct urb done.\n", k);
+/*---------------------------------------------------------------------------*/
+/*
+ * SAVE POINTER peasycap IN THIS INTERFACE.
+ */
+/*---------------------------------------------------------------------------*/
+ usb_set_intfdata(intf, peasycap);
+/*---------------------------------------------------------------------------*/
+/*
+ * THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
+ */
+/*---------------------------------------------------------------------------*/
+ JOM(4, "initializing ALSA card\n");
+
+ rc = easycap_alsa_probe(peasycap);
+ if (rc) {
+ err("easycap_alsa_probe() rc = %i\n", rc);
+ return -ENODEV;
+ }
+
+
+ JOM(8, "kref_get() with %i=kref.refcount.counter\n",
+ peasycap->kref.refcount.counter);
+ kref_get(&peasycap->kref);
+ peasycap->registered_audio++;
+ break;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED
+ */
+/*---------------------------------------------------------------------------*/
+ default:
+ JOM(4, "ERROR: unexpected interface %i\n", bInterfaceNumber);
+ return -EINVAL;
+ }
+ SAM("ends successfully for interface %i\n", bInterfaceNumber);
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * WHEN THIS FUNCTION IS CALLED THE EasyCAP HAS ALREADY BEEN PHYSICALLY
+ * UNPLUGGED. HENCE peasycap->pusb_device IS NO LONGER VALID.
+ *
+ * THIS FUNCTION AFFECTS ALSA. BEWARE.
+ */
+/*---------------------------------------------------------------------------*/
+static void easycap_usb_disconnect(struct usb_interface *pusb_interface)
+{
+ struct usb_host_interface *pusb_host_interface;
+ struct usb_interface_descriptor *pusb_interface_descriptor;
+ u8 bInterfaceNumber;
+ struct easycap *peasycap;
+
+ struct list_head *plist_head;
+ struct data_urb *pdata_urb;
+ int minor, m, kd;
+
+ JOT(4, "\n");
+
+ pusb_host_interface = pusb_interface->cur_altsetting;
+ if (!pusb_host_interface) {
+ JOT(4, "ERROR: pusb_host_interface is NULL\n");
+ return;
+ }
+ pusb_interface_descriptor = &(pusb_host_interface->desc);
+ if (!pusb_interface_descriptor) {
+ JOT(4, "ERROR: pusb_interface_descriptor is NULL\n");
+ return;
+ }
+ bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber;
+ minor = pusb_interface->minor;
+ JOT(4, "intf[%i]: minor=%i\n", bInterfaceNumber, minor);
+
+ if (1 == bInterfaceNumber)
+ return;
+
+ peasycap = usb_get_intfdata(pusb_interface);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * IF THE WAIT QUEUES ARE NOT CLEARED A DEADLOCK IS POSSIBLE. BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+ peasycap->video_eof = 1;
+ peasycap->audio_eof = 1;
+ wake_up_interruptible(&(peasycap->wq_video));
+ wake_up_interruptible(&(peasycap->wq_audio));
+/*---------------------------------------------------------------------------*/
+ switch (bInterfaceNumber) {
+ case 0: {
+ if (peasycap->purb_video_head) {
+ JOM(4, "killing video urbs\n");
+ m = 0;
+ list_for_each(plist_head, peasycap->purb_video_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ if (pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ }
+ JOM(4, "%i video urbs killed\n", m);
+ }
+ break;
+ }
+/*---------------------------------------------------------------------------*/
+ case 2: {
+ if (peasycap->purb_audio_head) {
+ JOM(4, "killing audio urbs\n");
+ m = 0;
+ list_for_each(plist_head, peasycap->purb_audio_head) {
+ pdata_urb = list_entry(plist_head,
+ struct data_urb, list_head);
+ if (pdata_urb) {
+ if (pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ }
+ JOM(4, "%i audio urbs killed\n", m);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+/*--------------------------------------------------------------------------*/
+/*
+ * DEREGISTER
+ *
+ * THIS PROCEDURE WILL BLOCK UNTIL easycap_poll(), VIDEO IOCTL AND AUDIO
+ * IOCTL ARE ALL UNLOCKED. IF THIS IS NOT DONE AN Oops CAN OCCUR WHEN
+ * AN EasyCAP IS UNPLUGGED WHILE THE URBS ARE RUNNING. BEWARE.
+ */
+/*--------------------------------------------------------------------------*/
+ kd = isdongle(peasycap);
+ switch (bInterfaceNumber) {
+ case 0: {
+ if (0 <= kd && DONGLE_MANY > kd) {
+ wake_up_interruptible(&peasycap->wq_video);
+ JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
+ mutex_video)) {
+ SAY("ERROR: "
+ "cannot lock dongle[%i].mutex_video\n", kd);
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_video\n", kd);
+ } else {
+ SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->v4l2_device.name[0]) {
+ SAM("ERROR: peasycap->v4l2_device.name is empty\n");
+ if (0 <= kd && DONGLE_MANY > kd)
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ return;
+ }
+ v4l2_device_disconnect(&peasycap->v4l2_device);
+ JOM(4, "v4l2_device_disconnect() OK\n");
+ v4l2_device_unregister(&peasycap->v4l2_device);
+ JOM(4, "v4l2_device_unregister() OK\n");
+
+ video_unregister_device(&peasycap->video_device);
+ JOM(4, "intf[%i]: video_unregister_device() minor=%i\n",
+ bInterfaceNumber, minor);
+ peasycap->registered_video--;
+/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
+
+ if (0 <= kd && DONGLE_MANY > kd) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
+ }
+ break;
+ }
+ case 2: {
+ if (0 <= kd && DONGLE_MANY > kd) {
+ wake_up_interruptible(&peasycap->wq_audio);
+ JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].
+ mutex_audio)) {
+ SAY("ERROR: "
+ "cannot lock dongle[%i].mutex_audio\n", kd);
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_audio\n", kd);
+ } else
+ SAY("ERROR: %i=kd is bad: cannot lock dongle\n", kd);
+ if (0 != snd_card_free(peasycap->psnd_card)) {
+ SAY("ERROR: snd_card_free() failed\n");
+ } else {
+ peasycap->psnd_card = NULL;
+ (peasycap->registered_audio)--;
+ }
+ if (0 <= kd && DONGLE_MANY > kd) {
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_audio);
+ JOM(4, "unlocked dongle[%i].mutex_audio\n", kd);
+ }
+ break;
+ }
+ default:
+ break;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap
+ * (ALSO WHEN ALSA HAS BEEN IN USE)
+ */
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->kref.refcount.counter) {
+ SAM("ERROR: peasycap->kref.refcount.counter is zero "
+ "so cannot call kref_put()\n");
+ SAM("ending unsuccessfully: may cause memory leak\n");
+ return;
+ }
+ if (0 <= kd && DONGLE_MANY > kd) {
+ JOM(4, "about to lock dongle[%i].mutex_video\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_video)) {
+ SAY("ERROR: cannot lock dongle[%i].mutex_video\n", kd);
+ SAM("ending unsuccessfully: may cause memory leak\n");
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_video\n", kd);
+ JOM(4, "about to lock dongle[%i].mutex_audio\n", kd);
+ if (mutex_lock_interruptible(&easycapdc60_dongle[kd].mutex_audio)) {
+ SAY("ERROR: cannot lock dongle[%i].mutex_audio\n", kd);
+ mutex_unlock(&(easycapdc60_dongle[kd].mutex_video));
+ JOM(4, "unlocked dongle[%i].mutex_video\n", kd);
+ SAM("ending unsuccessfully: may cause memory leak\n");
+ return;
+ }
+ JOM(4, "locked dongle[%i].mutex_audio\n", kd);
+ }
+ JOM(4, "intf[%i]: %i=peasycap->kref.refcount.counter\n",
+ bInterfaceNumber, (int)peasycap->kref.refcount.counter);
+ kref_put(&peasycap->kref, easycap_delete);
+ JOT(4, "intf[%i]: kref_put() done.\n", bInterfaceNumber);
+ if (0 <= kd && DONGLE_MANY > kd) {
+ mutex_unlock(&(easycapdc60_dongle[kd].mutex_audio));
+ JOT(4, "unlocked dongle[%i].mutex_audio\n", kd);
+ mutex_unlock(&easycapdc60_dongle[kd].mutex_video);
+ JOT(4, "unlocked dongle[%i].mutex_video\n", kd);
+ }
+/*---------------------------------------------------------------------------*/
+ JOM(4, "ends\n");
+ return;
+}
+/*****************************************************************************/
+
+/*---------------------------------------------------------------------------*/
+/*
+ * PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO
+ */
+/*---------------------------------------------------------------------------*/
+static struct usb_device_id easycap_usb_device_id_table[] = {
+ {USB_DEVICE(USB_EASYCAP_VENDOR_ID, USB_EASYCAP_PRODUCT_ID)},
+ { }
+};
+
+MODULE_DEVICE_TABLE(usb, easycap_usb_device_id_table);
+struct usb_driver easycap_usb_driver = {
+ .name = "easycap",
+ .id_table = easycap_usb_device_id_table,
+ .probe = easycap_usb_probe,
+ .disconnect = easycap_usb_disconnect,
+};
+
+static int __init easycap_module_init(void)
+{
+ int k, rc;
+
+ printk(KERN_INFO "Easycap version: "EASYCAP_DRIVER_VERSION "\n");
+
+ JOT(4, "begins. %i=debug %i=bars %i=gain\n",
+ easycap_debug, easycap_bars, easycap_gain);
+
+ mutex_init(&mutex_dongle);
+ for (k = 0; k < DONGLE_MANY; k++) {
+ easycapdc60_dongle[k].peasycap = NULL;
+ mutex_init(&easycapdc60_dongle[k].mutex_video);
+ mutex_init(&easycapdc60_dongle[k].mutex_audio);
+ }
+ rc = usb_register(&easycap_usb_driver);
+ if (rc)
+ printk(KERN_ERR "Easycap: usb_register failed rc=%d\n", rc);
+
+ return rc;
+}
+/*****************************************************************************/
+static void __exit easycap_module_exit(void)
+{
+ usb_deregister(&easycap_usb_driver);
+}
+/*****************************************************************************/
+
+module_init(easycap_module_init);
+module_exit(easycap_module_exit);
+
+/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_settings.c b/drivers/staging/media/easycap/easycap_settings.c
new file mode 100644
index 000000000000..70f59b13c34d
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap_settings.c
@@ -0,0 +1,696 @@
+/******************************************************************************
+* *
+* easycap_settings.c *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+
+/*---------------------------------------------------------------------------*/
+/*
+ * THE LEAST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
+ * 0 => 25 fps
+ * 1 => 30 fps
+ *
+ * THE MOST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
+ * 0 => full framerate
+ * 1 => 20% framerate
+ */
+/*---------------------------------------------------------------------------*/
+const struct easycap_standard easycap_standard[] = {
+ {
+ .mask = 0x00FF & PAL_BGHIN ,
+ .v4l2_standard = {
+ .index = PAL_BGHIN,
+ .id = (V4L2_STD_PAL_B |
+ V4L2_STD_PAL_G | V4L2_STD_PAL_H |
+ V4L2_STD_PAL_I | V4L2_STD_PAL_N),
+ .name = "PAL_BGHIN",
+ .frameperiod = {1, 25},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_N_443 ,
+ .v4l2_standard = {
+ .index = NTSC_N_443,
+ .id = V4L2_STD_UNKNOWN,
+ .name = "NTSC_N_443",
+ .frameperiod = {1, 25},
+ .framelines = 480,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & PAL_Nc ,
+ .v4l2_standard = {
+ .index = PAL_Nc,
+ .id = V4L2_STD_PAL_Nc,
+ .name = "PAL_Nc",
+ .frameperiod = {1, 25},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_N ,
+ .v4l2_standard = {
+ .index = NTSC_N,
+ .id = V4L2_STD_UNKNOWN,
+ .name = "NTSC_N",
+ .frameperiod = {1, 25},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & SECAM ,
+ .v4l2_standard = {
+ .index = SECAM,
+ .id = V4L2_STD_SECAM,
+ .name = "SECAM",
+ .frameperiod = {1, 25},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_M ,
+ .v4l2_standard = {
+ .index = NTSC_M,
+ .id = V4L2_STD_NTSC_M,
+ .name = "NTSC_M",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_M_JP ,
+ .v4l2_standard = {
+ .index = NTSC_M_JP,
+ .id = V4L2_STD_NTSC_M_JP,
+ .name = "NTSC_M_JP",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & PAL_60 ,
+ .v4l2_standard = {
+ .index = PAL_60,
+ .id = V4L2_STD_PAL_60,
+ .name = "PAL_60",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & NTSC_443 ,
+ .v4l2_standard = {
+ .index = NTSC_443,
+ .id = V4L2_STD_NTSC_443,
+ .name = "NTSC_443",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x00FF & PAL_M ,
+ .v4l2_standard = {
+ .index = PAL_M,
+ .id = V4L2_STD_PAL_M,
+ .name = "PAL_M",
+ .frameperiod = {1, 30},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_BGHIN_SLOW),
+ .v4l2_standard = {
+ .index = PAL_BGHIN_SLOW,
+ .id = (V4L2_STD_PAL_B | V4L2_STD_PAL_G |
+ V4L2_STD_PAL_H |
+ V4L2_STD_PAL_I | V4L2_STD_PAL_N |
+ (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_BGHIN_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_N_443_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_N_443_SLOW,
+ .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x11) << 32)),
+ .name = "NTSC_N_443_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 480,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_Nc_SLOW),
+ .v4l2_standard = {
+ .index = PAL_Nc_SLOW,
+ .id = (V4L2_STD_PAL_Nc | (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_Nc_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_N_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_N_SLOW,
+ .id = (V4L2_STD_UNKNOWN | (((v4l2_std_id)0x21) << 32)),
+ .name = "NTSC_N_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & SECAM_SLOW),
+ .v4l2_standard = {
+ .index = SECAM_SLOW,
+ .id = (V4L2_STD_SECAM | (((v4l2_std_id)0x01) << 32)),
+ .name = "SECAM_SLOW",
+ .frameperiod = {1, 5},
+ .framelines = 625,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_M_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_M_SLOW,
+ .id = (V4L2_STD_NTSC_M | (((v4l2_std_id)0x01) << 32)),
+ .name = "NTSC_M_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_M_JP_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_M_JP_SLOW,
+ .id = (V4L2_STD_NTSC_M_JP |
+ (((v4l2_std_id)0x01) << 32)),
+ .name = "NTSC_M_JP_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_60_SLOW),
+ .v4l2_standard = {
+ .index = PAL_60_SLOW,
+ .id = (V4L2_STD_PAL_60 | (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_60_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & NTSC_443_SLOW),
+ .v4l2_standard = {
+ .index = NTSC_443_SLOW,
+ .id = (V4L2_STD_NTSC_443 | (((v4l2_std_id)0x01) << 32)),
+ .name = "NTSC_443_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0x8000 | (0x00FF & PAL_M_SLOW),
+ .v4l2_standard = {
+ .index = PAL_M_SLOW,
+ .id = (V4L2_STD_PAL_M | (((v4l2_std_id)0x01) << 32)),
+ .name = "PAL_M_SLOW",
+ .frameperiod = {1, 6},
+ .framelines = 525,
+ .reserved = {0, 0, 0, 0}
+ }
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .mask = 0xFFFF
+ }
+};
+/*---------------------------------------------------------------------------*/
+/*
+ * THE 16-BIT easycap_format.mask HAS MEANING:
+ * (least significant) BIT 0: 0 => PAL, 25 FPS; 1 => NTSC, 30 FPS
+ * BITS 2-4: RESERVED FOR DIFFERENTIATING STANDARDS
+ * BITS 5-7: NUMBER OF BYTES PER PIXEL
+ * BIT 8: 0 => NATIVE BYTE ORDER; 1 => SWAPPED
+ * BITS 9-10: RESERVED FOR OTHER BYTE PERMUTATIONS
+ * BIT 11: 0 => UNDECIMATED; 1 => DECIMATED
+ * BIT 12: 0 => OFFER FRAMES; 1 => OFFER FIELDS
+ * BIT 13: 0 => FULL FRAMERATE; 1 => REDUCED
+ * (most significant) BITS 14-15: RESERVED FOR OTHER FIELD/FRAME OPTIONS
+ * IT FOLLOWS THAT:
+ * bytesperpixel IS ((0x00E0 & easycap_format.mask) >> 5)
+ * byteswaporder IS true IF (0 != (0x0100 & easycap_format.mask))
+ *
+ * decimatepixel IS true IF (0 != (0x0800 & easycap_format.mask))
+ *
+ * offerfields IS true IF (0 != (0x1000 & easycap_format.mask))
+ */
+/*---------------------------------------------------------------------------*/
+
+struct easycap_format easycap_format[1 + SETTINGS_MANY];
+
+int fillin_formats(void)
+{
+ const char *name1, *name2, *name3, *name4;
+ struct v4l2_format *fmt;
+ int i, j, k, m, n;
+ u32 width, height, pixelformat, bytesperline, sizeimage;
+ u16 mask1, mask2, mask3, mask4;
+ enum v4l2_field field;
+ enum v4l2_colorspace colorspace;
+
+ for (i = 0, n = 0; i < STANDARD_MANY; i++) {
+ mask1 = 0x0000;
+ switch (i) {
+ case PAL_BGHIN: {
+ mask1 = 0x1F & PAL_BGHIN;
+ name1 = "PAL_BGHIN";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case SECAM: {
+ mask1 = 0x1F & SECAM;
+ name1 = "SECAM";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_Nc: {
+ mask1 = 0x1F & PAL_Nc;
+ name1 = "PAL_Nc";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_60: {
+ mask1 = 0x1F & PAL_60;
+ name1 = "PAL_60";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_M: {
+ mask1 = 0x1F & PAL_M;
+ name1 = "PAL_M";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case NTSC_M: {
+ mask1 = 0x1F & NTSC_M;
+ name1 = "NTSC_M";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_443: {
+ mask1 = 0x1F & NTSC_443;
+ name1 = "NTSC_443";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_M_JP: {
+ mask1 = 0x1F & NTSC_M_JP;
+ name1 = "NTSC_M_JP";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N: {
+ mask1 = 0x1F & NTSC_M;
+ name1 = "NTSC_N";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N_443: {
+ mask1 = 0x1F & NTSC_N_443;
+ name1 = "NTSC_N_443";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case PAL_BGHIN_SLOW: {
+ mask1 = 0x001F & PAL_BGHIN_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_BGHIN_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case SECAM_SLOW: {
+ mask1 = 0x001F & SECAM_SLOW;
+ mask1 |= 0x0200;
+ name1 = "SECAM_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_Nc_SLOW: {
+ mask1 = 0x001F & PAL_Nc_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_Nc_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_60_SLOW: {
+ mask1 = 0x001F & PAL_60_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_60_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case PAL_M_SLOW: {
+ mask1 = 0x001F & PAL_M_SLOW;
+ mask1 |= 0x0200;
+ name1 = "PAL_M_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
+ break;
+ }
+ case NTSC_M_SLOW: {
+ mask1 = 0x001F & NTSC_M_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_M_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_443_SLOW: {
+ mask1 = 0x001F & NTSC_443_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_443_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_M_JP_SLOW: {
+ mask1 = 0x001F & NTSC_M_JP_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_M_JP_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N_SLOW: {
+ mask1 = 0x001F & NTSC_N_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_N_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ case NTSC_N_443_SLOW: {
+ mask1 = 0x001F & NTSC_N_443_SLOW;
+ mask1 |= 0x0200;
+ name1 = "NTSC_N_443_SLOW";
+ colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
+ break;
+ }
+ default:
+ return -1;
+ }
+
+ for (j = 0; j < RESOLUTION_MANY; j++) {
+ mask2 = 0x0000;
+ switch (j) {
+ case AT_720x576: {
+ if (0x1 & mask1)
+ continue;
+ name2 = "_AT_720x576";
+ width = 720;
+ height = 576;
+ break;
+ }
+ case AT_704x576: {
+ if (0x1 & mask1)
+ continue;
+ name2 = "_AT_704x576";
+ width = 704;
+ height = 576;
+ break;
+ }
+ case AT_640x480: {
+ name2 = "_AT_640x480";
+ width = 640;
+ height = 480;
+ break;
+ }
+ case AT_720x480: {
+ if (!(0x1 & mask1))
+ continue;
+ name2 = "_AT_720x480";
+ width = 720;
+ height = 480;
+ break;
+ }
+ case AT_360x288: {
+ if (0x1 & mask1)
+ continue;
+ name2 = "_AT_360x288";
+ width = 360;
+ height = 288;
+ mask2 = 0x0800;
+ break;
+ }
+ case AT_320x240: {
+ name2 = "_AT_320x240";
+ width = 320;
+ height = 240;
+ mask2 = 0x0800;
+ break;
+ }
+ case AT_360x240: {
+ if (!(0x1 & mask1))
+ continue;
+ name2 = "_AT_360x240";
+ width = 360;
+ height = 240;
+ mask2 = 0x0800;
+ break;
+ }
+ default:
+ return -2;
+ }
+
+ for (k = 0; k < PIXELFORMAT_MANY; k++) {
+ mask3 = 0x0000;
+ switch (k) {
+ case FMT_UYVY: {
+ name3 = __stringify(FMT_UYVY);
+ pixelformat = V4L2_PIX_FMT_UYVY;
+ mask3 |= (0x02 << 5);
+ break;
+ }
+ case FMT_YUY2: {
+ name3 = __stringify(FMT_YUY2);
+ pixelformat = V4L2_PIX_FMT_YUYV;
+ mask3 |= (0x02 << 5);
+ mask3 |= 0x0100;
+ break;
+ }
+ case FMT_RGB24: {
+ name3 = __stringify(FMT_RGB24);
+ pixelformat = V4L2_PIX_FMT_RGB24;
+ mask3 |= (0x03 << 5);
+ break;
+ }
+ case FMT_RGB32: {
+ name3 = __stringify(FMT_RGB32);
+ pixelformat = V4L2_PIX_FMT_RGB32;
+ mask3 |= (0x04 << 5);
+ break;
+ }
+ case FMT_BGR24: {
+ name3 = __stringify(FMT_BGR24);
+ pixelformat = V4L2_PIX_FMT_BGR24;
+ mask3 |= (0x03 << 5);
+ mask3 |= 0x0100;
+ break;
+ }
+ case FMT_BGR32: {
+ name3 = __stringify(FMT_BGR32);
+ pixelformat = V4L2_PIX_FMT_BGR32;
+ mask3 |= (0x04 << 5);
+ mask3 |= 0x0100;
+ break;
+ }
+ default:
+ return -3;
+ }
+ bytesperline = width * ((mask3 & 0x00E0) >> 5);
+ sizeimage = bytesperline * height;
+
+ for (m = 0; m < INTERLACE_MANY; m++) {
+ mask4 = 0x0000;
+ switch (m) {
+ case FIELD_NONE: {
+ name4 = "-n";
+ field = V4L2_FIELD_NONE;
+ break;
+ }
+ case FIELD_INTERLACED: {
+ name4 = "-i";
+ mask4 |= 0x1000;
+ field = V4L2_FIELD_INTERLACED;
+ break;
+ }
+ default:
+ return -4;
+ }
+ if (SETTINGS_MANY <= n)
+ return -5;
+
+ strcpy(easycap_format[n].name, name1);
+ strcat(easycap_format[n].name, name2);
+ strcat(easycap_format[n].name, "_");
+ strcat(easycap_format[n].name, name3);
+ strcat(easycap_format[n].name, name4);
+ easycap_format[n].mask =
+ mask1 | mask2 | mask3 | mask4;
+ fmt = &easycap_format[n].v4l2_format;
+
+ fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
+ fmt->fmt.pix.width = width;
+ fmt->fmt.pix.height = height;
+ fmt->fmt.pix.pixelformat = pixelformat;
+ fmt->fmt.pix.field = field;
+ fmt->fmt.pix.bytesperline = bytesperline;
+ fmt->fmt.pix.sizeimage = sizeimage;
+ fmt->fmt.pix.colorspace = colorspace;
+ fmt->fmt.pix.priv = 0;
+ n++;
+ }
+ }
+ }
+ }
+ if ((1 + SETTINGS_MANY) <= n)
+ return -6;
+ easycap_format[n].mask = 0xFFFF;
+ return n;
+}
+/*---------------------------------------------------------------------------*/
+struct v4l2_queryctrl easycap_control[] = {
+ {
+ .id = V4L2_CID_BRIGHTNESS,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Brightness",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0A_DEFAULT,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_CONTRAST,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Contrast",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0B_DEFAULT + 128,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_SATURATION,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Saturation",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0C_DEFAULT + 128,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_HUE,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Hue",
+ .minimum = 0,
+ .maximum = 255,
+ .step = 1,
+ .default_value = SAA_0D_DEFAULT + 128,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_AUDIO_VOLUME,
+ .type = V4L2_CTRL_TYPE_INTEGER,
+ .name = "Volume",
+ .minimum = 0,
+ .maximum = 31,
+ .step = 1,
+ .default_value = 16,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = V4L2_CID_AUDIO_MUTE,
+ .type = V4L2_CTRL_TYPE_BOOLEAN,
+ .name = "Mute",
+ .default_value = true,
+ .flags = 0,
+ .reserved = {0, 0}
+ },
+/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
+ {
+ .id = 0xFFFFFFFF
+ }
+};
+/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_sound.c b/drivers/staging/media/easycap/easycap_sound.c
new file mode 100644
index 000000000000..b22bb39b5f69
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap_sound.c
@@ -0,0 +1,816 @@
+/******************************************************************************
+* *
+* easycap_sound.c *
+* *
+* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 *
+* *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+
+/*--------------------------------------------------------------------------*/
+/*
+ * PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
+ */
+/*--------------------------------------------------------------------------*/
+static const struct snd_pcm_hardware alsa_hardware = {
+ .info = SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_MMAP_VALID,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_48000,
+ .rate_min = 32000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = PAGE_SIZE *
+ PAGES_PER_AUDIO_FRAGMENT *
+ AUDIO_FRAGMENT_MANY,
+ .period_bytes_min = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT,
+ .period_bytes_max = PAGE_SIZE * PAGES_PER_AUDIO_FRAGMENT * 2,
+ .periods_min = AUDIO_FRAGMENT_MANY,
+ .periods_max = AUDIO_FRAGMENT_MANY * 2,
+};
+
+
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE DAM BUFFER
+ * PROVIDED peasycap->audio_idle IS ZERO. REGARDLESS OF THIS BEING TRUE,
+ * IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO.
+ */
+/*---------------------------------------------------------------------------*/
+void
+easycap_alsa_complete(struct urb *purb)
+{
+ struct easycap *peasycap;
+ struct snd_pcm_substream *pss;
+ struct snd_pcm_runtime *prt;
+ int dma_bytes, fragment_bytes;
+ int isfragment;
+ u8 *p1, *p2;
+ s16 tmp;
+ int i, j, more, much, rc;
+#ifdef UPSAMPLE
+ int k;
+ s16 oldaudio, newaudio, delta;
+#endif /*UPSAMPLE*/
+
+ JOT(16, "\n");
+
+ if (!purb) {
+ SAY("ERROR: purb is NULL\n");
+ return;
+ }
+ peasycap = purb->context;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ much = 0;
+ if (peasycap->audio_idle) {
+ JOM(16, "%i=audio_idle %i=audio_isoc_streaming\n",
+ peasycap->audio_idle, peasycap->audio_isoc_streaming);
+ if (peasycap->audio_isoc_streaming)
+ goto resubmit;
+ }
+/*---------------------------------------------------------------------------*/
+ pss = peasycap->psubstream;
+ if (!pss)
+ goto resubmit;
+ prt = pss->runtime;
+ if (!prt)
+ goto resubmit;
+ dma_bytes = (int)prt->dma_bytes;
+ if (0 == dma_bytes)
+ goto resubmit;
+ fragment_bytes = 4 * ((int)prt->period_size);
+ if (0 == fragment_bytes)
+ goto resubmit;
+/* -------------------------------------------------------------------------*/
+ if (purb->status) {
+ if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
+ JOM(16, "urb status -ESHUTDOWN or -ENOENT\n");
+ return;
+ }
+ SAM("ERROR: non-zero urb status: -%s: %d\n",
+ strerror(purb->status), purb->status);
+ goto resubmit;
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * PROCEED HERE WHEN NO ERROR
+ */
+/*---------------------------------------------------------------------------*/
+
+#ifdef UPSAMPLE
+ oldaudio = peasycap->oldaudio;
+#endif /*UPSAMPLE*/
+
+ for (i = 0; i < purb->number_of_packets; i++) {
+ if (purb->iso_frame_desc[i].status < 0) {
+ SAM("-%s: %d\n",
+ strerror(purb->iso_frame_desc[i].status),
+ purb->iso_frame_desc[i].status);
+ }
+ if (purb->iso_frame_desc[i].status) {
+ JOM(12, "discarding audio samples because "
+ "%i=purb->iso_frame_desc[i].status\n",
+ purb->iso_frame_desc[i].status);
+ continue;
+ }
+ more = purb->iso_frame_desc[i].actual_length;
+ if (more == 0) {
+ peasycap->audio_mt++;
+ continue;
+ }
+ if (0 > more) {
+ SAM("MISTAKE: more is negative\n");
+ return;
+ }
+
+ if (peasycap->audio_mt) {
+ JOM(12, "%4i empty audio urb frames\n",
+ peasycap->audio_mt);
+ peasycap->audio_mt = 0;
+ }
+
+ p1 = (u8 *)(purb->transfer_buffer +
+ purb->iso_frame_desc[i].offset);
+
+ /*
+ * COPY more BYTES FROM ISOC BUFFER
+ * TO THE DMA BUFFER, CONVERTING
+ * 8-BIT MONO TO 16-BIT SIGNED
+ * LITTLE-ENDIAN SAMPLES IF NECESSARY
+ */
+ while (more) {
+ much = dma_bytes - peasycap->dma_fill;
+ if (0 > much) {
+ SAM("MISTAKE: much is negative\n");
+ return;
+ }
+ if (0 == much) {
+ peasycap->dma_fill = 0;
+ peasycap->dma_next = fragment_bytes;
+ JOM(8, "wrapped dma buffer\n");
+ }
+ if (!peasycap->microphone) {
+ if (much > more)
+ much = more;
+ memcpy(prt->dma_area + peasycap->dma_fill,
+ p1, much);
+ p1 += much;
+ more -= much;
+ } else {
+#ifdef UPSAMPLE
+ if (much % 16)
+ JOM(8, "MISTAKE? much"
+ " is not divisible by 16\n");
+ if (much > (16 * more))
+ much = 16 * more;
+ p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
+
+ for (j = 0; j < (much / 16); j++) {
+ newaudio = ((int) *p1) - 128;
+ newaudio = 128 * newaudio;
+
+ delta = (newaudio - oldaudio) / 4;
+ tmp = oldaudio + delta;
+
+ for (k = 0; k < 4; k++) {
+ *p2 = (0x00FF & tmp);
+ *(p2 + 1) = (0xFF00 & tmp) >> 8;
+ p2 += 2;
+ *p2 = (0x00FF & tmp);
+ *(p2 + 1) = (0xFF00 & tmp) >> 8;
+ p2 += 2;
+ tmp += delta;
+ }
+ p1++;
+ more--;
+ oldaudio = tmp;
+ }
+#else /*!UPSAMPLE*/
+ if (much > (2 * more))
+ much = 2 * more;
+ p2 = (u8 *)(prt->dma_area + peasycap->dma_fill);
+
+ for (j = 0; j < (much / 2); j++) {
+ tmp = ((int) *p1) - 128;
+ tmp = 128 * tmp;
+ *p2 = (0x00FF & tmp);
+ *(p2 + 1) = (0xFF00 & tmp) >> 8;
+ p1++;
+ p2 += 2;
+ more--;
+ }
+#endif /*UPSAMPLE*/
+ }
+ peasycap->dma_fill += much;
+ if (peasycap->dma_fill >= peasycap->dma_next) {
+ isfragment = peasycap->dma_fill / fragment_bytes;
+ if (0 > isfragment) {
+ SAM("MISTAKE: isfragment is negative\n");
+ return;
+ }
+ peasycap->dma_read = (isfragment - 1) * fragment_bytes;
+ peasycap->dma_next = (isfragment + 1) * fragment_bytes;
+ if (dma_bytes < peasycap->dma_next)
+ peasycap->dma_next = fragment_bytes;
+
+ if (0 <= peasycap->dma_read) {
+ JOM(8, "snd_pcm_period_elapsed(), %i="
+ "isfragment\n", isfragment);
+ snd_pcm_period_elapsed(pss);
+ }
+ }
+ }
+
+#ifdef UPSAMPLE
+ peasycap->oldaudio = oldaudio;
+#endif /*UPSAMPLE*/
+
+ }
+/*---------------------------------------------------------------------------*/
+/*
+ * RESUBMIT THIS URB
+ */
+/*---------------------------------------------------------------------------*/
+resubmit:
+ if (peasycap->audio_isoc_streaming == 0)
+ return;
+
+ rc = usb_submit_urb(purb, GFP_ATOMIC);
+ if (rc) {
+ if ((-ENODEV != rc) && (-ENOENT != rc)) {
+ SAM("ERROR: while %i=audio_idle, usb_submit_urb failed "
+ "with rc: -%s :%d\n",
+ peasycap->audio_idle, strerror(rc), rc);
+ }
+ if (0 < peasycap->audio_isoc_streaming)
+ peasycap->audio_isoc_streaming--;
+ }
+ return;
+}
+/*****************************************************************************/
+static int easycap_alsa_open(struct snd_pcm_substream *pss)
+{
+ struct snd_pcm *psnd_pcm;
+ struct snd_card *psnd_card;
+ struct easycap *peasycap;
+
+ JOT(4, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ psnd_pcm = pss->pcm;
+ if (!psnd_pcm) {
+ SAY("ERROR: psnd_pcm is NULL\n");
+ return -EFAULT;
+ }
+ psnd_card = psnd_pcm->card;
+ if (!psnd_card) {
+ SAY("ERROR: psnd_card is NULL\n");
+ return -EFAULT;
+ }
+
+ peasycap = psnd_card->private_data;
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (peasycap->psnd_card != psnd_card) {
+ SAM("ERROR: bad peasycap->psnd_card\n");
+ return -EFAULT;
+ }
+ if (peasycap->psubstream) {
+ SAM("ERROR: bad peasycap->psubstream\n");
+ return -EFAULT;
+ }
+ pss->private_data = peasycap;
+ peasycap->psubstream = pss;
+ pss->runtime->hw = peasycap->alsa_hardware;
+ pss->runtime->private_data = peasycap;
+ pss->private_data = peasycap;
+
+ if (0 != easycap_sound_setup(peasycap)) {
+ JOM(4, "ending unsuccessfully\n");
+ return -EFAULT;
+ }
+ JOM(4, "ending successfully\n");
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_close(struct snd_pcm_substream *pss)
+{
+ struct easycap *peasycap;
+
+ JOT(4, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ pss->private_data = NULL;
+ peasycap->psubstream = NULL;
+ JOT(4, "ending successfully\n");
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_vmalloc(struct snd_pcm_substream *pss, size_t sz)
+{
+ struct snd_pcm_runtime *prt;
+ JOT(4, "\n");
+
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ prt = pss->runtime;
+ if (!prt) {
+ SAY("ERROR: substream.runtime is NULL\n");
+ return -EFAULT;
+ }
+ if (prt->dma_area) {
+ if (prt->dma_bytes > sz)
+ return 0;
+ vfree(prt->dma_area);
+ }
+ prt->dma_area = vmalloc(sz);
+ if (!prt->dma_area)
+ return -ENOMEM;
+ prt->dma_bytes = sz;
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_hw_params(struct snd_pcm_substream *pss,
+ struct snd_pcm_hw_params *phw)
+{
+ int rc;
+
+ JOT(4, "%i\n", (params_buffer_bytes(phw)));
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ rc = easycap_alsa_vmalloc(pss, params_buffer_bytes(phw));
+ if (rc)
+ return rc;
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_hw_free(struct snd_pcm_substream *pss)
+{
+ struct snd_pcm_runtime *prt;
+ JOT(4, "\n");
+
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ prt = pss->runtime;
+ if (!prt) {
+ SAY("ERROR: substream.runtime is NULL\n");
+ return -EFAULT;
+ }
+ if (prt->dma_area) {
+ JOT(8, "prt->dma_area = %p\n", prt->dma_area);
+ vfree(prt->dma_area);
+ prt->dma_area = NULL;
+ } else
+ JOT(8, "dma_area already freed\n");
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_prepare(struct snd_pcm_substream *pss)
+{
+ struct easycap *peasycap;
+ struct snd_pcm_runtime *prt;
+
+ JOT(4, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ prt = pss->runtime;
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ JOM(16, "ALSA decides %8i Hz=rate\n", pss->runtime->rate);
+ JOM(16, "ALSA decides %8ld =period_size\n", pss->runtime->period_size);
+ JOM(16, "ALSA decides %8i =periods\n", pss->runtime->periods);
+ JOM(16, "ALSA decides %8ld =buffer_size\n", pss->runtime->buffer_size);
+ JOM(16, "ALSA decides %8zd =dma_bytes\n", pss->runtime->dma_bytes);
+ JOM(16, "ALSA decides %8ld =boundary\n", pss->runtime->boundary);
+ JOM(16, "ALSA decides %8i =period_step\n", pss->runtime->period_step);
+ JOM(16, "ALSA decides %8i =sample_bits\n", pss->runtime->sample_bits);
+ JOM(16, "ALSA decides %8i =frame_bits\n", pss->runtime->frame_bits);
+ JOM(16, "ALSA decides %8ld =min_align\n", pss->runtime->min_align);
+ JOM(12, "ALSA decides %8ld =hw_ptr_base\n", pss->runtime->hw_ptr_base);
+ JOM(12, "ALSA decides %8ld =hw_ptr_interrupt\n",
+ pss->runtime->hw_ptr_interrupt);
+
+ if (prt->dma_bytes != 4 * ((int)prt->period_size) * ((int)prt->periods)) {
+ SAY("MISTAKE: unexpected ALSA parameters\n");
+ return -ENOENT;
+ }
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_ack(struct snd_pcm_substream *pss)
+{
+ return 0;
+}
+/*****************************************************************************/
+static int easycap_alsa_trigger(struct snd_pcm_substream *pss, int cmd)
+{
+ struct easycap *peasycap;
+ int retval;
+
+ JOT(4, "%i=cmd cf %i=START %i=STOP\n", cmd, SNDRV_PCM_TRIGGER_START,
+ SNDRV_PCM_TRIGGER_STOP);
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START: {
+ peasycap->audio_idle = 0;
+ break;
+ }
+ case SNDRV_PCM_TRIGGER_STOP: {
+ peasycap->audio_idle = 1;
+ break;
+ }
+ default:
+ retval = -EINVAL;
+ }
+ return 0;
+}
+/*****************************************************************************/
+static snd_pcm_uframes_t easycap_alsa_pointer(struct snd_pcm_substream *pss)
+{
+ struct easycap *peasycap;
+ snd_pcm_uframes_t offset;
+
+ JOT(16, "\n");
+ if (!pss) {
+ SAY("ERROR: pss is NULL\n");
+ return -EFAULT;
+ }
+ peasycap = snd_pcm_substream_chip(pss);
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if ((0 != peasycap->audio_eof) || (0 != peasycap->audio_idle)) {
+ JOM(8, "returning -EIO because "
+ "%i=audio_idle %i=audio_eof\n",
+ peasycap->audio_idle, peasycap->audio_eof);
+ return -EIO;
+ }
+/*---------------------------------------------------------------------------*/
+ if (0 > peasycap->dma_read) {
+ JOM(8, "returning -EBUSY\n");
+ return -EBUSY;
+ }
+ offset = ((snd_pcm_uframes_t)peasycap->dma_read)/4;
+ JOM(8, "ALSA decides %8i =hw_ptr_base\n", (int)pss->runtime->hw_ptr_base);
+ JOM(8, "ALSA decides %8i =hw_ptr_interrupt\n",
+ (int)pss->runtime->hw_ptr_interrupt);
+ JOM(8, "%7i=offset %7i=dma_read %7i=dma_next\n",
+ (int)offset, peasycap->dma_read, peasycap->dma_next);
+ return offset;
+}
+/*****************************************************************************/
+static struct page *
+easycap_alsa_page(struct snd_pcm_substream *pss, unsigned long offset)
+{
+ return vmalloc_to_page(pss->runtime->dma_area + offset);
+}
+/*****************************************************************************/
+
+static struct snd_pcm_ops easycap_alsa_pcm_ops = {
+ .open = easycap_alsa_open,
+ .close = easycap_alsa_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = easycap_alsa_hw_params,
+ .hw_free = easycap_alsa_hw_free,
+ .prepare = easycap_alsa_prepare,
+ .ack = easycap_alsa_ack,
+ .trigger = easycap_alsa_trigger,
+ .pointer = easycap_alsa_pointer,
+ .page = easycap_alsa_page,
+};
+
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * THE FUNCTION snd_card_create() HAS THIS_MODULE AS AN ARGUMENT. THIS
+ * MEANS MODULE easycap. BEWARE.
+*/
+/*---------------------------------------------------------------------------*/
+int easycap_alsa_probe(struct easycap *peasycap)
+{
+ int rc;
+ struct snd_card *psnd_card;
+ struct snd_pcm *psnd_pcm;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -ENODEV;
+ }
+ if (0 > peasycap->minor) {
+ SAY("ERROR: no minor\n");
+ return -ENODEV;
+ }
+
+ peasycap->alsa_hardware = alsa_hardware;
+ if (peasycap->microphone) {
+ peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_32000;
+ peasycap->alsa_hardware.rate_min = 32000;
+ peasycap->alsa_hardware.rate_max = 32000;
+ } else {
+ peasycap->alsa_hardware.rates = SNDRV_PCM_RATE_48000;
+ peasycap->alsa_hardware.rate_min = 48000;
+ peasycap->alsa_hardware.rate_max = 48000;
+ }
+
+ if (0 != snd_card_create(SNDRV_DEFAULT_IDX1, "easycap_alsa",
+ THIS_MODULE, 0, &psnd_card)) {
+ SAY("ERROR: Cannot do ALSA snd_card_create()\n");
+ return -EFAULT;
+ }
+
+ sprintf(&psnd_card->id[0], "EasyALSA%i", peasycap->minor);
+ strcpy(&psnd_card->driver[0], EASYCAP_DRIVER_DESCRIPTION);
+ strcpy(&psnd_card->shortname[0], "easycap_alsa");
+ sprintf(&psnd_card->longname[0], "%s", &psnd_card->shortname[0]);
+
+ psnd_card->dev = &peasycap->pusb_device->dev;
+ psnd_card->private_data = peasycap;
+ peasycap->psnd_card = psnd_card;
+
+ rc = snd_pcm_new(psnd_card, "easycap_pcm", 0, 0, 1, &psnd_pcm);
+ if (rc) {
+ SAM("ERROR: Cannot do ALSA snd_pcm_new()\n");
+ snd_card_free(psnd_card);
+ return -EFAULT;
+ }
+
+ snd_pcm_set_ops(psnd_pcm, SNDRV_PCM_STREAM_CAPTURE,
+ &easycap_alsa_pcm_ops);
+ psnd_pcm->info_flags = 0;
+ strcpy(&psnd_pcm->name[0], &psnd_card->id[0]);
+ psnd_pcm->private_data = peasycap;
+ peasycap->psnd_pcm = psnd_pcm;
+ peasycap->psubstream = NULL;
+
+ rc = snd_card_register(psnd_card);
+ if (rc) {
+ SAM("ERROR: Cannot do ALSA snd_card_register()\n");
+ snd_card_free(psnd_card);
+ return -EFAULT;
+ }
+
+ SAM("registered %s\n", &psnd_card->id[0]);
+ return 0;
+}
+
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * COMMON AUDIO INITIALIZATION
+ */
+/*---------------------------------------------------------------------------*/
+int
+easycap_sound_setup(struct easycap *peasycap)
+{
+ int rc;
+
+ JOM(4, "starting initialization\n");
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL.\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -ENODEV;
+ }
+ JOM(16, "0x%08lX=peasycap->pusb_device\n", (long int)peasycap->pusb_device);
+
+ rc = audio_setup(peasycap);
+ JOM(8, "audio_setup() returned %i\n", rc);
+
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device has become NULL\n");
+ return -ENODEV;
+ }
+/*---------------------------------------------------------------------------*/
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device has become NULL\n");
+ return -ENODEV;
+ }
+ rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface,
+ peasycap->audio_altsetting_on);
+ JOM(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface,
+ peasycap->audio_altsetting_on, rc);
+
+ rc = wakeup_device(peasycap->pusb_device);
+ JOM(8, "wakeup_device() returned %i\n", rc);
+
+ peasycap->audio_eof = 0;
+ peasycap->audio_idle = 0;
+
+ submit_audio_urbs(peasycap);
+
+ JOM(4, "finished initialization\n");
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * SUBMIT ALL AUDIO URBS.
+ */
+/*---------------------------------------------------------------------------*/
+int
+submit_audio_urbs(struct easycap *peasycap)
+{
+ struct data_urb *pdata_urb;
+ struct urb *purb;
+ struct list_head *plist_head;
+ int j, isbad, nospc, m, rc;
+ int isbuf;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+ if (!peasycap->purb_audio_head) {
+ SAM("ERROR: peasycap->urb_audio_head uninitialized\n");
+ return -EFAULT;
+ }
+ if (!peasycap->pusb_device) {
+ SAM("ERROR: peasycap->pusb_device is NULL\n");
+ return -EFAULT;
+ }
+
+ if (peasycap->audio_isoc_streaming) {
+ JOM(4, "already streaming audio urbs\n");
+ return 0;
+ }
+
+ JOM(4, "initial submission of all audio urbs\n");
+ rc = usb_set_interface(peasycap->pusb_device,
+ peasycap->audio_interface,
+ peasycap->audio_altsetting_on);
+ JOM(8, "usb_set_interface(.,%i,%i) returned %i\n",
+ peasycap->audio_interface,
+ peasycap->audio_altsetting_on, rc);
+
+ isbad = 0;
+ nospc = 0;
+ m = 0;
+ list_for_each(plist_head, peasycap->purb_audio_head) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ purb = pdata_urb->purb;
+ isbuf = pdata_urb->isbuf;
+
+ purb->interval = 1;
+ purb->dev = peasycap->pusb_device;
+ purb->pipe = usb_rcvisocpipe(peasycap->pusb_device,
+ peasycap->audio_endpointnumber);
+ purb->transfer_flags = URB_ISO_ASAP;
+ purb->transfer_buffer = peasycap->audio_isoc_buffer[isbuf].pgo;
+ purb->transfer_buffer_length = peasycap->audio_isoc_buffer_size;
+ purb->complete = easycap_alsa_complete;
+ purb->context = peasycap;
+ purb->start_frame = 0;
+ purb->number_of_packets = peasycap->audio_isoc_framesperdesc;
+ for (j = 0; j < peasycap->audio_isoc_framesperdesc; j++) {
+ purb->iso_frame_desc[j].offset = j * peasycap->audio_isoc_maxframesize;
+ purb->iso_frame_desc[j].length = peasycap->audio_isoc_maxframesize;
+ }
+
+ rc = usb_submit_urb(purb, GFP_KERNEL);
+ if (rc) {
+ isbad++;
+ SAM("ERROR: usb_submit_urb() failed"
+ " for urb with rc: -%s: %d\n",
+ strerror(rc), rc);
+ } else {
+ m++;
+ }
+ } else {
+ isbad++;
+ }
+ }
+ if (nospc) {
+ SAM("-ENOSPC=usb_submit_urb() for %i urbs\n", nospc);
+ SAM("..... possibly inadequate USB bandwidth\n");
+ peasycap->audio_eof = 1;
+ }
+ if (isbad) {
+ JOM(4, "attempting cleanup instead of submitting\n");
+ list_for_each(plist_head, (peasycap->purb_audio_head)) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb)
+ usb_kill_urb(pdata_urb->purb);
+ }
+ peasycap->audio_isoc_streaming = 0;
+ } else {
+ peasycap->audio_isoc_streaming = m;
+ JOM(4, "submitted %i audio urbs\n", m);
+ }
+
+ return 0;
+}
+/*****************************************************************************/
+/*---------------------------------------------------------------------------*/
+/*
+ * KILL ALL AUDIO URBS.
+ */
+/*---------------------------------------------------------------------------*/
+int
+kill_audio_urbs(struct easycap *peasycap)
+{
+ int m;
+ struct list_head *plist_head;
+ struct data_urb *pdata_urb;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return -EFAULT;
+ }
+
+ if (!peasycap->audio_isoc_streaming) {
+ JOM(8, "%i=audio_isoc_streaming, no audio urbs killed\n",
+ peasycap->audio_isoc_streaming);
+ return 0;
+ }
+
+ if (!peasycap->purb_audio_head) {
+ SAM("ERROR: peasycap->purb_audio_head is NULL\n");
+ return -EFAULT;
+ }
+
+ peasycap->audio_isoc_streaming = 0;
+ JOM(4, "killing audio urbs\n");
+ m = 0;
+ list_for_each(plist_head, (peasycap->purb_audio_head)) {
+ pdata_urb = list_entry(plist_head, struct data_urb, list_head);
+ if (pdata_urb && pdata_urb->purb) {
+ usb_kill_urb(pdata_urb->purb);
+ m++;
+ }
+ }
+ JOM(4, "%i audio urbs killed\n", m);
+
+ return 0;
+}
+/*****************************************************************************/
diff --git a/drivers/staging/media/easycap/easycap_testcard.c b/drivers/staging/media/easycap/easycap_testcard.c
new file mode 100644
index 000000000000..0f71470ace39
--- /dev/null
+++ b/drivers/staging/media/easycap/easycap_testcard.c
@@ -0,0 +1,155 @@
+/******************************************************************************
+* *
+* easycap_testcard.c *
+* *
+******************************************************************************/
+/*
+ *
+ * Copyright (C) 2010 R.M. Thomas <rmthomas@sciolus.org>
+ *
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * The software 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 software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+*/
+/*****************************************************************************/
+
+#include "easycap.h"
+
+/*****************************************************************************/
+#define TESTCARD_BYTESPERLINE (2 * 720)
+void
+easycap_testcard(struct easycap *peasycap, int field)
+{
+ int total;
+ int y, u, v, r, g, b;
+ unsigned char uyvy[4];
+ int i1, line, k, m, n, more, much, barwidth, barheight;
+ unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2;
+ struct data_buffer *pfield_buffer;
+
+ if (!peasycap) {
+ SAY("ERROR: peasycap is NULL\n");
+ return;
+ }
+ JOM(8, "%i=field\n", field);
+ switch (peasycap->width) {
+ case 720:
+ case 360: {
+ barwidth = (2 * 720) / 8;
+ break;
+ }
+ case 704:
+ case 352: {
+ barwidth = (2 * 704) / 8;
+ break;
+ }
+ case 640:
+ case 320: {
+ barwidth = (2 * 640) / 8;
+ break;
+ }
+ default: {
+ SAM("ERROR: cannot set barwidth\n");
+ return;
+ }
+ }
+ if (TESTCARD_BYTESPERLINE < barwidth) {
+ SAM("ERROR: barwidth is too large\n");
+ return;
+ }
+ switch (peasycap->height) {
+ case 576:
+ case 288: {
+ barheight = 576;
+ break;
+ }
+ case 480:
+ case 240: {
+ barheight = 480;
+ break;
+ }
+ default: {
+ SAM("ERROR: cannot set barheight\n");
+ return;
+ }
+ }
+ total = 0;
+ k = field;
+ m = 0;
+ n = 0;
+
+ for (line = 0; line < (barheight / 2); line++) {
+ for (i1 = 0; i1 < 8; i1++) {
+ r = (i1 * 256)/8;
+ g = (i1 * 256)/8;
+ b = (i1 * 256)/8;
+
+ y = 299*r/1000 + 587*g/1000 + 114*b/1000 ;
+ u = -147*r/1000 - 289*g/1000 + 436*b/1000 ;
+ u = u + 128;
+ v = 615*r/1000 - 515*g/1000 - 100*b/1000 ;
+ v = v + 128;
+
+ uyvy[0] = 0xFF & u ;
+ uyvy[1] = 0xFF & y ;
+ uyvy[2] = 0xFF & v ;
+ uyvy[3] = 0xFF & y ;
+
+ p1 = &bfbar[0];
+ while (p1 < &bfbar[barwidth]) {
+ *p1++ = uyvy[0] ;
+ *p1++ = uyvy[1] ;
+ *p1++ = uyvy[2] ;
+ *p1++ = uyvy[3] ;
+ total += 4;
+ }
+
+ p1 = &bfbar[0];
+ more = barwidth;
+
+ while (more) {
+ if ((FIELD_BUFFER_SIZE/PAGE_SIZE) <= m) {
+ SAM("ERROR: bad m reached\n");
+ return;
+ }
+ if (PAGE_SIZE < n) {
+ SAM("ERROR: bad n reached\n");
+ return;
+ }
+
+ if (0 > more) {
+ SAM("ERROR: internal fault\n");
+ return;
+ }
+
+ much = PAGE_SIZE - n;
+ if (much > more)
+ much = more;
+ pfield_buffer = &peasycap->field_buffer[k][m];
+ p2 = pfield_buffer->pgo + n;
+ memcpy(p2, p1, much);
+
+ p1 += much;
+ n += much;
+ more -= much;
+ if (PAGE_SIZE == n) {
+ m++;
+ n = 0;
+ }
+ }
+ }
+ }
+ return;
+}