]> git.neil.brown.name Git - history.git/commitdiff
Import 1.1.74 1.1.74
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:47 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:47 +0000 (15:09 -0500)
43 files changed:
CREDITS
Makefile
arch/i386/config.in
arch/sparc/vac-flush.c [new file with mode: 0644]
drivers/block/README.sbpcd
drivers/block/sbpcd.c
drivers/char/Makefile
drivers/char/cyclades.c [new file with mode: 0644]
drivers/char/keyboard.c
drivers/char/serial.c
drivers/char/tty_io.c
drivers/char/vt.c
drivers/net/dummy.c
drivers/net/eexpress.c
drivers/net/plip.c
drivers/net/wd.c
drivers/scsi/README.st
drivers/scsi/eata.c
drivers/scsi/eata.h
drivers/scsi/hosts.c
drivers/scsi/hosts.h
drivers/scsi/scsi.c
drivers/scsi/scsi.h
drivers/scsi/scsi_ioctl.c
drivers/scsi/st.c
drivers/scsi/u14-34f.c
drivers/scsi/u14-34f.h
fs/buffer.c
fs/isofs/inode.c
fs/open.c
fs/proc/array.c
fs/super.c
include/asm-i386/types.h
include/asm-sparc/vac-ops.h [new file with mode: 0644]
include/linux/cdrom.h
include/linux/cyclades.h [new file with mode: 0644]
include/linux/fs.h
include/linux/interrupt.h
include/linux/sbpcd.h
include/linux/serial.h
init/main.c
kernel/irq.c
net/inet/tcp.c

diff --git a/CREDITS b/CREDITS
index 24f2da98199045c1063b310461717f806ee9c6f9..23ceb1d67e6b322383b0ba934d39d0486263e3ff 100644 (file)
--- a/CREDITS
+++ b/CREDITS
@@ -721,6 +721,16 @@ S: Kalevankatu 55 B 37
 S: 00180 Helsinki
 S: Finland
 
+N: Jeff Tranter
+E: Jeff_Tranter@Mitel.COM
+D: Enhancements to Joystick driver
+D: Author of Sound HOWTO and CD-ROM HOWTO
+D: Author of several small utilities
+D: (bogomips, scope, eject, statserial)
+S: 1 Laurie Court
+S: Kanata, Ontario
+S: CANADA K2L 1S2
+
 N: Theodore Ts'o
 E: tytso@mit.edu
 D: Random Linux hacker
index 6d0603a7f7223816e8f4eedcb79d83d172671652..de9113fbeac48971452586b572de6c60647209d7 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 1
 PATCHLEVEL = 1
-SUBLEVEL = 73
+SUBLEVEL = 74
 
 ARCH = i386
 
@@ -148,7 +148,7 @@ include/linux/version.h: $(CONFIGURE) Makefile
        @echo \#define LINUX_COMPILER \"`$(HOSTCC) -v 2>&1 | tail -1`\" >> include/linux/version.h
 
 tools/build: tools/build.c $(CONFIGURE)
-       $(HOSTCC) $(CFLAGS) -o $@ $<
+       $(HOSTCC) $(CFLAGS) -o $@ $< -I$(TOPDIR)/include
 
 boot/head.o: $(CONFIGURE) boot/head.s
 
index 7971c4f687f476ff6f7581aed4eb7568cf8ac4be..e83888d024b113f0601813655e9b8fc5cde16f96 100644 (file)
@@ -74,7 +74,7 @@ bool 'Seagate ST-02 and Future Domain TMC-8xx SCSI support' CONFIG_SCSI_SEAGATE
 bool 'Trantor T128/T128F/T228 SCSI support' CONFIG_SCSI_T128 n
 bool 'UltraStor SCSI support' CONFIG_SCSI_ULTRASTOR n
 bool '7000FASST SCSI support' CONFIG_SCSI_7000FASST n
-bool 'EISA EATA support' CONFIG_SCSI_EATA n
+bool 'EATA ISA/EISA (DPT PM2011/021/012/022/122/322) support' CONFIG_SCSI_EATA n
 #bool 'SCSI debugging host adapter' CONFIG_SCSI_DEBUG n
 fi
 
@@ -196,6 +196,7 @@ bool 'System V and Coherent filesystem support' CONFIG_SYSV_FS n
 
 comment 'character devices'
 
+bool 'Cyclades async mux support' CONFIG_CYCLADES n
 bool 'Parallel printer support' CONFIG_PRINTER n
 bool 'Logitech busmouse support' CONFIG_BUSMOUSE n
 bool 'PS/2 mouse (aka "auxiliary device") support' CONFIG_PSMOUSE n
diff --git a/arch/sparc/vac-flush.c b/arch/sparc/vac-flush.c
new file mode 100644 (file)
index 0000000..50d54c5
--- /dev/null
@@ -0,0 +1,93 @@
+/* vac.c:   Routines for flushing various amount of the Sparc VAC
+            (virtual address cache).
+
+   Copyright (C) 1994 David S. Miller (davem@caip.rutgers.edu)
+*/
+
+#include <asm/vac-ops.h>
+
+/* Flush all VAC entries for the current context */
+
+extern int do_hw_vac_flushes, vac_size, vac_linesize;
+extern int vac_entries_per_context, vac_entries_per_segment;
+extern int vac_entries_per_page;
+
+void
+flush_vac_context()
+{
+  register int entries_left, offset;
+  register char* address;
+
+  entries_left = vac_entries_per_context;
+  address = (char *) 0;
+
+  if(do_hw_vac_flushes)
+    {
+      while(entries_left-- >=0)
+       {
+         hw_flush_vac_context_entry(address);
+         address += 4096;
+       }
+    }
+  else
+    {
+      offset = vac_linesize;
+      while(entries_left-- >=0)
+       {
+         sw_flush_vac_context_entry(address);
+         address += offset;
+       }
+    }
+}
+
+void
+flush_vac_segment(register unsigned int segment)
+{
+  register int entries_left, offset;
+  register char* address;
+  
+  entries_left = vac_entries_per_segment;
+  __asm__ __volatile__("sll %0, 18, %1\n\t"
+                      "sra %1, 0x2, %1\n\t"
+                      : "=r" (segment) : "0" (address));
+
+  if(do_hw_vac_flushes)
+    {
+      while(entries_left-- >=0)
+       {
+         hw_flush_vac_segment_entry(address);
+         address += 4096;
+       }
+    }
+  else
+    {
+      offset = vac_linesize;
+      while(entries_left-- >=0)
+       {
+         sw_flush_vac_segment_entry(address);
+         address += offset;
+       }
+    }
+}
+
+void
+flush_vac_page(register unsigned int addr)
+{
+  register int entries_left, offset;
+
+  if(do_hw_vac_flushes)
+    {
+      hw_flush_vac_page_entry(addr);
+    }
+  else
+    {
+      entries_left = vac_entries_per_page;
+      offset = vac_linesize;
+      while(entries_left-- >=0)
+       {
+         sw_flush_vac_page_entry(addr);
+         addr += offset;
+       }
+    }
+}
+  
index 74f188c26206dc532173d6e020eb93861346b0b3..fd397753215b75bb605485c9d0e39947edfa90d4 100644 (file)
@@ -1,5 +1,6 @@
-This README belongs to release 2.8 or newer of the SoundBlaster Pro
-(Matsushita, Kotobuki, Panasonic, CreativeLabs) CD-ROM driver for Linux.
+This README belongs to release 2.9 or newer of the SoundBlaster Pro
+(Matsushita, Kotobuki, Panasonic, CreativeLabs, Longshine and soon TEAC, too)
+CD-ROM driver for Linux.
 
 The driver is able to drive the whole family of "traditional" IDE-style (that
 has nothing to do with the new "Enhanced IDE" drive standard) Matsushita,
@@ -7,26 +8,36 @@ Kotobuki, Panasonic drives, sometimes labelled as "CreativeLabs". The
 well-known drives are CR-521, CR-522, CR-523, CR-562, CR-563.
 
 The Longshine LCS-7260 is a double-speed drive which uses the "old"
-Matsushita command set. It should be fully supported soon, too.
+Matsushita command set. It is supported now - with help by Serge Robyns.
 
-There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-562
+There exists an "IBM External ISA CD-ROM Drive" which in fact is a CR-563
 with a special controller board. This drive is supported (the interface is
 of the "LaserMate" type), and it is possibly the best buy today (cheaper than
 an internal drive, and you can use it as an internal, too - f.e. plug it into
-a soundcard). If you own such a drive, please mail me the DOS driver (gzipped
-and uuencoded) and the specifications (exact name, address range etc.) I still
-have to confirm my opinion. ;-)
+a soundcard).
+
+The quad-speed TEAC CD-55A drive uses the same interface types, but has a
+totally different command and flow control scheme. It is not supported yet,
+but I plan to add it.
+
+CreativeLabs has a new drive "CD-200". This drive is not supported yet, but
+I plan to add it.
 
 This driver is NOT for Mitsumi or Sony or Aztech or Philips or XXX drives.
-The matter that some other brand's drives work with newer sound card
-interfaces does NOT make the drives compatible. :-)
+For Aztech CDA-268 drives (and for some Wearnes, Okano and Orchid drives),
+Werner Zimmermann has done a driver which currently resides at ftp.gwdg.de
+under /pub/linux/cdrom/drivers/aztech/.
 
-It will work with the soundcard interfaces (SB Pro, SB 16, Galaxy, SoundFX,
-...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
+This driver will work with the soundcard interfaces (SB Pro, SB 16, Galaxy,
+SoundFX, ...) and/or with the "no-sound" cards (Panasonic CI-101P, LaserMate,
 WDH-7001C, Longshine LCS-6853, older Aztech cards, ...).
+
 It should work too now with the "configurable" interface "Sequoia S-1000",
 which is found on the Spea Media FX sound card. I still need feedback about
-this.
+this, or such a card. Anyways, the procedure "boot DOS and wait until 
+CONFIG.SYS is done, then use CTL-ALT-DEL to boot Linux" should make it
+work.
+
 The interface type has to get configured in /usr/include/linux/sbpcd.h, 
 because the behavior of some sound card interfaces is different.
 
@@ -252,6 +263,70 @@ main(int argc, char *argv[])
 }
 /*===================== end program ========================================*/
 
+There is a new ioctl CDROMMULTISESSION to obtain with a user program if
+the CD is an XA disk and - if it is - where the last session starts. The
+following example illustrates how to call it:
+
+/*=================== begin program ========================================*/
+/*
+ * ask for multisession redirection info
+ *
+ * (c) 1994 Eberhard Moenkeberg <emoenke@gwdg.de>
+ *          may be used & enhanced freely
+ *
+ */
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <linux/cdrom.h>
+
+static struct cdrom_multisession ms_info;
+static int drive;
+static int err;
+
+main(int argc, char *argv[])
+{
+/*
+ * open /dev/cdrom
+ */
+  drive=open("/dev/cdrom", 0);
+  if (drive<0)
+    {
+      fprintf(stderr, "can't open drive /dev/cdrom.\n");
+      exit (-1);
+    }
+/*
+ * 
+ */
+  ms_info.addr_format=CDROM_LBA;
+  err=ioctl(drive, CDROMMULTISESSION, &ms_info);
+  if (err!=0)
+    {
+      fprintf(stderr, "CDROMMULTISESSION(lba) returns error %d.\n", err);
+      exit (-1);
+    }
+  else
+       if (ms_info.xa_flag)
+           fprintf(stdout, "lba: %d\n", ms_info.addr.lba);
+       else
+           fprintf(stdout, "not an XA disk.\n");
+
+  ms_info.addr_format=CDROM_MSF;
+  err=ioctl(drive, CDROMMULTISESSION, &ms_info);
+  if (err!=0)
+    {
+      fprintf(stderr, "CDROMMULTISESSION(msf) returns error %d.\n", err);
+      exit (-1);
+    }
+  else
+       if (ms_info.xa_flag)
+           fprintf(stdout, "msf: %02d:%02d:%02d\n",
+                                ms_info.addr.msf.minute,
+                                ms_info.addr.msf.second,
+                                ms_info.addr.msf.frame);
+       else
+           fprintf(stdout, "not an XA disk.\n");
+}
+/*===================== end program ========================================*/
 
 
 Auto-probing at boot time:
index a48dc6593b366e9fcd024e9c35658d44381cb9a7..f38f15b7308d6c62584e499ca14dd0f603f15bf5 100644 (file)
@@ -6,8 +6,10 @@
  *            Panasonic CI-101P.
  *            Also for the Longshine LCS-7260 drive.
  *            Also for the IBM "External ISA CD-Rom" drive.
+ *            Not for the TEAC CD-55A drive (yet).
+ *            Not for the CreativeLabs CDD-200 drive (yet).
  *
- *  NOTE:     This is release 2.8.
+ *  NOTE:     This is release 2.9.
  *            It works with my SbPro & drive CR-521 V2.11 from 2/92
  *            and with the new CR-562-B V0.75 on a "naked" Panasonic
  *            CI-101P interface. And vice versa. 
  *       Changed default of the "JUKEBOX" define. If you use this default,
  *       your tray will eject if you try to mount without a disk in. Next
  *       mount command will insert the tray - so, just insert a disk. ;-)
+ *       
+ *  2.9  Fulfilled the Longshine LCS-7260 support; with great help and
+ *       experiments by Serge Robyns.
+ *       First attempts to support the TEAC CD-55A drives; but still not
+ *       useable yet.
+ *       Implemented the CDROMMULTISESSION and CDROMMULTISESSION_SYS ioctls;
+ *       this is an attempt to handle multi session CDs more "transparent"
+ *       (redirection handling has to be done within the isofs routines, and
+ *       only for the special purpose of obtaining the "right" volume
+ *       descriptor; accesses to the raw device should not get redirected).
  *
  *  TODO
  *
 
 #include "blk.h"
 
-#define VERSION "2.8 Eberhard Moenkeberg <emoenke@gwdg.de>"
-
-#define SBPCD_DEBUG
-
-#ifndef CONFIG_ISO9660_FS
-#error "SBPCD: \"make config\" again. File system iso9660 is necessary."
-#endif
-
-/*
- * This may come back some day..
- */
-#define DDIOCSDBG      0x9000
+#define VERSION "2.9 Eberhard Moenkeberg <emoenke@gwdg.de>"
 
 /*
  * still testing around...
  */
+#define MULTISESSION_BY_DRIVER 0 /* needs "#define MULTISESSION" in
+                                  * linux/fs/isofs/inode.c
+                                  * if set to 0 here
+                                  */
+#define TEAC 0 /* if 1: enable TEAC CD-55A support (not useable yet) */
 #define JUKEBOX 1 /* tray control: eject tray if no disk is in */
 #define EJECT 1 /* tray control: eject tray after last use */
 #define LONG_TIMING 0 /* test against timeouts with "gold" CDs on CR-521 */
 #define SPEA_TEST 0
 #define PRINTK_BUG 0
 #define TEST_STI 0
+
+#if 0
+#define INLINE
+#else
+#define INLINE inline
+#endif
+
 /*==========================================================================*/
 /*
  * provisions for more than 1 driver issues
@@ -312,8 +325,10 @@ static int autoprobe[] =
               Galaxy (default), Reveal (one default) */
   0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
   0x260, 1, /* OmniCD */
-  0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default) */
-  0x340, 0, /* Lasermate, CI-101P */
+  0x320, 0, /* Lasermate, CI-101P, WDH-7001C, Galaxy, Reveal (other default),
+               Longshine LCS-6853 (default) */
+  0x338, 0, /* Reveal Sound Wave 32 card model #SC600 */
+  0x340, 0, /* Mozart sound card (default), Lasermate, CI-101P */
   0x360, 0, /* Lasermate, CI-101P */
   0x270, 1, /* Soundblaster 16 */
   0x670, 0, /* "sound card #9" */
@@ -326,7 +341,9 @@ static int autoprobe[] =
   0x630, 0, /* "sound card #9" (default) */
   0x650, 0, /* "sound card #9" */
 #if 0
-/* some "hazardous" locations (ethernet cards) */
+/* some "hazardous" locations
+ * (will stop the bus if a NE2000 ethernet card resides at offset -0x10)
+ */
   0x330, 0, /* Lasermate, CI-101P, WDH-7001C */
   0x350, 0, /* Lasermate, CI-101P */
   0x370, 0, /* Lasermate, CI-101P */
@@ -396,23 +413,23 @@ static int check_media_change(dev_t);
  * (1<<DBG_SQ)   dump SubQ frame
  * (1<<DBG_AUD)  "read audio" debugging
  * (1<<DBG_SEQ)  Sequoia interface configuration trace
+ * (1<<DBG_LCS)  Longshine LCS-7260 debugging trace
+ * (1<<DBG_TEA)  TEAC CD-55A debugging trace
  * (1<<DBG_000)  unnecessary information
  */
 #if 1
-static int sbpcd_debug =  (1<<DBG_INF) | (1<<DBG_WRN);
-#else
-#if SPEA_TEST
 static int sbpcd_debug =  (1<<DBG_INF) |
-                          (1<<DBG_INI) |
-                          (1<<DBG_ID)  |
-                          (1<<DBG_SEQ);
+                          (1<<DBG_WRN) |
+                          (1<<DBG_MUL);
 #else
 static int sbpcd_debug =  (1<<DBG_INF) |
                           (1<<DBG_TOC) |
                           (1<<DBG_MUL) |
+                          (1<<DBG_LCS) |
+                          (1<<DBG_TEA) |
                           (1<<DBG_UPC);
 #endif
-#endif
+static unsigned char setup_done = 0;
 static int sbpcd_ioaddr = CDROM_PORT;  /* default I/O base address */
 static int sbpro_type = SBPRO;
 static int CDo_command, CDo_reset;
@@ -426,6 +443,8 @@ static struct cdrom_tocentry tocentry;
 static struct cdrom_subchnl SC;
 static struct cdrom_volctrl volctrl;
 static struct cdrom_read_audio read_audio;
+static struct cdrom_multisession ms_info;
+
 static char *str_sb = "SoundBlaster";
 static char *str_lm = "LaserMate";
 static char *str_sp = "SPEA";
@@ -457,7 +476,7 @@ static struct wait_queue *sbp_waitq = NULL;
 /*==========================================================================*/
 
 static u_char drive_family[]="CR-5"; /* Panasonic CR-56x */
-static u_char lcs_family[]="LCS-"; /* Longshine LCS-7260 */
+static u_char lcs_family[]="LCS-7260"; /* Longshine LCS-7260 */
 static u_char drive_vendor[]="MATSHITA"; /* Matsushita CR-52x */
 
 static u_int response_count=0;
@@ -500,7 +519,7 @@ static int d=0; /* DriveStruct index: drive number */
 static struct {
   char drv_minor; /* minor number or -1 */
 
-  char drive_model[4];
+  char drive_model[9];
   char firmware_version[4];
   char f_eject; /* auto-eject flag: 0 or 1 */
   u_char *sbp_buf; /* Pointer to internal data buffer,
@@ -534,7 +553,7 @@ static struct {
   u_char vol_ctrl0;
   char vol_chan1;
   u_char vol_ctrl1;
-#if 000
+#if 000 /* no supported drive has it */
   char vol_chan2;
   u_char vol_ctrl2;
   char vol_chan3;
@@ -586,11 +605,8 @@ static struct {
 /*==========================================================================*/
 /*==========================================================================*/
 /*
- * DDI interface definitions
+ * DDI interface
  */
-#ifdef SBPCD_DEBUG
-# define DPRINTF(x)    sbpcd_dprintf x
-
 static void sbpcd_dprintf(int level, char *fmt, ...)
 {
   char buff[256];
@@ -608,13 +624,8 @@ static void sbpcd_dprintf(int level, char *fmt, ...)
 #endif
 }
 
-#else
-# define DPRINTF(x)    /* nothing */
-
-#endif SBPCD_DEBUG
-
 /*
- * maintain trace bit pattern
+ * DDI interface: runtime trace bit pattern maintainance
  */
 static int sbpcd_dbg_ioctl(unsigned long arg, int level)
 {
@@ -634,14 +645,13 @@ static int sbpcd_dbg_ioctl(unsigned long arg, int level)
   return(0);
 }
 
-
 /*==========================================================================*/
 /*==========================================================================*/
 /*
  * Wait a little while (used for polling the drive).  If in initialization,
  * setting a timeout doesn't work, so just loop for a while.
  */
-static inline void sbp_sleep(u_int jifs)
+static INLINE void sbp_sleep(u_int jifs)
 {
    current->state = TASK_INTERRUPTIBLE;
    current->timeout = jiffies + jifs;
@@ -653,7 +663,7 @@ static inline void sbp_sleep(u_int jifs)
 /*
  *  convert logical_block_address to m-s-f_number (3 bytes only)
  */
-static void lba2msf(int lba, u_char *msf)
+static INLINE void lba2msf(int lba, u_char *msf)
 {
   lba += CD_BLOCK_OFFSET;
   msf[0] = lba / (CD_SECS*CD_FRAMES);
@@ -666,12 +676,12 @@ static void lba2msf(int lba, u_char *msf)
 /*
  *  convert msf-bin to msf-bcd
  */
-static void bin2bcdx(u_char *p)  /* must work only up to 75 or 99 */
+static INLINE void bin2bcdx(u_char *p)  /* must work only up to 75 or 99 */
 {
   *p=((*p/10)<<4)|(*p%10);
 }
 /*==========================================================================*/
-static u_int blk2msf(u_int blk)
+static INLINE u_int blk2msf(u_int blk)
 {
   MSF msf;
   u_int mm;
@@ -684,32 +694,32 @@ static u_int blk2msf(u_int blk)
   return (msf.n);
 }
 /*==========================================================================*/
-static u_int make16(u_char rh, u_char rl)
+static INLINE u_int make16(u_char rh, u_char rl)
 {
   return ((rh<<8)|rl);
 }
 /*==========================================================================*/
-static u_int make32(u_int rh, u_int rl)
+static INLINE u_int make32(u_int rh, u_int rl)
 {
   return ((rh<<16)|rl);
 }
 /*==========================================================================*/
-static u_char swap_nibbles(u_char i)
+static INLINE u_char swap_nibbles(u_char i)
 {
   return ((i<<4)|(i>>4));
 }
 /*==========================================================================*/
-static u_char byt2bcd(u_char i)
+static INLINE u_char byt2bcd(u_char i)
 {
   return (((i/10)<<4)+i%10);
 }
 /*==========================================================================*/
-static u_char bcd2bin(u_char bcd)
+static INLINE u_char bcd2bin(u_char bcd)
 {
   return ((bcd>>4)*10+(bcd&0x0F));
 }
 /*==========================================================================*/
-static int msf2blk(int msfx)
+static INLINE int msf2blk(int msfx)
 {
   MSF msf;
   int i;
@@ -723,7 +733,7 @@ static int msf2blk(int msfx)
 /*
  *  convert m-s-f_number (3 bytes only) to logical_block_address 
  */
-static int msf2lba(u_char *msf)
+static INLINE int msf2lba(u_char *msf)
 {
   int i;
 
@@ -746,11 +756,13 @@ static int sta2err(int sta)
   if (sta==0x10) return (-11); /* read fault */
   if (sta>=0x16) return (-12); /* general failure */
   DriveStruct[d].CD_changed=0xFF;
-  if (sta==0x11) return (-15); /* invalid disk change */
+  if (sta==0x11) return (-15); /* invalid disk change (LCS: removed) */
+  if (lcs_drive)
+    if (sta==0x12) return (-15); /* invalid disk change (inserted) */
   return (-2); /* drive not ready */
 }
 /*==========================================================================*/
-static void clr_cmdbuf(void)
+static INLINE void clr_cmdbuf(void)
 {
   int i;
 
@@ -824,9 +836,41 @@ static int CDi_stat_loop(void)
        sbp_sleep(1);
        i = 1;
       }
+  DPRINTF((DBG_LCS,"SBPCD: CDi_stat_loop failed\n"));
   return (-1);
 }
 /*==========================================================================*/
+#if TEAC
+/*==========================================================================*/
+static int tst_DataReady(void)
+{
+  int i;
+  
+  i=inb(CDi_status);
+  if (i&s_not_data_ready) return (0);
+  return (1);
+}
+/*==========================================================================*/
+static int tst_ResultReady(void)
+{
+  int i;
+  
+  i=inb(CDi_status);
+  if (i&s_not_result_ready) return (0);
+  return (1);
+}
+/*==========================================================================*/
+static int tst_Attention(void)
+{
+  int i;
+  
+  i=inb(CDi_status);
+  if (i&s_attention) return (1);
+  return (0);
+}
+/*==========================================================================*/
+#endif TEAC
+/*==========================================================================*/
 static int ResponseInfo(void)
 {
   int i,j, st=0;
@@ -873,7 +917,7 @@ static int ResponseInfo(void)
 /*==========================================================================*/
 static int EvaluateStatus(int st)
 {
-  if (!new_drive)
+  if (old_drive)
     {
       DriveStruct[d].status_byte=0;
       if (st&p_caddin_old) DriveStruct[d].status_byte |= p_door_closed|p_caddy_in;
@@ -882,9 +926,21 @@ static int EvaluateStatus(int st)
       if (st&p_busy_old) DriveStruct[d].status_byte |= p_busy_new;
       if (st&p_disk_ok) DriveStruct[d].status_byte |= p_disk_ok;
     }
-  else { DriveStruct[d].status_byte=st;
-        st=p_success_old; /* for new drives: fake "successful" bit of old drives */
-       }
+  else if (lcs_drive)
+    {
+      DriveStruct[d].status_byte=0;
+      if (st&p_caddin_old) DriveStruct[d].status_byte |= p_disk_ok|p_caddy_in;
+      if (st&p_spinning) DriveStruct[d].status_byte |= p_spinning;
+      if (st&p_check) DriveStruct[d].status_byte |= p_check;
+      if (st&p_busy_old) DriveStruct[d].status_byte |= p_busy_new;
+      if (st&p_lcs_door_closed) DriveStruct[d].status_byte |= p_door_closed;
+      if (st&p_lcs_door_locked) DriveStruct[d].status_byte |= p_door_locked;
+      }
+  else /* new drive */ 
+    {
+      DriveStruct[d].status_byte=st;
+      st=p_success_old; /* for new drives: fake "successful" bit of old drives */
+    }
   return (st);
 }
 /*==========================================================================*/
@@ -932,6 +988,7 @@ static int ResponseStatus(void)
       return (-1);
     }
   i=inb(CDi_info);
+  DPRINTF((DBG_STA,"SBPCD: ResponseStatus: response %2X.\n", i));
   i=EvaluateStatus(i);
   return (i);
 }
@@ -941,15 +998,11 @@ static void xx_ReadStatus(void)
   int i;
 
   DPRINTF((DBG_STA,"SBPCD: giving xx_ReadStatus command\n"));
-
-  if (!new_drive) OUT(CDo_command,0x81);
-  else
-    {
-      SBPCD_CLI;
-      OUT(CDo_command,0x05);
-      for (i=0;i<6;i++) OUT(CDo_command,0);
-      SBPCD_STI;
-    }
+  SBPCD_CLI;
+  if (new_drive) OUT(CDo_command,0x05);
+  else OUT(CDo_command,0x81);
+  if (!old_drive) for (i=0;i<6;i++) OUT(CDo_command,0);
+  SBPCD_STI;
 }
 /*==========================================================================*/
 static int xx_ReadError(void)
@@ -968,14 +1021,17 @@ static int xx_ReadError(void)
     {
       drvcmd[0]=0x82;
       response_count=6;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
+      if (lcs_drive)
+       flags_cmd_out=f_putcmd;
+      else
+       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
     }
   i=cmd_out();
   DriveStruct[d].error_byte=0;
   DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: cmd_out(82) returns %d (%02X)\n",i,i));
   if (i<0) return (i);
-  if (new_drive) i=2;
-  else i=1;
+  if (old_drive) i=1;
+  else i=2;
   DriveStruct[d].error_byte=infobuf[i];
   DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: infobuf[%d] is %d (%02X)\n",i,DriveStruct[d].error_byte,DriveStruct[d].error_byte));
   i=sta2err(infobuf[i]);
@@ -1052,14 +1108,14 @@ static int xx_Seek(u_int pos, char f_blk_msf)
 
   clr_cmdbuf();
   if (f_blk_msf>1) return (-3);
-  if (!new_drive)
+  if (old_drive)
     {
       if (f_blk_msf==1) pos=msf2blk(pos);
       drvcmd[2]=(pos>>16)&0x00FF;
       drvcmd[3]=(pos>>8)&0x00FF;
       drvcmd[4]=pos&0x00FF;
       flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
-                      f_ResponseStatus | f_obey_p_check | f_bit1;
+                      f_ResponseStatus | f_obey_p_check | f_bit1;
     }
   else
     {
@@ -1067,7 +1123,10 @@ static int xx_Seek(u_int pos, char f_blk_msf)
       drvcmd[1]=(pos>>16)&0x00FF;
       drvcmd[2]=(pos>>8)&0x00FF;
       drvcmd[3]=pos&0x00FF;
-      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+      if (lcs_drive)
+       flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+      else
+       flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
     }
   drvcmd[0]=0x01;
   response_count=0;
@@ -1082,21 +1141,17 @@ static int xx_SpinUp(void)
   DPRINTF((DBG_SPI,"SBPCD: SpinUp.\n"));
   DriveStruct[d].in_SpinUp = 1;
   clr_cmdbuf();
-  if (old_drive)
+  if (!new_drive)
     {
       drvcmd[0]=0x05;
-      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+       f_ResponseStatus|f_obey_p_check|f_bit1;
     }
-  else if (new_drive)
+  else /* new_drive */
     {
       drvcmd[0]=0x02;
       flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
     }
-  else /* lcs_drive */
-    {
-      drvcmd[0]=0x0D;
-      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
-    }
   response_count=0;
   i=cmd_out();
   DriveStruct[d].in_SpinUp = 0;
@@ -1119,7 +1174,7 @@ static int yy_SpinDown(void)
     {
       drvcmd[0]=0x0D;
       drvcmd[1]=1;
-      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
       response_count=0;
     }
   i=cmd_out();
@@ -1206,7 +1261,7 @@ static int xx_SetVolume(void)
       drvcmd[1]=0x03;
       drvcmd[4]=control0;
       drvcmd[5]=value0;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
     }
   else /* old_drive, different firmware levels */
     {
@@ -1307,7 +1362,8 @@ static int xy_DriveReset(void)
       response_count=0;
       i=cmd_out();
     }
-  sbp_sleep(100); /* wait a second */
+  if (lcs_drive) sbp_sleep(500); /* wait 5 seconds */
+  else sbp_sleep(100); /* wait a second */
   flush_status();
   i=GetStatus();
   if (i>=0) return -1;
@@ -1367,7 +1423,12 @@ static int xx_Pause_Resume(int pau_res)
   else
     {
       drvcmd[0]=0x8D;
-      flags_cmd_out=f_putcmd|f_respo2|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if (lcs_drive)
+       flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+         f_obey_p_check|f_bit1;
+      else
+       flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|
+         f_obey_p_check;
     }
   if (pau_res!=1) drvcmd[1]=0x80;
   response_count=0;
@@ -1381,6 +1442,7 @@ static int yy_LockDoor(char lock)
 
   if (old_drive) return (0);
   DPRINTF((DBG_LCK,"SBPCD: yy_LockDoor: %d (drive %d)\n", lock, d));
+  DPRINTF((DBG_LCS,"SBPCD: p_door_locked bit %d before\n", st_door_locked));
   clr_cmdbuf();
   if (new_drive)
     {
@@ -1393,10 +1455,11 @@ static int yy_LockDoor(char lock)
     {
       drvcmd[0]=0x0E;
       if (lock==1) drvcmd[1]=0x01;
-      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
       response_count=0;
     }
   i=cmd_out();
+  DPRINTF((DBG_LCS,"SBPCD: p_door_locked bit %d after\n", st_door_locked));
   return (i);
 }
 /*==========================================================================*/
@@ -1404,14 +1467,25 @@ static int yy_CloseTray(void)
 {
   int i;
 
-  if (!new_drive) return (0);
+  if (old_drive) return (0);
   DPRINTF((DBG_LCK,"SBPCD: yy_CloseTray (drive %d)\n", d));
+  DPRINTF((DBG_LCS,"SBPCD: p_door_closed bit %d before\n", st_door_closed));
 
   clr_cmdbuf();
-  drvcmd[0]=0x07;
-  flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+  if (new_drive)
+    {
+      drvcmd[0]=0x07;
+      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+    }
+  else /* lcs_drive */
+    {
+      drvcmd[0]=0x0D;
+      flags_cmd_out=f_putcmd|f_respo2|f_lopsta|f_getsta|
+       f_ResponseStatus|f_obey_p_check|f_bit1;
+    }
   response_count=0;
   i=cmd_out();
+  DPRINTF((DBG_LCS,"SBPCD: p_door_closed bit %d after\n", st_door_closed));
   return (i);
 }
 /*==========================================================================*/
@@ -1433,7 +1507,10 @@ static int xx_ReadSubQ(void)
        {
          drvcmd[0]=0x89;
          drvcmd[1]=0x02;
-         flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+         if (lcs_drive)
+           flags_cmd_out=f_putcmd;
+         else
+           flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
          response_count=13;
        }
       i=cmd_out();
@@ -1483,7 +1560,10 @@ static int xx_ModeSense(void)
     {
       drvcmd[0]=0x85;
       drvcmd[1]=0x00;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if (lcs_drive)
+       flags_cmd_out=f_putcmd;
+      else
+       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
       response_count=2;
     }
   i=cmd_out();
@@ -1533,7 +1613,10 @@ static int xx_ModeSelect(int framesize)
       drvcmd[2]=(DriveStruct[d].frame_size>>8)&0xFF;
       drvcmd[3]=DriveStruct[d].frame_size&0xFF;
       drvcmd[4]=0x00;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+       flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check;
+      else
+       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   response_count=0;
   i=cmd_out();
@@ -1563,7 +1646,10 @@ static int xx_TellVolume(void)
       drvcmd[0]=0x85;
       drvcmd[1]=0x03;
       response_count=2;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+       flags_cmd_out=f_putcmd;
+      else
+       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   i=cmd_out();
   if (i<0) return (i);
@@ -1629,10 +1715,13 @@ static int xx_TellVolume(void)
   DriveStruct[d].vol_ctrl0=vol0;
   DriveStruct[d].vol_chan1=chan1;
   DriveStruct[d].vol_ctrl1=vol1;
-  DriveStruct[d].vol_chan2=2;
-  DriveStruct[d].vol_ctrl2=0xFF;
-  DriveStruct[d].vol_chan3=3;
-  DriveStruct[d].vol_ctrl3=0xFF;
+  if (!lcs_drive)
+    {
+      DriveStruct[d].vol_chan2=2;
+      DriveStruct[d].vol_ctrl2=0xFF;
+      DriveStruct[d].vol_chan3=3;
+      DriveStruct[d].vol_ctrl3=0xFF;
+    }
   DriveStruct[d].diskstate_flags |= volume_bit;
   return (0);
 }
