18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Jeilinj subdriver 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Supports some Jeilin dual-mode cameras which use bulk transport and 68c2ecf20Sopenharmony_ci * download raw JPEG data. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright (C) 2009 Theodore Kilgore 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Sportscam DV15 support and control settings are 118c2ecf20Sopenharmony_ci * Copyright (C) 2011 Patrice Chotard 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#define MODULE_NAME "jeilinj" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ci#include <linux/slab.h> 198c2ecf20Sopenharmony_ci#include "gspca.h" 208c2ecf20Sopenharmony_ci#include "jpeg.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ciMODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>"); 238c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver"); 248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 258c2ecf20Sopenharmony_ci 268c2ecf20Sopenharmony_ci/* Default timeouts, in ms */ 278c2ecf20Sopenharmony_ci#define JEILINJ_CMD_TIMEOUT 500 288c2ecf20Sopenharmony_ci#define JEILINJ_CMD_DELAY 160 298c2ecf20Sopenharmony_ci#define JEILINJ_DATA_TIMEOUT 1000 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci/* Maximum transfer size to use. */ 328c2ecf20Sopenharmony_ci#define JEILINJ_MAX_TRANSFER 0x200 338c2ecf20Sopenharmony_ci#define FRAME_HEADER_LEN 0x10 348c2ecf20Sopenharmony_ci#define FRAME_START 0xFFFFFFFF 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cienum { 378c2ecf20Sopenharmony_ci SAKAR_57379, 388c2ecf20Sopenharmony_ci SPORTSCAM_DV15, 398c2ecf20Sopenharmony_ci}; 408c2ecf20Sopenharmony_ci 418c2ecf20Sopenharmony_ci#define CAMQUALITY_MIN 0 /* highest cam quality */ 428c2ecf20Sopenharmony_ci#define CAMQUALITY_MAX 97 /* lowest cam quality */ 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci/* Structure to hold all of our device specific stuff */ 458c2ecf20Sopenharmony_cistruct sd { 468c2ecf20Sopenharmony_ci struct gspca_dev gspca_dev; /* !! must be the first item */ 478c2ecf20Sopenharmony_ci int blocks_left; 488c2ecf20Sopenharmony_ci const struct v4l2_pix_format *cap_mode; 498c2ecf20Sopenharmony_ci struct v4l2_ctrl *freq; 508c2ecf20Sopenharmony_ci struct v4l2_ctrl *jpegqual; 518c2ecf20Sopenharmony_ci /* Driver stuff */ 528c2ecf20Sopenharmony_ci u8 type; 538c2ecf20Sopenharmony_ci u8 quality; /* image quality */ 548c2ecf20Sopenharmony_ci#define QUALITY_MIN 35 558c2ecf20Sopenharmony_ci#define QUALITY_MAX 85 568c2ecf20Sopenharmony_ci#define QUALITY_DEF 85 578c2ecf20Sopenharmony_ci u8 jpeg_hdr[JPEG_HDR_SZ]; 588c2ecf20Sopenharmony_ci}; 598c2ecf20Sopenharmony_ci 608c2ecf20Sopenharmony_cistruct jlj_command { 618c2ecf20Sopenharmony_ci unsigned char instruction[2]; 628c2ecf20Sopenharmony_ci unsigned char ack_wanted; 638c2ecf20Sopenharmony_ci unsigned char delay; 648c2ecf20Sopenharmony_ci}; 658c2ecf20Sopenharmony_ci 668c2ecf20Sopenharmony_ci/* AFAICT these cameras will only do 320x240. */ 678c2ecf20Sopenharmony_cistatic struct v4l2_pix_format jlj_mode[] = { 688c2ecf20Sopenharmony_ci { 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, 698c2ecf20Sopenharmony_ci .bytesperline = 320, 708c2ecf20Sopenharmony_ci .sizeimage = 320 * 240, 718c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_JPEG, 728c2ecf20Sopenharmony_ci .priv = 0}, 738c2ecf20Sopenharmony_ci { 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE, 748c2ecf20Sopenharmony_ci .bytesperline = 640, 758c2ecf20Sopenharmony_ci .sizeimage = 640 * 480, 768c2ecf20Sopenharmony_ci .colorspace = V4L2_COLORSPACE_JPEG, 778c2ecf20Sopenharmony_ci .priv = 0} 788c2ecf20Sopenharmony_ci}; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci/* 818c2ecf20Sopenharmony_ci * cam uses endpoint 0x03 to send commands, 0x84 for read commands, 828c2ecf20Sopenharmony_ci * and 0x82 for bulk transfer. 838c2ecf20Sopenharmony_ci */ 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci/* All commands are two bytes only */ 868c2ecf20Sopenharmony_cistatic void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) 878c2ecf20Sopenharmony_ci{ 888c2ecf20Sopenharmony_ci int retval; 898c2ecf20Sopenharmony_ci 908c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 918c2ecf20Sopenharmony_ci return; 928c2ecf20Sopenharmony_ci memcpy(gspca_dev->usb_buf, command, 2); 938c2ecf20Sopenharmony_ci retval = usb_bulk_msg(gspca_dev->dev, 948c2ecf20Sopenharmony_ci usb_sndbulkpipe(gspca_dev->dev, 3), 958c2ecf20Sopenharmony_ci gspca_dev->usb_buf, 2, NULL, 500); 968c2ecf20Sopenharmony_ci if (retval < 0) { 978c2ecf20Sopenharmony_ci pr_err("command write [%02x] error %d\n", 988c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0], retval); 998c2ecf20Sopenharmony_ci gspca_dev->usb_err = retval; 1008c2ecf20Sopenharmony_ci } 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_ci/* Responses are one byte only */ 1048c2ecf20Sopenharmony_cistatic void jlj_read1(struct gspca_dev *gspca_dev, unsigned char *response) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci int retval; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 1098c2ecf20Sopenharmony_ci return; 1108c2ecf20Sopenharmony_ci retval = usb_bulk_msg(gspca_dev->dev, 1118c2ecf20Sopenharmony_ci usb_rcvbulkpipe(gspca_dev->dev, 0x84), 1128c2ecf20Sopenharmony_ci gspca_dev->usb_buf, 1, NULL, 500); 1138c2ecf20Sopenharmony_ci *response = gspca_dev->usb_buf[0]; 1148c2ecf20Sopenharmony_ci if (retval < 0) { 1158c2ecf20Sopenharmony_ci pr_err("read command [%02x] error %d\n", 1168c2ecf20Sopenharmony_ci gspca_dev->usb_buf[0], retval); 1178c2ecf20Sopenharmony_ci gspca_dev->usb_err = retval; 1188c2ecf20Sopenharmony_ci } 1198c2ecf20Sopenharmony_ci} 1208c2ecf20Sopenharmony_ci 1218c2ecf20Sopenharmony_cistatic void setfreq(struct gspca_dev *gspca_dev, s32 val) 1228c2ecf20Sopenharmony_ci{ 1238c2ecf20Sopenharmony_ci u8 freq_commands[][2] = { 1248c2ecf20Sopenharmony_ci {0x71, 0x80}, 1258c2ecf20Sopenharmony_ci {0x70, 0x07} 1268c2ecf20Sopenharmony_ci }; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci freq_commands[0][1] |= val >> 1; 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, freq_commands[0]); 1318c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, freq_commands[1]); 1328c2ecf20Sopenharmony_ci} 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic void setcamquality(struct gspca_dev *gspca_dev, s32 val) 1358c2ecf20Sopenharmony_ci{ 1368c2ecf20Sopenharmony_ci u8 quality_commands[][2] = { 1378c2ecf20Sopenharmony_ci {0x71, 0x1E}, 1388c2ecf20Sopenharmony_ci {0x70, 0x06} 1398c2ecf20Sopenharmony_ci }; 1408c2ecf20Sopenharmony_ci u8 camquality; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* adapt camera quality from jpeg quality */ 1438c2ecf20Sopenharmony_ci camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX) 1448c2ecf20Sopenharmony_ci / (QUALITY_MAX - QUALITY_MIN); 1458c2ecf20Sopenharmony_ci quality_commands[0][1] += camquality; 1468c2ecf20Sopenharmony_ci 1478c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, quality_commands[0]); 1488c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, quality_commands[1]); 1498c2ecf20Sopenharmony_ci} 1508c2ecf20Sopenharmony_ci 1518c2ecf20Sopenharmony_cistatic void setautogain(struct gspca_dev *gspca_dev, s32 val) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci u8 autogain_commands[][2] = { 1548c2ecf20Sopenharmony_ci {0x94, 0x02}, 1558c2ecf20Sopenharmony_ci {0xcf, 0x00} 1568c2ecf20Sopenharmony_ci }; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci autogain_commands[1][1] = val << 4; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, autogain_commands[0]); 1618c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, autogain_commands[1]); 1628c2ecf20Sopenharmony_ci} 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_cistatic void setred(struct gspca_dev *gspca_dev, s32 val) 1658c2ecf20Sopenharmony_ci{ 1668c2ecf20Sopenharmony_ci u8 setred_commands[][2] = { 1678c2ecf20Sopenharmony_ci {0x94, 0x02}, 1688c2ecf20Sopenharmony_ci {0xe6, 0x00} 1698c2ecf20Sopenharmony_ci }; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci setred_commands[1][1] = val; 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, setred_commands[0]); 1748c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, setred_commands[1]); 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_cistatic void setgreen(struct gspca_dev *gspca_dev, s32 val) 1788c2ecf20Sopenharmony_ci{ 1798c2ecf20Sopenharmony_ci u8 setgreen_commands[][2] = { 1808c2ecf20Sopenharmony_ci {0x94, 0x02}, 1818c2ecf20Sopenharmony_ci {0xe7, 0x00} 1828c2ecf20Sopenharmony_ci }; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci setgreen_commands[1][1] = val; 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, setgreen_commands[0]); 1878c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, setgreen_commands[1]); 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic void setblue(struct gspca_dev *gspca_dev, s32 val) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci u8 setblue_commands[][2] = { 1938c2ecf20Sopenharmony_ci {0x94, 0x02}, 1948c2ecf20Sopenharmony_ci {0xe9, 0x00} 1958c2ecf20Sopenharmony_ci }; 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_ci setblue_commands[1][1] = val; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, setblue_commands[0]); 2008c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, setblue_commands[1]); 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic int jlj_start(struct gspca_dev *gspca_dev) 2048c2ecf20Sopenharmony_ci{ 2058c2ecf20Sopenharmony_ci int i; 2068c2ecf20Sopenharmony_ci int start_commands_size; 2078c2ecf20Sopenharmony_ci u8 response = 0xff; 2088c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 2098c2ecf20Sopenharmony_ci struct jlj_command start_commands[] = { 2108c2ecf20Sopenharmony_ci {{0x71, 0x81}, 0, 0}, 2118c2ecf20Sopenharmony_ci {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY}, 2128c2ecf20Sopenharmony_ci {{0x95, 0x70}, 1, 0}, 2138c2ecf20Sopenharmony_ci {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0}, 2148c2ecf20Sopenharmony_ci {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY}, 2158c2ecf20Sopenharmony_ci {{0x95, 0x70}, 1, 0}, 2168c2ecf20Sopenharmony_ci {{0x71, 0x00}, 0, 0}, /* start streaming ??*/ 2178c2ecf20Sopenharmony_ci {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY}, 2188c2ecf20Sopenharmony_ci {{0x95, 0x70}, 1, 0}, 2198c2ecf20Sopenharmony_ci#define SPORTSCAM_DV15_CMD_SIZE 9 2208c2ecf20Sopenharmony_ci {{0x94, 0x02}, 0, 0}, 2218c2ecf20Sopenharmony_ci {{0xde, 0x24}, 0, 0}, 2228c2ecf20Sopenharmony_ci {{0x94, 0x02}, 0, 0}, 2238c2ecf20Sopenharmony_ci {{0xdd, 0xf0}, 0, 0}, 2248c2ecf20Sopenharmony_ci {{0x94, 0x02}, 0, 0}, 2258c2ecf20Sopenharmony_ci {{0xe3, 0x2c}, 0, 0}, 2268c2ecf20Sopenharmony_ci {{0x94, 0x02}, 0, 0}, 2278c2ecf20Sopenharmony_ci {{0xe4, 0x00}, 0, 0}, 2288c2ecf20Sopenharmony_ci {{0x94, 0x02}, 0, 0}, 2298c2ecf20Sopenharmony_ci {{0xe5, 0x00}, 0, 0}, 2308c2ecf20Sopenharmony_ci {{0x94, 0x02}, 0, 0}, 2318c2ecf20Sopenharmony_ci {{0xe6, 0x2c}, 0, 0}, 2328c2ecf20Sopenharmony_ci {{0x94, 0x03}, 0, 0}, 2338c2ecf20Sopenharmony_ci {{0xaa, 0x00}, 0, 0} 2348c2ecf20Sopenharmony_ci }; 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci sd->blocks_left = 0; 2378c2ecf20Sopenharmony_ci /* Under Windows, USB spy shows that only the 9 first start 2388c2ecf20Sopenharmony_ci * commands are used for SPORTSCAM_DV15 webcam 2398c2ecf20Sopenharmony_ci */ 2408c2ecf20Sopenharmony_ci if (sd->type == SPORTSCAM_DV15) 2418c2ecf20Sopenharmony_ci start_commands_size = SPORTSCAM_DV15_CMD_SIZE; 2428c2ecf20Sopenharmony_ci else 2438c2ecf20Sopenharmony_ci start_commands_size = ARRAY_SIZE(start_commands); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci for (i = 0; i < start_commands_size; i++) { 2468c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, start_commands[i].instruction); 2478c2ecf20Sopenharmony_ci if (start_commands[i].delay) 2488c2ecf20Sopenharmony_ci msleep(start_commands[i].delay); 2498c2ecf20Sopenharmony_ci if (start_commands[i].ack_wanted) 2508c2ecf20Sopenharmony_ci jlj_read1(gspca_dev, &response); 2518c2ecf20Sopenharmony_ci } 2528c2ecf20Sopenharmony_ci setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); 2538c2ecf20Sopenharmony_ci msleep(2); 2548c2ecf20Sopenharmony_ci setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); 2558c2ecf20Sopenharmony_ci if (gspca_dev->usb_err < 0) 2568c2ecf20Sopenharmony_ci gspca_err(gspca_dev, "Start streaming command failed\n"); 2578c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 2588c2ecf20Sopenharmony_ci} 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_cistatic void sd_pkt_scan(struct gspca_dev *gspca_dev, 2618c2ecf20Sopenharmony_ci u8 *data, int len) 2628c2ecf20Sopenharmony_ci{ 2638c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 2648c2ecf20Sopenharmony_ci int packet_type; 2658c2ecf20Sopenharmony_ci u32 header_marker; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Got %d bytes out of %d for Block 0\n", 2688c2ecf20Sopenharmony_ci len, JEILINJ_MAX_TRANSFER); 2698c2ecf20Sopenharmony_ci if (len != JEILINJ_MAX_TRANSFER) { 2708c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_PACK, "bad length\n"); 2718c2ecf20Sopenharmony_ci goto discard; 2728c2ecf20Sopenharmony_ci } 2738c2ecf20Sopenharmony_ci /* check if it's start of frame */ 2748c2ecf20Sopenharmony_ci header_marker = ((u32 *)data)[0]; 2758c2ecf20Sopenharmony_ci if (header_marker == FRAME_START) { 2768c2ecf20Sopenharmony_ci sd->blocks_left = data[0x0a] - 1; 2778c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "blocks_left = 0x%x\n", 2788c2ecf20Sopenharmony_ci sd->blocks_left); 2798c2ecf20Sopenharmony_ci /* Start a new frame, and add the JPEG header, first thing */ 2808c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, FIRST_PACKET, 2818c2ecf20Sopenharmony_ci sd->jpeg_hdr, JPEG_HDR_SZ); 2828c2ecf20Sopenharmony_ci /* Toss line 0 of data block 0, keep the rest. */ 2838c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, INTER_PACKET, 2848c2ecf20Sopenharmony_ci data + FRAME_HEADER_LEN, 2858c2ecf20Sopenharmony_ci JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); 2868c2ecf20Sopenharmony_ci } else if (sd->blocks_left > 0) { 2878c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "%d blocks remaining for frame\n", 2888c2ecf20Sopenharmony_ci sd->blocks_left); 2898c2ecf20Sopenharmony_ci sd->blocks_left -= 1; 2908c2ecf20Sopenharmony_ci if (sd->blocks_left == 0) 2918c2ecf20Sopenharmony_ci packet_type = LAST_PACKET; 2928c2ecf20Sopenharmony_ci else 2938c2ecf20Sopenharmony_ci packet_type = INTER_PACKET; 2948c2ecf20Sopenharmony_ci gspca_frame_add(gspca_dev, packet_type, 2958c2ecf20Sopenharmony_ci data, JEILINJ_MAX_TRANSFER); 2968c2ecf20Sopenharmony_ci } else 2978c2ecf20Sopenharmony_ci goto discard; 2988c2ecf20Sopenharmony_ci return; 2998c2ecf20Sopenharmony_cidiscard: 3008c2ecf20Sopenharmony_ci /* Discard data until a new frame starts. */ 3018c2ecf20Sopenharmony_ci gspca_dev->last_packet_type = DISCARD_PACKET; 3028c2ecf20Sopenharmony_ci} 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* This function is called at probe time just before sd_init */ 3058c2ecf20Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev, 3068c2ecf20Sopenharmony_ci const struct usb_device_id *id) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct cam *cam = &gspca_dev->cam; 3098c2ecf20Sopenharmony_ci struct sd *dev = (struct sd *) gspca_dev; 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_ci dev->type = id->driver_info; 3128c2ecf20Sopenharmony_ci dev->quality = QUALITY_DEF; 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci cam->cam_mode = jlj_mode; 3158c2ecf20Sopenharmony_ci cam->nmodes = ARRAY_SIZE(jlj_mode); 3168c2ecf20Sopenharmony_ci cam->bulk = 1; 3178c2ecf20Sopenharmony_ci cam->bulk_nurbs = 1; 3188c2ecf20Sopenharmony_ci cam->bulk_size = JEILINJ_MAX_TRANSFER; 3198c2ecf20Sopenharmony_ci return 0; 3208c2ecf20Sopenharmony_ci} 3218c2ecf20Sopenharmony_ci 3228c2ecf20Sopenharmony_cistatic void sd_stopN(struct gspca_dev *gspca_dev) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci int i; 3258c2ecf20Sopenharmony_ci u8 *buf; 3268c2ecf20Sopenharmony_ci static u8 stop_commands[][2] = { 3278c2ecf20Sopenharmony_ci {0x71, 0x00}, 3288c2ecf20Sopenharmony_ci {0x70, 0x09}, 3298c2ecf20Sopenharmony_ci {0x71, 0x80}, 3308c2ecf20Sopenharmony_ci {0x70, 0x05} 3318c2ecf20Sopenharmony_ci }; 3328c2ecf20Sopenharmony_ci 3338c2ecf20Sopenharmony_ci for (;;) { 3348c2ecf20Sopenharmony_ci /* get the image remaining blocks */ 3358c2ecf20Sopenharmony_ci usb_bulk_msg(gspca_dev->dev, 3368c2ecf20Sopenharmony_ci gspca_dev->urb[0]->pipe, 3378c2ecf20Sopenharmony_ci gspca_dev->urb[0]->transfer_buffer, 3388c2ecf20Sopenharmony_ci JEILINJ_MAX_TRANSFER, NULL, 3398c2ecf20Sopenharmony_ci JEILINJ_DATA_TIMEOUT); 3408c2ecf20Sopenharmony_ci 3418c2ecf20Sopenharmony_ci /* search for 0xff 0xd9 (EOF for JPEG) */ 3428c2ecf20Sopenharmony_ci i = 0; 3438c2ecf20Sopenharmony_ci buf = gspca_dev->urb[0]->transfer_buffer; 3448c2ecf20Sopenharmony_ci while ((i < (JEILINJ_MAX_TRANSFER - 1)) && 3458c2ecf20Sopenharmony_ci ((buf[i] != 0xff) || (buf[i+1] != 0xd9))) 3468c2ecf20Sopenharmony_ci i++; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci if (i != (JEILINJ_MAX_TRANSFER - 1)) 3498c2ecf20Sopenharmony_ci /* last remaining block found */ 3508c2ecf20Sopenharmony_ci break; 3518c2ecf20Sopenharmony_ci } 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(stop_commands); i++) 3548c2ecf20Sopenharmony_ci jlj_write2(gspca_dev, stop_commands[i]); 3558c2ecf20Sopenharmony_ci} 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci/* this function is called at probe and resume time */ 3588c2ecf20Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 3618c2ecf20Sopenharmony_ci} 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_ci/* Set up for getting frames. */ 3648c2ecf20Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev) 3658c2ecf20Sopenharmony_ci{ 3668c2ecf20Sopenharmony_ci struct sd *dev = (struct sd *) gspca_dev; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci /* create the JPEG header */ 3698c2ecf20Sopenharmony_ci jpeg_define(dev->jpeg_hdr, gspca_dev->pixfmt.height, 3708c2ecf20Sopenharmony_ci gspca_dev->pixfmt.width, 3718c2ecf20Sopenharmony_ci 0x21); /* JPEG 422 */ 3728c2ecf20Sopenharmony_ci jpeg_set_qual(dev->jpeg_hdr, dev->quality); 3738c2ecf20Sopenharmony_ci gspca_dbg(gspca_dev, D_STREAM, "Start streaming at %dx%d\n", 3748c2ecf20Sopenharmony_ci gspca_dev->pixfmt.height, gspca_dev->pixfmt.width); 3758c2ecf20Sopenharmony_ci jlj_start(gspca_dev); 3768c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 3778c2ecf20Sopenharmony_ci} 3788c2ecf20Sopenharmony_ci 3798c2ecf20Sopenharmony_ci/* Table of supported USB devices */ 3808c2ecf20Sopenharmony_cistatic const struct usb_device_id device_table[] = { 3818c2ecf20Sopenharmony_ci {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379}, 3828c2ecf20Sopenharmony_ci {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15}, 3838c2ecf20Sopenharmony_ci {} 3848c2ecf20Sopenharmony_ci}; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table); 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_cistatic int sd_s_ctrl(struct v4l2_ctrl *ctrl) 3898c2ecf20Sopenharmony_ci{ 3908c2ecf20Sopenharmony_ci struct gspca_dev *gspca_dev = 3918c2ecf20Sopenharmony_ci container_of(ctrl->handler, struct gspca_dev, ctrl_handler); 3928c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci gspca_dev->usb_err = 0; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci if (!gspca_dev->streaming) 3978c2ecf20Sopenharmony_ci return 0; 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_ci switch (ctrl->id) { 4008c2ecf20Sopenharmony_ci case V4L2_CID_POWER_LINE_FREQUENCY: 4018c2ecf20Sopenharmony_ci setfreq(gspca_dev, ctrl->val); 4028c2ecf20Sopenharmony_ci break; 4038c2ecf20Sopenharmony_ci case V4L2_CID_RED_BALANCE: 4048c2ecf20Sopenharmony_ci setred(gspca_dev, ctrl->val); 4058c2ecf20Sopenharmony_ci break; 4068c2ecf20Sopenharmony_ci case V4L2_CID_GAIN: 4078c2ecf20Sopenharmony_ci setgreen(gspca_dev, ctrl->val); 4088c2ecf20Sopenharmony_ci break; 4098c2ecf20Sopenharmony_ci case V4L2_CID_BLUE_BALANCE: 4108c2ecf20Sopenharmony_ci setblue(gspca_dev, ctrl->val); 4118c2ecf20Sopenharmony_ci break; 4128c2ecf20Sopenharmony_ci case V4L2_CID_AUTOGAIN: 4138c2ecf20Sopenharmony_ci setautogain(gspca_dev, ctrl->val); 4148c2ecf20Sopenharmony_ci break; 4158c2ecf20Sopenharmony_ci case V4L2_CID_JPEG_COMPRESSION_QUALITY: 4168c2ecf20Sopenharmony_ci jpeg_set_qual(sd->jpeg_hdr, ctrl->val); 4178c2ecf20Sopenharmony_ci setcamquality(gspca_dev, ctrl->val); 4188c2ecf20Sopenharmony_ci break; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci return gspca_dev->usb_err; 4218c2ecf20Sopenharmony_ci} 4228c2ecf20Sopenharmony_ci 4238c2ecf20Sopenharmony_cistatic const struct v4l2_ctrl_ops sd_ctrl_ops = { 4248c2ecf20Sopenharmony_ci .s_ctrl = sd_s_ctrl, 4258c2ecf20Sopenharmony_ci}; 4268c2ecf20Sopenharmony_ci 4278c2ecf20Sopenharmony_cistatic int sd_init_controls(struct gspca_dev *gspca_dev) 4288c2ecf20Sopenharmony_ci{ 4298c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *)gspca_dev; 4308c2ecf20Sopenharmony_ci struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; 4318c2ecf20Sopenharmony_ci static const struct v4l2_ctrl_config custom_autogain = { 4328c2ecf20Sopenharmony_ci .ops = &sd_ctrl_ops, 4338c2ecf20Sopenharmony_ci .id = V4L2_CID_AUTOGAIN, 4348c2ecf20Sopenharmony_ci .type = V4L2_CTRL_TYPE_INTEGER, 4358c2ecf20Sopenharmony_ci .name = "Automatic Gain (and Exposure)", 4368c2ecf20Sopenharmony_ci .max = 3, 4378c2ecf20Sopenharmony_ci .step = 1, 4388c2ecf20Sopenharmony_ci .def = 0, 4398c2ecf20Sopenharmony_ci }; 4408c2ecf20Sopenharmony_ci 4418c2ecf20Sopenharmony_ci gspca_dev->vdev.ctrl_handler = hdl; 4428c2ecf20Sopenharmony_ci v4l2_ctrl_handler_init(hdl, 6); 4438c2ecf20Sopenharmony_ci sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, 4448c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY, 4458c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, 4468c2ecf20Sopenharmony_ci V4L2_CID_POWER_LINE_FREQUENCY_60HZ); 4478c2ecf20Sopenharmony_ci v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL); 4488c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 4498c2ecf20Sopenharmony_ci V4L2_CID_RED_BALANCE, 0, 3, 1, 2); 4508c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 4518c2ecf20Sopenharmony_ci V4L2_CID_GAIN, 0, 3, 1, 2); 4528c2ecf20Sopenharmony_ci v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 4538c2ecf20Sopenharmony_ci V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2); 4548c2ecf20Sopenharmony_ci sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, 4558c2ecf20Sopenharmony_ci V4L2_CID_JPEG_COMPRESSION_QUALITY, 4568c2ecf20Sopenharmony_ci QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci if (hdl->error) { 4598c2ecf20Sopenharmony_ci pr_err("Could not initialize controls\n"); 4608c2ecf20Sopenharmony_ci return hdl->error; 4618c2ecf20Sopenharmony_ci } 4628c2ecf20Sopenharmony_ci return 0; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic int sd_set_jcomp(struct gspca_dev *gspca_dev, 4668c2ecf20Sopenharmony_ci const struct v4l2_jpegcompression *jcomp) 4678c2ecf20Sopenharmony_ci{ 4688c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4698c2ecf20Sopenharmony_ci 4708c2ecf20Sopenharmony_ci v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); 4718c2ecf20Sopenharmony_ci return 0; 4728c2ecf20Sopenharmony_ci} 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_cistatic int sd_get_jcomp(struct gspca_dev *gspca_dev, 4758c2ecf20Sopenharmony_ci struct v4l2_jpegcompression *jcomp) 4768c2ecf20Sopenharmony_ci{ 4778c2ecf20Sopenharmony_ci struct sd *sd = (struct sd *) gspca_dev; 4788c2ecf20Sopenharmony_ci 4798c2ecf20Sopenharmony_ci memset(jcomp, 0, sizeof *jcomp); 4808c2ecf20Sopenharmony_ci jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); 4818c2ecf20Sopenharmony_ci jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT 4828c2ecf20Sopenharmony_ci | V4L2_JPEG_MARKER_DQT; 4838c2ecf20Sopenharmony_ci return 0; 4848c2ecf20Sopenharmony_ci} 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci/* sub-driver description */ 4888c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc_sakar_57379 = { 4898c2ecf20Sopenharmony_ci .name = MODULE_NAME, 4908c2ecf20Sopenharmony_ci .config = sd_config, 4918c2ecf20Sopenharmony_ci .init = sd_init, 4928c2ecf20Sopenharmony_ci .start = sd_start, 4938c2ecf20Sopenharmony_ci .stopN = sd_stopN, 4948c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 4958c2ecf20Sopenharmony_ci}; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/* sub-driver description */ 4988c2ecf20Sopenharmony_cistatic const struct sd_desc sd_desc_sportscam_dv15 = { 4998c2ecf20Sopenharmony_ci .name = MODULE_NAME, 5008c2ecf20Sopenharmony_ci .config = sd_config, 5018c2ecf20Sopenharmony_ci .init = sd_init, 5028c2ecf20Sopenharmony_ci .init_controls = sd_init_controls, 5038c2ecf20Sopenharmony_ci .start = sd_start, 5048c2ecf20Sopenharmony_ci .stopN = sd_stopN, 5058c2ecf20Sopenharmony_ci .pkt_scan = sd_pkt_scan, 5068c2ecf20Sopenharmony_ci .get_jcomp = sd_get_jcomp, 5078c2ecf20Sopenharmony_ci .set_jcomp = sd_set_jcomp, 5088c2ecf20Sopenharmony_ci}; 5098c2ecf20Sopenharmony_ci 5108c2ecf20Sopenharmony_cistatic const struct sd_desc *sd_desc[2] = { 5118c2ecf20Sopenharmony_ci &sd_desc_sakar_57379, 5128c2ecf20Sopenharmony_ci &sd_desc_sportscam_dv15 5138c2ecf20Sopenharmony_ci}; 5148c2ecf20Sopenharmony_ci 5158c2ecf20Sopenharmony_ci/* -- device connect -- */ 5168c2ecf20Sopenharmony_cistatic int sd_probe(struct usb_interface *intf, 5178c2ecf20Sopenharmony_ci const struct usb_device_id *id) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci return gspca_dev_probe(intf, id, 5208c2ecf20Sopenharmony_ci sd_desc[id->driver_info], 5218c2ecf20Sopenharmony_ci sizeof(struct sd), 5228c2ecf20Sopenharmony_ci THIS_MODULE); 5238c2ecf20Sopenharmony_ci} 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_cistatic struct usb_driver sd_driver = { 5268c2ecf20Sopenharmony_ci .name = MODULE_NAME, 5278c2ecf20Sopenharmony_ci .id_table = device_table, 5288c2ecf20Sopenharmony_ci .probe = sd_probe, 5298c2ecf20Sopenharmony_ci .disconnect = gspca_disconnect, 5308c2ecf20Sopenharmony_ci#ifdef CONFIG_PM 5318c2ecf20Sopenharmony_ci .suspend = gspca_suspend, 5328c2ecf20Sopenharmony_ci .resume = gspca_resume, 5338c2ecf20Sopenharmony_ci .reset_resume = gspca_resume, 5348c2ecf20Sopenharmony_ci#endif 5358c2ecf20Sopenharmony_ci}; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_cimodule_usb_driver(sd_driver); 538