Path: blob/master/drivers/media/video/gspca/pac7311.c
17613 views
/*1* Pixart PAC7311 library2* Copyright (C) 2005 Thomas Kaiser [email protected]3*4* V4L2 by Jean-Francois Moine <http://moinejf.free.fr>5*6* This program is free software; you can redistribute it and/or modify7* it under the terms of the GNU General Public License as published by8* the Free Software Foundation; either version 2 of the License, or9* any later version.10*11* This program is distributed in the hope that it will be useful,12* but WITHOUT ANY WARRANTY; without even the implied warranty of13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the14* GNU General Public License for more details.15*16* You should have received a copy of the GNU General Public License17* along with this program; if not, write to the Free Software18* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA19*/2021/* Some documentation about various registers as determined by trial and error.22When the register addresses differ between the 7202 and the 7311 the 223different addresses are written as 7302addr/7311addr, when one of the 224addresses is a - sign that register description is not valid for the25matching IC.2627Register page 1:2829Address Description30-/0x08 Unknown compressor related, must always be 8 except when not31in 640x480 resolution and page 4 reg 2 <= 3 then set it to 9 !32-/0x1b Auto white balance related, bit 0 is AWB enable (inverted)33bits 345 seem to toggle per color gains on/off (inverted)340x78 Global control, bit 6 controls the LED (inverted)35-/0x80 JPEG compression ratio ? Best not touched3637Register page 3/4:3839Address Description400x02 Clock divider 2-63, fps =~ 60 / val. Must be a multiple of 3 on41the 7302, so one of 3, 6, 9, ..., except when between 6 and 12?42-/0x0f Master gain 1-245, low value = high gain430x10/- Master gain 0-3144-/0x10 Another gain 0-15, limited influence (1-2x gain I guess)450x21 Bitfield: 0-1 unused, 2-3 vflip/hflip, 4-5 unknown, 6-7 unused46-/0x27 Seems to toggle various gains on / off, Setting bit 7 seems to47completely disable the analog amplification block. Set to 0x6848for max gain, 0x14 for minimal gain.49*/5051#define MODULE_NAME "pac7311"5253#include <linux/input.h>54#include "gspca.h"5556MODULE_AUTHOR("Thomas Kaiser [email protected]");57MODULE_DESCRIPTION("Pixart PAC7311");58MODULE_LICENSE("GPL");5960/* specific webcam descriptor for pac7311 */61struct sd {62struct gspca_dev gspca_dev; /* !! must be the first item */6364unsigned char contrast;65unsigned char gain;66unsigned char exposure;67unsigned char autogain;68__u8 hflip;69__u8 vflip;7071u8 sof_read;72u8 autogain_ignore_frames;7374atomic_t avg_lum;75};7677/* V4L2 controls supported by the driver */78static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val);79static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val);80static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val);81static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val);82static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val);83static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val);84static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val);85static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val);86static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val);87static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val);88static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val);89static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val);9091static const struct ctrl sd_ctrls[] = {92/* This control is for both the 7302 and the 7311 */93{94{95.id = V4L2_CID_CONTRAST,96.type = V4L2_CTRL_TYPE_INTEGER,97.name = "Contrast",98.minimum = 0,99#define CONTRAST_MAX 255100.maximum = CONTRAST_MAX,101.step = 1,102#define CONTRAST_DEF 127103.default_value = CONTRAST_DEF,104},105.set = sd_setcontrast,106.get = sd_getcontrast,107},108/* All controls below are for both the 7302 and the 7311 */109{110{111.id = V4L2_CID_GAIN,112.type = V4L2_CTRL_TYPE_INTEGER,113.name = "Gain",114.minimum = 0,115#define GAIN_MAX 255116.maximum = GAIN_MAX,117.step = 1,118#define GAIN_DEF 127119#define GAIN_KNEE 255 /* Gain seems to cause little noise on the pac73xx */120.default_value = GAIN_DEF,121},122.set = sd_setgain,123.get = sd_getgain,124},125{126{127.id = V4L2_CID_EXPOSURE,128.type = V4L2_CTRL_TYPE_INTEGER,129.name = "Exposure",130.minimum = 0,131#define EXPOSURE_MAX 255132.maximum = EXPOSURE_MAX,133.step = 1,134#define EXPOSURE_DEF 16 /* 32 ms / 30 fps */135#define EXPOSURE_KNEE 50 /* 100 ms / 10 fps */136.default_value = EXPOSURE_DEF,137},138.set = sd_setexposure,139.get = sd_getexposure,140},141{142{143.id = V4L2_CID_AUTOGAIN,144.type = V4L2_CTRL_TYPE_BOOLEAN,145.name = "Auto Gain",146.minimum = 0,147.maximum = 1,148.step = 1,149#define AUTOGAIN_DEF 1150.default_value = AUTOGAIN_DEF,151},152.set = sd_setautogain,153.get = sd_getautogain,154},155{156{157.id = V4L2_CID_HFLIP,158.type = V4L2_CTRL_TYPE_BOOLEAN,159.name = "Mirror",160.minimum = 0,161.maximum = 1,162.step = 1,163#define HFLIP_DEF 0164.default_value = HFLIP_DEF,165},166.set = sd_sethflip,167.get = sd_gethflip,168},169{170{171.id = V4L2_CID_VFLIP,172.type = V4L2_CTRL_TYPE_BOOLEAN,173.name = "Vflip",174.minimum = 0,175.maximum = 1,176.step = 1,177#define VFLIP_DEF 0178.default_value = VFLIP_DEF,179},180.set = sd_setvflip,181.get = sd_getvflip,182},183};184185static const struct v4l2_pix_format vga_mode[] = {186{160, 120, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,187.bytesperline = 160,188.sizeimage = 160 * 120 * 3 / 8 + 590,189.colorspace = V4L2_COLORSPACE_JPEG,190.priv = 2},191{320, 240, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,192.bytesperline = 320,193.sizeimage = 320 * 240 * 3 / 8 + 590,194.colorspace = V4L2_COLORSPACE_JPEG,195.priv = 1},196{640, 480, V4L2_PIX_FMT_PJPG, V4L2_FIELD_NONE,197.bytesperline = 640,198.sizeimage = 640 * 480 * 3 / 8 + 590,199.colorspace = V4L2_COLORSPACE_JPEG,200.priv = 0},201};202203#define LOAD_PAGE4 254204#define END_OF_SEQUENCE 0205206/* pac 7311 */207static const __u8 init_7311[] = {2080x78, 0x40, /* Bit_0=start stream, Bit_6=LED */2090x78, 0x40, /* Bit_0=start stream, Bit_6=LED */2100x78, 0x44, /* Bit_0=start stream, Bit_6=LED */2110xff, 0x04,2120x27, 0x80,2130x28, 0xca,2140x29, 0x53,2150x2a, 0x0e,2160xff, 0x01,2170x3e, 0x20,218};219220static const __u8 start_7311[] = {221/* index, len, [value]* */2220xff, 1, 0x01, /* page 1 */2230x02, 43, 0x48, 0x0a, 0x40, 0x08, 0x00, 0x00, 0x08, 0x00,2240x06, 0xff, 0x11, 0xff, 0x5a, 0x30, 0x90, 0x4c,2250x00, 0x07, 0x00, 0x0a, 0x10, 0x00, 0xa0, 0x10,2260x02, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x01, 0x00,2270x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,2280x00, 0x00, 0x00,2290x3e, 42, 0x00, 0x00, 0x78, 0x52, 0x4a, 0x52, 0x78, 0x6e,2300x48, 0x46, 0x48, 0x6e, 0x5f, 0x49, 0x42, 0x49,2310x5f, 0x5f, 0x49, 0x42, 0x49, 0x5f, 0x6e, 0x48,2320x46, 0x48, 0x6e, 0x78, 0x52, 0x4a, 0x52, 0x78,2330x00, 0x00, 0x09, 0x1b, 0x34, 0x49, 0x5c, 0x9b,2340xd0, 0xff,2350x78, 6, 0x44, 0x00, 0xf2, 0x01, 0x01, 0x80,2360x7f, 18, 0x2a, 0x1c, 0x00, 0xc8, 0x02, 0x58, 0x03, 0x84,2370x12, 0x00, 0x1a, 0x04, 0x08, 0x0c, 0x10, 0x14,2380x18, 0x20,2390x96, 3, 0x01, 0x08, 0x04,2400xa0, 4, 0x44, 0x44, 0x44, 0x04,2410xf0, 13, 0x01, 0x00, 0x00, 0x00, 0x22, 0x00, 0x20, 0x00,2420x3f, 0x00, 0x0a, 0x01, 0x00,2430xff, 1, 0x04, /* page 4 */2440, LOAD_PAGE4, /* load the page 4 */2450x11, 1, 0x01,2460, END_OF_SEQUENCE /* end of sequence */247};248249#define SKIP 0xaa250/* page 4 - the value SKIP says skip the index - see reg_w_page() */251static const __u8 page4_7311[] = {252SKIP, SKIP, 0x04, 0x54, 0x07, 0x2b, 0x09, 0x0f,2530x09, 0x00, SKIP, SKIP, 0x07, 0x00, 0x00, 0x62,2540x08, SKIP, 0x07, 0x00, 0x00, 0x00, 0x00, 0x00,2550x00, 0x00, 0x00, 0x03, 0xa0, 0x01, 0xf4, SKIP,256SKIP, 0x00, 0x08, SKIP, 0x03, SKIP, 0x00, 0x68,2570xca, 0x10, 0x06, 0x78, 0x00, 0x00, 0x00, 0x00,2580x23, 0x28, 0x04, 0x11, 0x00, 0x00259};260261static void reg_w_buf(struct gspca_dev *gspca_dev,262__u8 index,263const u8 *buffer, int len)264{265int ret;266267if (gspca_dev->usb_err < 0)268return;269memcpy(gspca_dev->usb_buf, buffer, len);270ret = usb_control_msg(gspca_dev->dev,271usb_sndctrlpipe(gspca_dev->dev, 0),2720, /* request */273USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,2740, /* value */275index, gspca_dev->usb_buf, len,276500);277if (ret < 0) {278err("reg_w_buf() failed index 0x%02x, error %d",279index, ret);280gspca_dev->usb_err = ret;281}282}283284285static void reg_w(struct gspca_dev *gspca_dev,286__u8 index,287__u8 value)288{289int ret;290291if (gspca_dev->usb_err < 0)292return;293gspca_dev->usb_buf[0] = value;294ret = usb_control_msg(gspca_dev->dev,295usb_sndctrlpipe(gspca_dev->dev, 0),2960, /* request */297USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,2980, index, gspca_dev->usb_buf, 1,299500);300if (ret < 0) {301err("reg_w() failed index 0x%02x, value 0x%02x, error %d",302index, value, ret);303gspca_dev->usb_err = ret;304}305}306307static void reg_w_seq(struct gspca_dev *gspca_dev,308const __u8 *seq, int len)309{310while (--len >= 0) {311reg_w(gspca_dev, seq[0], seq[1]);312seq += 2;313}314}315316/* load the beginning of a page */317static void reg_w_page(struct gspca_dev *gspca_dev,318const __u8 *page, int len)319{320int index;321int ret = 0;322323if (gspca_dev->usb_err < 0)324return;325for (index = 0; index < len; index++) {326if (page[index] == SKIP) /* skip this index */327continue;328gspca_dev->usb_buf[0] = page[index];329ret = usb_control_msg(gspca_dev->dev,330usb_sndctrlpipe(gspca_dev->dev, 0),3310, /* request */332USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,3330, index, gspca_dev->usb_buf, 1,334500);335if (ret < 0) {336err("reg_w_page() failed index 0x%02x, "337"value 0x%02x, error %d",338index, page[index], ret);339gspca_dev->usb_err = ret;340break;341}342}343}344345/* output a variable sequence */346static void reg_w_var(struct gspca_dev *gspca_dev,347const __u8 *seq,348const __u8 *page4, unsigned int page4_len)349{350int index, len;351352for (;;) {353index = *seq++;354len = *seq++;355switch (len) {356case END_OF_SEQUENCE:357return;358case LOAD_PAGE4:359reg_w_page(gspca_dev, page4, page4_len);360break;361default:362if (len > USB_BUF_SZ) {363PDEBUG(D_ERR|D_STREAM,364"Incorrect variable sequence");365return;366}367while (len > 0) {368if (len < 8) {369reg_w_buf(gspca_dev,370index, seq, len);371seq += len;372break;373}374reg_w_buf(gspca_dev, index, seq, 8);375seq += 8;376index += 8;377len -= 8;378}379}380}381/* not reached */382}383384/* this function is called at probe time for pac7311 */385static int sd_config(struct gspca_dev *gspca_dev,386const struct usb_device_id *id)387{388struct sd *sd = (struct sd *) gspca_dev;389struct cam *cam;390391cam = &gspca_dev->cam;392393PDEBUG(D_CONF, "Find Sensor PAC7311");394cam->cam_mode = vga_mode;395cam->nmodes = ARRAY_SIZE(vga_mode);396397sd->contrast = CONTRAST_DEF;398sd->gain = GAIN_DEF;399sd->exposure = EXPOSURE_DEF;400sd->autogain = AUTOGAIN_DEF;401sd->hflip = HFLIP_DEF;402sd->vflip = VFLIP_DEF;403return 0;404}405406/* This function is used by pac7311 only */407static void setcontrast(struct gspca_dev *gspca_dev)408{409struct sd *sd = (struct sd *) gspca_dev;410411reg_w(gspca_dev, 0xff, 0x04);412reg_w(gspca_dev, 0x10, sd->contrast >> 4);413/* load registers to sensor (Bit 0, auto clear) */414reg_w(gspca_dev, 0x11, 0x01);415}416417static void setgain(struct gspca_dev *gspca_dev)418{419struct sd *sd = (struct sd *) gspca_dev;420int gain = GAIN_MAX - sd->gain;421422if (gain < 1)423gain = 1;424else if (gain > 245)425gain = 245;426reg_w(gspca_dev, 0xff, 0x04); /* page 4 */427reg_w(gspca_dev, 0x0e, 0x00);428reg_w(gspca_dev, 0x0f, gain);429430/* load registers to sensor (Bit 0, auto clear) */431reg_w(gspca_dev, 0x11, 0x01);432}433434static void setexposure(struct gspca_dev *gspca_dev)435{436struct sd *sd = (struct sd *) gspca_dev;437__u8 reg;438439/* register 2 of frame 3/4 contains the clock divider configuring the440no fps according to the formula: 60 / reg. sd->exposure is the441desired exposure time in ms. */442reg = 120 * sd->exposure / 1000;443if (reg < 2)444reg = 2;445else if (reg > 63)446reg = 63;447448reg_w(gspca_dev, 0xff, 0x04); /* page 4 */449reg_w(gspca_dev, 0x02, reg);450451/* Page 1 register 8 must always be 0x08 except when not in452640x480 mode and Page3/4 reg 2 <= 3 then it must be 9 */453reg_w(gspca_dev, 0xff, 0x01);454if (gspca_dev->cam.cam_mode[(int)gspca_dev->curr_mode].priv &&455reg <= 3) {456reg_w(gspca_dev, 0x08, 0x09);457} else {458reg_w(gspca_dev, 0x08, 0x08);459}460461/* load registers to sensor (Bit 0, auto clear) */462reg_w(gspca_dev, 0x11, 0x01);463}464465static void sethvflip(struct gspca_dev *gspca_dev)466{467struct sd *sd = (struct sd *) gspca_dev;468__u8 data;469470reg_w(gspca_dev, 0xff, 0x04); /* page 4 */471data = (sd->hflip ? 0x04 : 0x00) | (sd->vflip ? 0x08 : 0x00);472reg_w(gspca_dev, 0x21, data);473474/* load registers to sensor (Bit 0, auto clear) */475reg_w(gspca_dev, 0x11, 0x01);476}477478/* this function is called at probe and resume time for pac7311 */479static int sd_init(struct gspca_dev *gspca_dev)480{481reg_w_seq(gspca_dev, init_7311, sizeof(init_7311)/2);482return gspca_dev->usb_err;483}484485static int sd_start(struct gspca_dev *gspca_dev)486{487struct sd *sd = (struct sd *) gspca_dev;488489sd->sof_read = 0;490491reg_w_var(gspca_dev, start_7311,492page4_7311, sizeof(page4_7311));493setcontrast(gspca_dev);494setgain(gspca_dev);495setexposure(gspca_dev);496sethvflip(gspca_dev);497498/* set correct resolution */499switch (gspca_dev->cam.cam_mode[(int) gspca_dev->curr_mode].priv) {500case 2: /* 160x120 pac7311 */501reg_w(gspca_dev, 0xff, 0x01);502reg_w(gspca_dev, 0x17, 0x20);503reg_w(gspca_dev, 0x87, 0x10);504break;505case 1: /* 320x240 pac7311 */506reg_w(gspca_dev, 0xff, 0x01);507reg_w(gspca_dev, 0x17, 0x30);508reg_w(gspca_dev, 0x87, 0x11);509break;510case 0: /* 640x480 */511reg_w(gspca_dev, 0xff, 0x01);512reg_w(gspca_dev, 0x17, 0x00);513reg_w(gspca_dev, 0x87, 0x12);514break;515}516517sd->sof_read = 0;518sd->autogain_ignore_frames = 0;519atomic_set(&sd->avg_lum, -1);520521/* start stream */522reg_w(gspca_dev, 0xff, 0x01);523reg_w(gspca_dev, 0x78, 0x05);524525return gspca_dev->usb_err;526}527528static void sd_stopN(struct gspca_dev *gspca_dev)529{530reg_w(gspca_dev, 0xff, 0x04);531reg_w(gspca_dev, 0x27, 0x80);532reg_w(gspca_dev, 0x28, 0xca);533reg_w(gspca_dev, 0x29, 0x53);534reg_w(gspca_dev, 0x2a, 0x0e);535reg_w(gspca_dev, 0xff, 0x01);536reg_w(gspca_dev, 0x3e, 0x20);537reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */538reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */539reg_w(gspca_dev, 0x78, 0x44); /* Bit_0=start stream, Bit_6=LED */540}541542/* called on streamoff with alt 0 and on disconnect for 7311 */543static void sd_stop0(struct gspca_dev *gspca_dev)544{545}546547/* Include pac common sof detection functions */548#include "pac_common.h"549550static void do_autogain(struct gspca_dev *gspca_dev)551{552struct sd *sd = (struct sd *) gspca_dev;553int avg_lum = atomic_read(&sd->avg_lum);554int desired_lum, deadzone;555556if (avg_lum == -1)557return;558559desired_lum = 200;560deadzone = 20;561562if (sd->autogain_ignore_frames > 0)563sd->autogain_ignore_frames--;564else if (gspca_auto_gain_n_exposure(gspca_dev, avg_lum, desired_lum,565deadzone, GAIN_KNEE, EXPOSURE_KNEE))566sd->autogain_ignore_frames = PAC_AUTOGAIN_IGNORE_FRAMES;567}568569/* JPEG header, part 1 */570static const unsigned char pac_jpeg_header1[] = {5710xff, 0xd8, /* SOI: Start of Image */5725730xff, 0xc0, /* SOF0: Start of Frame (Baseline DCT) */5740x00, 0x11, /* length = 17 bytes (including this length field) */5750x08 /* Precision: 8 */576/* 2 bytes is placed here: number of image lines */577/* 2 bytes is placed here: samples per line */578};579580/* JPEG header, continued */581static const unsigned char pac_jpeg_header2[] = {5820x03, /* Number of image components: 3 */5830x01, 0x21, 0x00, /* ID=1, Subsampling 1x1, Quantization table: 0 */5840x02, 0x11, 0x01, /* ID=2, Subsampling 2x1, Quantization table: 1 */5850x03, 0x11, 0x01, /* ID=3, Subsampling 2x1, Quantization table: 1 */5865870xff, 0xda, /* SOS: Start Of Scan */5880x00, 0x0c, /* length = 12 bytes (including this length field) */5890x03, /* number of components: 3 */5900x01, 0x00, /* selector 1, table 0x00 */5910x02, 0x11, /* selector 2, table 0x11 */5920x03, 0x11, /* selector 3, table 0x11 */5930x00, 0x3f, /* Spectral selection: 0 .. 63 */5940x00 /* Successive approximation: 0 */595};596597static void pac_start_frame(struct gspca_dev *gspca_dev,598__u16 lines, __u16 samples_per_line)599{600unsigned char tmpbuf[4];601602gspca_frame_add(gspca_dev, FIRST_PACKET,603pac_jpeg_header1, sizeof(pac_jpeg_header1));604605tmpbuf[0] = lines >> 8;606tmpbuf[1] = lines & 0xff;607tmpbuf[2] = samples_per_line >> 8;608tmpbuf[3] = samples_per_line & 0xff;609610gspca_frame_add(gspca_dev, INTER_PACKET,611tmpbuf, sizeof(tmpbuf));612gspca_frame_add(gspca_dev, INTER_PACKET,613pac_jpeg_header2, sizeof(pac_jpeg_header2));614}615616/* this function is run at interrupt level */617static void sd_pkt_scan(struct gspca_dev *gspca_dev,618u8 *data, /* isoc packet */619int len) /* iso packet length */620{621struct sd *sd = (struct sd *) gspca_dev;622u8 *image;623unsigned char *sof;624625sof = pac_find_sof(&sd->sof_read, data, len);626if (sof) {627int n, lum_offset, footer_length;628629/* 6 bytes after the FF D9 EOF marker a number of lumination630bytes are send corresponding to different parts of the631image, the 14th and 15th byte after the EOF seem to632correspond to the center of the image */633lum_offset = 24 + sizeof pac_sof_marker;634footer_length = 26;635636/* Finish decoding current frame */637n = (sof - data) - (footer_length + sizeof pac_sof_marker);638if (n < 0) {639gspca_dev->image_len += n;640n = 0;641} else {642gspca_frame_add(gspca_dev, INTER_PACKET, data, n);643}644image = gspca_dev->image;645if (image != NULL646&& image[gspca_dev->image_len - 2] == 0xff647&& image[gspca_dev->image_len - 1] == 0xd9)648gspca_frame_add(gspca_dev, LAST_PACKET, NULL, 0);649650n = sof - data;651len -= n;652data = sof;653654/* Get average lumination */655if (gspca_dev->last_packet_type == LAST_PACKET &&656n >= lum_offset)657atomic_set(&sd->avg_lum, data[-lum_offset] +658data[-lum_offset + 1]);659else660atomic_set(&sd->avg_lum, -1);661662/* Start the new frame with the jpeg header */663pac_start_frame(gspca_dev,664gspca_dev->height, gspca_dev->width);665}666gspca_frame_add(gspca_dev, INTER_PACKET, data, len);667}668669static int sd_setcontrast(struct gspca_dev *gspca_dev, __s32 val)670{671struct sd *sd = (struct sd *) gspca_dev;672673sd->contrast = val;674if (gspca_dev->streaming)675setcontrast(gspca_dev);676return gspca_dev->usb_err;677}678679static int sd_getcontrast(struct gspca_dev *gspca_dev, __s32 *val)680{681struct sd *sd = (struct sd *) gspca_dev;682683*val = sd->contrast;684return 0;685}686687static int sd_setgain(struct gspca_dev *gspca_dev, __s32 val)688{689struct sd *sd = (struct sd *) gspca_dev;690691sd->gain = val;692if (gspca_dev->streaming)693setgain(gspca_dev);694return gspca_dev->usb_err;695}696697static int sd_getgain(struct gspca_dev *gspca_dev, __s32 *val)698{699struct sd *sd = (struct sd *) gspca_dev;700701*val = sd->gain;702return 0;703}704705static int sd_setexposure(struct gspca_dev *gspca_dev, __s32 val)706{707struct sd *sd = (struct sd *) gspca_dev;708709sd->exposure = val;710if (gspca_dev->streaming)711setexposure(gspca_dev);712return gspca_dev->usb_err;713}714715static int sd_getexposure(struct gspca_dev *gspca_dev, __s32 *val)716{717struct sd *sd = (struct sd *) gspca_dev;718719*val = sd->exposure;720return 0;721}722723static int sd_setautogain(struct gspca_dev *gspca_dev, __s32 val)724{725struct sd *sd = (struct sd *) gspca_dev;726727sd->autogain = val;728/* when switching to autogain set defaults to make sure729we are on a valid point of the autogain gain /730exposure knee graph, and give this change time to731take effect before doing autogain. */732if (sd->autogain) {733sd->exposure = EXPOSURE_DEF;734sd->gain = GAIN_DEF;735if (gspca_dev->streaming) {736sd->autogain_ignore_frames =737PAC_AUTOGAIN_IGNORE_FRAMES;738setexposure(gspca_dev);739setgain(gspca_dev);740}741}742743return gspca_dev->usb_err;744}745746static int sd_getautogain(struct gspca_dev *gspca_dev, __s32 *val)747{748struct sd *sd = (struct sd *) gspca_dev;749750*val = sd->autogain;751return 0;752}753754static int sd_sethflip(struct gspca_dev *gspca_dev, __s32 val)755{756struct sd *sd = (struct sd *) gspca_dev;757758sd->hflip = val;759if (gspca_dev->streaming)760sethvflip(gspca_dev);761return gspca_dev->usb_err;762}763764static int sd_gethflip(struct gspca_dev *gspca_dev, __s32 *val)765{766struct sd *sd = (struct sd *) gspca_dev;767768*val = sd->hflip;769return 0;770}771772static int sd_setvflip(struct gspca_dev *gspca_dev, __s32 val)773{774struct sd *sd = (struct sd *) gspca_dev;775776sd->vflip = val;777if (gspca_dev->streaming)778sethvflip(gspca_dev);779return gspca_dev->usb_err;780}781782static int sd_getvflip(struct gspca_dev *gspca_dev, __s32 *val)783{784struct sd *sd = (struct sd *) gspca_dev;785786*val = sd->vflip;787return 0;788}789790#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)791static int sd_int_pkt_scan(struct gspca_dev *gspca_dev,792u8 *data, /* interrupt packet data */793int len) /* interrupt packet length */794{795int ret = -EINVAL;796u8 data0, data1;797798if (len == 2) {799data0 = data[0];800data1 = data[1];801if ((data0 == 0x00 && data1 == 0x11) ||802(data0 == 0x22 && data1 == 0x33) ||803(data0 == 0x44 && data1 == 0x55) ||804(data0 == 0x66 && data1 == 0x77) ||805(data0 == 0x88 && data1 == 0x99) ||806(data0 == 0xaa && data1 == 0xbb) ||807(data0 == 0xcc && data1 == 0xdd) ||808(data0 == 0xee && data1 == 0xff)) {809input_report_key(gspca_dev->input_dev, KEY_CAMERA, 1);810input_sync(gspca_dev->input_dev);811input_report_key(gspca_dev->input_dev, KEY_CAMERA, 0);812input_sync(gspca_dev->input_dev);813ret = 0;814}815}816817return ret;818}819#endif820821/* sub-driver description for pac7311 */822static const struct sd_desc sd_desc = {823.name = MODULE_NAME,824.ctrls = sd_ctrls,825.nctrls = ARRAY_SIZE(sd_ctrls),826.config = sd_config,827.init = sd_init,828.start = sd_start,829.stopN = sd_stopN,830.stop0 = sd_stop0,831.pkt_scan = sd_pkt_scan,832.dq_callback = do_autogain,833#if defined(CONFIG_INPUT) || defined(CONFIG_INPUT_MODULE)834.int_pkt_scan = sd_int_pkt_scan,835#endif836};837838/* -- module initialisation -- */839static const struct usb_device_id device_table[] = {840{USB_DEVICE(0x093a, 0x2600)},841{USB_DEVICE(0x093a, 0x2601)},842{USB_DEVICE(0x093a, 0x2603)},843{USB_DEVICE(0x093a, 0x2608)},844{USB_DEVICE(0x093a, 0x260e)},845{USB_DEVICE(0x093a, 0x260f)},846{}847};848MODULE_DEVICE_TABLE(usb, device_table);849850/* -- device connect -- */851static int sd_probe(struct usb_interface *intf,852const struct usb_device_id *id)853{854return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),855THIS_MODULE);856}857858static struct usb_driver sd_driver = {859.name = MODULE_NAME,860.id_table = device_table,861.probe = sd_probe,862.disconnect = gspca_disconnect,863#ifdef CONFIG_PM864.suspend = gspca_suspend,865.resume = gspca_resume,866#endif867};868869/* -- module insert / remove -- */870static int __init sd_mod_init(void)871{872return usb_register(&sd_driver);873}874static void __exit sd_mod_exit(void)875{876usb_deregister(&sd_driver);877}878879module_init(sd_mod_init);880module_exit(sd_mod_exit);881882883