linux/drivers/media/video/gspca/pac7311.c
<<
>>
Prefs
   1/*
   2 *              Pixart PAC7311 library
   3 *              Copyright (C) 2005 Thomas Kaiser thomas@kaiser-linux.li
   4 *
   5 * V4L2 by Jean-Francois Moine <http://moinejf.free.fr>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License as published by
   9 * the Free Software Foundation; either version 2 of the License, or
  10 * any later version.
  11 *
  12 * This program is distributed in the hope that it will be useful,
  13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15 * GNU General Public License for more details.
  16 *
  17 * You should have received a copy of the GNU General Public License
  18 * along with this program; if not, write to the Free Software
  19 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  20 */
  21
  22/* Some documentation about various registers as determined by trial and error.
  23   When the register addresses differ between the 7202 and the 7311 the 2
  24   different addresses are written as 7302addr/7311addr, when one of the 2
  25   addresses is a - sign that register description is not valid for the
  26   matching IC.
  27
  28   Register page 1:
  29
  30   Address      Description
  31   -/0x08       Unknown compressor related, must always be 8 except when not
  32                in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !
  33   -/0x1b       Auto white balance related, bit 0 is AWB enable (inverted)
  34                bits 345 seem to toggle per color gains on/off (inverted)
  35   0x78         Global control, bit 6 controls the LED (inverted)
  36   -/0x80       JPEG compression ratio ? Best not touched
  37
  38   Register page 3/4:
  39
  40   Address      Description
  41   0x02         Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on
  42                the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?
  43   -/0x0f       Master gain 1-245, low value = high gain
  44   0x10/-       Master gain 0-31
  45   -/0x10       Another gain 0-15, limited influence (1-2x gain I guess)
  46   0x21         Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused
  47   -/0x27       Seems to toggle various gains on / off, Setting bit 7 seems to
  48                completely disable the analog amplification block. Set to 0x68
  49                for max gain, 0x14 for minimal gain.
  50*/
  51
  52#define MODULE_NAME "pac7311"
  53
  54#include "gspca.h"
  55
  56MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
  57MODULE_DESCRIPTION("Pixart PAC7311");
  58MODULE_LICENSE("GPL");
  59
  60/* specific webcam descriptor */
  61struct sd {
  62        struct gspca_dev gspca_dev;             /* !! must be the first item */
  63
  64        unsigned char brightness;
  65        unsigned char contrast;
  66        unsigned char colors;
  67        unsigned char gain;
  68        unsigned char exposure;
  69        unsigned char autogain;
  70        __u8 hflip;
  71        __u8 vflip;
  72
  73        __u8 sensor;
  74#define SENSOR_PAC7302 0
  75#define SENSOR_PAC7311 1
  76
  77        u8 sof_read;
  78        u8 autogain_ignore_frames;
  79
  80        atomic_t avg_lum;
  81};
  82
  83/* V4L2 controls supported by the driver */
  84static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val);
  85static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val);
  86static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);
  87static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);
  88static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val);
  89static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val);
  90static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);
  91static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);
  92static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);
  93static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);
  94static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);
  95static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);
  96static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);
  97static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);
  98static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);
  99static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);
 100
 101static struct ctrl sd_ctrls[] = {
 102/* This control is pac7302 only */
 103#define BRIGHTNESS_IDX 0
 104        {
 105            {
 106                .id      = V4L2_CID_BRIGHTNESS,
 107                .type    = V4L2_CTRL_TYPE_INTEGER,
 108                .name    = "Brightness",
 109                .minimum = 0,
 110#define BRIGHTNESS_MAX 0x20
 111                .maximum = BRIGHTNESS_MAX,
 112                .step    = 1,
 113#define BRIGHTNESS_DEF 0x10
 114                .default_value = BRIGHTNESS_DEF,
 115            },
 116            .set = sd_setbrightness,
 117            .get = sd_getbrightness,
 118        },
 119/* This control is for both the 7302 and the 7311 */
 120        {
 121            {
 122                .id      = V4L2_CID_CONTRAST,
 123                .type    = V4L2_CTRL_TYPE_INTEGER,
 124                .name    = "Contrast",
 125                .minimum = 0,
 126#define CONTRAST_MAX 255
 127                .maximum = CONTRAST_MAX,
 128                .step    = 1,
 129#define CONTRAST_DEF 127
 130                .default_value = CONTRAST_DEF,
 131            },
 132            .set = sd_setcontrast,
 133            .get = sd_getcontrast,
 134        },
 135/* This control is pac7302 only */
 136#define SATURATION_IDX 2
 137        {
 138            {
 139                .id      = V4L2_CID_SATURATION,
 140                .type    = V4L2_CTRL_TYPE_INTEGER,
 141                .name    = "Saturation",
 142                .minimum = 0,
 143#define COLOR_MAX 255
 144                .maximum = COLOR_MAX,
 145                .step    = 1,
 146#define COLOR_DEF 127
 147                .default_value = COLOR_DEF,
 148            },
 149            .set = sd_setcolors,
 150            .get = sd_getcolors,
 151        },
 152/* All controls below are for both the 7302 and the 7311 */
 153        {
 154            {
 155                .id      = V4L2_CID_GAIN,
 156                .type    = V4L2_CTRL_TYPE_INTEGER,
 157                .name    = "Gain",
 158                .minimum = 0,
 159#define GAIN_MAX 255
 160                .maximum = GAIN_MAX,
 161                .step    = 1,
 162#define GAIN_DEF 127
 163#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */
 164                .default_value = GAIN_DEF,
 165            },
 166            .set = sd_setgain,
 167            .get = sd_getgain,
 168        },
 169        {
 170            {
 171                .id      = V4L2_CID_EXPOSURE,
 172                .type    = V4L2_CTRL_TYPE_INTEGER,
 173                .name    = "Exposure",
 174                .minimum = 0,
 175#define EXPOSURE_MAX 255
 176                .maximum = EXPOSURE_MAX,
 177                .step    = 1,
 178#define EXPOSURE_DEF  16 /*  32 ms / 30 fps */
 179#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */
 180                .default_value = EXPOSURE_DEF,
 181            },
 182            .set = sd_setexposure,
 183            .get = sd_getexposure,
 184        },
 185        {
 186            {
 187                .id      = V4L2_CID_AUTOGAIN,
 188                .type    = V4L2_CTRL_TYPE_BOOLEAN,
 189                .name    = "Auto Gain",
 190                .minimum = 0,
 191                .maximum = 1,
 192                .step    = 1,
 193#define AUTOGAIN_DEF 1
 194                .default_value = AUTOGAIN_DEF,
 195            },
 196            .set = sd_setautogain,
 197            .get = sd_getautogain,
 198        },
 199        {
 200            {
 201                .id      = V4L2_CID_HFLIP,
 202                .type    = V4L2_CTRL_TYPE_BOOLEAN,
 203                .name    = "Mirror",
 204                .minimum = 0,
 205                .maximum = 1,
 206                .step    = 1,
 207#define HFLIP_DEF 0
 208                .default_value = HFLIP_DEF,
 209            },
 210            .set = sd_sethflip,
 211            .get = sd_gethflip,
 212        },
 213        {
 214            {
 215                .id      = V4L2_CID_VFLIP,
 216                .type    = V4L2_CTRL_TYPE_BOOLEAN,
 217                .name    = "Vflip",
 218                .minimum = 0,
 219                .maximum = 1,
 220                .step    = 1,
 221#define VFLIP_DEF 0
 222                .default_value = VFLIP_DEF,
 223            },
 224            .set = sd_setvflip,
 225            .get = sd_getvflip,
 226        },
 227};
 228
 229static const struct v4l2_pix_format vga_mode[] = {
 230        {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
 231                .bytesperline = 160,
 232                .sizeimage = 160 * 120 * 3 / 8 + 590,
 233                .colorspace = V4L2_COLORSPACE_JPEG,
 234                .priv = 2},
 235        {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
 236                .bytesperline = 320,
 237                .sizeimage = 320 * 240 * 3 / 8 + 590,
 238                .colorspace = V4L2_COLORSPACE_JPEG,
 239                .priv = 1},
 240        {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
 241                .bytesperline = 640,
 242                .sizeimage = 640 * 480 * 3 / 8 + 590,
 243                .colorspace = V4L2_COLORSPACE_JPEG,
 244                .priv = 0},
 245};
 246
 247/* pac 7302 */
 248static const __u8 init_7302[] = {
 249/*      index,value */
 250        0xff, 0x01,             /* page 1 */
 251        0x78, 0x00,             /* deactivate */
 252        0xff, 0x01,
 253        0x78, 0x40,             /* led off */
 254};
 255static const __u8 start_7302[] = {
 256/*      index, len, [value]* */
 257        0xff, 1,        0x00,           /* page 0 */
 258        0x00, 12,       0x01, 0x40, 0x40, 0x40, 0x01, 0xe0, 0x02, 0x80,
 259                        0x00, 0x00, 0x00, 0x00,
 260        0x0d, 24,       0x03, 0x01, 0x00, 0xb5, 0x07, 0xcb, 0x00, 0x00,
 261                        0x07, 0xc8, 0x00, 0xea, 0x07, 0xcf, 0x07, 0xf7,
 262                        0x07, 0x7e, 0x01, 0x0b, 0x00, 0x00, 0x00, 0x11,
 263        0x26, 2,        0xaa, 0xaa,
 264        0x2e, 1,        0x31,
 265        0x38, 1,        0x01,
 266        0x3a, 3,        0x14, 0xff, 0x5a,
 267        0x43, 11,       0x00, 0x0a, 0x18, 0x11, 0x01, 0x2c, 0x88, 0x11,
 268                        0x00, 0x54, 0x11,
 269        0x55, 1,        0x00,
 270        0x62, 4,        0x10, 0x1e, 0x1e, 0x18,
 271        0x6b, 1,        0x00,
 272        0x6e, 3,        0x08, 0x06, 0x00,
 273        0x72, 3,        0x00, 0xff, 0x00,
 274        0x7d, 23,       0x01, 0x01, 0x58, 0x46, 0x50, 0x3c, 0x50, 0x3c,
 275                        0x54, 0x46, 0x54, 0x56, 0x52, 0x50, 0x52, 0x50,
 276                        0x56, 0x64, 0xa4, 0x00, 0xda, 0x00, 0x00,
 277        0xa2, 10,       0x22, 0x2c, 0x3c, 0x54, 0x69, 0x7c, 0x9c, 0xb9,
 278                        0xd2, 0xeb,
 279        0xaf, 1,        0x02,
 280        0xb5, 2,        0x08, 0x08,
 281        0xb8, 2,        0x08, 0x88,
 282        0xc4, 4,        0xae, 0x01, 0x04, 0x01,
 283        0xcc, 1,        0x00,
 284        0xd1, 11,       0x01, 0x30, 0x49, 0x5e, 0x6f, 0x7f, 0x8e, 0xa9,
 285                        0xc1, 0xd7, 0xec,
 286        0xdc, 1,        0x01,
 287        0xff, 1,        0x01,           /* page 1 */
 288        0x12, 3,        0x02, 0x00, 0x01,
 289        0x3e, 2,        0x00, 0x00,
 290        0x76, 5,        0x01, 0x20, 0x40, 0x00, 0xf2,
 291        0x7c, 1,        0x00,
 292        0x7f, 10,       0x4b, 0x0f, 0x01, 0x2c, 0x02, 0x58, 0x03, 0x20,
 293                        0x02, 0x00,
 294        0x96, 5,        0x01, 0x10, 0x04, 0x01, 0x04,
 295        0xc8, 14,       0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00,
 296                        0x07, 0x00, 0x01, 0x07, 0x04, 0x01,
 297        0xd8, 1,        0x01,
 298        0xdb, 2,        0x00, 0x01,
 299        0xde, 7,        0x00, 0x01, 0x04, 0x04, 0x00, 0x00, 0x00,
 300        0xe6, 4,        0x00, 0x00, 0x00, 0x01,
 301        0xeb, 1,        0x00,
 302        0xff, 1,        0x02,           /* page 2 */
 303        0x22, 1,        0x00,
 304        0xff, 1,        0x03,           /* page 3 */
 305        0x00, 255,                      /* load the page 3 */
 306        0x11, 1,        0x01,
 307        0xff, 1,        0x02,           /* page 2 */
 308        0x13, 1,        0x00,
 309        0x22, 4,        0x1f, 0xa4, 0xf0, 0x96,
 310        0x27, 2,        0x14, 0x0c,
 311        0x2a, 5,        0xc8, 0x00, 0x18, 0x12, 0x22,
 312        0x64, 8,        0x00, 0x00, 0xf0, 0x01, 0x14, 0x44, 0x44, 0x44,
 313        0x6e, 1,        0x08,
 314        0xff, 1,        0x01,           /* page 1 */
 315        0x78, 1,        0x00,
 316        0, 0                            /* end of sequence */
 317};
 318
 319/* page 3 - the value 0xaa says skip the index - see reg_w_page() */
 320static const __u8 page3_7302[] = {
 321        0x90, 0x40, 0x03, 0x50, 0xc2, 0x01, 0x14, 0x16,
 322        0x14, 0x12, 0x00, 0x00, 0x00, 0x02, 0x33, 0x00,
 323        0x0f, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 324        0x00, 0x00, 0x00, 0x47, 0x01, 0xb3, 0x01, 0x00,
 325        0x00, 0x08, 0x00, 0x00, 0x0d, 0x00, 0x00, 0x21,
 326        0x00, 0x00, 0x00, 0x54, 0xf4, 0x02, 0x52, 0x54,
 327        0xa4, 0xb8, 0xe0, 0x2a, 0xf6, 0x00, 0x00, 0x00,
 328        0x00, 0x1e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 329        0x00, 0xfc, 0x00, 0xf2, 0x1f, 0x04, 0x00, 0x00,
 330        0x00, 0x00, 0x00, 0xc0, 0xc0, 0x10, 0x00, 0x00,
 331        0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 332        0x00, 0x40, 0xff, 0x03, 0x19, 0x00, 0x00, 0x00,
 333        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 334        0x00, 0x00, 0x00, 0x00, 0x00, 0xc8, 0xc8, 0xc8,
 335        0xc8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50,
 336        0x08, 0x10, 0x24, 0x40, 0x00, 0x00, 0x00, 0x00,
 337        0x01, 0x00, 0x02, 0x47, 0x00, 0x00, 0x00, 0x00,
 338        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 339        0x00, 0x02, 0xfa, 0x00, 0x64, 0x5a, 0x28, 0x00,
 340        0x00
 341};
 342
 343/* pac 7311 */
 344static const __u8 init_7311[] = {
 345        0x78, 0x40,     /* Bit_0=start stream, Bit_6=LED */
 346        0x78, 0x40,     /* Bit_0=start stream, Bit_6=LED */
 347        0x78, 0x44,     /* Bit_0=start stream, Bit_6=LED */
 348        0xff, 0x04,
 349        0x27, 0x80,
 350        0x28, 0xca,
 351        0x29, 0x53,
 352        0x2a, 0x0e,
 353        0xff, 0x01,
 354        0x3e, 0x20,
 355};
 356
 357static const __u8 start_7311[] = {
 358/*      index, len, [value]* */
 359        0xff, 1,        0x01,           /* page 1 */
 360        0x02, 43,       0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00,
 361                        0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c,
 362                        0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10,
 363                        0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00,
 364                        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
 365                        0x00, 0x00, 0x00,
 366        0x3e, 42,       0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e,
 367                        0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49,
 368                        0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48,
 369                        0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78,
 370                        0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b,
 371                        0xd0, 0xff,
 372        0x78, 6,        0x44, 0x00, 0xf2, 0x01, 0x01, 0x80,
 373        0x7f, 18,       0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84,
 374                        0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14,
 375                        0x18, 0x20,
 376        0x96, 3,        0x01, 0x08, 0x04,
 377        0xa0, 4,        0x44, 0x44, 0x44, 0x04,
 378        0xf0, 13,       0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
 379                        0x3f, 0x00, 0x0a, 0x01, 0x00,
 380        0xff, 1,        0x04,           /* page 4 */
 381        0x00, 254,                      /* load the page 4 */
 382        0x11, 1,        0x01,
 383        0, 0                            /* end of sequence */
 384};
 385
 386/* page 4 - the value 0xaa says skip the index - see reg_w_page() */
 387static const __u8 page4_7311[] = {
 388        0xaa, 0xaa, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
 389        0x09, 0x00, 0xaa, 0xaa, 0x07, 0x00, 0x00, 0x62,
 390        0x08, 0xaa, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
 391        0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, 0xaa,
 392        0xaa, 0x00, 0x08, 0xaa, 0x03, 0xaa, 0x00, 0x68,
 393        0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
 394        0x23, 0x28, 0x04, 0x11, 0x00, 0x00
 395};
 396
 397static void reg_w_buf(struct gspca_dev *gspca_dev,
 398                  __u8 index,
 399                  const char *buffer, int len)
 400{
 401        memcpy(gspca_dev->usb_buf, buffer, len);
 402        usb_control_msg(gspca_dev->dev,
 403                        usb_sndctrlpipe(gspca_dev->dev, 0),
 404                        1,              /* request */
 405                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 406                        0,              /* value */
 407                        index, gspca_dev->usb_buf, len,
 408                        500);
 409}
 410
 411
 412static void reg_w(struct gspca_dev *gspca_dev,
 413                  __u8 index,
 414                  __u8 value)
 415{
 416        gspca_dev->usb_buf[0] = value;
 417        usb_control_msg(gspca_dev->dev,
 418                        usb_sndctrlpipe(gspca_dev->dev, 0),
 419                        0,                      /* request */
 420                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 421                        0, index, gspca_dev->usb_buf, 1,
 422                        500);
 423}
 424
 425static void reg_w_seq(struct gspca_dev *gspca_dev,
 426                const __u8 *seq, int len)
 427{
 428        while (--len >= 0) {
 429                reg_w(gspca_dev, seq[0], seq[1]);
 430                seq += 2;
 431        }
 432}
 433
 434/* load the beginning of a page */
 435static void reg_w_page(struct gspca_dev *gspca_dev,
 436                        const __u8 *page, int len)
 437{
 438        int index;
 439
 440        for (index = 0; index < len; index++) {
 441                if (page[index] == 0xaa)                /* skip this index */
 442                        continue;
 443                gspca_dev->usb_buf[0] = page[index];
 444                usb_control_msg(gspca_dev->dev,
 445                                usb_sndctrlpipe(gspca_dev->dev, 0),
 446                                0,                      /* request */
 447                        USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
 448                                0, index, gspca_dev->usb_buf, 1,
 449                                500);
 450        }
 451}
 452
 453/* output a variable sequence */
 454static void reg_w_var(struct gspca_dev *gspca_dev,
 455                        const __u8 *seq)
 456{
 457        int index, len;
 458
 459        for (;;) {
 460                index = *seq++;
 461                len = *seq++;
 462                switch (len) {
 463                case 0:
 464                        return;
 465                case 254:
 466                        reg_w_page(gspca_dev, page4_7311, sizeof page4_7311);
 467                        break;
 468                case 255:
 469                        reg_w_page(gspca_dev, page3_7302, sizeof page3_7302);
 470                        break;
 471                default:
 472                        if (len > 64) {
 473                                PDEBUG(D_ERR|D_STREAM,
 474                                        "Incorrect variable sequence");
 475                                return;
 476                        }
 477                        while (len > 0) {
 478                                if (len < 8) {
 479                                        reg_w_buf(gspca_dev, index, seq, len);
 480                                        seq += len;
 481                                        break;
 482                                }
 483                                reg_w_buf(gspca_dev, index, seq, 8);
 484                                seq += 8;
 485                                index += 8;
 486                                len -= 8;
 487                        }
 488                }
 489        }
 490        /* not reached */
 491}
 492
 493/* this function is called at probe time */
 494static int sd_config(struct gspca_dev *gspca_dev,
 495                        const struct usb_device_id *id)
 496{
 497        struct sd *sd = (struct sd *) gspca_dev;
 498        struct cam *cam;
 499
 500        cam = &gspca_dev->cam;
 501
 502        sd->sensor = id->driver_info;
 503        if (sd->sensor == SENSOR_PAC7302) {
 504                PDEBUG(D_CONF, "Find Sensor PAC7302");
 505                cam->cam_mode = &vga_mode[2];   /* only 640x480 */
 506                cam->nmodes = 1;
 507        } else {
 508                PDEBUG(D_CONF, "Find Sensor PAC7311");
 509                cam->cam_mode = vga_mode;
 510                cam->nmodes = ARRAY_SIZE(vga_mode);
 511                gspca_dev->ctrl_dis = (1 << BRIGHTNESS_IDX)
 512                                | (1 << SATURATION_IDX);
 513        }
 514
 515        sd->brightness = BRIGHTNESS_DEF;
 516        sd->contrast = CONTRAST_DEF;
 517        sd->colors = COLOR_DEF;
 518        sd->gain = GAIN_DEF;
 519        sd->exposure = EXPOSURE_DEF;
 520        sd->autogain = AUTOGAIN_DEF;
 521        sd->hflip = HFLIP_DEF;
 522        sd->vflip = VFLIP_DEF;
 523        return 0;
 524}
 525
 526/* This function is used by pac7302 only */
 527static void setbrightcont(struct gspca_dev *gspca_dev)
 528{
 529        struct sd *sd = (struct sd *) gspca_dev;
 530        int i, v;
 531        static const __u8 max[10] =
 532                {0x29, 0x33, 0x42, 0x5a, 0x6e, 0x80, 0x9f, 0xbb,
 533                 0xd4, 0xec};
 534        static const __u8 delta[10] =
 535                {0x35, 0x33, 0x33, 0x2f, 0x2a, 0x25, 0x1e, 0x17,
 536                 0x11, 0x0b};
 537
 538        reg_w(gspca_dev, 0xff, 0x00);   /* page 0 */
 539        for (i = 0; i < 10; i++) {
 540                v = max[i];
 541                v += (sd->brightness - BRIGHTNESS_MAX)
 542                        * 150 / BRIGHTNESS_MAX;         /* 200 ? */
 543                v -= delta[i] * sd->contrast / CONTRAST_MAX;
 544                if (v < 0)
 545                        v = 0;
 546                else if (v > 0xff)
 547                        v = 0xff;
 548                reg_w(gspca_dev, 0xa2 + i, v);
 549        }
 550        reg_w(gspca_dev, 0xdc, 0x01);
 551}
 552
 553/* This function is used by pac7311 only */
 554static void setcontrast(struct gspca_dev *gspca_dev)
 555{
 556        struct sd *sd = (struct sd *) gspca_dev;
 557
 558        reg_w(gspca_dev, 0xff, 0x04);
 559        reg_w(gspca_dev, 0x10, sd->contrast >> 4);
 560        /* load registers to sensor (Bit 0, auto clear) */
 561        reg_w(gspca_dev, 0x11, 0x01);
 562}
 563
 564/* This function is used by pac7302 only */
 565static void setcolors(struct gspca_dev *gspca_dev)
 566{
 567        struct sd *sd = (struct sd *) gspca_dev;
 568        int i, v;
 569        static const int a[9] =
 570                {217, -212, 0, -101, 170, -67, -38, -315, 355};
 571        static const int b[9] =
 572                {19, 106, 0, 19, 106, 1, 19, 106, 1};
 573
 574        reg_w(gspca_dev, 0xff, 0x03);   /* page 3 */
 575        reg_w(gspca_dev, 0x11, 0x01);
 576        reg_w(gspca_dev, 0xff, 0x00);   /* page 0 */
 577        reg_w(gspca_dev, 0xff, 0x00);   /* page 0 */
 578        for (i = 0; i < 9; i++) {
 579                v = a[i] * sd->colors / COLOR_MAX + b[i];
 580                reg_w(gspca_dev, 0x0f + 2 * i, (v >> 8) & 0x07);
 581                reg_w(gspca_dev, 0x0f + 2 * i + 1, v);
 582        }
 583        reg_w(gspca_dev, 0xdc, 0x01);
 584        PDEBUG(D_CONF|D_STREAM, "color: %i", sd->colors);
 585}
 586
 587static void setgain(struct gspca_dev *gspca_dev)
 588{
 589        struct sd *sd = (struct sd *) gspca_dev;
 590
 591        if (sd->sensor == SENSOR_PAC7302) {
 592                reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
 593                reg_w(gspca_dev, 0x10, sd->gain >> 3);
 594        } else {
 595                int gain = GAIN_MAX - sd->gain;
 596                if (gain < 1)
 597                        gain = 1;
 598                else if (gain > 245)
 599                        gain = 245;
 600                reg_w(gspca_dev, 0xff, 0x04);           /* page 4 */
 601                reg_w(gspca_dev, 0x0e, 0x00);
 602                reg_w(gspca_dev, 0x0f, gain);
 603        }
 604        /* load registers to sensor (Bit 0, auto clear) */
 605        reg_w(gspca_dev, 0x11, 0x01);
 606}
 607
 608static void setexposure(struct gspca_dev *gspca_dev)
 609{
 610        struct sd *sd = (struct sd *) gspca_dev;
 611        __u8 reg;
 612
 613        /* register 2 of frame 3/4 contains the clock divider configuring the
 614           no fps according to the formula: 60 / reg. sd->exposure is the
 615           desired exposure time in ms. */
 616        reg = 120 * sd->exposure / 1000;
 617        if (reg < 2)
 618                reg = 2;
 619        else if (reg > 63)
 620                reg = 63;
 621
 622        if (sd->sensor == SENSOR_PAC7302) {
 623                /* On the pac7302 reg2 MUST be a multiple of 3, so round it to
 624                   the nearest multiple of 3, except when between 6 and 12? */
 625                if (reg < 6 || reg > 12)
 626                        reg = ((reg + 1) / 3) * 3;
 627                reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
 628                reg_w(gspca_dev, 0x02, reg);
 629        } else {
 630                reg_w(gspca_dev, 0xff, 0x04);           /* page 4 */
 631                reg_w(gspca_dev, 0x02, reg);
 632                /* Page 1 register 8 must always be 0x08 except when not in
 633                   640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */
 634                reg_w(gspca_dev, 0xff, 0x01);
 635                if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&
 636                                reg <= 3)
 637                        reg_w(gspca_dev, 0x08, 0x09);
 638                else
 639                        reg_w(gspca_dev, 0x08, 0x08);
 640        }
 641        /* load registers to sensor (Bit 0, auto clear) */
 642        reg_w(gspca_dev, 0x11, 0x01);
 643}
 644
 645static void sethvflip(struct gspca_dev *gspca_dev)
 646{
 647        struct sd *sd = (struct sd *) gspca_dev;
 648        __u8 data;
 649
 650        if (sd->sensor == SENSOR_PAC7302) {
 651                reg_w(gspca_dev, 0xff, 0x03);           /* page 3 */
 652                data = (sd->hflip ? 0x08 : 0x00)
 653                        | (sd->vflip ? 0x04 : 0x00);
 654        } else {
 655                reg_w(gspca_dev, 0xff, 0x04);           /* page 4 */
 656                data = (sd->hflip ? 0x04 : 0x00)
 657                        | (sd->vflip ? 0x08 : 0x00);
 658        }
 659        reg_w(gspca_dev, 0x21, data);
 660        /* load registers to sensor (Bit 0, auto clear) */
 661        reg_w(gspca_dev, 0x11, 0x01);
 662}
 663
 664/* this function is called at probe and resume time */
 665static int sd_init(struct gspca_dev *gspca_dev)
 666{
 667        struct sd *sd = (struct sd *) gspca_dev;
 668
 669        if (sd->sensor == SENSOR_PAC7302)
 670                reg_w_seq(gspca_dev, init_7302, sizeof init_7302);
 671        else
 672                reg_w_seq(gspca_dev, init_7311, sizeof init_7311);
 673
 674        return 0;
 675}
 676
 677static int sd_start(struct gspca_dev *gspca_dev)
 678{
 679        struct sd *sd = (struct sd *) gspca_dev;
 680
 681        sd->sof_read = 0;
 682
 683        if (sd->sensor == SENSOR_PAC7302) {
 684                reg_w_var(gspca_dev, start_7302);
 685                setbrightcont(gspca_dev);
 686                setcolors(gspca_dev);
 687        } else {
 688                reg_w_var(gspca_dev, start_7311);
 689                setcontrast(gspca_dev);
 690        }
 691        setgain(gspca_dev);
 692        setexposure(gspca_dev);
 693        sethvflip(gspca_dev);
 694
 695        /* set correct resolution */
 696        switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
 697        case 2:                                 /* 160x120 pac7311 */
 698                reg_w(gspca_dev, 0xff, 0x01);
 699                reg_w(gspca_dev, 0x17, 0x20);
 700                reg_w(gspca_dev, 0x87, 0x10);
 701                break;
 702        case 1:                                 /* 320x240 pac7311 */
 703                reg_w(gspca_dev, 0xff, 0x01);
 704                reg_w(gspca_dev, 0x17, 0x30);
 705                reg_w(gspca_dev, 0x87, 0x11);
 706                break;
 707        case 0:                                 /* 640x480 */
 708                if (sd->sensor == SENSOR_PAC7302)
 709                        break;
 710                reg_w(gspca_dev, 0xff, 0x01);
 711                reg_w(gspca_dev, 0x17, 0x00);
 712                reg_w(gspca_dev, 0x87, 0x12);
 713                break;
 714        }
 715
 716        sd->sof_read = 0;
 717        sd->autogain_ignore_frames = 0;
 718        atomic_set(&sd->avg_lum, -1);
 719
 720        /* start stream */
 721        reg_w(gspca_dev, 0xff, 0x01);
 722        if (sd->sensor == SENSOR_PAC7302)
 723                reg_w(gspca_dev, 0x78, 0x01);
 724        else
 725                reg_w(gspca_dev, 0x78, 0x05);
 726        return 0;
 727}
 728
 729static void sd_stopN(struct gspca_dev *gspca_dev)
 730{
 731        struct sd *sd = (struct sd *) gspca_dev;
 732
 733        if (sd->sensor == SENSOR_PAC7302) {
 734                reg_w(gspca_dev, 0xff, 0x01);
 735                reg_w(gspca_dev, 0x78, 0x00);
 736                reg_w(gspca_dev, 0x78, 0x00);
 737                return;
 738        }
 739        reg_w(gspca_dev, 0xff, 0x04);
 740        reg_w(gspca_dev, 0x27, 0x80);
 741        reg_w(gspca_dev, 0x28, 0xca);
 742        reg_w(gspca_dev, 0x29, 0x53);
 743        reg_w(gspca_dev, 0x2a, 0x0e);
 744        reg_w(gspca_dev, 0xff, 0x01);
 745        reg_w(gspca_dev, 0x3e, 0x20);
 746        reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
 747        reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
 748        reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */
 749}
 750
 751/* called on streamoff with alt 0 and on disconnect */
 752static void sd_stop0(struct gspca_dev *gspca_dev)
 753{
 754        struct sd *sd = (struct sd *) gspca_dev;
 755
 756        if (!gspca_dev->present)
 757                return;
 758        if (sd->sensor == SENSOR_PAC7302) {
 759                reg_w(gspca_dev, 0xff, 0x01);
 760                reg_w(gspca_dev, 0x78, 0x40);
 761        }
 762}
 763
 764/* Include pac common sof detection functions */
 765#include "pac_common.h"
 766
 767static void do_autogain(struct gspca_dev *gspca_dev)
 768{
 769        struct sd *sd = (struct sd *) gspca_dev;
 770        int avg_lum = atomic_read(&sd->avg_lum);
 771        int desired_lum, deadzone;
 772
 773        if (avg_lum == -1)
 774                return;
 775
 776        if (sd->sensor == SENSOR_PAC7302) {
 777                desired_lum = 270 + sd->brightness * 4;
 778                /* Hack hack, with the 7202 the first exposure step is
 779                   pretty large, so if we're about to make the first
 780                   exposure increase make the deadzone large to avoid
 781                   oscilating */
 782                if (desired_lum > avg_lum && sd->gain == GAIN_DEF &&
 783                                sd->exposure > EXPOSURE_DEF &&
 784                                sd->exposure < 42)
 785                        deadzone = 90;
 786                else
 787                        deadzone = 30;
 788        } else {
 789                desired_lum = 200;
 790                deadzone = 20;
 791        }
 792
 793        if (sd->autogain_ignore_frames > 0)
 794                sd->autogain_ignore_frames--;
 795        else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,
 796                        deadzone, GAIN_KNEE, EXPOSURE_KNEE))
 797                sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
 798}
 799
 800static const unsigned char pac7311_jpeg_header1[] = {
 801  0xff, 0xd8, 0xff, 0xc0, 0x00, 0x11, 0x08
 802};
 803
 804static const unsigned char pac7311_jpeg_header2[] = {
 805  0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, 0xff, 0xda,
 806  0x00, 0x0c, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, 0x00, 0x3f, 0x00
 807};
 808
 809/* this function is run at interrupt level */
 810static void sd_pkt_scan(struct gspca_dev *gspca_dev,
 811                        struct gspca_frame *frame,      /* target */
 812                        __u8 *data,                     /* isoc packet */
 813                        int len)                        /* iso packet length */
 814{
 815        struct sd *sd = (struct sd *) gspca_dev;
 816        unsigned char *sof;
 817
 818        sof = pac_find_sof(gspca_dev, data, len);
 819        if (sof) {
 820                unsigned char tmpbuf[4];
 821                int n, lum_offset, footer_length;
 822
 823                if (sd->sensor == SENSOR_PAC7302) {
 824                  /* 6 bytes after the FF D9 EOF marker a number of lumination
 825                     bytes are send corresponding to different parts of the
 826                     image, the 14th and 15th byte after the EOF seem to
 827                     correspond to the center of the image */
 828                  lum_offset = 61 + sizeof pac_sof_marker;
 829                  footer_length = 74;
 830                } else {
 831                  lum_offset = 24 + sizeof pac_sof_marker;
 832                  footer_length = 26;
 833                }
 834
 835                /* Finish decoding current frame */
 836                n = (sof - data) - (footer_length + sizeof pac_sof_marker);
 837                if (n < 0) {
 838                        frame->data_end += n;
 839                        n = 0;
 840                }
 841                frame = gspca_frame_add(gspca_dev, INTER_PACKET, frame,
 842                                        data, n);
 843                if (gspca_dev->last_packet_type != DISCARD_PACKET &&
 844                                frame->data_end[-2] == 0xff &&
 845                                frame->data_end[-1] == 0xd9)
 846                        frame = gspca_frame_add(gspca_dev, LAST_PACKET, frame,
 847                                                NULL, 0);
 848
 849                n = sof - data;
 850                len -= n;
 851                data = sof;
 852
 853                /* Get average lumination */
 854                if (gspca_dev->last_packet_type == LAST_PACKET &&
 855                                n >= lum_offset)
 856                        atomic_set(&sd->avg_lum, data[-lum_offset] +
 857                                                data[-lum_offset + 1]);
 858                else
 859                        atomic_set(&sd->avg_lum, -1);
 860
 861                /* Start the new frame with the jpeg header */
 862                gspca_frame_add(gspca_dev, FIRST_PACKET, frame,
 863                        pac7311_jpeg_header1, sizeof(pac7311_jpeg_header1));
 864                if (sd->sensor == SENSOR_PAC7302) {
 865                        /* The PAC7302 has the image rotated 90 degrees */
 866                        tmpbuf[0] = gspca_dev->width >> 8;
 867                        tmpbuf[1] = gspca_dev->width & 0xff;
 868                        tmpbuf[2] = gspca_dev->height >> 8;
 869                        tmpbuf[3] = gspca_dev->height & 0xff;
 870                } else {
 871                        tmpbuf[0] = gspca_dev->height >> 8;
 872                        tmpbuf[1] = gspca_dev->height & 0xff;
 873                        tmpbuf[2] = gspca_dev->width >> 8;
 874                        tmpbuf[3] = gspca_dev->width & 0xff;
 875                }
 876                gspca_frame_add(gspca_dev, INTER_PACKET, frame, tmpbuf, 4);
 877                gspca_frame_add(gspca_dev, INTER_PACKET, frame,
 878                        pac7311_jpeg_header2, sizeof(pac7311_jpeg_header2));
 879        }
 880        gspca_frame_add(gspca_dev, INTER_PACKET, frame, data, len);
 881}
 882
 883static int sd_setbrightness(struct gspca_dev *gspca_dev, __s32 val)
 884{
 885        struct sd *sd = (struct sd *) gspca_dev;
 886
 887        sd->brightness = val;
 888        if (gspca_dev->streaming)
 889                setbrightcont(gspca_dev);
 890        return 0;
 891}
 892
 893static int sd_getbrightness(struct gspca_dev *gspca_dev, __s32 *val)
 894{
 895        struct sd *sd = (struct sd *) gspca_dev;
 896
 897        *val = sd->brightness;
 898        return 0;
 899}
 900
 901static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)
 902{
 903        struct sd *sd = (struct sd *) gspca_dev;
 904
 905        sd->contrast = val;
 906        if (gspca_dev->streaming) {
 907                if (sd->sensor == SENSOR_PAC7302)
 908                        setbrightcont(gspca_dev);
 909                else
 910                        setcontrast(gspca_dev);
 911        }
 912        return 0;
 913}
 914
 915static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)
 916{
 917        struct sd *sd = (struct sd *) gspca_dev;
 918
 919        *val = sd->contrast;
 920        return 0;
 921}
 922
 923static int sd_setcolors(struct gspca_dev *gspca_dev, __s32 val)
 924{
 925        struct sd *sd = (struct sd *) gspca_dev;
 926
 927        sd->colors = val;
 928        if (gspca_dev->streaming)
 929                setcolors(gspca_dev);
 930        return 0;
 931}
 932
 933static int sd_getcolors(struct gspca_dev *gspca_dev, __s32 *val)
 934{
 935        struct sd *sd = (struct sd *) gspca_dev;
 936
 937        *val = sd->colors;
 938        return 0;
 939}
 940
 941static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)
 942{
 943        struct sd *sd = (struct sd *) gspca_dev;
 944
 945        sd->gain = val;
 946        if (gspca_dev->streaming)
 947                setgain(gspca_dev);
 948        return 0;
 949}
 950
 951static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)
 952{
 953        struct sd *sd = (struct sd *) gspca_dev;
 954
 955        *val = sd->gain;
 956        return 0;
 957}
 958
 959static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)
 960{
 961        struct sd *sd = (struct sd *) gspca_dev;
 962
 963        sd->exposure = val;
 964        if (gspca_dev->streaming)
 965                setexposure(gspca_dev);
 966        return 0;
 967}
 968
 969static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)
 970{
 971        struct sd *sd = (struct sd *) gspca_dev;
 972
 973        *val = sd->exposure;
 974        return 0;
 975}
 976
 977static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)
 978{
 979        struct sd *sd = (struct sd *) gspca_dev;
 980
 981        sd->autogain = val;
 982        /* when switching to autogain set defaults to make sure
 983           we are on a valid point of the autogain gain /
 984           exposure knee graph, and give this change time to
 985           take effect before doing autogain. */
 986        if (sd->autogain) {
 987                sd->exposure = EXPOSURE_DEF;
 988                sd->gain = GAIN_DEF;
 989                if (gspca_dev->streaming) {
 990                        sd->autogain_ignore_frames =
 991                                PAC_AUTOGAIN_IGNORE_FRAMES;
 992                        setexposure(gspca_dev);
 993                        setgain(gspca_dev);
 994                }
 995        }
 996
 997        return 0;
 998}
 999