@@ -1652,7 +1741,10 @@ static int xx_ReadCapacity(void)
   else
     {
       drvcmd[0]=0x88;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+       flags_cmd_out=f_putcmd;
+      else
+       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   response_count=5;
   i=cmd_out();
@@ -1679,7 +1771,10 @@ static int xx_ReadTocDescr(void)
   else
     {
       drvcmd[0]=0x8B;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+       flags_cmd_out=f_putcmd;
+      else
+       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   response_count=6;
   i=cmd_out();
@@ -1709,7 +1804,10 @@ static int xx_ReadTocEntry(int num)
     {
       drvcmd[0]=0x8C;
       drvcmd[1]=0x02;
-      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      if(lcs_drive)
+       flags_cmd_out=f_putcmd;
+      else
+       flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
     }
   drvcmd[2]=num;
   response_count=8;
@@ -1737,7 +1835,10 @@ static int xx_ReadPacket(void)
   clr_cmdbuf();
   drvcmd[0]=0x8E;
   drvcmd[1]=response_count;
-  flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+  if(lcs_drive)
+    flags_cmd_out=f_putcmd;
+  else
+    flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
   i=cmd_out();
   return (i);
 }
@@ -1801,6 +1902,7 @@ static int xx_ReadUPC(void)
       if (!new_drive)
        {
          response_count=16;
+         if (lcs_drive) flags_cmd_out=f_putcmd;
          i=xx_ReadPacket();
          if (i<0) return (i);
        }
@@ -1847,6 +1949,7 @@ static int yy_CheckMultiSession(void)
 
   DriveStruct[d].f_multisession=0;
   clr_cmdbuf();
+  DriveStruct[d].lba_multi=0;
   if (new_drive)
     {
       drvcmd[0]=0x8D;
@@ -1856,17 +1959,18 @@ static int yy_CheckMultiSession(void)
       if (i<0) return (i);
       if ((infobuf[0]&0x80)!=0)
        {
-         DPRINTF((DBG_MUL,"SBPCD: MultiSession CD detected: %02X %02X %02X %02X %02X %02X\n",
-                         infobuf[0], infobuf[1], infobuf[2],
-                        infobuf[3], infobuf[4], infobuf[5]));
          DriveStruct[d].f_multisession=1;
          DriveStruct[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
-                                   make16(infobuf[2],infobuf[3])));
-         DriveStruct[d].last_redirect=20;
+                                                 make16(infobuf[2],infobuf[3])));
+         DriveStruct[d].last_redirect=19;
          /* preliminary - has to get adjusted the following way:
            *   look at the first byte of frames 17 ff. until 0xFF is seen
            *   the frame before this one is the last continuation frame
            */
+         DPRINTF((DBG_MUL,"SBPCD: MultiSession CD detected: %02X %02X %02X %02X %02X %02X (%d)\n",
+                  infobuf[0], infobuf[1], infobuf[2],
+                  infobuf[3], infobuf[4], infobuf[5],
+                  DriveStruct[d].lba_multi));
        }
     }
   else if (lcs_drive)
@@ -1875,23 +1979,24 @@ static int yy_CheckMultiSession(void)
       drvcmd[1]=3;
       drvcmd[2]=1;
       response_count=8;
-      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+      flags_cmd_out=f_putcmd;
       i=cmd_out();
       if (i<0) return (i);
-      DPRINTF((DBG_MUL,"SBPCD: MultiSession Info: %02X %02X %02X %02X %02X %02X %02X %02X\n",
-              infobuf[0], infobuf[1], infobuf[2], infobuf[3],
-              infobuf[4], infobuf[5], infobuf[6], infobuf[7]));
       DriveStruct[d].lba_multi=msf2blk(make32(make16(0,infobuf[5]),
                                              make16(infobuf[6],infobuf[7])));
+      DPRINTF((DBG_MUL,"SBPCD: MultiSession Info: %02X %02X %02X %02X %02X %02X %02X %02X (%d)\n",
+              infobuf[0], infobuf[1], infobuf[2], infobuf[3],
+              infobuf[4], infobuf[5], infobuf[6], infobuf[7],
+              DriveStruct[d].lba_multi));
       if (DriveStruct[d].lba_multi>200)
        {
          DPRINTF((DBG_MUL,"SBPCD: MultiSession base: %06X\n", DriveStruct[d].lba_multi));
          DriveStruct[d].f_multisession=1;
-         DriveStruct[d].last_redirect=20;
+         DriveStruct[d].last_redirect=19;
          /* preliminary - has to get adjusted the following way:
-           *   look at the first byte of frames 17 ff. until 0xFF is seen;
-           *   the frame before this one is the last repetition frame.
-           */
+          *   look at the first byte of frames 17 ff. until 0xFF is seen;
+          *   the frame before this one is the last repetition frame.
+          */
        }
     }
   return (0);
@@ -1969,6 +2074,53 @@ static void check_datarate(void)
 #endif CDMKE
 }
 /*==========================================================================*/
+#if TEAC
+/*==========================================================================*/
+static void teac_reset(int drv_id)
+{
+  int i;
+#define CMD_TEAC_RESET 0xc0
+
+  OUT(CDo_sel_d_i,0);
+  OUT(CDo_enable,drv_id);
+  OUT(CDo_command,CMD_TEAC_RESET);
+  for (i=0;i<9;i++) OUT(CDo_command,0);
+  DPRINTF((DBG_TEA,"SBPCD: TEAC soft reset.\n"));
+  sbp_sleep(100); /* wait a second */
+}
+/*==========================================================================*/
+static int look_for_drive(int drv_id)
+{
+  int i;
+  if (sbpro_type!=1) teac_reset(drv_id);
+
+  OUT(CDo_enable,drv_id);
+  OUT(CDo_sel_d_i,0);
+  i=inb(CDi_status);
+  if (i&s_not_result_ready) return (-1); /* drive not present */
+  i=inb(CDi_info);
+  DPRINTF((DBG_TEA,"SBPCD: TEAC look_for_drive: %02X.\n",i));
+  if (i!=0x55) return (-2); /* something else present */
+  return (1); /* drive found */
+}
+/*==========================================================================*/
+static int find_teac_drives(void)
+{
+  int i, j, found;
+
+  found=0;
+  for (i=0;i<4;i++)
+    {
+      j=look_for_drive(i);
+      if (j<1) continue;
+      found++;
+      DPRINTF((DBG_TEA,"SBPCD: TEAC drive (id=%d) found.\n",i));
+    }
+  return (found);
+}
+/*==========================================================================*/
+#endif TEAC
+/*==========================================================================*/
 static int check_version(void)
 {
   int i, j;
@@ -2003,6 +2155,7 @@ static int check_version(void)
       DriveStruct[d].drive_model[1]=infobuf[i++];
       DriveStruct[d].drive_model[2]='-';
       DriveStruct[d].drive_model[3]='x';
+      DriveStruct[d].drive_model[4]=0;
       DriveStruct[d].drv_type=drv_new;
     }
   if (!DriveStruct[d].drv_type)
@@ -2014,18 +2167,18 @@ static int check_version(void)
          DriveStruct[d].drive_model[1]='x';
          DriveStruct[d].drive_model[2]='-';
          DriveStruct[d].drive_model[3]='x';
+         DriveStruct[d].drive_model[4]=0;
          DriveStruct[d].drv_type=drv_old;
        }
     }
   if (!DriveStruct[d].drv_type)
     {
-      for (i=0;i<4;i++) if (infobuf[i]!=lcs_family[i]) break;
-      if (i==4)
+      for (i=0;i<8;i++) if (infobuf[i]!=lcs_family[i]) break;
+      if (i==8)
        {
-         DriveStruct[d].drive_model[0]=infobuf[i++];
-         DriveStruct[d].drive_model[1]=infobuf[i++];
-         DriveStruct[d].drive_model[2]=infobuf[i++];
-         DriveStruct[d].drive_model[3]=infobuf[i++];
+         for (j=0;j<8;j++)
+           DriveStruct[d].drive_model[j]=infobuf[j];
+         DriveStruct[d].drive_model[8]=0;
          DriveStruct[d].drv_type=drv_lcs;
        }
     }
@@ -2034,10 +2187,18 @@ static int check_version(void)
       DPRINTF((DBG_INI,"SBPCD: check_version: error.\n"));
       return (-1);
     }
-  if (lcs_drive) DriveStruct[d].drv_type=drv_260; /* still needs a closer look */
+  for (j=0;j<4;j++) DriveStruct[d].firmware_version[j]=infobuf[i+j];
+  if (lcs_drive)
+    {
+      DriveStruct[d].drv_type=drv_260;
+      if ((DriveStruct[d].firmware_version[0]!='A') ||
+         (DriveStruct[d].firmware_version[1]!='4') ||
+         (DriveStruct[d].firmware_version[2]!='F') ||
+         (DriveStruct[d].firmware_version[3]!='4'))
+       printk("SBPCD: please mail me your LCS-7260 DOS driver.\n");
+    }
   else
     {
-      for (j=0;j<4;j++) DriveStruct[d].firmware_version[j]=infobuf[i+j];
       j = (DriveStruct[d].firmware_version[0] & 0x0F) * 100 +
        (DriveStruct[d].firmware_version[2] & 0x0F) *10 +
          (DriveStruct[d].firmware_version[3] & 0x0F);
@@ -2053,6 +2214,7 @@ static int check_version(void)
       else if (j<300) DriveStruct[d].drv_type=drv_211;
       else DriveStruct[d].drv_type=drv_300;
     }
+  DPRINTF((DBG_LCS,"SBPCD: drive type %02X\n",DriveStruct[d].drv_type));
   DPRINTF((DBG_INI,"SBPCD: check_version done.\n"));
   return (0);
 }
@@ -2094,11 +2256,21 @@ static int check_drives(void)
          DriveStruct[d].drv_options=drv_pattern[j];
          if (!new_drive)
            DriveStruct[d].drv_options&=~(speed_auto|speed_300|speed_150);
-         printk("%sDrive %d: %s%.4s (%.4s)\n", printk_header,
-                DriveStruct[d].drv_minor,
-                 drive_family,
-                 DriveStruct[d].drive_model,
-                 DriveStruct[d].firmware_version);
+         if (lcs_drive)
+           {
+             printk("%sDrive %d: %s (%.4s)\n", printk_header,
+                    DriveStruct[d].drv_minor,
+                    DriveStruct[d].drive_model,
+                    DriveStruct[d].firmware_version);
+           }
+         else
+           {
+             printk("%sDrive %d: %s%.4s (%.4s)\n", printk_header,
+                    DriveStruct[d].drv_minor,
+                    drive_family,
+                    DriveStruct[d].drive_model,
+                    DriveStruct[d].firmware_version);
+           }
          printk_header="       - ";
        }
       else DriveStruct[d].drv_minor=-1;
@@ -2529,6 +2701,7 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
                       u_long arg)
 {
   int i, st;
+  unsigned int *p_lba;
 
   DPRINTF((DBG_IO2,"SBPCD: ioctl(%d, 0x%08lX, 0x%08lX)\n",
                                MINOR(inode->i_rdev), cmd, arg));
@@ -2568,12 +2741,14 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
       switch (DriveStruct[d].audio_state)
        {
        case audio_playing:
-         i=xx_Pause_Resume(1);
+         if (lcs_drive) i=xx_ReadSubQ();
+         else i=xx_Pause_Resume(1);
          if (i<0) return (-EIO);
-         DriveStruct[d].audio_state=audio_pausing;
-         i=xx_ReadSubQ();
+         if (lcs_drive) i=xx_Pause_Resume(1);
+         else i=xx_ReadSubQ();
          if (i<0) return (-EIO);
          DriveStruct[d].pos_audio_start=DriveStruct[d].SubQ_run_tot;
+         DriveStruct[d].audio_state=audio_pausing;
          return (0);
        case audio_pausing:
          i=xx_Seek(DriveStruct[d].pos_audio_start,1);
@@ -2589,7 +2764,10 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
       /* been paused with a PAUSE command.                                */
       /* It will resume playing from the location saved in SubQ_run_tot.  */
       if (DriveStruct[d].audio_state!=audio_pausing) return -EINVAL;
-      i=xx_Pause_Resume(3);
+      if (lcs_drive)
+       i=xx_PlayAudio(DriveStruct[d].pos_audio_start,
+                      DriveStruct[d].pos_audio_end);
+      else i=xx_Pause_Resume(3);
       if (i<0) return (-EIO);
       DriveStruct[d].audio_state=audio_playing;
       return (0);
@@ -2716,7 +2894,7 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
       
     case CDROMEJECT_SW:
       DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMEJECT_SW entered.\n"));
-      if (!new_drive) return (0);
+      if (old_drive) return (0);
       DriveStruct[d].f_eject=arg;
       return (0);
       
@@ -2987,6 +3165,37 @@ static int sbpcd_ioctl(struct inode *inode, struct file *file, u_int cmd,
        return (0);
       } /* end of CDROMREADAUDIO */
 
+    case CDROMMULTISESSION: /* tell start-of-last-session to user */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMMULTISESSION entered.\n"));
+      st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_multisession));
+      if (st) return (st);
+      memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
+      if (ms_info.addr_format==CDROM_MSF) /* MSF-bin requested */
+       lba2msf(DriveStruct[d].lba_multi,&ms_info.addr.msf.minute);
+      else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
+       ms_info.addr.lba=DriveStruct[d].lba_multi;
+      else return (-EINVAL);
+      if (DriveStruct[d].f_multisession) ms_info.xa_flag=1; /* valid redirection address */
+      else ms_info.xa_flag=0; /* invalid redirection address */
+      st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_multisession));
+      if (st) return (st);
+      memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
+      DPRINTF((DBG_MUL,"SBPCD: ioctl: CDROMMULTISESSION done (%d, %08X).\n",
+              ms_info.xa_flag, ms_info.addr.lba));
+      return (0);
+
+    case CDROMMULTISESSION_SYS: /* tell start-of-last-session to kernel */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMMULTISESSION_SYS entered.\n"));
+      if(!suser()) return -EACCES;
+      p_lba=arg;
+      if (DriveStruct[d].f_multisession)
+       *p_lba=DriveStruct[d].lba_multi;
+      else
+       *p_lba=0;
+      DPRINTF((DBG_MUL,"SBPCD: ioctl: CDROMMULTISESSION_SYS done (%d).\n",
+              p_lba[0]));
+      return (0);
+
     case BLKRASET:
       if(!suser())  return -EACCES;
       if(!inode->i_rdev) return -EINVAL;
@@ -3129,7 +3338,8 @@ static void sbp_read_cmd(void)
   DriveStruct[d].sbp_first_frame=DriveStruct[d].sbp_last_frame=-1;      /* purge buffer */
   block=CURRENT->sector/4;
 
-  if (new_drive)
+#if MULTISESSION_BY_DRIVER
+  if (!old_drive)
     {
 #if MANY_SESSION
       DPRINTF((DBG_MUL,"SBPCD: read MSF %08X\n", blk2msf(block)));
@@ -3151,6 +3361,7 @@ static void sbp_read_cmd(void)
          }
 #endif MANY_SESSION
     }
+#endif MULTISESSION_BY_DRIVER
 
   if (block+SBP_BUFFER_FRAMES <= DriveStruct[d].CDsize_frm)
     DriveStruct[d].sbp_read_frames = SBP_BUFFER_FRAMES;
@@ -3247,7 +3458,7 @@ static int sbp_data(void)
 #if LONG_TIMING
       for (timeout=jiffies+900; ; )
 #else
-      for (timeout=jiffies+100; ; )
+      for (timeout=jiffies+ ( (lcs_drive)? 300:100 ); ; )
 #endif
        {
          for ( ; try!=0;try--)
@@ -3389,12 +3600,20 @@ static int sbpcd_open(struct inode *ip, struct file *fp)
   flags_cmd_out |= f_respo2;
   xx_ReadStatus();                         /* command: give 1-byte status */
   i=ResponseStatus();
-  if (!st_door_closed) yy_CloseTray();
-  if (!st_spinning) xx_SpinUp();
-
-  flags_cmd_out |= f_respo2;
-  xx_ReadStatus();                         /* command: give 1-byte status */
-  i=ResponseStatus();
+  if (!st_door_closed)
+    {
+      yy_CloseTray();
+      flags_cmd_out |= f_respo2;
+      xx_ReadStatus();
+      i=ResponseStatus();
+    }
+  if (!st_spinning)
+    {
+      xx_SpinUp();
+      flags_cmd_out |= f_respo2;
+      xx_ReadStatus();
+      i=ResponseStatus();
+    }
   if (i<0)
     {
       DPRINTF((DBG_INF,"SBPCD: sbpcd_open: xx_ReadStatus timed out\n"));
@@ -3408,7 +3627,7 @@ static int sbpcd_open(struct inode *ip, struct file *fp)
       do
        i=yy_LockDoor(0);
       while (i!=0);
-      if (new_drive) yy_SpinDown(); /* eject tray */
+      if (!old_drive) yy_SpinDown(); /* eject tray */
 #endif
       return (-ENXIO);
     }
@@ -3513,6 +3732,7 @@ static
 #endif
 void sbpcd_setup(char *s, int *p)
 {
+  setup_done++;
   DPRINTF((DBG_INI,"SBPCD: sbpcd_setup called with %04X,%s\n",p[1], s));
   sbpro_type=0;
   if (!strcmp(s,str_sb)) sbpro_type=1;
@@ -3522,13 +3742,13 @@ void sbpcd_setup(char *s, int *p)
   CDo_command=sbpcd_ioaddr;
   CDi_info=sbpcd_ioaddr;
   CDi_status=sbpcd_ioaddr+1;
+  CDo_sel_d_i=sbpcd_ioaddr+1;
   CDo_reset=sbpcd_ioaddr+2;
   CDo_enable=sbpcd_ioaddr+3; 
   if (sbpro_type==1)
     {
       MIXER_addr=sbpcd_ioaddr-0x10+0x04;
       MIXER_data=sbpcd_ioaddr-0x10+0x05;
-      CDo_sel_d_i=sbpcd_ioaddr+1;
       CDi_data=sbpcd_ioaddr;
     }
   else CDi_data=sbpcd_ioaddr+2;
@@ -3600,23 +3820,25 @@ unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
 
   DPRINTF((DBG_INF,"SBPCD version %s\n", VERSION));
 
-  DPRINTF((DBG_INF,"SBPCD: Looking for a SoundBlaster/Matsushita CD-ROM drive\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-  DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = W A R N I N G = = = = = = = = = =\n"));
-  DPRINTF((DBG_WRN,"SBPCD: Auto-Probing can cause a hang (f.e. touching an ethernet card).\n"));
-  DPRINTF((DBG_WRN,"SBPCD: If that happens, you have to reboot and use the\n"));
-  DPRINTF((DBG_WRN,"SBPCD: LILO (kernel) command line feature like:\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-  DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x230,SoundBlaster\n"));
-  DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
-  DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x300,LaserMate\n"));
-  DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
-  DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x330,SPEA\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-  DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n"));
-  DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
-  DPRINTF((DBG_WRN,"SBPCD: \n"));
-
+  if (!setup_done)
+    {
+      DPRINTF((DBG_INF,"SBPCD: Looking for Matsushita, Panasonic, CreativeLabs, IBM, Longshine CD-ROM drives\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+      DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = W A R N I N G = = = = = = = = = =\n"));
+      DPRINTF((DBG_WRN,"SBPCD: Auto-Probing can cause a hang (f.e. touching an ethernet card).\n"));
+      DPRINTF((DBG_WRN,"SBPCD: If that happens, you have to reboot and use the\n"));
+      DPRINTF((DBG_WRN,"SBPCD: LILO (kernel) command line feature like:\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+      DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x230,SoundBlaster\n"));
+      DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
+      DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x300,LaserMate\n"));
+      DPRINTF((DBG_WRN,"SBPCD: or like:\n"));
+      DPRINTF((DBG_WRN,"SBPCD:    LILO boot: linux sbpcd=0x330,SPEA\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+      DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n"));
+      DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
+      DPRINTF((DBG_WRN,"SBPCD: \n"));
+    }
   autoprobe[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
   autoprobe[1]=sbpro_type; /* possibly changed by kernel command line */
 
@@ -3642,6 +3864,14 @@ unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
              continue;
            }
        }
+#if TEAC
+      i=find_teac_drives();
+      if (i>0)
+       {
+         DPRINTF((DBG_INF,"SBPCD: found %d TEAC drives. A wonder.\n",i));
+         DPRINTF((DBG_INF,"SBPCD: - "));
+       }
+#endif TEAC
       i=check_drives();
       DPRINTF((DBG_INI,"SBPCD: check_drives done.\n"));
       if (i>=0) break; /* drive found */
@@ -3673,14 +3903,17 @@ unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
   check_datarate();
   DPRINTF((DBG_INI,"SBPCD: check_datarate done.\n"));
 
-  OUT(CDo_reset,0);
-  sbp_sleep(100);
+   if (!lcs_drive)
+   {
+     OUT(CDo_reset,0);
+     sbp_sleep(100);
+   }
 
   for (j=0;j<NR_SBPCD;j++)
     {
       if (DriveStruct[j].drv_minor==-1) continue;
       switch_drive(j);
-      xy_DriveReset();
+      if (!lcs_drive) xy_DriveReset();
       if (!st_spinning) xx_SpinUp();
       DriveStruct[d].sbp_first_frame = -1;  /* First frame in buffer */
       DriveStruct[d].sbp_last_frame = -1;   /* Last frame in buffer  */
@@ -3689,7 +3922,7 @@ unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
       DriveStruct[d].CD_changed=1;
       DriveStruct[d].frame_size=CD_FRAMESIZE;
 #if EJECT
-      if (new_drive) DriveStruct[d].f_eject=1;
+      if (!old_drive) DriveStruct[d].f_eject=1;
       else DriveStruct[d].f_eject=0;
 #else
       DriveStruct[d].f_eject=0;
@@ -3708,10 +3941,15 @@ unsigned long SBPCD_INIT(u_long mem_start, u_long mem_end)
            }
        }
       DPRINTF((DBG_INI,"SBPCD: init: first GetStatus: %d\n",i));
