/* Filename: frgrab.c * */ #include "frgrab.h" struct frgrab *psmart; struct video_window win; struct semaphore sem; ulong capture_w = 0, capture_h = 0; unsigned char frgrab_polarity = 0x01; const unsigned char startup_data[] = { 4, 12, 3,12, 0,13,14,15, 11, 13, 15, 0, 0, 0, 0, 0, 15, 8, 15, 8, 6, 0, 5, 0, 0, 0, 8, 6, 1, 8, 9, 0, 0, 0, 2, 12, 7, 15, 5, 14, 4, 2, 1, 10, 15, 15, 13, 10, 15, 0, 9, 11, 7, 12, 0, 3, 13, 2, 4, 1, 8, 0, 4, 1, 8, 0, 4, 15, 15, 14, 0, 1, 12, 15, 0, 15, 8, 3, 0, 1, 8, 1, 0, 3, 6, 0, 7, 1, 0, 2, 8, 12, 0, 3 }; DECLARE_WAIT_QUEUE_HEAD(jiq_wait); struct tq_struct jiq_task; unsigned long flags,live_bit; int sentinel=0; int readfield,lastfield,bufferrow; unsigned char * HRT_base; unsigned char read_byte; /* I2C_start --------- Send 'start' command onto I2C bus to be recieved by all devices on bus. (The HRT512-8 only has the A/D on the I2C bus) This tells all devices on the I2C bus to prepare for an address phase, i.e., that one of them will be addressed next. */ void I2C_start() { I2C_00; I2C_01; I2C_11; I2C_10; I2C_00; } /* I2C_stop -------- Tell selected device (A/D) that transmission has completed and bus is free. */ void I2C_stop() { I2C_00; I2C_10; I2C_11; I2C_01; I2C_11; } /* I2C_send_byte ------------- Send a byte to the A/D. */ void I2C_send_byte(unsigned char data) { unsigned char bitpos; unsigned char sda, sda_slc; /* ...need code to determine timeout value... */ for (bitpos = 8; bitpos > 0; bitpos--) { /* send a bit to the A/D device; clock must be low at this time */ sda = (data & (1<<(bitpos-1))) >> (bitpos-1); /* get the next bit from data value */ sda_slc = sda << 1; I2C_POKE(sda_slc); /* put data bit on bus and take clock low (b0=0) */ I2C_POKE(sda_slc + 4); /* send the go signal to initiate clock pulse */ while (I2C_BUSY) { /* spin loc */ /* ...need code here to time out and recover if there is no response... */ }; I2C_01; /* leave the sda line at high impedance (bit0=0 clock, bit1=1 data/high impedance) */ }; /* check for ack from A/D after each byte has been sent */ I2C_11; if (I2C_PEEK & 2) { /* error: no ACK after Data Byte transmision */ /* ...need recovery code here... */ } I2C_01; /* return the bus to high impediance */ } void I2C_init() { const unsigned char* next_digit; unsigned char hihex,lohex; int i; I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); /* Tell the A/D device it has been selected for data transmission. */ I2C_send_byte(0); /* Set the selected devices (A/D) internal address register pointer to zero. All data writes will autoincrement the A/D's address register pointer. */ next_digit=startup_data; /* for (i = 0; i < sizeof(startup_regs_0019); i++) { I2C_send_byte(startup_regs_0019[i]); }*/ for(i=0;i<26;i++){ hihex=*(next_digit++); lohex=*(next_digit++); I2C_send_byte(hihex*16+lohex); } /* The first 26 data bytes have been sent. The next and final 21 bytes must be sent sequentially starting at address 0x20. The A/D has autoincremented its pointer to decimal 26 (0x1A). Now we need to skip up to adress decimal 32 (0x20), so we send a stop, start, address phase, and then the rest of the data. */ I2C_stop(); I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); /* send address byte to select A/D, and check for ack */ I2C_send_byte(32); /* set address pointer to 0x20 */ for(i=32;i<53;i++){ hihex=*(next_digit++); lohex=*(next_digit++); I2C_send_byte(hihex*16+lohex); } /* for (i = 0; i < sizeof(startup_regs_2034); i++) { I2C_send_byte(startup_regs_2034[i]); }*/ I2C_stop(); } void read_frame(void * ptr) { save_flags(flags); cli(); struct frgrab *psvideo = psmart; live_bit = readb(psvideo->frgrab_mem + FRGRAB_CONTROL_REG) & FRGRAB_EXTRIGSTATUS_MASK; rmb(); if (sentinel==0) {/**first time**/ bufferrow=0; sentinel++; } /***read the whole frame at one time***/ for (bufferrow=0;bufferrow<512;bufferrow++) { /***set which row to read***/ writew(bufferrow & 0x1ff, psvideo->frgrab_mem + FRGRAB_Y_LOW_REG); wmb(); /***get one line from device memory to kernel memory***/ memcpy_toio(&(psvideo->buffer[bufferrow* 512]),psvideo->frgrab_mem,512); } if(bufferrow >=512 ) { sentinel=0; wake_up_interruptible(&jiq_wait); } restore_flags(flags); } static int frgrab_open(struct inode *inode, struct file *file) { MOD_INC_USE_COUNT; return 0; } static int frgrab_close(struct inode *inode, struct file *file) { MOD_DEC_USE_COUNT; return 0; } static int frgrab_read(struct file *file, char *buf, size_t count, loff_t *ppos) { struct frgrab *psvideo = psmart; writeb(frgrab_polarity, psvideo->frgrab_mem+FRGRAB_POLARITY_CONTROL_REG); wmb(); writeb(0x0091, psvideo->frgrab_mem+0x2000); wmb(); /***using tq_timer**/ queue_task(&jiq_task, &tq_timer); interruptible_sleep_on(&jiq_wait); if(signal_pending(current)) return -ERESTARTSYS; if(copy_to_user(buf, (char *)psvideo->buffer, psvideo->buf_size)) return -EFAULT; return (512*512); } /* Does the mapping from the device address to the VMA region */ int frgrab_mmap(struct file *filp, struct vm_area_struct *vma) { //unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; struct frgrab *psvideo = psmart; vma->vm_flags |= VM_IO | VM_RESERVED; /* Remaps the page range from the physical to the virtual address */ if (remap_page_range(vma->vm_start, psvideo->frgrab_addr, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; } loff_t no_llseek(struct file *file, loff_t offset, int origin) { return 0; } static int frgrab_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct video_capability v; unsigned int value; switch(cmd) { case VIDIOCGCAP: /* Sets the video parameters */ { v.type = VID_TYPE_CAPTURE | VID_TYPE_CHROMAKEY | VID_TYPE_SCALES | VID_TYPE_MONOCHROME; v.channels = 1; v.audios = 0; v.maxwidth = 512; v.minwidth = 16; v.minheight = 16; v.maxheight = 512; strcpy(v.name, "Our Camera"); if(copy_to_user((void*)arg, &v, sizeof(v))) return -EFAULT; return 0; } case VIDIOCGWIN: /* Sets the width and height of the frame */ { win.width=256; win.height=256; win.x=128; win.y=128; if(copy_to_user((char *)arg, &win, sizeof(win))) return -EFAULT; return 0; } case VIDIOCGPICT: /* Sets the parameters for better/worse viewing */ { struct video_picture v; v.brightness = 100; v.hue = 32768; v.colour = 32768; v.contrast = 32768; v.whiteness = 32768; v.depth = 8; v.palette = VIDEO_PALETTE_GREY; if(copy_to_user((char *)arg, &v, sizeof(v))) { printk ("\nerror here\n"); return -EFAULT; } return 0; } case SETBRIGHTNESS: /* Sets only the brightness */ { __GET_USER(value,(int *)arg); I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(25); I2C_send_byte(value); I2C_stop(); return 0; } case GETBRIGHTNESS: /* Gets the brightness value */ { value = startup_data[25]; __PUT_USER(value, (unsigned int *)arg); return 0; } case SETCONTRAST: /* Sets the contrast value */ { __GET_USER(value,(int *)arg); I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(19); I2C_send_byte(value); I2C_stop(); return 0; } case GETCONTRAST: /* Gets the contrast value */ { value = startup_data[19]; __PUT_USER(value, (unsigned int *)arg); return 0; } case SETHUE: /* Sets the contrast value */ { __GET_USER(value,(int *)arg); I2C_start(); I2C_send_byte(HRT_AD_DEVICE_ID); I2C_send_byte(7); I2C_send_byte(value); I2C_stop(); return 0; } case GETHUE: /* Gets the contrast value */ { value = startup_data[7]; __PUT_USER(value, (unsigned int *)arg); return 0; } case SETFREEZE: { writeb(0x0099, psmart->frgrab_mem+0x2000); return 0; } case SETLIVE: { writeb(0x0091, psmart->frgrab_mem+0x2000); return 0; } /*the commented ioctls work on installing a non conflicting videodevX*/ /* case VIDIOC_QUERYCAP: { struct v4l2_capability *b = (struct v4l2_capability *)arg; b->type = V4L2_TYPE_CAPTURE; b->flags = V4L2_FLAG_READ | V4L2_FLAG_STREAMING | V4L2_FLAG_SELECT; b->outputs = 0; b->type = 0; b->audios = 0; b->maxwidth = 640; b->minwidth = 16; b->minheight = 16; b->maxheight = 480; b->maxframerate = 30; if(copy_to_user((char *)arg, &b, sizeof(b))) return -EFAULT; return 0; } case VIDIOC_G_AUDIO: { return 0; } case VIDIOC_S_AUDIO: { return 0; } case VIDIOC_G_FBUF: { struct v4l2_buffer *buffer = (char *)arg; if(copy_to_user((char *)arg, &buffer, sizeof(buffer))) return -EFAULT; return 0; } case VIDIOC_S_FBUF: { struct v4l2_buffer *buffer = (char *)arg; if(copy_to_user((char *)arg, &buffer, sizeof(buffer))) return -EFAULT; return 0; } */ default: return -ENOIOCTLCMD; } //end of switch return -1; } /* The basic file operations structure */ static struct file_operations frgrab_fops = { .owner = THIS_MODULE, .open = frgrab_open, .release = frgrab_close, .llseek = no_llseek, .read = frgrab_read, .ioctl = frgrab_ioctl, .mmap = frgrab_mmap, }; /* The basic file operations structure */ struct video_device frgrab_template = { .owner = THIS_MODULE, .name = "frgrab", .type = VID_TYPE_CAPTURE | VID_TYPE_OVERLAY, .hardware = VID_HARDWARE_BT848, .minor = 0, .fops = &frgrab_fops, }; static void frgrab_cleanup_module(void) { struct frgrab *psvideo; if( (psvideo = psmart)){ /* First unmap the region */ iounmap((void *)((unsigned long)(psvideo->frgrab_mem))); /* Now release the memory region and buffer */ release_mem_region((unsigned long)psvideo->frgrab_addr, FRGRAB_MEM_ADDRESS_SPACE); if(psvideo->buffer){ free_page((unsigned long)psvideo->buffer); } } /* Finally unregister */ video_unregister_device(&psvideo->video_dev); } /* The basic function that probes for the device and then registers it */ static int frgrab_init_module(void) { struct pci_dev *dev = NULL; struct frgrab *psvideo = NULL; int i,order,row,detected=0; unsigned long c_reg,y_low,y_high; unsigned char wval1,wval2,oldx1,oldx2,rval1,rval2; unsigned long base_address[2]={0xDC000,0xD4000}; /* Check PCI compliance */ if(!pci_present()) return -ENODEV; /* Probes to find the device */ dev = pci_find_device(FRGRAB_VENDOR_ID, FRGRAB_DEVICE_ID, dev); if(!dev) { printk("Device not found!!\n"); return -ENODEV; } pci_enable_device(dev); /* Allocates memory for the video device */ psvideo = kmalloc(sizeof(struct frgrab), GFP_KERNEL); if(!psvideo) { printk("Memory allocation error!!\n"); return -ENOMEM; } psvideo->dev = dev; i=0; /*loop to detect correct base address*/ do{ if(i<2){/*jumper A off*/ psvideo->frgrab_addr = base_address[i]; } else{/*jumper A on*/ psvideo->frgrab_addr= pci_resource_start(dev,0); } if(! request_mem_region(psvideo->frgrab_addr, FRGRAB_MEM_ADDRESS_SPACE, "frgrab")){ if(i<2){ i++; continue;/*probe for remaining addresses*/ } else return -EBUSY; } psvideo->frgrab_mem = ioremap(psvideo->frgrab_addr, FRGRAB_MEM_ADDRESS_SPACE); HRT_base = psvideo->frgrab_mem; I2C_init(); psvideo->video_dev = frgrab_template; psmart = psvideo; if(video_register_device(&psvideo->video_dev, VFL_TYPE_GRABBER, 0) < 0) { printk(KERN_INFO"Registartion of video device failed\n"); iounmap((void *)((unsigned long)(psvideo->frgrab_mem))); release_mem_region((unsigned long)psvideo->frgrab_addr, FRGRAB_MEM_ADDRESS_SPACE); return -1; } writeb(FRGRAB_FREEZE_IMM_CMD, psvideo->frgrab_mem+FRGRAB_CONTROL_REG);wmb();/*first freeze before doing the "probe-test" writing/reading so that values don't change between read/writes*/ c_reg=readb(psvideo->frgrab_mem+FRGRAB_CONTROL_REG);rmb();//save old value at this location y_low=readb(psvideo->frgrab_mem+FRGRAB_Y_LOW_REG);rmb();//save old value y_high=readb(psvideo->frgrab_mem+FRGRAB_Y_HIGH_REG);rmb();//save old value row=1; writeb(row, psvideo->frgrab_mem + FRGRAB_Y_LOW_REG);wmb();//now row 1(arbitarily chosen) is put into cpu's memory writeb(0, psvideo->frgrab_mem + FRGRAB_Y_HIGH_REG);wmb(); wval1=1; oldx1=readb(psvideo->frgrab_mem +row*512+1);rmb();//save old value writeb(wval1,psvideo->frgrab_mem +row*512+1);wmb();//write value '1' in this address rval1=readb(psvideo->frgrab_mem +row*512+1);rmb();//read it back writeb(oldx1,psvideo->frgrab_mem +row*512+1);wmb();//restore old value row=5; writeb(row, psvideo->frgrab_mem + FRGRAB_Y_LOW_REG);wmb();//now row 1(arbitarily chosen) is put into cpu's memory writeb(0, psvideo->frgrab_mem + FRGRAB_Y_HIGH_REG);wmb(); wval2=2; oldx2=readb(psvideo->frgrab_mem +row*512+1);rmb();//save old value writeb(wval2,psvideo->frgrab_mem +row*512+1);wmb();//write value '2' in this address rval2=readb(psvideo->frgrab_mem +row*512+1);rmb();//read it back writeb(oldx2,psvideo->frgrab_mem +row*512+1);wmb();//restore old value if( (wval1==rval1)&&(wval2==rval2))//If written values match with read values { detected=1; //write old values back writeb(c_reg, psvideo->frgrab_mem+FRGRAB_CONTROL_REG);wmb(); writeb(y_low,psvideo->frgrab_mem+FRGRAB_Y_LOW_REG);wmb(); writeb(y_high,psvideo->frgrab_mem+FRGRAB_Y_HIGH_REG);wmb(); } else{ psvideo->buffer=NULL; frgrab_cleanup_module();//release allocated resources if(i==2) { printk(KERN_INFO"BASE ADDRESS NOT DETECTED\n"); return -ENODEV; } } i++; }while(!detected); order = (18 - PAGE_SHIFT > 0)? 18-PAGE_SHIFT:0;//video memory is 2^18=256K if( !(psvideo->buffer = (char *)__get_free_pages(GFP_KERNEL, order))){ printk(KERN_INFO"Memory allocation error!\n"); return -ENOMEM; }; psvideo->buf_size = 512 * 512;//256K /* Initialize the task timer */ jiq_task.routine=read_frame; jiq_task.data=NULL; /***initialize the semaphore***/ sema_init(&sem, 1); return 0; } module_init(frgrab_init_module); module_exit(frgrab_cleanup_module);