]> git.neil.brown.name Git - history.git/commitdiff
Import 2.2.8pre4 2.2.8pre4
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:18:50 +0000 (15:18 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:18:50 +0000 (15:18 -0500)
27 files changed:
Documentation/Changes
arch/ppc/kernel/pmac_pic.c
drivers/char/Config.in
drivers/char/Makefile
drivers/char/isicom.c
drivers/char/planb.c [new file with mode: 0644]
drivers/char/planb.h [new file with mode: 0644]
drivers/char/saa7196.h [new file with mode: 0644]
drivers/net/3c507.c
drivers/net/eexpress.c
drivers/net/ibmtr.c
drivers/net/ppp.c
drivers/scsi/imm.c
drivers/scsi/megaraid.c
drivers/scsi/ppa.c
drivers/scsi/sg.c
drivers/usb/usb.c
fs/nfs/dir.c
fs/nfs/file.c
fs/nfs/inode.c
include/asm-alpha/semaphore.h
include/asm-ppc/ide.h
include/linux/nfs_fs.h
include/linux/videodev.h
include/scsi/sg.h
init/main.c
net/sunrpc/sched.c

index ddc424046dce20d063e727135e7f3ee07b7db26c..633479eb991e9e58e0164964c12714fc4af7e210 100644 (file)
@@ -444,8 +444,8 @@ Real is aware of the problem and should have an updated version of the
 software available shortly.  In the mean time, you can always try
 backing up your copy of rvplayer, and then editing it by:
 
-   dd if=/dev/zero of=rvplayer bs=1 count=1 seek=657586 conv=notrunc dd
-if=/dev/zero of=rvplayer bs=1 count=1 seek=665986 conv=notrunc
+   dd if=/dev/zero of=rvplayer bs=1 count=1 seek=657586 conv=notrunc
+   dd if=/dev/zero of=rvplayer bs=1 count=1 seek=665986 conv=notrunc
 
    If you're lucky, you'll then have sound....
 
index 6a49f14052ea03fba70270739361e1f9526a4b1a..6b9d8ca5388153e7fa881827e8277ad5dffc19d8 100644 (file)
@@ -1,4 +1,4 @@
-
+#include <linux/config.h>
 #include <linux/stddef.h>
 #include <linux/init.h>
 #include <linux/sched.h>
index 1e20251c39b444bfccc64c35d2acd4ccb27749e0..16881b0e63d87db0d1e458ae02f1f10c8340afc3 100644 (file)
@@ -137,6 +137,9 @@ if [ "$CONFIG_VIDEO_DEV" != "n" ]; then
     dep_tristate 'Colour QuickCam Video For Linux (EXPERIMENTAL)' CONFIG_VIDEO_CQCAM $CONFIG_VIDEO_DEV $CONFIG_PARPORT
   fi
   dep_tristate 'Mediavision Pro Movie Studio Video For Linux' CONFIG_VIDEO_PMS $CONFIG_VIDEO_DEV
+  if [ "$CONFIG_PMAC" = "y" ]; then
+    dep_tristate 'PlanB Video-In on PowerMac' CONFIG_VIDEO_PLANB $CONFIG_VIDEO_DEV
+  fi
   dep_tristate 'SAA5249 Teletext processor' CONFIG_VIDEO_SAA5249 $CONFIG_VIDEO_DEV
   dep_tristate 'SF16FMI Radio' CONFIG_RADIO_SF16FMI $CONFIG_VIDEO_DEV
   if [ "$CONFIG_RADIO_SF16FMI" = "y" ]; then
index c86f75f43014209a23671d3f57e15d1251113832..4062808b0f1a64b0c6352b770e23a02118af3d98 100644 (file)
@@ -354,6 +354,14 @@ else
   endif
 endif
 
+ifeq ($(CONFIG_VIDEO_PLANB),y)
+L_OBJS += planb.o
+else
+  ifeq ($(CONFIG_VIDEO_PLANB),m)
+  M_OBJS += planb.o
+  endif
+endif
+
 ifeq ($(CONFIG_RADIO_AZTECH),y)
 L_OBJS += radio-aztech.o
 else
index 5f0c31ecf4c2577087bfa381dc3e1772ca704033..ba72b616a792be245340ef3ae9068f4dcad69a04 100644 (file)
  *                                     (fixed range check bug as a side effect)
  *                                     Printk clean up
  *     9/12/98 alan@redhat.com         Rough port to 2.1.x
+ *
+ *
+ *     ***********************************************************
+ *
+ *     To use this driver you also need the support package. You 
+ *     can find this in RPM format on
+ *             ftp://ftp.linux.org.uk/pub/linux/alan
+ *     
+ *     You can find the original tools for this direct from Multitech
+ *             ftp://ftp.multitech.com/ISI-Cards/
+ *
+ *     Having installed the cards the module options (/etc/conf.modules)
+ *
+ *     options isicom   io=card1,card2,card3,card4 irq=card1,card2,card3,card4
+ *
+ *     Omit those entries for boards you don't have installed.
+ *
  */
 
 #include <linux/module.h>
diff --git a/drivers/char/planb.c b/drivers/char/planb.c
new file mode 100644 (file)
index 0000000..8fd09da
--- /dev/null
@@ -0,0 +1,2390 @@
+/* 
+    planb - PlanB frame grabber driver
+
+    PlanB is used in the 7x00/8x00 series of PowerMacintosh
+    Computers as video input DMA controller.
+
+    Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
+
+    Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+    Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu)
+       (Some codes are stolen from proposed v4l2 videodev.c
+               of Bill Dirks <dirks@rendition.com>)
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* $Id: planb.c,v 1.18 1999/05/02 17:36:34 mlan Exp $ */
+
+#include <linux/config.h>
+#include <linux/version.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/malloc.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/vmalloc.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/wrapper.h>
+#include <linux/tqueue.h>
+#include <linux/videodev.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/prom.h>
+#include <asm/dbdma.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/irq.h>
+
+#include "planb.h"
+#include "saa7196.h"
+
+
+/* Would you mind for some ugly debugging? */
+//#define DEBUG(x...) printk(KERN_DEBUG ## x) /* Debug driver */
+#define DEBUG(x...)            /* Don't debug driver */        
+//#define IDEBUG(x...) printk(KERN_DEBUG ## x) /* Debug interrupt part */
+#define IDEBUG(x...)           /* Don't debug interrupt part */
+
+/* Ever seen a Mac with more than 1 of these? */
+#define PLANB_MAX 1
+
+static int planb_num;
+static struct planb planbs[PLANB_MAX];
+static volatile struct planb_registers *planb_regs;
+
+static int def_norm = PLANB_DEF_NORM;  /* default norm */
+
+MODULE_PARM(def_norm, "i");
+MODULE_PARM_DESC(def_norm, "Default startup norm (0=PAL, 1=NTSC, 2=SECAM)");
+
+/* ------------------ PlanB Exported Functions ------------------ */
+static long planb_write(struct video_device *, const char *, unsigned long, int);
+static long planb_read(struct video_device *, char *, unsigned long, int);
+static int planb_open(struct video_device *, int);
+static void planb_close(struct video_device *);
+static int planb_ioctl(struct video_device *, unsigned int, void *);
+static int planb_init_done(struct video_device *);
+static int planb_mmap(struct video_device *, const char *, unsigned long);
+static void planb_irq(int, void *, struct pt_regs *);
+static void release_planb(void);
+int init_planbs(struct video_init *);
+
+/* ------------------ PlanB Internal Functions ------------------ */
+static int planb_prepare_open(struct planb *);
+static void planb_prepare_close(struct planb *);
+static void saa_write_reg(unsigned char, unsigned char);
+static unsigned char saa_status(int, struct planb *);
+static void saa_set(unsigned char, unsigned char, struct planb *);
+static void saa_init_regs(struct planb *);
+static void * rvmalloc(unsigned long);
+static void rvfree(void *, unsigned long);
+static unsigned long vmalloc_to_bus(void *);
+static unsigned long vmalloc_to_phys(void *);
+static int fbuffer_alloc(struct planb *);
+static int vgrab(struct planb *, struct video_mmap *);
+static void add_clip(struct planb *, struct video_clip *);
+static void fill_cmd_buff(struct planb *);
+static void cmd_buff(struct planb *);
+static volatile struct dbdma_cmd *setup_grab_cmd(int, struct planb *);
+static void overlay_start(struct planb *);
+static void overlay_stop(struct planb *);
+static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *, unsigned short,
+       unsigned int);
+static inline void tab_cmd_store(volatile struct dbdma_cmd *, unsigned int,
+       unsigned int);
+static inline void tab_cmd_gen(volatile struct dbdma_cmd *, unsigned short,
+       unsigned short, unsigned int, unsigned int);
+static int init_planb(struct planb *);
+static int find_planb(void);
+static void planb_pre_capture(int, int, struct planb *);
+static volatile struct dbdma_cmd *cmd_geo_setup(volatile struct dbdma_cmd *,
+                                       int, int, int, int, int, struct planb *);
+static inline void planb_dbdma_stop(volatile struct dbdma_regs *);
+static unsigned int saa_geo_setup(int, int, int, int, struct planb *);
+static inline int overlay_is_active(struct planb *);
+
+/*******************************/
+/* Memory management functions */
+/*******************************/
+
+static void * rvmalloc(unsigned long size)
+{
+       void *mem, *memptr;
+       unsigned long page;
+        
+       mem=vmalloc(size);
+       if (mem) 
+       {
+               memset(mem, 0, size); /* Clear the ram out, leave no junk */
+               memptr = mem;
+               while (size > 0) 
+                {
+                       page = vmalloc_to_phys(memptr);
+                       mem_map_reserve(MAP_NR(phys_to_virt(page)));
+                       memptr+=PAGE_SIZE;
+                       size-=PAGE_SIZE;
+               }
+       }
+       return mem;
+}
+
+static void rvfree(void * mem, unsigned long size)
+{
+       void *memptr;
+        unsigned long page;
+        
+       if (mem) 
+       {
+               memptr = mem;
+               while (size > 0) 
+                {
+                       page = vmalloc_to_phys(memptr);
+                       mem_map_unreserve(MAP_NR(phys_to_virt(page)));
+                       memptr += PAGE_SIZE;
+                       size-=PAGE_SIZE;
+               }
+               vfree(mem);
+       }
+}
+
+/*  Useful for using vmalloc()ed memory as DMA target  */
+static unsigned long vmalloc_to_bus(void *virt)
+{
+       pgd_t           *pgd;
+       pmd_t           *pmd;
+       pte_t           *pte;
+       unsigned long   a = (unsigned long)virt;
+
+       if (pgd_none(*(pgd = pgd_offset(current->mm, a))) ||
+           pmd_none(*(pmd = pmd_offset(pgd,         a))) ||
+           pte_none(*(pte = pte_offset(pmd,         a))))
+               return 0;
+       return virt_to_bus((void *)pte_page(*pte))
+               + (a & (PAGE_SIZE - 1));
+}
+
+static unsigned long vmalloc_to_phys(void *virt) {
+       return virt_to_phys(bus_to_virt(vmalloc_to_bus(virt)));
+}
+
+/*
+ *     Create the giant waste of buffer space we need for now
+ *     until we get DMA to user space sorted out (probably 2.3.x)
+ *
+ *     We only create this as and when someone uses mmap
+ */
+static int fbuffer_alloc(struct planb *pb)
+{
+       if(!pb->fbuffer)
+               pb->fbuffer=(unsigned char *) rvmalloc(MAX_GBUFFERS
+                                       * PLANB_MAX_FBUF);
+       else
+               printk(KERN_ERR "PlanB: Double alloc of fbuffer!\n");
+       if(!pb->fbuffer)
+               return -ENOBUFS;
+       return 0;
+}
+
+/*****************************/
+/* Hardware access functions */
+/*****************************/
+
+static void saa_write_reg(unsigned char addr, unsigned char val)
+{
+       planb_regs->saa_addr = addr; eieio();
+       planb_regs->saa_regval = val; eieio();
+       return;
+}
+
+/* return  status byte 0 or 1: */
+static unsigned char saa_status(int byte, struct planb *pb)
+{
+       saa_regs[pb->win.norm][SAA7196_STDC] =
+               (saa_regs[pb->win.norm][SAA7196_STDC] & ~2) | ((byte & 1) << 1);
+       saa_write_reg (SAA7196_STDC, saa_regs[pb->win.norm][SAA7196_STDC]);
+
+       /* Let's wait 30msec for this one */
+       current->state = TASK_INTERRUPTIBLE;
+#if LINUX_VERSION_CODE >= 0x02017F
+       schedule_timeout(30 * HZ / 1000);
+#else
+       current->timeout = jiffies + 30 * HZ / 1000;    /* 30 ms */;
+       schedule();
+#endif
+
+       return (unsigned char)in_8 (&planb_regs->saa_status);
+}
+
+static void saa_set(unsigned char addr, unsigned char val, struct planb *pb)
+{
+       if(saa_regs[pb->win.norm][addr] != val) {
+               saa_regs[pb->win.norm][addr] = val;
+               saa_write_reg (addr, val);
+       }
+       return;
+}
+
+static void saa_init_regs(struct planb *pb)
+{
+       int i;
+
+       for (i = 0; i < SAA7196_NUMREGS; i++)
+               saa_write_reg (i, saa_regs[pb->win.norm][i]);
+}
+
+static unsigned int saa_geo_setup(int width, int height, int interlace, int bpp,
+       struct planb *pb)
+{
+       int ht, norm = pb->win.norm;
+
+       switch(bpp) {
+       case 2:
+               /* RGB555+a 1x16-bit + 16-bit transparent */
+               saa_regs[norm][SAA7196_FMTS] &= ~0x3;
+               break;
+       case 1:
+       case 4:
+               /* RGB888 1x24-bit + 8-bit transparent */
+               saa_regs[norm][SAA7196_FMTS] &= ~0x1;
+               saa_regs[norm][SAA7196_FMTS] |= 0x2;
+               break;
+       default:
+               return -EINVAL;
+       }
+       ht = (interlace ? height / 2 : height);
+       saa_regs[norm][SAA7196_OUTPIX] = (unsigned char) (width & 0x00ff);
+       saa_regs[norm][SAA7196_HFILT] = (saa_regs[norm][SAA7196_HFILT] & ~0x3)
+                                               | (width >> 8 & 0x3);
+       saa_regs[norm][SAA7196_OUTLINE] = (unsigned char) (ht & 0xff);
+       saa_regs[norm][SAA7196_VYP] = (saa_regs[norm][SAA7196_VYP] & ~0x3)
+                                               | (ht >> 8 & 0x3);
+       /* feed both fields if interlaced, or else feed only even fields */
+       saa_regs[norm][SAA7196_FMTS] = (interlace) ?
+                                       (saa_regs[norm][SAA7196_FMTS] & ~0x60)
+                                       : (saa_regs[norm][SAA7196_FMTS] | 0x60);
+       /* transparent mode; extended format enabled */
+       saa_regs[norm][SAA7196_DPATH] |= 0x3;
+
+       return 0;
+}
+
+/***************************/
+/* DBDMA support functions */
+/***************************/
+
+static inline void planb_dbdma_restart(volatile struct dbdma_regs *ch)
+{
+       out_le32(&ch->control, PLANB_CLR(RUN));
+       out_le32(&ch->control, PLANB_SET(RUN|WAKE) | PLANB_CLR(PAUSE));
+}
+
+static inline void planb_dbdma_stop(volatile struct dbdma_regs *ch)
+{
+       int i = 0;
+
+       out_le32(&ch->control, PLANB_CLR(RUN) | PLANB_SET(FLUSH));
+       while((in_le32(&ch->status) == (ACTIVE | FLUSH)) && (i < 999)) {
+               IDEBUG("PlanB: waiting for DMA to stop\n");
+               i++;
+       }
+}
+
+static inline void tab_cmd_dbdma(volatile struct dbdma_cmd *ch,
+       unsigned short command, unsigned int cmd_dep)
+{
+       st_le16(&ch->command, command);
+       st_le32(&ch->cmd_dep, cmd_dep);
+}
+
+static inline void tab_cmd_store(volatile struct dbdma_cmd *ch,
+       unsigned int phy_addr, unsigned int cmd_dep)
+{
+       st_le16(&ch->command, STORE_WORD | KEY_SYSTEM);
+       st_le16(&ch->req_count, 4);
+       st_le32(&ch->phy_addr, phy_addr);
+       st_le32(&ch->cmd_dep, cmd_dep);
+}
+
+static inline void tab_cmd_gen(volatile struct dbdma_cmd *ch,
+       unsigned short command, unsigned short req_count,
+       unsigned int phy_addr, unsigned int cmd_dep)
+{
+       st_le16(&ch->command, command);
+       st_le16(&ch->req_count, req_count);
+       st_le32(&ch->phy_addr, phy_addr);
+       st_le32(&ch->cmd_dep, cmd_dep);
+}
+
+static volatile struct dbdma_cmd *cmd_geo_setup(
+       volatile struct dbdma_cmd *c1, int width, int height, int interlace,
+       int bpp, int clip, struct planb *pb)
+{
+       int norm = pb->win.norm;
+
+       if((saa_geo_setup(width, height, interlace, bpp, pb)) != 0)
+               return (volatile struct dbdma_cmd *)NULL;
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+                                                       SAA7196_FMTS);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+                                       saa_regs[norm][SAA7196_FMTS]);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+                                                       SAA7196_DPATH);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+                                       saa_regs[norm][SAA7196_DPATH]);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->even),
+                                       bpp | ((clip)? PLANB_CLIPMASK: 0));
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->odd),
+                                       bpp | ((clip)? PLANB_CLIPMASK: 0));
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+                                                       SAA7196_OUTPIX);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+                                       saa_regs[norm][SAA7196_OUTPIX]);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+                                                       SAA7196_HFILT);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+                                       saa_regs[norm][SAA7196_HFILT]);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+                                                       SAA7196_OUTLINE);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+                                       saa_regs[norm][SAA7196_OUTLINE]);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_addr),
+                                                       SAA7196_VYP);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->saa_regval),
+                                       saa_regs[norm][SAA7196_VYP]);
+       return c1;
+}
+
+/******************************/
+/* misc. supporting functions */
+/******************************/
+
+static void __planb_wait(struct planb *pb)
+{
+       struct wait_queue wait = { current, NULL };
+
+       add_wait_queue(&pb->lockq, &wait);
+repeat:
+       current->state = TASK_UNINTERRUPTIBLE;
+       if (pb->lock) {
+               schedule();
+               goto repeat;
+       }
+       remove_wait_queue(&pb->lockq, &wait);
+       current->state = TASK_RUNNING;
+}
+
+static inline void planb_wait(struct planb *pb)
+{
+       DEBUG("PlanB: planb_wait\n");
+       if(pb->lock)
+               __planb_wait(pb);
+}
+
+static inline void planb_lock(struct planb *pb)
+{
+       DEBUG("PlanB: planb_lock\n");
+       if(pb->lock)
+               __planb_wait(pb);
+       pb->lock = 1;
+}
+
+static inline void planb_unlock(struct planb *pb)
+{
+       DEBUG("PlanB: planb_unlock\n");
+       pb->lock = 0;
+       wake_up(&pb->lockq);
+}
+
+/***************/
+/* Driver Core */
+/***************/
+
+static int planb_prepare_open(struct planb *pb)
+{
+       int     i, size;
+
+       /* allocate memory for two plus alpha command buffers (size: max lines,
+          plus 40 commands handling, plus 1 alignment), plus dummy command buf,
+          plus clipmask buffer, plus frame grabbing status */
+       size = (pb->tab_size*(2+MAX_GBUFFERS*TAB_FACTOR)+1+MAX_GBUFFERS
+               * PLANB_DUMMY)*sizeof(struct dbdma_cmd)
+               +(PLANB_MAXLINES*((PLANB_MAXPIXELS+7)& ~7))/8
+               +MAX_GBUFFERS*sizeof(unsigned int);
+       if ((pb->priv_space = kmalloc (size, GFP_KERNEL)) == 0)
+               return -ENOMEM;
+       memset ((void *) pb->priv_space, 0, size);
+       pb->overlay_last1 = pb->ch1_cmd = (volatile struct dbdma_cmd *)
+                                               DBDMA_ALIGN (pb->priv_space);
+       pb->overlay_last2 = pb->ch2_cmd = pb->ch1_cmd + pb->tab_size;
+       pb->ch1_cmd_phys = virt_to_bus(pb->ch1_cmd);
+       pb->cap_cmd[0] = pb->ch2_cmd + pb->tab_size;
+       pb->pre_cmd[0] = pb->cap_cmd[0] + pb->tab_size * TAB_FACTOR;
+       for (i = 1; i < MAX_GBUFFERS; i++) {
+               pb->cap_cmd[i] = pb->pre_cmd[i-1] + PLANB_DUMMY;
+               pb->pre_cmd[i] = pb->cap_cmd[i] + pb->tab_size * TAB_FACTOR;
+       }
+       pb->frame_stat=(volatile unsigned int *)(pb->pre_cmd[MAX_GBUFFERS-1]
+                                               + PLANB_DUMMY);
+       pb->mask = (unsigned char *)(pb->frame_stat+MAX_GBUFFERS);
+
+       pb->fbuffer = (unsigned char *)rvmalloc(MAX_GBUFFERS * PLANB_MAX_FBUF);
+       if (!pb->fbuffer) {
+               kfree(pb->priv_space);
+               return -ENOMEM;
+       }
+       pb->grabbing = 0;
+       for (i = 0; i < MAX_GBUFFERS; i++) {
+               pb->frame_stat[i] = GBUFFER_UNUSED;
+               pb->gwidth[i] = 0;
+               pb->gheight[i] = 0;
+               pb->gfmt[i] = 0;
+               pb->gnorm_switch[i] = 0;
+#ifndef PLANB_GSCANLINE
+               pb->lsize[i] = 0;
+               pb->lnum[i] = 0;
+               pb->l_fr_addr[i]=(unsigned char *)rvmalloc(PAGE_SIZE*MAX_LNUM);
+               if (!pb->l_fr_addr[i]) {
+                       int j;
+                       kfree(pb->priv_space);
+                       rvfree((void *)pb->fbuffer, MAX_GBUFFERS
+                                                       * PLANB_MAX_FBUF);
+                       for(j = 0; j < i; j++)
+                               rvfree((void *)pb->l_fr_addr[j], PAGE_SIZE
+                                                               * MAX_LNUM);
+                       return -ENOMEM;
+               }
+#endif /* PLANB_GSCANLINE */
+       }
+       pb->gcount = 0;
+       pb->suspend = 0;
+       pb->last_fr = -999;
+       pb->prev_last_fr = -999;
+       return 0;   
+}
+
+static void planb_prepare_close(struct planb *pb)
+{
+#ifndef PLANB_GSCANLINE
+       int i;
+#endif
+
+       /* make sure the dma's are idle */
+       planb_dbdma_stop(&pb->planb_base->ch2);
+       planb_dbdma_stop(&pb->planb_base->ch1);
+       /* free kernel memory of command buffers */
+       if(pb->priv_space != 0) {
+               kfree (pb->priv_space);
+               pb->priv_space = 0;
+               pb->cmd_buff_inited = 0;
+       }
+       if(pb->fbuffer)
+               rvfree((void *)pb->fbuffer, MAX_GBUFFERS*PLANB_MAX_FBUF);
+       pb->fbuffer = NULL;
+#ifndef PLANB_GSCANLINE
+       for(i = 0; i < MAX_GBUFFERS; i++) {
+               if(pb->l_fr_addr[i])
+                       rvfree((void *)pb->l_fr_addr[i], PAGE_SIZE * MAX_LNUM);
+               pb->l_fr_addr[i] = NULL;
+       }
+#endif /* PLANB_GSCANLINE */
+}
+
+/*****************************/
+/* overlay support functions */
+/*****************************/
+
+static void overlay_start(struct planb *pb)
+{
+
+       DEBUG("PlanB: overlay_start()\n");
+
+       if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+
+               DEBUG("PlanB: presumably, grabbing is in progress...\n");
+
+               planb_dbdma_stop(&pb->planb_base->ch2);
+               out_le32 (&pb->planb_base->ch2.cmdptr,
+                                               virt_to_bus(pb->ch2_cmd));
+               planb_dbdma_restart(&pb->planb_base->ch2);
+               st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
+               tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+                                       DBDMA_NOP | BR_ALWAYS,
+                                       virt_to_bus(pb->ch1_cmd));
+               eieio();
+               pb->prev_last_fr = pb->last_fr;
+               pb->last_fr = -2;
+               if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+                       IDEBUG("PlanB: became inactive "
+                               "in the mean time... reactivating\n");
+                       planb_dbdma_stop(&pb->planb_base->ch1);
+                       out_le32 (&pb->planb_base->ch1.cmdptr,
+                                               virt_to_bus(pb->ch1_cmd));
+                       planb_dbdma_restart(&pb->planb_base->ch1);
+               }
+       } else {
+
+               DEBUG("PlanB: currently idle, so can do whatever\n");
+
+               planb_dbdma_stop(&pb->planb_base->ch2);
+               planb_dbdma_stop(&pb->planb_base->ch1);
+               st_le32 (&pb->planb_base->ch2.cmdptr,
+                                               virt_to_bus(pb->ch2_cmd));
+               st_le32 (&pb->planb_base->ch1.cmdptr,
+                                               virt_to_bus(pb->ch1_cmd));
+               out_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
+               planb_dbdma_restart(&pb->planb_base->ch2);
+               planb_dbdma_restart(&pb->planb_base->ch1);
+               pb->last_fr = -1;
+       }
+       return;
+}
+
+static void overlay_stop(struct planb *pb)
+{
+       DEBUG("PlanB: overlay_stop()\n");
+
+       if(pb->last_fr == -1) {
+
+               DEBUG("PlanB: no grabbing, it seems...\n");
+
+               planb_dbdma_stop(&pb->planb_base->ch2);
+               planb_dbdma_stop(&pb->planb_base->ch1);
+               pb->last_fr = -999;
+       } else if(pb->last_fr == -2) {
+               unsigned int cmd_dep;
+               tab_cmd_dbdma(pb->cap_cmd[pb->prev_last_fr], DBDMA_STOP, 0);
+               eieio();
+               cmd_dep = (unsigned int)in_le32(&pb->overlay_last1->cmd_dep);
+               if(overlay_is_active(pb)) {
+
+                       DEBUG("PlanB: overlay is currently active\n");
+
+                       planb_dbdma_stop(&pb->planb_base->ch2);
+                       planb_dbdma_stop(&pb->planb_base->ch1);
+                       if(cmd_dep != pb->ch1_cmd_phys) {
+                               out_le32(&pb->planb_base->ch1.cmdptr,
+                                               virt_to_bus(pb->overlay_last1));
+                               planb_dbdma_restart(&pb->planb_base->ch1);
+                       }
+               }
+               pb->last_fr = pb->prev_last_fr;
+               pb->prev_last_fr = -999;
+       }
+       return;
+}
+
+static void suspend_overlay(struct planb *pb)
+{
+       int fr = -1;
+       struct dbdma_cmd last;
+
+       DEBUG("PlanB: suspend_overlay: %d\n", pb->suspend);
+
+       if(pb->suspend++)
+               return;
+       if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+               if(pb->last_fr == -2) {
+                       fr = pb->prev_last_fr;
+                       memcpy(&last, (void*)pb->last_cmd[fr], sizeof(last));
+                       tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
+               }
+               if(overlay_is_active(pb)) {
+                       planb_dbdma_stop(&pb->planb_base->ch2);
+                       planb_dbdma_stop(&pb->planb_base->ch1);
+                       pb->suspended.overlay = 1;
+                       pb->suspended.frame = fr;
+                       memcpy(&pb->suspended.cmd, &last, sizeof(last));
+                       return;
+               }
+       }
+       pb->suspended.overlay = 0;
+       pb->suspended.frame = fr;
+       memcpy(&pb->suspended.cmd, &last, sizeof(last));
+       return;
+}
+
+static void resume_overlay(struct planb *pb)
+{
+
+       DEBUG("PlanB: resume_overlay: %d\n", pb->suspend);
+
+       if(pb->suspend > 1)
+               return;
+       if(pb->suspended.frame != -1) {
+               memcpy((void*)pb->last_cmd[pb->suspended.frame],
+                               &pb->suspended.cmd, sizeof(pb->suspended.cmd));
+       }
+       if(ACTIVE & in_le32(&pb->planb_base->ch1.status)) {
+               goto finish;
+       }
+       if(pb->suspended.overlay) {
+
+               DEBUG("PlanB: overlay being resumed\n");
+
+               st_le16 (&pb->ch1_cmd->command, DBDMA_NOP);
+               st_le16 (&pb->ch2_cmd->command, DBDMA_NOP);
+               /* Set command buffer addresses */
+               st_le32(&pb->planb_base->ch1.cmdptr,
+                                       virt_to_bus(pb->overlay_last1));
+               out_le32(&pb->planb_base->ch2.cmdptr,
+                                       virt_to_bus(pb->overlay_last2));
+               /* Start the DMA controller */
+               out_le32 (&pb->planb_base->ch2.control,
+                               PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+               out_le32 (&pb->planb_base->ch1.control,
+                               PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+       } else if(pb->suspended.frame != -1) {
+               out_le32(&pb->planb_base->ch1.cmdptr,
+                               virt_to_bus(pb->last_cmd[pb->suspended.frame]));
+               out_le32 (&pb->planb_base->ch1.control,
+                               PLANB_CLR(PAUSE) | PLANB_SET(RUN|WAKE));
+       }
+
+finish:
+       pb->suspend--;
+       wake_up_interruptible(&pb->suspendq);
+}
+
+static void add_clip(struct planb *pb, struct video_clip *clip) 
+{
+       volatile unsigned char  *base;
+       int     xc = clip->x, yc = clip->y;
+       int     wc = clip->width, hc = clip->height;
+       int     ww = pb->win.width, hw = pb->win.height;
+       int     x, y, xtmp1, xtmp2;
+
+       DEBUG("PlanB: clip %dx%d+%d+%d\n", wc, hc, xc, yc);
+
+       if(xc < 0) {
+               wc += xc;
+               xc = 0;
+       }
+       if(yc < 0) {
+               hc += yc;
+               yc = 0;
+       }
+       if(xc + wc > ww)
+               wc = ww - xc;
+       if(wc <= 0) /* Nothing to do */
+               return;
+       if(yc + hc > hw)
+               hc = hw - yc;
+
+       for (y = yc; y < yc+hc; y++) {
+               xtmp1=xc>>3;
+               xtmp2=(xc+wc)>>3;
+               base = pb->mask + y*96;
+               if(xc != 0 || wc >= 8)
+                       *(base + xtmp1) &= (unsigned char)(0x00ff &
+                               (0xff00 >> (xc&7)));
+               for (x = xtmp1 + 1; x < xtmp2; x++) {
+                       *(base + x) = 0;
+               }
+               if(xc < (ww & ~0x7))
+                       *(base + xtmp2) &= (unsigned char)(0x00ff >>
+                               ((xc+wc) & 7));
+       }
+
+       return;
+}
+
+static void fill_cmd_buff(struct planb *pb)
+{
+       int restore = 0;
+       volatile struct dbdma_cmd last;
+
+       DEBUG("PlanB: fill_cmd_buff()\n");
+
+       if(pb->overlay_last1 != pb->ch1_cmd) {
+               restore = 1;
+               last = *(pb->overlay_last1);
+       }
+       memset ((void *) pb->ch1_cmd, 0, 2 * pb->tab_size
+                                       * sizeof(struct dbdma_cmd));
+       cmd_buff (pb);
+       if(restore)
+               *(pb->overlay_last1) = last;
+       if(pb->suspended.overlay) {
+               unsigned long jump_addr = in_le32(&pb->overlay_last1->cmd_dep);
+               if(jump_addr != pb->ch1_cmd_phys) {
+                       int i;
+
+                       DEBUG("PlanB: adjusting ch1's jump address\n");
+
+                       for(i = 0; i < MAX_GBUFFERS; i++) {
+                               if(pb->need_pre_capture[i]) {
+                                   if(jump_addr == virt_to_bus(pb->pre_cmd[i]))
+                                       goto found;
+                               } else {
+                                   if(jump_addr == virt_to_bus(pb->cap_cmd[i]))
+                                       goto found;
+                               }
+                       }
+
+                       DEBUG("PlanB: not found...\n");
+
+                       goto out;
+found:
+                       if(pb->need_pre_capture[i])
+                               out_le32(&pb->pre_cmd[i]->phy_addr,
+                                               virt_to_bus(pb->overlay_last1));
+                       else
+                               out_le32(&pb->cap_cmd[i]->phy_addr,
+                                               virt_to_bus(pb->overlay_last1));
+               }
+       }
+out:
+       pb->cmd_buff_inited = 1;
+
+       return;
+}
+
+static void cmd_buff(struct planb *pb)
+{
+       int             i, bpp, count, nlines, stepsize, interlace;
+       unsigned long   base, jump, addr_com, addr_dep;
+       volatile struct dbdma_cmd *c1 = pb->ch1_cmd;
+       volatile struct dbdma_cmd *c2 = pb->ch2_cmd;
+
+       interlace = pb->win.interlace;
+       bpp = pb->win.bpp;
+       count = (bpp * ((pb->win.x + pb->win.width > pb->win.swidth) ?
+               (pb->win.swidth - pb->win.x) : pb->win.width));
+       nlines = ((pb->win.y + pb->win.height > pb->win.sheight) ?
+               (pb->win.sheight - pb->win.y) : pb->win.height);
+
+       /* Do video in: */
+
+       /* Preamble commands: */
+       addr_com = virt_to_bus(c1);
+       addr_dep = virt_to_bus(&c1->cmd_dep);
+       tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
+       jump = virt_to_bus(c1+16); /* 14 by cmd_geo_setup() and 2 for padding */
+       if((c1 = cmd_geo_setup(c1, pb->win.width, pb->win.height, interlace,
+                                       bpp, 1, pb)) == NULL) {
+               printk(KERN_WARNING "PlanB: encountered serious problems\n");
+               tab_cmd_dbdma(pb->ch1_cmd + 1, DBDMA_STOP, 0);
+               tab_cmd_dbdma(pb->ch2_cmd + 1, DBDMA_STOP, 0);
+               return;
+       }
+       tab_cmd_store(c1++, addr_com, (unsigned)(DBDMA_NOP | BR_ALWAYS) << 16);
+       tab_cmd_store(c1++, addr_dep, jump);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
+                                                       PLANB_SET(FIELD_SYNC));
+               /* (1) wait for field sync to be set */
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+                                                       PLANB_SET(ODD_FIELD));
+               /* wait for field sync to be cleared */
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+               /* if not odd field, wait until field sync is set again */
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
+               /* assert ch_sync to ch2 */
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
+                                                       PLANB_SET(CH_SYNC));
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+                                                       PLANB_SET(DMA_ABORT));
+
+       base = (pb->frame_buffer_phys + pb->offset + pb->win.y * (pb->win.bpl
+                                       + pb->win.pad) + pb->win.x * bpp);
+
+       if (interlace) {
+               stepsize = 2;
+               jump = virt_to_bus(c1 + (nlines + 1) / 2);
+       } else {
+               stepsize = 1;
+               jump = virt_to_bus(c1 + nlines);
+       }
+
+       /* even field data: */
+       for (i=0; i < nlines; i += stepsize, c1++)
+               tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
+                       count, base + i * (pb->win.bpl + pb->win.pad), jump);
+
+       /* For non-interlaced, we use even fields only */
+       if (!interlace)
+               goto cmd_tab_data_end;
+
+       /* Resync to odd field */
+               /* (2) wait for field sync to be set */
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+                                                       PLANB_SET(ODD_FIELD));
+               /* wait for field sync to be cleared */
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+               /* if not odd field, wait until field sync is set again */
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
+               /* assert ch_sync to ch2 */
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch2.control),
+                                                       PLANB_SET(CH_SYNC));
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+                                                       PLANB_SET(DMA_ABORT));
+       
+       /* odd field data: */
+       jump = virt_to_bus(c1 + nlines / 2);
+       for (i=1; i < nlines; i += stepsize, c1++)
+               tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
+                       base + i * (pb->win.bpl + pb->win.pad), jump);
+
+       /* And jump back to the start */
+cmd_tab_data_end:
+       pb->overlay_last1 = c1; /* keep a pointer to the last command */
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch1_cmd));
+
+       /* Clipmask command buffer */
+
+       /* Preamble commands: */
+       tab_cmd_dbdma(c2++, DBDMA_NOP, 0);
+       tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
+                                                       PLANB_SET(CH_SYNC));
+               /* wait until ch1 asserts ch_sync */
+       tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
+               /* clear ch_sync asserted by ch1 */
+       tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.control),
+                                                       PLANB_CLR(CH_SYNC));
+       tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.wait_sel),
+                                                       PLANB_SET(FIELD_SYNC));
+       tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+                                                       PLANB_SET(ODD_FIELD));
+
+       /* jump to end of even field if appropriate */
+       /* this points to (interlace)? pos. C: pos. B */
+       jump = (interlace) ? virt_to_bus(c2 + (nlines + 1) / 2 + 2):
+                                               virt_to_bus(c2 + nlines + 2);
+               /* if odd field, skip over to odd field clipmasking */
+       tab_cmd_dbdma(c2++, DBDMA_NOP | BR_IFSET, jump);
+
+       /* even field mask: */
+       tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+                                                       PLANB_SET(DMA_ABORT));
+       /* this points to pos. B */
+       jump = (interlace) ? virt_to_bus(c2 + nlines + 1):
+                                               virt_to_bus(c2 + nlines);
+       base = virt_to_bus(pb->mask);
+       for (i=0; i < nlines; i += stepsize, c2++)
+               tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
+                       base + i * 96, jump);
+
+       /* For non-interlaced, we use only even fields */
+       if(!interlace)
+               goto cmd_tab_mask_end;
+
+       /* odd field mask: */
+/* C */        tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch2.br_sel),
+                                                       PLANB_SET(DMA_ABORT));
+       /* this points to pos. B */
+       jump = virt_to_bus(c2 + nlines / 2);
+       base = virt_to_bus(pb->mask);
+       for (i=1; i < nlines; i += 2, c2++)     /* abort if set */
+               tab_cmd_gen(c2, OUTPUT_MORE | KEY_STREAM0 | BR_IFSET, 96,
+                       base + i * 96, jump);
+
+       /* Inform channel 1 and jump back to start */
+cmd_tab_mask_end:
+       /* ok, I just realized this is kind of flawed. */
+       /* this part is reached only after odd field clipmasking. */
+       /* wanna clean up? */
+               /* wait for field sync to be set */
+               /* corresponds to fsync (1) of ch1 */
+/* B */        tab_cmd_dbdma(c2++, DBDMA_NOP | WAIT_IFCLR, 0);
+               /* restart ch1, meant to clear any dead bit or something */
+       tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
+                                                       PLANB_CLR(RUN));
+       tab_cmd_store(c2++, (unsigned)(&pb->planb_base_phys->ch1.control),
+                                                       PLANB_SET(RUN));
+       pb->overlay_last2 = c2; /* keep a pointer to the last command */
+               /* start over even field clipmasking */
+       tab_cmd_dbdma(c2, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->ch2_cmd));
+
+       eieio();
+       return;
+}
+
+/*********************************/
+/* grabdisplay support functions */
+/*********************************/
+
+static int palette2fmt[] = {
+       0,
+       PLANB_GRAY,
+       0,
+       0,
+       0,
+       PLANB_COLOUR32,
+       PLANB_COLOUR15,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+       0,
+};
+#define PLANB_PALETTE_MAX 15
+
+#define SWAP4(x) (((x>>24) & 0x000000ff) |\
+                  ((x>>8)  & 0x0000ff00) |\
+                  ((x<<8)  & 0x00ff0000) |\
+                  ((x<<24) & 0xff000000))
+
+static inline int overlay_is_active(struct planb *pb)
+{
+       unsigned int size = pb->tab_size * sizeof(struct dbdma_cmd);
+       unsigned int caddr = (unsigned)in_le32(&pb->planb_base->ch1.cmdptr);
+
+       return (in_le32(&pb->overlay_last1->cmd_dep) == pb->ch1_cmd_phys)
+                       && (caddr < (pb->ch1_cmd_phys + size))
+                       && (caddr >= (unsigned)pb->ch1_cmd_phys);
+}
+
+static int vgrab(struct planb *pb, struct video_mmap *mp)
+{
+       unsigned int fr = mp->frame;
+       unsigned int format;
+
+       if(pb->fbuffer==NULL) {
+               if(fbuffer_alloc(pb))
+                       return -ENOBUFS;
+       }
+
+       IDEBUG("PlanB: grab %d: %dx%d(%u)\n", pb->grabbing,
+                                               mp->width, mp->height, fr);
+
+       if(pb->grabbing >= MAX_GBUFFERS)
+               return -ENOBUFS;
+       if(fr > (MAX_GBUFFERS - 1) || fr < 0)
+               return -EINVAL;
+       if(mp->height <= 0 || mp->width <= 0)
+               return -EINVAL;
+       if(mp->format < 0 || mp->format >= PLANB_PALETTE_MAX)
+               return -EINVAL;
+       if((format = palette2fmt[mp->format]) == 0)
+               return -EINVAL;
+       if (mp->height * mp->width * format > PLANB_MAX_FBUF) /* format = bpp */
+               return -EINVAL;
+
+       planb_lock(pb);
+       pb->gbuffer[fr] = (unsigned char *)(pb->fbuffer + PLANB_MAX_FBUF * fr);
+       if(mp->width != pb->gwidth[fr] || mp->height != pb->gheight[fr] ||
+                       format != pb->gfmt[fr] || (pb->gnorm_switch[fr])) {
+#ifdef PLANB_GSCANLINE
+               int i;
+#else
+               unsigned int osize = pb->gwidth[fr] * pb->gheight[fr]
+                                                               * pb->gfmt[fr];
+               unsigned int nsize = mp->width * mp->height * format;
+#endif
+
+               IDEBUG("PlanB: gwidth = %d, gheight = %d, mp->format = %u\n",
+                                       mp->width, mp->height, mp->format);
+
+#ifndef PLANB_GSCANLINE
+               if(pb->gnorm_switch[fr])
+                       nsize = 0;
+               if(nsize < osize)
+                       memset((void *)(pb->gbuffer[fr] + nsize), 0,
+                                                               osize - nsize);
+               memset((void *)pb->l_fr_addr[fr], 0, PAGE_SIZE * pb->lnum[fr]);
+#else
+/* XXX TODO */
+/*
+               if(pb->gnorm_switch[fr])
+                       memset((void *)pb->gbuffer[fr], 0,
+                                       pb->gbytes_per_line * pb->gheight[fr]);
+               else {
+                       if(mp->
+                       for(i = 0; i < pb->gheight[fr]; i++) {
+                               memset((void *)(pb->gbuffer[fr]
+                                       + pb->gbytes_per_line * i
+                       }
+               }
+*/
+#endif
+               pb->gwidth[fr] = mp->width;
+               pb->gheight[fr] = mp->height;
+               pb->gfmt[fr] = format;
+               pb->last_cmd[fr] = setup_grab_cmd(fr, pb);
+               planb_pre_capture(fr, pb->gfmt[fr], pb); /* gfmt = bpp */
+               pb->need_pre_capture[fr] = 1;
+               pb->gnorm_switch[fr] = 0;
+       } else
+               pb->need_pre_capture[fr] = 0;
+       pb->frame_stat[fr] = GBUFFER_GRABBING;
+       if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+
+               IDEBUG("PlanB: ch1 inactive, initiating grabbing\n");
+
+               planb_dbdma_stop(&pb->planb_base->ch1);
+               if(pb->need_pre_capture[fr]) {
+
+                       IDEBUG("PlanB: padding pre-capture sequence\n");
+
+                       out_le32 (&pb->planb_base->ch1.cmdptr,
+                                               virt_to_bus(pb->pre_cmd[fr]));
+               } else {
+                       tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
+                       tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
+               /* let's be on the safe side. here is not timing critical. */
+                       tab_cmd_dbdma((pb->cap_cmd[fr] + 1), DBDMA_NOP, 0);
+                       out_le32 (&pb->planb_base->ch1.cmdptr,
+                                               virt_to_bus(pb->cap_cmd[fr]));
+               }
+               planb_dbdma_restart(&pb->planb_base->ch1);
+               pb->last_fr = fr;
+       } else {
+               int i;
+
+               IDEBUG("PlanB: ch1 active, grabbing being queued\n");
+
+               if((pb->last_fr == -1) || ((pb->last_fr == -2) &&
+                                               overlay_is_active(pb))) {
+
+                       IDEBUG("PlanB: overlay is active, grabbing defered\n");
+
+                       tab_cmd_dbdma(pb->last_cmd[fr],
+                                       DBDMA_NOP | BR_ALWAYS,
+                                       virt_to_bus(pb->ch1_cmd));
+                       if(pb->need_pre_capture[fr]) {
+
+                               IDEBUG("PlanB: padding pre-capture sequence\n");
+
+                               tab_cmd_store(pb->pre_cmd[fr],
+                                   virt_to_bus(&pb->overlay_last1->cmd_dep),
+                                               virt_to_bus(pb->ch1_cmd));
+                               eieio();
+                               out_le32 (&pb->overlay_last1->cmd_dep,
+                                               virt_to_bus(pb->pre_cmd[fr]));
+                       } else {
+                               tab_cmd_store(pb->cap_cmd[fr],
+                                   virt_to_bus(&pb->overlay_last1->cmd_dep),
+                                               virt_to_bus(pb->ch1_cmd));
+                               tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+                                                               DBDMA_NOP, 0);
+                               eieio();
+                               out_le32 (&pb->overlay_last1->cmd_dep,
+                                               virt_to_bus(pb->cap_cmd[fr]));
+                       }
+                       for(i = 0; overlay_is_active(pb) && i < 999; i++)
+                               IDEBUG("PlanB: waiting for overlay done\n");
+                       tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
+                       pb->prev_last_fr = fr;
+                       pb->last_fr = -2;
+               } else if(pb->last_fr == -2) {
+
+                       IDEBUG("PlanB: mixed mode detected, grabbing"
+                               " will be done before activating overlay\n");
+
+                       tab_cmd_dbdma(pb->ch1_cmd, DBDMA_NOP, 0);
+                       if(pb->need_pre_capture[fr]) {
+
+                               IDEBUG("PlanB: padding pre-capture sequence\n");
+
+                               tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
+                                               DBDMA_NOP | BR_ALWAYS,
+                                               virt_to_bus(pb->pre_cmd[fr]));
+                               eieio();
+                       } else {
+                               tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
+                               if(pb->gwidth[pb->prev_last_fr] !=
+                                                               pb->gwidth[fr]
+                                       || pb->gheight[pb->prev_last_fr] !=
+                                                               pb->gheight[fr]
+                                       || pb->gfmt[pb->prev_last_fr] !=
+                                                               pb->gfmt[fr])
+                                       tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+                                                               DBDMA_NOP, 0);
+                               else
+                                       tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+                                           DBDMA_NOP | BR_ALWAYS,
+                                           virt_to_bus(pb->cap_cmd[fr] + 16));
+                               tab_cmd_dbdma(pb->last_cmd[pb->prev_last_fr],
+                                               DBDMA_NOP | BR_ALWAYS,
+                                               virt_to_bus(pb->cap_cmd[fr]));
+                               eieio();
+                       }
+                       tab_cmd_dbdma(pb->last_cmd[fr],
+                                       DBDMA_NOP | BR_ALWAYS,
+                                       virt_to_bus(pb->ch1_cmd));
+                       eieio();
+                       pb->prev_last_fr = fr;
+                       pb->last_fr = -2;
+               } else {
+
+                       IDEBUG("PlanB: active grabbing session detected\n");
+
+                       if(pb->need_pre_capture[fr]) {
+
+                               IDEBUG("PlanB: padding pre-capture sequence\n");
+
+                               tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+                                               DBDMA_NOP | BR_ALWAYS,
+                                               virt_to_bus(pb->pre_cmd[fr]));
+                               eieio();
+                       } else {
+                               tab_cmd_dbdma(pb->last_cmd[fr], DBDMA_STOP, 0);
+                               tab_cmd_dbdma(pb->cap_cmd[fr], DBDMA_NOP, 0);
+                               if(pb->gwidth[pb->last_fr] != pb->gwidth[fr]
+                                       || pb->gheight[pb->last_fr] !=
+                                                               pb->gheight[fr]
+                                       || pb->gfmt[pb->last_fr] !=
+                                                               pb->gfmt[fr])
+                                       tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+                                                               DBDMA_NOP, 0);
+                               else
+                                       tab_cmd_dbdma((pb->cap_cmd[fr] + 1),
+                                           DBDMA_NOP | BR_ALWAYS,
+                                           virt_to_bus(pb->cap_cmd[fr] + 16));
+                               tab_cmd_dbdma(pb->last_cmd[pb->last_fr],
+                                               DBDMA_NOP | BR_ALWAYS,
+                                               virt_to_bus(pb->cap_cmd[fr]));
+                               eieio();
+                       }
+                       pb->last_fr = fr;
+               }
+               if(!(ACTIVE & in_le32(&pb->planb_base->ch1.status))) {
+
+                       IDEBUG("PlanB: became inactive in the mean time..."
+                               "reactivating\n");
+
+                       planb_dbdma_stop(&pb->planb_base->ch1);
+                       out_le32 (&pb->planb_base->ch1.cmdptr,
+                                               virt_to_bus(pb->cap_cmd[fr]));
+                       planb_dbdma_restart(&pb->planb_base->ch1);
+               }
+       }
+       pb->grabbing++;
+       planb_unlock(pb);
+
+       return 0;
+}
+
+static void planb_pre_capture(int fr, int bpp, struct planb *pb)
+{
+       volatile struct dbdma_cmd *c1 = pb->pre_cmd[fr];
+       int interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
+
+       tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
+       if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
+                                               bpp, 0, pb)) == NULL) {
+               printk(KERN_WARNING "PlanB: encountered some problems\n");
+               tab_cmd_dbdma(pb->pre_cmd[fr] + 1, DBDMA_STOP, 0);
+               return;
+       }
+       /* Sync to even field */
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
+               PLANB_SET(FIELD_SYNC));
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(ODD_FIELD));
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
+       tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(DMA_ABORT));
+       /* For non-interlaced, we use even fields only */
+       if (pb->gheight[fr] <= pb->maxlines/2)
+               goto cmd_tab_data_end;
+       /* Sync to odd field */
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(ODD_FIELD));
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(DMA_ABORT));
+cmd_tab_data_end:
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(pb->cap_cmd[fr]));
+
+       eieio();
+}
+
+static volatile struct dbdma_cmd *setup_grab_cmd(int fr, struct planb *pb)
+{
+       int             i, bpp, count, nlines, stepsize, interlace;
+#ifdef PLANB_GSCANLINE
+       int             scanline;
+#else
+       int             nlpp, leftover1;
+       unsigned long   base;
+#endif
+       unsigned long   jump;
+       unsigned char   *vaddr;
+       volatile struct dbdma_cmd *c1;
+       volatile struct dbdma_cmd *jump_addr;
+
+       c1 = pb->cap_cmd[fr];
+       interlace = (pb->gheight[fr] > pb->maxlines/2)? 1: 0;
+       bpp = pb->gfmt[fr];     /* gfmt = bpp */
+       count = bpp * pb->gwidth[fr];
+       nlines = pb->gheight[fr];
+#ifdef PLANB_GSCANLINE
+       scanline = pb->gbytes_per_line;
+#else
+       pb->lsize[fr] = count;
+       pb->lnum[fr] = 0;
+#endif
+
+       /* Do video in: */
+
+       /* Preamble commands: */
+       tab_cmd_dbdma(c1++, DBDMA_NOP, 0);
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, virt_to_bus(c1 + 16)); c1++;
+       if((c1 = cmd_geo_setup(c1, pb->gwidth[fr], pb->gheight[fr], interlace,
+                                               bpp, 0, pb)) == NULL) {
+               printk(KERN_WARNING "PlanB: encountered serious problems\n");
+               tab_cmd_dbdma(pb->cap_cmd[fr] + 1, DBDMA_STOP, 0);
+               return (pb->cap_cmd[fr] + 2);
+       }
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.wait_sel),
+               PLANB_SET(FIELD_SYNC));
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(ODD_FIELD));
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFSET, virt_to_bus(c1-3)); c1++;
+       tab_cmd_dbdma(c1++, DBDMA_NOP | INTR_ALWAYS, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(DMA_ABORT));
+
+       if (interlace) {
+               stepsize = 2;
+               jump_addr = c1 + TAB_FACTOR * (nlines + 1) / 2;
+       } else {
+               stepsize = 1;
+               jump_addr = c1 + TAB_FACTOR * nlines;
+       }
+       jump = virt_to_bus(jump_addr);
+
+       /* even field data: */
+
+       vaddr = pb->gbuffer[fr];
+#ifdef PLANB_GSCANLINE
+       for (i = 0; i < nlines; i += stepsize) {
+               tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+                               vmalloc_to_bus(vaddr + i * scanline), jump);
+       }
+#else
+       i = 0;
+       leftover1 = 0;
+       do {
+           int j;
+
+           base = vmalloc_to_bus((void*)vaddr);
+           nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
+           for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
+               tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET,
+                         count, base + count * j * stepsize + leftover1, jump);
+           if(i < nlines) {
+               int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
+
+               if(lov0 == 0)
+                   leftover1 = 0;
+               else {
+                   if(lov0 >= count) {
+                       tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count, base
+                               + count * nlpp * stepsize + leftover1, jump);
+                   } else {
+                       pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count * nlpp
+                                                       * stepsize + leftover1;
+                       tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+                               vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE
+                                                       * pb->lnum[fr]), jump);
+                       if(++pb->lnum[fr] > MAX_LNUM)
+                               pb->lnum[fr]--;
+                   }
+                   leftover1 = count * stepsize - lov0;
+                   i += stepsize;
+               }
+           }
+           vaddr += PAGE_SIZE;
+       } while(i < nlines);
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
+       c1 = jump_addr;
+#endif /* PLANB_GSCANLINE */
+
+       /* For non-interlaced, we use even fields only */
+       if (!interlace)
+               goto cmd_tab_data_end;
+
+       /* Sync to odd field */
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFCLR, 0);
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(ODD_FIELD));
+       tab_cmd_dbdma(c1++, DBDMA_NOP | WAIT_IFSET, 0);
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_IFCLR, virt_to_bus(c1-3)); c1++;
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->ch1.br_sel),
+               PLANB_SET(DMA_ABORT));
+       
+       /* odd field data: */
+       jump_addr = c1 + TAB_FACTOR * nlines / 2;
+       jump = virt_to_bus(jump_addr);
+#ifdef PLANB_GSCANLINE
+       for (i = 1; i < nlines; i += stepsize) {
+               tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+                               vmalloc_to_bus(vaddr + i * scanline), jump);
+       }
+#else
+       i = 1;
+       leftover1 = 0;
+       vaddr = pb->gbuffer[fr];
+       if(nlines <= 1)
+           goto skip;
+       do {
+           int j;
+
+           base = vmalloc_to_bus((void*)vaddr);
+           nlpp = (PAGE_SIZE - leftover1) / count / stepsize;
+           if(leftover1 >= count) {
+               tab_cmd_gen(c1++, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
+                                               base + leftover1 - count, jump);
+               i += stepsize;
+           }
+           for(j = 0; j < nlpp && i < nlines; j++, i += stepsize, c1++)
+               tab_cmd_gen(c1, INPUT_MORE | KEY_STREAM0 | BR_IFSET, count,
+                       base + count * (j * stepsize + 1) + leftover1, jump);
+           if(i < nlines) {
+               int lov0 = PAGE_SIZE - count * nlpp * stepsize - leftover1;
+
+               if(lov0 == 0)
+                   leftover1 = 0;
+               else {
+                   if(lov0 > count) {
+                       pb->l_to_addr[fr][pb->lnum[fr]] = vaddr + count
+                                       * (nlpp * stepsize + 1) + leftover1;
+                       tab_cmd_gen(c1++, INPUT_MORE | BR_IFSET, count,
+                               vmalloc_to_bus(pb->l_fr_addr[fr] + PAGE_SIZE
+                                                       * pb->lnum[fr]), jump);
+                       if(++pb->lnum[fr] > MAX_LNUM)
+                               pb->lnum[fr]--;
+                       i += stepsize;
+                   }
+                   leftover1 = count * stepsize - lov0;
+               }
+           }
+           vaddr += PAGE_SIZE;
+       } while(i < nlines);
+skip:
+       tab_cmd_dbdma(c1, DBDMA_NOP | BR_ALWAYS, jump);
+       c1 = jump_addr;
+#endif /* PLANB_GSCANLINE */
+
+cmd_tab_data_end:
+       tab_cmd_store(c1++, (unsigned)(&pb->planb_base_phys->intr_stat),
+                       (fr << 2) | PLANB_FRM_IRQ | PLANB_GEN_IRQ);
+       /* stop it */
+       tab_cmd_dbdma(c1, DBDMA_STOP, 0);
+
+       eieio();
+       return c1;
+}
+
+static void planb_irq(int irq, void *dev_id, struct pt_regs * regs)
+{
+       unsigned int stat, astat;
+       struct planb *pb = (struct planb *)dev_id;
+
+       IDEBUG("PlanB: planb_irq()\n");
+
+       /* get/clear interrupt status bits */
+       stat = in_le32(&pb->planb_base->intr_stat);
+       astat = stat & pb->intr_mask;
+       out_le32(&pb->planb_base->intr_stat, PLANB_IRQ_CMD_MASK
+                                       & ~astat & stat & ~PLANB_GEN_IRQ);
+
+       if(astat & PLANB_FRM_IRQ) {
+               unsigned int fr = stat >> 2;
+#ifndef PLANB_GSCANLINE
+               int i;
+#endif
+               IDEBUG("PlanB: PLANB_FRM_IRQ\n");
+
+               pb->gcount++;
+
+               IDEBUG("PlanB: grab %d: fr = %d, gcount = %d\n",
+                               pb->grabbing, fr, pb->gcount);
+#ifndef PLANB_GSCANLINE
+               IDEBUG("PlanB: %d * %d bytes are being copied over\n",
+                               pb->lnum[fr], pb->lsize[fr]);
+               for(i = 0; i < pb->lnum[fr]; i++)
+                       memcpy(pb->l_to_addr[fr][i], pb->l_fr_addr[fr]
+                                       + PAGE_SIZE * i, pb->lsize[fr]);
+#endif
+               pb->frame_stat[fr] = GBUFFER_DONE;
+               pb->grabbing--;
+               wake_up_interruptible(&pb->capq);
+               return;
+       }
+       /* incorrect interrupts? */
+       pb->intr_mask = PLANB_CLR_IRQ;
+       out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
+       printk(KERN_ERR "PlanB: IRQ lockup, cleared intrrupts"
+                                                       " unconditionally\n");
+}
+
+/*******************************
+ * Device Operations functions *
+ *******************************/
+
+static int planb_open(struct video_device *dev, int mode)
+{
+       struct planb *pb = (struct planb *)dev;
+
+       if (pb->user == 0) {
+               int err;
+               if((err = planb_prepare_open(pb)) != 0)
+                       return err;
+       }
+       pb->user++;
+
+       DEBUG("PlanB: device opened\n");
+
+       MOD_INC_USE_COUNT;
+       return 0;   
+}
+
+static void planb_close(struct video_device *dev)
+{
+       struct planb *pb = (struct planb *)dev;
+
+       if(pb->user < 1) /* ??? */
+               return;
+       planb_lock(pb);
+       if (pb->user == 1) {
+               if (pb->overlay) {
+                       planb_dbdma_stop(&pb->planb_base->ch2);
+                       planb_dbdma_stop(&pb->planb_base->ch1);
+                       pb->overlay = 0;
+               }
+               planb_prepare_close(pb);
+       }
+       pb->user--;
+       planb_unlock(pb);
+
+       DEBUG("PlanB: device closed\n");
+
+       MOD_DEC_USE_COUNT;  
+}
+
+static long planb_read(struct video_device *v, char *buf, unsigned long count,
+                               int nonblock)
+{
+       DEBUG("planb: read request\n");
+       return -EINVAL;
+}
+
+static long planb_write(struct video_device *v, const char *buf,
+                               unsigned long count, int nonblock)
+{
+       DEBUG("planb: write request\n");
+       return -EINVAL;
+}
+
+static int planb_ioctl(struct video_device *dev, unsigned int cmd, void *arg)
+{
+       struct planb *pb=(struct planb *)dev;
+       
+       switch (cmd)
+       {       
+               case VIDIOCGCAP:
+               {
+                       struct video_capability b;
+
+                       DEBUG("PlanB: IOCTL VIDIOCGCAP\n");
+
+                       strcpy (b.name, pb->video_dev.name);
+                       b.type = VID_TYPE_OVERLAY | VID_TYPE_CLIPPING |
+                                VID_TYPE_FRAMERAM | VID_TYPE_SCALES |
+                                VID_TYPE_CAPTURE;
+                       b.channels = 2; /* composite & svhs */
+                       b.audios = 0;
+                       b.maxwidth = PLANB_MAXPIXELS;
+                        b.maxheight = PLANB_MAXLINES;
+                        b.minwidth = 32; /* wild guess */
+                        b.minheight = 32;
+                        if (copy_to_user(arg,&b,sizeof(b)))
+                                return -EFAULT;
+                       return 0;
+               }
+               case VIDIOCSFBUF:
+               {
+                        struct video_buffer v;
+                       unsigned short bpp;
+                       unsigned int fmt;
+
+                       DEBUG("PlanB: IOCTL VIDIOCSFBUF\n");
+
+                        if (!capable(CAP_SYS_ADMIN))
+                                return -EPERM;
+                        if (copy_from_user(&v, arg,sizeof(v)))
+                                return -EFAULT;
+                       planb_lock(pb);
+                       switch(v.depth) {
+                       case 8:
+                               bpp = 1;
+                               fmt = PLANB_GRAY;
+                               break;
+                       case 15:
+                       case 16:
+                               bpp = 2;
+                               fmt = PLANB_COLOUR15;
+                               break;
+                       case 24:
+                       case 32:
+                               bpp = 4;
+                               fmt = PLANB_COLOUR32;
+                               break;
+                       default:
+                               planb_unlock(pb);
+                                return -EINVAL;
+                       }
+                       if (bpp * v.width > v.bytesperline) {
+                               planb_unlock(pb);
+                               return -EINVAL;
+                       }
+                       pb->win.bpp = bpp;
+                       pb->win.color_fmt = fmt;
+                       pb->frame_buffer_phys = (unsigned long) v.base;
+                       pb->win.sheight = v.height;
+                       pb->win.swidth = v.width;
+                       pb->picture.depth = pb->win.depth = v.depth;
+                       pb->win.bpl = pb->win.bpp * pb->win.swidth;
+                       pb->win.pad = v.bytesperline - pb->win.bpl;
+
+                        DEBUG("PlanB: Display at %p is %d by %d, bytedepth %d,"
+                               " bpl %d (+ %d)\n", v.base, v.width,v.height,
+                               pb->win.bpp, pb->win.bpl, pb->win.pad);
+
+                       pb->cmd_buff_inited = 0;
+                       if(pb->overlay) {
+                               suspend_overlay(pb);
+                               fill_cmd_buff(pb);
+                               resume_overlay(pb);
+                       }
+                       planb_unlock(pb);
+                       return 0;               
+               }
+               case VIDIOCGFBUF:
+               {
+                        struct video_buffer v;
+
+                       DEBUG("PlanB: IOCTL VIDIOCGFBUF\n");
+
+                       v.base = (void *)pb->frame_buffer_phys;
+                       v.height = pb->win.sheight;
+                       v.width = pb->win.swidth;
+                       v.depth = pb->win.depth;
+                       v.bytesperline = pb->win.bpl + pb->win.pad;
+                       if (copy_to_user(arg, &v, sizeof(v)))
+                                return -EFAULT;
+                       return 0;
+               }
+               case VIDIOCCAPTURE:
+               {
+                       int i;
+
+                        if(copy_from_user(&i, arg, sizeof(i)))
+                                return -EFAULT;
+                       if(i==0) {
+                               DEBUG("PlanB: IOCTL VIDIOCCAPTURE Stop\n");
+
+                               if (!(pb->overlay))
+                                       return 0;
+                               planb_lock(pb);
+                               pb->overlay = 0;
+                               overlay_stop(pb);
+                               planb_unlock(pb);
+                       } else {
+                               DEBUG("PlanB: IOCTL VIDIOCCAPTURE Start\n");
+
+                               if (pb->frame_buffer_phys == 0 ||
+                                         pb->win.width == 0 ||
+                                         pb->win.height == 0)
+                                       return -EINVAL;
+                               if (pb->overlay)
+                                       return 0;
+                               planb_lock(pb);
+                               pb->overlay = 1;
+                               if(!(pb->cmd_buff_inited))
+                                       fill_cmd_buff(pb);
+                               overlay_start(pb);
+                               planb_unlock(pb);
+                       }
+                       return 0;
+               }
+               case VIDIOCGCHAN:
+               {
+                       struct video_channel v;
+
+                       DEBUG("PlanB: IOCTL VIDIOCGCHAN\n");
+
+                       if(copy_from_user(&v, arg,sizeof(v)))
+                               return -EFAULT;
+                       v.flags = 0;
+                       v.tuners = 0;
+                       v.type = VIDEO_TYPE_CAMERA;
+                       v.norm = pb->win.norm;
+                       switch(v.channel)
+                       {
+                       case 0:
+                               strcpy(v.name,"Composite");
+                               break;
+                       case 1:
+                               strcpy(v.name,"SVHS");
+                               break;
+                       default:
+                               return -EINVAL;
+                               break;
+                       }
+                       if(copy_to_user(arg,&v,sizeof(v)))
+                               return -EFAULT;
+
+                       return 0;
+               }
+               case VIDIOCSCHAN:
+               {
+                       struct video_channel v;
+
+                       DEBUG("PlanB: IOCTL VIDIOCSCHAN\n");
+
+                       if(copy_from_user(&v, arg, sizeof(v)))
+                               return -EFAULT;
+
+                       if (v.norm != pb->win.norm) {
+                               int i, maxlines;
+
+                               switch (v.norm)
+                               {
+                               case VIDEO_MODE_PAL:
+                               case VIDEO_MODE_SECAM:
+                                       maxlines = PLANB_MAXLINES;
+                                       break;
+                               case VIDEO_MODE_NTSC:
+                                       maxlines = PLANB_NTSC_MAXLINES;
+                                       break;
+                               default:
+                                       return -EINVAL;
+                                       break;
+                               }
+                               planb_lock(pb);
+                               /* empty the grabbing queue */
+                               while(pb->grabbing)
+                                       interruptible_sleep_on(&pb->capq);
+                               pb->maxlines = maxlines;
+                               pb->win.norm = v.norm;
+                               /* Stop overlay if running */
+                               suspend_overlay(pb);
+                               for(i = 0; i < MAX_GBUFFERS; i++)
+                                       pb->gnorm_switch[i] = 1;
+                               /* I know it's an overkill, but.... */
+                               fill_cmd_buff(pb);
+                               /* ok, now init it accordingly */
+                               saa_init_regs (pb);
+                               /* restart overlay if it was running */
+                               resume_overlay(pb);
+                               planb_unlock(pb);
+                       }
+
+                       switch(v.channel)
+                       {
+                       case 0: /* Composite    */
+                               saa_set (SAA7196_IOCC,
+                                       ((saa_regs[pb->win.norm][SAA7196_IOCC] &
+                                         ~7) | 3), pb);
+                               break;
+                       case 1: /* SVHS         */
+                               saa_set (SAA7196_IOCC,
+                                       ((saa_regs[pb->win.norm][SAA7196_IOCC] &
+                                         ~7) | 4), pb);
+                               break;
+                       default:
+                               return -EINVAL;
+                               break;
+                       }
+
+                       return 0;
+               }
+               case VIDIOCGPICT:
+               {
+                       struct video_picture vp = pb->picture;
+
+                       DEBUG("PlanB: IOCTL VIDIOCGPICT\n");
+
+                       switch(pb->win.color_fmt) {
+                       case PLANB_GRAY:
+                               vp.palette = VIDEO_PALETTE_GREY;
+                       case PLANB_COLOUR15:
+                               vp.palette = VIDEO_PALETTE_RGB555;
+                               break;
+                       case PLANB_COLOUR32:
+                               vp.palette = VIDEO_PALETTE_RGB32;
+                               break;
+                       default:
+                               vp.palette = 0;
+                               break;
+                       }
+
+                       if(copy_to_user(arg,&vp,sizeof(vp)))
+                               return -EFAULT;
+                       return 0;
+               }
+               case VIDIOCSPICT:
+               {
+                       struct video_picture vp;
+
+                       DEBUG("PlanB: IOCTL VIDIOCSPICT\n");
+
+                       if(copy_from_user(&vp,arg,sizeof(vp)))
+                               return -EFAULT;
+                       pb->picture = vp;
+                       /* Should we do sanity checks here? */
+                       saa_set (SAA7196_BRIG, (unsigned char)
+                           ((pb->picture.brightness) >> 8), pb);
+                       saa_set (SAA7196_HUEC, (unsigned char)
+                           ((pb->picture.hue) >> 8) ^ 0x80, pb);
+                       saa_set (SAA7196_CSAT, (unsigned char)
+                           ((pb->picture.colour) >> 9), pb);
+                       saa_set (SAA7196_CONT, (unsigned char)
+                           ((pb->picture.contrast) >> 9), pb);
+
+                       return 0;
+               }
+               case VIDIOCSWIN:
+               {
+                       struct video_window     vw;
+                       struct video_clip       clip;
+                       int                     i;
+                       
+                       DEBUG("PlanB: IOCTL VIDIOCSWIN\n");
+
+                       if(copy_from_user(&vw,arg,sizeof(vw)))
+                               return -EFAULT;
+
+                       planb_lock(pb);
+                       /* Stop overlay if running */
+                       suspend_overlay(pb);
+                       pb->win.interlace = (vw.height > pb->maxlines/2)? 1: 0;
+                       if (pb->win.x != vw.x ||
+                           pb->win.y != vw.y ||
+                           pb->win.width != vw.width ||
+                           pb->win.height != vw.height ||
+                           !pb->cmd_buff_inited) {
+                               pb->win.x = vw.x;
+                               pb->win.y = vw.y;
+                               pb->win.width = vw.width;
+                               pb->win.height = vw.height;
+                               fill_cmd_buff(pb);
+                       }
+                       /* Reset clip mask */
+                       memset ((void *) pb->mask, 0xff, (pb->maxlines
+                                       * ((PLANB_MAXPIXELS + 7) & ~7)) / 8);
+                       /* Add any clip rects */
+                       for (i = 0; i < vw.clipcount; i++) {
+                               if (copy_from_user(&clip, vw.clips + i,
+                                               sizeof(struct video_clip)))
+                                       return -EFAULT;
+                               add_clip(pb, &clip);
+                       }
+                       /* restart overlay if it was running */
+                       resume_overlay(pb);
+                       planb_unlock(pb);
+                       return 0;
+               }
+               case VIDIOCGWIN:
+               {
+                       struct video_window vw;
+
+                       DEBUG("PlanB: IOCTL VIDIOCGWIN\n");
+
+                       vw.x=pb->win.x;
+                       vw.y=pb->win.y;
+                       vw.width=pb->win.width;
+                       vw.height=pb->win.height;
+                       vw.chromakey=0;
+                       vw.flags=0;
+                       if(pb->win.interlace)
+                               vw.flags|=VIDEO_WINDOW_INTERLACE;
+                       if(copy_to_user(arg,&vw,sizeof(vw)))
+                               return -EFAULT;
+                       return 0;
+               }
+               case VIDIOCSYNC: {
+                       int i;
+
+                       IDEBUG("PlanB: IOCTL VIDIOCSYNC\n");
+
+                       if(copy_from_user((void *)&i,arg,sizeof(int)))
+                               return -EFAULT;
+
+                       IDEBUG("PlanB: sync to frame %d\n", i);
+
+                        if(i > (MAX_GBUFFERS - 1) || i < 0)
+                                return -EINVAL;
+chk_grab:
+                        switch (pb->frame_stat[i]) {
+                        case GBUFFER_UNUSED:
+                                return -EINVAL;
+                       case GBUFFER_GRABBING:
+                               IDEBUG("PlanB: waiting for grab"
+                                                       " done (%d)\n", i);
+                               interruptible_sleep_on(&pb->capq);
+                               goto chk_grab;
+                        case GBUFFER_DONE:
+                                pb->frame_stat[i] = GBUFFER_UNUSED;
+                                break;
+                        }
+                        return 0;
+               }
+
+               case VIDIOCMCAPTURE:
+               {
+                        struct video_mmap vm;
+                       volatile unsigned int status;
+
+                       IDEBUG("PlanB: IOCTL VIDIOCMCAPTURE\n");
+
+                       if(copy_from_user((void *) &vm,(void *)arg,sizeof(vm)))
+                               return -EFAULT;
+                        status = pb->frame_stat[vm.frame];
+                        if (status != GBUFFER_UNUSED)
+                                return -EBUSY;
+
+                       return vgrab(pb, &vm);
+               }
+               
+               case VIDIOCGMBUF:
+               {
+                       int i;
+                       struct video_mbuf vm;
+
+                       DEBUG("PlanB: IOCTL VIDIOCGMBUF\n");
+
+                       memset(&vm, 0 , sizeof(vm));
+                       vm.size = PLANB_MAX_FBUF * MAX_GBUFFERS;
+                       vm.frames = MAX_GBUFFERS;
+                       for(i = 0; i<MAX_GBUFFERS; i++)
+                               vm.offsets[i] = PLANB_MAX_FBUF * i;
+                       if(copy_to_user((void *)arg, (void *)&vm, sizeof(vm)))
+                               return -EFAULT;
+                       return 0;
+               }
+               
+               case PLANBIOCGSAAREGS:
+               {
+                       struct planb_saa_regs preg;
+
+                       DEBUG("PlanB: IOCTL PLANBIOCGSAAREGS\n");
+
+                       if(copy_from_user(&preg, arg, sizeof(preg)))
+                               return -EFAULT;
+                       if(preg.addr >= SAA7196_NUMREGS)
+                               return -EINVAL;
+                       preg.val = saa_regs[pb->win.norm][preg.addr];
+                       if(copy_to_user((void *)arg, (void *)&preg,
+                                                               sizeof(preg)))
+                               return -EFAULT;
+                       return 0;
+               }
+               
+               case PLANBIOCSSAAREGS:
+               {
+                       struct planb_saa_regs preg;
+
+                       DEBUG("PlanB: IOCTL PLANBIOCSSAAREGS\n");
+
+                       if(copy_from_user(&preg, arg, sizeof(preg)))
+                               return -EFAULT;
+                       if(preg.addr >= SAA7196_NUMREGS)
+                               return -EINVAL;
+                       saa_set (preg.addr, preg.val, pb);
+                       return 0;
+               }
+               
+               case PLANBIOCGSTAT:
+               {
+                       struct planb_stat_regs pstat;
+
+                       DEBUG("PlanB: IOCTL PLANBIOCGSTAT\n");
+
+                       pstat.ch1_stat = in_le32(&pb->planb_base->ch1.status);
+                       pstat.ch2_stat = in_le32(&pb->planb_base->ch2.status);
+                       pstat.saa_stat0 = saa_status(0, pb);
+                       pstat.saa_stat1 = saa_status(1, pb);
+
+                       if(copy_to_user((void *)arg, (void *)&pstat,
+                                                       sizeof(pstat)))
+                               return -EFAULT;
+                       return 0;
+               }
+               
+               case PLANBIOCSMODE: {
+                       int v;
+
+                       DEBUG("PlanB: IOCTL PLANBIOCSMODE\n");
+
+                       if(copy_from_user(&v, arg, sizeof(v)))
+                               return -EFAULT;
+
+                       switch(v)
+                       {
+                       case PLANB_TV_MODE:
+                               saa_set (SAA7196_STDC,
+                                       (saa_regs[pb->win.norm][SAA7196_STDC] &
+                                         0x7f), pb);
+                               break;
+                       case PLANB_VTR_MODE:
+                               saa_set (SAA7196_STDC,
+                                       (saa_regs[pb->win.norm][SAA7196_STDC] |
+                                         0x80), pb);
+                               break;
+                       default:
+                               return -EINVAL;
+                               break;
+                       }
+                       pb->win.mode = v;
+                       return 0;
+               }
+               case PLANBIOCGMODE: {
+                       int v=pb->win.mode;
+
+                       DEBUG("PlanB: IOCTL PLANBIOCGMODE\n");
+
+                       if(copy_to_user(arg,&v,sizeof(v)))
+                               return -EFAULT;
+                       return 0;
+               }
+#ifdef PLANB_GSCANLINE
+               case PLANBG_GRAB_BPL: {
+                       int v=pb->gbytes_per_line;
+
+                       DEBUG("PlanB: IOCTL PLANBG_GRAB_BPL\n");
+
+                       if(copy_to_user(arg,&v,sizeof(v)))
+                               return -EFAULT;
+                       return 0;
+               }
+#endif /* PLANB_GSCANLINE */
+               case PLANB_INTR_DEBUG: {
+                       int i;
+
+                       DEBUG("PlanB: IOCTL PLANB_INTR_DEBUG\n");
+
+                       if(copy_from_user(&i, arg, sizeof(i)))
+                               return -EFAULT;
+
+                       /* avoid hang ups all together */
+                       for (i = 0; i < MAX_GBUFFERS; i++) {
+                               if(pb->frame_stat[i] == GBUFFER_GRABBING) {
+                                       pb->frame_stat[i] = GBUFFER_DONE;
+                               }
+                       }
+                       if(pb->grabbing)
+                               pb->grabbing--;
+                       wake_up_interruptible(&pb->capq);
+                       return 0;
+               }
+               case PLANB_INV_REGS: {
+                       int i;
+                       struct planb_any_regs any;
+
+                       DEBUG("PlanB: IOCTL PLANB_INV_REGS\n");
+
+                       if(copy_from_user(&any, arg, sizeof(any)))
+                               return -EFAULT;
+                       if(any.offset < 0 || any.offset + any.bytes > 0x400)
+                               return -EINVAL;
+                       if(any.bytes > 128)
+                               return -EINVAL;
+                       for (i = 0; i < any.bytes; i++) {
+                               any.data[i] =
+                                       in_8((unsigned char *)pb->planb_base
+                                                       + any.offset + i);
+                       }
+                       if(copy_to_user(arg,&any,sizeof(any)))
+                               return -EFAULT;
+                       return 0;
+               }
+               default:
+               {
+                       DEBUG("PlanB: Unimplemented IOCTL\n");
+                       return -ENOIOCTLCMD;
+               }
+       /* Some IOCTLs are currently unsupported on PlanB */
+               case VIDIOCGTUNER: {
+               DEBUG("PlanB: IOCTL VIDIOCGTUNER\n");
+                       goto unimplemented; }
+               case VIDIOCSTUNER: {
+               DEBUG("PlanB: IOCTL VIDIOCSTUNER\n");
+                       goto unimplemented; }
+               case VIDIOCSFREQ: {
+               DEBUG("PlanB: IOCTL VIDIOCSFREQ\n");
+                       goto unimplemented; }
+               case VIDIOCGFREQ: {
+               DEBUG("PlanB: IOCTL VIDIOCGFREQ\n");
+                       goto unimplemented; }
+               case VIDIOCKEY: {
+               DEBUG("PlanB: IOCTL VIDIOCKEY\n");
+                       goto unimplemented; }
+               case VIDIOCSAUDIO: {
+               DEBUG("PlanB: IOCTL VIDIOCSAUDIO\n");
+                       goto unimplemented; }
+               case VIDIOCGAUDIO: {
+               DEBUG("PlanB: IOCTL VIDIOCGAUDIO\n");
+                       goto unimplemented; }
+unimplemented:
+               DEBUG("       Unimplemented\n");
+                       return -ENOIOCTLCMD;
+       }
+       return 0;
+}
+
+/*
+ *     This maps the vmalloced and reserved fbuffer to user space.
+ *
+ *  FIXME: 
+ *  - PAGE_READONLY should suffice!?
+ *  - remap_page_range is kind of inefficient for page by page remapping.
+ *    But e.g. pte_alloc() does not work in modules ... :-(
+ */
+static int planb_mmap(struct video_device *dev, const char *adr, unsigned long size)
+{
+       struct planb *pb=(struct planb *)dev;
+        unsigned long start=(unsigned long) adr;
+       unsigned long page;
+       void *pos;
+
+       if (size>MAX_GBUFFERS*PLANB_MAX_FBUF)
+               return -EINVAL;
+       if (!pb->fbuffer)
+       {
+               if(fbuffer_alloc(pb))
+                       return -EINVAL;
+       }
+       pos = (void *)pb->fbuffer;
+       while (size > 0) 
+       {
+               page = vmalloc_to_phys(pos);
+               if (remap_page_range(start, page, PAGE_SIZE, PAGE_SHARED))
+                       return -EAGAIN;
+               start+=PAGE_SIZE;
+               pos+=PAGE_SIZE;
+               size-=PAGE_SIZE;    
+       }
+       return 0;
+}
+
+/* This gets called upon device registration */
+/* we could do some init here */
+static int planb_init_done(struct video_device *dev)
+{
+       return 0;
+}
+
+static struct video_device planb_template=
+{
+       PLANB_DEVICE_NAME,
+       VID_TYPE_OVERLAY,
+       VID_HARDWARE_PLANB,
+       planb_open,
+       planb_close,
+       planb_read,
+       planb_write,
+#if LINUX_VERSION_CODE >= 0x020100
+       NULL,   /* poll */
+#endif
+       planb_ioctl,
+       planb_mmap,     /* mmap? */
+       planb_init_done,
+       NULL,   /* pointer to private data */
+       0,
+       0
+};
+
+static int init_planb(struct planb *pb)
+{
+       unsigned char saa_rev;
+       int i, result;
+
+       memset ((void *) &pb->win, 0, sizeof (struct planb_window));
+       /* Simple sanity check */
+       if(def_norm >= NUM_SUPPORTED_NORM || def_norm < 0) {
+               printk(KERN_ERR "PlanB: Option(s) invalid\n");
+               return -2;
+       }
+       pb->win.norm = def_norm;
+       pb->win.mode = PLANB_TV_MODE;   /* TV mode */
+       pb->win.interlace=1;
+       pb->win.x=0;
+       pb->win.y=0;
+       pb->win.width=768; /* 640 */
+       pb->win.height=576; /* 480 */
+       pb->maxlines=576;
+#if 0
+       btv->win.cropwidth=768; /* 640 */
+       btv->win.cropheight=576; /* 480 */
+       btv->win.cropx=0;
+       btv->win.cropy=0;
+#endif
+       pb->win.pad=0;
+       pb->win.bpp=4;
+       pb->win.depth=32;
+       pb->win.color_fmt=PLANB_COLOUR32;
+       pb->win.bpl=1024*pb->win.bpp;
+       pb->win.swidth=1024;
+       pb->win.sheight=768;
+#ifdef PLANB_GSCANLINE
+       if((pb->gbytes_per_line = PLANB_MAXPIXELS * 4) > PAGE_SIZE
+                               || (pb->gbytes_per_line <= 0))
+               return -3;
+       else {
+               /* page align pb->gbytes_per_line for DMA purpose */
+               for(i = PAGE_SIZE; pb->gbytes_per_line < (i>>1);)
+                       i>>=1;
+               pb->gbytes_per_line = i;
+       }
+#endif
+       pb->tab_size = PLANB_MAXLINES + 40;
+       pb->suspend = 0;
+       pb->lock = 0;
+       pb->lockq = NULL;
+       pb->ch1_cmd = 0;
+       pb->ch2_cmd = 0;
+       pb->mask = 0;
+       pb->priv_space = 0;
+       pb->offset = 0;
+       pb->user = 0;
+       pb->overlay = 0;
+       pb->suspendq = NULL;
+       pb->cmd_buff_inited = 0;
+       pb->frame_buffer_phys = 0;
+
+       /* Reset DMA controllers */
+       planb_dbdma_stop(&pb->planb_base->ch2);
+       planb_dbdma_stop(&pb->planb_base->ch1);
+
+       saa_rev =  (saa_status(0, pb) & 0xf0) >> 4;
+       printk(KERN_INFO "PlanB: SAA7196 video processor rev. %d\n", saa_rev);
+       /* Initialize the SAA registers in memory and on chip */
+       saa_init_regs (pb);
+
+       /* clear interrupt mask */
+       pb->intr_mask = PLANB_CLR_IRQ;
+
+        result = request_irq(pb->irq, planb_irq, 0, "PlanB", (void *)pb);
+        if (result==-EINVAL) {
+                printk(KERN_ERR "PlanB: Bad irq number (%d) or handler\n",
+                       (int)pb->irq);
+                return result;
+        }
+       if (result==-EBUSY) {
+                printk(KERN_ERR "PlanB: I don't know why, but IRQ %d busy\n",
+                       (int)pb->irq);
+                return result;
+        }
+        if (result < 0) 
+                return result;
+        
+       /* Now add the template and register the device unit. */
+       memcpy(&pb->video_dev,&planb_template,sizeof(planb_template));
+
+       pb->picture.brightness=0x90<<8;
+       pb->picture.contrast = 0x70 << 8;
+       pb->picture.colour = 0x70<<8;
+       pb->picture.hue = 0x8000;
+       pb->picture.whiteness = 0;
+       pb->picture.depth = pb->win.depth;
+
+       pb->frame_stat=NULL;
+       pb->capq=NULL;
+       for(i=0; i<MAX_GBUFFERS; i++) {
+               pb->gbuffer[i]=NULL;
+               pb->gwidth[i]=0;
+               pb->gheight[i]=0;
+               pb->gfmt[i]=0;
+               pb->cap_cmd[i]=NULL;
+#ifndef PLANB_GSCANLINE
+               pb->l_fr_addr[i]=NULL;
+               pb->lsize[i] = 0;
+               pb->lnum[i] = 0;
+#endif
+       }
+       pb->fbuffer=NULL;
+       pb->grabbing=0;
+
+       /* clear interrupts */
+       out_le32(&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
+       /* set interrupt mask */
+       pb->intr_mask = PLANB_FRM_IRQ;
+
+       if(video_register_device(&pb->video_dev, VFL_TYPE_GRABBER)<0)
+               return -1;
+
+       return 0;
+}
+
+/*
+ *     Scan for a PlanB controller, request the irq and map the io memory 
+ */
+
+static int find_planb(void)
+{
+       struct planb            *pb;
+       struct device_node      *planb_devices;
+       unsigned char           dev_fn, confreg, bus;
+       unsigned int            old_base, new_base;
+       unsigned int            irq;
+
+       if (_machine != _MACH_Pmac)
+               return 0;
+
+       planb_devices = find_devices("planb");
+       if (planb_devices == 0) {
+               planb_num=0;
+               printk(KERN_WARNING "PlanB: no device found!\n");
+               return planb_num;
+       }
+
+       if (planb_devices->next != NULL)
+               printk(KERN_ERR "Warning: only using first PlanB device!\n");
+       pb = &planbs[0];
+       planb_num = 1;
+
+        if (planb_devices->n_addrs != 1) {
+                printk (KERN_WARNING "PlanB: expecting 1 address for planb "
+                       "(got %d)", planb_devices->n_addrs);
+               return 0;
+       }
+
+       if (planb_devices->n_intrs == 0) {
+               printk(KERN_WARNING "PlanB: no intrs for device %s\n",
+                      planb_devices->full_name);
+               return 0;
+       } else {
+               irq = planb_devices->intrs[0].line;
+       }
+
+       /* Initialize PlanB's PCI registers */
+
+       /* There is a bug with the way OF assigns addresses
+          to the devices behind the chaos bridge.
+          control needs only 0x1000 of space, but decodes only
+          the upper 16 bits. It therefore occupies a full 64K.
+          OF assigns the planb controller memory within this space;
+          so we need to change that here in order to access planb. */
+
+       /* We remap to 0xf1000000 in hope that nobody uses it ! */
+
+       bus = (planb_devices->addrs[0].space >> 16) & 0xff;
+       dev_fn = (planb_devices->addrs[0].space >> 8) & 0xff;
+       confreg = planb_devices->addrs[0].space & 0xff;
+       old_base = planb_devices->addrs[0].address;
+       new_base = 0xf1000000;
+
+       DEBUG("PlanB: Found on bus %d, dev %d, func %d, "
+               "membase 0x%x (base reg. 0x%x)\n",
+               bus, PCI_SLOT(dev_fn), PCI_FUNC(dev_fn), old_base, confreg);
+
+       /* Enable response in memory space, bus mastering,
+          use memory write and invalidate */
+       pcibios_write_config_word (bus, dev_fn, PCI_COMMAND,
+               PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER |
+               PCI_COMMAND_INVALIDATE);
+       /* Set PCI Cache line size & latency timer */
+       pcibios_write_config_byte (bus, dev_fn, PCI_CACHE_LINE_SIZE, 0x8);
+       pcibios_write_config_byte (bus, dev_fn, PCI_LATENCY_TIMER, 0x40);
+
+       /* Set the new base address */
+       pcibios_write_config_dword (bus, dev_fn, confreg, new_base);
+
+       planb_regs = (volatile struct planb_registers *)
+                                               ioremap (new_base, 0x400);
+       pb->planb_base = planb_regs;
+       pb->planb_base_phys = (struct planb_registers *)new_base;
+       pb->irq = irq;
+       
+       return planb_num;
+}
+
+static void release_planb(void)
+{
+       int i;
+       struct planb *pb;
+
+       for (i=0;i<planb_num; i++) 
+       {
+               pb=&planbs[i];
+
+               /* stop and flash DMAs unconditionally */
+               planb_dbdma_stop(&pb->planb_base->ch2);
+               planb_dbdma_stop(&pb->planb_base->ch1);
+
+               /* clear and free interrupts */
+               pb->intr_mask = PLANB_CLR_IRQ;
+               out_le32 (&pb->planb_base->intr_stat, PLANB_CLR_IRQ);
+               free_irq(pb->irq, pb);
+
+               /* make sure all allocated memory are freed */
+               planb_prepare_close(pb);
+
+               printk(KERN_INFO "PlanB: unregistering with v4l\n");
+               video_unregister_device(&pb->video_dev);
+
+               /* note that iounmap() does nothing on the PPC right now */
+               iounmap ((void *)pb->planb_base);
+       }
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+#else
+__initfunc(int init_planbs(struct video_init *unused))
+{
+#endif
+       int i;
+  
+       if (find_planb()<=0)
+               return -EIO;
+
+       for (i=0; i<planb_num; i++) {
+               if (init_planb(&planbs[i])<0) {
+                       printk(KERN_ERR "PlanB: error registering device %d"
+                                                       " with v4l\n", i);
+                       release_planb();
+                       return -EIO;
+               } 
+               printk(KERN_INFO "PlanB: registered device %d with v4l\n", i);
+       }  
+       return 0;
+}
+
+#ifdef MODULE
+
+void cleanup_module(void)
+{
+       release_planb();
+}
+
+#endif
diff --git a/drivers/char/planb.h b/drivers/char/planb.h
new file mode 100644 (file)
index 0000000..716163f
--- /dev/null
@@ -0,0 +1,229 @@
+/* 
+    planb - PlanB frame grabber driver
+
+    PlanB is used in the 7x00/8x00 series of PowerMacintosh
+    Computers as video input DMA controller.
+
+    Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
+
+    Based largely on the bttv driver by Ralph Metzler (rjkm@thp.uni-koeln.de)
+
+    Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu)
+
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+/* $Id: planb.h,v 1.13 1999/05/03 19:28:56 mlan Exp $ */
+
+#ifndef _PLANB_H_
+#define _PLANB_H_
+
+#ifdef __KERNEL__
+#include <asm/dbdma.h>
+#include "saa7196.h"
+#endif /* __KERNEL__ */
+
+#define PLANB_DEVICE_NAME      "Apple PlanB Video-In"
+#define PLANB_REV              "1.0"
+
+#ifdef __KERNEL__
+//#define PLANB_GSCANLINE      /* use this if apps have the notion of */
+                               /* grab buffer scanline */
+/* This should be safe for both PAL and NTSC */
+#define PLANB_MAXPIXELS 768
+#define PLANB_MAXLINES 576
+#define PLANB_NTSC_MAXLINES 480
+
+/* Uncomment your preferred norm ;-) */
+#define PLANB_DEF_NORM VIDEO_MODE_PAL
+//#define PLANB_DEF_NORM VIDEO_MODE_NTSC
+//#define PLANB_DEF_NORM VIDEO_MODE_SECAM
+
+/* fields settings */
+#define PLANB_GRAY     0x1     /*  8-bit mono? */
+#define PLANB_COLOUR15 0x2     /* 16-bit mode */
+#define PLANB_COLOUR32 0x4     /* 32-bit mode */
+#define PLANB_CLIPMASK 0x8     /* hardware clipmasking */
+
+/* misc. flags for PlanB DMA operation */
+#define        CH_SYNC         0x1     /* synchronize channels (set by ch1;
+                                  cleared by ch2) */
+#define FIELD_SYNC     0x2     /* used for the start of each field
+                                  (0 -> 1 -> 0 for ch1; 0 -> 1 for ch2) */
+#define EVEN_FIELD     0x0     /* even field is detected if unset */
+#define DMA_ABORT      0x2     /* error or just out of sync if set */
+#define ODD_FIELD      0x4     /* odd field is detected if set */
+
+/* for capture operations */
+#define MAX_GBUFFERS   2
+#ifdef PLANB_GSCANLINE
+#define PLANB_MAX_FBUF 0x240000        /* 576 * 1024 * 4 */
+#define TAB_FACTOR     (1)
+#else
+#define PLANB_MAX_FBUF 0x1b0000        /* 576 * 768 * 4 */
+#define TAB_FACTOR     (2)
+#endif
+#endif /* __KERNEL__ */
+
+struct planb_saa_regs {
+       unsigned char addr;
+       unsigned char val;
+};
+
+struct planb_stat_regs {
+       unsigned int ch1_stat;
+       unsigned int ch2_stat;
+       unsigned char saa_stat0;
+       unsigned char saa_stat1;
+};
+
+struct planb_any_regs {
+       unsigned int offset;
+       unsigned int bytes;
+       unsigned char data[128];
+};
+
+/* planb private ioctls */
+#define PLANBIOCGSAAREGS       _IOWR('v', BASE_VIDIOCPRIVATE, struct planb_saa_regs)   /* Read a saa7196 reg value */
+#define PLANBIOCSSAAREGS       _IOW('v', BASE_VIDIOCPRIVATE + 1, struct planb_saa_regs)        /* Set a saa7196 reg value */
+#define PLANBIOCGSTAT          _IOR('v', BASE_VIDIOCPRIVATE + 2, struct planb_stat_regs)       /* Read planb status */
+#define PLANB_TV_MODE          1
+#define PLANB_VTR_MODE         2
+#define PLANBIOCGMODE          _IOR('v', BASE_VIDIOCPRIVATE + 3, int)  /* Get TV/VTR mode */
+#define PLANBIOCSMODE          _IOW('v', BASE_VIDIOCPRIVATE + 4, int)  /* Set TV/VTR mode */
+
+#ifdef PLANB_GSCANLINE
+#define PLANBG_GRAB_BPL                _IOR('v', BASE_VIDIOCPRIVATE + 5, int)  /* # of bytes per scanline in grab buffer */
+#endif
+
+/* call wake_up_interruptible() with appropriate actions */
+#define PLANB_INTR_DEBUG       _IOW('v', BASE_VIDIOCPRIVATE + 20, int)
+/* investigate which reg does what */
+#define PLANB_INV_REGS         _IOWR('v', BASE_VIDIOCPRIVATE + 21, struct planb_any_regs)
+
+#ifdef __KERNEL__
+
+/* Potentially useful macros */
+#define PLANB_SET(x)   ((x) << 16 | (x))
+#define PLANB_CLR(x)   ((x) << 16)
+
+/* This represents the physical register layout */
+struct planb_registers {
+       volatile struct dbdma_regs      ch1;            /* 0x00: video in */
+       volatile unsigned int           even;           /* 0x40: even field setting */
+       volatile unsigned int           odd;            /* 0x44; odd field setting */
+       unsigned int                    pad1[14];       /* empty? */
+       volatile struct dbdma_regs      ch2;            /* 0x80: clipmask out */
+       unsigned int                    pad2[16];       /* 0xc0: empty? */
+       volatile unsigned int           reg3;           /* 0x100: ???? */
+       volatile unsigned int           intr_stat;      /* 0x104: irq status */
+#define PLANB_CLR_IRQ          0x00            /* clear Plan B interrupt */
+#define PLANB_GEN_IRQ          0x01            /* assert Plan B interrupt */
+#define PLANB_FRM_IRQ          0x02            /* end of frame */
+#define PLANB_IRQ_CMD_MASK     0x00000003U     /* reserve 2 lsbs for command */
+       unsigned int                    pad3[1];        /* empty? */
+       volatile unsigned int           reg5;           /* 0x10c: ??? */
+       unsigned int                    pad4[60];       /* empty? */
+       volatile unsigned char          saa_addr;       /* 0x200: SAA subadr */
+       char                            pad5[3];
+       volatile unsigned char          saa_regval;     /* SAA7196 write reg. val */
+       char                            pad6[3];
+       volatile unsigned char          saa_status;     /* SAA7196 status byte */
+       /* There is more unused stuff here */
+};
+
+struct planb_window {
+       int     x, y;
+       ushort  width, height;
+       ushort  bpp, bpl, depth, pad;
+       ushort  swidth, sheight;
+       int     norm;
+       int     interlace;
+       u32     color_fmt;
+       int     chromakey;
+       int     mode;           /* used to switch between TV/VTR modes */
+};
+
+struct planb_suspend {
+       int overlay;
+       int frame;
+       struct dbdma_cmd cmd;
+};
+
+struct planb {
+       struct  video_device video_dev;
+       struct  video_picture picture;          /* Current picture params */
+       struct  video_audio audio_dev;          /* Current audio params */
+  
+       volatile struct planb_registers *planb_base;    /* virt base of planb */
+       struct planb_registers *planb_base_phys;        /* phys base of planb */
+       void    *priv_space;                    /* Org. alloc. mem for kfree */
+       int     user;
+       unsigned int tab_size;
+       int     maxlines;
+       int lock;
+       struct wait_queue *lockq;
+       unsigned int    irq;                    /* interrupt number */
+       volatile unsigned int intr_mask;
+
+       int     overlay;                        /* overlay running? */
+       struct  planb_window win;
+       unsigned long frame_buffer_phys;        /* We need phys for DMA */
+       int     offset;                         /* offset of pixel 1 */
+       volatile struct dbdma_cmd *ch1_cmd;     /* Video In DMA cmd buffer */
+       volatile struct dbdma_cmd *ch2_cmd;     /* Clip Out DMA cmd buffer */
+       volatile struct dbdma_cmd *overlay_last1;
+       volatile struct dbdma_cmd *overlay_last2;
+       unsigned long ch1_cmd_phys;
+       volatile unsigned char *mask;           /* Clipmask buffer */
+       int suspend;
+       struct wait_queue *suspendq;
+       struct planb_suspend suspended;
+       int     cmd_buff_inited;                /* cmd buffer inited? */
+
+       int grabbing;
+       unsigned int gcount;
+       struct wait_queue *capq;
+       int last_fr;
+       int prev_last_fr;
+        unsigned char *fbuffer;
+       unsigned char *gbuffer[MAX_GBUFFERS];
+       volatile struct dbdma_cmd *cap_cmd[MAX_GBUFFERS];
+       volatile struct dbdma_cmd *last_cmd[MAX_GBUFFERS];
+       volatile struct dbdma_cmd *pre_cmd[MAX_GBUFFERS];
+       int need_pre_capture[MAX_GBUFFERS];
+#define PLANB_DUMMY 40 /* # of command buf's allocated for pre-capture seq. */
+       int gwidth[MAX_GBUFFERS], gheight[MAX_GBUFFERS];
+       unsigned int gfmt[MAX_GBUFFERS];
+       int gnorm_switch[MAX_GBUFFERS];
+        volatile unsigned int *frame_stat;
+#define GBUFFER_UNUSED       0x00U
+#define GBUFFER_GRABBING     0x01U
+#define GBUFFER_DONE         0x02U
+#ifdef PLANB_GSCANLINE
+       int gbytes_per_line;
+#else
+#define MAX_LNUM 431   /* change this if PLANB_MAXLINES or */
+                       /* PLANB_MAXPIXELS changes */
+       unsigned char *l_fr_addr[MAX_GBUFFERS];
+       unsigned char *l_to_addr[MAX_GBUFFERS][MAX_LNUM];
+       int lsize[MAX_GBUFFERS], lnum[MAX_GBUFFERS];
+#endif
+};
+
+#endif /* __KERNEL__ */
+
+#endif /* _PLANB_H_ */
diff --git a/drivers/char/saa7196.h b/drivers/char/saa7196.h
new file mode 100644 (file)
index 0000000..f92f21c
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+    Definitions for the Philips SAA7196 digital video decoder,
+    scaler, and clock generator circuit (DESCpro), as used in
+    the PlanB video input of the Powermac 7x00/8x00 series.
+  
+    Copyright (C) 1998 Michel Lanners (mlan@cpu.lu)
+
+    The register defines are shamelessly copied from the meteor
+    driver out of NetBSD (with permission),
+    and are copyrighted (c) 1995 Mark Tinguely and Jim Lowe
+    (Thanks !)
+  
+    Additional debugging and coding by Takashi Oe (toe@unlinfo.unl.edu)
+
+    The default values used for PlanB are my mistakes.
+*/
+
+/* $Id: saa7196.h,v 1.5 1999/03/26 23:28:47 mlan Exp $ */
+
+#ifndef _SAA7196_H_
+#define _SAA7196_H_
+
+#define SAA7196_NUMREGS        0x31    /* Number of registers (used)*/
+#define NUM_SUPPORTED_NORM 3   /* Number of supported norms by PlanB */
+
+/* Decoder part: */
+#define SAA7196_IDEL    0x00    /* Increment delay */
+#define SAA7196_HSB5    0x01    /* H-sync begin; 50 hz */
+#define SAA7196_HSS5    0x02    /* H-sync stop; 50 hz */
+#define SAA7196_HCB5    0x03    /* H-clamp begin; 50 hz */
+#define SAA7196_HCS5    0x04    /* H-clamp stop; 50 hz */
+#define SAA7196_HSP5    0x05    /* H-sync after PHI1; 50 hz */
+#define SAA7196_LUMC    0x06    /* Luminance control */
+#define SAA7196_HUEC    0x07    /* Hue control */
+#define SAA7196_CKTQ    0x08    /* Colour Killer Threshold QAM (PAL, NTSC) */
+#define SAA7196_CKTS    0x09    /* Colour Killer Threshold SECAM */
+#define SAA7196_PALS    0x0a    /* PAL switch sensitivity */
+#define SAA7196_SECAMS  0x0b    /* SECAM switch sensitivity */
+#define SAA7196_CGAINC  0x0c    /* Chroma gain control */
+#define SAA7196_STDC    0x0d    /* Standard/Mode control */
+#define SAA7196_IOCC    0x0e    /* I/O and Clock Control */
+#define SAA7196_CTRL1   0x0f    /* Control #1 */
+#define SAA7196_CTRL2   0x10    /* Control #2 */
+#define SAA7196_CGAINR  0x11    /* Chroma Gain Reference */
+#define SAA7196_CSAT    0x12    /* Chroma Saturation */
+#define SAA7196_CONT    0x13    /* Luminance Contrast */
+#define SAA7196_HSB6    0x14    /* H-sync begin; 60 hz */
+#define SAA7196_HSS6    0x15    /* H-sync stop; 60 hz */
+#define SAA7196_HCB6    0x16    /* H-clamp begin; 60 hz */
+#define SAA7196_HCS6    0x17    /* H-clamp stop; 60 hz */
+#define SAA7196_HSP6    0x18    /* H-sync after PHI1; 60 hz */
+#define SAA7196_BRIG    0x19    /* Luminance Brightness */
+
+/* Scaler part: */
+#define SAA7196_FMTS    0x20    /* Formats and sequence */
+#define SAA7196_OUTPIX  0x21    /* Output data pixel/line */
+#define SAA7196_INPIX   0x22    /* Input data pixel/line */
+#define SAA7196_HWS     0x23    /* Horiz. window start */
+#define SAA7196_HFILT   0x24    /* Horiz. filter */
+#define SAA7196_OUTLINE 0x25    /* Output data lines/field */
+#define SAA7196_INLINE  0x26    /* Input data lines/field */
+#define SAA7196_VWS     0x27    /* Vertical window start */
+#define SAA7196_VYP     0x28    /* AFS/vertical Y processing */
+#define SAA7196_VBS     0x29    /* Vertical Bypass start */
+#define SAA7196_VBCNT   0x2a    /* Vertical Bypass count */
+#define SAA7196_VBP     0x2b    /* veritcal Bypass Polarity */
+#define SAA7196_VLOW    0x2c    /* Colour-keying lower V limit */
+#define SAA7196_VHIGH   0x2d    /* Colour-keying upper V limit */
+#define SAA7196_ULOW    0x2e    /* Colour-keying lower U limit */
+#define SAA7196_UHIGH   0x2f    /* Colour-keying upper U limit */
+#define SAA7196_DPATH   0x30    /* Data path setting  */
+
+/* Initialization default values: */
+
+unsigned char saa_regs[NUM_SUPPORTED_NORM][SAA7196_NUMREGS] = {
+
+/* PAL, 768x576 (no scaling), composite video-in */
+/* Decoder: */
+      { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff,
+       0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x06, 0x3b, 0x98,
+       0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2,
+       0xe9, 0xa2,
+/* Padding */
+                   0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* Scaler: */
+       0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12,
+       0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+       0x87 },
+
+/* NTSC, 640x480? (no scaling), composite video-in */
+/* Decoder: */
+      { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x50, 0x00,
+       0xf8, 0xf0, 0xfe, 0xe0, 0x00, 0x06, 0x3b, 0x98,
+       0x00, 0x2c, 0x3d, 0x40, 0x34, 0x0a, 0xf4, 0xd2,
+       0xe9, 0x98,
+/* Padding */
+                   0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* Scaler: */
+       0x72, 0x80, 0x80, 0x03, 0x89, 0xf0, 0xf0, 0x0d,
+       0xa0, 0x0d, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+       0x87 },
+
+/* SECAM, 768x576 (no scaling), composite video-in */
+/* Decoder: */
+      { 0x50, 0x30, 0x00, 0xe8, 0xb6, 0xe5, 0x63, 0xff,
+       0xfe, 0xf0, 0xfe, 0xe0, 0x20, 0x07, 0x3b, 0x98,
+       0x00, 0x59, 0x41, 0x45, 0x34, 0x0a, 0xf4, 0xd2,
+       0xe9, 0xa2,
+/* Padding */
+                   0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
+/* Scaler: */
+       0x72, 0x80, 0x00, 0x03, 0x8d, 0x20, 0x20, 0x12,
+       0xa5, 0x12, 0x00, 0x00, 0x04, 0x00, 0x04, 0x00,
+       0x87 }
+       };
+
+#endif /* _SAA7196_H_ */
index ea881bd5546252fb82ab964ce0753e1e2a8d7615..b27fa21489eb320cde408ed34c854055c0a3fb0c 100644 (file)
@@ -40,6 +40,7 @@ static const char *version =
        info that the casual reader might think that it documents the i82586 :-<.
 */
 
+#include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
 #include <linux/types.h>
index 2e96a482c0100ff61098007e87e504821b00c6ad..d94e0efd6e6acf036a7c469b42db75fc71943f17 100644 (file)
@@ -83,6 +83,7 @@
  * practice.
  */
   
+#include <linux/config.h>
 #include <linux/module.h>
 
 #include <linux/kernel.h>
index d16ef0abf6358b79ec6e4cac1fa4c00b4fff4eb9..780b8a938f98cb327a1eb52ca6f5287fd6013bce 100644 (file)
@@ -1499,6 +1499,7 @@ static void tr_rx(struct device *dev)
               ti->asb + offsetof(struct asb_rec, rec_buf_addr));
 
        lan_hdr_len=readb(ti->arb + offsetof(struct arb_rec_req, lan_hdr_len));
