/* * video.c -- a module that probes the pixelsmart card * * 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; */ /* $Id: video.c,v 1.44 2003/06/19 16:51:34 holtz Exp $ */ #ifndef __KERNEL__ # define __KERNEL__ #endif #ifndef MODULE # define MODULE #endif unsigned long Actual_Base = 0; unsigned long Actual_Len = 0; static int buflen = 0; #include "video.h" #include "i2c.h" static int video_nr = -1; MODULE_PARM(video_nr,"i"); static int video_nr2 = 1; MODULE_PARM(video_nr2,"i"); static int height = 512; MODULE_PARM(height,"i"); static int width = 512; MODULE_PARM(width,"i"); static int depth = 8; MODULE_PARM(depth,"i"); MODULE_AUTHOR("Ashtosh Chickerur and Darren Holtz"); MODULE_LICENSE("GPL"); /*----------------------------------------------------------------------------*/ static struct stream_buffer * mmap_stream_buffer_from_offset(struct ps_device *dev, unsigned long off) { int i; int offset = off * PAGE_SIZE; for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) if (offset == dev->stream_buf[i].vidbuf.m.offset) return &dev->stream_buf[i]; return NULL; } /*----------------------------------------------------------------------------*/ static int capture_begin(struct ps_device *dev) { if (dev->ready_to_capture) return dev->ready_to_capture; return (dev->ready_to_capture = 1); } /*----------------------------------------------------------------------------*/ /* Start an image capture */ static void capture_grab_frame(unsigned long number) { // static int frame_counter= HRTGRAB_SLOWDOWN; static int old_field_id = 2; static int set_counter = 0; int field_id, i; spin_lock(&ps_data.slock); writeb(0x91, ps_data.mem + 0x2000); /* LIVE VIDEO command */ if (old_field_id == (field_id = (readb(ps_data.mem + 0x2000) & 0x01)) ) goto out; /* Save the new field_id */ /* Select the even or the odd lines to read from driver */ i = !(old_field_id = field_id); #if 0 /* Code to drop frames */ if ( frame_counter++ % HRTGRAB_SLOWDOWN ) goto out; if ( frame_counter > HRTGRAB_SLOWDOWN ) frame_counter = 0; #endif for (; i < 480; i+=2) { /* select the proper raster line */ writew(i, ps_data.mem + 0x2002); /* copy the raster line */ memcpy_fromio(ps_data.stream_buf[set_counter].vaddress, ps_data.mem, 512); } if (old_field_id) { ++set_counter; set_counter = set_counter % ps_data.stream_buffers_requested; } wake_up_interruptible(&ps_data.grabq); out: init_timer(&cbuf_timer); cbuf_timer.function = capture_grab_frame; cbuf_timer.expires = jiffies + (HZ/120); add_timer(&cbuf_timer); spin_unlock(&ps_data.slock); } /*----------------------------------------------------------------------------*/ /* mapping the buffers for streaming */ /*----------------------------------------------------------------------------*/ static int mmap_request_buffers( struct ps_device* dev, struct v4l2_requestbuffers *req) { int i; u32 type; if (dev->stream_buffers_mapped) return 0; /* can't make requests if buffers are mapped */ if (req->count < 1) req->count = 1; if (req->count > MAX_CAPTURE_BUFFERS) req->count = MAX_CAPTURE_BUFFERS; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /* The buffer length needs to be a multiple of the page size */ buflen = ((height * width) + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); printk(KERN_CRIT "Granting %d buffers\n",req->count); /* Now initialize the buffer structures. Don't allocate the */ /* buffers until they're mapped. */ for (i = 0; i < req->count; ++i) { dev->stream_buf[i].requested = 1; dev->stream_buf[i].vidbuf.index = i; dev->stream_buf[i].vidbuf.type = type; /* offset must be unique for each buffer, and a multiple */ /* of PAGE_SIZE on 2.4.x */ dev->stream_buf[i].vidbuf.memory = V4L2_MEMORY_MMAP; dev->stream_buf[i].vidbuf.m.offset = PAGE_SIZE * (i+1); printk(KERN_CRIT "Granting %d \n",dev->stream_buf[i].vidbuf.m.offset); dev->stream_buf[i].vidbuf.length = buflen; dev->stream_buf[i].vidbuf.bytesused = 0; dev->stream_buf[i].vidbuf.flags = 0; dev->stream_buf[i].vidbuf.timestamp.tv_sec = 0; dev->stream_buf[i].vidbuf.sequence = 0; memset(&dev->stream_buf[i].vidbuf.timecode, 0, sizeof(struct v4l2_timecode)); } for (i = req->count; i < MAX_CAPTURE_BUFFERS; ++i) dev->stream_buf[i].requested = 0; dev->stream_buffers_requested = req->count; return 1; } /*----------------------------------------------------------------------------*/ static void mmap_unrequest_buffers(struct ps_device *dev) { int i; if (dev->stream_buffers_requested == 0 || dev->stream_buffers_mapped) return; for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) dev->stream_buf[i].requested = 0; dev->stream_buffers_requested = 0; } /*----------------------------------------------------------------------------*/ static int capture_streamon(struct ps_device* dev, __u32 type) { // struct stream_buffer *buf; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk(KERN_CRIT "STREAMON wrong buffer type\n"); return 0; } if (!dev->streaming) { dev->streaming = 1; capture_grab_frame( 5 ); return 1; } return 0; } static void capture_streamoff(struct ps_device* dev, __u32 type) { if (!dev->streaming) return; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk(KERN_CRIT "STREAMOFF wrong buffer type\n"); return; } del_timer_sync(&cbuf_timer); dev->streaming = 0; } /*----------------------------------------------------------------------------*/ static void mmap_vma_open(struct vm_area_struct *vma) { struct ps_device *dev = &ps_data; struct stream_buffer *buf; if (dev == NULL) return; buf = mmap_stream_buffer_from_offset(dev, vma->vm_pgoff); if (dev->stream_contig_map) buf = &dev->stream_buf[0]; ++buf->vma_refcount; //debug_msg("vma_open called\n"); //MOD_INC_USE_COUNT; } /*----------------------------------------------------------------------------*/ static void mmap_vma_close(struct vm_area_struct *vma) { int i, n = 1; struct ps_device *dev = &ps_data; struct stream_buffer *buf = mmap_stream_buffer_from_offset(dev, vma->vm_pgoff); for (i = 0; i < n; ++i) { if (dev->streaming) { printk(KERN_DEBUG"Warning- munmap() called while streaming\n"); capture_streamoff(dev, buf->vidbuf.type); } v4l2_q_yank_node(&dev->stream_q_capture, &buf->qnode); v4l2_q_yank_node(&dev->stream_q_done, &buf->qnode); if (buf->vaddress != NULL && i == 0) vfree(buf->vaddress); buf->vaddress = NULL; buf->vaddress = NULL; buf->vidbuf.flags = 0; if (dev->stream_buffers_mapped > 0) --dev->stream_buffers_mapped; } //MOD_DEC_USE_COUNT; } /*----------------------------------------------------------------------------*/ static struct page * mmap_vma_nopage(struct vm_area_struct *vma, unsigned long address, int write) { struct ps_device *dev = &ps_data; struct stream_buffer *buf; unsigned long offset_into_buffer; struct page* page; pgd_t *pgd; pmd_t *pmd; pte_t *pte; unsigned long lpage = 0 ; int n= 1; buf = mmap_stream_buffer_from_offset(dev, vma->vm_pgoff); if (buf == NULL) return 0; offset_into_buffer = address - vma->vm_start; if (offset_into_buffer >= buf->vidbuf.length * n) { printk(KERN_CRIT "Attempt to read past end of mmap() buffer\n"); return 0; } page = (struct page*)VMALLOC_VMADDR(buf->vaddress + offset_into_buffer); 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); if (page == 0) return 0; get_page(page); atomic_inc(&page->count); return page; } /*----------------------------------------------------------------------------*/ static struct vm_operations_struct capture_vma_operations = { open : mmap_vma_open, close : mmap_vma_close, nopage : mmap_vma_nopage, }; /*----------------------------------------------------------*/ /* MMAP */ /*----------------------------------------------------------*/ static int ps_mmap(struct file *file, struct vm_area_struct *vma) { struct ps_device* dev = &ps_data; struct stream_buffer* buf; int i, n = 1; buf = mmap_stream_buffer_from_offset(dev, vma->vm_pgoff); if (buf == NULL) { printk(KERN_DEBUG "mmap() Invalid offset parameter\n"); return -EINVAL;/* no such buffer */ } for (i = 0; i < n; ++i) { if (!buf->requested) { printk(KERN_CRIT "mmap() Buffer is not available for" " mapping\n"); return -EINVAL;/* not requested */ } if (buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED) { printk(KERN_CRIT "mmap() Buffer is already mapped\n"); return -EINVAL;/* already mapped */ } if (buf->vaddress != NULL) vfree(buf->vaddress); if (i == 0) buf->vaddress = vmalloc(buf->vidbuf.length * n); else buf->vaddress = buf[-1].vaddress + buf->vidbuf.length; if (buf->vaddress == NULL) { printk(KERN_CRIT "Could not allocate mmap() buffer\n"); return -ENODEV; } buf->vidbuf.flags |= V4L2_BUF_FLAG_MAPPED; ++dev->stream_buffers_mapped; ++buf; } vma->vm_ops = &capture_vma_operations; if (vma->vm_ops->open) vma->vm_ops->open(vma); return 0; } /*----------------------------------------------------------------------------*/ void popq() { if (cbuf.head + HRTFRAME_SIZE >= cbuf.buf + HRTFRAME_SIZE * QUEUE_SIZE) cbuf.head = cbuf.buf; else cbuf.head = cbuf.head + HRTFRAME_SIZE; } /*----------------------------------------------------------------------------*/ void pushq() { /* check to see if there is room */ if ( ( (cbuf.tail + HRTFRAME_SIZE >= cbuf.buf + (HRTFRAME_SIZE * QUEUE_SIZE)) && ( cbuf.buf == cbuf.head )) || ( cbuf.tail + HRTFRAME_SIZE == cbuf.head)) { /* If you're in here there is no room in the queue. */ popq(); /* now there is room :) */ } /* check to see if the tail is at the end of the buffer */ if (cbuf.tail + HRTFRAME_SIZE >= cbuf.buf + HRTFRAME_SIZE * QUEUE_SIZE) cbuf.tail = cbuf.buf; else cbuf.tail = cbuf.tail + HRTFRAME_SIZE; } /*----------------------------------------------------------------------------*/ int inQueue() { /* Return true if something is in the queue. */ if (cbuf.head == cbuf.tail) return 0; else return 1; } /*----------------------------------------------------------------------------*/ int get_frame(int startxp, int startyp, int h, int w, unsigned char *buf) { int i; for (i = startyp; i 512*480) count = 512*480; retval = count; if ( readb(ps_data.mem + 0x2000) & 0x40) /* Get in freez frame mode */ get_frame(0,0,480,512,(char *)framedata); else /* Get data in live mode */ get_frame2(0,0,480,512,framedata); if (retval > 0 && copy_to_user(buf, framedata, count)) { retval = -EFAULT; goto out; } *f_pos = 0; out: return retval; } /*----------------------------------------------------------*/ /* IOCTL */ static int ps_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { switch(cmd) { int size; /*----------------------------------------------------*/ /* Device Specific IOCTLS */ /*----------------------------------------------------*/ case HRTGIMG: { struct HRT_img_struct *ptr = arg; if ( ( ptr->startxp > 512 || ptr->startxp < 0 ) || ( ptr->startyp > 512 || ptr->startyp < 0 ) || ( ptr->endxp > 512 || ptr->endxp < 0 ) || ( ptr->endyp > 512 || ptr->endyp < 0 ) || ( ptr->startxp > ptr->endxp ) || ( ptr->startyp > ptr->endyp ) ) return -EINVAL; if ( readb(ps_data.mem + 0x2000) & 0x40) /* Get in freez frame mode */ get_frame(ptr->startxp,ptr->startyp,ptr->endyp,ptr->endxp,framedata); else get_frame2(ptr->startxp,ptr->startyp,ptr->endyp,ptr->endxp,framedata); size = (ptr->endxp - ptr->startxp) * (ptr->endyp - ptr->startyp); copy_to_user(ptr->frame, framedata, size); return 0; } case HRTGPIX: { struct HRT_pix_struct *data_pix = arg; /* check which mode the card is in */ if ( readb(ps_data.mem + 0x2000) & 0x40) { /* Get in freez frame mode */ writew(data_pix->y, ps_data.mem + 0x2002); data_pix->pix = readb(ps_data.mem + data_pix->x); } else { /* get in live frame mode */ /* first find out if the pix is on an evin or odd line */ while ((data_pix->y % 2) != (readb(ps_data.mem + 0x2000) & 0x01)); /* now get the pix and return */ writew(data_pix->y, ps_data.mem + 0x2002); data_pix->pix = readb(ps_data.mem + data_pix->x); } return 0; } case HRTLIVEVID: { writeb(0x91, ps_data.mem + 0x2000); /* LIVE VIDEO command */ return 0; } case HRTFREEZE: { writeb(0x99, ps_data.mem + 0x2000); /* FREEZE command */ return 0; } case HRTHUECTRL: { unsigned long HueVal = (unsigned long)arg; I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(0x07); I2C_send_byte(HueVal); I2C_stop(); return 0; } case HRTBRIGHTCTRL: { unsigned long BrightVal = (unsigned long) arg; I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(0x19); I2C_send_byte(BrightVal); I2C_stop(); return 0; } case HRTCONTRASTCTRL: { unsigned int ContrastVal = (unsigned long) arg; I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(0x13); I2C_send_byte(ContrastVal); I2C_stop(); return 0; } /*----------------------------------------------------*/ /* V4L2 Specific IOCTLS */ /*----------------------------------------------------*/ case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *ctrl = arg; if (queryctrl_brightness.id == ctrl->id) { *ctrl = queryctrl_brightness; return 0; } if (queryctrl_contrast.id == ctrl->id) { *ctrl = queryctrl_contrast; return 0; } return -EINVAL; } case VIDIOC_S_CTRL: { struct v4l2_control *vid_ctrl = arg; /* set brightness */ if (vid_ctrl->id == V4L2_CID_BRIGHTNESS) { /* check for proper range */ if ( vid_ctrl->value > 255 || vid_ctrl->value < 0 ) return -ERANGE; /* set the brightness in the device */ ps_data.bright = vid_ctrl->value; I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(0x19); I2C_send_byte(ps_data.bright); I2C_stop(); return 0; } /* set contrast */ if (vid_ctrl->id == V4L2_CID_CONTRAST) { if ( vid_ctrl->value > 255 || vid_ctrl->value < 0 ) return -ERANGE; /* Set the contrast in the device */ ps_data.contrast = vid_ctrl->value; I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(0x13); I2C_send_byte(ps_data.contrast); I2C_stop(); return 0; } return -EINVAL; } case VIDIOC_G_CTRL: { struct v4l2_control *vid_ctrl = arg; /* get brightness */ if (vid_ctrl->id == V4L2_CID_BRIGHTNESS) { vid_ctrl->value = ps_data.bright; return 0; } /* get contrast */ if (vid_ctrl->id == V4L2_CID_CONTRAST) { vid_ctrl->value = ps_data.contrast; return 0; } return -EINVAL; } case 0x80585600: case VIDIOC_QUERYCAP: { struct v4l2_capability *cap = (struct v4l2_capability *)arg; strcpy(cap->driver,"pixelsmart"); strcpy(cap->card,"PIXELSMART"); strncpy(cap->card,ps_data.video_dev.name,sizeof(cap->card)); sprintf(cap->bus_info,"PCI:%s",ps_data.dev->slot_name); cap->version = PS_VERSION_CODE; cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; return 0; } case VIDIOC_G_FMT: { struct v4l2_format *f = arg; memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format)); f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.width = 512; f->fmt.pix.height = 480; // f->fmt.pix.field = fh->cap.field; f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; f->fmt.pix.bytesperline = 512; f->fmt.pix.sizeimage = 512 * 480 ; // MAX_WIDTH * MAX_HEIGHT; return 0; } case VIDIOC_S_FMT: { struct v4l2_format *f = arg; memset(&f->fmt.pix,0,sizeof(struct v4l2_pix_format)); f->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; f->fmt.pix.width = 512; f->fmt.pix.height = 480; // f->fmt.pix.field = fh->cap.field; f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; f->fmt.pix.bytesperline = 512; f->fmt.pix.sizeimage = 512 * 480 ; // MAX_WIDTH * MAX_HEIGHT; return 0; } case VIDIOC_G_STD: { v4l2_std_id *id = arg; *id = V4L2_STD_NTSC; return 0; } case VIDIOC_S_STD: { v4l2_std_id *id = arg; *id = V4L2_STD_NTSC; return 0; } case VIDIOC_ENUMINPUT: { struct v4l2_input *i = arg; int n = 0; memset(i,0,sizeof(*i)); i->index = n; i->type = V4L2_INPUT_TYPE_CAMERA; return 0; } case VIDIOC_G_PARM: { struct v4l2_streamparm *sp = arg; if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) return -EINVAL; sp->parm.capture = ps_data.capture; return 0; } case VIDIOC_S_PARM: { struct v4l2_streamparm *sp = arg; struct v4l2_captureparm *vp = &sp->parm.capture; if (vp->capturemode & ps_data.capture.capability) return 0; } case VIDIOC_G_INPUT: { return 0; } case VIDIOC_S_INPUT: { int input = *(int *)arg; if (input < 0 ) { return -EINVAL; } ps_data.input = input; return 0; } case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *f = arg; enum v4l2_buf_type type; int index; type = f->type; if (V4L2_BUF_TYPE_VIDEO_CAPTURE == type) { index = f->index; if (0 != index) return -EINVAL; memset(f,0,sizeof(*f)); f->index = index; f->type = type; f->pixelformat = V4L2_PIX_FMT_GREY; strcpy(f->description,"video data"); return 0; } } case VIDIOC_ENUMSTD: { struct v4l2_standard *vs = arg; struct v4l2_fract fraction = { 1001, 30000 }; unsigned int index = vs->index; if (index > 0) return -EINVAL; vs->id = V4L2_STD_NTSC; vs->index = index; vs->frameperiod = fraction; vs->framelines = 512; return 0; } case VIDIOCGWIN: { struct video_window *win = arg; memset(win,0,sizeof(*win)); win->x = 0; win->y = 0; win->width = MAX_WIDTH; win->height = MAX_HEIGHT; return 0; } case VIDIOCSWIN: { struct v4l2_window *win2 = arg; win2->field = V4L2_FIELD_ANY; win2->w.left = 0; win2->w.top = 0; win2->w.width = width; win2->w.height = height; return 0; } case VIDIOCGFBUF: { struct video_buffer *v = arg; printk(KERN_CRIT "VIDIOCGFBUF\n"); v->base = (void*) ps_data.addr; v->height = height; v->width = width; v->depth = depth; v->bytesperline = 22; return 0; } case VIDIOCSFBUF: { struct video_buffer *v = arg; if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) return -EPERM; //pcam->addr = (ulong)v->base; height = v->height; width = v->width; depth = v->depth; return 0; } case VIDIOC_G_FBUF: { struct video_buffer *v = arg; printk(KERN_CRIT "VIDIOCGFBUF\n"); v->base = (void*) ps_data.addr; v->height = height; v->width = width; v->depth = depth; v->bytesperline = 22; return 0; } case VIDIOCGMBUF: { struct video_mbuf *v = arg; v->size = PCI_BYTES; v->frames = 1; return 0; } case VIDIOC_S_FBUF: { struct video_buffer *v = arg; if(!capable(CAP_SYS_ADMIN) && !capable(CAP_SYS_RAWIO)) return -EPERM; //pcam->addr = (ulong)v->base; height = v->height; width = v->width; depth = v->depth; return 0; } case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; if(!(req->type == V4L2_BUF_TYPE_VIDEO_CAPTURE)) return -EINVAL; printk(KERN_CRIT " requesting buffers if buffers are there\n"); if (ps_data.stream_buffers_mapped) { printk(KERN_DEBUG "Can't request buffers if buffers are already mapped\n"); return -EINVAL; } mmap_unrequest_buffers(&ps_data); capture_begin(&ps_data); if (!mmap_request_buffers(&ps_data, req)) return -EINVAL; return 0; } case VIDIOC_STREAMON: { __u32* type = arg; if (!capture_streamon(&ps_data, *type)) return -EINVAL; return 0; } case VIDIOC_STREAMOFF: { //__u32* type = arg; // capture_streamoff(&ps_data, *type); return 0; } case VIDIOC_QBUF: { struct v4l2_buffer *vidbuf = arg; struct ps_device *dev = &ps_data; int i= vidbuf->index; struct stream_buffer *buf= NULL; if (vidbuf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk("QBUF wrong type\n"); return -EINVAL; } if (i < 0 || i >= MAX_CAPTURE_BUFFERS || !dev->stream_buf[i].requested) { printk(KERN_CRIT "QBUF buffer index %d is out of range\n", i); return -EINVAL; } buf = &dev->stream_buf[i]; if (!(buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED)) { printk(KERN_CRIT "QBUF buffer %d is not mapped\n", i); return -EINVAL; } if ((buf->vidbuf.flags & V4L2_BUF_FLAG_QUEUED)) { printk(KERN_CRIT "QBUF buffer %d is already queued\n", i); return -EINVAL; } buf->vidbuf.flags &= ~V4L2_BUF_FLAG_DONE; v4l2_q_add_tail(&dev->stream_q_capture, &buf->qnode); buf->vidbuf.flags |= V4L2_BUF_FLAG_QUEUED; ps_data.status = FBUFFER_DONE; buf->vidbuf.flags = 0; buf->vidbuf.flags |= V4L2_BUF_FLAG_QUEUED; return 0; } case VIDIOC_DQBUF: { struct v4l2_buffer *buf = arg; struct stream_buffer *newbuf = NULL; if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk(KERN_CRIT "DQBUF wrong buffer type\n"); return -EINVAL; } buf->m.userptr = (unsigned long) ps_data.stream_buf[0].vaddress; buf->length = BUF_LEN; buf->flags = 0; buf->flags |= (V4L2_BUF_FLAG_MAPPED & V4L2_BUF_FLAG_DONE); newbuf = v4l2_q_del_head(&ps_data.stream_q_done); if (newbuf == NULL) { printk(KERN_CRIT "DQBUF nothing on done queue\n"); return -EINVAL; } newbuf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; *buf = newbuf->vidbuf; return 0; } case VIDIOC_QUERYBUF: { struct v4l2_buffer *buf = arg; int i; i = buf->index; if (i < 0 || i >= MAX_CAPTURE_BUFFERS || !ps_data.stream_buf[i].requested || (buf->type & V4L2_BUF_TYPE_VIDEO_CAPTURE) != (ps_data.stream_buf[i].vidbuf.type & V4L2_BUF_TYPE_VIDEO_CAPTURE)) { printk(KERN_CRIT "QUERYBUF bad parameter\n"); return -EINVAL; } *buf = ps_data.stream_buf[i].vidbuf; return 0; } /*----------------------------------------------------*/ case VIDIOCGCAP: { struct video_capability *b = arg; strcpy(b->name, "PIXELSMART"); b->type = VID_TYPE_CAPTURE | VID_TYPE_MONOCHROME; /* i dont have channels or audio support*/ b->channels = 1; b->audios = 0; b->maxwidth = 521; b->maxheight = 480; b->minwidth = 32; b->minheight = 32; return 0; } case VIDIOCMCAPTURE: { struct video_mmap *vm = arg; if (vm->frame<0 || vm->frame>1 || vm->width<32 || vm->width>512 || vm->height<32 || vm->height>512) return -EINVAL; return 0; } case VIDIOCGCHAN: { struct video_channel *v = arg; if(v->channel!=0) return -EINVAL; v->flags=0; v->tuners=0; /* Good question.. its composite or SVHS so.. */ v->type = VIDEO_TYPE_CAMERA; strcpy(v->name, "Frame Grabber"); return 0; } case VIDIOCSYNC: { //udelay(1); 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; p->colour=2; p->hue=0x00; p->brightness=0x9b; p->contrast=0x5e; p->whiteness=0xfe; 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; return 0; } case VIDIOCKEY: return 0; case VIDIOCCAPTURE: case VIDIOCSAUDIO: return -EINVAL; default: return -ENOIOCTLCMD; } return 0; } static int ps_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, ps_do_ioctl); } static int ps_ioctl2(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { return video_usercopy(inode, file, cmd, arg, ps_do_ioctl); } /*----------------------------------------------------------*/ /* The VMA open function */ /*----------------------------------------------------------*/ void ps_vma_open(struct vm_area_struct *vma) { MOD_INC_USE_COUNT; } /*----------------------------------------------------------*/ /* The VMA open function */ /*----------------------------------------------------------*/ void ps_vma_close(struct vm_area_struct *vma) { MOD_DEC_USE_COUNT; } /*----------------------------------------------------------*/ /* The VMA fops structure */ /*----------------------------------------------------------*/ static struct vm_operations_struct ps_mmap_vm_ops = { open : ps_vma_open, close: ps_vma_close, }; /*----------------------------------------------------------*/ /* MMAP */ /*----------------------------------------------------------*/ static int ps_mmap2(struct file *file, struct vm_area_struct *vma) { /* struct ps_device* ptv = file->private_data; */ unsigned long offset = VMA_OFFSET(vma); unsigned long phy_addr = PCI_BASE; unsigned long vsize = vma->vm_end - vma->vm_start; unsigned long psize = PCI_BYTES - offset; /* ceck to see if the page size is bigger than the virtual size */ if(vsize > psize) return -EINVAL; /* remap the entire page range */ if (remap_page_range(vma->vm_start, phy_addr, vsize, vma->vm_page_prot)) return -EAGAIN; vma->vm_ops = &ps_mmap_vm_ops; /* calling vma open */ ps_vma_open(vma); return 0; } /*-------------------------------------------------*/ /* POLL */ /*-------------------------------------------------*/ static unsigned int ps_poll(struct file *file, poll_table *wait) { unsigned int mask = 0; void* tnode = NULL; if (down_interruptible(&ps_data.semlock)) return -ERESTARTSYS; tnode = v4l2_q_peek_head(&ps_data.stream_q_capture); if(tnode != NULL){ printk(KERN_CRIT "CALLING----- POLL\n"); return POLLERR; } up(&ps_data.semlock); poll_wait(file, &waitq, wait); return mask; } static unsigned int ps_poll2(struct file *file, poll_table *wait) { struct video_device * vdev = video_devdata(file); struct ps_device* ptv = vdev->priv; unsigned int mask = 0; poll_wait(file, &ptv->grabq, wait); if (inQueue()){ printk(KERN_CRIT "CALLING POLL-----------\n"); mask |= (POLLIN | POLLRDNORM); } return mask; } /*-------------------------------------------------*/ /* declare the fops array and pray it works */ /*-------------------------------------------------*/ struct file_operations ps_fops = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = ps_ioctl, .llseek = NULL, .read = ps_read, .mmap = ps_mmap, .poll = ps_poll, }; /*-------------------------------------------------*/ /* declare the fops array for the second device */ /* That will use a differen memory mapping function*/ /*-------------------------------------------------*/ struct file_operations ps_fops2 = { .owner = THIS_MODULE, .open = video_exclusive_open, .release = video_exclusive_release, .ioctl = ps_ioctl2, .llseek = NULL, .read = ps_read, .mmap = ps_mmap2, .poll = ps_poll2, }; /*-------------------------------------------------*/ /* declare the template for video_device structure */ /*-------------------------------------------------*/ static struct video_device pstv_video_template = { .owner = THIS_MODULE, .name = "PIXELSMART", type: VID_TYPE_CAPTURE|VID_TYPE_OVERLAY, .hardware = VID_HARDWARE_BT848, .fops = &ps_fops, .minor = 0, }; /*-------------------------------------------------*/ /* declare the template for second device */ /*-------------------------------------------------*/ static struct video_device pstv_video_template2 = { .owner = THIS_MODULE, .name = "PIXELSMART", type: VID_TYPE_CAPTURE|VID_TYPE_OVERLAY, .hardware = VID_HARDWARE_BT848, .fops = &ps_fops2, .minor = 1, }; /*-------------------------------------------------*/ /* register video4linux devices */ /*-------------------------------------------------*/ static int ps_register_video(struct ps_device *pstv) { if(video_register_device(&pstv->video_dev,VFL_TYPE_GRABBER, video_nr)<0) return -1; if(video_register_device(&pstv->video_dev2,VFL_TYPE_GRABBER, video_nr2)<0) return -1; return 0; } /*-------------------------------------------------*/ /*Probe for te device, find its base address */ /*-------------------------------------------------*/ static int check_pixelsmart_base (unsigned long address) { unsigned char temp, temp1, temp2, temp3; /* Backup values before changing them */ temp = readb(address + 0x2000); temp1 = readb(address + 0x2002); temp2 = readb(address + 2); rmb(); /* Freez the device */ writeb(0x5B, address + 0x2000); /* FREEZE Immediate command */ /* Move the y value to line 1 and back up this value */ writeb(1, address + 0x2002); wmb(); /* Write to a register to compare later */ writeb(0x0f, address); wmb(); temp3 = readb(address); rmb(); /* Move the y value to line 2 */ writeb(2, address + 0x2002); wmb(); /* Write to a register to compare later */ writeb(0x00, address); wmb(); temp3 = readb(address); rmb(); /* Now move back to y val of 1 and see if it was changed */ writeb(1, address + 0x2002); wmb(); temp3 = readb(address); rmb(); if ( temp3 != 0x0f ) { /* This is not the device */ /* repair the two byte values */ writeb(temp, address + 0x2000); writeb(temp1, address + 0x2002); writeb(temp2, address); wmb(); return 0; } else return 1; } static unsigned long get_pixelsmart_base (struct pci_dev *dev) { unsigned long address = 0, ioMapAddr = 0; /* First check for all jumpers on */ /* get the base address if all jumpers are on */ address = pci_resource_start(dev,0); PCI_LEN = pci_resource_len(dev,0); /* request memory and remap region */ if (!request_mem_region(address, PCI_LEN, "PIXELSMART")) return -EBUSY; ioMapAddr = (unsigned long) ioremap_nocache(address, PCI_LEN); /* Now check if the device is there */ if (!check_pixelsmart_base(ioMapAddr)) { /* if your in here device not found at that base */ /* unmap and release region */ release_mem_region(address, PCI_LEN); iounmap((void*)ioMapAddr); PCI_LEN = PCI_BYTES; /* now try first base address */ address = PCI_BASE1; if (!request_mem_region(PCI_BASE1, PCI_LEN, "PIXELSMART")) return -EBUSY; ioMapAddr = (unsigned long) ioremap_nocache(PCI_BASE1, PCI_LEN); /* check to see if device is there */ if (!check_pixelsmart_base(ioMapAddr)) { /* your in here device is not here */ /* unmap and release region */ release_mem_region(PCI_BASE1, PCI_LEN); iounmap((void*)ioMapAddr); /* I know the device is here so if the others fail this is what you are looking for. So I'll return this */ address = PCI_BASE2; if (!request_mem_region(PCI_BASE2, PCI_LEN, "PIXELSMART")) return -EBUSY; ioMapAddr = (unsigned long) ioremap_nocache(PCI_BASE2, PCI_LEN); } } Actual_Base = address; return ioMapAddr; } /*-------------------------------------------------*/ /* init and cleanup modules to follow */ /*-------------------------------------------------*/ int init_module(void) { struct pci_dev *dev = NULL; u16 vendorid, deviceid; cbuf.buf =(long *) __get_free_pages(GFP_KERNEL, 9); framedata =(char *) __get_free_pages(GFP_KERNEL, 6); if( !cbuf.buf ) return -ENOMEM; if (!pcibios_present()){ return printk(KERN_CRIT "No PCI bios present\n"); return -ENODEV; } /* lets find the device first */ dev = pci_find_device(PCI_VENDOR_ID_PIXELSMART, PCI_DEVICE_ID_PIXELSMART, NULL); if(!dev){ printk(KERN_CRIT " NOT FOUND PIXELSMART\n"); } else{ vendorid = dev->vendor; deviceid = dev->device; } /* initialization of all the members of the struct */ /* assign the pci device to the glodal data structure ps_device */ ps_data.dev = dev; ps_data.swidth = 512; ps_data.sheight = 512; ps_data.sdepth = 8; ps_data.bright = 0x9b; ps_data.contrast = 0x5e; init_waitqueue_head(&ps_data.grabq); /* set defaults and initialize the capture data variables */ ps_data.ready_to_capture = 0; ps_data.grabber_enabled = 0; ps_data.capture_completed = 0; ps_data.time_acquired = 0;/* millisecond time stamp */ ps_data.streaming = 0; ps_data.capture_buffer_size = 0; ps_data.stream_buffers_requested = 0; ps_data.stream_buffers_mapped = 0; ps_data.stream_contig_map = 0; ps_data.stream_last_frame = 0; ps_data.stream_capture_buffer = NULL; spin_lock_init(&ps_data.slock); sema_init(&ps_data.semlock, 1); /* enable the device */ if (pci_enable_device(dev)) { printk(KERN_WARNING "PIXELSMART: Can't enable device.\n"); return -EIO; } /* Probe the device and get the base address */ ps_data.mem = get_pixelsmart_base(dev); if ( ps_data.mem == -EBUSY ) return -EIO; /* one time assignment for i2c function */ io_base = ps_data.mem; /* copy the video_device template for registering the device with v4l2 */ memcpy(&ps_data.video_dev, &pstv_video_template, sizeof(pstv_video_template)); memcpy(&ps_data.video_dev2, &pstv_video_template2, sizeof(pstv_video_template2)); ps_data.video_dev.minor = 0; ps_data.video_dev.priv = &ps_data; pci_set_master(dev); pci_set_drvdata(dev,&ps_data); /* register video4linux */ ps_register_video(&ps_data); /* initialize the i2c bus and load the a/d with defaults */ I2C_init(); /* Initalize the circular queue */ cbuf.head = cbuf.tail = cbuf.buf; memset(cbuf.buf, 0, HRTFRAME_SIZE*QUEUE_SIZE); writeb(0x91, ps_data.mem + 0x2000); /* LIVE VIDEO command */ return 0; } void cleanup_module(void) { /* make sure the kernel timer is shutdown */ //del_timer_sync(&cbuf_timer); video_unregister_device(&ps_data.video_dev); video_unregister_device(&ps_data.video_dev2); release_mem_region(PCI_BASE, PCI_LEN); iounmap((void*)ps_data.mem); free_pages((unsigned long)cbuf.buf, 9); free_pages((unsigned long)framedata, 6); return; } #include "helperfunctions.h" /* $Author: holtz $ * $Date: 2003/06/19 16:51:34 $ * $Revision: 1.44 $ * $Log: video.c,v $ * Revision 1.44 2003/06/19 16:51:34 holtz * All hell is breaking loose. * * Revision 1.43 2003/06/19 15:33:54 holtz * Still not working. * * Revision 1.42 2003/06/19 14:40:05 ash * buf_len variable added * * Revision 1.41 2003/06/19 14:04:38 holtz * working on streaming * * Revision 1.40 2003/06/19 13:00:27 ash * GETTING THEREEEEEEEEEEE ;-) * * Revision 1.39 2003/06/19 12:44:31 holtz * More bug cleaning up * * Revision 1.38 2003/06/19 12:41:23 ash * GETTING TRID OF ERRORS NOW FINALLY.... * CVS: ---------------------------------------------------------------------- * * Revision 1.37 2003/06/19 11:29:22 holtz * Syncronizing * * Revision 1.36 2003/06/19 11:27:55 ash * GOT INTO REQ_BUF * * Revision 1.35 2003/06/19 10:51:22 ash * CVS SCREWED UP BIG TIME :-( * CVS: ---------------------------------------------------------------------- * * Revision 1.34 2003/06/19 10:24:21 ash * BAD NOT WORKING STREAMING>> NEEDS SOME WORK * CVS:---------------------------------------------------------------- * * Revision 1.33 2003/06/19 10:13:07 holtz * I added a second device. device video0 no longer has mmap function. Ash is * working on it. dev video1 now has the simple direct mmap function. * I'm getting tired of working on this project. * * Revision 1.32 2003/06/19 06:30:29 holtz * the brightness and contrast ioctls work in xawtv now. So sweet. * * Revision 1.31 2003/06/19 03:11:31 holtz * fixed a few comments * * Revision 1.30 2003/06/19 03:09:39 holtz * I got rid of the errors enough to compile and run the movie in xawtv. * I tried bttv and that failed. crashed out x. :( * However I did implement brightness and contrast adjustments the * video for linux way. :) * They should work fine if we ever get bttv up to test it. * * Revision 1.29 2003/06/19 01:38:34 ash * ADDED THE MMAP FUNCTIONOS FOR STREAMING * ADDED THE IOCTLS FROR IT TOO * THIS NEEDS TO BE COMEPLETED THOUGH :-) * * Revision 1.28 2003/06/18 22:50:29 ash * ADDED MEMORY MAPPING FUNCTIONS * NOT USED ANYWHERE * * Revision 1.27 2003/06/18 22:41:10 holtz * I like the read now ;) * Buffers and all * IT is quite nice. * * Revision 1.26 2003/06/17 22:02:16 holtz * REad really works now ;) * I had to remove the buffers tho. * * Revision 1.25 2003/06/17 13:10:39 ash * THIS IS ALL IOCTLS I CAN THINK OF. I HOPE NOW I GET XAWTV.....:-).. * * Revision 1.24 2003/06/17 13:08:27 holtz * Read works. I also set the module to be much smaller. :) got it to 1/8 the size. * Me tired. go to bed now :) * * Revision 1.23 2003/06/17 11:08:07 ash * IOCTLS ARE ALL DONE I HOPE. ,......;-) * * Revision 1.22 2003/06/17 11:07:07 holtz * huge increase in speed of read. * * Revision 1.21 2003/06/17 09:11:27 ash * IOCTL IMPROVEMENTS;-) not a whole lot though * * Revision 1.20 2003/06/17 09:10:10 holtz * rewrote my read to be a bit faster. * * Revision 1.19 2003/06/17 08:44:33 holtz * Fixed all of the compileing errors cauzed by the ioctls * * Revision 1.18 2003/06/17 08:27:16 ash * REMOVED WARNINGS * * Revision 1.17 2003/06/17 08:14:41 ash * TYRING TO FIX IOCTLS * * Revision 1.16 2003/06/17 08:13:50 holtz * read function streams badly but it does stream. * * Revision 1.15 2003/06/17 07:07:00 ash * ADDED IOCTLS * * Revision 1.14 2003/06/17 07:05:01 holtz * timer kinda works` * * Revision 1.13 2003/06/17 03:47:08 ash * IOCTLS UPDATE * * Revision 1.12 2003/06/17 03:46:31 holtz * made some fixes to read and frame filler * * Revision 1.11 2003/06/17 03:14:02 holtz * just sync the code. I made some changes in the queue. * * Revision 1.10 2003/06/16 22:23:33 holtz * Queue canges were make * * Revision 1.9 2003/06/16 21:46:29 holtz * Removed coding errors so now the code will compile again. * Removed semaphore beacuse it was not initalized correctly, * and I dont currently see a need for a semiphore. * * Revision 1.8 2003/06/16 20:52:58 ash * BROKE CODE TOO * added semaphore locks to read. Added poll.select. fixed the read bug for atleast half the part. * added the ioctls for contrast, hue and brightness. * * left to do make sure of mutual exclusion at all places. * and fix the read function in the timer. * xawtv is IMPORTANT. IF NOT FIND ANOTHER APPLICATION TO TEST VIDEO. * * Revision 1.7 2003/06/16 15:42:45 ash * Working on brightness and contrast ioctl. fixed bug in timer * function. * * Revision 1.6 2003/06/15 12:01:05 holtz * added a kernel timer and implemented the cqueue. I'm segfaulting for some * reasion in the kernel timer. I dont know why. i have it all commented out * currently. so it will run and compile and dump a crap load of messages * to the screen. * * Revision 1.5 2003/06/15 10:05:57 holtz * added the push and pop for the queue, and added the queue he he * * Revision 1.4 2003/06/15 09:27:42 holtz * All is good. runs well and I have a fast read ;) * * Revision 1.3 2003/06/14 11:03:13 holtz * workin hard * * Revision 1.2 2003/06/14 10:09:31 holtz * I added two ioctl an HRTFREEZE and HRTLIVEVID :) * * Revision 1.1 2003/06/14 09:16:46 holtz * Moved the files around. I hope i did no harm * * Revision 1.17 2003/06/14 09:13:02 holtz * more clean up * * Revision 1.16 2003/06/14 08:10:29 holtz * CLeaned up the code * * * Revision 1.15 2003/06/14 08:06:13 holtz * Fixed the log messages and the probe works for the most part ;) * * Revision 1.7 2003/06/11 03:54:17 holtz * recovered the basic.[ch] files * * Revision 1.6 2003/06/11 03:23:25 holtz * shut off the i2c stuff * * Revision 1.5 2003/06/11 02:47:05 ash * NOthing much.. * * Revision 1.4 2003/06/11 02:42:16 holtz * Added log messages to files. * */