2006-12-04 19:30:53 +08:00
/*
* USB USBVISION Video device driver 0.9 .8 .3 cvs ( For Kernel 2.4 .19 - 2.4 .32 + 2.6 .0 - 2.6 .16 )
*
*
*
* Copyright ( c ) 1999 - 2005 Joerg Heckenbach < joerg @ heckenbach - aw . de >
*
* This module is part of usbvision driver project .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
* Let ' s call the version 0. . . . until compression decoding is completely
* implemented .
*
* This driver is written by Jose Ignacio Gijon and Joerg Heckenbach .
* It was based on USB CPiA driver written by Peter Pregler ,
* Scott J . Bertin and Johannes Erdfelt
* Ideas are taken from bttv driver by Ralph Metzler , Marcus Metzler &
* Gerd Knorr and zoran 36120 / 36125 driver by Pauline Middelink
* Updates to driver completed by Dwaine P . Garden
*
* History :
*
* Mar . 2000 - 15.12 .2000 : ( 0.0 .0 - 0.2 .0 )
* Several alpha drivers and the first beta .
*
* Since Dec . 2000 : ( 0.2 .1 ) or ( v2 .1 )
* Code changes or updates by Dwaine Garden and every other person .
*
* Added : New Hauppauge TV device Vendor ID : 0x0573
* Product ID : 0x4D01
* ( Thanks to Giovanni Garberoglio )
*
* Added : UK Hauppauge WinTV - USB Vendor ID : 0x0573
* Product ID : 0x4D02
* ( Thanks to Derek Freeman - Jones )
*
* Feb , 2001 - Apr 08 , 2001 : ( 0.3 .0 )
* - Some fixes . Driver is now more stable .
* - Scratch is organized as ring - buffer now for better performance
* - DGA ( overlay ) is now supported .
* ! ! ! ! Danger ! ! ! ! Clipping is not yet implemented . Your system will
* crash if your video window leaves the screen ! ! !
* - Max . Framesize is set to 320 x240 . There isn ' t more memory on the
* nt1003 video device for the FIFO .
* - Supported video palettes : RGB565 , RGB555 , RGB24 , RGB32
*
*
* Apr 15 , 2001 : ( 0.3 .1 - test . . . )
* - Clipping is implemented
* - NTSC is now coloured ( Thanks to Dwaine Garden )
* - Added SECAM colour detection in saa7111 - new
* - Added : French Hauppauge WinTV USB Vendor ID : 0x0573
* Product ID : 0x4D03
* ( Thanks to Julius Hrivnac )
* - Added : US Hauppauge WINTV USB Vendor ID : 0x0573
* Product ID : 0x4D00
* ( Thanks to Derrick J Brashear )
* - Changes for adding new devices . There ' s now a table in usbvision . h .
* Adding your devices data to the usbvision_device_data table is all
* you need to add a new device .
*
* May 11 , 2001 : ( 0.3 .2 - test . . . ) ( Thanks to Derek Freeman - Jones )
* - Support YUV422 raw format for people with hardware scaling .
* - Only power on the device when opened ( use option PowerOnAtOpen = 0 to disable it ) .
* - Turn off audio so we can listen to Line In .
*
* July 5 , 2001 - ( Patch the driver to run with Kernel 2.4 .6 )
* - Fixed a problem with the number of parameters passed to video_register_device .
*
* July 6 , 2001 - Added : HAUPPAUGE WINTV - USB FM USA Vendor ID : 0x0573
* Product ID : 0x4D10
* ( Thanks to Braddock Gaskill )
* Added : USBGear USBG - V1 resp . HAMA USB
* Vendor ID : 0x0573
* Product ID : 0x0003
* ( Thanks to Bradley A . Singletary and Juergen Weigert )
*
* Jan 24 , 2002 - ( 0.3 .3 - test . . . )
* - Moved all global variables that are device specific the usb_usbvision struct
* - Fixed the 64 x48 unchangable image in xawtv when starting it with overlay
* - Add VideoNorm and TunerType to the usb_device_data table
* - Checked the audio channels and mute for HAUPPAUGE WinTV USB FM
* - Implemented the power on when opening the device . But some software opens
* the device several times when starting . So the i2c parts are just registered
* by an open , when they become deregistered by the next close . You can speed
* up tuner detection , when adding " options tuner addr=your_addr " to / etc / modules . conf
* - Begin to resize the frame in width and height . So it will be possible to watch i . e .
* 384 x288 pixels at 23 fps .
*
* Feb 10 , 2002
* - Added radio device
*
*
* Jul 30 , 2002 - ( Thanks Cameron Maxwell )
* - Changes to usbvision . h - - fixed usbvision device data structure , incorrectly had ( 0x0573 , 0x4d21 ) for WinTV - USB II , should be 0x4d20 .
* - Changes for device WinTV - USB II ( 0x0573 . 0x4D21 ) . It does not have a FM tuner .
* - Added the real device HAUPPAUGE WINTV - USB II ( PAL ) to the device structure in usbvision . h .
* - Changes to saa7113 - new , the video is 8 bit data for the Phillips SAA7113 not 16 bit like SAA7111 .
* - Tuned lots of setup registers for the Phillips SAA7113 video chipset .
* - Changes to the supplied makefile . ( Dwaine Garden ) Still needs to be fixed so it will compile modules on different distrubutions .
*
*
* Aug 10 , 2002 - ( Thanks Mike Klinke )
* - Changes to usbvision . txt - - Fixed instructions on the location to copy the contents of the tgz file .
* - Added device WinTV - USB FM Model 621 ( 0x0573 . 0x4D30 ) . There is another device which carries the same name . Kept that device in the device structure .
*
* Aug 12 , 2002 - Dwaine Garden
* - Added the ability to read the NT100x chip for the MaxISOPacketLength and USB Bandwidth
* Setting of the video device .
* - Adjustments to the SAA7113H code for proper video output .
* - Changes to usbvision . h , so all the devices with FM tuners are working .
*
* Feb 10 , 2003 - Joerg Heckenbach
* - fixed endian bug for Motorola PPC
*
* Feb 13 , 2003 - Joerg Heckenbach
* - fixed Vin_Reg setting and presetting from usbvision_device_data ( )
*
* Apr 19 , 2003 - Dwaine Garden
* - Fixed compiling errors under RedHat v9 .0 . from uvirt_to_kva and usbvision_mmap . ( Thanks Cameron Maxwell )
* - Changed pte_offset to pte_offset_kernel .
* - Changed remap_page_range and added additional parameter to function .
* - Change setup parameters for the D - Link V100 USB device
* - Added a new device to the usbvision driver . Pinnacle Studio PCTV USB ( PAL ) 0x2304 0x0110
* - Screwed up the sourceforge . net cvs respository ! 8 * )
*
* Apr 22 , 2002 - Dwaine Garden
* - Added a new device to the usbvision driver . Dazzle DVC - 80 ( PAL ) 0x07d0 0x0004 . ( Thanks Carl Anderson )
* - Changes to some of the comments .
*
* June 06 , 2002 - Ivan , Dwaine Garden
* - Ivan updates for fine tuning device parameters without driver recompiling . ( Ivan )
* - Changes to some of the comments . ( Dwaine Garden )
* - Changes to the makefile - Better CPU settings . ( Ivan )
* - Changes to device Hauppauge WinTv - USB III ( PAL ) FM Model 568 - Fine tuning parameters ( Ivan )
*
*
* Oct 16 , 2003 - 0.9 .0 - Joerg Heckenbach
* - Implementation of the first part of the decompression algorithm for intra frames .
* The resolution has to be 320 x240 . A dynamic adaption of compression deepth is
* missing yet .
*
* Oct 22 , 2003 - 0.9 .1 - Joerg Heckenbach
* - Implementation of the decompression algorithm for inter frames .
* The resolution still has to be 320 x240 .
*
* Nov 2003 - Feb 2004 - 0.9 .2 to 0.9 .3 - Joerg Heckenbach
* - Implement last unknown compressed block type . But color is still noisy .
* - Finding criteria for adaptive compression adjustment .
* - Porting to 2.6 kernels , but still working under 2.4
*
* Feb 04 , 2004 - 0.9 .4 Joerg Heckenbach
* - Found bug in color decompression . Color is OK now .
*
* Feb 09 , 2004 - 0.9 .5 Joerg Heckenbach
* - Add auto - recognition of chip type NT1003 or NT1004 .
* - Add adaptive compression adjustment .
* - Patched saa7113 multiplexer switching ( Thanks to Orlando F . S . Bordoni )
*
* Feb 24 , 2004 - 0.9 .6 Joerg Heckenbach
* - Add a timer to wait before poweroff at close , to save start time in
* some video applications
*
* Mar 4 , 2004 - 0.9 .6 Dwaine Garden
* - Added device Global Village GV - 007 ( NTSC ) to usbvision . h ( Thanks to Abe Skolnik )
* - Forgot to add this device to the driver . 8 * )
*
* June 2 , 2004 - 0.9 .6 Dwaine Garden
* - Fixed sourceforge . net cvs repository .
* - Added # if LINUX_VERSION_CODE > KERNEL_VERSION ( 2 , 4 , 26 ) for . owner to help compiling under kernels 2.4 . x which do not have the i2c v2 .8 . x updates .
* - Added device Hauppauge WinTv - USB III ( PAL ) FM Model 597 to usbvision . h
*
* July 1 , 2004 - 0.9 .6 Dwaine Garden
* - Patch was submitted by Hal Finkel to fix the problem with the tuner not working under kernel 2.6 .7 .
* - Thanks Hal . . . . .
*
* July 30 , 2004 - 0.9 .6 Dwaine Garden
* - Patch was submitted by Tobias Diaz to fix Model ID mismatch in usbvision . h .
* - Thanks . . . . .
*
* August 12 , 2004 - 0.9 .6 Dwaine Garden
* - Updated the readme file so people could install the driver under the configuration file for kernel 2.6 . x recompiles . Now people can use make xconfig !
* - Added new device " Camtel Technology Corp TVB330-USB FM " .
* - Sourceforge . net CVS has been updated with all the changes .
*
* August 20 , 2004 - 0.9 .7 Dwaine Garden
* - Added Device " Hauppauge USB Live Model 600 "
* - Fixed up all the devices which did not have a default tuner type in usbvision . h . It ' s best guess , at least until someone with the device tells me otherwise .
* - Sourceforge . net CVS has been updated with all the changes .
* - Clean up the driver .
*
* September 13 , 2004 - 0.9 .8 Dwaine Garden
* - Changed usbvision_muxsel to address the problem with black & white s - video output for NT1004 devices with saa7114 video decoder . Thanks to Emmanuel for the patch and testing .
* - Fixed up SECAM devices which could not properly output video . Changes to usbmuxsel . Thanks to Emmanuel for the patch and everyone with a SECAM device which help test .
* - Removed some commented out code . Clean up .
* - Tried to fix up the annoying empty directories in the sourceforge . net cvs . Fuck it up again . 8 * (
*
* November 15 , 2004 - 0.9 .8 Dwaine Garden
* - Release new tar - 0.9 .8 on sourceforge . net
* - Added some new devices to usbvision . h WinTV USB Model 602 40201 Rev B282 , Hauppague WinTV USB Model 602 40201 Rev B285
* - Added better compatibility for 2.6 . x kernels .
* - Hardware full screen scaling in grabdisplay mode .
* - Better support for sysfs . More code to follow for both video device and radio device . Device information is located at / sys / class / video4linux / video0
* - Added module_param so loaded module parameters are displayed in sysfs . Driver parameters should show up in / sys / module / usbvision
* - Adjusted the SAA7111 registers to match the 2.6 . x kernel SAA7111 code . Thanks to the person which helped test .
* - Changed to wait_event_interruptible . For all the people running Fedora 2.
* - Added some screenshots of actual video captures on sourceforge . net .
*
* November 24 , 2004 - 0.9 .8 .1 cvs Dwaine Garden
* - Added patch to check for palette and format in VIDIOCSPICT . Helix Producer should work fine with the driver now . Thanks Jason Simpson
* - Two device description changes and two additions for the maintainer of usb . ids .
*
* December 2 , 2004 - 0.9 .8 .1 cvs Dwaine Garden
* - Added patch for YUV420P and YUV422P video output . Thanks to Alex Smith .
* - Better support for mythtv .
*
* January 2 , 2005 - 0.9 .8 .1 cvs Dwaine Garden
* - Setup that you can specify which device is used for video . Default is auto detect next available device number eg . / dev / videoX
* - Setup that you can specify which device is used for radio . Default is auto detect next available device number eg . / dev / radioX
* - usb_unlink_urb ( ) is deprecated for synchronous unlinks . Using usb_kill_urb instead .
* - usbvision_kvirt_to_pa is deprecated . Removed .
* - Changes are related to kernel changes for 2.6 .10 . ( Fedora 4 )
*
* February 2 , 2005 - 0.9 .8 .1 cvs Dwaine Garden
* - Added a new device to usbvision . h Dazzle DVC 50. Thanks to Luiz S .
*
* March 29 , 2005 - 0.9 .8 .1 cvs Dwaine Garden
* - Fixed compile error with saa7113 under kernels 2.6 .11 +
* - Added module parameter to help people with Black and White output with using s - video input . Some cables and input device are wired differently .
* - Removed the . id from the i2c usbvision template . There was a change to the i2c with kernels 2.6 .11 + .
*
* April 9 , 2005 - 0.9 .8 .1 cvs Dwaine Garden
* - Added in the 2.4 and 2.6 readme files the SwitchSVideoInput parameter information . This will help people setup the right values for the parameter .
* If your device experiences Black and White images with the S - Video Input . Set this parameter to 1 when loading the module .
* - Replaced the wrong 2.6 readme file . I lost the right version . Someone sent me the right version by e - mail . Thanks .
* - Released new module version on sourceforge . net . So everyone can enjoy all the fixes and additional device support .
*
* April 20 , 2005 - 0.9 .8 .2 cvs Dwaine Garden
* - Release lock in usbvision_v4l_read_done . - Thanks to nplanel for the patch .
* - Additional comments to the driver .
* - Fixed some spelling mistakes . 8 * )
*
* April 23 , 2005 - 0.9 .8 .2 cvs Joerg Heckenbach
* - Found bug in usbvision line counting . Now there should be no spurious lines in the image any longer .
* - Swapped usbvision_register_video and usbvision_configure_video to fix problem with PowerOnAtOpen = 0.
* Thanks to Erwan Velu
*
* April 26 , 2005 - 0.9 .8 .2 cvs Joerg Heckenbach
* - Fixed problem with rmmod module and oppses . Replaced vfree ( usbvision - > overlay_base ) with iounmap ( usbvision - > overlay_base ) .
* - Added function usb_get_dev ( dev ) and ; To help with unloading the module multiple times without crashing .
* ( Keep the reference count in kobjects correct )
*
* June 14 , 2005 - 0.9 .8 .2 cvs Dwaine
* - Missed a change in saa7113 . c for checking kernel version . Added conditional if ' s .
*
* June 15 , 2005 - 0.9 .8 .2 cvs Dwaine
* - Added new device WinTV device VendorId 0573 and ProductId 4 d29 .
* - Hacked some support for newer NT1005 devices . This devices only seem to have one configuration , not multiple configurations like the NT1004 .
*
* June 29 , 2005 - 0.9 .8 .2 cvs Dwaine
* - Added new device WinTV device VendorId 0573 and ProductId 4 d37 .
* - Because of the first empty entry in usbvision_table , modutils failed to create the necessary entries for modules . usbmap .
* This means hotplug won ' t work for usbvision . Thanks to Gary Ng .
* - Sent an e - mail to the maintainer of usb . ids . New devices identified need to be added .
* - Fixed compile error with saa7113 under kernel 2.6 .12 .
*
* July 6 , 2005 - 0.9 .8 .2 cvs Dwaine
* - Patch submitted by Gary Ng for two additional procfs entries . Device Input and Frequency setting .
*
* July 12 , 2005 - 0.9 .8 .2 cvs Dwaine
* - New tuner identified for some devices it ' s called TCL_MFPE05 . This tuner uses the same API as tuner 38 in tuner . c .
* - Thanks to lynx31 for contacting Hauppage and asking them .
* - I have no clue as to which devices use this new tuner , so people will have to contact me and tell me .
*
* July 21 , 2005 - 0.9 .8 .2 cvs Dwaine
* - Patched usbvision . c with missing ifdef kernversion statement so the module will compile with older kernels and v4l .
* - Thanks to cipe007 . . . . . .
*
* May 19 , 2006 - 0.9 .8 .3 cvs Dwaine
* - Patched usbvision . c and i2c - algo . c so they will compile with kernel 2.6 .16
* - Adjust device " Pinnacle Studio PCTV USB (PAL) FM " values in usbvision . h
*
* May 24 , 2006 - 0.9 .8 .3 cvs Dwaine
* - Pinnacle Studio PCTV USB ( NTSC ) FM uses saa7111 , not saa7113 like first thought .
* - Updated usbvision . h
*
* Aug 15 , 2006 - 0.9 .8 .3 cvs Dwaine
* - Added saa711x module into cvs , since the newer saa7115 module in newer kernels is v4l2 . The usbvision driver is only v4l .
* - Updated makefile to put compiled modules into correct location .
*
* Aug 21 , 2006 - 0.9 .8 .3 cvs Dwaine
* - Changed number of bytes for i2c write to 4 as per the NT100X spec sheet . Thanks to Merlum for finding it .
* - Remove the radio option for device Hauppauge WinTV USB device Model 40219 Rev E189 . This device does not have a FM radio . Thanks to Shadwell .
* - Added radio option for device Hauppauge WinTV USB device Model 40219 Rev E189 again . Just got an e - mail indicating their device has one . 8 * )
*
* Aug 27 , 2006 - 0.9 .8 .3 cvs Dwaine
* - Changed ifdef statement so the usbvision driver will compile with kernels at 2.6 .12 .
* - Updated readme files for new updated tuner list for v4l devices .
*
*
*
* TODO :
* - use submit_urb for all setup packets
* - Fix memory settings for nt1004 . It is 4 times as big as the
* nt1003 memory .
* - Add audio on endpoint 3 for nt1004 chip . Seems impossible , needs a codec interface . Which one ?
* - Clean up the driver .
* - optimization for performance .
* - Add Videotext capability ( VBI ) . Working on it . . . . .
* - Check audio for other devices
* - Add v4l2 interface
*
*/
# include <linux/kernel.h>
# include <linux/sched.h>
# include <linux/list.h>
# include <linux/timer.h>
# include <linux/slab.h>
# include <linux/mm.h>
# include <linux/utsname.h>
# include <linux/highmem.h>
# include <linux/smp_lock.h>
# include <linux/videodev.h>
# include <linux/vmalloc.h>
# include <linux/module.h>
# include <linux/init.h>
# include <linux/spinlock.h>
# include <asm/io.h>
# include <linux/videodev2.h>
# include <linux/video_decoder.h>
# include <linux/i2c.h>
# define USBVISION_DRIVER_VERSION_MAJOR 0
# define USBVISION_DRIVER_VERSION_MINOR 8
# define USBVISION_DRIVER_VERSION_PATCHLEVEL 0
# define USBVISION_VERSION __stringify(USBVISION_DRIVER_VERSION_MAJOR) "." __stringify(USBVISION_DRIVER_VERSION_MINOR) "." __stringify(USBVISION_DRIVER_VERSION_PATCHLEVEL) " " USBVISION_DRIVER_VERSION_COMMENT
# define USBVISION_DRIVER_VERSION KERNEL_VERSION(USBVISION_DRIVER_VERSION_MAJOR,USBVISION_DRIVER_VERSION_MINOR,USBVISION_DRIVER_VERSION_PATCHLEVEL)
# include <media/saa7115.h>
# include <media/v4l2-common.h>
# include <media/tuner.h>
# include <media/audiochip.h>
# include <linux/moduleparam.h>
# include <linux/workqueue.h>
# ifdef CONFIG_KMOD
# include <linux/kmod.h>
# endif
# include "usbvision.h"
# include "usbvision_ioctl.h"
# define DRIVER_VERSION "0.9.8.3cvs for Linux kernels 2.4.19-2.4.32 + 2.6.0-2.6.17, compiled at "__DATE__", "__TIME__
# define EMAIL "joerg@heckenbach-aw.de"
# define DRIVER_AUTHOR "Joerg Heckenbach <joerg@heckenbach-aw.de>, Dwaine Garden <DwaineGarden@rogers.com>"
# define DRIVER_DESC "USBVision USB Video Device Driver for Linux"
# define DRIVER_LICENSE "GPL"
# define DRIVER_ALIAS "USBVision"
# define ENABLE_HEXDUMP 0 /* Enable if you need it */
# define USBVISION_DEBUG /* Turn on debug messages */
# ifdef USBVISION_DEBUG
# define PDEBUG(level, fmt, args...) \
if ( debug & ( level ) ) info ( " [%s:%d] " fmt , __PRETTY_FUNCTION__ , __LINE__ , # # args )
# else
# define PDEBUG(level, fmt, args...) do {} while(0)
# endif
# define DBG_IOCTL 1<<3
# define DBG_IO 1<<4
# define DBG_RIO 1<<5
# define DBG_HEADER 1<<7
# define DBG_PROBE 1<<8
# define DBG_IRQ 1<<9
# define DBG_ISOC 1<<10
# define DBG_PARSE 1<<11
# define DBG_SCRATCH 1<<12
# define DBG_FUNC 1<<13
# define DBG_I2C 1<<14
# define DEBUG(x...) /* General Debug */
# define IODEBUG(x...) /* Debug IO */
# define OVDEBUG(x...) /* Debug overlay */
# define MDEBUG(x...) /* Debug memory management */
//String operations
# define rmspace(str) while(*str==' ') str++;
# define goto2next(str) while(*str!=' ') str++; while(*str==' ') str++;
static int usbvision_nr = 0 ; // sequential number of usbvision device
static const int max_imgwidth = MAX_FRAME_WIDTH ;
static const int max_imgheight = MAX_FRAME_HEIGHT ;
static const int min_imgwidth = MIN_FRAME_WIDTH ;
static const int min_imgheight = MIN_FRAME_HEIGHT ;
# define FRAMERATE_MIN 0
# define FRAMERATE_MAX 31
enum {
ISOC_MODE_YUV422 = 0x03 ,
ISOC_MODE_YUV420 = 0x14 ,
ISOC_MODE_COMPRESS = 0x60 ,
} ;
2006-12-04 19:31:01 +08:00
static struct usbvision_v4l2_format_st usbvision_v4l2_format [ ] = {
{ 1 , 1 , 8 , V4L2_PIX_FMT_GREY , " GREY " } ,
{ 1 , 2 , 16 , V4L2_PIX_FMT_RGB565 , " RGB565 " } ,
{ 1 , 3 , 24 , V4L2_PIX_FMT_RGB24 , " RGB24 " } ,
{ 1 , 4 , 32 , V4L2_PIX_FMT_RGB32 , " RGB32 " } ,
{ 1 , 2 , 16 , V4L2_PIX_FMT_RGB555 , " RGB555 " } ,
{ 1 , 2 , 16 , V4L2_PIX_FMT_YUYV , " YUV422 " } ,
{ 1 , 2 , 12 , V4L2_PIX_FMT_YVU420 , " YUV420P " } , // 1.5 !
{ 1 , 2 , 16 , V4L2_PIX_FMT_YUV422P , " YUV422P " }
} ;
2006-12-04 19:30:53 +08:00
/*
* The value of ' scratch_buf_size ' affects quality of the picture
* in many ways . Shorter buffers may cause loss of data when client
* is too slow . Larger buffers are memory - consuming and take longer
* to work with . This setting can be adjusted , but the default value
* should be OK for most desktop users .
*/
# define DEFAULT_SCRATCH_BUF_SIZE (0x20000) // 128kB memory scratch buffer
static const int scratch_buf_size = DEFAULT_SCRATCH_BUF_SIZE ;
static int init_brightness = 128 ; // Initalize the brightness of the video device
static int init_contrast = 192 ; // Initalize the contrast of the video device
static int init_saturation = 128 ; // Initalize the staturation mode of the video device
static int init_hue = 128 ; // Initalize the Hue settings of the video device
// Function prototypes
static int usbvision_restart_isoc ( struct usb_usbvision * usbvision ) ;
static int usbvision_begin_streaming ( struct usb_usbvision * usbvision ) ;
static int usbvision_muxsel ( struct usb_usbvision * usbvision , int channel , int norm ) ;
static int usbvision_i2c_write ( void * data , unsigned char addr , char * buf , short len ) ;
static int usbvision_i2c_read ( void * data , unsigned char addr , char * buf , short len ) ;
static int usbvision_read_reg ( struct usb_usbvision * usbvision , unsigned char reg ) ;
static int usbvision_write_reg ( struct usb_usbvision * usbvision , unsigned char reg , unsigned char value ) ;
static int usbvision_request_intra ( struct usb_usbvision * usbvision ) ;
static int usbvision_unrequest_intra ( struct usb_usbvision * usbvision ) ;
static int usbvision_adjust_compression ( struct usb_usbvision * usbvision ) ;
static int usbvision_measure_bandwidth ( struct usb_usbvision * usbvision ) ;
static void usbvision_release ( struct usb_usbvision * usbvision ) ;
static int usbvision_set_input ( struct usb_usbvision * usbvision ) ;
static int usbvision_set_output ( struct usb_usbvision * usbvision , int width , int height ) ;
static void call_i2c_clients ( struct usb_usbvision * usbvision , unsigned int cmd , void * arg ) ;
// Bit flags (options)
# define FLAGS_RETRY_VIDIOCSYNC (1 << 0)
# define FLAGS_MONOCHROME (1 << 1)
# define FLAGS_DISPLAY_HINTS (1 << 2)
# define FLAGS_OSD_STATS (1 << 3)
# define FLAGS_FORCE_TESTPATTERN (1 << 4)
# define FLAGS_SEPARATE_FRAMES (1 << 5)
# define FLAGS_CLEAN_FRAMES (1 << 6)
// Default initalization of device driver parameters
static int flags = 0 ; // Set the default Overlay Display mode of the device driver
static int debug = 0 ; // Set the default Debug Mode of the device driver
static int isocMode = ISOC_MODE_COMPRESS ; // Set the default format for ISOC endpoint
static int adjustCompression = 1 ; // Set the compression to be adaptive
static int dga = 1 ; // Set the default Direct Graphic Access
static int PowerOnAtOpen = 1 ; // Set the default device to power on at startup
static int SwitchSVideoInput = 0 ; // To help people with Black and White output with using s-video input. Some cables and input device are wired differently.
static int video_nr = - 1 ; // Sequential Number of Video Device
static int radio_nr = - 1 ; // Sequential Number of Radio Device
static int vbi_nr = - 1 ; // Sequential Number of VBI Device
static char * CustomDevice = NULL ; // Set as nothing....
// Grab parameters for the device driver
# if defined(module_param) // Showing parameters under SYSFS
module_param ( flags , int , 0444 ) ;
module_param ( debug , int , 0444 ) ;
module_param ( isocMode , int , 0444 ) ;
module_param ( adjustCompression , int , 0444 ) ;
module_param ( dga , int , 0444 ) ;
module_param ( PowerOnAtOpen , int , 0444 ) ;
module_param ( SwitchSVideoInput , int , 0444 ) ;
module_param ( video_nr , int , 0444 ) ;
module_param ( radio_nr , int , 0444 ) ;
module_param ( vbi_nr , int , 0444 ) ;
module_param ( CustomDevice , charp , 0444 ) ;
# else // Old Style
MODULE_PARM ( flags , " i " ) ; // Grab the Overlay Display mode of the device driver
MODULE_PARM ( debug , " i " ) ; // Grab the Debug Mode of the device driver
MODULE_PARM ( isocMode , " i " ) ; // Grab the video format of the video device
MODULE_PARM ( adjustCompression , " i " ) ; // Grab the compression to be adaptive
MODULE_PARM ( dga , " i " ) ; // Grab the Direct Graphic Access
MODULE_PARM ( PowerOnAtOpen , " i " ) ; // Grab the device to power on at startup
MODULE_PARM ( SwitchSVideoInput , " i " ) ; // To help people with Black and White output with using s-video input. Some cables and input device are wired differently.
MODULE_PARM ( video_nr , " i " ) ; // video_nr option allows to specify a certain /dev/videoX device (like /dev/video0 or /dev/video1 ...)
MODULE_PARM ( radio_nr , " i " ) ; // radio_nr option allows to specify a certain /dev/radioX device (like /dev/radio0 or /dev/radio1 ...)
MODULE_PARM ( vbi_nr , " i " ) ; // vbi_nr option allows to specify a certain /dev/vbiX device (like /dev/vbi0 or /dev/vbi1 ...)
MODULE_PARM ( CustomDevice , " s " ) ; // .... CustomDevice
# endif
MODULE_PARM_DESC ( flags , " Set the default Overlay Display mode of the device driver. Default: 0 (Off) " ) ;
MODULE_PARM_DESC ( debug , " Set the default Debug Mode of the device driver. Default: 0 (Off) " ) ;
MODULE_PARM_DESC ( isocMode , " Set the default format for ISOC endpoint. Default: 0x60 (Compression On) " ) ;
MODULE_PARM_DESC ( adjustCompression , " Set the ADPCM compression for the device. Default: 1 (On) " ) ;
MODULE_PARM_DESC ( dga , " Set the Direct Graphic Access for the device. Default: 1 (On) " ) ;
MODULE_PARM_DESC ( PowerOnAtOpen , " Set the default device to power on when device is opened. Default: 1 (On) " ) ;
MODULE_PARM_DESC ( SwitchSVideoInput , " Set the S-Video input. Some cables and input device are wired differently. Default: 0 (Off) " ) ;
MODULE_PARM_DESC ( video_nr , " Set video device number (/dev/videoX). Default: -1 (autodetect) " ) ;
MODULE_PARM_DESC ( radio_nr , " Set radio device number (/dev/radioX). Default: -1 (autodetect) " ) ;
MODULE_PARM_DESC ( vbi_nr , " Set vbi device number (/dev/vbiX). Default: -1 (autodetect) " ) ;
MODULE_PARM_DESC ( CustomDevice , " Define the fine tuning parameters for the device. Default: null " ) ;
// Misc stuff
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( DRIVER_LICENSE ) ;
MODULE_VERSION ( DRIVER_VERSION ) ;
MODULE_ALIAS ( DRIVER_ALIAS ) ;
# ifdef MODULE
static unsigned int autoload = 1 ;
# else
static unsigned int autoload = 0 ;
# endif
/****************************************************************************************/
/* SYSFS Code - Copied from the stv680.c usb module. */
/* Device information is located at /sys/class/video4linux/video0 */
/* Device parameters information is located at /sys/module/usbvision */
/* Device USB Information is located at /sys/bus/usb/drivers/USBVision Video Grabber */
/****************************************************************************************/
# define YES_NO(x) ((x) ? "Yes" : "No")
static inline struct usb_usbvision * cd_to_usbvision ( struct class_device * cd )
{
struct video_device * vdev = to_video_device ( cd ) ;
return video_get_drvdata ( vdev ) ;
}
static ssize_t show_version ( struct class_device * cd , char * buf )
{
return sprintf ( buf , " %s \n " , DRIVER_VERSION ) ;
}
static CLASS_DEVICE_ATTR ( version , S_IRUGO , show_version , NULL ) ;
static ssize_t show_model ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %s \n " , usbvision_device_data [ usbvision - > DevModel ] . ModelString ) ;
}
static CLASS_DEVICE_ATTR ( model , S_IRUGO , show_model , NULL ) ;
static ssize_t show_hue ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %d \n " , usbvision - > hue > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( hue , S_IRUGO , show_hue , NULL ) ;
static ssize_t show_contrast ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %d \n " , usbvision - > contrast > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( contrast , S_IRUGO , show_contrast , NULL ) ;
static ssize_t show_brightness ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %d \n " , usbvision - > brightness > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( brightness , S_IRUGO , show_brightness , NULL ) ;
static ssize_t show_saturation ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %d \n " , usbvision - > saturation > > 8 ) ;
}
static CLASS_DEVICE_ATTR ( saturation , S_IRUGO , show_saturation , NULL ) ;
static ssize_t show_streaming ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %s \n " , YES_NO ( usbvision - > streaming ) ) ;
}
static CLASS_DEVICE_ATTR ( streaming , S_IRUGO , show_streaming , NULL ) ;
static ssize_t show_overlay ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %s \n " , YES_NO ( usbvision - > overlay ) ) ;
}
static CLASS_DEVICE_ATTR ( overlay , S_IRUGO , show_overlay , NULL ) ;
static ssize_t show_compression ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %s \n " , YES_NO ( usbvision - > isocMode = = ISOC_MODE_COMPRESS ) ) ;
}
static CLASS_DEVICE_ATTR ( compression , S_IRUGO , show_compression , NULL ) ;
static ssize_t show_device_bridge ( struct class_device * class_dev , char * buf )
{
struct video_device * vdev = to_video_device ( class_dev ) ;
struct usb_usbvision * usbvision = video_get_drvdata ( vdev ) ;
return sprintf ( buf , " %d \n " , usbvision - > bridgeType ) ;
}
static CLASS_DEVICE_ATTR ( bridge , S_IRUGO , show_device_bridge , NULL ) ;
static void usbvision_create_sysfs ( struct video_device * vdev )
{
if ( vdev ) {
video_device_create_file ( vdev , & class_device_attr_version ) ;
video_device_create_file ( vdev , & class_device_attr_model ) ;
video_device_create_file ( vdev , & class_device_attr_hue ) ;
video_device_create_file ( vdev , & class_device_attr_contrast ) ;
video_device_create_file ( vdev , & class_device_attr_brightness ) ;
video_device_create_file ( vdev , & class_device_attr_saturation ) ;
video_device_create_file ( vdev , & class_device_attr_streaming ) ;
video_device_create_file ( vdev , & class_device_attr_overlay ) ;
video_device_create_file ( vdev , & class_device_attr_compression ) ;
video_device_create_file ( vdev , & class_device_attr_bridge ) ;
}
}
static void usbvision_remove_sysfs ( struct video_device * vdev )
{
if ( vdev ) {
video_device_remove_file ( vdev , & class_device_attr_version ) ;
video_device_remove_file ( vdev , & class_device_attr_model ) ;
video_device_remove_file ( vdev , & class_device_attr_hue ) ;
video_device_remove_file ( vdev , & class_device_attr_contrast ) ;
video_device_remove_file ( vdev , & class_device_attr_brightness ) ;
video_device_remove_file ( vdev , & class_device_attr_saturation ) ;
video_device_remove_file ( vdev , & class_device_attr_streaming ) ;
video_device_remove_file ( vdev , & class_device_attr_overlay ) ;
video_device_remove_file ( vdev , & class_device_attr_compression ) ;
video_device_remove_file ( vdev , & class_device_attr_bridge ) ;
}
}
/*******************************/
/* Memory management functions */
/*******************************/
/*
* Here we want the physical address of the memory .
* This is used when initializing the contents of the area .
*/
void * usbvision_rvmalloc ( unsigned long size )
{
void * mem ;
unsigned long adr ;
size = PAGE_ALIGN ( size ) ;
mem = vmalloc_32 ( size ) ;
if ( ! mem )
return NULL ;
memset ( mem , 0 , size ) ; /* Clear the ram out, no junk to the user */
adr = ( unsigned long ) mem ;
while ( size > 0 ) {
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
mem_map_reserve ( vmalloc_to_page ( ( void * ) adr ) ) ;
# else
SetPageReserved ( vmalloc_to_page ( ( void * ) adr ) ) ;
# endif
adr + = PAGE_SIZE ;
size - = PAGE_SIZE ;
}
return mem ;
}
void usbvision_rvfree ( void * mem , unsigned long size )
{
unsigned long adr ;
if ( ! mem )
return ;
adr = ( unsigned long ) mem ;
while ( ( long ) size > 0 ) {
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
mem_map_unreserve ( vmalloc_to_page ( ( void * ) adr ) ) ;
# else
ClearPageReserved ( vmalloc_to_page ( ( void * ) adr ) ) ;
# endif
adr + = PAGE_SIZE ;
size - = PAGE_SIZE ;
}
vfree ( mem ) ;
}
# if ENABLE_HEXDUMP
static void usbvision_hexdump ( const unsigned char * data , int len )
{
char tmp [ 80 ] ;
int i , k ;
for ( i = k = 0 ; len > 0 ; i + + , len - - ) {
if ( i > 0 & & ( i % 16 = = 0 ) ) {
printk ( " %s \n " , tmp ) ;
k = 0 ;
}
k + = sprintf ( & tmp [ k ] , " %02x " , data [ i ] ) ;
}
if ( k > 0 )
printk ( " %s \n " , tmp ) ;
}
# endif
/* These procedures handle the scratch ring buffer */
int scratch_len ( struct usb_usbvision * usbvision ) /*This returns the amount of data actually in the buffer */
{
int len = usbvision - > scratch_write_ptr - usbvision - > scratch_read_ptr ;
if ( len < 0 ) {
len + = scratch_buf_size ;
}
PDEBUG ( DBG_SCRATCH , " scratch_len() = %d \n " , len ) ;
return len ;
}
/* This returns the free space left in the buffer */
int scratch_free ( struct usb_usbvision * usbvision )
{
int free = usbvision - > scratch_read_ptr - usbvision - > scratch_write_ptr ;
if ( free < = 0 ) {
free + = scratch_buf_size ;
}
if ( free ) {
free - = 1 ; /* at least one byte in the buffer must */
/* left blank, otherwise there is no chance to differ between full and empty */
}
PDEBUG ( DBG_SCRATCH , " return %d \n " , free ) ;
return free ;
}
void * debug_memcpy ( void * dest , void * src , size_t len )
{
printk ( KERN_DEBUG " memcpy(%p, %p, %d); \n " , dest , src , len ) ;
return memcpy ( dest , src , len ) ;
}
/* This puts data into the buffer */
int scratch_put ( struct usb_usbvision * usbvision , unsigned char * data , int len )
{
int len_part ;
if ( usbvision - > scratch_write_ptr + len < scratch_buf_size ) {
memcpy ( usbvision - > scratch + usbvision - > scratch_write_ptr , data , len ) ;
usbvision - > scratch_write_ptr + = len ;
}
else {
len_part = scratch_buf_size - usbvision - > scratch_write_ptr ;
memcpy ( usbvision - > scratch + usbvision - > scratch_write_ptr , data , len_part ) ;
if ( len = = len_part ) {
usbvision - > scratch_write_ptr = 0 ; /* just set write_ptr to zero */
}
else {
memcpy ( usbvision - > scratch , data + len_part , len - len_part ) ;
usbvision - > scratch_write_ptr = len - len_part ;
}
}
PDEBUG ( DBG_SCRATCH , " len=%d, new write_ptr=%d \n " , len , usbvision - > scratch_write_ptr ) ;
return len ;
}
/* This marks the write_ptr as position of new frame header */
void scratch_mark_header ( struct usb_usbvision * usbvision )
{
PDEBUG ( DBG_SCRATCH , " header at write_ptr=%d \n " , usbvision - > scratch_headermarker_write_ptr ) ;
usbvision - > scratch_headermarker [ usbvision - > scratch_headermarker_write_ptr ] =
usbvision - > scratch_write_ptr ;
usbvision - > scratch_headermarker_write_ptr + = 1 ;
usbvision - > scratch_headermarker_write_ptr % = USBVISION_NUM_HEADERMARKER ;
}
/* This gets data from the buffer at the given "ptr" position */
int scratch_get_extra ( struct usb_usbvision * usbvision , unsigned char * data , int * ptr , int len )
{
int len_part ;
if ( * ptr + len < scratch_buf_size ) {
memcpy ( data , usbvision - > scratch + * ptr , len ) ;
* ptr + = len ;
}
else {
len_part = scratch_buf_size - * ptr ;
memcpy ( data , usbvision - > scratch + * ptr , len_part ) ;
if ( len = = len_part ) {
* ptr = 0 ; /* just set the y_ptr to zero */
}
else {
memcpy ( data + len_part , usbvision - > scratch , len - len_part ) ;
* ptr = len - len_part ;
}
}
PDEBUG ( DBG_SCRATCH , " len=%d, new ptr=%d \n " , len , * ptr ) ;
return len ;
}
/* This sets the scratch extra read pointer */
void scratch_set_extra_ptr ( struct usb_usbvision * usbvision , int * ptr , int len )
{
* ptr = ( usbvision - > scratch_read_ptr + len ) % scratch_buf_size ;
PDEBUG ( DBG_SCRATCH , " ptr=%d \n " , * ptr ) ;
}
/*This increments the scratch extra read pointer */
void scratch_inc_extra_ptr ( int * ptr , int len )
{
* ptr = ( * ptr + len ) % scratch_buf_size ;
PDEBUG ( DBG_SCRATCH , " ptr=%d \n " , * ptr ) ;
}
/* This gets data from the buffer */
int scratch_get ( struct usb_usbvision * usbvision , unsigned char * data , int len )
{
int len_part ;
if ( usbvision - > scratch_read_ptr + len < scratch_buf_size ) {
memcpy ( data , usbvision - > scratch + usbvision - > scratch_read_ptr , len ) ;
usbvision - > scratch_read_ptr + = len ;
}
else {
len_part = scratch_buf_size - usbvision - > scratch_read_ptr ;
memcpy ( data , usbvision - > scratch + usbvision - > scratch_read_ptr , len_part ) ;
if ( len = = len_part ) {
usbvision - > scratch_read_ptr = 0 ; /* just set the read_ptr to zero */
}
else {
memcpy ( data + len_part , usbvision - > scratch , len - len_part ) ;
usbvision - > scratch_read_ptr = len - len_part ;
}
}
PDEBUG ( DBG_SCRATCH , " len=%d, new read_ptr=%d \n " , len , usbvision - > scratch_read_ptr ) ;
return len ;
}
/* This sets read pointer to next header and returns it */
int scratch_get_header ( struct usb_usbvision * usbvision , struct usbvision_frame_header * header )
{
int errCode = 0 ;
PDEBUG ( DBG_SCRATCH , " from read_ptr=%d " , usbvision - > scratch_headermarker_read_ptr ) ;
while ( usbvision - > scratch_headermarker_write_ptr -
usbvision - > scratch_headermarker_read_ptr ! = 0 ) {
usbvision - > scratch_read_ptr =
usbvision - > scratch_headermarker [ usbvision - > scratch_headermarker_read_ptr ] ;
usbvision - > scratch_headermarker_read_ptr + = 1 ;
usbvision - > scratch_headermarker_read_ptr % = USBVISION_NUM_HEADERMARKER ;
scratch_get ( usbvision , ( unsigned char * ) header , USBVISION_HEADER_LENGTH ) ;
if ( ( header - > magic_1 = = USBVISION_MAGIC_1 )
& & ( header - > magic_2 = = USBVISION_MAGIC_2 )
& & ( header - > headerLength = = USBVISION_HEADER_LENGTH ) ) {
errCode = USBVISION_HEADER_LENGTH ;
header - > frameWidth = header - > frameWidthLo + ( header - > frameWidthHi < < 8 ) ;
header - > frameHeight = header - > frameHeightLo + ( header - > frameHeightHi < < 8 ) ;
break ;
}
}
return errCode ;
}
/*This removes len bytes of old data from the buffer */
void scratch_rm_old ( struct usb_usbvision * usbvision , int len )
{
usbvision - > scratch_read_ptr + = len ;
usbvision - > scratch_read_ptr % = scratch_buf_size ;
PDEBUG ( DBG_SCRATCH , " read_ptr is now %d \n " , usbvision - > scratch_read_ptr ) ;
}
/*This resets the buffer - kills all data in it too */
void scratch_reset ( struct usb_usbvision * usbvision )
{
PDEBUG ( DBG_SCRATCH , " \n " ) ;
usbvision - > scratch_read_ptr = 0 ;
usbvision - > scratch_write_ptr = 0 ;
usbvision - > scratch_headermarker_read_ptr = 0 ;
usbvision - > scratch_headermarker_write_ptr = 0 ;
usbvision - > isocstate = IsocState_NoFrame ;
}
/* Here comes the OVERLAY stuff */
/* Tell the interrupt handler what to to. */
static
void usbvision_cap ( struct usb_usbvision * usbvision , int on )
{
DEBUG ( printk ( KERN_DEBUG " usbvision_cap: overlay was %d, set it to %d \n " , usbvision - > overlay , on ) ; )
if ( on ) {
usbvision - > overlay = 1 ;
}
else {
usbvision - > overlay = 0 ;
}
}
/* append a new clipregion to the vector of video_clips */
static
void usbvision_new_clip ( struct v4l2_format * vf , struct v4l2_clip * vcp , int x , int y , int w , int h )
{
vcp [ vf - > fmt . win . clipcount ] . c . left = x ;
vcp [ vf - > fmt . win . clipcount ] . c . top = y ;
vcp [ vf - > fmt . win . clipcount ] . c . width = w ;
vcp [ vf - > fmt . win . clipcount ] . c . height = h ;
vf - > fmt . win . clipcount + + ;
}
# define mark_pixel(x,y) usbvision->clipmask[((x) + (y) * MAX_FRAME_WIDTH) / 32] |= 0x00000001<<((x)%32)
# define clipped_pixel(index) usbvision->clipmask[(index) / 32] & (0x00000001<<((index)%32))
static
void usbvision_built_overlay ( struct usb_usbvision * usbvision , int count , struct v4l2_clip * vcp )
{
usbvision - > overlay_win = usbvision - > overlay_base +
( signed int ) usbvision - > vid_win . fmt . win . w . left * usbvision - > depth / 8 +
( signed int ) usbvision - > vid_win . fmt . win . w . top * usbvision - > vid_buf . fmt . bytesperline ;
IODEBUG ( printk ( KERN_DEBUG " built_overlay base=%p, win=%p, bpl=%d, clips=%d, size=%dx%d \n " ,
usbvision - > overlay_base , usbvision - > overlay_win ,
usbvision - > vid_buf . fmt . bytesperline , count ,
usbvision - > vid_win . fmt . win . w . width , usbvision - > vid_win . fmt . win . w . height ) ; )
/* Add here generation of clipping mask */
{
int x_start , x_end , y_start , y_end ;
int clip_index , x , y ;
memset ( usbvision - > clipmask , 0 , USBVISION_CLIPMASK_SIZE ) ;
OVDEBUG ( printk ( KERN_DEBUG " clips = %d \n " , count ) ; )
for ( clip_index = 0 ; clip_index < count ; clip_index + + ) {
OVDEBUG ( printk ( KERN_DEBUG " clip: %d,%d,%d,%d \n " , vcp [ clip_index ] . x ,
vcp [ clip_index ] . y ,
vcp [ clip_index ] . width ,
vcp [ clip_index ] . height ) ; )
x_start = vcp [ clip_index ] . c . left ;
if ( x_start > = ( int ) usbvision - > vid_win . fmt . win . w . width ) {
OVDEBUG ( printk ( KERN_DEBUG " x_start=%d \n " , x_start ) ; )
continue ; //clipping window is right of overlay window
}
x_end = x_start + vcp [ clip_index ] . c . width ;
if ( x_end < = 0 ) {
OVDEBUG ( printk ( KERN_DEBUG " x_end=%d \n " , x_end ) ; )
continue ; //clipping window is left of overlay window
}
y_start = vcp [ clip_index ] . c . top ;
if ( y_start > = ( int ) usbvision - > vid_win . fmt . win . w . height ) {
OVDEBUG ( printk ( KERN_DEBUG " y_start=%d \n " , y_start ) ; )
continue ; //clipping window is below overlay window
}
y_end = y_start + vcp [ clip_index ] . c . height ;
if ( y_end < = 0 ) {
OVDEBUG ( printk ( KERN_DEBUG " y_end=%d \n " , y_end ) ; )
continue ; //clipping window is above overlay window
}
//clip the clipping window
if ( x_start < 0 ) {
x_start = 0 ;
}
if ( x_end > ( int ) usbvision - > vid_win . fmt . win . w . width ) {
x_end = ( int ) usbvision - > vid_win . fmt . win . w . width ;
}
if ( y_start < 0 ) {
y_start = 0 ;
}
if ( y_end > ( int ) usbvision - > vid_win . fmt . win . w . height ) {
y_end = ( int ) usbvision - > vid_win . fmt . win . w . height ;
}
OVDEBUG ( printk ( KERN_DEBUG " clip_o: %d,%d,%d,%d \n " , x_start , y_start , x_end , y_end ) ; )
for ( y = y_start ; y < y_end ; y + + ) {
for ( x = x_start ; x < x_end ; x + + ) {
mark_pixel ( x , y ) ;
}
}
}
}
}
void usbvision_osd_char ( struct usb_usbvision * usbvision ,
struct usbvision_frame * frame , int x , int y , int ch )
{
static const unsigned short digits [ 16 ] = {
0xF6DE , /* 0 */
0x2492 , /* 1 */
0xE7CE , /* 2 */
0xE79E , /* 3 */
0xB792 , /* 4 */
0xF39E , /* 5 */
0xF3DE , /* 6 */
0xF492 , /* 7 */
0xF7DE , /* 8 */
0xF79E , /* 9 */
0x77DA , /* a */
0xD75C , /* b */
0xF24E , /* c */
0xD6DC , /* d */
0xF34E , /* e */
0xF348 /* f */
} ;
unsigned short digit ;
int ix , iy ;
if ( ( usbvision = = NULL ) | | ( frame = = NULL ) )
return ;
if ( ch > = ' 0 ' & & ch < = ' 9 ' )
ch - = ' 0 ' ;
else if ( ch > = ' A ' & & ch < = ' F ' )
ch = 10 + ( ch - ' A ' ) ;
else if ( ch > = ' a ' & & ch < = ' f ' )
ch = 10 + ( ch - ' a ' ) ;
else
return ;
digit = digits [ ch ] ;
for ( iy = 0 ; iy < 5 ; iy + + ) {
for ( ix = 0 ; ix < 3 ; ix + + ) {
if ( digit & 0x8000 ) {
// USBVISION_PUTPIXEL(frame, x + ix, y + iy,
// 0xFF, 0xFF, 0xFF);
}
digit = digit < < 1 ;
}
}
}
void usbvision_osd_string ( struct usb_usbvision * usbvision ,
struct usbvision_frame * frame ,
int x , int y , const char * str )
{
while ( * str ) {
usbvision_osd_char ( usbvision , frame , x , y , * str ) ;
str + + ;
x + = 4 ; /* 3 pixels character + 1 space */
}
}
/*
* usb_usbvision_osd_stats ( )
*
* On screen display of important debugging information .
*
*/
void usbvision_osd_stats ( struct usb_usbvision * usbvision ,
struct usbvision_frame * frame )
{
const int y_diff = 8 ;
char tmp [ 16 ] ;
int x = 10 ;
int y = 10 ;
sprintf ( tmp , " %8x " , usbvision - > frame_num ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8lx " , usbvision - > isocUrbCount ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8lx " , usbvision - > urb_length ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8lx " , usbvision - > isocDataCount ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8lx " , usbvision - > header_count ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8lx " , usbvision - > scratch_ovf_count ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8lx " , usbvision - > isocSkipCount ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8lx " , usbvision - > isocErrCount ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8x " , usbvision - > saturation ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8x " , usbvision - > hue ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8x " , usbvision - > brightness > > 8 ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
sprintf ( tmp , " %8x " , usbvision - > contrast > > 12 ) ;
usbvision_osd_string ( usbvision , frame , x , y , tmp ) ;
y + = y_diff ;
}
/*
* usbvision_testpattern ( )
*
* Procedure forms a test pattern ( yellow grid on blue background ) .
*
* Parameters :
* fullframe : if TRUE then entire frame is filled , otherwise the procedure
* continues from the current scanline .
* pmode 0 : fill the frame with solid blue color ( like on VCR or TV )
* 1 : Draw a colored grid
*
*/
void usbvision_testpattern ( struct usb_usbvision * usbvision , int fullframe ,
int pmode )
{
static const char proc [ ] = " usbvision_testpattern " ;
struct usbvision_frame * frame ;
unsigned char * f ;
int num_cell = 0 ;
int scan_length = 0 ;
static int num_pass = 0 ;
if ( usbvision = = NULL ) {
printk ( KERN_ERR " %s: usbvision == NULL \n " , proc ) ;
return ;
}
if ( ( usbvision - > curFrameNum < 0 )
| | ( usbvision - > curFrameNum > = USBVISION_NUMFRAMES ) ) {
printk ( KERN_ERR " %s: usbvision->curFrameNum=%d. \n " , proc ,
usbvision - > curFrameNum ) ;
return ;
}
/* Grab the current frame */
frame = & usbvision - > frame [ usbvision - > curFrameNum ] ;
/* Optionally start at the beginning */
if ( fullframe ) {
frame - > curline = 0 ;
frame - > scanlength = 0 ;
}
/* Form every scan line */
for ( ; frame - > curline < frame - > frmheight ; frame - > curline + + ) {
int i ;
f = frame - > data + ( usbvision - > curwidth * 3 * frame - > curline ) ;
for ( i = 0 ; i < usbvision - > curwidth ; i + + ) {
unsigned char cb = 0x80 ;
unsigned char cg = 0 ;
unsigned char cr = 0 ;
if ( pmode = = 1 ) {
if ( frame - > curline % 32 = = 0 )
cb = 0 , cg = cr = 0xFF ;
else if ( i % 32 = = 0 ) {
if ( frame - > curline % 32 = = 1 )
num_cell + + ;
cb = 0 , cg = cr = 0xFF ;
} else {
cb =
( ( num_cell * 7 ) +
num_pass ) & 0xFF ;
cg =
( ( num_cell * 5 ) +
num_pass * 2 ) & 0xFF ;
cr =
( ( num_cell * 3 ) +
num_pass * 3 ) & 0xFF ;
}
} else {
/* Just the blue screen */
}
* f + + = cb ;
* f + + = cg ;
* f + + = cr ;
scan_length + = 3 ;
}
}
frame - > grabstate = FrameState_Done ;
frame - > scanlength + = scan_length ;
+ + num_pass ;
/* We do this unconditionally, regardless of FLAGS_OSD_STATS */
usbvision_osd_stats ( usbvision , frame ) ;
}
/*
* Here comes the data parsing stuff that is run as interrupt
*/
/*
* usbvision_find_header ( )
*
* Locate one of supported header markers in the scratch buffer .
*/
static enum ParseState usbvision_find_header ( struct usb_usbvision * usbvision )
{
struct usbvision_frame * frame ;
int foundHeader = 0 ;
if ( usbvision - > overlay ) {
frame = & usbvision - > overlay_frame ;
}
else {
frame = & usbvision - > frame [ usbvision - > curFrameNum ] ;
}
while ( scratch_get_header ( usbvision , & frame - > isocHeader ) = = USBVISION_HEADER_LENGTH ) {
// found header in scratch
PDEBUG ( DBG_HEADER , " found header: 0x%02x%02x %d %d %d %d %#x 0x%02x %u %u " ,
frame - > isocHeader . magic_2 ,
frame - > isocHeader . magic_1 ,
frame - > isocHeader . headerLength ,
frame - > isocHeader . frameNum ,
frame - > isocHeader . framePhase ,
frame - > isocHeader . frameLatency ,
frame - > isocHeader . dataFormat ,
frame - > isocHeader . formatParam ,
frame - > isocHeader . frameWidth ,
frame - > isocHeader . frameHeight ) ;
if ( usbvision - > requestIntra ) {
if ( frame - > isocHeader . formatParam & 0x80 ) {
foundHeader = 1 ;
usbvision - > lastIsocFrameNum = - 1 ; // do not check for lost frames this time
usbvision_unrequest_intra ( usbvision ) ;
break ;
}
}
else {
foundHeader = 1 ;
break ;
}
}
if ( foundHeader ) {
frame - > frmwidth = frame - > isocHeader . frameWidth * usbvision - > stretch_width ;
frame - > frmheight = frame - > isocHeader . frameHeight * usbvision - > stretch_height ;
frame - > v4l2_linesize = ( frame - > frmwidth * frame - > v4l2_format . depth ) > > 3 ;
usbvision - > curFrame = frame ;
}
else { // no header found
PDEBUG ( DBG_HEADER , " skipping scratch data, no header " ) ;
scratch_reset ( usbvision ) ;
return ParseState_EndParse ;
}
// found header
if ( frame - > isocHeader . dataFormat = = ISOC_MODE_COMPRESS ) {
//check isocHeader.frameNum for lost frames
if ( usbvision - > lastIsocFrameNum > = 0 ) {
if ( ( ( usbvision - > lastIsocFrameNum + 1 ) % 32 ) ! = frame - > isocHeader . frameNum ) {
// unexpected frame drop: need to request new intra frame
PDEBUG ( DBG_HEADER , " Lost frame before %d on USB " , frame - > isocHeader . frameNum ) ;
usbvision_request_intra ( usbvision ) ;
return ParseState_NextFrame ;
}
}
usbvision - > lastIsocFrameNum = frame - > isocHeader . frameNum ;
}
usbvision - > header_count + + ;
frame - > scanstate = ScanState_Lines ;
frame - > curline = 0 ;
if ( flags & FLAGS_FORCE_TESTPATTERN ) {
usbvision_testpattern ( usbvision , 1 , 1 ) ;
return ParseState_NextFrame ;
}
return ParseState_Continue ;
}
static enum ParseState usbvision_parse_lines_422 ( struct usb_usbvision * usbvision ,
long * pcopylen )
{
volatile struct usbvision_frame * frame ;
unsigned char * f ;
int len ;
int i ;
unsigned char yuyv [ 4 ] = { 180 , 128 , 10 , 128 } ; // YUV components
unsigned char rv , gv , bv ; // RGB components
int clipmask_index , bytes_per_pixel ;
int overlay = usbvision - > overlay ;
int stretch_bytes , clipmask_add ;
if ( overlay ) {
frame = & usbvision - > overlay_frame ;
if ( usbvision - > overlay_base = = NULL ) {
//video_buffer is not set yet
return ParseState_NextFrame ;
}
f = usbvision - > overlay_win + frame - > curline *
usbvision - > vid_buf . fmt . bytesperline ;
}
else {
frame = & usbvision - > frame [ usbvision - > curFrameNum ] ;
f = frame - > data + ( frame - > v4l2_linesize * frame - > curline ) ;
}
/* Make sure there's enough data for the entire line */
len = ( frame - > isocHeader . frameWidth * 2 ) + 5 ;
if ( scratch_len ( usbvision ) < len ) {
PDEBUG ( DBG_PARSE , " out of data in line %d, need %u. \n " , frame - > curline , len ) ;
return ParseState_Out ;
}
if ( ( frame - > curline + 1 ) > = frame - > frmheight ) {
return ParseState_NextFrame ;
}
bytes_per_pixel = frame - > v4l2_format . bytes_per_pixel ;
stretch_bytes = ( usbvision - > stretch_width - 1 ) * bytes_per_pixel ;
clipmask_index = frame - > curline * MAX_FRAME_WIDTH ;
clipmask_add = usbvision - > stretch_width ;
for ( i = 0 ; i < frame - > frmwidth ; i + = ( 2 * usbvision - > stretch_width ) ) {
scratch_get ( usbvision , & yuyv [ 0 ] , 4 ) ;
if ( ( overlay ) & & ( clipped_pixel ( clipmask_index ) ) ) {
f + = bytes_per_pixel ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) {
* f + + = yuyv [ 0 ] ; // Y
* f + + = yuyv [ 3 ] ; // U
}
else {
YUV_TO_RGB_BY_THE_BOOK ( yuyv [ 0 ] , yuyv [ 1 ] , yuyv [ 3 ] , rv , gv , bv ) ;
switch ( frame - > v4l2_format . format ) {
case V4L2_PIX_FMT_RGB565 :
* f + + = ( 0x1F & ( bv > > 3 ) ) | ( 0xE0 & ( gv < < 3 ) ) ;
* f + + = ( 0x07 & ( gv > > 5 ) ) | ( 0xF8 & rv ) ;
break ;
case V4L2_PIX_FMT_RGB24 :
* f + + = bv ;
* f + + = gv ;
* f + + = rv ;
break ;
case V4L2_PIX_FMT_RGB32 :
* f + + = bv ;
* f + + = gv ;
* f + + = rv ;
f + + ;
break ;
case V4L2_PIX_FMT_RGB555 :
* f + + = ( 0x1F & ( bv > > 3 ) ) | ( 0xE0 & ( gv < < 2 ) ) ;
* f + + = ( 0x03 & ( gv > > 6 ) ) | ( 0x7C & ( rv > > 1 ) ) ;
break ;
}
}
clipmask_index + = clipmask_add ;
f + = stretch_bytes ;
if ( ( overlay ) & & ( clipped_pixel ( clipmask_index ) ) ) {
f + = bytes_per_pixel ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) {
* f + + = yuyv [ 2 ] ; // Y
* f + + = yuyv [ 1 ] ; // V
}
else {
YUV_TO_RGB_BY_THE_BOOK ( yuyv [ 2 ] , yuyv [ 1 ] , yuyv [ 3 ] , rv , gv , bv ) ;
switch ( frame - > v4l2_format . format ) {
case V4L2_PIX_FMT_RGB565 :
* f + + = ( 0x1F & ( bv > > 3 ) ) | ( 0xE0 & ( gv < < 3 ) ) ;
* f + + = ( 0x07 & ( gv > > 5 ) ) | ( 0xF8 & rv ) ;
break ;
case V4L2_PIX_FMT_RGB24 :
* f + + = bv ;
* f + + = gv ;
* f + + = rv ;
break ;
case V4L2_PIX_FMT_RGB32 :
* f + + = bv ;
* f + + = gv ;
* f + + = rv ;
f + + ;
break ;
case V4L2_PIX_FMT_RGB555 :
* f + + = ( 0x1F & ( bv > > 3 ) ) | ( 0xE0 & ( gv < < 2 ) ) ;
* f + + = ( 0x03 & ( gv > > 6 ) ) | ( 0x7C & ( rv > > 1 ) ) ;
break ;
}
}
clipmask_index + = clipmask_add ;
f + = stretch_bytes ;
}
frame - > curline + = usbvision - > stretch_height ;
* pcopylen + = frame - > v4l2_linesize * usbvision - > stretch_height ;
if ( frame - > curline > = frame - > frmheight ) {
return ParseState_NextFrame ;
}
else {
return ParseState_Continue ;
}
}
static int usbvision_decompress ( struct usb_usbvision * usbvision , unsigned char * Compressed ,
unsigned char * Decompressed , int * StartPos ,
int * BlockTypeStartPos , int Len )
{
int RestPixel , Idx , MaxPos , Pos , ExtraPos , BlockLen , BlockTypePos , BlockTypeLen ;
unsigned char BlockByte , BlockCode , BlockType , BlockTypeByte , Integrator ;
Integrator = 0 ;
Pos = * StartPos ;
BlockTypePos = * BlockTypeStartPos ;
MaxPos = 396 ; //Pos + Len;
ExtraPos = Pos ;
BlockLen = 0 ;
BlockByte = 0 ;
BlockCode = 0 ;
BlockType = 0 ;
BlockTypeByte = 0 ;
BlockTypeLen = 0 ;
RestPixel = Len ;
for ( Idx = 0 ; Idx < Len ; Idx + + ) {
if ( BlockLen = = 0 ) {
if ( BlockTypeLen = = 0 ) {
BlockTypeByte = Compressed [ BlockTypePos ] ;
BlockTypePos + + ;
BlockTypeLen = 4 ;
}
BlockType = ( BlockTypeByte & 0xC0 ) > > 6 ;
//statistic:
usbvision - > ComprBlockTypes [ BlockType ] + + ;
Pos = ExtraPos ;
if ( BlockType = = 0 ) {
if ( RestPixel > = 24 ) {
Idx + = 23 ;
RestPixel - = 24 ;
Integrator = Decompressed [ Idx ] ;
} else {
Idx + = RestPixel - 1 ;
RestPixel = 0 ;
}
} else {
BlockCode = Compressed [ Pos ] ;
Pos + + ;
if ( RestPixel > = 24 ) {
BlockLen = 24 ;
} else {
BlockLen = RestPixel ;
}
RestPixel - = BlockLen ;
ExtraPos = Pos + ( BlockLen / 4 ) ;
}
BlockTypeByte < < = 2 ;
BlockTypeLen - = 1 ;
}
if ( BlockLen > 0 ) {
if ( ( BlockLen % 4 ) = = 0 ) {
BlockByte = Compressed [ Pos ] ;
Pos + + ;
}
if ( BlockType = = 1 ) { //inter Block
Integrator = Decompressed [ Idx ] ;
}
switch ( BlockByte & 0xC0 ) {
case 0x03 < < 6 :
Integrator + = Compressed [ ExtraPos ] ;
ExtraPos + + ;
break ;
case 0x02 < < 6 :
Integrator + = BlockCode ;
break ;
case 0x00 :
Integrator - = BlockCode ;
break ;
}
Decompressed [ Idx ] = Integrator ;
BlockByte < < = 2 ;
BlockLen - = 1 ;
}
}
* StartPos = ExtraPos ;
* BlockTypeStartPos = BlockTypePos ;
return Idx ;
}
/*
* usbvision_parse_compress ( )
*
* Parse compressed frame from the scratch buffer , put
* decoded RGB value into the current frame buffer and add the written
* number of bytes ( RGB ) to the * pcopylen .
*
*/
static enum ParseState usbvision_parse_compress ( struct usb_usbvision * usbvision ,
long * pcopylen )
{
# define USBVISION_STRIP_MAGIC 0x5A
# define USBVISION_STRIP_LEN_MAX 400
# define USBVISION_STRIP_HEADER_LEN 3
struct usbvision_frame * frame ;
unsigned char * f , * u = NULL , * v = NULL ;
unsigned char StripData [ USBVISION_STRIP_LEN_MAX ] ;
unsigned char StripHeader [ USBVISION_STRIP_HEADER_LEN ] ;
int Idx , IdxEnd , StripLen , StripPtr , StartBlockPos , BlockPos , BlockTypePos ;
int clipmask_index , bytes_per_pixel , rc ;
int overlay = usbvision - > overlay ;
int imageSize ;
unsigned char rv , gv , bv ;
static unsigned char * Y , * U , * V ;
if ( overlay ) {
frame = & usbvision - > overlay_frame ;
imageSize = frame - > frmwidth * frame - > frmheight ;
if ( usbvision - > overlay_base = = NULL ) {
//video_buffer is not set yet
return ParseState_NextFrame ;
}
f = usbvision - > overlay_win + frame - > curline *
usbvision - > vid_buf . fmt . bytesperline ;
}
else {
frame = & usbvision - > frame [ usbvision - > curFrameNum ] ;
imageSize = frame - > frmwidth * frame - > frmheight ;
if ( ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUV422P ) | |
( frame - > v4l2_format . format = = V4L2_PIX_FMT_YVU420 ) )
{ // this is a planar format
//... v4l2_linesize not used here.
f = frame - > data + ( frame - > width * frame - > curline ) ;
} else
f = frame - > data + ( frame - > v4l2_linesize * frame - > curline ) ;
if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) { //initialise u and v pointers
// get base of u and b planes add halfoffset
u = frame - > data
+ imageSize
+ ( frame - > frmwidth > > 1 ) * frame - > curline ;
v = u + ( imageSize > > 1 ) ;
} else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YVU420 ) {
v = frame - > data + imageSize + ( ( frame - > curline * ( frame - > width ) ) > > 2 ) ;
u = v + ( imageSize > > 2 ) ;
}
}
if ( frame - > curline = = 0 ) {
usbvision_adjust_compression ( usbvision ) ;
}
if ( scratch_len ( usbvision ) < USBVISION_STRIP_HEADER_LEN ) {
return ParseState_Out ;
}
//get strip header without changing the scratch_read_ptr
scratch_set_extra_ptr ( usbvision , & StripPtr , 0 ) ;
scratch_get_extra ( usbvision , & StripHeader [ 0 ] , & StripPtr ,
USBVISION_STRIP_HEADER_LEN ) ;
if ( StripHeader [ 0 ] ! = USBVISION_STRIP_MAGIC ) {
// wrong strip magic
usbvision - > stripMagicErrors + + ;
return ParseState_NextFrame ;
}
if ( frame - > curline ! = ( int ) StripHeader [ 2 ] ) {
//line number missmatch error
usbvision - > stripLineNumberErrors + + ;
}
StripLen = 2 * ( unsigned int ) StripHeader [ 1 ] ;
if ( StripLen > USBVISION_STRIP_LEN_MAX ) {
// strip overrun
// I think this never happens
usbvision_request_intra ( usbvision ) ;
}
if ( scratch_len ( usbvision ) < StripLen ) {
//there is not enough data for the strip
return ParseState_Out ;
}
if ( usbvision - > IntraFrameBuffer ) {
Y = usbvision - > IntraFrameBuffer + frame - > frmwidth * frame - > curline ;
U = usbvision - > IntraFrameBuffer + imageSize + ( frame - > frmwidth / 2 ) * ( frame - > curline / 2 ) ;
V = usbvision - > IntraFrameBuffer + imageSize / 4 * 5 + ( frame - > frmwidth / 2 ) * ( frame - > curline / 2 ) ;
}
else {
return ParseState_NextFrame ;
}
bytes_per_pixel = frame - > v4l2_format . bytes_per_pixel ;
clipmask_index = frame - > curline * MAX_FRAME_WIDTH ;
scratch_get ( usbvision , StripData , StripLen ) ;
IdxEnd = frame - > frmwidth ;
BlockTypePos = USBVISION_STRIP_HEADER_LEN ;
StartBlockPos = BlockTypePos + ( IdxEnd - 1 ) / 96 + ( IdxEnd / 2 - 1 ) / 96 + 2 ;
BlockPos = StartBlockPos ;
usbvision - > BlockPos = BlockPos ;
if ( ( rc = usbvision_decompress ( usbvision , StripData , Y , & BlockPos , & BlockTypePos , IdxEnd ) ) ! = IdxEnd ) {
//return ParseState_Continue;
}
if ( StripLen > usbvision - > maxStripLen ) {
usbvision - > maxStripLen = StripLen ;
}
if ( frame - > curline % 2 ) {
if ( ( rc = usbvision_decompress ( usbvision , StripData , V , & BlockPos , & BlockTypePos , IdxEnd / 2 ) ) ! = IdxEnd / 2 ) {
//return ParseState_Continue;
}
}
else {
if ( ( rc = usbvision_decompress ( usbvision , StripData , U , & BlockPos , & BlockTypePos , IdxEnd / 2 ) ) ! = IdxEnd / 2 ) {
//return ParseState_Continue;
}
}
if ( BlockPos > usbvision - > comprBlockPos ) {
usbvision - > comprBlockPos = BlockPos ;
}
if ( BlockPos > StripLen ) {
usbvision - > stripLenErrors + + ;
}
for ( Idx = 0 ; Idx < IdxEnd ; Idx + + ) {
if ( ( overlay ) & & ( clipped_pixel ( clipmask_index ) ) ) {
f + = bytes_per_pixel ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) {
* f + + = Y [ Idx ] ;
* f + + = Idx & 0x01 ? U [ Idx / 2 ] : V [ Idx / 2 ] ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUV422P ) {
* f + + = Y [ Idx ] ;
if ( Idx & 0x01 )
* u + + = U [ Idx > > 1 ] ;
else
* v + + = V [ Idx > > 1 ] ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YVU420 ) {
* f + + = Y [ Idx ] ;
if ( ! ( ( Idx & 0x01 ) | ( frame - > curline & 0x01 ) ) ) {
/* only need do this for 1 in 4 pixels */
/* intraframe buffer is YUV420 format */
* u + + = U [ Idx > > 1 ] ;
* v + + = V [ Idx > > 1 ] ;
}
}
else {
YUV_TO_RGB_BY_THE_BOOK ( Y [ Idx ] , U [ Idx / 2 ] , V [ Idx / 2 ] , rv , gv , bv ) ;
switch ( frame - > v4l2_format . format ) {
case V4L2_PIX_FMT_GREY :
* f + + = Y [ Idx ] ;
break ;
case V4L2_PIX_FMT_RGB555 :
* f + + = ( 0x1F & ( bv > > 3 ) ) | ( 0xE0 & ( gv < < 2 ) ) ;
* f + + = ( 0x03 & ( gv > > 6 ) ) | ( 0x7C & ( rv > > 1 ) ) ;
break ;
case V4L2_PIX_FMT_RGB565 :
* f + + = ( 0x1F & ( bv > > 3 ) ) | ( 0xE0 & ( gv < < 3 ) ) ;
* f + + = ( 0x07 & ( gv > > 5 ) ) | ( 0xF8 & rv ) ;
break ;
case V4L2_PIX_FMT_RGB24 :
* f + + = bv ;
* f + + = gv ;
* f + + = rv ;
break ;
case V4L2_PIX_FMT_RGB32 :
* f + + = bv ;
* f + + = gv ;
* f + + = rv ;
f + + ;
break ;
}
}
clipmask_index + + ;
}
/* Deal with non-integer no. of bytes for YUV420P */
if ( frame - > v4l2_format . format ! = V4L2_PIX_FMT_YVU420 )
* pcopylen + = frame - > v4l2_linesize ;
else
* pcopylen + = frame - > curline & 0x01 ? frame - > v4l2_linesize : frame - > v4l2_linesize < < 1 ;
frame - > curline + = 1 ;
if ( frame - > curline > = frame - > frmheight ) {
return ParseState_NextFrame ;
}
else {
return ParseState_Continue ;
}
}
/*
* usbvision_parse_lines_420 ( )
*
* Parse two lines from the scratch buffer , put
* decoded RGB value into the current frame buffer and add the written
* number of bytes ( RGB ) to the * pcopylen .
*
*/
static enum ParseState usbvision_parse_lines_420 ( struct usb_usbvision * usbvision ,
long * pcopylen )
{
struct usbvision_frame * frame ;
unsigned char * f_even = NULL , * f_odd = NULL ;
unsigned int pixel_per_line , block ;
int pixel , block_split ;
int y_ptr , u_ptr , v_ptr , y_odd_offset ;
const int y_block_size = 128 ;
const int uv_block_size = 64 ;
const int sub_block_size = 32 ;
const int y_step [ ] = { 0 , 0 , 0 , 2 } , y_step_size = 4 ;
const int uv_step [ ] = { 0 , 0 , 0 , 4 } , uv_step_size = 4 ;
unsigned char y [ 2 ] , u , v ; /* YUV components */
int y_ , u_ , v_ , vb , uvg , ur ;
int r_ , g_ , b_ ; /* RGB components */
unsigned char g ;
int clipmask_even_index , clipmask_odd_index , bytes_per_pixel ;
int clipmask_add , stretch_bytes ;
int overlay = usbvision - > overlay ;
if ( overlay ) {
frame = & usbvision - > overlay_frame ;
if ( usbvision - > overlay_base = = NULL ) {
//video_buffer is not set yet
return ParseState_NextFrame ;
}
f_even = usbvision - > overlay_win + frame - > curline *
usbvision - > vid_buf . fmt . bytesperline ;
f_odd = f_even + usbvision - > vid_buf . fmt . bytesperline * usbvision - > stretch_height ;
}
else {
frame = & usbvision - > frame [ usbvision - > curFrameNum ] ;
f_even = frame - > data + ( frame - > v4l2_linesize * frame - > curline ) ;
f_odd = f_even + frame - > v4l2_linesize * usbvision - > stretch_height ;
}
/* Make sure there's enough data for the entire line */
/* In this mode usbvision transfer 3 bytes for every 2 pixels */
/* I need two lines to decode the color */
bytes_per_pixel = frame - > v4l2_format . bytes_per_pixel ;
stretch_bytes = ( usbvision - > stretch_width - 1 ) * bytes_per_pixel ;
clipmask_even_index = frame - > curline * MAX_FRAME_WIDTH ;
clipmask_odd_index = clipmask_even_index + MAX_FRAME_WIDTH ;
clipmask_add = usbvision - > stretch_width ;
pixel_per_line = frame - > isocHeader . frameWidth ;
if ( scratch_len ( usbvision ) < ( int ) pixel_per_line * 3 ) {
//printk(KERN_DEBUG "out of data, need %d\n", len);
return ParseState_Out ;
}
if ( ( frame - > curline + 1 ) > = frame - > frmheight ) {
return ParseState_NextFrame ;
}
block_split = ( pixel_per_line % y_block_size ) ? 1 : 0 ; //are some blocks splitted into different lines?
y_odd_offset = ( pixel_per_line / y_block_size ) * ( y_block_size + uv_block_size )
+ block_split * uv_block_size ;
scratch_set_extra_ptr ( usbvision , & y_ptr , y_odd_offset ) ;
scratch_set_extra_ptr ( usbvision , & u_ptr , y_block_size ) ;
scratch_set_extra_ptr ( usbvision , & v_ptr , y_odd_offset
+ ( 4 - block_split ) * sub_block_size ) ;
for ( block = 0 ; block < ( pixel_per_line / sub_block_size ) ;
block + + ) {
for ( pixel = 0 ; pixel < sub_block_size ; pixel + = 2 ) {
scratch_get ( usbvision , & y [ 0 ] , 2 ) ;
scratch_get_extra ( usbvision , & u , & u_ptr , 1 ) ;
scratch_get_extra ( usbvision , & v , & v_ptr , 1 ) ;
//I don't use the YUV_TO_RGB macro for better performance
v_ = v - 128 ;
u_ = u - 128 ;
vb = 132252 * v_ ;
uvg = - 53281 * u_ - 25625 * v_ ;
ur = 104595 * u_ ;
if ( ( overlay ) & & ( clipped_pixel ( clipmask_even_index ) ) ) {
f_even + = bytes_per_pixel ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) {
* f_even + + = y [ 0 ] ;
* f_even + + = v ;
}
else {
y_ = 76284 * ( y [ 0 ] - 16 ) ;
b_ = ( y_ + vb ) > > 16 ;
g_ = ( y_ + uvg ) > > 16 ;
r_ = ( y_ + ur ) > > 16 ;
switch ( frame - > v4l2_format . format ) {
case V4L2_PIX_FMT_RGB565 :
g = LIMIT_RGB ( g_ ) ;
* f_even + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 3 ) ) ;
* f_even + + = ( 0x07 & ( g > > 5 ) ) | ( 0xF8 & LIMIT_RGB ( r_ ) ) ;
break ;
case V4L2_PIX_FMT_RGB24 :
* f_even + + = LIMIT_RGB ( b_ ) ;
* f_even + + = LIMIT_RGB ( g_ ) ;
* f_even + + = LIMIT_RGB ( r_ ) ;
break ;
case V4L2_PIX_FMT_RGB32 :
* f_even + + = LIMIT_RGB ( b_ ) ;
* f_even + + = LIMIT_RGB ( g_ ) ;
* f_even + + = LIMIT_RGB ( r_ ) ;
f_even + + ;
break ;
case V4L2_PIX_FMT_RGB555 :
g = LIMIT_RGB ( g_ ) ;
* f_even + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 2 ) ) ;
* f_even + + = ( 0x03 & ( g > > 6 ) ) |
( 0x7C & ( LIMIT_RGB ( r_ ) > > 1 ) ) ;
break ;
}
}
clipmask_even_index + = clipmask_add ;
f_even + = stretch_bytes ;
if ( ( overlay ) & & ( clipped_pixel ( clipmask_even_index ) ) ) {
f_even + = bytes_per_pixel ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) {
* f_even + + = y [ 1 ] ;
* f_even + + = u ;
}
else {
y_ = 76284 * ( y [ 1 ] - 16 ) ;
b_ = ( y_ + vb ) > > 16 ;
g_ = ( y_ + uvg ) > > 16 ;
r_ = ( y_ + ur ) > > 16 ;
switch ( frame - > v4l2_format . format ) {
case V4L2_PIX_FMT_RGB565 :
g = LIMIT_RGB ( g_ ) ;
* f_even + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 3 ) ) ;
* f_even + + = ( 0x07 & ( g > > 5 ) ) | ( 0xF8 & LIMIT_RGB ( r_ ) ) ;
break ;
case V4L2_PIX_FMT_RGB24 :
* f_even + + = LIMIT_RGB ( b_ ) ;
* f_even + + = LIMIT_RGB ( g_ ) ;
* f_even + + = LIMIT_RGB ( r_ ) ;
break ;
case V4L2_PIX_FMT_RGB32 :
* f_even + + = LIMIT_RGB ( b_ ) ;
* f_even + + = LIMIT_RGB ( g_ ) ;
* f_even + + = LIMIT_RGB ( r_ ) ;
f_even + + ;
break ;
case V4L2_PIX_FMT_RGB555 :
g = LIMIT_RGB ( g_ ) ;
* f_even + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 2 ) ) ;
* f_even + + = ( 0x03 & ( g > > 6 ) ) |
( 0x7C & ( LIMIT_RGB ( r_ ) > > 1 ) ) ;
break ;
}
}
clipmask_even_index + = clipmask_add ;
f_even + = stretch_bytes ;
scratch_get_extra ( usbvision , & y [ 0 ] , & y_ptr , 2 ) ;
if ( ( overlay ) & & ( clipped_pixel ( clipmask_odd_index ) ) ) {
f_odd + = bytes_per_pixel ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) {
* f_odd + + = y [ 0 ] ;
* f_odd + + = v ;
}
else {
y_ = 76284 * ( y [ 0 ] - 16 ) ;
b_ = ( y_ + vb ) > > 16 ;
g_ = ( y_ + uvg ) > > 16 ;
r_ = ( y_ + ur ) > > 16 ;
switch ( frame - > v4l2_format . format ) {
case V4L2_PIX_FMT_RGB565 :
g = LIMIT_RGB ( g_ ) ;
* f_odd + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 3 ) ) ;
* f_odd + + = ( 0x07 & ( g > > 5 ) ) | ( 0xF8 & LIMIT_RGB ( r_ ) ) ;
break ;
case V4L2_PIX_FMT_RGB24 :
* f_odd + + = LIMIT_RGB ( b_ ) ;
* f_odd + + = LIMIT_RGB ( g_ ) ;
* f_odd + + = LIMIT_RGB ( r_ ) ;
break ;
case V4L2_PIX_FMT_RGB32 :
* f_odd + + = LIMIT_RGB ( b_ ) ;
* f_odd + + = LIMIT_RGB ( g_ ) ;
* f_odd + + = LIMIT_RGB ( r_ ) ;
f_odd + + ;
break ;
case V4L2_PIX_FMT_RGB555 :
g = LIMIT_RGB ( g_ ) ;
* f_odd + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 2 ) ) ;
* f_odd + + = ( 0x03 & ( g > > 6 ) ) |
( 0x7C & ( LIMIT_RGB ( r_ ) > > 1 ) ) ;
break ;
}
}
clipmask_odd_index + = clipmask_add ;
f_odd + = stretch_bytes ;
if ( ( overlay ) & & ( clipped_pixel ( clipmask_odd_index ) ) ) {
f_odd + = bytes_per_pixel ;
}
else if ( frame - > v4l2_format . format = = V4L2_PIX_FMT_YUYV ) {
* f_odd + + = y [ 1 ] ;
* f_odd + + = u ;
}
else {
y_ = 76284 * ( y [ 1 ] - 16 ) ;
b_ = ( y_ + vb ) > > 16 ;
g_ = ( y_ + uvg ) > > 16 ;
r_ = ( y_ + ur ) > > 16 ;
switch ( frame - > v4l2_format . format ) {
case V4L2_PIX_FMT_RGB565 :
g = LIMIT_RGB ( g_ ) ;
* f_odd + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 3 ) ) ;
* f_odd + + = ( 0x07 & ( g > > 5 ) ) | ( 0xF8 & LIMIT_RGB ( r_ ) ) ;
break ;
case V4L2_PIX_FMT_RGB24 :
* f_odd + + = LIMIT_RGB ( b_ ) ;
* f_odd + + = LIMIT_RGB ( g_ ) ;
* f_odd + + = LIMIT_RGB ( r_ ) ;
break ;
case V4L2_PIX_FMT_RGB32 :
* f_odd + + = LIMIT_RGB ( b_ ) ;
* f_odd + + = LIMIT_RGB ( g_ ) ;
* f_odd + + = LIMIT_RGB ( r_ ) ;
f_odd + + ;
break ;
case V4L2_PIX_FMT_RGB555 :
g = LIMIT_RGB ( g_ ) ;
* f_odd + + = ( 0x1F & ( LIMIT_RGB ( b_ ) > > 3 ) ) | ( 0xE0 & ( g < < 2 ) ) ;
* f_odd + + = ( 0x03 & ( g > > 6 ) ) |
( 0x7C & ( LIMIT_RGB ( r_ ) > > 1 ) ) ;
break ;
}
}
clipmask_odd_index + = clipmask_add ;
f_odd + = stretch_bytes ;
}
scratch_rm_old ( usbvision , y_step [ block % y_step_size ] * sub_block_size ) ;
scratch_inc_extra_ptr ( & y_ptr , y_step [ ( block + 2 * block_split ) % y_step_size ]
* sub_block_size ) ;
scratch_inc_extra_ptr ( & u_ptr , uv_step [ block % uv_step_size ]
* sub_block_size ) ;
scratch_inc_extra_ptr ( & v_ptr , uv_step [ ( block + 2 * block_split ) % uv_step_size ]
* sub_block_size ) ;
}
scratch_rm_old ( usbvision , pixel_per_line * 3 / 2
+ block_split * sub_block_size ) ;
frame - > curline + = 2 * usbvision - > stretch_height ;
* pcopylen + = frame - > v4l2_linesize * 2 * usbvision - > stretch_height ;
if ( frame - > curline > = frame - > frmheight )
return ParseState_NextFrame ;
else
return ParseState_Continue ;
}
/*
* usbvision_parse_data ( )
*
* Generic routine to parse the scratch buffer . It employs either
* usbvision_find_header ( ) or usbvision_parse_lines ( ) to do most
* of work .
*
*/
static void usbvision_parse_data ( struct usb_usbvision * usbvision )
{
struct usbvision_frame * frame ;
enum ParseState newstate ;
long copylen = 0 ;
if ( usbvision - > overlay ) {
frame = & usbvision - > overlay_frame ;
}
else {
frame = & usbvision - > frame [ usbvision - > curFrameNum ] ;
}
PDEBUG ( DBG_PARSE , " parsing len=%d \n " , scratch_len ( usbvision ) ) ;
while ( 1 ) {
newstate = ParseState_Out ;
if ( scratch_len ( usbvision ) ) {
if ( frame - > scanstate = = ScanState_Scanning ) {
newstate = usbvision_find_header ( usbvision ) ;
}
else if ( frame - > scanstate = = ScanState_Lines ) {
if ( usbvision - > isocMode = = ISOC_MODE_YUV420 ) {
newstate = usbvision_parse_lines_420 ( usbvision , & copylen ) ;
}
else if ( usbvision - > isocMode = = ISOC_MODE_YUV422 ) {
newstate = usbvision_parse_lines_422 ( usbvision , & copylen ) ;
}
else if ( usbvision - > isocMode = = ISOC_MODE_COMPRESS ) {
newstate = usbvision_parse_compress ( usbvision , & copylen ) ;
}
}
}
if ( newstate = = ParseState_Continue ) {
continue ;
}
else if ( ( newstate = = ParseState_NextFrame ) | | ( newstate = = ParseState_Out ) ) {
break ;
}
else {
return ; /* ParseState_EndParse */
}
}
if ( newstate = = ParseState_NextFrame ) {
frame - > grabstate = FrameState_Done ;
do_gettimeofday ( & ( frame - > timestamp ) ) ;
frame - > sequence = usbvision - > frame_num ;
if ( usbvision - > overlay ) {
frame - > grabstate = FrameState_Grabbing ;
frame - > scanstate = ScanState_Scanning ;
frame - > scanlength = 0 ;
copylen = 0 ;
}
else {
usbvision - > curFrameNum = - 1 ;
}
usbvision - > frame_num + + ;
/* Optionally display statistics on the screen */
if ( flags & FLAGS_OSD_STATS )
usbvision_osd_stats ( usbvision , frame ) ;
/* This will cause the process to request another frame. */
if ( waitqueue_active ( & frame - > wq ) ) {
wake_up_interruptible ( & frame - > wq ) ;
}
}
/* Update the frame's uncompressed length. */
frame - > scanlength + = copylen ;
}
/*
* Make all of the blocks of data contiguous
*/
static int usbvision_compress_isochronous ( struct usb_usbvision * usbvision ,
struct urb * urb )
{
unsigned char * packet_data ;
int i , totlen = 0 ;
for ( i = 0 ; i < urb - > number_of_packets ; i + + ) {
int packet_len = urb - > iso_frame_desc [ i ] . actual_length ;
int packet_stat = urb - > iso_frame_desc [ i ] . status ;
packet_data = urb - > transfer_buffer + urb - > iso_frame_desc [ i ] . offset ;
/* Detect and ignore errored packets */
if ( packet_stat ) { // packet_stat != 0 ?????????????
PDEBUG ( DBG_ISOC , " data error: [%d] len=%d, status=%X " , i , packet_len , packet_stat ) ;
usbvision - > isocErrCount + + ;
continue ;
}
/* Detect and ignore empty packets */
if ( packet_len < 0 ) {
PDEBUG ( DBG_ISOC , " error packet [%d] " , i ) ;
usbvision - > isocSkipCount + + ;
continue ;
}
else if ( packet_len = = 0 ) { /* Frame end ????? */
PDEBUG ( DBG_ISOC , " null packet [%d] " , i ) ;
usbvision - > isocstate = IsocState_NoFrame ;
usbvision - > isocSkipCount + + ;
continue ;
}
else if ( packet_len > usbvision - > isocPacketSize ) {
PDEBUG ( DBG_ISOC , " packet[%d] > isocPacketSize " , i ) ;
usbvision - > isocSkipCount + + ;
continue ;
}
PDEBUG ( DBG_ISOC , " packet ok [%d] len=%d " , i , packet_len ) ;
if ( usbvision - > isocstate = = IsocState_NoFrame ) { //new frame begins
usbvision - > isocstate = IsocState_InFrame ;
scratch_mark_header ( usbvision ) ;
usbvision_measure_bandwidth ( usbvision ) ;
PDEBUG ( DBG_ISOC , " packet with header " ) ;
}
/*
* If usbvision continues to feed us with data but there is no
* consumption ( if , for example , V4L client fell asleep ) we
* may overflow the buffer . We have to move old data over to
* free room for new data . This is bad for old data . If we
* just drop new data then it ' s bad for new data . . . choose
* your favorite evil here .
*/
if ( scratch_free ( usbvision ) < packet_len ) {
usbvision - > scratch_ovf_count + + ;
PDEBUG ( DBG_ISOC , " scratch buf overflow! scr_len: %d, n: %d " ,
scratch_len ( usbvision ) , packet_len ) ;
scratch_rm_old ( usbvision , packet_len - scratch_free ( usbvision ) ) ;
}
/* Now we know that there is enough room in scratch buffer */
scratch_put ( usbvision , packet_data , packet_len ) ;
totlen + = packet_len ;
usbvision - > isocDataCount + = packet_len ;
usbvision - > isocPacketCount + + ;
}
# if ENABLE_HEXDUMP
if ( totlen > 0 ) {
static int foo = 0 ;
if ( foo < 1 ) {
printk ( KERN_DEBUG " +%d. \n " , usbvision - > scratchlen ) ;
usbvision_hexdump ( data0 , ( totlen > 64 ) ? 64 : totlen ) ;
+ + foo ;
}
}
# endif
return totlen ;
}
static void usbvision_isocIrq ( struct urb * urb , struct pt_regs * regs )
{
int errCode = 0 ;
int len ;
struct usb_usbvision * usbvision = urb - > context ;
int i ;
unsigned long startTime = jiffies ;
/* We don't want to do anything if we are about to be removed! */
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return ;
if ( ! usbvision - > streaming ) {
PDEBUG ( DBG_IRQ , " oops, not streaming, but interrupt " ) ;
return ;
}
/* Copy the data received into our scratch buffer */
len = usbvision_compress_isochronous ( usbvision , urb ) ;
usbvision - > isocUrbCount + + ;
usbvision - > urb_length = len ;
for ( i = 0 ; i < USBVISION_URB_FRAMES ; i + + ) {
urb - > iso_frame_desc [ i ] . status = 0 ;
urb - > iso_frame_desc [ i ] . actual_length = 0 ;
}
urb - > status = 0 ;
urb - > dev = usbvision - > dev ;
errCode = usb_submit_urb ( urb , GFP_ATOMIC ) ;
/* Disable this warning. By design of the driver. */
// if(errCode) {
// err("%s: usb_submit_urb failed: error %d", __FUNCTION__, errCode);
// }
/* If we collected enough data let's parse! */
if ( scratch_len ( usbvision ) > USBVISION_HEADER_LENGTH ) { /* 12 == header_length */
/*If we don't have a frame we're current working on, complain */
if ( ( usbvision - > curFrameNum > = 0 ) | | ( usbvision - > overlay ) )
usbvision_parse_data ( usbvision ) ;
else {
PDEBUG ( DBG_IRQ , " received data, but no one needs it " ) ;
scratch_reset ( usbvision ) ;
}
}
usbvision - > timeInIrq + = jiffies - startTime ;
return ;
}
/*************************************/
/* Low level usbvision access functions */
/*************************************/
/*
* usbvision_read_reg ( )
*
* return < 0 - > Error
* > = 0 - > Data
*/
static int usbvision_read_reg ( struct usb_usbvision * usbvision , unsigned char reg )
{
int errCode = 0 ;
unsigned char buffer [ 1 ] ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return - 1 ;
errCode = usb_control_msg ( usbvision - > dev , usb_rcvctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT ,
0 , ( __u16 ) reg , buffer , 1 , HZ ) ;
if ( errCode < 0 ) {
err ( " %s: failed: error %d " , __FUNCTION__ , errCode ) ;
return errCode ;
}
return buffer [ 0 ] ;
}
/*
* usbvision_write_reg ( )
*
* return 1 - > Reg written
* 0 - > usbvision is not yet ready
* - 1 - > Something went wrong
*/
static int usbvision_write_reg ( struct usb_usbvision * usbvision , unsigned char reg ,
unsigned char value )
{
int errCode = 0 ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return 0 ;
errCode = usb_control_msg ( usbvision - > dev , usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE ,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_ENDPOINT , 0 , ( __u16 ) reg , & value , 1 , HZ ) ;
if ( errCode < 0 ) {
err ( " %s: failed: error %d " , __FUNCTION__ , errCode ) ;
}
return errCode ;
}
static void usbvision_ctrlUrb_complete ( struct urb * urb , struct pt_regs * regs )
{
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) urb - > context ;
PDEBUG ( DBG_IRQ , " " ) ;
usbvision - > ctrlUrbBusy = 0 ;
if ( waitqueue_active ( & usbvision - > ctrlUrb_wq ) ) {
wake_up_interruptible ( & usbvision - > ctrlUrb_wq ) ;
}
}
static int usbvision_write_reg_irq ( struct usb_usbvision * usbvision , int address ,
unsigned char * data , int len )
{
int errCode = 0 ;
PDEBUG ( DBG_IRQ , " " ) ;
if ( len > 8 ) {
return - EFAULT ;
}
// down(&usbvision->ctrlUrbLock);
if ( usbvision - > ctrlUrbBusy ) {
// up(&usbvision->ctrlUrbLock);
return - EBUSY ;
}
usbvision - > ctrlUrbBusy = 1 ;
// up(&usbvision->ctrlUrbLock);
usbvision - > ctrlUrbSetup . bRequestType = USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT ;
usbvision - > ctrlUrbSetup . bRequest = USBVISION_OP_CODE ;
usbvision - > ctrlUrbSetup . wValue = 0 ;
usbvision - > ctrlUrbSetup . wIndex = cpu_to_le16 ( address ) ;
usbvision - > ctrlUrbSetup . wLength = cpu_to_le16 ( len ) ;
usb_fill_control_urb ( usbvision - > ctrlUrb , usbvision - > dev ,
usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
( unsigned char * ) & usbvision - > ctrlUrbSetup ,
( void * ) usbvision - > ctrlUrbBuffer , len ,
usbvision_ctrlUrb_complete ,
( void * ) usbvision ) ;
memcpy ( usbvision - > ctrlUrbBuffer , data , len ) ;
errCode = usb_submit_urb ( usbvision - > ctrlUrb , GFP_ATOMIC ) ;
if ( errCode < 0 ) {
// error in usb_submit_urb()
usbvision - > ctrlUrbBusy = 0 ;
}
PDEBUG ( DBG_IRQ , " submit %d byte: error %d " , len , errCode ) ;
return errCode ;
}
static int usbvision_init_compression ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
usbvision - > lastIsocFrameNum = - 1 ;
usbvision - > isocDataCount = 0 ;
usbvision - > isocPacketCount = 0 ;
usbvision - > isocSkipCount = 0 ;
usbvision - > comprLevel = 50 ;
usbvision - > lastComprLevel = - 1 ;
usbvision - > isocUrbCount = 0 ;
usbvision - > requestIntra = 1 ;
usbvision - > isocMeasureBandwidthCount = 0 ;
return errCode ;
}
/* this function measures the used bandwidth since last call
* return : 0 : no error
* sets usedBandwidth to 1 - 100 : 1 - 100 % of full bandwidth resp . to isocPacketSize
*/
static int usbvision_measure_bandwidth ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
if ( usbvision - > isocMeasureBandwidthCount < 2 ) { // this gives an average bandwidth of 3 frames
usbvision - > isocMeasureBandwidthCount + + ;
return errCode ;
}
if ( ( usbvision - > isocPacketSize > 0 ) & & ( usbvision - > isocPacketCount > 0 ) ) {
usbvision - > usedBandwidth = usbvision - > isocDataCount /
( usbvision - > isocPacketCount + usbvision - > isocSkipCount ) *
100 / usbvision - > isocPacketSize ;
}
usbvision - > isocMeasureBandwidthCount = 0 ;
usbvision - > isocDataCount = 0 ;
usbvision - > isocPacketCount = 0 ;
usbvision - > isocSkipCount = 0 ;
return errCode ;
}
static int usbvision_adjust_compression ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
unsigned char buffer [ 6 ] ;
PDEBUG ( DBG_IRQ , " " ) ;
if ( ( adjustCompression ) & & ( usbvision - > usedBandwidth > 0 ) ) {
usbvision - > comprLevel + = ( usbvision - > usedBandwidth - 90 ) / 2 ;
RESTRICT_TO_RANGE ( usbvision - > comprLevel , 0 , 100 ) ;
if ( usbvision - > comprLevel ! = usbvision - > lastComprLevel ) {
int distorsion ;
if ( usbvision - > bridgeType = = BRIDGE_NT1004 | | usbvision - > bridgeType = = BRIDGE_NT1005 ) {
buffer [ 0 ] = ( unsigned char ) ( 4 + 16 * usbvision - > comprLevel / 100 ) ; // PCM Threshold 1
buffer [ 1 ] = ( unsigned char ) ( 4 + 8 * usbvision - > comprLevel / 100 ) ; // PCM Threshold 2
distorsion = 7 + 248 * usbvision - > comprLevel / 100 ;
buffer [ 2 ] = ( unsigned char ) ( distorsion & 0xFF ) ; // Average distorsion Threshold (inter)
buffer [ 3 ] = ( unsigned char ) ( distorsion & 0xFF ) ; // Average distorsion Threshold (intra)
distorsion = 1 + 42 * usbvision - > comprLevel / 100 ;
buffer [ 4 ] = ( unsigned char ) ( distorsion & 0xFF ) ; // Maximum distorsion Threshold (inter)
buffer [ 5 ] = ( unsigned char ) ( distorsion & 0xFF ) ; // Maximum distorsion Threshold (intra)
}
else { //BRIDGE_NT1003
buffer [ 0 ] = ( unsigned char ) ( 4 + 16 * usbvision - > comprLevel / 100 ) ; // PCM threshold 1
buffer [ 1 ] = ( unsigned char ) ( 4 + 8 * usbvision - > comprLevel / 100 ) ; // PCM threshold 2
distorsion = 2 + 253 * usbvision - > comprLevel / 100 ;
buffer [ 2 ] = ( unsigned char ) ( distorsion & 0xFF ) ; // distorsion threshold bit0-7
buffer [ 3 ] = 0 ; //(unsigned char)((distorsion >> 8) & 0x0F); // distorsion threshold bit 8-11
distorsion = 0 + 43 * usbvision - > comprLevel / 100 ;
buffer [ 4 ] = ( unsigned char ) ( distorsion & 0xFF ) ; // maximum distorsion bit0-7
buffer [ 5 ] = 0 ; //(unsigned char)((distorsion >> 8) & 0x01); // maximum distorsion bit 8
}
errCode = usbvision_write_reg_irq ( usbvision , USBVISION_PCM_THR1 , buffer , 6 ) ;
if ( errCode = = 0 ) {
PDEBUG ( DBG_IRQ , " new compr params %#02x %#02x %#02x %#02x %#02x %#02x " , buffer [ 0 ] ,
buffer [ 1 ] , buffer [ 2 ] , buffer [ 3 ] , buffer [ 4 ] , buffer [ 5 ] ) ;
usbvision - > lastComprLevel = usbvision - > comprLevel ;
}
}
}
return errCode ;
}
static int usbvision_request_intra ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
unsigned char buffer [ 1 ] ;
PDEBUG ( DBG_IRQ , " " ) ;
usbvision - > requestIntra = 1 ;
buffer [ 0 ] = 1 ;
usbvision_write_reg_irq ( usbvision , USBVISION_FORCE_INTRA , buffer , 1 ) ;
return errCode ;
}
static int usbvision_unrequest_intra ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
unsigned char buffer [ 1 ] ;
PDEBUG ( DBG_IRQ , " " ) ;
usbvision - > requestIntra = 0 ;
buffer [ 0 ] = 0 ;
usbvision_write_reg_irq ( usbvision , USBVISION_FORCE_INTRA , buffer , 1 ) ;
return errCode ;
}
/* ----------------------------------------------------------------------- */
/* I2C functions */
/* ----------------------------------------------------------------------- */
static void call_i2c_clients ( struct usb_usbvision * usbvision , unsigned int cmd ,
void * arg )
{
int i ;
for ( i = 0 ; i < USBVISION_I2C_CLIENTS_MAX ; i + + ) {
if ( NULL = = usbvision - > i2c_clients [ i ] )
continue ;
if ( NULL = = usbvision - > i2c_clients [ i ] - > driver - > command )
continue ;
usbvision - > i2c_clients [ i ] - > driver - > command ( usbvision - > i2c_clients [ i ] , cmd , arg ) ;
}
}
static int attach_inform ( struct i2c_client * client )
{
struct usb_usbvision * usbvision ;
struct tuner_setup tun_addr ;
int i ;
v4l2_std_id stdId ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
usbvision = ( struct usb_usbvision * ) client - > adapter - > data ;
# else
usbvision = ( struct usb_usbvision * ) i2c_get_adapdata ( client - > adapter ) ;
# endif
for ( i = 0 ; i < USBVISION_I2C_CLIENTS_MAX ; i + + ) {
if ( usbvision - > i2c_clients [ i ] = = NULL | |
usbvision - > i2c_clients [ i ] - > driver - > id = =
client - > driver - > id ) {
usbvision - > i2c_clients [ i ] = client ;
break ;
}
}
if ( ( usbvision - > have_tuner ) & & ( usbvision - > tuner_type ! = - 1 ) ) {
tun_addr . mode_mask = T_ANALOG_TV ;
tun_addr . type = usbvision - > tuner_type ;
tun_addr . addr = ADDR_UNSET ;
client - > driver - > command ( client , TUNER_SET_TYPE_ADDR , & tun_addr ) ;
call_i2c_clients ( usbvision , VIDIOC_S_INPUT , & usbvision - > input . index ) ;
}
// FIXME : need to add a call VIDIOC_S_CTRL for each control
/* call_i2c_clients(usbvision, DECODER_SET_PICTURE, &usbvision->vpic); */
stdId = usbvision - > input . std ;
call_i2c_clients ( usbvision , VIDIOC_S_STD , & stdId ) ;
PDEBUG ( DBG_I2C , " usbvision[%d] attaches %s " , usbvision - > nr , client - > name ) ;
return 0 ;
}
static int detach_inform ( struct i2c_client * client )
{
struct usb_usbvision * usbvision ;
int i ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
usbvision = ( struct usb_usbvision * ) client - > adapter - > data ;
# else
usbvision = ( struct usb_usbvision * ) i2c_get_adapdata ( client - > adapter ) ;
# endif
PDEBUG ( DBG_I2C , " usbvision[%d] detaches %s " , usbvision - > nr , client - > name ) ;
for ( i = 0 ; i < USBVISION_I2C_CLIENTS_MAX ; i + + ) {
if ( NULL ! = usbvision - > i2c_clients [ i ] & &
usbvision - > i2c_clients [ i ] - > driver - > id = =
client - > driver - > id ) {
usbvision - > i2c_clients [ i ] = NULL ;
break ;
}
}
return 0 ;
}
static int
usbvision_i2c_read_max4 ( struct usb_usbvision * usbvision , unsigned char addr ,
char * buf , short len )
{
int rc , retries ;
for ( retries = 5 ; ; ) {
rc = usbvision_write_reg ( usbvision , USBVISION_SER_ADRS , addr ) ;
if ( rc < 0 )
return rc ;
/* Initiate byte read cycle */
/* USBVISION_SER_CONT <- d0-d2 n. of bytes to r/w */
/* d3 0=Wr 1=Rd */
rc = usbvision_write_reg ( usbvision , USBVISION_SER_CONT ,
( len & 0x07 ) | 0x18 ) ;
if ( rc < 0 )
return rc ;
/* Test for Busy and ACK */
do {
/* USBVISION_SER_CONT -> d4 == 0 busy */
rc = usbvision_read_reg ( usbvision , USBVISION_SER_CONT ) ;
} while ( rc > 0 & & ( ( rc & 0x10 ) ! = 0 ) ) ; /* Retry while busy */
if ( rc < 0 )
return rc ;
/* USBVISION_SER_CONT -> d5 == 1 Not ack */
if ( ( rc & 0x20 ) = = 0 ) /* Ack? */
break ;
/* I2C abort */
rc = usbvision_write_reg ( usbvision , USBVISION_SER_CONT , 0x00 ) ;
if ( rc < 0 )
return rc ;
if ( - - retries < 0 )
return - 1 ;
}
switch ( len ) {
case 4 :
buf [ 3 ] = usbvision_read_reg ( usbvision , USBVISION_SER_DAT4 ) ;
case 3 :
buf [ 2 ] = usbvision_read_reg ( usbvision , USBVISION_SER_DAT3 ) ;
case 2 :
buf [ 1 ] = usbvision_read_reg ( usbvision , USBVISION_SER_DAT2 ) ;
case 1 :
buf [ 0 ] = usbvision_read_reg ( usbvision , USBVISION_SER_DAT1 ) ;
break ;
default :
printk ( KERN_ERR
" usbvision_i2c_read_max4: buffer length > 4 \n " ) ;
}
if ( debug & DBG_I2C ) {
int idx ;
for ( idx = 0 ; idx < len ; idx + + ) {
PDEBUG ( DBG_I2C , " read %x from address %x " , ( unsigned char ) buf [ idx ] , addr ) ;
}
}
return len ;
}
static int usbvision_i2c_write_max4 ( struct usb_usbvision * usbvision ,
unsigned char addr , const char * buf ,
short len )
{
int rc , retries ;
int i ;
unsigned char value [ 6 ] ;
unsigned char ser_cont ;
ser_cont = ( len & 0x07 ) | 0x10 ;
value [ 0 ] = addr ;
value [ 1 ] = ser_cont ;
for ( i = 0 ; i < len ; i + + )
value [ i + 2 ] = buf [ i ] ;
for ( retries = 5 ; ; ) {
rc = usb_control_msg ( usbvision - > dev ,
usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE ,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_ENDPOINT , 0 ,
( __u16 ) USBVISION_SER_ADRS , value ,
len + 2 , HZ ) ;
if ( rc < 0 )
return rc ;
rc = usbvision_write_reg ( usbvision , USBVISION_SER_CONT ,
( len & 0x07 ) | 0x10 ) ;
if ( rc < 0 )
return rc ;
/* Test for Busy and ACK */
do {
rc = usbvision_read_reg ( usbvision , USBVISION_SER_CONT ) ;
} while ( rc > 0 & & ( ( rc & 0x10 ) ! = 0 ) ) ; /* Retry while busy */
if ( rc < 0 )
return rc ;
if ( ( rc & 0x20 ) = = 0 ) /* Ack? */
break ;
/* I2C abort */
usbvision_write_reg ( usbvision , USBVISION_SER_CONT , 0x00 ) ;
if ( - - retries < 0 )
return - 1 ;
}
if ( debug & DBG_I2C ) {
int idx ;
for ( idx = 0 ; idx < len ; idx + + ) {
PDEBUG ( DBG_I2C , " wrote %x at address %x " , ( unsigned char ) buf [ idx ] , addr ) ;
}
}
return len ;
}
static int usbvision_i2c_write ( void * data , unsigned char addr , char * buf ,
short len )
{
char * bufPtr = buf ;
int retval ;
int wrcount = 0 ;
int count ;
int maxLen = 4 ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) data ;
while ( len > 0 ) {
count = ( len > maxLen ) ? maxLen : len ;
retval = usbvision_i2c_write_max4 ( usbvision , addr , bufPtr , count ) ;
if ( retval > 0 ) {
len - = count ;
bufPtr + = count ;
wrcount + = count ;
} else
return ( retval < 0 ) ? retval : - EFAULT ;
}
return wrcount ;
}
static int usbvision_i2c_read ( void * data , unsigned char addr , char * buf ,
short len )
{
char temp [ 4 ] ;
int retval , i ;
int rdcount = 0 ;
int count ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) data ;
while ( len > 0 ) {
count = ( len > 3 ) ? 4 : len ;
retval = usbvision_i2c_read_max4 ( usbvision , addr , temp , count ) ;
if ( retval > 0 ) {
for ( i = 0 ; i < len ; i + + )
buf [ rdcount + i ] = temp [ i ] ;
len - = count ;
rdcount + = count ;
} else
return ( retval < 0 ) ? retval : - EFAULT ;
}
return rdcount ;
}
static struct i2c_algo_usb_data i2c_algo_template = {
. data = NULL ,
. inb = usbvision_i2c_read ,
. outb = usbvision_i2c_write ,
. udelay = 10 ,
. mdelay = 10 ,
. timeout = 100 ,
} ;
static struct i2c_adapter i2c_adap_template = {
. owner = THIS_MODULE ,
. name = " usbvision " ,
. id = I2C_HW_B_BT848 , /* FIXME */
. algo = NULL ,
. algo_data = NULL ,
. client_register = attach_inform ,
. client_unregister = detach_inform ,
# if defined (I2C_ADAP_CLASS_TV_ANALOG)
. class = I2C_ADAP_CLASS_TV_ANALOG ,
# elif defined (I2C_CLASS_TV_ANALOG)
. class = I2C_CLASS_TV_ANALOG ,
# endif
} ;
static struct i2c_client i2c_client_template = {
. name = " usbvision internal " ,
. flags = 0 ,
. addr = 0 ,
. adapter = NULL ,
. driver = NULL ,
} ;
static int usbvision_init_i2c ( struct usb_usbvision * usbvision )
{
memcpy ( & usbvision - > i2c_adap , & i2c_adap_template ,
sizeof ( struct i2c_adapter ) ) ;
memcpy ( & usbvision - > i2c_algo , & i2c_algo_template ,
sizeof ( struct i2c_algo_usb_data ) ) ;
memcpy ( & usbvision - > i2c_client , & i2c_client_template ,
sizeof ( struct i2c_client ) ) ;
sprintf ( usbvision - > i2c_adap . name + strlen ( usbvision - > i2c_adap . name ) ,
" #%d " , usbvision - > vdev - > minor & 0x1f ) ;
PDEBUG ( DBG_I2C , " Adaptername: %s " , usbvision - > i2c_adap . name ) ;
i2c_set_adapdata ( & usbvision - > i2c_adap , usbvision ) ;
i2c_set_clientdata ( & usbvision - > i2c_client , usbvision ) ;
i2c_set_algo_usb_data ( & usbvision - > i2c_algo , usbvision ) ;
usbvision - > i2c_adap . algo_data = & usbvision - > i2c_algo ;
usbvision - > i2c_client . adapter = & usbvision - > i2c_adap ;
if ( usbvision_write_reg ( usbvision , USBVISION_SER_MODE , USBVISION_IIC_LRNACK ) < 0 ) {
printk ( KERN_ERR " usbvision_init_i2c: can't wirte reg \n " ) ;
return - EBUSY ;
}
# ifdef CONFIG_KMOD
/* Request the load of the i2c modules we need */
if ( autoload ) {
switch ( usbvision_device_data [ usbvision - > DevModel ] . Codec ) {
case CODEC_SAA7113 :
request_module ( " saa7115 " ) ;
break ;
case CODEC_SAA7111 :
request_module ( " saa7115 " ) ;
break ;
}
if ( usbvision_device_data [ usbvision - > DevModel ] . Tuner = = 1 ) {
request_module ( " tuner " ) ;
}
}
# endif
usbvision - > i2c_ok = usbvision_i2c_usb_add_bus ( & usbvision - > i2c_adap ) ;
return usbvision - > i2c_ok ;
}
/****************************/
/* usbvision utility functions */
/****************************/
static int usbvision_power_off ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
PDEBUG ( DBG_FUNC , " " ) ;
errCode = usbvision_write_reg ( usbvision , USBVISION_PWR_REG , USBVISION_SSPND_EN ) ;
if ( errCode = = 1 ) {
usbvision - > power = 0 ;
}
PDEBUG ( DBG_FUNC , " %s: errCode %d " , ( errCode ! = 1 ) ? " ERROR " : " power is off " , errCode ) ;
return errCode ;
}
// to call usbvision_power_off from task queue
static void call_usbvision_power_off ( void * _usbvision )
{
struct usb_usbvision * usbvision = _usbvision ;
PDEBUG ( DBG_FUNC , " " ) ;
down_interruptible ( & usbvision - > lock ) ;
if ( usbvision - > user = = 0 ) {
usbvision_i2c_usb_del_bus ( & usbvision - > i2c_adap ) ;
usbvision_power_off ( usbvision ) ;
usbvision - > initialized = 0 ;
}
up ( & usbvision - > lock ) ;
}
/*
* usbvision_set_video_format ( )
*
*/
static int usbvision_set_video_format ( struct usb_usbvision * usbvision , int format )
{
static const char proc [ ] = " usbvision_set_video_format " ;
int rc ;
unsigned char value [ 2 ] ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return 0 ;
PDEBUG ( DBG_FUNC , " isocMode %#02x " , format ) ;
if ( ( format ! = ISOC_MODE_YUV422 )
& & ( format ! = ISOC_MODE_YUV420 )
& & ( format ! = ISOC_MODE_COMPRESS ) ) {
printk ( KERN_ERR " usbvision: unknown video format %02x, using default YUV420 " ,
format ) ;
format = ISOC_MODE_YUV420 ;
}
value [ 0 ] = 0x0A ; //TODO: See the effect of the filter
value [ 1 ] = format ;
rc = usb_control_msg ( usbvision - > dev , usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE ,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_ENDPOINT , 0 ,
( __u16 ) USBVISION_FILT_CONT , value , 2 , HZ ) ;
if ( rc < 0 ) {
printk ( KERN_ERR " %s: ERROR=%d. USBVISION stopped - "
" reconnect or reload driver. \n " , proc , rc ) ;
}
usbvision - > isocMode = format ;
return rc ;
}
/*
* usbvision_set_output ( )
*
*/
static int usbvision_set_output ( struct usb_usbvision * usbvision , int width ,
int height )
{
int errCode = 0 ;
int UsbWidth , UsbHeight ;
unsigned int frameRate = 0 , frameDrop = 0 ;
unsigned char value [ 4 ] ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) ) {
return 0 ;
}
if ( width > MAX_USB_WIDTH ) {
UsbWidth = width / 2 ;
usbvision - > stretch_width = 2 ;
}
else {
UsbWidth = width ;
usbvision - > stretch_width = 1 ;
}
if ( height > MAX_USB_HEIGHT ) {
UsbHeight = height / 2 ;
usbvision - > stretch_height = 2 ;
}
else {
UsbHeight = height ;
usbvision - > stretch_height = 1 ;
}
RESTRICT_TO_RANGE ( UsbWidth , MIN_FRAME_WIDTH , MAX_USB_WIDTH ) ;
UsbWidth & = ~ ( MIN_FRAME_WIDTH - 1 ) ;
RESTRICT_TO_RANGE ( UsbHeight , MIN_FRAME_HEIGHT , MAX_USB_HEIGHT ) ;
UsbHeight & = ~ ( 1 ) ;
PDEBUG ( DBG_FUNC , " usb %dx%d; screen %dx%d; stretch %dx%d " ,
UsbWidth , UsbHeight , width , height ,
usbvision - > stretch_width , usbvision - > stretch_height ) ;
/* I'll not rewrite the same values */
if ( ( UsbWidth ! = usbvision - > curwidth ) | | ( UsbHeight ! = usbvision - > curheight ) ) {
value [ 0 ] = UsbWidth & 0xff ; //LSB
value [ 1 ] = ( UsbWidth > > 8 ) & 0x03 ; //MSB
value [ 2 ] = UsbHeight & 0xff ; //LSB
value [ 3 ] = ( UsbHeight > > 8 ) & 0x03 ; //MSB
errCode = usb_control_msg ( usbvision - > dev , usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT ,
0 , ( __u16 ) USBVISION_LXSIZE_O , value , 4 , HZ ) ;
if ( errCode < 0 ) {
err ( " %s failed: error %d " , __FUNCTION__ , errCode ) ;
return errCode ;
}
usbvision - > curwidth = usbvision - > stretch_width * UsbWidth ;
usbvision - > curheight = usbvision - > stretch_height * UsbHeight ;
}
if ( usbvision - > isocMode = = ISOC_MODE_YUV422 ) {
frameRate = ( usbvision - > isocPacketSize * 1000 ) / ( UsbWidth * UsbHeight * 2 ) ;
}
else if ( usbvision - > isocMode = = ISOC_MODE_YUV420 ) {
frameRate = ( usbvision - > isocPacketSize * 1000 ) / ( ( UsbWidth * UsbHeight * 12 ) / 8 ) ;
}
else {
frameRate = FRAMERATE_MAX ;
}
if ( usbvision - > input . std & V4L2_STD_625_50 ) {
frameDrop = frameRate * 32 / 25 - 1 ;
}
else if ( usbvision - > input . std & V4L2_STD_525_60 ) {
frameDrop = frameRate * 32 / 30 - 1 ;
}
RESTRICT_TO_RANGE ( frameDrop , FRAMERATE_MIN , FRAMERATE_MAX ) ;
PDEBUG ( DBG_FUNC , " frameRate %d fps, frameDrop %d " , frameRate , frameDrop ) ;
frameDrop = FRAMERATE_MAX ; // We can allow the maximum here, because dropping is controlled
/* frameDrop = 7; => framePhase = 1, 5, 9, 13, 17, 21, 25, 0, 4, 8, ...
= > frameSkip = 4 ;
= > frameRate = ( 7 + 1 ) * 25 / 32 = 200 / 32 = 6.25 ;
frameDrop = 9 ; = > framePhase = 1 , 5 , 8 , 11 , 14 , 17 , 21 , 24 , 27 , 1 , 4 , 8 , . . .
= > frameSkip = 4 , 3 , 3 , 3 , 3 , 4 , 3 , 3 , 3 , 3 , 4 , . . .
= > frameRate = ( 9 + 1 ) * 25 / 32 = 250 / 32 = 7.8125 ;
*/
errCode = usbvision_write_reg ( usbvision , USBVISION_FRM_RATE , frameDrop ) ;
return errCode ;
}
/*
* usbvision_set_compress_params ( )
*
*/
static int usbvision_set_compress_params ( struct usb_usbvision * usbvision )
{
static const char proc [ ] = " usbvision_set_compresion_params: " ;
int rc ;
unsigned char value [ 6 ] ;
value [ 0 ] = 0x0F ; // Intra-Compression cycle
value [ 1 ] = 0x01 ; // Reg.45 one line per strip
value [ 2 ] = 0x00 ; // Reg.46 Force intra mode on all new frames
value [ 3 ] = 0x00 ; // Reg.47 FORCE_UP <- 0 normal operation (not force)
value [ 4 ] = 0xA2 ; // Reg.48 BUF_THR I'm not sure if this does something in not compressed mode.
value [ 5 ] = 0x00 ; // Reg.49 DVI_YUV This has nothing to do with compression
//catched values for NT1004
// value[0] = 0xFF; // Never apply intra mode automatically
// value[1] = 0xF1; // Use full frame height for virtual strip width; One line per strip
// value[2] = 0x01; // Force intra mode on all new frames
// value[3] = 0x00; // Strip size 400 Bytes; do not force up
// value[4] = 0xA2; //
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return 0 ;
rc = usb_control_msg ( usbvision - > dev , usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE ,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_ENDPOINT , 0 ,
( __u16 ) USBVISION_INTRA_CYC , value , 5 , HZ ) ;
if ( rc < 0 ) {
printk ( KERN_ERR " %sERROR=%d. USBVISION stopped - "
" reconnect or reload driver. \n " , proc , rc ) ;
return rc ;
}
if ( usbvision - > bridgeType = = BRIDGE_NT1004 ) {
value [ 0 ] = 20 ; // PCM Threshold 1
value [ 1 ] = 12 ; // PCM Threshold 2
value [ 2 ] = 255 ; // Distorsion Threshold inter
value [ 3 ] = 255 ; // Distorsion Threshold intra
value [ 4 ] = 43 ; // Max Distorsion inter
value [ 5 ] = 43 ; // Max Distorsion intra
}
else {
value [ 0 ] = 20 ; // PCM Threshold 1
value [ 1 ] = 12 ; // PCM Threshold 2
value [ 2 ] = 255 ; // Distorsion Threshold d7-d0
value [ 3 ] = 0 ; // Distorsion Threshold d11-d8
value [ 4 ] = 43 ; // Max Distorsion d7-d0
value [ 5 ] = 0 ; // Max Distorsion d8
}
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return 0 ;
rc = usb_control_msg ( usbvision - > dev , usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE ,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_ENDPOINT , 0 ,
( __u16 ) USBVISION_PCM_THR1 , value , 6 , HZ ) ;
if ( rc < 0 ) {
printk ( KERN_ERR " %sERROR=%d. USBVISION stopped - "
" reconnect or reload driver. \n " , proc , rc ) ;
return rc ;
}
return rc ;
}
/*
* usbvision_set_input ( )
*
* Set the input ( saa711x , . . . ) size x y and other misc input params
* I ' ve no idea if this parameters are right
*
*/
static int usbvision_set_input ( struct usb_usbvision * usbvision )
{
static const char proc [ ] = " usbvision_set_input: " ;
int rc ;
unsigned char value [ 8 ] ;
unsigned char dvi_yuv_value ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return 0 ;
/* Set input format expected from decoder*/
if ( usbvision_device_data [ usbvision - > DevModel ] . Vin_Reg1 > = 0 ) {
value [ 0 ] = usbvision_device_data [ usbvision - > DevModel ] . Vin_Reg1 & 0xff ;
} else if ( usbvision_device_data [ usbvision - > DevModel ] . Codec = = CODEC_SAA7113 ) {
/* SAA7113 uses 8 bit output */
value [ 0 ] = USBVISION_8_422_SYNC ;
} else {
/* I'm sure only about d2-d0 [010] 16 bit 4:2:2 usin sync pulses
* as that is how saa7111 is configured */
value [ 0 ] = USBVISION_16_422_SYNC ;
/* | USBVISION_VSNC_POL | USBVISION_VCLK_POL);*/
}
rc = usbvision_write_reg ( usbvision , USBVISION_VIN_REG1 , value [ 0 ] ) ;
if ( rc < 0 ) {
printk ( KERN_ERR " %sERROR=%d. USBVISION stopped - "
" reconnect or reload driver. \n " , proc , rc ) ;
return rc ;
}
if ( usbvision - > input . std & V4L2_STD_PAL ) {
value [ 0 ] = 0xC0 ;
value [ 1 ] = 0x02 ; //0x02C0 -> 704 Input video line length
value [ 2 ] = 0x20 ;
value [ 3 ] = 0x01 ; //0x0120 -> 288 Input video n. of lines
value [ 4 ] = 0x60 ;
value [ 5 ] = 0x00 ; //0x0060 -> 96 Input video h offset
value [ 6 ] = 0x16 ;
value [ 7 ] = 0x00 ; //0x0016 -> 22 Input video v offset
} else if ( usbvision - > input . std & V4L2_STD_SECAM ) {
value [ 0 ] = 0xC0 ;
value [ 1 ] = 0x02 ; //0x02C0 -> 704 Input video line length
value [ 2 ] = 0x20 ;
value [ 3 ] = 0x01 ; //0x0120 -> 288 Input video n. of lines
value [ 4 ] = 0x01 ;
value [ 5 ] = 0x00 ; //0x0001 -> 01 Input video h offset
value [ 6 ] = 0x01 ;
value [ 7 ] = 0x00 ; //0x0001 -> 01 Input video v offset
} else { /* V4L2_STD_NTSC */
value [ 0 ] = 0xD0 ;
value [ 1 ] = 0x02 ; //0x02D0 -> 720 Input video line length
value [ 2 ] = 0xF0 ;
value [ 3 ] = 0x00 ; //0x00F0 -> 240 Input video number of lines
value [ 4 ] = 0x50 ;
value [ 5 ] = 0x00 ; //0x0050 -> 80 Input video h offset
value [ 6 ] = 0x10 ;
value [ 7 ] = 0x00 ; //0x0010 -> 16 Input video v offset
}
if ( usbvision_device_data [ usbvision - > DevModel ] . X_Offset > = 0 ) {
value [ 4 ] = usbvision_device_data [ usbvision - > DevModel ] . X_Offset & 0xff ;
value [ 5 ] = ( usbvision_device_data [ usbvision - > DevModel ] . X_Offset & 0x0300 ) > > 8 ;
}
if ( usbvision_device_data [ usbvision - > DevModel ] . Y_Offset > = 0 ) {
value [ 6 ] = usbvision_device_data [ usbvision - > DevModel ] . Y_Offset & 0xff ;
value [ 7 ] = ( usbvision_device_data [ usbvision - > DevModel ] . Y_Offset & 0x0300 ) > > 8 ;
}
rc = usb_control_msg ( usbvision - > dev , usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE , /* USBVISION specific code */
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT , 0 ,
( __u16 ) USBVISION_LXSIZE_I , value , 8 , HZ ) ;
if ( rc < 0 ) {
printk ( KERN_ERR " %sERROR=%d. USBVISION stopped - "
" reconnect or reload driver. \n " , proc , rc ) ;
return rc ;
}
dvi_yuv_value = 0x00 ; /* U comes after V, Ya comes after U/V, Yb comes after Yb */
if ( usbvision_device_data [ usbvision - > DevModel ] . Dvi_yuv > = 0 ) {
dvi_yuv_value = usbvision_device_data [ usbvision - > DevModel ] . Dvi_yuv & 0xff ;
}
else if ( usbvision_device_data [ usbvision - > DevModel ] . Codec = = CODEC_SAA7113 ) {
/* This changes as the fine sync control changes. Further investigation necessary */
dvi_yuv_value = 0x06 ;
}
return ( usbvision_write_reg ( usbvision , USBVISION_DVI_YUV , dvi_yuv_value ) ) ;
}
/*
* usbvision_set_dram_settings ( )
*
* Set the buffer address needed by the usbvision dram to operate
* This values has been taken with usbsnoop .
*
*/
static int usbvision_set_dram_settings ( struct usb_usbvision * usbvision )
{
int rc ;
unsigned char value [ 8 ] ;
if ( usbvision - > isocMode = = ISOC_MODE_COMPRESS ) {
value [ 0 ] = 0x42 ;
value [ 1 ] = 0x71 ;
value [ 2 ] = 0xff ;
value [ 3 ] = 0x00 ;
value [ 4 ] = 0x98 ;
value [ 5 ] = 0xe0 ;
value [ 6 ] = 0x71 ;
value [ 7 ] = 0xff ;
// UR: 0x0E200-0x3FFFF = 204288 Words (1 Word = 2 Byte)
// FDL: 0x00000-0x0E099 = 57498 Words
// VDW: 0x0E3FF-0x3FFFF
}
else {
value [ 0 ] = 0x42 ;
value [ 1 ] = 0x00 ;
value [ 2 ] = 0xff ;
value [ 3 ] = 0x00 ;
value [ 4 ] = 0x00 ;
value [ 5 ] = 0x00 ;
value [ 6 ] = 0x00 ;
value [ 7 ] = 0xff ;
}
/* These are the values of the address of the video buffer,
* they have to be loaded into the USBVISION_DRM_PRM1 - 8
*
* Start address of video output buffer for read : drm_prm1 - 2 - > 0x00000
* End address of video output buffer for read : drm_prm1 - 3 - > 0x1ffff
* Start address of video frame delay buffer : drm_prm1 - 4 - > 0x20000
* Only used in compressed mode
* End address of video frame delay buffer : drm_prm1 - 5 - 6 - > 0x3ffff
* Only used in compressed mode
* Start address of video output buffer for write : drm_prm1 - 7 - > 0x00000
* End address of video output buffer for write : drm_prm1 - 8 - > 0x1ffff
*/
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return 0 ;
rc = usb_control_msg ( usbvision - > dev , usb_sndctrlpipe ( usbvision - > dev , 1 ) ,
USBVISION_OP_CODE , /* USBVISION specific code */
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_ENDPOINT , 0 ,
( __u16 ) USBVISION_DRM_PRM1 , value , 8 , HZ ) ;
if ( rc < 0 ) {
err ( " %sERROR=%d " , __FUNCTION__ , rc ) ;
return rc ;
}
/* Restart the video buffer logic */
if ( ( rc = usbvision_write_reg ( usbvision , USBVISION_DRM_CONT , USBVISION_RES_UR |
USBVISION_RES_FDL | USBVISION_RES_VDW ) ) < 0 )
return rc ;
rc = usbvision_write_reg ( usbvision , USBVISION_DRM_CONT , 0x00 ) ;
return rc ;
}
/*
* ( )
*
* Power on the device , enables suspend - resume logic
* & reset the isoc End - Point
*
*/
static int usbvision_power_on ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
PDEBUG ( DBG_FUNC , " " ) ;
usbvision_write_reg ( usbvision , USBVISION_PWR_REG , USBVISION_SSPND_EN ) ;
usbvision_write_reg ( usbvision , USBVISION_PWR_REG ,
USBVISION_SSPND_EN | USBVISION_RES2 ) ;
usbvision_write_reg ( usbvision , USBVISION_PWR_REG ,
USBVISION_SSPND_EN | USBVISION_PWR_VID ) ;
errCode = usbvision_write_reg ( usbvision , USBVISION_PWR_REG ,
USBVISION_SSPND_EN | USBVISION_PWR_VID | USBVISION_RES2 ) ;
if ( errCode = = 1 ) {
usbvision - > power = 1 ;
}
PDEBUG ( DBG_FUNC , " %s: errCode %d " , ( errCode < 0 ) ? " ERROR " : " power is on " , errCode ) ;
return errCode ;
}
static void usbvision_powerOffTimer ( unsigned long data )
{
struct usb_usbvision * usbvision = ( void * ) data ;
PDEBUG ( DBG_FUNC , " " ) ;
del_timer ( & usbvision - > powerOffTimer ) ;
INIT_WORK ( & usbvision - > powerOffWork , call_usbvision_power_off , usbvision ) ;
( void ) schedule_work ( & usbvision - > powerOffWork ) ;
}
/*
* usbvision_begin_streaming ( )
* Sure you have to put bit 7 to 0 , if not incoming frames are droped , but no
* idea about the rest
*/
static int usbvision_begin_streaming ( struct usb_usbvision * usbvision )
{
int errCode = 0 ;
if ( usbvision - > isocMode = = ISOC_MODE_COMPRESS ) {
usbvision_init_compression ( usbvision ) ;
}
errCode = usbvision_write_reg ( usbvision , USBVISION_VIN_REG2 , USBVISION_NOHVALID |
usbvision - > Vin_Reg2_Preset ) ;
return errCode ;
}
/*
* usbvision_restart_isoc ( )
* Not sure yet if touching here PWR_REG make loose the config
*/
static int usbvision_restart_isoc ( struct usb_usbvision * usbvision )
{
int ret ;
if (
( ret =
usbvision_write_reg ( usbvision , USBVISION_PWR_REG ,
USBVISION_SSPND_EN | USBVISION_PWR_VID ) ) < 0 )
return ret ;
if (
( ret =
usbvision_write_reg ( usbvision , USBVISION_PWR_REG ,
USBVISION_SSPND_EN | USBVISION_PWR_VID |
USBVISION_RES2 ) ) < 0 )
return ret ;
if (
( ret =
usbvision_write_reg ( usbvision , USBVISION_VIN_REG2 ,
USBVISION_KEEP_BLANK | USBVISION_NOHVALID |
usbvision - > Vin_Reg2_Preset ) ) < 0 ) return ret ;
/* TODO: schedule timeout */
while ( ( usbvision_read_reg ( usbvision , USBVISION_STATUS_REG ) & & 0x01 ) ! = 1 ) ;
return 0 ;
}
static int usbvision_audio_on ( struct usb_usbvision * usbvision )
{
if ( usbvision_write_reg ( usbvision , USBVISION_IOPIN_REG , usbvision - > AudioChannel ) < 0 ) {
printk ( KERN_ERR " usbvision_audio_on: can't wirte reg \n " ) ;
return - 1 ;
}
DEBUG ( printk ( KERN_DEBUG " usbvision_audio_on: channel %d \n " , usbvision - > AudioChannel ) ) ;
usbvision - > AudioMute = 0 ;
return 0 ;
}
static int usbvision_audio_mute ( struct usb_usbvision * usbvision )
{
if ( usbvision_write_reg ( usbvision , USBVISION_IOPIN_REG , 0x03 ) < 0 ) {
printk ( KERN_ERR " usbvision_audio_mute: can't wirte reg \n " ) ;
return - 1 ;
}
DEBUG ( printk ( KERN_DEBUG " usbvision_audio_mute: audio mute \n " ) ) ;
usbvision - > AudioMute = 1 ;
return 0 ;
}
static int usbvision_audio_off ( struct usb_usbvision * usbvision )
{
if ( usbvision_write_reg ( usbvision , USBVISION_IOPIN_REG , USBVISION_AUDIO_MUTE ) < 0 ) {
printk ( KERN_ERR " usbvision_audio_off: can't wirte reg \n " ) ;
return - 1 ;
}
DEBUG ( printk ( KERN_DEBUG " usbvision_audio_off: audio off \n " ) ) ;
usbvision - > AudioMute = 0 ;
usbvision - > AudioChannel = USBVISION_AUDIO_MUTE ;
return 0 ;
}
static int usbvision_set_audio ( struct usb_usbvision * usbvision , int AudioChannel )
{
if ( ! usbvision - > AudioMute ) {
if ( usbvision_write_reg ( usbvision , USBVISION_IOPIN_REG , AudioChannel ) < 0 ) {
printk ( KERN_ERR " usbvision_set_audio: can't write iopin register for audio switching \n " ) ;
return - 1 ;
}
}
DEBUG ( printk ( KERN_DEBUG " usbvision_set_audio: channel %d \n " , AudioChannel ) ) ;
usbvision - > AudioChannel = AudioChannel ;
return 0 ;
}
static int usbvision_setup ( struct usb_usbvision * usbvision )
{
usbvision_set_video_format ( usbvision , isocMode ) ;
usbvision_set_dram_settings ( usbvision ) ;
usbvision_set_compress_params ( usbvision ) ;
usbvision_set_input ( usbvision ) ;
usbvision_set_output ( usbvision , MAX_USB_WIDTH , MAX_USB_HEIGHT ) ;
usbvision_restart_isoc ( usbvision ) ;
/* cosas del PCM */
return USBVISION_IS_OPERATIONAL ( usbvision ) ;
}
/*
* usbvision_init_isoc ( )
*
*/
static int usbvision_init_isoc ( struct usb_usbvision * usbvision )
{
struct usb_device * dev = usbvision - > dev ;
int bufIdx , errCode , regValue ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return - EFAULT ;
usbvision - > curFrameNum = - 1 ;
scratch_reset ( usbvision ) ;
/* Alternate interface 1 is is the biggest frame size */
errCode = usb_set_interface ( dev , usbvision - > iface , usbvision - > ifaceAltActive ) ;
if ( errCode < 0 ) {
usbvision - > last_error = errCode ;
return - EBUSY ;
}
regValue = ( 16 - usbvision_read_reg ( usbvision , USBVISION_ALTER_REG ) ) & 0x0F ;
usbvision - > isocPacketSize = ( regValue = = 0 ) ? 0 : ( regValue * 64 ) - 1 ;
PDEBUG ( DBG_ISOC , " ISO Packet Length:%d " , usbvision - > isocPacketSize ) ;
usbvision - > usb_bandwidth = regValue > > 1 ;
PDEBUG ( DBG_ISOC , " USB Bandwidth Usage: %dMbit/Sec " , usbvision - > usb_bandwidth ) ;
/* We double buffer the Iso lists */
for ( bufIdx = 0 ; bufIdx < USBVISION_NUMSBUF ; bufIdx + + ) {
int j , k ;
struct urb * urb ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
urb = usb_alloc_urb ( USBVISION_URB_FRAMES ) ;
# else
urb = usb_alloc_urb ( USBVISION_URB_FRAMES , GFP_KERNEL ) ;
# endif
if ( urb = = NULL ) {
err ( " %s: usb_alloc_urb() failed " , __FUNCTION__ ) ;
return - ENOMEM ;
}
usbvision - > sbuf [ bufIdx ] . urb = urb ;
urb - > dev = dev ;
urb - > context = usbvision ;
urb - > pipe = usb_rcvisocpipe ( dev , usbvision - > video_endp ) ;
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
urb - > transfer_flags = USB_ISO_ASAP ;
# else
urb - > transfer_flags = URB_ISO_ASAP ;
urb - > interval = 1 ;
# endif
urb - > transfer_buffer = usbvision - > sbuf [ bufIdx ] . data ;
urb - > complete = usbvision_isocIrq ;
urb - > number_of_packets = USBVISION_URB_FRAMES ;
urb - > transfer_buffer_length =
usbvision - > isocPacketSize * USBVISION_URB_FRAMES ;
for ( j = k = 0 ; j < USBVISION_URB_FRAMES ; j + + ,
k + = usbvision - > isocPacketSize ) {
urb - > iso_frame_desc [ j ] . offset = k ;
urb - > iso_frame_desc [ j ] . length = usbvision - > isocPacketSize ;
}
}
/* Submit all URBs */
for ( bufIdx = 0 ; bufIdx < USBVISION_NUMSBUF ; bufIdx + + ) {
# if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,0)
errCode = usb_submit_urb ( usbvision - > sbuf [ bufIdx ] . urb ) ;
# else
errCode = usb_submit_urb ( usbvision - > sbuf [ bufIdx ] . urb , GFP_KERNEL ) ;
# endif
if ( errCode ) {
err ( " %s: usb_submit_urb(%d) failed: error %d " , __FUNCTION__ , bufIdx , errCode ) ;
}
}
usbvision - > streaming = 1 ;
PDEBUG ( DBG_ISOC , " %s: streaming=1 usbvision->video_endp=$%02x " , __FUNCTION__ , usbvision - > video_endp ) ;
return 0 ;
}
/*
* usbvision_stop_isoc ( )
*
* This procedure stops streaming and deallocates URBs . Then it
* activates zero - bandwidth alt . setting of the video interface .
*
*/
static void usbvision_stop_isoc ( struct usb_usbvision * usbvision )
{
int bufIdx , errCode , regValue ;
if ( ! usbvision - > streaming | | ( usbvision - > dev = = NULL ) )
return ;
/* Unschedule all of the iso td's */
for ( bufIdx = 0 ; bufIdx < USBVISION_NUMSBUF ; bufIdx + + ) {
usb_kill_urb ( usbvision - > sbuf [ bufIdx ] . urb ) ;
usb_free_urb ( usbvision - > sbuf [ bufIdx ] . urb ) ;
usbvision - > sbuf [ bufIdx ] . urb = NULL ;
}
PDEBUG ( DBG_ISOC , " %s: streaming=0 \n " , __FUNCTION__ ) ;
usbvision - > streaming = 0 ;
if ( ! usbvision - > remove_pending ) {
/* Set packet size to 0 */
errCode = usb_set_interface ( usbvision - > dev , usbvision - > iface ,
usbvision - > ifaceAltInactive ) ;
if ( errCode < 0 ) {
err ( " %s: usb_set_interface() failed: error %d " , __FUNCTION__ , errCode ) ;
usbvision - > last_error = errCode ;
}
regValue = ( 16 - usbvision_read_reg ( usbvision , USBVISION_ALTER_REG ) ) & 0x0F ;
usbvision - > isocPacketSize = ( regValue = = 0 ) ? 0 : ( regValue * 64 ) - 1 ;
PDEBUG ( DBG_ISOC , " ISO Packet Length:%d " , usbvision - > isocPacketSize ) ;
usbvision - > usb_bandwidth = regValue > > 1 ;
PDEBUG ( DBG_ISOC , " USB Bandwidth Usage: %dMbit/Sec " , usbvision - > usb_bandwidth ) ;
}
}
/*
* usbvision_new_frame ( )
*
*/
static int usbvision_new_frame ( struct usb_usbvision * usbvision , int framenum )
{
struct usbvision_frame * frame ;
int n ; //byhec , width, height;
/* If we're not grabbing a frame right now and the other frame is */
/* ready to be grabbed into, then use it instead */
if ( usbvision - > curFrameNum ! = - 1 )
return 0 ;
n = ( framenum - 1 + USBVISION_NUMFRAMES ) % USBVISION_NUMFRAMES ;
if ( usbvision - > frame [ n ] . grabstate = = FrameState_Ready )
framenum = n ;
frame = & usbvision - > frame [ framenum ] ;
frame - > grabstate = FrameState_Grabbing ;
frame - > scanstate = ScanState_Scanning ;
frame - > scanlength = 0 ; /* Accumulated in usbvision_parse_data() */
usbvision - > curFrameNum = framenum ;
/*
* Normally we would want to copy previous frame into the current one
* before we even start filling it with data ; this allows us to stop
* filling at any moment ; top portion of the frame will be new and
* bottom portion will stay as it was in previous frame . If we don ' t
* do that then missing chunks of video stream will result in flickering
* portions of old data whatever it was before .
*
* If we choose not to copy previous frame ( to , for example , save few
* bus cycles - the frame can be pretty large ! ) then we have an option
* to clear the frame before using . If we experience losses in this
* mode then missing picture will be black ( flickering ) .
*
* Finally , if user chooses not to clean the current frame before
* filling it with data then the old data will be visible if we fail
* to refill entire frame with new data .
*/
if ( ! ( flags & FLAGS_SEPARATE_FRAMES ) ) {
/* This copies previous frame into this one to mask losses */
memmove ( frame - > data , usbvision - > frame [ 1 - framenum ] . data ,
MAX_FRAME_SIZE ) ;
} else {
if ( flags & FLAGS_CLEAN_FRAMES ) {
/*This provides a "clean" frame but slows things down */
memset ( frame - > data , 0 , MAX_FRAME_SIZE ) ;
}
}
return 0 ;
}
static int usbvision_muxsel ( struct usb_usbvision * usbvision , int channel , int norm )
{
int mode [ 4 ] ;
int audio [ ] = { 1 , 0 , 0 , 0 } ;
struct v4l2_routing route ;
//channel 0 is TV with audiochannel 1 (tuner mono)
//channel 1 is Composite with audio channel 0 (line in)
//channel 2 is S-Video with audio channel 0 (line in)
//channel 3 is additional video inputs to the device with audio channel 0 (line in)
RESTRICT_TO_RANGE ( channel , 0 , usbvision - > video_inputs ) ;
/* set the new video norm */
if ( usbvision - > input . std ! = norm ) {
v4l2_std_id video_command = norm ;
route . input = SAA7115_COMPOSITE1 ;
call_i2c_clients ( usbvision , VIDIOC_INT_S_VIDEO_ROUTING , & route ) ;
call_i2c_clients ( usbvision , VIDIOC_S_STD , & video_command ) ;
usbvision - > input . std = norm ;
call_i2c_clients ( usbvision , VIDIOC_S_INPUT , & usbvision - > input . index ) ; //set norm in tuner
}
// set the new channel
// Regular USB TV Tuners -> channel: 0 = Television, 1 = Composite, 2 = S-Video
// Four video input devices -> channel: 0 = Chan White, 1 = Chan Green, 2 = Chan Yellow, 3 = Chan Red
switch ( usbvision_device_data [ usbvision - > DevModel ] . Codec ) {
case CODEC_SAA7113 :
if ( SwitchSVideoInput ) { // To handle problems with S-Video Input for some devices. Use SwitchSVideoInput parameter when loading the module.
mode [ 2 ] = 1 ;
}
else {
mode [ 2 ] = 7 ;
}
if ( usbvision_device_data [ usbvision - > DevModel ] . VideoChannels = = 4 ) {
mode [ 0 ] = 0 ; mode [ 1 ] = 2 ; mode [ 3 ] = 3 ; // Special for four input devices
}
else {
mode [ 0 ] = 0 ; mode [ 1 ] = 2 ; //modes for regular saa7113 devices
}
break ;
case CODEC_SAA7111 :
mode [ 0 ] = 0 ; mode [ 1 ] = 1 ; mode [ 2 ] = 7 ; //modes for saa7111
break ;
default :
mode [ 0 ] = 0 ; mode [ 1 ] = 1 ; mode [ 2 ] = 7 ; //default modes
}
route . input = mode [ channel ] ;
call_i2c_clients ( usbvision , VIDIOC_INT_S_VIDEO_ROUTING , & route ) ;
usbvision - > channel = channel ;
usbvision_set_audio ( usbvision , audio [ channel ] ) ;
return 0 ;
}
/*
* usbvision_open ( )
*
* This is part of Video 4 Linux API . The driver can be opened by one
* client only ( checks internal counter ' usbvision - > user ' ) . The procedure
* then allocates buffers needed for video processing .
*
*/
static int usbvision_v4l2_open ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
const int sb_size = USBVISION_URB_FRAMES * USBVISION_MAX_ISOC_PACKET_SIZE ;
int i , errCode = 0 ;
PDEBUG ( DBG_IO , " open " ) ;
if ( timer_pending ( & usbvision - > powerOffTimer ) ) {
del_timer ( & usbvision - > powerOffTimer ) ;
}
if ( usbvision - > user )
errCode = - EBUSY ;
else {
/* Clean pointers so we know if we allocated something */
for ( i = 0 ; i < USBVISION_NUMSBUF ; i + + )
usbvision - > sbuf [ i ] . data = NULL ;
/* Allocate memory for the frame buffers */
usbvision - > max_frame_size = MAX_FRAME_SIZE ;
usbvision - > fbuf_size = USBVISION_NUMFRAMES * usbvision - > max_frame_size ;
usbvision - > fbuf = usbvision_rvmalloc ( usbvision - > fbuf_size ) ;
usbvision - > scratch = vmalloc ( scratch_buf_size ) ;
scratch_reset ( usbvision ) ;
if ( ( usbvision - > fbuf = = NULL ) | | ( usbvision - > scratch = = NULL ) ) {
err ( " %s: unable to allocate %d bytes for fbuf and %d bytes for scratch " ,
__FUNCTION__ , usbvision - > fbuf_size , scratch_buf_size ) ;
errCode = - ENOMEM ;
}
else {
/* Allocate all buffers */
for ( i = 0 ; i < USBVISION_NUMFRAMES ; i + + ) {
usbvision - > frame [ i ] . grabstate = FrameState_Unused ;
usbvision - > frame [ i ] . data = usbvision - > fbuf +
i * MAX_FRAME_SIZE ;
/*
* Set default sizes in case IOCTL
* ( VIDIOCMCAPTURE )
* is not used ( using read ( ) instead ) .
*/
usbvision - > stretch_width = 1 ;
usbvision - > stretch_height = 1 ;
usbvision - > frame [ i ] . width = usbvision - > curwidth ;
usbvision - > frame [ i ] . height = usbvision - > curheight ;
usbvision - > frame [ i ] . bytes_read = 0 ;
}
if ( dga ) { //set default for DGA
usbvision - > overlay_frame . grabstate = FrameState_Unused ;
usbvision - > overlay_frame . scanstate = ScanState_Scanning ;
usbvision - > overlay_frame . data = NULL ;
usbvision - > overlay_frame . width = usbvision - > curwidth ;
usbvision - > overlay_frame . height = usbvision - > curheight ;
usbvision - > overlay_frame . bytes_read = 0 ;
}
for ( i = 0 ; i < USBVISION_NUMSBUF ; i + + ) {
usbvision - > sbuf [ i ] . data = kzalloc ( sb_size , GFP_KERNEL ) ;
if ( usbvision - > sbuf [ i ] . data = = NULL ) {
err ( " %s: unable to allocate %d bytes for sbuf " , __FUNCTION__ , sb_size ) ;
errCode = - ENOMEM ;
break ;
}
}
}
if ( ( ! errCode ) & & ( usbvision - > isocMode = = ISOC_MODE_COMPRESS ) ) {
int IFB_size = MAX_FRAME_WIDTH * MAX_FRAME_HEIGHT * 3 / 2 ;
usbvision - > IntraFrameBuffer = vmalloc ( IFB_size ) ;
if ( usbvision - > IntraFrameBuffer = = NULL ) {
err ( " %s: unable to allocate %d for compr. frame buffer " , __FUNCTION__ , IFB_size ) ;
errCode = - ENOMEM ;
}
}
if ( errCode ) {
/* Have to free all that memory */
if ( usbvision - > fbuf ! = NULL ) {
usbvision_rvfree ( usbvision - > fbuf , usbvision - > fbuf_size ) ;
usbvision - > fbuf = NULL ;
}
if ( usbvision - > scratch ! = NULL ) {
vfree ( usbvision - > scratch ) ;
usbvision - > scratch = NULL ;
}
for ( i = 0 ; i < USBVISION_NUMSBUF ; i + + ) {
if ( usbvision - > sbuf [ i ] . data ! = NULL ) {
kfree ( usbvision - > sbuf [ i ] . data ) ;
usbvision - > sbuf [ i ] . data = NULL ;
}
}
if ( usbvision - > IntraFrameBuffer ! = NULL ) {
vfree ( usbvision - > IntraFrameBuffer ) ;
usbvision - > IntraFrameBuffer = NULL ;
}
}
}
/* If so far no errors then we shall start the camera */
if ( ! errCode ) {
down ( & usbvision - > lock ) ;
if ( usbvision - > power = = 0 ) {
usbvision_power_on ( usbvision ) ;
usbvision_init_i2c ( usbvision ) ;
}
/* Send init sequence only once, it's large! */
if ( ! usbvision - > initialized ) {
int setup_ok = 0 ;
setup_ok = usbvision_setup ( usbvision ) ;
if ( setup_ok )
usbvision - > initialized = 1 ;
else
errCode = - EBUSY ;
}
if ( ! errCode ) {
usbvision_begin_streaming ( usbvision ) ;
errCode = usbvision_init_isoc ( usbvision ) ;
usbvision - > user + + ;
}
else {
if ( PowerOnAtOpen ) {
usbvision_i2c_usb_del_bus ( & usbvision - > i2c_adap ) ;
usbvision_power_off ( usbvision ) ;
usbvision - > initialized = 0 ;
}
}
up ( & usbvision - > lock ) ;
}
if ( errCode ) {
}
PDEBUG ( DBG_IO , " success " ) ;
return errCode ;
}
/*
* usbvision_v4l2_close ( )
*
* This is part of Video 4 Linux API . The procedure
* stops streaming and deallocates all buffers that were earlier
* allocated in usbvision_v4l2_open ( ) .
*
*/
static int usbvision_v4l2_close ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
int i ;
PDEBUG ( DBG_IO , " close " ) ;
down ( & usbvision - > lock ) ;
usbvision_audio_off ( usbvision ) ;
usbvision_restart_isoc ( usbvision ) ;
usbvision_stop_isoc ( usbvision ) ;
if ( usbvision - > IntraFrameBuffer ! = NULL ) {
vfree ( usbvision - > IntraFrameBuffer ) ;
usbvision - > IntraFrameBuffer = NULL ;
}
usbvision_rvfree ( usbvision - > fbuf , usbvision - > fbuf_size ) ;
vfree ( usbvision - > scratch ) ;
for ( i = 0 ; i < USBVISION_NUMSBUF ; i + + )
kfree ( usbvision - > sbuf [ i ] . data ) ;
usbvision - > user - - ;
if ( PowerOnAtOpen ) {
mod_timer ( & usbvision - > powerOffTimer , jiffies + USBVISION_POWEROFF_TIME ) ;
usbvision - > initialized = 0 ;
}
up ( & usbvision - > lock ) ;
if ( usbvision - > remove_pending ) {
info ( " %s: Final disconnect " , __FUNCTION__ ) ;
usbvision_release ( usbvision ) ;
}
PDEBUG ( DBG_IO , " success " ) ;
return 0 ;
}
/*
* usbvision_ioctl ( )
*
* This is part of Video 4 Linux API . The procedure handles ioctl ( ) calls .
*
*/
static int usbvision_v4l2_do_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return - EFAULT ;
switch ( cmd ) {
case UVIOCSREG :
{
struct usbvision_reg * usbvision_reg = arg ;
int errCode ;
errCode = usbvision_write_reg ( usbvision , usbvision_reg - > addr , usbvision_reg - > value ) ;
if ( errCode < 0 ) {
err ( " %s: UVIOCSREG failed: error %d " , __FUNCTION__ , errCode ) ;
}
else {
PDEBUG ( DBG_IOCTL , " UVIOCSREG addr=0x%02X, value=0x%02X " ,
usbvision_reg - > addr , usbvision_reg - > value ) ;
errCode = 0 ;
}
return errCode ;
}
case UVIOCGREG :
{
struct usbvision_reg * usbvision_reg = arg ;
int errCode ;
errCode = usbvision_read_reg ( usbvision , usbvision_reg - > addr ) ;
if ( errCode < 0 ) {
err ( " %s: UVIOCGREG failed: error %d " , __FUNCTION__ , errCode ) ;
}
else {
usbvision_reg - > value = ( unsigned char ) errCode ;
PDEBUG ( DBG_IOCTL , " UVIOCGREG addr=0x%02X, value=0x%02X " ,
usbvision_reg - > addr , usbvision_reg - > value ) ;
errCode = 0 ; // No error
}
return errCode ;
}
case VIDIOC_QUERYCAP :
{
struct v4l2_capability * vc = arg ;
* vc = usbvision - > vcap ;
PDEBUG ( DBG_IOCTL , " VIDIOC_QUERYCAP " ) ;
return 0 ;
}
case VIDIOC_ENUMINPUT :
{
struct v4l2_input * vi = arg ;
int chan ;
if ( ( vi - > index > = usbvision - > video_inputs ) | | ( vi - > index < 0 ) )
return - EINVAL ;
if ( usbvision - > have_tuner ) {
chan = vi - > index ;
}
else {
chan = vi - > index + 1 ; //skip Television string
}
switch ( chan ) {
case 0 :
if ( usbvision_device_data [ usbvision - > DevModel ] . VideoChannels = = 4 ) {
strcpy ( vi - > name , " White Video Input " ) ;
}
else {
strcpy ( vi - > name , " Television " ) ;
vi - > type = V4L2_INPUT_TYPE_TUNER ;
vi - > audioset = 1 ;
vi - > tuner = chan ;
vi - > std = V4L2_STD_PAL | V4L2_STD_NTSC | V4L2_STD_SECAM ;
}
break ;
case 1 :
vi - > type = V4L2_INPUT_TYPE_CAMERA ;
if ( usbvision_device_data [ usbvision - > DevModel ] . VideoChannels = = 4 ) {
strcpy ( vi - > name , " Green Video Input " ) ;
}
else {
strcpy ( vi - > name , " Composite Video Input " ) ;
}
vi - > std = V4L2_STD_PAL ;
break ;
case 2 :
vi - > type = V4L2_INPUT_TYPE_CAMERA ;
if ( usbvision_device_data [ usbvision - > DevModel ] . VideoChannels = = 4 ) {
strcpy ( vi - > name , " Yellow Video Input " ) ;
}
else {
strcpy ( vi - > name , " S-Video Input " ) ;
}
vi - > std = V4L2_STD_PAL ;
break ;
case 3 :
vi - > type = V4L2_INPUT_TYPE_CAMERA ;
strcpy ( vi - > name , " Red Video Input " ) ;
vi - > std = V4L2_STD_PAL ;
break ;
}
PDEBUG ( DBG_IOCTL , " VIDIOC_ENUMINPUT name=%s:%d tuners=%d type=%d norm=%x " , vi - > name , vi - > index , vi - > tuner , vi - > type , ( int ) vi - > std ) ;
return 0 ;
}
case VIDIOC_ENUMSTD :
{
struct v4l2_standard * vs = arg ;
switch ( vs - > index ) {
case 0 :
vs - > id = V4L2_STD_PAL ;
strcpy ( vs - > name , " PAL " ) ;
vs - > frameperiod . numerator = 1 ;
vs - > frameperiod . denominator = 25 ;
vs - > framelines = 625 ;
break ;
case 1 :
vs - > id = V4L2_STD_NTSC ;
strcpy ( vs - > name , " NTSC " ) ;
vs - > frameperiod . numerator = 1001 ;
vs - > frameperiod . denominator = 30000 ;
vs - > framelines = 525 ;
break ;
case 2 :
vs - > id = V4L2_STD_SECAM ;
strcpy ( vs - > name , " SECAM " ) ;
vs - > frameperiod . numerator = 1 ;
vs - > frameperiod . denominator = 25 ;
vs - > framelines = 625 ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
case VIDIOC_G_INPUT :
{
int * input = arg ;
* input = usbvision - > input . index ;
return 0 ;
}
case VIDIOC_S_INPUT :
{
int * input = arg ;
if ( ( * input > = usbvision - > video_inputs ) | | ( * input < 0 ) )
return - EINVAL ;
usbvision - > input . index = * input ;
down ( & usbvision - > lock ) ;
usbvision_muxsel ( usbvision , usbvision - > input . index , usbvision - > input . std ) ;
usbvision_set_input ( usbvision ) ;
usbvision_set_output ( usbvision , usbvision - > curwidth , usbvision - > curheight ) ;
up ( & usbvision - > lock ) ;
return 0 ;
}
case VIDIOC_G_STD :
{
v4l2_std_id * std = arg ;
* std = usbvision - > input . std ;
PDEBUG ( DBG_IOCTL , " VIDIOC_G_STD std_id=%x " , ( unsigned ) * std ) ;
return 0 ;
}
case VIDIOC_S_STD :
{
v4l2_std_id * std = arg ;
down ( & usbvision - > lock ) ;
usbvision_muxsel ( usbvision , usbvision - > input . index , * std ) ;
usbvision_set_input ( usbvision ) ;
usbvision_set_output ( usbvision , usbvision - > curwidth , usbvision - > curheight ) ;
up ( & usbvision - > lock ) ;
usbvision - > input . std = * std ;
PDEBUG ( DBG_IOCTL , " VIDIOC_S_STD std_id=%x " , ( unsigned ) * std ) ;
return 0 ;
}
case VIDIOC_G_TUNER :
{
struct v4l2_tuner * vt = arg ;
struct v4l2_tuner status ;
if ( ! usbvision - > have_tuner | | vt - > index ) // Only tuner 0
return - EINVAL ;
strcpy ( vt - > name , " Television " ) ;
vt - > type = V4L2_TUNER_ANALOG_TV ;
vt - > capability = V4L2_TUNER_CAP_NORM ;
vt - > rangelow = 0 ;
vt - > rangehigh = ~ 0 ;
vt - > audmode = V4L2_TUNER_MODE_MONO ;
vt - > rxsubchans = V4L2_TUNER_SUB_MONO ;
call_i2c_clients ( usbvision , VIDIOC_G_TUNER , & status ) ;
vt - > signal = status . signal ;
PDEBUG ( DBG_IOCTL , " VIDIOC_G_TUNER " ) ;
return 0 ;
}
case VIDIOC_S_TUNER :
{
struct v4l2_tuner * vt = arg ;
// Only no or one tuner for now
if ( ! usbvision - > have_tuner | | vt - > index )
return - EINVAL ;
// FIXME vt->audmode Radio mode (STEREO/MONO/...)
// vt->reserved Radio freq
// usbvision_muxsel(usbvision, vt->index, vt->mode);
PDEBUG ( DBG_IOCTL , " VIDIOC_S_TUNER " ) ;
return 0 ;
}
case VIDIOC_G_FREQUENCY :
{
struct v4l2_frequency * freq = arg ;
freq - > tuner = 0 ; // Only one tuner
freq - > type = V4L2_TUNER_ANALOG_TV ;
freq - > frequency = usbvision - > freq ;
PDEBUG ( DBG_IOCTL , " VIDIOC_G_FREQUENCY freq=0x%X " , ( unsigned ) freq - > frequency ) ;
return 0 ;
}
case VIDIOC_S_FREQUENCY :
{
struct v4l2_frequency * freq = arg ;
usbvision - > freq = freq - > frequency ;
call_i2c_clients ( usbvision , cmd , freq ) ;
PDEBUG ( DBG_IOCTL , " VIDIOC_S_FREQUENCY freq=0x%X " , ( unsigned ) freq - > frequency ) ;
return 0 ;
}
case VIDIOC_G_AUDIO :
{
struct v4l2_audio * v = arg ;
memset ( v , 0 , sizeof ( v ) ) ;
strcpy ( v - > name , " TV " ) ;
PDEBUG ( DBG_IOCTL , " VIDIOC_G_AUDIO " ) ;
// FIXME: no more processings ???
return 0 ;
}
case VIDIOC_S_AUDIO :
{
struct v4l2_audio * v = arg ;
if ( v - > index ) {
return - EINVAL ;
}
PDEBUG ( DBG_IOCTL , " VIDIOC_S_AUDIO " ) ;
// FIXME: void function ???
return 0 ;
}
case VIDIOC_QUERYCTRL :
{
struct v4l2_queryctrl * qc = arg ;
switch ( qc - > id ) {
case V4L2_CID_BRIGHTNESS :
case V4L2_CID_HUE :
case V4L2_CID_SATURATION :
case V4L2_CID_CONTRAST :
case V4L2_CID_AUDIO_VOLUME :
case V4L2_CID_AUDIO_MUTE :
return v4l2_ctrl_query_fill_std ( qc ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
case VIDIOC_G_CTRL :
{
struct v4l2_control * ctrl = arg ;
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
ctrl - > value = usbvision - > brightness ;
break ;
case V4L2_CID_CONTRAST :
ctrl - > value = usbvision - > contrast ;
break ;
case V4L2_CID_SATURATION :
ctrl - > value = usbvision - > saturation ;
break ;
case V4L2_CID_HUE :
ctrl - > value = usbvision - > hue ;
break ;
case V4L2_CID_AUDIO_VOLUME :
/* ctrl->value = usbvision->volume; */
break ;
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = usbvision - > AudioMute ;
break ;
default :
return - EINVAL ;
}
PDEBUG ( DBG_IOCTL , " VIDIOC_G_CTRL id=%x value=%x " , ctrl - > id , ctrl - > value ) ;
return 0 ;
}
case VIDIOC_S_CTRL :
{
struct v4l2_control * ctrl = arg ;
PDEBUG ( DBG_IOCTL , " VIDIOC_S_CTRL id=%x value=%x " , ctrl - > id , ctrl - > value ) ;
call_i2c_clients ( usbvision , VIDIOC_S_CTRL , ctrl ) ;
return 0 ;
}
case VIDIOC_REQBUFS :
{
struct v4l2_requestbuffers * vr = arg ;
// FIXME : normally we allocate the requested number of buffers.
// this driver allocates statically the buffers.
vr - > count = 2 ;
if ( vr - > memory ! = V4L2_MEMORY_MMAP )
return - EINVAL ;
return 0 ;
}
case VIDIOC_QUERYBUF :
{
struct v4l2_buffer * vb = arg ;
struct usbvision_frame * frame ;
// FIXME : works only on VIDEO_CAPTURE MODE, MMAP.
if ( vb - > type ! = V4L2_CAP_VIDEO_CAPTURE ) {
return - EINVAL ;
}
if ( vb - > index > 1 ) {
return - EINVAL ;
}
vb - > flags = 0 ;
frame = & usbvision - > frame [ vb - > index ] ;
if ( frame - > grabstate = = FrameState_Grabbing )
vb - > flags | = V4L2_BUF_FLAG_QUEUED ;
if ( frame - > grabstate > = FrameState_Done )
vb - > flags | = V4L2_BUF_FLAG_DONE ;
if ( frame - > grabstate = = FrameState_Unused )
vb - > flags | = V4L2_BUF_FLAG_MAPPED ;
vb - > memory = V4L2_MEMORY_MMAP ;
if ( vb - > index = = 0 ) {
vb - > m . offset = 0 ;
}
else {
vb - > m . offset = MAX_FRAME_SIZE ;
}
vb - > length = MAX_FRAME_SIZE ;
vb - > timestamp = usbvision - > frame [ vb - > index ] . timestamp ;
vb - > sequence = usbvision - > frame_num ;
return 0 ;
}
case VIDIOC_QBUF : // VIDIOCMCAPTURE + VIDIOCSYNC
{
struct v4l2_buffer * vb = arg ;
struct usbvision_frame * frame ;
// FIXME : works only on VIDEO_CAPTURE MODE, MMAP.
if ( vb - > type ! = V4L2_CAP_VIDEO_CAPTURE ) {
return - EINVAL ;
}
if ( vb - > index > 1 ) {
return - EINVAL ;
}
frame = & usbvision - > frame [ vb - > index ] ;
if ( frame - > grabstate = = FrameState_Grabbing ) {
return - EBUSY ;
}
usbvision_set_output ( usbvision , usbvision - > curwidth , usbvision - > curheight ) ;
/* Mark it as ready */
frame - > grabstate = FrameState_Ready ;
vb - > flags & = ~ V4L2_BUF_FLAG_DONE ;
/* set v4l2_format index */
frame - > v4l2_format = usbvision - > palette ;
2006-12-04 19:31:04 +08:00
PDEBUG ( DBG_IOCTL , " VIDIOC_QBUF frame=%d " , vb - > index ) ;
2006-12-04 19:30:53 +08:00
return usbvision_new_frame ( usbvision , vb - > index ) ;
}
case VIDIOC_DQBUF :
{
struct v4l2_buffer * vb = arg ;
int errCode = 0 ;
DECLARE_WAITQUEUE ( wait , current ) ;
// FIXME : not the proper way to get the last filled frame
vb - > index = - 1 ;
if ( usbvision - > curFrameNum ! = - 1 ) vb - > index = usbvision - > curFrameNum ;
else {
if ( usbvision - > frame [ 1 ] . grabstate > = FrameState_Done )
vb - > index = 1 ;
else if ( usbvision - > frame [ 0 ] . grabstate > = FrameState_Done )
vb - > index = 0 ;
// If no FRAME_DONE, look for a FRAME_GRABBING state.
// See if a frame is in process (grabbing), then use it.
if ( vb - > index = = - 1 ) {
if ( usbvision - > frame [ 1 ] . grabstate = = FrameState_Grabbing )
vb - > index = 1 ;
else if ( usbvision - > frame [ 0 ] . grabstate = = FrameState_Grabbing )
vb - > index = 0 ;
}
}
if ( vb - > index = = - 1 )
return - EINVAL ;
2006-12-04 19:31:04 +08:00
PDEBUG ( DBG_IOCTL , " VIDIOC_DQBUF frame=%d, grabstate=%d, curframeNum=%d " ,
vb - > index , usbvision - > frame [ vb - > index ] . grabstate , usbvision - > curFrameNum ) ;
2006-12-04 19:30:53 +08:00
switch ( usbvision - > frame [ vb - > index ] . grabstate ) {
case FrameState_Unused :
errCode = - EINVAL ;
break ;
case FrameState_Grabbing :
add_wait_queue ( & usbvision - > frame [ vb - > index ] . wq , & wait ) ;
current - > state = TASK_INTERRUPTIBLE ;
while ( usbvision - > frame [ vb - > index ] . grabstate = = FrameState_Grabbing ) {
schedule ( ) ;
if ( signal_pending ( current ) ) {
remove_wait_queue ( & usbvision - > frame [ vb - > index ] . wq , & wait ) ;
current - > state = TASK_RUNNING ;
return - EINTR ;
}
}
remove_wait_queue ( & usbvision - > frame [ vb - > index ] . wq , & wait ) ;
current - > state = TASK_RUNNING ;
case FrameState_Ready :
case FrameState_Error :
case FrameState_Done :
errCode = ( usbvision - > frame [ vb - > index ] . grabstate = = FrameState_Error ) ? - EIO : 0 ;
vb - > memory = V4L2_MEMORY_MMAP ;
vb - > flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE ;
2006-12-04 19:31:04 +08:00
vb - > field = V4L2_FIELD_NONE ;
2006-12-04 19:30:53 +08:00
vb - > sequence = usbvision - > frame [ vb - > index ] . sequence ;
usbvision - > frame [ vb - > index ] . grabstate = FrameState_Unused ;
break ;
}
usbvision - > frame [ vb - > index ] . grabstate = FrameState_Unused ;
return errCode ;
}
case VIDIOC_STREAMON :
{
int b = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
call_i2c_clients ( usbvision , VIDIOC_STREAMON , & b ) ;
return 0 ;
}
case VIDIOC_STREAMOFF :
{
int b = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
down ( & usbvision - > lock ) ;
// Stop all video streamings
call_i2c_clients ( usbvision , VIDIOC_STREAMOFF , & b ) ;
usbvision - > frame_num = - 1 ;
usbvision - > frame [ 0 ] . grabstate = FrameState_Unused ;
usbvision - > frame [ 1 ] . grabstate = FrameState_Unused ;
up ( & usbvision - > lock ) ;
return 0 ;
}
case VIDIOC_G_FBUF :
{
struct v4l2_framebuffer * vb = arg ;
if ( dga ) {
* vb = usbvision - > vid_buf ;
}
else {
memset ( vb , 0 , sizeof ( vb ) ) ; //dga not supported, not used
}
PDEBUG ( DBG_IOCTL , " VIDIOC_G_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d " ,
vb - > base , vb - > fmt . width , vb - > fmt . height , vb - > fmt . pixelformat , vb - > fmt . bytesperline ) ;
return 0 ;
}
case VIDIOC_S_FBUF :
{
struct v4l2_framebuffer * vb = arg ;
int formatIdx ;
if ( dga = = 0 ) {
return - EINVAL ;
}
if ( ! capable ( CAP_SYS_ADMIN ) & & ! capable ( CAP_SYS_ADMIN ) ) {
return - EPERM ;
}
PDEBUG ( DBG_IOCTL , " VIDIOC_S_FBUF base=%p, width=%d, height=%d, pixelformat=%d, bpl=%d " ,
vb - > base , vb - > fmt . width , vb - > fmt . height , vb - > fmt . pixelformat , vb - > fmt . bytesperline ) ;
for ( formatIdx = 0 ; formatIdx < = USBVISION_SUPPORTED_PALETTES ; formatIdx + + ) {
if ( formatIdx = = USBVISION_SUPPORTED_PALETTES ) {
return - EINVAL ; // no matching video_format
}
if ( ( vb - > fmt . pixelformat = = usbvision_v4l2_format [ formatIdx ] . format ) & &
( usbvision_v4l2_format [ formatIdx ] . supported ) ) {
break ; //found matching video_format
}
}
if ( vb - > fmt . bytesperline < 1 ) {
return - EINVAL ;
}
if ( usbvision - > overlay ) {
return - EBUSY ;
}
down ( & usbvision - > lock ) ;
if ( usbvision - > overlay_base ) {
iounmap ( usbvision - > overlay_base ) ;
usbvision - > vid_buf_valid = 0 ;
}
usbvision - > overlay_base = ioremap ( ( ulong ) vb - > base , vb - > fmt . height * vb - > fmt . bytesperline ) ;
if ( usbvision - > overlay_base ) {
usbvision - > vid_buf_valid = 1 ;
}
usbvision - > vid_buf = * vb ;
usbvision - > overlay_frame . v4l2_format = usbvision_v4l2_format [ formatIdx ] ;
up ( & usbvision - > lock ) ;
return 0 ;
}
case VIDIOC_ENUM_FMT :
{
struct v4l2_fmtdesc * vfd = arg ;
if ( ( dga = = 0 ) & &
( vfd - > type = = V4L2_BUF_TYPE_VIDEO_OVERLAY ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YVU420 ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YUV422P ) ) {
return - EINVAL ;
}
if ( vfd - > index > = USBVISION_SUPPORTED_PALETTES - 1 ) {
return - EINVAL ;
}
vfd - > flags = 0 ;
strcpy ( vfd - > description , usbvision_v4l2_format [ vfd - > index ] . desc ) ;
vfd - > pixelformat = usbvision_v4l2_format [ vfd - > index ] . format ;
return 0 ;
}
case VIDIOC_G_FMT :
{
struct v4l2_format * vf = arg ;
if ( ( dga = = 0 ) & &
( vf - > type = = V4L2_BUF_TYPE_VIDEO_OVERLAY ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YVU420 ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YUV422P ) ) {
return - EINVAL ;
}
down ( & usbvision - > lock ) ;
* vf = usbvision - > vid_win ;
up ( & usbvision - > lock ) ;
PDEBUG ( DBG_IOCTL , " VIDIOC_G_FMT x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d " ,
vf - > fmt . win . w . left , vf - > fmt . win . w . top , vf - > fmt . win . w . width , vf - > fmt . win . w . height , vf - > fmt . win . chromakey , vf - > fmt . win . clipcount ) ;
return 0 ;
}
case VIDIOC_S_FMT :
{
struct v4l2_format * vf = arg ;
struct v4l2_clip * vc = NULL ;
int on , formatIdx ;
if ( ( dga = = 0 ) & &
( vf - > type = = V4L2_BUF_TYPE_VIDEO_OVERLAY ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YVU420 ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YUV422P ) ) {
return - EINVAL ;
}
if ( vf - > type = = V4L2_BUF_TYPE_VIDEO_OVERLAY ) {
if ( vf - > fmt . win . clipcount > 256 ) {
return - EDOM ; /* Too many clips! */
}
// Do every clips.
vc = vmalloc ( sizeof ( struct v4l2_clip ) * ( vf - > fmt . win . clipcount + 4 ) ) ;
if ( vc = = NULL ) {
return - ENOMEM ;
}
if ( vf - > fmt . win . clipcount & & copy_from_user ( vc , vf - > fmt . win . clips , sizeof ( struct v4l2_clip ) * vf - > fmt . win . clipcount ) ) {
return - EFAULT ;
}
on = usbvision - > overlay ; // Save overlay state
if ( on ) {
usbvision_cap ( usbvision , 0 ) ;
}
// strange, it seems xawtv sometimes calls us with 0
// width and/or height. Ignore these values
if ( vf - > fmt . win . w . left = = 0 ) {
vf - > fmt . win . w . left = usbvision - > vid_win . fmt . win . w . left ;
}
if ( vf - > fmt . win . w . top = = 0 ) {
vf - > fmt . win . w . top = usbvision - > vid_win . fmt . win . w . top ;
}
// by now we are committed to the new data...
down ( & usbvision - > lock ) ;
RESTRICT_TO_RANGE ( vf - > fmt . win . w . width , MIN_FRAME_WIDTH , MAX_FRAME_WIDTH ) ;
RESTRICT_TO_RANGE ( vf - > fmt . win . w . height , MIN_FRAME_HEIGHT , MAX_FRAME_HEIGHT ) ;
usbvision - > vid_win = * vf ;
usbvision - > overlay_frame . width = vf - > fmt . win . w . width ;
usbvision - > overlay_frame . height = vf - > fmt . win . w . height ;
usbvision_set_output ( usbvision , vf - > fmt . win . w . width , vf - > fmt . win . w . height ) ;
up ( & usbvision - > lock ) ;
// Impose display clips
if ( vf - > fmt . win . w . left + vf - > fmt . win . w . width > ( unsigned int ) usbvision - > vid_buf . fmt . width ) {
usbvision_new_clip ( vf , vc , usbvision - > vid_buf . fmt . width - vf - > fmt . win . w . left , 0 , vf - > fmt . win . w . width - 1 , vf - > fmt . win . w . height - 1 ) ;
}
if ( vf - > fmt . win . w . top + vf - > fmt . win . w . height > ( unsigned int ) usbvision - > vid_buf . fmt . height ) {
usbvision_new_clip ( vf , vc , 0 , usbvision - > vid_buf . fmt . height - vf - > fmt . win . w . top , vf - > fmt . win . w . width - 1 , vf - > fmt . win . w . height - 1 ) ;
}
// built the requested clipping zones
usbvision_built_overlay ( usbvision , vf - > fmt . win . clipcount , vc ) ;
vfree ( vc ) ;
// restore overlay state
if ( on ) {
usbvision_cap ( usbvision , 1 ) ;
}
usbvision - > vid_win_valid = 1 ;
PDEBUG ( DBG_IOCTL , " VIDIOC_S_FMT overlay x=%d, y=%d, w=%d, h=%d, chroma=%x, clips=%d " ,
vf - > fmt . win . w . left , vf - > fmt . win . w . top , vf - > fmt . win . w . width , vf - > fmt . win . w . height , vf - > fmt . win . chromakey , vf - > fmt . win . clipcount ) ;
}
else {
/* Find requested format in available ones */
for ( formatIdx = 0 ; formatIdx < USBVISION_SUPPORTED_PALETTES ; formatIdx + + ) {
if ( vf - > fmt . pix . pixelformat = = usbvision_v4l2_format [ formatIdx ] . format ) {
usbvision - > palette = usbvision_v4l2_format [ formatIdx ] ;
break ;
}
}
/* robustness */
if ( formatIdx = = USBVISION_SUPPORTED_PALETTES ) {
return - EINVAL ;
}
usbvision - > vid_win . fmt . pix . pixelformat = vf - > fmt . pix . pixelformat ;
usbvision - > vid_win . fmt . pix . width = vf - > fmt . pix . width ; //Image width in pixels.
usbvision - > vid_win . fmt . pix . height = vf - > fmt . pix . height ; // Image height in pixels.
// by now we are committed to the new data...
down ( & usbvision - > lock ) ;
RESTRICT_TO_RANGE ( vf - > fmt . pix . width , MIN_FRAME_WIDTH , MAX_FRAME_WIDTH ) ;
RESTRICT_TO_RANGE ( vf - > fmt . pix . height , MIN_FRAME_HEIGHT , MAX_FRAME_HEIGHT ) ;
usbvision - > vid_win = * vf ;
usbvision_set_output ( usbvision , vf - > fmt . pix . width , vf - > fmt . pix . height ) ;
up ( & usbvision - > lock ) ;
usbvision - > vid_win_valid = 1 ;
PDEBUG ( DBG_IOCTL , " VIDIOC_S_FMT grabdisplay w=%d, h=%d, " ,
vf - > fmt . pix . width , vf - > fmt . pix . height ) ;
}
return 0 ;
}
case VIDIOC_OVERLAY :
{
int * v = arg ;
if ( ( dga = = 0 ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YVU420 ) & &
( usbvision - > palette . format ! = V4L2_PIX_FMT_YUV422P ) ) {
PDEBUG ( DBG_IOCTL , " VIDIOC_OVERLAY DGA disabled " ) ;
return - EINVAL ;
}
if ( * v = = 0 ) {
usbvision_cap ( usbvision , 0 ) ;
}
else {
// are VIDIOCSFBUF and VIDIOCSWIN done?
if ( ( usbvision - > vid_buf_valid = = 0 ) | | ( usbvision - > vid_win_valid = = 0 ) ) {
PDEBUG ( DBG_IOCTL , " VIDIOC_OVERLAY vid_buf_valid %d; vid_win_valid %d " ,
usbvision - > vid_buf_valid , usbvision - > vid_win_valid ) ;
return - EINVAL ;
}
usbvision_cap ( usbvision , 1 ) ;
}
PDEBUG ( DBG_IOCTL , " VIDIOC_OVERLAY %s " , ( * v ) ? " on " : " off " ) ;
return 0 ;
}
default :
return - ENOIOCTLCMD ;
}
return 0 ;
}
static int usbvision_v4l2_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return video_usercopy ( inode , file , cmd , arg , usbvision_v4l2_do_ioctl ) ;
}
static ssize_t usbvision_v4l2_read ( struct file * file , char * buf ,
size_t count , loff_t * ppos )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
int noblock = file - > f_flags & O_NONBLOCK ;
int frmx = - 1 ;
int rc = 0 ;
struct usbvision_frame * frame ;
PDEBUG ( DBG_IO , " %s: %ld bytes, noblock=%d " , __FUNCTION__ , ( unsigned long ) count , noblock ) ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) | | ( buf = = NULL ) )
return - EFAULT ;
down ( & usbvision - > lock ) ;
//code for testing compression
if ( usbvision - > isocMode = = ISOC_MODE_COMPRESS ) {
usbvision - > frame [ 0 ] . v4l2_format = usbvision_v4l2_format [ 0 ] ; //V4L2_PIX_FMT_GREY;
usbvision - > frame [ 1 ] . v4l2_format = usbvision_v4l2_format [ 0 ] ; // V4L2_PIX_FMT_GREY;
}
// See if a frame is completed, then use it.
if ( usbvision - > frame [ 0 ] . grabstate > = FrameState_Done ) // _Done or _Error
frmx = 0 ;
else if ( usbvision - > frame [ 1 ] . grabstate > = FrameState_Done ) // _Done or _Error
frmx = 1 ;
if ( noblock & & ( frmx = = - 1 ) ) {
count = - EAGAIN ;
goto usbvision_v4l2_read_done ;
}
// If no FRAME_DONE, look for a FRAME_GRABBING state.
// See if a frame is in process (grabbing), then use it.
if ( frmx = = - 1 ) {
if ( usbvision - > frame [ 0 ] . grabstate = = FrameState_Grabbing )
frmx = 0 ;
else if ( usbvision - > frame [ 1 ] . grabstate = = FrameState_Grabbing )
frmx = 1 ;
}
// If no frame is active, start one.
if ( frmx = = - 1 )
usbvision_new_frame ( usbvision , frmx = 0 ) ;
frame = & usbvision - > frame [ frmx ] ;
restart :
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) ) {
count = - EIO ;
goto usbvision_v4l2_read_done ;
}
PDEBUG ( DBG_IO , " Waiting frame grabbing " ) ;
rc = wait_event_interruptible ( frame - > wq , ( frame - > grabstate = = FrameState_Done ) | |
( frame - > grabstate = = FrameState_Error ) ) ;
if ( rc ) {
goto usbvision_v4l2_read_done ;
}
if ( frame - > grabstate = = FrameState_Error ) {
frame - > bytes_read = 0 ;
if ( usbvision_new_frame ( usbvision , frmx ) ) {
err ( " %s: usbvision_new_frame() failed " , __FUNCTION__ ) ;
}
goto restart ;
}
PDEBUG ( DBG_IO , " %s: frmx=%d, bytes_read=%ld, scanlength=%ld " , __FUNCTION__ ,
frmx , frame - > bytes_read , frame - > scanlength ) ;
/* copy bytes to user space; we allow for partials reads */
if ( ( count + frame - > bytes_read ) > ( unsigned long ) frame - > scanlength )
count = frame - > scanlength - frame - > bytes_read ;
if ( copy_to_user ( buf , frame - > data + frame - > bytes_read , count ) ) {
count = - EFAULT ;
goto usbvision_v4l2_read_done ;
}
frame - > bytes_read + = count ;
PDEBUG ( DBG_IO , " %s: {copy} count used=%ld, new bytes_read=%ld " , __FUNCTION__ ,
( unsigned long ) count , frame - > bytes_read ) ;
if ( frame - > bytes_read > = frame - > scanlength ) { // All data has been read
frame - > bytes_read = 0 ;
/* Mark it as available to be used again. */
usbvision - > frame [ frmx ] . grabstate = FrameState_Unused ;
if ( usbvision_new_frame ( usbvision , frmx ? 0 : 1 ) )
err ( " %s: usbvision_new_frame() failed " , __FUNCTION__ ) ;
}
usbvision_v4l2_read_done :
up ( & usbvision - > lock ) ;
return count ;
}
static int usbvision_v4l2_mmap ( struct file * file , struct vm_area_struct * vma )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
unsigned long start = vma - > vm_start ;
unsigned long size = vma - > vm_end - vma - > vm_start ;
unsigned long page , pos ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return - EFAULT ;
if ( size > ( ( ( USBVISION_NUMFRAMES * usbvision - > max_frame_size ) + PAGE_SIZE - 1 ) & ~ ( PAGE_SIZE - 1 ) ) )
return - EINVAL ;
pos = ( unsigned long ) usbvision - > fbuf ;
while ( size > 0 ) {
// Really ugly....
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,10) //Compatibility for 2.6.10+ kernels
page = vmalloc_to_pfn ( ( void * ) pos ) ;
if ( remap_pfn_range ( vma , start , page , PAGE_SIZE , PAGE_SHARED ) ) {
return - EAGAIN ;
}
# else //Compatibility for 2.6.0 - 2.6.9 kernels
page = usbvision_kvirt_to_pa ( pos ) ;
if ( remap_page_range ( vma , start , page , PAGE_SIZE , PAGE_SHARED ) ) {
return - EAGAIN ;
}
# endif
start + = PAGE_SIZE ;
pos + = PAGE_SIZE ;
if ( size > PAGE_SIZE )
size - = PAGE_SIZE ;
else
size = 0 ;
}
return 0 ;
}
/*
* Here comes the stuff for radio on usbvision based devices
*
*/
static int usbvision_radio_open ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
struct v4l2_frequency freq ;
int errCode = 0 ;
PDEBUG ( DBG_RIO , " %s: " , __FUNCTION__ ) ;
down ( & usbvision - > lock ) ;
if ( usbvision - > user ) {
err ( " %s: Someone tried to open an already opened USBVision Radio! " , __FUNCTION__ ) ;
errCode = - EBUSY ;
}
else {
if ( PowerOnAtOpen ) {
if ( timer_pending ( & usbvision - > powerOffTimer ) ) {
del_timer ( & usbvision - > powerOffTimer ) ;
}
if ( usbvision - > power = = 0 ) {
usbvision_power_on ( usbvision ) ;
usbvision_init_i2c ( usbvision ) ;
}
}
// If so far no errors then we shall start the radio
usbvision - > radio = 1 ;
call_i2c_clients ( usbvision , AUDC_SET_RADIO , & usbvision - > tuner_type ) ;
freq . frequency = 1517 ; //SWR3 @ 94.8MHz
call_i2c_clients ( usbvision , VIDIOC_S_FREQUENCY , & freq ) ;
usbvision_set_audio ( usbvision , USBVISION_AUDIO_RADIO ) ;
usbvision - > user + + ;
}
if ( errCode ) {
if ( PowerOnAtOpen ) {
usbvision_i2c_usb_del_bus ( & usbvision - > i2c_adap ) ;
usbvision_power_off ( usbvision ) ;
usbvision - > initialized = 0 ;
}
}
up ( & usbvision - > lock ) ;
return errCode ;
}
static int usbvision_radio_close ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
int errCode = 0 ;
PDEBUG ( DBG_RIO , " " ) ;
down ( & usbvision - > lock ) ;
usbvision_audio_off ( usbvision ) ;
usbvision - > radio = 0 ;
usbvision - > user - - ;
if ( PowerOnAtOpen ) {
mod_timer ( & usbvision - > powerOffTimer , jiffies + USBVISION_POWEROFF_TIME ) ;
usbvision - > initialized = 0 ;
}
up ( & usbvision - > lock ) ;
if ( usbvision - > remove_pending ) {
info ( " %s: Final disconnect " , __FUNCTION__ ) ;
usbvision_release ( usbvision ) ;
}
PDEBUG ( DBG_RIO , " success " ) ;
return errCode ;
}
static int usbvision_do_radio_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return - EIO ;
switch ( cmd ) {
/***************************
* V4L2 IOCTLs *
* * * * * * * * * * * * * * * * * * * * * * * * * * */
case VIDIOC_QUERYCAP :
{
struct v4l2_capability * vc = arg ;
memset ( vc , 0 , sizeof ( struct v4l2_capability ) ) ;
strcpy ( vc - > driver , " usbvision radio " ) ;
strcpy ( vc - > card , usbvision - > vcap . card ) ;
strcpy ( vc - > bus_info , " usb " ) ;
vc - > version = USBVISION_DRIVER_VERSION ; /* version */
vc - > capabilities = V4L2_CAP_TUNER ; /* capabilities */
PDEBUG ( DBG_RIO , " %s: VIDIOC_QUERYCAP " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOC_QUERYCTRL :
{
struct v4l2_queryctrl * qc = arg ;
switch ( qc - > id )
{
case V4L2_CID_AUDIO_VOLUME :
case V4L2_CID_AUDIO_MUTE :
return v4l2_ctrl_query_fill_std ( qc ) ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
case VIDIOC_G_CTRL :
{
struct v4l2_control * ctrl = arg ;
PDEBUG ( DBG_IOCTL , " VIDIOC_G_CTRL id=%x value=%x " , ctrl - > id , ctrl - > value ) ;
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_VOLUME :
/* ctrl->value = usbvision->volume; */
break ;
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = usbvision - > AudioMute ;
break ;
default :
return - EINVAL ;
}
return 0 ;
}
case VIDIOC_S_CTRL :
{
struct v4l2_control * ctrl = arg ;
call_i2c_clients ( usbvision , VIDIOC_S_CTRL , ctrl ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOC_S_CTRL id=%x value=%x " , __FUNCTION__ , ctrl - > id , ctrl - > value ) ;
return 0 ;
}
case VIDIOC_G_TUNER :
{
struct v4l2_tuner * vt = arg ;
if ( ! usbvision - > have_tuner | | vt - > index ) // Only tuner 0
return - EINVAL ;
strcpy ( vt - > name , " Radio " ) ;
vt - > type = V4L2_TUNER_RADIO ;
vt - > capability = V4L2_TUNER_CAP_STEREO ;
// japan: 76.0 MHz - 89.9 MHz
// western europe: 87.5 MHz - 108.0 MHz
// russia: 65.0 MHz - 108.0 MHz
vt - > rangelow = ( int ) ( 65 * 16 ) ; ;
vt - > rangehigh = ( int ) ( 108 * 16 ) ;
vt - > audmode = V4L2_TUNER_MODE_STEREO ;
vt - > rxsubchans = V4L2_TUNER_SUB_STEREO ;
call_i2c_clients ( usbvision , VIDIOC_G_TUNER , & vt ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOC_G_TUNER signal=%d " , __FUNCTION__ , vt - > signal ) ;
return 0 ;
}
case VIDIOC_S_TUNER :
{
struct v4l2_tuner * vt = arg ;
// Only channel 0 has a tuner
if ( ( vt - > index ) | | ( usbvision - > channel ) ) {
return - EINVAL ;
}
PDEBUG ( DBG_RIO , " %s: VIDIOC_S_TUNER " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOC_G_AUDIO :
{
struct v4l2_audio * va = arg ;
memset ( va , 0 , sizeof ( va ) ) ;
va - > capability = V4L2_AUDCAP_STEREO ;
strcpy ( va - > name , " Radio " ) ;
PDEBUG ( DBG_IOCTL , " VIDIOC_G_AUDIO " ) ;
return 0 ;
}
case VIDIOC_S_AUDIO :
{
struct v4l2_audio * v = arg ;
if ( v - > index ) {
return - EINVAL ;
}
PDEBUG ( DBG_IOCTL , " VIDIOC_S_AUDIO " ) ;
// FIXME: void function ???
return 0 ;
}
case VIDIOC_G_FREQUENCY :
{
struct v4l2_frequency * freq = arg ;
freq - > tuner = 0 ; // Only one tuner
freq - > type = V4L2_TUNER_RADIO ;
freq - > frequency = usbvision - > freq ;
PDEBUG ( DBG_RIO , " VIDIOC_G_FREQUENCY freq=0x%X " , ( unsigned ) freq - > frequency ) ;
return 0 ;
}
case VIDIOC_S_FREQUENCY :
{
struct v4l2_frequency * freq = arg ;
usbvision - > freq = freq - > frequency ;
call_i2c_clients ( usbvision , cmd , freq ) ;
PDEBUG ( DBG_RIO , " VIDIOC_S_FREQUENCY freq=0x%X " , ( unsigned ) freq - > frequency ) ;
return 0 ;
}
/***************************
* V4L1 IOCTLs *
* * * * * * * * * * * * * * * * * * * * * * * * * * */
case VIDIOCGCAP :
{
struct video_capability * vc = arg ;
memset ( vc , 0 , sizeof ( struct video_capability ) ) ;
strcpy ( vc - > name , usbvision - > vcap . card ) ;
vc - > type = VID_TYPE_TUNER ;
vc - > channels = 1 ;
vc - > audios = 1 ;
PDEBUG ( DBG_RIO , " %s: VIDIOCGCAP " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOCGTUNER :
{
struct video_tuner * vt = arg ;
if ( ( vt - > tuner ) | | ( usbvision - > channel ) ) { /* Only tuner 0 */
return - EINVAL ;
}
strcpy ( vt - > name , " Radio " ) ;
// japan: 76.0 MHz - 89.9 MHz
// western europe: 87.5 MHz - 108.0 MHz
// russia: 65.0 MHz - 108.0 MHz
vt - > rangelow = ( int ) ( 65 * 16 ) ;
vt - > rangehigh = ( int ) ( 108 * 16 ) ;
vt - > flags = 0 ;
vt - > mode = 0 ;
call_i2c_clients ( usbvision , cmd , vt ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOCGTUNER signal=%d " , __FUNCTION__ , vt - > signal ) ;
return 0 ;
}
case VIDIOCSTUNER :
{
struct video_tuner * vt = arg ;
// Only channel 0 has a tuner
if ( ( vt - > tuner ) | | ( usbvision - > channel ) ) {
return - EINVAL ;
}
PDEBUG ( DBG_RIO , " %s: VIDIOCSTUNER " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOCGAUDIO :
{
struct video_audio * va = arg ;
memset ( va , 0 , sizeof ( struct video_audio ) ) ;
call_i2c_clients ( usbvision , cmd , va ) ;
va - > flags | = VIDEO_AUDIO_MUTABLE ;
va - > volume = 1 ;
va - > step = 1 ;
strcpy ( va - > name , " Radio " ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOCGAUDIO " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOCSAUDIO :
{
struct video_audio * va = arg ;
if ( va - > audio ) {
return - EINVAL ;
}
if ( va - > flags & VIDEO_AUDIO_MUTE ) {
if ( usbvision_audio_mute ( usbvision ) ) {
return - EFAULT ;
}
}
else {
if ( usbvision_audio_on ( usbvision ) ) {
return - EFAULT ;
}
}
PDEBUG ( DBG_RIO , " %s: VIDIOCSAUDIO flags=0x%x) " , __FUNCTION__ , va - > flags ) ;
return 0 ;
}
case VIDIOCGFREQ :
{
unsigned long * freq = arg ;
* freq = usbvision - > freq ;
PDEBUG ( DBG_RIO , " %s: VIDIOCGFREQ freq = %ld00 kHz " , __FUNCTION__ , ( * freq * 10 ) > > 4 ) ;
return 0 ;
}
case VIDIOCSFREQ :
{
unsigned long * freq = arg ;
usbvision - > freq = * freq ;
call_i2c_clients ( usbvision , cmd , freq ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOCSFREQ freq = %ld00 kHz " , __FUNCTION__ , ( * freq * 10 ) > > 4 ) ;
return 0 ;
}
default :
{
PDEBUG ( DBG_RIO , " %s: Unknown command %x " , __FUNCTION__ , cmd ) ;
return - ENOIOCTLCMD ;
}
}
return 0 ;
}
static int usbvision_radio_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return video_usercopy ( inode , file , cmd , arg , usbvision_do_radio_ioctl ) ;
}
/*
* Here comes the stuff for vbi on usbvision based devices
*
*/
static int usbvision_vbi_open ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
unsigned long freq ;
int errCode = 0 ;
PDEBUG ( DBG_RIO , " %s: " , __FUNCTION__ ) ;
down ( & usbvision - > lock ) ;
if ( usbvision - > user ) {
err ( " %s: Someone tried to open an already opened USBVision VBI! " , __FUNCTION__ ) ;
errCode = - EBUSY ;
}
else {
if ( PowerOnAtOpen ) {
if ( timer_pending ( & usbvision - > powerOffTimer ) ) {
del_timer ( & usbvision - > powerOffTimer ) ;
}
if ( usbvision - > power = = 0 ) {
usbvision_power_on ( usbvision ) ;
usbvision_init_i2c ( usbvision ) ;
}
}
// If so far no errors then we shall start the vbi device
//usbvision->vbi = 1;
call_i2c_clients ( usbvision , AUDC_SET_RADIO , & usbvision - > tuner_type ) ;
freq = 1517 ; //SWR3 @ 94.8MHz
call_i2c_clients ( usbvision , VIDIOCSFREQ , & freq ) ;
usbvision_set_audio ( usbvision , USBVISION_AUDIO_RADIO ) ;
usbvision - > user + + ;
}
if ( errCode ) {
if ( PowerOnAtOpen ) {
usbvision_i2c_usb_del_bus ( & usbvision - > i2c_adap ) ;
usbvision_power_off ( usbvision ) ;
usbvision - > initialized = 0 ;
}
}
up ( & usbvision - > lock ) ;
return errCode ;
}
static int usbvision_vbi_close ( struct inode * inode , struct file * file )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
int errCode = 0 ;
PDEBUG ( DBG_RIO , " " ) ;
down ( & usbvision - > lock ) ;
usbvision_audio_off ( usbvision ) ;
usbvision - > vbi = 0 ;
usbvision - > user - - ;
if ( PowerOnAtOpen ) {
mod_timer ( & usbvision - > powerOffTimer , jiffies + USBVISION_POWEROFF_TIME ) ;
usbvision - > initialized = 0 ;
}
up ( & usbvision - > lock ) ;
if ( usbvision - > remove_pending ) {
info ( " %s: Final disconnect " , __FUNCTION__ ) ;
usbvision_release ( usbvision ) ;
}
PDEBUG ( DBG_RIO , " success " ) ;
return errCode ;
}
static int usbvision_do_vbi_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , void * arg )
{
struct video_device * dev = video_devdata ( file ) ;
struct usb_usbvision * usbvision = ( struct usb_usbvision * ) video_get_drvdata ( dev ) ;
if ( ! USBVISION_IS_OPERATIONAL ( usbvision ) )
return - EIO ;
switch ( cmd ) {
case VIDIOC_QUERYCAP :
{
struct v4l2_capability * vc = arg ;
memset ( vc , 0 , sizeof ( struct v4l2_capability ) ) ;
strcpy ( vc - > driver , " usbvision vbi " ) ;
strcpy ( vc - > card , usbvision - > vcap . card ) ;
strcpy ( vc - > bus_info , " usb " ) ;
vc - > version = USBVISION_DRIVER_VERSION ; /* version */
vc - > capabilities = V4L2_CAP_VBI_CAPTURE ; /* capabilities */
PDEBUG ( DBG_RIO , " %s: VIDIOC_QUERYCAP " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOCGTUNER :
{
struct video_tuner * vt = arg ;
if ( ( vt - > tuner ) | | ( usbvision - > channel ) ) { /* Only tuner 0 */
return - EINVAL ;
}
strcpy ( vt - > name , " vbi " ) ;
// japan: 76.0 MHz - 89.9 MHz
// western europe: 87.5 MHz - 108.0 MHz
// russia: 65.0 MHz - 108.0 MHz
vt - > rangelow = ( int ) ( 65 * 16 ) ;
vt - > rangehigh = ( int ) ( 108 * 16 ) ;
vt - > flags = 0 ;
vt - > mode = 0 ;
call_i2c_clients ( usbvision , cmd , vt ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOCGTUNER signal=%d " , __FUNCTION__ , vt - > signal ) ;
return 0 ;
}
case VIDIOCSTUNER :
{
struct video_tuner * vt = arg ;
// Only channel 0 has a tuner
if ( ( vt - > tuner ) | | ( usbvision - > channel ) ) {
return - EINVAL ;
}
PDEBUG ( DBG_RIO , " %s: VIDIOCSTUNER " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOCGAUDIO :
{
struct video_audio * va = arg ;
memset ( va , 0 , sizeof ( struct video_audio ) ) ;
call_i2c_clients ( usbvision , cmd , va ) ;
va - > flags | = VIDEO_AUDIO_MUTABLE ;
va - > volume = 1 ;
va - > step = 1 ;
strcpy ( va - > name , " vbi " ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOCGAUDIO " , __FUNCTION__ ) ;
return 0 ;
}
case VIDIOCSAUDIO :
{
struct video_audio * va = arg ;
if ( va - > audio ) {
return - EINVAL ;
}
if ( va - > flags & VIDEO_AUDIO_MUTE ) {
if ( usbvision_audio_mute ( usbvision ) ) {
return - EFAULT ;
}
}
else {
if ( usbvision_audio_on ( usbvision ) ) {
return - EFAULT ;
}
}
PDEBUG ( DBG_RIO , " %s: VIDIOCSAUDIO flags=0x%x) " , __FUNCTION__ , va - > flags ) ;
return 0 ;
}
case VIDIOCGFREQ :
{
unsigned long * freq = arg ;
* freq = usbvision - > freq ;
PDEBUG ( DBG_RIO , " %s: VIDIOCGFREQ freq = %ld00 kHz " , __FUNCTION__ , ( * freq * 10 ) > > 4 ) ;
return 0 ;
}
case VIDIOCSFREQ :
{
unsigned long * freq = arg ;
usbvision - > freq = * freq ;
call_i2c_clients ( usbvision , cmd , freq ) ;
PDEBUG ( DBG_RIO , " %s: VIDIOCSFREQ freq = %ld00 kHz " , __FUNCTION__ , ( * freq * 10 ) > > 4 ) ;
return 0 ;
}
default :
{
PDEBUG ( DBG_RIO , " %s: Unknown command %d " , __FUNCTION__ , cmd ) ;
return - ENOIOCTLCMD ;
}
}
return 0 ;
}
static int usbvision_vbi_ioctl ( struct inode * inode , struct file * file ,
unsigned int cmd , unsigned long arg )
{
return video_usercopy ( inode , file , cmd , arg , usbvision_do_vbi_ioctl ) ;
}
static void usbvision_configure_video ( struct usb_usbvision * usbvision )
{
int model ;
if ( usbvision = = NULL )
return ;
model = usbvision - > DevModel ;
RESTRICT_TO_RANGE ( init_brightness , 0 , 255 ) ;
RESTRICT_TO_RANGE ( init_contrast , 0 , 255 ) ;
RESTRICT_TO_RANGE ( init_saturation , 0 , 255 ) ;
RESTRICT_TO_RANGE ( init_hue , 0 , 255 ) ;
usbvision - > saturation = init_saturation < < 8 ;
usbvision - > hue = init_hue < < 8 ;
usbvision - > brightness = init_brightness < < 8 ;
usbvision - > contrast = init_contrast < < 8 ;
usbvision - > depth = 24 ;
usbvision - > palette = usbvision_v4l2_format [ 2 ] ; // V4L2_PIX_FMT_RGB24;
if ( usbvision_device_data [ usbvision - > DevModel ] . Vin_Reg2 > = 0 ) {
usbvision - > Vin_Reg2_Preset = usbvision_device_data [ usbvision - > DevModel ] . Vin_Reg2 & 0xff ;
} else {
usbvision - > Vin_Reg2_Preset = 0 ;
}
memset ( & usbvision - > vcap , 0 , sizeof ( usbvision - > vcap ) ) ;
strcpy ( usbvision - > vcap . driver , " USBVision " ) ;
usbvision - > vcap . capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING |
( dga ? ( V4L2_FBUF_CAP_LIST_CLIPPING | V4L2_CAP_VIDEO_OVERLAY ) : 0 ) |
( usbvision - > have_tuner ? V4L2_CAP_TUNER : 0 ) ;
usbvision - > vcap . version = USBVISION_DRIVER_VERSION ; /* version */
usbvision - > video_inputs = usbvision_device_data [ model ] . VideoChannels ;
memset ( & usbvision - > input , 0 , sizeof ( usbvision - > input ) ) ;
usbvision - > input . tuner = 0 ;
usbvision - > input . type = V4L2_INPUT_TYPE_TUNER | VIDEO_TYPE_CAMERA ;
usbvision - > input . std = usbvision_device_data [ model ] . VideoNorm ;
/* This should be here to make i2c clients to be able to register */
usbvision_audio_off ( usbvision ) ; //first switch off audio
if ( ! PowerOnAtOpen ) {
usbvision_power_on ( usbvision ) ; //and then power up the noisy tuner
usbvision_init_i2c ( usbvision ) ;
}
}
//
// Video registration stuff
//
// Video template
static struct file_operations usbvision_fops = {
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
. owner = THIS_MODULE ,
# endif
. open = usbvision_v4l2_open ,
. release = usbvision_v4l2_close ,
. read = usbvision_v4l2_read ,
. mmap = usbvision_v4l2_mmap ,
. ioctl = usbvision_v4l2_ioctl ,
. llseek = no_llseek ,
} ;
static struct video_device usbvision_video_template = {
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
. owner = THIS_MODULE ,
# endif
. type = VID_TYPE_TUNER | VID_TYPE_CAPTURE ,
. hardware = VID_HARDWARE_USBVISION ,
. fops = & usbvision_fops ,
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
. name = " usbvision-video " ,
. release = video_device_release ,
# endif
. minor = - 1 ,
} ;
// Radio template
static struct file_operations usbvision_radio_fops = {
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
. owner = THIS_MODULE ,
# endif
. open = usbvision_radio_open ,
. release = usbvision_radio_close ,
. ioctl = usbvision_radio_ioctl ,
. llseek = no_llseek ,
} ;
static struct video_device usbvision_radio_template =
{
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
. owner = THIS_MODULE ,
# endif
. type = VID_TYPE_TUNER ,
. hardware = VID_HARDWARE_USBVISION ,
. fops = & usbvision_radio_fops ,
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
. release = video_device_release ,
. name = " usbvision-radio " ,
# endif
. minor = - 1 ,
} ;
// vbi template
static struct file_operations usbvision_vbi_fops = {
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
. owner = THIS_MODULE ,
# endif
. open = usbvision_vbi_open ,
. release = usbvision_vbi_close ,
. ioctl = usbvision_vbi_ioctl ,
. llseek = no_llseek ,
} ;
static struct video_device usbvision_vbi_template =
{
# if LINUX_VERSION_CODE > KERNEL_VERSION(2,4,31)
. owner = THIS_MODULE ,
# endif
. type = VID_TYPE_TUNER ,
. hardware = VID_HARDWARE_USBVISION ,
. fops = & usbvision_vbi_fops ,
# if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
. release = video_device_release ,
. name = " usbvision-vbi " ,
# endif
. minor = - 1 ,
} ;
static struct video_device * usbvision_vdev_init ( struct usb_usbvision * usbvision ,
struct video_device * vdev_template ,
char * name )
{
struct usb_device * usb_dev = usbvision - > dev ;
struct video_device * vdev ;
if ( usb_dev = = NULL ) {
err ( " %s: usbvision->dev is not set " , __FUNCTION__ ) ;
return NULL ;
}
vdev = video_device_alloc ( ) ;
if ( NULL = = vdev ) {
return NULL ;
}
* vdev = * vdev_template ;
// vdev->minor = -1;
vdev - > dev = & usb_dev - > dev ;
snprintf ( vdev - > name , sizeof ( vdev - > name ) , " %s " , name ) ;
video_set_drvdata ( vdev , usbvision ) ;
return vdev ;
}
// unregister video4linux devices
static void usbvision_unregister_video ( struct usb_usbvision * usbvision )
{
// vbi Device:
if ( usbvision - > vbi ) {
PDEBUG ( DBG_PROBE , " unregister /dev/vbi%d [v4l2] " , usbvision - > vbi - > minor & 0x1f ) ;
if ( usbvision - > vbi - > minor ! = - 1 ) {
video_unregister_device ( usbvision - > vbi ) ;
}
else {
video_device_release ( usbvision - > vbi ) ;
}
usbvision - > vbi = NULL ;
}
// Radio Device:
if ( usbvision - > rdev ) {
PDEBUG ( DBG_PROBE , " unregister /dev/radio%d [v4l2] " , usbvision - > rdev - > minor & 0x1f ) ;
if ( usbvision - > rdev - > minor ! = - 1 ) {
video_unregister_device ( usbvision - > rdev ) ;
}
else {
video_device_release ( usbvision - > rdev ) ;
}
usbvision - > rdev = NULL ;
}
// Video Device:
if ( usbvision - > vdev ) {
PDEBUG ( DBG_PROBE , " unregister /dev/video%d [v4l2] " , usbvision - > vdev - > minor & 0x1f ) ;
if ( usbvision - > vdev - > minor ! = - 1 ) {
video_unregister_device ( usbvision - > vdev ) ;
}
else {
video_device_release ( usbvision - > vdev ) ;
}
usbvision - > vdev = NULL ;
}
}
// register video4linux devices
static int __devinit usbvision_register_video ( struct usb_usbvision * usbvision )
{
// Video Device:
usbvision - > vdev = usbvision_vdev_init ( usbvision , & usbvision_video_template , " USBVision Video " ) ;
if ( usbvision - > vdev = = NULL ) {
goto err_exit ;
}
if ( video_register_device ( usbvision - > vdev , VFL_TYPE_GRABBER , video_nr ) < 0 ) {
goto err_exit ;
}
info ( " USBVision[%d]: registered USBVision Video device /dev/video%d [v4l2] " , usbvision - > nr , usbvision - > vdev - > minor & 0x1f ) ;
// Radio Device:
if ( usbvision_device_data [ usbvision - > DevModel ] . Radio ) {
// usbvision has radio
usbvision - > rdev = usbvision_vdev_init ( usbvision , & usbvision_radio_template , " USBVision Radio " ) ;
if ( usbvision - > rdev = = NULL ) {
goto err_exit ;
}
if ( video_register_device ( usbvision - > rdev , VFL_TYPE_RADIO , radio_nr ) < 0 ) {
goto err_exit ;
}
info ( " USBVision[%d]: registered USBVision Radio device /dev/radio%d [v4l2] " , usbvision - > nr , usbvision - > rdev - > minor & 0x1f ) ;
}
// vbi Device:
if ( usbvision_device_data [ usbvision - > DevModel ] . vbi ) {
usbvision - > vbi = usbvision_vdev_init ( usbvision , & usbvision_vbi_template , " USBVision VBI " ) ;
if ( usbvision - > vdev = = NULL ) {
goto err_exit ;
}
if ( video_register_device ( usbvision - > vbi , VFL_TYPE_VBI , vbi_nr ) < 0 ) {
goto err_exit ;
}
info ( " USBVision[%d]: registered USBVision VBI device /dev/vbi%d [v4l2] (Not Working Yet!) " , usbvision - > nr , usbvision - > vbi - > minor & 0x1f ) ;
}
// all done
return 0 ;
err_exit :
err ( " USBVision[%d]: video_register_device() failed " , usbvision - > nr ) ;
usbvision_unregister_video ( usbvision ) ;
return - 1 ;
}
/*
* usbvision_alloc ( )
*
* This code allocates the struct usb_usbvision . It is filled with default values .
*
* Returns NULL on error , a pointer to usb_usbvision else .
*
*/
static struct usb_usbvision * usbvision_alloc ( struct usb_device * dev )
{
struct usb_usbvision * usbvision ;
int FrameIdx ;
if ( ( usbvision = kzalloc ( sizeof ( struct usb_usbvision ) , GFP_KERNEL ) ) = = NULL ) {
goto err_exit ;
}
usbvision - > dev = dev ;
for ( FrameIdx = 0 ; FrameIdx < USBVISION_NUMFRAMES ; FrameIdx + + ) {
init_waitqueue_head ( & usbvision - > frame [ FrameIdx ] . wq ) ;
}
init_waitqueue_head ( & usbvision - > overlay_frame . wq ) ;
init_MUTEX ( & usbvision - > lock ) ; /* to 1 == available */
// prepare control urb for control messages during interrupts
usbvision - > ctrlUrb = usb_alloc_urb ( USBVISION_URB_FRAMES , GFP_KERNEL ) ;
if ( usbvision - > ctrlUrb = = NULL ) {
goto err_exit ;
}
init_waitqueue_head ( & usbvision - > ctrlUrb_wq ) ;
init_MUTEX ( & usbvision - > ctrlUrbLock ) ;
init_timer ( & usbvision - > powerOffTimer ) ;
usbvision - > powerOffTimer . data = ( long ) usbvision ;
usbvision - > powerOffTimer . function = usbvision_powerOffTimer ;
return usbvision ;
err_exit :
if ( usbvision & & usbvision - > ctrlUrb ) {
usb_free_urb ( usbvision - > ctrlUrb ) ;
}
if ( usbvision ) {
kfree ( usbvision ) ;
}
return NULL ;
}
/*
* usbvision_release ( )
*
* This code does final release of struct usb_usbvision . This happens
* after the device is disconnected - and - all clients closed their files .
*
*/
static void usbvision_release ( struct usb_usbvision * usbvision )
{
PDEBUG ( DBG_PROBE , " " ) ;
down ( & usbvision - > lock ) ;
if ( timer_pending ( & usbvision - > powerOffTimer ) ) {
del_timer ( & usbvision - > powerOffTimer ) ;
}
usbvision - > usbvision_used = 0 ;
usbvision - > initialized = 0 ;
up ( & usbvision - > lock ) ;
usbvision_remove_sysfs ( usbvision - > vdev ) ;
usbvision_unregister_video ( usbvision ) ;
if ( dga ) {
if ( usbvision - > overlay_base ) {
iounmap ( usbvision - > overlay_base ) ;
}
}
if ( usbvision - > ctrlUrb ) {
usb_free_urb ( usbvision - > ctrlUrb ) ;
}
kfree ( usbvision ) ;
PDEBUG ( DBG_PROBE , " success " ) ;
}
/*
* usbvision_probe ( )
*
* This procedure queries device descriptor and accepts the interface
* if it looks like USBVISION video device
*
*/
static int __devinit usbvision_probe ( struct usb_interface * intf , const struct usb_device_id * devid )
{
struct usb_device * dev = interface_to_usbdev ( intf ) ;
__u8 ifnum = intf - > altsetting - > desc . bInterfaceNumber ;
const struct usb_host_interface * interface ;
struct usb_usbvision * usbvision = NULL ;
const struct usb_endpoint_descriptor * endpoint ;
int model ;
PDEBUG ( DBG_PROBE , " VID=%#04x, PID=%#04x, ifnum=%u " ,
dev - > descriptor . idVendor , dev - > descriptor . idProduct , ifnum ) ;
/* Is it an USBVISION video dev? */
model = 0 ;
for ( model = 0 ; usbvision_device_data [ model ] . idVendor ; model + + ) {
if ( le16_to_cpu ( dev - > descriptor . idVendor ) ! = usbvision_device_data [ model ] . idVendor ) {
continue ;
}
if ( le16_to_cpu ( dev - > descriptor . idProduct ) ! = usbvision_device_data [ model ] . idProduct ) {
continue ;
}
info ( " %s: %s found " , __FUNCTION__ , usbvision_device_data [ model ] . ModelString ) ;
break ;
}
if ( usbvision_device_data [ model ] . idVendor = = 0 ) {
return - ENODEV ; //no matching device
}
if ( usbvision_device_data [ model ] . Interface > = 0 ) {
interface = & dev - > actconfig - > interface [ usbvision_device_data [ model ] . Interface ] - > altsetting [ 0 ] ;
}
else {
interface = & dev - > actconfig - > interface [ ifnum ] - > altsetting [ 0 ] ;
}
endpoint = & interface - > endpoint [ 1 ] . desc ;
if ( ( endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) ! = USB_ENDPOINT_XFER_ISOC ) {
err ( " %s: interface %d. has non-ISO endpoint! " , __FUNCTION__ , ifnum ) ;
err ( " %s: Endpoint attribures %d " , __FUNCTION__ , endpoint - > bmAttributes ) ;
return - ENODEV ;
}
if ( ( endpoint - > bEndpointAddress & USB_ENDPOINT_DIR_MASK ) = = USB_DIR_OUT ) {
err ( " %s: interface %d. has ISO OUT endpoint! " , __FUNCTION__ , ifnum ) ;
return - ENODEV ;
}
usb_get_dev ( dev ) ;
if ( ( usbvision = usbvision_alloc ( dev ) ) = = NULL ) {
err ( " %s: couldn't allocate USBVision struct " , __FUNCTION__ ) ;
return - ENOMEM ;
}
if ( dev - > descriptor . bNumConfigurations > 1 ) {
usbvision - > bridgeType = BRIDGE_NT1004 ;
}
else if ( usbvision_device_data [ model ] . ModelString = = " Dazzle Fusion Model DVC-90 Rev 1 (SECAM) " ) {
usbvision - > bridgeType = BRIDGE_NT1005 ;
}
else {
usbvision - > bridgeType = BRIDGE_NT1003 ;
}
PDEBUG ( DBG_PROBE , " bridgeType %d " , usbvision - > bridgeType ) ;
down ( & usbvision - > lock ) ;
usbvision - > nr = usbvision_nr + + ;
usbvision - > have_tuner = usbvision_device_data [ model ] . Tuner ;
if ( usbvision - > have_tuner ) {
usbvision - > tuner_type = usbvision_device_data [ model ] . TunerType ;
}
usbvision - > DevModel = model ;
usbvision - > remove_pending = 0 ;
usbvision - > last_error = 0 ;
usbvision - > iface = ifnum ;
usbvision - > ifaceAltInactive = 0 ;
usbvision - > ifaceAltActive = 1 ;
usbvision - > video_endp = endpoint - > bEndpointAddress ;
usbvision - > isocPacketSize = 0 ;
usbvision - > usb_bandwidth = 0 ;
usbvision - > user = 0 ;
usbvision_register_video ( usbvision ) ;
usbvision_configure_video ( usbvision ) ;
up ( & usbvision - > lock ) ;
usb_set_intfdata ( intf , usbvision ) ;
usbvision_create_sysfs ( usbvision - > vdev ) ;
PDEBUG ( DBG_PROBE , " success " ) ;
return 0 ;
}
/*
* usbvision_disconnect ( )
*
* This procedure stops all driver activity , deallocates interface - private
* structure ( pointed by ' ptr ' ) and after that driver should be removable
* with no ill consequences .
*
*/
static void __devexit usbvision_disconnect ( struct usb_interface * intf )
{
struct usb_usbvision * usbvision = usb_get_intfdata ( intf ) ;
PDEBUG ( DBG_PROBE , " " ) ;
if ( usbvision = = NULL ) {
err ( " %s: usb_get_intfdata() failed " , __FUNCTION__ ) ;
return ;
}
usb_set_intfdata ( intf , NULL ) ;
down ( & usbvision - > lock ) ;
// At this time we ask to cancel outstanding URBs
usbvision_stop_isoc ( usbvision ) ;
if ( usbvision - > power ) {
usbvision_i2c_usb_del_bus ( & usbvision - > i2c_adap ) ;
usbvision_power_off ( usbvision ) ;
}
usbvision - > remove_pending = 1 ; // Now all ISO data will be ignored
usb_put_dev ( usbvision - > dev ) ;
usbvision - > dev = NULL ; // USB device is no more
up ( & usbvision - > lock ) ;
if ( usbvision - > user ) {
info ( " %s: In use, disconnect pending " , __FUNCTION__ ) ;
}
else {
usbvision_release ( usbvision ) ;
}
PDEBUG ( DBG_PROBE , " success " ) ;
}
static struct usb_driver usbvision_driver = {
. name = " usbvision " ,
. id_table = usbvision_table ,
. probe = usbvision_probe ,
. disconnect = usbvision_disconnect
} ;
/*
* customdevice_process ( )
*
* This procedure preprocesses CustomDevice parameter if any
*
*/
void customdevice_process ( void )
{
usbvision_device_data [ 0 ] = usbvision_device_data [ 1 ] ;
usbvision_table [ 0 ] = usbvision_table [ 1 ] ;
if ( CustomDevice )
{
char * parse = CustomDevice ;
PDEBUG ( DBG_PROBE , " CustomDevide=%s " , CustomDevice ) ;
/*format is CustomDevice="0x0573 0x4D31 0 7113 3 PAL 1 1 1 5 -1 -1 -1 -1 -1"
usbvision_device_data [ 0 ] . idVendor ;
usbvision_device_data [ 0 ] . idProduct ;
usbvision_device_data [ 0 ] . Interface ;
usbvision_device_data [ 0 ] . Codec ;
usbvision_device_data [ 0 ] . VideoChannels ;
usbvision_device_data [ 0 ] . VideoNorm ;
usbvision_device_data [ 0 ] . AudioChannels ;
usbvision_device_data [ 0 ] . Radio ;
usbvision_device_data [ 0 ] . Tuner ;
usbvision_device_data [ 0 ] . TunerType ;
usbvision_device_data [ 0 ] . Vin_Reg1 ;
usbvision_device_data [ 0 ] . Vin_Reg2 ;
usbvision_device_data [ 0 ] . X_Offset ;
usbvision_device_data [ 0 ] . Y_Offset ;
usbvision_device_data [ 0 ] . Dvi_yuv ;
usbvision_device_data [ 0 ] . ModelString ;
*/
rmspace ( parse ) ;
usbvision_device_data [ 0 ] . ModelString = " USBVISION Custom Device " ;
parse + = 2 ;
sscanf ( parse , " %x " , & usbvision_device_data [ 0 ] . idVendor ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " idVendor=0x%.4X " , usbvision_device_data [ 0 ] . idVendor ) ;
parse + = 2 ;
sscanf ( parse , " %x " , & usbvision_device_data [ 0 ] . idProduct ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " idProduct=0x%.4X " , usbvision_device_data [ 0 ] . idProduct ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Interface ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " Interface=%d " , usbvision_device_data [ 0 ] . Interface ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Codec ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " Codec=%d " , usbvision_device_data [ 0 ] . Codec ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . VideoChannels ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " VideoChannels=%d " , usbvision_device_data [ 0 ] . VideoChannels ) ;
switch ( * parse )
{
case ' P ' :
PDEBUG ( DBG_PROBE , " VideoNorm=PAL " ) ;
usbvision_device_data [ 0 ] . VideoNorm = VIDEO_MODE_PAL ;
break ;
case ' S ' :
PDEBUG ( DBG_PROBE , " VideoNorm=SECAM " ) ;
usbvision_device_data [ 0 ] . VideoNorm = VIDEO_MODE_SECAM ;
break ;
case ' N ' :
PDEBUG ( DBG_PROBE , " VideoNorm=NTSC " ) ;
usbvision_device_data [ 0 ] . VideoNorm = VIDEO_MODE_NTSC ;
break ;
default :
PDEBUG ( DBG_PROBE , " VideoNorm=PAL (by default) " ) ;
usbvision_device_data [ 0 ] . VideoNorm = VIDEO_MODE_PAL ;
break ;
}
goto2next ( parse ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . AudioChannels ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " AudioChannels=%d " , usbvision_device_data [ 0 ] . AudioChannels ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Radio ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " Radio=%d " , usbvision_device_data [ 0 ] . Radio ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Tuner ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " Tuner=%d " , usbvision_device_data [ 0 ] . Tuner ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . TunerType ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " TunerType=%d " , usbvision_device_data [ 0 ] . TunerType ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Vin_Reg1 ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " Vin_Reg1=%d " , usbvision_device_data [ 0 ] . Vin_Reg1 ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Vin_Reg2 ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " Vin_Reg2=%d " , usbvision_device_data [ 0 ] . Vin_Reg2 ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . X_Offset ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " X_Offset=%d " , usbvision_device_data [ 0 ] . X_Offset ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Y_Offset ) ;
goto2next ( parse ) ;
PDEBUG ( DBG_PROBE , " Y_Offset=%d " , usbvision_device_data [ 0 ] . Y_Offset ) ;
sscanf ( parse , " %d " , & usbvision_device_data [ 0 ] . Dvi_yuv ) ;
PDEBUG ( DBG_PROBE , " Dvi_yuv=%d " , usbvision_device_data [ 0 ] . Dvi_yuv ) ;
//add to usbvision_table also
usbvision_table [ 0 ] . match_flags = USB_DEVICE_ID_MATCH_DEVICE ;
usbvision_table [ 0 ] . idVendor = usbvision_device_data [ 0 ] . idVendor ;
usbvision_table [ 0 ] . idProduct = usbvision_device_data [ 0 ] . idProduct ;
}
}
/*
* usbvision_init ( )
*
* This code is run to initialize the driver .
*
*/
static int __init usbvision_init ( void )
{
int errCode ;
PDEBUG ( DBG_PROBE , " " ) ;
PDEBUG ( DBG_IOCTL , " IOCTL debugging is enabled " ) ;
PDEBUG ( DBG_IO , " IO debugging is enabled " ) ;
PDEBUG ( DBG_RIO , " RIO debugging is enabled " ) ;
PDEBUG ( DBG_HEADER , " HEADER debugging is enabled " ) ;
PDEBUG ( DBG_PROBE , " PROBE debugging is enabled " ) ;
PDEBUG ( DBG_IRQ , " IRQ debugging is enabled " ) ;
PDEBUG ( DBG_ISOC , " ISOC debugging is enabled " ) ;
PDEBUG ( DBG_PARSE , " PARSE debugging is enabled " ) ;
PDEBUG ( DBG_SCRATCH , " SCRATCH debugging is enabled " ) ;
PDEBUG ( DBG_FUNC , " FUNC debugging is enabled " ) ;
PDEBUG ( DBG_I2C , " I2C debugging is enabled " ) ;
/* disable planar mode support unless compression enabled */
if ( isocMode ! = ISOC_MODE_COMPRESS ) {
// FIXME : not the right way to set supported flag
usbvision_v4l2_format [ 6 ] . supported = 0 ; // V4L2_PIX_FMT_YVU420
usbvision_v4l2_format [ 7 ] . supported = 0 ; // V4L2_PIX_FMT_YUV422P
}
customdevice_process ( ) ;
errCode = usb_register ( & usbvision_driver ) ;
if ( errCode = = 0 ) {
info ( DRIVER_DESC " : " DRIVER_VERSION ) ;
PDEBUG ( DBG_PROBE , " success " ) ;
}
return errCode ;
}
static void __exit usbvision_exit ( void )
{
PDEBUG ( DBG_PROBE , " " ) ;
usb_deregister ( & usbvision_driver ) ;
PDEBUG ( DBG_PROBE , " success " ) ;
}
module_init ( usbvision_init ) ;
module_exit ( usbvision_exit ) ;
/*
* Overrides for Emacs so that we follow Linus ' s tabbing style .
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
* Local variables :
* c - basic - offset : 8
* End :
*/