+       hdr_len = lan_hdr_len + sizeof(struct trllc) + sizeof(struct iphdr);
        
        llc=(rbuffer + offsetof(struct rec_buf, data) + lan_hdr_len);
 
@@ -1525,8 +1526,10 @@ static void tr_rx(struct device *dev)
                return;
        }
 
+       length = ntohs(readw(ti->arb+offsetof(struct arb_rec_req, frame_len)));
                if ((readb(llc + offsetof(struct trllc, dsap))==EXTENDED_SAP) &&
-                   (readb(llc + offsetof(struct trllc, ssap))==EXTENDED_SAP)) {
+                   (readb(llc + offsetof(struct trllc, ssap))==EXTENDED_SAP) &&
+               (length>=hdr_len)) {
                        IPv4_p = 1;
                }
 
@@ -1557,7 +1560,6 @@ static void tr_rx(struct device *dev)
                }
 #endif
 
-               length = ntohs(readw(ti->arb+offsetof(struct arb_rec_req, frame_len)));
                skb_size = length-lan_hdr_len+sizeof(struct trh_hdr)+sizeof(struct trllc);
  
                if (!(skb=dev_alloc_skb(skb_size))) {
@@ -1577,7 +1579,6 @@ static void tr_rx(struct device *dev)
 
        if (IPv4_p) {
                 /* Copy the headers without checksumming */
-               hdr_len = lan_hdr_len + sizeof(struct trllc) + sizeof(struct iphdr);
                memcpy_fromio(data, rbufdata, hdr_len);
 
                /* Watch for padded packets and bogons */
index 8fa3d8b3a582e26007b992fc9871ecb8a6239c82..b46ea12b7b9c2f9a32babdc338114ff3fed04b3a 100644 (file)
@@ -241,6 +241,7 @@ __u16 ppp_crc16_table[256] =
        0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
        0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
 };
+EXPORT_SYMBOL(ppp_crc16_table);
 
 #ifdef CHECK_CHARACTERS
 static __u32 paritytab[8] =
index beda7319efd0ec88dba3bcc65e184fa88f587f5a..fa8844b673eb355223680f3992331fef7d85582b 100644 (file)
@@ -881,6 +881,7 @@ static void imm_interrupt(void *data)
 {
     imm_struct *tmp = (imm_struct *) data;
     Scsi_Cmnd *cmd = tmp->cur_cmd;
+    unsigned long flags;
 
     if (!cmd) {
        printk("IMM: bug in imm_interrupt\n");
@@ -931,8 +932,10 @@ static void imm_interrupt(void *data)
     if (cmd->SCp.phase > 0)
        imm_pb_release(cmd->host->unique_id);
 
+    spin_lock_irqsave(&io_request_lock, flags);
     tmp->cur_cmd = 0;
     cmd->scsi_done(cmd);
+    spin_unlock_irqrestore(&io_request_lock, flags);
     return;
 }
 
index b8821f8199534663ba3a6faa3dad77c3dd8fba55..05f95e626427f55c6277230873c69ce30dfc0e47 100644 (file)
 
 #define CRLFSTR "\n"
 
-#include <linux/config.h>
 #include <linux/version.h>
 
 #ifdef MODULE
index 59c1ac0178849d91f5f902dcdbad4a5200a5713d..5904c881cf47b5379d48131685a1ceb729614f09 100644 (file)
@@ -741,6 +741,7 @@ static void ppa_interrupt(void *data)
 {
     ppa_struct *tmp = (ppa_struct *) data;
     Scsi_Cmnd *cmd = tmp->cur_cmd;
+    unsigned long flags;
 
     if (!cmd) {
        printk("PPA: bug in ppa_interrupt\n");
@@ -792,7 +793,10 @@ static void ppa_interrupt(void *data)
        ppa_pb_release(cmd->host->unique_id);
 
     tmp->cur_cmd = 0;
+    
+    spin_lock_irqsave(&io_request_lock, flags);
     cmd->scsi_done(cmd);
+    spin_unlock_irqrestore(&io_request_lock, flags);
     return;
 }
 
index ab5736ef26f428ac6eae2e5adc997b3ad3d7bf54..fd63ef0f6045b65906654d14dfc05bd6e368ae02 100644 (file)
@@ -16,7 +16,7 @@
  *
  *  Borrows code from st driver. Thanks to Alessandro Rubini's "dd" book.
  */
- static char * sg_version_str = "Version: 2.1.31 (990327)";
+ static char * sg_version_str = "Version: 2.1.32 (990501)";
 /*
  *  D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au)
  *      - scatter list logic replaces previous large atomic SG_BIG_BUFF
@@ -69,8 +69,9 @@
 
 int sg_big_buff = SG_SCATTER_SZ;  /* sg_big_buff is ro through sysctl */
 /* N.B. This global is here to keep existing software happy. It now holds
-   the size of the "first buffer" of the most recent sucessful sg_open(). */
-/* Only available when 'sg' compiled into kernel (rather than a module). */
+   the size of the "first buffer" of the most recent sucessful sg_open(). 
+   Only available when 'sg' compiled into kernel (rather than a module). 
+   This should probably be deprecated (use SG_GET_RESERVED_SIZE instead). */
 
 #define SG_SECTOR_SZ 512
 #define SG_SECTOR_MSK (SG_SECTOR_SZ - 1)
@@ -146,6 +147,7 @@ typedef struct sg_fd /* holds the state of a file descriptor */
     char closed;        /* 1 -> fd closed but request(s) outstanding */
     char my_mem_src;    /* heap whereabouts of this sg_fb object */
     char cmd_q;         /* 1 -> allow command queuing, 0 -> don't */
+    char underrun_flag; /* 1 -> flag underruns, 0 -> don't, 2 -> test */
 } Sg_fd; /* around 1192 bytes long on i386 */
 
 typedef struct sg_device /* holds the state of each scsi generic device */
@@ -173,7 +175,7 @@ static void sg_free(Sg_request * srp, char * buff, int size, int mem_src);
 static char * sg_low_malloc(int rqSz, int lowDma, int mem_src, 
                             int * retSzp);
 static void sg_low_free(char * buff, int size, int mem_src);
-static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev);
+static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev, int get_reserved);
 static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp);
 static Sg_request * sg_get_request(const Sg_fd * sfp, int pack_id);
 static Sg_request * sg_add_request(Sg_fd * sfp);
