/* * hrt.c * * last updated - 7 July 2008 * * Peng Chen/ Chi Zhang/ Dawei Su This is a device driver for the PixelSmart512-8 (greyscale) and Video Gala (color) framegrabbers. 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 Peng Chen, Chi Zhang, Dawei Su, in the summer of 2008. This driver carries forward the work done by other teams in the last 4 years by Jie Chen, Ke Chen, Sai, 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.25 Linux Kernel based on Video for Linux 2 std 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 #include /* udelay */ #include #include #include #include #include #include #include "hrt.h" -//#include -//#include /* error codes */ -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include -//#include /*#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 static int testing_isa_mode = 1; static int major_number = 82; hrt_parm(major_number, "i", int, 0); static unsigned int vid_limit = 16; module_param(vid_limit,int,0644); MODULE_PARM_DESC(vid_limit,"capture memory limit in megabytes"); /* Minor number, or -1 for first free */ static int video_minor = -1; static int video_nr = -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("Peng Chen, Chi Zhang, Dawei Su, Ted Baker, 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"); /* values for field hrt_dev->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_dev->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 #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 hrt_fmt { char *name; u32 fourcc; /* v4l2 format id */ int depth; }; static struct hrt_fmt formats[] = { { .name = "15 bpp RGB packed, le", .fourcc = V4L2_PIX_FMT_RGB555, .depth = 16, }, { .name = "8 bpp, grey packed", .fourcc = V4L2_PIX_FMT_GREY, .depth = 8, } }; /* buffer for one video frame */ struct hrt_buffer { /* common v4l buffer stuff -- must be first */ struct videobuf_buffer vb; struct hrt_fmt *fmt; unsigned int fields_grabbed; }; /********************************************************************/ /** * HRT device descriptor */ static LIST_HEAD(hrt_devlist); struct hrt_dev { struct list_head hrt_devlist; int users; /* see how many readers */ struct mutex lock; struct list_head capture; /* link all video capture buffers */ int registered; int num; int state; unsigned long virt_addr; unsigned long phys_addr; spinlock_t spinlock; int is_locked; wait_queue_head_t wait_queue; struct timer_list timer; struct tasklet_struct tasklet; int timer_active; /* timer_active != 0 iff timer or irq is active */ int tasklet_active; int irq; /* irq != 0 if board supports interrupts, < 0 if handler not installed > 0 if handler is installed */ volatile int irq_count; volatile int i2c_bits; /* last values written to i2c */ char saa7110_registers[HRT_SAA7110_MAXREG+1]; int mode; int rows, cols, bytes_per_pixel; int win_row, win_col, win_width, win_height; int field; /* current field being digitized */ int field_changes; /* how many field changes we are waiting for */ int is_frozen; /* the device is either frozen or being frozen */ unsigned int field_id; /* tracking the field being read */ unsigned char *localbuf; /* storing the frame currently read from the device */ 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; /* 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 }; /******************************************************************** *************** basic file operations (fops) ******************** */ int hrt_open (struct inode *inode, struct file *file); int hrt_close (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(struct hrt_dev *hrtdev, unsigned int cmd, unsigned long arg); /********************************************************************/ static struct file_operations hrt_fops = { .owner = THIS_MODULE, .open = hrt_open, .release = hrt_close, .read = hrt_read, .ioctl = hrt_ioctl, .mmap = hrt_mmap, .llseek = no_llseek, .poll = hrt_poll, }; static void grabber_release(struct video_device *vfd){ } /** * struct hrt_fh - contains data to be stored per file handle * * Private data for a device is not just the hrt_dev structure * but also the subwindow structure that represents the subwindow * of the entire frame being displayed */ struct hrt_fh { struct subwindow win; struct hrt_dev *dev; struct hrt_fmt *fmt; struct videobuf_queue vb_vidq; enum v4l2_buf_type type; }; /********************************************************************/ static int hrt_reqbufs(struct file *file, void *priv, struct v4l2_requestbuffers *p) { struct hrt_fh *fh = (struct hrt_fh*)priv; return videobuf_reqbufs(&fh->vb_vidq, p); } static int hrt_try_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { return 0; } static int hrt_querybuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct hrt_fh *fh = (struct hrt_fh*)priv; return videobuf_querybuf(&fh->vb_vidq, p); } static int hrt_qbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct hrt_fh *fh = (struct hrt_fh*)priv; return videobuf_qbuf(&fh->vb_vidq, p); } static int hrt_dqbuf(struct file *file, void *priv, struct v4l2_buffer *p) { struct hrt_fh *fh = (struct hrt_fh*)priv; return videobuf_dqbuf(&fh->vb_vidq, p, file->f_flags & O_NONBLOCK); } static int hrt_streamon(struct file *file, void *priv, enum v4l2_buf_type i) { struct hrt_fh *fh = (struct hrt_fh*)priv; return videobuf_streamon(&fh->vb_vidq); } static int hrt_streamoff(struct file *file, void *priv, enum v4l2_buf_type i) { struct hrt_fh *fh = (struct hrt_fh*)priv; return videobuf_streamoff(&fh->vb_vidq); } static int hrt_querycap(struct file *file, void *priv, struct v4l2_capability *cap) { strcpy(cap->driver, "hrt"); strncpy(cap->card, "PS 512-8-PCI", sizeof(cap->card)); cap->version = KERNEL_VERSION(0, 0, 2); if (disable_streaming) { cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE; } else { cap->capabilities = V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_READWRITE | V4L2_CAP_STREAMING; } return 0; } static int hrt_enum_fmt_cap(struct file *file, void *priv, struct v4l2_fmtdesc *f) { struct hrt_fh *fh = (struct hrt_fh*)priv; struct hrt_dev *dev = fh->dev; int index=f->index; enum v4l2_buf_type type = f->type; memset(f, 0, sizeof(*f)); f->index = index; f->type = type; if(dev->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; } static int hrt_s_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { return 0; } static int hrt_g_fmt_cap(struct file *file, void *priv, struct v4l2_format *f) { struct hrt_fh *fh = (struct hrt_fh*)priv; struct hrt_dev *dev = fh->dev; if (f->type == V4L2_BUF_TYPE_VIDEO_CAPTURE) { if(dev->mode & HRT_COLOR_MODE) f->fmt.pix.pixelformat = V4L2_PIX_FMT_RGB555; else f->fmt.pix.pixelformat = V4L2_PIX_FMT_GREY; mutex_lock(&dev->lock); f->fmt.pix.width = dev->win->width; f->fmt.pix.height = dev->win->height; f->fmt.pix.bytesperline = dev->bytesperline; f->fmt.pix.sizeimage = dev->framesize; mutex_unlock(&dev->lock); printk("pix.width (%d), pic.height (%d)", f->fmt.pix.width, f->fmt.pix.height); printk("pix.bytesperline (%d), pic.sizeimage (%d)", f->fmt.pix.bytesperline, f->fmt.pix.sizeimage); return 0; } else { return -EINVAL; } } static int hrt_g_crop(struct file *file, void *priv, struct v4l2_crop *win) { struct hrt_fh *fh = (struct hrt_fh*)priv; struct hrt_dev *dev = fh->dev; mutex_lock(&dev->lock); win->c.height = dev->win->height; win->c.width = dev->win->width; win->c.top = dev->win->starty; win->c.left = dev->win->startx; mutex_unlock(&dev->lock); return 0; } static int hrt_s_crop(struct file *file, void *priv, struct v4l2_crop *win) { struct hrt_fh *fh = (struct hrt_fh*)priv; struct hrt_dev *dev = fh->dev; mutex_lock(&dev->lock); dev->win->height = win->c.height; dev->win->width = win->c.width; dev->win->starty = win->c.top; dev->win->startx = win->c.left; mutex_unlock(&dev->lock); return 0; } static int hrt_queryctrl(struct file *file, void *priv, struct v4l2_queryctrl *c) { struct hrt_fh *fh = (struct hrt_fh*)priv; struct hrt_dev *dev = fh->dev; 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; } mutex_lock(&dev->lock); *c = hrt_ctls[i]; mutex_unlock(&dev->lock); return 0; } static int hrt_g_ctrl(struct file *file, void *priv, struct v4l2_control *c) { struct hrt_fh *fh = (struct hrt_fh*)priv; struct hrt_dev *dev = fh->dev; 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 = dev->saa7110_registers[HRT_BRIGHTNESS_REG]; } else if (c->id == V4L2_CID_CONTRAST) { c->value = dev->saa7110_registers[HRT_BRIGHTNESS_REG]; } else { return -EINVAL; } return 0; } static int hrt_s_ctrl(struct file *file, void *priv, struct v4l2_control *c) { return 0; } static int hrt_enum_input(struct file *file, void *priv, struct v4l2_input *inp) { unsigned int n; /* Only one input */ if (inp->index > 0) return -EINVAL; n = inp->index; memset(inp, 0, sizeof(*inp)); inp->index = n; inp->type = V4L2_INPUT_TYPE_CAMERA; sprintf(inp->name, "NTSC Camera"); return 0; } static int hrt_g_parm(struct file *file, void *priv, struct v4l2_streamparm * parm) { 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; } /********************************************************************/ static struct video_device hrt_template = { .owner = THIS_MODULE, .name = "HRT Pixelsmart PS512-8-PCI", .type = HRT_VID_TYPE, .fops = &hrt_fops, .release = grabber_release, .vidioc_querycap = hrt_querycap, .vidioc_enum_fmt_cap = hrt_enum_fmt_cap, .vidioc_s_fmt_cap = hrt_s_fmt_cap, .vidioc_g_fmt_cap = hrt_g_fmt_cap, .vidioc_g_crop = hrt_g_crop, .vidioc_s_crop = hrt_s_crop, .vidioc_reqbufs = hrt_reqbufs, .vidioc_querybuf = hrt_querybuf, .vidioc_qbuf = hrt_qbuf, .vidioc_dqbuf = hrt_dqbuf, .vidioc_enum_input = hrt_enum_input, .vidioc_queryctrl = hrt_queryctrl, .vidioc_g_ctrl = hrt_g_ctrl, .vidioc_s_ctrl = hrt_s_ctrl, .vidioc_streamon = hrt_streamon, .vidioc_streamoff = hrt_streamoff, .vidioc_g_parm = hrt_g_parm, .vidioc_try_fmt_cap = hrt_try_fmt_cap, .tvnorms = V4L2_STD_525_60, .current_norm = V4L2_STD_NTSC_M, }; 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 */ /*device number*/ int hrt_num_devices = 0; /* total number of devices detected */ #define HRT_IS_PCI(X) ((X) >= hrt_nonpci_devices) struct hrt_dev *hrt_devices[HRT_MAX_DEVICES]; #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; iirq_count, ips, tps, rps); } *eof = 1; return buf - org_buf; } /********************************************************************/ #define hrt_debug_init()\ { hrt_proc_read_entry = create_proc_read_entry("hrt0", 0, 0, hrt_read_proc, 0);} #define hrt_debug_cleanup()\ {if (hrt_proc_read_entry) remove_proc_entry("hrt0", 0);} #else /* hrt debugging is off */ #define HRT_CHECK(dev,states,msg) do{}while(0) #define HRT_MODULE_CHECK(states,msg) do{}while(0) #define HRT_DEBUG_MSG(args...) do{}while(0) #define hrt_debug_init() do{}while(0) #define hrt_debug_cleanup() do{}while(0) #endif /**************************** * low-level device control * ****************************/ /* I2C bits */ #define HRT_I2C_SCL 0x01 #define HRT_I2C_SDA 0x02 #define HRT_CONTROL(addr) (addr + HRT_CONTROL_REG) #define I2C_CONTROL(addr) (addr + HRT_I2C_REG) #define I2C_BUSY(addr) (!(ioread8((void *) HRT_CONTROL(addr)) & 0x80)) /* Bit 7 at 0x2000 (the HRT512-8 control register) tells whether * the CPU is sending data across the I2C bus */ #define hrt_freeze_next(dev) \ dev->is_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(struct hrt_dev *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(struct hrt_dev *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(); } /********************************************************************* * We did not use it for hardware data copy, * since it is hardware dependent. * Maybe, you can try readl *********************************************************************/ static inline void * datacpy(void * to, const void * from, size_t n) { int d0, d1, d2; __asm__ __volatile__( "rep ; movsl\n\t" "testb $2,%b4\n\t" "je 1f\n\t" "movsw\n" "1:\ttestb $1,%b4\n\t" "je 2f\n\t" "movsb\n" "2:" : "=&c" (d0), "=&D" (d1), "=&S" (d2) :"0" (n/4), "q" (n),"1" ((long) to),"2" ((long) from) : "memory"); return (to); } #if 0 /********************************************************************/ /** * hrt_do_private_ioctl - handle private ioctl's on device */ static int hrt_do_private_ioctl(struct hrt_dev *hrtdev, unsigned int cmd, unsigned long arg) { /*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 */ 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; */ /* printk("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; } #endif /********************************************************************/ /* * 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(struct hrt_dev *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(struct hrt_dev *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(struct hrt_dev *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(struct hrt_dev *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(struct hrt_dev *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(struct hrt_dev *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(struct hrt_dev *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; } /* ------------------------------------------------------------------ Videobuf operations ------------------------------------------------------------------*/ static int buffer_setup(struct videobuf_queue *vq, unsigned int *count, unsigned int *size) { struct hrt_fh *fh = vq->priv_data; *size = fh->win.width*fh->win.height*fh->win.depth; if (0 == *count) *count = 32; while (*size * *count > vid_limit * 1024 * 1024) (*count)--; return 0; } static void free_buffer(struct videobuf_queue *vq, struct hrt_buffer *buf) { if (in_interrupt()) BUG(); videobuf_waiton(&buf->vb, 0, 0); videobuf_vmalloc_free(&buf->vb); buf->vb.state = VIDEOBUF_NEEDS_INIT; } #define norm_maxw() 640 #define norm_maxh() 480 static int buffer_prepare(struct videobuf_queue *vq, struct videobuf_buffer *vb, enum v4l2_field field) { struct hrt_fh *fh = vq->priv_data; struct hrt_buffer *buf = container_of(vb, struct hrt_buffer, vb); int rc; BUG_ON(NULL == fh->fmt); if (fh->win.width < 48 || fh->win.width > norm_maxw() || fh->win.height < 32 || fh->win.height > norm_maxh()) return -EINVAL; buf->vb.size = fh->win.width*fh->win.height*fh->win.depth; if (0 != buf->vb.baddr && buf->vb.bsize < buf->vb.size) return -EINVAL; if (buf->fmt != fh->fmt || buf->vb.width != fh->win.width || buf->vb.height != fh->win.height || buf->vb.field != field) { buf->fmt = fh->fmt; buf->vb.width = fh->win.width; buf->vb.height = fh->win.height; buf->vb.field = field; } if (VIDEOBUF_NEEDS_INIT == buf->vb.state) { rc = videobuf_iolock(vq, &buf->vb, NULL); if (rc < 0) goto fail; } buf->vb.state = VIDEOBUF_PREPARED; return 0; fail: free_buffer(vq, buf); return rc; } static void buffer_queue(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct hrt_buffer *buf = container_of(vb,struct hrt_buffer,vb); struct hrt_fh *fh = vq->priv_data; struct hrt_dev *dev = fh->dev; buf->vb.state = VIDEOBUF_QUEUED; buf->fields_grabbed = 0; list_add_tail(&buf->vb.queue, &dev->capture); } static void buffer_release(struct videobuf_queue *vq, struct videobuf_buffer *vb) { struct hrt_buffer *buf = container_of(vb,struct hrt_buffer,vb); free_buffer(vq,buf); } static struct videobuf_queue_ops hrt_video_qops = { .buf_setup = buffer_setup, .buf_prepare = buffer_prepare, .buf_queue = buffer_queue, .buf_release = buffer_release, }; /******************************************************************** * * 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(struct hrt_dev *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) ){ 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 */ 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 { 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 { /* GRAY MODE */ linelen = hrtdev->win->width - hrtdev->win->startx; printk(KERN_ALERT "\n------> 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(struct hrt_dev *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(struct hrt_dev *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(struct hrt_dev* dev); void hrt_timer_cleanup(struct hrt_dev* dev); int hrt_timer_activate(struct hrt_dev* dev); void hrt_timer_deactivate(struct hrt_dev* dev); /******************************************************************** * hrt_cleanup * * Cleanup the device - called from the module cleanup routine */ void hrt_cleanup(struct hrt_dev* 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); iounmap((void *)dev->virt_addr); /* problem might be here dan */ dev->virt_addr = 0; } #ifdef CONFIG_PCI if (dev->pci_dev) { pci_disable_device(dev->pci_dev); dev->pci_dev = NULL; } #endif if (dev->registered == 1) { video_unregister_device(dev->video_dev); video_device_release(dev->video_dev); /* set the per-device state variable to indicate we are not registered any more */ dev->registered =0; } #if 0 /* De-allocate frame buffer */ if (dev->framedata) { vfree(dev->framedata); dev->framedata = NULL; } #endif /* 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; if((dev->hrt_devlist.prev!=NULL) && (dev->hrt_devlist.next!=NULL)) { printk("del device %d\n",dev->num); list_del(&dev->hrt_devlist); } kfree(dev); dev = NULL; } /******************************************************************** * hrt_init * * Long function.. * Probes and initialises the device */ int hrt_init(unsigned long phys_address, struct pci_dev * pci_dev) { struct hrt_dev * dev; struct video_device *vfd; int ret, 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); dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (NULL == dev) return -ENODEV; /* put the device in the unregistered state */ dev->registered = 0; dev->state = HRT_INITIALIZING_STATE; dev->num = hrt_num_devices; dev->users = 0; spin_lock_init(&dev->spinlock); 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); kfree(dev); dev = NULL; 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; dev->mode &= ~HRT_ISA_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)); 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)); if (val2 == (unsigned char) ~val1) { dev->mode |= HRT_COLOR_MODE; /* infer the frame geometry from the device type */ HRT_DEBUG_MSG(1, "COLOR device detected"); dev->cols = 640; dev->rows = 480; 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) { 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; } if(testing_isa_mode == 1){ dev->mode |= HRT_ISA_MODE; } 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"); hrt_cleanup(dev); return -ENODEV; } } /* discover whether this device supports interrupts */ hrt_timer_init(dev); if (dev->mode & HRT_IRQ_MODE) { /* 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; hrt_irq_enable(dev); hrt_go_live(dev); mdelay(20); hrt_freeze_immediate(dev); 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"); if(dev->irq > 0) free_irq(dev->irq, dev); dev->irq = 0; dev->mode &= ~HRT_IRQ_MODE; hrt_timer_init(dev); } } /* 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 */ mutex_init(&dev->lock); /* 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; dev->state = HRT_CLOSED_STATE; /* Make the lists used for streaming empty */ INIT_LIST_HEAD(&dev->capture); /* Register with V4L */ vfd = video_device_alloc(); if (NULL == vfd) { hrt_cleanup(dev); return -ENODEV; } *vfd = hrt_template; ret = video_register_device(vfd, VFL_TYPE_GRABBER, video_nr); if (ret < 0) { printk(KERN_ERR "Unable to register video device\n"); /* Failure in the device registration * Cleanup before exiting */ hrt_cleanup(dev); return -ENODEV; } snprintf(vfd->name, sizeof(vfd->name), "%s (%i)", hrt_template.name, vfd->minor); if (video_nr >= 0) video_nr++; dev->video_dev = vfd; /* 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 */ if(dev->mode & HRT_COLOR_MODE){ dev->bytesperline = HRT_COLOR_BYTES_PER_LINE; dev->framesize = HRT_COLOR_FRAMESIZE; } else{ dev->bytesperline = HRT_GRAY_BYTES_PER_LINE; dev->framesize = HRT_GRAY_FRAMESIZE; } /* Allocate the frame buffer */ if (dev->mode & HRT_COLOR_MODE) { dev->localbuf = (unsigned char *) kmalloc(sizeof(char) * 1664, GFP_KERNEL); if (dev->localbuf == NULL) { printk(KERN_ERR "Unable to allocate memory\n"); hrt_cleanup(dev); return -ENOMEM; } } else { dev->localbuf = NULL; } 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); list_add_tail(&dev->hrt_devlist, &hrt_devlist); hrt_devices[hrt_num_devices] = dev; hrt_num_devices++; /* Make the device go-live */ hrt_go_live(dev); 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 hrt_dev *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 } /******************************************************************** * 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) { struct hrt_dev* dev = (struct hrt_dev*)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(struct hrt_dev* 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, IRQF_SHARED, "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(struct hrt_dev* 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(struct hrt_dev* 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"); } 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(struct hrt_dev* dev) { HRT_DEBUG_MSG(1, "hrt_timer_cleanup"); HRT_CHECK(dev, HRT_FINALIZING_STATE, "3"); hrt_timer_deactivate (dev); tasklet_kill(&dev->tasklet); 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(struct hrt_dev* 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); 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(struct hrt_dev* 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; struct hrt_dev* dev = (struct hrt_dev*)data; struct hrt_buffer *item = NULL; void *vbuf =NULL; unsigned long flags; if (dev->state != HRT_OPEN_STATE) return; if (!spin_trylock_irqsave(&dev->spinlock,flags)) 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; } if (list_empty(&dev->capture)) goto done; /* SINGLE PORTED */ 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){ /* State 0 */ printk("State 0\n"); if(dev->is_frozen){ hrt_go_live(dev); } if (new_field == 1) { dev->streaming_state = 1; dev->field_changes = 2; } } else if (dev->streaming_state == 1){ /* State 1 */ 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; printk("HRT_FREEZE_NEXT\n"); hrt_freeze_next(dev); } } } else if (dev->streaming_state == 2){ /* State 2 */ printk("State 2 dev->field_changes = %d\n", dev->field_changes); new_field = hrt_get_field(dev); if (new_field != dev->field_id || ((new_field == dev->field_id) && (dev->mode & HRT_IRQ_MODE))){ dev->field_id = new_field; item = list_entry(dev->capture.next, struct hrt_buffer, vb.queue); /* The next is to obtain the buffer address of a videobuf_buffer. * The data buffer of the videobuf_buffer is allocated by * invoking __videobuf_iolock via calling buffer_prepare by * ether videobuf_qbuf or read operation */ vbuf = videobuf_to_vmalloc(&item->vb); if(vbuf != NULL) { /*grab a complete video frame from the device*/ grab_field(dev, (unsigned char *) vbuf, dev->field_id); grab_field(dev, (unsigned char *) vbuf, !dev->field_id); } else { printk("buffer pointer invalid\n"); goto done; } printk("after grab\n"); item->vb.state = VIDEOBUF_DONE; list_del(&item->vb.queue); wake_up(&item->vb.done); dev->streaming_state = 0; } /* field change */ else { /* no field change and no interrupt */ /* do nothing */ } } /* state 2 */ } /* color and single port mode */ else { /* DUAL PORTED * * 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->mode & HRT_IRQ_MODE)) { printk("Keep waiting field id change\n"); goto done; } else if (new_field != dev->field_id) { dev->field_id = new_field; } else if ((new_field == dev->field_id) && (dev->mode & HRT_IRQ_MODE)) { dev->field_id = !new_field; } item = list_entry(dev->capture.next, struct hrt_buffer, vb.queue); vbuf = videobuf_to_vmalloc(&item->vb); if(vbuf == NULL) { printk("buffer pointer invalid\n"); goto done; } if(dev->field_id == 1) { grab_field(dev, (unsigned char *) vbuf, !dev->field_id); item->fields_grabbed = 0x1; printk("grab field %d\n",!dev->field_id); } else if ((dev->field_id == 0) && (item->fields_grabbed == 0x1)) { grab_field(dev, (unsigned char *) vbuf, !dev->field_id); item->fields_grabbed |= 0x2; printk("grab field %d\n",!dev->field_id); } else { item->fields_grabbed = 0; printk("Fail to grab one frame: reset...\n"); } /* We have a complete frame */ if (item->fields_grabbed == 0x3) { item->fields_grabbed = 0; item->vb.state = VIDEOBUF_DONE; list_del(&item->vb.queue); wake_up(&item->vb.done); } } done: spin_unlock_irqrestore(&dev->spinlock,flags); } /********************************************************************/ /** * hrt_open * * checks minor number range, sets private_data, * starts timer if necessary, increments users */ int hrt_open(struct inode *inode, struct file *file) { int minor = iminor(inode); int result = 0; struct hrt_dev *dev; struct hrt_fh *fh; printk(KERN_DEBUG "hrt: open called (minor=%d)\n", minor); list_for_each_entry(dev, &hrt_devlist, hrt_devlist) if (dev->video_dev->minor == minor) goto found; return -ENODEV; found: fh = kzalloc(sizeof(*fh), GFP_KERNEL); if (NULL == fh) { return -ENOMEM; } file->private_data = fh; fh->dev = dev; fh->type = V4L2_BUF_TYPE_VIDEO_CAPTURE; fh->win.height = HRT_HEIGHT; if (dev->mode & HRT_COLOR_MODE) { fh->win.width = HRT_COLOR_WIDTH; fh->win.depth = HRT_COLOR_BYTES_PER_PIXEL; fh->fmt = &formats[0]; } else { fh->win.width = HRT_GRAY_WIDTH; fh->win.depth = HRT_GRAY_BYTES_PER_PIXEL; fh->fmt = &formats[1]; } fh->win.startx = 0; fh->win.starty = 0; videobuf_queue_vmalloc_init(&fh->vb_vidq, &hrt_video_qops, NULL, &dev->spinlock, fh->type, V4L2_FIELD_INTERLACED, sizeof(struct hrt_buffer), fh); /* If more than one user, mutex should be added */ mutex_lock(&dev->lock); dev->users++; if (1 == dev->users) { /* Set device state to OPEN */ dev->state = HRT_OPEN_STATE; /* Activate the timer */ HRT_DEBUG_MSG(2, "installing timer/irq"); result = hrt_timer_activate(dev); } mutex_unlock(&dev->lock); return result; } /********************************************************************/ /* hrt_close * * hrt_close 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_close(struct inode *inode, struct file *file) { struct hrt_fh *fh = file->private_data; struct hrt_dev *dev = fh->dev; int minor = iminor(inode); videobuf_stop(&fh->vb_vidq); videobuf_mmap_free(&fh->vb_vidq); kfree(fh); mutex_lock(&dev->lock); dev->users--; if (dev->users <= 0) { dev->state = HRT_CLOSED_STATE; hrt_timer_deactivate(dev); } mutex_unlock(&dev->lock); printk("close called (minor=%d, users=%d)\n",minor, dev->users); return 0; } /********************************************************************/ /* hrt_poll * * handles poll() and select() on /dev/video */ unsigned int hrt_poll(struct file *file, poll_table *wait) { struct hrt_fh *fh = file->private_data; struct hrt_buffer *buf; enum v4l2_field field; void *vbuf; unsigned long flags = 0; if (1 == fh->vb_vidq.streaming) { /* streaming capture */ if (list_empty(&fh->vb_vidq.stream)) { printk("list empty\n"); return POLLERR; } buf = list_entry(fh->vb_vidq.stream.next, struct hrt_buffer,vb.stream); poll_wait(file, &buf->vb.done, wait); vbuf = videobuf_to_vmalloc(&buf->vb); if (vbuf == NULL) { printk("Can not convert videobuf to vmalloc in poll!\n"); return POLLERR; } if (buf->vb.memory == V4L2_MEMORY_USERPTR) { memcpy((unsigned char *)buf->vb.baddr, vbuf, fh->win.width * fh->win.height * fh->win.depth); printk ("give user a buf : size is %d\n", fh->win.width * fh->win.height * fh->win.depth ); } } else { /* read() capture */ mutex_lock(&fh->vb_vidq.vb_lock); if (NULL == fh->vb_vidq.read_buf) { /* need to cature a new frame */ fh->vb_vidq.read_buf = videobuf_alloc(&fh->vb_vidq); if (NULL == fh->vb_vidq.read_buf) goto err; printk("After alloc in poll!\n"); fh->vb_vidq.read_buf->memory = V4L2_MEMORY_USERPTR; fh->vb_vidq.read_buf->bsize = fh->vb_vidq.msize; field = videobuf_next_field(&fh->vb_vidq); if (0 != fh->vb_vidq.ops->buf_prepare(&fh->vb_vidq, fh->vb_vidq.read_buf,field)) { kfree (fh->vb_vidq.read_buf); fh->vb_vidq.read_buf = NULL; goto err; } printk("After prepare buffer in poll!\n"); if (fh->vb_vidq.irqlock) spin_lock_irqsave(&fh->vb_vidq.irqlock, flags); fh->vb_vidq.ops->buf_queue(&fh->vb_vidq,fh->vb_vidq.read_buf); if (fh->vb_vidq.irqlock) spin_unlock_irqrestore(&fh->vb_vidq.irqlock, flags); fh->vb_vidq.read_off = 0; } mutex_unlock(&fh->vb_vidq.vb_lock); buf = (struct hrt_buffer*)fh->vb_vidq.read_buf; poll_wait(file, &buf->vb.done, wait); printk("after poll wait\n"); } if (buf->vb.state == VIDEOBUF_DONE || buf->vb.state == VIDEOBUF_ERROR) return POLLIN|POLLRDNORM; return 0; err: mutex_unlock(&fh->vb_vidq.vb_lock); return POLLERR; } #define CALL(q, f, arg...) \ ((q->int_ops->f) ? q->int_ops->f(arg) : 0) /* just for read */ size_t hrt_read_one(struct videobuf_queue *q, char __user *data, size_t count, loff_t *ppos, int nonblocking) { enum v4l2_field field; unsigned long flags = 0; unsigned size, nbufs; int retval; mutex_lock(&q->vb_lock); nbufs = 1; size = 0; q->ops->buf_setup(q, &nbufs, &size); if (NULL == q->read_buf) { /* need to capture a new frame */ retval = -ENOMEM; q->read_buf = videobuf_alloc(q); //printk(1, "video alloc=0x%p\n", q->read_buf); if (NULL == q->read_buf) goto done; q->read_buf->memory = V4L2_MEMORY_USERPTR; q->read_buf->bsize = count; /* preferred size */ field = videobuf_next_field(q); retval = q->ops->buf_prepare(q, q->read_buf, field); if (0 != retval) { kfree(q->read_buf); q->read_buf = NULL; goto done; } if (q->irqlock) spin_lock_irqsave(q->irqlock, flags); q->ops->buf_queue(q, q->read_buf); if (q->irqlock) spin_unlock_irqrestore(q->irqlock, flags); q->read_off = 0; } /* wait until capture is done */ retval = videobuf_waiton(q->read_buf, nonblocking, 1); if (0 != retval) goto done; CALL(q, sync, q, q->read_buf); if (VIDEOBUF_ERROR == q->read_buf->state) { /* catch I/O errors */ q->ops->buf_release(q, q->read_buf); kfree(q->read_buf); q->read_buf = NULL; retval = -EIO; goto done; } /* Copy to userspace */ retval = CALL(q, video_copy_to_user, q, data, count, nonblocking); if (retval < 0) goto done; q->read_off += retval; if (q->read_off == q->read_buf->size) { /* all data copied, cleanup */ q->ops->buf_release(q, q->read_buf); kfree(q->read_buf); q->read_buf = NULL; } done: mutex_unlock(&q->vb_lock); return retval; } /******************************************************************** * 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 __user *data, size_t count, loff_t * ppos) { struct hrt_fh *fh = file->private_data; int retval = 0; switch (fh->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: retval = hrt_read_one(&fh->vb_vidq, data, count, ppos, file->f_flags & O_NONBLOCK); break; default: BUG(); } return retval; } /******************************************************************** * * 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_fh *fh = file->private_data; struct hrt_dev *dev = fh->dev; unsigned long flags = 0; int ret=-EINVAL; dev->win = &fh->win; /* This nice code copied from xawtv's libng. It prints out the ioctl's that are done. */ switch (_IOC_TYPE(cmd)) { case 'v': printk("ioctl 0x%x (v4l1, VIDIOC%s)\n", cmd, (_IOC_NR(cmd) < ARRAY_SIZE(v4l1_ioctls)) ? v4l1_ioctls[_IOC_NR(cmd)] : "???"); break; case 'V': /*printk("ioctl 0x%x (v4l2, %s)\n", cmd, v4l2_ioctl_names[_IOC_NR(cmd)]); */ break; default: break;// printk("ioctl 0x%x (?)\n", cmd); } switch (cmd) { case IOC_HRT_FREEZE_FRAME: HRT_DEBUG_MSG(2, "IOC_HRT_FREEZE_FRAME: called"); spin_lock_irqsave(&dev->spinlock,flags); hrt_freeze_next(dev); spin_unlock_irqrestore(&dev->spinlock,flags); return 0; case IOC_HRT_GO_LIVE: HRT_DEBUG_MSG(2, "IOC_HRT_GO_LIVE: called"); spin_lock_irqsave(&dev->spinlock,flags); hrt_go_live(dev); spin_unlock_irqrestore(&dev->spinlock,flags); return 0; case IOC_HRT_UPSIDE_DOWN: HRT_DEBUG_MSG(2, "IOC_HRT_UPSIDE_DOWN: called"); mutex_lock(&dev->lock); if (dev->upside_down) dev->upside_down = 0; else dev->upside_down = 1; mutex_unlock(&dev->lock); return 0; /* case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; printk("ioctl reqbufs\n"); return videobuf_reqbufs(&fh->vb_vidq, req); } */ /* case VIDIOC_REQBUFS: { struct v4l2_requestbuffers *req = arg; printk("ioctl reqbufs\n"); if (!dev->video_dev->vidioc_reqbufs) break; return(dev->video_dev->vidioc_reqbufs(file, fh, arg)); } */ 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 = dev->win->height; return 0; } default: ret=video_ioctl2(inode,file,cmd,(unsigned long)arg); break; } return ret; } /******************************************************************** * 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_fh *per_file = file->private_data; struct hrt_dev *dev = per_file->dev; int ret; dev->win = &per_file->win; #if 0 mutex_lock(&dev->lock); ret = hrt_do_private_ioctl(dev, cmd, arg); if (ret != -EINVAL) { mutex_unlock(&dev->lock); // up(&dev->sem); return ret; } #endif //ret = video_usercopy(inode, file, cmd, arg, hrt_do_ioctl); ret = hrt_do_ioctl(inode, file, cmd, (void *)arg); // mutex_lock(&dev->lock); return ret; } /********************************************************************/ /** * hrt_mmap - called by the mmap() system call on /dev/video */ int hrt_mmap(struct file *file, struct vm_area_struct *vma) { struct hrt_fh *fh = file->private_data; return videobuf_mmap_mapper(&fh->vb_vidq, vma); } #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) { 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_dev objects; we just use the remove operation for removal of the device driver module */ void hrt_pci_remove (struct pci_dev *pci_dev) { struct hrt_dev *dev; HRT_DEBUG_MSG(2, "hrt_pci_remove"); dev = (struct hrt_dev *) pci_get_drvdata (pci_dev); hrt_cleanup(dev); pci_disable_device(pci_dev); } /********************************************************************/ void hrt_pci_init(void) { int ret; HRT_DEBUG_MSG(2, "pci_register_driver"); ret = pci_register_driver(&hrt_pci_driver); } /********************************************************************/ void hrt_pci_cleanup(void) { 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) { printk( " no pci support hrt_pci_cleanup\n"); } #endif /************************************* * module initialization and cleanup * *************************************/ int hrt_major_number = 0; /********************************************************************/ void hrt_cleanup_module(void) { 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_isa_cleanup(); hrt_pci_cleanup(); hrt_debug_cleanup(); hrt_module_state = HRT_MODULE_UNINITIALIZED_STATE; } /********************************************************************/ int hrt_init_module(void) { int result = 0; hrt_module_state = HRT_MODULE_INITIALIZING_STATE; hrt_debug_init(); /* 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 "------------ENTERING PCI MODE!!!!--------------\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; return 0; } module_init(hrt_init_module); module_exit(hrt_cleanup_module);