162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * ToupTek UCMOS / AmScope MU series camera driver 462306a36Sopenharmony_ci * TODO: contrast with ScopeTek / AmScope MDC cameras 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Copyright (C) 2012-2014 John McMaster <JohnDMcMaster@gmail.com> 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Special thanks to Bushing for helping with the decrypt algorithm and 962306a36Sopenharmony_ci * Sean O'Sullivan / the Rensselaer Center for Open Source 1062306a36Sopenharmony_ci * Software (RCOS) for helping me learn kernel development 1162306a36Sopenharmony_ci */ 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "gspca.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define MODULE_NAME "touptek" 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ciMODULE_AUTHOR("John McMaster"); 1862306a36Sopenharmony_ciMODULE_DESCRIPTION("ToupTek UCMOS / Amscope MU microscope camera driver"); 1962306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * Exposure reg is linear with exposure time 2362306a36Sopenharmony_ci * Exposure (sec), E (reg) 2462306a36Sopenharmony_ci * 0.000400, 0x0002 2562306a36Sopenharmony_ci * 0.001000, 0x0005 2662306a36Sopenharmony_ci * 0.005000, 0x0019 2762306a36Sopenharmony_ci * 0.020000, 0x0064 2862306a36Sopenharmony_ci * 0.080000, 0x0190 2962306a36Sopenharmony_ci * 0.400000, 0x07D0 3062306a36Sopenharmony_ci * 1.000000, 0x1388 3162306a36Sopenharmony_ci * 2.000000, 0x2710 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * Three gain stages 3462306a36Sopenharmony_ci * 0x1000: master channel enable bit 3562306a36Sopenharmony_ci * 0x007F: low gain bits 3662306a36Sopenharmony_ci * 0x0080: medium gain bit 3762306a36Sopenharmony_ci * 0x0100: high gain bit 3862306a36Sopenharmony_ci * gain = enable * (1 + regH) * (1 + regM) * z * regL 3962306a36Sopenharmony_ci * 4062306a36Sopenharmony_ci * Gain implementation 4162306a36Sopenharmony_ci * Want to do something similar to mt9v011.c's set_balance 4262306a36Sopenharmony_ci * 4362306a36Sopenharmony_ci * Gain does not vary with resolution (checked 640x480 vs 1600x1200) 4462306a36Sopenharmony_ci * 4562306a36Sopenharmony_ci * Constant derivation: 4662306a36Sopenharmony_ci * 4762306a36Sopenharmony_ci * Raw data: 4862306a36Sopenharmony_ci * Gain, GTOP, B, R, GBOT 4962306a36Sopenharmony_ci * 1.00, 0x105C, 0x1068, 0x10C8, 0x105C 5062306a36Sopenharmony_ci * 1.20, 0x106E, 0x107E, 0x10D6, 0x106E 5162306a36Sopenharmony_ci * 1.40, 0x10C0, 0x10CA, 0x10E5, 0x10C0 5262306a36Sopenharmony_ci * 1.60, 0x10C9, 0x10D4, 0x10F3, 0x10C9 5362306a36Sopenharmony_ci * 1.80, 0x10D2, 0x10DE, 0x11C1, 0x10D2 5462306a36Sopenharmony_ci * 2.00, 0x10DC, 0x10E9, 0x11C8, 0x10DC 5562306a36Sopenharmony_ci * 2.20, 0x10E5, 0x10F3, 0x11CF, 0x10E5 5662306a36Sopenharmony_ci * 2.40, 0x10EE, 0x10FE, 0x11D7, 0x10EE 5762306a36Sopenharmony_ci * 2.60, 0x10F7, 0x11C4, 0x11DE, 0x10F7 5862306a36Sopenharmony_ci * 2.80, 0x11C0, 0x11CA, 0x11E5, 0x11C0 5962306a36Sopenharmony_ci * 3.00, 0x11C5, 0x11CF, 0x11ED, 0x11C5 6062306a36Sopenharmony_ci * 6162306a36Sopenharmony_ci * zR = 0.0069605943152454778 6262306a36Sopenharmony_ci * about 3/431 = 0.0069605568445475635 6362306a36Sopenharmony_ci * zB = 0.0095695970695970703 6462306a36Sopenharmony_ci * about 6/627 = 0.0095693779904306216 6562306a36Sopenharmony_ci * zG = 0.010889328063241107 6662306a36Sopenharmony_ci * about 6/551 = 0.010889292196007259 6762306a36Sopenharmony_ci * about 10 bits for constant + 7 bits for value => at least 17 bit 6862306a36Sopenharmony_ci * intermediate with 32 bit ints should be fine for overflow etc 6962306a36Sopenharmony_ci * Essentially gains are in range 0-0x001FF 7062306a36Sopenharmony_ci * 7162306a36Sopenharmony_ci * However, V4L expects a main gain channel + R and B balance 7262306a36Sopenharmony_ci * To keep things simple for now saturate the values of balance is too high/low 7362306a36Sopenharmony_ci * This isn't really ideal but easy way to fit the Linux model 7462306a36Sopenharmony_ci * 7562306a36Sopenharmony_ci * Converted using gain model turns out to be quite linear: 7662306a36Sopenharmony_ci * Gain, GTOP, B, R, GBOT 7762306a36Sopenharmony_ci * 1.00, 92, 104, 144, 92 7862306a36Sopenharmony_ci * 1.20, 110, 126, 172, 110 7962306a36Sopenharmony_ci * 1.40, 128, 148, 202, 128 8062306a36Sopenharmony_ci * 1.60, 146, 168, 230, 146 8162306a36Sopenharmony_ci * 1.80, 164, 188, 260, 164 8262306a36Sopenharmony_ci * 2.00, 184, 210, 288, 184 8362306a36Sopenharmony_ci * 2.20, 202, 230, 316, 202 8462306a36Sopenharmony_ci * 2.40, 220, 252, 348, 220 8562306a36Sopenharmony_ci * 2.60, 238, 272, 376, 238 8662306a36Sopenharmony_ci * 2.80, 256, 296, 404, 256 8762306a36Sopenharmony_ci * 3.00, 276, 316, 436, 276 8862306a36Sopenharmony_ci * 8962306a36Sopenharmony_ci * Maximum gain is 0x7FF * 2 * 2 => 0x1FFC (8188) 9062306a36Sopenharmony_ci * or about 13 effective bits of gain 9162306a36Sopenharmony_ci * The highest the commercial driver goes in my setup 436 9262306a36Sopenharmony_ci * However, because could *maybe* damage circuits 9362306a36Sopenharmony_ci * limit the gain until have a reason to go higher 9462306a36Sopenharmony_ci * Solution: gain clipped and warning emitted 9562306a36Sopenharmony_ci */ 9662306a36Sopenharmony_ci#define GAIN_MAX 511 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* Frame sync is a short read */ 9962306a36Sopenharmony_ci#define BULK_SIZE 0x4000 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* MT9E001 reg names to give a rough approximation */ 10262306a36Sopenharmony_ci#define REG_COARSE_INTEGRATION_TIME_ 0x3012 10362306a36Sopenharmony_ci#define REG_GROUPED_PARAMETER_HOLD_ 0x3022 10462306a36Sopenharmony_ci#define REG_MODE_SELECT 0x0100 10562306a36Sopenharmony_ci#define REG_OP_SYS_CLK_DIV 0x030A 10662306a36Sopenharmony_ci#define REG_VT_SYS_CLK_DIV 0x0302 10762306a36Sopenharmony_ci#define REG_PRE_PLL_CLK_DIV 0x0304 10862306a36Sopenharmony_ci#define REG_VT_PIX_CLK_DIV 0x0300 10962306a36Sopenharmony_ci#define REG_OP_PIX_CLK_DIV 0x0308 11062306a36Sopenharmony_ci#define REG_PLL_MULTIPLIER 0x0306 11162306a36Sopenharmony_ci#define REG_COARSE_INTEGRATION_TIME_ 0x3012 11262306a36Sopenharmony_ci#define REG_FRAME_LENGTH_LINES 0x0340 11362306a36Sopenharmony_ci#define REG_FRAME_LENGTH_LINES_ 0x300A 11462306a36Sopenharmony_ci#define REG_GREEN1_GAIN 0x3056 11562306a36Sopenharmony_ci#define REG_GREEN2_GAIN 0x305C 11662306a36Sopenharmony_ci#define REG_GROUPED_PARAMETER_HOLD 0x0104 11762306a36Sopenharmony_ci#define REG_LINE_LENGTH_PCK_ 0x300C 11862306a36Sopenharmony_ci#define REG_MODE_SELECT 0x0100 11962306a36Sopenharmony_ci#define REG_PLL_MULTIPLIER 0x0306 12062306a36Sopenharmony_ci#define REG_READ_MODE 0x3040 12162306a36Sopenharmony_ci#define REG_BLUE_GAIN 0x3058 12262306a36Sopenharmony_ci#define REG_RED_GAIN 0x305A 12362306a36Sopenharmony_ci#define REG_RESET_REGISTER 0x301A 12462306a36Sopenharmony_ci#define REG_SCALE_M 0x0404 12562306a36Sopenharmony_ci#define REG_SCALING_MODE 0x0400 12662306a36Sopenharmony_ci#define REG_SOFTWARE_RESET 0x0103 12762306a36Sopenharmony_ci#define REG_X_ADDR_END 0x0348 12862306a36Sopenharmony_ci#define REG_X_ADDR_START 0x0344 12962306a36Sopenharmony_ci#define REG_X_ADDR_START 0x0344 13062306a36Sopenharmony_ci#define REG_X_OUTPUT_SIZE 0x034C 13162306a36Sopenharmony_ci#define REG_Y_ADDR_END 0x034A 13262306a36Sopenharmony_ci#define REG_Y_ADDR_START 0x0346 13362306a36Sopenharmony_ci#define REG_Y_OUTPUT_SIZE 0x034E 13462306a36Sopenharmony_ci 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci/* specific webcam descriptor */ 13762306a36Sopenharmony_cistruct sd { 13862306a36Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 13962306a36Sopenharmony_ci /* How many bytes this frame */ 14062306a36Sopenharmony_ci unsigned int this_f; 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* 14362306a36Sopenharmony_ci Device has separate gains for each Bayer quadrant 14462306a36Sopenharmony_ci V4L supports master gain which is referenced to G1/G2 and supplies 14562306a36Sopenharmony_ci individual balance controls for R/B 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_ci struct v4l2_ctrl *blue; 14862306a36Sopenharmony_ci struct v4l2_ctrl *red; 14962306a36Sopenharmony_ci}; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci/* Used to simplify reg write error handling */ 15262306a36Sopenharmony_cistruct cmd { 15362306a36Sopenharmony_ci u16 value; 15462306a36Sopenharmony_ci u16 index; 15562306a36Sopenharmony_ci}; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = { 15862306a36Sopenharmony_ci {800, 600, 15962306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 16062306a36Sopenharmony_ci V4L2_FIELD_NONE, 16162306a36Sopenharmony_ci .bytesperline = 800, 16262306a36Sopenharmony_ci .sizeimage = 800 * 600, 16362306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB}, 16462306a36Sopenharmony_ci {1600, 1200, 16562306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 16662306a36Sopenharmony_ci V4L2_FIELD_NONE, 16762306a36Sopenharmony_ci .bytesperline = 1600, 16862306a36Sopenharmony_ci .sizeimage = 1600 * 1200, 16962306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB}, 17062306a36Sopenharmony_ci {3264, 2448, 17162306a36Sopenharmony_ci V4L2_PIX_FMT_SGRBG8, 17262306a36Sopenharmony_ci V4L2_FIELD_NONE, 17362306a36Sopenharmony_ci .bytesperline = 3264, 17462306a36Sopenharmony_ci .sizeimage = 3264 * 2448, 17562306a36Sopenharmony_ci .colorspace = V4L2_COLORSPACE_SRGB}, 17662306a36Sopenharmony_ci}; 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* 17962306a36Sopenharmony_ci * As there's no known frame sync, the only way to keep synced is to try hard 18062306a36Sopenharmony_ci * to never miss any packets 18162306a36Sopenharmony_ci */ 18262306a36Sopenharmony_ci#if MAX_NURBS < 4 18362306a36Sopenharmony_ci#error "Not enough URBs in the gspca table" 18462306a36Sopenharmony_ci#endif 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_cistatic int val_reply(struct gspca_dev *gspca_dev, const char *reply, int rc) 18762306a36Sopenharmony_ci{ 18862306a36Sopenharmony_ci if (rc < 0) { 18962306a36Sopenharmony_ci gspca_err(gspca_dev, "reply has error %d\n", rc); 19062306a36Sopenharmony_ci return -EIO; 19162306a36Sopenharmony_ci } 19262306a36Sopenharmony_ci if (rc != 1) { 19362306a36Sopenharmony_ci gspca_err(gspca_dev, "Bad reply size %d\n", rc); 19462306a36Sopenharmony_ci return -EIO; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci if (reply[0] != 0x08) { 19762306a36Sopenharmony_ci gspca_err(gspca_dev, "Bad reply 0x%02x\n", (int)reply[0]); 19862306a36Sopenharmony_ci return -EIO; 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci return 0; 20162306a36Sopenharmony_ci} 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_cistatic void reg_w(struct gspca_dev *gspca_dev, u16 value, u16 index) 20462306a36Sopenharmony_ci{ 20562306a36Sopenharmony_ci char *buff = gspca_dev->usb_buf; 20662306a36Sopenharmony_ci int rc; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_USBO, 20962306a36Sopenharmony_ci "reg_w bReq=0x0B, bReqT=0xC0, wVal=0x%04X, wInd=0x%04X\n\n", 21062306a36Sopenharmony_ci value, index); 21162306a36Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 21262306a36Sopenharmony_ci 0x0B, 0xC0, value, index, buff, 1, 500); 21362306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_USBO, "rc=%d, ret={0x%02x}\n", rc, (int)buff[0]); 21462306a36Sopenharmony_ci if (rc < 0) { 21562306a36Sopenharmony_ci gspca_err(gspca_dev, "Failed reg_w(0x0B, 0xC0, 0x%04X, 0x%04X) w/ rc %d\n", 21662306a36Sopenharmony_ci value, index, rc); 21762306a36Sopenharmony_ci gspca_dev->usb_err = rc; 21862306a36Sopenharmony_ci return; 21962306a36Sopenharmony_ci } 22062306a36Sopenharmony_ci if (val_reply(gspca_dev, buff, rc)) { 22162306a36Sopenharmony_ci gspca_err(gspca_dev, "Bad reply to reg_w(0x0B, 0xC0, 0x%04X, 0x%04X\n", 22262306a36Sopenharmony_ci value, index); 22362306a36Sopenharmony_ci gspca_dev->usb_err = -EIO; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci} 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_cistatic void reg_w_buf(struct gspca_dev *gspca_dev, 22862306a36Sopenharmony_ci const struct cmd *p, int l) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci do { 23162306a36Sopenharmony_ci reg_w(gspca_dev, p->value, p->index); 23262306a36Sopenharmony_ci p++; 23362306a36Sopenharmony_ci } while (--l > 0); 23462306a36Sopenharmony_ci} 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_cistatic void setexposure(struct gspca_dev *gspca_dev, s32 val) 23762306a36Sopenharmony_ci{ 23862306a36Sopenharmony_ci u16 value; 23962306a36Sopenharmony_ci unsigned int w = gspca_dev->pixfmt.width; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (w == 800) 24262306a36Sopenharmony_ci value = val * 5; 24362306a36Sopenharmony_ci else if (w == 1600) 24462306a36Sopenharmony_ci value = val * 3; 24562306a36Sopenharmony_ci else if (w == 3264) 24662306a36Sopenharmony_ci value = val * 3 / 2; 24762306a36Sopenharmony_ci else { 24862306a36Sopenharmony_ci gspca_err(gspca_dev, "Invalid width %u\n", w); 24962306a36Sopenharmony_ci gspca_dev->usb_err = -EINVAL; 25062306a36Sopenharmony_ci return; 25162306a36Sopenharmony_ci } 25262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "exposure: 0x%04X ms\n\n", value); 25362306a36Sopenharmony_ci /* Wonder if there's a good reason for sending it twice */ 25462306a36Sopenharmony_ci /* probably not but leave it in because...why not */ 25562306a36Sopenharmony_ci reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); 25662306a36Sopenharmony_ci reg_w(gspca_dev, value, REG_COARSE_INTEGRATION_TIME_); 25762306a36Sopenharmony_ci} 25862306a36Sopenharmony_ci 25962306a36Sopenharmony_cistatic int gainify(int in) 26062306a36Sopenharmony_ci{ 26162306a36Sopenharmony_ci /* 26262306a36Sopenharmony_ci * TODO: check if there are any issues with corner cases 26362306a36Sopenharmony_ci * 0x000 (0):0x07F (127): regL 26462306a36Sopenharmony_ci * 0x080 (128) - 0x0FF (255): regM, regL 26562306a36Sopenharmony_ci * 0x100 (256) - max: regH, regM, regL 26662306a36Sopenharmony_ci */ 26762306a36Sopenharmony_ci if (in <= 0x7F) 26862306a36Sopenharmony_ci return 0x1000 | in; 26962306a36Sopenharmony_ci else if (in <= 0xFF) 27062306a36Sopenharmony_ci return 0x1080 | in / 2; 27162306a36Sopenharmony_ci else 27262306a36Sopenharmony_ci return 0x1180 | in / 4; 27362306a36Sopenharmony_ci} 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_cistatic void setggain(struct gspca_dev *gspca_dev, u16 global_gain) 27662306a36Sopenharmony_ci{ 27762306a36Sopenharmony_ci u16 normalized; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci normalized = gainify(global_gain); 28062306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "gain G1/G2 (0x%04X): 0x%04X (src 0x%04X)\n\n", 28162306a36Sopenharmony_ci REG_GREEN1_GAIN, 28262306a36Sopenharmony_ci normalized, global_gain); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci reg_w(gspca_dev, normalized, REG_GREEN1_GAIN); 28562306a36Sopenharmony_ci reg_w(gspca_dev, normalized, REG_GREEN2_GAIN); 28662306a36Sopenharmony_ci} 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic void setbgain(struct gspca_dev *gspca_dev, 28962306a36Sopenharmony_ci u16 gain, u16 global_gain) 29062306a36Sopenharmony_ci{ 29162306a36Sopenharmony_ci u16 normalized; 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci normalized = global_gain + 29462306a36Sopenharmony_ci ((u32)global_gain) * gain / GAIN_MAX; 29562306a36Sopenharmony_ci if (normalized > GAIN_MAX) { 29662306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Truncating blue 0x%04X w/ value 0x%04X\n\n", 29762306a36Sopenharmony_ci GAIN_MAX, normalized); 29862306a36Sopenharmony_ci normalized = GAIN_MAX; 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci normalized = gainify(normalized); 30162306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "gain B (0x%04X): 0x%04X w/ source 0x%04X\n\n", 30262306a36Sopenharmony_ci REG_BLUE_GAIN, normalized, gain); 30362306a36Sopenharmony_ci 30462306a36Sopenharmony_ci reg_w(gspca_dev, normalized, REG_BLUE_GAIN); 30562306a36Sopenharmony_ci} 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_cistatic void setrgain(struct gspca_dev *gspca_dev, 30862306a36Sopenharmony_ci u16 gain, u16 global_gain) 30962306a36Sopenharmony_ci{ 31062306a36Sopenharmony_ci u16 normalized; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci normalized = global_gain + 31362306a36Sopenharmony_ci ((u32)global_gain) * gain / GAIN_MAX; 31462306a36Sopenharmony_ci if (normalized > GAIN_MAX) { 31562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Truncating gain 0x%04X w/ value 0x%04X\n\n", 31662306a36Sopenharmony_ci GAIN_MAX, normalized); 31762306a36Sopenharmony_ci normalized = GAIN_MAX; 31862306a36Sopenharmony_ci } 31962306a36Sopenharmony_ci normalized = gainify(normalized); 32062306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "gain R (0x%04X): 0x%04X w / source 0x%04X\n\n", 32162306a36Sopenharmony_ci REG_RED_GAIN, normalized, gain); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci reg_w(gspca_dev, normalized, REG_RED_GAIN); 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_cistatic void configure_wh(struct gspca_dev *gspca_dev) 32762306a36Sopenharmony_ci{ 32862306a36Sopenharmony_ci unsigned int w = gspca_dev->pixfmt.width; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "configure_wh\n\n"); 33162306a36Sopenharmony_ci 33262306a36Sopenharmony_ci if (w == 800) { 33362306a36Sopenharmony_ci static const struct cmd reg_init_res[] = { 33462306a36Sopenharmony_ci {0x0060, REG_X_ADDR_START}, 33562306a36Sopenharmony_ci {0x0CD9, REG_X_ADDR_END}, 33662306a36Sopenharmony_ci {0x0036, REG_Y_ADDR_START}, 33762306a36Sopenharmony_ci {0x098F, REG_Y_ADDR_END}, 33862306a36Sopenharmony_ci {0x07C7, REG_READ_MODE}, 33962306a36Sopenharmony_ci }; 34062306a36Sopenharmony_ci 34162306a36Sopenharmony_ci reg_w_buf(gspca_dev, 34262306a36Sopenharmony_ci reg_init_res, ARRAY_SIZE(reg_init_res)); 34362306a36Sopenharmony_ci } else if (w == 1600) { 34462306a36Sopenharmony_ci static const struct cmd reg_init_res[] = { 34562306a36Sopenharmony_ci {0x009C, REG_X_ADDR_START}, 34662306a36Sopenharmony_ci {0x0D19, REG_X_ADDR_END}, 34762306a36Sopenharmony_ci {0x0068, REG_Y_ADDR_START}, 34862306a36Sopenharmony_ci {0x09C5, REG_Y_ADDR_END}, 34962306a36Sopenharmony_ci {0x06C3, REG_READ_MODE}, 35062306a36Sopenharmony_ci }; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci reg_w_buf(gspca_dev, 35362306a36Sopenharmony_ci reg_init_res, ARRAY_SIZE(reg_init_res)); 35462306a36Sopenharmony_ci } else if (w == 3264) { 35562306a36Sopenharmony_ci static const struct cmd reg_init_res[] = { 35662306a36Sopenharmony_ci {0x00E8, REG_X_ADDR_START}, 35762306a36Sopenharmony_ci {0x0DA7, REG_X_ADDR_END}, 35862306a36Sopenharmony_ci {0x009E, REG_Y_ADDR_START}, 35962306a36Sopenharmony_ci {0x0A2D, REG_Y_ADDR_END}, 36062306a36Sopenharmony_ci {0x0241, REG_READ_MODE}, 36162306a36Sopenharmony_ci }; 36262306a36Sopenharmony_ci 36362306a36Sopenharmony_ci reg_w_buf(gspca_dev, 36462306a36Sopenharmony_ci reg_init_res, ARRAY_SIZE(reg_init_res)); 36562306a36Sopenharmony_ci } else { 36662306a36Sopenharmony_ci gspca_err(gspca_dev, "bad width %u\n", w); 36762306a36Sopenharmony_ci gspca_dev->usb_err = -EINVAL; 36862306a36Sopenharmony_ci return; 36962306a36Sopenharmony_ci } 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci reg_w(gspca_dev, 0x0000, REG_SCALING_MODE); 37262306a36Sopenharmony_ci reg_w(gspca_dev, 0x0010, REG_SCALE_M); 37362306a36Sopenharmony_ci reg_w(gspca_dev, w, REG_X_OUTPUT_SIZE); 37462306a36Sopenharmony_ci reg_w(gspca_dev, gspca_dev->pixfmt.height, REG_Y_OUTPUT_SIZE); 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci if (w == 800) { 37762306a36Sopenharmony_ci reg_w(gspca_dev, 0x0384, REG_FRAME_LENGTH_LINES_); 37862306a36Sopenharmony_ci reg_w(gspca_dev, 0x0960, REG_LINE_LENGTH_PCK_); 37962306a36Sopenharmony_ci } else if (w == 1600) { 38062306a36Sopenharmony_ci reg_w(gspca_dev, 0x0640, REG_FRAME_LENGTH_LINES_); 38162306a36Sopenharmony_ci reg_w(gspca_dev, 0x0FA0, REG_LINE_LENGTH_PCK_); 38262306a36Sopenharmony_ci } else if (w == 3264) { 38362306a36Sopenharmony_ci reg_w(gspca_dev, 0x0B4B, REG_FRAME_LENGTH_LINES_); 38462306a36Sopenharmony_ci reg_w(gspca_dev, 0x1F40, REG_LINE_LENGTH_PCK_); 38562306a36Sopenharmony_ci } else { 38662306a36Sopenharmony_ci gspca_err(gspca_dev, "bad width %u\n", w); 38762306a36Sopenharmony_ci gspca_dev->usb_err = -EINVAL; 38862306a36Sopenharmony_ci return; 38962306a36Sopenharmony_ci } 39062306a36Sopenharmony_ci} 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci/* Packets that were encrypted, no idea if the grouping is significant */ 39362306a36Sopenharmony_cistatic void configure_encrypted(struct gspca_dev *gspca_dev) 39462306a36Sopenharmony_ci{ 39562306a36Sopenharmony_ci static const struct cmd reg_init_begin[] = { 39662306a36Sopenharmony_ci {0x0100, REG_SOFTWARE_RESET}, 39762306a36Sopenharmony_ci {0x0000, REG_MODE_SELECT}, 39862306a36Sopenharmony_ci {0x0100, REG_GROUPED_PARAMETER_HOLD}, 39962306a36Sopenharmony_ci {0x0004, REG_VT_PIX_CLK_DIV}, 40062306a36Sopenharmony_ci {0x0001, REG_VT_SYS_CLK_DIV}, 40162306a36Sopenharmony_ci {0x0008, REG_OP_PIX_CLK_DIV}, 40262306a36Sopenharmony_ci {0x0001, REG_OP_SYS_CLK_DIV}, 40362306a36Sopenharmony_ci {0x0004, REG_PRE_PLL_CLK_DIV}, 40462306a36Sopenharmony_ci {0x0040, REG_PLL_MULTIPLIER}, 40562306a36Sopenharmony_ci {0x0000, REG_GROUPED_PARAMETER_HOLD}, 40662306a36Sopenharmony_ci {0x0100, REG_GROUPED_PARAMETER_HOLD}, 40762306a36Sopenharmony_ci }; 40862306a36Sopenharmony_ci static const struct cmd reg_init_end[] = { 40962306a36Sopenharmony_ci {0x0000, REG_GROUPED_PARAMETER_HOLD}, 41062306a36Sopenharmony_ci {0x0301, 0x31AE}, 41162306a36Sopenharmony_ci {0x0805, 0x3064}, 41262306a36Sopenharmony_ci {0x0071, 0x3170}, 41362306a36Sopenharmony_ci {0x10DE, REG_RESET_REGISTER}, 41462306a36Sopenharmony_ci {0x0000, REG_MODE_SELECT}, 41562306a36Sopenharmony_ci {0x0010, REG_PLL_MULTIPLIER}, 41662306a36Sopenharmony_ci {0x0100, REG_MODE_SELECT}, 41762306a36Sopenharmony_ci }; 41862306a36Sopenharmony_ci 41962306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Encrypted begin, w = %u\n\n", 42062306a36Sopenharmony_ci gspca_dev->pixfmt.width); 42162306a36Sopenharmony_ci reg_w_buf(gspca_dev, reg_init_begin, ARRAY_SIZE(reg_init_begin)); 42262306a36Sopenharmony_ci configure_wh(gspca_dev); 42362306a36Sopenharmony_ci reg_w_buf(gspca_dev, reg_init_end, ARRAY_SIZE(reg_init_end)); 42462306a36Sopenharmony_ci reg_w(gspca_dev, 0x0100, REG_GROUPED_PARAMETER_HOLD); 42562306a36Sopenharmony_ci reg_w(gspca_dev, 0x0000, REG_GROUPED_PARAMETER_HOLD); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Encrypted end\n\n"); 42862306a36Sopenharmony_ci} 42962306a36Sopenharmony_ci 43062306a36Sopenharmony_cistatic int configure(struct gspca_dev *gspca_dev) 43162306a36Sopenharmony_ci{ 43262306a36Sopenharmony_ci int rc; 43362306a36Sopenharmony_ci char *buff = gspca_dev->usb_buf; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "configure()\n\n"); 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci /* 43862306a36Sopenharmony_ci * First driver sets a sort of encryption key 43962306a36Sopenharmony_ci * A number of futur requests of this type have wValue and wIndex 44062306a36Sopenharmony_ci * encrypted as follows: 44162306a36Sopenharmony_ci * -Compute key = this wValue rotate left by 4 bits 44262306a36Sopenharmony_ci * (decrypt.py rotates right because we are decrypting) 44362306a36Sopenharmony_ci * -Later packets encrypt packets by XOR'ing with key 44462306a36Sopenharmony_ci * XOR encrypt/decrypt is symmetrical 44562306a36Sopenharmony_ci * wValue, and wIndex are encrypted 44662306a36Sopenharmony_ci * bRequest is not and bRequestType is always 0xC0 44762306a36Sopenharmony_ci * This allows resyncing if key is unknown? 44862306a36Sopenharmony_ci * By setting 0 we XOR with 0 and the shifting and XOR drops out 44962306a36Sopenharmony_ci */ 45062306a36Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_rcvctrlpipe(gspca_dev->dev, 0), 45162306a36Sopenharmony_ci 0x16, 0xC0, 0x0000, 0x0000, buff, 2, 500); 45262306a36Sopenharmony_ci if (val_reply(gspca_dev, buff, rc)) { 45362306a36Sopenharmony_ci gspca_err(gspca_dev, "failed key req\n"); 45462306a36Sopenharmony_ci return -EIO; 45562306a36Sopenharmony_ci } 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci /* 45862306a36Sopenharmony_ci * Next does some sort of 2 packet challenge / response 45962306a36Sopenharmony_ci * evidence suggests its an Atmel I2C crypto part but nobody cares to 46062306a36Sopenharmony_ci * look 46162306a36Sopenharmony_ci * (to make sure its not cloned hardware?) 46262306a36Sopenharmony_ci * Ignore: I want to work with their hardware, not clone it 46362306a36Sopenharmony_ci * 16 bytes out challenge, requestType: 0x40 46462306a36Sopenharmony_ci * 16 bytes in response, requestType: 0xC0 46562306a36Sopenharmony_ci */ 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 46862306a36Sopenharmony_ci 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); 46962306a36Sopenharmony_ci if (rc < 0) { 47062306a36Sopenharmony_ci gspca_err(gspca_dev, "failed to replay packet 176 w/ rc %d\n", 47162306a36Sopenharmony_ci rc); 47262306a36Sopenharmony_ci return rc; 47362306a36Sopenharmony_ci } 47462306a36Sopenharmony_ci 47562306a36Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 47662306a36Sopenharmony_ci 0x01, 0x40, 0x0000, 0x000F, NULL, 0, 500); 47762306a36Sopenharmony_ci if (rc < 0) { 47862306a36Sopenharmony_ci gspca_err(gspca_dev, "failed to replay packet 178 w/ rc %d\n", 47962306a36Sopenharmony_ci rc); 48062306a36Sopenharmony_ci return rc; 48162306a36Sopenharmony_ci } 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 48462306a36Sopenharmony_ci 0x01, 0x40, 0x0001, 0x000F, NULL, 0, 500); 48562306a36Sopenharmony_ci if (rc < 0) { 48662306a36Sopenharmony_ci gspca_err(gspca_dev, "failed to replay packet 180 w/ rc %d\n", 48762306a36Sopenharmony_ci rc); 48862306a36Sopenharmony_ci return rc; 48962306a36Sopenharmony_ci } 49062306a36Sopenharmony_ci 49162306a36Sopenharmony_ci /* 49262306a36Sopenharmony_ci * Serial number? Doesn't seem to be required 49362306a36Sopenharmony_ci * cam1: \xE6\x0D\x00\x00, cam2: \x70\x19\x00\x00 49462306a36Sopenharmony_ci * rc = usb_control_msg(gspca_dev->dev, 49562306a36Sopenharmony_ci * usb_rcvctrlpipe(gspca_dev->dev, 0), 49662306a36Sopenharmony_ci * 0x20, 0xC0, 0x0000, 0x0000, buff, 4, 500); 49762306a36Sopenharmony_ci */ 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci /* Large (EEPROM?) read, skip it since no idea what to do with it */ 50062306a36Sopenharmony_ci gspca_dev->usb_err = 0; 50162306a36Sopenharmony_ci configure_encrypted(gspca_dev); 50262306a36Sopenharmony_ci if (gspca_dev->usb_err) 50362306a36Sopenharmony_ci return gspca_dev->usb_err; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci /* Omitted this by accident, does not work without it */ 50662306a36Sopenharmony_ci rc = usb_control_msg(gspca_dev->dev, usb_sndctrlpipe(gspca_dev->dev, 0), 50762306a36Sopenharmony_ci 0x01, 0x40, 0x0003, 0x000F, NULL, 0, 500); 50862306a36Sopenharmony_ci if (rc < 0) { 50962306a36Sopenharmony_ci gspca_err(gspca_dev, "failed to replay final packet w/ rc %d\n", 51062306a36Sopenharmony_ci rc); 51162306a36Sopenharmony_ci return rc; 51262306a36Sopenharmony_ci } 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Configure complete\n\n"); 51562306a36Sopenharmony_ci return 0; 51662306a36Sopenharmony_ci} 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 51962306a36Sopenharmony_ci const struct usb_device_id *id) 52062306a36Sopenharmony_ci{ 52162306a36Sopenharmony_ci gspca_dev->cam.cam_mode = vga_mode; 52262306a36Sopenharmony_ci gspca_dev->cam.nmodes = ARRAY_SIZE(vga_mode); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci /* Yes we want URBs and we want them now! */ 52562306a36Sopenharmony_ci gspca_dev->cam.no_urb_create = 0; 52662306a36Sopenharmony_ci gspca_dev->cam.bulk_nurbs = 4; 52762306a36Sopenharmony_ci /* Largest size the windows driver uses */ 52862306a36Sopenharmony_ci gspca_dev->cam.bulk_size = BULK_SIZE; 52962306a36Sopenharmony_ci /* Def need to use bulk transfers */ 53062306a36Sopenharmony_ci gspca_dev->cam.bulk = 1; 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci return 0; 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 53662306a36Sopenharmony_ci{ 53762306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 53862306a36Sopenharmony_ci int rc; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci sd->this_f = 0; 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci rc = configure(gspca_dev); 54362306a36Sopenharmony_ci if (rc < 0) { 54462306a36Sopenharmony_ci gspca_err(gspca_dev, "Failed configure\n"); 54562306a36Sopenharmony_ci return rc; 54662306a36Sopenharmony_ci } 54762306a36Sopenharmony_ci /* First two frames have messed up gains 54862306a36Sopenharmony_ci Drop them to avoid special cases in user apps? */ 54962306a36Sopenharmony_ci return 0; 55062306a36Sopenharmony_ci} 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 55362306a36Sopenharmony_ci u8 *data, /* isoc packet */ 55462306a36Sopenharmony_ci int len) /* iso packet length */ 55562306a36Sopenharmony_ci{ 55662306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 55762306a36Sopenharmony_ci 55862306a36Sopenharmony_ci if (len != BULK_SIZE) { 55962306a36Sopenharmony_ci /* can we finish a frame? */ 56062306a36Sopenharmony_ci if (sd->this_f + len == gspca_dev->pixfmt.sizeimage) { 56162306a36Sopenharmony_ci gspca_frame_add(gspca_dev, LAST_PACKET, data, len); 56262306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_FRAM, "finish frame sz %u/%u w/ len %u\n\n", 56362306a36Sopenharmony_ci sd->this_f, gspca_dev->pixfmt.sizeimage, len); 56462306a36Sopenharmony_ci /* lost some data, discard the frame */ 56562306a36Sopenharmony_ci } else { 56662306a36Sopenharmony_ci gspca_frame_add(gspca_dev, DISCARD_PACKET, NULL, 0); 56762306a36Sopenharmony_ci gspca_dbg(gspca_dev, D_FRAM, "abort frame sz %u/%u w/ len %u\n\n", 56862306a36Sopenharmony_ci sd->this_f, gspca_dev->pixfmt.sizeimage, len); 56962306a36Sopenharmony_ci } 57062306a36Sopenharmony_ci sd->this_f = 0; 57162306a36Sopenharmony_ci } else { 57262306a36Sopenharmony_ci if (sd->this_f == 0) 57362306a36Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, data, len); 57462306a36Sopenharmony_ci else 57562306a36Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, data, len); 57662306a36Sopenharmony_ci sd->this_f += len; 57762306a36Sopenharmony_ci } 57862306a36Sopenharmony_ci} 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 58162306a36Sopenharmony_ci{ 58262306a36Sopenharmony_ci return 0; 58362306a36Sopenharmony_ci} 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 58662306a36Sopenharmony_ci{ 58762306a36Sopenharmony_ci struct gspca_dev *gspca_dev = 58862306a36Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 58962306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_ci gspca_dev->usb_err = 0; 59262306a36Sopenharmony_ci 59362306a36Sopenharmony_ci if (!gspca_dev->streaming) 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci switch (ctrl->id) { 59762306a36Sopenharmony_ci case V4L2_CID_EXPOSURE: 59862306a36Sopenharmony_ci setexposure(gspca_dev, ctrl->val); 59962306a36Sopenharmony_ci break; 60062306a36Sopenharmony_ci case V4L2_CID_GAIN: 60162306a36Sopenharmony_ci /* gspca_dev->gain automatically updated */ 60262306a36Sopenharmony_ci setggain(gspca_dev, gspca_dev->gain->val); 60362306a36Sopenharmony_ci break; 60462306a36Sopenharmony_ci case V4L2_CID_BLUE_BALANCE: 60562306a36Sopenharmony_ci sd->blue->val = ctrl->val; 60662306a36Sopenharmony_ci setbgain(gspca_dev, sd->blue->val, gspca_dev->gain->val); 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci case V4L2_CID_RED_BALANCE: 60962306a36Sopenharmony_ci sd->red->val = ctrl->val; 61062306a36Sopenharmony_ci setrgain(gspca_dev, sd->red->val, gspca_dev->gain->val); 61162306a36Sopenharmony_ci break; 61262306a36Sopenharmony_ci } 61362306a36Sopenharmony_ci return gspca_dev->usb_err; 61462306a36Sopenharmony_ci} 61562306a36Sopenharmony_ci 61662306a36Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 61762306a36Sopenharmony_ci .s_ctrl = sd_s_ctrl, 61862306a36Sopenharmony_ci}; 61962306a36Sopenharmony_ci 62062306a36Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 62162306a36Sopenharmony_ci{ 62262306a36Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 62362306a36Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 62462306a36Sopenharmony_ci 62562306a36Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 62662306a36Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 4); 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci gspca_dev->exposure = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 62962306a36Sopenharmony_ci /* Mostly limited by URB timeouts */ 63062306a36Sopenharmony_ci /* XXX: make dynamic based on frame rate? */ 63162306a36Sopenharmony_ci V4L2_CID_EXPOSURE, 0, 800, 1, 350); 63262306a36Sopenharmony_ci gspca_dev->gain = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 63362306a36Sopenharmony_ci V4L2_CID_GAIN, 0, 511, 1, 128); 63462306a36Sopenharmony_ci sd->blue = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 63562306a36Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 0, 1023, 1, 80); 63662306a36Sopenharmony_ci sd->red = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 63762306a36Sopenharmony_ci V4L2_CID_RED_BALANCE, 0, 1023, 1, 295); 63862306a36Sopenharmony_ci 63962306a36Sopenharmony_ci if (hdl->error) { 64062306a36Sopenharmony_ci gspca_err(gspca_dev, "Could not initialize controls\n"); 64162306a36Sopenharmony_ci return hdl->error; 64262306a36Sopenharmony_ci } 64362306a36Sopenharmony_ci return 0; 64462306a36Sopenharmony_ci} 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci/* sub-driver description */ 64762306a36Sopenharmony_cistatic const struct sd_desc sd_desc = { 64862306a36Sopenharmony_ci .name = MODULE_NAME, 64962306a36Sopenharmony_ci .config = sd_config, 65062306a36Sopenharmony_ci .init = sd_init, 65162306a36Sopenharmony_ci .init_controls = sd_init_controls, 65262306a36Sopenharmony_ci .start = sd_start, 65362306a36Sopenharmony_ci .pkt_scan = sd_pkt_scan, 65462306a36Sopenharmony_ci}; 65562306a36Sopenharmony_ci 65662306a36Sopenharmony_ci/* Table of supported USB devices */ 65762306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = { 65862306a36Sopenharmony_ci /* Commented out devices should be related */ 65962306a36Sopenharmony_ci /* AS: AmScope, TT: ToupTek */ 66062306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6035) }, TT UCMOS00350KPA */ 66162306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6130) }, TT UCMOS01300KPA */ 66262306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6200) }, TT UCMOS02000KPA */ 66362306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6310) }, TT UCMOS03100KPA */ 66462306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6510) }, TT UCMOS05100KPA */ 66562306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6800) }, TT UCMOS08000KPA */ 66662306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6801) }, TT UCMOS08000KPB */ 66762306a36Sopenharmony_ci { USB_DEVICE(0x0547, 0x6801) }, /* TT UCMOS08000KPB, AS MU800 */ 66862306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6900) }, TT UCMOS09000KPA */ 66962306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6901) }, TT UCMOS09000KPB */ 67062306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6010) }, TT UCMOS10000KPA */ 67162306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6014) }, TT UCMOS14000KPA */ 67262306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6131) }, TT UCMOS01300KMA */ 67362306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x6511) }, TT UCMOS05100KMA */ 67462306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8080) }, TT UHCCD00800KPA */ 67562306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8140) }, TT UHCCD01400KPA */ 67662306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8141) }, TT EXCCD01400KPA */ 67762306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8200) }, TT UHCCD02000KPA */ 67862306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8201) }, TT UHCCD02000KPB */ 67962306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8310) }, TT UHCCD03100KPA */ 68062306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8500) }, TT UHCCD05000KPA */ 68162306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8510) }, TT UHCCD05100KPA */ 68262306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8600) }, TT UHCCD06000KPA */ 68362306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8800) }, TT UHCCD08000KPA */ 68462306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x8315) }, TT UHCCD03150KPA */ 68562306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7800) }, TT UHCCD00800KMA */ 68662306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7140) }, TT UHCCD01400KMA */ 68762306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7141) }, TT UHCCD01400KMB */ 68862306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7200) }, TT UHCCD02000KMA */ 68962306a36Sopenharmony_ci /* { USB_DEVICE(0x0547, 0x7315) }, TT UHCCD03150KMA */ 69062306a36Sopenharmony_ci { } 69162306a36Sopenharmony_ci}; 69262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 69562306a36Sopenharmony_ci const struct usb_device_id *id) 69662306a36Sopenharmony_ci{ 69762306a36Sopenharmony_ci return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd), 69862306a36Sopenharmony_ci THIS_MODULE); 69962306a36Sopenharmony_ci} 70062306a36Sopenharmony_ci 70162306a36Sopenharmony_cistatic struct usb_driver sd_driver = { 70262306a36Sopenharmony_ci .name = MODULE_NAME, 70362306a36Sopenharmony_ci .id_table = device_table, 70462306a36Sopenharmony_ci .probe = sd_probe, 70562306a36Sopenharmony_ci .disconnect = gspca_disconnect, 70662306a36Sopenharmony_ci#ifdef CONFIG_PM 70762306a36Sopenharmony_ci .suspend = gspca_suspend, 70862306a36Sopenharmony_ci .resume = gspca_resume, 70962306a36Sopenharmony_ci#endif 71062306a36Sopenharmony_ci}; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_cistatic int __init sd_mod_init(void) 71362306a36Sopenharmony_ci{ 71462306a36Sopenharmony_ci int ret; 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_ci ret = usb_register(&sd_driver); 71762306a36Sopenharmony_ci if (ret < 0) 71862306a36Sopenharmony_ci return ret; 71962306a36Sopenharmony_ci return 0; 72062306a36Sopenharmony_ci} 72162306a36Sopenharmony_cistatic void __exit sd_mod_exit(void) 72262306a36Sopenharmony_ci{ 72362306a36Sopenharmony_ci usb_deregister(&sd_driver); 72462306a36Sopenharmony_ci} 72562306a36Sopenharmony_ci 72662306a36Sopenharmony_cimodule_init(sd_mod_init); 72762306a36Sopenharmony_cimodule_exit(sd_mod_exit); 728