+      DPRINTF((DBG_LCS,"SBPCD: init: first GetStatus: error_byte=%d\n",
+              DriveStruct[d].error_byte));
       if (DriveStruct[d].error_byte==aud_12)
        {
          do { i=GetStatus();
               DPRINTF((DBG_INI,"SBPCD: init: second GetStatus: %02X\n",i));
+              DPRINTF((DBG_LCS,
+                       "SBPCD: init: second GetStatus: error_byte=%d\n",
+                       DriveStruct[d].error_byte));
               if (i<0) break;
               if (!st_caddy_in) break;
             }
index ec8430aac7cd21381733eaf404dea88b303313b7..4f766452c5a29e001821af983f2fd46423a15f96 100644 (file)
@@ -25,6 +25,11 @@ SRCS  = tty_io.c n_tty.c console.c keyboard.c serial.c \
        defkeymap.c uni_to_437.c
 
 
+ifdef CONFIG_CYCLADES
+OBJS := $(OBJS) cyclades.o
+SRCS := $(SRCS) cyclades.c
+endif
+
 ifdef CONFIG_ATIXL_BUSMOUSE
 M = y
 OBJS := $(OBJS) atixlmouse.o
diff --git a/drivers/char/cyclades.c b/drivers/char/cyclades.c
new file mode 100644 (file)
index 0000000..2e49035
--- /dev/null
@@ -0,0 +1,2696 @@
+static char rcsid[] =
+"$Revision: 1.35 $$Date: 1994/12/16 13:54:18 $";
+/*
+ *  linux/kernel/cyclades.c
+ *
+ * Much of the design and some of the code came from serial.c
+ * which was copyright (C) 1991, 1992  Linus Torvalds.  It was
+ * extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92,
+ * and then fixed as suggested by Michael K. Johnson 12/12/92.
+ *
+ * This version does not support shared irq's.
+ *
+ * This module exports the following rs232 io functions:
+ *   long cy_init(long);
+ *   int  cy_open(struct tty_struct *tty, struct file *filp);
+ *
+ * $Log: cyclades.c,v $
+ * Revision 1.35  1994/12/16  13:54:18  steffen
+ * addditional patch by Marcio Saito for board detection
+ * Accidently left out in 1.34
+ *
+ * Revision 1.34  1994/12/10  12:37:12  steffen
+ * This is the corrected version as suggested by Marcio Saito
+ *
+ * Revision 1.33  1994/12/01  22:41:18  bentson
+ * add hooks to support more high speeds directly; add tytso
+ * patch regarding CLOCAL wakeups
+ *
+ * Revision 1.32  1994/11/23  19:50:04  bentson
+ * allow direct kernel control of higher signalling rates;
+ * look for cards at additional locations
+ *
+ * Revision 1.31  1994/11/16  04:33:28  bentson
+ * ANOTHER fix from Corey Minyard, minyard@wf-rch.cirr.com--
+ * a problem in chars_in_buffer has been resolved by some
+ * small changes;  this should yield smoother output
+ *
+ * Revision 1.30  1994/11/16  04:28:05  bentson
+ * Fix from Corey Minyard, Internet: minyard@metronet.com,
+ * UUCP: minyard@wf-rch.cirr.com, WORK: minyardbnr.ca, to
+ * cy_hangup that appears to clear up much (all?) of the
+ * DTR glitches; also he's added/cleaned-up diagnostic messages
+ *
+ * Revision 1.29  1994/11/16  04:16:07  bentson
+ * add change proposed by Ralph Sims, ralphs@halcyon.com, to
+ * operate higher speeds in same way as other serial ports;
+ * add more serial ports (for up to two 16-port muxes).
+ *
+ * Revision 1.28  1994/11/04  00:13:16  root
+ * turn off diagnostic messages
+ *
+ * Revision 1.27  1994/11/03  23:46:37  root
+ * bunch of changes to bring driver into greater conformance
+ * with the serial.c driver (looking for missed fixes)
+ *
+ * Revision 1.26  1994/11/03  22:40:36  root
+ * automatic interrupt probing fixed.
+ *
+ * Revision 1.25  1994/11/03  20:17:02  root
+ * start to implement auto-irq
+ *
+ * Revision 1.24  1994/11/03  18:01:55  root
+ * still working on modem signals--trying not to drop DTR
+ * during the getty/login processes
+ *
+ * Revision 1.23  1994/11/03  17:51:36  root
+ * extend baud rate support; set receive threshold as function
+ * of baud rate; fix some problems with RTS/CTS;
+ *
+ * Revision 1.22  1994/11/02  18:05:35  root
+ * changed arguments to udelay to type long to get
+ * delays to be of correct duration
+ *
+ * Revision 1.21  1994/11/02  17:37:30  root
+ * employ udelay (after calibrating loops_per_second earlier
+ * in init/main.c) instead of using home-grown delay routines
+ *
+ * Revision 1.20  1994/11/02  03:11:38  root
+ * cy_chars_in_buffer forces a return value of 0 to let
+ * login work (don't know why it does); some functions
+ * that were returning EFAULT, now executes the code;
+ * more work on deciding when to disable xmit interrupts;
+ *
+ * Revision 1.19  1994/11/01  20:10:14  root
+ * define routine to start transmission interrupts (by enabling
+ * transmit interrupts); directly enable/disable modem interrupts;
+ *
+ * Revision 1.18  1994/11/01  18:40:45  bentson
+ * Don't always enable transmit interrupts in startup; interrupt on
+ * TxMpty instead of TxRdy to help characters get out before shutdown;
+ * restructure xmit interrupt to check for chars first and quit if
+ * none are ready to go; modem status (MXVRx) is upright, _not_ inverted
+ * (to my view);
+ *
+ * Revision 1.17  1994/10/30  04:39:45  bentson
+ * rename serial_driver and callout_driver to cy_serial_driver and
+ * cy_callout_driver to avoid linkage interference; initialize
+ * info->type to PORT_CIRRUS; ruggedize paranoia test; elide ->port
+ * from cyclades_port structure; add paranoia check to cy_close;
+ *
+ * Revision 1.16  1994/10/30  01:14:33  bentson
+ * change major numbers; add some _early_ return statements;
+ *
+ * Revision 1.15  1994/10/29  06:43:15  bentson
+ * final tidying up for clean compile;  enable some error reporting
+ *
+ * Revision 1.14  1994/10/28  20:30:22  Bentson
+ * lots of changes to drag the driver towards the new tty_io
+ * structures and operation.  not expected to work, but may
+ * compile cleanly.
+ *
+ * Revision 1.13  1994/07/21  23:08:57  Bentson
+ * add some diagnostic cruft; support 24 lines (for testing
+ * both -8Y and -16Y cards; be more thorough in servicing all
+ * chips during interrupt; add "volatile" a few places to
+ * circumvent compiler optimizations; fix base & offset
+ * computations in block_til_ready (was causing chip 0 to
+ * stop operation)
+ *
+ * Revision 1.12  1994/07/19  16:42:11  Bentson
+ * add some hackery for kernel version 1.1.8; expand
+ * error messages; refine timing for delay loops and
+ * declare loop params volatile
+ *
+ * Revision 1.11  1994/06/11  21:53:10  bentson
+ * get use of save_car right in transmit interrupt service
+ *
+ * Revision 1.10.1.1  1994/06/11  21:31:18  bentson
+ * add some diagnostic printing; try to fix save_car stuff
+ *
+ * Revision 1.10  1994/06/11  20:36:08  bentson
+ * clean up compiler warnings
+ *
+ * Revision 1.9  1994/06/11  19:42:46  bentson
+ * added a bunch of code to support modem signalling
+ *
+ * Revision 1.8  1994/06/11  17:57:07  bentson
+ * recognize break & parity error
+ *
+ * Revision 1.7  1994/06/05  05:51:34  bentson
+ * Reorder baud table to be monotonic; add cli to CP; discard
+ * incoming characters and status if the line isn't open; start to
+ * fold code into cy_throttle; start to port get_serial_info,
+ * set_serial_info, get_modem_info, set_modem_info, and send_break
+ * from serial.c; expand cy_ioctl; relocate and expand config_setup;
+ * get flow control characters from tty struct; invalidate ports w/o
+ * hardware;
+ *
+ * Revision 1.6  1994/05/31  18:42:21  bentson
+ * add a loop-breaker in the interrupt service routine;
+ * note when port is initialized so that it can be shut
+ * down under the right conditions; receive works without
+ * any obvious errors
+ *
+ * Revision 1.5  1994/05/30  00:55:02  bentson
+ * transmit works without obvious errors
+ *
+ * Revision 1.4  1994/05/27  18:46:27  bentson
+ * incorporated more code from lib_y.c; can now print short
+ * strings under interrupt control to port zero; seems to
+ * select ports/channels/lines correctly
+ *
+ * Revision 1.3  1994/05/25  22:12:44  bentson
+ * shifting from multi-port on a card to proper multiplexor
+ * data structures;  added skeletons of most routines
+ *
+ * Revision 1.2  1994/05/19  13:21:43  bentson
+ * start to crib from other sources
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/tty.h>
+#include <linux/serial.h>
+#include <linux/interrupt.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/cyclades.h>
+#include <linux/delay.h>
+#include <linux/major.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+
+#define small_delay(x) for(j=0;j<x;j++)k++;
+
+
+#define SERIAL_PARANOIA_CHECK
+#undef  SERIAL_DEBUG_OPEN
+#undef  SERIAL_DEBUG_THROTTLE
+#undef  SERIAL_DEBUG_OTHER
+#undef  SERIAL_DEBUG_IO
+#undef  SERIAL_DEBUG_COUNT
+#undef  SERIAL_DEBUG_DTR
+
+#ifndef MIN
+#define MIN(a,b)       ((a) < (b) ? (a) : (b))
+#endif
+
+#define WAKEUP_CHARS 256
+
+#define STD_COM_FLAGS (0)
+
+#define SERIAL_TYPE_NORMAL  1
+#define SERIAL_TYPE_CALLOUT 2
+
+
+DECLARE_TASK_QUEUE(tq_cyclades);
+
+struct tty_driver cy_serial_driver, cy_callout_driver;
+
+static volatile int cy_irq_triggered;
+static volatile int cy_triggered;
+static int cy_wild_int_mask;
+static unsigned char *intr_base_addr;
+
+/* This is the per-card data structure */
+struct cyclades_card cy_card[] = {
+ /* BASE_ADDR */
+    {0xD0000,0},
+    {0xD2000,0},
+    {0xD4000,10},
+    {0xD6000,11},
+    {0xD8000,12},
+    {0xDA000,15}
+};
+
+#define NR_CARDS        (sizeof(cy_card)/sizeof(struct cyclades_card))
+
+/*  No need has yet been found for this per-chip data structure
+ *  struct cyclades_chip cy_chip[] = {
+ *         {0}
+ *  };
+ *  #define NR_CHIPS  (sizeof(cy_chip)/sizeof(struct cyclades_chip))
+ */
+
+/* This is the per-port data structure */
+struct cyclades_port cy_port[] = {
+      /* CARD#  */
+        {-1 },      /* ttyC0 */
+        {-1 },      /* ttyC1 */
+        {-1 },      /* ttyC2 */
+        {-1 },      /* ttyC3 */
+        {-1 },      /* ttyC4 */
+        {-1 },      /* ttyC5 */
+        {-1 },      /* ttyC6 */
+        {-1 },      /* ttyC7 */
+        {-1 },      /* ttyC8 */
+        {-1 },      /* ttyC9 */
+        {-1 },      /* ttyC10 */
+        {-1 },      /* ttyC11 */
+        {-1 },      /* ttyC12 */
+        {-1 },      /* ttyC13 */
+        {-1 },      /* ttyC14 */
+        {-1 },      /* ttyC15 */
+        {-1 },      /* ttyC16 */
+        {-1 },      /* ttyC17 */
+        {-1 },      /* ttyC18 */
+        {-1 },      /* ttyC19 */
+        {-1 },      /* ttyC20 */
+        {-1 },      /* ttyC21 */
+        {-1 },      /* ttyC22 */
+        {-1 },      /* ttyC23 */
+        {-1 },      /* ttyC24 */
+        {-1 },      /* ttyC25 */
+        {-1 },      /* ttyC26 */
+        {-1 },      /* ttyC27 */
+        {-1 },      /* ttyC28 */
+        {-1 },      /* ttyC29 */
+        {-1 },      /* ttyC30 */
+        {-1 }       /* ttyC31 */
+};
+#define NR_PORTS        (sizeof(cy_port)/sizeof(struct cyclades_port))
+
+static int serial_refcount;
+
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+
+/* This is the per-irq data structure,
+               it maps an irq to the corresponding card */
+struct cyclades_card * IRQ_cards[16];
+
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the memcpy_fromfs blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf = 0;
+static struct semaphore tmp_buf_sem = MUTEX;
+
+/*
+ * This is used to look up the divsor speeds and the timeouts
+ * We're normally limited to 15 distinct baud rates.  The extra
+ * are accessed via settings in info->flags.
+ *         0,     1,     2,     3,     4,     5,     6,     7,     8,     9,
+ *        10,    11,    12,    13,    14,    15,    16,    17,    18,    19,
+ *                                                  HI            VHI
+ */
+static int baud_table[] = {
+           0,    50,    75,   110,   134,   150,   200,   300,   600,  1200,
+        1800,  2400,  4800,  9600, 19200, 38400, 57600, 76800,115200,150000,
+        0};
+
+static char baud_co[] = {  /* 25 MHz clock option table */
+        /* value =>    00    01   02    03    04 */
+        /* divide by    8    32   128   512  2048 */
+        0x00,  0x04,  0x04,  0x04,  0x04,  0x04,  0x03,  0x03,  0x03,  0x02,
+        0x02,  0x02,  0x01,  0x01,  0x00,  0x00,  0x00,  0x00,  0x00,  0x00};
+
+static char baud_bpr[] = {  /* 25 MHz baud rate period table */
+        0x00,  0xf5,  0xa3,  0x6f,  0x5c,  0x51,  0xf5,  0xa3,  0x51,  0xa3,
+        0x6d,  0x51,  0xa3,  0x51,  0xa3,  0x51,  0x36,  0x29,  0x1b,  0x15};
+
+static char baud_cor3[] = {  /* receive threashold */
+        0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,  0x0a,
+        0x0a,  0x0a,  0x0a,  0x08,  0x04,  0x02,  0x01,  0x01,  0x01,  0x01};
+
+
+
+static void shutdown(struct cyclades_port *);
+static int startup (struct cyclades_port *);
+static void cy_throttle(struct tty_struct *);
+static void cy_unthrottle(struct tty_struct *);
+static void config_setup(struct cyclades_port *);
+extern void console_print(const char *);
+static void show_status(int);
+
+
+
+static inline int
+serial_paranoia_check(struct cyclades_port *info,
+                       dev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+    static const char *badmagic =
+       "Warning: bad magic number for serial struct (%d, %d) in %s\n";
+    static const char *badinfo =
+       "Warning: null cyclades_port for (%d, %d) in %s\n";
+    static const char *badrange =
+       "Warning: cyclades_port out of range for (%d, %d) in %s\n";
+
+    if (!info) {
+       printk(badinfo, MAJOR(device), MINOR(device), routine);
+       return 1;
+    }
+
+    if( (long)info < (long)(&cy_port[0])
+    || (long)(&cy_port[NR_PORTS]) < (long)info ){
+       printk(badrange, MAJOR(device), MINOR(device), routine);
+       return 1;
+    }
+
+    if (info->magic != CYCLADES_MAGIC) {
+       printk(badmagic, MAJOR(device), MINOR(device), routine);
+       return 1;
+    }
+#endif
+       return 0;
+} /* serial_paranoia_check */
+
+/* The following diagnostic routines allow the driver to spew
+   information on the screen, even (especially!) during interrupts.
+ */
+void
+SP(char *data){
+  unsigned long flags;
+    save_flags(flags); cli();
+        console_print(data);
+    restore_flags(flags);
+}
+char scrn[2];
+void
+CP(char data){
+  unsigned long flags;
+    save_flags(flags); cli();
+        scrn[0] = data;
+        console_print(scrn);
+    restore_flags(flags);
+}/* CP */
+
+void CP1(int data) { (data<10)?  CP(data+'0'): CP(data+'A'-10); }/* CP1 */
+void CP2(int data) { CP1((data>>4) & 0x0f); CP1( data & 0x0f); }/* CP2 */
+void CP4(int data) { CP2((data>>8) & 0xff); CP2(data & 0xff); }/* CP4 */
+void CP8(long data) { CP4((data>>16) & 0xffff); CP4(data & 0xffff); }/* CP8 */
+
+
+/* This routine waits up to 100 micro-seconds for the previous
+   command to the Cirrus chip to complete and then issues the
+   new command.  An error is returned if the previous command
+   didn't finish within the time limit.
+ */
+u_short
+write_cy_cmd(u_char *base_addr, u_char cmd)
+{
+  unsigned long flags;
+  volatile int  i;
+
+    save_flags(flags); cli();
+       /* Check to see that the previous command has completed */
+       for(i = 0 ; i < 10000 ; i++){
+           if (base_addr[CyCCR] == 0){
+               break;
+           }
+           udelay(10L);
+       }
+       /* if the CCR never cleared, the previous command
+           didn't finish within the "reasonable time" */
+       if ( i == 10 ) {
+           restore_flags(flags);
+           return (-1);
+       }
+
+       /* Issue the new command */
+       base_addr[CyCCR] = cmd;
+    restore_flags(flags);
+    return(0);
+} /* write_cy_cmd */
+
+
+/* cy_start and cy_stop provide software output flow control as a
+   function of XON/XOFF, software CTS, and other such stuff. */
+
+static void
+cy_stop(struct tty_struct *tty)
+{
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned char *base_addr;
+  int chip,channel;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_stop ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_stop"))
+       return;
+       
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[info->card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)(channel & 0x0003); /* index channel */
+        base_addr[CySRER] &= ~CyTxMpty;
+    restore_flags(flags);
+
+    return;
+} /* cy_stop */
+
+static void
+cy_start(struct tty_struct *tty)
+{
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned char *base_addr;
+  int chip,channel;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_start ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_start"))
+       return;
+       
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[info->card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)(channel & 0x0003);
+        base_addr[CySRER] |= CyTxMpty;
+    restore_flags(flags);
+
+    return;
+} /* cy_start */
+
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver
+ * (also known as the "bottom half").  This can be called any
+ * number of times for any channel without harm.
+ */
+static inline void
+cy_sched_event(struct cyclades_port *info, int event)
+{
+    info->event |= 1 << event; /* remember what kind of event and who */
+    queue_task_irq_off(&info->tqueue, &tq_cyclades); /* it belongs to */
+    mark_bh(CYCLADES_BH);                       /* then trigger event */
+} /* cy_sched_event */
+
+
+
+/*
+ * This interrupt routine is used
+ * while we are probing for submarines.
+ */
+static void
+cy_probe(int irq)
+{
+    cy_irq_triggered = irq;
+    cy_triggered |= 1 << irq;
+    return;
+} /* cy_probe */
+
+/* The real interrupt service routine is called
+   whenever the card wants its hand held--chars
+   received, out buffer empty, modem change, etc.
+ */
+static void
+cy_interrupt(int irq)
+{
+  struct tty_struct *tty;
+  int status;
+  struct cyclades_card *cinfo;
+  struct cyclades_port *info;
+  volatile unsigned char *base_addr, *card_base_addr;
+  int chip;
+  int save_xir, channel, save_car;
+  char data;
+  volatile char vdata;
+  int char_count;
+  int outch;
+  int i,j;
+  int too_many;
+  int had_work;
+  int mdm_change;
+  int mdm_status;
+
+    if((cinfo = IRQ_cards[irq]) == 0){
+        return; /* spurious interrupt */
+    }
+
+    /* This loop checks all chips in the card.  Make a note whenever
+       _any_ chip had some work to do, as this is considered an
+       indication that there will be more to do.  Only when no chip
+       has any work does this outermost loop exit.
+     */
+    do{
+        had_work = 0;
+        for ( chip = 0 ; chip < cinfo->num_chips ; chip ++) {
+           base_addr = (unsigned char *)(cinfo->base_addr
+                                             + CyRegSize * chip);
+            too_many = 0;
+            while ( (status = base_addr[CySVRR]) != 0x00) {
+                had_work++;
+                /* The purpose of the following test is to ensure that
+                   no chip can monopolize the driver.  This forces the
+                   chips to be checked in a round-robin fashion (after
+                   draining each of a bunch (1000) of characters).
+                */
+                if(1000<too_many++){
+                    break;
+                }
+                if (status & CySRReceive) {      /* reception interrupt */
+
+                    /* determine the channel and change to that context */
+                    save_xir = (u_char) base_addr[CyRIR];
+                    channel = (u_short ) (save_xir & CyIRChannel);
+                    i = channel + chip * 4 + cinfo->first_line;
+                    info = &cy_port[i];
+                    info->last_active = jiffies;
+                    save_car = base_addr[CyCAR];
+                    base_addr[CyCAR] = save_xir;
+
+                    /* if there is nowhere to put the data, discard it */
+                    if(info->tty == 0){
+                        j = (base_addr[CyRIVR] & CyIVRMask);
+                        if ( j == CyIVRRxEx ) { /* exception */
+                            data = base_addr[CyRDSR];
+                        } else { /* normal character reception */
+                            char_count = base_addr[CyRDCR];
+                            while(char_count--){
+                                data = base_addr[CyRDSR];
+                            }
+                        }
+                    }else{ /* there is an open port for this data */
+                        tty = info->tty;
+                        j = (base_addr[CyRIVR] & CyIVRMask);
+                        if ( j == CyIVRRxEx ) { /* exception */
+                            data = base_addr[CyRDSR];
+                            if(data & info->ignore_status_mask){
+                                continue;
+                            }
+                            if (tty->flip.count < TTY_FLIPBUF_SIZE){
+                               tty->flip.count++;
+                               if (data & info->read_status_mask){
+                                   if(data & CyBREAK){
+                                       *tty->flip.flag_buf_ptr++ =
+                                                               TTY_BREAK;
+                                       *tty->flip.char_buf_ptr++ =
+                                                       base_addr[CyRDSR];
+                                       if (info->flags & ASYNC_SAK){
+                                           do_SAK(tty);
+                                       }
+                                   }else if(data & CyFRAME){
+                                       *tty->flip.flag_buf_ptr++ =
+                                                               TTY_FRAME;
+                                       *tty->flip.char_buf_ptr++ =
+                                                       base_addr[CyRDSR];
+                                   }else if(data & CyPARITY){
+                                       *tty->flip.flag_buf_ptr++ =
+                                                               TTY_PARITY;
+                                       *tty->flip.char_buf_ptr++ =
+                                                       base_addr[CyRDSR];
+                                   }else if(data & CyOVERRUN){
+                                       *tty->flip.flag_buf_ptr++ =
+                                                               TTY_OVERRUN;
+                                       *tty->flip.char_buf_ptr++ = 0;
+                                       /* If the flip buffer itself is
+                                          overflowing, we still loose
+                                          the next incoming character.
+                                        */
+                                       if(tty->flip.count < TTY_FLIPBUF_SIZE){
+                                           tty->flip.count++;
+                                           *tty->flip.flag_buf_ptr++ =
+                                                                TTY_NORMAL;
+                                           *tty->flip.char_buf_ptr++ =
+                                                       base_addr[CyRDSR];
+                                       }
+                                   /* These two conditions may imply */
+                                   /* a normal read should be done. */
+                                   /* }else if(data & CyTIMEOUT){ */
+                                   /* }else if(data & CySPECHAR){ */
+                                   }else{
+                                       *tty->flip.flag_buf_ptr++ = 0;
+                                       *tty->flip.char_buf_ptr++ = 0;
+                                   }
+                               }else{
+                                   *tty->flip.flag_buf_ptr++ = 0;
+                                   *tty->flip.char_buf_ptr++ = 0;
+                               }
+                            }else{
+                                /* there was a software buffer overrun
+                                   and nothing could be done about it!!! */
+                            }
+                        } else { /* normal character reception */
+                            /* load # characters available from the chip */
+                            char_count = base_addr[CyRDCR];
+
+                            while(char_count--){
+                               if (tty->flip.count >= TTY_FLIPBUF_SIZE){
+                                        break;
+                                }
+                               tty->flip.count++;
+                                data = base_addr[CyRDSR];
+                               *tty->flip.flag_buf_ptr++ = TTY_NORMAL;
+                               *tty->flip.char_buf_ptr++ = data;
+                            }
+                        }
+                        queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
+                    }
+                    /* end of service */
+                    base_addr[CyRIR] = (save_xir & 0x3f);
+                    base_addr[CyCAR] = (save_car);
+                }
+
+
+                if (status & CySRTransmit) {     /* transmission interrupt */
+                    /* Since we only get here when the transmit buffer is empty,
+                        we know we can always stuff a dozen characters. */
+
+                    /* determine the channel and change to that context */
+                    save_xir = (u_char) base_addr[CyTIR];
+                    channel = (u_short ) (save_xir & CyIRChannel);
+                    i = channel + chip * 4 + cinfo->first_line;
+                    save_car = base_addr[CyCAR];
+                    base_addr[CyCAR] = save_xir;
+
+                    /* validate the port number (as configured and open) */
+                    if( (i < 0) || (NR_PORTS <= i) ){
+                        base_addr[CySRER] &= ~CyTxMpty;
+                        goto txend;
+                    }
+                    info = &cy_port[i];
+                    info->last_active = jiffies;
+                    if(info->tty == 0){
+                        base_addr[CySRER] &= ~CyTxMpty;
+                        goto txdone;
+                    }
+
+                    /* load the on-chip space available for outbound data */
+                    char_count = info->xmit_fifo_size;
+
+
+                    if(info->x_char) { /* send special char */
+                        outch = info->x_char;
+                        base_addr[CyTDR] = outch;
+                        char_count--;
+                        info->x_char = 0;
+                    }
+
+                   if (info->x_break){
+                       /*  The Cirrus chip requires the "Embedded Transmit
+                           Commands" of start break, delay, and end break
+                           sequences to be sent.  The duration of the
+                           break is given in TICs, which runs at HZ
+                           (typically 100) and the PPR runs at 200 Hz,
+                           so the delay is duration * 200/HZ, and thus a
+                           break can run from 1/100 sec to about 5/4 sec.
+                        */
+                       base_addr[CyTDR] = 0; /* start break */
+                       base_addr[CyTDR] = 0x81;
+                       base_addr[CyTDR] = 0; /* delay a bit */
+                       base_addr[CyTDR] = 0x82;
+                       base_addr[CyTDR] = info->x_break*200/HZ;
+                       base_addr[CyTDR] = 0; /* terminate break */
+                       base_addr[CyTDR] = 0x83;
+                       char_count -= 7;
+                       info->x_break = 0;
+                   }
+
+                    while (char_count-- > 0){
+                        if (!info->xmit_cnt){
+                           base_addr[CySRER] &= ~CyTxMpty;
+                           goto txdone;
+                        }
+                       if (info->tty->stopped || info->tty->hw_stopped){
+                           base_addr[CySRER] &= ~CyTxMpty;
+                           goto txdone;
+                       }
+                        /* Because the Embedded Transmit Commands have been
+                           enabled, we must check to see if the escape
+                           character, NULL, is being sent.  If it is, we
+                           must ensure that there is room for it to be
+                           doubled in the output stream.  Therefore we
+                           no longer advance the pointer when the character
+                           is fetched, but rather wait until after the check
+                           for a NULL output character. (This is necessary
+                           because there may not be room for the two chars
+                           needed to send a NULL.
+                        */
+                        outch = info->xmit_buf[info->xmit_tail];
+                        if( outch ){
+                           info->xmit_cnt--;
+                           info->xmit_tail = (info->xmit_tail + 1)
+                                                     & (PAGE_SIZE - 1);
+                           base_addr[CyTDR] = outch;
+                        }else{
+                            if(char_count > 1){
+                               info->xmit_cnt--;
+                               info->xmit_tail = (info->xmit_tail + 1)
+                                                         & (PAGE_SIZE - 1);
+                               base_addr[CyTDR] = outch;
+                               base_addr[CyTDR] = 0;
+                               char_count--;
+                            }else{
+                            }
+                        }
+                    }
+
+        txdone:
+                    if (info->xmit_cnt < WAKEUP_CHARS) {
+                        cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+                    }
+
+        txend:
+                    /* end of service */
+                    base_addr[CyTIR] = (save_xir & 0x3f);
+                    base_addr[CyCAR] = (save_car);
+                }
+
+                if (status & CySRModem) {        /* modem interrupt */
+
+                    /* determine the channel and change to that context */
+                    save_xir = (u_char) base_addr[CyMIR];
+                    channel = (u_short ) (save_xir & CyIRChannel);
+                    info = &cy_port[channel + chip * 4 + cinfo->first_line];
+                    info->last_active = jiffies;
+                    save_car = base_addr[CyCAR];
+                    base_addr[CyCAR] = save_xir;
+
+                    mdm_change = base_addr[CyMISR];
+                    mdm_status = base_addr[CyMSVR1];
+
+                    if(info->tty == 0){ /* nowhere to put the data, ignore it */
+                        ;
+                    }else{
+                        if((mdm_change & CyDCD)
+                        && (info->flags & ASYNC_CHECK_CD)){
+                            if(mdm_status & CyDCD){
+/* CP('!'); */
+                                cy_sched_event(info, Cy_EVENT_OPEN_WAKEUP);
+                            }else if(!((info->flags & ASYNC_CALLOUT_ACTIVE)
+                                     &&(info->flags & ASYNC_CALLOUT_NOHUP))){
+/* CP('@'); */
+                                cy_sched_event(info, Cy_EVENT_HANGUP);
+                            }
+                        }
+                        if((mdm_change & CyCTS)
+                        && (info->flags & ASYNC_CTS_FLOW)){
+                            if(info->tty->stopped){
+                                if(mdm_status & CyCTS){
+                                    /* !!! cy_start isn't used because... */
+                                    info->tty->stopped = 0;
+                                   base_addr[CySRER] |= CyTxMpty;
+                                   cy_sched_event(info, Cy_EVENT_WRITE_WAKEUP);
+                                }
+                            }else{
+                                if(!(mdm_status & CyCTS)){
+                                    /* !!! cy_stop isn't used because... */
+                                    info->tty->stopped = 1;
+                                   base_addr[CySRER] &= ~CyTxMpty;
+                                }
+                            }
+                        }
+                        if(mdm_status & CyDSR){
+                        }
+                        if(mdm_status & CyRI){
+                        }
+                    }
+                    /* end of service */
+                    base_addr[CyMIR] = (save_xir & 0x3f);
+                    base_addr[CyCAR] = save_car;
+                }
+            }          /* end while status != 0 */
+        }            /* end loop for chips... */
+    } while(had_work);
+
+   /* clear interrupts */
+    card_base_addr = (unsigned char *)cinfo->base_addr;
+   vdata = *(card_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
+
+} /* cy_interrupt */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * cy_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using cy_sched_event(), and they get done here.
+ *
+ * This is done through one level of indirection--the task queue.
+ * When a hardware interrupt service routine wants service by the
+ * driver's bottom half, it enqueues the appropriate tq_struct (one
+ * per port) to the tq_cyclades work queue and sets a request flag
+ * via mark_bh for processing that queue.  When the time is right,
+ * do_cyclades_bh is called (because of the mark_bh) and it requests
+ * that the work queue be processed.
+ *
+ * Although this may seem unwieldy, it gives the system a way to
+ * pass an argument (in this case the pointer to the cyclades_port
+ * structure) to the bottom half of the driver.  Previous kernels
+ * had to poll every port to see if that port needed servicing.
+ */
+static void
+do_cyclades_bh(void *unused)
+{
+    run_task_queue(&tq_cyclades);
+} /* do_cyclades_bh */
+
+static void
+do_softint(void *private_)
+{
+  struct cyclades_port *info = (struct cyclades_port *) private_;
+  struct tty_struct    *tty;
+
+    tty = info->tty;
+    if (!tty)
+       return;
+
+    if (clear_bit(Cy_EVENT_HANGUP, &info->event)) {
+       tty_hangup(info->tty);
+       wake_up_interruptible(&info->open_wait);
+       info->flags &= ~(ASYNC_NORMAL_ACTIVE|
+                            ASYNC_CALLOUT_ACTIVE);
+    }
+    if (clear_bit(Cy_EVENT_OPEN_WAKEUP, &info->event)) {
+       wake_up_interruptible(&info->open_wait);
+    }
+    if (clear_bit(Cy_EVENT_WRITE_WAKEUP, &info->event)) {
+       if((tty->flags & (1<< TTY_DO_WRITE_WAKEUP))
+       && tty->ldisc.write_wakeup){
+           (tty->ldisc.write_wakeup)(tty);
+       }
+       wake_up_interruptible(&tty->write_wait);
+    }
+} /* do_softint */
+
+
+/*
+ * Grab all interrupts in preparation for doing an automatic irq
+ * detection.  dontgrab is a mask of irq's _not_ to grab.  Returns a
+ * mask of irq's which were grabbed and should therefore be freed
+ * using free_all_interrupts().
+ */
+static int
+grab_all_interrupts(int dontgrab)
+{
+  int irq_lines = 0;
+  int i, mask;
+    
+    for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+       if (!(mask & dontgrab)
+       && !request_irq(i, cy_probe, SA_INTERRUPT, "serial probe")) {
+           irq_lines |= mask;
+       }
+    }
+    return irq_lines;
+} /* grab_all_interrupts */
+
+/*
+ * Release all interrupts grabbed by grab_all_interrupts
+ */
+static void
+free_all_interrupts(int irq_lines)
+{
+  int i;
+    
+    for (i = 0; i < 16; i++) {
+       if (irq_lines & (1 << i))
+           free_irq(i);
+    }
+} /* free_all_interrupts */
+
+/*
+ * This routine returns a bitfield of "wild interrupts".  Basically,
+ * any unclaimed interrupts which is flapping around.
+ */
+static int
+check_wild_interrupts(void)
+{
+  int  i, mask;
+  int  wild_interrupts = 0;
+  int  irq_lines;
+  unsigned long timeout;
+  unsigned long flags;
+       
+    /*Turn on interrupts (they may be off) */
+    save_flags(flags); sti();
+
+       irq_lines = grab_all_interrupts(0);
+       
+       /*
+        * Delay for 0.1 seconds -- we use a busy loop since this may 
+        * occur during the bootup sequence
+        */
+       timeout = jiffies+10;
+       while (timeout >= jiffies)
+           ;
+       
+       cy_triggered = 0;       /* Reset after letting things settle */
+
+       timeout = jiffies+10;
+       while (timeout >= jiffies)
+               ;
+       
+       for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
+           if ((cy_triggered & (1 << i)) &&
+               (irq_lines & (1 << i))) {
+                   wild_interrupts |= mask;
+           }
+       }
+       free_all_interrupts(irq_lines);
+    restore_flags(flags);
+    return wild_interrupts;
+} /* check_wild_interrupts */
+
+/*
+ * This routine is called by do_auto_irq(); it attempts to determine
+ * which interrupt a serial port is configured to use.  It is not
+ * fool-proof, but it works a large part of the time.
+ */
+static int
+get_auto_irq(int card)
+{
+  unsigned long timeout;
+  unsigned char *base_addr;
+  int save_xir, save_car;
+  volatile char vdata;
+
+    base_addr = (unsigned char*) (cy_card[card].base_addr);
+    intr_base_addr = base_addr;
+       
+    /*
+     * Enable interrupts and see who answers
+     */
+    cy_irq_triggered = 0;
+    cli();
+       base_addr[CyCAR] = 0;
+       write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_XMTR);
+       base_addr[CySRER] |= CyTxMpty;
+    sti();
+    
+    timeout = jiffies+2;
+    while (timeout >= jiffies) {
+       if (cy_irq_triggered)
+           break;
+    }
+    /*
+     * Now check to see if we got any business, and clean up.
+     */
+    cli();
+       if(intr_base_addr[CySVRR] != 0){
+           save_xir = (u_char) intr_base_addr[CyTIR];
+           save_car = intr_base_addr[CyCAR];
+           if ((save_xir & 0x3) != 0){
+               printk("channel %x requesting unexpected interrupt\n",save_xir);
+           }
+           intr_base_addr[CyCAR] = (save_xir & 0x3);
+           intr_base_addr[CySRER] &= ~CyTxMpty;
+           intr_base_addr[CyTIR] = (save_xir & 0x3f);
+           intr_base_addr[CyCAR] = (save_car);
+           vdata = *(intr_base_addr + Cy_ClrIntr); /* Cy_ClrIntr is 0x1800 */
+       }
+    sti();
+
+    return(cy_irq_triggered);
+} /* get_auto_irq */
+
+/*
+ * Calls get_auto_irq() multiple times, to make sure we don't get
+ * faked out by random interrupts
+ */
+static int
+do_auto_irq(int card)
+{
+  int                  irq_lines = 0;
+  int                  irq_try_1 = 0, irq_try_2 = 0;
+  int                  retries;
+  unsigned long flags;
+
+    /* Turn on interrupts (they may be off) */
+    save_flags(flags); sti();
+
+        cy_wild_int_mask = check_wild_interrupts();
+
+       irq_lines = grab_all_interrupts(cy_wild_int_mask);
+       
+       for (retries = 0; retries < 5; retries++) {
+           if (!irq_try_1)
+               irq_try_1 = get_auto_irq(card);
+           if (!irq_try_2)
+               irq_try_2 = get_auto_irq(card);
+           if (irq_try_1 && irq_try_2) {
+               if (irq_try_1 == irq_try_2)
+                   break;
+               irq_try_1 = irq_try_2 = 0;
+           }
+       }
+    restore_flags(flags);
+    free_all_interrupts(irq_lines);
+    return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
+} /* do_auto_irq */
+
+
+/* This is called whenever a port becomes active;
+   interrupts are enabled and DTR & RTS are turned on.
+ */
+static int
+startup(struct cyclades_port * info)
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+    if (info->flags & ASYNC_INITIALIZED){
+       return 0;
+    }
+
+    if (!info->type){
+       if (info->tty){
+           set_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+       return 0;
+    }
+    if (!info->xmit_buf){
+       info->xmit_buf = (unsigned char *) get_free_page (GFP_KERNEL);
+       if (!info->xmit_buf){
+           return -ENOMEM;
+       }
+    }
+
+    config_setup(info);
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                  (cy_card[card].base_addr + chip * CyRegSize);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("startup card %d, chip %d, channel %d, base_addr %lx",
+        card, chip, channel, (long)base_addr);/**/
+#endif
+
+    save_flags(flags); cli();
+       base_addr[CyCAR] = (u_char)channel;
+
+       base_addr[CyRTPR] = 0x20; /* 32ms rx timeout */
+
+       write_cy_cmd(base_addr,CyCHAN_CTL|CyENB_RCVR|CyENB_XMTR);
+
+       base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+       base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('1'); */
+       base_addr[CyMSVR2] = CyDTR;
+
+#ifdef SERIAL_DEBUG_DTR
+        printk("cyc: %d: raising DTR\n", __LINE__);
+        printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+
+       base_addr[CySRER] |= CyRxData;
+       info->flags |= ASYNC_INITIALIZED;
+
+       if (info->tty){
+           clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+
+    restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk(" done\n");
+#endif
+    return 0;
+} /* startup */
+
+void
+start_xmit( struct cyclades_port *info )
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                  (cy_card[card].base_addr + chip * CyRegSize);
+    save_flags(flags); cli();
+       base_addr[CyCAR] = channel;
+       base_addr[CySRER] |= CyTxMpty;
+    restore_flags(flags);
+} /* start_xmit */
+
+/*
+ * This routine shuts down a serial port; interrupts are disabled,
+ * and DTR is dropped if the hangup on close termio flag is on.
+ */
+static void
+shutdown(struct cyclades_port * info)
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+    if (!(info->flags & ASYNC_INITIALIZED)){
+/* CP('$'); */
+       return;
+    }
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                  (cy_card[card].base_addr + chip * CyRegSize);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("shutdown card %d, chip %d, channel %d, base_addr %lx\n",
+           card, chip, channel, (long)base_addr);
+#endif
+
+    /* !!! REALLY MUST WAIT FOR LAST CHARACTER TO BE
+       SENT BEFORE DROPPING THE LINE !!!  (Perhaps
+       set some flag that is read when XMTY happens.)
+       Other choices are to delay some fixed interval
+       or schedule some later processing.
+     */
+    save_flags(flags); cli();
+       if (info->xmit_buf){
+           free_page((unsigned long) info->xmit_buf);
+           info->xmit_buf = 0;
+       }
+
+       base_addr[CyCAR] = (u_char)channel;
+       if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
+           base_addr[CyMSVR1] = ~CyRTS;
+/* CP('C');CP('1'); */
+           base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+        }
+       write_cy_cmd(base_addr,CyCHAN_CTL|CyDIS_RCVR);
+         /* it may be appropriate to clear _XMIT at
+           some later date (after testing)!!! */
+
+       if (info->tty){
+           set_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+       info->flags &= ~ASYNC_INITIALIZED;
+    restore_flags(flags);
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk(" done\n");
+#endif
+    return;
+} /* shutdown */
+
+/*
+ * This routine finds or computes the various line characteristics.
+ */
+static void
+config_setup(struct cyclades_port * info)
+{
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+  unsigned cflag;
+  int   i;
+
+    if (!info->tty || !info->tty->termios){
+        return;
+    }
+    if (info->line == -1){
+        return;
+    }
+    cflag = info->tty->termios->c_cflag;
+
+    /* baud rate */
+    i = cflag & CBAUD;
+#ifdef CBAUDEX
+/* Starting with kernel 1.1.65, there is direct support for
+   higher baud rates.  The following code supports those
+   changes.  The conditional aspect allows this driver to be
+   used for earlier as well as later kernel versions.  (The
+   mapping is slightly different from serial.c because there
+   is still the possibility of supporting 75 kbit/sec with
+   the Cyclades board.)
+ */
+    if (i & CBAUDEX) {
+       if (i == B57600)
+           i = 16;
+       else if(i == B115200) 
+           i = 18;
+#ifdef B78600
+       else if(i == B78600) 
+           i = 17;
+#endif
+       else
+           info->tty->termios->c_cflag &= ~CBAUDEX;
+    }
+#endif
+    if (i == 15) {
+           if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+                   i += 1;
+           if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+                   i += 3;
+    }
+    info->tbpr = baud_bpr[i]; /* Tx BPR */
+    info->tco = baud_co[i]; /* Tx CO */
+    info->rbpr = baud_bpr[i]; /* Rx BPR */
+    info->rco = baud_co[i]; /* Rx CO */
+    if (baud_table[i] == 134) {
+        info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
+        /* get it right for 134.5 baud */
+    } else if (baud_table[i]) {
+        info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
+        /* this needs to be propagated into the card info */
+    } else {
+        info->timeout = 0;
+    }
+    /* By tradition (is it a standard?) a baud rate of zero
+       implies the line should be/has been closed.  A bit
+       later in this routine such a test is performed. */
+
+    /* byte size and parity */
+    info->cor5 = 0;
+    info->cor4 = 0;
+    info->cor3 = baud_cor3[i]; /* receive threshold */
+    info->cor2 = CyETC;
+    switch(cflag & CSIZE){
+    case CS5:
+        info->cor1 = Cy_5_BITS;
+        break;
+    case CS6:
+        info->cor1 = Cy_6_BITS;
+        break;
+    case CS7:
+        info->cor1 = Cy_7_BITS;
+        break;
+    case CS8:
+        info->cor1 = Cy_8_BITS;
+        break;
+    }
+    if(cflag & CSTOPB){
+        info->cor1 |= Cy_2_STOP;
+    }
+    if (cflag & PARENB){
+        if (cflag & PARODD){
+            info->cor1 |= CyPARITY_O;
+        }else{
+            info->cor1 |= CyPARITY_E;
+        }
+    }else{
+        info->cor1 |= CyPARITY_NONE;
+    }
+       
+    /* CTS flow control flag */
+    if (cflag & CRTSCTS)
+       info->flags |= ASYNC_CTS_FLOW;
+    else
+       info->flags &= ~ASYNC_CTS_FLOW;
+    if (cflag & CLOCAL)
+       info->flags &= ~ASYNC_CHECK_CD;
+    else
+       info->flags |= ASYNC_CHECK_CD;
+
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                  (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+       base_addr[CyCAR] = (u_char)channel;
+
+       /* tx and rx baud rate */
+
+       base_addr[CyTCOR] = info->tco;
+       base_addr[CyTBPR] = info->tbpr;
+       base_addr[CyRCOR] = info->rco;
+       base_addr[CyRBPR] = info->rbpr;
+
+       /* set line characteristics  according configuration */
+
+       base_addr[CySCHR1] = START_CHAR(info->tty);
+       base_addr[CySCHR2] = STOP_CHAR(info->tty);
+       base_addr[CyCOR1] = info->cor1;
+       base_addr[CyCOR2] = info->cor2;
+       base_addr[CyCOR3] = info->cor3;
+       base_addr[CyCOR4] = info->cor4;
+       base_addr[CyCOR5] = info->cor5;
+
+       write_cy_cmd(base_addr,CyCOR_CHANGE|CyCOR1ch|CyCOR2ch|CyCOR3ch);
+
+       base_addr[CyCAR] = (u_char)channel; /* !!! Is this needed? */
+
+       base_addr[CyRTPR] = 0x20; /* 32ms rx timeout */
+
+       if (C_CLOCAL(info->tty)) {
+           base_addr[CySRER] |= 0; /* without modem intr */
+                                   /* ignore 1->0 modem transitions */
+           base_addr[CyMCOR1] = 0x0;
+                                   /* ignore 0->1 modem transitions */
+           base_addr[CyMCOR2] = 0x0;
+       } else {
+           base_addr[CySRER] |= CyMdmCh; /* with modem intr */
+                                   /* act on 1->0 modem transitions */
+           base_addr[CyMCOR1] = CyDSR|CyCTS|CyRI|CyDCD;
+                                   /* act on 0->1 modem transitions */
+           base_addr[CyMCOR2] = CyDSR|CyCTS|CyRI|CyDCD;
+       }
+
+       if(i == 0){ /* baud rate is zero, turn off line */
+           base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+       }else{
+           base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+       }
+
+       /* The hardware option, CyRtsAO, presents RTS when
+          the chip has characters to send.  Since most modems
+          use RTS as reverse (inbound) flow control, this
+          option is not used.  If inbound flow control is
+          necessary, DTR can be programmed to provide the
+          appropriate signals.  Contact Marcio Saito for
+          details. */
+       if (C_CRTSCTS(info->tty)) {
+           info->cor2 |= CyCtsAE;
+       }else{
+           info->cor2 &= ~CyCtsAE;
+       }
+
+       if (info->tty){
+           clear_bit(TTY_IO_ERROR, &info->tty->flags);
+       }
+
+    restore_flags(flags);
+
+} /* config_setup */
+
+
+static void
+cy_put_char(struct tty_struct *tty, unsigned char ch)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_put_char ttyC%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_put_char"))
+       return;
+
+    if (!tty || !info->xmit_buf)
+       return;
+
+    save_flags(flags); cli();
+       if (info->xmit_cnt >= PAGE_SIZE - 1) {
+           restore_flags(flags);
+           return;
+       }
+
+       info->xmit_buf[info->xmit_head++] = ch;
+       info->xmit_head &= PAGE_SIZE - 1;
+       info->xmit_cnt++;
+    restore_flags(flags);
+} /* cy_put_char */
+
+
+static void
+cy_flush_chars(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+                               
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_flush_chars ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_flush_chars"))
+       return;
+
+    if (info->xmit_cnt <= 0 || tty->stopped
+    || tty->hw_stopped || !info->xmit_buf)
+       return;
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                  (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+       base_addr[CyCAR] = channel;
+       base_addr[CySRER] |= CyTxMpty;
+    restore_flags(flags);
+} /* cy_flush_chars */
+
+
+/* This routine gets called when tty_write has put something into
+    the write_queue.  If the port is not already transmitting stuff,
+    start it off by enabling interrupts.  The interrupt service
+    routine will then ensure that the characters are sent.  If the
+    port is already active, there is no need to kick it.
+ */
+static int
+cy_write(struct tty_struct * tty, int from_user,
+           unsigned char *buf, int count)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  int c, total = 0;
+
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_write ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_write")){
+       return 0;
+    }
+       
+    if (!tty || !info->xmit_buf || !tmp_buf){
+        return 0;
+    }
+
+    while (1) {
+        save_flags(flags); cli();              
+       c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                          SERIAL_XMIT_SIZE - info->xmit_head));
+       if (c <= 0){
+           restore_flags(flags);
+           break;
+       }
+
+       if (from_user) {
+           down(&tmp_buf_sem);
+           memcpy_fromfs(tmp_buf, buf, c);
+           c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+                      SERIAL_XMIT_SIZE - info->xmit_head));
+           memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
+           up(&tmp_buf_sem);
+       } else
+           memcpy(info->xmit_buf + info->xmit_head, buf, c);
+       info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
+       info->xmit_cnt += c;
+       restore_flags(flags);
+       buf += c;
+       count -= c;
+       total += c;
+    }
+
+
+    if (info->xmit_cnt
+    && !tty->stopped
+    && !tty->hw_stopped ) {
+        start_xmit(info);
+    }
+    return total;
+} /* cy_write */
+
+
+static int
+cy_write_room(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  int  ret;
+                               
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_write_room ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_write_room"))
+       return 0;
+    ret = PAGE_SIZE - info->xmit_cnt - 1;
+    if (ret < 0)
+       ret = 0;
+    return ret;
+} /* cy_write_room */
+
+
+static int
+cy_chars_in_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+                               
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_chars_in_buffer ttyC%d %d\n", info->line, info->xmit_cnt); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_chars_in_buffer"))
+       return 0;
+
+    return info->xmit_cnt;
+} /* cy_chars_in_buffer */
+
+
+static void
+cy_flush_buffer(struct tty_struct *tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+                               
+#ifdef SERIAL_DEBUG_IO
+    printk("cy_flush_buffer ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_flush_buffer"))
+       return;
+    save_flags(flags); cli();
+       info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
+    restore_flags(flags);
+    wake_up_interruptible(&tty->write_wait);
+    if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP))
+    && tty->ldisc.write_wakeup)
+       (tty->ldisc.write_wakeup)(tty);
+} /* cy_flush_buffer */
+
+
+/* This routine is called by the upper-layer tty layer to signal
+   that incoming characters should be throttled or that the
+   throttle should be released.
+ */
+static void
+cy_throttle(struct tty_struct * tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+  char buf[64];
+       
+    printk("throttle %s: %d....\n", _tty_name(tty, buf),
+          tty->ldisc.chars_in_buffer(tty));
+    printk("cy_throttle ttyC%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+           return;
+    }
+
+    if (I_IXOFF(tty)) {
+       info->x_char = STOP_CHAR(tty);
+           /* Should use the "Send Special Character" feature!!! */
+    }
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                  (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+       base_addr[CyCAR] = (u_char)channel;
+       base_addr[CyMSVR1] = ~CyRTS;
+    restore_flags(flags);
+
+    return;
+} /* cy_throttle */
+
+
+static void
+cy_unthrottle(struct tty_struct * tty)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+  unsigned long flags;
+  unsigned char *base_addr;
+  int card,chip,channel;
+
+#ifdef SERIAL_DEBUG_THROTTLE
+  char buf[64];
+       
+    printk("throttle %s: %d....\n", _tty_name(tty, buf),
+          tty->ldisc.chars_in_buffer(tty));
+    printk("cy_unthrottle ttyC%d\n", info->line);
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_nthrottle")){
+           return;
+    }
+
+    if (I_IXOFF(tty)) {
+       info->x_char = START_CHAR(tty);
+       /* Should use the "Send Special Character" feature!!! */
+    }
+
+    card = info->card;
+    channel = info->line - cy_card[card].first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                  (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+       base_addr[CyCAR] = (u_char)channel;
+       base_addr[CyMSVR1] = CyRTS;
+    restore_flags(flags);
+
+    return;
+} /* cy_unthrottle */
+
+static int
+get_serial_info(struct cyclades_port * info,
+                           struct serial_struct * retinfo)
+{
+  struct serial_struct tmp;
+  struct cyclades_card *cinfo = &cy_card[info->card];
+
+/* CP('g'); */
+    if (!retinfo)
+            return -EFAULT;
+    memset(&tmp, 0, sizeof(tmp));
+    tmp.type = info->type;
+    tmp.line = info->line;
+    tmp.port = info->card * 0x100 + info->line - cinfo->first_line;
+    tmp.irq = cinfo->irq;
+    tmp.flags = info->flags;
+    tmp.baud_base = 0;          /*!!!*/
+    tmp.close_delay = info->close_delay;
+    tmp.custom_divisor = 0;     /*!!!*/
+    tmp.hub6 = 0;               /*!!!*/
+    memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
+    return 0;
+} /* get_serial_info */
+
+static int
+set_serial_info(struct cyclades_port * info,
+                           struct serial_struct * new_info)
+{
+  struct serial_struct new_serial;
+  struct cyclades_port old_info;
+
+/* CP('s'); */
+    if (!new_info)
+           return -EFAULT;
+    memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
+    old_info = *info;
+
+    if (!suser()) {
+           if ((new_serial.close_delay != info->close_delay) ||
+               ((new_serial.flags & ASYNC_FLAGS & ~ASYNC_USR_MASK) !=
+                (info->flags & ASYNC_FLAGS & ~ASYNC_USR_MASK)))
+                   return -EPERM;
+           info->flags = ((info->flags & ~ASYNC_USR_MASK) |
+                          (new_serial.flags & ASYNC_USR_MASK));
+           goto check_and_exit;
+    }
+
+
+    /*
+     * OK, past this point, all the error checking has been done.
+     * At this point, we start making changes.....
+     */
+
+    info->flags = ((info->flags & ~ASYNC_FLAGS) |
+                   (new_serial.flags & ASYNC_FLAGS));
+    info->close_delay = new_serial.close_delay;
+
+
+check_and_exit:
+    if (info->flags & ASYNC_INITIALIZED){
+       config_setup(info);
+       return 0;
+    }else{
+        return startup(info);
+    }
+} /* set_serial_info */
+
+static int
+get_modem_info(struct cyclades_port * info, unsigned int *value)
+{
+  int card,chip,channel;
+  unsigned char *base_addr;
+  unsigned long flags;
+  unsigned char status;
+  unsigned int result;
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[card].base_addr + chip * CyRegSize);
+
+    save_flags(flags); cli();
+        base_addr[CyCAR] = (u_char)channel;
+        status = base_addr[CyMSVR1] | base_addr[CyMSVR2];
+    restore_flags(flags);
+
+    result =  ((status  & CyRTS) ? TIOCM_RTS : 0)
+            | ((status  & CyDTR) ? TIOCM_DTR : 0)
+            | ((status  & CyDCD) ? TIOCM_CAR : 0)
+            | ((status  & CyRI) ? TIOCM_RNG : 0)
+            | ((status  & CyDSR) ? TIOCM_DSR : 0)
+            | ((status  & CyCTS) ? TIOCM_CTS : 0);
+    put_fs_long(result,(unsigned long *) value);
+    return 0;
+} /* get_modem_info */
+
+static int
+set_modem_info(struct cyclades_port * info, unsigned int cmd,
+                          unsigned int *value)
+{
+  int card,chip,channel;
+  unsigned char *base_addr;
+  unsigned long flags;
+  unsigned int arg = get_fs_long((unsigned long *) value);
+
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (unsigned char*)
+                   (cy_card[card].base_addr + chip * CyRegSize);
+
+    switch (cmd) {
+    case TIOCMBIS:
+       if (arg & TIOCM_RTS){
+           save_flags(flags); cli();
+               base_addr[CyCAR] = (u_char)channel;
+               base_addr[CyMSVR1] = CyRTS;
+           restore_flags(flags);
+       }
+       if (arg & TIOCM_DTR){
+           save_flags(flags); cli();
+           base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('2'); */
+           base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+           restore_flags(flags);
+       }
+       break;
+    case TIOCMBIC:
+       if (arg & TIOCM_RTS){
+           save_flags(flags); cli();
+               base_addr[CyCAR] = (u_char)channel;
+               base_addr[CyMSVR1] = ~CyRTS;
+           restore_flags(flags);
+       }
+       if (arg & TIOCM_DTR){
+           save_flags(flags); cli();
+           base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('2'); */
+           base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+           restore_flags(flags);
+       }
+       break;
+    case TIOCMSET:
+       if (arg & TIOCM_RTS){
+           save_flags(flags); cli();
+               base_addr[CyCAR] = (u_char)channel;
+               base_addr[CyMSVR1] = CyRTS;
+           restore_flags(flags);
+       }else{
+           save_flags(flags); cli();
+               base_addr[CyCAR] = (u_char)channel;
+               base_addr[CyMSVR1] = ~CyRTS;
+           restore_flags(flags);
+       }
+       if (arg & TIOCM_DTR){
+           save_flags(flags); cli();
+           base_addr[CyCAR] = (u_char)channel;
+/* CP('S');CP('3'); */
+           base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: raising DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+           restore_flags(flags);
+       }else{
+           save_flags(flags); cli();
+           base_addr[CyCAR] = (u_char)channel;
+/* CP('C');CP('3'); */
+           base_addr[CyMSVR2] = ~CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+            printk("cyc: %d: dropping DTR\n", __LINE__);
+            printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+           restore_flags(flags);
+       }
+       break;
+    default:
+               return -EINVAL;
+        }
+    return 0;
+} /* set_modem_info */
+
+static void
+send_break( struct cyclades_port * info, int duration)
+{ /* Let the transmit ISR take care of this (since it
+     requires stuffing characters into the output stream).
+   */
+    info->x_break = duration;
+    if (!info->xmit_cnt ) {
+       start_xmit(info);
+    }
+} /* send_break */
+
+
+static int
+cy_ioctl(struct tty_struct *tty, struct file * file,
+            unsigned int cmd, unsigned long arg)
+{
+  int error;
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+  int ret_val = 0;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_ioctl ttyC%d, cmd = %x arg = %lx\n", info->line, cmd, arg); /* */
+#endif
+
+    switch (cmd) {
+        case TCSBRK:    /* SVID version: non-zero arg --> no break */
+           ret_val = tty_check_change(tty);
+           if (ret_val)
+               return ret_val;
+            wait_until_sent(tty,0);
+            if (!arg)
+                send_break(info, HZ/4); /* 1/4 second */
+            break;
+        case TCSBRKP:   /* support for POSIX tcsendbreak() */
+           ret_val = tty_check_change(tty);
+           if (ret_val)
+               return ret_val;
+            wait_until_sent(tty,0);
+            send_break(info, arg ? arg*(HZ/10) : HZ/4);
+            break;
+        case TIOCMBIS:
+        case TIOCMBIC:
+        case TIOCMSET:
+            ret_val = set_modem_info(info, cmd, (unsigned int *) arg);
+
+/* The following commands are imcompletely implemented!!! */
+        case TIOCGSOFTCAR:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned int *));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            put_fs_long(C_CLOCAL(tty) ? 1 : 0,
+                        (unsigned long *) arg);
+            break;
+        case TIOCSSOFTCAR:
+            arg = get_fs_long((unsigned long *) arg);
+            tty->termios->c_cflag =
+                    ((tty->termios->c_cflag & ~CLOCAL) |
+                     (arg ? CLOCAL : 0));
+            break;
+        case TIOCMGET:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(unsigned int *));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_modem_info(info, (unsigned int *) arg);
+            break;
+        case TIOCGSERIAL:
+            error = verify_area(VERIFY_WRITE, (void *) arg
+                                ,sizeof(struct serial_struct));
+            if (error){
+                ret_val = error;
+                break;
+            }
+            ret_val = get_serial_info(info,
+                                   (struct serial_struct *) arg);
+            break;
+        case TIOCSSERIAL:
+            ret_val = set_serial_info(info,
+                                   (struct serial_struct *) arg);
+            break;
+        default:
+           ret_val = -ENOIOCTLCMD;
+    }
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_ioctl done\n");
+#endif
+
+    return ret_val;
+} /* cy_ioctl */
+
+
+
+
+static void
+cy_set_termios(struct tty_struct *tty, struct termios * old_termios)
+{
+  struct cyclades_port *info = (struct cyclades_port *)tty->driver_data;
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_set_termios ttyC%d\n", info->line);
+#endif
+
+    if (tty->termios->c_cflag == old_termios->c_cflag)
+        return;
+    config_setup(info);
+
+    if ((old_termios->c_cflag & CRTSCTS) &&
+        !(tty->termios->c_cflag & CRTSCTS)) {
+            tty->stopped = 0;
+            cy_start(tty);
+    }
+#ifdef tytso_patch_94Nov25_1726
+    if (!(old_termios->c_cflag & CLOCAL) &&
+        (tty->termios->c_cflag & CLOCAL))
+            wake_up_interruptible(&info->open_wait);
+#endif
+
+    return;
+} /* cy_set_termios */
+
+
+static void
+cy_close(struct tty_struct * tty, struct file * filp)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+
+/* CP('C'); */
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_close ttyC%d\n", info->line);
+#endif
+
+    if (!info
+    || serial_paranoia_check(info, tty->device, "cy_close")){
+        return;
+    }
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_close ttyC%d, count = %d\n", info->line, info->count);
+#endif
+
+    if ((tty->count == 1) && (info->count != 1)) {
+       /*
+        * Uh, oh.  tty->count is 1, which means that the tty
+        * structure will be freed.  Info->count should always
+        * be one in these conditions.  If it's greater than
+        * one, we've got real problems, since it means the
+        * serial port won't be shutdown.
+        */
+       printk("cy_close: bad serial port count; tty->count is 1, "
+          "info->count is %d\n", info->count);
+       info->count = 1;
+    }
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: decrmenting count to %d\n", __LINE__, info->count - 1);
+#endif
+    if (--info->count < 0) {
+       printk("cy_close: bad serial port count for ttys%d: %d\n",
+              info->line, info->count);
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+       info->count = 0;
+    }
+    if (info->count)
+       return;
+    info->flags |= ASYNC_CLOSING;
+    /*
+     * Save the termios structure, since this port may have
+     * separate termios for callout and dialin.
+     */
+    if (info->flags & ASYNC_NORMAL_ACTIVE)
+       info->normal_termios = *tty->termios;
+    if (info->flags & ASYNC_CALLOUT_ACTIVE)
+       info->callout_termios = *tty->termios;
+    if (info->flags & ASYNC_INITIALIZED)
+       wait_until_sent(tty, 3000); /* 30 seconds timeout */
+    shutdown(info);
+    if (tty->driver.flush_buffer)
+       tty->driver.flush_buffer(tty);
+    if (tty->ldisc.flush_buffer)
+       tty->ldisc.flush_buffer(tty);
+    info->event = 0;
+    info->tty = 0;
+    if (tty->ldisc.num != ldiscs[N_TTY].num) {
+       if (tty->ldisc.close)
+           (tty->ldisc.close)(tty);
+       tty->ldisc = ldiscs[N_TTY];
+       tty->termios->c_line = N_TTY;
+       if (tty->ldisc.open)
+           (tty->ldisc.open)(tty);
+    }
+    if (info->blocked_open) {
+       if (info->close_delay) {
+           current->state = TASK_INTERRUPTIBLE;
+           current->timeout = jiffies + info->close_delay;
+           schedule();
+       }
+       wake_up_interruptible(&info->open_wait);
+    }
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
+                    ASYNC_CLOSING);
+    wake_up_interruptible(&info->close_wait);
+
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_close done\n");
+#endif
+
+    return;
+} /* cy_close */
+
+/*
+ * cy_hangup() --- called by tty_hangup() when a hangup is signaled.
+ */
+void
+cy_hangup(struct tty_struct *tty)
+{
+  struct cyclades_port * info = (struct cyclades_port *)tty->driver_data;
+       
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_hangup ttyC%d\n", info->line); /* */
+#endif
+
+    if (serial_paranoia_check(info, tty->device, "cy_hangup"))
+       return;
+    
+    shutdown(info);
+#if 0
+    info->event = 0;
+    info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+    info->tty = 0;
+#endif
+    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
+    wake_up_interruptible(&info->open_wait);
+} /* cy_hangup */
+
+
+
+/*
+ * ------------------------------------------------------------
+ * cy_open() and friends
+ * ------------------------------------------------------------
+ */
+
+static int
+block_til_ready(struct tty_struct *tty, struct file * filp,
+                           struct cyclades_port *info)
+{
+  struct wait_queue wait = { current, NULL };
+  struct cyclades_card *cinfo;
+  unsigned long flags;
+  int chip, channel;
+  int retval;
+  char *base_addr;
+
+    /*
+     * If the device is in the middle of being closed, then block
+     * until it's done, and then try again.
+     */
+    if (info->flags & ASYNC_CLOSING) {
+       interruptible_sleep_on(&info->close_wait);
+       if (info->flags & ASYNC_HUP_NOTIFY){
+           return -EAGAIN;
+       }else{
+           return -ERESTARTSYS;
+       }
+    }
+
+    /*
+     * If this is a callout device, then just make sure the normal
+     * device isn't being used.
+     */
+    if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
+       if (info->flags & ASYNC_NORMAL_ACTIVE){
+           return -EBUSY;
+       }
+       if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+           (info->flags & ASYNC_SESSION_LOCKOUT) &&
+           (info->session != current->session)){
+           return -EBUSY;
+       }
+       if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
+           (info->flags & ASYNC_PGRP_LOCKOUT) &&
+           (info->pgrp != current->pgrp)){
+           return -EBUSY;
+       }
+       info->flags |= ASYNC_CALLOUT_ACTIVE;
+       return 0;
+    }
+
+    /*
+     * If non-blocking mode is set, then make the check up front
+     * and then exit.
+     */
+    if (filp->f_flags & O_NONBLOCK) {
+       if (info->flags & ASYNC_CALLOUT_ACTIVE){
+           return -EBUSY;
+       }
+       info->flags |= ASYNC_NORMAL_ACTIVE;
+       return 0;
+    }
+
+    /*
+     * Block waiting for the carrier detect and the line to become
+     * free (i.e., not in use by the callout).  While we are in
+     * this loop, info->count is dropped by one, so that
+     * cy_close() knows when to free things.  We restore it upon
+     * exit, either normal or abnormal.
+     */
+    retval = 0;
+    add_wait_queue(&info->open_wait, &wait);
+#ifdef SERIAL_DEBUG_OPEN
+    printk("block_til_ready before block: ttyC%d, count = %d\n",
+          info->line, info->count);/**/
+#endif
+    info->count--;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: decrmenting count to %d\n", __LINE__, info->count);
+#endif
+    info->blocked_open++;
+
+    cinfo = &cy_card[info->card];
+    channel = info->line - cinfo->first_line;
+    chip = channel>>2;
+    channel &= 0x03;
+    base_addr = (char *) (cinfo->base_addr + chip * CyRegSize);
+
+    while (1) {
+       save_flags(flags); cli();
+           if (!(info->flags & ASYNC_CALLOUT_ACTIVE)){
+               base_addr[CyCAR] = (u_char)channel;
+               base_addr[CyMSVR1] = CyRTS;
+/* CP('S');CP('4'); */
+               base_addr[CyMSVR2] = CyDTR;
+#ifdef SERIAL_DEBUG_DTR
+                printk("cyc: %d: raising DTR\n", __LINE__);
+                printk("     status: 0x%x, 0x%x\n", base_addr[CyMSVR1], base_addr[CyMSVR2]);
+#endif
+           }
+       restore_flags(flags);
+       current->state = TASK_INTERRUPTIBLE;
+       if (tty_hung_up_p(filp)
+       || !(info->flags & ASYNC_INITIALIZED) ){
+           if (info->flags & ASYNC_HUP_NOTIFY) {
+               retval = -EAGAIN;
+           }else{
+               retval = -ERESTARTSYS;
+           }
+           break;
+       }
+       save_flags(flags); cli();
+           base_addr[CyCAR] = (u_char)channel;
+/* CP('L');CP1(1 && C_CLOCAL(tty)); CP1(1 && (base_addr[CyMSVR1] & CyDCD) ); */
+           if (!(info->flags & ASYNC_CALLOUT_ACTIVE)
+           && !(info->flags & ASYNC_CLOSING)
+           && (C_CLOCAL(tty)
+               || (base_addr[CyMSVR1] & CyDCD))) {
+                   restore_flags(flags);
+                   break;
+           }
+       restore_flags(flags);
+       if (current->signal & ~current->blocked) {
+           retval = -ERESTARTSYS;
+           break;
+       }
+#ifdef SERIAL_DEBUG_OPEN
+       printk("block_til_ready blocking: ttyC%d, count = %d\n",
+              info->line, info->count);/**/
+#endif
+       schedule();
+    }
+    current->state = TASK_RUNNING;
+    remove_wait_queue(&info->open_wait, &wait);
+    if (!tty_hung_up_p(filp)){
+       info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: incrmenting count to %d\n", __LINE__, info->count);
+#endif
+    }
+    info->blocked_open--;
+#ifdef SERIAL_DEBUG_OPEN
+    printk("block_til_ready after blocking: ttyC%d, count = %d\n",
+          info->line, info->count);/**/
+#endif
+    if (retval)
+           return retval;
+    info->flags |= ASYNC_NORMAL_ACTIVE;
+    return 0;
+} /* block_til_ready */
+
+/*
+ * This routine is called whenever a serial port is opened.  It
+ * performs the serial-specific initalization for the tty structure.
+ */
+int
+cy_open(struct tty_struct *tty, struct file * filp)
+{
+  struct cyclades_port  *info;
+  int retval, line;
+
+/* CP('O'); */
+    line = MINOR(tty->device) - tty->driver.minor_start;
+    if ((line < 0) || (NR_PORTS <= line)){
+        return -ENODEV;
+    }
+    info = &cy_port[line];
+#ifdef SERIAL_DEBUG_OTHER
+    printk("cy_open ttyC%d\n", info->line); /* */
+#endif
+    if (serial_paranoia_check(info, tty->device, "cy_open")){
+        return -ENODEV;
+    }
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_open ttyC%d, count = %d\n", info->line, info->count);/**/
+#endif
+    info->count++;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: incrmenting count to %d\n", __LINE__, info->count);
+#endif
+    tty->driver_data = info;
+    info->tty = tty;
+
+    if (!tmp_buf) {
+       tmp_buf = (unsigned char *) get_free_page(GFP_KERNEL);
+       if (!tmp_buf){
+           return -ENOMEM;
+        }
+    }
+
+    if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
+       if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
+           *tty->termios = info->normal_termios;
+       else 
+           *tty->termios = info->callout_termios;
+    }
+    /*
+     * Start up serial port
+     */
+    retval = startup(info);
+    if (retval){
+       return retval;
+    }
+
+    retval = block_til_ready(tty, filp, info);
+    if (retval) {
+#ifdef SERIAL_DEBUG_OPEN
+       printk("cy_open returning after block_til_ready with %d\n",
+              retval);
+#endif
+       return retval;
+    }
+
+    info->session = current->session;
+    info->pgrp = current->pgrp;
+
+#ifdef SERIAL_DEBUG_OPEN
+    printk("cy_open done\n");/**/
+#endif
+    return 0;
+} /* cy_open */
+
+
+
+/*
+ * ---------------------------------------------------------------------
+ * cy_init() and friends
+ *
+ * cy_init() is called at boot-time to initialize the serial driver.
+ * ---------------------------------------------------------------------
+ */
+
+/*
+ * This routine prints out the appropriate serial driver version
+ * number, and identifies which options were configured into this
+ * driver.
+ */
+static void
+show_version(void)
+{
+    printk("Cyclom driver %s\n",rcsid);
+} /* show_version */
+
+/* initialize chips on card -- return number of valid
+   chips (which is number of ports/4) */
+int
+cy_init_card(unsigned char *base_addr)
+{
+  volatile unsigned short discard;
+  unsigned int chip_number;
+
+    discard = base_addr[Cy_HwReset]; /* Cy_HwReset is 0x1400 */
+    discard = base_addr[Cy_ClrIntr]; /* Cy_ClrIntr is 0x1800 */
+    udelay(500L);
+
+    for(chip_number=0; chip_number<CyMaxChipsPerCard; chip_number++){
+        udelay(1000L);
+        if(base_addr[CyCCR] != 0x00){
+            /*************
+            printk(" chip #%d at %#6lx is never idle (CCR != 0)\n",
+               chip_number, (unsigned long)base_addr);
+            *************/
+            return chip_number;
+        }
+
+        base_addr[CyGFRCR] = 0;
+        udelay(10L);
+
+        base_addr[CyCCR] = CyCHIP_RESET;
+        udelay(1000L);
+
+        if(base_addr[CyGFRCR] == 0x00){
+            printk(" chip #%d at %#6lx is not responding (GFRCR stayed 0)\n",
+               chip_number, (unsigned long)base_addr);
+            return chip_number;
+        }
+        if((0xf0 & base_addr[CyGFRCR]) != 0x40){
+            printk(" chip #%d at %#6lx is not valid (GFRCR == %#2x)\n",
+               chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+            return chip_number;
+        }
+        base_addr[CyGCR] = CyCH0_SERIAL;
+        base_addr[CyPPR] = CyCLOCK_25_1MS * 5; /* run clock at 200 Hz */
+
+        printk(" chip #%d at %#6lx is rev 0x%2x\n",
+               chip_number, (unsigned long)base_addr, base_addr[CyGFRCR]);
+
+        /* advance to next chip on card */
+        base_addr += CyRegSize;
+    }
+
+    return chip_number;
+} /* cy_init_card */
+
+/* The serial driver boot-time initialization code!
+    Hardware I/O ports are mapped to character special devices on a
+    first found, first allocated manner.  That is, this code searches
+    for Cyclom cards in the system.  As each is found, it is probed
+    to discover how many chips (and thus how many ports) are present.
+    These ports are mapped to the tty ports 32 and upward in monotonic
+    fashion.  If an 8-port card is replaced with a 16-port card, the
+    port mapping on a following card will shift.
+
+    This approach is different from what is used in the other serial
+    device driver because the Cyclom is more properly a multiplexer,
+    not just an aggregation of serial ports on one card.
+
+    If there are more cards with more ports than have been statically
+    allocated above, a warning is printed and the extra ports are ignored.
+ */
+long
+cy_init(long kmem_start)
+{
+  struct cyclades_port *info;
+  struct cyclades_card *cinfo;
+  int good_ports = 0;
+  int port_num = 0;
+  int index;
+#ifdef notyet
+  struct sigaction sa;
+#endif
+  int retval;
+
+scrn[1] = '\0';
+    show_version();
+
+    /* Initialize the tty_driver structure */
+    
+    memset(&cy_serial_driver, 0, sizeof(struct tty_driver));
+    cy_serial_driver.magic = TTY_DRIVER_MAGIC;
+    cy_serial_driver.name = "ttyC";
+    cy_serial_driver.major = 19 /* TTY_MAJOR */;
+    cy_serial_driver.minor_start = 32;
+    cy_serial_driver.num = NR_PORTS;
+    cy_serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
+    cy_serial_driver.subtype = SERIAL_TYPE_NORMAL;
+    cy_serial_driver.init_termios = tty_std_termios;
+    cy_serial_driver.init_termios.c_cflag =
+           B9600 | CS8 | CREAD | HUPCL | CLOCAL;
+    cy_serial_driver.flags = TTY_DRIVER_REAL_RAW;
+    cy_serial_driver.refcount = &serial_refcount;
+    cy_serial_driver.table = serial_table;
+    cy_serial_driver.termios = serial_termios;
+    cy_serial_driver.termios_locked = serial_termios_locked;
+    cy_serial_driver.open = cy_open;
+    cy_serial_driver.close = cy_close;
+    cy_serial_driver.write = cy_write;
+    cy_serial_driver.put_char = cy_put_char;
+    cy_serial_driver.flush_chars = cy_flush_chars;
+    cy_serial_driver.write_room = cy_write_room;
+    cy_serial_driver.chars_in_buffer = cy_chars_in_buffer;
+    cy_serial_driver.flush_buffer = cy_flush_buffer;
+    cy_serial_driver.ioctl = cy_ioctl;
+    cy_serial_driver.throttle = cy_throttle;
+    cy_serial_driver.unthrottle = cy_unthrottle;
+    cy_serial_driver.set_termios = cy_set_termios;
+    cy_serial_driver.stop = cy_stop;
+    cy_serial_driver.start = cy_start;
+    cy_serial_driver.hangup = cy_hangup;
+
+    /*
+     * The callout device is just like normal device except for
+     * major number and the subtype code.
+     */
+    cy_callout_driver = cy_serial_driver;
+    cy_callout_driver.name = "cub";
+    cy_callout_driver.major = 20 /* TTYAUX_MAJOR */;
+    cy_callout_driver.subtype = SERIAL_TYPE_CALLOUT;
+
+    if (tty_register_driver(&cy_serial_driver))
+           panic("Couldn't register Cyclom serial driver\n");
+    if (tty_register_driver(&cy_callout_driver))
+           panic("Couldn't register Cyclom callout driver\n");
+
+    bh_base[CYCLADES_BH].routine = do_cyclades_bh;
+
+    for (index = 0; index < 16; index++) {
+           IRQ_cards[index] = 0;
+    }
+
+    port_num = 0;
+    info = cy_port;
+    for (index = 0, cinfo = cy_card; index < NR_CARDS; index++,cinfo++) {
+       /*** initialize card ***/
+       if(0 == (cinfo->num_chips =
+                   cy_init_card((unsigned char *)cinfo->base_addr))){
+           /* this card is not present */
+           continue;
+       }
+
+#ifndef CY_DONT_PROBE
+       /* find out the board's irq by probing */
+       cinfo->irq = do_auto_irq(index);
+#endif
+
+       /** bind IRQ to card **/
+       if (cinfo->irq) {
+           retval = request_irq(cinfo->irq, cy_interrupt,
+                               SA_INTERRUPT, "cyclades");
+           if (retval){
+               printk("request_irq returned %d\n",retval);
+               /* return retval; */
+           }
+           IRQ_cards[cinfo->irq] = cinfo;
+        }else{
+           printk("couldn't get board's irq\n");
+           continue;
+       }
+
+       printk("  share IRQ %d: ", cinfo->irq);
+       good_ports = 4 * cinfo->num_chips;
+
+       if(port_num < NR_PORTS){
+           cinfo->first_line = port_num;
+           while( good_ports-- && port_num < NR_PORTS){
+               /*** initialize port ***/
+               info->magic = CYCLADES_MAGIC;
+               info->type = PORT_CIRRUS;
+               info->card = index;
+               info->line = port_num;
+               info->flags = STD_COM_FLAGS;
+               info->tty = 0;
+               info->xmit_fifo_size = 12;
+               info->cor1 = CyPARITY_NONE|Cy_1_STOP|Cy_8_BITS;
+               info->cor2 = CyETC;
+               info->cor3 = 0x08; /* _very_ small receive threshold */
+               info->cor4 = 0;
+               info->cor5 = 0;
+               info->tbpr = baud_bpr[13]; /* Tx BPR */
+               info->tco = baud_co[13]; /* Tx CO */
+               info->rbpr = baud_bpr[13]; /* Rx BPR */
+               info->rco = baud_co[13]; /* Rx CO */
+               info->close_delay = 0;
+               info->x_char = 0;
+               info->event = 0;
+               info->count = 0;
+#ifdef SERIAL_DEBUG_COUNT
+    printk("cyc: %d: setting count to 0\n", __LINE__);
+#endif
+               info->blocked_open = 0;
+               info->tqueue.routine = do_softint;
+               info->tqueue.data = info;
+               info->callout_termios =cy_callout_driver.init_termios;
+               info->normal_termios = cy_serial_driver.init_termios;
+               info->open_wait = 0;
+               info->close_wait = 0;
+               /* info->session */
+               /* info->pgrp */
+               /* info->read_status_mask */
+               /* info->timeout */
+
+               printk("ttyC%1d ", info->line);
+               port_num++;info++;
+               if(!(port_num & 7)){
+                   printk("\n               ");
+               }
+           }
+       }else{
+           cinfo->first_line = -1;
+       }
+       printk("\n");
+    }
+    while( port_num < NR_PORTS){
+       info->line = -1;
+       port_num++;info++;
+    }
+    return kmem_start;
+    
+} /* cy_init */
+
+static void
+show_status(int line_num)
+{
+  unsigned char *base_addr;
+  int card,chip,channel;
+  struct cyclades_port * info;
+  unsigned long flags;
+
+    info = &cy_port[line_num];
+    card = info->card;
+    channel = (info->line) - (cy_card[card].first_line);
+    chip = channel>>2;
+    channel &= 0x03;
+    printk("  card %d, chip %d, channel %d\n", card, chip, channel);/**/
+
+    printk(" cy_card\n");
+    printk("  irq base_addr num_chips first_line = %d %lx %d %d\n",
+           cy_card[card].irq, (long)cy_card[card].base_addr,
+           cy_card[card].num_chips, cy_card[card].first_line);
+
+    printk(" cy_port\n");
+    printk("  card line flags = %d %d %x\n",
+                 info->card, info->line, info->flags);
+    printk("  *tty read_status_mask timeout xmit_fifo_size = %lx %x %x %x\n",
+                 (long)info->tty, info->read_status_mask,
+                 info->timeout, info->xmit_fifo_size);
+    printk("  cor1,cor2,cor3,cor4,cor5 = %x %x %x %x %x\n",
+             info->cor1, info->cor2, info->cor3, info->cor4, info->cor5);
+    printk("  tbpr,tco,rbpr,rco = %d %d %d %d\n",
+             info->tbpr, info->tco, info->rbpr, info->rco);
+    printk("  close_delay event count = %d %d %d\n",
+             info->close_delay, info->event, info->count);
+    printk("  x_char blocked_open = %x %x\n",
+             info->x_char, info->blocked_open);
+    printk("  session pgrp open_wait = %lx %lx %lx\n",
+             info->session, info->pgrp, (long)info->open_wait);
+
+
+    save_flags(flags); cli();
+
+       base_addr = (unsigned char*)
+                      (cy_card[card].base_addr + chip * CyRegSize);
+
+/* Global Registers */
+
+       printk(" CyGFRCR %x\n", base_addr[CyGFRCR]);
+       printk(" CyCAR %x\n", base_addr[CyCAR]);
+       printk(" CyGCR %x\n", base_addr[CyGCR]);
+       printk(" CySVRR %x\n", base_addr[CySVRR]);
+       printk(" CyRICR %x\n", base_addr[CyRICR]);
+       printk(" CyTICR %x\n", base_addr[CyTICR]);
+       printk(" CyMICR %x\n", base_addr[CyMICR]);
+       printk(" CyRIR %x\n", base_addr[CyRIR]);
+       printk(" CyTIR %x\n", base_addr[CyTIR]);
+       printk(" CyMIR %x\n", base_addr[CyMIR]);
+       printk(" CyPPR %x\n", base_addr[CyPPR]);
+
+       base_addr[CyCAR] = (u_char)channel;
+
+/* Virtual Registers */
+
+       printk(" CyRIVR %x\n", base_addr[CyRIVR]);
+       printk(" CyTIVR %x\n", base_addr[CyTIVR]);
+       printk(" CyMIVR %x\n", base_addr[CyMIVR]);
+       printk(" CyMISR %x\n", base_addr[CyMISR]);
+
+/* Channel Registers */
+
+       printk(" CyCCR %x\n", base_addr[CyCCR]);
+       printk(" CySRER %x\n", base_addr[CySRER]);
+       printk(" CyCOR1 %x\n", base_addr[CyCOR1]);
+       printk(" CyCOR2 %x\n", base_addr[CyCOR2]);
+       printk(" CyCOR3 %x\n", base_addr[CyCOR3]);
+       printk(" CyCOR4 %x\n", base_addr[CyCOR4]);
+       printk(" CyCOR5 %x\n", base_addr[CyCOR5]);
+       printk(" CyCCSR %x\n", base_addr[CyCCSR]);
+       printk(" CyRDCR %x\n", base_addr[CyRDCR]);
+       printk(" CySCHR1 %x\n", base_addr[CySCHR1]);
+       printk(" CySCHR2 %x\n", base_addr[CySCHR2]);
+       printk(" CySCHR3 %x\n", base_addr[CySCHR3]);
+       printk(" CySCHR4 %x\n", base_addr[CySCHR4]);
+       printk(" CySCRL %x\n", base_addr[CySCRL]);
+       printk(" CySCRH %x\n", base_addr[CySCRH]);
+       printk(" CyLNC %x\n", base_addr[CyLNC]);
+       printk(" CyMCOR1 %x\n", base_addr[CyMCOR1]);
+       printk(" CyMCOR2 %x\n", base_addr[CyMCOR2]);
+       printk(" CyRTPR %x\n", base_addr[CyRTPR]);
+       printk(" CyMSVR1 %x\n", base_addr[CyMSVR1]);
+       printk(" CyMSVR2 %x\n", base_addr[CyMSVR2]);
+       printk(" CyRBPR %x\n", base_addr[CyRBPR]);
+       printk(" CyRCOR %x\n", base_addr[CyRCOR]);
+       printk(" CyTBPR %x\n", base_addr[CyTBPR]);
+       printk(" CyTCOR %x\n", base_addr[CyTCOR]);
+
+    restore_flags(flags);
+} /* show_status */
+
+
index fac55feb87dca18da030bbe36037e8f553cf7e14..4b90ff618186ffc0c188c9f5051ef6f7a5817681 100644 (file)
@@ -82,8 +82,8 @@ unsigned char kbd_read_mask = 0x01;   /* modified by psaux.c */
 
 /*
  * global state includes the following, and various static variables
- * in this module: prev_scancode, shift_state, diacr, npadch,
- *   dead_key_next, last_console
+ * in this module: prev_scancode, shift_state, diacr, npadch, dead_key_next.
+ * (last_console is now a global variable)
  */
 
 /* shift state counters.. */
@@ -91,8 +91,8 @@ static unsigned char k_down[NR_SHIFT] = {0, };
 /* keyboard key bitmap */
 static unsigned long key_down[8] = { 0, };
 
+extern int last_console;
 static int want_console = -1;
-static int last_console = 0;           /* last used VC */
 static int dead_key_next = 0;
 /* 
  * In order to retrieve the shift_state (for the mouse server), either
@@ -190,7 +190,7 @@ void to_utf8(ushort c) {
        put_queue(0x80 | ((c >> 6) & 0x3f));
        put_queue(0x80 | (c & 0x3f));
     }
-    /* utf-8 is defined for words of up to 36 bits,
+    /* UTF-8 is defined for words of up to 31 bits,
        but we need only 16 bits here */
 }
 
