cregit-Linux how code gets into the kernel

Release 4.7 drivers/media/usb/gspca/jeilinj.c

/*
 * Jeilinj subdriver
 *
 * Supports some Jeilin dual-mode cameras which use bulk transport and
 * download raw JPEG data.
 *
 * Copyright (C) 2009 Theodore Kilgore
 *
 * Sportscam DV15 support and control settings are
 * Copyright (C) 2011 Patrice Chotard
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt


#define MODULE_NAME "jeilinj"

#include <linux/slab.h>
#include "gspca.h"
#include "jpeg.h"

MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
MODULE_DESCRIPTION("GSPCA/JEILINJ USB Camera Driver");
MODULE_LICENSE("GPL");

/* Default timeouts, in ms */

#define JEILINJ_CMD_TIMEOUT 500

#define JEILINJ_CMD_DELAY 160

#define JEILINJ_DATA_TIMEOUT 1000

/* Maximum transfer size to use. */

#define JEILINJ_MAX_TRANSFER 0x200

#define FRAME_HEADER_LEN 0x10

#define FRAME_START 0xFFFFFFFF

enum {
	
SAKAR_57379,
	
SPORTSCAM_DV15,
};


#define CAMQUALITY_MIN 0	
/* highest cam quality */

#define CAMQUALITY_MAX 97	
/* lowest cam quality  */

/* Structure to hold all of our device specific stuff */

struct sd {
	
struct gspca_dev gspca_dev;	/* !! must be the first item */
	
int blocks_left;
	
const struct v4l2_pix_format *cap_mode;
	
struct v4l2_ctrl *freq;
	
struct v4l2_ctrl *jpegqual;
	/* Driver stuff */
	
u8 type;
	
u8 quality;				 /* image quality */

#define QUALITY_MIN 35

#define QUALITY_MAX 85

#define QUALITY_DEF 85
	
u8 jpeg_hdr[JPEG_HDR_SZ];
};


struct jlj_command {
	
unsigned char instruction[2];
	
unsigned char ack_wanted;
	
unsigned char delay;
};

/* AFAICT these cameras will only do 320x240. */

static struct v4l2_pix_format jlj_mode[] = {
	{ 320, 240, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
		.bytesperline = 320,
		.sizeimage = 320 * 240,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.priv = 0},
	{ 640, 480, V4L2_PIX_FMT_JPEG, V4L2_FIELD_NONE,
		.bytesperline = 640,
		.sizeimage = 640 * 480,
		.colorspace = V4L2_COLORSPACE_JPEG,
		.priv = 0}
};

/*
 * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
 * and 0x82 for bulk transfer.
 */

/* All commands are two bytes only */

static void jlj_write2(struct gspca_dev *gspca_dev, unsigned char *command) { int retval; if (gspca_dev->usb_err < 0) return; memcpy(gspca_dev->usb_buf, command, 2); retval = usb_bulk_msg(gspca_dev->dev, usb_sndbulkpipe(gspca_dev->dev, 3), gspca_dev->usb_buf, 2, NULL, 500); if (retval < 0) { pr_err("command write [%02x] error %d\n", gspca_dev->usb_buf[0], retval); gspca_dev->usb_err = retval; } }

Contributors

PersonTokensPropCommitsCommitProp
theodore kilgoretheodore kilgore6164.21%125.00%
patrice chotardpatrice chotard3233.68%250.00%
joe perchesjoe perches22.11%125.00%
Total95100.00%4100.00%

/* Responses are one byte only */
static void jlj_read1(struct gspca_dev *gspca_dev, unsigned char *response) { int retval; if (gspca_dev->usb_err < 0) return; retval = usb_bulk_msg(gspca_dev->dev, usb_rcvbulkpipe(gspca_dev->dev, 0x84), gspca_dev->usb_buf, 1, NULL, 500); *response = gspca_dev->usb_buf[0]; if (retval < 0) { pr_err("read command [%02x] error %d\n", gspca_dev->usb_buf[0], retval); gspca_dev->usb_err = retval; } }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard9095.74%133.33%
mauro carvalho chehabmauro carvalho chehab22.13%133.33%
joe perchesjoe perches22.13%133.33%
Total94100.00%3100.00%


