add idl4k kernel firmware version 1.13.0.105

This commit is contained in:
Jaroslav Kysela
2015-03-26 17:22:37 +01:00
parent 5194d2792e
commit e9070cdc77
31064 changed files with 12769984 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
config VIDEO_USBVIDEO
tristate
config USB_VICAM
tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)"
depends on VIDEO_V4L1 && EXPERIMENTAL
select VIDEO_USBVIDEO
---help---
Say Y here if you have 3com homeconnect camera (vicam).
To compile this driver as a module, choose M here: the
module will be called vicam.
config USB_IBMCAM
tristate "USB IBM (Xirlink) C-it Camera support"
depends on VIDEO_V4L1
select VIDEO_USBVIDEO
---help---
Say Y here if you want to connect a IBM "C-It" camera, also known as
"Xirlink PC Camera" to your computer's USB port.
To compile this driver as a module, choose M here: the
module will be called ibmcam.
This camera has several configuration options which
can be specified when you load the module. Read
<file:Documentation/video4linux/ibmcam.txt> to learn more.
config USB_KONICAWC
tristate "USB Konica Webcam support"
depends on VIDEO_V4L1
select VIDEO_USBVIDEO
---help---
Say Y here if you want support for webcams based on a Konica
chipset. This is known to work with the Intel YC76 webcam.
To compile this driver as a module, choose M here: the
module will be called konicawc.
config USB_QUICKCAM_MESSENGER
tristate "USB Logitech Quickcam Messenger (DEPRECATED)"
depends on VIDEO_V4L1
select VIDEO_USBVIDEO
---help---
This driver is DEPRECATED please use the gspca stv06xx module
instead.
Say Y or M here to enable support for the USB Logitech Quickcam
Messenger webcam.
To compile this driver as a module, choose M here: the
module will be called quickcam_messenger.

View File