@@ -1131,7 +1131,6 @@ static void kbd_bh(void * unused)
        }
        if (want_console >= 0) {
                if (want_console != fg_console) {
-                       last_console = fg_console;
                        change_console(want_console);
                        /* we only changed when the console had already
                           been allocated - a new console is not created
index 22f03c036ee0d2573a75938dcbcd7e2e77ded416..359669306fc015f19336320761dc120d849491a8 100644 (file)
@@ -1533,8 +1533,13 @@ static int get_modem_info(struct async_struct * info, unsigned int *value)
 static int set_modem_info(struct async_struct * info, unsigned int cmd,
                          unsigned int *value)
 {
-       unsigned int arg = get_fs_long((unsigned long *) value);
+       int error;
+       unsigned int arg;
 
+       error = verify_area(VERIFY_READ, value, sizeof(int));
+       if (error)
+               return error;
+       arg = get_fs_long((unsigned long *) value);
        switch (cmd) {
        case TIOCMBIS: 
                if (arg & TIOCM_RTS) {
index c88dc08522adf65cddca2e1d816632595d50c521..dfd9655f870d12c41cde35601de66fd73d0a70ff 100644 (file)
@@ -82,10 +82,12 @@ struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table     */
 
 /*
  * fg_console is the current virtual console,
+ * last_console is the last used one
  * redirect is the pseudo-tty that console output
  * is redirected to if asked by TIOCCONS.
  */
 int fg_console = 0;
+int last_console = 0;
 struct tty_struct * redirect = NULL;
 struct wait_queue * keypress_wait = NULL;
 
@@ -364,6 +366,7 @@ void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
        tty->flags = 0;
        tty->session = 0;
        tty->pgrp = -1;
+       tty->ctrl_status = 0;
        for_each_task(p) {
                if (p->tty == tty)
                        p->tty = NULL;
@@ -412,11 +415,11 @@ void disassociate_ctty(int priv)
 
        if (!tty)
                return;
-
        if (tty->pgrp > 0) {
                kill_pg(tty->pgrp, SIGHUP, priv);
                kill_pg(tty->pgrp, SIGCONT, priv);
        }
+
        tty->session = 0;
        tty->pgrp = -1;
 
@@ -470,6 +473,7 @@ void complete_change_console(unsigned int new_console)
                 return;
         if (!vc_cons_allocated(new_console))
                 return;
+       last_console = fg_console;
 
        /*
         * If we're switching, we could be going from KD_GRAPHICS to
@@ -1685,6 +1689,9 @@ long tty_init(long kmem_start)
 
        kmem_start = kbd_init(kmem_start);
        kmem_start = rs_init(kmem_start);
+#ifdef CONFIG_CYCLADES
+       kmem_start = cy_init(kmem_start);
+#endif
        kmem_start = pty_init(kmem_start);
        return kmem_start;
 }
index 820d7480e0764917646337b3ed74b7fe445c6c80..ddd4bf56ac4cea996ac5251dd520978bb091c90c 100644 (file)
@@ -190,6 +190,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                 * doesn't do a whole lot. i'm not sure if it should do any
                 * restoration of modes or what...
                 */
+               if (!suser())
+                       return -EPERM;
                switch (arg) {
                case KD_GRAPHICS:
                        break;
@@ -230,6 +232,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                return -EINVAL;
 
        case KDSKBMODE:
+               if (!suser())
+                       return -EPERM;
                switch(arg) {
                  case K_RAW:
                        kbd->kbdmode = VC_RAW;
@@ -309,6 +313,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                struct kbkeycode * const a = (struct kbkeycode *)arg;
                unsigned int sc, kc;
 
+               if (!suser())
+                       return -EPERM;
                i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbkeycode));
                if (i)
                        return i;
@@ -348,6 +354,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                u_char s;
                u_short v;
 
+               if (!suser())
+                       return -EPERM;
                i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbentry));
                if (i)
                        return i;
@@ -437,6 +445,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                u_char *p;
                char *q;
 
+               if (!suser())
+                       return -EPERM;
                i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
                if (i)
                        return i;
@@ -520,6 +530,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                struct kbdiacrs *a = (struct kbdiacrs *)arg;
                unsigned int ct;
 
+               if (!suser())
+                       return -EPERM;
                i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
                if (i)
                        return i;
@@ -542,6 +554,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                return 0;
 
        case KDSKBLED:
+               if (!suser())
+                       return -EPERM;
                if (arg & ~0x77)
                        return -EINVAL;
                kbd->ledflagstate = (arg & 7);
@@ -559,6 +573,7 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                return 0;
 
        case KDSETLED:
+               /* OK, I'll let ordinary users run blinkenlights - zblaxell */
                setledstate(kbd, arg);
                return 0;
 
@@ -567,6 +582,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                struct vt_mode *vtmode = (struct vt_mode *)arg;
                char mode;
 
+               if (!suser())
+                       return -EPERM;
                i = verify_area(VERIFY_WRITE, (void *)vtmode, sizeof(struct vt_mode));
                if (i)
                        return i;
@@ -642,6 +659,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
         * to preserve sanity).
         */
        case VT_ACTIVATE:
