]> git.neil.brown.name Git - history.git/commitdiff
Import 0.99.14x 0.99.14x
authorLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:20 +0000 (15:09 -0500)
committerLinus Torvalds <torvalds@linuxfoundation.org>
Fri, 23 Nov 2007 20:09:20 +0000 (15:09 -0500)
24 files changed:
Makefile
config.in
drivers/block/Makefile
drivers/block/blk.h
drivers/block/ll_rw_blk.c
drivers/block/sbpcd.c [new file with mode: 0644]
drivers/char/console.c
drivers/char/tty_ioctl.c
drivers/net/CONFIG
drivers/net/README.8390 [deleted file]
drivers/net/ne.c
fs/isofs/inode.c
fs/open.c
include/linux/major.h
include/linux/sbpcd.h [new file with mode: 0644]
include/linux/timer.h
include/linux/tty.h
init/main.c
kernel/ksyms.S
net/inet/ip.c
net/inet/sock.c
net/inet/sock.h
net/inet/tcp.c
net/inet/timer.c

index 564ea92da2b8436906cbd1bd9e10b12e025f1868..85ac1c87a9e6e1f16b1eb26fdd06c82abfe54966 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,6 @@
 VERSION = 0.99
 PATCHLEVEL = 14
-ALPHA = w
+ALPHA = x
 
 all:   Version zImage
 
index 1e7dc95b567284d3cbf1bea83e69e6832f472ca3..78672105586b92029c2111d6446e68b25eca3ed0 100644 (file)
--- a/config.in
+++ b/config.in
@@ -87,6 +87,7 @@ fi
 *
 bool 'Sony CDU31A CDROM driver support' CONFIG_CDU31A n
 bool 'Mitsumi CDROM driver support' CONFIG_MCD n
+bool 'Matsushita/Panasonic CDROM driver support' CONFIG_SBPCD n
 *
 * Filesystems
 *
index 253de65859d3d3a7b1bf32ec6f1761fe500d87fd..bc0dfc55f31d7abb018b6fae7ea880c26fb110a8 100644 (file)
@@ -34,6 +34,14 @@ OBJS := $(OBJS) mcd.o
 SRCS := $(SRCS) mcd.c
 endif
 
+ifdef CONFIG_SBPCD
+OBJS := $(OBJS) sbpcd.o
+SRCS := $(SRCS) sbpcd.c
+ifdef PATCHLEVEL
+CFLAGS := $(CFLAGS) -DPATCHLEVEL=$(PATCHLEVEL) 
+endif
+endif #CONFIG_SBPCD
+
 ifdef CONFIG_BLK_DEV_HD
 OBJS := $(OBJS) hd.o
 SRCS := $(SRCS) hd.c
index 970ba4d17ecb53e9aa065a3fb0b6b1dbc61e701a..faa5adfc527fd504767a1f6e30a561b6a134bc40 100644 (file)
@@ -189,6 +189,14 @@ static void floppy_off(unsigned int nr);
 #define DEVICE_ON(device)
 #define DEVICE_OFF(device)
 
+#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM"
+#define DEVICE_REQUEST do_sbpcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
 #else
 
 #error "unknown blk device"
index 1473621209761199199a6bf841e52a49c9f1df8d..7ea4f88ccadfe50014c8d8a911662bf33d46ce23 100644 (file)
 
 #include "blk.h"
 