@@ -240,11 +242,14 @@ static int sg_open(struct inode * inode, struct file * filp)
     if (! sdp->headfp) { /* no existing opens on this device */
         sdp->sgdebug = 0;
         sdp->sg_tablesize = sdp->device->host->sg_tablesize;
-        sdp->merge_fd = SG_DEF_MERGE_FD;
+        sdp->merge_fd = 0;   /* A little tricky if SG_DEF_MERGE_FD set */
     }
-    if ((sfp = sg_add_sfp(sdp, dev))) {
+    if ((sfp = sg_add_sfp(sdp, dev, O_RDWR == (flags & O_ACCMODE)))) {
+        filp->private_data = sfp;
+#if SG_DEF_MERGE_FD
         if (0 == sdp->merge_fd)
-            filp->private_data = sfp;
+            sdp->merge_fd = 1;
+#endif
     }
     else {
         if (flags & O_EXCL) sdp->exclude = 0; /* undo if error */
@@ -322,11 +327,12 @@ static ssize_t sg_read(struct file * filp, char * buf,
             return -ERESTARTSYS;
         srp = sg_get_request(sfp, req_pack_id);
     }
-    srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
+    if (2 != sfp->underrun_flag)
+        srp->header.pack_len = srp->header.reply_len;   /* Why ????? */
 
     /* Now copy the result back to the user buffer.  */
     if (count >= size_sg_header) {
-        copy_to_user(buf, &srp->header, size_sg_header);
+        __copy_to_user(buf, &srp->header, size_sg_header);
         buf += size_sg_header;
         if (count > srp->header.reply_len)
             count = srp->header.reply_len;
@@ -371,7 +377,7 @@ static ssize_t sg_write(struct file * filp, const char * buf,
         ; /* FIXME: Hmm.  Seek to the right place, or fail?  */
 
     if ((k = verify_area(VERIFY_READ, buf, count)))
-        return k;
+        return k;  /* protects following copy_from_user()s + get_user()s */
 /* The minimum scsi command length is 6 bytes.  If we get anything
  * less than this, it is clearly bogus.  */
     if (count < (size_sg_header + 6))
@@ -382,10 +388,10 @@ static ssize_t sg_write(struct file * filp, const char * buf,
         SCSI_LOG_TIMEOUT(1, printk("sg_write: queue full, domain error\n"));
         return -EDOM;
     }
-    copy_from_user(&srp->header, buf, size_sg_header);
+    __copy_from_user(&srp->header, buf, size_sg_header); 
     buf += size_sg_header;
     srp->header.pack_len = count;
-    get_user(opcode, buf);
+    __get_user(opcode, buf);
     cmd_size = COMMAND_SIZE(opcode);
     if ((opcode >= 0xc0) && srp->header.twelve_byte) 
         cmd_size = 12;
@@ -427,7 +433,7 @@ static ssize_t sg_write(struct file * filp, const char * buf,
     SCpnt->sense_buffer[0] = 0;
     SCpnt->cmd_len = cmd_size;
     /* Now copy the SCSI command from the user's address space.  */
-    copy_from_user(cmnd, buf, cmd_size);
+    __copy_from_user(cmnd, buf, cmd_size);
 
 /* Set the LUN field in the command structure.  */
     cmnd[1]= (cmnd[1] & 0x1f) | (sdp->device->lun << 5);
@@ -439,11 +445,10 @@ static ssize_t sg_write(struct file * filp, const char * buf,
     SCpnt->use_sg = srp->data.use_sg;
     SCpnt->sglist_len = srp->data.sglist_len;
     SCpnt->bufflen = srp->data.bufflen;
-    SCpnt->underflow = srp->data.bufflen;
-/* Not many drivers look at this:
-        aic7xxx driver gives DID_RETRY_COMMAND on underrun
-        seagate comments out its underrun checking code, and the rest ...
-*/
+    if (1 == sfp->underrun_flag)
+        SCpnt->underflow = srp->data.bufflen;
+    else
+        SCpnt->underflow = 0;
     SCpnt->buffer = srp->data.buffer;
     srp->data.use_sg = 0;
     srp->data.sglist_len = 0;
@@ -479,14 +484,10 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
     switch(cmd_in)
     {
     case SG_SET_TIMEOUT:
-        result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
-        if (result) return result;
         return get_user(sfp->timeout, (int *)arg);
     case SG_GET_TIMEOUT:
         return sfp->timeout; /* strange ..., for backward compatibility */
     case SG_SET_FORCE_LOW_DMA:
-        result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
-        if (result) return result;
         result = get_user(val, (int *)arg);
         if (result) return result;
         if (val) {
@@ -503,28 +504,23 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
             sfp->low_dma = sdp->device->host->unchecked_isa_dma;
         return 0;
     case SG_GET_LOW_DMA:
-        result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-        if (result) return result;
-        put_user((int)sfp->low_dma, (int *)arg);
-        return 0;
+        return put_user((int)sfp->low_dma, (int *)arg);
     case SG_GET_SCSI_ID:
         result = verify_area(VERIFY_WRITE, (void *)arg, sizeof(Sg_scsi_id));
         if (result) return result;
         else {
             Sg_scsi_id * sg_idp = (Sg_scsi_id *)arg;
-            put_user((int)sdp->device->host->host_no, &sg_idp->host_no);
-            put_user((int)sdp->device->channel, &sg_idp->channel);
-            put_user((int)sdp->device->id, &sg_idp->scsi_id);
-            put_user((int)sdp->device->lun, &sg_idp->lun);
-            put_user((int)sdp->device->type, &sg_idp->scsi_type);
-            put_user(0, &sg_idp->unused1);
-            put_user(0, &sg_idp->unused2);
-            put_user(0, &sg_idp->unused3);
+            __put_user((int)sdp->device->host->host_no, &sg_idp->host_no);
+            __put_user((int)sdp->device->channel, &sg_idp->channel);
+            __put_user((int)sdp->device->id, &sg_idp->scsi_id);
+            __put_user((int)sdp->device->lun, &sg_idp->lun);
+            __put_user((int)sdp->device->type, &sg_idp->scsi_type);
+            __put_user(0, &sg_idp->unused1);
+            __put_user(0, &sg_idp->unused2);
+            __put_user(0, &sg_idp->unused3);
             return 0;
         }
     case SG_SET_FORCE_PACK_ID:
-        result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
-        if (result) return result;
         result = get_user(val, (int *)arg);
         if (result) return result;
         sfp->force_packid = val ? 1 : 0;
@@ -535,16 +531,14 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
         srp = sfp->headrp;
         while (srp) {
             if (! srp->my_cmdp) {
-                put_user(srp->header.pack_id, (int *)arg);
+                __put_user(srp->header.pack_id, (int *)arg);
                 return 0;
             }
             srp = srp->nextrp;
         }
-        put_user(-1, (int *)arg);
+        __put_user(-1, (int *)arg);
         return 0;
     case SG_GET_NUM_WAITING:
-        result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-        if (result) return result;
         srp = sfp->headrp;
         val = 0;
         while (srp) {
@@ -552,36 +546,25 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
                 ++val;
             srp = srp->nextrp;
         }
-        put_user(val, (int *)arg);
-        return 0;
+        return put_user(val, (int *)arg);
     case SG_GET_SG_TABLESIZE:
-        result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-        if (result) return result;
-        put_user(sdp->sg_tablesize, (int *)arg);
-        return 0;
+        return put_user(sdp->sg_tablesize, (int *)arg);
     case SG_SET_RESERVED_SIZE:
         /* currently ignored, future extension */
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
             return -EACCES;
-        result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
+        result = get_user(val, (int *)arg);
         if (result) return result;
+        /* logic should go here */
         return 0;
     case SG_GET_RESERVED_SIZE:
-        result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-        if (result) return result;
-        put_user(sfp->fb_size, (int *)arg);
-        return 0;
+        return put_user(sfp->fb_size, (int *)arg);
     case SG_GET_MERGE_FD:
-        result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
-        if (result) return result;
-        put_user((int)sdp->merge_fd, (int *)arg);
-        return 0;
+        return put_user((int)sdp->merge_fd, (int *)arg);
     case SG_SET_MERGE_FD:
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
             return -EACCES; /* require write access since effect wider
                                then just this fd */
-        result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
-        if (result) return result;
         result = get_user(val, (int *)arg);
         if (result) return result;
         val = val ? 1 : 0;
@@ -591,17 +574,19 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
         sdp->merge_fd = val;
         return 0;
     case SG_SET_COMMAND_Q:
-        result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
-        if (result) return result;
         result = get_user(val, (int *)arg);
         if (result) return result;
         sfp->cmd_q = val ? 1 : 0;
         return 0;
     case SG_GET_COMMAND_Q:
-        result = verify_area(VERIFY_WRITE, (void *) arg, sizeof(int));
+        return put_user((int)sfp->cmd_q, (int *)arg);
+    case SG_SET_UNDERRUN_FLAG:
+        result = get_user(val, (int *)arg);
         if (result) return result;
-        put_user((int)sfp->cmd_q, (int *)arg);
+        sfp->underrun_flag = val;
         return 0;
+    case SG_GET_UNDERRUN_FLAG:
+        return put_user((int)sfp->underrun_flag, (int *)arg);
     case SG_EMULATED_HOST:
         return put_user(sdp->device->host->hostt->emulated, (int *)arg);
     case SCSI_IOCTL_SEND_COMMAND:
@@ -613,8 +598,6 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
                                dangerous */
         return scsi_ioctl_send_command(sdp->device, (void *)arg);
     case SG_SET_DEBUG:
-        result = verify_area(VERIFY_READ, (const void *)arg, sizeof(int));
-        if (result) return result;
         result = get_user(val, (int *)arg);
         if (result) return result;
         sdp->sgdebug = (char)val;
@@ -625,6 +608,8 @@ static int sg_ioctl(struct inode * inode, struct file * filp,
         return 0;
     case SCSI_IOCTL_GET_IDLUN:
     case SCSI_IOCTL_GET_BUS_NUMBER:
+    case SCSI_IOCTL_PROBE_HOST:
+    case SG_GET_TRANSFORM:
         return scsi_ioctl(sdp->device, cmd_in, (void *)arg);
     default:
         if (O_RDWR != (filp->f_flags & O_ACCMODE))
@@ -698,6 +683,7 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
     sdp = &sg_dev_arr[dev];
     if (NULL == sdp->device)
         return; /* Get out of here quick ... */
+
     sfp = sdp->headfp;
     while (sfp) {
         srp = sfp->headrp;
@@ -721,6 +707,8 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
     srp->data.sglist_len = SCpnt->sglist_len;
     srp->data.bufflen = SCpnt->bufflen;
     srp->data.buffer = SCpnt->buffer;
+    if (2 == sfp->underrun_flag)
+        srp->header.pack_len = SCpnt->underflow;
     sg_clr_scpnt(SCpnt);
     srp->my_cmdp = NULL;
 
@@ -755,7 +743,7 @@ static void sg_command_done(Scsi_Cmnd * SCpnt)
        * underrun or overrun should signal an error.  Until that can be
        * implemented, this kludge allows for returning useful error values
        * except in cases that return DID_ERROR that might be due to an
-       * underrun. [Underrun on advansys adapter yields DID_ABORT -dpg]  */
+       * underrun. */
       if (SCpnt->sense_buffer[0] == 0 &&
           status_byte(SCpnt->result) == GOOD)
           srp->header.result = 0;
@@ -887,8 +875,9 @@ static void sg_debug(const Sg_device * sdp, const Sg_fd * sfp, int part_of)
             printk(">> Following FD has NULL parent pointer ???\n");
         printk("   FD(%d): timeout=%d, fb_size=%d, cmd_q=%d\n",
                k, fp->timeout, fp->fb_size, (int)fp->cmd_q);
-        printk("   low_dma=%d, force_packid=%d, closed=%d\n",
-               (int)fp->low_dma, (int)fp->force_packid, (int)fp->closed);
+        printk("   low_dma=%d, force_packid=%d, urun_flag=%d, closed=%d\n",
+               (int)fp->low_dma, (int)fp->force_packid, 
+               (int)fp->underrun_flag, (int)fp->closed);
         srp = fp->headrp;
         if (NULL == srp)
             printk("     No requests active\n");
@@ -1002,7 +991,7 @@ static int sg_attach(Scsi_Device * scsidp)
     sdp->generic_wait = NULL;
     sdp->headfp= NULL;
     sdp->exclude = 0;
-    sdp->merge_fd = 0;
+    sdp->merge_fd = 0;  /* Cope with SG_DEF_MERGE_FD on open */
     sdp->sgdebug = 0;
     sdp->sg_tablesize = scsidp->host ? scsidp->host->sg_tablesize : 0;
     sdp->i_rdev = MKDEV(SCSI_GENERIC_MAJOR, k);
@@ -1120,7 +1109,7 @@ static int sg_sc_build(Sg_request * srp, int max_buff_size,
     if ((blk_size < 0) || (! srp))
         return -EFAULT;
     
-    SCSI_LOG_TIMEOUT(4, printk("sg build: m_b_s=%d, num_write_xfer=%d\n", 
+    SCSI_LOG_TIMEOUT(4, printk("sg_sc_build: m_b_s=%d, num_write_xfer=%d\n", 
                                   max_buff_size, num_write_xfer)); 
     if (0 == blk_size)
         ++blk_size;             /* don't know why */
@@ -1139,7 +1128,7 @@ static int sg_sc_build(Sg_request * srp, int max_buff_size,
             srp->data.mem_src = mem_src;
             srp->data.b_malloc_len = blk_size;
             if (inp && (num_write_xfer > 0))
-                copy_from_user(srp->data.buffer, inp, num_write_xfer);
+                __copy_from_user(srp->data.buffer, inp, num_write_xfer);
             return 0;
         }
     }
@@ -1185,17 +1174,17 @@ static int sg_sc_build(Sg_request * srp, int max_buff_size,
             }
             sclp->address = p;
             sclp->length = ret_sz;
-            sclp->alt_address = (char *)mem_src;
+            sclp->alt_address = (char *)(long)mem_src;
             
             if(inp && (num_write_xfer > 0)) {
                 num = (ret_sz > num_write_xfer) ? num_write_xfer : ret_sz;
-                copy_from_user(sclp->address, inp, num);
+                __copy_from_user(sclp->address, inp, num);
                 num_write_xfer -= num;
                 inp += num;
             }
             SCSI_LOG_TIMEOUT(5, 
-                printk("sg_sc_build: k=%d, a=0x%x, len=%d, ms=%d\n", 
-                k, (int)sclp->address, ret_sz, (int)sclp->alt_address));
+                printk("sg_sc_build: k=%d, a=0x%p, len=%d, ms=%d\n", 
+                k, sclp->address, ret_sz, mem_src));
         } /* end of for loop */
         srp->data.use_sg = k;
         SCSI_LOG_TIMEOUT(5, 
@@ -1224,20 +1213,20 @@ static int sg_sc_undo_rem(Sg_request * srp, char * outp,
             if (num_read_xfer > 0) {
                 num = (int)sclp->length;
                 if (num > num_read_xfer) {
-                    copy_to_user(outp, sclp->address, num_read_xfer);
+                    __copy_to_user(outp, sclp->address, num_read_xfer);
                     outp += num_read_xfer;
                     num_read_xfer = 0;
                 }
                 else {
-                    copy_to_user(outp, sclp->address, num);
+                    __copy_to_user(outp, sclp->address, num);
                     outp += num;
                     num_read_xfer -= num;
                 }
             }
-            mem_src = (int)sclp->alt_address;
+            mem_src = (int)(long)sclp->alt_address;
             SCSI_LOG_TIMEOUT(5, 
-                printk("sg_sc_undo_rem: k=%d, a=0x%x, len=%d, ms=%d\n", 
-                       k, (int)sclp->address, sclp->length, mem_src));
+                printk("sg_sc_undo_rem: k=%d, a=0x%p, len=%d, ms=%d\n", 
+                       k, sclp->address, sclp->length, mem_src));
             sg_free(srp, sclp->address, sclp->length, mem_src);
         }
         sg_free(srp, srp->data.buffer, srp->data.sglist_len, 
@@ -1245,13 +1234,13 @@ static int sg_sc_undo_rem(Sg_request * srp, char * outp,
     }
     else {
         if (num_read_xfer > 0) 
-            copy_to_user(outp, srp->data.buffer, num_read_xfer);
+            __copy_to_user(outp, srp->data.buffer, num_read_xfer);
         sg_free(srp, srp->data.buffer, srp->data.b_malloc_len, 
                 srp->data.mem_src);
     }
     if (0 == sg_remove_request(srp->parentfp, srp)) {
-        SCSI_LOG_TIMEOUT(1, printk("sg_sc_undo_rem: srp=%d not found\n", 
-                                   (int)srp));
+        SCSI_LOG_TIMEOUT(1, printk("sg_sc_undo_rem: srp=0x%p not found\n", 
+                                   srp));
     }
     return 0;
 }
@@ -1336,7 +1325,7 @@ static int sg_remove_request(Sg_fd * sfp, const Sg_request * srp)
     return 0;
 }
 
-static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
+static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev, int get_reserved)
 {
     Sg_fd * sfp;
 
@@ -1357,8 +1346,12 @@ static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
     sfp->low_dma = (SG_DEF_FORCE_LOW_DMA == 0) ?
                    sdp->device->host->unchecked_isa_dma : 1;
     sfp->cmd_q = SG_DEF_COMMAND_Q;
-    sfp->fst_buf = sg_low_malloc(SG_SCATTER_SZ, sfp->low_dma, 
-                                 SG_HEAP_PAGE, &sfp->fb_size);
+    sfp->underrun_flag = SG_DEF_UNDERRUN_FLAG;
+    if (get_reserved)
+        sfp->fst_buf = sg_low_malloc(SG_SCATTER_SZ, sfp->low_dma, 
+                                     SG_HEAP_PAGE, &sfp->fb_size);
+    else
+        sfp->fst_buf = NULL;
     if (! sfp->fst_buf)
         sfp->fb_size = 0;
     sfp->parentdp = sdp;
@@ -1371,10 +1364,10 @@ static Sg_fd * sg_add_sfp(Sg_device * sdp, int dev)
         pfp->nextfp = sfp;
     }
     sg_big_buff = sfp->fb_size; /* show sysctl most recent "fb" size */
-    SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%x, m_s=%d\n",
-                               (int)sfp, (int)sfp->my_mem_src));
-    SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp:   fb_sz=%d, fst_buf=0x%x\n",
-                               sfp->fb_size, (int)sfp->fst_buf));
+    SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp: sfp=0x%p, m_s=%d\n",
+                               sfp, (int)sfp->my_mem_src));
+    SCSI_LOG_TIMEOUT(3, printk("sg_add_sfp:   fb_sz=%d, fst_buf=0x%p\n",
+                               sfp->fb_size, sfp->fst_buf));
     return sfp;
 }
 
@@ -1416,13 +1409,13 @@ static int sg_remove_sfp(Sg_device * sdp, Sg_fd * sfp)
                 prev_fp = fp;
             }
         }
-SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    fb_sz=%d, fst_buf=0x%x\n",
-                 sfp->fb_size, (int)sfp->fst_buf));
+SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    fb_sz=%d, fst_buf=0x%p\n",
+                 sfp->fb_size, sfp->fst_buf));
         sg_low_free(sfp->fst_buf, sfp->fb_size, SG_HEAP_PAGE);
         sfp->parentdp = NULL;
         sfp->fst_buf = NULL;
         sfp->fb_size = 0;
-    SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    sfp=0x%x\n", (int)sfp));
+    SCSI_LOG_TIMEOUT(6, printk("sg_remove_sfp:    sfp=0x%p\n", sfp));
         sg_low_free((char *)sfp, sizeof(Sg_fd), sfp->my_mem_src);
         res = 1;
     }
@@ -1573,8 +1566,8 @@ static char * sg_malloc(Sg_request * srp, int size, int * retSzp,
         }
         if (resp) *mem_srcp = l_ms;
     }
-    SCSI_LOG_TIMEOUT(6, printk("sg_malloc: size=%d, ms=%d, ret=0x%x\n", 
-                               size, *mem_srcp, (int)resp));
+    SCSI_LOG_TIMEOUT(6, printk("sg_malloc: size=%d, ms=%d, ret=0x%p\n", 
+                               size, *mem_srcp, resp));
     return resp;
 }
 
