/* * * Driver for HRT Pixelsmart 512-8 PCI card * * Authors: Brett W. Thompson, Gilberto Morejon, Alex Rudnick * */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif #include #include #include #include #include #include #include #include #include #include "i2c.h" #define PCI_VENDOR_ID_PIXELSMART 0x0004 #define PCI_DEVICE_ID_PIXELSMART 0x0400 #define PCI_DEVICE_ID_PIXELSMART_GRAY 0x0404 #define PCI_DEVICE_ID_PIXELSMART_COLOR 0x0408 /* Default base segment and bytes needed starting at this address */ #define DEFAULT_BASEADDR1 0x000dc000 #define DEFAULT_BASEADDR2 0x000d4000 #define BYTES_NEEDED 0x4000 static int possible_baseaddrs[] = { DEFAULT_BASEADDR1, DEFAULT_BASEADDR2 }; #define NUM_POSSIBLE_BASEADDRS 2 #define MAXWIDTH 512 #define MAXHEIGHT 480 #define BYTES_PER_PIXEL 1 #define PIXELSMART_VID_TYPE (VID_TYPE_CAPTURE | VID_TYPE_MONOCHROME) /* Maximum number of buffers for streaming */ #define MAX_CAPTURE_BUFFERS 16 /* XXX: should use real ioctl numbers! */ #define IOC_PIXELSMART_SET_I2CREG 1 #define IOC_PIXELSMART_GET_I2CREG 2 static unsigned char *i2c; /* Is set equal to io_base + 0x2001 */ /* Controls the user can set */ static const struct v4l2_queryctrl pixelsmart_ctls[] = { { .id = V4L2_CID_BRIGHTNESS, .name = "Brightness", .minimum = 0, .maximum = 255, .step = 1, .default_value = 0x9b, .type = V4L2_CTRL_TYPE_INTEGER, },{ .id = V4L2_CID_CONTRAST, .name = "Contrast", .minimum = 0, .maximum = 255, .step = 1, .default_value = 0x5e, .type = V4L2_CTRL_TYPE_INTEGER, } }; /* Small utility used in read()... Probably there's an official one somewhere in the kernel, huh? */ #ifndef min # define min(a,b) ((a)<(b) ? (a) : (b)) #endif /* This is for the I2C register ioctl */ struct i2c_regval { int reg; unsigned char val; }; /* Maybe we should put some of these in a struct like in c-qcam.c? I think it's done in there so as to have multiple cameras... */ static u32 start_addr = 0, len_addr; static int using_pci = 0; static struct pci_dev *dev = NULL; static unsigned char *io_base; static struct semaphore sem; static unsigned int brightness = 0x9b, contrast = 0x5e; /* Flags to control state */ volatile static int dontqueue = 0, field_bit = 0; /* XXX: should this go in a struct? Or have a better name? */ DECLARE_WAIT_QUEUE_HEAD(waitqueue); struct tq_struct task; int streaming = 0; /* The height and width of the image we send to the user */ static int width = 512, height = 480; /* The data for a frame for read() */ static unsigned char *framedata; /* Minor number, or -1 for first free */ static int video_nr = -1; /* Only one user can open at at time */ static unsigned int exclusive = 1; /* Debugging output */ static unsigned int debug = 1; /* Major number for hrtmem device */ static unsigned int hrtmem_major = 0; MODULE_PARM(debug, "i"); MODULE_PARM_DESC(debug, "Enable debugging output"); MODULE_PARM(exclusive, "i"); MODULE_PARM_DESC(exclusive, "Only one process can open the device (default: off)"); MODULE_PARM(video_nr, "i"); MODULE_PARM_DESC(video_nr, "Device's minor number (default: dynamic)"); MODULE_PARM(start_addr, "l"); MODULE_PARM_DESC(start_addr, "Force a base address"); MODULE_PARM(hrtmem_major, "i"); MODULE_PARM_DESC(hrtmem_major, "Major number for hrtmem device"); MODULE_AUTHOR("Brett W. Thompson, Gilberto Morejon, Alex Rudnick"); MODULE_DESCRIPTION("Driver for HRT Pixelsmart 512-8-PCI"); MODULE_LICENSE("GPL"); #define dprintk(fmt, arg...) if (debug) \ printk("pixelsmart: " fmt, ## arg) /* Prototypes */ void grabframe(void); void init_i2c(void); int set_brightness(unsigned char b); int set_contrast(unsigned char c); void inline grab_evenfield(unsigned char *frame); void inline grab_oddfield(unsigned char *frame); void timer_func_stream(void *ptr); void timer_func(void *ptr); int i2c_set_reg(int reg, unsigned char val); /* Prototypes for file_operations */ static int pixelsmart_open(struct inode *inode, struct file *file); static int pixelsmart_release(struct inode *inode, struct file *file); static int pixelsmart_mmap(struct file *file, struct vm_area_struct *vma); static int pixelsmart_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); static int pixelsmart_read(struct file *file, char *buf, size_t count, loff_t *ppos); static unsigned int pixelsmart_poll(struct file *file, poll_table *wait); /* Functions for direct mmap device */ static int pixelsmart_direct_open(struct inode *inode, struct file *file); static int pixelsmart_direct_release(struct inode *inode, struct file *file); static int pixelsmart_direct_mmap(struct file *file, struct vm_area_struct *vma); static int pixelsmart_direct_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg); /* vma functions for V4L device */ static void mmap_vma_open(struct vm_area_struct *vma); static void mmap_vma_close(struct vm_area_struct *vma); static struct page *mmap_vma_nopage(struct vm_area_struct *vma, unsigned long address, int write); static struct file_operations pixelsmart_fops = { .owner = THIS_MODULE, .open = pixelsmart_open, .mmap = pixelsmart_mmap, .release = pixelsmart_release, .ioctl = pixelsmart_ioctl, .read = pixelsmart_read, .llseek = no_llseek, .poll = pixelsmart_poll, }; static struct vm_operations_struct capture_vma_operations = { mmap_vma_open, mmap_vma_close, mmap_vma_nopage, }; /* This is the fops for the device that mmap's the device memory directly. For userspace driver. */ static struct file_operations pixelsmart_direct_fops = { mmap: pixelsmart_direct_mmap, open: pixelsmart_direct_open, release: pixelsmart_direct_release, ioctl: pixelsmart_direct_ioctl, }; /* XXX: Get an actual official number from kraxel@bytesex.org (see /usr/src/linux/include/linux/videodev.h) */ #define VID_HARDWARE_PIXELSMART 42 static struct video_device vdev = { .owner = THIS_MODULE, .name = "HRT Pixelsmart (PS512-8-PCI)", .type = PIXELSMART_VID_TYPE, .hardware = VID_HARDWARE_PIXELSMART, .fops = &pixelsmart_fops, }; /* See if the card is at address test_addr. Returns 0 if it's not, 1 if it is. */ static int probe(u32 test_addr) { unsigned char *base; unsigned char x1, x2, x3, y, ctrl; /* Arbitrary values we write into memory */ const unsigned char LINE1 = 10, LINE2 = 20, X1 = 42, X2 = 66; if (!request_mem_region(test_addr, BYTES_NEEDED, "Pixelsmart")) { return 0; } base = ioremap_nocache(test_addr, BYTES_NEEDED); /* Save values */ ctrl = readb(base + 0x2000); y = readb(base + 0x2002); /* Freeze immediately; this will allow us to modify the card's RAM */ writeb(0x5B, base + 0x2000); /* Move to LINE1 */ writeb(LINE1, base + 0x2002); /* Save the value */ x1 = readb(base); /* Write an arbitrary value into a pixel */ writeb(X1, base); /* Move to second line */ writeb(LINE2, base + 0x2002); /* Save the value */ x2 = readb(base); /* Write the second arbitrary value */ writeb(X2, base); /* Now go back to the first line */ writeb(LINE1, base + 0x2002); /* Is our value still there? */ x3 = readb(base); /* Restore previous values */ writeb(x2, base); writeb(LINE1, base + 0x2002); writeb(x1, base); writeb(y, base + 0x2002); writeb(ctrl, base + 0x2000); /* XXX: is this the right order to do these in? */ release_mem_region(test_addr, BYTES_NEEDED); iounmap(base); if (x3 == X1) { return 1; } else { return 0; } } static void mmap_vma_open(struct vm_area_struct *vma) { dprintk("mmap_vma_open called\n"); } static void mmap_vma_close(struct vm_area_struct *vma) { dprintk("mmap_vma_close called\n"); } /* Based on ldd2's "simple" example driver */ static struct page *mmap_vma_nopage(struct vm_area_struct *vma, unsigned long address, int write) { struct page *page; pgd_t *pgd; pmd_t *pmd; pte_t *pte; unsigned long lpage; dprintk("mmap_vma_nopage called\n"); down(&sem); lpage = VMALLOC_VMADDR(framedata); spin_lock(&init_mm.page_table_lock); pgd = pgd_offset(&init_mm, lpage); pmd = pmd_offset(pgd, lpage); pte = pte_offset(pmd, lpage); page = pte_page(*pte); spin_unlock(&init_mm.page_table_lock); /* got it, now increment the count */ get_page(page); up(&sem); return page; } static int pixelsmart_direct_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { dprintk("ioctl: %x\n", cmd); if (cmd == IOC_PIXELSMART_SET_I2CREG) { struct i2c_regval r; copy_from_user(&r, (void *)arg, sizeof(r)); dprintk("%x, %d", r.reg, r.val); i2c_set_reg(r.reg, r.val); return 0; } else if (cmd == IOC_PIXELSMART_GET_I2CREG) { struct i2c_regval r; /* XXX: i2c_get_reg does not exist because I have no idea how to read from a register via I2C! */ return -EINVAL; /* r.val = i2c_get_reg(r.reg); */ copy_to_user(&r, (void *)arg, sizeof(r)); dprintk("%x, %d", r.reg, r.val); } return -EINVAL; } static const char *v4l1_ioctls[] = { "?", "CGAP", "GCHAN", "SCHAN", "GTUNER", "STUNER", "GPICT", "SPICT", "CCAPTURE", "GWIN", "SWIN", "GFBUF", "SFBUF", "KEY", "GFREQ", "SFREQ", "GAUDIO", "SAUDIO", "SYNC", "MCAPTURE", "GMBUF", "GUNIT", "GCAPTURE", "SCAPTURE", "SPLAYMODE", "SWRITEMODE", "GPLAYINFO", "SMICROCODE", "GVBIFMT", "SVBIFMT" }; static int pixelsmart_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { /* This nice nugget copied from xawtv's libng */ switch (_IOC_TYPE(cmd)) { case 'v': dprintk("ioctl 0x%x (v4l1, VIDIOC%s)\n", cmd, (_IOC_NR(cmd) < ARRAY_SIZE(v4l1_ioctls)) ? v4l1_ioctls[_IOC_NR(cmd)] : "???"); break; case 'V': dprintk("ioctl 0x%x (v4l2, %s)\n", cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); break; default: dprintk("ioctl 0x%x (?)\n", cmd); } switch(cmd) { /*** video4linux 2 ***/ case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; req->count = 1; req->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; req->memory = V4L2_MEMORY_MMAP; return 0; } case VIDIOC_QUERYBUF: { struct v4l2_buffer *buf = arg; dprintk("QUERYBUF called\n"); if (buf->index > 0) return -EINVAL; buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf->m.offset = 0; buf->length = 512*480; buf->bytesused = buf->length; return 0; } case VIDIOC_QBUF: { struct v4l2_buffer *buf = arg; if (buf->index > 0) return -EINVAL; buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf->m.offset = 0; buf->length = 512*480; buf->bytesused = buf->length; buf->flags &= ~V4L2_BUF_FLAG_DONE; buf->flags |= V4L2_BUF_FLAG_QUEUED; buf->flags |= V4L2_BUF_FLAG_MAPPED; return 0; } case VIDIOC_DQBUF: { struct v4l2_buffer *buf = arg; buf->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; buf->m.offset = 0; buf->length = 512*480; buf->bytesused = buf->length; buf->flags &= ~V4L2_BUF_FLAG_QUEUED; buf->flags |= V4L2_BUF_FLAG_DONE; return 0; } case VIDIOC_STREAMON: { /* XXX: What's this argument for? */ /* __u32 *type = arg;*/ streaming = 1; task.routine = timer_func_stream; return 0; } case VIDIOC_STREAMOFF: { /* __u32 *type = arg;*/ streaming = 0; task.routine = timer_func; return 0; } case VIDIOC_QUERYCAP: { struct v4l2_capability *b = (struct v4l2_capability *)arg; strcpy(b->driver, "hrt"); strncpy(b->card, "PS 512-8-PCI", sizeof(b->card)); if (using_pci) sprintf(b->bus_info, "PCI:%s", dev->slot_name); else b->bus_info[0] = 0; b->version = KERNEL_VERSION(0, 0, 2); /* XXX: Thus far, no streaming :( */ #if 0 b->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; #endif b->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; return 0; } case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *f = arg; enum v4l2_buf_type type = f->type; int index = f->index; dprintk("ioctl: VIDIOC_ENUM_FMT\n"); /* There's only one format */ if (index > 0) return -EINVAL; if (type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { memset(f, 0, sizeof(*f)); f->index = index; f->type = type; f->pixelformat = V4L2_PIX_FMT_GREY; strcpy(f->description, "Grey"); return 0; } else { return -EINVAL; } } case VIDIOC_TRY_FMT: case VIDIOC_S_FMT: case VIDIOC_G_FMT: { struct v4l2_format *f = arg; dprintk("ioctl: VIDIOC_G_FMT\n"); if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format)); f->fmt.pix.width = width; f->fmt.pix.height = height; f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; f->fmt.pix.bytesperline = width; f->fmt.pix.sizeimage = width * height; return 0; } else { return -EINVAL; } } case VIDIOC_G_STD: { v4l2_std_id *id = arg; *id = V4L2_STD_NTSC; return 0; } case VIDIOC_S_STD: { /* v4l2_std_id *id = arg; if (*id != V4L2_STD_NTSC) { return -EINVAL; } */ dprintk("VIDIOC_S_STD called\n"); return 0; } case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *c = arg; int i; for (i = 0; i < ARRAY_SIZE(pixelsmart_ctls); i++) if (pixelsmart_ctls[i].id == c->id) break; /* Didn't find the id */ if (i == ARRAY_SIZE(pixelsmart_ctls)) { return -EINVAL; } *c = pixelsmart_ctls[i]; return 0; } case VIDIOC_G_CTRL: { struct v4l2_control *c = arg; int i; for (i = 0; i < ARRAY_SIZE(pixelsmart_ctls); i++) if (pixelsmart_ctls[i].id == c->id) break; if (i == sizeof(pixelsmart_ctls)) return -EINVAL; if (c->id == V4L2_CID_BRIGHTNESS) { c->value = brightness; } else if (c->id == V4L2_CID_CONTRAST) { c->value = contrast; } else { return -EINVAL; } return 0; } case VIDIOC_S_CTRL: { struct v4l2_control *c = arg; int i; for (i = 0; i < sizeof(pixelsmart_ctls); i++) if (pixelsmart_ctls[i].id == c->id) break; if (i == sizeof(pixelsmart_ctls)) return -EINVAL; if (c->id == V4L2_CID_BRIGHTNESS) { if (c->value > 255) return -EINVAL; c->value = set_brightness(c->value); } else if (c->id == V4L2_CID_CONTRAST) { if (c->value > 255) return -EINVAL; c->value = set_contrast(c->value); } else { return -EINVAL; } return 0; } case VIDIOC_ENUMINPUT: { struct v4l2_input *i = arg; unsigned int n; dprintk("ioctl: VIDIOC_ENUMINPUT\n"); /* Only one input */ if (i->index > 0) return -EINVAL; n = i->index; memset(i, 0, sizeof(*i)); i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; sprintf(i->name, "Camera"); return 0; } case VIDIOC_ENUMSTD: { struct v4l2_standard *e = arg; struct v4l2_fract fract = { 1001, 30000 }; unsigned int index = e->index; /* One standard (NTSC) */ if (index > 0) return -EINVAL; dprintk("ioctl: VIDIOC_ENUMSTD\n"); /* v4l2_video_std_construct(e, V4L2_STD_NTSC_M, "NTSC");*/ e->id = V4L2_STD_NTSC_M; e->index = index; strcpy(e->name, "NTSC"); e->frameperiod = fract; e->framelines = height; return 0; } case VIDIOC_G_PARM: { struct v4l2_streamparm *parm = arg; struct v4l2_standard s; dprintk("VIDIOC_G_PARM called\n"); if (parm->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; memset(parm, 0, sizeof(*parm)); v4l2_video_std_construct(&s, V4L2_STD_NTSC, "NTSC"); parm->parm.capture.timeperframe = s.frameperiod; return 0; } case VIDIOC_G_INPUT: { dprintk("VIDIOC_G_INPUT called\n"); return 0; } case VIDIOC_S_INPUT: { dprintk("VIDIOC_S_INPUT called\n"); return 0; } /*** video4linux 1 ***/ case VIDIOCGCAP: { struct video_capability *b = arg; strcpy(b->name, "Pixelsmart"); b->type = PIXELSMART_VID_TYPE; b->channels = 1; b->audios = 0; b->maxwidth = width; b->maxheight = height; b->minwidth = width; b->minheight = height; return 0; } 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, "Camera Input"); return 0; } case VIDIOCSCHAN: { struct video_channel *v = arg; if (v->channel != 0) return -EINVAL; return 0; } case VIDIOCGTUNER: { struct video_tuner *v = arg; if (v->tuner) return -EINVAL; memset(v, 0, sizeof(*v)); strcpy(v->name, "Format"); v->mode = VIDEO_MODE_AUTO; return 0; } case VIDIOCSTUNER: { struct video_tuner *v = arg; if (v->tuner) return -EINVAL; if (v->mode != VIDEO_MODE_AUTO) return -EINVAL; return 0; } case VIDIOCGPICT: { struct video_picture *p = arg; /* XXX: We don't have colour and some of these other ones... */ #if 0 p->colour = 0x8000; p->hue = 0x8000; p->whiteness = 0x8000; #endif p->brightness = brightness; p->contrast = contrast; /* These are sure to be right */ p->depth = 8; p->palette = VIDEO_PALETTE_GREY; return 0; } case VIDIOCSPICT: { struct video_picture *p = arg; if (p->depth != 8 || p->palette != VIDEO_PALETTE_GREY) return -EINVAL; if (p->brightness > 255 || p->contrast > 255) return -EINVAL; brightness = p->brightness; contrast = p->contrast; set_brightness(brightness); set_contrast(contrast); return 0; } case VIDIOCSWIN: { /* Keep our width and height unchanged */ #if 0 struct video_window *vw = arg; if (vw->flags || vw->clipcount) return -EINVAL; if (vw->width > MAXWIDTH || vw->height > MAXHEIGHT) return -EINVAL; down(&sem); width = vw->width; height = vw->height; up(&sem); dprintk("Set width = %d, height = %d\n", width, height); #endif return 0; } case VIDIOCGWIN: { struct video_window *vw = arg; memset(vw, 0, sizeof(*vw)); vw->width = width; vw->height = height; return 0; } case VIDIOCSYNC: { dprintk("VIDIOCSYNC called\n"); } case VIDIOCKEY: return 0; case VIDIOCCAPTURE: case VIDIOCGFBUF: case VIDIOCGMBUF: case VIDIOCSFBUF: case VIDIOCGFREQ: case VIDIOCSFREQ: case VIDIOCGAUDIO: case VIDIOCSAUDIO: return -EINVAL; default: dprintk("Unknown ioctl cmd = 0x%x\n", cmd); return v4l_compat_translate_ioctl(inode, file, cmd, arg, pixelsmart_do_ioctl); /* return -ENOIOCTLCMD; */ } } static int pixelsmart_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, pixelsmart_do_ioctl); } /* This was our original read, without the timer. */ #if 0 static int pixelsmart_read(struct file *file, char *buf, size_t count, loff_t *ppos) { int len; /* dprintk("Request to read %d bytes, returning %d\n", count, len);*/ len = min((size_t)(width * height), count); down_interruptible(&sem); grabframe(); if (copy_to_user(buf, framedata, len)) { up(&sem); return -EFAULT; } up(&sem); return len; } #endif /* New, improved read; depends on timer_func to change the field_bit. Clocked at 23 FPS :) */ static int pixelsmart_read(struct file *file, char *buf, size_t count, loff_t *ppos) { int len; int old_field; len = min((size_t)(width * height), count); down_interruptible(&sem); old_field = field_bit; /* Grab a field */ if (old_field) { grab_evenfield(framedata); } else { grab_oddfield(framedata); } /* Go to sleep until the field bit changes */ while (old_field == field_bit) { up(&sem); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible(waitqueue, (old_field != field_bit))) return -ERESTARTSYS; if (down_interruptible(&sem)) return -ERESTARTSYS; } /* Grab the other field */ if (field_bit) { grab_evenfield(framedata); } else { grab_oddfield(framedata); } dprintk("Read %d bytes\n", len); if (copy_to_user(buf, framedata, len)) { up(&sem); return -EFAULT; } up(&sem); return len; } void timer_func(void *ptr) { int new_field; if (dontqueue) return; new_field = readb(io_base + 0x2000) & 1; if (new_field != field_bit) { field_bit = new_field; wake_up(&waitqueue); } queue_task(&task, &tq_timer); } /* XXX: Warning: under construction. Streaming doesn't work at the moment. */ void timer_func_stream(void *ptr) { int new_field; /* unsigned long flags; */ if (dontqueue) return; new_field = readb(io_base + 0x2000) & 1; if (new_field != field_bit) { field_bit = new_field; /* save_flags(flags); cli(); */ grab_evenfield(framedata); grab_oddfield(framedata); // restore_flags(flags); /* buf->vidbuf.bytesused = 512*480; */ // buf->vidbuf.flags |= V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_KEYFRAME; wake_up(&waitqueue); } queue_task(&task, &tq_timer); } int pixelsmart_open(struct inode *inode, struct file *file) { struct video_device *vfl = video_devdata(file); int retval = 0; down(&vfl->lock); if (vfl->users) { retval = -EBUSY; } else { if (exclusive) vfl->users++; /* Get the timer going */ task.routine = timer_func; dontqueue = 0; queue_task(&task, &tq_timer); } up(&vfl->lock); return retval; } static int pixelsmart_release(struct inode *inode, struct file *file) { struct video_device *vfl = video_devdata(file); if (exclusive) vfl->users--; /* Tell the timer to stop */ dontqueue = 1; return 0; } static int pixelsmart_mmap(struct file *file, struct vm_area_struct *vma) { dprintk("mmap was called\n"); vma->vm_ops = &capture_vma_operations; if (vma->vm_ops->open) vma->vm_ops->open(vma); return 0; } /* XXX: possibly incorrect which is a possible reason for streaming not working. */ unsigned int pixelsmart_poll(struct file *file, poll_table *wait) { struct video_device *vfl = video_devdata(file); dprintk("poll was called!\n"); down(&vfl->lock); poll_wait(file, &waitqueue, wait); dprintk("POLL woke up\n"); up(&vfl->lock); return POLLIN | POLLRDNORM; } int pixelsmart_pci_init(void) { if (!pci_present()) { dprintk(KERN_DEBUG "pixelsmart: PCI not present\n"); return 1; } dev = pci_find_device(PCI_VENDOR_ID_PIXELSMART, PCI_DEVICE_ID_PIXELSMART_GRAY, NULL); if (!dev) { dprintk("pixelsmart: Device not found on PCI\n"); return 1; } pci_enable_device(dev); start_addr = pci_resource_start(dev, 0); dprintk("Address from pci_resource_start = %lx\n", (unsigned long)start_addr); len_addr = pci_resource_len(dev, 0); dprintk("Length from pci_resource_len = %lx\n", (unsigned long)len_addr); return 0; } int init_module(void) { int i, result; /* Register our direct-to-device-memory character device */ result = register_chrdev(hrtmem_major, "hrtmem", &pixelsmart_direct_fops); if (result < 0) return result; if (hrtmem_major == 0) hrtmem_major = result; /* XXX: Put in support for devfs!! */ dprintk("pixelsmart: Registered hrtmem with major number = %d\n", hrtmem_major); if (start_addr == 0) { /* XXX: could use that nifty sizeof() trick to get size of array instead of a #define */ for (i = 0; i < NUM_POSSIBLE_BASEADDRS; i++) { if (probe(possible_baseaddrs[i])) { dprintk("pixelsmart: found card at %x\n", possible_baseaddrs[i]); start_addr = possible_baseaddrs[i]; len_addr = BYTES_NEEDED; using_pci = 0; break; } } /* The hardwired addresses didn't work, so PCI must be it */ if (start_addr == 0) { if (pixelsmart_pci_init() != 0) { return 1; } using_pci = 1; } } else { /* User passed start_addr parameter */ len_addr = BYTES_NEEDED; } /* Reserve our memory range */ if (!request_mem_region(start_addr, len_addr, "Pixelsmart")) { dprintk("pixelsmart: memory already in use\n"); return -EBUSY; } /* Remap it so we can access it */ io_base = ioremap_nocache(start_addr, BYTES_NEEDED); /* Initialize semaphore */ sema_init(&sem, 1); /* Register with V4L */ if (video_register_device(&vdev, VFL_TYPE_GRABBER, video_nr) < 0) { dprintk(KERN_ERR "Unable to register Pixelsmart\n"); return -ENODEV; } /* Initialize Philips A/D chip via I2C */ i2c = io_base + 0x2001; init_i2c(); /* Read the field id bit (should be 0 since we're shouldn't be in live mode yet) */ field_bit = readb(io_base + 0x2000) & 1; /* Put the card into Live mode */ writeb(0x91, io_base + 0x2000); /* Allocate frame buffer memory */ framedata = (unsigned char *)vmalloc(MAXWIDTH * MAXHEIGHT * BYTES_PER_PIXEL); if (framedata == NULL) { dprintk(KERN_ERR "Unable to allocate memory\n"); return -ENOMEM; } return 0; } static int pixelsmart_direct_open(struct inode *inode, struct file *file) { dprintk("pixelsmart_direct_open called\n"); return 0; } static int pixelsmart_direct_release(struct inode *inode, struct file *file) { dprintk("pixelsmart_direct_release called\n"); return 0; } void pixelsmart_direct_vma_open(struct vm_area_struct *vma) { dprintk("pixelsmart_direct_vma_open called\n"); } void pixelsmart_direct_vma_close(struct vm_area_struct *vma) { dprintk("pixelsmart_direct_vma_close called\n"); } static struct vm_operations_struct pixelsmart_direct_vm_ops = { open: pixelsmart_direct_vma_open, close: pixelsmart_direct_vma_close, }; static int pixelsmart_direct_mmap(struct file *file, struct vm_area_struct *vma) { dprintk("pixelsmart_direct_mmap called!\n"); vma->vm_flags |= (VM_IO | VM_RESERVED); vma->vm_ops = &pixelsmart_direct_vm_ops; /* Just map the pages directly */ if (remap_page_range(vma->vm_start, start_addr, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; } void cleanup_module(void) { /* Release our resouces */ video_unregister_device(&vdev); unregister_chrdev(hrtmem_major, "hrtmem"); release_mem_region(start_addr, len_addr); iounmap(io_base); vfree(framedata); } void inline grab_evenfield(unsigned char *frame) { int i; for (i = 0; i < height; i += 2) { /* Move the y register (current line) */ writew(i, io_base + 0x2002); wmb(); memcpy_fromio(&frame[i * width], io_base, width); } } void inline grab_oddfield(unsigned char *frame) { int i; for (i = 1; i < height; i += 2) { /* Move the y register (current line) */ writew(i, io_base + 0x2002); wmb(); memcpy_fromio(&frame[i * width], io_base, width); } } /* grabframe() was used by our old read */ #if 0 void grabframe(void) { unsigned int i; writeb(0x91, io_base + 0x2000); /* LIVE VIDEO command */ mdelay(35); writeb(0x99, io_base + 0x2000); /* FREEZE command */ for (i = 0; i < height; i++) { /* Move the y register (current line) */ writew(i, io_base + 0x2002); wmb(); memcpy_fromio(&framedata[i * width], io_base, width); } } #endif /* I2C routines */ void i2c_sendstop(void) { int i; for (i = 0; i < 5; i++) { writeb(i2c_stopcmd[i], i2c); wmb(); } } void i2c_sendstart(void) { int i; for (i = 0; i < 5; i++) { writeb(i2c_startcmd[i], i2c); wmb(); } } void i2c_sendbit(unsigned char sda) { unsigned char tmp; writeb(sda << 1, i2c); writeb((sda << 1) + 4, i2c); wmb(); for (;;) { tmp = readb(io_base + 0x2000); if ((tmp & 128) == 0) { static int dontfreeze = 0; mdelay(1); schedule(); dontfreeze++; if (dontfreeze > 10000) break; } else { break; } } writeb(2, i2c); wmb(); } void i2c_sendbyte(unsigned char byte) { int i, sda; unsigned char ack; for (i = 7; i >= 0; i--) { sda = (byte & (1 << i)) >> i; i2c_sendbit(sda); } /* Check for ACK */ writeb(3, i2c); wmb(); ack = readb(i2c); /* dprintk("ack = %d\n", ack); */ if ((ack & 2) == 2) { dprintk("Error: no ACK after sending a databyte\n"); } writeb(2, i2c); wmb(); } void init_i2c(void) { int i; /* Send start command */ i2c_sendstart(); /* Send unique A/D address */ i2c_sendbyte(HRT_AD_DEVICE_ID); /* Set device's pointer to 0 */ i2c_sendbyte(0); for (i = 0; i < 26; i++) { i2c_sendbyte(i2c_datalow[i]); } i2c_sendstop(); i2c_sendstart(); i2c_sendbyte(HRT_AD_DEVICE_ID); /* Set address pointer = 0x20 */ i2c_sendbyte(0x20); for (i = 0; i < 21; i++) { i2c_sendbyte(i2c_datahigh[i]); } i2c_sendstop(); } /* Set an I2C register */ int i2c_set_reg(int reg, unsigned char val) { i2c_sendstart(); i2c_sendbyte(HRT_AD_DEVICE_ID); i2c_sendbyte(reg); i2c_sendbyte(val); i2c_sendstop(); return val; } int set_brightness(unsigned char b) { i2c_set_reg(0x19, b); return b; } int set_contrast(unsigned char c) { i2c_set_reg(0x13, c); return c; } /* Say no to namespace pollution! */ EXPORT_NO_SYMBOLS;