+#ifdef CONFIG_SBPCD
+extern u_long sbpcd_init(u_long, u_long);
+#endif CONFIG_SBPCD
+
 /*
  * The request-struct contains all necessary data
  * to load a nr of sectors into memory
@@ -485,6 +489,9 @@ long blk_dev_init(long mem_start, long mem_end)
 #ifdef CONFIG_MCD
        mem_start = mcd_init(mem_start,mem_end);
 #endif
+#ifdef CONFIG_SBPCD
+       mem_start = sbpcd_init(mem_start, mem_end);
+#endif CONFIG_SBPCD
        if (ramdisk_size)
                mem_start += rd_init(mem_start, ramdisk_size*1024);
        return mem_start;
diff --git a/drivers/block/sbpcd.c b/drivers/block/sbpcd.c
new file mode 100644 (file)
index 0000000..43df7eb
--- /dev/null
@@ -0,0 +1,2985 @@
+/*
+ *  sbpcd.c   CD-ROM device driver for the whole family of IDE-style
+ *            Kotobuki/Matsushita/Panasonic CR-5xx drives for
+ *            SoundBlaster ("Pro" or "16 ASP" or compatible) cards
+ *            and for "no-sound" interfaces like Lasermate and the
+ *            Panasonic CI-101P.
+ *
+ *  NOTE:     This is release 1.0.
+ *            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. 
+ *  
+ *
+ *  VERSION HISTORY
+ *
+ *  0.1  initial release, April/May 93, after mcd.c
+ *  
+ *  0.2  the "repeat:"-loop in do_sbpcd_request did not check for
+ *       end-of-request_queue (resulting in kernel panic).
+ *       Flow control seems stable, but throughput is not better.  
+ *
+ *  0.3  interrupt locking totally eliminated (maybe "inb" and "outb"
+ *       are still locking) - 0.2 made keyboard-type-ahead losses.
+ *       check_sbpcd_media_change added (to use by isofs/inode.c)
+ *       - but it detects almost nothing.
+ *
+ *  0.4  use MAJOR 25 definitely.
+ *       Almost total re-design to support double-speed drives and
+ *       "naked" (no sound) interface cards.
+ *       Flow control should be exact now (tell me if not).
+ *       Don't occupy the SbPro IRQ line (not needed either); will
+ *       live together with Hannu Savolainen's sndkit now.
+ *      Speeded up data transfer to 150 kB/sec, with help from Kai
+ *       Makisara, the "provider" of the "mt" tape utility.
+ *       Give "SpinUp" command if necessary.
+ *       First steps to support up to 4 drives (but currently only one).
+ *       Implemented audio capabilities - workman should work, xcdplayer
+ *       gives some problems.
+ *       This version is still consuming too much CPU time, and
+ *       sleeping still has to be worked on.
+ *       During "long" implied seeks, it seems possible that a 
+ *       ReadStatus command gets ignored. That gives the message
+ *       "ResponseStatus timed out" (happens about 6 times here during
+ *       a "ls -alR" of the YGGDRASIL LGX-Beta CD). Such a case is
+ *       handled without data error, but it should get done better.
+ *
+ *  0.5  Free CPU during waits (again with help from Kai Makisara).
+ *       Made it work together with the LILO/kernel setup standard.
+ *       Included auto-probing code, as suggested by YGGDRASIL.
+ *       Formal redesign to add DDI debugging.
+ *       There are still flaws in IOCTL (workman with double speed drive).
+ *
+ *  1.0  Added support for all drive ids (0...3, no longer only 0)
+ *       and up to 4 drives on one controller.
+ *       Added "#define MANY_SESSION" for "old" multi session CDs.
+ *
+ *  1.1  Do SpinUp for new drives, too.
+ *       Revised for clean compile under "old" kernels (pl9).
+ *
+ *     special thanks to Kai Makisara (kai.makisara@vtt.fi) for his fine
+ *     elaborated speed-up experiments (and the fabulous results!), for
+ *     the "push" towards load-free wait loops, and for the extensive mail
+ *     thread which brought additional hints and bug fixes.
+ * 
+ *
+ *   Copyright (C) 1993, 1994  Eberhard Moenkeberg <emoenke@gwdg.de>
+ *                         or <eberhard_moenkeberg@rollo.central.de>
+ *
+ *                  The FTP-home of this driver is 
+ *                  ftp.gwdg.de:/pub/linux/cdrom/drivers/sbpcd/.
+ *                  I will serve tsx-11.mit.edu, sunsite.unc.edu and
+ *                  ftp.funet.fi, too.
+ *
+ *
+ *                  If you change this software, you should mail a .diff
+ *                  file with some description lines to emoenke.gwdg.de.
+ *                  I want to know about it.
+ *
+ *                  If you are the editor of a Linux CD, you should
+ *                  add sbpcd.c into your boot floppy kernel and send
+ *                  me one of your CDs for free.
+ *
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2, or (at your option)
+ *   any later version.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   (for example /usr/src/linux/COPYING); if not, write to the Free
+ *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+#ifndef PATCHLEVEL
+#define PATCHLEVEL 9
+#endif
+
+#include <linux/config.h>
+#include <linux/errno.h>
+
+#if SBPCD_USE_IRQ
+#include <linux/signal.h>
+#endif SBPCD_USE_IRQ
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/cdrom.h>
+#include <linux/ioport.h>
+#include <linux/sbpcd.h>
+
+#if PATCHLEVEL>13
+#include <linux/ddi.h>
+#include <linux/major.h> 
+#else
+#define DDIOCSDBG 0x9000
+#define MATSUSHITA_CDROM_MAJOR 25  
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <stdarg.h>
+
+#define MAJOR_NR MATSUSHITA_CDROM_MAJOR
+#include "blk.h"
+
+#define VERSION "1.1"
+
+#define SBPCD_DEBUG
+
+#ifndef CONFIG_SBPCD
+#error "SBPCD: \"make config\" again. Enable Matsushita/Panasonic CDROM support"
+#endif
+
+#ifndef CONFIG_ISO9660_FS
+#error "SBPCD: \"make config\" again. File system iso9660 is necessary."
+#endif
+
+/*
+ * still testing around...
+ */
+#define MANY_SESSION 0
+#define CDMKE
+#undef  FUTURE
+#define WORKMAN 1 /* some testing stuff to make it better */
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * auto-probing address list
+ * inspired by Adam J. Richter from Yggdrasil
+ *
+ * still not good enough - can cause a hang.
+ *   example: a NE 2000 ehernet card at 300 will cause a hang probing 310.
+ * if that happens, reboot and use the LILO (kernel) command line.
+ * the conflicting possible ethernet card addresses get probed last - to
+ * minimize the hang possibilities. 
+ *
+ * The SB Pro addresses get "mirrored" at 0x6xx - to avoid a type error,
+ * the 0x2xx-addresses must get checked before 0x6xx.
+ *
+ * what are other cards' default and range ???
+ * what about ESCOM PowerSound???
+ * what about HighScreen???
+ */
+static int autoprobe[] = 
+{
+  CDROM_PORT, SBPRO, /* probe with user's setup first */
+  0x230, 1, /* Soundblaster Pro and 16 (default) */
+  0x300, 0, /* CI-101P (default), Galaxy (default), Reveal (one default) */
+  0x250, 1, /* OmniCD default, Soundblaster Pro and 16 */
+  0x260, 1, /* OmniCD */
+  0x320, 0, /* Lasermate, CI-101P, Galaxy, Reveal (other default) */
+  0x340, 0, /* Lasermate, CI-101P */
+  0x360, 0, /* Lasermate, CI-101P */
+  0x270, 1, /* Soundblaster 16 */
+  0x630, 0, /* "sound card #9" (default) */
+  0x650, 0, /* "sound card #9" */
+  0x670, 0, /* "sound card #9" */
+  0x690, 0, /* "sound card #9" */
+  0x330, 0, /* Lasermate, CI-101P */
+  0x350, 0, /* Lasermate, CI-101P */
+  0x370, 0, /* Lasermate, CI-101P */
+  0x290, 1, /* Soundblaster 16 */
+  0x310, 0, /* Lasermate, CI-101P */
+};
+
+#define NUM_AUTOPROBE  (sizeof(autoprobe) / sizeof(int))
+
+
+/*==========================================================================*/
+/*
+ * the forward references:
+ */
+static void sbp_read_cmd(void);
+static int  sbp_data(void);
+
+/*==========================================================================*/
+
+/*
+ * pattern for printk selection:
+ *
+ * (1<<DBG_INF)  necessary information
+ * (1<<DBG_IRQ)  interrupt trace
+ * (1<<DBG_REA)  "read" status trace
+ * (1<<DBG_CHK)  "media check" trace
+ * (1<<DBG_TIM)  datarate timer test
+ * (1<<DBG_INI)  initialization trace
+ * (1<<DBG_TOC)  tell TocEntry values
+ * (1<<DBG_IOC)  ioctl trace
+ * (1<<DBG_STA)  "ResponseStatus" trace
+ * (1<<DBG_ERR)  "xx_ReadError" trace
+ * (1<<DBG_CMD)  "cmd_out" trace
+ * (1<<DBG_WRN)  give explanation before auto-probing
+ * (1<<DBG_MUL)  multi session code test
+ * (1<<DBG_ID)   "drive_id != 0" test code
+ * (1<<DBG_IOX)  some special information
+ * (1<<DBG_DID)  drive ID test
+ * (1<<DBG_RES)  drive reset info
+ * (1<<DBG_SPI)  SpinUp test info
+ * (1<<DBG_000)  unnecessary information
+ */
+#if 1
+static int sbpcd_debug =  (1<<DBG_INF) | (1<<DBG_WRN);
+#else
+static int sbpcd_debug =  (1<<DBG_INF) |
+                          (1<<DBG_TOC) |
+                          (1<<DBG_IOX);
+#endif
+static int sbpcd_ioaddr = CDROM_PORT;  /* default I/O base address */
+static int sbpro_type = SBPRO;
+static int CDo_command, CDo_reset;
+static int CDo_sel_d_i, CDo_enable;
+static int CDi_info, CDi_status, CDi_data;
+static int MIXER_addr, MIXER_data;
+static struct cdrom_msf msf;
+static struct cdrom_ti ti;
+static struct cdrom_tochdr tochdr;
+static struct cdrom_tocentry tocentry;
+static struct cdrom_subchnl SC;
+static struct cdrom_volctrl volctrl;
+char *str_sb = "SoundBlaster";
+char *str_lm = "LaserMate";
+char *type;
+
+/*==========================================================================*/
+
+#if FUTURE
+static struct wait_queue *sbp_waitq = NULL;
+#endif FUTURE
+
+/*==========================================================================*/
+
+#define SBP_BUFFER_FRAMES 4 /* driver's own read_ahead */
+
+/*==========================================================================*/
+
+static u_char drive_family[]="CR-5";
+static u_char drive_vendor[]="MATSHITA";
+
+static u_int response_count=0;
+static u_int flags_cmd_out;
+static u_char cmd_type=0;
+static u_char drvcmd[7];
+static u_char infobuf[20];
+
+static u_char timed_out=0;
+static u_int datarate= 1000000;
+static u_int maxtim16=16000000;
+static u_int maxtim04= 4000000;
+static u_int maxtim02= 2000000;
+static u_int maxtim_8=   30000;
+#if MANY_SESSION
+static u_int maxtim_data= 9000;
+#else
+static u_int maxtim_data= 3000;
+#endif MANY_SESSION
+
+/*==========================================================================*/
+
+static int ndrives=0;
+static u_char drv_pattern[4]={ 0x80, 0x80, 0x80, 0x80 }; /* auto speed */
+/*  /X:... drv_pattern[0] |= (sax_n1|sax_n2);         */
+/*  /A:... for (i=0;i<4;i++) drv_pattern[i] |= sax_a; */
+/*  /N:... ndrives=i-'0';                             */
+
+/*==========================================================================*/
+/*
+ * drive space begins here (needed separate for each unit) 
+ */
+static int d=0; /* DS index: drive number */
+
+static struct {
+  char drv_minor; /* minor number or -1 */
+
+  char drive_model[4];
+  char firmware_version[4];
+  u_char *sbp_buf; /* Pointer to internal data buffer,
+                           space allocated during sbpcd_init() */
+  int sbp_first_frame;  /* First frame in buffer */
+  int sbp_last_frame;   /* Last frame in buffer  */
+  int sbp_read_frames;   /* Number of frames being read to buffer */
+  int sbp_current;       /* Frame being currently read */
+
+  u_char drv_type;
+  u_char drv_options;
+  u_char status_byte;
+  u_char diskstate_flags;
+  u_char sense_byte;
+  
+  u_char CD_changed;
+  
+  u_char error_byte;
+  
+  u_char f_multisession;
+  u_int lba_multi;
+  
+  u_char audio_state;
+  u_int pos_audio_start;
+  u_int pos_audio_end;
+  char vol_chan0;
+  u_char vol_ctrl0;
+  char vol_chan1;
+  u_char vol_ctrl1;
+#if 000
+  char vol_chan2;
+  u_char vol_ctrl2;
+  char vol_chan3;
+  u_char vol_ctrl3;
+#endif 000
+  
+  u_char SubQ_audio;
+  u_char SubQ_ctl_adr;
+  u_char SubQ_trk;
+  u_char SubQ_pnt_idx;
+  u_int SubQ_run_tot;
+  u_int SubQ_run_trk;
+  u_char SubQ_whatisthis;
+  
+  u_char UPC_ctl_adr;
+  u_char UPC_buf[7];
+  
+  int CDsize_blk;
+  int frame_size;
+  int CDsize_frm;
+  
+  u_char xa_byte; /* 0x20: XA capabilities */
+  u_char n_first_track; /* binary */
+  u_char n_last_track; /* binary (not bcd), 0x01...0x63 */
+  u_int size_msf; /* time of whole CD, position of LeadOut track */
+  u_int size_blk;
+  
+  u_char TocEnt_nixbyte; /* em */
+  u_char TocEnt_ctl_adr;
+  u_char TocEnt_number;
+  u_char TocEnt_format; /* em */
+  u_int TocEnt_address;
+  u_char ored_ctl_adr; /* to detect if CDROM contains data tracks */
+  
+  struct {
+    u_char nixbyte; /* em */
+    u_char ctl_adr; /* 0x4x: data, 0x0x: audio */
+    u_char number;
+    u_char format; /* em */ /* 0x00: lba, 0x01: msf */
+    u_int address;
+  } TocBuffer[MAX_TRACKS+1]; /* last entry faked */ 
+  
+  int in_SpinUp;
+  
+} DS[4];
+
+/*
+ * drive space ends here (needed separate for each unit)
+ */
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * DDI interface definitions
+ */
+#ifdef SBPCD_DEBUG
+# define DPRINTF(x)    sbpcd_dprintf x
+
+void sbpcd_dprintf(int level, char *fmt, ...)
+{
+  char buff[256];
+  va_list args;
+  extern int vsprintf(char *buf, const char *fmt, va_list args);
+
+  if (! (sbpcd_debug & (1 << level))) return;
+
+  va_start(args, fmt);
+  vsprintf(buff, fmt, args);
+  va_end(args);
+  printk(buff);
+}
+
+#else
+# define DPRINTF(x)    /* nothing */
+
+#endif SBPCD_DEBUG
+
+/*
+ * maintain trace bit pattern
+ */
+static int sbpcd_dbg_ioctl(unsigned long arg, int level)
+{
+  int val;
+
+  val = get_fs_long((int *) arg);
+  switch(val)
+    {
+    case 0:    /* OFF */
+      sbpcd_debug = 0;
+      break;
+
+    default:
+      if (val >= 128) sbpcd_debug &= ~(1 << (val - 128));
+      else sbpcd_debug |= (1 << val);
+    }
+  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)
+{
+   current->state = TASK_INTERRUPTIBLE;
+   current->timeout = jiffies + jifs;
+   schedule();
+}
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ *  convert logical_block_address to m-s-f_number (3 bytes only)
+ */
+static void lba2msf(int lba, u_char *msf)
+{
+  lba += CD_BLOCK_OFFSET;
+  msf[0] = lba / (CD_SECS*CD_FRAMES);
+  lba %= CD_SECS*CD_FRAMES;
+  msf[1] = lba / CD_FRAMES;
+  msf[2] = lba % CD_FRAMES;
+}
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ *  convert msf-bin to msf-bcd
+ */
+static 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)
+{
+  MSF msf;
+  u_int mm;
+
+  msf.c[3] = 0;
+  msf.c[2] = (blk + CD_BLOCK_OFFSET) / (CD_SECS * CD_FRAMES);
+  mm = (blk + CD_BLOCK_OFFSET) % (CD_SECS * CD_FRAMES);
+  msf.c[1] = mm / CD_FRAMES;
+  msf.c[0] = mm % CD_FRAMES;
+  return (msf.n);
+}
+/*==========================================================================*/
+static u_int make16(u_char rh, u_char rl)
+{
+  return ((rh<<8)|rl);
+}
+/*==========================================================================*/
+static u_int make32(u_int rh, u_int rl)
+{
+  return ((rh<<16)|rl);
+}
+/*==========================================================================*/
+static u_char swap_nibbles(u_char i)
+{
+  return ((i<<4)|(i>>4));
+}
+/*==========================================================================*/
+static u_char byt2bcd(u_char i)
+{
+  return (((i/10)<<4)+i%10);
+}
+/*==========================================================================*/
+static u_char bcd2bin(u_char bcd)
+{
+  return ((bcd>>4)*10+(bcd&0x0F));
+}
+/*==========================================================================*/
+static int msf2blk(int msfx)
+{
+  MSF msf;
+  int i;
+
+  msf.n=msfx;
+  i=(msf.c[2] * CD_SECS + msf.c[1]) * CD_FRAMES + msf.c[0] - CD_BLOCK_OFFSET;
+  if (i<0) return (0);
+  return (i);
+}
+/*==========================================================================*/
+/* evaluate xx_ReadError code (still mysterious) */ 
+static int sta2err(int sta)
+{
+  if (sta<=2) return (sta);
+  if (sta==0x05) return (-4);
+  if (sta==0x06) return (-6);
+  if (sta==0x0d) return (-6);
+  if (sta==0x0e) return (-3);
+  if (sta==0x14) return (-3);
+  if (sta==0x0c) return (-11);
+  if (sta==0x0f) return (-11);
+  if (sta==0x10) return (-11);
+  if (sta>=0x16) return (-12);
+  DS[d].CD_changed=0xFF;
+  if (sta==0x11) return (-15);
+  return (-2);
+}
+/*==========================================================================*/
+static void clr_cmdbuf(void)
+{
+  int i;
+
+  for (i=0;i<7;i++) drvcmd[i]=0;
+  cmd_type=0;
+}
+/*==========================================================================*/
+static void mark_timeout(void)
+{
+  timed_out=1;
+  DPRINTF((DBG_TIM,"SBPCD: timer stopped.\n"));
+}
+/*==========================================================================*/
+static void flush_status(void)
+{
+#ifdef CDMKE
+  int i;
+
+  if (current == task[0])
+    for (i=maxtim02;i!=0;i--) inb(CDi_status);
+  else 
+    {
+      sbp_sleep(150);
+      for (i=maxtim_data;i!=0;i--) inb(CDi_status);
+    }
+#else
+  timed_out=0;
+  SET_TIMER(mark_timeout,150);
+  do { }
+  while (!timed_out);
+  CLEAR_TIMER;
+  inb(CDi_status);
+#endif CDMKE
+}
+/*==========================================================================*/
+static int CDi_stat_loop(void)
+{
+  int i,j;
+  u_long timeout;
+  
+  if (current == task[0])
+    for(i=maxtim16;i!=0;i--)
+      {
+       j=inb(CDi_status);
+        if (!(j&s_not_data_ready)) return (j);
+        if (!(j&s_not_result_ready)) return (j);
+        if (!new_drive) if (j&s_attention) return (j);
+      }
+  else
+    for(timeout = jiffies + 1000, i=maxtim_data; timeout > jiffies; )
+      {
+       for ( ;i!=0;i--)
+         {
+           j=inb(CDi_status);
+           if (!(j&s_not_data_ready)) return (j);
+           if (!(j&s_not_result_ready)) return (j);
+           if (!new_drive) if (j&s_attention) return (j);
+         }
+       sbp_sleep(1);
+       i = 1;
+      }
+  return (-1);
+}
+/*==========================================================================*/
+static int ResponseInfo(void)
+{
+  int i,j, st=0;
+  u_long timeout;
+
+  if (current == task[0])
+    for (i=0;i<response_count;i++)
+      {
+       for (j=maxtim_8;j!=0;j--)
+         {
+           st=inb(CDi_status);
+           if (!(st&s_not_result_ready)) break;
+         }
+       if (j==0) return (-1);
+       infobuf[i]=inb(CDi_info);
+      }
+  else 
+    {
+      for (i=0, timeout = jiffies + 100; i < response_count; i++) 
+       {
+         for (j=maxtim_data; ; )
+           {
+             for ( ;j!=0;j-- )
+               {
+                 st=inb(CDi_status);
+                 if (!(st&s_not_result_ready)) break;
+               }
+             if (j != 0 || timeout <= jiffies) break;
+             sbp_sleep(0);
+             j = 1;
+           }
+         if (timeout <= jiffies) return (-1);
+         infobuf[i]=inb(CDi_info);
+       }
+    }
+  return (0);
+}
+/*==========================================================================*/
+static int EvaluateStatus(int st)
+{
+  if (!new_drive)
+    {
+      DS[d].status_byte=0;
+      if (st&p_caddin_old) DS[d].status_byte |= p_door_closed|p_caddy_in;
+      if (st&p_spinning) DS[d].status_byte |= p_spinning;
+      if (st&p_check) DS[d].status_byte |= p_check;
+      if (st&p_busy_old) DS[d].status_byte |= p_busy_new;
+      if (st&p_disk_ok) DS[d].status_byte |= p_disk_ok;
+    }
+  else { DS[d].status_byte=st;
+        st=p_success_old; /* for new drives: fake "successful" bit of old drives */
+       }
+  return (st);
+}
+/*==========================================================================*/
+static int ResponseStatus(void)
+{
+  int i,j;
+  u_long timeout;
+
+  DPRINTF((DBG_STA,"SBPCD: doing ResponseStatus...\n"));
+
+  if (current == task[0])
+    {
+      if (flags_cmd_out & f_respo3) j = maxtim_8;
+      else if (flags_cmd_out&f_respo2) j=maxtim16;
+      else j=maxtim04;
+      for (;j!=0;j--)
+       {
+         i=inb(CDi_status);
+         if (!(i&s_not_result_ready)) break;
+       }
+    }
+  else
+    {
+      if (flags_cmd_out & f_respo3) timeout = jiffies;
+      else if (flags_cmd_out & f_respo2) timeout = jiffies + 1600;
+      else timeout = jiffies + 400;
+      j=maxtim_8;
+      do
+       {
+         for ( ;j!=0;j--)
+           { 
+             i=inb(CDi_status);
+             if (!(i&s_not_result_ready)) break;
+           }
+         if (j != 0 || timeout <= jiffies) break;
+         sbp_sleep(0);
+         j = 1;
+       }
+      while (1);
+    }
+  if (j==0) 
+    { if ((flags_cmd_out & f_respo3) == 0)
+       DPRINTF((DBG_STA,"SBPCD: ResponseStatus: timeout.\n"));
+      EvaluateStatus(0);
+      return (-1);
+    }
+  i=inb(CDi_info);
+  i=EvaluateStatus(i);
+  return (i);
+}
+/*==========================================================================*/
+static void xx_ReadStatus(void)
+{
+  int i;
+
+  DPRINTF((DBG_STA,"SBPCD: giving xx_ReadStatus command\n"));
+
+  if (!new_drive) OUT(CDo_command,0x81);
+  else
+    {
+#if SBPCD_DIS_IRQ
+      cli();
+#endif SBPCD_DIS_IRQ
+      OUT(CDo_command,0x05);
+      for (i=0;i<6;i++) OUT(CDo_command,0);
+#if SBPCD_DIS_IRQ
+      sti();
+#endif SBPCD_DIS_IRQ
+    }
+}
+/*==========================================================================*/
+int xx_ReadError(void)
+{
+  int cmd_out(void);
+  int i;
+
+  clr_cmdbuf();
+  DPRINTF((DBG_ERR,"SBPCD: giving xx_ReadError command.\n"));
+  if (new_drive)
+    {
+      drvcmd[0]=0x82;
+      response_count=8;
+      flags_cmd_out=f_putcmd|f_ResponseStatus;
+    }
+  else
+    {
+      drvcmd[0]=0x82;
+      response_count=6;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus;
+    }
+  i=cmd_out();
+  DS[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;
+  DS[d].error_byte=infobuf[i];
+  DPRINTF((DBG_ERR,"SBPCD: xx_ReadError: infobuf[%d] is %d (%02X)\n",i,DS[d].error_byte,DS[d].error_byte));
+  i=sta2err(infobuf[i]);
+  return (i);
+}
+/*==========================================================================*/
+int cmd_out(void)
+{
+  int i=0;
+
+  if (flags_cmd_out&f_putcmd)
+    { 
+      DPRINTF((DBG_CMD,"SBPCD: cmd_out: put"));
+      for (i=0;i<7;i++) DPRINTF((DBG_CMD," %02X",drvcmd[i]));
+      DPRINTF((DBG_CMD,"\n"));
+
+#if SBPCD_DIS_IRQ
+      cli();
+#endif SBPCD_DIS_IRQ
+      for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+#if SBPCD_DIS_IRQ
+      sti();
+#endif SBPCD_DIS_IRQ
+    }
+  if (response_count!=0)
+    {
+      if (cmd_type!=0)
+       {
+         if (sbpro_type) OUT(CDo_sel_d_i,0x01);
+         DPRINTF((DBG_INF,"SBPCD: misleaded to try ResponseData.\n"));
+         if (sbpro_type) OUT(CDo_sel_d_i,0x00);
+       }
+      else i=ResponseInfo();
+      if (i<0) return (-9);
+    }
+  if (DS[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to CDi_stat_loop.\n"));
+  if (flags_cmd_out&f_lopsta)
+    {
+      i=CDi_stat_loop();
+      if ((i<0)||!(i&s_attention)) return (-9);
+    }
+  if (!(flags_cmd_out&f_getsta)) goto LOC_229;
+  
+LOC_228:
+  if (DS[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to xx_ReadStatus.\n"));
+  xx_ReadStatus();
+
+LOC_229:
+  if (flags_cmd_out&f_ResponseStatus) 
+    {
+      if (DS[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to ResponseStatus.\n"));
+      i=ResponseStatus();
+                   /* builds status_byte, returns orig. status or p_busy_new */
+      if (i<0) return (-9);
+      if (flags_cmd_out&(f_bit1|f_wait_if_busy))
+       {
+         if (!st_check)
+           {
+             if (flags_cmd_out&f_bit1) if (i&p_success_old) goto LOC_232;
+             if (!(flags_cmd_out&f_wait_if_busy)) goto LOC_228;
+             if (!st_busy) goto LOC_228;
+           }
+       }
+    }
+LOC_232:
+  if (!(flags_cmd_out&f_obey_p_check)) return (0);
+  if (!st_check) return (0);
+  if (DS[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to xx_ReadError.\n"));
+  i=xx_ReadError();
+  if (DS[d].in_SpinUp != 0) DPRINTF((DBG_SPI,"SBPCD: to cmd_out OK.\n"));
+  return (i);
+}
+/*==========================================================================*/
+static int xx_Seek(u_int pos, char f_blk_msf)
+{
+  int i;
+
+  clr_cmdbuf();
+  if (f_blk_msf>1) return (-3);
+  if (!new_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;
+    }
+  else
+    {
+      if (f_blk_msf==0) pos=blk2msf(pos);
+      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;
+    }
+  drvcmd[0]=0x01;
+  response_count=0;
+  i=cmd_out();
+  return (i);
+}
+/*==========================================================================*/
+static int xx_SpinUp(void)
+{
+  int i;
+
+  DPRINTF((DBG_SPI,"SBPCD: SpinUp.\n"));
+  DS[d].in_SpinUp = 1;
+  clr_cmdbuf();
+  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;
+    }
+  else
+    {
+      drvcmd[0]=0x02;
+      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+    }
+  response_count=0;
+  i=cmd_out();
+  DS[d].in_SpinUp = 0;
+  return (i);
+}
+/*==========================================================================*/
+static int yy_SpinDown(void)
+{
+  int i;
+
+  if (!new_drive) return (-3);
+  clr_cmdbuf();
+  drvcmd[0]=0x06;
+  flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+  response_count=0;
+  i=cmd_out();
+  return (i);
+}
+/*==========================================================================*/
+static int yy_SetSpeed(u_char speed, u_char x1, u_char x2)
+{
+  int i;
+
+  if (!new_drive) return (-3);
+  clr_cmdbuf();
+  drvcmd[0]=0x09;
+  drvcmd[1]=0x03;
+  drvcmd[2]=speed;
+  drvcmd[3]=x1;
+  drvcmd[4]=x2;
+  flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+  response_count=0;
+  i=cmd_out();
+  return (i);
+}
+/*==========================================================================*/
+static int xx_SetVolume(void)
+{
+  int i;
+  u_char channel0,channel1,volume0,volume1;
+  u_char control0,value0,control1,value1;
+
+  DS[d].diskstate_flags &= ~volume_bit;
+  clr_cmdbuf();
+  channel0=DS[d].vol_chan0;
+  volume0=DS[d].vol_ctrl0;
+  channel1=control1=DS[d].vol_chan1;
+  volume1=value1=DS[d].vol_ctrl1;
+  control0=value0=0;
+
+  if (((DS[d].drv_options&sax_a)!=0)&&(DS[d].drv_type>=drv_211))
+    {
+      if ((volume0!=0)&&(volume1==0))
+       {
+         volume1=volume0;
+         channel1=channel0;
+       }
+      else if ((volume0==0)&&(volume1!=0))
+       {
+         volume0=volume1;
+         channel0=channel1;
+       }
+    }
+  if (channel0>1)
+    {
+      channel0=0;
+      volume0=0;
+    }
+  if (channel1>1)
+    {
+      channel1=1;
+      volume1=0;
+    }
+  
+  if (new_drive)
+    {
+      control0=channel0+1;
+      control1=channel1+1;
+      value0=(volume0>volume1)?volume0:volume1;
+      value1=value0;
+      if (volume0==0) control0=0;
+      if (volume1==0) control1=0;
+      drvcmd[0]=0x09;
+      drvcmd[1]=0x05;
+      drvcmd[3]=control0;
+      drvcmd[4]=value0;
+      drvcmd[5]=control1;
+      drvcmd[6]=value1;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+    }
+  else 
+    {
+      if (DS[d].drv_type>=drv_300)
+       {
+         control0=volume0&0xFC;
+         value0=volume1&0xFC;
+         if ((volume0!=0)&&(volume0<4)) control0 |= 0x04;
+         if ((volume1!=0)&&(volume1<4)) value0 |= 0x04;
+         if (channel0!=0) control0 |= 0x01;
+         if (channel1==1) value0 |= 0x01;
+       }
+      else
+       {
+         value0=(volume0>volume1)?volume0:volume1;
+         if (DS[d].drv_type<drv_211)
+           {
+             if (channel0!=0)
+               {
+                 i=channel1;
+                 channel1=channel0;
+                 channel0=i;
+                 i=volume1;
+                 volume1=volume0;
+                 volume0=i;
+               }
+             if (channel0==channel1)
+               {
+                 if (channel0==0)
+                   {
+                     channel1=1;
+                     volume1=0;
+                     volume0=value0;
+                   }
+                 else
+                   {
+                     channel0=0;
+                     volume0=0;
+                     volume1=value0;
+                   }
+               }
+           }
+
+         if ((volume0!=0)&&(volume1!=0))
+           {
+             if (volume0==0xFF) volume1=0xFF;
+             else if (volume1==0xFF) volume0=0xFF;
+           }
+         else if (DS[d].drv_type<drv_201) volume0=volume1=value0;
+
+         if (DS[d].drv_type>=drv_201)
+           {
+             if (volume0==0) control0 |= 0x80;
+             if (volume1==0) control0 |= 0x40;
+           }
+         if (DS[d].drv_type>=drv_211)
+           {
+             if (channel0!=0) control0 |= 0x20;
+             if (channel1!=1) control0 |= 0x10;
+           }
+       }
+      drvcmd[0]=0x84;
+      drvcmd[1]=0x83;
+      drvcmd[4]=control0;
+      drvcmd[5]=value0;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+    }
+
+  response_count=0;
+  i=cmd_out();
+  if (i>0) return (i);
+  DS[d].diskstate_flags |= volume_bit;
+  return (0);
+}
+/*==========================================================================*/
+static int GetStatus(void)
+{
+  int i;
+
+  flags_cmd_out=f_getsta|f_ResponseStatus|f_obey_p_check;
+  response_count=0;
+  cmd_type=0;
+  i=cmd_out();
+  return (i);
+}
+/*==========================================================================*/
+static int xy_DriveReset(void)
+{
+  int i;
+
+  DPRINTF((DBG_RES,"SBPCD: xy_DriveReset called.\n"));
+  if (!new_drive) OUT(CDo_reset,0x00);
+  else
+    {
+      clr_cmdbuf();
+      drvcmd[0]=0x0A;
+      flags_cmd_out=f_putcmd;
+      response_count=0;
+      i=cmd_out();
+    }
+  flush_status();
+  i=GetStatus();
+  if (i>=0) return -1;
+  if (DS[d].error_byte!=aud_12) return -1;
+  return (0);
+}
+/*==========================================================================*/
+static int SetSpeed(void)
+{
+  int i, speed;
+
+  if (!(DS[d].drv_options&(speed_auto|speed_300|speed_150))) return (0);
+  speed=0x80;
+  if (!(DS[d].drv_options&speed_auto))
+    {
+      speed |= 0x40;
+      if (!(DS[d].drv_options&speed_300)) speed=0;
+    }
+  i=yy_SetSpeed(speed,0,0);
+  return (i);
+}
+/*==========================================================================*/
+static int DriveReset(void)
+{
+  int i;
+
+  i=xy_DriveReset();
+  if (i<0) return (-2);
+  do
+    {
+      i=GetStatus();
+      if ((i<0)&&(i!=-15)) return (-2); /* i!=-15 is from sta2err */
+      if (!st_caddy_in) break;
+    }
+  while (!st_diskok);
+  DS[d].CD_changed=1;
+  i=SetSpeed();
+  if (i<0) return (-2);
+  return (0);
+}
+/*==========================================================================*/
+static int xx_Pause_Resume(int pau_res)
+{
+  int i;
+
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x0D;
+      flags_cmd_out=f_putcmd|f_respo2|f_ResponseStatus|f_obey_p_check;
+    }
+  else
+    {
+      drvcmd[0]=0x8D;
+      flags_cmd_out=f_putcmd|f_respo2|f_getsta|f_ResponseStatus|f_obey_p_check;
+    }
+  if (pau_res!=1) drvcmd[1]=0x80;
+  response_count=0;
+  i=cmd_out();
+  return (i);
+}
+/*==========================================================================*/
+#if 000
+static int yy_LockDoor(char lock)
+{
+  int i;
+
+  if (!new_drive) return (-3);
+  clr_cmdbuf();
+  drvcmd[0]=0x0C;
+  if (lock==1) drvcmd[1]=0x01;
+  flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+  response_count=0;
+  i=cmd_out();
+  return (i);
+}
+#endif 000
+/*==========================================================================*/
+static int xx_ReadSubQ(void)
+{
+  int i,j;
+
+  DS[d].diskstate_flags &= ~subq_bit;
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x87;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+      response_count=11;
+    }
+  else
+    {
+      drvcmd[0]=0x89;
+      drvcmd[1]=0x02;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      response_count=13;
+    }
+  for (j=0;j<255;j++)
+    {
+      i=cmd_out();
+      if (i<0) return (i);
+      if (infobuf[0]!=0) break;
+      if (!st_spinning)
+       {
+         DS[d].SubQ_ctl_adr=DS[d].SubQ_trk=DS[d].SubQ_pnt_idx=DS[d].SubQ_whatisthis=0;
+         DS[d].SubQ_run_tot=DS[d].SubQ_run_trk=0;
+         return (0);
+       }
+    }
+  DS[d].SubQ_audio=infobuf[0];
+  DS[d].SubQ_ctl_adr=swap_nibbles(infobuf[1]);
+  DS[d].SubQ_trk=byt2bcd(infobuf[2]);
+  DS[d].SubQ_pnt_idx=byt2bcd(infobuf[3]);
+  i=4;
+  if (!new_drive) i=5;
+  DS[d].SubQ_run_tot=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
+  i=7;
+  if (!new_drive) i=9;
+  DS[d].SubQ_run_trk=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2])); /* msf-bin */
+  DS[d].SubQ_whatisthis=infobuf[i+3];
+  DS[d].diskstate_flags |= subq_bit;
+  return (0);
+}
+/*==========================================================================*/
+static int xx_ModeSense(void)
+{
+  int i;
+
+  DS[d].diskstate_flags &= ~frame_size_bit;
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x84;
+      drvcmd[1]=0x00;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+      response_count=5;
+    }
+  else
+    {
+      drvcmd[0]=0x85;
+      drvcmd[1]=0x00;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+      response_count=2;
+    }
+  i=cmd_out();
+  if (i<0) return (i);
+  i=0;
+  if (new_drive) DS[d].sense_byte=infobuf[i++];
+  DS[d].frame_size=make16(infobuf[i],infobuf[i+1]);
+  DS[d].diskstate_flags |= frame_size_bit;
+  return (0);
+}
+/*==========================================================================*/
+#if 0000
+static int xx_TellVolume(void)
+{
+  int i;
+  u_char switches;
+  u_char chan0,vol0,chan1,vol1;
+
+  DS[d].diskstate_flags &= ~volume_bit;
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x84;
+      drvcmd[1]=0x05;
+      response_count=5;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+    }
+  else
+    {
+      drvcmd[0]=0x85;
+      drvcmd[1]=0x03;
+      response_count=2;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+    }
+  i=cmd_out();
+  if (i<0) return (i);
+  if (new_drive)
+    {
+      chan0=infobuf[1]&0x0F;
+      vol0=infobuf[2];
+      chan1=infobuf[3]&0x0F;
+      vol1=infobuf[4];
+      if (chan0==0)
+       {
+         chan0=1;
+         vol0=0;
+       }
+      if (chan1==0)
+       {
+         chan1=2;
+         vol1=0;
+       }
+      chan0 >>= 1;
+      chan1 >>= 1;
+    }
+  else
+    {
+      chan0=0;
+      chan1=1;
+      vol0=vol1=infobuf[1];
+      if (DS[d].drv_type>=drv_201)
+       {
+         if (DS[d].drv_type<drv_300)
+           {
+             switches=infobuf[0];
+             if ((switches&0x80)!=0) vol0=0;
+             if ((switches&0x40)!=0) vol1=0;
+             if (DS[d].drv_type>=drv_211)
+               {
+                 if ((switches&0x20)!=0) chan0=1;
+                 if ((switches&0x10)!=0) chan1=0;
+               }
+           }
+         else
+           {
+             vol0=infobuf[0];
+             if ((vol0&0x01)!=0) chan0=1;
+             if ((vol1&0x01)==0) chan1=0;
+             vol0 &= 0xFC;
+             vol1 &= 0xFC;
+             if (vol0!=0) vol0 += 3;
+             if (vol1!=0) vol1 += 3;
+           }
+       }
+    }
+  DS[d].vol_chan0=chan0;
+  DS[d].vol_ctrl0=vol0;
+  DS[d].vol_chan1=chan1;
+  DS[d].vol_ctrl1=vol1;
+  DS[d].vol_chan2=2;
+  DS[d].vol_ctrl2=0xFF;
+  DS[d].vol_chan3=3;
+  DS[d].vol_ctrl3=0xFF;
+  DS[d].diskstate_flags |= volume_bit;
+  return (0);
+}
+#endif
+/*==========================================================================*/
+static int xx_ReadCapacity(void)
+{
+  int i;
+
+  DS[d].diskstate_flags &= ~cd_size_bit;
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x85;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+    }
+  else
+    {
+      drvcmd[0]=0x88;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+    }
+  response_count=5;
+  i=cmd_out();
+  if (i<0) return (i);
+  DS[d].CDsize_blk=make32(make16(0,infobuf[0]),make16(infobuf[1],infobuf[2]));
+  if (new_drive) DS[d].CDsize_blk=msf2blk(DS[d].CDsize_blk);
+  DS[d].CDsize_frm = (DS[d].CDsize_blk * make16(infobuf[3],infobuf[4])) / CD_FRAMESIZE;
+  DS[d].CDsize_blk += 151;
+  DS[d].diskstate_flags |= cd_size_bit;
+  return (0);
+}
+/*==========================================================================*/
+static int xx_ReadTocDescr(void)
+{
+  int i;
+
+  DS[d].diskstate_flags &= ~toc_bit;
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x8B;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+    }
+  else
+    {
+      drvcmd[0]=0x8B;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+    }
+  response_count=6;
+  i=cmd_out();
+  if (i<0) return (i);
+  DS[d].xa_byte=infobuf[0];
+  DS[d].n_first_track=infobuf[1];
+  DS[d].n_last_track=infobuf[2];
+  DS[d].size_msf=make32(make16(0,infobuf[3]),make16(infobuf[4],infobuf[5]));
+  DS[d].size_blk=msf2blk(DS[d].size_msf);
+  DS[d].diskstate_flags |= toc_bit;
+  DPRINTF((DBG_TOC,"SBPCD: TocDesc: %02X %02X %02X %08X\n",
+        DS[d].xa_byte,DS[d].n_first_track,DS[d].n_last_track,DS[d].size_msf));
+  return (0);
+}
+/*==========================================================================*/
+static int xx_ReadTocEntry(int num)
+{
+  int i;
+
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x8C;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+    }
+  else
+    {
+      drvcmd[0]=0x8C;
+      drvcmd[1]=0x02;
+      flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+    }
+  drvcmd[2]=num;
+  response_count=8;
+  i=cmd_out();
+  if (i<0) return (i);
+  DS[d].TocEnt_nixbyte=infobuf[0];
+  DS[d].TocEnt_ctl_adr=swap_nibbles(infobuf[1]);
+  DS[d].TocEnt_number=infobuf[2];
+  DS[d].TocEnt_format=infobuf[3];
+  if (new_drive) i=4;
+  else i=5;
+  DS[d].TocEnt_address=make32(make16(0,infobuf[i]),make16(infobuf[i+1],infobuf[i+2]));
+  DPRINTF((DBG_TOC,"SBPCD: TocEntry: %02X %02X %02X %02X %08X\n",
+          DS[d].TocEnt_nixbyte,DS[d].TocEnt_ctl_adr,DS[d].TocEnt_number,
+          DS[d].TocEnt_format,DS[d].TocEnt_address));
+  return (0);
+}
+/*==========================================================================*/
+static int xx_ReadPacket(void)
+{
+  int i;
+
+  clr_cmdbuf();
+  drvcmd[0]=0x8E;
+  drvcmd[1]=response_count;
+  flags_cmd_out=f_putcmd|f_getsta|f_ResponseStatus|f_obey_p_check;
+  i=cmd_out();
+  return (i);
+}
+/*==========================================================================*/
+static int convert_UPC(u_char *p)
+{
+  int i;
+
+  p++;
+  if (!new_drive) p[13]=0;
+  for (i=0;i<7;i++)
+    {
+      if (new_drive) DS[d].UPC_buf[i]=swap_nibbles(*p++);
+      else
+       {
+         DS[d].UPC_buf[i]=((*p++)<<4)&0xFF;
+         DS[d].UPC_buf[i] |= *p++;
+       }
+    }
+  DS[d].UPC_buf[6] &= 0xF0;
+  return (0);
+}
+/*==========================================================================*/
+static int xx_ReadUPC(void)
+{
+  int i;
+
+  DS[d].diskstate_flags &= ~upc_bit;
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x88;
+      response_count=8;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+    }
+  else
+    {
+      drvcmd[0]=0x08;
+      response_count=0;
+      flags_cmd_out=f_putcmd|f_lopsta|f_getsta|f_ResponseStatus|f_obey_p_check|f_bit1;
+    }
+  i=cmd_out();
+  if (i<0) return (i);
+  if (!new_drive)
+    {
+      response_count=16;
+      i=xx_ReadPacket();
+      if (i<0) return (i);
+    }
+  DS[d].UPC_ctl_adr=0;
+  if (new_drive) i=0;
+  else i=2;
+  if ((infobuf[i]&0x80)!=0)
+    {
+      convert_UPC(&infobuf[i]);
+      DS[d].UPC_ctl_adr &= 0xF0;
+      DS[d].UPC_ctl_adr |= 0x02;
+    }
+  DS[d].diskstate_flags |= upc_bit;
+  return (0);
+}
+/*==========================================================================*/
+static int yy_CheckMultiSession(void)
+{
+  int i;
+
+  DS[d].diskstate_flags &= ~multisession_bit;
+  DS[d].f_multisession=0;
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x8D;
+      response_count=6;
+      flags_cmd_out=f_putcmd|f_ResponseStatus|f_obey_p_check;
+      i=cmd_out();
+      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]));
+         DS[d].f_multisession=1;
+         DS[d].lba_multi=msf2blk(make32(make16(0,infobuf[1]),
+                                   make16(infobuf[2],infobuf[3])));
+       }
+    }
+  DS[d].diskstate_flags |= multisession_bit;
+  return (0);
+}
+/*==========================================================================*/
+static void check_datarate(void)
+{
+#ifdef CDMKE
+  int i=0;
+
+  timed_out=0;
+  datarate=0;
+
+  /* set a timer to make (timed_out!=0) after 1.1 seconds */
+
+  DPRINTF((DBG_TIM,"SBPCD: timer started (110).\n"));
+  sti(); /* to avoid possible "printf" bug */
+
+  SET_TIMER(mark_timeout,110);
+  do
+    {
+      i=inb(CDi_status);
+      datarate++;
+
+#if 00000
+      if (datarate>0x0FFFFFFF) break;
+#endif 00000
+
+    }
+  while (!timed_out); /* originally looping for 1.1 seconds */
+  CLEAR_TIMER;
+  DPRINTF((DBG_TIM,"SBPCD: datarate: %d\n", datarate));
+  if (datarate<65536) datarate=65536;
+
+  maxtim16=datarate*16;
+  maxtim04=datarate*4;
+  maxtim02=datarate*2;
+  maxtim_8=datarate/32;
+#if MANY_SESSION
+  maxtim_data=datarate/100;
+#else
+  maxtim_data=datarate/300;
+#endif MANY_SESSION
+  DPRINTF((DBG_TIM,"SBPCD: maxtim_8 %d, maxtim_data %d.\n",
+          maxtim_8, maxtim_data));
+#endif CDMKE
+}
+/*==========================================================================*/
+static int check_version(void)
+{
+  int i, j;
+
+  /* clear any pending error state */
+  clr_cmdbuf();
+  drvcmd[0]=0x82;
+  response_count=9;
+  flags_cmd_out=f_putcmd;
+  cmd_out();
+
+  /* read drive version */
+  clr_cmdbuf();
+  for (i=0;i<12;i++) infobuf[i]=0;
+  drvcmd[0]=0x83;
+  response_count=12;
+  flags_cmd_out=f_putcmd;
+  i=cmd_out();
+  if (i<0) DPRINTF((DBG_INI,"SBPCD: cmd_83 returns %d\n",i));
+
+  DPRINTF((DBG_INI,"SBPCD: infobuf = \""));
+  for (i=0;i<12;i++) DPRINTF((DBG_INI,"%c",infobuf[i]));
+  DPRINTF((DBG_INI,"\"\n"));
+
+  for (i=0;i<4;i++) if (infobuf[i]!=drive_family[i]) break;
+  if (i==4)
+    {
+      DS[d].drive_model[0]=infobuf[i++];
+      DS[d].drive_model[1]=infobuf[i++];
+      DS[d].drive_model[2]='-';
+      DS[d].drive_model[3]='x';
+      DS[d].drv_type=drv_new;
+    }
+  else
+    {
+      for (i=0;i<8;i++) if (infobuf[i]!=drive_vendor[i]) break;
+      if (i!=8) return (-1);
+      DS[d].drive_model[0]='2';
+      DS[d].drive_model[1]='x';
+      DS[d].drive_model[2]='-';
+      DS[d].drive_model[3]='x';
+      DS[d].drv_type=drv_old;
+    }
+  for (j=0;j<4;j++) DS[d].firmware_version[j]=infobuf[i+j];
+  j = (DS[d].firmware_version[0] & 0x0F) * 100 +
+      (DS[d].firmware_version[2] & 0x0F) *10 +
+      (DS[d].firmware_version[3] & 0x0F);
+  if (new_drive)
+    {
+      if (j<100) DS[d].drv_type=drv_099;
+      else DS[d].drv_type=drv_100;
+    }
+  else if (j<200) DS[d].drv_type=drv_199;
+  else if (j<201) DS[d].drv_type=drv_200;
+  else if (j<210) DS[d].drv_type=drv_201;
+  else if (j<211) DS[d].drv_type=drv_210;
+  else if (j<300) DS[d].drv_type=drv_211;
+  else DS[d].drv_type=drv_300;
+  return (0);
+}
+/*==========================================================================*/
+static int switch_drive(int num)
+{
+  int i;
+
+  d=num;
+
+  i=num;
+  if (sbpro_type) i=(i&0x01)<<1|(i&0x02)>>1;
+  OUT(CDo_enable,i);
+  DPRINTF((DBG_DID,"SBPCD: switch_drive: drive %d activated.\n",DS[d].drv_minor));
+  return (0);
+}
+/*==========================================================================*/
+/*
+ * probe for the presence of drives on the selected controller
+ */
+static int check_drives(void)
+{
+  int i, j;
+  char *printk_header="";
+
+  DPRINTF((DBG_INI,"SBPCD: check_drives entered.\n"));
+
+  ndrives=0;
+  for (j=0;j<NR_SBPCD;j++)
+    {
+      DS[j].drv_minor=j;
+      switch_drive(j);
+      DPRINTF((DBG_ID,"SBPCD: check_drives: drive %d activated.\n",j));
+      i=check_version();
+      DPRINTF((DBG_ID,"SBPCD: check_version returns %d.\n",i));
+      if (i>=0)
+       {
+         ndrives++;
+         DS[d].drv_options=drv_pattern[j];
+         if (!new_drive) DS[d].drv_options&=~(speed_auto|speed_300|speed_150);
+         printk("%sDrive %d: %s%.4s (%.4s)\n", printk_header,
+                DS[d].drv_minor,
+                 drive_family,
+                 DS[d].drive_model,
+                 DS[d].firmware_version);
+         printk_header="       - ";
+       }
+      else DS[d].drv_minor=-1;
+    }
+  if (ndrives==0) return (-1);
+  return (0);
+}
+/*==========================================================================*/
+#if 000
+static void timewait(void)
+{
+  int i;
+  for (i=0; i<65500; i++);
+}
+#endif 000
+/*==========================================================================*/
+#if FUTURE
+/*
+ *  obtain if requested service disturbs current audio state
+ */            
+static int obey_audio_state(u_char audio_state, u_char func,u_char subfunc)
+{
+  switch (audio_state)                   /* audio status from controller  */
+    {
+    case aud_11: /* "audio play in progress" */
+    case audx11:
+      switch (func)                      /* DOS command code */
+       {
+       case cmd_07: /* input flush  */
+       case cmd_0d: /* open device  */
+       case cmd_0e: /* close device */
+       case cmd_0c: /* ioctl output */
+         return (1);
+       case cmd_03: /* ioctl input  */
+         switch (subfunc)
+           /* DOS ioctl input subfunction */
+           {
+           case cxi_00:
+           case cxi_06:
+           case cxi_09:
+             return (1);
+           default:
+             return (ERROR15);
+           }
+         return (1);
+       default:
+         return (ERROR15);
+       }
+      return (1);
+    case aud_12:                  /* "audio play paused"      */
+    case audx12:
+      return (1);
+    default:
+      return (2);
+    }
+}
+#endif FUTURE
+/*==========================================================================*/
+/* allowed is only
+ * ioctl_o, flush_input, open_device, close_device, 
+ * tell_address, tell_volume, tell_capabiliti,
+ * tell_framesize, tell_CD_changed, tell_audio_posi
+ */
+static int check_allowed1(u_char func1, u_char func2)
+{
+#if 000
+  if (func1==ioctl_o) return (0);
+  if (func1==read_long) return (-1);
+  if (func1==read_long_prefetch) return (-1);
+  if (func1==seek) return (-1);
+  if (func1==audio_play) return (-1);
+  if (func1==audio_pause) return (-1);
+  if (func1==audio_resume) return (-1);
+  if (func1!=ioctl_i) return (0);
+  if (func2==tell_SubQ_run_tot) return (-1);
+  if (func2==tell_cdsize) return (-1);
+  if (func2==tell_TocDescrip) return (-1);
+  if (func2==tell_TocEntry) return (-1);
+  if (func2==tell_subQ_info) return (-1);
+  if (new_drive) if (func2==tell_SubChanInfo) return (-1);
+  if (func2==tell_UPC) return (-1);
+#else
+  return (0);
+#endif 000
+}
+/*==========================================================================*/
+static int check_allowed2(u_char func1, u_char func2)
+{
+#if 000
+  if (func1==read_long) return (-1);
+  if (func1==read_long_prefetch) return (-1);
+  if (func1==seek) return (-1);
+  if (func1==audio_play) return (-1);
+  if (func1!=ioctl_o) return (0);
+  if (new_drive)
+    {
+      if (func2==EjectDisk) return (-1);
+      if (func2==CloseTray) return (-1);
+    }
+#else
+  return (0);
+#endif 000
+}
+/*==========================================================================*/
+static int check_allowed3(u_char func1, u_char func2)
+{
+#if 000
+  if (func1==ioctl_i)
+    {
+      if (func2==tell_address) return (0);
+      if (func2==tell_capabiliti) return (0);
+      if (func2==tell_CD_changed) return (0);
+      if (!new_drive) if (func2==tell_SubChanInfo) return (0);
+      return (-1);
+    }
+  if (func1==ioctl_o)
+    {
+      if (func2==DriveReset) return (0);
+      if (!new_drive)
+       {
+         if (func2==EjectDisk) return (0);
+         if (func2==LockDoor) return (0);
+         if (func2==CloseTray) return (0);
+       }
+      return (-1);
+    }
+  if (func1==flush_input) return (-1);
+  if (func1==read_long) return (-1);
+  if (func1==read_long_prefetch) return (-1);
+  if (func1==seek) return (-1);
+  if (func1==audio_play) return (-1);
+  if (func1==audio_pause) return (-1);
+  if (func1==audio_resume) return (-1);
+#else
+  return (0);
+#endif 000
+}
+/*==========================================================================*/
+static int seek_pos_audio_end(void)
+{
+  int i;
+
+  i=msf2blk(DS[d].pos_audio_end)-1;
+  if (i<0) return (-1);
+  i=xx_Seek(i,0);
+  return (i);
+}
+/*==========================================================================*/
+static int ReadToC(void)
+{
+  int i, j;
+  DS[d].diskstate_flags &= ~toc_bit;
+  DS[d].ored_ctl_adr=0;
+  for (j=DS[d].n_first_track;j<=DS[d].n_last_track;j++)
+    {
+      i=xx_ReadTocEntry(j);
+      if (i<0) return (i);
+      DS[d].TocBuffer[j].nixbyte=DS[d].TocEnt_nixbyte;
+      DS[d].TocBuffer[j].ctl_adr=DS[d].TocEnt_ctl_adr;
+      DS[d].TocBuffer[j].number=DS[d].TocEnt_number;
+      DS[d].TocBuffer[j].format=DS[d].TocEnt_format;
+      DS[d].TocBuffer[j].address=DS[d].TocEnt_address;
+      DS[d].ored_ctl_adr |= DS[d].TocEnt_ctl_adr;
+    }
+/* fake entry for LeadOut Track */
+  DS[d].TocBuffer[j].nixbyte=0;
+  DS[d].TocBuffer[j].ctl_adr=0;
+  DS[d].TocBuffer[j].number=0;
+  DS[d].TocBuffer[j].format=0;
+  DS[d].TocBuffer[j].address=DS[d].size_msf;
+
+  DS[d].diskstate_flags |= toc_bit;
+  return (0);
+}
+/*==========================================================================*/
+static int DiskInfo(void)
+{
+  int i;
+
+  i=SetSpeed();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: first SetSpeed returns %d\n", i));
+      i=SetSpeed();
+      if (i<0)
+       {
+         DPRINTF((DBG_INF,"SBPCD: DiskInfo: second SetSpeed returns %d\n", i));
+         return (i);
+       }
+    }
+  i=xx_ModeSense();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: first xx_ModeSense returns %d\n", i));
+      i=xx_ModeSense();
+      if (i<0)
+       {
+         DPRINTF((DBG_INF,"SBPCD: DiskInfo: second xx_ModeSense returns %d\n", i));
+         return (i);
+       }
+      return (i);
+    }
+  i=xx_ReadCapacity();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: first ReadCapacity returns %d\n", i));
+      i=xx_ReadCapacity();
+      if (i<0)
+       {
+         DPRINTF((DBG_INF,"SBPCD: DiskInfo: second ReadCapacity returns %d\n", i));
+         return (i);
+       }
+      return (i);
+    }
+  i=xx_ReadTocDescr();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: ReadTocDescr returns %d\n", i));
+      return (i);
+    }
+  i=ReadToC();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: ReadToC returns %d\n", i));
+      return (i);
+    }
+  i=yy_CheckMultiSession();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: yy_CheckMultiSession returns %d\n", i));
+      return (i);
+    }
+  i=xx_ReadTocEntry(DS[d].n_first_track);
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: xx_ReadTocEntry(1) returns %d\n", i));
+      return (i);
+    }
+  i=xx_ReadUPC();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: DiskInfo: xx_ReadUPC returns %d\n", i));
+      return (i);
+    }
+  return (0);
+}
+/*==========================================================================*/
+/*
+ *  called always if driver gets entered
+ *  returns 0 or ERROR2 or ERROR15
+ */
+static int prepare(u_char func, u_char subfunc)
+{
+  int i;
+
+  if (!new_drive)
+    {
+      i=inb(CDi_status);
+      if (i&s_attention) GetStatus();
+    }
+  else GetStatus();
+  if (DS[d].CD_changed==0xFF)
+    {
+#if MANY_SESSION
+#else
+      DS[d].diskstate_flags=0;
+#endif MANY_SESSION
+      DS[d].audio_state=0;
+      if (!st_diskok)
+       {
+         i=check_allowed1(func,subfunc);
+         if (i<0) return (-2);
+       }
+      else 
+       {
+         i=check_allowed3(func,subfunc);
+         if (i<0)
+           {
+             DS[d].CD_changed=1;
+             return (-15);
+           }
+       }
+    }
+  else
+    {
+      if (!st_diskok)
+       {
+#if MANY_SESSION
+#else
+         DS[d].diskstate_flags=0;
+#endif MANY_SESSION
+         DS[d].audio_state=0;
+         i=check_allowed1(func,subfunc);
+         if (i<0) return (-2);
+       }
+      else
+       { 
+         if (st_busy)
+           {
+             if (DS[d].audio_state!=audio_pausing)
+               {
+                 i=check_allowed2(func,subfunc);
+                 if (i<0) return (-2);
+               }
+           }
+         else
+           {
+             if (DS[d].audio_state==audio_playing) seek_pos_audio_end();
+             DS[d].audio_state=0;
+           }
+         if (!frame_size_valid)
+           {
+             i=DiskInfo();
+             if (i<0)
+               {
+#if MANY_SESSION
+#else
+                 DS[d].diskstate_flags=0;
+#endif MANY_SESSION
+                 DS[d].audio_state=0;
+                 i=check_allowed1(func,subfunc);
+                 if (i<0) return (-2);
+               }
+           }
+       }
+    }
+  return (0);
+}
+/*==========================================================================*/
+static int xx_PlayAudioMSF(int pos_audio_start,int pos_audio_end)
+{
+  int i;
+
+  if (DS[d].audio_state==audio_playing) return (-EINVAL);
+  clr_cmdbuf();
+  if (new_drive)
+    {
+      drvcmd[0]=0x0E;
+      flags_cmd_out = f_putcmd | f_respo2 | f_ResponseStatus |
+                       f_obey_p_check | f_wait_if_busy;
+    }
+  else
+    {
+      drvcmd[0]=0x0B;
+      flags_cmd_out = f_putcmd | f_respo2 | f_lopsta | f_getsta |
+                       f_ResponseStatus | f_obey_p_check | f_wait_if_busy;
+    }
+  drvcmd[1]=(pos_audio_start>>16)&0x00FF;
+  drvcmd[2]=(pos_audio_start>>8)&0x00FF;
+  drvcmd[3]=pos_audio_start&0x00FF;
+  drvcmd[4]=(pos_audio_end>>16)&0x00FF;
+  drvcmd[5]=(pos_audio_end>>8)&0x00FF;
+  drvcmd[6]=pos_audio_end&0x00FF;
+  response_count=0;
+  i=cmd_out();
+  return (i);
+}
+/*==========================================================================*/
+/*==========================================================================*/
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * ioctl support, adopted from scsi/sr_ioctl.c and mcd.c
+ */
+static int sbpcd_ioctl(struct inode *inode,struct file *file,
+                      u_int cmd, u_long arg)
+{
+  int i, st;
+
+  DPRINTF((DBG_IOC,"SBPCD: ioctl(%d, 0x%08lX, 0x%08lX)\n",
+                               MINOR(inode->i_rdev), cmd, arg));
+  if (!inode) return (-EINVAL);
+  st=GetStatus();
+  if (st<0) return (-EIO);
+  
+  if (!toc_valid)
+    {
+      i=DiskInfo();
+      if (i<0) return (-EIO);  /* error reading TOC */
+    }
+  
+  i=MINOR(inode->i_rdev);
+  if ( (i<0) || (i>=NR_SBPCD) )
+    {
+      DPRINTF((DBG_INF,"SBPCD: ioctl: bad device: %d\n", i));
+      return (-ENODEV);             /* no such drive */
+    }
+  switch_drive(i);
+
+
+  DPRINTF((DBG_IOC,"SBPCD: ioctl: device %d, request %04X\n",i,cmd));
+  switch (cmd)                 /* Sun-compatible */
+    {
+    case DDIOCSDBG:            /* DDI Debug */
+      if (! suser()) return (-EPERM);
+      i = verify_area(VERIFY_READ, (int *) arg, sizeof(int));
+      if (i>=0) i=sbpcd_dbg_ioctl(arg,1);
+      return (i);
+
+    case CDROMPAUSE:     /* Pause the drive */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMPAUSE entered.\n"));
+      /* pause the drive unit when it is currently in PLAY mode,         */
+      /* or reset the starting and ending locations when in PAUSED mode. */
+      /* If applicable, at the next stopping point it reaches            */
+      /* the drive will discontinue playing.                             */
+      switch (DS[d].audio_state)
+       {
+       case audio_playing:
+         i=xx_Pause_Resume(1);
+         if (i<0) return (-EIO);
+         DS[d].audio_state=audio_pausing;
+         i=xx_ReadSubQ();
+         if (i<0) return (-EIO);
+         DS[d].pos_audio_start=DS[d].SubQ_run_tot;
+         return (0);
+       case audio_pausing:
+         i=xx_Seek(DS[d].pos_audio_start,1);
+         if (i<0) return (-EIO);
+         return (0);
+       default:
+         return (-EINVAL);
+       }
+      
+    case CDROMRESUME: /* resume paused audio play */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMRESUME entered.\n"));
+      /* resume playing audio tracks when a previous PLAY AUDIO call has  */
+      /* been paused with a PAUSE command.                                */
+      /* It will resume playing from the location saved in SubQ_run_tot.  */
+      if (DS[d].audio_state!=audio_pausing) return -EINVAL;
+      i=xx_Pause_Resume(3);
+      if (i<0) return (-EIO);
+      DS[d].audio_state=audio_playing;
+      return (0);
+
+    case CDROMPLAYMSF:
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMPLAYMSF entered.\n"));
+#if WORKMAN
+      if (DS[d].audio_state==audio_playing)
+       {
+         i=xx_Pause_Resume(1);
+         if (i<0) return (-EIO);
+         i=xx_ReadSubQ();
+         if (i<0) return (-EIO);
+         DS[d].pos_audio_start=DS[d].SubQ_run_tot;
+         i=xx_Seek(DS[d].pos_audio_start,1);
+       }
+#else
+      if (DS[d].audio_state==audio_playing) return (-EINVAL);
+#endif
+      st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_msf));
+      if (st) return (st);
+      memcpy_fromfs(&msf, (void *) arg, sizeof(struct cdrom_msf));
+      /* values come as msf-bin */
+      DS[d].pos_audio_start = (msf.cdmsf_min0<<16) |
+                        (msf.cdmsf_sec0<<8) |
+                        msf.cdmsf_frame0;
+      DS[d].pos_audio_end = (msf.cdmsf_min1<<16) |
+                      (msf.cdmsf_sec1<<8) |
+                      msf.cdmsf_frame1;
+      DPRINTF((DBG_IOX,"SBPCD: ioctl: CDROMPLAYMSF %08X %08X\n",
+                              DS[d].pos_audio_start,DS[d].pos_audio_end));
+      i=xx_PlayAudioMSF(DS[d].pos_audio_start,DS[d].pos_audio_end);
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: xx_PlayAudioMSF returns %d\n",i));
+#if 0
+      if (i<0) return (-EIO);
+#endif 0
+      DS[d].audio_state=audio_playing;
+      return (0);
+
+    case CDROMPLAYTRKIND: /* Play a track.  This currently ignores index. */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMPLAYTRKIND entered.\n"));
+      if (DS[d].audio_state==audio_playing)
+       {
+         DPRINTF((DBG_IOX,"SBPCD: CDROMPLAYTRKIND: already audio_playing.\n"));
+         return (0);
+         return (-EINVAL);
+       }
+      st=verify_area(VERIFY_READ,(void *) arg,sizeof(struct cdrom_ti));
+      if (st<0)
+       {
+         DPRINTF((DBG_IOX,"SBPCD: CDROMPLAYTRKIND: verify_area error.\n"));
+         return (st);
+       }
+      memcpy_fromfs(&ti,(void *) arg,sizeof(struct cdrom_ti));
+      DPRINTF((DBG_IOX,"SBPCD: ioctl: trk0: %d, ind0: %d, trk1:%d, ind1:%d\n",
+            ti.cdti_trk0,ti.cdti_ind0,ti.cdti_trk1,ti.cdti_ind1));
+      if (ti.cdti_trk0<DS[d].n_first_track) return (-EINVAL);
+      if (ti.cdti_trk0>DS[d].n_last_track) return (-EINVAL);
+      if (ti.cdti_trk1<ti.cdti_trk0) ti.cdti_trk1=ti.cdti_trk0;
+      if (ti.cdti_trk1>DS[d].n_last_track) ti.cdti_trk1=DS[d].n_last_track;
+      DS[d].pos_audio_start=DS[d].TocBuffer[ti.cdti_trk0].address;
+      DS[d].pos_audio_end=DS[d].TocBuffer[ti.cdti_trk1+1].address;
+      i=xx_PlayAudioMSF(DS[d].pos_audio_start,DS[d].pos_audio_end);
+#if 0
+      if (i<0) return (-EIO);
+#endif 0
+      DS[d].audio_state=audio_playing;
+      return (0);
+           
+    case CDROMREADTOCHDR:        /* Read the table of contents header */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADTOCHDR entered.\n"));
+      tochdr.cdth_trk0=DS[d].n_first_track;
+      tochdr.cdth_trk1=DS[d].n_last_track;
+      st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_tochdr));
+      if (st) return (st);
+      memcpy_tofs((void *) arg, &tochdr, sizeof(struct cdrom_tochdr));
+      return (0);
+
+    case CDROMREADTOCENTRY:      /* Read an entry in the table of contents */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADTOCENTRY entered.\n"));
+      st=verify_area(VERIFY_READ, (void *) arg, sizeof(struct cdrom_tocentry));
+      if (st) return (st);
+      memcpy_fromfs(&tocentry, (void *) arg, sizeof(struct cdrom_tocentry));
+      i=tocentry.cdte_track;
+      if (i==CDROM_LEADOUT) i=DS[d].n_last_track+1;
+      else if (i<DS[d].n_first_track||i>DS[d].n_last_track) return (-EINVAL);
+      tocentry.cdte_adr=DS[d].TocBuffer[i].ctl_adr&0x0F;
+      tocentry.cdte_ctrl=(DS[d].TocBuffer[i].ctl_adr>>4)&0x0F;
+      tocentry.cdte_datamode=DS[d].TocBuffer[i].format;
+      if (tocentry.cdte_format==CDROM_MSF) /* MSF-bin required */
+       { tocentry.cdte_addr.msf.minute=(DS[d].TocBuffer[i].address>>16)&0x00FF;
+         tocentry.cdte_addr.msf.second=(DS[d].TocBuffer[i].address>>8)&0x00FF;
+         tocentry.cdte_addr.msf.frame=DS[d].TocBuffer[i].address&0x00FF;
+       }
+      else if (tocentry.cdte_format==CDROM_LBA) /* blk required */
+       tocentry.cdte_addr.lba=msf2blk(DS[d].TocBuffer[i].address);
+      else return (-EINVAL);
+      st=verify_area(VERIFY_WRITE,(void *) arg, sizeof(struct cdrom_tocentry));
+      if (st) return (st);
+      memcpy_tofs((void *) arg, &tocentry, sizeof(struct cdrom_tocentry));
+      return (0);
+
+    case CDROMSTOP:      /* Spin down the drive */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMSTOP entered.\n"));
+      i=DriveReset();
+#if WORKMAN
+      DS[d].CD_changed=0xFF;
+      DS[d].diskstate_flags=0;
+#endif WORKMAN
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: DriveReset returns %d\n",i));
+      DS[d].audio_state=0;
+      i=DiskInfo();
+      return (0);
+
+    case CDROMSTART:  /* Spin up the drive */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMSTART entered.\n"));
+      i=xx_SpinUp();
+      DS[d].audio_state=0;
+      return (0);
+      
+    case CDROMEJECT:
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMEJECT entered.\n"));
+      if (!new_drive) return (0);
+#if WORKMAN
+      DS[d].CD_changed=0xFF;
+      DS[d].diskstate_flags=0;
+#endif WORKMAN
+      i=yy_SpinDown();
+      if (i<0) return (-EIO);
+      DS[d].audio_state=0;
+      return (0);
+      
+    case CDROMVOLCTRL:   /* Volume control */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMVOLCTRL entered.\n"));
+      st=verify_area(VERIFY_READ,(void *) arg,sizeof(volctrl));
+      if (st) return (st);
+      memcpy_fromfs(&volctrl,(char *) arg,sizeof(volctrl));
+      DS[d].vol_chan0=0;
+      DS[d].vol_ctrl0=volctrl.channel0;
+      DS[d].vol_chan1=1;
+      DS[d].vol_ctrl1=volctrl.channel1;
+      i=xx_SetVolume();
+      return (0);
+
+    case CDROMSUBCHNL:   /* Get subchannel info */
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMSUBCHNL entered.\n"));
+      if ((st_spinning)||(!subq_valid)) { i=xx_ReadSubQ();
+                                         if (i<0) return (-EIO);
+                                       }
+      st=verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct cdrom_subchnl));
+      if (st)  return (st);
+      memcpy_fromfs(&SC, (void *) arg, sizeof(struct cdrom_subchnl));
+      if (DS[d].SubQ_audio==0x80) DS[d].SubQ_audio=CDROM_AUDIO_NO_STATUS;
+      SC.cdsc_audiostatus=DS[d].SubQ_audio;
+      SC.cdsc_adr=DS[d].SubQ_ctl_adr;
+      SC.cdsc_ctrl=DS[d].SubQ_ctl_adr>>4;
+      SC.cdsc_trk=bcd2bin(DS[d].SubQ_trk);
+      SC.cdsc_ind=bcd2bin(DS[d].SubQ_pnt_idx);
+      if (SC.cdsc_format==CDROM_LBA)
+       {
+         SC.cdsc_absaddr.lba=msf2blk(DS[d].SubQ_run_tot);
+         SC.cdsc_reladdr.lba=msf2blk(DS[d].SubQ_run_trk);
+       }
+      else /* not only if (SC.cdsc_format==CDROM_MSF) */
+       {
+         SC.cdsc_absaddr.msf.minute=(DS[d].SubQ_run_tot>>16)&0x00FF;
+         SC.cdsc_absaddr.msf.second=(DS[d].SubQ_run_tot>>8)&0x00FF;
+         SC.cdsc_absaddr.msf.frame=DS[d].SubQ_run_tot&0x00FF;
+         SC.cdsc_reladdr.msf.minute=(DS[d].SubQ_run_trk>>16)&0x00FF;
+         SC.cdsc_reladdr.msf.second=(DS[d].SubQ_run_trk>>8)&0x00FF;
+         SC.cdsc_reladdr.msf.frame=DS[d].SubQ_run_trk&0x00FF;
+       }
+      memcpy_tofs((void *) arg, &SC, sizeof(struct cdrom_subchnl));
+      DPRINTF((DBG_IOC,"SBPCD: CDROMSUBCHNL: %1X %02X %08X %08X %02X %02X %06X %06X\n",
+              SC.cdsc_format,SC.cdsc_audiostatus,
+              SC.cdsc_adr,SC.cdsc_ctrl,
+              SC.cdsc_trk,SC.cdsc_ind,
+              SC.cdsc_absaddr,SC.cdsc_reladdr));
+      return (0);
+
+    case CDROMREADMODE2:
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE2 requested.\n"));
+      return (-EINVAL);
+
+    case CDROMREADMODE1:
+      DPRINTF((DBG_IOC,"SBPCD: ioctl: CDROMREADMODE1 requested.\n"));
+      return (-EINVAL);
+      
+    default:
+      DPRINTF((DBG_IOX,"SBPCD: ioctl: unknown function request %04X\n", cmd));
+      return (-EINVAL);
+    } /* end switch(cmd) */
+}
+/*==========================================================================*/
+/*
+ *  Take care of the different block sizes between cdrom and Linux.
+ *  When Linux gets variable block sizes this will probably go away.
+ */
+static void sbp_transfer(void)
+{
+  long offs;
+  
+  while ( (CURRENT->nr_sectors > 0) &&
+         (CURRENT->sector/4 >= DS[d].sbp_first_frame) &&
+         (CURRENT->sector/4 <= DS[d].sbp_last_frame) )
+    {
+      offs = (CURRENT->sector - DS[d].sbp_first_frame * 4) * 512;
+      memcpy(CURRENT->buffer, DS[d].sbp_buf + offs, 512);
+      CURRENT->nr_sectors--;
+      CURRENT->sector++;
+      CURRENT->buffer += 512;
+    }
+}
+/*==========================================================================*/
+/*
+ *  We seem to get never an interrupt.
+ */
+#if SBPCD_USE_IRQ
+static void sbpcd_interrupt(int unused)
+{
+  int st;
+  
+  st = inb(CDi_status) & 0xFF;
+  DPRINTF((DBG_IRQ,"SBPCD: INTERRUPT received - CDi_status=%02X\n", st));
+}
+#endif SBPCD_USE_IRQ
+/*==========================================================================*/
+/*
+ * Called from the timer to check the results of the get-status cmd.
+ */
+static int sbp_status(void)
+{
+  int st;
+
+  st=ResponseStatus();
+  if (st<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: sbp_status: timeout.\n"));
+      return (0);
+    }
+
+  if (!st_spinning) DPRINTF((DBG_SPI,"SBPCD: motor got off - ignoring.\n"));
+
+  if (st_check) 
+    {
+      DPRINTF((DBG_INF,"SBPCD: st_check detected - retrying.\n"));
+      return (0);
+    }
+  if (!st_door_closed)
+    {
+      DPRINTF((DBG_INF,"SBPCD: door is open - retrying.\n"));
+      return (0);
+    }
+  if (!st_caddy_in)
+    {
+      DPRINTF((DBG_INF,"SBPCD: disk removed - retrying.\n"));
+      return (0);
+    }
+  if (!st_diskok) 
+    {
+      DPRINTF((DBG_INF,"SBPCD: !st_diskok detected - retrying.\n"));
+      return (0);
+    }
+  if (st_busy) 
+    {
+      DPRINTF((DBG_INF,"SBPCD: st_busy detected - retrying.\n"));
+      return (0);
+    }
+  return (1);
+}
+/*==========================================================================*/
+/*
+ *  I/O request routine, called from Linux kernel.
+ */
+static void do_sbpcd_request(void)
+{
+  u_int block;
+  int dev;
+  u_int nsect;
+  int i, status_tries, data_tries;
+  
+request_loop:
+
+  sti();
+
+  if ((CURRENT==NULL)||(CURRENT->dev<0)) return;
+  if (CURRENT -> sector == -1) return;
+
+  dev = MINOR(CURRENT->dev);
+  if ( (dev<0) || (dev>=NR_SBPCD) )
+    {
+      DPRINTF((DBG_INF,"SBPCD: do_request: bad device: %d\n", dev));
+      return;
+    }
+  switch_drive(dev);
+
+  INIT_REQUEST;
+  block = CURRENT->sector;
+  nsect = CURRENT->nr_sectors;
+
+  if (CURRENT->cmd != READ)
+    {
+      DPRINTF((DBG_INF,"SBPCD: bad cmd %d\n", CURRENT->cmd));
+      end_request(0);
+      goto request_loop;
+    }
+
+  DPRINTF((DBG_MUL,"SBPCD: read LBA %d\n", block/4));
+  sbp_transfer();
+
+  /* if we satisfied the request from the buffer, we're done. */
+
+  if (CURRENT->nr_sectors == 0)
+    {
+      end_request(1);
+      goto request_loop;
+    }
+
+  i=prepare(0,0); /* at moment not really a hassle check, but ... */
+  if (i!=0) DPRINTF((DBG_INF,"SBPCD: \"prepare\" tells error %d -- ignored\n", i));
+
+  if (!st_spinning) xx_SpinUp();
+
+  for (data_tries=3; data_tries > 0; data_tries--)
+    {
+      for (status_tries=3; status_tries > 0; status_tries--)
+       {
+         flags_cmd_out |= f_respo3;
+         xx_ReadStatus();
+         if (sbp_status() != 0) break;
+         sbp_sleep(1);    /* wait a bit, try again */
+       }
+      if (status_tries == 0)
+       {
+         DPRINTF((DBG_INF,"SBPCD: sbp_status: failed after 3 tries\n"));
+         break;
+       }
+
+      sbp_read_cmd();
+      sbp_sleep(0);
+      if (sbp_data() != 0)
+       {
+         end_request(1);
+         goto request_loop;
+       }
+    }
+  
+  end_request(0);
+  sbp_sleep(10);    /* wait a bit, try again */
+  goto request_loop;
+}
+/*==========================================================================*/
+/*
+ *  build and send the READ command.
+ *  Maybe it would be better to "set mode1" before ...
+ */
+static void sbp_read_cmd(void)
+{
+  int i;
+  int block;
+
+  DS[d].sbp_first_frame=DS[d].sbp_last_frame=-1;      /* purge buffer */
+  block=CURRENT->sector/4;
+
+  if (new_drive)
+    {
+#if MANY_SESSION
+      DPRINTF((DBG_MUL,"SBPCD: read MSF %08X\n", blk2msf(block)));
+      if ( (DS[d].f_multisession) && (multisession_valid) )
+       {
+         DPRINTF((DBG_MUL,"SBPCD: MultiSession: use %08X for %08X (msf)\n",
+                        blk2msf(DS[d].lba_multi+block),
+                         blk2msf(block)));
+         block=DS[d].lba_multi+block;
+       }
+#else
+      if ( (block==166) && (DS[d].f_multisession) && (multisession_valid) )
+       {
+         DPRINTF((DBG_MUL,"SBPCD: MultiSession: use %08X for %08X (msf)\n",
+                        blk2msf(DS[d].lba_multi+16),
+                         blk2msf(block)));
+         block=DS[d].lba_multi+16;
+       }
+#endif MANY_SESSION
+    }
+
+  if (block+SBP_BUFFER_FRAMES <= DS[d].CDsize_frm)
+    DS[d].sbp_read_frames = SBP_BUFFER_FRAMES;
+  else
+    {
+      DS[d].sbp_read_frames=DS[d].CDsize_frm-block;
+                                      /* avoid reading past end of data */
+      if (DS[d].sbp_read_frames < 1)
+       {
+         DPRINTF((DBG_INF,"SBPCD: requested frame %d, CD size %d ???\n",
+                       block, DS[d].CDsize_frm));
+         DS[d].sbp_read_frames=1;
+       }
+    }
+  DS[d].sbp_current = 0;
+
+  flags_cmd_out = f_putcmd |
+                  f_respo2 |
+                  f_ResponseStatus |
+                  f_obey_p_check;
+
+  if (!new_drive)
+    {
+      if (DS[d].drv_type>=drv_201)
+       {
+         lba2msf(block,&drvcmd[1]); /* msf-bcd format required */
+         bin2bcdx(&drvcmd[1]);
+         bin2bcdx(&drvcmd[2]);
+         bin2bcdx(&drvcmd[3]);
+       }
+      else
+       {
+         drvcmd[1]=(block>>16)&0x000000ff;
+         drvcmd[2]=(block>>8)&0x000000ff;
+         drvcmd[3]=block&0x000000ff;
+       }
+      drvcmd[4]=0;
+      drvcmd[5]=DS[d].sbp_read_frames;
+      drvcmd[6]=(DS[d].drv_type<drv_201)?0:2; /* flag "lba or msf-bcd format" */
+      drvcmd[0]=0x02;              /* "read frames" command for old drives */
+      flags_cmd_out |= f_lopsta|f_getsta|f_bit1;
+    }
+  else /* if new_drive */
+    {
+      lba2msf(block,&drvcmd[1]); /* msf-bin format required */
+      drvcmd[4]=0;
+      drvcmd[5]=0;
+      drvcmd[6]=DS[d].sbp_read_frames;
+      drvcmd[0]=0x10;              /* "read frames" command for new drives */
+    }
+#if SBPCD_DIS_IRQ
+  cli();
+#endif SBPCD_DIS_IRQ
+  for (i=0;i<7;i++) OUT(CDo_command,drvcmd[i]);
+#if SBPCD_DIS_IRQ
+  sti();
+#endif SBPCD_DIS_IRQ
+
+  return;
+}
+/*==========================================================================*/
+/*
+ *  Check the completion of the read-data command.  On success, read
+ *  the SBP_BUFFER_FRAMES * 2048 bytes of data from the disk into buffer.
+ */
+static int sbp_data(void)
+{
+  int i=0, j=0, frame;
+  u_int try=0;
+  u_long timeout;
+  u_char *p;
+  u_int data_tries = 0;
+  u_int data_waits = 0;
+  u_int data_retrying = 0;
+  int error_flag;
+
+  error_flag=0;
+
+  for (frame=DS[d].sbp_current;frame<DS[d].sbp_read_frames&&!error_flag; frame++)
+    {
+#if SBPCD_DIS_IRQ
+      cli();
+#endif SBPCD_DIS_IRQ
+      try=maxtim_data;
+      for (timeout=jiffies+100; ; )
+       {
+         for ( ; try!=0;try--)
+           {
+             j=inb(CDi_status);
+             if (!(j&s_not_data_ready)) break;
+             if (!(j&s_not_result_ready)) break;
+             if (!new_drive) if (j&s_attention) break;
+           }
+         if (try != 0 || timeout <= jiffies) break;
+         if (data_retrying == 0) data_waits++;
+         data_retrying = 1;
+         sbp_sleep(1);
+         try = 1;
+       }
+      if (try==0)
+       {
+         DPRINTF((DBG_INF,"SBPCD: sbp_data: CDi_status timeout.\n"));
+         error_flag++;
+         break;
+       }
+
+      if (j&s_not_data_ready)
+       {
+         if ((DS[d].ored_ctl_adr&0x40)==0)
+           DPRINTF((DBG_INF,"SBPCD: CD contains no data tracks.\n"));
+         else DPRINTF((DBG_INF,"SBPCD: sbp_data: DATA_READY timeout.\n"));
+         error_flag++;
+         break;
+       }
+      
+#if SBPCD_DIS_IRQ
+      sti();
+#endif SBPCD_DIS_IRQ
+
+      CLEAR_TIMER;
+      error_flag=0;
+      p = DS[d].sbp_buf + frame *  CD_FRAMESIZE;
+
+      if (sbpro_type) OUT(CDo_sel_d_i,0x01);
+      READ_DATA(CDi_data, p, CD_FRAMESIZE);
+      if (sbpro_type) OUT(CDo_sel_d_i,0x00);
+      DS[d].sbp_current++;
+
+      data_tries++;
+      data_retrying = 0;
+      if (data_tries >= 1000)
+       {
+         DPRINTF((DBG_INF,"SBPCD: info: %d waits in %d frames.\n",
+                       data_waits, data_tries));
+         data_waits = data_tries = 0;
+       }
+    }
+#if SBPCD_DIS_IRQ
+  sti();
+#endif SBPCD_DIS_IRQ
+  
+  if (error_flag)    /* must have been spurious D_RDY or (ATTN&&!D_RDY) */
+    {
+      DPRINTF((DBG_INF,"SBPCD: read aborted by drive\n"));
+      i=DriveReset();                /* ugly fix to prevent a hang */
+      return (0);
+    }
+
+  if (!new_drive)
+    {
+#if SBPCD_DIS_IRQ
+      cli();
+#endif SBPCD_DIS_IRQ
+      i=maxtim_data;
+      for (timeout=jiffies+100; timeout > jiffies; timeout--)
+       {
+         for ( ;i!=0;i--)
+           {
+             j=inb(CDi_status);
+             if (!(j&s_not_data_ready)) break;
+             if (!(j&s_not_result_ready)) break;
+             if (j&s_attention) break;
+           }
+         if (i != 0 || timeout <= jiffies) break;
+         sbp_sleep(0);
+         i = 1;
+       }
+      if (i==0) { DPRINTF((DBG_INF,"SBPCD: STATUS TIMEOUT AFTER READ")); }
+      if (!(j&s_attention))
+       {
+         DPRINTF((DBG_INF,"SBPCD: sbp_data: timeout waiting DRV_ATTN - retrying\n"));
+         i=DriveReset();  /* ugly fix to prevent a hang */
+#if SBPCD_DIS_IRQ
+         sti();
+#endif SBPCD_DIS_IRQ
+         return (0);
+       }
+#if SBPCD_DIS_IRQ
+      sti();
+#endif SBPCD_DIS_IRQ
+    }
+
+  do
+    {
+      if (!new_drive) xx_ReadStatus();
+      i=ResponseStatus();  /* builds status_byte, returns orig. status (old) or faked p_success_old (new) */
+      if (i<0) { DPRINTF((DBG_INF,"SBPCD: xx_ReadStatus error after read: %02X\n",
+                              DS[d].status_byte));
+                return (0);
+              }
+    }
+  while ((!new_drive)&&(!st_check)&&(!(i&p_success_old)));
+  if (st_check)
+    {
+      i=xx_ReadError();
+      DPRINTF((DBG_INF,"SBPCD: xx_ReadError was necessary after read: %02X\n",i));
+      return (0);
+    }
+
+  DS[d].sbp_first_frame = CURRENT -> sector / 4;
+  DS[d].sbp_last_frame = DS[d].sbp_first_frame + DS[d].sbp_read_frames - 1;
+  sbp_transfer();
+  return (1);
+}
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ *  Open the device special file.  Check that a disk is in. Read TOC.
+ */
+int sbpcd_open(struct inode *ip, struct file *fp)
+{
+  int i;
+
+  if (ndrives==0) return (-ENXIO);             /* no hardware */
+
+  i = MINOR(ip->i_rdev);
+  if ( (i<0) || (i>=NR_SBPCD) )
+    {
+      DPRINTF((DBG_INF,"SBPCD: open: bad device: %d\n", i));
+      return (-ENODEV);             /* no such drive */
+    }
+  switch_drive(i);
+
+  if (!st_spinning) xx_SpinUp();
+
+  flags_cmd_out |= f_respo2;
+  xx_ReadStatus();                         /* command: give 1-byte status */
+  i=ResponseStatus();
+  if (i<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: sbpcd_open: xx_ReadStatus timed out\n"));
+      return (-EIO);                  /* drive doesn't respond */
+    }
+  DPRINTF((DBG_STA,"SBPCD: sbpcd_open: status %02X\n", DS[d].status_byte));
+  if (!st_door_closed||!st_caddy_in)
+    {
+      DPRINTF((DBG_INF,"SBPCD: sbpcd_open: no disk in drive\n"));
+      return (-EIO);
+    }
+
+/*
+ * we could try to keep an "open" counter here and lock the door if 0->1.
+ * not done yet.
+ */
+
+  
+  if (!st_spinning) xx_SpinUp();
+
+  i=DiskInfo();
+  if ((DS[d].ored_ctl_adr&0x40)==0)
+    DPRINTF((DBG_INF,"SBPCD: CD contains no data tracks.\n"));
+  return (0);
+}
+/*==========================================================================*/
+/*
+ *  On close, we flush all sbp blocks from the buffer cache.
+ */
+static void sbpcd_release(struct inode * ip, struct file * file)
+{
+  int i;
+/*
+ * we could try to count down an "open" counter here
+ * and unlock the door if zero.
+ * not done yet.
+ */
+
+  i = MINOR(ip->i_rdev);
+  if ( (i<0) || (i>=NR_SBPCD) ) 
+    {
+      DPRINTF((DBG_INF,"SBPCD: release: bad device: %d\n", i));
+      return;
+    }
+  switch_drive(i);
+
+  DS[d].sbp_first_frame=DS[d].sbp_last_frame=-1;
+  sync_dev(ip->i_rdev);                    /* nonsense if read only device? */
+  invalidate_buffers(ip->i_rdev);
+  DS[d].diskstate_flags &= ~cd_size_bit;
+}
+/*==========================================================================*/
+/*
+ *
+ */
+static struct file_operations sbpcd_fops =
+{
+  NULL,                   /* lseek - default */
+  block_read,             /* read - general block-dev read */
+  block_write,            /* write - general block-dev write */
+  NULL,                   /* readdir - bad */
+  NULL,                   /* select */
+  sbpcd_ioctl,            /* ioctl */
+  NULL,                   /* mmap */
+  sbpcd_open,             /* open */
+  sbpcd_release           /* release */
+};
+/*==========================================================================*/
+/*
+ *  SBP interrupt descriptor
+ */
+#if SBPCD_USE_IRQ
+static struct sigaction sbpcd_sigaction =
+{
+  sbpcd_interrupt,
+  0,
+  SA_INTERRUPT,
+  NULL
+};
+#endif SBPCD_USE_IRQ
+/*==========================================================================*/
+/*
+ * accept "kernel command line" parameters 
+ * (suggested by Peter MacDonald with SLS 1.03)
+ *
+ * use: tell LILO:
+ *                 sbpcd=0x230,SoundBlaster
+ *             or
+ *                 sbpcd=0x300,LaserMate
+ *
+ * (upper/lower case sensitive here!!!).
+ *
+ * the address value has to be the TRUE CDROM PORT ADDRESS -
+ * not the soundcard base address.
+ *
+ */
+void sbpcd_setup(char *s, int *p)
+{
+  DPRINTF((DBG_INI,"SBPCD: sbpcd_setup called with %04X,%s\n",p[1], s));
+  if (!strcmp(s,str_sb)) sbpro_type=1;
+  else sbpro_type=0;
+  if (p[0]>0) sbpcd_ioaddr=p[1];
+
+  CDo_command=sbpcd_ioaddr;
+  CDi_info=sbpcd_ioaddr;
+  CDi_status=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;
+}
+/*==========================================================================*/
+/*
+ *  Test for presence of drive and initialize it.  Called at boot time.
+ */
+u_long sbpcd_init(u_long mem_start, u_long mem_end)
+{
+  int i=0, j=0;
+  int addr[2]={1, CDROM_PORT};
+  int port_index;
+   
+  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: \n"));
+  DPRINTF((DBG_WRN,"SBPCD: with your REAL address.\n"));
+  DPRINTF((DBG_WRN,"SBPCD: = = = = = = = = = = END of WARNING = = = = = = = = = =\n"));
+  DPRINTF((DBG_WRN,"SBPCD: \n"));
+  sti(); /* to avoid possible "printk" bug */
+
+  autoprobe[0]=sbpcd_ioaddr; /* possibly changed by kernel command line */
+  autoprobe[1]=sbpro_type; /* possibly changed by kernel command line */
+
+  for (port_index=0;port_index<NUM_AUTOPROBE;port_index+=2)
+    {
+      addr[1]=autoprobe[port_index];
+      if (check_region(addr[1],4)) continue;
+      DPRINTF((DBG_INI,"SBPCD: check_region done.\n"));
+      if (autoprobe[port_index+1]==0) type=str_lm;
+      else type=str_sb;
+      sbpcd_setup(type, addr);
+      DPRINTF((DBG_INF,"SBPCD: Trying to detect a %s CD-ROM drive at 0x%X.\n",
+                    type, CDo_command));
+
+      DPRINTF((DBG_INF,"SBPCD: - "));
+      sti(); /* to avoid possible "printk" bug */
+      i=check_drives();
+      DPRINTF((DBG_INI,"SBPCD: check_drives done.\n"));
+      sti(); /* to avoid possible "printk" bug */
+      if (i>=0) break; /* drive found */
+      printk ("\n");
+      sti(); /* to avoid possible "printk" bug */
+    } /* end of cycling through the set of possible I/O port addresses */
+
+  if (ndrives==0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: No drive found.\n"));
+      sti();
+      return (mem_start);
+    }
+
+  DPRINTF((DBG_INF,"SBPCD: %d %s CD-ROM drive(s) at 0x%04X.\n",
+          ndrives, type, CDo_command));
+  sti(); /* to avoid possible "printk" bug */
+  check_datarate();
+  DPRINTF((DBG_INI,"SBPCD: check_datarate done.\n"));
+  sti(); /* to avoid possible "printk" bug */
+
+  for (j=0;j<NR_SBPCD;j++)
+    {
+      if (DS[j].drv_minor==-1) continue;
+      switch_drive(j);
+      xy_DriveReset();
+      if (!st_spinning) xx_SpinUp();
+      DS[d].sbp_first_frame = -1;  /* First frame in buffer */
+      DS[d].sbp_last_frame = -1;   /* Last frame in buffer  */
+      DS[d].sbp_read_frames = 0;   /* Number of frames being read to buffer */
+      DS[d].sbp_current = 0;       /* Frame being currently read */
+      DS[d].CD_changed=1;
+      DS[d].frame_size=CD_FRAMESIZE;
+
+      xx_ReadStatus();
+      i=ResponseStatus();  /* returns orig. status or p_busy_new */
+      if (i<0)
+       DPRINTF((DBG_INF,"SBPCD: init: ResponseStatus returns %02X\n",i));
+      else
+       {
+         if (st_check)
+           {
+             i=xx_ReadError();
+             DPRINTF((DBG_INI,"SBPCD: init: xx_ReadError returns %d\n",i));
+             sti(); /* to avoid possible "printk" bug */
+           }
+       }
+      DPRINTF((DBG_INI,"SBPCD: init: first GetStatus: %d\n",i));
+      sti(); /* to avoid possible "printk" bug */
+      if (DS[d].error_byte==aud_12)
+       {
+         do { i=GetStatus();
+              DPRINTF((DBG_INI,"SBPCD: init: second GetStatus: %02X\n",i));
+              sti(); /* to avoid possible "printk" bug */
+              if (i<0) break;
+              if (!st_caddy_in) break;
+            }
+         while (!st_diskok);
+       }
+      i=SetSpeed();
+      if (i>=0) DS[d].CD_changed=1;
+    }
+
+  if (sbpro_type)
+    {
+      OUT(MIXER_addr,MIXER_CD_Volume);
+      OUT(MIXER_data,0xCC); /* one nibble per channel */
+    }
+  
+  if (register_blkdev(MATSUSHITA_CDROM_MAJOR, "sbpcd", &sbpcd_fops) != 0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: Can't get MAJOR %d for Matsushita CDROM\n",
+              MATSUSHITA_CDROM_MAJOR));
+      sti(); /* to avoid possible "printk" bug */
+      return (mem_start);
+    }
+  blk_dev[MATSUSHITA_CDROM_MAJOR].request_fn = DEVICE_REQUEST;
+  read_ahead[MATSUSHITA_CDROM_MAJOR] = 4; /* just one frame */ 
+  
+  snarf_region(CDo_command,4);
+
+#if SBPCD_USE_IRQ
+  if (irqaction(SBPCD_INTR_NR, &sbpcd_sigaction))
+    {
+      DPRINTF((DBG_INF,"SBPCD: Can't get IRQ%d for sbpcd driver\n",
+              SBPCD_INTR_NR));
+      sti(); /* to avoid possible "printk" bug */
+    }
+#endif SBPCD_USE_IRQ
+
+/*
+ * allocate memory for the frame buffers
+ */ 
+  for (j=0;j<NR_SBPCD;j++)
+    {
+      if (DS[j].drv_minor==-1) continue;
+      DS[j].sbp_buf=(u_char *)mem_start;
+      mem_start += SBP_BUFFER_FRAMES*CD_FRAMESIZE;
+    }
+  DPRINTF((DBG_INF,"SBPCD: init done.\n"));
+  sti(); /* to avoid possible "printk" bug */
+  return (mem_start);
+}
+/*==========================================================================*/
+/*
+ * adopted from sr.c
+ *
+ * Check if the media has changed in the CD-ROM drive.
+ * used externally (isofs/inode.c) - but still does not work.
+ *
+ */
+int check_sbpcd_media_change(int full_dev, int unused_minor)
+{
+  int st;
+
+  if (MAJOR(full_dev) != MATSUSHITA_CDROM_MAJOR) 
+    {
+      DPRINTF((DBG_INF,"SBPCD: media_check: invalid device.\n"));
+      return (-1);
+    }
+  
+  xx_ReadStatus();                         /* command: give 1-byte status */
+  st=ResponseStatus();
+  DPRINTF((DBG_CHK,"SBPCD: media_check: %02X\n",DS[d].status_byte));
+  if (st<0)
+    {
+      DPRINTF((DBG_INF,"SBPCD: media_check: ResponseStatus error.\n"));
+      return (1); /* status not obtainable */
+    }
+  if (DS[d].CD_changed==0xFF) DPRINTF((DBG_CHK,"SBPCD: media_check: \"changed\" assumed.\n"));
+  if (!st_spinning) DPRINTF((DBG_CHK,"SBPCD: media_check: motor off.\n"));
+  if (!st_door_closed)
+    {
+      DPRINTF((DBG_CHK,"SBPCD: media_check: door open.\n"));
+      DS[d].CD_changed=0xFF;
+    }
+  if (!st_caddy_in)
+    {
+      DPRINTF((DBG_CHK,"SBPCD: media_check: no disk in drive.\n"));
+      DS[d].CD_changed=0xFF;
+    }
+  if (!st_diskok) DPRINTF((DBG_CHK,"SBPCD: media_check: !st_diskok.\n"));
+  
+#if 0000
+  if (DS[d].CD_changed==0xFF)
+    {
+      DS[d].CD_changed=1;
+      return (1); /* driver had a change detected before */
+    }
+#endif 0000 /* seems to give additional errors at the moment */
+
+  if (!st_diskok) return (1); /* disk not o.k. */
+  if (!st_caddy_in) return (1); /* disk removed */
+  if (!st_door_closed) return (1); /* door open */
+  return (0);
+}
+/*==========================================================================*/
index 690250b93c24e22fc15510305ff6144b4fd8cde9..2a72deb585294ffb225fb838e96cd89783165a08 100644 (file)
@@ -213,7 +213,7 @@ static int screen_size = 0;
 #define VT102ID "\033[?6c"
 
 static unsigned char * translations[] = {
-/* 8-bit Latin-1 mapped to the PC charater set: '\0' means non-printable */
+/* 8-bit Latin-1 mapped to the PC character set: '\0' means non-printable */
 (unsigned char *)
        "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
        "\0\0\0\0\0\0\0\0\0\0\376\0\0\0\0\0"
index 8f4a744f24c9a4ac689843b5d92db515e56e5239..c443c3aece6a2e7e739666c1c2b44dc7663637cb 100644 (file)
@@ -213,24 +213,9 @@ static int set_termios_2(struct tty_struct * tty, struct termios * termios)
                wake_up_interruptible(&tty->link->secondary.proc_list);
        }
 
