1fd9871f7SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
20c0d06caSMauro Carvalho Chehab /*
30c0d06caSMauro Carvalho Chehab * SQ905 subdriver
40c0d06caSMauro Carvalho Chehab *
50c0d06caSMauro Carvalho Chehab * Copyright (C) 2008, 2009 Adam Baker and Theodore Kilgore
60c0d06caSMauro Carvalho Chehab */
70c0d06caSMauro Carvalho Chehab
80c0d06caSMauro Carvalho Chehab /*
90c0d06caSMauro Carvalho Chehab * History and Acknowledgments
100c0d06caSMauro Carvalho Chehab *
110c0d06caSMauro Carvalho Chehab * The original Linux driver for SQ905 based cameras was written by
123e4d8f48SMauro Carvalho Chehab * Marcell Lengyel and further developed by many other contributors
130c0d06caSMauro Carvalho Chehab * and is available from http://sourceforge.net/projects/sqcam/
140c0d06caSMauro Carvalho Chehab *
150c0d06caSMauro Carvalho Chehab * This driver takes advantage of the reverse engineering work done for
160c0d06caSMauro Carvalho Chehab * that driver and for libgphoto2 but shares no code with them.
170c0d06caSMauro Carvalho Chehab *
180c0d06caSMauro Carvalho Chehab * This driver has used as a base the finepix driver and other gspca
190c0d06caSMauro Carvalho Chehab * based drivers and may still contain code fragments taken from those
200c0d06caSMauro Carvalho Chehab * drivers.
210c0d06caSMauro Carvalho Chehab */
220c0d06caSMauro Carvalho Chehab
230c0d06caSMauro Carvalho Chehab #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
240c0d06caSMauro Carvalho Chehab
250c0d06caSMauro Carvalho Chehab #define MODULE_NAME "sq905"
260c0d06caSMauro Carvalho Chehab
270c0d06caSMauro Carvalho Chehab #include <linux/workqueue.h>
280c0d06caSMauro Carvalho Chehab #include <linux/slab.h>
290c0d06caSMauro Carvalho Chehab #include "gspca.h"
300c0d06caSMauro Carvalho Chehab
311ddc9f75SMauro Carvalho Chehab MODULE_AUTHOR("Adam Baker <linux@baker-net.org.uk>, Theodore Kilgore <kilgota@auburn.edu>");
320c0d06caSMauro Carvalho Chehab MODULE_DESCRIPTION("GSPCA/SQ905 USB Camera Driver");
330c0d06caSMauro Carvalho Chehab MODULE_LICENSE("GPL");
340c0d06caSMauro Carvalho Chehab
350c0d06caSMauro Carvalho Chehab /* Default timeouts, in ms */
360c0d06caSMauro Carvalho Chehab #define SQ905_CMD_TIMEOUT 500
370c0d06caSMauro Carvalho Chehab #define SQ905_DATA_TIMEOUT 1000
380c0d06caSMauro Carvalho Chehab
390c0d06caSMauro Carvalho Chehab /* Maximum transfer size to use. */
400c0d06caSMauro Carvalho Chehab #define SQ905_MAX_TRANSFER 0x8000
410c0d06caSMauro Carvalho Chehab #define FRAME_HEADER_LEN 64
420c0d06caSMauro Carvalho Chehab
430c0d06caSMauro Carvalho Chehab /* The known modes, or registers. These go in the "value" slot. */
440c0d06caSMauro Carvalho Chehab
450c0d06caSMauro Carvalho Chehab /* 00 is "none" obviously */
460c0d06caSMauro Carvalho Chehab
470c0d06caSMauro Carvalho Chehab #define SQ905_BULK_READ 0x03 /* precedes any bulk read */
480c0d06caSMauro Carvalho Chehab #define SQ905_COMMAND 0x06 /* precedes the command codes below */
490c0d06caSMauro Carvalho Chehab #define SQ905_PING 0x07 /* when reading an "idling" command */
500c0d06caSMauro Carvalho Chehab #define SQ905_READ_DONE 0xc0 /* ack bulk read completed */
510c0d06caSMauro Carvalho Chehab
520c0d06caSMauro Carvalho Chehab /* Any non-zero value in the bottom 2 bits of the 2nd byte of
530c0d06caSMauro Carvalho Chehab * the ID appears to indicate the camera can do 640*480. If the
540c0d06caSMauro Carvalho Chehab * LSB of that byte is set the image is just upside down, otherwise
550c0d06caSMauro Carvalho Chehab * it is rotated 180 degrees. */
560c0d06caSMauro Carvalho Chehab #define SQ905_HIRES_MASK 0x00000300
570c0d06caSMauro Carvalho Chehab #define SQ905_ORIENTATION_MASK 0x00000100
580c0d06caSMauro Carvalho Chehab
590c0d06caSMauro Carvalho Chehab /* Some command codes. These go in the "index" slot. */
600c0d06caSMauro Carvalho Chehab
610c0d06caSMauro Carvalho Chehab #define SQ905_ID 0xf0 /* asks for model string */
620c0d06caSMauro Carvalho Chehab #define SQ905_CONFIG 0x20 /* gets photo alloc. table, not used here */
630c0d06caSMauro Carvalho Chehab #define SQ905_DATA 0x30 /* accesses photo data, not used here */
640c0d06caSMauro Carvalho Chehab #define SQ905_CLEAR 0xa0 /* clear everything */
650c0d06caSMauro Carvalho Chehab #define SQ905_CAPTURE_LOW 0x60 /* Starts capture at 160x120 */
660c0d06caSMauro Carvalho Chehab #define SQ905_CAPTURE_MED 0x61 /* Starts capture at 320x240 */
670c0d06caSMauro Carvalho Chehab #define SQ905_CAPTURE_HIGH 0x62 /* Starts capture at 640x480 (some cams only) */
680c0d06caSMauro Carvalho Chehab /* note that the capture command also controls the output dimensions */
690c0d06caSMauro Carvalho Chehab
700c0d06caSMauro Carvalho Chehab /* Structure to hold all of our device specific stuff */
710c0d06caSMauro Carvalho Chehab struct sd {
720c0d06caSMauro Carvalho Chehab struct gspca_dev gspca_dev; /* !! must be the first item */
730c0d06caSMauro Carvalho Chehab
740c0d06caSMauro Carvalho Chehab /*
750c0d06caSMauro Carvalho Chehab * Driver stuff
760c0d06caSMauro Carvalho Chehab */
770c0d06caSMauro Carvalho Chehab struct work_struct work_struct;
780c0d06caSMauro Carvalho Chehab struct workqueue_struct *work_thread;
790c0d06caSMauro Carvalho Chehab };
800c0d06caSMauro Carvalho Chehab
810c0d06caSMauro Carvalho Chehab static struct v4l2_pix_format sq905_mode[] = {
820c0d06caSMauro Carvalho Chehab { 160, 120, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
830c0d06caSMauro Carvalho Chehab .bytesperline = 160,
840c0d06caSMauro Carvalho Chehab .sizeimage = 160 * 120,
850c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB,
860c0d06caSMauro Carvalho Chehab .priv = 0},
870c0d06caSMauro Carvalho Chehab { 320, 240, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
880c0d06caSMauro Carvalho Chehab .bytesperline = 320,
890c0d06caSMauro Carvalho Chehab .sizeimage = 320 * 240,
900c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB,
910c0d06caSMauro Carvalho Chehab .priv = 0},
920c0d06caSMauro Carvalho Chehab { 640, 480, V4L2_PIX_FMT_SBGGR8, V4L2_FIELD_NONE,
930c0d06caSMauro Carvalho Chehab .bytesperline = 640,
940c0d06caSMauro Carvalho Chehab .sizeimage = 640 * 480,
950c0d06caSMauro Carvalho Chehab .colorspace = V4L2_COLORSPACE_SRGB,
960c0d06caSMauro Carvalho Chehab .priv = 0}
970c0d06caSMauro Carvalho Chehab };
980c0d06caSMauro Carvalho Chehab
990c0d06caSMauro Carvalho Chehab /*
1000c0d06caSMauro Carvalho Chehab * Send a command to the camera.
1010c0d06caSMauro Carvalho Chehab */
sq905_command(struct gspca_dev * gspca_dev,u16 index)1020c0d06caSMauro Carvalho Chehab static int sq905_command(struct gspca_dev *gspca_dev, u16 index)
1030c0d06caSMauro Carvalho Chehab {
1040c0d06caSMauro Carvalho Chehab int ret;
1050c0d06caSMauro Carvalho Chehab
1060c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = '\0';
1070c0d06caSMauro Carvalho Chehab ret = usb_control_msg(gspca_dev->dev,
1080c0d06caSMauro Carvalho Chehab usb_sndctrlpipe(gspca_dev->dev, 0),
1090c0d06caSMauro Carvalho Chehab USB_REQ_SYNCH_FRAME, /* request */
1100c0d06caSMauro Carvalho Chehab USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1110c0d06caSMauro Carvalho Chehab SQ905_COMMAND, index, gspca_dev->usb_buf, 1,
1120c0d06caSMauro Carvalho Chehab SQ905_CMD_TIMEOUT);
1130c0d06caSMauro Carvalho Chehab if (ret < 0) {
1140c0d06caSMauro Carvalho Chehab pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
1150c0d06caSMauro Carvalho Chehab return ret;
1160c0d06caSMauro Carvalho Chehab }
1170c0d06caSMauro Carvalho Chehab
1180c0d06caSMauro Carvalho Chehab ret = usb_control_msg(gspca_dev->dev,
119*53ae298fSJohan Hovold usb_rcvctrlpipe(gspca_dev->dev, 0),
1200c0d06caSMauro Carvalho Chehab USB_REQ_SYNCH_FRAME, /* request */
1210c0d06caSMauro Carvalho Chehab USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1220c0d06caSMauro Carvalho Chehab SQ905_PING, 0, gspca_dev->usb_buf, 1,
1230c0d06caSMauro Carvalho Chehab SQ905_CMD_TIMEOUT);
1240c0d06caSMauro Carvalho Chehab if (ret < 0) {
1250c0d06caSMauro Carvalho Chehab pr_err("%s: usb_control_msg failed 2 (%d)\n", __func__, ret);
1260c0d06caSMauro Carvalho Chehab return ret;
1270c0d06caSMauro Carvalho Chehab }
1280c0d06caSMauro Carvalho Chehab
1290c0d06caSMauro Carvalho Chehab return 0;
1300c0d06caSMauro Carvalho Chehab }
1310c0d06caSMauro Carvalho Chehab
1320c0d06caSMauro Carvalho Chehab /*
1330c0d06caSMauro Carvalho Chehab * Acknowledge the end of a frame - see warning on sq905_command.
1340c0d06caSMauro Carvalho Chehab */
sq905_ack_frame(struct gspca_dev * gspca_dev)1350c0d06caSMauro Carvalho Chehab static int sq905_ack_frame(struct gspca_dev *gspca_dev)
1360c0d06caSMauro Carvalho Chehab {
1370c0d06caSMauro Carvalho Chehab int ret;
1380c0d06caSMauro Carvalho Chehab
1390c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = '\0';
1400c0d06caSMauro Carvalho Chehab ret = usb_control_msg(gspca_dev->dev,
1410c0d06caSMauro Carvalho Chehab usb_sndctrlpipe(gspca_dev->dev, 0),
1420c0d06caSMauro Carvalho Chehab USB_REQ_SYNCH_FRAME, /* request */
1430c0d06caSMauro Carvalho Chehab USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1440c0d06caSMauro Carvalho Chehab SQ905_READ_DONE, 0, gspca_dev->usb_buf, 1,
1450c0d06caSMauro Carvalho Chehab SQ905_CMD_TIMEOUT);
1460c0d06caSMauro Carvalho Chehab if (ret < 0) {
1470c0d06caSMauro Carvalho Chehab pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
1480c0d06caSMauro Carvalho Chehab return ret;
1490c0d06caSMauro Carvalho Chehab }
1500c0d06caSMauro Carvalho Chehab
1510c0d06caSMauro Carvalho Chehab return 0;
1520c0d06caSMauro Carvalho Chehab }
1530c0d06caSMauro Carvalho Chehab
1540c0d06caSMauro Carvalho Chehab /*
1550c0d06caSMauro Carvalho Chehab * request and read a block of data - see warning on sq905_command.
1560c0d06caSMauro Carvalho Chehab */
1570c0d06caSMauro Carvalho Chehab static int
sq905_read_data(struct gspca_dev * gspca_dev,u8 * data,int size,int need_lock)1580c0d06caSMauro Carvalho Chehab sq905_read_data(struct gspca_dev *gspca_dev, u8 *data, int size, int need_lock)
1590c0d06caSMauro Carvalho Chehab {
1600c0d06caSMauro Carvalho Chehab int ret;
161eaaea468SHans Verkuil int act_len = 0;
1620c0d06caSMauro Carvalho Chehab
1630c0d06caSMauro Carvalho Chehab gspca_dev->usb_buf[0] = '\0';
1640c0d06caSMauro Carvalho Chehab if (need_lock)
1650c0d06caSMauro Carvalho Chehab mutex_lock(&gspca_dev->usb_lock);
1660c0d06caSMauro Carvalho Chehab ret = usb_control_msg(gspca_dev->dev,
1670c0d06caSMauro Carvalho Chehab usb_sndctrlpipe(gspca_dev->dev, 0),
1680c0d06caSMauro Carvalho Chehab USB_REQ_SYNCH_FRAME, /* request */
1690c0d06caSMauro Carvalho Chehab USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
1700c0d06caSMauro Carvalho Chehab SQ905_BULK_READ, size, gspca_dev->usb_buf,
1710c0d06caSMauro Carvalho Chehab 1, SQ905_CMD_TIMEOUT);
1720c0d06caSMauro Carvalho Chehab if (need_lock)
1730c0d06caSMauro Carvalho Chehab mutex_unlock(&gspca_dev->usb_lock);
1740c0d06caSMauro Carvalho Chehab if (ret < 0) {
1750c0d06caSMauro Carvalho Chehab pr_err("%s: usb_control_msg failed (%d)\n", __func__, ret);
1760c0d06caSMauro Carvalho Chehab return ret;
1770c0d06caSMauro Carvalho Chehab }
1780c0d06caSMauro Carvalho Chehab ret = usb_bulk_msg(gspca_dev->dev,
1790c0d06caSMauro Carvalho Chehab usb_rcvbulkpipe(gspca_dev->dev, 0x81),
1800c0d06caSMauro Carvalho Chehab data, size, &act_len, SQ905_DATA_TIMEOUT);
1810c0d06caSMauro Carvalho Chehab
1820c0d06caSMauro Carvalho Chehab /* successful, it returns 0, otherwise negative */
1830c0d06caSMauro Carvalho Chehab if (ret < 0 || act_len != size) {
1840c0d06caSMauro Carvalho Chehab pr_err("bulk read fail (%d) len %d/%d\n", ret, act_len, size);
1850c0d06caSMauro Carvalho Chehab return -EIO;
1860c0d06caSMauro Carvalho Chehab }
1870c0d06caSMauro Carvalho Chehab return 0;
1880c0d06caSMauro Carvalho Chehab }
1890c0d06caSMauro Carvalho Chehab
190844db450SHans de Goede /*
191844db450SHans de Goede * This function is called as a workqueue function and runs whenever the camera
1920c0d06caSMauro Carvalho Chehab * is streaming data. Because it is a workqueue function it is allowed to sleep
1930c0d06caSMauro Carvalho Chehab * so we can use synchronous USB calls. To avoid possible collisions with other
194844db450SHans de Goede * threads attempting to use gspca_dev->usb_buf we take the usb_lock when
195844db450SHans de Goede * performing USB operations using it. In practice we don't really need this
196844db450SHans de Goede * as the camera doesn't provide any controls.
1970c0d06caSMauro Carvalho Chehab */
sq905_dostream(struct work_struct * work)1980c0d06caSMauro Carvalho Chehab static void sq905_dostream(struct work_struct *work)
1990c0d06caSMauro Carvalho Chehab {
2000c0d06caSMauro Carvalho Chehab struct sd *dev = container_of(work, struct sd, work_struct);
2010c0d06caSMauro Carvalho Chehab struct gspca_dev *gspca_dev = &dev->gspca_dev;
2020c0d06caSMauro Carvalho Chehab int bytes_left; /* bytes remaining in current frame. */
2030c0d06caSMauro Carvalho Chehab int data_len; /* size to use for the next read. */
2040c0d06caSMauro Carvalho Chehab int header_read; /* true if we have already read the frame header. */
2050c0d06caSMauro Carvalho Chehab int packet_type;
2060c0d06caSMauro Carvalho Chehab int frame_sz;
2070c0d06caSMauro Carvalho Chehab int ret;
2080c0d06caSMauro Carvalho Chehab u8 *data;
2090c0d06caSMauro Carvalho Chehab u8 *buffer;
2100c0d06caSMauro Carvalho Chehab
2115334b342SHans de Goede buffer = kmalloc(SQ905_MAX_TRANSFER, GFP_KERNEL);
2120c0d06caSMauro Carvalho Chehab if (!buffer) {
2130c0d06caSMauro Carvalho Chehab pr_err("Couldn't allocate USB buffer\n");
2140c0d06caSMauro Carvalho Chehab goto quit_stream;
2150c0d06caSMauro Carvalho Chehab }
2160c0d06caSMauro Carvalho Chehab
2170c0d06caSMauro Carvalho Chehab frame_sz = gspca_dev->cam.cam_mode[gspca_dev->curr_mode].sizeimage
2180c0d06caSMauro Carvalho Chehab + FRAME_HEADER_LEN;
2190c0d06caSMauro Carvalho Chehab
220345321dcSHans de Goede while (gspca_dev->present && gspca_dev->streaming) {
2210c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
2220c0d06caSMauro Carvalho Chehab if (gspca_dev->frozen)
2230c0d06caSMauro Carvalho Chehab break;
2240c0d06caSMauro Carvalho Chehab #endif
2250c0d06caSMauro Carvalho Chehab /* request some data and then read it until we have
2260c0d06caSMauro Carvalho Chehab * a complete frame. */
2270c0d06caSMauro Carvalho Chehab bytes_left = frame_sz;
2280c0d06caSMauro Carvalho Chehab header_read = 0;
2290c0d06caSMauro Carvalho Chehab
2300c0d06caSMauro Carvalho Chehab /* Note we do not check for gspca_dev->streaming here, as
2310c0d06caSMauro Carvalho Chehab we must finish reading an entire frame, otherwise the
2320c0d06caSMauro Carvalho Chehab next time we stream we start reading in the middle of a
2330c0d06caSMauro Carvalho Chehab frame. */
234345321dcSHans de Goede while (bytes_left > 0 && gspca_dev->present) {
2350c0d06caSMauro Carvalho Chehab data_len = bytes_left > SQ905_MAX_TRANSFER ?
2360c0d06caSMauro Carvalho Chehab SQ905_MAX_TRANSFER : bytes_left;
2370c0d06caSMauro Carvalho Chehab ret = sq905_read_data(gspca_dev, buffer, data_len, 1);
2380c0d06caSMauro Carvalho Chehab if (ret < 0)
2390c0d06caSMauro Carvalho Chehab goto quit_stream;
24037d5efb0SJoe Perches gspca_dbg(gspca_dev, D_PACK,
24137d5efb0SJoe Perches "Got %d bytes out of %d for frame\n",
2420c0d06caSMauro Carvalho Chehab data_len, bytes_left);
2430c0d06caSMauro Carvalho Chehab bytes_left -= data_len;
2440c0d06caSMauro Carvalho Chehab data = buffer;
2450c0d06caSMauro Carvalho Chehab if (!header_read) {
2460c0d06caSMauro Carvalho Chehab packet_type = FIRST_PACKET;
2470c0d06caSMauro Carvalho Chehab /* The first 64 bytes of each frame are
2480c0d06caSMauro Carvalho Chehab * a header full of FF 00 bytes */
2490c0d06caSMauro Carvalho Chehab data += FRAME_HEADER_LEN;
2500c0d06caSMauro Carvalho Chehab data_len -= FRAME_HEADER_LEN;
2510c0d06caSMauro Carvalho Chehab header_read = 1;
2520c0d06caSMauro Carvalho Chehab } else if (bytes_left == 0) {
2530c0d06caSMauro Carvalho Chehab packet_type = LAST_PACKET;
2540c0d06caSMauro Carvalho Chehab } else {
2550c0d06caSMauro Carvalho Chehab packet_type = INTER_PACKET;
2560c0d06caSMauro Carvalho Chehab }
2570c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, packet_type,
2580c0d06caSMauro Carvalho Chehab data, data_len);
2590c0d06caSMauro Carvalho Chehab /* If entire frame fits in one packet we still
2600c0d06caSMauro Carvalho Chehab need to add a LAST_PACKET */
2610c0d06caSMauro Carvalho Chehab if (packet_type == FIRST_PACKET &&
2620c0d06caSMauro Carvalho Chehab bytes_left == 0)
2630c0d06caSMauro Carvalho Chehab gspca_frame_add(gspca_dev, LAST_PACKET,
2640c0d06caSMauro Carvalho Chehab NULL, 0);
2650c0d06caSMauro Carvalho Chehab }
266345321dcSHans de Goede if (gspca_dev->present) {
2670c0d06caSMauro Carvalho Chehab /* acknowledge the frame */
2680c0d06caSMauro Carvalho Chehab mutex_lock(&gspca_dev->usb_lock);
2690c0d06caSMauro Carvalho Chehab ret = sq905_ack_frame(gspca_dev);
2700c0d06caSMauro Carvalho Chehab mutex_unlock(&gspca_dev->usb_lock);
2710c0d06caSMauro Carvalho Chehab if (ret < 0)
2720c0d06caSMauro Carvalho Chehab goto quit_stream;
2730c0d06caSMauro Carvalho Chehab }
2740c0d06caSMauro Carvalho Chehab }
2750c0d06caSMauro Carvalho Chehab quit_stream:
276345321dcSHans de Goede if (gspca_dev->present) {
2770c0d06caSMauro Carvalho Chehab mutex_lock(&gspca_dev->usb_lock);
2780c0d06caSMauro Carvalho Chehab sq905_command(gspca_dev, SQ905_CLEAR);
2790c0d06caSMauro Carvalho Chehab mutex_unlock(&gspca_dev->usb_lock);
2800c0d06caSMauro Carvalho Chehab }
2810c0d06caSMauro Carvalho Chehab kfree(buffer);
2820c0d06caSMauro Carvalho Chehab }
2830c0d06caSMauro Carvalho Chehab
2840c0d06caSMauro Carvalho Chehab /* This function is called at probe time just before sd_init */
sd_config(struct gspca_dev * gspca_dev,const struct usb_device_id * id)2850c0d06caSMauro Carvalho Chehab static int sd_config(struct gspca_dev *gspca_dev,
2860c0d06caSMauro Carvalho Chehab const struct usb_device_id *id)
2870c0d06caSMauro Carvalho Chehab {
2880c0d06caSMauro Carvalho Chehab struct cam *cam = &gspca_dev->cam;
2890c0d06caSMauro Carvalho Chehab struct sd *dev = (struct sd *) gspca_dev;
2900c0d06caSMauro Carvalho Chehab
2910c0d06caSMauro Carvalho Chehab /* We don't use the buffer gspca allocates so make it small. */
2920c0d06caSMauro Carvalho Chehab cam->bulk = 1;
2930c0d06caSMauro Carvalho Chehab cam->bulk_size = 64;
2940c0d06caSMauro Carvalho Chehab
2950c0d06caSMauro Carvalho Chehab INIT_WORK(&dev->work_struct, sq905_dostream);
2960c0d06caSMauro Carvalho Chehab
2970c0d06caSMauro Carvalho Chehab return 0;
2980c0d06caSMauro Carvalho Chehab }
2990c0d06caSMauro Carvalho Chehab
3000c0d06caSMauro Carvalho Chehab /* called on streamoff with alt==0 and on disconnect */
3010c0d06caSMauro Carvalho Chehab /* the usb_lock is held at entry - restore on exit */
sd_stop0(struct gspca_dev * gspca_dev)3020c0d06caSMauro Carvalho Chehab static void sd_stop0(struct gspca_dev *gspca_dev)
3030c0d06caSMauro Carvalho Chehab {
3040c0d06caSMauro Carvalho Chehab struct sd *dev = (struct sd *) gspca_dev;
3050c0d06caSMauro Carvalho Chehab
3060c0d06caSMauro Carvalho Chehab /* wait for the work queue to terminate */
3070c0d06caSMauro Carvalho Chehab mutex_unlock(&gspca_dev->usb_lock);
3080c0d06caSMauro Carvalho Chehab /* This waits for sq905_dostream to finish */
3090c0d06caSMauro Carvalho Chehab destroy_workqueue(dev->work_thread);
3100c0d06caSMauro Carvalho Chehab dev->work_thread = NULL;
3110c0d06caSMauro Carvalho Chehab mutex_lock(&gspca_dev->usb_lock);
3120c0d06caSMauro Carvalho Chehab }
3130c0d06caSMauro Carvalho Chehab
3140c0d06caSMauro Carvalho Chehab /* this function is called at probe and resume time */
sd_init(struct gspca_dev * gspca_dev)3150c0d06caSMauro Carvalho Chehab static int sd_init(struct gspca_dev *gspca_dev)
3160c0d06caSMauro Carvalho Chehab {
3170c0d06caSMauro Carvalho Chehab u32 ident;
3180c0d06caSMauro Carvalho Chehab int ret;
3190c0d06caSMauro Carvalho Chehab
3200c0d06caSMauro Carvalho Chehab /* connect to the camera and read
3210c0d06caSMauro Carvalho Chehab * the model ID and process that and put it away.
3220c0d06caSMauro Carvalho Chehab */
3230c0d06caSMauro Carvalho Chehab ret = sq905_command(gspca_dev, SQ905_CLEAR);
3240c0d06caSMauro Carvalho Chehab if (ret < 0)
3250c0d06caSMauro Carvalho Chehab return ret;
3260c0d06caSMauro Carvalho Chehab ret = sq905_command(gspca_dev, SQ905_ID);
3270c0d06caSMauro Carvalho Chehab if (ret < 0)
3280c0d06caSMauro Carvalho Chehab return ret;
3290c0d06caSMauro Carvalho Chehab ret = sq905_read_data(gspca_dev, gspca_dev->usb_buf, 4, 0);
3300c0d06caSMauro Carvalho Chehab if (ret < 0)
3310c0d06caSMauro Carvalho Chehab return ret;
3320c0d06caSMauro Carvalho Chehab /* usb_buf is allocated with kmalloc so is aligned.
3330c0d06caSMauro Carvalho Chehab * Camera model number is the right way round if we assume this
3340c0d06caSMauro Carvalho Chehab * reverse engineered ID is supposed to be big endian. */
3350c0d06caSMauro Carvalho Chehab ident = be32_to_cpup((__be32 *)gspca_dev->usb_buf);
3360c0d06caSMauro Carvalho Chehab ret = sq905_command(gspca_dev, SQ905_CLEAR);
3370c0d06caSMauro Carvalho Chehab if (ret < 0)
3380c0d06caSMauro Carvalho Chehab return ret;
33937d5efb0SJoe Perches gspca_dbg(gspca_dev, D_CONF, "SQ905 camera ID %08x detected\n", ident);
3400c0d06caSMauro Carvalho Chehab gspca_dev->cam.cam_mode = sq905_mode;
3410c0d06caSMauro Carvalho Chehab gspca_dev->cam.nmodes = ARRAY_SIZE(sq905_mode);
3420c0d06caSMauro Carvalho Chehab if (!(ident & SQ905_HIRES_MASK))
3430c0d06caSMauro Carvalho Chehab gspca_dev->cam.nmodes--;
3440c0d06caSMauro Carvalho Chehab
3450c0d06caSMauro Carvalho Chehab if (ident & SQ905_ORIENTATION_MASK)
3460c0d06caSMauro Carvalho Chehab gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP;
3470c0d06caSMauro Carvalho Chehab else
3480c0d06caSMauro Carvalho Chehab gspca_dev->cam.input_flags = V4L2_IN_ST_VFLIP |
3490c0d06caSMauro Carvalho Chehab V4L2_IN_ST_HFLIP;
3500c0d06caSMauro Carvalho Chehab return 0;
3510c0d06caSMauro Carvalho Chehab }
3520c0d06caSMauro Carvalho Chehab
3530c0d06caSMauro Carvalho Chehab /* Set up for getting frames. */
sd_start(struct gspca_dev * gspca_dev)3540c0d06caSMauro Carvalho Chehab static int sd_start(struct gspca_dev *gspca_dev)
3550c0d06caSMauro Carvalho Chehab {
3560c0d06caSMauro Carvalho Chehab struct sd *dev = (struct sd *) gspca_dev;
3570c0d06caSMauro Carvalho Chehab int ret;
3580c0d06caSMauro Carvalho Chehab
3590c0d06caSMauro Carvalho Chehab /* "Open the shutter" and set size, to start capture */
3600c0d06caSMauro Carvalho Chehab switch (gspca_dev->curr_mode) {
3610c0d06caSMauro Carvalho Chehab default:
3620c0d06caSMauro Carvalho Chehab /* case 2: */
36337d5efb0SJoe Perches gspca_dbg(gspca_dev, D_STREAM, "Start streaming at high resolution\n");
3640c0d06caSMauro Carvalho Chehab ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_HIGH);
3650c0d06caSMauro Carvalho Chehab break;
3660c0d06caSMauro Carvalho Chehab case 1:
36737d5efb0SJoe Perches gspca_dbg(gspca_dev, D_STREAM, "Start streaming at medium resolution\n");
3680c0d06caSMauro Carvalho Chehab ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_MED);
3690c0d06caSMauro Carvalho Chehab break;
3700c0d06caSMauro Carvalho Chehab case 0:
37137d5efb0SJoe Perches gspca_dbg(gspca_dev, D_STREAM, "Start streaming at low resolution\n");
3720c0d06caSMauro Carvalho Chehab ret = sq905_command(&dev->gspca_dev, SQ905_CAPTURE_LOW);
3730c0d06caSMauro Carvalho Chehab }
3740c0d06caSMauro Carvalho Chehab
3750c0d06caSMauro Carvalho Chehab if (ret < 0) {
37652173c5fSJoe Perches gspca_err(gspca_dev, "Start streaming command failed\n");
3770c0d06caSMauro Carvalho Chehab return ret;
3780c0d06caSMauro Carvalho Chehab }
3790c0d06caSMauro Carvalho Chehab /* Start the workqueue function to do the streaming */
3800c0d06caSMauro Carvalho Chehab dev->work_thread = create_singlethread_workqueue(MODULE_NAME);
3818dbdee8eSNavid Emamdoost if (!dev->work_thread)
3828dbdee8eSNavid Emamdoost return -ENOMEM;
3838dbdee8eSNavid Emamdoost
3840c0d06caSMauro Carvalho Chehab queue_work(dev->work_thread, &dev->work_struct);
3850c0d06caSMauro Carvalho Chehab
3860c0d06caSMauro Carvalho Chehab return 0;
3870c0d06caSMauro Carvalho Chehab }
3880c0d06caSMauro Carvalho Chehab
3890c0d06caSMauro Carvalho Chehab /* Table of supported USB devices */
3900c0d06caSMauro Carvalho Chehab static const struct usb_device_id device_table[] = {
3910c0d06caSMauro Carvalho Chehab {USB_DEVICE(0x2770, 0x9120)},
3920c0d06caSMauro Carvalho Chehab {}
3930c0d06caSMauro Carvalho Chehab };
3940c0d06caSMauro Carvalho Chehab
3950c0d06caSMauro Carvalho Chehab MODULE_DEVICE_TABLE(usb, device_table);
3960c0d06caSMauro Carvalho Chehab
3970c0d06caSMauro Carvalho Chehab /* sub-driver description */
3980c0d06caSMauro Carvalho Chehab static const struct sd_desc sd_desc = {
3990c0d06caSMauro Carvalho Chehab .name = MODULE_NAME,
4000c0d06caSMauro Carvalho Chehab .config = sd_config,
4010c0d06caSMauro Carvalho Chehab .init = sd_init,
4020c0d06caSMauro Carvalho Chehab .start = sd_start,
4030c0d06caSMauro Carvalho Chehab .stop0 = sd_stop0,
4040c0d06caSMauro Carvalho Chehab };
4050c0d06caSMauro Carvalho Chehab
4060c0d06caSMauro Carvalho Chehab /* -- device connect -- */
sd_probe(struct usb_interface * intf,const struct usb_device_id * id)4070c0d06caSMauro Carvalho Chehab static int sd_probe(struct usb_interface *intf,
4080c0d06caSMauro Carvalho Chehab const struct usb_device_id *id)
4090c0d06caSMauro Carvalho Chehab {
4100c0d06caSMauro Carvalho Chehab return gspca_dev_probe(intf, id,
4110c0d06caSMauro Carvalho Chehab &sd_desc,
4120c0d06caSMauro Carvalho Chehab sizeof(struct sd),
4130c0d06caSMauro Carvalho Chehab THIS_MODULE);
4140c0d06caSMauro Carvalho Chehab }
4150c0d06caSMauro Carvalho Chehab
4160c0d06caSMauro Carvalho Chehab static struct usb_driver sd_driver = {
4170c0d06caSMauro Carvalho Chehab .name = MODULE_NAME,
4180c0d06caSMauro Carvalho Chehab .id_table = device_table,
4190c0d06caSMauro Carvalho Chehab .probe = sd_probe,
4200c0d06caSMauro Carvalho Chehab .disconnect = gspca_disconnect,
4210c0d06caSMauro Carvalho Chehab #ifdef CONFIG_PM
4220c0d06caSMauro Carvalho Chehab .suspend = gspca_suspend,
4230c0d06caSMauro Carvalho Chehab .resume = gspca_resume,
4240c0d06caSMauro Carvalho Chehab .reset_resume = gspca_resume,
4250c0d06caSMauro Carvalho Chehab #endif
4260c0d06caSMauro Carvalho Chehab };
4270c0d06caSMauro Carvalho Chehab
4280c0d06caSMauro Carvalho Chehab module_usb_driver(sd_driver);
429