2005-11-09 08:37:07 +03:00
/*
2005-11-09 08:38:25 +03:00
em28xx - video . c - driver for Empia EM2800 / EM2820 / 2840 USB video capture devices
2005-11-09 08:37:07 +03:00
2005-11-09 08:38:25 +03:00
Copyright ( C ) 2005 Ludovico Cavedon < cavedon @ sssup . it >
Markus Rechberger < mrechberger @ gmail . com >
2006-04-03 14:53:40 +04:00
Mauro Carvalho Chehab < mchehab @ infradead . org >
2005-11-09 08:38:25 +03:00
Sascha Sommer < saschasommer @ freenet . de >
2005-11-09 08:37:07 +03:00
2006-01-23 22:10:54 +03:00
Some parts based on SN9C10x PC Camera Controllers GPL driver made
by Luca Risolia < luca . risolia @ studio . unibo . it >
2005-11-09 08:37:07 +03:00
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 .
*/
# include <linux/init.h>
# include <linux/list.h>
# include <linux/module.h>
# include <linux/kernel.h>
2006-01-23 22:11:08 +03:00
# include <linux/bitmap.h>
2005-11-09 08:37:07 +03:00
# include <linux/usb.h>
# include <linux/i2c.h>
2005-11-09 08:38:37 +03:00
# include <linux/version.h>
2007-11-01 07:16:09 +03:00
# include <linux/mm.h>
2006-01-15 12:52:23 +03:00
# include <linux/mutex.h>
2005-11-09 08:37:07 +03:00
2005-11-09 08:38:25 +03:00
# include "em28xx.h"
2006-01-09 20:25:14 +03:00
# include <media/v4l2-common.h>
2006-03-19 18:35:57 +03:00
# include <media/msp3400.h>
V4L/DVB (7060): em28xx: remove has_tuner
has_tuner flag doesn't make much sense, since tuner_type=TUNER_ABSENT
means the same thing.
Having two ways to say that a tuner is not present is
not nice, since it may lead to bad setups. In fact, with the previous
code, if a device were using has_tuner=0, but the user forces a tuner,
with modprobe option tuner=type, the modprobe option won't work.
Also, tveeprom returns TUNER_ABSENT, when tuner is unknown or absent.
So, with the previous logic, in this case, the driver should set
has_tuner=0, or has_tuner=1 otherwise.
Instead of adding several additional tests and setups, better just to
remove .has_tuner.
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-01-24 12:59:20 +03:00
# include <media/tuner.h>
2005-11-09 08:37:07 +03:00
2005-11-09 08:38:25 +03:00
# define DRIVER_AUTHOR "Ludovico Cavedon <cavedon@sssup.it>, " \
" Markus Rechberger <mrechberger@gmail.com>, " \
2006-04-03 14:53:40 +04:00
" Mauro Carvalho Chehab <mchehab@infradead.org>, " \
2005-11-09 08:38:25 +03:00
" Sascha Sommer <saschasommer@freenet.de> "
2005-11-09 08:37:07 +03:00
2005-11-09 08:38:25 +03:00
# define DRIVER_NAME "em28xx"
# define DRIVER_DESC "Empia em28xx based USB video device driver"
2007-11-11 19:17:17 +03:00
# define EM28XX_VERSION_CODE KERNEL_VERSION(0, 1, 0)
2005-11-09 08:37:07 +03:00
2005-11-09 08:38:27 +03:00
# define em28xx_videodbg(fmt, arg...) do {\
2005-11-09 08:37:43 +03:00
if ( video_debug ) \
printk ( KERN_INFO " %s %s : " fmt , \
2005-12-19 13:53:59 +03:00
dev - > name , __FUNCTION__ , # # arg ) ; } while ( 0 )
2005-11-09 08:37:07 +03:00
MODULE_AUTHOR ( DRIVER_AUTHOR ) ;
MODULE_DESCRIPTION ( DRIVER_DESC ) ;
MODULE_LICENSE ( " GPL " ) ;
2005-11-09 08:38:27 +03:00
static LIST_HEAD ( em28xx_devlist ) ;
2005-11-09 08:37:52 +03:00
2005-11-09 08:38:52 +03:00
static unsigned int card [ ] = { [ 0 . . . ( EM28XX_MAXBOARDS - 1 ) ] = UNSET } ;
2006-01-23 22:11:08 +03:00
static unsigned int video_nr [ ] = { [ 0 . . . ( EM28XX_MAXBOARDS - 1 ) ] = UNSET } ;
2008-01-05 23:22:01 +03:00
static unsigned int vbi_nr [ ] = { [ 0 . . . ( EM28XX_MAXBOARDS - 1 ) ] = UNSET } ;
static unsigned int radio_nr [ ] = { [ 0 . . . ( EM28XX_MAXBOARDS - 1 ) ] = UNSET } ;
2005-11-09 08:37:24 +03:00
module_param_array ( card , int , NULL , 0444 ) ;
2006-01-23 22:11:08 +03:00
module_param_array ( video_nr , int , NULL , 0444 ) ;
module_param_array ( vbi_nr , int , NULL , 0444 ) ;
2008-01-05 23:22:01 +03:00
module_param_array ( radio_nr , int , NULL , 0444 ) ;
MODULE_PARM_DESC ( card , " card type " ) ;
MODULE_PARM_DESC ( video_nr , " video device numbers " ) ;
MODULE_PARM_DESC ( vbi_nr , " vbi device numbers " ) ;
MODULE_PARM_DESC ( radio_nr , " radio device numbers " ) ;
2005-11-09 08:37:24 +03:00
2005-11-09 08:37:07 +03:00
static unsigned int video_debug = 0 ;
module_param ( video_debug , int , 0644 ) ;
MODULE_PARM_DESC ( video_debug , " enable debug messages [video] " ) ;
2006-01-23 22:11:08 +03:00
/* Bitmask marking allocated devices from 0 to EM28XX_MAXBOARDS */
static unsigned long em28xx_devused ;
2005-11-09 08:37:07 +03:00
/* supported controls */
2006-01-09 20:25:14 +03:00
/* Common to all boards */
2005-11-09 08:38:27 +03:00
static struct v4l2_queryctrl em28xx_qctrl [ ] = {
2006-01-09 20:25:14 +03:00
{
. id = V4L2_CID_AUDIO_VOLUME ,
. type = V4L2_CTRL_TYPE_INTEGER ,
. name = " Volume " ,
. minimum = 0x0 ,
. maximum = 0x1f ,
. step = 0x1 ,
. default_value = 0x1f ,
. flags = 0 ,
} , {
. id = V4L2_CID_AUDIO_MUTE ,
. type = V4L2_CTRL_TYPE_BOOLEAN ,
. name = " Mute " ,
. minimum = 0 ,
. maximum = 1 ,
. step = 1 ,
. default_value = 1 ,
. flags = 0 ,
}
} ;
2005-11-09 08:38:27 +03:00
static struct usb_driver em28xx_usb_driver ;
2005-11-09 08:37:07 +03:00
/********************* v4l2 interface ******************************************/
/*
2005-11-09 08:38:27 +03:00
* em28xx_config ( )
2005-11-09 08:37:07 +03:00
* inits registers with sane defaults
*/
2005-11-09 08:38:27 +03:00
static int em28xx_config ( struct em28xx * dev )
2005-11-09 08:37:07 +03:00
{
/* Sets I2C speed to 100 KHz */
2007-11-03 22:48:01 +03:00
if ( ! dev - > is_em2800 )
em28xx_write_regs_req ( dev , 0x00 , 0x06 , " \x40 " , 1 ) ;
2005-11-09 08:37:07 +03:00
/* enable vbi capturing */
2006-01-23 22:11:08 +03:00
2006-02-27 06:07:34 +03:00
/* em28xx_write_regs_req(dev,0x00,0x0e,"\xC0",1); audio register */
/* em28xx_write_regs_req(dev,0x00,0x0f,"\x80",1); clk register */
2006-01-23 22:11:08 +03:00
em28xx_write_regs_req ( dev , 0x00 , 0x11 , " \x51 " , 1 ) ;
2005-11-09 08:37:07 +03:00
dev - > mute = 1 ; /* maybe not the right place... */
dev - > volume = 0x1f ;
2008-01-05 15:53:54 +03:00
2005-11-09 08:38:27 +03:00
em28xx_outfmt_set_yuv422 ( dev ) ;
em28xx_colorlevels_set_default ( dev ) ;
em28xx_compression_disable ( dev ) ;
2005-11-09 08:37:07 +03:00
return 0 ;
}
/*
2005-11-09 08:38:27 +03:00
* em28xx_config_i2c ( )
2005-11-09 08:37:07 +03:00
* configure i2c attached devices
*/
2005-12-01 11:51:35 +03:00
static void em28xx_config_i2c ( struct em28xx * dev )
2005-11-09 08:37:07 +03:00
{
2006-04-02 20:35:00 +04:00
struct v4l2_routing route ;
route . input = INPUT ( dev - > ctl_input ) - > vmux ;
route . output = 0 ;
2006-10-11 01:48:37 +04:00
em28xx_i2c_call_clients ( dev , VIDIOC_INT_RESET , NULL ) ;
2006-04-02 20:35:00 +04:00
em28xx_i2c_call_clients ( dev , VIDIOC_INT_S_VIDEO_ROUTING , & route ) ;
2006-03-13 19:31:31 +03:00
em28xx_i2c_call_clients ( dev , VIDIOC_STREAMON , NULL ) ;
2005-11-09 08:37:07 +03:00
}
/*
2005-11-09 08:38:27 +03:00
* em28xx_empty_framequeues ( )
2005-11-09 08:37:07 +03:00
* prepare queues for incoming and outgoing frames
*/
2005-11-09 08:38:27 +03:00
static void em28xx_empty_framequeues ( struct em28xx * dev )
2005-11-09 08:37:07 +03:00
{
u32 i ;
INIT_LIST_HEAD ( & dev - > inqueue ) ;
INIT_LIST_HEAD ( & dev - > outqueue ) ;
2005-11-09 08:38:27 +03:00
for ( i = 0 ; i < EM28XX_NUM_FRAMES ; i + + ) {
2005-11-09 08:37:07 +03:00
dev - > frame [ i ] . state = F_UNUSED ;
dev - > frame [ i ] . buf . bytesused = 0 ;
}
}
2005-11-09 08:38:43 +03:00
static void video_mux ( struct em28xx * dev , int index )
{
2006-04-02 20:35:00 +04:00
struct v4l2_routing route ;
2005-11-09 08:38:43 +03:00
2006-04-02 20:35:00 +04:00
route . input = INPUT ( index ) - > vmux ;
route . output = 0 ;
2005-11-09 08:38:43 +03:00
dev - > ctl_input = index ;
dev - > ctl_ainput = INPUT ( index ) - > amux ;
2006-04-02 20:35:00 +04:00
em28xx_i2c_call_clients ( dev , VIDIOC_INT_S_VIDEO_ROUTING , & route ) ;
2005-11-09 08:38:43 +03:00
if ( dev - > has_msp34xx ) {
2006-01-09 20:25:37 +03:00
if ( dev - > i2s_speed )
em28xx_i2c_call_clients ( dev , VIDIOC_INT_I2S_CLOCK_FREQ , & dev - > i2s_speed ) ;
2006-03-19 18:35:57 +03:00
route . input = dev - > ctl_ainput ;
2006-04-02 01:03:23 +04:00
route . output = MSP_OUTPUT ( MSP_SC_IN_DSP_SCART1 ) ;
2006-03-19 18:35:57 +03:00
/* Note: this is msp3400 specific */
em28xx_i2c_call_clients ( dev , VIDIOC_INT_S_AUDIO_ROUTING , & route ) ;
2005-11-09 08:38:43 +03:00
}
2008-01-05 15:53:54 +03:00
2008-02-07 00:34:13 +03:00
em28xx_audio_analog_set ( dev ) ;
2005-11-09 08:38:43 +03:00
}
2007-11-11 07:08:26 +03:00
/* Usage lock check functions */
static int res_get ( struct em28xx_fh * fh )
{
struct em28xx * dev = fh - > dev ;
int rc = 0 ;
/* This instance already has stream_on */
if ( fh - > stream_on )
return rc ;
mutex_lock ( & dev - > lock ) ;
if ( dev - > stream_on )
rc = - EINVAL ;
else {
dev - > stream_on = 1 ;
fh - > stream_on = 1 ;
}
mutex_unlock ( & dev - > lock ) ;
return rc ;
}
static int res_check ( struct em28xx_fh * fh )
{
return ( fh - > stream_on ) ;
}
static void res_free ( struct em28xx_fh * fh )
{
struct em28xx * dev = fh - > dev ;
mutex_lock ( & dev - > lock ) ;
fh - > stream_on = 0 ;
dev - > stream_on = 0 ;
mutex_unlock ( & dev - > lock ) ;
}
2005-11-09 08:37:07 +03:00
/*
2007-11-11 19:17:17 +03:00
* em28xx_vm_open ( )
2005-11-09 08:37:07 +03:00
*/
2007-11-11 19:17:17 +03:00
static void em28xx_vm_open ( struct vm_area_struct * vma )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_frame_t * f = vma - > vm_private_data ;
f - > vma_use_count + + ;
}
2005-11-09 08:37:52 +03:00
2007-11-11 19:17:17 +03:00
/*
* em28xx_vm_close ( )
*/
static void em28xx_vm_close ( struct vm_area_struct * vma )
{
/* NOTE: buffers are not freed here */
struct em28xx_frame_t * f = vma - > vm_private_data ;
2005-11-09 08:37:52 +03:00
2007-11-11 19:17:17 +03:00
if ( f - > vma_use_count )
f - > vma_use_count - - ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
static struct vm_operations_struct em28xx_vm_ops = {
. open = em28xx_vm_open ,
. close = em28xx_vm_close ,
} ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/*
* em28xx_get_ctrl ( )
* return the current saturation , brightness or contrast , mute state
*/
static int em28xx_get_ctrl ( struct em28xx * dev , struct v4l2_control * ctrl )
{
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
ctrl - > value = dev - > mute ;
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
ctrl - > value = dev - > volume ;
return 0 ;
default :
return - EINVAL ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/*
* em28xx_set_ctrl ( )
* mute or set new saturation , brightness or contrast
*/
static int em28xx_set_ctrl ( struct em28xx * dev , const struct v4l2_control * ctrl )
{
switch ( ctrl - > id ) {
case V4L2_CID_AUDIO_MUTE :
if ( ctrl - > value ! = dev - > mute ) {
dev - > mute = ctrl - > value ;
return em28xx_audio_analog_set ( dev ) ;
}
return 0 ;
case V4L2_CID_AUDIO_VOLUME :
dev - > volume = ctrl - > value ;
return em28xx_audio_analog_set ( dev ) ;
default :
return - EINVAL ;
}
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/*
* em28xx_stream_interrupt ( )
* stops streaming
*/
static int em28xx_stream_interrupt ( struct em28xx * dev )
{
int rc = 0 ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* stop reading from the device */
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
dev - > stream = STREAM_INTERRUPT ;
rc = wait_event_timeout ( dev - > wait_stream ,
( dev - > stream = = STREAM_OFF ) | |
( dev - > state & DEV_DISCONNECTED ) ,
EM28XX_URB_TIMEOUT ) ;
2006-02-07 11:49:13 +03:00
2007-11-11 19:17:17 +03:00
if ( rc ) {
dev - > state | = DEV_MISCONFIGURED ;
em28xx_videodbg ( " device is misconfigured; close and "
" open /dev/video%d again \n " ,
dev - > vdev - > minor - MINOR_VFL_TYPE_GRABBER_MIN ) ;
return rc ;
}
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
return 0 ;
}
static int check_dev ( struct em28xx * dev )
{
if ( dev - > state & DEV_DISCONNECTED ) {
em28xx_errdev ( " v4l2 ioctl: device not present \n " ) ;
return - ENODEV ;
2006-01-23 22:11:08 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > state & DEV_MISCONFIGURED ) {
em28xx_errdev ( " v4l2 ioctl: device is misconfigured; "
" close and open it again \n " ) ;
return - EIO ;
}
return 0 ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
static void get_scale ( struct em28xx * dev ,
unsigned int width , unsigned int height ,
unsigned int * hscale , unsigned int * vscale )
{
unsigned int maxw = norm_maxw ( dev ) ;
unsigned int maxh = norm_maxh ( dev ) ;
* hscale = ( ( ( unsigned long ) maxw ) < < 12 ) / width - 4096L ;
if ( * hscale > = 0x4000 )
* hscale = 0x3fff ;
* vscale = ( ( ( unsigned long ) maxh ) < < 12 ) / height - 4096L ;
if ( * vscale > = 0x4000 )
* vscale = 0x3fff ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
/* ------------------------------------------------------------------
IOCTL vidioc handling
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
static int vidioc_g_fmt_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
f - > fmt . pix . width = dev - > width ;
f - > fmt . pix . height = dev - > height ;
f - > fmt . pix . pixelformat = V4L2_PIX_FMT_YUYV ;
f - > fmt . pix . bytesperline = dev - > bytesperline ;
f - > fmt . pix . sizeimage = dev - > frame_size ;
f - > fmt . pix . colorspace = V4L2_COLORSPACE_SMPTE170M ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
/* FIXME: TOP? NONE? BOTTOM? ALTENATE? */
f - > fmt . pix . field = dev - > interlaced ?
V4L2_FIELD_INTERLACED : V4L2_FIELD_TOP ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
return 0 ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
static int vidioc_try_fmt_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int width = f - > fmt . pix . width ;
int height = f - > fmt . pix . height ;
unsigned int maxw = norm_maxw ( dev ) ;
unsigned int maxh = norm_maxh ( dev ) ;
unsigned int hscale , vscale ;
/* width must even because of the YUYV format
height must be even because of interlacing */
height & = 0xfffe ;
width & = 0xfffe ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( height < 32 )
height = 32 ;
if ( height > maxh )
height = maxh ;
if ( width < 48 )
width = 48 ;
if ( width > maxw )
width = maxw ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2007-11-11 07:08:26 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > is_em2800 ) {
/* the em2800 can only scale down to 50% */
if ( height % ( maxh / 2 ) )
height = maxh ;
if ( width % ( maxw / 2 ) )
width = maxw ;
/* according to empiatech support */
/* the MaxPacketSize is to small to support */
/* framesizes larger than 640x480 @ 30 fps */
/* or 640x576 @ 25 fps. As this would cut */
/* of a part of the image we prefer */
/* 360x576 or 360x480 for now */
if ( width = = maxw & & height = = maxh )
width / = 2 ;
}
2007-11-11 07:08:26 +03:00
2007-11-11 19:17:17 +03:00
get_scale ( dev , width , height , & hscale , & vscale ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
width = ( ( ( unsigned long ) maxw ) < < 12 ) / ( hscale + 4096L ) ;
height = ( ( ( unsigned long ) maxh ) < < 12 ) / ( vscale + 4096L ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
f - > fmt . pix . width = width ;
f - > fmt . pix . height = height ;
f - > fmt . pix . pixelformat = V4L2_PIX_FMT_YUYV ;
f - > fmt . pix . bytesperline = width * 2 ;
f - > fmt . pix . sizeimage = width * 2 * height ;
f - > fmt . pix . colorspace = V4L2_COLORSPACE_SMPTE170M ;
f - > fmt . pix . field = V4L2_FIELD_INTERLACED ;
2005-11-09 08:37:07 +03:00
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
return 0 ;
}
2007-11-11 19:17:17 +03:00
static int vidioc_s_fmt_cap ( struct file * file , void * priv ,
struct v4l2_format * f )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc , i ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
2007-11-11 07:08:26 +03:00
2007-11-11 19:17:17 +03:00
vidioc_try_fmt_cap ( file , priv , f ) ;
2007-11-11 07:08:26 +03:00
2007-11-04 03:22:38 +03:00
mutex_lock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
for ( i = 0 ; i < dev - > num_frames ; i + + )
if ( dev - > frame [ i ] . vma_use_count ) {
em28xx_videodbg ( " VIDIOC_S_FMT failed. "
" Unmap the buffers first. \n " ) ;
rc = - EINVAL ;
goto err ;
2006-01-23 22:11:08 +03:00
}
2007-11-11 19:17:17 +03:00
/* stop io in case it is already in progress */
if ( dev - > stream = = STREAM_ON ) {
em28xx_videodbg ( " VIDIOC_SET_FMT: interrupting stream \n " ) ;
rc = em28xx_stream_interrupt ( dev ) ;
if ( rc < 0 )
goto err ;
2006-01-23 22:11:08 +03:00
}
2007-11-11 19:17:17 +03:00
em28xx_release_buffers ( dev ) ;
dev - > io = IO_NONE ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* set new image size */
dev - > width = f - > fmt . pix . width ;
dev - > height = f - > fmt . pix . height ;
dev - > frame_size = dev - > width * dev - > height * 2 ;
dev - > field_size = dev - > frame_size > > 1 ;
dev - > bytesperline = dev - > width * 2 ;
get_scale ( dev , dev - > width , dev - > height , & dev - > hscale , & dev - > vscale ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* FIXME: This is really weird! Why capture is starting with
this ioctl ? ? ?
*/
em28xx_uninit_isoc ( dev ) ;
em28xx_set_alternate ( dev ) ;
em28xx_capture_start ( dev , 1 ) ;
em28xx_resolution_set ( dev ) ;
em28xx_init_isoc ( dev ) ;
rc = 0 ;
err :
mutex_unlock ( & dev - > lock ) ;
return rc ;
}
static int vidioc_s_std ( struct file * file , void * priv , v4l2_std_id * norm )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
struct v4l2_format f ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
mutex_lock ( & dev - > lock ) ;
2007-11-11 20:15:34 +03:00
dev - > norm = * norm ;
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* Adjusts width/height, if needed */
f . fmt . pix . width = dev - > width ;
f . fmt . pix . height = dev - > height ;
vidioc_try_fmt_cap ( file , priv , & f ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* set new image size */
dev - > width = f . fmt . pix . width ;
dev - > height = f . fmt . pix . height ;
dev - > frame_size = dev - > width * dev - > height * 2 ;
dev - > field_size = dev - > frame_size > > 1 ;
dev - > bytesperline = dev - > width * 2 ;
get_scale ( dev , dev - > width , dev - > height , & dev - > hscale , & dev - > vscale ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
em28xx_resolution_set ( dev ) ;
2007-11-11 20:15:34 +03:00
em28xx_i2c_call_clients ( dev , VIDIOC_S_STD , & dev - > norm ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
return 0 ;
}
2007-11-11 07:13:49 +03:00
2007-11-11 19:17:17 +03:00
static const char * iname [ ] = {
[ EM28XX_VMUX_COMPOSITE1 ] = " Composite1 " ,
[ EM28XX_VMUX_COMPOSITE2 ] = " Composite2 " ,
[ EM28XX_VMUX_COMPOSITE3 ] = " Composite3 " ,
[ EM28XX_VMUX_COMPOSITE4 ] = " Composite4 " ,
[ EM28XX_VMUX_SVIDEO ] = " S-Video " ,
[ EM28XX_VMUX_TELEVISION ] = " Television " ,
[ EM28XX_VMUX_CABLE ] = " Cable TV " ,
[ EM28XX_VMUX_DVB ] = " DVB " ,
[ EM28XX_VMUX_DEBUG ] = " for debug only " ,
} ;
2007-11-11 07:13:49 +03:00
2007-11-11 19:17:17 +03:00
static int vidioc_enum_input ( struct file * file , void * priv ,
struct v4l2_input * i )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
unsigned int n ;
2007-11-11 07:13:49 +03:00
2007-11-11 19:17:17 +03:00
n = i - > index ;
if ( n > = MAX_EM28XX_INPUT )
return - EINVAL ;
if ( 0 = = INPUT ( n ) - > type )
return - EINVAL ;
2007-11-11 07:13:49 +03:00
2007-11-11 19:17:17 +03:00
i - > index = n ;
i - > type = V4L2_INPUT_TYPE_CAMERA ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
strcpy ( i - > name , iname [ INPUT ( n ) - > type ] ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( ( EM28XX_VMUX_TELEVISION = = INPUT ( n ) - > type ) | |
( EM28XX_VMUX_CABLE = = INPUT ( n ) - > type ) )
i - > type = V4L2_INPUT_TYPE_TUNER ;
2007-11-11 20:15:34 +03:00
i - > std = dev - > vdev - > tvnorms ;
2007-11-11 19:17:17 +03:00
return 0 ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
static int vidioc_g_input ( struct file * file , void * priv , unsigned int * i )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
* i = dev - > ctl_input ;
return 0 ;
}
static int vidioc_s_input ( struct file * file , void * priv , unsigned int i )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
if ( i > = MAX_EM28XX_INPUT )
return - EINVAL ;
if ( 0 = = INPUT ( i ) - > type )
return - EINVAL ;
2007-11-11 07:08:26 +03:00
2007-11-04 03:22:38 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
video_mux ( dev , i ) ;
mutex_unlock ( & dev - > lock ) ;
return 0 ;
}
static int vidioc_g_audio ( struct file * file , void * priv , struct v4l2_audio * a )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
unsigned int index = a - > index ;
if ( a - > index > 1 )
return - EINVAL ;
index = dev - > ctl_ainput ;
if ( index = = 0 ) {
strcpy ( a - > name , " Television " ) ;
2005-11-09 08:37:07 +03:00
} else {
2007-11-11 19:17:17 +03:00
strcpy ( a - > name , " Line In " ) ;
}
a - > capability = V4L2_AUDCAP_STEREO ;
a - > index = index ;
return 0 ;
}
static int vidioc_s_audio ( struct file * file , void * priv , struct v4l2_audio * a )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
if ( a - > index ! = dev - > ctl_ainput )
return - EINVAL ;
return 0 ;
}
static int vidioc_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int id = qc - > id ;
int i ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
memset ( qc , 0 , sizeof ( * qc ) ) ;
qc - > id = id ;
if ( ! dev - > has_msp34xx ) {
for ( i = 0 ; i < ARRAY_SIZE ( em28xx_qctrl ) ; i + + ) {
if ( qc - > id & & qc - > id = = em28xx_qctrl [ i ] . id ) {
memcpy ( qc , & ( em28xx_qctrl [ i ] ) , sizeof ( * qc ) ) ;
return 0 ;
2005-11-09 08:37:07 +03:00
}
}
2007-11-11 19:17:17 +03:00
}
mutex_lock ( & dev - > lock ) ;
em28xx_i2c_call_clients ( dev , VIDIOC_QUERYCTRL , qc ) ;
mutex_unlock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( qc - > type )
return 0 ;
else
return - EINVAL ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
static int vidioc_g_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( ! dev - > has_msp34xx )
rc = em28xx_get_ctrl ( dev , ctrl ) ;
else
rc = - EINVAL ;
if ( rc = = - EINVAL ) {
em28xx_i2c_call_clients ( dev , VIDIOC_G_CTRL , ctrl ) ;
rc = 0 ;
}
mutex_unlock ( & dev - > lock ) ;
return rc ;
}
static int vidioc_s_ctrl ( struct file * file , void * priv ,
struct v4l2_control * ctrl )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
u8 i ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
mutex_lock ( & dev - > lock ) ;
if ( dev - > has_msp34xx )
em28xx_i2c_call_clients ( dev , VIDIOC_S_CTRL , ctrl ) ;
else {
rc = 1 ;
for ( i = 0 ; i < ARRAY_SIZE ( em28xx_qctrl ) ; i + + ) {
if ( ctrl - > id = = em28xx_qctrl [ i ] . id ) {
if ( ctrl - > value < em28xx_qctrl [ i ] . minimum | |
ctrl - > value > em28xx_qctrl [ i ] . maximum ) {
rc = - ERANGE ;
break ;
}
rc = em28xx_set_ctrl ( dev , ctrl ) ;
break ;
}
2005-11-09 08:37:07 +03:00
}
}
2007-11-11 19:17:17 +03:00
/* Control not found - try to send it to the attached devices */
if ( rc = = 1 ) {
em28xx_i2c_call_clients ( dev , VIDIOC_S_CTRL , ctrl ) ;
rc = 0 ;
}
2007-11-04 03:22:38 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return rc ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
static int vidioc_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * t )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
if ( 0 ! = t - > index )
return - EINVAL ;
strcpy ( t - > name , " Tuner " ) ;
mutex_lock ( & dev - > lock ) ;
em28xx_i2c_call_clients ( dev , VIDIOC_G_TUNER , t ) ;
mutex_unlock ( & dev - > lock ) ;
return 0 ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
static int vidioc_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * t )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
2007-11-10 16:26:20 +03:00
2007-11-11 19:17:17 +03:00
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
if ( 0 ! = t - > index )
return - EINVAL ;
mutex_lock ( & dev - > lock ) ;
em28xx_i2c_call_clients ( dev , VIDIOC_S_TUNER , t ) ;
mutex_unlock ( & dev - > lock ) ;
return 0 ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
static int vidioc_g_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
2005-11-09 08:37:07 +03:00
2008-01-05 23:22:01 +03:00
f - > type = fh - > radio ? V4L2_TUNER_RADIO : V4L2_TUNER_ANALOG_TV ;
2007-11-11 19:17:17 +03:00
f - > frequency = dev - > ctl_freq ;
return 0 ;
}
static int vidioc_s_frequency ( struct file * file , void * priv ,
struct v4l2_frequency * f )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
2005-11-09 08:37:52 +03:00
2007-11-11 19:17:17 +03:00
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
if ( 0 ! = f - > tuner )
return - EINVAL ;
2008-01-05 23:22:01 +03:00
if ( unlikely ( 0 = = fh - > radio & & f - > type ! = V4L2_TUNER_ANALOG_TV ) )
return - EINVAL ;
if ( unlikely ( 1 = = fh - > radio & & f - > type ! = V4L2_TUNER_RADIO ) )
2007-11-11 19:17:17 +03:00
return - EINVAL ;
2007-11-11 07:08:26 +03:00
mutex_lock ( & dev - > lock ) ;
2007-11-11 04:21:01 +03:00
2007-11-11 19:17:17 +03:00
dev - > ctl_freq = f - > frequency ;
em28xx_i2c_call_clients ( dev , VIDIOC_S_FREQUENCY , f ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
return 0 ;
}
2005-11-09 08:37:07 +03:00
2008-02-06 15:00:41 +03:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
static int em28xx_reg_len ( int reg )
{
switch ( reg ) {
case AC97LSB_REG :
case HSCALELOW_REG :
case VSCALELOW_REG :
return 2 ;
default :
return 1 ;
}
}
static int vidioc_g_register ( struct file * file , void * priv ,
struct v4l2_register * reg )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int ret ;
if ( ! v4l2_chip_match_host ( reg - > match_type , reg - > match_chip ) )
return - EINVAL ;
if ( em28xx_reg_len ( reg - > reg ) = = 1 ) {
ret = em28xx_read_reg ( dev , reg - > reg ) ;
if ( ret < 0 )
return ret ;
reg - > val = ret ;
} else {
2008-02-06 21:56:16 +03:00
u64 val = 0 ;
2008-02-06 15:00:41 +03:00
ret = em28xx_read_reg_req_len ( dev , USB_REQ_GET_STATUS ,
reg - > reg , ( char * ) & val , 2 ) ;
if ( ret < 0 )
return ret ;
2008-02-06 21:56:16 +03:00
reg - > val = cpu_to_le64 ( ( __u64 ) val ) ;
2008-02-06 15:00:41 +03:00
}
return 0 ;
}
static int vidioc_s_register ( struct file * file , void * priv ,
struct v4l2_register * reg )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
2008-02-06 21:56:16 +03:00
u64 buf ;
2008-02-06 15:00:41 +03:00
2008-02-06 21:56:16 +03:00
buf = le64_to_cpu ( ( __u64 ) reg - > val ) ;
2008-02-06 15:00:41 +03:00
return em28xx_write_regs ( dev , reg - > reg , ( char * ) & buf ,
em28xx_reg_len ( reg - > reg ) ) ;
}
# endif
2007-11-11 19:17:17 +03:00
static int vidioc_cropcap ( struct file * file , void * priv ,
struct v4l2_cropcap * cc )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
if ( cc - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE )
2005-11-09 08:37:07 +03:00
return - EINVAL ;
2007-11-11 19:17:17 +03:00
cc - > bounds . left = 0 ;
cc - > bounds . top = 0 ;
cc - > bounds . width = dev - > width ;
cc - > bounds . height = dev - > height ;
cc - > defrect = cc - > bounds ;
cc - > pixelaspect . numerator = 54 ; /* 4:3 FIXME: remove magic numbers */
cc - > pixelaspect . denominator = 59 ;
return 0 ;
}
static int vidioc_streamon ( struct file * file , void * priv ,
enum v4l2_buf_type type )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
if ( type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE | | dev - > io ! = IO_MMAP )
2005-11-09 08:37:07 +03:00
return - EINVAL ;
2007-11-11 19:17:17 +03:00
if ( list_empty ( & dev - > inqueue ) )
return - EINVAL ;
mutex_lock ( & dev - > lock ) ;
if ( unlikely ( res_get ( fh ) < 0 ) ) {
mutex_unlock ( & dev - > lock ) ;
return - EBUSY ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
dev - > stream = STREAM_ON ; /* FIXME: Start video capture here? */
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
return 0 ;
}
static int vidioc_streamoff ( struct file * file , void * priv ,
enum v4l2_buf_type type )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
if ( type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE | | dev - > io ! = IO_MMAP )
return - EINVAL ;
mutex_lock ( & dev - > lock ) ;
if ( dev - > stream = = STREAM_ON ) {
em28xx_videodbg ( " VIDIOC_STREAMOFF: interrupting stream \n " ) ;
rc = em28xx_stream_interrupt ( dev ) ;
if ( rc < 0 ) {
2007-11-04 03:22:38 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return rc ;
2005-11-09 08:37:07 +03:00
}
}
2007-11-11 19:17:17 +03:00
em28xx_empty_framequeues ( dev ) ;
2005-11-09 08:37:07 +03:00
2007-11-04 03:22:38 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
return 0 ;
}
2007-11-11 19:17:17 +03:00
static int vidioc_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
strlcpy ( cap - > driver , " em28xx " , sizeof ( cap - > driver ) ) ;
strlcpy ( cap - > card , em28xx_boards [ dev - > model ] . name , sizeof ( cap - > card ) ) ;
strlcpy ( cap - > bus_info , dev - > udev - > dev . bus_id , sizeof ( cap - > bus_info ) ) ;
cap - > version = EM28XX_VERSION_CODE ;
cap - > capabilities =
V4L2_CAP_SLICED_VBI_CAPTURE |
V4L2_CAP_VIDEO_CAPTURE |
V4L2_CAP_AUDIO |
V4L2_CAP_READWRITE | V4L2_CAP_STREAMING ;
V4L/DVB (7060): em28xx: remove has_tuner
has_tuner flag doesn't make much sense, since tuner_type=TUNER_ABSENT
means the same thing.
Having two ways to say that a tuner is not present is
not nice, since it may lead to bad setups. In fact, with the previous
code, if a device were using has_tuner=0, but the user forces a tuner,
with modprobe option tuner=type, the modprobe option won't work.
Also, tveeprom returns TUNER_ABSENT, when tuner is unknown or absent.
So, with the previous logic, in this case, the driver should set
has_tuner=0, or has_tuner=1 otherwise.
Instead of adding several additional tests and setups, better just to
remove .has_tuner.
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-01-24 12:59:20 +03:00
if ( dev - > tuner_type ! = TUNER_ABSENT )
2007-11-11 19:17:17 +03:00
cap - > capabilities | = V4L2_CAP_TUNER ;
return 0 ;
2006-01-09 20:25:14 +03:00
}
2007-11-11 19:17:17 +03:00
static int vidioc_enum_fmt_cap ( struct file * file , void * priv ,
struct v4l2_fmtdesc * fmtd )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
if ( fmtd - > index ! = 0 )
2006-01-09 20:25:14 +03:00
return - EINVAL ;
2007-11-11 19:17:17 +03:00
fmtd - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
strcpy ( fmtd - > description , " Packed YUY2 " ) ;
fmtd - > pixelformat = V4L2_PIX_FMT_YUYV ;
memset ( fmtd - > reserved , 0 , sizeof ( fmtd - > reserved ) ) ;
return 0 ;
2006-01-09 20:25:14 +03:00
}
2007-11-11 19:17:17 +03:00
/* Sliced VBI ioctls */
static int vidioc_g_fmt_vbi_capture ( struct file * file , void * priv ,
struct v4l2_format * f )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
f - > fmt . sliced . service_set = 0 ;
em28xx_i2c_call_clients ( dev , VIDIOC_G_FMT , f ) ;
if ( f - > fmt . sliced . service_set = = 0 )
rc = - EINVAL ;
mutex_unlock ( & dev - > lock ) ;
return rc ;
}
static int vidioc_try_set_vbi_capture ( struct file * file , void * priv ,
struct v4l2_format * f )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
mutex_lock ( & dev - > lock ) ;
em28xx_i2c_call_clients ( dev , VIDIOC_G_FMT , f ) ;
mutex_unlock ( & dev - > lock ) ;
if ( f - > fmt . sliced . service_set = = 0 )
return - EINVAL ;
2005-11-09 08:37:07 +03:00
return 0 ;
}
2007-11-11 19:17:17 +03:00
static int vidioc_reqbufs ( struct file * file , void * priv ,
struct v4l2_requestbuffers * rb )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
u32 i ;
int rc ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( rb - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE | |
rb - > memory ! = V4L2_MEMORY_MMAP )
return - EINVAL ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > io = = IO_READ ) {
em28xx_videodbg ( " method is set to read; "
" close and open the device again to "
" choose the mmap I/O method \n " ) ;
return - EINVAL ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
for ( i = 0 ; i < dev - > num_frames ; i + + )
if ( dev - > frame [ i ] . vma_use_count ) {
em28xx_videodbg ( " VIDIOC_REQBUFS failed; "
" previous buffers are still mapped \n " ) ;
return - EINVAL ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > stream = = STREAM_ON ) {
em28xx_videodbg ( " VIDIOC_REQBUFS: interrupting stream \n " ) ;
rc = em28xx_stream_interrupt ( dev ) ;
if ( rc < 0 ) {
mutex_unlock ( & dev - > lock ) ;
return rc ;
}
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
em28xx_empty_framequeues ( dev ) ;
em28xx_release_buffers ( dev ) ;
if ( rb - > count )
rb - > count = em28xx_request_buffers ( dev , rb - > count ) ;
dev - > frame_current = NULL ;
dev - > io = rb - > count ? IO_MMAP : IO_NONE ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
return 0 ;
}
2007-11-11 19:17:17 +03:00
static int vidioc_querybuf ( struct file * file , void * priv ,
struct v4l2_buffer * b )
2006-01-23 22:11:08 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
if ( b - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE | |
b - > index > = dev - > num_frames | | dev - > io ! = IO_MMAP )
return - EINVAL ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
memcpy ( b , & dev - > frame [ b - > index ] . buf , sizeof ( * b ) ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > frame [ b - > index ] . vma_use_count )
b - > flags | = V4L2_BUF_FLAG_MAPPED ;
if ( dev - > frame [ b - > index ] . state = = F_DONE )
b - > flags | = V4L2_BUF_FLAG_DONE ;
else if ( dev - > frame [ b - > index ] . state ! = F_UNUSED )
b - > flags | = V4L2_BUF_FLAG_QUEUED ;
mutex_unlock ( & dev - > lock ) ;
return 0 ;
}
static int vidioc_qbuf ( struct file * file , void * priv , struct v4l2_buffer * b )
{
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
unsigned long lock_flags ;
int rc ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
if ( b - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE | | dev - > io ! = IO_MMAP | |
b - > index > = dev - > num_frames )
2006-01-23 22:11:08 +03:00
return - EINVAL ;
2007-11-11 19:17:17 +03:00
if ( dev - > frame [ b - > index ] . state ! = F_UNUSED )
return - EAGAIN ;
dev - > frame [ b - > index ] . state = F_QUEUED ;
/* add frame to fifo */
spin_lock_irqsave ( & dev - > queue_lock , lock_flags ) ;
list_add_tail ( & dev - > frame [ b - > index ] . frame , & dev - > inqueue ) ;
spin_unlock_irqrestore ( & dev - > queue_lock , lock_flags ) ;
return 0 ;
2006-01-23 22:11:08 +03:00
}
2007-11-11 19:17:17 +03:00
static int vidioc_dqbuf ( struct file * file , void * priv , struct v4l2_buffer * b )
2006-01-23 22:11:08 +03:00
{
2007-11-11 19:17:17 +03:00
struct em28xx_fh * fh = priv ;
struct em28xx * dev = fh - > dev ;
int rc ;
struct em28xx_frame_t * f ;
unsigned long lock_flags ;
rc = check_dev ( dev ) ;
if ( rc < 0 )
return rc ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
if ( b - > type ! = V4L2_BUF_TYPE_VIDEO_CAPTURE | | dev - > io ! = IO_MMAP )
return - EINVAL ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
if ( list_empty ( & dev - > outqueue ) ) {
if ( dev - > stream = = STREAM_OFF )
return - EINVAL ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
if ( file - > f_flags & O_NONBLOCK )
return - EAGAIN ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
rc = wait_event_interruptible ( dev - > wait_frame ,
( ! list_empty ( & dev - > outqueue ) ) | |
( dev - > state & DEV_DISCONNECTED ) ) ;
if ( rc )
return rc ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > state & DEV_DISCONNECTED )
return - ENODEV ;
2006-01-23 22:11:08 +03:00
}
2007-11-11 19:17:17 +03:00
spin_lock_irqsave ( & dev - > queue_lock , lock_flags ) ;
f = list_entry ( dev - > outqueue . next , struct em28xx_frame_t , frame ) ;
list_del ( dev - > outqueue . next ) ;
spin_unlock_irqrestore ( & dev - > queue_lock , lock_flags ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
f - > state = F_UNUSED ;
memcpy ( b , & f - > buf , sizeof ( * b ) ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
if ( f - > vma_use_count )
b - > flags | = V4L2_BUF_FLAG_MAPPED ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
return 0 ;
}
2006-01-23 22:11:08 +03:00
2008-01-05 23:22:01 +03:00
/* ----------------------------------------------------------- */
/* RADIO ESPECIFIC IOCTLS */
/* ----------------------------------------------------------- */
static int radio_querycap ( struct file * file , void * priv ,
struct v4l2_capability * cap )
{
struct em28xx * dev = ( ( struct em28xx_fh * ) priv ) - > dev ;
strlcpy ( cap - > driver , " em28xx " , sizeof ( cap - > driver ) ) ;
strlcpy ( cap - > card , em28xx_boards [ dev - > model ] . name , sizeof ( cap - > card ) ) ;
strlcpy ( cap - > bus_info , dev - > udev - > dev . bus_id , sizeof ( cap - > bus_info ) ) ;
cap - > version = EM28XX_VERSION_CODE ;
cap - > capabilities = V4L2_CAP_TUNER ;
return 0 ;
}
static int radio_g_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * t )
{
struct em28xx * dev = ( ( struct em28xx_fh * ) priv ) - > dev ;
if ( unlikely ( t - > index > 0 ) )
return - EINVAL ;
strcpy ( t - > name , " Radio " ) ;
t - > type = V4L2_TUNER_RADIO ;
em28xx_i2c_call_clients ( dev , VIDIOC_G_TUNER , t ) ;
return 0 ;
}
static int radio_enum_input ( struct file * file , void * priv ,
struct v4l2_input * i )
{
if ( i - > index ! = 0 )
return - EINVAL ;
strcpy ( i - > name , " Radio " ) ;
i - > type = V4L2_INPUT_TYPE_TUNER ;
return 0 ;
}
static int radio_g_audio ( struct file * file , void * priv , struct v4l2_audio * a )
{
if ( unlikely ( a - > index ) )
return - EINVAL ;
strcpy ( a - > name , " Radio " ) ;
return 0 ;
}
static int radio_s_tuner ( struct file * file , void * priv ,
struct v4l2_tuner * t )
{
struct em28xx * dev = ( ( struct em28xx_fh * ) priv ) - > dev ;
if ( 0 ! = t - > index )
return - EINVAL ;
em28xx_i2c_call_clients ( dev , VIDIOC_S_TUNER , t ) ;
return 0 ;
}
static int radio_s_audio ( struct file * file , void * fh ,
struct v4l2_audio * a )
{
return 0 ;
}
static int radio_s_input ( struct file * file , void * fh , unsigned int i )
{
return 0 ;
}
static int radio_queryctrl ( struct file * file , void * priv ,
struct v4l2_queryctrl * qc )
{
int i ;
if ( qc - > id < V4L2_CID_BASE | |
qc - > id > = V4L2_CID_LASTP1 )
return - EINVAL ;
for ( i = 0 ; i < ARRAY_SIZE ( em28xx_qctrl ) ; i + + ) {
if ( qc - > id & & qc - > id = = em28xx_qctrl [ i ] . id ) {
memcpy ( qc , & ( em28xx_qctrl [ i ] ) , sizeof ( * qc ) ) ;
return 0 ;
}
}
return - EINVAL ;
}
2007-11-11 19:17:17 +03:00
/*
* em28xx_v4l2_open ( )
* inits the device and starts isoc transfer
*/
static int em28xx_v4l2_open ( struct inode * inode , struct file * filp )
{
int minor = iminor ( inode ) ;
2008-01-05 23:22:01 +03:00
int errCode = 0 , radio = 0 ;
2007-11-11 19:17:17 +03:00
struct em28xx * h , * dev = NULL ;
struct em28xx_fh * fh ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
list_for_each_entry ( h , & em28xx_devlist , devlist ) {
if ( h - > vdev - > minor = = minor ) {
dev = h ;
dev - > type = V4L2_BUF_TYPE_VIDEO_CAPTURE ;
}
if ( h - > vbi_dev - > minor = = minor ) {
dev = h ;
dev - > type = V4L2_BUF_TYPE_VBI_CAPTURE ;
}
2008-01-05 23:22:01 +03:00
if ( h - > radio_dev & &
h - > radio_dev - > minor = = minor ) {
radio = 1 ;
dev = h ;
}
2006-01-23 22:11:08 +03:00
}
2007-11-11 19:17:17 +03:00
if ( NULL = = dev )
return - ENODEV ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
em28xx_videodbg ( " open minor=%d type=%s users=%d \n " ,
minor , v4l2_type_names [ dev - > type ] , dev - > users ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
fh = kzalloc ( sizeof ( struct em28xx_fh ) , GFP_KERNEL ) ;
2006-01-23 22:11:09 +03:00
2007-11-11 19:17:17 +03:00
if ( ! fh ) {
em28xx_errdev ( " em28xx-video.c: Out of memory?! \n " ) ;
return - ENOMEM ;
}
mutex_lock ( & dev - > lock ) ;
fh - > dev = dev ;
2008-01-05 23:22:01 +03:00
fh - > radio = radio ;
2007-11-11 19:17:17 +03:00
filp - > private_data = fh ;
2006-01-23 22:11:09 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > type = = V4L2_BUF_TYPE_VIDEO_CAPTURE & & dev - > users = = 0 ) {
em28xx_set_alternate ( dev ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
dev - > width = norm_maxw ( dev ) ;
dev - > height = norm_maxh ( dev ) ;
dev - > frame_size = dev - > width * dev - > height * 2 ;
dev - > field_size = dev - > frame_size > > 1 ; /*both_fileds ? dev->frame_size>>1 : dev->frame_size; */
dev - > bytesperline = dev - > width * 2 ;
dev - > hscale = 0 ;
dev - > vscale = 0 ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
em28xx_capture_start ( dev , 1 ) ;
em28xx_resolution_set ( dev ) ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
/* start the transfer */
errCode = em28xx_init_isoc ( dev ) ;
if ( errCode )
goto err ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
em28xx_empty_framequeues ( dev ) ;
2006-01-23 22:11:08 +03:00
}
2008-01-05 23:22:01 +03:00
if ( fh - > radio ) {
em28xx_videodbg ( " video_open: setting radio device \n " ) ;
em28xx_i2c_call_clients ( dev , AUDC_SET_RADIO , NULL ) ;
}
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
dev - > users + + ;
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
err :
mutex_unlock ( & dev - > lock ) ;
return errCode ;
2006-01-23 22:11:08 +03:00
}
2006-01-23 22:11:08 +03:00
2005-11-09 08:37:07 +03:00
/*
2007-11-11 19:17:17 +03:00
* em28xx_realease_resources ( )
* unregisters the v4l2 , i2c and usb devices
* called when the device gets disconected or at module unload
*/
static void em28xx_release_resources ( struct em28xx * dev )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
/*FIXME: I2C IR should be disconnected */
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
em28xx_info ( " V4L2 devices /dev/video%d and /dev/vbi%d deregistered \n " ,
dev - > vdev - > minor - MINOR_VFL_TYPE_GRABBER_MIN ,
dev - > vbi_dev - > minor - MINOR_VFL_TYPE_VBI_MIN ) ;
list_del ( & dev - > devlist ) ;
2008-01-05 23:22:01 +03:00
if ( dev - > radio_dev ) {
if ( - 1 ! = dev - > radio_dev - > minor )
video_unregister_device ( dev - > radio_dev ) ;
else
video_device_release ( dev - > radio_dev ) ;
dev - > radio_dev = NULL ;
}
if ( dev - > vbi_dev ) {
if ( - 1 ! = dev - > vbi_dev - > minor )
video_unregister_device ( dev - > vbi_dev ) ;
else
video_device_release ( dev - > vbi_dev ) ;
dev - > vbi_dev = NULL ;
}
if ( dev - > vdev ) {
if ( - 1 ! = dev - > vdev - > minor )
video_unregister_device ( dev - > vdev ) ;
else
video_device_release ( dev - > vdev ) ;
dev - > vdev = NULL ;
}
2007-11-11 19:17:17 +03:00
em28xx_i2c_unregister ( dev ) ;
usb_put_dev ( dev - > udev ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* Mark device as unused */
em28xx_devused & = ~ ( 1 < < dev - > devno ) ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/*
* em28xx_v4l2_close ( )
* stops streaming and deallocates all resources allocated by the v4l2 calls and ioctls
*/
static int em28xx_v4l2_close ( struct inode * inode , struct file * filp )
{
struct em28xx_fh * fh = filp - > private_data ;
struct em28xx * dev = fh - > dev ;
int errCode ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
em28xx_videodbg ( " users=%d \n " , dev - > users ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( res_check ( fh ) )
res_free ( fh ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > users = = 1 ) {
em28xx_uninit_isoc ( dev ) ;
em28xx_release_buffers ( dev ) ;
dev - > io = IO_NONE ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* the device is already disconnect,
free the remaining resources */
if ( dev - > state & DEV_DISCONNECTED ) {
em28xx_release_resources ( dev ) ;
mutex_unlock ( & dev - > lock ) ;
kfree ( dev ) ;
return 0 ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* set alternate 0 */
dev - > alt = 0 ;
em28xx_videodbg ( " setting alternate 0 \n " ) ;
errCode = usb_set_interface ( dev - > udev , 0 , 0 ) ;
if ( errCode < 0 ) {
em28xx_errdev ( " cannot change alternate number to "
" 0 (error=%i) \n " , errCode ) ;
}
2006-01-23 22:11:09 +03:00
}
2007-11-11 19:17:17 +03:00
kfree ( fh ) ;
dev - > users - - ;
wake_up_interruptible_nr ( & dev - > open , 1 ) ;
mutex_unlock ( & dev - > lock ) ;
return 0 ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/*
* em28xx_v4l2_read ( )
* will allocate buffers when called for the first time
*/
static ssize_t
em28xx_v4l2_read ( struct file * filp , char __user * buf , size_t count ,
loff_t * f_pos )
{
struct em28xx_frame_t * f , * i ;
unsigned long lock_flags ;
int ret = 0 ;
struct em28xx_fh * fh = filp - > private_data ;
struct em28xx * dev = fh - > dev ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/* FIXME: read() is not prepared to allow changing the video
resolution while streaming . Seems a bug at em28xx_set_fmt
*/
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( unlikely ( res_get ( fh ) < 0 ) )
return - EBUSY ;
2006-01-23 22:11:09 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > type = = V4L2_BUF_TYPE_VIDEO_CAPTURE )
em28xx_videodbg ( " V4l2_Buf_type_videocapture is set \n " ) ;
if ( dev - > type = = V4L2_BUF_TYPE_VBI_CAPTURE ) {
em28xx_videodbg ( " V4L2_BUF_TYPE_VBI_CAPTURE is set \n " ) ;
em28xx_videodbg ( " not supported yet! ... \n " ) ;
if ( copy_to_user ( buf , " " , 1 ) ) {
mutex_unlock ( & dev - > lock ) ;
return - EFAULT ;
2006-01-23 22:11:09 +03:00
}
2007-11-04 03:22:38 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return ( 1 ) ;
2006-01-23 22:11:09 +03:00
}
2007-11-11 19:17:17 +03:00
if ( dev - > type = = V4L2_BUF_TYPE_SLICED_VBI_CAPTURE ) {
em28xx_videodbg ( " V4L2_BUF_TYPE_SLICED_VBI_CAPTURE is set \n " ) ;
em28xx_videodbg ( " not supported yet! ... \n " ) ;
if ( copy_to_user ( buf , " " , 1 ) ) {
2007-11-04 03:22:38 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return - EFAULT ;
2006-01-23 22:11:09 +03:00
}
2007-11-04 03:22:38 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return ( 1 ) ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > state & DEV_DISCONNECTED ) {
em28xx_videodbg ( " device not present \n " ) ;
mutex_unlock ( & dev - > lock ) ;
return - ENODEV ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > state & DEV_MISCONFIGURED ) {
em28xx_videodbg ( " device misconfigured; close and open it again \n " ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return - EIO ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > io = = IO_MMAP ) {
em28xx_videodbg ( " IO method is set to mmap; close and open "
" the device again to choose the read method \n " ) ;
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return - EINVAL ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > io = = IO_NONE ) {
if ( ! em28xx_request_buffers ( dev , EM28XX_NUM_READ_FRAMES ) ) {
em28xx_errdev ( " read failed, not enough memory \n " ) ;
mutex_unlock ( & dev - > lock ) ;
return - ENOMEM ;
}
dev - > io = IO_READ ;
dev - > stream = STREAM_ON ;
em28xx_queue_unusedframes ( dev ) ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( ! count ) {
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2006-01-23 22:11:09 +03:00
return 0 ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( list_empty ( & dev - > outqueue ) ) {
if ( filp - > f_flags & O_NONBLOCK ) {
mutex_unlock ( & dev - > lock ) ;
return - EAGAIN ;
}
ret = wait_event_interruptible
( dev - > wait_frame ,
( ! list_empty ( & dev - > outqueue ) ) | |
( dev - > state & DEV_DISCONNECTED ) ) ;
if ( ret ) {
mutex_unlock ( & dev - > lock ) ;
return ret ;
}
if ( dev - > state & DEV_DISCONNECTED ) {
mutex_unlock ( & dev - > lock ) ;
return - ENODEV ;
}
dev - > video_bytesread = 0 ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
f = list_entry ( dev - > outqueue . prev , struct em28xx_frame_t , frame ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
em28xx_queue_unusedframes ( dev ) ;
2007-11-11 07:08:26 +03:00
2007-11-11 19:17:17 +03:00
if ( count > f - > buf . length )
count = f - > buf . length ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( ( dev - > video_bytesread + count ) > dev - > frame_size )
count = dev - > frame_size - dev - > video_bytesread ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( copy_to_user ( buf , f - > bufmem + dev - > video_bytesread , count ) ) {
em28xx_err ( " Error while copying to user \n " ) ;
return - EFAULT ;
2006-01-23 22:11:09 +03:00
}
2007-11-11 19:17:17 +03:00
dev - > video_bytesread + = count ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > video_bytesread = = dev - > frame_size ) {
spin_lock_irqsave ( & dev - > queue_lock , lock_flags ) ;
list_for_each_entry ( i , & dev - > outqueue , frame )
i - > state = F_UNUSED ;
INIT_LIST_HEAD ( & dev - > outqueue ) ;
spin_unlock_irqrestore ( & dev - > queue_lock , lock_flags ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
em28xx_queue_unusedframes ( dev ) ;
dev - > video_bytesread = 0 ;
}
2007-11-11 04:21:01 +03:00
2007-11-11 19:17:17 +03:00
* f_pos + = count ;
2006-01-23 22:11:09 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
return count ;
2005-11-09 08:37:07 +03:00
}
/*
2007-11-11 19:17:17 +03:00
* em28xx_v4l2_poll ( )
* will allocate buffers when called for the first time
2005-11-09 08:37:07 +03:00
*/
2007-11-11 19:17:17 +03:00
static unsigned int em28xx_v4l2_poll ( struct file * filp , poll_table * wait )
2005-11-09 08:37:07 +03:00
{
2007-11-11 19:17:17 +03:00
unsigned int mask = 0 ;
2007-11-11 04:21:01 +03:00
struct em28xx_fh * fh = filp - > private_data ;
2007-11-11 19:17:17 +03:00
struct em28xx * dev = fh - > dev ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( unlikely ( res_get ( fh ) < 0 ) )
return POLLERR ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > state & DEV_DISCONNECTED ) {
em28xx_videodbg ( " device not present \n " ) ;
} else if ( dev - > state & DEV_MISCONFIGURED ) {
em28xx_videodbg ( " device is misconfigured; close and open it again \n " ) ;
} else {
if ( dev - > io = = IO_NONE ) {
if ( ! em28xx_request_buffers
( dev , EM28XX_NUM_READ_FRAMES ) ) {
em28xx_warn
( " poll() failed, not enough memory \n " ) ;
} else {
dev - > io = IO_READ ;
dev - > stream = STREAM_ON ;
}
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > io = = IO_READ ) {
em28xx_queue_unusedframes ( dev ) ;
poll_wait ( filp , & dev - > wait_frame , wait ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( ! list_empty ( & dev - > outqueue ) )
mask | = POLLIN | POLLRDNORM ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
return mask ;
2006-01-23 22:11:09 +03:00
}
2007-11-11 19:17:17 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_unlock ( & dev - > lock ) ;
return POLLERR ;
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
/*
* em28xx_v4l2_mmap ( )
*/
static int em28xx_v4l2_mmap ( struct file * filp , struct vm_area_struct * vma )
{
struct em28xx_fh * fh = filp - > private_data ;
struct em28xx * dev = fh - > dev ;
unsigned long size = vma - > vm_end - vma - > vm_start ;
unsigned long start = vma - > vm_start ;
void * pos ;
u32 i ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( unlikely ( res_get ( fh ) < 0 ) )
return - EBUSY ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > state & DEV_DISCONNECTED ) {
em28xx_videodbg ( " mmap: device not present \n " ) ;
2007-11-04 03:22:38 +03:00
mutex_unlock ( & dev - > lock ) ;
2007-11-11 19:17:17 +03:00
return - ENODEV ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > state & DEV_MISCONFIGURED ) {
em28xx_videodbg ( " mmap: Device is misconfigured; close and "
" open it again \n " ) ;
mutex_unlock ( & dev - > lock ) ;
return - EIO ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( dev - > io ! = IO_MMAP | | ! ( vma - > vm_flags & VM_WRITE ) ) {
mutex_unlock ( & dev - > lock ) ;
return - EINVAL ;
2006-01-23 22:11:09 +03:00
}
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
if ( size > PAGE_ALIGN ( dev - > frame [ 0 ] . buf . length ) )
size = PAGE_ALIGN ( dev - > frame [ 0 ] . buf . length ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
for ( i = 0 ; i < dev - > num_frames ; i + + ) {
if ( ( dev - > frame [ i ] . buf . m . offset > > PAGE_SHIFT ) = = vma - > vm_pgoff )
break ;
2006-01-23 22:11:09 +03:00
}
2007-11-11 19:17:17 +03:00
if ( i = = dev - > num_frames ) {
em28xx_videodbg ( " mmap: user supplied mapping address is out of range \n " ) ;
mutex_unlock ( & dev - > lock ) ;
return - EINVAL ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
/* VM_IO is eventually going to replace PageReserved altogether */
vma - > vm_flags | = VM_IO ;
vma - > vm_flags | = VM_RESERVED ; /* avoid to swap out this VMA */
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
pos = dev - > frame [ i ] . bufmem ;
while ( size > 0 ) { /* size is page-aligned */
if ( vm_insert_page ( vma , start , vmalloc_to_page ( pos ) ) ) {
em28xx_videodbg ( " mmap: vm_insert_page failed \n " ) ;
mutex_unlock ( & dev - > lock ) ;
return - EAGAIN ;
}
start + = PAGE_SIZE ;
pos + = PAGE_SIZE ;
size - = PAGE_SIZE ;
2005-11-09 08:37:07 +03:00
}
2007-11-11 19:17:17 +03:00
vma - > vm_ops = & em28xx_vm_ops ;
vma - > vm_private_data = & dev - > frame [ i ] ;
2005-11-09 08:37:07 +03:00
2007-11-11 19:17:17 +03:00
em28xx_vm_open ( vma ) ;
mutex_unlock ( & dev - > lock ) ;
return 0 ;
2005-11-09 08:37:07 +03:00
}
2007-02-12 11:55:33 +03:00
static const struct file_operations em28xx_v4l_fops = {
2007-11-11 19:17:17 +03:00
. owner = THIS_MODULE ,
. open = em28xx_v4l2_open ,
. release = em28xx_v4l2_close ,
. read = em28xx_v4l2_read ,
. poll = em28xx_v4l2_poll ,
. mmap = em28xx_v4l2_mmap ,
. ioctl = video_ioctl2 ,
. llseek = no_llseek ,
. compat_ioctl = v4l_compat_ioctl32 ,
} ;
2006-01-09 20:24:58 +03:00
2008-01-05 23:22:01 +03:00
static const struct file_operations radio_fops = {
. owner = THIS_MODULE ,
. open = em28xx_v4l2_open ,
. release = em28xx_v4l2_close ,
. ioctl = video_ioctl2 ,
. compat_ioctl = v4l_compat_ioctl32 ,
. llseek = no_llseek ,
} ;
2007-11-11 19:17:17 +03:00
static const struct video_device em28xx_video_template = {
. fops = & em28xx_v4l_fops ,
. release = video_device_release ,
. minor = - 1 ,
. vidioc_querycap = vidioc_querycap ,
. vidioc_enum_fmt_cap = vidioc_enum_fmt_cap ,
. vidioc_g_fmt_cap = vidioc_g_fmt_cap ,
. vidioc_try_fmt_cap = vidioc_try_fmt_cap ,
. vidioc_s_fmt_cap = vidioc_s_fmt_cap ,
. vidioc_g_audio = vidioc_g_audio ,
. vidioc_s_audio = vidioc_s_audio ,
. vidioc_cropcap = vidioc_cropcap ,
. vidioc_g_fmt_vbi_capture = vidioc_g_fmt_vbi_capture ,
. vidioc_try_fmt_vbi_capture = vidioc_try_set_vbi_capture ,
. vidioc_s_fmt_vbi_capture = vidioc_try_set_vbi_capture ,
. vidioc_reqbufs = vidioc_reqbufs ,
. vidioc_querybuf = vidioc_querybuf ,
. vidioc_qbuf = vidioc_qbuf ,
. vidioc_dqbuf = vidioc_dqbuf ,
. vidioc_s_std = vidioc_s_std ,
. vidioc_enum_input = vidioc_enum_input ,
. vidioc_g_input = vidioc_g_input ,
. vidioc_s_input = vidioc_s_input ,
. vidioc_queryctrl = vidioc_queryctrl ,
. vidioc_g_ctrl = vidioc_g_ctrl ,
. vidioc_s_ctrl = vidioc_s_ctrl ,
. vidioc_streamon = vidioc_streamon ,
. vidioc_streamoff = vidioc_streamoff ,
. vidioc_g_tuner = vidioc_g_tuner ,
. vidioc_s_tuner = vidioc_s_tuner ,
. vidioc_g_frequency = vidioc_g_frequency ,
. vidioc_s_frequency = vidioc_s_frequency ,
2008-02-06 15:00:41 +03:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
. vidioc_g_register = vidioc_g_register ,
. vidioc_s_register = vidioc_s_register ,
# endif
2007-11-11 19:17:17 +03:00
. tvnorms = V4L2_STD_ALL ,
2007-11-11 20:15:34 +03:00
. current_norm = V4L2_STD_PAL ,
2005-11-09 08:37:07 +03:00
} ;
2008-01-05 23:22:01 +03:00
static struct video_device em28xx_radio_template = {
. name = " em28xx-radio " ,
. type = VID_TYPE_TUNER ,
. fops = & radio_fops ,
. minor = - 1 ,
. vidioc_querycap = radio_querycap ,
. vidioc_g_tuner = radio_g_tuner ,
. vidioc_enum_input = radio_enum_input ,
. vidioc_g_audio = radio_g_audio ,
. vidioc_s_tuner = radio_s_tuner ,
. vidioc_s_audio = radio_s_audio ,
. vidioc_s_input = radio_s_input ,
. vidioc_queryctrl = radio_queryctrl ,
. vidioc_g_ctrl = vidioc_g_ctrl ,
. vidioc_s_ctrl = vidioc_s_ctrl ,
. vidioc_g_frequency = vidioc_g_frequency ,
. vidioc_s_frequency = vidioc_s_frequency ,
2008-02-06 15:00:41 +03:00
# ifdef CONFIG_VIDEO_ADV_DEBUG
. vidioc_g_register = vidioc_g_register ,
. vidioc_s_register = vidioc_s_register ,
# endif
2008-01-05 23:22:01 +03:00
} ;
2005-11-09 08:37:07 +03:00
/******************************** usb interface *****************************************/
2008-01-05 15:57:31 +03:00
static LIST_HEAD ( em28xx_extension_devlist ) ;
static DEFINE_MUTEX ( em28xx_extension_devlist_lock ) ;
int em28xx_register_extension ( struct em28xx_ops * ops )
{
struct em28xx * h , * dev = NULL ;
list_for_each_entry ( h , & em28xx_devlist , devlist )
dev = h ;
mutex_lock ( & em28xx_extension_devlist_lock ) ;
list_add_tail ( & ops - > next , & em28xx_extension_devlist ) ;
if ( dev )
ops - > init ( dev ) ;
printk ( KERN_INFO " Em28xx: Initialized (%s) extension \n " , ops - > name ) ;
mutex_unlock ( & em28xx_extension_devlist_lock ) ;
return 0 ;
}
EXPORT_SYMBOL ( em28xx_register_extension ) ;
void em28xx_unregister_extension ( struct em28xx_ops * ops )
{
struct em28xx * h , * dev = NULL ;
list_for_each_entry ( h , & em28xx_devlist , devlist )
dev = h ;
if ( dev )
ops - > fini ( dev ) ;
mutex_lock ( & em28xx_extension_devlist_lock ) ;
printk ( KERN_INFO " Em28xx: Removed (%s) extension \n " , ops - > name ) ;
list_del ( & ops - > next ) ;
mutex_unlock ( & em28xx_extension_devlist_lock ) ;
}
EXPORT_SYMBOL ( em28xx_unregister_extension ) ;
2008-01-29 04:10:48 +03:00
static struct video_device * em28xx_vdev_init ( struct em28xx * dev ,
const struct video_device * template ,
const int type ,
const char * type_name )
2008-01-05 23:22:01 +03:00
{
struct video_device * vfd ;
vfd = video_device_alloc ( ) ;
if ( NULL = = vfd )
return NULL ;
* vfd = * template ;
vfd - > minor = - 1 ;
vfd - > dev = & dev - > udev - > dev ;
vfd - > release = video_device_release ;
vfd - > type = type ;
snprintf ( vfd - > name , sizeof ( vfd - > name ) , " %s %s " ,
dev - > name , type_name ) ;
return vfd ;
}
2005-11-09 08:37:07 +03:00
/*
2005-11-09 08:38:27 +03:00
* em28xx_init_dev ( )
2005-11-09 08:37:07 +03:00
* allocates and inits the device structs , registers i2c bus and v4l device
*/
2005-11-09 08:38:27 +03:00
static int em28xx_init_dev ( struct em28xx * * devhandle , struct usb_device * udev ,
2007-11-04 03:20:59 +03:00
int minor )
2005-11-09 08:37:07 +03:00
{
2008-01-05 15:57:31 +03:00
struct em28xx_ops * ops = NULL ;
2005-11-09 08:38:27 +03:00
struct em28xx * dev = * devhandle ;
2005-11-09 08:37:07 +03:00
int retval = - ENOMEM ;
2007-11-11 20:15:34 +03:00
int errCode ;
2005-11-09 08:37:07 +03:00
unsigned int maxh , maxw ;
dev - > udev = udev ;
2006-02-07 11:49:14 +03:00
mutex_init ( & dev - > lock ) ;
2007-11-11 04:21:01 +03:00
spin_lock_init ( & dev - > queue_lock ) ;
2005-11-09 08:37:07 +03:00
init_waitqueue_head ( & dev - > open ) ;
2007-11-11 04:21:01 +03:00
init_waitqueue_head ( & dev - > wait_frame ) ;
init_waitqueue_head ( & dev - > wait_stream ) ;
2005-11-09 08:37:07 +03:00
2005-11-09 08:38:27 +03:00
dev - > em28xx_write_regs = em28xx_write_regs ;
dev - > em28xx_read_reg = em28xx_read_reg ;
dev - > em28xx_read_reg_req_len = em28xx_read_reg_req_len ;
dev - > em28xx_write_regs_req = em28xx_write_regs_req ;
dev - > em28xx_read_reg_req = em28xx_read_reg_req ;
2007-11-04 14:06:48 +03:00
dev - > is_em2800 = em28xx_boards [ dev - > model ] . is_em2800 ;
2005-11-09 08:37:07 +03:00
2008-01-05 15:59:03 +03:00
errCode = em28xx_read_reg ( dev , CHIPID_REG ) ;
if ( errCode > = 0 )
em28xx_info ( " em28xx chip ID = %d \n " , errCode ) ;
2006-01-23 22:11:10 +03:00
em28xx_pre_card_setup ( dev ) ;
2007-11-04 03:20:59 +03:00
2005-11-09 08:38:27 +03:00
errCode = em28xx_config ( dev ) ;
2005-11-09 08:37:07 +03:00
if ( errCode ) {
2005-11-09 08:38:27 +03:00
em28xx_errdev ( " error configuring device \n " ) ;
2007-11-04 03:20:59 +03:00
em28xx_devused & = ~ ( 1 < < dev - > devno ) ;
2006-04-12 01:19:33 +04:00
kfree ( dev ) ;
2005-11-09 08:37:07 +03:00
return - ENOMEM ;
}
/* register i2c bus */
2005-11-09 08:38:27 +03:00
em28xx_i2c_register ( dev ) ;
2005-11-09 08:37:07 +03:00
/* Do board specific init and eeprom reading */
2005-11-09 08:38:27 +03:00
em28xx_card_setup ( dev ) ;
2005-11-09 08:37:07 +03:00
2008-01-05 23:01:41 +03:00
/* Configure audio */
em28xx_audio_analog_set ( dev ) ;
2005-11-09 08:37:07 +03:00
/* configure the device */
2005-11-09 08:38:27 +03:00
em28xx_config_i2c ( dev ) ;
2005-11-09 08:37:07 +03:00
2007-11-11 20:15:34 +03:00
/* set default norm */
dev - > norm = em28xx_video_template . current_norm ;
2007-11-04 03:20:59 +03:00
maxw = norm_maxw ( dev ) ;
maxh = norm_maxh ( dev ) ;
/* set default image size */
dev - > width = maxw ;
dev - > height = maxh ;
dev - > interlaced = EM28XX_INTERLACED_DEFAULT ;
dev - > field_size = dev - > width * dev - > height ;
dev - > frame_size =
dev - > interlaced ? dev - > field_size < < 1 : dev - > field_size ;
dev - > bytesperline = dev - > width * 2 ;
dev - > hscale = 0 ;
dev - > vscale = 0 ;
dev - > ctl_input = 2 ;
2005-11-09 08:38:27 +03:00
errCode = em28xx_config ( dev ) ;
2005-11-09 08:37:07 +03:00
2008-01-05 23:22:01 +03:00
list_add_tail ( & dev - > devlist , & em28xx_devlist ) ;
2007-11-11 19:17:17 +03:00
/* allocate and fill video video_device struct */
2008-01-05 23:22:01 +03:00
dev - > vdev = em28xx_vdev_init ( dev , & em28xx_video_template ,
VID_TYPE_CAPTURE , " video " ) ;
2005-11-09 08:37:07 +03:00
if ( NULL = = dev - > vdev ) {
2005-11-09 08:38:27 +03:00
em28xx_errdev ( " cannot allocate video_device. \n " ) ;
2008-01-05 23:22:01 +03:00
goto fail_unreg ;
2006-01-23 22:11:08 +03:00
}
V4L/DVB (7060): em28xx: remove has_tuner
has_tuner flag doesn't make much sense, since tuner_type=TUNER_ABSENT
means the same thing.
Having two ways to say that a tuner is not present is
not nice, since it may lead to bad setups. In fact, with the previous
code, if a device were using has_tuner=0, but the user forces a tuner,
with modprobe option tuner=type, the modprobe option won't work.
Also, tveeprom returns TUNER_ABSENT, when tuner is unknown or absent.
So, with the previous logic, in this case, the driver should set
has_tuner=0, or has_tuner=1 otherwise.
Instead of adding several additional tests and setups, better just to
remove .has_tuner.
Signed-off-by: Mauro Carvalho Chehab <mchehab@infradead.org>
2008-01-24 12:59:20 +03:00
if ( dev - > tuner_type ! = TUNER_ABSENT )
2007-11-11 19:17:17 +03:00
dev - > vdev - > type | = VID_TYPE_TUNER ;
2008-01-05 23:22:01 +03:00
/* register v4l2 video video_device */
retval = video_register_device ( dev - > vdev , VFL_TYPE_GRABBER ,
video_nr [ dev - > devno ] ) ;
if ( retval ) {
em28xx_errdev ( " unable to register video device (error=%i). \n " ,
retval ) ;
goto fail_unreg ;
}
2006-01-23 22:11:08 +03:00
2007-11-11 19:17:17 +03:00
/* Allocate and fill vbi video_device struct */
2008-01-05 23:22:01 +03:00
dev - > vbi_dev = em28xx_vdev_init ( dev , & em28xx_video_template ,
VFL_TYPE_VBI , " vbi " ) ;
/* register v4l2 vbi video_device */
if ( video_register_device ( dev - > vbi_dev , VFL_TYPE_VBI ,
vbi_nr [ dev - > devno ] ) < 0 ) {
em28xx_errdev ( " unable to register vbi device \n " ) ;
retval = - ENODEV ;
goto fail_unreg ;
}
if ( em28xx_boards [ dev - > model ] . radio . type = = EM28XX_RADIO ) {
dev - > radio_dev = em28xx_vdev_init ( dev , & em28xx_radio_template ,
VFL_TYPE_RADIO , " radio " ) ;
if ( NULL = = dev - > radio_dev ) {
em28xx_errdev ( " cannot allocate video_device. \n " ) ;
goto fail_unreg ;
}
retval = video_register_device ( dev - > radio_dev , VFL_TYPE_RADIO ,
radio_nr [ dev - > devno ] ) ;
if ( retval < 0 ) {
em28xx_errdev ( " can't register radio device \n " ) ;
goto fail_unreg ;
}
em28xx_info ( " Registered radio device as /dev/radio%d \n " ,
dev - > radio_dev - > minor & 0x1f ) ;
2005-11-09 08:37:07 +03:00
}
2006-01-23 22:11:08 +03:00
2005-11-09 08:37:07 +03:00
2007-11-04 03:22:38 +03:00
if ( dev - > has_msp34xx ) {
/* Send a reset to other chips via gpio */
em28xx_write_regs_req ( dev , 0x00 , 0x08 , " \xf7 " , 1 ) ;
msleep ( 3 ) ;
em28xx_write_regs_req ( dev , 0x00 , 0x08 , " \xff " , 1 ) ;
msleep ( 3 ) ;
}
2007-11-11 19:17:17 +03:00
2007-11-04 03:22:38 +03:00
video_mux ( dev , 0 ) ;
2006-01-23 22:11:08 +03:00
em28xx_info ( " V4L2 device registered as /dev/video%d and /dev/vbi%d \n " ,
dev - > vdev - > minor - MINOR_VFL_TYPE_GRABBER_MIN ,
dev - > vbi_dev - > minor - MINOR_VFL_TYPE_VBI_MIN ) ;
2005-11-09 08:37:07 +03:00
2008-01-05 15:57:31 +03:00
mutex_lock ( & em28xx_extension_devlist_lock ) ;
if ( ! list_empty ( & em28xx_extension_devlist ) ) {
list_for_each_entry ( ops , & em28xx_extension_devlist , next ) {
if ( ops - > id )
ops - > init ( dev ) ;
}
}
mutex_unlock ( & em28xx_extension_devlist_lock ) ;
2005-11-09 08:37:07 +03:00
return 0 ;
2008-01-05 23:22:01 +03:00
fail_unreg :
em28xx_release_resources ( dev ) ;
mutex_unlock ( & dev - > lock ) ;
kfree ( dev ) ;
return retval ;
2005-11-09 08:37:07 +03:00
}
2008-01-05 15:59:03 +03:00
# if defined(CONFIG_MODULES) && defined(MODULE)
static void request_module_async ( struct work_struct * work )
{
struct em28xx * dev = container_of ( work ,
struct em28xx , request_module_wk ) ;
2008-01-06 15:54:17 +03:00
if ( dev - > has_audio_class )
request_module ( " snd-usb-audio " ) ;
else
2008-01-05 15:59:03 +03:00
request_module ( " em28xx-alsa " ) ;
}
static void request_modules ( struct em28xx * dev )
{
INIT_WORK ( & dev - > request_module_wk , request_module_async ) ;
schedule_work ( & dev - > request_module_wk ) ;
}
# else
# define request_modules(dev)
# endif /* CONFIG_MODULES */
2005-11-09 08:37:07 +03:00
/*
2005-11-09 08:38:27 +03:00
* em28xx_usb_probe ( )
2005-11-09 08:37:07 +03:00
* checks for supported devices
*/
2005-11-09 08:38:27 +03:00
static int em28xx_usb_probe ( struct usb_interface * interface ,
2005-11-09 08:37:07 +03:00
const struct usb_device_id * id )
{
const struct usb_endpoint_descriptor * endpoint ;
struct usb_device * udev ;
2005-11-09 08:38:52 +03:00
struct usb_interface * uif ;
2005-11-09 08:38:27 +03:00
struct em28xx * dev = NULL ;
2005-11-09 08:37:07 +03:00
int retval = - ENODEV ;
2007-11-04 03:20:59 +03:00
int i , nr , ifnum ;
2005-11-09 08:37:07 +03:00
udev = usb_get_dev ( interface_to_usbdev ( interface ) ) ;
2005-11-09 08:37:32 +03:00
ifnum = interface - > altsetting [ 0 ] . desc . bInterfaceNumber ;
2006-01-23 22:11:08 +03:00
/* Check to see next free device and mark as used */
nr = find_first_zero_bit ( & em28xx_devused , EM28XX_MAXBOARDS ) ;
em28xx_devused | = 1 < < nr ;
2005-11-09 08:37:07 +03:00
2005-11-09 08:37:24 +03:00
/* Don't register audio interfaces */
2005-11-09 08:38:13 +03:00
if ( interface - > altsetting [ 0 ] . desc . bInterfaceClass = = USB_CLASS_AUDIO ) {
2005-11-09 08:38:27 +03:00
em28xx_err ( DRIVER_NAME " audio device (%04x:%04x): interface %i, class %i \n " ,
2005-11-09 08:38:13 +03:00
udev - > descriptor . idVendor , udev - > descriptor . idProduct ,
ifnum ,
interface - > altsetting [ 0 ] . desc . bInterfaceClass ) ;
2006-01-23 22:11:08 +03:00
em28xx_devused & = ~ ( 1 < < nr ) ;
2005-11-09 08:37:24 +03:00
return - ENODEV ;
2005-11-09 08:38:13 +03:00
}
2005-11-09 08:38:27 +03:00
em28xx_err ( DRIVER_NAME " new video device (%04x:%04x): interface %i, class %i \n " ,
2005-11-09 08:38:13 +03:00
udev - > descriptor . idVendor , udev - > descriptor . idProduct ,
ifnum ,
interface - > altsetting [ 0 ] . desc . bInterfaceClass ) ;
2005-11-09 08:37:24 +03:00
2005-11-09 08:37:32 +03:00
endpoint = & interface - > cur_altsetting - > endpoint [ 1 ] . desc ;
2007-05-09 10:57:56 +04:00
/* check if the device has the iso in endpoint at the correct place */
2005-11-09 08:37:07 +03:00
if ( ( endpoint - > bmAttributes & USB_ENDPOINT_XFERTYPE_MASK ) ! =
USB_ENDPOINT_XFER_ISOC ) {
2005-11-09 08:38:27 +03:00
em28xx_err ( DRIVER_NAME " probing error: endpoint is non-ISO endpoint! \n " ) ;
2006-01-23 22:11:08 +03:00
em28xx_devused & = ~ ( 1 < < nr ) ;
2005-11-09 08:37:07 +03:00
return - ENODEV ;
}
if ( ( endpoint - > bEndpointAddress & USB_ENDPOINT_DIR_MASK ) = = USB_DIR_OUT ) {
2005-11-09 08:38:27 +03:00
em28xx_err ( DRIVER_NAME " probing error: endpoint is ISO OUT endpoint! \n " ) ;
2006-01-23 22:11:08 +03:00
em28xx_devused & = ~ ( 1 < < nr ) ;
2005-11-09 08:37:07 +03:00
return - ENODEV ;
}
2006-03-14 23:24:57 +03:00
if ( nr > = EM28XX_MAXBOARDS ) {
2005-11-09 08:38:52 +03:00
printk ( DRIVER_NAME " : Supports only %i em28xx boards. \n " , EM28XX_MAXBOARDS ) ;
2006-01-23 22:11:08 +03:00
em28xx_devused & = ~ ( 1 < < nr ) ;
2005-11-09 08:37:24 +03:00
return - ENOMEM ;
}
/* allocate memory for our device state and initialize it */
2006-01-12 00:40:56 +03:00
dev = kzalloc ( sizeof ( * dev ) , GFP_KERNEL ) ;
2005-11-09 08:37:24 +03:00
if ( dev = = NULL ) {
2005-11-09 08:38:27 +03:00
em28xx_err ( DRIVER_NAME " : out of memory! \n " ) ;
2006-01-23 22:11:08 +03:00
em28xx_devused & = ~ ( 1 < < nr ) ;
2005-11-09 08:37:24 +03:00
return - ENOMEM ;
}
2006-01-23 22:11:08 +03:00
snprintf ( dev - > name , 29 , " em28xx #%d " , nr ) ;
2007-11-04 03:20:59 +03:00
dev - > devno = nr ;
dev - > model = id - > driver_info ;
2006-01-23 22:11:08 +03:00
2008-01-05 15:59:03 +03:00
/* Checks if audio is provided by some interface */
for ( i = 0 ; i < udev - > config - > desc . bNumInterfaces ; i + + ) {
uif = udev - > config - > interface [ i ] ;
if ( uif - > altsetting [ 0 ] . desc . bInterfaceClass = = USB_CLASS_AUDIO ) {
dev - > has_audio_class = 1 ;
break ;
}
}
printk ( KERN_INFO DRIVER_NAME " %s usb audio class \n " ,
dev - > has_audio_class ? " Has " : " Doesn't have " ) ;
2005-11-09 08:38:52 +03:00
/* compute alternate max packet sizes */
uif = udev - > actconfig - > interface [ 0 ] ;
dev - > num_alt = uif - > num_altsetting ;
2006-01-23 22:11:08 +03:00
em28xx_info ( " Alternate settings: %i \n " , dev - > num_alt ) ;
2005-11-09 08:38:52 +03:00
// dev->alt_max_pkt_size = kmalloc(sizeof(*dev->alt_max_pkt_size)*
dev - > alt_max_pkt_size = kmalloc ( 32 *
dev - > num_alt , GFP_KERNEL ) ;
if ( dev - > alt_max_pkt_size = = NULL ) {
2006-01-23 22:11:08 +03:00
em28xx_errdev ( " out of memory! \n " ) ;
em28xx_devused & = ~ ( 1 < < nr ) ;
2007-08-10 01:02:36 +04:00
kfree ( dev ) ;
2005-11-09 08:38:52 +03:00
return - ENOMEM ;
}
for ( i = 0 ; i < dev - > num_alt ; i + + ) {
u16 tmp = le16_to_cpu ( uif - > altsetting [ i ] . endpoint [ 1 ] . desc .
wMaxPacketSize ) ;
dev - > alt_max_pkt_size [ i ] =
( tmp & 0x07ff ) * ( ( ( tmp & 0x1800 ) > > 11 ) + 1 ) ;
2006-01-23 22:11:08 +03:00
em28xx_info ( " Alternate setting %i, max size= %i \n " , i ,
2005-11-09 08:38:52 +03:00
dev - > alt_max_pkt_size [ i ] ) ;
}
2005-11-09 08:38:27 +03:00
if ( ( card [ nr ] > = 0 ) & & ( card [ nr ] < em28xx_bcount ) )
2007-11-04 03:20:59 +03:00
dev - > model = card [ nr ] ;
2005-11-09 08:37:24 +03:00
2005-11-09 08:37:07 +03:00
/* allocate device struct */
2007-11-04 03:20:59 +03:00
retval = em28xx_init_dev ( & dev , udev , nr ) ;
2005-11-09 08:37:07 +03:00
if ( retval )
return retval ;
2007-11-04 03:20:59 +03:00
em28xx_info ( " Found %s \n " , em28xx_boards [ dev - > model ] . name ) ;
2005-11-09 08:37:07 +03:00
/* save our data pointer in this interface device */
usb_set_intfdata ( interface , dev ) ;
2008-01-05 15:59:03 +03:00
request_modules ( dev ) ;
2005-11-09 08:37:07 +03:00
return 0 ;
}
/*
2005-11-09 08:38:27 +03:00
* em28xx_usb_disconnect ( )
2005-11-09 08:37:07 +03:00
* called when the device gets diconencted
* video device will be unregistered on v4l2_close in case it is still open
*/
2005-11-09 08:38:27 +03:00
static void em28xx_usb_disconnect ( struct usb_interface * interface )
2005-11-09 08:37:07 +03:00
{
2007-11-04 03:22:38 +03:00
struct em28xx * dev ;
2008-01-05 15:57:31 +03:00
struct em28xx_ops * ops = NULL ;
2007-11-04 03:22:38 +03:00
dev = usb_get_intfdata ( interface ) ;
2005-11-09 08:37:07 +03:00
usb_set_intfdata ( interface , NULL ) ;
if ( ! dev )
return ;
2007-11-04 03:22:38 +03:00
em28xx_info ( " disconnecting %s \n " , dev - > vdev - > name ) ;
2005-11-09 08:37:07 +03:00
2007-11-04 03:22:38 +03:00
/* wait until all current v4l2 io is finished then deallocate resources */
2006-02-07 11:49:14 +03:00
mutex_lock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
wake_up_interruptible_all ( & dev - > open ) ;
if ( dev - > users ) {
2005-11-09 08:38:27 +03:00
em28xx_warn
2005-11-09 08:37:07 +03:00
( " device /dev/video%d is open! Deregistration and memory "
2006-01-23 22:11:08 +03:00
" deallocation are deferred on close. \n " ,
dev - > vdev - > minor - MINOR_VFL_TYPE_GRABBER_MIN ) ;
2005-11-09 08:37:07 +03:00
dev - > state | = DEV_MISCONFIGURED ;
2005-11-09 08:38:27 +03:00
em28xx_uninit_isoc ( dev ) ;
2005-11-09 08:37:07 +03:00
dev - > state | = DEV_DISCONNECTED ;
wake_up_interruptible ( & dev - > wait_frame ) ;
wake_up_interruptible ( & dev - > wait_stream ) ;
} else {
dev - > state | = DEV_DISCONNECTED ;
2005-11-09 08:38:27 +03:00
em28xx_release_resources ( dev ) ;
2005-11-09 08:37:07 +03:00
}
2006-02-07 11:49:14 +03:00
mutex_unlock ( & dev - > lock ) ;
2005-11-09 08:37:07 +03:00
2008-01-05 15:57:31 +03:00
mutex_lock ( & em28xx_extension_devlist_lock ) ;
if ( ! list_empty ( & em28xx_extension_devlist ) ) {
list_for_each_entry ( ops , & em28xx_extension_devlist , next ) {
ops - > fini ( dev ) ;
}
}
mutex_unlock ( & em28xx_extension_devlist_lock ) ;
2005-11-09 08:38:52 +03:00
if ( ! dev - > users ) {
kfree ( dev - > alt_max_pkt_size ) ;
2005-11-09 08:37:07 +03:00
kfree ( dev ) ;
2005-11-09 08:38:52 +03:00
}
2005-11-09 08:37:07 +03:00
}
2005-11-09 08:38:27 +03:00
static struct usb_driver em28xx_usb_driver = {
. name = " em28xx " ,
. probe = em28xx_usb_probe ,
. disconnect = em28xx_usb_disconnect ,
. id_table = em28xx_id_table ,
2005-11-09 08:37:07 +03:00
} ;
2005-11-09 08:38:27 +03:00
static int __init em28xx_module_init ( void )
2005-11-09 08:37:07 +03:00
{
int result ;
printk ( KERN_INFO DRIVER_NAME " v4l2 driver version %d.%d.%d loaded \n " ,
2005-11-09 08:38:27 +03:00
( EM28XX_VERSION_CODE > > 16 ) & 0xff ,
( EM28XX_VERSION_CODE > > 8 ) & 0xff , EM28XX_VERSION_CODE & 0xff ) ;
2005-11-09 08:37:07 +03:00
# ifdef SNAPSHOT
printk ( KERN_INFO DRIVER_NAME " snapshot date %04d-%02d-%02d \n " ,
SNAPSHOT / 10000 , ( SNAPSHOT / 100 ) % 100 , SNAPSHOT % 100 ) ;
# endif
/* register this driver with the USB subsystem */
2005-11-09 08:38:27 +03:00
result = usb_register ( & em28xx_usb_driver ) ;
2005-11-09 08:37:07 +03:00
if ( result )
2005-11-09 08:38:27 +03:00
em28xx_err ( DRIVER_NAME
2005-11-09 08:37:07 +03:00
" usb_register failed. Error number %d. \n " , result ) ;
return result ;
}
2005-11-09 08:38:27 +03:00
static void __exit em28xx_module_exit ( void )
2005-11-09 08:37:07 +03:00
{
/* deregister this driver with the USB subsystem */
2005-11-09 08:38:27 +03:00
usb_deregister ( & em28xx_usb_driver ) ;
2005-11-09 08:37:07 +03:00
}
2005-11-09 08:38:27 +03:00
module_init ( em28xx_module_init ) ;
module_exit ( em28xx_module_exit ) ;