18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/****************************************************************************
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *  Filename: cpia2_usb.c
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci *  Copyright 2001, STMicrolectronics, Inc.
78c2ecf20Sopenharmony_ci *      Contact:  steve.miller@st.com
88c2ecf20Sopenharmony_ci *
98c2ecf20Sopenharmony_ci *  Description:
108c2ecf20Sopenharmony_ci *     This is a USB driver for CPia2 based video cameras.
118c2ecf20Sopenharmony_ci *     The infrastructure of this driver is based on the cpia usb driver by
128c2ecf20Sopenharmony_ci *     Jochen Scharrlach and Johannes Erdfeldt.
138c2ecf20Sopenharmony_ci *
148c2ecf20Sopenharmony_ci *  Stripped of 2.4 stuff ready for main kernel submit by
158c2ecf20Sopenharmony_ci *		Alan Cox <alan@lxorguk.ukuu.org.uk>
168c2ecf20Sopenharmony_ci ****************************************************************************/
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/kernel.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/usb.h>
218c2ecf20Sopenharmony_ci#include <linux/module.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include "cpia2.h"
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic int frame_sizes[] = {
268c2ecf20Sopenharmony_ci	0,	// USBIF_CMDONLY
278c2ecf20Sopenharmony_ci	0,	// USBIF_BULK
288c2ecf20Sopenharmony_ci	128,	// USBIF_ISO_1
298c2ecf20Sopenharmony_ci	384,	// USBIF_ISO_2
308c2ecf20Sopenharmony_ci	640,	// USBIF_ISO_3
318c2ecf20Sopenharmony_ci	768,	// USBIF_ISO_4
328c2ecf20Sopenharmony_ci	896,	// USBIF_ISO_5
338c2ecf20Sopenharmony_ci	1023,	// USBIF_ISO_6
348c2ecf20Sopenharmony_ci};
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci#define FRAMES_PER_DESC    10
378c2ecf20Sopenharmony_ci#define FRAME_SIZE_PER_DESC   frame_sizes[cam->cur_alt]
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistatic void process_frame(struct camera_data *cam);
408c2ecf20Sopenharmony_cistatic void cpia2_usb_complete(struct urb *urb);
418c2ecf20Sopenharmony_cistatic int cpia2_usb_probe(struct usb_interface *intf,
428c2ecf20Sopenharmony_ci			   const struct usb_device_id *id);
438c2ecf20Sopenharmony_cistatic void cpia2_usb_disconnect(struct usb_interface *intf);
448c2ecf20Sopenharmony_cistatic int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message);
458c2ecf20Sopenharmony_cistatic int cpia2_usb_resume(struct usb_interface *intf);
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_cistatic void free_sbufs(struct camera_data *cam);
488c2ecf20Sopenharmony_cistatic void add_APPn(struct camera_data *cam);
498c2ecf20Sopenharmony_cistatic void add_COM(struct camera_data *cam);
508c2ecf20Sopenharmony_cistatic int submit_urbs(struct camera_data *cam);
518c2ecf20Sopenharmony_cistatic int set_alternate(struct camera_data *cam, unsigned int alt);
528c2ecf20Sopenharmony_cistatic int configure_transfer_mode(struct camera_data *cam, unsigned int alt);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_cistatic const struct usb_device_id cpia2_id_table[] = {
558c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0553, 0x0100)},
568c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0553, 0x0140)},
578c2ecf20Sopenharmony_ci	{USB_DEVICE(0x0553, 0x0151)},  /* STV0676 */
588c2ecf20Sopenharmony_ci	{}			/* Terminating entry */
598c2ecf20Sopenharmony_ci};
608c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(usb, cpia2_id_table);
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_cistatic struct usb_driver cpia2_driver = {
638c2ecf20Sopenharmony_ci	.name		= "cpia2",
648c2ecf20Sopenharmony_ci	.probe		= cpia2_usb_probe,
658c2ecf20Sopenharmony_ci	.disconnect	= cpia2_usb_disconnect,
668c2ecf20Sopenharmony_ci	.suspend	= cpia2_usb_suspend,
678c2ecf20Sopenharmony_ci	.resume		= cpia2_usb_resume,
688c2ecf20Sopenharmony_ci	.reset_resume	= cpia2_usb_resume,
698c2ecf20Sopenharmony_ci	.id_table	= cpia2_id_table
708c2ecf20Sopenharmony_ci};
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/******************************************************************************
748c2ecf20Sopenharmony_ci *
758c2ecf20Sopenharmony_ci *  process_frame
768c2ecf20Sopenharmony_ci *
778c2ecf20Sopenharmony_ci *****************************************************************************/
788c2ecf20Sopenharmony_cistatic void process_frame(struct camera_data *cam)
798c2ecf20Sopenharmony_ci{
808c2ecf20Sopenharmony_ci	static int frame_count;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	unsigned char *inbuff = cam->workbuff->data;
838c2ecf20Sopenharmony_ci
848c2ecf20Sopenharmony_ci	DBG("Processing frame #%d, current:%d\n",
858c2ecf20Sopenharmony_ci	    cam->workbuff->num, cam->curbuff->num);
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	if(cam->workbuff->length > cam->workbuff->max_length)
888c2ecf20Sopenharmony_ci		cam->workbuff->max_length = cam->workbuff->length;
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) {
918c2ecf20Sopenharmony_ci		frame_count++;
928c2ecf20Sopenharmony_ci	} else {
938c2ecf20Sopenharmony_ci		cam->workbuff->status = FRAME_ERROR;
948c2ecf20Sopenharmony_ci		DBG("Start of frame not found\n");
958c2ecf20Sopenharmony_ci		return;
968c2ecf20Sopenharmony_ci	}
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	/***
998c2ecf20Sopenharmony_ci	 * Now the output buffer should have a JPEG image in it.
1008c2ecf20Sopenharmony_ci	 ***/
1018c2ecf20Sopenharmony_ci	if(!cam->first_image_seen) {
1028c2ecf20Sopenharmony_ci		/* Always skip the first image after streaming
1038c2ecf20Sopenharmony_ci		 * starts. It is almost certainly corrupt. */
1048c2ecf20Sopenharmony_ci		cam->first_image_seen = 1;
1058c2ecf20Sopenharmony_ci		cam->workbuff->status = FRAME_EMPTY;
1068c2ecf20Sopenharmony_ci		return;
1078c2ecf20Sopenharmony_ci	}
1088c2ecf20Sopenharmony_ci	if (cam->workbuff->length > 3) {
1098c2ecf20Sopenharmony_ci		if(cam->mmapped &&
1108c2ecf20Sopenharmony_ci		   cam->workbuff->length < cam->workbuff->max_length) {
1118c2ecf20Sopenharmony_ci			/* No junk in the buffers */
1128c2ecf20Sopenharmony_ci			memset(cam->workbuff->data+cam->workbuff->length,
1138c2ecf20Sopenharmony_ci			       0, cam->workbuff->max_length-
1148c2ecf20Sopenharmony_ci				  cam->workbuff->length);
1158c2ecf20Sopenharmony_ci		}
1168c2ecf20Sopenharmony_ci		cam->workbuff->max_length = cam->workbuff->length;
1178c2ecf20Sopenharmony_ci		cam->workbuff->status = FRAME_READY;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci		if(!cam->mmapped && cam->num_frames > 2) {
1208c2ecf20Sopenharmony_ci			/* During normal reading, the most recent
1218c2ecf20Sopenharmony_ci			 * frame will be read.  If the current frame
1228c2ecf20Sopenharmony_ci			 * hasn't started reading yet, it will never
1238c2ecf20Sopenharmony_ci			 * be read, so mark it empty.  If the buffer is
1248c2ecf20Sopenharmony_ci			 * mmapped, or we have few buffers, we need to
1258c2ecf20Sopenharmony_ci			 * wait for the user to free the buffer.
1268c2ecf20Sopenharmony_ci			 *
1278c2ecf20Sopenharmony_ci			 * NOTE: This is not entirely foolproof with 3
1288c2ecf20Sopenharmony_ci			 * buffers, but it would take an EXTREMELY
1298c2ecf20Sopenharmony_ci			 * overloaded system to cause problems (possible
1308c2ecf20Sopenharmony_ci			 * image data corruption).  Basically, it would
1318c2ecf20Sopenharmony_ci			 * need to take more time to execute cpia2_read
1328c2ecf20Sopenharmony_ci			 * than it would for the camera to send
1338c2ecf20Sopenharmony_ci			 * cam->num_frames-2 frames before problems
1348c2ecf20Sopenharmony_ci			 * could occur.
1358c2ecf20Sopenharmony_ci			 */
1368c2ecf20Sopenharmony_ci			cam->curbuff->status = FRAME_EMPTY;
1378c2ecf20Sopenharmony_ci		}
1388c2ecf20Sopenharmony_ci		cam->curbuff = cam->workbuff;
1398c2ecf20Sopenharmony_ci		cam->workbuff = cam->workbuff->next;
1408c2ecf20Sopenharmony_ci		DBG("Changed buffers, work:%d, current:%d\n",
1418c2ecf20Sopenharmony_ci		    cam->workbuff->num, cam->curbuff->num);
1428c2ecf20Sopenharmony_ci		return;
1438c2ecf20Sopenharmony_ci	} else {
1448c2ecf20Sopenharmony_ci		DBG("Not enough data for an image.\n");
1458c2ecf20Sopenharmony_ci	}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci	cam->workbuff->status = FRAME_ERROR;
1488c2ecf20Sopenharmony_ci	return;
1498c2ecf20Sopenharmony_ci}
1508c2ecf20Sopenharmony_ci
1518c2ecf20Sopenharmony_ci/******************************************************************************
1528c2ecf20Sopenharmony_ci *
1538c2ecf20Sopenharmony_ci *  add_APPn
1548c2ecf20Sopenharmony_ci *
1558c2ecf20Sopenharmony_ci *  Adds a user specified APPn record
1568c2ecf20Sopenharmony_ci *****************************************************************************/
1578c2ecf20Sopenharmony_cistatic void add_APPn(struct camera_data *cam)
1588c2ecf20Sopenharmony_ci{
1598c2ecf20Sopenharmony_ci	if(cam->APP_len > 0) {
1608c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = 0xFF;
1618c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn;
1628c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = 0;
1638c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2;
1648c2ecf20Sopenharmony_ci		memcpy(cam->workbuff->data+cam->workbuff->length,
1658c2ecf20Sopenharmony_ci		       cam->APP_data, cam->APP_len);
1668c2ecf20Sopenharmony_ci		cam->workbuff->length += cam->APP_len;
1678c2ecf20Sopenharmony_ci	}
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci/******************************************************************************
1718c2ecf20Sopenharmony_ci *
1728c2ecf20Sopenharmony_ci *  add_COM
1738c2ecf20Sopenharmony_ci *
1748c2ecf20Sopenharmony_ci *  Adds a user specified COM record
1758c2ecf20Sopenharmony_ci *****************************************************************************/
1768c2ecf20Sopenharmony_cistatic void add_COM(struct camera_data *cam)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if(cam->COM_len > 0) {
1798c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = 0xFF;
1808c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = 0xFE;
1818c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = 0;
1828c2ecf20Sopenharmony_ci		cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2;
1838c2ecf20Sopenharmony_ci		memcpy(cam->workbuff->data+cam->workbuff->length,
1848c2ecf20Sopenharmony_ci		       cam->COM_data, cam->COM_len);
1858c2ecf20Sopenharmony_ci		cam->workbuff->length += cam->COM_len;
1868c2ecf20Sopenharmony_ci	}
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci/******************************************************************************
1908c2ecf20Sopenharmony_ci *
1918c2ecf20Sopenharmony_ci *  cpia2_usb_complete
1928c2ecf20Sopenharmony_ci *
1938c2ecf20Sopenharmony_ci *  callback when incoming packet is received
1948c2ecf20Sopenharmony_ci *****************************************************************************/
1958c2ecf20Sopenharmony_cistatic void cpia2_usb_complete(struct urb *urb)
1968c2ecf20Sopenharmony_ci{
1978c2ecf20Sopenharmony_ci	int i;
1988c2ecf20Sopenharmony_ci	unsigned char *cdata;
1998c2ecf20Sopenharmony_ci	static bool frame_ready = false;
2008c2ecf20Sopenharmony_ci	struct camera_data *cam = (struct camera_data *) urb->context;
2018c2ecf20Sopenharmony_ci
2028c2ecf20Sopenharmony_ci	if (urb->status!=0) {
2038c2ecf20Sopenharmony_ci		if (!(urb->status == -ENOENT ||
2048c2ecf20Sopenharmony_ci		      urb->status == -ECONNRESET ||
2058c2ecf20Sopenharmony_ci		      urb->status == -ESHUTDOWN))
2068c2ecf20Sopenharmony_ci		{
2078c2ecf20Sopenharmony_ci			DBG("urb->status = %d!\n", urb->status);
2088c2ecf20Sopenharmony_ci		}
2098c2ecf20Sopenharmony_ci		DBG("Stopping streaming\n");
2108c2ecf20Sopenharmony_ci		return;
2118c2ecf20Sopenharmony_ci	}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_ci	if (!cam->streaming || !video_is_registered(&cam->vdev)) {
2148c2ecf20Sopenharmony_ci		LOG("Will now stop the streaming: streaming = %d, present=%d\n",
2158c2ecf20Sopenharmony_ci		    cam->streaming, video_is_registered(&cam->vdev));
2168c2ecf20Sopenharmony_ci		return;
2178c2ecf20Sopenharmony_ci	}
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	/***
2208c2ecf20Sopenharmony_ci	 * Packet collater
2218c2ecf20Sopenharmony_ci	 ***/
2228c2ecf20Sopenharmony_ci	//DBG("Collating %d packets\n", urb->number_of_packets);
2238c2ecf20Sopenharmony_ci	for (i = 0; i < urb->number_of_packets; i++) {
2248c2ecf20Sopenharmony_ci		u16 checksum, iso_checksum;
2258c2ecf20Sopenharmony_ci		int j;
2268c2ecf20Sopenharmony_ci		int n = urb->iso_frame_desc[i].actual_length;
2278c2ecf20Sopenharmony_ci		int st = urb->iso_frame_desc[i].status;
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci		if(cam->workbuff->status == FRAME_READY) {
2308c2ecf20Sopenharmony_ci			struct framebuf *ptr;
2318c2ecf20Sopenharmony_ci			/* Try to find an available buffer */
2328c2ecf20Sopenharmony_ci			DBG("workbuff full, searching\n");
2338c2ecf20Sopenharmony_ci			for (ptr = cam->workbuff->next;
2348c2ecf20Sopenharmony_ci			     ptr != cam->workbuff;
2358c2ecf20Sopenharmony_ci			     ptr = ptr->next)
2368c2ecf20Sopenharmony_ci			{
2378c2ecf20Sopenharmony_ci				if (ptr->status == FRAME_EMPTY) {
2388c2ecf20Sopenharmony_ci					ptr->status = FRAME_READING;
2398c2ecf20Sopenharmony_ci					ptr->length = 0;
2408c2ecf20Sopenharmony_ci					break;
2418c2ecf20Sopenharmony_ci				}
2428c2ecf20Sopenharmony_ci			}
2438c2ecf20Sopenharmony_ci			if (ptr == cam->workbuff)
2448c2ecf20Sopenharmony_ci				break; /* No READING or EMPTY buffers left */
2458c2ecf20Sopenharmony_ci
2468c2ecf20Sopenharmony_ci			cam->workbuff = ptr;
2478c2ecf20Sopenharmony_ci		}
2488c2ecf20Sopenharmony_ci
2498c2ecf20Sopenharmony_ci		if (cam->workbuff->status == FRAME_EMPTY ||
2508c2ecf20Sopenharmony_ci		    cam->workbuff->status == FRAME_ERROR) {
2518c2ecf20Sopenharmony_ci			cam->workbuff->status = FRAME_READING;
2528c2ecf20Sopenharmony_ci			cam->workbuff->length = 0;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		//DBG("   Packet %d length = %d, status = %d\n", i, n, st);
2568c2ecf20Sopenharmony_ci		cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
2578c2ecf20Sopenharmony_ci
2588c2ecf20Sopenharmony_ci		if (st) {
2598c2ecf20Sopenharmony_ci			LOG("cpia2 data error: [%d] len=%d, status = %d\n",
2608c2ecf20Sopenharmony_ci			    i, n, st);
2618c2ecf20Sopenharmony_ci			if(!ALLOW_CORRUPT)
2628c2ecf20Sopenharmony_ci				cam->workbuff->status = FRAME_ERROR;
2638c2ecf20Sopenharmony_ci			continue;
2648c2ecf20Sopenharmony_ci		}
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci		if(n<=2)
2678c2ecf20Sopenharmony_ci			continue;
2688c2ecf20Sopenharmony_ci
2698c2ecf20Sopenharmony_ci		checksum = 0;
2708c2ecf20Sopenharmony_ci		for(j=0; j<n-2; ++j)
2718c2ecf20Sopenharmony_ci			checksum += cdata[j];
2728c2ecf20Sopenharmony_ci		iso_checksum = cdata[j] + cdata[j+1]*256;
2738c2ecf20Sopenharmony_ci		if(checksum != iso_checksum) {
2748c2ecf20Sopenharmony_ci			LOG("checksum mismatch: [%d] len=%d, calculated = %x, checksum = %x\n",
2758c2ecf20Sopenharmony_ci			    i, n, (int)checksum, (int)iso_checksum);
2768c2ecf20Sopenharmony_ci			if(!ALLOW_CORRUPT) {
2778c2ecf20Sopenharmony_ci				cam->workbuff->status = FRAME_ERROR;
2788c2ecf20Sopenharmony_ci				continue;
2798c2ecf20Sopenharmony_ci			}
2808c2ecf20Sopenharmony_ci		}
2818c2ecf20Sopenharmony_ci		n -= 2;
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci		if(cam->workbuff->status != FRAME_READING) {
2848c2ecf20Sopenharmony_ci			if((0xFF == cdata[0] && 0xD8 == cdata[1]) ||
2858c2ecf20Sopenharmony_ci			   (0xD8 == cdata[0] && 0xFF == cdata[1] &&
2868c2ecf20Sopenharmony_ci			    0 != cdata[2])) {
2878c2ecf20Sopenharmony_ci				/* frame is skipped, but increment total
2888c2ecf20Sopenharmony_ci				 * frame count anyway */
2898c2ecf20Sopenharmony_ci				cam->frame_count++;
2908c2ecf20Sopenharmony_ci			}
2918c2ecf20Sopenharmony_ci			DBG("workbuff not reading, status=%d\n",
2928c2ecf20Sopenharmony_ci			    cam->workbuff->status);
2938c2ecf20Sopenharmony_ci			continue;
2948c2ecf20Sopenharmony_ci		}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_ci		if (cam->frame_size < cam->workbuff->length + n) {
2978c2ecf20Sopenharmony_ci			ERR("buffer overflow! length: %d, n: %d\n",
2988c2ecf20Sopenharmony_ci			    cam->workbuff->length, n);
2998c2ecf20Sopenharmony_ci			cam->workbuff->status = FRAME_ERROR;
3008c2ecf20Sopenharmony_ci			if(cam->workbuff->length > cam->workbuff->max_length)
3018c2ecf20Sopenharmony_ci				cam->workbuff->max_length =
3028c2ecf20Sopenharmony_ci					cam->workbuff->length;
3038c2ecf20Sopenharmony_ci			continue;
3048c2ecf20Sopenharmony_ci		}
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_ci		if (cam->workbuff->length == 0) {
3078c2ecf20Sopenharmony_ci			int data_offset;
3088c2ecf20Sopenharmony_ci			if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) {
3098c2ecf20Sopenharmony_ci				data_offset = 1;
3108c2ecf20Sopenharmony_ci			} else if((0xFF == cdata[0]) && (0xD8 == cdata[1])
3118c2ecf20Sopenharmony_ci				  && (0xFF == cdata[2])) {
3128c2ecf20Sopenharmony_ci				data_offset = 2;
3138c2ecf20Sopenharmony_ci			} else {
3148c2ecf20Sopenharmony_ci				DBG("Ignoring packet, not beginning!\n");
3158c2ecf20Sopenharmony_ci				continue;
3168c2ecf20Sopenharmony_ci			}
3178c2ecf20Sopenharmony_ci			DBG("Start of frame pattern found\n");
3188c2ecf20Sopenharmony_ci			cam->workbuff->ts = ktime_get_ns();
3198c2ecf20Sopenharmony_ci			cam->workbuff->seq = cam->frame_count++;
3208c2ecf20Sopenharmony_ci			cam->workbuff->data[0] = 0xFF;
3218c2ecf20Sopenharmony_ci			cam->workbuff->data[1] = 0xD8;
3228c2ecf20Sopenharmony_ci			cam->workbuff->length = 2;
3238c2ecf20Sopenharmony_ci			add_APPn(cam);
3248c2ecf20Sopenharmony_ci			add_COM(cam);
3258c2ecf20Sopenharmony_ci			memcpy(cam->workbuff->data+cam->workbuff->length,
3268c2ecf20Sopenharmony_ci			       cdata+data_offset, n-data_offset);
3278c2ecf20Sopenharmony_ci			cam->workbuff->length += n-data_offset;
3288c2ecf20Sopenharmony_ci		} else if (cam->workbuff->length > 0) {
3298c2ecf20Sopenharmony_ci			memcpy(cam->workbuff->data + cam->workbuff->length,
3308c2ecf20Sopenharmony_ci			       cdata, n);
3318c2ecf20Sopenharmony_ci			cam->workbuff->length += n;
3328c2ecf20Sopenharmony_ci		}
3338c2ecf20Sopenharmony_ci
3348c2ecf20Sopenharmony_ci		if ((cam->workbuff->length >= 3) &&
3358c2ecf20Sopenharmony_ci		    (cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) &&
3368c2ecf20Sopenharmony_ci		    (cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) &&
3378c2ecf20Sopenharmony_ci		    (cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) {
3388c2ecf20Sopenharmony_ci			frame_ready = true;
3398c2ecf20Sopenharmony_ci			cam->workbuff->data[cam->workbuff->length - 1] = 0;
3408c2ecf20Sopenharmony_ci			cam->workbuff->length -= 1;
3418c2ecf20Sopenharmony_ci		} else if ((cam->workbuff->length >= 2) &&
3428c2ecf20Sopenharmony_ci		   (cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) &&
3438c2ecf20Sopenharmony_ci		   (cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) {
3448c2ecf20Sopenharmony_ci			frame_ready = true;
3458c2ecf20Sopenharmony_ci		}
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci		if (frame_ready) {
3488c2ecf20Sopenharmony_ci			DBG("Workbuff image size = %d\n",cam->workbuff->length);
3498c2ecf20Sopenharmony_ci			process_frame(cam);
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci			frame_ready = false;
3528c2ecf20Sopenharmony_ci
3538c2ecf20Sopenharmony_ci			if (waitqueue_active(&cam->wq_stream))
3548c2ecf20Sopenharmony_ci				wake_up_interruptible(&cam->wq_stream);
3558c2ecf20Sopenharmony_ci		}
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	if(cam->streaming) {
3598c2ecf20Sopenharmony_ci		/* resubmit */
3608c2ecf20Sopenharmony_ci		urb->dev = cam->dev;
3618c2ecf20Sopenharmony_ci		if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
3628c2ecf20Sopenharmony_ci			ERR("%s: usb_submit_urb ret %d!\n", __func__, i);
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci}
3658c2ecf20Sopenharmony_ci
3668c2ecf20Sopenharmony_ci/******************************************************************************
3678c2ecf20Sopenharmony_ci *
3688c2ecf20Sopenharmony_ci * configure_transfer_mode
3698c2ecf20Sopenharmony_ci *
3708c2ecf20Sopenharmony_ci *****************************************************************************/
3718c2ecf20Sopenharmony_cistatic int configure_transfer_mode(struct camera_data *cam, unsigned int alt)
3728c2ecf20Sopenharmony_ci{
3738c2ecf20Sopenharmony_ci	static unsigned char iso_regs[8][4] = {
3748c2ecf20Sopenharmony_ci		{0x00, 0x00, 0x00, 0x00},
3758c2ecf20Sopenharmony_ci		{0x00, 0x00, 0x00, 0x00},
3768c2ecf20Sopenharmony_ci		{0xB9, 0x00, 0x00, 0x7E},
3778c2ecf20Sopenharmony_ci		{0xB9, 0x00, 0x01, 0x7E},
3788c2ecf20Sopenharmony_ci		{0xB9, 0x00, 0x02, 0x7E},
3798c2ecf20Sopenharmony_ci		{0xB9, 0x00, 0x02, 0xFE},
3808c2ecf20Sopenharmony_ci		{0xB9, 0x00, 0x03, 0x7E},
3818c2ecf20Sopenharmony_ci		{0xB9, 0x00, 0x03, 0xFD}
3828c2ecf20Sopenharmony_ci	};
3838c2ecf20Sopenharmony_ci	struct cpia2_command cmd;
3848c2ecf20Sopenharmony_ci	unsigned char reg;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	if (!video_is_registered(&cam->vdev))
3878c2ecf20Sopenharmony_ci		return -ENODEV;
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	/***
3908c2ecf20Sopenharmony_ci	 * Write the isoc registers according to the alternate selected
3918c2ecf20Sopenharmony_ci	 ***/
3928c2ecf20Sopenharmony_ci	cmd.direction = TRANSFER_WRITE;
3938c2ecf20Sopenharmony_ci	cmd.buffer.block_data[0] = iso_regs[alt][0];
3948c2ecf20Sopenharmony_ci	cmd.buffer.block_data[1] = iso_regs[alt][1];
3958c2ecf20Sopenharmony_ci	cmd.buffer.block_data[2] = iso_regs[alt][2];
3968c2ecf20Sopenharmony_ci	cmd.buffer.block_data[3] = iso_regs[alt][3];
3978c2ecf20Sopenharmony_ci	cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
3988c2ecf20Sopenharmony_ci	cmd.start = CPIA2_VC_USB_ISOLIM;
3998c2ecf20Sopenharmony_ci	cmd.reg_count = 4;
4008c2ecf20Sopenharmony_ci	cpia2_send_command(cam, &cmd);
4018c2ecf20Sopenharmony_ci
4028c2ecf20Sopenharmony_ci	/***
4038c2ecf20Sopenharmony_ci	 * Enable relevant streams before starting polling.
4048c2ecf20Sopenharmony_ci	 * First read USB Stream Config Register.
4058c2ecf20Sopenharmony_ci	 ***/
4068c2ecf20Sopenharmony_ci	cmd.direction = TRANSFER_READ;
4078c2ecf20Sopenharmony_ci	cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
4088c2ecf20Sopenharmony_ci	cmd.start = CPIA2_VC_USB_STRM;
4098c2ecf20Sopenharmony_ci	cmd.reg_count = 1;
4108c2ecf20Sopenharmony_ci	cpia2_send_command(cam, &cmd);
4118c2ecf20Sopenharmony_ci	reg = cmd.buffer.block_data[0];
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_ci	/* Clear iso, bulk, and int */
4148c2ecf20Sopenharmony_ci	reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE |
4158c2ecf20Sopenharmony_ci		 CPIA2_VC_USB_STRM_ISO_ENABLE |
4168c2ecf20Sopenharmony_ci		 CPIA2_VC_USB_STRM_INT_ENABLE);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	if (alt == USBIF_BULK) {
4198c2ecf20Sopenharmony_ci		DBG("Enabling bulk xfer\n");
4208c2ecf20Sopenharmony_ci		reg |= CPIA2_VC_USB_STRM_BLK_ENABLE;	/* Enable Bulk */
4218c2ecf20Sopenharmony_ci		cam->xfer_mode = XFER_BULK;
4228c2ecf20Sopenharmony_ci	} else if (alt >= USBIF_ISO_1) {
4238c2ecf20Sopenharmony_ci		DBG("Enabling ISOC xfer\n");
4248c2ecf20Sopenharmony_ci		reg |= CPIA2_VC_USB_STRM_ISO_ENABLE;
4258c2ecf20Sopenharmony_ci		cam->xfer_mode = XFER_ISOC;
4268c2ecf20Sopenharmony_ci	}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_ci	cmd.buffer.block_data[0] = reg;
4298c2ecf20Sopenharmony_ci	cmd.direction = TRANSFER_WRITE;
4308c2ecf20Sopenharmony_ci	cmd.start = CPIA2_VC_USB_STRM;
4318c2ecf20Sopenharmony_ci	cmd.reg_count = 1;
4328c2ecf20Sopenharmony_ci	cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
4338c2ecf20Sopenharmony_ci	cpia2_send_command(cam, &cmd);
4348c2ecf20Sopenharmony_ci
4358c2ecf20Sopenharmony_ci	return 0;
4368c2ecf20Sopenharmony_ci}
4378c2ecf20Sopenharmony_ci
4388c2ecf20Sopenharmony_ci/******************************************************************************
4398c2ecf20Sopenharmony_ci *
4408c2ecf20Sopenharmony_ci * cpia2_usb_change_streaming_alternate
4418c2ecf20Sopenharmony_ci *
4428c2ecf20Sopenharmony_ci *****************************************************************************/
4438c2ecf20Sopenharmony_ciint cpia2_usb_change_streaming_alternate(struct camera_data *cam,
4448c2ecf20Sopenharmony_ci					 unsigned int alt)
4458c2ecf20Sopenharmony_ci{
4468c2ecf20Sopenharmony_ci	int ret = 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6)
4498c2ecf20Sopenharmony_ci		return -EINVAL;
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	if(alt == cam->params.camera_state.stream_mode)
4528c2ecf20Sopenharmony_ci		return 0;
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	cpia2_usb_stream_pause(cam);
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	configure_transfer_mode(cam, alt);
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci	cam->params.camera_state.stream_mode = alt;
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_ci	/* Reset the camera to prevent image quality degradation */
4618c2ecf20Sopenharmony_ci	cpia2_reset_camera(cam);
4628c2ecf20Sopenharmony_ci
4638c2ecf20Sopenharmony_ci	cpia2_usb_stream_resume(cam);
4648c2ecf20Sopenharmony_ci
4658c2ecf20Sopenharmony_ci	return ret;
4668c2ecf20Sopenharmony_ci}
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci/******************************************************************************
4698c2ecf20Sopenharmony_ci *
4708c2ecf20Sopenharmony_ci * set_alternate
4718c2ecf20Sopenharmony_ci *
4728c2ecf20Sopenharmony_ci *****************************************************************************/
4738c2ecf20Sopenharmony_cistatic int set_alternate(struct camera_data *cam, unsigned int alt)
4748c2ecf20Sopenharmony_ci{
4758c2ecf20Sopenharmony_ci	int ret = 0;
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	if(alt == cam->cur_alt)
4788c2ecf20Sopenharmony_ci		return 0;
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_ci	if (cam->cur_alt != USBIF_CMDONLY) {
4818c2ecf20Sopenharmony_ci		DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY);
4828c2ecf20Sopenharmony_ci		ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY);
4838c2ecf20Sopenharmony_ci		if (ret != 0)
4848c2ecf20Sopenharmony_ci			return ret;
4858c2ecf20Sopenharmony_ci	}
4868c2ecf20Sopenharmony_ci	if (alt != USBIF_CMDONLY) {
4878c2ecf20Sopenharmony_ci		DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt);
4888c2ecf20Sopenharmony_ci		ret = usb_set_interface(cam->dev, cam->iface, alt);
4898c2ecf20Sopenharmony_ci		if (ret != 0)
4908c2ecf20Sopenharmony_ci			return ret;
4918c2ecf20Sopenharmony_ci	}
4928c2ecf20Sopenharmony_ci
4938c2ecf20Sopenharmony_ci	cam->old_alt = cam->cur_alt;
4948c2ecf20Sopenharmony_ci	cam->cur_alt = alt;
4958c2ecf20Sopenharmony_ci
4968c2ecf20Sopenharmony_ci	return ret;
4978c2ecf20Sopenharmony_ci}
4988c2ecf20Sopenharmony_ci
4998c2ecf20Sopenharmony_ci/******************************************************************************
5008c2ecf20Sopenharmony_ci *
5018c2ecf20Sopenharmony_ci * free_sbufs
5028c2ecf20Sopenharmony_ci *
5038c2ecf20Sopenharmony_ci * Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL
5048c2ecf20Sopenharmony_ci * are assumed to be allocated. Non-NULL .urb members are also assumed to be
5058c2ecf20Sopenharmony_ci * submitted (and must therefore be killed before they are freed).
5068c2ecf20Sopenharmony_ci *****************************************************************************/
5078c2ecf20Sopenharmony_cistatic void free_sbufs(struct camera_data *cam)
5088c2ecf20Sopenharmony_ci{
5098c2ecf20Sopenharmony_ci	int i;
5108c2ecf20Sopenharmony_ci
5118c2ecf20Sopenharmony_ci	for (i = 0; i < NUM_SBUF; i++) {
5128c2ecf20Sopenharmony_ci		if(cam->sbuf[i].urb) {
5138c2ecf20Sopenharmony_ci			usb_kill_urb(cam->sbuf[i].urb);
5148c2ecf20Sopenharmony_ci			usb_free_urb(cam->sbuf[i].urb);
5158c2ecf20Sopenharmony_ci			cam->sbuf[i].urb = NULL;
5168c2ecf20Sopenharmony_ci		}
5178c2ecf20Sopenharmony_ci		if(cam->sbuf[i].data) {
5188c2ecf20Sopenharmony_ci			kfree(cam->sbuf[i].data);
5198c2ecf20Sopenharmony_ci			cam->sbuf[i].data = NULL;
5208c2ecf20Sopenharmony_ci		}
5218c2ecf20Sopenharmony_ci	}
5228c2ecf20Sopenharmony_ci}
5238c2ecf20Sopenharmony_ci
5248c2ecf20Sopenharmony_ci/*******
5258c2ecf20Sopenharmony_ci* Convenience functions
5268c2ecf20Sopenharmony_ci*******/
5278c2ecf20Sopenharmony_ci/****************************************************************************
5288c2ecf20Sopenharmony_ci *
5298c2ecf20Sopenharmony_ci *  write_packet
5308c2ecf20Sopenharmony_ci *
5318c2ecf20Sopenharmony_ci ***************************************************************************/
5328c2ecf20Sopenharmony_cistatic int write_packet(struct usb_device *udev,
5338c2ecf20Sopenharmony_ci			u8 request, u8 * registers, u16 start, size_t size)
5348c2ecf20Sopenharmony_ci{
5358c2ecf20Sopenharmony_ci	unsigned char *buf;
5368c2ecf20Sopenharmony_ci	int ret;
5378c2ecf20Sopenharmony_ci
5388c2ecf20Sopenharmony_ci	if (!registers || size <= 0)
5398c2ecf20Sopenharmony_ci		return -EINVAL;
5408c2ecf20Sopenharmony_ci
5418c2ecf20Sopenharmony_ci	buf = kmemdup(registers, size, GFP_KERNEL);
5428c2ecf20Sopenharmony_ci	if (!buf)
5438c2ecf20Sopenharmony_ci		return -ENOMEM;
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_ci	ret = usb_control_msg(udev,
5468c2ecf20Sopenharmony_ci			       usb_sndctrlpipe(udev, 0),
5478c2ecf20Sopenharmony_ci			       request,
5488c2ecf20Sopenharmony_ci			       USB_TYPE_VENDOR | USB_RECIP_DEVICE,
5498c2ecf20Sopenharmony_ci			       start,	/* value */
5508c2ecf20Sopenharmony_ci			       0,	/* index */
5518c2ecf20Sopenharmony_ci			       buf,	/* buffer */
5528c2ecf20Sopenharmony_ci			       size,
5538c2ecf20Sopenharmony_ci			       1000);
5548c2ecf20Sopenharmony_ci
5558c2ecf20Sopenharmony_ci	kfree(buf);
5568c2ecf20Sopenharmony_ci	return ret;
5578c2ecf20Sopenharmony_ci}
5588c2ecf20Sopenharmony_ci
5598c2ecf20Sopenharmony_ci/****************************************************************************
5608c2ecf20Sopenharmony_ci *
5618c2ecf20Sopenharmony_ci *  read_packet
5628c2ecf20Sopenharmony_ci *
5638c2ecf20Sopenharmony_ci ***************************************************************************/
5648c2ecf20Sopenharmony_cistatic int read_packet(struct usb_device *udev,
5658c2ecf20Sopenharmony_ci		       u8 request, u8 * registers, u16 start, size_t size)
5668c2ecf20Sopenharmony_ci{
5678c2ecf20Sopenharmony_ci	unsigned char *buf;
5688c2ecf20Sopenharmony_ci	int ret;
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	if (!registers || size <= 0)
5718c2ecf20Sopenharmony_ci		return -EINVAL;
5728c2ecf20Sopenharmony_ci
5738c2ecf20Sopenharmony_ci	buf = kmalloc(size, GFP_KERNEL);
5748c2ecf20Sopenharmony_ci	if (!buf)
5758c2ecf20Sopenharmony_ci		return -ENOMEM;
5768c2ecf20Sopenharmony_ci
5778c2ecf20Sopenharmony_ci	ret = usb_control_msg(udev,
5788c2ecf20Sopenharmony_ci			       usb_rcvctrlpipe(udev, 0),
5798c2ecf20Sopenharmony_ci			       request,
5808c2ecf20Sopenharmony_ci			       USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE,
5818c2ecf20Sopenharmony_ci			       start,	/* value */
5828c2ecf20Sopenharmony_ci			       0,	/* index */
5838c2ecf20Sopenharmony_ci			       buf,	/* buffer */
5848c2ecf20Sopenharmony_ci			       size,
5858c2ecf20Sopenharmony_ci			       1000);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	if (ret >= 0)
5888c2ecf20Sopenharmony_ci		memcpy(registers, buf, size);
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	kfree(buf);
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	return ret;
5938c2ecf20Sopenharmony_ci}
5948c2ecf20Sopenharmony_ci
5958c2ecf20Sopenharmony_ci/******************************************************************************
5968c2ecf20Sopenharmony_ci *
5978c2ecf20Sopenharmony_ci *  cpia2_usb_transfer_cmd
5988c2ecf20Sopenharmony_ci *
5998c2ecf20Sopenharmony_ci *****************************************************************************/
6008c2ecf20Sopenharmony_ciint cpia2_usb_transfer_cmd(struct camera_data *cam,
6018c2ecf20Sopenharmony_ci			   void *registers,
6028c2ecf20Sopenharmony_ci			   u8 request, u8 start, u8 count, u8 direction)
6038c2ecf20Sopenharmony_ci{
6048c2ecf20Sopenharmony_ci	int err = 0;
6058c2ecf20Sopenharmony_ci	struct usb_device *udev = cam->dev;
6068c2ecf20Sopenharmony_ci
6078c2ecf20Sopenharmony_ci	if (!udev) {
6088c2ecf20Sopenharmony_ci		ERR("%s: Internal driver error: udev is NULL\n", __func__);
6098c2ecf20Sopenharmony_ci		return -EINVAL;
6108c2ecf20Sopenharmony_ci	}
6118c2ecf20Sopenharmony_ci
6128c2ecf20Sopenharmony_ci	if (!registers) {
6138c2ecf20Sopenharmony_ci		ERR("%s: Internal driver error: register array is NULL\n", __func__);
6148c2ecf20Sopenharmony_ci		return -EINVAL;
6158c2ecf20Sopenharmony_ci	}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_ci	if (direction == TRANSFER_READ) {
6188c2ecf20Sopenharmony_ci		err = read_packet(udev, request, (u8 *)registers, start, count);
6198c2ecf20Sopenharmony_ci		if (err > 0)
6208c2ecf20Sopenharmony_ci			err = 0;
6218c2ecf20Sopenharmony_ci	} else if (direction == TRANSFER_WRITE) {
6228c2ecf20Sopenharmony_ci		err =write_packet(udev, request, (u8 *)registers, start, count);
6238c2ecf20Sopenharmony_ci		if (err < 0) {
6248c2ecf20Sopenharmony_ci			LOG("Control message failed, err val = %d\n", err);
6258c2ecf20Sopenharmony_ci			LOG("Message: request = 0x%0X, start = 0x%0X\n",
6268c2ecf20Sopenharmony_ci			    request, start);
6278c2ecf20Sopenharmony_ci			LOG("Message: count = %d, register[0] = 0x%0X\n",
6288c2ecf20Sopenharmony_ci			    count, ((unsigned char *) registers)[0]);
6298c2ecf20Sopenharmony_ci		} else
6308c2ecf20Sopenharmony_ci			err=0;
6318c2ecf20Sopenharmony_ci	} else {
6328c2ecf20Sopenharmony_ci		LOG("Unexpected first byte of direction: %d\n",
6338c2ecf20Sopenharmony_ci		       direction);
6348c2ecf20Sopenharmony_ci		return -EINVAL;
6358c2ecf20Sopenharmony_ci	}
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci	if(err != 0)
6388c2ecf20Sopenharmony_ci		LOG("Unexpected error: %d\n", err);
6398c2ecf20Sopenharmony_ci	return err;
6408c2ecf20Sopenharmony_ci}
6418c2ecf20Sopenharmony_ci
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci/******************************************************************************
6448c2ecf20Sopenharmony_ci *
6458c2ecf20Sopenharmony_ci *  submit_urbs
6468c2ecf20Sopenharmony_ci *
6478c2ecf20Sopenharmony_ci *****************************************************************************/
6488c2ecf20Sopenharmony_cistatic int submit_urbs(struct camera_data *cam)
6498c2ecf20Sopenharmony_ci{
6508c2ecf20Sopenharmony_ci	struct urb *urb;
6518c2ecf20Sopenharmony_ci	int fx, err, i, j;
6528c2ecf20Sopenharmony_ci
6538c2ecf20Sopenharmony_ci	for(i=0; i<NUM_SBUF; ++i) {
6548c2ecf20Sopenharmony_ci		if (cam->sbuf[i].data)
6558c2ecf20Sopenharmony_ci			continue;
6568c2ecf20Sopenharmony_ci		cam->sbuf[i].data =
6578c2ecf20Sopenharmony_ci		    kmalloc_array(FRAME_SIZE_PER_DESC, FRAMES_PER_DESC,
6588c2ecf20Sopenharmony_ci				  GFP_KERNEL);
6598c2ecf20Sopenharmony_ci		if (!cam->sbuf[i].data) {
6608c2ecf20Sopenharmony_ci			while (--i >= 0) {
6618c2ecf20Sopenharmony_ci				kfree(cam->sbuf[i].data);
6628c2ecf20Sopenharmony_ci				cam->sbuf[i].data = NULL;
6638c2ecf20Sopenharmony_ci			}
6648c2ecf20Sopenharmony_ci			return -ENOMEM;
6658c2ecf20Sopenharmony_ci		}
6668c2ecf20Sopenharmony_ci	}
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* We double buffer the Isoc lists, and also know the polling
6698c2ecf20Sopenharmony_ci	 * interval is every frame (1 == (1 << (bInterval -1))).
6708c2ecf20Sopenharmony_ci	 */
6718c2ecf20Sopenharmony_ci	for(i=0; i<NUM_SBUF; ++i) {
6728c2ecf20Sopenharmony_ci		if(cam->sbuf[i].urb) {
6738c2ecf20Sopenharmony_ci			continue;
6748c2ecf20Sopenharmony_ci		}
6758c2ecf20Sopenharmony_ci		urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
6768c2ecf20Sopenharmony_ci		if (!urb) {
6778c2ecf20Sopenharmony_ci			for (j = 0; j < i; j++)
6788c2ecf20Sopenharmony_ci				usb_free_urb(cam->sbuf[j].urb);
6798c2ecf20Sopenharmony_ci			for (j = 0; j < NUM_SBUF; j++) {
6808c2ecf20Sopenharmony_ci				kfree(cam->sbuf[j].data);
6818c2ecf20Sopenharmony_ci				cam->sbuf[j].data = NULL;
6828c2ecf20Sopenharmony_ci			}
6838c2ecf20Sopenharmony_ci			return -ENOMEM;
6848c2ecf20Sopenharmony_ci		}
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci		cam->sbuf[i].urb = urb;
6878c2ecf20Sopenharmony_ci		urb->dev = cam->dev;
6888c2ecf20Sopenharmony_ci		urb->context = cam;
6898c2ecf20Sopenharmony_ci		urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/);
6908c2ecf20Sopenharmony_ci		urb->transfer_flags = URB_ISO_ASAP;
6918c2ecf20Sopenharmony_ci		urb->transfer_buffer = cam->sbuf[i].data;
6928c2ecf20Sopenharmony_ci		urb->complete = cpia2_usb_complete;
6938c2ecf20Sopenharmony_ci		urb->number_of_packets = FRAMES_PER_DESC;
6948c2ecf20Sopenharmony_ci		urb->interval = 1;
6958c2ecf20Sopenharmony_ci		urb->transfer_buffer_length =
6968c2ecf20Sopenharmony_ci			FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci		for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
6998c2ecf20Sopenharmony_ci			urb->iso_frame_desc[fx].offset =
7008c2ecf20Sopenharmony_ci				FRAME_SIZE_PER_DESC * fx;
7018c2ecf20Sopenharmony_ci			urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
7028c2ecf20Sopenharmony_ci		}
7038c2ecf20Sopenharmony_ci	}
7048c2ecf20Sopenharmony_ci
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci	/* Queue the ISO urbs, and resubmit in the completion handler */
7078c2ecf20Sopenharmony_ci	for(i=0; i<NUM_SBUF; ++i) {
7088c2ecf20Sopenharmony_ci		err = usb_submit_urb(cam->sbuf[i].urb, GFP_KERNEL);
7098c2ecf20Sopenharmony_ci		if (err) {
7108c2ecf20Sopenharmony_ci			ERR("usb_submit_urb[%d]() = %d\n", i, err);
7118c2ecf20Sopenharmony_ci			return err;
7128c2ecf20Sopenharmony_ci		}
7138c2ecf20Sopenharmony_ci	}
7148c2ecf20Sopenharmony_ci
7158c2ecf20Sopenharmony_ci	return 0;
7168c2ecf20Sopenharmony_ci}
7178c2ecf20Sopenharmony_ci
7188c2ecf20Sopenharmony_ci/******************************************************************************
7198c2ecf20Sopenharmony_ci *
7208c2ecf20Sopenharmony_ci *  cpia2_usb_stream_start
7218c2ecf20Sopenharmony_ci *
7228c2ecf20Sopenharmony_ci *****************************************************************************/
7238c2ecf20Sopenharmony_ciint cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate)
7248c2ecf20Sopenharmony_ci{
7258c2ecf20Sopenharmony_ci	int ret;
7268c2ecf20Sopenharmony_ci	int old_alt;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if(cam->streaming)
7298c2ecf20Sopenharmony_ci		return 0;
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	if (cam->flush) {
7328c2ecf20Sopenharmony_ci		int i;
7338c2ecf20Sopenharmony_ci		DBG("Flushing buffers\n");
7348c2ecf20Sopenharmony_ci		for(i=0; i<cam->num_frames; ++i) {
7358c2ecf20Sopenharmony_ci			cam->buffers[i].status = FRAME_EMPTY;
7368c2ecf20Sopenharmony_ci			cam->buffers[i].length = 0;
7378c2ecf20Sopenharmony_ci		}
7388c2ecf20Sopenharmony_ci		cam->curbuff = &cam->buffers[0];
7398c2ecf20Sopenharmony_ci		cam->workbuff = cam->curbuff->next;
7408c2ecf20Sopenharmony_ci		cam->flush = false;
7418c2ecf20Sopenharmony_ci	}
7428c2ecf20Sopenharmony_ci
7438c2ecf20Sopenharmony_ci	old_alt = cam->params.camera_state.stream_mode;
7448c2ecf20Sopenharmony_ci	cam->params.camera_state.stream_mode = 0;
7458c2ecf20Sopenharmony_ci	ret = cpia2_usb_change_streaming_alternate(cam, alternate);
7468c2ecf20Sopenharmony_ci	if (ret < 0) {
7478c2ecf20Sopenharmony_ci		int ret2;
7488c2ecf20Sopenharmony_ci		ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret);
7498c2ecf20Sopenharmony_ci		cam->params.camera_state.stream_mode = old_alt;
7508c2ecf20Sopenharmony_ci		ret2 = set_alternate(cam, USBIF_CMDONLY);
7518c2ecf20Sopenharmony_ci		if (ret2 < 0) {
7528c2ecf20Sopenharmony_ci			ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already failed. Then tried to call set_alternate(USBIF_CMDONLY) = %d.\n",
7538c2ecf20Sopenharmony_ci			    alternate, ret, ret2);
7548c2ecf20Sopenharmony_ci		}
7558c2ecf20Sopenharmony_ci	} else {
7568c2ecf20Sopenharmony_ci		cam->frame_count = 0;
7578c2ecf20Sopenharmony_ci		cam->streaming = 1;
7588c2ecf20Sopenharmony_ci		ret = cpia2_usb_stream_resume(cam);
7598c2ecf20Sopenharmony_ci	}
7608c2ecf20Sopenharmony_ci	return ret;
7618c2ecf20Sopenharmony_ci}
7628c2ecf20Sopenharmony_ci
7638c2ecf20Sopenharmony_ci/******************************************************************************
7648c2ecf20Sopenharmony_ci *
7658c2ecf20Sopenharmony_ci *  cpia2_usb_stream_pause
7668c2ecf20Sopenharmony_ci *
7678c2ecf20Sopenharmony_ci *****************************************************************************/
7688c2ecf20Sopenharmony_ciint cpia2_usb_stream_pause(struct camera_data *cam)
7698c2ecf20Sopenharmony_ci{
7708c2ecf20Sopenharmony_ci	int ret = 0;
7718c2ecf20Sopenharmony_ci	if(cam->streaming) {
7728c2ecf20Sopenharmony_ci		free_sbufs(cam);
7738c2ecf20Sopenharmony_ci		ret = set_alternate(cam, USBIF_CMDONLY);
7748c2ecf20Sopenharmony_ci	}
7758c2ecf20Sopenharmony_ci	return ret;
7768c2ecf20Sopenharmony_ci}
7778c2ecf20Sopenharmony_ci
7788c2ecf20Sopenharmony_ci/******************************************************************************
7798c2ecf20Sopenharmony_ci *
7808c2ecf20Sopenharmony_ci *  cpia2_usb_stream_resume
7818c2ecf20Sopenharmony_ci *
7828c2ecf20Sopenharmony_ci *****************************************************************************/
7838c2ecf20Sopenharmony_ciint cpia2_usb_stream_resume(struct camera_data *cam)
7848c2ecf20Sopenharmony_ci{
7858c2ecf20Sopenharmony_ci	int ret = 0;
7868c2ecf20Sopenharmony_ci	if(cam->streaming) {
7878c2ecf20Sopenharmony_ci		cam->first_image_seen = 0;
7888c2ecf20Sopenharmony_ci		ret = set_alternate(cam, cam->params.camera_state.stream_mode);
7898c2ecf20Sopenharmony_ci		if(ret == 0) {
7908c2ecf20Sopenharmony_ci			/* for some reason the user effects need to be set
7918c2ecf20Sopenharmony_ci			   again when starting streaming. */
7928c2ecf20Sopenharmony_ci			cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
7938c2ecf20Sopenharmony_ci					cam->params.vp_params.user_effects);
7948c2ecf20Sopenharmony_ci			ret = submit_urbs(cam);
7958c2ecf20Sopenharmony_ci		}
7968c2ecf20Sopenharmony_ci	}
7978c2ecf20Sopenharmony_ci	return ret;
7988c2ecf20Sopenharmony_ci}
7998c2ecf20Sopenharmony_ci
8008c2ecf20Sopenharmony_ci/******************************************************************************
8018c2ecf20Sopenharmony_ci *
8028c2ecf20Sopenharmony_ci *  cpia2_usb_stream_stop
8038c2ecf20Sopenharmony_ci *
8048c2ecf20Sopenharmony_ci *****************************************************************************/
8058c2ecf20Sopenharmony_ciint cpia2_usb_stream_stop(struct camera_data *cam)
8068c2ecf20Sopenharmony_ci{
8078c2ecf20Sopenharmony_ci	int ret;
8088c2ecf20Sopenharmony_ci
8098c2ecf20Sopenharmony_ci	ret = cpia2_usb_stream_pause(cam);
8108c2ecf20Sopenharmony_ci	cam->streaming = 0;
8118c2ecf20Sopenharmony_ci	configure_transfer_mode(cam, 0);
8128c2ecf20Sopenharmony_ci	return ret;
8138c2ecf20Sopenharmony_ci}
8148c2ecf20Sopenharmony_ci
8158c2ecf20Sopenharmony_ci/******************************************************************************
8168c2ecf20Sopenharmony_ci *
8178c2ecf20Sopenharmony_ci *  cpia2_usb_probe
8188c2ecf20Sopenharmony_ci *
8198c2ecf20Sopenharmony_ci *  Probe and initialize.
8208c2ecf20Sopenharmony_ci *****************************************************************************/
8218c2ecf20Sopenharmony_cistatic int cpia2_usb_probe(struct usb_interface *intf,
8228c2ecf20Sopenharmony_ci			   const struct usb_device_id *id)
8238c2ecf20Sopenharmony_ci{
8248c2ecf20Sopenharmony_ci	struct usb_device *udev = interface_to_usbdev(intf);
8258c2ecf20Sopenharmony_ci	struct usb_interface_descriptor *interface;
8268c2ecf20Sopenharmony_ci	struct camera_data *cam;
8278c2ecf20Sopenharmony_ci	int ret;
8288c2ecf20Sopenharmony_ci
8298c2ecf20Sopenharmony_ci	/* A multi-config CPiA2 camera? */
8308c2ecf20Sopenharmony_ci	if (udev->descriptor.bNumConfigurations != 1)
8318c2ecf20Sopenharmony_ci		return -ENODEV;
8328c2ecf20Sopenharmony_ci	interface = &intf->cur_altsetting->desc;
8338c2ecf20Sopenharmony_ci
8348c2ecf20Sopenharmony_ci	/* If we get to this point, we found a CPiA2 camera */
8358c2ecf20Sopenharmony_ci	LOG("CPiA2 USB camera found\n");
8368c2ecf20Sopenharmony_ci
8378c2ecf20Sopenharmony_ci	cam = cpia2_init_camera_struct(intf);
8388c2ecf20Sopenharmony_ci	if (cam == NULL)
8398c2ecf20Sopenharmony_ci		return -ENOMEM;
8408c2ecf20Sopenharmony_ci
8418c2ecf20Sopenharmony_ci	cam->dev = udev;
8428c2ecf20Sopenharmony_ci	cam->iface = interface->bInterfaceNumber;
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci	ret = set_alternate(cam, USBIF_CMDONLY);
8458c2ecf20Sopenharmony_ci	if (ret < 0) {
8468c2ecf20Sopenharmony_ci		ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret);
8478c2ecf20Sopenharmony_ci		goto alt_err;
8488c2ecf20Sopenharmony_ci	}
8498c2ecf20Sopenharmony_ci
8508c2ecf20Sopenharmony_ci
8518c2ecf20Sopenharmony_ci	if((ret = cpia2_init_camera(cam)) < 0) {
8528c2ecf20Sopenharmony_ci		ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret);
8538c2ecf20Sopenharmony_ci		goto alt_err;
8548c2ecf20Sopenharmony_ci	}
8558c2ecf20Sopenharmony_ci	LOG("  CPiA Version: %d.%02d (%d.%d)\n",
8568c2ecf20Sopenharmony_ci	       cam->params.version.firmware_revision_hi,
8578c2ecf20Sopenharmony_ci	       cam->params.version.firmware_revision_lo,
8588c2ecf20Sopenharmony_ci	       cam->params.version.asic_id,
8598c2ecf20Sopenharmony_ci	       cam->params.version.asic_rev);
8608c2ecf20Sopenharmony_ci	LOG("  CPiA PnP-ID: %04x:%04x:%04x\n",
8618c2ecf20Sopenharmony_ci	       cam->params.pnp_id.vendor,
8628c2ecf20Sopenharmony_ci	       cam->params.pnp_id.product,
8638c2ecf20Sopenharmony_ci	       cam->params.pnp_id.device_revision);
8648c2ecf20Sopenharmony_ci	LOG("  SensorID: %d.(version %d)\n",
8658c2ecf20Sopenharmony_ci	       cam->params.version.sensor_flags,
8668c2ecf20Sopenharmony_ci	       cam->params.version.sensor_rev);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, cam);
8698c2ecf20Sopenharmony_ci
8708c2ecf20Sopenharmony_ci	ret = cpia2_register_camera(cam);
8718c2ecf20Sopenharmony_ci	if (ret < 0) {
8728c2ecf20Sopenharmony_ci		ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret);
8738c2ecf20Sopenharmony_ci		goto alt_err;
8748c2ecf20Sopenharmony_ci	}
8758c2ecf20Sopenharmony_ci
8768c2ecf20Sopenharmony_ci	return 0;
8778c2ecf20Sopenharmony_ci
8788c2ecf20Sopenharmony_cialt_err:
8798c2ecf20Sopenharmony_ci	cpia2_deinit_camera_struct(cam, intf);
8808c2ecf20Sopenharmony_ci	return ret;
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_ci/******************************************************************************
8848c2ecf20Sopenharmony_ci *
8858c2ecf20Sopenharmony_ci *  cpia2_disconnect
8868c2ecf20Sopenharmony_ci *
8878c2ecf20Sopenharmony_ci *****************************************************************************/
8888c2ecf20Sopenharmony_cistatic void cpia2_usb_disconnect(struct usb_interface *intf)
8898c2ecf20Sopenharmony_ci{
8908c2ecf20Sopenharmony_ci	struct camera_data *cam = usb_get_intfdata(intf);
8918c2ecf20Sopenharmony_ci	usb_set_intfdata(intf, NULL);
8928c2ecf20Sopenharmony_ci
8938c2ecf20Sopenharmony_ci	DBG("Stopping stream\n");
8948c2ecf20Sopenharmony_ci	cpia2_usb_stream_stop(cam);
8958c2ecf20Sopenharmony_ci
8968c2ecf20Sopenharmony_ci	mutex_lock(&cam->v4l2_lock);
8978c2ecf20Sopenharmony_ci	DBG("Unregistering camera\n");
8988c2ecf20Sopenharmony_ci	cpia2_unregister_camera(cam);
8998c2ecf20Sopenharmony_ci	v4l2_device_disconnect(&cam->v4l2_dev);
9008c2ecf20Sopenharmony_ci	mutex_unlock(&cam->v4l2_lock);
9018c2ecf20Sopenharmony_ci
9028c2ecf20Sopenharmony_ci	if(cam->buffers) {
9038c2ecf20Sopenharmony_ci		DBG("Wakeup waiting processes\n");
9048c2ecf20Sopenharmony_ci		cam->curbuff->status = FRAME_READY;
9058c2ecf20Sopenharmony_ci		cam->curbuff->length = 0;
9068c2ecf20Sopenharmony_ci		wake_up_interruptible(&cam->wq_stream);
9078c2ecf20Sopenharmony_ci	}
9088c2ecf20Sopenharmony_ci
9098c2ecf20Sopenharmony_ci	v4l2_device_put(&cam->v4l2_dev);
9108c2ecf20Sopenharmony_ci
9118c2ecf20Sopenharmony_ci	LOG("CPiA2 camera disconnected.\n");
9128c2ecf20Sopenharmony_ci}
9138c2ecf20Sopenharmony_ci
9148c2ecf20Sopenharmony_cistatic int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message)
9158c2ecf20Sopenharmony_ci{
9168c2ecf20Sopenharmony_ci	struct camera_data *cam = usb_get_intfdata(intf);
9178c2ecf20Sopenharmony_ci
9188c2ecf20Sopenharmony_ci	mutex_lock(&cam->v4l2_lock);
9198c2ecf20Sopenharmony_ci	if (cam->streaming) {
9208c2ecf20Sopenharmony_ci		cpia2_usb_stream_stop(cam);
9218c2ecf20Sopenharmony_ci		cam->streaming = 1;
9228c2ecf20Sopenharmony_ci	}
9238c2ecf20Sopenharmony_ci	mutex_unlock(&cam->v4l2_lock);
9248c2ecf20Sopenharmony_ci
9258c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "going into suspend..\n");
9268c2ecf20Sopenharmony_ci	return 0;
9278c2ecf20Sopenharmony_ci}
9288c2ecf20Sopenharmony_ci
9298c2ecf20Sopenharmony_ci/* Resume device - start device. */
9308c2ecf20Sopenharmony_cistatic int cpia2_usb_resume(struct usb_interface *intf)
9318c2ecf20Sopenharmony_ci{
9328c2ecf20Sopenharmony_ci	struct camera_data *cam = usb_get_intfdata(intf);
9338c2ecf20Sopenharmony_ci
9348c2ecf20Sopenharmony_ci	mutex_lock(&cam->v4l2_lock);
9358c2ecf20Sopenharmony_ci	v4l2_ctrl_handler_setup(&cam->hdl);
9368c2ecf20Sopenharmony_ci	if (cam->streaming) {
9378c2ecf20Sopenharmony_ci		cam->streaming = 0;
9388c2ecf20Sopenharmony_ci		cpia2_usb_stream_start(cam,
9398c2ecf20Sopenharmony_ci				cam->params.camera_state.stream_mode);
9408c2ecf20Sopenharmony_ci	}
9418c2ecf20Sopenharmony_ci	mutex_unlock(&cam->v4l2_lock);
9428c2ecf20Sopenharmony_ci
9438c2ecf20Sopenharmony_ci	dev_info(&intf->dev, "coming out of suspend..\n");
9448c2ecf20Sopenharmony_ci	return 0;
9458c2ecf20Sopenharmony_ci}
9468c2ecf20Sopenharmony_ci
9478c2ecf20Sopenharmony_ci/******************************************************************************
9488c2ecf20Sopenharmony_ci *
9498c2ecf20Sopenharmony_ci *  usb_cpia2_init
9508c2ecf20Sopenharmony_ci *
9518c2ecf20Sopenharmony_ci *****************************************************************************/
9528c2ecf20Sopenharmony_ciint cpia2_usb_init(void)
9538c2ecf20Sopenharmony_ci{
9548c2ecf20Sopenharmony_ci	return usb_register(&cpia2_driver);
9558c2ecf20Sopenharmony_ci}
9568c2ecf20Sopenharmony_ci
9578c2ecf20Sopenharmony_ci/******************************************************************************
9588c2ecf20Sopenharmony_ci *
9598c2ecf20Sopenharmony_ci *  usb_cpia_cleanup
9608c2ecf20Sopenharmony_ci *
9618c2ecf20Sopenharmony_ci *****************************************************************************/
9628c2ecf20Sopenharmony_civoid cpia2_usb_cleanup(void)
9638c2ecf20Sopenharmony_ci{
9648c2ecf20Sopenharmony_ci	schedule_timeout(2 * HZ);
9658c2ecf20Sopenharmony_ci	usb_deregister(&cpia2_driver);
9668c2ecf20Sopenharmony_ci}
967