diff options
author | Linus Torvalds <torvalds@cc.helsinki.fi> | 1994-01-29 12:57:42 +0000 |
---|---|---|
committer | Nicolas Pitre <nico@cam.org> | 2007-08-19 14:19:30 -0400 |
commit | 0ef15e52af53204c01d894863c7bd1bf2ee86cd6 (patch) | |
tree | 1b61351d0da736c9632bd44961a027652343c239 | |
parent | 5924f25a1b43d674b453dbcf9adde23d0bc8c2b2 (diff) | |
download | archive-0ef15e52af53204c01d894863c7bd1bf2ee86cd6.tar.gz |
ALPHA-pl14x
-rw-r--r-- | Makefile | 2 | ||||
-rw-r--r-- | config.in | 1 | ||||
-rw-r--r-- | drivers/block/Makefile | 8 | ||||
-rw-r--r-- | drivers/block/blk.h | 8 | ||||
-rw-r--r-- | drivers/block/ll_rw_blk.c | 7 | ||||
-rw-r--r-- | drivers/block/sbpcd.c | 2985 | ||||
-rw-r--r-- | drivers/char/console.c | 2 | ||||
-rw-r--r-- | drivers/char/tty_ioctl.c | 15 | ||||
-rw-r--r-- | drivers/net/CONFIG | 44 | ||||
-rw-r--r-- | drivers/net/README.8390 | 96 | ||||
-rw-r--r-- | drivers/net/ne.c | 10 | ||||
-rw-r--r-- | fs/isofs/inode.c | 10 | ||||
-rw-r--r-- | fs/open.c | 1 | ||||
-rw-r--r-- | include/linux/major.h | 3 | ||||
-rw-r--r-- | include/linux/sbpcd.h | 473 | ||||
-rw-r--r-- | include/linux/timer.h | 4 | ||||
-rw-r--r-- | include/linux/tty.h | 2 | ||||
-rw-r--r-- | init/main.c | 6 | ||||
-rw-r--r-- | kernel/ksyms.S | 4 | ||||
-rw-r--r-- | net/inet/ip.c | 71 | ||||
-rw-r--r-- | net/inet/sock.c | 8 | ||||
-rw-r--r-- | net/inet/sock.h | 8 | ||||
-rw-r--r-- | net/inet/tcp.c | 283 | ||||
-rw-r--r-- | net/inet/timer.c | 7 |
24 files changed, 3786 insertions, 272 deletions
@@ -1,6 +1,6 @@ VERSION = 0.99 PATCHLEVEL = 14 -ALPHA = w +ALPHA = x all: Version zImage @@ -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 * diff --git a/drivers/block/Makefile b/drivers/block/Makefile index 253de65..bc0dfc5 100644 --- a/drivers/block/Makefile +++ b/drivers/block/Makefile @@ -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 diff --git a/drivers/block/blk.h b/drivers/block/blk.h index 970ba4d..faa5adf 100644 --- a/drivers/block/blk.h +++ b/drivers/block/blk.h @@ -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" diff --git a/drivers/block/ll_rw_blk.c b/drivers/block/ll_rw_blk.c index 1473621..7ea4f88 100644 --- a/drivers/block/ll_rw_blk.c +++ b/drivers/block/ll_rw_blk.c @@ -18,6 +18,10 @@ #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 index e69de29..43df7eb 100644 --- a/drivers/block/sbpcd.c +++ b/drivers/block/sbpcd.c @@ -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); +} +/*==========================================================================*/ diff --git a/drivers/char/console.c b/drivers/char/console.c index 690250b..2a72deb 100644 --- a/drivers/char/console.c +++ b/drivers/char/console.c @@ -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" diff --git a/drivers/char/tty_ioctl.c b/drivers/char/tty_ioctl.c index 8f4a744..c443c3a 100644 --- a/drivers/char/tty_ioctl.c +++ b/drivers/char/tty_ioctl.c @@ -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); diff --git a/drivers/net/CONFIG b/drivers/net/CONFIG index 882a654..cb9bbed 100644 --- a/drivers/net/CONFIG +++ b/drivers/net/CONFIG @@ -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 @@ -16,28 +19,37 @@ # 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 index 87af97b..e69de29 100644 --- a/drivers/net/README.8390 +++ b/drivers/net/README.8390 @@ -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. diff --git a/drivers/net/ne.c b/drivers/net/ne.c index 9ad85fd..e198d55 100644 --- a/drivers/net/ne.c +++ b/drivers/net/ne.c @@ -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]; diff --git a/fs/isofs/inode.c b/fs/isofs/inode.c index 550d792..d800d59 100644 --- a/fs/isofs/inode.c +++ b/fs/isofs/inode.c @@ -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); @@ -225,6 +225,7 @@ asmlinkage int sys_fchdir(unsigned int fd) return -EACCES; iput(current->pwd); current->pwd = inode; + inode->i_count++; return (0); } diff --git a/include/linux/major.h b/include/linux/major.h index 9990250..8b3a8ab 100644 --- a/include/linux/major.h +++ b/include/linux/major.h @@ -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 index e69de29..880781f 100644 --- a/include/linux/sbpcd.h +++ b/include/linux/sbpcd.h @@ -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; + +/*==========================================================================*/ + + + + + + diff --git a/include/linux/timer.h b/include/linux/timer.h index 59ae295..ad23e32 100644 --- a/include/linux/timer.h +++ b/include/linux/timer.h @@ -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); diff --git a/include/linux/tty.h b/include/linux/tty.h index 732bd4e..49fdb90 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h @@ -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. */ diff --git a/init/main.c b/init/main.c index 15e3f6b..41309a4 100644 --- a/init/main.c +++ b/init/main.c @@ -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 } }; diff --git a/kernel/ksyms.S b/kernel/ksyms.S index acb4752..d219643 100644 --- a/kernel/ksyms.S +++ b/kernel/ksyms.S @@ -9,9 +9,11 @@ _register_chrdev _unregister_chrdev -___verify_write _wake_up_interruptible +_wp_works_ok +___verify_write + _current _jiffies _printk diff --git a/net/inet/ip.c b/net/inet/ip.c index af6fdbf..fdef4c6 100644 --- a/net/inet/ip.c +++ b/net/inet/ip.c @@ -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); } /* diff --git a/net/inet/sock.c b/net/inet/sock.c index 8001bae..37efd6f 100644 --- a/net/inet/sock.c +++ b/net/inet/sock.c @@ -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; diff --git a/net/inet/sock.h b/net/inet/sock.h index cbd6027..ee6fb0e 100644 --- a/net/inet/sock.h +++ b/net/inet/sock.h @@ -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; diff --git a/net/inet/tcp.c b/net/inet/tcp.c index 41a3262..9c1223b 100644 --- a/net/inet/tcp.c +++ b/net/inet/tcp.c @@ -103,6 +103,7 @@ #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 ++; } diff --git a/net/inet/timer.c b/net/inet/timer.c index ed5c69b..73d4b24 100644 --- a/net/inet/timer.c +++ b/net/inet/timer.c @@ -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; } |