From 0aa77f6c2954896b132f8b6f2e9f063c52800913 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Tue, 14 Aug 2012 01:01:29 -0300 Subject: [media] move the remaining USB drivers to drivers/media/usb Move the 3 remaining usb drivers to their proper space. Signed-off-by: Mauro Carvalho Chehab --- MAINTAINERS | 2 +- drivers/media/usb/Kconfig | 3 + drivers/media/usb/Makefile | 2 + drivers/media/usb/s2255/Kconfig | 9 + drivers/media/usb/s2255/Makefile | 2 + drivers/media/usb/s2255/s2255drv.c | 2689 ++++++++++++++++++++++++++++++ drivers/media/usb/stkwebcam/Kconfig | 13 + drivers/media/usb/stkwebcam/Makefile | 4 + drivers/media/usb/stkwebcam/stk-sensor.c | 595 +++++++ drivers/media/usb/stkwebcam/stk-webcam.c | 1380 +++++++++++++++ drivers/media/usb/stkwebcam/stk-webcam.h | 134 ++ drivers/media/usb/zr364xx/Kconfig | 14 + drivers/media/usb/zr364xx/Makefile | 2 + drivers/media/usb/zr364xx/zr364xx.c | 1643 ++++++++++++++++++ drivers/media/video/Kconfig | 50 - drivers/media/video/Makefile | 8 - drivers/media/video/s2255drv.c | 2689 ------------------------------ drivers/media/video/stk-sensor.c | 595 ------- drivers/media/video/stk-webcam.c | 1380 --------------- drivers/media/video/stk-webcam.h | 134 -- drivers/media/video/zr364xx.c | 1643 ------------------ 21 files changed, 6491 insertions(+), 6500 deletions(-) create mode 100644 drivers/media/usb/s2255/Kconfig create mode 100644 drivers/media/usb/s2255/Makefile create mode 100644 drivers/media/usb/s2255/s2255drv.c create mode 100644 drivers/media/usb/stkwebcam/Kconfig create mode 100644 drivers/media/usb/stkwebcam/Makefile create mode 100644 drivers/media/usb/stkwebcam/stk-sensor.c create mode 100644 drivers/media/usb/stkwebcam/stk-webcam.c create mode 100644 drivers/media/usb/stkwebcam/stk-webcam.h create mode 100644 drivers/media/usb/zr364xx/Kconfig create mode 100644 drivers/media/usb/zr364xx/Makefile create mode 100644 drivers/media/usb/zr364xx/zr364xx.c delete mode 100644 drivers/media/video/s2255drv.c delete mode 100644 drivers/media/video/stk-sensor.c delete mode 100644 drivers/media/video/stk-webcam.c delete mode 100644 drivers/media/video/stk-webcam.h delete mode 100644 drivers/media/video/zr364xx.c diff --git a/MAINTAINERS b/MAINTAINERS index 13fd97f6466a..99a930d712cf 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7371,7 +7371,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/mchehab/linux-media.git W: http://royale.zerezo.com/zr364xx/ S: Maintained F: Documentation/video4linux/zr364xx.txt -F: drivers/media/video/zr364xx.c +F: drivers/media/usb/zr364xx.c USER-MODE LINUX (UML) M: Jeff Dike diff --git a/drivers/media/usb/Kconfig b/drivers/media/usb/Kconfig index a1e25ee9d671..069a3c1d03f5 100644 --- a/drivers/media/usb/Kconfig +++ b/drivers/media/usb/Kconfig @@ -11,6 +11,9 @@ source "drivers/media/usb/uvc/Kconfig" source "drivers/media/usb/gspca/Kconfig" source "drivers/media/usb/pwc/Kconfig" source "drivers/media/usb/cpia2/Kconfig" +source "drivers/media/usb/zr364xx/Kconfig" +source "drivers/media/usb/stkwebcam/Kconfig" +source "drivers/media/usb/s2255/Kconfig" source "drivers/media/usb/sn9c102/Kconfig" endif diff --git a/drivers/media/usb/Makefile b/drivers/media/usb/Makefile index 428827a4d97a..63e37bb2ed74 100644 --- a/drivers/media/usb/Makefile +++ b/drivers/media/usb/Makefile @@ -4,6 +4,8 @@ # DVB USB-only drivers obj-y := ttusb-dec/ ttusb-budget/ dvb-usb/ dvb-usb-v2/ siano/ b2c2/ +obj-y := zr364xx/ stkwebcam/ s2255/ + obj-$(CONFIG_USB_VIDEO_CLASS) += uvc/ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_USB_PWC) += pwc/ diff --git a/drivers/media/usb/s2255/Kconfig b/drivers/media/usb/s2255/Kconfig new file mode 100644 index 000000000000..7e8ee1f864ab --- /dev/null +++ b/drivers/media/usb/s2255/Kconfig @@ -0,0 +1,9 @@ +config USB_S2255 + tristate "USB Sensoray 2255 video capture device" + depends on VIDEO_V4L2 + select VIDEOBUF_VMALLOC + default n + help + Say Y here if you want support for the Sensoray 2255 USB device. + This driver can be compiled as a module, called s2255drv. + diff --git a/drivers/media/usb/s2255/Makefile b/drivers/media/usb/s2255/Makefile new file mode 100644 index 000000000000..197d0bb2adfd --- /dev/null +++ b/drivers/media/usb/s2255/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_USB_S2255) += s2255drv.o + diff --git a/drivers/media/usb/s2255/s2255drv.c b/drivers/media/usb/s2255/s2255drv.c new file mode 100644 index 000000000000..6c7960cc7506 --- /dev/null +++ b/drivers/media/usb/s2255/s2255drv.c @@ -0,0 +1,2689 @@ +/* + * s2255drv.c - a driver for the Sensoray 2255 USB video capture device + * + * Copyright (C) 2007-2010 by Sensoray Company Inc. + * Dean Anderson + * + * Some video buffer code based on vivi driver: + * + * Sensoray 2255 device supports 4 simultaneous channels. + * The channels are not "crossbar" inputs, they are physically + * attached to separate video decoders. + * + * Because of USB2.0 bandwidth limitations. There is only a + * certain amount of data which may be transferred at one time. + * + * Example maximum bandwidth utilization: + * + * -full size, color mode YUYV or YUV422P: 2 channels at once + * -full or half size Grey scale: all 4 channels at once + * -half size, color mode YUYV or YUV422P: all 4 channels at once + * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels + * at once. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define S2255_VERSION "1.22.1" +#define FIRMWARE_FILE_NAME "f2255usb.bin" + +/* default JPEG quality */ +#define S2255_DEF_JPEG_QUAL 50 +/* vendor request in */ +#define S2255_VR_IN 0 +/* vendor request out */ +#define S2255_VR_OUT 1 +/* firmware query */ +#define S2255_VR_FW 0x30 +/* USB endpoint number for configuring the device */ +#define S2255_CONFIG_EP 2 +/* maximum time for DSP to start responding after last FW word loaded(ms) */ +#define S2255_DSP_BOOTTIME 800 +/* maximum time to wait for firmware to load (ms) */ +#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) +#define S2255_DEF_BUFS 16 +#define S2255_SETMODE_TIMEOUT 500 +#define S2255_VIDSTATUS_TIMEOUT 350 +#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) +#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) +#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) +#define S2255_RESPONSE_FW cpu_to_le32(0x10) +#define S2255_RESPONSE_STATUS cpu_to_le32(0x20) +#define S2255_USB_XFER_SIZE (16 * 1024) +#define MAX_CHANNELS 4 +#define SYS_FRAMES 4 +/* maximum size is PAL full size plus room for the marker header(s) */ +#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) +#define DEF_USB_BLOCK S2255_USB_XFER_SIZE +#define LINE_SZ_4CIFS_NTSC 640 +#define LINE_SZ_2CIFS_NTSC 640 +#define LINE_SZ_1CIFS_NTSC 320 +#define LINE_SZ_4CIFS_PAL 704 +#define LINE_SZ_2CIFS_PAL 704 +#define LINE_SZ_1CIFS_PAL 352 +#define NUM_LINES_4CIFS_NTSC 240 +#define NUM_LINES_2CIFS_NTSC 240 +#define NUM_LINES_1CIFS_NTSC 240 +#define NUM_LINES_4CIFS_PAL 288 +#define NUM_LINES_2CIFS_PAL 288 +#define NUM_LINES_1CIFS_PAL 288 +#define LINE_SZ_DEF 640 +#define NUM_LINES_DEF 240 + + +/* predefined settings */ +#define FORMAT_NTSC 1 +#define FORMAT_PAL 2 + +#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ +#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ +#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ +/* SCALE_4CIFSI is the 2 fields interpolated into one */ +#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */ + +#define COLOR_YUVPL 1 /* YUV planar */ +#define COLOR_YUVPK 2 /* YUV packed */ +#define COLOR_Y8 4 /* monochrome */ +#define COLOR_JPG 5 /* JPEG */ + +#define MASK_COLOR 0x000000ff +#define MASK_JPG_QUALITY 0x0000ff00 +#define MASK_INPUT_TYPE 0x000f0000 +/* frame decimation. */ +#define FDEC_1 1 /* capture every frame. default */ +#define FDEC_2 2 /* capture every 2nd frame */ +#define FDEC_3 3 /* capture every 3rd frame */ +#define FDEC_5 5 /* capture every 5th frame */ + +/*------------------------------------------------------- + * Default mode parameters. + *-------------------------------------------------------*/ +#define DEF_SCALE SCALE_4CIFS +#define DEF_COLOR COLOR_YUVPL +#define DEF_FDEC FDEC_1 +#define DEF_BRIGHT 0 +#define DEF_CONTRAST 0x5c +#define DEF_SATURATION 0x80 +#define DEF_HUE 0 + +/* usb config commands */ +#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) +#define CMD_2255 0xc2255000 +#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) +#define CMD_START cpu_to_le32((CMD_2255 | 0x20)) +#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) +#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) + +struct s2255_mode { + u32 format; /* input video format (NTSC, PAL) */ + u32 scale; /* output video scale */ + u32 color; /* output video color format */ + u32 fdec; /* frame decimation */ + u32 bright; /* brightness */ + u32 contrast; /* contrast */ + u32 saturation; /* saturation */ + u32 hue; /* hue (NTSC only)*/ + u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/ + u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */ + u32 restart; /* if DSP requires restart */ +}; + + +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 + +/* frame structure */ +struct s2255_framei { + unsigned long size; + unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct s2255_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ +}; + +#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \ + DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \ + DEF_HUE, 0, DEF_USB_BLOCK, 0} + +struct s2255_dmaqueue { + struct list_head active; + struct s2255_dev *dev; +}; + +/* for firmware loading, fw_state */ +#define S2255_FW_NOTLOADED 0 +#define S2255_FW_LOADED_DSPWAIT 1 +#define S2255_FW_SUCCESS 2 +#define S2255_FW_FAILED 3 +#define S2255_FW_DISCONNECTING 4 +#define S2255_FW_MARKER cpu_to_le32(0x22552f2f) +/* 2255 read states */ +#define S2255_READ_IDLE 0 +#define S2255_READ_FRAME 1 +struct s2255_fw { + int fw_loaded; + int fw_size; + struct urb *fw_urb; + atomic_t fw_state; + void *pfw_data; + wait_queue_head_t wait_fw; + const struct firmware *fw; +}; + +struct s2255_pipeinfo { + u32 max_transfer_size; + u32 cur_transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *dev; /* back pointer to s2255_dev struct*/ + u32 err_count; + u32 idx; +}; + +struct s2255_fmt; /*forward declaration */ +struct s2255_dev; + +struct s2255_channel { + struct video_device vdev; + int resources; + struct s2255_dmaqueue vidq; + struct s2255_bufferi buffer; + struct s2255_mode mode; + /* jpeg compression */ + struct v4l2_jpegcompression jc; + /* capture parameters (for high quality mode full size) */ + struct v4l2_captureparm cap_parm; + int cur_frame; + int last_frame; + + int b_acquire; + /* allocated image size */ + unsigned long req_image_size; + /* received packet size */ + unsigned long pkt_size; + int bad_payload; + unsigned long frame_count; + /* if JPEG image */ + int jpg_size; + /* if channel configured to default state */ + int configured; + wait_queue_head_t wait_setmode; + int setmode_ready; + /* video status items */ + int vidstatus; + wait_queue_head_t wait_vidstatus; + int vidstatus_ready; + unsigned int width; + unsigned int height; + const struct s2255_fmt *fmt; + int idx; /* channel number on device, 0-3 */ +}; + + +struct s2255_dev { + struct s2255_channel channel[MAX_CHANNELS]; + struct v4l2_device v4l2_dev; + atomic_t num_channels; + int frames; + struct mutex lock; /* channels[].vdev.lock */ + struct usb_device *udev; + struct usb_interface *interface; + u8 read_endpoint; + struct timer_list timer; + struct s2255_fw *fw_data; + struct s2255_pipeinfo pipe; + u32 cc; /* current channel */ + int frame_ready; + int chn_ready; + spinlock_t slock; + /* dsp firmware version (f2255usb.bin) */ + int dsp_fw_ver; + u16 pid; /* product id */ +}; + +static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) +{ + return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); +} + +struct s2255_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* buffer for one video frame */ +struct s2255_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct s2255_fmt *fmt; +}; + +struct s2255_fh { + struct s2255_dev *dev; + struct videobuf_queue vb_vidq; + enum v4l2_buf_type type; + struct s2255_channel *channel; + int resources; +}; + +/* current cypress EEPROM firmware version */ +#define S2255_CUR_USB_FWVER ((3 << 8) | 12) +/* current DSP FW version */ +#define S2255_CUR_DSP_FWVER 10104 +/* Need DSP version 5+ for video status feature */ +#define S2255_MIN_DSP_STATUS 5 +#define S2255_MIN_DSP_COLORFILTER 8 +#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) + +/* private V4L2 controls */ + +/* + * The following chart displays how COLORFILTER should be set + * ========================================================= + * = fourcc = COLORFILTER = + * = =============================== + * = = 0 = 1 = + * ========================================================= + * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= + * = = s-video or = composite = + * = = B/W camera = input = + * ========================================================= + * = other = color, svideo = color, = + * = = = composite = + * ========================================================= + * + * Notes: + * channels 0-3 on 2255 are composite + * channels 0-1 on 2257 are composite, 2-3 are s-video + * If COLORFILTER is 0 with a composite color camera connected, + * the output will appear monochrome but hatching + * will occur. + * COLORFILTER is different from "color killer" and "color effects" + * for reasons above. + */ +#define S2255_V4L2_YC_ON 1 +#define S2255_V4L2_YC_OFF 0 +#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) + +/* frame prefix size (sent once every frame) */ +#define PREFIX_SIZE 512 + +/* Channels on box are in reverse order */ +static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0}; + +static int debug; +static int *s2255_debug = &debug; + +static int s2255_start_readpipe(struct s2255_dev *dev); +static void s2255_stop_readpipe(struct s2255_dev *dev); +static int s2255_start_acquire(struct s2255_channel *channel); +static int s2255_stop_acquire(struct s2255_channel *channel); +static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf, + int jpgsize); +static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode); +static int s2255_board_shutdown(struct s2255_dev *dev); +static void s2255_fwload_start(struct s2255_dev *dev, int reset); +static void s2255_destroy(struct s2255_dev *dev); +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, + u16 index, u16 value, void *buf, + s32 buf_len, int bOut); + +/* dev_err macro with driver name */ +#define S2255_DRIVER_NAME "s2255" +#define s2255_dev_err(dev, fmt, arg...) \ + dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) + +#define dprintk(level, fmt, arg...) \ + do { \ + if (*s2255_debug >= (level)) { \ + printk(KERN_DEBUG S2255_DRIVER_NAME \ + ": " fmt, ##arg); \ + } \ + } while (0) + +static struct usb_driver s2255_driver; + +/* Declare static vars that will be used as parameters */ +static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ + +/* start video number */ +static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ + +/* Enable jpeg capture. */ +static int jpeg_enable = 1; + +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); +module_param(vid_limit, int, 0644); +MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)"); +module_param(video_nr, int, 0644); +MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); +module_param(jpeg_enable, int, 0644); +MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1"); + +/* USB device table */ +#define USB_SENSORAY_VID 0x1943 +static struct usb_device_id s2255_table[] = { + {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, + {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ + { } /* Terminating entry */ +}; +MODULE_DEVICE_TABLE(usb, s2255_table); + +#define BUFFER_TIMEOUT msecs_to_jiffies(400) + +/* image formats. */ +/* JPEG formats must be defined last to support jpeg_enable parameter */ +static const struct s2255_fmt formats[] = { + { + .name = "4:2:2, planar, YUV422P", + .fourcc = V4L2_PIX_FMT_YUV422P, + .depth = 16 + + }, { + .name = "4:2:2, packed, YUYV", + .fourcc = V4L2_PIX_FMT_YUYV, + .depth = 16 + + }, { + .name = "4:2:2, packed, UYVY", + .fourcc = V4L2_PIX_FMT_UYVY, + .depth = 16 + }, { + .name = "8bpp GREY", + .fourcc = V4L2_PIX_FMT_GREY, + .depth = 8 + }, { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + }, { + .name = "MJPG", + .fourcc = V4L2_PIX_FMT_MJPEG, + .depth = 24 + } +}; + +static int norm_maxw(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; +} + +static int norm_maxh(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); +} + +static int norm_minw(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; +} + +static int norm_minh(struct video_device *vdev) +{ + return (vdev->current_norm & V4L2_STD_NTSC) ? + (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); +} + + +/* + * TODO: fixme: move YUV reordering to hardware + * converts 2255 planar format to yuyv or uyvy + */ +static void planar422p_to_yuv_packed(const unsigned char *in, + unsigned char *out, + int width, int height, + int fmt) +{ + unsigned char *pY; + unsigned char *pCb; + unsigned char *pCr; + unsigned long size = height * width; + unsigned int i; + pY = (unsigned char *)in; + pCr = (unsigned char *)in + height * width; + pCb = (unsigned char *)in + height * width + (height * width / 2); + for (i = 0; i < size * 2; i += 4) { + out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; + out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; + out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; + out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; + } + return; +} + +static void s2255_reset_dsppower(struct s2255_dev *dev) +{ + s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1); + msleep(10); + s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); + msleep(600); + s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1); + return; +} + +/* kickstarts the firmware loading. from probe + */ +static void s2255_timer(unsigned long user_data) +{ + struct s2255_fw *data = (struct s2255_fw *)user_data; + dprintk(100, "%s\n", __func__); + if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { + printk(KERN_ERR "s2255: can't submit urb\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } +} + + +/* this loads the firmware asynchronously. + Originally this was done synchroously in probe. + But it is better to load it asynchronously here than block + inside the probe function. Blocking inside probe affects boot time. + FW loading is triggered by the timer in the probe function +*/ +static void s2255_fwchunk_complete(struct urb *urb) +{ + struct s2255_fw *data = urb->context; + struct usb_device *udev = urb->dev; + int len; + dprintk(100, "%s: udev %p urb %p", __func__, udev, urb); + if (urb->status) { + dev_err(&udev->dev, "URB failed with status %d\n", urb->status); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } + if (data->fw_urb == NULL) { + s2255_dev_err(&udev->dev, "disconnected\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } +#define CHUNK_SIZE 512 + /* all USB transfers must be done with continuous kernel memory. + can't allocate more than 128k in current linux kernel, so + upload the firmware in chunks + */ + if (data->fw_loaded < data->fw_size) { + len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? + data->fw_size % CHUNK_SIZE : CHUNK_SIZE; + + if (len < CHUNK_SIZE) + memset(data->pfw_data, 0, CHUNK_SIZE); + + dprintk(100, "completed len %d, loaded %d \n", len, + data->fw_loaded); + + memcpy(data->pfw_data, + (char *) data->fw->data + data->fw_loaded, len); + + usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), + data->pfw_data, CHUNK_SIZE, + s2255_fwchunk_complete, data); + if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { + dev_err(&udev->dev, "failed submit URB\n"); + atomic_set(&data->fw_state, S2255_FW_FAILED); + /* wake up anything waiting for the firmware */ + wake_up(&data->wait_fw); + return; + } + data->fw_loaded += len; + } else { + atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); + dprintk(100, "%s: firmware upload complete\n", __func__); + } + return; + +} + +static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) +{ + struct s2255_dmaqueue *dma_q = &channel->vidq; + struct s2255_buffer *buf; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + unsigned long flags = 0; + int rc = 0; + spin_lock_irqsave(&dev->slock, flags); + if (list_empty(&dma_q->active)) { + dprintk(1, "No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct s2255_buffer, vb.queue); + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + s2255_fillbuff(channel, buf, jpgsize); + wake_up(&buf->vb.done); + dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&dev->slock, flags); + return rc; +} + +static const struct s2255_fmt *format_by_fourcc(int fourcc) +{ + unsigned int i; + for (i = 0; i < ARRAY_SIZE(formats); i++) { + if (-1 == formats[i].fourcc) + continue; + if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) + continue; + if (formats[i].fourcc == fourcc) + return formats + i; + } + return NULL; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab + * Ted Walther + * John Sokol + * http://v4l.videotechnology.com/ + * + */ +static void s2255_fillbuff(struct s2255_channel *channel, + struct s2255_buffer *buf, int jpgsize) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + + if (!vbuf) + return; + last_frame = channel->last_frame; + if (last_frame != -1) { + tmpbuf = + (const char *)channel->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + planar422p_to_yuv_packed((const unsigned char *)tmpbuf, + vbuf, buf->vb.width, + buf->vb.height, + buf->fmt->fourcc); + break; + case V4L2_PIX_FMT_GREY: + memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); + break; + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_MJPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + case V4L2_PIX_FMT_YUV422P: + memcpy(vbuf, tmpbuf, + buf->vb.width * buf->vb.height * 2); + break; + default: + printk(KERN_DEBUG "s2255: unknown format?\n"); + } + channel->last_frame = -1; + } else { + printk(KERN_ERR "s2255: =======no frame\n"); + return; + + } + dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n", + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = channel->frame_count * 2; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +} + + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; + *size = channel->width * channel->height * (channel->fmt->depth >> 3); + + if (0 == *count) + *count = S2255_DEF_BUFS; + + if (*size * *count > vid_limit * 1024 * 1024) + *count = (vid_limit * 1024 * 1024) / *size; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf) +{ + dprintk(4, "%s\n", __func__); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + int rc; + int w = channel->width; + int h = channel->height; + dprintk(4, "%s, field=%d\n", __func__, field); + if (channel->fmt == NULL) + return -EINVAL; + + if ((w < norm_minw(&channel->vdev)) || + (w > norm_maxw(&channel->vdev)) || + (h < norm_minh(&channel->vdev)) || + (h > norm_maxh(&channel->vdev))) { + dprintk(4, "invalid buffer prepare\n"); + return -EINVAL; + } + buf->vb.size = w * h * (channel->fmt->depth >> 3); + if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) { + dprintk(4, "invalid buffer prepare\n"); + return -EINVAL; + } + + buf->fmt = channel->fmt; + buf->vb.width = w; + buf->vb.height = h; + buf->vb.field = field; + + if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct s2255_fh *fh = vq->priv_data; + struct s2255_channel *channel = fh->channel; + struct s2255_dmaqueue *vidq = &channel->vidq; + dprintk(1, "%s\n", __func__); + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &vidq->active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); + struct s2255_fh *fh = vq->priv_data; + dprintk(4, "%s %d\n", __func__, fh->channel->idx); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops s2255_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + + +static int res_get(struct s2255_fh *fh) +{ + struct s2255_channel *channel = fh->channel; + /* is it free? */ + if (channel->resources) + return 0; /* no, someone else uses it */ + /* it's free, grab it */ + channel->resources = 1; + fh->resources = 1; + dprintk(1, "s2255: res: get\n"); + return 1; +} + +static int res_locked(struct s2255_fh *fh) +{ + return fh->channel->resources; +} + +static int res_check(struct s2255_fh *fh) +{ + return fh->resources; +} + + +static void res_free(struct s2255_fh *fh) +{ + struct s2255_channel *channel = fh->channel; + channel->resources = 0; + fh->resources = 0; + dprintk(1, "res: put\n"); +} + +static int vidioc_querymenu(struct file *file, void *priv, + struct v4l2_querymenu *qmenu) +{ + static const char *colorfilter[] = { + "Off", + "On", + NULL + }; + if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { + int i; + const char **menu_items = colorfilter; + for (i = 0; i < qmenu->index && menu_items[i]; i++) + ; /* do nothing (from v4l2-common.c) */ + if (menu_items[i] == NULL || menu_items[i][0] == '\0') + return -EINVAL; + strlcpy(qmenu->name, menu_items[qmenu->index], + sizeof(qmenu->name)); + return 0; + } + return v4l2_ctrl_query_menu(qmenu, NULL, NULL); +} + +static int vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + strlcpy(cap->driver, "s2255", sizeof(cap->driver)); + strlcpy(cap->card, "s2255", sizeof(cap->card)); + usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; + return 0; +} + +static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_fmtdesc *f) +{ + int index = f->index; + + if (index >= ARRAY_SIZE(formats)) + return -EINVAL; + if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || + (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) + return -EINVAL; + dprintk(4, "name %s\n", formats[index].name); + strlcpy(f->description, formats[index].name, sizeof(f->description)); + f->pixelformat = formats[index].fourcc; + return 0; +} + +static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + + f->fmt.pix.width = channel->width; + f->fmt.pix.height = channel->height; + f->fmt.pix.field = fh->vb_vidq.field; + f->fmt.pix.pixelformat = channel->fmt->fourcc; + f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3); + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + return 0; +} + +static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + const struct s2255_fmt *fmt; + enum v4l2_field field; + int b_any_field = 0; + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + int is_ntsc; + is_ntsc = + (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + if (fmt == NULL) + return -EINVAL; + + field = f->fmt.pix.field; + if (field == V4L2_FIELD_ANY) + b_any_field = 1; + + dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n", + __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); + if (is_ntsc) { + /* NTSC */ + if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { + f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; + if (b_any_field) { + field = V4L2_FIELD_SEQ_TB; + } else if (!((field == V4L2_FIELD_INTERLACED) || + (field == V4L2_FIELD_SEQ_TB) || + (field == V4L2_FIELD_INTERLACED_TB))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } else { + f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; + if (b_any_field) { + field = V4L2_FIELD_TOP; + } else if (!((field == V4L2_FIELD_TOP) || + (field == V4L2_FIELD_BOTTOM))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + + } + if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; + else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_2CIFS_NTSC; + else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC) + f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; + else + f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; + } else { + /* PAL */ + if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { + f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; + if (b_any_field) { + field = V4L2_FIELD_SEQ_TB; + } else if (!((field == V4L2_FIELD_INTERLACED) || + (field == V4L2_FIELD_SEQ_TB) || + (field == V4L2_FIELD_INTERLACED_TB))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } else { + f->fmt.pix.height = NUM_LINES_1CIFS_PAL; + if (b_any_field) { + field = V4L2_FIELD_TOP; + } else if (!((field == V4L2_FIELD_TOP) || + (field == V4L2_FIELD_BOTTOM))) { + dprintk(1, "unsupported field setting\n"); + return -EINVAL; + } + } + if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { + f->fmt.pix.width = LINE_SZ_4CIFS_PAL; + field = V4L2_FIELD_SEQ_TB; + } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { + f->fmt.pix.width = LINE_SZ_2CIFS_PAL; + field = V4L2_FIELD_TOP; + } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { + f->fmt.pix.width = LINE_SZ_1CIFS_PAL; + field = V4L2_FIELD_TOP; + } else { + f->fmt.pix.width = LINE_SZ_1CIFS_PAL; + field = V4L2_FIELD_TOP; + } + } + f->fmt.pix.field = field; + f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + dprintk(50, "%s: set width %d height %d field %d\n", __func__, + f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); + return 0; +} + +static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + const struct s2255_fmt *fmt; + struct videobuf_queue *q = &fh->vb_vidq; + struct s2255_mode mode; + int ret; + + ret = vidioc_try_fmt_vid_cap(file, fh, f); + + if (ret < 0) + return ret; + + fmt = format_by_fourcc(f->fmt.pix.pixelformat); + + if (fmt == NULL) + return -EINVAL; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + dprintk(1, "queue busy\n"); + ret = -EBUSY; + goto out_s_fmt; + } + + if (res_locked(fh)) { + dprintk(1, "%s: channel busy\n", __func__); + ret = -EBUSY; + goto out_s_fmt; + } + mode = channel->mode; + channel->fmt = fmt; + channel->width = f->fmt.pix.width; + channel->height = f->fmt.pix.height; + fh->vb_vidq.field = f->fmt.pix.field; + fh->type = f->type; + if (channel->width > norm_minw(&channel->vdev)) { + if (channel->height > norm_minh(&channel->vdev)) { + if (channel->cap_parm.capturemode & + V4L2_MODE_HIGHQUALITY) + mode.scale = SCALE_4CIFSI; + else + mode.scale = SCALE_4CIFS; + } else + mode.scale = SCALE_2CIFS; + + } else { + mode.scale = SCALE_1CIFS; + } + /* color mode */ + switch (channel->fmt->fourcc) { + case V4L2_PIX_FMT_GREY: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_Y8; + break; + case V4L2_PIX_FMT_JPEG: + case V4L2_PIX_FMT_MJPEG: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_JPG; + mode.color |= (channel->jc.quality << 8); + break; + case V4L2_PIX_FMT_YUV422P: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPL; + break; + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_UYVY: + default: + mode.color &= ~MASK_COLOR; + mode.color |= COLOR_YUVPK; + break; + } + if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR)) + mode.restart = 1; + else if (mode.scale != channel->mode.scale) + mode.restart = 1; + else if (mode.format != channel->mode.format) + mode.restart = 1; + channel->mode = mode; + (void) s2255_set_mode(channel, &mode); + ret = 0; +out_s_fmt: + mutex_unlock(&q->vb_lock); + return ret; +} + +static int vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_reqbufs(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_querybuf(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_qbuf(&fh->vb_vidq, p); + return rc; +} + +static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) +{ + int rc; + struct s2255_fh *fh = priv; + rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +/* write to the configuration pipe, synchronously */ +static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, + int size) +{ + int pipe; + int done; + long retval = -1; + if (udev) { + pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); + retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); + } + return retval; +} + +static u32 get_transfer_size(struct s2255_mode *mode) +{ + int linesPerFrame = LINE_SZ_DEF; + int pixelsPerLine = NUM_LINES_DEF; + u32 outImageSize; + u32 usbInSize; + unsigned int mask_mult; + + if (mode == NULL) + return 0; + + if (mode->format == FORMAT_NTSC) { + switch (mode->scale) { + case SCALE_4CIFS: + case SCALE_4CIFSI: + linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; + pixelsPerLine = LINE_SZ_4CIFS_NTSC; + break; + case SCALE_2CIFS: + linesPerFrame = NUM_LINES_2CIFS_NTSC; + pixelsPerLine = LINE_SZ_2CIFS_NTSC; + break; + case SCALE_1CIFS: + linesPerFrame = NUM_LINES_1CIFS_NTSC; + pixelsPerLine = LINE_SZ_1CIFS_NTSC; + break; + default: + break; + } + } else if (mode->format == FORMAT_PAL) { + switch (mode->scale) { + case SCALE_4CIFS: + case SCALE_4CIFSI: + linesPerFrame = NUM_LINES_4CIFS_PAL * 2; + pixelsPerLine = LINE_SZ_4CIFS_PAL; + break; + case SCALE_2CIFS: + linesPerFrame = NUM_LINES_2CIFS_PAL; + pixelsPerLine = LINE_SZ_2CIFS_PAL; + break; + case SCALE_1CIFS: + linesPerFrame = NUM_LINES_1CIFS_PAL; + pixelsPerLine = LINE_SZ_1CIFS_PAL; + break; + default: + break; + } + } + outImageSize = linesPerFrame * pixelsPerLine; + if ((mode->color & MASK_COLOR) != COLOR_Y8) { + /* 2 bytes/pixel if not monochrome */ + outImageSize *= 2; + } + + /* total bytes to send including prefix and 4K padding; + must be a multiple of USB_READ_SIZE */ + usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ + mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; + /* if size not a multiple of USB_READ_SIZE */ + if (usbInSize & ~mask_mult) + usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); + return usbInSize; +} + +static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) +{ + struct device *dev = &sdev->udev->dev; + dev_info(dev, "------------------------------------------------\n"); + dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); + dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); + dev_info(dev, "bright: 0x%x\n", mode->bright); + dev_info(dev, "------------------------------------------------\n"); +} + +/* + * set mode is the function which controls the DSP. + * the restart parameter in struct s2255_mode should be set whenever + * the image size could change via color format, video system or image + * size. + * When the restart parameter is set, we sleep for ONE frame to allow the + * DSP time to get the new frame + */ +static int s2255_set_mode(struct s2255_channel *channel, + struct s2255_mode *mode) +{ + int res; + __le32 *buffer; + unsigned long chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + dprintk(3, "%s channel: %d\n", __func__, channel->idx); + /* if JPEG, set the quality */ + if ((mode->color & MASK_COLOR) == COLOR_JPG) { + mode->color &= ~MASK_COLOR; + mode->color |= COLOR_JPG; + mode->color &= ~MASK_JPG_QUALITY; + mode->color |= (channel->jc.quality << 8); + } + /* save the mode */ + channel->mode = *mode; + channel->req_image_size = get_transfer_size(mode); + dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size); + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + /* set the mode */ + buffer[0] = IN_DATA_TOKEN; + buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[2] = CMD_SET_MODE; + memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode)); + channel->setmode_ready = 0; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (debug) + s2255_print_cfg(dev, mode); + kfree(buffer); + /* wait at least 3 frames before continuing */ + if (mode->restart) { + wait_event_timeout(channel->wait_setmode, + (channel->setmode_ready != 0), + msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); + if (channel->setmode_ready != 1) { + printk(KERN_DEBUG "s2255: no set mode response\n"); + res = -EFAULT; + } + } + /* clear the restart flag */ + channel->mode.restart = 0; + dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res); + return res; +} + +static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus) +{ + int res; + __le32 *buffer; + u32 chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + dprintk(4, "%s chan %d\n", __func__, channel->idx); + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + /* form the get vid status command */ + buffer[0] = IN_DATA_TOKEN; + buffer[1] = (__le32) cpu_to_le32(chn_rev); + buffer[2] = CMD_STATUS; + *pstatus = 0; + channel->vidstatus_ready = 0; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + kfree(buffer); + wait_event_timeout(channel->wait_vidstatus, + (channel->vidstatus_ready != 0), + msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); + if (channel->vidstatus_ready != 1) { + printk(KERN_DEBUG "s2255: no vidstatus response\n"); + res = -EFAULT; + } + *pstatus = channel->vidstatus; + dprintk(4, "%s, vid status %d\n", __func__, *pstatus); + return res; +} + +static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) +{ + int res; + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + int j; + dprintk(4, "%s\n", __func__); + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_err(&dev->udev->dev, "invalid fh type0\n"); + return -EINVAL; + } + if (i != fh->type) { + dev_err(&dev->udev->dev, "invalid fh type1\n"); + return -EINVAL; + } + + if (!res_get(fh)) { + s2255_dev_err(&dev->udev->dev, "stream busy\n"); + return -EBUSY; + } + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; + channel->frame_count = 0; + for (j = 0; j < SYS_FRAMES; j++) { + channel->buffer.frame[j].ulState = S2255_READ_IDLE; + channel->buffer.frame[j].cur_size = 0; + } + res = videobuf_streamon(&fh->vb_vidq); + if (res == 0) { + s2255_start_acquire(channel); + channel->b_acquire = 1; + } else + res_free(fh); + + return res; +} + +static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) +{ + struct s2255_fh *fh = priv; + dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx); + if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + printk(KERN_ERR "invalid fh type0\n"); + return -EINVAL; + } + if (i != fh->type) { + printk(KERN_ERR "invalid type i\n"); + return -EINVAL; + } + s2255_stop_acquire(fh->channel); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + return 0; +} + +static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) +{ + struct s2255_fh *fh = priv; + struct s2255_mode mode; + struct videobuf_queue *q = &fh->vb_vidq; + int ret = 0; + mutex_lock(&q->vb_lock); + if (videobuf_queue_is_busy(q)) { + dprintk(1, "queue busy\n"); + ret = -EBUSY; + goto out_s_std; + } + if (res_locked(fh)) { + dprintk(1, "can't change standard after started\n"); + ret = -EBUSY; + goto out_s_std; + } + mode = fh->channel->mode; + if (*i & V4L2_STD_NTSC) { + dprintk(4, "%s NTSC\n", __func__); + /* if changing format, reset frame decimation/intervals */ + if (mode.format != FORMAT_NTSC) { + mode.restart = 1; + mode.format = FORMAT_NTSC; + mode.fdec = FDEC_1; + } + } else if (*i & V4L2_STD_PAL) { + dprintk(4, "%s PAL\n", __func__); + if (mode.format != FORMAT_PAL) { + mode.restart = 1; + mode.format = FORMAT_PAL; + mode.fdec = FDEC_1; + } + } else { + ret = -EINVAL; + } + if (mode.restart) + s2255_set_mode(fh->channel, &mode); +out_s_std: + mutex_unlock(&q->vb_lock); + return ret; +} + +/* Sensoray 2255 is a multiple channel capture device. + It does not have a "crossbar" of inputs. + We use one V4L device per channel. The user must + be aware that certain combinations are not allowed. + For instance, you cannot do full FPS on more than 2 channels(2 videodevs) + at once in color(you can do full fps on 4 channels with greyscale. +*/ +static int vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + u32 status = 0; + if (inp->index != 0) + return -EINVAL; + inp->type = V4L2_INPUT_TYPE_CAMERA; + inp->std = S2255_NORMS; + inp->status = 0; + if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { + int rc; + rc = s2255_cmd_status(fh->channel, &status); + dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); + if (rc == 0) + inp->status = (status & 0x01) ? 0 + : V4L2_IN_ST_NO_SIGNAL; + } + switch (dev->pid) { + case 0x2255: + default: + strlcpy(inp->name, "Composite", sizeof(inp->name)); + break; + case 0x2257: + strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video", + sizeof(inp->name)); + break; + } + return 0; +} + +static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} +static int vidioc_s_input(struct file *file, void *priv, unsigned int i) +{ + if (i > 0) + return -EINVAL; + return 0; +} + +/* --- controls ---------------------------------------------- */ +static int vidioc_queryctrl(struct file *file, void *priv, + struct v4l2_queryctrl *qc) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = fh->dev; + switch (qc->id) { + case V4L2_CID_BRIGHTNESS: + v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); + break; + case V4L2_CID_CONTRAST: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); + break; + case V4L2_CID_SATURATION: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); + break; + case V4L2_CID_HUE: + v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE); + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (channel->idx > 1)) + return -EINVAL; + strlcpy(qc->name, "Color Filter", sizeof(qc->name)); + qc->type = V4L2_CTRL_TYPE_MENU; + qc->minimum = 0; + qc->maximum = 1; + qc->step = 1; + qc->default_value = 1; + qc->flags = 0; + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d\n", __func__, qc->id); + return 0; +} + +static int vidioc_g_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct s2255_fh *fh = priv; + struct s2255_dev *dev = fh->dev; + struct s2255_channel *channel = fh->channel; + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + ctrl->value = channel->mode.bright; + break; + case V4L2_CID_CONTRAST: + ctrl->value = channel->mode.contrast; + break; + case V4L2_CID_SATURATION: + ctrl->value = channel->mode.saturation; + break; + case V4L2_CID_HUE: + ctrl->value = channel->mode.hue; + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (channel->idx > 1)) + return -EINVAL; + ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); + break; + default: + return -EINVAL; + } + dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); + return 0; +} + +static int vidioc_s_ctrl(struct file *file, void *priv, + struct v4l2_control *ctrl) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + struct s2255_mode mode; + mode = channel->mode; + dprintk(4, "%s\n", __func__); + /* update the mode to the corresponding value */ + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + mode.bright = ctrl->value; + break; + case V4L2_CID_CONTRAST: + mode.contrast = ctrl->value; + break; + case V4L2_CID_HUE: + mode.hue = ctrl->value; + break; + case V4L2_CID_SATURATION: + mode.saturation = ctrl->value; + break; + case V4L2_CID_PRIVATE_COLORFILTER: + if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + return -EINVAL; + if ((dev->pid == 0x2257) && (channel->idx > 1)) + return -EINVAL; + mode.color &= ~MASK_INPUT_TYPE; + mode.color |= ((ctrl->value ? 0 : 1) << 16); + break; + default: + return -EINVAL; + } + mode.restart = 0; + /* set mode here. Note: stream does not need restarted. + some V4L programs restart stream unnecessarily + after a s_crtl. + */ + s2255_set_mode(fh->channel, &mode); + return 0; +} + +static int vidioc_g_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + *jc = channel->jc; + dprintk(2, "%s: quality %d\n", __func__, jc->quality); + return 0; +} + +static int vidioc_s_jpegcomp(struct file *file, void *priv, + struct v4l2_jpegcompression *jc) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + if (jc->quality < 0 || jc->quality > 100) + return -EINVAL; + channel->jc.quality = jc->quality; + dprintk(2, "%s: quality %d\n", __func__, jc->quality); + return 0; +} + +static int vidioc_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct s2255_fh *fh = priv; + __u32 def_num, def_dem; + struct s2255_channel *channel = fh->channel; + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + memset(sp, 0, sizeof(struct v4l2_streamparm)); + sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; + sp->parm.capture.capturemode = channel->cap_parm.capturemode; + def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000; + sp->parm.capture.timeperframe.denominator = def_dem; + switch (channel->mode.fdec) { + default: + case FDEC_1: + sp->parm.capture.timeperframe.numerator = def_num; + break; + case FDEC_2: + sp->parm.capture.timeperframe.numerator = def_num * 2; + break; + case FDEC_3: + sp->parm.capture.timeperframe.numerator = def_num * 3; + break; + case FDEC_5: + sp->parm.capture.timeperframe.numerator = def_num * 5; + break; + } + dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator); + return 0; +} + +static int vidioc_s_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct s2255_fh *fh = priv; + struct s2255_channel *channel = fh->channel; + struct s2255_mode mode; + int fdec = FDEC_1; + __u32 def_num, def_dem; + if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + mode = channel->mode; + /* high quality capture mode requires a stream restart */ + if (channel->cap_parm.capturemode + != sp->parm.capture.capturemode && res_locked(fh)) + return -EBUSY; + def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; + def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; + if (def_dem != sp->parm.capture.timeperframe.denominator) + sp->parm.capture.timeperframe.numerator = def_num; + else if (sp->parm.capture.timeperframe.numerator <= def_num) + sp->parm.capture.timeperframe.numerator = def_num; + else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { + sp->parm.capture.timeperframe.numerator = def_num * 2; + fdec = FDEC_2; + } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { + sp->parm.capture.timeperframe.numerator = def_num * 3; + fdec = FDEC_3; + } else { + sp->parm.capture.timeperframe.numerator = def_num * 5; + fdec = FDEC_5; + } + mode.fdec = fdec; + sp->parm.capture.timeperframe.denominator = def_dem; + s2255_set_mode(channel, &mode); + dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", + __func__, + sp->parm.capture.capturemode, + sp->parm.capture.timeperframe.numerator, + sp->parm.capture.timeperframe.denominator, fdec); + return 0; +} + +static int vidioc_enum_frameintervals(struct file *file, void *priv, + struct v4l2_frmivalenum *fe) +{ + int is_ntsc = 0; +#define NUM_FRAME_ENUMS 4 + int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; + if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS) + return -EINVAL; + switch (fe->width) { + case 640: + if (fe->height != 240 && fe->height != 480) + return -EINVAL; + is_ntsc = 1; + break; + case 320: + if (fe->height != 240) + return -EINVAL; + is_ntsc = 1; + break; + case 704: + if (fe->height != 288 && fe->height != 576) + return -EINVAL; + break; + case 352: + if (fe->height != 288) + return -EINVAL; + break; + default: + return -EINVAL; + } + fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; + fe->discrete.denominator = is_ntsc ? 30000 : 25000; + fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; + dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator, + fe->discrete.denominator); + return 0; +} + +static int __s2255_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct s2255_channel *channel = video_drvdata(file); + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); + struct s2255_fh *fh; + enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int state; + dprintk(1, "s2255: open called (dev=%s)\n", + video_device_node_name(vdev)); + state = atomic_read(&dev->fw_data->fw_state); + switch (state) { + case S2255_FW_DISCONNECTING: + return -ENODEV; + case S2255_FW_FAILED: + s2255_dev_err(&dev->udev->dev, + "firmware load failed. retrying.\n"); + s2255_fwload_start(dev, 1); + wait_event_timeout(dev->fw_data->wait_fw, + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); + break; + case S2255_FW_NOTLOADED: + case S2255_FW_LOADED_DSPWAIT: + /* give S2255_LOAD_TIMEOUT time for firmware to load in case + driver loaded and then device immediately opened */ + printk(KERN_INFO "%s waiting for firmware load\n", __func__); + wait_event_timeout(dev->fw_data->wait_fw, + ((atomic_read(&dev->fw_data->fw_state) + == S2255_FW_SUCCESS) || + (atomic_read(&dev->fw_data->fw_state) + == S2255_FW_DISCONNECTING)), + msecs_to_jiffies(S2255_LOAD_TIMEOUT)); + /* state may have changed, re-read */ + state = atomic_read(&dev->fw_data->fw_state); + break; + case S2255_FW_SUCCESS: + default: + break; + } + /* state may have changed in above switch statement */ + switch (state) { + case S2255_FW_SUCCESS: + break; + case S2255_FW_FAILED: + printk(KERN_INFO "2255 firmware load failed.\n"); + return -ENODEV; + case S2255_FW_DISCONNECTING: + printk(KERN_INFO "%s: disconnecting\n", __func__); + return -ENODEV; + case S2255_FW_LOADED_DSPWAIT: + case S2255_FW_NOTLOADED: + printk(KERN_INFO "%s: firmware not loaded yet" + "please try again later\n", + __func__); + /* + * Timeout on firmware load means device unusable. + * Set firmware failure state. + * On next s2255_open the firmware will be reloaded. + */ + atomic_set(&dev->fw_data->fw_state, + S2255_FW_FAILED); + return -EAGAIN; + default: + printk(KERN_INFO "%s: unknown state\n", __func__); + return -EFAULT; + } + /* allocate + initialize per filehandle data */ + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (NULL == fh) + return -ENOMEM; + file->private_data = fh; + fh->dev = dev; + fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + fh->channel = channel; + if (!channel->configured) { + /* configure channel to default state */ + channel->fmt = &formats[0]; + s2255_set_mode(channel, &channel->mode); + channel->configured = 1; + } + dprintk(1, "%s: dev=%s type=%s\n", __func__, + video_device_node_name(vdev), v4l2_type_names[type]); + dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__, + (unsigned long)fh, (unsigned long)dev, + (unsigned long)&channel->vidq); + dprintk(4, "%s: list_empty active=%d\n", __func__, + list_empty(&channel->vidq.active)); + videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, + NULL, &dev->slock, + fh->type, + V4L2_FIELD_INTERLACED, + sizeof(struct s2255_buffer), + fh, vdev->lock); + return 0; +} + +static int s2255_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + int ret; + + if (mutex_lock_interruptible(vdev->lock)) + return -ERESTARTSYS; + ret = __s2255_open(file); + mutex_unlock(vdev->lock); + return ret; +} + +static unsigned int s2255_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + int rc; + dprintk(100, "%s\n", __func__); + if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) + return POLLERR; + mutex_lock(&dev->lock); + rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); + mutex_unlock(&dev->lock); + return rc; +} + +static void s2255_destroy(struct s2255_dev *dev) +{ + /* board shutdown stops the read pipe if it is running */ + s2255_board_shutdown(dev); + /* make sure firmware still not trying to load */ + del_timer(&dev->timer); /* only started in .probe and .open */ + if (dev->fw_data->fw_urb) { + usb_kill_urb(dev->fw_data->fw_urb); + usb_free_urb(dev->fw_data->fw_urb); + dev->fw_data->fw_urb = NULL; + } + release_firmware(dev->fw_data->fw); + kfree(dev->fw_data->pfw_data); + kfree(dev->fw_data); + /* reset the DSP so firmware can be reloaded next time */ + s2255_reset_dsppower(dev); + mutex_destroy(&dev->lock); + usb_put_dev(dev->udev); + v4l2_device_unregister(&dev->v4l2_dev); + dprintk(1, "%s", __func__); + kfree(dev); +} + +static int s2255_release(struct file *file) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + struct video_device *vdev = video_devdata(file); + struct s2255_channel *channel = fh->channel; + if (!dev) + return -ENODEV; + mutex_lock(&dev->lock); + /* turn off stream */ + if (res_check(fh)) { + if (channel->b_acquire) + s2255_stop_acquire(fh->channel); + videobuf_streamoff(&fh->vb_vidq); + res_free(fh); + } + videobuf_mmap_free(&fh->vb_vidq); + mutex_unlock(&dev->lock); + dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); + kfree(fh); + return 0; +} + +static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) +{ + struct s2255_fh *fh = file->private_data; + struct s2255_dev *dev = fh->dev; + int ret; + + if (!fh) + return -ENODEV; + dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma); + if (mutex_lock_interruptible(&dev->lock)) + return -ERESTARTSYS; + ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); + mutex_unlock(&dev->lock); + dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__, + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; +} + +static const struct v4l2_file_operations s2255_fops_v4l = { + .owner = THIS_MODULE, + .open = s2255_open, + .release = s2255_release, + .poll = s2255_poll, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = s2255_mmap_v4l, +}; + +static const struct v4l2_ioctl_ops s2255_ioctl_ops = { + .vidioc_querymenu = vidioc_querymenu, + .vidioc_querycap = vidioc_querycap, + .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, + .vidioc_reqbufs = vidioc_reqbufs, + .vidioc_querybuf = vidioc_querybuf, + .vidioc_qbuf = vidioc_qbuf, + .vidioc_dqbuf = vidioc_dqbuf, + .vidioc_s_std = vidioc_s_std, + .vidioc_enum_input = vidioc_enum_input, + .vidioc_g_input = vidioc_g_input, + .vidioc_s_input = vidioc_s_input, + .vidioc_queryctrl = vidioc_queryctrl, + .vidioc_g_ctrl = vidioc_g_ctrl, + .vidioc_s_ctrl = vidioc_s_ctrl, + .vidioc_streamon = vidioc_streamon, + .vidioc_streamoff = vidioc_streamoff, + .vidioc_s_jpegcomp = vidioc_s_jpegcomp, + .vidioc_g_jpegcomp = vidioc_g_jpegcomp, + .vidioc_s_parm = vidioc_s_parm, + .vidioc_g_parm = vidioc_g_parm, + .vidioc_enum_frameintervals = vidioc_enum_frameintervals, +}; + +static void s2255_video_device_release(struct video_device *vdev) +{ + struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); + dprintk(4, "%s, chnls: %d \n", __func__, + atomic_read(&dev->num_channels)); + if (atomic_dec_and_test(&dev->num_channels)) + s2255_destroy(dev); + return; +} + +static struct video_device template = { + .name = "s2255v", + .fops = &s2255_fops_v4l, + .ioctl_ops = &s2255_ioctl_ops, + .release = s2255_video_device_release, + .tvnorms = S2255_NORMS, + .current_norm = V4L2_STD_NTSC_M, +}; + +static int s2255_probe_v4l(struct s2255_dev *dev) +{ + int ret; + int i; + int cur_nr = video_nr; + struct s2255_channel *channel; + ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); + if (ret) + return ret; + /* initialize all video 4 linux */ + /* register 4 video devices */ + for (i = 0; i < MAX_CHANNELS; i++) { + channel = &dev->channel[i]; + INIT_LIST_HEAD(&channel->vidq.active); + channel->vidq.dev = dev; + /* register 4 video devices */ + channel->vdev = template; + channel->vdev.lock = &dev->lock; + channel->vdev.v4l2_dev = &dev->v4l2_dev; + video_set_drvdata(&channel->vdev, channel); + if (video_nr == -1) + ret = video_register_device(&channel->vdev, + VFL_TYPE_GRABBER, + video_nr); + else + ret = video_register_device(&channel->vdev, + VFL_TYPE_GRABBER, + cur_nr + i); + + if (ret) { + dev_err(&dev->udev->dev, + "failed to register video device!\n"); + break; + } + atomic_inc(&dev->num_channels); + v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", + video_device_node_name(&channel->vdev)); + + } + printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n", + S2255_VERSION); + /* if no channels registered, return error and probe will fail*/ + if (atomic_read(&dev->num_channels) == 0) { + v4l2_device_unregister(&dev->v4l2_dev); + return ret; + } + if (atomic_read(&dev->num_channels) != MAX_CHANNELS) + printk(KERN_WARNING "s2255: Not all channels available.\n"); + return 0; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process( call this + * function again). + * + * Received frame structure: + * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) + * bytes 4-7: channel: 0-3 + * bytes 8-11: payload size: size of the frame + * bytes 12-payloadsize+12: frame data + */ +static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) +{ + char *pdest; + u32 offset = 0; + int bframe = 0; + char *psrc; + unsigned long copy_size; + unsigned long size; + s32 idx = -1; + struct s2255_framei *frm; + unsigned char *pdata; + struct s2255_channel *channel; + dprintk(100, "buffer to user\n"); + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; + if (frm->ulState == S2255_READ_IDLE) { + int jj; + unsigned int cc; + __le32 *pdword; /*data from dsp is little endian */ + int payload; + /* search for marker codes */ + pdata = (unsigned char *)pipe_info->transfer_buffer; + pdword = (__le32 *)pdata; + for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { + switch (*pdword) { + case S2255_MARKER_FRAME: + dprintk(4, "found frame marker at offset:" + " %d [%x %x]\n", jj, pdata[0], + pdata[1]); + offset = jj + PREFIX_SIZE; + bframe = 1; + cc = le32_to_cpu(pdword[1]); + if (cc >= MAX_CHANNELS) { + printk(KERN_ERR + "bad channel\n"); + return -EINVAL; + } + /* reverse it */ + dev->cc = G_chnmap[cc]; + channel = &dev->channel[dev->cc]; + payload = le32_to_cpu(pdword[3]); + if (payload > channel->req_image_size) { + channel->bad_payload++; + /* discard the bad frame */ + return -EINVAL; + } + channel->pkt_size = payload; + channel->jpg_size = le32_to_cpu(pdword[4]); + break; + case S2255_MARKER_RESPONSE: + + pdata += DEF_USB_BLOCK; + jj += DEF_USB_BLOCK; + if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) + break; + cc = G_chnmap[le32_to_cpu(pdword[1])]; + if (cc >= MAX_CHANNELS) + break; + channel = &dev->channel[cc]; + switch (pdword[2]) { + case S2255_RESPONSE_SETMODE: + /* check if channel valid */ + /* set mode ready */ + channel->setmode_ready = 1; + wake_up(&channel->wait_setmode); + dprintk(5, "setmode ready %d\n", cc); + break; + case S2255_RESPONSE_FW: + dev->chn_ready |= (1 << cc); + if ((dev->chn_ready & 0x0f) != 0x0f) + break; + /* all channels ready */ + printk(KERN_INFO "s2255: fw loaded\n"); + atomic_set(&dev->fw_data->fw_state, + S2255_FW_SUCCESS); + wake_up(&dev->fw_data->wait_fw); + break; + case S2255_RESPONSE_STATUS: + channel->vidstatus = le32_to_cpu(pdword[3]); + channel->vidstatus_ready = 1; + wake_up(&channel->wait_vidstatus); + dprintk(5, "got vidstatus %x chan %d\n", + le32_to_cpu(pdword[3]), cc); + break; + default: + printk(KERN_INFO "s2255 unknown resp\n"); + } + default: + pdata++; + break; + } + if (bframe) + break; + } /* for */ + if (!bframe) + return -EINVAL; + } + channel = &dev->channel[dev->cc]; + idx = channel->cur_frame; + frm = &channel->buffer.frame[idx]; + /* search done. now find out if should be acquiring on this channel */ + if (!channel->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = S2255_READ_IDLE; + return -EINVAL; + } + + if (frm->ulState == S2255_READ_IDLE) { + frm->ulState = S2255_READ_FRAME; + frm->cur_size = 0; + } + + /* skip the marker 512 bytes (and offset if out of sync) */ + psrc = (u8 *)pipe_info->transfer_buffer + offset; + + + if (frm->lpvbits == NULL) { + dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d", + frm, dev, dev->cc, idx); + return -ENOMEM; + } + + pdest = frm->lpvbits + frm->cur_size; + + copy_size = (pipe_info->cur_transfer_size - offset); + + size = channel->pkt_size - PREFIX_SIZE; + + /* sanity check on pdest */ + if ((copy_size + frm->cur_size) < channel->req_image_size) + memcpy(pdest, psrc, copy_size); + + frm->cur_size += copy_size; + dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); + + if (frm->cur_size >= size) { + dprintk(2, "****************[%d]Buffer[%d]full*************\n", + dev->cc, idx); + channel->last_frame = channel->cur_frame; + channel->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if ((channel->cur_frame == SYS_FRAMES) || + (channel->cur_frame == channel->buffer.dwFrames)) + channel->cur_frame = 0; + /* frame ready */ + if (channel->b_acquire) + s2255_got_frame(channel, channel->jpg_size); + channel->frame_count++; + frm->ulState = S2255_READ_IDLE; + frm->cur_size = 0; + + } + /* done successfully */ + return 0; +} + +static void s2255_read_video_callback(struct s2255_dev *dev, + struct s2255_pipeinfo *pipe_info) +{ + int res; + dprintk(50, "callback read video \n"); + + if (dev->cc >= MAX_CHANNELS) { + dev->cc = 0; + dev_err(&dev->udev->dev, "invalid channel\n"); + return; + } + /* otherwise copy to the system buffers */ + res = save_frame(dev, pipe_info); + if (res != 0) + dprintk(4, "s2255: read callback failed\n"); + + dprintk(50, "callback read video done\n"); + return; +} + +static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, + u16 Index, u16 Value, void *TransferBuffer, + s32 TransferBufferLength, int bOut) +{ + int r; + if (!bOut) { + r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), + Request, + USB_TYPE_VENDOR | USB_RECIP_DEVICE | + USB_DIR_IN, + Value, Index, TransferBuffer, + TransferBufferLength, HZ * 5); + } else { + r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), + Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, + Value, Index, TransferBuffer, + TransferBufferLength, HZ * 5); + } + return r; +} + +/* + * retrieve FX2 firmware version. future use. + * @param dev pointer to device extension + * @return -1 for fail, else returns firmware version as an int(16 bits) + */ +static int s2255_get_fx2fw(struct s2255_dev *dev) +{ + int fw; + int ret; + unsigned char transBuffer[64]; + ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2, + S2255_VR_IN); + if (ret < 0) + dprintk(2, "get fw error: %x\n", ret); + fw = transBuffer[0] + (transBuffer[1] << 8); + dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); + return fw; +} + +/* + * Create the system ring buffer to copy frames into from the + * usb read pipe. + */ +static int s2255_create_sys_buffers(struct s2255_channel *channel) +{ + unsigned long i; + unsigned long reqsize; + dprintk(1, "create sys buffers\n"); + channel->buffer.dwFrames = SYS_FRAMES; + /* always allocate maximum size(PAL) for system buffers */ + reqsize = SYS_FRAMES_MAXSIZE; + + if (reqsize > SYS_FRAMES_MAXSIZE) + reqsize = SYS_FRAMES_MAXSIZE; + + for (i = 0; i < SYS_FRAMES; i++) { + /* allocate the frames */ + channel->buffer.frame[i].lpvbits = vmalloc(reqsize); + dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n", + &channel->buffer.frame[i], channel->idx, i, + channel->buffer.frame[i].lpvbits); + channel->buffer.frame[i].size = reqsize; + if (channel->buffer.frame[i].lpvbits == NULL) { + printk(KERN_INFO "out of memory. using less frames\n"); + channel->buffer.dwFrames = i; + break; + } + } + + /* make sure internal states are set */ + for (i = 0; i < SYS_FRAMES; i++) { + channel->buffer.frame[i].ulState = 0; + channel->buffer.frame[i].cur_size = 0; + } + + channel->cur_frame = 0; + channel->last_frame = -1; + return 0; +} + +static int s2255_release_sys_buffers(struct s2255_channel *channel) +{ + unsigned long i; + dprintk(1, "release sys buffers\n"); + for (i = 0; i < SYS_FRAMES; i++) { + if (channel->buffer.frame[i].lpvbits) { + dprintk(1, "vfree %p\n", + channel->buffer.frame[i].lpvbits); + vfree(channel->buffer.frame[i].lpvbits); + } + channel->buffer.frame[i].lpvbits = NULL; + } + return 0; +} + +static int s2255_board_init(struct s2255_dev *dev) +{ + struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; + int fw_ver; + int j; + struct s2255_pipeinfo *pipe = &dev->pipe; + dprintk(4, "board init: %p", dev); + memset(pipe, 0, sizeof(*pipe)); + pipe->dev = dev; + pipe->cur_transfer_size = S2255_USB_XFER_SIZE; + pipe->max_transfer_size = S2255_USB_XFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + dprintk(1, "out of memory!\n"); + return -ENOMEM; + } + /* query the firmware */ + fw_ver = s2255_get_fx2fw(dev); + + printk(KERN_INFO "s2255: usb firmware version %d.%d\n", + (fw_ver >> 8) & 0xff, + fw_ver & 0xff); + + if (fw_ver < S2255_CUR_USB_FWVER) + printk(KERN_INFO "s2255: newer USB firmware available\n"); + + for (j = 0; j < MAX_CHANNELS; j++) { + struct s2255_channel *channel = &dev->channel[j]; + channel->b_acquire = 0; + channel->mode = mode_def; + if (dev->pid == 0x2257 && j > 1) + channel->mode.color |= (1 << 16); + channel->jc.quality = S2255_DEF_JPEG_QUAL; + channel->width = LINE_SZ_4CIFS_NTSC; + channel->height = NUM_LINES_4CIFS_NTSC * 2; + channel->fmt = &formats[0]; + channel->mode.restart = 1; + channel->req_image_size = get_transfer_size(&mode_def); + channel->frame_count = 0; + /* create the system buffers */ + s2255_create_sys_buffers(channel); + } + /* start read pipe */ + s2255_start_readpipe(dev); + dprintk(1, "%s: success\n", __func__); + return 0; +} + +static int s2255_board_shutdown(struct s2255_dev *dev) +{ + u32 i; + dprintk(1, "%s: dev: %p", __func__, dev); + + for (i = 0; i < MAX_CHANNELS; i++) { + if (dev->channel[i].b_acquire) + s2255_stop_acquire(&dev->channel[i]); + } + s2255_stop_readpipe(dev); + for (i = 0; i < MAX_CHANNELS; i++) + s2255_release_sys_buffers(&dev->channel[i]); + /* release transfer buffer */ + kfree(dev->pipe.transfer_buffer); + return 0; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct s2255_pipeinfo *pipe_info; + struct s2255_dev *dev; + int status; + int pipe; + pipe_info = purb->context; + dprintk(100, "%s: urb:%p, status %d\n", __func__, purb, + purb->status); + if (pipe_info == NULL) { + dev_err(&purb->dev->dev, "no context!\n"); + return; + } + + dev = pipe_info->dev; + if (dev == NULL) { + dev_err(&purb->dev->dev, "no context!\n"); + return; + } + status = purb->status; + /* if shutting down, do not resubmit, exit immediately */ + if (status == -ESHUTDOWN) { + dprintk(2, "%s: err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + dprintk(2, "%s: exiting USB pipe", __func__); + return; + } + + if (status == 0) + s2255_read_video_callback(dev, pipe_info); + else { + pipe_info->err_count++; + dprintk(1, "%s: failed URB %d\n", __func__, status); + } + + pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) { + dev_err(&dev->udev->dev, "error submitting urb\n"); + } + } else { + dprintk(2, "%s :complete state 0\n", __func__); + } + return; +} + +static int s2255_start_readpipe(struct s2255_dev *dev) +{ + int pipe; + int retval; + struct s2255_pipeinfo *pipe_info = &dev->pipe; + pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); + dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint); + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&dev->udev->dev, + "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->cur_transfer_size, + read_pipe_completion, pipe_info); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR "s2255: start read pipe failed\n"); + return retval; + } + return 0; +} + +/* starts acquisition process */ +static int s2255_start_acquire(struct s2255_channel *channel) +{ + unsigned char *buffer; + int res; + unsigned long chn_rev; + int j; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + + channel->last_frame = -1; + channel->bad_payload = 0; + channel->cur_frame = 0; + for (j = 0; j < SYS_FRAMES; j++) { + channel->buffer.frame[j].ulState = 0; + channel->buffer.frame[j].cur_size = 0; + } + + /* send the start command */ + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_START; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (res != 0) + dev_err(&dev->udev->dev, "CMD_START error\n"); + + dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res); + kfree(buffer); + return 0; +} + +static int s2255_stop_acquire(struct s2255_channel *channel) +{ + unsigned char *buffer; + int res; + unsigned long chn_rev; + struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); + chn_rev = G_chnmap[channel->idx]; + buffer = kzalloc(512, GFP_KERNEL); + if (buffer == NULL) { + dev_err(&dev->udev->dev, "out of mem\n"); + return -ENOMEM; + } + /* send the stop command */ + *(__le32 *) buffer = IN_DATA_TOKEN; + *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); + *((__le32 *) buffer + 2) = CMD_STOP; + res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); + if (res != 0) + dev_err(&dev->udev->dev, "CMD_STOP error\n"); + kfree(buffer); + channel->b_acquire = 0; + dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res); + return res; +} + +static void s2255_stop_readpipe(struct s2255_dev *dev) +{ + struct s2255_pipeinfo *pipe = &dev->pipe; + + pipe->state = 0; + if (pipe->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe->stream_urb); + usb_free_urb(pipe->stream_urb); + pipe->stream_urb = NULL; + } + dprintk(4, "%s", __func__); + return; +} + +static void s2255_fwload_start(struct s2255_dev *dev, int reset) +{ + if (reset) + s2255_reset_dsppower(dev); + dev->fw_data->fw_size = dev->fw_data->fw->size; + atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); + memcpy(dev->fw_data->pfw_data, + dev->fw_data->fw->data, CHUNK_SIZE); + dev->fw_data->fw_loaded = CHUNK_SIZE; + usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev, + usb_sndbulkpipe(dev->udev, 2), + dev->fw_data->pfw_data, + CHUNK_SIZE, s2255_fwchunk_complete, + dev->fw_data); + mod_timer(&dev->timer, jiffies + HZ); +} + +/* standard usb probe function */ +static int s2255_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + struct s2255_dev *dev = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + int i; + int retval = -ENOMEM; + __le32 *pdata; + int fw_size; + dprintk(2, "%s\n", __func__); + /* allocate memory for our device state and initialize it to zero */ + dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); + if (dev == NULL) { + s2255_dev_err(&interface->dev, "out of memory\n"); + return -ENOMEM; + } + atomic_set(&dev->num_channels, 0); + dev->pid = id->idProduct; + dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); + if (!dev->fw_data) + goto errorFWDATA1; + mutex_init(&dev->lock); + /* grab usb_device and save it */ + dev->udev = usb_get_dev(interface_to_usbdev(interface)); + if (dev->udev == NULL) { + dev_err(&interface->dev, "null usb device\n"); + retval = -ENODEV; + goto errorUDEV; + } + dprintk(1, "dev: %p, udev %p interface %p\n", dev, + dev->udev, interface); + dev->interface = interface; + /* set up the endpoint information */ + iface_desc = interface->cur_altsetting; + dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + dev->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!dev->read_endpoint) { + dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); + goto errorEP; + } + init_timer(&dev->timer); + dev->timer.function = s2255_timer; + dev->timer.data = (unsigned long)dev->fw_data; + init_waitqueue_head(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) { + struct s2255_channel *channel = &dev->channel[i]; + dev->channel[i].idx = i; + init_waitqueue_head(&channel->wait_setmode); + init_waitqueue_head(&channel->wait_vidstatus); + } + + dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!dev->fw_data->fw_urb) { + dev_err(&interface->dev, "out of memory!\n"); + goto errorFWURB; + } + + dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); + if (!dev->fw_data->pfw_data) { + dev_err(&interface->dev, "out of memory!\n"); + goto errorFWDATA2; + } + /* load the first chunk */ + if (request_firmware(&dev->fw_data->fw, + FIRMWARE_FILE_NAME, &dev->udev->dev)) { + printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); + goto errorREQFW; + } + /* check the firmware is valid */ + fw_size = dev->fw_data->fw->size; + pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; + + if (*pdata != S2255_FW_MARKER) { + printk(KERN_INFO "Firmware invalid.\n"); + retval = -ENODEV; + goto errorFWMARKER; + } else { + /* make sure firmware is the latest */ + __le32 *pRel; + pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; + printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); + dev->dsp_fw_ver = le32_to_cpu(*pRel); + if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) + printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); + if (dev->pid == 0x2257 && + dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) + printk(KERN_WARNING "s2255: 2257 requires firmware %d" + " or above.\n", S2255_MIN_DSP_COLORFILTER); + } + usb_reset_device(dev->udev); + /* load 2255 board specific */ + retval = s2255_board_init(dev); + if (retval) + goto errorBOARDINIT; + spin_lock_init(&dev->slock); + s2255_fwload_start(dev, 0); + /* loads v4l specific */ + retval = s2255_probe_v4l(dev); + if (retval) + goto errorBOARDINIT; + dev_info(&interface->dev, "Sensoray 2255 detected\n"); + return 0; +errorBOARDINIT: + s2255_board_shutdown(dev); +errorFWMARKER: + release_firmware(dev->fw_data->fw); +errorREQFW: + kfree(dev->fw_data->pfw_data); +errorFWDATA2: + usb_free_urb(dev->fw_data->fw_urb); +errorFWURB: + del_timer(&dev->timer); +errorEP: + usb_put_dev(dev->udev); +errorUDEV: + kfree(dev->fw_data); + mutex_destroy(&dev->lock); +errorFWDATA1: + kfree(dev); + printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval); + return retval; +} + +/* disconnect routine. when board is removed physically or with rmmod */ +static void s2255_disconnect(struct usb_interface *interface) +{ + struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); + int i; + int channels = atomic_read(&dev->num_channels); + mutex_lock(&dev->lock); + v4l2_device_disconnect(&dev->v4l2_dev); + mutex_unlock(&dev->lock); + /*see comments in the uvc_driver.c usb disconnect function */ + atomic_inc(&dev->num_channels); + /* unregister each video device. */ + for (i = 0; i < channels; i++) + video_unregister_device(&dev->channel[i].vdev); + /* wake up any of our timers */ + atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); + wake_up(&dev->fw_data->wait_fw); + for (i = 0; i < MAX_CHANNELS; i++) { + dev->channel[i].setmode_ready = 1; + wake_up(&dev->channel[i].wait_setmode); + dev->channel[i].vidstatus_ready = 1; + wake_up(&dev->channel[i].wait_vidstatus); + } + if (atomic_dec_and_test(&dev->num_channels)) + s2255_destroy(dev); + dev_info(&interface->dev, "%s\n", __func__); +} + +static struct usb_driver s2255_driver = { + .name = S2255_DRIVER_NAME, + .probe = s2255_probe, + .disconnect = s2255_disconnect, + .id_table = s2255_table, +}; + +module_usb_driver(s2255_driver); + +MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); +MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(S2255_VERSION); +MODULE_FIRMWARE(FIRMWARE_FILE_NAME); diff --git a/drivers/media/usb/stkwebcam/Kconfig b/drivers/media/usb/stkwebcam/Kconfig new file mode 100644 index 000000000000..2fb0c2b687f4 --- /dev/null +++ b/drivers/media/usb/stkwebcam/Kconfig @@ -0,0 +1,13 @@ +config USB_STKWEBCAM + tristate "USB Syntek DC1125 Camera support" + depends on VIDEO_V4L2 && EXPERIMENTAL + ---help--- + Say Y here if you want to use this type of camera. + Supported devices are typically found in some Asus laptops, + with USB id 174f:a311 and 05e1:0501. Other Syntek cameras + may be supported by the stk11xx driver, from which this is + derived, see + + To compile this driver as a module, choose M here: the + module will be called stkwebcam. + diff --git a/drivers/media/usb/stkwebcam/Makefile b/drivers/media/usb/stkwebcam/Makefile new file mode 100644 index 000000000000..20ef8a4b990c --- /dev/null +++ b/drivers/media/usb/stkwebcam/Makefile @@ -0,0 +1,4 @@ +stkwebcam-objs := stk-webcam.o stk-sensor.o + +obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o + diff --git a/drivers/media/usb/stkwebcam/stk-sensor.c b/drivers/media/usb/stkwebcam/stk-sensor.c new file mode 100644 index 000000000000..e546b014d7ad --- /dev/null +++ b/drivers/media/usb/stkwebcam/stk-sensor.c @@ -0,0 +1,595 @@ +/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams) + * + * Copyright 2007-2008 Jaime Velasco Juan + * + * Some parts derived from ov7670.c: + * Copyright 2006 One Laptop Per Child Association, Inc. Written + * by Jonathan Corbet with substantial inspiration from Mark + * McClelland's ovcamchip code. + * + * Copyright 2006-7 Jonathan Corbet + * + * This file may be distributed under the terms of the GNU General + * 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. + */ + +/* Controlling the sensor via the STK1125 vendor specific control interface: + * The camera uses an OmniVision sensor and the stk1125 provides an + * SCCB(i2c)-USB bridge which let us program the sensor. + * In my case the sensor id is 0x9652, it can be read from sensor's register + * 0x0A and 0x0B as follows: + * - read register #R: + * output #R to index 0x0208 + * output 0x0070 to index 0x0200 + * input 1 byte from index 0x0201 (some kind of status register) + * until its value is 0x01 + * input 1 byte from index 0x0209. This is the value of #R + * - write value V to register #R + * output #R to index 0x0204 + * output V to index 0x0205 + * output 0x0005 to index 0x0200 + * input 1 byte from index 0x0201 until its value becomes 0x04 + */ + +/* It seems the i2c bus is controlled with these registers */ + +#include "stk-webcam.h" + +#define STK_IIC_BASE (0x0200) +# define STK_IIC_OP (STK_IIC_BASE) +# define STK_IIC_OP_TX (0x05) +# define STK_IIC_OP_RX (0x70) +# define STK_IIC_STAT (STK_IIC_BASE+1) +# define STK_IIC_STAT_TX_OK (0x04) +# define STK_IIC_STAT_RX_OK (0x01) +/* I don't know what does this register. + * when it is 0x00 or 0x01, we cannot talk to the sensor, + * other values work */ +# define STK_IIC_ENABLE (STK_IIC_BASE+2) +# define STK_IIC_ENABLE_NO (0x00) +/* This is what the driver writes in windows */ +# define STK_IIC_ENABLE_YES (0x1e) +/* + * Address of the slave. Seems like the binary driver look for the + * sensor in multiple places, attempting a reset sequence. + * We only know about the ov9650 + */ +# define STK_IIC_ADDR (STK_IIC_BASE+3) +# define STK_IIC_TX_INDEX (STK_IIC_BASE+4) +# define STK_IIC_TX_VALUE (STK_IIC_BASE+5) +# define STK_IIC_RX_INDEX (STK_IIC_BASE+8) +# define STK_IIC_RX_VALUE (STK_IIC_BASE+9) + +#define MAX_RETRIES (50) + +#define SENSOR_ADDRESS (0x60) + +/* From ov7670.c (These registers aren't fully accurate) */ + +/* Registers */ +#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ +#define REG_BLUE 0x01 /* blue gain */ +#define REG_RED 0x02 /* red gain */ +#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ +#define REG_COM1 0x04 /* Control 1 */ +#define COM1_CCIR656 0x40 /* CCIR656 enable */ +#define COM1_QFMT 0x20 /* QVGA/QCIF format */ +#define COM1_SKIP_0 0x00 /* Do not skip any row */ +#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */ +#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */ +#define REG_BAVE 0x05 /* U/B Average level */ +#define REG_GbAVE 0x06 /* Y/Gb Average level */ +#define REG_AECHH 0x07 /* AEC MS 5 bits */ +#define REG_RAVE 0x08 /* V/R Average level */ +#define REG_COM2 0x09 /* Control 2 */ +#define COM2_SSLEEP 0x10 /* Soft sleep mode */ +#define REG_PID 0x0a /* Product ID MSB */ +#define REG_VER 0x0b /* Product ID LSB */ +#define REG_COM3 0x0c /* Control 3 */ +#define COM3_SWAP 0x40 /* Byte swap */ +#define COM3_SCALEEN 0x08 /* Enable scaling */ +#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ +#define REG_COM4 0x0d /* Control 4 */ +#define REG_COM5 0x0e /* All "reserved" */ +#define REG_COM6 0x0f /* Control 6 */ +#define REG_AECH 0x10 /* More bits of AEC value */ +#define REG_CLKRC 0x11 /* Clock control */ +#define CLK_PLL 0x80 /* Enable internal PLL */ +#define CLK_EXT 0x40 /* Use external clock directly */ +#define CLK_SCALE 0x3f /* Mask for internal clock scale */ +#define REG_COM7 0x12 /* Control 7 */ +#define COM7_RESET 0x80 /* Register reset */ +#define COM7_FMT_MASK 0x38 +#define COM7_FMT_SXGA 0x00 +#define COM7_FMT_VGA 0x40 +#define COM7_FMT_CIF 0x20 /* CIF format */ +#define COM7_FMT_QVGA 0x10 /* QVGA format */ +#define COM7_FMT_QCIF 0x08 /* QCIF format */ +#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ +#define COM7_YUV 0x00 /* YUV */ +#define COM7_BAYER 0x01 /* Bayer format */ +#define COM7_PBAYER 0x05 /* "Processed bayer" */ +#define REG_COM8 0x13 /* Control 8 */ +#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ +#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ +#define COM8_BFILT 0x20 /* Band filter enable */ +#define COM8_AGC 0x04 /* Auto gain enable */ +#define COM8_AWB 0x02 /* White balance enable */ +#define COM8_AEC 0x01 /* Auto exposure enable */ +#define REG_COM9 0x14 /* Control 9 - gain ceiling */ +#define REG_COM10 0x15 /* Control 10 */ +#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ +#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ +#define COM10_HREF_REV 0x08 /* Reverse HREF */ +#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ +#define COM10_VS_NEG 0x02 /* VSYNC negative */ +#define COM10_HS_NEG 0x01 /* HSYNC negative */ +#define REG_HSTART 0x17 /* Horiz start high bits */ +#define REG_HSTOP 0x18 /* Horiz stop high bits */ +#define REG_VSTART 0x19 /* Vert start high bits */ +#define REG_VSTOP 0x1a /* Vert stop high bits */ +#define REG_PSHFT 0x1b /* Pixel delay after HREF */ +#define REG_MIDH 0x1c /* Manuf. ID high */ +#define REG_MIDL 0x1d /* Manuf. ID low */ +#define REG_MVFP 0x1e /* Mirror / vflip */ +#define MVFP_MIRROR 0x20 /* Mirror image */ +#define MVFP_FLIP 0x10 /* Vertical flip */ + +#define REG_AEW 0x24 /* AGC upper limit */ +#define REG_AEB 0x25 /* AGC lower limit */ +#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ +#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */ +#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */ +#define REG_HSYST 0x30 /* HSYNC rising edge delay */ +#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ +#define REG_HREF 0x32 /* HREF pieces */ +#define REG_TSLB 0x3a /* lots of stuff */ +#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ +#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */ +#define REG_COM11 0x3b /* Control 11 */ +#define COM11_NIGHT 0x80 /* NIght mode enable */ +#define COM11_NMFR 0x60 /* Two bit NM frame rate */ +#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ +#define COM11_50HZ 0x08 /* Manual 50Hz select */ +#define COM11_EXP 0x02 +#define REG_COM12 0x3c /* Control 12 */ +#define COM12_HREF 0x80 /* HREF always */ +#define REG_COM13 0x3d /* Control 13 */ +#define COM13_GAMMA 0x80 /* Gamma enable */ +#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ +#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */ +#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ +#define REG_COM14 0x3e /* Control 14 */ +#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ +#define REG_EDGE 0x3f /* Edge enhancement factor */ +#define REG_COM15 0x40 /* Control 15 */ +#define COM15_R10F0 0x00 /* Data range 10 to F0 */ +#define COM15_R01FE 0x80 /* 01 to FE */ +#define COM15_R00FF 0xc0 /* 00 to FF */ +#define COM15_RGB565 0x10 /* RGB565 output */ +#define COM15_RGBFIXME 0x20 /* FIXME */ +#define COM15_RGB555 0x30 /* RGB555 output */ +#define REG_COM16 0x41 /* Control 16 */ +#define COM16_AWBGAIN 0x08 /* AWB gain enable */ +#define REG_COM17 0x42 /* Control 17 */ +#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ +#define COM17_CBAR 0x08 /* DSP Color bar */ + +/* + * This matrix defines how the colors are generated, must be + * tweaked to adjust hue and saturation. + * + * Order: v-red, v-green, v-blue, u-red, u-green, u-blue + * + * They are nine-bit signed quantities, with the sign bit + * stored in 0x58. Sign for v-red is bit 0, and up from there. + */ +#define REG_CMATRIX_BASE 0x4f +#define CMATRIX_LEN 6 +#define REG_CMATRIX_SIGN 0x58 + + +#define REG_BRIGHT 0x55 /* Brightness */ +#define REG_CONTRAS 0x56 /* Contrast control */ + +#define REG_GFIX 0x69 /* Fix gain control */ + +#define REG_RGB444 0x8c /* RGB 444 control */ +#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ +#define R444_RGBX 0x01 /* Empty nibble at end */ + +#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ +#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ + +#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ +#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ +#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ +#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ +#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ +#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ +#define REG_BD60MAX 0xab /* 60hz banding step limit */ + + + + +/* Returns 0 if OK */ +static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) +{ + int i = 0; + int tmpval = 0; + + if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX)) + return 1; + do { + if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) + return 1; + i++; + } while (tmpval == 0 && i < MAX_RETRIES); + if (tmpval != STK_IIC_STAT_TX_OK) { + if (tmpval) + STK_ERROR("stk_sensor_outb failed, status=0x%02x\n", + tmpval); + return 1; + } else + return 0; +} + +static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) +{ + int i = 0; + int tmpval = 0; + + if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg)) + return 1; + if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX)) + return 1; + do { + if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) + return 1; + i++; + } while (tmpval == 0 && i < MAX_RETRIES); + if (tmpval != STK_IIC_STAT_RX_OK) { + if (tmpval) + STK_ERROR("stk_sensor_inb failed, status=0x%02x\n", + tmpval); + return 1; + } + + if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval)) + return 1; + + *val = (u8) tmpval; + return 0; +} + +static int stk_sensor_write_regvals(struct stk_camera *dev, + struct regval *rv) +{ + int ret; + if (rv == NULL) + return 0; + while (rv->reg != 0xff || rv->val != 0xff) { + ret = stk_sensor_outb(dev, rv->reg, rv->val); + if (ret != 0) + return ret; + rv++; + } + return 0; +} + +int stk_sensor_sleep(struct stk_camera *dev) +{ + u8 tmp; + return stk_sensor_inb(dev, REG_COM2, &tmp) + || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP); +} + +int stk_sensor_wakeup(struct stk_camera *dev) +{ + u8 tmp; + return stk_sensor_inb(dev, REG_COM2, &tmp) + || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP); +} + +static struct regval ov_initvals[] = { + {REG_CLKRC, CLK_PLL}, + {REG_COM11, 0x01}, + {0x6a, 0x7d}, + {REG_AECH, 0x40}, + {REG_GAIN, 0x00}, + {REG_BLUE, 0x80}, + {REG_RED, 0x80}, + /* Do not enable fast AEC for now */ + /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/ + {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC}, + {0x39, 0x50}, {0x38, 0x93}, + {0x37, 0x00}, {0x35, 0x81}, + {REG_COM5, 0x20}, + {REG_COM1, 0x00}, + {REG_COM3, 0x00}, + {REG_COM4, 0x00}, + {REG_PSHFT, 0x00}, + {0x16, 0x07}, + {0x33, 0xe2}, {0x34, 0xbf}, + {REG_COM16, 0x00}, + {0x96, 0x04}, + /* Gamma curve values */ +/* { 0x7a, 0x20 }, { 0x7b, 0x10 }, + { 0x7c, 0x1e }, { 0x7d, 0x35 }, + { 0x7e, 0x5a }, { 0x7f, 0x69 }, + { 0x80, 0x76 }, { 0x81, 0x80 }, + { 0x82, 0x88 }, { 0x83, 0x8f }, + { 0x84, 0x96 }, { 0x85, 0xa3 }, + { 0x86, 0xaf }, { 0x87, 0xc4 }, + { 0x88, 0xd7 }, { 0x89, 0xe8 }, +*/ + {REG_GFIX, 0x40}, + {0x8e, 0x00}, + {REG_COM12, 0x73}, + {0x8f, 0xdf}, {0x8b, 0x06}, + {0x8c, 0x20}, + {0x94, 0x88}, {0x95, 0x88}, +/* {REG_COM15, 0xc1}, TODO */ + {0x29, 0x3f}, + {REG_COM6, 0x42}, + {REG_BD50MAX, 0x80}, + {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92}, + {REG_BD60MAX, 0x0a}, + {0x90, 0x00}, {0x91, 0x00}, + {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00}, + {REG_AEW, 0x68}, {REG_AEB, 0x5c}, + {REG_VPT, 0xc3}, + {REG_COM9, 0x2e}, + {0x2a, 0x00}, {0x2b, 0x00}, + + {0xff, 0xff}, /* END MARKER */ +}; + +/* Probe the I2C bus and initialise the sensor chip */ +int stk_sensor_init(struct stk_camera *dev) +{ + u8 idl = 0; + u8 idh = 0; + + if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) + || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) + || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { + STK_ERROR("Sensor resetting failed\n"); + return -ENODEV; + } + msleep(10); + /* Read the manufacturer ID: ov = 0x7FA2 */ + if (stk_sensor_inb(dev, REG_MIDH, &idh) + || stk_sensor_inb(dev, REG_MIDL, &idl)) { + STK_ERROR("Strange error reading sensor ID\n"); + return -ENODEV; + } + if (idh != 0x7f || idl != 0xa2) { + STK_ERROR("Huh? you don't have a sensor from ovt\n"); + return -ENODEV; + } + if (stk_sensor_inb(dev, REG_PID, &idh) + || stk_sensor_inb(dev, REG_VER, &idl)) { + STK_ERROR("Could not read sensor model\n"); + return -ENODEV; + } + stk_sensor_write_regvals(dev, ov_initvals); + msleep(10); + STK_INFO("OmniVision sensor detected, id %02X%02X" + " at address %x\n", idh, idl, SENSOR_ADDRESS); + return 0; +} + +/* V4L2_PIX_FMT_UYVY */ +static struct regval ov_fmt_uyvy[] = { + {REG_TSLB, TSLB_YLAST|0x08 }, + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; +/* V4L2_PIX_FMT_YUYV */ +static struct regval ov_fmt_yuyv[] = { + {REG_TSLB, 0 }, + { 0x4f, 0x80 }, /* "matrix coefficient 1" */ + { 0x50, 0x80 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x22 }, /* "matrix coefficient 4" */ + { 0x53, 0x5e }, /* "matrix coefficient 5" */ + { 0x54, 0x80 }, /* "matrix coefficient 6" */ + {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; + +/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */ +static struct regval ov_fmt_rgbr[] = { + { REG_RGB444, 0 }, /* No RGB444 please */ + {REG_TSLB, 0x00}, + { REG_COM1, 0x0 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA }, + { REG_COM15, COM15_RGB565|COM15_R00FF }, + { 0xff, 0xff }, +}; + +/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */ +static struct regval ov_fmt_rgbp[] = { + { REG_RGB444, 0 }, /* No RGB444 please */ + {REG_TSLB, TSLB_BYTEORD }, + { REG_COM1, 0x0 }, + { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ + { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ + { 0x50, 0xb3 }, /* "matrix coefficient 2" */ + { 0x51, 0 }, /* vb */ + { 0x52, 0x3d }, /* "matrix coefficient 4" */ + { 0x53, 0xa7 }, /* "matrix coefficient 5" */ + { 0x54, 0xe4 }, /* "matrix coefficient 6" */ + { REG_COM13, COM13_GAMMA }, + { REG_COM15, COM15_RGB565|COM15_R00FF }, + { 0xff, 0xff }, +}; + +/* V4L2_PIX_FMT_SRGGB8 */ +static struct regval ov_fmt_bayer[] = { + /* This changes color order */ + {REG_TSLB, 0x40}, /* BGGR */ + /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */ + {REG_COM15, COM15_R00FF }, + {0xff, 0xff}, /* END MARKER */ +}; +/* + * Store a set of start/stop values into the camera. + */ +static int stk_sensor_set_hw(struct stk_camera *dev, + int hstart, int hstop, int vstart, int vstop) +{ + int ret; + unsigned char v; +/* + * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of + * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is + * a mystery "edge offset" value in the top two bits of href. + */ + ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff); + ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff); + ret += stk_sensor_inb(dev, REG_HREF, &v); + v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); + msleep(10); + ret += stk_sensor_outb(dev, REG_HREF, v); +/* + * Vertical: similar arrangement (note: this is different from ov7670.c) + */ + ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff); + ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff); + ret += stk_sensor_inb(dev, REG_VREF, &v); + v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); + msleep(10); + ret += stk_sensor_outb(dev, REG_VREF, v); + return ret; +} + + +int stk_sensor_configure(struct stk_camera *dev) +{ + int com7; + /* + * We setup the sensor to output dummy lines in low-res modes, + * so we don't get absurdly hight framerates. + */ + unsigned dummylines; + int flip; + struct regval *rv; + + switch (dev->vsettings.mode) { + case MODE_QCIF: com7 = COM7_FMT_QCIF; + dummylines = 604; + break; + case MODE_QVGA: com7 = COM7_FMT_QVGA; + dummylines = 267; + break; + case MODE_CIF: com7 = COM7_FMT_CIF; + dummylines = 412; + break; + case MODE_VGA: com7 = COM7_FMT_VGA; + dummylines = 11; + break; + case MODE_SXGA: com7 = COM7_FMT_SXGA; + dummylines = 0; + break; + default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode); + return -EFAULT; + } + switch (dev->vsettings.palette) { + case V4L2_PIX_FMT_UYVY: + com7 |= COM7_YUV; + rv = ov_fmt_uyvy; + break; + case V4L2_PIX_FMT_YUYV: + com7 |= COM7_YUV; + rv = ov_fmt_yuyv; + break; + case V4L2_PIX_FMT_RGB565: + com7 |= COM7_RGB; + rv = ov_fmt_rgbp; + break; + case V4L2_PIX_FMT_RGB565X: + com7 |= COM7_RGB; + rv = ov_fmt_rgbr; + break; + case V4L2_PIX_FMT_SBGGR8: + com7 |= COM7_PBAYER; + rv = ov_fmt_bayer; + break; + default: STK_ERROR("Unsupported colorspace\n"); + return -EFAULT; + } + /*FIXME sometimes the sensor go to a bad state + stk_sensor_write_regvals(dev, ov_initvals); */ + stk_sensor_outb(dev, REG_COM7, com7); + msleep(50); + stk_sensor_write_regvals(dev, rv); + flip = (dev->vsettings.vflip?MVFP_FLIP:0) + | (dev->vsettings.hflip?MVFP_MIRROR:0); + stk_sensor_outb(dev, REG_MVFP, flip); + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8 + && !dev->vsettings.vflip) + stk_sensor_outb(dev, REG_TSLB, 0x08); + stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8); + stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff); + msleep(50); + switch (dev->vsettings.mode) { + case MODE_VGA: + if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) + STK_ERROR("stk_sensor_set_hw failed (VGA)\n"); + break; + case MODE_SXGA: + case MODE_CIF: + case MODE_QVGA: + case MODE_QCIF: + /*FIXME These settings seem ignored by the sensor + if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) + STK_ERROR("stk_sensor_set_hw failed (SXGA)\n"); + */ + break; + } + msleep(10); + return 0; +} + +int stk_sensor_set_brightness(struct stk_camera *dev, int br) +{ + if (br < 0 || br > 0xff) + return -EINVAL; + stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6)); + stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6)); + return 0; +} + diff --git a/drivers/media/usb/stkwebcam/stk-webcam.c b/drivers/media/usb/stkwebcam/stk-webcam.c new file mode 100644 index 000000000000..86a0fc56c330 --- /dev/null +++ b/drivers/media/usb/stkwebcam/stk-webcam.c @@ -0,0 +1,1380 @@ +/* + * stk-webcam.c : Driver for Syntek 1125 USB webcam controller + * + * Copyright (C) 2006 Nicolas VIVIEN + * Copyright 2007-2008 Jaime Velasco Juan + * + * Some parts are inspired from cafe_ccic.c + * Copyright 2006-2007 Jonathan Corbet + * + * 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 + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "stk-webcam.h" + + +static bool hflip; +module_param(hflip, bool, 0444); +MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); + +static bool vflip; +module_param(vflip, bool, 0444); +MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); + +static int debug; +module_param(debug, int, 0444); +MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jaime Velasco Juan and Nicolas VIVIEN"); +MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); + + +/* bool for webcam LED management */ +int first_init = 1; + +/* Some cameras have audio interfaces, we aren't interested in those */ +static struct usb_device_id stkwebcam_table[] = { + { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, + { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, + { } +}; +MODULE_DEVICE_TABLE(usb, stkwebcam_table); + +/* + * Basic stuff + */ +int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) +{ + struct usb_device *udev = dev->udev; + int ret; + + ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), + 0x01, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + index, + NULL, + 0, + 500); + if (ret < 0) + return ret; + else + return 0; +} + +int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value) +{ + struct usb_device *udev = dev->udev; + int ret; + + ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), + 0x00, + USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0x00, + index, + (u8 *) value, + sizeof(u8), + 500); + if (ret < 0) + return ret; + else + return 0; +} + +static int stk_start_stream(struct stk_camera *dev) +{ + int value; + int i, ret; + int value_116, value_117; + + if (!is_present(dev)) + return -ENODEV; + if (!is_memallocd(dev) || !is_initialised(dev)) { + STK_ERROR("FIXME: Buffers are not allocated\n"); + return -EFAULT; + } + ret = usb_set_interface(dev->udev, 0, 5); + + if (ret < 0) + STK_ERROR("usb_set_interface failed !\n"); + if (stk_sensor_wakeup(dev)) + STK_ERROR("error awaking the sensor\n"); + + stk_camera_read_reg(dev, 0x0116, &value_116); + stk_camera_read_reg(dev, 0x0117, &value_117); + + stk_camera_write_reg(dev, 0x0116, 0x0000); + stk_camera_write_reg(dev, 0x0117, 0x0000); + + stk_camera_read_reg(dev, 0x0100, &value); + stk_camera_write_reg(dev, 0x0100, value | 0x80); + + stk_camera_write_reg(dev, 0x0116, value_116); + stk_camera_write_reg(dev, 0x0117, value_117); + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].urb) { + ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); + atomic_inc(&dev->urbs_used); + if (ret) + return ret; + } + } + set_streaming(dev); + return 0; +} + +static int stk_stop_stream(struct stk_camera *dev) +{ + int value; + int i; + if (is_present(dev)) { + stk_camera_read_reg(dev, 0x0100, &value); + stk_camera_write_reg(dev, 0x0100, value & ~0x80); + if (dev->isobufs != NULL) { + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].urb) + usb_kill_urb(dev->isobufs[i].urb); + } + } + unset_streaming(dev); + + if (usb_set_interface(dev->udev, 0, 0)) + STK_ERROR("usb_set_interface failed !\n"); + if (stk_sensor_sleep(dev)) + STK_ERROR("error suspending the sensor\n"); + } + return 0; +} + +/* + * This seems to be the shortest init sequence we + * must do in order to find the sensor + * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor + * is also reset. Maybe powers down it? + * Rest of values don't make a difference + */ + +static struct regval stk1125_initvals[] = { + /*TODO: What means this sequence? */ + {0x0000, 0x24}, + {0x0100, 0x21}, + {0x0002, 0x68}, + {0x0003, 0x80}, + {0x0005, 0x00}, + {0x0007, 0x03}, + {0x000d, 0x00}, + {0x000f, 0x02}, + {0x0300, 0x12}, + {0x0350, 0x41}, + {0x0351, 0x00}, + {0x0352, 0x00}, + {0x0353, 0x00}, + {0x0018, 0x10}, + {0x0019, 0x00}, + {0x001b, 0x0e}, + {0x001c, 0x46}, + {0x0300, 0x80}, + {0x001a, 0x04}, + {0x0110, 0x00}, + {0x0111, 0x00}, + {0x0112, 0x00}, + {0x0113, 0x00}, + + {0xffff, 0xff}, +}; + + +static int stk_initialise(struct stk_camera *dev) +{ + struct regval *rv; + int ret; + if (!is_present(dev)) + return -ENODEV; + if (is_initialised(dev)) + return 0; + rv = stk1125_initvals; + while (rv->reg != 0xffff) { + ret = stk_camera_write_reg(dev, rv->reg, rv->val); + if (ret) + return ret; + rv++; + } + if (stk_sensor_init(dev) == 0) { + set_initialised(dev); + return 0; + } else + return -1; +} + +/* *********************************************** */ +/* + * This function is called as an URB transfert is complete (Isochronous pipe). + * So, the traitement is done in interrupt time, so it has be fast, not crash, + * and not stall. Neat. + */ +static void stk_isoc_handler(struct urb *urb) +{ + int i; + int ret; + int framelen; + unsigned long flags; + + unsigned char *fill = NULL; + unsigned char *iso_buf = NULL; + + struct stk_camera *dev; + struct stk_sio_buffer *fb; + + dev = (struct stk_camera *) urb->context; + + if (dev == NULL) { + STK_ERROR("isoc_handler called with NULL device !\n"); + return; + } + + if (urb->status == -ENOENT || urb->status == -ECONNRESET + || urb->status == -ESHUTDOWN) { + atomic_dec(&dev->urbs_used); + return; + } + + spin_lock_irqsave(&dev->spinlock, flags); + + if (urb->status != -EINPROGRESS && urb->status != 0) { + STK_ERROR("isoc_handler: urb->status == %d\n", urb->status); + goto resubmit; + } + + if (list_empty(&dev->sio_avail)) { + /*FIXME Stop streaming after a while */ + (void) (printk_ratelimit() && + STK_ERROR("isoc_handler without available buffer!\n")); + goto resubmit; + } + fb = list_first_entry(&dev->sio_avail, + struct stk_sio_buffer, list); + fill = fb->buffer + fb->v4lbuf.bytesused; + + for (i = 0; i < urb->number_of_packets; i++) { + if (urb->iso_frame_desc[i].status != 0) { + if (urb->iso_frame_desc[i].status != -EXDEV) + STK_ERROR("Frame %d has error %d\n", i, + urb->iso_frame_desc[i].status); + continue; + } + framelen = urb->iso_frame_desc[i].actual_length; + iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + if (framelen <= 4) + continue; /* no data */ + + /* + * we found something informational from there + * the isoc frames have to type of headers + * type1: 00 xx 00 00 or 20 xx 00 00 + * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 + * xx is a sequencer which has never been seen over 0x3f + * imho data written down looks like bayer, i see similarities + * after every 640 bytes + */ + if (*iso_buf & 0x80) { + framelen -= 8; + iso_buf += 8; + /* This marks a new frame */ + if (fb->v4lbuf.bytesused != 0 + && fb->v4lbuf.bytesused != dev->frame_size) { + (void) (printk_ratelimit() && + STK_ERROR("frame %d, " + "bytesused=%d, skipping\n", + i, fb->v4lbuf.bytesused)); + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } else if (fb->v4lbuf.bytesused == dev->frame_size) { + if (list_is_singular(&dev->sio_avail)) { + /* Always reuse the last buffer */ + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } else { + list_move_tail(dev->sio_avail.next, + &dev->sio_full); + wake_up(&dev->wait_frame); + fb = list_first_entry(&dev->sio_avail, + struct stk_sio_buffer, list); + fb->v4lbuf.bytesused = 0; + fill = fb->buffer; + } + } + } else { + framelen -= 4; + iso_buf += 4; + } + + /* Our buffer is full !!! */ + if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { + (void) (printk_ratelimit() && + STK_ERROR("Frame buffer overflow, lost sync\n")); + /*FIXME Do something here? */ + continue; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + memcpy(fill, iso_buf, framelen); + spin_lock_irqsave(&dev->spinlock, flags); + fill += framelen; + + /* New size of our buffer */ + fb->v4lbuf.bytesused += framelen; + } + +resubmit: + spin_unlock_irqrestore(&dev->spinlock, flags); + urb->dev = dev->udev; + ret = usb_submit_urb(urb, GFP_ATOMIC); + if (ret != 0) { + STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n", + ret); + } +} + +/* -------------------------------------------- */ + +static int stk_prepare_iso(struct stk_camera *dev) +{ + void *kbuf; + int i, j; + struct urb *urb; + struct usb_device *udev; + + if (dev == NULL) + return -ENXIO; + udev = dev->udev; + + if (dev->isobufs) + STK_ERROR("isobufs already allocated. Bad\n"); + else + dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), + GFP_KERNEL); + if (dev->isobufs == NULL) { + STK_ERROR("Unable to allocate iso buffers\n"); + return -ENOMEM; + } + for (i = 0; i < MAX_ISO_BUFS; i++) { + if (dev->isobufs[i].data == NULL) { + kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); + if (kbuf == NULL) { + STK_ERROR("Failed to allocate iso buffer %d\n", + i); + goto isobufs_out; + } + dev->isobufs[i].data = kbuf; + } else + STK_ERROR("isobuf data already allocated\n"); + if (dev->isobufs[i].urb == NULL) { + urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); + if (urb == NULL) { + STK_ERROR("Failed to allocate URB %d\n", i); + goto isobufs_out; + } + dev->isobufs[i].urb = urb; + } else { + STK_ERROR("Killing URB\n"); + usb_kill_urb(dev->isobufs[i].urb); + urb = dev->isobufs[i].urb; + } + urb->interval = 1; + urb->dev = udev; + urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = dev->isobufs[i].data; + urb->transfer_buffer_length = ISO_BUFFER_SIZE; + urb->complete = stk_isoc_handler; + urb->context = dev; + urb->start_frame = 0; + urb->number_of_packets = ISO_FRAMES_PER_DESC; + + for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { + urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; + urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; + } + } + set_memallocd(dev); + return 0; + +isobufs_out: + for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) + kfree(dev->isobufs[i].data); + for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) + usb_free_urb(dev->isobufs[i].urb); + kfree(dev->isobufs); + dev->isobufs = NULL; + return -ENOMEM; +} + +static void stk_clean_iso(struct stk_camera *dev) +{ + int i; + + if (dev == NULL || dev->isobufs == NULL) + return; + + for (i = 0; i < MAX_ISO_BUFS; i++) { + struct urb *urb; + + urb = dev->isobufs[i].urb; + if (urb) { + if (atomic_read(&dev->urbs_used) && is_present(dev)) + usb_kill_urb(urb); + usb_free_urb(urb); + } + kfree(dev->isobufs[i].data); + } + kfree(dev->isobufs); + dev->isobufs = NULL; + unset_memallocd(dev); +} + +static int stk_setup_siobuf(struct stk_camera *dev, int index) +{ + struct stk_sio_buffer *buf = dev->sio_bufs + index; + INIT_LIST_HEAD(&buf->list); + buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); + buf->buffer = vmalloc_user(buf->v4lbuf.length); + if (buf->buffer == NULL) + return -ENOMEM; + buf->mapcount = 0; + buf->dev = dev; + buf->v4lbuf.index = index; + buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + buf->v4lbuf.field = V4L2_FIELD_NONE; + buf->v4lbuf.memory = V4L2_MEMORY_MMAP; + buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; + return 0; +} + +static int stk_free_sio_buffers(struct stk_camera *dev) +{ + int i; + int nbufs; + unsigned long flags; + if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) + return 0; + /* + * If any buffers are mapped, we cannot free them at all. + */ + for (i = 0; i < dev->n_sbufs; i++) { + if (dev->sio_bufs[i].mapcount > 0) + return -EBUSY; + } + /* + * OK, let's do it. + */ + spin_lock_irqsave(&dev->spinlock, flags); + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + nbufs = dev->n_sbufs; + dev->n_sbufs = 0; + spin_unlock_irqrestore(&dev->spinlock, flags); + for (i = 0; i < nbufs; i++) { + if (dev->sio_bufs[i].buffer != NULL) + vfree(dev->sio_bufs[i].buffer); + } + kfree(dev->sio_bufs); + dev->sio_bufs = NULL; + return 0; +} + +static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) +{ + int i; + if (dev->sio_bufs != NULL) + STK_ERROR("sio_bufs already allocated\n"); + else { + dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer), + GFP_KERNEL); + if (dev->sio_bufs == NULL) + return -ENOMEM; + for (i = 0; i < n_sbufs; i++) { + if (stk_setup_siobuf(dev, i)) + return (dev->n_sbufs > 1 ? 0 : -ENOMEM); + dev->n_sbufs = i+1; + } + } + return 0; +} + +static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) +{ + int err; + err = stk_prepare_iso(dev); + if (err) { + stk_clean_iso(dev); + return err; + } + err = stk_prepare_sio_buffers(dev, n_sbufs); + if (err) { + stk_free_sio_buffers(dev); + return err; + } + return 0; +} + +static void stk_free_buffers(struct stk_camera *dev) +{ + stk_clean_iso(dev); + stk_free_sio_buffers(dev); +} +/* -------------------------------------------- */ + +/* v4l file operations */ + +static int v4l_stk_open(struct file *fp) +{ + struct stk_camera *dev; + struct video_device *vdev; + + vdev = video_devdata(fp); + dev = vdev_to_camera(vdev); + + if (dev == NULL || !is_present(dev)) + return -ENXIO; + + if (!first_init) + stk_camera_write_reg(dev, 0x0, 0x24); + else + first_init = 0; + + fp->private_data = dev; + usb_autopm_get_interface(dev->interface); + + return 0; +} + +static int v4l_stk_release(struct file *fp) +{ + struct stk_camera *dev = fp->private_data; + + if (dev->owner == fp) { + stk_stop_stream(dev); + stk_free_buffers(dev); + stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ + unset_initialised(dev); + dev->owner = NULL; + } + + if (is_present(dev)) + usb_autopm_put_interface(dev->interface); + + return 0; +} + +static ssize_t v4l_stk_read(struct file *fp, char __user *buf, + size_t count, loff_t *f_pos) +{ + int i; + int ret; + unsigned long flags; + struct stk_sio_buffer *sbuf; + struct stk_camera *dev = fp->private_data; + + if (!is_present(dev)) + return -EIO; + if (dev->owner && dev->owner != fp) + return -EBUSY; + dev->owner = fp; + if (!is_streaming(dev)) { + if (stk_initialise(dev) + || stk_allocate_buffers(dev, 3) + || stk_start_stream(dev)) + return -ENOMEM; + spin_lock_irqsave(&dev->spinlock, flags); + for (i = 0; i < dev->n_sbufs; i++) { + list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); + dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + } + if (*f_pos == 0) { + if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) + return -EWOULDBLOCK; + ret = wait_event_interruptible(dev->wait_frame, + !list_empty(&dev->sio_full) || !is_present(dev)); + if (ret) + return ret; + if (!is_present(dev)) + return -EIO; + } + if (count + *f_pos > dev->frame_size) + count = dev->frame_size - *f_pos; + spin_lock_irqsave(&dev->spinlock, flags); + if (list_empty(&dev->sio_full)) { + spin_unlock_irqrestore(&dev->spinlock, flags); + STK_ERROR("BUG: No siobufs ready\n"); + return 0; + } + sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); + spin_unlock_irqrestore(&dev->spinlock, flags); + + if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) + return -EFAULT; + + *f_pos += count; + + if (*f_pos >= dev->frame_size) { + *f_pos = 0; + spin_lock_irqsave(&dev->spinlock, flags); + list_move_tail(&sbuf->list, &dev->sio_avail); + spin_unlock_irqrestore(&dev->spinlock, flags); + } + return count; +} + +static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait) +{ + struct stk_camera *dev = fp->private_data; + + poll_wait(fp, &dev->wait_frame, wait); + + if (!is_present(dev)) + return POLLERR; + + if (!list_empty(&dev->sio_full)) + return POLLIN | POLLRDNORM; + + return 0; +} + + +static void stk_v4l_vm_open(struct vm_area_struct *vma) +{ + struct stk_sio_buffer *sbuf = vma->vm_private_data; + sbuf->mapcount++; +} +static void stk_v4l_vm_close(struct vm_area_struct *vma) +{ + struct stk_sio_buffer *sbuf = vma->vm_private_data; + sbuf->mapcount--; + if (sbuf->mapcount == 0) + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; +} +static const struct vm_operations_struct stk_v4l_vm_ops = { + .open = stk_v4l_vm_open, + .close = stk_v4l_vm_close +}; + +static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) +{ + unsigned int i; + int ret; + unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; + struct stk_camera *dev = fp->private_data; + struct stk_sio_buffer *sbuf = NULL; + + if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) + return -EINVAL; + + for (i = 0; i < dev->n_sbufs; i++) { + if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { + sbuf = dev->sio_bufs + i; + break; + } + } + if (sbuf == NULL) + return -EINVAL; + ret = remap_vmalloc_range(vma, sbuf->buffer, 0); + if (ret) + return ret; + vma->vm_flags |= VM_DONTEXPAND; + vma->vm_private_data = sbuf; + vma->vm_ops = &stk_v4l_vm_ops; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; + stk_v4l_vm_open(vma); + return 0; +} + +/* v4l ioctl handlers */ + +static int stk_vidioc_querycap(struct file *filp, + void *priv, struct v4l2_capability *cap) +{ + strcpy(cap->driver, "stk"); + strcpy(cap->card, "stk"); + cap->version = DRIVER_VERSION_NUM; + + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE + | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; + return 0; +} + +static int stk_vidioc_enum_input(struct file *filp, + void *priv, struct v4l2_input *input) +{ + if (input->index != 0) + return -EINVAL; + + strcpy(input->name, "Syntek USB Camera"); + input->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + + +static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) +{ + *i = 0; + return 0; +} + +static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) +{ + if (i != 0) + return -EINVAL; + else + return 0; +} + +/* from vivi.c */ +static int stk_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) +{ + return 0; +} + +/* List of all V4Lv2 controls supported by the driver */ +static struct v4l2_queryctrl stk_controls[] = { + { + .id = V4L2_CID_BRIGHTNESS, + .type = V4L2_CTRL_TYPE_INTEGER, + .name = "Brightness", + .minimum = 0, + .maximum = 0xffff, + .step = 0x0100, + .default_value = 0x6000, + }, + { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Horizontal Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Vertical Flip", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 1, + }, +}; + +static int stk_vidioc_queryctrl(struct file *filp, + void *priv, struct v4l2_queryctrl *c) +{ + int i; + int nbr; + nbr = ARRAY_SIZE(stk_controls); + + for (i = 0; i < nbr; i++) { + if (stk_controls[i].id == c->id) { + memcpy(c, &stk_controls[i], + sizeof(struct v4l2_queryctrl)); + return 0; + } + } + return -EINVAL; +} + +static int stk_vidioc_g_ctrl(struct file *filp, + void *priv, struct v4l2_control *c) +{ + struct stk_camera *dev = priv; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + c->value = dev->vsettings.brightness; + break; + case V4L2_CID_HFLIP: + c->value = dev->vsettings.hflip; + break; + case V4L2_CID_VFLIP: + c->value = dev->vsettings.vflip; + break; + default: + return -EINVAL; + } + return 0; +} + +static int stk_vidioc_s_ctrl(struct file *filp, + void *priv, struct v4l2_control *c) +{ + struct stk_camera *dev = priv; + switch (c->id) { + case V4L2_CID_BRIGHTNESS: + dev->vsettings.brightness = c->value; + return stk_sensor_set_brightness(dev, c->value >> 8); + case V4L2_CID_HFLIP: + dev->vsettings.hflip = c->value; + return 0; + case V4L2_CID_VFLIP: + dev->vsettings.vflip = c->value; + return 0; + default: + return -EINVAL; + } + return 0; +} + + +static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_fmtdesc *fmtd) +{ + switch (fmtd->index) { + case 0: + fmtd->pixelformat = V4L2_PIX_FMT_RGB565; + strcpy(fmtd->description, "r5g6b5"); + break; + case 1: + fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; + strcpy(fmtd->description, "r5g6b5BE"); + break; + case 2: + fmtd->pixelformat = V4L2_PIX_FMT_UYVY; + strcpy(fmtd->description, "yuv4:2:2"); + break; + case 3: + fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; + strcpy(fmtd->description, "Raw bayer"); + break; + case 4: + fmtd->pixelformat = V4L2_PIX_FMT_YUYV; + strcpy(fmtd->description, "yuv4:2:2"); + break; + default: + return -EINVAL; + } + return 0; +} + +static struct stk_size { + unsigned w; + unsigned h; + enum stk_mode m; +} stk_sizes[] = { + { .w = 1280, .h = 1024, .m = MODE_SXGA, }, + { .w = 640, .h = 480, .m = MODE_VGA, }, + { .w = 352, .h = 288, .m = MODE_CIF, }, + { .w = 320, .h = 240, .m = MODE_QVGA, }, + { .w = 176, .h = 144, .m = MODE_QCIF, }, +}; + +static int stk_vidioc_g_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *f) +{ + struct v4l2_pix_format *pix_format = &f->fmt.pix; + struct stk_camera *dev = priv; + int i; + + for (i = 0; i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode; i++) + ; + if (i == ARRAY_SIZE(stk_sizes)) { + STK_ERROR("ERROR: mode invalid\n"); + return -EINVAL; + } + pix_format->width = stk_sizes[i].w; + pix_format->height = stk_sizes[i].h; + pix_format->field = V4L2_FIELD_NONE; + pix_format->colorspace = V4L2_COLORSPACE_SRGB; + pix_format->pixelformat = dev->vsettings.palette; + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) + pix_format->bytesperline = pix_format->width; + else + pix_format->bytesperline = 2 * pix_format->width; + pix_format->sizeimage = pix_format->bytesperline + * pix_format->height; + return 0; +} + +static int stk_vidioc_try_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *fmtd) +{ + int i; + switch (fmtd->fmt.pix.pixelformat) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_SBGGR8: + break; + default: + return -EINVAL; + } + for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { + if (fmtd->fmt.pix.width > stk_sizes[i].w) + break; + } + if (i == ARRAY_SIZE(stk_sizes) + || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) + < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { + fmtd->fmt.pix.height = stk_sizes[i-1].h; + fmtd->fmt.pix.width = stk_sizes[i-1].w; + fmtd->fmt.pix.priv = i - 1; + } else { + fmtd->fmt.pix.height = stk_sizes[i].h; + fmtd->fmt.pix.width = stk_sizes[i].w; + fmtd->fmt.pix.priv = i; + } + + fmtd->fmt.pix.field = V4L2_FIELD_NONE; + fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; + if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) + fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; + else + fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; + fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline + * fmtd->fmt.pix.height; + return 0; +} + +static int stk_setup_format(struct stk_camera *dev) +{ + int i = 0; + int depth; + if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) + depth = 1; + else + depth = 2; + while (i < ARRAY_SIZE(stk_sizes) && + stk_sizes[i].m != dev->vsettings.mode) + i++; + if (i == ARRAY_SIZE(stk_sizes)) { + STK_ERROR("Something is broken in %s\n", __func__); + return -EFAULT; + } + /* This registers controls some timings, not sure of what. */ + stk_camera_write_reg(dev, 0x001b, 0x0e); + if (dev->vsettings.mode == MODE_SXGA) + stk_camera_write_reg(dev, 0x001c, 0x0e); + else + stk_camera_write_reg(dev, 0x001c, 0x46); + /* + * Registers 0x0115 0x0114 are the size of each line (bytes), + * regs 0x0117 0x0116 are the heigth of the image. + */ + stk_camera_write_reg(dev, 0x0115, + ((stk_sizes[i].w * depth) >> 8) & 0xff); + stk_camera_write_reg(dev, 0x0114, + (stk_sizes[i].w * depth) & 0xff); + stk_camera_write_reg(dev, 0x0117, + (stk_sizes[i].h >> 8) & 0xff); + stk_camera_write_reg(dev, 0x0116, + stk_sizes[i].h & 0xff); + return stk_sensor_configure(dev); +} + +static int stk_vidioc_s_fmt_vid_cap(struct file *filp, + void *priv, struct v4l2_format *fmtd) +{ + int ret; + struct stk_camera *dev = priv; + + if (dev == NULL) + return -ENODEV; + if (!is_present(dev)) + return -ENODEV; + if (is_streaming(dev)) + return -EBUSY; + if (dev->owner && dev->owner != filp) + return -EBUSY; + ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd); + if (ret) + return ret; + dev->owner = filp; + + dev->vsettings.palette = fmtd->fmt.pix.pixelformat; + stk_free_buffers(dev); + dev->frame_size = fmtd->fmt.pix.sizeimage; + dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m; + + stk_initialise(dev); + return stk_setup_format(dev); +} + +static int stk_vidioc_reqbufs(struct file *filp, + void *priv, struct v4l2_requestbuffers *rb) +{ + struct stk_camera *dev = priv; + + if (dev == NULL) + return -ENODEV; + if (rb->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + if (is_streaming(dev) + || (dev->owner && dev->owner != filp)) + return -EBUSY; + dev->owner = filp; + + /*FIXME If they ask for zero, we must stop streaming and free */ + if (rb->count < 3) + rb->count = 3; + /* Arbitrary limit */ + else if (rb->count > 5) + rb->count = 5; + + stk_allocate_buffers(dev, rb->count); + rb->count = dev->n_sbufs; + return 0; +} + +static int stk_vidioc_querybuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = priv; + struct stk_sio_buffer *sbuf; + + if (buf->index >= dev->n_sbufs) + return -EINVAL; + sbuf = dev->sio_bufs + buf->index; + *buf = sbuf->v4lbuf; + return 0; +} + +static int stk_vidioc_qbuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = priv; + struct stk_sio_buffer *sbuf; + unsigned long flags; + + if (buf->memory != V4L2_MEMORY_MMAP) + return -EINVAL; + + if (buf->index >= dev->n_sbufs) + return -EINVAL; + sbuf = dev->sio_bufs + buf->index; + if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) + return 0; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; + spin_lock_irqsave(&dev->spinlock, flags); + list_add_tail(&sbuf->list, &dev->sio_avail); + *buf = sbuf->v4lbuf; + spin_unlock_irqrestore(&dev->spinlock, flags); + return 0; +} + +static int stk_vidioc_dqbuf(struct file *filp, + void *priv, struct v4l2_buffer *buf) +{ + struct stk_camera *dev = priv; + struct stk_sio_buffer *sbuf; + unsigned long flags; + int ret; + + if (!is_streaming(dev)) + return -EINVAL; + + if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) + return -EWOULDBLOCK; + ret = wait_event_interruptible(dev->wait_frame, + !list_empty(&dev->sio_full) || !is_present(dev)); + if (ret) + return ret; + if (!is_present(dev)) + return -EIO; + + spin_lock_irqsave(&dev->spinlock, flags); + sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); + list_del_init(&sbuf->list); + spin_unlock_irqrestore(&dev->spinlock, flags); + sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; + sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; + sbuf->v4lbuf.sequence = ++dev->sequence; + do_gettimeofday(&sbuf->v4lbuf.timestamp); + + *buf = sbuf->v4lbuf; + return 0; +} + +static int stk_vidioc_streamon(struct file *filp, + void *priv, enum v4l2_buf_type type) +{ + struct stk_camera *dev = priv; + if (is_streaming(dev)) + return 0; + if (dev->sio_bufs == NULL) + return -EINVAL; + dev->sequence = 0; + return stk_start_stream(dev); +} + +static int stk_vidioc_streamoff(struct file *filp, + void *priv, enum v4l2_buf_type type) +{ + struct stk_camera *dev = priv; + unsigned long flags; + int i; + stk_stop_stream(dev); + spin_lock_irqsave(&dev->spinlock, flags); + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + for (i = 0; i < dev->n_sbufs; i++) { + INIT_LIST_HEAD(&dev->sio_bufs[i].list); + dev->sio_bufs[i].v4lbuf.flags = 0; + } + spin_unlock_irqrestore(&dev->spinlock, flags); + return 0; +} + + +static int stk_vidioc_g_parm(struct file *filp, + void *priv, struct v4l2_streamparm *sp) +{ + /*FIXME This is not correct */ + sp->parm.capture.timeperframe.numerator = 1; + sp->parm.capture.timeperframe.denominator = 30; + sp->parm.capture.readbuffers = 2; + return 0; +} + +static int stk_vidioc_enum_framesizes(struct file *filp, + void *priv, struct v4l2_frmsizeenum *frms) +{ + if (frms->index >= ARRAY_SIZE(stk_sizes)) + return -EINVAL; + switch (frms->pixel_format) { + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_UYVY: + case V4L2_PIX_FMT_YUYV: + case V4L2_PIX_FMT_SBGGR8: + frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; + frms->discrete.width = stk_sizes[frms->index].w; + frms->discrete.height = stk_sizes[frms->index].h; + return 0; + default: return -EINVAL; + } +} + +static struct v4l2_file_operations v4l_stk_fops = { + .owner = THIS_MODULE, + .open = v4l_stk_open, + .release = v4l_stk_release, + .read = v4l_stk_read, + .poll = v4l_stk_poll, + .mmap = v4l_stk_mmap, + .ioctl = video_ioctl2, +}; + +static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { + .vidioc_querycap = stk_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, + .vidioc_enum_input = stk_vidioc_enum_input, + .vidioc_s_input = stk_vidioc_s_input, + .vidioc_g_input = stk_vidioc_g_input, + .vidioc_s_std = stk_vidioc_s_std, + .vidioc_reqbufs = stk_vidioc_reqbufs, + .vidioc_querybuf = stk_vidioc_querybuf, + .vidioc_qbuf = stk_vidioc_qbuf, + .vidioc_dqbuf = stk_vidioc_dqbuf, + .vidioc_streamon = stk_vidioc_streamon, + .vidioc_streamoff = stk_vidioc_streamoff, + .vidioc_queryctrl = stk_vidioc_queryctrl, + .vidioc_g_ctrl = stk_vidioc_g_ctrl, + .vidioc_s_ctrl = stk_vidioc_s_ctrl, + .vidioc_g_parm = stk_vidioc_g_parm, + .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, +}; + +static void stk_v4l_dev_release(struct video_device *vd) +{ + struct stk_camera *dev = vdev_to_camera(vd); + + if (dev->sio_bufs != NULL || dev->isobufs != NULL) + STK_ERROR("We are leaking memory\n"); + usb_put_intf(dev->interface); + kfree(dev); +} + +static struct video_device stk_v4l_data = { + .name = "stkwebcam", + .tvnorms = V4L2_STD_UNKNOWN, + .current_norm = V4L2_STD_UNKNOWN, + .fops = &v4l_stk_fops, + .ioctl_ops = &v4l_stk_ioctl_ops, + .release = stk_v4l_dev_release, +}; + + +static int stk_register_video_device(struct stk_camera *dev) +{ + int err; + + dev->vdev = stk_v4l_data; + dev->vdev.debug = debug; + dev->vdev.parent = &dev->interface->dev; + err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); + if (err) + STK_ERROR("v4l registration failed\n"); + else + STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n", + video_device_node_name(&dev->vdev)); + return err; +} + + +/* USB Stuff */ + +static int stk_camera_probe(struct usb_interface *interface, + const struct usb_device_id *id) +{ + int i; + int err = 0; + + struct stk_camera *dev = NULL; + struct usb_device *udev = interface_to_usbdev(interface); + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + + dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); + if (dev == NULL) { + STK_ERROR("Out of memory !\n"); + return -ENOMEM; + } + + spin_lock_init(&dev->spinlock); + init_waitqueue_head(&dev->wait_frame); + + dev->udev = udev; + dev->interface = interface; + usb_get_intf(interface); + + dev->vsettings.vflip = vflip; + dev->vsettings.hflip = hflip; + dev->n_sbufs = 0; + set_present(dev); + + /* Set up the endpoint information + * use only the first isoc-in endpoint + * for the current alternate setting */ + iface_desc = interface->cur_altsetting; + + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + + if (!dev->isoc_ep + && usb_endpoint_is_isoc_in(endpoint)) { + /* we found an isoc in endpoint */ + dev->isoc_ep = usb_endpoint_num(endpoint); + break; + } + } + if (!dev->isoc_ep) { + STK_ERROR("Could not find isoc-in endpoint"); + err = -ENODEV; + goto error; + } + dev->vsettings.brightness = 0x7fff; + dev->vsettings.palette = V4L2_PIX_FMT_RGB565; + dev->vsettings.mode = MODE_VGA; + dev->frame_size = 640 * 480 * 2; + + INIT_LIST_HEAD(&dev->sio_avail); + INIT_LIST_HEAD(&dev->sio_full); + + usb_set_intfdata(interface, dev); + + err = stk_register_video_device(dev); + if (err) + goto error; + + return 0; + +error: + kfree(dev); + return err; +} + +static void stk_camera_disconnect(struct usb_interface *interface) +{ + struct stk_camera *dev = usb_get_intfdata(interface); + + usb_set_intfdata(interface, NULL); + unset_present(dev); + + wake_up_interruptible(&dev->wait_frame); + + STK_INFO("Syntek USB2.0 Camera release resources device %s\n", + video_device_node_name(&dev->vdev)); + + video_unregister_device(&dev->vdev); +} + +#ifdef CONFIG_PM +static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct stk_camera *dev = usb_get_intfdata(intf); + if (is_streaming(dev)) { + stk_stop_stream(dev); + /* yes, this is ugly */ + set_streaming(dev); + } + return 0; +} + +static int stk_camera_resume(struct usb_interface *intf) +{ + struct stk_camera *dev = usb_get_intfdata(intf); + if (!is_initialised(dev)) + return 0; + unset_initialised(dev); + stk_initialise(dev); + stk_camera_write_reg(dev, 0x0, 0x49); + stk_setup_format(dev); + if (is_streaming(dev)) + stk_start_stream(dev); + return 0; +} +#endif + +static struct usb_driver stk_camera_driver = { + .name = "stkwebcam", + .probe = stk_camera_probe, + .disconnect = stk_camera_disconnect, + .id_table = stkwebcam_table, +#ifdef CONFIG_PM + .suspend = stk_camera_suspend, + .resume = stk_camera_resume, +#endif +}; + +module_usb_driver(stk_camera_driver); diff --git a/drivers/media/usb/stkwebcam/stk-webcam.h b/drivers/media/usb/stkwebcam/stk-webcam.h new file mode 100644 index 000000000000..9f6736637571 --- /dev/null +++ b/drivers/media/usb/stkwebcam/stk-webcam.h @@ -0,0 +1,134 @@ +/* + * stk-webcam.h : Driver for Syntek 1125 USB webcam controller + * + * Copyright (C) 2006 Nicolas VIVIEN + * Copyright 2007-2008 Jaime Velasco Juan + * + * 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 + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef STKWEBCAM_H +#define STKWEBCAM_H + +#include +#include + +#define DRIVER_VERSION "v0.0.1" +#define DRIVER_VERSION_NUM 0x000001 + +#define MAX_ISO_BUFS 3 +#define ISO_FRAMES_PER_DESC 16 +#define ISO_MAX_FRAME_SIZE 3 * 1024 +#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) + + +#define PREFIX "stkwebcam: " +#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args) +#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args) +#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args) + +struct stk_iso_buf { + void *data; + int length; + int read; + struct urb *urb; +}; + +/* Streaming IO buffers */ +struct stk_sio_buffer { + struct v4l2_buffer v4lbuf; + char *buffer; + int mapcount; + struct stk_camera *dev; + struct list_head list; +}; + +enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; + +struct stk_video { + enum stk_mode mode; + int brightness; + __u32 palette; + int hflip; + int vflip; +}; + +enum stk_status { + S_PRESENT = 1, + S_INITIALISED = 2, + S_MEMALLOCD = 4, + S_STREAMING = 8, +}; +#define is_present(dev) ((dev)->status & S_PRESENT) +#define is_initialised(dev) ((dev)->status & S_INITIALISED) +#define is_streaming(dev) ((dev)->status & S_STREAMING) +#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD) +#define set_present(dev) ((dev)->status = S_PRESENT) +#define unset_present(dev) ((dev)->status &= \ + ~(S_PRESENT|S_INITIALISED|S_STREAMING)) +#define set_initialised(dev) ((dev)->status |= S_INITIALISED) +#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED) +#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD) +#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD) +#define set_streaming(dev) ((dev)->status |= S_STREAMING) +#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING) + +struct regval { + unsigned reg; + unsigned val; +}; + +struct stk_camera { + struct video_device vdev; + struct usb_device *udev; + struct usb_interface *interface; + int webcam_model; + struct file *owner; + + u8 isoc_ep; + + /* Not sure if this is right */ + atomic_t urbs_used; + + struct stk_video vsettings; + + enum stk_status status; + + spinlock_t spinlock; + wait_queue_head_t wait_frame; + + struct stk_iso_buf *isobufs; + + int frame_size; + /* Streaming buffers */ + unsigned int n_sbufs; + struct stk_sio_buffer *sio_bufs; + struct list_head sio_avail; + struct list_head sio_full; + unsigned sequence; +}; + +#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev) + +int stk_camera_write_reg(struct stk_camera *, u16, u8); +int stk_camera_read_reg(struct stk_camera *, u16, int *); + +int stk_sensor_init(struct stk_camera *); +int stk_sensor_configure(struct stk_camera *); +int stk_sensor_sleep(struct stk_camera *dev); +int stk_sensor_wakeup(struct stk_camera *dev); +int stk_sensor_set_brightness(struct stk_camera *dev, int br); + +#endif diff --git a/drivers/media/usb/zr364xx/Kconfig b/drivers/media/usb/zr364xx/Kconfig new file mode 100644 index 000000000000..0f585662881d --- /dev/null +++ b/drivers/media/usb/zr364xx/Kconfig @@ -0,0 +1,14 @@ +config USB_ZR364XX + tristate "USB ZR364XX Camera support" + depends on VIDEO_V4L2 + select VIDEOBUF_GEN + select VIDEOBUF_VMALLOC + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. + See for more info + and list of supported cameras. + + To compile this driver as a module, choose M here: the + module will be called zr364xx. + diff --git a/drivers/media/usb/zr364xx/Makefile b/drivers/media/usb/zr364xx/Makefile new file mode 100644 index 000000000000..a5777883a1f8 --- /dev/null +++ b/drivers/media/usb/zr364xx/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_USB_ZR364XX) += zr364xx.o + diff --git a/drivers/media/usb/zr364xx/zr364xx.c b/drivers/media/usb/zr364xx/zr364xx.c new file mode 100644 index 000000000000..9afab35878b4 --- /dev/null +++ b/drivers/media/usb/zr364xx/zr364xx.c @@ -0,0 +1,1643 @@ +/* + * Zoran 364xx based USB webcam module version 0.73 + * + * Allows you to use your USB webcam with V4L2 applications + * This is still in heavy developpement ! + * + * Copyright (C) 2004 Antoine Jacquet + * http://royale.zerezo.com/zr364xx/ + * + * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers + * V4L2 version inspired by meye.c driver + * + * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Version Information */ +#define DRIVER_VERSION "0.7.4" +#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" +#define DRIVER_DESC "Zoran 364xx" + + +/* Camera */ +#define FRAMES 1 +#define MAX_FRAME_SIZE 200000 +#define BUFFER_SIZE 0x1000 +#define CTRL_TIMEOUT 500 + +#define ZR364XX_DEF_BUFS 4 +#define ZR364XX_READ_IDLE 0 +#define ZR364XX_READ_FRAME 1 + +/* Debug macro */ +#define DBG(fmt, args...) \ + do { \ + if (debug) { \ + printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ + } \ + } while (0) + +/*#define FULL_DEBUG 1*/ +#ifdef FULL_DEBUG +#define _DBG DBG +#else +#define _DBG(fmt, args...) +#endif + +/* Init methods, need to find nicer names for these + * the exact names of the chipsets would be the best if someone finds it */ +#define METHOD0 0 +#define METHOD1 1 +#define METHOD2 2 +#define METHOD3 3 + + +/* Module parameters */ +static int debug; +static int mode; + + +/* Module parameters interface */ +module_param(debug, int, 0644); +MODULE_PARM_DESC(debug, "Debug level"); +module_param(mode, int, 0644); +MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); + + +/* Devices supported by this driver + * .driver_info contains the init method used by the camera */ +static struct usb_device_id device_table[] = { + {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, + {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, + {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, + {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, + {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, + {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, + {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, + {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, + {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, + {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, + {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, + {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, + {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, + {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, + {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, + {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, + {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, + {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +/* frame structure */ +struct zr364xx_framei { + unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, + ZR364XX_READ_FRAME */ + void *lpvbits; /* image data */ + unsigned long cur_size; /* current data copied to it */ +}; + +/* image buffer structure */ +struct zr364xx_bufferi { + unsigned long dwFrames; /* number of frames in buffer */ + struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ +}; + +struct zr364xx_dmaqueue { + struct list_head active; + struct zr364xx_camera *cam; +}; + +struct zr364xx_pipeinfo { + u32 transfer_size; + u8 *transfer_buffer; + u32 state; + void *stream_urb; + void *cam; /* back pointer to zr364xx_camera struct */ + u32 err_count; + u32 idx; +}; + +struct zr364xx_fmt { + char *name; + u32 fourcc; + int depth; +}; + +/* image formats. */ +static const struct zr364xx_fmt formats[] = { + { + .name = "JPG", + .fourcc = V4L2_PIX_FMT_JPEG, + .depth = 24 + } +}; + +/* Camera stuff */ +struct zr364xx_camera { + struct usb_device *udev; /* save off the usb device pointer */ + struct usb_interface *interface;/* the interface for this device */ + struct v4l2_device v4l2_dev; + struct v4l2_ctrl_handler ctrl_handler; + struct video_device vdev; /* v4l video device */ + struct v4l2_fh *owner; /* owns the streaming */ + int nb; + struct zr364xx_bufferi buffer; + int skip; + int width; + int height; + int method; + struct mutex lock; + + spinlock_t slock; + struct zr364xx_dmaqueue vidq; + int last_frame; + int cur_frame; + unsigned long frame_count; + int b_acquire; + struct zr364xx_pipeinfo pipe[1]; + + u8 read_endpoint; + + const struct zr364xx_fmt *fmt; + struct videobuf_queue vb_vidq; + bool was_streaming; +}; + +/* buffer for one video frame */ +struct zr364xx_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + const struct zr364xx_fmt *fmt; +}; + +/* function used to send initialisation commands to the camera */ +static int send_control_msg(struct usb_device *udev, u8 request, u16 value, + u16 index, unsigned char *cp, u16 size) +{ + int status; + + unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); + if (!transfer_buffer) { + dev_err(&udev->dev, "kmalloc(%d) failed\n", size); + return -ENOMEM; + } + + memcpy(transfer_buffer, cp, size); + + status = usb_control_msg(udev, + usb_sndctrlpipe(udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + transfer_buffer, size, CTRL_TIMEOUT); + + kfree(transfer_buffer); + return status; +} + + +/* Control messages sent to the camera to initialize it + * and launch the capture */ +typedef struct { + unsigned int value; + unsigned int size; + unsigned char *bytes; +} message; + +/* method 0 */ +static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; +static unsigned char m0d3[] = { 0, 0 }; +static message m0[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0x3370, sizeof(m0d1), m0d1}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2610, sizeof(m0d2), m0d2}, + {0xe107, 0, NULL}, + {0x2502, 0, NULL}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m0d3), m0d3}, + {-1, -1, NULL} +}; + +/* method 1 */ +static unsigned char m1d1[] = { 0xff, 0xff }; +static unsigned char m1d2[] = { 0x00, 0x00 }; +static message m1[] = { + {0x1f30, 0, NULL}, + {0xd000, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m1d1), m1d1}, + {0x1f70, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0xd000, 0, NULL}, + {0x9a01, sizeof(m1d2), m1d2}, + {-1, -1, NULL} +}; + +/* method 2 */ +static unsigned char m2d1[] = { 0xff, 0xff }; +static message m2[] = { + {0x1f30, 0, NULL}, + {0xf000, 0, NULL}, + {0x2000, 0, NULL}, + {0x2f0f, 0, NULL}, + {0x2650, 0, NULL}, + {0xe107, 0, NULL}, + {0x2502, sizeof(m2d1), m2d1}, + {0x1f70, 0, NULL}, + {-1, -1, NULL} +}; + +/* init table */ +static message *init[4] = { m0, m1, m2, m2 }; + + +/* JPEG static data in header (Huffman table, etc) */ +static unsigned char header1[] = { + 0xFF, 0xD8, + /* + 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', + 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, + */ + 0xFF, 0xDB, 0x00, 0x84 +}; +static unsigned char header2[] = { + 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, + 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, + 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, + 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, + 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, + 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, + 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, + 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, + 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, + 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, + 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, + 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, + 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, + 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, + 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, + 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, + 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, + 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, + 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, + 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, + 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, + 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, + 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, + 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, + 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, + 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, + 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, + 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, + 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, + 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, + 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, + 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, + 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, + 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, + 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, + 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, + 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, + 0x00, 0x3F, 0x00 +}; +static unsigned char header3; + +/* ------------------------------------------------------------------ + Videobuf operations + ------------------------------------------------------------------*/ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct zr364xx_camera *cam = vq->priv_data; + + *size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (*count == 0) + *count = ZR364XX_DEF_BUFS; + + if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) + *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; + + return 0; +} + +static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) +{ + _DBG("%s\n", __func__); + + if (in_interrupt()) + BUG(); + + videobuf_vmalloc_free(&buf->vb); + buf->vb.state = VIDEOBUF_NEEDS_INIT; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct zr364xx_camera *cam = vq->priv_data; + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + int rc; + + DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ? + cam->fmt->name : ""); + if (cam->fmt == NULL) + return -EINVAL; + + buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); + + if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { + DBG("invalid buffer prepare\n"); + return -EINVAL; + } + + buf->fmt = cam->fmt; + buf->vb.width = cam->width; + buf->vb.height = cam->height; + buf->vb.field = field; + + if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { + rc = videobuf_iolock(vq, &buf->vb, NULL); + if (rc < 0) + goto fail; + } + + buf->vb.state = VIDEOBUF_PREPARED; + return 0; +fail: + free_buffer(vq, buf); + return rc; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + struct zr364xx_camera *cam = vq->priv_data; + + _DBG("%s\n", __func__); + + buf->vb.state = VIDEOBUF_QUEUED; + list_add_tail(&buf->vb.queue, &cam->vidq.active); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, + vb); + + _DBG("%s\n", __func__); + free_buffer(vq, buf); +} + +static struct videobuf_queue_ops zr364xx_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/********************/ +/* V4L2 integration */ +/********************/ +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type); + +static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, + loff_t * ppos) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err = 0; + + _DBG("%s\n", __func__); + + if (!buf) + return -EINVAL; + + if (!count) + return -EINVAL; + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = zr364xx_vidioc_streamon(file, file->private_data, + V4L2_BUF_TYPE_VIDEO_CAPTURE); + if (err == 0) { + DBG("%s: reading %d bytes at pos %d.\n", __func__, + (int) count, (int) *ppos); + + /* NoMan Sux ! */ + err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, + file->f_flags & O_NONBLOCK); + } + mutex_unlock(&cam->lock); + return err; +} + +/* video buffer vmalloc implementation based partly on VIVI driver which is + * Copyright (c) 2006 by + * Mauro Carvalho Chehab + * Ted Walther + * John Sokol + * http://v4l.videotechnology.com/ + * + */ +static void zr364xx_fillbuff(struct zr364xx_camera *cam, + struct zr364xx_buffer *buf, + int jpgsize) +{ + int pos = 0; + struct timeval ts; + const char *tmpbuf; + char *vbuf = videobuf_to_vmalloc(&buf->vb); + unsigned long last_frame; + + if (!vbuf) + return; + + last_frame = cam->last_frame; + if (last_frame != -1) { + tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; + switch (buf->fmt->fourcc) { + case V4L2_PIX_FMT_JPEG: + buf->vb.size = jpgsize; + memcpy(vbuf, tmpbuf, buf->vb.size); + break; + default: + printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); + } + cam->last_frame = -1; + } else { + printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); + return; + } + DBG("%s: Buffer 0x%08lx size= %d\n", __func__, + (unsigned long)vbuf, pos); + /* tell v4l buffer was filled */ + + buf->vb.field_count = cam->frame_count * 2; + do_gettimeofday(&ts); + buf->vb.ts = ts; + buf->vb.state = VIDEOBUF_DONE; +} + +static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) +{ + struct zr364xx_dmaqueue *dma_q = &cam->vidq; + struct zr364xx_buffer *buf; + unsigned long flags = 0; + int rc = 0; + + DBG("wakeup: %p\n", &dma_q); + spin_lock_irqsave(&cam->slock, flags); + + if (list_empty(&dma_q->active)) { + DBG("No active queue to serve\n"); + rc = -1; + goto unlock; + } + buf = list_entry(dma_q->active.next, + struct zr364xx_buffer, vb.queue); + + if (!waitqueue_active(&buf->vb.done)) { + /* no one active */ + rc = -1; + goto unlock; + } + list_del(&buf->vb.queue); + do_gettimeofday(&buf->vb.ts); + DBG("[%p/%d] wakeup\n", buf, buf->vb.i); + zr364xx_fillbuff(cam, buf, jpgsize); + wake_up(&buf->vb.done); + DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); +unlock: + spin_unlock_irqrestore(&cam->slock, flags); + return rc; +} + +/* this function moves the usb stream read pipe data + * into the system buffers. + * returns 0 on success, EAGAIN if more data to process (call this + * function again). + */ +static int zr364xx_read_video_callback(struct zr364xx_camera *cam, + struct zr364xx_pipeinfo *pipe_info, + struct urb *purb) +{ + unsigned char *pdest; + unsigned char *psrc; + s32 idx = -1; + struct zr364xx_framei *frm; + int i = 0; + unsigned char *ptr = NULL; + + _DBG("buffer to user\n"); + idx = cam->cur_frame; + frm = &cam->buffer.frame[idx]; + + /* swap bytes if camera needs it */ + if (cam->method == METHOD0) { + u16 *buf = (u16 *)pipe_info->transfer_buffer; + for (i = 0; i < purb->actual_length/2; i++) + swab16s(buf + i); + } + + /* search done. now find out if should be acquiring */ + if (!cam->b_acquire) { + /* we found a frame, but this channel is turned off */ + frm->ulState = ZR364XX_READ_IDLE; + return -EINVAL; + } + + psrc = (u8 *)pipe_info->transfer_buffer; + ptr = pdest = frm->lpvbits; + + if (frm->ulState == ZR364XX_READ_IDLE) { + frm->ulState = ZR364XX_READ_FRAME; + frm->cur_size = 0; + + _DBG("jpeg header, "); + memcpy(ptr, header1, sizeof(header1)); + ptr += sizeof(header1); + header3 = 0; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc, 64); + ptr += 64; + header3 = 1; + memcpy(ptr, &header3, 1); + ptr++; + memcpy(ptr, psrc + 64, 64); + ptr += 64; + memcpy(ptr, header2, sizeof(header2)); + ptr += sizeof(header2); + memcpy(ptr, psrc + 128, + purb->actual_length - 128); + ptr += purb->actual_length - 128; + _DBG("header : %d %d %d %d %d %d %d %d %d\n", + psrc[0], psrc[1], psrc[2], + psrc[3], psrc[4], psrc[5], + psrc[6], psrc[7], psrc[8]); + frm->cur_size = ptr - pdest; + } else { + if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { + dev_info(&cam->udev->dev, + "%s: buffer (%d bytes) too small to hold " + "frame data. Discarding frame data.\n", + __func__, MAX_FRAME_SIZE); + } else { + pdest += frm->cur_size; + memcpy(pdest, psrc, purb->actual_length); + frm->cur_size += purb->actual_length; + } + } + /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, + purb->actual_length);*/ + + if (purb->actual_length < pipe_info->transfer_size) { + _DBG("****************Buffer[%d]full*************\n", idx); + cam->last_frame = cam->cur_frame; + cam->cur_frame++; + /* end of system frame ring buffer, start at zero */ + if (cam->cur_frame == cam->buffer.dwFrames) + cam->cur_frame = 0; + + /* frame ready */ + /* go back to find the JPEG EOI marker */ + ptr = pdest = frm->lpvbits; + ptr += frm->cur_size - 2; + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xD9 + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr == pdest) + DBG("No EOI marker\n"); + + /* Sometimes there is junk data in the middle of the picture, + * we want to skip this bogus frames */ + while (ptr > pdest) { + if (*ptr == 0xFF && *(ptr + 1) == 0xFF + && *(ptr + 2) == 0xFF) + break; + ptr--; + } + if (ptr != pdest) { + DBG("Bogus frame ? %d\n", ++(cam->nb)); + } else if (cam->b_acquire) { + /* we skip the 2 first frames which are usually buggy */ + if (cam->skip) + cam->skip--; + else { + _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", + frm->cur_size, + pdest[0], pdest[1], pdest[2], pdest[3], + pdest[4], pdest[5], pdest[6], pdest[7]); + + zr364xx_got_frame(cam, frm->cur_size); + } + } + cam->frame_count++; + frm->ulState = ZR364XX_READ_IDLE; + frm->cur_size = 0; + } + /* done successfully */ + return 0; +} + +static int zr364xx_vidioc_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); + strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); + strlcpy(cap->bus_info, dev_name(&cam->udev->dev), + sizeof(cap->bus_info)); + cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | + V4L2_CAP_READWRITE | + V4L2_CAP_STREAMING; + cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; + + return 0; +} + +static int zr364xx_vidioc_enum_input(struct file *file, void *priv, + struct v4l2_input *i) +{ + if (i->index != 0) + return -EINVAL; + strcpy(i->name, DRIVER_DESC " Camera"); + i->type = V4L2_INPUT_TYPE_CAMERA; + return 0; +} + +static int zr364xx_vidioc_g_input(struct file *file, void *priv, + unsigned int *i) +{ + *i = 0; + return 0; +} + +static int zr364xx_vidioc_s_input(struct file *file, void *priv, + unsigned int i) +{ + if (i != 0) + return -EINVAL; + return 0; +} + +static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) +{ + struct zr364xx_camera *cam = + container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); + int temp; + + switch (ctrl->id) { + case V4L2_CID_BRIGHTNESS: + /* hardware brightness */ + send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); + temp = (0x60 << 8) + 127 - ctrl->val; + send_control_msg(cam->udev, 1, temp, 0, NULL, 0); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, + void *priv, struct v4l2_fmtdesc *f) +{ + if (f->index > 0) + return -EINVAL; + f->flags = V4L2_FMT_FLAG_COMPRESSED; + strcpy(f->description, formats[0].name); + f->pixelformat = formats[0].fourcc; + return 0; +} + +static char *decode_fourcc(__u32 pixelformat, char *buf) +{ + buf[0] = pixelformat & 0xff; + buf[1] = (pixelformat >> 8) & 0xff; + buf[2] = (pixelformat >> 16) & 0xff; + buf[3] = (pixelformat >> 24) & 0xff; + buf[4] = '\0'; + return buf; +} + +static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + char pixelformat_name[5]; + + if (cam == NULL) + return -ENODEV; + + if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { + DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); + return -EINVAL; + } + + if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && + !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { + f->fmt.pix.width = 320; + f->fmt.pix.height = 240; + } + + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return 0; +} + +static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam; + + if (file == NULL) + return -ENODEV; + cam = video_drvdata(file); + + f->fmt.pix.pixelformat = formats[0].fourcc; + f->fmt.pix.field = V4L2_FIELD_NONE; + f->fmt.pix.width = cam->width; + f->fmt.pix.height = cam->height; + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + return 0; +} + +static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, + struct v4l2_format *f) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + char pixelformat_name[5]; + int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); + int i; + + if (ret < 0) + return ret; + + mutex_lock(&q->vb_lock); + + if (videobuf_queue_is_busy(&cam->vb_vidq)) { + DBG("%s queue busy\n", __func__); + ret = -EBUSY; + goto out; + } + + if (cam->owner) { + DBG("%s can't change format after started\n", __func__); + ret = -EBUSY; + goto out; + } + + cam->width = f->fmt.pix.width; + cam->height = f->fmt.pix.height; + DBG("%s: %dx%d mode selected\n", __func__, + cam->width, cam->height); + f->fmt.pix.bytesperline = f->fmt.pix.width * 2; + f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; + f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; + f->fmt.pix.priv = 0; + cam->vb_vidq.field = f->fmt.pix.field; + + if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) + mode = 1; + else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) + mode = 2; + else + mode = 0; + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + for (i = 0; init[cam->method][i].size != -1; i++) { + ret = + send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (ret < 0) { + dev_err(&cam->udev->dev, + "error during resolution change sequence: %d\n", i); + goto out; + } + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + cam->skip = 2; + ret = 0; + +out: + mutex_unlock(&q->vb_lock); + + DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, + decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), + f->fmt.pix.field); + return ret; +} + +static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *p) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + if (cam->owner && cam->owner != priv) + return -EBUSY; + return videobuf_reqbufs(&cam->vb_vidq, p); +} + +static int zr364xx_vidioc_querybuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + rc = videobuf_querybuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_qbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_qbuf(&cam->vb_vidq, p); + return rc; +} + +static int zr364xx_vidioc_dqbuf(struct file *file, + void *priv, + struct v4l2_buffer *p) +{ + int rc; + struct zr364xx_camera *cam = video_drvdata(file); + _DBG("%s\n", __func__); + if (cam->owner && cam->owner != priv) + return -EBUSY; + rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); + return rc; +} + +static void read_pipe_completion(struct urb *purb) +{ + struct zr364xx_pipeinfo *pipe_info; + struct zr364xx_camera *cam; + int pipe; + + pipe_info = purb->context; + _DBG("%s %p, status %d\n", __func__, purb, purb->status); + if (pipe_info == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + cam = pipe_info->cam; + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); + return; + } + + /* if shutting down, do not resubmit, exit immediately */ + if (purb->status == -ESHUTDOWN) { + DBG("%s, err shutdown\n", __func__); + pipe_info->err_count++; + return; + } + + if (pipe_info->state == 0) { + DBG("exiting USB pipe\n"); + return; + } + + if (purb->actual_length < 0 || + purb->actual_length > pipe_info->transfer_size) { + dev_err(&cam->udev->dev, "wrong number of bytes\n"); + return; + } + + if (purb->status == 0) + zr364xx_read_video_callback(cam, pipe_info, purb); + else { + pipe_info->err_count++; + DBG("%s: failed URB %d\n", __func__, purb->status); + } + + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + + /* reuse urb */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + if (pipe_info->state != 0) { + purb->status = usb_submit_urb(pipe_info->stream_urb, + GFP_ATOMIC); + + if (purb->status) + dev_err(&cam->udev->dev, + "error submitting urb (error=%i)\n", + purb->status); + } else + DBG("read pipe complete state 0\n"); +} + +static int zr364xx_start_readpipe(struct zr364xx_camera *cam) +{ + int pipe; + int retval; + struct zr364xx_pipeinfo *pipe_info = cam->pipe; + pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); + DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); + + pipe_info->state = 1; + pipe_info->err_count = 0; + pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); + if (!pipe_info->stream_urb) { + dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n"); + return -ENOMEM; + } + /* transfer buffer allocated in board_init */ + usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, + pipe, + pipe_info->transfer_buffer, + pipe_info->transfer_size, + read_pipe_completion, pipe_info); + + DBG("submitting URB %p\n", pipe_info->stream_urb); + retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); + if (retval) { + printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); + return retval; + } + + return 0; +} + +static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe_info; + + if (cam == NULL) { + printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); + return; + } + DBG("stop read pipe\n"); + pipe_info = cam->pipe; + if (pipe_info) { + if (pipe_info->state != 0) + pipe_info->state = 0; + + if (pipe_info->stream_urb) { + /* cancel urb */ + usb_kill_urb(pipe_info->stream_urb); + usb_free_urb(pipe_info->stream_urb); + pipe_info->stream_urb = NULL; + } + } + return; +} + +/* starts acquisition process */ +static int zr364xx_start_acquire(struct zr364xx_camera *cam) +{ + int j; + + DBG("start acquire\n"); + + cam->last_frame = -1; + cam->cur_frame = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + cam->b_acquire = 1; + return 0; +} + +static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) +{ + cam->b_acquire = 0; + return 0; +} + +static int zr364xx_prepare(struct zr364xx_camera *cam) +{ + int res; + int i, j; + + for (i = 0; init[cam->method][i].size != -1; i++) { + res = send_control_msg(cam->udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + if (res < 0) { + dev_err(&cam->udev->dev, + "error during open sequence: %d\n", i); + return res; + } + } + + cam->skip = 2; + cam->last_frame = -1; + cam->cur_frame = 0; + cam->frame_count = 0; + for (j = 0; j < FRAMES; j++) { + cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[j].cur_size = 0; + } + v4l2_ctrl_handler_setup(&cam->ctrl_handler); + return 0; +} + +static int zr364xx_vidioc_streamon(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int res; + + DBG("%s\n", __func__); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (cam->owner && cam->owner != priv) + return -EBUSY; + + res = zr364xx_prepare(cam); + if (res) + return res; + res = videobuf_streamon(&cam->vb_vidq); + if (res == 0) { + zr364xx_start_acquire(cam); + cam->owner = file->private_data; + } + return res; +} + +static int zr364xx_vidioc_streamoff(struct file *file, void *priv, + enum v4l2_buf_type type) +{ + struct zr364xx_camera *cam = video_drvdata(file); + + DBG("%s\n", __func__); + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + if (cam->owner && cam->owner != priv) + return -EBUSY; + zr364xx_stop_acquire(cam); + return videobuf_streamoff(&cam->vb_vidq); +} + + +/* open the camera */ +static int zr364xx_open(struct file *file) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int err; + + DBG("%s\n", __func__); + + if (mutex_lock_interruptible(&cam->lock)) + return -ERESTARTSYS; + + err = v4l2_fh_open(file); + if (err) + goto out; + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + err = 0; + +out: + mutex_unlock(&cam->lock); + DBG("%s: %d\n", __func__, err); + return err; +} + +static void zr364xx_release(struct v4l2_device *v4l2_dev) +{ + struct zr364xx_camera *cam = + container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); + unsigned long i; + + v4l2_device_unregister(&cam->v4l2_dev); + + videobuf_mmap_free(&cam->vb_vidq); + + /* release sys buffers */ + for (i = 0; i < FRAMES; i++) { + if (cam->buffer.frame[i].lpvbits) { + DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); + vfree(cam->buffer.frame[i].lpvbits); + } + cam->buffer.frame[i].lpvbits = NULL; + } + + v4l2_ctrl_handler_free(&cam->ctrl_handler); + /* release transfer buffer */ + kfree(cam->pipe->transfer_buffer); + kfree(cam); +} + +/* release the camera */ +static int zr364xx_close(struct file *file) +{ + struct zr364xx_camera *cam; + struct usb_device *udev; + int i; + + DBG("%s\n", __func__); + cam = video_drvdata(file); + + mutex_lock(&cam->lock); + udev = cam->udev; + + if (file->private_data == cam->owner) { + /* turn off stream */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + videobuf_streamoff(&cam->vb_vidq); + + for (i = 0; i < 2; i++) { + send_control_msg(udev, 1, init[cam->method][i].value, + 0, init[cam->method][i].bytes, + init[cam->method][i].size); + } + cam->owner = NULL; + } + + /* Added some delay here, since opening/closing the camera quickly, + * like Ekiga does during its startup, can crash the webcam + */ + mdelay(100); + mutex_unlock(&cam->lock); + return v4l2_fh_release(file); +} + + +static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct zr364xx_camera *cam = video_drvdata(file); + int ret; + + if (cam == NULL) { + DBG("%s: cam == NULL\n", __func__); + return -ENODEV; + } + DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); + + ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); + + DBG("vma start=0x%08lx, size=%ld, ret=%d\n", + (unsigned long)vma->vm_start, + (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); + return ret; +} + +static unsigned int zr364xx_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct zr364xx_camera *cam = video_drvdata(file); + struct videobuf_queue *q = &cam->vb_vidq; + unsigned res = v4l2_ctrl_poll(file, wait); + + _DBG("%s\n", __func__); + + return res | videobuf_poll_stream(file, q, wait); +} + +static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { + .s_ctrl = zr364xx_s_ctrl, +}; + +static const struct v4l2_file_operations zr364xx_fops = { + .owner = THIS_MODULE, + .open = zr364xx_open, + .release = zr364xx_close, + .read = zr364xx_read, + .mmap = zr364xx_mmap, + .unlocked_ioctl = video_ioctl2, + .poll = zr364xx_poll, +}; + +static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { + .vidioc_querycap = zr364xx_vidioc_querycap, + .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, + .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, + .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, + .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, + .vidioc_enum_input = zr364xx_vidioc_enum_input, + .vidioc_g_input = zr364xx_vidioc_g_input, + .vidioc_s_input = zr364xx_vidioc_s_input, + .vidioc_streamon = zr364xx_vidioc_streamon, + .vidioc_streamoff = zr364xx_vidioc_streamoff, + .vidioc_reqbufs = zr364xx_vidioc_reqbufs, + .vidioc_querybuf = zr364xx_vidioc_querybuf, + .vidioc_qbuf = zr364xx_vidioc_qbuf, + .vidioc_dqbuf = zr364xx_vidioc_dqbuf, + .vidioc_log_status = v4l2_ctrl_log_status, + .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, + .vidioc_unsubscribe_event = v4l2_event_unsubscribe, +}; + +static struct video_device zr364xx_template = { + .name = DRIVER_DESC, + .fops = &zr364xx_fops, + .ioctl_ops = &zr364xx_ioctl_ops, + .release = video_device_release_empty, +}; + + + +/*******************/ +/* USB integration */ +/*******************/ +static int zr364xx_board_init(struct zr364xx_camera *cam) +{ + struct zr364xx_pipeinfo *pipe = cam->pipe; + unsigned long i; + + DBG("board init: %p\n", cam); + memset(pipe, 0, sizeof(*pipe)); + pipe->cam = cam; + pipe->transfer_size = BUFFER_SIZE; + + pipe->transfer_buffer = kzalloc(pipe->transfer_size, + GFP_KERNEL); + if (pipe->transfer_buffer == NULL) { + DBG("out of memory!\n"); + return -ENOMEM; + } + + cam->b_acquire = 0; + cam->frame_count = 0; + + /*** start create system buffers ***/ + for (i = 0; i < FRAMES; i++) { + /* always allocate maximum size for system buffers */ + cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); + + DBG("valloc %p, idx %lu, pdata %p\n", + &cam->buffer.frame[i], i, + cam->buffer.frame[i].lpvbits); + if (cam->buffer.frame[i].lpvbits == NULL) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. " + "Using less frames\n"); + break; + } + } + + if (i == 0) { + printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); + kfree(cam->pipe->transfer_buffer); + cam->pipe->transfer_buffer = NULL; + return -ENOMEM; + } else + cam->buffer.dwFrames = i; + + /* make sure internal states are set */ + for (i = 0; i < FRAMES; i++) { + cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; + cam->buffer.frame[i].cur_size = 0; + } + + cam->cur_frame = 0; + cam->last_frame = -1; + /*** end create system buffers ***/ + + /* start read pipe */ + zr364xx_start_readpipe(cam); + DBG(": board initialized\n"); + return 0; +} + +static int zr364xx_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *udev = interface_to_usbdev(intf); + struct zr364xx_camera *cam = NULL; + struct usb_host_interface *iface_desc; + struct usb_endpoint_descriptor *endpoint; + struct v4l2_ctrl_handler *hdl; + int err; + int i; + + DBG("probing...\n"); + + dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); + dev_info(&intf->dev, "model %04x:%04x detected\n", + le16_to_cpu(udev->descriptor.idVendor), + le16_to_cpu(udev->descriptor.idProduct)); + + cam = kzalloc(sizeof(struct zr364xx_camera), GFP_KERNEL); + if (cam == NULL) { + dev_err(&udev->dev, "cam: out of memory !\n"); + return -ENOMEM; + } + + cam->v4l2_dev.release = zr364xx_release; + err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); + if (err < 0) { + dev_err(&udev->dev, "couldn't register v4l2_device\n"); + kfree(cam); + return err; + } + hdl = &cam->ctrl_handler; + v4l2_ctrl_handler_init(hdl, 1); + v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, + V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); + if (hdl->error) { + err = hdl->error; + dev_err(&udev->dev, "couldn't register control\n"); + goto fail; + } + /* save the init method used by this camera */ + cam->method = id->driver_info; + mutex_init(&cam->lock); + cam->vdev = zr364xx_template; + cam->vdev.lock = &cam->lock; + cam->vdev.v4l2_dev = &cam->v4l2_dev; + cam->vdev.ctrl_handler = &cam->ctrl_handler; + set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); + video_set_drvdata(&cam->vdev, cam); + if (debug) + cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; + + cam->udev = udev; + + switch (mode) { + case 1: + dev_info(&udev->dev, "160x120 mode selected\n"); + cam->width = 160; + cam->height = 120; + break; + case 2: + dev_info(&udev->dev, "640x480 mode selected\n"); + cam->width = 640; + cam->height = 480; + break; + default: + dev_info(&udev->dev, "320x240 mode selected\n"); + cam->width = 320; + cam->height = 240; + break; + } + + m0d1[0] = mode; + m1[2].value = 0xf000 + mode; + m2[1].value = 0xf000 + mode; + + /* special case for METHOD3, the modes are different */ + if (cam->method == METHOD3) { + switch (mode) { + case 1: + m2[1].value = 0xf000 + 4; + break; + case 2: + m2[1].value = 0xf000 + 0; + break; + default: + m2[1].value = 0xf000 + 1; + break; + } + } + + header2[437] = cam->height / 256; + header2[438] = cam->height % 256; + header2[439] = cam->width / 256; + header2[440] = cam->width % 256; + + cam->nb = 0; + + DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); + + /* set up the endpoint information */ + iface_desc = intf->cur_altsetting; + DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); + for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { + endpoint = &iface_desc->endpoint[i].desc; + if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { + /* we found the bulk in endpoint */ + cam->read_endpoint = endpoint->bEndpointAddress; + } + } + + if (!cam->read_endpoint) { + err = -ENOMEM; + dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); + goto fail; + } + + /* v4l */ + INIT_LIST_HEAD(&cam->vidq.active); + cam->vidq.cam = cam; + + usb_set_intfdata(intf, cam); + + /* load zr364xx board specific */ + err = zr364xx_board_init(cam); + if (!err) + err = v4l2_ctrl_handler_setup(hdl); + if (err) + goto fail; + + spin_lock_init(&cam->slock); + + cam->fmt = formats; + + videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, + NULL, &cam->slock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, + V4L2_FIELD_NONE, + sizeof(struct zr364xx_buffer), cam, &cam->lock); + + err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); + if (err) { + dev_err(&udev->dev, "video_register_device failed\n"); + goto fail; + } + + dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", + video_device_node_name(&cam->vdev)); + return 0; + +fail: + v4l2_ctrl_handler_free(hdl); + v4l2_device_unregister(&cam->v4l2_dev); + kfree(cam); + return err; +} + + +static void zr364xx_disconnect(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + mutex_lock(&cam->lock); + usb_set_intfdata(intf, NULL); + dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); + video_unregister_device(&cam->vdev); + v4l2_device_disconnect(&cam->v4l2_dev); + + /* stops the read pipe if it is running */ + if (cam->b_acquire) + zr364xx_stop_acquire(cam); + + zr364xx_stop_readpipe(cam); + mutex_unlock(&cam->lock); + v4l2_device_put(&cam->v4l2_dev); +} + + +#ifdef CONFIG_PM +static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + + cam->was_streaming = cam->b_acquire; + if (!cam->was_streaming) + return 0; + zr364xx_stop_acquire(cam); + zr364xx_stop_readpipe(cam); + return 0; +} + +static int zr364xx_resume(struct usb_interface *intf) +{ + struct zr364xx_camera *cam = usb_get_intfdata(intf); + int res; + + if (!cam->was_streaming) + return 0; + + zr364xx_start_readpipe(cam); + res = zr364xx_prepare(cam); + if (!res) + zr364xx_start_acquire(cam); + return res; +} +#endif + +/**********************/ +/* Module integration */ +/**********************/ + +static struct usb_driver zr364xx_driver = { + .name = "zr364xx", + .probe = zr364xx_probe, + .disconnect = zr364xx_disconnect, +#ifdef CONFIG_PM + .suspend = zr364xx_suspend, + .resume = zr364xx_resume, + .reset_resume = zr364xx_resume, +#endif + .id_table = device_table +}; + +module_usb_driver(zr364xx_driver); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_VERSION(DRIVER_VERSION); diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 097b17ced172..f52799232029 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -605,56 +605,6 @@ config VIDEO_VIVI Say Y here if you want to test video apps or debug V4L devices. In doubt, say N. -# -# USB Multimedia device configuration -# - -menuconfig V4L_USB_DRIVERS - bool "V4L USB devices" - depends on USB - default y - -if V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT - -config USB_ZR364XX - tristate "USB ZR364XX Camera support" - depends on VIDEO_V4L2 - select VIDEOBUF_GEN - select VIDEOBUF_VMALLOC - ---help--- - Say Y here if you want to connect this type of camera to your - computer's USB port. - See for more info - and list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called zr364xx. - -config USB_STKWEBCAM - tristate "USB Syntek DC1125 Camera support" - depends on VIDEO_V4L2 && EXPERIMENTAL - ---help--- - Say Y here if you want to use this type of camera. - Supported devices are typically found in some Asus laptops, - with USB id 174f:a311 and 05e1:0501. Other Syntek cameras - may be supported by the stk11xx driver, from which this is - derived, see - - To compile this driver as a module, choose M here: the - module will be called stkwebcam. - -config USB_S2255 - tristate "USB Sensoray 2255 video capture device" - depends on VIDEO_V4L2 - select VIDEOBUF_VMALLOC - default n - help - Say Y here if you want support for the Sensoray 2255 USB device. - This driver can be compiled as a module, called s2255drv. - - -endif # V4L_USB_DRIVERS && MEDIA_CAMERA_SUPPORT - # # PCI drivers configuration - No devices here are for webcams # diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index a22a2580ce30..4ad5bd9246bf 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -4,8 +4,6 @@ msp3400-objs := msp3400-driver.o msp3400-kthreads.o -stkwebcam-objs := stk-webcam.o stk-sensor.o - omap2cam-objs := omap24xxcam.o omap24xxcam-dma.o # Helper modules @@ -119,12 +117,6 @@ obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o obj-$(CONFIG_VIDEO_OMAP3) += omap3isp/ -obj-$(CONFIG_USB_ZR364XX) += zr364xx.o -obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o - - -obj-$(CONFIG_USB_S2255) += s2255drv.o - obj-$(CONFIG_VIDEO_IVTV) += ivtv/ obj-$(CONFIG_VIDEO_CX18) += cx18/ diff --git a/drivers/media/video/s2255drv.c b/drivers/media/video/s2255drv.c deleted file mode 100644 index 6c7960cc7506..000000000000 --- a/drivers/media/video/s2255drv.c +++ /dev/null @@ -1,2689 +0,0 @@ -/* - * s2255drv.c - a driver for the Sensoray 2255 USB video capture device - * - * Copyright (C) 2007-2010 by Sensoray Company Inc. - * Dean Anderson - * - * Some video buffer code based on vivi driver: - * - * Sensoray 2255 device supports 4 simultaneous channels. - * The channels are not "crossbar" inputs, they are physically - * attached to separate video decoders. - * - * Because of USB2.0 bandwidth limitations. There is only a - * certain amount of data which may be transferred at one time. - * - * Example maximum bandwidth utilization: - * - * -full size, color mode YUYV or YUV422P: 2 channels at once - * -full or half size Grey scale: all 4 channels at once - * -half size, color mode YUYV or YUV422P: all 4 channels at once - * -full size, color mode YUYV or YUV422P 1/2 frame rate: all 4 channels - * at once. - * - * 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. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define S2255_VERSION "1.22.1" -#define FIRMWARE_FILE_NAME "f2255usb.bin" - -/* default JPEG quality */ -#define S2255_DEF_JPEG_QUAL 50 -/* vendor request in */ -#define S2255_VR_IN 0 -/* vendor request out */ -#define S2255_VR_OUT 1 -/* firmware query */ -#define S2255_VR_FW 0x30 -/* USB endpoint number for configuring the device */ -#define S2255_CONFIG_EP 2 -/* maximum time for DSP to start responding after last FW word loaded(ms) */ -#define S2255_DSP_BOOTTIME 800 -/* maximum time to wait for firmware to load (ms) */ -#define S2255_LOAD_TIMEOUT (5000 + S2255_DSP_BOOTTIME) -#define S2255_DEF_BUFS 16 -#define S2255_SETMODE_TIMEOUT 500 -#define S2255_VIDSTATUS_TIMEOUT 350 -#define S2255_MARKER_FRAME cpu_to_le32(0x2255DA4AL) -#define S2255_MARKER_RESPONSE cpu_to_le32(0x2255ACACL) -#define S2255_RESPONSE_SETMODE cpu_to_le32(0x01) -#define S2255_RESPONSE_FW cpu_to_le32(0x10) -#define S2255_RESPONSE_STATUS cpu_to_le32(0x20) -#define S2255_USB_XFER_SIZE (16 * 1024) -#define MAX_CHANNELS 4 -#define SYS_FRAMES 4 -/* maximum size is PAL full size plus room for the marker header(s) */ -#define SYS_FRAMES_MAXSIZE (720*288*2*2 + 4096) -#define DEF_USB_BLOCK S2255_USB_XFER_SIZE -#define LINE_SZ_4CIFS_NTSC 640 -#define LINE_SZ_2CIFS_NTSC 640 -#define LINE_SZ_1CIFS_NTSC 320 -#define LINE_SZ_4CIFS_PAL 704 -#define LINE_SZ_2CIFS_PAL 704 -#define LINE_SZ_1CIFS_PAL 352 -#define NUM_LINES_4CIFS_NTSC 240 -#define NUM_LINES_2CIFS_NTSC 240 -#define NUM_LINES_1CIFS_NTSC 240 -#define NUM_LINES_4CIFS_PAL 288 -#define NUM_LINES_2CIFS_PAL 288 -#define NUM_LINES_1CIFS_PAL 288 -#define LINE_SZ_DEF 640 -#define NUM_LINES_DEF 240 - - -/* predefined settings */ -#define FORMAT_NTSC 1 -#define FORMAT_PAL 2 - -#define SCALE_4CIFS 1 /* 640x480(NTSC) or 704x576(PAL) */ -#define SCALE_2CIFS 2 /* 640x240(NTSC) or 704x288(PAL) */ -#define SCALE_1CIFS 3 /* 320x240(NTSC) or 352x288(PAL) */ -/* SCALE_4CIFSI is the 2 fields interpolated into one */ -#define SCALE_4CIFSI 4 /* 640x480(NTSC) or 704x576(PAL) high quality */ - -#define COLOR_YUVPL 1 /* YUV planar */ -#define COLOR_YUVPK 2 /* YUV packed */ -#define COLOR_Y8 4 /* monochrome */ -#define COLOR_JPG 5 /* JPEG */ - -#define MASK_COLOR 0x000000ff -#define MASK_JPG_QUALITY 0x0000ff00 -#define MASK_INPUT_TYPE 0x000f0000 -/* frame decimation. */ -#define FDEC_1 1 /* capture every frame. default */ -#define FDEC_2 2 /* capture every 2nd frame */ -#define FDEC_3 3 /* capture every 3rd frame */ -#define FDEC_5 5 /* capture every 5th frame */ - -/*------------------------------------------------------- - * Default mode parameters. - *-------------------------------------------------------*/ -#define DEF_SCALE SCALE_4CIFS -#define DEF_COLOR COLOR_YUVPL -#define DEF_FDEC FDEC_1 -#define DEF_BRIGHT 0 -#define DEF_CONTRAST 0x5c -#define DEF_SATURATION 0x80 -#define DEF_HUE 0 - -/* usb config commands */ -#define IN_DATA_TOKEN cpu_to_le32(0x2255c0de) -#define CMD_2255 0xc2255000 -#define CMD_SET_MODE cpu_to_le32((CMD_2255 | 0x10)) -#define CMD_START cpu_to_le32((CMD_2255 | 0x20)) -#define CMD_STOP cpu_to_le32((CMD_2255 | 0x30)) -#define CMD_STATUS cpu_to_le32((CMD_2255 | 0x40)) - -struct s2255_mode { - u32 format; /* input video format (NTSC, PAL) */ - u32 scale; /* output video scale */ - u32 color; /* output video color format */ - u32 fdec; /* frame decimation */ - u32 bright; /* brightness */ - u32 contrast; /* contrast */ - u32 saturation; /* saturation */ - u32 hue; /* hue (NTSC only)*/ - u32 single; /* capture 1 frame at a time (!=0), continuously (==0)*/ - u32 usb_block; /* block size. should be 4096 of DEF_USB_BLOCK */ - u32 restart; /* if DSP requires restart */ -}; - - -#define S2255_READ_IDLE 0 -#define S2255_READ_FRAME 1 - -/* frame structure */ -struct s2255_framei { - unsigned long size; - unsigned long ulState; /* ulState:S2255_READ_IDLE, S2255_READ_FRAME*/ - void *lpvbits; /* image data */ - unsigned long cur_size; /* current data copied to it */ -}; - -/* image buffer structure */ -struct s2255_bufferi { - unsigned long dwFrames; /* number of frames in buffer */ - struct s2255_framei frame[SYS_FRAMES]; /* array of FRAME structures */ -}; - -#define DEF_MODEI_NTSC_CONT {FORMAT_NTSC, DEF_SCALE, DEF_COLOR, \ - DEF_FDEC, DEF_BRIGHT, DEF_CONTRAST, DEF_SATURATION, \ - DEF_HUE, 0, DEF_USB_BLOCK, 0} - -struct s2255_dmaqueue { - struct list_head active; - struct s2255_dev *dev; -}; - -/* for firmware loading, fw_state */ -#define S2255_FW_NOTLOADED 0 -#define S2255_FW_LOADED_DSPWAIT 1 -#define S2255_FW_SUCCESS 2 -#define S2255_FW_FAILED 3 -#define S2255_FW_DISCONNECTING 4 -#define S2255_FW_MARKER cpu_to_le32(0x22552f2f) -/* 2255 read states */ -#define S2255_READ_IDLE 0 -#define S2255_READ_FRAME 1 -struct s2255_fw { - int fw_loaded; - int fw_size; - struct urb *fw_urb; - atomic_t fw_state; - void *pfw_data; - wait_queue_head_t wait_fw; - const struct firmware *fw; -}; - -struct s2255_pipeinfo { - u32 max_transfer_size; - u32 cur_transfer_size; - u8 *transfer_buffer; - u32 state; - void *stream_urb; - void *dev; /* back pointer to s2255_dev struct*/ - u32 err_count; - u32 idx; -}; - -struct s2255_fmt; /*forward declaration */ -struct s2255_dev; - -struct s2255_channel { - struct video_device vdev; - int resources; - struct s2255_dmaqueue vidq; - struct s2255_bufferi buffer; - struct s2255_mode mode; - /* jpeg compression */ - struct v4l2_jpegcompression jc; - /* capture parameters (for high quality mode full size) */ - struct v4l2_captureparm cap_parm; - int cur_frame; - int last_frame; - - int b_acquire; - /* allocated image size */ - unsigned long req_image_size; - /* received packet size */ - unsigned long pkt_size; - int bad_payload; - unsigned long frame_count; - /* if JPEG image */ - int jpg_size; - /* if channel configured to default state */ - int configured; - wait_queue_head_t wait_setmode; - int setmode_ready; - /* video status items */ - int vidstatus; - wait_queue_head_t wait_vidstatus; - int vidstatus_ready; - unsigned int width; - unsigned int height; - const struct s2255_fmt *fmt; - int idx; /* channel number on device, 0-3 */ -}; - - -struct s2255_dev { - struct s2255_channel channel[MAX_CHANNELS]; - struct v4l2_device v4l2_dev; - atomic_t num_channels; - int frames; - struct mutex lock; /* channels[].vdev.lock */ - struct usb_device *udev; - struct usb_interface *interface; - u8 read_endpoint; - struct timer_list timer; - struct s2255_fw *fw_data; - struct s2255_pipeinfo pipe; - u32 cc; /* current channel */ - int frame_ready; - int chn_ready; - spinlock_t slock; - /* dsp firmware version (f2255usb.bin) */ - int dsp_fw_ver; - u16 pid; /* product id */ -}; - -static inline struct s2255_dev *to_s2255_dev(struct v4l2_device *v4l2_dev) -{ - return container_of(v4l2_dev, struct s2255_dev, v4l2_dev); -} - -struct s2255_fmt { - char *name; - u32 fourcc; - int depth; -}; - -/* buffer for one video frame */ -struct s2255_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - const struct s2255_fmt *fmt; -}; - -struct s2255_fh { - struct s2255_dev *dev; - struct videobuf_queue vb_vidq; - enum v4l2_buf_type type; - struct s2255_channel *channel; - int resources; -}; - -/* current cypress EEPROM firmware version */ -#define S2255_CUR_USB_FWVER ((3 << 8) | 12) -/* current DSP FW version */ -#define S2255_CUR_DSP_FWVER 10104 -/* Need DSP version 5+ for video status feature */ -#define S2255_MIN_DSP_STATUS 5 -#define S2255_MIN_DSP_COLORFILTER 8 -#define S2255_NORMS (V4L2_STD_PAL | V4L2_STD_NTSC) - -/* private V4L2 controls */ - -/* - * The following chart displays how COLORFILTER should be set - * ========================================================= - * = fourcc = COLORFILTER = - * = =============================== - * = = 0 = 1 = - * ========================================================= - * = V4L2_PIX_FMT_GREY(Y8) = monochrome from = monochrome= - * = = s-video or = composite = - * = = B/W camera = input = - * ========================================================= - * = other = color, svideo = color, = - * = = = composite = - * ========================================================= - * - * Notes: - * channels 0-3 on 2255 are composite - * channels 0-1 on 2257 are composite, 2-3 are s-video - * If COLORFILTER is 0 with a composite color camera connected, - * the output will appear monochrome but hatching - * will occur. - * COLORFILTER is different from "color killer" and "color effects" - * for reasons above. - */ -#define S2255_V4L2_YC_ON 1 -#define S2255_V4L2_YC_OFF 0 -#define V4L2_CID_PRIVATE_COLORFILTER (V4L2_CID_PRIVATE_BASE + 0) - -/* frame prefix size (sent once every frame) */ -#define PREFIX_SIZE 512 - -/* Channels on box are in reverse order */ -static unsigned long G_chnmap[MAX_CHANNELS] = {3, 2, 1, 0}; - -static int debug; -static int *s2255_debug = &debug; - -static int s2255_start_readpipe(struct s2255_dev *dev); -static void s2255_stop_readpipe(struct s2255_dev *dev); -static int s2255_start_acquire(struct s2255_channel *channel); -static int s2255_stop_acquire(struct s2255_channel *channel); -static void s2255_fillbuff(struct s2255_channel *chn, struct s2255_buffer *buf, - int jpgsize); -static int s2255_set_mode(struct s2255_channel *chan, struct s2255_mode *mode); -static int s2255_board_shutdown(struct s2255_dev *dev); -static void s2255_fwload_start(struct s2255_dev *dev, int reset); -static void s2255_destroy(struct s2255_dev *dev); -static long s2255_vendor_req(struct s2255_dev *dev, unsigned char req, - u16 index, u16 value, void *buf, - s32 buf_len, int bOut); - -/* dev_err macro with driver name */ -#define S2255_DRIVER_NAME "s2255" -#define s2255_dev_err(dev, fmt, arg...) \ - dev_err(dev, S2255_DRIVER_NAME " - " fmt, ##arg) - -#define dprintk(level, fmt, arg...) \ - do { \ - if (*s2255_debug >= (level)) { \ - printk(KERN_DEBUG S2255_DRIVER_NAME \ - ": " fmt, ##arg); \ - } \ - } while (0) - -static struct usb_driver s2255_driver; - -/* Declare static vars that will be used as parameters */ -static unsigned int vid_limit = 16; /* Video memory limit, in Mb */ - -/* start video number */ -static int video_nr = -1; /* /dev/videoN, -1 for autodetect */ - -/* Enable jpeg capture. */ -static int jpeg_enable = 1; - -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level(0-100) default 0"); -module_param(vid_limit, int, 0644); -MODULE_PARM_DESC(vid_limit, "video memory limit(Mb)"); -module_param(video_nr, int, 0644); -MODULE_PARM_DESC(video_nr, "start video minor(-1 default autodetect)"); -module_param(jpeg_enable, int, 0644); -MODULE_PARM_DESC(jpeg_enable, "Jpeg enable(1-on 0-off) default 1"); - -/* USB device table */ -#define USB_SENSORAY_VID 0x1943 -static struct usb_device_id s2255_table[] = { - {USB_DEVICE(USB_SENSORAY_VID, 0x2255)}, - {USB_DEVICE(USB_SENSORAY_VID, 0x2257)}, /*same family as 2255*/ - { } /* Terminating entry */ -}; -MODULE_DEVICE_TABLE(usb, s2255_table); - -#define BUFFER_TIMEOUT msecs_to_jiffies(400) - -/* image formats. */ -/* JPEG formats must be defined last to support jpeg_enable parameter */ -static const struct s2255_fmt formats[] = { - { - .name = "4:2:2, planar, YUV422P", - .fourcc = V4L2_PIX_FMT_YUV422P, - .depth = 16 - - }, { - .name = "4:2:2, packed, YUYV", - .fourcc = V4L2_PIX_FMT_YUYV, - .depth = 16 - - }, { - .name = "4:2:2, packed, UYVY", - .fourcc = V4L2_PIX_FMT_UYVY, - .depth = 16 - }, { - .name = "8bpp GREY", - .fourcc = V4L2_PIX_FMT_GREY, - .depth = 8 - }, { - .name = "JPG", - .fourcc = V4L2_PIX_FMT_JPEG, - .depth = 24 - }, { - .name = "MJPG", - .fourcc = V4L2_PIX_FMT_MJPEG, - .depth = 24 - } -}; - -static int norm_maxw(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - LINE_SZ_4CIFS_NTSC : LINE_SZ_4CIFS_PAL; -} - -static int norm_maxh(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - (NUM_LINES_1CIFS_NTSC * 2) : (NUM_LINES_1CIFS_PAL * 2); -} - -static int norm_minw(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - LINE_SZ_1CIFS_NTSC : LINE_SZ_1CIFS_PAL; -} - -static int norm_minh(struct video_device *vdev) -{ - return (vdev->current_norm & V4L2_STD_NTSC) ? - (NUM_LINES_1CIFS_NTSC) : (NUM_LINES_1CIFS_PAL); -} - - -/* - * TODO: fixme: move YUV reordering to hardware - * converts 2255 planar format to yuyv or uyvy - */ -static void planar422p_to_yuv_packed(const unsigned char *in, - unsigned char *out, - int width, int height, - int fmt) -{ - unsigned char *pY; - unsigned char *pCb; - unsigned char *pCr; - unsigned long size = height * width; - unsigned int i; - pY = (unsigned char *)in; - pCr = (unsigned char *)in + height * width; - pCb = (unsigned char *)in + height * width + (height * width / 2); - for (i = 0; i < size * 2; i += 4) { - out[i] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCr++; - out[i + 1] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCr++ : *pY++; - out[i + 2] = (fmt == V4L2_PIX_FMT_YUYV) ? *pY++ : *pCb++; - out[i + 3] = (fmt == V4L2_PIX_FMT_YUYV) ? *pCb++ : *pY++; - } - return; -} - -static void s2255_reset_dsppower(struct s2255_dev *dev) -{ - s2255_vendor_req(dev, 0x40, 0x0000, 0x0001, NULL, 0, 1); - msleep(10); - s2255_vendor_req(dev, 0x50, 0x0000, 0x0000, NULL, 0, 1); - msleep(600); - s2255_vendor_req(dev, 0x10, 0x0000, 0x0000, NULL, 0, 1); - return; -} - -/* kickstarts the firmware loading. from probe - */ -static void s2255_timer(unsigned long user_data) -{ - struct s2255_fw *data = (struct s2255_fw *)user_data; - dprintk(100, "%s\n", __func__); - if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { - printk(KERN_ERR "s2255: can't submit urb\n"); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } -} - - -/* this loads the firmware asynchronously. - Originally this was done synchroously in probe. - But it is better to load it asynchronously here than block - inside the probe function. Blocking inside probe affects boot time. - FW loading is triggered by the timer in the probe function -*/ -static void s2255_fwchunk_complete(struct urb *urb) -{ - struct s2255_fw *data = urb->context; - struct usb_device *udev = urb->dev; - int len; - dprintk(100, "%s: udev %p urb %p", __func__, udev, urb); - if (urb->status) { - dev_err(&udev->dev, "URB failed with status %d\n", urb->status); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } - if (data->fw_urb == NULL) { - s2255_dev_err(&udev->dev, "disconnected\n"); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } -#define CHUNK_SIZE 512 - /* all USB transfers must be done with continuous kernel memory. - can't allocate more than 128k in current linux kernel, so - upload the firmware in chunks - */ - if (data->fw_loaded < data->fw_size) { - len = (data->fw_loaded + CHUNK_SIZE) > data->fw_size ? - data->fw_size % CHUNK_SIZE : CHUNK_SIZE; - - if (len < CHUNK_SIZE) - memset(data->pfw_data, 0, CHUNK_SIZE); - - dprintk(100, "completed len %d, loaded %d \n", len, - data->fw_loaded); - - memcpy(data->pfw_data, - (char *) data->fw->data + data->fw_loaded, len); - - usb_fill_bulk_urb(data->fw_urb, udev, usb_sndbulkpipe(udev, 2), - data->pfw_data, CHUNK_SIZE, - s2255_fwchunk_complete, data); - if (usb_submit_urb(data->fw_urb, GFP_ATOMIC) < 0) { - dev_err(&udev->dev, "failed submit URB\n"); - atomic_set(&data->fw_state, S2255_FW_FAILED); - /* wake up anything waiting for the firmware */ - wake_up(&data->wait_fw); - return; - } - data->fw_loaded += len; - } else { - atomic_set(&data->fw_state, S2255_FW_LOADED_DSPWAIT); - dprintk(100, "%s: firmware upload complete\n", __func__); - } - return; - -} - -static int s2255_got_frame(struct s2255_channel *channel, int jpgsize) -{ - struct s2255_dmaqueue *dma_q = &channel->vidq; - struct s2255_buffer *buf; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - unsigned long flags = 0; - int rc = 0; - spin_lock_irqsave(&dev->slock, flags); - if (list_empty(&dma_q->active)) { - dprintk(1, "No active queue to serve\n"); - rc = -1; - goto unlock; - } - buf = list_entry(dma_q->active.next, - struct s2255_buffer, vb.queue); - list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); - s2255_fillbuff(channel, buf, jpgsize); - wake_up(&buf->vb.done); - dprintk(2, "%s: [buf/i] [%p/%d]\n", __func__, buf, buf->vb.i); -unlock: - spin_unlock_irqrestore(&dev->slock, flags); - return rc; -} - -static const struct s2255_fmt *format_by_fourcc(int fourcc) -{ - unsigned int i; - for (i = 0; i < ARRAY_SIZE(formats); i++) { - if (-1 == formats[i].fourcc) - continue; - if (!jpeg_enable && ((formats[i].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[i].fourcc == V4L2_PIX_FMT_MJPEG))) - continue; - if (formats[i].fourcc == fourcc) - return formats + i; - } - return NULL; -} - -/* video buffer vmalloc implementation based partly on VIVI driver which is - * Copyright (c) 2006 by - * Mauro Carvalho Chehab - * Ted Walther - * John Sokol - * http://v4l.videotechnology.com/ - * - */ -static void s2255_fillbuff(struct s2255_channel *channel, - struct s2255_buffer *buf, int jpgsize) -{ - int pos = 0; - struct timeval ts; - const char *tmpbuf; - char *vbuf = videobuf_to_vmalloc(&buf->vb); - unsigned long last_frame; - - if (!vbuf) - return; - last_frame = channel->last_frame; - if (last_frame != -1) { - tmpbuf = - (const char *)channel->buffer.frame[last_frame].lpvbits; - switch (buf->fmt->fourcc) { - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - planar422p_to_yuv_packed((const unsigned char *)tmpbuf, - vbuf, buf->vb.width, - buf->vb.height, - buf->fmt->fourcc); - break; - case V4L2_PIX_FMT_GREY: - memcpy(vbuf, tmpbuf, buf->vb.width * buf->vb.height); - break; - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_MJPEG: - buf->vb.size = jpgsize; - memcpy(vbuf, tmpbuf, buf->vb.size); - break; - case V4L2_PIX_FMT_YUV422P: - memcpy(vbuf, tmpbuf, - buf->vb.width * buf->vb.height * 2); - break; - default: - printk(KERN_DEBUG "s2255: unknown format?\n"); - } - channel->last_frame = -1; - } else { - printk(KERN_ERR "s2255: =======no frame\n"); - return; - - } - dprintk(2, "s2255fill at : Buffer 0x%08lx size= %d\n", - (unsigned long)vbuf, pos); - /* tell v4l buffer was filled */ - - buf->vb.field_count = channel->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; - buf->vb.state = VIDEOBUF_DONE; -} - - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct s2255_fh *fh = vq->priv_data; - struct s2255_channel *channel = fh->channel; - *size = channel->width * channel->height * (channel->fmt->depth >> 3); - - if (0 == *count) - *count = S2255_DEF_BUFS; - - if (*size * *count > vid_limit * 1024 * 1024) - *count = (vid_limit * 1024 * 1024) / *size; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct s2255_buffer *buf) -{ - dprintk(4, "%s\n", __func__); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct s2255_fh *fh = vq->priv_data; - struct s2255_channel *channel = fh->channel; - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); - int rc; - int w = channel->width; - int h = channel->height; - dprintk(4, "%s, field=%d\n", __func__, field); - if (channel->fmt == NULL) - return -EINVAL; - - if ((w < norm_minw(&channel->vdev)) || - (w > norm_maxw(&channel->vdev)) || - (h < norm_minh(&channel->vdev)) || - (h > norm_maxh(&channel->vdev))) { - dprintk(4, "invalid buffer prepare\n"); - return -EINVAL; - } - buf->vb.size = w * h * (channel->fmt->depth >> 3); - if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) { - dprintk(4, "invalid buffer prepare\n"); - return -EINVAL; - } - - buf->fmt = channel->fmt; - buf->vb.width = w; - buf->vb.height = h; - buf->vb.field = field; - - if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; -fail: - free_buffer(vq, buf); - return rc; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); - struct s2255_fh *fh = vq->priv_data; - struct s2255_channel *channel = fh->channel; - struct s2255_dmaqueue *vidq = &channel->vidq; - dprintk(1, "%s\n", __func__); - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &vidq->active); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct s2255_buffer *buf = container_of(vb, struct s2255_buffer, vb); - struct s2255_fh *fh = vq->priv_data; - dprintk(4, "%s %d\n", __func__, fh->channel->idx); - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops s2255_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - - -static int res_get(struct s2255_fh *fh) -{ - struct s2255_channel *channel = fh->channel; - /* is it free? */ - if (channel->resources) - return 0; /* no, someone else uses it */ - /* it's free, grab it */ - channel->resources = 1; - fh->resources = 1; - dprintk(1, "s2255: res: get\n"); - return 1; -} - -static int res_locked(struct s2255_fh *fh) -{ - return fh->channel->resources; -} - -static int res_check(struct s2255_fh *fh) -{ - return fh->resources; -} - - -static void res_free(struct s2255_fh *fh) -{ - struct s2255_channel *channel = fh->channel; - channel->resources = 0; - fh->resources = 0; - dprintk(1, "res: put\n"); -} - -static int vidioc_querymenu(struct file *file, void *priv, - struct v4l2_querymenu *qmenu) -{ - static const char *colorfilter[] = { - "Off", - "On", - NULL - }; - if (qmenu->id == V4L2_CID_PRIVATE_COLORFILTER) { - int i; - const char **menu_items = colorfilter; - for (i = 0; i < qmenu->index && menu_items[i]; i++) - ; /* do nothing (from v4l2-common.c) */ - if (menu_items[i] == NULL || menu_items[i][0] == '\0') - return -EINVAL; - strlcpy(qmenu->name, menu_items[qmenu->index], - sizeof(qmenu->name)); - return 0; - } - return v4l2_ctrl_query_menu(qmenu, NULL, NULL); -} - -static int vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - strlcpy(cap->driver, "s2255", sizeof(cap->driver)); - strlcpy(cap->card, "s2255", sizeof(cap->card)); - usb_make_path(dev->udev, cap->bus_info, sizeof(cap->bus_info)); - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING; - return 0; -} - -static int vidioc_enum_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_fmtdesc *f) -{ - int index = f->index; - - if (index >= ARRAY_SIZE(formats)) - return -EINVAL; - if (!jpeg_enable && ((formats[index].fourcc == V4L2_PIX_FMT_JPEG) || - (formats[index].fourcc == V4L2_PIX_FMT_MJPEG))) - return -EINVAL; - dprintk(4, "name %s\n", formats[index].name); - strlcpy(f->description, formats[index].name, sizeof(f->description)); - f->pixelformat = formats[index].fourcc; - return 0; -} - -static int vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - - f->fmt.pix.width = channel->width; - f->fmt.pix.height = channel->height; - f->fmt.pix.field = fh->vb_vidq.field; - f->fmt.pix.pixelformat = channel->fmt->fourcc; - f->fmt.pix.bytesperline = f->fmt.pix.width * (channel->fmt->depth >> 3); - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - return 0; -} - -static int vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - const struct s2255_fmt *fmt; - enum v4l2_field field; - int b_any_field = 0; - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - int is_ntsc; - is_ntsc = - (channel->vdev.current_norm & V4L2_STD_NTSC) ? 1 : 0; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - - if (fmt == NULL) - return -EINVAL; - - field = f->fmt.pix.field; - if (field == V4L2_FIELD_ANY) - b_any_field = 1; - - dprintk(50, "%s NTSC: %d suggested width: %d, height: %d\n", - __func__, is_ntsc, f->fmt.pix.width, f->fmt.pix.height); - if (is_ntsc) { - /* NTSC */ - if (f->fmt.pix.height >= NUM_LINES_1CIFS_NTSC * 2) { - f->fmt.pix.height = NUM_LINES_1CIFS_NTSC * 2; - if (b_any_field) { - field = V4L2_FIELD_SEQ_TB; - } else if (!((field == V4L2_FIELD_INTERLACED) || - (field == V4L2_FIELD_SEQ_TB) || - (field == V4L2_FIELD_INTERLACED_TB))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - } else { - f->fmt.pix.height = NUM_LINES_1CIFS_NTSC; - if (b_any_field) { - field = V4L2_FIELD_TOP; - } else if (!((field == V4L2_FIELD_TOP) || - (field == V4L2_FIELD_BOTTOM))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - - } - if (f->fmt.pix.width >= LINE_SZ_4CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_4CIFS_NTSC; - else if (f->fmt.pix.width >= LINE_SZ_2CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_2CIFS_NTSC; - else if (f->fmt.pix.width >= LINE_SZ_1CIFS_NTSC) - f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; - else - f->fmt.pix.width = LINE_SZ_1CIFS_NTSC; - } else { - /* PAL */ - if (f->fmt.pix.height >= NUM_LINES_1CIFS_PAL * 2) { - f->fmt.pix.height = NUM_LINES_1CIFS_PAL * 2; - if (b_any_field) { - field = V4L2_FIELD_SEQ_TB; - } else if (!((field == V4L2_FIELD_INTERLACED) || - (field == V4L2_FIELD_SEQ_TB) || - (field == V4L2_FIELD_INTERLACED_TB))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - } else { - f->fmt.pix.height = NUM_LINES_1CIFS_PAL; - if (b_any_field) { - field = V4L2_FIELD_TOP; - } else if (!((field == V4L2_FIELD_TOP) || - (field == V4L2_FIELD_BOTTOM))) { - dprintk(1, "unsupported field setting\n"); - return -EINVAL; - } - } - if (f->fmt.pix.width >= LINE_SZ_4CIFS_PAL) { - f->fmt.pix.width = LINE_SZ_4CIFS_PAL; - field = V4L2_FIELD_SEQ_TB; - } else if (f->fmt.pix.width >= LINE_SZ_2CIFS_PAL) { - f->fmt.pix.width = LINE_SZ_2CIFS_PAL; - field = V4L2_FIELD_TOP; - } else if (f->fmt.pix.width >= LINE_SZ_1CIFS_PAL) { - f->fmt.pix.width = LINE_SZ_1CIFS_PAL; - field = V4L2_FIELD_TOP; - } else { - f->fmt.pix.width = LINE_SZ_1CIFS_PAL; - field = V4L2_FIELD_TOP; - } - } - f->fmt.pix.field = field; - f->fmt.pix.bytesperline = (f->fmt.pix.width * fmt->depth) >> 3; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - dprintk(50, "%s: set width %d height %d field %d\n", __func__, - f->fmt.pix.width, f->fmt.pix.height, f->fmt.pix.field); - return 0; -} - -static int vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - const struct s2255_fmt *fmt; - struct videobuf_queue *q = &fh->vb_vidq; - struct s2255_mode mode; - int ret; - - ret = vidioc_try_fmt_vid_cap(file, fh, f); - - if (ret < 0) - return ret; - - fmt = format_by_fourcc(f->fmt.pix.pixelformat); - - if (fmt == NULL) - return -EINVAL; - - mutex_lock(&q->vb_lock); - - if (videobuf_queue_is_busy(&fh->vb_vidq)) { - dprintk(1, "queue busy\n"); - ret = -EBUSY; - goto out_s_fmt; - } - - if (res_locked(fh)) { - dprintk(1, "%s: channel busy\n", __func__); - ret = -EBUSY; - goto out_s_fmt; - } - mode = channel->mode; - channel->fmt = fmt; - channel->width = f->fmt.pix.width; - channel->height = f->fmt.pix.height; - fh->vb_vidq.field = f->fmt.pix.field; - fh->type = f->type; - if (channel->width > norm_minw(&channel->vdev)) { - if (channel->height > norm_minh(&channel->vdev)) { - if (channel->cap_parm.capturemode & - V4L2_MODE_HIGHQUALITY) - mode.scale = SCALE_4CIFSI; - else - mode.scale = SCALE_4CIFS; - } else - mode.scale = SCALE_2CIFS; - - } else { - mode.scale = SCALE_1CIFS; - } - /* color mode */ - switch (channel->fmt->fourcc) { - case V4L2_PIX_FMT_GREY: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_Y8; - break; - case V4L2_PIX_FMT_JPEG: - case V4L2_PIX_FMT_MJPEG: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_JPG; - mode.color |= (channel->jc.quality << 8); - break; - case V4L2_PIX_FMT_YUV422P: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_YUVPL; - break; - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_UYVY: - default: - mode.color &= ~MASK_COLOR; - mode.color |= COLOR_YUVPK; - break; - } - if ((mode.color & MASK_COLOR) != (channel->mode.color & MASK_COLOR)) - mode.restart = 1; - else if (mode.scale != channel->mode.scale) - mode.restart = 1; - else if (mode.format != channel->mode.format) - mode.restart = 1; - channel->mode = mode; - (void) s2255_set_mode(channel, &mode); - ret = 0; -out_s_fmt: - mutex_unlock(&q->vb_lock); - return ret; -} - -static int vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_reqbufs(&fh->vb_vidq, p); - return rc; -} - -static int vidioc_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_querybuf(&fh->vb_vidq, p); - return rc; -} - -static int vidioc_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_qbuf(&fh->vb_vidq, p); - return rc; -} - -static int vidioc_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) -{ - int rc; - struct s2255_fh *fh = priv; - rc = videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK); - return rc; -} - -/* write to the configuration pipe, synchronously */ -static int s2255_write_config(struct usb_device *udev, unsigned char *pbuf, - int size) -{ - int pipe; - int done; - long retval = -1; - if (udev) { - pipe = usb_sndbulkpipe(udev, S2255_CONFIG_EP); - retval = usb_bulk_msg(udev, pipe, pbuf, size, &done, 500); - } - return retval; -} - -static u32 get_transfer_size(struct s2255_mode *mode) -{ - int linesPerFrame = LINE_SZ_DEF; - int pixelsPerLine = NUM_LINES_DEF; - u32 outImageSize; - u32 usbInSize; - unsigned int mask_mult; - - if (mode == NULL) - return 0; - - if (mode->format == FORMAT_NTSC) { - switch (mode->scale) { - case SCALE_4CIFS: - case SCALE_4CIFSI: - linesPerFrame = NUM_LINES_4CIFS_NTSC * 2; - pixelsPerLine = LINE_SZ_4CIFS_NTSC; - break; - case SCALE_2CIFS: - linesPerFrame = NUM_LINES_2CIFS_NTSC; - pixelsPerLine = LINE_SZ_2CIFS_NTSC; - break; - case SCALE_1CIFS: - linesPerFrame = NUM_LINES_1CIFS_NTSC; - pixelsPerLine = LINE_SZ_1CIFS_NTSC; - break; - default: - break; - } - } else if (mode->format == FORMAT_PAL) { - switch (mode->scale) { - case SCALE_4CIFS: - case SCALE_4CIFSI: - linesPerFrame = NUM_LINES_4CIFS_PAL * 2; - pixelsPerLine = LINE_SZ_4CIFS_PAL; - break; - case SCALE_2CIFS: - linesPerFrame = NUM_LINES_2CIFS_PAL; - pixelsPerLine = LINE_SZ_2CIFS_PAL; - break; - case SCALE_1CIFS: - linesPerFrame = NUM_LINES_1CIFS_PAL; - pixelsPerLine = LINE_SZ_1CIFS_PAL; - break; - default: - break; - } - } - outImageSize = linesPerFrame * pixelsPerLine; - if ((mode->color & MASK_COLOR) != COLOR_Y8) { - /* 2 bytes/pixel if not monochrome */ - outImageSize *= 2; - } - - /* total bytes to send including prefix and 4K padding; - must be a multiple of USB_READ_SIZE */ - usbInSize = outImageSize + PREFIX_SIZE; /* always send prefix */ - mask_mult = 0xffffffffUL - DEF_USB_BLOCK + 1; - /* if size not a multiple of USB_READ_SIZE */ - if (usbInSize & ~mask_mult) - usbInSize = (usbInSize & mask_mult) + (DEF_USB_BLOCK); - return usbInSize; -} - -static void s2255_print_cfg(struct s2255_dev *sdev, struct s2255_mode *mode) -{ - struct device *dev = &sdev->udev->dev; - dev_info(dev, "------------------------------------------------\n"); - dev_info(dev, "format: %d\nscale %d\n", mode->format, mode->scale); - dev_info(dev, "fdec: %d\ncolor %d\n", mode->fdec, mode->color); - dev_info(dev, "bright: 0x%x\n", mode->bright); - dev_info(dev, "------------------------------------------------\n"); -} - -/* - * set mode is the function which controls the DSP. - * the restart parameter in struct s2255_mode should be set whenever - * the image size could change via color format, video system or image - * size. - * When the restart parameter is set, we sleep for ONE frame to allow the - * DSP time to get the new frame - */ -static int s2255_set_mode(struct s2255_channel *channel, - struct s2255_mode *mode) -{ - int res; - __le32 *buffer; - unsigned long chn_rev; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - dprintk(3, "%s channel: %d\n", __func__, channel->idx); - /* if JPEG, set the quality */ - if ((mode->color & MASK_COLOR) == COLOR_JPG) { - mode->color &= ~MASK_COLOR; - mode->color |= COLOR_JPG; - mode->color &= ~MASK_JPG_QUALITY; - mode->color |= (channel->jc.quality << 8); - } - /* save the mode */ - channel->mode = *mode; - channel->req_image_size = get_transfer_size(mode); - dprintk(1, "%s: reqsize %ld\n", __func__, channel->req_image_size); - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - /* set the mode */ - buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); - buffer[2] = CMD_SET_MODE; - memcpy(&buffer[3], &channel->mode, sizeof(struct s2255_mode)); - channel->setmode_ready = 0; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (debug) - s2255_print_cfg(dev, mode); - kfree(buffer); - /* wait at least 3 frames before continuing */ - if (mode->restart) { - wait_event_timeout(channel->wait_setmode, - (channel->setmode_ready != 0), - msecs_to_jiffies(S2255_SETMODE_TIMEOUT)); - if (channel->setmode_ready != 1) { - printk(KERN_DEBUG "s2255: no set mode response\n"); - res = -EFAULT; - } - } - /* clear the restart flag */ - channel->mode.restart = 0; - dprintk(1, "%s chn %d, result: %d\n", __func__, channel->idx, res); - return res; -} - -static int s2255_cmd_status(struct s2255_channel *channel, u32 *pstatus) -{ - int res; - __le32 *buffer; - u32 chn_rev; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - dprintk(4, "%s chan %d\n", __func__, channel->idx); - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - /* form the get vid status command */ - buffer[0] = IN_DATA_TOKEN; - buffer[1] = (__le32) cpu_to_le32(chn_rev); - buffer[2] = CMD_STATUS; - *pstatus = 0; - channel->vidstatus_ready = 0; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - kfree(buffer); - wait_event_timeout(channel->wait_vidstatus, - (channel->vidstatus_ready != 0), - msecs_to_jiffies(S2255_VIDSTATUS_TIMEOUT)); - if (channel->vidstatus_ready != 1) { - printk(KERN_DEBUG "s2255: no vidstatus response\n"); - res = -EFAULT; - } - *pstatus = channel->vidstatus; - dprintk(4, "%s, vid status %d\n", __func__, *pstatus); - return res; -} - -static int vidioc_streamon(struct file *file, void *priv, enum v4l2_buf_type i) -{ - int res; - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - int j; - dprintk(4, "%s\n", __func__); - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - dev_err(&dev->udev->dev, "invalid fh type0\n"); - return -EINVAL; - } - if (i != fh->type) { - dev_err(&dev->udev->dev, "invalid fh type1\n"); - return -EINVAL; - } - - if (!res_get(fh)) { - s2255_dev_err(&dev->udev->dev, "stream busy\n"); - return -EBUSY; - } - channel->last_frame = -1; - channel->bad_payload = 0; - channel->cur_frame = 0; - channel->frame_count = 0; - for (j = 0; j < SYS_FRAMES; j++) { - channel->buffer.frame[j].ulState = S2255_READ_IDLE; - channel->buffer.frame[j].cur_size = 0; - } - res = videobuf_streamon(&fh->vb_vidq); - if (res == 0) { - s2255_start_acquire(channel); - channel->b_acquire = 1; - } else - res_free(fh); - - return res; -} - -static int vidioc_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) -{ - struct s2255_fh *fh = priv; - dprintk(4, "%s\n, channel: %d", __func__, fh->channel->idx); - if (fh->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { - printk(KERN_ERR "invalid fh type0\n"); - return -EINVAL; - } - if (i != fh->type) { - printk(KERN_ERR "invalid type i\n"); - return -EINVAL; - } - s2255_stop_acquire(fh->channel); - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - return 0; -} - -static int vidioc_s_std(struct file *file, void *priv, v4l2_std_id *i) -{ - struct s2255_fh *fh = priv; - struct s2255_mode mode; - struct videobuf_queue *q = &fh->vb_vidq; - int ret = 0; - mutex_lock(&q->vb_lock); - if (videobuf_queue_is_busy(q)) { - dprintk(1, "queue busy\n"); - ret = -EBUSY; - goto out_s_std; - } - if (res_locked(fh)) { - dprintk(1, "can't change standard after started\n"); - ret = -EBUSY; - goto out_s_std; - } - mode = fh->channel->mode; - if (*i & V4L2_STD_NTSC) { - dprintk(4, "%s NTSC\n", __func__); - /* if changing format, reset frame decimation/intervals */ - if (mode.format != FORMAT_NTSC) { - mode.restart = 1; - mode.format = FORMAT_NTSC; - mode.fdec = FDEC_1; - } - } else if (*i & V4L2_STD_PAL) { - dprintk(4, "%s PAL\n", __func__); - if (mode.format != FORMAT_PAL) { - mode.restart = 1; - mode.format = FORMAT_PAL; - mode.fdec = FDEC_1; - } - } else { - ret = -EINVAL; - } - if (mode.restart) - s2255_set_mode(fh->channel, &mode); -out_s_std: - mutex_unlock(&q->vb_lock); - return ret; -} - -/* Sensoray 2255 is a multiple channel capture device. - It does not have a "crossbar" of inputs. - We use one V4L device per channel. The user must - be aware that certain combinations are not allowed. - For instance, you cannot do full FPS on more than 2 channels(2 videodevs) - at once in color(you can do full fps on 4 channels with greyscale. -*/ -static int vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *inp) -{ - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - u32 status = 0; - if (inp->index != 0) - return -EINVAL; - inp->type = V4L2_INPUT_TYPE_CAMERA; - inp->std = S2255_NORMS; - inp->status = 0; - if (dev->dsp_fw_ver >= S2255_MIN_DSP_STATUS) { - int rc; - rc = s2255_cmd_status(fh->channel, &status); - dprintk(4, "s2255_cmd_status rc: %d status %x\n", rc, status); - if (rc == 0) - inp->status = (status & 0x01) ? 0 - : V4L2_IN_ST_NO_SIGNAL; - } - switch (dev->pid) { - case 0x2255: - default: - strlcpy(inp->name, "Composite", sizeof(inp->name)); - break; - case 0x2257: - strlcpy(inp->name, (channel->idx < 2) ? "Composite" : "S-Video", - sizeof(inp->name)); - break; - } - return 0; -} - -static int vidioc_g_input(struct file *file, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} -static int vidioc_s_input(struct file *file, void *priv, unsigned int i) -{ - if (i > 0) - return -EINVAL; - return 0; -} - -/* --- controls ---------------------------------------------- */ -static int vidioc_queryctrl(struct file *file, void *priv, - struct v4l2_queryctrl *qc) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = fh->dev; - switch (qc->id) { - case V4L2_CID_BRIGHTNESS: - v4l2_ctrl_query_fill(qc, -127, 127, 1, DEF_BRIGHT); - break; - case V4L2_CID_CONTRAST: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_CONTRAST); - break; - case V4L2_CID_SATURATION: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_SATURATION); - break; - case V4L2_CID_HUE: - v4l2_ctrl_query_fill(qc, 0, 255, 1, DEF_HUE); - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - strlcpy(qc->name, "Color Filter", sizeof(qc->name)); - qc->type = V4L2_CTRL_TYPE_MENU; - qc->minimum = 0; - qc->maximum = 1; - qc->step = 1; - qc->default_value = 1; - qc->flags = 0; - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d\n", __func__, qc->id); - return 0; -} - -static int vidioc_g_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct s2255_fh *fh = priv; - struct s2255_dev *dev = fh->dev; - struct s2255_channel *channel = fh->channel; - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - ctrl->value = channel->mode.bright; - break; - case V4L2_CID_CONTRAST: - ctrl->value = channel->mode.contrast; - break; - case V4L2_CID_SATURATION: - ctrl->value = channel->mode.saturation; - break; - case V4L2_CID_HUE: - ctrl->value = channel->mode.hue; - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - ctrl->value = !((channel->mode.color & MASK_INPUT_TYPE) >> 16); - break; - default: - return -EINVAL; - } - dprintk(4, "%s, id %d val %d\n", __func__, ctrl->id, ctrl->value); - return 0; -} - -static int vidioc_s_ctrl(struct file *file, void *priv, - struct v4l2_control *ctrl) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - struct s2255_mode mode; - mode = channel->mode; - dprintk(4, "%s\n", __func__); - /* update the mode to the corresponding value */ - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - mode.bright = ctrl->value; - break; - case V4L2_CID_CONTRAST: - mode.contrast = ctrl->value; - break; - case V4L2_CID_HUE: - mode.hue = ctrl->value; - break; - case V4L2_CID_SATURATION: - mode.saturation = ctrl->value; - break; - case V4L2_CID_PRIVATE_COLORFILTER: - if (dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - return -EINVAL; - if ((dev->pid == 0x2257) && (channel->idx > 1)) - return -EINVAL; - mode.color &= ~MASK_INPUT_TYPE; - mode.color |= ((ctrl->value ? 0 : 1) << 16); - break; - default: - return -EINVAL; - } - mode.restart = 0; - /* set mode here. Note: stream does not need restarted. - some V4L programs restart stream unnecessarily - after a s_crtl. - */ - s2255_set_mode(fh->channel, &mode); - return 0; -} - -static int vidioc_g_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jc) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - *jc = channel->jc; - dprintk(2, "%s: quality %d\n", __func__, jc->quality); - return 0; -} - -static int vidioc_s_jpegcomp(struct file *file, void *priv, - struct v4l2_jpegcompression *jc) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - if (jc->quality < 0 || jc->quality > 100) - return -EINVAL; - channel->jc.quality = jc->quality; - dprintk(2, "%s: quality %d\n", __func__, jc->quality); - return 0; -} - -static int vidioc_g_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct s2255_fh *fh = priv; - __u32 def_num, def_dem; - struct s2255_channel *channel = fh->channel; - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - memset(sp, 0, sizeof(struct v4l2_streamparm)); - sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME; - sp->parm.capture.capturemode = channel->cap_parm.capturemode; - def_num = (channel->mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (channel->mode.format == FORMAT_NTSC) ? 30000 : 25000; - sp->parm.capture.timeperframe.denominator = def_dem; - switch (channel->mode.fdec) { - default: - case FDEC_1: - sp->parm.capture.timeperframe.numerator = def_num; - break; - case FDEC_2: - sp->parm.capture.timeperframe.numerator = def_num * 2; - break; - case FDEC_3: - sp->parm.capture.timeperframe.numerator = def_num * 3; - break; - case FDEC_5: - sp->parm.capture.timeperframe.numerator = def_num * 5; - break; - } - dprintk(4, "%s capture mode, %d timeperframe %d/%d\n", __func__, - sp->parm.capture.capturemode, - sp->parm.capture.timeperframe.numerator, - sp->parm.capture.timeperframe.denominator); - return 0; -} - -static int vidioc_s_parm(struct file *file, void *priv, - struct v4l2_streamparm *sp) -{ - struct s2255_fh *fh = priv; - struct s2255_channel *channel = fh->channel; - struct s2255_mode mode; - int fdec = FDEC_1; - __u32 def_num, def_dem; - if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - mode = channel->mode; - /* high quality capture mode requires a stream restart */ - if (channel->cap_parm.capturemode - != sp->parm.capture.capturemode && res_locked(fh)) - return -EBUSY; - def_num = (mode.format == FORMAT_NTSC) ? 1001 : 1000; - def_dem = (mode.format == FORMAT_NTSC) ? 30000 : 25000; - if (def_dem != sp->parm.capture.timeperframe.denominator) - sp->parm.capture.timeperframe.numerator = def_num; - else if (sp->parm.capture.timeperframe.numerator <= def_num) - sp->parm.capture.timeperframe.numerator = def_num; - else if (sp->parm.capture.timeperframe.numerator <= (def_num * 2)) { - sp->parm.capture.timeperframe.numerator = def_num * 2; - fdec = FDEC_2; - } else if (sp->parm.capture.timeperframe.numerator <= (def_num * 3)) { - sp->parm.capture.timeperframe.numerator = def_num * 3; - fdec = FDEC_3; - } else { - sp->parm.capture.timeperframe.numerator = def_num * 5; - fdec = FDEC_5; - } - mode.fdec = fdec; - sp->parm.capture.timeperframe.denominator = def_dem; - s2255_set_mode(channel, &mode); - dprintk(4, "%s capture mode, %d timeperframe %d/%d, fdec %d\n", - __func__, - sp->parm.capture.capturemode, - sp->parm.capture.timeperframe.numerator, - sp->parm.capture.timeperframe.denominator, fdec); - return 0; -} - -static int vidioc_enum_frameintervals(struct file *file, void *priv, - struct v4l2_frmivalenum *fe) -{ - int is_ntsc = 0; -#define NUM_FRAME_ENUMS 4 - int frm_dec[NUM_FRAME_ENUMS] = {1, 2, 3, 5}; - if (fe->index < 0 || fe->index >= NUM_FRAME_ENUMS) - return -EINVAL; - switch (fe->width) { - case 640: - if (fe->height != 240 && fe->height != 480) - return -EINVAL; - is_ntsc = 1; - break; - case 320: - if (fe->height != 240) - return -EINVAL; - is_ntsc = 1; - break; - case 704: - if (fe->height != 288 && fe->height != 576) - return -EINVAL; - break; - case 352: - if (fe->height != 288) - return -EINVAL; - break; - default: - return -EINVAL; - } - fe->type = V4L2_FRMIVAL_TYPE_DISCRETE; - fe->discrete.denominator = is_ntsc ? 30000 : 25000; - fe->discrete.numerator = (is_ntsc ? 1001 : 1000) * frm_dec[fe->index]; - dprintk(4, "%s discrete %d/%d\n", __func__, fe->discrete.numerator, - fe->discrete.denominator); - return 0; -} - -static int __s2255_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - struct s2255_channel *channel = video_drvdata(file); - struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); - struct s2255_fh *fh; - enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - int state; - dprintk(1, "s2255: open called (dev=%s)\n", - video_device_node_name(vdev)); - state = atomic_read(&dev->fw_data->fw_state); - switch (state) { - case S2255_FW_DISCONNECTING: - return -ENODEV; - case S2255_FW_FAILED: - s2255_dev_err(&dev->udev->dev, - "firmware load failed. retrying.\n"); - s2255_fwload_start(dev, 1); - wait_event_timeout(dev->fw_data->wait_fw, - ((atomic_read(&dev->fw_data->fw_state) - == S2255_FW_SUCCESS) || - (atomic_read(&dev->fw_data->fw_state) - == S2255_FW_DISCONNECTING)), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - /* state may have changed, re-read */ - state = atomic_read(&dev->fw_data->fw_state); - break; - case S2255_FW_NOTLOADED: - case S2255_FW_LOADED_DSPWAIT: - /* give S2255_LOAD_TIMEOUT time for firmware to load in case - driver loaded and then device immediately opened */ - printk(KERN_INFO "%s waiting for firmware load\n", __func__); - wait_event_timeout(dev->fw_data->wait_fw, - ((atomic_read(&dev->fw_data->fw_state) - == S2255_FW_SUCCESS) || - (atomic_read(&dev->fw_data->fw_state) - == S2255_FW_DISCONNECTING)), - msecs_to_jiffies(S2255_LOAD_TIMEOUT)); - /* state may have changed, re-read */ - state = atomic_read(&dev->fw_data->fw_state); - break; - case S2255_FW_SUCCESS: - default: - break; - } - /* state may have changed in above switch statement */ - switch (state) { - case S2255_FW_SUCCESS: - break; - case S2255_FW_FAILED: - printk(KERN_INFO "2255 firmware load failed.\n"); - return -ENODEV; - case S2255_FW_DISCONNECTING: - printk(KERN_INFO "%s: disconnecting\n", __func__); - return -ENODEV; - case S2255_FW_LOADED_DSPWAIT: - case S2255_FW_NOTLOADED: - printk(KERN_INFO "%s: firmware not loaded yet" - "please try again later\n", - __func__); - /* - * Timeout on firmware load means device unusable. - * Set firmware failure state. - * On next s2255_open the firmware will be reloaded. - */ - atomic_set(&dev->fw_data->fw_state, - S2255_FW_FAILED); - return -EAGAIN; - default: - printk(KERN_INFO "%s: unknown state\n", __func__); - return -EFAULT; - } - /* allocate + initialize per filehandle data */ - fh = kzalloc(sizeof(*fh), GFP_KERNEL); - if (NULL == fh) - return -ENOMEM; - file->private_data = fh; - fh->dev = dev; - fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - fh->channel = channel; - if (!channel->configured) { - /* configure channel to default state */ - channel->fmt = &formats[0]; - s2255_set_mode(channel, &channel->mode); - channel->configured = 1; - } - dprintk(1, "%s: dev=%s type=%s\n", __func__, - video_device_node_name(vdev), v4l2_type_names[type]); - dprintk(2, "%s: fh=0x%08lx, dev=0x%08lx, vidq=0x%08lx\n", __func__, - (unsigned long)fh, (unsigned long)dev, - (unsigned long)&channel->vidq); - dprintk(4, "%s: list_empty active=%d\n", __func__, - list_empty(&channel->vidq.active)); - videobuf_queue_vmalloc_init(&fh->vb_vidq, &s2255_video_qops, - NULL, &dev->slock, - fh->type, - V4L2_FIELD_INTERLACED, - sizeof(struct s2255_buffer), - fh, vdev->lock); - return 0; -} - -static int s2255_open(struct file *file) -{ - struct video_device *vdev = video_devdata(file); - int ret; - - if (mutex_lock_interruptible(vdev->lock)) - return -ERESTARTSYS; - ret = __s2255_open(file); - mutex_unlock(vdev->lock); - return ret; -} - -static unsigned int s2255_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - int rc; - dprintk(100, "%s\n", __func__); - if (V4L2_BUF_TYPE_VIDEO_CAPTURE != fh->type) - return POLLERR; - mutex_lock(&dev->lock); - rc = videobuf_poll_stream(file, &fh->vb_vidq, wait); - mutex_unlock(&dev->lock); - return rc; -} - -static void s2255_destroy(struct s2255_dev *dev) -{ - /* board shutdown stops the read pipe if it is running */ - s2255_board_shutdown(dev); - /* make sure firmware still not trying to load */ - del_timer(&dev->timer); /* only started in .probe and .open */ - if (dev->fw_data->fw_urb) { - usb_kill_urb(dev->fw_data->fw_urb); - usb_free_urb(dev->fw_data->fw_urb); - dev->fw_data->fw_urb = NULL; - } - release_firmware(dev->fw_data->fw); - kfree(dev->fw_data->pfw_data); - kfree(dev->fw_data); - /* reset the DSP so firmware can be reloaded next time */ - s2255_reset_dsppower(dev); - mutex_destroy(&dev->lock); - usb_put_dev(dev->udev); - v4l2_device_unregister(&dev->v4l2_dev); - dprintk(1, "%s", __func__); - kfree(dev); -} - -static int s2255_release(struct file *file) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - struct video_device *vdev = video_devdata(file); - struct s2255_channel *channel = fh->channel; - if (!dev) - return -ENODEV; - mutex_lock(&dev->lock); - /* turn off stream */ - if (res_check(fh)) { - if (channel->b_acquire) - s2255_stop_acquire(fh->channel); - videobuf_streamoff(&fh->vb_vidq); - res_free(fh); - } - videobuf_mmap_free(&fh->vb_vidq); - mutex_unlock(&dev->lock); - dprintk(1, "%s (dev=%s)\n", __func__, video_device_node_name(vdev)); - kfree(fh); - return 0; -} - -static int s2255_mmap_v4l(struct file *file, struct vm_area_struct *vma) -{ - struct s2255_fh *fh = file->private_data; - struct s2255_dev *dev = fh->dev; - int ret; - - if (!fh) - return -ENODEV; - dprintk(4, "%s, vma=0x%08lx\n", __func__, (unsigned long)vma); - if (mutex_lock_interruptible(&dev->lock)) - return -ERESTARTSYS; - ret = videobuf_mmap_mapper(&fh->vb_vidq, vma); - mutex_unlock(&dev->lock); - dprintk(4, "%s vma start=0x%08lx, size=%ld, ret=%d\n", __func__, - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; -} - -static const struct v4l2_file_operations s2255_fops_v4l = { - .owner = THIS_MODULE, - .open = s2255_open, - .release = s2255_release, - .poll = s2255_poll, - .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ - .mmap = s2255_mmap_v4l, -}; - -static const struct v4l2_ioctl_ops s2255_ioctl_ops = { - .vidioc_querymenu = vidioc_querymenu, - .vidioc_querycap = vidioc_querycap, - .vidioc_enum_fmt_vid_cap = vidioc_enum_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = vidioc_g_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = vidioc_s_fmt_vid_cap, - .vidioc_reqbufs = vidioc_reqbufs, - .vidioc_querybuf = vidioc_querybuf, - .vidioc_qbuf = vidioc_qbuf, - .vidioc_dqbuf = vidioc_dqbuf, - .vidioc_s_std = vidioc_s_std, - .vidioc_enum_input = vidioc_enum_input, - .vidioc_g_input = vidioc_g_input, - .vidioc_s_input = vidioc_s_input, - .vidioc_queryctrl = vidioc_queryctrl, - .vidioc_g_ctrl = vidioc_g_ctrl, - .vidioc_s_ctrl = vidioc_s_ctrl, - .vidioc_streamon = vidioc_streamon, - .vidioc_streamoff = vidioc_streamoff, - .vidioc_s_jpegcomp = vidioc_s_jpegcomp, - .vidioc_g_jpegcomp = vidioc_g_jpegcomp, - .vidioc_s_parm = vidioc_s_parm, - .vidioc_g_parm = vidioc_g_parm, - .vidioc_enum_frameintervals = vidioc_enum_frameintervals, -}; - -static void s2255_video_device_release(struct video_device *vdev) -{ - struct s2255_dev *dev = to_s2255_dev(vdev->v4l2_dev); - dprintk(4, "%s, chnls: %d \n", __func__, - atomic_read(&dev->num_channels)); - if (atomic_dec_and_test(&dev->num_channels)) - s2255_destroy(dev); - return; -} - -static struct video_device template = { - .name = "s2255v", - .fops = &s2255_fops_v4l, - .ioctl_ops = &s2255_ioctl_ops, - .release = s2255_video_device_release, - .tvnorms = S2255_NORMS, - .current_norm = V4L2_STD_NTSC_M, -}; - -static int s2255_probe_v4l(struct s2255_dev *dev) -{ - int ret; - int i; - int cur_nr = video_nr; - struct s2255_channel *channel; - ret = v4l2_device_register(&dev->interface->dev, &dev->v4l2_dev); - if (ret) - return ret; - /* initialize all video 4 linux */ - /* register 4 video devices */ - for (i = 0; i < MAX_CHANNELS; i++) { - channel = &dev->channel[i]; - INIT_LIST_HEAD(&channel->vidq.active); - channel->vidq.dev = dev; - /* register 4 video devices */ - channel->vdev = template; - channel->vdev.lock = &dev->lock; - channel->vdev.v4l2_dev = &dev->v4l2_dev; - video_set_drvdata(&channel->vdev, channel); - if (video_nr == -1) - ret = video_register_device(&channel->vdev, - VFL_TYPE_GRABBER, - video_nr); - else - ret = video_register_device(&channel->vdev, - VFL_TYPE_GRABBER, - cur_nr + i); - - if (ret) { - dev_err(&dev->udev->dev, - "failed to register video device!\n"); - break; - } - atomic_inc(&dev->num_channels); - v4l2_info(&dev->v4l2_dev, "V4L2 device registered as %s\n", - video_device_node_name(&channel->vdev)); - - } - printk(KERN_INFO "Sensoray 2255 V4L driver Revision: %s\n", - S2255_VERSION); - /* if no channels registered, return error and probe will fail*/ - if (atomic_read(&dev->num_channels) == 0) { - v4l2_device_unregister(&dev->v4l2_dev); - return ret; - } - if (atomic_read(&dev->num_channels) != MAX_CHANNELS) - printk(KERN_WARNING "s2255: Not all channels available.\n"); - return 0; -} - -/* this function moves the usb stream read pipe data - * into the system buffers. - * returns 0 on success, EAGAIN if more data to process( call this - * function again). - * - * Received frame structure: - * bytes 0-3: marker : 0x2255DA4AL (S2255_MARKER_FRAME) - * bytes 4-7: channel: 0-3 - * bytes 8-11: payload size: size of the frame - * bytes 12-payloadsize+12: frame data - */ -static int save_frame(struct s2255_dev *dev, struct s2255_pipeinfo *pipe_info) -{ - char *pdest; - u32 offset = 0; - int bframe = 0; - char *psrc; - unsigned long copy_size; - unsigned long size; - s32 idx = -1; - struct s2255_framei *frm; - unsigned char *pdata; - struct s2255_channel *channel; - dprintk(100, "buffer to user\n"); - channel = &dev->channel[dev->cc]; - idx = channel->cur_frame; - frm = &channel->buffer.frame[idx]; - if (frm->ulState == S2255_READ_IDLE) { - int jj; - unsigned int cc; - __le32 *pdword; /*data from dsp is little endian */ - int payload; - /* search for marker codes */ - pdata = (unsigned char *)pipe_info->transfer_buffer; - pdword = (__le32 *)pdata; - for (jj = 0; jj < (pipe_info->cur_transfer_size - 12); jj++) { - switch (*pdword) { - case S2255_MARKER_FRAME: - dprintk(4, "found frame marker at offset:" - " %d [%x %x]\n", jj, pdata[0], - pdata[1]); - offset = jj + PREFIX_SIZE; - bframe = 1; - cc = le32_to_cpu(pdword[1]); - if (cc >= MAX_CHANNELS) { - printk(KERN_ERR - "bad channel\n"); - return -EINVAL; - } - /* reverse it */ - dev->cc = G_chnmap[cc]; - channel = &dev->channel[dev->cc]; - payload = le32_to_cpu(pdword[3]); - if (payload > channel->req_image_size) { - channel->bad_payload++; - /* discard the bad frame */ - return -EINVAL; - } - channel->pkt_size = payload; - channel->jpg_size = le32_to_cpu(pdword[4]); - break; - case S2255_MARKER_RESPONSE: - - pdata += DEF_USB_BLOCK; - jj += DEF_USB_BLOCK; - if (le32_to_cpu(pdword[1]) >= MAX_CHANNELS) - break; - cc = G_chnmap[le32_to_cpu(pdword[1])]; - if (cc >= MAX_CHANNELS) - break; - channel = &dev->channel[cc]; - switch (pdword[2]) { - case S2255_RESPONSE_SETMODE: - /* check if channel valid */ - /* set mode ready */ - channel->setmode_ready = 1; - wake_up(&channel->wait_setmode); - dprintk(5, "setmode ready %d\n", cc); - break; - case S2255_RESPONSE_FW: - dev->chn_ready |= (1 << cc); - if ((dev->chn_ready & 0x0f) != 0x0f) - break; - /* all channels ready */ - printk(KERN_INFO "s2255: fw loaded\n"); - atomic_set(&dev->fw_data->fw_state, - S2255_FW_SUCCESS); - wake_up(&dev->fw_data->wait_fw); - break; - case S2255_RESPONSE_STATUS: - channel->vidstatus = le32_to_cpu(pdword[3]); - channel->vidstatus_ready = 1; - wake_up(&channel->wait_vidstatus); - dprintk(5, "got vidstatus %x chan %d\n", - le32_to_cpu(pdword[3]), cc); - break; - default: - printk(KERN_INFO "s2255 unknown resp\n"); - } - default: - pdata++; - break; - } - if (bframe) - break; - } /* for */ - if (!bframe) - return -EINVAL; - } - channel = &dev->channel[dev->cc]; - idx = channel->cur_frame; - frm = &channel->buffer.frame[idx]; - /* search done. now find out if should be acquiring on this channel */ - if (!channel->b_acquire) { - /* we found a frame, but this channel is turned off */ - frm->ulState = S2255_READ_IDLE; - return -EINVAL; - } - - if (frm->ulState == S2255_READ_IDLE) { - frm->ulState = S2255_READ_FRAME; - frm->cur_size = 0; - } - - /* skip the marker 512 bytes (and offset if out of sync) */ - psrc = (u8 *)pipe_info->transfer_buffer + offset; - - - if (frm->lpvbits == NULL) { - dprintk(1, "s2255 frame buffer == NULL.%p %p %d %d", - frm, dev, dev->cc, idx); - return -ENOMEM; - } - - pdest = frm->lpvbits + frm->cur_size; - - copy_size = (pipe_info->cur_transfer_size - offset); - - size = channel->pkt_size - PREFIX_SIZE; - - /* sanity check on pdest */ - if ((copy_size + frm->cur_size) < channel->req_image_size) - memcpy(pdest, psrc, copy_size); - - frm->cur_size += copy_size; - dprintk(4, "cur_size size %lu size %lu \n", frm->cur_size, size); - - if (frm->cur_size >= size) { - dprintk(2, "****************[%d]Buffer[%d]full*************\n", - dev->cc, idx); - channel->last_frame = channel->cur_frame; - channel->cur_frame++; - /* end of system frame ring buffer, start at zero */ - if ((channel->cur_frame == SYS_FRAMES) || - (channel->cur_frame == channel->buffer.dwFrames)) - channel->cur_frame = 0; - /* frame ready */ - if (channel->b_acquire) - s2255_got_frame(channel, channel->jpg_size); - channel->frame_count++; - frm->ulState = S2255_READ_IDLE; - frm->cur_size = 0; - - } - /* done successfully */ - return 0; -} - -static void s2255_read_video_callback(struct s2255_dev *dev, - struct s2255_pipeinfo *pipe_info) -{ - int res; - dprintk(50, "callback read video \n"); - - if (dev->cc >= MAX_CHANNELS) { - dev->cc = 0; - dev_err(&dev->udev->dev, "invalid channel\n"); - return; - } - /* otherwise copy to the system buffers */ - res = save_frame(dev, pipe_info); - if (res != 0) - dprintk(4, "s2255: read callback failed\n"); - - dprintk(50, "callback read video done\n"); - return; -} - -static long s2255_vendor_req(struct s2255_dev *dev, unsigned char Request, - u16 Index, u16 Value, void *TransferBuffer, - s32 TransferBufferLength, int bOut) -{ - int r; - if (!bOut) { - r = usb_control_msg(dev->udev, usb_rcvctrlpipe(dev->udev, 0), - Request, - USB_TYPE_VENDOR | USB_RECIP_DEVICE | - USB_DIR_IN, - Value, Index, TransferBuffer, - TransferBufferLength, HZ * 5); - } else { - r = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0), - Request, USB_TYPE_VENDOR | USB_RECIP_DEVICE, - Value, Index, TransferBuffer, - TransferBufferLength, HZ * 5); - } - return r; -} - -/* - * retrieve FX2 firmware version. future use. - * @param dev pointer to device extension - * @return -1 for fail, else returns firmware version as an int(16 bits) - */ -static int s2255_get_fx2fw(struct s2255_dev *dev) -{ - int fw; - int ret; - unsigned char transBuffer[64]; - ret = s2255_vendor_req(dev, S2255_VR_FW, 0, 0, transBuffer, 2, - S2255_VR_IN); - if (ret < 0) - dprintk(2, "get fw error: %x\n", ret); - fw = transBuffer[0] + (transBuffer[1] << 8); - dprintk(2, "Get FW %x %x\n", transBuffer[0], transBuffer[1]); - return fw; -} - -/* - * Create the system ring buffer to copy frames into from the - * usb read pipe. - */ -static int s2255_create_sys_buffers(struct s2255_channel *channel) -{ - unsigned long i; - unsigned long reqsize; - dprintk(1, "create sys buffers\n"); - channel->buffer.dwFrames = SYS_FRAMES; - /* always allocate maximum size(PAL) for system buffers */ - reqsize = SYS_FRAMES_MAXSIZE; - - if (reqsize > SYS_FRAMES_MAXSIZE) - reqsize = SYS_FRAMES_MAXSIZE; - - for (i = 0; i < SYS_FRAMES; i++) { - /* allocate the frames */ - channel->buffer.frame[i].lpvbits = vmalloc(reqsize); - dprintk(1, "valloc %p chan %d, idx %lu, pdata %p\n", - &channel->buffer.frame[i], channel->idx, i, - channel->buffer.frame[i].lpvbits); - channel->buffer.frame[i].size = reqsize; - if (channel->buffer.frame[i].lpvbits == NULL) { - printk(KERN_INFO "out of memory. using less frames\n"); - channel->buffer.dwFrames = i; - break; - } - } - - /* make sure internal states are set */ - for (i = 0; i < SYS_FRAMES; i++) { - channel->buffer.frame[i].ulState = 0; - channel->buffer.frame[i].cur_size = 0; - } - - channel->cur_frame = 0; - channel->last_frame = -1; - return 0; -} - -static int s2255_release_sys_buffers(struct s2255_channel *channel) -{ - unsigned long i; - dprintk(1, "release sys buffers\n"); - for (i = 0; i < SYS_FRAMES; i++) { - if (channel->buffer.frame[i].lpvbits) { - dprintk(1, "vfree %p\n", - channel->buffer.frame[i].lpvbits); - vfree(channel->buffer.frame[i].lpvbits); - } - channel->buffer.frame[i].lpvbits = NULL; - } - return 0; -} - -static int s2255_board_init(struct s2255_dev *dev) -{ - struct s2255_mode mode_def = DEF_MODEI_NTSC_CONT; - int fw_ver; - int j; - struct s2255_pipeinfo *pipe = &dev->pipe; - dprintk(4, "board init: %p", dev); - memset(pipe, 0, sizeof(*pipe)); - pipe->dev = dev; - pipe->cur_transfer_size = S2255_USB_XFER_SIZE; - pipe->max_transfer_size = S2255_USB_XFER_SIZE; - - pipe->transfer_buffer = kzalloc(pipe->max_transfer_size, - GFP_KERNEL); - if (pipe->transfer_buffer == NULL) { - dprintk(1, "out of memory!\n"); - return -ENOMEM; - } - /* query the firmware */ - fw_ver = s2255_get_fx2fw(dev); - - printk(KERN_INFO "s2255: usb firmware version %d.%d\n", - (fw_ver >> 8) & 0xff, - fw_ver & 0xff); - - if (fw_ver < S2255_CUR_USB_FWVER) - printk(KERN_INFO "s2255: newer USB firmware available\n"); - - for (j = 0; j < MAX_CHANNELS; j++) { - struct s2255_channel *channel = &dev->channel[j]; - channel->b_acquire = 0; - channel->mode = mode_def; - if (dev->pid == 0x2257 && j > 1) - channel->mode.color |= (1 << 16); - channel->jc.quality = S2255_DEF_JPEG_QUAL; - channel->width = LINE_SZ_4CIFS_NTSC; - channel->height = NUM_LINES_4CIFS_NTSC * 2; - channel->fmt = &formats[0]; - channel->mode.restart = 1; - channel->req_image_size = get_transfer_size(&mode_def); - channel->frame_count = 0; - /* create the system buffers */ - s2255_create_sys_buffers(channel); - } - /* start read pipe */ - s2255_start_readpipe(dev); - dprintk(1, "%s: success\n", __func__); - return 0; -} - -static int s2255_board_shutdown(struct s2255_dev *dev) -{ - u32 i; - dprintk(1, "%s: dev: %p", __func__, dev); - - for (i = 0; i < MAX_CHANNELS; i++) { - if (dev->channel[i].b_acquire) - s2255_stop_acquire(&dev->channel[i]); - } - s2255_stop_readpipe(dev); - for (i = 0; i < MAX_CHANNELS; i++) - s2255_release_sys_buffers(&dev->channel[i]); - /* release transfer buffer */ - kfree(dev->pipe.transfer_buffer); - return 0; -} - -static void read_pipe_completion(struct urb *purb) -{ - struct s2255_pipeinfo *pipe_info; - struct s2255_dev *dev; - int status; - int pipe; - pipe_info = purb->context; - dprintk(100, "%s: urb:%p, status %d\n", __func__, purb, - purb->status); - if (pipe_info == NULL) { - dev_err(&purb->dev->dev, "no context!\n"); - return; - } - - dev = pipe_info->dev; - if (dev == NULL) { - dev_err(&purb->dev->dev, "no context!\n"); - return; - } - status = purb->status; - /* if shutting down, do not resubmit, exit immediately */ - if (status == -ESHUTDOWN) { - dprintk(2, "%s: err shutdown\n", __func__); - pipe_info->err_count++; - return; - } - - if (pipe_info->state == 0) { - dprintk(2, "%s: exiting USB pipe", __func__); - return; - } - - if (status == 0) - s2255_read_video_callback(dev, pipe_info); - else { - pipe_info->err_count++; - dprintk(1, "%s: failed URB %d\n", __func__, status); - } - - pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); - /* reuse urb */ - usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->cur_transfer_size, - read_pipe_completion, pipe_info); - - if (pipe_info->state != 0) { - if (usb_submit_urb(pipe_info->stream_urb, GFP_ATOMIC)) { - dev_err(&dev->udev->dev, "error submitting urb\n"); - } - } else { - dprintk(2, "%s :complete state 0\n", __func__); - } - return; -} - -static int s2255_start_readpipe(struct s2255_dev *dev) -{ - int pipe; - int retval; - struct s2255_pipeinfo *pipe_info = &dev->pipe; - pipe = usb_rcvbulkpipe(dev->udev, dev->read_endpoint); - dprintk(2, "%s: IN %d\n", __func__, dev->read_endpoint); - pipe_info->state = 1; - pipe_info->err_count = 0; - pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pipe_info->stream_urb) { - dev_err(&dev->udev->dev, - "ReadStream: Unable to alloc URB\n"); - return -ENOMEM; - } - /* transfer buffer allocated in board_init */ - usb_fill_bulk_urb(pipe_info->stream_urb, dev->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->cur_transfer_size, - read_pipe_completion, pipe_info); - retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); - if (retval) { - printk(KERN_ERR "s2255: start read pipe failed\n"); - return retval; - } - return 0; -} - -/* starts acquisition process */ -static int s2255_start_acquire(struct s2255_channel *channel) -{ - unsigned char *buffer; - int res; - unsigned long chn_rev; - int j; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - - channel->last_frame = -1; - channel->bad_payload = 0; - channel->cur_frame = 0; - for (j = 0; j < SYS_FRAMES; j++) { - channel->buffer.frame[j].ulState = 0; - channel->buffer.frame[j].cur_size = 0; - } - - /* send the start command */ - *(__le32 *) buffer = IN_DATA_TOKEN; - *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); - *((__le32 *) buffer + 2) = CMD_START; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (res != 0) - dev_err(&dev->udev->dev, "CMD_START error\n"); - - dprintk(2, "start acquire exit[%d] %d \n", channel->idx, res); - kfree(buffer); - return 0; -} - -static int s2255_stop_acquire(struct s2255_channel *channel) -{ - unsigned char *buffer; - int res; - unsigned long chn_rev; - struct s2255_dev *dev = to_s2255_dev(channel->vdev.v4l2_dev); - chn_rev = G_chnmap[channel->idx]; - buffer = kzalloc(512, GFP_KERNEL); - if (buffer == NULL) { - dev_err(&dev->udev->dev, "out of mem\n"); - return -ENOMEM; - } - /* send the stop command */ - *(__le32 *) buffer = IN_DATA_TOKEN; - *((__le32 *) buffer + 1) = (__le32) cpu_to_le32(chn_rev); - *((__le32 *) buffer + 2) = CMD_STOP; - res = s2255_write_config(dev->udev, (unsigned char *)buffer, 512); - if (res != 0) - dev_err(&dev->udev->dev, "CMD_STOP error\n"); - kfree(buffer); - channel->b_acquire = 0; - dprintk(4, "%s: chn %d, res %d\n", __func__, channel->idx, res); - return res; -} - -static void s2255_stop_readpipe(struct s2255_dev *dev) -{ - struct s2255_pipeinfo *pipe = &dev->pipe; - - pipe->state = 0; - if (pipe->stream_urb) { - /* cancel urb */ - usb_kill_urb(pipe->stream_urb); - usb_free_urb(pipe->stream_urb); - pipe->stream_urb = NULL; - } - dprintk(4, "%s", __func__); - return; -} - -static void s2255_fwload_start(struct s2255_dev *dev, int reset) -{ - if (reset) - s2255_reset_dsppower(dev); - dev->fw_data->fw_size = dev->fw_data->fw->size; - atomic_set(&dev->fw_data->fw_state, S2255_FW_NOTLOADED); - memcpy(dev->fw_data->pfw_data, - dev->fw_data->fw->data, CHUNK_SIZE); - dev->fw_data->fw_loaded = CHUNK_SIZE; - usb_fill_bulk_urb(dev->fw_data->fw_urb, dev->udev, - usb_sndbulkpipe(dev->udev, 2), - dev->fw_data->pfw_data, - CHUNK_SIZE, s2255_fwchunk_complete, - dev->fw_data); - mod_timer(&dev->timer, jiffies + HZ); -} - -/* standard usb probe function */ -static int s2255_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - struct s2255_dev *dev = NULL; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - int i; - int retval = -ENOMEM; - __le32 *pdata; - int fw_size; - dprintk(2, "%s\n", __func__); - /* allocate memory for our device state and initialize it to zero */ - dev = kzalloc(sizeof(struct s2255_dev), GFP_KERNEL); - if (dev == NULL) { - s2255_dev_err(&interface->dev, "out of memory\n"); - return -ENOMEM; - } - atomic_set(&dev->num_channels, 0); - dev->pid = id->idProduct; - dev->fw_data = kzalloc(sizeof(struct s2255_fw), GFP_KERNEL); - if (!dev->fw_data) - goto errorFWDATA1; - mutex_init(&dev->lock); - /* grab usb_device and save it */ - dev->udev = usb_get_dev(interface_to_usbdev(interface)); - if (dev->udev == NULL) { - dev_err(&interface->dev, "null usb device\n"); - retval = -ENODEV; - goto errorUDEV; - } - dprintk(1, "dev: %p, udev %p interface %p\n", dev, - dev->udev, interface); - dev->interface = interface; - /* set up the endpoint information */ - iface_desc = interface->cur_altsetting; - dprintk(1, "num endpoints %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!dev->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - dev->read_endpoint = endpoint->bEndpointAddress; - } - } - - if (!dev->read_endpoint) { - dev_err(&interface->dev, "Could not find bulk-in endpoint\n"); - goto errorEP; - } - init_timer(&dev->timer); - dev->timer.function = s2255_timer; - dev->timer.data = (unsigned long)dev->fw_data; - init_waitqueue_head(&dev->fw_data->wait_fw); - for (i = 0; i < MAX_CHANNELS; i++) { - struct s2255_channel *channel = &dev->channel[i]; - dev->channel[i].idx = i; - init_waitqueue_head(&channel->wait_setmode); - init_waitqueue_head(&channel->wait_vidstatus); - } - - dev->fw_data->fw_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!dev->fw_data->fw_urb) { - dev_err(&interface->dev, "out of memory!\n"); - goto errorFWURB; - } - - dev->fw_data->pfw_data = kzalloc(CHUNK_SIZE, GFP_KERNEL); - if (!dev->fw_data->pfw_data) { - dev_err(&interface->dev, "out of memory!\n"); - goto errorFWDATA2; - } - /* load the first chunk */ - if (request_firmware(&dev->fw_data->fw, - FIRMWARE_FILE_NAME, &dev->udev->dev)) { - printk(KERN_ERR "sensoray 2255 failed to get firmware\n"); - goto errorREQFW; - } - /* check the firmware is valid */ - fw_size = dev->fw_data->fw->size; - pdata = (__le32 *) &dev->fw_data->fw->data[fw_size - 8]; - - if (*pdata != S2255_FW_MARKER) { - printk(KERN_INFO "Firmware invalid.\n"); - retval = -ENODEV; - goto errorFWMARKER; - } else { - /* make sure firmware is the latest */ - __le32 *pRel; - pRel = (__le32 *) &dev->fw_data->fw->data[fw_size - 4]; - printk(KERN_INFO "s2255 dsp fw version %x\n", *pRel); - dev->dsp_fw_ver = le32_to_cpu(*pRel); - if (dev->dsp_fw_ver < S2255_CUR_DSP_FWVER) - printk(KERN_INFO "s2255: f2255usb.bin out of date.\n"); - if (dev->pid == 0x2257 && - dev->dsp_fw_ver < S2255_MIN_DSP_COLORFILTER) - printk(KERN_WARNING "s2255: 2257 requires firmware %d" - " or above.\n", S2255_MIN_DSP_COLORFILTER); - } - usb_reset_device(dev->udev); - /* load 2255 board specific */ - retval = s2255_board_init(dev); - if (retval) - goto errorBOARDINIT; - spin_lock_init(&dev->slock); - s2255_fwload_start(dev, 0); - /* loads v4l specific */ - retval = s2255_probe_v4l(dev); - if (retval) - goto errorBOARDINIT; - dev_info(&interface->dev, "Sensoray 2255 detected\n"); - return 0; -errorBOARDINIT: - s2255_board_shutdown(dev); -errorFWMARKER: - release_firmware(dev->fw_data->fw); -errorREQFW: - kfree(dev->fw_data->pfw_data); -errorFWDATA2: - usb_free_urb(dev->fw_data->fw_urb); -errorFWURB: - del_timer(&dev->timer); -errorEP: - usb_put_dev(dev->udev); -errorUDEV: - kfree(dev->fw_data); - mutex_destroy(&dev->lock); -errorFWDATA1: - kfree(dev); - printk(KERN_WARNING "Sensoray 2255 driver load failed: 0x%x\n", retval); - return retval; -} - -/* disconnect routine. when board is removed physically or with rmmod */ -static void s2255_disconnect(struct usb_interface *interface) -{ - struct s2255_dev *dev = to_s2255_dev(usb_get_intfdata(interface)); - int i; - int channels = atomic_read(&dev->num_channels); - mutex_lock(&dev->lock); - v4l2_device_disconnect(&dev->v4l2_dev); - mutex_unlock(&dev->lock); - /*see comments in the uvc_driver.c usb disconnect function */ - atomic_inc(&dev->num_channels); - /* unregister each video device. */ - for (i = 0; i < channels; i++) - video_unregister_device(&dev->channel[i].vdev); - /* wake up any of our timers */ - atomic_set(&dev->fw_data->fw_state, S2255_FW_DISCONNECTING); - wake_up(&dev->fw_data->wait_fw); - for (i = 0; i < MAX_CHANNELS; i++) { - dev->channel[i].setmode_ready = 1; - wake_up(&dev->channel[i].wait_setmode); - dev->channel[i].vidstatus_ready = 1; - wake_up(&dev->channel[i].wait_vidstatus); - } - if (atomic_dec_and_test(&dev->num_channels)) - s2255_destroy(dev); - dev_info(&interface->dev, "%s\n", __func__); -} - -static struct usb_driver s2255_driver = { - .name = S2255_DRIVER_NAME, - .probe = s2255_probe, - .disconnect = s2255_disconnect, - .id_table = s2255_table, -}; - -module_usb_driver(s2255_driver); - -MODULE_DESCRIPTION("Sensoray 2255 Video for Linux driver"); -MODULE_AUTHOR("Dean Anderson (Sensoray Company Inc.)"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(S2255_VERSION); -MODULE_FIRMWARE(FIRMWARE_FILE_NAME); diff --git a/drivers/media/video/stk-sensor.c b/drivers/media/video/stk-sensor.c deleted file mode 100644 index e546b014d7ad..000000000000 --- a/drivers/media/video/stk-sensor.c +++ /dev/null @@ -1,595 +0,0 @@ -/* stk-sensor.c: Driver for ov96xx sensor (used in some Syntek webcams) - * - * Copyright 2007-2008 Jaime Velasco Juan - * - * Some parts derived from ov7670.c: - * Copyright 2006 One Laptop Per Child Association, Inc. Written - * by Jonathan Corbet with substantial inspiration from Mark - * McClelland's ovcamchip code. - * - * Copyright 2006-7 Jonathan Corbet - * - * This file may be distributed under the terms of the GNU General - * 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. - */ - -/* Controlling the sensor via the STK1125 vendor specific control interface: - * The camera uses an OmniVision sensor and the stk1125 provides an - * SCCB(i2c)-USB bridge which let us program the sensor. - * In my case the sensor id is 0x9652, it can be read from sensor's register - * 0x0A and 0x0B as follows: - * - read register #R: - * output #R to index 0x0208 - * output 0x0070 to index 0x0200 - * input 1 byte from index 0x0201 (some kind of status register) - * until its value is 0x01 - * input 1 byte from index 0x0209. This is the value of #R - * - write value V to register #R - * output #R to index 0x0204 - * output V to index 0x0205 - * output 0x0005 to index 0x0200 - * input 1 byte from index 0x0201 until its value becomes 0x04 - */ - -/* It seems the i2c bus is controlled with these registers */ - -#include "stk-webcam.h" - -#define STK_IIC_BASE (0x0200) -# define STK_IIC_OP (STK_IIC_BASE) -# define STK_IIC_OP_TX (0x05) -# define STK_IIC_OP_RX (0x70) -# define STK_IIC_STAT (STK_IIC_BASE+1) -# define STK_IIC_STAT_TX_OK (0x04) -# define STK_IIC_STAT_RX_OK (0x01) -/* I don't know what does this register. - * when it is 0x00 or 0x01, we cannot talk to the sensor, - * other values work */ -# define STK_IIC_ENABLE (STK_IIC_BASE+2) -# define STK_IIC_ENABLE_NO (0x00) -/* This is what the driver writes in windows */ -# define STK_IIC_ENABLE_YES (0x1e) -/* - * Address of the slave. Seems like the binary driver look for the - * sensor in multiple places, attempting a reset sequence. - * We only know about the ov9650 - */ -# define STK_IIC_ADDR (STK_IIC_BASE+3) -# define STK_IIC_TX_INDEX (STK_IIC_BASE+4) -# define STK_IIC_TX_VALUE (STK_IIC_BASE+5) -# define STK_IIC_RX_INDEX (STK_IIC_BASE+8) -# define STK_IIC_RX_VALUE (STK_IIC_BASE+9) - -#define MAX_RETRIES (50) - -#define SENSOR_ADDRESS (0x60) - -/* From ov7670.c (These registers aren't fully accurate) */ - -/* Registers */ -#define REG_GAIN 0x00 /* Gain lower 8 bits (rest in vref) */ -#define REG_BLUE 0x01 /* blue gain */ -#define REG_RED 0x02 /* red gain */ -#define REG_VREF 0x03 /* Pieces of GAIN, VSTART, VSTOP */ -#define REG_COM1 0x04 /* Control 1 */ -#define COM1_CCIR656 0x40 /* CCIR656 enable */ -#define COM1_QFMT 0x20 /* QVGA/QCIF format */ -#define COM1_SKIP_0 0x00 /* Do not skip any row */ -#define COM1_SKIP_2 0x04 /* Skip 2 rows of 4 */ -#define COM1_SKIP_3 0x08 /* Skip 3 rows of 4 */ -#define REG_BAVE 0x05 /* U/B Average level */ -#define REG_GbAVE 0x06 /* Y/Gb Average level */ -#define REG_AECHH 0x07 /* AEC MS 5 bits */ -#define REG_RAVE 0x08 /* V/R Average level */ -#define REG_COM2 0x09 /* Control 2 */ -#define COM2_SSLEEP 0x10 /* Soft sleep mode */ -#define REG_PID 0x0a /* Product ID MSB */ -#define REG_VER 0x0b /* Product ID LSB */ -#define REG_COM3 0x0c /* Control 3 */ -#define COM3_SWAP 0x40 /* Byte swap */ -#define COM3_SCALEEN 0x08 /* Enable scaling */ -#define COM3_DCWEN 0x04 /* Enable downsamp/crop/window */ -#define REG_COM4 0x0d /* Control 4 */ -#define REG_COM5 0x0e /* All "reserved" */ -#define REG_COM6 0x0f /* Control 6 */ -#define REG_AECH 0x10 /* More bits of AEC value */ -#define REG_CLKRC 0x11 /* Clock control */ -#define CLK_PLL 0x80 /* Enable internal PLL */ -#define CLK_EXT 0x40 /* Use external clock directly */ -#define CLK_SCALE 0x3f /* Mask for internal clock scale */ -#define REG_COM7 0x12 /* Control 7 */ -#define COM7_RESET 0x80 /* Register reset */ -#define COM7_FMT_MASK 0x38 -#define COM7_FMT_SXGA 0x00 -#define COM7_FMT_VGA 0x40 -#define COM7_FMT_CIF 0x20 /* CIF format */ -#define COM7_FMT_QVGA 0x10 /* QVGA format */ -#define COM7_FMT_QCIF 0x08 /* QCIF format */ -#define COM7_RGB 0x04 /* bits 0 and 2 - RGB format */ -#define COM7_YUV 0x00 /* YUV */ -#define COM7_BAYER 0x01 /* Bayer format */ -#define COM7_PBAYER 0x05 /* "Processed bayer" */ -#define REG_COM8 0x13 /* Control 8 */ -#define COM8_FASTAEC 0x80 /* Enable fast AGC/AEC */ -#define COM8_AECSTEP 0x40 /* Unlimited AEC step size */ -#define COM8_BFILT 0x20 /* Band filter enable */ -#define COM8_AGC 0x04 /* Auto gain enable */ -#define COM8_AWB 0x02 /* White balance enable */ -#define COM8_AEC 0x01 /* Auto exposure enable */ -#define REG_COM9 0x14 /* Control 9 - gain ceiling */ -#define REG_COM10 0x15 /* Control 10 */ -#define COM10_HSYNC 0x40 /* HSYNC instead of HREF */ -#define COM10_PCLK_HB 0x20 /* Suppress PCLK on horiz blank */ -#define COM10_HREF_REV 0x08 /* Reverse HREF */ -#define COM10_VS_LEAD 0x04 /* VSYNC on clock leading edge */ -#define COM10_VS_NEG 0x02 /* VSYNC negative */ -#define COM10_HS_NEG 0x01 /* HSYNC negative */ -#define REG_HSTART 0x17 /* Horiz start high bits */ -#define REG_HSTOP 0x18 /* Horiz stop high bits */ -#define REG_VSTART 0x19 /* Vert start high bits */ -#define REG_VSTOP 0x1a /* Vert stop high bits */ -#define REG_PSHFT 0x1b /* Pixel delay after HREF */ -#define REG_MIDH 0x1c /* Manuf. ID high */ -#define REG_MIDL 0x1d /* Manuf. ID low */ -#define REG_MVFP 0x1e /* Mirror / vflip */ -#define MVFP_MIRROR 0x20 /* Mirror image */ -#define MVFP_FLIP 0x10 /* Vertical flip */ - -#define REG_AEW 0x24 /* AGC upper limit */ -#define REG_AEB 0x25 /* AGC lower limit */ -#define REG_VPT 0x26 /* AGC/AEC fast mode op region */ -#define REG_ADVFL 0x2d /* Insert dummy lines (LSB) */ -#define REG_ADVFH 0x2e /* Insert dummy lines (MSB) */ -#define REG_HSYST 0x30 /* HSYNC rising edge delay */ -#define REG_HSYEN 0x31 /* HSYNC falling edge delay */ -#define REG_HREF 0x32 /* HREF pieces */ -#define REG_TSLB 0x3a /* lots of stuff */ -#define TSLB_YLAST 0x04 /* UYVY or VYUY - see com13 */ -#define TSLB_BYTEORD 0x08 /* swap bytes in 16bit mode? */ -#define REG_COM11 0x3b /* Control 11 */ -#define COM11_NIGHT 0x80 /* NIght mode enable */ -#define COM11_NMFR 0x60 /* Two bit NM frame rate */ -#define COM11_HZAUTO 0x10 /* Auto detect 50/60 Hz */ -#define COM11_50HZ 0x08 /* Manual 50Hz select */ -#define COM11_EXP 0x02 -#define REG_COM12 0x3c /* Control 12 */ -#define COM12_HREF 0x80 /* HREF always */ -#define REG_COM13 0x3d /* Control 13 */ -#define COM13_GAMMA 0x80 /* Gamma enable */ -#define COM13_UVSAT 0x40 /* UV saturation auto adjustment */ -#define COM13_CMATRIX 0x10 /* Enable color matrix for RGB or YUV */ -#define COM13_UVSWAP 0x01 /* V before U - w/TSLB */ -#define REG_COM14 0x3e /* Control 14 */ -#define COM14_DCWEN 0x10 /* DCW/PCLK-scale enable */ -#define REG_EDGE 0x3f /* Edge enhancement factor */ -#define REG_COM15 0x40 /* Control 15 */ -#define COM15_R10F0 0x00 /* Data range 10 to F0 */ -#define COM15_R01FE 0x80 /* 01 to FE */ -#define COM15_R00FF 0xc0 /* 00 to FF */ -#define COM15_RGB565 0x10 /* RGB565 output */ -#define COM15_RGBFIXME 0x20 /* FIXME */ -#define COM15_RGB555 0x30 /* RGB555 output */ -#define REG_COM16 0x41 /* Control 16 */ -#define COM16_AWBGAIN 0x08 /* AWB gain enable */ -#define REG_COM17 0x42 /* Control 17 */ -#define COM17_AECWIN 0xc0 /* AEC window - must match COM4 */ -#define COM17_CBAR 0x08 /* DSP Color bar */ - -/* - * This matrix defines how the colors are generated, must be - * tweaked to adjust hue and saturation. - * - * Order: v-red, v-green, v-blue, u-red, u-green, u-blue - * - * They are nine-bit signed quantities, with the sign bit - * stored in 0x58. Sign for v-red is bit 0, and up from there. - */ -#define REG_CMATRIX_BASE 0x4f -#define CMATRIX_LEN 6 -#define REG_CMATRIX_SIGN 0x58 - - -#define REG_BRIGHT 0x55 /* Brightness */ -#define REG_CONTRAS 0x56 /* Contrast control */ - -#define REG_GFIX 0x69 /* Fix gain control */ - -#define REG_RGB444 0x8c /* RGB 444 control */ -#define R444_ENABLE 0x02 /* Turn on RGB444, overrides 5x5 */ -#define R444_RGBX 0x01 /* Empty nibble at end */ - -#define REG_HAECC1 0x9f /* Hist AEC/AGC control 1 */ -#define REG_HAECC2 0xa0 /* Hist AEC/AGC control 2 */ - -#define REG_BD50MAX 0xa5 /* 50hz banding step limit */ -#define REG_HAECC3 0xa6 /* Hist AEC/AGC control 3 */ -#define REG_HAECC4 0xa7 /* Hist AEC/AGC control 4 */ -#define REG_HAECC5 0xa8 /* Hist AEC/AGC control 5 */ -#define REG_HAECC6 0xa9 /* Hist AEC/AGC control 6 */ -#define REG_HAECC7 0xaa /* Hist AEC/AGC control 7 */ -#define REG_BD60MAX 0xab /* 60hz banding step limit */ - - - - -/* Returns 0 if OK */ -static int stk_sensor_outb(struct stk_camera *dev, u8 reg, u8 val) -{ - int i = 0; - int tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_TX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_TX_VALUE, val)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_TX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_TX_OK) { - if (tmpval) - STK_ERROR("stk_sensor_outb failed, status=0x%02x\n", - tmpval); - return 1; - } else - return 0; -} - -static int stk_sensor_inb(struct stk_camera *dev, u8 reg, u8 *val) -{ - int i = 0; - int tmpval = 0; - - if (stk_camera_write_reg(dev, STK_IIC_RX_INDEX, reg)) - return 1; - if (stk_camera_write_reg(dev, STK_IIC_OP, STK_IIC_OP_RX)) - return 1; - do { - if (stk_camera_read_reg(dev, STK_IIC_STAT, &tmpval)) - return 1; - i++; - } while (tmpval == 0 && i < MAX_RETRIES); - if (tmpval != STK_IIC_STAT_RX_OK) { - if (tmpval) - STK_ERROR("stk_sensor_inb failed, status=0x%02x\n", - tmpval); - return 1; - } - - if (stk_camera_read_reg(dev, STK_IIC_RX_VALUE, &tmpval)) - return 1; - - *val = (u8) tmpval; - return 0; -} - -static int stk_sensor_write_regvals(struct stk_camera *dev, - struct regval *rv) -{ - int ret; - if (rv == NULL) - return 0; - while (rv->reg != 0xff || rv->val != 0xff) { - ret = stk_sensor_outb(dev, rv->reg, rv->val); - if (ret != 0) - return ret; - rv++; - } - return 0; -} - -int stk_sensor_sleep(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp|COM2_SSLEEP); -} - -int stk_sensor_wakeup(struct stk_camera *dev) -{ - u8 tmp; - return stk_sensor_inb(dev, REG_COM2, &tmp) - || stk_sensor_outb(dev, REG_COM2, tmp&~COM2_SSLEEP); -} - -static struct regval ov_initvals[] = { - {REG_CLKRC, CLK_PLL}, - {REG_COM11, 0x01}, - {0x6a, 0x7d}, - {REG_AECH, 0x40}, - {REG_GAIN, 0x00}, - {REG_BLUE, 0x80}, - {REG_RED, 0x80}, - /* Do not enable fast AEC for now */ - /*{REG_COM8, COM8_FASTAEC|COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC},*/ - {REG_COM8, COM8_AECSTEP|COM8_BFILT|COM8_AGC|COM8_AEC}, - {0x39, 0x50}, {0x38, 0x93}, - {0x37, 0x00}, {0x35, 0x81}, - {REG_COM5, 0x20}, - {REG_COM1, 0x00}, - {REG_COM3, 0x00}, - {REG_COM4, 0x00}, - {REG_PSHFT, 0x00}, - {0x16, 0x07}, - {0x33, 0xe2}, {0x34, 0xbf}, - {REG_COM16, 0x00}, - {0x96, 0x04}, - /* Gamma curve values */ -/* { 0x7a, 0x20 }, { 0x7b, 0x10 }, - { 0x7c, 0x1e }, { 0x7d, 0x35 }, - { 0x7e, 0x5a }, { 0x7f, 0x69 }, - { 0x80, 0x76 }, { 0x81, 0x80 }, - { 0x82, 0x88 }, { 0x83, 0x8f }, - { 0x84, 0x96 }, { 0x85, 0xa3 }, - { 0x86, 0xaf }, { 0x87, 0xc4 }, - { 0x88, 0xd7 }, { 0x89, 0xe8 }, -*/ - {REG_GFIX, 0x40}, - {0x8e, 0x00}, - {REG_COM12, 0x73}, - {0x8f, 0xdf}, {0x8b, 0x06}, - {0x8c, 0x20}, - {0x94, 0x88}, {0x95, 0x88}, -/* {REG_COM15, 0xc1}, TODO */ - {0x29, 0x3f}, - {REG_COM6, 0x42}, - {REG_BD50MAX, 0x80}, - {REG_HAECC6, 0xb8}, {REG_HAECC7, 0x92}, - {REG_BD60MAX, 0x0a}, - {0x90, 0x00}, {0x91, 0x00}, - {REG_HAECC1, 0x00}, {REG_HAECC2, 0x00}, - {REG_AEW, 0x68}, {REG_AEB, 0x5c}, - {REG_VPT, 0xc3}, - {REG_COM9, 0x2e}, - {0x2a, 0x00}, {0x2b, 0x00}, - - {0xff, 0xff}, /* END MARKER */ -}; - -/* Probe the I2C bus and initialise the sensor chip */ -int stk_sensor_init(struct stk_camera *dev) -{ - u8 idl = 0; - u8 idh = 0; - - if (stk_camera_write_reg(dev, STK_IIC_ENABLE, STK_IIC_ENABLE_YES) - || stk_camera_write_reg(dev, STK_IIC_ADDR, SENSOR_ADDRESS) - || stk_sensor_outb(dev, REG_COM7, COM7_RESET)) { - STK_ERROR("Sensor resetting failed\n"); - return -ENODEV; - } - msleep(10); - /* Read the manufacturer ID: ov = 0x7FA2 */ - if (stk_sensor_inb(dev, REG_MIDH, &idh) - || stk_sensor_inb(dev, REG_MIDL, &idl)) { - STK_ERROR("Strange error reading sensor ID\n"); - return -ENODEV; - } - if (idh != 0x7f || idl != 0xa2) { - STK_ERROR("Huh? you don't have a sensor from ovt\n"); - return -ENODEV; - } - if (stk_sensor_inb(dev, REG_PID, &idh) - || stk_sensor_inb(dev, REG_VER, &idl)) { - STK_ERROR("Could not read sensor model\n"); - return -ENODEV; - } - stk_sensor_write_regvals(dev, ov_initvals); - msleep(10); - STK_INFO("OmniVision sensor detected, id %02X%02X" - " at address %x\n", idh, idl, SENSOR_ADDRESS); - return 0; -} - -/* V4L2_PIX_FMT_UYVY */ -static struct regval ov_fmt_uyvy[] = { - {REG_TSLB, TSLB_YLAST|0x08 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* V4L2_PIX_FMT_YUYV */ -static struct regval ov_fmt_yuyv[] = { - {REG_TSLB, 0 }, - { 0x4f, 0x80 }, /* "matrix coefficient 1" */ - { 0x50, 0x80 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x22 }, /* "matrix coefficient 4" */ - { 0x53, 0x5e }, /* "matrix coefficient 5" */ - { 0x54, 0x80 }, /* "matrix coefficient 6" */ - {REG_COM13, COM13_UVSAT|COM13_CMATRIX}, - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; - -/* V4L2_PIX_FMT_RGB565X rrrrrggg gggbbbbb */ -static struct regval ov_fmt_rgbr[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, 0x00}, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_RGB565 gggbbbbb rrrrrggg */ -static struct regval ov_fmt_rgbp[] = { - { REG_RGB444, 0 }, /* No RGB444 please */ - {REG_TSLB, TSLB_BYTEORD }, - { REG_COM1, 0x0 }, - { REG_COM9, 0x38 }, /* 16x gain ceiling; 0x8 is reserved bit */ - { 0x4f, 0xb3 }, /* "matrix coefficient 1" */ - { 0x50, 0xb3 }, /* "matrix coefficient 2" */ - { 0x51, 0 }, /* vb */ - { 0x52, 0x3d }, /* "matrix coefficient 4" */ - { 0x53, 0xa7 }, /* "matrix coefficient 5" */ - { 0x54, 0xe4 }, /* "matrix coefficient 6" */ - { REG_COM13, COM13_GAMMA }, - { REG_COM15, COM15_RGB565|COM15_R00FF }, - { 0xff, 0xff }, -}; - -/* V4L2_PIX_FMT_SRGGB8 */ -static struct regval ov_fmt_bayer[] = { - /* This changes color order */ - {REG_TSLB, 0x40}, /* BGGR */ - /* {REG_TSLB, 0x08}, */ /* BGGR with vertical image flipping */ - {REG_COM15, COM15_R00FF }, - {0xff, 0xff}, /* END MARKER */ -}; -/* - * Store a set of start/stop values into the camera. - */ -static int stk_sensor_set_hw(struct stk_camera *dev, - int hstart, int hstop, int vstart, int vstop) -{ - int ret; - unsigned char v; -/* - * Horizontal: 11 bits, top 8 live in hstart and hstop. Bottom 3 of - * hstart are in href[2:0], bottom 3 of hstop in href[5:3]. There is - * a mystery "edge offset" value in the top two bits of href. - */ - ret = stk_sensor_outb(dev, REG_HSTART, (hstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_HSTOP, (hstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_HREF, &v); - v = (v & 0xc0) | ((hstop & 0x7) << 3) | (hstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_HREF, v); -/* - * Vertical: similar arrangement (note: this is different from ov7670.c) - */ - ret += stk_sensor_outb(dev, REG_VSTART, (vstart >> 3) & 0xff); - ret += stk_sensor_outb(dev, REG_VSTOP, (vstop >> 3) & 0xff); - ret += stk_sensor_inb(dev, REG_VREF, &v); - v = (v & 0xc0) | ((vstop & 0x7) << 3) | (vstart & 0x7); - msleep(10); - ret += stk_sensor_outb(dev, REG_VREF, v); - return ret; -} - - -int stk_sensor_configure(struct stk_camera *dev) -{ - int com7; - /* - * We setup the sensor to output dummy lines in low-res modes, - * so we don't get absurdly hight framerates. - */ - unsigned dummylines; - int flip; - struct regval *rv; - - switch (dev->vsettings.mode) { - case MODE_QCIF: com7 = COM7_FMT_QCIF; - dummylines = 604; - break; - case MODE_QVGA: com7 = COM7_FMT_QVGA; - dummylines = 267; - break; - case MODE_CIF: com7 = COM7_FMT_CIF; - dummylines = 412; - break; - case MODE_VGA: com7 = COM7_FMT_VGA; - dummylines = 11; - break; - case MODE_SXGA: com7 = COM7_FMT_SXGA; - dummylines = 0; - break; - default: STK_ERROR("Unsupported mode %d\n", dev->vsettings.mode); - return -EFAULT; - } - switch (dev->vsettings.palette) { - case V4L2_PIX_FMT_UYVY: - com7 |= COM7_YUV; - rv = ov_fmt_uyvy; - break; - case V4L2_PIX_FMT_YUYV: - com7 |= COM7_YUV; - rv = ov_fmt_yuyv; - break; - case V4L2_PIX_FMT_RGB565: - com7 |= COM7_RGB; - rv = ov_fmt_rgbp; - break; - case V4L2_PIX_FMT_RGB565X: - com7 |= COM7_RGB; - rv = ov_fmt_rgbr; - break; - case V4L2_PIX_FMT_SBGGR8: - com7 |= COM7_PBAYER; - rv = ov_fmt_bayer; - break; - default: STK_ERROR("Unsupported colorspace\n"); - return -EFAULT; - } - /*FIXME sometimes the sensor go to a bad state - stk_sensor_write_regvals(dev, ov_initvals); */ - stk_sensor_outb(dev, REG_COM7, com7); - msleep(50); - stk_sensor_write_regvals(dev, rv); - flip = (dev->vsettings.vflip?MVFP_FLIP:0) - | (dev->vsettings.hflip?MVFP_MIRROR:0); - stk_sensor_outb(dev, REG_MVFP, flip); - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8 - && !dev->vsettings.vflip) - stk_sensor_outb(dev, REG_TSLB, 0x08); - stk_sensor_outb(dev, REG_ADVFH, dummylines >> 8); - stk_sensor_outb(dev, REG_ADVFL, dummylines & 0xff); - msleep(50); - switch (dev->vsettings.mode) { - case MODE_VGA: - if (stk_sensor_set_hw(dev, 302, 1582, 6, 486)) - STK_ERROR("stk_sensor_set_hw failed (VGA)\n"); - break; - case MODE_SXGA: - case MODE_CIF: - case MODE_QVGA: - case MODE_QCIF: - /*FIXME These settings seem ignored by the sensor - if (stk_sensor_set_hw(dev, 220, 1500, 10, 1034)) - STK_ERROR("stk_sensor_set_hw failed (SXGA)\n"); - */ - break; - } - msleep(10); - return 0; -} - -int stk_sensor_set_brightness(struct stk_camera *dev, int br) -{ - if (br < 0 || br > 0xff) - return -EINVAL; - stk_sensor_outb(dev, REG_AEB, max(0x00, br - 6)); - stk_sensor_outb(dev, REG_AEW, min(0xff, br + 6)); - return 0; -} - diff --git a/drivers/media/video/stk-webcam.c b/drivers/media/video/stk-webcam.c deleted file mode 100644 index 86a0fc56c330..000000000000 --- a/drivers/media/video/stk-webcam.c +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * stk-webcam.c : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan - * - * Some parts are inspired from cafe_ccic.c - * Copyright 2006-2007 Jonathan Corbet - * - * 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 - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "stk-webcam.h" - - -static bool hflip; -module_param(hflip, bool, 0444); -MODULE_PARM_DESC(hflip, "Horizontal image flip (mirror). Defaults to 0"); - -static bool vflip; -module_param(vflip, bool, 0444); -MODULE_PARM_DESC(vflip, "Vertical image flip. Defaults to 0"); - -static int debug; -module_param(debug, int, 0444); -MODULE_PARM_DESC(debug, "Debug v4l ioctls. Defaults to 0"); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jaime Velasco Juan and Nicolas VIVIEN"); -MODULE_DESCRIPTION("Syntek DC1125 webcam driver"); - - -/* bool for webcam LED management */ -int first_init = 1; - -/* Some cameras have audio interfaces, we aren't interested in those */ -static struct usb_device_id stkwebcam_table[] = { - { USB_DEVICE_AND_INTERFACE_INFO(0x174f, 0xa311, 0xff, 0xff, 0xff) }, - { USB_DEVICE_AND_INTERFACE_INFO(0x05e1, 0x0501, 0xff, 0xff, 0xff) }, - { } -}; -MODULE_DEVICE_TABLE(usb, stkwebcam_table); - -/* - * Basic stuff - */ -int stk_camera_write_reg(struct stk_camera *dev, u16 index, u8 value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0), - 0x01, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - index, - NULL, - 0, - 500); - if (ret < 0) - return ret; - else - return 0; -} - -int stk_camera_read_reg(struct stk_camera *dev, u16 index, int *value) -{ - struct usb_device *udev = dev->udev; - int ret; - - ret = usb_control_msg(udev, usb_rcvctrlpipe(udev, 0), - 0x00, - USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0x00, - index, - (u8 *) value, - sizeof(u8), - 500); - if (ret < 0) - return ret; - else - return 0; -} - -static int stk_start_stream(struct stk_camera *dev) -{ - int value; - int i, ret; - int value_116, value_117; - - if (!is_present(dev)) - return -ENODEV; - if (!is_memallocd(dev) || !is_initialised(dev)) { - STK_ERROR("FIXME: Buffers are not allocated\n"); - return -EFAULT; - } - ret = usb_set_interface(dev->udev, 0, 5); - - if (ret < 0) - STK_ERROR("usb_set_interface failed !\n"); - if (stk_sensor_wakeup(dev)) - STK_ERROR("error awaking the sensor\n"); - - stk_camera_read_reg(dev, 0x0116, &value_116); - stk_camera_read_reg(dev, 0x0117, &value_117); - - stk_camera_write_reg(dev, 0x0116, 0x0000); - stk_camera_write_reg(dev, 0x0117, 0x0000); - - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value | 0x80); - - stk_camera_write_reg(dev, 0x0116, value_116); - stk_camera_write_reg(dev, 0x0117, value_117); - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) { - ret = usb_submit_urb(dev->isobufs[i].urb, GFP_KERNEL); - atomic_inc(&dev->urbs_used); - if (ret) - return ret; - } - } - set_streaming(dev); - return 0; -} - -static int stk_stop_stream(struct stk_camera *dev) -{ - int value; - int i; - if (is_present(dev)) { - stk_camera_read_reg(dev, 0x0100, &value); - stk_camera_write_reg(dev, 0x0100, value & ~0x80); - if (dev->isobufs != NULL) { - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].urb) - usb_kill_urb(dev->isobufs[i].urb); - } - } - unset_streaming(dev); - - if (usb_set_interface(dev->udev, 0, 0)) - STK_ERROR("usb_set_interface failed !\n"); - if (stk_sensor_sleep(dev)) - STK_ERROR("error suspending the sensor\n"); - } - return 0; -} - -/* - * This seems to be the shortest init sequence we - * must do in order to find the sensor - * Bit 5 of reg. 0x0000 here is important, when reset to 0 the sensor - * is also reset. Maybe powers down it? - * Rest of values don't make a difference - */ - -static struct regval stk1125_initvals[] = { - /*TODO: What means this sequence? */ - {0x0000, 0x24}, - {0x0100, 0x21}, - {0x0002, 0x68}, - {0x0003, 0x80}, - {0x0005, 0x00}, - {0x0007, 0x03}, - {0x000d, 0x00}, - {0x000f, 0x02}, - {0x0300, 0x12}, - {0x0350, 0x41}, - {0x0351, 0x00}, - {0x0352, 0x00}, - {0x0353, 0x00}, - {0x0018, 0x10}, - {0x0019, 0x00}, - {0x001b, 0x0e}, - {0x001c, 0x46}, - {0x0300, 0x80}, - {0x001a, 0x04}, - {0x0110, 0x00}, - {0x0111, 0x00}, - {0x0112, 0x00}, - {0x0113, 0x00}, - - {0xffff, 0xff}, -}; - - -static int stk_initialise(struct stk_camera *dev) -{ - struct regval *rv; - int ret; - if (!is_present(dev)) - return -ENODEV; - if (is_initialised(dev)) - return 0; - rv = stk1125_initvals; - while (rv->reg != 0xffff) { - ret = stk_camera_write_reg(dev, rv->reg, rv->val); - if (ret) - return ret; - rv++; - } - if (stk_sensor_init(dev) == 0) { - set_initialised(dev); - return 0; - } else - return -1; -} - -/* *********************************************** */ -/* - * This function is called as an URB transfert is complete (Isochronous pipe). - * So, the traitement is done in interrupt time, so it has be fast, not crash, - * and not stall. Neat. - */ -static void stk_isoc_handler(struct urb *urb) -{ - int i; - int ret; - int framelen; - unsigned long flags; - - unsigned char *fill = NULL; - unsigned char *iso_buf = NULL; - - struct stk_camera *dev; - struct stk_sio_buffer *fb; - - dev = (struct stk_camera *) urb->context; - - if (dev == NULL) { - STK_ERROR("isoc_handler called with NULL device !\n"); - return; - } - - if (urb->status == -ENOENT || urb->status == -ECONNRESET - || urb->status == -ESHUTDOWN) { - atomic_dec(&dev->urbs_used); - return; - } - - spin_lock_irqsave(&dev->spinlock, flags); - - if (urb->status != -EINPROGRESS && urb->status != 0) { - STK_ERROR("isoc_handler: urb->status == %d\n", urb->status); - goto resubmit; - } - - if (list_empty(&dev->sio_avail)) { - /*FIXME Stop streaming after a while */ - (void) (printk_ratelimit() && - STK_ERROR("isoc_handler without available buffer!\n")); - goto resubmit; - } - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fill = fb->buffer + fb->v4lbuf.bytesused; - - for (i = 0; i < urb->number_of_packets; i++) { - if (urb->iso_frame_desc[i].status != 0) { - if (urb->iso_frame_desc[i].status != -EXDEV) - STK_ERROR("Frame %d has error %d\n", i, - urb->iso_frame_desc[i].status); - continue; - } - framelen = urb->iso_frame_desc[i].actual_length; - iso_buf = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - if (framelen <= 4) - continue; /* no data */ - - /* - * we found something informational from there - * the isoc frames have to type of headers - * type1: 00 xx 00 00 or 20 xx 00 00 - * type2: 80 xx 00 00 00 00 00 00 or a0 xx 00 00 00 00 00 00 - * xx is a sequencer which has never been seen over 0x3f - * imho data written down looks like bayer, i see similarities - * after every 640 bytes - */ - if (*iso_buf & 0x80) { - framelen -= 8; - iso_buf += 8; - /* This marks a new frame */ - if (fb->v4lbuf.bytesused != 0 - && fb->v4lbuf.bytesused != dev->frame_size) { - (void) (printk_ratelimit() && - STK_ERROR("frame %d, " - "bytesused=%d, skipping\n", - i, fb->v4lbuf.bytesused)); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else if (fb->v4lbuf.bytesused == dev->frame_size) { - if (list_is_singular(&dev->sio_avail)) { - /* Always reuse the last buffer */ - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } else { - list_move_tail(dev->sio_avail.next, - &dev->sio_full); - wake_up(&dev->wait_frame); - fb = list_first_entry(&dev->sio_avail, - struct stk_sio_buffer, list); - fb->v4lbuf.bytesused = 0; - fill = fb->buffer; - } - } - } else { - framelen -= 4; - iso_buf += 4; - } - - /* Our buffer is full !!! */ - if (framelen + fb->v4lbuf.bytesused > dev->frame_size) { - (void) (printk_ratelimit() && - STK_ERROR("Frame buffer overflow, lost sync\n")); - /*FIXME Do something here? */ - continue; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - memcpy(fill, iso_buf, framelen); - spin_lock_irqsave(&dev->spinlock, flags); - fill += framelen; - - /* New size of our buffer */ - fb->v4lbuf.bytesused += framelen; - } - -resubmit: - spin_unlock_irqrestore(&dev->spinlock, flags); - urb->dev = dev->udev; - ret = usb_submit_urb(urb, GFP_ATOMIC); - if (ret != 0) { - STK_ERROR("Error (%d) re-submitting urb in stk_isoc_handler.\n", - ret); - } -} - -/* -------------------------------------------- */ - -static int stk_prepare_iso(struct stk_camera *dev) -{ - void *kbuf; - int i, j; - struct urb *urb; - struct usb_device *udev; - - if (dev == NULL) - return -ENXIO; - udev = dev->udev; - - if (dev->isobufs) - STK_ERROR("isobufs already allocated. Bad\n"); - else - dev->isobufs = kcalloc(MAX_ISO_BUFS, sizeof(*dev->isobufs), - GFP_KERNEL); - if (dev->isobufs == NULL) { - STK_ERROR("Unable to allocate iso buffers\n"); - return -ENOMEM; - } - for (i = 0; i < MAX_ISO_BUFS; i++) { - if (dev->isobufs[i].data == NULL) { - kbuf = kzalloc(ISO_BUFFER_SIZE, GFP_KERNEL); - if (kbuf == NULL) { - STK_ERROR("Failed to allocate iso buffer %d\n", - i); - goto isobufs_out; - } - dev->isobufs[i].data = kbuf; - } else - STK_ERROR("isobuf data already allocated\n"); - if (dev->isobufs[i].urb == NULL) { - urb = usb_alloc_urb(ISO_FRAMES_PER_DESC, GFP_KERNEL); - if (urb == NULL) { - STK_ERROR("Failed to allocate URB %d\n", i); - goto isobufs_out; - } - dev->isobufs[i].urb = urb; - } else { - STK_ERROR("Killing URB\n"); - usb_kill_urb(dev->isobufs[i].urb); - urb = dev->isobufs[i].urb; - } - urb->interval = 1; - urb->dev = udev; - urb->pipe = usb_rcvisocpipe(udev, dev->isoc_ep); - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = dev->isobufs[i].data; - urb->transfer_buffer_length = ISO_BUFFER_SIZE; - urb->complete = stk_isoc_handler; - urb->context = dev; - urb->start_frame = 0; - urb->number_of_packets = ISO_FRAMES_PER_DESC; - - for (j = 0; j < ISO_FRAMES_PER_DESC; j++) { - urb->iso_frame_desc[j].offset = j * ISO_MAX_FRAME_SIZE; - urb->iso_frame_desc[j].length = ISO_MAX_FRAME_SIZE; - } - } - set_memallocd(dev); - return 0; - -isobufs_out: - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].data; i++) - kfree(dev->isobufs[i].data); - for (i = 0; i < MAX_ISO_BUFS && dev->isobufs[i].urb; i++) - usb_free_urb(dev->isobufs[i].urb); - kfree(dev->isobufs); - dev->isobufs = NULL; - return -ENOMEM; -} - -static void stk_clean_iso(struct stk_camera *dev) -{ - int i; - - if (dev == NULL || dev->isobufs == NULL) - return; - - for (i = 0; i < MAX_ISO_BUFS; i++) { - struct urb *urb; - - urb = dev->isobufs[i].urb; - if (urb) { - if (atomic_read(&dev->urbs_used) && is_present(dev)) - usb_kill_urb(urb); - usb_free_urb(urb); - } - kfree(dev->isobufs[i].data); - } - kfree(dev->isobufs); - dev->isobufs = NULL; - unset_memallocd(dev); -} - -static int stk_setup_siobuf(struct stk_camera *dev, int index) -{ - struct stk_sio_buffer *buf = dev->sio_bufs + index; - INIT_LIST_HEAD(&buf->list); - buf->v4lbuf.length = PAGE_ALIGN(dev->frame_size); - buf->buffer = vmalloc_user(buf->v4lbuf.length); - if (buf->buffer == NULL) - return -ENOMEM; - buf->mapcount = 0; - buf->dev = dev; - buf->v4lbuf.index = index; - buf->v4lbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - buf->v4lbuf.field = V4L2_FIELD_NONE; - buf->v4lbuf.memory = V4L2_MEMORY_MMAP; - buf->v4lbuf.m.offset = 2*index*buf->v4lbuf.length; - return 0; -} - -static int stk_free_sio_buffers(struct stk_camera *dev) -{ - int i; - int nbufs; - unsigned long flags; - if (dev->n_sbufs == 0 || dev->sio_bufs == NULL) - return 0; - /* - * If any buffers are mapped, we cannot free them at all. - */ - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].mapcount > 0) - return -EBUSY; - } - /* - * OK, let's do it. - */ - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - nbufs = dev->n_sbufs; - dev->n_sbufs = 0; - spin_unlock_irqrestore(&dev->spinlock, flags); - for (i = 0; i < nbufs; i++) { - if (dev->sio_bufs[i].buffer != NULL) - vfree(dev->sio_bufs[i].buffer); - } - kfree(dev->sio_bufs); - dev->sio_bufs = NULL; - return 0; -} - -static int stk_prepare_sio_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int i; - if (dev->sio_bufs != NULL) - STK_ERROR("sio_bufs already allocated\n"); - else { - dev->sio_bufs = kzalloc(n_sbufs * sizeof(struct stk_sio_buffer), - GFP_KERNEL); - if (dev->sio_bufs == NULL) - return -ENOMEM; - for (i = 0; i < n_sbufs; i++) { - if (stk_setup_siobuf(dev, i)) - return (dev->n_sbufs > 1 ? 0 : -ENOMEM); - dev->n_sbufs = i+1; - } - } - return 0; -} - -static int stk_allocate_buffers(struct stk_camera *dev, unsigned n_sbufs) -{ - int err; - err = stk_prepare_iso(dev); - if (err) { - stk_clean_iso(dev); - return err; - } - err = stk_prepare_sio_buffers(dev, n_sbufs); - if (err) { - stk_free_sio_buffers(dev); - return err; - } - return 0; -} - -static void stk_free_buffers(struct stk_camera *dev) -{ - stk_clean_iso(dev); - stk_free_sio_buffers(dev); -} -/* -------------------------------------------- */ - -/* v4l file operations */ - -static int v4l_stk_open(struct file *fp) -{ - struct stk_camera *dev; - struct video_device *vdev; - - vdev = video_devdata(fp); - dev = vdev_to_camera(vdev); - - if (dev == NULL || !is_present(dev)) - return -ENXIO; - - if (!first_init) - stk_camera_write_reg(dev, 0x0, 0x24); - else - first_init = 0; - - fp->private_data = dev; - usb_autopm_get_interface(dev->interface); - - return 0; -} - -static int v4l_stk_release(struct file *fp) -{ - struct stk_camera *dev = fp->private_data; - - if (dev->owner == fp) { - stk_stop_stream(dev); - stk_free_buffers(dev); - stk_camera_write_reg(dev, 0x0, 0x49); /* turn off the LED */ - unset_initialised(dev); - dev->owner = NULL; - } - - if (is_present(dev)) - usb_autopm_put_interface(dev->interface); - - return 0; -} - -static ssize_t v4l_stk_read(struct file *fp, char __user *buf, - size_t count, loff_t *f_pos) -{ - int i; - int ret; - unsigned long flags; - struct stk_sio_buffer *sbuf; - struct stk_camera *dev = fp->private_data; - - if (!is_present(dev)) - return -EIO; - if (dev->owner && dev->owner != fp) - return -EBUSY; - dev->owner = fp; - if (!is_streaming(dev)) { - if (stk_initialise(dev) - || stk_allocate_buffers(dev, 3) - || stk_start_stream(dev)) - return -ENOMEM; - spin_lock_irqsave(&dev->spinlock, flags); - for (i = 0; i < dev->n_sbufs; i++) { - list_add_tail(&dev->sio_bufs[i].list, &dev->sio_avail); - dev->sio_bufs[i].v4lbuf.flags = V4L2_BUF_FLAG_QUEUED; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - } - if (*f_pos == 0) { - if (fp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - } - if (count + *f_pos > dev->frame_size) - count = dev->frame_size - *f_pos; - spin_lock_irqsave(&dev->spinlock, flags); - if (list_empty(&dev->sio_full)) { - spin_unlock_irqrestore(&dev->spinlock, flags); - STK_ERROR("BUG: No siobufs ready\n"); - return 0; - } - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - spin_unlock_irqrestore(&dev->spinlock, flags); - - if (copy_to_user(buf, sbuf->buffer + *f_pos, count)) - return -EFAULT; - - *f_pos += count; - - if (*f_pos >= dev->frame_size) { - *f_pos = 0; - spin_lock_irqsave(&dev->spinlock, flags); - list_move_tail(&sbuf->list, &dev->sio_avail); - spin_unlock_irqrestore(&dev->spinlock, flags); - } - return count; -} - -static unsigned int v4l_stk_poll(struct file *fp, poll_table *wait) -{ - struct stk_camera *dev = fp->private_data; - - poll_wait(fp, &dev->wait_frame, wait); - - if (!is_present(dev)) - return POLLERR; - - if (!list_empty(&dev->sio_full)) - return POLLIN | POLLRDNORM; - - return 0; -} - - -static void stk_v4l_vm_open(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount++; -} -static void stk_v4l_vm_close(struct vm_area_struct *vma) -{ - struct stk_sio_buffer *sbuf = vma->vm_private_data; - sbuf->mapcount--; - if (sbuf->mapcount == 0) - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_MAPPED; -} -static const struct vm_operations_struct stk_v4l_vm_ops = { - .open = stk_v4l_vm_open, - .close = stk_v4l_vm_close -}; - -static int v4l_stk_mmap(struct file *fp, struct vm_area_struct *vma) -{ - unsigned int i; - int ret; - unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; - struct stk_camera *dev = fp->private_data; - struct stk_sio_buffer *sbuf = NULL; - - if (!(vma->vm_flags & VM_WRITE) || !(vma->vm_flags & VM_SHARED)) - return -EINVAL; - - for (i = 0; i < dev->n_sbufs; i++) { - if (dev->sio_bufs[i].v4lbuf.m.offset == offset) { - sbuf = dev->sio_bufs + i; - break; - } - } - if (sbuf == NULL) - return -EINVAL; - ret = remap_vmalloc_range(vma, sbuf->buffer, 0); - if (ret) - return ret; - vma->vm_flags |= VM_DONTEXPAND; - vma->vm_private_data = sbuf; - vma->vm_ops = &stk_v4l_vm_ops; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_MAPPED; - stk_v4l_vm_open(vma); - return 0; -} - -/* v4l ioctl handlers */ - -static int stk_vidioc_querycap(struct file *filp, - void *priv, struct v4l2_capability *cap) -{ - strcpy(cap->driver, "stk"); - strcpy(cap->card, "stk"); - cap->version = DRIVER_VERSION_NUM; - - cap->capabilities = V4L2_CAP_VIDEO_CAPTURE - | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; - return 0; -} - -static int stk_vidioc_enum_input(struct file *filp, - void *priv, struct v4l2_input *input) -{ - if (input->index != 0) - return -EINVAL; - - strcpy(input->name, "Syntek USB Camera"); - input->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - - -static int stk_vidioc_g_input(struct file *filp, void *priv, unsigned int *i) -{ - *i = 0; - return 0; -} - -static int stk_vidioc_s_input(struct file *filp, void *priv, unsigned int i) -{ - if (i != 0) - return -EINVAL; - else - return 0; -} - -/* from vivi.c */ -static int stk_vidioc_s_std(struct file *filp, void *priv, v4l2_std_id *a) -{ - return 0; -} - -/* List of all V4Lv2 controls supported by the driver */ -static struct v4l2_queryctrl stk_controls[] = { - { - .id = V4L2_CID_BRIGHTNESS, - .type = V4L2_CTRL_TYPE_INTEGER, - .name = "Brightness", - .minimum = 0, - .maximum = 0xffff, - .step = 0x0100, - .default_value = 0x6000, - }, - { - .id = V4L2_CID_HFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Horizontal Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, - { - .id = V4L2_CID_VFLIP, - .type = V4L2_CTRL_TYPE_BOOLEAN, - .name = "Vertical Flip", - .minimum = 0, - .maximum = 1, - .step = 1, - .default_value = 1, - }, -}; - -static int stk_vidioc_queryctrl(struct file *filp, - void *priv, struct v4l2_queryctrl *c) -{ - int i; - int nbr; - nbr = ARRAY_SIZE(stk_controls); - - for (i = 0; i < nbr; i++) { - if (stk_controls[i].id == c->id) { - memcpy(c, &stk_controls[i], - sizeof(struct v4l2_queryctrl)); - return 0; - } - } - return -EINVAL; -} - -static int stk_vidioc_g_ctrl(struct file *filp, - void *priv, struct v4l2_control *c) -{ - struct stk_camera *dev = priv; - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - c->value = dev->vsettings.brightness; - break; - case V4L2_CID_HFLIP: - c->value = dev->vsettings.hflip; - break; - case V4L2_CID_VFLIP: - c->value = dev->vsettings.vflip; - break; - default: - return -EINVAL; - } - return 0; -} - -static int stk_vidioc_s_ctrl(struct file *filp, - void *priv, struct v4l2_control *c) -{ - struct stk_camera *dev = priv; - switch (c->id) { - case V4L2_CID_BRIGHTNESS: - dev->vsettings.brightness = c->value; - return stk_sensor_set_brightness(dev, c->value >> 8); - case V4L2_CID_HFLIP: - dev->vsettings.hflip = c->value; - return 0; - case V4L2_CID_VFLIP: - dev->vsettings.vflip = c->value; - return 0; - default: - return -EINVAL; - } - return 0; -} - - -static int stk_vidioc_enum_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_fmtdesc *fmtd) -{ - switch (fmtd->index) { - case 0: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565; - strcpy(fmtd->description, "r5g6b5"); - break; - case 1: - fmtd->pixelformat = V4L2_PIX_FMT_RGB565X; - strcpy(fmtd->description, "r5g6b5BE"); - break; - case 2: - fmtd->pixelformat = V4L2_PIX_FMT_UYVY; - strcpy(fmtd->description, "yuv4:2:2"); - break; - case 3: - fmtd->pixelformat = V4L2_PIX_FMT_SBGGR8; - strcpy(fmtd->description, "Raw bayer"); - break; - case 4: - fmtd->pixelformat = V4L2_PIX_FMT_YUYV; - strcpy(fmtd->description, "yuv4:2:2"); - break; - default: - return -EINVAL; - } - return 0; -} - -static struct stk_size { - unsigned w; - unsigned h; - enum stk_mode m; -} stk_sizes[] = { - { .w = 1280, .h = 1024, .m = MODE_SXGA, }, - { .w = 640, .h = 480, .m = MODE_VGA, }, - { .w = 352, .h = 288, .m = MODE_CIF, }, - { .w = 320, .h = 240, .m = MODE_QVGA, }, - { .w = 176, .h = 144, .m = MODE_QCIF, }, -}; - -static int stk_vidioc_g_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *f) -{ - struct v4l2_pix_format *pix_format = &f->fmt.pix; - struct stk_camera *dev = priv; - int i; - - for (i = 0; i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode; i++) - ; - if (i == ARRAY_SIZE(stk_sizes)) { - STK_ERROR("ERROR: mode invalid\n"); - return -EINVAL; - } - pix_format->width = stk_sizes[i].w; - pix_format->height = stk_sizes[i].h; - pix_format->field = V4L2_FIELD_NONE; - pix_format->colorspace = V4L2_COLORSPACE_SRGB; - pix_format->pixelformat = dev->vsettings.palette; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - pix_format->bytesperline = pix_format->width; - else - pix_format->bytesperline = 2 * pix_format->width; - pix_format->sizeimage = pix_format->bytesperline - * pix_format->height; - return 0; -} - -static int stk_vidioc_try_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - int i; - switch (fmtd->fmt.pix.pixelformat) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - break; - default: - return -EINVAL; - } - for (i = 1; i < ARRAY_SIZE(stk_sizes); i++) { - if (fmtd->fmt.pix.width > stk_sizes[i].w) - break; - } - if (i == ARRAY_SIZE(stk_sizes) - || (abs(fmtd->fmt.pix.width - stk_sizes[i-1].w) - < abs(fmtd->fmt.pix.width - stk_sizes[i].w))) { - fmtd->fmt.pix.height = stk_sizes[i-1].h; - fmtd->fmt.pix.width = stk_sizes[i-1].w; - fmtd->fmt.pix.priv = i - 1; - } else { - fmtd->fmt.pix.height = stk_sizes[i].h; - fmtd->fmt.pix.width = stk_sizes[i].w; - fmtd->fmt.pix.priv = i; - } - - fmtd->fmt.pix.field = V4L2_FIELD_NONE; - fmtd->fmt.pix.colorspace = V4L2_COLORSPACE_SRGB; - if (fmtd->fmt.pix.pixelformat == V4L2_PIX_FMT_SBGGR8) - fmtd->fmt.pix.bytesperline = fmtd->fmt.pix.width; - else - fmtd->fmt.pix.bytesperline = 2 * fmtd->fmt.pix.width; - fmtd->fmt.pix.sizeimage = fmtd->fmt.pix.bytesperline - * fmtd->fmt.pix.height; - return 0; -} - -static int stk_setup_format(struct stk_camera *dev) -{ - int i = 0; - int depth; - if (dev->vsettings.palette == V4L2_PIX_FMT_SBGGR8) - depth = 1; - else - depth = 2; - while (i < ARRAY_SIZE(stk_sizes) && - stk_sizes[i].m != dev->vsettings.mode) - i++; - if (i == ARRAY_SIZE(stk_sizes)) { - STK_ERROR("Something is broken in %s\n", __func__); - return -EFAULT; - } - /* This registers controls some timings, not sure of what. */ - stk_camera_write_reg(dev, 0x001b, 0x0e); - if (dev->vsettings.mode == MODE_SXGA) - stk_camera_write_reg(dev, 0x001c, 0x0e); - else - stk_camera_write_reg(dev, 0x001c, 0x46); - /* - * Registers 0x0115 0x0114 are the size of each line (bytes), - * regs 0x0117 0x0116 are the heigth of the image. - */ - stk_camera_write_reg(dev, 0x0115, - ((stk_sizes[i].w * depth) >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0114, - (stk_sizes[i].w * depth) & 0xff); - stk_camera_write_reg(dev, 0x0117, - (stk_sizes[i].h >> 8) & 0xff); - stk_camera_write_reg(dev, 0x0116, - stk_sizes[i].h & 0xff); - return stk_sensor_configure(dev); -} - -static int stk_vidioc_s_fmt_vid_cap(struct file *filp, - void *priv, struct v4l2_format *fmtd) -{ - int ret; - struct stk_camera *dev = priv; - - if (dev == NULL) - return -ENODEV; - if (!is_present(dev)) - return -ENODEV; - if (is_streaming(dev)) - return -EBUSY; - if (dev->owner && dev->owner != filp) - return -EBUSY; - ret = stk_vidioc_try_fmt_vid_cap(filp, priv, fmtd); - if (ret) - return ret; - dev->owner = filp; - - dev->vsettings.palette = fmtd->fmt.pix.pixelformat; - stk_free_buffers(dev); - dev->frame_size = fmtd->fmt.pix.sizeimage; - dev->vsettings.mode = stk_sizes[fmtd->fmt.pix.priv].m; - - stk_initialise(dev); - return stk_setup_format(dev); -} - -static int stk_vidioc_reqbufs(struct file *filp, - void *priv, struct v4l2_requestbuffers *rb) -{ - struct stk_camera *dev = priv; - - if (dev == NULL) - return -ENODEV; - if (rb->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - if (is_streaming(dev) - || (dev->owner && dev->owner != filp)) - return -EBUSY; - dev->owner = filp; - - /*FIXME If they ask for zero, we must stop streaming and free */ - if (rb->count < 3) - rb->count = 3; - /* Arbitrary limit */ - else if (rb->count > 5) - rb->count = 5; - - stk_allocate_buffers(dev, rb->count); - rb->count = dev->n_sbufs; - return 0; -} - -static int stk_vidioc_querybuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = priv; - struct stk_sio_buffer *sbuf; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_qbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = priv; - struct stk_sio_buffer *sbuf; - unsigned long flags; - - if (buf->memory != V4L2_MEMORY_MMAP) - return -EINVAL; - - if (buf->index >= dev->n_sbufs) - return -EINVAL; - sbuf = dev->sio_bufs + buf->index; - if (sbuf->v4lbuf.flags & V4L2_BUF_FLAG_QUEUED) - return 0; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_DONE; - spin_lock_irqsave(&dev->spinlock, flags); - list_add_tail(&sbuf->list, &dev->sio_avail); - *buf = sbuf->v4lbuf; - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - -static int stk_vidioc_dqbuf(struct file *filp, - void *priv, struct v4l2_buffer *buf) -{ - struct stk_camera *dev = priv; - struct stk_sio_buffer *sbuf; - unsigned long flags; - int ret; - - if (!is_streaming(dev)) - return -EINVAL; - - if (filp->f_flags & O_NONBLOCK && list_empty(&dev->sio_full)) - return -EWOULDBLOCK; - ret = wait_event_interruptible(dev->wait_frame, - !list_empty(&dev->sio_full) || !is_present(dev)); - if (ret) - return ret; - if (!is_present(dev)) - return -EIO; - - spin_lock_irqsave(&dev->spinlock, flags); - sbuf = list_first_entry(&dev->sio_full, struct stk_sio_buffer, list); - list_del_init(&sbuf->list); - spin_unlock_irqrestore(&dev->spinlock, flags); - sbuf->v4lbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; - sbuf->v4lbuf.flags |= V4L2_BUF_FLAG_DONE; - sbuf->v4lbuf.sequence = ++dev->sequence; - do_gettimeofday(&sbuf->v4lbuf.timestamp); - - *buf = sbuf->v4lbuf; - return 0; -} - -static int stk_vidioc_streamon(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = priv; - if (is_streaming(dev)) - return 0; - if (dev->sio_bufs == NULL) - return -EINVAL; - dev->sequence = 0; - return stk_start_stream(dev); -} - -static int stk_vidioc_streamoff(struct file *filp, - void *priv, enum v4l2_buf_type type) -{ - struct stk_camera *dev = priv; - unsigned long flags; - int i; - stk_stop_stream(dev); - spin_lock_irqsave(&dev->spinlock, flags); - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - for (i = 0; i < dev->n_sbufs; i++) { - INIT_LIST_HEAD(&dev->sio_bufs[i].list); - dev->sio_bufs[i].v4lbuf.flags = 0; - } - spin_unlock_irqrestore(&dev->spinlock, flags); - return 0; -} - - -static int stk_vidioc_g_parm(struct file *filp, - void *priv, struct v4l2_streamparm *sp) -{ - /*FIXME This is not correct */ - sp->parm.capture.timeperframe.numerator = 1; - sp->parm.capture.timeperframe.denominator = 30; - sp->parm.capture.readbuffers = 2; - return 0; -} - -static int stk_vidioc_enum_framesizes(struct file *filp, - void *priv, struct v4l2_frmsizeenum *frms) -{ - if (frms->index >= ARRAY_SIZE(stk_sizes)) - return -EINVAL; - switch (frms->pixel_format) { - case V4L2_PIX_FMT_RGB565: - case V4L2_PIX_FMT_RGB565X: - case V4L2_PIX_FMT_UYVY: - case V4L2_PIX_FMT_YUYV: - case V4L2_PIX_FMT_SBGGR8: - frms->type = V4L2_FRMSIZE_TYPE_DISCRETE; - frms->discrete.width = stk_sizes[frms->index].w; - frms->discrete.height = stk_sizes[frms->index].h; - return 0; - default: return -EINVAL; - } -} - -static struct v4l2_file_operations v4l_stk_fops = { - .owner = THIS_MODULE, - .open = v4l_stk_open, - .release = v4l_stk_release, - .read = v4l_stk_read, - .poll = v4l_stk_poll, - .mmap = v4l_stk_mmap, - .ioctl = video_ioctl2, -}; - -static const struct v4l2_ioctl_ops v4l_stk_ioctl_ops = { - .vidioc_querycap = stk_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = stk_vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = stk_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = stk_vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = stk_vidioc_g_fmt_vid_cap, - .vidioc_enum_input = stk_vidioc_enum_input, - .vidioc_s_input = stk_vidioc_s_input, - .vidioc_g_input = stk_vidioc_g_input, - .vidioc_s_std = stk_vidioc_s_std, - .vidioc_reqbufs = stk_vidioc_reqbufs, - .vidioc_querybuf = stk_vidioc_querybuf, - .vidioc_qbuf = stk_vidioc_qbuf, - .vidioc_dqbuf = stk_vidioc_dqbuf, - .vidioc_streamon = stk_vidioc_streamon, - .vidioc_streamoff = stk_vidioc_streamoff, - .vidioc_queryctrl = stk_vidioc_queryctrl, - .vidioc_g_ctrl = stk_vidioc_g_ctrl, - .vidioc_s_ctrl = stk_vidioc_s_ctrl, - .vidioc_g_parm = stk_vidioc_g_parm, - .vidioc_enum_framesizes = stk_vidioc_enum_framesizes, -}; - -static void stk_v4l_dev_release(struct video_device *vd) -{ - struct stk_camera *dev = vdev_to_camera(vd); - - if (dev->sio_bufs != NULL || dev->isobufs != NULL) - STK_ERROR("We are leaking memory\n"); - usb_put_intf(dev->interface); - kfree(dev); -} - -static struct video_device stk_v4l_data = { - .name = "stkwebcam", - .tvnorms = V4L2_STD_UNKNOWN, - .current_norm = V4L2_STD_UNKNOWN, - .fops = &v4l_stk_fops, - .ioctl_ops = &v4l_stk_ioctl_ops, - .release = stk_v4l_dev_release, -}; - - -static int stk_register_video_device(struct stk_camera *dev) -{ - int err; - - dev->vdev = stk_v4l_data; - dev->vdev.debug = debug; - dev->vdev.parent = &dev->interface->dev; - err = video_register_device(&dev->vdev, VFL_TYPE_GRABBER, -1); - if (err) - STK_ERROR("v4l registration failed\n"); - else - STK_INFO("Syntek USB2.0 Camera is now controlling device %s\n", - video_device_node_name(&dev->vdev)); - return err; -} - - -/* USB Stuff */ - -static int stk_camera_probe(struct usb_interface *interface, - const struct usb_device_id *id) -{ - int i; - int err = 0; - - struct stk_camera *dev = NULL; - struct usb_device *udev = interface_to_usbdev(interface); - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - - dev = kzalloc(sizeof(struct stk_camera), GFP_KERNEL); - if (dev == NULL) { - STK_ERROR("Out of memory !\n"); - return -ENOMEM; - } - - spin_lock_init(&dev->spinlock); - init_waitqueue_head(&dev->wait_frame); - - dev->udev = udev; - dev->interface = interface; - usb_get_intf(interface); - - dev->vsettings.vflip = vflip; - dev->vsettings.hflip = hflip; - dev->n_sbufs = 0; - set_present(dev); - - /* Set up the endpoint information - * use only the first isoc-in endpoint - * for the current alternate setting */ - iface_desc = interface->cur_altsetting; - - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - - if (!dev->isoc_ep - && usb_endpoint_is_isoc_in(endpoint)) { - /* we found an isoc in endpoint */ - dev->isoc_ep = usb_endpoint_num(endpoint); - break; - } - } - if (!dev->isoc_ep) { - STK_ERROR("Could not find isoc-in endpoint"); - err = -ENODEV; - goto error; - } - dev->vsettings.brightness = 0x7fff; - dev->vsettings.palette = V4L2_PIX_FMT_RGB565; - dev->vsettings.mode = MODE_VGA; - dev->frame_size = 640 * 480 * 2; - - INIT_LIST_HEAD(&dev->sio_avail); - INIT_LIST_HEAD(&dev->sio_full); - - usb_set_intfdata(interface, dev); - - err = stk_register_video_device(dev); - if (err) - goto error; - - return 0; - -error: - kfree(dev); - return err; -} - -static void stk_camera_disconnect(struct usb_interface *interface) -{ - struct stk_camera *dev = usb_get_intfdata(interface); - - usb_set_intfdata(interface, NULL); - unset_present(dev); - - wake_up_interruptible(&dev->wait_frame); - - STK_INFO("Syntek USB2.0 Camera release resources device %s\n", - video_device_node_name(&dev->vdev)); - - video_unregister_device(&dev->vdev); -} - -#ifdef CONFIG_PM -static int stk_camera_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (is_streaming(dev)) { - stk_stop_stream(dev); - /* yes, this is ugly */ - set_streaming(dev); - } - return 0; -} - -static int stk_camera_resume(struct usb_interface *intf) -{ - struct stk_camera *dev = usb_get_intfdata(intf); - if (!is_initialised(dev)) - return 0; - unset_initialised(dev); - stk_initialise(dev); - stk_camera_write_reg(dev, 0x0, 0x49); - stk_setup_format(dev); - if (is_streaming(dev)) - stk_start_stream(dev); - return 0; -} -#endif - -static struct usb_driver stk_camera_driver = { - .name = "stkwebcam", - .probe = stk_camera_probe, - .disconnect = stk_camera_disconnect, - .id_table = stkwebcam_table, -#ifdef CONFIG_PM - .suspend = stk_camera_suspend, - .resume = stk_camera_resume, -#endif -}; - -module_usb_driver(stk_camera_driver); diff --git a/drivers/media/video/stk-webcam.h b/drivers/media/video/stk-webcam.h deleted file mode 100644 index 9f6736637571..000000000000 --- a/drivers/media/video/stk-webcam.h +++ /dev/null @@ -1,134 +0,0 @@ -/* - * stk-webcam.h : Driver for Syntek 1125 USB webcam controller - * - * Copyright (C) 2006 Nicolas VIVIEN - * Copyright 2007-2008 Jaime Velasco Juan - * - * 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 - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#ifndef STKWEBCAM_H -#define STKWEBCAM_H - -#include -#include - -#define DRIVER_VERSION "v0.0.1" -#define DRIVER_VERSION_NUM 0x000001 - -#define MAX_ISO_BUFS 3 -#define ISO_FRAMES_PER_DESC 16 -#define ISO_MAX_FRAME_SIZE 3 * 1024 -#define ISO_BUFFER_SIZE (ISO_FRAMES_PER_DESC * ISO_MAX_FRAME_SIZE) - - -#define PREFIX "stkwebcam: " -#define STK_INFO(str, args...) printk(KERN_INFO PREFIX str, ##args) -#define STK_ERROR(str, args...) printk(KERN_ERR PREFIX str, ##args) -#define STK_WARNING(str, args...) printk(KERN_WARNING PREFIX str, ##args) - -struct stk_iso_buf { - void *data; - int length; - int read; - struct urb *urb; -}; - -/* Streaming IO buffers */ -struct stk_sio_buffer { - struct v4l2_buffer v4lbuf; - char *buffer; - int mapcount; - struct stk_camera *dev; - struct list_head list; -}; - -enum stk_mode {MODE_VGA, MODE_SXGA, MODE_CIF, MODE_QVGA, MODE_QCIF}; - -struct stk_video { - enum stk_mode mode; - int brightness; - __u32 palette; - int hflip; - int vflip; -}; - -enum stk_status { - S_PRESENT = 1, - S_INITIALISED = 2, - S_MEMALLOCD = 4, - S_STREAMING = 8, -}; -#define is_present(dev) ((dev)->status & S_PRESENT) -#define is_initialised(dev) ((dev)->status & S_INITIALISED) -#define is_streaming(dev) ((dev)->status & S_STREAMING) -#define is_memallocd(dev) ((dev)->status & S_MEMALLOCD) -#define set_present(dev) ((dev)->status = S_PRESENT) -#define unset_present(dev) ((dev)->status &= \ - ~(S_PRESENT|S_INITIALISED|S_STREAMING)) -#define set_initialised(dev) ((dev)->status |= S_INITIALISED) -#define unset_initialised(dev) ((dev)->status &= ~S_INITIALISED) -#define set_memallocd(dev) ((dev)->status |= S_MEMALLOCD) -#define unset_memallocd(dev) ((dev)->status &= ~S_MEMALLOCD) -#define set_streaming(dev) ((dev)->status |= S_STREAMING) -#define unset_streaming(dev) ((dev)->status &= ~S_STREAMING) - -struct regval { - unsigned reg; - unsigned val; -}; - -struct stk_camera { - struct video_device vdev; - struct usb_device *udev; - struct usb_interface *interface; - int webcam_model; - struct file *owner; - - u8 isoc_ep; - - /* Not sure if this is right */ - atomic_t urbs_used; - - struct stk_video vsettings; - - enum stk_status status; - - spinlock_t spinlock; - wait_queue_head_t wait_frame; - - struct stk_iso_buf *isobufs; - - int frame_size; - /* Streaming buffers */ - unsigned int n_sbufs; - struct stk_sio_buffer *sio_bufs; - struct list_head sio_avail; - struct list_head sio_full; - unsigned sequence; -}; - -#define vdev_to_camera(d) container_of(d, struct stk_camera, vdev) - -int stk_camera_write_reg(struct stk_camera *, u16, u8); -int stk_camera_read_reg(struct stk_camera *, u16, int *); - -int stk_sensor_init(struct stk_camera *); -int stk_sensor_configure(struct stk_camera *); -int stk_sensor_sleep(struct stk_camera *dev); -int stk_sensor_wakeup(struct stk_camera *dev); -int stk_sensor_set_brightness(struct stk_camera *dev, int br); - -#endif diff --git a/drivers/media/video/zr364xx.c b/drivers/media/video/zr364xx.c deleted file mode 100644 index 9afab35878b4..000000000000 --- a/drivers/media/video/zr364xx.c +++ /dev/null @@ -1,1643 +0,0 @@ -/* - * Zoran 364xx based USB webcam module version 0.73 - * - * Allows you to use your USB webcam with V4L2 applications - * This is still in heavy developpement ! - * - * Copyright (C) 2004 Antoine Jacquet - * http://royale.zerezo.com/zr364xx/ - * - * Heavily inspired by usb-skeleton.c, vicam.c, cpia.c and spca50x.c drivers - * V4L2 version inspired by meye.c driver - * - * Some video buffer code by Lamarque based on s2255drv.c and vivi.c drivers. - * - * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - - -/* Version Information */ -#define DRIVER_VERSION "0.7.4" -#define DRIVER_AUTHOR "Antoine Jacquet, http://royale.zerezo.com/" -#define DRIVER_DESC "Zoran 364xx" - - -/* Camera */ -#define FRAMES 1 -#define MAX_FRAME_SIZE 200000 -#define BUFFER_SIZE 0x1000 -#define CTRL_TIMEOUT 500 - -#define ZR364XX_DEF_BUFS 4 -#define ZR364XX_READ_IDLE 0 -#define ZR364XX_READ_FRAME 1 - -/* Debug macro */ -#define DBG(fmt, args...) \ - do { \ - if (debug) { \ - printk(KERN_INFO KBUILD_MODNAME " " fmt, ##args); \ - } \ - } while (0) - -/*#define FULL_DEBUG 1*/ -#ifdef FULL_DEBUG -#define _DBG DBG -#else -#define _DBG(fmt, args...) -#endif - -/* Init methods, need to find nicer names for these - * the exact names of the chipsets would be the best if someone finds it */ -#define METHOD0 0 -#define METHOD1 1 -#define METHOD2 2 -#define METHOD3 3 - - -/* Module parameters */ -static int debug; -static int mode; - - -/* Module parameters interface */ -module_param(debug, int, 0644); -MODULE_PARM_DESC(debug, "Debug level"); -module_param(mode, int, 0644); -MODULE_PARM_DESC(mode, "0 = 320x240, 1 = 160x120, 2 = 640x480"); - - -/* Devices supported by this driver - * .driver_info contains the init method used by the camera */ -static struct usb_device_id device_table[] = { - {USB_DEVICE(0x08ca, 0x0109), .driver_info = METHOD0 }, - {USB_DEVICE(0x041e, 0x4024), .driver_info = METHOD0 }, - {USB_DEVICE(0x0d64, 0x0108), .driver_info = METHOD0 }, - {USB_DEVICE(0x0546, 0x3187), .driver_info = METHOD0 }, - {USB_DEVICE(0x0d64, 0x3108), .driver_info = METHOD0 }, - {USB_DEVICE(0x0595, 0x4343), .driver_info = METHOD0 }, - {USB_DEVICE(0x0bb0, 0x500d), .driver_info = METHOD0 }, - {USB_DEVICE(0x0feb, 0x2004), .driver_info = METHOD0 }, - {USB_DEVICE(0x055f, 0xb500), .driver_info = METHOD0 }, - {USB_DEVICE(0x08ca, 0x2062), .driver_info = METHOD2 }, - {USB_DEVICE(0x052b, 0x1a18), .driver_info = METHOD1 }, - {USB_DEVICE(0x04c8, 0x0729), .driver_info = METHOD0 }, - {USB_DEVICE(0x04f2, 0xa208), .driver_info = METHOD0 }, - {USB_DEVICE(0x0784, 0x0040), .driver_info = METHOD1 }, - {USB_DEVICE(0x06d6, 0x0034), .driver_info = METHOD0 }, - {USB_DEVICE(0x0a17, 0x0062), .driver_info = METHOD2 }, - {USB_DEVICE(0x06d6, 0x003b), .driver_info = METHOD0 }, - {USB_DEVICE(0x0a17, 0x004e), .driver_info = METHOD2 }, - {USB_DEVICE(0x041e, 0x405d), .driver_info = METHOD2 }, - {USB_DEVICE(0x08ca, 0x2102), .driver_info = METHOD3 }, - {USB_DEVICE(0x06d6, 0x003d), .driver_info = METHOD0 }, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -/* frame structure */ -struct zr364xx_framei { - unsigned long ulState; /* ulState:ZR364XX_READ_IDLE, - ZR364XX_READ_FRAME */ - void *lpvbits; /* image data */ - unsigned long cur_size; /* current data copied to it */ -}; - -/* image buffer structure */ -struct zr364xx_bufferi { - unsigned long dwFrames; /* number of frames in buffer */ - struct zr364xx_framei frame[FRAMES]; /* array of FRAME structures */ -}; - -struct zr364xx_dmaqueue { - struct list_head active; - struct zr364xx_camera *cam; -}; - -struct zr364xx_pipeinfo { - u32 transfer_size; - u8 *transfer_buffer; - u32 state; - void *stream_urb; - void *cam; /* back pointer to zr364xx_camera struct */ - u32 err_count; - u32 idx; -}; - -struct zr364xx_fmt { - char *name; - u32 fourcc; - int depth; -}; - -/* image formats. */ -static const struct zr364xx_fmt formats[] = { - { - .name = "JPG", - .fourcc = V4L2_PIX_FMT_JPEG, - .depth = 24 - } -}; - -/* Camera stuff */ -struct zr364xx_camera { - struct usb_device *udev; /* save off the usb device pointer */ - struct usb_interface *interface;/* the interface for this device */ - struct v4l2_device v4l2_dev; - struct v4l2_ctrl_handler ctrl_handler; - struct video_device vdev; /* v4l video device */ - struct v4l2_fh *owner; /* owns the streaming */ - int nb; - struct zr364xx_bufferi buffer; - int skip; - int width; - int height; - int method; - struct mutex lock; - - spinlock_t slock; - struct zr364xx_dmaqueue vidq; - int last_frame; - int cur_frame; - unsigned long frame_count; - int b_acquire; - struct zr364xx_pipeinfo pipe[1]; - - u8 read_endpoint; - - const struct zr364xx_fmt *fmt; - struct videobuf_queue vb_vidq; - bool was_streaming; -}; - -/* buffer for one video frame */ -struct zr364xx_buffer { - /* common v4l buffer stuff -- must be first */ - struct videobuf_buffer vb; - const struct zr364xx_fmt *fmt; -}; - -/* function used to send initialisation commands to the camera */ -static int send_control_msg(struct usb_device *udev, u8 request, u16 value, - u16 index, unsigned char *cp, u16 size) -{ - int status; - - unsigned char *transfer_buffer = kmalloc(size, GFP_KERNEL); - if (!transfer_buffer) { - dev_err(&udev->dev, "kmalloc(%d) failed\n", size); - return -ENOMEM; - } - - memcpy(transfer_buffer, cp, size); - - status = usb_control_msg(udev, - usb_sndctrlpipe(udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, - transfer_buffer, size, CTRL_TIMEOUT); - - kfree(transfer_buffer); - return status; -} - - -/* Control messages sent to the camera to initialize it - * and launch the capture */ -typedef struct { - unsigned int value; - unsigned int size; - unsigned char *bytes; -} message; - -/* method 0 */ -static unsigned char m0d1[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; -static unsigned char m0d2[] = { 0, 0, 0, 0, 0, 0 }; -static unsigned char m0d3[] = { 0, 0 }; -static message m0[] = { - {0x1f30, 0, NULL}, - {0xd000, 0, NULL}, - {0x3370, sizeof(m0d1), m0d1}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2610, sizeof(m0d2), m0d2}, - {0xe107, 0, NULL}, - {0x2502, 0, NULL}, - {0x1f70, 0, NULL}, - {0xd000, 0, NULL}, - {0x9a01, sizeof(m0d3), m0d3}, - {-1, -1, NULL} -}; - -/* method 1 */ -static unsigned char m1d1[] = { 0xff, 0xff }; -static unsigned char m1d2[] = { 0x00, 0x00 }; -static message m1[] = { - {0x1f30, 0, NULL}, - {0xd000, 0, NULL}, - {0xf000, 0, NULL}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2650, 0, NULL}, - {0xe107, 0, NULL}, - {0x2502, sizeof(m1d1), m1d1}, - {0x1f70, 0, NULL}, - {0xd000, 0, NULL}, - {0xd000, 0, NULL}, - {0xd000, 0, NULL}, - {0x9a01, sizeof(m1d2), m1d2}, - {-1, -1, NULL} -}; - -/* method 2 */ -static unsigned char m2d1[] = { 0xff, 0xff }; -static message m2[] = { - {0x1f30, 0, NULL}, - {0xf000, 0, NULL}, - {0x2000, 0, NULL}, - {0x2f0f, 0, NULL}, - {0x2650, 0, NULL}, - {0xe107, 0, NULL}, - {0x2502, sizeof(m2d1), m2d1}, - {0x1f70, 0, NULL}, - {-1, -1, NULL} -}; - -/* init table */ -static message *init[4] = { m0, m1, m2, m2 }; - - -/* JPEG static data in header (Huffman table, etc) */ -static unsigned char header1[] = { - 0xFF, 0xD8, - /* - 0xFF, 0xE0, 0x00, 0x10, 'J', 'F', 'I', 'F', - 0x00, 0x01, 0x01, 0x00, 0x33, 0x8A, 0x00, 0x00, 0x33, 0x88, - */ - 0xFF, 0xDB, 0x00, 0x84 -}; -static unsigned char header2[] = { - 0xFF, 0xC4, 0x00, 0x1F, 0x00, 0x00, 0x01, 0x05, 0x01, 0x01, 0x01, - 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, - 0xFF, 0xC4, 0x00, 0xB5, 0x10, 0x00, 0x02, 0x01, 0x03, 0x03, 0x02, - 0x04, 0x03, 0x05, 0x05, 0x04, 0x04, 0x00, 0x00, 0x01, 0x7D, 0x01, - 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, - 0x13, 0x51, 0x61, 0x07, 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, - 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, 0x24, 0x33, - 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, - 0x26, 0x27, 0x28, 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, - 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, - 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, - 0x68, 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, - 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, - 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, - 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, - 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, - 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, - 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, - 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFF, 0xC4, 0x00, 0x1F, - 0x01, 0x00, 0x03, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, - 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0xFF, 0xC4, 0x00, 0xB5, - 0x11, 0x00, 0x02, 0x01, 0x02, 0x04, 0x04, 0x03, 0x04, 0x07, 0x05, - 0x04, 0x04, 0x00, 0x01, 0x02, 0x77, 0x00, 0x01, 0x02, 0x03, 0x11, - 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, - 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, - 0x09, 0x23, 0x33, 0x52, 0xF0, 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, - 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, 0x27, - 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, - 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, - 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, - 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, - 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, - 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, - 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, - 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, - 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE2, 0xE3, 0xE4, 0xE5, - 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, - 0xF8, 0xF9, 0xFA, 0xFF, 0xC0, 0x00, 0x11, 0x08, 0x00, 0xF0, 0x01, - 0x40, 0x03, 0x01, 0x21, 0x00, 0x02, 0x11, 0x01, 0x03, 0x11, 0x01, - 0xFF, 0xDA, 0x00, 0x0C, 0x03, 0x01, 0x00, 0x02, 0x11, 0x03, 0x11, - 0x00, 0x3F, 0x00 -}; -static unsigned char header3; - -/* ------------------------------------------------------------------ - Videobuf operations - ------------------------------------------------------------------*/ - -static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, - unsigned int *size) -{ - struct zr364xx_camera *cam = vq->priv_data; - - *size = cam->width * cam->height * (cam->fmt->depth >> 3); - - if (*count == 0) - *count = ZR364XX_DEF_BUFS; - - if (*size * *count > ZR364XX_DEF_BUFS * 1024 * 1024) - *count = (ZR364XX_DEF_BUFS * 1024 * 1024) / *size; - - return 0; -} - -static void free_buffer(struct videobuf_queue *vq, struct zr364xx_buffer *buf) -{ - _DBG("%s\n", __func__); - - if (in_interrupt()) - BUG(); - - videobuf_vmalloc_free(&buf->vb); - buf->vb.state = VIDEOBUF_NEEDS_INIT; -} - -static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, - enum v4l2_field field) -{ - struct zr364xx_camera *cam = vq->priv_data; - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - int rc; - - DBG("%s, field=%d, fmt name = %s\n", __func__, field, cam->fmt != NULL ? - cam->fmt->name : ""); - if (cam->fmt == NULL) - return -EINVAL; - - buf->vb.size = cam->width * cam->height * (cam->fmt->depth >> 3); - - if (buf->vb.baddr != 0 && buf->vb.bsize < buf->vb.size) { - DBG("invalid buffer prepare\n"); - return -EINVAL; - } - - buf->fmt = cam->fmt; - buf->vb.width = cam->width; - buf->vb.height = cam->height; - buf->vb.field = field; - - if (buf->vb.state == VIDEOBUF_NEEDS_INIT) { - rc = videobuf_iolock(vq, &buf->vb, NULL); - if (rc < 0) - goto fail; - } - - buf->vb.state = VIDEOBUF_PREPARED; - return 0; -fail: - free_buffer(vq, buf); - return rc; -} - -static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) -{ - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - struct zr364xx_camera *cam = vq->priv_data; - - _DBG("%s\n", __func__); - - buf->vb.state = VIDEOBUF_QUEUED; - list_add_tail(&buf->vb.queue, &cam->vidq.active); -} - -static void buffer_release(struct videobuf_queue *vq, - struct videobuf_buffer *vb) -{ - struct zr364xx_buffer *buf = container_of(vb, struct zr364xx_buffer, - vb); - - _DBG("%s\n", __func__); - free_buffer(vq, buf); -} - -static struct videobuf_queue_ops zr364xx_video_qops = { - .buf_setup = buffer_setup, - .buf_prepare = buffer_prepare, - .buf_queue = buffer_queue, - .buf_release = buffer_release, -}; - -/********************/ -/* V4L2 integration */ -/********************/ -static int zr364xx_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type); - -static ssize_t zr364xx_read(struct file *file, char __user *buf, size_t count, - loff_t * ppos) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int err = 0; - - _DBG("%s\n", __func__); - - if (!buf) - return -EINVAL; - - if (!count) - return -EINVAL; - - if (mutex_lock_interruptible(&cam->lock)) - return -ERESTARTSYS; - - err = zr364xx_vidioc_streamon(file, file->private_data, - V4L2_BUF_TYPE_VIDEO_CAPTURE); - if (err == 0) { - DBG("%s: reading %d bytes at pos %d.\n", __func__, - (int) count, (int) *ppos); - - /* NoMan Sux ! */ - err = videobuf_read_one(&cam->vb_vidq, buf, count, ppos, - file->f_flags & O_NONBLOCK); - } - mutex_unlock(&cam->lock); - return err; -} - -/* video buffer vmalloc implementation based partly on VIVI driver which is - * Copyright (c) 2006 by - * Mauro Carvalho Chehab - * Ted Walther - * John Sokol - * http://v4l.videotechnology.com/ - * - */ -static void zr364xx_fillbuff(struct zr364xx_camera *cam, - struct zr364xx_buffer *buf, - int jpgsize) -{ - int pos = 0; - struct timeval ts; - const char *tmpbuf; - char *vbuf = videobuf_to_vmalloc(&buf->vb); - unsigned long last_frame; - - if (!vbuf) - return; - - last_frame = cam->last_frame; - if (last_frame != -1) { - tmpbuf = (const char *)cam->buffer.frame[last_frame].lpvbits; - switch (buf->fmt->fourcc) { - case V4L2_PIX_FMT_JPEG: - buf->vb.size = jpgsize; - memcpy(vbuf, tmpbuf, buf->vb.size); - break; - default: - printk(KERN_DEBUG KBUILD_MODNAME ": unknown format?\n"); - } - cam->last_frame = -1; - } else { - printk(KERN_ERR KBUILD_MODNAME ": =======no frame\n"); - return; - } - DBG("%s: Buffer 0x%08lx size= %d\n", __func__, - (unsigned long)vbuf, pos); - /* tell v4l buffer was filled */ - - buf->vb.field_count = cam->frame_count * 2; - do_gettimeofday(&ts); - buf->vb.ts = ts; - buf->vb.state = VIDEOBUF_DONE; -} - -static int zr364xx_got_frame(struct zr364xx_camera *cam, int jpgsize) -{ - struct zr364xx_dmaqueue *dma_q = &cam->vidq; - struct zr364xx_buffer *buf; - unsigned long flags = 0; - int rc = 0; - - DBG("wakeup: %p\n", &dma_q); - spin_lock_irqsave(&cam->slock, flags); - - if (list_empty(&dma_q->active)) { - DBG("No active queue to serve\n"); - rc = -1; - goto unlock; - } - buf = list_entry(dma_q->active.next, - struct zr364xx_buffer, vb.queue); - - if (!waitqueue_active(&buf->vb.done)) { - /* no one active */ - rc = -1; - goto unlock; - } - list_del(&buf->vb.queue); - do_gettimeofday(&buf->vb.ts); - DBG("[%p/%d] wakeup\n", buf, buf->vb.i); - zr364xx_fillbuff(cam, buf, jpgsize); - wake_up(&buf->vb.done); - DBG("wakeup [buf/i] [%p/%d]\n", buf, buf->vb.i); -unlock: - spin_unlock_irqrestore(&cam->slock, flags); - return rc; -} - -/* this function moves the usb stream read pipe data - * into the system buffers. - * returns 0 on success, EAGAIN if more data to process (call this - * function again). - */ -static int zr364xx_read_video_callback(struct zr364xx_camera *cam, - struct zr364xx_pipeinfo *pipe_info, - struct urb *purb) -{ - unsigned char *pdest; - unsigned char *psrc; - s32 idx = -1; - struct zr364xx_framei *frm; - int i = 0; - unsigned char *ptr = NULL; - - _DBG("buffer to user\n"); - idx = cam->cur_frame; - frm = &cam->buffer.frame[idx]; - - /* swap bytes if camera needs it */ - if (cam->method == METHOD0) { - u16 *buf = (u16 *)pipe_info->transfer_buffer; - for (i = 0; i < purb->actual_length/2; i++) - swab16s(buf + i); - } - - /* search done. now find out if should be acquiring */ - if (!cam->b_acquire) { - /* we found a frame, but this channel is turned off */ - frm->ulState = ZR364XX_READ_IDLE; - return -EINVAL; - } - - psrc = (u8 *)pipe_info->transfer_buffer; - ptr = pdest = frm->lpvbits; - - if (frm->ulState == ZR364XX_READ_IDLE) { - frm->ulState = ZR364XX_READ_FRAME; - frm->cur_size = 0; - - _DBG("jpeg header, "); - memcpy(ptr, header1, sizeof(header1)); - ptr += sizeof(header1); - header3 = 0; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, psrc, 64); - ptr += 64; - header3 = 1; - memcpy(ptr, &header3, 1); - ptr++; - memcpy(ptr, psrc + 64, 64); - ptr += 64; - memcpy(ptr, header2, sizeof(header2)); - ptr += sizeof(header2); - memcpy(ptr, psrc + 128, - purb->actual_length - 128); - ptr += purb->actual_length - 128; - _DBG("header : %d %d %d %d %d %d %d %d %d\n", - psrc[0], psrc[1], psrc[2], - psrc[3], psrc[4], psrc[5], - psrc[6], psrc[7], psrc[8]); - frm->cur_size = ptr - pdest; - } else { - if (frm->cur_size + purb->actual_length > MAX_FRAME_SIZE) { - dev_info(&cam->udev->dev, - "%s: buffer (%d bytes) too small to hold " - "frame data. Discarding frame data.\n", - __func__, MAX_FRAME_SIZE); - } else { - pdest += frm->cur_size; - memcpy(pdest, psrc, purb->actual_length); - frm->cur_size += purb->actual_length; - } - } - /*_DBG("cur_size %lu urb size %d\n", frm->cur_size, - purb->actual_length);*/ - - if (purb->actual_length < pipe_info->transfer_size) { - _DBG("****************Buffer[%d]full*************\n", idx); - cam->last_frame = cam->cur_frame; - cam->cur_frame++; - /* end of system frame ring buffer, start at zero */ - if (cam->cur_frame == cam->buffer.dwFrames) - cam->cur_frame = 0; - - /* frame ready */ - /* go back to find the JPEG EOI marker */ - ptr = pdest = frm->lpvbits; - ptr += frm->cur_size - 2; - while (ptr > pdest) { - if (*ptr == 0xFF && *(ptr + 1) == 0xD9 - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr == pdest) - DBG("No EOI marker\n"); - - /* Sometimes there is junk data in the middle of the picture, - * we want to skip this bogus frames */ - while (ptr > pdest) { - if (*ptr == 0xFF && *(ptr + 1) == 0xFF - && *(ptr + 2) == 0xFF) - break; - ptr--; - } - if (ptr != pdest) { - DBG("Bogus frame ? %d\n", ++(cam->nb)); - } else if (cam->b_acquire) { - /* we skip the 2 first frames which are usually buggy */ - if (cam->skip) - cam->skip--; - else { - _DBG("jpeg(%lu): %d %d %d %d %d %d %d %d\n", - frm->cur_size, - pdest[0], pdest[1], pdest[2], pdest[3], - pdest[4], pdest[5], pdest[6], pdest[7]); - - zr364xx_got_frame(cam, frm->cur_size); - } - } - cam->frame_count++; - frm->ulState = ZR364XX_READ_IDLE; - frm->cur_size = 0; - } - /* done successfully */ - return 0; -} - -static int zr364xx_vidioc_querycap(struct file *file, void *priv, - struct v4l2_capability *cap) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - strlcpy(cap->driver, DRIVER_DESC, sizeof(cap->driver)); - strlcpy(cap->card, cam->udev->product, sizeof(cap->card)); - strlcpy(cap->bus_info, dev_name(&cam->udev->dev), - sizeof(cap->bus_info)); - cap->device_caps = V4L2_CAP_VIDEO_CAPTURE | - V4L2_CAP_READWRITE | - V4L2_CAP_STREAMING; - cap->capabilities = cap->device_caps | V4L2_CAP_DEVICE_CAPS; - - return 0; -} - -static int zr364xx_vidioc_enum_input(struct file *file, void *priv, - struct v4l2_input *i) -{ - if (i->index != 0) - return -EINVAL; - strcpy(i->name, DRIVER_DESC " Camera"); - i->type = V4L2_INPUT_TYPE_CAMERA; - return 0; -} - -static int zr364xx_vidioc_g_input(struct file *file, void *priv, - unsigned int *i) -{ - *i = 0; - return 0; -} - -static int zr364xx_vidioc_s_input(struct file *file, void *priv, - unsigned int i) -{ - if (i != 0) - return -EINVAL; - return 0; -} - -static int zr364xx_s_ctrl(struct v4l2_ctrl *ctrl) -{ - struct zr364xx_camera *cam = - container_of(ctrl->handler, struct zr364xx_camera, ctrl_handler); - int temp; - - switch (ctrl->id) { - case V4L2_CID_BRIGHTNESS: - /* hardware brightness */ - send_control_msg(cam->udev, 1, 0x2001, 0, NULL, 0); - temp = (0x60 << 8) + 127 - ctrl->val; - send_control_msg(cam->udev, 1, temp, 0, NULL, 0); - break; - default: - return -EINVAL; - } - - return 0; -} - -static int zr364xx_vidioc_enum_fmt_vid_cap(struct file *file, - void *priv, struct v4l2_fmtdesc *f) -{ - if (f->index > 0) - return -EINVAL; - f->flags = V4L2_FMT_FLAG_COMPRESSED; - strcpy(f->description, formats[0].name); - f->pixelformat = formats[0].fourcc; - return 0; -} - -static char *decode_fourcc(__u32 pixelformat, char *buf) -{ - buf[0] = pixelformat & 0xff; - buf[1] = (pixelformat >> 8) & 0xff; - buf[2] = (pixelformat >> 16) & 0xff; - buf[3] = (pixelformat >> 24) & 0xff; - buf[4] = '\0'; - return buf; -} - -static int zr364xx_vidioc_try_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam = video_drvdata(file); - char pixelformat_name[5]; - - if (cam == NULL) - return -ENODEV; - - if (f->fmt.pix.pixelformat != V4L2_PIX_FMT_JPEG) { - DBG("%s: unsupported pixelformat V4L2_PIX_FMT_%s\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name)); - return -EINVAL; - } - - if (!(f->fmt.pix.width == 160 && f->fmt.pix.height == 120) && - !(f->fmt.pix.width == 640 && f->fmt.pix.height == 480)) { - f->fmt.pix.width = 320; - f->fmt.pix.height = 240; - } - - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), - f->fmt.pix.field); - return 0; -} - -static int zr364xx_vidioc_g_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam; - - if (file == NULL) - return -ENODEV; - cam = video_drvdata(file); - - f->fmt.pix.pixelformat = formats[0].fourcc; - f->fmt.pix.field = V4L2_FIELD_NONE; - f->fmt.pix.width = cam->width; - f->fmt.pix.height = cam->height; - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - return 0; -} - -static int zr364xx_vidioc_s_fmt_vid_cap(struct file *file, void *priv, - struct v4l2_format *f) -{ - struct zr364xx_camera *cam = video_drvdata(file); - struct videobuf_queue *q = &cam->vb_vidq; - char pixelformat_name[5]; - int ret = zr364xx_vidioc_try_fmt_vid_cap(file, cam, f); - int i; - - if (ret < 0) - return ret; - - mutex_lock(&q->vb_lock); - - if (videobuf_queue_is_busy(&cam->vb_vidq)) { - DBG("%s queue busy\n", __func__); - ret = -EBUSY; - goto out; - } - - if (cam->owner) { - DBG("%s can't change format after started\n", __func__); - ret = -EBUSY; - goto out; - } - - cam->width = f->fmt.pix.width; - cam->height = f->fmt.pix.height; - DBG("%s: %dx%d mode selected\n", __func__, - cam->width, cam->height); - f->fmt.pix.bytesperline = f->fmt.pix.width * 2; - f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline; - f->fmt.pix.colorspace = V4L2_COLORSPACE_JPEG; - f->fmt.pix.priv = 0; - cam->vb_vidq.field = f->fmt.pix.field; - - if (f->fmt.pix.width == 160 && f->fmt.pix.height == 120) - mode = 1; - else if (f->fmt.pix.width == 640 && f->fmt.pix.height == 480) - mode = 2; - else - mode = 0; - - m0d1[0] = mode; - m1[2].value = 0xf000 + mode; - m2[1].value = 0xf000 + mode; - - /* special case for METHOD3, the modes are different */ - if (cam->method == METHOD3) { - switch (mode) { - case 1: - m2[1].value = 0xf000 + 4; - break; - case 2: - m2[1].value = 0xf000 + 0; - break; - default: - m2[1].value = 0xf000 + 1; - break; - } - } - - header2[437] = cam->height / 256; - header2[438] = cam->height % 256; - header2[439] = cam->width / 256; - header2[440] = cam->width % 256; - - for (i = 0; init[cam->method][i].size != -1; i++) { - ret = - send_control_msg(cam->udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (ret < 0) { - dev_err(&cam->udev->dev, - "error during resolution change sequence: %d\n", i); - goto out; - } - } - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - cam->skip = 2; - ret = 0; - -out: - mutex_unlock(&q->vb_lock); - - DBG("%s: V4L2_PIX_FMT_%s (%d) ok!\n", __func__, - decode_fourcc(f->fmt.pix.pixelformat, pixelformat_name), - f->fmt.pix.field); - return ret; -} - -static int zr364xx_vidioc_reqbufs(struct file *file, void *priv, - struct v4l2_requestbuffers *p) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - if (cam->owner && cam->owner != priv) - return -EBUSY; - return videobuf_reqbufs(&cam->vb_vidq, p); -} - -static int zr364xx_vidioc_querybuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - rc = videobuf_querybuf(&cam->vb_vidq, p); - return rc; -} - -static int zr364xx_vidioc_qbuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - _DBG("%s\n", __func__); - if (cam->owner && cam->owner != priv) - return -EBUSY; - rc = videobuf_qbuf(&cam->vb_vidq, p); - return rc; -} - -static int zr364xx_vidioc_dqbuf(struct file *file, - void *priv, - struct v4l2_buffer *p) -{ - int rc; - struct zr364xx_camera *cam = video_drvdata(file); - _DBG("%s\n", __func__); - if (cam->owner && cam->owner != priv) - return -EBUSY; - rc = videobuf_dqbuf(&cam->vb_vidq, p, file->f_flags & O_NONBLOCK); - return rc; -} - -static void read_pipe_completion(struct urb *purb) -{ - struct zr364xx_pipeinfo *pipe_info; - struct zr364xx_camera *cam; - int pipe; - - pipe_info = purb->context; - _DBG("%s %p, status %d\n", __func__, purb, purb->status); - if (pipe_info == NULL) { - printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); - return; - } - - cam = pipe_info->cam; - if (cam == NULL) { - printk(KERN_ERR KBUILD_MODNAME ": no context!\n"); - return; - } - - /* if shutting down, do not resubmit, exit immediately */ - if (purb->status == -ESHUTDOWN) { - DBG("%s, err shutdown\n", __func__); - pipe_info->err_count++; - return; - } - - if (pipe_info->state == 0) { - DBG("exiting USB pipe\n"); - return; - } - - if (purb->actual_length < 0 || - purb->actual_length > pipe_info->transfer_size) { - dev_err(&cam->udev->dev, "wrong number of bytes\n"); - return; - } - - if (purb->status == 0) - zr364xx_read_video_callback(cam, pipe_info, purb); - else { - pipe_info->err_count++; - DBG("%s: failed URB %d\n", __func__, purb->status); - } - - pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); - - /* reuse urb */ - usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->transfer_size, - read_pipe_completion, pipe_info); - - if (pipe_info->state != 0) { - purb->status = usb_submit_urb(pipe_info->stream_urb, - GFP_ATOMIC); - - if (purb->status) - dev_err(&cam->udev->dev, - "error submitting urb (error=%i)\n", - purb->status); - } else - DBG("read pipe complete state 0\n"); -} - -static int zr364xx_start_readpipe(struct zr364xx_camera *cam) -{ - int pipe; - int retval; - struct zr364xx_pipeinfo *pipe_info = cam->pipe; - pipe = usb_rcvbulkpipe(cam->udev, cam->read_endpoint); - DBG("%s: start pipe IN x%x\n", __func__, cam->read_endpoint); - - pipe_info->state = 1; - pipe_info->err_count = 0; - pipe_info->stream_urb = usb_alloc_urb(0, GFP_KERNEL); - if (!pipe_info->stream_urb) { - dev_err(&cam->udev->dev, "ReadStream: Unable to alloc URB\n"); - return -ENOMEM; - } - /* transfer buffer allocated in board_init */ - usb_fill_bulk_urb(pipe_info->stream_urb, cam->udev, - pipe, - pipe_info->transfer_buffer, - pipe_info->transfer_size, - read_pipe_completion, pipe_info); - - DBG("submitting URB %p\n", pipe_info->stream_urb); - retval = usb_submit_urb(pipe_info->stream_urb, GFP_KERNEL); - if (retval) { - printk(KERN_ERR KBUILD_MODNAME ": start read pipe failed\n"); - return retval; - } - - return 0; -} - -static void zr364xx_stop_readpipe(struct zr364xx_camera *cam) -{ - struct zr364xx_pipeinfo *pipe_info; - - if (cam == NULL) { - printk(KERN_ERR KBUILD_MODNAME ": invalid device\n"); - return; - } - DBG("stop read pipe\n"); - pipe_info = cam->pipe; - if (pipe_info) { - if (pipe_info->state != 0) - pipe_info->state = 0; - - if (pipe_info->stream_urb) { - /* cancel urb */ - usb_kill_urb(pipe_info->stream_urb); - usb_free_urb(pipe_info->stream_urb); - pipe_info->stream_urb = NULL; - } - } - return; -} - -/* starts acquisition process */ -static int zr364xx_start_acquire(struct zr364xx_camera *cam) -{ - int j; - - DBG("start acquire\n"); - - cam->last_frame = -1; - cam->cur_frame = 0; - for (j = 0; j < FRAMES; j++) { - cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[j].cur_size = 0; - } - cam->b_acquire = 1; - return 0; -} - -static inline int zr364xx_stop_acquire(struct zr364xx_camera *cam) -{ - cam->b_acquire = 0; - return 0; -} - -static int zr364xx_prepare(struct zr364xx_camera *cam) -{ - int res; - int i, j; - - for (i = 0; init[cam->method][i].size != -1; i++) { - res = send_control_msg(cam->udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - if (res < 0) { - dev_err(&cam->udev->dev, - "error during open sequence: %d\n", i); - return res; - } - } - - cam->skip = 2; - cam->last_frame = -1; - cam->cur_frame = 0; - cam->frame_count = 0; - for (j = 0; j < FRAMES; j++) { - cam->buffer.frame[j].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[j].cur_size = 0; - } - v4l2_ctrl_handler_setup(&cam->ctrl_handler); - return 0; -} - -static int zr364xx_vidioc_streamon(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int res; - - DBG("%s\n", __func__); - - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - - if (cam->owner && cam->owner != priv) - return -EBUSY; - - res = zr364xx_prepare(cam); - if (res) - return res; - res = videobuf_streamon(&cam->vb_vidq); - if (res == 0) { - zr364xx_start_acquire(cam); - cam->owner = file->private_data; - } - return res; -} - -static int zr364xx_vidioc_streamoff(struct file *file, void *priv, - enum v4l2_buf_type type) -{ - struct zr364xx_camera *cam = video_drvdata(file); - - DBG("%s\n", __func__); - if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) - return -EINVAL; - if (cam->owner && cam->owner != priv) - return -EBUSY; - zr364xx_stop_acquire(cam); - return videobuf_streamoff(&cam->vb_vidq); -} - - -/* open the camera */ -static int zr364xx_open(struct file *file) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int err; - - DBG("%s\n", __func__); - - if (mutex_lock_interruptible(&cam->lock)) - return -ERESTARTSYS; - - err = v4l2_fh_open(file); - if (err) - goto out; - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - err = 0; - -out: - mutex_unlock(&cam->lock); - DBG("%s: %d\n", __func__, err); - return err; -} - -static void zr364xx_release(struct v4l2_device *v4l2_dev) -{ - struct zr364xx_camera *cam = - container_of(v4l2_dev, struct zr364xx_camera, v4l2_dev); - unsigned long i; - - v4l2_device_unregister(&cam->v4l2_dev); - - videobuf_mmap_free(&cam->vb_vidq); - - /* release sys buffers */ - for (i = 0; i < FRAMES; i++) { - if (cam->buffer.frame[i].lpvbits) { - DBG("vfree %p\n", cam->buffer.frame[i].lpvbits); - vfree(cam->buffer.frame[i].lpvbits); - } - cam->buffer.frame[i].lpvbits = NULL; - } - - v4l2_ctrl_handler_free(&cam->ctrl_handler); - /* release transfer buffer */ - kfree(cam->pipe->transfer_buffer); - kfree(cam); -} - -/* release the camera */ -static int zr364xx_close(struct file *file) -{ - struct zr364xx_camera *cam; - struct usb_device *udev; - int i; - - DBG("%s\n", __func__); - cam = video_drvdata(file); - - mutex_lock(&cam->lock); - udev = cam->udev; - - if (file->private_data == cam->owner) { - /* turn off stream */ - if (cam->b_acquire) - zr364xx_stop_acquire(cam); - videobuf_streamoff(&cam->vb_vidq); - - for (i = 0; i < 2; i++) { - send_control_msg(udev, 1, init[cam->method][i].value, - 0, init[cam->method][i].bytes, - init[cam->method][i].size); - } - cam->owner = NULL; - } - - /* Added some delay here, since opening/closing the camera quickly, - * like Ekiga does during its startup, can crash the webcam - */ - mdelay(100); - mutex_unlock(&cam->lock); - return v4l2_fh_release(file); -} - - -static int zr364xx_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct zr364xx_camera *cam = video_drvdata(file); - int ret; - - if (cam == NULL) { - DBG("%s: cam == NULL\n", __func__); - return -ENODEV; - } - DBG("mmap called, vma=0x%08lx\n", (unsigned long)vma); - - ret = videobuf_mmap_mapper(&cam->vb_vidq, vma); - - DBG("vma start=0x%08lx, size=%ld, ret=%d\n", - (unsigned long)vma->vm_start, - (unsigned long)vma->vm_end - (unsigned long)vma->vm_start, ret); - return ret; -} - -static unsigned int zr364xx_poll(struct file *file, - struct poll_table_struct *wait) -{ - struct zr364xx_camera *cam = video_drvdata(file); - struct videobuf_queue *q = &cam->vb_vidq; - unsigned res = v4l2_ctrl_poll(file, wait); - - _DBG("%s\n", __func__); - - return res | videobuf_poll_stream(file, q, wait); -} - -static const struct v4l2_ctrl_ops zr364xx_ctrl_ops = { - .s_ctrl = zr364xx_s_ctrl, -}; - -static const struct v4l2_file_operations zr364xx_fops = { - .owner = THIS_MODULE, - .open = zr364xx_open, - .release = zr364xx_close, - .read = zr364xx_read, - .mmap = zr364xx_mmap, - .unlocked_ioctl = video_ioctl2, - .poll = zr364xx_poll, -}; - -static const struct v4l2_ioctl_ops zr364xx_ioctl_ops = { - .vidioc_querycap = zr364xx_vidioc_querycap, - .vidioc_enum_fmt_vid_cap = zr364xx_vidioc_enum_fmt_vid_cap, - .vidioc_try_fmt_vid_cap = zr364xx_vidioc_try_fmt_vid_cap, - .vidioc_s_fmt_vid_cap = zr364xx_vidioc_s_fmt_vid_cap, - .vidioc_g_fmt_vid_cap = zr364xx_vidioc_g_fmt_vid_cap, - .vidioc_enum_input = zr364xx_vidioc_enum_input, - .vidioc_g_input = zr364xx_vidioc_g_input, - .vidioc_s_input = zr364xx_vidioc_s_input, - .vidioc_streamon = zr364xx_vidioc_streamon, - .vidioc_streamoff = zr364xx_vidioc_streamoff, - .vidioc_reqbufs = zr364xx_vidioc_reqbufs, - .vidioc_querybuf = zr364xx_vidioc_querybuf, - .vidioc_qbuf = zr364xx_vidioc_qbuf, - .vidioc_dqbuf = zr364xx_vidioc_dqbuf, - .vidioc_log_status = v4l2_ctrl_log_status, - .vidioc_subscribe_event = v4l2_ctrl_subscribe_event, - .vidioc_unsubscribe_event = v4l2_event_unsubscribe, -}; - -static struct video_device zr364xx_template = { - .name = DRIVER_DESC, - .fops = &zr364xx_fops, - .ioctl_ops = &zr364xx_ioctl_ops, - .release = video_device_release_empty, -}; - - - -/*******************/ -/* USB integration */ -/*******************/ -static int zr364xx_board_init(struct zr364xx_camera *cam) -{ - struct zr364xx_pipeinfo *pipe = cam->pipe; - unsigned long i; - - DBG("board init: %p\n", cam); - memset(pipe, 0, sizeof(*pipe)); - pipe->cam = cam; - pipe->transfer_size = BUFFER_SIZE; - - pipe->transfer_buffer = kzalloc(pipe->transfer_size, - GFP_KERNEL); - if (pipe->transfer_buffer == NULL) { - DBG("out of memory!\n"); - return -ENOMEM; - } - - cam->b_acquire = 0; - cam->frame_count = 0; - - /*** start create system buffers ***/ - for (i = 0; i < FRAMES; i++) { - /* always allocate maximum size for system buffers */ - cam->buffer.frame[i].lpvbits = vmalloc(MAX_FRAME_SIZE); - - DBG("valloc %p, idx %lu, pdata %p\n", - &cam->buffer.frame[i], i, - cam->buffer.frame[i].lpvbits); - if (cam->buffer.frame[i].lpvbits == NULL) { - printk(KERN_INFO KBUILD_MODNAME ": out of memory. " - "Using less frames\n"); - break; - } - } - - if (i == 0) { - printk(KERN_INFO KBUILD_MODNAME ": out of memory. Aborting\n"); - kfree(cam->pipe->transfer_buffer); - cam->pipe->transfer_buffer = NULL; - return -ENOMEM; - } else - cam->buffer.dwFrames = i; - - /* make sure internal states are set */ - for (i = 0; i < FRAMES; i++) { - cam->buffer.frame[i].ulState = ZR364XX_READ_IDLE; - cam->buffer.frame[i].cur_size = 0; - } - - cam->cur_frame = 0; - cam->last_frame = -1; - /*** end create system buffers ***/ - - /* start read pipe */ - zr364xx_start_readpipe(cam); - DBG(": board initialized\n"); - return 0; -} - -static int zr364xx_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *udev = interface_to_usbdev(intf); - struct zr364xx_camera *cam = NULL; - struct usb_host_interface *iface_desc; - struct usb_endpoint_descriptor *endpoint; - struct v4l2_ctrl_handler *hdl; - int err; - int i; - - DBG("probing...\n"); - - dev_info(&intf->dev, DRIVER_DESC " compatible webcam plugged\n"); - dev_info(&intf->dev, "model %04x:%04x detected\n", - le16_to_cpu(udev->descriptor.idVendor), - le16_to_cpu(udev->descriptor.idProduct)); - - cam = kzalloc(sizeof(struct zr364xx_camera), GFP_KERNEL); - if (cam == NULL) { - dev_err(&udev->dev, "cam: out of memory !\n"); - return -ENOMEM; - } - - cam->v4l2_dev.release = zr364xx_release; - err = v4l2_device_register(&intf->dev, &cam->v4l2_dev); - if (err < 0) { - dev_err(&udev->dev, "couldn't register v4l2_device\n"); - kfree(cam); - return err; - } - hdl = &cam->ctrl_handler; - v4l2_ctrl_handler_init(hdl, 1); - v4l2_ctrl_new_std(hdl, &zr364xx_ctrl_ops, - V4L2_CID_BRIGHTNESS, 0, 127, 1, 64); - if (hdl->error) { - err = hdl->error; - dev_err(&udev->dev, "couldn't register control\n"); - goto fail; - } - /* save the init method used by this camera */ - cam->method = id->driver_info; - mutex_init(&cam->lock); - cam->vdev = zr364xx_template; - cam->vdev.lock = &cam->lock; - cam->vdev.v4l2_dev = &cam->v4l2_dev; - cam->vdev.ctrl_handler = &cam->ctrl_handler; - set_bit(V4L2_FL_USE_FH_PRIO, &cam->vdev.flags); - video_set_drvdata(&cam->vdev, cam); - if (debug) - cam->vdev.debug = V4L2_DEBUG_IOCTL | V4L2_DEBUG_IOCTL_ARG; - - cam->udev = udev; - - switch (mode) { - case 1: - dev_info(&udev->dev, "160x120 mode selected\n"); - cam->width = 160; - cam->height = 120; - break; - case 2: - dev_info(&udev->dev, "640x480 mode selected\n"); - cam->width = 640; - cam->height = 480; - break; - default: - dev_info(&udev->dev, "320x240 mode selected\n"); - cam->width = 320; - cam->height = 240; - break; - } - - m0d1[0] = mode; - m1[2].value = 0xf000 + mode; - m2[1].value = 0xf000 + mode; - - /* special case for METHOD3, the modes are different */ - if (cam->method == METHOD3) { - switch (mode) { - case 1: - m2[1].value = 0xf000 + 4; - break; - case 2: - m2[1].value = 0xf000 + 0; - break; - default: - m2[1].value = 0xf000 + 1; - break; - } - } - - header2[437] = cam->height / 256; - header2[438] = cam->height % 256; - header2[439] = cam->width / 256; - header2[440] = cam->width % 256; - - cam->nb = 0; - - DBG("dev: %p, udev %p interface %p\n", cam, cam->udev, intf); - - /* set up the endpoint information */ - iface_desc = intf->cur_altsetting; - DBG("num endpoints %d\n", iface_desc->desc.bNumEndpoints); - for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) { - endpoint = &iface_desc->endpoint[i].desc; - if (!cam->read_endpoint && usb_endpoint_is_bulk_in(endpoint)) { - /* we found the bulk in endpoint */ - cam->read_endpoint = endpoint->bEndpointAddress; - } - } - - if (!cam->read_endpoint) { - err = -ENOMEM; - dev_err(&intf->dev, "Could not find bulk-in endpoint\n"); - goto fail; - } - - /* v4l */ - INIT_LIST_HEAD(&cam->vidq.active); - cam->vidq.cam = cam; - - usb_set_intfdata(intf, cam); - - /* load zr364xx board specific */ - err = zr364xx_board_init(cam); - if (!err) - err = v4l2_ctrl_handler_setup(hdl); - if (err) - goto fail; - - spin_lock_init(&cam->slock); - - cam->fmt = formats; - - videobuf_queue_vmalloc_init(&cam->vb_vidq, &zr364xx_video_qops, - NULL, &cam->slock, - V4L2_BUF_TYPE_VIDEO_CAPTURE, - V4L2_FIELD_NONE, - sizeof(struct zr364xx_buffer), cam, &cam->lock); - - err = video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1); - if (err) { - dev_err(&udev->dev, "video_register_device failed\n"); - goto fail; - } - - dev_info(&udev->dev, DRIVER_DESC " controlling device %s\n", - video_device_node_name(&cam->vdev)); - return 0; - -fail: - v4l2_ctrl_handler_free(hdl); - v4l2_device_unregister(&cam->v4l2_dev); - kfree(cam); - return err; -} - - -static void zr364xx_disconnect(struct usb_interface *intf) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - - mutex_lock(&cam->lock); - usb_set_intfdata(intf, NULL); - dev_info(&intf->dev, DRIVER_DESC " webcam unplugged\n"); - video_unregister_device(&cam->vdev); - v4l2_device_disconnect(&cam->v4l2_dev); - - /* stops the read pipe if it is running */ - if (cam->b_acquire) - zr364xx_stop_acquire(cam); - - zr364xx_stop_readpipe(cam); - mutex_unlock(&cam->lock); - v4l2_device_put(&cam->v4l2_dev); -} - - -#ifdef CONFIG_PM -static int zr364xx_suspend(struct usb_interface *intf, pm_message_t message) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - - cam->was_streaming = cam->b_acquire; - if (!cam->was_streaming) - return 0; - zr364xx_stop_acquire(cam); - zr364xx_stop_readpipe(cam); - return 0; -} - -static int zr364xx_resume(struct usb_interface *intf) -{ - struct zr364xx_camera *cam = usb_get_intfdata(intf); - int res; - - if (!cam->was_streaming) - return 0; - - zr364xx_start_readpipe(cam); - res = zr364xx_prepare(cam); - if (!res) - zr364xx_start_acquire(cam); - return res; -} -#endif - -/**********************/ -/* Module integration */ -/**********************/ - -static struct usb_driver zr364xx_driver = { - .name = "zr364xx", - .probe = zr364xx_probe, - .disconnect = zr364xx_disconnect, -#ifdef CONFIG_PM - .suspend = zr364xx_suspend, - .resume = zr364xx_resume, - .reset_resume = zr364xx_resume, -#endif - .id_table = device_table -}; - -module_usb_driver(zr364xx_driver); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRIVER_VERSION); -- cgit