+               if (!suser())
+                       return -EPERM;
                if (arg == 0 || arg > MAX_NR_CONSOLES)
                        return -ENXIO;
                arg--;
@@ -655,6 +674,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
         * wait until the specified VT has been activated
         */
        case VT_WAITACTIVE:
+               if (!suser())
+                       return -EPERM;
                if (arg == 0 || arg > MAX_NR_CONSOLES)
                        return -ENXIO;
                arg--;
@@ -676,6 +697,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
         *      2:      completed switch-to OK
         */
        case VT_RELDISP:
+               if (!suser())
+                       return -EPERM;
                if (vt_cons[console]->vt_mode.mode != VT_PROCESS)
                        return -EINVAL;
 
@@ -724,6 +747,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
          * Disallocate memory associated to VT (but leave VT1)
          */
         case VT_DISALLOCATE:
+               if (!suser())
+                       return -EPERM;
                if (arg > MAX_NR_CONSOLES)
                        return -ENXIO;
                if (arg == 0) {
@@ -751,6 +776,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
        {
                struct vt_sizes *vtsizes = (struct vt_sizes *) arg;
                ushort ll,cc;
+               if (!suser())
+                       return -EPERM;
                i = verify_area(VERIFY_READ, (void *)vtsizes, sizeof(struct vt_sizes));
                if (i)
                        return i;
@@ -760,6 +787,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
        }
 
        case PIO_FONT:
+               if (!suser())
+                       return -EPERM;
                return con_set_font((char *)arg);
                /* con_set_font() defined in console.c */
 
@@ -768,6 +797,8 @@ int vt_ioctl(struct tty_struct *tty, struct file * file,
                /* con_get_font() defined in console.c */
 
        case PIO_SCRNMAP:
+               if (!suser())
+                       return -EPERM;
                return con_set_trans((char *)arg);
                /* con_set_trans() defined in console.c */
 
index c70629d73f68bcd9f3594453a719ae10994dee46..9d2d6b06ba25f2aae957213986dd801f8474fbbd 100644 (file)
@@ -75,6 +75,7 @@ dummy_init(struct device *dev)
 
        /* Fill in the fields of the device structure with ethernet-generic values. */
        ether_setup(dev);
+       dev->flags |= IFF_NOARP;
 
        return 0;
 }
index 4ff157cc92b506961d336f84e41b6bbc6cd8714b..a902b651ddfc4d36655f6955c380e0d9ee0dcd08 100644 (file)
@@ -686,6 +686,8 @@ eexp_get_stats(struct device *dev)
 static void
 set_multicast_list(struct device *dev, int num_addrs, void *addrs)
 {
+/* This doesn't work yet */
+#if 0
        short ioaddr = dev->base_addr;
        if (num_addrs < 0) {
                /* Not written yet, this requires expanding the init_words config
@@ -702,6 +704,7 @@ set_multicast_list(struct device *dev, int num_addrs, void *addrs)
                   cmd. */
                outw(99, ioaddr);               /* Disable promiscuous mode, use normal mode */
        }
+#endif
 }
 
 /* The horrible routine to read a word from the serial EEPROM. */
index 5a239a64c358d2772c45235afebaf0e04e7c7a0c..05b94f901bbff02cb0da3fb28ef03281defb9692 100644 (file)
@@ -1,3 +1,4 @@
+/* $Id: plip.c,v 1.7 1994/12/16 06:20:02 gniibe Exp $ */
 /* plip.c: A parallel port "network" driver for linux. */
 /* This driver is for parallel port with 5-bit cable (LapLink (R) cable). */
 /*
@@ -31,7 +32,7 @@ static char *version =
 #ifdef MODULE
     "MODULAR "    
 #endif    
-    "PLIP.014 gniibe@mri.co.jp\n";
+    "PLIP $Revision: 1.7 $ gniibe@mri.co.jp\n";
 
 #include <linux/config.h>
 
@@ -110,9 +111,9 @@ static unsigned int net_debug = NET_DEBUG;
 /* Nibble time out = PLIP_NIBBLE_WAIT * PLIP_DELAY_UNIT usec */
 #define PLIP_NIBBLE_WAIT        5000
 
-#define PAR_DATA(dev)          (dev->base_addr+0)
-#define PAR_STATUS(dev)                (dev->base_addr+1)
-#define PAR_CONTROL(dev)       (dev->base_addr+2)
+#define PAR_DATA(dev)          ((dev)->base_addr+0)
+#define PAR_STATUS(dev)                ((dev)->base_addr+1)
+#define PAR_CONTROL(dev)       ((dev)->base_addr+2)
 
 /* Index to functions, as function prototypes. */
 static int plip_tx_packet(struct sk_buff *skb, struct device *dev);
@@ -226,6 +227,8 @@ plip_init(struct device *dev)
        }
     }
 
