From 702422bd2d3f44e454a97ca7054edde84cc18126 Mon Sep 17 00:00:00 2001 From: "R.M. Thomas" Date: Fri, 18 Jun 2010 12:29:49 -0700 Subject: [PATCH] Staging: easycap: add easycap driver This adds the easycap USB video adapter driver to the staging directory. Signed-off-by: Greg Kroah-Hartman --- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/easycap/Kconfig | 16 + drivers/staging/easycap/Makefile | 13 + drivers/staging/easycap/README | 130 + drivers/staging/easycap/easycap.h | 632 +++ drivers/staging/easycap/easycap_debug.h | 27 + drivers/staging/easycap/easycap_ioctl.c | 2651 ++++++++++++ drivers/staging/easycap/easycap_ioctl.h | 29 + drivers/staging/easycap/easycap_low.c | 1057 +++++ drivers/staging/easycap/easycap_main.c | 4342 ++++++++++++++++++++ drivers/staging/easycap/easycap_settings.c | 489 +++ drivers/staging/easycap/easycap_sound.c | 973 +++++ drivers/staging/easycap/easycap_sound.h | 30 + drivers/staging/easycap/easycap_standard.h | 27 + drivers/staging/easycap/easycap_testcard.c | 392 ++ 16 files changed, 10811 insertions(+) create mode 100644 drivers/staging/easycap/Kconfig create mode 100644 drivers/staging/easycap/Makefile create mode 100644 drivers/staging/easycap/README create mode 100644 drivers/staging/easycap/easycap.h create mode 100644 drivers/staging/easycap/easycap_debug.h create mode 100644 drivers/staging/easycap/easycap_ioctl.c create mode 100644 drivers/staging/easycap/easycap_ioctl.h create mode 100644 drivers/staging/easycap/easycap_low.c create mode 100644 drivers/staging/easycap/easycap_main.c create mode 100644 drivers/staging/easycap/easycap_settings.c create mode 100644 drivers/staging/easycap/easycap_sound.c create mode 100644 drivers/staging/easycap/easycap_sound.h create mode 100644 drivers/staging/easycap/easycap_standard.h create mode 100644 drivers/staging/easycap/easycap_testcard.c diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 2faff9697467..d5b827ccecf8 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -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 diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index 6d21710d3311..2c14f89387be 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -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/ diff --git a/drivers/staging/easycap/Kconfig b/drivers/staging/easycap/Kconfig new file mode 100644 index 000000000000..9bff7cf74f01 --- /dev/null +++ b/drivers/staging/easycap/Kconfig @@ -0,0 +1,16 @@ +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 + diff --git a/drivers/staging/easycap/Makefile b/drivers/staging/easycap/Makefile new file mode 100644 index 000000000000..d93bd6b70a4b --- /dev/null +++ b/drivers/staging/easycap/Makefile @@ -0,0 +1,13 @@ + +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 + diff --git a/drivers/staging/easycap/README b/drivers/staging/easycap/README new file mode 100644 index 000000000000..3775481f05e8 --- /dev/null +++ b/drivers/staging/easycap/README @@ -0,0 +1,130 @@ + + *********************************************************** + * EasyCAP USB 2.0 Video Adapter with Audio, Model DC60 * + * and * + * EasyCAP002 4-Channel USB 2.0 DVR * + *********************************************************** + Mike Thomas + + + +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. + diff --git a/drivers/staging/easycap/easycap.h b/drivers/staging/easycap/easycap.h new file mode 100644 index 000000000000..83ae2fb6db7c --- /dev/null +++ b/drivers/staging/easycap/easycap.h @@ -0,0 +1,632 @@ +/***************************************************************************** +* * +* easycap.h * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +/*vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv*/ +#if defined(EASYCAP_IS_VIDEODEV_CLIENT) +#if (!defined(__OLD_VIDIOC_)) +#define __OLD_VIDIOC_ +#endif /* !defined(__OLD_VIDIOC_) */ + +#include + +#if defined(EASYCAP_NEEDS_V4L2_DEVICE_H) +#include +#endif /*EASYCAP_NEEDS_V4L2_DEVICE_H*/ +#endif /*EASYCAP_IS_VIDEODEV_CLIENT*/ +/*^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/ + +#if (!defined(__OLD_VIDIOC_)) +#define __OLD_VIDIOC_ +#endif /* !defined(__OLD_VIDIOC_) */ +#include + +#include + +#if defined(EASYCAP_NEEDS_USBVIDEO_H) +#include +#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*/ diff --git a/drivers/staging/easycap/easycap_debug.h b/drivers/staging/easycap/easycap_debug.h new file mode 100644 index 000000000000..1d10d7ea7d68 --- /dev/null +++ b/drivers/staging/easycap/easycap_debug.h @@ -0,0 +1,27 @@ +/***************************************************************************** +* * +* easycap_debug.h * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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; diff --git a/drivers/staging/easycap/easycap_ioctl.c b/drivers/staging/easycap/easycap_ioctl.c new file mode 100644 index 000000000000..f71cd9eaeb6b --- /dev/null +++ b/drivers/staging/easycap/easycap_ioctl.c @@ -0,0 +1,2651 @@ +/****************************************************************************** +* * +* easycap_ioctl.c * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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; +} +/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_ioctl.h b/drivers/staging/easycap/easycap_ioctl.h new file mode 100644 index 000000000000..7cab0943d913 --- /dev/null +++ b/drivers/staging/easycap/easycap_ioctl.h @@ -0,0 +1,29 @@ +/***************************************************************************** +* * +* easycap_ioctl.h * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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; diff --git a/drivers/staging/easycap/easycap_low.c b/drivers/staging/easycap/easycap_low.c new file mode 100644 index 000000000000..e7c189af1476 --- /dev/null +++ b/drivers/staging/easycap/easycap_low.c @@ -0,0 +1,1057 @@ +/***************************************************************************** +* * +* * +* easycap_low.c * +* * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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) + 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; +} +/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_main.c b/drivers/staging/easycap/easycap_main.c new file mode 100644 index 000000000000..18259df3fe7d --- /dev/null +++ b/drivers/staging/easycap/easycap_main.c @@ -0,0 +1,4342 @@ +/****************************************************************************** +* * +* easycap_main.c * +* * +* Video driver for EasyCAP USB2.0 Video Capture Device DC60 * +* * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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 "); +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*/ +/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_settings.c b/drivers/staging/easycap/easycap_settings.c new file mode 100644 index 000000000000..1e4eb23885e3 --- /dev/null +++ b/drivers/staging/easycap/easycap_settings.c @@ -0,0 +1,489 @@ +/****************************************************************************** +* * +* easycap_settings.c * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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 +} + }; +/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_sound.c b/drivers/staging/easycap/easycap_sound.c new file mode 100644 index 000000000000..532c4105738a --- /dev/null +++ b/drivers/staging/easycap/easycap_sound.c @@ -0,0 +1,973 @@ +/****************************************************************************** +* * +* easycap_sound.c * +* * +* Audio driver for EasyCAP USB2.0 Video Capture Device DC60 * +* * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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; +} +/*****************************************************************************/ diff --git a/drivers/staging/easycap/easycap_sound.h b/drivers/staging/easycap/easycap_sound.h new file mode 100644 index 000000000000..a6e2a9d22319 --- /dev/null +++ b/drivers/staging/easycap/easycap_sound.h @@ -0,0 +1,30 @@ +/***************************************************************************** +* * +* easycap_sound.h * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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; diff --git a/drivers/staging/easycap/easycap_standard.h b/drivers/staging/easycap/easycap_standard.h new file mode 100644 index 000000000000..cadc8d27a856 --- /dev/null +++ b/drivers/staging/easycap/easycap_standard.h @@ -0,0 +1,27 @@ +/***************************************************************************** +* * +* easycap_standard.h * +* * +*****************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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[]; diff --git a/drivers/staging/easycap/easycap_testcard.c b/drivers/staging/easycap/easycap_testcard.c new file mode 100644 index 000000000000..3c2ce28fab95 --- /dev/null +++ b/drivers/staging/easycap/easycap_testcard.c @@ -0,0 +1,392 @@ +/****************************************************************************** +* * +* easycap_testcard.c * +* * +******************************************************************************/ +/* + * + * Copyright (C) 2010 R.M. Thomas + * + * + * 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 +#include + +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*/ +/*****************************************************************************/ -- GitLab