@@ -0,0 +1,5 @@
obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o
obj-$(CONFIG_USB_IBMCAM) += ibmcam.o ultracam.o
obj-$(CONFIG_USB_KONICAWC) += konicawc.o
obj-$(CONFIG_USB_VICAM) += vicam.o
obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += quickcam_messenger.o

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,991 @@
/*
* konicawc.c - konica webcam driver
*
* Author: Simon Evans <spse@secret.org.uk>
*
* Copyright (C) 2002 Simon Evans
*
* Licence: GPL
*
* Driver for USB webcams based on Konica chipset. This
* chipset is used in Intel YC76 camera.
*
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/usb/input.h>
#include "usbvideo.h"
#define MAX_BRIGHTNESS 108
#define MAX_CONTRAST 108
#define MAX_SATURATION 108
#define MAX_SHARPNESS 108
#define MAX_WHITEBAL 372
#define MAX_SPEED 6
#define MAX_CAMERAS 1
#define DRIVER_VERSION "v1.4"
#define DRIVER_DESC "Konica Webcam driver"
enum ctrl_req {
SetWhitebal = 0x01,
SetBrightness = 0x02,
SetSharpness = 0x03,
SetContrast = 0x04,
SetSaturation = 0x05,
};
enum frame_sizes {
SIZE_160X120 = 0,
SIZE_160X136 = 1,
SIZE_176X144 = 2,
SIZE_320X240 = 3,
};
#define MAX_FRAME_SIZE SIZE_320X240
static struct usbvideo *cams;
#ifdef CONFIG_USB_DEBUG
static int debug;
#define DEBUG(n, format, arg...) \
if (n <= debug) { \
printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __func__ , ## arg); \
}
#else
#define DEBUG(n, arg...)
static const int debug;
#endif
/* Some default values for initial camera settings,
can be set by modprobe */
static int size;
static int speed = 6; /* Speed (fps) 0 (slowest) to 6 (fastest) */
static int brightness = MAX_BRIGHTNESS/2;
static int contrast = MAX_CONTRAST/2;
static int saturation = MAX_SATURATION/2;
static int sharpness = MAX_SHARPNESS/2;
static int whitebal = 3*(MAX_WHITEBAL/4);
static const int spd_to_iface[] = { 1, 0, 3, 2, 4, 5, 6 };
/* These FPS speeds are from the windows config box. They are
* indexed on size (0-2) and speed (0-6). Divide by 3 to get the
* real fps.
*/
static const int spd_to_fps[][7] = { { 24, 40, 48, 60, 72, 80, 100 },
{ 24, 40, 48, 60, 72, 80, 100 },
{ 18, 30, 36, 45, 54, 60, 75 },
{ 6, 10, 12, 15, 18, 21, 25 } };
struct cam_size {
u16 width;
u16 height;
u8 cmd;
};
static const struct cam_size camera_sizes[] = { { 160, 120, 0x7 },
{ 160, 136, 0xa },
{ 176, 144, 0x4 },
{ 320, 240, 0x5 } };
struct konicawc {
u8 brightness; /* camera uses 0 - 9, x11 for real value */
u8 contrast; /* as above */
u8 saturation; /* as above */
u8 sharpness; /* as above */
u8 white_bal; /* 0 - 33, x11 for real value */
u8 speed; /* Stored as 0 - 6, used as index in spd_to_* (above) */
u8 size; /* Frame Size */
int height;
int width;
struct urb *sts_urb[USBVIDEO_NUMSBUF];
u8 sts_buf[USBVIDEO_NUMSBUF][FRAMES_PER_DESC];
struct urb *last_data_urb;
int lastframe;
int cur_frame_size; /* number of bytes in current frame size */
int maxline; /* number of lines per frame */
int yplanesz; /* Number of bytes in the Y plane */
unsigned int buttonsts:1;
#ifdef CONFIG_INPUT
struct input_dev *input;
char input_physname[64];
#endif
};
#define konicawc_set_misc(uvd, req, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, req, value, index, NULL, 0)
#define konicawc_get_misc(uvd, req, value, index, buf, sz) konicawc_ctrl_msg(uvd, USB_DIR_IN, req, value, index, buf, sz)
#define konicawc_set_value(uvd, value, index) konicawc_ctrl_msg(uvd, USB_DIR_OUT, 2, value, index, NULL, 0)
static int konicawc_ctrl_msg(struct uvd *uvd, u8 dir, u8 request, u16 value, u16 index, void *buf, int len)
{
int retval = usb_control_msg(uvd->dev,
dir ? usb_rcvctrlpipe(uvd->dev, 0) : usb_sndctrlpipe(uvd->dev, 0),
request, 0x40 | dir, value, index, buf, len, 1000);
return retval < 0 ? retval : 0;
}
static inline void konicawc_camera_on(struct uvd *uvd)
{
DEBUG(0, "camera on");
konicawc_set_misc(uvd, 0x2, 1, 0x0b);
}
static inline void konicawc_camera_off(struct uvd *uvd)
{
DEBUG(0, "camera off");
konicawc_set_misc(uvd, 0x2, 0, 0x0b);
}
static void konicawc_set_camera_size(struct uvd *uvd)
{
struct konicawc *cam = (struct konicawc *)uvd->user_data;
konicawc_set_misc(uvd, 0x2, camera_sizes[cam->size].cmd, 0x08);
cam->width = camera_sizes[cam->size].width;
cam->height = camera_sizes[cam->size].height;
cam->yplanesz = cam->height * cam->width;
cam->cur_frame_size = (cam->yplanesz * 3) / 2;
cam->maxline = cam->yplanesz / 256;
uvd->videosize = VIDEOSIZE(cam->width, cam->height);
}
static int konicawc_setup_on_open(struct uvd *uvd)
{
struct konicawc *cam = (struct konicawc *)uvd->user_data;
DEBUG(1, "setting brightness to %d (%d)", cam->brightness,
cam->brightness * 11);
konicawc_set_value(uvd, cam->brightness, SetBrightness);
DEBUG(1, "setting white balance to %d (%d)", cam->white_bal,
cam->white_bal * 11);
konicawc_set_value(uvd, cam->white_bal, SetWhitebal);
DEBUG(1, "setting contrast to %d (%d)", cam->contrast,
cam->contrast * 11);
konicawc_set_value(uvd, cam->contrast, SetContrast);
DEBUG(1, "setting saturation to %d (%d)", cam->saturation,
cam->saturation * 11);
konicawc_set_value(uvd, cam->saturation, SetSaturation);
DEBUG(1, "setting sharpness to %d (%d)", cam->sharpness,
cam->sharpness * 11);
konicawc_set_value(uvd, cam->sharpness, SetSharpness);
konicawc_set_camera_size(uvd);
cam->lastframe = -2;
cam->buttonsts = 0;
return 0;
}
static void konicawc_adjust_picture(struct uvd *uvd)
{
struct konicawc *cam = (struct konicawc *)uvd->user_data;
konicawc_camera_off(uvd);
DEBUG(1, "new brightness: %d", uvd->vpic.brightness);
uvd->vpic.brightness = (uvd->vpic.brightness > MAX_BRIGHTNESS) ? MAX_BRIGHTNESS : uvd->vpic.brightness;
if(cam->brightness != uvd->vpic.brightness / 11) {
cam->brightness = uvd->vpic.brightness / 11;
DEBUG(1, "setting brightness to %d (%d)", cam->brightness,
cam->brightness * 11);
konicawc_set_value(uvd, cam->brightness, SetBrightness);
}
DEBUG(1, "new contrast: %d", uvd->vpic.contrast);
uvd->vpic.contrast = (uvd->vpic.contrast > MAX_CONTRAST) ? MAX_CONTRAST : uvd->vpic.contrast;
if(cam->contrast != uvd->vpic.contrast / 11) {
cam->contrast = uvd->vpic.contrast / 11;
DEBUG(1, "setting contrast to %d (%d)", cam->contrast,
cam->contrast * 11);
konicawc_set_value(uvd, cam->contrast, SetContrast);
}
konicawc_camera_on(uvd);
}
#ifdef CONFIG_INPUT
static void konicawc_register_input(struct konicawc *cam, struct usb_device *dev)
{
struct input_dev *input_dev;
int error;
usb_make_path(dev, cam->input_physname, sizeof(cam->input_physname));
strncat(cam->input_physname, "/input0", sizeof(cam->input_physname));
cam->input = input_dev = input_allocate_device();
if (!input_dev) {
dev_warn(&dev->dev,
"Not enough memory for camera's input device\n");
return;
}
input_dev->name = "Konicawc snapshot button";
input_dev->phys = cam->input_physname;
usb_to_input_id(dev, &input_dev->id);
input_dev->dev.parent = &dev->dev;
input_dev->evbit[0] = BIT_MASK(EV_KEY);
input_dev->keybit[BIT_WORD(KEY_CAMERA)] = BIT_MASK(KEY_CAMERA);
error = input_register_device(cam->input);
if (error) {
dev_warn(&dev->dev,
"Failed to register camera's input device, err: %d\n",
error);
input_free_device(cam->input);
cam->input = NULL;
}
}
static void konicawc_unregister_input(struct konicawc *cam)
{
if (cam->input) {
input_unregister_device(cam->input);
cam->input = NULL;
}
}
static void konicawc_report_buttonstat(struct konicawc *cam)
{
if (cam->input) {
input_report_key(cam->input, KEY_CAMERA, cam->buttonsts);
input_sync(cam->input);
}
}
#else
static inline void konicawc_register_input(struct konicawc *cam, struct usb_device *dev) { }
static inline void konicawc_unregister_input(struct konicawc *cam) { }
static inline void konicawc_report_buttonstat(struct konicawc *cam) { }
#endif /* CONFIG_INPUT */
static int konicawc_compress_iso(struct uvd *uvd, struct urb *dataurb, struct urb *stsurb)
{
char *cdata;
int i, totlen = 0;
unsigned char *status = stsurb->transfer_buffer;
int keep = 0, discard = 0, bad = 0;
struct konicawc *cam = (struct konicawc *)uvd->user_data;
for (i = 0; i < dataurb->number_of_packets; i++) {
int button = cam->buttonsts;
unsigned char sts;
int n = dataurb->iso_frame_desc[i].actual_length;
int st = dataurb->iso_frame_desc[i].status;
cdata = dataurb->transfer_buffer +
dataurb->iso_frame_desc[i].offset;
/* Detect and ignore errored packets */
if (st < 0) {
DEBUG(1, "Data error: packet=%d. len=%d. status=%d.",
i, n, st);
uvd->stats.iso_err_count++;
continue;
}
/* Detect and ignore empty packets */
if (n <= 0) {
uvd->stats.iso_skip_count++;
continue;
}
/* See what the status data said about the packet */
sts = *(status+stsurb->iso_frame_desc[i].offset);
/* sts: 0x80-0xff: frame start with frame number (ie 0-7f)
* otherwise:
* bit 0 0: keep packet
* 1: drop packet (padding data)
*
* bit 4 0 button not clicked
* 1 button clicked
* button is used to `take a picture' (in software)
*/
if(sts < 0x80) {
button = !!(sts & 0x40);
sts &= ~0x40;
}
/* work out the button status, but don't do
anything with it for now */
if(button != cam->buttonsts) {
DEBUG(2, "button: %sclicked", button ? "" : "un");
cam->buttonsts = button;
konicawc_report_buttonstat(cam);
}
if(sts == 0x01) { /* drop frame */
discard++;
continue;
}
if((sts > 0x01) && (sts < 0x80)) {
dev_info(&uvd->dev->dev, "unknown status %2.2x\n",
sts);
bad++;
continue;
}
if(!sts && cam->lastframe == -2) {
DEBUG(2, "dropping frame looking for image start");
continue;
}
keep++;
if(sts & 0x80) { /* frame start */
unsigned char marker[] = { 0, 0xff, 0, 0x00 };
if(cam->lastframe == -2) {
DEBUG(2, "found initial image");
cam->lastframe = -1;
}
marker[3] = sts & 0x7F;
RingQueue_Enqueue(&uvd->dp, marker, 4);
totlen += 4;
}
totlen += n; /* Little local accounting */
RingQueue_Enqueue(&uvd->dp, cdata, n);
}
DEBUG(8, "finished: keep = %d discard = %d bad = %d added %d bytes",
keep, discard, bad, totlen);
return totlen;
}
static void resubmit_urb(struct uvd *uvd, struct urb *urb)
{
int i, ret;
for (i = 0; i < FRAMES_PER_DESC; i++) {
urb->iso_frame_desc[i].status = 0;
}
urb->dev = uvd->dev;
urb->status = 0;
ret = usb_submit_urb(urb, GFP_ATOMIC);
DEBUG(3, "submitting urb of length %d", urb->transfer_buffer_length);
if(ret)
err("usb_submit_urb error (%d)", ret);
}
static void konicawc_isoc_irq(struct urb *urb)
{
struct uvd *uvd = urb->context;
struct konicawc *cam = (struct konicawc *)uvd->user_data;
/* We don't want to do anything if we are about to be removed! */
if (!CAMERA_IS_OPERATIONAL(uvd))
return;
if (!uvd->streaming) {
DEBUG(1, "Not streaming, but interrupt!");
return;
}
DEBUG(3, "got frame %d len = %d buflen =%d", urb->start_frame, urb->actual_length, urb->transfer_buffer_length);
uvd->stats.urb_count++;
if (urb->transfer_buffer_length > 32) {
cam->last_data_urb = urb;
return;
}
/* Copy the data received into ring queue */
if(cam->last_data_urb) {
int len = 0;
if(urb->start_frame != cam->last_data_urb->start_frame)
err("Lost sync on frames");
else if (!urb->status && !cam->last_data_urb->status)
len = konicawc_compress_iso(uvd, cam->last_data_urb, urb);
resubmit_urb(uvd, cam->last_data_urb);
resubmit_urb(uvd, urb);
cam->last_data_urb = NULL;
uvd->stats.urb_length = len;
uvd->stats.data_count += len;
if(len)
RingQueue_WakeUpInterruptible(&uvd->dp);
return;
}
return;
}
static int konicawc_start_data(struct uvd *uvd)
{
struct usb_device *dev = uvd->dev;
int i, errFlag;
struct konicawc *cam = (struct konicawc *)uvd->user_data;
int pktsz;
struct usb_interface *intf;
struct usb_host_interface *interface = NULL;
intf = usb_ifnum_to_if(dev, uvd->iface);
if (intf)
interface = usb_altnum_to_altsetting(intf,
spd_to_iface[cam->speed]);
if (!interface)
return -ENXIO;
pktsz = le16_to_cpu(interface->endpoint[1].desc.wMaxPacketSize);
DEBUG(1, "pktsz = %d", pktsz);
if (!CAMERA_IS_OPERATIONAL(uvd)) {
err("Camera is not operational");
return -EFAULT;
}
uvd->curframe = -1;
konicawc_camera_on(uvd);
/* Alternate interface 1 is is the biggest frame size */
i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive);
if (i < 0) {
err("usb_set_interface error");
uvd->last_error = i;
return -EBUSY;
}
/* We double buffer the Iso lists */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
int j, k;
struct urb *urb = uvd->sbuf[i].urb;
urb->dev = dev;
urb->context = uvd;
urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp);
urb->interval = 1;
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = uvd->sbuf[i].data;
urb->complete = konicawc_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = pktsz * FRAMES_PER_DESC;
for (j=k=0; j < FRAMES_PER_DESC; j++, k += pktsz) {
urb->iso_frame_desc[j].offset = k;
urb->iso_frame_desc[j].length = pktsz;
}
urb = cam->sts_urb[i];
urb->dev = dev;
urb->context = uvd;
urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp-1);
urb->interval = 1;
urb->transfer_flags = URB_ISO_ASAP;
urb->transfer_buffer = cam->sts_buf[i];
urb->complete = konicawc_isoc_irq;
urb->number_of_packets = FRAMES_PER_DESC;
urb->transfer_buffer_length = FRAMES_PER_DESC;
for (j=0; j < FRAMES_PER_DESC; j++) {
urb->iso_frame_desc[j].offset = j;
urb->iso_frame_desc[j].length = 1;
}
}
cam->last_data_urb = NULL;
/* Submit all URBs */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
errFlag = usb_submit_urb(cam->sts_urb[i], GFP_KERNEL);
if (errFlag)
err("usb_submit_isoc(%d) ret %d", i, errFlag);
errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL);
if (errFlag)
err ("usb_submit_isoc(%d) ret %d", i, errFlag);
}
uvd->streaming = 1;
DEBUG(1, "streaming=1 video_endp=$%02x", uvd->video_endp);
return 0;
}
static void konicawc_stop_data(struct uvd *uvd)
{
int i, j;
struct konicawc *cam;
if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL))
return;
konicawc_camera_off(uvd);
uvd->streaming = 0;
cam = (struct konicawc *)uvd->user_data;
cam->last_data_urb = NULL;
/* Unschedule all of the iso td's */
for (i=0; i < USBVIDEO_NUMSBUF; i++) {
usb_kill_urb(uvd->sbuf[i].urb);
usb_kill_urb(cam->sts_urb[i]);
}
if (!uvd->remove_pending) {
/* Set packet size to 0 */
j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive);
if (j < 0) {
err("usb_set_interface() error %d.", j);
uvd->last_error = j;
}
}
}
static void konicawc_process_isoc(struct uvd *uvd, struct usbvideo_frame *frame)
{
struct konicawc *cam = (struct konicawc *)uvd->user_data;
int maxline = cam->maxline;
int yplanesz = cam->yplanesz;
assert(frame != NULL);
DEBUG(5, "maxline = %d yplanesz = %d", maxline, yplanesz);
DEBUG(3, "Frame state = %d", frame->scanstate);
if(frame->scanstate == ScanState_Scanning) {
int drop = 0;
int curframe;
int fdrops = 0;
DEBUG(3, "Searching for marker, queue len = %d", RingQueue_GetLength(&uvd->dp));
while(RingQueue_GetLength(&uvd->dp) >= 4) {
if ((RING_QUEUE_PEEK(&uvd->dp, 0) == 0x00) &&
(RING_QUEUE_PEEK(&uvd->dp, 1) == 0xff) &&
(RING_QUEUE_PEEK(&uvd->dp, 2) == 0x00) &&
(RING_QUEUE_PEEK(&uvd->dp, 3) < 0x80)) {
curframe = RING_QUEUE_PEEK(&uvd->dp, 3);
if(cam->lastframe >= 0) {
fdrops = (0x80 + curframe - cam->lastframe) & 0x7F;
fdrops--;
if(fdrops) {
dev_info(&uvd->dev->dev,
"Dropped %d frames "
"(%d -> %d)\n",
fdrops,
cam->lastframe,
curframe);
}
}
cam->lastframe = curframe;
frame->curline = 0;
frame->scanstate = ScanState_Lines;
RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 4);
break;
}
RING_QUEUE_DEQUEUE_BYTES(&uvd->dp, 1);
drop++;
}
if(drop)
DEBUG(2, "dropped %d bytes looking for new frame", drop);
}
if(frame->scanstate == ScanState_Scanning)
return;
/* Try to move data from queue into frame buffer
* We get data in blocks of 384 bytes made up of:
* 256 Y, 64 U, 64 V.
* This needs to be written out as a Y plane, a U plane and a V plane.
*/
while ( frame->curline < maxline && (RingQueue_GetLength(&uvd->dp) >= 384)) {
/* Y */
RingQueue_Dequeue(&uvd->dp, frame->data + (frame->curline * 256), 256);
/* U */
RingQueue_Dequeue(&uvd->dp, frame->data + yplanesz + (frame->curline * 64), 64);
/* V */
RingQueue_Dequeue(&uvd->dp, frame->data + (5 * yplanesz)/4 + (frame->curline * 64), 64);
frame->seqRead_Length += 384;
frame->curline++;
}
/* See if we filled the frame */
if (frame->curline == maxline) {
DEBUG(5, "got whole frame");
frame->frameState = FrameState_Done_Hold;
frame->curline = 0;
uvd->curframe = -1;
uvd->stats.frame_num++;
}
}
static int konicawc_find_fps(int size, int fps)
{
int i;
fps *= 3;
DEBUG(1, "konica_find_fps: size = %d fps = %d", size, fps);
if(fps <= spd_to_fps[size][0])
return 0;
if(fps >= spd_to_fps[size][MAX_SPEED])
return MAX_SPEED;
for(i = 0; i < MAX_SPEED; i++) {
if((fps >= spd_to_fps[size][i]) && (fps <= spd_to_fps[size][i+1])) {
DEBUG(2, "fps %d between %d and %d", fps, i, i+1);
if( (fps - spd_to_fps[size][i]) < (spd_to_fps[size][i+1] - fps))
return i;
else
return i+1;
}
}
return MAX_SPEED+1;
}
static int konicawc_set_video_mode(struct uvd *uvd, struct video_window *vw)
{
struct konicawc *cam = (struct konicawc *)uvd->user_data;
int newspeed = cam->speed;
int newsize;
int x = vw->width;
int y = vw->height;
int fps = vw->flags;
if(x > 0 && y > 0) {
DEBUG(2, "trying to find size %d,%d", x, y);
for(newsize = 0; newsize <= MAX_FRAME_SIZE; newsize++) {
if((camera_sizes[newsize].width == x) && (camera_sizes[newsize].height == y))
break;
}
} else {
newsize = cam->size;
}
if(newsize > MAX_FRAME_SIZE) {
DEBUG(1, "couldn't find size %d,%d", x, y);
return -EINVAL;
}
if(fps > 0) {
DEBUG(1, "trying to set fps to %d", fps);
newspeed = konicawc_find_fps(newsize, fps);
DEBUG(1, "find_fps returned %d (%d)", newspeed, spd_to_fps[newsize][newspeed]);
}
if(newspeed > MAX_SPEED)
return -EINVAL;
DEBUG(1, "setting size to %d speed to %d", newsize, newspeed);
if((newsize == cam->size) && (newspeed == cam->speed)) {
DEBUG(1, "Nothing to do");
return 0;
}
DEBUG(0, "setting to %dx%d @ %d fps", camera_sizes[newsize].width,
camera_sizes[newsize].height, spd_to_fps[newsize][newspeed]/3);
konicawc_stop_data(uvd);
uvd->ifaceAltActive = spd_to_iface[newspeed];
DEBUG(1, "new interface = %d", uvd->ifaceAltActive);
cam->speed = newspeed;
if(cam->size != newsize) {
cam->size = newsize;
konicawc_set_camera_size(uvd);
}
/* Flush the input queue and clear any current frame in progress */
RingQueue_Flush(&uvd->dp);
cam->lastframe = -2;
if(uvd->curframe != -1) {
uvd->frame[uvd->curframe].curline = 0;
uvd->frame[uvd->curframe].seqRead_Length = 0;
uvd->frame[uvd->curframe].seqRead_Index = 0;
}
konicawc_start_data(uvd);
return 0;
}
static int konicawc_calculate_fps(struct uvd *uvd)
{
struct konicawc *cam = uvd->user_data;
return spd_to_fps[cam->size][cam->speed]/3;
}
static void konicawc_configure_video(struct uvd *uvd)
{
struct konicawc *cam = (struct konicawc *)uvd->user_data;
u8 buf[2];
memset(&uvd->vpic, 0, sizeof(uvd->vpic));
memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
RESTRICT_TO_RANGE(brightness, 0, MAX_BRIGHTNESS);
RESTRICT_TO_RANGE(contrast, 0, MAX_CONTRAST);
RESTRICT_TO_RANGE(saturation, 0, MAX_SATURATION);
RESTRICT_TO_RANGE(sharpness, 0, MAX_SHARPNESS);
RESTRICT_TO_RANGE(whitebal, 0, MAX_WHITEBAL);
cam->brightness = brightness / 11;
cam->contrast = contrast / 11;
cam->saturation = saturation / 11;
cam->sharpness = sharpness / 11;
cam->white_bal = whitebal / 11;
uvd->vpic.colour = 108;
uvd->vpic.hue = 108;
uvd->vpic.brightness = brightness;
uvd->vpic.contrast = contrast;
uvd->vpic.whiteness = whitebal;
uvd->vpic.depth = 6;
uvd->vpic.palette = VIDEO_PALETTE_YUV420P;
memset(&uvd->vcap, 0, sizeof(uvd->vcap));
strcpy(uvd->vcap.name, "Konica Webcam");
uvd->vcap.type = VID_TYPE_CAPTURE;
uvd->vcap.channels = 1;
uvd->vcap.audios = 0;
uvd->vcap.minwidth = camera_sizes[SIZE_160X120].width;
uvd->vcap.minheight = camera_sizes[SIZE_160X120].height;
uvd->vcap.maxwidth = camera_sizes[SIZE_320X240].width;
uvd->vcap.maxheight = camera_sizes[SIZE_320X240].height;
memset(&uvd->vchan, 0, sizeof(uvd->vchan));
uvd->vchan.flags = 0 ;
uvd->vchan.tuners = 0;
uvd->vchan.channel = 0;
uvd->vchan.type = VIDEO_TYPE_CAMERA;
strcpy(uvd->vchan.name, "Camera");
/* Talk to device */
DEBUG(1, "device init");
if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2))
DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]);
if(!konicawc_get_misc(uvd, 0x3, 0, 0x10, buf, 2))
DEBUG(2, "3,10 -> %2.2x %2.2x", buf[0], buf[1]);
if(konicawc_set_misc(uvd, 0x2, 0, 0xd))
DEBUG(2, "2,0,d failed");
DEBUG(1, "setting initial values");
}
static int konicawc_probe(struct usb_interface *intf, const struct usb_device_id *devid)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct uvd *uvd = NULL;
int ix, i, nas;
int actInterface=-1, inactInterface=-1, maxPS=0;
unsigned char video_ep = 0;
DEBUG(1, "konicawc_probe(%p)", intf);
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
return -ENODEV;
dev_info(&intf->dev, "Konica Webcam (rev. 0x%04x)\n",
le16_to_cpu(dev->descriptor.bcdDevice));
RESTRICT_TO_RANGE(speed, 0, MAX_SPEED);
/* Validate found interface: must have one ISO endpoint */
nas = intf->num_altsetting;
if (nas != 8) {
err("Incorrect number of alternate settings (%d) for this camera!", nas);
return -ENODEV;
}
/* Validate all alternate settings */
for (ix=0; ix < nas; ix++) {
const struct usb_host_interface *interface;
const struct usb_endpoint_descriptor *endpoint;
interface = &intf->altsetting[ix];
i = interface->desc.bAlternateSetting;
if (interface->desc.bNumEndpoints != 2) {
err("Interface %d. has %u. endpoints!",
interface->desc.bInterfaceNumber,
(unsigned)(interface->desc.bNumEndpoints));
return -ENODEV;
}
endpoint = &interface->endpoint[1].desc;
DEBUG(1, "found endpoint: addr: 0x%2.2x maxps = 0x%4.4x",
endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize));
if (video_ep == 0)
video_ep = endpoint->bEndpointAddress;
else if (video_ep != endpoint->bEndpointAddress) {
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
if (!usb_endpoint_xfer_isoc(endpoint)) {
err("Interface %d. has non-ISO endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) {
if (inactInterface < 0)
inactInterface = i;
else {
err("More than one inactive alt. setting!");
return -ENODEV;
}
} else {
if (i == spd_to_iface[speed]) {
/* This one is the requested one */
actInterface = i;
}
}
if (le16_to_cpu(endpoint->wMaxPacketSize) > maxPS)
maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
}
if(actInterface == -1) {
err("Cant find required endpoint");
return -ENODEV;
}
DEBUG(1, "Selecting requested active setting=%d. maxPS=%d.", actInterface, maxPS);
uvd = usbvideo_AllocateDevice(cams);
if (uvd != NULL) {
struct konicawc *cam = (struct konicawc *)(uvd->user_data);
/* Here uvd is a fully allocated uvd object */
for(i = 0; i < USBVIDEO_NUMSBUF; i++) {
cam->sts_urb[i] = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL);
if(cam->sts_urb[i] == NULL) {
while(i--) {
usb_free_urb(cam->sts_urb[i]);
}
err("can't allocate urbs");
return -ENOMEM;
}
}
cam->speed = speed;
RESTRICT_TO_RANGE(size, SIZE_160X120, SIZE_320X240);
cam->width = camera_sizes[size].width;
cam->height = camera_sizes[size].height;
cam->size = size;
uvd->flags = 0;
uvd->debug = debug;
uvd->dev = dev;
uvd->iface = intf->altsetting->desc.bInterfaceNumber;
uvd->ifaceAltInactive = inactInterface;
uvd->ifaceAltActive = actInterface;
uvd->video_endp = video_ep;
uvd->iso_packet_len = maxPS;
uvd->paletteBits = 1L << VIDEO_PALETTE_YUV420P;
uvd->defaultPalette = VIDEO_PALETTE_YUV420P;
uvd->canvas = VIDEOSIZE(320, 240);
uvd->videosize = VIDEOSIZE(cam->width, cam->height);
/* Initialize konicawc specific data */
konicawc_configure_video(uvd);
i = usbvideo_RegisterVideoDevice(uvd);
uvd->max_frame_size = (320 * 240 * 3)/2;
if (i != 0) {
err("usbvideo_RegisterVideoDevice() failed.");
uvd = NULL;
}
konicawc_register_input(cam, dev);
}
if (uvd) {
usb_set_intfdata (intf, uvd);
return 0;
}
return -EIO;
}
static void konicawc_free_uvd(struct uvd *uvd)
{
int i;
struct konicawc *cam = (struct konicawc *)uvd->user_data;
konicawc_unregister_input(cam);
for (i = 0; i < USBVIDEO_NUMSBUF; i++) {
usb_free_urb(cam->sts_urb[i]);
cam->sts_urb[i] = NULL;
}
}
static struct usb_device_id id_table[] = {
{ USB_DEVICE(0x04c8, 0x0720) }, /* Intel YC 76 */
{ } /* Terminating entry */
};
static int __init konicawc_init(void)
{
struct usbvideo_cb cbTbl;
printk(KERN_INFO KBUILD_MODNAME ": " DRIVER_VERSION ":"
DRIVER_DESC "\n");
memset(&cbTbl, 0, sizeof(cbTbl));
cbTbl.probe = konicawc_probe;
cbTbl.setupOnOpen = konicawc_setup_on_open;
cbTbl.processData = konicawc_process_isoc;
cbTbl.getFPS = konicawc_calculate_fps;
cbTbl.setVideoMode = konicawc_set_video_mode;
cbTbl.startDataPump = konicawc_start_data;
cbTbl.stopDataPump = konicawc_stop_data;
cbTbl.adjustPicture = konicawc_adjust_picture;
cbTbl.userFree = konicawc_free_uvd;
return usbvideo_register(
&cams,
MAX_CAMERAS,
sizeof(struct konicawc),
"konicawc",
&cbTbl,
THIS_MODULE,
id_table);
}
static void __exit konicawc_cleanup(void)
{
usbvideo_Deregister(&cams);
}
MODULE_DEVICE_TABLE(usb, id_table);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
MODULE_DESCRIPTION(DRIVER_DESC);
module_param(speed, int, 0);
MODULE_PARM_DESC(speed, "Initial speed: 0 (slowest) - 6 (fastest)");
module_param(size, int, 0);
MODULE_PARM_DESC(size, "Initial Size 0: 160x120 1: 160x136 2: 176x144 3: 320x240");
module_param(brightness, int, 0);
MODULE_PARM_DESC(brightness, "Initial brightness 0 - 108");
module_param(contrast, int, 0);
MODULE_PARM_DESC(contrast, "Initial contrast 0 - 108");
module_param(saturation, int, 0);
MODULE_PARM_DESC(saturation, "Initial saturation 0 - 108");
module_param(sharpness, int, 0);
MODULE_PARM_DESC(sharpness, "Initial brightness 0 - 108");
module_param(whitebal, int, 0);
MODULE_PARM_DESC(whitebal, "Initial white balance 0 - 363");
#ifdef CONFIG_USB_DEBUG
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
#endif
module_init(konicawc_init);
module_exit(konicawc_cleanup);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
#ifndef quickcam_messenger_h
#define quickcam_messenger_h
#ifndef CONFIG_INPUT
/* if we're not using input we dummy out these functions */
#define qcm_register_input(...)
#define qcm_unregister_input(...)
#define qcm_report_buttonstat(...)
#define qcm_setup_input_int(...) 0
#define qcm_stop_int_data(...)
#define qcm_alloc_int_urb(...) 0
#define qcm_free_int(...)
#endif
#define CHECK_RET(ret, expr) \
if ((ret = expr) < 0) return ret
/* Control Registers for the STVV6422 ASIC
* - this define is taken from the qc-usb-messenger code
*/
#define STV_ISO_ENABLE 0x1440
#define ISOC_PACKET_SIZE 1023
/* Chip identification number including revision indicator */
#define CMOS_SENSOR_IDREV 0xE00A
struct rgb {
u8 b;
u8 g;
u8 r;
u8 b2;
u8 g2;
u8 r2;
};
struct bayL0 {
u8 g;
u8 r;
};
struct bayL1 {
u8 b;
u8 g;
};
struct cam_size {
u16 width;
u16 height;
u8 cmd;
};
static const struct cam_size camera_sizes[] = {
{ 160, 120, 0xf },
{ 320, 240, 0x2 },
};
enum frame_sizes {
SIZE_160X120 = 0,
SIZE_320X240 = 1,
};
#define MAX_FRAME_SIZE SIZE_320X240
struct qcm {
u16 colour;
u16 hue;
u16 brightness;
u16 contrast;
u16 whiteness;
u8 size;
int height;
int width;
u8 *scratch;
struct urb *button_urb;
u8 button_sts;
u8 button_sts_buf;
#ifdef CONFIG_INPUT
struct input_dev *input;
char input_physname[64];
#endif
};
struct regval {
u16 reg;
u8 val;
};
/* this table is derived from the
qc-usb-messenger code */
static const struct regval regval_table[] = {
{ STV_ISO_ENABLE, 0x00 },
{ 0x1436, 0x00 }, { 0x1432, 0x03 },
{ 0x143a, 0xF9 }, { 0x0509, 0x38 },
{ 0x050a, 0x38 }, { 0x050b, 0x38 },
{ 0x050c, 0x2A }, { 0x050d, 0x01 },
{ 0x1431, 0x00 }, { 0x1433, 0x34 },
{ 0x1438, 0x18 }, { 0x1439, 0x00 },
{ 0x143b, 0x05 }, { 0x143c, 0x00 },
{ 0x143e, 0x01 }, { 0x143d, 0x00 },
{ 0x1442, 0xe2 }, { 0x1500, 0xd0 },
{ 0x1500, 0xd0 }, { 0x1500, 0x50 },
{ 0x1501, 0xaf }, { 0x1502, 0xc2 },
{ 0x1503, 0x45 }, { 0x1505, 0x02 },
{ 0x150e, 0x8e }, { 0x150f, 0x37 },
{ 0x15c0, 0x00 },
};
static const unsigned char marker[] = { 0x00, 0xff, 0x00, 0xFF };
#endif /* quickcam_messenger_h */

