r5u870

Ricoh R5U870 Linux Driver
git clone https://logand.com/git/r5u870.git/
Log | Files | Refs | README | LICENSE

commit 2bf3b305535fb7c3a7c9c0c0a5de9d7035b856ce
parent ccb72a37290703dbf0a433a47cbfede0d58cb456
Author: alex <alex@022568fa-442e-4ef8-a3e8-54dcafdb011a>
Date:   Fri, 18 Jan 2008 02:33:48 +0000

* Added changelog entries.
* Provide working V4L1 support. \o/


git-svn-id: http://svn.mediati.org/svn/r5u870/trunk@41 022568fa-442e-4ef8-a3e8-54dcafdb011a

Diffstat:
MChangeLog | 6++++++
Musbcam/usbcam_fops.c | 564++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
2 files changed, 391 insertions(+), 179 deletions(-)

diff --git a/ChangeLog b/ChangeLog @@ -1,3 +1,9 @@ +2008-01-18 Alexander Hixon <hixon.alexander@mediati.org> + * usbcam/usbcam_fops.c: Provide working V4L1 support. Probably needs a bit + of a cleanup, but works perfectly fine. + * r5u870.c: Added support for the 1837 UVC camera - apparently uses the + same firmware as the 1836 model. Awating test results from a user. + 2008-01-17 Alexander Hixon <hixon.alexander@mediati.org> * usbcam/usbcam.c: Remove unnecessary file - accidentially kept post- merge. diff --git a/usbcam/usbcam_fops.c b/usbcam/usbcam_fops.c @@ -28,6 +28,61 @@ * - Add debug tracing to more ioctl paths */ +/* HELPER FOR V4L1 COMPAT OPS */ + +const static unsigned int palette2pixelformat[] = { + [VIDEO_PALETTE_GREY] = V4L2_PIX_FMT_GREY, + [VIDEO_PALETTE_RGB555] = V4L2_PIX_FMT_RGB555, + [VIDEO_PALETTE_RGB565] = V4L2_PIX_FMT_RGB565, + [VIDEO_PALETTE_RGB24] = V4L2_PIX_FMT_BGR24, + [VIDEO_PALETTE_RGB32] = V4L2_PIX_FMT_BGR32, + /* yuv packed pixel */ + [VIDEO_PALETTE_YUYV] = V4L2_PIX_FMT_YUYV, + [VIDEO_PALETTE_YUV422] = V4L2_PIX_FMT_YUYV, + [VIDEO_PALETTE_UYVY] = V4L2_PIX_FMT_UYVY, + /* yuv planar */ + [VIDEO_PALETTE_YUV410P] = V4L2_PIX_FMT_YUV410, + [VIDEO_PALETTE_YUV420] = V4L2_PIX_FMT_YUV420, + [VIDEO_PALETTE_YUV420P] = V4L2_PIX_FMT_YUV420, + [VIDEO_PALETTE_YUV411P] = V4L2_PIX_FMT_YUV411P, + [VIDEO_PALETTE_YUV422P] = V4L2_PIX_FMT_YUV422P, +}; + +static unsigned int __pure +palette_to_pixelformat(unsigned int palette) +{ + if (palette < ARRAY_SIZE(palette2pixelformat)) + return palette2pixelformat[palette]; + else + return 0; +} + +static int poll_one(struct file *file) +{ + int retval = 1; + poll_table *table; + struct poll_wqueues pwq; + + poll_initwait(&pwq); + table = &pwq.pt; + for (;;) { + int mask; + set_current_state(TASK_INTERRUPTIBLE); + mask = file->f_op->poll(file, table); + if (mask & POLLIN) + break; + table = NULL; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } + schedule(); + } + set_current_state(TASK_RUNNING); + poll_freewait(&pwq); + return retval; +} + /* * V4L file_operations callout implementations */ @@ -184,185 +239,6 @@ static int usbcam_v4l_mmap(struct file *filp, struct vm_area_struct * vma) return videobuf_mmap_mapper(&ufp->ufh_vbq, vma); } -/* Intercept calls to minidriver V4L handler thing. */ -static int usbcam_v4l_int_ioctl(struct inode *inodep, struct file *filp, - unsigned int cmd, void *arg) -{ - struct usbcam_fh *ufp = (struct usbcam_fh *) filp->private_data; - struct usbcam_dev *udp = ufp->ufh_dev; - - if (cmd == VIDIOCGCAP) { - struct video_capability *cap = (struct video_capability *) arg; - - usbcam_lock(udp); - - strlcpy(cap->name, udp->ud_vdev.name, sizeof(cap->name)); - cap->type = VID_TYPE_CAPTURE; - cap->audios = 0; - cap->channels = 1; /* only one input source, the camera */ - - cap->maxwidth = udp->ud_format.width; - cap->maxheight = udp->ud_format.height; - - /* - * We lie, here. These values normally return 640x480, which is - * actually the maximum, not the minimum. Minimum is usually - * 160x120. It's sort of useful to lie since lots of software - * just stick with the minimum - we want higher res for the - * user where possible. - */ - - cap->minwidth = udp->ud_format.width; - cap->minheight = udp->ud_format.height; - - usbcam_unlock(udp); - return 0; - } - else if (cmd == VIDIOCGFBUF) { - struct video_buffer *buf = (struct video_buffer *) arg; - - usbcam_lock(udp); - buf->base = NULL; /* no physical frame buffer access */ - buf->height = udp->ud_format.height; - buf->width = udp->ud_format.width; - - // graciously stolen from drivers/media/video/v4l1-compat.c - // and modified slightly. - switch (udp->ud_format.pixelformat) { - case V4L2_PIX_FMT_RGB332: - buf->depth = 8; - break; - case V4L2_PIX_FMT_RGB555: - buf->depth = 15; - break; - case V4L2_PIX_FMT_RGB565: - buf->depth = 16; - break; - case V4L2_PIX_FMT_BGR24: - buf->depth = 24; - break; - case V4L2_PIX_FMT_BGR32: - buf->depth = 32; - break; - default: - buf->depth = 0; - } - - if (udp->ud_format.bytesperline) { - buf->bytesperline = udp->ud_format.bytesperline; - - /* typically comes out at 16 bit depth as non-rgb */ - if (!buf->depth && buf->width) - buf->depth = ((udp->ud_format.bytesperline<<3) - + (buf->width-1) ) - /buf->width; - } else { - buf->bytesperline = - (buf->width * buf->depth + 7) & 7; - buf->bytesperline >>= 3; - } - - usbcam_unlock(udp); - return 0; - } - else { - usbcam_warn(udp, "usbcam_v4l_int_ioctl called without valid ioctl"); - return -ENOIOCTLCMD; - } -} - -static int usbcam_v4l_ioctl (struct inode *inodep, struct file *file, - unsigned int cmd, unsigned long arg) -{ -#ifdef CONFIG_VIDEO_V4L1_COMPAT - if (cmd == VIDIOCGCAP || cmd == VIDIOCGFBUF) - { - // run our own internal ioctl handler for these V4L compat commands. - return video_usercopy(inodep, file, cmd, arg, usbcam_v4l_int_ioctl); - } -#endif - - /*if (cmd != VIDIOCGMBUF && _IOC_TYPE(cmd) == 'v') - { - // send the command off to the compat translator if it's any other v4l1 - return v4l_compat_translate_ioctl(inodep, file, cmd, arg, - usbcam_v4l_int_ioctl); - } - else - {*/ - // normal v4l2 command - return video_ioctl2(inodep, file, cmd, arg); - //} -} - -/* - * The template file_operations structure - * - * Each usbcam_minidrv_t contains its own copy of this, which - * is associated with the video4linux device created for that - * minidriver. - * - * In general, copies will differ only in the .owner field, which - * will refer to the minidriver module, not usbcam. - */ - -struct file_operations usbcam_v4l_fops_template = { - .owner = THIS_MODULE, - .open = usbcam_v4l_open, - .release = usbcam_v4l_release, - .read = usbcam_v4l_read, - .poll = usbcam_v4l_poll, - .mmap = usbcam_v4l_mmap, - /*.ioctl = video_ioctl2,*/ - .ioctl = usbcam_v4l_ioctl, -#ifdef CONFIG_COMPAT - .compat_ioctl = v4l_compat_ioctl32, -#endif - .llseek = no_llseek, -}; - - -static void usbcam_dbg_v4l2_buffer_res(struct usbcam_dev *udp, int res, - void *arg, const char *prefix) -{ - struct v4l2_buffer *b __attribute__((unused)) = - (struct v4l2_buffer *) arg; - - if (res) { - usbcam_dbg(udp, IOCTL_BUF, "%s res:%d", prefix, res); - return; - } - - usbcam_dbg(udp, IOCTL_BUF, "%s out: index=%d type=%d bytesused=%d " - "flags=0x%x field=%d memory=%d m=0x%lx length=%d", - prefix, b->index, b->type, b->bytesused, - b->flags, b->field, b->memory, b->m.userptr, b->length); -} - -static void usbcam_dbg_v4l2_pix_format(struct usbcam_dev *udp, - struct v4l2_pix_format *f, - const char *prefix) -{ - __u32 pixfmt = f->pixelformat; - if (!pixfmt) - pixfmt = 0x3f3f3f3f; - usbcam_dbg(udp, IOCTL_FMT, "%s wid=%d hgt=%d fmt=%.4s field=%d " - "bpl=%d size=%d cs=%d", prefix, - f->width, f->height, (char *) &pixfmt, f->field, - f->bytesperline, f->sizeimage, f->colorspace); -} - -static void usbcam_dbg_v4l2_pix_format_res(struct usbcam_dev *udp, int res, - struct v4l2_pix_format *f, - const char *prefix) -{ - if (res) { - usbcam_dbg(udp, IOCTL_FMT, "%s %d", prefix, res); - return; - } - usbcam_dbg_v4l2_pix_format(udp, f, prefix); -} - static int usbcam_v4l_vidiocgmbuf(struct file *filp, void *fh, struct video_mbuf *p) { @@ -429,6 +305,47 @@ static int usbcam_v4l_vidiocgmbuf(struct file *filp, void *fh, return 0; } +static void usbcam_dbg_v4l2_buffer_res(struct usbcam_dev *udp, int res, + void *arg, const char *prefix) +{ + struct v4l2_buffer *b __attribute__((unused)) = + (struct v4l2_buffer *) arg; + + if (res) { + usbcam_dbg(udp, IOCTL_BUF, "%s res:%d", prefix, res); + return; + } + + usbcam_dbg(udp, IOCTL_BUF, "%s out: index=%d type=%d bytesused=%d " + "flags=0x%x field=%d memory=%d m=0x%lx length=%d", + prefix, b->index, b->type, b->bytesused, + b->flags, b->field, b->memory, b->m.userptr, b->length); +} + +static void usbcam_dbg_v4l2_pix_format(struct usbcam_dev *udp, + struct v4l2_pix_format *f, + const char *prefix) +{ + __u32 pixfmt = f->pixelformat; + if (!pixfmt) + pixfmt = 0x3f3f3f3f; + usbcam_dbg(udp, IOCTL_FMT, "%s wid=%d hgt=%d fmt=%.4s field=%d " + "bpl=%d size=%d cs=%d", prefix, + f->width, f->height, (char *) &pixfmt, f->field, + f->bytesperline, f->sizeimage, f->colorspace); +} + +static void usbcam_dbg_v4l2_pix_format_res(struct usbcam_dev *udp, int res, + struct v4l2_pix_format *f, + const char *prefix) +{ + if (res) { + usbcam_dbg(udp, IOCTL_FMT, "%s %d", prefix, res); + return; + } + usbcam_dbg_v4l2_pix_format(udp, f, prefix); +} + static int usbcam_v4l_vidioc_reqbufs(struct file *filp, void *fh, struct v4l2_requestbuffers *r) { @@ -955,6 +872,295 @@ static int usbcam_v4l_vidioc_s_input(struct file *filp, void *fh, return 0; } +/* Intercept calls to minidriver V4L handler thing for compat calls. */ +static int usbcam_v4l_int_ioctl(struct inode *inodep, struct file *filp, + unsigned int cmd, void *arg) +{ + struct usbcam_fh *ufp = (struct usbcam_fh *) filp->private_data; + struct usbcam_dev *udp = ufp->ufh_dev; + + struct v4l2_buffer buf2; + enum v4l2_buf_type captype = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int err = 0; + + + if (cmd == VIDIOCGCAP) { + struct video_capability *cap = (struct video_capability *) arg; + + usbcam_lock(udp); + + strlcpy(cap->name, udp->ud_vdev.name, sizeof(cap->name)); + cap->type = VID_TYPE_CAPTURE; + cap->audios = 0; + cap->channels = 1; /* only one input source, the camera */ + + cap->maxwidth = udp->ud_format.width; + cap->maxheight = udp->ud_format.height; + + /* + * We lie, here. These values normally return 640x480, which is + * actually the maximum, not the minimum. Minimum is usually + * 160x120. It's sort of useful to lie since lots of software + * just stick with the minimum - we want higher res for the + * user where possible. + */ + + cap->minwidth = udp->ud_format.width; + cap->minheight = udp->ud_format.height; + + usbcam_unlock(udp); + return 0; + } + else if (cmd == VIDIOCGCHAN) { + struct video_channel *chan = (struct video_channel *) arg; + + usbcam_lock(udp); + + chan->channel = 0; + strlcpy(chan->name, udp->ud_vdev.name, sizeof(chan->name)); + chan->tuners = 0; + chan->type = VIDEO_TYPE_CAMERA; + + usbcam_unlock(udp); + return 0; + } + else if (cmd == VIDIOCSCHAN) { + struct video_channel *chan = (struct video_channel *) arg; + + if (chan->norm != 0) + return -EINVAL; + return 0; + } + else if (cmd == VIDIOCGAUDIO) { + return -ENOIOCTLCMD; + } + else if (cmd == VIDIOCGTUNER) { + return -ENOIOCTLCMD; + } + else if (cmd == VIDIOCGPICT) { + return -ENOIOCTLCMD; + } + else if (cmd == VIDIOCGWIN) { + struct video_window *win = (struct video_window *) arg; + + usbcam_lock(udp); + + win->x = 0; + win->y = 0; + win->width = udp->ud_format.width; + win->height = udp->ud_format.height; + win->chromakey = 0; + win->clips = NULL; + win->clipcount = 0; + win->flags = 0; + + usbcam_unlock(udp); + return 0; + } + else if (cmd == VIDIOCGFBUF) { + struct video_buffer *buf = (struct video_buffer *) arg; + + usbcam_lock(udp); + + buf->base = NULL; /* no physical frame buffer access */ + buf->height = udp->ud_format.height; + buf->width = udp->ud_format.width; + + /* + * graciously stolen from drivers/media/video/v4l1-compat.c + * and modified slightly. + */ + switch (udp->ud_format.pixelformat) { + case V4L2_PIX_FMT_RGB332: + buf->depth = 8; + break; + case V4L2_PIX_FMT_RGB555: + buf->depth = 15; + break; + case V4L2_PIX_FMT_RGB565: + buf->depth = 16; + break; + case V4L2_PIX_FMT_BGR24: + buf->depth = 24; + break; + case V4L2_PIX_FMT_BGR32: + buf->depth = 32; + break; + default: + buf->depth = 0; + } + + if (udp->ud_format.bytesperline) { + buf->bytesperline = udp->ud_format.bytesperline; + + /* typically comes out at 16 bit depth as non-rgb */ + if (!buf->depth && buf->width) + buf->depth = ((udp->ud_format.bytesperline<<3) + + (buf->width-1) ) + /buf->width; + } else { + buf->bytesperline = + (buf->width * buf->depth + 7) & 7; + buf->bytesperline >>= 3; + } + + usbcam_unlock(udp); + return 0; + } + else if (cmd == VIDIOCGMBUF) { + struct video_mbuf *mbuf = (struct video_mbuf *) arg; + return usbcam_v4l_vidiocgmbuf(filp, filp->private_data, mbuf); + } + else if (cmd == VIDIOCSFBUF) { + usbcam_warn(udp, "VIDIOCSFBUF called."); + return -ENOIOCTLCMD; + } + else if (cmd == VIDIOCSWIN) { + return -ENOIOCTLCMD; + } + else if (cmd == VIDIOCMCAPTURE) { + struct v4l2_format *fmt2 = NULL; + struct video_mmap *mm = arg; + + fmt2 = kzalloc(sizeof(*fmt2),GFP_KERNEL); + memset(&buf2,0,sizeof(buf2)); + + fmt2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = usbcam_v4l_vidioc_g_fmt_cap(filp, filp->private_data, fmt2); + if (err < 0) { + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCMCAPTURE / VIDIOC_G_FMT: %d\n",err); + return err; + } + if (mm->width != fmt2->fmt.pix.width || + mm->height != fmt2->fmt.pix.height || + palette_to_pixelformat(mm->format) != + fmt2->fmt.pix.pixelformat) + {/* New capture format... */ + fmt2->fmt.pix.width = mm->width; + fmt2->fmt.pix.height = mm->height; + fmt2->fmt.pix.pixelformat = + palette_to_pixelformat(mm->format); + fmt2->fmt.pix.field = V4L2_FIELD_ANY; + fmt2->fmt.pix.bytesperline = 0; + err = usbcam_v4l_vidioc_s_fmt_cap(filp, filp->private_data, fmt2); + if (err < 0) { + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCMCAPTURE / VIDIOC_S_FMT: %d\n",err); + return err; + } + } + buf2.index = mm->frame; + buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = usbcam_v4l_vidioc_querybuf(filp, filp->private_data, &buf2); + if (err < 0) { + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCMCAPTURE / VIDIOC_QUERYBUF: %d\n",err); + return err; + } + err = usbcam_v4l_vidioc_qbuf(filp, filp->private_data, &buf2); + if (err < 0) { + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCMCAPTURE / VIDIOC_QBUF: %d\n",err); + return err; + } + err = usbcam_v4l_vidioc_streamon(filp, filp->private_data, captype); + if (err < 0) + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCMCAPTURE / VIDIOC_STREAMON: %d\n",err); + return 0; + } + else if (cmd == VIDIOCSYNC) { + int *i = arg; + + memset(&buf2,0,sizeof(buf2)); + buf2.index = *i; + buf2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + err = usbcam_v4l_vidioc_querybuf(filp, filp->private_data, &buf2); + if (err < 0) { + /* No such buffer */ + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n",err); + return err; + } + if (!(buf2.flags & V4L2_BUF_FLAG_MAPPED)) { + /* Buffer is not mapped */ + err = -EINVAL; + return err; + } + + /* make sure capture actually runs so we don't block forever */ + err = usbcam_v4l_vidioc_streamon(filp, filp->private_data, captype); + if (err < 0) { + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCSYNC / VIDIOC_STREAMON: %d\n",err); + return err; + } + + /* Loop as long as the buffer is queued, but not done */ + while ((buf2.flags & + (V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_DONE)) + == V4L2_BUF_FLAG_QUEUED) + { + err = poll_one(filp); + if (err < 0 || /* error or sleep was interrupted */ + err == 0) /* timeout? Shouldn't occur. */ + return err; + err = usbcam_v4l_vidioc_querybuf(filp, filp->private_data, &buf2); + if (err < 0) + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCSYNC / VIDIOC_QUERYBUF: %d\n",err); + } + if (!(buf2.flags & V4L2_BUF_FLAG_DONE)) /* not done */ + return err; + do { + err = usbcam_v4l_vidioc_dqbuf(filp, filp->private_data, &buf2); + if (err < 0) + usbcam_dbg(udp, IOCTL_MISC, "VIDIOCSYNC / VIDIOC_DQBUF: %d\n",err); + } while (err == 0 && buf2.index != *i); + return err; + } + else { + usbcam_warn(udp, "usbcam_v4l_int_ioctl called without valid ioctl"); + return -ENOIOCTLCMD; + } +} + +static int usbcam_v4l_ioctl (struct inode *inodep, struct file *file, + unsigned int cmd, unsigned long arg) +{ + printk("usbcam: received v4l ioctl: %d\n", cmd); + +#ifdef CONFIG_VIDEO_V4L1_COMPAT + if (_IOC_TYPE(cmd) == 'v') + { + // run our own internal ioctl handler for these V4L compat ioctl. + return video_usercopy(inodep, file, cmd, arg, usbcam_v4l_int_ioctl); + } +#endif + + return video_ioctl2(inodep, file, cmd, arg); +} + +/* + * The template file_operations structure + * + * Each usbcam_minidrv_t contains its own copy of this, which + * is associated with the video4linux device created for that + * minidriver. + * + * In general, copies will differ only in the .owner field, which + * will refer to the minidriver module, not usbcam. + */ + +struct file_operations usbcam_v4l_fops_template = { + .owner = THIS_MODULE, + .open = usbcam_v4l_open, + .release = usbcam_v4l_release, + .read = usbcam_v4l_read, + .poll = usbcam_v4l_poll, + .mmap = usbcam_v4l_mmap, + /*.ioctl = video_ioctl2,*/ + .ioctl = usbcam_v4l_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = v4l_compat_ioctl32, +#endif + .llseek = no_llseek, +}; + + /* * The template video_device structure *