/* * hrt.c * * last updated - 18 June 2007 * * Jie Chen/ Ke Chen/ Sai This is a device driver for the PixelSmart512-8 (greyscale) and Video Gala (color) framegrabbers. For more information on these devices, see http://www.pixelsmart.com. This driver is produced as a course project for the 'linux kernel programming' course at the Florida State University, as a term project submitted by the team comprising of Jie Chen, Ke Chen, Sai, in the summer of 2007. This driver carries forward the work done by other teams in the last 3 years by Atulya Mahajan, Sean Toh,Brett W. Thompson, Gilberto Morejon, Veena Adityan, Arthi Gokarn and Alex Rudnick, Ryan Walega And Daniel Beech, under the guidance of Dr Ted Baker. This driver supports the following features 1. Completely Support for the greyscale as well as color frame grabber cards 2. Completely Support for interrupt driven I/O 3. Completely Support for streaming I/O from color/grey scale devices 4. Automatic detection of the type of device present 5. Completely ported to 2.6.21.1 Linux Kernel Note: This was tested and ran under the Fedora distro. This code was purposly left 'dirty' with printk's to help others who wish to pursue this understand program flow. */ #ifndef __KERNEL__ #define __KERNEL__ #endif /* #include tpb */ #include /* udelay */ #include /* error codes */ #include #include #include #include #include #include #include #include #include #include #include #include #include /* #include tpb */ #include #include #include #include #include #include /* After 2.6.18, to use the v4l2, we should include this head file */ #include #include "hrt.h" #ifndef HAVE_V4L2 # error Sorry, this module requires v4l2. You should patch your kernel with v4l2 or you could download the non-v4l2 dependent module hrtmem.c. #endif /*#define HRT_DEBUG*/ #define HRT_ERROR_MSG(args...) do{printk("<1>hrt: "); printk(args);\ printk("\n");}while(0) /********************* * Module Parameters * *********************/ #ifndef module_param #define hrt_parm(name, pstr, type, perm) MODULE_PARM(name, pstr) #else #define hrt_parm(name, pstr, type, perm) module_param(name, type, perm) #endif int testing_isa_mode = 1; int cleanup_context = 0; static int major_number = 82; hrt_parm(major_number, "i", int, 0); #ifndef HRT_MAX_DEVICES #define HRT_MAX_DEVICES 4 #endif #ifndef HRT_IO_SIZE #define HRT_IO_SIZE 0x4000 #endif /********************************************************************/ /** * hrt_ctls - controls the user can set via V4L ioctl's */ static const struct v4l2_queryctrl hrt_ctls[] = { { .id = V4L2_CID_BRIGHTNESS, .name = "Brightness", .minimum = 0, .maximum = 255, .step = 1, .default_value = 0x9b, // (decimal: 155) .type = V4L2_CTRL_TYPE_INTEGER, }, { .id = V4L2_CID_CONTRAST, .name = "Contrast", .minimum = 0, .maximum = 255, .step = 1, .default_value = 0x5e, // (decimal: 94) .type = V4L2_CTRL_TYPE_INTEGER, } }; /********************************************************************/ /** * hrt_addresses - a list of possible jumper-selected addresses * jumper A on = plug and play address (above one MB address) * jumper A off = hardwired to 0xdc000 or 0xd4000 * jumper B on = address 0xd4000 (ignored if jumper A is on) * jumper B off = address 0xdc000 (ignored if jumper A is on) */ const unsigned long hrt_addresses[] = { 0xd4000, 0xdc000 }; /*************************** * Card-specific constants * ***************************/ #define HRT_VENDOR_ID 0x0004 #define HRT_DEVICE_ID_GREY 0x0404 #define HRT_DEVICE_ID_COLOR 0x0408 /* Number of registers on the board- checked in i2c_init() */ #define HRT_SAA7110_MAXREG 0x34 /********************************************************************/ /** * struct stream_buffer - represents a single buffer for streaming */ struct stream_buffer { struct v4l2_buffer vidbuf; /* For the V4L ioctl's */ int fields_grabbed; /* Two-bit pair of flags indicating which fields have been grabbed (i.e., 00 = no fields, 11 = both) */ struct list_head node; /* Connection into queues */ int requested; /* Set by REQBUF ioctl */ unsigned char *vaddress; /* This is the pointer to the vmalloc()'d memory into which the actual frame goes */ int vma_refcount; /* Reference count */ }; /********************************************************************/ /** * struct stream_buffer_queue - FIFO of stream_buffer structs */ struct stream_buffer_queue { struct stream_buffer *streambuf; /* Pointer to node data */ struct list_head head; /* Start of the queue */ rwlock_t lock; /* Lock to protect the queue */ int length; /* How many nodes are on the queue */ }; /********************************************************************/ /** * HRT device descriptor */ typedef struct { /* Added By Ryan Walega & Daniel Beech */ int registered; int num; int state; /* minor device number, index in hrt_devices[] */ unsigned long virt_addr; unsigned long phys_addr; /* cannot be opened again until released */ /* spinlock and is_locked control access to the device */ spinlock_t spinlock; int is_locked; wait_queue_head_t wait_queue; struct timer_list timer; struct tasklet_struct tasklet; int timer_active; int tasklet_active; /* timer_active != 0 iff timer or irq is active */ int irq; /* irq != 0 if board supports interrupts = -irq if handler not installed = irq if handler is installed */ volatile int irq_count; volatile int i2c_bits; /* last values written to i2c */ char saa7110_registers[HRT_SAA7110_MAXREG+1]; /* video data format, and dependent values */ int mode; int rows, cols, bytes_per_pixel; int win_row, win_col, win_width, win_height; /* current field being digitized */ int field; /* how many field changes we are waiting for */ int field_changes; /* the device is either frozen or being frozen */ int is_frozen; /* Local buffer for storing the frame currently read from the device */ /* Added june 8,2005 - atulya/sean */ unsigned char *framedata; /* Variable to keep track of the field being read inside the current frame */ unsigned int field_id; /* Semaphore for the device structure */ struct semaphore sem; /* Local buffer used for manipulation of the data returned * by the color device. * ONLY used by the color device */ unsigned char *localbuf; /* Flag for streaming mode */ int streaming; /* Number of buffers for streaming */ int numbufs; /* Array of buffers */ struct stream_buffer *streambufs; /* List of queued buffers */ struct stream_buffer_queue capture_list; /* List of filled buffers */ struct stream_buffer_queue done_list; int stream_buffers_mapped; struct stream_buffer stream_buf[MAX_CAPTURE_BUFFERS]; int stream_buffers_requested; /* Variable used to keep track of our state while streaming * added 13 June 2005 - Atulya/Sean */ int streaming_state; /* We allow the user to make the view upside down too ! * came out of compulsion actually since we have the cameras installed * upside down ! * the following variable determines what sort of output we return to the user * set using an ioctl */ int upside_down; /* Following structure used for registering with V4L */ struct video_device video_dev; struct v4l2_format clientfmt; struct v4l2_captureparm capture; /* Bytes per raster line. 640 for grey scale card * and 2*640 = 1280 for the color card */ unsigned int bytesperline; /* Memory size of the entire frame * = 512 X 480 X 1 bytes per pixel= 240 KB for grey scale card * = 640 X 480 X 2 bytes per pixel= 600 KB for color card */ unsigned int framesize; /* Current region of interest * fields = width,height,starty,startx */ struct subwindow *win; #ifdef CONFIG_PCI /* structure to represent a pci device */ struct pci_dev *pci_dev; #endif } hrt_t; /******************************************************************** *************** basic file operations (fops) ******************** */ int hrt_open (struct inode *inode, struct file *file); int hrt_release(struct inode *inode, struct file *file); int hrt_read (struct file *file, char *buf, size_t count, loff_t * ppos); int hrt_ioctl (struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg); int hrt_mmap (struct file *file, struct vm_area_struct *vma); unsigned int hrt_poll (struct file *file, poll_table *wait); static int hrt_do_private_ioctl(hrt_t *hrtdev, unsigned int cmd, unsigned long arg); /********************************************************************/ static struct file_operations hrt_fops = { .owner = THIS_MODULE, .open = hrt_open, .release = hrt_release, .read = hrt_read, .ioctl = hrt_ioctl, .mmap = hrt_mmap, .llseek = no_llseek, .poll = hrt_poll, }; /********************************************************************/ /* XXX: Get an actual official number from kraxel@bytesex.org (see /usr/src/linux/include/linux/videodev.h) */ #define VID_HARDWARE_HRT 36 /* AddED BY RYAN WALEGA */ //void grabber_release(void); static void grabber_release(struct video_device *vfd){ } /********************************************************************/ static struct video_device videodev_template = { .owner = THIS_MODULE, .name = "HRT Pixelsmart (PS512-8-PCI)", .type = HRT_VID_TYPE, .hardware = VID_HARDWARE_HRT, .fops = &hrt_fops, .release = grabber_release, }; /********************************************************************/ /** * struct hrt_per_file - contains data to be stored per file handle * * Private data for a device is not just the hrt_t structure * but also the subwindow structure that represents the subwindow * of the entire frame being displayed */ struct hrt_per_file { struct subwindow win; hrt_t *hrtdev; }; /********************************************************************/ /* Minor number, or -1 for first free */ static int video_minor = -1; /* Don't put V4L2_CAP_STREAMING in capabilities; recommended for xawtv */ static unsigned int disable_streaming = 0; module_param(video_minor, int, 0); module_param(disable_streaming, int, 0); MODULE_AUTHOR("Brett W. Thompson, Gilberto Morejon, Veena Adityan, Arthi Gokarn, Alex Rudnick, Sean Toh, Atulya Mahajan"); MODULE_DESCRIPTION("Driver for HRT Pixelsmart 512-8-PCI"); MODULE_LICENSE("GPL"); /********************************************************************/ /** * dprintk, hrt_printk - thin wrappers around printk() */ #define dprintk(fmt, arg...) { printk("hrt: " fmt, ## arg); } #define hrt_printk(fmt, arg...) { printk(KERN_ALERT "hrt: " fmt, ## arg); } /********************************************************************/ /* values for field hrt_t->state */ #define HRT_UNINITIALIZED_STATE 1 #define HRT_INITIALIZING_STATE 2 #define HRT_CLOSED_STATE 4 #define HRT_OPEN_STATE 8 #define HRT_FINALIZING_STATE 16 /* values for field hrt_t->mode */ #define HRT_DUAL_PORTED_MODE 1 #define HRT_COLOR_MODE 2 #define HRT_STREAMING_MODE 4 #define HRT_IRQ_MODE 8 #define HRT_ISA_MODE 16 /* values for global hrt_module_state */ #define HRT_MODULE_UNINITIALIZED_STATE 1 #define HRT_MODULE_INITIALIZING_STATE 2 #define HRT_MODULE_INITIALIZED_STATE 4 #define HRT_MODULE_FINALIZING_STATE 8 /********************************************************************/ int hrt_module_state = HRT_MODULE_UNINITIALIZED_STATE; int hrt_nonpci_devices = 0; /* number of non-pci devices detected */ /* non-pci devices have lower numbers */ int hrt_num_devices = 0; /* total number of devices detected */ #define HRT_IS_PCI(X) ((X) >= hrt_nonpci_devices) /* ??? consider dynamically allocating the device objects as they are probed and detected, and using a linked list here instead of an array */ hrt_t hrt_devices[HRT_MAX_DEVICES]; /* Flag to change semantics of mmap() upon the IOC_HRT_NEXT_MMAP_IS_DIRECT ioctl */ static unsigned int next_mmap_is_direct = 0; struct hrt_per_file *per_file_init( hrt_t *hrtdev); /* Veena and Arthi's streaming functions */ int mmap_request_buffers(hrt_t *dev, struct v4l2_requestbuffers *req); int hrt_streamon(hrt_t *hrtdev, __u32 type); void hrt_streamoff(hrt_t *hrtdev, __u32 type); int hrt_dequeuebuffer(hrt_t *hrtdev, struct v4l2_buffer *buf); int hrt_queuebuffer(hrt_t *hrtdev, struct v4l2_buffer *buf); #ifdef HRT_DEBUG /****************************** * optional debugging support * ******************************/ #define HRT_DEBUG_LEVEL 3 #define HRT_CHECK(dev,states,msg) \ {if (dev->state & ~(states))\ {HRT_DEBUG_MSG(1, "* unexpected state 0x%2x (%s)", dev->state, msg);}} #define HRT_MODULE_CHECK(states,msg) \ {if (hrt_module_state & ~(states))\ {HRT_DEBUG_MSG(1, "* unexpected state 0x%2x (%s)", hrt_module_state, msg);}} #define HRT_DEBUG_MSG(level,args...) {if (level <= HRT_DEBUG_LEVEL)\ {printk("<1>hrt * "); printk(args); printk("\n");}} struct proc_dir_entry *hrt_proc_read_entry = NULL; #define write_buf(args...) \ do { \ n = snprintf(buf, count, args); \ buf += n; \ count -= n; \ } while(0) /********************************************************************/ int hrt_read_proc(char *buf, char **start, off_t offset, int count, int *eof, void *data) { int n, i; int ips, tps, rps; char *org_buf = buf; ips = 0; tps = 0; rps = 0; /* dont excede count bytes when writing to buf */ /* just write to buf as a normal ptr to a file */ write_buf("dev HZ/10 int timer read int/s timer/s read/s\n"); for (i=0; iis_frozen = 1;\ iowrite8(HRT_FREEZE_NEXT_CMD, (void *) (dev->virt_addr + HRT_CONTROL_REG)) #define hrt_freeze_immediate(dev) \ dev->is_frozen = 1;\ iowrite8(HRT_FREEZE_IMM_CMD, (void *) (dev->virt_addr + HRT_CONTROL_REG)) #define hrt_go_live(dev) \ dev->is_frozen = 0;\ iowrite8(HRT_LIVE_CMD, (void *) (dev->virt_addr + HRT_CONTROL_REG)) #define hrt_get_field(dev) \ ioread8((void *) (dev->virt_addr + HRT_CONTROL_REG)) & 0x1 #define hrt_is_live(dev) \ ioread8((void *) (dev->virt_addr + HRT_CONTROL_REG)) & 0x1 #define hrt_i2c_delay() udelay(5) /* Unique I2C bus address of the SAA7110 (A/D) device */ #define HRT_AD_DEVICE_ID (128+16+8+4) /********************************************************************/ const unsigned char saa7110_default_init_regs[] = { 94, /* there are 94 bytes that follow */ 0x00, 0x4c, /* increment delay (IDEL) */ 0x01, 0x3c, /* HSY begin 50 Hz */ 0x02, 0x0d, /* HSY stop 50 Hz */ 0x03, 0xef, /* HCL begin 50 Hz */ 0x04, 0xbd, /* HCL stop 50 Hz */ 0x05, 0xf0, /* HSY after PHI1 50 Hz */ 0x06, 0x00, /* luminance control */ 0x07, 0x00, /* hue control */ 0x08, 0xf8, /* colour killer threshold QUAM (PAL/NTSC) */ 0x09, 0xf8, /* colour killer threshold SECAM */ 0x0A, 0x60, /* PAL switch sensitivity */ 0x0B, 0x50, /* SECAM switch sensitivity */ 0x0C, 0x00, /* gain control chrominance */ 0x0D, 0x86, /* standard/mode control */ /* 7 VTRC = 1 (VCR mode, not TV) 6 XXX 5 XXX 4 XXX 3 RTSE = 0 (PLIN switched to output) 2 HRMV = 1 (HREF normal position) 1 SSTB = 1 (status byte = 1) 0 SECS = 0 (other standards, not SECAM) */ 0x0E, 0x18, /* I/O and clock control */ 0x0F, 0x90, /* control #1 */ 0x10, 0x00, /* control #2 */ 0x11, 0x2c, /* chrominance gain reference */ 0x12, 0x7f, /* chrominance saturation */ 0x13, 0x5e, /* luminance contrast */ 0x14, 0x42, /* HSY begin 60 Hz */ 0x15, 0x1a, /* HSY stop 60 Hz */ 0x16, 0xff, /* HCL begin 60 Hz */ 0x17, 0xda, /* HCL stop 60 Hz */ 0x18, 0xf0, /* HSY after PHI1 60 Hz */ 0x19, 0x9b, /* luminance brightness */ /* 0x1A - not used 0x1B - not used 0x1C - not used 0x1D - not used 0x1E - not used 0x1F - not used */ 0x20, 0x7c, /* analog control #1 */ 0x21, 0x03, /* analog control #2 */ 0x22, 0xd2, /* mixer control #1 */ 0x23, 0x41, /* clamping level control 21 */ 0x24, 0x80, /* clamping level control 22 */ 0x25, 0x41, /* clamping level control 31 */ 0x26, 0x80, /* clamping level control 32 */ 0x27, 0x4f, /* gain control #1 */ 0x28, 0xfe, /* white peak control */ 0x29, 0x01, /* sync bottom control */ 0x2A, 0xcf, /* gain control analog #2 */ 0x2B, 0x0f, /* gain control analog #3 */ 0x2C, 0x83, /* mixer control #2 */ 0x2D, 0x01, /* integration value gain */ 0x2E, 0x81, /* vertical blanking pulse set */ 0x2F, 0x03, /* vertical blanking pulse reset */ 0x30, 0x60, /* ADCs gain control */ 0x31, 0x71, /* mixer control #3 */ 0x32, 0x02, /* integration value white peak */ 0x33, 0x8c, /* mixer control #4 */ 0x34, 0x03, /* gain update level */ }; /********************************************************************/ /* * sda = set data bit on I2C bus * to the value given by parameter high */ static inline void hrt_sda(hrt_t *dev, unsigned long addr, int high) { if (high) dev->i2c_bits |= HRT_I2C_SDA; else dev->i2c_bits &= ~HRT_I2C_SDA; iowrite8(dev->i2c_bits, (void *) I2C_CONTROL(addr)); wmb(); } /********************************************************************/ /* * scl = set clock bit on I2C bus * to the value given by parameter high */ static inline void hrt_scl(hrt_t *dev, unsigned long addr, int high) { if (high) dev->i2c_bits |= HRT_I2C_SCL; else dev->i2c_bits &= ~HRT_I2C_SCL; iowrite8(dev->i2c_bits, (void *) I2C_CONTROL(addr)); wmb(); } /********************************************************************/ /** * Queue manipulation functions (basically wrappers for the Linux * linked list functions) */ void queue_add_tail(struct stream_buffer *streambuf, struct stream_buffer_queue *q) { unsigned long flags; if (streambuf == NULL || q == NULL) return; write_lock_irqsave(&q->lock, flags); list_add_tail(&streambuf->node, &q->head); q->length++; write_unlock_irqrestore(&q->lock, flags); } /********************************************************************/ void queue_del(struct stream_buffer *streambuf, struct stream_buffer_queue *q) { unsigned long flags; if (q == NULL) return; write_lock_irqsave(&q->lock, flags); list_del(&streambuf->node); q->length--; write_unlock_irqrestore(&q->lock, flags); } /********************************************************************/ struct stream_buffer *queue_peek_head(struct stream_buffer_queue *q) { unsigned long flags; struct stream_buffer *streambuf; if (q == NULL) return NULL; read_lock_irqsave(&q->lock, flags); streambuf = list_entry(q->head.next, struct stream_buffer, node); read_unlock_irqrestore(&q->lock, flags); return streambuf; } /********************************************************************/ struct stream_buffer *queue_del_head(struct stream_buffer_queue *q) { unsigned long flags; struct stream_buffer *streambuf; if (q == NULL) return NULL; if (!q->length) return NULL; read_lock_irqsave(&q->lock, flags); streambuf = list_entry(q->head.next, struct stream_buffer, node); list_del(&streambuf->node); q->length--; read_unlock_irqrestore(&q->lock, flags); return streambuf; } /********************************************************************/ /** * hrt_streamoff - called by the STREAMOFF ioctl. * Used to turn off the streaming read from the device */ void hrt_streamoff(hrt_t *hrtdev, __u32 type) { int i = 0; struct stream_buffer *buf; /* Cant turn streaming off if it isnt on !! */ if (!hrtdev->streaming) { hrt_printk("STREAMOFF - Not in streaming mode\n"); return; } /* We can only have the following type specified * anything else is an error */ if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { hrt_printk("STREAMOFF - Wrong buffer type\n"); return; } /* Set the 'streaming' variable to 0 * indicates that we are no more in streaming mode * used by the timer handler to determine what to do */ hrtdev->streaming = 0; /* Added 13 June 2005 - Atulya/Sean */ hrtdev->streaming_state = 0; hrtdev->upside_down = 0; /* Wait till the queue is empty */ while ((buf = queue_del_head(&hrtdev->done_list))) { buf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; i++; } while (queue_del_head(&hrtdev->capture_list)); hrt_printk("Streaming is turned off\n"); } /********************************************************************/ /** * hrt_do_private_ioctl - handle private ioctl's on device */ static int hrt_do_private_ioctl(hrt_t *hrtdev, unsigned int cmd, unsigned long arg) { if (cmd == IOC_HRT_NEXT_MMAP_IS_DIRECT) { /* The next mmap() that's called on /dev/video will map the device's memory directly- this is for userspace drivers */ next_mmap_is_direct = 1; return 0; } /*else if (cmd == IOC_HRT_SET_I2CREG) { struct i2c_regval r; if (copy_from_user(&r, (void *) arg, sizeof(r))) { return -EFAULT; } i2c_set_reg(hrtdev, r.reg, r.val); return 0; } else if (cmd == IOC_HRT_GET_I2CREG) { struct i2c_regval r; r.val = hrtdev->regvals[r.reg]; if (copy_to_user((void *) arg, &r, sizeof(r))) { return -EFAULT; } return 0; } */ /* Set the region of interest - subwindow */ else if (cmd == IOC_HRT_SET_ROI) { struct subwindow win; if (copy_from_user(&win, (void *) arg, sizeof(win))) { hrt_printk("ioctl IOC_HRT_SET_ROI failed\n"); return -EFAULT; } /* Check if the specified height and width are valid values */ if (win.height > HRT_HEIGHT || win.height < 0) return -EINVAL; if(hrtdev->mode & HRT_COLOR_MODE){ if (win.width > HRT_COLOR_WIDTH || win.width < 0) return -EINVAL; } else{ if (win.width > HRT_GRAY_WIDTH || win.width < 0) return -EINVAL; } if (win.startx > win.width || win.startx < 0) return -EINVAL; if (win.starty > win.height || win.starty < 0) return -EINVAL; hrt_printk ("setting hrtdev->win\n"); *hrtdev->win = win; return 0; /* Return the current region of interest parameters to the user */ } else if (cmd == IOC_HRT_GET_ROI) { if (copy_to_user((void *) arg, hrtdev->win, sizeof(*hrtdev->win))) { hrt_printk("ioctl IOC_HRT_GET_ROI failed\n"); return -EFAULT; } return 0; } /* else if (cmd == IOC_HRT_SET_I2CREGS) { */ /* unsigned char len; */ /* int retval; */ /* unsigned char *buf; */ /* /\* Stop the timer first *\/ */ /* del_timer_sync(&timer); */ /* timer_running = 0; */ /* /\* First byte is the length of the array *\/ */ /* if (copy_from_user(&len, (void *) arg, 1)) */ /* return -EFAULT; */ /* dprintk("size of array = %d\n", len); */ /* buf = vmalloc(len + 1); */ /* if (copy_from_user(buf, (void *) arg, len)) { */ /* vfree(buf); */ /* return -EFAULT; */ /* } */ /* retval = i2c_init(hrtdev, buf); */ /* vfree(buf); */ /* /\* Start the timer again *\/ */ /* timer.expires = jiffies + (HZ/100); */ /* add_timer(&timer); */ /* timer_running = 1; */ /* return retval; */ return -EINVAL; } /********************************************************************/ /** * hrt_direct_mmap - provides direct access to the card's memory */ static int hrt_direct_mmap(struct file *file, struct vm_area_struct *vma) { struct hrt_per_file *per_file = file->private_data; hrt_t *hrtdev = per_file->hrtdev; vma->vm_flags |= (VM_IO | VM_RESERVED); /* Just map the pages directly */ if (remap_pfn_range(vma, vma->vm_start, hrtdev->phys_addr, vma->vm_end - vma->vm_start, vma->vm_page_prot)) return -EAGAIN; return 0; } /********************************************************************/ /** * mmap_stream_buffer_from_offset - get a buffer via the specified offset */ static struct stream_buffer *mmap_stream_buffer_from_offset(hrt_t *dev, unsigned long offset) { int i; offset *= PAGE_SIZE; for (i = 0; i < MAX_CAPTURE_BUFFERS; ++i) { if (offset == dev->stream_buf[i].vidbuf.m.offset) { hrt_printk("mmap_stream_buffer_from_offset: offset found at : 0x%lx", offset); return &dev->stream_buf[i]; } } hrt_printk("mmap_stream_buffer_from_offset: NOTHING found."); return NULL; } /* * * Dr Baker's I2C routines follow * */ /********************************************************************/ /* * sda_scl = set data and clock bits on I2C bus * to the values given by parameters sda_high and scl_high */ static inline void hrt_sda_scl(hrt_t *dev, unsigned long addr, int sda_high, int scl_high) { if (sda_high) dev->i2c_bits |= HRT_I2C_SDA; else dev->i2c_bits &= ~HRT_I2C_SDA; if (scl_high) dev->i2c_bits |= HRT_I2C_SCL; else dev->i2c_bits &= ~HRT_I2C_SCL; iowrite8(dev->i2c_bits, (void *) I2C_CONTROL(addr)); wmb(); } /********************************************************************/ /* * sda_read = read data bit from I2C control register */ static inline int hrt_sda_read(unsigned long addr) { char c = ioread8((void *) I2C_CONTROL(addr)); return (c & HRT_I2C_SDA); } /********************************************************************/ /* * hrt_i2c_start = start I2C data transmission */ static inline void hrt_i2c_start(hrt_t *dev, unsigned long addr) { hrt_sda_scl(dev, addr, 0, 0); hrt_i2c_delay(); hrt_sda(dev, addr, 1); hrt_i2c_delay(); hrt_scl(dev, addr, 1); hrt_i2c_delay(); hrt_sda(dev, addr, 0); hrt_i2c_delay(); hrt_scl(dev, addr, 0); } /********************************************************************/ /* * hrt_i2c_stop = end I2C data transmission */ static inline void hrt_i2c_stop(hrt_t *dev, unsigned long addr) { hrt_sda_scl(dev, addr, 0, 0); hrt_i2c_delay(); hrt_scl(dev, addr, 1); hrt_i2c_delay(); hrt_sda(dev, addr, 1); hrt_i2c_delay(); hrt_scl(dev, addr, 0); hrt_scl(dev, addr, 1); } /********************************************************************/ /* * hrt_i2c_send_bit */ static int hrt_i2c_send_bit(hrt_t *dev, unsigned long addr, unsigned char bit) { unsigned long timeout; if (bit) hrt_sda(dev, addr, 1); else hrt_sda(dev, addr, 0); hrt_i2c_delay(); iowrite8(dev->i2c_bits | 0x04, (void *) I2C_CONTROL(addr)); wmb(); hrt_i2c_delay(); if (I2C_BUSY(addr)) { timeout = jiffies + HZ/10; while (I2C_BUSY(addr)) { if (jiffies > timeout) { HRT_ERROR_MSG("i2c bus timeout"); return -1; } } } return 0; } /********************************************************************/ /* * hrt_i2c_send_byte */ static int hrt_i2c_send_byte(hrt_t *dev, unsigned long addr, unsigned char data) { char bitpos, bit; for(bitpos = 0; bitpos < 8; bitpos++) { bit = (data & 0x80) >> 7; data <<= 1; if(hrt_i2c_send_bit(dev, addr, bit)) goto failure; } hrt_i2c_delay(); hrt_sda_scl(dev, addr, 1, 0); hrt_scl(dev, addr, 1); /* leave clock high */ udelay(10); if (hrt_sda_read(addr)) { HRT_ERROR_MSG("no i2c ack"); goto failure; } hrt_sda_scl(dev, addr, 1, 0); return 0; failure: hrt_sda_scl(dev, addr, 1, 0); hrt_i2c_stop(dev, addr); return -1; } /********************************************************************/ /* * hrt_i2c_init_registers * * sends a sequence of values to the A/D converter device over the I2C bus. * For an example of the format of "sequence" see the declaration of * saa7110_default_init_regs[] above. The first byte is the number of * data bytes that follow. The rest of the sequence is a series of pairs * of a register number followed by a value. It is better if the values * are sorted in increasing order of register number, but the sequence may * have gaps and need not be in order. */ int hrt_i2c_init_registers(hrt_t *dev, const char *sequence) { unsigned long addr; int i, len, cur_reg; len = (int) (*sequence++); addr = dev->virt_addr; if (len <= 2) { HRT_ERROR_MSG("invalid register initialization sequence"); return -1; } hrt_i2c_start(dev, addr); /* here we select the A/D Device on the i2c bus * that should pay attention to the following bytes */ if (hrt_i2c_send_byte(dev, dev->virt_addr, HRT_AD_DEVICE_ID)) { HRT_ERROR_MSG("send_byte failed"); return -1; } /* start at the first register and increment along the way */ if (hrt_i2c_send_byte(dev, dev->virt_addr, cur_reg = sequence[0])) { HRT_ERROR_MSG("send_byte failed(2)"); return -1; } for(i = 0; i < len; i += 2, sequence += 2) { char reg = sequence[0]; char data = sequence[1]; if (reg > HRT_SAA7110_MAXREG) { HRT_ERROR_MSG("register %02X out of range!", reg); return -1; } if (reg != cur_reg) { /* we're going to an entirely different register */ hrt_i2c_stop(dev, addr); hrt_i2c_start(dev, addr); /* select the chip/device on the bus */ if (hrt_i2c_send_byte(dev, dev->virt_addr, HRT_AD_DEVICE_ID)) { HRT_ERROR_MSG("send_byte failed(3)"); return -1; } /* select the register */ if (hrt_i2c_send_byte(dev, dev->virt_addr, cur_reg = reg)) { HRT_ERROR_MSG("send_byte failed(4)"); return -1; } } if (hrt_i2c_send_byte(dev, dev->virt_addr, data)) { HRT_ERROR_MSG("send_byte failed(5)"); return -1; } dev->saa7110_registers[cur_reg++] = data; } /* free the i2c bus */ hrt_i2c_stop(dev, addr); return 0; } /********************************************************************/ int hrt_i2c_init_device(hrt_t *dev) { int result = 0; HRT_DEBUG_MSG(2, "hrt_i2c_init_device entered" " (virt_addr = %08X, addr = %08X)", (unsigned) dev->virt_addr, (unsigned) dev->phys_addr); result = hrt_i2c_init_registers(dev, saa7110_default_init_regs); if (result) { HRT_ERROR_MSG("hrt_i2c_init_registers failed %d", result); return result; } HRT_DEBUG_MSG(2, "hrt_i2c_init_device returning %d", result); return result; } /******************************************************************** * * grab_field * * Function that reads one field from the device and copies it to the * hrtdev->framedata buffer. This grab_field is executed twice to read * fields and then we have a complete frame for the user. * This function is implemented differantly for color and gray scale devices * since they are stored differantly inside the device's memory * the grey scale implementation is straightforward * but the color device stores its data in a peculiar fashion. * Each line has 1664 bytes organised as follows * 512 bytes - first 512 LSBs * 512 bytes - first 512 MSBs * 128 bytes - last 128 LSBs * 384 bytes - BLANK !! * 128 bytes - last 128 MSBs * All this needs to be reorganised the 1280 bytes for one line * And in case of a smaller subwindow the coordinates of the subwindow * need to be taken care of while implementing this * * the 'parity' argument determines the location of this field * inside the current frame * * Point to note - * GreyScale requires 1 byte per pixel * Color requires 2 bytes per pixel */ void grab_field( hrt_t *hrtdev, unsigned char *framedata, int parity) { int i,j,k; /* Length of the line; used here only by the grey scale implementation */ int linelen; /* Coordinates of the sub window */ int win_width, win_height, win_col, win_row; win_width = hrtdev->win->width; win_height = hrtdev->win->height; win_row = hrtdev->win->starty; win_col = hrtdev->win->startx; printk(KERN_ALERT "win_width = %d\n", win_width); printk(KERN_ALERT "win_height = %d\n", win_height); printk(KERN_ALERT "win_row = %d\n", win_row); printk(KERN_ALERT "win_col = %d\n", win_col); if( (hrtdev->mode & HRT_COLOR_MODE) ){ printk(KERN_ALERT "win_width = %d\n", win_width); printk(KERN_ALERT "win_height = %d\n", win_height); printk(KERN_ALERT "win_row = %d\n", win_row); printk(KERN_ALERT "win_col = %d\n", win_col); linelen = hrtdev->win->width; /* For the color case we split up work into three cases * Case 1 : window completely to the left of the 512 pixel boundary * Case 2 : window crossing the 512 pixel boundary * Case 3 : window completely to the right of the 512 pixel boundary last 128 pixels of the row */ for (i = (!parity) + win_row; i < win_height; i += 2) { /* Write the address of the required line into the register */ iowrite16(i, (void*)hrtdev->virt_addr + HRT_Y_LOW_REG); wmb(); /* color card - number of bytes required = 2 X win_width */ memset(hrtdev->localbuf, 0, 2*win_width); /* Case 1 */ if ( (win_col + win_width) < 512){ printk(KERN_ALERT "IM IN CASE 1\n"); /* Get the first win_width MSBs */ memcpy(hrtdev->localbuf, (void *)hrtdev->virt_addr + win_col, win_width); /* Get the first win_width LSBs */ memcpy(hrtdev->localbuf + win_width, (void *)hrtdev->virt_addr + 512 + win_col, win_width); } /* Case 2 * This one gets messy, but this is correct * and works fine ! * all this for efficiency ! * */ else if ( ((win_col +win_width) >= 512) && (win_col < 512) ){ //printk(KERN_ALERT "IM IN CASE 2\n"); /* Get the first (512 - win_col) MSBs */ memcpy(hrtdev->localbuf, (void *)hrtdev->virt_addr + win_col , 512 - win_col); /* Get the last (win_width - 512 + win_col) MSBs */ memcpy(hrtdev->localbuf+ 512 - win_col, (void *)hrtdev->virt_addr + 1024, win_width -512 +win_col); /* Get the first (512 - win_col) LSBs */ memcpy(hrtdev->localbuf + win_width , (void *)hrtdev->virt_addr + 512 + win_col, 512 - win_col); /* Get the last (win_width - 512 + win_col) LSBs */ memcpy(hrtdev->localbuf + win_width +512 - win_col, (void *)hrtdev->virt_addr + 1536, win_width - 512 + win_col); } /* Case 3 */ else if ( ((win_col +win_width) >= 512) && (win_col >= 512) ){ printk(KERN_ALERT "IM IN CASE 3\n"); /* Get the first win_width MSBs */ memcpy(hrtdev->localbuf, (void *)hrtdev->virt_addr + win_col + 1024, win_width); /* Get the first win_width MSBs */ memcpy(hrtdev->localbuf + win_width, (void *)hrtdev->virt_addr + 1536 + win_col, win_width); } /* At this point for all three cases we have all the bytes * stored in the form of all MSBs followed by all LSBs * Now copy into the framedata buffer one MSB followed * by the corresponding LSB * * However the destination for the copy is differant for the case of streaming I/O * as compared to the regular reads. * For streaming, we want to copy into the buffer that was passed as a parameter to * this function * But for the read operation we need to copy into the device's frame sized buffer 'framedata' * * Neat !!! * */ if (!hrtdev->streaming) { /* This is for the blocking read based display */ for (k = 0; k < win_width; k++) { j = k; if ( !(hrtdev->upside_down) ){ hrtdev->framedata[(i*2*win_width) + 2*k] = hrtdev->localbuf[j]; hrtdev->framedata[(i*2*win_width) + 2*k+1] = hrtdev->localbuf[j+win_width]; } else { /* If the user wishes to see the image upside down then this code * makes that happen. In our case we as user wanted to have that functionality * since our camera was mounted upside down. So implemented this feature !! */ hrtdev->framedata[((win_height-i+win_row-parity)*2*win_width) + 2*k] = hrtdev->localbuf[j]; hrtdev->framedata[((win_height-i+win_row-parity)*2*win_width) + 2*k+1] = hrtdev->localbuf[j+win_width]; } } } else { /* This is for the streaming I/O */ for (k = 0; k < win_width; k++) { j = k; if ( !(hrtdev->upside_down) ){ framedata[(i*2*win_width) + 2*k] = hrtdev->localbuf[j]; framedata[(i*2*win_width) + 2*k+1] = hrtdev->localbuf[j+win_width]; } else { /* If the user wishes to see the image upside down then this code * makes that happen. In our case we as user wanted to have that functionality * since our camera was mounted upside down. So implemented this feature !! */ framedata[((win_height-i+win_row-parity)*2*win_width) + 2*k] = hrtdev->localbuf[j]; framedata[((win_height-i+win_row-parity)*2*win_width) + 2*k+1] = hrtdev->localbuf[j+win_width]; } } } } } else { /* Out of the color mode. This one is for gray scale */ /* Following takes care of the sub window coordinates */ linelen = hrtdev->win->width - hrtdev->win->startx; /* Since this is just a single field that we are writing into the buffer * we write each line of this field into an alternate line of the buffer. * Interleaving. */ printk(KERN_ALERT "\nDAN---------------> I AM GREY SCALE MODE <-----------------\n"); if (hrtdev->upside_down == 1) { for (i = (!parity) + hrtdev->win->starty; i < hrtdev->win->height; i += 2) { iowrite16(i, (void *)hrtdev->virt_addr + HRT_Y_LOW_REG); wmb(); /* Copy into the 'framedata' the desired part of the current line, * at the correct location inside the buffer */ memcpy_fromio(framedata + (hrtdev->win->height - i - parity - hrtdev->win->starty) * linelen, (void *)hrtdev->virt_addr + hrtdev->win->starty, linelen); memset ( (void *)hrtdev->virt_addr + hrtdev->win->starty ,0, linelen); } } else { for (i = (!parity) + hrtdev->win->starty; i < hrtdev->win->height; i += 2) { iowrite16(i, (void *)hrtdev->virt_addr + HRT_Y_LOW_REG); wmb(); /* Copy into the 'framedata' the desired part of the current line, * at the correct location inside the buffer */ memcpy_fromio(framedata + (i * linelen), (void *)hrtdev->virt_addr + hrtdev->win->starty, linelen); memset ( (void *)hrtdev->virt_addr + hrtdev->win->starty,0, linelen); } } } } /********************************************************************/ static inline void hrt_irq_enable(hrt_t *dev) { int val; val = ioread8((void *)(dev->virt_addr + HRT_IRQ_ENABLE)); val |= 0x1; iowrite8(val, (void *) (dev->virt_addr + HRT_IRQ_ENABLE)); } /********************************************************************/ static inline void hrt_irq_disable(hrt_t *dev) { int val; val = ioread8((void *)(dev->virt_addr + HRT_IRQ_ENABLE)); val &= ~0x1; iowrite8(val, (void *) (dev->virt_addr + HRT_IRQ_ENABLE)); } /******************************************************************** ******** timer/tasklet related functions declared here *********** */ void hrt_tasklet(unsigned long); void hrt_timer_init(hrt_t* dev); void hrt_timer_cleanup(hrt_t* dev); int hrt_timer_activate(hrt_t* dev); void hrt_timer_deactivate(hrt_t* dev); /******************************************************************** * hrt_cleanup * * Cleanup the device - called from the module cleanup routine */ void hrt_cleanup(hrt_t* dev) { HRT_DEBUG_MSG(1, "shutting down hrt device %d at 0x%lx", dev->num, dev->phys_addr); HRT_MODULE_CHECK (HRT_MODULE_FINALIZING_STATE | HRT_MODULE_INITIALIZING_STATE, "1"); HRT_CHECK (dev, HRT_INITIALIZING_STATE | HRT_CLOSED_STATE | HRT_FINALIZING_STATE, "2"); dev->state = HRT_FINALIZING_STATE; /* restore device to an inactive state */ hrt_freeze_next(dev); /* deactivate and remove interrupt handler or timer */ hrt_timer_cleanup (dev); if (dev->phys_addr) { HRT_DEBUG_MSG(2, "release_mem_region 0x%lx, 0x%lx", (unsigned long)dev, (unsigned long) dev->phys_addr); release_mem_region(dev->phys_addr, HRT_IO_SIZE); dev->phys_addr = 0; } if (dev->virt_addr) { HRT_DEBUG_MSG(2, "iounmap %lx", (unsigned long) dev->virt_addr); printk (KERN_ALERT "DAN ---> unmapping virtual memory for device \n"); iounmap((void *)dev->virt_addr); /* problem might be here dan */ dev->virt_addr = 0; } /* possible problem2 dan */ #ifdef CONFIG_PCI printk(KERN_ALERT "DAN --- > DISABLING THE PCI DEVICE\n"); if (dev->pci_dev) { printk(KERN_ALERT "DAN --- > INSIDE THE IF --> DISABLING THE PCI DEVICE\n"); pci_disable_device(dev->pci_dev); dev->pci_dev = NULL; } #endif if (dev->registered == 1) { printk(KERN_ALERT "TRYING TO KILL ISA DEVICE \n"); video_unregister_device(&dev->video_dev); /* set the per-device state variable to indicate we are not registered any more */ dev->registered =0; } /* De-allocate frame buffer */ if (dev->framedata) { vfree(dev->framedata); dev->framedata = NULL; } /* Deallocate the smaller local buffer */ if (dev->localbuf) { kfree(dev->localbuf); dev->localbuf = NULL; } /* Set the device state to uninitialised * now that it is all cleaned up */ dev->state = HRT_UNINITIALIZED_STATE; } /******************************************************************** * hrt_lock_init * * Initialises the spin lock used in the module */ void hrt_lock_init(hrt_t * dev) { spin_lock_init(&dev->spinlock); dev->is_locked = 0; } /******************************************************************** * hrt_trylock * * Does nothing. Just checks the spinlock. */ int hrt_trylock(hrt_t * dev) { spin_lock(&dev->spinlock); if (dev->is_locked) { spin_unlock(&dev->spinlock); return 1; } dev->is_locked = 1; spin_unlock(&dev->spinlock); return 0; } /******************************************************************** * hrt_lock * * lock the device */ void hrt_lock(hrt_t * dev) { unsigned long flags; spin_lock_irqsave(&dev->spinlock, flags); if (dev->is_locked) { HRT_ERROR_MSG("locking an already-locked device"); } dev->is_locked = 1; spin_unlock_irqrestore(&dev->spinlock, flags); } /******************************************************************** * hrt_unlock * * Unlock the device */ void hrt_unlock(hrt_t * dev) { spin_lock(&dev->spinlock); if (!dev->is_locked) { HRT_ERROR_MSG("unlocking unlocked device"); } dev->is_locked = 0; spin_unlock(&dev->spinlock); } /******************************************************************** * hrt_init * * Long function.. * Probes and initialises the device */ int hrt_init(unsigned long phys_address, struct pci_dev * pci_dev) { hrt_t * dev = &hrt_devices[hrt_num_devices]; int i, result; char *bus, *color, *ported; unsigned int old_control, old_y_high, old_y_low; unsigned char val1, val2; unsigned long virt_address; HRT_DEBUG_MSG(1, "probing device at 0x%lx", phys_address); memset(dev, 0, sizeof(hrt_t)); dev = &hrt_devices[hrt_num_devices]; /* put the device in the unregistered state */ dev->registered = 0; printk(KERN_ALERT "RYAN: $$$$$ DEV->MODE = %d\n", dev->mode); dev->state = HRT_INITIALIZING_STATE; dev->num = hrt_num_devices; hrt_lock_init (dev); tasklet_init(&dev->tasklet, hrt_tasklet, (unsigned long) dev); dev->tasklet_active = 1; /* reserve the I/O address space for this device */ if (!request_mem_region(phys_address, HRT_IO_SIZE, "hrt")) { HRT_ERROR_MSG("I/O memory at %lx already in use", (unsigned long) phys_address); // hrt_cleanup(dev); return -EBUSY; } dev->phys_addr = phys_address; /* map the device's I/O space into kernel memory */ virt_address = (unsigned long) ioremap_nocache(phys_address, HRT_IO_SIZE); if (!virt_address) { HRT_ERROR_MSG("couldn't remap io memory!!"); hrt_cleanup(dev); return -ENODEV; } dev->virt_addr = virt_address; #ifdef CONFIG_PCI /* pci-specific processing */ if (pci_dev) { pci_set_drvdata (pci_dev, dev); dev->pci_dev = pci_dev; dev->irq = -pci_dev->irq; printk(KERN_ALERT "RYAN: @@@@@ MODE = %d\n", dev->mode); dev->mode &= ~HRT_ISA_MODE; printk(KERN_ALERT "RYAN: @@@@@ ISA_MODE = %d\n", HRT_ISA_MODE); printk(KERN_ALERT "RYAN: @@@@@ MODE &= ~HRT_ISA_MODE = %d\n", dev->mode); /* make certain IRQ is disabled before enabling device, or else we may get an IRQ we are not prepared to handle? */ hrt_irq_disable(dev); if (pci_enable_device(pci_dev)) { HRT_ERROR_MSG("pci_enable_device failed"); hrt_cleanup(dev); return -EIO; } } #endif /* find out whether there is an hrt device at this address, and which type of device it is */ /* save the values we are about to modify */ old_control = ioread8((void *)(HRT_CONTROL_REG + virt_address)); old_y_low = ioread8((void *)(HRT_Y_LOW_REG + virt_address)); old_y_high = ioread8((void *)(HRT_Y_HIGH_REG + virt_address)); /* freeze the frame grabbing, immediately */ iowrite8(0x5B, (void *) (HRT_CONTROL_REG + virt_address)); /* complement pixel (0,0) */ iowrite16(0, (void *) (HRT_Y_LOW_REG + virt_address)); iowrite16(0, (void *) (HRT_Y_HIGH_REG + virt_address)); val1 = ioread8((void *) virt_address); iowrite8(~val1, (void *) virt_address); /* write old value of pixel (0,0) to (1,0) */ iowrite16(1, (void *) (HRT_Y_LOW_REG + virt_address)); iowrite8(val1, (void *) virt_address); /* read the value at the previous raster/row */ iowrite16(0, (void *) (HRT_Y_LOW_REG + virt_address)); val2 = ioread8((void *) virt_address); if (val2 != (unsigned char)~val1) { HRT_DEBUG_MSG(1, "no hrt device at address 0x%lx", virt_address); /* restore the old values, and hope we did no damage to some other device at this address; this is pretty poor, since if there is another device at that address the effects of these writes could be harmful; ideally, there should be a way to identify the devices that only uses read operations */ iowrite8(val1, (void *) virt_address); iowrite8(old_y_low, (void *) (HRT_Y_LOW_REG + virt_address)); iowrite8(old_y_high, (void *) (HRT_Y_HIGH_REG + virt_address)); iowrite8(old_control, (void *) (HRT_CONTROL_REG + virt_address)); hrt_cleanup(dev); return -ENODEV; } /* test whether we have a color or greyscale card: the color frame buffer has line of 2048 = 0x400 pixels; greyscale has only 512 pixels/line, so the memory mapped row of frame buffer memory should wrap around at 0x200 and 0x400 on a greyscale card but should be good through 0x400 on a color card. */ iowrite8(HRT_FREEZE_IMM_CMD, (void *) (HRT_CONTROL_REG + virt_address)); iowrite16(0, (void *) (HRT_Y_LOW_REG + virt_address)); iowrite16(0, (void *) (HRT_Y_HIGH_REG + virt_address)); val1 = ioread8((void *)(0x400 + virt_address)); printk(KERN_ALERT "RYAN: #### VAL1 = %d \n", val1); iowrite8(~val1, (void *) (0x400 + virt_address)); /* in case greyscale addresses wrap around, refresh the value at offset zero */ iowrite8(val1, (void *) (virt_address)); val2 = ioread8((void *)(0x400 + virt_address)); printk(KERN_ALERT "RYAN: #### VAL2 = %d \n", val2); if (val2 == (unsigned char) ~val1) { printk(KERN_ALERT "RYAN: ------I AM IN VAL2---- \n"); printk(KERN_ALERT "RYAN: #### DEV->MODE = %d \n", dev->mode); printk(KERN_ALERT "RYAN: #### HRT_COLOR_MODE = %d\n", HRT_COLOR_MODE); dev->mode |= HRT_COLOR_MODE; printk(KERN_ALERT "RYAN: #### DEV->MODE |=HRT_COLOR_MODE = %d \n", dev->mode); /* infer the frame geometry from the device type */ HRT_DEBUG_MSG(1, "COLOR device detected"); dev->rows = 399; dev->cols = 512; dev->bytes_per_pixel = 2; } else { HRT_DEBUG_MSG(1, "GREYSCALE device detected"); dev->cols = 512; /* is for NTSC, 512 for PAL */ dev->rows = 480; dev->bytes_per_pixel = 1; } /* try to initialize the device */ result = hrt_i2c_init_device(dev); if (result) { HRT_ERROR_MSG("hrt_i2c_init_device %d failed",result); hrt_cleanup(dev); return -ENODEV; } hrt_go_live(dev); /* delete this once driver is debugged */ /* find out whether the device has dual-ported memory*/ while (ioread8((void *)(dev->virt_addr + HRT_CONTROL_REG)) & 0x40) { hrt_printk( "waiting on dual port check\n"); schedule(); } /* try to overwrite the line with ones */ for (i = 0; i < 512; i++) { iowrite8(255, (void *) (dev->virt_addr + i)); } /* freeze the image */ hrt_freeze_immediate(dev); /* write zeros to horizontal raster line 500, a line that the A/D unit does not modify */ iowrite16(0, (void *) (dev->virt_addr + HRT_Y_HIGH_REG)); iowrite16(500, (void *) (dev->virt_addr + HRT_Y_LOW_REG)); for (i = 0; i < 512; i++) { iowrite8(0, (void *)(dev->virt_addr + i)); } /* set capturing mode */ hrt_go_live(dev); /* try to overwrite the line with ones */ for (i = 0; i < 512; i++) { iowrite8(255, (void *) (dev->virt_addr + i)); } /* freeze the image */ hrt_freeze_immediate(dev); /* read back the line; if some of the pixels are still zero the memory is not dual ported */ result = 1; for (i = 0; i < 512; i++) { if (!ioread8((void *)(dev->virt_addr + i))) result = 0; } if (result) { HRT_DEBUG_MSG(1, "seems to be dual ported"); dev->mode |= HRT_DUAL_PORTED_MODE; } printk(KERN_ALERT "Dan after dual ported message \n"); //printk(KERN_ALERT "RYAN: !!!! Dev->mode = %d\n",dev->mode); //printk(KERN_ALERT "RYAN: !!!! HRT_ISA_MODE = %d\n",HRT_ISA_MODE); if(testing_isa_mode == 1){ dev->mode |= HRT_ISA_MODE; } //printk(KERN_ALERT "RYAN: !!!! Dev->mode |=ISA_MODE = %d\n",dev->mode); //printk(KERN_ALERT "Dan after dev->mode or \n"); if(testing_isa_mode != 1){ printk(KERN_ALERT "I AM CONFIG PCI\n"); if ((pci_dev->device == HRT_DEVICE_ID_COLOR) != ((dev->mode & HRT_COLOR_MODE) == HRT_COLOR_MODE)) HRT_ERROR_MSG("PCI and probed types don't match"); } printk(KERN_ALERT "Dan before to dicover interrupts device\n"); /* discover whether this device supports interrupts */ hrt_timer_init(dev); printk(KERN_ALERT "Dan after hrt_timer_init device\n"); if (dev->mode & HRT_IRQ_MODE) { printk(KERN_ALERT "Dan Trying to discover interrupts\n"); /* BASIC IDEA * Enable the device for 20 ms * and count the number of interrupts generated * a non zero value means that this device supports * IRQs */ dev->irq_count = 0; printk(KERN_ALERT "Dan before to enable device\n"); hrt_irq_enable(dev); printk(KERN_ALERT "Dan after to enable device\n"); printk(KERN_ALERT "Dan Trying to go live\n"); hrt_go_live(dev); printk(KERN_ALERT "Dan after to go live\n"); mdelay(20); printk(KERN_ALERT "Dan Trying to freeze device\n"); hrt_freeze_immediate(dev); printk(KERN_ALERT "Dan After to enable device\n"); hrt_irq_disable(dev); if (dev->irq_count) { HRT_DEBUG_MSG(1, "counted %d irqs in 20 ms", dev->irq_count); HRT_ERROR_MSG("devices supports IRQs"); } else { HRT_DEBUG_MSG(1, "no irqs in 20 ms"); dev->irq = 0; dev->mode &= ~HRT_IRQ_MODE; } } /* describe this device for the log */ if (dev->mode & HRT_ISA_MODE) bus = "ISA"; else bus = "PCI"; if (dev->mode & HRT_COLOR_MODE) color = "COLOR"; else color = "GREYSCALE"; if (dev->mode & HRT_DUAL_PORTED_MODE) ported = "DUAL"; else ported = "SINGLE"; HRT_ERROR_MSG("found device %d at 0x%lx", dev->num, dev->phys_addr); HRT_ERROR_MSG("%s %s with %s-ported memory", bus, color, ported); if (dev->mode & HRT_IRQ_MODE) HRT_ERROR_MSG("using IRQ %d", dev->irq); init_waitqueue_head(&dev->wait_queue); /* Read the field bit and assign to the field_id variable */ dev->field_id = ioread8((void *)dev->virt_addr + HRT_CONTROL_REG) & 1; /* Initialize the semaphore */ init_MUTEX(&dev->sem); /* set default window size to cover the entire frame buffer */ HRT_DEBUG_MSG(1, "setting width, cols = %d", dev->cols); dev->win_row = 0; dev->win_width = dev->cols; dev->win_col = 0; dev->win_height = dev->rows; dev->field = -1; hrt_num_devices++; dev->state = HRT_CLOSED_STATE; /* Make the lists used for streaming empty */ /* Added 2005.06.11 Sean/ Atulya begin */ INIT_LIST_HEAD(&dev->capture_list.head); INIT_LIST_HEAD(&dev->done_list.head); dev->capture_list.length = dev->done_list.length = 0; /* Initialize the spinlocks to protect the lists */ dev->capture_list.lock = RW_LOCK_UNLOCKED; dev->done_list.lock = RW_LOCK_UNLOCKED; /* Register with V4L */ dev->video_dev = videodev_template; /* Register the video driver */ printk(KERN_ALERT "RYAN: Before video_register_device\n"); if (video_register_device(&dev->video_dev, VFL_TYPE_GRABBER, video_minor) < 0) { hrt_printk(KERN_ERR "Unable to register video device\n"); /* Failure in the device registration * Cleanup before exiting */ hrt_cleanup(dev); return -ENODEV; } printk(KERN_ALERT "RYAN: After video_register_device...Must have registered???\n"); /* set the device status to registered */ dev->registered = 1; /* Set private data (to access through struct file) */ dev->video_dev.priv = dev; /* Set the constants depending on * whether the device supports color * or grey scale */ printk(KERN_ALERT "RYAN: @@@@ DEV->MODE = %d\n", dev->mode); printk(KERN_ALERT "RYAN: @@@@ COLOR_MODE = %d\n", HRT_COLOR_MODE); if(dev->mode & HRT_COLOR_MODE){ printk(KERN_ALERT "RYAN: IM INSIDE COLOR MODE\n"); dev->bytesperline = HRT_COLOR_BYTES_PER_LINE; dev->framesize = HRT_COLOR_FRAMESIZE; } else{ printk(KERN_ALERT "RYAN: IM INSIDE GRAYSCALE MODE\n"); dev->bytesperline = HRT_GRAY_BYTES_PER_LINE; dev->framesize = HRT_GRAY_FRAMESIZE; } /* Added 2005.06.11 Sean/ Atulya end */ /* Allocate the frame buffer */ if (dev->mode & HRT_COLOR_MODE) { dev->framedata = (unsigned char *) vmalloc(HRT_COLOR_FRAMESIZE); dev->localbuf = (unsigned char *) kmalloc(sizeof(char) * 1664, GFP_KERNEL); /* Added 2005.06.11 Sean/ Atulya begin */ if ((dev->framedata == NULL) || (dev->localbuf == NULL)) { hrt_printk(KERN_ERR "Unable to allocate memory\n"); //video_unregister_device(&dev->video_dev); hrt_cleanup(dev); return -ENOMEM; } /* Added 2005.06.11 Sean/ Atulya end */ } else { dev->framedata = (unsigned char *) vmalloc(HRT_GRAY_FRAMESIZE); dev->localbuf = NULL; /* Added 2005.06.11 Sean/ Atulya begin */ if (dev->framedata == NULL) { hrt_printk(KERN_ERR "Unable to allocate memory\n"); //video_unregister_device(&dev->video_dev); hrt_cleanup(dev); return -ENOMEM; } /* Added 2005.06.11 Sean/ Atulya end */ } /* The device opens in the non streaming mode by default */ dev->streaming = 0; /* Added 13 June 2005 */ dev->streaming_state = 0; dev->upside_down = 0; /* Read the field bit and assign to the field_id */ dev->field_id = hrt_get_field(dev); /* Make the device go-live */ hrt_go_live(dev); printk(KERN_ALERT "RYAN: Returning from HRT_INIT w 0 \n"); return 0; } /********************************************************************/ int hrt_isa_init(void) { int i; for (i = 0; i < ARRAY_SIZE(hrt_addresses); i++) hrt_init(hrt_addresses[i], NULL); hrt_nonpci_devices = hrt_num_devices; return 0; } /********************************************************************/ void hrt_isa_cleanup(void) { int i; for (i=0; ivirt_addr + HRT_CONTROL_REG)) & 0x4; rmb(); return r; } /********************************************************************/ irqreturn_t hrt_irq_handler(int irq, void* dev_id, struct pt_regs* regs) { hrt_t* dev = dev_id; if (!hrt_irq_pending(dev)) goto none; /* acknowledge the interrupt */ hrt_irq_disable(dev); dev->irq_count++; /* do not try to do any I/O on the device here, or else risk race with last scheduled tasklet */ tasklet_schedule(&dev->tasklet); /* enable the next interrupt */ hrt_irq_enable(dev); #ifdef IRQ_HANDLED return IRQ_HANDLED; #endif none: #ifdef IRQ_NONE return IRQ_NONE; #endif } /********************************************************************/ /** * streaming_fieldchange_color * * Invoked for performing the data reads when streaming * is in progress * Called by the timer handler when it determines that * a complete frame is available for reading into the buffer * */ static inline void streaming_fieldchange_color(hrt_t *hrtdev) { struct stream_buffer *streambuf; /* Capture list is empty */ if (!hrtdev->capture_list.length) { return; } streambuf = queue_peek_head(&hrtdev->capture_list); /* Sanity checks- don't want to do anything bad in interrupt mode */ if (streambuf == NULL) { hrt_printk("streambuf is NULL!\n"); return; } if (streambuf->vaddress == NULL) { hrt_printk("vaddress is NULL!\n"); return; } if (hrtdev->framedata == NULL) { hrt_printk("framedata is NULL!\n"); return; } /* Depending on the device being color or grey scale * the buffer would have differant sizes * clear the entire buffer before writing into it * * But only if it is empty ! */ if (streambuf->fields_grabbed == 0 ){ if (hrtdev->mode & HRT_COLOR_MODE) memset(streambuf->vaddress, 0, HRT_COLOR_FRAMESIZE); else memset(streambuf->vaddress, 0, HRT_GRAY_FRAMESIZE); } /* Grab the current field and increment the fields_grabbed variable */ grab_field(hrtdev, streambuf->vaddress,hrtdev->field_id); streambuf->fields_grabbed++; if(streambuf->fields_grabbed > 2){ /* We have a complete frame */ streambuf->vidbuf.flags |= V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_KEYFRAME; /* Move the buffer to the done queue */ queue_del(streambuf, &hrtdev->capture_list); queue_add_tail(streambuf, &hrtdev->done_list); hrtdev->field_id = !hrtdev->field_id; /* Set streaming_state to 0 to repeat the streaming read cycle */ hrtdev->streaming_state = 0; /* Set fields_grabbed to 0 */ streambuf->fields_grabbed = 0; /* wake up the user to notify abt the data being available */ wake_up(&hrtdev->wait_queue); } } /********************************************************************/ /** * streaming_fieldchange_irq * * This function used for streaming if we have IRQs running * Assumes that every interrupt we know that the field has changed * So faster than using the timer based mechanism */ static inline void streaming_fieldchange_irq(hrt_t *hrtdev) { struct stream_buffer *streambuf; /* Capture list is empty */ if (!hrtdev->capture_list.length) { return; } streambuf = queue_peek_head(&hrtdev->capture_list); /* Sanity checks- don't want to do anything bad in interrupt mode */ if (streambuf == NULL) { hrt_printk("streambuf is NULL!\n"); return; } if (streambuf->vaddress == NULL) { hrt_printk("vaddress is NULL!\n"); return; } if (hrtdev->framedata == NULL) { hrt_printk("framedata is NULL!\n"); return; } /* Now that we have checked everything * and all buffers seem to be fine * extract the current field from the device * and copy to the buffer */ grab_field(hrtdev, streambuf->vaddress, !hrtdev->field_id); streambuf->fields_grabbed++; /* We have a complete frame */ if (streambuf->fields_grabbed > 1) { streambuf->fields_grabbed = 0; streambuf->vidbuf.flags |= V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_KEYFRAME; /* Move the buffer to the done queue */ queue_del(streambuf, &hrtdev->capture_list); queue_add_tail(streambuf, &hrtdev->done_list); /* notify the wait queue that a frame is available * for display to the user */ wake_up(&hrtdev->wait_queue); } hrt_printk("streaming_fieldchange ... complete\n"); } /********************************************************************/ /** * streaming_fieldchange * * if streaming is on, this function is called * from timer_func when the field_id changes */ static inline void streaming_fieldchange(hrt_t *hrtdev) { struct stream_buffer *streambuf; /* Capture list is empty */ if (!hrtdev->capture_list.length) { return; } streambuf = queue_peek_head(&hrtdev->capture_list); /* Sanity checks- don't want to do anything bad in interrupt mode */ if (streambuf == NULL) { hrt_printk("streambuf is NULL!\n"); return; } if (streambuf->vaddress == NULL) { hrt_printk("vaddress is NULL!\n"); return; } if (hrtdev->framedata == NULL) { hrt_printk("framedata is NULL!\n"); return; } /* Now that we have checked everything * and all buffers seem to be fine * extract the current field from the device * and copy to the buffer */ grab_field(hrtdev, streambuf->vaddress, !hrtdev->field_id); streambuf->fields_grabbed |= (!hrtdev->field_id) + 1; /* We have a complete frame */ if (streambuf->fields_grabbed == 3) { streambuf->fields_grabbed = 0; streambuf->vidbuf.flags |= V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_KEYFRAME; /* Move the buffer to the done queue */ queue_del(streambuf, &hrtdev->capture_list); queue_add_tail(streambuf, &hrtdev->done_list); /* notify the wait queue that a frame is available * for display to the user */ wake_up(&hrtdev->wait_queue); } } /******************************************************************** * hrt_timer_handler * * Runs every HZ/100 jiffies * Core of the driver's functionality in the case where * we do not have interrupts running on the card. * * If we are in the blocking read mode, then this timer * notifies the reader about field changes, and wakes up * the reader when a new frame becomes available * * If we are in the streaming I/O mode, then the timer * performs checks on the device field changes * and invokes the streaming_fieldchange or the streaming_fieldchange_color * function based on the device's mode. */ void hrt_timer_handler(unsigned long data) { hrt_t* dev = (hrt_t*)data; if(dev->timer_active){ tasklet_schedule(&dev->tasklet); /* Schedule the timer to go off in another (HZ/100) jiffies */ dev->timer.expires = jiffies + (HZ/100); add_timer(&dev->timer); } } /******************************************************************** * hrt_irq_init * * Initialises the IRQs, if the device supports interrupts */ void hrt_irq_init(hrt_t* dev) { HRT_DEBUG_MSG(2, "hrt_irq_init"); dev->irq_count = 0; if (dev->irq == 0) return; if ((dev->irq > 0) || (dev->mode & HRT_IRQ_MODE)) { HRT_ERROR_MSG("irq handler already installed"); return; } hrt_irq_disable(dev); /* insurance */ if (request_irq(-dev->irq, hrt_irq_handler, SA_SHIRQ, "hrt", (void*)dev)) { HRT_ERROR_MSG("unable to reserve IRQ"); dev->irq = 0; } else { dev->irq = -dev->irq; dev->mode |= HRT_IRQ_MODE; } } /******************************************************************** * hrt_irq_cleanup * * Cleanup the IRQs */ void hrt_irq_cleanup(hrt_t* dev) { HRT_DEBUG_MSG(2, "hrt_irq_cleanup"); hrt_irq_disable(dev); /* insurance */ if (dev->mode & HRT_IRQ_MODE) { free_irq(dev->irq, dev); dev->mode &= ~HRT_IRQ_MODE; } } /******************************************************************* * hrt_timer_init * * If the device supports interrupts then initialise IRQs * else initialise the timer */ void hrt_timer_init(hrt_t* dev) { HRT_DEBUG_MSG(1, "hrt_timer_init"); HRT_CHECK(dev, HRT_INITIALIZING_STATE, "4"); if (dev->timer_active) { HRT_ERROR_MSG("irq or timer already active"); /* Commented by Sean & Atulya 2005.06.15 debugging irg return; */ } /* Added 15 june 2005, to add the functionality to use interrupts */ hrt_irq_init(dev); if (!(dev->mode & HRT_IRQ_MODE)) { if (dev->timer.function) { HRT_ERROR_MSG("timer already initialized"); return; } HRT_DEBUG_MSG(1, "installing timer\n"); init_timer(&dev->timer); dev->timer.function = hrt_timer_handler; dev->timer.data = (unsigned long)dev; } } /******************************************************************** * hrt_timer_cleanup * * Cleanup the timer / IRQ */ void hrt_timer_cleanup(hrt_t* dev) { HRT_DEBUG_MSG(1, "hrt_timer_cleanup"); HRT_CHECK(dev, HRT_FINALIZING_STATE, "3"); hrt_timer_deactivate (dev); /* Added by Sean/ Atulya 2005.06.15 --- begin --- */ tasklet_kill(&dev->tasklet); /* --- end --- */ hrt_irq_cleanup(dev); dev->timer.function = NULL; } /******************************************************************** * hrt_timer_deactivate * * If timer is running then deactivate it else disable the irqs. * The tasklet runs in both cases. So disable the tasklet */ void hrt_timer_deactivate(hrt_t* dev) { HRT_DEBUG_MSG(1, "hrt_timer_deactivate"); if (dev->timer_active) { dev->timer_active = 0; if (dev->mode & HRT_IRQ_MODE){ hrt_irq_disable(dev); } else del_timer_sync(&dev->timer); // Added 15 june 2005 if (dev->tasklet_active) { dev->tasklet_active = 0; tasklet_disable(&dev->tasklet); } } } /********************************************************************/ /* hrt_timer_activate * * Starts a task to monitor the state of the device. * This may be driven by a timer or by an interrupt * from the device. */ int hrt_timer_activate(hrt_t* dev) { int result = 0; HRT_DEBUG_MSG(1, "hrt_timer_activate"); if (dev->timer_active) { HRT_ERROR_MSG("irq or timer already active"); return 0; } dev->timer_active = 1; if (dev->mode & HRT_IRQ_MODE) { HRT_DEBUG_MSG(1, "enabling irq"); if (!dev->tasklet_active) { dev->tasklet_active = 1; tasklet_enable(&dev->tasklet); } hrt_irq_enable(dev); } else { if (!dev->timer.function) { HRT_ERROR_MSG("timer not initialized"); return -1; } printk("Enabling the tasklet inside the timer_activate\n"); if (!dev->tasklet_active) { dev->tasklet_active = 1; tasklet_enable(&dev->tasklet); } dev->timer.expires = jiffies + HZ/100; add_timer(&dev->timer); } return result; } /******************************************************************** * hrt_tasklet * * The tasklet is the core of the module * In both IRQ and Timer 'mode' finally the tasklet * performs the required function * * For the irq mode we know that every invocation we have * a field change * But for the timer mode, we check every time to see if the * field_id has changed * */ void hrt_tasklet(unsigned long data) { int new_field; hrt_t* dev = (hrt_t*)data; if (dev->state != HRT_OPEN_STATE) return; if (hrt_trylock (dev)) { /* device is busy */ return; } /* Get the current value of the field bit */ new_field = hrt_get_field (dev); if (dev->field_id == -1) { /* just started; don't know previous state */ dev->field_id = new_field; goto done; } /* We need to do something only if the field bit has changed from * the last value. This would indicate that the field has changed * from what we read in the last timer execution. * * So now we need to take some action if we detect the field change */ /* CASE 1 - NO IRQs. TIMER RUNNING EVERY HZ/100 JIFFIES */ if (!(dev->mode & HRT_IRQ_MODE)) { if (dev->streaming) { /* If we have streaming running, then call the function * responsible for updating the streaming display * * Differant implementations of streaming * for color and grey scale follow */ if ((dev->mode & HRT_COLOR_MODE) || !(dev->mode & HRT_DUAL_PORTED_MODE)){ /* if we are in the color mode, we follow differant states * * State 0 * * device goes live * state changes to 1 * * State 1 * * detect 2 field changes * issue freeze_next command * move to state 2 * * State 2 * * detect 2 field changes * call the streaming_framechange function * to read the current frame * move back to state 0 */ if (dev->streaming_state == 0){ hrt_printk("State 0\n"); if(dev->is_frozen){ hrt_printk("HRT_GO_LIVE\n"); hrt_go_live(dev); } else hrt_printk ("THIS IS AN ERROR\n"); dev->streaming_state = 1; dev->field_changes = 2; } else if (dev->streaming_state == 1){ hrt_printk("State 1 dev->field_changes = %d\n", dev->field_changes); new_field = hrt_get_field(dev); if (new_field != dev->field_id){ dev->field_id = new_field; if(dev->field_changes > 0) dev->field_changes--; if (dev->field_changes == 0){ dev->streaming_state = 2; dev->field_changes = 2; hrt_printk("HRT_FREEZE_NEXT\n"); hrt_freeze_next(dev); } } } else if (dev->streaming_state == 2){ hrt_printk("State 2 dev->field_changes = %d\n", dev->field_changes); new_field = hrt_get_field(dev); if (new_field != dev->field_id){ dev->field_id = new_field; if(dev->field_changes > 0) dev->field_changes--; if (dev->field_changes == 0){ dev->streaming_state = 0; streaming_fieldchange_color(dev); } } } } else { /* Streaming + Grey scale mode * * This one not as complicated as the color function * Just read the current available field * whenever there is a field change */ if (new_field != dev->field_id) { dev->field_id = new_field; streaming_fieldchange(dev); } } } else { /* Not streaming. * We are in the read mode * update the reader and let them know that another * field is available. The reader always waits till we have read * two fields. Thus he/she/it(?) needs to be notified * when we read two fields */ if (new_field != dev->field_id) { dev->field_id = new_field; if (dev->field_changes >0) dev->field_changes--; wake_up_interruptible(&dev->wait_queue); } } } else { /* CASE 2 - IRQs ENABLES. AN INTERRUPT RAISED WHENEVER THE FIELD BIT CHANGES */ if (dev->streaming) { /* If we have streaming running, then call the function * responsible for updating the streaming display * * Differant implementations of streaming * for color and grey scale follow */ if ((dev->mode & HRT_COLOR_MODE) || !(dev->mode & HRT_DUAL_PORTED_MODE)){ /* if we are in the color mode, we follow differant states * * State 0 * * device goes live * state changes to 1 * * State 1 * * detect 2 field changes * issue freeze_next command * move to state 2 * * State 2 * * detect 2 field changes * call the streaming_framechange function * to read the current frame * move back to state 0 */ if (dev->streaming_state == 0){ hrt_printk("State 0\n"); if(dev->is_frozen){ hrt_printk("HRT_GO_LIVE\n"); hrt_go_live(dev); } else hrt_printk ("THIS IS AN ERROR\n"); dev->streaming_state = 1; dev->field_changes = 2; } else if (dev->streaming_state == 1){ hrt_printk("State 1 dev->field_changes = %d\n", dev->field_changes); new_field = hrt_get_field(dev); if (new_field != dev->field_id){ dev->field_id = new_field; if(dev->field_changes > 0) dev->field_changes--; if (dev->field_changes == 0){ dev->streaming_state = 2; dev->field_changes = 2; hrt_printk("HRT_FREEZE_NEXT\n"); hrt_freeze_next(dev); } } } else if (dev->streaming_state == 2){ hrt_printk("State 2 dev->field_changes = %d\n", dev->field_changes); new_field = hrt_get_field(dev); if (new_field != dev->field_id){ dev->field_id = new_field; streaming_fieldchange_color(dev); } } } else { /* Streaming + Grey scale mode * * This one not as complicated as the color function * Just read the current available field * whenever there is a field change */ dev->field_id = new_field; streaming_fieldchange_irq(dev); } } else { /* Not streaming. * We are in the read mode * update the reader and let them know that another * field is available. The reader always waits till we have read * two fields. Thus he/she/it(?) needs to be notified * when we read two fields */ dev->field_id = new_field; if (dev->field_changes > 0) dev->field_changes--; wake_up_interruptible(&dev->wait_queue); } } done: hrt_unlock(dev); } /******************************************************************** * per_file_init * * allocates and initializes per-file data, which * is currently just the ROI (region-of-interest) subwindow * and a pointer back to this hrtdev */ struct hrt_per_file *per_file_init(hrt_t *hrtdev) { struct hrt_per_file *per_file = vmalloc(sizeof(*per_file)); per_file->hrtdev = hrtdev; per_file->win.height = HRT_HEIGHT; if (hrtdev->mode & HRT_COLOR_MODE) per_file->win.width = HRT_COLOR_WIDTH; else per_file->win.width = HRT_GRAY_WIDTH; per_file->win.startx = 0; per_file->win.starty = 0; return per_file; } /********************************************************************/ /** * hrt_open * * checks minor number range, sets private_data, * starts timer if necessary, increments users */ int hrt_open(struct inode *inode, struct file *file) { unsigned int minor; int result; /* video device structure required to provide video4linux support */ struct video_device *vfl = video_devdata(file); hrt_t *dev = vfl->priv; result = 0; minor = MINOR(inode->i_rdev); if (minor >= HRT_MAX_DEVICES) return -ENODEV; dev = hrt_devices + minor; HRT_DEBUG_MSG(1, "opening device %d at addr %lX", minor, (unsigned long)dev->phys_addr); /* Cannot open if device is not in closed state */ if (dev->state != HRT_CLOSED_STATE) { HRT_DEBUG_MSG(2, "already open"); return -EBUSY; } /* Now we set the private data for the device file * this is in the form of a structure hrt_per_file * that consists of a hrt_t struct and a subwindow struct */ HRT_DEBUG_MSG(2, "setting private data"); file->private_data = per_file_init(dev); /* Set device state to OPEN */ dev->state = HRT_OPEN_STATE; /* The device opens in the non streaming mode by default */ dev->streaming = 0; /* Added 13 June 2005 */ dev->streaming_state = 0; /* Read the field bit and assign to the field_id */ dev->field_id = hrt_get_field(dev); /* Activate the timer */ HRT_DEBUG_MSG(2, "installing timer/irq"); result = hrt_timer_activate(dev); return result; } /********************************************************************/ /* hrt_release * * hrt_release should not need any lock, because it is only called by the system, on last close; at that point there should be no possibility of more calls that touch this device, unless they are from a timer or interrrupt handler */ int hrt_release(struct inode *inode, struct file *file) { struct video_device *vfl = video_devdata(file); hrt_t *dev = vfl->priv; hrt_printk( " hrt_release\n"); if (dev->is_locked) { hrt_unlock(dev); } if (dev->state != HRT_OPEN_STATE) return -ENODEV; dev->state = HRT_CLOSED_STATE; hrt_timer_deactivate(dev); /* Free our private data */ vfree(file->private_data); return 0; } /********************************************************************/ /* hrt_poll * * handles poll() and select() on /dev/video */ unsigned int hrt_poll(struct file *file, poll_table *wait) { struct hrt_per_file *per_file = file->private_data; hrt_t *hrtdev = per_file->hrtdev; if (!hrtdev->streaming) { poll_wait(file, &hrtdev->wait_queue, wait); } else { /* Go to sleep if the done list is empty */ while (!hrtdev->done_list.length) { up(&hrtdev->sem); if (wait_event_interruptible (hrtdev->wait_queue, (hrtdev->done_list.length))) return -ERESTARTSYS; if (down_interruptible(&hrtdev->sem)) return -ERESTARTSYS; } } return POLLIN | POLLRDNORM; } /******************************************************************** * hrt_read * * Does a blocking read and return count number of bytes back to the user * Differant implementations for color/grey scale/single ported/dual ported */ int hrt_read(struct file *file, char *buf, size_t count, loff_t * ppos) { int max_count; /* atulya/sean - to measure the frame rate * record the jiffies at this time * then find the jiffies at the time the function returns * the differance gives the time taken to read one frame * and thus the frame rate */ unsigned int starttime; int old_field; struct video_device *vfl = video_devdata(file); hrt_t *dev = vfl->priv; HRT_DEBUG_MSG(3, "hrt_read count = %d", count); if (dev->state != HRT_OPEN_STATE) return -ENODEV; //hrt_printk( "hrt_read: (start) jiffies = %ld\n", jiffies); starttime = jiffies; if (dev->mode & HRT_COLOR_MODE) { /* First implementation - COLOR device * * These ones are single ported so * need to wait till the entire field gets digitized before we can read it * This is a conservative approach..and wastes an extra frame just to make * sure that we get the correct frame boundary. * Can be tweaked to make it faster */ hrt_lock(dev); dev->field_changes = 2; if (dev->is_frozen) { hrt_go_live (dev); hrt_unlock(dev); /* Go live * and * wait for two field changes */ if (wait_event_interruptible(dev->wait_queue, dev->field_changes == 0)) { return -ERESTARTSYS; } hrt_lock(dev); dev->field_changes = 2; } hrt_freeze_next(dev); hrt_unlock(dev); /* Now we know that we are at a frame bondary * Wait for 2 field changes * and at that time we can read both fields as one frame */ if (wait_event_interruptible(dev->wait_queue, dev->field_changes == 0)) { return -ERESTARTSYS; } /* Cannot return more bytes than what the device sends to us * Check for that */ max_count = dev->win->width * dev->win->height * dev->bytes_per_pixel; if (count > max_count) count = max_count; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; old_field = dev->field_id; /* Grab a field */ grab_field(dev, dev->framedata, old_field); printk(KERN_ALERT "\nDEV FIELD IS %d \n", old_field); /* Grab the other field */ grab_field(dev, dev->framedata,(!old_field) ); } else { /* Implementation 2 - GREY SCALE DULA ported device * * These devices are dual ported so we can * read at a faster rate * Dont need the extra checking required in the * case of single ported devices. * Results in a faster rate * * Just grab one field, * wait for a field change * and grab the other field */ if (dev->mode & HRT_DUAL_PORTED_MODE) { max_count = dev->win->width * dev->win->height * dev->bytes_per_pixel; if (count > max_count) count = max_count; down_interruptible(&dev->sem); old_field = dev->field_id; /* Grab a field */ grab_field(dev, dev->framedata, old_field); /* Go to sleep until the field bit changes */ while (old_field == dev->field_id) { up(&dev->sem); if (file->f_flags & O_NONBLOCK) return -EAGAIN; if (wait_event_interruptible (dev->wait_queue, (old_field != dev->field_id))) return -ERESTARTSYS; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; } /* Grab the other field */ grab_field(dev, dev->framedata, dev->field_id); } else { /* Imlementation 3 - GREYSCALE SINGLE ported device * * Again similar implementation as the color devices */ hrt_lock(dev); dev->field_changes = 2; if (dev->is_frozen) { hrt_go_live (dev); hrt_unlock(dev); if (wait_event_interruptible(dev->wait_queue, dev->field_changes == 0)) { return -ERESTARTSYS; } hrt_lock(dev); dev->field_changes = 2; } hrt_freeze_next(dev); hrt_unlock(dev); if (wait_event_interruptible(dev->wait_queue, dev->field_changes == 0)) { return -ERESTARTSYS; } max_count = dev->win->width * dev->win->height * dev->bytes_per_pixel; if (count > max_count) count = max_count; if (down_interruptible(&dev->sem)) return -ERESTARTSYS; old_field = dev->field_id; /* Grab a field */ grab_field(dev, dev->framedata, old_field); /* Grab the other field */ grab_field(dev, dev->framedata,(!old_field) ); } } /* Both returned fields have been stored in their corresponding * location inside the dev->framedata buffer * Return to the user */ if (copy_to_user(buf, dev->framedata, count)) { up(&dev->sem); return -EFAULT; } /* Clear the buffer */ if (dev->mode & HRT_COLOR_MODE) memset(dev->framedata, 0, HRT_COLOR_FRAMESIZE); else memset(dev->framedata, 0, HRT_GRAY_FRAMESIZE); up(&dev->sem); /* (1/ (time taken to read one frame)) = frame rate */ hrt_printk( " Frame rate = %d\n", (int)( HZ / (jiffies-starttime) ) ); return count; } /******************************************************************** * * v4l1_ioctls - used for printing out ioctl codes in hrt_do_ioctl */ 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" }; /******************************************************************** * * hrt_do_ioctl - handles ioctl's on /dev/video */ static int hrt_do_ioctl(struct inode *inode, struct file *file, unsigned int cmd, void *arg) { struct hrt_per_file *per_file = file->private_data; hrt_t *hrtdev = per_file->hrtdev; hrtdev->win = &per_file->win; /* This nice code copied from xawtv's libng. It prints out the ioctl's that are done. */ 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) { case IOC_HRT_FREEZE_FRAME: HRT_DEBUG_MSG(2, "IOC_HRT_FREEZE_FRAME: called"); hrt_freeze_next(hrtdev); break; case IOC_HRT_GO_LIVE: HRT_DEBUG_MSG(2, "IOC_HRT_GO_LIVE: called"); hrt_go_live(hrtdev); break; case IOC_HRT_UPSIDE_DOWN: HRT_DEBUG_MSG(2, "IOC_HRT_UPSIDE_DOWN: called"); if (hrtdev->upside_down) hrtdev->upside_down = 0; else hrtdev->upside_down = 1; break; /* Streaming ioctl's */ case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; if (hrtdev->stream_buffers_mapped) { hrt_printk("REQBUFS - Can't request buffers if " " buffers are already mapped\n"); return -EPERM; } if (!mmap_request_buffers(hrtdev, req)) { hrt_printk("REQBUFS - Request of buffers failed\n"); return -EINVAL; } return 0; } case VIDIOC_QUERYBUF: { struct v4l2_buffer *buf = (struct v4l2_buffer *) arg; int i; i = buf->index; if (i < 0 || i >= MAX_CAPTURE_BUFFERS || !hrtdev->stream_buf[i].requested || (buf->type & V4L2_BUF_TYPE_VIDEO_CAPTURE) != (hrtdev->stream_buf[i].vidbuf.type & V4L2_BUF_TYPE_VIDEO_CAPTURE)) { hrt_printk("QUERYBUF - bad parameter\n"); return -EINVAL; } *buf = hrtdev->stream_buf[i].vidbuf; return 0; } case VIDIOC_QBUF: { struct v4l2_buffer *buf = (struct v4l2_buffer *) arg; if (!hrtdev->stream_buffers_mapped) { return -EINVAL; } if (!hrt_queuebuffer(hrtdev, buf)) { return -EINVAL; } return 0; } case VIDIOC_DQBUF: { struct v4l2_buffer *buf = (struct v4l2_buffer *) arg; if (!hrt_dequeuebuffer(hrtdev, buf)) { return -EINVAL; } return 0; } case VIDIOC_STREAMON: { __u32 *type = (__u32 *) arg; if (!hrt_streamon(hrtdev, *type)) return -EINVAL; return 0; } case VIDIOC_STREAMOFF: { __u32 *type = (__u32 *) arg; hrt_streamoff(hrtdev, *type); return 0; } /* Non-streaming ioctl's */ 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)); /*sprintf(b->bus_info,"PCI:%s", hrtdev->pci_dev->slot_name);*/ b->version = KERNEL_VERSION(0, 0, 2); if (disable_streaming) { b->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; } else { b->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; } return 0; } case VIDIOC_ENUM_FMT: { struct v4l2_fmtdesc *f = arg; enum v4l2_buf_type type = f->type; int index = f->index; /* 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; if(hrtdev->mode & HRT_COLOR_MODE){ f->pixelformat = V4L2_PIX_FMT_RGB555; //check this one strcpy(f->description, "Color"); } else{ 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; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { /* memset(&f->fmt.pix, 0, sizeof(struct v4l2_pix_format));*/ f->fmt.pix.width = hrtdev->win->width; f->fmt.pix.height = hrtdev->win->height; if(hrtdev->mode & HRT_COLOR_MODE) f->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB555; else f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; //f->fmt.pix.pixelformat = V4L2_FIELD_ANY; f->fmt.pix.bytesperline = hrtdev->bytesperline; f->fmt.pix.sizeimage = hrtdev->framesize; hrt_printk("pix.width (%d), pic.height (%d)", f->fmt.pix.width, f->fmt.pix.height); hrt_printk("pix.bytesperline (%d), pic.sizeimage (%d)", f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); return 0; } else { return -EINVAL; } } case VIDIOC_G_CROP: { struct v4l2_crop * win = arg; win->c.height = hrtdev->win->height; win->c.width = hrtdev->win->width; win->c.top = hrtdev->win->starty; win->c.left = hrtdev->win->startx; return 0; } case VIDIOC_S_CROP: { struct v4l2_crop *win = arg; hrtdev->win->height = win->c.height; hrtdev->win->width = win->c.width; hrtdev->win->starty = win->c.top; hrtdev->win->startx = win->c.left; return 0; } case VIDIOC_G_STD: { v4l2_std_id *id = arg; *id = V4L2_STD_NTSC; return 0; } case VIDIOC_S_STD: { /* There's only one standard that we support, NTSC */ return 0; } case VIDIOC_QUERYCTRL: { struct v4l2_queryctrl *c = arg; int i; for (i = 0; i < ARRAY_SIZE(hrt_ctls); i++) if (hrt_ctls[i].id == c->id) break; /* Didn't find the id */ if (i == ARRAY_SIZE(hrt_ctls)) { return -EINVAL; } *c = hrt_ctls[i]; return 0; } case VIDIOC_G_CTRL: { struct v4l2_control *c = arg; int i; for (i = 0; i < ARRAY_SIZE(hrt_ctls); i++) if (hrt_ctls[i].id == c->id) break; if (i == sizeof(hrt_ctls)) return -EINVAL; if (c->id == V4L2_CID_BRIGHTNESS) { /* c->value = hrtdev->regvals[HRT_BRIGHTNESS_REG]; */ c->value = hrtdev->saa7110_registers[HRT_BRIGHTNESS_REG]; } else if (c->id == V4L2_CID_CONTRAST) { /* c->value = hrtdev->regvals[HRT_CONTRAST_REG]; */ c->value = hrtdev->saa7110_registers[HRT_BRIGHTNESS_REG]; } else { return -EINVAL; } return 0; } case VIDIOC_S_CTRL: { /* struct v4l2_control *c = arg; int i; for (i = 0; i < sizeof(hrt_ctls); i++) if (hrt_ctls[i].id == c->id) break; if (i == sizeof(hrt_ctls)) return -EINVAL; if (c->id == V4L2_CID_BRIGHTNESS) { if (c->value > 255) return -EINVAL; c->value = i2c_set_reg(hrtdev, HRT_BRIGHTNESS_REG, c->value); } else if (c->id == V4L2_CID_CONTRAST) { if (c->value > 255) return -EINVAL; c->value = i2c_set_reg(hrtdev, HRT_CONTRAST_REG, c->value); } else { return -EINVAL; } */ return 0; } case VIDIOC_ENUMINPUT: { struct v4l2_input *i = arg; unsigned int 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, "NTSC 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; e->id = V4L2_STD_NTSC_M; e->index = index; strcpy(e->name, "NTSC"); e->frameperiod = fract; e->framelines = hrtdev->win->height; return 0; } case VIDIOC_G_PARM: { struct v4l2_streamparm *parm = arg; struct v4l2_standard s; 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: { return 0; } case VIDIOC_S_INPUT: { return 0; } default: return -ENOIOCTLCMD; } return 0; } /******************************************************************** * hrt_ioctl * * handler for ioctl's on /dev/video. Tries the private * ioctl's first, via hrt_do_private_ioctl(). */ int hrt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg) { struct hrt_per_file *per_file = file->private_data; hrt_t *hrtdev = per_file->hrtdev; int ret; hrtdev->win = &per_file->win; dprintk("ioctl arg = %lx\n", arg); down(&hrtdev->sem); ret = hrt_do_private_ioctl(hrtdev, cmd, arg); if (ret != -EINVAL) { up(&hrtdev->sem); return ret; } ret = video_usercopy(inode, file, cmd, arg, hrt_do_ioctl); up(&hrtdev->sem); return ret; } #ifdef CONFIG_PCI /*************** * PCI support * ***************/ /********************************************************************/ MODULE_DEVICE_TABLE(pci, hrt_pci_tbl); int hrt_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id); void hrt_pci_remove (struct pci_dev *pci_dev); /********************************************************************/ static struct pci_device_id hrt_pci_tbl[] __devinitdata = { {PCI_DEVICE(HRT_VENDOR_ID, HRT_DEVICE_ID_GREY)}, {PCI_DEVICE(HRT_VENDOR_ID, HRT_DEVICE_ID_COLOR)}, {0,} }; /********************************************************************/ static struct pci_driver hrt_pci_driver = { .name = "hrt", .id_table = hrt_pci_tbl, .probe = hrt_pci_probe, .remove = __devexit_p(hrt_pci_remove) }; /********************************************************************/ int hrt_pci_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id) { printk(KERN_ALERT "******I am in PCI Probe ********\n"); HRT_DEBUG_MSG(2, "probing pci device"); /* check that this is a type of device we support */ if ((pci_dev->device != HRT_DEVICE_ID_COLOR) && (pci_dev->device != HRT_DEVICE_ID_GREY)) { HRT_ERROR_MSG("unknown device type 0x%hx", pci_dev->device); return -ENODEV; } if (hrt_num_devices >= HRT_MAX_DEVICES) { HRT_ERROR_MSG("need to increase HRT_MAX_DEVICES"); return -ENOMEM; } return hrt_init(pci_resource_start(pci_dev, 0), pci_dev); } /********************************************************************/ /* since these cards are not intended to be hot-pluggable, we do not try to reuse hrt_t objects; we just use the remove operation for removal of the device driver module */ void hrt_pci_remove (struct pci_dev *pci_dev) { hrt_t *dev; HRT_DEBUG_MSG(2, "hrt_pci_remove"); dev = (hrt_t *) pci_get_drvdata (pci_dev); hrt_cleanup(dev); /* Added by Sean/ Atulya 2005.06.14 video_unregister_device(&dev->video_dev); now moved to hrt_cleanup */ pci_disable_device(pci_dev); } /********************************************************************/ void hrt_pci_init(void) { HRT_DEBUG_MSG(2, "pci_register_driver"); pci_register_driver(&hrt_pci_driver); } /********************************************************************/ void hrt_pci_cleanup(void) { hrt_printk( " hrt_pci_cleanup\n"); HRT_DEBUG_MSG(2, "pci_unregister_driver"); pci_unregister_driver(&hrt_pci_driver); } #else /* no PCI support */ /********************************************************************/ void hrt_pci_init(void) { } /********************************************************************/ void hrt_pci_cleanup(void) { hrt_printk( " no pci support hrt_pci_cleanup\n"); } #endif /************************************* * module initialization and cleanup * *************************************/ int hrt_major_number = 0; /********************************************************************/ void hrt_cleanup_module(void) { hrt_printk( " initiating hrt_cleanup_module\n"); HRT_MODULE_CHECK (HRT_MODULE_INITIALIZING_STATE | HRT_MODULE_INITIALIZED_STATE, "6"); hrt_module_state = HRT_MODULE_FINALIZING_STATE; HRT_DEBUG_MSG(2, "hrt_cleanup_module"); hrt_printk( " calling hrt_isa_cleanup\n"); hrt_isa_cleanup(); hrt_printk( " calling hrt_pci_cleanup\n"); hrt_pci_cleanup(); hrt_printk( " unregistering the 'hrt' device\n"); if (hrt_major_number) unregister_chrdev(hrt_major_number, "hrt"); hrt_printk( " calling hrt_debug_cleanup\n"); hrt_debug_cleanup(); hrt_module_state = HRT_MODULE_UNINITIALIZED_STATE; hrt_printk( " cleanup completed\n"); } /********************************************************************/ int hrt_init_module(void) { int result = 0; hrt_module_state = HRT_MODULE_INITIALIZING_STATE; hrt_debug_init(); printk(KERN_ALERT "DAN ->hrt_mdule_state is %d\n", hrt_module_state); /* Added 2005.06.11 Sean/ Atulya begin */ memset(hrt_devices, 0, sizeof(hrt_devices)); /* use major number returned by system if no major number is specified by this module */ if (major_number == 0) hrt_major_number = result; else hrt_major_number = major_number; HRT_ERROR_MSG("module initializing with major number %d", hrt_major_number); /* detect ISA/PC104 devices, and PCI devices that are jumpered to use the ISA/PC104 I/O address space */ hrt_isa_init(); testing_isa_mode = 0; printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "------------ENTERING PCI MODE!!!!--------------\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); printk(KERN_ALERT "\n"); hrt_pci_init(); /* now detect regular PCI devices */ if (hrt_num_devices == 0) { HRT_ERROR_MSG("no hrt devices detected"); hrt_module_state = HRT_MODULE_UNINITIALIZED_STATE; return -ENODEV; } else { HRT_ERROR_MSG("found %d hrt devices", hrt_num_devices); } hrt_module_state = HRT_MODULE_INITIALIZED_STATE; printk(KERN_ALERT "DAN ->hrt_mdule_state is %d\n", hrt_module_state); /* I am done probling change global state */ cleanup_context = 1; return 0; } /********************************************************************/ /** * hrt_vma_open - increments reference count */ static void hrt_vma_open(struct vm_area_struct *vma) { struct video_device *vdev = video_devdata(vma->vm_file); hrt_t *hrtdev = vdev->priv; struct stream_buffer *buf; hrt_printk( "hrt_vma_open\n"); if (hrtdev == NULL) return; buf = mmap_stream_buffer_from_offset(hrtdev, vma->vm_pgoff); if (buf == NULL) return; else { hrt_printk("hrt_vma_open : buf allocated at (0x%lx)", (unsigned long)buf); } buf->vma_refcount++; } /********************************************************************/ /** * hrt_vma_close - decrements reference count of a stream buffer */ static void hrt_vma_close(struct vm_area_struct *vma) { struct video_device *vdev = video_devdata(vma->vm_file); hrt_t *hrtdev = vdev->priv; struct stream_buffer *buf; if (hrtdev == NULL) return; buf = mmap_stream_buffer_from_offset(hrtdev, vma->vm_pgoff); buf->vma_refcount--; if (buf->vma_refcount > 0) { return; } if (hrtdev->streaming) { hrt_printk(KERN_WARNING "munmap() called while streaming\n"); hrt_streamoff(hrtdev, buf->vidbuf.type); } if (buf->vaddress != NULL) { vfree(buf->vaddress); buf->vaddress = NULL; } buf->vidbuf.flags = 0; if (hrtdev->stream_buffers_mapped > 0) hrtdev->stream_buffers_mapped--; } /********************************************************************/ /** * hrt_vma_nopage - calls kvirt_to_pa to convert the vaddress in the * stream buffer (indexed via the offset parameter to mmap()) to * a page number for access from userspace */ struct page *hrt_vma_nopage(struct vm_area_struct *vma, unsigned long address, int * type) { struct video_device *vdev = video_devdata(vma->vm_file); hrt_t *hrtdev = vdev->priv; struct stream_buffer *buf; unsigned long offset_into_buf; struct page *page; buf = mmap_stream_buffer_from_offset(hrtdev, vma->vm_pgoff); if (buf == NULL) return 0; offset_into_buf = address - vma->vm_start; if (offset_into_buf >= buf->vidbuf.length) { hrt_printk("Attempt to read past end of mmap() buffer\n"); return 0; } page = vmalloc_to_page(buf->vaddress + offset_into_buf); if (page != NULL) atomic_inc(&page->_count); return page; } /********************************************************************/ /** * hrt_vm_ops - vm operations on /dev/video */ struct vm_operations_struct hrt_vm_ops = { .open = hrt_vma_open, .close = hrt_vma_close, .nopage = hrt_vma_nopage, }; /********************************************************************/ int hrt_queuebuffer( hrt_t *hrtdev, struct v4l2_buffer *vidbuf) { int i = vidbuf->index; struct stream_buffer *buf = NULL; if ( !(hrtdev->stream_buffers_mapped) ) { return 0; } if (vidbuf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { hrt_printk("QBUF - wrong type (type = %x)\n", vidbuf->type); return 0; } if (i < 0 || i >= MAX_CAPTURE_BUFFERS || !hrtdev->stream_buf[i].requested) { hrt_printk("QBUF - buffer index %d is out of range\n", i); return 0; } buf = &hrtdev->stream_buf[i]; hrt_printk("hrt_queuebuffer : buf (0x%lx)", (unsigned long)buf); if (!(buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED)) { hrt_printk("QBUF - buffer %d is not mapped\n", i); return 0; } if ((buf->vidbuf.flags & V4L2_BUF_FLAG_QUEUED)) { hrt_printk("QBUF - buffer %d is already queued\n", i); return 0; } buf->vidbuf.flags &= ~V4L2_BUF_FLAG_DONE; do_gettimeofday(&buf->vidbuf.timestamp); queue_add_tail(buf, &hrtdev->capture_list); buf->vidbuf.flags |= V4L2_BUF_FLAG_QUEUED; return 1; } /********************************************************************/ /** * hrt_dequeuebuffer - called by the DQBUF ioctl. * Returns a buffer from the done queue. */ int hrt_dequeuebuffer(hrt_t *hrtdev, struct v4l2_buffer *buf) { struct stream_buffer *newbuf; if (buf->type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { hrt_printk("DQBUF - Wrong buffer type\n"); return 0; } newbuf = queue_del_head(&hrtdev->done_list); if (newbuf == NULL) { hrt_printk("DQBUF - No buffers on done queue\n"); return 0; } newbuf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; *buf = newbuf->vidbuf; return 1; } /********************************************************************/ /** * mmap_request_buffers - called by the ioctl REQBUF to request buffers */ int mmap_request_buffers(hrt_t *dev, struct v4l2_requestbuffers *req) { int i; u32 buflen; u32 type; 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 = (dev->framesize + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); hrt_printk("Granting %d buffers, buflen (%d)\n", req->count, buflen); /* 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.m.offset = PAGE_SIZE * (i + 1); dev->stream_buf[i].vidbuf.length = buflen; dev->stream_buf[i].vidbuf.bytesused = 0; dev->stream_buf[i].vidbuf.flags = 0; /* XXX: should set timestamp? */ dev->stream_buf[i].vidbuf.sequence = 0; dev->stream_buf[i].vma_refcount = 0; dev->stream_buf[i].vaddress = NULL; 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; } /********************************************************************/ /** * hrt_mmap - called by the mmap() system call on /dev/video */ int hrt_mmap(struct file *file, struct vm_area_struct *vma) { struct video_device *dev = video_devdata(file); hrt_t *hrtdev = dev->priv; struct stream_buffer *buf = NULL; down(&hrtdev->sem); hrt_printk("hrt_mmap\n"); if (next_mmap_is_direct) { int ret; next_mmap_is_direct = 0; ret = hrt_direct_mmap(file, vma); up(&hrtdev->sem); return ret; } buf = mmap_stream_buffer_from_offset(hrtdev, vma->vm_pgoff); if (buf == NULL) { hrt_printk("mmap() - Invalid offset parameter\n"); up(&hrtdev->sem); return -EINVAL; } if (buf->vidbuf.length != vma->vm_end - vma->vm_start) { hrt_printk("mmap() - Bad length parameter\n"); up(&hrtdev->sem); return -EINVAL; } if (!buf->requested) { hrt_printk("mmap() - Buffer is not available for mapping\n"); up(&hrtdev->sem); return -EINVAL; } if (buf->vidbuf.flags & V4L2_BUF_FLAG_MAPPED) { hrt_printk("mmap() - Buffer is already mapped\n"); up(&hrtdev->sem); return -EINVAL; } if (buf->vaddress != NULL) vfree(buf->vaddress); /* * Allocate virtual memory. This memory is mapped to the * buffer in user space. */ buf->vaddress = vmalloc(buf->vidbuf.length); if (buf->vaddress == NULL) { hrt_printk("Could not allocate mmap() buffer\n"); up(&hrtdev->sem); return -ENODEV; } else { hrt_printk("Successfully allocated mmap() buffer\n"); } buf->vidbuf.flags |= V4L2_BUF_FLAG_MAPPED; hrtdev->stream_buffers_mapped++; vma->vm_ops = &hrt_vm_ops; if (vma->vm_ops->open) vma->vm_ops->open(vma); up(&hrtdev->sem); return 0; } /********************************************************************/ /** * hrt_streamon - called by the STREAMON ioctl. */ int hrt_streamon(hrt_t *hrtdev, __u32 type) { struct stream_buffer *buf; if (type != V4L2_BUF_TYPE_VIDEO_CAPTURE) { printk("STREAMON - Wrong buffer type\n"); return 0; } /* Move any leftover done buffers to the free pool */ while ((buf = queue_del_head(&hrtdev->done_list))) buf->vidbuf.flags &= ~V4L2_BUF_FLAG_QUEUED; hrtdev->streaming = 1; hrt_printk("Streaming is turned on\n"); return 1; } /********************************************************************/ /*MODULE_LICENSE("GPL");*/ module_init(hrt_init_module); module_exit(hrt_cleanup_module);