+    snarf_region(PAR_DATA(dev), 3);
+
     /* Fill in the generic fields of the device structure. */
     ether_setup(dev);
 
@@ -297,6 +300,8 @@ plip_bh(struct device *dev)
        case PLIP_CN_RECEIVE:
            sti();
            disable_irq(dev->irq);
+           outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
+           outb(0x01, PAR_DATA(dev));   /* send ACK */
            dev->interrupt = 0;
            result = plip_receive_packet(dev);
            if (result == 0) { /* success */
@@ -307,9 +312,11 @@ plip_bh(struct device *dev)
                netif_rx(skb);
                if (snd->state != PLIP_PK_DONE) {
                    nl->connection = PLIP_CN_SEND;
+                   outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
                    enable_irq(dev->irq);
                } else {
                    nl->connection = PLIP_CN_NONE;
+                   outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
                    enable_irq(dev->irq);
                    return;
                }
@@ -340,6 +347,7 @@ plip_bh(struct device *dev)
                snd->skb = NULL;
                nl->connection = PLIP_CN_CLOSING;
                queue_task(&nl->deferred, &tq_timer);
+               outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
                enable_irq(dev->irq);
                return;
            } else
@@ -359,6 +367,7 @@ plip_bh(struct device *dev)
            if (result == 0) {
                nl->connection = PLIP_CN_NONE;
                dev->tbusy = 0;
+               outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
                enable_irq(dev->irq);
                return;
            } else {
@@ -392,6 +401,7 @@ plip_bh(struct device *dev)
                dev_kfree_skb(rcv->skb, FREE_WRITE);
        }
        disable_irq(dev->irq);
+       outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
        dev->tbusy = 1;
        nl->connection = PLIP_CN_ERROR;
        outb(0x00, PAR_DATA(dev));
@@ -542,6 +552,7 @@ plip_device_clear(struct device *dev)
     cli();
     dev->tbusy = 0;
     sti();
+    outb(LP_PINITP|LP_PSELECP|LP_PINTEN, PAR_CONTROL(dev));
     enable_irq(dev->irq);
 }
 
@@ -702,7 +713,6 @@ plip_interrupt(int reg_ptr)
            printk("plip: spurious interrupt\n");
        return;
     }
-    outb(0x01, PAR_DATA(dev));   /* send ACK */
     dev->interrupt = 1;
     if (net_debug > 3)
        printk("%s: interrupt.\n", dev->name);
@@ -809,6 +819,7 @@ plip_send_packet(struct device *dev)
            c0 = inb(PAR_STATUS(dev));
            if (c0 & 0x08) {
                disable_irq(dev->irq);
+               outb(LP_PINITP|LP_PSELECP, PAR_CONTROL(dev));
                if (net_debug > 3)
                    printk("+");
                /* OK, connection established! */
@@ -952,16 +963,19 @@ cleanup_module(void)
     else {
        if (dev_plip0.priv) {
            unregister_netdev(&dev_plip0);
+           release_region(PAR_DATA(&dev_plip0), 3);
            kfree_s(dev_plip0.priv, sizeof(struct net_local));
            dev_plip0.priv = NULL;
        }
        if (dev_plip1.priv) {
            unregister_netdev(&dev_plip1);
+           release_region(PAR_DATA(&dev_plip1), 3);
            kfree_s(dev_plip1.priv, sizeof(struct net_local));
            dev_plip1.priv = NULL;
        }
        if (dev_plip2.priv) {
            unregister_netdev(&dev_plip2);
+           release_region(PAR_DATA(&dev_plip2), 3);
            kfree_s(dev_plip2.priv, sizeof(struct net_local));
            dev_plip2.priv = NULL;
        }
index 9dba2ea2a77e18544d331669b0a0c6ae14073788..428ee91a2d22969b23158c803ee6a19c8997d14d 100644 (file)
@@ -262,6 +262,13 @@ int wd_probe1(struct device *dev, int ioaddr)
        dev->stop = &wd_close_card;
        NS8390_init(dev, 0);
 
+#if 1
+       /* Enable interrupt generation on softconfig cards -- M.U */
+       /* .. but possibly potentially unsafe - Donald */
+       if (inb(ioaddr+14) & 0x20)
+               outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
        return 0;
 }
 
index dd809c380afb97338d700d190c767c5cc49a6d8c..1412ca33baba772b729a6d44a1332f15824fe978 100644 (file)
@@ -1,5 +1,5 @@
 This file contains brief information about the SCSI tape driver.
-Last modified: Wed Nov 30 20:34:03 1994 by root@kai.home
+Last modified: Sat Dec 17 13:38:43 1994 by root@kai.home
 
 BASICS
 
@@ -11,8 +11,9 @@ variable block size (within buffer limits). Both the auto-rewind
 device number) are implemented.
 
 By default the driver writes one filemark when the device is closed after
-writing. Two filemarks can be optionally written. In both cases end
-of data is signified by returning zero bytes for two consecutive reads.
+writing and the last operation has been a write. Two filemarks can be
+optionally written. In both cases end of data is signified by
+returning zero bytes for two consecutive reads.
 
 BUFFERING
 
@@ -129,7 +130,8 @@ MTIOCGET Returns some status information.
         mt_dsreg (shifts for the subfields are MT_ST_BLKSIZE_SHIFT and
         MT_ST_DENSITY_SHIFT).
        The GMT_xxx status bits reflect the drive status. GMT_DR_OPEN
-       is set if there is no tape in the drive.
+       is set if there is no tape in the drive. GMT_EOD means either
+       end of recorded data or end of tape. GMT_EOT means end of tape.
 
 
 MISCELLANEOUS COMPILE OPTIONS
index 8671f41c4a5bafcc13b04a6ea56da57806caf875..bf2b02e7d94f02a2307764733b0c5bc9f5928bed 100644 (file)
@@ -1,5 +1,16 @@
 /*
- *      eata.c - Low-level SCSI driver for EISA EATA SCSI controllers.
+ *      eata.c - Low-level driver for EATA/DMA SCSI host adapters.
+ *
+ *      17 Dec 1994 rev. 1.11 for linux 1.1.74
+ *          Use the scsicam_bios_param routine. This allows an easy
+ *          migration path from disk partition tables created using 
+ *          different SCSI drivers and non optimal disk geometry.
+ *
+ *      15 Dec 1994 rev. 1.10 for linux 1.1.74
+ *          Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
+ *          The host->block flag is set for all the detected ISA boards.
+ *          The detect routine no longer enforces LEVEL triggering
+ *          for EISA boards, it just prints a warning message.
  *
  *      30 Nov 1994 rev. 1.09 for linux 1.1.68
  *          Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
  *
  *
  *          This driver is based on the CAM (Common Access Method Committee)
- *          EATA (Enhanced AT Bus Attachment) rev. 2.0A.
+ *          EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
  *
  *      Released by Dario Ballabio (Dario_Ballabio@milano.europe.dg.com)
  *
  */
 
 /*
+ *
+ *  Here is a brief description of the DPT SCSI host adapters.
+ *  All these boards provide an EATA/DMA compatible programming interface
+ *  and are fully supported by this driver:
+ *
+ *  PM2011B/9X -  Entry Level ISA
+ *  PM2021A/9X -  High Performance ISA
+ *  PM2012A       Old EISA
+ *  PM2012B       Old EISA
+ *  PM2022A/9X -  Entry Level EISA
+ *  PM2122A/9X -  High Performance EISA
+ *  PM2322A/9X -  Extra High Performance EISA
+ *
+ *  The DPT PM2001 provides only the EATA/PIO interface and hence is not
+ *  supported by this driver.
  *
  *  This code has been tested with up to 3 Distributed Processing Technology 
  *  PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) eisa controllers,
  *  All boards should be configured at the same IRQ level.
  *  Multiple IRQ configurations are supported too.
  *  Boards can be located in any eisa slot (1-15) and are named EATA0, 
- *  EATA1,... in increasing eisa slot number.
- *  In order to detect the boards, the IRQ must be _level_ triggered 
- *  (not _edge_ triggered).
+ *  EATA1,... in increasing eisa slot number. ISA boards are detected
+ *  after the eisa slot probes.
+ *
+ *  The IRQ for EISA boards should be _level_ triggered (not _edge_ triggered).
+ *  This is a requirement in order to support multiple boards on the same IRQ.
  *
  *  Other eisa configuration parameters are:
  *
@@ -41,6 +69,8 @@
  *  COMMAND TIMEOUT   : ENABLED
  *  CACHE             : DISABLED
  *
+ *  In order to support multiple ISA boards in a reliable way,
+ *  the driver sets host->block = TRUE for all ISA boards.
  */
 
 #include <linux/string.h>
 #include "scsi.h"
 #include "hosts.h"
 #include "sd.h"
+#include <asm/dma.h>
 #include <asm/irq.h>
 #include "linux/in.h"
 #include "eata.h"
 
-#define NO_DEBUG_DETECT
-#define NO_DEBUG_INTERRUPT
-#define NO_DEBUG_STATISTICS
-#define NO_SINGLE_HOST_OPERATIONS
+/* Subversion values */
+#define ISA  0
+#define ESA 1
+
+#undef  DEBUG_DETECT
+#undef  DEBUG_INTERRUPT
+#undef  DEBUG_STATISTICS
 
 #define MAX_TARGET 8
 #define MAX_IRQ 16
-#define MAX_BOARDS 15
+#define MAX_BOARDS 18
 #define MAX_MAILBOXES 64
 #define MAX_SGLIST 64
 #define MAX_CMD_PER_LUN 2
 #define REG_LM          3
 #define REG_MID         4
 #define REG_MSB         5
+#define REG_REGION      9
+#define EISA_RANGE      0xf000
 #define BSY_ASSERTED      0x80
 #define DRQ_ASSERTED      0x08
 #define ABSY_ASSERTED     0x01
@@ -130,7 +166,7 @@ struct eata_info {
    unchar     irq:4,    /* Interrupt Request assigned to this controller */
            irq_tr:1,    /* 0 for edge triggered, 1 for level triggered */
            second:1,    /* 1 if this is a secondary (not primary) controller */
-             drqx:2;    /* DRQ Index (0=DRQ0, 1=DRQ7, 2=DRQ6, 3=DRQ5) */
+             drqx:2;    /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
    unchar  sync;        /* 1 if scsi target id 7...0 is running sync scsi */
    ushort ipad[250];
    };
@@ -157,7 +193,7 @@ struct mssp {
    char mess[12];
    };
 