-#if 0
-       /* puting mpty's into echo mode is very bad, and I think under
-          some situations can cause the kernel to do nothing but
-          copy characters back and forth. -RAB */
-       /* This can no longer happen because a set_termios is redirected
-          to the pty slave.  -- jrs */
-       if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO;
-#endif
-
        unset_locked_termios(tty->termios, &old_termios,
                             termios_locked[tty->line]);
 
-#if 0
-       retval = tty_set_ldisc(tty, tty->termios->c_line);
-       if (retval)
-               return retval;
-#endif
-
        if (tty->set_termios)
                (*tty->set_termios)(tty, &old_termios);
 
index 882a654610ecef38ab07019d2b6a74fe1ecafc8b..cb9bbed355c034475e28e40bfa37148ebc19ac0c 100644 (file)
@@ -1,9 +1,12 @@
 #
-# Set any special options here.  Most drivers will autoprobe/autoIRQ
-# if you set the address or IRQ to zero, so we do that by default.
-# Cards and options supported:
+# This file is used for selecting non-standard netcard options, and
+# need not be modified for typical use.
+#
+# Drivers are *not* selected in this file, but rather with files
+# automatically generated during the top-level kernel configuration.
+#
+# Special options supported, indexed by their 'config' name:
 #
-#  EI_DEBUG            Set the debugging level for 8390-based boards
 #  CONFIG_WD80x3       The Western Digital (SMC) WD80x3 driver
 #      WD_SHMEM=xxx    Forces the address of the shared memory
 #      WD_no_mapout    Don't map out the shared memory (faster, but
 #      rw_bugfix       Patch an obscure bug with a version of the 8390.
 #  CONFIG_HPLAN                The HP-LAN driver (for 8390-based boards only).
 #      rw_bugfix       Fix the same obscure bug.
