static int cam_s_fmt_vid_cap(struct file *file, void *fh, struct v4l2_format *fmt) { struct w9966 *cam = video_drvdata(file); struct v4l2_pix_format *pix = &fmt->fmt.pix; int ret = cam_try_fmt_vid_cap(file, fh, fmt); if (ret) return ret; mutex_lock(&cam->lock); /* Update camera regs */ w9966_pdev_claim(cam); ret = w9966_setup(cam, 0, 0, 1023, 1023, pix->width, pix->height); w9966_pdev_release(cam); mutex_unlock(&cam->lock); return ret; }
static int w9966_v4l_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { struct video_device *vdev = video_devdata(file); struct w9966_dev *cam = vdev->priv; switch(cmd) { case VIDIOCGCAP: { static struct video_capability vcap = { .name = W9966_DRIVERNAME, .type = VID_TYPE_CAPTURE | VID_TYPE_SCALES, .channels = 1, .maxwidth = W9966_WND_MAX_W, .maxheight = W9966_WND_MAX_H, .minwidth = 2, .minheight = 1, }; struct video_capability *cap = arg; *cap = vcap; return 0; } case VIDIOCGCHAN: { struct video_channel *vch = arg; if(vch->channel != 0) // We only support one channel (#0) return -EINVAL; memset(vch,0,sizeof(*vch)); strcpy(vch->name, "CCD-input"); vch->type = VIDEO_TYPE_CAMERA; return 0; } case VIDIOCSCHAN: { struct video_channel *vch = arg; if(vch->channel != 0) return -EINVAL; return 0; } case VIDIOCGTUNER: { struct video_tuner *vtune = arg; if(vtune->tuner != 0) return -EINVAL; strcpy(vtune->name, "no tuner"); vtune->rangelow = 0; vtune->rangehigh = 0; vtune->flags = VIDEO_TUNER_NORM; vtune->mode = VIDEO_MODE_AUTO; vtune->signal = 0xffff; return 0; } case VIDIOCSTUNER: { struct video_tuner *vtune = arg; if (vtune->tuner != 0) return -EINVAL; if (vtune->mode != VIDEO_MODE_AUTO) return -EINVAL; return 0; } case VIDIOCGPICT: { struct video_picture vpic = { cam->brightness << 8, // brightness (cam->hue + 128) << 8, // hue cam->color << 9, // color cam->contrast << 9, // contrast 0x8000, // whiteness 16, VIDEO_PALETTE_YUV422// bpp, palette format }; struct video_picture *pic = arg; *pic = vpic; return 0; } case VIDIOCSPICT: { struct video_picture *vpic = arg; if (vpic->depth != 16 || (vpic->palette != VIDEO_PALETTE_YUV422 && vpic->palette != VIDEO_PALETTE_YUYV)) return -EINVAL; cam->brightness = vpic->brightness >> 8; cam->hue = (vpic->hue >> 8) - 128; cam->color = vpic->colour >> 9; cam->contrast = vpic->contrast >> 9; w9966_pdev_claim(cam); if ( w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 || w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 || w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 || w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1 ) { w9966_pdev_release(cam); return -EIO; } w9966_pdev_release(cam); return 0; } case VIDIOCSWIN: { int ret; struct video_window *vwin = arg; if (vwin->flags != 0) return -EINVAL; if (vwin->clipcount != 0) return -EINVAL; if (vwin->width < 2 || vwin->width > W9966_WND_MAX_W) return -EINVAL; if (vwin->height < 1 || vwin->height > W9966_WND_MAX_H) return -EINVAL; // Update camera regs w9966_pdev_claim(cam); ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin->width, vwin->height); w9966_pdev_release(cam); if (ret != 0) { DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n"); return -EIO; } return 0; } case VIDIOCGWIN: { struct video_window *vwin = arg; memset(vwin, 0, sizeof(*vwin)); vwin->width = cam->width; vwin->height = cam->height; return 0; } // Unimplemented case VIDIOCCAPTURE: case VIDIOCGFBUF: case VIDIOCSFBUF: case VIDIOCKEY: case VIDIOCGFREQ: case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: return -EINVAL; default: return -ENOIOCTLCMD; } return 0; } static int w9966_v4l_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, w9966_v4l_do_ioctl); } // Capture data static ssize_t w9966_v4l_read(struct file *file, char __user *buf, size_t count, loff_t *ppos) { struct video_device *vdev = video_devdata(file); struct w9966_dev *cam = vdev->priv; unsigned char addr = 0xa0; // ECP, read, CCD-transfer, 00000 unsigned char __user *dest = (unsigned char __user *)buf; unsigned long dleft = count; unsigned char *tbuf; // Why would anyone want more than this?? if (count > cam->width * cam->height * 2) return -EINVAL; w9966_pdev_claim(cam); w9966_wReg(cam, 0x00, 0x02); // Reset ECP-FIFO buffer w9966_wReg(cam, 0x00, 0x00); // Return to normal operation w9966_wReg(cam, 0x01, 0x98); // Enable capture // write special capture-addr and negotiate into data transfer if ( (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_ADDR) != 0 )|| (parport_write(cam->pport, &addr, 1) != 1 )|| (parport_negotiate(cam->pport, cam->ppmode|IEEE1284_DATA) != 0 ) ) { w9966_pdev_release(cam); return -EFAULT; } tbuf = kmalloc(W9966_RBUFFER, GFP_KERNEL); if (tbuf == NULL) { count = -ENOMEM; goto out; } while(dleft > 0) { unsigned long tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; if (parport_read(cam->pport, tbuf, tsize) < tsize) { count = -EFAULT; goto out; } if (copy_to_user(dest, tbuf, tsize) != 0) { count = -EFAULT; goto out; } dest += tsize; dleft -= tsize; } w9966_wReg(cam, 0x01, 0x18); // Disable capture out: kfree(tbuf); w9966_pdev_release(cam); return count; } // Called once for every parport on init static void w9966_attach(struct parport *port) { int i; for (i = 0; i < W9966_MAXCAMS; i++) { if (w9966_cams[i].dev_state != 0) // Cam is already assigned continue; if ( strcmp(pardev[i], "aggressive") == 0 || strcmp(pardev[i], port->name) == 0 ) { if (w9966_init(&w9966_cams[i], port) != 0) w9966_term(&w9966_cams[i]); break; // return } } }
static int w9966_v4l_ioctl(struct video_device *vdev, unsigned int cmd, void *arg) { struct w9966_dev *cam = (struct w9966_dev*)vdev->priv; switch(cmd) { case VIDIOCGCAP: { struct video_capability vcap = { W9966_DRIVERNAME, // name VID_TYPE_CAPTURE | VID_TYPE_SCALES, // type 1, 0, // vid, aud channels W9966_WND_MAX_W, // max w W9966_WND_MAX_H, // max h 2, 1 // min w, min h }; if(copy_to_user(arg, &vcap, sizeof(vcap)) != 0) return -EFAULT; return 0; } case VIDIOCGCHAN: { struct video_channel vch; if(copy_from_user(&vch, arg, sizeof(vch)) != 0) return -EFAULT; if(vch.channel != 0) // We only support one channel (#0) return -EINVAL; strcpy(vch.name, "CCD-input"); vch.flags = 0; // We have no tuner or audio vch.tuners = 0; vch.type = VIDEO_TYPE_CAMERA; vch.norm = 0; // ??? if(copy_to_user(arg, &vch, sizeof(vch)) != 0) return -EFAULT; return 0; } case VIDIOCSCHAN: { struct video_channel vch; if(copy_from_user(&vch, arg, sizeof(vch) ) != 0) return -EFAULT; if(vch.channel != 0) return -EINVAL; return 0; } case VIDIOCGTUNER: { struct video_tuner vtune; if(copy_from_user(&vtune, arg, sizeof(vtune)) != 0) return -EFAULT; if(vtune.tuner != 0); return -EINVAL; strcpy(vtune.name, "no tuner"); vtune.rangelow = 0; vtune.rangehigh = 0; vtune.flags = VIDEO_TUNER_NORM; vtune.mode = VIDEO_MODE_AUTO; vtune.signal = 0xffff; if(copy_to_user(arg, &vtune, sizeof(vtune)) != 0) return -EFAULT; return 0; } case VIDIOCSTUNER: { struct video_tuner vtune; if (copy_from_user(&vtune, arg, sizeof(vtune)) != 0) return -EFAULT; if (vtune.tuner != 0) return -EINVAL; if (vtune.mode != VIDEO_MODE_AUTO) return -EINVAL; return 0; } case VIDIOCGPICT: { struct video_picture vpic = { cam->brightness << 8, // brightness (cam->hue + 128) << 8, // hue cam->color << 9, // color cam->contrast << 9, // contrast 0x8000, // whiteness 16, VIDEO_PALETTE_YUV422// bpp, palette format }; if(copy_to_user(arg, &vpic, sizeof(vpic)) != 0) return -EFAULT; return 0; } case VIDIOCSPICT: { struct video_picture vpic; if(copy_from_user(&vpic, arg, sizeof(vpic)) != 0) return -EFAULT; if (vpic.depth != 16 || vpic.palette != VIDEO_PALETTE_YUV422) return -EINVAL; cam->brightness = vpic.brightness >> 8; cam->hue = (vpic.hue >> 8) - 128; cam->color = vpic.colour >> 9; cam->contrast = vpic.contrast >> 9; w9966_pdev_claim(cam); if ( w9966_wReg_i2c(cam, 0x0a, cam->brightness) == -1 || w9966_wReg_i2c(cam, 0x0b, cam->contrast) == -1 || w9966_wReg_i2c(cam, 0x0c, cam->color) == -1 || w9966_wReg_i2c(cam, 0x0d, cam->hue) == -1 ) { w9966_pdev_release(cam); return -EFAULT; } w9966_pdev_release(cam); return 0; } case VIDIOCSWIN: { int ret; struct video_window vwin; if (copy_from_user(&vwin, arg, sizeof(vwin)) != 0) return -EFAULT; if (vwin.flags != 0) return -EINVAL; if (vwin.clipcount != 0) return -EINVAL; if (vwin.width < 2 || vwin.width > W9966_WND_MAX_W) return -EINVAL; if (vwin.height < 1 || vwin.height > W9966_WND_MAX_H) return -EINVAL; // Update camera regs w9966_pdev_claim(cam); ret = w9966_setup(cam, 0, 0, 1023, 1023, vwin.width, vwin.height); w9966_pdev_release(cam); if (ret != 0) { DPRINTF("VIDIOCSWIN: w9966_setup() failed.\n"); return -EFAULT; } return 0; } case VIDIOCGWIN: { struct video_window vwin; memset(&vwin, 0, sizeof(vwin)); vwin.width = cam->width; vwin.height = cam->height; if(copy_to_user(arg, &vwin, sizeof(vwin)) != 0) return -EFAULT; return 0; } // Unimplemented case VIDIOCCAPTURE: case VIDIOCGFBUF: case VIDIOCSFBUF: case VIDIOCKEY: case VIDIOCGFREQ: case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: return -EINVAL; default: return -ENOIOCTLCMD; } return 0; }
// Initialize camera device. Setup all internal flags, set a // default video mode, setup ccd-chip, register v4l device etc.. // Also used for 'probing' of hardware. // -1 on error static int w9966_init(struct w9966_dev* cam, struct parport* port) { if (cam->dev_state != 0) return -1; cam->pport = port; cam->brightness = 128; cam->contrast = 64; cam->color = 64; cam->hue = 0; // Select requested transfer mode switch(parmode) { default: // Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) case 0: if (port->modes & PARPORT_MODE_ECP) cam->ppmode = IEEE1284_MODE_ECP; /* else if (port->modes & PARPORT_MODE_EPP) cam->ppmode = IEEE1284_MODE_EPP;*/ else cam->ppmode = IEEE1284_MODE_ECPSWE; break; case 1: // hw- or sw-ecp if (port->modes & PARPORT_MODE_ECP) cam->ppmode = IEEE1284_MODE_ECP; else cam->ppmode = IEEE1284_MODE_ECPSWE; break; case 2: // hw- or sw-epp if (port->modes & PARPORT_MODE_EPP) cam->ppmode = IEEE1284_MODE_EPP; else cam->ppmode = IEEE1284_MODE_EPPSWE; break; } // Tell the parport driver that we exists cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); if (cam->pdev == NULL) { DPRINTF("parport_register_device() failed\n"); return -1; } w9966_setState(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); w9966_pdev_claim(cam); // Setup a default capture mode if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { DPRINTF("w9966_setup() failed.\n"); return -1; } w9966_pdev_release(cam); // Fill in the video_device struct and register us to v4l memset(&cam->vdev, 0, sizeof(struct video_device)); strcpy(cam->vdev.name, W9966_DRIVERNAME); cam->vdev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->vdev.hardware = VID_HARDWARE_W9966; cam->vdev.open = &w9966_v4l_open; cam->vdev.close = &w9966_v4l_close; cam->vdev.read = &w9966_v4l_read; cam->vdev.ioctl = &w9966_v4l_ioctl; cam->vdev.priv = (void*)cam; cam->vdev.owner = THIS_MODULE; if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) == -1) return -1; w9966_setState(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); cam->buffer = NULL; // All ok printk( "w9966cf: Found and initialized a webcam on %s.\n", cam->pport->name ); return 0; }
/* Initialize camera device. Setup all internal flags, set a default video mode, setup ccd-chip, register v4l device etc.. Also used for 'probing' of hardware. -1 on error */ static int w9966_init(struct w9966 *cam, struct parport *port) { struct v4l2_device *v4l2_dev = &cam->v4l2_dev; if (cam->dev_state != 0) return -1; strlcpy(v4l2_dev->name, "w9966", sizeof(v4l2_dev->name)); if (v4l2_device_register(NULL, v4l2_dev) < 0) { v4l2_err(v4l2_dev, "Could not register v4l2_device\n"); return -1; } cam->pport = port; cam->brightness = 128; cam->contrast = 64; cam->color = 64; cam->hue = 0; /* Select requested transfer mode */ switch (parmode) { default: /* Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) */ case 0: if (port->modes & PARPORT_MODE_ECP) cam->ppmode = IEEE1284_MODE_ECP; else if (port->modes & PARPORT_MODE_EPP) cam->ppmode = IEEE1284_MODE_EPP; else cam->ppmode = IEEE1284_MODE_ECP; break; case 1: /* hw- or sw-ecp */ cam->ppmode = IEEE1284_MODE_ECP; break; case 2: /* hw- or sw-epp */ cam->ppmode = IEEE1284_MODE_EPP; break; } /* Tell the parport driver that we exists */ cam->pdev = parport_register_device(port, "w9966", NULL, NULL, NULL, 0, NULL); if (cam->pdev == NULL) { DPRINTF("parport_register_device() failed\n"); return -1; } w9966_set_state(cam, W9966_STATE_PDEV, W9966_STATE_PDEV); w9966_pdev_claim(cam); /* Setup a default capture mode */ if (w9966_setup(cam, 0, 0, 1023, 1023, 200, 160) != 0) { DPRINTF("w9966_setup() failed.\n"); return -1; } w9966_pdev_release(cam); /* Fill in the video_device struct and register us to v4l */ strlcpy(cam->vdev.name, W9966_DRIVERNAME, sizeof(cam->vdev.name)); cam->vdev.v4l2_dev = v4l2_dev; cam->vdev.fops = &w9966_fops; cam->vdev.ioctl_ops = &w9966_ioctl_ops; cam->vdev.release = video_device_release_empty; video_set_drvdata(&cam->vdev, cam); mutex_init(&cam->lock); if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, video_nr) < 0) return -1; w9966_set_state(cam, W9966_STATE_VDEV, W9966_STATE_VDEV); /* All ok */ v4l2_info(v4l2_dev, "Found and initialized a webcam on %s.\n", cam->pport->name); return 0; }
// Initialize camera device. Setup all internal flags, set a // default video mode, setup ccd-chip, register v4l device etc.. // Also used for 'probing' of hardware. // 1 on success, else 0 static int w9966_init(struct w9966_dev* cam, struct parport* port, int vidnr) { if (cam->dev_state != 0) return 0; cam->pport = port; cam->brightness = 128; cam->contrast = 64; cam->color = 64; cam->hue = 0; // Select requested transfer mode switch(parmode) { default: // Auto-detect (priority: hw-ecp, hw-epp, sw-ecp) case 0: if (port->modes & PARPORT_MODE_ECP) cam->ppmode = IEEE1284_MODE_ECP; else if (port->modes & PARPORT_MODE_EPP) cam->ppmode = IEEE1284_MODE_EPP; else cam->ppmode = IEEE1284_MODE_ECPSWE; break; case 1: // hw- or sw-ecp if (port->modes & PARPORT_MODE_ECP) cam->ppmode = IEEE1284_MODE_ECP; else cam->ppmode = IEEE1284_MODE_ECPSWE; break; case 2: // hw- or sw-epp if (port->modes & PARPORT_MODE_EPP) cam->ppmode = IEEE1284_MODE_EPP; else cam->ppmode = IEEE1284_MODE_EPPSWE; break; case 3: // hw-ecp cam->ppmode = IEEE1284_MODE_ECP; break; } // Tell the parport driver that we exists cam->pdev = parport_register_device( port, W9966_DRIVERNAME, NULL, NULL, NULL, 0, NULL); if (cam->pdev == NULL) { DPRINTF("parport_register_device() failed.\n"); return 0; } w9966_flag_set(cam, W9966_STATE_PDEV); // Claim parport if (!w9966_pdev_claim(cam)) { DPRINTF("w9966_pdev_claim() failed.\n"); return 0; } // Perform initial w9966 setup if (!w9966_setup(cam)) { DPRINTF("w9966_setup() failed.\n"); return 0; } // Detect model if (!w9966_saa7111_init(cam)) { DPRINTF("w9966_saa7111_init() failed.\n"); return 0; } if (!w9966_lc99053_init(cam)) { DPRINTF("w9966_lc99053_init() failed.\n"); return 0; } if (!w9966_flag_test(cam, W9966_STATE_DETECTED)) { DPRINTF("Camera model not identified.\n"); return 0; } // Setup w9966 with a default capture mode (QCIF res.) if (!w9966_window(cam, 0, 0, 1023, 1023, 176, 144)) { DPRINTF("w9966_window() failed.\n"); return 0; } w9966_pdev_release(cam); // Fill in the video_device struct and register us to v4l memset(&cam->vdev, 0, sizeof(struct video_device)); strcpy(cam->vdev.name, W9966_DRIVERNAME); cam->vdev.type = VID_TYPE_CAPTURE | VID_TYPE_SCALES; cam->vdev.hardware = VID_HARDWARE_W9966; cam->vdev.open = &w9966_v4l_open; cam->vdev.close = &w9966_v4l_close; cam->vdev.read = &w9966_v4l_read; cam->vdev.ioctl = &w9966_v4l_ioctl; cam->vdev.priv = (void*)cam; cam->vdev.owner = THIS_MODULE; if (video_register_device(&cam->vdev, VFL_TYPE_GRABBER, vidnr) == -1) { DPRINTF("video_register_device() failed (minor: %d).\n", vidnr); return 0; } w9966_flag_set(cam, W9966_STATE_VDEV); // All ok printk("w9966: Found and initialized %s on %s.\n", cam->name, cam->pport->name); return 1; }