📄 w9966.c
字号:
{ // saa7111 regs 0x00 trough 0x12 const u8 regs[] = { 0x00, // not written 0x00, 0xd8, 0x23, 0x00, 0x80, 0x80, 0x00, 0x88, 0x10, cam->brightness, // 0x0a cam->contrast, // 0x0b cam->color, // 0x0c cam->hue, // 0x0d 0x01, 0x00, 0x48, 0x0c, 0x00, }; int i; if (w9966_flag_test(cam, W9966_STATE_DETECTED)) return 1; // Write regs to saa7111 chip for (i = 1; i < 0x13; i++) if (!w9966_i2c_wreg(cam, W9966_SAA7111_ID, i, regs[i])) return 1; // Read back regs for (i = 1; i < 0x13; i++) if (w9966_i2c_rreg(cam, W9966_SAA7111_ID, i) != regs[i]) return 1; // Fill in model specific data cam->name = "Lifeview Flycam Supra"; cam->sramsize = 128 << 10; // 128 kib cam->sramid = 0x02; // see w9966.pdf cam->cmask = 0x18; // normal polarity cam->min_x = 16; // empirically determined cam->max_x = 705; cam->min_y = 14; cam->max_y = 253; cam->image = &w9966_saa7111_image; DPRINTF("Found and initialized a saa7111 chip.\n"); w9966_flag_set(cam, W9966_STATE_DETECTED); return 1;}// Setup image properties (brightness, hue, etc.) for the saa7111 chip// expects a claimed parport// 1 on success, else 0static int w9966_saa7111_image(struct w9966_dev* cam){ if (!w9966_i2c_wreg(cam, W9966_SAA7111_ID, 0x0a, cam->brightness) || !w9966_i2c_wreg(cam, W9966_SAA7111_ID, 0x0b, cam->contrast) || !w9966_i2c_wreg(cam, W9966_SAA7111_ID, 0x0c, cam->color) || !w9966_i2c_wreg(cam, W9966_SAA7111_ID, 0x0d, cam->hue)) return 0; return 1;}// Detect and initialize lc99053 ccd-controller chip.// expects a claimed parport// this is currently a hack, no detection is done, we just assume an Eyestar2// 1 on success, else 0static int w9966_lc99053_init(struct w9966_dev* cam){ if (w9966_flag_test(cam, W9966_STATE_DETECTED)) return 1; // Fill in model specific data cam->name = "Microtek Eyestar2"; cam->sramsize = 128 << 10; // 128 kib cam->sramid = 0x02; // w9966cf.pdf cam->cmask = 0x10; // reverse polarity cam->min_x = 16; // empirically determined cam->max_x = 705; cam->min_y = 14; cam->max_y = 253; cam->image = &w9966_lc99053_image; DPRINTF("Found and initialized a lc99053 chip.\n"); w9966_flag_set(cam, W9966_STATE_DETECTED); return 1;}// Setup image properties (brightness, hue, etc.) for the lc99053 chip// expects a claimed parport// 1 on success, else 0static int w9966_lc99053_image(struct w9966_dev* cam){ return 1;}/* * Ugly and primitive i2c protocol functions */// Sets the data line on the i2c bus.// Expects a claimed pdev.static inline void w9966_i2c_setsda(struct w9966_dev* cam, int state){ if (state) cam->i2c_state |= W9966_I2C_W_DATA; else cam->i2c_state &= ~W9966_I2C_W_DATA; w9966_wreg(cam, 0x18, cam->i2c_state); udelay(W9966_I2C_UDELAY);}// Sets the clock line on the i2c bus.// Expects a claimed pdev.// 1 on success, else 0static inline int w9966_i2c_setscl(struct w9966_dev* cam, int state){ if (state) cam->i2c_state |= W9966_I2C_W_CLOCK; else cam->i2c_state &= ~W9966_I2C_W_CLOCK; w9966_wreg(cam, 0x18, cam->i2c_state); udelay(W9966_I2C_UDELAY); // when we go to high, we also expect the peripheral to ack. if (state) { const int timeout = jiffies + W9966_I2C_TIMEOUT; while (!w9966_i2c_getscl(cam)) { if (time_after(jiffies, timeout)) return 0; } } return 1;}// Get peripheral data line// Expects a claimed pdev.static inline int w9966_i2c_getsda(struct w9966_dev* cam){ const u8 pins = w9966_rreg(cam, 0x18); return ((pins & W9966_I2C_R_DATA) > 0);}// Get peripheral clock line// Expects a claimed pdev.static inline int w9966_i2c_getscl(struct w9966_dev* cam){ const u8 pins = w9966_rreg(cam, 0x18); return ((pins & W9966_I2C_R_CLOCK) > 0);}// Write a byte with ack to the i2c bus.// Expects a claimed pdev.// 1 on success, else 0static int w9966_i2c_wbyte(struct w9966_dev* cam, int data){ int i; for (i = 7; i >= 0; i--) { w9966_i2c_setsda(cam, (data >> i) & 0x01); if (!w9966_i2c_setscl(cam, 1) || !w9966_i2c_setscl(cam, 0)) return 0; } w9966_i2c_setsda(cam, 1); if (!w9966_i2c_setscl(cam, 1) || !w9966_i2c_setscl(cam, 0)) return 0; return 1;}// Read a data byte with ack from the i2c-bus// Expects a claimed pdev. -1 on errorstatic int w9966_i2c_rbyte(struct w9966_dev* cam){ u8 data = 0x00; int i; w9966_i2c_setsda(cam, 1); for (i = 0; i < 8; i++) { if (!w9966_i2c_setscl(cam, 1)) return -1; data = data << 1; if (w9966_i2c_getsda(cam)) data |= 0x01; w9966_i2c_setscl(cam, 0); } return data;}// Read a register from the i2c device.// Expects claimed pdev. -1 on errorstatic int w9966_i2c_rreg(struct w9966_dev* cam, int device, int reg){ int data; w9966_i2c_setsda(cam, 0); w9966_i2c_setscl(cam, 0); if (!w9966_i2c_wbyte(cam, device << 1) || !w9966_i2c_wbyte(cam, reg)) return -1; w9966_i2c_setsda(cam, 1); if (!w9966_i2c_setscl(cam, 1)) return -1; w9966_i2c_setsda(cam, 0); w9966_i2c_setscl(cam, 0); if (!w9966_i2c_wbyte(cam, (device << 1) | 1) || (data = w9966_i2c_rbyte(cam)) == -1) return -1; w9966_i2c_setsda(cam, 0); if (!w9966_i2c_setscl(cam, 1)) return -1; w9966_i2c_setsda(cam, 1); return data;}// Write a register to the i2c device.// Expects claimed pdev.// 1 on success, else 0static int w9966_i2c_wreg(struct w9966_dev* cam, int device, int reg, int data){ w9966_i2c_setsda(cam, 0); w9966_i2c_setscl(cam, 0); if (!w9966_i2c_wbyte(cam, device << 1) || !w9966_i2c_wbyte(cam, reg) || !w9966_i2c_wbyte(cam, data)) return 0; w9966_i2c_setsda(cam, 0); if (!w9966_i2c_setscl(cam, 1)) return 0; w9966_i2c_setsda(cam, 1); return 1;}/* * Video4linux interface */static int w9966_v4l_open(struct video_device *vdev, int flags){ struct w9966_dev *cam = (struct w9966_dev*)vdev->priv; // Claim parport if (!w9966_pdev_claim(cam)) { DPRINTF("Unable to claim parport"); return -EFAULT; } // Allocate read buffer cam->buffer = (u8*)kmalloc(W9966_RBUFFER, GFP_KERNEL); if (cam->buffer == NULL) { w9966_pdev_release(cam); return -ENOMEM; } w9966_flag_set(cam, W9966_STATE_BUFFER); return 0;}static void w9966_v4l_close(struct video_device *vdev){ struct w9966_dev *cam = (struct w9966_dev*)vdev->priv; // Free read buffer if (w9966_flag_test(cam, W9966_STATE_BUFFER)) { kfree(cam->buffer); w9966_flag_clear(cam, W9966_STATE_BUFFER); } // release parport w9966_pdev_release(cam);}// expects a claimed parportstatic 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 cam->max_x - cam->min_x, cam->max_y - cam->min_y, W9966_WND_MIN_W, W9966_WND_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; if (!cam->image(cam)) return -EFAULT; return 0; } case VIDIOCSWIN: { struct video_window vwin; if (copy_from_user(&vwin, arg, sizeof(vwin)) != 0) return -EFAULT; if ( vwin.flags != 0 || vwin.clipcount != 0) return -EINVAL; if (vwin.width > cam->max_x - cam->min_x || vwin.height > cam->max_y - cam->min_y || vwin.width < W9966_WND_MIN_W || vwin.height < W9966_WND_MIN_H) return -EINVAL; // Update camera regs if (!w9966_window(cam, 0, 0, 1023, 1023, vwin.width, vwin.height)) 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;}// Capture data// expects a claimed parport and allocated read bufferstatic long w9966_v4l_read(struct video_device *vdev, char *buf, unsigned long count, int noblock){ struct w9966_dev *cam = (struct w9966_dev *)vdev->priv; const u8 addr = 0xa0; // ECP, read, CCD-transfer, 00000 u8* dest = (u8*)buf; unsigned long dleft = count; // Why would anyone want more than this?? if (count > cam->width * cam->height * 2) count = cam->width * cam->height * 2; w9966_wreg(cam, 0x00, 0x02); // Reset ECP-FIFO buffer w9966_wreg(cam, 0x00, 0x00); // Return to normal operation w9966_wreg(cam, 0x01, cam->cmask | 0x80); // 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) { DPRINTF("Unable to write capture-addr.\n"); return -EFAULT; } while(dleft > 0) { const size_t tsize = (dleft > W9966_RBUFFER) ? W9966_RBUFFER : dleft; if (parport_read(cam->pport, cam->buffer, tsize) < tsize) return -EFAULT; if (copy_to_user(dest, cam->buffer, tsize) != 0) return -EFAULT; dest += tsize; dleft -= tsize; } w9966_wreg(cam, 0x01, cam->cmask); // Disable capture return count;}// Called once for every parport on initstatic void w9966_attach(struct parport *port){ int i; for (i = 0; i < W9966_MAXCAMS; i++) { if (strcmp(pardev[i], "none") == 0 || // Skip if 'none' or if w9966_cams[i].dev_state != 0) // cam already assigned continue; if (strcmp(pardev[i], "auto") == 0 || strcmp(pardev[i], port->name) == 0) { if (!w9966_init(&w9966_cams[i], port, video_nr[i])) w9966_term(&w9966_cams[i]); break; // return } }}// Called once for every parport on terminationstatic void w9966_detach(struct parport *port){ int i; for (i = 0; i < W9966_MAXCAMS; i++) if (w9966_cams[i].dev_state != 0 && w9966_cams[i].pport == port) w9966_term(&w9966_cams[i]);}static struct parport_driver w9966_ppd = { W9966_DRIVERNAME, w9966_attach, w9966_detach, NULL};// Module entry pointstatic int __init w9966_mod_init(void){ int i, err; w9966_cams = kmalloc( sizeof(struct w9966_dev) * W9966_MAXCAMS, GFP_KERNEL); if (!w9966_cams) return -ENOMEM; for (i = 0; i < W9966_MAXCAMS; i++) w9966_cams[i].dev_state = 0; // Register parport driver if ((err = parport_register_driver(&w9966_ppd)) != 0) { kfree(w9966_cams); w9966_cams = 0; return err; } return 0;}// Module cleanupstatic void __exit w9966_mod_term(void){ if (w9966_cams) kfree(w9966_cams); parport_unregister_driver(&w9966_ppd);}module_init(w9966_mod_init);module_exit(w9966_mod_term);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -