提交 702422bd 编写于 作者: R R.M. Thomas 提交者: Greg Kroah-Hartman

Staging: easycap: add easycap driver

This adds the easycap USB video adapter driver to
the staging directory.
Signed-off-by: NGreg Kroah-Hartman <gregkh@suse.de>
上级 178f16db
......@@ -149,5 +149,7 @@ source "drivers/staging/mrst-touchscreen/Kconfig"
source "drivers/staging/msm/Kconfig"
source "drivers/staging/easycap/Kconfig"
endif # !STAGING_EXCLUDE_BUILD
endif # STAGING
......@@ -55,3 +55,4 @@ obj-$(CONFIG_ADIS16255) += adis16255/
obj-$(CONFIG_FB_XGI) += xgifb/
obj-$(CONFIG_TOUCHSCREEN_MRSTOUCH) += mrst-touchscreen/
obj-$(CONFIG_MSM_STAGING) += msm/
obj-$(CONFIG_EASYCAP) += easycap/
config EASYCAP
tristate "EasyCAP USB ID 05e1:0408 support"
---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
obj-$(CONFIG_EASYCAP) += easycap.o
easycap-objs := easycap_main.o easycap_low.o easycap_sound.o
easycap-objs += easycap_ioctl.o easycap_settings.o
easycap-objs += easycap_testcard.o
EXTRA_CFLAGS += -Wall
# Impose all or none of the following:
EXTRA_CFLAGS += -DEASYCAP_IS_VIDEODEV_CLIENT
EXTRA_CFLAGS += -DEASYCAP_NEEDS_V4L2_DEVICE_H
EXTRA_CFLAGS += -DEASYCAP_NEEDS_V4L2_FOPS
***********************************************************
* 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
------------------------------
If the parameter EASYCAP_IS_VIDEODEV_CLIENT is undefined during compilation
the built module is entirely independent of the videodev module, and when
the EasyCAP is physically plugged into a USB port the special files
/dev/easycap0 and /dev/easysnd1 are created as video and sound sources
respectively.
If the parameter EASYCAP_IS_VIDEODEV_CLIENT is defined during compilation
the built easycap module is configured to register with the videodev module,
in which case the special files created when the EasyCAP is plugged in are
/dev/video0 and /dev/easysnd0. Use of the easycap module as a client of
the videodev module has received very little testing as of June 2010.
KNOWN BUILD PROBLEMS
--------------------
(1) Recent gcc versions may generate the message:
warning: the frame size of .... bytes is larger than 1024 bytes
This warning can be suppressed by specifying in the Makefile:
EXTRA_CFLAGS += -Wframe-larger-than=8192
but it would be preferable to remove the cause of the warning.
KNOWN RUNTIME ISSUES
--------------------
(1) Randomly (maybe 5 to 10% of occasions) the driver fails to produce any
output at start-up. Closing mplayer (or whatever the user program is) and
restarting it restores normal performance without any other remedial action
being necessary. The reason for this is not known.
(2) Intentionally, this driver will not stream material which is unambiguously
identified by the hardware as copy-protected. The video output will freeze
within about a minute when this situation arises.
(3) The controls for luminance, contrast, saturation, hue and volume may not
always work properly.
(4) Reduced-resolution S-Video seems to suffer from moire artefacts. No
attempt has yet been made to rememdy this.
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.
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 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.
This driver does not understand the V4L1 IOCTL commands, so programs such
as camorama are not compatible. There are reports that the driver does
work with sufficiently recent (V4L2) versions of zoneminder, but I have not
attempted to confirm this myself.
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"
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.
/*****************************************************************************
* *
* 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
* EASYCAP_IS_VIDEODEV_CLIENT
* EASYCAP_NEEDS_USBVIDEO_H
* EASYCAP_NEEDS_V4L2_DEVICE_H
* EASYCAP_NEEDS_V4L2_FOPS
*
* IF REQUIRED THEY MUST BE EXTERNALLY DEFINED, FOR EXAMPLE AS COMPILER
* OPTIONS.
*/
/*---------------------------------------------------------------------------*/
#if (!defined(EASYCAP_H))
#define EASYCAP_H
#if defined(EASYCAP_DEBUG)
#if (9 < EASYCAP_DEBUG)
#error Debug levels 0 to 9 are okay.\
To achieve higher levels, remove this trap manually from easycap.h
#endif
#endif /*EASYCAP_DEBUG*/
/*---------------------------------------------------------------------------*/
/*
* THESE ARE FOR MAINTENANCE ONLY - NORMALLY UNDEFINED:
*/
/*---------------------------------------------------------------------------*/
#undef PREFER_NTSC
#undef EASYCAP_TESTCARD
#undef EASYCAP_TESTTONE
#undef LOCKFRAME
#undef NOREADBACK
#undef AUDIOTIME
/*---------------------------------------------------------------------------*/
/*
*
* DEFINE BRIDGER TO ACTIVATE THE ROUTINE FOR BRIDGING VIDEOTAPE DROPOUTS.
*
* *** UNDER DEVELOPMENT/TESTING - NOT READY YET!***
*
*/
/*---------------------------------------------------------------------------*/
#undef BRIDGER
/*---------------------------------------------------------------------------*/
#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/smp_lock.h>
#include <linux/usb.h>
#include <linux/uaccess.h>
#include <linux/i2c.h>
#include <linux/version.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>
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
#if (!defined(__OLD_VIDIOC_))
#define __OLD_VIDIOC_
#endif /* !defined(__OLD_VIDIOC_) */
#include <media/v4l2-dev.h>
#if defined(EASYCAP_NEEDS_V4L2_DEVICE_H)
#include <media/v4l2-device.h>
#endif /*EASYCAP_NEEDS_V4L2_DEVICE_H*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#if (!defined(__OLD_VIDIOC_))
#define __OLD_VIDIOC_
#endif /* !defined(__OLD_VIDIOC_) */
#include <linux/videodev2.h>
#include <linux/soundcard.h>
#if defined(EASYCAP_NEEDS_USBVIDEO_H)
#include <config/video/usbvideo.h>
#endif /*EASYCAP_NEEDS_USBVIDEO_H*/
#if (!defined(PAGE_SIZE))
#error "PAGE_SIZE not defined"
#endif
#define STRINGIZE_AGAIN(x) #x
#define STRINGIZE(x) STRINGIZE_AGAIN(x)
/*---------------------------------------------------------------------------*/
/* 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.8"
#define EASYCAP_DRIVER_DESCRIPTION "easycapdc60"
#define USB_SKEL_MINOR_BASE 192
#define VIDEO_DEVICE_MANY 8
/*---------------------------------------------------------------------------*/
/*
* 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
/*---------------------------------------------------------------------------*/
/*
* 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 3
#define AUDIO_ISOC_BUFFER_SIZE (PAGE_SIZE << AUDIO_ISOC_ORDER)
/*---------------------------------------------------------------------------*/
/*
* AUDIO BUFFERS
*/
/*---------------------------------------------------------------------------*/
#define AUDIO_FRAGMENT_MANY 32
/*---------------------------------------------------------------------------*/
/*
* STRUCTURE DEFINITIONS
*/
/*---------------------------------------------------------------------------*/
struct data_buffer {
struct list_head list_head;
void *pgo;
void *pto;
__u16 kount;
};
/*---------------------------------------------------------------------------*/
struct data_urb {
struct list_head list_head;
struct urb *purb;
int isbuf;
int length;
};
/*---------------------------------------------------------------------------*/
/*
* 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 ilk;
bool microphone;
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
struct video_device *pvideo_device;
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
struct usb_device *pusb_device;
struct usb_interface *pusb_interface;
struct kref kref;
struct mutex mutex_mmap_video[FRAME_BUFFER_MANY];
struct mutex mutex_timeval0;
struct mutex mutex_timeval1;
int queued[FRAME_BUFFER_MANY];
int done[FRAME_BUFFER_MANY];
wait_queue_head_t wq_video;
wait_queue_head_t wq_audio;
int input;
int polled;
int standard_offset;
int format_offset;
int fps;
int usec;
int tolerate;
int merit[180];
struct timeval timeval0;
struct timeval timeval1;
struct timeval timeval2;
struct timeval timeval7;
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;
int fudge;
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;
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;
__u32 field;
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;
/*---------------------------------------------------------------------------*/
/*
* 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;
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 easysnd_complete(). */
/* Bumped only by easysnd_complete(). */
int audio_read; /* Audio buffer page being read by easysnd_read(). */
/* Set by easysnd_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[];
};
/*---------------------------------------------------------------------------*/
struct easycap_standard {
__u16 mask;
struct v4l2_standard v4l2_standard;
};
struct easycap_format {
__u16 mask;
char name[128];
struct v4l2_format v4l2_format;
};
/*---------------------------------------------------------------------------*/
/*
* VIDEO FUNCTION PROTOTYPES
*/
/*---------------------------------------------------------------------------*/
void easycap_complete(struct urb *);
int easycap_open(struct inode *, struct file *);
int easycap_release(struct inode *, struct file *);
int easycap_ioctl(struct inode *, struct file *, \
unsigned int, unsigned long);
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
int easycap_open_noinode(struct file *);
int easycap_release_noinode(struct file *);
long easycap_ioctl_noinode(struct file *, \
unsigned int, unsigned long);
int videodev_release(struct video_device *);
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
unsigned int easycap_poll(struct file *, poll_table *);
int easycap_mmap(struct file *, struct vm_area_struct *);
int easycap_usb_probe(struct usb_interface *, \
const struct usb_device_id *);
void easycap_usb_disconnect(struct usb_interface *);
void easycap_delete(struct kref *);
void easycap_vma_open(struct vm_area_struct *);
void easycap_vma_close(struct vm_area_struct *);
int easycap_vma_fault(struct vm_area_struct *, struct vm_fault *);
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 debrief(struct easycap *);
void sayreadonly(struct easycap *);
void easycap_testcard(struct easycap *, int);
int explain_ioctl(__u32);
int explain_cid(__u32);
int fillin_formats(void);
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
*/
/*---------------------------------------------------------------------------*/
void easysnd_complete(struct urb *);
ssize_t easysnd_read(struct file *, char __user *, size_t, loff_t *);
int easysnd_open(struct inode *, struct file *);
int easysnd_release(struct inode *, struct file *);
int easysnd_ioctl(struct inode *, struct file *, \
unsigned int, unsigned long);
unsigned int easysnd_poll(struct file *, poll_table *);
void easysnd_delete(struct kref *);
int submit_audio_urbs(struct easycap *);
int kill_audio_urbs(struct easycap *);
void easysnd_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 *);
int setup_saa(struct usb_device *);
int setup_vt(struct usb_device *);
int check_stk(struct usb_device *);
int check_saa(struct usb_device *);
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 wait_i2c(struct usb_device *);
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 set2to78(struct usb_device *);
int set2to93(struct usb_device *);
int regset(struct usb_device *, __u16, __u16);
int regget(struct usb_device *, __u16, void *);
/*---------------------------------------------------------------------------*/
struct signed_div_result {
long long int quotient;
unsigned long long int remainder;
} signed_div(long long int, long long int);
/*---------------------------------------------------------------------------*/
/*
* 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 STANDARD_MANY 10
/*---------------------------------------------------------------------------*/
/*
* 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,
FIELD_ALTERNATE,
INTERLACE_MANY
};
#define SETTINGS_MANY (STANDARD_MANY * \
RESOLUTION_MANY * \
2 * \
PIXELFORMAT_MANY * \
INTERLACE_MANY)
/*---------------------------------------------------------------------------*/
/*
* MACROS
*/
/*---------------------------------------------------------------------------*/
#define GET(X, Y, Z) do { \
int rc; \
*(Z) = (__u16)0; \
rc = regget(X, Y, Z); \
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)
/*---------------------------------------------------------------------------*/
#define SAY(format, args...) do { \
printk(KERN_DEBUG "easycap: %s: " format, __func__, ##args); \
} while (0)
#if defined(EASYCAP_DEBUG)
#define JOT(n, format, args...) do { \
if (n <= easycap_debug) { \
printk(KERN_DEBUG "easycap: %s: " format, __func__, ##args); \
} \
} while (0)
#else
#define JOT(n, format, args...) do {} while (0)
#endif /*EASYCAP_DEBUG*/
#define POUT JOT(8, ":-(in file %s line %4i\n", __FILE__, __LINE__)
#define MICROSECONDS(X, Y) \
((1000000*((long long int)(X.tv_sec - Y.tv_sec))) + \
(long long int)(X.tv_usec - Y.tv_usec))
/*---------------------------------------------------------------------------*/
/*
* (unsigned char *)P pointer to next byte pair
* (long int *)X pointer to accumulating count
* (long int *)Y pointer to accumulating sum
* (long long int *)Z pointer to accumulating sum of squares
*/
/*---------------------------------------------------------------------------*/
#define SUMMER(P, X, Y, Z) do { \
unsigned char *p; \
unsigned int u0, u1, u2; \
long int s; \
p = (unsigned char *)(P); \
u0 = (unsigned int) (*p); \
u1 = (unsigned int) (*(p + 1)); \
u2 = (unsigned int) ((u1 << 8) | u0); \
if (0x8000 & u2) \
s = -(long int)(0x7FFF & (~u2)); \
else \
s = (long int)(0x7FFF & u2); \
*((X)) += (long int) 1; \
*((Y)) += (long int) s; \
*((Z)) += ((long long int)(s) * (long long int)(s)); \
} while (0)
/*---------------------------------------------------------------------------*/
#endif /*EASYCAP_H*/
/*****************************************************************************
* *
* easycap_debug.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
*
*/
/*****************************************************************************/
extern int easycap_debug;
/******************************************************************************
* *
* 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 "easycap.h"
#include "easycap_debug.h"
#include "easycap_standard.h"
#include "easycap_ioctl.h"
/*--------------------------------------------------------------------------*/
/*
* UNLESS THERE IS A PREMATURE ERROR RETURN THIS ROUTINE UPDATES THE
* FOLLOWING:
* peasycap->standard_offset
* peasycap->fps
* peasycap->usec
* peasycap->tolerate
*/
/*---------------------------------------------------------------------------*/
int adjust_standard(struct easycap *peasycap, v4l2_std_id std_id)
{
struct easycap_standard *peasycap_standard;
__u16 reg, set;
int ir, rc, need;
unsigned int itwas, isnow;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("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) {
SAY("ERROR: 0x%08X=std_id: standard not found\n", \
(unsigned int)std_id);
return -EINVAL;
}
SAY("user requests standard: %s\n", \
&(peasycap_standard->v4l2_standard.name[0]));
if (peasycap->standard_offset == \
(int)(peasycap_standard - &easycap_standard[0])) {
SAY("requested standard already in effect\n");
return 0;
}
peasycap->standard_offset = (int)(peasycap_standard - &easycap_standard[0]);
peasycap->fps = peasycap_standard->v4l2_standard.frameperiod.denominator / \
peasycap_standard->v4l2_standard.frameperiod.numerator;
if (!peasycap->fps) {
SAY("MISTAKE: frames-per-second is zero\n");
return -EFAULT;
}
JOT(8, "%i frames-per-second\n", peasycap->fps);
peasycap->usec = 1000000 / (2 * peasycap->fps);
peasycap->tolerate = 1000 * (25 / peasycap->fps);
kill_video_urbs(peasycap);
/*--------------------------------------------------------------------------*/
/*
* 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)
SAY("ERROR: cannot read SAA register 0x%02X\n", reg);
else
itwas = (unsigned int)ir;
set2to78(peasycap->pusb_device);
rc = write_saa(peasycap->pusb_device, reg, set);
if (0 != rc)
SAY("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)
JOT(8, "SAA register 0x%02X changed " \
"to 0x%02X\n", reg, isnow);
else
JOT(8, "SAA register 0x%02X changed " \
"from 0x%02X to 0x%02X\n", reg, itwas, isnow);
set2to78(peasycap->pusb_device);
}
reg = 0x0B; set = 0x48;
ir = read_saa(peasycap->pusb_device, reg);
if (0 > ir)
SAY("ERROR: cannot read SAA register 0x%02X\n", reg);
else
itwas = (unsigned int)ir;
set2to78(peasycap->pusb_device);
rc = write_saa(peasycap->pusb_device, reg, set);
if (0 != rc)
SAY("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)
JOT(8, "SAA register 0x%02X changed " \
"to 0x%02X\n", reg, isnow);
else
JOT(8, "SAA register 0x%02X changed " \
"from 0x%02X to 0x%02X\n", reg, itwas, isnow);
set2to78(peasycap->pusb_device);
}
/*--------------------------------------------------------------------------*/
/*
* 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)
SAY("ERROR: failed to read SAA register 0x%02X\n", reg);
else
itwas = (unsigned int)ir;
set2to78(peasycap->pusb_device);
rc = write_saa(peasycap->pusb_device, reg, set);
if (0 != write_saa(peasycap->pusb_device, reg, set)) {
SAY("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)
JOT(8, "SAA register 0x%02X changed " \
"to 0x%02X\n", reg, isnow);
else
JOT(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)
SAY("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 ;
set2to78(peasycap->pusb_device);
rc = write_saa(peasycap->pusb_device, reg, set);
if (0 != rc)
SAY("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)
JOT(8, "SAA register 0x%02X changed to 0x%02X\n", reg, isnow);
else
JOT(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)
SAY("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 ;
set2to78(peasycap->pusb_device);
rc = write_saa(peasycap->pusb_device, reg, set);
if (0 != rc)
SAY("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)
JOT(8, "SAA register 0x%02X changed to 0x%02X\n", reg, isnow);
else
JOT(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)
SAY("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 ;
set2to78(peasycap->pusb_device);
if (0 != write_saa(peasycap->pusb_device, reg, set))
SAY("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)
JOT(8, "SAA register 0x%02X changed "
"to 0x%02X\n", reg, isnow);
else
JOT(8, "SAA register 0x%02X changed "
"from 0x%02X to 0x%02X\n", reg, itwas, isnow);
}
if (0 != check_saa(peasycap->pusb_device))
SAY("ERROR: check_saa() failed\n");
return 0;
}
/*****************************************************************************/
/*--------------------------------------------------------------------------*/
/*
* THE ALGORITHM FOR RESPONDING TO THE VIDIO_S_FMT IOCTL DEPENDS ON THE
* CURRENT VALUE OF peasycap->standard_offset.
* PROVIDED THE ARGUMENT try IS false AND THERE IS NO PREMATURE ERROR RETURN
* THIS ROUTINE UPDATES THE FOLLOWING:
* peasycap->format_offset
* peasycap->pixelformat
* peasycap->field
* 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;
char bf[5], *pc;
__u32 uc;
if ((struct easycap *)NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return -EFAULT;
}
p = peasycap->pusb_device;
if ((struct usb_device *)NULL == p) {
SAY("ERROR: peaycap->pusb_device is NULL\n");
return -EFAULT;
}
pc = &bf[0];
uc = pixelformat; memcpy((void *)pc, (void *)(&uc), 4); bf[4] = 0;
mask = easycap_standard[peasycap->standard_offset].mask;
SAY("sought: %ix%i,%s(0x%08X),%i=field,0x%02X=std mask\n", \
width, height, pc, pixelformat, field, mask);
if (V4L2_FIELD_ANY == field) {
field = V4L2_FIELD_INTERLACED;
SAY("prefer: V4L2_FIELD_INTERLACED=field, was V4L2_FIELD_ANY\n");
}
peasycap_best_format = (struct easycap_format *)NULL;
peasycap_format = &easycap_format[0];
while (0 != peasycap_format->v4l2_format.fmt.pix.width) {
JOT(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 & 0x0F) == (mask & 0x0F)) && \
(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) {
SAY("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 & 0x0F) == (mask & 0x0F)) && \
(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) {
SAY("cannot do %ix... with standard mask 0x%02X\n", \
width, mask);
SAY("cannot do ...x%i with standard mask 0x%02X\n", \
height, mask);
SAY(" %ix%i unmatched\n", width, height);
return peasycap->format_offset;
}
}
if ((struct easycap_format *)NULL == peasycap_best_format) {
SAY("MISTAKE: peasycap_best_format is NULL");
return -EINVAL;
}
peasycap_format = peasycap_best_format;
/*...........................................................................*/
if (true == try)
return (int)(peasycap_best_format - &easycap_format[0]);
/*...........................................................................*/
if (false != try) {
SAY("MISTAKE: true==try where is should be false\n");
return -EINVAL;
}
SAY("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->field = peasycap_format->v4l2_format.fmt.pix.field;
peasycap->format_offset = (int)(peasycap_format - &easycap_format[0]);
peasycap->bytesperpixel = (0x00F0 & peasycap_format->mask) >> 4 ;
if (0x0100 & peasycap_format->mask)
peasycap->byteswaporder = true;
else
peasycap->byteswaporder = false;
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 (true == 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 (true == peasycap->offerfields) {
SAY("WARNING: %i=peasycap->field is untested: " \
"please report problems\n", peasycap->field);
/*
* FIXME ---- THIS IS UNTESTED, MAY BE (AND PROBABLY IS) INCORRECT:
*
* peasycap->frame_buffer_used = peasycap->frame_buffer_used / 2;
*
* SO DO NOT RISK IT YET.
*
*/
}
kill_video_urbs(peasycap);
/*---------------------------------------------------------------------------*/
/*
* 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 (0 != set_resolution(p, 0x0000, 0x0001, 0x05A0, 0x0121)) {
SAY("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 (0 != set_resolution(p, 0x0004, 0x0001, 0x0584, 0x0121)) {
SAY("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 (0 != set_resolution(p, 0x0014, 0x0020, 0x0514, 0x0110)) {
SAY("ERROR: set_resolution() failed\n");
return -EINVAL;
}
} else {
SAY("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 (0 != set_resolution(p, 0x0000, 0x0003, 0x05A0, 0x00F3)) {
SAY("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 (0 != set_resolution(p, 0x0014, 0x0003, 0x0514, 0x00F3)) {
SAY("ERROR: set_resolution() failed\n");
return -EINVAL;
}
} else {
SAY("MISTAKE: bad format, cannot set resolution\n");
return -EINVAL;
}
}
/*---------------------------------------------------------------------------*/
check_stk(peasycap->pusb_device);
return (int)(peasycap_best_format - &easycap_format[0]);
}
/*****************************************************************************/
int adjust_brightness(struct easycap *peasycap, int value)
{
unsigned int mood;
int i1;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("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;
peasycap->brightness = value;
mood = 0x00FF & (unsigned int)peasycap->brightness;
set2to78(peasycap->pusb_device);
if (!write_saa(peasycap->pusb_device, 0x0A, mood)) {
SAY("adjusting brightness to 0x%02X\n", mood);
return 0;
} else {
SAY("WARNING: failed to adjust brightness " \
"to 0x%02X\n", mood);
return -ENOENT;
}
set2to78(peasycap->pusb_device);
break;
}
i1++;
}
SAY("WARNING: failed to adjust brightness: control not found\n");
return -ENOENT;
}
/*****************************************************************************/
int adjust_contrast(struct easycap *peasycap, int value)
{
unsigned int mood;
int i1;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("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;
peasycap->contrast = value;
mood = 0x00FF & (unsigned int) (peasycap->contrast - 128);
set2to78(peasycap->pusb_device);
if (!write_saa(peasycap->pusb_device, 0x0B, mood)) {
SAY("adjusting contrast to 0x%02X\n", mood);
return 0;
} else {
SAY("WARNING: failed to adjust contrast to " \
"0x%02X\n", mood);
return -ENOENT;
}
set2to78(peasycap->pusb_device);
break;
}
i1++;
}
SAY("WARNING: failed to adjust contrast: control not found\n");
return -ENOENT;
}
/*****************************************************************************/
int adjust_saturation(struct easycap *peasycap, int value)
{
unsigned int mood;
int i1;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("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;
peasycap->saturation = value;
mood = 0x00FF & (unsigned int) (peasycap->saturation - 128);
set2to78(peasycap->pusb_device);
if (!write_saa(peasycap->pusb_device, 0x0C, mood)) {
SAY("adjusting saturation to 0x%02X\n", mood);
return 0;
} else {
SAY("WARNING: failed to adjust saturation to " \
"0x%02X\n", mood);
return -ENOENT;
}
break;
set2to78(peasycap->pusb_device);
}
i1++;
}
SAY("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;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("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;
peasycap->hue = value;
i2 = peasycap->hue - 128;
mood = 0x00FF & ((int) i2);
set2to78(peasycap->pusb_device);
if (!write_saa(peasycap->pusb_device, 0x0D, mood)) {
SAY("adjusting hue to 0x%02X\n", mood);
return 0;
} else {
SAY("WARNING: failed to adjust hue to 0x%02X\n", mood);
return -ENOENT;
}
set2to78(peasycap->pusb_device);
break;
}
i1++;
}
SAY("WARNING: failed to adjust hue: control not found\n");
return -ENOENT;
}
/*****************************************************************************/
int adjust_volume(struct easycap *peasycap, int value)
{
__s8 mood;
int i1;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("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;
peasycap->volume = value;
mood = (16 > peasycap->volume) ? 16 : \
((31 < peasycap->volume) ? 31 : \
(__s8) peasycap->volume);
if (!audio_gainset(peasycap->pusb_device, mood)) {
SAY("adjusting volume to 0x%01X\n", mood);
return 0;
} else {
SAY("WARNING: failed to adjust volume to " \
"0x%1X\n", mood);
return -ENOENT;
}
break;
}
i1++;
}
SAY("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 easysnd_complete() DECLINES TO RESUBMIT
* THE URB AND THE PIPELINE COLLAPSES IRRETRIEVABLY. BEWARE.
*/
/*---------------------------------------------------------------------------*/
int adjust_mute(struct easycap *peasycap, int value)
{
int i1;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("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;
peasycap->timeval0.tv_sec = 0;
SAY("adjusting mute: %i=peasycap->audio_idle\n", \
peasycap->audio_idle);
return 0;
}
default: {
peasycap->audio_idle = 0;
SAY("adjusting mute: %i=peasycap->audio_idle\n", \
peasycap->audio_idle);
return 0;
}
}
break;
}
i1++;
}
SAY("WARNING: failed to adjust mute: control not found\n");
return -ENOENT;
}
/****************************************************************************/
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
long
easycap_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg)\
{
return easycap_ioctl((struct inode *)NULL, file, cmd, arg);
}
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*--------------------------------------------------------------------------*/
int easycap_ioctl(struct inode *inode, struct file *file, \
unsigned int cmd, unsigned long arg)
{
static struct easycap *peasycap;
static struct usb_device *p;
static __u32 isequence;
peasycap = (struct easycap *)file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return -1;
}
p = peasycap->pusb_device;
if ((struct usb_device *)NULL == p) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
/*
* MOST OF THE VARIABLES DECLARED static IN THE case{} BLOCKS BELOW ARE SO
* DECLARED SIMPLY TO AVOID A COMPILER WARNING OF THE KIND:
* easycap_ioctl.c: warning:
* the frame size of ... bytes is larger than 1024 bytes
*/
/*---------------------------------------------------------------------------*/
switch (cmd) {
case VIDIOC_QUERYCAP: {
static struct v4l2_capability v4l2_capability;
static char version[16], *p1, *p2;
static int i, rc, k[3];
static long lng;
JOT(8, "VIDIOC_QUERYCAP\n");
if (16 <= strlen(EASYCAP_DRIVER_VERSION)) {
SAY("ERROR: bad driver version string\n"); 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 (0 != rc) {
SAY("ERROR: %i=strict_strtol(%s,.,,)\n", \
rc, p1);
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]);
JOT(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));
JOT(8, "%s=v4l2_capability.bus_info\n", \
&v4l2_capability.bus_info[0]);
}
if (0 != copy_to_user((void __user *)arg, &v4l2_capability, \
sizeof(struct v4l2_capability))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_ENUMINPUT: {
static struct v4l2_input v4l2_input;
static __u32 index;
JOT(8, "VIDIOC_ENUMINPUT\n");
if (0 != copy_from_user(&v4l2_input, (void __user *)arg, \
sizeof(struct v4l2_input))) {
POUT;
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;
JOT(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;
JOT(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;
JOT(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;
JOT(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;
JOT(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;
JOT(8, "%i=index: %s\n", index, &v4l2_input.name[0]);
break;
}
default: {
JOT(8, "%i=index: exhausts inputs\n", index);
return -EINVAL;
}
}
if (0 != copy_to_user((void __user *)arg, &v4l2_input, \
sizeof(struct v4l2_input))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_INPUT: {
static __u32 index;
JOT(8, "VIDIOC_G_INPUT\n");
index = (__u32)peasycap->input;
JOT(8, "user is told: %i\n", index);
if (0 != copy_to_user((void __user *)arg, &index, sizeof(__u32))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_INPUT:
{
static __u32 index;
JOT(8, "VIDIOC_S_INPUT\n");
if (0 != copy_from_user(&index, (void __user *)arg, sizeof(__u32))) {
POUT;
return -EFAULT;
}
JOT(8, "user requests input %i\n", index);
if ((int)index == peasycap->input) {
SAY("requested input already in effect\n");
break;
}
if ((0 > index) || (5 < index)) {
JOT(8, "ERROR: bad requested input: %i\n", index);
return -EINVAL;
}
peasycap->input = (int)index;
select_input(peasycap->pusb_device, peasycap->input, 9);
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_ENUMAUDIO: {
JOT(8, "VIDIOC_ENUMAUDIO\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_ENUMAUDOUT: {
static struct v4l2_audioout v4l2_audioout;
JOT(8, "VIDIOC_ENUMAUDOUT\n");
if (0 != copy_from_user(&v4l2_audioout, (void __user *)arg, \
sizeof(struct v4l2_audioout))) {
POUT;
return -EFAULT;
}
if (0 != v4l2_audioout.index)
return -EINVAL;
memset(&v4l2_audioout, 0, sizeof(struct v4l2_audioout));
v4l2_audioout.index = 0;
strcpy(&v4l2_audioout.name[0], "Soundtrack");
if (0 != copy_to_user((void __user *)arg, &v4l2_audioout, \
sizeof(struct v4l2_audioout))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_QUERYCTRL: {
static int i1;
static struct v4l2_queryctrl v4l2_queryctrl;
JOT(8, "VIDIOC_QUERYCTRL\n");
if (0 != copy_from_user(&v4l2_queryctrl, (void __user *)arg, \
sizeof(struct v4l2_queryctrl))) {
POUT;
return -EFAULT;
}
i1 = 0;
while (0xFFFFFFFF != easycap_control[i1].id) {
if (easycap_control[i1].id == v4l2_queryctrl.id) {
JOT(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) {
JOT(8, "%i=index: exhausts controls\n", i1);
return -EINVAL;
}
if (0 != copy_to_user((void __user *)arg, &v4l2_queryctrl, \
sizeof(struct v4l2_queryctrl))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_QUERYMENU: {
JOT(8, "VIDIOC_QUERYMENU unsupported\n");
return -EINVAL;
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_CTRL: {
static struct v4l2_control v4l2_control;
JOT(8, "VIDIOC_G_CTRL\n");
if (0 != copy_from_user(&v4l2_control, (void __user *)arg, \
sizeof(struct v4l2_control))) {
POUT;
return -EFAULT;
}
switch (v4l2_control.id) {
case V4L2_CID_BRIGHTNESS: {
v4l2_control.value = peasycap->brightness;
JOT(8, "user enquires brightness: %i\n", v4l2_control.value);
break;
}
case V4L2_CID_CONTRAST: {
v4l2_control.value = peasycap->contrast;
JOT(8, "user enquires contrast: %i\n", v4l2_control.value);
break;
}
case V4L2_CID_SATURATION: {
v4l2_control.value = peasycap->saturation;
JOT(8, "user enquires saturation: %i\n", v4l2_control.value);
break;
}
case V4L2_CID_HUE: {
v4l2_control.value = peasycap->hue;
JOT(8, "user enquires hue: %i\n", v4l2_control.value);
break;
}
case V4L2_CID_AUDIO_VOLUME: {
v4l2_control.value = peasycap->volume;
JOT(8, "user enquires volume: %i\n", v4l2_control.value);
break;
}
case V4L2_CID_AUDIO_MUTE: {
if (1 == peasycap->mute)
v4l2_control.value = true;
else
v4l2_control.value = false;
JOT(8, "user enquires mute: %i\n", v4l2_control.value);
break;
}
default: {
SAY("ERROR: unknown V4L2 control: 0x%08X=id\n", \
v4l2_control.id);
explain_cid(v4l2_control.id);
return -EINVAL;
}
}
if (0 != copy_to_user((void __user *)arg, &v4l2_control, \
sizeof(struct v4l2_control))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(VIDIOC_S_CTRL_OLD)
case VIDIOC_S_CTRL_OLD: {
JOT(8, "VIDIOC_S_CTRL_OLD required at least for xawtv\n");
}
#endif /*VIDIOC_S_CTRL_OLD*/
case VIDIOC_S_CTRL:
{
static struct v4l2_control v4l2_control;
JOT(8, "VIDIOC_S_CTRL\n");
if (0 != copy_from_user(&v4l2_control, (void __user *)arg, \
sizeof(struct v4l2_control))) {
POUT;
return -EFAULT;
}
switch (v4l2_control.id) {
case V4L2_CID_BRIGHTNESS: {
JOT(8, "user requests brightness %i\n", v4l2_control.value);
if (0 != adjust_brightness(peasycap, v4l2_control.value))
;
break;
}
case V4L2_CID_CONTRAST: {
JOT(8, "user requests contrast %i\n", v4l2_control.value);
if (0 != adjust_contrast(peasycap, v4l2_control.value))
;
break;
}
case V4L2_CID_SATURATION: {
JOT(8, "user requests saturation %i\n", v4l2_control.value);
if (0 != adjust_saturation(peasycap, v4l2_control.value))
;
break;
}
case V4L2_CID_HUE: {
JOT(8, "user requests hue %i\n", v4l2_control.value);
if (0 != adjust_hue(peasycap, v4l2_control.value))
;
break;
}
case V4L2_CID_AUDIO_VOLUME: {
JOT(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;
JOT(8, "user requests mute %i\n", v4l2_control.value);
if (true == v4l2_control.value)
mute = 1;
else
mute = 0;
if (0 != adjust_mute(peasycap, mute))
SAY("WARNING: failed to adjust mute to %i\n", mute);
break;
}
default: {
SAY("ERROR: unknown V4L2 control: 0x%08X=id\n", \
v4l2_control.id);
explain_cid(v4l2_control.id);
return -EINVAL;
}
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_EXT_CTRLS: {
JOT(8, "VIDIOC_S_EXT_CTRLS unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_ENUM_FMT: {
static __u32 index;
static struct v4l2_fmtdesc v4l2_fmtdesc;
JOT(8, "VIDIOC_ENUM_FMT\n");
if (0 != copy_from_user(&v4l2_fmtdesc, (void __user *)arg, \
sizeof(struct v4l2_fmtdesc))) {
POUT;
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;
JOT(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;
JOT(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;
JOT(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;
JOT(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;
JOT(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;
JOT(8, "%i=index: %s\n", index, &v4l2_fmtdesc.description[0]);
break;
}
default: {
JOT(8, "%i=index: exhausts formats\n", index);
return -EINVAL;
}
}
if (0 != copy_to_user((void __user *)arg, &v4l2_fmtdesc, \
sizeof(struct v4l2_fmtdesc))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_ENUM_FRAMESIZES: {
JOT(8, "VIDIOC_ENUM_FRAMESIZES unsupported\n");
return -EINVAL;
}
case VIDIOC_ENUM_FRAMEINTERVALS: {
JOT(8, "VIDIOC_ENUM_FRAME_INTERVALS unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_FMT: {
static struct v4l2_format v4l2_format;
static struct v4l2_pix_format v4l2_pix_format;
JOT(8, "VIDIOC_G_FMT\n");
if (0 != copy_from_user(&v4l2_format, (void __user *)arg, \
sizeof(struct v4l2_format))) {
POUT;
return -EFAULT;
}
if (v4l2_format.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
POUT;
return -EINVAL;
}
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[peasycap->format_offset]\
.v4l2_format.fmt.pix), sizeof(v4l2_pix_format));
JOT(8, "user is told: %s\n", \
&easycap_format[peasycap->format_offset].name[0]);
if (0 != copy_to_user((void __user *)arg, &v4l2_format, \
sizeof(struct v4l2_format))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_TRY_FMT:
case VIDIOC_S_FMT: {
static struct v4l2_format v4l2_format;
static struct v4l2_pix_format v4l2_pix_format;
static bool try;
static int best_format;
if (VIDIOC_TRY_FMT == cmd) {
JOT(8, "VIDIOC_TRY_FMT\n");
try = true;
} else {
JOT(8, "VIDIOC_S_FMT\n");
try = false;
}
if (0 != copy_from_user(&v4l2_format, (void __user *)arg, \
sizeof(struct v4l2_format))) {
POUT;
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) {
JOT(8, "WARNING: adjust_format() returned %i\n", best_format);
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));
JOT(8, "user is told: %s\n", &easycap_format[best_format].name[0]);
if (0 != copy_to_user((void __user *)arg, &v4l2_format, \
sizeof(struct v4l2_format))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_CROPCAP: {
static struct v4l2_cropcap v4l2_cropcap;
JOT(8, "VIDIOC_CROPCAP\n");
if (0 != copy_from_user(&v4l2_cropcap, (void __user *)arg, \
sizeof(struct v4l2_cropcap))) {
POUT;
return -EFAULT;
}
if (v4l2_cropcap.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
JOT(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;
JOT(8, "user is told: %ix%i\n", peasycap->width, peasycap->height);
if (0 != copy_to_user((void __user *)arg, &v4l2_cropcap, \
sizeof(struct v4l2_cropcap))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_CROP:
case VIDIOC_S_CROP: {
JOT(8, "VIDIOC_G_CROP|VIDIOC_S_CROP unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_QUERYSTD: {
JOT(8, "VIDIOC_QUERYSTD: " \
"EasyCAP is incapable of detecting standard\n");
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: {
static int last0 = -1, last1 = -1, last2 = -1, last3 = -1;
static struct v4l2_standard v4l2_standard;
static __u32 index;
static struct easycap_standard *peasycap_standard;
JOT(8, "VIDIOC_ENUMSTD\n");
if (0 != copy_from_user(&v4l2_standard, (void __user *)arg, \
sizeof(struct v4l2_standard))) {
POUT;
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) {
JOT(8, "%i=index: exhausts standards\n", index);
return -EINVAL;
}
JOT(8, "%i=index: %s\n", index, \
&(peasycap_standard->v4l2_standard.name[0]));
peasycap_standard->v4l2_standard.index = index;
v4l2_standard.index = index;
if (0 != copy_to_user((void __user *)arg, \
&(peasycap_standard->v4l2_standard), \
sizeof(struct v4l2_standard))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_STD: {
static v4l2_std_id std_id;
static struct easycap_standard *peasycap_standard;
JOT(8, "VIDIOC_G_STD\n");
if (0 != copy_from_user(&std_id, (void __user *)arg, \
sizeof(v4l2_std_id))) {
POUT;
return -EFAULT;
}
peasycap_standard = &easycap_standard[peasycap->standard_offset];
std_id = peasycap_standard->v4l2_standard.id;
JOT(8, "user is told: %s\n", \
&peasycap_standard->v4l2_standard.name[0]);
if (0 != copy_to_user((void __user *)arg, &std_id, \
sizeof(v4l2_std_id))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_STD: {
static v4l2_std_id std_id;
static int rc;
JOT(8, "VIDIOC_S_STD\n");
if (0 != copy_from_user(&std_id, (void __user *)arg, \
sizeof(v4l2_std_id))) {
POUT;
return -EFAULT;
}
rc = adjust_standard(peasycap, std_id);
if (0 > rc) {
JOT(8, "WARNING: adjust_standard() returned %i\n", rc);
return -ENOENT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_REQBUFS: {
static int nbuffers;
static struct v4l2_requestbuffers v4l2_requestbuffers;
JOT(8, "VIDIOC_REQBUFS\n");
if (0 != copy_from_user(&v4l2_requestbuffers, (void __user *)arg, \
sizeof(struct v4l2_requestbuffers))) {
POUT;
return -EFAULT;
}
if (v4l2_requestbuffers.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (v4l2_requestbuffers.memory != V4L2_MEMORY_MMAP) {
POUT;
return -EINVAL;
}
nbuffers = v4l2_requestbuffers.count;
JOT(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) {
JOT(8, " ... agree to %i buffers\n", \
nbuffers);
} else {
JOT(8, " ... insist on %i buffers\n", \
nbuffers);
v4l2_requestbuffers.count = nbuffers;
}
peasycap->frame_buffer_many = nbuffers;
if (0 != copy_to_user((void __user *)arg, &v4l2_requestbuffers, \
sizeof(struct v4l2_requestbuffers))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_QUERYBUF: {
static __u32 index;
static struct v4l2_buffer v4l2_buffer;
JOT(8, "VIDIOC_QUERYBUF\n");
if (peasycap->video_eof) {
JOT(8, "returning -1 because %i=video_eof\n", \
peasycap->video_eof);
return -1;
}
if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
sizeof(struct v4l2_buffer))) {
POUT;
return -EFAULT;
}
if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
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 = peasycap->field;
v4l2_buffer.memory = V4L2_MEMORY_MMAP;
v4l2_buffer.m.offset = index * FRAME_BUFFER_SIZE;
v4l2_buffer.length = FRAME_BUFFER_SIZE;
JOT(16, " %10i=index\n", v4l2_buffer.index);
JOT(16, " 0x%08X=type\n", v4l2_buffer.type);
JOT(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
JOT(16, " 0x%08X=flags\n", v4l2_buffer.flags);
JOT(16, " %10i=field\n", v4l2_buffer.field);
JOT(16, " %10li=timestamp.tv_usec\n", \
(long)v4l2_buffer.timestamp.tv_usec);
JOT(16, " %10i=sequence\n", v4l2_buffer.sequence);
JOT(16, " 0x%08X=memory\n", v4l2_buffer.memory);
JOT(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
JOT(16, " %10i=length\n", v4l2_buffer.length);
if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
sizeof(struct v4l2_buffer))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_QBUF: {
static struct v4l2_buffer v4l2_buffer;
JOT(8, "VIDIOC_QBUF\n");
if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
sizeof(struct v4l2_buffer))) {
POUT;
return -EFAULT;
}
if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (v4l2_buffer.memory != V4L2_MEMORY_MMAP)
return -EINVAL;
if (v4l2_buffer.index < 0 || \
(v4l2_buffer.index >= peasycap->frame_buffer_many))
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 (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
sizeof(struct v4l2_buffer))) {
POUT;
return -EFAULT;
}
JOT(8, "..... user queueing frame buffer %i\n", \
(int)v4l2_buffer.index);
peasycap->frame_lock = 0;
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_DQBUF:
{
#if defined(AUDIOTIME)
static struct signed_div_result sdr;
static long long int above, below, dnbydt, fudge, sll;
static unsigned long long int ull;
static struct timeval timeval0;
struct timeval timeval1;
#endif /*AUDIOTIME*/
static struct timeval timeval, timeval2;
static int i, j;
static struct v4l2_buffer v4l2_buffer;
JOT(8, "VIDIOC_DQBUF\n");
if ((peasycap->video_idle) || (peasycap->video_eof)) {
JOT(8, "returning -EIO because " \
"%i=video_idle %i=video_eof\n", \
peasycap->video_idle, peasycap->video_eof);
return -EIO;
}
if (0 != copy_from_user(&v4l2_buffer, (void __user *)arg, \
sizeof(struct v4l2_buffer))) {
POUT;
return -EFAULT;
}
if (v4l2_buffer.type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
return -EINVAL;
if (!peasycap->video_isoc_streaming) {
JOT(16, "returning -EIO because video urbs not streaming\n");
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) {
if (-EIO == easycap_dqbuf(peasycap, 0))
return -EIO;
} else {
if (peasycap->video_eof)
return -EIO;
}
if (V4L2_BUF_FLAG_DONE != peasycap->done[peasycap->frame_read]) {
SAY("ERROR: V4L2_BUF_FLAG_DONE != 0x%08X\n", \
peasycap->done[peasycap->frame_read]);
}
peasycap->polled = 0;
if (!(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) {
SAY("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;
v4l2_buffer.field = peasycap->field;
if (V4L2_FIELD_ALTERNATE == v4l2_buffer.field)
v4l2_buffer.field = \
0x000F & (peasycap->\
frame_buffer[peasycap->frame_read][0].kount);
do_gettimeofday(&timeval);
timeval2 = timeval;
#if defined(AUDIOTIME)
if (!peasycap->timeval0.tv_sec) {
timeval0 = timeval;
timeval1 = timeval;
timeval2 = timeval;
dnbydt = 192000;
if (mutex_lock_interruptible(&(peasycap->mutex_timeval0)))
return -ERESTARTSYS;
peasycap->timeval0 = timeval0;
mutex_unlock(&(peasycap->mutex_timeval0));
} else {
if (mutex_lock_interruptible(&(peasycap->mutex_timeval1)))
return -ERESTARTSYS;
dnbydt = peasycap->dnbydt;
timeval1 = peasycap->timeval1;
mutex_unlock(&(peasycap->mutex_timeval1));
above = dnbydt * MICROSECONDS(timeval, timeval1);
below = 192000;
sdr = signed_div(above, below);
above = sdr.quotient + timeval1.tv_usec - 350000;
below = 1000000;
sdr = signed_div(above, below);
timeval2.tv_usec = sdr.remainder;
timeval2.tv_sec = timeval1.tv_sec + sdr.quotient;
}
if (!(isequence % 500)) {
fudge = ((long long int)(1000000)) * \
((long long int)(timeval.tv_sec - \
timeval2.tv_sec)) + \
(long long int)(timeval.tv_usec - \
timeval2.tv_usec);
sdr = signed_div(fudge, 1000);
sll = sdr.quotient;
ull = sdr.remainder;
SAY("%5lli.%-3lli=ms timestamp fudge\n", sll, ull);
}
#endif /*AUDIOTIME*/
v4l2_buffer.timestamp = timeval2;
v4l2_buffer.sequence = isequence++;
v4l2_buffer.memory = V4L2_MEMORY_MMAP;
v4l2_buffer.m.offset = v4l2_buffer.index * FRAME_BUFFER_SIZE;
v4l2_buffer.length = FRAME_BUFFER_SIZE;
JOT(16, " %10i=index\n", v4l2_buffer.index);
JOT(16, " 0x%08X=type\n", v4l2_buffer.type);
JOT(16, " %10i=bytesused\n", v4l2_buffer.bytesused);
JOT(16, " 0x%08X=flags\n", v4l2_buffer.flags);
JOT(16, " %10i=field\n", v4l2_buffer.field);
JOT(16, " %10li=timestamp.tv_usec\n", \
(long)v4l2_buffer.timestamp.tv_usec);
JOT(16, " %10i=sequence\n", v4l2_buffer.sequence);
JOT(16, " 0x%08X=memory\n", v4l2_buffer.memory);
JOT(16, " %10i=m.offset\n", v4l2_buffer.m.offset);
JOT(16, " %10i=length\n", v4l2_buffer.length);
if (0 != copy_to_user((void __user *)arg, &v4l2_buffer, \
sizeof(struct v4l2_buffer))) {
POUT;
return -EFAULT;
}
JOT(8, "..... user is offered frame buffer %i\n", \
peasycap->frame_read);
peasycap->frame_lock = 1;
if (peasycap->frame_read == peasycap->frame_fill) {
if (peasycap->frame_lock) {
JOT(8, "ERROR: filling frame buffer " \
"while offered to user\n");
}
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
/*---------------------------------------------------------------------------*/
/*
* AUDIO URBS HAVE ALREADY BEEN SUBMITTED WHEN THIS COMMAND IS RECEIVED;
* VIDEO URBS HAVE NOT.
*/
/*---------------------------------------------------------------------------*/
case VIDIOC_STREAMON: {
static int i;
JOT(8, "VIDIOC_STREAMON\n");
isequence = 0;
for (i = 0; i < 180; i++)
peasycap->merit[i] = 0;
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
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: {
JOT(8, "VIDIOC_STREAMOFF\n");
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
peasycap->video_idle = 1;
peasycap->audio_idle = 1; peasycap->timeval0.tv_sec = 0;
/*---------------------------------------------------------------------------*/
/*
* IF THE WAIT QUEUES ARE NOT CLEARED IN RESPONSE TO THE STREAMOFF COMMAND
* THE USERSPACE PROGRAM, E.G. mplayer, MAY HANG ON EXIT. BEWARE.
*/
/*---------------------------------------------------------------------------*/
JOT(8, "calling wake_up on wq_video and wq_audio\n");
wake_up_interruptible(&(peasycap->wq_video));
wake_up_interruptible(&(peasycap->wq_audio));
/*---------------------------------------------------------------------------*/
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_PARM: {
static struct v4l2_streamparm v4l2_streamparm;
JOT(8, "VIDIOC_G_PARM\n");
if (0 != copy_from_user(&v4l2_streamparm, (void __user *)arg, \
sizeof(struct v4l2_streamparm))) {
POUT;
return -EFAULT;
}
if (v4l2_streamparm.type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
POUT;
return -EINVAL;
}
v4l2_streamparm.parm.capture.capability = 0;
v4l2_streamparm.parm.capture.capturemode = 0;
v4l2_streamparm.parm.capture.timeperframe.numerator = 1;
v4l2_streamparm.parm.capture.timeperframe.denominator = 30;
v4l2_streamparm.parm.capture.readbuffers = peasycap->frame_buffer_many;
v4l2_streamparm.parm.capture.extendedmode = 0;
if (0 != copy_to_user((void __user *)arg, &v4l2_streamparm, \
sizeof(struct v4l2_streamparm))) {
POUT;
return -EFAULT;
}
break;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_PARM: {
JOT(8, "VIDIOC_S_PARM unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_AUDIO: {
JOT(8, "VIDIOC_G_AUDIO unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_AUDIO: {
JOT(8, "VIDIOC_S_AUDIO unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_S_TUNER: {
JOT(8, "VIDIOC_S_TUNER unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_FBUF:
case VIDIOC_S_FBUF:
case VIDIOC_OVERLAY: {
JOT(8, "VIDIOC_G_FBUF|VIDIOC_S_FBUF|VIDIOC_OVERLAY unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
case VIDIOC_G_TUNER: {
JOT(8, "VIDIOC_G_TUNER unsupported\n");
return -EINVAL;
}
case VIDIOC_G_FREQUENCY:
case VIDIOC_S_FREQUENCY: {
JOT(8, "VIDIOC_G_FREQUENCY|VIDIOC_S_FREQUENCY unsupported\n");
return -EINVAL;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
default: {
JOT(8, "ERROR: unrecognized V4L2 IOCTL command: 0x%08X\n", cmd);
explain_ioctl(cmd);
POUT;
return -ENOIOCTLCMD;
}
}
return 0;
}
/****************************************************************************/
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
long
easysnd_ioctl_noinode(struct file *file, unsigned int cmd, unsigned long arg)
{
return easysnd_ioctl((struct inode *)NULL, file, cmd, arg);
}
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*--------------------------------------------------------------------------*/
int easysnd_ioctl(struct inode *inode, struct file *file, \
unsigned int cmd, unsigned long arg)
{
struct easycap *peasycap;
struct usb_device *p;
peasycap = (struct easycap *)file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL.\n");
return -1;
}
p = peasycap->pusb_device;
/*---------------------------------------------------------------------------*/
switch (cmd) {
case SNDCTL_DSP_GETCAPS: {
int caps;
JOT(8, "SNDCTL_DSP_GETCAPS\n");
if (true == peasycap->microphone)
caps = 0x02400000;
else
caps = 0x04400000;
if (0 != copy_to_user((void __user *)arg, &caps, sizeof(int)))
return -EFAULT;
break;
}
case SNDCTL_DSP_GETFMTS: {
int incoming;
JOT(8, "SNDCTL_DSP_GETFMTS\n");
if (true == peasycap->microphone)
incoming = AFMT_S16_LE;
else
incoming = AFMT_S16_LE;
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
return -EFAULT;
break;
}
case SNDCTL_DSP_SETFMT: {
int incoming, outgoing;
JOT(8, "SNDCTL_DSP_SETFMT\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
return -EFAULT;
JOT(8, "........... %i=incoming\n", incoming);
if (true == peasycap->microphone)
outgoing = AFMT_S16_LE;
else
outgoing = AFMT_S16_LE;
if (incoming != outgoing) {
JOT(8, "........... %i=outgoing\n", outgoing);
JOT(8, " cf. %i=AFMT_S16_LE\n", AFMT_S16_LE);
JOT(8, " cf. %i=AFMT_U8\n", AFMT_U8);
if (0 != copy_to_user((void __user *)arg, &outgoing, \
sizeof(int)))
return -EFAULT;
return -EINVAL ;
}
break;
}
case SNDCTL_DSP_STEREO: {
int incoming;
JOT(8, "SNDCTL_DSP_STEREO\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
return -EFAULT;
JOT(8, "........... %i=incoming\n", incoming);
if (true == peasycap->microphone)
incoming = 0;
else
incoming = 1;
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
return -EFAULT;
break;
}
case SNDCTL_DSP_SPEED: {
int incoming;
JOT(8, "SNDCTL_DSP_SPEED\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
return -EFAULT;
JOT(8, "........... %i=incoming\n", incoming);
if (true == peasycap->microphone)
incoming = 8000;
else
incoming = 48000;
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
return -EFAULT;
break;
}
case SNDCTL_DSP_GETTRIGGER: {
int incoming;
JOT(8, "SNDCTL_DSP_GETTRIGGER\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
return -EFAULT;
JOT(8, "........... %i=incoming\n", incoming);
incoming = PCM_ENABLE_INPUT;
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
return -EFAULT;
break;
}
case SNDCTL_DSP_SETTRIGGER: {
int incoming;
JOT(8, "SNDCTL_DSP_SETTRIGGER\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
return -EFAULT;
JOT(8, "........... %i=incoming\n", incoming);
JOT(8, "........... cf 0x%x=PCM_ENABLE_INPUT " \
"0x%x=PCM_ENABLE_OUTPUT\n", \
PCM_ENABLE_INPUT, PCM_ENABLE_OUTPUT);
;
;
;
;
break;
}
case SNDCTL_DSP_GETBLKSIZE: {
int incoming;
JOT(8, "SNDCTL_DSP_GETBLKSIZE\n");
if (0 != copy_from_user(&incoming, (void __user *)arg, sizeof(int)))
return -EFAULT;
JOT(8, "........... %i=incoming\n", incoming);
incoming = audio_bytes_per_fragment;
if (0 != copy_to_user((void __user *)arg, &incoming, sizeof(int)))
return -EFAULT;
break;
}
case SNDCTL_DSP_GETISPACE: {
struct audio_buf_info audio_buf_info;
JOT(8, "SNDCTL_DSP_GETISPACE\n");
audio_buf_info.bytes = audio_bytes_per_fragment;
audio_buf_info.fragments = 1;
audio_buf_info.fragsize = 0;
audio_buf_info.fragstotal = 0;
if (0 != copy_to_user((void __user *)arg, &audio_buf_info, \
sizeof(int)))
return -EFAULT;
break;
}
default: {
JOT(8, "ERROR: unrecognized DSP IOCTL command: 0x%08X\n", cmd);
POUT;
return -ENOIOCTLCMD;
}
}
return 0;
}
/*****************************************************************************/
int explain_ioctl(__u32 wot)
{
int k;
/*---------------------------------------------------------------------------*/
/*
* THE DATA FOR THE ARRAY mess BELOW WERE CONSTRUCTED BY RUNNING THE FOLLOWING
* SHELL SCRIPT:
* #
* cat /usr/src/linux-headers-`uname -r`/include/linux/videodev2.h | \
* grep "^#define VIDIOC_" - | grep -v "_OLD" - | \
* sed -e "s,_IO.*$,,;p" | sed -e "N;s,\n,, " | \
* sed -e "s/^#define / {/;s/#define /, \"/;s/$/\"},/" | \
* sed -e "s, ,,g;s, ,,g" >ioctl.tmp
* echo "{0xFFFFFFFF,\"\"}" >>ioctl.tmp
* exit 0
* #
* AND REINSTATING THE EXCISED "_OLD" CASES WERE LATER MANUALLY.
*
* THE DATA FOR THE ARRAY mess1 BELOW WERE CONSTRUCTED BY RUNNING THE FOLLOWING
* SHELL SCRIPT:
* cat /usr/src/linux-headers-`uname -r`/include/linux/videodev.h | \
* grep "^#define VIDIOC" - | grep -v "_OLD" - | \
* sed -e "s,_IO.*$,,;p" | sed -e "N;s,\n,, " | \
* sed -e "s/^#define / {/;s/#define /, \"/;s/$/\"},/" | \
* sed -e "s, ,,g;s, ,,g" >ioctl.tmp
* echo "{0xFFFFFFFF,\"\"}" >>ioctl.tmp
* exit 0
* #
*/
/*---------------------------------------------------------------------------*/
static struct mess {
__u32 command;
char name[64];
} mess[] = {
#if defined(VIDIOC_QUERYCAP)
{VIDIOC_QUERYCAP, "VIDIOC_QUERYCAP"},
#endif
#if defined(VIDIOC_RESERVED)
{VIDIOC_RESERVED, "VIDIOC_RESERVED"},
#endif
#if defined(VIDIOC_ENUM_FMT)
{VIDIOC_ENUM_FMT, "VIDIOC_ENUM_FMT"},
#endif
#if defined(VIDIOC_G_FMT)
{VIDIOC_G_FMT, "VIDIOC_G_FMT"},
#endif
#if defined(VIDIOC_S_FMT)
{VIDIOC_S_FMT, "VIDIOC_S_FMT"},
#endif
#if defined(VIDIOC_REQBUFS)
{VIDIOC_REQBUFS, "VIDIOC_REQBUFS"},
#endif
#if defined(VIDIOC_QUERYBUF)
{VIDIOC_QUERYBUF, "VIDIOC_QUERYBUF"},
#endif
#if defined(VIDIOC_G_FBUF)
{VIDIOC_G_FBUF, "VIDIOC_G_FBUF"},
#endif
#if defined(VIDIOC_S_FBUF)
{VIDIOC_S_FBUF, "VIDIOC_S_FBUF"},
#endif
#if defined(VIDIOC_OVERLAY)
{VIDIOC_OVERLAY, "VIDIOC_OVERLAY"},
#endif
#if defined(VIDIOC_QBUF)
{VIDIOC_QBUF, "VIDIOC_QBUF"},
#endif
#if defined(VIDIOC_DQBUF)
{VIDIOC_DQBUF, "VIDIOC_DQBUF"},
#endif
#if defined(VIDIOC_STREAMON)
{VIDIOC_STREAMON, "VIDIOC_STREAMON"},
#endif
#if defined(VIDIOC_STREAMOFF)
{VIDIOC_STREAMOFF, "VIDIOC_STREAMOFF"},
#endif
#if defined(VIDIOC_G_PARM)
{VIDIOC_G_PARM, "VIDIOC_G_PARM"},
#endif
#if defined(VIDIOC_S_PARM)
{VIDIOC_S_PARM, "VIDIOC_S_PARM"},
#endif
#if defined(VIDIOC_G_STD)
{VIDIOC_G_STD, "VIDIOC_G_STD"},
#endif
#if defined(VIDIOC_S_STD)
{VIDIOC_S_STD, "VIDIOC_S_STD"},
#endif
#if defined(VIDIOC_ENUMSTD)
{VIDIOC_ENUMSTD, "VIDIOC_ENUMSTD"},
#endif
#if defined(VIDIOC_ENUMINPUT)
{VIDIOC_ENUMINPUT, "VIDIOC_ENUMINPUT"},
#endif
#if defined(VIDIOC_G_CTRL)
{VIDIOC_G_CTRL, "VIDIOC_G_CTRL"},
#endif
#if defined(VIDIOC_S_CTRL)
{VIDIOC_S_CTRL, "VIDIOC_S_CTRL"},
#endif
#if defined(VIDIOC_G_TUNER)
{VIDIOC_G_TUNER, "VIDIOC_G_TUNER"},
#endif
#if defined(VIDIOC_S_TUNER)
{VIDIOC_S_TUNER, "VIDIOC_S_TUNER"},
#endif
#if defined(VIDIOC_G_AUDIO)
{VIDIOC_G_AUDIO, "VIDIOC_G_AUDIO"},
#endif
#if defined(VIDIOC_S_AUDIO)
{VIDIOC_S_AUDIO, "VIDIOC_S_AUDIO"},
#endif
#if defined(VIDIOC_QUERYCTRL)
{VIDIOC_QUERYCTRL, "VIDIOC_QUERYCTRL"},
#endif
#if defined(VIDIOC_QUERYMENU)
{VIDIOC_QUERYMENU, "VIDIOC_QUERYMENU"},
#endif
#if defined(VIDIOC_G_INPUT)
{VIDIOC_G_INPUT, "VIDIOC_G_INPUT"},
#endif
#if defined(VIDIOC_S_INPUT)
{VIDIOC_S_INPUT, "VIDIOC_S_INPUT"},
#endif
#if defined(VIDIOC_G_OUTPUT)
{VIDIOC_G_OUTPUT, "VIDIOC_G_OUTPUT"},
#endif
#if defined(VIDIOC_S_OUTPUT)
{VIDIOC_S_OUTPUT, "VIDIOC_S_OUTPUT"},
#endif
#if defined(VIDIOC_ENUMOUTPUT)
{VIDIOC_ENUMOUTPUT, "VIDIOC_ENUMOUTPUT"},
#endif
#if defined(VIDIOC_G_AUDOUT)
{VIDIOC_G_AUDOUT, "VIDIOC_G_AUDOUT"},
#endif
#if defined(VIDIOC_S_AUDOUT)
{VIDIOC_S_AUDOUT, "VIDIOC_S_AUDOUT"},
#endif
#if defined(VIDIOC_G_MODULATOR)
{VIDIOC_G_MODULATOR, "VIDIOC_G_MODULATOR"},
#endif
#if defined(VIDIOC_S_MODULATOR)
{VIDIOC_S_MODULATOR, "VIDIOC_S_MODULATOR"},
#endif
#if defined(VIDIOC_G_FREQUENCY)
{VIDIOC_G_FREQUENCY, "VIDIOC_G_FREQUENCY"},
#endif
#if defined(VIDIOC_S_FREQUENCY)
{VIDIOC_S_FREQUENCY, "VIDIOC_S_FREQUENCY"},
#endif
#if defined(VIDIOC_CROPCAP)
{VIDIOC_CROPCAP, "VIDIOC_CROPCAP"},
#endif
#if defined(VIDIOC_G_CROP)
{VIDIOC_G_CROP, "VIDIOC_G_CROP"},
#endif
#if defined(VIDIOC_S_CROP)
{VIDIOC_S_CROP, "VIDIOC_S_CROP"},
#endif
#if defined(VIDIOC_G_JPEGCOMP)
{VIDIOC_G_JPEGCOMP, "VIDIOC_G_JPEGCOMP"},
#endif
#if defined(VIDIOC_S_JPEGCOMP)
{VIDIOC_S_JPEGCOMP, "VIDIOC_S_JPEGCOMP"},
#endif
#if defined(VIDIOC_QUERYSTD)
{VIDIOC_QUERYSTD, "VIDIOC_QUERYSTD"},
#endif
#if defined(VIDIOC_TRY_FMT)
{VIDIOC_TRY_FMT, "VIDIOC_TRY_FMT"},
#endif
#if defined(VIDIOC_ENUMAUDIO)
{VIDIOC_ENUMAUDIO, "VIDIOC_ENUMAUDIO"},
#endif
#if defined(VIDIOC_ENUMAUDOUT)
{VIDIOC_ENUMAUDOUT, "VIDIOC_ENUMAUDOUT"},
#endif
#if defined(VIDIOC_G_PRIORITY)
{VIDIOC_G_PRIORITY, "VIDIOC_G_PRIORITY"},
#endif
#if defined(VIDIOC_S_PRIORITY)
{VIDIOC_S_PRIORITY, "VIDIOC_S_PRIORITY"},
#endif
#if defined(VIDIOC_G_SLICED_VBI_CAP)
{VIDIOC_G_SLICED_VBI_CAP, "VIDIOC_G_SLICED_VBI_CAP"},
#endif
#if defined(VIDIOC_LOG_STATUS)
{VIDIOC_LOG_STATUS, "VIDIOC_LOG_STATUS"},
#endif
#if defined(VIDIOC_G_EXT_CTRLS)
{VIDIOC_G_EXT_CTRLS, "VIDIOC_G_EXT_CTRLS"},
#endif
#if defined(VIDIOC_S_EXT_CTRLS)
{VIDIOC_S_EXT_CTRLS, "VIDIOC_S_EXT_CTRLS"},
#endif
#if defined(VIDIOC_TRY_EXT_CTRLS)
{VIDIOC_TRY_EXT_CTRLS, "VIDIOC_TRY_EXT_CTRLS"},
#endif
#if defined(VIDIOC_ENUM_FRAMESIZES)
{VIDIOC_ENUM_FRAMESIZES, "VIDIOC_ENUM_FRAMESIZES"},
#endif
#if defined(VIDIOC_ENUM_FRAMEINTERVALS)
{VIDIOC_ENUM_FRAMEINTERVALS, "VIDIOC_ENUM_FRAMEINTERVALS"},
#endif
#if defined(VIDIOC_G_ENC_INDEX)
{VIDIOC_G_ENC_INDEX, "VIDIOC_G_ENC_INDEX"},
#endif
#if defined(VIDIOC_ENCODER_CMD)
{VIDIOC_ENCODER_CMD, "VIDIOC_ENCODER_CMD"},
#endif
#if defined(VIDIOC_TRY_ENCODER_CMD)
{VIDIOC_TRY_ENCODER_CMD, "VIDIOC_TRY_ENCODER_CMD"},
#endif
#if defined(VIDIOC_G_CHIP_IDENT)
{VIDIOC_G_CHIP_IDENT, "VIDIOC_G_CHIP_IDENT"},
#endif
#if defined(VIDIOC_OVERLAY_OLD)
{VIDIOC_OVERLAY_OLD, "VIDIOC_OVERLAY_OLD"},
#endif
#if defined(VIDIOC_S_PARM_OLD)
{VIDIOC_S_PARM_OLD, "VIDIOC_S_PARM_OLD"},
#endif
#if defined(VIDIOC_S_CTRL_OLD)
{VIDIOC_S_CTRL_OLD, "VIDIOC_S_CTRL_OLD"},
#endif
#if defined(VIDIOC_G_AUDIO_OLD)
{VIDIOC_G_AUDIO_OLD, "VIDIOC_G_AUDIO_OLD"},
#endif
#if defined(VIDIOC_G_AUDOUT_OLD)
{VIDIOC_G_AUDOUT_OLD, "VIDIOC_G_AUDOUT_OLD"},
#endif
#if defined(VIDIOC_CROPCAP_OLD)
{VIDIOC_CROPCAP_OLD, "VIDIOC_CROPCAP_OLD"},
#endif
{0xFFFFFFFF, ""}
};
static struct mess mess1[] = \
{
#if defined(VIDIOCGCAP)
{VIDIOCGCAP, "VIDIOCGCAP"},
#endif
#if defined(VIDIOCGCHAN)
{VIDIOCGCHAN, "VIDIOCGCHAN"},
#endif
#if defined(VIDIOCSCHAN)
{VIDIOCSCHAN, "VIDIOCSCHAN"},
#endif
#if defined(VIDIOCGTUNER)
{VIDIOCGTUNER, "VIDIOCGTUNER"},
#endif
#if defined(VIDIOCSTUNER)
{VIDIOCSTUNER, "VIDIOCSTUNER"},
#endif
#if defined(VIDIOCGPICT)
{VIDIOCGPICT, "VIDIOCGPICT"},
#endif
#if defined(VIDIOCSPICT)
{VIDIOCSPICT, "VIDIOCSPICT"},
#endif
#if defined(VIDIOCCAPTURE)
{VIDIOCCAPTURE, "VIDIOCCAPTURE"},
#endif
#if defined(VIDIOCGWIN)
{VIDIOCGWIN, "VIDIOCGWIN"},
#endif
#if defined(VIDIOCSWIN)
{VIDIOCSWIN, "VIDIOCSWIN"},
#endif
#if defined(VIDIOCGFBUF)
{VIDIOCGFBUF, "VIDIOCGFBUF"},
#endif
#if defined(VIDIOCSFBUF)
{VIDIOCSFBUF, "VIDIOCSFBUF"},
#endif
#if defined(VIDIOCKEY)
{VIDIOCKEY, "VIDIOCKEY"},
#endif
#if defined(VIDIOCGFREQ)
{VIDIOCGFREQ, "VIDIOCGFREQ"},
#endif
#if defined(VIDIOCSFREQ)
{VIDIOCSFREQ, "VIDIOCSFREQ"},
#endif
#if defined(VIDIOCGAUDIO)
{VIDIOCGAUDIO, "VIDIOCGAUDIO"},
#endif
#if defined(VIDIOCSAUDIO)
{VIDIOCSAUDIO, "VIDIOCSAUDIO"},
#endif
#if defined(VIDIOCSYNC)
{VIDIOCSYNC, "VIDIOCSYNC"},
#endif
#if defined(VIDIOCMCAPTURE)
{VIDIOCMCAPTURE, "VIDIOCMCAPTURE"},
#endif
#if defined(VIDIOCGMBUF)
{VIDIOCGMBUF, "VIDIOCGMBUF"},
#endif
#if defined(VIDIOCGUNIT)
{VIDIOCGUNIT, "VIDIOCGUNIT"},
#endif
#if defined(VIDIOCGCAPTURE)
{VIDIOCGCAPTURE, "VIDIOCGCAPTURE"},
#endif
#if defined(VIDIOCSCAPTURE)
{VIDIOCSCAPTURE, "VIDIOCSCAPTURE"},
#endif
#if defined(VIDIOCSPLAYMODE)
{VIDIOCSPLAYMODE, "VIDIOCSPLAYMODE"},
#endif
#if defined(VIDIOCSWRITEMODE)
{VIDIOCSWRITEMODE, "VIDIOCSWRITEMODE"},
#endif
#if defined(VIDIOCGPLAYINFO)
{VIDIOCGPLAYINFO, "VIDIOCGPLAYINFO"},
#endif
#if defined(VIDIOCSMICROCODE)
{VIDIOCSMICROCODE, "VIDIOCSMICROCODE"},
#endif
{0xFFFFFFFF, ""}
};
k = 0;
while (mess[k].name[0]) {
if (wot == mess[k].command) {
JOT(8, "ioctl 0x%08X is %s\n", \
mess[k].command, &mess[k].name[0]);
return 0;
}
k++;
}
JOT(8, "ioctl 0x%08X is not in videodev2.h\n", wot);
k = 0;
while (mess1[k].name[0]) {
if (wot == mess1[k].command) {
JOT(8, "ioctl 0x%08X is %s (V4L1)\n", \
mess1[k].command, &mess1[k].name[0]);
return 0;
}
k++;
}
JOT(8, "ioctl 0x%08X is not in videodev.h\n", wot);
return -1;
}
/*****************************************************************************/
int explain_cid(__u32 wot)
{
int k;
/*---------------------------------------------------------------------------*/
/*
* THE DATA FOR THE ARRAY mess BELOW WERE CONSTRUCTED BY RUNNING THE FOLLOWING
* SHELL SCRIPT:
* #
* cat /usr/src/linux-headers-`uname -r`/include/linux/videodev2.h | \
* grep "^#define V4L2_CID_" | \
* sed -e "s,(.*$,,;p" | sed -e "N;s,\n,, " | \
* sed -e "s/^#define / {/;s/#define /, \"/;s/$/\"},/" | \
* sed -e "s, ,,g;s, ,,g" | grep -v "_BASE" | grep -v "MPEG" >cid.tmp
* echo "{0xFFFFFFFF,\"\"}" >>cid.tmp
* exit 0
* #
*/
/*---------------------------------------------------------------------------*/
static struct mess
{
__u32 command;
char name[64];
} mess[] = {
#if defined(V4L2_CID_USER_CLASS)
{V4L2_CID_USER_CLASS, "V4L2_CID_USER_CLASS"},
#endif
#if defined(V4L2_CID_BRIGHTNESS)
{V4L2_CID_BRIGHTNESS, "V4L2_CID_BRIGHTNESS"},
#endif
#if defined(V4L2_CID_CONTRAST)
{V4L2_CID_CONTRAST, "V4L2_CID_CONTRAST"},
#endif
#if defined(V4L2_CID_SATURATION)
{V4L2_CID_SATURATION, "V4L2_CID_SATURATION"},
#endif
#if defined(V4L2_CID_HUE)
{V4L2_CID_HUE, "V4L2_CID_HUE"},
#endif
#if defined(V4L2_CID_AUDIO_VOLUME)
{V4L2_CID_AUDIO_VOLUME, "V4L2_CID_AUDIO_VOLUME"},
#endif
#if defined(V4L2_CID_AUDIO_BALANCE)
{V4L2_CID_AUDIO_BALANCE, "V4L2_CID_AUDIO_BALANCE"},
#endif
#if defined(V4L2_CID_AUDIO_BASS)
{V4L2_CID_AUDIO_BASS, "V4L2_CID_AUDIO_BASS"},
#endif
#if defined(V4L2_CID_AUDIO_TREBLE)
{V4L2_CID_AUDIO_TREBLE, "V4L2_CID_AUDIO_TREBLE"},
#endif
#if defined(V4L2_CID_AUDIO_MUTE)
{V4L2_CID_AUDIO_MUTE, "V4L2_CID_AUDIO_MUTE"},
#endif
#if defined(V4L2_CID_AUDIO_LOUDNESS)
{V4L2_CID_AUDIO_LOUDNESS, "V4L2_CID_AUDIO_LOUDNESS"},
#endif
#if defined(V4L2_CID_BLACK_LEVEL)
{V4L2_CID_BLACK_LEVEL, "V4L2_CID_BLACK_LEVEL"},
#endif
#if defined(V4L2_CID_AUTO_WHITE_BALANCE)
{V4L2_CID_AUTO_WHITE_BALANCE, "V4L2_CID_AUTO_WHITE_BALANCE"},
#endif
#if defined(V4L2_CID_DO_WHITE_BALANCE)
{V4L2_CID_DO_WHITE_BALANCE, "V4L2_CID_DO_WHITE_BALANCE"},
#endif
#if defined(V4L2_CID_RED_BALANCE)
{V4L2_CID_RED_BALANCE, "V4L2_CID_RED_BALANCE"},
#endif
#if defined(V4L2_CID_BLUE_BALANCE)
{V4L2_CID_BLUE_BALANCE, "V4L2_CID_BLUE_BALANCE"},
#endif
#if defined(V4L2_CID_GAMMA)
{V4L2_CID_GAMMA, "V4L2_CID_GAMMA"},
#endif
#if defined(V4L2_CID_WHITENESS)
{V4L2_CID_WHITENESS, "V4L2_CID_WHITENESS"},
#endif
#if defined(V4L2_CID_EXPOSURE)
{V4L2_CID_EXPOSURE, "V4L2_CID_EXPOSURE"},
#endif
#if defined(V4L2_CID_AUTOGAIN)
{V4L2_CID_AUTOGAIN, "V4L2_CID_AUTOGAIN"},
#endif
#if defined(V4L2_CID_GAIN)
{V4L2_CID_GAIN, "V4L2_CID_GAIN"},
#endif
#if defined(V4L2_CID_HFLIP)
{V4L2_CID_HFLIP, "V4L2_CID_HFLIP"},
#endif
#if defined(V4L2_CID_VFLIP)
{V4L2_CID_VFLIP, "V4L2_CID_VFLIP"},
#endif
#if defined(V4L2_CID_HCENTER)
{V4L2_CID_HCENTER, "V4L2_CID_HCENTER"},
#endif
#if defined(V4L2_CID_VCENTER)
{V4L2_CID_VCENTER, "V4L2_CID_VCENTER"},
#endif
#if defined(V4L2_CID_POWER_LINE_FREQUENCY)
{V4L2_CID_POWER_LINE_FREQUENCY, "V4L2_CID_POWER_LINE_FREQUENCY"},
#endif
#if defined(V4L2_CID_HUE_AUTO)
{V4L2_CID_HUE_AUTO, "V4L2_CID_HUE_AUTO"},
#endif
#if defined(V4L2_CID_WHITE_BALANCE_TEMPERATURE)
{V4L2_CID_WHITE_BALANCE_TEMPERATURE, "V4L2_CID_WHITE_BALANCE_TEMPERATURE"},
#endif
#if defined(V4L2_CID_SHARPNESS)
{V4L2_CID_SHARPNESS, "V4L2_CID_SHARPNESS"},
#endif
#if defined(V4L2_CID_BACKLIGHT_COMPENSATION)
{V4L2_CID_BACKLIGHT_COMPENSATION, "V4L2_CID_BACKLIGHT_COMPENSATION"},
#endif
#if defined(V4L2_CID_CHROMA_AGC)
{V4L2_CID_CHROMA_AGC, "V4L2_CID_CHROMA_AGC"},
#endif
#if defined(V4L2_CID_COLOR_KILLER)
{V4L2_CID_COLOR_KILLER, "V4L2_CID_COLOR_KILLER"},
#endif
#if defined(V4L2_CID_LASTP1)
{V4L2_CID_LASTP1, "V4L2_CID_LASTP1"},
#endif
#if defined(V4L2_CID_CAMERA_CLASS)
{V4L2_CID_CAMERA_CLASS, "V4L2_CID_CAMERA_CLASS"},
#endif
#if defined(V4L2_CID_EXPOSURE_AUTO)
{V4L2_CID_EXPOSURE_AUTO, "V4L2_CID_EXPOSURE_AUTO"},
#endif
#if defined(V4L2_CID_EXPOSURE_ABSOLUTE)
{V4L2_CID_EXPOSURE_ABSOLUTE, "V4L2_CID_EXPOSURE_ABSOLUTE"},
#endif
#if defined(V4L2_CID_EXPOSURE_AUTO_PRIORITY)
{V4L2_CID_EXPOSURE_AUTO_PRIORITY, "V4L2_CID_EXPOSURE_AUTO_PRIORITY"},
#endif
#if defined(V4L2_CID_PAN_RELATIVE)
{V4L2_CID_PAN_RELATIVE, "V4L2_CID_PAN_RELATIVE"},
#endif
#if defined(V4L2_CID_TILT_RELATIVE)
{V4L2_CID_TILT_RELATIVE, "V4L2_CID_TILT_RELATIVE"},
#endif
#if defined(V4L2_CID_PAN_RESET)
{V4L2_CID_PAN_RESET, "V4L2_CID_PAN_RESET"},
#endif
#if defined(V4L2_CID_TILT_RESET)
{V4L2_CID_TILT_RESET, "V4L2_CID_TILT_RESET"},
#endif
#if defined(V4L2_CID_PAN_ABSOLUTE)
{V4L2_CID_PAN_ABSOLUTE, "V4L2_CID_PAN_ABSOLUTE"},
#endif
#if defined(V4L2_CID_TILT_ABSOLUTE)
{V4L2_CID_TILT_ABSOLUTE, "V4L2_CID_TILT_ABSOLUTE"},
#endif
#if defined(V4L2_CID_FOCUS_ABSOLUTE)
{V4L2_CID_FOCUS_ABSOLUTE, "V4L2_CID_FOCUS_ABSOLUTE"},
#endif
#if defined(V4L2_CID_FOCUS_RELATIVE)
{V4L2_CID_FOCUS_RELATIVE, "V4L2_CID_FOCUS_RELATIVE"},
#endif
#if defined(V4L2_CID_FOCUS_AUTO)
{V4L2_CID_FOCUS_AUTO, "V4L2_CID_FOCUS_AUTO"},
#endif
{0xFFFFFFFF, ""}
};
k = 0;
while (mess[k].name[0]) {
if (wot == mess[k].command) {
JOT(8, "ioctl 0x%08X is %s\n", \
mess[k].command, &mess[k].name[0]);
return 0;
}
k++;
}
JOT(8, "cid 0x%08X is not in videodev2.h\n", wot);
return -1;
}
/*****************************************************************************/
/*****************************************************************************
* *
* easycap_ioctl.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
*
*/
/*****************************************************************************/
extern struct easycap_format easycap_format[];
extern struct v4l2_queryctrl easycap_control[];
extern unsigned int audio_bytes_per_fragment;
/*****************************************************************************
* *
* *
* 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_debug.h"
#include "easycap.h"
/*--------------------------------------------------------------------------*/
const struct stk1160config { int reg; int set; } stk1160config[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},
#if defined(PREFER_NTSC)
#undef OLDMARGIN
#if defined(OLDMARGIN)
{0x110, 0x0008},
#else
{0x110, 0x0014},
#endif /*OLDMARGIN*/
{0x111, 0x0000},
{0x112, 0x0003},
{0x113, 0x0000},
#if defined(OLDMARGIN)
{0x114, 0x0508},
#else
{0x114, 0x0514},
#endif /*OLDMARGIN*/
{0x115, 0x0005},
{0x116, 0x00F3},
{0x117, 0x0000},
#else /* ! PREFER_NTSC*/
#if defined(OLDMARGIN)
{0x110, 0x0008},
#else
{0x110, 0x0014},
#endif /*OLDMARGIN*/
{0x111, 0x0000},
{0x112, 0x0020},
{0x113, 0x0000},
#if defined(OLDMARGIN)
{0x114, 0x0508},
#else
{0x114, 0x0514},
#endif /*OLDMARGIN*/
{0x115, 0x0005},
{0x116, 0x0110},
{0x117, 0x0001},
#endif /* ! PREFER_NTSC*/
{0x202, 0x000F},
{0x203, 0x004A},
{0x2FF, 0x0000},
/*---------------------------------------------------------------------------*/
{0xFFF, 0xFFFF}
};
/*--------------------------------------------------------------------------*/
const struct saa7113config { int reg; int set; } saa7113config[256] = {
{0x01, 0x08},
{0x02, 0x80},
{0x03, 0x33},
{0x04, 0x00},
{0x05, 0x00},
{0x06, 0xE9},
{0x07, 0x0D},
#if defined(PREFER_NTSC)
{0x08, 0x78},
#else
{0x08, 0x38},
#endif /* ! PREFER_NTSC*/
{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},
#if defined(PREFER_NTSC)
{0x40, 0x82},
#else
{0x40, 0x02},
#endif /* ! PREFER_NTSC*/
{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},
#if defined(PREFER_NTSC)
{0x5A, 0x0A},
#else
{0x5A, 0x07},
#endif /* ! PREFER_NTSC*/
{0x5B, 0x83},
{0xFF, 0xFF}
};
/*--------------------------------------------------------------------------*/
/****************************************************************************/
int
confirm_resolution(struct usb_device *p)
{
__u8 get0, get1, get2, get3, get4, get5, get6, get7;
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;
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)
{
int i0;
i0 = 0;
while (0xFFF != stk1160config[i0].reg) {
SET(p, stk1160config[i0].reg, stk1160config[i0].set);
i0++;
}
write_300(p);
return 0;
}
/****************************************************************************/
int
setup_saa(struct usb_device *p)
{
int i0, ir;
set2to78(p);
i0 = 0;
while (0xFF != saa7113config[i0].reg) {
ir = write_saa(p, saa7113config[i0].reg, saa7113config[i0].set);
i0++;
}
return 0;
}
/****************************************************************************/
int
write_000(struct usb_device *p, __u16 set2, __u16 set0)
{
__u8 igot0, igot2;
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)
{
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;
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;
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)
{
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)
{
int i0, ir, rc;
i0 = 0;
rc = 0;
while (0xFF != saa7113config[i0].reg) {
if (0x0F == saa7113config[i0].reg) {
i0++; continue;
}
ir = read_saa(p, saa7113config[i0].reg);
if (ir != saa7113config[i0].set) {
SAY("SAA register 0x%02X has 0x%02X, expected 0x%02X\n", \
saa7113config[i0].reg, ir, saa7113config[i0].set);
rc--;
}
i0++;
}
if (-8 > rc)
return rc;
else
return 0;
}
/****************************************************************************/
int
merit_saa(struct usb_device *p)
{
int rc;
rc = read_saa(p, 0x1F);
if ((0 > rc) || (0x02 & rc))
return 1 ;
else
return 0;
}
/****************************************************************************/
int
ready_saa(struct usb_device *p)
{
int j, rc;
static int max = 10;
j = 0;
while (max > j) {
rc = read_saa(p, 0x1F);
if (0 <= rc) {
if ((1 == (0x01 & rc))&&(0 == (0x40 & rc)))
break;
}
msleep(100); j++;
}
if (max == j)
return -1;
else {
if (0x20 & rc)
JOT(8, "hardware detects 60 Hz\n");
else
JOT(8, "hardware detects 50 Hz\n");
if (0x80 & rc)
JOT(8, "hardware detects interlacing\n");
else
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)
{
int i0, ir;
i0 = 0;
while (0xFFF != stk1160config[i0].reg) {
if (0x000 == stk1160config[i0].reg) {
i0++; continue;
}
if (0x002 == stk1160config[i0].reg) {
i0++; continue;
}
ir = read_stk(p, stk1160config[i0].reg);
if (0x100 == stk1160config[i0].reg) {
if ((ir != (0xFF & stk1160config[i0].set)) && \
(ir != (0x80 | (0xFF & stk1160config[i0].set))) && \
(0xFFFF != stk1160config[i0].set)) {
SAY("STK register 0x%03X has 0x%02X, " \
"expected 0x%02X\n", \
stk1160config[i0].reg, ir, \
stk1160config[i0].set);
}
i0++; continue;
}
if ((ir != (0xFF & stk1160config[i0].set)) && \
(0xFFFF != stk1160config[i0].set)) {
SAY("STK register 0x%03X has 0x%02X, " \
"expected 0x%02X\n", \
stk1160config[i0].reg, ir, \
stk1160config[i0].set);
}
i0++;
}
return 0;
}
/****************************************************************************/
int
read_saa(struct usb_device *p, __u16 reg0)
{
__u8 igot;
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;
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)
{
stop_100(p);
msleep(20);
switch (input) {
case 0:
case 1: {
SET(p, 0x0000, 0x0098); break;
}
case 2: {
SET(p, 0x0000, 0x0090); break;
}
case 3: {
SET(p, 0x0000, 0x0088); break;
}
case 4: {
SET(p, 0x0000, 0x0080); 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);
}
break;
}
default:
{
SAY("ERROR: bad input: %i\n", input);
return -1;
}
}
msleep(20);
SET(p, 0x0002, 0x0093);
msleep(20);
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;
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 get0;
__u8 igot;
GET(p, 0x0100, &igot); get0 = igot;
msleep(0x1f4);
SET(p, 0x0100, (0x80 | get0));
msleep(0x1f4);
return 0;
}
/****************************************************************************/
int
stop_100(struct usb_device *p)
{
__u16 get0;
__u8 igot;
GET(p, 0x0100, &igot); get0 = igot;
msleep(0x1f4);
SET(p, 0x0100, (0x7F & get0));
msleep(0x1f4);
return 0;
}
/****************************************************************************/
/*--------------------------------------------------------------------------*/
/*
* FUNCTION wait_i2c() RETURNS 0 ON SUCCESS
*/
/*--------------------------------------------------------------------------*/
int
wait_i2c(struct usb_device *p)
{
__u16 get0;
__u8 igot;
const int max = 4;
int k;
for (k = 0; k < max; k++) {
GET(p, 0x0201, &igot); get0 = igot;
switch (get0) {
case 0x04:
case 0x01: {
return 0;
}
case 0x00: {
msleep(10);
continue;
}
default: {
return get0 - 1;
}
}
}
return -1;
}
/****************************************************************************/
int
regset(struct usb_device *pusb_device, __u16 index, __u16 value)
{
__u16 igot;
int rc0, rc1;
if (!pusb_device)
return -EFAULT;
rc1 = 0; igot = 0;
rc0 = usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \
(__u8)0x01, \
(__u8)(USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE), \
(__u16)value, \
(__u16)index, \
(void *)NULL, \
(__u16)0, \
(int)500);
#if defined(NOREADBACK)
#
#else
rc1 = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0), \
(__u8)0x00, \
(__u8)(USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE), \
(__u16)0x00, \
(__u16)index, \
(void *)&igot, \
(__u16)sizeof(__u16), \
(int)50000);
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 (0 != igot) {
JOT(8, "unexpected 0x%02X for STK register 0x%03X\n", \
igot, index);
}
break;
}
case 0x114:
case 0x116: {
if ((0xFF & value) != igot) {
JOT(8, "unexpected 0x%02X != 0x%02X " \
"for STK register 0x%03X\n", \
igot, value, index);
}
break;
}
case 0x200: {
if (0 == igot)
break;
}
default: {
if (value != igot) {
JOT(8, "unexpected 0x%02X != 0x%02X " \
"for STK register 0x%03X\n", \
igot, value, index);
}
break;
}
}
#endif /* ! NOREADBACK*/
return (0 > rc0) ? rc0 : rc1;
}
/*****************************************************************************/
int
regget(struct usb_device *pusb_device, __u16 index, void *pvoid)
{
int ir;
if (!pusb_device)
return -EFAULT;
ir = usb_control_msg(pusb_device, usb_rcvctrlpipe(pusb_device, 0), \
(__u8)0x00, \
(__u8)(USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE), \
(__u16)0x00, \
(__u16)index, \
(void *)pvoid, \
sizeof(__u8), \
(int)50000);
return 0xFF & ir;
}
/*****************************************************************************/
int
wakeup_device(struct usb_device *pusb_device)
{
return usb_control_msg(pusb_device, usb_sndctrlpipe(pusb_device, 0), \
(__u8)USB_REQ_SET_FEATURE, \
(__u8)(USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE), \
USB_DEVICE_REMOTE_WAKEUP, \
(__u16)0, \
(void *) NULL, \
(__u16)0, \
(int)50000);
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* 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.
*/
/*---------------------------------------------------------------------------*/
int
audio_setup(struct easycap *peasycap)
{
struct usb_device *pusb_device;
static __u8 request = 0x01;
static __u8 requesttype = \
(__u8)(USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE);
static __u16 value_unmute = 0x0200;
static __u16 index = 0x0301;
static unsigned char buffer[1];
static __u16 length = 1;
int rc;
if (NULL == peasycap)
return -EFAULT;
pusb_device = peasycap->pusb_device;
if (NULL == pusb_device)
return -EFAULT;
JOT(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), \
(__u8)request, \
(__u8)requesttype, \
(__u16)value_unmute, \
(__u16)index, \
(void *)&buffer[0], \
(__u16)length, \
(int)50000);
JOT(8, "0x%02X=buffer\n", *((__u8 *) &buffer[0]));
if (rc != (int)length)
SAY("ERROR: usb_control_msg returned %i\n", rc);
/*--------------------------------------------------------------------------*/
/*
* 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);
if (false == peasycap->microphone) {
/*-------------------------------------------------------------------*/
/*
* SELECT AUDIO SOURCE "LINE IN" AND SET DEFAULT GAIN TO 0dB.
*/
/*-------------------------------------------------------------------*/
write_vt(pusb_device, 0x0002, 0x8000);
write_vt(pusb_device, 0x001C, 0x8000);
write_vt(pusb_device, 0x000E, 0x0000);
write_vt(pusb_device, 0x0010, 0x0000);
write_vt(pusb_device, 0x0012, 0x8000);
write_vt(pusb_device, 0x0016, 0x0000);
write_vt(pusb_device, 0x001A, 0x0404);
write_vt(pusb_device, 0x0002, 0x0000);
write_vt(pusb_device, 0x001C, 0x0000);
} else {
/*-------------------------------------------------------------------*/
/*
* SELECT AUDIO SOURCE "MIC" AND SET DEFAULT GAIN TO 0 dB.
*
* REGISTER 0x000E CAN BE SET TO PROVIDE UP TO 34.5 dB ATTENTUATION,
* BUT THIS HAS NOT PROVED NECESSARY FOR THE FEW SIGNAL SOURCES
* TESTED HITHERTO.
*/
/*-------------------------------------------------------------------*/
write_vt(pusb_device, 0x0006, 0x8000);
write_vt(pusb_device, 0x001C, 0x8000);
write_vt(pusb_device, 0x000E, 0x0008);
write_vt(pusb_device, 0x0010, 0x0000);
write_vt(pusb_device, 0x0012, 0x8000);
write_vt(pusb_device, 0x0016, 0x0000);
write_vt(pusb_device, 0x001A, 0x0000);
write_vt(pusb_device, 0x0006, 0x0000);
write_vt(pusb_device, 0x001C, 0x0000);
}
check_vt(pusb_device);
return 0;
}
/*****************************************************************************/
int
check_vt(struct usb_device *pusb_device)
{
int igot;
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, 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, 0x001A);
if (0 > igot)
SAY("ERROR: failed to read VT1612A register 0x1A\n");
if (0x8000 & igot)
SAY("register 0x%02X muted\n", 0x1A);
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);
*
* IF 16<loud<31 VT1621A REGISTER 0x1C IS SET FOR POSITIVE GAIN.
* IF loud<=16 VT1621A REGISTER 0x1C IS SET FOR ZERO GAIN.
* THERE IS NEVER ANY (ADDITIONAL) ATTENUATION.
*/
/*---------------------------------------------------------------------------*/
int
audio_gainset(struct usb_device *pusb_device, __s8 loud)
{
int igot;
__u8 u8;
__u16 mute;
if (16 > loud)
loud = 16;
u8 = 0x000F & (__u8)(loud - 16);
write_vt(pusb_device, 0x0002, 0x8000);
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);
JOT(8, "0x%04X=(mute|u8|(u8<<8))\n", mute | u8 | (u8 << 8));
write_vt(pusb_device, 0x001C, 0x8000);
write_vt(pusb_device, 0x001C, (mute | u8 | (u8 << 8)));
write_vt(pusb_device, 0x0002, 0x0000);
return 0;
}
/*****************************************************************************/
int
audio_gainget(struct usb_device *pusb_device)
{
int igot;
igot = read_vt(pusb_device, 0x001C);
if (0 > igot)
SAY("ERROR: failed to read VT1612A register 0x1C\n");
return igot;
}
/*****************************************************************************/
int
set2to78(struct usb_device *p)
{
int ir;
msleep(20);
ir = regset(p, 0x0002, 0x0078);
if (0 > ir)
SAY("ERROR: failed to set register 0x0002 to 0x0078\n");
msleep(20);
return ir;
}
/*****************************************************************************/
int
set2to93(struct usb_device *p)
{
int ir;
msleep(20);
ir = regset(p, 0x0002, 0x0093);
if (0 > ir)
SAY("ERROR: failed to set register 0x0002 to 0x0078\n");
msleep(20);
return ir;
}
/*****************************************************************************/
/******************************************************************************
* *
* 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 "easycap_standard.h"
int easycap_debug;
module_param(easycap_debug, int, S_IRUGO | S_IWUSR);
unsigned int audio_pages_per_fragment;
unsigned int audio_bytes_per_fragment;
unsigned int audio_buffer_page_many;
/*---------------------------------------------------------------------------*/
/*
* PARAMETERS APPLICABLE TO ENTIRE DRIVER, I.E. BOTH VIDEO AND AUDIO
*/
/*---------------------------------------------------------------------------*/
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,
};
/*---------------------------------------------------------------------------*/
/*
* 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.
*/
/*---------------------------------------------------------------------------*/
const struct file_operations easycap_fops = {
.owner = THIS_MODULE,
.open = easycap_open,
.release = easycap_release,
.ioctl = easycap_ioctl,
.poll = easycap_poll,
.mmap = easycap_mmap,
.llseek = no_llseek,
};
struct vm_operations_struct easycap_vm_ops = {
.open = easycap_vma_open,
.close = easycap_vma_close,
.fault = easycap_vma_fault,
};
struct usb_class_driver easycap_class = {
.name = "usb/easycap%d",
.fops = &easycap_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
#if defined(EASYCAP_NEEDS_V4L2_FOPS)
struct v4l2_file_operations v4l2_fops = {
.owner = THIS_MODULE,
.open = easycap_open_noinode,
.release = easycap_release_noinode,
.ioctl = easycap_ioctl_noinode,
.poll = easycap_poll,
.mmap = easycap_mmap,
};
#endif /*EASYCAP_NEEDS_V4L2_FOPS*/
int video_device_many /*=0*/;
struct video_device *pvideo_array[VIDEO_DEVICE_MANY], *pvideo_device;
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*--------------------------------------------------------------------------*/
/*
* PARAMETERS USED WHEN REGISTERING THE AUDIO INTERFACE
*/
/*--------------------------------------------------------------------------*/
const struct file_operations easysnd_fops = {
.owner = THIS_MODULE,
.open = easysnd_open,
.release = easysnd_release,
.ioctl = easysnd_ioctl,
.read = easysnd_read,
.llseek = no_llseek,
};
struct usb_class_driver easysnd_class = {
.name = "usb/easysnd%d",
.fops = &easysnd_fops,
.minor_base = USB_SKEL_MINOR_BASE,
};
/****************************************************************************/
/*--------------------------------------------------------------------------*/
/*
* IT IS NOT APPROPRIATE FOR easycap_open() TO SUBMIT THE VIDEO URBS HERE,
* BECAUSE THERE WILL ALWAYS BE SUBSEQUENT NEGOTIATION OF TV STANDARD AND
* FORMAT BY IOCTL AND IT IS INADVISABLE TO HAVE THE URBS RUNNING WHILE
* REGISTERS OF THE SA7113H ARE BEING MANIPULATED.
*
* THE SUBMISSION OF VIDEO URBS IS THEREFORE DELAYED UNTIL THE IOCTL COMMAND
* STREAMON IS RECEIVED.
*/
/*--------------------------------------------------------------------------*/
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
int
easycap_open_noinode(struct file *file)
{
return easycap_open((struct inode *)NULL, file);
}
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
int
easycap_open(struct inode *inode, struct file *file)
{
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
struct usb_interface *pusb_interface;
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
struct usb_device *p;
struct easycap *peasycap;
int i, k, m, rc;
JOT(4, "\n");
SAY("==========OPEN=========\n");
peasycap = (struct easycap *)NULL;
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
if ((struct inode *)NULL == inode) {
SAY("ERROR: inode is NULL.\n");
return -EFAULT;
}
pusb_interface = usb_find_interface(&easycap_usb_driver, iminor(inode));
if (!pusb_interface) {
SAY("ERROR: pusb_interface is NULL.\n");
return -EFAULT;
}
peasycap = usb_get_intfdata(pusb_interface);
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#else
for (i = 0; i < video_device_many; i++) {
pvideo_device = pvideo_array[i];
if ((struct video_device *)NULL != pvideo_device) {
peasycap = (struct easycap *)video_get_drvdata(pvideo_device);
break;
}
}
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
if ((struct easycap *)NULL == peasycap) {
SAY("MISTAKE: peasycap is NULL\n");
return -EFAULT;
}
file->private_data = peasycap;
/*---------------------------------------------------------------------------*/
/*
* INITIALIZATION
*/
/*---------------------------------------------------------------------------*/
JOT(4, "starting initialization\n");
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);
}
p = peasycap->pusb_device;
if ((struct usb_device *)NULL == p) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
} else {
JOT(16, "0x%08lX=peasycap->pusb_device\n", \
(long int)peasycap->pusb_device);
}
rc = wakeup_device(peasycap->pusb_device);
if (0 == rc)
JOT(8, "wakeup_device() OK\n");
else {
SAY("ERROR: wakeup_device() returned %i\n", rc);
return -EFAULT;
}
rc = setup_stk(p); peasycap->input = 0;
if (0 == rc)
JOT(8, "setup_stk() OK\n");
else {
SAY("ERROR: setup_stk() returned %i\n", rc);
return -EFAULT;
}
rc = setup_saa(p);
if (0 == rc)
JOT(8, "setup_saa() OK\n");
else {
SAY("ERROR: setup_saa() returned %i\n", rc);
return -EFAULT;
}
rc = check_saa(p);
if (0 == rc)
JOT(8, "check_saa() OK\n");
else if (-8 < rc)
SAY("check_saa() returned %i\n", rc);
else {
SAY("ERROR: check_saa() returned %i\n", rc);
return -EFAULT;
}
peasycap->standard_offset = -1;
/*---------------------------------------------------------------------------*/
#if defined(PREFER_NTSC)
rc = adjust_standard(peasycap, V4L2_STD_NTSC_M);
if (0 == rc)
JOT(8, "adjust_standard(.,NTSC_M) OK\n");
else {
SAY("ERROR: adjust_standard(.,NTSC_M) returned %i\n", rc);
return -EFAULT;
}
rc = adjust_format(peasycap, 640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, \
false);
if (0 <= rc)
JOT(8, "adjust_format(.,640,480,UYVY) OK\n");
else {
SAY("ERROR: adjust_format(.,640,480,UYVY) returned %i\n", rc);
return -EFAULT;
}
#else
rc = adjust_standard(peasycap, \
(V4L2_STD_PAL_B | V4L2_STD_PAL_G | V4L2_STD_PAL_H | \
V4L2_STD_PAL_I | V4L2_STD_PAL_N));
if (0 == rc)
JOT(8, "adjust_standard(.,PAL_BGHIN) OK\n");
else {
SAY("ERROR: adjust_standard(.,PAL_BGHIN) returned %i\n", rc);
return -EFAULT;
}
rc = adjust_format(peasycap, 640, 480, V4L2_PIX_FMT_UYVY, V4L2_FIELD_NONE, \
false);
if (0 <= rc)
JOT(8, "adjust_format(.,640,480,uyvy,false) OK\n");
else {
SAY("ERROR: adjust_format(.,640,480,uyvy,false) returned %i\n", rc);
return -EFAULT;
}
#endif /* !PREFER_NTSC*/
/*---------------------------------------------------------------------------*/
rc = adjust_brightness(peasycap, -8192);
if (0 != rc) {
SAY("ERROR: adjust_brightness(default) returned %i\n", rc);
return -EFAULT;
}
rc = adjust_contrast(peasycap, -8192);
if (0 != rc) {
SAY("ERROR: adjust_contrast(default) returned %i\n", rc);
return -EFAULT;
}
rc = adjust_saturation(peasycap, -8192);
if (0 != rc) {
SAY("ERROR: adjust_saturation(default) returned %i\n", rc);
return -EFAULT;
}
rc = adjust_hue(peasycap, -8192);
if (0 != rc) {
SAY("ERROR: adjust_hue(default) returned %i\n", rc);
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
rc = usb_set_interface(peasycap->pusb_device, peasycap->video_interface, \
peasycap->video_altsetting_on);
if (0 == rc)
JOT(8, "usb_set_interface(.,%i,%i) OK\n", peasycap->video_interface, \
peasycap->video_altsetting_on);
else {
SAY("ERROR: usb_set_interface() returned %i\n", rc);
return -EFAULT;
}
rc = start_100(p);
if (0 == rc)
JOT(8, "start_100() OK\n");
else {
SAY("ERROR: start_100() returned %i\n", rc);
return -EFAULT;
}
peasycap->video_isoc_sequence = VIDEO_ISOC_BUFFER_MANY - 1;
peasycap->video_idle = 0;
peasycap->video_junk = 0;
for (i = 0; i < 180; i++)
peasycap->merit[i] = 0;
peasycap->video_eof = 0;
peasycap->audio_eof = 0;
do_gettimeofday(&peasycap->timeval7);
peasycap->fudge = 0;
JOT(4, "finished initialization\n");
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, m, rc;
int isbuf;
if ((struct list_head *)NULL == peasycap->purb_video_head) {
SAY("ERROR: peasycap->urb_video_head uninitialized\n");
return -EFAULT;
}
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
if (!peasycap->video_isoc_streaming) {
JOT(4, "submission of all video urbs\n");
if (0 != ready_saa(peasycap->pusb_device)) {
SAY("ERROR: not ready to capture after waiting " \
"one second\n");
SAY("..... continuing anyway\n");
}
isbad = 0; m = 0;
list_for_each(plist_head, (peasycap->purb_video_head)) {
pdata_urb = list_entry(plist_head, struct data_urb, list_head);
if (NULL != pdata_urb) {
purb = pdata_urb->purb;
if (NULL != 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 (0 != rc) {
isbad++;
SAY("ERROR: usb_submit_urb() failed " \
"for urb with rc:\n");
switch (rc) {
case -ENOMEM: {
SAY("ENOMEM\n");
break;
}
case -ENODEV: {
SAY("ENODEV\n");
break;
}
case -ENXIO: {
SAY("ENXIO\n");
break;
}
case -EINVAL: {
SAY("EINVAL\n");
break;
}
case -EAGAIN: {
SAY("EAGAIN\n");
break;
}
case -EFBIG: {
SAY("EFBIG\n");
break;
}
case -EPIPE: {
SAY("EPIPE\n");
break;
}
case -EMSGSIZE: {
SAY("EMSGSIZE\n");
break;
}
default: {
SAY("unknown error code %i\n",\
rc);
break;
}
}
} else {
m++;
}
} else {
isbad++;
}
} else {
isbad++;
}
}
if (isbad) {
JOT(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 (NULL != pdata_urb) {
purb = pdata_urb->purb;
if (NULL != purb)
usb_kill_urb(purb);
}
}
peasycap->video_isoc_streaming = 0;
} else {
peasycap->video_isoc_streaming = 1;
JOT(4, "submitted %i video urbs\n", m);
}
} else {
JOT(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 ((struct easycap *)NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return -EFAULT;
}
if (peasycap->video_isoc_streaming) {
if ((struct list_head *)NULL != peasycap->purb_video_head) {
peasycap->video_isoc_streaming = 0;
JOT(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 ((struct data_urb *)NULL != pdata_urb) {
if ((struct urb *)NULL != pdata_urb->purb) {
usb_kill_urb(pdata_urb->purb);
m++;
}
}
}
JOT(4, "%i video urbs killed\n", m);
} else {
SAY("ERROR: peasycap->purb_video_head is NULL\n");
return -EFAULT;
}
} else {
JOT(8, "%i=video_isoc_streaming, no video urbs killed\n", \
peasycap->video_isoc_streaming);
}
return 0;
}
/****************************************************************************/
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
int
easycap_release_noinode(struct file *file)
{
return easycap_release((struct inode *)NULL, file);
}
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/*--------------------------------------------------------------------------*/
int
easycap_release(struct inode *inode, struct file *file)
{
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
struct easycap *peasycap;
JOT(4, "\n");
peasycap = (struct easycap *)file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL.\n");
SAY("ending unsuccessfully\n");
return -EFAULT;
}
if (0 != kill_video_urbs(peasycap)) {
SAY("ERROR: kill_video_urbs() failed\n");
return -EFAULT;
}
JOT(4, "ending successfully\n");
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#else
#
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
return 0;
}
/****************************************************************************/
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#if defined(EASYCAP_IS_VIDEODEV_CLIENT)
int
videodev_release(struct video_device *pvd)
{
struct easycap *peasycap;
int i, j, k;
JOT(4, "\n");
k = 0;
for (i = 0; i < video_device_many; i++) {
pvideo_device = pvideo_array[i];
if ((struct video_device *)NULL != pvideo_device) {
if (pvd->minor == pvideo_device->minor) {
peasycap = (struct easycap *)\
video_get_drvdata(pvideo_device);
if ((struct easycap *)NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
SAY("ending unsuccessfully\n");
return -EFAULT;
}
if (0 != kill_video_urbs(peasycap)) {
SAY("ERROR: kill_video_urbs() failed\n");
return -EFAULT;
}
JOT(4, "freeing video_device structure: " \
"/dev/video%i\n", i);
kfree((void *)pvideo_device);
for (j = i; j < (VIDEO_DEVICE_MANY - 1); j++)
pvideo_array[j] = pvideo_array[j + 1];
video_device_many--; k++;
break;
}
}
}
if (!k) {
SAY("ERROR: lost video_device structure for %i=minor\n", pvd->minor);
SAY("cannot free: may cause memory leak\n");
SAY("ending unsuccessfully\n");
return -EFAULT;
}
JOT(4, "ending successfully\n");
return 0;
}
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
/****************************************************************************/
/*--------------------------------------------------------------------------*/
/*
* THIS FUNCTION IS CALLED FROM WITHIN easycap_usb_disconnect().
* BY THIS STAGE THE DEVICE HAS ALREADY BEEN PHYSICALLY UNPLUGGED.
* peasycap->pusb_device IS NO LONGER VALID AND SHOULD HAVE BEEN SET TO NULL.
*/
/*---------------------------------------------------------------------------*/
void
easycap_delete(struct kref *pkref)
{
int k, m, lost;
int allocation_video_urb, allocation_video_page, allocation_video_struct;
int allocation_audio_urb, allocation_audio_page, allocation_audio_struct;
int registered_video, registered_audio;
struct easycap *peasycap;
struct data_urb *pdata_urb;
struct list_head *plist_head, *plist_next;
JOT(4, "\n");
peasycap = container_of(pkref, struct easycap, kref);
if ((struct easycap *)NULL == peasycap) {
SAY("ERROR: peasycap is NULL: cannot perform deletions\n");
return;
}
/*---------------------------------------------------------------------------*/
/*
* FREE VIDEO.
*/
/*---------------------------------------------------------------------------*/
if ((struct list_head *)NULL != peasycap->purb_video_head) {
JOT(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 (NULL == pdata_urb)
JOT(4, "ERROR: pdata_urb is NULL\n");
else {
if ((struct urb *)NULL != pdata_urb->purb) {
usb_free_urb(pdata_urb->purb);
pdata_urb->purb = (struct urb *)NULL;
peasycap->allocation_video_urb -= 1;
m++;
}
}
}
JOT(4, "%i video urbs freed\n", m);
/*---------------------------------------------------------------------------*/
JOT(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 ((struct data_urb *)NULL != pdata_urb) {
kfree(pdata_urb); pdata_urb = (struct data_urb *)NULL;
peasycap->allocation_video_struct -= \
sizeof(struct data_urb);
m++;
}
}
JOT(4, "%i video data_urb structures freed\n", m);
JOT(4, "setting peasycap->purb_video_head=NULL\n");
peasycap->purb_video_head = (struct list_head *)NULL;
} else {
JOT(4, "peasycap->purb_video_head is NULL\n");
}
/*---------------------------------------------------------------------------*/
JOT(4, "freeing video isoc buffers.\n");
m = 0;
for (k = 0; k < VIDEO_ISOC_BUFFER_MANY; k++) {
if ((void *)NULL != 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 = (void *)NULL;
peasycap->allocation_video_page -= \
((unsigned int)(0x01 << VIDEO_ISOC_ORDER));
m++;
}
}
JOT(4, "isoc video buffers freed: %i pages\n", m * (0x01 << VIDEO_ISOC_ORDER));
/*---------------------------------------------------------------------------*/
JOT(4, "freeing video field buffers.\n");
lost = 0;
for (k = 0; k < FIELD_BUFFER_MANY; k++) {
for (m = 0; m < FIELD_BUFFER_SIZE/PAGE_SIZE; m++) {
if ((void *)NULL != peasycap->field_buffer[k][m].pgo) {
free_page((unsigned long)\
(peasycap->field_buffer[k][m].pgo));
peasycap->field_buffer[k][m].pgo = (void *)NULL;
peasycap->allocation_video_page -= 1;
lost++;
}
}
}
JOT(4, "video field buffers freed: %i pages\n", lost);
/*---------------------------------------------------------------------------*/
JOT(4, "freeing video frame buffers.\n");
lost = 0;
for (k = 0; k < FRAME_BUFFER_MANY; k++) {
for (m = 0; m < FRAME_BUFFER_SIZE/PAGE_SIZE; m++) {
if ((void *)NULL != peasycap->frame_buffer[k][m].pgo) {
free_page((unsigned long)\
(peasycap->frame_buffer[k][m].pgo));
peasycap->frame_buffer[k][m].pgo = (void *)NULL;
peasycap->allocation_video_page -= 1;
lost++;
}
}
}
JOT(4, "video frame buffers freed: %i pages\n", lost);
/*---------------------------------------------------------------------------*/
/*
* FREE AUDIO.
*/
/*---------------------------------------------------------------------------*/
if ((struct list_head *)NULL != peasycap->purb_audio_head) {
JOT(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 (NULL == pdata_urb)
JOT(4, "ERROR: pdata_urb is NULL\n");
else {
if ((struct urb *)NULL != pdata_urb->purb) {
usb_free_urb(pdata_urb->purb);
pdata_urb->purb = (struct urb *)NULL;
peasycap->allocation_audio_urb -= 1;
m++;
}
}
}
JOT(4, "%i audio urbs freed\n", m);
/*---------------------------------------------------------------------------*/
JOT(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 ((struct data_urb *)NULL != pdata_urb) {
kfree(pdata_urb); pdata_urb = (struct data_urb *)NULL;
peasycap->allocation_audio_struct -= \
sizeof(struct data_urb);
m++;
}
}
JOT(4, "%i audio data_urb structures freed\n", m);
JOT(4, "setting peasycap->purb_audio_head=NULL\n");
peasycap->purb_audio_head = (struct list_head *)NULL;
} else {
JOT(4, "peasycap->purb_audio_head is NULL\n");
}
/*---------------------------------------------------------------------------*/
JOT(4, "freeing audio isoc buffers.\n");
m = 0;
for (k = 0; k < AUDIO_ISOC_BUFFER_MANY; k++) {
if ((void *)NULL != 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 = (void *)NULL;
peasycap->allocation_audio_page -= \
((unsigned int)(0x01 << AUDIO_ISOC_ORDER));
m++;
}
}
JOT(4, "easysnd_delete(): isoc audio buffers freed: %i pages\n", \
m * (0x01 << AUDIO_ISOC_ORDER));
/*---------------------------------------------------------------------------*/
JOT(4, "freeing audio buffers.\n");
lost = 0;
for (k = 0; k < audio_buffer_page_many; k++) {
if ((void *)NULL != peasycap->audio_buffer[k].pgo) {
free_page((unsigned long)(peasycap->audio_buffer[k].pgo));
peasycap->audio_buffer[k].pgo = (void *)NULL;
peasycap->allocation_audio_page -= 1;
lost++;
}
}
JOT(4, "easysnd_delete(): audio buffers freed: %i pages\n", lost);
/*---------------------------------------------------------------------------*/
JOT(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;
m = 0;
if ((struct easycap *)NULL != peasycap) {
kfree(peasycap); peasycap = (struct easycap *)NULL;
allocation_video_struct -= sizeof(struct easycap);
m++;
}
JOT(4, "%i easycap structure freed\n", m);
/*---------------------------------------------------------------------------*/
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;
}
/*****************************************************************************/
unsigned int easycap_poll(struct file *file, poll_table *wait)
{
struct easycap *peasycap;
JOT(8, "\n");
if (NULL == ((poll_table *)wait))
JOT(8, "WARNING: poll table pointer is NULL ... continuing\n");
if (NULL == ((struct file *)file)) {
SAY("ERROR: file pointer is NULL\n");
return -EFAULT;
}
peasycap = (struct easycap *)file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return -EFAULT;
}
peasycap->polled = 1;
if (0 == easycap_dqbuf(peasycap, 0))
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 miss, rc;
JOT(8, "\n");
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
/*
* WAIT FOR FIELD 0
*/
/*---------------------------------------------------------------------------*/
miss = 0;
if (mutex_lock_interruptible(&(peasycap->mutex_mmap_video[0])))
return -ERESTARTSYS;
while ((peasycap->field_read == peasycap->field_fill) || \
(0 != (0xFF00 & peasycap->field_buffer\
[peasycap->field_read][0].kount)) || \
(0 != (0x00FF & peasycap->field_buffer\
[peasycap->field_read][0].kount))) {
mutex_unlock(&(peasycap->mutex_mmap_video[0]));
if (mode)
return -EAGAIN;
JOT(8, "first wait on wq_video, " \
"%i=field_read %i=field_fill\n", \
peasycap->field_read, peasycap->field_fill);
msleep(1);
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)) && \
(0 == (0x00FF & peasycap->field_buffer\
[peasycap->field_read][0].kount))))))){
SAY("aborted by signal\n");
return -EIO;
}
if (peasycap->video_idle) {
JOT(8, "%i=peasycap->video_idle\n", peasycap->video_idle);
return -EIO;
}
if (peasycap->video_eof) {
JOT(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
debrief(peasycap);
kill_video_urbs(peasycap);
return -EIO;
}
miss++;
if (mutex_lock_interruptible(&(peasycap->mutex_mmap_video[0])))
return -ERESTARTSYS;
}
mutex_unlock(&(peasycap->mutex_mmap_video[0]));
JOT(8, "first awakening on wq_video after %i waits\n", miss);
rc = field2frame(peasycap);
if (0 != rc)
SAY("ERROR: field2frame() returned %i\n", rc);
if (true == peasycap->offerfields) {
peasycap->frame_read = peasycap->frame_fill;
(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_BOTTOM;
} else {
peasycap->frame_buffer[peasycap->frame_read][0].kount = \
V4L2_FIELD_TOP;
}
JOT(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read);
JOT(8, "bumped to: %i=peasycap->frame_fill\n", peasycap->frame_fill);
}
/*---------------------------------------------------------------------------*/
/*
* WAIT FOR FIELD 1
*/
/*---------------------------------------------------------------------------*/
miss = 0;
if (mutex_lock_interruptible(&(peasycap->mutex_mmap_video[0])))
return -ERESTARTSYS;
while ((peasycap->field_read == peasycap->field_fill) || \
(0 != (0xFF00 & peasycap->field_buffer\
[peasycap->field_read][0].kount)) || \
(0 == (0x00FF & peasycap->field_buffer\
[peasycap->field_read][0].kount))) {
mutex_unlock(&(peasycap->mutex_mmap_video[0]));
if (mode)
return -EAGAIN;
JOT(8, "second wait on wq_video, " \
"%i=field_read %i=field_fill\n", \
peasycap->field_read, peasycap->field_fill);
msleep(1);
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)) && \
(0 != (0x00FF & peasycap->field_buffer\
[peasycap->field_read][0].kount))))))){
SAY("aborted by signal\n");
return -EIO;
}
if (peasycap->video_idle) {
JOT(8, "%i=peasycap->video_idle\n", peasycap->video_idle);
return -EIO;
}
if (peasycap->video_eof) {
JOT(8, "%i=peasycap->video_eof\n", peasycap->video_eof);
debrief(peasycap);
kill_video_urbs(peasycap);
return -EIO;
}
miss++;
if (mutex_lock_interruptible(&(peasycap->mutex_mmap_video[0])))
return -ERESTARTSYS;
}
mutex_unlock(&(peasycap->mutex_mmap_video[0]));
JOT(8, "second awakening on wq_video after %i waits\n", miss);
rc = field2frame(peasycap);
if (0 != rc)
SAY("ERROR: field2frame() returned %i\n", rc);
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;
}
JOT(8, "setting: %i=peasycap->frame_read\n", peasycap->frame_read);
JOT(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_ALTERNATE. NO USERSPACE PROGRAM TESTED
* TO DATE HAS DONE THIS. BUGS ARE LIKELY.
*/
/*---------------------------------------------------------------------------*/
int
field2frame(struct easycap *peasycap)
{
static struct timeval timeval0;
struct timeval timeval;
long long int above, below;
__u32 remainder;
struct signed_div_result sdr;
void *pex, *pad;
int kex, kad, mex, mad, rex, rad, rad2;
int c2, c3, w2, w3, cz, wz;
int rc, bytesperpixel, multiplier, much, more, over, rump, caches;
__u8 mask, margin;
bool odd, isuy, decimatepixel, offerfields;
JOT(8, "===== parity %i, field buffer %i --> frame buffer %i\n", \
peasycap->field_buffer[peasycap->field_read][0].kount,\
peasycap->field_read, peasycap->frame_fill);
JOT(8, "===== %i=bytesperpixel\n", peasycap->bytesperpixel);
if (true == peasycap->offerfields)
JOT(8, "===== offerfields\n");
/*---------------------------------------------------------------------------*/
/*
* REJECT OR CLEAN BAD FIELDS
*/
/*---------------------------------------------------------------------------*/
if (peasycap->field_read == peasycap->field_fill) {
SAY("ERROR: on entry, still filling field buffer %i\n", \
peasycap->field_read);
return 0;
}
#if defined(EASYCAP_TESTCARD)
easycap_testcard(peasycap, peasycap->field_read);
#else
if (0 != (0x0400 & peasycap->field_buffer[peasycap->field_read][0].kount))
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)) {
SAY("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
return -EFAULT;
}
if (true == 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;
if (peasycap->field_buffer[kex][0].kount)
odd = true;
else
odd = false;
if ((true == odd) && (false == offerfields) &&(false == decimatepixel)) {
JOT(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 (false == decimatepixel) {
over = w2;
do {
much = over; more = 0; margin = 0; mask = 0x00;
if (rex < much)
much = rex;
rump = 0;
if (much % 2) {
SAY("MISTAKE: much is odd\n");
return -EFAULT;
}
more = (bytesperpixel * \
much) / 2;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (1 < bytesperpixel) {
if ((rad * \
2) < (much * \
bytesperpixel)) {
/*
** INJUDICIOUS ALTERATION OF THIS
** 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 {
SAY("MISTAKE: %i=bytesperpixel\n", \
bytesperpixel);
return -EFAULT;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (rump)
caches++;
rc = redaub(peasycap, pad, pex, much, more, \
mask, margin, isuy);
if (0 > rc) {
SAY("ERROR: redaub() failed\n");
return -EFAULT;
}
if (much % 4) {
if (isuy)
isuy = false;
else
isuy = true;
}
over -= much; cz += much;
pex += much; rex -= much;
if (!rex) {
mex++;
pex = peasycap->field_buffer[kex][mex].pgo;
rex = PAGE_SIZE;
}
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 (((false == odd) || (cz != wz))&&(false == offerfields)) {
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 (false == odd) {
over = w2;
do {
much = over; more = 0; margin = 0; mask = 0x00;
if (rex < much)
much = rex;
rump = 0;
if (much % 2) {
SAY("MISTAKE: much is odd\n");
return -EFAULT;
}
more = (bytesperpixel * \
much) / 4;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (1 < bytesperpixel) {
if ((rad * 4) < (much * \
bytesperpixel)) {
/*
** INJUDICIOUS ALTERATION OF THIS
** 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 {
SAY("MISTAKE: %i=bytesperpixel\n", \
bytesperpixel);
return -EFAULT;
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
if (rump)
caches++;
rc = redaub(peasycap, pad, pex, much, more, \
mask, margin, isuy);
if (0 > rc) {
SAY("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;
}
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;
}
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)
SAY("ERROR: discrepancy %i in bytes read\n", c2 - cz);
c3 = (mad + 1)*PAGE_SIZE - rad;
if (false == decimatepixel) {
if (bytesperpixel * \
cz != c3) \
SAY("ERROR: discrepancy %i in bytes written\n", \
c3 - (bytesperpixel * \
cz));
} else {
if (false == odd) {
if (bytesperpixel * \
cz != (4 * c3))
SAY("ERROR: discrepancy %i in bytes written\n", \
(2*c3)-(bytesperpixel * \
cz));
} else {
if (0 != c3)
SAY("ERROR: discrepancy %i " \
"in bytes written\n", c3);
}
}
if (rump)
SAY("ERROR: undischarged cache at end of line in frame buffer\n");
JOT(8, "===== field2frame(): %i bytes --> %i bytes (incl skip)\n", c2, c3);
JOT(8, "===== field2frame(): %i=mad %i=rad\n", mad, rad);
if (true == odd)
JOT(8, "+++++ field2frame(): frame buffer %i is full\n", kad);
if (peasycap->field_read == peasycap->field_fill)
SAY("WARNING: on exit, filling field buffer %i\n", \
peasycap->field_read);
/*---------------------------------------------------------------------------*/
/*
* CALCULATE VIDEO STREAMING RATE
*/
/*---------------------------------------------------------------------------*/
do_gettimeofday(&timeval);
if (timeval0.tv_sec) {
below = ((long long int)(1000000)) * \
((long long int)(timeval.tv_sec - timeval0.tv_sec)) + \
(long long int)(timeval.tv_usec - timeval0.tv_usec);
above = (long long int)1000000;
sdr = signed_div(above, below);
above = sdr.quotient;
remainder = (__u32)sdr.remainder;
JOT(8, "video streaming at %3lli.%03i fields per second\n", above, \
(remainder/1000));
}
timeval0 = timeval;
if (caches)
JOT(8, "%i=caches\n", caches);
return 0;
}
/*****************************************************************************/
struct signed_div_result
signed_div(long long int above, long long int below)
{
struct signed_div_result sdr;
if (((0 <= above) && (0 <= below)) || ((0 > above) && (0 > below))) {
sdr.remainder = (unsigned long long int) do_div(above, below);
sdr.quotient = (long long int) above;
} else {
if (0 > above)
above = -above;
if (0 > below)
below = -below;
sdr.remainder = (unsigned long long int) do_div(above, below);
sdr.quotient = -((long long int) above);
}
return sdr;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* 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];
static __u8 cache[8], *pcache;
__u8 r, g, b, y, u, v, c, *p2, *p3, *pz, *pr;
int bytesperpixel;
bool byteswaporder, decimatepixel, last;
int j, rump;
__s32 s32;
if (much % 2) {
SAY("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++) {
s32 = (0xFF00 & (453 * j)) >> 8;
bu[j + 128] = s32; bu[127 - j] = -s32;
s32 = (0xFF00 & (359 * j)) >> 8;
rv[j + 128] = s32; rv[127 - j] = -s32;
s32 = (0xFF00 & (88 * j)) >> 8;
gu[j + 128] = s32; gu[127 - j] = -s32;
s32 = (0xFF00 & (183 * j)) >> 8;
gv[j + 128] = s32; gv[127 - j] = -s32;
}
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];
JOT(8, "lookup tables are prepared\n");
}
if ((__u8 *)NULL == pcache)
pcache = &cache[0];
/*---------------------------------------------------------------------------*/
/*
* TRANSFER CONTENTS OF CACHE TO THE FRAME BUFFER
*/
/*---------------------------------------------------------------------------*/
if (!pcache) {
SAY("MISTAKE: pcache is NULL\n");
return -EFAULT;
}
if (pcache != &cache[0])
JOT(16, "cache has %i bytes\n", (int)(pcache - &cache[0]));
p2 = &cache[0];
p3 = (__u8 *)pad - (int)(pcache - &cache[0]);
while (p2 < pcache) {
*p3++ = *p2; p2++;
}
pcache = &cache[0];
if (p3 != pad) {
SAY("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 (true == isuy)
u = *(p2 - 1);
else
v = *(p2 - 1);
if (rump)
JOT(16, "%4i=much %4i=more %i=rump\n", much, more, rump);
/*---------------------------------------------------------------------------*/
switch (bytesperpixel) {
case 2: {
if (false == decimatepixel) {
memcpy(pad, pex, (size_t)much);
if (false == 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 (false == 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 (false == decimatepixel) {
if (false == byteswaporder) {
/*---------------------------------------------------*/
/*
** RGB
**/
/*---------------------------------------------------*/
while (pz > p2) {
if (pr <= (p3 + bytesperpixel))
last = true;
else
last = false;
y = *p2;
if ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
} else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("MISTAKE: %i=rump\n", \
bytesperpixel - rump);
return -EFAULT;
}
}
} else {
*p3 = r;
*(p3 + 1) = g;
*(p3 + 2) = b;
}
p2 += 2;
if (true == 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 ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
}
else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("MISTAKE: %i=rump\n", \
bytesperpixel - rump);
return -EFAULT;
}
}
} else {
*p3 = b;
*(p3 + 1) = g;
*(p3 + 2) = r;
}
p2 += 2;
if (true == isuy)
isuy = false;
else
isuy = true;
p3 += bytesperpixel;
}
}
return 0;
} else {
if (false == byteswaporder) {
/*---------------------------------------------------*/
/*
** RGB DECIMATED
*/
/*---------------------------------------------------*/
while (pz > p2) {
if (pr <= (p3 + bytesperpixel))
last = true;
else
last = false;
y = *p2;
if ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
} else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
if (true == isuy) {
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - \
gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("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 ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
} else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
if (true == isuy) {
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - \
gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("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 (false == decimatepixel) {
if (false == byteswaporder) {
/*---------------------------------------------------*/
/*
** RGBA
*/
/*---------------------------------------------------*/
while (pz > p2) {
if (pr <= (p3 + bytesperpixel))
last = true;
else
last = false;
y = *p2;
if ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
} else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("MISTAKE: %i=rump\n", \
bytesperpixel - rump);
return -EFAULT;
}
}
} else {
*p3 = r;
*(p3 + 1) = g;
*(p3 + 2) = b;
*(p3 + 3) = 0;
}
p2 += 2;
if (true == 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 ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
} else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("MISTAKE: %i=rump\n", \
bytesperpixel - rump);
return -EFAULT;
}
}
} else {
*p3 = b;
*(p3 + 1) = g;
*(p3 + 2) = r;
*(p3 + 3) = 0;
}
p2 += 2;
if (true == isuy)
isuy = false;
else
isuy = true;
p3 += bytesperpixel;
}
}
return 0;
} else {
if (false == byteswaporder) {
/*---------------------------------------------------*/
/*
** RGBA DECIMATED
*/
/*---------------------------------------------------*/
while (pz > p2) {
if (pr <= (p3 + bytesperpixel))
last = true;
else
last = false;
y = *p2;
if ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
} else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
if (true == isuy) {
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - \
gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("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 ((true == last) && (0x0C & mask)) {
if (0x04 & mask) {
if (true == isuy)
v = margin;
else
u = margin;
} else
if (0x08 & mask)
;
} else {
if (true == isuy)
v = *(p2 + 1);
else
u = *(p2 + 1);
}
if (true == isuy) {
s32 = ay[(int)y] + rv[(int)v];
r = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] - gu[(int)u] - \
gv[(int)v];
g = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
s32 = ay[(int)y] + bu[(int)u];
b = (255 < s32) ? 255 : ((0 > s32) ? \
0 : (__u8)s32);
if ((true == last) && rump) {
pcache = &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: {
SAY("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: {
SAY("MISTAKE: %i=bytesperpixel\n", bytesperpixel);
return -EFAULT;
}
}
return 0;
}
/*****************************************************************************/
void
debrief(struct easycap *peasycap)
{
if ((struct usb_device *)NULL != peasycap->pusb_device) {
check_stk(peasycap->pusb_device);
check_saa(peasycap->pusb_device);
sayreadonly(peasycap);
SAY("%i=peasycap->field_fill\n", peasycap->field_fill);
SAY("%i=peasycap->field_read\n", peasycap->field_read);
SAY("%i=peasycap->frame_fill\n", peasycap->frame_fill);
SAY("%i=peasycap->frame_read\n", peasycap->frame_read);
}
return;
}
/*****************************************************************************/
void
sayreadonly(struct easycap *peasycap)
{
static int done;
int got00, got1F, got60, got61, got62;
if ((!done) && ((struct usb_device *)NULL != peasycap->pusb_device)) {
done = 1;
got00 = read_saa(peasycap->pusb_device, 0x00);
got1F = read_saa(peasycap->pusb_device, 0x1F);
got60 = read_saa(peasycap->pusb_device, 0x60);
got61 = read_saa(peasycap->pusb_device, 0x61);
got62 = read_saa(peasycap->pusb_device, 0x62);
SAY("0x%02X=reg0x00 0x%02X=reg0x1F\n", got00, got1F);
SAY("0x%02X=reg0x60 0x%02X=reg0x61 0x%02X=reg0x62\n", \
got60, got61, got62);
}
return;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* SEE CORBET ET AL. "LINUX DEVICE DRIVERS", 3rd EDITION, PAGES 430-434
*/
/*---------------------------------------------------------------------------*/
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 (NULL != file)
pvma->vm_private_data = file->private_data;
easycap_vma_open(pvma);
return 0;
}
/*****************************************************************************/
void
easycap_vma_open(struct vm_area_struct *pvma)
{
struct easycap *peasycap;
peasycap = pvma->vm_private_data;
if (NULL != peasycap)
peasycap->vma_many++;
JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
return;
}
/*****************************************************************************/
void
easycap_vma_close(struct vm_area_struct *pvma)
{
struct easycap *peasycap;
peasycap = pvma->vm_private_data;
if (NULL != peasycap) {
peasycap->vma_many--;
JOT(8, "%i=peasycap->vma_many\n", peasycap->vma_many);
}
return;
}
/*****************************************************************************/
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;
pbuf = (void *)NULL;
page = (struct page *)NULL;
if (NULL == pvma) {
SAY("pvma is NULL\n");
return retcode;
}
if (NULL == 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 (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return retcode;
}
mutex_lock(&(peasycap->mutex_mmap_video[0]));
/*---------------------------------------------------------------------------*/
pbuf = peasycap->frame_buffer[k][m].pgo;
if (NULL == pbuf) {
SAY("ERROR: pbuf is NULL\n");
goto finish;
}
page = virt_to_page(pbuf);
if (NULL == page) {
SAY("ERROR: page is NULL\n");
goto finish;
}
get_page(page);
/*---------------------------------------------------------------------------*/
finish:
mutex_unlock(&(peasycap->mutex_mmap_video[0]));
if (NULL == page) {
SAY("ERROR: page is NULL after get_page(page)\n");
} else {
pvmf->page = page;
retcode = VM_FAULT_MINOR;
}
return retcode;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* ON COMPLETION OF A VIDEO URB ITS DATA IS COPIED TO THE FIELD BUFFERS
* PROVIDED peasycap->video_idle IS ZER0. 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 & 0x0400) => FIELD WAS SUBMITTED BY BRIDGER ROUTINE
* 0 != (kount & 0x0200) => FIELD BUFFER NOT YET CHECKED
* 0 != (kount & 0x0100) => BUFFER HAS TWO EXTRA BYTES - WHY?
*/
/*---------------------------------------------------------------------------*/
void
easycap_complete(struct urb *purb)
{
static int mt;
struct easycap *peasycap;
struct data_buffer *pfield_buffer;
char errbuf[16];
int i, more, much, leap, rc, last;
int videofieldamount;
unsigned int override;
int framestatus, framelength, frameactual, frameoffset;
__u8 *pu;
#if defined(BRIDGER)
struct timeval timeval;
long long usec;
#endif /*BRIDGER*/
if (NULL == purb) {
SAY("ERROR: easycap_complete(): purb is NULL\n");
return;
}
peasycap = purb->context;
if (NULL == 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;
JOT(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))) {
SAY("ERROR: out-of-order urbs %i,%i ... continuing\n", last, i);
}
peasycap->video_isoc_sequence = i;
if (peasycap->video_idle) {
JOT(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 (0 != rc) {
SAY("ERROR: while %i=video_idle, " \
"usb_submit_urb() failed with rc:\n", \
peasycap->video_idle);
switch (rc) {
case -ENOMEM: {
SAY("ENOMEM\n");
break;
}
case -ENODEV: {
SAY("ENODEV\n");
break;
}
case -ENXIO: {
SAY("ENXIO\n");
break;
}
case -EINVAL: {
SAY("EINVAL\n");
break;
}
case -EAGAIN: {
SAY("EAGAIN\n");
break;
}
case -EFBIG: {
SAY("EFBIG\n");
break;
}
case -EPIPE: {
SAY("EPIPE\n");
break;
}
case -EMSGSIZE: {
SAY("EMSGSIZE\n");
break;
}
default: {
SAY("0x%08X\n", rc);
break;
}
}
}
}
return;
}
override = 0;
/*---------------------------------------------------------------------------*/
if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
SAY("ERROR: bad peasycap->field_fill\n");
return;
}
if (purb->status) {
if ((-ESHUTDOWN == purb->status) || (-ENOENT == purb->status)) {
JOT(8, "urb status -ESHUTDOWN or -ENOENT\n");
return;
}
(peasycap->field_buffer[peasycap->field_fill][0].kount) |= 0x8000 ;
SAY("ERROR: bad urb status:\n");
switch (purb->status) {
case -EINPROGRESS: {
SAY("-EINPROGRESS\n"); break;
}
case -ENOSR: {
SAY("-ENOSR\n"); break;
}
case -EPIPE: {
SAY("-EPIPE\n"); break;
}
case -EOVERFLOW: {
SAY("-EOVERFLOW\n"); break;
}
case -EPROTO: {
SAY("-EPROTO\n"); break;
}
case -EILSEQ: {
SAY("-EILSEQ\n"); break;
}
case -ETIMEDOUT: {
SAY("-ETIMEDOUT\n"); break;
}
case -EMSGSIZE: {
SAY("-EMSGSIZE\n"); break;
}
case -EOPNOTSUPP: {
SAY("-EOPNOTSUPP\n"); break;
}
case -EPFNOSUPPORT: {
SAY("-EPFNOSUPPORT\n"); break;
}
case -EAFNOSUPPORT: {
SAY("-EAFNOSUPPORT\n"); break;
}
case -EADDRINUSE: {
SAY("-EADDRINUSE\n"); break;
}
case -EADDRNOTAVAIL: {
SAY("-EADDRNOTAVAIL\n"); break;
}
case -ENOBUFS: {
SAY("-ENOBUFS\n"); break;
}
case -EISCONN: {
SAY("-EISCONN\n"); break;
}
case -ENOTCONN: {
SAY("-ENOTCONN\n"); break;
}
case -ESHUTDOWN: {
SAY("-ESHUTDOWN\n"); break;
}
case -ENOENT: {
SAY("-ENOENT\n"); break;
}
case -ECONNRESET: {
SAY("-ECONNRESET\n"); break;
}
default: {
SAY("unknown error code 0x%08X\n", purb->status); break;
}
}
/*---------------------------------------------------------------------------*/
} 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 ;
switch (purb->iso_frame_desc[i].status) {
case 0: {
strcpy(&errbuf[0], "OK"); break;
}
case -ENOENT: {
strcpy(&errbuf[0], "-ENOENT"); break;
}
case -EINPROGRESS: {
strcpy(&errbuf[0], "-EINPROGRESS"); break;
}
case -EPROTO: {
strcpy(&errbuf[0], "-EPROTO"); break;
}
case -EILSEQ: {
strcpy(&errbuf[0], "-EILSEQ"); break;
}
case -ETIME: {
strcpy(&errbuf[0], "-ETIME"); break;
}
case -ETIMEDOUT: {
strcpy(&errbuf[0], "-ETIMEDOUT"); break;
}
case -EPIPE: {
strcpy(&errbuf[0], "-EPIPE"); break;
}
case -ECOMM: {
strcpy(&errbuf[0], "-ECOMM"); break;
}
case -ENOSR: {
strcpy(&errbuf[0], "-ENOSR"); break;
}
case -EOVERFLOW: {
strcpy(&errbuf[0], "-EOVERFLOW"); break;
}
case -EREMOTEIO: {
strcpy(&errbuf[0], "-EREMOTEIO"); break;
}
case -ENODEV: {
strcpy(&errbuf[0], "-ENODEV"); break;
}
case -EXDEV: {
strcpy(&errbuf[0], "-EXDEV"); break;
}
case -EINVAL: {
strcpy(&errbuf[0], "-EINVAL"); break;
}
case -ECONNRESET: {
strcpy(&errbuf[0], "-ECONNRESET"); break;
}
case -ESHUTDOWN: {
strcpy(&errbuf[0], "-ESHUTDOWN"); break;
}
default: {
strcpy(&errbuf[0], "unknown error"); break;
}
}
}
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;
JOT(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)
mt++;
if (4 < more) {
if (mt) {
JOT(8, "%4i empty video urb frames\n", mt);
mt = 0;
}
if (FIELD_BUFFER_MANY <= peasycap->field_fill) {
SAY("ERROR: bad peasycap->field_fill\n");
return;
}
if (FIELD_BUFFER_SIZE/PAGE_SIZE <= \
peasycap->field_page) {
SAY("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;
else
(peasycap->field_buffer\
[peasycap->field_fill]\
[0].kount) |= 0x4000;
} else if (videofieldamount < \
peasycap->\
videofieldamount) {
(peasycap->field_buffer\
[peasycap->field_fill]\
[0].kount) |= 0x2000;
}
if (!(0xFF00 & peasycap->field_buffer\
[peasycap->field_fill]\
[0].kount)) {
(peasycap->video_junk)--;
if (-16 > peasycap->video_junk)
peasycap->video_junk = -16;
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;
JOT(8, "bumped to: %i=peasycap->" \
"field_fill %i=parity\n", \
peasycap->field_fill, \
0x00FF & pfield_buffer->kount);
JOT(8, "field buffer %i has %i " \
"bytes fit to be read\n", \
peasycap->field_read, \
videofieldamount);
JOT(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));
do_gettimeofday(&peasycap->timeval7);
} else {
peasycap->video_junk++;
JOT(8, "field buffer %i had %i " \
"bytes, now discarded\n", \
peasycap->field_fill, \
videofieldamount);
(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;
JOT(8, "bumped to: %i=peasycap->" \
"field_fill %i=parity\n", \
peasycap->field_fill, \
0x00FF & pfield_buffer->kount);
}
if (8 == more) {
JOT(8, "end-of-field: received " \
"parity byte 0x%02X\n", \
(0xFF & *pu));
if (0x40 & *pu)
pfield_buffer->kount = 0x0000;
else
pfield_buffer->kount = 0x0001;
JOT(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) {
SAY("ERROR: bad peasycap->field_fill\n");
return;
}
if (FIELD_BUFFER_SIZE/PAGE_SIZE <= \
peasycap->field_page) {
SAY("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)) {
SAY("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) {
JOT(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;
}
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;
}
}
}
}
}
/*---------------------------------------------------------------------------*/
/*
*
*
* *** UNDER DEVELOPMENT/TESTING - NOT READY YET! ***
*
*
*
* VIDEOTAPES MAY HAVE BEEN MANUALLY PAUSED AND RESTARTED DURING RECORDING.
* THIS CAUSES LOSS OF SYNC, CONFUSING DOWNSTREAM USERSPACE PROGRAMS WHICH
* MAY INTERPRET THE INTERRUPTION AS A SYMPTOM OF LATENCY. TO OVERCOME THIS
* THE DRIVER BRIDGES THE HIATUS BY SENDING DUMMY VIDEO FRAMES AT ROUGHLY
* THE RIGHT TIME INTERVALS IN THE HOPE OF PERSUADING THE DOWNSTREAM USERSPACE
* PROGRAM TO RESUME NORMAL SERVICE WHEN THE INTERRUPTION IS OVER.
*/
/*---------------------------------------------------------------------------*/
#if defined(BRIDGER)
do_gettimeofday(&timeval);
if (peasycap->timeval7.tv_sec) {
usec = 1000000*(timeval.tv_sec - peasycap->timeval7.tv_sec) + \
(timeval.tv_usec - peasycap->timeval7.tv_usec);
if (usec > (peasycap->usec + peasycap->tolerate)) {
JOT(8, "bridging hiatus\n");
peasycap->video_junk = 0;
peasycap->field_buffer[peasycap->field_fill][0].kount |= 0x0400;
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;
JOT(8, "bumped to: %i=peasycap->field_fill %i=parity\n", \
peasycap->field_fill, 0x00FF & pfield_buffer->kount);
JOT(8, "field buffer %i has %i bytes to be overwritten\n", \
peasycap->field_read, videofieldamount);
JOT(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));
do_gettimeofday(&peasycap->timeval7);
}
}
#endif /*BRIDGER*/
/*---------------------------------------------------------------------------*/
/*
* 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) {
SAY("easycap driver shutting down on condition green\n");
peasycap->video_eof = 1;
peasycap->audio_eof = 1;
peasycap->video_junk = -VIDEO_ISOC_BUFFER_MANY;
wake_up_interruptible(&(peasycap->wq_video));
wake_up_interruptible(&(peasycap->wq_audio));
return;
}
if (peasycap->video_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (0 != rc) {
SAY("ERROR: while %i=video_idle, usb_submit_urb() failed " \
"with rc:\n", peasycap->video_idle);
switch (rc) {
case -ENOMEM: {
SAY("ENOMEM\n"); break;
}
case -ENODEV: {
SAY("ENODEV\n"); break;
}
case -ENXIO: {
SAY("ENXIO\n"); break;
}
case -EINVAL: {
SAY("EINVAL\n"); break;
}
case -EAGAIN: {
SAY("EAGAIN\n"); break;
}
case -EFBIG: {
SAY("EFBIG\n"); break;
}
case -EPIPE: {
SAY("EPIPE\n"); break;
}
case -EMSGSIZE: {
SAY("EMSGSIZE\n"); break;
}
default: {
SAY("0x%08X\n", rc); break;
}
}
}
}
return;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
*
* FIXME
*
*
* THIS FUNCTION ASSUMES THAT, ON EACH AND EVERY OCCASION THAT THE DEVICE IS
* PHYSICALLY PLUGGED IN, INTERFACE 0 IS PROBED FIRST.
* IF THIS IS NOT TRUE, THERE IS THE POSSIBILITY OF AN Oops.
*
* THIS HAS NEVER BEEN A PROBLEM IN PRACTICE, BUT SOMETHING SEEMS WRONG HERE.
*/
/*---------------------------------------------------------------------------*/
int
easycap_usb_probe(struct usb_interface *pusb_interface, \
const struct usb_device_id *id)
{
struct usb_device *pusb_device, *pusb_device1;
struct usb_host_interface *pusb_host_interface;
struct usb_endpoint_descriptor *pepd;
struct usb_interface_descriptor *pusb_interface_descriptor;
struct usb_interface_assoc_descriptor *pusb_interface_assoc_descriptor;
struct urb *purb;
static struct easycap *peasycap /*=NULL*/;
struct data_urb *pdata_urb;
size_t wMaxPacketSize;
int ISOCwMaxPacketSize;
int BULKwMaxPacketSize;
int INTwMaxPacketSize;
int CTRLwMaxPacketSize;
__u8 bEndpointAddress;
__u8 ISOCbEndpointAddress;
__u8 INTbEndpointAddress;
int isin, i, j, k, m;
__u8 bInterfaceNumber;
__u8 bInterfaceClass;
__u8 bInterfaceSubClass;
void *pbuf;
int okalt[8], isokalt;
int okepn[8], isokepn;
int okmps[8], isokmps;
int maxpacketsize;
int rc;
JOT(4, "\n");
if ((struct usb_interface *)NULL == pusb_interface) {
SAY("ERROR: pusb_interface is NULL\n");
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
/*
* GET POINTER TO STRUCTURE usb_device
*/
/*---------------------------------------------------------------------------*/
pusb_device1 = container_of(pusb_interface->dev.parent, \
struct usb_device, dev);
if ((struct usb_device *)NULL == pusb_device1) {
SAY("ERROR: pusb_device1 is NULL\n");
return -EFAULT;
}
pusb_device = usb_get_dev(pusb_device1);
if ((struct usb_device *)NULL == pusb_device) {
SAY("ERROR: pusb_device is NULL\n");
return -EFAULT;
}
if ((unsigned long int)pusb_device1 != (unsigned long int)pusb_device) {
JOT(4, "ERROR: pusb_device1 != pusb_device\n");
return -EFAULT;
}
JOT(4, "bNumConfigurations=%i\n", pusb_device->descriptor.bNumConfigurations);
/*---------------------------------------------------------------------------*/
pusb_host_interface = pusb_interface->cur_altsetting;
if (NULL == pusb_host_interface) {
SAY("ERROR: pusb_host_interface is NULL\n");
return -EFAULT;
}
pusb_interface_descriptor = &(pusb_host_interface->desc);
if (NULL == pusb_interface_descriptor) {
SAY("ERROR: pusb_interface_descriptor is NULL\n");
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
/*
* GET PROPERTIES OF PROBED INTERFACE
*/
/*---------------------------------------------------------------------------*/
bInterfaceNumber = pusb_interface_descriptor->bInterfaceNumber;
bInterfaceClass = pusb_interface_descriptor->bInterfaceClass;
bInterfaceSubClass = pusb_interface_descriptor->bInterfaceSubClass;
JOT(4, "intf[%i]: pusb_interface->num_altsetting=%i\n", \
bInterfaceNumber, pusb_interface->num_altsetting);
JOT(4, "intf[%i]: pusb_interface->cur_altsetting - " \
"pusb_interface->altsetting=%li\n", bInterfaceNumber, \
(long int)(pusb_interface->cur_altsetting - \
pusb_interface->altsetting));
switch (bInterfaceClass) {
case USB_CLASS_AUDIO: {
JOT(4, "intf[%i]: bInterfaceClass=0x%02X=USB_CLASS_AUDIO\n", \
bInterfaceNumber, bInterfaceClass); break;
}
case USB_CLASS_VIDEO: {
JOT(4, "intf[%i]: bInterfaceClass=0x%02X=USB_CLASS_VIDEO\n", \
bInterfaceNumber, bInterfaceClass); break;
}
case USB_CLASS_VENDOR_SPEC: {
JOT(4, "intf[%i]: bInterfaceClass=0x%02X=USB_CLASS_VENDOR_SPEC\n", \
bInterfaceNumber, bInterfaceClass); break;
}
default:
break;
}
switch (bInterfaceSubClass) {
case 0x01: {
JOT(4, "intf[%i]: bInterfaceSubClass=0x%02X=AUDIOCONTROL\n", \
bInterfaceNumber, bInterfaceSubClass); break;
}
case 0x02: {
JOT(4, "intf[%i]: bInterfaceSubClass=0x%02X=AUDIOSTREAMING\n", \
bInterfaceNumber, bInterfaceSubClass); break;
}
case 0x03: {
JOT(4, "intf[%i]: bInterfaceSubClass=0x%02X=MIDISTREAMING\n", \
bInterfaceNumber, bInterfaceSubClass); break;
}
default:
break;
}
/*---------------------------------------------------------------------------*/
pusb_interface_assoc_descriptor = pusb_interface->intf_assoc;
if (NULL != pusb_interface_assoc_descriptor) {
JOT(4, "intf[%i]: bFirstInterface=0x%02X bInterfaceCount=0x%02X\n", \
bInterfaceNumber, \
pusb_interface_assoc_descriptor->bFirstInterface, \
pusb_interface_assoc_descriptor->bInterfaceCount);
} else {
JOT(4, "intf[%i]: pusb_interface_assoc_descriptor is NULL\n", \
bInterfaceNumber);
}
/*---------------------------------------------------------------------------*/
/*
* 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 DEVICE WAS PHYSICALLY
* UNPLUGGED.
*/
/*---------------------------------------------------------------------------*/
if (0 == bInterfaceNumber) {
peasycap = kzalloc(sizeof(struct easycap), GFP_KERNEL);
if (NULL == peasycap) {
SAY("ERROR: Could not allocate peasycap\n");
return -ENOMEM;
} else {
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;
}
/*---------------------------------------------------------------------------*/
/*
* INITIALIZE THE NEW easycap STRUCTURE.
* NO PARAMETERS ARE SPECIFIED HERE REQUIRING THE SETTING OF REGISTERS.
* THAT IS DONE FIRST BY easycap_open() AND LATER BY easycap_ioctl().
*/
/*---------------------------------------------------------------------------*/
peasycap->pusb_device = pusb_device;
peasycap->pusb_interface = pusb_interface;
kref_init(&peasycap->kref);
JOT(8, "intf[%i]: after kref_init(..._video) " \
"%i=peasycap->kref.refcount.counter\n", \
bInterfaceNumber, peasycap->kref.refcount.counter);
init_waitqueue_head(&(peasycap->wq_video));
init_waitqueue_head(&(peasycap->wq_audio));
mutex_init(&(peasycap->mutex_timeval0));
mutex_init(&(peasycap->mutex_timeval1));
for (k = 0; k < FRAME_BUFFER_MANY; k++)
mutex_init(&(peasycap->mutex_mmap_video[k]));
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;
if ((struct mutex *)NULL == &(peasycap->mutex_mmap_video[0])) {
SAY("ERROR: &(peasycap->mutex_mmap_video[%i]) is NULL\n", 0);
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
/*
* DYNAMICALLY FILL IN THE AVAILABLE FORMATS.
*/
/*---------------------------------------------------------------------------*/
rc = fillin_formats();
if (0 > rc) {
SAY("ERROR: fillin_formats() returned %i\n", rc);
return -EFAULT;
}
JOT(4, "%i formats available\n", rc);
} else {
/*---------------------------------------------------------------------------*/
if ((struct easycap *)NULL == peasycap) {
SAY("ERROR: peasycap is NULL " \
"when probing interface %i\n", \
bInterfaceNumber);
return -EFAULT;
}
JOT(8, "kref_get() with %i=peasycap->kref.refcount.counter\n", \
(int)peasycap->kref.refcount.counter);
kref_get(&peasycap->kref);
}
/*---------------------------------------------------------------------------*/
if ((USB_CLASS_VIDEO == bInterfaceClass) || \
(USB_CLASS_VENDOR_SPEC == bInterfaceClass)) {
if (-1 == peasycap->video_interface) {
peasycap->video_interface = bInterfaceNumber;
JOT(4, "setting peasycap->video_interface=%i\n", \
peasycap->video_interface);
} else {
if (peasycap->video_interface != bInterfaceNumber) {
SAY("ERROR: attempting to reset " \
"peasycap->video_interface\n");
SAY("...... continuing with " \
"%i=peasycap->video_interface\n", \
peasycap->video_interface);
}
}
} else if ((USB_CLASS_AUDIO == bInterfaceClass) && \
(0x02 == bInterfaceSubClass)) {
if (-1 == peasycap->audio_interface) {
peasycap->audio_interface = bInterfaceNumber;
JOT(4, "setting peasycap->audio_interface=%i\n", \
peasycap->audio_interface);
} else {
if (peasycap->audio_interface != bInterfaceNumber) {
SAY("ERROR: attempting to reset " \
"peasycap->audio_interface\n");
SAY("...... 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;
isokepn = 0;
isokmps = 0;
for (i = 0; i < pusb_interface->num_altsetting; i++) {
pusb_host_interface = &(pusb_interface->altsetting[i]);
if ((struct usb_host_interface *)NULL == pusb_host_interface) {
SAY("ERROR: pusb_host_interface is NULL\n");
return -EFAULT;
}
pusb_interface_descriptor = &(pusb_host_interface->desc);
if ((struct usb_interface_descriptor *)NULL == \
pusb_interface_descriptor) {
SAY("ERROR: pusb_interface_descriptor is NULL\n");
return -EFAULT;
}
JOT(4, "intf[%i]alt[%i]: desc.bDescriptorType=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->bDescriptorType);
JOT(4, "intf[%i]alt[%i]: desc.bInterfaceNumber=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->bInterfaceNumber);
JOT(4, "intf[%i]alt[%i]: desc.bAlternateSetting=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->bAlternateSetting);
JOT(4, "intf[%i]alt[%i]: desc.bNumEndpoints=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->bNumEndpoints);
JOT(4, "intf[%i]alt[%i]: desc.bInterfaceClass=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->bInterfaceClass);
JOT(4, "intf[%i]alt[%i]: desc.bInterfaceSubClass=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->bInterfaceSubClass);
JOT(4, "intf[%i]alt[%i]: desc.bInterfaceProtocol=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->bInterfaceProtocol);
JOT(4, "intf[%i]alt[%i]: desc.iInterface=0x%02X\n", \
bInterfaceNumber, i, pusb_interface_descriptor->iInterface);
ISOCwMaxPacketSize = -1;
BULKwMaxPacketSize = -1;
INTwMaxPacketSize = -1;
CTRLwMaxPacketSize = -1;
ISOCbEndpointAddress = 0;
INTbEndpointAddress = 0;
if (0 == pusb_interface_descriptor->bNumEndpoints)
JOT(4, "intf[%i]alt[%i] has no endpoints\n", \
bInterfaceNumber, i);
/*---------------------------------------------------------------------------*/
for (j = 0; j < pusb_interface_descriptor->bNumEndpoints; j++) {
pepd = &(pusb_host_interface->endpoint[j].desc);
if ((struct usb_endpoint_descriptor *)NULL == pepd) {
SAY("ERROR: pepd is NULL.\n");
SAY("...... skipping\n");
continue;
}
wMaxPacketSize = le16_to_cpu(pepd->wMaxPacketSize);
bEndpointAddress = pepd->bEndpointAddress;
JOT(4, "intf[%i]alt[%i]end[%i]: bEndpointAddress=0x%X\n", \
bInterfaceNumber, i, j, \
pepd->bEndpointAddress);
JOT(4, "intf[%i]alt[%i]end[%i]: bmAttributes=0x%X\n", \
bInterfaceNumber, i, j, \
pepd->bmAttributes);
JOT(4, "intf[%i]alt[%i]end[%i]: wMaxPacketSize=%i\n", \
bInterfaceNumber, i, j, \
pepd->wMaxPacketSize);
JOT(4, "intf[%i]alt[%i]end[%i]: bInterval=%i\n",
bInterfaceNumber, i, j, \
pepd->bInterval);
if (pepd->bEndpointAddress & USB_DIR_IN) {
JOT(4, "intf[%i]alt[%i]end[%i] is an IN endpoint\n",\
bInterfaceNumber, i, j);
isin = 1;
} else {
JOT(4, "intf[%i]alt[%i]end[%i] is an OUT endpoint\n",\
bInterfaceNumber, i, j);
SAY("ERROR: OUT endpoint unexpected\n");
SAY("...... continuing\n");
isin = 0;
}
if ((pepd->bmAttributes & \
USB_ENDPOINT_XFERTYPE_MASK) == \
USB_ENDPOINT_XFER_ISOC) {
JOT(4, "intf[%i]alt[%i]end[%i] is an ISOC endpoint\n",\
bInterfaceNumber, i, j);
if (isin) {
switch (bInterfaceClass) {
case USB_CLASS_VIDEO:
case USB_CLASS_VENDOR_SPEC: {
if (!peasycap) {
SAY("MISTAKE: " \
"peasycap is NULL\n");
return -EFAULT;
}
if (pepd->wMaxPacketSize) {
if (8 > isokalt) {
okalt[isokalt] = i;
JOT(4,\
"%i=okalt[%i]\n", \
okalt[isokalt], \
isokalt);
isokalt++;
}
if (8 > isokepn) {
okepn[isokepn] = \
pepd->\
bEndpointAddress & \
0x0F;
JOT(4,\
"%i=okepn[%i]\n", \
okepn[isokepn], \
isokepn);
isokepn++;
}
if (8 > isokmps) {
okmps[isokmps] = \
le16_to_cpu(pepd->\
wMaxPacketSize);
JOT(4,\
"%i=okmps[%i]\n", \
okmps[isokmps], \
isokmps);
isokmps++;
}
} else {
if (-1 == peasycap->\
video_altsetting_off) {
peasycap->\
video_altsetting_off =\
i;
JOT(4, "%i=video_" \
"altsetting_off " \
"<====\n", \
peasycap->\
video_altsetting_off);
} else {
SAY("ERROR: peasycap" \
"->video_altsetting_" \
"off already set\n");
SAY("...... " \
"continuing with " \
"%i=peasycap->video_" \
"altsetting_off\n", \
peasycap->\
video_altsetting_off);
}
}
break;
}
case USB_CLASS_AUDIO: {
if (0x02 != bInterfaceSubClass)
break;
if (!peasycap) {
SAY("MISTAKE: " \
"peasycap is NULL\n");
return -EFAULT;
}
if (pepd->wMaxPacketSize) {
if (8 > isokalt) {
okalt[isokalt] = i ;
JOT(4,\
"%i=okalt[%i]\n", \
okalt[isokalt], \
isokalt);
isokalt++;
}
if (8 > isokepn) {
okepn[isokepn] = \
pepd->\
bEndpointAddress & \
0x0F;
JOT(4,\
"%i=okepn[%i]\n", \
okepn[isokepn], \
isokepn);
isokepn++;
}
if (8 > isokmps) {
okmps[isokmps] = \
le16_to_cpu(pepd->\
wMaxPacketSize);
JOT(4,\
"%i=okmps[%i]\n",\
okmps[isokmps], \
isokmps);
isokmps++;
}
} else {
if (-1 == peasycap->\
audio_altsetting_off) {
peasycap->\
audio_altsetting_off =\
i;
JOT(4, "%i=audio_" \
"altsetting_off " \
"<====\n", \
peasycap->\
audio_altsetting_off);
} else {
SAY("ERROR: peasycap" \
"->audio_altsetting_" \
"off already set\n");
SAY("...... " \
"continuing with " \
"%i=peasycap->\
audio_altsetting_" \
"off\n",
peasycap->\
audio_altsetting_off);
}
}
break;
}
default:
break;
}
}
} else if ((pepd->bmAttributes & \
USB_ENDPOINT_XFERTYPE_MASK) ==\
USB_ENDPOINT_XFER_BULK) {
JOT(4, "intf[%i]alt[%i]end[%i] is a BULK endpoint\n",\
bInterfaceNumber, i, j);
} else if ((pepd->bmAttributes & \
USB_ENDPOINT_XFERTYPE_MASK) ==\
USB_ENDPOINT_XFER_INT) {
JOT(4, "intf[%i]alt[%i]end[%i] is an INT endpoint\n",\
bInterfaceNumber, i, j);
} else {
JOT(4, "intf[%i]alt[%i]end[%i] is a CTRL endpoint\n",\
bInterfaceNumber, i, j);
}
if (0 == pepd->wMaxPacketSize) {
JOT(4, "intf[%i]alt[%i]end[%i] " \
"has zero packet size\n", \
bInterfaceNumber, i, j);
}
}
}
/*---------------------------------------------------------------------------*/
/*
* PERFORM INITIALIZATION OF THE PROBED INTERFACE
*/
/*---------------------------------------------------------------------------*/
JOT(4, "initialization begins for interface %i\n", \
pusb_interface_descriptor->bInterfaceNumber);
switch (bInterfaceNumber) {
/*---------------------------------------------------------------------------*/
/*
* INTERFACE 0 IS THE VIDEO INTERFACE
*/
/*---------------------------------------------------------------------------*/
case 0: {
if (!peasycap) {
SAY("MISTAKE: peasycap is NULL\n");
return -EFAULT;
}
if (!isokalt) {
SAY("ERROR: no viable video_altsetting_on\n");
return -ENOENT;
} else {
peasycap->video_altsetting_on = okalt[isokalt - 1];
JOT(4, "%i=video_altsetting_on <====\n", \
peasycap->video_altsetting_on);
}
if (!isokepn) {
SAY("ERROR: no viable video_endpointnumber\n");
return -ENOENT;
} else {
peasycap->video_endpointnumber = okepn[isokepn - 1];
JOT(4, "%i=video_endpointnumber\n", \
peasycap->video_endpointnumber);
}
if (!isokmps) {
SAY("ERROR: no viable video_maxpacketsize\n");
return -ENOENT;
/*---------------------------------------------------------------------------*/
/*
* DECIDE THE VIDEO STREAMING PARAMETERS
*/
/*---------------------------------------------------------------------------*/
} else {
maxpacketsize = okmps[isokmps - 1] - 1024;
if (USB_2_0_MAXPACKETSIZE > maxpacketsize) {
peasycap->video_isoc_maxframesize = maxpacketsize;
} else {
peasycap->video_isoc_maxframesize = \
USB_2_0_MAXPACKETSIZE;
}
JOT(4, "%i=video_isoc_maxframesize\n", \
peasycap->video_isoc_maxframesize);
if (0 >= peasycap->video_isoc_maxframesize) {
SAY("ERROR: bad video_isoc_maxframesize\n");
return -ENOENT;
}
peasycap->video_isoc_framesperdesc = VIDEO_ISOC_FRAMESPERDESC;
JOT(4, "%i=video_isoc_framesperdesc\n", \
peasycap->video_isoc_framesperdesc);
if (0 >= peasycap->video_isoc_framesperdesc) {
SAY("ERROR: bad video_isoc_framesperdesc\n");
return -ENOENT;
}
peasycap->video_isoc_buffer_size = \
peasycap->video_isoc_maxframesize * \
peasycap->video_isoc_framesperdesc;
JOT(4, "%i=video_isoc_buffer_size\n", \
peasycap->video_isoc_buffer_size);
if ((PAGE_SIZE << VIDEO_ISOC_ORDER) < \
peasycap->video_isoc_buffer_size) {
SAY("MISTAKE: " \
"peasycap->video_isoc_buffer_size too big\n");
return -EFAULT;
}
}
/*---------------------------------------------------------------------------*/
if (-1 == peasycap->video_interface) {
SAY("MISTAKE: video_interface is unset\n");
return -EFAULT;
}
if (-1 == peasycap->video_altsetting_on) {
SAY("MISTAKE: video_altsetting_on is unset\n");
return -EFAULT;
}
if (-1 == peasycap->video_altsetting_off) {
SAY("MISTAKE: video_interface_off is unset\n");
return -EFAULT;
}
if (-1 == peasycap->video_endpointnumber) {
SAY("MISTAKE: video_endpointnumber is unset\n");
return -EFAULT;
}
if (-1 == peasycap->video_isoc_maxframesize) {
SAY("MISTAKE: video_isoc_maxframesize is unset\n");
return -EFAULT;
}
if (-1 == peasycap->video_isoc_buffer_size) {
SAY("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);
/*---------------------------------------------------------------------------*/
JOT(4, "allocating %i frame buffers of size %li\n", \
FRAME_BUFFER_MANY, (long int)FRAME_BUFFER_SIZE);
JOT(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 ((void *)NULL != peasycap->frame_buffer[k][m].pgo)
SAY("attempting to reallocate frame " \
" buffers\n");
else {
pbuf = (void *)__get_free_page(GFP_KERNEL);
if ((void *)NULL == pbuf) {
SAY("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;
JOT(4, "allocation of frame buffers done: %i pages\n", k * \
m);
/*---------------------------------------------------------------------------*/
JOT(4, "allocating %i field buffers of size %li\n", \
FIELD_BUFFER_MANY, (long int)FIELD_BUFFER_SIZE);
JOT(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 ((void *)NULL != peasycap->field_buffer[k][m].pgo) {
SAY("ERROR: attempting to reallocate " \
"field buffers\n");
} else {
pbuf = (void *) __get_free_page(GFP_KERNEL);
if ((void *)NULL == pbuf) {
SAY("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;
JOT(4, "allocation of field buffers done: %i pages\n", k * \
m);
/*---------------------------------------------------------------------------*/
JOT(4, "allocating %i isoc video buffers of size %i\n", \
VIDEO_ISOC_BUFFER_MANY, \
peasycap->video_isoc_buffer_size);
JOT(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 (NULL == pbuf) {
SAY("ERROR: Could not allocate isoc video buffer " \
"%i\n", k);
return -ENOMEM;
} else
peasycap->allocation_video_page += \
((unsigned int)(0x01 << 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;
}
JOT(4, "allocation of isoc video buffers done: %i pages\n", \
k * (0x01 << VIDEO_ISOC_ORDER));
/*---------------------------------------------------------------------------*/
/*
* ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
*/
/*---------------------------------------------------------------------------*/
JOT(4, "allocating %i struct urb.\n", VIDEO_ISOC_BUFFER_MANY);
JOT(4, "using %i=peasycap->video_isoc_framesperdesc\n", \
peasycap->video_isoc_framesperdesc);
JOT(4, "using %i=peasycap->video_isoc_maxframesize\n", \
peasycap->video_isoc_maxframesize);
JOT(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 (NULL == purb) {
SAY("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 (NULL == pdata_urb) {
SAY("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) {
JOT(4, "initializing video urbs thus:\n");
JOT(4, " purb->interval = 1;\n");
JOT(4, " purb->dev = peasycap->pusb_device;\n");
JOT(4, " purb->pipe = usb_rcvisocpipe" \
"(peasycap->pusb_device,%i);\n", \
peasycap->video_endpointnumber);
JOT(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
JOT(4, " purb->transfer_buffer = peasycap->" \
"video_isoc_buffer[.].pgo;\n");
JOT(4, " purb->transfer_buffer_length = %i;\n", \
peasycap->video_isoc_buffer_size);
JOT(4, " purb->complete = easycap_complete;\n");
JOT(4, " purb->context = peasycap;\n");
JOT(4, " purb->start_frame = 0;\n");
JOT(4, " purb->number_of_packets = %i;\n", \
peasycap->video_isoc_framesperdesc);
JOT(4, " for (j = 0; j < %i; j++)\n", \
peasycap->video_isoc_framesperdesc);
JOT(4, " {\n");
JOT(4, " purb->iso_frame_desc[j].offset = j*%i;\n",\
peasycap->video_isoc_maxframesize);
JOT(4, " purb->iso_frame_desc[j].length = %i;\n", \
peasycap->video_isoc_maxframesize);
JOT(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;
}
}
JOT(4, "allocation of %i struct urb done.\n", k);
/*--------------------------------------------------------------------------*/
/*
* SAVE POINTER peasycap IN THIS INTERFACE.
*/
/*--------------------------------------------------------------------------*/
usb_set_intfdata(pusb_interface, peasycap);
/*--------------------------------------------------------------------------*/
/*
* THE VIDEO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
*/
/*--------------------------------------------------------------------------*/
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
if (0 != (usb_register_dev(pusb_interface, &easycap_class))) {
err("Not able to get a minor for this device");
usb_set_intfdata(pusb_interface, NULL);
return -ENODEV;
} else
(peasycap->registered_video)++;
SAY("easycap attached to minor #%d\n", pusb_interface->minor);
break;
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#else
pvideo_device = (struct video_device *)\
kzalloc(sizeof(struct video_device), GFP_KERNEL);
if ((struct video_device *)NULL == pvideo_device) {
SAY("ERROR: Could not allocate structure video_device\n");
return -ENOMEM;
}
if (VIDEO_DEVICE_MANY <= video_device_many) {
SAY("ERROR: Too many /dev/videos\n");
return -ENOMEM;
}
pvideo_array[video_device_many] = pvideo_device; video_device_many++;
strcpy(&pvideo_device->name[0], "easycapdc60");
#if defined(EASYCAP_NEEDS_V4L2_FOPS)
pvideo_device->fops = &v4l2_fops;
#else
pvideo_device->fops = &easycap_fops;
#endif /*EASYCAP_NEEDS_V4L2_FOPS*/
pvideo_device->minor = -1;
pvideo_device->release = (void *)(&videodev_release);
video_set_drvdata(pvideo_device, (void *)peasycap);
rc = video_register_device(pvideo_device, VFL_TYPE_GRABBER, -1);
if (0 != rc) {
err("Not able to register with videodev");
videodev_release(pvideo_device);
return -ENODEV;
} else {
peasycap->pvideo_device = pvideo_device;
(peasycap->registered_video)++;
JOT(4, "registered with videodev: %i=minor\n", \
pvideo_device->minor);
}
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
break;
}
/*--------------------------------------------------------------------------*/
/*
* INTERFACE 1 IS THE AUDIO CONTROL INTERFACE
* INTERFACE 2 IS THE AUDIO STREAMING INTERFACE
*/
/*--------------------------------------------------------------------------*/
case 1: {
/*--------------------------------------------------------------------------*/
/*
* SAVE POINTER peasycap IN INTERFACE 1
*/
/*--------------------------------------------------------------------------*/
usb_set_intfdata(pusb_interface, peasycap);
JOT(4, "no initialization required for interface %i\n", \
pusb_interface_descriptor->bInterfaceNumber);
break;
}
/*--------------------------------------------------------------------------*/
case 2: {
if (!peasycap) {
SAY("MISTAKE: peasycap is NULL\n");
return -EFAULT;
}
if (!isokalt) {
SAY("ERROR: no viable audio_altsetting_on\n");
return -ENOENT;
} else {
peasycap->audio_altsetting_on = okalt[isokalt - 1];
JOT(4, "%i=audio_altsetting_on <====\n", \
peasycap->audio_altsetting_on);
}
if (!isokepn) {
SAY("ERROR: no viable audio_endpointnumber\n");
return -ENOENT;
} else {
peasycap->audio_endpointnumber = okepn[isokepn - 1];
JOT(4, "%i=audio_endpointnumber\n", \
peasycap->audio_endpointnumber);
}
if (!isokmps) {
SAY("ERROR: no viable audio_maxpacketsize\n");
return -ENOENT;
} else {
peasycap->audio_isoc_maxframesize = okmps[isokmps - 1];
JOT(4, "%i=audio_isoc_maxframesize\n", \
peasycap->audio_isoc_maxframesize);
if (0 >= peasycap->audio_isoc_maxframesize) {
SAY("ERROR: bad audio_isoc_maxframesize\n");
return -ENOENT;
}
if (9 == peasycap->audio_isoc_maxframesize) {
peasycap->ilk |= 0x02;
SAY("hardware is FOUR-CVBS\n");
peasycap->microphone = true;
audio_pages_per_fragment = 2;
} else if (256 == peasycap->audio_isoc_maxframesize) {
peasycap->ilk &= ~0x02;
SAY("hardware is CVBS+S-VIDEO\n");
peasycap->microphone = false;
audio_pages_per_fragment = 4;
} else {
SAY("hardware is unidentified:\n");
SAY("%i=audio_isoc_maxframesize\n", \
peasycap->audio_isoc_maxframesize);
return -ENOENT;
}
audio_bytes_per_fragment = audio_pages_per_fragment * \
PAGE_SIZE ;
audio_buffer_page_many = (AUDIO_FRAGMENT_MANY * \
audio_pages_per_fragment);
JOT(4, "%6i=AUDIO_FRAGMENT_MANY\n", AUDIO_FRAGMENT_MANY);
JOT(4, "%6i=audio_pages_per_fragment\n", \
audio_pages_per_fragment);
JOT(4, "%6i=audio_bytes_per_fragment\n", \
audio_bytes_per_fragment);
JOT(4, "%6i=audio_buffer_page_many\n", audio_buffer_page_many);
peasycap->audio_isoc_framesperdesc = 128;
JOT(4, "%i=audio_isoc_framesperdesc\n", \
peasycap->audio_isoc_framesperdesc);
if (0 >= peasycap->audio_isoc_framesperdesc) {
SAY("ERROR: bad audio_isoc_framesperdesc\n");
return -ENOENT;
}
peasycap->audio_isoc_buffer_size = \
peasycap->audio_isoc_maxframesize * \
peasycap->audio_isoc_framesperdesc;
JOT(4, "%i=audio_isoc_buffer_size\n", \
peasycap->audio_isoc_buffer_size);
if (AUDIO_ISOC_BUFFER_SIZE < \
peasycap->audio_isoc_buffer_size) {
SAY("MISTAKE: audio_isoc_buffer_size bigger "
"than %li=AUDIO_ISOC_BUFFER_SIZE\n", \
AUDIO_ISOC_BUFFER_SIZE);
return -EFAULT;
}
}
if (-1 == peasycap->audio_interface) {
SAY("MISTAKE: audio_interface is unset\n");
return -EFAULT;
}
if (-1 == peasycap->audio_altsetting_on) {
SAY("MISTAKE: audio_altsetting_on is unset\n");
return -EFAULT;
}
if (-1 == peasycap->audio_altsetting_off) {
SAY("MISTAKE: audio_interface_off is unset\n");
return -EFAULT;
}
if (-1 == peasycap->audio_endpointnumber) {
SAY("MISTAKE: audio_endpointnumber is unset\n");
return -EFAULT;
}
if (-1 == peasycap->audio_isoc_maxframesize) {
SAY("MISTAKE: audio_isoc_maxframesize is unset\n");
return -EFAULT;
}
if (-1 == peasycap->audio_isoc_buffer_size) {
SAY("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);
JOT(4, "allocating an audio buffer\n");
JOT(4, ".... scattered over %i pages\n", audio_buffer_page_many);
for (k = 0; k < audio_buffer_page_many; k++) {
if ((void *)NULL != peasycap->audio_buffer[k].pgo) {
SAY("ERROR: attempting to reallocate audio buffers\n");
} else {
pbuf = (void *) __get_free_page(GFP_KERNEL);
if ((void *)NULL == pbuf) {
SAY("ERROR: Could not allocate audio " \
"buffer page %i\n", k);
return -ENOMEM;
} else
peasycap->allocation_audio_page += 1;
peasycap->audio_buffer[k].pgo = pbuf;
}
peasycap->audio_buffer[k].pto = peasycap->audio_buffer[k].pgo;
}
peasycap->audio_fill = 0;
peasycap->audio_read = 0;
JOT(4, "allocation of audio buffer done: %i pages\n", k);
/*---------------------------------------------------------------------------*/
JOT(4, "allocating %i isoc audio buffers of size %i\n", \
AUDIO_ISOC_BUFFER_MANY, peasycap->audio_isoc_buffer_size);
JOT(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 (NULL == pbuf) {
SAY("ERROR: Could not allocate isoc audio buffer " \
"%i\n", k);
return -ENOMEM;
} else
peasycap->allocation_audio_page += \
((unsigned int)(0x01 << 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;
}
JOT(4, "allocation of isoc audio buffers done.\n");
/*---------------------------------------------------------------------------*/
/*
* ALLOCATE AND INITIALIZE MULTIPLE struct urb ...
*/
/*---------------------------------------------------------------------------*/
JOT(4, "allocating %i struct urb.\n", AUDIO_ISOC_BUFFER_MANY);
JOT(4, "using %i=peasycap->audio_isoc_framesperdesc\n", \
peasycap->audio_isoc_framesperdesc);
JOT(4, "using %i=peasycap->audio_isoc_maxframesize\n", \
peasycap->audio_isoc_maxframesize);
JOT(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 (NULL == purb) {
SAY("ERROR: usb_alloc_urb returned NULL for buffer " \
"%i\n", k);
return -ENOMEM;
} else
peasycap->allocation_audio_urb += 1 ;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
pdata_urb = kzalloc(sizeof(struct data_urb), GFP_KERNEL);
if (NULL == pdata_urb) {
SAY("ERROR: Could not allocate struct data_urb.\n");
return -ENOMEM;
} else
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) {
JOT(4, "initializing audio urbs thus:\n");
JOT(4, " purb->interval = 1;\n");
JOT(4, " purb->dev = peasycap->pusb_device;\n");
JOT(4, " purb->pipe = usb_rcvisocpipe(peasycap->" \
"pusb_device,%i);\n", \
peasycap->audio_endpointnumber);
JOT(4, " purb->transfer_flags = URB_ISO_ASAP;\n");
JOT(4, " purb->transfer_buffer = " \
"peasycap->audio_isoc_buffer[.].pgo;\n");
JOT(4, " purb->transfer_buffer_length = %i;\n", \
peasycap->audio_isoc_buffer_size);
JOT(4, " purb->complete = easysnd_complete;\n");
JOT(4, " purb->context = peasycap;\n");
JOT(4, " purb->start_frame = 0;\n");
JOT(4, " purb->number_of_packets = %i;\n", \
peasycap->audio_isoc_framesperdesc);
JOT(4, " for (j = 0; j < %i; j++)\n", \
peasycap->audio_isoc_framesperdesc);
JOT(4, " {\n");
JOT(4, " purb->iso_frame_desc[j].offset = j*%i;\n",\
peasycap->audio_isoc_maxframesize);
JOT(4, " purb->iso_frame_desc[j].length = %i;\n", \
peasycap->audio_isoc_maxframesize);
JOT(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 = easysnd_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;
}
}
JOT(4, "allocation of %i struct urb done.\n", k);
/*---------------------------------------------------------------------------*/
/*
* SAVE POINTER peasycap IN THIS INTERFACE.
*/
/*---------------------------------------------------------------------------*/
usb_set_intfdata(pusb_interface, peasycap);
/*---------------------------------------------------------------------------*/
/*
* THE AUDIO DEVICE CAN BE REGISTERED NOW, AS IT IS READY.
*/
/*---------------------------------------------------------------------------*/
rc = usb_register_dev(pusb_interface, &easysnd_class);
if (0 != rc) {
err("Not able to get a minor for this device.");
usb_set_intfdata(pusb_interface, NULL);
return -ENODEV;
} else
(peasycap->registered_audio)++;
/*---------------------------------------------------------------------------*/
/*
* LET THE USER KNOW WHAT NODE THE AUDIO DEVICE IS ATTACHED TO.
*/
/*---------------------------------------------------------------------------*/
SAY("easysnd attached to minor #%d\n", pusb_interface->minor);
break;
}
/*---------------------------------------------------------------------------*/
/*
* INTERFACES OTHER THAN 0, 1 AND 2 ARE UNEXPECTED
*/
/*---------------------------------------------------------------------------*/
default: {
JOT(4, "ERROR: unexpected interface %i\n", bInterfaceNumber);
return -EINVAL;
}
}
JOT(4, "ends successfully for interface %i\n", \
pusb_interface_descriptor->bInterfaceNumber);
return 0;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* WHEN THIS FUNCTION IS CALLED THE DEVICE HAS ALREADY BEEN PHYSICALLY
* UNPLUGGED.
* HENCE peasycap->pusb_device IS NO LONGER VALID AND MUST BE SET TO NULL.
*/
/*---------------------------------------------------------------------------*/
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;
JOT(4, "\n");
if ((struct usb_interface *)NULL == pusb_interface) {
JOT(4, "ERROR: pusb_interface is NULL\n");
return;
}
pusb_host_interface = pusb_interface->cur_altsetting;
if ((struct usb_host_interface *)NULL == pusb_host_interface) {
JOT(4, "ERROR: pusb_host_interface is NULL\n");
return;
}
pusb_interface_descriptor = &(pusb_host_interface->desc);
if ((struct usb_interface_descriptor *)NULL == 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);
peasycap = usb_get_intfdata(pusb_interface);
if ((struct easycap *)NULL == peasycap)
SAY("ERROR: peasycap is NULL\n");
else {
peasycap->pusb_device = (struct usb_device *)NULL;
switch (bInterfaceNumber) {
/*---------------------------------------------------------------------------*/
case 0: {
if ((struct list_head *)NULL != peasycap->purb_video_head) {
JOT(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 ((struct data_urb *)NULL != pdata_urb) {
if ((struct urb *)NULL != \
pdata_urb->purb) {
usb_kill_urb(pdata_urb->purb);
m++;
}
}
}
JOT(4, "%i video urbs killed\n", m);
} else
SAY("ERROR: peasycap->purb_video_head is NULL\n");
break;
}
/*---------------------------------------------------------------------------*/
case 2: {
if ((struct list_head *)NULL != peasycap->purb_audio_head) {
JOT(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 ((struct data_urb *)NULL != pdata_urb) {
if ((struct urb *)NULL != \
pdata_urb->purb) {
usb_kill_urb(pdata_urb->purb);
m++;
}
}
}
JOT(4, "%i audio urbs killed\n", m);
} else
SAY("ERROR: peasycap->purb_audio_head is NULL\n");
break;
}
/*---------------------------------------------------------------------------*/
default:
break;
}
}
/*--------------------------------------------------------------------------*/
/*
* DEREGISTER
*/
/*--------------------------------------------------------------------------*/
switch (bInterfaceNumber) {
case 0: {
#if (!defined(EASYCAP_IS_VIDEODEV_CLIENT))
if ((struct easycap *)NULL == peasycap) {
SAY("ERROR: peasycap has become NULL\n");
} else {
lock_kernel();
usb_deregister_dev(pusb_interface, &easycap_class);
(peasycap->registered_video)--;
JOT(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber);
unlock_kernel();
SAY("easycap detached from minor #%d\n", minor);
}
/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/
#else
if ((struct easycap *)NULL == peasycap)
SAY("ERROR: peasycap has become NULL\n");
else {
lock_kernel();
video_unregister_device(peasycap->pvideo_device);
(peasycap->registered_video)--;
unlock_kernel();
JOT(4, "unregistered with videodev: %i=minor\n", \
pvideo_device->minor);
}
/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/
#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/
break;
}
case 2: {
lock_kernel();
usb_deregister_dev(pusb_interface, &easysnd_class);
if ((struct easycap *)NULL != peasycap)
(peasycap->registered_audio)--;
JOT(4, "intf[%i]: usb_deregister_dev()\n", bInterfaceNumber);
unlock_kernel();
SAY("easysnd detached from minor #%d\n", minor);
break;
}
default:
break;
}
/*---------------------------------------------------------------------------*/
/*
* CALL easycap_delete() IF NO REMAINING REFERENCES TO peasycap
*/
/*---------------------------------------------------------------------------*/
if ((struct easycap *)NULL == peasycap) {
SAY("ERROR: peasycap has become NULL\n");
SAY("cannot call kref_put()\n");
SAY("ending unsuccessfully: may cause memory leak\n");
return;
}
if (!peasycap->kref.refcount.counter) {
SAY("ERROR: peasycap->kref.refcount.counter is zero " \
"so cannot call kref_put()\n");
SAY("ending unsuccessfully: may cause memory leak\n");
return;
}
JOT(4, "intf[%i]: kref_put() with %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);
/*---------------------------------------------------------------------------*/
JOT(4, "ends\n");
return;
}
/*****************************************************************************/
int __init
easycap_module_init(void)
{
int result;
SAY("========easycap=======\n");
JOT(4, "begins. %i=debug\n", easycap_debug);
SAY("version: " EASYCAP_DRIVER_VERSION "\n");
/*---------------------------------------------------------------------------*/
/*
* REGISTER THIS DRIVER WITH THE USB SUBSYTEM.
*/
/*---------------------------------------------------------------------------*/
JOT(4, "registering driver easycap\n");
result = usb_register(&easycap_usb_driver);
if (0 != result)
SAY("ERROR: usb_register returned %i\n", result);
JOT(4, "ends\n");
return result;
}
/*****************************************************************************/
void __exit
easycap_module_exit(void)
{
JOT(4, "begins\n");
/*---------------------------------------------------------------------------*/
/*
* DEREGISTER THIS DRIVER WITH THE USB SUBSYTEM.
*/
/*---------------------------------------------------------------------------*/
usb_deregister(&easycap_usb_driver);
JOT(4, "ends\n");
}
/*****************************************************************************/
module_init(easycap_module_init);
module_exit(easycap_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("R.M. Thomas <rmthomas@sciolus.org>");
MODULE_DESCRIPTION(EASYCAP_DRIVER_DESCRIPTION);
MODULE_VERSION(EASYCAP_DRIVER_VERSION);
#if defined(EASYCAP_DEBUG)
MODULE_PARM_DESC(easycap_debug, "debug: 0 (default), 1, 2,...");
#endif /*EASYCAP_DEBUG*/
/*****************************************************************************/
/******************************************************************************
* *
* 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"
#include "easycap_debug.h"
/*---------------------------------------------------------------------------*/
/*
* THE LEAST SIGNIFICANT BIT OF easycap_standard.mask HAS MEANING:
* 0 => 25 fps
* 1 => 30 fps
*/
/*---------------------------------------------------------------------------*/
struct easycap_standard easycap_standard[] = {
{
.mask = 0x000F & 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 = 0x000F & 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 = 0x000F & 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 = 0x000F & 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 = 0x000F & SECAM ,
.v4l2_standard = {
.index = SECAM,
.id = V4L2_STD_SECAM,
.name = "SECAM",
.frameperiod = {1, 25},
.framelines = 625,
.reserved = {0, 0, 0, 0}
}
},
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
{
.mask = 0x000F & 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 = 0x000F & 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 = 0x000F & 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 = 0x000F & 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 = 0x000F & 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 = 0xFFFF
}
};
/*---------------------------------------------------------------------------*/
/*
* THE 16-BIT easycap_format.mask HAS MEANING:
* (least significant) BIT 0: 0 => PAL, 25 FPS; 1 => NTSC, 30 FPS
* BITS 1-3: RESERVED FOR DIFFERENTIATING STANDARDS
* BITS 4-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
* (most significant) BITS 13-15: RESERVED FOR OTHER FIELD ORDER OPTIONS
* IT FOLLOWS THAT:
* bytesperpixel IS ((0x00F0 & easycap_format.mask) >> 4)
* 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)
{
int i, j, k, m, n;
__u32 width, height, pixelformat, bytesperline, sizeimage;
__u32 field, colorspace;
__u16 mask1, mask2, mask3, mask4;
char name1[32], name2[32], name3[32], name4[32];
for (i = 0, n = 0; i < STANDARD_MANY; i++) {
mask1 = 0x0000;
switch (i) {
case PAL_BGHIN: {
mask1 = PAL_BGHIN;
strcpy(&name1[0], "PAL_BGHIN");
colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
break;
}
case SECAM: {
mask1 = SECAM;
strcpy(&name1[0], "SECAM");
colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
break;
}
case PAL_Nc: {
mask1 = PAL_Nc;
strcpy(&name1[0], "PAL_Nc");
colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
break;
}
case PAL_60: {
mask1 = PAL_60;
strcpy(&name1[0], "PAL_60");
colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
break;
}
case PAL_M: {
mask1 = PAL_M;
strcpy(&name1[0], "PAL_M");
colorspace = V4L2_COLORSPACE_470_SYSTEM_BG;
break;
}
case NTSC_M: {
mask1 = NTSC_M;
strcpy(&name1[0], "NTSC_M");
colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
break;
}
case NTSC_443: {
mask1 = NTSC_443;
strcpy(&name1[0], "NTSC_443");
colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
break;
}
case NTSC_M_JP: {
mask1 = NTSC_M_JP;
strcpy(&name1[0], "NTSC_M_JP");
colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
break;
}
case NTSC_N: {
mask1 = NTSC_M;
strcpy(&name1[0], "NTSC_N");
colorspace = V4L2_COLORSPACE_470_SYSTEM_M;
break;
}
case NTSC_N_443: {
mask1 = NTSC_N_443;
strcpy(&name1[0], "NTSC_N_443");
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;
strcpy(&name2[0], "_AT_720x576");
width = 720; height = 576; break;
}
case AT_704x576: {
if (0x1 & mask1)
continue;
strcpy(&name2[0], "_AT_704x576");
width = 704; height = 576; break;
}
case AT_640x480: {
strcpy(&name2[0], "_AT_640x480");
width = 640; height = 480; break;
}
case AT_720x480: {
if (!(0x1 & mask1))
continue;
strcpy(&name2[0], "_AT_720x480");
width = 720; height = 480; break;
}
case AT_360x288: {
if (0x1 & mask1)
continue;
strcpy(&name2[0], "_AT_360x288");
width = 360; height = 288; mask2 = 0x0800; break;
}
case AT_320x240: {
strcpy(&name2[0], "_AT_320x240");
width = 320; height = 240; mask2 = 0x0800; break;
}
case AT_360x240: {
if (!(0x1 & mask1))
continue;
strcpy(&name2[0], "_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: {
strcpy(&name3[0], "_" STRINGIZE(FMT_UYVY));
pixelformat = V4L2_PIX_FMT_UYVY;
mask3 |= (0x02 << 4);
break;
}
case FMT_YUY2: {
strcpy(&name3[0], "_" STRINGIZE(FMT_YUY2));
pixelformat = V4L2_PIX_FMT_YUYV;
mask3 |= (0x02 << 4);
mask3 |= 0x0100;
break;
}
case FMT_RGB24: {
strcpy(&name3[0], "_" STRINGIZE(FMT_RGB24));
pixelformat = V4L2_PIX_FMT_RGB24;
mask3 |= (0x03 << 4);
break;
}
case FMT_RGB32: {
strcpy(&name3[0], "_" STRINGIZE(FMT_RGB32));
pixelformat = V4L2_PIX_FMT_RGB32;
mask3 |= (0x04 << 4);
break;
}
case FMT_BGR24: {
strcpy(&name3[0], "_" STRINGIZE(FMT_BGR24));
pixelformat = V4L2_PIX_FMT_BGR24;
mask3 |= (0x03 << 4);
mask3 |= 0x0100;
break;
}
case FMT_BGR32: {
strcpy(&name3[0], "_" STRINGIZE(FMT_BGR32));
pixelformat = V4L2_PIX_FMT_BGR32;
mask3 |= (0x04 << 4);
mask3 |= 0x0100;
break;
}
default:
return -3;
}
bytesperline = width * ((mask3 & 0x00F0) >> 4);
sizeimage = bytesperline * height;
for (m = 0; m < INTERLACE_MANY; m++) {
mask4 = 0x0000;
switch (m) {
case FIELD_NONE: {
strcpy(&name4[0], "-n");
field = V4L2_FIELD_NONE;
break;
}
case FIELD_INTERLACED: {
strcpy(&name4[0], "-i");
field = V4L2_FIELD_INTERLACED;
break;
}
case FIELD_ALTERNATE: {
strcpy(&name4[0], "-a");
mask4 |= 0x1000;
field = V4L2_FIELD_ALTERNATE;
break;
}
default:
return -4;
}
if (SETTINGS_MANY <= n)
return -5;
strcpy(&easycap_format[n].name[0], &name1[0]);
strcat(&easycap_format[n].name[0], &name2[0]);
strcat(&easycap_format[n].name[0], &name3[0]);
strcat(&easycap_format[n].name[0], &name4[0]);
easycap_format[n].mask = \
mask1 | mask2 | mask3 | mask4;
easycap_format[n].v4l2_format\
.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
easycap_format[n].v4l2_format\
.fmt.pix.width = width;
easycap_format[n].v4l2_format\
.fmt.pix.height = height;
easycap_format[n].v4l2_format\
.fmt.pix.pixelformat = pixelformat;
easycap_format[n].v4l2_format\
.fmt.pix.field = field;
easycap_format[n].v4l2_format\
.fmt.pix.bytesperline = bytesperline;
easycap_format[n].v4l2_format\
.fmt.pix.sizeimage = sizeimage;
easycap_format[n].v4l2_format\
.fmt.pix.colorspace = colorspace;
easycap_format[n].v4l2_format\
.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
}
};
/*****************************************************************************/
/******************************************************************************
* *
* 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"
#include "easycap_debug.h"
#include "easycap_sound.h"
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* ON COMPLETION OF AN AUDIO URB ITS DATA IS COPIED TO THE AUDIO BUFFERS
* PROVIDED peasycap->audio_idle IS ZER0. REGARDLESS OF THIS BEING TRUE,
* IT IS RESUBMITTED PROVIDED peasycap->audio_isoc_streaming IS NOT ZERO.
*/
/*---------------------------------------------------------------------------*/
void
easysnd_complete(struct urb *purb)
{
static int mt;
struct easycap *peasycap;
struct data_buffer *paudio_buffer;
char errbuf[16];
__u8 *p1, *p2;
__s16 s16;
int i, j, more, much, leap, rc;
JOT(16, "\n");
if (NULL == purb) {
SAY("ERROR: purb is NULL\n");
return;
}
peasycap = purb->context;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
return;
}
much = 0;
if (peasycap->audio_idle) {
JOT(16, "%i=audio_idle %i=audio_isoc_streaming\n", \
peasycap->audio_idle, peasycap->audio_isoc_streaming);
if (peasycap->audio_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (0 != rc) {
SAY("ERROR: while %i=audio_idle, " \
"usb_submit_urb() failed with rc:\n", \
peasycap->audio_idle);
switch (rc) {
case -ENOMEM: {
SAY("ENOMEM\n"); break;
}
case -ENODEV: {
SAY("ENODEV\n"); break;
}
case -ENXIO: {
SAY("ENXIO\n"); break;
}
case -EINVAL: {
SAY("EINVAL\n"); break;
}
case -EAGAIN: {
SAY("EAGAIN\n"); break;
}
case -EFBIG: {
SAY("EFBIG\n"); break;
}
case -EPIPE: {
SAY("EPIPE\n"); break;
}
case -EMSGSIZE: {
SAY("EMSGSIZE\n"); break;
}
default: {
SAY("0x%08X\n", rc); break;
}
}
}
}
return;
}
/*---------------------------------------------------------------------------*/
if (purb->status) {
if (-ESHUTDOWN == purb->status) {
JOT(16, "immediate return because -ESHUTDOWN=purb->status\n");
return;
}
SAY("ERROR: non-zero urb status:\n");
switch (purb->status) {
case -EINPROGRESS: {
SAY("-EINPROGRESS\n"); break;
}
case -ENOSR: {
SAY("-ENOSR\n"); break;
}
case -EPIPE: {
SAY("-EPIPE\n"); break;
}
case -EOVERFLOW: {
SAY("-EOVERFLOW\n"); break;
}
case -EPROTO: {
SAY("-EPROTO\n"); break;
}
case -EILSEQ: {
SAY("-EILSEQ\n"); break;
}
case -ETIMEDOUT: {
SAY("-ETIMEDOUT\n"); break;
}
case -EMSGSIZE: {
SAY("-EMSGSIZE\n"); break;
}
case -EOPNOTSUPP: {
SAY("-EOPNOTSUPP\n"); break;
}
case -EPFNOSUPPORT: {
SAY("-EPFNOSUPPORT\n"); break;
}
case -EAFNOSUPPORT: {
SAY("-EAFNOSUPPORT\n"); break;
}
case -EADDRINUSE: {
SAY("-EADDRINUSE\n"); break;
}
case -EADDRNOTAVAIL: {
SAY("-EADDRNOTAVAIL\n"); break;
}
case -ENOBUFS: {
SAY("-ENOBUFS\n"); break;
}
case -EISCONN: {
SAY("-EISCONN\n"); break;
}
case -ENOTCONN: {
SAY("-ENOTCONN\n"); break;
}
case -ESHUTDOWN: {
SAY("-ESHUTDOWN\n"); break;
}
case -ENOENT: {
SAY("-ENOENT\n"); break;
}
case -ECONNRESET: {
SAY("-ECONNRESET\n"); break;
}
default: {
SAY("unknown error code 0x%08X\n", purb->status); break;
}
}
/*---------------------------------------------------------------------------*/
/*
* RESUBMIT THIS URB AFTER AN ERROR
*
* (THIS IS DUPLICATE CODE TO REDUCE INDENTATION OF THE NO-ERROR PATH)
*/
/*---------------------------------------------------------------------------*/
if (peasycap->audio_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (0 != rc) {
SAY("ERROR: while %i=audio_idle, usb_submit_urb() "
"failed with rc:\n", peasycap->audio_idle);
switch (rc) {
case -ENOMEM: {
SAY("ENOMEM\n"); break;
}
case -ENODEV: {
SAY("ENODEV\n"); break;
}
case -ENXIO: {
SAY("ENXIO\n"); break;
}
case -EINVAL: {
SAY("EINVAL\n"); break;
}
case -EAGAIN: {
SAY("EAGAIN\n"); break;
}
case -EFBIG: {
SAY("EFBIG\n"); break;
}
case -EPIPE: {
SAY("EPIPE\n"); break;
}
case -EMSGSIZE: {
SAY("EMSGSIZE\n"); break;
}
default: {
SAY("0x%08X\n", rc); break;
}
}
}
}
return;
}
/*---------------------------------------------------------------------------*/
/*
* PROCEED HERE WHEN NO ERROR
*/
/*---------------------------------------------------------------------------*/
for (i = 0; i < purb->number_of_packets; i++) {
switch (purb->iso_frame_desc[i].status) {
case 0: {
strcpy(&errbuf[0], "OK"); break;
}
case -ENOENT: {
strcpy(&errbuf[0], "-ENOENT"); break;
}
case -EINPROGRESS: {
strcpy(&errbuf[0], "-EINPROGRESS"); break;
}
case -EPROTO: {
strcpy(&errbuf[0], "-EPROTO"); break;
}
case -EILSEQ: {
strcpy(&errbuf[0], "-EILSEQ"); break;
}
case -ETIME: {
strcpy(&errbuf[0], "-ETIME"); break;
}
case -ETIMEDOUT: {
strcpy(&errbuf[0], "-ETIMEDOUT"); break;
}
case -EPIPE: {
strcpy(&errbuf[0], "-EPIPE"); break;
}
case -ECOMM: {
strcpy(&errbuf[0], "-ECOMM"); break;
}
case -ENOSR: {
strcpy(&errbuf[0], "-ENOSR"); break;
}
case -EOVERFLOW: {
strcpy(&errbuf[0], "-EOVERFLOW"); break;
}
case -EREMOTEIO: {
strcpy(&errbuf[0], "-EREMOTEIO"); break;
}
case -ENODEV: {
strcpy(&errbuf[0], "-ENODEV"); break;
}
case -EXDEV: {
strcpy(&errbuf[0], "-EXDEV"); break;
}
case -EINVAL: {
strcpy(&errbuf[0], "-EINVAL"); break;
}
case -ECONNRESET: {
strcpy(&errbuf[0], "-ECONNRESET"); break;
}
case -ESHUTDOWN: {
strcpy(&errbuf[0], "-ESHUTDOWN"); break;
}
default: {
strcpy(&errbuf[0], "UNKNOWN"); break;
}
}
if ((!purb->iso_frame_desc[i].status) && 0) {
JOT(16, "frame[%2i]: %i=status{=%16s} " \
"%5i=actual " \
"%5i=length " \
"%3i=offset\n", \
i, purb->iso_frame_desc[i].status, &errbuf[0],
purb->iso_frame_desc[i].actual_length,
purb->iso_frame_desc[i].length,
purb->iso_frame_desc[i].offset);
}
if (!purb->iso_frame_desc[i].status) {
more = purb->iso_frame_desc[i].actual_length;
#if defined(TESTTONE)
if (!more)
more = purb->iso_frame_desc[i].length;
#endif
if (!more)
mt++;
else {
if (mt) {
JOT(16, "%4i empty audio urb frames\n", mt);
mt = 0;
}
p1 = (__u8 *)(purb->transfer_buffer + \
purb->iso_frame_desc[i].offset);
leap = 0;
p1 += leap;
more -= leap;
/*---------------------------------------------------------------------------*/
/*
* COPY more BYTES FROM ISOC BUFFER TO AUDIO BUFFER,
* CONVERTING 8-BIT SAMPLES TO 16-BIT SIGNED LITTLE-ENDED SAMPLES IF NECESSARY
*/
/*---------------------------------------------------------------------------*/
while (more) {
if (0 > more) {
SAY("easysnd_complete: MISTAKE: " \
"more is negative\n");
return;
}
if (audio_buffer_page_many <= \
peasycap->audio_fill) {
SAY("ERROR: bad " \
"peasycap->audio_fill\n");
return;
}
paudio_buffer = &peasycap->audio_buffer\
[peasycap->audio_fill];
if (PAGE_SIZE < (paudio_buffer->pto - \
paudio_buffer->pgo)) {
SAY("ERROR: bad paudio_buffer->pto\n");
return;
}
if (PAGE_SIZE == (paudio_buffer->pto - \
paudio_buffer->pgo)) {
#if defined(TESTTONE)
easysnd_testtone(peasycap, \
peasycap->audio_fill);
#endif /*TESTTONE*/
paudio_buffer->pto = \
paudio_buffer->pgo;
(peasycap->audio_fill)++;
if (audio_buffer_page_many <= \
peasycap->audio_fill)
peasycap->audio_fill = 0;
JOT(12, "bumped peasycap->" \
"audio_fill to %i\n", \
peasycap->audio_fill);
paudio_buffer = &peasycap->\
audio_buffer\
[peasycap->audio_fill];
paudio_buffer->pto = \
paudio_buffer->pgo;
if (!(peasycap->audio_fill % \
audio_pages_per_fragment)) {
JOT(12, "wakeup call on wq_" \
"audio, %i=frag reading %i" \
"=fragment fill\n", \
(peasycap->audio_read / \
audio_pages_per_fragment), \
(peasycap->audio_fill / \
audio_pages_per_fragment));
wake_up_interruptible\
(&(peasycap->wq_audio));
}
}
much = PAGE_SIZE - (int)(paudio_buffer->pto -\
paudio_buffer->pgo);
if (much % 2)
JOT(8, "MISTAKE? much is odd\n");
if (false == peasycap->microphone) {
if (much > more)
much = more;
memcpy(paudio_buffer->pto, p1, much);
p1 += much;
more -= much;
} else {
if (much > (2 * more))
much = 2 * more;
p2 = (__u8 *)paudio_buffer->pto;
for (j = 0; j < (much / 2); j++) {
s16 = ((int) *p1) - 128;
*p2 = (0xFF00 & s16) >> 8;
*(p2 + 1) = (0x00FF & s16);
p1++; p2 += 2;
more--;
}
}
(paudio_buffer->pto) += much;
}
}
} else {
JOT(12, "discarding audio samples because " \
"%i=purb->iso_frame_desc[i].status\n", \
purb->iso_frame_desc[i].status);
}
}
/*---------------------------------------------------------------------------*/
/*
* RESUBMIT THIS URB AFTER NO ERROR
*/
/*---------------------------------------------------------------------------*/
if (peasycap->audio_isoc_streaming) {
rc = usb_submit_urb(purb, GFP_ATOMIC);
if (0 != rc) {
SAY("ERROR: while %i=audio_idle, usb_submit_urb() failed " \
"with rc:\n", peasycap->audio_idle);
switch (rc) {
case -ENOMEM: {
SAY("ENOMEM\n"); break;
}
case -ENODEV: {
SAY("ENODEV\n"); break;
}
case -ENXIO: {
SAY("ENXIO\n"); break;
}
case -EINVAL: {
SAY("EINVAL\n"); break;
}
case -EAGAIN: {
SAY("EAGAIN\n"); break;
}
case -EFBIG: {
SAY("EFBIG\n"); break;
}
case -EPIPE: {
SAY("EPIPE\n"); break;
}
case -EMSGSIZE: {
SAY("EMSGSIZE\n"); break;
}
default: {
SAY("0x%08X\n", rc); break;
}
}
}
}
return;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* THE AUDIO URBS ARE SUBMITTED AT THIS EARLY STAGE SO THAT IT IS POSSIBLE TO
* STREAM FROM /dev/easysnd1 WITH SIMPLE PROGRAMS SUCH AS cat WHICH DO NOT
* HAVE AN IOCTL INTERFACE. THE VIDEO URBS, BY CONTRAST, MUST BE SUBMITTED
* MUCH LATER: SEE COMMENTS IN FILE easycap_main.c.
*/
/*---------------------------------------------------------------------------*/
int
easysnd_open(struct inode *inode, struct file *file)
{
struct usb_interface *pusb_interface;
struct easycap *peasycap;
int subminor, rc;
JOT(4, "begins.\n");
subminor = iminor(inode);
pusb_interface = usb_find_interface(&easycap_usb_driver, subminor);
if (NULL == pusb_interface) {
SAY("ERROR: pusb_interface is NULL\n");
SAY("ending unsuccessfully\n");
return -1;
}
peasycap = usb_get_intfdata(pusb_interface);
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL\n");
SAY("ending unsuccessfully\n");
return -1;
}
file->private_data = peasycap;
/*---------------------------------------------------------------------------*/
/*
* INITIALIZATION.
*/
/*---------------------------------------------------------------------------*/
JOT(4, "starting initialization\n");
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
} else {
JOT(16, "0x%08lX=peasycap->pusb_device\n", \
(long int)peasycap->pusb_device);
}
rc = audio_setup(peasycap);
if (0 <= rc)
JOT(8, "audio_setup() returned %i\n", rc);
else
JOT(8, "easysnd open(): ERROR: audio_setup() returned %i\n", rc);
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device has become NULL\n");
return -EFAULT;
}
rc = adjust_volume(peasycap, -8192);
if (0 != rc) {
SAY("ERROR: adjust_volume(default) returned %i\n", rc);
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device has become NULL\n");
return -EFAULT;
}
rc = usb_set_interface(peasycap->pusb_device, peasycap->audio_interface, \
peasycap->audio_altsetting_on);
JOT(8, "usb_set_interface(.,%i,%i) returned %i\n", peasycap->audio_interface, \
peasycap->audio_altsetting_on, rc);
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device has become NULL\n");
return -EFAULT;
}
rc = wakeup_device(peasycap->pusb_device);
if (0 == rc)
JOT(8, "wakeup_device() returned %i\n", rc);
else
JOT(8, "easysnd open(): ERROR: wakeup_device() returned %i\n", rc);
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device has become NULL\n");
return -EFAULT;
}
submit_audio_urbs(peasycap);
peasycap->audio_idle = 0;
peasycap->timeval1.tv_sec = 0;
peasycap->timeval1.tv_usec = 0;
JOT(4, "finished initialization\n");
return 0;
}
/*****************************************************************************/
int
easysnd_release(struct inode *inode, struct file *file)
{
struct easycap *peasycap;
JOT(4, "begins\n");
peasycap = (struct easycap *)file->private_data;
if (NULL == peasycap) {
SAY("ERROR: peasycap is NULL.\n");
return -EFAULT;
}
if (0 != kill_audio_urbs(peasycap)) {
SAY("ERROR: kill_audio_urbs() failed\n");
return -EFAULT;
}
JOT(4, "ending successfully\n");
return 0;
}
/*****************************************************************************/
ssize_t
easysnd_read(struct file *file, char __user *puserspacebuffer, \
size_t kount, loff_t *poff)
{
struct timeval timeval;
static struct timeval timeval1;
static long long int audio_bytes, above, below, mean;
struct signed_div_result sdr;
unsigned char *p0;
long int kount1, more, rc, l0, lm;
int fragment;
struct easycap *peasycap;
struct data_buffer *pdata_buffer;
size_t szret;
/*---------------------------------------------------------------------------*/
/*
* DO A BLOCKING READ TO TRANSFER DATA TO USER SPACE.
*
******************************************************************************
***** N.B. IF THIS FUNCTION RETURNS 0, NOTHING IS SEEN IN USER SPACE. ******
***** THIS CONDITION SIGNIFIES END-OF-FILE. ******
******************************************************************************
*/
/*---------------------------------------------------------------------------*/
JOT(8, "===== easysnd_read(): kount=%i, *poff=%i\n", (int)kount, (int)(*poff));
peasycap = (struct easycap *)(file->private_data);
if (NULL == peasycap) {
SAY("ERROR in easysnd_read(): peasycap is NULL\n");
return -EFAULT;
}
/*---------------------------------------------------------------------------*/
if ((0 > peasycap->audio_read) || \
(audio_buffer_page_many <= peasycap->audio_read)) {
SAY("ERROR: peasycap->audio_read out of range\n");
return -EFAULT;
}
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) {
SAY("ERROR: pdata_buffer is NULL\n");
return -EFAULT;
}
JOT(12, "before wait, %i=frag read %i=frag fill\n", \
(peasycap->audio_read / audio_pages_per_fragment), \
(peasycap->audio_fill / audio_pages_per_fragment));
fragment = (peasycap->audio_read / audio_pages_per_fragment);
while ((fragment == (peasycap->audio_fill / audio_pages_per_fragment)) || \
(0 == (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo)))) {
if (file->f_flags & O_NONBLOCK) {
JOT(16, "returning -EAGAIN as instructed\n");
return -EAGAIN;
}
rc = wait_event_interruptible(peasycap->wq_audio, \
(peasycap->audio_idle || peasycap->audio_eof || \
((fragment != (peasycap->audio_fill / \
audio_pages_per_fragment)) && \
(0 < (PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo))))));
if (0 != rc) {
SAY("aborted by signal\n");
return -ERESTARTSYS;
}
if (peasycap->audio_eof) {
JOT(8, "returning 0 because %i=audio_eof\n", \
peasycap->audio_eof);
kill_audio_urbs(peasycap);
msleep(500);
return 0;
}
if (peasycap->audio_idle) {
JOT(16, "returning 0 because %i=audio_idle\n", \
peasycap->audio_idle);
return 0;
}
if (!peasycap->audio_isoc_streaming) {
JOT(16, "returning 0 because audio urbs not streaming\n");
return 0;
}
}
JOT(12, "after wait, %i=frag read %i=frag fill\n", \
(peasycap->audio_read / audio_pages_per_fragment), \
(peasycap->audio_fill / audio_pages_per_fragment));
szret = (size_t)0;
while (fragment == (peasycap->audio_read / audio_pages_per_fragment)) {
if (NULL == pdata_buffer->pgo) {
SAY("ERROR: pdata_buffer->pgo is NULL\n");
return -EFAULT;
}
if (NULL == pdata_buffer->pto) {
SAY("ERROR: pdata_buffer->pto is NULL\n");
return -EFAULT;
}
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
if (0 > kount1) {
SAY("easysnd_read: MISTAKE: kount1 is negative\n");
return -ERESTARTSYS;
}
if (!kount1) {
(peasycap->audio_read)++;
if (audio_buffer_page_many <= peasycap->audio_read)
peasycap->audio_read = 0;
JOT(12, "bumped peasycap->audio_read to %i\n", \
peasycap->audio_read);
if (fragment != (peasycap->audio_read / \
audio_pages_per_fragment))
break;
if ((0 > peasycap->audio_read) || \
(audio_buffer_page_many <= peasycap->audio_read)) {
SAY("ERROR: peasycap->audio_read out of range\n");
return -EFAULT;
}
pdata_buffer = &peasycap->audio_buffer[peasycap->audio_read];
if ((struct data_buffer *)NULL == pdata_buffer) {
SAY("ERROR: pdata_buffer is NULL\n");
return -EFAULT;
}
if (NULL == pdata_buffer->pgo) {
SAY("ERROR: pdata_buffer->pgo is NULL\n");
return -EFAULT;
}
if (NULL == pdata_buffer->pto) {
SAY("ERROR: pdata_buffer->pto is NULL\n");
return -EFAULT;
}
kount1 = PAGE_SIZE - (pdata_buffer->pto - pdata_buffer->pgo);
}
JOT(12, "ready to send %li bytes\n", (long int) kount1);
JOT(12, "still to send %li bytes\n", (long int) kount);
more = kount1;
if (more > kount)
more = kount;
JOT(12, "agreed to send %li bytes from page %i\n", \
more, peasycap->audio_read);
if (!more)
break;
/*---------------------------------------------------------------------------*/
/*
* ACCUMULATE DYNAMIC-RANGE INFORMATION
*/
/*---------------------------------------------------------------------------*/
p0 = (unsigned char *)pdata_buffer->pgo; l0 = 0; lm = more/2;
while (l0 < lm) {
SUMMER(p0, &peasycap->audio_sample, &peasycap->audio_niveau, \
&peasycap->audio_square); l0++; p0 += 2;
}
/*---------------------------------------------------------------------------*/
rc = copy_to_user(puserspacebuffer, pdata_buffer->pto, more);
if (0 != rc) {
SAY("ERROR: copy_to_user() returned %li\n", rc);
return -EFAULT;
}
*poff += (loff_t)more;
szret += (size_t)more;
pdata_buffer->pto += more;
puserspacebuffer += more;
kount -= (size_t)more;
}
JOT(12, "after read, %i=frag read %i=frag fill\n", \
(peasycap->audio_read / audio_pages_per_fragment), \
(peasycap->audio_fill / audio_pages_per_fragment));
if (kount < 0) {
SAY("MISTAKE: %li=kount %li=szret\n", \
(long int)kount, (long int)szret);
}
/*---------------------------------------------------------------------------*/
/*
* CALCULATE DYNAMIC RANGE FOR (VAPOURWARE) AUTOMATIC VOLUME CONTROL
*/
/*---------------------------------------------------------------------------*/
if (peasycap->audio_sample) {
below = peasycap->audio_sample;
above = peasycap->audio_square;
sdr = signed_div(above, below);
above = sdr.quotient;
mean = peasycap->audio_niveau;
sdr = signed_div(mean, peasycap->audio_sample);
JOT(12, "%8lli=mean %8lli=meansquare after %lli samples, =>\n", \
sdr.quotient, above, peasycap->audio_sample);
sdr = signed_div(above, 32768);
JOT(8, "audio dynamic range is roughly %lli\n", sdr.quotient);
}
/*---------------------------------------------------------------------------*/
/*
* UPDATE THE AUDIO CLOCK
*/
/*---------------------------------------------------------------------------*/
do_gettimeofday(&timeval);
if (!peasycap->timeval1.tv_sec) {
audio_bytes = 0;
timeval1 = timeval;
if (mutex_lock_interruptible(&(peasycap->mutex_timeval1)))
return -ERESTARTSYS;
peasycap->timeval1 = timeval1;
mutex_unlock(&(peasycap->mutex_timeval1));
sdr.quotient = 192000;
} else {
audio_bytes += (long long int) szret;
below = ((long long int)(1000000)) * \
((long long int)(timeval.tv_sec - timeval1.tv_sec)) + \
(long long int)(timeval.tv_usec - timeval1.tv_usec);
above = 1000000 * ((long long int) audio_bytes);
if (below)
sdr = signed_div(above, below);
else
sdr.quotient = 192000;
}
JOT(8, "audio streaming at %lli bytes/second\n", sdr.quotient);
if (mutex_lock_interruptible(&(peasycap->mutex_timeval1)))
return -ERESTARTSYS;
peasycap->dnbydt = sdr.quotient;
mutex_unlock(&(peasycap->mutex_timeval1));
JOT(8, "returning %li\n", (long int)szret);
return szret;
}
/*****************************************************************************/
/*---------------------------------------------------------------------------*/
/*
* 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, m, rc;
int isbuf;
if ((struct list_head *)NULL == peasycap->purb_audio_head) {
SAY("ERROR: peasycap->urb_audio_head uninitialized\n");
return -EFAULT;
}
if ((struct usb_device *)NULL == peasycap->pusb_device) {
SAY("ERROR: peasycap->pusb_device is NULL\n");
return -EFAULT;
}
if (!peasycap->audio_isoc_streaming) {
JOT(4, "initial submission of all audio urbs\n");
rc = usb_set_interface(peasycap->pusb_device,
peasycap->audio_interface, \
peasycap->audio_altsetting_on);
JOT(8, "usb_set_interface(.,%i,%i) returned %i\n", \
peasycap->audio_interface, \
peasycap->audio_altsetting_on, rc);
isbad = 0; m = 0;
list_for_each(plist_head, (peasycap->purb_audio_head)) {
pdata_urb = list_entry(plist_head, struct data_urb, list_head);
if (NULL != pdata_urb) {
purb = pdata_urb->purb;
if (NULL != 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 = easysnd_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 (0 != rc) {
isbad++;
SAY("ERROR: usb_submit_urb() failed" \
" for urb with rc:\n");
switch (rc) {
case -ENOMEM: {
SAY("ENOMEM\n"); break;
}
case -ENODEV: {
SAY("ENODEV\n"); break;
}
case -ENXIO: {
SAY("ENXIO\n"); break;
}
case -EINVAL: {
SAY("EINVAL\n"); break;
}
case -EAGAIN: {
SAY("EAGAIN\n"); break;
}
case -EFBIG: {
SAY("EFBIG\n"); break;
}
case -EPIPE: {
SAY("EPIPE\n"); break;
}
case -EMSGSIZE: {
SAY("EMSGSIZE\n"); break;
}
default: {
SAY("unknown error code %i\n",\
rc); break;
}
}
} else {
m++;
}
} else {
isbad++;
}
} else {
isbad++;
}
}
if (isbad) {
JOT(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 (NULL != pdata_urb) {
purb = pdata_urb->purb;
if (NULL != purb)
usb_kill_urb(purb);
}
}
peasycap->audio_isoc_streaming = 0;
} else {
peasycap->audio_isoc_streaming = 1;
JOT(4, "submitted %i audio urbs\n", m);
}
} else
JOT(4, "already streaming audio urbs\n");
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->audio_isoc_streaming) {
if ((struct list_head *)NULL != peasycap->purb_audio_head) {
peasycap->audio_isoc_streaming = 0;
JOT(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 ((struct data_urb *)NULL != pdata_urb) {
if ((struct urb *)NULL != pdata_urb->purb) {
usb_kill_urb(pdata_urb->purb);
m++;
}
}
}
JOT(4, "%i audio urbs killed\n", m);
} else {
SAY("ERROR: peasycap->purb_audio_head is NULL\n");
return -EFAULT;
}
} else {
JOT(8, "%i=audio_isoc_streaming, no audio urbs killed\n", \
peasycap->audio_isoc_streaming);
}
return 0;
}
/*****************************************************************************/
/*****************************************************************************
* *
* easycap_sound.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
*
*/
/*****************************************************************************/
extern struct easycap *peasycap;
extern struct usb_driver easycap_usb_driver;
extern unsigned int audio_buffer_page_many;
extern unsigned int audio_pages_per_fragment;
/*****************************************************************************
* *
* easycap_standard.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
*
*/
/*****************************************************************************/
extern struct easycap_standard easycap_standard[];
/******************************************************************************
* *
* 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"
#include "easycap_debug.h"
/*****************************************************************************/
#define TESTCARD_BYTESPERLINE (2 * 1440)
void
easycap_testcard(struct easycap *peasycap, int field_fill)
{
int total;
int y, u, v, r, g, b;
unsigned char uyvy[4];
int i1, line, k, m, n, more, much, barwidth;
unsigned char bfbar[TESTCARD_BYTESPERLINE / 8], *p1, *p2;
struct data_buffer *pfield_buffer;
JOT(8, "%i=field_fill\n", field_fill);
if ((TESTCARD_BYTESPERLINE / 2) < peasycap->width) {
SAY("ERROR: image is too wide\n");
return;
}
if (peasycap->width % 16) {
SAY("ERROR: indivisible image width\n");
return;
}
total = 0;
barwidth = (2 * peasycap->width) / 8;
k = field_fill;
m = 0;
n = 0;
for (line = 0; line < (peasycap->height / 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) {
SAY("ERROR: bad m reached\n");
return;
}
if (PAGE_SIZE < n) {
SAY("ERROR: bad n reached\n"); return;
}
if (0 > more) {
SAY("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;
}
}
}
}
JOT(8, "%i=total\n", total);
if (total != peasycap->width * peasycap->height)
SAY("ERROR: wrong number of bytes written: %i\n", total);
return;
}
/*****************************************************************************/
#if defined(EASYCAP_TESTTONE)
/*-----------------------------------------------------------------------------
THE tones[] ARRAY BELOW IS THE OUTPUT OF THIS PROGRAM,
COMPILED gcc -o prog -lm prog.c
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
#include <stdio.h>
#include <math.h>
int main(void);
int
main(void)
{
int i1, i2, last;
double d1, d2;
last = 1024 - 1;
d1 = 10.0*3.14159265/1024.0;
printf("int tones[2048] =\n{\n");
for (i1 = 0; i1 <= last; i1++)
{
d2 = ((double)i1) * d1;
i2 = (int)(16384.0*sin(d2));
if (last != i1)
{
printf("%6i, ", i2); printf("%6i, ", i2);
if (!((i1 + 1)%5)) printf("\n");
}
else
{
printf("%6i, ", i2); printf("%6i\n};\n", i2);
}
}
return(0);
}
-----------------------------------------------------------------------------*/
int tones[2048] = {
0, 0, 502, 502, 1004, 1004, 1505, 1505, 2005, 2005,
2503, 2503, 2998, 2998, 3491, 3491, 3980, 3980, 4466, 4466,
4948, 4948, 5424, 5424, 5896, 5896, 6362, 6362, 6822, 6822,
7276, 7276, 7723, 7723, 8162, 8162, 8594, 8594, 9018, 9018,
9434, 9434, 9840, 9840, 10237, 10237, 10625, 10625, 11002, 11002,
11370, 11370, 11726, 11726, 12072, 12072, 12406, 12406, 12728, 12728,
13038, 13038, 13337, 13337, 13622, 13622, 13895, 13895, 14155, 14155,
14401, 14401, 14634, 14634, 14853, 14853, 15058, 15058, 15249, 15249,
15426, 15426, 15588, 15588, 15735, 15735, 15868, 15868, 15985, 15985,
16088, 16088, 16175, 16175, 16248, 16248, 16305, 16305, 16346, 16346,
16372, 16372, 16383, 16383, 16379, 16379, 16359, 16359, 16323, 16323,
16272, 16272, 16206, 16206, 16125, 16125, 16028, 16028, 15917, 15917,
15790, 15790, 15649, 15649, 15492, 15492, 15322, 15322, 15136, 15136,
14937, 14937, 14723, 14723, 14496, 14496, 14255, 14255, 14001, 14001,
13733, 13733, 13452, 13452, 13159, 13159, 12854, 12854, 12536, 12536,
12207, 12207, 11866, 11866, 11513, 11513, 11150, 11150, 10777, 10777,
10393, 10393, 10000, 10000, 9597, 9597, 9185, 9185, 8765, 8765,
8336, 8336, 7900, 7900, 7456, 7456, 7005, 7005, 6547, 6547,
6083, 6083, 5614, 5614, 5139, 5139, 4659, 4659, 4175, 4175,
3687, 3687, 3196, 3196, 2701, 2701, 2204, 2204, 1705, 1705,
1205, 1205, 703, 703, 201, 201, -301, -301, -803, -803,
-1305, -1305, -1805, -1805, -2304, -2304, -2801, -2801, -3294, -3294,
-3785, -3785, -4272, -4272, -4756, -4756, -5234, -5234, -5708, -5708,
-6176, -6176, -6639, -6639, -7095, -7095, -7545, -7545, -7988, -7988,
-8423, -8423, -8850, -8850, -9268, -9268, -9679, -9679, -10079, -10079,
-10471, -10471, -10853, -10853, -11224, -11224, -11585, -11585, -11935, -11935,
-12273, -12273, -12600, -12600, -12916, -12916, -13219, -13219, -13510, -13510,
-13788, -13788, -14053, -14053, -14304, -14304, -14543, -14543, -14767, -14767,
-14978, -14978, -15175, -15175, -15357, -15357, -15525, -15525, -15678, -15678,
-15817, -15817, -15940, -15940, -16049, -16049, -16142, -16142, -16221, -16221,
-16284, -16284, -16331, -16331, -16364, -16364, -16381, -16381, -16382, -16382,
-16368, -16368, -16339, -16339, -16294, -16294, -16234, -16234, -16159, -16159,
-16069, -16069, -15963, -15963, -15842, -15842, -15707, -15707, -15557, -15557,
-15392, -15392, -15212, -15212, -15018, -15018, -14810, -14810, -14589, -14589,
-14353, -14353, -14104, -14104, -13842, -13842, -13566, -13566, -13278, -13278,
-12977, -12977, -12665, -12665, -12340, -12340, -12003, -12003, -11656, -11656,
-11297, -11297, -10928, -10928, -10548, -10548, -10159, -10159, -9759, -9759,
-9351, -9351, -8934, -8934, -8509, -8509, -8075, -8075, -7634, -7634,
-7186, -7186, -6731, -6731, -6269, -6269, -5802, -5802, -5329, -5329,
-4852, -4852, -4369, -4369, -3883, -3883, -3393, -3393, -2900, -2900,
-2404, -2404, -1905, -1905, -1405, -1405, -904, -904, -402, -402,
100, 100, 603, 603, 1105, 1105, 1605, 1605, 2105, 2105,
2602, 2602, 3097, 3097, 3589, 3589, 4078, 4078, 4563, 4563,
5043, 5043, 5519, 5519, 5990, 5990, 6455, 6455, 6914, 6914,
7366, 7366, 7811, 7811, 8249, 8249, 8680, 8680, 9102, 9102,
9516, 9516, 9920, 9920, 10315, 10315, 10701, 10701, 11077, 11077,
11442, 11442, 11796, 11796, 12139, 12139, 12471, 12471, 12791, 12791,
13099, 13099, 13395, 13395, 13678, 13678, 13948, 13948, 14205, 14205,
14449, 14449, 14679, 14679, 14895, 14895, 15098, 15098, 15286, 15286,
15459, 15459, 15618, 15618, 15763, 15763, 15892, 15892, 16007, 16007,
16107, 16107, 16191, 16191, 16260, 16260, 16314, 16314, 16353, 16353,
16376, 16376, 16384, 16384, 16376, 16376, 16353, 16353, 16314, 16314,
16260, 16260, 16191, 16191, 16107, 16107, 16007, 16007, 15892, 15892,
15763, 15763, 15618, 15618, 15459, 15459, 15286, 15286, 15098, 15098,
14895, 14895, 14679, 14679, 14449, 14449, 14205, 14205, 13948, 13948,
13678, 13678, 13395, 13395, 13099, 13099, 12791, 12791, 12471, 12471,
12139, 12139, 11796, 11796, 11442, 11442, 11077, 11077, 10701, 10701,
10315, 10315, 9920, 9920, 9516, 9516, 9102, 9102, 8680, 8680,
8249, 8249, 7811, 7811, 7366, 7366, 6914, 6914, 6455, 6455,
5990, 5990, 5519, 5519, 5043, 5043, 4563, 4563, 4078, 4078,
3589, 3589, 3097, 3097, 2602, 2602, 2105, 2105, 1605, 1605,
1105, 1105, 603, 603, 100, 100, -402, -402, -904, -904,
-1405, -1405, -1905, -1905, -2404, -2404, -2900, -2900, -3393, -3393,
-3883, -3883, -4369, -4369, -4852, -4852, -5329, -5329, -5802, -5802,
-6269, -6269, -6731, -6731, -7186, -7186, -7634, -7634, -8075, -8075,
-8509, -8509, -8934, -8934, -9351, -9351, -9759, -9759, -10159, -10159,
-10548, -10548, -10928, -10928, -11297, -11297, -11656, -11656, -12003, -12003,
-12340, -12340, -12665, -12665, -12977, -12977, -13278, -13278, -13566, -13566,
-13842, -13842, -14104, -14104, -14353, -14353, -14589, -14589, -14810, -14810,
-15018, -15018, -15212, -15212, -15392, -15392, -15557, -15557, -15707, -15707,
-15842, -15842, -15963, -15963, -16069, -16069, -16159, -16159, -16234, -16234,
-16294, -16294, -16339, -16339, -16368, -16368, -16382, -16382, -16381, -16381,
-16364, -16364, -16331, -16331, -16284, -16284, -16221, -16221, -16142, -16142,
-16049, -16049, -15940, -15940, -15817, -15817, -15678, -15678, -15525, -15525,
-15357, -15357, -15175, -15175, -14978, -14978, -14767, -14767, -14543, -14543,
-14304, -14304, -14053, -14053, -13788, -13788, -13510, -13510, -13219, -13219,
-12916, -12916, -12600, -12600, -12273, -12273, -11935, -11935, -11585, -11585,
-11224, -11224, -10853, -10853, -10471, -10471, -10079, -10079, -9679, -9679,
-9268, -9268, -8850, -8850, -8423, -8423, -7988, -7988, -7545, -7545,
-7095, -7095, -6639, -6639, -6176, -6176, -5708, -5708, -5234, -5234,
-4756, -4756, -4272, -4272, -3785, -3785, -3294, -3294, -2801, -2801,
-2304, -2304, -1805, -1805, -1305, -1305, -803, -803, -301, -301,
201, 201, 703, 703, 1205, 1205, 1705, 1705, 2204, 2204,
2701, 2701, 3196, 3196, 3687, 3687, 4175, 4175, 4659, 4659,
5139, 5139, 5614, 5614, 6083, 6083, 6547, 6547, 7005, 7005,
7456, 7456, 7900, 7900, 8336, 8336, 8765, 8765, 9185, 9185,
9597, 9597, 10000, 10000, 10393, 10393, 10777, 10777, 11150, 11150,
11513, 11513, 11866, 11866, 12207, 12207, 12536, 12536, 12854, 12854,
13159, 13159, 13452, 13452, 13733, 13733, 14001, 14001, 14255, 14255,
14496, 14496, 14723, 14723, 14937, 14937, 15136, 15136, 15322, 15322,
15492, 15492, 15649, 15649, 15790, 15790, 15917, 15917, 16028, 16028,
16125, 16125, 16206, 16206, 16272, 16272, 16323, 16323, 16359, 16359,
16379, 16379, 16383, 16383, 16372, 16372, 16346, 16346, 16305, 16305,
16248, 16248, 16175, 16175, 16088, 16088, 15985, 15985, 15868, 15868,
15735, 15735, 15588, 15588, 15426, 15426, 15249, 15249, 15058, 15058,
14853, 14853, 14634, 14634, 14401, 14401, 14155, 14155, 13895, 13895,
13622, 13622, 13337, 13337, 13038, 13038, 12728, 12728, 12406, 12406,
12072, 12072, 11726, 11726, 11370, 11370, 11002, 11002, 10625, 10625,
10237, 10237, 9840, 9840, 9434, 9434, 9018, 9018, 8594, 8594,
8162, 8162, 7723, 7723, 7276, 7276, 6822, 6822, 6362, 6362,
5896, 5896, 5424, 5424, 4948, 4948, 4466, 4466, 3980, 3980,
3491, 3491, 2998, 2998, 2503, 2503, 2005, 2005, 1505, 1505,
1004, 1004, 502, 502, 0, 0, -502, -502, -1004, -1004,
-1505, -1505, -2005, -2005, -2503, -2503, -2998, -2998, -3491, -3491,
-3980, -3980, -4466, -4466, -4948, -4948, -5424, -5424, -5896, -5896,
-6362, -6362, -6822, -6822, -7276, -7276, -7723, -7723, -8162, -8162,
-8594, -8594, -9018, -9018, -9434, -9434, -9840, -9840, -10237, -10237,
-10625, -10625, -11002, -11002, -11370, -11370, -11726, -11726, -12072, -12072,
-12406, -12406, -12728, -12728, -13038, -13038, -13337, -13337, -13622, -13622,
-13895, -13895, -14155, -14155, -14401, -14401, -14634, -14634, -14853, -14853,
-15058, -15058, -15249, -15249, -15426, -15426, -15588, -15588, -15735, -15735,
-15868, -15868, -15985, -15985, -16088, -16088, -16175, -16175, -16248, -16248,
-16305, -16305, -16346, -16346, -16372, -16372, -16383, -16383, -16379, -16379,
-16359, -16359, -16323, -16323, -16272, -16272, -16206, -16206, -16125, -16125,
-16028, -16028, -15917, -15917, -15790, -15790, -15649, -15649, -15492, -15492,
-15322, -15322, -15136, -15136, -14937, -14937, -14723, -14723, -14496, -14496,
-14255, -14255, -14001, -14001, -13733, -13733, -13452, -13452, -13159, -13159,
-12854, -12854, -12536, -12536, -12207, -12207, -11866, -11866, -11513, -11513,
-11150, -11150, -10777, -10777, -10393, -10393, -10000, -10000, -9597, -9597,
-9185, -9185, -8765, -8765, -8336, -8336, -7900, -7900, -7456, -7456,
-7005, -7005, -6547, -6547, -6083, -6083, -5614, -5614, -5139, -5139,
-4659, -4659, -4175, -4175, -3687, -3687, -3196, -3196, -2701, -2701,
-2204, -2204, -1705, -1705, -1205, -1205, -703, -703, -201, -201,
301, 301, 803, 803, 1305, 1305, 1805, 1805, 2304, 2304,
2801, 2801, 3294, 3294, 3785, 3785, 4272, 4272, 4756, 4756,
5234, 5234, 5708, 5708, 6176, 6176, 6639, 6639, 7095, 7095,
7545, 7545, 7988, 7988, 8423, 8423, 8850, 8850, 9268, 9268,
9679, 9679, 10079, 10079, 10471, 10471, 10853, 10853, 11224, 11224,
11585, 11585, 11935, 11935, 12273, 12273, 12600, 12600, 12916, 12916,
13219, 13219, 13510, 13510, 13788, 13788, 14053, 14053, 14304, 14304,
14543, 14543, 14767, 14767, 14978, 14978, 15175, 15175, 15357, 15357,
15525, 15525, 15678, 15678, 15817, 15817, 15940, 15940, 16049, 16049,
16142, 16142, 16221, 16221, 16284, 16284, 16331, 16331, 16364, 16364,
16381, 16381, 16382, 16382, 16368, 16368, 16339, 16339, 16294, 16294,
16234, 16234, 16159, 16159, 16069, 16069, 15963, 15963, 15842, 15842,
15707, 15707, 15557, 15557, 15392, 15392, 15212, 15212, 15018, 15018,
14810, 14810, 14589, 14589, 14353, 14353, 14104, 14104, 13842, 13842,
13566, 13566, 13278, 13278, 12977, 12977, 12665, 12665, 12340, 12340,
12003, 12003, 11656, 11656, 11297, 11297, 10928, 10928, 10548, 10548,
10159, 10159, 9759, 9759, 9351, 9351, 8934, 8934, 8509, 8509,
8075, 8075, 7634, 7634, 7186, 7186, 6731, 6731, 6269, 6269,
5802, 5802, 5329, 5329, 4852, 4852, 4369, 4369, 3883, 3883,
3393, 3393, 2900, 2900, 2404, 2404, 1905, 1905, 1405, 1405,
904, 904, 402, 402, -100, -100, -603, -603, -1105, -1105,
-1605, -1605, -2105, -2105, -2602, -2602, -3097, -3097, -3589, -3589,
-4078, -4078, -4563, -4563, -5043, -5043, -5519, -5519, -5990, -5990,
-6455, -6455, -6914, -6914, -7366, -7366, -7811, -7811, -8249, -8249,
-8680, -8680, -9102, -9102, -9516, -9516, -9920, -9920, -10315, -10315,
-10701, -10701, -11077, -11077, -11442, -11442, -11796, -11796, -12139, -12139,
-12471, -12471, -12791, -12791, -13099, -13099, -13395, -13395, -13678, -13678,
-13948, -13948, -14205, -14205, -14449, -14449, -14679, -14679, -14895, -14895,
-15098, -15098, -15286, -15286, -15459, -15459, -15618, -15618, -15763, -15763,
-15892, -15892, -16007, -16007, -16107, -16107, -16191, -16191, -16260, -16260,
-16314, -16314, -16353, -16353, -16376, -16376, -16383, -16383, -16376, -16376,
-16353, -16353, -16314, -16314, -16260, -16260, -16191, -16191, -16107, -16107,
-16007, -16007, -15892, -15892, -15763, -15763, -15618, -15618, -15459, -15459,
-15286, -15286, -15098, -15098, -14895, -14895, -14679, -14679, -14449, -14449,
-14205, -14205, -13948, -13948, -13678, -13678, -13395, -13395, -13099, -13099,
-12791, -12791, -12471, -12471, -12139, -12139, -11796, -11796, -11442, -11442,
-11077, -11077, -10701, -10701, -10315, -10315, -9920, -9920, -9516, -9516,
-9102, -9102, -8680, -8680, -8249, -8249, -7811, -7811, -7366, -7366,
-6914, -6914, -6455, -6455, -5990, -5990, -5519, -5519, -5043, -5043,
-4563, -4563, -4078, -4078, -3589, -3589, -3097, -3097, -2602, -2602,
-2105, -2105, -1605, -1605, -1105, -1105, -603, -603, -100, -100,
402, 402, 904, 904, 1405, 1405, 1905, 1905, 2404, 2404,
2900, 2900, 3393, 3393, 3883, 3883, 4369, 4369, 4852, 4852,
5329, 5329, 5802, 5802, 6269, 6269, 6731, 6731, 7186, 7186,
7634, 7634, 8075, 8075, 8509, 8509, 8934, 8934, 9351, 9351,
9759, 9759, 10159, 10159, 10548, 10548, 10928, 10928, 11297, 11297,
11656, 11656, 12003, 12003, 12340, 12340, 12665, 12665, 12977, 12977,
13278, 13278, 13566, 13566, 13842, 13842, 14104, 14104, 14353, 14353,
14589, 14589, 14810, 14810, 15018, 15018, 15212, 15212, 15392, 15392,
15557, 15557, 15707, 15707, 15842, 15842, 15963, 15963, 16069, 16069,
16159, 16159, 16234, 16234, 16294, 16294, 16339, 16339, 16368, 16368,
16382, 16382, 16381, 16381, 16364, 16364, 16331, 16331, 16284, 16284,
16221, 16221, 16142, 16142, 16049, 16049, 15940, 15940, 15817, 15817,
15678, 15678, 15525, 15525, 15357, 15357, 15175, 15175, 14978, 14978,
14767, 14767, 14543, 14543, 14304, 14304, 14053, 14053, 13788, 13788,
13510, 13510, 13219, 13219, 12916, 12916, 12600, 12600, 12273, 12273,
11935, 11935, 11585, 11585, 11224, 11224, 10853, 10853, 10471, 10471,
10079, 10079, 9679, 9679, 9268, 9268, 8850, 8850, 8423, 8423,
7988, 7988, 7545, 7545, 7095, 7095, 6639, 6639, 6176, 6176,
5708, 5708, 5234, 5234, 4756, 4756, 4272, 4272, 3785, 3785,
3294, 3294, 2801, 2801, 2304, 2304, 1805, 1805, 1305, 1305,
803, 803, 301, 301, -201, -201, -703, -703, -1205, -1205,
-1705, -1705, -2204, -2204, -2701, -2701, -3196, -3196, -3687, -3687,
-4175, -4175, -4659, -4659, -5139, -5139, -5614, -5614, -6083, -6083,
-6547, -6547, -7005, -7005, -7456, -7456, -7900, -7900, -8336, -8336,
-8765, -8765, -9185, -9185, -9597, -9597, -10000, -10000, -10393, -10393,
-10777, -10777, -11150, -11150, -11513, -11513, -11866, -11866, -12207, -12207,
-12536, -12536, -12854, -12854, -13159, -13159, -13452, -13452, -13733, -13733,
-14001, -14001, -14255, -14255, -14496, -14496, -14723, -14723, -14937, -14937,
-15136, -15136, -15322, -15322, -15492, -15492, -15649, -15649, -15790, -15790,
-15917, -15917, -16028, -16028, -16125, -16125, -16206, -16206, -16272, -16272,
-16323, -16323, -16359, -16359, -16379, -16379, -16383, -16383, -16372, -16372,
-16346, -16346, -16305, -16305, -16248, -16248, -16175, -16175, -16088, -16088,
-15985, -15985, -15868, -15868, -15735, -15735, -15588, -15588, -15426, -15426,
-15249, -15249, -15058, -15058, -14853, -14853, -14634, -14634, -14401, -14401,
-14155, -14155, -13895, -13895, -13622, -13622, -13337, -13337, -13038, -13038,
-12728, -12728, -12406, -12406, -12072, -12072, -11726, -11726, -11370, -11370,
-11002, -11002, -10625, -10625, -10237, -10237, -9840, -9840, -9434, -9434,
-9018, -9018, -8594, -8594, -8162, -8162, -7723, -7723, -7276, -7276,
-6822, -6822, -6362, -6362, -5896, -5896, -5424, -5424, -4948, -4948,
-4466, -4466, -3980, -3980, -3491, -3491, -2998, -2998, -2503, -2503,
-2005, -2005, -1505, -1505, -1004, -1004, -502, -502
};
/*****************************************************************************/
void
easysnd_testtone(struct easycap *peasycap, int audio_fill)
{
int i1;
unsigned char *p2;
struct data_buffer *paudio_buffer;
JOT(8, "%i=audio_fill\n", audio_fill);
paudio_buffer = &peasycap->audio_buffer[audio_fill];
p2 = (unsigned char *)(paudio_buffer->pgo);
for (i1 = 0; i1 < PAGE_SIZE; i1 += 4, p2 += 4) {
*p2 = (unsigned char) (0x00FF & tones[i1/2]);
*(p2 + 1) = (unsigned char)((0xFF00 & tones[i1/2]) >> 8);
*(p2 + 2) = (unsigned char) (0x00FF & tones[i1/2 + 1]);
*(p2 + 3) = (unsigned char)((0xFF00 & tones[i1/2 + 1]) >> 8);
}
return;
}
#endif /*EASYCAP_TESTTONE*/
/*****************************************************************************/
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册