-#  CONFIG_EL1          The 3c501 driver (just joking, never released)
 #  CONFIG_EL2          The 3c503 EtherLink II driver
 #      EL2_AUI         Default to the AUI port instead of the BNC port
 #      no_probe_nonshared_memory  Don't probe for programmed-I/O boards.
 #      EL2MEMTEST      Test shared memory at boot-time.
-#  CONFIG_EL3
-#      EL3_DEBUG       Set the debugging message level.
-#  CONFIG_AT1500
-#      LANCE_DEBUG     Set the debugging message level.
-#      DEFAULT_DMA     Change the default DMA to other than 5.
 #  CONFIG_PLIP         The Crynwr-protocol PL/IP driver
+#      INITIALTIMEOUTFACTOR    Timing parameters.
+#      MAXTIMEOUTFACTOR
 #  D_LINK              The D-Link DE-600 Portable Ethernet Adaptor.
-#        D_LINK_IO     The D-Link I/O address (0x378 == default)
-#        D_LINK_IRQ    The D-Link IRQ number to use (IRQ7 == default)
+#        D_LINK_IO     The D-Link I/O address (0x378 == typical)
+#        D_LINK_IRQ    The D-Link IRQ number to use (IRQ7 == typical)
 #        D_LINK_DEBUG  Enable or disable D-Link debugging
 #
 