@@ -1598,8 +1591,8 @@ static void sg_low_free(char * buff, int size, int mem_src)
         free_pages((unsigned long)buff, order);
     }
     else
-        printk("sg_low_free: bad mem_src=%d, buff=0x%x, rqSz=%df\n", 
-               mem_src, (int)buff, size);
+        printk("sg_low_free: bad mem_src=%d, buff=0x%p, rqSz=%df\n", 
+               mem_src, buff, size);
 }
 
 static void sg_free(Sg_request * srp, char * buff, int size, int mem_src)
@@ -1607,7 +1600,7 @@ static void sg_free(Sg_request * srp, char * buff, int size, int mem_src)
     Sg_fd * sfp = srp->parentfp;
 
     SCSI_LOG_TIMEOUT(6, 
-        printk("sg_free: buff=0x%x, size=%d\n", (int)buff, size));
+        printk("sg_free: buff=0x%p, size=%d\n", buff, size));
     if ((! sfp) || (! buff) || (size <= 0))
         ;
     else if (sfp->fst_buf == buff) {
@@ -1624,5 +1617,7 @@ static void sg_clr_scpnt(Scsi_Cmnd * SCpnt)
     SCpnt->sglist_len = 0;
     SCpnt->bufflen = 0;
     SCpnt->buffer = NULL;
+    SCpnt->underflow = 0;
+    SCpnt->request.rq_dev = MKDEV(0, 0);  /* "sg" _disowns_ command blk */
 }
 
