162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Jeilin JL2005B/C/D library
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#define MODULE_NAME "jl2005bcd"
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/workqueue.h>
1162306a36Sopenharmony_ci#include <linux/slab.h>
1262306a36Sopenharmony_ci#include "gspca.h"
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciMODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
1662306a36Sopenharmony_ciMODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
1762306a36Sopenharmony_ciMODULE_LICENSE("GPL");
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci/* Default timeouts, in ms */
2062306a36Sopenharmony_ci#define JL2005C_CMD_TIMEOUT 500
2162306a36Sopenharmony_ci#define JL2005C_DATA_TIMEOUT 1000
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci/* Maximum transfer size to use. */
2462306a36Sopenharmony_ci#define JL2005C_MAX_TRANSFER 0x200
2562306a36Sopenharmony_ci#define FRAME_HEADER_LEN 16
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci/* specific webcam descriptor */
2962306a36Sopenharmony_cistruct sd {
3062306a36Sopenharmony_ci	struct gspca_dev gspca_dev;  /* !! must be the first item */
3162306a36Sopenharmony_ci	unsigned char firmware_id[6];
3262306a36Sopenharmony_ci	const struct v4l2_pix_format *cap_mode;
3362306a36Sopenharmony_ci	/* Driver stuff */
3462306a36Sopenharmony_ci	struct work_struct work_struct;
3562306a36Sopenharmony_ci	u8 frame_brightness;
3662306a36Sopenharmony_ci	int block_size;	/* block size of camera */
3762306a36Sopenharmony_ci	int vga;	/* 1 if vga cam, 0 if cif cam */
3862306a36Sopenharmony_ci};
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci/* Camera has two resolution settings. What they are depends on model. */
4262306a36Sopenharmony_cistatic const struct v4l2_pix_format cif_mode[] = {
4362306a36Sopenharmony_ci	{176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
4462306a36Sopenharmony_ci		.bytesperline = 176,
4562306a36Sopenharmony_ci		.sizeimage = 176 * 144,
4662306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
4762306a36Sopenharmony_ci		.priv = 0},
4862306a36Sopenharmony_ci	{352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
4962306a36Sopenharmony_ci		.bytesperline = 352,
5062306a36Sopenharmony_ci		.sizeimage = 352 * 288,
5162306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
5262306a36Sopenharmony_ci		.priv = 0},
5362306a36Sopenharmony_ci};
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_cistatic const struct v4l2_pix_format vga_mode[] = {
5662306a36Sopenharmony_ci	{320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
5762306a36Sopenharmony_ci		.bytesperline = 320,
5862306a36Sopenharmony_ci		.sizeimage = 320 * 240,
5962306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
6062306a36Sopenharmony_ci		.priv = 0},
6162306a36Sopenharmony_ci	{640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
6262306a36Sopenharmony_ci		.bytesperline = 640,
6362306a36Sopenharmony_ci		.sizeimage = 640 * 480,
6462306a36Sopenharmony_ci		.colorspace = V4L2_COLORSPACE_SRGB,
6562306a36Sopenharmony_ci		.priv = 0},
6662306a36Sopenharmony_ci};
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci/*
6962306a36Sopenharmony_ci * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
7062306a36Sopenharmony_ci * and 0x82 for bulk data transfer.
7162306a36Sopenharmony_ci */
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci/* All commands are two bytes only */
7462306a36Sopenharmony_cistatic int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
7562306a36Sopenharmony_ci{
7662306a36Sopenharmony_ci	int retval;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	memcpy(gspca_dev->usb_buf, command, 2);
7962306a36Sopenharmony_ci	retval = usb_bulk_msg(gspca_dev->dev,
8062306a36Sopenharmony_ci			usb_sndbulkpipe(gspca_dev->dev, 3),
8162306a36Sopenharmony_ci			gspca_dev->usb_buf, 2, NULL, 500);
8262306a36Sopenharmony_ci	if (retval < 0)
8362306a36Sopenharmony_ci		pr_err("command write [%02x] error %d\n",
8462306a36Sopenharmony_ci		       gspca_dev->usb_buf[0], retval);
8562306a36Sopenharmony_ci	return retval;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci/* Response to a command is one byte in usb_buf[0], only if requested. */
8962306a36Sopenharmony_cistatic int jl2005c_read1(struct gspca_dev *gspca_dev)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	int retval;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	retval = usb_bulk_msg(gspca_dev->dev,
9462306a36Sopenharmony_ci				usb_rcvbulkpipe(gspca_dev->dev, 0x84),
9562306a36Sopenharmony_ci				gspca_dev->usb_buf, 1, NULL, 500);
9662306a36Sopenharmony_ci	if (retval < 0)
9762306a36Sopenharmony_ci		pr_err("read command [0x%02x] error %d\n",
9862306a36Sopenharmony_ci		       gspca_dev->usb_buf[0], retval);
9962306a36Sopenharmony_ci	return retval;
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_ci/* Response appears in gspca_dev->usb_buf[0] */
10362306a36Sopenharmony_cistatic int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
10462306a36Sopenharmony_ci{
10562306a36Sopenharmony_ci	int retval;
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	static u8 instruction[2] = {0x95, 0x00};
10862306a36Sopenharmony_ci	/* put register to read in byte 1 */
10962306a36Sopenharmony_ci	instruction[1] = reg;
11062306a36Sopenharmony_ci	/* Send the read request */
11162306a36Sopenharmony_ci	retval = jl2005c_write2(gspca_dev, instruction);
11262306a36Sopenharmony_ci	if (retval < 0)
11362306a36Sopenharmony_ci		return retval;
11462306a36Sopenharmony_ci	retval = jl2005c_read1(gspca_dev);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return retval;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	int i;
12262306a36Sopenharmony_ci	int retval;
12362306a36Sopenharmony_ci	int frame_brightness = 0;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	static u8 instruction[2] = {0x7f, 0x01};
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	retval = jl2005c_write2(gspca_dev, instruction);
12862306a36Sopenharmony_ci	if (retval < 0)
12962306a36Sopenharmony_ci		return retval;
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci	i = 0;
13262306a36Sopenharmony_ci	while (i < 20 && !frame_brightness) {
13362306a36Sopenharmony_ci		/* If we tried 20 times, give up. */
13462306a36Sopenharmony_ci		retval = jl2005c_read_reg(gspca_dev, 0x7e);
13562306a36Sopenharmony_ci		if (retval < 0)
13662306a36Sopenharmony_ci			return retval;
13762306a36Sopenharmony_ci		frame_brightness = gspca_dev->usb_buf[0];
13862306a36Sopenharmony_ci		retval = jl2005c_read_reg(gspca_dev, 0x7d);
13962306a36Sopenharmony_ci		if (retval < 0)
14062306a36Sopenharmony_ci			return retval;
14162306a36Sopenharmony_ci		i++;
14262306a36Sopenharmony_ci	}
14362306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_FRAM, "frame_brightness is 0x%02x\n",
14462306a36Sopenharmony_ci		  gspca_dev->usb_buf[0]);
14562306a36Sopenharmony_ci	return retval;
14662306a36Sopenharmony_ci}
14762306a36Sopenharmony_ci
14862306a36Sopenharmony_cistatic int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
14962306a36Sopenharmony_ci						    unsigned char value)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	int retval;
15262306a36Sopenharmony_ci	u8 instruction[2];
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	instruction[0] = reg;
15562306a36Sopenharmony_ci	instruction[1] = value;
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	retval = jl2005c_write2(gspca_dev, instruction);
15862306a36Sopenharmony_ci	if (retval < 0)
15962306a36Sopenharmony_ci			return retval;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return retval;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct sd *sd = (struct sd *)gspca_dev;
16762306a36Sopenharmony_ci	int i = 0;
16862306a36Sopenharmony_ci	int retval;
16962306a36Sopenharmony_ci	static const unsigned char regs_to_read[] = {
17062306a36Sopenharmony_ci		0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f
17162306a36Sopenharmony_ci	};
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n");
17462306a36Sopenharmony_ci	/* Read the first ID byte once for warmup */
17562306a36Sopenharmony_ci	retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
17662306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "response is %02x\n",
17762306a36Sopenharmony_ci		  gspca_dev->usb_buf[0]);
17862306a36Sopenharmony_ci	if (retval < 0)
17962306a36Sopenharmony_ci		return retval;
18062306a36Sopenharmony_ci	/* Now actually get the ID string */
18162306a36Sopenharmony_ci	for (i = 0; i < 6; i++) {
18262306a36Sopenharmony_ci		retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
18362306a36Sopenharmony_ci		if (retval < 0)
18462306a36Sopenharmony_ci			return retval;
18562306a36Sopenharmony_ci		sd->firmware_id[i] = gspca_dev->usb_buf[0];
18662306a36Sopenharmony_ci	}
18762306a36Sopenharmony_ci	gspca_dbg(gspca_dev, D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x\n",
18862306a36Sopenharmony_ci		  sd->firmware_id[0],
18962306a36Sopenharmony_ci		  sd->firmware_id[1],
19062306a36Sopenharmony_ci		  sd->firmware_id[2],
19162306a36Sopenharmony_ci		  sd->firmware_id[3],
19262306a36Sopenharmony_ci		  sd->firmware_id[4],
19362306a36Sopenharmony_ci		  sd->firmware_id[5]);
19462306a36Sopenharmony_ci	return 0;
19562306a36Sopenharmony_ci}
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_cistatic int jl2005c_stream_start_vga_lg
19862306a36Sopenharmony_ci		    (struct gspca_dev *gspca_dev)
19962306a36Sopenharmony_ci{
20062306a36Sopenharmony_ci	int i;
20162306a36Sopenharmony_ci	int retval = -1;
20262306a36Sopenharmony_ci	static u8 instruction[][2] = {
20362306a36Sopenharmony_ci		{0x05, 0x00},
20462306a36Sopenharmony_ci		{0x7c, 0x00},
20562306a36Sopenharmony_ci		{0x7d, 0x18},
20662306a36Sopenharmony_ci		{0x02, 0x00},
20762306a36Sopenharmony_ci		{0x01, 0x00},
20862306a36Sopenharmony_ci		{0x04, 0x52},
20962306a36Sopenharmony_ci	};
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
21262306a36Sopenharmony_ci		msleep(60);
21362306a36Sopenharmony_ci		retval = jl2005c_write2(gspca_dev, instruction[i]);
21462306a36Sopenharmony_ci		if (retval < 0)
21562306a36Sopenharmony_ci			return retval;
21662306a36Sopenharmony_ci	}
21762306a36Sopenharmony_ci	msleep(60);
21862306a36Sopenharmony_ci	return retval;
21962306a36Sopenharmony_ci}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_cistatic int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
22262306a36Sopenharmony_ci{
22362306a36Sopenharmony_ci	int i;
22462306a36Sopenharmony_ci	int retval = -1;
22562306a36Sopenharmony_ci	static u8 instruction[][2] = {
22662306a36Sopenharmony_ci		{0x06, 0x00},
22762306a36Sopenharmony_ci		{0x7c, 0x00},
22862306a36Sopenharmony_ci		{0x7d, 0x1a},
22962306a36Sopenharmony_ci		{0x02, 0x00},
23062306a36Sopenharmony_ci		{0x01, 0x00},
23162306a36Sopenharmony_ci		{0x04, 0x52},
23262306a36Sopenharmony_ci	};
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
23562306a36Sopenharmony_ci		msleep(60);
23662306a36Sopenharmony_ci		retval = jl2005c_write2(gspca_dev, instruction[i]);
23762306a36Sopenharmony_ci		if (retval < 0)
23862306a36Sopenharmony_ci			return retval;
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci	msleep(60);
24162306a36Sopenharmony_ci	return retval;
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	int i;
24762306a36Sopenharmony_ci	int retval = -1;
24862306a36Sopenharmony_ci	static u8 instruction[][2] = {
24962306a36Sopenharmony_ci		{0x05, 0x00},
25062306a36Sopenharmony_ci		{0x7c, 0x00},
25162306a36Sopenharmony_ci		{0x7d, 0x30},
25262306a36Sopenharmony_ci		{0x02, 0x00},
25362306a36Sopenharmony_ci		{0x01, 0x00},
25462306a36Sopenharmony_ci		{0x04, 0x42},
25562306a36Sopenharmony_ci	};
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
25862306a36Sopenharmony_ci		msleep(60);
25962306a36Sopenharmony_ci		retval = jl2005c_write2(gspca_dev, instruction[i]);
26062306a36Sopenharmony_ci		if (retval < 0)
26162306a36Sopenharmony_ci			return retval;
26262306a36Sopenharmony_ci	}
26362306a36Sopenharmony_ci	msleep(60);
26462306a36Sopenharmony_ci	return retval;
26562306a36Sopenharmony_ci}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_cistatic int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
26862306a36Sopenharmony_ci{
26962306a36Sopenharmony_ci	int i;
27062306a36Sopenharmony_ci	int retval = -1;
27162306a36Sopenharmony_ci	static u8 instruction[][2] = {
27262306a36Sopenharmony_ci		{0x06, 0x00},
27362306a36Sopenharmony_ci		{0x7c, 0x00},
27462306a36Sopenharmony_ci		{0x7d, 0x32},
27562306a36Sopenharmony_ci		{0x02, 0x00},
27662306a36Sopenharmony_ci		{0x01, 0x00},
27762306a36Sopenharmony_ci		{0x04, 0x42},
27862306a36Sopenharmony_ci	};
27962306a36Sopenharmony_ci
28062306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
28162306a36Sopenharmony_ci		msleep(60);
28262306a36Sopenharmony_ci		retval = jl2005c_write2(gspca_dev, instruction[i]);
28362306a36Sopenharmony_ci		if (retval < 0)
28462306a36Sopenharmony_ci			return retval;
28562306a36Sopenharmony_ci	}
28662306a36Sopenharmony_ci	msleep(60);
28762306a36Sopenharmony_ci	return retval;
28862306a36Sopenharmony_ci}
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci
29162306a36Sopenharmony_cistatic int jl2005c_stop(struct gspca_dev *gspca_dev)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	return jl2005c_write_reg(gspca_dev, 0x07, 0x00);
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci/*
29762306a36Sopenharmony_ci * This function is called as a workqueue function and runs whenever the camera
29862306a36Sopenharmony_ci * is streaming data. Because it is a workqueue function it is allowed to sleep
29962306a36Sopenharmony_ci * so we can use synchronous USB calls. To avoid possible collisions with other
30062306a36Sopenharmony_ci * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
30162306a36Sopenharmony_ci * performing USB operations using it. In practice we don't really need this
30262306a36Sopenharmony_ci * as the camera doesn't provide any controls.
30362306a36Sopenharmony_ci */
30462306a36Sopenharmony_cistatic void jl2005c_dostream(struct work_struct *work)
30562306a36Sopenharmony_ci{
30662306a36Sopenharmony_ci	struct sd *dev = container_of(work, struct sd, work_struct);
30762306a36Sopenharmony_ci	struct gspca_dev *gspca_dev = &dev->gspca_dev;
30862306a36Sopenharmony_ci	int bytes_left = 0; /* bytes remaining in current frame. */
30962306a36Sopenharmony_ci	int data_len;   /* size to use for the next read. */
31062306a36Sopenharmony_ci	int header_read = 0;
31162306a36Sopenharmony_ci	unsigned char header_sig[2] = {0x4a, 0x4c};
31262306a36Sopenharmony_ci	int act_len;
31362306a36Sopenharmony_ci	int packet_type;
31462306a36Sopenharmony_ci	int ret;
31562306a36Sopenharmony_ci	u8 *buffer;
31662306a36Sopenharmony_ci
31762306a36Sopenharmony_ci	buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL);
31862306a36Sopenharmony_ci	if (!buffer) {
31962306a36Sopenharmony_ci		pr_err("Couldn't allocate USB buffer\n");
32062306a36Sopenharmony_ci		goto quit_stream;
32162306a36Sopenharmony_ci	}
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci	while (gspca_dev->present && gspca_dev->streaming) {
32462306a36Sopenharmony_ci#ifdef CONFIG_PM
32562306a36Sopenharmony_ci		if (gspca_dev->frozen)
32662306a36Sopenharmony_ci			break;
32762306a36Sopenharmony_ci#endif
32862306a36Sopenharmony_ci		/* Check if this is a new frame. If so, start the frame first */
32962306a36Sopenharmony_ci		if (!header_read) {
33062306a36Sopenharmony_ci			mutex_lock(&gspca_dev->usb_lock);
33162306a36Sopenharmony_ci			ret = jl2005c_start_new_frame(gspca_dev);
33262306a36Sopenharmony_ci			mutex_unlock(&gspca_dev->usb_lock);
33362306a36Sopenharmony_ci			if (ret < 0)
33462306a36Sopenharmony_ci				goto quit_stream;
33562306a36Sopenharmony_ci			ret = usb_bulk_msg(gspca_dev->dev,
33662306a36Sopenharmony_ci				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
33762306a36Sopenharmony_ci				buffer, JL2005C_MAX_TRANSFER, &act_len,
33862306a36Sopenharmony_ci				JL2005C_DATA_TIMEOUT);
33962306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK,
34062306a36Sopenharmony_ci				  "Got %d bytes out of %d for header\n",
34162306a36Sopenharmony_ci				  act_len, JL2005C_MAX_TRANSFER);
34262306a36Sopenharmony_ci			if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
34362306a36Sopenharmony_ci				goto quit_stream;
34462306a36Sopenharmony_ci			/* Check whether we actually got the first blodk */
34562306a36Sopenharmony_ci			if (memcmp(header_sig, buffer, 2) != 0) {
34662306a36Sopenharmony_ci				pr_err("First block is not the first block\n");
34762306a36Sopenharmony_ci				goto quit_stream;
34862306a36Sopenharmony_ci			}
34962306a36Sopenharmony_ci			/* total size to fetch is byte 7, times blocksize
35062306a36Sopenharmony_ci			 * of which we already got act_len */
35162306a36Sopenharmony_ci			bytes_left = buffer[0x07] * dev->block_size - act_len;
35262306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK, "bytes_left = 0x%x\n",
35362306a36Sopenharmony_ci				  bytes_left);
35462306a36Sopenharmony_ci			/* We keep the header. It has other information, too.*/
35562306a36Sopenharmony_ci			packet_type = FIRST_PACKET;
35662306a36Sopenharmony_ci			gspca_frame_add(gspca_dev, packet_type,
35762306a36Sopenharmony_ci					buffer, act_len);
35862306a36Sopenharmony_ci			header_read = 1;
35962306a36Sopenharmony_ci		}
36062306a36Sopenharmony_ci		while (bytes_left > 0 && gspca_dev->present) {
36162306a36Sopenharmony_ci			data_len = bytes_left > JL2005C_MAX_TRANSFER ?
36262306a36Sopenharmony_ci				JL2005C_MAX_TRANSFER : bytes_left;
36362306a36Sopenharmony_ci			ret = usb_bulk_msg(gspca_dev->dev,
36462306a36Sopenharmony_ci				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
36562306a36Sopenharmony_ci				buffer, data_len, &act_len,
36662306a36Sopenharmony_ci				JL2005C_DATA_TIMEOUT);
36762306a36Sopenharmony_ci			if (ret < 0 || act_len < data_len)
36862306a36Sopenharmony_ci				goto quit_stream;
36962306a36Sopenharmony_ci			gspca_dbg(gspca_dev, D_PACK,
37062306a36Sopenharmony_ci				  "Got %d bytes out of %d for frame\n",
37162306a36Sopenharmony_ci				  data_len, bytes_left);
37262306a36Sopenharmony_ci			bytes_left -= data_len;
37362306a36Sopenharmony_ci			if (bytes_left == 0) {
37462306a36Sopenharmony_ci				packet_type = LAST_PACKET;
37562306a36Sopenharmony_ci				header_read = 0;
37662306a36Sopenharmony_ci			} else
37762306a36Sopenharmony_ci				packet_type = INTER_PACKET;
37862306a36Sopenharmony_ci			gspca_frame_add(gspca_dev, packet_type,
37962306a36Sopenharmony_ci					buffer, data_len);
38062306a36Sopenharmony_ci		}
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ciquit_stream:
38362306a36Sopenharmony_ci	if (gspca_dev->present) {
38462306a36Sopenharmony_ci		mutex_lock(&gspca_dev->usb_lock);
38562306a36Sopenharmony_ci		jl2005c_stop(gspca_dev);
38662306a36Sopenharmony_ci		mutex_unlock(&gspca_dev->usb_lock);
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	kfree(buffer);
38962306a36Sopenharmony_ci}
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci/* This function is called at probe time */
39562306a36Sopenharmony_cistatic int sd_config(struct gspca_dev *gspca_dev,
39662306a36Sopenharmony_ci			const struct usb_device_id *id)
39762306a36Sopenharmony_ci{
39862306a36Sopenharmony_ci	struct cam *cam;
39962306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci	cam = &gspca_dev->cam;
40262306a36Sopenharmony_ci	/* We don't use the buffer gspca allocates so make it small. */
40362306a36Sopenharmony_ci	cam->bulk_size = 64;
40462306a36Sopenharmony_ci	cam->bulk = 1;
40562306a36Sopenharmony_ci	/* For the rest, the camera needs to be detected */
40662306a36Sopenharmony_ci	jl2005c_get_firmware_id(gspca_dev);
40762306a36Sopenharmony_ci	/* Here are some known firmware IDs
40862306a36Sopenharmony_ci	 * First some JL2005B cameras
40962306a36Sopenharmony_ci	 * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2}	Sakar KidzCam
41062306a36Sopenharmony_ci	 * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2}	No-name JL2005B
41162306a36Sopenharmony_ci	 * JL2005C cameras
41262306a36Sopenharmony_ci	 * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8}	Argus DC-1512
41362306a36Sopenharmony_ci	 * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8}	ICarly
41462306a36Sopenharmony_ci	 * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4}	Jazz
41562306a36Sopenharmony_ci	 *
41662306a36Sopenharmony_ci	 * Based upon this scanty evidence, we can detect a CIF camera by
41762306a36Sopenharmony_ci	 * testing byte 0 for 0x4x.
41862306a36Sopenharmony_ci	 */
41962306a36Sopenharmony_ci	if ((sd->firmware_id[0] & 0xf0) == 0x40) {
42062306a36Sopenharmony_ci		cam->cam_mode	= cif_mode;
42162306a36Sopenharmony_ci		cam->nmodes	= ARRAY_SIZE(cif_mode);
42262306a36Sopenharmony_ci		sd->block_size	= 0x80;
42362306a36Sopenharmony_ci	} else {
42462306a36Sopenharmony_ci		cam->cam_mode	= vga_mode;
42562306a36Sopenharmony_ci		cam->nmodes	= ARRAY_SIZE(vga_mode);
42662306a36Sopenharmony_ci		sd->block_size	= 0x200;
42762306a36Sopenharmony_ci	}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	INIT_WORK(&sd->work_struct, jl2005c_dostream);
43062306a36Sopenharmony_ci
43162306a36Sopenharmony_ci	return 0;
43262306a36Sopenharmony_ci}
43362306a36Sopenharmony_ci
43462306a36Sopenharmony_ci/* this function is called at probe and resume time */
43562306a36Sopenharmony_cistatic int sd_init(struct gspca_dev *gspca_dev)
43662306a36Sopenharmony_ci{
43762306a36Sopenharmony_ci	return 0;
43862306a36Sopenharmony_ci}
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cistatic int sd_start(struct gspca_dev *gspca_dev)
44162306a36Sopenharmony_ci{
44262306a36Sopenharmony_ci
44362306a36Sopenharmony_ci	struct sd *sd = (struct sd *) gspca_dev;
44462306a36Sopenharmony_ci	sd->cap_mode = gspca_dev->cam.cam_mode;
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci	switch (gspca_dev->pixfmt.width) {
44762306a36Sopenharmony_ci	case 640:
44862306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at vga resolution\n");
44962306a36Sopenharmony_ci		jl2005c_stream_start_vga_lg(gspca_dev);
45062306a36Sopenharmony_ci		break;
45162306a36Sopenharmony_ci	case 320:
45262306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at qvga resolution\n");
45362306a36Sopenharmony_ci		jl2005c_stream_start_vga_small(gspca_dev);
45462306a36Sopenharmony_ci		break;
45562306a36Sopenharmony_ci	case 352:
45662306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at cif resolution\n");
45762306a36Sopenharmony_ci		jl2005c_stream_start_cif_lg(gspca_dev);
45862306a36Sopenharmony_ci		break;
45962306a36Sopenharmony_ci	case 176:
46062306a36Sopenharmony_ci		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at qcif resolution\n");
46162306a36Sopenharmony_ci		jl2005c_stream_start_cif_small(gspca_dev);
46262306a36Sopenharmony_ci		break;
46362306a36Sopenharmony_ci	default:
46462306a36Sopenharmony_ci		pr_err("Unknown resolution specified\n");
46562306a36Sopenharmony_ci		return -1;
46662306a36Sopenharmony_ci	}
46762306a36Sopenharmony_ci
46862306a36Sopenharmony_ci	schedule_work(&sd->work_struct);
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	return 0;
47162306a36Sopenharmony_ci}
47262306a36Sopenharmony_ci
47362306a36Sopenharmony_ci/* called on streamoff with alt==0 and on disconnect */
47462306a36Sopenharmony_ci/* the usb_lock is held at entry - restore on exit */
47562306a36Sopenharmony_cistatic void sd_stop0(struct gspca_dev *gspca_dev)
47662306a36Sopenharmony_ci{
47762306a36Sopenharmony_ci	struct sd *dev = (struct sd *) gspca_dev;
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	/* wait for the work queue to terminate */
48062306a36Sopenharmony_ci	mutex_unlock(&gspca_dev->usb_lock);
48162306a36Sopenharmony_ci	/* This waits for sq905c_dostream to finish */
48262306a36Sopenharmony_ci	flush_work(&dev->work_struct);
48362306a36Sopenharmony_ci	mutex_lock(&gspca_dev->usb_lock);
48462306a36Sopenharmony_ci}
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_ci
48862306a36Sopenharmony_ci/* sub-driver description */
48962306a36Sopenharmony_cistatic const struct sd_desc sd_desc = {
49062306a36Sopenharmony_ci	.name = MODULE_NAME,
49162306a36Sopenharmony_ci	.config = sd_config,
49262306a36Sopenharmony_ci	.init = sd_init,
49362306a36Sopenharmony_ci	.start = sd_start,
49462306a36Sopenharmony_ci	.stop0 = sd_stop0,
49562306a36Sopenharmony_ci};
49662306a36Sopenharmony_ci
49762306a36Sopenharmony_ci/* -- module initialisation -- */
49862306a36Sopenharmony_cistatic const struct usb_device_id device_table[] = {
49962306a36Sopenharmony_ci	{USB_DEVICE(0x0979, 0x0227)},
50062306a36Sopenharmony_ci	{}
50162306a36Sopenharmony_ci};
50262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(usb, device_table);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci/* -- device connect -- */
50562306a36Sopenharmony_cistatic int sd_probe(struct usb_interface *intf,
50662306a36Sopenharmony_ci				const struct usb_device_id *id)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
50962306a36Sopenharmony_ci				THIS_MODULE);
51062306a36Sopenharmony_ci}
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_cistatic struct usb_driver sd_driver = {
51362306a36Sopenharmony_ci	.name = MODULE_NAME,
51462306a36Sopenharmony_ci	.id_table = device_table,
51562306a36Sopenharmony_ci	.probe = sd_probe,
51662306a36Sopenharmony_ci	.disconnect = gspca_disconnect,
51762306a36Sopenharmony_ci#ifdef CONFIG_PM
51862306a36Sopenharmony_ci	.suspend = gspca_suspend,
51962306a36Sopenharmony_ci	.resume = gspca_resume,
52062306a36Sopenharmony_ci	.reset_resume = gspca_resume,
52162306a36Sopenharmony_ci#endif
52262306a36Sopenharmony_ci};
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_cimodule_usb_driver(sd_driver);
525