1000static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)
1001{
1002        struct sd *sd = (struct sd *) gspca_dev;
1003
1004        *val = sd->autogain;
1005        return 0;
1006}
1007
1008static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)
1009{
1010        struct sd *sd = (struct sd *) gspca_dev;
1011
1012        sd->hflip = val;
1013        if (gspca_dev->streaming)
1014                sethvflip(gspca_dev);
1015        return 0;
1016}
1017
1018static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)
1019{
1020        struct sd *sd = (struct sd *) gspca_dev;
1021
1022        *val = sd->hflip;
1023        return 0;
1024}
1025
1026static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)
1027{
1028        struct sd *sd = (struct sd *) gspca_dev;
1029
1030        sd->vflip = val;
1031        if (gspca_dev->streaming)
1032                sethvflip(gspca_dev);
1033        return 0;
1034}
1035
1036static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)
1037{
1038        struct sd *sd = (struct sd *) gspca_dev;
1039
1040        *val = sd->vflip;
1041        return 0;
1042}
1043
1044/* sub-driver description */
1045static struct sd_desc sd_desc = {
1046        .name = MODULE_NAME,
1047        .ctrls = sd_ctrls,
1048        .nctrls = ARRAY_SIZE(sd_ctrls),
1049        .config = sd_config,
1050        .init = sd_init,
1051        .start = sd_start,
1052        .stopN = sd_stopN,
1053        .stop0 = sd_stop0,
1054        .pkt_scan = sd_pkt_scan,
1055        .dq_callback = do_autogain,
1056};
1057
1058/* -- module initialisation -- */
1059static __devinitdata struct usb_device_id device_table[] = {
1060        {USB_DEVICE(0x06f8, 0x3009), .driver_info = SENSOR_PAC7302},
1061        {USB_DEVICE(0x093a, 0x2600), .driver_info = SENSOR_PAC7311},
1062        {USB_DEVICE(0x093a, 0x2601), .driver_info = SENSOR_PAC7311},
1063        {USB_DEVICE(0x093a, 0x2603), .driver_info = SENSOR_PAC7311},
1064        {USB_DEVICE(0x093a, 0x2608), .driver_info = SENSOR_PAC7311},
1065        {USB_DEVICE(0x093a, 0x260e), .driver_info = SENSOR_PAC7311},
1066        {USB_DEVICE(0x093a, 0x260f), .driver_info = SENSOR_PAC7311},
1067        {USB_DEVICE(0x093a, 0x2620), .driver_info = SENSOR_PAC7302},
1068        {USB_DEVICE(0x093a, 0x2621), .driver_info = SENSOR_PAC7302},
1069        {USB_DEVICE(0x093a, 0x2622), .driver_info = SENSOR_PAC7302},
1070        {USB_DEVICE(0x093a, 0x2624), .driver_info = SENSOR_PAC7302},
1071        {USB_DEVICE(0x093a, 0x2626), .driver_info = SENSOR_PAC7302},
1072        {USB_DEVICE(0x093a, 0x2629), .driver_info = SENSOR_PAC7302},
1073        {USB_DEVICE(0x093a, 0x262a), .driver_info = SENSOR_PAC7302},
1074        {USB_DEVICE(0x093a, 0x262c), .driver_info = SENSOR_PAC7302},
1075        {}
1076};
1077MODULE_DEVICE_TABLE(usb, device_table);
1078
1079/* -- device connect -- */
1080static int sd_probe(struct usb_interface *intf,
1081                        const struct usb_device_id *id)
1082{
1083        return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
1084                                THIS_MODULE);
1085}
1086
1087static struct usb_driver sd_driver = {
1088        .name = MODULE_NAME,
1089        .id_table = device_table,
1090        .probe = sd_probe,
1091        .disconnect = gspca_disconnect,
1092#ifdef CONFIG_PM
1093        .suspend = gspca_suspend,
1094        .resume = gspca_resume,
1095#endif
1096};
1097
1098/* -- module insert / remove -- */
1099static int __init sd_mod_init(void)
1100{
1101        int ret;
1102        ret = usb_register(&sd_driver);
1103        if (ret < 0)
1104                return ret;
1105        PDEBUG(D_PROBE, "registered");
1106        return 0;
1107}
1108static void __exit sd_mod_exit(void)
1109{
1110        usb_deregister(&sd_driver);
1111        PDEBUG(D_PROBE, "deregistered");
1112}
1113
1114module_init(sd_mod_init);
1115module_exit(sd_mod_exit);
1116
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.