index 06ecb57952255165a577da5960300346915bbee1..f5e89ea8986cc2ae16ef9209a698ba223a4ecb44 100644 (file)
@@ -569,24 +569,29 @@ int usb_get_report(struct usb_device *dev)
 
 int usb_get_configuration(struct usb_device *dev)
 {
-       unsigned int size;
+       unsigned int cfgno,size;
        unsigned char buffer[400];
-
-       /* Get the first 8 bytes - guaranteed */
-       if (usb_get_descriptor(dev, USB_DT_CONFIG, 0, buffer, 8))
-               return -1;
-
-       /* Get the full buffer */
-       size = *(unsigned short *)(buffer+2);
-       if (size > sizeof(buffer))
-       {
-               printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
-               size = sizeof(buffer);
+       unsigned char * bufptr;
+       
+       bufptr=buffer;
+        for (cfgno=0;cfgno<dev->descriptor.bNumConfigurations;cfgno++) {
+               /* Get the first 8 bytes - guaranteed */
+               if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, 8))
+                       return -1;
+
+               /* Get the full buffer */
+               size = *(unsigned short *)(bufptr+2);
+               if (bufptr+size > buffer+sizeof(buffer))
+               {
+                       printk(KERN_INFO "usb: truncated DT_CONFIG (want %d).\n", size);
+                       size = buffer+sizeof(buffer)-bufptr;
+               }
+               if (usb_get_descriptor(dev, USB_DT_CONFIG, cfgno, bufptr, size))
+                       return -1;
+                       
+               /* Prepare for next configuration */
+               bufptr+=size;
        }
