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; }
// Setup the cameras capture window etc. // Expects a claimed pdev // return -1 on error static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h) { unsigned int i; unsigned int enh_s, enh_e; unsigned char scale_x, scale_y; unsigned char regs[0x1c]; unsigned char saa7111_regs[] = { 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00, 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00, 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0 }; if (w*h*2 > W9966_SRAMSIZE) { DPRINTF("capture window exceeds SRAM size!.\n"); w = 200; h = 160; // Pick default values } w &= ~0x1; if (w < 2) w = 2; if (h < 1) h = 1; if (w > W9966_WND_MAX_W) w = W9966_WND_MAX_W; if (h > W9966_WND_MAX_H) h = W9966_WND_MAX_H; cam->width = w; cam->height = h; enh_s = 0; enh_e = w*h*2; // Modify capture window if necessary and calculate downscaling if ( w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0 ) return -1; DPRINTF( "%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", w, h, x1, x2, y1, y2, scale_x&~0x80, scale_y&~0x80 ); // Setup registers regs[0x00] = 0x00; // Set normal operation regs[0x01] = 0x18; // Capture mode regs[0x02] = scale_y; // V-scaling regs[0x03] = scale_x; // H-scaling // Capture window regs[0x04] = (x1 & 0x0ff); // X-start (8 low bits) regs[0x05] = (x1 & 0x300)>>8; // X-start (2 high bits) regs[0x06] = (y1 & 0x0ff); // Y-start (8 low bits) regs[0x07] = (y1 & 0x300)>>8; // Y-start (2 high bits) regs[0x08] = (x2 & 0x0ff); // X-end (8 low bits) regs[0x09] = (x2 & 0x300)>>8; // X-end (2 high bits) regs[0x0a] = (y2 & 0x0ff); // Y-end (8 low bits) regs[0x0c] = W9966_SRAMID; // SRAM-banks (1x 128kb) // Enhancement layer regs[0x0d] = (enh_s& 0x000ff); // Enh. start (0-7) regs[0x0e] = (enh_s& 0x0ff00)>>8; // Enh. start (8-15) regs[0x0f] = (enh_s& 0x70000)>>16; // Enh. start (16-17/18??) regs[0x10] = (enh_e& 0x000ff); // Enh. end (0-7) regs[0x11] = (enh_e& 0x0ff00)>>8; // Enh. end (8-15) regs[0x12] = (enh_e& 0x70000)>>16; // Enh. end (16-17/18??) // Misc regs[0x13] = 0x40; // VEE control (raw 4:2:2) regs[0x17] = 0x00; // ??? regs[0x18] = cam->i2c_state = 0x00; // Serial bus regs[0x19] = 0xff; // I/O port direction control regs[0x1a] = 0xff; // I/O port data register regs[0x1b] = 0x10; // ??? // SAA7111 chip settings saa7111_regs[0x0a] = cam->brightness; saa7111_regs[0x0b] = cam->contrast; saa7111_regs[0x0c] = cam->color; saa7111_regs[0x0d] = cam->hue; // Reset (ECP-fifo & serial-bus) if (w9966_wReg(cam, 0x00, 0x03) == -1) return -1; // Write regs to w9966cf chip for (i = 0; i < 0x1c; i++) if (w9966_wReg(cam, i, regs[i]) == -1) return -1; // Write regs to saa7111 chip for (i = 0; i < 0x20; i++) if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1) return -1; return 0; }
static int w9966_setup(struct w9966_dev* cam, int x1, int y1, int x2, int y2, int w, int h) { unsigned int i; unsigned int enh_s, enh_e; unsigned char scale_x, scale_y; unsigned char regs[0x1c]; unsigned char saa7111_regs[] = { 0x21, 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00, 0x88, 0x10, 0x80, 0x40, 0x40, 0x00, 0x01, 0x00, 0x48, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x71, 0xe7, 0x00, 0x00, 0xc0 }; if (w*h*2 > W9966_SRAMSIZE) { DPRINTF("capture window exceeds SRAM size!.\n"); w = 200; h = 160; } w &= ~0x1; if (w < 2) w = 2; if (h < 1) h = 1; if (w > W9966_WND_MAX_W) w = W9966_WND_MAX_W; if (h > W9966_WND_MAX_H) h = W9966_WND_MAX_H; cam->width = w; cam->height = h; enh_s = 0; enh_e = w*h*2; if ( w9966_calcscale(w, W9966_WND_MIN_X, W9966_WND_MAX_X, &x1, &x2, &scale_x) != 0 || w9966_calcscale(h, W9966_WND_MIN_Y, W9966_WND_MAX_Y, &y1, &y2, &scale_y) != 0 ) return -1; DPRINTF( "%dx%d, x: %d<->%d, y: %d<->%d, sx: %d/64, sy: %d/64.\n", w, h, x1, x2, y1, y2, scale_x&~0x80, scale_y&~0x80 ); regs[0x00] = 0x00; regs[0x01] = 0x18; regs[0x02] = scale_y; regs[0x03] = scale_x; regs[0x04] = (x1 & 0x0ff); regs[0x05] = (x1 & 0x300)>>8; regs[0x06] = (y1 & 0x0ff); regs[0x07] = (y1 & 0x300)>>8; regs[0x08] = (x2 & 0x0ff); regs[0x09] = (x2 & 0x300)>>8; regs[0x0a] = (y2 & 0x0ff); regs[0x0c] = W9966_SRAMID; regs[0x0d] = (enh_s& 0x000ff); regs[0x0e] = (enh_s& 0x0ff00)>>8; regs[0x0f] = (enh_s& 0x70000)>>16; regs[0x10] = (enh_e& 0x000ff); regs[0x11] = (enh_e& 0x0ff00)>>8; regs[0x12] = (enh_e& 0x70000)>>16; regs[0x13] = 0x40; regs[0x17] = 0x00; regs[0x18] = cam->i2c_state = 0x00; regs[0x19] = 0xff; regs[0x1a] = 0xff; regs[0x1b] = 0x10; saa7111_regs[0x0a] = cam->brightness; saa7111_regs[0x0b] = cam->contrast; saa7111_regs[0x0c] = cam->color; saa7111_regs[0x0d] = cam->hue; if (w9966_wReg(cam, 0x00, 0x03) == -1) return -1; for (i = 0; i < 0x1c; i++) if (w9966_wReg(cam, i, regs[i]) == -1) return -1; for (i = 0; i < 0x20; i++) if (w9966_wReg_i2c(cam, i, saa7111_regs[i]) == -1) return -1; return 0; }