2019-05-27 14:55:06 +08:00
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2006-02-27 11:09:05 +08:00
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* Filename: cpia2_usb.c
|
|
|
|
*
|
|
|
|
* Copyright 2001, STMicrolectronics, Inc.
|
|
|
|
* Contact: steve.miller@st.com
|
|
|
|
*
|
|
|
|
* Description:
|
|
|
|
* This is a USB driver for CPia2 based video cameras.
|
|
|
|
* The infrastructure of this driver is based on the cpia usb driver by
|
|
|
|
* Jochen Scharrlach and Johannes Erdfeldt.
|
|
|
|
*
|
|
|
|
* Stripped of 2.4 stuff ready for main kernel submit by
|
2008-10-28 02:13:47 +08:00
|
|
|
* Alan Cox <alan@lxorguk.ukuu.org.uk>
|
2006-02-27 11:09:05 +08:00
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/usb.h>
|
2011-07-04 02:03:12 +08:00
|
|
|
#include <linux/module.h>
|
2006-02-27 11:09:05 +08:00
|
|
|
|
|
|
|
#include "cpia2.h"
|
|
|
|
|
|
|
|
static int frame_sizes[] = {
|
|
|
|
0, // USBIF_CMDONLY
|
2018-01-05 02:08:56 +08:00
|
|
|
0, // USBIF_BULK
|
|
|
|
128, // USBIF_ISO_1
|
|
|
|
384, // USBIF_ISO_2
|
|
|
|
640, // USBIF_ISO_3
|
|
|
|
768, // USBIF_ISO_4
|
|
|
|
896, // USBIF_ISO_5
|
|
|
|
1023, // USBIF_ISO_6
|
2006-02-27 11:09:05 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
#define FRAMES_PER_DESC 10
|
|
|
|
#define FRAME_SIZE_PER_DESC frame_sizes[cam->cur_alt]
|
|
|
|
|
|
|
|
static void process_frame(struct camera_data *cam);
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static void cpia2_usb_complete(struct urb *urb);
|
2006-02-27 11:09:05 +08:00
|
|
|
static int cpia2_usb_probe(struct usb_interface *intf,
|
|
|
|
const struct usb_device_id *id);
|
|
|
|
static void cpia2_usb_disconnect(struct usb_interface *intf);
|
2012-04-29 19:44:44 +08:00
|
|
|
static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message);
|
|
|
|
static int cpia2_usb_resume(struct usb_interface *intf);
|
2006-02-27 11:09:05 +08:00
|
|
|
|
|
|
|
static void free_sbufs(struct camera_data *cam);
|
|
|
|
static void add_APPn(struct camera_data *cam);
|
|
|
|
static void add_COM(struct camera_data *cam);
|
|
|
|
static int submit_urbs(struct camera_data *cam);
|
|
|
|
static int set_alternate(struct camera_data *cam, unsigned int alt);
|
|
|
|
static int configure_transfer_mode(struct camera_data *cam, unsigned int alt);
|
|
|
|
|
2017-08-13 16:54:43 +08:00
|
|
|
static const struct usb_device_id cpia2_id_table[] = {
|
2006-02-27 11:09:05 +08:00
|
|
|
{USB_DEVICE(0x0553, 0x0100)},
|
|
|
|
{USB_DEVICE(0x0553, 0x0140)},
|
|
|
|
{USB_DEVICE(0x0553, 0x0151)}, /* STV0676 */
|
|
|
|
{} /* Terminating entry */
|
|
|
|
};
|
|
|
|
MODULE_DEVICE_TABLE(usb, cpia2_id_table);
|
|
|
|
|
|
|
|
static struct usb_driver cpia2_driver = {
|
|
|
|
.name = "cpia2",
|
|
|
|
.probe = cpia2_usb_probe,
|
|
|
|
.disconnect = cpia2_usb_disconnect,
|
2012-04-29 19:44:44 +08:00
|
|
|
.suspend = cpia2_usb_suspend,
|
|
|
|
.resume = cpia2_usb_resume,
|
|
|
|
.reset_resume = cpia2_usb_resume,
|
2006-02-27 11:09:05 +08:00
|
|
|
.id_table = cpia2_id_table
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* process_frame
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
static void process_frame(struct camera_data *cam)
|
|
|
|
{
|
2008-04-23 01:41:48 +08:00
|
|
|
static int frame_count;
|
2006-02-27 11:09:05 +08:00
|
|
|
|
|
|
|
unsigned char *inbuff = cam->workbuff->data;
|
|
|
|
|
|
|
|
DBG("Processing frame #%d, current:%d\n",
|
|
|
|
cam->workbuff->num, cam->curbuff->num);
|
|
|
|
|
|
|
|
if(cam->workbuff->length > cam->workbuff->max_length)
|
|
|
|
cam->workbuff->max_length = cam->workbuff->length;
|
|
|
|
|
|
|
|
if ((inbuff[0] == 0xFF) && (inbuff[1] == 0xD8)) {
|
|
|
|
frame_count++;
|
|
|
|
} else {
|
|
|
|
cam->workbuff->status = FRAME_ERROR;
|
|
|
|
DBG("Start of frame not found\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
* Now the output buffer should have a JPEG image in it.
|
|
|
|
***/
|
|
|
|
if(!cam->first_image_seen) {
|
|
|
|
/* Always skip the first image after streaming
|
|
|
|
* starts. It is almost certainly corrupt. */
|
|
|
|
cam->first_image_seen = 1;
|
|
|
|
cam->workbuff->status = FRAME_EMPTY;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (cam->workbuff->length > 3) {
|
|
|
|
if(cam->mmapped &&
|
|
|
|
cam->workbuff->length < cam->workbuff->max_length) {
|
|
|
|
/* No junk in the buffers */
|
|
|
|
memset(cam->workbuff->data+cam->workbuff->length,
|
|
|
|
0, cam->workbuff->max_length-
|
|
|
|
cam->workbuff->length);
|
|
|
|
}
|
|
|
|
cam->workbuff->max_length = cam->workbuff->length;
|
|
|
|
cam->workbuff->status = FRAME_READY;
|
|
|
|
|
|
|
|
if(!cam->mmapped && cam->num_frames > 2) {
|
|
|
|
/* During normal reading, the most recent
|
|
|
|
* frame will be read. If the current frame
|
|
|
|
* hasn't started reading yet, it will never
|
|
|
|
* be read, so mark it empty. If the buffer is
|
|
|
|
* mmapped, or we have few buffers, we need to
|
|
|
|
* wait for the user to free the buffer.
|
|
|
|
*
|
|
|
|
* NOTE: This is not entirely foolproof with 3
|
|
|
|
* buffers, but it would take an EXTREMELY
|
|
|
|
* overloaded system to cause problems (possible
|
|
|
|
* image data corruption). Basically, it would
|
|
|
|
* need to take more time to execute cpia2_read
|
|
|
|
* than it would for the camera to send
|
|
|
|
* cam->num_frames-2 frames before problems
|
|
|
|
* could occur.
|
|
|
|
*/
|
|
|
|
cam->curbuff->status = FRAME_EMPTY;
|
|
|
|
}
|
|
|
|
cam->curbuff = cam->workbuff;
|
|
|
|
cam->workbuff = cam->workbuff->next;
|
|
|
|
DBG("Changed buffers, work:%d, current:%d\n",
|
|
|
|
cam->workbuff->num, cam->curbuff->num);
|
|
|
|
return;
|
|
|
|
} else {
|
|
|
|
DBG("Not enough data for an image.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
cam->workbuff->status = FRAME_ERROR;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* add_APPn
|
|
|
|
*
|
|
|
|
* Adds a user specified APPn record
|
|
|
|
*****************************************************************************/
|
|
|
|
static void add_APPn(struct camera_data *cam)
|
|
|
|
{
|
|
|
|
if(cam->APP_len > 0) {
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = 0xFF;
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = 0xE0+cam->APPn;
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = 0;
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = cam->APP_len+2;
|
|
|
|
memcpy(cam->workbuff->data+cam->workbuff->length,
|
|
|
|
cam->APP_data, cam->APP_len);
|
|
|
|
cam->workbuff->length += cam->APP_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* add_COM
|
|
|
|
*
|
|
|
|
* Adds a user specified COM record
|
|
|
|
*****************************************************************************/
|
|
|
|
static void add_COM(struct camera_data *cam)
|
|
|
|
{
|
|
|
|
if(cam->COM_len > 0) {
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = 0xFF;
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = 0xFE;
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = 0;
|
|
|
|
cam->workbuff->data[cam->workbuff->length++] = cam->COM_len+2;
|
|
|
|
memcpy(cam->workbuff->data+cam->workbuff->length,
|
|
|
|
cam->COM_data, cam->COM_len);
|
|
|
|
cam->workbuff->length += cam->COM_len;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_complete
|
|
|
|
*
|
|
|
|
* callback when incoming packet is received
|
|
|
|
*****************************************************************************/
|
IRQ: Maintain regs pointer globally rather than passing to IRQ handlers
Maintain a per-CPU global "struct pt_regs *" variable which can be used instead
of passing regs around manually through all ~1800 interrupt handlers in the
Linux kernel.
The regs pointer is used in few places, but it potentially costs both stack
space and code to pass it around. On the FRV arch, removing the regs parameter
from all the genirq function results in a 20% speed up of the IRQ exit path
(ie: from leaving timer_interrupt() to leaving do_IRQ()).
Where appropriate, an arch may override the generic storage facility and do
something different with the variable. On FRV, for instance, the address is
maintained in GR28 at all times inside the kernel as part of general exception
handling.
Having looked over the code, it appears that the parameter may be handed down
through up to twenty or so layers of functions. Consider a USB character
device attached to a USB hub, attached to a USB controller that posts its
interrupts through a cascaded auxiliary interrupt controller. A character
device driver may want to pass regs to the sysrq handler through the input
layer which adds another few layers of parameter passing.
I've build this code with allyesconfig for x86_64 and i386. I've runtested the
main part of the code on FRV and i386, though I can't test most of the drivers.
I've also done partial conversion for powerpc and MIPS - these at least compile
with minimal configurations.
This will affect all archs. Mostly the changes should be relatively easy.
Take do_IRQ(), store the regs pointer at the beginning, saving the old one:
struct pt_regs *old_regs = set_irq_regs(regs);
And put the old one back at the end:
set_irq_regs(old_regs);
Don't pass regs through to generic_handle_irq() or __do_IRQ().
In timer_interrupt(), this sort of change will be necessary:
- update_process_times(user_mode(regs));
- profile_tick(CPU_PROFILING, regs);
+ update_process_times(user_mode(get_irq_regs()));
+ profile_tick(CPU_PROFILING);
I'd like to move update_process_times()'s use of get_irq_regs() into itself,
except that i386, alone of the archs, uses something other than user_mode().
Some notes on the interrupt handling in the drivers:
(*) input_dev() is now gone entirely. The regs pointer is no longer stored in
the input_dev struct.
(*) finish_unlinks() in drivers/usb/host/ohci-q.c needs checking. It does
something different depending on whether it's been supplied with a regs
pointer or not.
(*) Various IRQ handler function pointers have been moved to type
irq_handler_t.
Signed-Off-By: David Howells <dhowells@redhat.com>
(cherry picked from 1b16e7ac850969f38b375e511e3fa2f474a33867 commit)
2006-10-05 21:55:46 +08:00
|
|
|
static void cpia2_usb_complete(struct urb *urb)
|
2006-02-27 11:09:05 +08:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
unsigned char *cdata;
|
2013-09-22 06:27:38 +08:00
|
|
|
static bool frame_ready = false;
|
2006-02-27 11:09:05 +08:00
|
|
|
struct camera_data *cam = (struct camera_data *) urb->context;
|
|
|
|
|
|
|
|
if (urb->status!=0) {
|
|
|
|
if (!(urb->status == -ENOENT ||
|
|
|
|
urb->status == -ECONNRESET ||
|
|
|
|
urb->status == -ESHUTDOWN))
|
|
|
|
{
|
|
|
|
DBG("urb->status = %d!\n", urb->status);
|
|
|
|
}
|
|
|
|
DBG("Stopping streaming\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
if (!cam->streaming || !video_is_registered(&cam->vdev)) {
|
|
|
|
LOG("Will now stop the streaming: streaming = %d, present=%d\n",
|
|
|
|
cam->streaming, video_is_registered(&cam->vdev));
|
2006-02-27 11:09:05 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/***
|
|
|
|
* Packet collater
|
|
|
|
***/
|
|
|
|
//DBG("Collating %d packets\n", urb->number_of_packets);
|
|
|
|
for (i = 0; i < urb->number_of_packets; i++) {
|
|
|
|
u16 checksum, iso_checksum;
|
|
|
|
int j;
|
|
|
|
int n = urb->iso_frame_desc[i].actual_length;
|
|
|
|
int st = urb->iso_frame_desc[i].status;
|
|
|
|
|
|
|
|
if(cam->workbuff->status == FRAME_READY) {
|
|
|
|
struct framebuf *ptr;
|
|
|
|
/* Try to find an available buffer */
|
|
|
|
DBG("workbuff full, searching\n");
|
|
|
|
for (ptr = cam->workbuff->next;
|
|
|
|
ptr != cam->workbuff;
|
|
|
|
ptr = ptr->next)
|
|
|
|
{
|
|
|
|
if (ptr->status == FRAME_EMPTY) {
|
|
|
|
ptr->status = FRAME_READING;
|
|
|
|
ptr->length = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (ptr == cam->workbuff)
|
|
|
|
break; /* No READING or EMPTY buffers left */
|
|
|
|
|
|
|
|
cam->workbuff = ptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->workbuff->status == FRAME_EMPTY ||
|
|
|
|
cam->workbuff->status == FRAME_ERROR) {
|
|
|
|
cam->workbuff->status = FRAME_READING;
|
|
|
|
cam->workbuff->length = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//DBG(" Packet %d length = %d, status = %d\n", i, n, st);
|
|
|
|
cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset;
|
|
|
|
|
|
|
|
if (st) {
|
|
|
|
LOG("cpia2 data error: [%d] len=%d, status = %d\n",
|
|
|
|
i, n, st);
|
|
|
|
if(!ALLOW_CORRUPT)
|
|
|
|
cam->workbuff->status = FRAME_ERROR;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(n<=2)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
checksum = 0;
|
|
|
|
for(j=0; j<n-2; ++j)
|
|
|
|
checksum += cdata[j];
|
|
|
|
iso_checksum = cdata[j] + cdata[j+1]*256;
|
|
|
|
if(checksum != iso_checksum) {
|
|
|
|
LOG("checksum mismatch: [%d] len=%d, calculated = %x, checksum = %x\n",
|
|
|
|
i, n, (int)checksum, (int)iso_checksum);
|
|
|
|
if(!ALLOW_CORRUPT) {
|
|
|
|
cam->workbuff->status = FRAME_ERROR;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
n -= 2;
|
|
|
|
|
|
|
|
if(cam->workbuff->status != FRAME_READING) {
|
|
|
|
if((0xFF == cdata[0] && 0xD8 == cdata[1]) ||
|
|
|
|
(0xD8 == cdata[0] && 0xFF == cdata[1] &&
|
|
|
|
0 != cdata[2])) {
|
|
|
|
/* frame is skipped, but increment total
|
|
|
|
* frame count anyway */
|
|
|
|
cam->frame_count++;
|
|
|
|
}
|
|
|
|
DBG("workbuff not reading, status=%d\n",
|
|
|
|
cam->workbuff->status);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->frame_size < cam->workbuff->length + n) {
|
|
|
|
ERR("buffer overflow! length: %d, n: %d\n",
|
|
|
|
cam->workbuff->length, n);
|
|
|
|
cam->workbuff->status = FRAME_ERROR;
|
|
|
|
if(cam->workbuff->length > cam->workbuff->max_length)
|
|
|
|
cam->workbuff->max_length =
|
|
|
|
cam->workbuff->length;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cam->workbuff->length == 0) {
|
|
|
|
int data_offset;
|
|
|
|
if ((0xD8 == cdata[0]) && (0xFF == cdata[1])) {
|
|
|
|
data_offset = 1;
|
|
|
|
} else if((0xFF == cdata[0]) && (0xD8 == cdata[1])
|
|
|
|
&& (0xFF == cdata[2])) {
|
|
|
|
data_offset = 2;
|
|
|
|
} else {
|
|
|
|
DBG("Ignoring packet, not beginning!\n");
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
DBG("Start of frame pattern found\n");
|
2019-01-21 21:32:25 +08:00
|
|
|
cam->workbuff->ts = ktime_get_ns();
|
2006-02-27 11:09:05 +08:00
|
|
|
cam->workbuff->seq = cam->frame_count++;
|
|
|
|
cam->workbuff->data[0] = 0xFF;
|
|
|
|
cam->workbuff->data[1] = 0xD8;
|
|
|
|
cam->workbuff->length = 2;
|
|
|
|
add_APPn(cam);
|
|
|
|
add_COM(cam);
|
|
|
|
memcpy(cam->workbuff->data+cam->workbuff->length,
|
|
|
|
cdata+data_offset, n-data_offset);
|
|
|
|
cam->workbuff->length += n-data_offset;
|
|
|
|
} else if (cam->workbuff->length > 0) {
|
|
|
|
memcpy(cam->workbuff->data + cam->workbuff->length,
|
|
|
|
cdata, n);
|
|
|
|
cam->workbuff->length += n;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((cam->workbuff->length >= 3) &&
|
|
|
|
(cam->workbuff->data[cam->workbuff->length - 3] == 0xFF) &&
|
|
|
|
(cam->workbuff->data[cam->workbuff->length - 2] == 0xD9) &&
|
|
|
|
(cam->workbuff->data[cam->workbuff->length - 1] == 0xFF)) {
|
|
|
|
frame_ready = true;
|
|
|
|
cam->workbuff->data[cam->workbuff->length - 1] = 0;
|
|
|
|
cam->workbuff->length -= 1;
|
|
|
|
} else if ((cam->workbuff->length >= 2) &&
|
|
|
|
(cam->workbuff->data[cam->workbuff->length - 2] == 0xFF) &&
|
|
|
|
(cam->workbuff->data[cam->workbuff->length - 1] == 0xD9)) {
|
|
|
|
frame_ready = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (frame_ready) {
|
|
|
|
DBG("Workbuff image size = %d\n",cam->workbuff->length);
|
|
|
|
process_frame(cam);
|
|
|
|
|
|
|
|
frame_ready = false;
|
|
|
|
|
|
|
|
if (waitqueue_active(&cam->wq_stream))
|
|
|
|
wake_up_interruptible(&cam->wq_stream);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(cam->streaming) {
|
|
|
|
/* resubmit */
|
|
|
|
urb->dev = cam->dev;
|
|
|
|
if ((i = usb_submit_urb(urb, GFP_ATOMIC)) != 0)
|
|
|
|
ERR("%s: usb_submit_urb ret %d!\n", __func__, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* configure_transfer_mode
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
static int configure_transfer_mode(struct camera_data *cam, unsigned int alt)
|
|
|
|
{
|
|
|
|
static unsigned char iso_regs[8][4] = {
|
|
|
|
{0x00, 0x00, 0x00, 0x00},
|
|
|
|
{0x00, 0x00, 0x00, 0x00},
|
|
|
|
{0xB9, 0x00, 0x00, 0x7E},
|
|
|
|
{0xB9, 0x00, 0x01, 0x7E},
|
|
|
|
{0xB9, 0x00, 0x02, 0x7E},
|
|
|
|
{0xB9, 0x00, 0x02, 0xFE},
|
|
|
|
{0xB9, 0x00, 0x03, 0x7E},
|
|
|
|
{0xB9, 0x00, 0x03, 0xFD}
|
|
|
|
};
|
|
|
|
struct cpia2_command cmd;
|
|
|
|
unsigned char reg;
|
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
if (!video_is_registered(&cam->vdev))
|
2006-02-27 11:09:05 +08:00
|
|
|
return -ENODEV;
|
|
|
|
|
|
|
|
/***
|
|
|
|
* Write the isoc registers according to the alternate selected
|
|
|
|
***/
|
|
|
|
cmd.direction = TRANSFER_WRITE;
|
|
|
|
cmd.buffer.block_data[0] = iso_regs[alt][0];
|
|
|
|
cmd.buffer.block_data[1] = iso_regs[alt][1];
|
|
|
|
cmd.buffer.block_data[2] = iso_regs[alt][2];
|
|
|
|
cmd.buffer.block_data[3] = iso_regs[alt][3];
|
|
|
|
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
|
|
|
|
cmd.start = CPIA2_VC_USB_ISOLIM;
|
|
|
|
cmd.reg_count = 4;
|
|
|
|
cpia2_send_command(cam, &cmd);
|
|
|
|
|
|
|
|
/***
|
|
|
|
* Enable relevant streams before starting polling.
|
|
|
|
* First read USB Stream Config Register.
|
|
|
|
***/
|
|
|
|
cmd.direction = TRANSFER_READ;
|
|
|
|
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
|
|
|
|
cmd.start = CPIA2_VC_USB_STRM;
|
|
|
|
cmd.reg_count = 1;
|
|
|
|
cpia2_send_command(cam, &cmd);
|
|
|
|
reg = cmd.buffer.block_data[0];
|
|
|
|
|
|
|
|
/* Clear iso, bulk, and int */
|
|
|
|
reg &= ~(CPIA2_VC_USB_STRM_BLK_ENABLE |
|
|
|
|
CPIA2_VC_USB_STRM_ISO_ENABLE |
|
|
|
|
CPIA2_VC_USB_STRM_INT_ENABLE);
|
|
|
|
|
|
|
|
if (alt == USBIF_BULK) {
|
|
|
|
DBG("Enabling bulk xfer\n");
|
|
|
|
reg |= CPIA2_VC_USB_STRM_BLK_ENABLE; /* Enable Bulk */
|
|
|
|
cam->xfer_mode = XFER_BULK;
|
|
|
|
} else if (alt >= USBIF_ISO_1) {
|
|
|
|
DBG("Enabling ISOC xfer\n");
|
|
|
|
reg |= CPIA2_VC_USB_STRM_ISO_ENABLE;
|
|
|
|
cam->xfer_mode = XFER_ISOC;
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd.buffer.block_data[0] = reg;
|
|
|
|
cmd.direction = TRANSFER_WRITE;
|
|
|
|
cmd.start = CPIA2_VC_USB_STRM;
|
|
|
|
cmd.reg_count = 1;
|
|
|
|
cmd.req_mode = CAMERAACCESS_TYPE_BLOCK | CAMERAACCESS_VC;
|
|
|
|
cpia2_send_command(cam, &cmd);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_change_streaming_alternate
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
int cpia2_usb_change_streaming_alternate(struct camera_data *cam,
|
|
|
|
unsigned int alt)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if(alt < USBIF_ISO_1 || alt > USBIF_ISO_6)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if(alt == cam->params.camera_state.stream_mode)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
cpia2_usb_stream_pause(cam);
|
|
|
|
|
|
|
|
configure_transfer_mode(cam, alt);
|
|
|
|
|
|
|
|
cam->params.camera_state.stream_mode = alt;
|
|
|
|
|
|
|
|
/* Reset the camera to prevent image quality degradation */
|
|
|
|
cpia2_reset_camera(cam);
|
|
|
|
|
|
|
|
cpia2_usb_stream_resume(cam);
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* set_alternate
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
2008-09-04 14:33:43 +08:00
|
|
|
static int set_alternate(struct camera_data *cam, unsigned int alt)
|
2006-02-27 11:09:05 +08:00
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
|
|
|
|
if(alt == cam->cur_alt)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (cam->cur_alt != USBIF_CMDONLY) {
|
|
|
|
DBG("Changing from alt %d to %d\n", cam->cur_alt, USBIF_CMDONLY);
|
|
|
|
ret = usb_set_interface(cam->dev, cam->iface, USBIF_CMDONLY);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
if (alt != USBIF_CMDONLY) {
|
|
|
|
DBG("Changing from alt %d to %d\n", USBIF_CMDONLY, alt);
|
|
|
|
ret = usb_set_interface(cam->dev, cam->iface, alt);
|
|
|
|
if (ret != 0)
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
cam->old_alt = cam->cur_alt;
|
|
|
|
cam->cur_alt = alt;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* free_sbufs
|
|
|
|
*
|
|
|
|
* Free all cam->sbuf[]. All non-NULL .data and .urb members that are non-NULL
|
|
|
|
* are assumed to be allocated. Non-NULL .urb members are also assumed to be
|
|
|
|
* submitted (and must therefore be killed before they are freed).
|
|
|
|
*****************************************************************************/
|
|
|
|
static void free_sbufs(struct camera_data *cam)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < NUM_SBUF; i++) {
|
|
|
|
if(cam->sbuf[i].urb) {
|
|
|
|
usb_kill_urb(cam->sbuf[i].urb);
|
|
|
|
usb_free_urb(cam->sbuf[i].urb);
|
|
|
|
cam->sbuf[i].urb = NULL;
|
|
|
|
}
|
|
|
|
if(cam->sbuf[i].data) {
|
|
|
|
kfree(cam->sbuf[i].data);
|
|
|
|
cam->sbuf[i].data = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*******
|
|
|
|
* Convenience functions
|
|
|
|
*******/
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* write_packet
|
|
|
|
*
|
|
|
|
***************************************************************************/
|
|
|
|
static int write_packet(struct usb_device *udev,
|
|
|
|
u8 request, u8 * registers, u16 start, size_t size)
|
|
|
|
{
|
2016-10-10 19:28:26 +08:00
|
|
|
unsigned char *buf;
|
|
|
|
int ret;
|
|
|
|
|
2006-02-27 11:09:05 +08:00
|
|
|
if (!registers || size <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-12-24 03:34:36 +08:00
|
|
|
buf = kmemdup(registers, size, GFP_KERNEL);
|
2016-10-10 19:28:26 +08:00
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = usb_control_msg(udev,
|
2006-02-27 11:09:05 +08:00
|
|
|
usb_sndctrlpipe(udev, 0),
|
|
|
|
request,
|
|
|
|
USB_TYPE_VENDOR | USB_RECIP_DEVICE,
|
|
|
|
start, /* value */
|
|
|
|
0, /* index */
|
2016-10-10 19:28:26 +08:00
|
|
|
buf, /* buffer */
|
2006-02-27 11:09:05 +08:00
|
|
|
size,
|
2021-10-25 20:16:37 +08:00
|
|
|
1000);
|
2016-10-10 19:28:26 +08:00
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
return ret;
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/****************************************************************************
|
|
|
|
*
|
|
|
|
* read_packet
|
|
|
|
*
|
|
|
|
***************************************************************************/
|
|
|
|
static int read_packet(struct usb_device *udev,
|
|
|
|
u8 request, u8 * registers, u16 start, size_t size)
|
|
|
|
{
|
2016-10-10 19:28:26 +08:00
|
|
|
unsigned char *buf;
|
|
|
|
int ret;
|
|
|
|
|
2006-02-27 11:09:05 +08:00
|
|
|
if (!registers || size <= 0)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2016-10-10 19:28:26 +08:00
|
|
|
buf = kmalloc(size, GFP_KERNEL);
|
|
|
|
if (!buf)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
ret = usb_control_msg(udev,
|
2006-02-27 11:09:05 +08:00
|
|
|
usb_rcvctrlpipe(udev, 0),
|
|
|
|
request,
|
|
|
|
USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE,
|
|
|
|
start, /* value */
|
|
|
|
0, /* index */
|
2016-10-10 19:28:26 +08:00
|
|
|
buf, /* buffer */
|
2006-02-27 11:09:05 +08:00
|
|
|
size,
|
2021-10-25 20:16:37 +08:00
|
|
|
1000);
|
2016-10-10 19:28:26 +08:00
|
|
|
|
|
|
|
if (ret >= 0)
|
|
|
|
memcpy(registers, buf, size);
|
|
|
|
|
|
|
|
kfree(buf);
|
|
|
|
|
|
|
|
return ret;
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_transfer_cmd
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
int cpia2_usb_transfer_cmd(struct camera_data *cam,
|
|
|
|
void *registers,
|
|
|
|
u8 request, u8 start, u8 count, u8 direction)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
struct usb_device *udev = cam->dev;
|
|
|
|
|
|
|
|
if (!udev) {
|
|
|
|
ERR("%s: Internal driver error: udev is NULL\n", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!registers) {
|
|
|
|
ERR("%s: Internal driver error: register array is NULL\n", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (direction == TRANSFER_READ) {
|
|
|
|
err = read_packet(udev, request, (u8 *)registers, start, count);
|
|
|
|
if (err > 0)
|
|
|
|
err = 0;
|
|
|
|
} else if (direction == TRANSFER_WRITE) {
|
|
|
|
err =write_packet(udev, request, (u8 *)registers, start, count);
|
|
|
|
if (err < 0) {
|
|
|
|
LOG("Control message failed, err val = %d\n", err);
|
|
|
|
LOG("Message: request = 0x%0X, start = 0x%0X\n",
|
|
|
|
request, start);
|
|
|
|
LOG("Message: count = %d, register[0] = 0x%0X\n",
|
|
|
|
count, ((unsigned char *) registers)[0]);
|
|
|
|
} else
|
|
|
|
err=0;
|
|
|
|
} else {
|
|
|
|
LOG("Unexpected first byte of direction: %d\n",
|
|
|
|
direction);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(err != 0)
|
|
|
|
LOG("Unexpected error: %d\n", err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* submit_urbs
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
static int submit_urbs(struct camera_data *cam)
|
|
|
|
{
|
|
|
|
struct urb *urb;
|
2008-09-04 22:21:03 +08:00
|
|
|
int fx, err, i, j;
|
2006-02-27 11:09:05 +08:00
|
|
|
|
|
|
|
for(i=0; i<NUM_SBUF; ++i) {
|
|
|
|
if (cam->sbuf[i].data)
|
|
|
|
continue;
|
|
|
|
cam->sbuf[i].data =
|
treewide: kmalloc() -> kmalloc_array()
The kmalloc() function has a 2-factor argument form, kmalloc_array(). This
patch replaces cases of:
kmalloc(a * b, gfp)
with:
kmalloc_array(a * b, gfp)
as well as handling cases of:
kmalloc(a * b * c, gfp)
with:
kmalloc(array3_size(a, b, c), gfp)
as it's slightly less ugly than:
kmalloc_array(array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
kmalloc(4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
The tools/ directory was manually excluded, since it has its own
implementation of kmalloc().
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
type TYPE;
expression THING, E;
@@
(
kmalloc(
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
kmalloc(
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression COUNT;
typedef u8;
typedef __u8;
@@
(
kmalloc(
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
kmalloc(
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(char) * COUNT
+ COUNT
, ...)
|
kmalloc(
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
identifier SIZE, COUNT;
@@
- kmalloc
+ kmalloc_array
(
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
kmalloc(
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
kmalloc(
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
kmalloc(
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
kmalloc(
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
identifier STRIDE, SIZE, COUNT;
@@
(
kmalloc(
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
kmalloc(
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
kmalloc(
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
kmalloc(sizeof(THING) * C2, ...)
|
kmalloc(sizeof(TYPE) * C2, ...)
|
kmalloc(C1 * C2 * C3, ...)
|
kmalloc(C1 * C2, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * E2
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- (E1) * (E2)
+ E1, E2
, ...)
|
- kmalloc
+ kmalloc_array
(
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-13 04:55:00 +08:00
|
|
|
kmalloc_array(FRAME_SIZE_PER_DESC, FRAMES_PER_DESC,
|
|
|
|
GFP_KERNEL);
|
2006-02-27 11:09:05 +08:00
|
|
|
if (!cam->sbuf[i].data) {
|
2006-12-20 20:30:45 +08:00
|
|
|
while (--i >= 0) {
|
|
|
|
kfree(cam->sbuf[i].data);
|
|
|
|
cam->sbuf[i].data = NULL;
|
|
|
|
}
|
2006-02-27 11:09:05 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We double buffer the Isoc lists, and also know the polling
|
|
|
|
* interval is every frame (1 == (1 << (bInterval -1))).
|
|
|
|
*/
|
|
|
|
for(i=0; i<NUM_SBUF; ++i) {
|
|
|
|
if(cam->sbuf[i].urb) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
|
|
|
|
if (!urb) {
|
2008-09-04 22:21:03 +08:00
|
|
|
for (j = 0; j < i; j++)
|
|
|
|
usb_free_urb(cam->sbuf[j].urb);
|
2019-08-17 13:27:46 +08:00
|
|
|
for (j = 0; j < NUM_SBUF; j++) {
|
|
|
|
kfree(cam->sbuf[j].data);
|
|
|
|
cam->sbuf[j].data = NULL;
|
|
|
|
}
|
2006-02-27 11:09:05 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
cam->sbuf[i].urb = urb;
|
|
|
|
urb->dev = cam->dev;
|
|
|
|
urb->context = cam;
|
|
|
|
urb->pipe = usb_rcvisocpipe(cam->dev, 1 /*ISOC endpoint*/);
|
|
|
|
urb->transfer_flags = URB_ISO_ASAP;
|
|
|
|
urb->transfer_buffer = cam->sbuf[i].data;
|
|
|
|
urb->complete = cpia2_usb_complete;
|
|
|
|
urb->number_of_packets = FRAMES_PER_DESC;
|
|
|
|
urb->interval = 1;
|
|
|
|
urb->transfer_buffer_length =
|
|
|
|
FRAME_SIZE_PER_DESC * FRAMES_PER_DESC;
|
|
|
|
|
|
|
|
for (fx = 0; fx < FRAMES_PER_DESC; fx++) {
|
|
|
|
urb->iso_frame_desc[fx].offset =
|
|
|
|
FRAME_SIZE_PER_DESC * fx;
|
|
|
|
urb->iso_frame_desc[fx].length = FRAME_SIZE_PER_DESC;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/* Queue the ISO urbs, and resubmit in the completion handler */
|
|
|
|
for(i=0; i<NUM_SBUF; ++i) {
|
|
|
|
err = usb_submit_urb(cam->sbuf[i].urb, GFP_KERNEL);
|
|
|
|
if (err) {
|
|
|
|
ERR("usb_submit_urb[%d]() = %d\n", i, err);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_stream_start
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
int cpia2_usb_stream_start(struct camera_data *cam, unsigned int alternate)
|
|
|
|
{
|
|
|
|
int ret;
|
|
|
|
int old_alt;
|
|
|
|
|
|
|
|
if(cam->streaming)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (cam->flush) {
|
|
|
|
int i;
|
|
|
|
DBG("Flushing buffers\n");
|
|
|
|
for(i=0; i<cam->num_frames; ++i) {
|
|
|
|
cam->buffers[i].status = FRAME_EMPTY;
|
|
|
|
cam->buffers[i].length = 0;
|
|
|
|
}
|
|
|
|
cam->curbuff = &cam->buffers[0];
|
|
|
|
cam->workbuff = cam->curbuff->next;
|
|
|
|
cam->flush = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
old_alt = cam->params.camera_state.stream_mode;
|
|
|
|
cam->params.camera_state.stream_mode = 0;
|
|
|
|
ret = cpia2_usb_change_streaming_alternate(cam, alternate);
|
|
|
|
if (ret < 0) {
|
|
|
|
int ret2;
|
|
|
|
ERR("cpia2_usb_change_streaming_alternate() = %d!\n", ret);
|
|
|
|
cam->params.camera_state.stream_mode = old_alt;
|
|
|
|
ret2 = set_alternate(cam, USBIF_CMDONLY);
|
|
|
|
if (ret2 < 0) {
|
[media] cpia2: don't break long lines
Due to the 80-cols restrictions, and latter due to checkpatch
warnings, several strings were broken into multiple lines. This
is not considered a good practice anymore, as it makes harder
to grep for strings at the source code.
As we're right now fixing other drivers due to KERN_CONT, we need
to be able to identify what printk strings don't end with a "\n".
It is a way easier to detect those if we don't break long lines.
So, join those continuation lines.
The patch was generated via the script below, and manually
adjusted if needed.
</script>
use Text::Tabs;
while (<>) {
if ($next ne "") {
$c=$_;
if ($c =~ /^\s+\"(.*)/) {
$c2=$1;
$next =~ s/\"\n$//;
$n = expand($next);
$funpos = index($n, '(');
$pos = index($c2, '",');
if ($funpos && $pos > 0) {
$s1 = substr $c2, 0, $pos + 2;
$s2 = ' ' x ($funpos + 1) . substr $c2, $pos + 2;
$s2 =~ s/^\s+//;
$s2 = ' ' x ($funpos + 1) . $s2 if ($s2 ne "");
print unexpand("$next$s1\n");
print unexpand("$s2\n") if ($s2 ne "");
} else {
print "$next$c2\n";
}
$next="";
next;
} else {
print $next;
}
$next="";
} else {
if (m/\"$/) {
if (!m/\\n\"$/) {
$next=$_;
next;
}
}
}
print $_;
}
</script>
Signed-off-by: Mauro Carvalho Chehab <mchehab@s-opensource.com>
2016-10-19 03:44:14 +08:00
|
|
|
ERR("cpia2_usb_change_streaming_alternate(%d) =%d has already failed. Then tried to call set_alternate(USBIF_CMDONLY) = %d.\n",
|
2006-02-27 11:09:05 +08:00
|
|
|
alternate, ret, ret2);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
cam->frame_count = 0;
|
|
|
|
cam->streaming = 1;
|
|
|
|
ret = cpia2_usb_stream_resume(cam);
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_stream_pause
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
int cpia2_usb_stream_pause(struct camera_data *cam)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
if(cam->streaming) {
|
|
|
|
free_sbufs(cam);
|
2012-04-29 19:44:44 +08:00
|
|
|
ret = set_alternate(cam, USBIF_CMDONLY);
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_stream_resume
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
int cpia2_usb_stream_resume(struct camera_data *cam)
|
|
|
|
{
|
|
|
|
int ret = 0;
|
|
|
|
if(cam->streaming) {
|
|
|
|
cam->first_image_seen = 0;
|
|
|
|
ret = set_alternate(cam, cam->params.camera_state.stream_mode);
|
|
|
|
if(ret == 0) {
|
2012-04-29 19:44:44 +08:00
|
|
|
/* for some reason the user effects need to be set
|
|
|
|
again when starting streaming. */
|
|
|
|
cpia2_do_command(cam, CPIA2_CMD_SET_USER_EFFECTS, TRANSFER_WRITE,
|
|
|
|
cam->params.vp_params.user_effects);
|
2006-02-27 11:09:05 +08:00
|
|
|
ret = submit_urbs(cam);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_stream_stop
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
int cpia2_usb_stream_stop(struct camera_data *cam)
|
|
|
|
{
|
|
|
|
int ret;
|
2012-04-29 19:44:44 +08:00
|
|
|
|
2006-02-27 11:09:05 +08:00
|
|
|
ret = cpia2_usb_stream_pause(cam);
|
|
|
|
cam->streaming = 0;
|
|
|
|
configure_transfer_mode(cam, 0);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_usb_probe
|
|
|
|
*
|
|
|
|
* Probe and initialize.
|
|
|
|
*****************************************************************************/
|
|
|
|
static int cpia2_usb_probe(struct usb_interface *intf,
|
|
|
|
const struct usb_device_id *id)
|
|
|
|
{
|
|
|
|
struct usb_device *udev = interface_to_usbdev(intf);
|
|
|
|
struct usb_interface_descriptor *interface;
|
|
|
|
struct camera_data *cam;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
/* A multi-config CPiA2 camera? */
|
|
|
|
if (udev->descriptor.bNumConfigurations != 1)
|
|
|
|
return -ENODEV;
|
|
|
|
interface = &intf->cur_altsetting->desc;
|
|
|
|
|
|
|
|
/* If we get to this point, we found a CPiA2 camera */
|
|
|
|
LOG("CPiA2 USB camera found\n");
|
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
cam = cpia2_init_camera_struct(intf);
|
|
|
|
if (cam == NULL)
|
2006-02-27 11:09:05 +08:00
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
cam->dev = udev;
|
|
|
|
cam->iface = interface->bInterfaceNumber;
|
|
|
|
|
|
|
|
ret = set_alternate(cam, USBIF_CMDONLY);
|
|
|
|
if (ret < 0) {
|
|
|
|
ERR("%s: usb_set_interface error (ret = %d)\n", __func__, ret);
|
2021-04-22 03:43:45 +08:00
|
|
|
goto alt_err;
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if((ret = cpia2_init_camera(cam)) < 0) {
|
|
|
|
ERR("%s: failed to initialize cpia2 camera (ret = %d)\n", __func__, ret);
|
2021-04-22 03:43:45 +08:00
|
|
|
goto alt_err;
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
LOG(" CPiA Version: %d.%02d (%d.%d)\n",
|
|
|
|
cam->params.version.firmware_revision_hi,
|
|
|
|
cam->params.version.firmware_revision_lo,
|
|
|
|
cam->params.version.asic_id,
|
|
|
|
cam->params.version.asic_rev);
|
|
|
|
LOG(" CPiA PnP-ID: %04x:%04x:%04x\n",
|
|
|
|
cam->params.pnp_id.vendor,
|
|
|
|
cam->params.pnp_id.product,
|
|
|
|
cam->params.pnp_id.device_revision);
|
|
|
|
LOG(" SensorID: %d.(version %d)\n",
|
|
|
|
cam->params.version.sensor_flags,
|
|
|
|
cam->params.version.sensor_rev);
|
|
|
|
|
|
|
|
usb_set_intfdata(intf, cam);
|
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
ret = cpia2_register_camera(cam);
|
|
|
|
if (ret < 0) {
|
|
|
|
ERR("%s: Failed to register cpia2 camera (ret = %d)\n", __func__, ret);
|
2021-04-22 03:43:45 +08:00
|
|
|
goto alt_err;
|
2012-04-29 19:44:44 +08:00
|
|
|
}
|
|
|
|
|
2006-02-27 11:09:05 +08:00
|
|
|
return 0;
|
2021-04-22 03:43:45 +08:00
|
|
|
|
|
|
|
alt_err:
|
|
|
|
cpia2_deinit_camera_struct(cam, intf);
|
|
|
|
return ret;
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* cpia2_disconnect
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
static void cpia2_usb_disconnect(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct camera_data *cam = usb_get_intfdata(intf);
|
|
|
|
usb_set_intfdata(intf, NULL);
|
|
|
|
|
|
|
|
DBG("Stopping stream\n");
|
|
|
|
cpia2_usb_stream_stop(cam);
|
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
mutex_lock(&cam->v4l2_lock);
|
2006-02-27 11:09:05 +08:00
|
|
|
DBG("Unregistering camera\n");
|
|
|
|
cpia2_unregister_camera(cam);
|
2012-04-29 19:44:44 +08:00
|
|
|
v4l2_device_disconnect(&cam->v4l2_dev);
|
|
|
|
mutex_unlock(&cam->v4l2_lock);
|
2006-02-27 11:09:05 +08:00
|
|
|
|
|
|
|
if(cam->buffers) {
|
|
|
|
DBG("Wakeup waiting processes\n");
|
|
|
|
cam->curbuff->status = FRAME_READY;
|
|
|
|
cam->curbuff->length = 0;
|
2015-10-09 08:35:40 +08:00
|
|
|
wake_up_interruptible(&cam->wq_stream);
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
|
2019-05-09 16:57:09 +08:00
|
|
|
v4l2_device_put(&cam->v4l2_dev);
|
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
LOG("CPiA2 camera disconnected.\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
static int cpia2_usb_suspend(struct usb_interface *intf, pm_message_t message)
|
|
|
|
{
|
|
|
|
struct camera_data *cam = usb_get_intfdata(intf);
|
|
|
|
|
|
|
|
mutex_lock(&cam->v4l2_lock);
|
|
|
|
if (cam->streaming) {
|
|
|
|
cpia2_usb_stream_stop(cam);
|
|
|
|
cam->streaming = 1;
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
2012-04-29 19:44:44 +08:00
|
|
|
mutex_unlock(&cam->v4l2_lock);
|
2006-02-27 11:09:05 +08:00
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
dev_info(&intf->dev, "going into suspend..\n");
|
|
|
|
return 0;
|
2006-02-27 11:09:05 +08:00
|
|
|
}
|
|
|
|
|
2012-04-29 19:44:44 +08:00
|
|
|
/* Resume device - start device. */
|
|
|
|
static int cpia2_usb_resume(struct usb_interface *intf)
|
|
|
|
{
|
|
|
|
struct camera_data *cam = usb_get_intfdata(intf);
|
|
|
|
|
|
|
|
mutex_lock(&cam->v4l2_lock);
|
|
|
|
v4l2_ctrl_handler_setup(&cam->hdl);
|
|
|
|
if (cam->streaming) {
|
|
|
|
cam->streaming = 0;
|
|
|
|
cpia2_usb_stream_start(cam,
|
|
|
|
cam->params.camera_state.stream_mode);
|
|
|
|
}
|
|
|
|
mutex_unlock(&cam->v4l2_lock);
|
|
|
|
|
|
|
|
dev_info(&intf->dev, "coming out of suspend..\n");
|
|
|
|
return 0;
|
|
|
|
}
|
2006-02-27 11:09:05 +08:00
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* usb_cpia2_init
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
int cpia2_usb_init(void)
|
|
|
|
{
|
|
|
|
return usb_register(&cpia2_driver);
|
|
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
*
|
|
|
|
* usb_cpia_cleanup
|
|
|
|
*
|
|
|
|
*****************************************************************************/
|
|
|
|
void cpia2_usb_cleanup(void)
|
|
|
|
{
|
|
|
|
schedule_timeout(2 * HZ);
|
|
|
|
usb_deregister(&cpia2_driver);
|
|
|
|
}
|