From 3ffea4988be3f3fa65f2104ba31eff2b5e0e82a0 Mon Sep 17 00:00:00 2001 From: David Härdeman Date: Fri, 29 Oct 2010 16:08:12 -0300 Subject: [media] ir-core: more cleanups of ir-functions.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit cx88 only depends on VIDEO_IR because it needs ir_extract_bits(). Move that function to ir-core.h and make it inline. Lots of drivers had dependencies on VIDEO_IR when they really wanted IR_CORE. The only remaining drivers to depend on VIDEO_IR are bt8xx and saa7134 (ir_rc5_timer_end is the only function exported by ir-functions). Rename VIDEO_IR -> IR_LEGACY to give a hint to anyone writing or converting drivers to IR_CORE that they do not want a dependency on IR_LEGACY. Signed-off-by: David Härdeman Acked-by: Jarod Wilson Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 6830d2848bd7..22feac4d1e8f 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -96,7 +96,7 @@ config VIDEO_HELPER_CHIPS_AUTO config VIDEO_IR_I2C tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO - depends on I2C && VIDEO_IR + depends on I2C && IR_CORE default y ---help--- Most boards have an IR chip directly connected via GPIO. However, -- cgit v1.2.3 From 9eae42e5a216059a146b3fbbe24b4fdc0b10c723 Mon Sep 17 00:00:00 2001 From: Richard Röjfors Date: Mon, 8 Nov 2010 10:45:44 -0300 Subject: [media] media: Add timberdale video-in driver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch adds the timberdale video-in driver. The video IP of timberdale delivers the video data via DMA. The driver uses the DMA api to handle DMA transfers, and make use of the V4L2 video buffers to handle buffers against user space. If available the driver uses an encoder to get/set the video standard Signed-off-by: Richard Röjfors Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 9 + drivers/media/video/Makefile | 1 + drivers/media/video/timblogiw.c | 894 ++++++++++++++++++++++++++++++++++++++++ drivers/mfd/timberdale.c | 2 - include/media/timb_radio.h | 1 - include/media/timb_video.h | 33 ++ 6 files changed, 937 insertions(+), 3 deletions(-) create mode 100644 drivers/media/video/timblogiw.c create mode 100644 include/media/timb_video.h (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 22feac4d1e8f..73962d601bf3 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -666,6 +666,15 @@ config VIDEO_HEXIUM_GEMINI To compile this driver as a module, choose M here: the module will be called hexium_gemini. +config VIDEO_TIMBERDALE + tristate "Support for timberdale Video In/LogiWIN" + depends on VIDEO_V4L2 && I2C + select TIMB_DMA + select VIDEO_ADV7180 + select VIDEOBUF_DMA_CONTIG + ---help--- + Add support for the Video In peripherial of the timberdale FPGA. + source "drivers/media/video/cx88/Kconfig" source "drivers/media/video/cx23885/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index af79d476a4c8..482f14b3ff84 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -106,6 +106,7 @@ obj-$(CONFIG_VIDEO_CPIA2) += cpia2/ obj-$(CONFIG_VIDEO_MXB) += mxb.o obj-$(CONFIG_VIDEO_HEXIUM_ORION) += hexium_orion.o obj-$(CONFIG_VIDEO_HEXIUM_GEMINI) += hexium_gemini.o +obj-$(CONFIG_VIDEO_TIMBERDALE) += timblogiw.o obj-$(CONFIG_VIDEOBUF_GEN) += videobuf-core.o obj-$(CONFIG_VIDEOBUF_DMA_SG) += videobuf-dma-sg.o diff --git a/drivers/media/video/timblogiw.c b/drivers/media/video/timblogiw.c new file mode 100644 index 000000000000..bddcdb6ffef5 --- /dev/null +++ b/drivers/media/video/timblogiw.c @@ -0,0 +1,894 @@ +/* + * timblogiw.c timberdale FPGA LogiWin Video In driver + * Copyright (c) 2009-2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * 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. + */ + +/* Supports: + * Timberdale FPGA LogiWin Video In + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRIVER_NAME "timb-video" + +#define TIMBLOGIWIN_NAME "Timberdale Video-In" +#define TIMBLOGIW_VERSION_CODE 0x04 + +#define TIMBLOGIW_LINES_PER_DESC 44 +#define TIMBLOGIW_MAX_VIDEO_MEM 16 + +#define TIMBLOGIW_HAS_DECODER(lw) (lw->pdata.encoder.module_name) + + +struct timblogiw { + struct video_device video_dev; + struct v4l2_device v4l2_dev; /* mutual exclusion */ + struct mutex lock; + struct device *dev; + struct timb_video_platform_data pdata; + struct v4l2_subdev *sd_enc; /* encoder */ + bool opened; +}; + +struct timblogiw_tvnorm { + v4l2_std_id std; + u16 width; + u16 height; + u8 fps; +}; + +struct timblogiw_fh { + struct videobuf_queue vb_vidq; + struct timblogiw_tvnorm const *cur_norm; + struct list_head capture; + struct dma_chan *chan; + spinlock_t queue_lock; /* mutual exclusion */ + unsigned int frame_count; +}; + +struct timblogiw_buffer { + /* common v4l buffer stuff -- must be first */ + struct videobuf_buffer vb; + struct scatterlist sg[16]; + dma_cookie_t cookie; + struct timblogiw_fh *fh; +}; + +const struct timblogiw_tvnorm timblogiw_tvnorms[] = { + { + .std = V4L2_STD_PAL, + .width = 720, + .height = 576, + .fps = 25 + }, + { + .std = V4L2_STD_NTSC, + .width = 720, + .height = 480, + .fps = 30 + } +}; + +static int timblogiw_bytes_per_line(const struct timblogiw_tvnorm *norm) +{ + return norm->width * 2; +} + + +static int timblogiw_frame_size(const struct timblogiw_tvnorm *norm) +{ + return norm->height * timblogiw_bytes_per_line(norm); +} + +static const struct timblogiw_tvnorm *timblogiw_get_norm(const v4l2_std_id std) +{ + int i; + for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) + if (timblogiw_tvnorms[i].std & std) + return timblogiw_tvnorms + i; + + /* default to first element */ + return timblogiw_tvnorms; +} + +static void timblogiw_dma_cb(void *data) +{ + struct timblogiw_buffer *buf = data; + struct timblogiw_fh *fh = buf->fh; + struct videobuf_buffer *vb = &buf->vb; + + spin_lock(&fh->queue_lock); + + /* mark the transfer done */ + buf->cookie = -1; + + fh->frame_count++; + + if (vb->state != VIDEOBUF_ERROR) { + list_del(&vb->queue); + do_gettimeofday(&vb->ts); + vb->field_count = fh->frame_count * 2; + vb->state = VIDEOBUF_DONE; + + wake_up(&vb->done); + } + + if (!list_empty(&fh->capture)) { + vb = list_entry(fh->capture.next, struct videobuf_buffer, + queue); + vb->state = VIDEOBUF_ACTIVE; + } + + spin_unlock(&fh->queue_lock); +} + +static bool timblogiw_dma_filter_fn(struct dma_chan *chan, void *filter_param) +{ + return chan->chan_id == (int)filter_param; +} + +/* IOCTL functions */ + +static int timblogiw_g_fmt(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw *lw = video_get_drvdata(vdev); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s entry\n", __func__); + + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + mutex_lock(&lw->lock); + + format->fmt.pix.width = fh->cur_norm->width; + format->fmt.pix.height = fh->cur_norm->height; + format->fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY; + format->fmt.pix.bytesperline = timblogiw_bytes_per_line(fh->cur_norm); + format->fmt.pix.sizeimage = timblogiw_frame_size(fh->cur_norm); + format->fmt.pix.field = V4L2_FIELD_NONE; + + mutex_unlock(&lw->lock); + + return 0; +} + +static int timblogiw_try_fmt(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct video_device *vdev = video_devdata(file); + struct v4l2_pix_format *pix = &format->fmt.pix; + + dev_dbg(&vdev->dev, + "%s - width=%d, height=%d, pixelformat=%d, field=%d\n" + "bytes per line %d, size image: %d, colorspace: %d\n", + __func__, + pix->width, pix->height, pix->pixelformat, pix->field, + pix->bytesperline, pix->sizeimage, pix->colorspace); + + if (format->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + if (pix->field != V4L2_FIELD_NONE) + return -EINVAL; + + if (pix->pixelformat != V4L2_PIX_FMT_UYVY) + return -EINVAL; + + return 0; +} + +static int timblogiw_s_fmt(struct file *file, void *priv, + struct v4l2_format *format) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw *lw = video_get_drvdata(vdev); + struct timblogiw_fh *fh = priv; + struct v4l2_pix_format *pix = &format->fmt.pix; + int err; + + mutex_lock(&lw->lock); + + err = timblogiw_try_fmt(file, priv, format); + if (err) + goto out; + + if (videobuf_queue_is_busy(&fh->vb_vidq)) { + dev_err(&vdev->dev, "%s queue busy\n", __func__); + err = -EBUSY; + goto out; + } + + pix->width = fh->cur_norm->width; + pix->height = fh->cur_norm->height; + +out: + mutex_unlock(&lw->lock); + return err; +} + +static int timblogiw_querycap(struct file *file, void *priv, + struct v4l2_capability *cap) +{ + struct video_device *vdev = video_devdata(file); + + dev_dbg(&vdev->dev, "%s: Entry\n", __func__); + memset(cap, 0, sizeof(*cap)); + strncpy(cap->card, TIMBLOGIWIN_NAME, sizeof(cap->card)-1); + strncpy(cap->driver, DRIVER_NAME, sizeof(cap->card)-1); + strlcpy(cap->bus_info, vdev->name, sizeof(cap->bus_info)); + cap->version = TIMBLOGIW_VERSION_CODE; + cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | + V4L2_CAP_READWRITE; + + return 0; +} + +static int timblogiw_enum_fmt(struct file *file, void *priv, + struct v4l2_fmtdesc *fmt) +{ + struct video_device *vdev = video_devdata(file); + + dev_dbg(&vdev->dev, "%s, index: %d\n", __func__, fmt->index); + + if (fmt->index != 0) + return -EINVAL; + memset(fmt, 0, sizeof(*fmt)); + fmt->index = 0; + fmt->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + strncpy(fmt->description, "4:2:2, packed, YUYV", + sizeof(fmt->description)-1); + fmt->pixelformat = V4L2_PIX_FMT_UYVY; + + return 0; +} + +static int timblogiw_g_parm(struct file *file, void *priv, + struct v4l2_streamparm *sp) +{ + struct timblogiw_fh *fh = priv; + struct v4l2_captureparm *cp = &sp->parm.capture; + + cp->capability = V4L2_CAP_TIMEPERFRAME; + cp->timeperframe.numerator = 1; + cp->timeperframe.denominator = fh->cur_norm->fps; + + return 0; +} + +static int timblogiw_reqbufs(struct file *file, void *priv, + struct v4l2_requestbuffers *rb) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + return videobuf_reqbufs(&fh->vb_vidq, rb); +} + +static int timblogiw_querybuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + return videobuf_querybuf(&fh->vb_vidq, b); +} + +static int timblogiw_qbuf(struct file *file, void *priv, struct v4l2_buffer *b) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + return videobuf_qbuf(&fh->vb_vidq, b); +} + +static int timblogiw_dqbuf(struct file *file, void *priv, + struct v4l2_buffer *b) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + return videobuf_dqbuf(&fh->vb_vidq, b, file->f_flags & O_NONBLOCK); +} + +static int timblogiw_g_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + *std = fh->cur_norm->std; + return 0; +} + +static int timblogiw_s_std(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw *lw = video_get_drvdata(vdev); + struct timblogiw_fh *fh = priv; + int err = 0; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + mutex_lock(&lw->lock); + + if (TIMBLOGIW_HAS_DECODER(lw)) + err = v4l2_subdev_call(lw->sd_enc, core, s_std, *std); + + if (!err) + fh->cur_norm = timblogiw_get_norm(*std); + + mutex_unlock(&lw->lock); + + return err; +} + +static int timblogiw_enuminput(struct file *file, void *priv, + struct v4l2_input *inp) +{ + struct video_device *vdev = video_devdata(file); + int i; + + dev_dbg(&vdev->dev, "%s: Entry\n", __func__); + + if (inp->index != 0) + return -EINVAL; + + memset(inp, 0, sizeof(*inp)); + inp->index = 0; + + strncpy(inp->name, "Timb input 1", sizeof(inp->name) - 1); + inp->type = V4L2_INPUT_TYPE_CAMERA; + + inp->std = 0; + for (i = 0; i < ARRAY_SIZE(timblogiw_tvnorms); i++) + inp->std |= timblogiw_tvnorms[i].std; + + return 0; +} + +static int timblogiw_g_input(struct file *file, void *priv, + unsigned int *input) +{ + struct video_device *vdev = video_devdata(file); + + dev_dbg(&vdev->dev, "%s: Entry\n", __func__); + + *input = 0; + + return 0; +} + +static int timblogiw_s_input(struct file *file, void *priv, unsigned int input) +{ + struct video_device *vdev = video_devdata(file); + + dev_dbg(&vdev->dev, "%s: Entry\n", __func__); + + if (input != 0) + return -EINVAL; + return 0; +} + +static int timblogiw_streamon(struct file *file, void *priv, unsigned int type) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { + dev_dbg(&vdev->dev, "%s - No capture device\n", __func__); + return -EINVAL; + } + + fh->frame_count = 0; + return videobuf_streamon(&fh->vb_vidq); +} + +static int timblogiw_streamoff(struct file *file, void *priv, + unsigned int type) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s entry\n", __func__); + + if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) + return -EINVAL; + + return videobuf_streamoff(&fh->vb_vidq); +} + +static int timblogiw_querystd(struct file *file, void *priv, v4l2_std_id *std) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw *lw = video_get_drvdata(vdev); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s entry\n", __func__); + + if (TIMBLOGIW_HAS_DECODER(lw)) + return v4l2_subdev_call(lw->sd_enc, video, querystd, std); + else { + *std = fh->cur_norm->std; + return 0; + } +} + +static int timblogiw_enum_framesizes(struct file *file, void *priv, + struct v4l2_frmsizeenum *fsize) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = priv; + + dev_dbg(&vdev->dev, "%s - index: %d, format: %d\n", __func__, + fsize->index, fsize->pixel_format); + + if ((fsize->index != 0) || + (fsize->pixel_format != V4L2_PIX_FMT_UYVY)) + return -EINVAL; + + fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE; + fsize->discrete.width = fh->cur_norm->width; + fsize->discrete.height = fh->cur_norm->height; + + return 0; +} + +/* Video buffer functions */ + +static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, + unsigned int *size) +{ + struct timblogiw_fh *fh = vq->priv_data; + + *size = timblogiw_frame_size(fh->cur_norm); + + if (!*count) + *count = 32; + + while (*size * *count > TIMBLOGIW_MAX_VIDEO_MEM * 1024 * 1024) + (*count)--; + + return 0; +} + +static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, + enum v4l2_field field) +{ + struct timblogiw_fh *fh = vq->priv_data; + struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, + vb); + unsigned int data_size = timblogiw_frame_size(fh->cur_norm); + int err = 0; + + if (vb->baddr && vb->bsize < data_size) + /* User provided buffer, but it is too small */ + return -ENOMEM; + + vb->size = data_size; + vb->width = fh->cur_norm->width; + vb->height = fh->cur_norm->height; + vb->field = field; + + if (vb->state == VIDEOBUF_NEEDS_INIT) { + int i; + unsigned int size; + unsigned int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * + timblogiw_bytes_per_line(fh->cur_norm); + dma_addr_t addr; + + sg_init_table(buf->sg, ARRAY_SIZE(buf->sg)); + + err = videobuf_iolock(vq, vb, NULL); + if (err) + goto err; + + addr = videobuf_to_dma_contig(vb); + for (i = 0, size = 0; size < data_size; i++) { + sg_dma_address(buf->sg + i) = addr + size; + size += bytes_per_desc; + sg_dma_len(buf->sg + i) = (size > data_size) ? + (bytes_per_desc - (size - data_size)) : + bytes_per_desc; + } + + vb->state = VIDEOBUF_PREPARED; + buf->cookie = -1; + buf->fh = fh; + } + + return 0; + +err: + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; + return err; +} + +static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) +{ + struct timblogiw_fh *fh = vq->priv_data; + struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, + vb); + struct dma_async_tx_descriptor *desc; + int sg_elems; + int bytes_per_desc = TIMBLOGIW_LINES_PER_DESC * + timblogiw_bytes_per_line(fh->cur_norm); + + sg_elems = timblogiw_frame_size(fh->cur_norm) / bytes_per_desc; + sg_elems += + (timblogiw_frame_size(fh->cur_norm) % bytes_per_desc) ? 1 : 0; + + if (list_empty(&fh->capture)) + vb->state = VIDEOBUF_ACTIVE; + else + vb->state = VIDEOBUF_QUEUED; + + list_add_tail(&vb->queue, &fh->capture); + + spin_unlock_irq(&fh->queue_lock); + + desc = fh->chan->device->device_prep_slave_sg(fh->chan, + buf->sg, sg_elems, DMA_FROM_DEVICE, + DMA_PREP_INTERRUPT | DMA_COMPL_SKIP_SRC_UNMAP); + if (!desc) { + spin_lock_irq(&fh->queue_lock); + list_del_init(&vb->queue); + vb->state = VIDEOBUF_PREPARED; + return; + } + + desc->callback_param = buf; + desc->callback = timblogiw_dma_cb; + + buf->cookie = desc->tx_submit(desc); + + spin_lock_irq(&fh->queue_lock); +} + +static void buffer_release(struct videobuf_queue *vq, + struct videobuf_buffer *vb) +{ + struct timblogiw_fh *fh = vq->priv_data; + struct timblogiw_buffer *buf = container_of(vb, struct timblogiw_buffer, + vb); + + videobuf_waiton(vq, vb, 0, 0); + if (buf->cookie >= 0) + dma_sync_wait(fh->chan, buf->cookie); + + videobuf_dma_contig_free(vq, vb); + vb->state = VIDEOBUF_NEEDS_INIT; +} + +static struct videobuf_queue_ops timblogiw_video_qops = { + .buf_setup = buffer_setup, + .buf_prepare = buffer_prepare, + .buf_queue = buffer_queue, + .buf_release = buffer_release, +}; + +/* Device Operations functions */ + +static int timblogiw_open(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw *lw = video_get_drvdata(vdev); + struct timblogiw_fh *fh; + v4l2_std_id std; + dma_cap_mask_t mask; + int err = 0; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + mutex_lock(&lw->lock); + if (lw->opened) { + err = -EBUSY; + goto out; + } + + if (TIMBLOGIW_HAS_DECODER(lw) && !lw->sd_enc) { + struct i2c_adapter *adapt; + + /* find the video decoder */ + adapt = i2c_get_adapter(lw->pdata.i2c_adapter); + if (!adapt) { + dev_err(&vdev->dev, "No I2C bus #%d\n", + lw->pdata.i2c_adapter); + err = -ENODEV; + goto out; + } + + /* now find the encoder */ + lw->sd_enc = v4l2_i2c_new_subdev_board(&lw->v4l2_dev, adapt, + lw->pdata.encoder.info, NULL); + + i2c_put_adapter(adapt); + + if (!lw->sd_enc) { + dev_err(&vdev->dev, "Failed to get encoder: %s\n", + lw->pdata.encoder.module_name); + err = -ENODEV; + goto out; + } + } + + fh = kzalloc(sizeof(*fh), GFP_KERNEL); + if (!fh) { + err = -ENOMEM; + goto out; + } + + fh->cur_norm = timblogiw_tvnorms; + timblogiw_querystd(file, fh, &std); + fh->cur_norm = timblogiw_get_norm(std); + + INIT_LIST_HEAD(&fh->capture); + spin_lock_init(&fh->queue_lock); + + dma_cap_zero(mask); + dma_cap_set(DMA_SLAVE, mask); + dma_cap_set(DMA_PRIVATE, mask); + + /* find the DMA channel */ + fh->chan = dma_request_channel(mask, timblogiw_dma_filter_fn, + (void *)lw->pdata.dma_channel); + if (!fh->chan) { + dev_err(&vdev->dev, "Failed to get DMA channel\n"); + kfree(fh); + err = -ENODEV; + goto out; + } + + file->private_data = fh; + videobuf_queue_dma_contig_init(&fh->vb_vidq, + &timblogiw_video_qops, lw->dev, &fh->queue_lock, + V4L2_BUF_TYPE_VIDEO_CAPTURE, V4L2_FIELD_NONE, + sizeof(struct timblogiw_buffer), fh, NULL); + + lw->opened = true; +out: + mutex_unlock(&lw->lock); + + return err; +} + +static int timblogiw_close(struct file *file) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw *lw = video_get_drvdata(vdev); + struct timblogiw_fh *fh = file->private_data; + + dev_dbg(&vdev->dev, "%s: Entry\n", __func__); + + videobuf_stop(&fh->vb_vidq); + videobuf_mmap_free(&fh->vb_vidq); + + dma_release_channel(fh->chan); + + kfree(fh); + + mutex_lock(&lw->lock); + lw->opened = false; + mutex_unlock(&lw->lock); + return 0; +} + +static ssize_t timblogiw_read(struct file *file, char __user *data, + size_t count, loff_t *ppos) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = file->private_data; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + return videobuf_read_stream(&fh->vb_vidq, data, count, ppos, 0, + file->f_flags & O_NONBLOCK); +} + +static unsigned int timblogiw_poll(struct file *file, + struct poll_table_struct *wait) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = file->private_data; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + return videobuf_poll_stream(file, &fh->vb_vidq, wait); +} + +static int timblogiw_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *vdev = video_devdata(file); + struct timblogiw_fh *fh = file->private_data; + + dev_dbg(&vdev->dev, "%s: entry\n", __func__); + + return videobuf_mmap_mapper(&fh->vb_vidq, vma); +} + +/* Platform device functions */ + +static const __devinitdata struct v4l2_ioctl_ops timblogiw_ioctl_ops = { + .vidioc_querycap = timblogiw_querycap, + .vidioc_enum_fmt_vid_cap = timblogiw_enum_fmt, + .vidioc_g_fmt_vid_cap = timblogiw_g_fmt, + .vidioc_try_fmt_vid_cap = timblogiw_try_fmt, + .vidioc_s_fmt_vid_cap = timblogiw_s_fmt, + .vidioc_g_parm = timblogiw_g_parm, + .vidioc_reqbufs = timblogiw_reqbufs, + .vidioc_querybuf = timblogiw_querybuf, + .vidioc_qbuf = timblogiw_qbuf, + .vidioc_dqbuf = timblogiw_dqbuf, + .vidioc_g_std = timblogiw_g_std, + .vidioc_s_std = timblogiw_s_std, + .vidioc_enum_input = timblogiw_enuminput, + .vidioc_g_input = timblogiw_g_input, + .vidioc_s_input = timblogiw_s_input, + .vidioc_streamon = timblogiw_streamon, + .vidioc_streamoff = timblogiw_streamoff, + .vidioc_querystd = timblogiw_querystd, + .vidioc_enum_framesizes = timblogiw_enum_framesizes, +}; + +static const __devinitdata struct v4l2_file_operations timblogiw_fops = { + .owner = THIS_MODULE, + .open = timblogiw_open, + .release = timblogiw_close, + .unlocked_ioctl = video_ioctl2, /* V4L2 ioctl handler */ + .mmap = timblogiw_mmap, + .read = timblogiw_read, + .poll = timblogiw_poll, +}; + +static const __devinitdata struct video_device timblogiw_template = { + .name = TIMBLOGIWIN_NAME, + .fops = &timblogiw_fops, + .ioctl_ops = &timblogiw_ioctl_ops, + .release = video_device_release_empty, + .minor = -1, + .tvnorms = V4L2_STD_PAL | V4L2_STD_NTSC +}; + +static int __devinit timblogiw_probe(struct platform_device *pdev) +{ + int err; + struct timblogiw *lw = NULL; + struct timb_video_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) { + dev_err(&pdev->dev, "No platform data\n"); + err = -EINVAL; + goto err; + } + + if (!pdata->encoder.module_name) + dev_info(&pdev->dev, "Running without decoder\n"); + + lw = kzalloc(sizeof(*lw), GFP_KERNEL); + if (!lw) { + err = -ENOMEM; + goto err; + } + + if (pdev->dev.parent) + lw->dev = pdev->dev.parent; + else + lw->dev = &pdev->dev; + + memcpy(&lw->pdata, pdata, sizeof(lw->pdata)); + + mutex_init(&lw->lock); + + lw->video_dev = timblogiw_template; + + strlcpy(lw->v4l2_dev.name, DRIVER_NAME, sizeof(lw->v4l2_dev.name)); + err = v4l2_device_register(NULL, &lw->v4l2_dev); + if (err) + goto err_register; + + lw->video_dev.v4l2_dev = &lw->v4l2_dev; + + platform_set_drvdata(pdev, lw); + video_set_drvdata(&lw->video_dev, lw); + + err = video_register_device(&lw->video_dev, VFL_TYPE_GRABBER, 0); + if (err) { + dev_err(&pdev->dev, "Error reg video: %d\n", err); + goto err_request; + } + + + return 0; + +err_request: + platform_set_drvdata(pdev, NULL); + v4l2_device_unregister(&lw->v4l2_dev); +err_register: + kfree(lw); +err: + dev_err(&pdev->dev, "Failed to register: %d\n", err); + + return err; +} + +static int __devexit timblogiw_remove(struct platform_device *pdev) +{ + struct timblogiw *lw = platform_get_drvdata(pdev); + + video_unregister_device(&lw->video_dev); + + v4l2_device_unregister(&lw->v4l2_dev); + + kfree(lw); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver timblogiw_platform_driver = { + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, + .probe = timblogiw_probe, + .remove = __devexit_p(timblogiw_remove), +}; + +/* Module functions */ + +static int __init timblogiw_init(void) +{ + return platform_driver_register(&timblogiw_platform_driver); +} + +static void __exit timblogiw_exit(void) +{ + platform_driver_unregister(&timblogiw_platform_driver); +} + +module_init(timblogiw_init); +module_exit(timblogiw_exit); + +MODULE_DESCRIPTION(TIMBLOGIWIN_NAME); +MODULE_AUTHOR("Pelagicore AB "); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:"DRIVER_NAME); diff --git a/drivers/mfd/timberdale.c b/drivers/mfd/timberdale.c index 727f62c15a60..e948a2b45b86 100644 --- a/drivers/mfd/timberdale.c +++ b/drivers/mfd/timberdale.c @@ -271,11 +271,9 @@ static __devinitdata struct timb_radio_platform_data timberdale_radio_platform_data = { .i2c_adapter = 0, .tuner = { - .module_name = "tef6862", .info = &timberdale_tef6868_i2c_board_info }, .dsp = { - .module_name = "saa7706h", .info = &timberdale_saa7706_i2c_board_info } }; diff --git a/include/media/timb_radio.h b/include/media/timb_radio.h index fcd32a3696ba..a59a84854dc1 100644 --- a/include/media/timb_radio.h +++ b/include/media/timb_radio.h @@ -24,7 +24,6 @@ struct timb_radio_platform_data { int i2c_adapter; /* I2C adapter where the tuner and dsp are attached */ struct { - const char *module_name; struct i2c_board_info *info; } tuner; struct { diff --git a/include/media/timb_video.h b/include/media/timb_video.h new file mode 100644 index 000000000000..70ae43970a49 --- /dev/null +++ b/include/media/timb_video.h @@ -0,0 +1,33 @@ +/* + * timb_video.h Platform struct for the Timberdale video driver + * Copyright (c) 2009-2010 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _TIMB_VIDEO_ +#define _TIMB_VIDEO_ 1 + +#include + +struct timb_video_platform_data { + int dma_channel; + int i2c_adapter; /* The I2C adapter where the encoder is attached */ + struct { + const char *module_name; + struct i2c_board_info *info; + } encoder; +}; + +#endif -- cgit v1.2.3 From 9c0147b6941e082f52dcbb9688d0e6e5f359744f Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 24 Nov 2010 16:41:09 -0300 Subject: [media] timblogiw: fix kconfig & build error timblogiw uses dma() interfaces and it selects TIMB_DMA for that support. However, drivers/dma/ is not built unless CONFIG_DMA_ENGINE is enabled, so select/enable that symbol also. drivers/built-in.o: In function `timblogiw_close': timblogiw.c:(.text+0x4419fe): undefined reference to `dma_release_channel' drivers/built-in.o: In function `buffer_release': timblogiw.c:(.text+0x441a8d): undefined reference to `dma_sync_wait' drivers/built-in.o: In function `timblogiw_open': timblogiw.c:(.text+0x44212b): undefined reference to `__dma_request_channel' Signed-off-by: Randy Dunlap Signed-off-by: Andrew Morton Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 73962d601bf3..963b3e6c4a97 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -669,6 +669,7 @@ config VIDEO_HEXIUM_GEMINI config VIDEO_TIMBERDALE tristate "Support for timberdale Video In/LogiWIN" depends on VIDEO_V4L2 && I2C + select DMA_ENGINE select TIMB_DMA select VIDEO_ADV7180 select VIDEOBUF_DMA_CONTIG -- cgit v1.2.3 From 6bda96447cef24fbf97a798b1ea664224d5fdc25 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Wed, 17 Nov 2010 13:28:38 -0300 Subject: [media] rc: rename the remaining things to rc_core The Remote Controller subsystem is meant to be used not only by Infra Red but also for similar types of Remote Controllers. The core is not specific to Infra Red. As such, rename: - ir-core.h to rc-core.h - IR_CORE to RC_CORE - namespace inside rc-core.c/rc-core.h To be consistent with the other changes. No functional change on this patch. Signed-off-by: Mauro Carvalho Chehab --- drivers/media/dvb/dm1105/Kconfig | 2 +- drivers/media/dvb/dm1105/dm1105.c | 2 +- drivers/media/dvb/dvb-usb/Kconfig | 2 +- drivers/media/dvb/dvb-usb/dvb-usb.h | 2 +- drivers/media/dvb/dvb-usb/lmedm04.c | 2 +- drivers/media/dvb/mantis/Kconfig | 2 +- drivers/media/dvb/mantis/mantis_input.c | 2 +- drivers/media/dvb/siano/Kconfig | 2 +- drivers/media/dvb/siano/smsir.h | 2 +- drivers/media/dvb/ttpci/Kconfig | 2 +- drivers/media/dvb/ttpci/budget-ci.c | 2 +- drivers/media/rc/Kconfig | 35 ++--- drivers/media/rc/Makefile | 2 +- drivers/media/rc/ene_ir.c | 2 +- drivers/media/rc/imon.c | 2 +- drivers/media/rc/ir-lirc-codec.c | 2 +- drivers/media/rc/keymaps/Kconfig | 2 +- drivers/media/rc/keymaps/rc-lirc.c | 2 +- drivers/media/rc/mceusb.c | 2 +- drivers/media/rc/nuvoton-cir.c | 2 +- drivers/media/rc/rc-core-priv.h | 2 +- drivers/media/rc/rc-main.c | 18 +-- drivers/media/rc/rc-raw.c | 2 +- drivers/media/rc/streamzap.c | 2 +- drivers/media/rc/winbond-cir.c | 2 +- drivers/media/video/Kconfig | 2 +- drivers/media/video/bt8xx/Kconfig | 2 +- drivers/media/video/bt8xx/bttvp.h | 2 +- drivers/media/video/cx18/Kconfig | 2 +- drivers/media/video/cx231xx/Kconfig | 4 +- drivers/media/video/cx231xx/cx231xx.h | 2 +- drivers/media/video/cx23885/Kconfig | 2 +- drivers/media/video/cx23885/cx23885-input.c | 2 +- drivers/media/video/cx23885/cx23885.h | 2 +- drivers/media/video/cx23885/cx23888-ir.c | 2 +- drivers/media/video/cx25840/cx25840-ir.c | 2 +- drivers/media/video/cx88/Kconfig | 2 +- drivers/media/video/cx88/cx88-input.c | 2 +- drivers/media/video/em28xx/Kconfig | 2 +- drivers/media/video/em28xx/em28xx.h | 2 +- drivers/media/video/ir-kbd-i2c.c | 2 +- drivers/media/video/ivtv/Kconfig | 2 +- drivers/media/video/saa7134/Kconfig | 2 +- drivers/media/video/saa7134/saa7134.h | 2 +- drivers/media/video/tlg2300/Kconfig | 2 +- drivers/staging/cx25821/Kconfig | 2 +- drivers/staging/go7007/Kconfig | 2 +- drivers/staging/tm6000/Kconfig | 2 +- drivers/staging/tm6000/tm6000-input.c | 2 +- include/media/ir-core.h | 211 ---------------------------- include/media/ir-kbd-i2c.h | 2 +- include/media/rc-core.h | 211 ++++++++++++++++++++++++++++ 52 files changed, 287 insertions(+), 286 deletions(-) delete mode 100644 include/media/ir-core.h create mode 100644 include/media/rc-core.h (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/dvb/dm1105/Kconfig b/drivers/media/dvb/dm1105/Kconfig index 576f3b70d792..f3de0a4d63f2 100644 --- a/drivers/media/dvb/dm1105/Kconfig +++ b/drivers/media/dvb/dm1105/Kconfig @@ -8,7 +8,7 @@ config DVB_DM1105 select DVB_CX24116 if !DVB_FE_CUSTOMISE select DVB_SI21XX if !DVB_FE_CUSTOMISE select DVB_DS3000 if !DVB_FE_CUSTOMISE - depends on IR_CORE + depends on RC_CORE help Support for cards based on the SDMC DM1105 PCI chip like DvbWorld 2002 diff --git a/drivers/media/dvb/dm1105/dm1105.c b/drivers/media/dvb/dm1105/dm1105.c index d1a43858f29c..e9cacf6e9277 100644 --- a/drivers/media/dvb/dm1105/dm1105.c +++ b/drivers/media/dvb/dm1105/dm1105.c @@ -27,7 +27,7 @@ #include #include #include -#include +#include #include "demux.h" #include "dmxdev.h" diff --git a/drivers/media/dvb/dvb-usb/Kconfig b/drivers/media/dvb/dvb-usb/Kconfig index 2525d3b3c88d..3d48ba019342 100644 --- a/drivers/media/dvb/dvb-usb/Kconfig +++ b/drivers/media/dvb/dvb-usb/Kconfig @@ -1,6 +1,6 @@ config DVB_USB tristate "Support for various USB DVB devices" - depends on DVB_CORE && USB && I2C && IR_CORE + depends on DVB_CORE && USB && I2C && RC_CORE help By enabling this you will be able to choose the various supported USB1.1 and USB2.0 DVB devices. diff --git a/drivers/media/dvb/dvb-usb/dvb-usb.h b/drivers/media/dvb/dvb-usb/dvb-usb.h index 83aa9826560f..18828cb282c4 100644 --- a/drivers/media/dvb/dvb-usb/dvb-usb.h +++ b/drivers/media/dvb/dvb-usb/dvb-usb.h @@ -14,7 +14,7 @@ #include #include #include -#include +#include #include "dvb_frontend.h" #include "dvb_demux.h" diff --git a/drivers/media/dvb/dvb-usb/lmedm04.c b/drivers/media/dvb/dvb-usb/lmedm04.c index d8f1b150e1cb..f0c030837f29 100644 --- a/drivers/media/dvb/dvb-usb/lmedm04.c +++ b/drivers/media/dvb/dvb-usb/lmedm04.c @@ -61,7 +61,7 @@ #define DVB_USB_LOG_PREFIX "LME2510(C)" #include #include -#include +#include #include "dvb-usb.h" #include "lmedm04.h" diff --git a/drivers/media/dvb/mantis/Kconfig b/drivers/media/dvb/mantis/Kconfig index fd0830ed10d8..a13a50503134 100644 --- a/drivers/media/dvb/mantis/Kconfig +++ b/drivers/media/dvb/mantis/Kconfig @@ -1,6 +1,6 @@ config MANTIS_CORE tristate "Mantis/Hopper PCI bridge based devices" - depends on PCI && I2C && INPUT && IR_CORE + depends on PCI && I2C && INPUT && RC_CORE help Support for PCI cards based on the Mantis and Hopper PCi bridge. diff --git a/drivers/media/dvb/mantis/mantis_input.c b/drivers/media/dvb/mantis/mantis_input.c index 209f2110e20c..e030b18ad6d9 100644 --- a/drivers/media/dvb/mantis/mantis_input.c +++ b/drivers/media/dvb/mantis/mantis_input.c @@ -18,7 +18,7 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ -#include +#include #include #include "dmxdev.h" diff --git a/drivers/media/dvb/siano/Kconfig b/drivers/media/dvb/siano/Kconfig index e520bceee0af..bc6456eb2c4f 100644 --- a/drivers/media/dvb/siano/Kconfig +++ b/drivers/media/dvb/siano/Kconfig @@ -4,7 +4,7 @@ config SMS_SIANO_MDTV tristate "Siano SMS1xxx based MDTV receiver" - depends on DVB_CORE && IR_CORE && HAS_DMA + depends on DVB_CORE && RC_CORE && HAS_DMA ---help--- Choose Y or M here if you have MDTV receiver with a Siano chipset. diff --git a/drivers/media/dvb/siano/smsir.h b/drivers/media/dvb/siano/smsir.h index c2f68a460ee1..ae92b3a8587e 100644 --- a/drivers/media/dvb/siano/smsir.h +++ b/drivers/media/dvb/siano/smsir.h @@ -28,7 +28,7 @@ along with this program. If not, see . #define __SMS_IR_H__ #include -#include +#include #define IR_DEFAULT_TIMEOUT 100 diff --git a/drivers/media/dvb/ttpci/Kconfig b/drivers/media/dvb/ttpci/Kconfig index 0ffd6943da83..44afab2fdc2d 100644 --- a/drivers/media/dvb/ttpci/Kconfig +++ b/drivers/media/dvb/ttpci/Kconfig @@ -97,7 +97,7 @@ config DVB_BUDGET_CI select DVB_LNBP21 if !DVB_FE_CUSTOMISE select DVB_TDA10023 if !DVB_FE_CUSTOMISE select MEDIA_TUNER_TDA827X if !MEDIA_TUNER_CUSTOMISE - depends on IR_CORE + depends on RC_CORE help Support for simple SAA7146 based DVB cards (so called Budget- or Nova-PCI cards) without onboard diff --git a/drivers/media/dvb/ttpci/budget-ci.c b/drivers/media/dvb/ttpci/budget-ci.c index 9aca0f37993b..32caa9b7af8e 100644 --- a/drivers/media/dvb/ttpci/budget-ci.c +++ b/drivers/media/dvb/ttpci/budget-ci.c @@ -34,7 +34,7 @@ #include #include #include -#include +#include #include "budget.h" diff --git a/drivers/media/rc/Kconfig b/drivers/media/rc/Kconfig index ef19375899ec..42b4feb0e7f9 100644 --- a/drivers/media/rc/Kconfig +++ b/drivers/media/rc/Kconfig @@ -1,16 +1,17 @@ -menuconfig IR_CORE - tristate "Infrared remote controller adapters" +menuconfig RC_CORE + tristate "Remote Controller adapters" depends on INPUT default INPUT ---help--- Enable support for Remote Controllers on Linux. This is needed in order to support several video capture adapters. + Currently, all supported devices use InfraRed. Enable this option if you have a video capture board even if you don't need IR, as otherwise, you may not be able to compile the driver for your adapter. -if IR_CORE +if RC_CORE config LIRC tristate @@ -27,7 +28,7 @@ source "drivers/media/rc/keymaps/Kconfig" config IR_NEC_DECODER tristate "Enable IR raw decoder for the NEC protocol" - depends on IR_CORE + depends on RC_CORE select BITREVERSE default y @@ -37,7 +38,7 @@ config IR_NEC_DECODER config IR_RC5_DECODER tristate "Enable IR raw decoder for the RC-5 protocol" - depends on IR_CORE + depends on RC_CORE select BITREVERSE default y @@ -47,7 +48,7 @@ config IR_RC5_DECODER config IR_RC6_DECODER tristate "Enable IR raw decoder for the RC6 protocol" - depends on IR_CORE + depends on RC_CORE select BITREVERSE default y @@ -57,7 +58,7 @@ config IR_RC6_DECODER config IR_JVC_DECODER tristate "Enable IR raw decoder for the JVC protocol" - depends on IR_CORE + depends on RC_CORE select BITREVERSE default y @@ -67,7 +68,7 @@ config IR_JVC_DECODER config IR_SONY_DECODER tristate "Enable IR raw decoder for the Sony protocol" - depends on IR_CORE + depends on RC_CORE default y ---help--- @@ -76,7 +77,7 @@ config IR_SONY_DECODER config IR_RC5_SZ_DECODER tristate "Enable IR raw decoder for the RC-5 (streamzap) protocol" - depends on IR_CORE + depends on RC_CORE select BITREVERSE default y @@ -88,7 +89,7 @@ config IR_RC5_SZ_DECODER config IR_LIRC_CODEC tristate "Enable IR to LIRC bridge" - depends on IR_CORE + depends on RC_CORE depends on LIRC default y @@ -99,7 +100,7 @@ config IR_LIRC_CODEC config IR_ENE tristate "ENE eHome Receiver/Transceiver (pnp id: ENE0100/ENE02xxx)" depends on PNP - depends on IR_CORE + depends on RC_CORE ---help--- Say Y here to enable support for integrated infrared receiver /transceiver made by ENE. @@ -113,7 +114,7 @@ config IR_ENE config IR_IMON tristate "SoundGraph iMON Receiver and Display" depends on USB_ARCH_HAS_HCD - depends on IR_CORE + depends on RC_CORE select USB ---help--- Say Y here if you want to use a SoundGraph iMON (aka Antec Veris) @@ -125,7 +126,7 @@ config IR_IMON config IR_MCEUSB tristate "Windows Media Center Ed. eHome Infrared Transceiver" depends on USB_ARCH_HAS_HCD - depends on IR_CORE + depends on RC_CORE select USB ---help--- Say Y here if you want to use a Windows Media Center Edition @@ -137,7 +138,7 @@ config IR_MCEUSB config IR_NUVOTON tristate "Nuvoton w836x7hg Consumer Infrared Transceiver" depends on PNP - depends on IR_CORE + depends on RC_CORE ---help--- Say Y here to enable support for integrated infrared receiver /transciever made by Nuvoton (formerly Winbond). This chip is @@ -150,7 +151,7 @@ config IR_NUVOTON config IR_STREAMZAP tristate "Streamzap PC Remote IR Receiver" depends on USB_ARCH_HAS_HCD - depends on IR_CORE + depends on RC_CORE select USB ---help--- Say Y here if you want to use a Streamzap PC Remote @@ -162,7 +163,7 @@ config IR_STREAMZAP config IR_WINBOND_CIR tristate "Winbond IR remote control" depends on X86 && PNP - depends on IR_CORE + depends on RC_CORE select NEW_LEDS select LEDS_CLASS select LEDS_TRIGGERS @@ -176,4 +177,4 @@ config IR_WINBOND_CIR To compile this driver as a module, choose M here: the module will be called winbond_cir. -endif #IR_CORE +endif #RC_CORE diff --git a/drivers/media/rc/Makefile b/drivers/media/rc/Makefile index 8c0e4cb19551..21251ba55904 100644 --- a/drivers/media/rc/Makefile +++ b/drivers/media/rc/Makefile @@ -2,7 +2,7 @@ rc-core-objs := rc-main.o rc-raw.o obj-y += keymaps/ -obj-$(CONFIG_IR_CORE) += rc-core.o +obj-$(CONFIG_RC_CORE) += rc-core.o obj-$(CONFIG_LIRC) += lirc_dev.o obj-$(CONFIG_IR_NEC_DECODER) += ir-nec-decoder.o obj-$(CONFIG_IR_RC5_DECODER) += ir-rc5-decoder.o diff --git a/drivers/media/rc/ene_ir.c b/drivers/media/rc/ene_ir.c index 0a4151f03c7e..1ace4581f157 100644 --- a/drivers/media/rc/ene_ir.c +++ b/drivers/media/rc/ene_ir.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include "ene_ir.h" static int sample_period; diff --git a/drivers/media/rc/imon.c b/drivers/media/rc/imon.c index 8d4b35d1ae56..b6ba3c7dc632 100644 --- a/drivers/media/rc/imon.c +++ b/drivers/media/rc/imon.c @@ -38,7 +38,7 @@ #include #include #include -#include +#include #include #include diff --git a/drivers/media/rc/ir-lirc-codec.c b/drivers/media/rc/ir-lirc-codec.c index ceabea6053de..f9086c5ba8b1 100644 --- a/drivers/media/rc/ir-lirc-codec.c +++ b/drivers/media/rc/ir-lirc-codec.c @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include "rc-core-priv.h" #define LIRCBUF_SIZE 256 diff --git a/drivers/media/rc/keymaps/Kconfig b/drivers/media/rc/keymaps/Kconfig index 14b22f58f823..8e615fd55852 100644 --- a/drivers/media/rc/keymaps/Kconfig +++ b/drivers/media/rc/keymaps/Kconfig @@ -1,6 +1,6 @@ config RC_MAP tristate "Compile Remote Controller keymap modules" - depends on IR_CORE + depends on RC_CORE default y ---help--- diff --git a/drivers/media/rc/keymaps/rc-lirc.c b/drivers/media/rc/keymaps/rc-lirc.c index 43fcf9035082..9c8577d6e497 100644 --- a/drivers/media/rc/keymaps/rc-lirc.c +++ b/drivers/media/rc/keymaps/rc-lirc.c @@ -9,7 +9,7 @@ * (at your option) any later version. */ -#include +#include static struct ir_scancode lirc[] = { { }, diff --git a/drivers/media/rc/mceusb.c b/drivers/media/rc/mceusb.c index 539bec2974b7..db7787ac67a3 100644 --- a/drivers/media/rc/mceusb.c +++ b/drivers/media/rc/mceusb.c @@ -37,7 +37,7 @@ #include #include #include -#include +#include #define DRIVER_VERSION "1.91" #define DRIVER_AUTHOR "Jarod Wilson " diff --git a/drivers/media/rc/nuvoton-cir.c b/drivers/media/rc/nuvoton-cir.c index 0ce328f9dacf..bf3f58f7bd7c 100644 --- a/drivers/media/rc/nuvoton-cir.c +++ b/drivers/media/rc/nuvoton-cir.c @@ -32,7 +32,7 @@ #include #include #include -#include +#include #include #include "nuvoton-cir.h" diff --git a/drivers/media/rc/rc-core-priv.h b/drivers/media/rc/rc-core-priv.h index 3616c32d3f87..48065b714d00 100644 --- a/drivers/media/rc/rc-core-priv.h +++ b/drivers/media/rc/rc-core-priv.h @@ -18,7 +18,7 @@ #include #include -#include +#include struct ir_raw_handler { struct list_head list; diff --git a/drivers/media/rc/rc-main.c b/drivers/media/rc/rc-main.c index 5b67eea96ebb..d91b62cf1324 100644 --- a/drivers/media/rc/rc-main.c +++ b/drivers/media/rc/rc-main.c @@ -12,7 +12,7 @@ * GNU General Public License for more details. */ -#include +#include #include #include #include @@ -1103,11 +1103,11 @@ EXPORT_SYMBOL_GPL(rc_unregister_device); * Init/exit code for the module. Basically, creates/removes /sys/class/rc */ -static int __init ir_core_init(void) +static int __init rc_core_init(void) { int rc = class_register(&ir_input_class); if (rc) { - printk(KERN_ERR "ir_core: unable to register rc class\n"); + printk(KERN_ERR "rc_core: unable to register rc class\n"); return rc; } @@ -1118,18 +1118,18 @@ static int __init ir_core_init(void) return 0; } -static void __exit ir_core_exit(void) +static void __exit rc_core_exit(void) { class_unregister(&ir_input_class); ir_unregister_map(&empty_map); } -module_init(ir_core_init); -module_exit(ir_core_exit); +module_init(rc_core_init); +module_exit(rc_core_exit); -int ir_core_debug; /* ir_debug level (0,1,2) */ -EXPORT_SYMBOL_GPL(ir_core_debug); -module_param_named(debug, ir_core_debug, int, 0644); +int rc_core_debug; /* ir_debug level (0,1,2) */ +EXPORT_SYMBOL_GPL(rc_core_debug); +module_param_named(debug, rc_core_debug, int, 0644); MODULE_AUTHOR("Mauro Carvalho Chehab "); MODULE_LICENSE("GPL"); diff --git a/drivers/media/rc/rc-raw.c b/drivers/media/rc/rc-raw.c index ab9b1e4071c0..165412fd240e 100644 --- a/drivers/media/rc/rc-raw.c +++ b/drivers/media/rc/rc-raw.c @@ -357,7 +357,7 @@ static void init_decoders(struct work_struct *work) load_lirc_codec(); /* If needed, we may later add some init code. In this case, - it is needed to change the CONFIG_MODULE test at ir-core.h + it is needed to change the CONFIG_MODULE test at rc-core.h */ } #endif diff --git a/drivers/media/rc/streamzap.c b/drivers/media/rc/streamzap.c index f05f5c173fdf..ea2cb636a193 100644 --- a/drivers/media/rc/streamzap.c +++ b/drivers/media/rc/streamzap.c @@ -36,7 +36,7 @@ #include #include #include -#include +#include #define DRIVER_VERSION "1.61" #define DRIVER_NAME "streamzap" diff --git a/drivers/media/rc/winbond-cir.c b/drivers/media/rc/winbond-cir.c index 0ee16ec23bbf..186de5522001 100644 --- a/drivers/media/rc/winbond-cir.c +++ b/drivers/media/rc/winbond-cir.c @@ -50,7 +50,7 @@ #include #include #include -#include +#include #define DRVNAME "winbond-cir" diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 963b3e6c4a97..4b751ef47a83 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -96,7 +96,7 @@ config VIDEO_HELPER_CHIPS_AUTO config VIDEO_IR_I2C tristate "I2C module for IR" if !VIDEO_HELPER_CHIPS_AUTO - depends on I2C && IR_CORE + depends on I2C && RC_CORE default y ---help--- Most boards have an IR chip directly connected via GPIO. However, diff --git a/drivers/media/video/bt8xx/Kconfig b/drivers/media/video/bt8xx/Kconfig index 3c7c0a572c45..7da5c2e1fc12 100644 --- a/drivers/media/video/bt8xx/Kconfig +++ b/drivers/media/video/bt8xx/Kconfig @@ -4,7 +4,7 @@ config VIDEO_BT848 select I2C_ALGOBIT select VIDEO_BTCX select VIDEOBUF_DMA_SG - depends on IR_CORE + depends on RC_CORE select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_MSP3400 if VIDEO_HELPER_CHIPS_AUTO diff --git a/drivers/media/video/bt8xx/bttvp.h b/drivers/media/video/bt8xx/bttvp.h index 0bbdd481e336..b71d04d12085 100644 --- a/drivers/media/video/bt8xx/bttvp.h +++ b/drivers/media/video/bt8xx/bttvp.h @@ -41,7 +41,7 @@ #include #include #include -#include +#include #include #include "bt848.h" diff --git a/drivers/media/video/cx18/Kconfig b/drivers/media/video/cx18/Kconfig index f3c3ccb491fe..d9d2f6ad6ffb 100644 --- a/drivers/media/video/cx18/Kconfig +++ b/drivers/media/video/cx18/Kconfig @@ -2,7 +2,7 @@ config VIDEO_CX18 tristate "Conexant cx23418 MPEG encoder support" depends on VIDEO_V4L2 && DVB_CORE && PCI && I2C && EXPERIMENTAL select I2C_ALGOBIT - depends on IR_CORE + depends on RC_CORE select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X diff --git a/drivers/media/video/cx231xx/Kconfig b/drivers/media/video/cx231xx/Kconfig index d6a235007a7d..ae85a7a7bd73 100644 --- a/drivers/media/video/cx231xx/Kconfig +++ b/drivers/media/video/cx231xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_CX231XX depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM - depends on IR_CORE + depends on RC_CORE select VIDEOBUF_VMALLOC select VIDEO_CX25840 select VIDEO_CX2341X @@ -16,7 +16,7 @@ config VIDEO_CX231XX config VIDEO_CX231XX_RC bool "Conexant cx231xx Remote Controller additional support" - depends on IR_CORE + depends on RC_CORE depends on VIDEO_CX231XX default y ---help--- diff --git a/drivers/media/video/cx231xx/cx231xx.h b/drivers/media/video/cx231xx/cx231xx.h index fcccc9d2a08e..87a20ae6fcc0 100644 --- a/drivers/media/video/cx231xx/cx231xx.h +++ b/drivers/media/video/cx231xx/cx231xx.h @@ -34,7 +34,7 @@ #include #include -#include +#include #include #include diff --git a/drivers/media/video/cx23885/Kconfig b/drivers/media/video/cx23885/Kconfig index e1367b35647a..6b4a516addfe 100644 --- a/drivers/media/video/cx23885/Kconfig +++ b/drivers/media/video/cx23885/Kconfig @@ -5,7 +5,7 @@ config VIDEO_CX23885 select VIDEO_BTCX select VIDEO_TUNER select VIDEO_TVEEPROM - depends on IR_CORE + depends on RC_CORE select VIDEOBUF_DVB select VIDEOBUF_DMA_SG select VIDEO_CX25840 diff --git a/drivers/media/video/cx23885/cx23885-input.c b/drivers/media/video/cx23885/cx23885-input.c index f1bb3a8683c1..e824ba63fdde 100644 --- a/drivers/media/video/cx23885/cx23885-input.c +++ b/drivers/media/video/cx23885/cx23885-input.c @@ -36,7 +36,7 @@ */ #include -#include +#include #include #include "cx23885.h" diff --git a/drivers/media/video/cx23885/cx23885.h b/drivers/media/video/cx23885/cx23885.h index f350d88944e8..fd387229daff 100644 --- a/drivers/media/video/cx23885/cx23885.h +++ b/drivers/media/video/cx23885/cx23885.h @@ -30,7 +30,7 @@ #include #include #include -#include +#include #include "btcx-risc.h" #include "cx23885-reg.h" diff --git a/drivers/media/video/cx23885/cx23888-ir.c b/drivers/media/video/cx23885/cx23888-ir.c index e78e3e4c8112..e37be6fcf67d 100644 --- a/drivers/media/video/cx23885/cx23888-ir.c +++ b/drivers/media/video/cx23885/cx23888-ir.c @@ -26,7 +26,7 @@ #include #include -#include +#include #include "cx23885.h" diff --git a/drivers/media/video/cx25840/cx25840-ir.c b/drivers/media/video/cx25840/cx25840-ir.c index 97a4e9b25fe4..627926f6bde8 100644 --- a/drivers/media/video/cx25840/cx25840-ir.c +++ b/drivers/media/video/cx25840/cx25840-ir.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include "cx25840-core.h" diff --git a/drivers/media/video/cx88/Kconfig b/drivers/media/video/cx88/Kconfig index dbae62935e1f..5c42abdf422f 100644 --- a/drivers/media/video/cx88/Kconfig +++ b/drivers/media/video/cx88/Kconfig @@ -1,6 +1,6 @@ config VIDEO_CX88 tristate "Conexant 2388x (bt878 successor) support" - depends on VIDEO_DEV && PCI && I2C && IR_CORE + depends on VIDEO_DEV && PCI && I2C && RC_CORE select I2C_ALGOBIT select VIDEO_BTCX select VIDEOBUF_DMA_SG diff --git a/drivers/media/video/cx88/cx88-input.c b/drivers/media/video/cx88/cx88-input.c index 3b2ef45d6348..a73033846fce 100644 --- a/drivers/media/video/cx88/cx88-input.c +++ b/drivers/media/video/cx88/cx88-input.c @@ -29,7 +29,7 @@ #include #include "cx88.h" -#include +#include #define MODULE_NAME "cx88xx" diff --git a/drivers/media/video/em28xx/Kconfig b/drivers/media/video/em28xx/Kconfig index 72ea2baba790..985100ea17a4 100644 --- a/drivers/media/video/em28xx/Kconfig +++ b/drivers/media/video/em28xx/Kconfig @@ -3,7 +3,7 @@ config VIDEO_EM28XX depends on VIDEO_DEV && I2C select VIDEO_TUNER select VIDEO_TVEEPROM - depends on IR_CORE + depends on RC_CORE select VIDEOBUF_VMALLOC select VIDEO_SAA711X if VIDEO_HELPER_CHIPS_AUTO select VIDEO_TVP5150 if VIDEO_HELPER_CHIPS_AUTO diff --git a/drivers/media/video/em28xx/em28xx.h b/drivers/media/video/em28xx/em28xx.h index 6a48043a7b92..6f2795a3d4b7 100644 --- a/drivers/media/video/em28xx/em28xx.h +++ b/drivers/media/video/em28xx/em28xx.h @@ -33,7 +33,7 @@ #include #include #include -#include +#include #if defined(CONFIG_VIDEO_EM28XX_DVB) || defined(CONFIG_VIDEO_EM28XX_DVB_MODULE) #include #endif diff --git a/drivers/media/video/ir-kbd-i2c.c b/drivers/media/video/ir-kbd-i2c.c index de0060fa33f1..83662a4ff73c 100644 --- a/drivers/media/video/ir-kbd-i2c.c +++ b/drivers/media/video/ir-kbd-i2c.c @@ -46,7 +46,7 @@ #include #include -#include +#include #include /* ----------------------------------------------------------------------- */ diff --git a/drivers/media/video/ivtv/Kconfig b/drivers/media/video/ivtv/Kconfig index c4f19801c73b..89f65914cc8e 100644 --- a/drivers/media/video/ivtv/Kconfig +++ b/drivers/media/video/ivtv/Kconfig @@ -2,7 +2,7 @@ config VIDEO_IVTV tristate "Conexant cx23416/cx23415 MPEG encoder/decoder support" depends on VIDEO_V4L2 && PCI && I2C select I2C_ALGOBIT - depends on IR_CORE + depends on RC_CORE select VIDEO_TUNER select VIDEO_TVEEPROM select VIDEO_CX2341X diff --git a/drivers/media/video/saa7134/Kconfig b/drivers/media/video/saa7134/Kconfig index e03bff9e4246..380f1b28cfcc 100644 --- a/drivers/media/video/saa7134/Kconfig +++ b/drivers/media/video/saa7134/Kconfig @@ -26,7 +26,7 @@ config VIDEO_SAA7134_ALSA config VIDEO_SAA7134_RC bool "Philips SAA7134 Remote Controller support" - depends on IR_CORE + depends on RC_CORE depends on VIDEO_SAA7134 default y ---help--- diff --git a/drivers/media/video/saa7134/saa7134.h b/drivers/media/video/saa7134/saa7134.h index c6ef95ec68f2..1923f3ca0eab 100644 --- a/drivers/media/video/saa7134/saa7134.h +++ b/drivers/media/video/saa7134/saa7134.h @@ -37,7 +37,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/drivers/media/video/tlg2300/Kconfig b/drivers/media/video/tlg2300/Kconfig index 580580ec9362..645d915267e6 100644 --- a/drivers/media/video/tlg2300/Kconfig +++ b/drivers/media/video/tlg2300/Kconfig @@ -3,7 +3,7 @@ config VIDEO_TLG2300 depends on VIDEO_DEV && I2C && SND && DVB_CORE select VIDEO_TUNER select VIDEO_TVEEPROM - depends on IR_CORE + depends on RC_CORE select VIDEOBUF_VMALLOC select SND_PCM select VIDEOBUF_DVB diff --git a/drivers/staging/cx25821/Kconfig b/drivers/staging/cx25821/Kconfig index f8f2bb01055f..b2656957aa8f 100644 --- a/drivers/staging/cx25821/Kconfig +++ b/drivers/staging/cx25821/Kconfig @@ -5,7 +5,7 @@ config VIDEO_CX25821 select I2C_ALGOBIT select VIDEO_BTCX select VIDEO_TVEEPROM - depends on IR_CORE + depends on RC_CORE select VIDEOBUF_DVB select VIDEOBUF_DMA_SG select VIDEO_CX25840 diff --git a/drivers/staging/go7007/Kconfig b/drivers/staging/go7007/Kconfig index edc90919f3c6..1da57df5cbcb 100644 --- a/drivers/staging/go7007/Kconfig +++ b/drivers/staging/go7007/Kconfig @@ -4,7 +4,7 @@ config VIDEO_GO7007 depends on BKL # please fix depends on SND select VIDEOBUF_DMA_SG - depends on IR_CORE + depends on RC_CORE select VIDEO_TUNER select VIDEO_TVEEPROM select SND_PCM diff --git a/drivers/staging/tm6000/Kconfig b/drivers/staging/tm6000/Kconfig index de7ebb99d8f6..114eec8a630a 100644 --- a/drivers/staging/tm6000/Kconfig +++ b/drivers/staging/tm6000/Kconfig @@ -1,6 +1,6 @@ config VIDEO_TM6000 tristate "TV Master TM5600/6000/6010 driver" - depends on VIDEO_DEV && I2C && INPUT && IR_CORE && USB && EXPERIMENTAL + depends on VIDEO_DEV && I2C && INPUT && RC_CORE && USB && EXPERIMENTAL select VIDEO_TUNER select MEDIA_TUNER_XC2028 select MEDIA_TUNER_XC5000 diff --git a/drivers/staging/tm6000/tm6000-input.c b/drivers/staging/tm6000/tm6000-input.c index 3517d20c1310..58e93d0f304f 100644 --- a/drivers/staging/tm6000/tm6000-input.c +++ b/drivers/staging/tm6000/tm6000-input.c @@ -24,7 +24,7 @@ #include #include -#include +#include #include "tm6000.h" #include "tm6000-regs.h" diff --git a/include/media/ir-core.h b/include/media/ir-core.h deleted file mode 100644 index c5909981b075..000000000000 --- a/include/media/ir-core.h +++ /dev/null @@ -1,211 +0,0 @@ -/* - * Remote Controller core header - * - * Copyright (C) 2009-2010 by Mauro Carvalho Chehab - * - * 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 version 2 of the License. - * - * 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. - */ - -#ifndef _IR_CORE -#define _IR_CORE - -#include -#include -#include -#include -#include - -extern int ir_core_debug; -#define IR_dprintk(level, fmt, arg...) if (ir_core_debug >= level) \ - printk(KERN_DEBUG "%s: " fmt , __func__, ## arg) - -enum rc_driver_type { - RC_DRIVER_SCANCODE = 0, /* Driver or hardware generates a scancode */ - RC_DRIVER_IR_RAW, /* Needs a Infra-Red pulse/space decoder */ -}; - -/** - * struct rc_dev - represents a remote control device - * @dev: driver model's view of this device - * @input_name: name of the input child device - * @input_phys: physical path to the input child device - * @input_id: id of the input child device (struct input_id) - * @driver_name: name of the hardware driver which registered this device - * @map_name: name of the default keymap - * @rc_tab: current scan/key table - * @devno: unique remote control device number - * @raw: additional data for raw pulse/space devices - * @input_dev: the input child device used to communicate events to userspace - * @driver_type: specifies if protocol decoding is done in hardware or software - * @idle: used to keep track of RX state - * @allowed_protos: bitmask with the supported IR_TYPE_* protocols - * @scanmask: some hardware decoders are not capable of providing the full - * scancode to the application. As this is a hardware limit, we can't do - * anything with it. Yet, as the same keycode table can be used with other - * devices, a mask is provided to allow its usage. Drivers should generally - * leave this field in blank - * @priv: driver-specific data - * @keylock: protects the remaining members of the struct - * @keypressed: whether a key is currently pressed - * @keyup_jiffies: time (in jiffies) when the current keypress should be released - * @timer_keyup: timer for releasing a keypress - * @last_keycode: keycode of last keypress - * @last_scancode: scancode of last keypress - * @last_toggle: toggle value of last command - * @timeout: optional time after which device stops sending data - * @min_timeout: minimum timeout supported by device - * @max_timeout: maximum timeout supported by device - * @rx_resolution : resolution (in ns) of input sampler - * @tx_resolution: resolution (in ns) of output sampler - * @change_protocol: allow changing the protocol used on hardware decoders - * @open: callback to allow drivers to enable polling/irq when IR input device - * is opened. - * @close: callback to allow drivers to disable polling/irq when IR input device - * is opened. - * @s_tx_mask: set transmitter mask (for devices with multiple tx outputs) - * @s_tx_carrier: set transmit carrier frequency - * @s_tx_duty_cycle: set transmit duty cycle (0% - 100%) - * @s_rx_carrier: inform driver about carrier it is expected to handle - * @tx_ir: transmit IR - * @s_idle: enable/disable hardware idle mode, upon which, - * device doesn't interrupt host until it sees IR pulses - * @s_learning_mode: enable wide band receiver used for learning - * @s_carrier_report: enable carrier reports - */ -struct rc_dev { - struct device dev; - const char *input_name; - const char *input_phys; - struct input_id input_id; - char *driver_name; - const char *map_name; - struct ir_scancode_table rc_tab; - unsigned long devno; - struct ir_raw_event_ctrl *raw; - struct input_dev *input_dev; - enum rc_driver_type driver_type; - bool idle; - u64 allowed_protos; - u32 scanmask; - void *priv; - spinlock_t keylock; - bool keypressed; - unsigned long keyup_jiffies; - struct timer_list timer_keyup; - u32 last_keycode; - u32 last_scancode; - u8 last_toggle; - u32 timeout; - u32 min_timeout; - u32 max_timeout; - u32 rx_resolution; - u32 tx_resolution; - int (*change_protocol)(struct rc_dev *dev, u64 ir_type); - int (*open)(struct rc_dev *dev); - void (*close)(struct rc_dev *dev); - int (*s_tx_mask)(struct rc_dev *dev, u32 mask); - int (*s_tx_carrier)(struct rc_dev *dev, u32 carrier); - int (*s_tx_duty_cycle)(struct rc_dev *dev, u32 duty_cycle); - int (*s_rx_carrier_range)(struct rc_dev *dev, u32 min, u32 max); - int (*tx_ir)(struct rc_dev *dev, int *txbuf, u32 n); - void (*s_idle)(struct rc_dev *dev, bool enable); - int (*s_learning_mode)(struct rc_dev *dev, int enable); - int (*s_carrier_report) (struct rc_dev *dev, int enable); -}; - -enum raw_event_type { - IR_SPACE = (1 << 0), - IR_PULSE = (1 << 1), - IR_START_EVENT = (1 << 2), - IR_STOP_EVENT = (1 << 3), -}; - -#define to_rc_dev(d) container_of(d, struct rc_dev, dev) - - -void ir_repeat(struct rc_dev *dev); -void ir_keydown(struct rc_dev *dev, int scancode, u8 toggle); -void ir_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle); -void ir_keyup(struct rc_dev *dev); -u32 ir_g_keycode_from_table(struct rc_dev *dev, u32 scancode); - -/* From ir-raw-event.c */ -struct ir_raw_event { - union { - u32 duration; - - struct { - u32 carrier; - u8 duty_cycle; - }; - }; - - unsigned pulse:1; - unsigned reset:1; - unsigned timeout:1; - unsigned carrier_report:1; -}; - -#define DEFINE_IR_RAW_EVENT(event) \ - struct ir_raw_event event = { \ - { .duration = 0 } , \ - .pulse = 0, \ - .reset = 0, \ - .timeout = 0, \ - .carrier_report = 0 } - -static inline void init_ir_raw_event(struct ir_raw_event *ev) -{ - memset(ev, 0, sizeof(*ev)); -} - -#define IR_MAX_DURATION 0xFFFFFFFF /* a bit more than 4 seconds */ - -struct rc_dev *rc_allocate_device(void); -void rc_free_device(struct rc_dev *dev); -int rc_register_device(struct rc_dev *dev); -void rc_unregister_device(struct rc_dev *dev); - -void ir_raw_event_handle(struct rc_dev *dev); -int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev); -int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type); -int ir_raw_event_store_with_filter(struct rc_dev *dev, - struct ir_raw_event *ev); -void ir_raw_event_set_idle(struct rc_dev *dev, bool idle); - -static inline void ir_raw_event_reset(struct rc_dev *dev) -{ - DEFINE_IR_RAW_EVENT(ev); - ev.reset = true; - - ir_raw_event_store(dev, &ev); - ir_raw_event_handle(dev); -} - - -/* extract mask bits out of data and pack them into the result */ -static inline u32 ir_extract_bits(u32 data, u32 mask) -{ - u32 vbit = 1, value = 0; - - do { - if (mask & 1) { - if (data & 1) - value |= vbit; - vbit <<= 1; - } - data >>= 1; - } while (mask >>= 1); - - return value; -} - - -#endif /* _IR_CORE */ diff --git a/include/media/ir-kbd-i2c.h b/include/media/ir-kbd-i2c.h index d27505f2448e..f22b359c37ff 100644 --- a/include/media/ir-kbd-i2c.h +++ b/include/media/ir-kbd-i2c.h @@ -1,7 +1,7 @@ #ifndef _IR_I2C #define _IR_I2C -#include +#include #define DEFAULT_POLLING_INTERVAL 100 /* ms */ diff --git a/include/media/rc-core.h b/include/media/rc-core.h new file mode 100644 index 000000000000..eedb2f0575a4 --- /dev/null +++ b/include/media/rc-core.h @@ -0,0 +1,211 @@ +/* + * Remote Controller core header + * + * Copyright (C) 2009-2010 by Mauro Carvalho Chehab + * + * 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 version 2 of the License. + * + * 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. + */ + +#ifndef _IR_CORE +#define _IR_CORE + +#include +#include +#include +#include +#include + +extern int rc_core_debug; +#define IR_dprintk(level, fmt, arg...) if (rc_core_debug >= level) \ + printk(KERN_DEBUG "%s: " fmt , __func__, ## arg) + +enum rc_driver_type { + RC_DRIVER_SCANCODE = 0, /* Driver or hardware generates a scancode */ + RC_DRIVER_IR_RAW, /* Needs a Infra-Red pulse/space decoder */ +}; + +/** + * struct rc_dev - represents a remote control device + * @dev: driver model's view of this device + * @input_name: name of the input child device + * @input_phys: physical path to the input child device + * @input_id: id of the input child device (struct input_id) + * @driver_name: name of the hardware driver which registered this device + * @map_name: name of the default keymap + * @rc_tab: current scan/key table + * @devno: unique remote control device number + * @raw: additional data for raw pulse/space devices + * @input_dev: the input child device used to communicate events to userspace + * @driver_type: specifies if protocol decoding is done in hardware or software + * @idle: used to keep track of RX state + * @allowed_protos: bitmask with the supported IR_TYPE_* protocols + * @scanmask: some hardware decoders are not capable of providing the full + * scancode to the application. As this is a hardware limit, we can't do + * anything with it. Yet, as the same keycode table can be used with other + * devices, a mask is provided to allow its usage. Drivers should generally + * leave this field in blank + * @priv: driver-specific data + * @keylock: protects the remaining members of the struct + * @keypressed: whether a key is currently pressed + * @keyup_jiffies: time (in jiffies) when the current keypress should be released + * @timer_keyup: timer for releasing a keypress + * @last_keycode: keycode of last keypress + * @last_scancode: scancode of last keypress + * @last_toggle: toggle value of last command + * @timeout: optional time after which device stops sending data + * @min_timeout: minimum timeout supported by device + * @max_timeout: maximum timeout supported by device + * @rx_resolution : resolution (in ns) of input sampler + * @tx_resolution: resolution (in ns) of output sampler + * @change_protocol: allow changing the protocol used on hardware decoders + * @open: callback to allow drivers to enable polling/irq when IR input device + * is opened. + * @close: callback to allow drivers to disable polling/irq when IR input device + * is opened. + * @s_tx_mask: set transmitter mask (for devices with multiple tx outputs) + * @s_tx_carrier: set transmit carrier frequency + * @s_tx_duty_cycle: set transmit duty cycle (0% - 100%) + * @s_rx_carrier: inform driver about carrier it is expected to handle + * @tx_ir: transmit IR + * @s_idle: enable/disable hardware idle mode, upon which, + * device doesn't interrupt host until it sees IR pulses + * @s_learning_mode: enable wide band receiver used for learning + * @s_carrier_report: enable carrier reports + */ +struct rc_dev { + struct device dev; + const char *input_name; + const char *input_phys; + struct input_id input_id; + char *driver_name; + const char *map_name; + struct ir_scancode_table rc_tab; + unsigned long devno; + struct ir_raw_event_ctrl *raw; + struct input_dev *input_dev; + enum rc_driver_type driver_type; + bool idle; + u64 allowed_protos; + u32 scanmask; + void *priv; + spinlock_t keylock; + bool keypressed; + unsigned long keyup_jiffies; + struct timer_list timer_keyup; + u32 last_keycode; + u32 last_scancode; + u8 last_toggle; + u32 timeout; + u32 min_timeout; + u32 max_timeout; + u32 rx_resolution; + u32 tx_resolution; + int (*change_protocol)(struct rc_dev *dev, u64 ir_type); + int (*open)(struct rc_dev *dev); + void (*close)(struct rc_dev *dev); + int (*s_tx_mask)(struct rc_dev *dev, u32 mask); + int (*s_tx_carrier)(struct rc_dev *dev, u32 carrier); + int (*s_tx_duty_cycle)(struct rc_dev *dev, u32 duty_cycle); + int (*s_rx_carrier_range)(struct rc_dev *dev, u32 min, u32 max); + int (*tx_ir)(struct rc_dev *dev, int *txbuf, u32 n); + void (*s_idle)(struct rc_dev *dev, bool enable); + int (*s_learning_mode)(struct rc_dev *dev, int enable); + int (*s_carrier_report) (struct rc_dev *dev, int enable); +}; + +enum raw_event_type { + IR_SPACE = (1 << 0), + IR_PULSE = (1 << 1), + IR_START_EVENT = (1 << 2), + IR_STOP_EVENT = (1 << 3), +}; + +#define to_rc_dev(d) container_of(d, struct rc_dev, dev) + + +void ir_repeat(struct rc_dev *dev); +void ir_keydown(struct rc_dev *dev, int scancode, u8 toggle); +void ir_keydown_notimeout(struct rc_dev *dev, int scancode, u8 toggle); +void ir_keyup(struct rc_dev *dev); +u32 ir_g_keycode_from_table(struct rc_dev *dev, u32 scancode); + +/* From ir-raw-event.c */ +struct ir_raw_event { + union { + u32 duration; + + struct { + u32 carrier; + u8 duty_cycle; + }; + }; + + unsigned pulse:1; + unsigned reset:1; + unsigned timeout:1; + unsigned carrier_report:1; +}; + +#define DEFINE_IR_RAW_EVENT(event) \ + struct ir_raw_event event = { \ + { .duration = 0 } , \ + .pulse = 0, \ + .reset = 0, \ + .timeout = 0, \ + .carrier_report = 0 } + +static inline void init_ir_raw_event(struct ir_raw_event *ev) +{ + memset(ev, 0, sizeof(*ev)); +} + +#define IR_MAX_DURATION 0xFFFFFFFF /* a bit more than 4 seconds */ + +struct rc_dev *rc_allocate_device(void); +void rc_free_device(struct rc_dev *dev); +int rc_register_device(struct rc_dev *dev); +void rc_unregister_device(struct rc_dev *dev); + +void ir_raw_event_handle(struct rc_dev *dev); +int ir_raw_event_store(struct rc_dev *dev, struct ir_raw_event *ev); +int ir_raw_event_store_edge(struct rc_dev *dev, enum raw_event_type type); +int ir_raw_event_store_with_filter(struct rc_dev *dev, + struct ir_raw_event *ev); +void ir_raw_event_set_idle(struct rc_dev *dev, bool idle); + +static inline void ir_raw_event_reset(struct rc_dev *dev) +{ + DEFINE_IR_RAW_EVENT(ev); + ev.reset = true; + + ir_raw_event_store(dev, &ev); + ir_raw_event_handle(dev); +} + + +/* extract mask bits out of data and pack them into the result */ +static inline u32 ir_extract_bits(u32 data, u32 mask) +{ + u32 vbit = 1, value = 0; + + do { + if (mask & 1) { + if (data & 1) + value |= vbit; + vbit <<= 1; + } + data >>= 1; + } while (mask >>= 1); + + return value; +} + + +#endif /* _IR_CORE */ -- cgit v1.2.3 From 745da4280f272840976d47afba22ed853f07e1b2 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 25 Dec 2010 07:27:47 -0300 Subject: [media] usbvideo: deprecate the vicam driver Move usbvideo to staging and mark it deprecated. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 2 - drivers/media/video/Makefile | 4 - drivers/media/video/usbvideo/Kconfig | 12 - drivers/media/video/usbvideo/Makefile | 2 - drivers/media/video/usbvideo/usbvideo.c | 2230 ------------------------------- drivers/media/video/usbvideo/usbvideo.h | 395 ------ drivers/media/video/usbvideo/vicam.c | 952 ------------- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/usbvideo/Kconfig | 15 + drivers/staging/usbvideo/Makefile | 2 + drivers/staging/usbvideo/usbvideo.c | 2230 +++++++++++++++++++++++++++++++ drivers/staging/usbvideo/usbvideo.h | 395 ++++++ drivers/staging/usbvideo/vicam.c | 952 +++++++++++++ 14 files changed, 3597 insertions(+), 3597 deletions(-) delete mode 100644 drivers/media/video/usbvideo/Kconfig delete mode 100644 drivers/media/video/usbvideo/Makefile delete mode 100644 drivers/media/video/usbvideo/usbvideo.c delete mode 100644 drivers/media/video/usbvideo/usbvideo.h delete mode 100644 drivers/media/video/usbvideo/vicam.c create mode 100644 drivers/staging/usbvideo/Kconfig create mode 100644 drivers/staging/usbvideo/Makefile create mode 100644 drivers/staging/usbvideo/usbvideo.c create mode 100644 drivers/staging/usbvideo/usbvideo.h create mode 100644 drivers/staging/usbvideo/vicam.c (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 4b751ef47a83..3655b30254d9 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -915,8 +915,6 @@ source "drivers/media/video/cx231xx/Kconfig" source "drivers/media/video/usbvision/Kconfig" -source "drivers/media/video/usbvideo/Kconfig" - source "drivers/media/video/et61x251/Kconfig" config USB_SE401 diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index ace7b6eaf5b8..2f2e3ac877b4 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -133,10 +133,6 @@ obj-$(CONFIG_USB_GSPCA) += gspca/ obj-$(CONFIG_VIDEO_HDPVR) += hdpvr/ -obj-$(CONFIG_USB_IBMCAM) += usbvideo/ -obj-$(CONFIG_USB_KONICAWC) += usbvideo/ -obj-$(CONFIG_USB_VICAM) += usbvideo/ -obj-$(CONFIG_USB_QUICKCAM_MESSENGER) += usbvideo/ obj-$(CONFIG_USB_S2255) += s2255drv.o obj-$(CONFIG_VIDEO_IVTV) += ivtv/ diff --git a/drivers/media/video/usbvideo/Kconfig b/drivers/media/video/usbvideo/Kconfig deleted file mode 100644 index 357860d42a8b..000000000000 --- a/drivers/media/video/usbvideo/Kconfig +++ /dev/null @@ -1,12 +0,0 @@ -config VIDEO_USBVIDEO - tristate - -config USB_VICAM - tristate "USB 3com HomeConnect (aka vicam) support (EXPERIMENTAL)" - depends on VIDEO_V4L1 && EXPERIMENTAL - select VIDEO_USBVIDEO - ---help--- - Say Y here if you have 3com homeconnect camera (vicam). - - To compile this driver as a module, choose M here: the - module will be called vicam. diff --git a/drivers/media/video/usbvideo/Makefile b/drivers/media/video/usbvideo/Makefile deleted file mode 100644 index 3c99a9a2d8d3..000000000000 --- a/drivers/media/video/usbvideo/Makefile +++ /dev/null @@ -1,2 +0,0 @@ -obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o -obj-$(CONFIG_USB_VICAM) += vicam.o diff --git a/drivers/media/video/usbvideo/usbvideo.c b/drivers/media/video/usbvideo/usbvideo.c deleted file mode 100644 index f1fcf9744961..000000000000 --- a/drivers/media/video/usbvideo/usbvideo.c +++ /dev/null @@ -1,2230 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include "usbvideo.h" - -#if defined(MAP_NR) -#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */ -#endif - -static int video_nr = -1; -module_param(video_nr, int, 0); - -/* - * Local prototypes. - */ -static void usbvideo_Disconnect(struct usb_interface *intf); -static void usbvideo_CameraRelease(struct uvd *uvd); - -static long usbvideo_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg); -static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma); -static int usbvideo_v4l_open(struct file *file); -static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos); -static int usbvideo_v4l_close(struct file *file); - -static int usbvideo_StartDataPump(struct uvd *uvd); -static void usbvideo_StopDataPump(struct uvd *uvd); -static int usbvideo_GetFrame(struct uvd *uvd, int frameNum); -static int usbvideo_NewFrame(struct uvd *uvd, int framenum); -static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, - struct usbvideo_frame *frame); - -/*******************************/ -/* Memory management functions */ -/*******************************/ -static void *usbvideo_rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void usbvideo_rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -static void RingQueue_Initialize(struct RingQueue *rq) -{ - assert(rq != NULL); - init_waitqueue_head(&rq->wqh); -} - -static void RingQueue_Allocate(struct RingQueue *rq, int rqLen) -{ - /* Make sure the requested size is a power of 2 and - round up if necessary. This allows index wrapping - using masks rather than modulo */ - - int i = 1; - assert(rq != NULL); - assert(rqLen > 0); - - while(rqLen >> i) - i++; - if(rqLen != 1 << (i-1)) - rqLen = 1 << i; - - rq->length = rqLen; - rq->ri = rq->wi = 0; - rq->queue = usbvideo_rvmalloc(rq->length); - assert(rq->queue != NULL); -} - -static int RingQueue_IsAllocated(const struct RingQueue *rq) -{ - if (rq == NULL) - return 0; - return (rq->queue != NULL) && (rq->length > 0); -} - -static void RingQueue_Free(struct RingQueue *rq) -{ - assert(rq != NULL); - if (RingQueue_IsAllocated(rq)) { - usbvideo_rvfree(rq->queue, rq->length); - rq->queue = NULL; - rq->length = 0; - } -} - -int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len) -{ - int rql, toread; - - assert(rq != NULL); - assert(dst != NULL); - - rql = RingQueue_GetLength(rq); - if(!rql) - return 0; - - /* Clip requested length to available data */ - if(len > rql) - len = rql; - - toread = len; - if(rq->ri > rq->wi) { - /* Read data from tail */ - int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri; - memcpy(dst, rq->queue + rq->ri, read); - toread -= read; - dst += read; - rq->ri = (rq->ri + read) & (rq->length-1); - } - if(toread) { - /* Read data from head */ - memcpy(dst, rq->queue + rq->ri, toread); - rq->ri = (rq->ri + toread) & (rq->length-1); - } - return len; -} - -EXPORT_SYMBOL(RingQueue_Dequeue); - -int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n) -{ - int enqueued = 0; - - assert(rq != NULL); - assert(cdata != NULL); - assert(rq->length > 0); - while (n > 0) { - int m, q_avail; - - /* Calculate the largest chunk that fits the tail of the ring */ - q_avail = rq->length - rq->wi; - if (q_avail <= 0) { - rq->wi = 0; - q_avail = rq->length; - } - m = n; - assert(q_avail > 0); - if (m > q_avail) - m = q_avail; - - memcpy(rq->queue + rq->wi, cdata, m); - RING_QUEUE_ADVANCE_INDEX(rq, wi, m); - cdata += m; - enqueued += m; - n -= m; - } - return enqueued; -} - -EXPORT_SYMBOL(RingQueue_Enqueue); - -static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq) -{ - assert(rq != NULL); - interruptible_sleep_on(&rq->wqh); -} - -void RingQueue_WakeUpInterruptible(struct RingQueue *rq) -{ - assert(rq != NULL); - if (waitqueue_active(&rq->wqh)) - wake_up_interruptible(&rq->wqh); -} - -EXPORT_SYMBOL(RingQueue_WakeUpInterruptible); - -void RingQueue_Flush(struct RingQueue *rq) -{ - assert(rq != NULL); - rq->ri = 0; - rq->wi = 0; -} - -EXPORT_SYMBOL(RingQueue_Flush); - - -/* - * usbvideo_VideosizeToString() - * - * This procedure converts given videosize value to readable string. - * - * History: - * 07-Aug-2000 Created. - * 19-Oct-2000 Reworked for usbvideo module. - */ -static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) -{ - char tmp[40]; - int n; - - n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); - assert(n < sizeof(tmp)); - if ((buf == NULL) || (bufLen < n)) - err("usbvideo_VideosizeToString: buffer is too small."); - else - memmove(buf, tmp, n); -} - -/* - * usbvideo_OverlayChar() - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, - int x, int y, int ch) -{ - static const unsigned short digits[16] = { - 0xF6DE, /* 0 */ - 0x2492, /* 1 */ - 0xE7CE, /* 2 */ - 0xE79E, /* 3 */ - 0xB792, /* 4 */ - 0xF39E, /* 5 */ - 0xF3DE, /* 6 */ - 0xF492, /* 7 */ - 0xF7DE, /* 8 */ - 0xF79E, /* 9 */ - 0x77DA, /* a */ - 0xD75C, /* b */ - 0xF24E, /* c */ - 0xD6DC, /* d */ - 0xF34E, /* e */ - 0xF348 /* f */ - }; - unsigned short digit; - int ix, iy; - int value; - - if ((uvd == NULL) || (frame == NULL)) - return; - - value = hex_to_bin(ch); - if (value < 0) - return; - digit = digits[value]; - - for (iy=0; iy < 5; iy++) { - for (ix=0; ix < 3; ix++) { - if (digit & 0x8000) { - if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) { -/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); - } - } - digit = digit << 1; - } - } -} - -/* - * usbvideo_OverlayString() - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, - int x, int y, const char *str) -{ - while (*str) { - usbvideo_OverlayChar(uvd, frame, x, y, *str); - str++; - x += 4; /* 3 pixels character + 1 space */ - } -} - -/* - * usbvideo_OverlayStats() - * - * Overlays important debugging information. - * - * History: - * 01-Feb-2000 Created. - */ -static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame) -{ - const int y_diff = 8; - char tmp[16]; - int x = 10, y=10; - long i, j, barLength; - const int qi_x1 = 60, qi_y1 = 10; - const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; - - /* Call the user callback, see if we may proceed after that */ - if (VALID_CALLBACK(uvd, overlayHook)) { - if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) - return; - } - - /* - * We draw a (mostly) hollow rectangle with qi_xxx coordinates. - * Left edge symbolizes the queue index 0; right edge symbolizes - * the full capacity of the queue. - */ - barLength = qi_x2 - qi_x1 - 2; - if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) { -/* TODO */ long u_lo, u_hi, q_used; - long m_ri, m_wi, m_lo, m_hi; - - /* - * Determine fill zones (used areas of the queue): - * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length - * - * if u_lo < 0 then there is no first filler. - */ - - q_used = RingQueue_GetLength(&uvd->dp); - if ((uvd->dp.ri + q_used) >= uvd->dp.length) { - u_hi = uvd->dp.length; - u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1); - } else { - u_hi = (q_used + uvd->dp.ri); - u_lo = -1; - } - - /* Convert byte indices into screen units */ - m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); - m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); - m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; - m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); - - for (j=qi_y1; j < (qi_y1 + qi_h); j++) { - for (i=qi_x1; i < qi_x2; i++) { - /* Draw border lines */ - if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || - (i == qi_x1) || (i == (qi_x2 - 1))) { - RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); - continue; - } - /* For all other points the Y coordinate does not matter */ - if ((i >= m_ri) && (i <= (m_ri + 3))) { - RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); - } else if ((i >= m_wi) && (i <= (m_wi + 3))) { - RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); - } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) - RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); - } - } - } - - sprintf(tmp, "%8lx", uvd->stats.frame_num); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.urb_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.urb_length); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.data_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.header_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8lx", uvd->stats.iso_err_count); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.colour); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.hue); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; - - sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); - usbvideo_OverlayString(uvd, frame, x, y, tmp); - y += y_diff; -} - -/* - * usbvideo_ReportStatistics() - * - * This procedure prints packet and transfer statistics. - * - * History: - * 14-Jan-2000 Corrected default multiplier. - */ -static void usbvideo_ReportStatistics(const struct uvd *uvd) -{ - if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { - unsigned long allPackets, badPackets, goodPackets, percent; - allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; - badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; - goodPackets = allPackets - badPackets; - /* Calculate percentage wisely, remember integer limits */ - assert(allPackets != 0); - if (goodPackets < (((unsigned long)-1)/100)) - percent = (100 * goodPackets) / allPackets; - else - percent = goodPackets / (allPackets / 100); - dev_info(&uvd->dev->dev, - "Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%\n", - allPackets, badPackets, percent); - if (uvd->iso_packet_len > 0) { - unsigned long allBytes, xferBytes; - char multiplier = ' '; - allBytes = allPackets * uvd->iso_packet_len; - xferBytes = uvd->stats.data_count; - assert(allBytes != 0); - if (xferBytes < (((unsigned long)-1)/100)) - percent = (100 * xferBytes) / allBytes; - else - percent = xferBytes / (allBytes / 100); - /* Scale xferBytes for easy reading */ - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'K'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'M'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'G'; - if (xferBytes > 10*1024) { - xferBytes /= 1024; - multiplier = 'T'; - } - } - } - } - dev_info(&uvd->dev->dev, - "Transfer Statistics: Transferred=%lu%cB Usage=%lu%%\n", - xferBytes, multiplier, percent); - } - } -} - -/* - * usbvideo_TestPattern() - * - * Procedure forms a test pattern (yellow grid on blue background). - * - * Parameters: - * fullframe: if TRUE then entire frame is filled, otherwise the procedure - * continues from the current scanline. - * pmode 0: fill the frame with solid blue color (like on VCR or TV) - * 1: Draw a colored grid - * - * History: - * 01-Feb-2000 Created. - */ -void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) -{ - struct usbvideo_frame *frame; - int num_cell = 0; - int scan_length = 0; - static int num_pass; - - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return; - } - if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { - err("%s: uvd->curframe=%d.", __func__, uvd->curframe); - return; - } - - /* Grab the current frame */ - frame = &uvd->frame[uvd->curframe]; - - /* Optionally start at the beginning */ - if (fullframe) { - frame->curline = 0; - frame->seqRead_Length = 0; - } -#if 0 - { /* For debugging purposes only */ - char tmp[20]; - usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); - dev_info(&uvd->dev->dev, "testpattern: frame=%s\n", tmp); - } -#endif - /* Form every scan line */ - for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) { - int i; - unsigned char *f = frame->data + - (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline); - for (i=0; i < VIDEOSIZE_X(frame->request); i++) { - unsigned char cb=0x80; - unsigned char cg = 0; - unsigned char cr = 0; - - if (pmode == 1) { - if (frame->curline % 32 == 0) - cb = 0, cg = cr = 0xFF; - else if (i % 32 == 0) { - if (frame->curline % 32 == 1) - num_cell++; - cb = 0, cg = cr = 0xFF; - } else { - cb = ((num_cell*7) + num_pass) & 0xFF; - cg = ((num_cell*5) + num_pass*2) & 0xFF; - cr = ((num_cell*3) + num_pass*3) & 0xFF; - } - } else { - /* Just the blue screen */ - } - - *f++ = cb; - *f++ = cg; - *f++ = cr; - scan_length += 3; - } - } - - frame->frameState = FrameState_Done; - frame->seqRead_Length += scan_length; - ++num_pass; - - /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ - usbvideo_OverlayStats(uvd, frame); -} - -EXPORT_SYMBOL(usbvideo_TestPattern); - - -#ifdef DEBUG -/* - * usbvideo_HexDump() - * - * A debugging tool. Prints hex dumps. - * - * History: - * 29-Jul-2000 Added printing of offsets. - */ -void usbvideo_HexDump(const unsigned char *data, int len) -{ - const int bytes_per_line = 32; - char tmp[128]; /* 32*3 + 5 */ - int i, k; - - for (i=k=0; len > 0; i++, len--) { - if (i > 0 && ((i % bytes_per_line) == 0)) { - printk("%s\n", tmp); - k=0; - } - if ((i % bytes_per_line) == 0) - k += sprintf(&tmp[k], "%04x: ", i); - k += sprintf(&tmp[k], "%02x ", data[i]); - } - if (k > 0) - printk("%s\n", tmp); -} - -EXPORT_SYMBOL(usbvideo_HexDump); - -#endif - -/* ******************************************************************** */ - -/* XXX: this piece of crap really wants some error handling.. */ -static int usbvideo_ClientIncModCount(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return -EINVAL; - } - if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __func__); - return -EINVAL; - } - if (!try_module_get(uvd->handle->md_module)) { - err("%s: try_module_get() == 0", __func__); - return -ENODEV; - } - return 0; -} - -static void usbvideo_ClientDecModCount(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: uvd == NULL", __func__); - return; - } - if (uvd->handle == NULL) { - err("%s: uvd->handle == NULL", __func__); - return; - } - if (uvd->handle->md_module == NULL) { - err("%s: uvd->handle->md_module == NULL", __func__); - return; - } - module_put(uvd->handle->md_module); -} - -int usbvideo_register( - struct usbvideo **pCams, - const int num_cams, - const int num_extra, - const char *driverName, - const struct usbvideo_cb *cbTbl, - struct module *md, - const struct usb_device_id *id_table) -{ - struct usbvideo *cams; - int i, base_size, result; - - /* Check parameters for sanity */ - if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { - err("%s: Illegal call", __func__); - return -EINVAL; - } - - /* Check registration callback - must be set! */ - if (cbTbl->probe == NULL) { - err("%s: probe() is required!", __func__); - return -EINVAL; - } - - base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo); - cams = kzalloc(base_size, GFP_KERNEL); - if (cams == NULL) { - err("Failed to allocate %d. bytes for usbvideo struct", base_size); - return -ENOMEM; - } - dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", - __func__, cams, base_size, num_cams); - - /* Copy callbacks, apply defaults for those that are not set */ - memmove(&cams->cb, cbTbl, sizeof(cams->cb)); - if (cams->cb.getFrame == NULL) - cams->cb.getFrame = usbvideo_GetFrame; - if (cams->cb.disconnect == NULL) - cams->cb.disconnect = usbvideo_Disconnect; - if (cams->cb.startDataPump == NULL) - cams->cb.startDataPump = usbvideo_StartDataPump; - if (cams->cb.stopDataPump == NULL) - cams->cb.stopDataPump = usbvideo_StopDataPump; - - cams->num_cameras = num_cams; - cams->cam = (struct uvd *) &cams[1]; - cams->md_module = md; - mutex_init(&cams->lock); /* to 1 == available */ - - for (i = 0; i < num_cams; i++) { - struct uvd *up = &cams->cam[i]; - - up->handle = cams; - - /* Allocate user_data separately because of kmalloc's limits */ - if (num_extra > 0) { - up->user_size = num_cams * num_extra; - up->user_data = kmalloc(up->user_size, GFP_KERNEL); - if (up->user_data == NULL) { - err("%s: Failed to allocate user_data (%d. bytes)", - __func__, up->user_size); - while (i) { - up = &cams->cam[--i]; - kfree(up->user_data); - } - kfree(cams); - return -ENOMEM; - } - dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", - __func__, i, up->user_data, up->user_size); - } - } - - /* - * Register ourselves with USB stack. - */ - strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown"); - cams->usbdrv.name = cams->drvName; - cams->usbdrv.probe = cams->cb.probe; - cams->usbdrv.disconnect = cams->cb.disconnect; - cams->usbdrv.id_table = id_table; - - /* - * Update global handle to usbvideo. This is very important - * because probe() can be called before usb_register() returns. - * If the handle is not yet updated then the probe() will fail. - */ - *pCams = cams; - result = usb_register(&cams->usbdrv); - if (result) { - for (i = 0; i < num_cams; i++) { - struct uvd *up = &cams->cam[i]; - kfree(up->user_data); - } - kfree(cams); - } - - return result; -} - -EXPORT_SYMBOL(usbvideo_register); - -/* - * usbvideo_Deregister() - * - * Procedure frees all usbvideo and user data structures. Be warned that - * if you had some dynamically allocated components in ->user field then - * you should free them before calling here. - */ -void usbvideo_Deregister(struct usbvideo **pCams) -{ - struct usbvideo *cams; - int i; - - if (pCams == NULL) { - err("%s: pCams == NULL", __func__); - return; - } - cams = *pCams; - if (cams == NULL) { - err("%s: cams == NULL", __func__); - return; - } - - dbg("%s: Deregistering %s driver.", __func__, cams->drvName); - usb_deregister(&cams->usbdrv); - - dbg("%s: Deallocating cams=$%p (%d. cameras)", __func__, cams, cams->num_cameras); - for (i=0; i < cams->num_cameras; i++) { - struct uvd *up = &cams->cam[i]; - int warning = 0; - - if (up->user_data != NULL) { - if (up->user_size <= 0) - ++warning; - } else { - if (up->user_size > 0) - ++warning; - } - if (warning) { - err("%s: Warning: user_data=$%p user_size=%d.", - __func__, up->user_data, up->user_size); - } else { - dbg("%s: Freeing %d. $%p->user_data=$%p", - __func__, i, up, up->user_data); - kfree(up->user_data); - } - } - /* Whole array was allocated in one chunk */ - dbg("%s: Freed %d uvd structures", - __func__, cams->num_cameras); - kfree(cams); - *pCams = NULL; -} - -EXPORT_SYMBOL(usbvideo_Deregister); - -/* - * usbvideo_Disconnect() - * - * This procedure stops all driver activity. Deallocation of - * the interface-private structure (pointed by 'ptr') is done now - * (if we don't have any open files) or later, when those files - * are closed. After that driver should be removable. - * - * This code handles surprise removal. The uvd->user is a counter which - * increments on open() and decrements on close(). If we see here that - * this counter is not 0 then we have a client who still has us opened. - * We set uvd->remove_pending flag as early as possible, and after that - * all access to the camera will gracefully fail. These failures should - * prompt client to (eventually) close the video device, and then - in - * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter. - * - * History: - * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone. - * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close() - * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - * 19-Oct-2000 Moved to usbvideo module. - */ -static void usbvideo_Disconnect(struct usb_interface *intf) -{ - struct uvd *uvd = usb_get_intfdata (intf); - int i; - - if (uvd == NULL) { - err("%s($%p): Illegal call.", __func__, intf); - return; - } - - usb_set_intfdata (intf, NULL); - - usbvideo_ClientIncModCount(uvd); - if (uvd->debug > 0) - dev_info(&intf->dev, "%s(%p.)\n", __func__, intf); - - mutex_lock(&uvd->lock); - uvd->remove_pending = 1; /* Now all ISO data will be ignored */ - - /* At this time we ask to cancel outstanding URBs */ - GET_CALLBACK(uvd, stopDataPump)(uvd); - - for (i=0; i < USBVIDEO_NUMSBUF; i++) - usb_free_urb(uvd->sbuf[i].urb); - - usb_put_dev(uvd->dev); - uvd->dev = NULL; /* USB device is no more */ - - video_unregister_device(&uvd->vdev); - if (uvd->debug > 0) - dev_info(&intf->dev, "%s: Video unregistered.\n", __func__); - - if (uvd->user) - dev_info(&intf->dev, "%s: In use, disconnect pending.\n", - __func__); - else - usbvideo_CameraRelease(uvd); - mutex_unlock(&uvd->lock); - dev_info(&intf->dev, "USB camera disconnected.\n"); - - usbvideo_ClientDecModCount(uvd); -} - -/* - * usbvideo_CameraRelease() - * - * This code does final release of uvd. This happens - * after the device is disconnected -and- all clients - * closed their files. - * - * History: - * 27-Jan-2000 Created. - */ -static void usbvideo_CameraRelease(struct uvd *uvd) -{ - if (uvd == NULL) { - err("%s: Illegal call", __func__); - return; - } - - RingQueue_Free(&uvd->dp); - if (VALID_CALLBACK(uvd, userFree)) - GET_CALLBACK(uvd, userFree)(uvd); - uvd->uvd_used = 0; /* This is atomic, no need to take mutex */ -} - -/* - * usbvideo_find_struct() - * - * This code searches the array of preallocated (static) structures - * and returns index of the first one that isn't in use. Returns -1 - * if there are no free structures. - * - * History: - * 27-Jan-2000 Created. - */ -static int usbvideo_find_struct(struct usbvideo *cams) -{ - int u, rv = -1; - - if (cams == NULL) { - err("No usbvideo handle?"); - return -1; - } - mutex_lock(&cams->lock); - for (u = 0; u < cams->num_cameras; u++) { - struct uvd *uvd = &cams->cam[u]; - if (!uvd->uvd_used) /* This one is free */ - { - uvd->uvd_used = 1; /* In use now */ - mutex_init(&uvd->lock); /* to 1 == available */ - uvd->dev = NULL; - rv = u; - break; - } - } - mutex_unlock(&cams->lock); - return rv; -} - -static const struct v4l2_file_operations usbvideo_fops = { - .owner = THIS_MODULE, - .open = usbvideo_v4l_open, - .release =usbvideo_v4l_close, - .read = usbvideo_v4l_read, - .mmap = usbvideo_v4l_mmap, - .ioctl = usbvideo_v4l_ioctl, -}; -static const struct video_device usbvideo_template = { - .fops = &usbvideo_fops, -}; - -struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) -{ - int i, devnum; - struct uvd *uvd = NULL; - - if (cams == NULL) { - err("No usbvideo handle?"); - return NULL; - } - - devnum = usbvideo_find_struct(cams); - if (devnum == -1) { - err("IBM USB camera driver: Too many devices!"); - return NULL; - } - uvd = &cams->cam[devnum]; - dbg("Device entry #%d. at $%p", devnum, uvd); - - /* Not relying upon caller we increase module counter ourselves */ - usbvideo_ClientIncModCount(uvd); - - mutex_lock(&uvd->lock); - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); - if (uvd->sbuf[i].urb == NULL) { - err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); - uvd->uvd_used = 0; - uvd = NULL; - goto allocate_done; - } - } - uvd->user=0; - uvd->remove_pending = 0; - uvd->last_error = 0; - RingQueue_Initialize(&uvd->dp); - - /* Initialize video device structure */ - uvd->vdev = usbvideo_template; - sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName); - /* - * The client is free to overwrite those because we - * return control to the client's probe function right now. - */ -allocate_done: - mutex_unlock(&uvd->lock); - usbvideo_ClientDecModCount(uvd); - return uvd; -} - -EXPORT_SYMBOL(usbvideo_AllocateDevice); - -int usbvideo_RegisterVideoDevice(struct uvd *uvd) -{ - char tmp1[20], tmp2[20]; /* Buffers for printing */ - - if (uvd == NULL) { - err("%s: Illegal call.", __func__); - return -EINVAL; - } - if (uvd->video_endp == 0) { - dev_info(&uvd->dev->dev, - "%s: No video endpoint specified; data pump disabled.\n", - __func__); - } - if (uvd->paletteBits == 0) { - err("%s: No palettes specified!", __func__); - return -EINVAL; - } - if (uvd->defaultPalette == 0) { - dev_info(&uvd->dev->dev, "%s: No default palette!\n", - __func__); - } - - uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * - VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL; - usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize); - usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas); - - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "%s: iface=%d. endpoint=$%02x paletteBits=$%08lx\n", - __func__, uvd->iface, uvd->video_endp, - uvd->paletteBits); - } - if (uvd->dev == NULL) { - err("%s: uvd->dev == NULL", __func__); - return -EINVAL; - } - uvd->vdev.parent = &uvd->dev->dev; - uvd->vdev.release = video_device_release_empty; - if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { - err("%s: video_register_device failed", __func__); - return -EPIPE; - } - if (uvd->debug > 1) { - dev_info(&uvd->dev->dev, - "%s: video_register_device() successful\n", __func__); - } - - dev_info(&uvd->dev->dev, "%s on %s: canvas=%s videosize=%s\n", - (uvd->handle != NULL) ? uvd->handle->drvName : "???", - video_device_node_name(&uvd->vdev), tmp2, tmp1); - - usb_get_dev(uvd->dev); - return 0; -} - -EXPORT_SYMBOL(usbvideo_RegisterVideoDevice); - -/* ******************************************************************** */ - -static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct uvd *uvd = file->private_data; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return -EFAULT; - - if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) - return -EINVAL; - - pos = (unsigned long) uvd->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -/* - * usbvideo_v4l_open() - * - * This is part of Video 4 Linux API. The driver can be opened by one - * client only (checks internal counter 'uvdser'). The procedure - * then allocates buffers needed for video processing. - * - * History: - * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the - * camera is also initialized here (once per connect), at - * expense of V4L client (it waits on open() call). - * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. - * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). - */ -static int usbvideo_v4l_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct uvd *uvd = (struct uvd *) dev; - const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; - int i, errCode = 0; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - - if (usbvideo_ClientIncModCount(uvd) < 0) - return -ENODEV; - mutex_lock(&uvd->lock); - - if (uvd->user) { - err("%s: Someone tried to open an already opened device!", __func__); - errCode = -EBUSY; - } else { - /* Clear statistics */ - memset(&uvd->stats, 0, sizeof(uvd->stats)); - - /* Clean pointers so we know if we allocated something */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) - uvd->sbuf[i].data = NULL; - - /* Allocate memory for the frame buffers */ - uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; - uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); - RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); - if ((uvd->fbuf == NULL) || - (!RingQueue_IsAllocated(&uvd->dp))) { - err("%s: Failed to allocate fbuf or dp", __func__); - errCode = -ENOMEM; - } else { - /* Allocate all buffers */ - for (i=0; i < USBVIDEO_NUMFRAMES; i++) { - uvd->frame[i].frameState = FrameState_Unused; - uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); - /* - * Set default sizes in case IOCTL (VIDIOCMCAPTURE) - * is not used (using read() instead). - */ - uvd->frame[i].canvas = uvd->canvas; - uvd->frame[i].seqRead_Index = 0; - } - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); - if (uvd->sbuf[i].data == NULL) { - errCode = -ENOMEM; - break; - } - } - } - if (errCode != 0) { - /* Have to free all that memory */ - if (uvd->fbuf != NULL) { - usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); - uvd->fbuf = NULL; - } - RingQueue_Free(&uvd->dp); - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - kfree(uvd->sbuf[i].data); - uvd->sbuf[i].data = NULL; - } - } - } - - /* If so far no errors then we shall start the camera */ - if (errCode == 0) { - /* Start data pump if we have valid endpoint */ - if (uvd->video_endp != 0) - errCode = GET_CALLBACK(uvd, startDataPump)(uvd); - if (errCode == 0) { - if (VALID_CALLBACK(uvd, setupOnOpen)) { - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: setupOnOpen callback\n", - __func__); - errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); - if (errCode < 0) { - err("%s: setupOnOpen callback failed (%d.).", - __func__, errCode); - } else if (uvd->debug > 1) { - dev_info(&uvd->dev->dev, - "%s: setupOnOpen callback successful\n", - __func__); - } - } - if (errCode == 0) { - uvd->settingsAdjusted = 0; - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: Open succeeded.\n", - __func__); - uvd->user++; - file->private_data = uvd; - } - } - } - mutex_unlock(&uvd->lock); - if (errCode != 0) - usbvideo_ClientDecModCount(uvd); - if (uvd->debug > 0) - dev_info(&uvd->dev->dev, "%s: Returning %d.\n", __func__, - errCode); - return errCode; -} - -/* - * usbvideo_v4l_close() - * - * This is part of Video 4 Linux API. The procedure - * stops streaming and deallocates all buffers that were earlier - * allocated in usbvideo_v4l_open(). - * - * History: - * 22-Jan-2000 Moved scratch buffer deallocation here. - * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. - * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. - */ -static int usbvideo_v4l_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct uvd *uvd = (struct uvd *) dev; - int i; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); - - mutex_lock(&uvd->lock); - GET_CALLBACK(uvd, stopDataPump)(uvd); - usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); - uvd->fbuf = NULL; - RingQueue_Free(&uvd->dp); - - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - kfree(uvd->sbuf[i].data); - uvd->sbuf[i].data = NULL; - } - -#if USBVIDEO_REPORT_STATS - usbvideo_ReportStatistics(uvd); -#endif - - uvd->user--; - if (uvd->remove_pending) { - if (uvd->debug > 0) - dev_info(&uvd->dev->dev, "%s: Final disconnect.\n", - __func__); - usbvideo_CameraRelease(uvd); - } - mutex_unlock(&uvd->lock); - usbvideo_ClientDecModCount(uvd); - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s: Completed.\n", __func__); - file->private_data = NULL; - return 0; -} - -/* - * usbvideo_v4l_ioctl() - * - * This is part of Video 4 Linux API. The procedure handles ioctl() calls. - * - * History: - * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. - */ -static long usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct uvd *uvd = file->private_data; - - if (!CAMERA_IS_OPERATIONAL(uvd)) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - *b = uvd->vcap; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - *v = uvd->vchan; - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - if (v->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *pic = arg; - *pic = uvd->vpic; - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *pic = arg; - /* - * Use temporary 'video_picture' structure to preserve our - * own settings (such as color depth, palette) that we - * aren't allowing everyone (V4L client) to change. - */ - uvd->vpic.brightness = pic->brightness; - uvd->vpic.hue = pic->hue; - uvd->vpic.colour = pic->colour; - uvd->vpic.contrast = pic->contrast; - uvd->settingsAdjusted = 0; /* Will force new settings */ - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if(VALID_CALLBACK(uvd, setVideoMode)) { - return GET_CALLBACK(uvd, setVideoMode)(uvd, vw); - } - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (vw->width != VIDEOSIZE_X(uvd->canvas)) - return -EINVAL; - if (vw->height != VIDEOSIZE_Y(uvd->canvas)) - return -EINVAL; - - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; - vw->y = 0; - vw->width = VIDEOSIZE_X(uvd->videosize); - vw->height = VIDEOSIZE_Y(uvd->videosize); - vw->chromakey = 0; - if (VALID_CALLBACK(uvd, getFPS)) - vw->flags = GET_CALLBACK(uvd, getFPS)(uvd); - else - vw->flags = 10; /* FIXME: do better! */ - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES; - vm->frames = USBVIDEO_NUMFRAMES; - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) - vm->offsets[i] = i * uvd->max_frame_size; - - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - - if (uvd->debug >= 1) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.\n", - vm->frame, vm->width, vm->height, vm->format); - } - /* - * Check if the requested size is supported. If the requestor - * requests too big a frame then we may be tricked into accessing - * outside of own preallocated frame buffer (in uvd->frame). - * This will cause oops or a security hole. Theoretically, we - * could only clamp the size down to acceptable bounds, but then - * we'd need to figure out how to insert our smaller buffer into - * larger caller's buffer... this is not an easy question. So we - * here just flatly reject too large requests, assuming that the - * caller will resubmit with smaller size. Callers should know - * what size we support (returned by VIDIOCGCAP). However vidcat, - * for one, does not care and allows to ask for any size. - */ - if ((vm->width > VIDEOSIZE_X(uvd->canvas)) || - (vm->height > VIDEOSIZE_Y(uvd->canvas))) { - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: Size=%dx%d " - "too large; allowed only up " - "to %ldx%ld\n", vm->width, - vm->height, - VIDEOSIZE_X(uvd->canvas), - VIDEOSIZE_Y(uvd->canvas)); - } - return -EINVAL; - } - /* Check if the palette is supported */ - if (((1L << vm->format) & uvd->paletteBits) == 0) { - if (uvd->debug > 0) { - dev_info(&uvd->dev->dev, - "VIDIOCMCAPTURE: format=%d. " - "not supported " - "(paletteBits=$%08lx)\n", - vm->format, uvd->paletteBits); - } - return -EINVAL; - } - if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) { - err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1); - return -EINVAL; - } - if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) { - /* Not an error - can happen */ - } - uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height); - uvd->frame[vm->frame].palette = vm->format; - - /* Mark it as ready */ - uvd->frame[vm->frame].frameState = FrameState_Ready; - - return usbvideo_NewFrame(uvd, vm->frame); - } - case VIDIOCSYNC: - { - int *frameNum = arg; - int ret; - - if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES) - return -EINVAL; - - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "VIDIOCSYNC: syncing to frame %d.\n", - *frameNum); - if (uvd->flags & FLAGS_NO_DECODING) - ret = usbvideo_GetFrame(uvd, *frameNum); - else if (VALID_CALLBACK(uvd, getFrame)) { - ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum); - if ((ret < 0) && (uvd->debug >= 1)) { - err("VIDIOCSYNC: getFrame() returned %d.", ret); - } - } else { - err("VIDIOCSYNC: getFrame is not set"); - ret = -EFAULT; - } - - /* - * The frame is in FrameState_Done_Hold state. Release it - * right now because its data is already mapped into - * the user space and it's up to the application to - * make use of it until it asks for another frame. - */ - uvd->frame[*frameNum].frameState = FrameState_Unused; - return ret; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb, 0, sizeof(*vb)); - return 0; - } - case VIDIOCKEY: - return 0; - - case VIDIOCCAPTURE: - return -EINVAL; - - case VIDIOCSFBUF: - - case VIDIOCGTUNER: - case VIDIOCSTUNER: - - case VIDIOCGFREQ: - case VIDIOCSFREQ: - - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - - default: - return -ENOIOCTLCMD; - } - return 0; -} - -static long usbvideo_v4l_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl); -} - -/* - * usbvideo_v4l_read() - * - * This is mostly boring stuff. We simply ask for a frame and when it - * arrives copy all the video data from it into user space. There is - * no obvious need to override this method. - * - * History: - * 20-Oct-2000 Created. - * 01-Nov-2000 Added mutex (uvd->lock). - */ -static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - struct uvd *uvd = file->private_data; - int noblock = file->f_flags & O_NONBLOCK; - int frmx = -1, i; - struct usbvideo_frame *frame; - - if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) - return -EFAULT; - - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "%s: %Zd. bytes, noblock=%d.\n", - __func__, count, noblock); - - mutex_lock(&uvd->lock); - - /* See if a frame is completed, then use it. */ - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { - if ((uvd->frame[i].frameState == FrameState_Done) || - (uvd->frame[i].frameState == FrameState_Done_Hold) || - (uvd->frame[i].frameState == FrameState_Error)) { - frmx = i; - break; - } - } - - /* FIXME: If we don't start a frame here then who ever does? */ - if (noblock && (frmx == -1)) { - count = -EAGAIN; - goto read_done; - } - - /* - * If no FrameState_Done, look for a FrameState_Grabbing state. - * See if a frame is in process (grabbing), then use it. - * We will need to wait until it becomes cooked, of course. - */ - if (frmx == -1) { - for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { - if (uvd->frame[i].frameState == FrameState_Grabbing) { - frmx = i; - break; - } - } - } - - /* - * If no frame is active, start one. We don't care which one - * it will be, so #0 is as good as any. - * In read access mode we don't have convenience of VIDIOCMCAPTURE - * to specify the requested palette (video format) on per-frame - * basis. This means that we have to return data in -some- format - * and just hope that the client knows what to do with it. - * The default format is configured in uvd->defaultPalette field - * as one of VIDEO_PALETTE_xxx values. We stuff it into the new - * frame and initiate the frame filling process. - */ - if (frmx == -1) { - if (uvd->defaultPalette == 0) { - err("%s: No default palette; don't know what to do!", __func__); - count = -EFAULT; - goto read_done; - } - frmx = 0; - /* - * We have no per-frame control over video size. - * Therefore we only can use whatever size was - * specified as default. - */ - uvd->frame[frmx].request = uvd->videosize; - uvd->frame[frmx].palette = uvd->defaultPalette; - uvd->frame[frmx].frameState = FrameState_Ready; - usbvideo_NewFrame(uvd, frmx); - /* Now frame 0 is supposed to start filling... */ - } - - /* - * Get a pointer to the active frame. It is either previously - * completed frame or frame in progress but not completed yet. - */ - frame = &uvd->frame[frmx]; - - /* - * Sit back & wait until the frame gets filled and postprocessed. - * If we fail to get the picture [in time] then return the error. - * In this call we specify that we want the frame to be waited for, - * postprocessed and switched into FrameState_Done_Hold state. This - * state is used to hold the frame as "fully completed" between - * subsequent partial reads of the same frame. - */ - if (frame->frameState != FrameState_Done_Hold) { - long rv = -EFAULT; - if (uvd->flags & FLAGS_NO_DECODING) - rv = usbvideo_GetFrame(uvd, frmx); - else if (VALID_CALLBACK(uvd, getFrame)) - rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); - else - err("getFrame is not set"); - if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { - count = rv; - goto read_done; - } - } - - /* - * Copy bytes to user space. We allow for partial reads, which - * means that the user application can request read less than - * the full frame size. It is up to the application to issue - * subsequent calls until entire frame is read. - * - * First things first, make sure we don't copy more than we - * have - even if the application wants more. That would be - * a big security embarassment! - */ - if ((count + frame->seqRead_Index) > frame->seqRead_Length) - count = frame->seqRead_Length - frame->seqRead_Index; - - /* - * Copy requested amount of data to user space. We start - * copying from the position where we last left it, which - * will be zero for a new frame (not read before). - */ - if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { - count = -EFAULT; - goto read_done; - } - - /* Update last read position */ - frame->seqRead_Index += count; - if (uvd->debug >= 1) { - err("%s: {copy} count used=%Zd, new seqRead_Index=%ld", - __func__, count, frame->seqRead_Index); - } - - /* Finally check if the frame is done with and "release" it */ - if (frame->seqRead_Index >= frame->seqRead_Length) { - /* All data has been read */ - frame->seqRead_Index = 0; - - /* Mark it as available to be used again. */ - uvd->frame[frmx].frameState = FrameState_Unused; - if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) { - err("%s: usbvideo_NewFrame failed.", __func__); - } - } -read_done: - mutex_unlock(&uvd->lock); - return count; -} - -/* - * Make all of the blocks of data contiguous - */ -static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb) -{ - char *cdata; - int i, totlen = 0; - - for (i = 0; i < urb->number_of_packets; i++) { - int n = urb->iso_frame_desc[i].actual_length; - int st = urb->iso_frame_desc[i].status; - - cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; - - /* Detect and ignore errored packets */ - if (st < 0) { - if (uvd->debug >= 1) - err("Data error: packet=%d. len=%d. status=%d.", i, n, st); - uvd->stats.iso_err_count++; - continue; - } - - /* Detect and ignore empty packets */ - if (n <= 0) { - uvd->stats.iso_skip_count++; - continue; - } - totlen += n; /* Little local accounting */ - RingQueue_Enqueue(&uvd->dp, cdata, n); - } - return totlen; -} - -static void usbvideo_IsocIrq(struct urb *urb) -{ - int i, ret, len; - struct uvd *uvd = urb->context; - - /* We don't want to do anything if we are about to be removed! */ - if (!CAMERA_IS_OPERATIONAL(uvd)) - return; -#if 0 - if (urb->actual_length > 0) { - dev_info(&uvd->dev->dev, - "urb=$%p status=%d. errcount=%d. length=%d.\n", - urb, urb->status, urb->error_count, - urb->actual_length); - } else { - static int c = 0; - if (c++ % 100 == 0) - dev_info(&uvd->dev->dev, "No Isoc data\n"); - } -#endif - - if (!uvd->streaming) { - if (uvd->debug >= 1) - dev_info(&uvd->dev->dev, - "Not streaming, but interrupt!\n"); - return; - } - - uvd->stats.urb_count++; - if (urb->actual_length <= 0) - goto urb_done_with; - - /* Copy the data received into ring queue */ - len = usbvideo_CompressIsochronous(uvd, urb); - uvd->stats.urb_length = len; - if (len <= 0) - goto urb_done_with; - - /* Here we got some data */ - uvd->stats.data_count += len; - RingQueue_WakeUpInterruptible(&uvd->dp); - -urb_done_with: - for (i = 0; i < FRAMES_PER_DESC; i++) { - urb->iso_frame_desc[i].status = 0; - urb->iso_frame_desc[i].actual_length = 0; - } - urb->status = 0; - urb->dev = uvd->dev; - ret = usb_submit_urb (urb, GFP_KERNEL); - if(ret) - err("usb_submit_urb error (%d)", ret); - return; -} - -/* - * usbvideo_StartDataPump() - * - * History: - * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead - * of hardcoded values. Simplified by using for loop, - * allowed any number of URBs. - */ -static int usbvideo_StartDataPump(struct uvd *uvd) -{ - struct usb_device *dev = uvd->dev; - int i, errFlag; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); - - if (!CAMERA_IS_OPERATIONAL(uvd)) { - err("%s: Camera is not operational", __func__); - return -EFAULT; - } - uvd->curframe = -1; - - /* Alternate interface 1 is is the biggest frame size */ - i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); - if (i < 0) { - err("%s: usb_set_interface error", __func__); - uvd->last_error = i; - return -EBUSY; - } - if (VALID_CALLBACK(uvd, videoStart)) - GET_CALLBACK(uvd, videoStart)(uvd); - else - err("%s: videoStart not set", __func__); - - /* We double buffer the Iso lists */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - int j, k; - struct urb *urb = uvd->sbuf[i].urb; - urb->dev = dev; - urb->context = uvd; - urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); - urb->interval = 1; - urb->transfer_flags = URB_ISO_ASAP; - urb->transfer_buffer = uvd->sbuf[i].data; - urb->complete = usbvideo_IsocIrq; - urb->number_of_packets = FRAMES_PER_DESC; - urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; - for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { - urb->iso_frame_desc[j].offset = k; - urb->iso_frame_desc[j].length = uvd->iso_packet_len; - } - } - - /* Submit all URBs */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); - if (errFlag) - err("%s: usb_submit_isoc(%d) ret %d", __func__, i, errFlag); - } - - uvd->streaming = 1; - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, - "%s: streaming=1 video_endp=$%02x\n", __func__, - uvd->video_endp); - return 0; -} - -/* - * usbvideo_StopDataPump() - * - * This procedure stops streaming and deallocates URBs. Then it - * activates zero-bandwidth alt. setting of the video interface. - * - * History: - * 22-Jan-2000 Corrected order of actions to work after surprise removal. - * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. - */ -static void usbvideo_StopDataPump(struct uvd *uvd) -{ - int i, j; - - if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) - return; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); - - /* Unschedule all of the iso td's */ - for (i=0; i < USBVIDEO_NUMSBUF; i++) { - usb_kill_urb(uvd->sbuf[i].urb); - } - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "%s: streaming=0\n", __func__); - uvd->streaming = 0; - - if (!uvd->remove_pending) { - /* Invoke minidriver's magic to stop the camera */ - if (VALID_CALLBACK(uvd, videoStop)) - GET_CALLBACK(uvd, videoStop)(uvd); - else - err("%s: videoStop not set", __func__); - - /* Set packet size to 0 */ - j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); - if (j < 0) { - err("%s: usb_set_interface() error %d.", __func__, j); - uvd->last_error = j; - } - } -} - -/* - * usbvideo_NewFrame() - * - * History: - * 29-Mar-00 Added copying of previous frame into the current one. - * 6-Aug-00 Added model 3 video sizes, removed redundant width, height. - */ -static int usbvideo_NewFrame(struct uvd *uvd, int framenum) -{ - struct usbvideo_frame *frame; - int n; - - if (uvd->debug > 1) - dev_info(&uvd->dev->dev, "usbvideo_NewFrame($%p,%d.)\n", uvd, - framenum); - - /* If we're not grabbing a frame right now and the other frame is */ - /* ready to be grabbed into, then use it instead */ - if (uvd->curframe != -1) - return 0; - - /* If necessary we adjust picture settings between frames */ - if (!uvd->settingsAdjusted) { - if (VALID_CALLBACK(uvd, adjustPicture)) - GET_CALLBACK(uvd, adjustPicture)(uvd); - uvd->settingsAdjusted = 1; - } - - n = (framenum + 1) % USBVIDEO_NUMFRAMES; - if (uvd->frame[n].frameState == FrameState_Ready) - framenum = n; - - frame = &uvd->frame[framenum]; - - frame->frameState = FrameState_Grabbing; - frame->scanstate = ScanState_Scanning; - frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */ - frame->deinterlace = Deinterlace_None; - frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */ - uvd->curframe = framenum; - - /* - * Normally we would want to copy previous frame into the current one - * before we even start filling it with data; this allows us to stop - * filling at any moment; top portion of the frame will be new and - * bottom portion will stay as it was in previous frame. If we don't - * do that then missing chunks of video stream will result in flickering - * portions of old data whatever it was before. - * - * If we choose not to copy previous frame (to, for example, save few - * bus cycles - the frame can be pretty large!) then we have an option - * to clear the frame before using. If we experience losses in this - * mode then missing picture will be black (no flickering). - * - * Finally, if user chooses not to clean the current frame before - * filling it with data then the old data will be visible if we fail - * to refill entire frame with new data. - */ - if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) { - /* This copies previous frame into this one to mask losses */ - int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES; - memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size); - } else { - if (uvd->flags & FLAGS_CLEAN_FRAMES) { - /* This provides a "clean" frame but slows things down */ - memset(frame->data, 0, uvd->max_frame_size); - } - } - return 0; -} - -/* - * usbvideo_CollectRawData() - * - * This procedure can be used instead of 'processData' callback if you - * only want to dump the raw data from the camera into the output - * device (frame buffer). You can look at it with V4L client, but the - * image will be unwatchable. The main purpose of this code and of the - * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from - * new, unknown cameras. This procedure will be automatically invoked - * instead of the specified callback handler when uvd->flags has bit - * FLAGS_NO_DECODING set. Therefore, any regular build of any driver - * based on usbvideo can use this feature at any time. - */ -static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame) -{ - int n; - - assert(uvd != NULL); - assert(frame != NULL); - - /* Try to move data from queue into frame buffer */ - n = RingQueue_GetLength(&uvd->dp); - if (n > 0) { - int m; - /* See how much space we have left */ - m = uvd->max_frame_size - frame->seqRead_Length; - if (n > m) - n = m; - /* Now move that much data into frame buffer */ - RingQueue_Dequeue( - &uvd->dp, - frame->data + frame->seqRead_Length, - m); - frame->seqRead_Length += m; - } - /* See if we filled the frame */ - if (frame->seqRead_Length >= uvd->max_frame_size) { - frame->frameState = FrameState_Done; - uvd->curframe = -1; - uvd->stats.frame_num++; - } -} - -static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) -{ - struct usbvideo_frame *frame = &uvd->frame[frameNum]; - - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, "%s($%p,%d.)\n", __func__, uvd, - frameNum); - - switch (frame->frameState) { - case FrameState_Unused: - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, "%s: FrameState_Unused\n", - __func__); - return -EINVAL; - case FrameState_Ready: - case FrameState_Grabbing: - case FrameState_Error: - { - int ntries, signalPending; - redo: - if (!CAMERA_IS_OPERATIONAL(uvd)) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Camera is not operational (1)\n", - __func__); - return -EIO; - } - ntries = 0; - do { - RingQueue_InterruptibleSleepOn(&uvd->dp); - signalPending = signal_pending(current); - if (!CAMERA_IS_OPERATIONAL(uvd)) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Camera is not " - "operational (2)\n", __func__); - return -EIO; - } - assert(uvd->fbuf != NULL); - if (signalPending) { - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Signal=$%08x\n", __func__, - signalPending); - if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { - usbvideo_TestPattern(uvd, 1, 0); - uvd->curframe = -1; - uvd->stats.frame_num++; - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Forced test " - "pattern screen\n", - __func__); - return 0; - } else { - /* Standard answer: Interrupted! */ - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Interrupted!\n", - __func__); - return -EINTR; - } - } else { - /* No signals - we just got new data in dp queue */ - if (uvd->flags & FLAGS_NO_DECODING) - usbvideo_CollectRawData(uvd, frame); - else if (VALID_CALLBACK(uvd, processData)) - GET_CALLBACK(uvd, processData)(uvd, frame); - else - err("%s: processData not set", __func__); - } - } while (frame->frameState == FrameState_Grabbing); - if (uvd->debug >= 2) { - dev_info(&uvd->dev->dev, - "%s: Grabbing done; state=%d. (%lu. bytes)\n", - __func__, frame->frameState, - frame->seqRead_Length); - } - if (frame->frameState == FrameState_Error) { - int ret = usbvideo_NewFrame(uvd, frameNum); - if (ret < 0) { - err("%s: usbvideo_NewFrame() failed (%d.)", __func__, ret); - return ret; - } - goto redo; - } - /* Note that we fall through to meet our destiny below */ - } - case FrameState_Done: - /* - * Do all necessary postprocessing of data prepared in - * "interrupt" code and the collecting code above. The - * frame gets marked as FrameState_Done by queue parsing code. - * This status means that we collected enough data and - * most likely processed it as we went through. However - * the data may need postprocessing, such as deinterlacing - * or picture adjustments implemented in software (horror!) - * - * As soon as the frame becomes "final" it gets promoted to - * FrameState_Done_Hold status where it will remain until the - * caller consumed all the video data from the frame. Then - * the empty shell of ex-frame is thrown out for dogs to eat. - * But we, worried about pets, will recycle the frame! - */ - uvd->stats.frame_num++; - if ((uvd->flags & FLAGS_NO_DECODING) == 0) { - if (VALID_CALLBACK(uvd, postProcess)) - GET_CALLBACK(uvd, postProcess)(uvd, frame); - if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST) - usbvideo_SoftwareContrastAdjustment(uvd, frame); - } - frame->frameState = FrameState_Done_Hold; - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: Entered FrameState_Done_Hold state.\n", - __func__); - return 0; - - case FrameState_Done_Hold: - /* - * We stay in this state indefinitely until someone external, - * like ioctl() or read() call finishes digesting the frame - * data. Then it will mark the frame as FrameState_Unused and - * it will be released back into the wild to roam freely. - */ - if (uvd->debug >= 2) - dev_info(&uvd->dev->dev, - "%s: FrameState_Done_Hold state.\n", - __func__); - return 0; - } - - /* Catch-all for other cases. We shall not be here. */ - err("%s: Invalid state %d.", __func__, frame->frameState); - frame->frameState = FrameState_Unused; - return 0; -} - -/* - * usbvideo_DeinterlaceFrame() - * - * This procedure deinterlaces the given frame. Some cameras produce - * only half of scanlines - sometimes only even lines, sometimes only - * odd lines. The deinterlacing method is stored in frame->deinterlace - * variable. - * - * Here we scan the frame vertically and replace missing scanlines with - * average between surrounding ones - before and after. If we have no - * line above then we just copy next line. Similarly, if we need to - * create a last line then preceding line is used. - */ -void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame) -{ - if ((uvd == NULL) || (frame == NULL)) - return; - - if ((frame->deinterlace == Deinterlace_FillEvenLines) || - (frame->deinterlace == Deinterlace_FillOddLines)) - { - const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; - int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1; - - for (; i < VIDEOSIZE_Y(frame->request); i += 2) { - const unsigned char *fs1, *fs2; - unsigned char *fd; - int ip, in, j; /* Previous and next lines */ - - /* - * Need to average lines before and after 'i'. - * If we go out of bounds seeking those lines then - * we point back to existing line. - */ - ip = i - 1; /* First, get rough numbers */ - in = i + 1; - - /* Now validate */ - if (ip < 0) - ip = in; - if (in >= VIDEOSIZE_Y(frame->request)) - in = ip; - - /* Sanity check */ - if ((ip < 0) || (in < 0) || - (ip >= VIDEOSIZE_Y(frame->request)) || - (in >= VIDEOSIZE_Y(frame->request))) - { - err("Error: ip=%d. in=%d. req.height=%ld.", - ip, in, VIDEOSIZE_Y(frame->request)); - break; - } - - /* Now we need to average lines 'ip' and 'in' to produce line 'i' */ - fs1 = frame->data + (v4l_linesize * ip); - fs2 = frame->data + (v4l_linesize * in); - fd = frame->data + (v4l_linesize * i); - - /* Average lines around destination */ - for (j=0; j < v4l_linesize; j++) { - fd[j] = (unsigned char)((((unsigned) fs1[j]) + - ((unsigned)fs2[j])) >> 1); - } - } - } - - /* Optionally display statistics on the screen */ - if (uvd->flags & FLAGS_OVERLAY_STATS) - usbvideo_OverlayStats(uvd, frame); -} - -EXPORT_SYMBOL(usbvideo_DeinterlaceFrame); - -/* - * usbvideo_SoftwareContrastAdjustment() - * - * This code adjusts the contrast of the frame, assuming RGB24 format. - * As most software image processing, this job is CPU-intensive. - * Get a camera that supports hardware adjustment! - * - * History: - * 09-Feb-2001 Created. - */ -static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, - struct usbvideo_frame *frame) -{ - int i, j, v4l_linesize; - signed long adj; - const int ccm = 128; /* Color correction median - see below */ - - if ((uvd == NULL) || (frame == NULL)) { - err("%s: Illegal call.", __func__); - return; - } - adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ - RESTRICT_TO_RANGE(adj, -ccm, ccm+1); - if (adj == 0) { - /* In rare case of no adjustment */ - return; - } - v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; - for (i=0; i < VIDEOSIZE_Y(frame->request); i++) { - unsigned char *fd = frame->data + (v4l_linesize * i); - for (j=0; j < v4l_linesize; j++) { - signed long v = (signed long) fd[j]; - /* Magnify up to 2 times, reduce down to zero */ - v = 128 + ((ccm + adj) * (v - 128)) / ccm; - RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */ - fd[j] = (unsigned char) v; - } - } -} - -MODULE_LICENSE("GPL"); diff --git a/drivers/media/video/usbvideo/usbvideo.h b/drivers/media/video/usbvideo/usbvideo.h deleted file mode 100644 index c66985beb8c9..000000000000 --- a/drivers/media/video/usbvideo/usbvideo.h +++ /dev/null @@ -1,395 +0,0 @@ -/* - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2, or (at your option) - * any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ -#ifndef usbvideo_h -#define usbvideo_h - -#include -#include -#include -#include -#include - -/* Most helpful debugging aid */ -#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) - -#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */ - -/* Bit flags (options) */ -#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) -#define FLAGS_MONOCHROME (1 << 1) -#define FLAGS_DISPLAY_HINTS (1 << 2) -#define FLAGS_OVERLAY_STATS (1 << 3) -#define FLAGS_FORCE_TESTPATTERN (1 << 4) -#define FLAGS_SEPARATE_FRAMES (1 << 5) -#define FLAGS_CLEAN_FRAMES (1 << 6) -#define FLAGS_NO_DECODING (1 << 7) - -/* Bit flags for frames (apply to the frame where they are specified) */ -#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0) - -/* Camera capabilities (maximum) */ -#define CAMERA_URB_FRAMES 32 -#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ -#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) -#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) - -/* This macro restricts an int variable to an inclusive range */ -#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } - -#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */ - -/* - * Use this macro to construct constants for different video sizes. - * We have to deal with different video sizes that have to be - * configured in the device or compared against when we receive - * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y - * #defines and that's the end of story. However this solution - * does not allow to convert between real pixel sizes and the - * constant (integer) value that may be used to tag a frame or - * whatever. The set of macros below constructs videosize constants - * from the pixel size and allows to reconstruct the pixel size - * from the combined value later. - */ -#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16)) -#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL) -#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL) -typedef unsigned long videosize_t; - -/* - * This macro checks if the camera is still operational. The 'uvd' - * pointer must be valid, uvd->dev must be valid, we are not - * removing the device and the device has not erred on us. - */ -#define CAMERA_IS_OPERATIONAL(uvd) (\ - (uvd != NULL) && \ - ((uvd)->dev != NULL) && \ - ((uvd)->last_error == 0) && \ - (!(uvd)->remove_pending)) - -/* - * We use macros to do YUV -> RGB conversion because this is - * very important for speed and totally unimportant for size. - * - * YUV -> RGB Conversion - * --------------------- - * - * B = 1.164*(Y-16) + 2.018*(V-128) - * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) - * R = 1.164*(Y-16) + 1.596*(U-128) - * - * If you fancy integer arithmetics (as you should), hear this: - * - * 65536*B = 76284*(Y-16) + 132252*(V-128) - * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) - * 65536*R = 76284*(Y-16) + 104595*(U-128) - * - * Make sure the output values are within [0..255] range. - */ -#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) -#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ - int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ - mm_y = (my) - 16; \ - mm_u = (mu) - 128; \ - mm_v = (mv) - 128; \ - mm_yc= mm_y * 76284; \ - mm_b = (mm_yc + 132252*mm_v ) >> 16; \ - mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ - mm_r = (mm_yc + 104595*mm_u ) >> 16; \ - mb = LIMIT_RGB(mm_b); \ - mg = LIMIT_RGB(mm_g); \ - mr = LIMIT_RGB(mm_r); \ -} - -#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */ -#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1) -#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) -#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)]) - -struct RingQueue { - unsigned char *queue; /* Data from the Isoc data pump */ - int length; /* How many bytes allocated for the queue */ - int wi; /* That's where we write */ - int ri; /* Read from here until you hit write index */ - wait_queue_head_t wqh; /* Processes waiting */ -}; - -enum ScanState { - ScanState_Scanning, /* Scanning for header */ - ScanState_Lines /* Parsing lines */ -}; - -/* Completion states of the data parser */ -enum ParseState { - scan_Continue, /* Just parse next item */ - scan_NextFrame, /* Frame done, send it to V4L */ - scan_Out, /* Not enough data for frame */ - scan_EndParse /* End parsing */ -}; - -enum FrameState { - FrameState_Unused, /* Unused (no MCAPTURE) */ - FrameState_Ready, /* Ready to start grabbing */ - FrameState_Grabbing, /* In the process of being grabbed into */ - FrameState_Done, /* Finished grabbing, but not been synced yet */ - FrameState_Done_Hold, /* Are syncing or reading */ - FrameState_Error, /* Something bad happened while processing */ -}; - -/* - * Some frames may contain only even or odd lines. This type - * specifies what type of deinterlacing is required. - */ -enum Deinterlace { - Deinterlace_None=0, - Deinterlace_FillOddLines, - Deinterlace_FillEvenLines -}; - -#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */ -#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */ - -/* This structure represents one Isoc request - URB and buffer */ -struct usbvideo_sbuf { - char *data; - struct urb *urb; -}; - -struct usbvideo_frame { - char *data; /* Frame buffer */ - unsigned long header; /* Significant bits from the header */ - - videosize_t canvas; /* The canvas (max. image) allocated */ - videosize_t request; /* That's what the application asked for */ - unsigned short palette; /* The desired format */ - - enum FrameState frameState;/* State of grabbing */ - enum ScanState scanstate; /* State of scanning */ - enum Deinterlace deinterlace; - int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */ - - int curline; /* Line of frame we're working on */ - - long seqRead_Length; /* Raw data length of frame */ - long seqRead_Index; /* Amount of data that has been already read */ - - void *user; /* Additional data that user may need */ -}; - -/* Statistics that can be overlaid on screen */ -struct usbvideo_statistics { - unsigned long frame_num; /* Sequential number of the frame */ - unsigned long urb_count; /* How many URBs we received so far */ - unsigned long urb_length; /* Length of last URB */ - unsigned long data_count; /* How many bytes we received */ - unsigned long header_count; /* How many frame headers we found */ - unsigned long iso_skip_count; /* How many empty ISO packets received */ - unsigned long iso_err_count; /* How many bad ISO packets received */ -}; - -struct usbvideo; - -struct uvd { - struct video_device vdev; /* Must be the first field! */ - struct usb_device *dev; - struct usbvideo *handle; /* Points back to the struct usbvideo */ - void *user_data; /* Camera-dependent data */ - int user_size; /* Size of that camera-dependent data */ - int debug; /* Debug level for usbvideo */ - unsigned char iface; /* Video interface number */ - unsigned char video_endp; - unsigned char ifaceAltActive; - unsigned char ifaceAltInactive; /* Alt settings */ - unsigned long flags; /* FLAGS_USBVIDEO_xxx */ - unsigned long paletteBits; /* Which palettes we accept? */ - unsigned short defaultPalette; /* What palette to use for read() */ - struct mutex lock; - int user; /* user count for exclusive use */ - - videosize_t videosize; /* Current setting */ - videosize_t canvas; /* This is the width,height of the V4L canvas */ - int max_frame_size; /* Bytes in one video frame */ - - int uvd_used; /* Is this structure in use? */ - int streaming; /* Are we streaming Isochronous? */ - int grabbing; /* Are we grabbing? */ - int settingsAdjusted; /* Have we adjusted contrast etc.? */ - int last_error; /* What calamity struck us? */ - - char *fbuf; /* Videodev buffer area */ - int fbuf_size; /* Videodev buffer size */ - - int curframe; - int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ - - struct RingQueue dp; /* Isoc data pump */ - struct usbvideo_frame frame[USBVIDEO_NUMFRAMES]; - struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF]; - - volatile int remove_pending; /* If set then about to exit */ - - struct video_picture vpic, vpic_old; /* Picture settings */ - struct video_capability vcap; /* Video capabilities */ - struct video_channel vchan; /* May be used for tuner support */ - struct usbvideo_statistics stats; - char videoName[32]; /* Holds name like "video7" */ -}; - -/* - * usbvideo callbacks (virtual methods). They are set when usbvideo - * services are registered. All of these default to NULL, except those - * that default to usbvideo-provided methods. - */ -struct usbvideo_cb { - int (*probe)(struct usb_interface *, const struct usb_device_id *); - void (*userFree)(struct uvd *); - void (*disconnect)(struct usb_interface *); - int (*setupOnOpen)(struct uvd *); - void (*videoStart)(struct uvd *); - void (*videoStop)(struct uvd *); - void (*processData)(struct uvd *, struct usbvideo_frame *); - void (*postProcess)(struct uvd *, struct usbvideo_frame *); - void (*adjustPicture)(struct uvd *); - int (*getFPS)(struct uvd *); - int (*overlayHook)(struct uvd *, struct usbvideo_frame *); - int (*getFrame)(struct uvd *, int); - int (*startDataPump)(struct uvd *uvd); - void (*stopDataPump)(struct uvd *uvd); - int (*setVideoMode)(struct uvd *uvd, struct video_window *vw); -}; - -struct usbvideo { - int num_cameras; /* As allocated */ - struct usb_driver usbdrv; /* Interface to the USB stack */ - char drvName[80]; /* Driver name */ - struct mutex lock; /* Mutex protecting camera structures */ - struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ - struct video_device vdt; /* Video device template */ - struct uvd *cam; /* Array of camera structures */ - struct module *md_module; /* Minidriver module */ -}; - - -/* - * This macro retrieves callback address from the struct uvd object. - * No validity checks are done here, so be sure to check the - * callback beforehand with VALID_CALLBACK. - */ -#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName) - -/* - * This macro returns either callback pointer or NULL. This is safe - * macro, meaning that most of components of data structures involved - * may be NULL - this only results in NULL being returned. You may - * wish to use this macro to make sure that the callback is callable. - * However keep in mind that those checks take time. - */ -#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ - ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) - -int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len); -int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n); -void RingQueue_WakeUpInterruptible(struct RingQueue *rq); -void RingQueue_Flush(struct RingQueue *rq); - -static inline int RingQueue_GetLength(const struct RingQueue *rq) -{ - return (rq->wi - rq->ri + rq->length) & (rq->length-1); -} - -static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq) -{ - return rq->length - RingQueue_GetLength(rq); -} - -void usbvideo_DrawLine( - struct usbvideo_frame *frame, - int x1, int y1, - int x2, int y2, - unsigned char cr, unsigned char cg, unsigned char cb); -void usbvideo_HexDump(const unsigned char *data, int len); -void usbvideo_SayAndWait(const char *what); -void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode); - -/* Memory allocation routines */ -unsigned long usbvideo_kvirt_to_pa(unsigned long adr); - -int usbvideo_register( - struct usbvideo **pCams, - const int num_cams, - const int num_extra, - const char *driverName, - const struct usbvideo_cb *cbTable, - struct module *md, - const struct usb_device_id *id_table); -struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams); -int usbvideo_RegisterVideoDevice(struct uvd *uvd); -void usbvideo_Deregister(struct usbvideo **uvt); - -int usbvideo_v4l_initialize(struct video_device *dev); - -void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame); - -/* - * This code performs bounds checking - use it when working with - * new formats, or else you may get oopses all over the place. - * If pixel falls out of bounds then it gets shoved back (as close - * to place of offence as possible) and is painted bright red. - * - * There are two important concepts: frame width, height and - * V4L canvas width, height. The former is the area requested by - * the application -for this very frame-. The latter is the largest - * possible frame that we can serve (we advertise that via V4L ioctl). - * The frame data is expected to be formatted as lines of length - * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines. - */ -static inline void RGB24_PUTPIXEL( - struct usbvideo_frame *fr, - int ix, int iy, - unsigned char vr, - unsigned char vg, - unsigned char vb) -{ - register unsigned char *pf; - int limiter = 0, mx, my; - mx = ix; - my = iy; - if (mx < 0) { - mx=0; - limiter++; - } else if (mx >= VIDEOSIZE_X((fr)->request)) { - mx= VIDEOSIZE_X((fr)->request) - 1; - limiter++; - } - if (my < 0) { - my = 0; - limiter++; - } else if (my >= VIDEOSIZE_Y((fr)->request)) { - my = VIDEOSIZE_Y((fr)->request) - 1; - limiter++; - } - pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix)); - if (limiter) { - *pf++ = 0; - *pf++ = 0; - *pf++ = 0xFF; - } else { - *pf++ = (vb); - *pf++ = (vg); - *pf++ = (vr); - } -} - -#endif /* usbvideo_h */ diff --git a/drivers/media/video/usbvideo/vicam.c b/drivers/media/video/usbvideo/vicam.c deleted file mode 100644 index dc17cce2fbb6..000000000000 --- a/drivers/media/video/usbvideo/vicam.c +++ /dev/null @@ -1,952 +0,0 @@ -/* - * USB ViCam WebCam driver - * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), - * Christopher L Cheney (ccheney@cheney.cx), - * Pavel Machek (pavel@ucw.cz), - * John Tyner (jtyner@cs.ucr.edu), - * Monroe Williams (monroe@pobox.com) - * - * Supports 3COM HomeConnect PC Digital WebCam - * Supports Compro PS39U WebCam - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - * - * This source code is based heavily on the CPiA webcam driver which was - * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt - * - * Portions of this code were also copied from usbvideo.c - * - * Special thanks to the whole team at Sourceforge for help making - * this driver become a reality. Notably: - * Andy Armstrong who reverse engineered the color encoding and - * Pavel Machek and Chris Cheney who worked on reverse engineering the - * camera controls and wrote the first generation driver. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "usbvideo.h" - -// #define VICAM_DEBUG - -#ifdef VICAM_DEBUG -#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __func__, lineno, ##args) -#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) -#else -#define DBG(fmn,args...) do {} while(0) -#endif - -#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" -#define DRIVER_DESC "ViCam WebCam Driver" - -/* Define these values to match your device */ -#define USB_VICAM_VENDOR_ID 0x04c1 -#define USB_VICAM_PRODUCT_ID 0x009d -#define USB_COMPRO_VENDOR_ID 0x0602 -#define USB_COMPRO_PRODUCT_ID 0x1001 - -#define VICAM_BYTES_PER_PIXEL 3 -#define VICAM_MAX_READ_SIZE (512*242+128) -#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) -#define VICAM_FRAMES 2 - -#define VICAM_HEADER_SIZE 64 - -/* rvmalloc / rvfree copied from usbvideo.c - * - * Not sure why these are not yet non-statics which I can reference through - * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime - * in the future. - * -*/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - -struct vicam_camera { - u16 shutter_speed; // capture shutter speed - u16 gain; // capture gain - - u8 *raw_image; // raw data captured from the camera - u8 *framebuf; // processed data in RGB24 format - u8 *cntrlbuf; // area used to send control msgs - - struct video_device vdev; // v4l video device - struct usb_device *udev; // usb device - - /* guard against simultaneous accesses to the camera */ - struct mutex cam_lock; - - int is_initialized; - u8 open_count; - u8 bulkEndpoint; - int needsDummyRead; -}; - -static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); -static void vicam_disconnect(struct usb_interface *intf); -static void read_frame(struct vicam_camera *cam, int framenum); -static void vicam_decode_color(const u8 *, u8 *); - -static int __send_control_msg(struct vicam_camera *cam, - u8 request, - u16 value, - u16 index, - unsigned char *cp, - u16 size) -{ - int status; - - /* cp must be memory that has been allocated by kmalloc */ - - status = usb_control_msg(cam->udev, - usb_sndctrlpipe(cam->udev, 0), - request, - USB_DIR_OUT | USB_TYPE_VENDOR | - USB_RECIP_DEVICE, value, index, - cp, size, 1000); - - status = min(status, 0); - - if (status < 0) { - printk(KERN_INFO "Failed sending control message, error %d.\n", - status); - } - - return status; -} - -static int send_control_msg(struct vicam_camera *cam, - u8 request, - u16 value, - u16 index, - unsigned char *cp, - u16 size) -{ - int status = -ENODEV; - mutex_lock(&cam->cam_lock); - if (cam->udev) { - status = __send_control_msg(cam, request, value, - index, cp, size); - } - mutex_unlock(&cam->cam_lock); - return status; -} -static int -initialize_camera(struct vicam_camera *cam) -{ - int err; - const struct ihex_binrec *rec; - const struct firmware *uninitialized_var(fw); - - err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev); - if (err) { - printk(KERN_ERR "Failed to load \"vicam/firmware.fw\": %d\n", - err); - return err; - } - - for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { - memcpy(cam->cntrlbuf, rec->data, be16_to_cpu(rec->len)); - - err = send_control_msg(cam, 0xff, 0, 0, - cam->cntrlbuf, be16_to_cpu(rec->len)); - if (err) - break; - } - - release_firmware(fw); - - return err; -} - -static int -set_camera_power(struct vicam_camera *cam, int state) -{ - int status; - - if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0) - return status; - - if (state) { - send_control_msg(cam, 0x55, 1, 0, NULL, 0); - } - - return 0; -} - -static long -vicam_ioctl(struct file *file, unsigned int ioctlnr, unsigned long arg) -{ - void __user *user_arg = (void __user *)arg; - struct vicam_camera *cam = file->private_data; - long retval = 0; - - if (!cam) - return -ENODEV; - - switch (ioctlnr) { - /* query capabilities */ - case VIDIOCGCAP: - { - struct video_capability b; - - DBG("VIDIOCGCAP\n"); - memset(&b, 0, sizeof(b)); - strcpy(b.name, "ViCam-based Camera"); - b.type = VID_TYPE_CAPTURE; - b.channels = 1; - b.audios = 0; - b.maxwidth = 320; /* VIDEOSIZE_CIF */ - b.maxheight = 240; - b.minwidth = 320; /* VIDEOSIZE_48_48 */ - b.minheight = 240; - - if (copy_to_user(user_arg, &b, sizeof(b))) - retval = -EFAULT; - - break; - } - /* get/set video source - we are a camera and nothing else */ - case VIDIOCGCHAN: - { - struct video_channel v; - - DBG("VIDIOCGCHAN\n"); - if (copy_from_user(&v, user_arg, sizeof(v))) { - retval = -EFAULT; - break; - } - if (v.channel != 0) { - retval = -EINVAL; - break; - } - - v.channel = 0; - strcpy(v.name, "Camera"); - v.tuners = 0; - v.flags = 0; - v.type = VIDEO_TYPE_CAMERA; - v.norm = 0; - - if (copy_to_user(user_arg, &v, sizeof(v))) - retval = -EFAULT; - break; - } - - case VIDIOCSCHAN: - { - int v; - - if (copy_from_user(&v, user_arg, sizeof(v))) - retval = -EFAULT; - DBG("VIDIOCSCHAN %d\n", v); - - if (retval == 0 && v != 0) - retval = -EINVAL; - - break; - } - - /* image properties */ - case VIDIOCGPICT: - { - struct video_picture vp; - DBG("VIDIOCGPICT\n"); - memset(&vp, 0, sizeof (struct video_picture)); - vp.brightness = cam->gain << 8; - vp.depth = 24; - vp.palette = VIDEO_PALETTE_RGB24; - if (copy_to_user(user_arg, &vp, sizeof (struct video_picture))) - retval = -EFAULT; - break; - } - - case VIDIOCSPICT: - { - struct video_picture vp; - - if (copy_from_user(&vp, user_arg, sizeof(vp))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth, - vp.palette); - - cam->gain = vp.brightness >> 8; - - if (vp.depth != 24 - || vp.palette != VIDEO_PALETTE_RGB24) - retval = -EINVAL; - - break; - } - - /* get/set capture window */ - case VIDIOCGWIN: - { - struct video_window vw; - vw.x = 0; - vw.y = 0; - vw.width = 320; - vw.height = 240; - vw.chromakey = 0; - vw.flags = 0; - vw.clips = NULL; - vw.clipcount = 0; - - DBG("VIDIOCGWIN\n"); - - if (copy_to_user(user_arg, (void *)&vw, sizeof(vw))) - retval = -EFAULT; - - // I'm not sure what the deal with a capture window is, it is very poorly described - // in the doc. So I won't support it now. - break; - } - - case VIDIOCSWIN: - { - - struct video_window vw; - - if (copy_from_user(&vw, user_arg, sizeof(vw))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height); - - if ( vw.width != 320 || vw.height != 240 ) - retval = -EFAULT; - - break; - } - - /* mmap interface */ - case VIDIOCGMBUF: - { - struct video_mbuf vm; - int i; - - DBG("VIDIOCGMBUF\n"); - memset(&vm, 0, sizeof (vm)); - vm.size = - VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; - vm.frames = VICAM_FRAMES; - for (i = 0; i < VICAM_FRAMES; i++) - vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; - - if (copy_to_user(user_arg, (void *)&vm, sizeof(vm))) - retval = -EFAULT; - - break; - } - - case VIDIOCMCAPTURE: - { - struct video_mmap vm; - // int video_size; - - if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) { - retval = -EFAULT; - break; - } - - DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); - - if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) - retval = -EINVAL; - - // in theory right here we'd start the image capturing - // (fill in a bulk urb and submit it asynchronously) - // - // Instead we're going to do a total hack job for now and - // retrieve the frame in VIDIOCSYNC - - break; - } - - case VIDIOCSYNC: - { - int frame; - - if (copy_from_user((void *)&frame, user_arg, sizeof(int))) { - retval = -EFAULT; - break; - } - DBG("VIDIOCSYNC: %d\n", frame); - - read_frame(cam, frame); - vicam_decode_color(cam->raw_image, - cam->framebuf + - frame * VICAM_MAX_FRAME_SIZE ); - - break; - } - - /* pointless to implement overlay with this camera */ - case VIDIOCCAPTURE: - case VIDIOCGFBUF: - case VIDIOCSFBUF: - case VIDIOCKEY: - retval = -EINVAL; - break; - - /* tuner interface - we have none */ - case VIDIOCGTUNER: - case VIDIOCSTUNER: - case VIDIOCGFREQ: - case VIDIOCSFREQ: - retval = -EINVAL; - break; - - /* audio interface - we have none */ - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - retval = -EINVAL; - break; - default: - retval = -ENOIOCTLCMD; - break; - } - - return retval; -} - -static int -vicam_open(struct file *file) -{ - struct vicam_camera *cam = video_drvdata(file); - - DBG("open\n"); - - if (!cam) { - printk(KERN_ERR - "vicam video_device improperly initialized"); - return -EINVAL; - } - - /* cam_lock/open_count protects us from simultaneous opens - * ... for now. we probably shouldn't rely on this fact forever. - */ - - mutex_lock(&cam->cam_lock); - if (cam->open_count > 0) { - printk(KERN_INFO - "vicam_open called on already opened camera"); - mutex_unlock(&cam->cam_lock); - return -EBUSY; - } - - cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); - if (!cam->raw_image) { - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - if (!cam->framebuf) { - kfree(cam->raw_image); - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!cam->cntrlbuf) { - kfree(cam->raw_image); - rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - mutex_unlock(&cam->cam_lock); - return -ENOMEM; - } - - cam->needsDummyRead = 1; - cam->open_count++; - - file->private_data = cam; - mutex_unlock(&cam->cam_lock); - - - // First upload firmware, then turn the camera on - - if (!cam->is_initialized) { - initialize_camera(cam); - - cam->is_initialized = 1; - } - - set_camera_power(cam, 1); - - return 0; -} - -static int -vicam_close(struct file *file) -{ - struct vicam_camera *cam = file->private_data; - int open_count; - struct usb_device *udev; - - DBG("close\n"); - - /* it's not the end of the world if - * we fail to turn the camera off. - */ - - set_camera_power(cam, 0); - - kfree(cam->raw_image); - rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); - kfree(cam->cntrlbuf); - - mutex_lock(&cam->cam_lock); - - cam->open_count--; - open_count = cam->open_count; - udev = cam->udev; - - mutex_unlock(&cam->cam_lock); - - if (!open_count && !udev) { - kfree(cam); - } - - return 0; -} - -static void vicam_decode_color(const u8 *data, u8 *rgb) -{ - /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB - * Copyright (C) 2002 Monroe Williams (monroe@pobox.com) - */ - - int i, prevY, nextY; - - prevY = 512; - nextY = 512; - - data += VICAM_HEADER_SIZE; - - for( i = 0; i < 240; i++, data += 512 ) { - const int y = ( i * 242 ) / 240; - - int j, prevX, nextX; - int Y, Cr, Cb; - - if ( y == 242 - 1 ) { - nextY = -512; - } - - prevX = 1; - nextX = 1; - - for ( j = 0; j < 320; j++, rgb += 3 ) { - const int x = ( j * 512 ) / 320; - const u8 * const src = &data[x]; - - if ( x == 512 - 1 ) { - nextX = -1; - } - - Cr = ( src[prevX] - src[0] ) + - ( src[nextX] - src[0] ); - Cr /= 2; - - Cb = ( src[prevY] - src[prevX + prevY] ) + - ( src[prevY] - src[nextX + prevY] ) + - ( src[nextY] - src[prevX + nextY] ) + - ( src[nextY] - src[nextX + nextY] ); - Cb /= 4; - - Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 ); - - if ( i & 1 ) { - int Ct = Cr; - Cr = Cb; - Cb = Ct; - } - - if ( ( x ^ i ) & 1 ) { - Cr = -Cr; - Cb = -Cb; - } - - rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) + - 500 ) / 900, 0, 255 ); - rgb[1] = clamp( ( ( Y - ( 392 * Cb ) - - ( 813 * Cr ) ) + - 500 ) / 1000, 0, 255 ); - rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) + - 500 ) / 1300, 0, 255 ); - - prevX = -1; - } - - prevY = -512; - } -} - -static void -read_frame(struct vicam_camera *cam, int framenum) -{ - unsigned char *request = cam->cntrlbuf; - int realShutter; - int n; - int actual_length; - - if (cam->needsDummyRead) { - cam->needsDummyRead = 0; - read_frame(cam, framenum); - } - - memset(request, 0, 16); - request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain - - request[1] = 0; // 512x242 capture - - request[2] = 0x90; // the function of these two bytes - request[3] = 0x07; // is not yet understood - - if (cam->shutter_speed > 60) { - // Short exposure - realShutter = - ((-15631900 / cam->shutter_speed) + 260533) / 1000; - request[4] = realShutter & 0xFF; - request[5] = (realShutter >> 8) & 0xFF; - request[6] = 0x03; - request[7] = 0x01; - } else { - // Long exposure - realShutter = 15600 / cam->shutter_speed - 1; - request[4] = 0; - request[5] = 0; - request[6] = realShutter & 0xFF; - request[7] = realShutter >> 8; - } - - // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 - request[8] = 0; - // bytes 9-15 do not seem to affect exposure or image quality - - mutex_lock(&cam->cam_lock); - - if (!cam->udev) { - goto done; - } - - n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16); - - if (n < 0) { - printk(KERN_ERR - " Problem sending frame capture control message"); - goto done; - } - - n = usb_bulk_msg(cam->udev, - usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), - cam->raw_image, - 512 * 242 + 128, &actual_length, 10000); - - if (n < 0) { - printk(KERN_ERR "Problem during bulk read of frame data: %d\n", - n); - } - - done: - mutex_unlock(&cam->cam_lock); -} - -static ssize_t -vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos ) -{ - struct vicam_camera *cam = file->private_data; - - DBG("read %d bytes.\n", (int) count); - - if (*ppos >= VICAM_MAX_FRAME_SIZE) { - *ppos = 0; - return 0; - } - - if (*ppos == 0) { - read_frame(cam, 0); - vicam_decode_color(cam->raw_image, - cam->framebuf + - 0 * VICAM_MAX_FRAME_SIZE); - } - - count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos); - - if (copy_to_user(buf, &cam->framebuf[*ppos], count)) { - count = -EFAULT; - } else { - *ppos += count; - } - - if (count == VICAM_MAX_FRAME_SIZE) { - *ppos = 0; - } - - return count; -} - - -static int -vicam_mmap(struct file *file, struct vm_area_struct *vma) -{ - // TODO: allocate the raw frame buffer if necessary - unsigned long page, pos; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - struct vicam_camera *cam = file->private_data; - - if (!cam) - return -ENODEV; - - DBG("vicam_mmap: %ld\n", size); - - /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes - * to the size the application requested for mmap and it was screwing apps up. - if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) - return -EINVAL; - */ - - pos = (unsigned long)cam->framebuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) - return -EAGAIN; - - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - - return 0; -} - -static const struct v4l2_file_operations vicam_fops = { - .owner = THIS_MODULE, - .open = vicam_open, - .release = vicam_close, - .read = vicam_read, - .mmap = vicam_mmap, - .ioctl = vicam_ioctl, -}; - -static struct video_device vicam_template = { - .name = "ViCam-based USB Camera", - .fops = &vicam_fops, - .release = video_device_release_empty, -}; - -/* table of devices that work with this driver */ -static struct usb_device_id vicam_table[] = { - {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, - {USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)}, - {} /* Terminating entry */ -}; - -MODULE_DEVICE_TABLE(usb, vicam_table); - -static struct usb_driver vicam_driver = { - .name = "vicam", - .probe = vicam_probe, - .disconnect = vicam_disconnect, - .id_table = vicam_table -}; - -/** - * vicam_probe - * @intf: the interface - * @id: the device id - * - * Called by the usb core when a new device is connected that it thinks - * this driver might be interested in. - */ -static int -vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - int bulkEndpoint = 0; - const struct usb_host_interface *interface; - const struct usb_endpoint_descriptor *endpoint; - struct vicam_camera *cam; - - printk(KERN_INFO "ViCam based webcam connected\n"); - - interface = intf->cur_altsetting; - - DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", - interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); - endpoint = &interface->endpoint[0].desc; - - if (usb_endpoint_is_bulk_in(endpoint)) { - /* we found a bulk in endpoint */ - bulkEndpoint = endpoint->bEndpointAddress; - } else { - printk(KERN_ERR - "No bulk in endpoint was found ?! (this is bad)\n"); - } - - if ((cam = - kzalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { - printk(KERN_WARNING - "could not allocate kernel memory for vicam_camera struct\n"); - return -ENOMEM; - } - - - cam->shutter_speed = 15; - - mutex_init(&cam->cam_lock); - - memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template)); - video_set_drvdata(&cam->vdev, cam); - - cam->udev = dev; - cam->bulkEndpoint = bulkEndpoint; - - if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) { - kfree(cam); - printk(KERN_WARNING "video_register_device failed\n"); - return -EIO; - } - - printk(KERN_INFO "ViCam webcam driver now controlling device %s\n", - video_device_node_name(&cam->vdev)); - - usb_set_intfdata (intf, cam); - - return 0; -} - -static void -vicam_disconnect(struct usb_interface *intf) -{ - int open_count; - struct vicam_camera *cam = usb_get_intfdata (intf); - usb_set_intfdata (intf, NULL); - - /* we must unregister the device before taking its - * cam_lock. This is because the video open call - * holds the same lock as video unregister. if we - * unregister inside of the cam_lock and open also - * uses the cam_lock, we get deadlock. - */ - - video_unregister_device(&cam->vdev); - - /* stop the camera from being used */ - - mutex_lock(&cam->cam_lock); - - /* mark the camera as gone */ - - cam->udev = NULL; - - /* the only thing left to do is synchronize with - * our close/release function on who should release - * the camera memory. if there are any users using the - * camera, it's their job. if there are no users, - * it's ours. - */ - - open_count = cam->open_count; - - mutex_unlock(&cam->cam_lock); - - if (!open_count) { - kfree(cam); - } - - printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); -} - -/* - */ -static int __init -usb_vicam_init(void) -{ - int retval; - DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); - retval = usb_register(&vicam_driver); - if (retval) - printk(KERN_WARNING "usb_register failed!\n"); - return retval; -} - -static void __exit -usb_vicam_exit(void) -{ - DBG(KERN_INFO - "ViCam-based WebCam driver shutdown\n"); - - usb_deregister(&vicam_driver); -} - -module_init(usb_vicam_init); -module_exit(usb_vicam_exit); - -MODULE_AUTHOR(DRIVER_AUTHOR); -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); -MODULE_FIRMWARE("vicam/firmware.fw"); diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index 5eafdf435550..a402a5614b86 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -55,6 +55,8 @@ source "drivers/staging/cpia/Kconfig" source "drivers/staging/stradis/Kconfig" +source "drivers/staging/usbvideo/Kconfig" + source "drivers/staging/usbip/Kconfig" source "drivers/staging/winbond/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index a97a955c094b..d4eb7146a87c 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_VIDEO_CX25821) += cx25821/ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_CPIA) += cpia/ obj-$(CONFIG_VIDEO_STRADIS) += stradis/ +obj-$(CONFIG_USB_VICAM) += usbvideo/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/usbvideo/Kconfig b/drivers/staging/usbvideo/Kconfig new file mode 100644 index 000000000000..aae863e8a902 --- /dev/null +++ b/drivers/staging/usbvideo/Kconfig @@ -0,0 +1,15 @@ +config VIDEO_USBVIDEO + tristate + +config USB_VICAM + tristate "USB 3com HomeConnect (aka vicam) support (DEPRECATED)" + depends on VIDEO_V4L1 && EXPERIMENTAL + select VIDEO_USBVIDEO + ---help--- + Say Y here if you have 3com homeconnect camera (vicam). + + This driver uses the deprecated V4L1 API and will be removed in + 2.6.39, unless someone converts it to the V4L2 API. + + To compile this driver as a module, choose M here: the + module will be called vicam. diff --git a/drivers/staging/usbvideo/Makefile b/drivers/staging/usbvideo/Makefile new file mode 100644 index 000000000000..3c99a9a2d8d3 --- /dev/null +++ b/drivers/staging/usbvideo/Makefile @@ -0,0 +1,2 @@ +obj-$(CONFIG_VIDEO_USBVIDEO) += usbvideo.o +obj-$(CONFIG_USB_VICAM) += vicam.o diff --git a/drivers/staging/usbvideo/usbvideo.c b/drivers/staging/usbvideo/usbvideo.c new file mode 100644 index 000000000000..f1fcf9744961 --- /dev/null +++ b/drivers/staging/usbvideo/usbvideo.c @@ -0,0 +1,2230 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "usbvideo.h" + +#if defined(MAP_NR) +#define virt_to_page(v) MAP_NR(v) /* Kernels 2.2.x */ +#endif + +static int video_nr = -1; +module_param(video_nr, int, 0); + +/* + * Local prototypes. + */ +static void usbvideo_Disconnect(struct usb_interface *intf); +static void usbvideo_CameraRelease(struct uvd *uvd); + +static long usbvideo_v4l_ioctl(struct file *file, + unsigned int cmd, unsigned long arg); +static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma); +static int usbvideo_v4l_open(struct file *file); +static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos); +static int usbvideo_v4l_close(struct file *file); + +static int usbvideo_StartDataPump(struct uvd *uvd); +static void usbvideo_StopDataPump(struct uvd *uvd); +static int usbvideo_GetFrame(struct uvd *uvd, int frameNum); +static int usbvideo_NewFrame(struct uvd *uvd, int framenum); +static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, + struct usbvideo_frame *frame); + +/*******************************/ +/* Memory management functions */ +/*******************************/ +static void *usbvideo_rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void usbvideo_rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +static void RingQueue_Initialize(struct RingQueue *rq) +{ + assert(rq != NULL); + init_waitqueue_head(&rq->wqh); +} + +static void RingQueue_Allocate(struct RingQueue *rq, int rqLen) +{ + /* Make sure the requested size is a power of 2 and + round up if necessary. This allows index wrapping + using masks rather than modulo */ + + int i = 1; + assert(rq != NULL); + assert(rqLen > 0); + + while(rqLen >> i) + i++; + if(rqLen != 1 << (i-1)) + rqLen = 1 << i; + + rq->length = rqLen; + rq->ri = rq->wi = 0; + rq->queue = usbvideo_rvmalloc(rq->length); + assert(rq->queue != NULL); +} + +static int RingQueue_IsAllocated(const struct RingQueue *rq) +{ + if (rq == NULL) + return 0; + return (rq->queue != NULL) && (rq->length > 0); +} + +static void RingQueue_Free(struct RingQueue *rq) +{ + assert(rq != NULL); + if (RingQueue_IsAllocated(rq)) { + usbvideo_rvfree(rq->queue, rq->length); + rq->queue = NULL; + rq->length = 0; + } +} + +int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len) +{ + int rql, toread; + + assert(rq != NULL); + assert(dst != NULL); + + rql = RingQueue_GetLength(rq); + if(!rql) + return 0; + + /* Clip requested length to available data */ + if(len > rql) + len = rql; + + toread = len; + if(rq->ri > rq->wi) { + /* Read data from tail */ + int read = (toread < (rq->length - rq->ri)) ? toread : rq->length - rq->ri; + memcpy(dst, rq->queue + rq->ri, read); + toread -= read; + dst += read; + rq->ri = (rq->ri + read) & (rq->length-1); + } + if(toread) { + /* Read data from head */ + memcpy(dst, rq->queue + rq->ri, toread); + rq->ri = (rq->ri + toread) & (rq->length-1); + } + return len; +} + +EXPORT_SYMBOL(RingQueue_Dequeue); + +int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n) +{ + int enqueued = 0; + + assert(rq != NULL); + assert(cdata != NULL); + assert(rq->length > 0); + while (n > 0) { + int m, q_avail; + + /* Calculate the largest chunk that fits the tail of the ring */ + q_avail = rq->length - rq->wi; + if (q_avail <= 0) { + rq->wi = 0; + q_avail = rq->length; + } + m = n; + assert(q_avail > 0); + if (m > q_avail) + m = q_avail; + + memcpy(rq->queue + rq->wi, cdata, m); + RING_QUEUE_ADVANCE_INDEX(rq, wi, m); + cdata += m; + enqueued += m; + n -= m; + } + return enqueued; +} + +EXPORT_SYMBOL(RingQueue_Enqueue); + +static void RingQueue_InterruptibleSleepOn(struct RingQueue *rq) +{ + assert(rq != NULL); + interruptible_sleep_on(&rq->wqh); +} + +void RingQueue_WakeUpInterruptible(struct RingQueue *rq) +{ + assert(rq != NULL); + if (waitqueue_active(&rq->wqh)) + wake_up_interruptible(&rq->wqh); +} + +EXPORT_SYMBOL(RingQueue_WakeUpInterruptible); + +void RingQueue_Flush(struct RingQueue *rq) +{ + assert(rq != NULL); + rq->ri = 0; + rq->wi = 0; +} + +EXPORT_SYMBOL(RingQueue_Flush); + + +/* + * usbvideo_VideosizeToString() + * + * This procedure converts given videosize value to readable string. + * + * History: + * 07-Aug-2000 Created. + * 19-Oct-2000 Reworked for usbvideo module. + */ +static void usbvideo_VideosizeToString(char *buf, int bufLen, videosize_t vs) +{ + char tmp[40]; + int n; + + n = 1 + sprintf(tmp, "%ldx%ld", VIDEOSIZE_X(vs), VIDEOSIZE_Y(vs)); + assert(n < sizeof(tmp)); + if ((buf == NULL) || (bufLen < n)) + err("usbvideo_VideosizeToString: buffer is too small."); + else + memmove(buf, tmp, n); +} + +/* + * usbvideo_OverlayChar() + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayChar(struct uvd *uvd, struct usbvideo_frame *frame, + int x, int y, int ch) +{ + static const unsigned short digits[16] = { + 0xF6DE, /* 0 */ + 0x2492, /* 1 */ + 0xE7CE, /* 2 */ + 0xE79E, /* 3 */ + 0xB792, /* 4 */ + 0xF39E, /* 5 */ + 0xF3DE, /* 6 */ + 0xF492, /* 7 */ + 0xF7DE, /* 8 */ + 0xF79E, /* 9 */ + 0x77DA, /* a */ + 0xD75C, /* b */ + 0xF24E, /* c */ + 0xD6DC, /* d */ + 0xF34E, /* e */ + 0xF348 /* f */ + }; + unsigned short digit; + int ix, iy; + int value; + + if ((uvd == NULL) || (frame == NULL)) + return; + + value = hex_to_bin(ch); + if (value < 0) + return; + digit = digits[value]; + + for (iy=0; iy < 5; iy++) { + for (ix=0; ix < 3; ix++) { + if (digit & 0x8000) { + if (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24)) { +/* TODO */ RGB24_PUTPIXEL(frame, x+ix, y+iy, 0xFF, 0xFF, 0xFF); + } + } + digit = digit << 1; + } + } +} + +/* + * usbvideo_OverlayString() + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayString(struct uvd *uvd, struct usbvideo_frame *frame, + int x, int y, const char *str) +{ + while (*str) { + usbvideo_OverlayChar(uvd, frame, x, y, *str); + str++; + x += 4; /* 3 pixels character + 1 space */ + } +} + +/* + * usbvideo_OverlayStats() + * + * Overlays important debugging information. + * + * History: + * 01-Feb-2000 Created. + */ +static void usbvideo_OverlayStats(struct uvd *uvd, struct usbvideo_frame *frame) +{ + const int y_diff = 8; + char tmp[16]; + int x = 10, y=10; + long i, j, barLength; + const int qi_x1 = 60, qi_y1 = 10; + const int qi_x2 = VIDEOSIZE_X(frame->request) - 10, qi_h = 10; + + /* Call the user callback, see if we may proceed after that */ + if (VALID_CALLBACK(uvd, overlayHook)) { + if (GET_CALLBACK(uvd, overlayHook)(uvd, frame) < 0) + return; + } + + /* + * We draw a (mostly) hollow rectangle with qi_xxx coordinates. + * Left edge symbolizes the queue index 0; right edge symbolizes + * the full capacity of the queue. + */ + barLength = qi_x2 - qi_x1 - 2; + if ((barLength > 10) && (uvd->paletteBits & (1L << VIDEO_PALETTE_RGB24))) { +/* TODO */ long u_lo, u_hi, q_used; + long m_ri, m_wi, m_lo, m_hi; + + /* + * Determine fill zones (used areas of the queue): + * 0 xxxxxxx u_lo ...... uvd->dp.ri xxxxxxxx u_hi ..... uvd->dp.length + * + * if u_lo < 0 then there is no first filler. + */ + + q_used = RingQueue_GetLength(&uvd->dp); + if ((uvd->dp.ri + q_used) >= uvd->dp.length) { + u_hi = uvd->dp.length; + u_lo = (q_used + uvd->dp.ri) & (uvd->dp.length-1); + } else { + u_hi = (q_used + uvd->dp.ri); + u_lo = -1; + } + + /* Convert byte indices into screen units */ + m_ri = qi_x1 + ((barLength * uvd->dp.ri) / uvd->dp.length); + m_wi = qi_x1 + ((barLength * uvd->dp.wi) / uvd->dp.length); + m_lo = (u_lo > 0) ? (qi_x1 + ((barLength * u_lo) / uvd->dp.length)) : -1; + m_hi = qi_x1 + ((barLength * u_hi) / uvd->dp.length); + + for (j=qi_y1; j < (qi_y1 + qi_h); j++) { + for (i=qi_x1; i < qi_x2; i++) { + /* Draw border lines */ + if ((j == qi_y1) || (j == (qi_y1 + qi_h - 1)) || + (i == qi_x1) || (i == (qi_x2 - 1))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0xFF, 0xFF); + continue; + } + /* For all other points the Y coordinate does not matter */ + if ((i >= m_ri) && (i <= (m_ri + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0x00, 0xFF, 0x00); + } else if ((i >= m_wi) && (i <= (m_wi + 3))) { + RGB24_PUTPIXEL(frame, i, j, 0xFF, 0x00, 0x00); + } else if ((i < m_lo) || ((i > m_ri) && (i < m_hi))) + RGB24_PUTPIXEL(frame, i, j, 0x00, 0x00, 0xFF); + } + } + } + + sprintf(tmp, "%8lx", uvd->stats.frame_num); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.urb_length); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.data_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.header_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_skip_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8lx", uvd->stats.iso_err_count); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.colour); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.hue); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.brightness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8x", uvd->vpic.contrast >> 12); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; + + sprintf(tmp, "%8d", uvd->vpic.whiteness >> 8); + usbvideo_OverlayString(uvd, frame, x, y, tmp); + y += y_diff; +} + +/* + * usbvideo_ReportStatistics() + * + * This procedure prints packet and transfer statistics. + * + * History: + * 14-Jan-2000 Corrected default multiplier. + */ +static void usbvideo_ReportStatistics(const struct uvd *uvd) +{ + if ((uvd != NULL) && (uvd->stats.urb_count > 0)) { + unsigned long allPackets, badPackets, goodPackets, percent; + allPackets = uvd->stats.urb_count * CAMERA_URB_FRAMES; + badPackets = uvd->stats.iso_skip_count + uvd->stats.iso_err_count; + goodPackets = allPackets - badPackets; + /* Calculate percentage wisely, remember integer limits */ + assert(allPackets != 0); + if (goodPackets < (((unsigned long)-1)/100)) + percent = (100 * goodPackets) / allPackets; + else + percent = goodPackets / (allPackets / 100); + dev_info(&uvd->dev->dev, + "Packet Statistics: Total=%lu. Empty=%lu. Usage=%lu%%\n", + allPackets, badPackets, percent); + if (uvd->iso_packet_len > 0) { + unsigned long allBytes, xferBytes; + char multiplier = ' '; + allBytes = allPackets * uvd->iso_packet_len; + xferBytes = uvd->stats.data_count; + assert(allBytes != 0); + if (xferBytes < (((unsigned long)-1)/100)) + percent = (100 * xferBytes) / allBytes; + else + percent = xferBytes / (allBytes / 100); + /* Scale xferBytes for easy reading */ + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'K'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'M'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'G'; + if (xferBytes > 10*1024) { + xferBytes /= 1024; + multiplier = 'T'; + } + } + } + } + dev_info(&uvd->dev->dev, + "Transfer Statistics: Transferred=%lu%cB Usage=%lu%%\n", + xferBytes, multiplier, percent); + } + } +} + +/* + * usbvideo_TestPattern() + * + * Procedure forms a test pattern (yellow grid on blue background). + * + * Parameters: + * fullframe: if TRUE then entire frame is filled, otherwise the procedure + * continues from the current scanline. + * pmode 0: fill the frame with solid blue color (like on VCR or TV) + * 1: Draw a colored grid + * + * History: + * 01-Feb-2000 Created. + */ +void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode) +{ + struct usbvideo_frame *frame; + int num_cell = 0; + int scan_length = 0; + static int num_pass; + + if (uvd == NULL) { + err("%s: uvd == NULL", __func__); + return; + } + if ((uvd->curframe < 0) || (uvd->curframe >= USBVIDEO_NUMFRAMES)) { + err("%s: uvd->curframe=%d.", __func__, uvd->curframe); + return; + } + + /* Grab the current frame */ + frame = &uvd->frame[uvd->curframe]; + + /* Optionally start at the beginning */ + if (fullframe) { + frame->curline = 0; + frame->seqRead_Length = 0; + } +#if 0 + { /* For debugging purposes only */ + char tmp[20]; + usbvideo_VideosizeToString(tmp, sizeof(tmp), frame->request); + dev_info(&uvd->dev->dev, "testpattern: frame=%s\n", tmp); + } +#endif + /* Form every scan line */ + for (; frame->curline < VIDEOSIZE_Y(frame->request); frame->curline++) { + int i; + unsigned char *f = frame->data + + (VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL * frame->curline); + for (i=0; i < VIDEOSIZE_X(frame->request); i++) { + unsigned char cb=0x80; + unsigned char cg = 0; + unsigned char cr = 0; + + if (pmode == 1) { + if (frame->curline % 32 == 0) + cb = 0, cg = cr = 0xFF; + else if (i % 32 == 0) { + if (frame->curline % 32 == 1) + num_cell++; + cb = 0, cg = cr = 0xFF; + } else { + cb = ((num_cell*7) + num_pass) & 0xFF; + cg = ((num_cell*5) + num_pass*2) & 0xFF; + cr = ((num_cell*3) + num_pass*3) & 0xFF; + } + } else { + /* Just the blue screen */ + } + + *f++ = cb; + *f++ = cg; + *f++ = cr; + scan_length += 3; + } + } + + frame->frameState = FrameState_Done; + frame->seqRead_Length += scan_length; + ++num_pass; + + /* We do this unconditionally, regardless of FLAGS_OVERLAY_STATS */ + usbvideo_OverlayStats(uvd, frame); +} + +EXPORT_SYMBOL(usbvideo_TestPattern); + + +#ifdef DEBUG +/* + * usbvideo_HexDump() + * + * A debugging tool. Prints hex dumps. + * + * History: + * 29-Jul-2000 Added printing of offsets. + */ +void usbvideo_HexDump(const unsigned char *data, int len) +{ + const int bytes_per_line = 32; + char tmp[128]; /* 32*3 + 5 */ + int i, k; + + for (i=k=0; len > 0; i++, len--) { + if (i > 0 && ((i % bytes_per_line) == 0)) { + printk("%s\n", tmp); + k=0; + } + if ((i % bytes_per_line) == 0) + k += sprintf(&tmp[k], "%04x: ", i); + k += sprintf(&tmp[k], "%02x ", data[i]); + } + if (k > 0) + printk("%s\n", tmp); +} + +EXPORT_SYMBOL(usbvideo_HexDump); + +#endif + +/* ******************************************************************** */ + +/* XXX: this piece of crap really wants some error handling.. */ +static int usbvideo_ClientIncModCount(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: uvd == NULL", __func__); + return -EINVAL; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", __func__); + return -EINVAL; + } + if (!try_module_get(uvd->handle->md_module)) { + err("%s: try_module_get() == 0", __func__); + return -ENODEV; + } + return 0; +} + +static void usbvideo_ClientDecModCount(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: uvd == NULL", __func__); + return; + } + if (uvd->handle == NULL) { + err("%s: uvd->handle == NULL", __func__); + return; + } + if (uvd->handle->md_module == NULL) { + err("%s: uvd->handle->md_module == NULL", __func__); + return; + } + module_put(uvd->handle->md_module); +} + +int usbvideo_register( + struct usbvideo **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const struct usbvideo_cb *cbTbl, + struct module *md, + const struct usb_device_id *id_table) +{ + struct usbvideo *cams; + int i, base_size, result; + + /* Check parameters for sanity */ + if ((num_cams <= 0) || (pCams == NULL) || (cbTbl == NULL)) { + err("%s: Illegal call", __func__); + return -EINVAL; + } + + /* Check registration callback - must be set! */ + if (cbTbl->probe == NULL) { + err("%s: probe() is required!", __func__); + return -EINVAL; + } + + base_size = num_cams * sizeof(struct uvd) + sizeof(struct usbvideo); + cams = kzalloc(base_size, GFP_KERNEL); + if (cams == NULL) { + err("Failed to allocate %d. bytes for usbvideo struct", base_size); + return -ENOMEM; + } + dbg("%s: Allocated $%p (%d. bytes) for %d. cameras", + __func__, cams, base_size, num_cams); + + /* Copy callbacks, apply defaults for those that are not set */ + memmove(&cams->cb, cbTbl, sizeof(cams->cb)); + if (cams->cb.getFrame == NULL) + cams->cb.getFrame = usbvideo_GetFrame; + if (cams->cb.disconnect == NULL) + cams->cb.disconnect = usbvideo_Disconnect; + if (cams->cb.startDataPump == NULL) + cams->cb.startDataPump = usbvideo_StartDataPump; + if (cams->cb.stopDataPump == NULL) + cams->cb.stopDataPump = usbvideo_StopDataPump; + + cams->num_cameras = num_cams; + cams->cam = (struct uvd *) &cams[1]; + cams->md_module = md; + mutex_init(&cams->lock); /* to 1 == available */ + + for (i = 0; i < num_cams; i++) { + struct uvd *up = &cams->cam[i]; + + up->handle = cams; + + /* Allocate user_data separately because of kmalloc's limits */ + if (num_extra > 0) { + up->user_size = num_cams * num_extra; + up->user_data = kmalloc(up->user_size, GFP_KERNEL); + if (up->user_data == NULL) { + err("%s: Failed to allocate user_data (%d. bytes)", + __func__, up->user_size); + while (i) { + up = &cams->cam[--i]; + kfree(up->user_data); + } + kfree(cams); + return -ENOMEM; + } + dbg("%s: Allocated cams[%d].user_data=$%p (%d. bytes)", + __func__, i, up->user_data, up->user_size); + } + } + + /* + * Register ourselves with USB stack. + */ + strcpy(cams->drvName, (driverName != NULL) ? driverName : "Unknown"); + cams->usbdrv.name = cams->drvName; + cams->usbdrv.probe = cams->cb.probe; + cams->usbdrv.disconnect = cams->cb.disconnect; + cams->usbdrv.id_table = id_table; + + /* + * Update global handle to usbvideo. This is very important + * because probe() can be called before usb_register() returns. + * If the handle is not yet updated then the probe() will fail. + */ + *pCams = cams; + result = usb_register(&cams->usbdrv); + if (result) { + for (i = 0; i < num_cams; i++) { + struct uvd *up = &cams->cam[i]; + kfree(up->user_data); + } + kfree(cams); + } + + return result; +} + +EXPORT_SYMBOL(usbvideo_register); + +/* + * usbvideo_Deregister() + * + * Procedure frees all usbvideo and user data structures. Be warned that + * if you had some dynamically allocated components in ->user field then + * you should free them before calling here. + */ +void usbvideo_Deregister(struct usbvideo **pCams) +{ + struct usbvideo *cams; + int i; + + if (pCams == NULL) { + err("%s: pCams == NULL", __func__); + return; + } + cams = *pCams; + if (cams == NULL) { + err("%s: cams == NULL", __func__); + return; + } + + dbg("%s: Deregistering %s driver.", __func__, cams->drvName); + usb_deregister(&cams->usbdrv); + + dbg("%s: Deallocating cams=$%p (%d. cameras)", __func__, cams, cams->num_cameras); + for (i=0; i < cams->num_cameras; i++) { + struct uvd *up = &cams->cam[i]; + int warning = 0; + + if (up->user_data != NULL) { + if (up->user_size <= 0) + ++warning; + } else { + if (up->user_size > 0) + ++warning; + } + if (warning) { + err("%s: Warning: user_data=$%p user_size=%d.", + __func__, up->user_data, up->user_size); + } else { + dbg("%s: Freeing %d. $%p->user_data=$%p", + __func__, i, up, up->user_data); + kfree(up->user_data); + } + } + /* Whole array was allocated in one chunk */ + dbg("%s: Freed %d uvd structures", + __func__, cams->num_cameras); + kfree(cams); + *pCams = NULL; +} + +EXPORT_SYMBOL(usbvideo_Deregister); + +/* + * usbvideo_Disconnect() + * + * This procedure stops all driver activity. Deallocation of + * the interface-private structure (pointed by 'ptr') is done now + * (if we don't have any open files) or later, when those files + * are closed. After that driver should be removable. + * + * This code handles surprise removal. The uvd->user is a counter which + * increments on open() and decrements on close(). If we see here that + * this counter is not 0 then we have a client who still has us opened. + * We set uvd->remove_pending flag as early as possible, and after that + * all access to the camera will gracefully fail. These failures should + * prompt client to (eventually) close the video device, and then - in + * usbvideo_v4l_close() - we decrement uvd->uvd_used and usage counter. + * + * History: + * 22-Jan-2000 Added polling of MOD_IN_USE to delay removal until all users gone. + * 27-Jan-2000 Reworked to allow pending disconnects; see xxx_close() + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + * 19-Oct-2000 Moved to usbvideo module. + */ +static void usbvideo_Disconnect(struct usb_interface *intf) +{ + struct uvd *uvd = usb_get_intfdata (intf); + int i; + + if (uvd == NULL) { + err("%s($%p): Illegal call.", __func__, intf); + return; + } + + usb_set_intfdata (intf, NULL); + + usbvideo_ClientIncModCount(uvd); + if (uvd->debug > 0) + dev_info(&intf->dev, "%s(%p.)\n", __func__, intf); + + mutex_lock(&uvd->lock); + uvd->remove_pending = 1; /* Now all ISO data will be ignored */ + + /* At this time we ask to cancel outstanding URBs */ + GET_CALLBACK(uvd, stopDataPump)(uvd); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) + usb_free_urb(uvd->sbuf[i].urb); + + usb_put_dev(uvd->dev); + uvd->dev = NULL; /* USB device is no more */ + + video_unregister_device(&uvd->vdev); + if (uvd->debug > 0) + dev_info(&intf->dev, "%s: Video unregistered.\n", __func__); + + if (uvd->user) + dev_info(&intf->dev, "%s: In use, disconnect pending.\n", + __func__); + else + usbvideo_CameraRelease(uvd); + mutex_unlock(&uvd->lock); + dev_info(&intf->dev, "USB camera disconnected.\n"); + + usbvideo_ClientDecModCount(uvd); +} + +/* + * usbvideo_CameraRelease() + * + * This code does final release of uvd. This happens + * after the device is disconnected -and- all clients + * closed their files. + * + * History: + * 27-Jan-2000 Created. + */ +static void usbvideo_CameraRelease(struct uvd *uvd) +{ + if (uvd == NULL) { + err("%s: Illegal call", __func__); + return; + } + + RingQueue_Free(&uvd->dp); + if (VALID_CALLBACK(uvd, userFree)) + GET_CALLBACK(uvd, userFree)(uvd); + uvd->uvd_used = 0; /* This is atomic, no need to take mutex */ +} + +/* + * usbvideo_find_struct() + * + * This code searches the array of preallocated (static) structures + * and returns index of the first one that isn't in use. Returns -1 + * if there are no free structures. + * + * History: + * 27-Jan-2000 Created. + */ +static int usbvideo_find_struct(struct usbvideo *cams) +{ + int u, rv = -1; + + if (cams == NULL) { + err("No usbvideo handle?"); + return -1; + } + mutex_lock(&cams->lock); + for (u = 0; u < cams->num_cameras; u++) { + struct uvd *uvd = &cams->cam[u]; + if (!uvd->uvd_used) /* This one is free */ + { + uvd->uvd_used = 1; /* In use now */ + mutex_init(&uvd->lock); /* to 1 == available */ + uvd->dev = NULL; + rv = u; + break; + } + } + mutex_unlock(&cams->lock); + return rv; +} + +static const struct v4l2_file_operations usbvideo_fops = { + .owner = THIS_MODULE, + .open = usbvideo_v4l_open, + .release =usbvideo_v4l_close, + .read = usbvideo_v4l_read, + .mmap = usbvideo_v4l_mmap, + .ioctl = usbvideo_v4l_ioctl, +}; +static const struct video_device usbvideo_template = { + .fops = &usbvideo_fops, +}; + +struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams) +{ + int i, devnum; + struct uvd *uvd = NULL; + + if (cams == NULL) { + err("No usbvideo handle?"); + return NULL; + } + + devnum = usbvideo_find_struct(cams); + if (devnum == -1) { + err("IBM USB camera driver: Too many devices!"); + return NULL; + } + uvd = &cams->cam[devnum]; + dbg("Device entry #%d. at $%p", devnum, uvd); + + /* Not relying upon caller we increase module counter ourselves */ + usbvideo_ClientIncModCount(uvd); + + mutex_lock(&uvd->lock); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].urb = usb_alloc_urb(FRAMES_PER_DESC, GFP_KERNEL); + if (uvd->sbuf[i].urb == NULL) { + err("usb_alloc_urb(%d.) failed.", FRAMES_PER_DESC); + uvd->uvd_used = 0; + uvd = NULL; + goto allocate_done; + } + } + uvd->user=0; + uvd->remove_pending = 0; + uvd->last_error = 0; + RingQueue_Initialize(&uvd->dp); + + /* Initialize video device structure */ + uvd->vdev = usbvideo_template; + sprintf(uvd->vdev.name, "%.20s USB Camera", cams->drvName); + /* + * The client is free to overwrite those because we + * return control to the client's probe function right now. + */ +allocate_done: + mutex_unlock(&uvd->lock); + usbvideo_ClientDecModCount(uvd); + return uvd; +} + +EXPORT_SYMBOL(usbvideo_AllocateDevice); + +int usbvideo_RegisterVideoDevice(struct uvd *uvd) +{ + char tmp1[20], tmp2[20]; /* Buffers for printing */ + + if (uvd == NULL) { + err("%s: Illegal call.", __func__); + return -EINVAL; + } + if (uvd->video_endp == 0) { + dev_info(&uvd->dev->dev, + "%s: No video endpoint specified; data pump disabled.\n", + __func__); + } + if (uvd->paletteBits == 0) { + err("%s: No palettes specified!", __func__); + return -EINVAL; + } + if (uvd->defaultPalette == 0) { + dev_info(&uvd->dev->dev, "%s: No default palette!\n", + __func__); + } + + uvd->max_frame_size = VIDEOSIZE_X(uvd->canvas) * + VIDEOSIZE_Y(uvd->canvas) * V4L_BYTES_PER_PIXEL; + usbvideo_VideosizeToString(tmp1, sizeof(tmp1), uvd->videosize); + usbvideo_VideosizeToString(tmp2, sizeof(tmp2), uvd->canvas); + + if (uvd->debug > 0) { + dev_info(&uvd->dev->dev, + "%s: iface=%d. endpoint=$%02x paletteBits=$%08lx\n", + __func__, uvd->iface, uvd->video_endp, + uvd->paletteBits); + } + if (uvd->dev == NULL) { + err("%s: uvd->dev == NULL", __func__); + return -EINVAL; + } + uvd->vdev.parent = &uvd->dev->dev; + uvd->vdev.release = video_device_release_empty; + if (video_register_device(&uvd->vdev, VFL_TYPE_GRABBER, video_nr) < 0) { + err("%s: video_register_device failed", __func__); + return -EPIPE; + } + if (uvd->debug > 1) { + dev_info(&uvd->dev->dev, + "%s: video_register_device() successful\n", __func__); + } + + dev_info(&uvd->dev->dev, "%s on %s: canvas=%s videosize=%s\n", + (uvd->handle != NULL) ? uvd->handle->drvName : "???", + video_device_node_name(&uvd->vdev), tmp2, tmp1); + + usb_get_dev(uvd->dev); + return 0; +} + +EXPORT_SYMBOL(usbvideo_RegisterVideoDevice); + +/* ******************************************************************** */ + +static int usbvideo_v4l_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct uvd *uvd = file->private_data; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EFAULT; + + if (size > (((USBVIDEO_NUMFRAMES * uvd->max_frame_size) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1))) + return -EINVAL; + + pos = (unsigned long) uvd->fbuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +/* + * usbvideo_v4l_open() + * + * This is part of Video 4 Linux API. The driver can be opened by one + * client only (checks internal counter 'uvdser'). The procedure + * then allocates buffers needed for video processing. + * + * History: + * 22-Jan-2000 Rewrote, moved scratch buffer allocation here. Now the + * camera is also initialized here (once per connect), at + * expense of V4L client (it waits on open() call). + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Corrected to prevent race condition (MOD_xxx_USE_COUNT). + */ +static int usbvideo_v4l_open(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct uvd *uvd = (struct uvd *) dev; + const int sb_size = FRAMES_PER_DESC * uvd->iso_packet_len; + int i, errCode = 0; + + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); + + if (usbvideo_ClientIncModCount(uvd) < 0) + return -ENODEV; + mutex_lock(&uvd->lock); + + if (uvd->user) { + err("%s: Someone tried to open an already opened device!", __func__); + errCode = -EBUSY; + } else { + /* Clear statistics */ + memset(&uvd->stats, 0, sizeof(uvd->stats)); + + /* Clean pointers so we know if we allocated something */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) + uvd->sbuf[i].data = NULL; + + /* Allocate memory for the frame buffers */ + uvd->fbuf_size = USBVIDEO_NUMFRAMES * uvd->max_frame_size; + uvd->fbuf = usbvideo_rvmalloc(uvd->fbuf_size); + RingQueue_Allocate(&uvd->dp, RING_QUEUE_SIZE); + if ((uvd->fbuf == NULL) || + (!RingQueue_IsAllocated(&uvd->dp))) { + err("%s: Failed to allocate fbuf or dp", __func__); + errCode = -ENOMEM; + } else { + /* Allocate all buffers */ + for (i=0; i < USBVIDEO_NUMFRAMES; i++) { + uvd->frame[i].frameState = FrameState_Unused; + uvd->frame[i].data = uvd->fbuf + i*(uvd->max_frame_size); + /* + * Set default sizes in case IOCTL (VIDIOCMCAPTURE) + * is not used (using read() instead). + */ + uvd->frame[i].canvas = uvd->canvas; + uvd->frame[i].seqRead_Index = 0; + } + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + uvd->sbuf[i].data = kmalloc(sb_size, GFP_KERNEL); + if (uvd->sbuf[i].data == NULL) { + errCode = -ENOMEM; + break; + } + } + } + if (errCode != 0) { + /* Have to free all that memory */ + if (uvd->fbuf != NULL) { + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + } + RingQueue_Free(&uvd->dp); + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + kfree(uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + } + } + + /* If so far no errors then we shall start the camera */ + if (errCode == 0) { + /* Start data pump if we have valid endpoint */ + if (uvd->video_endp != 0) + errCode = GET_CALLBACK(uvd, startDataPump)(uvd); + if (errCode == 0) { + if (VALID_CALLBACK(uvd, setupOnOpen)) { + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, + "%s: setupOnOpen callback\n", + __func__); + errCode = GET_CALLBACK(uvd, setupOnOpen)(uvd); + if (errCode < 0) { + err("%s: setupOnOpen callback failed (%d.).", + __func__, errCode); + } else if (uvd->debug > 1) { + dev_info(&uvd->dev->dev, + "%s: setupOnOpen callback successful\n", + __func__); + } + } + if (errCode == 0) { + uvd->settingsAdjusted = 0; + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, + "%s: Open succeeded.\n", + __func__); + uvd->user++; + file->private_data = uvd; + } + } + } + mutex_unlock(&uvd->lock); + if (errCode != 0) + usbvideo_ClientDecModCount(uvd); + if (uvd->debug > 0) + dev_info(&uvd->dev->dev, "%s: Returning %d.\n", __func__, + errCode); + return errCode; +} + +/* + * usbvideo_v4l_close() + * + * This is part of Video 4 Linux API. The procedure + * stops streaming and deallocates all buffers that were earlier + * allocated in usbvideo_v4l_open(). + * + * History: + * 22-Jan-2000 Moved scratch buffer deallocation here. + * 27-Jan-2000 Used USBVIDEO_NUMSBUF as number of URB buffers. + * 24-May-2000 Moved MOD_DEC_USE_COUNT outside of code that can sleep. + */ +static int usbvideo_v4l_close(struct file *file) +{ + struct video_device *dev = file->private_data; + struct uvd *uvd = (struct uvd *) dev; + int i; + + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, dev); + + mutex_lock(&uvd->lock); + GET_CALLBACK(uvd, stopDataPump)(uvd); + usbvideo_rvfree(uvd->fbuf, uvd->fbuf_size); + uvd->fbuf = NULL; + RingQueue_Free(&uvd->dp); + + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + kfree(uvd->sbuf[i].data); + uvd->sbuf[i].data = NULL; + } + +#if USBVIDEO_REPORT_STATS + usbvideo_ReportStatistics(uvd); +#endif + + uvd->user--; + if (uvd->remove_pending) { + if (uvd->debug > 0) + dev_info(&uvd->dev->dev, "%s: Final disconnect.\n", + __func__); + usbvideo_CameraRelease(uvd); + } + mutex_unlock(&uvd->lock); + usbvideo_ClientDecModCount(uvd); + + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, "%s: Completed.\n", __func__); + file->private_data = NULL; + return 0; +} + +/* + * usbvideo_v4l_ioctl() + * + * This is part of Video 4 Linux API. The procedure handles ioctl() calls. + * + * History: + * 22-Jan-2000 Corrected VIDIOCSPICT to reject unsupported settings. + */ +static long usbvideo_v4l_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + struct uvd *uvd = file->private_data; + + if (!CAMERA_IS_OPERATIONAL(uvd)) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + *b = uvd->vcap; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + *v = uvd->vchan; + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *pic = arg; + *pic = uvd->vpic; + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *pic = arg; + /* + * Use temporary 'video_picture' structure to preserve our + * own settings (such as color depth, palette) that we + * aren't allowing everyone (V4L client) to change. + */ + uvd->vpic.brightness = pic->brightness; + uvd->vpic.hue = pic->hue; + uvd->vpic.colour = pic->colour; + uvd->vpic.contrast = pic->contrast; + uvd->settingsAdjusted = 0; /* Will force new settings */ + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if(VALID_CALLBACK(uvd, setVideoMode)) { + return GET_CALLBACK(uvd, setVideoMode)(uvd, vw); + } + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (vw->width != VIDEOSIZE_X(uvd->canvas)) + return -EINVAL; + if (vw->height != VIDEOSIZE_Y(uvd->canvas)) + return -EINVAL; + + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; + vw->y = 0; + vw->width = VIDEOSIZE_X(uvd->videosize); + vw->height = VIDEOSIZE_Y(uvd->videosize); + vw->chromakey = 0; + if (VALID_CALLBACK(uvd, getFPS)) + vw->flags = GET_CALLBACK(uvd, getFPS)(uvd); + else + vw->flags = 10; /* FIXME: do better! */ + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = uvd->max_frame_size * USBVIDEO_NUMFRAMES; + vm->frames = USBVIDEO_NUMFRAMES; + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) + vm->offsets[i] = i * uvd->max_frame_size; + + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + + if (uvd->debug >= 1) { + dev_info(&uvd->dev->dev, + "VIDIOCMCAPTURE: frame=%d. size=%dx%d, format=%d.\n", + vm->frame, vm->width, vm->height, vm->format); + } + /* + * Check if the requested size is supported. If the requestor + * requests too big a frame then we may be tricked into accessing + * outside of own preallocated frame buffer (in uvd->frame). + * This will cause oops or a security hole. Theoretically, we + * could only clamp the size down to acceptable bounds, but then + * we'd need to figure out how to insert our smaller buffer into + * larger caller's buffer... this is not an easy question. So we + * here just flatly reject too large requests, assuming that the + * caller will resubmit with smaller size. Callers should know + * what size we support (returned by VIDIOCGCAP). However vidcat, + * for one, does not care and allows to ask for any size. + */ + if ((vm->width > VIDEOSIZE_X(uvd->canvas)) || + (vm->height > VIDEOSIZE_Y(uvd->canvas))) { + if (uvd->debug > 0) { + dev_info(&uvd->dev->dev, + "VIDIOCMCAPTURE: Size=%dx%d " + "too large; allowed only up " + "to %ldx%ld\n", vm->width, + vm->height, + VIDEOSIZE_X(uvd->canvas), + VIDEOSIZE_Y(uvd->canvas)); + } + return -EINVAL; + } + /* Check if the palette is supported */ + if (((1L << vm->format) & uvd->paletteBits) == 0) { + if (uvd->debug > 0) { + dev_info(&uvd->dev->dev, + "VIDIOCMCAPTURE: format=%d. " + "not supported " + "(paletteBits=$%08lx)\n", + vm->format, uvd->paletteBits); + } + return -EINVAL; + } + if ((vm->frame < 0) || (vm->frame >= USBVIDEO_NUMFRAMES)) { + err("VIDIOCMCAPTURE: vm.frame=%d. !E [0-%d]", vm->frame, USBVIDEO_NUMFRAMES-1); + return -EINVAL; + } + if (uvd->frame[vm->frame].frameState == FrameState_Grabbing) { + /* Not an error - can happen */ + } + uvd->frame[vm->frame].request = VIDEOSIZE(vm->width, vm->height); + uvd->frame[vm->frame].palette = vm->format; + + /* Mark it as ready */ + uvd->frame[vm->frame].frameState = FrameState_Ready; + + return usbvideo_NewFrame(uvd, vm->frame); + } + case VIDIOCSYNC: + { + int *frameNum = arg; + int ret; + + if (*frameNum < 0 || *frameNum >= USBVIDEO_NUMFRAMES) + return -EINVAL; + + if (uvd->debug >= 1) + dev_info(&uvd->dev->dev, + "VIDIOCSYNC: syncing to frame %d.\n", + *frameNum); + if (uvd->flags & FLAGS_NO_DECODING) + ret = usbvideo_GetFrame(uvd, *frameNum); + else if (VALID_CALLBACK(uvd, getFrame)) { + ret = GET_CALLBACK(uvd, getFrame)(uvd, *frameNum); + if ((ret < 0) && (uvd->debug >= 1)) { + err("VIDIOCSYNC: getFrame() returned %d.", ret); + } + } else { + err("VIDIOCSYNC: getFrame is not set"); + ret = -EFAULT; + } + + /* + * The frame is in FrameState_Done_Hold state. Release it + * right now because its data is already mapped into + * the user space and it's up to the application to + * make use of it until it asks for another frame. + */ + uvd->frame[*frameNum].frameState = FrameState_Unused; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb, 0, sizeof(*vb)); + return 0; + } + case VIDIOCKEY: + return 0; + + case VIDIOCCAPTURE: + return -EINVAL; + + case VIDIOCSFBUF: + + case VIDIOCGTUNER: + case VIDIOCSTUNER: + + case VIDIOCGFREQ: + case VIDIOCSFREQ: + + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + + default: + return -ENOIOCTLCMD; + } + return 0; +} + +static long usbvideo_v4l_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, usbvideo_v4l_do_ioctl); +} + +/* + * usbvideo_v4l_read() + * + * This is mostly boring stuff. We simply ask for a frame and when it + * arrives copy all the video data from it into user space. There is + * no obvious need to override this method. + * + * History: + * 20-Oct-2000 Created. + * 01-Nov-2000 Added mutex (uvd->lock). + */ +static ssize_t usbvideo_v4l_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + struct uvd *uvd = file->private_data; + int noblock = file->f_flags & O_NONBLOCK; + int frmx = -1, i; + struct usbvideo_frame *frame; + + if (!CAMERA_IS_OPERATIONAL(uvd) || (buf == NULL)) + return -EFAULT; + + if (uvd->debug >= 1) + dev_info(&uvd->dev->dev, + "%s: %Zd. bytes, noblock=%d.\n", + __func__, count, noblock); + + mutex_lock(&uvd->lock); + + /* See if a frame is completed, then use it. */ + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { + if ((uvd->frame[i].frameState == FrameState_Done) || + (uvd->frame[i].frameState == FrameState_Done_Hold) || + (uvd->frame[i].frameState == FrameState_Error)) { + frmx = i; + break; + } + } + + /* FIXME: If we don't start a frame here then who ever does? */ + if (noblock && (frmx == -1)) { + count = -EAGAIN; + goto read_done; + } + + /* + * If no FrameState_Done, look for a FrameState_Grabbing state. + * See if a frame is in process (grabbing), then use it. + * We will need to wait until it becomes cooked, of course. + */ + if (frmx == -1) { + for(i = 0; i < USBVIDEO_NUMFRAMES; i++) { + if (uvd->frame[i].frameState == FrameState_Grabbing) { + frmx = i; + break; + } + } + } + + /* + * If no frame is active, start one. We don't care which one + * it will be, so #0 is as good as any. + * In read access mode we don't have convenience of VIDIOCMCAPTURE + * to specify the requested palette (video format) on per-frame + * basis. This means that we have to return data in -some- format + * and just hope that the client knows what to do with it. + * The default format is configured in uvd->defaultPalette field + * as one of VIDEO_PALETTE_xxx values. We stuff it into the new + * frame and initiate the frame filling process. + */ + if (frmx == -1) { + if (uvd->defaultPalette == 0) { + err("%s: No default palette; don't know what to do!", __func__); + count = -EFAULT; + goto read_done; + } + frmx = 0; + /* + * We have no per-frame control over video size. + * Therefore we only can use whatever size was + * specified as default. + */ + uvd->frame[frmx].request = uvd->videosize; + uvd->frame[frmx].palette = uvd->defaultPalette; + uvd->frame[frmx].frameState = FrameState_Ready; + usbvideo_NewFrame(uvd, frmx); + /* Now frame 0 is supposed to start filling... */ + } + + /* + * Get a pointer to the active frame. It is either previously + * completed frame or frame in progress but not completed yet. + */ + frame = &uvd->frame[frmx]; + + /* + * Sit back & wait until the frame gets filled and postprocessed. + * If we fail to get the picture [in time] then return the error. + * In this call we specify that we want the frame to be waited for, + * postprocessed and switched into FrameState_Done_Hold state. This + * state is used to hold the frame as "fully completed" between + * subsequent partial reads of the same frame. + */ + if (frame->frameState != FrameState_Done_Hold) { + long rv = -EFAULT; + if (uvd->flags & FLAGS_NO_DECODING) + rv = usbvideo_GetFrame(uvd, frmx); + else if (VALID_CALLBACK(uvd, getFrame)) + rv = GET_CALLBACK(uvd, getFrame)(uvd, frmx); + else + err("getFrame is not set"); + if ((rv != 0) || (frame->frameState != FrameState_Done_Hold)) { + count = rv; + goto read_done; + } + } + + /* + * Copy bytes to user space. We allow for partial reads, which + * means that the user application can request read less than + * the full frame size. It is up to the application to issue + * subsequent calls until entire frame is read. + * + * First things first, make sure we don't copy more than we + * have - even if the application wants more. That would be + * a big security embarassment! + */ + if ((count + frame->seqRead_Index) > frame->seqRead_Length) + count = frame->seqRead_Length - frame->seqRead_Index; + + /* + * Copy requested amount of data to user space. We start + * copying from the position where we last left it, which + * will be zero for a new frame (not read before). + */ + if (copy_to_user(buf, frame->data + frame->seqRead_Index, count)) { + count = -EFAULT; + goto read_done; + } + + /* Update last read position */ + frame->seqRead_Index += count; + if (uvd->debug >= 1) { + err("%s: {copy} count used=%Zd, new seqRead_Index=%ld", + __func__, count, frame->seqRead_Index); + } + + /* Finally check if the frame is done with and "release" it */ + if (frame->seqRead_Index >= frame->seqRead_Length) { + /* All data has been read */ + frame->seqRead_Index = 0; + + /* Mark it as available to be used again. */ + uvd->frame[frmx].frameState = FrameState_Unused; + if (usbvideo_NewFrame(uvd, (frmx + 1) % USBVIDEO_NUMFRAMES)) { + err("%s: usbvideo_NewFrame failed.", __func__); + } + } +read_done: + mutex_unlock(&uvd->lock); + return count; +} + +/* + * Make all of the blocks of data contiguous + */ +static int usbvideo_CompressIsochronous(struct uvd *uvd, struct urb *urb) +{ + char *cdata; + int i, totlen = 0; + + for (i = 0; i < urb->number_of_packets; i++) { + int n = urb->iso_frame_desc[i].actual_length; + int st = urb->iso_frame_desc[i].status; + + cdata = urb->transfer_buffer + urb->iso_frame_desc[i].offset; + + /* Detect and ignore errored packets */ + if (st < 0) { + if (uvd->debug >= 1) + err("Data error: packet=%d. len=%d. status=%d.", i, n, st); + uvd->stats.iso_err_count++; + continue; + } + + /* Detect and ignore empty packets */ + if (n <= 0) { + uvd->stats.iso_skip_count++; + continue; + } + totlen += n; /* Little local accounting */ + RingQueue_Enqueue(&uvd->dp, cdata, n); + } + return totlen; +} + +static void usbvideo_IsocIrq(struct urb *urb) +{ + int i, ret, len; + struct uvd *uvd = urb->context; + + /* We don't want to do anything if we are about to be removed! */ + if (!CAMERA_IS_OPERATIONAL(uvd)) + return; +#if 0 + if (urb->actual_length > 0) { + dev_info(&uvd->dev->dev, + "urb=$%p status=%d. errcount=%d. length=%d.\n", + urb, urb->status, urb->error_count, + urb->actual_length); + } else { + static int c = 0; + if (c++ % 100 == 0) + dev_info(&uvd->dev->dev, "No Isoc data\n"); + } +#endif + + if (!uvd->streaming) { + if (uvd->debug >= 1) + dev_info(&uvd->dev->dev, + "Not streaming, but interrupt!\n"); + return; + } + + uvd->stats.urb_count++; + if (urb->actual_length <= 0) + goto urb_done_with; + + /* Copy the data received into ring queue */ + len = usbvideo_CompressIsochronous(uvd, urb); + uvd->stats.urb_length = len; + if (len <= 0) + goto urb_done_with; + + /* Here we got some data */ + uvd->stats.data_count += len; + RingQueue_WakeUpInterruptible(&uvd->dp); + +urb_done_with: + for (i = 0; i < FRAMES_PER_DESC; i++) { + urb->iso_frame_desc[i].status = 0; + urb->iso_frame_desc[i].actual_length = 0; + } + urb->status = 0; + urb->dev = uvd->dev; + ret = usb_submit_urb (urb, GFP_KERNEL); + if(ret) + err("usb_submit_urb error (%d)", ret); + return; +} + +/* + * usbvideo_StartDataPump() + * + * History: + * 27-Jan-2000 Used ibmcam->iface, ibmcam->ifaceAltActive instead + * of hardcoded values. Simplified by using for loop, + * allowed any number of URBs. + */ +static int usbvideo_StartDataPump(struct uvd *uvd) +{ + struct usb_device *dev = uvd->dev; + int i, errFlag; + + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); + + if (!CAMERA_IS_OPERATIONAL(uvd)) { + err("%s: Camera is not operational", __func__); + return -EFAULT; + } + uvd->curframe = -1; + + /* Alternate interface 1 is is the biggest frame size */ + i = usb_set_interface(dev, uvd->iface, uvd->ifaceAltActive); + if (i < 0) { + err("%s: usb_set_interface error", __func__); + uvd->last_error = i; + return -EBUSY; + } + if (VALID_CALLBACK(uvd, videoStart)) + GET_CALLBACK(uvd, videoStart)(uvd); + else + err("%s: videoStart not set", __func__); + + /* We double buffer the Iso lists */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + int j, k; + struct urb *urb = uvd->sbuf[i].urb; + urb->dev = dev; + urb->context = uvd; + urb->pipe = usb_rcvisocpipe(dev, uvd->video_endp); + urb->interval = 1; + urb->transfer_flags = URB_ISO_ASAP; + urb->transfer_buffer = uvd->sbuf[i].data; + urb->complete = usbvideo_IsocIrq; + urb->number_of_packets = FRAMES_PER_DESC; + urb->transfer_buffer_length = uvd->iso_packet_len * FRAMES_PER_DESC; + for (j=k=0; j < FRAMES_PER_DESC; j++, k += uvd->iso_packet_len) { + urb->iso_frame_desc[j].offset = k; + urb->iso_frame_desc[j].length = uvd->iso_packet_len; + } + } + + /* Submit all URBs */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + errFlag = usb_submit_urb(uvd->sbuf[i].urb, GFP_KERNEL); + if (errFlag) + err("%s: usb_submit_isoc(%d) ret %d", __func__, i, errFlag); + } + + uvd->streaming = 1; + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, + "%s: streaming=1 video_endp=$%02x\n", __func__, + uvd->video_endp); + return 0; +} + +/* + * usbvideo_StopDataPump() + * + * This procedure stops streaming and deallocates URBs. Then it + * activates zero-bandwidth alt. setting of the video interface. + * + * History: + * 22-Jan-2000 Corrected order of actions to work after surprise removal. + * 27-Jan-2000 Used uvd->iface, uvd->ifaceAltInactive instead of hardcoded values. + */ +static void usbvideo_StopDataPump(struct uvd *uvd) +{ + int i, j; + + if ((uvd == NULL) || (!uvd->streaming) || (uvd->dev == NULL)) + return; + + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, "%s($%p)\n", __func__, uvd); + + /* Unschedule all of the iso td's */ + for (i=0; i < USBVIDEO_NUMSBUF; i++) { + usb_kill_urb(uvd->sbuf[i].urb); + } + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, "%s: streaming=0\n", __func__); + uvd->streaming = 0; + + if (!uvd->remove_pending) { + /* Invoke minidriver's magic to stop the camera */ + if (VALID_CALLBACK(uvd, videoStop)) + GET_CALLBACK(uvd, videoStop)(uvd); + else + err("%s: videoStop not set", __func__); + + /* Set packet size to 0 */ + j = usb_set_interface(uvd->dev, uvd->iface, uvd->ifaceAltInactive); + if (j < 0) { + err("%s: usb_set_interface() error %d.", __func__, j); + uvd->last_error = j; + } + } +} + +/* + * usbvideo_NewFrame() + * + * History: + * 29-Mar-00 Added copying of previous frame into the current one. + * 6-Aug-00 Added model 3 video sizes, removed redundant width, height. + */ +static int usbvideo_NewFrame(struct uvd *uvd, int framenum) +{ + struct usbvideo_frame *frame; + int n; + + if (uvd->debug > 1) + dev_info(&uvd->dev->dev, "usbvideo_NewFrame($%p,%d.)\n", uvd, + framenum); + + /* If we're not grabbing a frame right now and the other frame is */ + /* ready to be grabbed into, then use it instead */ + if (uvd->curframe != -1) + return 0; + + /* If necessary we adjust picture settings between frames */ + if (!uvd->settingsAdjusted) { + if (VALID_CALLBACK(uvd, adjustPicture)) + GET_CALLBACK(uvd, adjustPicture)(uvd); + uvd->settingsAdjusted = 1; + } + + n = (framenum + 1) % USBVIDEO_NUMFRAMES; + if (uvd->frame[n].frameState == FrameState_Ready) + framenum = n; + + frame = &uvd->frame[framenum]; + + frame->frameState = FrameState_Grabbing; + frame->scanstate = ScanState_Scanning; + frame->seqRead_Length = 0; /* Accumulated in xxx_parse_data() */ + frame->deinterlace = Deinterlace_None; + frame->flags = 0; /* No flags yet, up to minidriver (or us) to set them */ + uvd->curframe = framenum; + + /* + * Normally we would want to copy previous frame into the current one + * before we even start filling it with data; this allows us to stop + * filling at any moment; top portion of the frame will be new and + * bottom portion will stay as it was in previous frame. If we don't + * do that then missing chunks of video stream will result in flickering + * portions of old data whatever it was before. + * + * If we choose not to copy previous frame (to, for example, save few + * bus cycles - the frame can be pretty large!) then we have an option + * to clear the frame before using. If we experience losses in this + * mode then missing picture will be black (no flickering). + * + * Finally, if user chooses not to clean the current frame before + * filling it with data then the old data will be visible if we fail + * to refill entire frame with new data. + */ + if (!(uvd->flags & FLAGS_SEPARATE_FRAMES)) { + /* This copies previous frame into this one to mask losses */ + int prev = (framenum - 1 + USBVIDEO_NUMFRAMES) % USBVIDEO_NUMFRAMES; + memmove(frame->data, uvd->frame[prev].data, uvd->max_frame_size); + } else { + if (uvd->flags & FLAGS_CLEAN_FRAMES) { + /* This provides a "clean" frame but slows things down */ + memset(frame->data, 0, uvd->max_frame_size); + } + } + return 0; +} + +/* + * usbvideo_CollectRawData() + * + * This procedure can be used instead of 'processData' callback if you + * only want to dump the raw data from the camera into the output + * device (frame buffer). You can look at it with V4L client, but the + * image will be unwatchable. The main purpose of this code and of the + * mode FLAGS_NO_DECODING is debugging and capturing of datastreams from + * new, unknown cameras. This procedure will be automatically invoked + * instead of the specified callback handler when uvd->flags has bit + * FLAGS_NO_DECODING set. Therefore, any regular build of any driver + * based on usbvideo can use this feature at any time. + */ +static void usbvideo_CollectRawData(struct uvd *uvd, struct usbvideo_frame *frame) +{ + int n; + + assert(uvd != NULL); + assert(frame != NULL); + + /* Try to move data from queue into frame buffer */ + n = RingQueue_GetLength(&uvd->dp); + if (n > 0) { + int m; + /* See how much space we have left */ + m = uvd->max_frame_size - frame->seqRead_Length; + if (n > m) + n = m; + /* Now move that much data into frame buffer */ + RingQueue_Dequeue( + &uvd->dp, + frame->data + frame->seqRead_Length, + m); + frame->seqRead_Length += m; + } + /* See if we filled the frame */ + if (frame->seqRead_Length >= uvd->max_frame_size) { + frame->frameState = FrameState_Done; + uvd->curframe = -1; + uvd->stats.frame_num++; + } +} + +static int usbvideo_GetFrame(struct uvd *uvd, int frameNum) +{ + struct usbvideo_frame *frame = &uvd->frame[frameNum]; + + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, "%s($%p,%d.)\n", __func__, uvd, + frameNum); + + switch (frame->frameState) { + case FrameState_Unused: + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, "%s: FrameState_Unused\n", + __func__); + return -EINVAL; + case FrameState_Ready: + case FrameState_Grabbing: + case FrameState_Error: + { + int ntries, signalPending; + redo: + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, + "%s: Camera is not operational (1)\n", + __func__); + return -EIO; + } + ntries = 0; + do { + RingQueue_InterruptibleSleepOn(&uvd->dp); + signalPending = signal_pending(current); + if (!CAMERA_IS_OPERATIONAL(uvd)) { + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, + "%s: Camera is not " + "operational (2)\n", __func__); + return -EIO; + } + assert(uvd->fbuf != NULL); + if (signalPending) { + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, + "%s: Signal=$%08x\n", __func__, + signalPending); + if (uvd->flags & FLAGS_RETRY_VIDIOCSYNC) { + usbvideo_TestPattern(uvd, 1, 0); + uvd->curframe = -1; + uvd->stats.frame_num++; + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, + "%s: Forced test " + "pattern screen\n", + __func__); + return 0; + } else { + /* Standard answer: Interrupted! */ + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, + "%s: Interrupted!\n", + __func__); + return -EINTR; + } + } else { + /* No signals - we just got new data in dp queue */ + if (uvd->flags & FLAGS_NO_DECODING) + usbvideo_CollectRawData(uvd, frame); + else if (VALID_CALLBACK(uvd, processData)) + GET_CALLBACK(uvd, processData)(uvd, frame); + else + err("%s: processData not set", __func__); + } + } while (frame->frameState == FrameState_Grabbing); + if (uvd->debug >= 2) { + dev_info(&uvd->dev->dev, + "%s: Grabbing done; state=%d. (%lu. bytes)\n", + __func__, frame->frameState, + frame->seqRead_Length); + } + if (frame->frameState == FrameState_Error) { + int ret = usbvideo_NewFrame(uvd, frameNum); + if (ret < 0) { + err("%s: usbvideo_NewFrame() failed (%d.)", __func__, ret); + return ret; + } + goto redo; + } + /* Note that we fall through to meet our destiny below */ + } + case FrameState_Done: + /* + * Do all necessary postprocessing of data prepared in + * "interrupt" code and the collecting code above. The + * frame gets marked as FrameState_Done by queue parsing code. + * This status means that we collected enough data and + * most likely processed it as we went through. However + * the data may need postprocessing, such as deinterlacing + * or picture adjustments implemented in software (horror!) + * + * As soon as the frame becomes "final" it gets promoted to + * FrameState_Done_Hold status where it will remain until the + * caller consumed all the video data from the frame. Then + * the empty shell of ex-frame is thrown out for dogs to eat. + * But we, worried about pets, will recycle the frame! + */ + uvd->stats.frame_num++; + if ((uvd->flags & FLAGS_NO_DECODING) == 0) { + if (VALID_CALLBACK(uvd, postProcess)) + GET_CALLBACK(uvd, postProcess)(uvd, frame); + if (frame->flags & USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST) + usbvideo_SoftwareContrastAdjustment(uvd, frame); + } + frame->frameState = FrameState_Done_Hold; + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, + "%s: Entered FrameState_Done_Hold state.\n", + __func__); + return 0; + + case FrameState_Done_Hold: + /* + * We stay in this state indefinitely until someone external, + * like ioctl() or read() call finishes digesting the frame + * data. Then it will mark the frame as FrameState_Unused and + * it will be released back into the wild to roam freely. + */ + if (uvd->debug >= 2) + dev_info(&uvd->dev->dev, + "%s: FrameState_Done_Hold state.\n", + __func__); + return 0; + } + + /* Catch-all for other cases. We shall not be here. */ + err("%s: Invalid state %d.", __func__, frame->frameState); + frame->frameState = FrameState_Unused; + return 0; +} + +/* + * usbvideo_DeinterlaceFrame() + * + * This procedure deinterlaces the given frame. Some cameras produce + * only half of scanlines - sometimes only even lines, sometimes only + * odd lines. The deinterlacing method is stored in frame->deinterlace + * variable. + * + * Here we scan the frame vertically and replace missing scanlines with + * average between surrounding ones - before and after. If we have no + * line above then we just copy next line. Similarly, if we need to + * create a last line then preceding line is used. + */ +void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame) +{ + if ((uvd == NULL) || (frame == NULL)) + return; + + if ((frame->deinterlace == Deinterlace_FillEvenLines) || + (frame->deinterlace == Deinterlace_FillOddLines)) + { + const int v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + int i = (frame->deinterlace == Deinterlace_FillEvenLines) ? 0 : 1; + + for (; i < VIDEOSIZE_Y(frame->request); i += 2) { + const unsigned char *fs1, *fs2; + unsigned char *fd; + int ip, in, j; /* Previous and next lines */ + + /* + * Need to average lines before and after 'i'. + * If we go out of bounds seeking those lines then + * we point back to existing line. + */ + ip = i - 1; /* First, get rough numbers */ + in = i + 1; + + /* Now validate */ + if (ip < 0) + ip = in; + if (in >= VIDEOSIZE_Y(frame->request)) + in = ip; + + /* Sanity check */ + if ((ip < 0) || (in < 0) || + (ip >= VIDEOSIZE_Y(frame->request)) || + (in >= VIDEOSIZE_Y(frame->request))) + { + err("Error: ip=%d. in=%d. req.height=%ld.", + ip, in, VIDEOSIZE_Y(frame->request)); + break; + } + + /* Now we need to average lines 'ip' and 'in' to produce line 'i' */ + fs1 = frame->data + (v4l_linesize * ip); + fs2 = frame->data + (v4l_linesize * in); + fd = frame->data + (v4l_linesize * i); + + /* Average lines around destination */ + for (j=0; j < v4l_linesize; j++) { + fd[j] = (unsigned char)((((unsigned) fs1[j]) + + ((unsigned)fs2[j])) >> 1); + } + } + } + + /* Optionally display statistics on the screen */ + if (uvd->flags & FLAGS_OVERLAY_STATS) + usbvideo_OverlayStats(uvd, frame); +} + +EXPORT_SYMBOL(usbvideo_DeinterlaceFrame); + +/* + * usbvideo_SoftwareContrastAdjustment() + * + * This code adjusts the contrast of the frame, assuming RGB24 format. + * As most software image processing, this job is CPU-intensive. + * Get a camera that supports hardware adjustment! + * + * History: + * 09-Feb-2001 Created. + */ +static void usbvideo_SoftwareContrastAdjustment(struct uvd *uvd, + struct usbvideo_frame *frame) +{ + int i, j, v4l_linesize; + signed long adj; + const int ccm = 128; /* Color correction median - see below */ + + if ((uvd == NULL) || (frame == NULL)) { + err("%s: Illegal call.", __func__); + return; + } + adj = (uvd->vpic.contrast - 0x8000) >> 8; /* -128..+127 = -ccm..+(ccm-1)*/ + RESTRICT_TO_RANGE(adj, -ccm, ccm+1); + if (adj == 0) { + /* In rare case of no adjustment */ + return; + } + v4l_linesize = VIDEOSIZE_X(frame->request) * V4L_BYTES_PER_PIXEL; + for (i=0; i < VIDEOSIZE_Y(frame->request); i++) { + unsigned char *fd = frame->data + (v4l_linesize * i); + for (j=0; j < v4l_linesize; j++) { + signed long v = (signed long) fd[j]; + /* Magnify up to 2 times, reduce down to zero */ + v = 128 + ((ccm + adj) * (v - 128)) / ccm; + RESTRICT_TO_RANGE(v, 0, 0xFF); /* Must flatten tails */ + fd[j] = (unsigned char) v; + } + } +} + +MODULE_LICENSE("GPL"); diff --git a/drivers/staging/usbvideo/usbvideo.h b/drivers/staging/usbvideo/usbvideo.h new file mode 100644 index 000000000000..c66985beb8c9 --- /dev/null +++ b/drivers/staging/usbvideo/usbvideo.h @@ -0,0 +1,395 @@ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#ifndef usbvideo_h +#define usbvideo_h + +#include +#include +#include +#include +#include + +/* Most helpful debugging aid */ +#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__)))) + +#define USBVIDEO_REPORT_STATS 1 /* Set to 0 to block statistics on close */ + +/* Bit flags (options) */ +#define FLAGS_RETRY_VIDIOCSYNC (1 << 0) +#define FLAGS_MONOCHROME (1 << 1) +#define FLAGS_DISPLAY_HINTS (1 << 2) +#define FLAGS_OVERLAY_STATS (1 << 3) +#define FLAGS_FORCE_TESTPATTERN (1 << 4) +#define FLAGS_SEPARATE_FRAMES (1 << 5) +#define FLAGS_CLEAN_FRAMES (1 << 6) +#define FLAGS_NO_DECODING (1 << 7) + +/* Bit flags for frames (apply to the frame where they are specified) */ +#define USBVIDEO_FRAME_FLAG_SOFTWARE_CONTRAST (1 << 0) + +/* Camera capabilities (maximum) */ +#define CAMERA_URB_FRAMES 32 +#define CAMERA_MAX_ISO_PACKET 1023 /* 1022 actually sent by camera */ +#define FRAMES_PER_DESC (CAMERA_URB_FRAMES) +#define FRAME_SIZE_PER_DESC (CAMERA_MAX_ISO_PACKET) + +/* This macro restricts an int variable to an inclusive range */ +#define RESTRICT_TO_RANGE(v,mi,ma) { if ((v) < (mi)) (v) = (mi); else if ((v) > (ma)) (v) = (ma); } + +#define V4L_BYTES_PER_PIXEL 3 /* Because we produce RGB24 */ + +/* + * Use this macro to construct constants for different video sizes. + * We have to deal with different video sizes that have to be + * configured in the device or compared against when we receive + * a data. Normally one would define a bunch of VIDEOSIZE_x_by_y + * #defines and that's the end of story. However this solution + * does not allow to convert between real pixel sizes and the + * constant (integer) value that may be used to tag a frame or + * whatever. The set of macros below constructs videosize constants + * from the pixel size and allows to reconstruct the pixel size + * from the combined value later. + */ +#define VIDEOSIZE(x,y) (((x) & 0xFFFFL) | (((y) & 0xFFFFL) << 16)) +#define VIDEOSIZE_X(vs) ((vs) & 0xFFFFL) +#define VIDEOSIZE_Y(vs) (((vs) >> 16) & 0xFFFFL) +typedef unsigned long videosize_t; + +/* + * This macro checks if the camera is still operational. The 'uvd' + * pointer must be valid, uvd->dev must be valid, we are not + * removing the device and the device has not erred on us. + */ +#define CAMERA_IS_OPERATIONAL(uvd) (\ + (uvd != NULL) && \ + ((uvd)->dev != NULL) && \ + ((uvd)->last_error == 0) && \ + (!(uvd)->remove_pending)) + +/* + * We use macros to do YUV -> RGB conversion because this is + * very important for speed and totally unimportant for size. + * + * YUV -> RGB Conversion + * --------------------- + * + * B = 1.164*(Y-16) + 2.018*(V-128) + * G = 1.164*(Y-16) - 0.813*(U-128) - 0.391*(V-128) + * R = 1.164*(Y-16) + 1.596*(U-128) + * + * If you fancy integer arithmetics (as you should), hear this: + * + * 65536*B = 76284*(Y-16) + 132252*(V-128) + * 65536*G = 76284*(Y-16) - 53281*(U-128) - 25625*(V-128) + * 65536*R = 76284*(Y-16) + 104595*(U-128) + * + * Make sure the output values are within [0..255] range. + */ +#define LIMIT_RGB(x) (((x) < 0) ? 0 : (((x) > 255) ? 255 : (x))) +#define YUV_TO_RGB_BY_THE_BOOK(my,mu,mv,mr,mg,mb) { \ + int mm_y, mm_yc, mm_u, mm_v, mm_r, mm_g, mm_b; \ + mm_y = (my) - 16; \ + mm_u = (mu) - 128; \ + mm_v = (mv) - 128; \ + mm_yc= mm_y * 76284; \ + mm_b = (mm_yc + 132252*mm_v ) >> 16; \ + mm_g = (mm_yc - 53281*mm_u - 25625*mm_v ) >> 16; \ + mm_r = (mm_yc + 104595*mm_u ) >> 16; \ + mb = LIMIT_RGB(mm_b); \ + mg = LIMIT_RGB(mm_g); \ + mr = LIMIT_RGB(mm_r); \ +} + +#define RING_QUEUE_SIZE (128*1024) /* Must be a power of 2 */ +#define RING_QUEUE_ADVANCE_INDEX(rq,ind,n) (rq)->ind = ((rq)->ind + (n)) & ((rq)->length-1) +#define RING_QUEUE_DEQUEUE_BYTES(rq,n) RING_QUEUE_ADVANCE_INDEX(rq,ri,n) +#define RING_QUEUE_PEEK(rq,ofs) ((rq)->queue[((ofs) + (rq)->ri) & ((rq)->length-1)]) + +struct RingQueue { + unsigned char *queue; /* Data from the Isoc data pump */ + int length; /* How many bytes allocated for the queue */ + int wi; /* That's where we write */ + int ri; /* Read from here until you hit write index */ + wait_queue_head_t wqh; /* Processes waiting */ +}; + +enum ScanState { + ScanState_Scanning, /* Scanning for header */ + ScanState_Lines /* Parsing lines */ +}; + +/* Completion states of the data parser */ +enum ParseState { + scan_Continue, /* Just parse next item */ + scan_NextFrame, /* Frame done, send it to V4L */ + scan_Out, /* Not enough data for frame */ + scan_EndParse /* End parsing */ +}; + +enum FrameState { + FrameState_Unused, /* Unused (no MCAPTURE) */ + FrameState_Ready, /* Ready to start grabbing */ + FrameState_Grabbing, /* In the process of being grabbed into */ + FrameState_Done, /* Finished grabbing, but not been synced yet */ + FrameState_Done_Hold, /* Are syncing or reading */ + FrameState_Error, /* Something bad happened while processing */ +}; + +/* + * Some frames may contain only even or odd lines. This type + * specifies what type of deinterlacing is required. + */ +enum Deinterlace { + Deinterlace_None=0, + Deinterlace_FillOddLines, + Deinterlace_FillEvenLines +}; + +#define USBVIDEO_NUMFRAMES 2 /* How many frames we work with */ +#define USBVIDEO_NUMSBUF 2 /* How many URBs linked in a ring */ + +/* This structure represents one Isoc request - URB and buffer */ +struct usbvideo_sbuf { + char *data; + struct urb *urb; +}; + +struct usbvideo_frame { + char *data; /* Frame buffer */ + unsigned long header; /* Significant bits from the header */ + + videosize_t canvas; /* The canvas (max. image) allocated */ + videosize_t request; /* That's what the application asked for */ + unsigned short palette; /* The desired format */ + + enum FrameState frameState;/* State of grabbing */ + enum ScanState scanstate; /* State of scanning */ + enum Deinterlace deinterlace; + int flags; /* USBVIDEO_FRAME_FLAG_xxx bit flags */ + + int curline; /* Line of frame we're working on */ + + long seqRead_Length; /* Raw data length of frame */ + long seqRead_Index; /* Amount of data that has been already read */ + + void *user; /* Additional data that user may need */ +}; + +/* Statistics that can be overlaid on screen */ +struct usbvideo_statistics { + unsigned long frame_num; /* Sequential number of the frame */ + unsigned long urb_count; /* How many URBs we received so far */ + unsigned long urb_length; /* Length of last URB */ + unsigned long data_count; /* How many bytes we received */ + unsigned long header_count; /* How many frame headers we found */ + unsigned long iso_skip_count; /* How many empty ISO packets received */ + unsigned long iso_err_count; /* How many bad ISO packets received */ +}; + +struct usbvideo; + +struct uvd { + struct video_device vdev; /* Must be the first field! */ + struct usb_device *dev; + struct usbvideo *handle; /* Points back to the struct usbvideo */ + void *user_data; /* Camera-dependent data */ + int user_size; /* Size of that camera-dependent data */ + int debug; /* Debug level for usbvideo */ + unsigned char iface; /* Video interface number */ + unsigned char video_endp; + unsigned char ifaceAltActive; + unsigned char ifaceAltInactive; /* Alt settings */ + unsigned long flags; /* FLAGS_USBVIDEO_xxx */ + unsigned long paletteBits; /* Which palettes we accept? */ + unsigned short defaultPalette; /* What palette to use for read() */ + struct mutex lock; + int user; /* user count for exclusive use */ + + videosize_t videosize; /* Current setting */ + videosize_t canvas; /* This is the width,height of the V4L canvas */ + int max_frame_size; /* Bytes in one video frame */ + + int uvd_used; /* Is this structure in use? */ + int streaming; /* Are we streaming Isochronous? */ + int grabbing; /* Are we grabbing? */ + int settingsAdjusted; /* Have we adjusted contrast etc.? */ + int last_error; /* What calamity struck us? */ + + char *fbuf; /* Videodev buffer area */ + int fbuf_size; /* Videodev buffer size */ + + int curframe; + int iso_packet_len; /* Videomode-dependent, saves bus bandwidth */ + + struct RingQueue dp; /* Isoc data pump */ + struct usbvideo_frame frame[USBVIDEO_NUMFRAMES]; + struct usbvideo_sbuf sbuf[USBVIDEO_NUMSBUF]; + + volatile int remove_pending; /* If set then about to exit */ + + struct video_picture vpic, vpic_old; /* Picture settings */ + struct video_capability vcap; /* Video capabilities */ + struct video_channel vchan; /* May be used for tuner support */ + struct usbvideo_statistics stats; + char videoName[32]; /* Holds name like "video7" */ +}; + +/* + * usbvideo callbacks (virtual methods). They are set when usbvideo + * services are registered. All of these default to NULL, except those + * that default to usbvideo-provided methods. + */ +struct usbvideo_cb { + int (*probe)(struct usb_interface *, const struct usb_device_id *); + void (*userFree)(struct uvd *); + void (*disconnect)(struct usb_interface *); + int (*setupOnOpen)(struct uvd *); + void (*videoStart)(struct uvd *); + void (*videoStop)(struct uvd *); + void (*processData)(struct uvd *, struct usbvideo_frame *); + void (*postProcess)(struct uvd *, struct usbvideo_frame *); + void (*adjustPicture)(struct uvd *); + int (*getFPS)(struct uvd *); + int (*overlayHook)(struct uvd *, struct usbvideo_frame *); + int (*getFrame)(struct uvd *, int); + int (*startDataPump)(struct uvd *uvd); + void (*stopDataPump)(struct uvd *uvd); + int (*setVideoMode)(struct uvd *uvd, struct video_window *vw); +}; + +struct usbvideo { + int num_cameras; /* As allocated */ + struct usb_driver usbdrv; /* Interface to the USB stack */ + char drvName[80]; /* Driver name */ + struct mutex lock; /* Mutex protecting camera structures */ + struct usbvideo_cb cb; /* Table of callbacks (virtual methods) */ + struct video_device vdt; /* Video device template */ + struct uvd *cam; /* Array of camera structures */ + struct module *md_module; /* Minidriver module */ +}; + + +/* + * This macro retrieves callback address from the struct uvd object. + * No validity checks are done here, so be sure to check the + * callback beforehand with VALID_CALLBACK. + */ +#define GET_CALLBACK(uvd,cbName) ((uvd)->handle->cb.cbName) + +/* + * This macro returns either callback pointer or NULL. This is safe + * macro, meaning that most of components of data structures involved + * may be NULL - this only results in NULL being returned. You may + * wish to use this macro to make sure that the callback is callable. + * However keep in mind that those checks take time. + */ +#define VALID_CALLBACK(uvd,cbName) ((((uvd) != NULL) && \ + ((uvd)->handle != NULL)) ? GET_CALLBACK(uvd,cbName) : NULL) + +int RingQueue_Dequeue(struct RingQueue *rq, unsigned char *dst, int len); +int RingQueue_Enqueue(struct RingQueue *rq, const unsigned char *cdata, int n); +void RingQueue_WakeUpInterruptible(struct RingQueue *rq); +void RingQueue_Flush(struct RingQueue *rq); + +static inline int RingQueue_GetLength(const struct RingQueue *rq) +{ + return (rq->wi - rq->ri + rq->length) & (rq->length-1); +} + +static inline int RingQueue_GetFreeSpace(const struct RingQueue *rq) +{ + return rq->length - RingQueue_GetLength(rq); +} + +void usbvideo_DrawLine( + struct usbvideo_frame *frame, + int x1, int y1, + int x2, int y2, + unsigned char cr, unsigned char cg, unsigned char cb); +void usbvideo_HexDump(const unsigned char *data, int len); +void usbvideo_SayAndWait(const char *what); +void usbvideo_TestPattern(struct uvd *uvd, int fullframe, int pmode); + +/* Memory allocation routines */ +unsigned long usbvideo_kvirt_to_pa(unsigned long adr); + +int usbvideo_register( + struct usbvideo **pCams, + const int num_cams, + const int num_extra, + const char *driverName, + const struct usbvideo_cb *cbTable, + struct module *md, + const struct usb_device_id *id_table); +struct uvd *usbvideo_AllocateDevice(struct usbvideo *cams); +int usbvideo_RegisterVideoDevice(struct uvd *uvd); +void usbvideo_Deregister(struct usbvideo **uvt); + +int usbvideo_v4l_initialize(struct video_device *dev); + +void usbvideo_DeinterlaceFrame(struct uvd *uvd, struct usbvideo_frame *frame); + +/* + * This code performs bounds checking - use it when working with + * new formats, or else you may get oopses all over the place. + * If pixel falls out of bounds then it gets shoved back (as close + * to place of offence as possible) and is painted bright red. + * + * There are two important concepts: frame width, height and + * V4L canvas width, height. The former is the area requested by + * the application -for this very frame-. The latter is the largest + * possible frame that we can serve (we advertise that via V4L ioctl). + * The frame data is expected to be formatted as lines of length + * VIDEOSIZE_X(fr->request), total VIDEOSIZE_Y(frame->request) lines. + */ +static inline void RGB24_PUTPIXEL( + struct usbvideo_frame *fr, + int ix, int iy, + unsigned char vr, + unsigned char vg, + unsigned char vb) +{ + register unsigned char *pf; + int limiter = 0, mx, my; + mx = ix; + my = iy; + if (mx < 0) { + mx=0; + limiter++; + } else if (mx >= VIDEOSIZE_X((fr)->request)) { + mx= VIDEOSIZE_X((fr)->request) - 1; + limiter++; + } + if (my < 0) { + my = 0; + limiter++; + } else if (my >= VIDEOSIZE_Y((fr)->request)) { + my = VIDEOSIZE_Y((fr)->request) - 1; + limiter++; + } + pf = (fr)->data + V4L_BYTES_PER_PIXEL*((iy)*VIDEOSIZE_X((fr)->request) + (ix)); + if (limiter) { + *pf++ = 0; + *pf++ = 0; + *pf++ = 0xFF; + } else { + *pf++ = (vb); + *pf++ = (vg); + *pf++ = (vr); + } +} + +#endif /* usbvideo_h */ diff --git a/drivers/staging/usbvideo/vicam.c b/drivers/staging/usbvideo/vicam.c new file mode 100644 index 000000000000..dc17cce2fbb6 --- /dev/null +++ b/drivers/staging/usbvideo/vicam.c @@ -0,0 +1,952 @@ +/* + * USB ViCam WebCam driver + * Copyright (c) 2002 Joe Burks (jburks@wavicle.org), + * Christopher L Cheney (ccheney@cheney.cx), + * Pavel Machek (pavel@ucw.cz), + * John Tyner (jtyner@cs.ucr.edu), + * Monroe Williams (monroe@pobox.com) + * + * Supports 3COM HomeConnect PC Digital WebCam + * Supports Compro PS39U WebCam + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * This source code is based heavily on the CPiA webcam driver which was + * written by Peter Pregler, Scott J. Bertin and Johannes Erdfelt + * + * Portions of this code were also copied from usbvideo.c + * + * Special thanks to the whole team at Sourceforge for help making + * this driver become a reality. Notably: + * Andy Armstrong who reverse engineered the color encoding and + * Pavel Machek and Chris Cheney who worked on reverse engineering the + * camera controls and wrote the first generation driver. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "usbvideo.h" + +// #define VICAM_DEBUG + +#ifdef VICAM_DEBUG +#define ADBG(lineno,fmt,args...) printk(fmt, jiffies, __func__, lineno, ##args) +#define DBG(fmt,args...) ADBG((__LINE__),KERN_DEBUG __FILE__"(%ld):%s (%d):"fmt,##args) +#else +#define DBG(fmn,args...) do {} while(0) +#endif + +#define DRIVER_AUTHOR "Joe Burks, jburks@wavicle.org" +#define DRIVER_DESC "ViCam WebCam Driver" + +/* Define these values to match your device */ +#define USB_VICAM_VENDOR_ID 0x04c1 +#define USB_VICAM_PRODUCT_ID 0x009d +#define USB_COMPRO_VENDOR_ID 0x0602 +#define USB_COMPRO_PRODUCT_ID 0x1001 + +#define VICAM_BYTES_PER_PIXEL 3 +#define VICAM_MAX_READ_SIZE (512*242+128) +#define VICAM_MAX_FRAME_SIZE (VICAM_BYTES_PER_PIXEL*320*240) +#define VICAM_FRAMES 2 + +#define VICAM_HEADER_SIZE 64 + +/* rvmalloc / rvfree copied from usbvideo.c + * + * Not sure why these are not yet non-statics which I can reference through + * usbvideo.h the same as it is in 2.4.20. I bet this will get fixed sometime + * in the future. + * +*/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + +struct vicam_camera { + u16 shutter_speed; // capture shutter speed + u16 gain; // capture gain + + u8 *raw_image; // raw data captured from the camera + u8 *framebuf; // processed data in RGB24 format + u8 *cntrlbuf; // area used to send control msgs + + struct video_device vdev; // v4l video device + struct usb_device *udev; // usb device + + /* guard against simultaneous accesses to the camera */ + struct mutex cam_lock; + + int is_initialized; + u8 open_count; + u8 bulkEndpoint; + int needsDummyRead; +}; + +static int vicam_probe( struct usb_interface *intf, const struct usb_device_id *id); +static void vicam_disconnect(struct usb_interface *intf); +static void read_frame(struct vicam_camera *cam, int framenum); +static void vicam_decode_color(const u8 *, u8 *); + +static int __send_control_msg(struct vicam_camera *cam, + u8 request, + u16 value, + u16 index, + unsigned char *cp, + u16 size) +{ + int status; + + /* cp must be memory that has been allocated by kmalloc */ + + status = usb_control_msg(cam->udev, + usb_sndctrlpipe(cam->udev, 0), + request, + USB_DIR_OUT | USB_TYPE_VENDOR | + USB_RECIP_DEVICE, value, index, + cp, size, 1000); + + status = min(status, 0); + + if (status < 0) { + printk(KERN_INFO "Failed sending control message, error %d.\n", + status); + } + + return status; +} + +static int send_control_msg(struct vicam_camera *cam, + u8 request, + u16 value, + u16 index, + unsigned char *cp, + u16 size) +{ + int status = -ENODEV; + mutex_lock(&cam->cam_lock); + if (cam->udev) { + status = __send_control_msg(cam, request, value, + index, cp, size); + } + mutex_unlock(&cam->cam_lock); + return status; +} +static int +initialize_camera(struct vicam_camera *cam) +{ + int err; + const struct ihex_binrec *rec; + const struct firmware *uninitialized_var(fw); + + err = request_ihex_firmware(&fw, "vicam/firmware.fw", &cam->udev->dev); + if (err) { + printk(KERN_ERR "Failed to load \"vicam/firmware.fw\": %d\n", + err); + return err; + } + + for (rec = (void *)fw->data; rec; rec = ihex_next_binrec(rec)) { + memcpy(cam->cntrlbuf, rec->data, be16_to_cpu(rec->len)); + + err = send_control_msg(cam, 0xff, 0, 0, + cam->cntrlbuf, be16_to_cpu(rec->len)); + if (err) + break; + } + + release_firmware(fw); + + return err; +} + +static int +set_camera_power(struct vicam_camera *cam, int state) +{ + int status; + + if ((status = send_control_msg(cam, 0x50, state, 0, NULL, 0)) < 0) + return status; + + if (state) { + send_control_msg(cam, 0x55, 1, 0, NULL, 0); + } + + return 0; +} + +static long +vicam_ioctl(struct file *file, unsigned int ioctlnr, unsigned long arg) +{ + void __user *user_arg = (void __user *)arg; + struct vicam_camera *cam = file->private_data; + long retval = 0; + + if (!cam) + return -ENODEV; + + switch (ioctlnr) { + /* query capabilities */ + case VIDIOCGCAP: + { + struct video_capability b; + + DBG("VIDIOCGCAP\n"); + memset(&b, 0, sizeof(b)); + strcpy(b.name, "ViCam-based Camera"); + b.type = VID_TYPE_CAPTURE; + b.channels = 1; + b.audios = 0; + b.maxwidth = 320; /* VIDEOSIZE_CIF */ + b.maxheight = 240; + b.minwidth = 320; /* VIDEOSIZE_48_48 */ + b.minheight = 240; + + if (copy_to_user(user_arg, &b, sizeof(b))) + retval = -EFAULT; + + break; + } + /* get/set video source - we are a camera and nothing else */ + case VIDIOCGCHAN: + { + struct video_channel v; + + DBG("VIDIOCGCHAN\n"); + if (copy_from_user(&v, user_arg, sizeof(v))) { + retval = -EFAULT; + break; + } + if (v.channel != 0) { + retval = -EINVAL; + break; + } + + v.channel = 0; + strcpy(v.name, "Camera"); + v.tuners = 0; + v.flags = 0; + v.type = VIDEO_TYPE_CAMERA; + v.norm = 0; + + if (copy_to_user(user_arg, &v, sizeof(v))) + retval = -EFAULT; + break; + } + + case VIDIOCSCHAN: + { + int v; + + if (copy_from_user(&v, user_arg, sizeof(v))) + retval = -EFAULT; + DBG("VIDIOCSCHAN %d\n", v); + + if (retval == 0 && v != 0) + retval = -EINVAL; + + break; + } + + /* image properties */ + case VIDIOCGPICT: + { + struct video_picture vp; + DBG("VIDIOCGPICT\n"); + memset(&vp, 0, sizeof (struct video_picture)); + vp.brightness = cam->gain << 8; + vp.depth = 24; + vp.palette = VIDEO_PALETTE_RGB24; + if (copy_to_user(user_arg, &vp, sizeof (struct video_picture))) + retval = -EFAULT; + break; + } + + case VIDIOCSPICT: + { + struct video_picture vp; + + if (copy_from_user(&vp, user_arg, sizeof(vp))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCSPICT depth = %d, pal = %d\n", vp.depth, + vp.palette); + + cam->gain = vp.brightness >> 8; + + if (vp.depth != 24 + || vp.palette != VIDEO_PALETTE_RGB24) + retval = -EINVAL; + + break; + } + + /* get/set capture window */ + case VIDIOCGWIN: + { + struct video_window vw; + vw.x = 0; + vw.y = 0; + vw.width = 320; + vw.height = 240; + vw.chromakey = 0; + vw.flags = 0; + vw.clips = NULL; + vw.clipcount = 0; + + DBG("VIDIOCGWIN\n"); + + if (copy_to_user(user_arg, (void *)&vw, sizeof(vw))) + retval = -EFAULT; + + // I'm not sure what the deal with a capture window is, it is very poorly described + // in the doc. So I won't support it now. + break; + } + + case VIDIOCSWIN: + { + + struct video_window vw; + + if (copy_from_user(&vw, user_arg, sizeof(vw))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCSWIN %d x %d\n", vw.width, vw.height); + + if ( vw.width != 320 || vw.height != 240 ) + retval = -EFAULT; + + break; + } + + /* mmap interface */ + case VIDIOCGMBUF: + { + struct video_mbuf vm; + int i; + + DBG("VIDIOCGMBUF\n"); + memset(&vm, 0, sizeof (vm)); + vm.size = + VICAM_MAX_FRAME_SIZE * VICAM_FRAMES; + vm.frames = VICAM_FRAMES; + for (i = 0; i < VICAM_FRAMES; i++) + vm.offsets[i] = VICAM_MAX_FRAME_SIZE * i; + + if (copy_to_user(user_arg, (void *)&vm, sizeof(vm))) + retval = -EFAULT; + + break; + } + + case VIDIOCMCAPTURE: + { + struct video_mmap vm; + // int video_size; + + if (copy_from_user((void *)&vm, user_arg, sizeof(vm))) { + retval = -EFAULT; + break; + } + + DBG("VIDIOCMCAPTURE frame=%d, height=%d, width=%d, format=%d.\n",vm.frame,vm.width,vm.height,vm.format); + + if ( vm.frame >= VICAM_FRAMES || vm.format != VIDEO_PALETTE_RGB24 ) + retval = -EINVAL; + + // in theory right here we'd start the image capturing + // (fill in a bulk urb and submit it asynchronously) + // + // Instead we're going to do a total hack job for now and + // retrieve the frame in VIDIOCSYNC + + break; + } + + case VIDIOCSYNC: + { + int frame; + + if (copy_from_user((void *)&frame, user_arg, sizeof(int))) { + retval = -EFAULT; + break; + } + DBG("VIDIOCSYNC: %d\n", frame); + + read_frame(cam, frame); + vicam_decode_color(cam->raw_image, + cam->framebuf + + frame * VICAM_MAX_FRAME_SIZE ); + + break; + } + + /* pointless to implement overlay with this camera */ + case VIDIOCCAPTURE: + case VIDIOCGFBUF: + case VIDIOCSFBUF: + case VIDIOCKEY: + retval = -EINVAL; + break; + + /* tuner interface - we have none */ + case VIDIOCGTUNER: + case VIDIOCSTUNER: + case VIDIOCGFREQ: + case VIDIOCSFREQ: + retval = -EINVAL; + break; + + /* audio interface - we have none */ + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + retval = -EINVAL; + break; + default: + retval = -ENOIOCTLCMD; + break; + } + + return retval; +} + +static int +vicam_open(struct file *file) +{ + struct vicam_camera *cam = video_drvdata(file); + + DBG("open\n"); + + if (!cam) { + printk(KERN_ERR + "vicam video_device improperly initialized"); + return -EINVAL; + } + + /* cam_lock/open_count protects us from simultaneous opens + * ... for now. we probably shouldn't rely on this fact forever. + */ + + mutex_lock(&cam->cam_lock); + if (cam->open_count > 0) { + printk(KERN_INFO + "vicam_open called on already opened camera"); + mutex_unlock(&cam->cam_lock); + return -EBUSY; + } + + cam->raw_image = kmalloc(VICAM_MAX_READ_SIZE, GFP_KERNEL); + if (!cam->raw_image) { + mutex_unlock(&cam->cam_lock); + return -ENOMEM; + } + + cam->framebuf = rvmalloc(VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + if (!cam->framebuf) { + kfree(cam->raw_image); + mutex_unlock(&cam->cam_lock); + return -ENOMEM; + } + + cam->cntrlbuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!cam->cntrlbuf) { + kfree(cam->raw_image); + rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + mutex_unlock(&cam->cam_lock); + return -ENOMEM; + } + + cam->needsDummyRead = 1; + cam->open_count++; + + file->private_data = cam; + mutex_unlock(&cam->cam_lock); + + + // First upload firmware, then turn the camera on + + if (!cam->is_initialized) { + initialize_camera(cam); + + cam->is_initialized = 1; + } + + set_camera_power(cam, 1); + + return 0; +} + +static int +vicam_close(struct file *file) +{ + struct vicam_camera *cam = file->private_data; + int open_count; + struct usb_device *udev; + + DBG("close\n"); + + /* it's not the end of the world if + * we fail to turn the camera off. + */ + + set_camera_power(cam, 0); + + kfree(cam->raw_image); + rvfree(cam->framebuf, VICAM_MAX_FRAME_SIZE * VICAM_FRAMES); + kfree(cam->cntrlbuf); + + mutex_lock(&cam->cam_lock); + + cam->open_count--; + open_count = cam->open_count; + udev = cam->udev; + + mutex_unlock(&cam->cam_lock); + + if (!open_count && !udev) { + kfree(cam); + } + + return 0; +} + +static void vicam_decode_color(const u8 *data, u8 *rgb) +{ + /* vicam_decode_color - Convert from Vicam Y-Cr-Cb to RGB + * Copyright (C) 2002 Monroe Williams (monroe@pobox.com) + */ + + int i, prevY, nextY; + + prevY = 512; + nextY = 512; + + data += VICAM_HEADER_SIZE; + + for( i = 0; i < 240; i++, data += 512 ) { + const int y = ( i * 242 ) / 240; + + int j, prevX, nextX; + int Y, Cr, Cb; + + if ( y == 242 - 1 ) { + nextY = -512; + } + + prevX = 1; + nextX = 1; + + for ( j = 0; j < 320; j++, rgb += 3 ) { + const int x = ( j * 512 ) / 320; + const u8 * const src = &data[x]; + + if ( x == 512 - 1 ) { + nextX = -1; + } + + Cr = ( src[prevX] - src[0] ) + + ( src[nextX] - src[0] ); + Cr /= 2; + + Cb = ( src[prevY] - src[prevX + prevY] ) + + ( src[prevY] - src[nextX + prevY] ) + + ( src[nextY] - src[prevX + nextY] ) + + ( src[nextY] - src[nextX + nextY] ); + Cb /= 4; + + Y = 1160 * ( src[0] + ( Cr / 2 ) - 16 ); + + if ( i & 1 ) { + int Ct = Cr; + Cr = Cb; + Cb = Ct; + } + + if ( ( x ^ i ) & 1 ) { + Cr = -Cr; + Cb = -Cb; + } + + rgb[0] = clamp( ( ( Y + ( 2017 * Cb ) ) + + 500 ) / 900, 0, 255 ); + rgb[1] = clamp( ( ( Y - ( 392 * Cb ) - + ( 813 * Cr ) ) + + 500 ) / 1000, 0, 255 ); + rgb[2] = clamp( ( ( Y + ( 1594 * Cr ) ) + + 500 ) / 1300, 0, 255 ); + + prevX = -1; + } + + prevY = -512; + } +} + +static void +read_frame(struct vicam_camera *cam, int framenum) +{ + unsigned char *request = cam->cntrlbuf; + int realShutter; + int n; + int actual_length; + + if (cam->needsDummyRead) { + cam->needsDummyRead = 0; + read_frame(cam, framenum); + } + + memset(request, 0, 16); + request[0] = cam->gain; // 0 = 0% gain, FF = 100% gain + + request[1] = 0; // 512x242 capture + + request[2] = 0x90; // the function of these two bytes + request[3] = 0x07; // is not yet understood + + if (cam->shutter_speed > 60) { + // Short exposure + realShutter = + ((-15631900 / cam->shutter_speed) + 260533) / 1000; + request[4] = realShutter & 0xFF; + request[5] = (realShutter >> 8) & 0xFF; + request[6] = 0x03; + request[7] = 0x01; + } else { + // Long exposure + realShutter = 15600 / cam->shutter_speed - 1; + request[4] = 0; + request[5] = 0; + request[6] = realShutter & 0xFF; + request[7] = realShutter >> 8; + } + + // Per John Markus Bjørndalen, byte at index 8 causes problems if it isn't 0 + request[8] = 0; + // bytes 9-15 do not seem to affect exposure or image quality + + mutex_lock(&cam->cam_lock); + + if (!cam->udev) { + goto done; + } + + n = __send_control_msg(cam, 0x51, 0x80, 0, request, 16); + + if (n < 0) { + printk(KERN_ERR + " Problem sending frame capture control message"); + goto done; + } + + n = usb_bulk_msg(cam->udev, + usb_rcvbulkpipe(cam->udev, cam->bulkEndpoint), + cam->raw_image, + 512 * 242 + 128, &actual_length, 10000); + + if (n < 0) { + printk(KERN_ERR "Problem during bulk read of frame data: %d\n", + n); + } + + done: + mutex_unlock(&cam->cam_lock); +} + +static ssize_t +vicam_read( struct file *file, char __user *buf, size_t count, loff_t *ppos ) +{ + struct vicam_camera *cam = file->private_data; + + DBG("read %d bytes.\n", (int) count); + + if (*ppos >= VICAM_MAX_FRAME_SIZE) { + *ppos = 0; + return 0; + } + + if (*ppos == 0) { + read_frame(cam, 0); + vicam_decode_color(cam->raw_image, + cam->framebuf + + 0 * VICAM_MAX_FRAME_SIZE); + } + + count = min_t(size_t, count, VICAM_MAX_FRAME_SIZE - *ppos); + + if (copy_to_user(buf, &cam->framebuf[*ppos], count)) { + count = -EFAULT; + } else { + *ppos += count; + } + + if (count == VICAM_MAX_FRAME_SIZE) { + *ppos = 0; + } + + return count; +} + + +static int +vicam_mmap(struct file *file, struct vm_area_struct *vma) +{ + // TODO: allocate the raw frame buffer if necessary + unsigned long page, pos; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + struct vicam_camera *cam = file->private_data; + + if (!cam) + return -ENODEV; + + DBG("vicam_mmap: %ld\n", size); + + /* We let mmap allocate as much as it wants because Linux was adding 2048 bytes + * to the size the application requested for mmap and it was screwing apps up. + if (size > VICAM_FRAMES*VICAM_MAX_FRAME_SIZE) + return -EINVAL; + */ + + pos = (unsigned long)cam->framebuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) + return -EAGAIN; + + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + + return 0; +} + +static const struct v4l2_file_operations vicam_fops = { + .owner = THIS_MODULE, + .open = vicam_open, + .release = vicam_close, + .read = vicam_read, + .mmap = vicam_mmap, + .ioctl = vicam_ioctl, +}; + +static struct video_device vicam_template = { + .name = "ViCam-based USB Camera", + .fops = &vicam_fops, + .release = video_device_release_empty, +}; + +/* table of devices that work with this driver */ +static struct usb_device_id vicam_table[] = { + {USB_DEVICE(USB_VICAM_VENDOR_ID, USB_VICAM_PRODUCT_ID)}, + {USB_DEVICE(USB_COMPRO_VENDOR_ID, USB_COMPRO_PRODUCT_ID)}, + {} /* Terminating entry */ +}; + +MODULE_DEVICE_TABLE(usb, vicam_table); + +static struct usb_driver vicam_driver = { + .name = "vicam", + .probe = vicam_probe, + .disconnect = vicam_disconnect, + .id_table = vicam_table +}; + +/** + * vicam_probe + * @intf: the interface + * @id: the device id + * + * Called by the usb core when a new device is connected that it thinks + * this driver might be interested in. + */ +static int +vicam_probe( struct usb_interface *intf, const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + int bulkEndpoint = 0; + const struct usb_host_interface *interface; + const struct usb_endpoint_descriptor *endpoint; + struct vicam_camera *cam; + + printk(KERN_INFO "ViCam based webcam connected\n"); + + interface = intf->cur_altsetting; + + DBG(KERN_DEBUG "Interface %d. has %u. endpoints!\n", + interface->desc.bInterfaceNumber, (unsigned) (interface->desc.bNumEndpoints)); + endpoint = &interface->endpoint[0].desc; + + if (usb_endpoint_is_bulk_in(endpoint)) { + /* we found a bulk in endpoint */ + bulkEndpoint = endpoint->bEndpointAddress; + } else { + printk(KERN_ERR + "No bulk in endpoint was found ?! (this is bad)\n"); + } + + if ((cam = + kzalloc(sizeof (struct vicam_camera), GFP_KERNEL)) == NULL) { + printk(KERN_WARNING + "could not allocate kernel memory for vicam_camera struct\n"); + return -ENOMEM; + } + + + cam->shutter_speed = 15; + + mutex_init(&cam->cam_lock); + + memcpy(&cam->vdev, &vicam_template, sizeof(vicam_template)); + video_set_drvdata(&cam->vdev, cam); + + cam->udev = dev; + cam->bulkEndpoint = bulkEndpoint; + + if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, -1) < 0) { + kfree(cam); + printk(KERN_WARNING "video_register_device failed\n"); + return -EIO; + } + + printk(KERN_INFO "ViCam webcam driver now controlling device %s\n", + video_device_node_name(&cam->vdev)); + + usb_set_intfdata (intf, cam); + + return 0; +} + +static void +vicam_disconnect(struct usb_interface *intf) +{ + int open_count; + struct vicam_camera *cam = usb_get_intfdata (intf); + usb_set_intfdata (intf, NULL); + + /* we must unregister the device before taking its + * cam_lock. This is because the video open call + * holds the same lock as video unregister. if we + * unregister inside of the cam_lock and open also + * uses the cam_lock, we get deadlock. + */ + + video_unregister_device(&cam->vdev); + + /* stop the camera from being used */ + + mutex_lock(&cam->cam_lock); + + /* mark the camera as gone */ + + cam->udev = NULL; + + /* the only thing left to do is synchronize with + * our close/release function on who should release + * the camera memory. if there are any users using the + * camera, it's their job. if there are no users, + * it's ours. + */ + + open_count = cam->open_count; + + mutex_unlock(&cam->cam_lock); + + if (!open_count) { + kfree(cam); + } + + printk(KERN_DEBUG "ViCam-based WebCam disconnected\n"); +} + +/* + */ +static int __init +usb_vicam_init(void) +{ + int retval; + DBG(KERN_INFO "ViCam-based WebCam driver startup\n"); + retval = usb_register(&vicam_driver); + if (retval) + printk(KERN_WARNING "usb_register failed!\n"); + return retval; +} + +static void __exit +usb_vicam_exit(void) +{ + DBG(KERN_INFO + "ViCam-based WebCam driver shutdown\n"); + + usb_deregister(&vicam_driver); +} + +module_init(usb_vicam_init); +module_exit(usb_vicam_exit); + +MODULE_AUTHOR(DRIVER_AUTHOR); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_LICENSE("GPL"); +MODULE_FIRMWARE("vicam/firmware.fw"); -- cgit v1.2.3 From 71bb2876a3b2af8e6fd5ac20921ee0de9e07d809 Mon Sep 17 00:00:00 2001 From: Hans Verkuil Date: Sat, 25 Dec 2010 07:36:55 -0300 Subject: [media] se401: deprecate driver, move to staging The se401 driver is deprecated and is moved to staging. If no one will convert this driver to V4L2, then it will be removed in 2.6.39. Signed-off-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 11 - drivers/media/video/Makefile | 1 - drivers/media/video/se401.c | 1492 ---------------------------------------- drivers/media/video/se401.h | 236 ------- drivers/staging/Kconfig | 2 + drivers/staging/Makefile | 1 + drivers/staging/se401/Kconfig | 13 + drivers/staging/se401/Makefile | 1 + drivers/staging/se401/se401.c | 1492 ++++++++++++++++++++++++++++++++++++++++ drivers/staging/se401/se401.h | 236 +++++++ 10 files changed, 1745 insertions(+), 1740 deletions(-) delete mode 100644 drivers/media/video/se401.c delete mode 100644 drivers/media/video/se401.h create mode 100644 drivers/staging/se401/Kconfig create mode 100644 drivers/staging/se401/Makefile create mode 100644 drivers/staging/se401/se401.c create mode 100644 drivers/staging/se401/se401.h (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 3655b30254d9..b3bf04368bf2 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -917,17 +917,6 @@ source "drivers/media/video/usbvision/Kconfig" source "drivers/media/video/et61x251/Kconfig" -config USB_SE401 - tristate "USB SE401 Camera support" - depends on VIDEO_V4L1 - ---help--- - Say Y here if you want to connect this type of camera to your - computer's USB port. See - for more information and for a list of supported cameras. - - To compile this driver as a module, choose M here: the - module will be called se401. - source "drivers/media/video/sn9c102/Kconfig" source "drivers/media/video/pwc/Kconfig" diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 2f2e3ac877b4..fd684dd4f91c 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -122,7 +122,6 @@ obj-$(CONFIG_VIDEO_CAFE_CCIC) += cafe_ccic.o obj-$(CONFIG_VIDEO_VIA_CAMERA) += via-camera.o obj-$(CONFIG_USB_DABUSB) += dabusb.o -obj-$(CONFIG_USB_SE401) += se401.o obj-$(CONFIG_USB_ZR364XX) += zr364xx.o obj-$(CONFIG_USB_STKWEBCAM) += stkwebcam.o diff --git a/drivers/media/video/se401.c b/drivers/media/video/se401.c deleted file mode 100644 index 41360d7c3e96..000000000000 --- a/drivers/media/video/se401.c +++ /dev/null @@ -1,1492 +0,0 @@ -/* - * Endpoints (formerly known as AOX) se401 USB Camera Driver - * - * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) - * - * Still somewhat based on the Linux ov511 driver. - * - * 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. - * - * - * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on - * their chipset available and supporting me while writing this driver. - * - Jeroen Vreeken - */ - -static const char version[] = "0.24"; - -#include -#include -#include -#include -#include -#include -#include "se401.h" - -static int flickerless; -static int video_nr = -1; - -static struct usb_device_id device_table[] = { - { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ - { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ - { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ - { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */ - { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */ - { } -}; - -MODULE_DEVICE_TABLE(usb, device_table); - -MODULE_AUTHOR("Jeroen Vreeken "); -MODULE_DESCRIPTION("SE401 USB Camera Driver"); -MODULE_LICENSE("GPL"); -module_param(flickerless, int, 0); -MODULE_PARM_DESC(flickerless, - "Net frequency to adjust exposure time to (0/50/60)"); -module_param(video_nr, int, 0); - -static struct usb_driver se401_driver; - - -/********************************************************************** - * - * Memory management - * - **********************************************************************/ -static void *rvmalloc(unsigned long size) -{ - void *mem; - unsigned long adr; - - size = PAGE_ALIGN(size); - mem = vmalloc_32(size); - if (!mem) - return NULL; - - memset(mem, 0, size); /* Clear the ram out, no junk to the user */ - adr = (unsigned long) mem; - while (size > 0) { - SetPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - - return mem; -} - -static void rvfree(void *mem, unsigned long size) -{ - unsigned long adr; - - if (!mem) - return; - - adr = (unsigned long) mem; - while ((long) size > 0) { - ClearPageReserved(vmalloc_to_page((void *)adr)); - adr += PAGE_SIZE; - size -= PAGE_SIZE; - } - vfree(mem); -} - - - -/**************************************************************************** - * - * se401 register read/write functions - * - ***************************************************************************/ - -static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, - unsigned short value, unsigned char *cp, int size) -{ - return usb_control_msg( - se401->dev, - set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), - req, - (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - value, - 0, - cp, - size, - 1000 - ); -} - -static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, - unsigned short param) -{ - /* specs say that the selector (address) should go in the value field - and the param in index, but in the logs of the windows driver they do - this the other way around... - */ - return usb_control_msg( - se401->dev, - usb_sndctrlpipe(se401->dev, 0), - SE401_REQ_SET_EXT_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - param, - selector, - NULL, - 0, - 1000 - ); -} - -static unsigned short se401_get_feature(struct usb_se401 *se401, - unsigned short selector) -{ - /* For 'set' the selecetor should be in index, not sure if the spec is - wrong here to.... - */ - unsigned char cp[2]; - usb_control_msg( - se401->dev, - usb_rcvctrlpipe(se401->dev, 0), - SE401_REQ_GET_EXT_FEATURE, - USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, - 0, - selector, - cp, - 2, - 1000 - ); - return cp[0]+cp[1]*256; -} - -/**************************************************************************** - * - * Camera control - * - ***************************************************************************/ - - -static int se401_send_pict(struct usb_se401 *se401) -{ - /* integration time low */ - se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l); - /* integration time mid */ - se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m); - /* integration time mid */ - se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h); - /* reset level value */ - se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); - /* red color gain */ - se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain); - /* green color gain */ - se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain); - /* blue color gain */ - se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain); - - return 0; -} - -static void se401_set_exposure(struct usb_se401 *se401, int brightness) -{ - int integration = brightness << 5; - - if (flickerless == 50) - integration = integration-integration % 106667; - if (flickerless == 60) - integration = integration-integration % 88889; - se401->brightness = integration >> 5; - se401->expose_h = (integration >> 16) & 0xff; - se401->expose_m = (integration >> 8) & 0xff; - se401->expose_l = integration & 0xff; -} - -static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) -{ - p->brightness = se401->brightness; - if (se401->enhance) - p->whiteness = 32768; - else - p->whiteness = 0; - - p->colour = 65535; - p->contrast = 65535; - p->hue = se401->rgain << 10; - p->palette = se401->palette; - p->depth = 3; /* rgb24 */ - return 0; -} - - -static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) -{ - if (p->palette != VIDEO_PALETTE_RGB24) - return 1; - se401->palette = p->palette; - if (p->hue != se401->hue) { - se401->rgain = p->hue >> 10; - se401->bgain = 0x40-(p->hue >> 10); - se401->hue = p->hue; - } - if (p->brightness != se401->brightness) - se401_set_exposure(se401, p->brightness); - - if (p->whiteness >= 32768) - se401->enhance = 1; - else - se401->enhance = 0; - se401_send_pict(se401); - se401_send_pict(se401); - return 0; -} - -/* - Hyundai have some really nice docs about this and other sensor related - stuff on their homepage: www.hei.co.kr -*/ -static void se401_auto_resetlevel(struct usb_se401 *se401) -{ - unsigned int ahrc, alrc; - int oldreset = se401->resetlevel; - - /* For some reason this normally read-only register doesn't get reset - to zero after reading them just once... - */ - se401_get_feature(se401, HV7131_REG_HIREFNOH); - se401_get_feature(se401, HV7131_REG_HIREFNOL); - se401_get_feature(se401, HV7131_REG_LOREFNOH); - se401_get_feature(se401, HV7131_REG_LOREFNOL); - ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + - se401_get_feature(se401, HV7131_REG_HIREFNOL); - alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + - se401_get_feature(se401, HV7131_REG_LOREFNOL); - - /* Not an exact science, but it seems to work pretty well... */ - if (alrc > 10) { - while (alrc >= 10 && se401->resetlevel < 63) { - se401->resetlevel++; - alrc /= 2; - } - } else if (ahrc > 20) { - while (ahrc >= 20 && se401->resetlevel > 0) { - se401->resetlevel--; - ahrc /= 2; - } - } - if (se401->resetlevel != oldreset) - se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); - - return; -} - -/* irq handler for snapshot button */ -static void se401_button_irq(struct urb *urb) -{ - struct usb_se401 *se401 = urb->context; - int status; - - if (!se401->dev) { - dev_info(&urb->dev->dev, "device vapourished\n"); - return; - } - - switch (urb->status) { - case 0: - /* success */ - break; - case -ECONNRESET: - case -ENOENT: - case -ESHUTDOWN: - /* this urb is terminated, clean up */ - dbg("%s - urb shutting down with status: %d", - __func__, urb->status); - return; - default: - dbg("%s - nonzero urb status received: %d", - __func__, urb->status); - goto exit; - } - - if (urb->actual_length >= 2) - if (se401->button) - se401->buttonpressed = 1; -exit: - status = usb_submit_urb(urb, GFP_ATOMIC); - if (status) - err("%s - usb_submit_urb failed with result %d", - __func__, status); -} - -static void se401_video_irq(struct urb *urb) -{ - struct usb_se401 *se401 = urb->context; - int length = urb->actual_length; - - /* ohoh... */ - if (!se401->streaming) - return; - - if (!se401->dev) { - dev_info(&urb->dev->dev, "device vapourished\n"); - return; - } - - /* 0 sized packets happen if we are to fast, but sometimes the camera - keeps sending them forever... - */ - if (length && !urb->status) { - se401->nullpackets = 0; - switch (se401->scratch[se401->scratch_next].state) { - case BUFFER_READY: - case BUFFER_BUSY: - se401->dropped++; - break; - case BUFFER_UNUSED: - memcpy(se401->scratch[se401->scratch_next].data, - (unsigned char *)urb->transfer_buffer, length); - se401->scratch[se401->scratch_next].state - = BUFFER_READY; - se401->scratch[se401->scratch_next].offset - = se401->bayeroffset; - se401->scratch[se401->scratch_next].length = length; - if (waitqueue_active(&se401->wq)) - wake_up_interruptible(&se401->wq); - se401->scratch_overflow = 0; - se401->scratch_next++; - if (se401->scratch_next >= SE401_NUMSCRATCH) - se401->scratch_next = 0; - break; - } - se401->bayeroffset += length; - if (se401->bayeroffset >= se401->cheight * se401->cwidth) - se401->bayeroffset = 0; - } else { - se401->nullpackets++; - if (se401->nullpackets > SE401_MAX_NULLPACKETS) - if (waitqueue_active(&se401->wq)) - wake_up_interruptible(&se401->wq); - } - - /* Resubmit urb for new data */ - urb->status = 0; - urb->dev = se401->dev; - if (usb_submit_urb(urb, GFP_KERNEL)) - dev_info(&urb->dev->dev, "urb burned down\n"); - return; -} - -static void se401_send_size(struct usb_se401 *se401, int width, int height) -{ - int i = 0; - int mode = 0x03; /* No compression */ - int sendheight = height; - int sendwidth = width; - - /* JangGu compression can only be used with the camera supported sizes, - but bayer seems to work with any size that fits on the sensor. - We check if we can use compression with the current size with either - 4 or 16 times subcapturing, if not we use uncompressed bayer data - but this will result in cutouts of the maximum size.... - */ - while (i < se401->sizes && !(se401->width[i] == width && - se401->height[i] == height)) - i++; - while (i < se401->sizes) { - if (se401->width[i] == width * 2 && - se401->height[i] == height * 2) { - sendheight = se401->height[i]; - sendwidth = se401->width[i]; - mode = 0x40; - } - if (se401->width[i] == width * 4 && - se401->height[i] == height * 4) { - sendheight = se401->height[i]; - sendwidth = se401->width[i]; - mode = 0x42; - } - i++; - } - - se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); - se401_set_feature(se401, SE401_OPERATINGMODE, mode); - - if (mode == 0x03) - se401->format = FMT_BAYER; - else - se401->format = FMT_JANGGU; -} - -/* - In this function se401_send_pict is called several times, - for some reason (depending on the state of the sensor and the phase of - the moon :) doing this only in either place doesn't always work... -*/ -static int se401_start_stream(struct usb_se401 *se401) -{ - struct urb *urb; - int err = 0, i; - se401->streaming = 1; - - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - - /* Set picture settings */ - /* windowed + pix intg */ - se401_set_feature(se401, HV7131_REG_MODE_B, 0x05); - se401_send_pict(se401); - - se401_send_size(se401, se401->cwidth, se401->cheight); - - se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, - 0, NULL, 0); - - /* Do some memory allocation */ - for (i = 0; i < SE401_NUMFRAMES; i++) { - se401->frame[i].data = se401->fbuf + i * se401->maxframesize; - se401->frame[i].curpix = 0; - } - for (i = 0; i < SE401_NUMSBUF; i++) { - se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); - if (!se401->sbuf[i].data) { - for (i = i - 1; i >= 0; i--) { - kfree(se401->sbuf[i].data); - se401->sbuf[i].data = NULL; - } - return -ENOMEM; - } - } - - se401->bayeroffset = 0; - se401->scratch_next = 0; - se401->scratch_use = 0; - se401->scratch_overflow = 0; - for (i = 0; i < SE401_NUMSCRATCH; i++) { - se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); - if (!se401->scratch[i].data) { - for (i = i - 1; i >= 0; i--) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - goto nomem_sbuf; - } - se401->scratch[i].state = BUFFER_UNUSED; - } - - for (i = 0; i < SE401_NUMSBUF; i++) { - urb = usb_alloc_urb(0, GFP_KERNEL); - if (!urb) { - for (i = i - 1; i >= 0; i--) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - } - goto nomem_scratch; - } - - usb_fill_bulk_urb(urb, se401->dev, - usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), - se401->sbuf[i].data, SE401_PACKETSIZE, - se401_video_irq, - se401); - - se401->urb[i] = urb; - - err = usb_submit_urb(se401->urb[i], GFP_KERNEL); - if (err) - err("urb burned down"); - } - - se401->framecount = 0; - - return 0; - - nomem_scratch: - for (i = 0; i < SE401_NUMSCRATCH; i++) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - nomem_sbuf: - for (i = 0; i < SE401_NUMSBUF; i++) { - kfree(se401->sbuf[i].data); - se401->sbuf[i].data = NULL; - } - return -ENOMEM; -} - -static int se401_stop_stream(struct usb_se401 *se401) -{ - int i; - - if (!se401->streaming || !se401->dev) - return 1; - - se401->streaming = 0; - - se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); - - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); - - for (i = 0; i < SE401_NUMSBUF; i++) - if (se401->urb[i]) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - kfree(se401->sbuf[i].data); - } - for (i = 0; i < SE401_NUMSCRATCH; i++) { - kfree(se401->scratch[i].data); - se401->scratch[i].data = NULL; - } - - return 0; -} - -static int se401_set_size(struct usb_se401 *se401, int width, int height) -{ - int wasstreaming = se401->streaming; - /* Check to see if we need to change */ - if (se401->cwidth == width && se401->cheight == height) - return 0; - - /* Check for a valid mode */ - if (!width || !height) - return 1; - if ((width & 1) || (height & 1)) - return 1; - if (width > se401->width[se401->sizes-1]) - return 1; - if (height > se401->height[se401->sizes-1]) - return 1; - - /* Stop a current stream and start it again at the new size */ - if (wasstreaming) - se401_stop_stream(se401); - se401->cwidth = width; - se401->cheight = height; - if (wasstreaming) - se401_start_stream(se401); - return 0; -} - - -/**************************************************************************** - * - * Video Decoding - * - ***************************************************************************/ - -/* - This shouldn't really be done in a v4l driver.... - But it does make the image look a lot more usable. - Basically it lifts the dark pixels more than the light pixels. -*/ -static inline void enhance_picture(unsigned char *frame, int len) -{ - while (len--) { - *frame = (((*frame^255)*(*frame^255))/255)^255; - frame++; - } -} - -static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) -{ - struct se401_frame *frame = &se401->frame[se401->curframe]; - int linelength = se401->cwidth * 3; - - if (frame->curlinepix >= linelength) { - frame->curlinepix = 0; - frame->curline += linelength; - } - - /* First three are absolute, all others relative. - * Format is rgb from right to left (mirrorred image), - * we flip it to get bgr from left to right. */ - if (frame->curlinepix < 3) - *(frame->curline-frame->curlinepix) = 1 + data * 4; - else - *(frame->curline-frame->curlinepix) = - *(frame->curline-frame->curlinepix + 3) + data * 4; - frame->curlinepix++; -} - -static inline void decode_JangGu_vlc(struct usb_se401 *se401, - unsigned char *data, int bit_exp, int packetlength) -{ - int pos = 0; - int vlc_cod = 0; - int vlc_size = 0; - int vlc_data = 0; - int bit_cur; - int bit; - data += 4; - while (pos < packetlength) { - bit_cur = 8; - while (bit_cur && bit_exp) { - bit = ((*data) >> (bit_cur-1))&1; - if (!vlc_cod) { - if (bit) { - vlc_size++; - } else { - if (!vlc_size) - decode_JangGu_integrate(se401, 0); - else { - vlc_cod = 2; - vlc_data = 0; - } - } - } else { - if (vlc_cod == 2) { - if (!bit) - vlc_data = -(1 << vlc_size) + 1; - vlc_cod--; - } - vlc_size--; - vlc_data += bit << vlc_size; - if (!vlc_size) { - decode_JangGu_integrate(se401, vlc_data); - vlc_cod = 0; - } - } - bit_cur--; - bit_exp--; - } - pos++; - data++; - } -} - -static inline void decode_JangGu(struct usb_se401 *se401, - struct se401_scratch *buffer) -{ - unsigned char *data = buffer->data; - int len = buffer->length; - int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size; - int datapos = 0; - - /* New image? */ - if (!se401->frame[se401->curframe].curpix) { - se401->frame[se401->curframe].curlinepix = 0; - se401->frame[se401->curframe].curline = - se401->frame[se401->curframe].data+ - se401->cwidth * 3 - 1; - if (se401->frame[se401->curframe].grabstate == FRAME_READY) - se401->frame[se401->curframe].grabstate = FRAME_GRABBING; - se401->vlcdatapos = 0; - } - while (datapos < len) { - size = 1024 - se401->vlcdatapos; - if (size+datapos > len) - size = len-datapos; - memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); - se401->vlcdatapos += size; - packetlength = 0; - if (se401->vlcdatapos >= 4) { - bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8); - pix_exp = se401->vlcdata[1] + - ((se401->vlcdata[0] & 0x3f) << 8); - frameinfo = se401->vlcdata[0] & 0xc0; - packetlength = ((bit_exp + 47) >> 4) << 1; - if (packetlength > 1024) { - se401->vlcdatapos = 0; - datapos = len; - packetlength = 0; - se401->error++; - se401->frame[se401->curframe].curpix = 0; - } - } - if (packetlength && se401->vlcdatapos >= packetlength) { - decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, - packetlength); - se401->frame[se401->curframe].curpix += pix_exp * 3; - datapos += size-(se401->vlcdatapos-packetlength); - se401->vlcdatapos = 0; - if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) { - if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) { - if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) { - se401->frame[se401->curframe].grabstate = FRAME_DONE; - se401->framecount++; - se401->readcount++; - } - if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) - se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1); - } else - se401->error++; - se401->frame[se401->curframe].curpix = 0; - datapos = len; - } - } else - datapos += size; - } -} - -static inline void decode_bayer(struct usb_se401 *se401, - struct se401_scratch *buffer) -{ - unsigned char *data = buffer->data; - int len = buffer->length; - int offset = buffer->offset; - int datasize = se401->cwidth * se401->cheight; - struct se401_frame *frame = &se401->frame[se401->curframe]; - unsigned char *framedata = frame->data, *curline, *nextline; - int width = se401->cwidth; - int blineoffset = 0, bline; - int linelength = width * 3, i; - - - if (frame->curpix == 0) { - if (frame->grabstate == FRAME_READY) - frame->grabstate = FRAME_GRABBING; - - frame->curline = framedata + linelength; - frame->curlinepix = 0; - } - - if (offset != frame->curpix) { - /* Regard frame as lost :( */ - frame->curpix = 0; - se401->error++; - return; - } - - /* Check if we have to much data */ - if (frame->curpix + len > datasize) - len = datasize-frame->curpix; - - if (se401->cheight % 4) - blineoffset = 1; - bline = frame->curpix / se401->cwidth+blineoffset; - - curline = frame->curline; - nextline = curline + linelength; - if (nextline >= framedata+datasize * 3) - nextline = curline; - while (len) { - if (frame->curlinepix >= width) { - frame->curlinepix -= width; - bline = frame->curpix / width + blineoffset; - curline += linelength*2; - nextline += linelength*2; - if (curline >= framedata+datasize * 3) { - frame->curlinepix++; - curline -= 3; - nextline -= 3; - len--; - data++; - frame->curpix++; - } - if (nextline >= framedata+datasize*3) - nextline = curline; - } - if (bline & 1) { - if (frame->curlinepix & 1) { - *(curline + 2) = *data; - *(curline - 1) = *data; - *(nextline + 2) = *data; - *(nextline - 1) = *data; - } else { - *(curline + 1) = - (*(curline + 1) + *data) / 2; - *(curline-2) = - (*(curline - 2) + *data) / 2; - *(nextline + 1) = *data; - *(nextline - 2) = *data; - } - } else { - if (frame->curlinepix & 1) { - *(curline + 1) = - (*(curline + 1) + *data) / 2; - *(curline - 2) = - (*(curline - 2) + *data) / 2; - *(nextline + 1) = *data; - *(nextline - 2) = *data; - } else { - *curline = *data; - *(curline - 3) = *data; - *nextline = *data; - *(nextline - 3) = *data; - } - } - frame->curlinepix++; - curline -= 3; - nextline -= 3; - len--; - data++; - frame->curpix++; - } - frame->curline = curline; - - if (frame->curpix >= datasize) { - /* Fix the top line */ - framedata += linelength; - for (i = 0; i < linelength; i++) { - framedata--; - *framedata = *(framedata + linelength); - } - /* Fix the left side (green is already present) */ - for (i = 0; i < se401->cheight; i++) { - *framedata = *(framedata + 3); - *(framedata + 1) = *(framedata + 4); - *(framedata + 2) = *(framedata + 5); - framedata += linelength; - } - frame->curpix = 0; - frame->grabstate = FRAME_DONE; - se401->framecount++; - se401->readcount++; - if (se401->frame[(se401->curframe + 1) & - (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) { - se401->curframe = (se401->curframe+1) & - (SE401_NUMFRAMES-1); - } - } -} - -static int se401_newframe(struct usb_se401 *se401, int framenr) -{ - DECLARE_WAITQUEUE(wait, current); - int errors = 0; - - while (se401->streaming && - (se401->frame[framenr].grabstate == FRAME_READY || - se401->frame[framenr].grabstate == FRAME_GRABBING)) { - if (!se401->frame[framenr].curpix) - errors++; - - wait_interruptible( - se401->scratch[se401->scratch_use].state != BUFFER_READY, - &se401->wq, &wait); - if (se401->nullpackets > SE401_MAX_NULLPACKETS) { - se401->nullpackets = 0; - dev_info(&se401->dev->dev, - "too many null length packets, restarting capture\n"); - se401_stop_stream(se401); - se401_start_stream(se401); - } else { - if (se401->scratch[se401->scratch_use].state != - BUFFER_READY) { - se401->frame[framenr].grabstate = FRAME_ERROR; - return -EIO; - } - se401->scratch[se401->scratch_use].state = BUFFER_BUSY; - if (se401->format == FMT_JANGGU) - decode_JangGu(se401, - &se401->scratch[se401->scratch_use]); - else - decode_bayer(se401, - &se401->scratch[se401->scratch_use]); - - se401->scratch[se401->scratch_use].state = - BUFFER_UNUSED; - se401->scratch_use++; - if (se401->scratch_use >= SE401_NUMSCRATCH) - se401->scratch_use = 0; - if (errors > SE401_MAX_ERRORS) { - errors = 0; - dev_info(&se401->dev->dev, - "too many errors, restarting capture\n"); - se401_stop_stream(se401); - se401_start_stream(se401); - } - } - } - - if (se401->frame[framenr].grabstate == FRAME_DONE) - if (se401->enhance) - enhance_picture(se401->frame[framenr].data, - se401->cheight * se401->cwidth * 3); - return 0; -} - -static void usb_se401_remove_disconnected(struct usb_se401 *se401) -{ - int i; - - se401->dev = NULL; - - for (i = 0; i < SE401_NUMSBUF; i++) - if (se401->urb[i]) { - usb_kill_urb(se401->urb[i]); - usb_free_urb(se401->urb[i]); - se401->urb[i] = NULL; - kfree(se401->sbuf[i].data); - } - - for (i = 0; i < SE401_NUMSCRATCH; i++) - kfree(se401->scratch[i].data); - - if (se401->inturb) { - usb_kill_urb(se401->inturb); - usb_free_urb(se401->inturb); - } - dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name); - - /* Free the memory */ - kfree(se401->width); - kfree(se401->height); - kfree(se401); -} - - - -/**************************************************************************** - * - * Video4Linux - * - ***************************************************************************/ - - -static int se401_open(struct file *file) -{ - struct video_device *dev = video_devdata(file); - struct usb_se401 *se401 = (struct usb_se401 *)dev; - int err = 0; - - mutex_lock(&se401->lock); - if (se401->user) { - mutex_unlock(&se401->lock); - return -EBUSY; - } - se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES); - if (se401->fbuf) - file->private_data = dev; - else - err = -ENOMEM; - se401->user = !err; - mutex_unlock(&se401->lock); - - return err; -} - -static int se401_close(struct file *file) -{ - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - int i; - - rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES); - if (se401->removed) { - dev_info(&se401->dev->dev, "device unregistered\n"); - usb_se401_remove_disconnected(se401); - } else { - for (i = 0; i < SE401_NUMFRAMES; i++) - se401->frame[i].grabstate = FRAME_UNUSED; - if (se401->streaming) - se401_stop_stream(se401); - se401->user = 0; - } - file->private_data = NULL; - return 0; -} - -static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) -{ - struct video_device *vdev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)vdev; - - if (!se401->dev) - return -EIO; - - switch (cmd) { - case VIDIOCGCAP: - { - struct video_capability *b = arg; - strcpy(b->name, se401->camera_name); - b->type = VID_TYPE_CAPTURE; - b->channels = 1; - b->audios = 0; - b->maxwidth = se401->width[se401->sizes-1]; - b->maxheight = se401->height[se401->sizes-1]; - b->minwidth = se401->width[0]; - b->minheight = se401->height[0]; - return 0; - } - case VIDIOCGCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - v->flags = 0; - v->tuners = 0; - v->type = VIDEO_TYPE_CAMERA; - strcpy(v->name, "Camera"); - return 0; - } - case VIDIOCSCHAN: - { - struct video_channel *v = arg; - - if (v->channel != 0) - return -EINVAL; - return 0; - } - case VIDIOCGPICT: - { - struct video_picture *p = arg; - - se401_get_pict(se401, p); - return 0; - } - case VIDIOCSPICT: - { - struct video_picture *p = arg; - - if (se401_set_pict(se401, p)) - return -EINVAL; - return 0; - } - case VIDIOCSWIN: - { - struct video_window *vw = arg; - - if (vw->flags) - return -EINVAL; - if (vw->clipcount) - return -EINVAL; - if (se401_set_size(se401, vw->width, vw->height)) - return -EINVAL; - return 0; - } - case VIDIOCGWIN: - { - struct video_window *vw = arg; - - vw->x = 0; /* FIXME */ - vw->y = 0; - vw->chromakey = 0; - vw->flags = 0; - vw->clipcount = 0; - vw->width = se401->cwidth; - vw->height = se401->cheight; - return 0; - } - case VIDIOCGMBUF: - { - struct video_mbuf *vm = arg; - int i; - - memset(vm, 0, sizeof(*vm)); - vm->size = SE401_NUMFRAMES * se401->maxframesize; - vm->frames = SE401_NUMFRAMES; - for (i = 0; i < SE401_NUMFRAMES; i++) - vm->offsets[i] = se401->maxframesize * i; - return 0; - } - case VIDIOCMCAPTURE: - { - struct video_mmap *vm = arg; - - if (vm->format != VIDEO_PALETTE_RGB24) - return -EINVAL; - if (vm->frame >= SE401_NUMFRAMES) - return -EINVAL; - if (se401->frame[vm->frame].grabstate != FRAME_UNUSED) - return -EBUSY; - - /* Is this according to the v4l spec??? */ - if (se401_set_size(se401, vm->width, vm->height)) - return -EINVAL; - se401->frame[vm->frame].grabstate = FRAME_READY; - - if (!se401->streaming) - se401_start_stream(se401); - - /* Set the picture properties */ - if (se401->framecount == 0) - se401_send_pict(se401); - /* Calibrate the reset level after a few frames. */ - if (se401->framecount % 20 == 1) - se401_auto_resetlevel(se401); - - return 0; - } - case VIDIOCSYNC: - { - int *frame = arg; - int ret = 0; - - if (*frame < 0 || *frame >= SE401_NUMFRAMES) - return -EINVAL; - - ret = se401_newframe(se401, *frame); - se401->frame[*frame].grabstate = FRAME_UNUSED; - return ret; - } - case VIDIOCGFBUF: - { - struct video_buffer *vb = arg; - - memset(vb, 0, sizeof(*vb)); - return 0; - } - case VIDIOCKEY: - return 0; - case VIDIOCCAPTURE: - return -EINVAL; - case VIDIOCSFBUF: - return -EINVAL; - case VIDIOCGTUNER: - case VIDIOCSTUNER: - return -EINVAL; - case VIDIOCGFREQ: - case VIDIOCSFREQ: - return -EINVAL; - case VIDIOCGAUDIO: - case VIDIOCSAUDIO: - return -EINVAL; - default: - return -ENOIOCTLCMD; - } /* end switch */ - - return 0; -} - -static long se401_ioctl(struct file *file, - unsigned int cmd, unsigned long arg) -{ - return video_usercopy(file, cmd, arg, se401_do_ioctl); -} - -static ssize_t se401_read(struct file *file, char __user *buf, - size_t count, loff_t *ppos) -{ - int realcount = count, ret = 0; - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - - - if (se401->dev == NULL) - return -EIO; - if (realcount > se401->cwidth*se401->cheight*3) - realcount = se401->cwidth*se401->cheight*3; - - /* Shouldn't happen: */ - if (se401->frame[0].grabstate == FRAME_GRABBING) - return -EBUSY; - se401->frame[0].grabstate = FRAME_READY; - se401->frame[1].grabstate = FRAME_UNUSED; - se401->curframe = 0; - - if (!se401->streaming) - se401_start_stream(se401); - - /* Set the picture properties */ - if (se401->framecount == 0) - se401_send_pict(se401); - /* Calibrate the reset level after a few frames. */ - if (se401->framecount%20 == 1) - se401_auto_resetlevel(se401); - - ret = se401_newframe(se401, 0); - - se401->frame[0].grabstate = FRAME_UNUSED; - if (ret) - return ret; - if (copy_to_user(buf, se401->frame[0].data, realcount)) - return -EFAULT; - - return realcount; -} - -static int se401_mmap(struct file *file, struct vm_area_struct *vma) -{ - struct video_device *dev = file->private_data; - struct usb_se401 *se401 = (struct usb_se401 *)dev; - unsigned long start = vma->vm_start; - unsigned long size = vma->vm_end-vma->vm_start; - unsigned long page, pos; - - mutex_lock(&se401->lock); - - if (se401->dev == NULL) { - mutex_unlock(&se401->lock); - return -EIO; - } - if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) - & ~(PAGE_SIZE - 1))) { - mutex_unlock(&se401->lock); - return -EINVAL; - } - pos = (unsigned long)se401->fbuf; - while (size > 0) { - page = vmalloc_to_pfn((void *)pos); - if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { - mutex_unlock(&se401->lock); - return -EAGAIN; - } - start += PAGE_SIZE; - pos += PAGE_SIZE; - if (size > PAGE_SIZE) - size -= PAGE_SIZE; - else - size = 0; - } - mutex_unlock(&se401->lock); - - return 0; -} - -static const struct v4l2_file_operations se401_fops = { - .owner = THIS_MODULE, - .open = se401_open, - .release = se401_close, - .read = se401_read, - .mmap = se401_mmap, - .ioctl = se401_ioctl, -}; -static struct video_device se401_template = { - .name = "se401 USB camera", - .fops = &se401_fops, - .release = video_device_release_empty, -}; - - - -/***************************/ -static int se401_init(struct usb_se401 *se401, int button) -{ - int i = 0, rc; - unsigned char cp[0x40]; - char temp[200]; - int slen; - - /* led on */ - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - - /* get camera descriptor */ - rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, - cp, sizeof(cp)); - if (cp[1] != 0x41) { - err("Wrong descriptor type"); - return 1; - } - slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]); - - se401->sizes = cp[4] + cp[5] * 256; - se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); - if (!se401->width) - return 1; - se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); - if (!se401->height) { - kfree(se401->width); - return 1; - } - for (i = 0; i < se401->sizes; i++) { - se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256; - se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256; - } - slen += snprintf(temp + slen, 200 - slen, " Sizes:"); - for (i = 0; i < se401->sizes; i++) { - slen += snprintf(temp + slen, 200 - slen, - " %dx%d", se401->width[i], se401->height[i]); - } - dev_info(&se401->dev->dev, "%s\n", temp); - se401->maxframesize = se401->width[se401->sizes-1] * - se401->height[se401->sizes - 1] * 3; - - rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); - se401->cwidth = cp[0]+cp[1]*256; - rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); - se401->cheight = cp[0]+cp[1]*256; - - if (!(cp[2] & SE401_FORMAT_BAYER)) { - err("Bayer format not supported!"); - return 1; - } - /* set output mode (BAYER) */ - se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, - SE401_FORMAT_BAYER, NULL, 0); - - rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); - se401->brightness = cp[0]+cp[1]*256; - /* some default values */ - se401->resetlevel = 0x2d; - se401->rgain = 0x20; - se401->ggain = 0x20; - se401->bgain = 0x20; - se401_set_exposure(se401, 20000); - se401->palette = VIDEO_PALETTE_RGB24; - se401->enhance = 1; - se401->dropped = 0; - se401->error = 0; - se401->framecount = 0; - se401->readcount = 0; - - /* Start interrupt transfers for snapshot button */ - if (button) { - se401->inturb = usb_alloc_urb(0, GFP_KERNEL); - if (!se401->inturb) { - dev_info(&se401->dev->dev, - "Allocation of inturb failed\n"); - return 1; - } - usb_fill_int_urb(se401->inturb, se401->dev, - usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT), - &se401->button, sizeof(se401->button), - se401_button_irq, - se401, - 8 - ); - if (usb_submit_urb(se401->inturb, GFP_KERNEL)) { - dev_info(&se401->dev->dev, "int urb burned down\n"); - return 1; - } - } else - se401->inturb = NULL; - - /* Flash the led */ - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); - se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); - - return 0; -} - -static int se401_probe(struct usb_interface *intf, - const struct usb_device_id *id) -{ - struct usb_device *dev = interface_to_usbdev(intf); - struct usb_interface_descriptor *interface; - struct usb_se401 *se401; - char *camera_name = NULL; - int button = 1; - - /* We don't handle multi-config cameras */ - if (dev->descriptor.bNumConfigurations != 1) - return -ENODEV; - - interface = &intf->cur_altsetting->desc; - - /* Is it an se401? */ - if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && - le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { - camera_name = "Endpoints/Aox SE401"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && - le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { - camera_name = "Philips PCVC665K"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { - camera_name = "Kensington VideoCAM 67014"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { - camera_name = "Kensington VideoCAM 6701(5/7)"; - } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && - le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { - camera_name = "Kensington VideoCAM 67016"; - button = 0; - } else - return -ENODEV; - - /* Checking vendor/product should be enough, but what the hell */ - if (interface->bInterfaceClass != 0x00) - return -ENODEV; - if (interface->bInterfaceSubClass != 0x00) - return -ENODEV; - - /* We found one */ - dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name); - - se401 = kzalloc(sizeof(*se401), GFP_KERNEL); - if (se401 == NULL) { - err("couldn't kmalloc se401 struct"); - return -ENOMEM; - } - - se401->dev = dev; - se401->iface = interface->bInterfaceNumber; - se401->camera_name = camera_name; - - dev_info(&intf->dev, "firmware version: %02x\n", - le16_to_cpu(dev->descriptor.bcdDevice) & 255); - - if (se401_init(se401, button)) { - kfree(se401); - return -EIO; - } - - memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); - memcpy(se401->vdev.name, se401->camera_name, - strlen(se401->camera_name)); - init_waitqueue_head(&se401->wq); - mutex_init(&se401->lock); - wmb(); - - if (video_register_device(&se401->vdev, - VFL_TYPE_GRABBER, video_nr) < 0) { - kfree(se401); - err("video_register_device failed"); - return -EIO; - } - dev_info(&intf->dev, "registered new video device: %s\n", - video_device_node_name(&se401->vdev)); - - usb_set_intfdata(intf, se401); - return 0; -} - -static void se401_disconnect(struct usb_interface *intf) -{ - struct usb_se401 *se401 = usb_get_intfdata(intf); - - usb_set_intfdata(intf, NULL); - if (se401) { - video_unregister_device(&se401->vdev); - if (!se401->user) - usb_se401_remove_disconnected(se401); - else { - se401->frame[0].grabstate = FRAME_ERROR; - se401->frame[0].grabstate = FRAME_ERROR; - - se401->streaming = 0; - - wake_up_interruptible(&se401->wq); - se401->removed = 1; - } - } -} - -static struct usb_driver se401_driver = { - .name = "se401", - .id_table = device_table, - .probe = se401_probe, - .disconnect = se401_disconnect, -}; - - - -/**************************************************************************** - * - * Module routines - * - ***************************************************************************/ - -static int __init usb_se401_init(void) -{ - printk(KERN_INFO "SE401 usb camera driver version %s registering\n", - version); - if (flickerless) - if (flickerless != 50 && flickerless != 60) { - printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n"); - return -1; - } - return usb_register(&se401_driver); -} - -static void __exit usb_se401_exit(void) -{ - usb_deregister(&se401_driver); - printk(KERN_INFO "SE401 driver deregistered\frame"); -} - -module_init(usb_se401_init); -module_exit(usb_se401_exit); diff --git a/drivers/media/video/se401.h b/drivers/media/video/se401.h deleted file mode 100644 index bf7d2e9765b0..000000000000 --- a/drivers/media/video/se401.h +++ /dev/null @@ -1,236 +0,0 @@ - -#ifndef __LINUX_se401_H -#define __LINUX_se401_H - -#include -#include -#include -#include -#include - -#define se401_DEBUG /* Turn on debug messages */ - -#ifdef se401_DEBUG -# define PDEBUG(level, fmt, args...) \ -if (debug >= level) \ - info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) -#else -# define PDEBUG(level, fmt, args...) do {} while (0) -#endif - -/* An almost drop-in replacement for sleep_on_interruptible */ -#define wait_interruptible(test, queue, wait) \ -{ \ - add_wait_queue(queue, wait); \ - set_current_state(TASK_INTERRUPTIBLE); \ - if (test) \ - schedule(); \ - remove_wait_queue(queue, wait); \ - set_current_state(TASK_RUNNING); \ - if (signal_pending(current)) \ - break; \ -} - -#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 -#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 -#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 -#define SE401_REQ_CAPTURE_FRAME 0x43 -#define SE401_REQ_GET_BRT 0x44 -#define SE401_REQ_SET_BRT 0x45 -#define SE401_REQ_GET_WIDTH 0x4c -#define SE401_REQ_SET_WIDTH 0x4d -#define SE401_REQ_GET_HEIGHT 0x4e -#define SE401_REQ_SET_HEIGHT 0x4f -#define SE401_REQ_GET_OUTPUT_MODE 0x50 -#define SE401_REQ_SET_OUTPUT_MODE 0x51 -#define SE401_REQ_GET_EXT_FEATURE 0x52 -#define SE401_REQ_SET_EXT_FEATURE 0x53 -#define SE401_REQ_CAMERA_POWER 0x56 -#define SE401_REQ_LED_CONTROL 0x57 -#define SE401_REQ_BIOS 0xff - -#define SE401_BIOS_READ 0x07 - -#define SE401_FORMAT_BAYER 0x40 - -/* Hyundai hv7131b registers - 7121 and 7141 should be the same (haven't really checked...) */ -/* Mode registers: */ -#define HV7131_REG_MODE_A 0x00 -#define HV7131_REG_MODE_B 0x01 -#define HV7131_REG_MODE_C 0x02 -/* Frame registers: */ -#define HV7131_REG_FRSU 0x10 -#define HV7131_REG_FRSL 0x11 -#define HV7131_REG_FCSU 0x12 -#define HV7131_REG_FCSL 0x13 -#define HV7131_REG_FWHU 0x14 -#define HV7131_REG_FWHL 0x15 -#define HV7131_REG_FWWU 0x16 -#define HV7131_REG_FWWL 0x17 -/* Timing registers: */ -#define HV7131_REG_THBU 0x20 -#define HV7131_REG_THBL 0x21 -#define HV7131_REG_TVBU 0x22 -#define HV7131_REG_TVBL 0x23 -#define HV7131_REG_TITU 0x25 -#define HV7131_REG_TITM 0x26 -#define HV7131_REG_TITL 0x27 -#define HV7131_REG_TMCD 0x28 -/* Adjust Registers: */ -#define HV7131_REG_ARLV 0x30 -#define HV7131_REG_ARCG 0x31 -#define HV7131_REG_AGCG 0x32 -#define HV7131_REG_ABCG 0x33 -#define HV7131_REG_APBV 0x34 -#define HV7131_REG_ASLP 0x54 -/* Offset Registers: */ -#define HV7131_REG_OFSR 0x50 -#define HV7131_REG_OFSG 0x51 -#define HV7131_REG_OFSB 0x52 -/* REset level statistics registers: */ -#define HV7131_REG_LOREFNOH 0x57 -#define HV7131_REG_LOREFNOL 0x58 -#define HV7131_REG_HIREFNOH 0x59 -#define HV7131_REG_HIREFNOL 0x5a - -/* se401 registers */ -#define SE401_OPERATINGMODE 0x2000 - - -/* size of usb transfers */ -#define SE401_PACKETSIZE 4096 -/* number of queued bulk transfers to use, should be about 8 */ -#define SE401_NUMSBUF 1 -/* read the usb specs for this one :) */ -#define SE401_VIDEO_ENDPOINT 1 -#define SE401_BUTTON_ENDPOINT 2 -/* number of frames supported by the v4l part */ -#define SE401_NUMFRAMES 2 -/* scratch buffers for passing data to the decoders */ -#define SE401_NUMSCRATCH 32 -/* maximum amount of data in a JangGu packet */ -#define SE401_VLCDATALEN 1024 -/* number of nul sized packets to receive before kicking the camera */ -#define SE401_MAX_NULLPACKETS 4000 -/* number of decoding errors before kicking the camera */ -#define SE401_MAX_ERRORS 200 - -struct usb_device; - -struct se401_sbuf { - unsigned char *data; -}; - -enum { - FRAME_UNUSED, /* Unused (no MCAPTURE) */ - FRAME_READY, /* Ready to start grabbing */ - FRAME_GRABBING, /* In the process of being grabbed into */ - FRAME_DONE, /* Finished grabbing, but not been synced yet */ - FRAME_ERROR, /* Something bad happened while processing */ -}; - -enum { - FMT_BAYER, - FMT_JANGGU, -}; - -enum { - BUFFER_UNUSED, - BUFFER_READY, - BUFFER_BUSY, - BUFFER_DONE, -}; - -struct se401_scratch { - unsigned char *data; - volatile int state; - int offset; - int length; -}; - -struct se401_frame { - unsigned char *data; /* Frame buffer */ - - volatile int grabstate; /* State of grabbing */ - - unsigned char *curline; - int curlinepix; - int curpix; -}; - -struct usb_se401 { - struct video_device vdev; - - /* Device structure */ - struct usb_device *dev; - - unsigned char iface; - - char *camera_name; - - int change; - int brightness; - int hue; - int rgain; - int ggain; - int bgain; - int expose_h; - int expose_m; - int expose_l; - int resetlevel; - - int enhance; - - int format; - int sizes; - int *width; - int *height; - int cwidth; /* current width */ - int cheight; /* current height */ - int palette; - int maxframesize; - int cframesize; /* current framesize */ - - struct mutex lock; - int user; /* user count for exclusive use */ - int removed; /* device disconnected */ - - int streaming; /* Are we streaming video? */ - - char *fbuf; /* Videodev buffer area */ - - struct urb *urb[SE401_NUMSBUF]; - struct urb *inturb; - - int button; - int buttonpressed; - - int curframe; /* Current receiving frame */ - struct se401_frame frame[SE401_NUMFRAMES]; - int readcount; - int framecount; - int error; - int dropped; - - int scratch_next; - int scratch_use; - int scratch_overflow; - struct se401_scratch scratch[SE401_NUMSCRATCH]; - - /* Decoder specific data: */ - unsigned char vlcdata[SE401_VLCDATALEN]; - int vlcdatapos; - int bayeroffset; - - struct se401_sbuf sbuf[SE401_NUMSBUF]; - - wait_queue_head_t wq; /* Processes waiting */ - - int nullpackets; -}; - - - -#endif - diff --git a/drivers/staging/Kconfig b/drivers/staging/Kconfig index a402a5614b86..d9e3b6ddf682 100644 --- a/drivers/staging/Kconfig +++ b/drivers/staging/Kconfig @@ -55,6 +55,8 @@ source "drivers/staging/cpia/Kconfig" source "drivers/staging/stradis/Kconfig" +source "drivers/staging/se401/Kconfig" + source "drivers/staging/usbvideo/Kconfig" source "drivers/staging/usbip/Kconfig" diff --git a/drivers/staging/Makefile b/drivers/staging/Makefile index d4eb7146a87c..8058a15b0cd7 100644 --- a/drivers/staging/Makefile +++ b/drivers/staging/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_VIDEO_TM6000) += tm6000/ obj-$(CONFIG_VIDEO_CPIA) += cpia/ obj-$(CONFIG_VIDEO_STRADIS) += stradis/ obj-$(CONFIG_USB_VICAM) += usbvideo/ +obj-$(CONFIG_USB_SE401) += se401/ obj-$(CONFIG_LIRC_STAGING) += lirc/ obj-$(CONFIG_USB_IP_COMMON) += usbip/ obj-$(CONFIG_W35UND) += winbond/ diff --git a/drivers/staging/se401/Kconfig b/drivers/staging/se401/Kconfig new file mode 100644 index 000000000000..586fc0432a4b --- /dev/null +++ b/drivers/staging/se401/Kconfig @@ -0,0 +1,13 @@ +config USB_SE401 + tristate "USB SE401 Camera support (DEPRECATED)" + depends on VIDEO_V4L1 + ---help--- + Say Y here if you want to connect this type of camera to your + computer's USB port. See + for more information and for a list of supported cameras. + + This driver uses the deprecated V4L1 API and will be removed in + 2.6.39, unless someone converts it to the V4L2 API. + + To compile this driver as a module, choose M here: the + module will be called se401. diff --git a/drivers/staging/se401/Makefile b/drivers/staging/se401/Makefile new file mode 100644 index 000000000000..b465d49783af --- /dev/null +++ b/drivers/staging/se401/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_USB_SE401) += se401.o diff --git a/drivers/staging/se401/se401.c b/drivers/staging/se401/se401.c new file mode 100644 index 000000000000..41360d7c3e96 --- /dev/null +++ b/drivers/staging/se401/se401.c @@ -0,0 +1,1492 @@ +/* + * Endpoints (formerly known as AOX) se401 USB Camera Driver + * + * Copyright (c) 2000 Jeroen B. Vreeken (pe1rxq@amsat.org) + * + * Still somewhat based on the Linux ov511 driver. + * + * 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. + * + * + * Thanks to Endpoints Inc. (www.endpoints.com) for making documentation on + * their chipset available and supporting me while writing this driver. + * - Jeroen Vreeken + */ + +static const char version[] = "0.24"; + +#include +#include +#include +#include +#include +#include +#include "se401.h" + +static int flickerless; +static int video_nr = -1; + +static struct usb_device_id device_table[] = { + { USB_DEVICE(0x03e8, 0x0004) },/* Endpoints/Aox SE401 */ + { USB_DEVICE(0x0471, 0x030b) },/* Philips PCVC665K */ + { USB_DEVICE(0x047d, 0x5001) },/* Kensington 67014 */ + { USB_DEVICE(0x047d, 0x5002) },/* Kensington 6701(5/7) */ + { USB_DEVICE(0x047d, 0x5003) },/* Kensington 67016 */ + { } +}; + +MODULE_DEVICE_TABLE(usb, device_table); + +MODULE_AUTHOR("Jeroen Vreeken "); +MODULE_DESCRIPTION("SE401 USB Camera Driver"); +MODULE_LICENSE("GPL"); +module_param(flickerless, int, 0); +MODULE_PARM_DESC(flickerless, + "Net frequency to adjust exposure time to (0/50/60)"); +module_param(video_nr, int, 0); + +static struct usb_driver se401_driver; + + +/********************************************************************** + * + * Memory management + * + **********************************************************************/ +static void *rvmalloc(unsigned long size) +{ + void *mem; + unsigned long adr; + + size = PAGE_ALIGN(size); + mem = vmalloc_32(size); + if (!mem) + return NULL; + + memset(mem, 0, size); /* Clear the ram out, no junk to the user */ + adr = (unsigned long) mem; + while (size > 0) { + SetPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + + return mem; +} + +static void rvfree(void *mem, unsigned long size) +{ + unsigned long adr; + + if (!mem) + return; + + adr = (unsigned long) mem; + while ((long) size > 0) { + ClearPageReserved(vmalloc_to_page((void *)adr)); + adr += PAGE_SIZE; + size -= PAGE_SIZE; + } + vfree(mem); +} + + + +/**************************************************************************** + * + * se401 register read/write functions + * + ***************************************************************************/ + +static int se401_sndctrl(int set, struct usb_se401 *se401, unsigned short req, + unsigned short value, unsigned char *cp, int size) +{ + return usb_control_msg( + se401->dev, + set ? usb_sndctrlpipe(se401->dev, 0) : usb_rcvctrlpipe(se401->dev, 0), + req, + (set ? USB_DIR_OUT : USB_DIR_IN) | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + value, + 0, + cp, + size, + 1000 + ); +} + +static int se401_set_feature(struct usb_se401 *se401, unsigned short selector, + unsigned short param) +{ + /* specs say that the selector (address) should go in the value field + and the param in index, but in the logs of the windows driver they do + this the other way around... + */ + return usb_control_msg( + se401->dev, + usb_sndctrlpipe(se401->dev, 0), + SE401_REQ_SET_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + param, + selector, + NULL, + 0, + 1000 + ); +} + +static unsigned short se401_get_feature(struct usb_se401 *se401, + unsigned short selector) +{ + /* For 'set' the selecetor should be in index, not sure if the spec is + wrong here to.... + */ + unsigned char cp[2]; + usb_control_msg( + se401->dev, + usb_rcvctrlpipe(se401->dev, 0), + SE401_REQ_GET_EXT_FEATURE, + USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE, + 0, + selector, + cp, + 2, + 1000 + ); + return cp[0]+cp[1]*256; +} + +/**************************************************************************** + * + * Camera control + * + ***************************************************************************/ + + +static int se401_send_pict(struct usb_se401 *se401) +{ + /* integration time low */ + se401_set_feature(se401, HV7131_REG_TITL, se401->expose_l); + /* integration time mid */ + se401_set_feature(se401, HV7131_REG_TITM, se401->expose_m); + /* integration time mid */ + se401_set_feature(se401, HV7131_REG_TITU, se401->expose_h); + /* reset level value */ + se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); + /* red color gain */ + se401_set_feature(se401, HV7131_REG_ARCG, se401->rgain); + /* green color gain */ + se401_set_feature(se401, HV7131_REG_AGCG, se401->ggain); + /* blue color gain */ + se401_set_feature(se401, HV7131_REG_ABCG, se401->bgain); + + return 0; +} + +static void se401_set_exposure(struct usb_se401 *se401, int brightness) +{ + int integration = brightness << 5; + + if (flickerless == 50) + integration = integration-integration % 106667; + if (flickerless == 60) + integration = integration-integration % 88889; + se401->brightness = integration >> 5; + se401->expose_h = (integration >> 16) & 0xff; + se401->expose_m = (integration >> 8) & 0xff; + se401->expose_l = integration & 0xff; +} + +static int se401_get_pict(struct usb_se401 *se401, struct video_picture *p) +{ + p->brightness = se401->brightness; + if (se401->enhance) + p->whiteness = 32768; + else + p->whiteness = 0; + + p->colour = 65535; + p->contrast = 65535; + p->hue = se401->rgain << 10; + p->palette = se401->palette; + p->depth = 3; /* rgb24 */ + return 0; +} + + +static int se401_set_pict(struct usb_se401 *se401, struct video_picture *p) +{ + if (p->palette != VIDEO_PALETTE_RGB24) + return 1; + se401->palette = p->palette; + if (p->hue != se401->hue) { + se401->rgain = p->hue >> 10; + se401->bgain = 0x40-(p->hue >> 10); + se401->hue = p->hue; + } + if (p->brightness != se401->brightness) + se401_set_exposure(se401, p->brightness); + + if (p->whiteness >= 32768) + se401->enhance = 1; + else + se401->enhance = 0; + se401_send_pict(se401); + se401_send_pict(se401); + return 0; +} + +/* + Hyundai have some really nice docs about this and other sensor related + stuff on their homepage: www.hei.co.kr +*/ +static void se401_auto_resetlevel(struct usb_se401 *se401) +{ + unsigned int ahrc, alrc; + int oldreset = se401->resetlevel; + + /* For some reason this normally read-only register doesn't get reset + to zero after reading them just once... + */ + se401_get_feature(se401, HV7131_REG_HIREFNOH); + se401_get_feature(se401, HV7131_REG_HIREFNOL); + se401_get_feature(se401, HV7131_REG_LOREFNOH); + se401_get_feature(se401, HV7131_REG_LOREFNOL); + ahrc = 256*se401_get_feature(se401, HV7131_REG_HIREFNOH) + + se401_get_feature(se401, HV7131_REG_HIREFNOL); + alrc = 256*se401_get_feature(se401, HV7131_REG_LOREFNOH) + + se401_get_feature(se401, HV7131_REG_LOREFNOL); + + /* Not an exact science, but it seems to work pretty well... */ + if (alrc > 10) { + while (alrc >= 10 && se401->resetlevel < 63) { + se401->resetlevel++; + alrc /= 2; + } + } else if (ahrc > 20) { + while (ahrc >= 20 && se401->resetlevel > 0) { + se401->resetlevel--; + ahrc /= 2; + } + } + if (se401->resetlevel != oldreset) + se401_set_feature(se401, HV7131_REG_ARLV, se401->resetlevel); + + return; +} + +/* irq handler for snapshot button */ +static void se401_button_irq(struct urb *urb) +{ + struct usb_se401 *se401 = urb->context; + int status; + + if (!se401->dev) { + dev_info(&urb->dev->dev, "device vapourished\n"); + return; + } + + switch (urb->status) { + case 0: + /* success */ + break; + case -ECONNRESET: + case -ENOENT: + case -ESHUTDOWN: + /* this urb is terminated, clean up */ + dbg("%s - urb shutting down with status: %d", + __func__, urb->status); + return; + default: + dbg("%s - nonzero urb status received: %d", + __func__, urb->status); + goto exit; + } + + if (urb->actual_length >= 2) + if (se401->button) + se401->buttonpressed = 1; +exit: + status = usb_submit_urb(urb, GFP_ATOMIC); + if (status) + err("%s - usb_submit_urb failed with result %d", + __func__, status); +} + +static void se401_video_irq(struct urb *urb) +{ + struct usb_se401 *se401 = urb->context; + int length = urb->actual_length; + + /* ohoh... */ + if (!se401->streaming) + return; + + if (!se401->dev) { + dev_info(&urb->dev->dev, "device vapourished\n"); + return; + } + + /* 0 sized packets happen if we are to fast, but sometimes the camera + keeps sending them forever... + */ + if (length && !urb->status) { + se401->nullpackets = 0; + switch (se401->scratch[se401->scratch_next].state) { + case BUFFER_READY: + case BUFFER_BUSY: + se401->dropped++; + break; + case BUFFER_UNUSED: + memcpy(se401->scratch[se401->scratch_next].data, + (unsigned char *)urb->transfer_buffer, length); + se401->scratch[se401->scratch_next].state + = BUFFER_READY; + se401->scratch[se401->scratch_next].offset + = se401->bayeroffset; + se401->scratch[se401->scratch_next].length = length; + if (waitqueue_active(&se401->wq)) + wake_up_interruptible(&se401->wq); + se401->scratch_overflow = 0; + se401->scratch_next++; + if (se401->scratch_next >= SE401_NUMSCRATCH) + se401->scratch_next = 0; + break; + } + se401->bayeroffset += length; + if (se401->bayeroffset >= se401->cheight * se401->cwidth) + se401->bayeroffset = 0; + } else { + se401->nullpackets++; + if (se401->nullpackets > SE401_MAX_NULLPACKETS) + if (waitqueue_active(&se401->wq)) + wake_up_interruptible(&se401->wq); + } + + /* Resubmit urb for new data */ + urb->status = 0; + urb->dev = se401->dev; + if (usb_submit_urb(urb, GFP_KERNEL)) + dev_info(&urb->dev->dev, "urb burned down\n"); + return; +} + +static void se401_send_size(struct usb_se401 *se401, int width, int height) +{ + int i = 0; + int mode = 0x03; /* No compression */ + int sendheight = height; + int sendwidth = width; + + /* JangGu compression can only be used with the camera supported sizes, + but bayer seems to work with any size that fits on the sensor. + We check if we can use compression with the current size with either + 4 or 16 times subcapturing, if not we use uncompressed bayer data + but this will result in cutouts of the maximum size.... + */ + while (i < se401->sizes && !(se401->width[i] == width && + se401->height[i] == height)) + i++; + while (i < se401->sizes) { + if (se401->width[i] == width * 2 && + se401->height[i] == height * 2) { + sendheight = se401->height[i]; + sendwidth = se401->width[i]; + mode = 0x40; + } + if (se401->width[i] == width * 4 && + se401->height[i] == height * 4) { + sendheight = se401->height[i]; + sendwidth = se401->width[i]; + mode = 0x42; + } + i++; + } + + se401_sndctrl(1, se401, SE401_REQ_SET_WIDTH, sendwidth, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_SET_HEIGHT, sendheight, NULL, 0); + se401_set_feature(se401, SE401_OPERATINGMODE, mode); + + if (mode == 0x03) + se401->format = FMT_BAYER; + else + se401->format = FMT_JANGGU; +} + +/* + In this function se401_send_pict is called several times, + for some reason (depending on the state of the sensor and the phase of + the moon :) doing this only in either place doesn't always work... +*/ +static int se401_start_stream(struct usb_se401 *se401) +{ + struct urb *urb; + int err = 0, i; + se401->streaming = 1; + + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + + /* Set picture settings */ + /* windowed + pix intg */ + se401_set_feature(se401, HV7131_REG_MODE_B, 0x05); + se401_send_pict(se401); + + se401_send_size(se401, se401->cwidth, se401->cheight); + + se401_sndctrl(1, se401, SE401_REQ_START_CONTINUOUS_CAPTURE, + 0, NULL, 0); + + /* Do some memory allocation */ + for (i = 0; i < SE401_NUMFRAMES; i++) { + se401->frame[i].data = se401->fbuf + i * se401->maxframesize; + se401->frame[i].curpix = 0; + } + for (i = 0; i < SE401_NUMSBUF; i++) { + se401->sbuf[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + if (!se401->sbuf[i].data) { + for (i = i - 1; i >= 0; i--) { + kfree(se401->sbuf[i].data); + se401->sbuf[i].data = NULL; + } + return -ENOMEM; + } + } + + se401->bayeroffset = 0; + se401->scratch_next = 0; + se401->scratch_use = 0; + se401->scratch_overflow = 0; + for (i = 0; i < SE401_NUMSCRATCH; i++) { + se401->scratch[i].data = kmalloc(SE401_PACKETSIZE, GFP_KERNEL); + if (!se401->scratch[i].data) { + for (i = i - 1; i >= 0; i--) { + kfree(se401->scratch[i].data); + se401->scratch[i].data = NULL; + } + goto nomem_sbuf; + } + se401->scratch[i].state = BUFFER_UNUSED; + } + + for (i = 0; i < SE401_NUMSBUF; i++) { + urb = usb_alloc_urb(0, GFP_KERNEL); + if (!urb) { + for (i = i - 1; i >= 0; i--) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + } + goto nomem_scratch; + } + + usb_fill_bulk_urb(urb, se401->dev, + usb_rcvbulkpipe(se401->dev, SE401_VIDEO_ENDPOINT), + se401->sbuf[i].data, SE401_PACKETSIZE, + se401_video_irq, + se401); + + se401->urb[i] = urb; + + err = usb_submit_urb(se401->urb[i], GFP_KERNEL); + if (err) + err("urb burned down"); + } + + se401->framecount = 0; + + return 0; + + nomem_scratch: + for (i = 0; i < SE401_NUMSCRATCH; i++) { + kfree(se401->scratch[i].data); + se401->scratch[i].data = NULL; + } + nomem_sbuf: + for (i = 0; i < SE401_NUMSBUF; i++) { + kfree(se401->sbuf[i].data); + se401->sbuf[i].data = NULL; + } + return -ENOMEM; +} + +static int se401_stop_stream(struct usb_se401 *se401) +{ + int i; + + if (!se401->streaming || !se401->dev) + return 1; + + se401->streaming = 0; + + se401_sndctrl(1, se401, SE401_REQ_STOP_CONTINUOUS_CAPTURE, 0, NULL, 0); + + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); + + for (i = 0; i < SE401_NUMSBUF; i++) + if (se401->urb[i]) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + kfree(se401->sbuf[i].data); + } + for (i = 0; i < SE401_NUMSCRATCH; i++) { + kfree(se401->scratch[i].data); + se401->scratch[i].data = NULL; + } + + return 0; +} + +static int se401_set_size(struct usb_se401 *se401, int width, int height) +{ + int wasstreaming = se401->streaming; + /* Check to see if we need to change */ + if (se401->cwidth == width && se401->cheight == height) + return 0; + + /* Check for a valid mode */ + if (!width || !height) + return 1; + if ((width & 1) || (height & 1)) + return 1; + if (width > se401->width[se401->sizes-1]) + return 1; + if (height > se401->height[se401->sizes-1]) + return 1; + + /* Stop a current stream and start it again at the new size */ + if (wasstreaming) + se401_stop_stream(se401); + se401->cwidth = width; + se401->cheight = height; + if (wasstreaming) + se401_start_stream(se401); + return 0; +} + + +/**************************************************************************** + * + * Video Decoding + * + ***************************************************************************/ + +/* + This shouldn't really be done in a v4l driver.... + But it does make the image look a lot more usable. + Basically it lifts the dark pixels more than the light pixels. +*/ +static inline void enhance_picture(unsigned char *frame, int len) +{ + while (len--) { + *frame = (((*frame^255)*(*frame^255))/255)^255; + frame++; + } +} + +static inline void decode_JangGu_integrate(struct usb_se401 *se401, int data) +{ + struct se401_frame *frame = &se401->frame[se401->curframe]; + int linelength = se401->cwidth * 3; + + if (frame->curlinepix >= linelength) { + frame->curlinepix = 0; + frame->curline += linelength; + } + + /* First three are absolute, all others relative. + * Format is rgb from right to left (mirrorred image), + * we flip it to get bgr from left to right. */ + if (frame->curlinepix < 3) + *(frame->curline-frame->curlinepix) = 1 + data * 4; + else + *(frame->curline-frame->curlinepix) = + *(frame->curline-frame->curlinepix + 3) + data * 4; + frame->curlinepix++; +} + +static inline void decode_JangGu_vlc(struct usb_se401 *se401, + unsigned char *data, int bit_exp, int packetlength) +{ + int pos = 0; + int vlc_cod = 0; + int vlc_size = 0; + int vlc_data = 0; + int bit_cur; + int bit; + data += 4; + while (pos < packetlength) { + bit_cur = 8; + while (bit_cur && bit_exp) { + bit = ((*data) >> (bit_cur-1))&1; + if (!vlc_cod) { + if (bit) { + vlc_size++; + } else { + if (!vlc_size) + decode_JangGu_integrate(se401, 0); + else { + vlc_cod = 2; + vlc_data = 0; + } + } + } else { + if (vlc_cod == 2) { + if (!bit) + vlc_data = -(1 << vlc_size) + 1; + vlc_cod--; + } + vlc_size--; + vlc_data += bit << vlc_size; + if (!vlc_size) { + decode_JangGu_integrate(se401, vlc_data); + vlc_cod = 0; + } + } + bit_cur--; + bit_exp--; + } + pos++; + data++; + } +} + +static inline void decode_JangGu(struct usb_se401 *se401, + struct se401_scratch *buffer) +{ + unsigned char *data = buffer->data; + int len = buffer->length; + int bit_exp = 0, pix_exp = 0, frameinfo = 0, packetlength = 0, size; + int datapos = 0; + + /* New image? */ + if (!se401->frame[se401->curframe].curpix) { + se401->frame[se401->curframe].curlinepix = 0; + se401->frame[se401->curframe].curline = + se401->frame[se401->curframe].data+ + se401->cwidth * 3 - 1; + if (se401->frame[se401->curframe].grabstate == FRAME_READY) + se401->frame[se401->curframe].grabstate = FRAME_GRABBING; + se401->vlcdatapos = 0; + } + while (datapos < len) { + size = 1024 - se401->vlcdatapos; + if (size+datapos > len) + size = len-datapos; + memcpy(se401->vlcdata+se401->vlcdatapos, data+datapos, size); + se401->vlcdatapos += size; + packetlength = 0; + if (se401->vlcdatapos >= 4) { + bit_exp = se401->vlcdata[3] + (se401->vlcdata[2] << 8); + pix_exp = se401->vlcdata[1] + + ((se401->vlcdata[0] & 0x3f) << 8); + frameinfo = se401->vlcdata[0] & 0xc0; + packetlength = ((bit_exp + 47) >> 4) << 1; + if (packetlength > 1024) { + se401->vlcdatapos = 0; + datapos = len; + packetlength = 0; + se401->error++; + se401->frame[se401->curframe].curpix = 0; + } + } + if (packetlength && se401->vlcdatapos >= packetlength) { + decode_JangGu_vlc(se401, se401->vlcdata, bit_exp, + packetlength); + se401->frame[se401->curframe].curpix += pix_exp * 3; + datapos += size-(se401->vlcdatapos-packetlength); + se401->vlcdatapos = 0; + if (se401->frame[se401->curframe].curpix >= se401->cwidth * se401->cheight * 3) { + if (se401->frame[se401->curframe].curpix == se401->cwidth * se401->cheight * 3) { + if (se401->frame[se401->curframe].grabstate == FRAME_GRABBING) { + se401->frame[se401->curframe].grabstate = FRAME_DONE; + se401->framecount++; + se401->readcount++; + } + if (se401->frame[(se401->curframe + 1) & (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) + se401->curframe = (se401->curframe + 1) & (SE401_NUMFRAMES - 1); + } else + se401->error++; + se401->frame[se401->curframe].curpix = 0; + datapos = len; + } + } else + datapos += size; + } +} + +static inline void decode_bayer(struct usb_se401 *se401, + struct se401_scratch *buffer) +{ + unsigned char *data = buffer->data; + int len = buffer->length; + int offset = buffer->offset; + int datasize = se401->cwidth * se401->cheight; + struct se401_frame *frame = &se401->frame[se401->curframe]; + unsigned char *framedata = frame->data, *curline, *nextline; + int width = se401->cwidth; + int blineoffset = 0, bline; + int linelength = width * 3, i; + + + if (frame->curpix == 0) { + if (frame->grabstate == FRAME_READY) + frame->grabstate = FRAME_GRABBING; + + frame->curline = framedata + linelength; + frame->curlinepix = 0; + } + + if (offset != frame->curpix) { + /* Regard frame as lost :( */ + frame->curpix = 0; + se401->error++; + return; + } + + /* Check if we have to much data */ + if (frame->curpix + len > datasize) + len = datasize-frame->curpix; + + if (se401->cheight % 4) + blineoffset = 1; + bline = frame->curpix / se401->cwidth+blineoffset; + + curline = frame->curline; + nextline = curline + linelength; + if (nextline >= framedata+datasize * 3) + nextline = curline; + while (len) { + if (frame->curlinepix >= width) { + frame->curlinepix -= width; + bline = frame->curpix / width + blineoffset; + curline += linelength*2; + nextline += linelength*2; + if (curline >= framedata+datasize * 3) { + frame->curlinepix++; + curline -= 3; + nextline -= 3; + len--; + data++; + frame->curpix++; + } + if (nextline >= framedata+datasize*3) + nextline = curline; + } + if (bline & 1) { + if (frame->curlinepix & 1) { + *(curline + 2) = *data; + *(curline - 1) = *data; + *(nextline + 2) = *data; + *(nextline - 1) = *data; + } else { + *(curline + 1) = + (*(curline + 1) + *data) / 2; + *(curline-2) = + (*(curline - 2) + *data) / 2; + *(nextline + 1) = *data; + *(nextline - 2) = *data; + } + } else { + if (frame->curlinepix & 1) { + *(curline + 1) = + (*(curline + 1) + *data) / 2; + *(curline - 2) = + (*(curline - 2) + *data) / 2; + *(nextline + 1) = *data; + *(nextline - 2) = *data; + } else { + *curline = *data; + *(curline - 3) = *data; + *nextline = *data; + *(nextline - 3) = *data; + } + } + frame->curlinepix++; + curline -= 3; + nextline -= 3; + len--; + data++; + frame->curpix++; + } + frame->curline = curline; + + if (frame->curpix >= datasize) { + /* Fix the top line */ + framedata += linelength; + for (i = 0; i < linelength; i++) { + framedata--; + *framedata = *(framedata + linelength); + } + /* Fix the left side (green is already present) */ + for (i = 0; i < se401->cheight; i++) { + *framedata = *(framedata + 3); + *(framedata + 1) = *(framedata + 4); + *(framedata + 2) = *(framedata + 5); + framedata += linelength; + } + frame->curpix = 0; + frame->grabstate = FRAME_DONE; + se401->framecount++; + se401->readcount++; + if (se401->frame[(se401->curframe + 1) & + (SE401_NUMFRAMES - 1)].grabstate == FRAME_READY) { + se401->curframe = (se401->curframe+1) & + (SE401_NUMFRAMES-1); + } + } +} + +static int se401_newframe(struct usb_se401 *se401, int framenr) +{ + DECLARE_WAITQUEUE(wait, current); + int errors = 0; + + while (se401->streaming && + (se401->frame[framenr].grabstate == FRAME_READY || + se401->frame[framenr].grabstate == FRAME_GRABBING)) { + if (!se401->frame[framenr].curpix) + errors++; + + wait_interruptible( + se401->scratch[se401->scratch_use].state != BUFFER_READY, + &se401->wq, &wait); + if (se401->nullpackets > SE401_MAX_NULLPACKETS) { + se401->nullpackets = 0; + dev_info(&se401->dev->dev, + "too many null length packets, restarting capture\n"); + se401_stop_stream(se401); + se401_start_stream(se401); + } else { + if (se401->scratch[se401->scratch_use].state != + BUFFER_READY) { + se401->frame[framenr].grabstate = FRAME_ERROR; + return -EIO; + } + se401->scratch[se401->scratch_use].state = BUFFER_BUSY; + if (se401->format == FMT_JANGGU) + decode_JangGu(se401, + &se401->scratch[se401->scratch_use]); + else + decode_bayer(se401, + &se401->scratch[se401->scratch_use]); + + se401->scratch[se401->scratch_use].state = + BUFFER_UNUSED; + se401->scratch_use++; + if (se401->scratch_use >= SE401_NUMSCRATCH) + se401->scratch_use = 0; + if (errors > SE401_MAX_ERRORS) { + errors = 0; + dev_info(&se401->dev->dev, + "too many errors, restarting capture\n"); + se401_stop_stream(se401); + se401_start_stream(se401); + } + } + } + + if (se401->frame[framenr].grabstate == FRAME_DONE) + if (se401->enhance) + enhance_picture(se401->frame[framenr].data, + se401->cheight * se401->cwidth * 3); + return 0; +} + +static void usb_se401_remove_disconnected(struct usb_se401 *se401) +{ + int i; + + se401->dev = NULL; + + for (i = 0; i < SE401_NUMSBUF; i++) + if (se401->urb[i]) { + usb_kill_urb(se401->urb[i]); + usb_free_urb(se401->urb[i]); + se401->urb[i] = NULL; + kfree(se401->sbuf[i].data); + } + + for (i = 0; i < SE401_NUMSCRATCH; i++) + kfree(se401->scratch[i].data); + + if (se401->inturb) { + usb_kill_urb(se401->inturb); + usb_free_urb(se401->inturb); + } + dev_info(&se401->dev->dev, "%s disconnected", se401->camera_name); + + /* Free the memory */ + kfree(se401->width); + kfree(se401->height); + kfree(se401); +} + + + +/**************************************************************************** + * + * Video4Linux + * + ***************************************************************************/ + + +static int se401_open(struct file *file) +{ + struct video_device *dev = video_devdata(file); + struct usb_se401 *se401 = (struct usb_se401 *)dev; + int err = 0; + + mutex_lock(&se401->lock); + if (se401->user) { + mutex_unlock(&se401->lock); + return -EBUSY; + } + se401->fbuf = rvmalloc(se401->maxframesize * SE401_NUMFRAMES); + if (se401->fbuf) + file->private_data = dev; + else + err = -ENOMEM; + se401->user = !err; + mutex_unlock(&se401->lock); + + return err; +} + +static int se401_close(struct file *file) +{ + struct video_device *dev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)dev; + int i; + + rvfree(se401->fbuf, se401->maxframesize * SE401_NUMFRAMES); + if (se401->removed) { + dev_info(&se401->dev->dev, "device unregistered\n"); + usb_se401_remove_disconnected(se401); + } else { + for (i = 0; i < SE401_NUMFRAMES; i++) + se401->frame[i].grabstate = FRAME_UNUSED; + if (se401->streaming) + se401_stop_stream(se401); + se401->user = 0; + } + file->private_data = NULL; + return 0; +} + +static long se401_do_ioctl(struct file *file, unsigned int cmd, void *arg) +{ + struct video_device *vdev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)vdev; + + if (!se401->dev) + return -EIO; + + switch (cmd) { + case VIDIOCGCAP: + { + struct video_capability *b = arg; + strcpy(b->name, se401->camera_name); + b->type = VID_TYPE_CAPTURE; + b->channels = 1; + b->audios = 0; + b->maxwidth = se401->width[se401->sizes-1]; + b->maxheight = se401->height[se401->sizes-1]; + b->minwidth = se401->width[0]; + b->minheight = se401->height[0]; + return 0; + } + case VIDIOCGCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + v->flags = 0; + v->tuners = 0; + v->type = VIDEO_TYPE_CAMERA; + strcpy(v->name, "Camera"); + return 0; + } + case VIDIOCSCHAN: + { + struct video_channel *v = arg; + + if (v->channel != 0) + return -EINVAL; + return 0; + } + case VIDIOCGPICT: + { + struct video_picture *p = arg; + + se401_get_pict(se401, p); + return 0; + } + case VIDIOCSPICT: + { + struct video_picture *p = arg; + + if (se401_set_pict(se401, p)) + return -EINVAL; + return 0; + } + case VIDIOCSWIN: + { + struct video_window *vw = arg; + + if (vw->flags) + return -EINVAL; + if (vw->clipcount) + return -EINVAL; + if (se401_set_size(se401, vw->width, vw->height)) + return -EINVAL; + return 0; + } + case VIDIOCGWIN: + { + struct video_window *vw = arg; + + vw->x = 0; /* FIXME */ + vw->y = 0; + vw->chromakey = 0; + vw->flags = 0; + vw->clipcount = 0; + vw->width = se401->cwidth; + vw->height = se401->cheight; + return 0; + } + case VIDIOCGMBUF: + { + struct video_mbuf *vm = arg; + int i; + + memset(vm, 0, sizeof(*vm)); + vm->size = SE401_NUMFRAMES * se401->maxframesize; + vm->frames = SE401_NUMFRAMES; + for (i = 0; i < SE401_NUMFRAMES; i++) + vm->offsets[i] = se401->maxframesize * i; + return 0; + } + case VIDIOCMCAPTURE: + { + struct video_mmap *vm = arg; + + if (vm->format != VIDEO_PALETTE_RGB24) + return -EINVAL; + if (vm->frame >= SE401_NUMFRAMES) + return -EINVAL; + if (se401->frame[vm->frame].grabstate != FRAME_UNUSED) + return -EBUSY; + + /* Is this according to the v4l spec??? */ + if (se401_set_size(se401, vm->width, vm->height)) + return -EINVAL; + se401->frame[vm->frame].grabstate = FRAME_READY; + + if (!se401->streaming) + se401_start_stream(se401); + + /* Set the picture properties */ + if (se401->framecount == 0) + se401_send_pict(se401); + /* Calibrate the reset level after a few frames. */ + if (se401->framecount % 20 == 1) + se401_auto_resetlevel(se401); + + return 0; + } + case VIDIOCSYNC: + { + int *frame = arg; + int ret = 0; + + if (*frame < 0 || *frame >= SE401_NUMFRAMES) + return -EINVAL; + + ret = se401_newframe(se401, *frame); + se401->frame[*frame].grabstate = FRAME_UNUSED; + return ret; + } + case VIDIOCGFBUF: + { + struct video_buffer *vb = arg; + + memset(vb, 0, sizeof(*vb)); + return 0; + } + case VIDIOCKEY: + return 0; + case VIDIOCCAPTURE: + return -EINVAL; + case VIDIOCSFBUF: + return -EINVAL; + case VIDIOCGTUNER: + case VIDIOCSTUNER: + return -EINVAL; + case VIDIOCGFREQ: + case VIDIOCSFREQ: + return -EINVAL; + case VIDIOCGAUDIO: + case VIDIOCSAUDIO: + return -EINVAL; + default: + return -ENOIOCTLCMD; + } /* end switch */ + + return 0; +} + +static long se401_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + return video_usercopy(file, cmd, arg, se401_do_ioctl); +} + +static ssize_t se401_read(struct file *file, char __user *buf, + size_t count, loff_t *ppos) +{ + int realcount = count, ret = 0; + struct video_device *dev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)dev; + + + if (se401->dev == NULL) + return -EIO; + if (realcount > se401->cwidth*se401->cheight*3) + realcount = se401->cwidth*se401->cheight*3; + + /* Shouldn't happen: */ + if (se401->frame[0].grabstate == FRAME_GRABBING) + return -EBUSY; + se401->frame[0].grabstate = FRAME_READY; + se401->frame[1].grabstate = FRAME_UNUSED; + se401->curframe = 0; + + if (!se401->streaming) + se401_start_stream(se401); + + /* Set the picture properties */ + if (se401->framecount == 0) + se401_send_pict(se401); + /* Calibrate the reset level after a few frames. */ + if (se401->framecount%20 == 1) + se401_auto_resetlevel(se401); + + ret = se401_newframe(se401, 0); + + se401->frame[0].grabstate = FRAME_UNUSED; + if (ret) + return ret; + if (copy_to_user(buf, se401->frame[0].data, realcount)) + return -EFAULT; + + return realcount; +} + +static int se401_mmap(struct file *file, struct vm_area_struct *vma) +{ + struct video_device *dev = file->private_data; + struct usb_se401 *se401 = (struct usb_se401 *)dev; + unsigned long start = vma->vm_start; + unsigned long size = vma->vm_end-vma->vm_start; + unsigned long page, pos; + + mutex_lock(&se401->lock); + + if (se401->dev == NULL) { + mutex_unlock(&se401->lock); + return -EIO; + } + if (size > (((SE401_NUMFRAMES * se401->maxframesize) + PAGE_SIZE - 1) + & ~(PAGE_SIZE - 1))) { + mutex_unlock(&se401->lock); + return -EINVAL; + } + pos = (unsigned long)se401->fbuf; + while (size > 0) { + page = vmalloc_to_pfn((void *)pos); + if (remap_pfn_range(vma, start, page, PAGE_SIZE, PAGE_SHARED)) { + mutex_unlock(&se401->lock); + return -EAGAIN; + } + start += PAGE_SIZE; + pos += PAGE_SIZE; + if (size > PAGE_SIZE) + size -= PAGE_SIZE; + else + size = 0; + } + mutex_unlock(&se401->lock); + + return 0; +} + +static const struct v4l2_file_operations se401_fops = { + .owner = THIS_MODULE, + .open = se401_open, + .release = se401_close, + .read = se401_read, + .mmap = se401_mmap, + .ioctl = se401_ioctl, +}; +static struct video_device se401_template = { + .name = "se401 USB camera", + .fops = &se401_fops, + .release = video_device_release_empty, +}; + + + +/***************************/ +static int se401_init(struct usb_se401 *se401, int button) +{ + int i = 0, rc; + unsigned char cp[0x40]; + char temp[200]; + int slen; + + /* led on */ + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + + /* get camera descriptor */ + rc = se401_sndctrl(0, se401, SE401_REQ_GET_CAMERA_DESCRIPTOR, 0, + cp, sizeof(cp)); + if (cp[1] != 0x41) { + err("Wrong descriptor type"); + return 1; + } + slen = snprintf(temp, 200, "ExtraFeatures: %d", cp[3]); + + se401->sizes = cp[4] + cp[5] * 256; + se401->width = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + if (!se401->width) + return 1; + se401->height = kmalloc(se401->sizes*sizeof(int), GFP_KERNEL); + if (!se401->height) { + kfree(se401->width); + return 1; + } + for (i = 0; i < se401->sizes; i++) { + se401->width[i] = cp[6 + i * 4 + 0] + cp[6 + i*4 + 1] * 256; + se401->height[i] = cp[6 + i * 4 + 2] + cp[6 + i * 4 + 3] * 256; + } + slen += snprintf(temp + slen, 200 - slen, " Sizes:"); + for (i = 0; i < se401->sizes; i++) { + slen += snprintf(temp + slen, 200 - slen, + " %dx%d", se401->width[i], se401->height[i]); + } + dev_info(&se401->dev->dev, "%s\n", temp); + se401->maxframesize = se401->width[se401->sizes-1] * + se401->height[se401->sizes - 1] * 3; + + rc = se401_sndctrl(0, se401, SE401_REQ_GET_WIDTH, 0, cp, sizeof(cp)); + se401->cwidth = cp[0]+cp[1]*256; + rc = se401_sndctrl(0, se401, SE401_REQ_GET_HEIGHT, 0, cp, sizeof(cp)); + se401->cheight = cp[0]+cp[1]*256; + + if (!(cp[2] & SE401_FORMAT_BAYER)) { + err("Bayer format not supported!"); + return 1; + } + /* set output mode (BAYER) */ + se401_sndctrl(1, se401, SE401_REQ_SET_OUTPUT_MODE, + SE401_FORMAT_BAYER, NULL, 0); + + rc = se401_sndctrl(0, se401, SE401_REQ_GET_BRT, 0, cp, sizeof(cp)); + se401->brightness = cp[0]+cp[1]*256; + /* some default values */ + se401->resetlevel = 0x2d; + se401->rgain = 0x20; + se401->ggain = 0x20; + se401->bgain = 0x20; + se401_set_exposure(se401, 20000); + se401->palette = VIDEO_PALETTE_RGB24; + se401->enhance = 1; + se401->dropped = 0; + se401->error = 0; + se401->framecount = 0; + se401->readcount = 0; + + /* Start interrupt transfers for snapshot button */ + if (button) { + se401->inturb = usb_alloc_urb(0, GFP_KERNEL); + if (!se401->inturb) { + dev_info(&se401->dev->dev, + "Allocation of inturb failed\n"); + return 1; + } + usb_fill_int_urb(se401->inturb, se401->dev, + usb_rcvintpipe(se401->dev, SE401_BUTTON_ENDPOINT), + &se401->button, sizeof(se401->button), + se401_button_irq, + se401, + 8 + ); + if (usb_submit_urb(se401->inturb, GFP_KERNEL)) { + dev_info(&se401->dev->dev, "int urb burned down\n"); + return 1; + } + } else + se401->inturb = NULL; + + /* Flash the led */ + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 1, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_CAMERA_POWER, 0, NULL, 0); + se401_sndctrl(1, se401, SE401_REQ_LED_CONTROL, 0, NULL, 0); + + return 0; +} + +static int se401_probe(struct usb_interface *intf, + const struct usb_device_id *id) +{ + struct usb_device *dev = interface_to_usbdev(intf); + struct usb_interface_descriptor *interface; + struct usb_se401 *se401; + char *camera_name = NULL; + int button = 1; + + /* We don't handle multi-config cameras */ + if (dev->descriptor.bNumConfigurations != 1) + return -ENODEV; + + interface = &intf->cur_altsetting->desc; + + /* Is it an se401? */ + if (le16_to_cpu(dev->descriptor.idVendor) == 0x03e8 && + le16_to_cpu(dev->descriptor.idProduct) == 0x0004) { + camera_name = "Endpoints/Aox SE401"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x0471 && + le16_to_cpu(dev->descriptor.idProduct) == 0x030b) { + camera_name = "Philips PCVC665K"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5001) { + camera_name = "Kensington VideoCAM 67014"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5002) { + camera_name = "Kensington VideoCAM 6701(5/7)"; + } else if (le16_to_cpu(dev->descriptor.idVendor) == 0x047d && + le16_to_cpu(dev->descriptor.idProduct) == 0x5003) { + camera_name = "Kensington VideoCAM 67016"; + button = 0; + } else + return -ENODEV; + + /* Checking vendor/product should be enough, but what the hell */ + if (interface->bInterfaceClass != 0x00) + return -ENODEV; + if (interface->bInterfaceSubClass != 0x00) + return -ENODEV; + + /* We found one */ + dev_info(&intf->dev, "SE401 camera found: %s\n", camera_name); + + se401 = kzalloc(sizeof(*se401), GFP_KERNEL); + if (se401 == NULL) { + err("couldn't kmalloc se401 struct"); + return -ENOMEM; + } + + se401->dev = dev; + se401->iface = interface->bInterfaceNumber; + se401->camera_name = camera_name; + + dev_info(&intf->dev, "firmware version: %02x\n", + le16_to_cpu(dev->descriptor.bcdDevice) & 255); + + if (se401_init(se401, button)) { + kfree(se401); + return -EIO; + } + + memcpy(&se401->vdev, &se401_template, sizeof(se401_template)); + memcpy(se401->vdev.name, se401->camera_name, + strlen(se401->camera_name)); + init_waitqueue_head(&se401->wq); + mutex_init(&se401->lock); + wmb(); + + if (video_register_device(&se401->vdev, + VFL_TYPE_GRABBER, video_nr) < 0) { + kfree(se401); + err("video_register_device failed"); + return -EIO; + } + dev_info(&intf->dev, "registered new video device: %s\n", + video_device_node_name(&se401->vdev)); + + usb_set_intfdata(intf, se401); + return 0; +} + +static void se401_disconnect(struct usb_interface *intf) +{ + struct usb_se401 *se401 = usb_get_intfdata(intf); + + usb_set_intfdata(intf, NULL); + if (se401) { + video_unregister_device(&se401->vdev); + if (!se401->user) + usb_se401_remove_disconnected(se401); + else { + se401->frame[0].grabstate = FRAME_ERROR; + se401->frame[0].grabstate = FRAME_ERROR; + + se401->streaming = 0; + + wake_up_interruptible(&se401->wq); + se401->removed = 1; + } + } +} + +static struct usb_driver se401_driver = { + .name = "se401", + .id_table = device_table, + .probe = se401_probe, + .disconnect = se401_disconnect, +}; + + + +/**************************************************************************** + * + * Module routines + * + ***************************************************************************/ + +static int __init usb_se401_init(void) +{ + printk(KERN_INFO "SE401 usb camera driver version %s registering\n", + version); + if (flickerless) + if (flickerless != 50 && flickerless != 60) { + printk(KERN_ERR "Invallid flickerless value, use 0, 50 or 60.\n"); + return -1; + } + return usb_register(&se401_driver); +} + +static void __exit usb_se401_exit(void) +{ + usb_deregister(&se401_driver); + printk(KERN_INFO "SE401 driver deregistered\frame"); +} + +module_init(usb_se401_init); +module_exit(usb_se401_exit); diff --git a/drivers/staging/se401/se401.h b/drivers/staging/se401/se401.h new file mode 100644 index 000000000000..bf7d2e9765b0 --- /dev/null +++ b/drivers/staging/se401/se401.h @@ -0,0 +1,236 @@ + +#ifndef __LINUX_se401_H +#define __LINUX_se401_H + +#include +#include +#include +#include +#include + +#define se401_DEBUG /* Turn on debug messages */ + +#ifdef se401_DEBUG +# define PDEBUG(level, fmt, args...) \ +if (debug >= level) \ + info("[" __PRETTY_FUNCTION__ ":%d] " fmt, __LINE__ , ## args) +#else +# define PDEBUG(level, fmt, args...) do {} while (0) +#endif + +/* An almost drop-in replacement for sleep_on_interruptible */ +#define wait_interruptible(test, queue, wait) \ +{ \ + add_wait_queue(queue, wait); \ + set_current_state(TASK_INTERRUPTIBLE); \ + if (test) \ + schedule(); \ + remove_wait_queue(queue, wait); \ + set_current_state(TASK_RUNNING); \ + if (signal_pending(current)) \ + break; \ +} + +#define SE401_REQ_GET_CAMERA_DESCRIPTOR 0x06 +#define SE401_REQ_START_CONTINUOUS_CAPTURE 0x41 +#define SE401_REQ_STOP_CONTINUOUS_CAPTURE 0x42 +#define SE401_REQ_CAPTURE_FRAME 0x43 +#define SE401_REQ_GET_BRT 0x44 +#define SE401_REQ_SET_BRT 0x45 +#define SE401_REQ_GET_WIDTH 0x4c +#define SE401_REQ_SET_WIDTH 0x4d +#define SE401_REQ_GET_HEIGHT 0x4e +#define SE401_REQ_SET_HEIGHT 0x4f +#define SE401_REQ_GET_OUTPUT_MODE 0x50 +#define SE401_REQ_SET_OUTPUT_MODE 0x51 +#define SE401_REQ_GET_EXT_FEATURE 0x52 +#define SE401_REQ_SET_EXT_FEATURE 0x53 +#define SE401_REQ_CAMERA_POWER 0x56 +#define SE401_REQ_LED_CONTROL 0x57 +#define SE401_REQ_BIOS 0xff + +#define SE401_BIOS_READ 0x07 + +#define SE401_FORMAT_BAYER 0x40 + +/* Hyundai hv7131b registers + 7121 and 7141 should be the same (haven't really checked...) */ +/* Mode registers: */ +#define HV7131_REG_MODE_A 0x00 +#define HV7131_REG_MODE_B 0x01 +#define HV7131_REG_MODE_C 0x02 +/* Frame registers: */ +#define HV7131_REG_FRSU 0x10 +#define HV7131_REG_FRSL 0x11 +#define HV7131_REG_FCSU 0x12 +#define HV7131_REG_FCSL 0x13 +#define HV7131_REG_FWHU 0x14 +#define HV7131_REG_FWHL 0x15 +#define HV7131_REG_FWWU 0x16 +#define HV7131_REG_FWWL 0x17 +/* Timing registers: */ +#define HV7131_REG_THBU 0x20 +#define HV7131_REG_THBL 0x21 +#define HV7131_REG_TVBU 0x22 +#define HV7131_REG_TVBL 0x23 +#define HV7131_REG_TITU 0x25 +#define HV7131_REG_TITM 0x26 +#define HV7131_REG_TITL 0x27 +#define HV7131_REG_TMCD 0x28 +/* Adjust Registers: */ +#define HV7131_REG_ARLV 0x30 +#define HV7131_REG_ARCG 0x31 +#define HV7131_REG_AGCG 0x32 +#define HV7131_REG_ABCG 0x33 +#define HV7131_REG_APBV 0x34 +#define HV7131_REG_ASLP 0x54 +/* Offset Registers: */ +#define HV7131_REG_OFSR 0x50 +#define HV7131_REG_OFSG 0x51 +#define HV7131_REG_OFSB 0x52 +/* REset level statistics registers: */ +#define HV7131_REG_LOREFNOH 0x57 +#define HV7131_REG_LOREFNOL 0x58 +#define HV7131_REG_HIREFNOH 0x59 +#define HV7131_REG_HIREFNOL 0x5a + +/* se401 registers */ +#define SE401_OPERATINGMODE 0x2000 + + +/* size of usb transfers */ +#define SE401_PACKETSIZE 4096 +/* number of queued bulk transfers to use, should be about 8 */ +#define SE401_NUMSBUF 1 +/* read the usb specs for this one :) */ +#define SE401_VIDEO_ENDPOINT 1 +#define SE401_BUTTON_ENDPOINT 2 +/* number of frames supported by the v4l part */ +#define SE401_NUMFRAMES 2 +/* scratch buffers for passing data to the decoders */ +#define SE401_NUMSCRATCH 32 +/* maximum amount of data in a JangGu packet */ +#define SE401_VLCDATALEN 1024 +/* number of nul sized packets to receive before kicking the camera */ +#define SE401_MAX_NULLPACKETS 4000 +/* number of decoding errors before kicking the camera */ +#define SE401_MAX_ERRORS 200 + +struct usb_device; + +struct se401_sbuf { + unsigned char *data; +}; + +enum { + FRAME_UNUSED, /* Unused (no MCAPTURE) */ + FRAME_READY, /* Ready to start grabbing */ + FRAME_GRABBING, /* In the process of being grabbed into */ + FRAME_DONE, /* Finished grabbing, but not been synced yet */ + FRAME_ERROR, /* Something bad happened while processing */ +}; + +enum { + FMT_BAYER, + FMT_JANGGU, +}; + +enum { + BUFFER_UNUSED, + BUFFER_READY, + BUFFER_BUSY, + BUFFER_DONE, +}; + +struct se401_scratch { + unsigned char *data; + volatile int state; + int offset; + int length; +}; + +struct se401_frame { + unsigned char *data; /* Frame buffer */ + + volatile int grabstate; /* State of grabbing */ + + unsigned char *curline; + int curlinepix; + int curpix; +}; + +struct usb_se401 { + struct video_device vdev; + + /* Device structure */ + struct usb_device *dev; + + unsigned char iface; + + char *camera_name; + + int change; + int brightness; + int hue; + int rgain; + int ggain; + int bgain; + int expose_h; + int expose_m; + int expose_l; + int resetlevel; + + int enhance; + + int format; + int sizes; + int *width; + int *height; + int cwidth; /* current width */ + int cheight; /* current height */ + int palette; + int maxframesize; + int cframesize; /* current framesize */ + + struct mutex lock; + int user; /* user count for exclusive use */ + int removed; /* device disconnected */ + + int streaming; /* Are we streaming video? */ + + char *fbuf; /* Videodev buffer area */ + + struct urb *urb[SE401_NUMSBUF]; + struct urb *inturb; + + int button; + int buttonpressed; + + int curframe; /* Current receiving frame */ + struct se401_frame frame[SE401_NUMFRAMES]; + int readcount; + int framecount; + int error; + int dropped; + + int scratch_next; + int scratch_use; + int scratch_overflow; + struct se401_scratch scratch[SE401_NUMSCRATCH]; + + /* Decoder specific data: */ + unsigned char vlcdata[SE401_VLCDATALEN]; + int vlcdatapos; + int bayeroffset; + + struct se401_sbuf sbuf[SE401_NUMSBUF]; + + wait_queue_head_t wq; /* Processes waiting */ + + int nullpackets; +}; + + + +#endif + -- cgit v1.2.3 From 58c66df3e38ffb1d59cc5162bb9e07c859288034 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Mon, 27 Dec 2010 07:38:48 -0300 Subject: [media] Remove VIDEO_V4L1 Kconfig option There's no sense on keeping VIDEO_V4L1 Kconfig option just because of two deprecated drivers moved to staging scheduled to die on 2.6.39. Reviewed-by: Hans Verkuil Signed-off-by: Mauro Carvalho Chehab --- drivers/media/Kconfig | 14 -------------- drivers/media/video/Kconfig | 5 ----- drivers/staging/se401/Kconfig | 2 +- drivers/staging/usbvideo/Kconfig | 2 +- include/linux/videodev.h | 4 ---- 5 files changed, 2 insertions(+), 25 deletions(-) (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/Kconfig b/drivers/media/Kconfig index 9ea1a6d70f0a..147c92b9b4f6 100644 --- a/drivers/media/Kconfig +++ b/drivers/media/Kconfig @@ -40,20 +40,6 @@ config VIDEO_V4L2_COMMON depends on (I2C || I2C=n) && VIDEO_DEV default (I2C || I2C=n) && VIDEO_DEV -config VIDEO_ALLOW_V4L1 - bool "Enable Video For Linux API 1 (DEPRECATED)" - depends on VIDEO_DEV && VIDEO_V4L2_COMMON - default VIDEO_DEV && VIDEO_V4L2_COMMON - ---help--- - Enables drivers based on the legacy V4L1 API. - - This api were developed to be used at Kernel 2.2 and 2.4, but - lacks support for several video standards. There are several - drivers at kernel that still depends on it. - - If you are unsure as to whether this is required, answer Y. - - # # DVB Core # diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index b3bf04368bf2..78b8c5de1f6b 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -7,11 +7,6 @@ config VIDEO_V4L2 depends on VIDEO_DEV && VIDEO_V4L2_COMMON default VIDEO_DEV && VIDEO_V4L2_COMMON -config VIDEO_V4L1 - tristate - depends on VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 - default VIDEO_DEV && VIDEO_V4L2_COMMON && VIDEO_ALLOW_V4L1 - config VIDEOBUF_GEN tristate diff --git a/drivers/staging/se401/Kconfig b/drivers/staging/se401/Kconfig index 586fc0432a4b..ee7313e66c79 100644 --- a/drivers/staging/se401/Kconfig +++ b/drivers/staging/se401/Kconfig @@ -1,6 +1,6 @@ config USB_SE401 tristate "USB SE401 Camera support (DEPRECATED)" - depends on VIDEO_V4L1 + depends on VIDEO_DEV && VIDEO_V4L2_COMMON ---help--- Say Y here if you want to connect this type of camera to your computer's USB port. See diff --git a/drivers/staging/usbvideo/Kconfig b/drivers/staging/usbvideo/Kconfig index aae863e8a902..d9fc867d09eb 100644 --- a/drivers/staging/usbvideo/Kconfig +++ b/drivers/staging/usbvideo/Kconfig @@ -3,7 +3,7 @@ config VIDEO_USBVIDEO config USB_VICAM tristate "USB 3com HomeConnect (aka vicam) support (DEPRECATED)" - depends on VIDEO_V4L1 && EXPERIMENTAL + depends on VIDEO_DEV && VIDEO_V4L2_COMMON select VIDEO_USBVIDEO ---help--- Say Y here if you have 3com homeconnect camera (vicam). diff --git a/include/linux/videodev.h b/include/linux/videodev.h index 8a7aead76a38..f11efbef1c05 100644 --- a/include/linux/videodev.h +++ b/include/linux/videodev.h @@ -16,8 +16,6 @@ #include #include -#if defined(CONFIG_VIDEO_V4L1) || defined(CONFIG_VIDEO_V4L1_MODULE) || !defined(__KERNEL__) - #define VID_TYPE_CAPTURE 1 /* Can capture */ #define VID_TYPE_TUNER 2 /* Can tune */ #define VID_TYPE_TELETEXT 4 /* Does teletext */ @@ -311,8 +309,6 @@ struct video_code #define VID_PLAY_RESET 13 #define VID_PLAY_END_MARK 14 -#endif /* CONFIG_VIDEO_V4L1 */ - #endif /* __LINUX_VIDEODEV_H */ /* -- cgit v1.2.3 From 3153ac9c6208892ee237caccdbe2290f8247e236 Mon Sep 17 00:00:00 2001 From: Alberto Panizzo Date: Thu, 2 Dec 2010 11:53:28 -0300 Subject: [media] V4L2: Add a v4l2-subdev (soc-camera) driver for OmniVision OV2640 sensor Signed-off-by: Alberto Panizzo Signed-off-by: Guennadi Liakhovetski Signed-off-by: Mauro Carvalho Chehab --- drivers/media/video/Kconfig | 6 + drivers/media/video/Makefile | 1 + drivers/media/video/ov2640.c | 1205 +++++++++++++++++++++++++++++++++++++++ include/media/v4l2-chip-ident.h | 1 + 4 files changed, 1213 insertions(+) create mode 100644 drivers/media/video/ov2640.c (limited to 'drivers/media/video/Kconfig') diff --git a/drivers/media/video/Kconfig b/drivers/media/video/Kconfig index 78b8c5de1f6b..eb875af05e79 100644 --- a/drivers/media/video/Kconfig +++ b/drivers/media/video/Kconfig @@ -794,6 +794,12 @@ config SOC_CAMERA_PLATFORM help This is a generic SoC camera platform driver, useful for testing +config SOC_CAMERA_OV2640 + tristate "ov2640 camera support" + depends on SOC_CAMERA && I2C + help + This is a ov2640 camera driver + config SOC_CAMERA_OV6650 tristate "ov6650 sensor support" depends on SOC_CAMERA && I2C diff --git a/drivers/media/video/Makefile b/drivers/media/video/Makefile index 98825e7d8ca4..81e38cb0b846 100644 --- a/drivers/media/video/Makefile +++ b/drivers/media/video/Makefile @@ -75,6 +75,7 @@ obj-$(CONFIG_SOC_CAMERA_MT9M111) += mt9m111.o obj-$(CONFIG_SOC_CAMERA_MT9T031) += mt9t031.o obj-$(CONFIG_SOC_CAMERA_MT9T112) += mt9t112.o obj-$(CONFIG_SOC_CAMERA_MT9V022) += mt9v022.o +obj-$(CONFIG_SOC_CAMERA_OV2640) += ov2640.o obj-$(CONFIG_SOC_CAMERA_OV6650) += ov6650.o obj-$(CONFIG_SOC_CAMERA_OV772X) += ov772x.o obj-$(CONFIG_SOC_CAMERA_OV9640) += ov9640.o diff --git a/drivers/media/video/ov2640.c b/drivers/media/video/ov2640.c new file mode 100644 index 000000000000..0cea0cf36679 --- /dev/null +++ b/drivers/media/video/ov2640.c @@ -0,0 +1,1205 @@ +/* + * ov2640 Camera Driver + * + * Copyright (C) 2010 Alberto Panizzo + * + * Based on ov772x, ov9640 drivers and previous non merged implementations. + * + * Copyright 2005-2009 Freescale Semiconductor, Inc. All Rights Reserved. + * Copyright (C) 2006, OmniVision + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define VAL_SET(x, mask, rshift, lshift) \ + ((((x) >> rshift) & mask) << lshift) +/* + * DSP registers + * register offset for BANK_SEL == BANK_SEL_DSP + */ +#define R_BYPASS 0x05 /* Bypass DSP */ +#define R_BYPASS_DSP_BYPAS 0x01 /* Bypass DSP, sensor out directly */ +#define R_BYPASS_USE_DSP 0x00 /* Use the internal DSP */ +#define QS 0x44 /* Quantization Scale Factor */ +#define CTRLI 0x50 +#define CTRLI_LP_DP 0x80 +#define CTRLI_ROUND 0x40 +#define CTRLI_V_DIV_SET(x) VAL_SET(x, 0x3, 0, 3) +#define CTRLI_H_DIV_SET(x) VAL_SET(x, 0x3, 0, 0) +#define HSIZE 0x51 /* H_SIZE[7:0] (real/4) */ +#define HSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) +#define VSIZE 0x52 /* V_SIZE[7:0] (real/4) */ +#define VSIZE_SET(x) VAL_SET(x, 0xFF, 2, 0) +#define XOFFL 0x53 /* OFFSET_X[7:0] */ +#define XOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) +#define YOFFL 0x54 /* OFFSET_Y[7:0] */ +#define YOFFL_SET(x) VAL_SET(x, 0xFF, 0, 0) +#define VHYX 0x55 /* Offset and size completion */ +#define VHYX_VSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 7) +#define VHYX_HSIZE_SET(x) VAL_SET(x, 0x1, (8+2), 3) +#define VHYX_YOFF_SET(x) VAL_SET(x, 0x3, 8, 4) +#define VHYX_XOFF_SET(x) VAL_SET(x, 0x3, 8, 0) +#define DPRP 0x56 +#define TEST 0x57 /* Horizontal size completion */ +#define TEST_HSIZE_SET(x) VAL_SET(x, 0x1, (9+2), 7) +#define ZMOW 0x5A /* Zoom: Out Width OUTW[7:0] (real/4) */ +#define ZMOW_OUTW_SET(x) VAL_SET(x, 0xFF, 2, 0) +#define ZMOH 0x5B /* Zoom: Out Height OUTH[7:0] (real/4) */ +#define ZMOH_OUTH_SET(x) VAL_SET(x, 0xFF, 2, 0) +#define ZMHH 0x5C /* Zoom: Speed and H&W completion */ +#define ZMHH_ZSPEED_SET(x) VAL_SET(x, 0x0F, 0, 4) +#define ZMHH_OUTH_SET(x) VAL_SET(x, 0x1, (8+2), 2) +#define ZMHH_OUTW_SET(x) VAL_SET(x, 0x3, (8+2), 0) +#define BPADDR 0x7C /* SDE Indirect Register Access: Address */ +#define BPDATA 0x7D /* SDE Indirect Register Access: Data */ +#define CTRL2 0x86 /* DSP Module enable 2 */ +#define CTRL2_DCW_EN 0x20 +#define CTRL2_SDE_EN 0x10 +#define CTRL2_UV_ADJ_EN 0x08 +#define CTRL2_UV_AVG_EN 0x04 +#define CTRL2_CMX_EN 0x01 +#define CTRL3 0x87 /* DSP Module enable 3 */ +#define CTRL3_BPC_EN 0x80 +#define CTRL3_WPC_EN 0x40 +#define SIZEL 0x8C /* Image Size Completion */ +#define SIZEL_HSIZE8_11_SET(x) VAL_SET(x, 0x1, 11, 6) +#define SIZEL_HSIZE8_SET(x) VAL_SET(x, 0x7, 0, 3) +#define SIZEL_VSIZE8_SET(x) VAL_SET(x, 0x7, 0, 0) +#define HSIZE8 0xC0 /* Image Horizontal Size HSIZE[10:3] */ +#define HSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) +#define VSIZE8 0xC1 /* Image Vertical Size VSIZE[10:3] */ +#define VSIZE8_SET(x) VAL_SET(x, 0xFF, 3, 0) +#define CTRL0 0xC2 /* DSP Module enable 0 */ +#define CTRL0_AEC_EN 0x80 +#define CTRL0_AEC_SEL 0x40 +#define CTRL0_STAT_SEL 0x20 +#define CTRL0_VFIRST 0x10 +#define CTRL0_YUV422 0x08 +#define CTRL0_YUV_EN 0x04 +#define CTRL0_RGB_EN 0x02 +#define CTRL0_RAW_EN 0x01 +#define CTRL1 0xC3 /* DSP Module enable 1 */ +#define CTRL1_CIP 0x80 +#define CTRL1_DMY 0x40 +#define CTRL1_RAW_GMA 0x20 +#define CTRL1_DG 0x10 +#define CTRL1_AWB 0x08 +#define CTRL1_AWB_GAIN 0x04 +#define CTRL1_LENC 0x02 +#define CTRL1_PRE 0x01 +#define R_DVP_SP 0xD3 /* DVP output speed control */ +#define R_DVP_SP_AUTO_MODE 0x80 +#define R_DVP_SP_DVP_MASK 0x3F /* DVP PCLK = sysclk (48)/[6:0] (YUV0); + * = sysclk (48)/(2*[6:0]) (RAW);*/ +#define IMAGE_MODE 0xDA /* Image Output Format Select */ +#define IMAGE_MODE_Y8_DVP_EN 0x40 +#define IMAGE_MODE_JPEG_EN 0x10 +#define IMAGE_MODE_YUV422 0x00 +#define IMAGE_MODE_RAW10 0x04 /* (DVP) */ +#define IMAGE_MODE_RGB565 0x08 +#define IMAGE_MODE_HREF_VSYNC 0x02 /* HREF timing select in DVP JPEG output + * mode (0 for HREF is same as sensor) */ +#define IMAGE_MODE_LBYTE_FIRST 0x01 /* Byte swap enable for DVP + * 1: Low byte first UYVY (C2[4] =0) + * VYUY (C2[4] =1) + * 0: High byte first YUYV (C2[4]=0) + * YVYU (C2[4] = 1) */ +#define RESET 0xE0 /* Reset */ +#define RESET_MICROC 0x40 +#define RESET_SCCB 0x20 +#define RESET_JPEG 0x10 +#define RESET_DVP 0x04 +#define RESET_IPU 0x02 +#define RESET_CIF 0x01 +#define REGED 0xED /* Register ED */ +#define REGED_CLK_OUT_DIS 0x10 +#define MS_SP 0xF0 /* SCCB Master Speed */ +#define SS_ID 0xF7 /* SCCB Slave ID */ +#define SS_CTRL 0xF8 /* SCCB Slave Control */ +#define SS_CTRL_ADD_AUTO_INC 0x20 +#define SS_CTRL_EN 0x08 +#define SS_CTRL_DELAY_CLK 0x04 +#define SS_CTRL_ACC_EN 0x02 +#define SS_CTRL_SEN_PASS_THR 0x01 +#define MC_BIST 0xF9 /* Microcontroller misc register */ +#define MC_BIST_RESET 0x80 /* Microcontroller Reset */ +#define MC_BIST_BOOT_ROM_SEL 0x40 +#define MC_BIST_12KB_SEL 0x20 +#define MC_BIST_12KB_MASK 0x30 +#define MC_BIST_512KB_SEL 0x08 +#define MC_BIST_512KB_MASK 0x0C +#define MC_BIST_BUSY_BIT_R 0x02 +#define MC_BIST_MC_RES_ONE_SH_W 0x02 +#define MC_BIST_LAUNCH 0x01 +#define BANK_SEL 0xFF /* Register Bank Select */ +#define BANK_SEL_DSP 0x00 +#define BANK_SEL_SENS 0x01 + +/* + * Sensor registers + * register offset for BANK_SEL == BANK_SEL_SENS + */ +#define GAIN 0x00 /* AGC - Gain control gain setting */ +#define COM1 0x03 /* Common control 1 */ +#define COM1_1_DUMMY_FR 0x40 +#define COM1_3_DUMMY_FR 0x80 +#define COM1_7_DUMMY_FR 0xC0 +#define COM1_VWIN_LSB_UXGA 0x0F +#define COM1_VWIN_LSB_SVGA 0x0A +#define COM1_VWIN_LSB_CIF 0x06 +#define REG04 0x04 /* Register 04 */ +#define REG04_DEF 0x20 /* Always set */ +#define REG04_HFLIP_IMG 0x80 /* Horizontal mirror image ON/OFF */ +#define REG04_VFLIP_IMG 0x40 /* Vertical flip image ON/OFF */ +#define REG04_VREF_EN 0x10 +#define REG04_HREF_EN 0x08 +#define REG04_AEC_SET(x) VAL_SET(x, 0x3, 0, 0) +#define REG08 0x08 /* Frame Exposure One-pin Control Pre-charge Row Num */ +#define COM2 0x09 /* Common control 2 */ +#define COM2_SOFT_SLEEP_MODE 0x10 /* Soft sleep mode */ + /* Output drive capability */ +#define COM2_OCAP_Nx_SET(N) (((N) - 1) & 0x03) /* N = [1x .. 4x] */ +#define PID 0x0A /* Product ID Number MSB */ +#define VER 0x0B /* Product ID Number LSB */ +#define COM3 0x0C /* Common control 3 */ +#define COM3_BAND_50H 0x04 /* 0 For Banding at 60H */ +#define COM3_BAND_AUTO 0x02 /* Auto Banding */ +#define COM3_SING_FR_SNAPSH 0x01 /* 0 For enable live video output after the + * snapshot sequence*/ +#define AEC 0x10 /* AEC[9:2] Exposure Value */ +#define CLKRC 0x11 /* Internal clock */ +#define CLKRC_EN 0x80 +#define CLKRC_DIV_SET(x) (((x) - 1) & 0x1F) /* CLK = XVCLK/(x) */ +#define COM7 0x12 /* Common control 7 */ +#define COM7_SRST 0x80 /* Initiates system reset. All registers are + * set to factory default values after which + * the chip resumes normal operation */ +#define COM7_RES_UXGA 0x00 /* Resolution selectors for UXGA */ +#define COM7_RES_SVGA 0x40 /* SVGA */ +#define COM7_RES_CIF 0x20 /* CIF */ +#define COM7_ZOOM_EN 0x04 /* Enable Zoom mode */ +#define COM7_COLOR_BAR_TEST 0x02 /* Enable Color Bar Test Pattern */ +#define COM8 0x13 /* Common control 8 */ +#define COM8_DEF 0xC0 /* Banding filter ON/OFF */ +#define COM8_BNDF_EN 0x20 /* Banding filter ON/OFF */ +#define COM8_AGC_EN 0x04 /* AGC Auto/Manual control selection */ +#define COM8_AEC_EN 0x01 /* Auto/Manual Exposure control */ +#define COM9 0x14 /* Common control 9 + * Automatic gain ceiling - maximum AGC value [7:5]*/ +#define COM9_AGC_GAIN_2x 0x00 /* 000 : 2x */ +#define COM9_AGC_GAIN_4x 0x20 /* 001 : 4x */ +#define COM9_AGC_GAIN_8x 0x40 /* 010 : 8x */ +#define COM9_AGC_GAIN_16x 0x60 /* 011 : 16x */ +#define COM9_AGC_GAIN_32x 0x80 /* 100 : 32x */ +#define COM9_AGC_GAIN_64x 0xA0 /* 101 : 64x */ +#define COM9_AGC_GAIN_128x 0xC0 /* 110 : 128x */ +#define COM10 0x15 /* Common control 10 */ +#define COM10_PCLK_HREF 0x20 /* PCLK output qualified by HREF */ +#define COM10_PCLK_RISE 0x10 /* Data is updated at the rising edge of + * PCLK (user can latch data at the next + * falling edge of PCLK). + * 0 otherwise. */ +#define COM10_HREF_INV 0x08 /* Invert HREF polarity: + * HREF negative for valid data*/ +#define COM10_VSINC_INV 0x02 /* Invert VSYNC polarity */ +#define HSTART 0x17 /* Horizontal Window start MSB 8 bit */ +#define HEND 0x18 /* Horizontal Window end MSB 8 bit */ +#define VSTART 0x19 /* Vertical Window start MSB 8 bit */ +#define VEND 0x1A /* Vertical Window end MSB 8 bit */ +#define MIDH 0x1C /* Manufacturer ID byte - high */ +#define MIDL 0x1D /* Manufacturer ID byte - low */ +#define AEW 0x24 /* AGC/AEC - Stable operating region (upper limit) */ +#define AEB 0x25 /* AGC/AEC - Stable operating region (lower limit) */ +#define VV 0x26 /* AGC/AEC Fast mode operating region */ +#define VV_HIGH_TH_SET(x) VAL_SET(x, 0xF, 0, 4) +#define VV_LOW_TH_SET(x) VAL_SET(x, 0xF, 0, 0) +#define REG2A 0x2A /* Dummy pixel insert MSB */ +#define FRARL 0x2B /* Dummy pixel insert LSB */ +#define ADDVFL 0x2D /* LSB of insert dummy lines in Vertical direction */ +#define ADDVFH 0x2E /* MSB of insert dummy lines in Vertical direction */ +#define YAVG 0x2F /* Y/G Channel Average value */ +#define REG32 0x32 /* Common Control 32 */ +#define REG32_PCLK_DIV_2 0x80 /* PCLK freq divided by 2 */ +#define REG32_PCLK_DIV_4 0xC0 /* PCLK freq divided by 4 */ +#define ARCOM2 0x34 /* Zoom: Horizontal start point */ +#define REG45 0x45 /* Register 45 */ +#define FLL 0x46 /* Frame Length Adjustment LSBs */ +#define FLH 0x47 /* Frame Length Adjustment MSBs */ +#define COM19 0x48 /* Zoom: Vertical start point */ +#define ZOOMS 0x49 /* Zoom: Vertical start point */ +#define COM22 0x4B /* Flash light control */ +#define COM25 0x4E /* For Banding operations */ +#define BD50 0x4F /* 50Hz Banding AEC 8 LSBs */ +#define BD60 0x50 /* 60Hz Banding AEC 8 LSBs */ +#define REG5D 0x5D /* AVGsel[7:0], 16-zone average weight option */ +#define REG5E 0x5E /* AVGsel[15:8], 16-zone average weight option */ +#define REG5F 0x5F /* AVGsel[23:16], 16-zone average weight option */ +#define REG60 0x60 /* AVGsel[31:24], 16-zone average weight option */ +#define HISTO_LOW 0x61 /* Histogram Algorithm Low Level */ +#define HISTO_HIGH 0x62 /* Histogram Algorithm High Level */ + +/* + * ID + */ +#define MANUFACTURER_ID 0x7FA2 +#define PID_OV2640 0x2642 +#define VERSION(pid, ver) ((pid << 8) | (ver & 0xFF)) + +/* + * Struct + */ +struct regval_list { + u8 reg_num; + u8 value; +}; + +/* Supported resolutions */ +enum ov2640_width { + W_QCIF = 176, + W_QVGA = 320, + W_CIF = 352, + W_VGA = 640, + W_SVGA = 800, + W_XGA = 1024, + W_SXGA = 1280, + W_UXGA = 1600, +}; + +enum ov2640_height { + H_QCIF = 144, + H_QVGA = 240, + H_CIF = 288, + H_VGA = 480, + H_SVGA = 600, + H_XGA = 768, + H_SXGA = 1024, + H_UXGA = 1200, +}; + +struct ov2640_win_size { + char *name; + enum ov2640_width width; + enum ov2640_height height; + const struct regval_list *regs; +}; + + +struct ov2640_priv { + struct v4l2_subdev subdev; + struct ov2640_camera_info *info; + enum v4l2_mbus_pixelcode cfmt_code; + const struct ov2640_win_size *win; + int model; + u16 flag_vflip:1; + u16 flag_hflip:1; +}; + +/* + * Registers settings + */ + +#define ENDMARKER { 0xff, 0xff } + +static const struct regval_list ov2640_init_regs[] = { + { BANK_SEL, BANK_SEL_DSP }, + { 0x2c, 0xff }, + { 0x2e, 0xdf }, + { BANK_SEL, BANK_SEL_SENS }, + { 0x3c, 0x32 }, + { CLKRC, CLKRC_DIV_SET(1) }, + { COM2, COM2_OCAP_Nx_SET(3) }, + { REG04, REG04_DEF | REG04_HREF_EN }, + { COM8, COM8_DEF | COM8_BNDF_EN | COM8_AGC_EN | COM8_AEC_EN }, + { COM9, COM9_AGC_GAIN_8x | 0x08}, + { 0x2c, 0x0c }, + { 0x33, 0x78 }, + { 0x3a, 0x33 }, + { 0x3b, 0xfb }, + { 0x3e, 0x00 }, + { 0x43, 0x11 }, + { 0x16, 0x10 }, + { 0x39, 0x02 }, + { 0x35, 0x88 }, + { 0x22, 0x0a }, + { 0x37, 0x40 }, + { 0x23, 0x00 }, + { ARCOM2, 0xa0 }, + { 0x06, 0x02 }, + { 0x06, 0x88 }, + { 0x07, 0xc0 }, + { 0x0d, 0xb7 }, + { 0x0e, 0x01 }, + { 0x4c, 0x00 }, + { 0x4a, 0x81 }, + { 0x21, 0x99 }, + { AEW, 0x40 }, + { AEB, 0x38 }, + { VV, VV_HIGH_TH_SET(0x08) | VV_LOW_TH_SET(0x02) }, + { 0x5c, 0x00 }, + { 0x63, 0x00 }, + { FLL, 0x22 }, + { COM3, 0x38 | COM3_BAND_AUTO }, + { REG5D, 0x55 }, + { REG5E, 0x7d }, + { REG5F, 0x7d }, + { REG60, 0x55 }, + { HISTO_LOW, 0x70 }, + { HISTO_HIGH, 0x80 }, + { 0x7c, 0x05 }, + { 0x20, 0x80 }, + { 0x28, 0x30 }, + { 0x6c, 0x00 }, + { 0x6d, 0x80 }, + { 0x6e, 0x00 }, + { 0x70, 0x02 }, + { 0x71, 0x94 }, + { 0x73, 0xc1 }, + { 0x3d, 0x34 }, + { COM7, COM7_RES_UXGA | COM7_ZOOM_EN }, + { 0x5a, 0x57 }, + { BD50, 0xbb }, + { BD60, 0x9c }, + { BANK_SEL, BANK_SEL_DSP }, + { 0xe5, 0x7f }, + { MC_BIST, MC_BIST_RESET | MC_BIST_BOOT_ROM_SEL }, + { 0x41, 0x24 }, + { RESET, RESET_JPEG | RESET_DVP }, + { 0x76, 0xff }, + { 0x33, 0xa0 }, + { 0x42, 0x20 }, + { 0x43, 0x18 }, + { 0x4c, 0x00 }, + { CTRL3, CTRL3_BPC_EN | CTRL3_WPC_EN | 0x10 }, + { 0x88, 0x3f }, + { 0xd7, 0x03 }, + { 0xd9, 0x10 }, + { R_DVP_SP , R_DVP_SP_AUTO_MODE | 0x2 }, + { 0xc8, 0x08 }, + { 0xc9, 0x80 }, + { BPADDR, 0x00 }, + { BPDATA, 0x00 }, + { BPADDR, 0x03 }, + { BPDATA, 0x48 }, + { BPDATA, 0x48 }, + { BPADDR, 0x08 }, + { BPDATA, 0x20 }, + { BPDATA, 0x10 }, + { BPDATA, 0x0e }, + { 0x90, 0x00 }, + { 0x91, 0x0e }, + { 0x91, 0x1a }, + { 0x91, 0x31 }, + { 0x91, 0x5a }, + { 0x91, 0x69 }, + { 0x91, 0x75 }, + { 0x91, 0x7e }, + { 0x91, 0x88 }, + { 0x91, 0x8f }, + { 0x91, 0x96 }, + { 0x91, 0xa3 }, + { 0x91, 0xaf }, + { 0x91, 0xc4 }, + { 0x91, 0xd7 }, + { 0x91, 0xe8 }, + { 0x91, 0x20 }, + { 0x92, 0x00 }, + { 0x93, 0x06 }, + { 0x93, 0xe3 }, + { 0x93, 0x03 }, + { 0x93, 0x03 }, + { 0x93, 0x00 }, + { 0x93, 0x02 }, + { 0x93, 0x00 }, + { 0x93, 0x00 }, + { 0x93, 0x00 }, + { 0x93, 0x00 }, + { 0x93, 0x00 }, + { 0x93, 0x00 }, + { 0x93, 0x00 }, + { 0x96, 0x00 }, + { 0x97, 0x08 }, + { 0x97, 0x19 }, + { 0x97, 0x02 }, + { 0x97, 0x0c }, + { 0x97, 0x24 }, + { 0x97, 0x30 }, + { 0x97, 0x28 }, + { 0x97, 0x26 }, + { 0x97, 0x02 }, + { 0x97, 0x98 }, + { 0x97, 0x80 }, + { 0x97, 0x00 }, + { 0x97, 0x00 }, + { 0xa4, 0x00 }, + { 0xa8, 0x00 }, + { 0xc5, 0x11 }, + { 0xc6, 0x51 }, + { 0xbf, 0x80 }, + { 0xc7, 0x10 }, + { 0xb6, 0x66 }, + { 0xb8, 0xA5 }, + { 0xb7, 0x64 }, + { 0xb9, 0x7C }, + { 0xb3, 0xaf }, + { 0xb4, 0x97 }, + { 0xb5, 0xFF }, + { 0xb0, 0xC5 }, + { 0xb1, 0x94 }, + { 0xb2, 0x0f }, + { 0xc4, 0x5c }, + { 0xa6, 0x00 }, + { 0xa7, 0x20 }, + { 0xa7, 0xd8 }, + { 0xa7, 0x1b }, + { 0xa7, 0x31 }, + { 0xa7, 0x00 }, + { 0xa7, 0x18 }, + { 0xa7, 0x20 }, + { 0xa7, 0xd8 }, + { 0xa7, 0x19 }, + { 0xa7, 0x31 }, + { 0xa7, 0x00 }, + { 0xa7, 0x18 }, + { 0xa7, 0x20 }, + { 0xa7, 0xd8 }, + { 0xa7, 0x19 }, + { 0xa7, 0x31 }, + { 0xa7, 0x00 }, + { 0xa7, 0x18 }, + { 0x7f, 0x00 }, + { 0xe5, 0x1f }, + { 0xe1, 0x77 }, + { 0xdd, 0x7f }, + { CTRL0, CTRL0_YUV422 | CTRL0_YUV_EN | CTRL0_RGB_EN }, + ENDMARKER, +}; + +/* + * Register settings for window size + * The preamble, setup the internal DSP to input an UXGA (1600x1200) image. + * Then the different zooming configurations will setup the output image size. + */ +static const struct regval_list ov2640_size_change_preamble_regs[] = { + { BANK_SEL, BANK_SEL_DSP }, + { RESET, RESET_DVP }, + { HSIZE8, HSIZE8_SET(W_UXGA) }, + { VSIZE8, VSIZE8_SET(H_UXGA) }, + { CTRL2, CTRL2_DCW_EN | CTRL2_SDE_EN | + CTRL2_UV_AVG_EN | CTRL2_CMX_EN | CTRL2_UV_ADJ_EN }, + { HSIZE, HSIZE_SET(W_UXGA) }, + { VSIZE, VSIZE_SET(H_UXGA) }, + { XOFFL, XOFFL_SET(0) }, + { YOFFL, YOFFL_SET(0) }, + { VHYX, VHYX_HSIZE_SET(W_UXGA) | VHYX_VSIZE_SET(H_UXGA) | + VHYX_XOFF_SET(0) | VHYX_YOFF_SET(0)}, + { TEST, TEST_HSIZE_SET(W_UXGA) }, + ENDMARKER, +}; + +#define PER_SIZE_REG_SEQ(x, y, v_div, h_div, pclk_div) \ + { CTRLI, CTRLI_LP_DP | CTRLI_V_DIV_SET(v_div) | \ + CTRLI_H_DIV_SET(h_div)}, \ + { ZMOW, ZMOW_OUTW_SET(x) }, \ + { ZMOH, ZMOH_OUTH_SET(y) }, \ + { ZMHH, ZMHH_OUTW_SET(x) | ZMHH_OUTH_SET(y) }, \ + { R_DVP_SP, pclk_div }, \ + { RESET, 0x00} + +static const struct regval_list ov2640_qcif_regs[] = { + PER_SIZE_REG_SEQ(W_QCIF, H_QCIF, 3, 3, 4), + ENDMARKER, +}; + +static const struct regval_list ov2640_qvga_regs[] = { + PER_SIZE_REG_SEQ(W_QVGA, H_QVGA, 2, 2, 4), + ENDMARKER, +}; + +static const struct regval_list ov2640_cif_regs[] = { + PER_SIZE_REG_SEQ(W_CIF, H_CIF, 2, 2, 8), + ENDMARKER, +}; + +static const struct regval_list ov2640_vga_regs[] = { + PER_SIZE_REG_SEQ(W_VGA, H_VGA, 0, 0, 2), + ENDMARKER, +}; + +static const struct regval_list ov2640_svga_regs[] = { + PER_SIZE_REG_SEQ(W_SVGA, H_SVGA, 1, 1, 2), + ENDMARKER, +}; + +static const struct regval_list ov2640_xga_regs[] = { + PER_SIZE_REG_SEQ(W_XGA, H_XGA, 0, 0, 2), + { CTRLI, 0x00}, + ENDMARKER, +}; + +static const struct regval_list ov2640_sxga_regs[] = { + PER_SIZE_REG_SEQ(W_SXGA, H_SXGA, 0, 0, 2), + { CTRLI, 0x00}, + { R_DVP_SP, 2 | R_DVP_SP_AUTO_MODE }, + ENDMARKER, +}; + +static const struct regval_list ov2640_uxga_regs[] = { + PER_SIZE_REG_SEQ(W_UXGA, H_UXGA, 0, 0, 0), + { CTRLI, 0x00}, + { R_DVP_SP, 0 | R_DVP_SP_AUTO_MODE }, + ENDMARKER, +}; + +#define OV2640_SIZE(n, w, h, r) \ + {.name = n, .width = w , .height = h, .regs = r } + +static const struct ov2640_win_size ov2640_supported_win_sizes[] = { + OV2640_SIZE("QCIF", W_QCIF, H_QCIF, ov2640_qcif_regs), + OV2640_SIZE("QVGA", W_QVGA, H_QVGA, ov2640_qvga_regs), + OV2640_SIZE("CIF", W_CIF, H_CIF, ov2640_cif_regs), + OV2640_SIZE("VGA", W_VGA, H_VGA, ov2640_vga_regs), + OV2640_SIZE("SVGA", W_SVGA, H_SVGA, ov2640_svga_regs), + OV2640_SIZE("XGA", W_XGA, H_XGA, ov2640_xga_regs), + OV2640_SIZE("SXGA", W_SXGA, H_SXGA, ov2640_sxga_regs), + OV2640_SIZE("UXGA", W_UXGA, H_UXGA, ov2640_uxga_regs), +}; + +/* + * Register settings for pixel formats + */ +static const struct regval_list ov2640_format_change_preamble_regs[] = { + { BANK_SEL, BANK_SEL_DSP }, + { R_BYPASS, R_BYPASS_USE_DSP }, + ENDMARKER, +}; + +static const struct regval_list ov2640_yuv422_regs[] = { + { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_YUV422 }, + { 0xD7, 0x01 }, + { 0x33, 0xa0 }, + { 0xe1, 0x67 }, + { RESET, 0x00 }, + { R_BYPASS, R_BYPASS_USE_DSP }, + ENDMARKER, +}; + +static const struct regval_list ov2640_rgb565_regs[] = { + { IMAGE_MODE, IMAGE_MODE_LBYTE_FIRST | IMAGE_MODE_RGB565 }, + { 0xd7, 0x03 }, + { RESET, 0x00 }, + { R_BYPASS, R_BYPASS_USE_DSP }, + ENDMARKER, +}; + +static enum v4l2_mbus_pixelcode ov2640_codes[] = { + V4L2_MBUS_FMT_UYVY8_2X8, + V4L2_MBUS_FMT_RGB565_2X8_LE, +}; + +/* + * Supported controls + */ +static const struct v4l2_queryctrl ov2640_controls[] = { + { + .id = V4L2_CID_VFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Vertically", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, { + .id = V4L2_CID_HFLIP, + .type = V4L2_CTRL_TYPE_BOOLEAN, + .name = "Flip Horizontally", + .minimum = 0, + .maximum = 1, + .step = 1, + .default_value = 0, + }, +}; + +/* + * General functions + */ +static struct ov2640_priv *to_ov2640(const struct i2c_client *client) +{ + return container_of(i2c_get_clientdata(client), struct ov2640_priv, + subdev); +} + +static int ov2640_write_array(struct i2c_client *client, + const struct regval_list *vals) +{ + int ret; + + while ((vals->reg_num != 0xff) || (vals->value != 0xff)) { + ret = i2c_smbus_write_byte_data(client, + vals->reg_num, vals->value); + dev_vdbg(&client->dev, "array: 0x%02x, 0x%02x", + vals->reg_num, vals->value); + + if (ret < 0) + return ret; + vals++; + } + return 0; +} + +static int ov2640_mask_set(struct i2c_client *client, + u8 reg, u8 mask, u8 set) +{ + s32 val = i2c_smbus_read_byte_data(client, reg); + if (val < 0) + return val; + + val &= ~mask; + val |= set & mask; + + dev_vdbg(&client->dev, "masks: 0x%02x, 0x%02x", reg, val); + + return i2c_smbus_write_byte_data(client, reg, val); +} + +static int ov2640_reset(struct i2c_client *client) +{ + int ret; + const struct regval_list reset_seq[] = { + {BANK_SEL, BANK_SEL_SENS}, + {COM7, COM7_SRST}, + ENDMARKER, + }; + + ret = ov2640_write_array(client, reset_seq); + if (ret) + goto err; + + msleep(5); +err: + dev_dbg(&client->dev, "%s: (ret %d)", __func__, ret); + return ret; +} + +/* + * soc_camera_ops functions + */ +static int ov2640_s_stream(struct v4l2_subdev *sd, int enable) +{ + return 0; +} + +static int ov2640_set_bus_param(struct soc_camera_device *icd, + unsigned long flags) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + unsigned long width_flag = flags & SOCAM_DATAWIDTH_MASK; + + /* Only one width bit may be set */ + if (!is_power_of_2(width_flag)) + return -EINVAL; + + if (icl->set_bus_param) + return icl->set_bus_param(icl, width_flag); + + /* + * Without board specific bus width settings we support only the + * sensors native bus width witch are tested working + */ + if (width_flag & (SOCAM_DATAWIDTH_10 | SOCAM_DATAWIDTH_8)) + return 0; + + return 0; +} + +static unsigned long ov2640_query_bus_param(struct soc_camera_device *icd) +{ + struct soc_camera_link *icl = to_soc_camera_link(icd); + unsigned long flags = SOCAM_PCLK_SAMPLE_RISING | SOCAM_MASTER | + SOCAM_VSYNC_ACTIVE_HIGH | SOCAM_HSYNC_ACTIVE_HIGH | + SOCAM_DATA_ACTIVE_HIGH; + + if (icl->query_bus_param) + flags |= icl->query_bus_param(icl) & SOCAM_DATAWIDTH_MASK; + else + flags |= SOCAM_DATAWIDTH_10; + + return soc_camera_apply_sensor_flags(icl, flags); +} + +static int ov2640_g_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + ctrl->value = priv->flag_vflip; + break; + case V4L2_CID_HFLIP: + ctrl->value = priv->flag_hflip; + break; + } + return 0; +} + +static int ov2640_s_ctrl(struct v4l2_subdev *sd, struct v4l2_control *ctrl) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + int ret = 0; + u8 val; + + switch (ctrl->id) { + case V4L2_CID_VFLIP: + val = ctrl->value ? REG04_VFLIP_IMG : 0x00; + priv->flag_vflip = ctrl->value ? 1 : 0; + ret = ov2640_mask_set(client, REG04, REG04_VFLIP_IMG, val); + break; + case V4L2_CID_HFLIP: + val = ctrl->value ? REG04_HFLIP_IMG : 0x00; + priv->flag_hflip = ctrl->value ? 1 : 0; + ret = ov2640_mask_set(client, REG04, REG04_HFLIP_IMG, val); + break; + } + + return ret; +} + +static int ov2640_g_chip_ident(struct v4l2_subdev *sd, + struct v4l2_dbg_chip_ident *id) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + + id->ident = priv->model; + id->revision = 0; + + return 0; +} + +#ifdef CONFIG_VIDEO_ADV_DEBUG +static int ov2640_g_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + reg->size = 1; + if (reg->reg > 0xff) + return -EINVAL; + + ret = i2c_smbus_read_byte_data(client, reg->reg); + if (ret < 0) + return ret; + + reg->val = ret; + + return 0; +} + +static int ov2640_s_register(struct v4l2_subdev *sd, + struct v4l2_dbg_register *reg) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + + if (reg->reg > 0xff || + reg->val > 0xff) + return -EINVAL; + + return i2c_smbus_write_byte_data(client, reg->reg, reg->val); +} +#endif + +/* Select the nearest higher resolution for capture */ +static const struct ov2640_win_size *ov2640_select_win(u32 *width, u32 *height) +{ + int i, default_size = ARRAY_SIZE(ov2640_supported_win_sizes) - 1; + + for (i = 0; i < ARRAY_SIZE(ov2640_supported_win_sizes); i++) { + if (ov2640_supported_win_sizes[i].width >= *width && + ov2640_supported_win_sizes[i].height >= *height) { + *width = ov2640_supported_win_sizes[i].width; + *height = ov2640_supported_win_sizes[i].height; + return &ov2640_supported_win_sizes[i]; + } + } + + *width = ov2640_supported_win_sizes[default_size].width; + *height = ov2640_supported_win_sizes[default_size].height; + return &ov2640_supported_win_sizes[default_size]; +} + +static int ov2640_set_params(struct i2c_client *client, u32 *width, u32 *height, + enum v4l2_mbus_pixelcode code) +{ + struct ov2640_priv *priv = to_ov2640(client); + const struct regval_list *selected_cfmt_regs; + int ret; + + /* select win */ + priv->win = ov2640_select_win(width, height); + + /* select format */ + priv->cfmt_code = 0; + switch (code) { + case V4L2_MBUS_FMT_RGB565_2X8_LE: + dev_dbg(&client->dev, "%s: Selected cfmt RGB565", __func__); + selected_cfmt_regs = ov2640_rgb565_regs; + break; + default: + case V4L2_MBUS_FMT_UYVY8_2X8: + dev_dbg(&client->dev, "%s: Selected cfmt YUV422", __func__); + selected_cfmt_regs = ov2640_yuv422_regs; + } + + /* reset hardware */ + ov2640_reset(client); + + /* initialize the sensor with default data */ + dev_dbg(&client->dev, "%s: Init default", __func__); + ret = ov2640_write_array(client, ov2640_init_regs); + if (ret < 0) + goto err; + + /* select preamble */ + dev_dbg(&client->dev, "%s: Set size to %s", __func__, priv->win->name); + ret = ov2640_write_array(client, ov2640_size_change_preamble_regs); + if (ret < 0) + goto err; + + /* set size win */ + ret = ov2640_write_array(client, priv->win->regs); + if (ret < 0) + goto err; + + /* cfmt preamble */ + dev_dbg(&client->dev, "%s: Set cfmt", __func__); + ret = ov2640_write_array(client, ov2640_format_change_preamble_regs); + if (ret < 0) + goto err; + + /* set cfmt */ + ret = ov2640_write_array(client, selected_cfmt_regs); + if (ret < 0) + goto err; + + priv->cfmt_code = code; + *width = priv->win->width; + *height = priv->win->height; + + return 0; + +err: + dev_err(&client->dev, "%s: Error %d", __func__, ret); + ov2640_reset(client); + priv->win = NULL; + + return ret; +} + +static int ov2640_g_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + struct ov2640_priv *priv = to_ov2640(client); + + if (!priv->win) { + u32 width = W_SVGA, height = H_SVGA; + int ret = ov2640_set_params(client, &width, &height, + V4L2_MBUS_FMT_UYVY8_2X8); + if (ret < 0) + return ret; + } + + mf->width = priv->win->width; + mf->height = priv->win->height; + mf->code = priv->cfmt_code; + + switch (mf->code) { + case V4L2_MBUS_FMT_RGB565_2X8_LE: + mf->colorspace = V4L2_COLORSPACE_SRGB; + break; + default: + case V4L2_MBUS_FMT_UYVY8_2X8: + mf->colorspace = V4L2_COLORSPACE_JPEG; + } + mf->field = V4L2_FIELD_NONE; + + return 0; +} + +static int ov2640_s_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + struct i2c_client *client = v4l2_get_subdevdata(sd); + int ret; + + + switch (mf->code) { + case V4L2_MBUS_FMT_RGB565_2X8_LE: + mf->colorspace = V4L2_COLORSPACE_SRGB; + break; + default: + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + case V4L2_MBUS_FMT_UYVY8_2X8: + mf->colorspace = V4L2_COLORSPACE_JPEG; + } + + ret = ov2640_set_params(client, &mf->width, &mf->height, mf->code); + + return ret; +} + +static int ov2640_try_fmt(struct v4l2_subdev *sd, + struct v4l2_mbus_framefmt *mf) +{ + const struct ov2640_win_size *win; + + /* + * select suitable win + */ + win = ov2640_select_win(&mf->width, &mf->height); + + mf->field = V4L2_FIELD_NONE; + + switch (mf->code) { + case V4L2_MBUS_FMT_RGB565_2X8_LE: + mf->colorspace = V4L2_COLORSPACE_SRGB; + break; + default: + mf->code = V4L2_MBUS_FMT_UYVY8_2X8; + case V4L2_MBUS_FMT_UYVY8_2X8: + mf->colorspace = V4L2_COLORSPACE_JPEG; + } + + return 0; +} + +static int ov2640_enum_fmt(struct v4l2_subdev *sd, unsigned int index, + enum v4l2_mbus_pixelcode *code) +{ + if (index >= ARRAY_SIZE(ov2640_codes)) + return -EINVAL; + + *code = ov2640_codes[index]; + return 0; +} + +static int ov2640_g_crop(struct v4l2_subdev *sd, struct v4l2_crop *a) +{ + a->c.left = 0; + a->c.top = 0; + a->c.width = W_UXGA; + a->c.height = H_UXGA; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + return 0; +} + +static int ov2640_cropcap(struct v4l2_subdev *sd, struct v4l2_cropcap *a) +{ + a->bounds.left = 0; + a->bounds.top = 0; + a->bounds.width = W_UXGA; + a->bounds.height = H_UXGA; + a->defrect = a->bounds; + a->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + a->pixelaspect.numerator = 1; + a->pixelaspect.denominator = 1; + + return 0; +} + +static int ov2640_video_probe(struct soc_camera_device *icd, + struct i2c_client *client) +{ + struct ov2640_priv *priv = to_ov2640(client); + u8 pid, ver, midh, midl; + const char *devname; + int ret; + + /* + * we must have a parent by now. And it cannot be a wrong one. + * So this entire test is completely redundant. + */ + if (!icd->dev.parent || + to_soc_camera_host(icd->dev.parent)->nr != icd->iface) { + dev_err(&client->dev, "Parent missing or invalid!\n"); + ret = -ENODEV; + goto err; + } + + /* + * check and show product ID and manufacturer ID + */ + i2c_smbus_write_byte_data(client, BANK_SEL, BANK_SEL_SENS); + pid = i2c_smbus_read_byte_data(client, PID); + ver = i2c_smbus_read_byte_data(client, VER); + midh = i2c_smbus_read_byte_data(client, MIDH); + midl = i2c_smbus_read_byte_data(client, MIDL); + + switch (VERSION(pid, ver)) { + case PID_OV2640: + devname = "ov2640"; + priv->model = V4L2_IDENT_OV2640; + break; + default: + dev_err(&client->dev, + "Product ID error %x:%x\n", pid, ver); + ret = -ENODEV; + goto err; + } + + dev_info(&client->dev, + "%s Product ID %0x:%0x Manufacturer ID %x:%x\n", + devname, pid, ver, midh, midl); + + return 0; + +err: + return ret; +} + +static struct soc_camera_ops ov2640_ops = { + .set_bus_param = ov2640_set_bus_param, + .query_bus_param = ov2640_query_bus_param, + .controls = ov2640_controls, + .num_controls = ARRAY_SIZE(ov2640_controls), +}; + +static struct v4l2_subdev_core_ops ov2640_subdev_core_ops = { + .g_ctrl = ov2640_g_ctrl, + .s_ctrl = ov2640_s_ctrl, + .g_chip_ident = ov2640_g_chip_ident, +#ifdef CONFIG_VIDEO_ADV_DEBUG + .g_register = ov2640_g_register, + .s_register = ov2640_s_register, +#endif +}; + +static struct v4l2_subdev_video_ops ov2640_subdev_video_ops = { + .s_stream = ov2640_s_stream, + .g_mbus_fmt = ov2640_g_fmt, + .s_mbus_fmt = ov2640_s_fmt, + .try_mbus_fmt = ov2640_try_fmt, + .cropcap = ov2640_cropcap, + .g_crop = ov2640_g_crop, + .enum_mbus_fmt = ov2640_enum_fmt, +}; + +static struct v4l2_subdev_ops ov2640_subdev_ops = { + .core = &ov2640_subdev_core_ops, + .video = &ov2640_subdev_video_ops, +}; + +/* + * i2c_driver functions + */ +static int ov2640_probe(struct i2c_client *client, + const struct i2c_device_id *did) +{ + struct ov2640_priv *priv; + struct soc_camera_device *icd = client->dev.platform_data; + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct soc_camera_link *icl; + int ret; + + if (!icd) { + dev_err(&adapter->dev, "OV2640: missing soc-camera data!\n"); + return -EINVAL; + } + + icl = to_soc_camera_link(icd); + if (!icl) { + dev_err(&adapter->dev, + "OV2640: Missing platform_data for driver\n"); + return -EINVAL; + } + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA)) { + dev_err(&adapter->dev, + "OV2640: I2C-Adapter doesn't support SMBUS\n"); + return -EIO; + } + + priv = kzalloc(sizeof(struct ov2640_priv), GFP_KERNEL); + if (!priv) { + dev_err(&adapter->dev, + "Failed to allocate memory for private data!\n"); + return -ENOMEM; + } + + priv->info = icl->priv; + + v4l2_i2c_subdev_init(&priv->subdev, client, &ov2640_subdev_ops); + + icd->ops = &ov2640_ops; + + ret = ov2640_video_probe(icd, client); + if (ret) { + icd->ops = NULL; + kfree(priv); + } else { + dev_info(&adapter->dev, "OV2640 Probed\n"); + } + + return ret; +} + +static int ov2640_remove(struct i2c_client *client) +{ + struct ov2640_priv *priv = to_ov2640(client); + struct soc_camera_device *icd = client->dev.platform_data; + + icd->ops = NULL; + kfree(priv); + return 0; +} + +static const struct i2c_device_id ov2640_id[] = { + { "ov2640", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, ov2640_id); + +static struct i2c_driver ov2640_i2c_driver = { + .driver = { + .name = "ov2640", + }, + .probe = ov2640_probe, + .remove = ov2640_remove, + .id_table = ov2640_id, +}; + +/* + * Module functions + */ +static int __init ov2640_module_init(void) +{ + return i2c_add_driver(&ov2640_i2c_driver); +} + +static void __exit ov2640_module_exit(void) +{ + i2c_del_driver(&ov2640_i2c_driver); +} + +module_init(ov2640_module_init); +module_exit(ov2640_module_exit); + +MODULE_DESCRIPTION("SoC Camera driver for Omni Vision 2640 sensor"); +MODULE_AUTHOR("Alberto Panizzo"); +MODULE_LICENSE("GPL v2"); diff --git a/include/media/v4l2-chip-ident.h b/include/media/v4l2-chip-ident.h index 51e89f2267b8..44fe44ec9ea7 100644 --- a/include/media/v4l2-chip-ident.h +++ b/include/media/v4l2-chip-ident.h @@ -74,6 +74,7 @@ enum { V4L2_IDENT_SOI968 = 256, V4L2_IDENT_OV9640 = 257, V4L2_IDENT_OV6650 = 258, + V4L2_IDENT_OV2640 = 259, /* module saa7146: reserved range 300-309 */ V4L2_IDENT_SAA7146 = 300, -- cgit v1.2.3