-OPTS           = #-DEI8390=0 -DEI8390_IRQ=0
-WD_OPTS                = #-DWD_SHMEM=0
-EL2_OPTS       = #-UEL2_AUI
+# The following options exist, but cannot be set in this file.
+#  lance.c
+#      LANCE_DMA       Change the default DMA to other than DMA5.
+#  8390.c
+#      NO_PINGPONG     Disable ping-pong transmit buffers.
+
+
+# Most drivers also have a *_DEBUG setting that may be adjusted.
+# The 8390 drivers share the EI_DEBUG settting.
+
+# General options for Space.c
+OPTS           = # -DETH0_ADDR=0x300 -DETH0_IRQ=11
+
+WD_OPTS                = #-DWD_SHMEM=0xDD000
+EL2_OPTS       = #-DEL2_AUI
 NE_OPTS                =
 HP_OPTS                =
 PLIP_OPTS      =
+
+# The following are the only parameters that must be set in this file.
 DL_OPTS                = -DD_LINK_IO=0x378 -DD_LINK_IRQ=7 -UD_LINK_DEBUG
-AT_OPTS                = # -DLANCE_DMA=5
diff --git a/drivers/net/README.8390 b/drivers/net/README.8390
deleted file mode 100644 (file)
index 87af97b..0000000
+++ /dev/null
@@ -1,96 +0,0 @@
-
-These drivers support all common 8390-based ethernet boards.  Currently
-"common" is defined as:
-
-       3Com Products:
-*      3Com 3c503      Board loaned by Chance Reschke, USRA.edu (thanks!)
-       3Com 3c503/16   and excellent documentation provided by 3Com.
-
-       Clones-n-things
-       NE1000          Novell and Eagle are useless for documentation,
-*      NE2000          but copied the designs directly from NatSemi;->.
-
-       WD/SMC products
-       WD8003
-*      WD8013          Board loaned by Russ Nelson, Crynwr Software.  Thanks!
-
-* I've seen it work myself!
-
-There is support for the following boards, but since I've only been
-able to borrow a thinnet of an HP ethercard I've relied upon reports
-from others:
-
-       HP LAN adaptors
-**     HP27245
-**     HP27247
-**     HP27250
-
-Thanks are due to the dozens of alpha testers.  Special thanks are due
-to Chance Reschke <@usra.edu> and Russ Nelson <@crynwr.com> for
-loaning me ethercards.
-
-The following addresses are autoprobed, in this order:
-wd.c:  0x300, 0x280, 0x380, 0x240
-3c503: 0x300, 0x310, 0x330, 0x350, 0x250, 0x280, 0x2a0, 0x2e0
-ne.c:  0x300, 0x280, 0x320, 0x340, 0x360
-hp.c:  0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240
-
-80x3 clones that are reported to work:
-       LANNET LEC-45
-
-"NE2000" clones that are reported to work:
-       Alta Combo(NE2000 clone)
-       Aritsoft LANtastic AE-2 (NE2000 clone w/ extra memory)
-       Asante Etherpak 2001/2003
-       D-Link Ethernet II
-       LTC E-NET/16 P/N: 8300-200-002 (lipka@lip.hanse.de)
-       Network Solutions HE-203
-       SVEC 4 Dimension Ethernet
-       4-Dimension FD0490 EtherBoard16
-       Cabletron products:
-        E1010          No ID PROM and sketchy info from Ctron means you'll
-        E1010-x        have to compile-in information about your board.
-        E2010
-        E2010-x
-               N.B. The E2100 will not work with Linux until Cabletron
-               releases the programming information!
-
-Important defines
-
-For Space.c
-#define EI8390 0               /* The base address of your ethercard.  */
-#define EI8390_IRQ 0           /* and the interrupt you want to use.   */
-                               /* '0' means autoconfigure              */
-For 8390.c
-#define EI_DEBUG 2             /* Use '0' for no messages.             */
-#define EL2                    /* For the 3c503 driver.                */
-#define NE2000                 /* For the NE1000/NE2000/Ctron driver.  */
-#define WD80x3                 /* For the WD8003/WD8013 driver.        */
-#define HPLAN                  /* For the HP27xxx driver.              */
-
-For the individual drivers
-
-EI8390 Define (probably in autoconf.h or config.site.h) this to the base
-       address of your ethernet card.
-EI8390_IRQ Define (probably in autoconf.h or config.site.h) this to the
-       IRQ line of your ethernet card.  Most drivers convert a IRQ2 to an
-       IRQ9 for you, so don't be surprised.
-EI_DEBUG   Set to the desired numeric debugging level.  Use 3 or
-       greater when actively debugging a problem, '1' for a
-       casual interest in what's going on, and '0' for normal
-       use. 
-NO_PINGPONG
-       Define this if you don't want ping-pong transmit buffers.
-EL2_AUI
-       Define for this if you are using the 3c503 and use the AUI/DIX
-       connector rather than the built-in thin-net transceiver.
-WD_SHMEM
-       Define this to override the shared memory address used by the
-       WD driver.  This should only be necessary for jumpered ethercards.
-
-If you have a Cabletron ethercard you might want to look at ne.c:neprobe()
-for info on how to enable more packet buffer space.
-
-ETHERLINK1_IRQ
-ETHERLINK1 Define these to the base address and IRQ of a 3c501 (NOT 3c503)
-       card. Refer to net/tcp/Space.c.
index 9ad85fd78c9d86706c222164d43ac9a987b119dc..e198d55755e79e79f6f18f8d7600606863148e4f 100644 (file)
@@ -17,7 +17,7 @@
 /* Routines for the NatSemi-based designs (NE[12]000). */
 
 static char *version =
