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