View File

@@ -0,0 +1,685 @@
/*
* USB NB Camera driver
*
* HISTORY:
* 25-Dec-2002 Dmitri Removed lighting, sharpness parameters, methods.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include "usbvideo.h"
#define ULTRACAM_VENDOR_ID 0x0461
#define ULTRACAM_PRODUCT_ID 0x0813
#define MAX_CAMERAS 4 /* How many devices we allow to connect */
/*
* This structure lives in uvd_t->user field.
*/
typedef struct {
int initialized; /* Had we already sent init sequence? */
int camera_model; /* What type of IBM camera we got? */
int has_hdr;
} ultracam_t;
#define ULTRACAM_T(uvd) ((ultracam_t *)((uvd)->user_data))
static struct usbvideo *cams = NULL;
static int debug;
static int flags; /* FLAGS_DISPLAY_HINTS | FLAGS_OVERLAY_STATS; */
static const int min_canvasWidth = 8;
static const int min_canvasHeight = 4;
#define FRAMERATE_MIN 0
#define FRAMERATE_MAX 6
static int framerate = -1;
/*
* Here we define several initialization variables. They may
* be used to automatically set color, hue, brightness and
* contrast to desired values. This is particularly useful in
* case of webcams (which have no controls and no on-screen
* output) and also when a client V4L software is used that
* does not have some of those controls. In any case it's
* good to have startup values as options.
*
* These values are all in [0..255] range. This simplifies
* operation. Note that actual values of V4L variables may
* be scaled up (as much as << 8). User can see that only
* on overlay output, however, or through a V4L client.
*/
static int init_brightness = 128;
static int init_contrast = 192;
static int init_color = 128;
static int init_hue = 128;
static int hue_correction = 128;
module_param(debug, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(debug, "Debug level: 0-9 (default=0)");
module_param(flags, int, 0);
MODULE_PARM_DESC(flags,
"Bitfield: 0=VIDIOCSYNC, "
"1=B/W, "
"2=show hints, "
"3=show stats, "
"4=test pattern, "
"5=separate frames, "
"6=clean frames");
module_param(framerate, int, 0);
MODULE_PARM_DESC(framerate, "Framerate setting: 0=slowest, 6=fastest (default=2)");
module_param(init_brightness, int, 0);
MODULE_PARM_DESC(init_brightness, "Brightness preconfiguration: 0-255 (default=128)");
module_param(init_contrast, int, 0);
MODULE_PARM_DESC(init_contrast, "Contrast preconfiguration: 0-255 (default=192)");
module_param(init_color, int, 0);
MODULE_PARM_DESC(init_color, "Color preconfiguration: 0-255 (default=128)");
module_param(init_hue, int, 0);
MODULE_PARM_DESC(init_hue, "Hue preconfiguration: 0-255 (default=128)");
module_param(hue_correction, int, 0);
MODULE_PARM_DESC(hue_correction, "YUV colorspace regulation: 0-255 (default=128)");
/*
* ultracam_ProcessIsocData()
*
* Generic routine to parse the ring queue data. It employs either
* ultracam_find_header() or ultracam_parse_lines() to do most
* of work.
*
* 02-Nov-2000 First (mostly dummy) version.
* 06-Nov-2000 Rewrote to dump all data into frame.
*/
static void ultracam_ProcessIsocData(struct uvd *uvd, struct usbvideo_frame *frame)
{
int n;
assert(uvd != NULL);
assert(frame != NULL);
/* Try to move data from queue into frame buffer */
n = RingQueue_GetLength(&uvd->dp);
if (n > 0) {
int m;
/* See how much spare we have left */
m = uvd->max_frame_size - frame->seqRead_Length;
if (n > m)
n = m;
/* Now move that much data into frame buffer */
RingQueue_Dequeue(
&uvd->dp,
frame->data + frame->seqRead_Length,
m);
frame->seqRead_Length += m;
}
/* See if we filled the frame */
if (frame->seqRead_Length >= uvd->max_frame_size) {
frame->frameState = FrameState_Done;
uvd->curframe = -1;
uvd->stats.frame_num++;
}
}
/*
* ultracam_veio()
*
* History:
* 1/27/00 Added check for dev == NULL; this happens if camera is unplugged.
*/
static int ultracam_veio(
struct uvd *uvd,
unsigned char req,
unsigned short value,
unsigned short index,
int is_out)
{
static const char proc[] = "ultracam_veio";
unsigned char cp[8] /* = { 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef } */;
int i;
if (!CAMERA_IS_OPERATIONAL(uvd))
return 0;
if (!is_out) {
i = usb_control_msg(
uvd->dev,
usb_rcvctrlpipe(uvd->dev, 0),
req,
USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value,
index,
cp,
sizeof(cp),
1000);
#if 1
dev_info(&uvd->dev->dev,
"USB => %02x%02x%02x%02x%02x%02x%02x%02x "
"(req=$%02x val=$%04x ind=$%04x)\n",
cp[0],cp[1],cp[2],cp[3],cp[4],cp[5],cp[6],cp[7],
req, value, index);
#endif
} else {
i = usb_control_msg(
uvd->dev,
usb_sndctrlpipe(uvd->dev, 0),
req,
USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
value,
index,
NULL,
0,
1000);
}
if (i < 0) {
err("%s: ERROR=%d. Camera stopped; Reconnect or reload driver.",
proc, i);
uvd->last_error = i;
}
return i;
}
/*
* ultracam_calculate_fps()
*/
static int ultracam_calculate_fps(struct uvd *uvd)
{
return 3 + framerate*4 + framerate/2;
}
/*
* ultracam_adjust_contrast()
*/
static void ultracam_adjust_contrast(struct uvd *uvd)
{
}
/*
* ultracam_set_brightness()
*
* This procedure changes brightness of the picture.
*/
static void ultracam_set_brightness(struct uvd *uvd)
{
}
static void ultracam_set_hue(struct uvd *uvd)
{
}
/*
* ultracam_adjust_picture()
*
* This procedure gets called from V4L interface to update picture settings.
* Here we change brightness and contrast.
*/
static void ultracam_adjust_picture(struct uvd *uvd)
{
ultracam_adjust_contrast(uvd);
ultracam_set_brightness(uvd);
ultracam_set_hue(uvd);
}
/*
* ultracam_video_stop()
*
* This code tells camera to stop streaming. The interface remains
* configured and bandwidth - claimed.
*/
static void ultracam_video_stop(struct uvd *uvd)
{
}
/*
* ultracam_reinit_iso()
*
* This procedure sends couple of commands to the camera and then
* resets the video pipe. This sequence was observed to reinit the
* camera or, at least, to initiate ISO data stream.
*/
static void ultracam_reinit_iso(struct uvd *uvd, int do_stop)
{
}
static void ultracam_video_start(struct uvd *uvd)
{
ultracam_reinit_iso(uvd, 0);
}
static int ultracam_resetPipe(struct uvd *uvd)
{
usb_clear_halt(uvd->dev, uvd->video_endp);
return 0;
}
static int ultracam_alternateSetting(struct uvd *uvd, int setting)
{
static const char proc[] = "ultracam_alternateSetting";
int i;
i = usb_set_interface(uvd->dev, uvd->iface, setting);
if (i < 0) {
err("%s: usb_set_interface error", proc);
uvd->last_error = i;
return -EBUSY;
}
return 0;
}
/*
* Return negative code on failure, 0 on success.
*/
static int ultracam_setup_on_open(struct uvd *uvd)
{
int setup_ok = 0; /* Success by default */
/* Send init sequence only once, it's large! */
if (!ULTRACAM_T(uvd)->initialized) {
ultracam_alternateSetting(uvd, 0x04);
ultracam_alternateSetting(uvd, 0x00);
ultracam_veio(uvd, 0x02, 0x0004, 0x000b, 1);
ultracam_veio(uvd, 0x02, 0x0001, 0x0005, 1);
ultracam_veio(uvd, 0x02, 0x8000, 0x0000, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1);
ultracam_veio(uvd, 0x00, 0x00b0, 0x0001, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x0002, 1);
ultracam_veio(uvd, 0x00, 0x000c, 0x0003, 1);
ultracam_veio(uvd, 0x00, 0x000b, 0x0004, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x0005, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x0006, 1);
ultracam_veio(uvd, 0x00, 0x0079, 0x0007, 1);
ultracam_veio(uvd, 0x00, 0x003b, 0x0008, 1);
ultracam_veio(uvd, 0x00, 0x0002, 0x000f, 1);
ultracam_veio(uvd, 0x00, 0x0001, 0x0010, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x0011, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00bf, 1);
ultracam_veio(uvd, 0x00, 0x0001, 0x00c0, 1);
ultracam_veio(uvd, 0x00, 0x0010, 0x00cb, 1);
ultracam_veio(uvd, 0x01, 0x00a4, 0x0001, 1);
ultracam_veio(uvd, 0x01, 0x0010, 0x0002, 1);
ultracam_veio(uvd, 0x01, 0x0066, 0x0007, 1);
ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1);
ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1);
ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1);
ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1);
ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1);
ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1);
ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1);
ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1);
ultracam_veio(uvd, 0x01, 0x000b, 0x0011, 1);
ultracam_veio(uvd, 0x01, 0x0001, 0x0012, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x0013, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x0014, 1);
ultracam_veio(uvd, 0x01, 0x0087, 0x0051, 1);
ultracam_veio(uvd, 0x01, 0x0040, 0x0052, 1);
ultracam_veio(uvd, 0x01, 0x0058, 0x0053, 1);
ultracam_veio(uvd, 0x01, 0x0040, 0x0054, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x0040, 1);
ultracam_veio(uvd, 0x01, 0x0010, 0x0041, 1);
ultracam_veio(uvd, 0x01, 0x0020, 0x0042, 1);
ultracam_veio(uvd, 0x01, 0x0030, 0x0043, 1);
ultracam_veio(uvd, 0x01, 0x0040, 0x0044, 1);
ultracam_veio(uvd, 0x01, 0x0050, 0x0045, 1);
ultracam_veio(uvd, 0x01, 0x0060, 0x0046, 1);
ultracam_veio(uvd, 0x01, 0x0070, 0x0047, 1);
ultracam_veio(uvd, 0x01, 0x0080, 0x0048, 1);
ultracam_veio(uvd, 0x01, 0x0090, 0x0049, 1);
ultracam_veio(uvd, 0x01, 0x00a0, 0x004a, 1);
ultracam_veio(uvd, 0x01, 0x00b0, 0x004b, 1);
ultracam_veio(uvd, 0x01, 0x00c0, 0x004c, 1);
ultracam_veio(uvd, 0x01, 0x00d0, 0x004d, 1);
ultracam_veio(uvd, 0x01, 0x00e0, 0x004e, 1);
ultracam_veio(uvd, 0x01, 0x00f0, 0x004f, 1);
ultracam_veio(uvd, 0x01, 0x00ff, 0x0050, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x0056, 1);
ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0080, 0x00c1, 1);
ultracam_veio(uvd, 0x00, 0x0004, 0x00c2, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0002, 0x00c1, 1);
ultracam_veio(uvd, 0x00, 0x0020, 0x00c2, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0040, 0x00c1, 1);
ultracam_veio(uvd, 0x00, 0x0017, 0x00c2, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c3, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c4, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c5, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c6, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c7, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c8, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c9, 1);
ultracam_veio(uvd, 0x00, 0x00c0, 0x00c1, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00c2, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
ultracam_veio(uvd, 0x02, 0xc040, 0x0001, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x0008, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x0009, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x000b, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x000c, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x000d, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x000e, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x000f, 0);
ultracam_veio(uvd, 0x01, 0x0000, 0x0010, 0);
ultracam_veio(uvd, 0x01, 0x000b, 0x0008, 1);
ultracam_veio(uvd, 0x01, 0x0034, 0x0009, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x000a, 1);
ultracam_veio(uvd, 0x01, 0x002e, 0x000b, 1);
ultracam_veio(uvd, 0x01, 0x00d6, 0x000c, 1);
ultracam_veio(uvd, 0x01, 0x00fc, 0x000d, 1);
ultracam_veio(uvd, 0x01, 0x00f1, 0x000e, 1);
ultracam_veio(uvd, 0x01, 0x00da, 0x000f, 1);
ultracam_veio(uvd, 0x01, 0x0036, 0x0010, 1);
ultracam_veio(uvd, 0x01, 0x0000, 0x0001, 0);
ultracam_veio(uvd, 0x01, 0x0064, 0x0001, 1);
ultracam_veio(uvd, 0x01, 0x0059, 0x0051, 1);
ultracam_veio(uvd, 0x01, 0x003f, 0x0052, 1);
ultracam_veio(uvd, 0x01, 0x0094, 0x0053, 1);
ultracam_veio(uvd, 0x01, 0x00ff, 0x0011, 1);
ultracam_veio(uvd, 0x01, 0x0003, 0x0012, 1);
ultracam_veio(uvd, 0x01, 0x00f7, 0x0013, 1);
ultracam_veio(uvd, 0x00, 0x0009, 0x0011, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x0001, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x0000, 1);
ultracam_veio(uvd, 0x00, 0x0020, 0x00c1, 1);
ultracam_veio(uvd, 0x00, 0x0010, 0x00c2, 1);
ultracam_veio(uvd, 0x00, 0x0000, 0x00ca, 1);
ultracam_alternateSetting(uvd, 0x04);
ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1);
ultracam_veio(uvd, 0x02, 0x0000, 0x0001, 1);
ultracam_veio(uvd, 0x02, 0x0000, 0x0006, 1);
ultracam_veio(uvd, 0x02, 0x9000, 0x0007, 1);
ultracam_veio(uvd, 0x02, 0x0042, 0x0001, 1);
ultracam_veio(uvd, 0x02, 0x0000, 0x000b, 0);
ultracam_resetPipe(uvd);
ULTRACAM_T(uvd)->initialized = (setup_ok != 0);
}
return setup_ok;
}
static void ultracam_configure_video(struct uvd *uvd)
{
if (uvd == NULL)
return;
RESTRICT_TO_RANGE(init_brightness, 0, 255);
RESTRICT_TO_RANGE(init_contrast, 0, 255);
RESTRICT_TO_RANGE(init_color, 0, 255);
RESTRICT_TO_RANGE(init_hue, 0, 255);
RESTRICT_TO_RANGE(hue_correction, 0, 255);
memset(&uvd->vpic, 0, sizeof(uvd->vpic));
memset(&uvd->vpic_old, 0x55, sizeof(uvd->vpic_old));
uvd->vpic.colour = init_color << 8;
uvd->vpic.hue = init_hue << 8;
uvd->vpic.brightness = init_brightness << 8;
uvd->vpic.contrast = init_contrast << 8;
uvd->vpic.whiteness = 105 << 8; /* This one isn't used */
uvd->vpic.depth = 24;
uvd->vpic.palette = VIDEO_PALETTE_RGB24;
memset(&uvd->vcap, 0, sizeof(uvd->vcap));
strcpy(uvd->vcap.name, "IBM Ultra Camera");
uvd->vcap.type = VID_TYPE_CAPTURE;
uvd->vcap.channels = 1;
uvd->vcap.audios = 0;
uvd->vcap.maxwidth = VIDEOSIZE_X(uvd->canvas);
uvd->vcap.maxheight = VIDEOSIZE_Y(uvd->canvas);
uvd->vcap.minwidth = min_canvasWidth;
uvd->vcap.minheight = min_canvasHeight;
memset(&uvd->vchan, 0, sizeof(uvd->vchan));
uvd->vchan.flags = 0;
uvd->vchan.tuners = 0;
uvd->vchan.channel = 0;
uvd->vchan.type = VIDEO_TYPE_CAMERA;
strcpy(uvd->vchan.name, "Camera");
}
/*
* ultracam_probe()
*
* This procedure queries device descriptor and accepts the interface
* if it looks like our camera.
*
* History:
* 12-Nov-2000 Reworked to comply with new probe() signature.
* 23-Jan-2001 Added compatibility with 2.2.x kernels.
*/
static int ultracam_probe(struct usb_interface *intf, const struct usb_device_id *devid)
{
struct usb_device *dev = interface_to_usbdev(intf);
struct uvd *uvd = NULL;
int ix, i, nas;
int actInterface=-1, inactInterface=-1, maxPS=0;
unsigned char video_ep = 0;
if (debug >= 1)
dev_info(&intf->dev, "ultracam_probe\n");
/* We don't handle multi-config cameras */
if (dev->descriptor.bNumConfigurations != 1)
return -ENODEV;
dev_info(&intf->dev, "IBM Ultra camera found (rev. 0x%04x)\n",
le16_to_cpu(dev->descriptor.bcdDevice));
/* Validate found interface: must have one ISO endpoint */
nas = intf->num_altsetting;
if (debug > 0)
dev_info(&intf->dev, "Number of alternate settings=%d.\n",
nas);
if (nas < 8) {
err("Too few alternate settings for this camera!");
return -ENODEV;
}
/* Validate all alternate settings */
for (ix=0; ix < nas; ix++) {
const struct usb_host_interface *interface;
const struct usb_endpoint_descriptor *endpoint;
interface = &intf->altsetting[ix];
i = interface->desc.bAlternateSetting;
if (interface->desc.bNumEndpoints != 1) {
err("Interface %d. has %u. endpoints!",
interface->desc.bInterfaceNumber,
(unsigned)(interface->desc.bNumEndpoints));
return -ENODEV;
}
endpoint = &interface->endpoint[0].desc;
if (video_ep == 0)
video_ep = endpoint->bEndpointAddress;
else if (video_ep != endpoint->bEndpointAddress) {
err("Alternate settings have different endpoint addresses!");
return -ENODEV;
}
if (!usb_endpoint_xfer_isoc(endpoint)) {
err("Interface %d. has non-ISO endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
if (usb_endpoint_dir_out(endpoint)) {
err("Interface %d. has ISO OUT endpoint!",
interface->desc.bInterfaceNumber);
return -ENODEV;
}
if (le16_to_cpu(endpoint->wMaxPacketSize) == 0) {
if (inactInterface < 0)
inactInterface = i;
else {
err("More than one inactive alt. setting!");
return -ENODEV;
}
} else {
if (actInterface < 0) {
actInterface = i;
maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
if (debug > 0)
dev_info(&intf->dev,
"Active setting=%d. "
"maxPS=%d.\n", i, maxPS);
} else {
/* Got another active alt. setting */
if (maxPS < le16_to_cpu(endpoint->wMaxPacketSize)) {
/* This one is better! */
actInterface = i;
maxPS = le16_to_cpu(endpoint->wMaxPacketSize);
if (debug > 0) {
dev_info(&intf->dev,
"Even better ctive "
"setting=%d. "
"maxPS=%d.\n",
i, maxPS);
}
}
}
}
}
if ((maxPS <= 0) || (actInterface < 0) || (inactInterface < 0)) {
err("Failed to recognize the camera!");
return -ENODEV;
}
uvd = usbvideo_AllocateDevice(cams);
if (uvd != NULL) {
/* Here uvd is a fully allocated uvd object */
uvd->flags = flags;
uvd->debug = debug;
uvd->dev = dev;
uvd->iface = intf->altsetting->desc.bInterfaceNumber;
uvd->ifaceAltInactive = inactInterface;
uvd->ifaceAltActive = actInterface;
uvd->video_endp = video_ep;
uvd->iso_packet_len = maxPS;
uvd->paletteBits = 1L << VIDEO_PALETTE_RGB24;
uvd->defaultPalette = VIDEO_PALETTE_RGB24;
uvd->canvas = VIDEOSIZE(640, 480); /* FIXME */
uvd->videosize = uvd->canvas; /* ultracam_size_to_videosize(size);*/
/* Initialize ibmcam-specific data */
assert(ULTRACAM_T(uvd) != NULL);
ULTRACAM_T(uvd)->camera_model = 0; /* Not used yet */
ULTRACAM_T(uvd)->initialized = 0;
ultracam_configure_video(uvd);
i = usbvideo_RegisterVideoDevice(uvd);
if (i != 0) {
err("usbvideo_RegisterVideoDevice() failed.");
uvd = NULL;
}
}
if (uvd) {
usb_set_intfdata (intf, uvd);
return 0;
}
return -EIO;
}
static struct usb_device_id id_table[] = {
{ USB_DEVICE(ULTRACAM_VENDOR_ID, ULTRACAM_PRODUCT_ID) },
{ } /* Terminating entry */
};
/*
* ultracam_init()
*
* This code is run to initialize the driver.
*/
static int __init ultracam_init(void)
{
struct usbvideo_cb cbTbl;
memset(&cbTbl, 0, sizeof(cbTbl));
cbTbl.probe = ultracam_probe;
cbTbl.setupOnOpen = ultracam_setup_on_open;
cbTbl.videoStart = ultracam_video_start;
cbTbl.videoStop = ultracam_video_stop;
cbTbl.processData = ultracam_ProcessIsocData;
cbTbl.postProcess = usbvideo_DeinterlaceFrame;
cbTbl.adjustPicture = ultracam_adjust_picture;
cbTbl.getFPS = ultracam_calculate_fps;
return usbvideo_register(
&cams,
MAX_CAMERAS,
sizeof(ultracam_t),
"ultracam",
&cbTbl,
THIS_MODULE,
id_table);
}
static void __exit ultracam_cleanup(void)
{
usbvideo_Deregister(&cams);
}
MODULE_DEVICE_TABLE(usb, id_table);
MODULE_LICENSE("GPL");
module_init(ultracam_init);
module_exit(ultracam_cleanup);

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,395 @@
/*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef usbvideo_h
#define usbvideo_h
#include <linux/videodev.h>
#include <media/v4l2-common.h>
#include <media/v4l2-ioctl.h>
#include <linux/usb.h>
#include <linux/mutex.h>
/* Most helpful debugging aid */
#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */
/* Bit flags (options) */
#define FLAGS_RETRY_VIDIOCSYNC (1 << 0)
#define FLAGS_MONOCHROME (1 << 1)
#define FLAGS_DISPLAY_HINTS (1 << 2)
#define FLAGS_OVERLAY_STATS (1 << 3)
#define FLAGS_FORCE_TESTPATTERN (1 << 4)
#define FLAGS_SEPARATE_FRAMES (1 << 5)
#define FLAGS_CLEAN_FRAMES (1 << 6)
#define FLAGS_NO_DECODING (1 << 7)
/* Bit flags for frames (apply to the frame where they are specified) */
#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0)
/* Camera capabilities (maximum) */
#define CAMERA_URB_FRAMES 32
#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */
#define FRAMES_PER_DESC (CAMERA_URB_FRAMES)
#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET)
/* This macro restricts an int variable to an inclusive range */
#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); }
#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */
/*
* Use this macro to construct constants for different video sizes.
* We have to deal with different video sizes that have to be
* configured in the device or compared against when we receive
* a data. Normally one would define a bunch of VIDEOSIZE_x_by_y
* #defines and that's the end of story. However this solution
* does not allow to convert between real pixel sizes and the
* constant (integer) value that may be used to tag a frame or
* whatever. The set of macros below constructs videosize constants
* from the pixel size and allows to reconstruct the pixel size
* from the combined value later.
*/
#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16))
#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL)
#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL)
typedef unsigned long videosize_t;
/*
* This macro checks if the camera is still operational. The 'uvd'
* pointer must be valid, uvd->dev must be valid, we are not
* removing the device and the device has not erred on us.
*/
#define CAMERA_IS_OPERATIONAL(uvd) (\
(uvd != NULL) && \
((uvd)->dev != NULL) && \
((uvd)->last_error == 0) && \
(!(uvd)->remove_pending))
/*
* We use macros to do YUV -> RGB conversion because this is
* very important for speed and totally unimportant for size.
*
* YUV -> RGB Conversion
* ---------------------
*
* B = 1.164*(Y-16) + 2.018*(V-128)
* G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128)
* R = 1.164*(Y-16) + 1.596*(U-128)
*
* If you fancy integer arithmetics (as you should), hear this:
*
* 65536*B = 76284*(Y-16) + 132252*(V-128)
* 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128)
* 65536*R = 76284*(Y-16) + 104595*(U-128)
*
* Make sure the output values are within [0..255] range.
*/
#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x)))
#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \
int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \
mm_y = (my) - 16; \
mm_u = (mu) - 128; \
mm_v = (mv) - 128; \
mm_yc= mm_y * 76284; \
mm_b = (mm_yc + 132252*mm_v ) >> 16; \
mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \
mm_r = (mm_yc + 104595*mm_u ) >> 16; \
mb = LIMIT_RGB(mm_b); \
mg = LIMIT_RGB(mm_g); \
mr = LIMIT_RGB(mm_r); \
}
#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */
#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1)
#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n)
#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)])
struct RingQueue {
unsigned char *queue; /* Data from the Isoc data pump */
int length; /* How many bytes allocated for the queue */
int wi; /* That's where we write */
int ri; /* Read from here until you hit write index */
wait_queue_head_t wqh; /* Processes waiting */
};
enum ScanState {
ScanState_Scanning, /* Scanning for header */
ScanState_Lines /* Parsing lines */
};
/* Completion states of the data parser */
enum ParseState {
scan_Continue, /* Just parse next item */
scan_NextFrame, /* Frame done, send it to V4L */
scan_Out, /* Not enough data for frame */
scan_EndParse /* End parsing */
};
enum FrameState {
FrameState_Unused, /* Unused (no MCAPTURE) */
FrameState_Ready, /* Ready to start grabbing */
FrameState_Grabbing, /* In the process of being grabbed into */
FrameState_Done, /* Finished grabbing, but not been synced yet */
FrameState_Done_Hold, /* Are syncing or reading */
FrameState_Error, /* Something bad happened while processing */
};
/*
* Some frames may contain only even or odd lines. This type
* specifies what type of deinterlacing is required.
*/
enum Deinterlace {
Deinterlace_None=0,
Deinterlace_FillOddLines,
Deinterlace_FillEvenLines
};
#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */
#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */
/* This structure represents one Isoc request - URB and buffer */
struct usbvideo_sbuf {
char *data;
struct urb *urb;
};
struct usbvideo_frame {
char *data; /* Frame buffer */
unsigned long header; /* Significant bits from the header */
videosize_t canvas; /* The canvas (max. image) allocated */
videosize_t request; /* That's what the application asked for */
unsigned short palette; /* The desired format */
enum FrameState frameState;/* State of grabbing */
enum ScanState scanstate; /* State of scanning */
enum Deinterlace deinterlace;
int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */
int curline; /* Line of frame we're working on */
long seqRead_Length; /* Raw data length of frame */
long seqRead_Index; /* Amount of data that has been already read */
void *user; /* Additional data that user may need */
};
/* Statistics that can be overlaid on screen */
struct usbvideo_statistics {
unsigned long frame_num; /* Sequential number of the frame */
unsigned long urb_count; /* How many URBs we received so far */
unsigned long urb_length; /* Length of last URB */
unsigned long data_count; /* How many bytes we received */
unsigned long header_count; /* How many frame headers we found */
unsigned long iso_skip_count; /* How many empty ISO packets received */
unsigned long iso_err_count; /* How many bad ISO packets received */
};
struct usbvideo;
struct uvd {
struct video_device vdev; /* Must be the first field! */
struct usb_device *dev;
struct usbvideo *handle; /* Points back to the struct usbvideo */
void *user_data; /* Camera-dependent data */
int user_size; /* Size of that camera-dependent data */
int debug; /* Debug level for usbvideo */
unsigned char iface; /* Video interface number */
unsigned char video_endp;
unsigned char ifaceAltActive;
unsigned char ifaceAltInactive; /* Alt settings */
unsigned long flags; /* FLAGS_USBVIDEO_xxx */
unsigned long paletteBits; /* Which palettes we accept? */
unsigned short defaultPalette; /* What palette to use for read() */
struct mutex lock;
int user; /* user count for exclusive use */
videosize_t videosize; /* Current setting */
videosize_t canvas; /* This is the width,height of the V4L canvas */
int max_frame_size; /* Bytes in one video frame */
int uvd_used; /* Is this structure in use? */
int streaming; /* Are we streaming Isochronous? */
int grabbing; /* Are we grabbing? */
int settingsAdjusted; /* Have we adjusted contrast etc.? */
int last_error; /* What calamity struck us? */
char *fbuf; /* Videodev buffer area */
int fbuf_size; /* Videodev buffer size */
int curframe;
int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */
struct RingQueue dp; /* Isoc data pump */
struct usbvideo_frame frame[USBVIDEO_NUMFRAMES];
struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF];
volatile int remove_pending; /* If set then about to exit */
struct video_picture vpic, vpic_old; /* Picture settings */
struct video_capability vcap; /* Video capabilities */
struct video_channel vchan; /* May be used for tuner support */
struct usbvideo_statistics stats;
char videoName[32]; /* Holds name like "video7" */
};
/*
* usbvideo callbacks (virtual methods). They are set when usbvideo
* services are registered. All of these default to NULL, except those
* that default to usbvideo-provided methods.
*/
struct usbvideo_cb {
int (*probe)(struct usb_interface *, const struct usb_device_id *);
void (*userFree)(struct uvd *);
void (*disconnect)(struct usb_interface *);
int (*setupOnOpen)(struct uvd *);
void (*videoStart)(struct uvd *);
void (*videoStop)(struct uvd *);
void (*processData)(struct uvd *, struct usbvideo_frame *);
void (*postProcess)(struct uvd *, struct usbvideo_frame *);
void (*adjustPicture)(struct uvd *);
int (*getFPS)(struct uvd *);
int (*overlayHook)(struct uvd *, struct usbvideo_frame *);
int (*getFrame)(struct uvd *, int);
int (*startDataPump)(struct uvd *uvd);
void (*stopDataPump)(struct uvd *uvd);
int (*setVideoMode)(struct uvd *uvd, struct video_window *vw);
};
struct usbvideo {
int num_cameras; /* As allocated */
struct usb_driver usbdrv; /* Interface to the USB stack */
char drvName[80]; /* Driver name */
struct mutex lock; /* Mutex protecting camera structures */
struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */
struct video_device vdt; /* Video device template */
struct uvd *cam; /* Array of camera structures */
struct module *md_module; /* Minidriver module */
};
/*
* This macro retrieves callback address from the struct uvd object.
* No validity checks are done here, so be sure to check the
* callback beforehand with VALID_CALLBACK.
*/
#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName)
/*
* This macro returns either callback pointer or NULL. This is safe
* macro, meaning that most of components of data structures involved
* may be NULL - this only results in NULL being returned. You may
* wish to use this macro to make sure that the callback is callable.
* However keep in mind that those checks take time.
*/
#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \
((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL)
int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len);
int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n);
void RingQueue_WakeUpInterruptible(struct RingQueue *rq);
void RingQueue_Flush(struct RingQueue *rq);
static inline int RingQueue_GetLength(const struct RingQueue *rq)
{
return (rq->wi - rq->ri + rq->length) & (rq->length-1);
}
static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq)
{
return rq->length - RingQueue_GetLength(rq);
}
void usbvideo_DrawLine(
struct usbvideo_frame *frame,
int x1, int y1,
int x2, int y2,
unsigned char cr, unsigned char cg, unsigned char cb);
void usbvideo_HexDump(const unsigned char *data, int len);
void usbvideo_SayAndWait(const char *what);
void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode);
/* Memory allocation routines */
unsigned long usbvideo_kvirt_to_pa(unsigned long adr);
int usbvideo_register(
struct usbvideo **pCams,
const int num_cams,
const int num_extra,
const char *driverName,
const struct usbvideo_cb *cbTable,
struct module *md,
const struct usb_device_id *id_table);
struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams);
int usbvideo_RegisterVideoDevice(struct uvd *uvd);
void usbvideo_Deregister(struct usbvideo **uvt);
int usbvideo_v4l_initialize(struct video_device *dev);
void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame);
/*
* This code performs bounds checking - use it when working with
* new formats, or else you may get oopses all over the place.
* If pixel falls out of bounds then it gets shoved back (as close
* to place of offence as possible) and is painted bright red.
*
* There are two important concepts: frame width, height and
* V4L canvas width, height. The former is the area requested by
* the application -for this very frame-. The latter is the largest
* possible frame that we can serve (we advertise that via V4L ioctl).
* The frame data is expected to be formatted as lines of length
* VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines.
*/
static inline void RGB24_PUTPIXEL(
struct usbvideo_frame *fr,
int ix, int iy,
unsigned char vr,
unsigned char vg,
unsigned char vb)
{
register unsigned char *pf;
int limiter = 0, mx, my;
mx = ix;
my = iy;
if (mx < 0) {
mx=0;
limiter++;
} else if (mx >= VIDEOSIZE_X((fr)->request)) {
mx= VIDEOSIZE_X((fr)->request) - 1;
limiter++;
}
if (my < 0) {
my = 0;
limiter++;
} else if (my >= VIDEOSIZE_Y((fr)->request)) {
my = VIDEOSIZE_Y((fr)->request) - 1;
limiter++;
}
pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix));
if (limiter) {
*pf++ = 0;
*pf++ = 0;
*pf++ = 0xFF;
} else {
*pf++ = (vb);
*pf++ = (vg);
*pf++ = (vr);
}
}
#endif /* usbvideo_h */