-/* Command packet structure */
+/* MailBox SCSI Command Packet */
 struct mscp {
    unchar  sreset:1,     /* SCSI Bus Reset Signal should be asserted */
            interp:1,     /* The controller interprets cp, not the target */ 
@@ -204,6 +240,7 @@ struct hostdata {
    int in_reset;                        /* True if board is doing a reset */
    int target_time_out[MAX_TARGET];     /* N. of timeout errors on target */
    int target_reset[MAX_TARGET];        /* If TRUE redo operation on target */
+   unsigned char subversion;            /* Bus type, either ISA or ESA */
    struct mssp sp[MAX_MAILBOXES];       /* Returned status for this board */
    };
 
@@ -257,14 +294,22 @@ static inline unchar read_pio (ushort iobase, ushort *start, ushort *end) {
    return FALSE;
 }
 
-static inline int port_detect (ushort *port_base, unsigned int j, 
-                               Scsi_Host_Template * tpnt) {
+static inline int port_detect(ushort *port_base, unsigned int j, 
+                              Scsi_Host_Template * tpnt) {
+   unsigned char irq, dma_channel, subversion;
    struct eata_info info;
    struct eata_config config;
+   char *board_status;
+
+   /* Allowed DMA channels for ISA (0 indicates reserved) */
+   unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
    char name[16];
 
    sprintf(name, "%s%d", driver_name, j);
 
+   if(check_region(*port_base, REG_REGION)) return FALSE;
+
    if (do_dma(*port_base, 0, READ_CONFIG_PIO)) return FALSE;
 
    /* Read the info structure */
@@ -274,20 +319,49 @@ static inline int port_detect (ushort *port_base, unsigned int j,
    /* check the controller "EATA" signature */
    if (info.sign != EATA_SIGNATURE) return FALSE;
 
-   if (!info.haaval || info.ata || info.drqvld || !info.dmasup) {
-      printk("%s: unusable board found, detaching.\n", name);
-      return FALSE;
+   irq = info.irq;
+
+   if (*port_base & EISA_RANGE) {
+
+      if (!info.haaval || info.ata || info.drqvld || !info.dmasup) {
+         printk("%s: unusable EISA board found, detaching.\n", name);
+         return FALSE;
+         }
+
+      subversion = ESA;
+      dma_channel = 0;
+      }
+   else {
+
+      if (!info.haaval || info.ata || !info.drqvld || !info.dmasup) {
+         printk("%s: unusable ISA board found, detaching.\n", name);
+         return FALSE;
+         }
+
+      subversion = ISA;
+      dma_channel = dma_channel_table[3 - info.drqx];
       }
 
-   if (!info.irq_tr) {
-      printk("%s: IRQ must be level triggered, detaching.\n", name);
+   if (subversion == ESA && !info.irq_tr)
+      printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
+             name, irq);
+
+   if (info.second)
+      board_status = "Sec.";
+   else
+      board_status = "Prim.";
+
+   /* Board detected, allocate its IRQ if not already done */
+   if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
+       (irq, eata_interrupt_handler, SA_INTERRUPT, driver_name))) {
+      printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
       return FALSE;
       }
 
-   /* EATA board detected, allocate its IRQ if not already done */
-   if ((info.irq >= MAX_IRQ) || ((irqlist[info.irq] == NO_IRQ) && request_irq
-       (info.irq, eata_interrupt_handler, SA_INTERRUPT, driver_name))) {
-      printk("%s: unable to allocate IRQ %u, detaching.\n", name, info.irq);
+   if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+      printk("%s: unable to allocate DMA channel %u, detaching.\n",
+             name, dma_channel);
+      free_irq(irq);
       return FALSE;
       }
 
@@ -303,21 +377,38 @@ static inline int port_detect (ushort *port_base, unsigned int j,
 
    sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
    sh[j]->io_port = *port_base;
-   sh[j]->dma_channel = 0;
-   sh[j]->irq = info.irq;
+   sh[j]->dma_channel = dma_channel;
+   sh[j]->irq = irq;
    sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size);
    sh[j]->this_id = (ushort) ntohl(info.host_addr);
    sh[j]->can_queue = (ushort) ntohs(info.queue_size);
    sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
-   sh[j]->unchecked_isa_dma = FALSE;
+
+   /* Register the I/O space that we use */
+   snarf_region(sh[j]->io_port, REG_REGION);
+
    memset(HD(j), 0, sizeof(struct hostdata));
+   HD(j)->subversion = subversion;
    HD(j)->board_number = j;
-   irqlist[info.irq] = j;
+   irqlist[irq] = j;
+
+   if (HD(j)->subversion == ESA)
+      sh[j]->unchecked_isa_dma = FALSE;
+   else {
+      sh[j]->block = sh[j];
+      sh[j]->unchecked_isa_dma = TRUE;
+      disable_dma(dma_channel);
+      clear_dma_ff(dma_channel);
+      set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+      enable_dma(dma_channel);
+      }
+
    strcpy(BN(j), name);
 
-   printk("%s: SCSI ID %d, PORT 0x%03x, IRQ %u, SG %d, "\
-          "Mbox %d, CmdLun %d.\n", BN(j), sh[j]->this_id, 
-           sh[j]->io_port, sh[j]->irq, sh[j]->sg_tablesize, 
+   printk("%s: %s, ID %d, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
+          "Mbox %d, CmdLun %d.\n", BN(j), board_status, sh[j]->this_id, 
+           sh[j]->io_port, sh[j]->irq, 
+           sh[j]->dma_channel, sh[j]->sg_tablesize, 
            sh[j]->can_queue, sh[j]->cmd_per_lun);
 
    /* DPT PM2012 does not allow to detect sg_tablesize correctly */
@@ -344,12 +435,13 @@ static inline int port_detect (ushort *port_base, unsigned int j,
 int eata_detect (Scsi_Host_Template * tpnt) {
    unsigned int j = 0, k, flags;
 
-   ushort eisa_io_port[] = { 
+   ushort io_port[] = { 
       0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
-      0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88, 0x0
+      0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88, 
+      0x330,  0x230,  0x1f0,  0x170,  0x0
       };
 
-   ushort *port_base = eisa_io_port;
+   ushort *port_base = io_port;
 
    save_flags(flags);
    cli();
@@ -363,21 +455,11 @@ int eata_detect (Scsi_Host_Template * tpnt) {
 
    while (*port_base) {
 
-      if(j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+      if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
 
       port_base++;
       }
 
-#if defined (SINGLE_HOST_OPERATIONS)
-   /* Create a circular linked list among the detected boards. */
-   if (j > 1) {
-
-      for (k = 0; k < (j - 1); k++) sh[k]->block = sh[k + 1];
-
-      sh[j - 1]->block = sh[0];
-      }
-#endif
-
    restore_flags(flags);
    return j;
 }
@@ -388,7 +470,7 @@ static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
 
    sgpnt = (struct scatterlist *) SCpnt->request_buffer;
 
-   for(k = 0; k < SCpnt->use_sg; k++) {
+   for (k = 0; k < SCpnt->use_sg; k++) {
       cpp->sglist[k].address = htonl((unsigned int) sgpnt[k].address);
       cpp->sglist[k].num_bytes = htonl((unsigned int) sgpnt[k].length);
       }
@@ -413,7 +495,7 @@ int eata_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
 
-   for(k = 0; k < sh[j]->can_queue; k++, i++) {
+   for (k = 0; k < sh[j]->can_queue; k++, i++) {
 
       if (i >= sh[j]->can_queue) i = 0;
 
@@ -664,30 +746,6 @@ int eata_reset (Scsi_Cmnd *SCarg) {
       }
 }
 
-int eata_bios_param (Disk * disk, int dev, int *ip) {
-   int size = disk->capacity;
-
-   if(size < 0x200000) {           /* < 1Gbyte */
-      ip[0]=64;
-      ip[1]=32;
-      }
-   else if (size < 0x400000) {     /* < 2Gbyte */
-      ip[0]=65;
-      ip[1]=63;
-      }
-   else if(size < 0x800000) {      /* < 4Gbyte */
-      ip[0]=128;
-      ip[1]=63;
-      }
-   else {                          /* ok up to 8Gbyte */
-      ip[0]=255;
-      ip[1]=63;
-      }    
-
-   ip[2] = size / (ip[0] * ip[1]);
-   return 0;
-}
-
 static void eata_interrupt_handler(int irq) {
    Scsi_Cmnd *SCpnt;
    unsigned int i, j, k, flags, status, loops, total_loops = 0;
@@ -725,7 +783,7 @@ static void eata_interrupt_handler(int irq) {
          inb(sh[j]->io_port + REG_STATUS);
    
          /* Service all mailboxes of this board */
-         for(i = 0; i < sh[j]->can_queue; i++) {
+         for (i = 0; i < sh[j]->can_queue; i++) {
             spp = &HD(j)->sp[i];
    
             /* Check if this mailbox has completed the operation */
index 694d5feaa21cc27fc1d48ae3db8c48af5d18058f..b1af19d35921544782f8d3350cefd5119e27bb31 100644 (file)
@@ -1,21 +1,22 @@
 /*
- *     eata.h - used by low-level scsi driver for EISA EATA controllers.
+ *     eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
  *
  */
-#ifndef _EISA_EATA_H
-#define _EISA_EATA_H
+#ifndef _EATA_H
+#define _EATA_H
 
-#define EATA_VERSION "1.09.01"
+#include <linux/scsicam.h>
+
+#define EATA_VERSION "1.11.00"
 
 int eata_detect(Scsi_Host_Template *);
 int eata_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
 int eata_abort(Scsi_Cmnd *);
 int eata_reset(Scsi_Cmnd *);
-int eata_bios_param(Disk *, int, int*);
 
 #define EATA {  NULL, /* Ptr for modules */                    \
                 NULL, /* usage count for modules */           \
-                "EISA EATA 2.0A rev. " EATA_VERSION " by "     \
+                "EATA/DMA 2.0A rev. " EATA_VERSION " by "      \
                 "Dario_Ballabio@milano.europe.dg.com.",        \
                 eata_detect,                                  \
                 NULL, /* Release */                           \
@@ -25,13 +26,13 @@ int eata_bios_param(Disk *, int, int*);
                eata_abort,                                    \
                eata_reset,                                    \
                NULL,                                          \
-               eata_bios_param,                               \
+               scsicam_bios_param,                            \
                0,   /* can_queue, reset by detect */          \
                 7,   /* this_id, reset by detect */            \
                 0,   /* sg_tablesize, reset by detect */       \
                 0,   /* cmd_per_lun, reset by detect */        \
                0,   /* number of boards present */            \
-                0,   /* unchecked isa dma */                   \
+                0,   /* unchecked isa dma, reset by detect */  \
                 ENABLE_CLUSTERING                              \
                 }
 #endif
index e8e644e5c3a0e4b02ceab30302cbda9d53d8e003..6e42031583a5ae441f9f9f54b8a6c4493e155ad5 100644 (file)
@@ -313,6 +313,30 @@ unsigned int scsi_init()
        }
        printk ("scsi : %d hosts.\n", count);
 
+      {
+      int block_count = 0, index;
+      struct Scsi_Host * sh[128], * shpnt;
+
+         for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+            if (shpnt->block) sh[block_count++] = shpnt;
+      
+         if (block_count == 1) sh[0]->block = NULL;
+
+         else if (block_count > 1) {
+
+            for(index = 0; index < block_count - 1; index++) {
+               sh[index]->block = sh[index + 1];
+               printk("scsi%d : added to blocked host list.\n", 
+                      sh[index]->host_no);
+               }
+           
+            sh[block_count - 1]->block = sh[0];
+            printk("scsi%d : added to blocked host list.\n", 
+                   sh[index]->host_no);
+            }
+
+      }
+
        /* Now attach the high level drivers */
 #ifdef CONFIG_BLK_DEV_SD
        scsi_register_device(&sd_template);
index 20259643f9847b77e37fb1e62dc103246b84ceb1..4fb7625ba89d6543987dd122ca0840f7b3573a60 100644 (file)
@@ -228,8 +228,6 @@ typedef struct  SHT
        be two Scsi_Host entries, but only 1 Scsi_Host_Template entries.
 */
 
-#define SCSI_HOST_BLOCK 0x80
-
 struct Scsi_Host
        {
                struct Scsi_Host * next;
index 63bd2cb534d36bb7cc8b4b006b1ea896ae31ed8d..6c4f46da10cd24906a6e15c6524ee62b56b831f4 100644 (file)
@@ -46,6 +46,9 @@ static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
 static int time_start;
 static int time_elapsed;
 static int scsi_maybe_deadlocked = 0;
+static int max_active = 0;
+static struct Scsi_Host * host_active = NULL;
+static struct Scsi_Host * host_next = NULL;
 
 #define MAX_SCSI_DEVICE_CODE 10
 const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
@@ -160,6 +163,8 @@ static struct blist blacklist[] =
    {"QUANTUM","LPS525S","3110"},/* Locks sometimes if polled for lun != 0 */
    {"QUANTUM","PD1225S","3110"},/* Locks sometimes if polled for lun != 0 */
    {"MEDIAVIS","CDR-H93MV","1.31"},  /* Locks up if polled for lun != 0 */
+   {"SANKYO", "CP525","6.64"},  /* causes failed REQ SENSE, extra reset */
+   {"HP", "C1750A", "3226"},    /* scanjet iic */
    {NULL, NULL, NULL}};        
 
 static int blacklisted(unsigned char * response_data){
@@ -190,6 +195,21 @@ static int blacklisted(unsigned char * response_data){
 
 volatile int in_scan_scsis = 0;
 static int the_result;
+
+static void restart_all(void) {
+ /*
+  * If we might have deadlocked, get things started again. Only
+  * for block devices. Character devices should never deadlock.
+  */
+  struct Scsi_Device_Template * sdtpnt;
+
+  for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+
+     if (sdtpnt->blk && blk_dev[sdtpnt->major].request_fn)
+        (*blk_dev[sdtpnt->major].request_fn)();
+
+  }
+
 static void scan_scsis_done (Scsi_Cmnd * SCpnt)
        {
        
@@ -375,6 +395,8 @@ static void scan_scsis (struct Scsi_Host * shpnt)
                case TYPE_TAPE :
                case TYPE_DISK :
                case TYPE_MOD :
+               case TYPE_PROCESSOR :
+               case TYPE_SCANNER :
                  SDpnt->writeable = 1;
                  break;
                case TYPE_WORM :
@@ -589,8 +611,17 @@ Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
 
   if (!SCpnt) return NULL;
 
-  if (device->host->can_queue
-      && device->host->host_busy >= device->host->can_queue) return NULL;
+  if (device->host->can_queue &&
+       ((device->host->block && host_active && device->host != host_active) || 
+        (device->host->block && host_next && device->host != host_next) ||
+        device->host->host_busy >= device->host->can_queue)) {
+
+     if (device->host->block && !host_next && host_active 
+                             && device->host != host_active) 
+        host_next = device->host;
+
+     return NULL;
+     }
 
   if (req) {
     memcpy(&SCpnt->request, req, sizeof(struct request));
@@ -665,11 +696,18 @@ Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
   
   host = device->host;
   
-  if (intr_count && device->host->can_queue
-      && device->host->host_busy >= device->host->can_queue) {
-    scsi_maybe_deadlocked = 1;
-    return NULL;
-  }
+  if (intr_count && host->can_queue &&
+                      ((host->block && host_active && host != host_active) ||
+                       (host->block && host_next && host != host_next) ||
+                       host->host_busy >= host->can_queue)) {
+
+     scsi_maybe_deadlocked = 1;
+
+     if (host->block && !host_next && host_active && host != host_active) 
+        host_next = host;
+
+     return NULL;
+     }
 
   while (1==1){
     SCpnt = host->host_queue;
@@ -891,25 +929,49 @@ void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
 
        SCpnt->pid = scsi_pid++; 
 
-       while (1==1){
-         cli();
-         if (host->can_queue
-             && host->host_busy >= host->can_queue)
-           {
-             sti();
-             SCSI_SLEEP(&host->host_wait, 
-                        (host->host_busy >= host->can_queue));
-           } else {
-             host->host_busy++;
-             if (host->block) {
-               struct Scsi_Host * block;
-               for(block = host->block; block != host; block = block->block)
-                 block->host_busy |= SCSI_HOST_BLOCK;
-             }
-             sti();
-             break;
-           };
-      };
+        for(;;) {
+
+          cli();
+
+         if (host->can_queue &&
+                ((host->block && host_active && host != host_active) ||
+                (host->block && host_next && host != host_next) ||
+                host->host_busy >= host->can_queue)) {
+
+             if (intr_count) 
+                panic("scsi: queueing to inactive host while in interrupt.\n");
+
+             if (host->block && !host_next && host_active 
+                                           && host != host_active) 
+                host_next = host;
+
+            sti();
+            SCSI_SLEEP(&host->host_wait, 
+                       ((host->block && host_active && host != host_active) ||
+                        (host->block && host_next && host != host_next) ||
+                       host->host_busy >= host->can_queue));
+            } 
+
+          else {
+            host->host_busy++;
+
+            if (host->block) {
+
+               if (!host_active) {
+                   max_active = 0;
+                   host_active = host;
+                   host_next = NULL;
+                   }
+                else if (max_active > 7 && !host_next) 
+                   host_next = host->block;
+
+                max_active++;
+                }
+
+            sti();
+            break;
+            }
+          }
 /*
        Our own function scsi_done (which marks the host as not busy, disables 
        the timeout counter, etc) will be called by us or by the 
@@ -1344,6 +1406,9 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
                        /* fall through to REDO */
 
                case REDO:
+
+                        if (host->block) scsi_maybe_deadlocked = 1;
+
                        if (SCpnt->flags & WAS_SENSE)                   
                                scsi_request_sense(SCpnt);      
                        else    
@@ -1362,44 +1427,39 @@ static void scsi_done (Scsi_Cmnd * SCpnt)
                        INTERNAL_ERROR;
                }
 
-       if (status == FINISHED) 
-         {
+
+       if (status == FINISHED) {
 #ifdef DEBUG
-               printk("Calling done function - at address %08x\n", SCpnt->done);
+          printk("Calling done function - at address %08x\n", SCpnt->done);
 #endif
-               host->host_busy--; /* Indicate that we are free */
-               if (host->host_busy == 0 && host->block) {
-                 struct Scsi_Host * block;
-                 /*
-                  * Now remove the locks for all of the related hosts.
-                  */
-                 for(block = host->block; block != host; block = block->block)
-                     block->host_busy &= ~SCSI_HOST_BLOCK;
-                 /*
-                  * Now wake them up.  We do this in two separate stages to prevent
-                  * race conditions.
-                  */
-                 for(block = host->block; block != host; block = block->block)
-                     wake_up(&block->host_wait);
-                 /*
-                  * If we might have deadlocked, get things started again.  Only
-                  * for block devices - character devices should never deadlock.
-                  */
-                 if (scsi_maybe_deadlocked) {
-                   struct Scsi_Device_Template * sdtpnt;
-                   scsi_maybe_deadlocked = 0;
-                   for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
-                     if (sdtpnt->blk && blk_dev[sdtpnt->major].request_fn)
-                       (*blk_dev[sdtpnt->major].request_fn)();
-                 }
-               }
-               wake_up(&host->host_wait);
-               SCpnt->result = result | ((exit & 0xff) << 24);
-               SCpnt->use_sg = SCpnt->old_use_sg;
-               SCpnt->cmd_len = SCpnt->old_cmd_len;
-               SCpnt->done (SCpnt);
-               }
+          host->host_busy--; /* Indicate that we are free */
+
+          if (host->block && host->host_busy == 0) {
+              struct Scsi_Host * block;
+
+              block = host_next;
+              host_active = NULL;
+
+              if (block) wake_up(&block->host_wait);
+
+              for (block = host->block; block != host; block = block->block)
+                 wake_up(&block->host_wait);
+
+              host_next = NULL;
 
+              if (scsi_maybe_deadlocked) {
+                 scsi_maybe_deadlocked = 0;
+                 restart_all();
+                 }
+
+              }
+
+           wake_up(&host->host_wait);
+          SCpnt->result = result | ((exit & 0xff) << 24);
+          SCpnt->use_sg = SCpnt->old_use_sg;
+          SCpnt->cmd_len = SCpnt->old_cmd_len;
+          SCpnt->done (SCpnt);
+          }
 
 #undef FINISHED
 #undef REDO
@@ -1561,13 +1621,26 @@ int scsi_reset (Scsi_Cmnd * SCpnt)
                        else
                                {
                                host->host_busy++;
+
+                                if (host->block) {
+                                   host_active = host;
+                                   host_next = NULL;
+                                   }
        
                                sti();
                                temp = host->hostt->reset(SCpnt);
                                host->last_reset = jiffies;
                                host->host_busy--;
+
                                }
 
+                        if (host->block && host->host_busy == 0) {
+                           host_active = NULL;
+                           host_next = NULL;
+                           restart_all();
+                           }
+
+
 #ifdef DEBUG
                        printk("scsi reset function returned %d\n", temp);
 #endif
@@ -2283,7 +2356,7 @@ scsi_dump_status(void)
     for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
       {
        /*  (0) 0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x      */
-       printk("(%d) %d:%d:%d (%4.4x %d %d %d %d) (%d %d %x) (%d %d %d) %x %x %x\n",
+       printk("(%d) %d:%d:%d (%4.4x %ld %ld %ld %ld) (%d %d %x) (%d %d %d) %x %x %x\n",
               i++, SCpnt->host->host_no,
               SCpnt->target,
               SCpnt->lun,
@@ -2302,7 +2375,7 @@ scsi_dump_status(void)
               SCpnt->sense_buffer[2],
               SCpnt->result);
       };
-  printk("wait_for_request = %x\n", wait_for_request);
+  printk("wait_for_request = %p\n", wait_for_request);
   /* Now dump the request lists for each block device */
   printk("Dump of pending block device requests\n");
   for(i=0; i<MAX_BLKDEV; i++)
@@ -2312,7 +2385,7 @@ scsi_dump_status(void)
        printk("%d: ", i);
        req = blk_dev[i].current_request;
        while(req) {
-         printk("(%x %d %d %d %d) ",
+         printk("(%x %d %ld %ld %ld) ",
                 req->dev,
                 req->cmd,
                 req->sector,
index 83d7d55abbeb3b6acf03aa2e53d72fdb1b8708d1..6bf6e533bb8393a5f64cd92ca886893b96c7d68f 100644 (file)
@@ -234,8 +234,10 @@ extern const unsigned char scsi_command_size[8];
 
 #define TYPE_DISK      0x00
 #define TYPE_TAPE      0x01
+#define TYPE_PROCESSOR 0x03    /* HP scanners use this */
 #define TYPE_WORM      0x04    /* Treated as ROM by our system */
 #define TYPE_ROM       0x05
+#define TYPE_SCANNER   0x06
 #define TYPE_MOD       0x07  /* Magneto-optical disk - treated as TYPE_DISK */
 #define TYPE_NO_LUN    0x7f
 
index e74e78d2a75158befc6801da63c3c934a938d14f..1ea7bf4b2fe927cd86e2f41711f620e823192fbb 100644 (file)
@@ -13,7 +13,7 @@
 #include "scsi_ioctl.h"
 
 #define MAX_RETRIES 5  
-#define MAX_TIMEOUT 200
+#define MAX_TIMEOUT 900
 #define MAX_BUF 4096
 
 #define max(a,b) (((a) > (b)) ? (a) : (b))
index 9fa6b8748ad30e2411d4d31bd2f7170f99bf77d1..1fc8072000fd546943e6a158914a1330e9c6409e 100644 (file)
@@ -5,13 +5,13 @@
   History:
   Rewritten from Dwayne Forsyth's SCSI tape driver by Kai Makisara.
   Contribution and ideas from several people including (in alphabetical
-  order) Klaus Ehrenfried, Wolfgang Denk, J"org Weule, and
-  Eric Youngdale.
+  order) Klaus Ehrenfried, Wolfgang Denk, Andreas Koppenh"ofer, J"org Weule,
+  and Eric Youngdale.
 
   Copyright 1992, 1993, 1994 Kai Makisara
                 email makisara@vtinsx.ins.vtt.fi or Kai.Makisara@vtt.fi
 
-  Last modified: Wed Nov 30 21:09:53 1994 by root@kai.home
+  Last modified: Sun Dec 18 10:15:33 1994 by root@kai.home
 */
 
 #include <linux/fs.h>
@@ -74,7 +74,7 @@ static int debugging = 1;
 #define MAX_READY_RETRIES 5
 #define NO_TAPE  NOT_READY
 
-#define ST_TIMEOUT 27000
+#define ST_TIMEOUT 90000
 #define ST_LONG_TIMEOUT 200000
 
 static int st_nbr_buffers;
@@ -249,7 +249,8 @@ back_over_eof(int dev)
   SCpnt->request.dev = -1;
   if ((STp->buffer)->last_result != 0) {
     printk("st%d: Backing over filemark failed.\n", dev);
-    (STp->mt_status)->mt_fileno += 1;
+    if ((STp->mt_status)->mt_fileno >= 0)
+      (STp->mt_status)->mt_fileno += 1;
     (STp->mt_status)->mt_blkno = 0;
   }
 
@@ -363,6 +364,7 @@ flush_buffer(struct inode * inode, struct file * filp, int seek_next)
        STp->block_size;
   (STp->buffer)->buffer_bytes = 0;
   (STp->buffer)->read_pointer = 0;
+  STp->drv_block -= backspace;
   result = 0;
   if (!seek_next) {
     if ((STp->eof == ST_FM) && !STp->eof_hit) {
@@ -637,7 +639,8 @@ scsi_tape_close(struct inode * inode, struct file * filp)
        }
        else {
          SCpnt->request.dev = -1;  /* Mark as not busy */
-          (STp->mt_status)->mt_fileno++ ;
+         if ((STp->mt_status)->mt_fileno >= 0)
+             (STp->mt_status)->mt_fileno++ ;
          STp->drv_block = 0;
          if (STp->two_fm)
            back_over_eof(dev);
@@ -677,6 +680,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
     int dev;
     int total, do_count, blks, retval, transfer;
     int write_threshold;
+    int doing_write = 0;
     static unsigned char cmd[10];
     char *b_point;
     Scsi_Cmnd * SCpnt;
@@ -757,6 +761,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
          (STp->block_size != 0 &&
           (STp->buffer)->buffer_bytes + count > write_threshold))
     {
+      doing_write = 1;
       if (STp->block_size == 0)
        do_count = count;
       else {
@@ -864,7 +869,7 @@ st_write(struct inode * inode, struct file * filp, char * buf, int count)
       count = 0;
     }
 
-    if ((STp->buffer)->last_result_fatal != 0) {
+    if (doing_write && (STp->buffer)->last_result_fatal != 0) {
       SCpnt->request.dev = -1;
       return (STp->buffer)->last_result_fatal;
     }
@@ -1123,7 +1128,8 @@ st_read(struct inode * inode, struct file * filp, char * buf, int count)
          STp->drv_block = 0;
          if (STp->moves_after_eof > 1)
            STp->moves_after_eof = 0;
-         (STp->mt_status)->mt_fileno++;
+         if ((STp->mt_status)->mt_fileno >= 0)
+           (STp->mt_status)->mt_fileno++;
        }
        if (total == 0 && STp->eof == ST_EOM_OK)
          return (-EIO);  /* ST_EOM_ERROR not used in read */
@@ -1223,7 +1229,8 @@ st_int_ioctl(struct inode * inode,struct file * file,
         printk("st%d: Spacing tape forward over %d filemarks.\n", dev,
                cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
 #endif
-       fileno += arg;
+       if (fileno >= 0)
+        fileno += arg;
        blkno = 0;
        at_sm &= (arg != 0);
        break; 
@@ -1243,11 +1250,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
         printk("st%d: Spacing tape backward over %ld filemarks.\n", dev, (-ltmp));
        }
 #endif
-       fileno -= arg;
+       if (fileno >= 0)
+        fileno -= arg;
        blkno = (-1);  /* We can't know the block number */
        at_sm &= (arg != 0);
        break; 
-      case MTFSR:
+     case MTFSR:
        cmd[0] = SPACE;
        cmd[1] = 0x00; /* Space Blocks */
        cmd[2] = (arg >> 16);
@@ -1281,7 +1289,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
         blkno -= arg;
        at_sm &= (arg != 0);
        break; 
-      case MTFSS:
+     case MTFSS:
        cmd[0] = SPACE;
        cmd[1] = 0x04; /* Space Setmarks */
        cmd[2] = (arg >> 16);
@@ -1338,7 +1346,8 @@ st_int_ioctl(struct inode * inode,struct file * file,
                  cmd[2] * 65536 + cmd[3] * 256 + cmd[4]);
        }
 #endif
-       fileno += arg;
+       if (fileno >= 0)
+        fileno += arg;
        blkno = 0;
        at_sm = (cmd_in == MTWSM);
        break; 
@@ -1390,6 +1399,8 @@ st_int_ioctl(struct inode * inode,struct file * file,
        /* space to the end of tape */
        ioctl_result = st_int_ioctl(inode, file, MTFSF, 0x3fff);
        fileno = (STp->mt_status)->mt_fileno ;
+       if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
+        return 0;
        /* The next lines would hide the number of spaced FileMarks
           That's why I inserted the previous lines. I had no luck
          with detecting EOM with FSF, so we go now to EOM.
@@ -1400,7 +1411,7 @@ st_int_ioctl(struct inode * inode,struct file * file,
        if (debugging)
         printk("st%d: Spacing to end of recorded medium.\n", dev);
 #endif
-       blkno = (-1);
+       blkno = 0;
        at_sm = 0;
        break; 
      case MTERASE:
@@ -1408,6 +1419,12 @@ st_int_ioctl(struct inode * inode,struct file * file,
         return (-EACCES);
        cmd[0] = ERASE;
        cmd[1] = 1;  /* To the end of tape */
+#ifdef ST_NOWAIT
+       cmd[1] |= 2;  /* Don't wait for completion */
+       timeout = ST_TIMEOUT;
+#else
+       timeout = ST_LONG_TIMEOUT * 8;
+#endif
 #ifdef DEBUG
        if (debugging)
         printk("st%d: Erasing tape.\n", dev);
@@ -1509,6 +1526,10 @@ st_int_ioctl(struct inode * inode,struct file * file,
 
    SCpnt->request.dev = -1;  /* Mark as not busy */
 
+   if (cmd_in == MTFSF)
+     STp->moves_after_eof = 0;
+   else
+     STp->moves_after_eof = 1;
    if (!ioctl_result) {
      if (cmd_in != MTSEEK) {
        STp->drv_block = blkno;
@@ -1519,10 +1540,6 @@ st_int_ioctl(struct inode * inode,struct file * file,
        STp->drv_block = (STp->mt_status)->mt_fileno = (-1);
        STp->at_sm = 0;
      }
-     if (cmd_in == MTFSF)
-       STp->moves_after_eof = 0;
-     else
-       STp->moves_after_eof = 1;
      if (cmd_in == MTBSFM)
        ioctl_result = st_int_ioctl(inode, file, MTFSF, 1);
      else if (cmd_in == MTFSFM)
@@ -1565,17 +1582,24 @@ st_int_ioctl(struct inode * inode,struct file * file,
           (SCpnt->sense_buffer[4] << 16) +
           (SCpnt->sense_buffer[5] << 8) +
           SCpnt->sense_buffer[6] );
-     if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) )
-       (STp->mt_status)->mt_fileno = fileno - undone ;
-     else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) )
+     if ( (cmd_in == MTFSF) || (cmd_in == MTFSFM) ) {
+       if (fileno >= 0)
+        (STp->mt_status)->mt_fileno = fileno - undone ;
+       else
+        (STp->mt_status)->mt_fileno = fileno;
+       STp->drv_block = 0;
+     }
+     else if ( (cmd_in == MTBSF) || (cmd_in == MTBSFM) ) {
        (STp->mt_status)->mt_fileno = fileno + undone ;
+       STp->drv_block = 0;
+     }
      else if (cmd_in == MTFSR) {
        if (blkno >= undone)
         STp->drv_block = blkno - undone;
        else
         STp->drv_block = (-1);
      }
-     else if (cmd_in == MTBSR && blkno >= 0) {
+     else if (cmd_in == MTBSR) {
        if (blkno >= 0)
         STp->drv_block = blkno + undone;
        else
@@ -1625,11 +1649,15 @@ st_ioctl(struct inode * inode,struct file * file,
 
      memcpy_fromfs((char *) &mtc, (char *)arg, sizeof(struct mtop));
 
-     i = flush_buffer(inode, file, mtc.mt_op == MTNOP || mtc.mt_op == MTSEEK ||
+     i = flush_buffer(inode, file, mtc.mt_op == MTSEEK ||
                      mtc.mt_op == MTREW || mtc.mt_op == MTOFFL ||
                      mtc.mt_op == MTRETEN || mtc.mt_op == MTEOM);
      if (i < 0)
        return i;
+     if (mtc.mt_op != MTNOP && mtc.mt_op != MTSETBLK &&
+        mtc.mt_op != MTSETDENSITY && mtc.mt_op != MTWSM &&
+        mtc.mt_op != MTSETDRVBUFFER)
+       STp->rw = ST_IDLE;  /* Prevent automatic WEOF */
 
      if (mtc.mt_op == MTSETDRVBUFFER &&
         (mtc.mt_count & MT_ST_OPTIONS) != 0)
@@ -1669,7 +1697,7 @@ st_ioctl(struct inode * inode,struct file * file,
      }
      if (STp->eof == ST_EOM_OK || STp->eof == ST_EOM_ERROR)
        (STp->mt_status)->mt_gstat |= GMT_EOT(0xffffffff);
-     else if (STp->eof == ST_EOD)
+     else if (STp->eof == ST_EOD || STp->eof == ST_EOM_OK)
        (STp->mt_status)->mt_gstat |= GMT_EOD(0xffffffff);
      if (STp->density == 1)
        (STp->mt_status)->mt_gstat |= GMT_D_800(0xffffffff);
index 42feecbac57dfc47f2f987c52cc9402875cd243b..776023ed989aae6b7f631b3823759f3a570acaec 100644 (file)
@@ -1,7 +1,10 @@
 /*
- *      u14-34f.c - Low-level SCSI driver for UltraStor 14F/34F
+ *      u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
  *
- *      30 Nov 1994 rev. 1.09 for linux 1.1.68
+ *      15 Dec 1994 rev. 1.12 for linux 1.1.74
+ *          The host->block flag is set for all the detected ISA boards.
+ *
+ *      30 Nov 1994 rev. 1.11 for linux 1.1.68
  *          Redo i/o on target status CONDITION_GOOD for TYPE_DISK only.
  *          Added optional support for using a single board at a time.
  *
  *
  *      Released by Dario Ballabio (Dario_Ballabio@milano.europe.dg.com)
  *
- *      WARNING: if your 14F board has old firmware revision (see below)
- *               keep the following statement, otherwise comment it.
+ *      WARNING: if your 14F board has an old firmware revision (see below)
+ *               you must change "#undef" into "#define" in the following
+ *               statement.
  */
-#if 0
-#define HAVE_OLD_U14F_FIRMWARE
-#endif
+#undef HAVE_OLD_U14F_FIRMWARE
 /*
  *  The UltraStor 14F, 24F, and 34F are a family of intelligent, high
  *  performance SCSI-2 host adapters.
@@ -33,7 +35,7 @@
  *
  *  14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
  *  24F - EISA Bus Master HA with floppy support and WD1003 emulation.
- *  34F - VL-Bus Bus Master HA (no WD1003 emulation).
+ *  34F - VESA Local-Bus Bus Master HA (no WD1003 emulation).
  *
  *  This code has been tested with up to two U14F boards, using both 
  *  firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware
@@ -54,7 +56,7 @@
  *  The boot controller must have its BIOS enabled, while other boards can
  *  have their BIOS disabled, or enabled to an higher address.
  *  Boards are named Ux4F0, Ux4F1..., according to the port address order in
- *  the isa_io_port[] array.
+ *  the io_port[] array.
  *  
  *  The following facts are based on real testing results (not on
  *  documentation) on the above U14F board.
@@ -93,6 +95,8 @@
  *    The new firmware has fixed all the above problems and has been tested 
  *    with up to 33 scatter/gather lists.
  *
+ *  In order to support multiple ISA boards in a reliable way,
+ *  the driver sets host->block = TRUE for all ISA boards.
  */
 
 #include <linux/string.h>
 #include <asm/irq.h>
 #include "u14-34f.h"
 
-/* Values for the PRODUCT_ID ports for the 14F */
-#define U14F_PRODUCT_ID1  0x56
-#define U14F_PRODUCT_ID2  0x40        /* NOTE: Only upper nibble is used */
+/* Values for the PRODUCT_ID ports for the 14/34F */
+#define PRODUCT_ID1  0x56
+#define PRODUCT_ID2  0x40        /* NOTE: Only upper nibble is used */
 
 /* Subversion values */
-#define U14F 0
-#define U34F 1
+#define ISA  0
+#define ESA 1
 
 #define OP_HOST_ADAPTER   0x1
 #define OP_SCSI           0x2
 #define U34F_MAX_SGLIST 33
 #define U34F_CLUSTERING ENABLE_CLUSTERING
 
-#define DO_BUS_RESET          /* Reset SCSI bus on error */
-#define NO_DEBUG_DETECT
-#define NO_DEBUG_INTERRUPT
-#define NO_DEBUG_STATISTICS
-#define SINGLE_HOST_OPERATIONS
+#undef  DEBUG_DETECT
+#undef  DEBUG_INTERRUPT
+#undef  DEBUG_STATISTICS
 
 #define MAX_TARGET 8
 #define MAX_IRQ 16
@@ -230,7 +232,7 @@ struct hostdata {
    unsigned char bios_drive_number: 1;
    unsigned char heads;
    unsigned char sectors;
-   unsigned char subversion: 4;
+   unsigned char subversion: 4;         /* Bus type, either ISA or ESA */
 
    /* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */
    unsigned char slot;
@@ -257,7 +259,8 @@ static inline unchar wait_on_busy(ushort iobase) {
 
 static inline int port_detect(ushort *port_base, unsigned int j, 
                               Scsi_Host_Template * tpnt) {
-   unsigned char irq, dma_channel, in_byte, subversion, sys_mask, lcl_mask;
+   unsigned char irq, dma_channel, subversion;
+   unsigned char in_byte;
 
    /* Allowed BIOS base addresses (NULL indicates reserved) */
    void *bios_segment_table[8] = { 
@@ -267,10 +270,10 @@ static inline int port_detect(ushort *port_base, unsigned int j,
       };
    
    /* Allowed IRQs */
-   unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
+   unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
    
-   /* Allowed DMA channels for 14f (0 indicates reserved) */
-   unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
+   /* Allowed DMA channels for ISA (0 indicates reserved) */
+   unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
    
    /* Head/sector mappings */
    struct {
@@ -300,26 +303,27 @@ static inline int port_detect(ushort *port_base, unsigned int j,
 
    if(check_region(*port_base, REG_REGION)) return FALSE;
 
-   if (inb(*port_base + REG_PRODUCT_ID1) != U14F_PRODUCT_ID1) return FALSE;
+   if (inb(*port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) return FALSE;
 
    in_byte = inb(*port_base + REG_PRODUCT_ID2);
 
-   if ((in_byte & 0xf0) != U14F_PRODUCT_ID2) return FALSE;
+   if ((in_byte & 0xf0) != PRODUCT_ID2) return FALSE;
 
    *(char *)&config_1 = inb(*port_base + REG_CONFIG1);
    *(char *)&config_2 = inb(*port_base + REG_CONFIG2);
 
-   irq = interrupt_table_14f[config_1.interrupt];
-   dma_channel = dma_channel_table_14f[config_1.dma_channel];
+   irq = interrupt_table[config_1.interrupt];
+   dma_channel = dma_channel_table[config_1.dma_channel];
    subversion = (in_byte & 0x0f);
 
+   /* Board detected, allocate its IRQ if not already done */
    if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
        (irq, u14_34f_interrupt_handler, SA_INTERRUPT, driver_name))) {
       printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
       return FALSE;
       }
 
-   if (subversion == U14F && request_dma(dma_channel, driver_name)) {
+   if (subversion == ISA && request_dma(dma_channel, driver_name)) {
       printk("%s: unable to allocate DMA channel %u, detaching.\n",
              name, dma_channel);
       free_irq(irq);
@@ -333,24 +337,20 @@ static inline int port_detect(ushort *port_base, unsigned int j,
    sh[j]->this_id = config_2.ha_scsi_id;
    sh[j]->can_queue = MAX_MAILBOXES;
    sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
-   sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
-   lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
 
 #if defined(DEBUG_DETECT)
+   {
+   unsigned char sys_mask, lcl_mask;
+
+   sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
+   lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
    printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask);
+   }
 #endif
 
    /* If BIOS is disabled, force enable interrupts */
    if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
 
-#if defined (DO_BUS_RESET)
-   lcl_mask = 0xc2;
-#else
-   lcl_mask = 0x82;
-#endif
-
-   outb(lcl_mask, sh[j]->io_port + REG_LCL_MASK);
-
    /* Register the I/O space that we use */
    snarf_region(sh[j]->io_port, REG_REGION);
 
@@ -362,7 +362,7 @@ static inline int port_detect(ushort *port_base, unsigned int j,
    HD(j)->board_number = j;
    irqlist[irq] = j;
 
-   if (HD(j)->subversion == U34F) {
+   if (HD(j)->subversion == ESA) {
       sh[j]->dma_channel = 0;
       sh[j]->unchecked_isa_dma = FALSE;
       sh[j]->sg_tablesize = U34F_MAX_SGLIST;
@@ -371,9 +371,9 @@ static inline int port_detect(ushort *port_base, unsigned int j,
       }
    else {
       sh[j]->dma_channel = dma_channel;
+      sh[j]->block = sh[j];
       sh[j]->unchecked_isa_dma = TRUE;
       sh[j]->sg_tablesize = U14F_MAX_SGLIST;
-      /*if (j > 0) sh[j]->sg_tablesize = 0;*/
       sh[j]->hostt->use_clustering = U14F_CLUSTERING;
       sprintf(BN(j), "U14F%d", j);
       disable_dma(dma_channel);
@@ -394,11 +394,11 @@ static inline int port_detect(ushort *port_base, unsigned int j,
 int u14_34f_detect (Scsi_Host_Template * tpnt) {
    unsigned int j = 0, k, flags;
 
-   ushort isa_io_port[] = {
+   ushort io_port[] = {
       0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140, 0x0
       };
 
-   ushort *port_base = isa_io_port;
+   ushort *port_base = io_port;
 
    save_flags(flags);
    cli();
@@ -412,21 +412,11 @@ int u14_34f_detect (Scsi_Host_Template * tpnt) {
 
    while (*port_base) {
 
-      if(j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+      if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
 
       port_base++;
       }
 
-#if defined (SINGLE_HOST_OPERATIONS)
-   /* Create a circular linked list among the detected boards. */
-   if (j > 1) {
-
-      for (k = 0; k < (j - 1); k++) sh[k]->block = sh[k + 1];
-
-      sh[j - 1]->block = sh[0];
-      }
-#endif
-
    restore_flags(flags);
    return j;
 }
@@ -437,7 +427,7 @@ static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
 
    sgpnt = (struct scatterlist *) SCpnt->request_buffer;
 
-   for(k = 0; k < SCpnt->use_sg; k++) {
+   for (k = 0; k < SCpnt->use_sg; k++) {
       cpp->sglist[k].address = (unsigned int) sgpnt[k].address;
       cpp->sglist[k].num_bytes = sgpnt[k].length;
       data_len += sgpnt[k].length;
@@ -463,7 +453,7 @@ int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
       starting from last_cp_used */
    i = HD(j)->last_cp_used + 1;
 
-   for(k = 0; k < sh[j]->can_queue; k++, i++) {
+   for (k = 0; k < sh[j]->can_queue; k++, i++) {
 
       if (i >= sh[j]->can_queue) i = 0;
 
index b36c935811411335694c67604dabf46e1cdcb160..c3311379293194f3418f1705121bcbcf91c50d90 100644 (file)
@@ -1,5 +1,5 @@
 /*
- *   u14-34f.h - used by low-level scsi driver for UltraStor 14F/34F
+ *   u14-34f.h - used by the low-level driver for UltraStor 14F/34F
  */
 #ifndef _U14_34F_H
 #define _U14_34F_H
@@ -10,7 +10,7 @@ int u14_34f_abort(Scsi_Cmnd *);
 int u14_34f_reset(Scsi_Cmnd *);
 int u14_34f_biosparam(Disk *, int, int *);
 
-#define U14_34F_VERSION "1.11.01"
+#define U14_34F_VERSION "1.12.04"
 
 #define ULTRASTOR_14_34F {                                            \
                 NULL,                                                 \
index 299a5172bbadba3b467acb7e3401856350cb20a4..5e1199c3357992171609b42ec355bcd4363ec846 100644 (file)
@@ -777,12 +777,7 @@ void brelse(struct buffer_head * buf)
 {
        if (!buf)
                return;
-/*
- * Hmm.. Leaving this in for now, as I'm a bit nervous about it..
- */
-#if 0
        wait_on_buffer(buf);
-#endif
 
        /* If dirty, mark the time this buffer should be written back */
        set_writetime(buf, 0);
index 89853271d56032e48b381b3ffc638ab00371c45e..b272ba11eac5cced6b39641b9e4b720109ca1b86 100644 (file)
 #include <linux/malloc.h>
 #include <linux/errno.h>
 
+#define MULTISESSION /* emoenke@gwdg.de */
+#ifdef MULTISESSION
+#include <linux/cdrom.h>
+#endif MULTISESSION
+
 #include <asm/system.h>
 #include <asm/segment.h>
 
@@ -145,6 +150,14 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
        unsigned int blocksize_bits;
        int high_sierra;
        int dev=s->s_dev;
+#ifdef MULTISESSION
+       int i;
+       unsigned int vol_desc_start;
+       unsigned int *p_vol_desc_start=&vol_desc_start;
+       struct inode inode_fake;
+       struct file file_fake;
+       extern struct file_operations * get_blkfops(unsigned int);
+#endif MULTISESSION
        struct iso_volume_descriptor *vdp;
        struct hs_volume_descriptor *hdp;
 
@@ -184,7 +197,27 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
 
        s->u.isofs_sb.s_high_sierra = high_sierra = 0; /* default is iso9660 */
 
+#ifdef MULTISESSION
+       /*
+        * look if the driver can tell the multi session redirection
+         * value; this allows to do the redirection if we are looking
+         * for the volume descriptor, and to avoid it during "raw" access.
+         */
+       vol_desc_start=0;
+       inode_fake.i_rdev=dev;
+       i=get_blkfops(MAJOR(dev))->ioctl(&inode_fake,
+                                        &file_fake,
+                                        CDROMMULTISESSION_SYS,
+                                        (unsigned long) p_vol_desc_start);
+       if (i!=0) vol_desc_start=0;
+#if 0
+       printk("isofs.inode: CDROMMULTISESSION_SYS rc=%d\n",i);
+       printk("isofs.inode: vol_desc_start = %d\n", vol_desc_start);
+#endif
+       for (iso_blknum = vol_desc_start+16; iso_blknum < vol_desc_start+100; iso_blknum++) {
+#else
        for (iso_blknum = 16; iso_blknum < 100; iso_blknum++) {
+#endif MULTISESSION
                if (!(bh = bread(dev, iso_blknum << (ISOFS_BLOCK_BITS-blocksize_bits), opt.blocksize))) {
                        s->s_dev=0;
                        printk("isofs_read_super: bread failed, dev 0x%x iso_blknum %d\n",
index 2540957a162a15fba7c4e6c7dd38d03f94b8f8f1..7d3848b62fa3e2fadc5efa838c34de0a876ebd08 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -147,6 +147,11 @@ asmlinkage int sys_utime(char * filename, struct utimbuf * times)
        }
        /* Don't worry, the checks are done in inode_change_ok() */
        if (times) {
+               error = verify_area(VERIFY_READ, times, sizeof(*times));
+               if (error) {
+                       iput(inode);
+                       return error;
+               }
                actime = get_fs_long((unsigned long *) &times->actime);
                modtime = get_fs_long((unsigned long *) &times->modtime);
                newattrs.ia_ctime = CURRENT_TIME;
index f1978011833dd89e312adaaa9293a52d83746d86..8458d40bc927a40994b2d9076fa60c697e2f9855 100644 (file)
@@ -247,9 +247,9 @@ static int get_cpuinfo(char * buffer)
           advance */
        char *yes="yes";
        char *no="no";
-       char *model[2][9]={{"DX","SX","DX/2","4","5","6",
+       char *model[2][9]={{"DX","SX","DX/2","4","SX/2","6",
                                "7","DX/4"},
-                       {"Pentium 60/66","Pentium 90","3",
+                       {"Pentium 60/66","Pentium 90/100","3",
                                "4","5","6","7","8"}};
        return sprintf(buffer,"cpu\t\t: %c86\n"
                              "model\t\t: %s\n"
index 9ead32a3e56390df10336d517804d9cc5c74da0b..8a9c044d6d11b61495f1f354e40168cdc7bf231d 100644 (file)
@@ -286,6 +286,7 @@ static struct super_block * read_super(dev_t dev,char *name,int flags,
        s->s_covered = NULL;
        s->s_rd_only = 0;
        s->s_dirt = 0;
+       s->s_type = type;
        return s;
 }
 
index 5fc8c8280478866b233887b006867917288ecf16..1f26e39f5d02a43368d8bb28029be57f37a63833 100644 (file)
@@ -21,17 +21,17 @@ typedef int ptrdiff_t;
  * header files exported to user space
  */
 
-typedef signed char __s8;
+typedef __signed__ char __s8;
 typedef unsigned char __u8;
 
-typedef signed short __s16;
+typedef __signed__ short __s16;
 typedef unsigned short __u16;
 
-typedef signed long __s32;
+typedef __signed__ long __s32;
 typedef unsigned long __u32;
 
 #ifndef __STRICT_ANSI__
-typedef signed long long __s64;
+typedef __signed__ long long __s64;
 typedef unsigned long long __u64;
 #endif
 
diff --git a/include/asm-sparc/vac-ops.h b/include/asm-sparc/vac-ops.h
new file mode 100644 (file)
index 0000000..14819bf
--- /dev/null
@@ -0,0 +1,68 @@
+/* vac-ops.h: Inline assembly routines to do operations on the Sparc
+              VAC (virtual address cache).
+
+   Copyright (C) 1994, David S. Miller (davem@caip.rutgers.edu)
+*/
+
+/* enable_vac() enables the virtual address cache. It returns 0 on
+   success, 1 on failure.
+*/
+
+extern __inline__ int enable_vac()
+{
+  int success;
+
+  __asm__ __volatile__("lduba [0x40000000] 0x2, %0\n\t"
+                      "or    %0, 0x10, %0\n\t"
+                      "stba  %0, [0x40000000] 0x2\n\t"
+                      "or    %g0, %g0, %0" : "=r" (success) : "0" (success));
+  return success;
+}
+
+/* disable_vac() disables the virtual address cache. It returns 0 on
+   success, 1 on failure.
+*/
+
+extern __inline__ int disable_vac()
+{
+  int success;
+
+  __asm__ __volatile__("lduba [0x40000000] 0x2, %0\n\t"
+                       "xor   %0, 0x10, %0\n\t"
+                       "stba  %0, [0x40000000] 0x2\n\t"
+                       "or    %g0, %g0, %0" : "=r" (success) : "0" (success));
+  return success;
+}
+
+/* Various one-shot VAC entry flushes on the Sparc */
+
+extern __inline__ void hw_flush_vac_context_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0x7" : : "r" (addr));
+}
+
+extern __inline__ void sw_flush_vac_context_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0xe" : : "r" (addr));
+}
+
+extern __inline__ void hw_flush_vac_segment_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0x5" : : "r" (addr));
+}
+
+extern __inline__ void sw_flush_vac_segment_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0], 0xc" : : "r" (addr));
+}
+
+extern __inline__ void hw_flush_vac_page_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0x6" : : "r" (addr));
+}
+
+extern __inline__ void sw_flush_vac_page_entry(char* addr)
+{
+  __asm__ __volatile__("sta 0, [%0] 0xd" : : "r" (addr));
+}
index f7e7d7b5c980cbd16f591ff7d0944528a7885b97..78f6f19156fc79da969a053742af4b1ce002a4a7 100644 (file)
@@ -334,6 +334,28 @@ struct cdrom_read_audio
          u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
        };
 
+/*
+ * preliminary extension for obtaining multi session info
+ * (still may change if other drivers will use it, too):
+ * this has to be the "arg" of the CDROMMULTISESSION ioctl.
+ * The returned "addr" is valid only if "xa_flag" is true.
+ */
+struct cdrom_multisession
+       {
+         union
+           {
+             struct                    
+               {
+                 u_char minute;
+                 u_char second;
+                 u_char frame;
+               } msf;
+             int lba;
+           } addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
+         u_char xa_flag; /* 1: "is XA disk" */
+         u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+       };
+
 #ifdef FIVETWELVE
 #define        CDROM_MODE1_SIZE        512
 #else
@@ -387,6 +409,14 @@ struct cdrom_read_audio
  * (still may change if other drivers will use it, too):
  */
 #define        CDROMEJECT_SW           0x530f          /* arg: 0 or 1 */
+/*
+ * preliminary extension for obtaining the start-of-last-session
+ * address of multi session disks
+ * currently used by sbpcd.c
+ * (still may change if more drivers will use it).
+ */
+#define        CDROMMULTISESSION_SYS   0x5310 /* internal use only (linux/fs/isofs/inode.c) */
+#define        CDROMMULTISESSION       0x5311 /* "user" interface (arg has to be a "cdrom_multisession" struct) */
 
 #endif  _LINUX_CDROM_H
-
diff --git a/include/linux/cyclades.h b/include/linux/cyclades.h
new file mode 100644 (file)
index 0000000..218f68e
--- /dev/null
@@ -0,0 +1,236 @@
+struct cyclades_card {
+    int base_addr;
+    int irq;
+    int num_chips; /* implies card type, 0 if card is absent */
+    int first_line; /* line number of first channel of first chip on card */
+};
+
+struct cyclades_chip {
+  int filler;
+};
+
+/*
+ * This is our internal structure for each serial port's state.
+ * 
+ * Many fields are paralleled by the structure used by the serial_struct
+ * structure.
+ *
+ * For definitions of the flags field, see tty.h
+ */
+
+struct cyclades_port {
+       int                     magic;
+       int                     type;
+       int                     card;
+       int                     line;
+       int                     flags;          /* defined in tty.h */
+       struct tty_struct       *tty;
+       int                     read_status_mask;
+       int                     timeout;
+       int                     xmit_fifo_size;
+       int                     cor1,cor2,cor3,cor4,cor5;
+       int                     tbpr,tco,rbpr,rco;
+       int                     ignore_status_mask;
+       int                     close_delay;
+       int                     IER;    /* Interrupt Enable Register */
+       int                     event;
+       unsigned long           last_active;
+       int                     count;  /* # of fd on device */
+       int                     x_char; /* to be pushed out ASAP */
+       int                     x_break;
+       int                     blocked_open; /* # of blocked opens */
+       long                    session; /* Session of opening process */
+       long                    pgrp; /* pgrp of opening process */
+       unsigned char           *xmit_buf;
+       int                     xmit_head;
+       int                     xmit_tail;
+       int                     xmit_cnt;
+       struct tq_struct        tqueue;
+       struct termios          normal_termios;
+       struct termios          callout_termios;
+       struct wait_queue       *open_wait;
+       struct wait_queue       *close_wait;
+};
+
+#define CYCLADES_MAGIC  0x4359
+/*
+ * Events are used to schedule things to happen at timer-interrupt
+ * time, instead of at cy interrupt time.
+ */
+#define Cy_EVENT_READ_PROCESS  0
+#define Cy_EVENT_WRITE_WAKEUP  1
+#define Cy_EVENT_HANGUP                2
+#define Cy_EVENT_BREAK         3
+#define Cy_EVENT_OPEN_WAKEUP   4
+
+
+
+#define CyMaxChipsPerCard 4
+
+/**** CD1400 registers ****/
+
+#define CyRegSize  0x0400
+#define Cy_HwReset 0x1400
+#define Cy_ClrIntr 0x1800
+
+/* Global Registers */
+
+#define CyGFRCR                (0x40*2)
+#define      CyRevE            (44)
+#define CyCAR          (0x68*2)
+#define      CyCHAN_0          (0x00)
+#define      CyCHAN_1          (0x01)
+#define      CyCHAN_2          (0x02)
+#define      CyCHAN_3          (0x03)
+#define CyGCR          (0x4B*2)
+#define      CyCH0_SERIAL      (0x00)
+#define      CyCH0_PARALLEL    (0x80)
+#define CySVRR         (0x67*2)
+#define      CySRModem         (0x04)
+#define      CySRTransmit      (0x02)
+#define      CySRReceive       (0x01)
+#define CyRICR         (0x44*2)
+#define CyTICR         (0x45*2)
+#define CyMICR         (0x46*2)
+#define      CyICR0            (0x00)
+#define      CyICR1            (0x01)
+#define      CyICR2            (0x02)
+#define      CyICR3            (0x03)
+#define CyRIR          (0x6B*2)
+#define CyTIR          (0x6A*2)
+#define CyMIR          (0x69*2)
+#define      CyIRDirEq         (0x80)
+#define      CyIRBusy          (0x40)
+#define      CyIRUnfair                (0x20)
+#define      CyIRContext       (0x1C)
+#define      CyIRChannel       (0x03)
+#define CyPPR          (0x7E*2)
+#define      CyCLOCK_20_1MS    (0x27)
+#define      CyCLOCK_25_1MS    (0x31)
+
+/* Virtual Registers */
+
+#define CyRIVR         (0x43*2)
+#define CyTIVR         (0x42*2)
+#define CyMIVR         (0x41*2)
+#define      CyIVRMask (0x07)
+#define      CyIVRRxEx (0x07)
+#define      CyIVRRxOK (0x03)
+#define      CyIVRTxOK (0x02)
+#define      CyIVRMdmOK (0x01)
+#define CyTDR          (0x63*2)
+#define CyRDSR         (0x62*2)
+#define      CyTIMEOUT         (0x80)
+#define      CySPECHAR         (0x70)
+#define      CyBREAK           (0x08)
+#define      CyPARITY          (0x04)
+#define      CyFRAME           (0x02)
+#define      CyOVERRUN         (0x01)
+#define CyMISR         (0x4C*2)
+/* see CyMCOR_ and CyMSVR_ for bits*/
+#define CyEOSRR                (0x60*2)
+
+/* Channel Registers */
+
+#define CyLIVR         (0x18*2)
+#define      CyMscsr           (0x01)
+#define      CyTdsr            (0x02)
+#define      CyRgdsr           (0x03)
+#define      CyRedsr           (0x07)
+#define CyCCR          (0x05*2)
+/* Format 1 */
+#define      CyCHAN_RESET      (0x80)
+#define      CyCHIP_RESET      (0x81)
+#define      CyFlushTransFIFO  (0x82)
+/* Format 2 */
+#define      CyCOR_CHANGE      (0x40)
+#define      CyCOR1ch          (0x02)
+#define      CyCOR2ch          (0x04)
+#define      CyCOR3ch          (0x08)
+/* Format 3 */
+#define      CySEND_SPEC_1     (0x21)
+#define      CySEND_SPEC_2     (0x22)
+#define      CySEND_SPEC_3     (0x23)
+#define      CySEND_SPEC_4     (0x24)
+/* Format 4 */
+#define      CyCHAN_CTL                (0x10)
+#define      CyDIS_RCVR                (0x01)
+#define      CyENB_RCVR                (0x02)
+#define      CyDIS_XMTR                (0x04)
+#define      CyENB_XMTR                (0x08)
+#define CySRER         (0x06*2)
+#define      CyMdmCh           (0x80)
+#define      CyRxData          (0x10)
+#define      CyTxRdy           (0x04)
+#define      CyTxMpty          (0x02)
+#define      CyNNDT            (0x01)
+#define CyCOR1         (0x08*2)
+#define      CyPARITY_NONE     (0x00)
+#define      CyPARITY_0                (0x20)
+#define      CyPARITY_1                (0xA0)
+#define      CyPARITY_E                (0x40)
+#define      CyPARITY_O                (0xC0)
+#define      Cy_1_STOP         (0x00)
+#define      Cy_1_5_STOP       (0x04)
+#define      Cy_2_STOP         (0x08)
+#define      Cy_5_BITS         (0x00)
+#define      Cy_6_BITS         (0x01)
+#define      Cy_7_BITS         (0x02)
+#define      Cy_8_BITS         (0x03)
+#define CyCOR2         (0x09*2)
+#define      CyIXM             (0x80)
+#define      CyTxIBE           (0x40)
+#define      CyETC             (0x20)
+#define      CyAUTO_TXFL       (0x60)
+#define      CyLLM             (0x10)
+#define      CyRLM             (0x08)
+#define      CyRtsAO           (0x04)
+#define      CyCtsAE           (0x02)
+#define      CyDsrAE           (0x01)
+#define CyCOR3         (0x0A*2)
+#define      CySPL_CH_DRANGE   (0x80)  /* special character detect range */
+#define      CySPL_CH_DET1     (0x40)  /* enable special character detection
+                                                               on SCHR4-SCHR3 */
+#define      CyFL_CTRL_TRNSP   (0x20)  /* Flow Control Transparency */
+#define      CySPL_CH_DET2     (0x10)  /* Enable special character detection
+                                                               on SCHR2-SCHR1 */
+#define      CyREC_FIFO                (0x0F)  /* Receive FIFO threshold */
+#define CyCOR4         (0x1E*2)
+#define CyCOR5         (0x1F*2)
+#define CyCCSR         (0x0B*2)
+#define      CyRxEN            (0x80)
+#define      CyRxFloff         (0x40)
+#define      CyRxFlon          (0x20)
+#define      CyTxEN            (0x08)
+#define      CyTxFloff         (0x04)
+#define      CyTxFlon          (0x02)
+#define CyRDCR         (0x0E*2)
+#define CySCHR1                (0x1A*2)
+#define CySCHR2        (0x1B*2)
+#define CySCHR3                (0x1C*2)
+#define CySCHR4                (0x1D*2)
+#define CySCRL         (0x22*2)
+#define CySCRH         (0x23*2)
+#define CyLNC          (0x24*2)
+#define CyMCOR1        (0x15*2)
+#define CyMCOR2                (0x16*2)
+#define CyRTPR         (0x21*2)
+#define CyMSVR1                (0x6C*2)
+#define CyMSVR2                (0x6D*2)
+#define      CyDSR             (0x80)
+#define      CyCTS             (0x40)
+#define      CyRI              (0x20)
+#define      CyDCD             (0x10)
+#define      CyDTR              (0x02)
+#define      CyRTS              (0x01)
+#define CyPVSR         (0x6F*2)
+#define CyRBPR         (0x78*2)
+#define CyRCOR         (0x7C*2)
+#define CyTBPR         (0x72*2)
+#define CyTCOR         (0x76*2)
+
+/* max number of chars in the FIFO */
+
+#define CyMAX_CHAR_FIFO        12
+
+/***************************************************************************/
index a50133004734f2686b944db11d6a1812bf89e46e..48bbc7adfb0ccda93f1c8ad638c3de8ad20586d0 100644 (file)
@@ -301,6 +301,7 @@ struct super_block {
        unsigned char s_lock;
        unsigned char s_rd_only;
        unsigned char s_dirt;
+       struct file_system_type *s_type;
        struct super_operations *s_op;
        unsigned long s_flags;
        unsigned long s_magic;
index 2ccaec523c5f8b1637252e8fae0d1ae89531ab7b..4fd8585c45116c9b6c73b04a9a9773d1f4bbe9d7 100644 (file)
@@ -21,7 +21,8 @@ enum {
        SERIAL_BH,
        NET_BH,
        IMMEDIATE_BH,
-       KEYBOARD_BH
+       KEYBOARD_BH,
+       CYCLADES_BH
 };
 
 extern inline void mark_bh(int nr)
index 1095175ae202199b28f095caf406f2c9c9df4c69..4b4e65e7c1f85804c8dd24dc277466ca6c700543 100644 (file)
@@ -42,8 +42,8 @@
  * then I can include better information with the next release.
  */
 #if !(SBPCD_ISSUE-1) /* first (or if you have only one) interface board: */
-#define CDROM_PORT 0x0230
-#define SBPRO     1
+#define CDROM_PORT 0x0340
+#define SBPRO     0
 #endif
 
 /*
@@ -54,7 +54,7 @@
  * Example: #define SOUND_BASE 0x220 enables the sound card's CD channels
  *          #define SOUND_BASE 0     leaves the soundcard untouched
  */
-#define SOUND_BASE 0
+#define SOUND_BASE 0x220
 
 /* ignore the rest if you have only one interface board & driver */
 
@@ -67,8 +67,8 @@
 #define SBPRO     0
 #endif
 #if !(SBPCD_ISSUE-4) /* fourth interface board: */
-#define CDROM_PORT 0x0340
-#define SBPRO     0
+#define CDROM_PORT 0x0230
+#define SBPRO     1
 #endif
 
 /*==========================================================================*/
 #define _LINUX_SBPCD_H
 
 /*==========================================================================*/
+/*==========================================================================*/
+/*
+ * DDI interface definitions
+ * "invented" by Fred N. van Kempen..
+ */
+#define DDIOCSDBG      0x9000
+#define DPRINTF(x)     sbpcd_dprintf x
+
 /*==========================================================================*/
 /*
  * Debug output levels
 #define DBG_SQ                 24      /* dump SubQ frame */
 #define DBG_AUD                25      /* "read audio" debugging */
 #define DBG_SEQ                26      /* Sequoia interface configuration trace */
-#define DBG_000                27      /* unnecessary information */
+#define DBG_LCS                27      /* Longshine LCS-7260 debugging trace */
+#define DBG_TEA                28      /* TEAC CD-55A debugging trace */
+#define DBG_000                29      /* unnecessary information */
 
 /*==========================================================================*/
 /*==========================================================================*/
 #define p_check 0x10
 #define p_busy_new 0x08
 #define p_door_locked 0x04
-#define p_bit_1 0x02
+#define p_bit_1 0x02 /* hopefully unused now */
+#define p_lcs_door_locked 0x02 /* new use of old bit */
 #define p_disk_ok 0x01
+#define p_lcs_door_closed 0x01 /* new use of old bit */
 /*
  * "old" drives status result bits:
  */
index 1877f42b29673fa9e098d5d89abd435616e02a8e..aff70148b9432582cbdece27a90e5c80426c3b77 100644 (file)
@@ -33,7 +33,8 @@ struct serial_struct {
 #define PORT_16450     2
 #define PORT_16550     3
 #define PORT_16550A    4
-#define PORT_MAX       4
+#define PORT_CIRRUS     5
+#define PORT_MAX       5
 
 /*
  * Definitions for async_struct (and serial_struct) flags field
index 545ffa1b97aa739c9509da9d39e8917b3479dea1..7b0c9830ed23b2795610179108de3d8940517e77 100644 (file)
@@ -542,10 +542,12 @@ asmlinkage void start_kernel(void)
        memory_start = console_init(memory_start,memory_end);
        memory_start = bios32_init(memory_start,memory_end);
        memory_start = kmalloc_init(memory_start,memory_end);
+       sti();
+       calibrate_delay();
+       cli();
        memory_start = chr_dev_init(memory_start,memory_end);
        memory_start = blk_dev_init(memory_start,memory_end);
        sti();
-       calibrate_delay();
 #ifdef CONFIG_SCSI
        memory_start = scsi_dev_init(memory_start,memory_end);
 #endif
index 2de16db53ac8169971f0c6144fd0d8042801e6df..45c662e0e603948515d5ed02168e496976ea616f 100644 (file)
@@ -108,7 +108,7 @@ asmlinkage void do_bottom_half(void)
        }
        return;
 bad_bh:
-       printk ("irq.c:bad bottom half entry\n");
+       printk ("irq.c:bad bottom half entry %08lx\n", mask);
 }
 
 /*
index 5353ba8bf95409716d53b8206e3961853e58dfab..cb8aef93857b0f9067c8153b3295d48a680c8fae 100644 (file)
@@ -3291,10 +3291,15 @@ static int tcp_fin(struct sk_buff *skb, struct sock *sk, struct tcphdr *th)
                         * happens, we must ack the received FIN and
                         * enter the CLOSING state.
                         *
-                        * XXX timeout not set properly
+                        * This causes a WRITE timeout, which will either
+                        * move on to TIME_WAIT when we timeout, or resend
+                        * the FIN properly (maybe we get rid of that annoying
+                        * FIN lost hang). The TIME_WRITE code is already correct
+                        * for handling this timeout.
                         */
 
-                       reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
+                       if(sk->timeout != TIME_WRITE)
+                               reset_timer(sk, TIME_WRITE, sk->rto);
                        tcp_set_state(sk,TCP_CLOSING);
                        break;
                case TCP_FIN_WAIT2:
@@ -4008,9 +4013,11 @@ ignore_it:
 
 static int tcp_std_reset(struct sock *sk, struct sk_buff *skb)
 {
-       sk->zapped=1;
+       sk->zapped = 1;
        sk->err = ECONNRESET;
-       if(sk->state==TCP_CLOSE_WAIT)
+       if (sk->state == TCP_SYN_SENT)
+               sk->err = ECONNREFUSED;
+       if (sk->state == TCP_CLOSE_WAIT)
                sk->err = EPIPE;
 #ifdef TCP_DO_RFC1337          
        /*
@@ -4320,14 +4327,18 @@ int tcp_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
                if(sk->debug)
                        printk("Doing a BSD time wait\n");
                tcp_statistics.TcpEstabResets++;           
+               sk->rmem_alloc -= skb->mem_len;
+               skb->sk = NULL;
                sk->err=ECONNRESET;
                tcp_set_state(sk, TCP_CLOSE);
                sk->shutdown = SHUTDOWN_MASK;
                release_sock(sk);
-               sk=get_sock(&tcp_prot, th->source, daddr, th->dest, saddr);
+               sk=get_sock(&tcp_prot, th->dest, saddr, th->source, daddr);
                if(sk && sk->state==TCP_LISTEN)
                {
                        sk->inuse=1;
+                       skb->sk = sk;
+                       sk->rmem_alloc += skb->mem_len;
                        tcp_conn_request(sk, skb, daddr, saddr,opt, dev,seq+128000);
                        release_sock(sk);
                        return 0;