-
-       if (usb_get_descriptor(dev, USB_DT_CONFIG, 0, buffer, size))
-               return -1;
-
        return usb_parse_configuration(dev, buffer, size);
 }
 
index 4bc541f9a9a0f1d30cbfc06c48f679b8ddcf1a29..df84e618c6f368e8f57a2c82ae27c1697ece6d63 100644 (file)
@@ -72,7 +72,7 @@ static struct file_operations nfs_dir_operations = {
        NULL,                   /* select - default */
        NULL,                   /* ioctl - default */
        NULL,                   /* mmap */
-       NULL,                   /* no special open is needed */
+       nfs_open,               /* open - revalidate the inode */
        NULL,                   /* flush */
        NULL,                   /* no special release code */
        NULL                    /* fsync */
@@ -389,8 +389,11 @@ static int nfs_lookup_revalidate(struct dentry * dentry)
         * If we don't have an inode, let's just assume
         * a 5-second "live" time for negative dentries.
         */
-       if (!inode)
-               goto do_lookup;
+       if (!inode) {
+               if (time_after(jiffies, dentry->d_time + NFS_REVALIDATE_INTERVAL))
+                       goto out_bad;
+               goto out_valid;
+       }
 
        if (is_bad_inode(inode)) {
                dfprintk(VFS, "nfs_lookup_validate: %s/%s has dud inode\n",
@@ -398,27 +401,17 @@ static int nfs_lookup_revalidate(struct dentry * dentry)
                goto out_bad;
        }
 
-       if (_nfs_revalidate_inode(NFS_DSERVER(dentry), dentry))
-               goto out_bad;
-
        if (time_before(jiffies,dentry->d_time+NFS_ATTRTIMEO(inode)))
                goto out_valid;
 
        if (IS_ROOT(dentry))
                goto out_valid;
 
-do_lookup:
        /*
         * Do a new lookup and check the dentry attributes.
         */
        error = nfs_proc_lookup(NFS_DSERVER(parent), NFS_FH(parent), 
                                dentry->d_name.name, &fhandle, &fattr);
-       if (dentry->d_inode == NULL) {
-               if (error == -ENOENT &&
-                   time_before(jiffies,dentry->d_time+NFS_REVALIDATE_INTERVAL))
-                       goto out_valid;
-               goto out_bad;
-       }
        if (error)
                goto out_bad;
 
index c5dc24b834132e5d9013e6817fe34c08c870c82c..8a7d1571a55cdb31f3e8c02ef632173216f06994 100644 (file)
@@ -46,7 +46,7 @@ static struct file_operations nfs_file_operations = {
        NULL,                   /* select - default */
        NULL,                   /* ioctl - default */
        nfs_file_mmap,          /* mmap */
-       NULL,                   /* no special open is needed */
+       nfs_open,               /* open - revalidate the inode */
        nfs_file_flush,         /* flush */
        NULL,                   /* release */
        nfs_fsync,              /* fsync */
index 5efe7d898844d349340928d386a5ba2e6e2067d7..6617893921a709cba23fc1ebefd35e7e27348262 100644 (file)
@@ -695,6 +695,28 @@ nfs_revalidate(struct dentry *dentry)
        return nfs_revalidate_inode(NFS_DSERVER(dentry), dentry);
 }
 
+/*
+ * Revalidate the file on open (this
+ * is separate from the path-revalidation
+ * that we do on any lookup).
+ *
+ * When we actually open a file, we want
+ * fairly strict consistency: make sure that
+ * we've updated the attributes within the
+ * last second or so..
+ */
+int nfs_open(struct inode *inode, struct file *filp)
+{
+       int retval = 0;
+
+       if (time_after(jiffies, NFS_READTIME(inode) + HZ/2)) {
+               struct dentry *dentry = filp->f_dentry;
+               struct nfs_server *server = NFS_DSERVER(dentry);
+               retval = _nfs_revalidate_inode(server, dentry);
+       }
+       return retval;
+}
+
 /*
  * This function is called whenever some part of NFS notices that
  * the cached attributes have to be refreshed.
index 30f1372eac53b670215efdd6246d136beba70aaa..698ce18211a804f3bcd87f8eaa836411481d7dff 100644 (file)
@@ -190,7 +190,7 @@ extern inline void up(struct semaphore * sem)
                "       stl_c   $28,%1\n"
                "       beq     $28,2f\n"
                "       mb\n"
-               "       ble     $27,3f\n"
+               "       ble     $24,3f\n"
                "4:\n"
                ".section .text2,\"ax\"\n"
                "2:     br      1b\n"
index b620454f19926dc6f7c5bcd37d5d4a260818dfa1..c53267b77e0aa648644976964d55f73e61fdd048 100644 (file)
@@ -11,7 +11,6 @@
 #ifndef __ASMPPC_IDE_H
 #define __ASMPPC_IDE_H
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <asm/processor.h>
 
index 64aa54eaeb348465b3c552a7c598f28596e52697..5bf9b9c54abcf9f6b8557b60bde156245e946fd5 100644 (file)
@@ -181,6 +181,7 @@ extern struct inode *nfs_fhget(struct dentry *, struct nfs_fh *,
                                struct nfs_fattr *);
 extern int nfs_refresh_inode(struct inode *, struct nfs_fattr *);
 extern int nfs_revalidate(struct dentry *);
+extern int nfs_open(struct inode *, struct file *);
 extern int _nfs_revalidate_inode(struct nfs_server *, struct dentry *);
 
 /*
index 95ea3944dfec42d4dc93d2a02bee690ea6d03e0b..119b60eec11c076f71f378140044185526cc8d62 100644 (file)
@@ -95,6 +95,8 @@ struct video_tuner
 #define VIDEO_TUNER_LOW                8       /* Uses KHz not MHz */
 #define VIDEO_TUNER_NORM       16      /* Tuner can set norm */
 #define VIDEO_TUNER_STEREO_ON  128     /* Tuner is seeing stereo */
+#define VIDEO_TUNER_RDS_ON      256     /* Tuner is seeing an RDS datastream */
+#define VIDEO_TUNER_MBS_ON      512     /* Tuner is seeing an MBS datastream */
        __u16 mode;                     /* PAL/NTSC/SECAM/OTHER */
 #define VIDEO_MODE_PAL         0
 #define VIDEO_MODE_NTSC                1
index bfc3c246df09789d9fba15c7337a0f7481851790..3e112c659ae6ae81331ccba785198cfbb6ad82c1 100644 (file)
@@ -12,10 +12,16 @@ Original driver (sg.h):
 *       Copyright (C) 1998, 1999 Douglas Gilbert
 
 
-    Version: 2.1.31 (990327)
+    Version: 2.1.32 (990501)
     This version for later 2.1.x series and 2.2.x kernels
     D. P. Gilbert (dgilbert@interlog.com, dougg@triode.net.au)
 
+    Changes since 2.1.31 (990327)
+        - add ioctls SG_GET_UNDERRUN_FLAG and _SET_. Change the default
+          to _not_ flag underruns (affects aic7xxx driver)
+        - clean up logging of pointers to use %p (for 64 bit architectures)
+        - rework usage of get_user/copy_to_user family of kernel calls
+        - "disown" scsi_command blocks before releasing them
     Changes since 2.1.30 (990320)
         - memory tweaks: change flags on kmalloc (GFP_KERNEL to GFP_ATOMIC)
         -                increase max allowable mid-level pool usage
@@ -113,7 +119,7 @@ Original driver (sg.h):
  requesting 512KB) and scale them back in the face of ENOMEM errors.
  N.B. Queuing up commands also ties up kernel memory.
 
- More documentation can be found at www.netwinder.org/~dougg
+ More documentation can be found at www.torque.net/sg
 */
 
 #define SG_MAX_SENSE 16   /* too little, unlikely to change in 2.2.x */
@@ -197,6 +203,11 @@ typedef struct sg_scsi_id {
 #define SG_GET_COMMAND_Q 0x2270   /* Yields 0 (queuing off) or 1 (on) */
 #define SG_SET_COMMAND_Q 0x2271   /* Change queuing state with 0 or 1 */
 
+/* Get/set whether DMA underrun will cause an error (DID_ERROR) [this only
+   currently applies to the [much-used] aic7xxx driver) */
+#define SG_GET_UNDERRUN_FLAG 0x2280 /* Yields 0 (don't flag) or 1 (flag) */
+#define SG_SET_UNDERRUN_FLAG 0x2281 /* Change flag underrun state */
+
 
 #define SG_DEFAULT_TIMEOUT (60*HZ) /* HZ == 'jiffies in 1 second' */
 #define SG_DEFAULT_RETRIES 1
@@ -206,6 +217,7 @@ typedef struct sg_scsi_id {
 #define SG_DEF_MERGE_FD 0       /* was 1 -> per device sequencing */
 #define SG_DEF_FORCE_LOW_DMA 0  /* was 1 -> memory below 16MB on i386 */
 #define SG_DEF_FORCE_PACK_ID 0
+#define SG_DEF_UNDERRUN_FLAG 0
 
 /* maximum outstanding requests, write() yields EDOM if exceeded */
 #define SG_MAX_QUEUE 16
index 75be78278834ec873bed55e599f6f73545f4a496..8ca50174bba24f753a3bde81128dc0ab6f8fdb0d 100644 (file)
@@ -354,7 +354,7 @@ static unsigned long memory_end = 0;
 int rows, cols;
 
 #ifdef CONFIG_BLK_DEV_RAM
-extern int rd_doload;          /* 1 = load ramdisk, 0 = don't load */
+extern int rd_doload;          /* 1 = load ramdisk, 0 = don't load 2 = dual disk */
 extern int rd_prompt;          /* 1 = prompt for ramdisk, 0 = don't prompt */
 extern int rd_size;            /* Size of the ramdisk(s) */
 extern int rd_image_start;     /* starting block # of image */
index c6c6d4773264abd64fdaa59806590a51b81f6f1d..222a3f9ecfe8e1f360b7473218450555b56626fa 100644 (file)
@@ -174,6 +174,7 @@ rpc_make_runnable(struct rpc_task *task)
                printk(KERN_ERR "RPC: task w/ running timer in rpc_make_runnable!!\n");
                return;
        }
+       task->tk_flags |= RPC_TASK_RUNNING;
        if (RPC_IS_ASYNC(task)) {
                int status;
                status = rpc_add_wait_queue(&schedq, task);
@@ -186,7 +187,6 @@ rpc_make_runnable(struct rpc_task *task)
        } else {
                wake_up(&task->tk_wait);
        }
-       task->tk_flags |= RPC_TASK_RUNNING;
 }
 
 
@@ -447,7 +447,10 @@ __rpc_execute(struct rpc_task *task)
                                                        task->tk_pid);
                        if (current->pid == rpciod_pid)
                                printk(KERN_ERR "RPC: rpciod waiting on sync task!\n");
-                       sleep_on(&task->tk_wait);
+
+                       sti();
+                       __wait_event(task->tk_wait, RPC_IS_RUNNING(task));
+                       cli();
 
                        /*
                         * When the task received a signal, remove from