1fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab  * Jeilin JL2005B/C/D library
40c0d06caSMauro Carvalho Chehab  *
50c0d06caSMauro Carvalho Chehab  * Copyright (C) 2011 Theodore Kilgore <kilgota@auburn.edu>
60c0d06caSMauro Carvalho Chehab  */
70c0d06caSMauro Carvalho Chehab 
80c0d06caSMauro Carvalho Chehab #define MODULE_NAME "jl2005bcd"
90c0d06caSMauro Carvalho Chehab 
100c0d06caSMauro Carvalho Chehab #include <linux/workqueue.h>
110c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
120c0d06caSMauro Carvalho Chehab #include "gspca.h"
130c0d06caSMauro Carvalho Chehab 
140c0d06caSMauro Carvalho Chehab 
150c0d06caSMauro Carvalho Chehab MODULE_AUTHOR("Theodore Kilgore <kilgota@auburn.edu>");
160c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("JL2005B/C/D USB Camera Driver");
170c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
180c0d06caSMauro Carvalho Chehab 
190c0d06caSMauro Carvalho Chehab /* Default timeouts, in ms */
200c0d06caSMauro Carvalho Chehab #define JL2005C_CMD_TIMEOUT 500
210c0d06caSMauro Carvalho Chehab #define JL2005C_DATA_TIMEOUT 1000
220c0d06caSMauro Carvalho Chehab 
230c0d06caSMauro Carvalho Chehab /* Maximum transfer size to use. */
240c0d06caSMauro Carvalho Chehab #define JL2005C_MAX_TRANSFER 0x200
250c0d06caSMauro Carvalho Chehab #define FRAME_HEADER_LEN 16
260c0d06caSMauro Carvalho Chehab 
270c0d06caSMauro Carvalho Chehab 
280c0d06caSMauro Carvalho Chehab /* specific webcam descriptor */
290c0d06caSMauro Carvalho Chehab struct sd {
300c0d06caSMauro Carvalho Chehab 	struct gspca_dev gspca_dev;  /* !! must be the first item */
310c0d06caSMauro Carvalho Chehab 	unsigned char firmware_id[6];
320c0d06caSMauro Carvalho Chehab 	const struct v4l2_pix_format *cap_mode;
330c0d06caSMauro Carvalho Chehab 	/* Driver stuff */
340c0d06caSMauro Carvalho Chehab 	struct work_struct work_struct;
350c0d06caSMauro Carvalho Chehab 	u8 frame_brightness;
360c0d06caSMauro Carvalho Chehab 	int block_size;	/* block size of camera */
370c0d06caSMauro Carvalho Chehab 	int vga;	/* 1 if vga cam, 0 if cif cam */
380c0d06caSMauro Carvalho Chehab };
390c0d06caSMauro Carvalho Chehab 
400c0d06caSMauro Carvalho Chehab 
410c0d06caSMauro Carvalho Chehab /* Camera has two resolution settings. What they are depends on model. */
420c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format cif_mode[] = {
430c0d06caSMauro Carvalho Chehab 	{176, 144, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
440c0d06caSMauro Carvalho Chehab 		.bytesperline = 176,
450c0d06caSMauro Carvalho Chehab 		.sizeimage = 176 * 144,
460c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
470c0d06caSMauro Carvalho Chehab 		.priv = 0},
480c0d06caSMauro Carvalho Chehab 	{352, 288, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
490c0d06caSMauro Carvalho Chehab 		.bytesperline = 352,
500c0d06caSMauro Carvalho Chehab 		.sizeimage = 352 * 288,
510c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
520c0d06caSMauro Carvalho Chehab 		.priv = 0},
530c0d06caSMauro Carvalho Chehab };
540c0d06caSMauro Carvalho Chehab 
550c0d06caSMauro Carvalho Chehab static const struct v4l2_pix_format vga_mode[] = {
560c0d06caSMauro Carvalho Chehab 	{320, 240, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
570c0d06caSMauro Carvalho Chehab 		.bytesperline = 320,
580c0d06caSMauro Carvalho Chehab 		.sizeimage = 320 * 240,
590c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
600c0d06caSMauro Carvalho Chehab 		.priv = 0},
610c0d06caSMauro Carvalho Chehab 	{640, 480, V4L2_PIX_FMT_JL2005BCD, V4L2_FIELD_NONE,
620c0d06caSMauro Carvalho Chehab 		.bytesperline = 640,
630c0d06caSMauro Carvalho Chehab 		.sizeimage = 640 * 480,
640c0d06caSMauro Carvalho Chehab 		.colorspace = V4L2_COLORSPACE_SRGB,
650c0d06caSMauro Carvalho Chehab 		.priv = 0},
660c0d06caSMauro Carvalho Chehab };
670c0d06caSMauro Carvalho Chehab 
680c0d06caSMauro Carvalho Chehab /*
690c0d06caSMauro Carvalho Chehab  * cam uses endpoint 0x03 to send commands, 0x84 for read commands,
700c0d06caSMauro Carvalho Chehab  * and 0x82 for bulk data transfer.
710c0d06caSMauro Carvalho Chehab  */
720c0d06caSMauro Carvalho Chehab 
730c0d06caSMauro Carvalho Chehab /* All commands are two bytes only */
jl2005c_write2(struct gspca_dev * gspca_dev,unsigned char * command)740c0d06caSMauro Carvalho Chehab static int jl2005c_write2(struct gspca_dev *gspca_dev, unsigned char *command)
750c0d06caSMauro Carvalho Chehab {
760c0d06caSMauro Carvalho Chehab 	int retval;
770c0d06caSMauro Carvalho Chehab 
780c0d06caSMauro Carvalho Chehab 	memcpy(gspca_dev->usb_buf, command, 2);
790c0d06caSMauro Carvalho Chehab 	retval = usb_bulk_msg(gspca_dev->dev,
800c0d06caSMauro Carvalho Chehab 			usb_sndbulkpipe(gspca_dev->dev, 3),
810c0d06caSMauro Carvalho Chehab 			gspca_dev->usb_buf, 2, NULL, 500);
820c0d06caSMauro Carvalho Chehab 	if (retval < 0)
830c0d06caSMauro Carvalho Chehab 		pr_err("command write [%02x] error %d\n",
840c0d06caSMauro Carvalho Chehab 		       gspca_dev->usb_buf[0], retval);
850c0d06caSMauro Carvalho Chehab 	return retval;
860c0d06caSMauro Carvalho Chehab }
870c0d06caSMauro Carvalho Chehab 
880c0d06caSMauro Carvalho Chehab /* Response to a command is one byte in usb_buf[0], only if requested. */
jl2005c_read1(struct gspca_dev * gspca_dev)890c0d06caSMauro Carvalho Chehab static int jl2005c_read1(struct gspca_dev *gspca_dev)
900c0d06caSMauro Carvalho Chehab {
910c0d06caSMauro Carvalho Chehab 	int retval;
920c0d06caSMauro Carvalho Chehab 
930c0d06caSMauro Carvalho Chehab 	retval = usb_bulk_msg(gspca_dev->dev,
940c0d06caSMauro Carvalho Chehab 				usb_rcvbulkpipe(gspca_dev->dev, 0x84),
950c0d06caSMauro Carvalho Chehab 				gspca_dev->usb_buf, 1, NULL, 500);
960c0d06caSMauro Carvalho Chehab 	if (retval < 0)
970c0d06caSMauro Carvalho Chehab 		pr_err("read command [0x%02x] error %d\n",
980c0d06caSMauro Carvalho Chehab 		       gspca_dev->usb_buf[0], retval);
990c0d06caSMauro Carvalho Chehab 	return retval;
1000c0d06caSMauro Carvalho Chehab }
1010c0d06caSMauro Carvalho Chehab 
1020c0d06caSMauro Carvalho Chehab /* Response appears in gspca_dev->usb_buf[0] */
jl2005c_read_reg(struct gspca_dev * gspca_dev,unsigned char reg)1030c0d06caSMauro Carvalho Chehab static int jl2005c_read_reg(struct gspca_dev *gspca_dev, unsigned char reg)
1040c0d06caSMauro Carvalho Chehab {
1050c0d06caSMauro Carvalho Chehab 	int retval;
1060c0d06caSMauro Carvalho Chehab 
1070c0d06caSMauro Carvalho Chehab 	static u8 instruction[2] = {0x95, 0x00};
1080c0d06caSMauro Carvalho Chehab 	/* put register to read in byte 1 */
1090c0d06caSMauro Carvalho Chehab 	instruction[1] = reg;
1100c0d06caSMauro Carvalho Chehab 	/* Send the read request */
1110c0d06caSMauro Carvalho Chehab 	retval = jl2005c_write2(gspca_dev, instruction);
1120c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1130c0d06caSMauro Carvalho Chehab 		return retval;
1140c0d06caSMauro Carvalho Chehab 	retval = jl2005c_read1(gspca_dev);
1150c0d06caSMauro Carvalho Chehab 
1160c0d06caSMauro Carvalho Chehab 	return retval;
1170c0d06caSMauro Carvalho Chehab }
1180c0d06caSMauro Carvalho Chehab 
jl2005c_start_new_frame(struct gspca_dev * gspca_dev)1190c0d06caSMauro Carvalho Chehab static int jl2005c_start_new_frame(struct gspca_dev *gspca_dev)
1200c0d06caSMauro Carvalho Chehab {
1210c0d06caSMauro Carvalho Chehab 	int i;
1220c0d06caSMauro Carvalho Chehab 	int retval;
1230c0d06caSMauro Carvalho Chehab 	int frame_brightness = 0;
1240c0d06caSMauro Carvalho Chehab 
1250c0d06caSMauro Carvalho Chehab 	static u8 instruction[2] = {0x7f, 0x01};
1260c0d06caSMauro Carvalho Chehab 
1270c0d06caSMauro Carvalho Chehab 	retval = jl2005c_write2(gspca_dev, instruction);
1280c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1290c0d06caSMauro Carvalho Chehab 		return retval;
1300c0d06caSMauro Carvalho Chehab 
1310c0d06caSMauro Carvalho Chehab 	i = 0;
1320c0d06caSMauro Carvalho Chehab 	while (i < 20 && !frame_brightness) {
1330c0d06caSMauro Carvalho Chehab 		/* If we tried 20 times, give up. */
1340c0d06caSMauro Carvalho Chehab 		retval = jl2005c_read_reg(gspca_dev, 0x7e);
1350c0d06caSMauro Carvalho Chehab 		if (retval < 0)
1360c0d06caSMauro Carvalho Chehab 			return retval;
1370c0d06caSMauro Carvalho Chehab 		frame_brightness = gspca_dev->usb_buf[0];
1380c0d06caSMauro Carvalho Chehab 		retval = jl2005c_read_reg(gspca_dev, 0x7d);
1390c0d06caSMauro Carvalho Chehab 		if (retval < 0)
1400c0d06caSMauro Carvalho Chehab 			return retval;
1410c0d06caSMauro Carvalho Chehab 		i++;
1420c0d06caSMauro Carvalho Chehab 	}
14337d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_FRAM, "frame_brightness is 0x%02x\n",
14437d5efb0SJoe Perches 		  gspca_dev->usb_buf[0]);
1450c0d06caSMauro Carvalho Chehab 	return retval;
1460c0d06caSMauro Carvalho Chehab }
1470c0d06caSMauro Carvalho Chehab 
jl2005c_write_reg(struct gspca_dev * gspca_dev,unsigned char reg,unsigned char value)1480c0d06caSMauro Carvalho Chehab static int jl2005c_write_reg(struct gspca_dev *gspca_dev, unsigned char reg,
1490c0d06caSMauro Carvalho Chehab 						    unsigned char value)
1500c0d06caSMauro Carvalho Chehab {
1510c0d06caSMauro Carvalho Chehab 	int retval;
1520c0d06caSMauro Carvalho Chehab 	u8 instruction[2];
1530c0d06caSMauro Carvalho Chehab 
1540c0d06caSMauro Carvalho Chehab 	instruction[0] = reg;
1550c0d06caSMauro Carvalho Chehab 	instruction[1] = value;
1560c0d06caSMauro Carvalho Chehab 
1570c0d06caSMauro Carvalho Chehab 	retval = jl2005c_write2(gspca_dev, instruction);
1580c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1590c0d06caSMauro Carvalho Chehab 			return retval;
1600c0d06caSMauro Carvalho Chehab 
1610c0d06caSMauro Carvalho Chehab 	return retval;
1620c0d06caSMauro Carvalho Chehab }
1630c0d06caSMauro Carvalho Chehab 
jl2005c_get_firmware_id(struct gspca_dev * gspca_dev)1640c0d06caSMauro Carvalho Chehab static int jl2005c_get_firmware_id(struct gspca_dev *gspca_dev)
1650c0d06caSMauro Carvalho Chehab {
1660c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *)gspca_dev;
1670c0d06caSMauro Carvalho Chehab 	int i = 0;
168e1f60f42SColin Ian King 	int retval;
169*19bc4f40SColin Ian King 	static const unsigned char regs_to_read[] = {
170*19bc4f40SColin Ian King 		0x57, 0x02, 0x03, 0x5d, 0x5e, 0x5f
171*19bc4f40SColin Ian King 	};
1720c0d06caSMauro Carvalho Chehab 
17337d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_PROBE, "Running jl2005c_get_firmware_id\n");
1740c0d06caSMauro Carvalho Chehab 	/* Read the first ID byte once for warmup */
1750c0d06caSMauro Carvalho Chehab 	retval = jl2005c_read_reg(gspca_dev, regs_to_read[0]);
17637d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_PROBE, "response is %02x\n",
17737d5efb0SJoe Perches 		  gspca_dev->usb_buf[0]);
1780c0d06caSMauro Carvalho Chehab 	if (retval < 0)
1790c0d06caSMauro Carvalho Chehab 		return retval;
1800c0d06caSMauro Carvalho Chehab 	/* Now actually get the ID string */
1810c0d06caSMauro Carvalho Chehab 	for (i = 0; i < 6; i++) {
1820c0d06caSMauro Carvalho Chehab 		retval = jl2005c_read_reg(gspca_dev, regs_to_read[i]);
1830c0d06caSMauro Carvalho Chehab 		if (retval < 0)
1840c0d06caSMauro Carvalho Chehab 			return retval;
1850c0d06caSMauro Carvalho Chehab 		sd->firmware_id[i] = gspca_dev->usb_buf[0];
1860c0d06caSMauro Carvalho Chehab 	}
18737d5efb0SJoe Perches 	gspca_dbg(gspca_dev, D_PROBE, "firmware ID is %02x%02x%02x%02x%02x%02x\n",
1880c0d06caSMauro Carvalho Chehab 		  sd->firmware_id[0],
1890c0d06caSMauro Carvalho Chehab 		  sd->firmware_id[1],
1900c0d06caSMauro Carvalho Chehab 		  sd->firmware_id[2],
1910c0d06caSMauro Carvalho Chehab 		  sd->firmware_id[3],
1920c0d06caSMauro Carvalho Chehab 		  sd->firmware_id[4],
1930c0d06caSMauro Carvalho Chehab 		  sd->firmware_id[5]);
1940c0d06caSMauro Carvalho Chehab 	return 0;
1950c0d06caSMauro Carvalho Chehab }
1960c0d06caSMauro Carvalho Chehab 
jl2005c_stream_start_vga_lg(struct gspca_dev * gspca_dev)1970c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_vga_lg
1980c0d06caSMauro Carvalho Chehab 		    (struct gspca_dev *gspca_dev)
1990c0d06caSMauro Carvalho Chehab {
2000c0d06caSMauro Carvalho Chehab 	int i;
2010c0d06caSMauro Carvalho Chehab 	int retval = -1;
2020c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2030c0d06caSMauro Carvalho Chehab 		{0x05, 0x00},
2040c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2050c0d06caSMauro Carvalho Chehab 		{0x7d, 0x18},
2060c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2070c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2080c0d06caSMauro Carvalho Chehab 		{0x04, 0x52},
2090c0d06caSMauro Carvalho Chehab 	};
2100c0d06caSMauro Carvalho Chehab 
2110c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2120c0d06caSMauro Carvalho Chehab 		msleep(60);
2130c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2140c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2150c0d06caSMauro Carvalho Chehab 			return retval;
2160c0d06caSMauro Carvalho Chehab 	}
2170c0d06caSMauro Carvalho Chehab 	msleep(60);
2180c0d06caSMauro Carvalho Chehab 	return retval;
2190c0d06caSMauro Carvalho Chehab }
2200c0d06caSMauro Carvalho Chehab 
jl2005c_stream_start_vga_small(struct gspca_dev * gspca_dev)2210c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_vga_small(struct gspca_dev *gspca_dev)
2220c0d06caSMauro Carvalho Chehab {
2230c0d06caSMauro Carvalho Chehab 	int i;
2240c0d06caSMauro Carvalho Chehab 	int retval = -1;
2250c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2260c0d06caSMauro Carvalho Chehab 		{0x06, 0x00},
2270c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2280c0d06caSMauro Carvalho Chehab 		{0x7d, 0x1a},
2290c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2300c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2310c0d06caSMauro Carvalho Chehab 		{0x04, 0x52},
2320c0d06caSMauro Carvalho Chehab 	};
2330c0d06caSMauro Carvalho Chehab 
2340c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2350c0d06caSMauro Carvalho Chehab 		msleep(60);
2360c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2370c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2380c0d06caSMauro Carvalho Chehab 			return retval;
2390c0d06caSMauro Carvalho Chehab 	}
2400c0d06caSMauro Carvalho Chehab 	msleep(60);
2410c0d06caSMauro Carvalho Chehab 	return retval;
2420c0d06caSMauro Carvalho Chehab }
2430c0d06caSMauro Carvalho Chehab 
jl2005c_stream_start_cif_lg(struct gspca_dev * gspca_dev)2440c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_cif_lg(struct gspca_dev *gspca_dev)
2450c0d06caSMauro Carvalho Chehab {
2460c0d06caSMauro Carvalho Chehab 	int i;
2470c0d06caSMauro Carvalho Chehab 	int retval = -1;
2480c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2490c0d06caSMauro Carvalho Chehab 		{0x05, 0x00},
2500c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2510c0d06caSMauro Carvalho Chehab 		{0x7d, 0x30},
2520c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2530c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2540c0d06caSMauro Carvalho Chehab 		{0x04, 0x42},
2550c0d06caSMauro Carvalho Chehab 	};
2560c0d06caSMauro Carvalho Chehab 
2570c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2580c0d06caSMauro Carvalho Chehab 		msleep(60);
2590c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2600c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2610c0d06caSMauro Carvalho Chehab 			return retval;
2620c0d06caSMauro Carvalho Chehab 	}
2630c0d06caSMauro Carvalho Chehab 	msleep(60);
2640c0d06caSMauro Carvalho Chehab 	return retval;
2650c0d06caSMauro Carvalho Chehab }
2660c0d06caSMauro Carvalho Chehab 
jl2005c_stream_start_cif_small(struct gspca_dev * gspca_dev)2670c0d06caSMauro Carvalho Chehab static int jl2005c_stream_start_cif_small(struct gspca_dev *gspca_dev)
2680c0d06caSMauro Carvalho Chehab {
2690c0d06caSMauro Carvalho Chehab 	int i;
2700c0d06caSMauro Carvalho Chehab 	int retval = -1;
2710c0d06caSMauro Carvalho Chehab 	static u8 instruction[][2] = {
2720c0d06caSMauro Carvalho Chehab 		{0x06, 0x00},
2730c0d06caSMauro Carvalho Chehab 		{0x7c, 0x00},
2740c0d06caSMauro Carvalho Chehab 		{0x7d, 0x32},
2750c0d06caSMauro Carvalho Chehab 		{0x02, 0x00},
2760c0d06caSMauro Carvalho Chehab 		{0x01, 0x00},
2770c0d06caSMauro Carvalho Chehab 		{0x04, 0x42},
2780c0d06caSMauro Carvalho Chehab 	};
2790c0d06caSMauro Carvalho Chehab 
2800c0d06caSMauro Carvalho Chehab 	for (i = 0; i < ARRAY_SIZE(instruction); i++) {
2810c0d06caSMauro Carvalho Chehab 		msleep(60);
2820c0d06caSMauro Carvalho Chehab 		retval = jl2005c_write2(gspca_dev, instruction[i]);
2830c0d06caSMauro Carvalho Chehab 		if (retval < 0)
2840c0d06caSMauro Carvalho Chehab 			return retval;
2850c0d06caSMauro Carvalho Chehab 	}
2860c0d06caSMauro Carvalho Chehab 	msleep(60);
2870c0d06caSMauro Carvalho Chehab 	return retval;
2880c0d06caSMauro Carvalho Chehab }
2890c0d06caSMauro Carvalho Chehab 
2900c0d06caSMauro Carvalho Chehab 
jl2005c_stop(struct gspca_dev * gspca_dev)2910c0d06caSMauro Carvalho Chehab static int jl2005c_stop(struct gspca_dev *gspca_dev)
2920c0d06caSMauro Carvalho Chehab {
29329a8d979SMasahiro Yamada 	return jl2005c_write_reg(gspca_dev, 0x07, 0x00);
2940c0d06caSMauro Carvalho Chehab }
2950c0d06caSMauro Carvalho Chehab 
296844db450SHans de Goede /*
297844db450SHans de Goede  * This function is called as a workqueue function and runs whenever the camera
2980c0d06caSMauro Carvalho Chehab  * is streaming data. Because it is a workqueue function it is allowed to sleep
2990c0d06caSMauro Carvalho Chehab  * so we can use synchronous USB calls. To avoid possible collisions with other
300844db450SHans de Goede  * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
301844db450SHans de Goede  * performing USB operations using it. In practice we don't really need this
302844db450SHans de Goede  * as the camera doesn't provide any controls.
3030c0d06caSMauro Carvalho Chehab  */
jl2005c_dostream(struct work_struct * work)3040c0d06caSMauro Carvalho Chehab static void jl2005c_dostream(struct work_struct *work)
3050c0d06caSMauro Carvalho Chehab {
3060c0d06caSMauro Carvalho Chehab 	struct sd *dev = container_of(work, struct sd, work_struct);
3070c0d06caSMauro Carvalho Chehab 	struct gspca_dev *gspca_dev = &dev->gspca_dev;
3080c0d06caSMauro Carvalho Chehab 	int bytes_left = 0; /* bytes remaining in current frame. */
3090c0d06caSMauro Carvalho Chehab 	int data_len;   /* size to use for the next read. */
3100c0d06caSMauro Carvalho Chehab 	int header_read = 0;
3110c0d06caSMauro Carvalho Chehab 	unsigned char header_sig[2] = {0x4a, 0x4c};
3120c0d06caSMauro Carvalho Chehab 	int act_len;
3130c0d06caSMauro Carvalho Chehab 	int packet_type;
3140c0d06caSMauro Carvalho Chehab 	int ret;
3150c0d06caSMauro Carvalho Chehab 	u8 *buffer;
3160c0d06caSMauro Carvalho Chehab 
3175334b342SHans de Goede 	buffer = kmalloc(JL2005C_MAX_TRANSFER, GFP_KERNEL);
3180c0d06caSMauro Carvalho Chehab 	if (!buffer) {
3190c0d06caSMauro Carvalho Chehab 		pr_err("Couldn't allocate USB buffer\n");
3200c0d06caSMauro Carvalho Chehab 		goto quit_stream;
3210c0d06caSMauro Carvalho Chehab 	}
3220c0d06caSMauro Carvalho Chehab 
323345321dcSHans de Goede 	while (gspca_dev->present && gspca_dev->streaming) {
3240c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
3250c0d06caSMauro Carvalho Chehab 		if (gspca_dev->frozen)
3260c0d06caSMauro Carvalho Chehab 			break;
3270c0d06caSMauro Carvalho Chehab #endif
3280c0d06caSMauro Carvalho Chehab 		/* Check if this is a new frame. If so, start the frame first */
3290c0d06caSMauro Carvalho Chehab 		if (!header_read) {
3300c0d06caSMauro Carvalho Chehab 			mutex_lock(&gspca_dev->usb_lock);
3310c0d06caSMauro Carvalho Chehab 			ret = jl2005c_start_new_frame(gspca_dev);
3320c0d06caSMauro Carvalho Chehab 			mutex_unlock(&gspca_dev->usb_lock);
3330c0d06caSMauro Carvalho Chehab 			if (ret < 0)
3340c0d06caSMauro Carvalho Chehab 				goto quit_stream;
3350c0d06caSMauro Carvalho Chehab 			ret = usb_bulk_msg(gspca_dev->dev,
3360c0d06caSMauro Carvalho Chehab 				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
3370c0d06caSMauro Carvalho Chehab 				buffer, JL2005C_MAX_TRANSFER, &act_len,
3380c0d06caSMauro Carvalho Chehab 				JL2005C_DATA_TIMEOUT);
33937d5efb0SJoe Perches 			gspca_dbg(gspca_dev, D_PACK,
34037d5efb0SJoe Perches 				  "Got %d bytes out of %d for header\n",
3410c0d06caSMauro Carvalho Chehab 				  act_len, JL2005C_MAX_TRANSFER);
3420c0d06caSMauro Carvalho Chehab 			if (ret < 0 || act_len < JL2005C_MAX_TRANSFER)
3430c0d06caSMauro Carvalho Chehab 				goto quit_stream;
3440c0d06caSMauro Carvalho Chehab 			/* Check whether we actually got the first blodk */
3450c0d06caSMauro Carvalho Chehab 			if (memcmp(header_sig, buffer, 2) != 0) {
3460c0d06caSMauro Carvalho Chehab 				pr_err("First block is not the first block\n");
3470c0d06caSMauro Carvalho Chehab 				goto quit_stream;
3480c0d06caSMauro Carvalho Chehab 			}
3490c0d06caSMauro Carvalho Chehab 			/* total size to fetch is byte 7, times blocksize
3500c0d06caSMauro Carvalho Chehab 			 * of which we already got act_len */
3510c0d06caSMauro Carvalho Chehab 			bytes_left = buffer[0x07] * dev->block_size - act_len;
35237d5efb0SJoe Perches 			gspca_dbg(gspca_dev, D_PACK, "bytes_left = 0x%x\n",
35337d5efb0SJoe Perches 				  bytes_left);
3540c0d06caSMauro Carvalho Chehab 			/* We keep the header. It has other information, too.*/
3550c0d06caSMauro Carvalho Chehab 			packet_type = FIRST_PACKET;
3560c0d06caSMauro Carvalho Chehab 			gspca_frame_add(gspca_dev, packet_type,
3570c0d06caSMauro Carvalho Chehab 					buffer, act_len);
3580c0d06caSMauro Carvalho Chehab 			header_read = 1;
3590c0d06caSMauro Carvalho Chehab 		}
360345321dcSHans de Goede 		while (bytes_left > 0 && gspca_dev->present) {
3610c0d06caSMauro Carvalho Chehab 			data_len = bytes_left > JL2005C_MAX_TRANSFER ?
3620c0d06caSMauro Carvalho Chehab 				JL2005C_MAX_TRANSFER : bytes_left;
3630c0d06caSMauro Carvalho Chehab 			ret = usb_bulk_msg(gspca_dev->dev,
3640c0d06caSMauro Carvalho Chehab 				usb_rcvbulkpipe(gspca_dev->dev, 0x82),
3650c0d06caSMauro Carvalho Chehab 				buffer, data_len, &act_len,
3660c0d06caSMauro Carvalho Chehab 				JL2005C_DATA_TIMEOUT);
3670c0d06caSMauro Carvalho Chehab 			if (ret < 0 || act_len < data_len)
3680c0d06caSMauro Carvalho Chehab 				goto quit_stream;
36937d5efb0SJoe Perches 			gspca_dbg(gspca_dev, D_PACK,
37037d5efb0SJoe Perches 				  "Got %d bytes out of %d for frame\n",
3710c0d06caSMauro Carvalho Chehab 				  data_len, bytes_left);
3720c0d06caSMauro Carvalho Chehab 			bytes_left -= data_len;
3730c0d06caSMauro Carvalho Chehab 			if (bytes_left == 0) {
3740c0d06caSMauro Carvalho Chehab 				packet_type = LAST_PACKET;
3750c0d06caSMauro Carvalho Chehab 				header_read = 0;
3760c0d06caSMauro Carvalho Chehab 			} else
3770c0d06caSMauro Carvalho Chehab 				packet_type = INTER_PACKET;
3780c0d06caSMauro Carvalho Chehab 			gspca_frame_add(gspca_dev, packet_type,
3790c0d06caSMauro Carvalho Chehab 					buffer, data_len);
3800c0d06caSMauro Carvalho Chehab 		}
3810c0d06caSMauro Carvalho Chehab 	}
3820c0d06caSMauro Carvalho Chehab quit_stream:
383345321dcSHans de Goede 	if (gspca_dev->present) {
3840c0d06caSMauro Carvalho Chehab 		mutex_lock(&gspca_dev->usb_lock);
3850c0d06caSMauro Carvalho Chehab 		jl2005c_stop(gspca_dev);
3860c0d06caSMauro Carvalho Chehab 		mutex_unlock(&gspca_dev->usb_lock);
3870c0d06caSMauro Carvalho Chehab 	}
3880c0d06caSMauro Carvalho Chehab 	kfree(buffer);
3890c0d06caSMauro Carvalho Chehab }
3900c0d06caSMauro Carvalho Chehab 
3910c0d06caSMauro Carvalho Chehab 
3920c0d06caSMauro Carvalho Chehab 
3930c0d06caSMauro Carvalho Chehab 
3940c0d06caSMauro Carvalho Chehab /* This function is called at probe time */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)3950c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev,
3960c0d06caSMauro Carvalho Chehab 			const struct usb_device_id *id)
3970c0d06caSMauro Carvalho Chehab {
3980c0d06caSMauro Carvalho Chehab 	struct cam *cam;
3990c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4000c0d06caSMauro Carvalho Chehab 
4010c0d06caSMauro Carvalho Chehab 	cam = &gspca_dev->cam;
4020c0d06caSMauro Carvalho Chehab 	/* We don't use the buffer gspca allocates so make it small. */
4030c0d06caSMauro Carvalho Chehab 	cam->bulk_size = 64;
4040c0d06caSMauro Carvalho Chehab 	cam->bulk = 1;
4050c0d06caSMauro Carvalho Chehab 	/* For the rest, the camera needs to be detected */
4060c0d06caSMauro Carvalho Chehab 	jl2005c_get_firmware_id(gspca_dev);
4070c0d06caSMauro Carvalho Chehab 	/* Here are some known firmware IDs
4080c0d06caSMauro Carvalho Chehab 	 * First some JL2005B cameras
4090c0d06caSMauro Carvalho Chehab 	 * {0x41, 0x07, 0x04, 0x2c, 0xe8, 0xf2}	Sakar KidzCam
4100c0d06caSMauro Carvalho Chehab 	 * {0x45, 0x02, 0x08, 0xb9, 0x00, 0xd2}	No-name JL2005B
4110c0d06caSMauro Carvalho Chehab 	 * JL2005C cameras
4120c0d06caSMauro Carvalho Chehab 	 * {0x01, 0x0c, 0x16, 0x10, 0xf8, 0xc8}	Argus DC-1512
4130c0d06caSMauro Carvalho Chehab 	 * {0x12, 0x04, 0x03, 0xc0, 0x00, 0xd8}	ICarly
4140c0d06caSMauro Carvalho Chehab 	 * {0x86, 0x08, 0x05, 0x02, 0x00, 0xd4}	Jazz
4150c0d06caSMauro Carvalho Chehab 	 *
4160c0d06caSMauro Carvalho Chehab 	 * Based upon this scanty evidence, we can detect a CIF camera by
4170c0d06caSMauro Carvalho Chehab 	 * testing byte 0 for 0x4x.
4180c0d06caSMauro Carvalho Chehab 	 */
4190c0d06caSMauro Carvalho Chehab 	if ((sd->firmware_id[0] & 0xf0) == 0x40) {
4200c0d06caSMauro Carvalho Chehab 		cam->cam_mode	= cif_mode;
4210c0d06caSMauro Carvalho Chehab 		cam->nmodes	= ARRAY_SIZE(cif_mode);
4220c0d06caSMauro Carvalho Chehab 		sd->block_size	= 0x80;
4230c0d06caSMauro Carvalho Chehab 	} else {
4240c0d06caSMauro Carvalho Chehab 		cam->cam_mode	= vga_mode;
4250c0d06caSMauro Carvalho Chehab 		cam->nmodes	= ARRAY_SIZE(vga_mode);
4260c0d06caSMauro Carvalho Chehab 		sd->block_size	= 0x200;
4270c0d06caSMauro Carvalho Chehab 	}
4280c0d06caSMauro Carvalho Chehab 
4290c0d06caSMauro Carvalho Chehab 	INIT_WORK(&sd->work_struct, jl2005c_dostream);
4300c0d06caSMauro Carvalho Chehab 
4310c0d06caSMauro Carvalho Chehab 	return 0;
4320c0d06caSMauro Carvalho Chehab }
4330c0d06caSMauro Carvalho Chehab 
4340c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */
sd_init(struct gspca_dev * gspca_dev)4350c0d06caSMauro Carvalho Chehab static int sd_init(struct gspca_dev *gspca_dev)
4360c0d06caSMauro Carvalho Chehab {
4370c0d06caSMauro Carvalho Chehab 	return 0;
4380c0d06caSMauro Carvalho Chehab }
4390c0d06caSMauro Carvalho Chehab 
sd_start(struct gspca_dev * gspca_dev)4400c0d06caSMauro Carvalho Chehab static int sd_start(struct gspca_dev *gspca_dev)
4410c0d06caSMauro Carvalho Chehab {
4420c0d06caSMauro Carvalho Chehab 
4430c0d06caSMauro Carvalho Chehab 	struct sd *sd = (struct sd *) gspca_dev;
4440c0d06caSMauro Carvalho Chehab 	sd->cap_mode = gspca_dev->cam.cam_mode;
4450c0d06caSMauro Carvalho Chehab 
4461966bc2aSOndrej Zary 	switch (gspca_dev->pixfmt.width) {
4470c0d06caSMauro Carvalho Chehab 	case 640:
44837d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at vga resolution\n");
4490c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_vga_lg(gspca_dev);
4500c0d06caSMauro Carvalho Chehab 		break;
4510c0d06caSMauro Carvalho Chehab 	case 320:
45237d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at qvga resolution\n");
4530c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_vga_small(gspca_dev);
4540c0d06caSMauro Carvalho Chehab 		break;
4550c0d06caSMauro Carvalho Chehab 	case 352:
45637d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at cif resolution\n");
4570c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_cif_lg(gspca_dev);
4580c0d06caSMauro Carvalho Chehab 		break;
4590c0d06caSMauro Carvalho Chehab 	case 176:
46037d5efb0SJoe Perches 		gspca_dbg(gspca_dev, D_STREAM, "Start streaming at qcif resolution\n");
4610c0d06caSMauro Carvalho Chehab 		jl2005c_stream_start_cif_small(gspca_dev);
4620c0d06caSMauro Carvalho Chehab 		break;
4630c0d06caSMauro Carvalho Chehab 	default:
4640c0d06caSMauro Carvalho Chehab 		pr_err("Unknown resolution specified\n");
4650c0d06caSMauro Carvalho Chehab 		return -1;
4660c0d06caSMauro Carvalho Chehab 	}
4670c0d06caSMauro Carvalho Chehab 
468e5964689SBhaktipriya Shridhar 	schedule_work(&sd->work_struct);
4690c0d06caSMauro Carvalho Chehab 
4700c0d06caSMauro Carvalho Chehab 	return 0;
4710c0d06caSMauro Carvalho Chehab }
4720c0d06caSMauro Carvalho Chehab 
4730c0d06caSMauro Carvalho Chehab /* called on streamoff with alt==0 and on disconnect */
4740c0d06caSMauro Carvalho Chehab /* the usb_lock is held at entry - restore on exit */
sd_stop0(struct gspca_dev * gspca_dev)4750c0d06caSMauro Carvalho Chehab static void sd_stop0(struct gspca_dev *gspca_dev)
4760c0d06caSMauro Carvalho Chehab {
4770c0d06caSMauro Carvalho Chehab 	struct sd *dev = (struct sd *) gspca_dev;
4780c0d06caSMauro Carvalho Chehab 
4790c0d06caSMauro Carvalho Chehab 	/* wait for the work queue to terminate */
4800c0d06caSMauro Carvalho Chehab 	mutex_unlock(&gspca_dev->usb_lock);
4810c0d06caSMauro Carvalho Chehab 	/* This waits for sq905c_dostream to finish */
482e5964689SBhaktipriya Shridhar 	flush_work(&dev->work_struct);
4830c0d06caSMauro Carvalho Chehab 	mutex_lock(&gspca_dev->usb_lock);
4840c0d06caSMauro Carvalho Chehab }
4850c0d06caSMauro Carvalho Chehab 
4860c0d06caSMauro Carvalho Chehab 
4870c0d06caSMauro Carvalho Chehab 
4880c0d06caSMauro Carvalho Chehab /* sub-driver description */
4890c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc = {
4900c0d06caSMauro Carvalho Chehab 	.name = MODULE_NAME,
4910c0d06caSMauro Carvalho Chehab 	.config = sd_config,
4920c0d06caSMauro Carvalho Chehab 	.init = sd_init,
4930c0d06caSMauro Carvalho Chehab 	.start = sd_start,
4940c0d06caSMauro Carvalho Chehab 	.stop0 = sd_stop0,
4950c0d06caSMauro Carvalho Chehab };
4960c0d06caSMauro Carvalho Chehab 
4970c0d06caSMauro Carvalho Chehab /* -- module initialisation -- */
49879e8c7beSMauro Carvalho Chehab static const struct usb_device_id device_table[] = {
4990c0d06caSMauro Carvalho Chehab 	{USB_DEVICE(0x0979, 0x0227)},
5000c0d06caSMauro Carvalho Chehab 	{}
5010c0d06caSMauro Carvalho Chehab };
5020c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table);
5030c0d06caSMauro Carvalho Chehab 
5040c0d06caSMauro Carvalho Chehab /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)5050c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf,
5060c0d06caSMauro Carvalho Chehab 				const struct usb_device_id *id)
5070c0d06caSMauro Carvalho Chehab {
5080c0d06caSMauro Carvalho Chehab 	return gspca_dev_probe(intf, id, &sd_desc, sizeof(struct sd),
5090c0d06caSMauro Carvalho Chehab 				THIS_MODULE);
5100c0d06caSMauro Carvalho Chehab }
5110c0d06caSMauro Carvalho Chehab 
5120c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = {
5130c0d06caSMauro Carvalho Chehab 	.name = MODULE_NAME,
5140c0d06caSMauro Carvalho Chehab 	.id_table = device_table,
5150c0d06caSMauro Carvalho Chehab 	.probe = sd_probe,
5160c0d06caSMauro Carvalho Chehab 	.disconnect = gspca_disconnect,
5170c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
5180c0d06caSMauro Carvalho Chehab 	.suspend = gspca_suspend,
5190c0d06caSMauro Carvalho Chehab 	.resume = gspca_resume,
5200c0d06caSMauro Carvalho Chehab 	.reset_resume = gspca_resume,
5210c0d06caSMauro Carvalho Chehab #endif
5220c0d06caSMauro Carvalho Chehab };
5230c0d06caSMauro Carvalho Chehab 
524e52ec680SSachin Kamat module_usb_driver(sd_driver);
525