0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
0048
0049 #define MODULE_NAME "pac7311"
0050
0051 #include <linux/input.h>
0052 #include "gspca.h"
0053
0054 #include "pac_common.h"
0055
0056 #define PAC7311_GAIN_DEFAULT 122
0057 #define PAC7311_EXPOSURE_DEFAULT 3
0058
0059 MODULE_AUTHOR("Thomas Kaiser thomas@kaiser-linux.li");
0060 MODULE_DESCRIPTION("Pixart PAC7311");
0061 MODULE_LICENSE("GPL");
0062
0063 struct sd {
0064 struct gspca_dev gspca_dev;
0065
0066 struct v4l2_ctrl *contrast;
0067 struct v4l2_ctrl *hflip;
0068
0069 u8 sof_read;
0070 u8 autogain_ignore_frames;
0071
0072 atomic_t avg_lum;
0073 };
0074
0075 static const struct v4l2_pix_format vga_mode[] = {
0076 {160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
0077 .bytesperline = 160,
0078 .sizeimage = 160 * 120 * 3 / 8 + 590,
0079 .colorspace = V4L2_COLORSPACE_JPEG,
0080 .priv = 2},
0081 {320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
0082 .bytesperline = 320,
0083 .sizeimage = 320 * 240 * 3 / 8 + 590,
0084 .colorspace = V4L2_COLORSPACE_JPEG,
0085 .priv = 1},
0086 {640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,
0087 .bytesperline = 640,
0088 .sizeimage = 640 * 480 * 3 / 8 + 590,
0089 .colorspace = V4L2_COLORSPACE_JPEG,
0090 .priv = 0},
0091 };
0092
0093 #define LOAD_PAGE4 254
0094 #define END_OF_SEQUENCE 0
0095
0096 static const __u8 init_7311[] = {
0097 0xff, 0x01,
0098 0x78, 0x40,
0099 0x78, 0x40,
0100 0x78, 0x44,
0101 0xff, 0x04,
0102 0x27, 0x80,
0103 0x28, 0xca,
0104 0x29, 0x53,
0105 0x2a, 0x0e,
0106 0xff, 0x01,
0107 0x3e, 0x20,
0108 };
0109
0110 static const __u8 start_7311[] = {
0111
0112 0xff, 1, 0x01,
0113 0x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00,
0114 0x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c,
0115 0x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10,
0116 0x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00,
0117 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0118 0x00, 0x00, 0x00,
0119 0x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e,
0120 0x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49,
0121 0x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48,
0122 0x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78,
0123 0x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b,
0124 0xd0, 0xff,
0125 0x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80,
0126 0x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84,
0127 0x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14,
0128 0x18, 0x20,
0129 0x96, 3, 0x01, 0x08, 0x04,
0130 0xa0, 4, 0x44, 0x44, 0x44, 0x04,
0131 0xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,
0132 0x3f, 0x00, 0x0a, 0x01, 0x00,
0133 0xff, 1, 0x04,
0134 0, LOAD_PAGE4,
0135 0x11, 1, 0x01,
0136 0, END_OF_SEQUENCE
0137 };
0138
0139 #define SKIP 0xaa
0140
0141 static const __u8 page4_7311[] = {
0142 SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,
0143 0x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,
0144 0x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,
0145 0x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,
0146 SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,
0147 0xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,
0148 0x23, 0x28, 0x04, 0x11, 0x00, 0x00
0149 };
0150
0151 static void reg_w_buf(struct gspca_dev *gspca_dev,
0152 __u8 index,
0153 const u8 *buffer, int len)
0154 {
0155 int ret;
0156
0157 if (gspca_dev->usb_err < 0)
0158 return;
0159 memcpy(gspca_dev->usb_buf, buffer, len);
0160 ret = usb_control_msg(gspca_dev->dev,
0161 usb_sndctrlpipe(gspca_dev->dev, 0),
0162 0,
0163 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0164 0,
0165 index, gspca_dev->usb_buf, len,
0166 500);
0167 if (ret < 0) {
0168 pr_err("reg_w_buf() failed index 0x%02x, error %d\n",
0169 index, ret);
0170 gspca_dev->usb_err = ret;
0171 }
0172 }
0173
0174
0175 static void reg_w(struct gspca_dev *gspca_dev,
0176 __u8 index,
0177 __u8 value)
0178 {
0179 int ret;
0180
0181 if (gspca_dev->usb_err < 0)
0182 return;
0183 gspca_dev->usb_buf[0] = value;
0184 ret = usb_control_msg(gspca_dev->dev,
0185 usb_sndctrlpipe(gspca_dev->dev, 0),
0186 0,
0187 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0188 0, index, gspca_dev->usb_buf, 1,
0189 500);
0190 if (ret < 0) {
0191 pr_err("reg_w() failed index 0x%02x, value 0x%02x, error %d\n",
0192 index, value, ret);
0193 gspca_dev->usb_err = ret;
0194 }
0195 }
0196
0197 static void reg_w_seq(struct gspca_dev *gspca_dev,
0198 const __u8 *seq, int len)
0199 {
0200 while (--len >= 0) {
0201 reg_w(gspca_dev, seq[0], seq[1]);
0202 seq += 2;
0203 }
0204 }
0205
0206
0207 static void reg_w_page(struct gspca_dev *gspca_dev,
0208 const __u8 *page, int len)
0209 {
0210 int index;
0211 int ret = 0;
0212
0213 if (gspca_dev->usb_err < 0)
0214 return;
0215 for (index = 0; index < len; index++) {
0216 if (page[index] == SKIP)
0217 continue;
0218 gspca_dev->usb_buf[0] = page[index];
0219 ret = usb_control_msg(gspca_dev->dev,
0220 usb_sndctrlpipe(gspca_dev->dev, 0),
0221 0,
0222 USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
0223 0, index, gspca_dev->usb_buf, 1,
0224 500);
0225 if (ret < 0) {
0226 pr_err("reg_w_page() failed index 0x%02x, value 0x%02x, error %d\n",
0227 index, page[index], ret);
0228 gspca_dev->usb_err = ret;
0229 break;
0230 }
0231 }
0232 }
0233
0234
0235 static void reg_w_var(struct gspca_dev *gspca_dev,
0236 const __u8 *seq,
0237 const __u8 *page4, unsigned int page4_len)
0238 {
0239 int index, len;
0240
0241 for (;;) {
0242 index = *seq++;
0243 len = *seq++;
0244 switch (len) {
0245 case END_OF_SEQUENCE:
0246 return;
0247 case LOAD_PAGE4:
0248 reg_w_page(gspca_dev, page4, page4_len);
0249 break;
0250 default:
0251 if (len > USB_BUF_SZ) {
0252 gspca_err(gspca_dev, "Incorrect variable sequence\n");
0253 return;
0254 }
0255 while (len > 0) {
0256 if (len < 8) {
0257 reg_w_buf(gspca_dev,
0258 index, seq, len);
0259 seq += len;
0260 break;
0261 }
0262 reg_w_buf(gspca_dev, index, seq, 8);
0263 seq += 8;
0264 index += 8;
0265 len -= 8;
0266 }
0267 }
0268 }
0269
0270 }
0271
0272
0273 static int sd_config(struct gspca_dev *gspca_dev,
0274 const struct usb_device_id *id)
0275 {
0276 struct cam *cam = &gspca_dev->cam;
0277
0278 cam->cam_mode = vga_mode;
0279 cam->nmodes = ARRAY_SIZE(vga_mode);
0280 cam->input_flags = V4L2_IN_ST_VFLIP;
0281
0282 return 0;
0283 }
0284
0285 static void setcontrast(struct gspca_dev *gspca_dev, s32 val)
0286 {
0287 reg_w(gspca_dev, 0xff, 0x04);
0288 reg_w(gspca_dev, 0x10, val);
0289
0290 reg_w(gspca_dev, 0x11, 0x01);
0291 }
0292
0293 static void setgain(struct gspca_dev *gspca_dev, s32 val)
0294 {
0295 reg_w(gspca_dev, 0xff, 0x04);
0296 reg_w(gspca_dev, 0x0e, 0x00);
0297 reg_w(gspca_dev, 0x0f, gspca_dev->gain->maximum - val + 1);
0298
0299
0300 reg_w(gspca_dev, 0x11, 0x01);
0301 }
0302
0303 static void setexposure(struct gspca_dev *gspca_dev, s32 val)
0304 {
0305 reg_w(gspca_dev, 0xff, 0x04);
0306 reg_w(gspca_dev, 0x02, val);
0307
0308
0309 reg_w(gspca_dev, 0x11, 0x01);
0310
0311
0312
0313
0314
0315 reg_w(gspca_dev, 0xff, 0x01);
0316 if (gspca_dev->pixfmt.width != 640 && val <= 3)
0317 reg_w(gspca_dev, 0x08, 0x09);
0318 else
0319 reg_w(gspca_dev, 0x08, 0x08);
0320
0321
0322
0323
0324
0325
0326
0327 if (gspca_dev->pixfmt.width == 640 && val == 2)
0328 reg_w(gspca_dev, 0x80, 0x01);
0329 else
0330 reg_w(gspca_dev, 0x80, 0x1c);
0331
0332
0333 reg_w(gspca_dev, 0x11, 0x01);
0334 }
0335
0336 static void sethvflip(struct gspca_dev *gspca_dev, s32 hflip, s32 vflip)
0337 {
0338 __u8 data;
0339
0340 reg_w(gspca_dev, 0xff, 0x04);
0341 data = (hflip ? 0x04 : 0x00) |
0342 (vflip ? 0x08 : 0x00);
0343 reg_w(gspca_dev, 0x21, data);
0344
0345
0346 reg_w(gspca_dev, 0x11, 0x01);
0347 }
0348
0349
0350 static int sd_init(struct gspca_dev *gspca_dev)
0351 {
0352 reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);
0353 return gspca_dev->usb_err;
0354 }
0355
0356 static int sd_s_ctrl(struct v4l2_ctrl *ctrl)
0357 {
0358 struct gspca_dev *gspca_dev =
0359 container_of(ctrl->handler, struct gspca_dev, ctrl_handler);
0360 struct sd *sd = (struct sd *)gspca_dev;
0361
0362 gspca_dev->usb_err = 0;
0363
0364 if (ctrl->id == V4L2_CID_AUTOGAIN && ctrl->is_new && ctrl->val) {
0365
0366
0367
0368
0369 gspca_dev->exposure->val = PAC7311_EXPOSURE_DEFAULT;
0370 gspca_dev->gain->val = PAC7311_GAIN_DEFAULT;
0371 sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
0372 }
0373
0374 if (!gspca_dev->streaming)
0375 return 0;
0376
0377 switch (ctrl->id) {
0378 case V4L2_CID_CONTRAST:
0379 setcontrast(gspca_dev, ctrl->val);
0380 break;
0381 case V4L2_CID_AUTOGAIN:
0382 if (gspca_dev->exposure->is_new || (ctrl->is_new && ctrl->val))
0383 setexposure(gspca_dev, gspca_dev->exposure->val);
0384 if (gspca_dev->gain->is_new || (ctrl->is_new && ctrl->val))
0385 setgain(gspca_dev, gspca_dev->gain->val);
0386 break;
0387 case V4L2_CID_HFLIP:
0388 sethvflip(gspca_dev, sd->hflip->val, 1);
0389 break;
0390 default:
0391 return -EINVAL;
0392 }
0393 return gspca_dev->usb_err;
0394 }
0395
0396 static const struct v4l2_ctrl_ops sd_ctrl_ops = {
0397 .s_ctrl = sd_s_ctrl,
0398 };
0399
0400
0401 static int sd_init_controls(struct gspca_dev *gspca_dev)
0402 {
0403 struct sd *sd = (struct sd *) gspca_dev;
0404 struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler;
0405
0406 gspca_dev->vdev.ctrl_handler = hdl;
0407 v4l2_ctrl_handler_init(hdl, 5);
0408
0409 sd->contrast = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0410 V4L2_CID_CONTRAST, 0, 15, 1, 7);
0411 gspca_dev->autogain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0412 V4L2_CID_AUTOGAIN, 0, 1, 1, 1);
0413 gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0414 V4L2_CID_EXPOSURE, 2, 63, 1,
0415 PAC7311_EXPOSURE_DEFAULT);
0416 gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0417 V4L2_CID_GAIN, 0, 244, 1,
0418 PAC7311_GAIN_DEFAULT);
0419 sd->hflip = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops,
0420 V4L2_CID_HFLIP, 0, 1, 1, 0);
0421
0422 if (hdl->error) {
0423 pr_err("Could not initialize controls\n");
0424 return hdl->error;
0425 }
0426
0427 v4l2_ctrl_auto_cluster(3, &gspca_dev->autogain, 0, false);
0428 return 0;
0429 }
0430
0431
0432 static int sd_start(struct gspca_dev *gspca_dev)
0433 {
0434 struct sd *sd = (struct sd *) gspca_dev;
0435
0436 sd->sof_read = 0;
0437
0438 reg_w_var(gspca_dev, start_7311,
0439 page4_7311, sizeof(page4_7311));
0440 setcontrast(gspca_dev, v4l2_ctrl_g_ctrl(sd->contrast));
0441 setgain(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->gain));
0442 setexposure(gspca_dev, v4l2_ctrl_g_ctrl(gspca_dev->exposure));
0443 sethvflip(gspca_dev, v4l2_ctrl_g_ctrl(sd->hflip), 1);
0444
0445
0446 switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {
0447 case 2:
0448 reg_w(gspca_dev, 0xff, 0x01);
0449 reg_w(gspca_dev, 0x17, 0x20);
0450 reg_w(gspca_dev, 0x87, 0x10);
0451 break;
0452 case 1:
0453 reg_w(gspca_dev, 0xff, 0x01);
0454 reg_w(gspca_dev, 0x17, 0x30);
0455 reg_w(gspca_dev, 0x87, 0x11);
0456 break;
0457 case 0:
0458 reg_w(gspca_dev, 0xff, 0x01);
0459 reg_w(gspca_dev, 0x17, 0x00);
0460 reg_w(gspca_dev, 0x87, 0x12);
0461 break;
0462 }
0463
0464 sd->sof_read = 0;
0465 sd->autogain_ignore_frames = 0;
0466 atomic_set(&sd->avg_lum, -1);
0467
0468
0469 reg_w(gspca_dev, 0xff, 0x01);
0470 reg_w(gspca_dev, 0x78, 0x05);
0471
0472 return gspca_dev->usb_err;
0473 }
0474
0475 static void sd_stopN(struct gspca_dev *gspca_dev)
0476 {
0477 reg_w(gspca_dev, 0xff, 0x04);
0478 reg_w(gspca_dev, 0x27, 0x80);
0479 reg_w(gspca_dev, 0x28, 0xca);
0480 reg_w(gspca_dev, 0x29, 0x53);
0481 reg_w(gspca_dev, 0x2a, 0x0e);
0482 reg_w(gspca_dev, 0xff, 0x01);
0483 reg_w(gspca_dev, 0x3e, 0x20);
0484 reg_w(gspca_dev, 0x78, 0x44);
0485 reg_w(gspca_dev, 0x78, 0x44);
0486 reg_w(gspca_dev, 0x78, 0x44);
0487 }
0488
0489 static void do_autogain(struct gspca_dev *gspca_dev)
0490 {
0491 struct sd *sd = (struct sd *) gspca_dev;
0492 int avg_lum = atomic_read(&sd->avg_lum);
0493 int desired_lum, deadzone;
0494
0495 if (avg_lum == -1)
0496 return;
0497
0498 desired_lum = 170;
0499 deadzone = 20;
0500
0501 if (sd->autogain_ignore_frames > 0)
0502 sd->autogain_ignore_frames--;
0503 else if (gspca_coarse_grained_expo_autogain(gspca_dev, avg_lum,
0504 desired_lum, deadzone))
0505 sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;
0506 }
0507
0508
0509 static const unsigned char pac_jpeg_header1[] = {
0510 0xff, 0xd8,
0511
0512 0xff, 0xc0,
0513 0x00, 0x11,
0514 0x08
0515
0516
0517 };
0518
0519
0520 static const unsigned char pac_jpeg_header2[] = {
0521 0x03,
0522 0x01, 0x21, 0x00,
0523 0x02, 0x11, 0x01,
0524 0x03, 0x11, 0x01,
0525
0526 0xff, 0xda,
0527 0x00, 0x0c,
0528 0x03,
0529 0x01, 0x00,
0530 0x02, 0x11,
0531 0x03, 0x11,
0532 0x00, 0x3f,
0533 0x00
0534 };
0535
0536 static void pac_start_frame(struct gspca_dev *gspca_dev,
0537 __u16 lines, __u16 samples_per_line)
0538 {
0539 unsigned char tmpbuf[4];
0540
0541 gspca_frame_add(gspca_dev, FIRST_PACKET,
0542 pac_jpeg_header1, sizeof(pac_jpeg_header1));
0543
0544 tmpbuf[0] = lines >> 8;
0545 tmpbuf[1] = lines & 0xff;
0546 tmpbuf[2] = samples_per_line >> 8;
0547 tmpbuf[3] = samples_per_line & 0xff;
0548
0549 gspca_frame_add(gspca_dev, INTER_PACKET,
0550 tmpbuf, sizeof(tmpbuf));
0551 gspca_frame_add(gspca_dev, INTER_PACKET,
0552 pac_jpeg_header2, sizeof(pac_jpeg_header2));
0553 }
0554
0555
0556 static void sd_pkt_scan(struct gspca_dev *gspca_dev,
0557 u8 *data,
0558 int len)
0559 {
0560 struct sd *sd = (struct sd *) gspca_dev;
0561 u8 *image;
0562 unsigned char *sof;
0563
0564 sof = pac_find_sof(gspca_dev, &sd->sof_read, data, len);
0565 if (sof) {
0566 int n, lum_offset, footer_length;
0567
0568
0569
0570
0571
0572
0573
0574 lum_offset = 24 + sizeof pac_sof_marker;
0575 footer_length = 26;
0576
0577
0578 n = (sof - data) - (footer_length + sizeof pac_sof_marker);
0579 if (n < 0) {
0580 gspca_dev->image_len += n;
0581 n = 0;
0582 } else {
0583 gspca_frame_add(gspca_dev, INTER_PACKET, data, n);
0584 }
0585 image = gspca_dev->image;
0586 if (image != NULL
0587 && image[gspca_dev->image_len - 2] == 0xff
0588 && image[gspca_dev->image_len - 1] == 0xd9)
0589 gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);
0590
0591 n = sof - data;
0592 len -= n;
0593 data = sof;
0594
0595
0596 if (gspca_dev->last_packet_type == LAST_PACKET &&
0597 n >= lum_offset)
0598 atomic_set(&sd->avg_lum, data[-lum_offset] +
0599 data[-lum_offset + 1]);
0600 else
0601 atomic_set(&sd->avg_lum, -1);
0602
0603
0604 pac_start_frame(gspca_dev,
0605 gspca_dev->pixfmt.height, gspca_dev->pixfmt.width);
0606 }
0607 gspca_frame_add(gspca_dev, INTER_PACKET, data, len);
0608 }
0609
0610 #if IS_ENABLED(CONFIG_INPUT)
0611 static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,
0612 u8 *data,
0613 int len)
0614 {
0615 int ret = -EINVAL;
0616 u8 data0, data1;
0617
0618 if (len == 2) {
0619 data0 = data[0];
0620 data1 = data[1];
0621 if ((data0 == 0x00 && data1 == 0x11) ||
0622 (data0 == 0x22 && data1 == 0x33) ||
0623 (data0 == 0x44 && data1 == 0x55) ||
0624 (data0 == 0x66 && data1 == 0x77) ||
0625 (data0 == 0x88 && data1 == 0x99) ||
0626 (data0 == 0xaa && data1 == 0xbb) ||
0627 (data0 == 0xcc && data1 == 0xdd) ||
0628 (data0 == 0xee && data1 == 0xff)) {
0629 input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);
0630 input_sync(gspca_dev->input_dev);
0631 input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);
0632 input_sync(gspca_dev->input_dev);
0633 ret = 0;
0634 }
0635 }
0636
0637 return ret;
0638 }
0639 #endif
0640
0641 static const struct sd_desc sd_desc = {
0642 .name = MODULE_NAME,
0643 .config = sd_config,
0644 .init = sd_init,
0645 .init_controls = sd_init_controls,
0646 .start = sd_start,
0647 .stopN = sd_stopN,
0648 .pkt_scan = sd_pkt_scan,
0649 .dq_callback = do_autogain,
0650 #if IS_ENABLED(CONFIG_INPUT)
0651 .int_pkt_scan = sd_int_pkt_scan,
0652 #endif
0653 };
0654
0655
0656 static const struct usb_device_id device_table[] = {
0657 {USB_DEVICE(0x093a, 0x2600)},
0658 {USB_DEVICE(0x093a, 0x2601)},
0659 {USB_DEVICE(0x093a, 0x2603)},
0660 {USB_DEVICE(0x093a, 0x2608)},
0661 {USB_DEVICE(0x093a, 0x260e)},
0662 {USB_DEVICE(0x093a, 0x260f)},
0663 {}
0664 };
0665 MODULE_DEVICE_TABLE(usb, device_table);
0666
0667
0668 static int sd_probe(struct usb_interface *intf,
0669 const struct usb_device_id *id)
0670 {
0671 return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
0672 THIS_MODULE);
0673 }
0674
0675 static struct usb_driver sd_driver = {
0676 .name = MODULE_NAME,
0677 .id_table = device_table,
0678 .probe = sd_probe,
0679 .disconnect = gspca_disconnect,
0680 #ifdef CONFIG_PM
0681 .suspend = gspca_suspend,
0682 .resume = gspca_resume,
0683 .reset_resume = gspca_resume,
0684 #endif
0685 };
0686
0687 module_usb_driver(sd_driver);