2008-04-23 08:09:12 -03:00
/*
* Pixart PAC207BCA library
*
2010-09-05 07:06:04 -03:00
* Copyright ( C ) 2008 Hans de Goede < hdegoede @ redhat . com >
2008-04-23 08:09:12 -03:00
* Copyright ( C ) 2005 Thomas Kaiser thomas @ kaiser - linux . li
* Copyleft ( C ) 2005 Michel Xhaard mxhaard @ magic . fr
*
* V4L2 by Jean - Francois Moine < http : //moinejf.free.fr>
*
* 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
2011-08-21 19:56:54 -03:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2008-04-23 08:09:12 -03:00
# define MODULE_NAME "pac207"
2010-01-29 11:02:10 -03:00
# include <linux/input.h>
2008-04-23 08:09:12 -03:00
# include "gspca.h"
2012-05-10 08:34:42 -03:00
/* Include pac common sof detection functions */
# include "pac_common.h"
2008-04-23 08:09:12 -03:00
2010-09-05 07:06:04 -03:00
MODULE_AUTHOR ( " Hans de Goede <hdegoede@redhat.com> " ) ;
2008-04-23 08:09:12 -03:00
MODULE_DESCRIPTION ( " Pixart PAC207 " ) ;
MODULE_LICENSE ( " GPL " ) ;
# define PAC207_CTRL_TIMEOUT 100 /* ms */
# define PAC207_BRIGHTNESS_MIN 0
# define PAC207_BRIGHTNESS_MAX 255
2009-07-29 08:03:17 -03:00
# define PAC207_BRIGHTNESS_DEFAULT 46
2012-05-10 08:34:42 -03:00
# define PAC207_BRIGHTNESS_REG 0x08
2009-07-29 08:03:17 -03:00
# define PAC207_EXPOSURE_MIN 3
2011-10-31 07:50:32 -03:00
# define PAC207_EXPOSURE_MAX 90 /* 1 sec expo time / 1 fps */
2009-07-29 08:03:17 -03:00
# define PAC207_EXPOSURE_DEFAULT 5 /* power on default: 3 */
2012-05-10 08:34:42 -03:00
# define PAC207_EXPOSURE_REG 0x02
2008-04-23 08:09:12 -03:00
# define PAC207_GAIN_MIN 0
# define PAC207_GAIN_MAX 31
2011-10-31 07:50:32 -03:00
# define PAC207_GAIN_DEFAULT 7 /* power on default: 9 */
2012-05-10 08:34:42 -03:00
# define PAC207_GAIN_REG 0x0e
2008-04-23 08:09:12 -03:00
# define PAC207_AUTOGAIN_DEADZONE 30
/* specific webcam descriptor */
struct sd {
struct gspca_dev gspca_dev ; /* !! must be the first item */
2012-05-10 08:34:42 -03:00
struct v4l2_ctrl * brightness ;
2008-04-23 08:09:12 -03:00
2012-05-10 08:34:42 -03:00
u8 mode ;
2008-04-23 08:09:12 -03:00
u8 sof_read ;
2008-07-04 18:29:32 -03:00
u8 header_read ;
2008-04-23 08:09:12 -03:00
u8 autogain_ignore_frames ;
atomic_t avg_lum ;
} ;
2008-12-29 07:49:41 -03:00
static const struct v4l2_pix_format sif_mode [ ] = {
2008-07-05 11:49:20 -03:00
{ 176 , 144 , V4L2_PIX_FMT_PAC207 , V4L2_FIELD_NONE ,
. bytesperline = 176 ,
. sizeimage = ( 176 + 2 ) * 144 ,
/* uncompressed, add 2 bytes / line for line header */
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = 1 } ,
{ 352 , 288 , V4L2_PIX_FMT_PAC207 , V4L2_FIELD_NONE ,
. bytesperline = 352 ,
2008-07-06 06:40:55 -03:00
/* compressed, but only when needed (not compressed
when the framerate is low ) */
. sizeimage = ( 352 + 2 ) * 288 ,
2008-07-05 11:49:20 -03:00
. colorspace = V4L2_COLORSPACE_SRGB ,
. priv = 0 } ,
2008-04-23 08:09:12 -03:00
} ;
static const __u8 pac207_sensor_init [ ] [ 8 ] = {
2009-07-29 08:03:17 -03:00
{ 0x10 , 0x12 , 0x0d , 0x12 , 0x0c , 0x01 , 0x29 , 0x84 } ,
{ 0x49 , 0x64 , 0x64 , 0x64 , 0x04 , 0x10 , 0xf0 , 0x30 } ,
2008-04-23 08:09:12 -03:00
{ 0x00 , 0x00 , 0x00 , 0x70 , 0xa0 , 0xf8 , 0x00 , 0x00 } ,
2010-10-29 13:58:22 -03:00
{ 0x32 , 0x00 , 0x96 , 0x00 , 0xa2 , 0x02 , 0xaf , 0x00 } ,
2008-04-23 08:09:12 -03:00
} ;
2012-05-09 12:13:29 -03:00
static void pac207_write_regs ( struct gspca_dev * gspca_dev , u16 index ,
2008-04-23 08:09:12 -03:00
const u8 * buffer , u16 length )
{
struct usb_device * udev = gspca_dev - > dev ;
int err ;
2012-05-09 12:13:29 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2008-07-14 09:38:29 -03:00
memcpy ( gspca_dev - > usb_buf , buffer , length ) ;
2008-04-23 08:09:12 -03:00
err = usb_control_msg ( udev , usb_sndctrlpipe ( udev , 0 ) , 0x01 ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE ,
2008-07-14 09:38:29 -03:00
0x00 , index ,
gspca_dev - > usb_buf , length , PAC207_CTRL_TIMEOUT ) ;
2012-05-09 12:13:29 -03:00
if ( err < 0 ) {
2011-08-21 19:56:54 -03:00
pr_err ( " Failed to write registers to index 0x%04X, error %d \n " ,
index , err ) ;
2012-05-09 12:13:29 -03:00
gspca_dev - > usb_err = err ;
}
2008-04-23 08:09:12 -03:00
}
2012-05-09 12:13:29 -03:00
static void pac207_write_reg ( struct gspca_dev * gspca_dev , u16 index , u16 value )
2008-04-23 08:09:12 -03:00
{
struct usb_device * udev = gspca_dev - > dev ;
int err ;
2012-05-09 12:13:29 -03:00
if ( gspca_dev - > usb_err < 0 )
return ;
2008-04-23 08:09:12 -03:00
err = usb_control_msg ( udev , usb_sndctrlpipe ( udev , 0 ) , 0x00 ,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_INTERFACE ,
value , index , NULL , 0 , PAC207_CTRL_TIMEOUT ) ;
2012-05-09 12:13:29 -03:00
if ( err ) {
2011-08-21 19:56:54 -03:00
pr_err ( " Failed to write a register (index 0x%04X, value 0x%02X, error %d) \n " ,
index , value , err ) ;
2012-05-09 12:13:29 -03:00
gspca_dev - > usb_err = err ;
}
2008-04-23 08:09:12 -03:00
}
2008-07-22 02:35:05 -03:00
static int pac207_read_reg ( struct gspca_dev * gspca_dev , u16 index )
2008-04-23 08:09:12 -03:00
{
struct usb_device * udev = gspca_dev - > dev ;
int res ;
2012-05-09 12:13:29 -03:00
if ( gspca_dev - > usb_err < 0 )
return 0 ;
2008-04-23 08:09:12 -03:00
res = usb_control_msg ( udev , usb_rcvctrlpipe ( udev , 0 ) , 0x00 ,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_INTERFACE ,
2008-07-14 09:38:29 -03:00
0x00 , index ,
gspca_dev - > usb_buf , 1 , PAC207_CTRL_TIMEOUT ) ;
2008-04-23 08:09:12 -03:00
if ( res < 0 ) {
2011-08-21 19:56:54 -03:00
pr_err ( " Failed to read a register (index 0x%04X, error %d) \n " ,
index , res ) ;
2012-05-09 12:13:29 -03:00
gspca_dev - > usb_err = res ;
return 0 ;
2008-04-23 08:09:12 -03:00
}
2008-07-14 09:38:29 -03:00
return gspca_dev - > usb_buf [ 0 ] ;
2008-04-23 08:09:12 -03:00
}
/* this function is called at probe time */
static int sd_config ( struct gspca_dev * gspca_dev ,
const struct usb_device_id * id )
{
struct cam * cam ;
u8 idreg [ 2 ] ;
idreg [ 0 ] = pac207_read_reg ( gspca_dev , 0x0000 ) ;
idreg [ 1 ] = pac207_read_reg ( gspca_dev , 0x0001 ) ;
2010-10-29 13:58:22 -03:00
idreg [ 0 ] = ( ( idreg [ 0 ] & 0x0f ) < < 4 ) | ( ( idreg [ 1 ] & 0xf0 ) > > 4 ) ;
2008-04-23 08:09:12 -03:00
idreg [ 1 ] = idreg [ 1 ] & 0x0f ;
PDEBUG ( D_PROBE , " Pixart Sensor ID 0x%02X Chips ID 0x%02X " ,
idreg [ 0 ] , idreg [ 1 ] ) ;
if ( idreg [ 0 ] ! = 0x27 ) {
PDEBUG ( D_PROBE , " Error invalid sensor ID! " ) ;
return - ENODEV ;
}
PDEBUG ( D_PROBE ,
" Pixart PAC207BCA Image Processor and Control Chip detected "
" (vid/pid 0x%04X:0x%04X) " , id - > idVendor , id - > idProduct ) ;
cam = & gspca_dev - > cam ;
cam - > cam_mode = sif_mode ;
cam - > nmodes = ARRAY_SIZE ( sif_mode ) ;
return 0 ;
}
2008-09-03 17:12:16 -03:00
/* this function is called at probe and resume time */
static int sd_init ( struct gspca_dev * gspca_dev )
2008-04-23 08:09:12 -03:00
{
2008-09-03 17:12:16 -03:00
pac207_write_reg ( gspca_dev , 0x41 , 0x00 ) ;
/* Bit_0=Image Format,
* Bit_1 = LED ,
* Bit_2 = Compression test mode enable */
pac207_write_reg ( gspca_dev , 0x0f , 0x00 ) ; /* Power Control */
2008-04-23 08:09:12 -03:00
2012-05-09 12:13:29 -03:00
return gspca_dev - > usb_err ;
2008-04-23 08:09:12 -03:00
}
2012-05-10 08:34:42 -03:00
static void setcontrol ( struct gspca_dev * gspca_dev , u16 reg , u16 val )
{
pac207_write_reg ( gspca_dev , reg , val ) ;
pac207_write_reg ( gspca_dev , 0x13 , 0x01 ) ; /* Bit 0, auto clear */
pac207_write_reg ( gspca_dev , 0x1c , 0x01 ) ; /* not documented */
}
static int sd_s_ctrl ( struct v4l2_ctrl * ctrl )
{
struct gspca_dev * gspca_dev =
container_of ( ctrl - > handler , struct gspca_dev , ctrl_handler ) ;
struct sd * sd = ( struct sd * ) gspca_dev ;
gspca_dev - > usb_err = 0 ;
if ( ctrl - > id = = V4L2_CID_AUTOGAIN & & ctrl - > is_new & & ctrl - > val ) {
/* when switching to autogain set defaults to make sure
we are on a valid point of the autogain gain /
exposure knee graph , and give this change time to
take effect before doing autogain . */
gspca_dev - > exposure - > val = PAC207_EXPOSURE_DEFAULT ;
gspca_dev - > gain - > val = PAC207_GAIN_DEFAULT ;
sd - > autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES ;
}
if ( ! gspca_dev - > streaming )
return 0 ;
switch ( ctrl - > id ) {
case V4L2_CID_BRIGHTNESS :
setcontrol ( gspca_dev , PAC207_BRIGHTNESS_REG , ctrl - > val ) ;
break ;
case V4L2_CID_AUTOGAIN :
if ( gspca_dev - > exposure - > is_new | | ( ctrl - > is_new & & ctrl - > val ) )
setcontrol ( gspca_dev , PAC207_EXPOSURE_REG ,
gspca_dev - > exposure - > val ) ;
if ( gspca_dev - > gain - > is_new | | ( ctrl - > is_new & & ctrl - > val ) )
setcontrol ( gspca_dev , PAC207_GAIN_REG ,
gspca_dev - > gain - > val ) ;
break ;
default :
return - EINVAL ;
}
return gspca_dev - > usb_err ;
}
static const struct v4l2_ctrl_ops sd_ctrl_ops = {
. s_ctrl = sd_s_ctrl ,
} ;
/* this function is called at probe time */
static int sd_init_controls ( struct gspca_dev * gspca_dev )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
struct v4l2_ctrl_handler * hdl = & gspca_dev - > ctrl_handler ;
gspca_dev - > vdev . ctrl_handler = hdl ;
v4l2_ctrl_handler_init ( hdl , 4 ) ;
sd - > brightness = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_BRIGHTNESS ,
PAC207_BRIGHTNESS_MIN , PAC207_BRIGHTNESS_MAX ,
1 , PAC207_BRIGHTNESS_DEFAULT ) ;
gspca_dev - > autogain = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_AUTOGAIN , 0 , 1 , 1 , 1 ) ;
gspca_dev - > exposure = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_EXPOSURE ,
PAC207_EXPOSURE_MIN , PAC207_EXPOSURE_MAX ,
1 , PAC207_EXPOSURE_DEFAULT ) ;
gspca_dev - > gain = v4l2_ctrl_new_std ( hdl , & sd_ctrl_ops ,
V4L2_CID_GAIN ,
PAC207_GAIN_MIN , PAC207_GAIN_MAX ,
1 , PAC207_GAIN_DEFAULT ) ;
if ( hdl - > error ) {
pr_err ( " Could not initialize controls \n " ) ;
return hdl - > error ;
}
v4l2_ctrl_auto_cluster ( 3 , & gspca_dev - > autogain , 0 , false ) ;
return 0 ;
}
2008-04-23 08:09:12 -03:00
/* -- start the camera -- */
2008-09-20 06:39:08 -03:00
static int sd_start ( struct gspca_dev * gspca_dev )
2008-04-23 08:09:12 -03:00
{
struct sd * sd = ( struct sd * ) gspca_dev ;
__u8 mode ;
pac207_write_reg ( gspca_dev , 0x0f , 0x10 ) ; /* Power control (Bit 6-0) */
pac207_write_regs ( gspca_dev , 0x0002 , pac207_sensor_init [ 0 ] , 8 ) ;
pac207_write_regs ( gspca_dev , 0x000a , pac207_sensor_init [ 1 ] , 8 ) ;
pac207_write_regs ( gspca_dev , 0x0012 , pac207_sensor_init [ 2 ] , 8 ) ;
2009-07-29 08:02:50 -03:00
pac207_write_regs ( gspca_dev , 0x0042 , pac207_sensor_init [ 3 ] , 8 ) ;
2008-04-23 08:09:12 -03:00
/* Compression Balance */
if ( gspca_dev - > width = = 176 )
pac207_write_reg ( gspca_dev , 0x4a , 0xff ) ;
else
2009-07-29 08:03:17 -03:00
pac207_write_reg ( gspca_dev , 0x4a , 0x30 ) ;
2008-04-23 08:09:12 -03:00
pac207_write_reg ( gspca_dev , 0x4b , 0x00 ) ; /* Sram test value */
2012-05-10 08:34:42 -03:00
pac207_write_reg ( gspca_dev , 0x08 , v4l2_ctrl_g_ctrl ( sd - > brightness ) ) ;
2008-04-23 08:09:12 -03:00
/* PGA global gain (Bit 4-0) */
2012-05-10 08:34:42 -03:00
pac207_write_reg ( gspca_dev , 0x0e ,
v4l2_ctrl_g_ctrl ( gspca_dev - > gain ) ) ;
pac207_write_reg ( gspca_dev , 0x02 ,
v4l2_ctrl_g_ctrl ( gspca_dev - > exposure ) ) ; /* PXCK = 12MHz /n */
2008-04-23 08:09:12 -03:00
mode = 0x02 ; /* Image Format (Bit 0), LED (1), Compr. test mode (2) */
2008-06-12 10:58:58 -03:00
if ( gspca_dev - > width = = 176 ) { /* 176x144 */
2008-04-23 08:09:12 -03:00
mode | = 0x01 ;
PDEBUG ( D_STREAM , " pac207_start mode 176x144 " ) ;
2008-06-12 10:58:58 -03:00
} else { /* 352x288 */
2008-04-23 08:09:12 -03:00
PDEBUG ( D_STREAM , " pac207_start mode 352x288 " ) ;
2008-06-12 10:58:58 -03:00
}
2008-04-23 08:09:12 -03:00
pac207_write_reg ( gspca_dev , 0x41 , mode ) ;
pac207_write_reg ( gspca_dev , 0x13 , 0x01 ) ; /* Bit 0, auto clear */
pac207_write_reg ( gspca_dev , 0x1c , 0x01 ) ; /* not documented */
2008-06-30 15:50:11 -03:00
msleep ( 10 ) ;
2008-04-23 08:09:12 -03:00
pac207_write_reg ( gspca_dev , 0x40 , 0x01 ) ; /* Start ISO pipe */
sd - > sof_read = 0 ;
sd - > autogain_ignore_frames = 0 ;
atomic_set ( & sd - > avg_lum , - 1 ) ;
2012-05-09 12:13:29 -03:00
return gspca_dev - > usb_err ;
2008-04-23 08:09:12 -03:00
}
static void sd_stopN ( struct gspca_dev * gspca_dev )
{
pac207_write_reg ( gspca_dev , 0x40 , 0x00 ) ; /* Stop ISO pipe */
pac207_write_reg ( gspca_dev , 0x41 , 0x00 ) ; /* Turn of LED */
pac207_write_reg ( gspca_dev , 0x0f , 0x00 ) ; /* Power Control */
}
2008-09-03 17:12:17 -03:00
2008-04-23 08:09:12 -03:00
static void pac207_do_auto_gain ( struct gspca_dev * gspca_dev )
{
struct sd * sd = ( struct sd * ) gspca_dev ;
int avg_lum = atomic_read ( & sd - > avg_lum ) ;
2008-07-10 10:40:53 -03:00
if ( avg_lum = = - 1 )
2008-04-23 08:09:12 -03:00
return ;
2008-07-10 10:40:53 -03:00
if ( sd - > autogain_ignore_frames > 0 )
2008-04-23 08:09:12 -03:00
sd - > autogain_ignore_frames - - ;
2012-05-10 08:52:37 -03:00
else if ( gspca_coarse_grained_expo_autogain ( gspca_dev , avg_lum ,
90 , PAC207_AUTOGAIN_DEADZONE ) )
2008-09-03 17:12:17 -03:00
sd - > autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES ;
2008-04-23 08:09:12 -03:00
}
static void sd_pkt_scan ( struct gspca_dev * gspca_dev ,
2009-11-13 09:21:03 -03:00
u8 * data ,
2008-04-23 08:09:12 -03:00
int len )
{
2008-07-04 18:29:32 -03:00
struct sd * sd = ( struct sd * ) gspca_dev ;
2008-04-23 08:09:12 -03:00
unsigned char * sof ;
2009-11-02 08:05:51 -03:00
sof = pac_find_sof ( & sd - > sof_read , data , len ) ;
2008-04-23 08:09:12 -03:00
if ( sof ) {
2008-07-04 18:29:32 -03:00
int n ;
2008-04-23 08:09:12 -03:00
/* finish decoding current frame */
2008-07-04 18:29:32 -03:00
n = sof - data ;
2008-09-03 17:12:14 -03:00
if ( n > sizeof pac_sof_marker )
n - = sizeof pac_sof_marker ;
2008-07-04 18:29:32 -03:00
else
n = 0 ;
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , LAST_PACKET ,
data , n ) ;
2008-07-04 18:29:32 -03:00
sd - > header_read = 0 ;
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , FIRST_PACKET , NULL , 0 ) ;
2008-04-23 08:09:12 -03:00
len - = sof - data ;
data = sof ;
}
2008-07-04 18:29:32 -03:00
if ( sd - > header_read < 11 ) {
int needed ;
2008-04-23 08:09:12 -03:00
2008-07-04 18:29:32 -03:00
/* get average lumination from frame header (byte 5) */
if ( sd - > header_read < 5 ) {
needed = 5 - sd - > header_read ;
if ( len > = needed )
atomic_set ( & sd - > avg_lum , data [ needed - 1 ] ) ;
}
/* skip the rest of the header */
needed = 11 - sd - > header_read ;
if ( len < = needed ) {
sd - > header_read + = len ;
return ;
}
data + = needed ;
len - = needed ;
sd - > header_read = 11 ;
}
2008-04-23 08:09:12 -03:00
2009-11-13 09:21:03 -03:00
gspca_frame_add ( gspca_dev , INTER_PACKET , data , len ) ;
2008-04-23 08:09:12 -03:00
}
2010-10-01 07:33:26 -03:00
# if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
2010-01-29 11:02:10 -03:00
static int sd_int_pkt_scan ( struct gspca_dev * gspca_dev ,
u8 * data , /* interrupt packet data */
int len ) /* interrput packet length */
{
int ret = - EINVAL ;
if ( len = = 2 & & data [ 0 ] = = 0x5a & & data [ 1 ] = = 0x5a ) {
input_report_key ( gspca_dev - > input_dev , KEY_CAMERA , 1 ) ;
input_sync ( gspca_dev - > input_dev ) ;
input_report_key ( gspca_dev - > input_dev , KEY_CAMERA , 0 ) ;
input_sync ( gspca_dev - > input_dev ) ;
ret = 0 ;
}
return ret ;
}
# endif
2008-04-23 08:09:12 -03:00
/* sub-driver description */
2008-07-04 11:16:16 -03:00
static const struct sd_desc sd_desc = {
2008-04-23 08:09:12 -03:00
. name = MODULE_NAME ,
. config = sd_config ,
2008-09-03 17:12:16 -03:00
. init = sd_init ,
2012-05-10 08:34:42 -03:00
. init_controls = sd_init_controls ,
2008-04-23 08:09:12 -03:00
. start = sd_start ,
. stopN = sd_stopN ,
. dq_callback = pac207_do_auto_gain ,
. pkt_scan = sd_pkt_scan ,
2010-10-01 07:33:26 -03:00
# if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)
2010-01-29 11:02:10 -03:00
. int_pkt_scan = sd_int_pkt_scan ,
# endif
2008-04-23 08:09:12 -03:00
} ;
/* -- module initialisation -- */
2011-01-13 05:20:29 -03:00
static const struct usb_device_id device_table [ ] = {
2008-07-25 08:53:03 -03:00
{ USB_DEVICE ( 0x041e , 0x4028 ) } ,
{ USB_DEVICE ( 0x093a , 0x2460 ) } ,
2008-12-20 14:30:58 -03:00
{ USB_DEVICE ( 0x093a , 0x2461 ) } ,
2008-07-25 08:53:03 -03:00
{ USB_DEVICE ( 0x093a , 0x2463 ) } ,
{ USB_DEVICE ( 0x093a , 0x2464 ) } ,
{ USB_DEVICE ( 0x093a , 0x2468 ) } ,
{ USB_DEVICE ( 0x093a , 0x2470 ) } ,
{ USB_DEVICE ( 0x093a , 0x2471 ) } ,
{ USB_DEVICE ( 0x093a , 0x2472 ) } ,
2009-01-25 14:37:26 -03:00
{ USB_DEVICE ( 0x093a , 0x2474 ) } ,
2008-09-06 04:18:48 -03:00
{ USB_DEVICE ( 0x093a , 0x2476 ) } ,
2008-10-28 08:00:23 -03:00
{ USB_DEVICE ( 0x145f , 0x013a ) } ,
2008-07-25 08:53:03 -03:00
{ USB_DEVICE ( 0x2001 , 0xf115 ) } ,
2008-04-23 08:09:12 -03:00
{ }
} ;
MODULE_DEVICE_TABLE ( usb , device_table ) ;
/* -- device connect -- */
static int sd_probe ( struct usb_interface * intf ,
const struct usb_device_id * id )
{
2008-06-12 10:58:58 -03:00
return gspca_dev_probe ( intf , id , & sd_desc , sizeof ( struct sd ) ,
THIS_MODULE ) ;
2008-04-23 08:09:12 -03:00
}
static struct usb_driver sd_driver = {
. name = MODULE_NAME ,
. id_table = device_table ,
. probe = sd_probe ,
. disconnect = gspca_disconnect ,
2008-09-03 16:48:10 -03:00
# ifdef CONFIG_PM
. suspend = gspca_suspend ,
. resume = gspca_resume ,
# endif
2008-04-23 08:09:12 -03:00
} ;
2011-11-18 09:46:12 -08:00
module_usb_driver ( sd_driver ) ;