static void setfreq(struct gspca_dev *gspca_dev, s32 val) { u8 freq_commands[][2] = { {0x71, 0x80}, {0x70, 0x07} }; freq_commands[0][1] |= val >> 1; jlj_write2(gspca_dev, freq_commands[0]); jlj_write2(gspca_dev, freq_commands[1]); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard6495.52%150.00%
hans verkuilhans verkuil34.48%150.00%
Total67100.00%2100.00%


static void setcamquality(struct gspca_dev *gspca_dev, s32 val) { u8 quality_commands[][2] = { {0x71, 0x1E}, {0x70, 0x06} }; u8 camquality; /* adapt camera quality from jpeg quality */ camquality = ((QUALITY_MAX - val) * CAMQUALITY_MAX) / (QUALITY_MAX - QUALITY_MIN); quality_commands[0][1] += camquality; jlj_write2(gspca_dev, quality_commands[0]); jlj_write2(gspca_dev, quality_commands[1]); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard8395.40%150.00%
hans verkuilhans verkuil44.60%150.00%
Total87100.00%2100.00%


static void setautogain(struct gspca_dev *gspca_dev, s32 val) { u8 autogain_commands[][2] = { {0x94, 0x02}, {0xcf, 0x00} }; autogain_commands[1][1] = val << 4; jlj_write2(gspca_dev, autogain_commands[0]); jlj_write2(gspca_dev, autogain_commands[1]); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard6495.52%150.00%
hans verkuilhans verkuil34.48%150.00%
Total67100.00%2100.00%


static void setred(struct gspca_dev *gspca_dev, s32 val) { u8 setred_commands[][2] = { {0x94, 0x02}, {0xe6, 0x00} }; setred_commands[1][1] = val; jlj_write2(gspca_dev, setred_commands[0]); jlj_write2(gspca_dev, setred_commands[1]); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard6295.38%150.00%
hans verkuilhans verkuil34.62%150.00%
Total65100.00%2100.00%


static void setgreen(struct gspca_dev *gspca_dev, s32 val) { u8 setgreen_commands[][2] = { {0x94, 0x02}, {0xe7, 0x00} }; setgreen_commands[1][1] = val; jlj_write2(gspca_dev, setgreen_commands[0]); jlj_write2(gspca_dev, setgreen_commands[1]); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard6295.38%150.00%
hans verkuilhans verkuil34.62%150.00%
Total65100.00%2100.00%


static void setblue(struct gspca_dev *gspca_dev, s32 val) { u8 setblue_commands[][2] = { {0x94, 0x02}, {0xe9, 0x00} }; setblue_commands[1][1] = val; jlj_write2(gspca_dev, setblue_commands[0]); jlj_write2(gspca_dev, setblue_commands[1]); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard6295.38%150.00%
hans verkuilhans verkuil34.62%150.00%
Total65100.00%2100.00%


static int jlj_start(struct gspca_dev *gspca_dev) { int i; int start_commands_size; u8 response = 0xff; struct sd *sd = (struct sd *) gspca_dev; struct jlj_command start_commands[] = { {{0x71, 0x81}, 0, 0}, {{0x70, 0x05}, 0, JEILINJ_CMD_DELAY}, {{0x95, 0x70}, 1, 0}, {{0x71, 0x81 - gspca_dev->curr_mode}, 0, 0}, {{0x70, 0x04}, 0, JEILINJ_CMD_DELAY}, {{0x95, 0x70}, 1, 0}, {{0x71, 0x00}, 0, 0}, /* start streaming ??*/ {{0x70, 0x08}, 0, JEILINJ_CMD_DELAY}, {{0x95, 0x70}, 1, 0}, #define SPORTSCAM_DV15_CMD_SIZE 9 {{0x94, 0x02}, 0, 0}, {{0xde, 0x24}, 0, 0}, {{0x94, 0x02}, 0, 0}, {{0xdd, 0xf0}, 0, 0}, {{0x94, 0x02}, 0, 0}, {{0xe3, 0x2c}, 0, 0}, {{0x94, 0x02}, 0, 0}, {{0xe4, 0x00}, 0, 0}, {{0x94, 0x02}, 0, 0}, {{0xe5, 0x00}, 0, 0}, {{0x94, 0x02}, 0, 0}, {{0xe6, 0x2c}, 0, 0}, {{0x94, 0x03}, 0, 0}, {{0xaa, 0x00}, 0, 0} }; sd->blocks_left = 0; /* Under Windows, USB spy shows that only the 9 first start * commands are used for SPORTSCAM_DV15 webcam */ if (sd->type == SPORTSCAM_DV15) start_commands_size = SPORTSCAM_DV15_CMD_SIZE; else start_commands_size = ARRAY_SIZE(start_commands); for (i = 0; i < start_commands_size; i++) { jlj_write2(gspca_dev, start_commands[i].instruction); if (start_commands[i].delay) msleep(start_commands[i].delay); if (start_commands[i].ack_wanted) jlj_read1(gspca_dev, &response); } setcamquality(gspca_dev, v4l2_ctrl_g_ctrl(sd->jpegqual)); msleep(2); setfreq(gspca_dev, v4l2_ctrl_g_ctrl(sd->freq)); if (gspca_dev->usb_err < 0) PERR("Start streaming command failed"); return gspca_dev->usb_err; }

Contributors

PersonTokensPropCommitsCommitProp
theodore kilgoretheodore kilgore25154.21%225.00%
patrice chotardpatrice chotard14130.45%450.00%
hans verkuilhans verkuil7015.12%112.50%
mauro carvalho chehabmauro carvalho chehab10.22%112.50%
Total463100.00%8100.00%


static void sd_pkt_scan(struct gspca_dev *gspca_dev, u8 *data, int len) { struct sd *sd = (struct sd *) gspca_dev; int packet_type; u32 header_marker; PDEBUG(D_STREAM, "Got %d bytes out of %d for Block 0", len, JEILINJ_MAX_TRANSFER); if (len != JEILINJ_MAX_TRANSFER) { PDEBUG(D_PACK, "bad length"); goto discard; } /* check if it's start of frame */ header_marker = ((u32 *)data)[0]; if (header_marker == FRAME_START) { sd->blocks_left = data[0x0a] - 1; PDEBUG(D_STREAM, "blocks_left = 0x%x", sd->blocks_left); /* Start a new frame, and add the JPEG header, first thing */ gspca_frame_add(gspca_dev, FIRST_PACKET, sd->jpeg_hdr, JPEG_HDR_SZ); /* Toss line 0 of data block 0, keep the rest. */ gspca_frame_add(gspca_dev, INTER_PACKET, data + FRAME_HEADER_LEN, JEILINJ_MAX_TRANSFER - FRAME_HEADER_LEN); } else if (sd->blocks_left > 0) { PDEBUG(D_STREAM, "%d blocks remaining for frame", sd->blocks_left); sd->blocks_left -= 1; if (sd->blocks_left == 0) packet_type = LAST_PACKET; else packet_type = INTER_PACKET; gspca_frame_add(gspca_dev, packet_type, data, JEILINJ_MAX_TRANSFER); } else goto discard; return; discard: /* Discard data until a new frame starts. */ gspca_dev->last_packet_type = DISCARD_PACKET; }

Contributors

PersonTokensPropCommitsCommitProp
theodore kilgoretheodore kilgore12157.89%133.33%
patrice chotardpatrice chotard7435.41%133.33%
hans de goedehans de goede146.70%133.33%
Total209100.00%3100.00%

/* This function is called at probe time just before sd_init */
static int sd_config(struct gspca_dev *gspca_dev, const struct usb_device_id *id) { struct cam *cam = &gspca_dev->cam; struct sd *dev = (struct sd *) gspca_dev; dev->type = id->driver_info; dev->quality = QUALITY_DEF; cam->cam_mode = jlj_mode; cam->nmodes = ARRAY_SIZE(jlj_mode); cam->bulk = 1; cam->bulk_nurbs = 1; cam->bulk_size = JEILINJ_MAX_TRANSFER; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
theodore kilgoretheodore kilgore6977.53%120.00%
patrice chotardpatrice chotard2022.47%480.00%
Total89100.00%5100.00%


static void sd_stopN(struct gspca_dev *gspca_dev) { int i; u8 *buf; static u8 stop_commands[][2] = { {0x71, 0x00}, {0x70, 0x09}, {0x71, 0x80}, {0x70, 0x05} }; for (;;) { /* get the image remaining blocks */ usb_bulk_msg(gspca_dev->dev, gspca_dev->urb[0]->pipe, gspca_dev->urb[0]->transfer_buffer, JEILINJ_MAX_TRANSFER, NULL, JEILINJ_DATA_TIMEOUT); /* search for 0xff 0xd9 (EOF for JPEG) */ i = 0; buf = gspca_dev->urb[0]->transfer_buffer; while ((i < (JEILINJ_MAX_TRANSFER - 1)) && ((buf[i] != 0xff) || (buf[i+1] != 0xd9))) i++; if (i != (JEILINJ_MAX_TRANSFER - 1)) /* last remaining block found */ break; } for (i = 0; i < ARRAY_SIZE(stop_commands); i++) jlj_write2(gspca_dev, stop_commands[i]); }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard15585.16%133.33%
theodore kilgoretheodore kilgore2614.29%133.33%
jean-francois moinejean-francois moine10.55%133.33%
Total182100.00%3100.00%

/* this function is called at probe and resume time */
static int sd_init(struct gspca_dev *gspca_dev) { return gspca_dev->usb_err; }

Contributors

PersonTokensPropCommitsCommitProp
theodore kilgoretheodore kilgore1381.25%150.00%
patrice chotardpatrice chotard318.75%150.00%
Total16100.00%2100.00%

/* Set up for getting frames. */
static int sd_start(struct gspca_dev *gspca_dev) { struct sd *dev = (struct sd *) gspca_dev; /* create the JPEG header */ jpeg_define(dev->jpeg_hdr, gspca_dev->pixfmt.height, gspca_dev->pixfmt.width, 0x21); /* JPEG 422 */ jpeg_set_qual(dev->jpeg_hdr, dev->quality); PDEBUG(D_STREAM, "Start streaming at %dx%d", gspca_dev->pixfmt.height, gspca_dev->pixfmt.width); jlj_start(gspca_dev); return gspca_dev->usb_err; }

Contributors

PersonTokensPropCommitsCommitProp
theodore kilgoretheodore kilgore6676.74%125.00%
patrice chotardpatrice chotard1213.95%250.00%
ondrej zaryondrej zary89.30%125.00%
Total86100.00%4100.00%

/* Table of supported USB devices */ static const struct usb_device_id device_table[] = { {USB_DEVICE(0x0979, 0x0280), .driver_info = SAKAR_57379}, {USB_DEVICE(0x0979, 0x0270), .driver_info = SPORTSCAM_DV15}, {} }; MODULE_DEVICE_TABLE(usb, device_table);
static int sd_s_ctrl(struct v4l2_ctrl *ctrl) { struct gspca_dev *gspca_dev = container_of(ctrl->handler, struct gspca_dev, ctrl_handler); struct sd *sd = (struct sd *)gspca_dev; gspca_dev->usb_err = 0; if (!gspca_dev->streaming) return 0; switch (ctrl->id) { case V4L2_CID_POWER_LINE_FREQUENCY: setfreq(gspca_dev, ctrl->val); break; case V4L2_CID_RED_BALANCE: setred(gspca_dev, ctrl->val); break; case V4L2_CID_GAIN: setgreen(gspca_dev, ctrl->val); break; case V4L2_CID_BLUE_BALANCE: setblue(gspca_dev, ctrl->val); break; case V4L2_CID_AUTOGAIN: setautogain(gspca_dev, ctrl->val); break; case V4L2_CID_JPEG_COMPRESSION_QUALITY: jpeg_set_qual(sd->jpeg_hdr, ctrl->val); setcamquality(gspca_dev, ctrl->val); break; } return gspca_dev->usb_err; }

Contributors

PersonTokensPropCommitsCommitProp
hans verkuilhans verkuil11472.15%150.00%
patrice chotardpatrice chotard4427.85%150.00%
Total158100.00%2100.00%

static const struct v4l2_ctrl_ops sd_ctrl_ops = { .s_ctrl = sd_s_ctrl, };
static int sd_init_controls(struct gspca_dev *gspca_dev) { struct sd *sd = (struct sd *)gspca_dev; struct v4l2_ctrl_handler *hdl = &gspca_dev->ctrl_handler; static const struct v4l2_ctrl_config custom_autogain = { .ops = &sd_ctrl_ops, .id = V4L2_CID_AUTOGAIN, .type = V4L2_CTRL_TYPE_INTEGER, .name = "Automatic Gain (and Exposure)", .max = 3, .step = 1, .def = 0, }; gspca_dev->vdev.ctrl_handler = hdl; v4l2_ctrl_handler_init(hdl, 6); sd->freq = v4l2_ctrl_new_std_menu(hdl, &sd_ctrl_ops, V4L2_CID_POWER_LINE_FREQUENCY, V4L2_CID_POWER_LINE_FREQUENCY_60HZ, 1, V4L2_CID_POWER_LINE_FREQUENCY_60HZ); v4l2_ctrl_new_custom(hdl, &custom_autogain, NULL); v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_RED_BALANCE, 0, 3, 1, 2); v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_GAIN, 0, 3, 1, 2); v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_BLUE_BALANCE, 0, 3, 1, 2); sd->jpegqual = v4l2_ctrl_new_std(hdl, &sd_ctrl_ops, V4L2_CID_JPEG_COMPRESSION_QUALITY, QUALITY_MIN, QUALITY_MAX, 1, QUALITY_DEF); if (hdl->error) { pr_err("Could not initialize controls\n"); return hdl->error; } return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hans verkuilhans verkuil18182.65%150.00%
patrice chotardpatrice chotard3817.35%150.00%
Total219100.00%2100.00%


static int sd_set_jcomp(struct gspca_dev *gspca_dev, const struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; v4l2_ctrl_s_ctrl(sd->jpegqual, jcomp->quality); return 0; }

Contributors

PersonTokensPropCommitsCommitProp
hans verkuilhans verkuil3172.09%266.67%
patrice chotardpatrice chotard1227.91%133.33%
Total43100.00%3100.00%


static int sd_get_jcomp(struct gspca_dev *gspca_dev, struct v4l2_jpegcompression *jcomp) { struct sd *sd = (struct sd *) gspca_dev; memset(jcomp, 0, sizeof *jcomp); jcomp->quality = v4l2_ctrl_g_ctrl(sd->jpegqual); jcomp->jpeg_markers = V4L2_JPEG_MARKER_DHT | V4L2_JPEG_MARKER_DQT; return 0; }

Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard5793.44%150.00%
hans verkuilhans verkuil46.56%150.00%
Total61100.00%2100.00%

/* sub-driver description */ static const struct sd_desc sd_desc_sakar_57379 = { .name = MODULE_NAME, .config = sd_config, .init = sd_init, .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, }; /* sub-driver description */ static const struct sd_desc sd_desc_sportscam_dv15 = { .name = MODULE_NAME, .config = sd_config, .init = sd_init, .init_controls = sd_init_controls, .start = sd_start, .stopN = sd_stopN, .pkt_scan = sd_pkt_scan, .get_jcomp = sd_get_jcomp, .set_jcomp = sd_set_jcomp, }; static const struct sd_desc *sd_desc[2] = { &sd_desc_sakar_57379, &sd_desc_sportscam_dv15 }; /* -- device connect -- */
static int sd_probe(struct usb_interface *intf, const struct usb_device_id *id) { return gspca_dev_probe(intf, id, sd_desc[id->driver_info], sizeof(struct sd), THIS_MODULE); }

Contributors

PersonTokensPropCommitsCommitProp
theodore kilgoretheodore kilgore3587.50%150.00%
patrice chotardpatrice chotard512.50%150.00%
Total40100.00%2100.00%

static struct usb_driver sd_driver = { .name = MODULE_NAME, .id_table = device_table, .probe = sd_probe, .disconnect = gspca_disconnect, #ifdef CONFIG_PM .suspend = gspca_suspend, .resume = gspca_resume, .reset_resume = gspca_resume, #endif }; module_usb_driver(sd_driver);

Overall Contributors

PersonTokensPropCommitsCommitProp
patrice chotardpatrice chotard125447.68%526.32%
theodore kilgoretheodore kilgore87633.31%210.53%
hans verkuilhans verkuil44717.00%210.53%
hans de goedehans de goede190.72%210.53%
joe perchesjoe perches110.42%15.26%
ondrej zaryondrej zary80.30%15.26%
jean-francois moinejean-francois moine70.27%315.79%
tejun heotejun heo30.11%15.26%
mauro carvalho chehabmauro carvalho chehab30.11%15.26%
greg kroah-hartmangreg kroah-hartman20.08%15.26%
Total2630100.00%19100.00%
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
{% endraw %}