1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261 |
- /* Linux driver for Philips webcam
- USB and Video4Linux interface part.
- (C) 1999-2004 Nemosoft Unv.
- (C) 2004-2006 Luc Saillard (luc@saillard.org)
- NOTE: this version of pwc is an unofficial (modified) release of pwc & pcwx
- driver and thus may have bugs that are not present in the original version.
- Please send bug reports and support requests to <luc@saillard.org>.
- The decompression routines have been implemented by reverse-engineering the
- Nemosoft binary pwcx module. Caveat emptor.
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
- #include <linux/errno.h>
- #include <linux/init.h>
- #include <linux/mm.h>
- #include <linux/module.h>
- #include <linux/poll.h>
- #include <linux/slab.h>
- #include <linux/vmalloc.h>
- #include <asm/io.h>
- #include "pwc.h"
- static struct v4l2_queryctrl pwc_controls[] = {
- {
- .id = V4L2_CID_BRIGHTNESS,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Brightness",
- .minimum = 0,
- .maximum = 128,
- .step = 1,
- .default_value = 64,
- },
- {
- .id = V4L2_CID_CONTRAST,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contrast",
- .minimum = 0,
- .maximum = 64,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_SATURATION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Saturation",
- .minimum = -100,
- .maximum = 100,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_GAMMA,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gamma",
- .minimum = 0,
- .maximum = 32,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_RED_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Red Gain",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_BLUE_BALANCE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Blue Gain",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_AUTO_WHITE_BALANCE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto White Balance",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_EXPOSURE,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Shutter Speed (Exposure)",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 200,
- },
- {
- .id = V4L2_CID_AUTOGAIN,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto Gain Enabled",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 1,
- },
- {
- .id = V4L2_CID_GAIN,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Gain Level",
- .minimum = 0,
- .maximum = 256,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_SAVE_USER,
- .type = V4L2_CTRL_TYPE_BUTTON,
- .name = "Save User Settings",
- .minimum = 0,
- .maximum = 0,
- .step = 0,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_RESTORE_USER,
- .type = V4L2_CTRL_TYPE_BUTTON,
- .name = "Restore User Settings",
- .minimum = 0,
- .maximum = 0,
- .step = 0,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_RESTORE_FACTORY,
- .type = V4L2_CTRL_TYPE_BUTTON,
- .name = "Restore Factory Settings",
- .minimum = 0,
- .maximum = 0,
- .step = 0,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_COLOUR_MODE,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Colour mode",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_AUTOCONTOUR,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Auto contour",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_CONTOUR,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Contour",
- .minimum = 0,
- .maximum = 63,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_BACKLIGHT,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Backlight compensation",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_FLICKERLESS,
- .type = V4L2_CTRL_TYPE_BOOLEAN,
- .name = "Flickerless",
- .minimum = 0,
- .maximum = 1,
- .step = 1,
- .default_value = 0,
- },
- {
- .id = V4L2_CID_PRIVATE_NOISE_REDUCTION,
- .type = V4L2_CTRL_TYPE_INTEGER,
- .name = "Noise reduction",
- .minimum = 0,
- .maximum = 3,
- .step = 1,
- .default_value = 0,
- },
- };
- static void pwc_vidioc_fill_fmt(const struct pwc_device *pdev, struct v4l2_format *f)
- {
- memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));
- f->fmt.pix.width = pdev->view.x;
- f->fmt.pix.height = pdev->view.y;
- f->fmt.pix.field = V4L2_FIELD_NONE;
- if (pdev->vpalette == VIDEO_PALETTE_YUV420P) {
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_YUV420;
- f->fmt.pix.bytesperline = (f->fmt.pix.width * 3)/2;
- f->fmt.pix.sizeimage = f->fmt.pix.height * f->fmt.pix.bytesperline;
- } else {
- /* vbandlength contains 4 lines ... */
- f->fmt.pix.bytesperline = pdev->vbandlength/4;
- f->fmt.pix.sizeimage = pdev->frame_size + sizeof(struct pwc_raw_frame);
- if (DEVICE_USE_CODEC1(pdev->type))
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC1;
- else
- f->fmt.pix.pixelformat = V4L2_PIX_FMT_PWC2;
- }
- PWC_DEBUG_IOCTL("pwc_vidioc_fill_fmt() "
- "width=%d, height=%d, bytesperline=%d, sizeimage=%d, pixelformat=%c%c%c%c\n",
- f->fmt.pix.width,
- f->fmt.pix.height,
- f->fmt.pix.bytesperline,
- f->fmt.pix.sizeimage,
- (f->fmt.pix.pixelformat)&255,
- (f->fmt.pix.pixelformat>>8)&255,
- (f->fmt.pix.pixelformat>>16)&255,
- (f->fmt.pix.pixelformat>>24)&255);
- }
- /* ioctl(VIDIOC_TRY_FMT) */
- static int pwc_vidioc_try_fmt(struct pwc_device *pdev, struct v4l2_format *f)
- {
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- PWC_DEBUG_IOCTL("Bad video type must be V4L2_BUF_TYPE_VIDEO_CAPTURE\n");
- return -EINVAL;
- }
- switch (f->fmt.pix.pixelformat) {
- case V4L2_PIX_FMT_YUV420:
- break;
- case V4L2_PIX_FMT_PWC1:
- if (DEVICE_USE_CODEC23(pdev->type)) {
- PWC_DEBUG_IOCTL("codec1 is only supported for old pwc webcam\n");
- return -EINVAL;
- }
- break;
- case V4L2_PIX_FMT_PWC2:
- if (DEVICE_USE_CODEC1(pdev->type)) {
- PWC_DEBUG_IOCTL("codec23 is only supported for new pwc webcam\n");
- return -EINVAL;
- }
- break;
- default:
- PWC_DEBUG_IOCTL("Unsupported pixel format\n");
- return -EINVAL;
- }
- if (f->fmt.pix.width > pdev->view_max.x)
- f->fmt.pix.width = pdev->view_max.x;
- else if (f->fmt.pix.width < pdev->view_min.x)
- f->fmt.pix.width = pdev->view_min.x;
- if (f->fmt.pix.height > pdev->view_max.y)
- f->fmt.pix.height = pdev->view_max.y;
- else if (f->fmt.pix.height < pdev->view_min.y)
- f->fmt.pix.height = pdev->view_min.y;
- return 0;
- }
- /* ioctl(VIDIOC_SET_FMT) */
- static int pwc_vidioc_set_fmt(struct pwc_device *pdev, struct v4l2_format *f)
- {
- int ret, fps, snapshot, compression, pixelformat;
- ret = pwc_vidioc_try_fmt(pdev, f);
- if (ret<0)
- return ret;
- pixelformat = f->fmt.pix.pixelformat;
- compression = pdev->vcompression;
- snapshot = 0;
- fps = pdev->vframes;
- if (f->fmt.pix.priv) {
- compression = (f->fmt.pix.priv & PWC_QLT_MASK) >> PWC_QLT_SHIFT;
- snapshot = !!(f->fmt.pix.priv & PWC_FPS_SNAPSHOT);
- fps = (f->fmt.pix.priv & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
- if (fps == 0)
- fps = pdev->vframes;
- }
- if (pixelformat == V4L2_PIX_FMT_YUV420)
- pdev->vpalette = VIDEO_PALETTE_YUV420P;
- else
- pdev->vpalette = VIDEO_PALETTE_RAW;
- PWC_DEBUG_IOCTL("Try to change format to: width=%d height=%d fps=%d "
- "compression=%d snapshot=%d format=%c%c%c%c\n",
- f->fmt.pix.width, f->fmt.pix.height, fps,
- compression, snapshot,
- (pixelformat)&255,
- (pixelformat>>8)&255,
- (pixelformat>>16)&255,
- (pixelformat>>24)&255);
- ret = pwc_try_video_mode(pdev,
- f->fmt.pix.width,
- f->fmt.pix.height,
- fps,
- compression,
- snapshot);
- PWC_DEBUG_IOCTL("pwc_try_video_mode(), return=%d\n", ret);
- if (ret)
- return ret;
- pwc_vidioc_fill_fmt(pdev, f);
- return 0;
- }
- long pwc_video_do_ioctl(struct file *file, unsigned int cmd, void *arg)
- {
- struct video_device *vdev = video_devdata(file);
- struct pwc_device *pdev;
- DECLARE_WAITQUEUE(wait, current);
- if (vdev == NULL)
- return -EFAULT;
- pdev = video_get_drvdata(vdev);
- if (pdev == NULL)
- return -EFAULT;
- #ifdef CONFIG_USB_PWC_DEBUG
- if (PWC_DEBUG_LEVEL_IOCTL & pwc_trace) {
- v4l_printk_ioctl(cmd);
- printk("\n");
- }
- #endif
- switch (cmd) {
- /* Query cabapilities */
- case VIDIOCGCAP:
- {
- struct video_capability *caps = arg;
- strcpy(caps->name, vdev->name);
- caps->type = VID_TYPE_CAPTURE;
- caps->channels = 1;
- caps->audios = 1;
- caps->minwidth = pdev->view_min.x;
- caps->minheight = pdev->view_min.y;
- caps->maxwidth = pdev->view_max.x;
- caps->maxheight = pdev->view_max.y;
- break;
- }
- /* Channel functions (simulate 1 channel) */
- 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, "Webcam");
- return 0;
- }
- case VIDIOCSCHAN:
- {
- /* The spec says the argument is an integer, but
- the bttv driver uses a video_channel arg, which
- makes sense becasue it also has the norm flag.
- */
- struct video_channel *v = arg;
- if (v->channel != 0)
- return -EINVAL;
- return 0;
- }
- /* Picture functions; contrast etc. */
- case VIDIOCGPICT:
- {
- struct video_picture *p = arg;
- int val;
- val = pwc_get_brightness(pdev);
- if (val >= 0)
- p->brightness = (val<<9);
- else
- p->brightness = 0xffff;
- val = pwc_get_contrast(pdev);
- if (val >= 0)
- p->contrast = (val<<10);
- else
- p->contrast = 0xffff;
- /* Gamma, Whiteness, what's the difference? :) */
- val = pwc_get_gamma(pdev);
- if (val >= 0)
- p->whiteness = (val<<11);
- else
- p->whiteness = 0xffff;
- if (pwc_get_saturation(pdev, &val)<0)
- p->colour = 0xffff;
- else
- p->colour = 32768 + val * 327;
- p->depth = 24;
- p->palette = pdev->vpalette;
- p->hue = 0xFFFF; /* N/A */
- break;
- }
- case VIDIOCSPICT:
- {
- struct video_picture *p = arg;
- /*
- * FIXME: Suppose we are mid read
- ANSWER: No problem: the firmware of the camera
- can handle brightness/contrast/etc
- changes at _any_ time, and the palette
- is used exactly once in the uncompress
- routine.
- */
- pwc_set_brightness(pdev, p->brightness);
- pwc_set_contrast(pdev, p->contrast);
- pwc_set_gamma(pdev, p->whiteness);
- pwc_set_saturation(pdev, (p->colour-32768)/327);
- if (p->palette && p->palette != pdev->vpalette) {
- switch (p->palette) {
- case VIDEO_PALETTE_YUV420P:
- case VIDEO_PALETTE_RAW:
- pdev->vpalette = p->palette;
- return pwc_try_video_mode(pdev, pdev->image.x, pdev->image.y, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
- break;
- default:
- return -EINVAL;
- break;
- }
- }
- break;
- }
- /* Window/size parameters */
- case VIDIOCGWIN:
- {
- struct video_window *vw = arg;
- vw->x = 0;
- vw->y = 0;
- vw->width = pdev->view.x;
- vw->height = pdev->view.y;
- vw->chromakey = 0;
- vw->flags = (pdev->vframes << PWC_FPS_SHIFT) |
- (pdev->vsnapshot ? PWC_FPS_SNAPSHOT : 0);
- break;
- }
- case VIDIOCSWIN:
- {
- struct video_window *vw = arg;
- int fps, snapshot, ret;
- fps = (vw->flags & PWC_FPS_FRMASK) >> PWC_FPS_SHIFT;
- snapshot = vw->flags & PWC_FPS_SNAPSHOT;
- if (fps == 0)
- fps = pdev->vframes;
- if (pdev->view.x == vw->width && pdev->view.y && fps == pdev->vframes && snapshot == pdev->vsnapshot)
- return 0;
- ret = pwc_try_video_mode(pdev, vw->width, vw->height, fps, pdev->vcompression, snapshot);
- if (ret)
- return ret;
- break;
- }
- /* We don't have overlay support (yet) */
- case VIDIOCGFBUF:
- {
- struct video_buffer *vb = arg;
- memset(vb,0,sizeof(*vb));
- break;
- }
- /* mmap() functions */
- case VIDIOCGMBUF:
- {
- /* Tell the user program how much memory is needed for a mmap() */
- struct video_mbuf *vm = arg;
- int i;
- memset(vm, 0, sizeof(*vm));
- vm->size = pwc_mbufs * pdev->len_per_image;
- vm->frames = pwc_mbufs; /* double buffering should be enough for most applications */
- for (i = 0; i < pwc_mbufs; i++)
- vm->offsets[i] = i * pdev->len_per_image;
- break;
- }
- case VIDIOCMCAPTURE:
- {
- /* Start capture into a given image buffer (called 'frame' in video_mmap structure) */
- struct video_mmap *vm = arg;
- PWC_DEBUG_READ("VIDIOCMCAPTURE: %dx%d, frame %d, format %d\n", vm->width, vm->height, vm->frame, vm->format);
- if (vm->frame < 0 || vm->frame >= pwc_mbufs)
- return -EINVAL;
- /* xawtv is nasty. It probes the available palettes
- by setting a very small image size and trying
- various palettes... The driver doesn't support
- such small images, so I'm working around it.
- */
- if (vm->format)
- {
- switch (vm->format)
- {
- case VIDEO_PALETTE_YUV420P:
- case VIDEO_PALETTE_RAW:
- break;
- default:
- return -EINVAL;
- break;
- }
- }
- if ((vm->width != pdev->view.x || vm->height != pdev->view.y) &&
- (vm->width >= pdev->view_min.x && vm->height >= pdev->view_min.y)) {
- int ret;
- PWC_DEBUG_OPEN("VIDIOCMCAPTURE: changing size to please xawtv :-(.\n");
- ret = pwc_try_video_mode(pdev, vm->width, vm->height, pdev->vframes, pdev->vcompression, pdev->vsnapshot);
- if (ret)
- return ret;
- } /* ... size mismatch */
- /* FIXME: should we lock here? */
- if (pdev->image_used[vm->frame])
- return -EBUSY; /* buffer wasn't available. Bummer */
- pdev->image_used[vm->frame] = 1;
- /* Okay, we're done here. In the SYNC call we wait until a
- frame comes available, then expand image into the given
- buffer.
- In contrast to the CPiA cam the Philips cams deliver a
- constant stream, almost like a grabber card. Also,
- we have separate buffers for the rawdata and the image,
- meaning we can nearly always expand into the requested buffer.
- */
- PWC_DEBUG_READ("VIDIOCMCAPTURE done.\n");
- break;
- }
- case VIDIOCSYNC:
- {
- /* The doc says: "Whenever a buffer is used it should
- call VIDIOCSYNC to free this frame up and continue."
- The only odd thing about this whole procedure is
- that MCAPTURE flags the buffer as "in use", and
- SYNC immediately unmarks it, while it isn't
- after SYNC that you know that the buffer actually
- got filled! So you better not start a CAPTURE in
- the same frame immediately (use double buffering).
- This is not a problem for this cam, since it has
- extra intermediate buffers, but a hardware
- grabber card will then overwrite the buffer
- you're working on.
- */
- int *mbuf = arg;
- int ret;
- PWC_DEBUG_READ("VIDIOCSYNC called (%d).\n", *mbuf);
- /* bounds check */
- if (*mbuf < 0 || *mbuf >= pwc_mbufs)
- return -EINVAL;
- /* check if this buffer was requested anyway */
- if (pdev->image_used[*mbuf] == 0)
- return -EINVAL;
- /* Add ourselves to the frame wait-queue.
- FIXME: needs auditing for safety.
- QUESTION: In what respect? I think that using the
- frameq is safe now.
- */
- add_wait_queue(&pdev->frameq, &wait);
- while (pdev->full_frames == NULL) {
- /* Check for unplugged/etc. here */
- if (pdev->error_status) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -pdev->error_status;
- }
- if (signal_pending(current)) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -ERESTARTSYS;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- /* The frame is ready. Expand in the image buffer
- requested by the user. I don't care if you
- mmap() 5 buffers and request data in this order:
- buffer 4 2 3 0 1 2 3 0 4 3 1 . . .
- Grabber hardware may not be so forgiving.
- */
- PWC_DEBUG_READ("VIDIOCSYNC: frame ready.\n");
- pdev->fill_image = *mbuf; /* tell in which buffer we want the image to be expanded */
- /* Decompress, etc */
- ret = pwc_handle_frame(pdev);
- pdev->image_used[*mbuf] = 0;
- if (ret)
- return -EFAULT;
- break;
- }
- case VIDIOCGAUDIO:
- {
- struct video_audio *v = arg;
- strcpy(v->name, "Microphone");
- v->audio = -1; /* unknown audio minor */
- v->flags = 0;
- v->mode = VIDEO_SOUND_MONO;
- v->volume = 0;
- v->bass = 0;
- v->treble = 0;
- v->balance = 0x8000;
- v->step = 1;
- break;
- }
- case VIDIOCSAUDIO:
- {
- /* Dummy: nothing can be set */
- break;
- }
- case VIDIOCGUNIT:
- {
- struct video_unit *vu = arg;
- vu->video = pdev->vdev->minor & 0x3F;
- vu->audio = -1; /* not known yet */
- vu->vbi = -1;
- vu->radio = -1;
- vu->teletext = -1;
- break;
- }
- /* V4L2 Layer */
- case VIDIOC_QUERYCAP:
- {
- struct v4l2_capability *cap = arg;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCAP) This application "\
- "try to use the v4l2 layer\n");
- strcpy(cap->driver,PWC_NAME);
- strlcpy(cap->card, vdev->name, sizeof(cap->card));
- usb_make_path(pdev->udev,cap->bus_info,sizeof(cap->bus_info));
- cap->version = PWC_VERSION_CODE;
- cap->capabilities =
- V4L2_CAP_VIDEO_CAPTURE |
- V4L2_CAP_STREAMING |
- V4L2_CAP_READWRITE;
- return 0;
- }
- case VIDIOC_ENUMINPUT:
- {
- struct v4l2_input *i = arg;
- if ( i->index ) /* Only one INPUT is supported */
- return -EINVAL;
- memset(i, 0, sizeof(struct v4l2_input));
- strcpy(i->name, "usb");
- return 0;
- }
- case VIDIOC_G_INPUT:
- {
- int *i = arg;
- *i = 0; /* Only one INPUT is supported */
- return 0;
- }
- case VIDIOC_S_INPUT:
- {
- int *i = arg;
- if ( *i ) { /* Only one INPUT is supported */
- PWC_DEBUG_IOCTL("Only one input source is"\
- " supported with this webcam.\n");
- return -EINVAL;
- }
- return 0;
- }
- /* TODO: */
- case VIDIOC_QUERYCTRL:
- {
- struct v4l2_queryctrl *c = arg;
- int i;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) query id=%d\n", c->id);
- for (i=0; i<sizeof(pwc_controls)/sizeof(struct v4l2_queryctrl); i++) {
- if (pwc_controls[i].id == c->id) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) found\n");
- memcpy(c,&pwc_controls[i],sizeof(struct v4l2_queryctrl));
- return 0;
- }
- }
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYCTRL) not found\n");
- return -EINVAL;
- }
- case VIDIOC_G_CTRL:
- {
- struct v4l2_control *c = arg;
- int ret;
- switch (c->id)
- {
- case V4L2_CID_BRIGHTNESS:
- c->value = pwc_get_brightness(pdev);
- if (c->value<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_CONTRAST:
- c->value = pwc_get_contrast(pdev);
- if (c->value<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_SATURATION:
- ret = pwc_get_saturation(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAMMA:
- c->value = pwc_get_gamma(pdev);
- if (c->value<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_RED_BALANCE:
- ret = pwc_get_red_gain(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_BLUE_BALANCE:
- ret = pwc_get_blue_gain(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- ret = pwc_get_awb(pdev);
- if (ret<0)
- return -EINVAL;
- c->value = (ret == PWC_WB_MANUAL)?0:1;
- return 0;
- case V4L2_CID_GAIN:
- ret = pwc_get_agc(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value >>= 8;
- return 0;
- case V4L2_CID_AUTOGAIN:
- ret = pwc_get_agc(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- c->value = (c->value < 0)?1:0;
- return 0;
- case V4L2_CID_EXPOSURE:
- ret = pwc_get_shutter_speed(pdev, &c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_COLOUR_MODE:
- ret = pwc_get_colour_mode(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_AUTOCONTOUR:
- ret = pwc_get_contour(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value=(c->value == -1?1:0);
- return 0;
- case V4L2_CID_PRIVATE_CONTOUR:
- ret = pwc_get_contour(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value >>= 10;
- return 0;
- case V4L2_CID_PRIVATE_BACKLIGHT:
- ret = pwc_get_backlight(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_FLICKERLESS:
- ret = pwc_get_flicker(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- c->value=(c->value?1:0);
- return 0;
- case V4L2_CID_PRIVATE_NOISE_REDUCTION:
- ret = pwc_get_dynamic_noise(pdev, &c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_SAVE_USER:
- case V4L2_CID_PRIVATE_RESTORE_USER:
- case V4L2_CID_PRIVATE_RESTORE_FACTORY:
- return -EINVAL;
- }
- return -EINVAL;
- }
- case VIDIOC_S_CTRL:
- {
- struct v4l2_control *c = arg;
- int ret;
- switch (c->id)
- {
- case V4L2_CID_BRIGHTNESS:
- c->value <<= 9;
- ret = pwc_set_brightness(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_CONTRAST:
- c->value <<= 10;
- ret = pwc_set_contrast(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_SATURATION:
- ret = pwc_set_saturation(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAMMA:
- c->value <<= 11;
- ret = pwc_set_gamma(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_RED_BALANCE:
- c->value <<= 8;
- ret = pwc_set_red_gain(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_BLUE_BALANCE:
- c->value <<= 8;
- ret = pwc_set_blue_gain(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_AUTO_WHITE_BALANCE:
- c->value = (c->value == 0)?PWC_WB_MANUAL:PWC_WB_AUTO;
- ret = pwc_set_awb(pdev, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_EXPOSURE:
- c->value <<= 8;
- ret = pwc_set_shutter_speed(pdev, c->value?0:1, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_AUTOGAIN:
- /* autogain off means nothing without a gain */
- if (c->value == 0)
- return 0;
- ret = pwc_set_agc(pdev, c->value, 0);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_GAIN:
- c->value <<= 8;
- ret = pwc_set_agc(pdev, 0, c->value);
- if (ret<0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_SAVE_USER:
- if (pwc_save_user(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_RESTORE_USER:
- if (pwc_restore_user(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_RESTORE_FACTORY:
- if (pwc_restore_factory(pdev))
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_COLOUR_MODE:
- ret = pwc_set_colour_mode(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_AUTOCONTOUR:
- c->value=(c->value == 1)?-1:0;
- ret = pwc_set_contour(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_CONTOUR:
- c->value <<= 10;
- ret = pwc_set_contour(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_BACKLIGHT:
- ret = pwc_set_backlight(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- case V4L2_CID_PRIVATE_FLICKERLESS:
- ret = pwc_set_flicker(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- case V4L2_CID_PRIVATE_NOISE_REDUCTION:
- ret = pwc_set_dynamic_noise(pdev, c->value);
- if (ret < 0)
- return -EINVAL;
- return 0;
- }
- return -EINVAL;
- }
- case VIDIOC_ENUM_FMT:
- {
- struct v4l2_fmtdesc *f = arg;
- int index;
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- /* We only support two format: the raw format, and YUV */
- index = f->index;
- memset(f,0,sizeof(struct v4l2_fmtdesc));
- f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- f->index = index;
- switch(index)
- {
- case 0:
- /* RAW format */
- f->pixelformat = pdev->type<=646?V4L2_PIX_FMT_PWC1:V4L2_PIX_FMT_PWC2;
- f->flags = V4L2_FMT_FLAG_COMPRESSED;
- strlcpy(f->description,"Raw Philips Webcam",sizeof(f->description));
- break;
- case 1:
- f->pixelformat = V4L2_PIX_FMT_YUV420;
- strlcpy(f->description,"4:2:0, planar, Y-Cb-Cr",sizeof(f->description));
- break;
- default:
- return -EINVAL;
- }
- return 0;
- }
- case VIDIOC_G_FMT:
- {
- struct v4l2_format *f = arg;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_G_FMT) return size %dx%d\n",pdev->image.x,pdev->image.y);
- if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- pwc_vidioc_fill_fmt(pdev, f);
- return 0;
- }
- case VIDIOC_TRY_FMT:
- return pwc_vidioc_try_fmt(pdev, arg);
- case VIDIOC_S_FMT:
- return pwc_vidioc_set_fmt(pdev, arg);
- case VIDIOC_G_STD:
- {
- v4l2_std_id *std = arg;
- *std = V4L2_STD_UNKNOWN;
- return 0;
- }
- case VIDIOC_S_STD:
- {
- v4l2_std_id *std = arg;
- if (*std != V4L2_STD_UNKNOWN)
- return -EINVAL;
- return 0;
- }
- case VIDIOC_ENUMSTD:
- {
- struct v4l2_standard *std = arg;
- if (std->index != 0)
- return -EINVAL;
- std->id = V4L2_STD_UNKNOWN;
- strncpy(std->name, "webcam", sizeof(std->name));
- return 0;
- }
- case VIDIOC_REQBUFS:
- {
- struct v4l2_requestbuffers *rb = arg;
- int nbuffers;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_REQBUFS) count=%d\n",rb->count);
- if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (rb->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
- nbuffers = rb->count;
- if (nbuffers < 2)
- nbuffers = 2;
- else if (nbuffers > pwc_mbufs)
- nbuffers = pwc_mbufs;
- /* Force to use our # of buffers */
- rb->count = pwc_mbufs;
- return 0;
- }
- case VIDIOC_QUERYBUF:
- {
- struct v4l2_buffer *buf = arg;
- int index;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) index=%d\n",buf->index);
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad type\n");
- return -EINVAL;
- }
- if (buf->memory != V4L2_MEMORY_MMAP) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad memory type\n");
- return -EINVAL;
- }
- index = buf->index;
- if (index < 0 || index >= pwc_mbufs) {
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QUERYBUF) Bad index %d\n", buf->index);
- return -EINVAL;
- }
- memset(buf, 0, sizeof(struct v4l2_buffer));
- buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf->index = index;
- buf->m.offset = index * pdev->len_per_image;
- if (pdev->vpalette == VIDEO_PALETTE_RAW)
- buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
- else
- buf->bytesused = pdev->view.size;
- buf->field = V4L2_FIELD_NONE;
- buf->memory = V4L2_MEMORY_MMAP;
- //buf->flags = V4L2_BUF_FLAG_MAPPED;
- buf->length = pdev->len_per_image;
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: index=%d\n",buf->index);
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: m.offset=%d\n",buf->m.offset);
- PWC_DEBUG_READ("VIDIOC_QUERYBUF: bytesused=%d\n",buf->bytesused);
- return 0;
- }
- case VIDIOC_QBUF:
- {
- struct v4l2_buffer *buf = arg;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_QBUF) index=%d\n",buf->index);
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- if (buf->memory != V4L2_MEMORY_MMAP)
- return -EINVAL;
- if (buf->index < 0 || buf->index >= pwc_mbufs)
- return -EINVAL;
- buf->flags |= V4L2_BUF_FLAG_QUEUED;
- buf->flags &= ~V4L2_BUF_FLAG_DONE;
- return 0;
- }
- case VIDIOC_DQBUF:
- {
- struct v4l2_buffer *buf = arg;
- int ret;
- PWC_DEBUG_IOCTL("ioctl(VIDIOC_DQBUF)\n");
- if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
- return -EINVAL;
- /* Add ourselves to the frame wait-queue.
- FIXME: needs auditing for safety.
- QUESTION: In what respect? I think that using the
- frameq is safe now.
- */
- add_wait_queue(&pdev->frameq, &wait);
- while (pdev->full_frames == NULL) {
- if (pdev->error_status) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -pdev->error_status;
- }
- if (signal_pending(current)) {
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- return -ERESTARTSYS;
- }
- schedule();
- set_current_state(TASK_INTERRUPTIBLE);
- }
- remove_wait_queue(&pdev->frameq, &wait);
- set_current_state(TASK_RUNNING);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: frame ready.\n");
- /* Decompress data in pdev->images[pdev->fill_image] */
- ret = pwc_handle_frame(pdev);
- if (ret)
- return -EFAULT;
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: after pwc_handle_frame\n");
- buf->index = pdev->fill_image;
- if (pdev->vpalette == VIDEO_PALETTE_RAW)
- buf->bytesused = pdev->frame_size + sizeof(struct pwc_raw_frame);
- else
- buf->bytesused = pdev->view.size;
- buf->flags = V4L2_BUF_FLAG_MAPPED;
- buf->field = V4L2_FIELD_NONE;
- do_gettimeofday(&buf->timestamp);
- buf->sequence = 0;
- buf->memory = V4L2_MEMORY_MMAP;
- buf->m.offset = pdev->fill_image * pdev->len_per_image;
- buf->length = pdev->len_per_image;
- pwc_next_image(pdev);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->index=%d\n",buf->index);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: buf->length=%d\n",buf->length);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: m.offset=%d\n",buf->m.offset);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: bytesused=%d\n",buf->bytesused);
- PWC_DEBUG_IOCTL("VIDIOC_DQBUF: leaving\n");
- return 0;
- }
- case VIDIOC_STREAMON:
- {
- /* WARNING: pwc_try_video_mode() called pwc_isoc_init */
- pwc_isoc_init(pdev);
- return 0;
- }
- case VIDIOC_STREAMOFF:
- {
- pwc_isoc_cleanup(pdev);
- return 0;
- }
- case VIDIOC_ENUM_FRAMESIZES:
- {
- struct v4l2_frmsizeenum *fsize = arg;
- unsigned int i = 0, index = fsize->index;
- if (fsize->pixel_format == V4L2_PIX_FMT_YUV420) {
- for (i = 0; i < PSZ_MAX; i++) {
- if (pdev->image_mask & (1UL << i)) {
- if (!index--) {
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = pwc_image_sizes[i].x;
- fsize->discrete.height = pwc_image_sizes[i].y;
- return 0;
- }
- }
- }
- } else if (fsize->index == 0 &&
- ((fsize->pixel_format == V4L2_PIX_FMT_PWC1 && DEVICE_USE_CODEC1(pdev->type)) ||
- (fsize->pixel_format == V4L2_PIX_FMT_PWC2 && DEVICE_USE_CODEC23(pdev->type)))) {
- fsize->type = V4L2_FRMSIZE_TYPE_DISCRETE;
- fsize->discrete.width = pdev->abs_max.x;
- fsize->discrete.height = pdev->abs_max.y;
- return 0;
- }
- return -EINVAL;
- }
- case VIDIOC_ENUM_FRAMEINTERVALS:
- {
- struct v4l2_frmivalenum *fival = arg;
- int size = -1;
- unsigned int i;
- for (i = 0; i < PSZ_MAX; i++) {
- if (pwc_image_sizes[i].x == fival->width &&
- pwc_image_sizes[i].y == fival->height) {
- size = i;
- break;
- }
- }
- /* TODO: Support raw format */
- if (size < 0 || fival->pixel_format != V4L2_PIX_FMT_YUV420) {
- return -EINVAL;
- }
- i = pwc_get_fps(pdev, fival->index, size);
- if (!i)
- return -EINVAL;
- fival->type = V4L2_FRMIVAL_TYPE_DISCRETE;
- fival->discrete.numerator = 1;
- fival->discrete.denominator = i;
- return 0;
- }
- default:
- return pwc_ioctl(pdev, cmd, arg);
- } /* ..switch */
- return 0;
- }
- /* vim: set cino= formatoptions=croql cindent shiftwidth=8 tabstop=8: */
|