-    "ne.c:v0.99-15 1/19/93 Donald Becker (becker@super.org)\n";
+    "ne.c:v0.99-14a 12/3/93 Donald Becker (becker@super.org)\n";
 
 #include <linux/config.h>
 #include <linux/kernel.h>
@@ -123,10 +123,6 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
 
     printk("NE*000 ethercard probe at %#3x:", ioaddr);
 
-    /* First hard-reset the ethercard. */
-    i = inb_p(ioaddr + NE_RESET);
-    outb_p(i, ioaddr + NE_RESET);
-
     /* Read the 16 bytes of station address prom, returning 1 for
        an eight-bit interface and 2 for a 16-bit interface.
        We must first initialize registers, similar to NS8390_init(eifdev, 0).
@@ -162,8 +158,8 @@ static int neprobe1(int ioaddr, struct device *dev, int verbose)
        /* We must set the 8390 for word mode, AND RESET IT. */
        int tmp;
        outb_p(0x49, ioaddr + EN0_DCFG);
-       tmp = inb_p(ioaddr + NE_RESET);
-       outb(tmp, ioaddr + NE_RESET);
+       tmp = inb_p(NE_BASE + NE_RESET);
+       outb(tmp, NE_BASE + NE_RESET);
        /* Un-double the SA_prom values. */
        for (i = 0; i < 16; i++)
            SA_prom[i] = SA_prom[i+i];
index 550d792d08c327eaa18b9524d83740abd6be4264..d800d59ffcda1ba8318492307435d19585f714f3 100644 (file)
@@ -30,6 +30,9 @@ extern int check_cdu31a_media_change(int, int);
 #if defined(CONFIG_MCD)
 extern int check_mcd_media_change(int, int);
 #endif
+#if defined (CONFIG_SBPCD)
+extern int check_sbpcd_media_change(int, int);
+#endif CONFIG_SBPCD
 
 #ifdef LEAK_CHECK
 static int check_malloc = 0;
@@ -288,6 +291,13 @@ struct super_block *isofs_read_super(struct super_block *s,void *data,
                  goto out;
        }
 #endif
+#if defined(CONFIG_SBPCD)
+       if (MAJOR(s->s_dev) == MATSUSHITA_CDROM_MAJOR) {
+               if (check_sbpcd_media_change(s->s_dev,0))
+                 goto out;
+       };
+#endif CONFIG_SBPCD
+
        return s;
  out: /* Kick out for various error conditions */
        brelse(bh);
index b7dc800fa0f8a8cdeb21974bbe80cc5cd8d52823..88a55917e4730f4c91bb8a16def8f70f6d3263d3 100644 (file)
--- a/fs/open.c
+++ b/fs/open.c
@@ -225,6 +225,7 @@ asmlinkage int sys_fchdir(unsigned int fd)
                return -EACCES;
        iput(current->pwd);
        current->pwd = inode;
+       inode->i_count++;
        return (0);
 }
 
index 9990250b987481adabb2353afbadf4a3a8174d65..8b3a8ab2f3d00740a7c14a7dd6c62832c8b8523b 100644 (file)
@@ -42,7 +42,7 @@
  * 22 -                        (at2disk)
  * 23 -                        mitsumi cdrom
  * 24 -                               sony535 cdrom
- * 25 -
+ * 25 -                        matsushita cdrom       minors 0..3
  * 26 -
  * 27 - qic117 tape
  */
@@ -71,6 +71,7 @@
 /* unused: 22 */
 #define MITSUMI_CDROM_MAJOR 23
 #define CDU535_CDROM_MAJOR 24