View File

@@ -0,0 +1,954 @@
/*
* USB ViCam WebCam driver
* Copyright (c) 2002 Joe Burks (jburks@wavicle.org),
* Christopher L Cheney (ccheney@cheney.cx),
* Pavel Machek (pavel@suse.cz),
* John Tyner (jtyner@cs.ucr.edu),
* Monroe Williams (monroe@pobox.com)
*
* Supports 3COM HomeConnect PC Digital WebCam
* Supports Compro PS39U WebCam
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*
* This source code is based heavily on the CPiA webcam driver which was
* written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt
*
* Portions of this code were also copied from usbvideo.c
*
* Special thanks to the whole team at Sourceforge for help making
* this driver become a reality. Notably:
* Andy Armstrong who reverse engineered the color encoding and
* Pavel Machek and Chris Cheney who worked on reverse engineering the
* camera controls and wrote the first generation driver.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/videodev.h>
#include <linux/usb.h>
#include <linux/vmalloc.h>
#include <linux/mm.h>
#include <linux/slab.h>
#include <linux/smp_lock.h>
#include <linux/mutex.h>
#include <linux/firmware.h>
#include <linux/ihex.h>
#include "usbvideo.h"
// #define VICAM_DEBUG
#ifdef VICAM_DEBUG
#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __func__, lineno, ##args)
#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args)
#else
#define DBG(fmn,args...) do {} while(0)
#endif
#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org"
#define DRIVER_DESC "ViCam WebCam Driver"
/* Define these values to match your device */
#define USB_VICAM_VENDOR_ID 0x04c1
#define USB_VICAM_PRODUCT_ID 0x009d
#define USB_COMPRO_VENDOR_ID 0x0602
#define USB_COMPRO_PRODUCT_ID 0x1001
#define VICAM_BYTES_PER_PIXEL 3
#define VICAM_MAX_READ_SIZE (512*242+128)
#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240)
#define VICAM_FRAMES 2
#define VICAM_HEADER_SIZE 64
/* rvmalloc / rvfree copied from usbvideo.c
*
* Not sure why these are not yet non-statics which I can reference through
* usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime
* in the future.
*
*/
static void *rvmalloc(unsigned long size)
{
void *mem;
unsigned long adr;
size = PAGE_ALIGN(size);
mem = vmalloc_32(size);
if (!mem)
return NULL;
memset(mem, 0, size); /* Clear the ram out, no junk to the user */
adr = (unsigned long) mem;
while (size > 0) {
SetPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
return mem;
}
static void rvfree(void *mem, unsigned long size)
{
unsigned long adr;
if (!mem)
return;
adr = (unsigned long) mem;
while ((long) size > 0) {
ClearPageReserved(vmalloc_to_page((void *)adr));
adr += PAGE_SIZE;
size -= PAGE_SIZE;
}
vfree(mem);
}
struct vicam_camera {
u16 shutter_speed; // capture shutter speed
u16 gain; // capture gain
u8 *raw_image; // raw data captured from the camera
u8 *framebuf; // processed data in RGB24 format
u8 *cntrlbuf; // area used to send control msgs
struct video_device vdev; // v4l video device
struct usb_device *udev; // usb device
/* guard against simultaneous accesses to the camera */
struct mutex cam_lock;
int is_initialized;
u8 open_count;
u8 bulkEndpoint;
int needsDummyRead;
};
static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id);
static void vicam_disconnect(struct usb_interface *intf);
static void read_frame(struct vicam_camera *cam, int framenum);
static void vicam_decode_color(const u8 *, u8 *);
static int __send_control_msg(struct vicam_camera *cam,
u8 request,
u16 value,
u16 index,
unsigned char *cp,
u16 size)
{
int status;
/* cp must be memory that has been allocated by kmalloc */
status = usb_control_msg(cam->udev,
usb_sndctrlpipe(cam->udev, 0),
request,
USB_DIR_OUT | USB_TYPE_VENDOR |
USB_RECIP_DEVICE, value, index,
cp, size, 1000);
status = min(status, 0);
if (status < 0) {
printk(KERN_INFO "Failed sending control message, error %d.\n",
status);
}
return status;
}
static int send_control_msg(struct vicam_camera *cam,
u8 request,
u16 value,
u16 index,
unsigned char *cp,
u16 size)
{
int status = -ENODEV;
mutex_lock(&cam->cam_lock);
if (cam->udev) {
status = __send_control_msg(cam, request, value,
index, cp, size);
}
mutex_unlock(&cam->cam_lock);
return status;
}
static int
initialize_camera(struct vicam_camera *cam)
{
int err;
const struct ihex_binrec *rec;
const struct firmware *uninitialized_var(fw);
err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev);
if (err) {
printk(KERN_ERR "Failed to load \"vicam/firmware.fw\": %d\n",
err);
return err;
}
for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) {
memcpy(cam->cntrlbuf, rec->data, be16_to_cpu(rec->len));
err = send_control_msg(cam, 0xff, 0, 0,
cam->cntrlbuf, be16_to_cpu(rec->len));
if (err)
break;
}
release_firmware(fw);
return err;
}
static int
set_camera_power(struct vicam_camera *cam, int state)
{
int status;
if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0)
return status;
if (state) {
send_control_msg(cam, 0x55, 1, 0, NULL, 0);
}
return 0;
}
static long
vicam_ioctl(struct file *file, unsigned int ioctlnr, unsigned long arg)
{
void __user *user_arg = (void __user *)arg;
struct vicam_camera *cam = file->private_data;
long retval = 0;
if (!cam)
return -ENODEV;
switch (ioctlnr) {
/* query capabilities */
case VIDIOCGCAP:
{
struct video_capability b;
DBG("VIDIOCGCAP\n");
memset(&b, 0, sizeof(b));
strcpy(b.name, "ViCam-based Camera");
b.type = VID_TYPE_CAPTURE;
b.channels = 1;
b.audios = 0;
b.maxwidth = 320; /* VIDEOSIZE_CIF */
b.maxheight = 240;
b.minwidth = 320; /* VIDEOSIZE_48_48 */
b.minheight = 240;
if (copy_to_user(user_arg, &b, sizeof(b)))
retval = -EFAULT;
break;
}
/* get/set video source - we are a camera and nothing else */
case VIDIOCGCHAN:
{
struct video_channel v;
DBG("VIDIOCGCHAN\n");
if (copy_from_user(&v, user_arg, sizeof(v))) {
retval = -EFAULT;
break;
}
if (v.channel != 0) {
retval = -EINVAL;
break;
}
v.channel = 0;
strcpy(v.name, "Camera");
v.tuners = 0;
v.flags = 0;
v.type = VIDEO_TYPE_CAMERA;
v.norm = 0;
if (copy_to_user(user_arg, &v, sizeof(v)))
retval = -EFAULT;
break;
}
case VIDIOCSCHAN:
{
int v;
if (copy_from_user(&v, user_arg, sizeof(v)))
retval = -EFAULT;
DBG("VIDIOCSCHAN %d\n", v);
if (retval == 0 && v != 0)
retval = -EINVAL;
break;
}
/* image properties */
case VIDIOCGPICT:
{
struct video_picture vp;
DBG("VIDIOCGPICT\n");
memset(&vp, 0, sizeof (struct video_picture));
vp.brightness = cam->gain << 8;
vp.depth = 24;
vp.palette = VIDEO_PALETTE_RGB24;
if (copy_to_user(user_arg, &vp, sizeof (struct video_picture)))
retval = -EFAULT;
break;
}
case VIDIOCSPICT:
{
struct video_picture vp;
if (copy_from_user(&vp, user_arg, sizeof(vp))) {
retval = -EFAULT;
break;
}
DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth,
vp.palette);
cam->gain = vp.brightness >> 8;
if (vp.depth != 24
|| vp.palette != VIDEO_PALETTE_RGB24)
retval = -EINVAL;
break;
}
/* get/set capture window */
case VIDIOCGWIN:
{
struct video_window vw;
vw.x = 0;
vw.y = 0;
vw.width = 320;
vw.height = 240;
vw.chromakey = 0;
vw.flags = 0;
vw.clips = NULL;
vw.clipcount = 0;
DBG("VIDIOCGWIN\n");
if (copy_to_user(user_arg, (void *)&vw, sizeof(vw)))
retval = -EFAULT;
// I'm not sure what the deal with a capture window is, it is very poorly described
// in the doc. So I won't support it now.
break;
}
case VIDIOCSWIN:
{
struct video_window vw;
if (copy_from_user(&vw, user_arg, sizeof(vw))) {
retval = -EFAULT;
break;
}
DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height);
if ( vw.width != 320 || vw.height != 240 )
retval = -EFAULT;
break;
}
/* mmap interface */
case VIDIOCGMBUF:
{
struct video_mbuf vm;
int i;
DBG("VIDIOCGMBUF\n");
memset(&vm, 0, sizeof (vm));
vm.size =
VICAM_MAX_FRAME_SIZE * VICAM_FRAMES;
vm.frames = VICAM_FRAMES;
for (i = 0; i < VICAM_FRAMES; i++)
vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i;
if (copy_to_user(user_arg, (void *)&vm, sizeof(vm)))
retval = -EFAULT;
break;
}
case VIDIOCMCAPTURE:
{
struct video_mmap vm;
// int video_size;
if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) {
retval = -EFAULT;
break;
}
DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format);
if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 )
retval = -EINVAL;
// in theory right here we'd start the image capturing
// (fill in a bulk urb and submit it asynchronously)
//
// Instead we're going to do a total hack job for now and
// retrieve the frame in VIDIOCSYNC
break;
}
case VIDIOCSYNC:
{
int frame;
if (copy_from_user((void *)&frame, user_arg, sizeof(int))) {
retval = -EFAULT;
break;
}
DBG("VIDIOCSYNC: %d\n", frame);
read_frame(cam, frame);
vicam_decode_color(cam->raw_image,
cam->framebuf +
frame * VICAM_MAX_FRAME_SIZE );
break;
}
/* pointless to implement overlay with this camera */
case VIDIOCCAPTURE:
case VIDIOCGFBUF:
case VIDIOCSFBUF:
case VIDIOCKEY:
retval = -EINVAL;
break;
/* tuner interface - we have none */
case VIDIOCGTUNER:
case VIDIOCSTUNER:
case VIDIOCGFREQ:
case VIDIOCSFREQ:
retval = -EINVAL;
break;
/* audio interface - we have none */
case VIDIOCGAUDIO:
case VIDIOCSAUDIO:
retval = -EINVAL;
break;
default:
retval = -ENOIOCTLCMD;
break;
}
return retval;
}
static int
vicam_open(struct file *file)
{
struct vicam_camera *cam = video_drvdata(file);
DBG("open\n");
if (!cam) {
printk(KERN_ERR
"vicam video_device improperly initialized");
return -EINVAL;
}
/* the videodev_lock held above us protects us from
* simultaneous opens...for now. we probably shouldn't
* rely on this fact forever.
*/
lock_kernel();
if (cam->open_count > 0) {
printk(KERN_INFO
"vicam_open called on already opened camera");
unlock_kernel();
return -EBUSY;
}
cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL);
if (!cam->raw_image) {
unlock_kernel();
return -ENOMEM;
}
cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
if (!cam->framebuf) {
kfree(cam->raw_image);
unlock_kernel();
return -ENOMEM;
}
cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
if (!cam->cntrlbuf) {
kfree(cam->raw_image);
rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
unlock_kernel();
return -ENOMEM;
}
// First upload firmware, then turn the camera on
if (!cam->is_initialized) {
initialize_camera(cam);
cam->is_initialized = 1;
}
set_camera_power(cam, 1);
cam->needsDummyRead = 1;
cam->open_count++;
file->private_data = cam;
unlock_kernel();
return 0;
}
static int
vicam_close(struct file *file)
{
struct vicam_camera *cam = file->private_data;
int open_count;
struct usb_device *udev;
DBG("close\n");
/* it's not the end of the world if
* we fail to turn the camera off.
*/
set_camera_power(cam, 0);
kfree(cam->raw_image);
rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES);
kfree(cam->cntrlbuf);
mutex_lock(&cam->cam_lock);
cam->open_count--;
open_count = cam->open_count;
udev = cam->udev;
mutex_unlock(&cam->cam_lock);
if (!open_count && !udev) {
kfree(cam);
}
return 0;
}
static void vicam_decode_color(const u8 *data, u8 *rgb)
{
/* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB
* Copyright (C) 2002 Monroe Williams (monroe@pobox.com)
*/
int i, prevY, nextY;
prevY = 512;
nextY = 512;
data += VICAM_HEADER_SIZE;
for( i = 0; i < 240; i++, data += 512 ) {
const int y = ( i * 242 ) / 240;
int j, prevX, nextX;
int Y, Cr, Cb;
if ( y == 242 - 1 ) {
nextY = -512;
}
prevX = 1;
nextX = 1;
for ( j = 0; j < 320; j++, rgb += 3 ) {
const int x = ( j * 512 ) / 320;
const u8 * const src = &data[x];
if ( x == 512 - 1 ) {
nextX = -1;
}
Cr = ( src[prevX] - src[0] ) +
( src[nextX] - src[0] );
Cr /= 2;
Cb = ( src[prevY] - src[prevX + prevY] ) +
( src[prevY] - src[nextX + prevY] ) +
( src[nextY] - src[prevX + nextY] ) +
( src[nextY] - src[nextX + nextY] );
Cb /= 4;
Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 );
if ( i & 1 ) {
int Ct = Cr;
Cr = Cb;
Cb = Ct;
}
if ( ( x ^ i ) & 1 ) {
Cr = -Cr;
Cb = -Cb;
}
rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) +
500 ) / 900, 0, 255 );
rgb[1] = clamp( ( ( Y - ( 392 * Cb ) -
( 813 * Cr ) ) +
500 ) / 1000, 0, 255 );
rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) +
500 ) / 1300, 0, 255 );
prevX = -1;
}
prevY = -512;
}
}
static void
read_frame(struct vicam_camera *cam, int framenum)
{
unsigned char *request = cam->cntrlbuf;
int realShutter;
int n;
int actual_length;
if (cam->needsDummyRead) {
cam->needsDummyRead = 0;
read_frame(cam, framenum);
}
memset(request, 0, 16);
request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain
request[1] = 0; // 512x242 capture
request[2] = 0x90; // the function of these two bytes
request[3] = 0x07; // is not yet understood
if (cam->shutter_speed > 60) {
// Short exposure
realShutter =
((-15631900 / cam->shutter_speed) + 260533) / 1000;
request[4] = realShutter & 0xFF;
request[5] = (realShutter >> 8) & 0xFF;
request[6] = 0x03;
request[7] = 0x01;
} else {
// Long exposure
realShutter = 15600 / cam->shutter_speed - 1;
request[4] = 0;
request[5] = 0;
request[6] = realShutter & 0xFF;
request[7] = realShutter >> 8;
}
// Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0
request[8] = 0;
// bytes 9-15 do not seem to affect exposure or image quality
mutex_lock(&cam->cam_lock);
if (!cam->udev) {
goto done;
}
n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16);
if (n < 0) {
printk(KERN_ERR
" Problem sending frame capture control message");
goto done;
}
n = usb_bulk_msg(cam->udev,
usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint),
cam->raw_image,
512 * 242 + 128, &actual_length, 10000);
if (n < 0) {
printk(KERN_ERR "Problem during bulk read of frame data: %d\n",
n);
}
done:
mutex_unlock(&cam->cam_lock);
}
static ssize_t
vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos )
{
struct vicam_camera *cam = file->private_data;
DBG("read %d bytes.\n", (int) count);
if (*ppos >= VICAM_MAX_FRAME_SIZE) {
*ppos = 0;
return 0;
}
if (*ppos == 0) {
read_frame(cam, 0);
vicam_decode_color(cam->raw_image,
cam->framebuf +
0 * VICAM_MAX_FRAME_SIZE);
}
count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos);
if (copy_to_user(buf, &cam->framebuf[*ppos], count)) {
count = -EFAULT;
} else {
*ppos += count;
}
if (count == VICAM_MAX_FRAME_SIZE) {
*ppos = 0;
}
return count;
}
static int
vicam_mmap(struct file *file, struct vm_area_struct *vma)
{
// TODO: allocate the raw frame buffer if necessary
unsigned long page, pos;
unsigned long start = vma->vm_start;
unsigned long size = vma->vm_end-vma->vm_start;
struct vicam_camera *cam = file->private_data;
if (!cam)
return -ENODEV;
DBG("vicam_mmap: %ld\n", size);
/* We let mmap allocate as much as it wants because Linux was adding 2048 bytes
* to the size the application requested for mmap and it was screwing apps up.
if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE)
return -EINVAL;
*/
pos = (unsigned long)cam->framebuf;
while (size > 0) {
page = vmalloc_to_pfn((void *)pos);
if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED))
return -EAGAIN;
start += PAGE_SIZE;
pos += PAGE_SIZE;
if (size > PAGE_SIZE)
size -= PAGE_SIZE;
else
size = 0;
}
return 0;
}
static const struct v4l2_file_operations vicam_fops = {
.owner = THIS_MODULE,
.open = vicam_open,
.release = vicam_close,
.read = vicam_read,
.mmap = vicam_mmap,
.ioctl = vicam_ioctl,
};
static struct video_device vicam_template = {
.name = "ViCam-based USB Camera",
.fops = &vicam_fops,
.minor = -1,
.release = video_device_release_empty,
};
/* table of devices that work with this driver */
static struct usb_device_id vicam_table[] = {
{USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)},
{USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)},
{} /* Terminating entry */
};
MODULE_DEVICE_TABLE(usb, vicam_table);
static struct usb_driver vicam_driver = {
.name = "vicam",
.probe = vicam_probe,
.disconnect = vicam_disconnect,
.id_table = vicam_table
};
/**
* vicam_probe
* @intf: the interface
* @id: the device id
*
* Called by the usb core when a new device is connected that it thinks
* this driver might be interested in.
*/
static int
vicam_probe( struct usb_interface *intf, const struct usb_device_id *id)
{
struct usb_device *dev = interface_to_usbdev(intf);
int bulkEndpoint = 0;
const struct usb_host_interface *interface;
const struct usb_endpoint_descriptor *endpoint;
struct vicam_camera *cam;
printk(KERN_INFO "ViCam based webcam connected\n");
interface = intf->cur_altsetting;
DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n",
interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints));
endpoint = &interface->endpoint[0].desc;
if (usb_endpoint_is_bulk_in(endpoint)) {
/* we found a bulk in endpoint */
bulkEndpoint = endpoint->bEndpointAddress;
} else {
printk(KERN_ERR
"No bulk in endpoint was found ?! (this is bad)\n");
}
if ((cam =
kzalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) {
printk(KERN_WARNING
"could not allocate kernel memory for vicam_camera struct\n");
return -ENOMEM;
}
cam->shutter_speed = 15;
mutex_init(&cam->cam_lock);
memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template));
video_set_drvdata(&cam->vdev, cam);
cam->udev = dev;
cam->bulkEndpoint = bulkEndpoint;
if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) {
kfree(cam);
printk(KERN_WARNING "video_register_device failed\n");
return -EIO;
}
printk(KERN_INFO "ViCam webcam driver now controlling video device %d\n",
cam->vdev.num);
usb_set_intfdata (intf, cam);
return 0;
}
static void
vicam_disconnect(struct usb_interface *intf)
{
int open_count;
struct vicam_camera *cam = usb_get_intfdata (intf);
usb_set_intfdata (intf, NULL);
/* we must unregister the device before taking its
* cam_lock. This is because the video open call
* holds the same lock as video unregister. if we
* unregister inside of the cam_lock and open also
* uses the cam_lock, we get deadlock.
*/
video_unregister_device(&cam->vdev);
/* stop the camera from being used */
mutex_lock(&cam->cam_lock);
/* mark the camera as gone */
cam->udev = NULL;
/* the only thing left to do is synchronize with
* our close/release function on who should release
* the camera memory. if there are any users using the
* camera, it's their job. if there are no users,
* it's ours.
*/
open_count = cam->open_count;
mutex_unlock(&cam->cam_lock);
if (!open_count) {
kfree(cam);
}
printk(KERN_DEBUG "ViCam-based WebCam disconnected\n");
}
/*
*/
static int __init
usb_vicam_init(void)
{
int retval;
DBG(KERN_INFO "ViCam-based WebCam driver startup\n");
retval = usb_register(&vicam_driver);
if (retval)
printk(KERN_WARNING "usb_register failed!\n");
return retval;
}
static void __exit
usb_vicam_exit(void)
{
DBG(KERN_INFO
"ViCam-based WebCam driver shutdown\n");
usb_deregister(&vicam_driver);
}
module_init(usb_vicam_init);
module_exit(usb_vicam_exit);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
MODULE_FIRMWARE("vicam/firmware.fw");