/* * Video for Linux Two * * A generic video device interface for the LINUX operating system * using a set of device structures/vectors for low level operations. * * This file replaces the videodev.c file that comes with the * regular kernel distribution. * * 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. * * Author: Bill Dirks * based on code by Alan Cox, * */ #ifndef __KERNEL__ #define __KERNEL__ #endif #include #ifndef EXPORT_SYMTAB #define EXPORT_SYMTAB #endif #include #include #include #include #include #include #include #include #include #include #if defined(CONFIG_UST) || defined(CONFIG_UST_MODULE) #include #endif #include #include #include #include #ifdef CONFIG_KMOD #include #endif #ifndef min #define min(a,b) (((a)<(b))?(a):(b)) #endif static int v4l2_major = 81; MODULE_PARM(v4l2_major, "i"); #define V4L2_NUM_DEVICES 256 /* * Active devices */ static struct v4l2_device *v4l2_device[V4L2_NUM_DEVICES]; static struct v4l2_clock *masterclock; /* * D E V I C E O P E R A T I O N S * */ /* * Open a video device. */ static int video_open(struct inode *inode, struct file *file) { unsigned int minor = MINOR(inode->i_rdev); struct v4l2_device *vfl; int err; if (minor >= V4L2_NUM_DEVICES) return -ENODEV; vfl = v4l2_device[minor]; if (vfl == NULL) { #ifdef CONFIG_KMOD /* KMOD code by Erik Walthinsen */ char modname[20]; sprintf(modname, "char-major-%d-%d", v4l2_major, minor); request_module(modname); vfl = v4l2_device[minor]; if (vfl == NULL) #endif return -ENODEV; } if (vfl->open == NULL) return -ENODEV; err = vfl->open(vfl, file->f_flags, &file->private_data); if (err == 0 && file->private_data == NULL) { printk(KERN_ERR"V4L2: Device returned NULL open id\n"); err = -ENODEV; } if (err == 0) ++vfl->busy; return err; } /* * Last close of a struct file */ static int video_release(struct inode *inode, struct file *file) { struct v4l2_device *vfl = v4l2_device[MINOR(inode->i_rdev)]; if (vfl->close) vfl->close(file->private_data); file->private_data = NULL; if (vfl->busy) --vfl->busy; return 0; } /* * Read from a video device */ static ssize_t video_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct v4l2_device *vfl = v4l2_device[MINOR(file->f_dentry->d_inode->i_rdev)]; if (vfl->read) return vfl->read(file->private_data, buf, count, file->f_flags & O_NONBLOCK); return -EINVAL; } /* * Write to a video device */ static ssize_t video_write(struct file *file, const char *buf, size_t count, loff_t *ppos) { struct v4l2_device *vfl = v4l2_device[MINOR(file->f_dentry->d_inode->i_rdev)]; if (vfl->write) return vfl->write(file->private_data, buf, count, file->f_flags & O_NONBLOCK); return 0; } /* * IO Control */ static void fill_ctrl_category(struct v4l2_queryctrl *qc); static int translate_ioctl(struct file *file, struct v4l2_device *vfl, void *per_open_data, int cmd, void *arg); static int video_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct v4l2_device *vfl = v4l2_device[MINOR(inode->i_rdev)]; char targ[V4L2_MAX_IOCTL_SIZE]; void *parg = (void *)arg; int err = -EINVAL; if (vfl->ioctl == NULL) return -EINVAL; /* Copy arguments into temp kernel buffer */ switch (_IOC_DIR(cmd)) { case _IOC_NONE: parg = (void *)arg; break; case _IOC_WRITE: case (_IOC_WRITE | _IOC_READ): if (_IOC_SIZE(cmd) > sizeof(targ)) { printk(KERN_ERR"V4L2: ioctl 0x%08x arguments are " "too big, > %d\n", cmd, sizeof(targ)); break;/* Arguments are too big. */ } if (copy_from_user(targ, (void *)arg, _IOC_SIZE(cmd))) { printk(KERN_INFO"V4L2: Fault on write ioctl 0x%08x " "copying data from user buffer\n", cmd); return -EFAULT; } parg = targ; break; case _IOC_READ: parg = targ; break; } /* Fill in the category for pre-defined controls */ if (cmd == VIDIOC_QUERYCTRL) fill_ctrl_category((struct v4l2_queryctrl *)parg); /* Try passing it to the driver first */ err = vfl->ioctl(file->private_data, cmd, parg); /* If the driver doesn't recognize it and it's an old ioctl, pass it through the translation layer. */ if (err == -ENOIOCTLCMD && _IOC_TYPE(cmd) == 'v') { err = translate_ioctl(file, vfl, file->private_data, cmd, parg); } /* Copy results into user buffer */ switch (_IOC_DIR(cmd)) { case _IOC_READ: case (_IOC_WRITE | _IOC_READ): if (parg == targ && copy_to_user((void *)arg, parg, _IOC_SIZE(cmd))) { printk(KERN_INFO"V4L2: Fault on read ioctl 0x%08x " "copying results to user buffer\n", cmd); return -EFAULT; } break; } if (err != -ENOIOCTLCMD) return err; /* Handle ioctls not recognized by the driver */ return -EINVAL; } /* * Memory mapping */ static int video_mmap(struct file *file, struct vm_area_struct *vma) { struct v4l2_device *vfl = v4l2_device[MINOR(file->f_dentry->d_inode->i_rdev)]; int err; if (vfl->mmap) { vma->vm_file = file; /* For v4l compatibility. v4l apps typically pass zero */ /* for the offset, so replace it with the real value */ /* saved from before */ if (vma->vm_offset == 0 && vfl->ioctl) { struct v4l2_buffer buf; buf.index = 0; buf.type = V4L2_BUF_TYPE_CAPTURE; if (vfl->ioctl(file->private_data, VIDIOC_QUERYBUF, &buf) == 0) vma->vm_offset = buf.offset; } err = vfl->mmap(file->private_data, vma); #if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,3) /* This is now done in the kernel, as it should be */ if (err == 0 && vma->vm_file != NULL) ++vma->vm_file->f_count; #endif return err; } return -ENODEV; } /* * Poll (select()) support */ static unsigned int video_poll(struct file *file, poll_table *table) { struct v4l2_device *vfl = v4l2_device[MINOR(file->f_dentry->d_inode->i_rdev)]; if (vfl->poll) return vfl->poll(file->private_data, file, table); return POLLERR; } /* * Not used. */ static loff_t video_llseek(struct file *file, loff_t offset, int origin) { return -ESPIPE; } /* * CONTROL CATEGORIES */ static void fill_ctrl_category(struct v4l2_queryctrl *qc) { if ((qc->id >= V4L2_CID_BRIGHTNESS && qc->id <= V4L2_CID_HUE) || (qc->id >= V4L2_CID_BLACK_LEVEL && qc->id <= V4L2_CID_LASTP1-1)) { qc->category = V4L2_CTRL_CAT_VIDEO; strcpy(qc->group, "Video"); } else if ((qc->id >= V4L2_CID_AUDIO_VOLUME && qc->id <= V4L2_CID_AUDIO_LOUDNESS)) { qc->category = V4L2_CTRL_CAT_AUDIO; strcpy(qc->group, "Audio"); } else if ((qc->id >= V4L2_CID_EFFECT_BASE && qc->id <= V4L2_CID_EFFECT_BASE + 10000)) { qc->category = V4L2_CTRL_CAT_EFFECT; strcpy(qc->group, "Effect"); } else { strcpy(qc->group, "Private"); } } /* * B A C K W A R D C O M P A T I B I L I T Y * * This code allows applications written for the original API to * work with drivers that only understand the new API. */ static int get_v4l_control(struct v4l2_device *vfl, void *context, int cid) { struct v4l2_queryctrl qctrl2; struct v4l2_control ctrl2; int err; qctrl2.id = cid; err = vfl->ioctl(context, VIDIOC_QUERYCTRL, &qctrl2); if (err == 0 && !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) { ctrl2.id = qctrl2.id; vfl->ioctl(context, VIDIOC_G_CTRL, &ctrl2); return ((ctrl2.value - qctrl2.minimum) * 65535 + (qctrl2.maximum - qctrl2.minimum) / 2) / (qctrl2.maximum - qctrl2.minimum); } return 0; } static int set_v4l_control(struct v4l2_device *vfl, void *context, int cid, int value) { struct v4l2_queryctrl qctrl2; struct v4l2_control ctrl2; int err; qctrl2.id = cid; err = vfl->ioctl(context, VIDIOC_QUERYCTRL, &qctrl2); if (err == 0 && !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED) && !(qctrl2.flags & V4L2_CTRL_FLAG_GRABBED)) { if (value < 0) value = 0; if (value > 65535) value = 65535; if (value && qctrl2.type == V4L2_CTRL_TYPE_BOOLEAN) value = 65535; ctrl2.id = qctrl2.id; ctrl2.value = (value * (qctrl2.maximum - qctrl2.minimum) + 32767) / 65535; ctrl2.value += qctrl2.minimum; vfl->ioctl(context, VIDIOC_S_CTRL, &ctrl2); } return 0; } static int find_tuner(struct v4l2_device *vfl, void *context, int n) { struct v4l2_input inp2; int i; int err; /* Find the input number of the n'th tuner */ for (i = 0; i < 100/*arbitrary*/; ++i) { inp2.index = i; err = vfl->ioctl(context, VIDIOC_ENUMINPUT, &inp2); if (err < 0) break; if (inp2.type != V4L2_INPUT_TYPE_TUNER) continue; if (n == 0) break; --n; } if (err < 0) return err; return i; } static int palette_to_pixelformat(int palette) { int pixelformat = 0; switch (palette) { case VIDEO_PALETTE_GREY: pixelformat = V4L2_PIX_FMT_GREY; break; case VIDEO_PALETTE_RGB555: pixelformat = V4L2_PIX_FMT_RGB555; break; case VIDEO_PALETTE_RGB565: pixelformat = V4L2_PIX_FMT_RGB565; break; case VIDEO_PALETTE_RGB24: pixelformat = V4L2_PIX_FMT_BGR24; break; case VIDEO_PALETTE_RGB32: pixelformat = V4L2_PIX_FMT_BGR32; break; case VIDEO_PALETTE_YUYV: case VIDEO_PALETTE_YUV422: pixelformat = V4L2_PIX_FMT_YUYV; break; case VIDEO_PALETTE_UYVY: pixelformat = V4L2_PIX_FMT_UYVY; break; case VIDEO_PALETTE_YUV420: pixelformat = V4L2_PIX_FMT_YUV420; break; case VIDEO_PALETTE_YUV422P: pixelformat = V4L2_PIX_FMT_YVU422P; break; case VIDEO_PALETTE_YUV411P: pixelformat = V4L2_PIX_FMT_YVU411P; break; } return pixelformat; } static int pixelformat_to_palette(int pixelformat) { int palette = 0; switch (pixelformat) { case V4L2_PIX_FMT_GREY: palette = VIDEO_PALETTE_GREY; break; case V4L2_PIX_FMT_RGB555: palette = VIDEO_PALETTE_RGB555; break; case V4L2_PIX_FMT_RGB565: palette = VIDEO_PALETTE_RGB565; break; case V4L2_PIX_FMT_BGR24: palette = VIDEO_PALETTE_RGB24; break; case V4L2_PIX_FMT_BGR32: palette = VIDEO_PALETTE_RGB32; break; case V4L2_PIX_FMT_YUYV: palette = VIDEO_PALETTE_YUYV; break; case V4L2_PIX_FMT_UYVY: palette = VIDEO_PALETTE_UYVY; break; case V4L2_PIX_FMT_YUV420: palette = VIDEO_PALETTE_YUV420; break; case V4L2_PIX_FMT_YVU422P: palette = VIDEO_PALETTE_YUV422P; break; case V4L2_PIX_FMT_YVU411P: palette = VIDEO_PALETTE_YUV411P; break; } return palette; } /* Do an 'in' (wait for input) select on a single file descriptor */ /* This stuff plaigarized from linux/fs/select.c */ #define __FD_IN(fds, n) (fds->in + n) #define BIT(i) (1UL << ((i)&(__NFDBITS-1))) #define SET(i,m) (*(m) |= (i)) #ifndef HAVE_DO_SELECT /* Note: This code, inside the #ifndef HAVE_DO_SELECT ... #endif, is copied from fs/select.c, and is only included here because do_select() is not exported to modules. In the future export do_select() to modules and delete this code down to the #endif. Thank you. */ #include #include #define BITS(fds, n) (*__FD_IN(fds, n)) #define DEFAULT_POLLMASK (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM) #define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR) #define ISSET(i,m) (((i)&*(m)) != 0) static void free_wait(poll_table * p) { struct poll_table_entry * entry; poll_table *old; while (p) { entry = p->entry + p->nr; while (p->nr > 0) { p->nr--; entry--; remove_wait_queue(entry->wait_address,&entry->wait); fput(entry->filp); } old = p; p = p->next; free_page((unsigned long) old); } } static int my_do_select(int n, fd_set_bits *fds, long *timeout) { poll_table *wait_table, *wait; int retval, i, off; long __timeout = *timeout; wait = wait_table = NULL; if (__timeout) { wait_table = (poll_table *) __get_free_page(GFP_KERNEL); if (!wait_table) return -ENOMEM; wait_table->nr = 0; wait_table->entry = (struct poll_table_entry *)(wait_table + 1); wait_table->next = NULL; wait = wait_table; } lock_kernel(); // retval = max_select_fd(n, fds); //if (retval < 0) // goto out; //n = retval; retval = 0; for (;;) { current->state = TASK_INTERRUPTIBLE; for (i = 0 ; i < n; i++) { unsigned long bit = BIT(i); unsigned long mask; struct file *file; off = i / __NFDBITS; if (!(bit & BITS(fds, off))) continue; /* * The poll_wait routine will increment f_count if * the file is added to the wait table, so we don't * need to increment it now. */ file = fcheck(i); mask = POLLNVAL; if (file) { mask = DEFAULT_POLLMASK; if (file->f_op && file->f_op->poll) mask = file->f_op->poll(file, wait); } if ((mask & POLLIN_SET) && ISSET(bit, __FD_IN(fds,off))) { retval++; wait = NULL; } } wait = NULL; if (retval || !__timeout || signal_pending(current)) break; __timeout = schedule_timeout(__timeout); } current->state = TASK_RUNNING; //out: if (*timeout) free_wait(wait_table); /* * Up-to-date the caller timeout. */ *timeout = __timeout; unlock_kernel(); return retval; } #endif // HAVE_DO_SELECT static int simple_select(struct file *file) { fd_set_bits fds; char *bits; long timeout; int i, fd, n, ret, size; for (i = 0; i < current->files->max_fds; ++i) if (file == current->files->fd[i]) break; if (i == current->files->max_fds) return -EINVAL; fd = i; n = fd + 1; timeout = MAX_SCHEDULE_TIMEOUT; /* * We need 6 bitmaps (in/out/ex for both incoming and outgoing), * since we used fdset we need to allocate memory in units of * long-words. */ ret = -ENOMEM; size = FDS_BYTES(n); bits = kmalloc(6 * size, GFP_KERNEL); if (!bits) goto out_nofds; fds.in = (unsigned long *) bits; fds.out = (unsigned long *) (bits + size); fds.ex = (unsigned long *) (bits + 2*size); fds.res_in = (unsigned long *) (bits + 3*size); fds.res_out = (unsigned long *) (bits + 4*size); fds.res_ex = (unsigned long *) (bits + 5*size); /* All zero except our one file descriptor bit, for input */ memset(bits, 0, 6 * size); SET(BIT(fd), __FD_IN((&fds), fd / __NFDBITS)); ret = my_do_select(n, &fds, &timeout); if (ret < 0) goto out; if (!ret) { ret = -ERESTARTNOHAND; if (signal_pending(current)) goto out; ret = 0; } out: kfree(bits); out_nofds: return ret; } static int translate_ioctl(struct file *file, struct v4l2_device *vfl, void *context, int cmd, void *arg) { int err = -ENOIOCTLCMD; switch (cmd) { case VIDIOCGCAP: /* capability */ { struct video_capability *cap = arg; struct v4l2_capability cap2; struct v4l2_framebuffer fbuf2; err = vfl->ioctl(context, VIDIOC_QUERYCAP, &cap2); if (err < 0) break; if (cap2.flags & V4L2_FLAG_PREVIEW) { err = vfl->ioctl(context, VIDIOC_G_FBUF, &fbuf2); if (err < 0) memset(&fbuf2, 0, sizeof(fbuf2)); err = 0; } memset(cap, 0, sizeof(cap)); memcpy(cap->name, cap2.name, min(sizeof(cap->name), sizeof(cap2.name))); cap->name[sizeof(cap->name) - 1] = 0; if (cap2.type == V4L2_TYPE_CAPTURE) cap->type = VID_TYPE_CAPTURE; if (cap2.flags & V4L2_FLAG_TUNER) cap->type |= VID_TYPE_TUNER; if (cap2.flags & V4L2_FLAG_DATA_SERVICE) cap->type |= VID_TYPE_TELETEXT; if (cap2.flags & V4L2_FLAG_PREVIEW) cap->type |= VID_TYPE_OVERLAY; if (cap2.flags & V4L2_FLAG_MONOCHROME) cap->type |= VID_TYPE_MONOCHROME; if (fbuf2.flags & V4L2_FBUF_FLAG_PRIMARY) cap->type |= VID_TYPE_FRAMERAM; if (fbuf2.capability & V4L2_FBUF_CAP_CHROMAKEY) cap->type |= VID_TYPE_CHROMAKEY; if (fbuf2.capability & V4L2_FBUF_CAP_CLIPPING) cap->type |= VID_TYPE_CLIPPING; if (fbuf2.capability & V4L2_FBUF_CAP_SCALEUP || fbuf2.capability & V4L2_FBUF_CAP_SCALEDOWN) cap->type |= VID_TYPE_SCALES; cap->channels = cap2.inputs; cap->audios = cap2.audios; cap->maxwidth = cap2.maxwidth; cap->maxheight = cap2.maxheight; cap->minwidth = cap2.minwidth; cap->minheight = cap2.minheight; break; } case VIDIOCGFBUF: /* get frame buffer */ { struct video_buffer *buffer = arg; struct v4l2_framebuffer fbuf2; err = vfl->ioctl(context, VIDIOC_G_FBUF, &fbuf2); if (err < 0) break; buffer->base = fbuf2.base[0]; buffer->height = fbuf2.fmt.height; buffer->width = fbuf2.fmt.width; buffer->depth = fbuf2.fmt.depth; if (fbuf2.fmt.flags & V4L2_FMT_FLAG_BYTESPERLINE) buffer->bytesperline = fbuf2.fmt.bytesperline; else { buffer->bytesperline = (fbuf2.fmt.width * fbuf2.fmt.depth + 7) & 7; buffer->bytesperline >>= 3; } if (fbuf2.fmt.pixelformat == V4L2_PIX_FMT_RGB555) buffer->depth = 15; break; } case VIDIOCSFBUF: /* set frame buffer */ { struct video_buffer *buffer = arg; struct v4l2_framebuffer fbuf2; memset(&fbuf2, 0, sizeof(fbuf2)); fbuf2.base[0] = buffer->base; fbuf2.fmt.height = buffer->height; fbuf2.fmt.width = buffer->width; fbuf2.fmt.depth = buffer->depth; switch (fbuf2.fmt.depth) { case 8: fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB332; break; case 15: fbuf2.fmt.depth = 16; fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB555; break; case 16: fbuf2.fmt.pixelformat = V4L2_PIX_FMT_RGB565; break; case 24: fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR24; break; case 32: fbuf2.fmt.pixelformat = V4L2_PIX_FMT_BGR32; break; } fbuf2.fmt.flags |= V4L2_FMT_FLAG_BYTESPERLINE; fbuf2.fmt.bytesperline = buffer->bytesperline; fbuf2.flags = V4L2_FBUF_FLAG_PRIMARY; err = vfl->ioctl(context, VIDIOC_S_FBUF, &fbuf2); break; } case VIDIOCGWIN: /* get window or capture dimensions */ { struct video_window *win = arg; struct v4l2_window win2; struct v4l2_format fmt2; err = vfl->ioctl(context, VIDIOC_G_WIN, &win2); if (err == 0) { win->x = win2.x; win->y = win2.y; win->width = win2.width; win->height = win2.height; win->chromakey = win2.chromakey; win->clips = NULL; win->clipcount = 0; break; } fmt2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_G_FMT, &fmt2); if (err < 0) break; win->x = 0; win->y = 0; win->width = fmt2.fmt.pix.width; win->height = fmt2.fmt.pix.height; win->chromakey = 0; win->clips = NULL; win->clipcount = 0; break; } case VIDIOCSWIN: /* set window and/or capture dimensions */ { struct video_window *win = arg; struct v4l2_window win2; struct v4l2_format fmt2; fmt2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_G_FMT, &fmt2); if (err == 0) { fmt2.fmt.pix.width = win->width; fmt2.fmt.pix.height = win->height; fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; err = vfl->ioctl(context, VIDIOC_S_FMT, &fmt2); win->width = fmt2.fmt.pix.width; win->height = fmt2.fmt.pix.height; } win2.x = win->x; win2.y = win->y; win2.width = win->width; win2.height = win->height; win2.chromakey = win->chromakey; win2.clips = (void *)win->clips; win2.clipcount = win->clipcount; vfl->ioctl(context, VIDIOC_S_WIN, &win2); break; } case VIDIOCCAPTURE: /* turn on/off preview */ { err = vfl->ioctl(context, VIDIOC_PREVIEW, arg); break; } case VIDIOCGCHAN: /* get input information */ { struct video_channel *chan = arg; struct v4l2_input input2; struct v4l2_standard std2; int sid; input2.index = chan->channel; err = vfl->ioctl(context, VIDIOC_ENUMINPUT, &input2); if (err < 0) break; chan->channel = input2.index; memcpy(chan->name, input2.name, min(sizeof(chan->name), sizeof(input2.name))); chan->name[sizeof(chan->name) - 1] = 0; chan->tuners = (input2.type == V4L2_INPUT_TYPE_TUNER) ? 1 : 0; chan->flags = (chan->tuners) ? VIDEO_VC_TUNER : 0; if (input2.capability & V4L2_INPUT_CAP_AUDIO) chan->flags |= VIDEO_VC_AUDIO; switch (input2.type) { case V4L2_INPUT_TYPE_TUNER: chan->type = VIDEO_TYPE_TV; break; default: case V4L2_INPUT_TYPE_CAMERA: chan->type = VIDEO_TYPE_CAMERA; break; } chan->norm = 0; err = vfl->ioctl(context, VIDIOC_G_STD, &std2); if (err == 0) { sid = v4l2_video_std_confirm(&std2); switch (sid) { case V4L2_STD_NTSC: chan->norm = VIDEO_MODE_NTSC; break; case V4L2_STD_PAL: chan->norm = VIDEO_MODE_PAL; break; case V4L2_STD_SECAM: chan->norm = VIDEO_MODE_SECAM; break; } } break; } case VIDIOCSCHAN: /* set input */ { err = vfl->ioctl(context, VIDIOC_S_INPUT, arg); break; } case VIDIOCGPICT: /* get tone controls & partial capture format */ { struct video_picture *pict = arg; struct v4l2_format fmt2; pict->brightness = get_v4l_control(vfl, context, V4L2_CID_BRIGHTNESS); pict->hue = get_v4l_control(vfl, context, V4L2_CID_HUE); pict->contrast = get_v4l_control(vfl, context, V4L2_CID_CONTRAST); pict->colour = get_v4l_control(vfl, context, V4L2_CID_SATURATION); pict->whiteness = get_v4l_control(vfl, context, V4L2_CID_WHITENESS); fmt2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_G_FMT, &fmt2); if (err < 0) break; pict->depth = fmt2.fmt.pix.depth; pict->palette = pixelformat_to_palette( fmt2.fmt.pix.pixelformat); if (pict->palette == VIDEO_PALETTE_RGB555) pict->depth = 15; break; } case VIDIOCSPICT: /* set tone controls & partial capture format */ { struct video_picture *pict = arg; struct v4l2_format fmt2; set_v4l_control(vfl, context, V4L2_CID_BRIGHTNESS, pict->brightness); set_v4l_control(vfl, context, V4L2_CID_HUE, pict->hue); set_v4l_control(vfl, context, V4L2_CID_CONTRAST, pict->contrast); set_v4l_control(vfl, context, V4L2_CID_SATURATION, pict->colour); set_v4l_control(vfl, context, V4L2_CID_WHITENESS, pict->whiteness); err = 0; fmt2.type = V4L2_BUF_TYPE_CAPTURE; vfl->ioctl(context, VIDIOC_G_FMT, &fmt2); if (fmt2.fmt.pix.pixelformat != palette_to_pixelformat(pict->palette)) { fmt2.fmt.pix.pixelformat = palette_to_pixelformat( pict->palette); fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; err = vfl->ioctl(context, VIDIOC_S_FMT, &fmt2); } break; } case VIDIOCGTUNER: /* get tuner information */ { struct video_tuner *tun = arg; struct v4l2_tuner tun2; int i; int sid; i = find_tuner(vfl, context, tun->tuner); if (i < 0) { err = i; break; } tun2.input = i; err = vfl->ioctl(context, VIDIOC_G_TUNER, &tun2); if (err < 0) break; memcpy(tun->name, tun2.name, min(sizeof(tun->name), sizeof(tun2.name))); tun->name[sizeof(tun->name) - 1] = 0; tun->rangelow = tun2.rangelow; tun->rangehigh = tun2.rangehigh; tun->flags = 0; tun->mode = VIDEO_MODE_AUTO; sid = v4l2_video_std_confirm(&tun2.std); switch (sid) { case V4L2_STD_NTSC: tun->flags = VIDEO_TUNER_NTSC; tun->mode = VIDEO_MODE_NTSC; break; case V4L2_STD_PAL: tun->flags = VIDEO_TUNER_PAL; tun->mode = VIDEO_MODE_PAL; break; case V4L2_STD_SECAM: tun->flags = VIDEO_TUNER_SECAM; tun->mode = VIDEO_MODE_SECAM; break; } if (tun2.capability & V4L2_TUNER_CAP_LOW) tun->flags |= VIDEO_TUNER_LOW; if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) tun->flags |= VIDEO_TUNER_STEREO_ON; tun->signal = tun2.signal; break; } case VIDIOCSTUNER: /* select a tuner input */ { int i; i = find_tuner(vfl, context, (int)arg); if (i < 0) { err = i; break; } err = vfl->ioctl(context, VIDIOC_S_INPUT, &i); break; } case VIDIOCGFREQ: /* get frequency */ { err = vfl->ioctl(context, VIDIOC_G_FREQ, arg); break; } case VIDIOCSFREQ: /* set frequency */ { err = vfl->ioctl(context, VIDIOC_S_FREQ, arg); break; } case VIDIOCGAUDIO: /* get audio properties/controls */ { struct video_audio *aud = arg; struct v4l2_audio aud2; struct v4l2_queryctrl qctrl2; struct v4l2_tuner tun2; int v; err = vfl->ioctl(context, VIDIOC_G_AUDIO, &aud2); if (err < 0) break; memcpy(aud->name, aud2.name, min(sizeof(aud->name), sizeof(aud2.name))); aud->name[sizeof(aud->name) - 1] = 0; aud->audio = aud2.audio; aud->flags = 0; v = get_v4l_control(vfl, context, V4L2_CID_AUDIO_VOLUME); if (v >= 0) { aud->volume = v; aud->flags |= VIDEO_AUDIO_VOLUME; } v = get_v4l_control(vfl, context, V4L2_CID_AUDIO_BASS); if (v >= 0) { aud->bass = v; aud->flags |= VIDEO_AUDIO_BASS; } v = get_v4l_control(vfl, context, V4L2_CID_AUDIO_TREBLE); if (v >= 0) { aud->treble = v; aud->flags |= VIDEO_AUDIO_TREBLE; } v = get_v4l_control(vfl, context, V4L2_CID_AUDIO_BALANCE); if (v >= 0) { aud->balance = v; aud->flags |= VIDEO_AUDIO_BALANCE; } v = get_v4l_control(vfl, context, V4L2_CID_AUDIO_MUTE); if (v >= 0) { if (v) aud->flags |= VIDEO_AUDIO_MUTE; aud->flags |= VIDEO_AUDIO_MUTABLE; } aud->step = 1; qctrl2.id = V4L2_CID_AUDIO_VOLUME; if (vfl->ioctl(context, VIDIOC_QUERYCTRL, &qctrl2) == 0 && !(qctrl2.flags & V4L2_CTRL_FLAG_DISABLED)) aud->step = qctrl2.step; aud->mode = 0; err = vfl->ioctl(context, VIDIOC_G_TUNER, &tun2); if (err < 0) { err = 0; break; } if (tun2.rxsubchans & V4L2_TUNER_SUB_LANG2) aud->mode = VIDEO_SOUND_LANG1 | VIDEO_SOUND_LANG2; else if (tun2.rxsubchans & V4L2_TUNER_SUB_STEREO) aud->mode = VIDEO_SOUND_STEREO; else if (tun2.rxsubchans & V4L2_TUNER_SUB_MONO) aud->mode = VIDEO_SOUND_MONO; break; } case VIDIOCSAUDIO: /* set audio controls */ { struct video_audio *aud = arg; struct v4l2_audio aud2; struct v4l2_tuner tun2; int i; aud2.audio = aud->audio; err = vfl->ioctl(context, VIDIOC_S_AUDIO, &aud2); if (err < 0) break; set_v4l_control(vfl, context, V4L2_CID_AUDIO_VOLUME, aud->volume); set_v4l_control(vfl, context, V4L2_CID_AUDIO_BASS, aud->bass); set_v4l_control(vfl, context, V4L2_CID_AUDIO_TREBLE, aud->treble); set_v4l_control(vfl, context, V4L2_CID_AUDIO_BALANCE, aud->balance); set_v4l_control(vfl, context, V4L2_CID_AUDIO_MUTE, !!(aud->flags & VIDEO_AUDIO_MUTE)); err = vfl->ioctl(context, VIDIOC_G_INPUT, &i); if (err < 0) { err = 0; break; } tun2.input = i; err = vfl->ioctl(context, VIDIOC_G_TUNER, &tun2); if (err == 0) { switch (aud->mode) { default: case VIDEO_SOUND_MONO: case VIDEO_SOUND_LANG1: tun2.audmode = V4L2_TUNER_MODE_MONO; break; case VIDEO_SOUND_STEREO: tun2.audmode = V4L2_TUNER_MODE_STEREO; break; case VIDEO_SOUND_LANG2: tun2.audmode = V4L2_TUNER_MODE_LANG2; break; } vfl->ioctl(context, VIDIOC_S_TUNER, &tun2); } err = 0; break; } case VIDIOCGMBUF: /* get mmap parameters */ { struct video_mbuf *mbuf = arg; struct v4l2_requestbuffers reqbuf2; struct v4l2_buffer buf2; struct v4l2_format fmt2, fmt2o; struct v4l2_capability cap2; int i; /* Set the format to maximum dimensions */ if ((err = vfl->ioctl(context, VIDIOC_QUERYCAP, &cap2)) < 0) break; fmt2o.type = V4L2_BUF_TYPE_CAPTURE; if ((err = vfl->ioctl(context, VIDIOC_G_FMT, &fmt2o)) < 0) break; fmt2 = fmt2o; fmt2.fmt.pix.width = cap2.maxwidth; fmt2.fmt.pix.height = cap2.maxheight; fmt2.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; if ((err = vfl->ioctl(context, VIDIOC_S_FMT, &fmt2)) < 0) break; reqbuf2.count = 2; /* v4l always used two buffers */ reqbuf2.type = V4L2_BUF_TYPE_CAPTURE | V4L2_BUF_REQ_CONTIG; err = vfl->ioctl(context, VIDIOC_REQBUFS, &reqbuf2); if (err < 0 || reqbuf2.count < 2 || reqbuf2.type != (V4L2_BUF_TYPE_CAPTURE | V4L2_BUF_REQ_CONTIG)) {/* Driver doesn't support v4l back-compatibility */ fmt2o.fmt.pix.flags |= V4L2_FMT_FLAG_INTERLACED; vfl->ioctl(context, VIDIOC_S_FMT, &fmt2o); reqbuf2.count = 1; reqbuf2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_REQBUFS, &reqbuf2); if (err < 0) { err = -EINVAL; break; } printk(KERN_INFO"V4L2: Device \"%s\" doesn't support" " v4l memory mapping\n", vfl->name); } buf2.index = 0; buf2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_QUERYBUF, &buf2); mbuf->size = buf2.length * reqbuf2.count; mbuf->frames = reqbuf2.count; memset(mbuf->offsets, 0, sizeof(mbuf->offsets)); for (i = 0; i < mbuf->frames; ++i) mbuf->offsets[i] = i * buf2.length; break; } case VIDIOCMCAPTURE: /* capture a frame */ { struct video_mmap *mm = arg; struct v4l2_buffer buf2; struct v4l2_format fmt2; fmt2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_G_FMT, &fmt2); if (err < 0) break; 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.flags |= V4L2_FMT_FLAG_INTERLACED; err = vfl->ioctl(context, VIDIOC_S_FMT, &fmt2); if (err < 0) break; } buf2.index = mm->frame; buf2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_QUERYBUF, &buf2); if (err < 0) break; err = vfl->ioctl(context, VIDIOC_QBUF, &buf2); if (err < 0) break; vfl->ioctl(context, VIDIOC_STREAMON, &buf2.type); break; } case VIDIOCSYNC: /* wait for a frame */ { int *i = arg; struct v4l2_buffer buf2; buf2.index = *i; buf2.type = V4L2_BUF_TYPE_CAPTURE; err = vfl->ioctl(context, VIDIOC_QUERYBUF, &buf2); if (err < 0) /* No such buffer */ break; if (!(buf2.flags & V4L2_BUF_FLAG_MAPPED)) {/* Buffer is not mapped */ err = -EINVAL; break; } /* 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 = simple_select(file); if (err < 0 || /* error or sleep was interrupted */ err == 0) /* timeout? Shouldn't occur. */ break; vfl->ioctl(context, VIDIOC_QUERYBUF, &buf2); } if (!(buf2.flags & V4L2_BUF_FLAG_DONE)) /* not done */ break; do { err = vfl->ioctl(context, VIDIOC_DQBUF, &buf2); } while (err == 0 && buf2.index != *i); break; } case VIDIOCGUNIT: /* get related device minors */ /* No translation */ break; case VIDIOCGCAPTURE: /* */ /* No translation, yet... */ printk(KERN_INFO"V4L2: VIDIOCGCAPTURE not implemented. " "Send patches to bdirks@pacbell.net :-)\n"); break; case VIDIOCSCAPTURE: /* */ /* No translation, yet... */ printk(KERN_INFO"V4L2: VIDIOCSCAPTURE not implemented. " "Send patches to bdirks@pacbell.net :-)\n"); break; } return err; } /* * D E V I C E R E G I S T R A T I O N * * Video for Linux Two device drivers request registration here. */ int v4l2_register_device(struct v4l2_device *vfl) { int i = 0; int err; if (vfl == NULL) { printk(KERN_ERR"V4L2: v4l2_register_device() passed" " a NULL pointer!\n"); return -1; } i = vfl->minor; if (vfl->open == NULL) { printk(KERN_ERR "V4L2: Device %d has no open method\n", i); return -1; } if (i < 0 || i >= V4L2_NUM_DEVICES) { printk(KERN_ERR"V4L2: Minor value %d is out of range\n", i); return -1; } if (v4l2_device[i] != NULL) { printk(KERN_ERR"V4L2: %s and %s have both been assigned" " minor %d\n", v4l2_device[i]->name, vfl->name, i); return 1; } v4l2_device[i] = vfl; /* The init call may sleep so we book the slot out then call */ MOD_INC_USE_COUNT; err = 0; if (vfl->initialize) err = vfl->initialize(vfl); if (err < 0) { printk(KERN_ERR "V4L2: %s initialize method failed\n", vfl->name); v4l2_device[i] = NULL; MOD_DEC_USE_COUNT; return err; } vfl->busy = 0; vfl->name[sizeof(vfl->name) - 1] = 0; printk(KERN_INFO"V4L2: Registered \"%s\" as char device %d, %d\n", vfl->name, v4l2_major, vfl->minor); memset(vfl->v4l2_reserved, 0, sizeof(vfl->v4l2_reserved)); vfl->v4l2_priv = NULL; return 0; } /* * Unregister an unused video for linux device */ void v4l2_unregister_device(struct v4l2_device *vfl) { if (vfl->minor < 0 || vfl->minor >= V4L2_NUM_DEVICES || v4l2_device[vfl->minor] != vfl) { printk(KERN_ERR"V4L2: bad unregister\n"); return; } v4l2_device[vfl->minor] = NULL; MOD_DEC_USE_COUNT; } #ifdef CONFIG_PROC_FS /* * / p r o c / v i d e o d e v H A N D L E R */ /* Original /proc file code from Erik Walthinsen */ static char *device_types[] = { "capture", "codec", "output", "effects", "vbi", "vtr", "teletext", "radio", "undef", "undef", "undef", "undef", }; static int video_read_proc(char *buf, char **start, off_t offset, int len, int unused) { struct v4l2_device *vfl; int i; char *t; len = 0; len += sprintf(buf, "Video for Linux Two: V%d.%d alpha." " Major device: %d\n", V4L2_MAJOR_VERSION, V4L2_MINOR_VERSION, v4l2_major); //len += sprintf(buf+len,"minor: type busy name\n"); for (i = 0; i < V4L2_NUM_DEVICES; i++) { vfl = v4l2_device[i]; if (vfl == NULL) continue; if (len > (PAGE_SIZE - 80)) return len; if (vfl->type >= 0 && vfl->type < sizeof(device_types)/sizeof(char*)) t = device_types[vfl->type]; else if (vfl->type >= V4L2_TYPE_PRIVATE) t = "private"; else t = "undef"; len += sprintf(buf+len, "%5d: %-9s %3d %s\n", vfl->minor, t, vfl->busy, vfl->name); } return len; } /* proc file for /proc/videodev */ static struct proc_dir_entry video_proc_entry = { 0, 8, "videodev", S_IFREG | S_IRUGO, 1, 0, 0, 0, NULL, &video_read_proc }; #endif /* * V I D E O F O R L I N U X T W O I N I T I A L I Z A T I O N */ static struct file_operations video_fops = { video_llseek, video_read, video_write, NULL, /* readdir */ video_poll, video_ioctl, video_mmap, video_open, #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,118) NULL, #endif video_release }; /* * Initialize Video for Linux Two */ int videodev_init(void) { int i; printk(KERN_INFO"Video for Linux Two: V%d.%d alpha." " Major device: %d\n", V4L2_MAJOR_VERSION, V4L2_MINOR_VERSION, v4l2_major); if (register_chrdev(v4l2_major, "v4l2", &video_fops)) { printk("V4L2: Unable to get major %d\n", v4l2_major); return -EIO; } /* make sure there's a way to tell if a device is not there */ for (i = 0; i < V4L2_NUM_DEVICES; i++) v4l2_device[i] = NULL; #ifdef CONFIG_PROC_FS proc_register(&proc_root, &video_proc_entry); #endif masterclock = NULL; return 0; } #ifdef MODULE int init_module(void) { return videodev_init(); } void cleanup_module(void) { #ifdef CONFIG_PROC_FS proc_unregister(&proc_root, video_proc_entry.low_ino); #endif unregister_chrdev(v4l2_major, "v4l2"); } #endif /* * * V 4 L 2 D R I V E R H E L P E R A P I * */ void v4l2_version(int *major, int *minor) { *major = V4L2_MAJOR_VERSION; *minor = V4L2_MINOR_VERSION; } int v4l2_major_number(void) { return v4l2_major; } struct v4l2_device * v4l2_device_from_minor(int minor) { if (minor < 0 || minor >= V4L2_NUM_DEVICES) return NULL; return v4l2_device[minor]; } struct v4l2_device * v4l2_device_from_file(struct file *file) { if (file == NULL) return NULL; return v4l2_device_from_minor(MINOR(file->f_dentry->d_inode->i_rdev)); } void * v4l2_openid_from_file(struct file *file) { if (file == NULL) return NULL; return file->private_data; } static struct mm_struct * find_init_mm(void) { static struct mm_struct *mm; struct task_struct *p; if (mm) return mm; for (p = current; p && (p = p->next_task) != current; ) if (p->pid == 0) break; mm = (p) ? p->mm : NULL; return mm; } /* Useful for using vmalloc()ed memory as DMA target */ unsigned long v4l2_vmalloc_to_bus(void *virt) { pgd_t *pgd; pmd_t *pmd; pte_t *pte; unsigned long a = (unsigned long)virt; struct mm_struct *mm = find_init_mm(); if (mm == NULL || pgd_none(*(pgd = pgd_offset(mm, a))) || pmd_none(*(pmd = pmd_offset(pgd, a))) || pte_none(*(pte = pte_offset(pmd, a)))) return 0; return virt_to_bus((void *)pte_page(*pte)) + (a & (PAGE_SIZE - 1)); } /* Useful for a nopage handler when mmap()ing vmalloc()ed memory */ unsigned long v4l2_vmalloc_to_page(void *virt) { pgd_t *pgd; pmd_t *pmd; pte_t *pte; unsigned long a = (unsigned long)virt; struct mm_struct *mm = find_init_mm(); if (mm == NULL || pgd_none(*(pgd = pgd_offset(current->mm, a))) || pmd_none(*(pmd = pmd_offset(pgd, a))) || pte_none(*(pte = pte_offset(pmd, a)))) return 0; return pte_page(*pte); } /* * Simple queue management */ static rwlock_t rw_lock_unlocked = RW_LOCK_UNLOCKED; void v4l2_q_init(struct v4l2_queue *q) { if (q == NULL) return; q->qlock = rw_lock_unlocked; q->forw = (struct v4l2_q_node *)q; q->back = (struct v4l2_q_node *)q; } void v4l2_q_add_head(struct v4l2_queue *q, struct v4l2_q_node *node) { unsigned long flags; if (q == NULL || node == NULL) return; if (q->forw == NULL || q->back == NULL) v4l2_q_init(q); write_lock_irqsave(&(q->qlock), flags); node->forw = q->forw; node->back = (struct v4l2_q_node *)q; q->forw->back = node; q->forw = node; write_unlock_irqrestore(&(q->qlock), flags); } void v4l2_q_add_tail(struct v4l2_queue *q, struct v4l2_q_node *node) { unsigned long flags; if (q == NULL || node == NULL) return; if (q->forw == NULL || q->back == NULL) v4l2_q_init(q); write_lock_irqsave(&(q->qlock), flags); node->forw = (struct v4l2_q_node *)q; node->back = q->back; q->back->forw = node; q->back = node; write_unlock_irqrestore(&(q->qlock), flags); } void * v4l2_q_del_head(struct v4l2_queue *q) { unsigned long flags; struct v4l2_q_node *node; if (q == NULL) return NULL; write_lock_irqsave(&(q->qlock), flags); if (q->forw == NULL || q->back == NULL || q->forw == (struct v4l2_q_node *)q || q->back == (struct v4l2_q_node *)q) { write_unlock_irqrestore(&(q->qlock), flags); return NULL; } node = q->forw; node->forw->back = (struct v4l2_q_node *)q; q->forw = node->forw; node->forw = NULL; node->back = NULL; write_unlock_irqrestore(&(q->qlock), flags); return node; } void * v4l2_q_del_tail(struct v4l2_queue *q) { unsigned long flags; struct v4l2_q_node *node; if (q == NULL) return NULL; write_lock_irqsave(&(q->qlock), flags); if (q->forw == NULL || q->back == NULL || q->forw == (struct v4l2_q_node *)q || q->back == (struct v4l2_q_node *)q) { write_unlock_irqrestore(&(q->qlock), flags); return NULL; } node = q->back; node->back->forw = (struct v4l2_q_node *)q; q->back = node->back; node->forw = NULL; node->back = NULL; write_unlock_irqrestore(&(q->qlock), flags); return node; } void * v4l2_q_peek_head(struct v4l2_queue *q) { unsigned long flags; struct v4l2_q_node *node; read_lock_irqsave(&(q->qlock), flags); if (q == NULL || q->forw == NULL || q->forw == (struct v4l2_q_node *)q) { read_unlock_irqrestore(&(q->qlock), flags); return NULL; } node = q->forw; read_unlock_irqrestore(&(q->qlock), flags); return node; } void * v4l2_q_peek_tail(struct v4l2_queue *q) { unsigned long flags; struct v4l2_q_node *node; read_lock_irqsave(&(q->qlock), flags); if (q == NULL || q->back == NULL || q->back == (struct v4l2_q_node *)q) { read_unlock_irqrestore(&(q->qlock), flags); return NULL; } node = q->back; read_unlock_irqrestore(&(q->qlock), flags); return node; } void * v4l2_q_yank_node(struct v4l2_queue *q, struct v4l2_q_node *node) { unsigned long flags; struct v4l2_q_node *t; if (v4l2_q_peek_head(q) == NULL || node == NULL) return NULL; write_lock_irqsave(&(q->qlock), flags); for (t = q->forw; t != (struct v4l2_q_node *)q; t = t->forw) if (t == node) { node->back->forw = node->forw; node->forw->back = node->back; node->forw = NULL; node->back = NULL; write_unlock_irqrestore(&(q->qlock), flags); return node; } write_unlock_irqrestore(&(q->qlock), flags); return NULL; } /* * Math functions */ u32 v4l2_math_div6432(u64 a, u32 d, u32 *r) { u32 q, m; #ifdef __i386__ /* Danger: This function will fault if the quotient exceeds (1<<32) - 1 */ __asm__ __volatile__ ( " movl %2,%%eax\n" " movl %3,%%edx\n" " divl %4\n" " movl %%eax,%0\n" " movl %%edx,%1\n" : "=g" (q), "=g" (m) : "g" ((u32)a), "g" ((u32)(a >> 32)), "g" (d) : "eax", "edx" ); #else q = a / d; m = a % d; #endif if (r) *r = m; return q; } unsigned long v4l2_timestamp_divide(stamp_t t, unsigned long p_100ns) { /* Note: 't' is in 1ns units, 'p_100ns' is in 100ns units, */ /* and the quotient is rounded */ u64 p; p = (u64)p_100ns * 100; /* 1ns units */ t >>= 6; /* /64 to allow p_100ns longer than 4 secs. */ p >>= 6; /* to keep quotient the same */ return v4l2_math_div6432((u64)t + (p >> 1), (u32)p, NULL); } /* Force the timestamp to be an integer multiple of p_100ns */ unsigned long v4l2_timestamp_correct(stamp_t *t, unsigned long p_100ns) { /* Note: 't' is in 1ns units, 'p_100ns' is in 100ns units */ unsigned long n; n = v4l2_timestamp_divide((u64)*t, p_100ns); *t = (u64)p_100ns * n * 100; return n; } /* * Master clock operations */ int v4l2_masterclock_register(struct v4l2_clock *clock) { if (clock == NULL || clock->gettime == NULL) return -1; if (masterclock != NULL) return -1; masterclock = clock; MOD_INC_USE_COUNT; return 0; } void v4l2_masterclock_unregister(struct v4l2_clock *clock) { if (clock != masterclock) return; masterclock = NULL; MOD_DEC_USE_COUNT; } void v4l2_masterclock_gettime(stamp_t *curr) { if (masterclock) masterclock->gettime(curr); else { #if defined(CONFIG_UST) || defined(CONFIG_UST_MODULE) ust_gettime(curr); #else struct timeval t; stamp_t stamp; do_gettimeofday(&t); stamp = (stamp_t)t.tv_sec * 1000000 + t.tv_usec; stamp *= 1000; *curr = stamp; #endif } } /* * Video Standard Operations (contributed by Michael Schimek) */ /* This is the recommended method to deal with the framerate fields. More sophisticated drivers will access the fields directly. */ unsigned int v4l2_video_std_fps(struct v4l2_standard *vs) { if (vs->framerate.numerator > 0) return (((vs->framerate.denominator << 8) / vs->framerate.numerator) + (1 << 7)) / (1 << 8); return 0; } /* Compute the time per frame in 100ns units */ unsigned long v4l2_video_std_tpf(struct v4l2_standard *vs) { return v4l2_math_div6432( (u64)vs->framerate.numerator * 10000000 + vs->framerate.denominator / 2, vs->framerate.denominator, NULL); } /* Used only in v4l2_video_std_confirm() */ static void catc1p2e6(__u8 *s, char c, int n) { n /= 10000; sprintf(s + strlen(s), "%c%d.%02d", c, n / 100, n % 100); } /* Verify the validity of the parameters of a v4l2_standard structure and create the name and id from the other fields. It does not relieve a driver from examining if it can fulfill the request. Returns an errno < 0 if inconsistent, 0 if an unknown but maybe usable format, or the V4L2_STD_XXX_X value if a known standard. */ int v4l2_video_std_confirm(struct v4l2_standard *vs) { unsigned int rate = 0; unsigned int lines = vs->framelines; int std = 0; strcpy(vs->name, "Unknown"); if (vs->reserved1 || vs->reserved2) return -EINVAL; if (vs->framerate.numerator > 0 && vs->framerate.denominator > 0) rate = v4l2_video_std_fps(vs); if (vs->framelines >= 624 && vs->framelines <= 626) lines = 625; else if (vs->framelines >= 524 && vs->framelines <= 526) lines = 525; if (rate == 0 || lines == 0 || rate > 200) return -EINVAL; switch (vs->colorstandard) { case V4L2_COLOR_STD_PAL: strcpy(vs->name, "PAL"); if (rate == 25 && lines == 625) switch (vs->colorstandard_data.pal.colorsubcarrier) { case V4L2_COLOR_SUBC_PAL_N: strcpy(vs->name, "PAL-N"); if (vs->transmission & ~V4L2_TRANSM_STD_N) return -EINVAL; return V4L2_STD_PAL_N; case V4L2_COLOR_SUBC_PAL: if (vs->transmission & ~(V4L2_TRANSM_STD_B | V4L2_TRANSM_STD_G | V4L2_TRANSM_STD_H | V4L2_TRANSM_STD_I | V4L2_TRANSM_STD_D)) return -EINVAL; std = V4L2_STD_PAL; goto addtransm; } else if (rate == 30 && lines == 525) switch (vs->colorstandard_data.pal.colorsubcarrier) { case V4L2_COLOR_SUBC_PAL_M: strcpy(vs->name, "PAL-M"); if (vs->transmission & ~V4L2_TRANSM_STD_M) return -EINVAL; return V4L2_STD_PAL_M; case V4L2_COLOR_SUBC_PAL: strcpy(vs->name, "PAL-60"); if (vs->transmission) return -EINVAL; return V4L2_STD_PAL_60; } if (vs->transmission) return -EINVAL; catc1p2e6(vs->name, ' ', vs->colorstandard_data.pal.colorsubcarrier); break; case V4L2_COLOR_STD_NTSC: strcpy(vs->name, "NTSC"); if (rate == 25 && lines == 625) switch (vs->colorstandard_data.ntsc.colorsubcarrier) { case V4L2_COLOR_SUBC_NTSC: strcpy(vs->name, "NTSC-N"); if (vs->transmission & ~V4L2_TRANSM_STD_N) return -EINVAL; return V4L2_STD_NTSC_N; } else if (rate == 30 && lines == 525) switch (vs->colorstandard_data.ntsc.colorsubcarrier) { case V4L2_COLOR_SUBC_NTSC: if (vs->transmission & ~V4L2_TRANSM_STD_M) return -EINVAL; std = V4L2_STD_NTSC; goto addtransm; case V4L2_COLOR_SUBC_PAL: strcpy(vs->name, "NTSC-44"); if (vs->transmission) return -EINVAL; return V4L2_STD_NTSC_44; } if (vs->transmission) return -EINVAL; catc1p2e6(vs->name, ' ', vs->colorstandard_data.ntsc.colorsubcarrier); break; case V4L2_COLOR_STD_SECAM: strcpy(vs->name, "SECAM"); if (rate == 25 && lines == 625) if (vs->colorstandard_data.secam.f0b == V4L2_COLOR_SUBC_SECAMB && vs->colorstandard_data.secam.f0r == V4L2_COLOR_SUBC_SECAMR) { if (vs->transmission & ~(V4L2_TRANSM_STD_B | V4L2_TRANSM_STD_D | V4L2_TRANSM_STD_G | V4L2_TRANSM_STD_K | V4L2_TRANSM_STD_K1 | V4L2_TRANSM_STD_L)) return -EINVAL; std = V4L2_STD_SECAM; goto addtransm; } if (vs->transmission) return -EINVAL; catc1p2e6(vs->name, ' ', vs->colorstandard_data.secam.f0b); catc1p2e6(vs->name, '/', vs->colorstandard_data.secam.f0r); break; default: return -EINVAL; } sprintf(vs->name + strlen(vs->name), " %d/%d", vs->framelines, rate); return std; addtransm: if (vs->transmission) strcat(vs->name, "-"); if (vs->transmission & V4L2_TRANSM_STD_B) strcat(vs->name, "B/"); if (vs->transmission & V4L2_TRANSM_STD_G) strcat(vs->name, "G/"); if (vs->transmission & V4L2_TRANSM_STD_H) strcat(vs->name, "H/"); if (vs->transmission & V4L2_TRANSM_STD_I) strcat(vs->name, "I/"); if (vs->transmission & V4L2_TRANSM_STD_D) strcat(vs->name, "D/"); if (vs->transmission & V4L2_TRANSM_STD_K) strcat(vs->name, "K/"); if (vs->transmission & V4L2_TRANSM_STD_K1) strcat(vs->name, "K1/"); if (vs->transmission & V4L2_TRANSM_STD_L) strcat(vs->name, "L/"); if (vs->transmission & V4L2_TRANSM_STD_M) strcat(vs->name, "M/"); if (vs->transmission & V4L2_TRANSM_STD_N) strcat(vs->name, "N/"); if (vs->name[strlen(vs->name) - 1] == '/') vs->name[strlen(vs->name) - 1] = 0; return std; } /* Fill in the fields of a v4l2_standard structure according to the 'id' and 'transmission' parameters. Returns negative on error. */ int v4l2_video_std_construct(struct v4l2_standard *vs, int id, __u32 transmission) { memset(vs, 0, sizeof(struct v4l2_standard)); vs->framerate.numerator = 1; vs->framerate.denominator = 25; vs->framelines = 625; switch (id) { case V4L2_STD_PAL_60: vs->framerate.numerator = 1001; vs->framerate.denominator = 30000; vs->framelines = 525; /* fall thru */ case V4L2_STD_PAL: vs->colorstandard = V4L2_COLOR_STD_PAL; vs->colorstandard_data.pal.colorsubcarrier = V4L2_COLOR_SUBC_PAL; break; case V4L2_STD_PAL_M: vs->framerate.numerator = 1001; vs->framerate.denominator = 30000; vs->framelines = 525; vs->colorstandard = V4L2_COLOR_STD_PAL; vs->colorstandard_data.pal.colorsubcarrier = V4L2_COLOR_SUBC_PAL_M; break; case V4L2_STD_PAL_N: vs->colorstandard = V4L2_COLOR_STD_PAL; vs->colorstandard_data.pal.colorsubcarrier = V4L2_COLOR_SUBC_PAL_N; break; case V4L2_STD_NTSC: vs->framerate.numerator = 1001; vs->framerate.denominator = 30000; vs->framelines = 525; /* fall thru */ case V4L2_STD_NTSC_N: vs->colorstandard = V4L2_COLOR_STD_NTSC; vs->colorstandard_data.ntsc.colorsubcarrier = V4L2_COLOR_SUBC_NTSC; break; case V4L2_STD_NTSC_44: vs->framerate.numerator = 1001; vs->framerate.denominator = 30000; vs->framelines = 525; vs->colorstandard = V4L2_COLOR_STD_NTSC; vs->colorstandard_data.ntsc.colorsubcarrier = V4L2_COLOR_SUBC_PAL; break; case V4L2_STD_SECAM: vs->colorstandard = V4L2_COLOR_STD_SECAM; vs->colorstandard_data.secam.f0b = V4L2_COLOR_SUBC_SECAMB; vs->colorstandard_data.secam.f0r = V4L2_COLOR_SUBC_SECAMR; break; default: return -EINVAL; } vs->transmission = transmission; return v4l2_video_std_confirm(vs); } /*---------------------------------------*/ EXPORT_SYMBOL(v4l2_register_device); EXPORT_SYMBOL(v4l2_unregister_device); EXPORT_SYMBOL(v4l2_version); EXPORT_SYMBOL(v4l2_major_number); EXPORT_SYMBOL(v4l2_device_from_minor); EXPORT_SYMBOL(v4l2_device_from_file); EXPORT_SYMBOL(v4l2_openid_from_file); EXPORT_SYMBOL(v4l2_vmalloc_to_bus); EXPORT_SYMBOL(v4l2_vmalloc_to_page); EXPORT_SYMBOL(v4l2_q_init); EXPORT_SYMBOL(v4l2_q_add_head); EXPORT_SYMBOL(v4l2_q_add_tail); EXPORT_SYMBOL(v4l2_q_del_head); EXPORT_SYMBOL(v4l2_q_del_tail); EXPORT_SYMBOL(v4l2_q_peek_head); EXPORT_SYMBOL(v4l2_q_peek_tail); EXPORT_SYMBOL(v4l2_q_yank_node); EXPORT_SYMBOL(v4l2_math_div6432); EXPORT_SYMBOL(v4l2_timestamp_divide); EXPORT_SYMBOL(v4l2_timestamp_correct); EXPORT_SYMBOL(v4l2_masterclock_register); EXPORT_SYMBOL(v4l2_masterclock_unregister); EXPORT_SYMBOL(v4l2_masterclock_gettime); EXPORT_SYMBOL(v4l2_video_std_fps); EXPORT_SYMBOL(v4l2_video_std_tpf); EXPORT_SYMBOL(v4l2_video_std_confirm); EXPORT_SYMBOL(v4l2_video_std_construct);