+#define MATSUSHITA_CDROM_MAJOR 25
 #define QIC117_TAPE_MAJOR 27
 
 /*
diff --git a/include/linux/sbpcd.h b/include/linux/sbpcd.h
new file mode 100644 (file)
index 0000000..880781f
--- /dev/null
@@ -0,0 +1,473 @@
+/*
+ * sbpcd.h   Specify interface address and interface type here.
+ */
+
+/*
+ * these definitions can get overridden by the kernel command line
+ * ("lilo boot option"). Examples:
+ *                                 sbpcd=0x230,SoundBlaster
+ *                             or
+ *                                 sbpcd=0x300,LaserMate
+ * these strings are case sensitive !!!
+ */
+
+/* 
+ * change this to select the type of your interface board:
+ *
+ * set SBPRO to 1 for "true" SoundBlaster card
+ * set SBPRO to 0 for "poor" (no sound) interface cards
+ *                and for "compatible" soundcards.
+ *
+ * most "compatible" sound boards like Galaxy need to set SBPRO to 0 !!!
+ * if SBPRO gets set wrong, the drive will get found - but any
+ * data access will give errors (audio access will work).
+ * The OmniCD interface card from CreativeLabs needs SBPRO 1.
+ *
+ * mail to emoenke@gwdg.de if your "compatible" card needs SBPRO 1
+ * (currently I do not know any "compatible" with SBPRO 1)
+ * then I can include better information with the next release.
+ */
+#define SBPRO     1
+
+/*
+ * put your CDROM port base address here:
+ * SBPRO addresses typically are 0x0230 (=0x220+0x10), 0x0250, ...
+ * LASERMATE (CI-101P) adresses typically are 0x0300, 0x0310, ...
+ * there are some soundcards on the market with 0x0630, 0x0650, ...
+ *
+ * obey! changed against v0.4 !!!
+ * for SBPRO cards, specify the CDROM address - no longer the audio address! 
+ * example: if your SBPRO audio address is 0x220, specify 0x230.
+ *
+ * a fill-in is not always necessary - the driver does auto-probing now,
+ * with the here specified address first...
+ */
+#define CDROM_PORT 0x0230
+
+
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * nothing to change below here if you are not experimenting
+ */
+/*==========================================================================*/
+/*==========================================================================*/
+/*
+ * Debug output levels
+ */
+#define DBG_INF                1       /* necessary information */
+#define DBG_IRQ                2       /* interrupt trace */
+#define DBG_REA                3       /* "read" status trace */
+#define DBG_CHK                4       /* "media check" trace */
+#define DBG_TIM                5       /* datarate timer test */
+#define DBG_INI                6       /* initialization trace */
+#define DBG_TOC                7       /* tell TocEntry values */
+#define DBG_IOC         8      /* ioctl trace */
+#define DBG_STA                9       /* "ResponseStatus" trace */
+#define DBG_ERR                10      /* "xx_ReadError" trace */
+#define DBG_CMD                11      /* "cmd_out" trace */
+#define DBG_WRN                12      /* give explanation before auto-probing */
+#define DBG_MUL         13      /* multi session code test */
+#define DBG_ID         14      /* "drive_id !=0" test code */
+#define DBG_IOX                15      /* some special information */
+#define DBG_DID                16      /* drive ID test */
+#define DBG_RES                17      /* drive reset info */
+#define DBG_SPI                18      /* SpinUp test */
+#define DBG_000                19      /* unnecessary information */
+
+/*==========================================================================*/
+/*==========================================================================*/
+
+/*
+ * bits of flags_cmd_out:
+ */
+#define f_respo3 0x100
+#define f_putcmd 0x80
+#define f_respo2 0x40
+#define f_lopsta 0x20
+#define f_getsta 0x10
+#define f_ResponseStatus 0x08
+#define f_obey_p_check 0x04
+#define f_bit1 0x02
+#define f_wait_if_busy 0x01
+
+/*
+ * diskstate_flags:
+ */
+#define upc_bit 0x40
+#define volume_bit 0x20
+#define toc_bit 0x10
+#define multisession_bit 0x08
+#define cd_size_bit 0x04
+#define subq_bit 0x02
+#define frame_size_bit 0x01
+
+/*
+ * disk states (bits of diskstate_flags):
+ */
+#define upc_valid (DS[d].diskstate_flags&upc_bit)
+#define volume_valid (DS[d].diskstate_flags&volume_bit)
+#define toc_valid (DS[d].diskstate_flags&toc_bit)
+#define multisession_valid (DS[d].diskstate_flags&multisession_bit)
+#define cd_size_valid (DS[d].diskstate_flags&cd_size_bit)
+#define subq_valid (DS[d].diskstate_flags&subq_bit)
+#define frame_size_valid (DS[d].diskstate_flags&frame_size_bit)
+
+
+/*
+ * bits of the status_byte (result of xx_ReadStatus):
+ */
+#define p_door_closed 0x80
+#define p_caddy_in 0x40
+#define p_spinning 0x20
+#define p_check 0x10
+#define p_busy_new 0x08
+#define p_door_locked 0x04
+#define p_bit_1 0x02
+#define p_disk_ok 0x01
+/*
+ * "old" drives status result bits:
+ */
+#define p_caddin_old 0x40
+#define p_success_old 0x08
+#define p_busy_old 0x04
+
+/*
+ * used drive states:
+ */
+#define st_door_closed (DS[d].status_byte&p_door_closed)
+#define st_caddy_in (DS[d].status_byte&p_caddy_in)
+#define st_spinning (DS[d].status_byte&p_spinning)
+#define st_check (DS[d].status_byte&p_check)
+#define st_busy (DS[d].status_byte&p_busy_new)
+#define st_door_locked (DS[d].status_byte&p_door_locked)
+#define st_diskok (DS[d].status_byte&p_disk_ok)
+
+/*
+ * bits of the CDi_status register:
+ */
+#define s_not_result_ready 0x04  /* 0: "result ready" */
+#define s_not_data_ready 0x02    /* 0: "data ready"   */
+#define s_attention 0x01         /* 1: "attention required" */
+/*
+ * usable as:
+ */
+#define DRV_ATTN               ((inb(CDi_status)&s_attention)!=0)
+#define DATA_READY             ((inb(CDi_status)&s_not_data_ready)==0)
+#define RESULT_READY           ((inb(CDi_status)&s_not_result_ready)==0)
+
+/*
+ * drive types (firmware versions):
+ */
+#define drv_199 0       /* <200 */
+#define drv_200 1       /* <201 */
+#define drv_201 2       /* <210 */
+#define drv_210 3       /* <211 */
+#define drv_211 4       /* <300 */
+#define drv_300 5       /* else */
+#define drv_099 0x10    /* new,  <100 */
+#define drv_100 0x11    /* new, >=100 */
+#define drv_new 0x10    /* all new drives have that bit set */
+#define drv_old 0x00    /*  */
+
+/*
+ * drv_099 and drv_100 are the "new" drives
+ */
+#define new_drive (DS[d].drv_type&0x10)
+
+/*
+ * audio states:
+ */
+#define audio_playing 2
+#define audio_pausing 1
+
+/*
+ * drv_pattern, drv_options:
+ */
+#define speed_auto 0x80
+#define speed_300 0x40
+#define speed_150 0x20
+#define sax_a 0x04
+#define sax_xn2 0x02
+#define sax_xn1 0x01
+
+/*
+ * values of cmd_type (0 else):
+ */
+#define cmd_type_READ_M1  0x01 /* "data mode 1": 2048 bytes per frame */
+#define cmd_type_READ_M2  0x02 /* "data mode 2": 12+2048+280 bytes per frame */
+#define cmd_type_READ_SC  0x04 /* "subchannel info": 96 bytes per frame */
+
+/*
+ * sense byte: used only if new_drive
+ *                  only during cmd 09 00 xx ah al 00 00
+ *
+ *          values: 00
+ *                  82
+ *                  xx from infobuf[0] after 85 00 00 00 00 00 00
+ */
+
+
+#define CD_MINS                   75  /* minutes per CD                  */
+#define CD_SECS                   60  /* seconds per minutes             */
+#define CD_FRAMES                 75  /* frames per second               */
+#define CD_FRAMESIZE            2048  /* bytes per frame, data mode      */
+#define CD_FRAMESIZE_XA                2340  /* bytes per frame, "xa" mode      */
+#define CD_FRAMESIZE_RAW        2352  /* bytes per frame, "raw" mode     */
+#define CD_BLOCK_OFFSET          150  /* offset of first logical frame   */
+
+
+/* audio status (bin) */
+#define aud_00 0x00 /* Audio status byte not supported or not valid */
+#define audx11 0x0b /* Audio play operation in progress             */
+#define audx12 0x0c /* Audio play operation paused                  */
+#define audx13 0x0d /* Audio play operation successfully completed  */
+#define audx14 0x0e /* Audio play operation stopped due to error    */
+#define audx15 0x0f /* No current audio status to return            */
+
+/* audio status (bcd) */
+#define aud_11 0x11 /* Audio play operation in progress             */
+#define aud_12 0x12 /* Audio play operation paused                  */
+#define aud_13 0x13 /* Audio play operation successfully completed  */
+#define aud_14 0x14 /* Audio play operation stopped due to error    */
+#define aud_15 0x15 /* No current audio status to return            */
+
+/*============================================================================
+==============================================================================
+
+COMMAND SET of "old" drives like CR-521, CR-522
+               (the CR-562 family is different):
+
+No.    Command                        Code
+--------------------------------------------
+
+Drive Commands:
+ 1     Seek                            01      
+ 2     Read Data                       02
+ 3     Read XA-Data                    03
+ 4     Read Header                     04
+ 5     Spin Up                         05
+ 6     Spin Down                       06
+ 7     Diagnostic                      07
+ 8     Read UPC                        08
+ 9     Read ISRC                       09
+10     Play Audio                      0A
+11     Play Audio MSF                  0B
+12     Play Audio Track/Index          0C
+
+Status Commands:
+13     Read Status                     81      
+14     Read Error                      82
+15     Read Drive Version              83
+16     Mode Select                     84
+17     Mode Sense                      85
+18     Set XA Parameter                86
+19     Read XA Parameter               87
+20     Read Capacity                   88
+21     Read SUB_Q                      89
+22     Read Disc Code                  8A
+23     Read Disc Information           8B
+24     Read TOC                        8C
+25     Pause/Resume                    8D
+26     Read Packet                     8E
+27     Read Path Check                 00
+all numbers (lba, msf-bin, msf-bcd, counts) to transfer high byte first
+
+mnemo     7-byte command        #bytes response (r0...rn)
+________ ____________________  ____ 
+
+Read Status:
+status:  81.                    (1)  one-byte command, gives the main
+                                                          status byte
+Read Error:
+check1:  82 00 00 00 00 00 00.  (6)  r1: audio status
+
+Read Packet:
+check2:  8e xx 00 00 00 00 00. (xx)  gets xx bytes response, relating
+                                        to commands 01 04 05 07 08 09
+
+Play Audio:
+play:    0a ll-bb-aa nn-nn-nn.  (0)  play audio, ll-bb-aa: starting block (lba),
+                                                 nn-nn-nn: #blocks
+Play Audio MSF:
+         0b mm-ss-ff mm-ss-ff   (0)  play audio from/to
+
+Play Audio Track/Index:
+         0c ...
+
+Pause/Resume:
+pause:   8d pr 00 00 00 00 00.  (0)  pause (pr=00) 
+                                     resume (pr=80) audio playing
+
+Mode Select:
+         84 00 nn-nn ??-?? 00   (0)  nn-nn: 2048 or 2340
+                                     possibly defines transfer size
+
+set_vol: 84 83 00 00 sw le 00.  (0)  sw(itch): lrxxxxxx (off=1)
+                                     le(vel): min=0, max=FF, else half
+                                    (firmware 2.11)
+
+Mode Sense:
+get_vol: 85 03 00 00 00 00 00.  (2)  tell current audio volume setting
+
+Read Disc Information:
+tocdesc: 8b 00 00 00 00 00 00.  (6)  read the toc descriptor ("msf-bin"-format)
+
+Read TOC:
+tocent:  8c fl nn 00 00 00 00.  (8)  read toc entry #nn
+                                       (fl=0:"lba"-, =2:"msf-bin"-format)
+
+Read Capacity:
+capacit: 88 00 00 00 00 00 00.  (5)  "read CD-ROM capacity"
+
+
+Read Path Check:
+ping:    00 00 00 00 00 00 00.  (2)  r0=AA, r1=55
+                                     ("ping" if the drive is connected)
+
+Read Drive Version:
+ident:   83 00 00 00 00 00 00. (12)  gives "MATSHITAn.nn" 
+                                     (n.nn = 2.01, 2.11., 3.00, ...)
+
+Seek:
+seek:    01 00 ll-bb-aa 00 00.  (0)  
+seek:    01 02 mm-ss-ff 00 00.  (0)  
+
+Read Data:
+read:    02 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2048 bytes,
+                                     starting at block xx-xx-xx  
+                                     fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
+
+Read XA-Data:
+read:    03 xx-xx-xx nn-nn fl. (??)  read nn-nn blocks of 2340 bytes, 
+                                     starting at block xx-xx-xx  
+                                     fl=0: "lba"-, =2:"msf-bcd"-coded xx-xx-xx
+
+Read SUB_Q:
+         89 fl 00 00 00 00 00. (13)  r0: audio status, r4-r7: lba/msf, 
+                                       fl=0: "lba", fl=2: "msf"
+
+Read Disc Code:
+         8a 00 00 00 00 00 00. (14)  possibly extended "check condition"-info
+
+Read Header:
+         04 00 ll-bb-aa 00 00.  (0)   4 bytes response with "check2"
+         04 02 mm-ss-ff 00 00.  (0)   4 bytes response with "check2"
+
+Spin Up:
+         05 00 ll-bb-aa 00 00.  (0)  possibly implies a "seek"
+
+Spin Down:
+         06 ...
+
+Diagnostic:
+         07 00 ll-bb-aa 00 00.  (2)   2 bytes response with "check2"
+         07 02 mm-ss-ff 00 00.  (2)   2 bytes response with "check2"
+
+Read UPC:
+         08 00 ll-bb-aa 00 00. (16)  
+         08 02 mm-ss-ff 00 00. (16)  
+
+Read ISRC:
+         09 00 ll-bb-aa 00 00. (15)  15 bytes response with "check2"
+         09 02 mm-ss-ff 00 00. (15)  15 bytes response with "check2"
+
+Set XA Parameter:
+         86 ...
+
+Read XA Parameter:
+         87 ...
+
+==============================================================================
+============================================================================*/
+
+/*==========================================================================*/
+/*==========================================================================*/
+
+/*
+ * highest allowed drive number (MINOR+1)
+ * currently only one controller, maybe later up to 4
+ */
+#define NR_SBPCD 4
+
+/*
+ * we try to never disable interrupts - seems to work
+ */
+#define SBPCD_DIS_IRQ 0
+
+/*
+ * we don't use the IRQ line - leave it free for the sound driver
+ */
+#define SBPCD_USE_IRQ  0
+
+/*
+ * you can set the interrupt number of your interface board here:
+ * It is not used at this time. No need to set it correctly.
+ */
+#define SBPCD_INTR_NR  7            
+
+/*
+ * "write byte to port"
+ */
+#define OUT(x,y) outb(y,x)
+
+
+#define MIXER_CD_Volume        0x28
+
+/*==========================================================================*/
+/*
+ * use "REP INSB" for strobing the data in:
+ */
+#if PATCHLEVEL<15
+#define READ_DATA(port, buf, nr) \
+__asm__("cld;rep;insb": :"d" (port),"D" (buf),"c" (nr):"cx","dx","di")
+#else
+#define READ_DATA(port, buf, nr) insb(port, buf, nr)
+#endif
+
+/*==========================================================================*/
+/*
+ * to fork and execute a function after some elapsed time:
+ * one "jifs" unit is 10 msec.
+ */
+#define SET_TIMER(func, jifs) \
+        ((timer_table[SBPCD_TIMER].expires = jiffies + jifs), \
+        (timer_table[SBPCD_TIMER].fn = func), \
+        (timer_active |= 1<<SBPCD_TIMER))
+
+#define CLEAR_TIMER    timer_active &= ~(1<<SBPCD_TIMER)
+
+/*==========================================================================*/
+/*
+ * Creative Labs Programmers did this:
+ */
+#define MAX_TRACKS     120 /* why more than 99? */
+
+
+/*==========================================================================*/
+/*
+ * To make conversions easier (machine dependent!)
+ */
+typedef union _msf
+{
+  u_int n;
+  u_char c[4];
+}
+MSF;
+
+typedef union _blk
+{
+  u_int n;
+  u_char c[4];
+}
+BLK;
+
+/*==========================================================================*/
+
+
+
+
+
+
index 59ae295ae7354c3ed926f6e1599b41bf7bbaf91b..ad23e32d60c0fdeb03cb78c208a56b50733c889c 100644 (file)
@@ -30,6 +30,8 @@
  * TAPE_QIC02_TIMER    timer for QIC-02 tape driver (it's not hardcoded)
  *
  * MCD_TIMER           Mitsumi CD-ROM Timer
+ *
+ * SBPCD_TIMER         SoundBlaster/Matsushita/Panasonic CD-ROM timer
  */
 
 #define BLANK_TIMER    0
@@ -48,6 +50,8 @@
 
 #define HD_TIMER2      24
 
+#define SBPCD_TIMER    25
+
 struct timer_struct {
        unsigned long expires;
        void (*fn)(void);
index 732bd4e06b063bac32b8ff75d287159f5c7af179..49fdb904c5726dda35d3f4c4fcc4997591ffedc9 100644 (file)
@@ -294,7 +294,7 @@ struct tty_ldisc {
                         unsigned int cmd, unsigned long arg);
        int     (*select)(struct tty_struct * tty, struct inode * inode,
                          struct file * file, int sel_type,
-                         select_table *wait);
+                         struct select_table_struct *wait);
        /*
         * The following routines are called from below.
         */
index 15e3f6b8c82c444bc7f925275308f0885bfbdca6..41309a4baf8b7335b33edf8532edfc3986c6b6c8 100644 (file)
@@ -91,6 +91,9 @@ extern void t128_setup(char *str, int *ints);
 extern void generic_NCR5380_setup(char *str, int *intr);
 extern void aha152x_setup(char *str, int *ints);
 extern void sound_setup(char *str, int *ints);
+#ifdef CONFIG_SBPCD
+extern void sbpcd_setup(char *str, int *ints);
+#endif CONFIG_SBPCD
 
 #ifdef CONFIG_SYSVIPC
 extern void ipc_init(void);
@@ -198,6 +201,9 @@ struct {
 #ifdef CONFIG_SOUND
        { "sound=", sound_setup },
 #endif
+#ifdef CONFIG_SBPCD
+       { "sbpcd=", sbpcd_setup },
+#endif CONFIG_SBPCD
        { 0, 0 }
 };
 
index acb4752191057762403cc93d0919aa70f685cb85..d2196438d7666a58b161f102da23a976f7a5da9e 100644 (file)
@@ -9,9 +9,11 @@
 
 _register_chrdev
 _unregister_chrdev
-___verify_write
 _wake_up_interruptible
 
+_wp_works_ok
+___verify_write
+
 _current
 _jiffies
 _printk
index af6fdbf9b0c942322a46d7b21539432c1c671386..fdef4c640ba307a8c8f49a75d3d9d63affefccc1 100644 (file)
@@ -72,6 +72,8 @@
 extern int last_retran;
 extern void sort_send(struct sock *sk);
 
+#define min(a,b)       ((a)<(b)?(a):(b))
+
 void
 ip_print(struct iphdr *ip)
 {
@@ -1402,8 +1404,7 @@ ip_queue_xmit(struct sock *sk, struct device *dev,
                }
        }
        sti();
-       reset_timer(sk, TIME_WRITE,
-               backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
+       reset_timer(sk, TIME_WRITE, sk->rto);
   } else {
        skb->sk = sk;
   }
@@ -1423,14 +1424,16 @@ ip_queue_xmit(struct sock *sk, struct device *dev,
 
 
 void
-ip_retransmit(struct sock *sk, int all)
+ip_do_retransmit(struct sock *sk, int all)
 {
   struct sk_buff * skb;
   struct proto *prot;
   struct device *dev;
+  int retransmits;
 
   prot = sk->prot;
   skb = sk->send_head;
+  retransmits = sk->retransmits;
   while (skb != NULL) {
        dev = skb->dev;
        /* I know this can't happen but as it does.. */
@@ -1469,7 +1472,7 @@ ip_retransmit(struct sock *sk, int all)
        /*        else dev->queue_xmit(skb, dev, SOPRI_NORMAL ); CANNOT HAVE SK=NULL HERE */
        }
 
-oops:  sk->retransmits++;
+oops:  retransmits++;
        sk->prot->retransmits ++;
        if (!all) break;
 
@@ -1477,43 +1480,37 @@ oops:   sk->retransmits++;
        if (sk->retransmits > sk->cong_window) break;
        skb = (struct sk_buff *)skb->link3;
   }
-
-  /*
-   * Increase the RTT time every time we retransmit. 
-   * This will cause exponential back off on how hard we try to
-   * get through again.  Once we get through, the rtt will settle
-   * back down reasonably quickly.
-   */
-  sk->backoff++;
-  reset_timer(sk, TIME_WRITE, backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
 }
 
-/* Backoff function - the subject of much research */
-int backoff(int n)
+/*
+ * This is the normal code called for timeouts.  It does the retransmission
+ * and then does backoff.  ip_do_retransmit is separated out because
+ * tcp_ack needs to send stuff from the retransmit queue without
+ * initiating a backoff.
+ */
+
+void
+ip_retransmit(struct sock *sk, int all)
 {
-       /* Use binary exponential up to retry #4, and quadratic after that
-        * This yields the sequence
-        * 1, 2, 4, 8, 16, 25, 36, 49, 64, 81, 100 ...
-        */
+  ip_do_retransmit(sk, all);
 
-       if(n<0)
-       {
-               printk("Backoff < 0!\n");
-               return 16;      /* Make up a value */
-       }
-       
-       if(n <= 4)
-               return 1 << n;  /* Binary exponential back off */
-       else
-       {
-               if(n<255)
-                       return n * n;   /* Quadratic back off */
-               else
-               {
-                       printk("Overloaded backoff!\n");
-                       return 255*255;
-               }
-       }
+  /*
+   * Increase the timeout each time we retransmit.  Note that
+   * we do not increase the rtt estimate.  rto is initialized
+   * from rtt, but increases here.  Jacobson (SIGCOMM 88) suggests
+   * that doubling rto each time is the least we can get away with.
+   * In KA9Q, Karns uses this for the first few times, and then
+   * goes to quadratic.  netBSD doubles, but only goes up to *64,
+   * and clamps at 1 to 64 sec afterwards.  Note that 120 sec is
+   * defined in the protocol as the maximum possible RTT.  I guess
+   * we'll have to use something other than TCP to talk to the
+   * University of Mars.
+   */
+
+  sk->retransmits++;
+  sk->backoff++;
+  sk->rto = min(sk->rto << 1, 120*HZ);
+  reset_timer(sk, TIME_WRITE, sk->rto);
 }
 
 /*
index 8001bae025ea1902f476ad5f4bc00256b49bbdc9..37efd6feda7d5620df5ff47c1648a2c2bde39240 100644 (file)
@@ -843,13 +843,15 @@ inet_create(struct socket *sock, int protocol)
   sk->copied_seq = 0;
   sk->fin_seq = 0;
   sk->proc = 0;
-  sk->rtt = TCP_WRITE_TIME;
+  sk->rtt = TCP_WRITE_TIME << 3;
+  sk->rto = TCP_WRITE_TIME;
   sk->mdev = 0;
   sk->backoff = 0;
   sk->packets_out = 0;
   sk->cong_window = 1; /* start with only sending one packet at a time. */
-  sk->exp_growth = 1;  /* if set cong_window grow exponentially every time
-                         we get an ack. */
+  sk->cong_count = 0;
+  sk->ssthresh = 0;
+  sk->max_window = 0;
   sk->urginline = 0;
   sk->intr = 0;
   sk->linger = 0;
index cbd60277f3396e6d68e75d183dfd0c87c0c86956..ee6fb0e1fd990bacd1d125ec3809f2e322a283f3 100644 (file)
@@ -79,7 +79,6 @@ struct sock {
                                destroy,
                                ack_timed,
                                no_check,
-                               exp_growth,
                                zapped, /* In ax25 & ipx means not linked */
                                broadcast,
                                nonagle;
@@ -103,14 +102,21 @@ struct sock {
   unsigned short               window;
   unsigned short               bytes_rcv;
   unsigned short               mtu;
+  unsigned short               max_window;
   unsigned short               num;
   volatile unsigned short      cong_window;
+  volatile unsigned short      cong_count;
+  volatile unsigned short      ssthresh;
   volatile unsigned short      packets_out;
   volatile unsigned short      urg;
   volatile unsigned short      shutdown;
   unsigned short               mss;
   volatile unsigned long       rtt;
   volatile unsigned long       mdev;
+  volatile unsigned long       rto;
+/* currently backoff isn't used, but I'm maintaining it in case
+ * we want to go back to a backoff formula that needs it
+ */
   volatile unsigned short      backoff;
   volatile short               err;
   unsigned char                        protocol;
index 41a32628ca4410752c97930b3108fd1bf5b94afd..9c1223b94478ae294c9ba15a8d64ac56971df87a 100644 (file)
 
 #define SEQ_TICK 3
 unsigned long seq_offset;
+#define LOCALNET_BIGPACKETS
 
 static __inline__ int 
 min(unsigned int a, unsigned int b)
@@ -177,10 +178,18 @@ static int tcp_select_window(struct sock *sk)
 {
        int new_window = sk->prot->rspace(sk);
 
-       /* Enforce RFC793 - we've offered it we must live with it */    
-       if(new_window<sk->window)
-               return(sk->window);
-       
+/*
+ * two things are going on here.  First, we don't ever offer a
+ * window less than min(sk->mtu, MAX_WINDOW/2).  This is the
+ * receiver side of SWS as specified in RFC1122.
+ * Second, we always give them at least the window they
+ * had before, in order to avoid retracting window.  This
+ * is technically allowed, but RFC1122 advises against it and
+ * in practice it causes trouble.
+ */
+       if (new_window < min(sk->mtu, MAX_WINDOW/2) ||
+           new_window < sk->window)
+         return(sk->window);
        return(new_window);
 }
 
@@ -210,18 +219,13 @@ tcp_retransmit(struct sock *sk, int all)
        return;
   }
 
-/*
- *  If we had the full V-J mechanism, this might be right.  But
- *  for the moment we want simple slow start after error.
- *
- *  if (sk->cong_window > 4)
- *       sk->cong_window = sk->cong_window / 2;
- */
+  sk->ssthresh = sk->cong_window >> 1; /* remember window where we lost */
+  /* sk->ssthresh in theory can be zero.  I guess that's OK */
+  sk->cong_count = 0;
+
   sk->cong_window = 1;
-  sk->exp_growth = 0;
 
-  /* Do the actuall retransmit. */
+  /* Do the actual retransmit. */
   ip_retransmit(sk, all);
 }
 
@@ -638,8 +642,7 @@ static void tcp_send_skb(struct sock *sk, struct sk_buff *skb)
                if (before(sk->window_seq, sk->wfront->h.seq) &&
                    sk->send_head == NULL &&
                    sk->ack_backlog == 0)
-                 reset_timer(sk, TIME_PROBE0, 
-                             backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
+                 reset_timer(sk, TIME_PROBE0, sk->rto);
        } else {
                sk->prot->queue_xmit(sk, skb->dev, skb, 0);
        }
@@ -918,25 +921,26 @@ tcp_write(struct sock *sk, unsigned char *from,
                continue;
        }
 
-#if 0
        /*
         * We also need to worry about the window.
-        * If window < 1/4 offered window, don't use it.  That's
-        *   silly window prevention.  What we actually do is 
+        * If window < 1/2 the maximum window we've seen from this
+        *   host, don't use it.  This is sender side
+        *   silly window prevention, as specified in RFC1122.
+        *   (Note that this is diffferent than earlier versions of
+        *   SWS prevention, e.g. RFC813.).  What we actually do is 
         *   use the whole MTU.  Since the results in the right
         *   edge of the packet being outside the window, it will
         *   be queued for later rather than sent.
         */
 
        copy = diff(sk->window_seq, sk->send_seq);
-       if (copy < (diff(sk->window_seq, sk->rcv_ack_seq) >> 2))
-         copy = sk->mtu;
+       if (sk->max_window > 1) {
+         if (copy < (sk->max_window >> 1))
+           copy = sk->mtu;
+       } else  /* no max_window yet, punt this test */
+         copy = sk->mtu;       
        copy = min(copy, sk->mtu);
        copy = min(copy, len);
-#else
-       /* This also prevents silly windows by simply ignoring the offered window.. */
-       copy = min(sk->mtu, len);
-#endif
 
   /* We should really check the window here also. */
        if (sk->packets_out && copy < sk->mtu && !(flags & MSG_OOB)) {
@@ -1780,6 +1784,16 @@ tcp_options(struct sock *sk, struct tcphdr *th)
 
 }
 
+static inline unsigned long default_mask(unsigned long dst)
+{
+       dst = ntohl(dst);
+       if (IN_CLASSA(dst))
+               return htonl(IN_CLASSA_NET);
+       if (IN_CLASSB(dst))
+               return htonl(IN_CLASSB_NET);
+       return htonl(IN_CLASSC_NET);
+}
+
 /*
  * This routine handles a connection request.
  * It should make sure we haven't already responded.
@@ -1846,8 +1860,13 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
   newsk->send_head = NULL;
   newsk->send_tail = NULL;
   newsk->back_log = NULL;
-  newsk->rtt = TCP_CONNECT_TIME;
+  newsk->rtt = TCP_CONNECT_TIME << 3;
+  newsk->rto = TCP_CONNECT_TIME;
   newsk->mdev = 0;
+  newsk->max_window = 0;
+  newsk->cong_window = 1;
+  newsk->cong_count = 0;
+  newsk->ssthresh = 0;
   newsk->backoff = 0;
   newsk->blog = 0;
   newsk->intr = 0;
@@ -1903,8 +1922,14 @@ tcp_conn_request(struct sock *sk, struct sk_buff *skb,
 /* note use of sk->mss, since user has no direct access to newsk */
   if (sk->mss)
     newsk->mtu = sk->mss;
-  else
-    newsk->mtu = 576 - HEADER_SIZE;
+  else {
+#ifdef LOCALNET_BIGPACKETS
+    if ((saddr & default_mask(saddr)) == (daddr & default_mask(daddr)))
+      newsk->mtu = MAX_WINDOW;
+    else
+#endif
+      newsk->mtu = 576 - HEADER_SIZE;
+  }
 /* but not bigger than device MTU */
   newsk->mtu = min(newsk->mtu, dev->mtu - HEADER_SIZE);
 
@@ -2036,7 +2061,11 @@ tcp_close(struct sock *sk, int timeout)
        case TCP_FIN_WAIT2:
        case TCP_LAST_ACK:
                /* start a timer. */
-               reset_timer(sk, TIME_CLOSE, 4 * sk->rtt);
+                /* original code was 4 * sk->rtt.  In converting to the
+                * new rtt representation, we can't quite use that.
+                * it seems to make most sense to  use the backed off value
+                */
+               reset_timer(sk, TIME_CLOSE, 4 * sk->rto);
                if (timeout) tcp_time_wait(sk);
                release_sock(sk);
                return; /* break causes a double release - messy */
@@ -2109,8 +2138,7 @@ tcp_close(struct sock *sk, int timeout)
                if (sk->wfront == NULL) {
                        prot->queue_xmit(sk, dev, buff, 0);
                } else {
-                       reset_timer(sk, TIME_WRITE,
-                         backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
+                       reset_timer(sk, TIME_WRITE, sk->rto);
                        buff->next = NULL;
                        if (sk->wback == NULL) {
                                sk->wfront=buff;
@@ -2218,6 +2246,12 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
 {
   unsigned long ack;
   int flag = 0;
+  /* 
+   * 1 - there was data in packet as well as ack or new data is sent or 
+   *     in shutdown state
+   * 2 - data from retransmit queue was acked and removed
+   * 4 - window shrunk or data from retransmit queue was acked and removed
+   */
 
   if(sk->zapped)
        return(1);      /* Dead, cant ack any more so why bother */
@@ -2227,6 +2261,9 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
          "sk->rcv_ack_seq=%d, sk->window_seq = %d\n",
          ack, ntohs(th->window), sk->rcv_ack_seq, sk->window_seq));
 
+  if (ntohs(th->window) > sk->max_window)
+       sk->max_window = ntohs(th->window);
+
   if (sk->retransmits && sk->timeout == TIME_KEEPOPEN)
        sk->retransmits = 0;
 
@@ -2309,9 +2346,29 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
 
   /* We don't want too many packets out there. */
   if (sk->timeout == TIME_WRITE && 
-      sk->cong_window < 2048 && ack != sk->rcv_ack_seq) {
-       if (sk->exp_growth) sk->cong_window *= 2;
-         else sk->cong_window++;
+      sk->cong_window < 2048 && after(ack, sk->rcv_ack_seq)) {
+/* 
+ * This is Jacobson's slow start and congestion avoidance. 
+ * SIGCOMM '88, p. 328.  Because we keep cong_window in integral
+ * mss's, we can't do cwnd += 1 / cwnd.  Instead, maintain a 
+ * counter and increment it once every cwnd times.  It's possible
+ * that this should be done only if sk->retransmits == 0.  I'm
+ * interpreting "new data is acked" as including data that has
+ * been retransmitted but is just now being acked.
+ */
+       if (sk->cong_window < sk->ssthresh)  
+         /* in "safe" area, increase */
+         sk->cong_window++;
+       else {
+         /* in dangerous area, increase slowly.  In theory this is
+            sk->cong_window += 1 / sk->cong_window
+          */
+         if (sk->cong_count >= sk->cong_window) {
+           sk->cong_window++;
+           sk->cong_count = 0;
+         } else 
+           sk->cong_count++;
+       }
   }
 
   DPRINTF((DBG_TCP, "tcp_ack: Updating rcv ack sequence.\n"));
@@ -2327,6 +2384,12 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
            ! before (sk->window_seq, sk->wfront->h.seq)) {
          sk->retransmits = 0;
          sk->backoff = 0;
+         /* recompute rto from rtt.  this eliminates any backoff */
+         sk->rto = ((sk->rtt >> 2) + sk->mdev) >> 1;
+         if (sk->rto > 120*HZ)
+           sk->rto = 120*HZ;
+         if (sk->rto < 1*HZ)
+           sk->rto = 1*HZ;
        }
   }
 
@@ -2344,23 +2407,36 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
 
                if (sk->retransmits) {
 
-                 /* if we're retransmitting, don't start any new
-                  * packets until after everything in retransmit queue
-                  * is acked.  That's as close as I can come at the
-                  * moment to slow start the way this code is organized
+                 /* we were retransmitting.  don't count this in RTT est */
+                 flag |= 2;
+
+                 /*
+                  * even though we've gotten an ack, we're still
+                  * retransmitting as long as we're sending from
+                  * the retransmit queue.  Keeping retransmits non-zero
+                  * prevents us from getting new data interspersed with
+                  * retransmissions.
                   */
+
                  if (sk->send_head->link3)
                    sk->retransmits = 1;
                  else
                    sk->retransmits = 0;
+
                }
 
-               /*
-                * need to restart backoff whenever we get a response,
-                * or things get impossible if we lose a window-full of
-                * data with very small MSS
+               /*
+                * Note that we only reset backoff and rto in the
+                * rtt recomputation code.  And that doesn't happen
+                * if there were retransmissions in effect.  So the
+                * first new packet after the retransmissions is
+                * sent with the backoff still in effect.  Not until
+                * we get an ack from a non-retransmitted packet do
+                * we reset the backoff and rto.  This allows us to deal
+                * with a situation where the network delay has increased
+                * suddenly.  I.e. Karn's algorithm. (SIGCOMM '87, p5.)
                 */
-               sk->backoff = 0;
+
                /* We have one less packet out there. */
                if (sk->packets_out > 0) sk->packets_out --;
                DPRINTF((DBG_TCP, "skb=%X skb->h.seq = %d acked ack=%d\n",
@@ -2371,42 +2447,32 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
 
                oskb = sk->send_head;
 
-               /* 
-                * In theory we're supposed to ignore rtt's when there's
-                * retransmission in process.  Unfortunately this means
-                * that if there's a sharp increase in RTT, we may 
-                * never get out of retransmission.  For the moment
-                * ignore the test.
-                */
+               if (!(flag&2)) {
+                 long m;
 
-               if (/* sk->retransmits == 0 && */ !(flag&2)) {
-                 long abserr, rtt = jiffies - oskb->when;
-
-                 /*
-                  * Berkeley's code puts these limits on a separate timeout
-                  * field, not on the RTT estimate itself.  However the way this
-                  * code is done, that would complicate things.  If we're going
-                  * to clamp the values, we have to do so before calculating
-                  * the mdev, or we'll get unreasonably large mdev's.  Experience
-                  * shows that with a minium rtt of .1 sec, we get spurious
-                  * retransmits, due to delayed acks on some hosts.  Berkeley uses
-                  * 1 sec, so why not?
+                 /* The following amusing code comes from Jacobson's
+                  * article in SIGCOMM '88.  Note that rtt and mdev
+                  * are scaled versions of rtt and mean deviation.
+                  * This is designed to be as fast as possible 
+                  * m stands for "measurement".
                   */
 
-                 if (rtt < 100) rtt = 100; /* 1 sec */
-                 if (rtt > 12000) rtt = 12000; /* 2 min - max rtt allowed by protocol */
-
-                 if (sk->state == TCP_SYN_SENT || sk->state == TCP_SYN_RECV) {
-                   /* first ack, so nothing else to average with */
-                   sk->rtt = rtt;
-                   sk->mdev = rtt; /* overcautious initial estimate */
-                 }
-                 else {
-                   abserr = (rtt > sk->rtt) ? rtt - sk->rtt : sk->rtt - rtt;
-                   sk->rtt = (7 * sk->rtt + rtt) >> 3;
-                   sk->mdev = (3 * sk->mdev + abserr) >> 2;
-                 }
+                 m = jiffies - oskb->when;  /* RTT */
+                 m -= (sk->rtt >> 3);       /* m is now error in rtt est */
+                 sk->rtt += m;              /* rtt = 7/8 rtt + 1/8 new */
+                 if (m < 0)
+                   m = -m;                  /* m is now abs(error) */
+                 m -= (sk->mdev >> 2);      /* similar update on mdev */
+                 sk->mdev += m;             /* mdev = 3/4 mdev + 1/4 new */
+
+                 /* now update timeout.  Note that this removes any backoff */
+                 sk->rto = ((sk->rtt >> 2) + sk->mdev) >> 1;
+                 if (sk->rto > 120*HZ)
+                   sk->rto = 120*HZ;
+                 if (sk->rto < 1*HZ)
+                   sk->rto = 1*HZ;
                  sk->backoff = 0;
+
                }
                flag |= (2|4);
 
@@ -2446,8 +2512,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
                   sk->send_head == NULL &&
                   sk->ack_backlog == 0 &&
                   sk->state != TCP_TIME_WAIT) {
-               reset_timer(sk, TIME_PROBE0, 
-                           backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
+               reset_timer(sk, TIME_PROBE0, sk->rto);
        }               
   } else {
        if (sk->send_head == NULL && sk->ack_backlog == 0 &&
@@ -2461,8 +2526,7 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
                        delete_timer(sk);
        } else {
                if (sk->state != (unsigned char) sk->keepopen) {
-                       reset_timer(sk, TIME_WRITE,
-                         backoff(sk->backoff) * (2 * sk->mdev + sk->rtt));
+                       reset_timer(sk, TIME_WRITE, sk->rto);
                }
                if (sk->state == TCP_TIME_WAIT) {
                        reset_timer(sk, TIME_CLOSE, TCP_TIMEWAIT_LEN);
@@ -2503,12 +2567,41 @@ tcp_ack(struct sock *sk, struct tcphdr *th, unsigned long saddr, int len)
        }
   }
 
+/*
+ * I make no guarantees about the first clause in the following
+ * test, i.e. "(!flag) || (flag&4)".  I'm not entirely sure under
+ * what conditions "!flag" would be true.  However I think the rest
+ * of the conditions would prevent that from causing any
+ * unnecessary retransmission. 
+ *   Clearly if the first packet has expired it should be 
+ * retransmitted.  The other alternative, "flag&2 && retransmits", is
+ * harder to explain:  You have to look carefully at how and when the
+ * timer is set and with what timeout.  The most recent transmission always
+ * sets the timer.  So in general if the most recent thing has timed
+ * out, everything before it has as well.  So we want to go ahead and
+ * retransmit some more.  If we didn't explicitly test for this
+ * condition with "flag&2 && retransmits", chances are "when + rto < jiffies"
+ * would not be true.  If you look at the pattern of timing, you can
+ * show that rto is increased fast enough that the next packet would
+ * almost never be retransmitted immediately.  Then you'd end up
+ * waiting for a timeout to send each packet on the retranmission
+ * queue.  With my implementation of the Karn sampling algorithm,
+ * the timeout would double each time.  The net result is that it would
+ * take a hideous amount of time to recover from a single dropped packet.
+ * It's possible that there should also be a test for TIME_WRITE, but
+ * I think as long as "send_head != NULL" and "retransmit" is on, we've
+ * got to be in real retransmission mode.
+ *   Note that ip_do_retransmit is called with all==1.  Setting cong_window
+ * back to 1 at the timeout will cause us to send 1, then 2, etc. packets.
+ * As long as no further losses occur, this seems reasonable.
+ */
+
   if (((!flag) || (flag&4)) && sk->send_head != NULL &&
-      (sk->send_head->when + backoff(sk->backoff) * (2 * sk->mdev + sk->rtt)
-       < jiffies)) {
-       sk->exp_growth = 0;
-       ip_retransmit(sk, 1);
-  }
+      (((flag&2) && sk->retransmits) ||
+       (sk->send_head->when + sk->rto < jiffies))) {
+       ip_do_retransmit(sk, 1);
+       reset_timer(sk, TIME_WRITE, sk->rto);
+      }
 
   DPRINTF((DBG_TCP, "leaving tcp_ack\n"));
   return(1);
@@ -2979,8 +3072,15 @@ tcp_connect(struct sock *sk, struct sockaddr_in *usin, int addr_len)
 /* use 512 or whatever user asked for */
   if (sk->mss)
     sk->mtu = sk->mss;
-  else
-    sk->mtu = 576 - HEADER_SIZE;
+  else {
+#ifdef LOCALNET_BIGPACKETS
+    if ((sk->saddr & default_mask(sk->saddr)) == 
+       (sk->daddr & default_mask(sk->daddr)))
+      sk->mtu = MAX_WINDOW;
+    else
+#endif
+      sk->mtu = 576 - HEADER_SIZE;
+  }
 /* but not bigger than device MTU */
   sk->mtu = min(sk->mtu, dev->mtu - HEADER_SIZE);
 
@@ -3639,8 +3739,15 @@ tcp_send_probe0(struct sock *sk)
    */
   sk->prot->queue_xmit(sk, dev, skb2, 1);
   sk->backoff++;
-  reset_timer (sk, TIME_PROBE0, 
-              backoff (sk->backoff) * (2 * sk->mdev + sk->rtt));
+  /*
+   * in the case of retransmissions, there's good reason to limit
+   * rto to 120 sec, as that's the maximum legal RTT on the Internet.
+   * For probes it could reasonably be longer.  However making it
+   * much longer could cause unacceptable delays in some situation,
+   * so we might as well use the same value
+   */
+  sk->rto = min(sk->rto << 1, 120*HZ);
+  reset_timer (sk, TIME_PROBE0, sk->rto);
   sk->retransmits++;
   sk->prot->retransmits ++;
 }
index ed5c69b5ff73ad4d09d5381dbd284053e07068ac..73d4b24087afe6f189326946b8974d298f4b28e0 100644 (file)
@@ -157,10 +157,9 @@ net_timer (unsigned long data)
         * So we need to check for that.
         */
        if (sk->send_head) {
-         if (jiffies < (sk->send_head->when + backoff (sk->backoff)
-           * (2 * sk->mdev + sk->rtt))) {
-           reset_timer (sk, TIME_WRITE, (sk->send_head->when
-               + backoff (sk->backoff) * (2 * sk->mdev + sk->rtt)) - jiffies);
+         if (jiffies < (sk->send_head->when + sk->rto)) {
+           reset_timer (sk, TIME_WRITE, 
+                        (sk->send_head->when + sk->rto - jiffies));
            release_sock (sk);
            break;
          }