diff -urN sys.orig/i386/apm/apm.c sys/i386/apm/apm.c --- sys.orig/i386/apm/apm.c Mon Nov 10 23:40:40 1997 +++ sys/i386/apm/apm.c Tue Apr 14 08:47:52 1998 @@ -25,6 +25,7 @@ #endif /*DEVFS*/ #include #include +#include #include #include #include @@ -39,11 +40,17 @@ static int apm_int __P((u_long *eax, u_long *ebx, u_long *ecx)); static void apm_resume __P((void)); +#define APM_FORCE_APM10_FLAG 0x02 +#define APM_NO_CLOCK_ADJUST_FLAG 0x04 + + /* static data */ struct apm_softc { int initialized, active; int always_halt_cpu, slow_idle_cpu; int disabled, disengaged; + int shutdown_off; + int suspending; u_int minorversion, majorversion; u_int cs32_base, cs16_base, ds_base; u_int cs_limit, ds_limit; @@ -207,6 +214,22 @@ return 0; } +static int +apm_standby_system(void) +{ + u_long eax, ebx, ecx; + + eax = (APM_BIOS << 8) | APM_SETPWSTATE; + ebx = PMDV_ALLDEV; + ecx = PMST_STANDBY; + if (apm_int(&eax, &ebx, &ecx)) { + printf("Entire system standby failure: errcode = %ld\n", + 0xff & (eax >> 8)); + return 1; + } + return 0; +} + /* Display control */ /* * Experimental implementation: My laptop machine can't handle this function @@ -229,6 +252,35 @@ return 0; } +/* + * Shutdown the system completely + * + * It does not work on some laptops. I think it works on some desktops + * that has ATX power supply. + */ +void +apm_power_off(int howto, void *dummy_arg) +{ + struct apm_softc *sc = &apm_softc; + u_long eax, ebx, ecx; + + if (sc->shutdown_off && howto == RB_HALT) { + if (!apm_softc.active) + return; + + /* wait 1sec before turning off the system power */ + DELAY(1000000); + + eax = (APM_BIOS << 8) | APM_SETPWSTATE; + ebx = PMDV_ALLDEV; + ecx = PMST_OFF; + if (apm_int(&eax, &ebx, &ecx)) { + printf("Power off failure: errcode = %ld\n", + 0xff & (eax >> 8)); + } + } +} + /* APM Battery low handler */ static void @@ -329,6 +381,7 @@ static struct timeval suspend_time; static struct timeval diff_time; +static int apm_no_clock_adjust = 0; static int apm_default_resume(void *arg) @@ -337,21 +390,27 @@ u_int second, minute, hour; struct timeval resume_time, tmp_time; - /* modified for adjkerntz */ - pl = splsoftclock(); - inittodr(0); /* adjust time to RTC */ - microtime(&resume_time); - tmp_time = time; /* because 'time' is volatile */ - timevaladd(&tmp_time, &diff_time); - time = tmp_time; - splx(pl); - second = resume_time.tv_sec - suspend_time.tv_sec; - hour = second / 3600; - second %= 3600; - minute = second / 60; - second %= 60; - log(LOG_NOTICE, "resumed from suspended mode (slept %02d:%02d:%02d)\n", - hour, minute, second); + if (apm_no_clock_adjust) { + log(LOG_NOTICE, "resumed from suspended mode\n"); + } + else { + /* modified for adjkerntz */ + pl = splsoftclock(); + inittodr(0); /* adjust time to RTC */ + microtime(&resume_time); + tmp_time = time; /* because 'time' is volatile */ + timevaladd(&tmp_time, &diff_time); + time = tmp_time; + splx(pl); + second = resume_time.tv_sec - suspend_time.tv_sec; + hour = second / 3600; + second %= 3600; + minute = second / 60; + second %= 60; + log(LOG_NOTICE, + "resumed from suspended mode (slept %02d:%02d:%02d)\n", + hour, minute, second); + } return 0; } @@ -360,15 +419,22 @@ { int pl; - pl = splsoftclock(); - microtime(&diff_time); - inittodr(0); - microtime(&suspend_time); - timevalsub(&diff_time, &suspend_time); - splx(pl); + if (!apm_no_clock_adjust) { + pl = splsoftclock(); + microtime(&diff_time); + inittodr(0); + microtime(&suspend_time); + timevalsub(&diff_time, &suspend_time); + splx(pl); + } return 0; } +/* + * Do not suspend immediately after the system is resumed from + * suspended mode + */ + static void apm_processevent(void); /* @@ -386,6 +452,10 @@ if (!sc) return; + if (sc->suspending != 0) /* avoid reentry */ + return; + sc->suspending = 1; + if (sc->initialized) { apm_execute_hook(hook[APM_HOOK_SUSPEND]); if (apm_suspend_system() == 0) @@ -397,6 +467,19 @@ } void +apm_standby(void) +{ + struct apm_softc *sc = &apm_softc; + + if (!sc) + return; + + if (sc->initialized) { + apm_standby_system(); + } +} + +void apm_resume(void) { struct apm_softc *sc = &apm_softc; @@ -404,6 +487,8 @@ if (!sc) return; + sc->suspending = 0; + if (sc->initialized) apm_execute_hook(hook[APM_HOOK_RESUME]); } @@ -606,7 +691,7 @@ apm_event = apm_getevent(); switch (apm_event) { OPMEV_DEBUGMESSAGE(PMEV_STANDBYREQ); - apm_suspend(); + apm_standby(); break; OPMEV_DEBUGMESSAGE(PMEV_SUSPENDREQ); apm_suspend(); @@ -624,11 +709,17 @@ apm_resume(); break; OPMEV_DEBUGMESSAGE(PMEV_STANDBYRESUME); - apm_resume(); +#if 0 + apm_resume(); /* should we call this or not? */ +#else + inittodr(0); /* adjust time to RTC */ +#endif break; OPMEV_DEBUGMESSAGE(PMEV_BATTERYLOW); apm_battery_low(); +#ifdef APM_BATT_LOW_SUSPEND apm_suspend(); +#endif /* APM_BATT_LOW_SUSPEND */ break; OPMEV_DEBUGMESSAGE(PMEV_POWERSTATECHANGE); break; @@ -655,6 +746,21 @@ { #define APM_KERNBASE KERNBASE struct apm_softc *sc = &apm_softc; + int apm_force_apm10 = 0; + int rversion, rmajorversion, rminorversion; + +#ifdef FORCE_APM10 + apm_force_apm10 = 1; +#else /* FORCE_APM10 */ + apm_force_apm10 = (dvp->id_flags & APM_FORCE_APM10_FLAG); +#endif /* FORCE_APM10 */ + +#ifdef APM_NO_CLOCK_ADJUST + apm_no_clock_adjust = 1; +#else /* APM_NO_CLOCK_ADJUST */ + apm_no_clock_adjust = (dvp->id_flags & APM_NO_CLOCK_ADJUST_FLAG); +#endif /* APM_NO_CLOCK_ADJUST */ + sc->initialized = 0; @@ -702,30 +808,51 @@ apm_addr.segment = GSEL(GAPMCODE32_SEL, SEL_KPL); apm_addr.offset = sc->cs_entry; -#ifdef FORCE_APM10 - apm_version = 0x100; - sc->majorversion = 1; - sc->minorversion = 0; - sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); - printf("apm: running in APM 1.0 compatible mode\n"); -#else - /* Try to kick bios into 1.1 or greater mode */ - apm_driver_version(); - sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + - ((apm_version & 0x000f) >> 0); - sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + - ((apm_version & 0x0f00) >> 8); - - sc->intversion = INTVERSION(sc->majorversion, sc->minorversion); - -#ifdef APM_DEBUG - if (sc->intversion >= INTVERSION(1, 1)) - printf("apm: Engaged control %s\n", is_enabled(!sc->disengaged)); + rversion = apm_version; + rminorversion = ((rversion & 0x00f0) >> 4) * 10 + + ((rversion & 0x000f) >> 0); + rmajorversion = ((rversion & 0xf000) >> 12) * 10 + + ((rversion & 0x0f00) >> 8); + + if (apm_force_apm10) { + apm_version = 0x100; + sc->majorversion = 1; + sc->minorversion = 0; + sc->intversion = INTVERSION(1, 0); + printf("apm: running in APM 1.0 compatible mode\n"); + } + else { + if (rmajorversion >= 1 && rminorversion >= 1) { + apm_driver_version(); + } + sc->minorversion = ((apm_version & 0x00f0) >> 4) * 10 + + ((apm_version & 0x000f) >> 0); + sc->majorversion = ((apm_version & 0xf000) >> 12) * 10 + + ((apm_version & 0x0f00) >> 8); + + if ((sc->majorversion == 1 && sc->minorversion == 0 + && rmajorversion >= 1 && rminorversion >= 1) + || sc->majorversion > 10 /* for broken APM 1.1 */ + ) { + apm_version = 0x100; + sc->majorversion = 1; + sc->minorversion = 0; + sc->intversion = INTVERSION(1, 0); + printf("apm: running in APM 1.0 compatible mode\n"); + } + else { + sc->intversion = + INTVERSION(sc->majorversion, sc->minorversion); +#ifdef APM_DEBUG + if (sc->intversion >= INTVERSION(1, 1)) { + printf("apm: Engaged control %s\n", + is_enabled(!sc->disengaged)); + } #endif - - printf("apm: found APM BIOS version %d.%d\n", - sc->majorversion, sc->minorversion); -#endif /* FORCE_APM10 */ + printf("apm: found APM BIOS version %d.%d\n", + sc->majorversion, sc->minorversion); + } + } #ifdef APM_DEBUG printf("apm: Slow Idling CPU %s\n", is_enabled(sc->slow_idle_cpu)); @@ -767,6 +894,8 @@ apm_hook_establish(APM_HOOK_SUSPEND, &sc->sc_suspend); apm_hook_establish(APM_HOOK_RESUME , &sc->sc_resume); + at_shutdown(apm_power_off, NULL, SHUTDOWN_PRE_HALT); + apm_event_enable(); sc->initialized = 1; @@ -834,6 +963,9 @@ newstate = *(int *)addr; if (apm_display(newstate)) error = ENXIO; + break; + case APMIO_SHUTDOWNOFF: + sc->shutdown_off = *(int *)addr; break; default: error = EINVAL; diff -urN sys.orig/i386/conf/PAO sys/i386/conf/PAO --- sys.orig/i386/conf/PAO Thu Jan 1 09:00:00 1970 +++ sys/i386/conf/PAO Tue Apr 14 08:44:38 1998 @@ -0,0 +1,255 @@ +# +# Sample Laptop Configuration +# for lenlen.ntc.keio.ac.jp (Toshiba Libretto 50CT) +# Tatsumi Hosokawa +# +# Note: wlp and scc drivers are not configured without +# without installing these drivers (because they make +# the size of sys.patch in PAO-boot.flp larger). +# + +machine "i386" +cpu "I386_CPU" +cpu "I486_CPU" +cpu "I586_CPU" +cpu "I686_CPU" +ident PAO +maxusers 10 + +options MATH_EMULATE #Support for x87 emulation +options INET #InterNETworking +options FFS #Berkeley Fast Filesystem +options NFS #Network Filesystem +options MFS #Memory Filesystem +options MSDOSFS #MSDOS Filesystem +options "CD9660" #ISO 9660 Filesystem +options PROCFS #Process filesystem +options "COMPAT_43" #Compatible with BSD 4.3 [KEEP THIS!] +options "SCSI_DELAY=15" #Be pessimistic about Joe SCSI device +options BOUNCE_BUFFERS #include support for DMA bounce buffers +options UCONSOLE #Allow users to grab the console +options FAILSAFE #Be conservative +options USERCONFIG #boot -c editor +options VISUAL_USERCONFIG #visual boot -c editor + +options SYSVSHM +options SYSVSEM +options SYSVMSG + +# laptop-specific configuration +options LAPTOP + +# If your laptop have not had Windoze95-Ready BIOS, please update it. +# Such old BIOS'es sometimes have critical bugs at 32-bit protected +# mode APM BIOS interface (which have not used by Windoze 3.1). + +# PC-card suspend/resume support (experimental) +options APM_PCCARD_RESUME +options PCIC_RESUME_RESET + +# Keep power for serial cards when the system suspends +# (If your machine hangs up when you try to suspend the system with +# FAX/Modem PCMCIA card, uncomment this option). +#options SIO_SUSP_KEEP_PWR + +# Detach SCSI devices when the SCSI card is removed +options SCSI_DETACH + +# Japanese version of WaveLAN PCMCIA uses 2.4GHz band instead of 915MHz +# band that US version uses. If you want to use Japanese version of +# WaveLAN, uncomment this line, or edit the corresponding config entry +# of /etc/pccard.conf. +#options "WAVELAN_PCMCIA_24" + +# Suspend the system when the battery status is "Low" +#options "APM_BATT_LOW_SUSPEND" + +# If you want to use NTP on laptop machines, uncomment the following +# option. Current APM implementation affects NTP client. +#options "APM_NO_CLOCK_ADJUST" + +# Some X-servers cannot survive suspend/resume on laptop. +# This option kicks her when the system is resumed from suspended mode. +#options SYSCONS_VTY_RESUME + + +config kernel root on wd0 + +controller isa0 +#controller eisa0 +controller pci0 + +# Dont remove these two lines! +pseudo-device card 1 +device pcic0 at isa? port 0x3e0 irq 5 vector pcicintr +device pcic1 at isa? port 0x3e2 # for HiNote Ultra II +device pcic1 at isa? port 0x3e4 + +# If your machine says that PC-cards are inserted and removed frequently +# even if you don't insert or remove the cards, please try to specify +# the IRQ of PCIC explicitly. +#options "PCIC_IRQ=12" # for machines with serial trackball +#options "PCIC_IRQ=0" # zero means no IRQ mode + +# Some PCMCIA-PCI bridge has peculiar I/O address (default: 0x3e0). +# If you want to specify I/O address explicitly, uncomment and edit the +# following line (for example, I/O address of PCMCIA bridge of SOTEC +# Winbook Quattro/V is 0x3000). To know this value, please read the +# manual of your laptop or device property of PCMCIA bridge from +# Windows95's device control panel. +#options "PCIC_IO=0x3000" # for Sotec Winbook Quattro/V + +# This option might be usefule for those who has a PCI-ISA bridge that +# is capable of IRQ routing and BIOS that properly configures it. +# Assumes this condition, one could possibly use IRQ9 and IRQ10, which +# CLPD6701 Interrupt Deserializer cannot handle. +# We can't blindly assume this condition, this option is turned off by +# default. +#options "PCIC_CLPD6832_NO_EXPLICIT_ISA_IRQ" + +controller fdc0 at isa? port "IO_FD1" bio irq 6 drq 2 vector fdintr +disk fd0 at fdc0 drive 0 +disk fd1 at fdc0 drive 1 +tape ft0 at fdc0 drive 2 + +controller wdc0 at isa? port "IO_WD1" bio irq 14 vector wdintr +disk wd0 at wdc0 drive 0 + +controller wdc1 at isa? port "IO_WD2" bio irq 15 vector wdintr +disk wd1 at wdc1 drive 0 + +controller wdc2 at isa? disable port "IO_WD2" bio irq 15 vector wdintr +disk wd2 at wdc2 drive 0 + +controller wdc3 at isa? disable port "IO_WD2" bio irq 15 vector wdintr +disk wd3 at wdc3 drive 0 + +options ATAPI #Enable ATAPI support for IDE bus +options ATAPI_STATIC #Don't do it as an LKM + +device wcd0 #IDE CD-ROM +#device wfd0 #LS-120 + +#controller ncr0 +#controller ahb0 +#controller ahc0 + +#controller bt0 at isa? port "IO_BT0" bio irq ? vector bt_isa_intr +#controller uha0 at isa? port "IO_UHA0" bio irq ? drq 5 vector uhaintr +#controller aha0 at isa? port "IO_AHA0" bio irq ? drq 5 vector ahaintr +controller aic0 at isa? port 0x340 bio irq 11 vector aicintr +#controller nca0 at isa? port 0x1f88 bio irq 10 vector ncaintr +#controller nca1 at isa? port 0x350 bio irq 5 vector ncaintr +#controller sea0 at isa? bio irq 5 iomem 0xc8000 iosiz 0x2000 vector seaintr +controller spc0 at isa? port 0x320 bio irq 11 iomem 0xd0000 flags 0x01 vector spcintr + +# Future domain and Q-logic PC-card SCSI drivers +# ported from NetBSD/pc98 (based on NetBSD 1.2) +options SCSI_LOW # XXX: for ncv? and stg? driver +controller ncv0 at isa? port 0x320 bio irq 5 vector ncvintr +controller stg0 at isa? port 0x320 bio irq 5 vector stgintr + +controller scbus0 + +device sd0 + +device od0 #See LINT for possible `od' options + +device st0 + +device cd0 #Only need one of these, the code dynamically grows + +#device wt0 at isa? port 0x300 bio irq 5 drq 1 vector wtintr +#device mcd0 at isa? port 0x300 bio irq 10 vector mcdintr + +#controller matcd0 at isa? port 0x230 bio + +#device scd0 at isa? port 0x230 bio + +# syscons is the default console driver, resembling an SCO console +device sc0 at isa? port "IO_KBD" tty irq 1 vector scintr +# Enable this and PCVT_FREEBSD for pcvt vt220 compatible console driver +#device vt0 at isa? port "IO_KBD" tty irq 1 vector pcrint +#options "PCVT_FREEBSD=210" # pcvt running on FreeBSD 2.1 +#options XSERVER # include code for XFree86 +# If you have a ThinkPAD, uncomment this along with the rest of the PCVT lines +#options PCVT_SCANSET=2 # IBM keyboards are non-std + +# Mandatory, don't remove +device npx0 at isa? port "IO_NPX" flags 0x01 irq 13 vector npxintr + +# +# Laptop support (see LINT for more options) +# +device apm0 at isa? # Advanced Power Management +options APM_BROKEN_STATCLOCK # Workaround some buggy APM BIOS + +device sio0 at isa? port "IO_COM1" tty irq 4 vector siointr +device sio1 at isa? port "IO_COM2" tty irq 3 vector siointr +device sio2 at isa? port "IO_COM3" tty irq 5 vector siointr +device sio3 at isa? disable port "IO_COM4" tty irq 9 vector siointr +device sio4 at isa? disable port "IO_COM3" tty irq 5 vector siointr + +device lpt0 at isa? port? tty irq 7 vector lptintr +device lpt1 at isa? port? tty +#device mse0 at isa? port 0x23c tty irq 5 vector mseintr +#device psm0 at isa? disable port "IO_KBD" conflicts tty irq 12 vector psmintr +device psm0 at isa? port "IO_KBD" conflicts tty irq 12 vector psmintr +# Order is important here due to intrusive probes, do *not* alphabetize +# this list of network interfaces until the probes have been fixed. +# Right now it appears that the ie0 must be probed before ep0. See +# revision 1.20 of this file. +#device de0 +#device fxp0 +#device vx0 +device cnw0 at isa? port 0x300 net irq 5 vector cnwintr +#device cnw1 at isa? disable port 0x300 net irq 5 vector cnwintr +device ed0 at isa? port 0x280 net irq 5 iomem 0xd8000 vector edintr +device ed1 at isa? disable port 0x300 net irq 5 iomem 0xd8000 vector edintr +#device ie0 at isa? port 0x360 net irq 7 iomem 0xd0000 vector ieintr +device ep0 at isa? port 0x300 net irq 10 vector epintr +device ep1 at isa? disable port 0x300 net irq 10 vector epintr +device fe0 at isa? port 0x300 net irq 10 vector feintr +device fe1 at isa? disable port 0x300 net irq 10 vector feintr +#device ix0 at isa? port 0x300 net irq 10 iomem 0xd0000 iosiz 32768 vector ixintr +#device le0 at isa? port 0x300 net irq 5 iomem 0xd0000 vector le_intr +#device lnc0 at isa? port 0x280 net irq 10 drq 0 vector lncintr +device sn0 at isa? port 0x300 net irq 10 vector snintr +device sn1 at isa? disable port 0x300 net irq 10 vector snintr +#device wlp0 at isa? port 0x300 net irq 11 vector wlpintr +#device wlp1 at isa? disable port 0x300 net irq 11 vector wlpintr + +# do not enable ze0 and zp0 (these devices are obsolete) +##device ze0 at isa? port 0x300 net irq 5 iomem 0xd8000 vector zeintr +##device zp0 at isa? port 0x300 net irq 10 iomem 0xd8000 vector zpintr + +# IBM Smart Capture PCMCIA card +#device scc0 at isa? port 0x240 irq 10 iomem 0xd4000 vector sccintr +#device scc1 at isa? disable port 0x244 irq 11 iomem 0xd8000 vector sccintr + +# Hitachi microcomputer system Speach Synthesizer card +#device hss0 at isa? port? +#device hss1 at isa? port? + +# PCMCIA Joystick +#device joy0 at isa? port "IO_GAME" + +pseudo-device loop +pseudo-device ether +pseudo-device log +pseudo-device sl 1 +# DHCP uses BPF (Berkeley Packet Filter) +pseudo-device bpfilter 4 +# ijppp uses tun instead of ppp device +#pseudo-device ppp 1 +pseudo-device tun 1 +pseudo-device pty 16 +pseudo-device gzip # Exec gzipped a.out's +pseudo-device vn #Vnode driver (turns a file into a device) + +options DDB + +# KTRACE enables the system-call tracing facility ktrace(2). +# This adds 4 KB bloat to your kernel, and slightly increases +# the costs of each syscall. +#options KTRACE #kernel tracing diff -urN sys.orig/i386/conf/files.i386 sys/i386/conf/files.i386 --- sys.orig/i386/conf/files.i386 Tue Mar 24 04:40:12 1998 +++ sys/i386/conf/files.i386 Tue Apr 14 08:44:40 1998 @@ -101,7 +101,9 @@ i386/isa/gpib.c optional gp device-driver i386/isa/asc.c optional asc device-driver i386/isa/gsc.c optional gsc device-driver +i386/isa/hss.c optional hss device-driver i386/isa/if_ar.c optional ar device-driver +i386/isa/if_cnw.c optional cnw device-driver i386/isa/if_cx.c optional cx device-driver i386/isa/if_ed.c optional ed device-driver i386/isa/if_eg.c optional eg device-driver @@ -112,8 +114,10 @@ i386/isa/if_ie.c optional ie device-driver i386/isa/if_le.c optional le device-driver i386/isa/if_lnc.c optional lnc device-driver +i386/isa/if_sn.c optional sn device-driver i386/isa/if_sr.c optional sr device-driver i386/isa/if_wl.c optional wl device-driver +i386/isa/if_wlp.c optional wlp device-driver i386/isa/if_ze.c optional ze device-driver i386/isa/if_zp.c optional zp device-driver i386/isa/isa.c optional isa device-driver @@ -146,6 +150,8 @@ i386/isa/qcamio.c optional qcam device-driver i386/isa/random_machdep.c standard i386/isa/rc.c optional rc device-driver +i386/isa/scc.c optional scc device-driver +i386/isa/scc_subr.c optional scc device-driver i386/isa/scd.c optional scd device-driver i386/isa/seagate.c optional sea device-driver i386/isa/si.c optional si device-driver @@ -203,6 +209,7 @@ i386/isa/sound/trix.c optional trix device-driver i386/isa/sound/sscape.c optional sscape device-driver i386/isa/sound/awe_wave.c optional awe device-driver +i386/isa/spc.c optional spc device-driver i386/isa/spigot.c optional spigot device-driver i386/isa/spkr.c optional speaker device-driver i386/isa/stallion.c optional stl device-driver @@ -233,6 +240,12 @@ i386/scsi/aic7xxx.c optional ahc device-driver \ dependency "aic7xxx_{reg,seq}.h" i386/scsi/bt.c optional bt device-driver +i386/scsi/ncr53c500/ncr53c500.c optional ncv device-driver +i386/scsi/ncr53c500/ncr53c500if.c optional ncv device-driver +i386/scsi/tmc18c30/tmc18c30.c optional stg device-driver +i386/scsi/tmc18c30/tmc18c30if.c optional stg device-driver +i386/scsi/scsi_low/scsi_low.c optional scsi_low +i386/scsi/scsi_low/scsi_low_pisa.c optional scsi_low libkern/bcd.c standard libkern/divdi3.c standard libkern/inet_ntoa.c standard diff -urN sys.orig/i386/conf/majors.i386 sys/i386/conf/majors.i386 --- sys.orig/i386/conf/majors.i386 Sat Mar 7 08:44:27 1998 +++ sys/i386/conf/majors.i386 Tue Apr 14 08:44:40 1998 @@ -124,6 +124,7 @@ 80 xdcp Assigned to Chris Ficklin 81 ?? RocketPort/Steve Gericke 82 ppi Generic Parallel I/O +83 hss Hitachi microcomputer system Speech Synthesizer card (itojun@itojun.org) 87 wfd ATAPI floppy client of "ata" 88 dpt DPT RAID Controller 89 pps Pulse-Per-Second timing interface diff -urN sys.orig/i386/i386/autoconf.c sys/i386/i386/autoconf.c --- sys.orig/i386/i386/autoconf.c Tue Mar 24 11:38:00 1998 +++ sys/i386/i386/autoconf.c Tue Apr 14 08:44:41 1998 @@ -213,6 +213,11 @@ enable_intr(); INTREN(IRQ_SLAVE); +#if NCARD > 0 + /* Initialize PC-card support before ISA drivers are initialized */ + pccard_driver_init(); +#endif + #if NEISA > 0 eisa_configure(); #endif diff -urN sys.orig/i386/i386/userconfig.c sys/i386/i386/userconfig.c --- sys.orig/i386/i386/userconfig.c Tue Mar 10 17:04:34 1998 +++ sys/i386/i386/userconfig.c Fri Apr 17 13:50:03 1998 @@ -236,6 +236,9 @@ {"aic", "Adaptec 152x SCSI and compatible sound cards", 0, CLS_STORAGE}, {"nca", "ProAudio Spectrum SCSI and compatibles", 0, CLS_STORAGE}, {"sea", "Seagate ST01/ST02 SCSI and compatibles", 0, CLS_STORAGE}, +{"spc", "Fujitsu MB89352A (SPC) SCSI controller", 0, CLS_STORAGE}, +{"ncv", "NCR/Symbios 53C500/53C406 SCSI controller", 0, CLS_STORAGE}, +{"stg", "Future Domain TMC18C30/18C50 SCSI controller", 0, CLS_STORAGE}, {"wds", "Western Digitial WD7000 SCSI controller", 0, CLS_STORAGE}, {"ncr", "NCR/Symbios 53C810/15/25/60/75 SCSI controller",FLG_FIXED,CLS_STORAGE}, {"wdc", "IDE/ESDI/MFM disk controller", 0, CLS_STORAGE}, @@ -259,6 +262,8 @@ {"lnc", "Isolan, Novell NE2100/NE32-VL Ethernet adapters", 0,CLS_NETWORK}, {"tx", "SMC 9432TX Ethernet adapters", 0, CLS_NETWORK}, {"vx", "3COM 3C590/3C595 Ethernet adapters", 0, CLS_NETWORK}, +{"sn", "SMC/Megahertz Ethernet adapters", 0, CLS_NETWORK}, +{"wlp", "AT&T GIS WaveLAN", 0, CLS_NETWORK}, {"ze", "IBM/National Semiconductor PCMCIA Ethernet adapter",0, CLS_NETWORK}, {"zp", "3COM PCMCIA Etherlink III Ethernet adapter", 0, CLS_NETWORK}, {"de", "DEC DC21040 Ethernet adapter", FLG_FIXED, CLS_NETWORK}, @@ -310,6 +315,8 @@ {"vga", "Catchall PCI VGA driver", FLG_INVISIBLE, CLS_MISC}, {"chip", "PCI chipset support", FLG_INVISIBLE, CLS_MISC}, {"piix", "Intel 82371 Bus-master IDE controller", FLG_INVISIBLE, CLS_MISC}, +{"pcic", "PC Card controller", FLG_INVISIBLE, CLS_MISC}, + {"","",0,0}}; diff -urN sys.orig/i386/include/apm_bios.h sys/i386/include/apm_bios.h --- sys.orig/i386/include/apm_bios.h Wed Nov 5 04:02:35 1997 +++ sys/i386/include/apm_bios.h Tue Apr 14 08:44:44 1998 @@ -74,6 +74,10 @@ #define APM_DRVVERSION 0x0e #endif #define APM_ENGAGEDISENGAGEPM 0x0f +#define APM_GETCAPABILITY 0x10 +#define APM_CTLRESUMETIMER 0x11 +#define APM_ENABLEDISABLEROR 0x12 +#define APM_ENABLEDISABLETBR 0x13 #define APM_OEMFUNC 0x80 /* error code */ @@ -88,6 +92,8 @@ #define APME_UNKNOWNDEVICEID 0x09 #define APME_OUTOFRANGE 0x0a #define APME_NOTENGAGED 0x0b +#define APME_UNSUPPORTFUNC 0x0c +#define APME_RESMTIMERDISABLED 0x0d #define APME_CANTENTERSTATE 0x60 #define APME_NOPMEVENT 0x80 #define APME_NOAPMPRESENT 0x86 @@ -120,7 +126,10 @@ #define PMDV_PCMCIA1 0x0601 #define PMDV_PCMCIA2 0x0602 #define PMDV_PCMCIA3 0x0603 -/* 0x0700 - 0xdfff Reserved */ +/* 0x0700 - 0x7fff Reserved */ +#define PMDV_BATT0 0x8000 +#define PMDV_BATT1 0x8001 +/* 0x8100 - 0xdfff Reserved */ /* 0xe000 - 0xefff OEM-defined power device IDs */ /* 0xf000 - 0xffff Reserved */ @@ -152,10 +161,12 @@ #define NAPM_HOOK 2 void apm_suspend(void); +void apm_standby(void); struct apmhook *apm_hook_establish (int apmh, struct apmhook *); void apm_hook_disestablish (int apmh, struct apmhook *); void apm_cpu_idle(void); void apm_cpu_busy(void); +void apm_power_off(int, void*); #endif /* !ASSEMBLER && !INITIALIZER */ @@ -178,7 +189,8 @@ #define PMEV_USERSTANDBYREQ 0x0009 #define PMEV_USERSUSPENDREQ 0x000a #define PMEV_STANDBYRESUME 0x000b -/* 0x000c - 0x00ff Reserved system events */ +#define PMEV_CAPABLITYCHANGE 0x000c +/* 0x000d - 0x00ff Reserved system events */ /* 0x0100 - 0x01ff Reserved device events */ /* 0x0200 - 0x02ff OEM-defined APM events */ /* 0x0300 - 0xffff Reserved */ @@ -202,6 +214,7 @@ #define APMIO_HALTCPU _IO('P', 7) #define APMIO_NOTHALTCPU _IO('P', 8) #define APMIO_DISPLAY _IOW('P', 9, int) +#define APMIO_SHUTDOWNOFF _IOW('P', 10, int) #endif /* !ASSEMBLER && !INITIALIZER */ diff -urN sys.orig/i386/include/clock.h sys/i386/include/clock.h --- sys.orig/i386/include/clock.h Sun Jan 19 01:16:41 1997 +++ sys/i386/include/clock.h Tue Apr 14 08:44:44 1998 @@ -59,6 +59,7 @@ int release_timer1 __P((void)); #endif int sysbeep __P((int pitch, int period)); +int sysbeep_cancel __P((void)); #ifdef CLOCK_HAIR diff -urN sys.orig/i386/include/cpu.h sys/i386/include/cpu.h --- sys.orig/i386/include/cpu.h Sat Aug 30 23:08:44 1997 +++ sys/i386/include/cpu.h Tue Apr 14 08:44:45 1998 @@ -129,7 +129,10 @@ #define CPU_DISRTCSET 3 /* int: disable resettodr() call */ #define CPU_BOOTINFO 4 /* struct: bootinfo */ #define CPU_WALLCLOCK 5 /* int: indicates wall CMOS clock */ -#define CPU_MAXID 6 /* number of valid machdep ids */ +#define CPU_INTRINUSE 6 /* int: intrs in use */ +#define CPU_CHECKIO 7 /* int: can we allocate the ioaddr? */ +#define CPU_CHECKMEM 8 /* int: can we allocate the memory? */ +#define CPU_MAXID 9 /* number of valid machdep ids */ #define CTL_MACHDEP_NAMES { \ { 0, 0 }, \ @@ -138,6 +141,9 @@ { "disable_rtc_set", CTLTYPE_INT }, \ { "bootinfo", CTLTYPE_STRUCT }, \ { "wall_cmos_clock", CTLTYPE_INT }, \ + { "intr_inuse", CTLTYPE_INT }, \ + { "checkio", CTLTYPE_INT }, \ + { "checkmem", CTLTYPE_INT }, \ } #ifdef KERNEL diff -urN sys.orig/i386/include/if_cnwioctl.h sys/i386/include/if_cnwioctl.h --- sys.orig/i386/include/if_cnwioctl.h Thu Jan 1 09:00:00 1970 +++ sys/i386/include/if_cnwioctl.h Tue Apr 14 08:44:46 1998 @@ -0,0 +1,89 @@ +/* + * Copyright (c) 1996 Berkeley Software Design, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that this notice is retained, + * the conditions in the following notices are met, and terms applying + * to contributors in the following notices also apply to Berkeley + * Software Design, Inc. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * Berkeley Software Design, Inc. + * 4. Neither the name of the Berkeley Software Design, Inc. nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * + * BSDI $Id: if_cnwioctl.h,v 1.1.2.1 1997/12/11 13:59:40 itojun Exp $ + */ +struct cnwstatus { + struct ifreq ifr; + u_char data[0x100]; +}; + +struct cnwstats { + u_int nws_rx; + u_int nws_rxerr; + u_int nws_rxoverflow; + u_int nws_rxoverrun; + u_int nws_rxcrcerror; + u_int nws_rxframe; + u_int nws_rxerrors; + u_int nws_rxavail; + u_int nws_rxone; + u_int nws_tx; + u_int nws_txokay; + u_int nws_txabort; + u_int nws_txlostcd; + u_int nws_txerrors; + u_int nws_txretries[16]; +}; + +struct cnwistats { + struct ifreq ifr; + struct cnwstats stats; +}; + +struct cnwtrail { + u_char what; + u_char status; + u_short length; + struct timeval when; + struct timeval done; +}; + +struct cnwitrail { + struct ifreq ifr; + int head; + struct cnwtrail trail[128]; +}; + +#define ifr_domain ifr_ifru.ifru_flags /* domain */ +#define ifr_key ifr_ifru.ifru_flags /* scramble key */ + +#define SIOCSCNWDOMAIN _IOW('i', 254, struct ifreq) /* set domain */ +#define SIOCGCNWDOMAIN _IOWR('i', 253, struct ifreq) /* get domain */ +#define SIOCSCNWKEY _IOWR('i', 252, struct ifreq) /* set scramble key */ +#define SIOCGCNWSTATUS _IOWR('i', 251, struct cnwstatus)/* get raw status */ +#define SIOCGCNWSTATS _IOWR('i', 250, struct cnwistats)/* get stats */ +#define SIOCGCNWTRAIL _IOWR('i', 249, struct cnwitrail)/* get trail */ diff -urN sys.orig/i386/isa/aic6360.c sys/i386/isa/aic6360.c --- sys.orig/i386/isa/aic6360.c Thu Oct 30 09:38:17 1997 +++ sys/i386/isa/aic6360.c Tue Apr 14 08:44:48 1998 @@ -670,7 +670,11 @@ void aic_print_active_acb __P((void)); void aic_dump6360 __P((void)); void aic_dump_driver __P((void)); + #endif +#ifdef SCSI_DETACH +static void aicdetach __P((struct isa_device *dev)); +#endif /* SCSI_DETACH */ /* Linkup to the rest of the kernel */ struct isa_driver aicdriver = { @@ -768,6 +772,9 @@ #if 0 aicstop(devi->isahd.id_unit); #endif +#ifdef SCSI_DETACH + aicdetach(&devi->isahd); +#endif } /* @@ -899,6 +906,20 @@ AIC_START(("chip revision %d\n",(int)inb(REV))); return 0; } + +#ifdef SCSI_DETACH +static void +aicdetach(dev) + struct isa_device *dev; +{ + int unit = dev->id_unit; + struct aic_data *aic = aicdata[unit]; + struct scsibus_data *scbus; + + scbus = (struct scsibus_data *)scsi_extend_get(aicdata[unit]->sc_link.scsibus); + scsi_detachdev(scbus); +} +#endif /* SCSI_DETACH */ /* * Attach the AIC6360, fill out some high and low level data structures diff -urN sys.orig/i386/isa/atapi.c sys/i386/isa/atapi.c --- sys.orig/i386/isa/atapi.c Sat Jan 17 07:28:42 1998 +++ sys/i386/isa/atapi.c Tue Apr 14 08:44:48 1998 @@ -144,6 +144,12 @@ ++atapi_ndrv; return (1); } + +#ifdef ATAPI_DETACH +int atapi_detach (int ctlr, int unit, int port) +{ +} +#endif /* ATAPI_DETACH */ #else /* ATAPI_STATIC */ /* this code is compiled part of the module */ @@ -174,6 +180,9 @@ extern int wdstart (int ctrlr); extern int wfdattach(struct atapi*, int, struct atapi_params*, int); extern int wcdattach(struct atapi*, int, struct atapi_params*, int); +#ifdef ATAPI_DETACH +extern int wcddetach(struct atapi*, int, struct atapi_params*, int); +#endif /* ATAPI_DETACH */ /* * Probe the ATAPI device at IDE controller `ctlr', drive `unit'. @@ -319,6 +328,59 @@ return (0); #endif /* ATAPI_MODULE */ } + +#ifdef ATAPI_DETACH +#ifdef ATAPI_MODULE +static +#endif +int atapi_detach (int ctlr, int unit, int port) +{ + struct atapi *ata = atapitab + ctlr; + struct atapi_params *ap; + + if (ata->ctrlr == ctlr && + ata->port == port) { + ap = ata->params[unit]; + switch (ap->devtype) { + default: + /* unknown ATAPI device */ + break; + + case AT_TYPE_DIRECT: /* direct-access */ + case AT_TYPE_CDROM: /* CD-ROM device */ +#if NWCD > 0 + /* ATAPI CD-ROM */ + if (wcddetach (ata, unit, ap, ata->debug) < 0) + break; + /* Device detached successfully. */ + ata->attached[unit] = 0; +#endif + break; + + case AT_TYPE_TAPE: /* streaming tape (QIC-121 model) */ +#if NWMT > 0 + /* Add your driver here */ +#endif + break; + + case AT_TYPE_OPTICAL: /* optical disk */ +#if NWMD > 0 + /* Add your driver here */ +#endif + break; + } + /* Detach failed. */ + if (ata->attached[unit]) + return EBUSY; + + if (ata->params[unit]) { + free (ata->params[unit], M_TEMP); + ata->params[unit] = 0; + } + } + return (1); +} +#endif /* ATAPI_DETACH */ static char *cmdname (u_char cmd) { diff -urN sys.orig/i386/isa/atapi.h sys/i386/isa/atapi.h --- sys.orig/i386/isa/atapi.h Sat Jan 17 07:28:42 1998 +++ sys/i386/isa/atapi.h Tue Apr 14 08:44:49 1998 @@ -260,6 +260,9 @@ #ifndef ATAPI_MODULE int atapi_attach (int ctlr, int unit, int port); +#ifdef ATAPI_DETACH +int atapi_detach (int ctlr, int unit, int port); +#endif #endif /* diff -urN sys.orig/i386/isa/clock.c sys/i386/isa/clock.c --- sys.orig/i386/isa/clock.c Sun Apr 27 22:44:19 1997 +++ sys/i386/isa/clock.c Tue Apr 14 08:44:49 1998 @@ -483,6 +483,17 @@ return (0); } +int +sysbeep_cancel(void) +{ + if (beeping) { + untimeout(sysbeepstop, (void *)NULL); + sysbeepstop((void *)NULL); + return 0; + } + return -1; +} + /* * RTC support routines */ @@ -625,6 +636,32 @@ write_eflags(ef); } +#if 1 /* XXX ncv port */ +unsigned int delaycount; +#define FIRST_GUESS 0x2000 + +static void +findcpuspeed() +{ + int i; + int remainder; + + /* Put counter in count down mode */ + outb(TIMER_MODE, TIMER_SEL0 | TIMER_16BIT | TIMER_RATEGEN); + outb(TIMER_CNTR0, 0xff); + outb(TIMER_CNTR0, 0xff); + for (i = FIRST_GUESS; i; i--) + ; + /* Read the value left in the counter */ + remainder = getit(); + /* + * Formula for delaycount is: + * (loopcount * timer clock speed) / (counter ticks * 1000) + */ + delaycount = (FIRST_GUESS * TIMER_DIV(1000)) / (0xffff-remainder); +} +#endif /* ncv port */ + /* * Initialize 8253 timer 0 early so that it can be used in DELAY(). * XXX initialization of other timers is unintentionally left blank. @@ -634,6 +671,9 @@ { u_int delta, freq; +#if 1 /* XXX ncv port */ + findcpuspeed(); +#endif writertc(RTC_STATUSA, rtc_statusa); writertc(RTC_STATUSB, RTCSB_24HR); diff -urN sys.orig/i386/isa/ic/i82593.h sys/i386/isa/ic/i82593.h --- sys.orig/i386/isa/ic/i82593.h Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/ic/i82593.h Tue Apr 14 08:44:52 1998 @@ -0,0 +1,238 @@ +/* + * HISTORY + * $Log: i82593.h,v $ + * Revision 1.1.2.1 1997/12/11 14:00:39 itojun + * PAO-971210 import. hope this works well... + * + * Obtained from: hosokawa + * + * Revision 1.2 1994/06/16 23:57:31 klemets + * Mirrored all the fields in the configuration block. + * + * Revision 1.1 1994/06/02 20:25:34 klemets + * Initial revision + * + */ +/* + * Definitions for Intel 82593 CSMA/CD Core LAN Controller + * The definitions are taken from the 1992 users manual with Intel + * order number 297125-001. + * + * Copyright 1994, Anders Klemets + * + * I permit including this code in the releases of FreeBSD. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. The names of the authors may not be used to endorse or promote products + * derived from this software withough specific prior written permission + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef _PCMCIA_I82593_H +#define _PCMCIA_I82593_H + +/* Intel 82593 CSMA/CD Core LAN Controller */ + +/* Port 0 Command Register definitions */ + +/* Execution operations */ +#define OP0_NOP 0 /* CHNL = 0 */ +#define OP0_SWIT_TO_PORT_1 0 /* CHNL = 1 */ +#define OP0_IA_SETUP 1 +#define OP0_CONFIGURE 2 +#define OP0_MC_SETUP 3 +#define OP0_TRANSMIT 4 +#define OP0_TDR 5 +#define OP0_DUMP 6 +#define OP0_DIAGNOSE 7 +#define OP0_TRANSMIT_NO_CRC 9 +#define OP0_RETRANSMIT 12 +#define OP0_ABORT 13 +/* Reception operations */ +#define OP0_RCV_ENABLE 8 +#define OP0_RCV_DISABLE 10 +#define OP0_STOP_RCV 11 +/* Status pointer control operations */ +#define OP0_FIX_PTR 15 /* CHNL = 1 */ +#define OP0_RLS_PTR 15 /* CHNL = 0 */ +#define OP0_RESET 14 + +#define CR0_CHNL (1 << 4) /* 0=Channel 0, 1=Channel 1 */ +#define CR0_STATUS_0 0x00 +#define CR0_STATUS_1 0x20 +#define CR0_STATUS_2 0x40 +#define CR0_STATUS_3 0x60 +#define CR0_INT_ACK (1 << 7) /* 0=No ack, 1=acknowledge */ + +/* Port 0 Status Register definitions */ + +#define SR0_NO_RESULT 0 /* dummy */ +#define SR0_EVENT_MASK 0x0f +#define SR0_IA_SETUP_DONE 1 +#define SR0_CONFIGURE_DONE 2 +#define SR0_MC_SETUP_DONE 3 +#define SR0_TRANSMIT_DONE 4 +#define SR0_TDR_DONE 5 +#define SR0_DUMP_DONE 6 +#define SR0_DIAGNOSE_PASSED 7 +#define SR0_TRANSMIT_NO_CRC_DONE 9 +#define SR0_RETRANSMIT_DONE 12 +#define SR0_EXECUTION_ABORTED 13 +#define SR0_END_OF_FRAME 8 +#define SR0_RECEPTION_ABORTED 10 +#define SR0_DIAGNOSE_FAILED 15 +#define SR0_STOP_REG_HIT 11 + +#define SR0_CHNL (1 << 4) +#define SR0_EXECUTION (1 << 5) +#define SR0_RECEPTION (1 << 6) +#define SR0_INTERRUPT (1 << 7) + +#define SR3_EXEC_STATE_MASK 0x03 +#define SR3_EXEC_IDLE 0 +#define SR3_TX_ABORT_IN_PROGRESS 1 +#define SR3_EXEC_ACTIVE 2 +#define SR3_ABORT_IN_PROGRESS 3 +#define SR3_EXEC_CHNL (1 << 2) +#define SR3_STP_ON_NO_RSRC (1 << 3) +#define SR3_RCVING_NO_RSRC (1 << 4) +#define SR3_RCV_STATE_MASK 0x60 +#define SR3_RCV_IDLE 0x00 +#define SR3_RCV_READY 0x20 +#define SR3_RCV_ACTIVE 0x40 +#define SR3_RCV_STOP_IN_PROG 0x60 +#define SR3_RCV_CHNL (1 << 7) + +/* Port 1 Command Register definitions */ + +#define OP1_NOP 0 +#define OP1_SWIT_TO_PORT_0 1 +#define OP1_INT_DISABLE 2 +#define OP1_INT_ENABLE 3 +#define OP1_SET_TS 5 +#define OP1_RST_TS 7 +#define OP1_POWER_DOWN 8 +#define OP1_RESET_RING_MNGMT 11 +#define OP1_RESET 14 +#define OP1_SEL_RST 15 + +#define CR1_STATUS_4 0x00 +#define CR1_STATUS_5 0x20 +#define CR1_STATUS_6 0x40 +#define CR1_STOP_REG_UPDATE (1 << 7) + +/* Receive frame status bits */ + +#define RX_RCLD (1 << 0) +#define RX_IA_MATCH (1 << 1) +#define RX_NO_AD_MATCH (1 << 2) +#define RX_NO_SFD (1 << 3) +#define RX_SRT_FRM (1 << 7) +#define RX_OVRRUN (1 << 8) +#define RX_ALG_ERR (1 << 10) +#define RX_CRC_ERR (1 << 11) +#define RX_LEN_ERR (1 << 12) +#define RX_RCV_OK (1 << 13) +#define RX_TYP_LEN (1 << 15) + +/* Transmit status bits */ + +#define TX_NCOL_MASK 0x0f +#define TX_FRTL (1 << 4) +#define TX_MAX_COL (1 << 5) +#define TX_HRT_BEAT (1 << 6) +#define TX_DEFER (1 << 7) +#define TX_UND_RUN (1 << 8) +#define TX_LOST_CTS (1 << 9) +#define TX_LOST_CRS (1 << 10) +#define TX_LTCOL (1 << 11) +#define TX_OK (1 << 13) +#define TX_COLL (1 << 15) + + +struct i82593_conf_block { + u_char fifo_limit : 4, + forgnesi : 1, + fifo_32 : 1, + d6mod : 1, + throttle_enb : 1; + u_char throttle : 6, + cntrxint : 1, + contin : 1; + u_char addr_len : 3, + acloc : 1, + preamb_len : 2, + loopback : 2; + u_char lin_prio : 3, + tbofstop : 1, + exp_prio : 3, + bof_met : 1; + u_char : 4, + ifrm_spc : 4; + u_char : 5, + slottim_low : 3; + u_char slottim_hi : 3, + : 1, + max_retr : 4; + u_char prmisc : 1, + bc_dis : 1, + : 1, + crs_1 : 1, + nocrc_ins : 1, + crc_1632 : 1, + : 1, + crs_cdt : 1; + u_char cs_filter : 3, + crs_src : 1, + cd_filter : 3, + : 1; + u_char : 2, + min_fr_len : 6; + u_char lng_typ : 1, + lng_fld : 1, + rxcrc_xf : 1, + artx : 1, + sarec : 1, + tx_jabber : 1, /* why is this called max_len in the manual? */ + hash_1 : 1, + lbpkpol : 1; + u_char : 6, + fdx : 1, + : 1; + u_char dummy_6 : 6, /* supposed to be ones */ + mult_ia : 1, + dis_bof : 1; + u_char dummy_1 : 1, /* supposed to be one */ + tx_ifs_retrig : 2, + mc_all : 1, + rcv_mon : 2, + frag_acpt : 1, + tstrttrs : 1; + u_char fretx : 1, + runt_eop : 1, + hw_sw_pin : 1, + big_endn : 1, + syncrqs : 1, + sttlen : 1, + tx_eop : 1, + rx_eop : 1; + u_char rbuf_size : 5, + rcvstop : 1, + : 2; +}; + +#endif _PCMCIA_I82593_H + diff -urN sys.orig/i386/isa/if_cnw.c sys/i386/isa/if_cnw.c --- sys.orig/i386/isa/if_cnw.c Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/if_cnw.c Tue Apr 14 08:47:53 1998 @@ -0,0 +1,2083 @@ +/* + * Copyright (c) 1996, 1997 Berkeley Software Design, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that this notice is retained, + * the conditions in the following notices are met, and terms applying + * to contributors in the following notices also apply to Berkeley + * Software Design, Inc. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * Berkeley Software Design, Inc. + * 4. Neither the name of the Berkeley Software Design, Inc. nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI $Id: if_cnw.c,v 1.1.2.4 1998/02/27 09:14:31 itojun Exp $ + * + * Paul Borman, December 1996 + * + * This driver is derived from a generic frame work which is + * Copyright(c) 1994,1995,1996 + * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project + * and Foretune. All rights reserved. + * + * A linux driver was used as the "hardware reference manual" (i.e., + * to determine registers and a general outline of how the card works) + * That driver is publically available and copyright + * + * John Markus Bj,Ax(Brndalen + * Department of Computer Science + * University of Troms,Ax(B + * Norway + * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/ + */ + +/* + * Netwave AirSurfer Wireless LAN + * (Formerly known as the Xircom CreditCard Netwave Adapter) + */ + +#if defined(__FreeBSD__) +/* + * FreeBSD port to use with PAO by Kenjiro Cho . + * Fred L. Templin modified pccardd to support + * card_mem flags and multiple card_mem entries. + */ +#include "cnw.h" +#endif +#if !defined(__FreeBSD__) || NCNW > 0 +/* + * Keep track of the last 128 packets sent/received, along with status. + * Only used to help determine timing related optimizations. +#define CNW_TRAIL + */ + +/* + * Display each packet on the console as it is received. + * This is only for hard core debugging. +#define RECV_PKT_DEBUG + */ + +/* + * The card appears to work much better when we only allow one packet + * "in the air" at a time. This is done by not allowing another packet + * on the card, even if there is room. Turning this off will allow the + * driver to stuff packets on the card as soon as a transmit buffer is + * available. This does increase the number of collisions, though. + * We can que a second packet if there are transmit buffers available, + * but we do not actually send the packet until the last packet has + * been written. + */ +#define ONE_AT_A_TIME + +/* + * Use internal (slow) versions of bcmp and bcopy. These are declared + * as volatile too keep gcc from complaining when we pass in volatile data. + * + * We should not need these routines, but the tcic controller will not work + * without them. This is probably a bug in the tcic controller, but for + * now it is much easier to fix here. You can probably turn this off if + * you are not using a tcic controller. + */ +#define USEVBCMP + +/* + * Pad all packets up to at least ETHER_MIN_LEN bytes. I am not sure + * this is actually needed. + */ +#define ETHER_MIN_LEN 64 + +#if 1 /* __FreeBSD__ */ +/* + * set 0x100 as a default domain when using access-points. +#define USE_ACCESSPOINTS + */ +#endif + +/* + * If cnw_debug is not 0, basic debugging statements are turned on. + * If the low nibble is > 1, more extensive debugging is turned on. + * If bit 0x10 is on in cnw_debug, interrupt debugging it turned on. + */ +int cnw_debug = 0; + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif +#ifdef INET6 +#ifndef INET +#include +#endif +#include +#endif + +#include +#include +#if defined(__bsdi__) +#include +#elif defined(__FreeBSD__) +#include +#endif + +#include "bpfilter.h" +#if NBPFILTER > 0 +#include +#include +#endif /*NBPFILTER*/ + +#include +#include +#if defined(__FreeBSD__) +#include +#include +#include +#include +#include +#endif + +#if defined(__bsdi__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "if_cnwreg.h" +#include "if_cnwioctl.h" + +#elif defined(__FreeBSD__) +#include "card.h" +#include +#include +#include +#include + +#include + +#include +#include + +#endif /* __FreeBSD__ */ + +#ifdef USEVBCMP +#define bcmp vbcmp +#define bcopy vbcopy +static int vbcmp(char volatile *, char volatile *, int); +static void vbcopy(char volatile *, char volatile *, int); +#endif + +#if defined(__FreeBSD__) +#define MULTICAST +#define IFF_NOTRAILERS 0 +#define ETHER_IS_MULTICAST(x) ((*(u_char *)x) & 0x01) +#endif + +struct nw_softc { + struct device nw_dev; /* base device (must be first) */ +#define nw_unit nw_dev.dv_unit +#if defined(__bsdi__) + struct isadev nw_id; /* ISA device */ + struct intrhand nw_ih; /* interrupt vectoring */ +#elif defined(__FreeBSD__) + struct pccard_devinfo *nw_devi; /* pccard device */ +#endif + struct arpcom nw_ac; /* Ethernet common part */ +#define nw_if nw_ac.ac_if /* network-visible interface */ +#define nw_addr nw_ac.ac_enaddr /* hardware Ethernet address */ + + int nw_ifoflags; + + caddr_t nw_mbase; /* shared memory base */ + int nw_base; /* io port base address */ + int nw_psize; /* number of io ports */ + volatile struct nwreg *nw_reg; /* memory buffer */ + volatile struct nwcreg *nw_creg;/* control registers */ + + u_short nw_domain; /* current active NetWave domain */ + u_short nw_skey; /* current scramble key */ + struct cnwstats nw_stats; + +#if defined(__bsdi__) + CliHandle_t nw_clihdl; +#endif + int nw_socket; +#if defined(__bsdi__) + cs_rio_t nw_rio; + cs_rirq_t nw_rirq; + cs_cfg_t nw_rcfg; + WinHandle_t nw_winhdl; +#endif + int nw_irq; /* interrupt vectoring */ + u_short nw_txready; /* Number of bytes ready to xmit */ + short nw_configured:1; + short nw_detaching:1; + short nw_active:1; /* Currently xmitting a packet */ + struct timeval nw_txlast; /* time of last packet sent */ +}; + +int cnwprobe (struct device *, struct cfdata *, void *); +void cnwattach (struct device *, struct device *, void *); +int cnwintr (struct nw_softc *); +#if defined(__bsdi__) +int cnwstart (struct ifnet *); +int cnwioctl (struct ifnet *, u_long, caddr_t); +int cnw_cse_handler(); +#elif defined(__FreeBSD__) +void cnwstart (struct ifnet *); +int cnwioctl (struct ifnet *, int, caddr_t); +#endif + +#if defined(__FreeBSD__) +#define DV_NET DV_IFNET +#endif +struct cfdriver cnwcd = + { NULL, "cnw", cnwprobe, cnwattach, DV_NET, sizeof(struct nw_softc) }; + +static int cnwmax; + +#if defined(__bsdi__) +static int cnw_cc_probe(); +static int cnw_cc_attach(); +static int cnw_cc_detach(); +#elif defined(__FreeBSD__) +static int cnw_cc_probe(struct nw_softc *, int); +static int cnw_cc_attach(struct nw_softc *, int); +static int cnw_cc_detach(struct nw_softc *, int); +#endif + +static int cnw_cmd(struct nw_softc *, ...); +static int cnw_cwait (volatile struct nwcreg *); +static int cnw_init (int); +#if defined(__FreeBSD__) +static void cnw_ifinit (void *); +#endif +static int cnw_reset(struct nw_softc *); +static int cnw_setdomain(struct nw_softc *, int); +static int cnw_setkey(struct nw_softc *, int); +static void cnw_recv (struct nw_softc *); + +static void _cnwprintf(struct nw_softc *, char *, ...); + +static int mcopy_out (struct nw_softc *, struct mbuf *); +static struct mbuf *mcopy_in (struct nw_softc *); + +/* + * Wait for last command to complete + */ +static int wccline = 0; +#define WCC(nwc) ((wccline = __LINE__), !((nwc)->nwc_isr & (NWR_READY)) ? cnw_cwait(nwc) : 0) + +/* + * Offsets into memory areas + */ +#define nwreg_off(x) ((int)&((struct nwreg *)0)->x) +#define nwcreg_off(x) ((int)&((struct nwcreg *)0)->x) + +/* + * Debug printfs + */ +#define cnwprintf if (cnw_debug) _cnwprintf +#define cnwprintf2 if ((cnw_debug & 0x0f) >= 2) _cnwprintf +#define icnwprintf if (cnw_debug & 0x10) _cnwprintf + +#ifdef CNW_TRAIL +static struct cnwtrail trail[128]; +static int head = 0; +static int lastsent = 0; +#endif + +#if defined(__FreeBSD__) + +/* + * PC-Card (PCMCIA) specific code. + */ +static int card_intr(struct pccard_devinfo *); /* Interrupt handler */ +static void cnwunload(struct pccard_devinfo *); /* Disable driver */ +static void cnwsuspend(struct pccard_devinfo *); /* Suspend driver */ +static int cnwcrdinit(struct pccard_devinfo *, int); /* init device */ + +static struct pccard_device cnw_info = { + "cnw", + cnwcrdinit, + cnwunload, + card_intr, +/* cnwsuspend, */ + 0, /* Attributes - presently unused */ + &net_imask /* Interrupt mask for device */ +}; + +struct nw_softc *cnw_scs[NCNW]; + +/* + * Called when a power down is requested. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * wlpcrdinit with first=0. This is called when the user suspends + * the system, or the APM code suspends the system. + */ +static void +cnwsuspend(struct pccard_devinfo *devi) +{ + struct nw_softc *nw; + + printf("cnw%d: suspending\n", devi->isahd.id_unit); + if ((nw = cnw_scs[devi->isahd.id_unit]) != NULL) { + (void)cnw_reset(nw); + nw->nw_if.if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + } +} + +/* + * Initialize the device - called from Slot manager. + * If first is set, then check for the device's existence + * before initializing it. Once initialized, the device table may + * be set up. + */ +static int +cnwcrdinit(struct pccard_devinfo *devi, int first) +{ + struct nw_softc *nw; + int sock, unit; + + unit = devi->isahd.id_unit; + + /* validate unit number. */ + if (first) { + /* first means "card insertion event", not-first + means "resume"! */ + if (devi->isahd.id_unit >= NCNW) + return(ENODEV); + + if ((nw = cnw_scs[devi->isahd.id_unit]) == NULL) { + /* real first time */ + MALLOC(nw, struct nw_softc *, sizeof(struct nw_softc), + M_DEVBUF, M_WAITOK); + if (nw == NULL) + return(ENOBUFS); + + bzero(nw, sizeof(struct nw_softc)); + + /* fake a cfdriver structure */ + cnwcd.cd_devs = (void **)cnw_scs; + cnwcd.cd_devs[unit] = nw; + cnwcd.cd_ndevs = NCNW; + /* fake a device structure in nw_softc */ + nw->nw_dev.dv_unit = unit; + sprintf(nw->nw_dev.dv_xname, "cnw%d", unit); + nw->nw_if.if_snd.ifq_maxlen = ifqmaxlen; /* XXX */ + nw->nw_devi = devi; /* pccard_dev */ + + cnwattach(NULL, (struct device *)nw, NULL); + } + else { + /* card is reinserted! we don't free nw_softc when + * the card is ejected since freeing if (which is + * in the nw_softc) is problematic. + */ + nw->nw_devi = devi; /* don't forget to update pccard_dev */ + } + + /* + * Probe the device. If a value is returned, the + * device was found at the location. + */ + nw->nw_socket = -1; +#if 1 + sock = 0; +#else + sock = devi->sp->slot; /* really? */ +#endif + if (cnw_cc_probe(nw, sock)) { + if (cnw_cc_attach(nw, sock) == 0) { + printf("cnw%d: attach failed!\n", unit); + return (ENXIO); + } +#ifdef INET6 + in6_ifattach(&nw->nw_if, IN6_IFT_802, + (caddr_t)nw->nw_addr); +#endif /* INET6 */ + } + } + /* + * XXX TODO: + * If it was initialized before, the device structure + * should also be initialized. We should + * reset (and possibly restart) the hardware, but + * I am not sure of the best way to do this... + */ + return(0); +} + +/* + * wlpunload - unload the driver and clear the table. + * XXX TODO: + * This is usually called when the card is ejected, but + * can be caused by a modunload of a controller driver. + * The idea is to reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +static void +cnwunload(struct pccard_devinfo *devi) +{ + struct nw_softc *nw = cnwcd.cd_devs[devi->isahd.id_unit]; + struct ifnet *ifp = &nw->nw_if; + + ifp->if_flags &= ~IFF_RUNNING; + if_down(ifp); + nw->nw_socket = -1; + printf("cnw%d: unload\n", devi->isahd.id_unit); +} + +/* + * card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +card_intr(struct pccard_devinfo *devi) +{ + struct nw_softc *nw = cnwcd.cd_devs[devi->isahd.id_unit]; + + cnwintr(nw); + return(1); +} + +int cnw_isa_probe(struct isa_device *); +static int cnw_isa_attach(struct isa_device *is); + +struct isa_driver cnwdriver = { + cnw_isa_probe, + cnw_isa_attach, + "cnw", + 0 +}; + +int +cnw_isa_probe(id) + struct isa_device *id; +{ + /* + * If PC-Card probe required, then register driver with + * slot manager. + */ + pccard_add_driver(&cnw_info); + return 0; +} + +/* never called */ +static int +cnw_isa_attach(id) + struct isa_device *id; +{ + printf("cnw: isa_attach!! shouldn't be called\n"); + return 1; +} + +#endif /* __FreeBSD__ */ + +/* ARGSUSED */ +int +cnwprobe(struct device *parent, struct cfdata *cf, void *aux) +{ +#if defined(__bsdi__) + register struct isa_attach_args *ia = (struct isa_attach_args *) aux; + + ia->ia_irq = IRQNONE; +#ifdef DIAGNOSTIC + if (sizeof(struct nwreg) != 0x1A0) { + printf("cnw: %d != 0x1A0\n", sizeof(struct nwreg)); + return (0); + } +#endif +#elif defined(__FreeBSD__) + /* never happnes */ +#endif /* __FreeBSD__ */ + return (1); +} + +/* + * cnwattach: this is a dummy routine that make the system think + * there's an interface. Make the interface (virtually) visilble + * by filling in the network interface record. Also register with + * socket driver to inform us in the event of card insertion. + */ +/* ARGSUSED */ +void +cnwattach(struct device *parent, struct device *self, void *aux) +{ + register struct nw_softc *nw = (struct nw_softc *) self; + int unit = nw->nw_unit; + register struct ifnet *ifp = &nw->nw_if; +#if defined(__bsdi__) + cs_rclient_t rcli; +#endif + + /* + * Initialize local variables + */ + nw->nw_mbase = NULL; +#ifndef USE_ACCESSPOINTS + nw->nw_domain = NW_DOMAIN; +#else + nw->nw_domain = NW_ACCESSPOINT; /* default to use access-points */ +#endif + nw->nw_skey = 0; + + cnwmax = max(unit, cnwmax); + + /* + * Initialize interface structure + */ + ifp->if_unit = unit; + ifp->if_name = cnwcd.cd_name; + /* + * Our MTU could be much larger, but ether_attach will set it + * to ETHERMTU as well. + * An MTU of 3000 has shown up to a 5% improvement in speed, + * that is only between machines on the local netwave net. + */ + ifp->if_flags = IFF_BROADCAST | IFF_MULTICAST | IFF_SIMPLEX | + IFF_NOTRAILERS; +#if defined(__bsdi__) + ifp->if_init = cnw_init; +#elif defined(__FreeBSD__) + ifp->if_init = cnw_ifinit; +#endif + ifp->if_start = cnwstart; + ifp->if_ioctl = cnwioctl; + ifp->if_watchdog = 0; +#if defined(__bsdi__) + ether_attach(ifp); +#elif defined(__FreeBSD__) + ifp->if_output = ether_output; + ifp->if_softc = nw; + if_attach(ifp); + ether_ifattach(ifp); +#endif + ifp->if_baudrate = 1*1000*1000; /* Default, drivers may update */ +#ifndef USE_ACCESSPOINTS + ifp->if_mtu = 3000; /* We can do better than 1500 */ +#endif + +#if NBPFILTER > 0 +#if defined(__bsdi__) + bpfattach(&ifp->if_bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#elif defined(__FreeBSD__) + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif +#endif /*NBPFILTER*/ + + bzero(nw->nw_addr, sizeof(nw->nw_addr)); + nw->nw_irq = 0; + nw->nw_reg = 0; + nw->nw_configured = 0; + nw->nw_socket = -1; + nw->nw_detaching = 0; + +#if defined(__bsdi__) + nw->nw_ih.ih_fun = cnwintr; + nw->nw_ih.ih_arg = (void *)nw; + + /* + * Initialize card client structure + */ + rcli.Attributes = CSCLI_IO; + rcli.EventMask = ~CSEM_CD_CHANGE; + rcli.CallBack = cnw_cse_handler; + rcli.CallBackArg = nw; + cs_RegisterClient(&nw->nw_clihdl, &rcli); + + isa_establish(&nw->nw_id, &nw->nw_dev); + + printf(": PCCARD Netwave AirSurfer\n"); +#elif defined(__FreeBSD__) + printf("cnw%d: PCCARD Netwave AirSurfer: domain set to 0x%x\n", + unit, nw->nw_domain); +#endif /* __FreeBSD__ */ +} + +/* + * Handle a pending interrupt. Keep looping until all interrupts + * are handled. + */ +int +cnwintr(register struct nw_softc *nw) +{ + register volatile struct nwreg *nwr = nw->nw_reg; + register volatile struct nwcreg *nwc = nw->nw_creg; + register u_char isr, stat; + register struct ifnet *ifp = &nw->nw_if; + + if (nw->nw_detaching) + return (1); + + if (nw->nw_socket < 0) { + printf("cnwintr: stale\n"); + return (0); + } + + nwc->nwc_ictrl = NWR_IDISABLE; + +again: + if (WCC(nwc)) goto wedged; + + if (!(nwc->nwc_ccsr & NWR_IREADY)) { + icnwprintf(nw, "no interrupt ready\n"); + nwc->nwc_ictrl = NWR_IENABLE; + return (1); + } + + isr = nwc->nwc_isr; + + icnwprintf(nw, "isr 0x%x\n", isr); + + if ((isr & (NWR_RXAVAIL|NWR_RXERR|NWR_TXSTAT)) == 0) { + icnwprintf(nw, "empty interrupt\n"); + nwc->nwc_ictrl = NWR_IENABLE; + return (1); + } + + /* + * The linux driver actually picks up multiple packets at this + * point, looking at nwc->nwc_isr after each packet to see if + * the NWR_RXAVAIL bit has been turned off. + */ + if (isr & NWR_RXAVAIL) { + nw->nw_stats.nws_rx++; + cnw_recv(nw); + } + + if (isr & NWR_RXERR) { + nw->nw_stats.nws_rxerr++; + if (WCC(nwc)) goto wedged; + nwr->nwr_runstate = NWR_HALT; + + if (WCC(nwc)) goto wedged; + + stat = nwr->nwr_rstat; + + icnwprintf(nw, "rstat 0x%x\n", stat); + + if (stat & NWR_RSTAT_OVERFLOW) { + /* + * RX overflow detected. Disable receiver and + * process all pending packets before going on. + */ + nw->nw_stats.nws_rxoverflow++; + icnwprintf(nw, "overflow, rbuffer 0x%x\n", + nwr->nwr_rbuffer); + + /* Disable receiver */ + if (cnw_cmd(nw, NW_CMD_SET_RXMODE, 0, -1)) + goto wedged; + + cnw_recv(nw); + + nwr->nwr_rclr = NWR_RSTAT_RXERROR | + (stat & (NWR_RSTAT_CRCERR|NWR_RSTAT_FRAME)); + + /* Re-enable receiver */ + if (cnw_cmd(nw, NW_CMD_SET_RXMODE, NW_RXMODE(nw), -1)) + goto wedged; + } + + if (stat & NWR_RSTAT_OVERRUN) { + /* + * RX overrun. Not much we can do here except for + * blaming slow 80188. + */ + nw->nw_stats.nws_rxoverrun++; + } + + if (stat & NWR_RSTAT_CRCERR) { + /* + * RX crc error. Look for microwave ovens near by. + */ + nw->nw_stats.nws_rxcrcerror++; + } + + if (stat & NWR_RSTAT_FRAME) { + /* + * received a framing error + */ + nw->nw_stats.nws_rxframe++; + } + + if (stat & NWR_RSTAT_RXERROR) { + /* + * Generic RX error + */ + nw->nw_stats.nws_rxerrors++; + } + + if (stat & NWR_RSTAT_RXAVAIL) { + /* + * There is a buffer available + */ + nw->nw_stats.nws_rxavail++; + } + + if (stat & 1) { + /* + * This bit is unused? Keep track of it anyhow. + */ + nw->nw_stats.nws_rxone++; + } + + nwr->nwr_rclr = NWR_RSTAT_RXERROR | + (stat & (NWR_RSTAT_CRCERR|NWR_RSTAT_FRAME)); + + } + + if (isr & NWR_TXSTAT) { +#ifdef ONE_AT_A_TIME + /* + * Any response to a transmit means the current + * packet is done + */ + nw->nw_active = 0; +#endif + /* + * TX status change interrupt. + */ + + if (WCC(nwc)) goto wedged; + stat = nwr->nwr_tstat; + + icnwprintf(nw, "tstat 0x%x\n", stat); +#ifdef CNW_TRAIL + microtime(&trail[lastsent].done); + trail[lastsent].status = stat; +#endif + + if (stat & NWR_TSTAT_OKAY) { + /* + * Transmission complete without error. + */ + nw->nw_stats.nws_txokay++; + nw->nw_stats.nws_txretries[stat & 0xf]++; + + ifp->if_opackets++; + ifp->if_collisions += stat & 0xf; + icnwprintf(nw, "Packet was sent!\n"); + + if (WCC(nwc)) goto wedged; + nwr->nwr_tclr = NWR_TSTAT_OKAY | 0xf; + } + + if (stat & NWR_TSTAT_ABORT) { + /* + * TX giveup: Happens if destination is not + * within the reach when unicast is attempted + * in peer-to-peer mode. Note that broadcast + * packets does not cause this error. + */ + nw->nw_stats.nws_txabort++; + icnwprintf(nw, "transmit error\n"); + } + + if (stat & NWR_TSTAT_CARRLOST) { + /* + * TX carrier lost: Happens if AP is not + * responding when operating in the server- + * client mode. Note that both unitcast and + * broadcast packets cause this error, since + * all packets are initially destined for + * AP first. + */ + nw->nw_stats.nws_txlostcd++; + icnwprintf(nw, "transmit carrier lost\n"); + } + if (stat & NWR_TSTAT_TERR) { + nw->nw_stats.nws_txerrors++; + icnwprintf(nw, "transmit error\n"); + } + if (stat & (NWR_TSTAT_ABORT|NWR_TSTAT_CARRLOST|NWR_TSTAT_TERR)){ + if (WCC(nwc)) goto wedged; + nwr->nwr_tclr = NWR_TSTAT_ABORT | NWR_TSTAT_CARRLOST | + NWR_TSTAT_TERR | 0xf; + ifp->if_oerrors++; + } + if (WCC(nwc)) goto wedged; + + ifp->if_flags &= ~IFF_OACTIVE; + + if (nw->nw_txready || ifp->if_snd.ifq_len) + cnwstart(ifp); + } + + goto again; + + if (0) { +wedged: cnwprintf(nw, "controller wedged @ %d\n", wccline); + } + nwc->nwc_ictrl = NWR_IENABLE; + return (1); +} + +/* + * Send out any packets that have already been loaded into the card but + * not yet sent. Then, start pulling mbufs of the chain and stuffing + * them into the card. Stop when we find that the previous packet we + * stuffed in was not sent or when we run out of mbufs. + */ +#if defined(__bsdi__) +int +#elif defined(__FreeBSD__) +void +#endif +cnwstart(struct ifnet *ifp) +{ + struct nw_softc *nw = cnwcd.cd_devs[ifp->if_unit]; + register volatile struct nwreg *nwr = nw->nw_reg; + register volatile struct nwcreg *nwc = nw->nw_creg; + struct mbuf *m0; + struct timeval now; + +#ifdef DIAGNOSTIC + if (nw->nw_if.if_flags & IFF_OACTIVE) + printf("cnw%d: cnwstart reentered", nw->nw_unit); +#endif + for (;;) { + microtime(&now); + now.tv_sec -= nw->nw_txlast.tv_sec; + now.tv_usec -= nw->nw_txlast.tv_usec; + if (now.tv_usec < 0) { + now.tv_usec += 1000000; + now.tv_sec -= 1; + } + if (nw->nw_txready) { +#ifdef ONE_AT_A_TIME + /* + * Don't ship this packet out until the last + * packet has left the building. + * If we have not tried to send a packet for 1/5 + * a second then we assume we lost an interrupt, + * lets go on and send the next packet anyhow. + * + * I suppose we could check to see if it is okay + * to put additional packets on the card (beyond + * the one already waiting to be sent) but I don't + * think we would get any improvement in speed as + * we should have ample time to put the next packet + * on while this one is going out. + */ + if (nw->nw_active && + now.tv_sec == 0 && now.tv_usec < 200000) + goto out; +#endif +#ifdef ETHER_MIN_LEN + if (nw->nw_txready < ETHER_MIN_LEN) + nw->nw_txready = ETHER_MIN_LEN; +#endif + + cnwprintf(nw, "sending packet of %d bytes\n", + nw->nw_txready); + nw->nw_stats.nws_tx++; + microtime(&nw->nw_txlast); +#ifdef CNW_TRAIL + trail[head].what = NW_CMD_TX_START; + trail[head].status = 0; + trail[head].length = nw->nw_txready; + trail[head].when = nw->nw_txlast; + lastsent = head; + if (++head == 128) + head = 0; +#endif + if (cnw_cmd(nw,NW_CMD_TX_START,SA(nw->nw_txready),-1)) { + nw->nw_txready = 0; + goto out; + } + nw->nw_txready = 0; +#ifdef ONE_AT_A_TIME + nw->nw_active = 1; +#endif + } + + /* + * Make sure the link integrity field is on + */ + if (WCC(nwc)) goto wedged; + + if (nwr->nwr_lif == 0) { + nw->nw_if.if_flags &= ~IFF_OACTIVE; +#if defined(__bsdi__) + return (ENETDOWN); +#elif defined(__FreeBSD__) + return; +#endif + } + + /* + * Make sure the transmit buffer is available + */ + if (WCC(nwc)) goto wedged; + + if ((nwc->nwc_isr & NWR_TXEMP) == 0) { + cnwprintf(nw, "no empty buffers\n"); + goto out; + } + + if (WCC(nwc)) goto wedged; + + IF_DEQUEUE(&nw->nw_if.if_snd, m0); + + if (m0 == 0) { + cnwprintf(nw, "no more mbufs\n"); + goto out; + } + + nw->nw_if.if_flags |= IFF_OACTIVE; + + /* + * Feed outgoing packet to bpf + */ +#if NBPFILTER > 0 +#if defined(__bsdi__) + if (nw->nw_if.if_bpf) + bpf_mtap(nw->nw_if.if_bpf, m0); +#elif defined(__FreeBSD__) + if (nw->nw_if.if_bpf) + bpf_mtap(&nw->nw_if, m0); +#endif +#endif /*NBPFILTER*/ + + nw->nw_txready = mcopy_out(nw, m0); + } +out: + if (0) { +wedged: cnwprintf(nw, "controller wedged @ %d\n", wccline); + } + nw->nw_if.if_flags &= ~IFF_OACTIVE; +#if defined(__bsdi__) + return (0); +#endif +} + +/* + * Process an ioctl request. + */ +int +#if defined(__bsdi__) +cnwioctl(struct ifnet *ifp, u_long cmd, caddr_t data) +#elif defined(__FreeBSD__) +cnwioctl(struct ifnet *ifp, int cmd, caddr_t data) +#endif +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct nw_softc *nw = (struct nw_softc *) cnwcd.cd_devs[ifp->if_unit]; + int s; + int error = 0; + struct proc *p = curproc; + + s = splimp(); + switch (cmd) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + cnw_init(ifp->if_unit); + arp_ifinit((struct arpcom *)ifp, ifa); + break; +#endif +#ifdef INET6 + case AF_INET6: + cnw_init(ifp->if_unit); + break; +#endif + default: + cnw_init(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && + ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); +#if defined(__FreeBSD__) + /* if is down, stop the card. */ + cnw_reset(nw); +#endif + } else + cnw_init(ifp->if_unit); + break; + +#ifdef SIOCSIFMTU + case SIOCSIFMTU: + /* + * set the interface MTU. it is ok to set a larger MTU + * than Ethernet. + */ + ifp->if_mtu = ((struct ifreq *)data)->ifr_mtu; + break; +#endif + +#ifdef MULTICAST + /* + * Update our multicast list. + */ + case SIOCADDMULTI: + error = ether_addmulti((struct ifreq *)data, &nw->nw_ac); + goto reset; + + case SIOCDELMULTI: + error = ether_delmulti((struct ifreq *)data, &nw->nw_ac); + reset: + if (error == ENETRESET) { + cnw_init(ifp->if_unit); + error = 0; + } + break; +#endif +#ifdef notdef + case SIOCGHWADDR: + bcopy(nw->nw_addr, &ifr->ifr_data, sizeof(nw->nw_addr)); + break; +#endif + case SIOCGCNWDOMAIN: + ((struct ifreq *)data)->ifr_domain = nw->nw_domain; + break; + case SIOCSCNWDOMAIN: + if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) + error = cnw_setdomain(nw, + ((struct ifreq *)data)->ifr_domain); +#if 1 /* __FreeBSD__ */ + if (error == 0) { + /* + * reset MTU. 1500 bytes when using access-points, + * 3000 bytes when peer-to-peer. + */ + if (nw->nw_domain & 0x100) + ifp->if_mtu = ETHERMTU; + else + ifp->if_mtu = 3000; + } +#endif + break; + case SIOCSCNWKEY: + if ((error = suser(p->p_ucred, &p->p_acflag)) == 0) + error = cnw_setkey(nw, + ((struct ifreq *)data)->ifr_key); + break; + case SIOCGCNWSTATUS: + if ((error = suser(p->p_ucred, &p->p_acflag)) == 0 && + ifp->if_flags & IFF_RUNNING) + bcopy((void *)nw->nw_reg->nwr_cmd, + ((struct cnwstatus *)data)->data, 0x100); + break; + case SIOCGCNWSTATS: + bcopy((void *)&nw->nw_stats, + (void *)&(((struct cnwistats *)data)->stats), + sizeof(struct cnwstats)); + break; +#ifdef CNW_TRAIL + case SIOCGCNWTRAIL: + bcopy((void *)&trail, (void *)((struct cnwitrail *)data)->trail, + sizeof(struct cnwitrail)); + ((struct cnwitrail *)data)->head = head; + break; +#endif + default: + error = EINVAL; + } + nw->nw_ifoflags = ifp->if_flags; + splx(s); + return (error); +} + +#if defined(__bsdi__) +/* + * Callback interface: + * Handler(CliData, Function, Socket, Info, MTDRequest, Buffer, Misc, Status) + * + * Function: event/function. + * Socket: socket affected by the event. + * Info: contains other information specific to the event. + * MTDRequest: + * Buffer: + * Misc: + */ +struct mtdreq; /* we never use it, but this makes gcc2 shut up */ + +int +cnw_cse_handler(void *clidata, int func, int sock, int info, + struct mtdreq *mtdreq, char *buf, int misc) +{ + struct nw_softc *nw = (struct nw_softc *)clidata; + + switch(func) { + case CSE_CARD_INSERTION: + if (cu_configured(&nw->nw_clihdl, sock) || nw->nw_socket >= 0) + break; + + if (cnw_cc_probe(nw, sock)) + cnw_cc_attach(nw, sock); + break; + + case CSE_CARD_REMOVAL: + cnw_cc_detach(nw, sock); + break; + + case CSE_CLIENT_INFO: + break; + + default: + break; + } + return (0); +} +#endif /* __bsdi__ */ + +/* + * Probe routine called upon the card insertion event. + */ +static int +cnw_cc_probe(struct nw_softc *nw, int socket) +{ + u_char addr[6]; + int err, j; + struct nw_softc *no; + +#if defined(__bsdi__) + cs_rmem_t rmem; + cs_rmemrw_t rmemrw; + MemHandle_t mh; + + if (cs_spec_lookup(socket, "cnw", NULL, 0)) { + cnwprintf(nw, "cnw_cc_probe: no match\n"); + return (0); + } + + rmem.Socket = socket; + rmem.Attributes = 0; + rmem.Offset = 0; + + if ((err = cs_OpenMemory(&nw->nw_clihdl, &mh, &rmem)) != 0) { + cnwprintf(nw, "OpenMemory err 0x%x\n", err); + return (0); + } + + rmemrw.CardOffset = NW_MEM_ADDR + nwreg_off(nwr_addr); + rmemrw.Count = sizeof(addr); + + err = cs_ReadMemory(&nw->nw_clihdl, &mh, addr, &rmemrw); + + cs_CloseMemory(&nw->nw_clihdl, &mh); + + if (err) { + cnwprintf(nw, "ReadMemory err 0x%x\n", err); + return (0); + } + cnwprintf(nw, "ether address of %s\n", ether_sprintf(addr)); + + /* + * First, check out if this card has been handled by other units. + * But before doing that, we scan other units to see + * if the card has been accepted by is one of them. + */ + + for (j = 0; j < cnwmax; j++) { + if (j == nw->nw_unit) + continue; /* This is me */ + no = cnwcd.cd_devs[j]; + if (bcmp(no->nw_addr, addr, 6) == 0) { + cnwprintf(nw, "card handled by cnw%d\n", j); + return (0); + } + } + + if (nw->nw_addr[0] == 0 && nw->nw_addr[1] == 0 + && nw->nw_addr[2] == 0 && nw->nw_addr[3] == 0 + && nw->nw_addr[4] == 0 && nw->nw_addr[5] == 0) { + register struct ifnet *ifp = &nw->nw_if; + register struct ifaddr *ifa; + register struct sockaddr_dl *sdl; + /* + * This client is fresh. Can accept a new card. + */ + bcopy(addr, (void *)nw->nw_addr, sizeof(nw->nw_addr)); + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { + if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && + sdl->sdl_family == AF_LINK) { + bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr+ LLADDR(sdl), ifp->if_addrlen); /* ###ori### */ + break; + } + } + cnwprintf(nw, "first card inserted\n"); + return (1); + } + + /* + * This client has accepted a card before. + * Check out if the card is the one we've seen before. + */ + if (bcmp(addr, nw->nw_addr, sizeof(nw->nw_addr)) == 0) { + cnwprintf(nw, "same card re-inserted\n"); + return (1); + } + + cnwprintf(nw, "different card inserted (rejected)\n"); + return (0); +#elif defined(__FreeBSD__) + struct pccard_devinfo *devi = nw->nw_devi; + int i; + + /* ethernet address is passed from pccardd */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + addr[i] = devi->misc[i]; + + cnwprintf(nw, "ether address of %6D\n", addr, ":"); + + if (*(int *)addr == 0) { + printf("ether address invalid!\n"); + return 0; + } + + /* + * FreeBSD reuses unit numbers + */ + /* + * Check out if the card is the one we've seen before. + */ + if (bcmp(addr, nw->nw_addr, sizeof(nw->nw_addr)) == 0) { + cnwprintf(nw, "same card re-inserted\n"); + return (1); + } + + do { + register struct ifnet *ifp = &nw->nw_if; + register struct ifaddr *ifa; + register struct sockaddr_dl *sdl; + + bcopy(addr, (void *)nw->nw_addr, sizeof(nw->nw_addr)); + for (ifa = ifp->if_addrlist; ifa; ifa = ifa->ifa_next) { + if ((sdl = (struct sockaddr_dl *)ifa->ifa_addr) && + sdl->sdl_family == AF_LINK) { + bcopy((caddr_t)((struct arpcom *)ifp)->ac_enaddr, + + LLADDR(sdl), ifp->if_addrlen); + break; + } + } + if (nw->nw_addr[0] == 0 && nw->nw_addr[1] == 0 + && nw->nw_addr[2] == 0 && nw->nw_addr[3] == 0 + && nw->nw_addr[4] == 0 && nw->nw_addr[5] == 0) + cnwprintf(nw, "first card inserted\n"); + else + cnwprintf(nw, "different card inserted\n"); + + return (1); + } while (0); + return (0); +#endif /* __FreeBSD__ */ +} + +/* + * Attach it. It would be be nice if we could look at the CIS and determine + * how to program the card, but some cards contain A WHOLE LOTS OF GARBAGE + * in the CIS including several big lies, such as the CISTPL_CONF that's + * trying to pretend it is a plain memory card as well as an incorrect VPP + * specification, so we will presume the following configuration: + * + * Power: 5V to Vcc, 0V to Vpp1 and Vpp2. + * I/O Ports: either 16 or none required (currently we use memory mapping) + * IRQ: One required. + * Window: Common memory, card offset 0x20000-0x28fff for controller + * command and buffer access. No wait state (Assume 400nsec). + * COR: At 0x200 + * COR value: 0x41 (0x01) + * Other regs: CCSR + */ + +static int +cnw_cc_attach(struct nw_softc *nw, int socket) +{ + int unit = nw->nw_dev.dv_unit; +#if defined(__bsdi__) + cs_rwin_t rwin; + cs_rpage_t rpage; +#endif + int err; + + nw->nw_socket = socket; + +#if defined(__bsdi__) + /* + * Start out with allocating IRQ + */ + nw->nw_rirq.Socket = socket; + nw->nw_rirq.Attributes = 0; + nw->nw_rirq.IRQInfo1 = CSIRQ_LEVEL|CSIRQ_MASKS; + nw->nw_rirq.IRQInfo2 = 0xffff; + + err = cs_RequestIRQ(&nw->nw_clihdl, &nw->nw_rirq); + + if (err) { + printf("cnw%d: RequestIRQ err 0x%x\n", unit, err); + goto bad; + } + + nw->nw_irq = irq_indextomask(nw->nw_rirq.AssignedIRQ); + dyna_intr_establish(nw->nw_irq, &nw->nw_ih, DV_NET); + + nw->nw_rcfg.cscfg_Socket = socket; + nw->nw_rcfg.cscfg_Attributes = 2; /* Enable IRQ */ + nw->nw_rcfg.cscfg_Vcc = 50; /* 5V to Vcc */ + nw->nw_rcfg.cscfg_Vpp1 = 0; + nw->nw_rcfg.cscfg_Vpp2 = 0; /* Vpp1 = Vpp2 = 0 */ + nw->nw_rcfg.cscfg_IntType = 2; /* I/O card */ + nw->nw_rcfg.cscfg_Present = CREGMAP_COR; + nw->nw_rcfg.cscfg_ConfigBase = 0x200; /* tpcc.tpcc_radr */ + nw->nw_rcfg.cscfg_ConfigIndex = 0x41; + + if ((err = cs_RequestConfiguration(&nw->nw_clihdl, &nw->nw_rcfg)) != 0){ + printf("cnw%d: RequestConfiguration err 0x%x\n", unit, err); + goto bad; + } +#endif + + nw->nw_configured = 1; + +#if defined(__bsdi__) + /* + * Things are going okay. Memory stuff next. + */ + rwin.Socket = socket; + rwin.Attributes = CSRWIN_ENABLE; + rwin.Base = 0; /* Pick available address */ + rwin.Size = 0x9000; /* Map 36KB (32K window + 4K register */ + rwin.AccessSpeed = (9<<3) | 2; /* 400 nsec */ + rwin.AccessSpeed = 1; /* XXX */ + if ((err = cs_RequestWindow(&nw->nw_clihdl, &nw->nw_winhdl, &rwin))!=0){ + printf("cnw%d: can't alloc win err 0x%x\n", unit, err); + goto bad; + } + + rpage.CardOffset = 0x20000; + rpage.Page = 0; + if ((err = cs_MapMemPage(&nw->nw_clihdl, &nw->nw_winhdl,&rpage)) != 0) { + printf("cnw%d: can't map win(comm) err 0x%x\n", unit, err); + goto bad; + } + + nw->nw_mbase = (caddr_t)cs_ptokvm(rwin.Base); + nw->nw_reg = (struct nwreg *)nw->nw_mbase; + nw->nw_creg = (struct nwcreg *)(nw->nw_mbase + NW_REG_ADDR-NW_MEM_ADDR); + cnwprintf(nw, "memory 0x%x(0x%x,0x%x)", rwin.Base, + nw->nw_reg, nw->nw_creg); +#elif defined(__FreeBSD__) + if (nw->nw_devi->isahd.id_msize < 0x9000) { + printf("allocated memory too small!! size=0x%x need=0x%x\n", + nw->nw_devi->isahd.id_msize, 0x9000); + goto bad; + } + nw->nw_mbase = nw->nw_devi->isahd.id_maddr; + nw->nw_reg = (struct nwreg *)nw->nw_mbase; + nw->nw_creg = (struct nwcreg *)(nw->nw_mbase + NW_REG_ADDR-NW_MEM_ADDR); + cnwprintf(nw, "memory 0x%x(0x%x,0x%x)\n", nw->nw_mbase, + nw->nw_reg, nw->nw_creg); +#endif /* __FreeBSD__ */ + + /* + * Initialize + */ + if (cnw_reset(nw)) + goto bad; + + /* + * Sanity check CS/SS. If mapping has been done without trouble + * we should be able to read the mac address from nwr_addr + * (offset 0x160). If not, it's a botched assertion, and who knows. + */ + if (bcmp(nw->nw_addr, (void *)nw->nw_reg->nwr_addr, + sizeof(nw->nw_addr))) { + printf("cnw%d: sanity check failed on memory access\n", unit); +#if defined(__bsdi__) + cnwprintf(nw, "%s != ", ether_sprintf(nw->nw_addr)); + cnwprintf(NULL, "%s\n", + ether_sprintf((u_char *)nw->nw_reg->nwr_addr)); +#elif defined(__FreeBSD__) + printf("%6D != ", nw->nw_addr, ":"); + printf("%6D\n", nw->nw_reg->nwr_addr, ":"); +#endif + goto bad; + } + + cnwprintf(nw, "ID \"%c%c\" revision %04x %04x\n", + nw->nw_reg->nwr_id[0], nw->nw_reg->nwr_id[1], + nw->nw_reg->nwr_rev[0], nw->nw_reg->nwr_rev[1]); + +#if defined(__FreeBSD__) + /* initialization is done at if-up, to save power consumption */ + nw->nw_if.if_flags &= ~(IFF_RUNNING|IFF_OACTIVE); + return (1); +#endif + + if (cnw_init(nw->nw_if.if_unit) == 0) { + cnwprintf(nw, "initialization failure\n"); + goto bad; + } + + if (WCC(nw->nw_creg)) goto bad; + + nw->nw_creg->nwc_enable = NWR_ENORMAL; + + if (cnw_cmd(nw, NW_CMD_RUN, -1)) + goto bad; + + if (WCC(nw->nw_creg)) goto bad; + + return (1); + +bad: + cnw_cc_detach(nw, socket); + return (0); +} + +/* + * Detach the card and free all of its resources + */ +static int +cnw_cc_detach(struct nw_softc *nw, int socket) +{ + if (socket < 0 || nw->nw_socket != socket) + return (0); + + nw->nw_detaching = 1; + + nw->nw_if.if_flags &= ~IFF_RUNNING; + + if (nw->nw_reg) { + cnwprintf(nw, "releasing window\n"); +#if defined(__bsdi__) + cs_ReleaseWindow(&nw->nw_clihdl, &nw->nw_winhdl); +#endif + nw->nw_reg = 0; + } + + if (nw->nw_configured) { + cnwprintf(nw, "releasing configuration\n"); +#if defined(__bsdi__) + cs_ReleaseConfiguration(&nw->nw_clihdl, &nw->nw_rcfg); +#endif + nw->nw_configured = 0; + } + + if (nw->nw_base) { + cnwprintf(nw, "releasing io ports\n"); +#if defined(__bsdi__) + cs_ReleaseIO(&nw->nw_clihdl, &nw->nw_rio); +#endif + nw->nw_base = 0; + } + + if (nw->nw_irq) { + cnwprintf(nw, "releasing interrupt vector\n"); +#if defined(__bsdi__) + cs_ReleaseIRQ(&nw->nw_clihdl, &nw->nw_rirq); + + cnwprintf(nw, "releasing interrupt handler\n"); + dyna_intr_release(nw->nw_irq, &nw->nw_ih); +#endif + nw->nw_irq = 0; + } + + nw->nw_socket = -1; + nw->nw_detaching = 0; + cnwprintf(nw, "released\n"); + return (0); +} + +/* + * Initialize card + */ +#if defined(__FreeBSD__) +static void cnw_ifinit (sc) + void *sc; +{ + struct nw_softc *nw = (struct nw_softc *)sc; + + cnw_init(nw->nw_unit); +} +#endif +static int +cnw_init(int unit) +{ + register struct nw_softc *nw = cnwcd.cd_devs[unit]; + struct ifnet *ifp = &nw->nw_if; + int s; + register volatile struct nwcreg *nwc = nw->nw_creg; + + if (ifp->if_addrlist == (struct ifaddr *) 0) { + cnwprintf(nw, "no address list\n"); + return (0); + } + + if ((ifp->if_flags & IFF_RUNNING) && ifp->if_flags == nw->nw_ifoflags) { + cnwprintf(nw, "already running\n"); + return (0); + } + + if (nw->nw_socket < 0) { + cnwprintf(nw, "unit not currently inserted\n"); + return (0); + } + + s = splimp(); + + if (cnw_cmd(nw, NW_CMD_INIT, -1)) + goto wedged; + + /* + * RX mode + */ + if (cnw_cmd(nw, NW_CMD_SET_RXMODE, NW_RXMODE(nw), -1)) + goto wedged; + + /* + * TX mode + */ + if (cnw_cmd(nw, NW_CMD_SET_TXMODE, NW_TXENA, -1)) + goto wedged; + + /* + * Set domain (access point or add hoc) + */ + if (cnw_cmd(nw, NW_CMD_SET_DOMAIN, SA(nw->nw_domain), -1)) + goto wedged; + + /* + * Set Scramble Key + */ + if (cnw_cmd(nw, NW_CMD_SET_SKEY, SA(nw->nw_skey), -1)) + goto wedged; + + if (WCC(nwc)) goto wedged; + + nwc->nwc_ictrl = NWR_INORMAL; + + if (WCC(nwc)) goto wedged; + +#if defined(__FreeBSD__) + /* run command is added here to stop the card when if is down. */ + + nw->nw_creg->nwc_enable = NWR_ENORMAL; + + if (cnw_cmd(nw, NW_CMD_RUN, -1)) + goto wedged; + + if (WCC(nw->nw_creg)) goto wedged; +#endif /* __FreeBSD__ */ + + nw->nw_if.if_flags &= ~IFF_OACTIVE; + nw->nw_if.if_flags |= IFF_RUNNING; + + cnwstart(ifp); + splx(s); + return (1); +wedged: + splx(s); + cnwprintf(nw, "controller wedged @ %d\n", wccline); + return (0); +} + +/* + * do a hardware reset on the card + */ +static int +cnw_reset(struct nw_softc *nw) +{ + int retry = 0; + + nw->nw_creg->nwc_reset = NWR_RESET; + nw->nw_reg->nwr_runstate = NWR_READY; + nw->nw_creg->nwc_reset = NWR_NOTRESET; + + while (!(nw->nw_creg->nwc_isr & NWR_READY)) { + if (++retry > 1000) { + nw->nw_creg->nwc_reset = NWR_RESET; + cnwprintf(nw, "hardware reset failed\n"); + return (1); + } + DELAY(1000); + } + + return (0); +} + +/* + * Set the domain for the card. + * Domains 0x000-0x0ff are add-hoc. + * Domains 0x100-0x1ff are to an access point. + */ +static int +cnw_setdomain(struct nw_softc *nw, int domain) +{ + int s; + + if (domain & ~0x1ff) + return (EINVAL); + + nw->nw_domain = domain; + + if (nw->nw_socket < 0) + return (0); + + s = splnet(); + if (cnw_cmd(nw, NW_CMD_SET_DOMAIN, SA(domain), -1)) + goto wedged; + splx(s); + return (0); +wedged: + splx(s); + cnwprintf(nw, "controller wedged @ %d\n", wccline); + return (EBUSY); +} + +/* + * Set the scramble key for the card + */ +static int +cnw_setkey(struct nw_softc *nw, int key) +{ + int s; + + if (key & ~0xffff) + return (EINVAL); + + nw->nw_skey = key; + + if (nw->nw_socket < 0) + return (0); + + s = splnet(); + if (cnw_cmd(nw, NW_CMD_SET_SKEY, SA(key), -1)) + goto wedged; + splx(s); + return (0); +wedged: + splx(s); + cnwprintf(nw, "controller wedged @ %d\n", wccline); + return (EBUSY); +} + +/* + * We recieved an interrupt indicating that a packet is ready to be read. + * read the packet. An oddity I have noticed is that some packets arrive + * as type 0x0811 instead of type 0x0800. I am not sure why, but convert + * these to 0x0800 so everyone else likes the packet. + */ +static void +cnw_recv(struct nw_softc *nw) +{ + struct mbuf *m; + struct ether_header *eh; + + if ((m = mcopy_in(nw)) != NULL) { +#ifdef RECV_PKT_DEBUG + u_char *b = mtod(m, u_char *); + int n; + printf("cnw%d: read (%d)", nw->nw_unit, m->m_len); + for (n = 0; n < m->m_len; ++n, ++b) + if ((n & 0xf) == 0) + printf("\n%04x %02x", n, *b); + else + printf(" %02x", *b); + printf("\n"); +#endif + + nw->nw_if.if_ipackets++; + eh = mtod(m, struct ether_header *); +#if NBPFILTER > 0 + /* + * Check if there's a bpf filter listening on this interface. + * If so, hand off the raw packet to bpf. + */ +#if defined(__bsdi__) + if (nw->nw_if.if_bpf) + bpf_mtap(nw->nw_if.if_bpf, m); +#elif defined(__FreeBSD__) + if (nw->nw_if.if_bpf) + bpf_mtap(&nw->nw_if, m); +#endif +#endif /*NBPFILTER*/ + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no bpf listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + * + * XXX This test does not support multicasts. + */ + if ((nw->nw_if.if_flags & IFF_PROMISC) + && bcmp(eh->ether_dhost, nw->nw_addr, sizeof(eh->ether_dhost)) +#ifdef MULTICAST + && !ETHER_IS_MULTICAST(eh->ether_dhost) /*also non-broadcast*/ + +#else + && bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dst)) +#endif + ) { + m_freem(m); + return; + } + +#if defined(__bsdi__) + eh->ether_type = ntohs(eh->ether_type); + if (eh->ether_type == 0x811) { + cnwprintf(nw, "converted packet from type 0x0811\n"); + eh->ether_type = 0x800; + } + m_adj(m, sizeof(struct ether_header)) ; + cnwprintf(nw, "recieved packet of type %04x\n", eh->ether_type); +#elif defined(__FreeBSD__) + /* ick! freebsd's ether_input expects ether_type in + network byte order */ + if (ntohs(eh->ether_type) == 0x811) { + cnwprintf(nw, "converted packet from type 0x0811\n"); + eh->ether_type = htons(0x800); + } + m_adj(m, sizeof(struct ether_header)) ; + cnwprintf(nw, "recieved packet of type %04x\n", ntohs(eh->ether_type)); +#endif + + ether_input(&nw->nw_if, eh, m) ; + } +} + +/* + * Send out a command, terminated by a -1. + * Normally each argument is a single unsigned byte, however, when we + * need to send out a short the bit SHORT_ARG will be sent and we + * need to split the short into two bytes. The macro SA(x) will or in + * the SHORT_ARG bit to x. + */ +static int +cnw_cmd(struct nw_softc *nw, ...) +{ + volatile struct nwreg *nwr = nw->nw_reg; + va_list ap; + int i, c; + + if (WCC(nw->nw_creg)) { + printf("cnw%d: controller wedged\n", nw->nw_unit); + return (1); + } + + va_start(ap, nw); + i = 0; + while ((c = va_arg(ap, int)) != -1) { + nwr->nwr_cmd[i++] = c; + if (c & SHORT_ARG) + nwr->nwr_cmd[i++] = c >> 8; + } + nwr->nwr_cmd[i] = 0; /* No more commands. */ + va_end(ap); + return (0); +} + +/* + * Wait for command completion + */ +static int +cnw_cwait(volatile struct nwcreg *nwc) +{ + register int rv = 0x10000; + + while ((nwc->nwc_isr & NWR_READY) == 0 && rv-- > 0) + (void)inb(0x61); + + return (rv == 0); +} + +/* + * debug print routine. It takes a normal printf style format string + * an prepends the unit name before printing it out. + */ +static void +_cnwprintf(struct nw_softc *nw, char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); +#if defined(__bsdi__) + if (nw != NULL) + printf("cnw%d: %r", nw->nw_unit, fmt, ap); + else + printf("%r", fmt, ap); +#elif defined(__FreeBSD__) + if (nw != NULL) { + printf("cnw%d: ", nw->nw_unit); + vprintf(fmt, ap); + } + else + vprintf(fmt, ap); +#endif + va_end(ap); +} + +/* + * Copy the specified mbuf to the next available transmit buffer + * We have already verified there is a transmit buffer available. + */ +static int +mcopy_out(struct nw_softc *nw, struct mbuf *m0) +{ + volatile struct nwreg *nwr = nw->nw_reg; + volatile u_char *bp; + u_short nb; + int len, n, xlen, ml; + u_short buffer, buflen, bufoff; + struct mbuf *m = m0; + char *mp; + + buffer = nwr->nwr_tbuffer; + buflen = nwr->nwr_tlen; + bufoff = nwr->nwr_toff; + +#if 0 /*toku*/ + len = m->m_pkthdr.len; +#else + for (len = 0, m = m0; m; m = m->m_next) + len += m->m_len; +#endif + + cnwprintf(nw, "mcopy_out %d bytes to buffer %x, off %x, len %d\n", + len, buffer, bufoff, buflen); + + nb = 0; + bp = NULL; /* this keeps gcc2 happy */ + xlen = 0; + + m = m0; + ml = m->m_len; + mp = mtod(m, char *); + cnwprintf2(nw, "got an mbuf of %d bytes\n", ml); + + while (m) { + if (nb == 0) { + cnwprintf2(nw, "Copy out buffer %x\n", buffer); + bp = (u_char *)nwr + buffer; + buffer = *(u_short *)bp; + bp += bufoff; + nb = buflen; + cnwprintf2(nw, "buffer has %d bytes (need %d more)\n", + nb, len - xlen); + } + n = nb < ml ? nb : ml; +#ifdef DIAGNOSTIC + if (xlen + n > len) { + printf("cnw%d: mbuf overrun. Wanted to copy %d bytes of len %d\n", nw->nw_unit, xlen + n, len); + break; + } +#endif + bcopy(mp, (void *)bp, n); + ml -= n; + mp += n; + if (ml == 0 && (m = m->m_next) != NULL) { + cnwprintf2(nw, "got an mbuf of %d bytes\n", ml); + ml = m->m_len; + mp = mtod(m, char *); + } + bp += n; + nb -= n; + xlen += n; + cnwprintf2(nw, "copied %d of %d bytes\n", xlen, len); + } + m_freem(m0); + return (xlen < len ? 0 : len); +} + +/* + * Copy the next packet into an mbuf and release the associated buffers + */ +static struct mbuf * +mcopy_in(struct nw_softc *nw) +{ + volatile struct nwreg *nwr = nw->nw_reg; + volatile u_char *bp; + char *mp; + u_short nb, buffer; + int len, n; + struct mbuf *m, *m0, *m1; + + m0 = NULL; + + if (WCC(nw->nw_creg)) goto wedged; + + len = nwr->nwr_rlen; + buffer = nwr->nwr_rbuffer; + cnwprintf(nw, "recieve buffer %x length %d\n", buffer, len); +#ifdef CNW_TRAIL + trail[head].what = NW_CMD_RX_RELEASE; + trail[head].length = len; + microtime(&trail[head].when); + if (++head == 128) + head = 0; +#endif + + if (len <= 0 || len > nw->nw_if.if_mtu + 14) + goto bad; + + MGETHDR(m0, M_DONTWAIT, MT_DATA); + if (m0 != NULL && len + 2 > MHLEN) { + MCLGET(m0, M_DONTWAIT); + cnwprintf2(nw, "using an mbuf cluster\n"); + if ((m0->m_flags & M_EXT) == 0) { + printf("cnw%d: out of mbuf clusters\n", nw->nw_unit); + goto bad; + } + } + + if (m0 == NULL) + goto bad; + + m = m0; + m->m_pkthdr.rcvif = &nw->nw_if; + m->m_pkthdr.len = len; + + /* + * The ethernet header is 14 bytes long. Since we want the + * IP header on a 4 byte boundry, start 2 bytes in + */ + m->m_data += 2; + m->m_len = 0; + nb = 0; + bp = NULL; /* keep gcc2 happy */ + mp = mtod(m, char *); + + while (len > 0) { + if (nb == 0) { + cnwprintf2(nw, "Copy buffer %x\n", buffer); +#ifdef DIAGNOSTIC + if (buffer < 0x200 || buffer > 0x8000) + goto bad; +#endif + bp = (u_char *)nwr + buffer; + buffer = *(u_short *)bp; + nb = *(u_short *)(bp+2); + cnwprintf2(nw, "buffer has %d bytes (need %d more)\n", + nb, len); + bp += *(u_short *)(bp+4); + } + + n = len < nb ? len : nb; + if (M_TRAILINGSPACE(m) == 0) { + MGET(m1, M_DONTWAIT, MT_DATA); + if (m1 != NULL && len > MLEN) + MCLGET(m1, M_DONTWAIT); + if (m1 == NULL) + goto bad; + m->m_next = m1; + m = m1; + m->m_len = 0; + mp = mtod(m, char *); + } + if (M_TRAILINGSPACE(m) < n) + n = M_TRAILINGSPACE(m); + + bcopy((void *)bp, mp, n); + m->m_len += n; + len -= n; + bp += n; + mp += n; + nb -= n; + } + + if (0) { +bad: + if (m0) + m_freem(m0); + m0 = NULL; + } + + if (cnw_cmd(nw, NW_CMD_RX_RELEASE, -1)) + goto wedged; + return (m0); + +wedged: + cnwprintf(nw, "controller wedged @ %d\n", wccline); + if (m0) + m_freem(m0); + return (NULL); +} + +#ifdef USEVBCMP +static int +vbcmp(char volatile *p1, char volatile *p2, int s) +{ + int r = 0; + + while (s-- > 0) + if ((r = (*p1++ - *p2++)) != 0) + break; + return (r); +} + +static void +vbcopy(char volatile *p1, char volatile *p2, int s) +{ + while (s-- > 0) + *p2++ = *p1++; +} +#endif + +#endif /* !defined(__FreeBSD__) || NCNW > 0 */ diff -urN sys.orig/i386/isa/if_cnwioctl.h sys/i386/isa/if_cnwioctl.h --- sys.orig/i386/isa/if_cnwioctl.h Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/if_cnwioctl.h Tue Apr 14 08:44:53 1998 @@ -0,0 +1,52 @@ +/* BSDI $Id: if_cnwioctl.h,v 1.1.2.1 1998/02/23 04:12:01 itojun Exp $ */ +struct cnwstatus { + struct ifreq ifr; + u_char data[0x100]; +}; + +struct cnwstats { + u_int nws_rx; + u_int nws_rxerr; + u_int nws_rxoverflow; + u_int nws_rxoverrun; + u_int nws_rxcrcerror; + u_int nws_rxframe; + u_int nws_rxerrors; + u_int nws_rxavail; + u_int nws_rxone; + u_int nws_tx; + u_int nws_txokay; + u_int nws_txabort; + u_int nws_txlostcd; + u_int nws_txerrors; + u_int nws_txretries[16]; +}; + +struct cnwistats { + struct ifreq ifr; + struct cnwstats stats; +}; + +struct cnwtrail { + u_char what; + u_char status; + u_short length; + struct timeval when; + struct timeval done; +}; + +struct cnwitrail { + struct ifreq ifr; + int head; + struct cnwtrail trail[128]; +}; + +#define ifr_domain ifr_ifru.ifru_flags /* domain */ +#define ifr_key ifr_ifru.ifru_flags /* scramble key */ + +#define SIOCSCNWDOMAIN _IOW('i', 254, struct ifreq) /* set domain */ +#define SIOCGCNWDOMAIN _IOWR('i', 253, struct ifreq) /* get domain */ +#define SIOCSCNWKEY _IOWR('i', 252, struct ifreq) /* set scramble key */ +#define SIOCGCNWSTATUS _IOWR('i', 251, struct cnwstatus)/* get raw status */ +#define SIOCGCNWSTATS _IOWR('i', 250, struct cnwistats)/* get stats */ +#define SIOCGCNWTRAIL _IOWR('i', 249, struct cnwitrail)/* get trail */ diff -urN sys.orig/i386/isa/if_cnwreg.h sys/i386/isa/if_cnwreg.h --- sys.orig/i386/isa/if_cnwreg.h Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/if_cnwreg.h Tue Apr 14 08:44:54 1998 @@ -0,0 +1,213 @@ +/* + * Copyright (c) 1996 Berkeley Software Design, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that this notice is retained, + * the conditions in the following notices are met, and terms applying + * to contributors in the following notices also apply to Berkeley + * Software Design, Inc. + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by + * Berkeley Software Design, Inc. + * 4. Neither the name of the Berkeley Software Design, Inc. nor the names + * of its contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY BERKELEY SOFTWARE DESIGN, INC. ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL BERKELEY SOFTWARE DESIGN, INC. BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * BSDI $Id: if_cnwreg.h,v 1.1.2.1 1997/12/11 14:00:02 itojun Exp $ + * + * This driver is derived from a generic frame work which is + * Copyright(c) 1994,1995,1996 + * Yoichi Shinoda, Yoshitaka Tokugawa, WIDE Project, Wildboar Project + * and Foretune. All rights reserved. + * + * A linux driver was used as the "hardware reference manual" and + * is copyright by: + * + * John Markus Bjørndalen + * Department of Computer Science + * University of Tromsø + * Norway + * johnm@staff.cs.uit.no, http://www.cs.uit.no/~johnm/ + */ + +/* + * Netwave AirSurfer Wireless LAN + * (Formerly known as the Xircom CreditCard Netwave Adapter) + */ + +/* + * Memory based control registers + */ +#define NW_MEM_ADDR 0x20000 + +struct nwreg { +/*000*/ u_char nwr_rsv00[0x100]; +/*100*/ u_char nwr_cmd[0x10]; /* Command buffer (write) */ +/*110*/ u_char nwr_rsv01[4]; + u_char nwr_runstate; /* Run state (write) */ +# define NWR_RUN 0x00 +# define NWR_READY 0x08 /* Ready for command/status read */ +# define NWR_HALT 0x40 + u_char nwr_rsv02[11]; + +/*120*/ u_char nwr_rstat; /* status on rx interrupt */ +#define NWR_RSTAT_RXAVAIL 0x80 +#define NWR_RSTAT_RXERROR 0x40 +#define NWR_RSTAT_OVERFLOW 0x10 +#define NWR_RSTAT_OVERRUN 0x08 +#define NWR_RSTAT_CRCERR 0x04 +#define NWR_RSTAT_FRAME 0x02 + u_char nwr_rsv03[3]; + u_char nwr_rclr; /* bits to clear in rstat (write) */ + u_char nwr_rsv04[11]; + +/*130*/ u_char nwr_tstat; /* status on tx interrupt */ +#define NWR_TSTAT_ABORT 0x80 /* abort */ +#define NWR_TSTAT_CARRLOST 0x40 /* No access point */ +#define NWR_TSTAT_OKAY 0x20 /* packet sent okay */ +#define NWR_TSTAT_TERR 0x10 /* Trasmitter error summary */ +#define NWR_TSTAT_RETRYMASK 0x0f + u_char nwr_rsv05[3]; + u_char nwr_tclr; /* bits to clear in tstat (write) */ + u_char nwr_rsv06[11]; + +/*140*/ u_short nwr_tbuffer; /* first available transmit buffer */ + u_short nwr_tlen; /* data bytes per buffer */ + u_short nwr_toff; /* starting offset into buffer */ + u_char nwr_rsv07[8]; + u_char nwr_lif; /* link integrity field */ + u_char nwr_rsv08[1]; + +/*150*/ short nwr_rlen; /* length of recieved packet */ + short nwr_rbuffer; /* first receive buffer of list */ + u_char nwr_spcq; /* connection quality? */ + u_char nwr_spu; + u_char nwr_isplq; /* link quality? */ + u_char nwr_rsv09[1]; + u_char nwr_hhc; + u_char nwr_rsv10[7]; + +/*160*/ u_char nwr_addr[6]; /* boards ethernet address */ + u_short nwr_rev[2]; /* revision number */ + u_char nwr_rsv11[1]; + u_char nwr_mhs; + u_char nwr_rsv12[2]; + char nwr_id[2]; /* boards id (NW) */ + +/*170*/ short nwr_bmemlen; + short nwr_bmembas; + u_char nwr_rsv13[12]; + +/*180*/ u_short nwr_rsv14[2]; + u_short nwr_rxerr; /* # of recieve errors */ + u_short nwr_frame; /* # of framing errors */ + u_short nwr_ihb; /* input heart beat count */ + u_short nwr_rsv15[2]; + u_short nwr_rxbufs; /* # of buffers received */ +/*190*/ u_short nwr_rxmulti; /* # of multi-buffer packets */ + u_short nwr_txretries; /* # of transmit retries */ + u_short nwr_txabort; /* # of transmit aborts */ + u_short nwr_rsv16[1]; + u_short nwr_ohb; /* output heart beat count */ + u_short nwr_txokay; /* # of transmits okay */ + u_short nwr_txsent; /* # of transmits requested */ + u_short nwr_rsv17[1]; +/*1A0*/ +}; + +/* + * ioport/memory based control registers + * These can either be gotten to by ioports or by memory mapping + */ + +#define NW_REG_ADDR 0x28000 + +struct nwcreg { +/*00*/ u_char nwc_enable; /* Interrupt enable register */ +# define NWR_IENA 0x01 /* Interrupt enable */ +# define NWR_LVLREQ 0x40 /* Level request */ +# define NWR_ENORMAL (NWR_IENA | NWR_LVLREQ) +/*01*/ u_char nwc_rsv00[1]; +/*02*/ u_char nwc_ccsr; /* command complete status register */ +# define NWR_IREADY 0x02 /* Interrupt Ready */ +/*03*/ u_char nwc_rsv01[1]; +/*04*/ u_char nwc_isr; /* interupt status register */ +# define NWR_RXAVAIL 0x80 /* Packet Received */ +# define NWR_RXERR 0x40 /* Receive status change */ +# define NWR_TXSTAT 0x20 /* Transmit status change */ +# define NWR_READY 0x08 /* Ready for command/status read */ +# define NWR_TXEMP 0x01 /* Transmit descriptor available */ +/*05*/ u_char nwc_rsv02[1]; +/*06*/ u_short nwc_io; +/*08*/ u_char nwc_ioctl; +/*09*/ u_char nwc_rsv03[1]; +/*0A*/ u_char nwc_ictrl; /* interrupt control register */ +# define NWR_IDISABLE 0x00 +# define NWR_IENABLE 0x02 +# define NWR_IDISUS 0x10 /* make unused sources not interrupt */ +# define NWR_INORMAL (NWR_IENABLE | NWR_IDISUS) +/*0B*/ u_char nwc_rsv04[1]; +/*0C*/ u_char nwc_reset; /* resetting runstate */ +# define NWR_RESET 0x80 +# define NWR_NOTRESET 0 +/*0D*/ u_char nwc_rsv05[3]; +}; + + +/* command bytes */ + +#define SHORT_ARG 0x10000 /* Argument is 16 bits, not 8 */ +#define SA(x) (x | SHORT_ARG) + +#define NW_CMD_INIT 0x0 /* Initialize */ +#define NW_CMD_SET_RXMODE 0x1 /* Set receive mode */ +# define NW_RXENA 0x80 /* Receive Enable */ +# define NW_RXMAC 0x20 /* MAC host receive mode*/ +# define NW_RXPROM 0x10 /* Promiscuous */ +# define NW_RXMCAST 0x08 /* Accept Multicast Packets */ +# define NW_RXBCAST 0x04 /* Accept Broadcast Packets */ + /* + * Determine the default receive mode + */ +# define NW_RXMODE(nw) ((nw->nw_if.if_flags & IFF_PROMISC) ? \ + (NW_RXENA | NW_RXMCAST | NW_RXBCAST | NW_RXPROM) : \ + (NW_RXENA | NW_RXMCAST | NW_RXBCAST)) + +#define NW_CMD_SET_TXMODE 0x2 /* Set transmit mode */ +# define NW_TXENA 0x80 /* Transmit Enable */ +# define NW_TXMAC 0x20 /* Host sends MAC mode */ +# define NW_TXUDATA 0x10 /* Enable Uni-Data packets */ +# define NW_TXSCRAMBLE 0x02 /* Scramble data packets */ +# define NW_TXLOOPBACK 0x01 /* Loopback mode */ +#define NW_CMD_ADD_MCADDR 0x3 /* Add mc addr */ +#define NW_CMD_DEL_MCADDR 0x4 /* Delete mc addr */ +#define NW_CMD_MC_CTRL 0x5 /* Enable/Disable mc (?) */ +#define NW_CMD_RUN 0x6 /* You can move */ +#define NW_CMD_HALT 0x7 /* Don't move */ +#define NW_CMD_TX_START 0x8 /* Start tx on current tx desc */ +#define NW_CMD_RX_RELEASE 0x9 /* Release current rx desc */ +#define NW_CMD_SET_SKEY 0xa /* Set scramble key */ +#define NW_CMD_SET_DOMAIN 0xb /* Set domain */ +# define NW_ACCESSPOINT 0x100 /* Default access point domain */ +# define NW_ADDHOC 0x0 /* Default add hoc domain */ +# define NW_DOMAIN NW_ADDHOC /* Default domain */ diff -urN sys.orig/i386/isa/if_ed.c sys/i386/isa/if_ed.c --- sys.orig/i386/isa/if_ed.c Sun Nov 2 16:15:08 1997 +++ sys/i386/isa/if_ed.c Tue Apr 14 08:44:55 1998 @@ -134,6 +134,7 @@ u_char rec_page_start; /* first page of RX ring-buffer */ u_char rec_page_stop; /* last page of RX ring-buffer */ u_char next_packet; /* pointer to next unread RX packet */ + u_char delay_output; struct ifmib_iso_8802_3 mibdata; /* stuff for network mgmt */ }; @@ -164,7 +165,7 @@ #include "card.h" #if NCARD > 0 -static int ed_probe_pccard __P((struct isa_device *, u_char *)); +static int ed_probe_pccard __P((struct isa_device *)); #endif static void ds_getmcaf __P((struct ed_softc *, u_long *)); @@ -224,6 +225,8 @@ static int edinit(struct pccard_devinfo *devi) { + int i; + u_char e; struct ed_softc *sc = &ed_softc[devi->isahd.id_unit]; /* validate unit number. */ @@ -234,8 +237,14 @@ * device was found at the location. */ sc->gone = 0; - if (ed_probe_pccard(&devi->isahd, devi->misc) == 0) + if (ed_probe_pccard(&devi->isahd)==0) return(ENXIO); + e = 0; + for (i = 0; i < ETHER_ADDR_LEN; ++i) + e |= devi->misc[i]; + if (e) + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->arpcom.ac_enaddr[i] = devi->misc[i]; if (ed_attach_isa(&devi->isahd) == 0) return(ENXIO); @@ -1294,17 +1303,16 @@ * supplied (from the CIS), relying on the probe to find it instead. */ static int -ed_probe_pccard(isa_dev, ether) +ed_probe_pccard(isa_dev) struct isa_device *isa_dev; - u_char *ether; { int nports; - nports = ed_probe_WD80x3(isa_dev); + nports = ed_probe_Novell(isa_dev); if (nports) return (nports); - nports = ed_probe_Novell(isa_dev); + nports = ed_probe_WD80x3(isa_dev); if (nports) return (nports); @@ -1642,6 +1650,8 @@ { struct ifnet *ifp = &sc->arpcom.ac_if; + sc->delay_output = (flags & ED_FLAGS_DELAY_OUTPUT); + /* * Set interface to stopped condition (reset) */ @@ -2887,7 +2897,8 @@ * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); - + if (sc->delay_output) + DELAY(5); } else if ((sc->vendor == ED_VENDOR_HP) && (sc->type == ED_TYPE_HP_PCLANPLUS)) { diff -urN sys.orig/i386/isa/if_edreg.h sys/i386/isa/if_edreg.h --- sys.orig/i386/isa/if_edreg.h Wed Aug 7 20:18:23 1996 +++ sys/i386/isa/if_edreg.h Tue Apr 14 08:44:55 1998 @@ -598,6 +598,13 @@ #define ED_FLAGS_FORCE_PIO 0x0010 /* + * This flag is for cards with incomplete DMA status register. I don't + * know whether it works, but Linux PCMCIA package has this flag. + * Tatsumi Hosokawa (hosokawa@jp.FreeBSD.org) + */ +#define ED_FLAGS_DELAY_OUTPUT 0x0020 + +/* * Definitions for Western digital/SMC WD80x3 series ASIC */ /* @@ -983,6 +990,25 @@ * Reset register; reading from this register causes a board reset */ #define ED_NOVELL_RESET 0x0f + +/* + * Definitions for PCCARD + */ +#define ED_PC_PAGE_OFFSET 0x40 /* page offset for NIC access to mem */ +#define ED_PC_IO_PORTS 32 +#define ED_PC_RESET 0x1f +#define ED_PC_MISC 0x18 +#define ED_PC_ASIC_OFFSET 0x10 + +/* + * if_ze.h constants + */ + +#define ZE_PAGE_OFFSET 0x40 /* mem buffer starts at 0x4000 */ + +#define ZE_DATA_IO 0x10 +#define ZE_MISC 0x18 +#define ZE_RESET 0x1F /* * Definitions for PCCARD diff -urN sys.orig/i386/isa/if_ep.c sys/i386/isa/if_ep.c --- sys.orig/i386/isa/if_ep.c Sat Nov 29 23:41:48 1997 +++ sys/i386/isa/if_ep.c Tue Apr 14 08:44:57 1998 @@ -129,6 +129,9 @@ static void epstart __P((struct ifnet *)); static void epstop __P((struct ep_softc *)); static void epwatchdog __P((struct ifnet *)); +static size_t ep_countmcast __P((struct ep_softc *)); +static void ep_setmcast __P((struct ep_softc *)); +void epeitr(int unit); #if 0 static int send_ID_sequence __P((int)); @@ -586,10 +589,11 @@ struct sockaddr_dl *sdl; u_short *p; int i; + static int ep_attached[NEP]; int attached; sc->gone = 0; - attached = (ifp->if_softc != 0); + attached = ep_attached[sc->unit]; printf("ep%d: ", sc->unit); /* @@ -670,6 +674,7 @@ bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); } #endif + ep_attached[sc->unit] = 1; return 0; } @@ -730,12 +735,7 @@ outw(BASE + EP_COMMAND, SET_INTR_MASK | S_5_INTS); - if (ifp->if_flags & IFF_PROMISC) - outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | - FIL_GROUP | FIL_BRDCST | FIL_ALL); - else - outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | - FIL_GROUP | FIL_BRDCST); + ep_setmcast(sc); /* * S.B. @@ -1333,12 +1333,20 @@ } break; case SIOCADDMULTI: + error = ether_addmulti(ifr, &sc->arpcom); + if (error == ENETRESET) { + /* update multicast filter list. */ + ep_setmcast(sc); + error = 0; + } + break; case SIOCDELMULTI: - /* Now this driver has no support for programmable - * multicast filters. If some day it will gain this - * support this part of code must be extended. - */ - error = 0; + error = ether_delmulti(ifr, &sc->arpcom); + if (error == ENETRESET) { + /* update multicast filter list. */ + ep_setmcast(sc); + error = 0; + } break; default: error = EINVAL; @@ -1435,6 +1443,52 @@ for (i = 0; i < 16; i++) data = (data << 1) | (inw(id_port) & 1); return (data); +} + +static size_t +ep_countmcast(sc) + struct ep_softc *sc; +{ + register struct ether_multi *enm; + register struct ether_multistep step; + size_t count; + + count = 0; + ETHER_FIRST_MULTI(step, &sc->arpcom, enm); + while (enm != NULL) { + count++; + ETHER_NEXT_MULTI(step, enm); + } + + return count; +} + +static void +ep_setmcast(sc) + struct ep_softc *sc; +{ + struct ifnet *ifp = (struct ifnet *)sc; + int dopromisc = 0; + + if (ifp->if_flags & IFF_PROMISC) + dopromisc = 1; + else if (ifp->if_flags & IFF_MULTICAST) { + if (ifp->if_flags & IFF_ALLMULTI) + dopromisc = 1; + else if (ep_countmcast(sc)) + dopromisc = 1; + else + dopromisc = 0; + } else + dopromisc = 0; + + if (dopromisc) { + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST | FIL_ALL); + } else { + outw(BASE + EP_COMMAND, SET_RX_FILTER | FIL_INDIVIDUAL | + FIL_GROUP | FIL_BRDCST); + } } #endif /* NEP > 0 */ diff -urN sys.orig/i386/isa/if_sn.c sys/i386/isa/if_sn.c --- sys.orig/i386/isa/if_sn.c Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/if_sn.c Tue Apr 14 08:47:54 1998 @@ -0,0 +1,1741 @@ +/* + * Copyright (c) 1996 Gardner Buchanan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gardner Buchanan. + * 4. The name of Gardner Buchanan may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: if_sn.c,v 1.1.2.3 1998/02/19 05:01:51 hosokawa Exp $ + */ + +/* + * This is a driver for SMC's 9000 series of Ethernet adapters. + * + * This FreeBSD driver is derived from the smc9194 Linux driver by + * Erik Stahlman and is Copyright (C) 1996 by Erik Stahlman. + * This driver also shamelessly borrows from the FreeBSD ep driver + * which is Copyright (C) 1994 Herb Peyerl + * All rights reserved. + * + * It is set up for my SMC91C92 equipped Ampro LittleBoard embedded + * PC. It is adapted from Erik Stahlman's Linux driver which worked + * with his EFA Info*Express SVC VLB adaptor. According to SMC's databook, + * it will work for the entire SMC 9xxx series. (Ha Ha) + * + * "Features" of the SMC chip: + * 4608 byte packet memory. (for the 91C92. Others have more) + * EEPROM for configuration + * AUI/TP selection + * + * Authors: + * Erik Stahlman erik@vt.edu + * Herb Peyerl hpeyerl@novatel.ca + * Andres Vega Garcia avega@sophia.inria.fr + * Serge Babkin babkin@hq.icb.chel.su + * Gardner Buchanan gbuchanan@shl.com + * + * Sources: + * o SMC databook + * o "smc9194.c:v0.10(FIXED) 02/15/96 by Erik Stahlman (erik@vt.edu)" + * o "if_ep.c,v 1.19 1995/01/24 20:53:45 davidg Exp" + * + * Known Bugs: + * o The hardware multicast filter isn't used yet. + * o Setting of the hardware address isn't supported. + * o Hardware padding isn't used. + */ + +/* + * Modifications for Megahertz X-Jack Ethernet Card (XJ-10BT) + * + * Copyright (c) 1996 by Tatsumi Hosokawa + * BSD-nomads, Tokyo, Japan. + */ +/* + * Multicast support by Kei TANAKA + * Special thanks to itojun@itojun.org + */ +#include "sn.h" +#if NSN > 0 + +#undef SN_DEBUG /* (by hosokawa) */ + +#include "bpfilter.h" + +#include +#if defined(__FreeBSD__) +#include +#include +#endif +#include +#include +#include +#include +#include +#if defined(__NetBSD__) +#include +#endif + +#include +#include +#include + +#ifdef INET +#include +#include +#include +#include +#include +#endif + +#ifdef NS +#include +#include +#endif + +#if NBPFILTER > 0 +#include +#include +#endif + +#if defined(__FreeBSD__) +#include +#endif + +#include +#include +#include + +/* PCCARD suport */ +#include "card.h" +#if NCARD > 0 +#include +#include +#include +#include +#endif /* NCARD > 0 */ + +#include + +/* for PCMCIA Ethernet */ +static int sn_pccard[NSN]; /* set to 1 if it's PCMCIA card */ +static u_char sn_pccard_macaddr[NSN][6]; +static int sn_import_macaddr[NSN]; + +static int snprobe __P((struct isa_device *)); +static int snattach __P((struct isa_device *)); +static int snioctl __P((struct ifnet * ifp, int, caddr_t)); + +static int smc_probe __P((int ioaddr, int pccard)); +static void snresume __P((struct ifnet *)); + +void sninit __P((int)); +void snintr __P((int)); +void snread __P((struct ifnet *)); +void snreset __P((int)); +void snstart __P((struct ifnet *)); +void snstop __P((int)); +void snwatchdog __P((struct ifnet *)); + +static void sn_setmcast(struct sn_softc *); +static int sn_getmcf(struct arpcom *ac, u_char *mcf); +static u_int smc_crc(u_char *); + +/* I (GB) have been unlucky getting the hardware padding + * to work properly. + */ +#define SW_PAD + +struct sn_softc sn_softc[NSN]; + +struct isa_driver sndriver = { + snprobe, + snattach, + "sn", + 0 +}; + + +/* PCCARD Support */ +#if NCARD > 0 +/* + * PC-Card (PCMCIA) specific code. + */ +static int sn_card_intr(struct pccard_devinfo *); /* Interrupt handler */ +static void snunload(struct pccard_devinfo *); /* Disable driver */ +static void snsuspend(struct pccard_devinfo *); /* Suspend driver */ +static int sn_card_init(struct pccard_devinfo *, int); /* init device */ + +static struct pccard_device sn_info = +{ + "sn", + sn_card_init, + snunload, + sn_card_intr, +/* snsuspend, */ + 0, /* Attributes - presently unused */ + &net_imask /* Interrupt mask for device */ + /* This should also include net_imask?? */ +}; + +DATA_SET(pccarddrv_set, sn_info); + +static void +snsuspend(struct pccard_devinfo *devi) +{ + int unit = devi->isahd.id_unit; + struct sn_softc *sc = &sn_softc[devi->isahd.id_unit]; + + printf("sn%d: suspending\n", unit); + sc->gone = 1; +} + +static int +sn_card_init(struct pccard_devinfo *devi, int first) +{ + int unit = devi->isahd.id_unit; + struct sn_softc *sc = &sn_softc[devi->isahd.id_unit]; + + sn_pccard[unit] = 1; + sn_import_macaddr[unit] = 0; + if (devi->misc[0] | devi->misc[1] | devi->misc[2]) { + int i; + for (i = 0; i < 6; i++) { + sn_pccard_macaddr[unit][i] = devi->misc[i]; + } + sn_import_macaddr[unit] = 1; + } + + if (first) { + sc->gone = 0; + /* + * validate unit number. + */ + if (unit >= NSN) + return ENODEV; + /* + * Probe the device. If a value is returned, the + * device was found at the location. + */ +#ifdef SN_DEBUG +printf("snprobe()\n"); +#endif + if (snprobe(&devi->isahd)==0) + return ENXIO; +#ifdef SN_DEBUG +printf("snattach()\n"); +#endif + if (snattach(&devi->isahd)==0) + return ENXIO; + /* initialize interface dynamically */ + sc->arpcom.ac_if.if_snd.ifq_maxlen = ifqmaxlen; + } + else { + sc->gone = 0; + } + + return 0; +} + +static void +snunload(struct pccard_devinfo *devi) +{ + int unit = devi->isahd.id_unit; + struct sn_softc *sc = &sn_softc[devi->isahd.id_unit]; + + if (sc->gone) { + printf("sn%d: already unloaded.\n", unit); + return; + } + + snstop(unit); + sc->gone = 1; + sc->arpcom.ac_if.if_flags &= ~IFF_RUNNING; + printf("sn%d: unload.\n", unit); +} + +static int +sn_card_intr(struct pccard_devinfo *devi) +{ + int unit = devi->isahd.id_unit; + snintr(unit); + return(1); +} + +#endif /* NCARD > 0 */ + + +int +snprobe(struct isa_device *is) +{ + /* + * Device was configured with 'port ?' In this case we complain + */ + if (is->id_iobase == -1) { /* port? */ + printf("sn%d: SMC91Cxx cannot determine ioaddr\n", is->id_unit); + return 0; + } + /* + * Device was configured with 'irq ?' In this case we complain + */ + if (is->id_irq == 0) { + printf("sn%d: SMC91Cxx cannot determine irq\n", is->id_unit); + return (0); + } + /* + * Device was configured with 'port xxx', 'irq xx' In this case we + * search for the card with that address + */ + if (smc_probe(is->id_iobase, sn_pccard[is->id_unit]) != 0) + return (0); + + return (SMC_IO_EXTENT); +} + + +static int +snattach(struct isa_device *is) +{ + struct sn_softc *sc = &sn_softc[is->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + u_short i; + int j; + u_char *p; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + int rev; + u_short address; +#if NCARD > 0 + static int alredy_ifatch[NSN]; +#endif + + /* + * This is the value used for BASE + */ + sc->sn_io_addr = is->id_iobase; + + sc->pages_wanted = -1; + + printf("sn%d: ", is->id_unit); + + SMC_SELECT_BANK(3); + rev = inw(BASE + REVISION_REG_W); + if (chip_ids[(rev >> 4) & 0xF]) + printf("%s ", chip_ids[(rev >> 4) & 0xF]); + + SMC_SELECT_BANK(1); + i = inw(BASE + CONFIG_REG_W); + printf(i & CR_AUI_SELECT ? "AUI" : "UTP"); + + if (sn_import_macaddr[is->id_unit]) { + for (j = 0; j < 3; j++) { + u_short w; + + w = (u_short)sn_pccard_macaddr[is->id_unit][j * 2] | + (((u_short)sn_pccard_macaddr[is->id_unit][j * 2 + 1]) << 8); + outw(BASE + IAR_ADDR0_REG_W + j * 2, w); + } + } + + /* + * Read the station address from the chip. The MAC address is bank 1, + * regs 4 - 9 + */ + SMC_SELECT_BANK(1); + p = (u_char *) & sc->arpcom.ac_enaddr; + for (i = 0; i < 6; i += 2) { + address = inw(BASE + IAR_ADDR0_REG_W + i); + p[i + 1] = address >> 8; + p[i] = address & 0xFF; + } + printf(" MAC address %6D\n", sc->arpcom.ac_enaddr, ":"); + ifp->if_unit = is->id_unit; + ifp->if_name = "sn"; + ifp->if_mtu = ETHERMTU; + ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; + ifp->if_output = ether_output; + ifp->if_start = snstart; + ifp->if_ioctl = snioctl; + ifp->if_watchdog = snwatchdog; + + ifp->if_timer = 0; + +#if NCARD > 0 + if (alredy_ifatch[is->id_unit] != 1) { + if_attach( ifp ); + alredy_ifatch[is->id_unit] = 1; + } +#else + if_attach(ifp); +#endif + ether_ifattach(ifp); + /* + * Fill the hardware address into ifa_addr if we find an AF_LINK + * entry. We need to do this so bpf's can get the hardware addr of + * this card. netstat likes this too! + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + sdl = (struct sockaddr_dl *) ifa->ifa_addr; + sdl->sdl_type = IFT_ETHER; + sdl->sdl_alen = ETHER_ADDR_LEN; + sdl->sdl_slen = 0; + bcopy(sc->arpcom.ac_enaddr, LLADDR(sdl), ETHER_ADDR_LEN); + } + snstop(is->id_unit); + +#if NBPFILTER > 0 + bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + + return 1; +} + + +/* + * Reset and initialize the chip + */ +void +sninit(int unit) +{ + register struct sn_softc *sc = &sn_softc[unit]; + register struct ifnet *ifp = &sc->arpcom.ac_if; + int s; + int flags; + int mask; + +#if NCARD > 0 + if (sc->gone) + return; +#endif + s = splimp(); + + /* + * This resets the registers mostly to defaults, but doesn't affect + * EEPROM. After the reset cycle, we pause briefly for the chip to + * be happy. + */ + SMC_SELECT_BANK(0); + outw(BASE + RECV_CONTROL_REG_W, RCR_SOFTRESET); + SMC_DELAY(); + outw(BASE + RECV_CONTROL_REG_W, 0x0000); + SMC_DELAY(); + SMC_DELAY(); + + outw(BASE + TXMIT_CONTROL_REG_W, 0x0000); + + /* + * Set the control register to automatically release succesfully + * transmitted packets (making the best use out of our limited + * memory) and to enable the EPH interrupt on certain TX errors. + */ + SMC_SELECT_BANK(1); + outw(BASE + CONTROL_REG_W, (CTR_AUTO_RELEASE | CTR_TE_ENABLE | + CTR_CR_ENABLE | CTR_LE_ENABLE)); + + /* + * Reset the MMU and wait for it to be un-busy. + */ + SMC_SELECT_BANK(2); + outw(BASE + MMU_CMD_REG_W, MMUCR_RESET); + while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ + ; + + /* + * Disable all interrupts + */ + outb(BASE + INTR_MASK_REG_B, 0x00); + + sn_setmcast(sc); + + /* + * Set the transmitter control. We want it enabled. + */ + flags = TCR_ENABLE; + +#ifndef SW_PAD + /* + * I (GB) have been unlucky getting this to work. + */ + flags |= TCR_PAD_ENABLE; +#endif /* SW_PAD */ + + outw(BASE + TXMIT_CONTROL_REG_W, flags); + + + /* + * Now, enable interrupts + */ + SMC_SELECT_BANK(2); + + mask = IM_EPH_INT | + IM_RX_OVRN_INT | + IM_RCV_INT | + IM_TX_INT; + + outb(BASE + INTR_MASK_REG_B, mask); + sc->intr_mask = mask; + sc->pages_wanted = -1; + + + /* + * Mark the interface running but not active. + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * Attempt to push out any waiting packets. + */ + snstart(ifp); + + splx(s); +} + + +void +snstart(struct ifnet *ifp) +{ + register struct sn_softc *sc = &sn_softc[ifp->if_unit]; + register u_int len; + register struct mbuf *m; + struct mbuf *top; + int s, pad; + int mask; + u_short length; + u_short numPages; + u_char packet_no; + int time_out; + +#if NCARD > 0 + if (sc->gone) + return; +#endif + + s = splimp(); + + if (sc->arpcom.ac_if.if_flags & IFF_OACTIVE) { + splx(s); + return; + } + if (sc->pages_wanted != -1) { + splx(s); + printf("sn%d: snstart() while memory allocation pending\n", + ifp->if_unit); + return; + } +startagain: + + /* + * Sneak a peek at the next packet + */ + m = sc->arpcom.ac_if.if_snd.ifq_head; + if (m == 0) { + splx(s); + return; + } + /* + * Compute the frame length and set pad to give an overall even + * number of bytes. Below we assume that the packet length is even. + */ + for (len = 0, top = m; m; m = m->m_next) + len += m->m_len; + + pad = (len & 1); + + /* + * We drop packets that are too large. Perhaps we should truncate + * them instead? + */ + if (len + pad > ETHER_MAX_LEN - ETHER_CRC_LEN) { + + printf("sn%d: large packet discarded (A)\n", ifp->if_unit); + + ++sc->arpcom.ac_if.if_oerrors; + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + m_freem(m); + goto readcheck; + } +#ifdef SW_PAD + + /* + * If HW padding is not turned on, then pad to ETHER_MIN_LEN. + */ + if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) + pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len; + +#endif /* SW_PAD */ + + length = pad + len; + + /* + * The MMU wants the number of pages to be the number of 256 byte + * 'pages', minus 1 (A packet can't ever have 0 pages. We also + * include space for the status word, byte count and control bytes in + * the allocation request. + */ + numPages = (length + 6) >> 8; + + + /* + * Now, try to allocate the memory + */ + SMC_SELECT_BANK(2); + outw(BASE + MMU_CMD_REG_W, MMUCR_ALLOC | numPages); + + /* + * Wait a short amount of time to see if the allocation request + * completes. Otherwise, I enable the interrupt and wait for + * completion asyncronously. + */ + + time_out = MEMORY_WAIT_TIME; + do { + if (inb(BASE + INTR_STAT_REG_B) & IM_ALLOC_INT) + break; + } while (--time_out); + + if (!time_out) { + + /* + * No memory now. Oh well, wait until the chip finds memory + * later. Remember how many pages we were asking for and + * enable the allocation completion interrupt. Also set a + * watchdog in case we miss the interrupt. We mark the + * interface active since there is no point in attempting an + * snstart() until after the memory is available. + */ + mask = inb(BASE + INTR_MASK_REG_B) | IM_ALLOC_INT; + outb(BASE + INTR_MASK_REG_B, mask); + sc->intr_mask = mask; + + sc->arpcom.ac_if.if_timer = 1; + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + sc->pages_wanted = numPages; + + splx(s); + return; + } + /* + * The memory allocation completed. Check the results. + */ + packet_no = inb(BASE + ALLOC_RESULT_REG_B); + if (packet_no & ARR_FAILED) { + printf("sn%d: Memory allocation failed\n", ifp->if_unit); + goto startagain; + } + /* + * We have a packet number, so tell the card to use it. + */ + outb(BASE + PACKET_NUM_REG_B, packet_no); + + /* + * Point to the beginning of the packet + */ + outw(BASE + POINTER_REG_W, PTR_AUTOINC | 0x0000); + + /* + * Send the packet length (+6 for status, length and control byte) + * and the status word (set to zeros) + */ + outw(BASE + DATA_REG_W, 0); + outb(BASE + DATA_REG_B, (length + 6) & 0xFF); + outb(BASE + DATA_REG_B, (length + 6) >> 8); + + /* + * Get the packet from the kernel. This will include the Ethernet + * frame header, MAC Addresses etc. + */ + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + + /* + * Push out the data to the card. + */ + for (top = m; m != 0; m = m->m_next) { + + /* + * Push out words. + */ + outsw(BASE + DATA_REG_W, mtod(m, caddr_t), m->m_len / 2); + + /* + * Push out remaining byte. + */ + if (m->m_len & 1) + outb(BASE + DATA_REG_B, *(mtod(m, caddr_t) + m->m_len - 1)); + } + + /* + * Push out padding. + */ + while (pad > 1) { + outw(BASE + DATA_REG_W, 0); + pad -= 2; + } + if (pad) + outb(BASE + DATA_REG_B, 0); + + /* + * Push out control byte and unused packet byte The control byte is 0 + * meaning the packet is even lengthed and no special CRC handling is + * desired. + */ + outw(BASE + DATA_REG_W, 0); + + /* + * Enable the interrupts and let the chipset deal with it Also set a + * watchdog in case we miss the interrupt. + */ + mask = inb(BASE + INTR_MASK_REG_B) | (IM_TX_INT | IM_TX_EMPTY_INT); + outb(BASE + INTR_MASK_REG_B, mask); + sc->intr_mask = mask; + + outw(BASE + MMU_CMD_REG_W, MMUCR_ENQUEUE); + + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + sc->arpcom.ac_if.if_timer = 1; + +#if NBPFILTER > 0 + if (ifp->if_bpf) { + bpf_mtap(ifp, top); + } +#endif + + sc->arpcom.ac_if.if_opackets++; + m_freem(top); + + +readcheck: + + /* + * Is another packet coming in? We don't want to overflow the tiny + * RX FIFO. If nothing has arrived then attempt to queue another + * transmit packet. + */ + if (inw(BASE + FIFO_PORTS_REG_W) & FIFO_REMPTY) + goto startagain; + + splx(s); + return; +} + + + +/* Resume a packet transmit operation after a memory allocation + * has completed. + * + * This is basically a hacked up copy of snstart() which handles + * a completed memory allocation the same way snstart() does. + * It then passes control to snstart to handle any other queued + * packets. + */ +static void +snresume(struct ifnet *ifp) +{ + register struct sn_softc *sc = &sn_softc[ifp->if_unit]; + register u_int len; + register struct mbuf *m; + struct mbuf *top; + int pad; + int mask; + u_short length; + u_short numPages; + u_short pages_wanted; + u_char packet_no; + + if (sc->pages_wanted < 0) + return; + + pages_wanted = sc->pages_wanted; + sc->pages_wanted = -1; + + /* + * Sneak a peek at the next packet + */ + m = sc->arpcom.ac_if.if_snd.ifq_head; + if (m == 0) { + printf("sn%d: snresume() with nothing to send\n", ifp->if_unit); + return; + } + /* + * Compute the frame length and set pad to give an overall even + * number of bytes. Below we assume that the packet length is even. + */ + for (len = 0, top = m; m; m = m->m_next) + len += m->m_len; + + pad = (len & 1); + + /* + * We drop packets that are too large. Perhaps we should truncate + * them instead? + */ + if (len + pad > ETHER_MAX_LEN - ETHER_CRC_LEN) { + + printf("sn%d: large packet discarded (B)\n", ifp->if_unit); + + ++sc->arpcom.ac_if.if_oerrors; + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + m_freem(m); + return; + } +#ifdef SW_PAD + + /* + * If HW padding is not turned on, then pad to ETHER_MIN_LEN. + */ + if (len < ETHER_MIN_LEN - ETHER_CRC_LEN) + pad = ETHER_MIN_LEN - ETHER_CRC_LEN - len; + +#endif /* SW_PAD */ + + length = pad + len; + + + /* + * The MMU wants the number of pages to be the number of 256 byte + * 'pages', minus 1 (A packet can't ever have 0 pages. We also + * include space for the status word, byte count and control bytes in + * the allocation request. + */ + numPages = (length + 6) >> 8; + + + SMC_SELECT_BANK(2); + + /* + * The memory allocation completed. Check the results. If it failed, + * we simply set a watchdog timer and hope for the best. + */ + packet_no = inb(BASE + ALLOC_RESULT_REG_B); + if (packet_no & ARR_FAILED) { + + printf("sn%d: Memory allocation failed. Weird.\n", ifp->if_unit); + + sc->arpcom.ac_if.if_timer = 1; + + goto try_start; + return; + } + /* + * We have a packet number, so tell the card to use it. + */ + outb(BASE + PACKET_NUM_REG_B, packet_no); + + /* + * Now, numPages should match the pages_wanted recorded when the + * memory allocation was initiated. + */ + if (pages_wanted != numPages) { + + printf("sn%d: memory allocation wrong size. Weird.\n", ifp->if_unit); + + /* + * If the allocation was the wrong size we simply release the + * memory once it is granted. Wait for the MMU to be un-busy. + */ + while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ + ; + outw(BASE + MMU_CMD_REG_W, MMUCR_FREEPKT); + + return; + } + /* + * Point to the beginning of the packet + */ + outw(BASE + POINTER_REG_W, PTR_AUTOINC | 0x0000); + + /* + * Send the packet length (+6 for status, length and control byte) + * and the status word (set to zeros) + */ + outw(BASE + DATA_REG_W, 0); + outb(BASE + DATA_REG_B, (length + 6) & 0xFF); + outb(BASE + DATA_REG_B, (length + 6) >> 8); + + /* + * Get the packet from the kernel. This will include the Ethernet + * frame header, MAC Addresses etc. + */ + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + + /* + * Push out the data to the card. + */ + for (top = m; m != 0; m = m->m_next) { + + /* + * Push out words. + */ + outsw(BASE + DATA_REG_W, mtod(m, caddr_t), m->m_len / 2); + + /* + * Push out remaining byte. + */ + if (m->m_len & 1) + outb(BASE + DATA_REG_B, *(mtod(m, caddr_t) + m->m_len - 1)); + } + + /* + * Push out padding. + */ + while (pad > 1) { + outw(BASE + DATA_REG_W, 0); + pad -= 2; + } + if (pad) + outb(BASE + DATA_REG_B, 0); + + /* + * Push out control byte and unused packet byte The control byte is 0 + * meaning the packet is even lengthed and no special CRC handling is + * desired. + */ + outw(BASE + DATA_REG_W, 0); + + /* + * Enable the interrupts and let the chipset deal with it Also set a + * watchdog in case we miss the interrupt. + */ + mask = inb(BASE + INTR_MASK_REG_B) | (IM_TX_INT | IM_TX_EMPTY_INT); + outb(BASE + INTR_MASK_REG_B, mask); + sc->intr_mask = mask; + outw(BASE + MMU_CMD_REG_W, MMUCR_ENQUEUE); + +#if NBPFILTER > 0 + if (ifp->if_bpf) { + bpf_mtap(ifp, top); + } +#endif + + sc->arpcom.ac_if.if_opackets++; + m_freem(top); + +try_start: + + /* + * Now pass control to snstart() to queue any additional packets + */ + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + snstart(ifp); + + /* + * We've sent something, so we're active. Set a watchdog in case the + * TX_EMPTY interrupt is lost. + */ + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + sc->arpcom.ac_if.if_timer = 1; + + return; +} + + +void +snintr(int unit) +{ + int status, interrupts; + register struct sn_softc *sc = &sn_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + int x; + + /* + * Chip state registers + */ + u_char mask; + u_char packet_no; + u_short tx_status; + u_short card_stats; + +#if NCARD > 0 + if (sc->gone) + return; +#endif + + /* + * if_ep.c did this, so I do too. Yet if_ed.c doesn't. I wonder... + */ + x = splbio(); + + /* + * Clear the watchdog. + */ + ifp->if_timer = 0; + + SMC_SELECT_BANK(2); + + /* + * Obtain the current interrupt mask and clear the hardware mask + * while servicing interrupts. + */ + mask = inb(BASE + INTR_MASK_REG_B); + outb(BASE + INTR_MASK_REG_B, 0x00); + + /* + * Get the set of interrupts which occurred and eliminate any which + * are masked. + */ + interrupts = inb(BASE + INTR_STAT_REG_B); + status = interrupts & mask; + + /* + * Now, process each of the interrupt types. + */ + + /* + * Receive Overrun. + */ + if (status & IM_RX_OVRN_INT) { + + /* + * Acknowlege Interrupt + */ + SMC_SELECT_BANK(2); + outb(BASE + INTR_ACK_REG_B, IM_RX_OVRN_INT); + + ++sc->arpcom.ac_if.if_ierrors; + } + /* + * Got a packet. + */ + if (status & IM_RCV_INT) { +#if 1 + int packet_number; + + SMC_SELECT_BANK(2); + packet_number = inw(BASE + FIFO_PORTS_REG_W); + + if (packet_number & FIFO_REMPTY) { + + /* + * we got called , but nothing was on the FIFO + */ + printf("sn: Receive interrupt with nothing on FIFO\n"); + + goto out; + } +#endif + snread(ifp); + } + /* + * An on-card memory allocation came through. + */ + if (status & IM_ALLOC_INT) { + + /* + * Disable this interrupt. + */ + mask &= ~IM_ALLOC_INT; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + snresume(&sc->arpcom.ac_if); + } + /* + * TX Completion. Handle a transmit error message. This will only be + * called when there is an error, because of the AUTO_RELEASE mode. + */ + if (status & IM_TX_INT) { + + /* + * Acknowlege Interrupt + */ + SMC_SELECT_BANK(2); + outb(BASE + INTR_ACK_REG_B, IM_TX_INT); + + packet_no = inw(BASE + FIFO_PORTS_REG_W); + packet_no &= FIFO_TX_MASK; + + /* + * select this as the packet to read from + */ + outb(BASE + PACKET_NUM_REG_B, packet_no); + + /* + * Position the pointer to the first word from this packet + */ + outw(BASE + POINTER_REG_W, PTR_AUTOINC | PTR_READ | 0x0000); + + /* + * Fetch the TX status word. The value found here will be a + * copy of the EPH_STATUS_REG_W at the time the transmit + * failed. + */ + tx_status = inw(BASE + DATA_REG_W); + + if (tx_status & EPHSR_TX_SUC) { + printf("sn%d: Successful packet caused interrupt\n", unit); + } else { + ++sc->arpcom.ac_if.if_oerrors; + } + + if (tx_status & EPHSR_LATCOL) + ++sc->arpcom.ac_if.if_collisions; + + /* + * Some of these errors will have disabled transmit. + * Re-enable transmit now. + */ + SMC_SELECT_BANK(0); + +#ifdef SW_PAD + outw(BASE + TXMIT_CONTROL_REG_W, TCR_ENABLE); +#else + outw(BASE + TXMIT_CONTROL_REG_W, TCR_ENABLE | TCR_PAD_ENABLE); +#endif /* SW_PAD */ + + /* + * kill the failed packet. Wait for the MMU to be un-busy. + */ + SMC_SELECT_BANK(2); + while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ + ; + outw(BASE + MMU_CMD_REG_W, MMUCR_FREEPKT); + + /* + * Attempt to queue more transmits. + */ + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + snstart(&sc->arpcom.ac_if); + } + /* + * Transmit underrun. We use this opportunity to update transmit + * statistics from the card. + */ + if (status & IM_TX_EMPTY_INT) { + + /* + * Acknowlege Interrupt + */ + SMC_SELECT_BANK(2); + outb(BASE + INTR_ACK_REG_B, IM_TX_EMPTY_INT); + + /* + * Disable this interrupt. + */ + mask &= ~IM_TX_EMPTY_INT; + + SMC_SELECT_BANK(0); + card_stats = inw(BASE + COUNTER_REG_W); + + /* + * Single collisions + */ + sc->arpcom.ac_if.if_collisions += card_stats & ECR_COLN_MASK; + + /* + * Multiple collisions + */ + sc->arpcom.ac_if.if_collisions += (card_stats & ECR_MCOLN_MASK) >> 4; + + SMC_SELECT_BANK(2); + + /* + * Attempt to enqueue some more stuff. + */ + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + snstart(&sc->arpcom.ac_if); + } + /* + * Some other error. Try to fix it by resetting the adapter. + */ + if (status & IM_EPH_INT) { + snstop(unit); + sninit(unit); + } + +out: + /* + * Handled all interrupt sources. + */ + + SMC_SELECT_BANK(2); + + /* + * Reestablish interrupts from mask which have not been deselected + * during this interrupt. Note that the hardware mask, which was set + * to 0x00 at the start of this service routine, may have been + * updated by one or more of the interrupt handers and we must let + * those new interrupts stay enabled here. + */ + mask |= inb(BASE + INTR_MASK_REG_B); + outb(BASE + INTR_MASK_REG_B, mask); + sc->intr_mask = mask; + + splx(x); +} + +void +snread(register struct ifnet *ifp) +{ + struct sn_softc *sc = &sn_softc[ifp->if_unit]; + struct ether_header *eh; + struct mbuf *m; + short status; + int packet_number; + u_short packet_length; + u_char *data; + + SMC_SELECT_BANK(2); +#if 0 + packet_number = inw(BASE + FIFO_PORTS_REG_W); + + if (packet_number & FIFO_REMPTY) { + + /* + * we got called , but nothing was on the FIFO + */ + printf("sn: Receive interrupt with nothing on FIFO\n"); + return; + } +#endif +read_another: + + /* + * Start reading from the start of the packet. Since PTR_RCV is set, + * packet number is found in FIFO_PORTS_REG_W, FIFO_RX_MASK. + */ + outw(BASE + POINTER_REG_W, PTR_READ | PTR_RCV | PTR_AUTOINC | 0x0000); + + /* + * First two words are status and packet_length + */ + status = inw(BASE + DATA_REG_W); + packet_length = inw(BASE + DATA_REG_W) & RLEN_MASK; + + /* + * The packet length contains 3 extra words: status, length, and a + * extra word with the control byte. + */ + packet_length -= 6; + + /* + * Account for receive errors and discard. + */ + if (status & RS_ERRORS) { + ++sc->arpcom.ac_if.if_ierrors; + goto out; + } + /* + * A packet is received. + */ + + /* + * Adjust for odd-length packet. + */ + if (status & RS_ODDFRAME) + packet_length++; + + /* + * Allocate a header mbuf from the kernel. + */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + goto out; + + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + m->m_pkthdr.len = m->m_len = packet_length; + + /* + * Attach an mbuf cluster + */ + MCLGET(m, M_DONTWAIT); + + /* + * Insist on getting a cluster + */ + if ((m->m_flags & M_EXT) == 0) { + m_freem(m); + ++sc->arpcom.ac_if.if_ierrors; + printf("sn: snread() kernel memory allocation problem\n"); + goto out; + } + eh = mtod(m, struct ether_header *); + + /* + * Get packet, including link layer address, from interface. + */ + + data = (u_char *) eh; + insw(BASE + DATA_REG_W, data, packet_length >> 1); + if (packet_length & 1) { + data += packet_length & ~1; + *data = inb(BASE + DATA_REG_B); + } + ++sc->arpcom.ac_if.if_ipackets; + +#if NBPFILTER > 0 + if (sc->arpcom.ac_if.if_bpf) + { + bpf_mtap(&sc->arpcom.ac_if, m); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no BPF listeners. And if we are in promiscuous + * mode, we have to check if this packet is really ours. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && + (eh->ether_dhost[0] & 1) == 0 && + bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 && + bcmp(eh->ether_dhost, etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) { + m_freem(m); + goto out; + } + } +#endif + + /* + * Remove link layer addresses and whatnot. + */ + m->m_pkthdr.len = m->m_len = packet_length - sizeof(struct ether_header); + m->m_data += sizeof(struct ether_header); + + ether_input(&sc->arpcom.ac_if, eh, m); + +out: + + /* + * Error or good, tell the card to get rid of this packet Wait for + * the MMU to be un-busy. + */ + SMC_SELECT_BANK(2); + while (inw(BASE + MMU_CMD_REG_W) & MMUCR_BUSY) /* NOTHING */ + ; + outw(BASE + MMU_CMD_REG_W, MMUCR_RELEASE); + + /* + * Check whether another packet is ready + */ + packet_number = inw(BASE + FIFO_PORTS_REG_W); + if (packet_number & FIFO_REMPTY) { + return; + } + goto read_another; +} + + +/* + * Handle IOCTLS. This function is completely stolen from if_ep.c + * As with its progenitor, it does not handle hardware address + * changes. + */ +static int +snioctl(register struct ifnet *ifp, int cmd, caddr_t data) +{ + register struct ifaddr *ifa = (struct ifaddr *) data; + struct sn_softc *sc = &sn_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; + +#if NCARD > 0 + if (sc->gone) { + ifp->if_flags &= ~IFF_RUNNING; + return ENXIO; + } +#endif + + s = splimp(); + + switch (cmd) { + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + sninit(ifp->if_unit); /* before arpwhohas */ + arp_ifinit((struct arpcom *) ifp, ifa); + break; +#endif +#ifdef IPX + case AF_IPX: + { + register struct ipx_addr *ina = &IA_SIPX(ifa)->sipx_addr; + + if (ipx_nullhost(*ina)) + ina->x_host = + *(union ipx_host *) (sc->sc_arpcom.ac_enaddr); + else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t) sc->sc_arpcom.ac_enaddr, + sizeof(sc->sc_arpcom.ac_enaddr)); + } + /* Set new address. */ + sninit(sc); + break; + } +#endif +#ifdef NS + case AF_NS: + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *) (sc->arpcom.ac_enaddr); + sn else { + ifp->if_flags &= ~IFF_RUNNING; + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t) sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + sninit(ifp->if_unit); + break; + } +#endif + default: + sninit(ifp->if_unit); + break; + } + break; + case SIOCGIFADDR: + { + struct sockaddr *sa; + + sa = (struct sockaddr *) & ifr->ifr_data; + bcopy((caddr_t) sc->arpcom.ac_enaddr, + (caddr_t) sa->sa_data, ETHER_ADDR_LEN); + } + break; + case SIOCSIFFLAGS: + if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { + ifp->if_flags &= ~IFF_RUNNING; + snstop(ifp->if_unit); + break; + } else { + /* reinitialize card on any parameter change */ + sninit(ifp->if_unit); + break; + } + break; + +#ifdef notdef + case SIOCGHWADDR: + bcopy((caddr_t) sc->sc_addr, (caddr_t) & ifr->ifr_data, + sizeof(sc->sc_addr)); + break; +#endif + + case SIOCSIFMTU: + + /* + * Set the interface MTU. + */ + if (ifr->ifr_mtu > ETHERMTU) { + error = EINVAL; + } else { + ifp->if_mtu = ifr->ifr_mtu; + } + break; + case SIOCADDMULTI: + error = ether_addmulti(ifr, &sc->arpcom); + if (error == ENETRESET) { + /* update multicast filter list. */ + sn_setmcast(sc); + error = 0; + } + break; + case SIOCDELMULTI: + error = ether_delmulti(ifr, &sc->arpcom); + if (error == ENETRESET) { + /* update multicast filter list. */ + sn_setmcast(sc); + error = 0; + } + break; + default: + error = EINVAL; + } + + splx(s); + + return (error); +} + +void +snreset(int unit) +{ + int s; + struct sn_softc *sc = &sn_softc[unit]; + +#if NCARD > 0 + if (sc->gone) + return; +#endif + s = splimp(); + snstop(unit); + sninit(unit); + + splx(s); +} + +void +snwatchdog(struct ifnet *ifp) +{ + int s; + struct sn_softc *sc = &sn_softc[ifp->if_unit]; + +#if NCARD > 0 + if (sc->gone) + return; +#endif + s = splimp(); + snintr(ifp->if_unit); + splx(s); +} + + +/* 1. zero the interrupt mask + * 2. clear the enable receive flag + * 3. clear the enable xmit flags + */ +void +snstop(int unit) +{ + struct sn_softc *sc = &sn_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + +#if NCARD > 0 + if (sc->gone) + return; +#endif + /* + * Clear interrupt mask; disable all interrupts. + */ + SMC_SELECT_BANK(2); + outb(BASE + INTR_MASK_REG_B, 0x00); + + /* + * Disable transmitter and Receiver + */ + SMC_SELECT_BANK(0); + outw(BASE + RECV_CONTROL_REG_W, 0x0000); + outw(BASE + TXMIT_CONTROL_REG_W, 0x0000); + + /* + * Cancel watchdog. + */ + ifp->if_timer = 0; +} + + + +/* + * Function: smc_probe( int ioaddr, int pccard ) + * + * Purpose: + * Tests to see if a given ioaddr points to an SMC9xxx chip. + * Tries to cause as little damage as possible if it's not a SMC chip. + * Returns a 0 on success + * + * Algorithm: + * (1) see if the high byte of BANK_SELECT is 0x33 + * (2) compare the ioaddr with the base register's address + * (3) see if I recognize the chip ID in the appropriate register + * + * + */ +static int +smc_probe(int ioaddr, int pccard) +{ + u_int bank; + u_short revision_register; + u_short base_address_register; + + /* + * First, see if the high byte is 0x33 + */ + bank = inw(ioaddr + BANK_SELECT_REG_W); + if ((bank & BSR_DETECT_MASK) != BSR_DETECT_VALUE) { +#ifdef SN_DEBUG +printf("test1 failed\n"); +#endif + return -ENODEV; + } + /* + * The above MIGHT indicate a device, but I need to write to further + * test this. Go to bank 0, then test that the register still + * reports the high byte is 0x33. + */ + outw(ioaddr + BANK_SELECT_REG_W, 0x0000); + bank = inw(ioaddr + BANK_SELECT_REG_W); + if ((bank & BSR_DETECT_MASK) != BSR_DETECT_VALUE) { +#ifdef SN_DEBUG +printf("test2 failed\n"); +#endif + return -ENODEV; + } + /* + * well, we've already written once, so hopefully another time won't + * hurt. This time, I need to switch the bank register to bank 1, so + * I can access the base address register. The contents of the + * BASE_ADDR_REG_W register, after some jiggery pokery, is expected + * to match the I/O port address where the adapter is being probed. + */ + outw(ioaddr + BANK_SELECT_REG_W, 0x0001); + base_address_register = inw(ioaddr + BASE_ADDR_REG_W); + + /* + * This test is nonsence on PC-card architecture, so if + * pccard == 1, skip this test. (hosokawa) + */ + if (!pccard && (ioaddr != (base_address_register >> 3 & 0x3E0))) { + + /* + * Well, the base address register didn't match. Must not + * have been a SMC chip after all. + */ + /* + * printf("sn: ioaddr %x doesn't match card configuration + * (%x)\n", ioaddr, base_address_register >> 3 & 0x3E0 ); + */ + +#ifdef SN_DEBUG +printf("test3 failed ioaddr = 0x%x, base_address_register = 0x%x\n", + ioaddr, base_address_register >> 3 & 0x3E0); +#endif + return -ENODEV; + } + /* + * Check if the revision register is something that I recognize. + * These might need to be added to later, as future revisions could + * be added. + */ + outw(ioaddr + BANK_SELECT_REG_W, 0x3); + revision_register = inw(ioaddr + REVISION_REG_W); + if (!chip_ids[(revision_register >> 4) & 0xF]) { + + /* + * I don't regonize this chip, so... + */ + /* + * printf("sn: ioaddr %x unrecognized revision register: + * %x\n", ioaddr, revision_register ); + */ + +#ifdef SN_DEBUG +printf("test4 failed\n"); +#endif + return -ENODEV; + } + /* + * at this point I'll assume that the chip is an SMC9xxx. It might be + * prudent to check a listing of MAC addresses against the hardware + * address, or do some other tests. + */ + return 0; +} + +#define MCFSZ 8 + +static void +sn_setmcast(struct sn_softc *sc) +{ + struct ifnet *ifp = (struct ifnet *)sc; + int flags; + /* + * Set the receiver filter. We want receive enabled and auto strip + * of CRC from received packet. If we are promiscuous then set that + * bit too. + */ + flags = RCR_ENABLE | RCR_STRIP_CRC; + + if (ifp->if_flags & IFF_PROMISC) { + flags |= RCR_PROMISC | RCR_ALMUL; + } else if (ifp->if_flags & IFF_ALLMULTI) { + flags |= RCR_ALMUL; + } else { + u_char mcf[MCFSZ]; + if (sn_getmcf(&sc->arpcom, mcf)) { + /* set filter */ + SMC_SELECT_BANK(3); + outw(BASE + MULTICAST1_REG_W, + ((u_short)mcf[1] << 8) | mcf[0]); + outw(BASE + MULTICAST2_REG_W, + ((u_short)mcf[3] << 8) | mcf[2]); + outw(BASE + MULTICAST3_REG_W, + ((u_short)mcf[5] << 8) | mcf[4]); + outw(BASE + MULTICAST4_REG_W, + ((u_short)mcf[7] << 8) | mcf[6]); + } else { + flags |= RCR_ALMUL; + } + } + SMC_SELECT_BANK(0); + outw(BASE + RECV_CONTROL_REG_W, flags); +} + +static int +sn_getmcf(struct arpcom *ac, u_char *mcf) +{ + int i; + struct ether_multi *enm; + struct ether_multistep step; + u_int a, b; + + bzero(mcf, MCFSZ); + ETHER_FIRST_MULTI(step, ac, enm); + while (enm) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { + /* impossible to hash */ + bzero(mcf, MCFSZ); + return 0; + } + a = smc_crc(enm->enm_addrlo) & 0x3f; + b = 0; + for (i=0; i < 6; i++) { + b <<= 1; + b |= (a & 0x01); + a >>= 1; + } + mcf[b >> 3] |= 1 << (b & 7); + ETHER_NEXT_MULTI(step, enm); + } + return 1; /* use multicast filter */ +} + +static u_int +smc_crc(u_char *s) +{ + int perByte; + int perBit; + const u_int poly = 0xedb88320; + u_int v = 0xffffffff; + u_char c; + + for (perByte = 0; perByte < ETHER_ADDR_LEN; perByte++) { + c = s[perByte]; + for (perBit = 0; perBit < 8; perBit++) { + v = (v >> 1)^(((v ^ c) & 0x01) ? poly : 0); + c >>= 1; + } + } + return v; +} +#endif diff -urN sys.orig/i386/isa/if_snreg.h sys/i386/isa/if_snreg.h --- sys.orig/i386/isa/if_snreg.h Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/if_snreg.h Tue Apr 14 08:45:02 1998 @@ -0,0 +1,445 @@ +/* + * Copyright (c) 1996 Gardner Buchanan + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Gardner Buchanan. + * 4. The name of Gardner Buchanan may not be used to endorse or promote + * products derived from this software without specific prior written + * permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * $Id: if_snreg.h,v 1.1.2.1 1997/12/11 14:00:15 itojun Exp $ + */ + +/* + * This file contains register information and access macros for + * the SMC91xxx chipset. + * + * Information contained in this file was obtained from the SMC91C92 + * and SMC91C94 manuals from SMC. You will need one of these in order + * to make any meaningful changes to this driver. Information about + * obtaining one can be found at http://www.smc.com in the components + * division. + * + * This FreeBSD driver is derived in part from the smc9194 Linux driver + * by Erik Stahlman and is Copyright (C) 1996 by Erik Stahlman. + * It is also derived in part from the FreeBSD ep (3C509) driver which + * is Copyright (c) 1993 Herb Peyerl (hpeyerl@novatel.ca) All rights + * reserved. + * + */ +#ifndef _IF_SNREG_H_ +#define _IF_SNREG_H_ + + +/* + * Ethernet software status per interface. The first element MUST + * be the arpcom struct since the address of the arpcom struct is + * used as a backdoor to obtain the address of this whole structure + * in many cases. + */ +struct sn_softc { + struct arpcom arpcom; /* Ethernet common part */ + short sn_io_addr; /* i/o bus address (BASE) */ + int pages_wanted; /* Size of outstanding MMU ALLOC */ + int intr_mask; /* Most recently set interrupt mask */ +#if NCARD > 0 + int gone; +#endif /* NCARD > 0 */ +}; + + +/* + * Wait time for memory to be free. This probably shouldn't be + * tuned that much, as waiting for this means nothing else happens + * in the system + */ +#define MEMORY_WAIT_TIME 1000 + + +/* The SMC91xxx uses 16 I/O ports + */ +#define SMC_IO_EXTENT 16 + + +/* + * A description of the SMC registers is probably in order here, + * although for details, the SMC datasheet is invaluable. + * The data sheet I (GB) am using is "SMC91C92 Single Chip Ethernet + * Controller With RAM", Rev. 12/0/94. Constant definitions I give + * here are loosely based on the mnemonic names given to them in the + * data sheet, but there are many exceptions. + * + * Basically, the chip has 4 banks of registers (0 to 3), which + * are accessed by writing a number into the BANK_SELECT register + * (I also use a SMC_SELECT_BANK macro for this). Registers are + * either Byte or Word sized. My constant definitions end in _B + * or _W as appropriate. + * + * The banks are arranged so that for most purposes, bank 2 is all + * that is needed for normal run time tasks. + */ + +/* + * Bank Select Register. This also doubles as + * a chip identification register. This register + * is mapped at the same position in all banks. + */ +#define BANK_SELECT_REG_W 0x0e +#define BSR_DETECT_MASK 0xff00 +#define BSR_DETECT_VALUE 0x3300 + + +/* BANK 0 + */ + +/* Transmit Control Register controls some aspects of the transmit + * behavior of the Ethernet Protocol Handler. + */ +#define TXMIT_CONTROL_REG_W 0x00 + +#define TCR_ENABLE 0x0001 /* if this is 1, we can transmit */ +#define TCR_LOOP 0x0002 /* Enable internal analogue loopback */ +#define TCR_FORCOL 0x0004 /* Force Collision on next TX */ +#define TCR_PAD_ENABLE 0x0080 /* Pad short packets to 64 bytes */ +#define TCR_NOCRC 0x0100 /* Do not append CRC */ +#define TCR_MON_CSN 0x0400 /* monitors the carrier status */ +#define TCR_FDUPLX 0x0800 /* receive packets sent out */ +#define TCR_STP_SQET 0x1000 /* stop transmitting if Signal quality error */ +#define TCR_EPH_LOOP 0x2000 /* Enable internal digital loopback */ + + +/* Status of the last transmitted frame and instantaneous status of + * the Ethernet Protocol Handler jumbled together. In auto-release + * mode this information is simply discarded after each TX. This info + * is copied to the status word of in-memory packets after transmit + * where relevent statuses can be checked. + */ +#define EPH_STATUS_REG_W 0x02 + +#define EPHSR_TX_SUC 0x0001 /* Transmit was successful */ +#define EPHSR_SNGLCOL 0x0002 /* Single collision occurred */ +#define EPHSR_MULCOL 0x0004 /* Multiple Collisions occurred */ +#define EPHSR_LTX_MULT 0x0008 /* Transmit was a multicast */ +#define EPHSR_16COL 0x0010 /* 16 Collisions occurred, TX disabled */ +#define EPHSR_SQET 0x0020 /* SQE Test failed, TX disabled */ +#define EPHSR_LTX_BRD 0x0040 /* Transmit was a broadcast */ +#define EPHSR_DEFR 0x0080 /* TX deferred due to carrier det. */ +#define EPHSR_LATCOL 0x0200 /* Late collision detected, TX disabled */ +#define EPHSR_LOST_CAR 0x0400 /* Lost carrier sense, TX disabled */ +#define EPHSR_EXC_DEF 0x0800 /* Excessive deferrals in TX >2 MAXETHER + * times */ +#define EPHSR_CTR_ROL 0x1000 /* Some ECR Counter(s) rolled over */ +#define EPHSR_RX_OVRN 0x2000 /* Receiver overrun, packets dropped */ +#define EPHSR_LINK_OK 0x4000 /* Link integrity is OK */ +#define EPHSR_TXUNRN 0x8000 /* Transmit underrun */ + + +/* Receiver Control Register controls some aspects of the receive + * behavior of the Ethernet Protocol Handler. + */ +#define RECV_CONTROL_REG_W 0x04 + +#define RCR_RX_ABORT 0x0001 /* Received huge packet */ +#define RCR_PROMISC 0x0002 /* enable promiscuous mode */ +#define RCR_ALMUL 0x0004 /* receive all multicast packets */ +#define RCR_ENABLE 0x0100 /* IFF this is set, we can recieve packets */ +#define RCR_STRIP_CRC 0x0200 /* strips CRC */ +#define RCR_GAIN_BITS 0x0c00 /* PLL Gain control (for testing) */ +#define RCR_FILT_CAR 0x4000 /* Enable 12 bit carrier filter */ +#define RCR_SOFTRESET 0x8000 /* Resets the EPH logic */ + + +/* TX Statistics counters + */ +#define COUNTER_REG_W 0x06 + +#define ECR_COLN_MASK 0x000f /* Vanilla collisions */ +#define ECR_MCOLN_MASK 0x00f0 /* Multiple collisions */ +#define ECR_DTX_MASK 0x0f00 /* Deferred transmits */ +#define ECR_EXDTX_MASK 0xf000 /* Excessively deferred transmits */ + +/* Memory Information + */ +#define MEM_INFO_REG_W 0x08 + +#define MIR_FREE_MASK 0xff00 /* Free memory pages available */ +#define MIR_TOTAL_MASK 0x00ff /* Total memory pages available */ + +/* Memory Configuration + */ +#define MEM_CFG_REG_W 0x0a + +#define MCR_TXRSV_MASK 0x001f /* Count of pages reserved for transmit */ + + +/* Bank 0, Register 0x0c is unised in the SMC91C92 + */ + + +/* BANK 1 + */ + +/* Adapter configuration + */ +#define CONFIG_REG_W 0x00 + +#define CR_INT_SEL0 0x0002 /* Interrupt selector */ +#define CR_INT_SEL1 0x0004 /* Interrupt selector */ +#define CR_DIS_LINK 0x0040 /* Disable 10BaseT Link Test */ +#define CR_16BIT 0x0080 /* Bus width */ +#define CR_AUI_SELECT 0x0100 /* Use external (AUI) Transceiver */ +#define CR_SET_SQLCH 0x0200 /* Squelch level */ +#define CR_FULL_STEP 0x0400 /* AUI signalling mode */ +#define CR_NOW_WAIT_ST 0x1000 /* Disable bus wait states */ + +/* The contents of this port are used by the adapter + * to decode its I/O address. We use it as a varification + * that the adapter is detected properly when probing. + */ +#define BASE_ADDR_REG_W 0x02 /* The select IO Base addr. */ + +/* These registers hold the Ethernet MAC address. + */ +#define IAR_ADDR0_REG_W 0x04 /* My Ethernet address */ +#define IAR_ADDR1_REG_W 0x06 /* My Ethernet address */ +#define IAR_ADDR2_REG_W 0x08 /* My Ethernet address */ + +/* General purpose register used for talking to the EEPROM. + */ +#define GENERAL_REG_W 0x0a + +/* Control register used for talking to the EEPROM and + * setting some EPH functions. + */ +#define CONTROL_REG_W 0x0c +#define CTR_STORE 0x0001 /* Store something to EEPROM */ +#define CTR_RELOAD 0x0002 /* Read EEPROM into registers */ +#define CTR_EEPROM_SEL 0x0004 /* Select registers for Reload/Store */ +#define CTR_TE_ENABLE 0x0020 /* Enable TX Error detection via EPH_INT */ +#define CTR_CR_ENABLE 0x0040 /* Enable Counter Rollover via EPH_INT */ +#define CTR_LE_ENABLE 0x0080 /* Enable Link Error detection via EPH_INT */ +#define CTR_AUTO_RELEASE 0x0800 /* Enable auto release mode for TX */ +#define CTR_POWERDOWN 0x2000 /* Enter powerdown mode */ +#define CTR_RCV_BAD 0x4000 /* Enable receipt of frames with bad CRC */ + + +/* BANK 2 + */ + +/* Memory Management Unit Control Register + * Controls allocation of memory to receive and + * transmit functions. + */ +#define MMU_CMD_REG_W 0x00 +#define MMUCR_BUSY 0x0001 /* MMU busy performing a release */ + +/* MMU Commands: + */ +#define MMUCR_NOP 0x0000 /* Do nothing */ +#define MMUCR_ALLOC 0x0020 /* Or with number of 256 byte packets - 1 */ +#define MMUCR_RESET 0x0040 /* Reset MMU State */ +#define MMUCR_REMOVE 0x0060 /* Dequeue (but not free) current RX packet */ +#define MMUCR_RELEASE 0x0080 /* Dequeue and free the current RX packet */ +#define MMUCR_FREEPKT 0x00a0 /* Release packet in PNR register */ +#define MMUCR_ENQUEUE 0x00c0 /* Enqueue the packet for transmit */ +#define MMUCR_RESETTX 0x00e0 /* Reset transmit queues */ + +/* Packet Number at TX Area + */ +#define PACKET_NUM_REG_B 0x02 + +/* Packet number resulting from MMUCR_ALLOC + */ +#define ALLOC_RESULT_REG_B 0x03 +#define ARR_FAILED 0x80 + +/* Transmit and receive queue heads + */ +#define FIFO_PORTS_REG_W 0x04 +#define FIFO_REMPTY 0x8000 +#define FIFO_TEMPTY 0x0080 +#define FIFO_RX_MASK 0x7f00 +#define FIFO_TX_MASK 0x007f + +/* The address within the packet for reading/writing. The + * PTR_RCV bit is tricky. When PTR_RCV==1, the packet number + * to be read is found in the FIFO_PORTS_REG_W, FIFO_RX_MASK. + * When PTR_RCV==0, the packet number to be written is found + * in the PACKET_NUM_REG_B. + */ +#define POINTER_REG_W 0x06 +#define PTR_READ 0x2000 /* Intended access mode */ +#define PTR_AUTOINC 0x4000 /* Do auto inc after read/write */ +#define PTR_RCV 0x8000 /* FIFO_RX is packet, otherwise PNR is packet */ +/* Data I/O register to be used in conjunction with + * The pointer register to read and write data from the + * card. The same register can be used for byte and word + * ops. + */ +#define DATA_REG_W 0x08 +#define DATA_REG_B 0x08 +#define DATA_1_REG_B 0x08 +#define DATA_2_REG_B 0x0a + +/* Sense interrupt status (READ) + */ +#define INTR_STAT_REG_B 0x0c + +/* Acknowledge interrupt sources (WRITE) + */ +#define INTR_ACK_REG_B 0x0c + +/* Interrupt mask. Bit set indicates interrupt allowed. + */ +#define INTR_MASK_REG_B 0x0d + +/* Interrupts + */ +#define IM_RCV_INT 0x01 /* A packet has been received */ +#define IM_TX_INT 0x02 /* Packet TX complete */ +#define IM_TX_EMPTY_INT 0x04 /* No packets left to TX */ +#define IM_ALLOC_INT 0x08 /* Memory allocation completed */ +#define IM_RX_OVRN_INT 0x10 /* Receiver was overrun */ +#define IM_EPH_INT 0x20 /* Misc. EPH conditions (see CONTROL_REG_W) */ +#define IM_ERCV_INT 0x40 /* not on SMC9192 */ + +/* BANK 3 + */ + +/* Multicast subscriptions. + * The multicast handling in the SMC90Cxx is quite complicated. A table + * of multicast address subscriptions is provided and a clever way of + * speeding the search of that table by hashing is implemented in the + * hardware. I have ignored this and simply subscribed to all multicasts + * and let the kernel deal with the results. + */ +#define MULTICAST1_REG_W 0x00 +#define MULTICAST2_REG_W 0x02 +#define MULTICAST3_REG_W 0x04 +#define MULTICAST4_REG_W 0x06 + +/* These registers do not exist on SMC9192, or at least + * are not documented in the SMC91C92 data sheet. + * The REVISION_REG_W register does however seem to work. + */ +#define MGMT_REG_W 0x08 +#define REVISION_REG_W 0x0a /* (hi: chip id low: rev #) */ +#define ERCV_REG_W 0x0c + +/* These are constants expected to be found in the + * chip id register. + */ +#define CHIP_9190 3 +#define CHIP_9194 4 +#define CHIP_9195 5 +#define CHIP_91100 7 + +static const char *chip_ids[15] = { + NULL, NULL, NULL, + /* 3 */ "SMC91C90/91C92", + /* 4 */ "SMC91C94", + /* 5 */ "SMC91C95", + NULL, + /* 7 */ "SMC91C100", + NULL, NULL, NULL, NULL, + NULL, NULL, NULL +}; + + +/* When packets are stuffed into the card or sucked out of the card + * they are set up more or less as follows: + * + * Addr msbyte lsbyte + * 00 SSSSSSSS SSSSSSSS - STATUS-WORD 16 bit TX or RX status + * 02 RRRRR - RESERVED (unused) + * 02 CCC CCCCCCCC - BYTE COUNT (RX: always even, TX: bit 0 ignored) + * 04 DDDDDDDD DDDDDDDD - DESTINATION ADDRESS + * 06 DDDDDDDD DDDDDDDD (48 bit Ethernet MAC Address) + * 08 DDDDDDDD DDDDDDDD + * 0A SSSSSSSS SSSSSSSS - SOURCE ADDRESS + * 0C SSSSSSSS SSSSSSSS (48 bit Ethernet MAC Address) + * 0E SSSSSSSS SSSSSSSS + * 10 PPPPPPPP PPPPPPPP + * .. PPPPPPPP PPPPPPPP + * C-2 CCCCCCCC - CONTROL BYTE + * C-2 PPPPPPPP - Last data byte (If odd length) + * + * The STATUS_WORD is derived from the EPH_STATUS_REG_W register + * during transmit and is composed of another set of bits described + * below during receive. + */ + + +/* Receive status bits. These values are found in the status word + * field of a received packet. For receive packets I use the RS_ODDFRAME + * to detect whether a frame has an extra byte on it. The CTLB_ODD + * bit of the control byte tells the same thing. + */ +#define RS_MULTICAST 0x0001 /* Packet is multicast */ +#define RS_HASH_MASK 0x007e /* Mask of multicast hash value */ +#define RS_TOOSHORT 0x0400 /* Frame was a runt, <64 bytes */ +#define RS_TOOLONG 0x0800 /* Frame was giant, >1518 */ +#define RS_ODDFRAME 0x1000 /* Frame is odd lengthed */ +#define RS_BADCRC 0x2000 /* Frame had CRC error */ +#define RS_ALGNERR 0x8000 /* Frame had alignment error */ +#define RS_ERRORS (RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) + +#define RLEN_MASK 0x07ff /* Significant length bits in RX length */ + +/* The control byte has the following significant bits. + * For transmit, the CTLB_ODD bit specifies whether an extra byte + * is present in the frame. Bit 0 of the byte count field is + * ignored. I just pad every frame to even length and forget about + * it. + */ +#define CTLB_CRC 0x10 /* Add CRC for this packet (TX only) */ +#define CTLB_ODD 0x20 /* The packet length is ODD */ + + +/* + * I define some macros to make it easier to do somewhat common + * or slightly complicated, repeated tasks. + */ + +/* The base I/O address. + */ +#define BASE (sc->sn_io_addr) + +/* Select a register bank, 0 to 3 + */ +#define SMC_SELECT_BANK(x) { outw( BASE + BANK_SELECT_REG_W, (x) ); } + +/* Define a small delay for the reset + */ +#define SMC_DELAY() { inw( BASE + RECV_CONTROL_REG_W );\ + inw( BASE + RECV_CONTROL_REG_W );\ + inw( BASE + RECV_CONTROL_REG_W ); } + +/* Define flags + */ + +#define SN_FLAGS_PCCARD 0x0001 /* PCMCIA (PC-card) */ +#define SN_FLAGS_XJBT10 0x0002 /* Megahertz XJ-BT10 (PCMCIA) */ + +#endif /* _IF_SNREG_H_ */ diff -urN sys.orig/i386/isa/if_ze.c sys/i386/isa/if_ze.c --- sys.orig/i386/isa/if_ze.c Thu Oct 30 09:38:20 1997 +++ sys/i386/isa/if_ze.c Tue Apr 14 08:45:04 1998 @@ -39,7 +39,7 @@ /* * I doubled delay loops in this file because it is not enough for some * laptop machines' PCIC (especially, on my Chaplet ILFA 350 ^^;). - * HOSOKAWA, Tatsumi + * HOSOKAWA, Tatsumi */ /* * Very small patch for IBM Ethernet PCMCIA Card II and IBM ThinkPad230Cs. diff -urN sys.orig/i386/isa/if_zp.c sys/i386/isa/if_zp.c --- sys.orig/i386/isa/if_zp.c Thu Oct 30 09:38:21 1997 +++ sys/i386/isa/if_zp.c Tue Apr 14 08:45:05 1998 @@ -7,8 +7,8 @@ * (4) RT-Mach implementation on PCMCIA/ISA/EISA Etherlink III * by Seiji Murata * - * Copyright (c) by HOSOKAWA, Tatsumi - * Copyright (c) by Seiji Murata + * Copyright (c) by HOSOKAWA, Tatsumi + * Copyright (c) by Seiji Murata */ /* * Copyright (c) 1993 Herb Peyerl @@ -93,7 +93,7 @@ /* * I doubled delay loops in this file because it is not enough for some * laptop machines' PCIC (especially, on my Chaplet ILFA 350 ^^;). - * HOSOKAWA, Tatsumi + * HOSOKAWA, Tatsumi */ /* * Very small patch for IBM Ethernet PCMCIA Card II and IBM ThinkPad230Cs. diff -urN sys.orig/i386/isa/isa.c sys/i386/isa/isa.c --- sys.orig/i386/isa/isa.c Thu Jan 8 21:32:31 1998 +++ sys/i386/isa/isa.c Wed Apr 15 12:56:25 1998 @@ -54,8 +54,11 @@ #include #include #include +#include +#include #include #include +#include #include #include #include @@ -86,7 +89,9 @@ u_int intr_mask[ICU_LEN]; u_int* intr_mptr[ICU_LEN]; int intr_unit[ICU_LEN]; +static u_int intr_inuse; +SYSCTL_INT(_machdep, CPU_INTRINUSE, intr_inuse, CTLFLAG_RD, &intr_inuse, 0, ""); static inthand_t *fastintr[ICU_LEN] = { &IDTVEC(fastintr0), &IDTVEC(fastintr1), &IDTVEC(fastintr2), &IDTVEC(fastintr3), @@ -482,6 +487,11 @@ /* icu vectors */ for (i = 0; i < ICU_LEN; i++) unregister_intr(i, (inthand2_t *)NULL); + intr_inuse = 0; + /* XXX hardcoded kludge! - should be automated */ + intr_inuse |= (1 << 2); /* master/slave connection IRQ*/ + intr_inuse |= (1 << 8); /* RTC */ + intr_inuse |= (1 << 13); /* FPU */ /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ @@ -947,7 +957,6 @@ if (eisa_port & ENMI_IOSTATUS) panic("EISA I/O port status error."); - printf("\nNMI ISA %x, EISA %x\n", isa_port, eisa_port); return(0); } @@ -1097,6 +1106,7 @@ intr_mptr[intr] = maskptr; intr_mask[intr] = mask | (1 << intr); intr_unit[intr] = unit; + intr_inuse |= (1 << intr); setidt(ICU_OFFSET + intr, flags & RI_FAST ? fastintr[intr] : slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); @@ -1146,8 +1156,119 @@ intr_mptr[intr] = NULL; intr_mask[intr] = HWI_MASK | SWI_MASK; intr_unit[intr] = intr; + intr_inuse &= ~(1 << intr); setidt(ICU_OFFSET + intr, slowintr[intr], SDT_SYS386IGT, SEL_KPL, GSEL(GCODE_SEL, SEL_KPL)); write_eflags(ef); return (0); } + +static int +sysctl_machdep_checkio SYSCTL_HANDLER_ARGS +{ + int *name = (int *)arg1; + u_int namelen = arg2; + int iobase, iorange; + struct isa_device *tmpdvp; + int status; + + if (namelen != 2) + return EINVAL; + iobase = name[0]; + iorange = name[1]; + +#define NOCONFLICT \ + (tmpdvp->id_alive == 0 || tmpdvp->id_alive == -1 \ + || iobase + iorange - 1 < tmpdvp->id_iobase \ + || tmpdvp->id_iobase + tmpdvp->id_alive - 1 < iobase) + + status = 0; + for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } + for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } + for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } + for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } +#undef NOCONFLICT + +answer: + SYSCTL_OUT(req, &status, sizeof(status)); + return 0; +} + +SYSCTL_NODE(_machdep, CPU_CHECKIO, checkio, CTLFLAG_RD, + sysctl_machdep_checkio, ""); + + +static int +sysctl_machdep_checkmem SYSCTL_HANDLER_ARGS +{ + int *name = (int *)arg1; + u_int namelen = arg2; + caddr_t membase; + int memrange; + struct isa_device *tmpdvp; + int status; + + if (namelen != 2) + return EINVAL; + membase = (caddr_t)name[0]; + memrange = name[1]; + +#define NOCONFLICT \ + (tmpdvp->id_msize == 0 || tmpdvp->id_msize == -1 \ + || KERNBASE + membase + memrange - 1 < tmpdvp->id_maddr \ + || tmpdvp->id_maddr + tmpdvp->id_msize - 1 < KERNBASE + membase) + + status = 0; + for (tmpdvp = isa_devtab_tty; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } + for (tmpdvp = isa_devtab_bio; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } + for (tmpdvp = isa_devtab_net; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } + for (tmpdvp = isa_devtab_null; tmpdvp->id_driver; tmpdvp++) { + if (!NOCONFLICT) { + status = -1; + goto answer; + } + } +#undef NOCONFLICT + +answer: + SYSCTL_OUT(req, &status, sizeof(status)); + return 0; +} + +SYSCTL_NODE(_machdep, CPU_CHECKMEM, checkmem, CTLFLAG_RD, + sysctl_machdep_checkmem, ""); diff -urN sys.orig/i386/isa/joy.c sys/i386/isa/joy.c --- sys.orig/i386/isa/joy.c Thu Nov 6 22:00:13 1997 +++ sys/i386/isa/joy.c Tue Apr 14 08:47:54 1998 @@ -46,6 +46,19 @@ #include #include +#ifndef JOY_MODULE +#include "card.h" +#else +#define NCARD 0 +#endif + +#if NCARD > 0 +#include +#include +#include +#include +#endif + /* The game port can manage 4 buttons and 4 variable resistors (usually 2 * joysticks, each with 2 buttons and 2 pots.) via the port at address 0x201. * Getting the state of the buttons is done by reading the game port: @@ -97,6 +110,103 @@ static int get_tick __P((void)); +#if NCARD > 0 +/* + * PC-Card (PCMCIA) specific code. + */ +static int joy_card_intr(struct pccard_devinfo *); /* Interrupt handler */ +static void joy_card_unload(struct pccard_devinfo *); /* Disable driver */ +static void joy_card_suspend(struct pccard_devinfo *); /* Suspend driver */ +static int joy_card_init(struct pccard_devinfo *, int); /* init device */ + +static struct pccard_device joy_info = { + "joy", + joy_card_init, + joy_card_unload, + joy_card_intr, +/* joy_card_suspend, */ + 0, /* Attributes - presently unused */ + &bio_imask /* Interrupt mask for device */ + /* XXX - Should this also include net_imask? */ +}; + +DATA_SET(pccarddrv_set, joy_info); + +static int pccard_mode[NJOY]; +/* + * Called when a power down is requested. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * joyinit with first=0. This is called when the user suspends + * the system, or the APM code suspends the system. + */ +static void +joy_card_suspend(struct pccard_devinfo *devi) +{ + printf("joy%d: suspending\n", devi->isahd.id_unit); +} + +/* + * Initialize the device - called from Slot manager. + * If first is set, then check for the device's existence + * before initializing it. Once initialized, the device table may + * be set up. + */ +int +joy_card_init(struct pccard_devinfo *devi, int first) +{ + int unit = devi->isahd.id_unit; + /* validate unit number. */ + if (first) { + if (unit >= NJOY) + return(ENODEV); + pccard_mode[unit] = 1; + /* + * Probe the device. If a value is returned, the + * device was found at the location. + */ + if (joyprobe(&devi->isahd)==0) + return(ENXIO); + if (joyattach(&devi->isahd)==0) + return(ENXIO); + } + /* + * XXX TODO: + * If it was initialized before, the device structure + * should also be initialized. We should + * reset (and possibly restart) the hardware, but + * I am not sure of the best way to do this... + */ + return(0); +} + +/* + * joy_card_unload - unload the driver and clear the table. + * XXX TODO: + * This is usually called when the card is ejected, but + * can be caused by a modunload of a controller driver. + * The idea is to reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +static void +joy_card_unload(struct pccard_devinfo *devi) +{ + int unit = devi->isahd.id_unit; + + pccard_mode[unit] = 0; +} + +/* + * card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +joy_card_intr(struct pccard_devinfo *devi) +{ + return(1); +} +#endif /* NCARD > 0 */ static int joyprobe (struct isa_device *dev) diff -urN sys.orig/i386/isa/sio.c sys/i386/isa/sio.c --- sys.orig/i386/isa/sio.c Sun Mar 8 18:57:35 1998 +++ sys/i386/isa/sio.c Tue Apr 14 08:47:55 1998 @@ -31,11 +31,13 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.147.2.13 1998/03/08 09:57:35 jkh Exp $ + * $Id: sio.c,v 1.199 1998/02/27 06:39:32 msmith Exp $ */ #include "opt_comconsole.h" +/* #include "opt_compat.h" */ #include "opt_ddb.h" +#include "opt_devfs.h" #include "opt_sio.h" #include "sio.h" #include "pnp.h" @@ -62,16 +64,15 @@ #include #include #include -#include +#include #include #include #include #include -#include -#include +#include #include -#include #include +#include #ifdef DEVFS #include #endif @@ -81,6 +82,7 @@ #include #include #include +/* #include */ #ifdef COM_ESP #include @@ -90,7 +92,6 @@ #include "card.h" #if NCARD > 0 #include -#include #include #endif @@ -98,13 +99,24 @@ #include #endif +#ifdef SMP +#define disable_intr() COM_DISABLE_INTR() +#define enable_intr() COM_ENABLE_INTR() +#endif /* SMP */ + +#ifdef APIC_IO +/* + * INTs are masked in the (global) IO APIC, + * but the IRR register is in each LOCAL APIC, + * so we would have to unmask the INT to be able to "see INT pending". + * So instead we just look in the 8259 ICU. + */ +#define isa_irq_pending icu_irq_pending +#endif /* APIC_IO */ + #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) -#ifndef DPTOPT /* not sure why this is in dpt.. latency requirements? [JRE] */ #define RS_IBUFSIZE 256 -#else -#define RS_IBUFSIZE 512 -#endif /* DPTOPT */ #define CALLOUT_MASK 0x80 #define CONTROL_MASK 0x60 @@ -123,9 +135,20 @@ #define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) #endif /* COM_MULTIPORT */ +#define COM_CONSOLE(dev) ((dev)->id_flags & 0x10) +#define COM_FORCECONSOLE(dev) ((dev)->id_flags & 0x20) +#define COM_LLCONSOLE(dev) ((dev)->id_flags & 0x40) #define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) #define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) #define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) +#define COM_IRQBUG(dev) ((dev)->id_flags & 0x10) +#define COM_NOTST3(dev) ((dev)->id_flags & 0x10000) +#define COM_ST16650A(dev) ((dev)->id_flags & 0x20000) +#define COM_C_NOPROBE (0x40000) +#define COM_NOPROBE(dev) ((dev)->id_flags & COM_C_NOPROBE) +#define COM_C_IIR_TXRDYBUG (0x80000) +#define COM_IIR_TXRDYBUG(dev) ((dev)->id_flags & COM_C_IIR_TXRDYBUG) +#define COM_FIFOSIZE(dev) (((dev)->id_flags & 0xff000000) >> 24) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ @@ -193,6 +216,7 @@ /* com device structure */ struct com_s { + u_int id_flags; /* Copy isa device falgas */ u_char state; /* miscellaneous flag bits */ bool_t active_out; /* nonzero if the callout device is open */ u_char cfcr_image; /* copy of value written to CFCR */ @@ -202,6 +226,7 @@ u_char extra_state; /* more flag bits, separate for order trick */ u_char fifo_image; /* copy of value written to FIFO */ bool_t hasfifo; /* nonzero for 16550 UARTs */ + bool_t st16650a; /* Is a Startech 16650A or RTS/CTS compat */ bool_t loses_outints; /* nonzero if device loses output interrupts */ u_char mcr_image; /* copy of value written to MCR */ #ifdef COM_MULTIPORT @@ -243,6 +268,7 @@ Port_t modem_ctl_port; Port_t line_status_port; Port_t modem_status_port; + Port_t intr_ctl_port; /* Ports of IIR register */ struct tty *tp; /* cross reference */ @@ -294,6 +320,7 @@ */ /* Interrupt handling entry point. */ +inthand2_t siointrts; void siopoll __P((void)); /* Device switch entry points. */ @@ -345,13 +372,14 @@ static struct cdevsw sio_cdevsw = { sioopen, sioclose, sioread, siowrite, sioioctl, siostop, noreset, siodevtotty, - ttselect, nommap, NULL, driver_name, + ttselect, nommap, NULL, driver_name, NULL, -1, }; static int comconsole = -1; -static speed_t comdefaultrate = CONSPEED; +static volatile speed_t comdefaultrate = CONSPEED; static u_int com_events; /* input chars + weighted output completions */ +static Port_t siocniobase; static int sio_timeout; static int sio_timeouts_until_log; #if 0 /* XXX */ @@ -383,19 +411,80 @@ { -1, -1 } }; +static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; + #ifdef COM_ESP /* XXX configure this properly. */ -static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; #endif +/* + * handle sysctl read/write requests for console speed + * + * In addition to setting comdefaultrate for I/O through /dev/console, + * also set the initial and lock values for the /dev/ttyXX device + * if there is one associated with the console. Finally, if the /dev/tty + * device has already been open, change the speed on the open running port + * itself. + */ + +static int +sysctl_machdep_comdefaultrate SYSCTL_HANDLER_ARGS +{ + int error, s; + speed_t newspeed; + struct com_s *com; + struct tty *tp; + + newspeed = comdefaultrate; + + error = sysctl_handle_opaque(oidp, &newspeed, sizeof newspeed, req); + if (error || !req->newptr) + return (error); + + comdefaultrate = newspeed; + + if (comconsole < 0) /* serial console not selected? */ + return (0); + + com = com_addr(comconsole); + if (!com) + return (ENXIO); + + /* + * set the initial and lock rates for /dev/ttydXX and /dev/cuaXX + * (note, the lock rates really are boolean -- if non-zero, disallow + * speed changes) + */ + com->it_in.c_ispeed = com->it_in.c_ospeed = + com->lt_in.c_ispeed = com->lt_in.c_ospeed = + com->it_out.c_ispeed = com->it_out.c_ospeed = + com->lt_out.c_ispeed = com->lt_out.c_ospeed = comdefaultrate; + + /* + * if we're open, change the running rate too + */ + tp = com->tp; + if (tp && (tp->t_state & TS_ISOPEN)) { + tp->t_termios.c_ispeed = + tp->t_termios.c_ospeed = comdefaultrate; + s = spltty(); + error = comparam(tp, &tp->t_termios); + splx(s); + } + return error; +} + +SYSCTL_PROC(_machdep, OID_AUTO, conspeed, CTLTYPE_INT | CTLFLAG_RW, + 0, 0, sysctl_machdep_comdefaultrate, "I", ""); + #if NCARD > 0 /* * PC-Card (PCMCIA) specific code. */ -static int sioinit(struct pccard_devinfo *); /* init device */ -static void siounload(struct pccard_devinfo *); /* Disable driver */ -static int card_intr(struct pccard_devinfo *); /* Interrupt handler */ +static int sioinit __P((struct pccard_devinfo *)); +static void siounload __P((struct pccard_devinfo *)); +static int card_intr __P((struct pccard_devinfo *)); static struct pccard_device sio_info = { driver_name, @@ -409,6 +498,8 @@ DATA_SET(pccarddrv_set, sio_info); +static int pccard_mode[NSIO]; + /* * Initialize the device - called from Slot manager. */ @@ -416,12 +507,19 @@ sioinit(struct pccard_devinfo *devi) { + int unit = devi->isahd.id_unit; + /* validate unit number. */ - if (devi->isahd.id_unit >= (NSIOTOT)) + if (unit >= (NSIOTOT)) return(ENODEV); + pccard_mode[unit] = 1; /* Make sure it isn't already probed. */ - if (com_addr(devi->isahd.id_unit)) + if (com_addr(unit)) return(EBUSY); + + /* It's already probed as serial by Upper */ + devi->isahd.id_flags |= COM_C_NOPROBE; + /* * Probe the device. If a value is returned, the * device was found at the location. @@ -490,6 +588,7 @@ { static bool_t already_init; bool_t failures[10]; + Port_t *com_ptr; int fn; struct isa_device *idev; Port_t iobase; @@ -497,6 +596,23 @@ int result; struct isa_device *xdev; +#if 0 + int sio_irq_bug = 0; +#endif +#if NCARD > 0 + int probe_pccard = pccard_mode[dev->id_unit]; + int iperror = 0; +#endif /* NCARD > 0 */ + +#if NCARD > 0 + probe_pccard = pccard_mode[dev->id_unit]; +#endif /* NCARD > 0 */ +#if 0 +#ifdef SIO_IRQ_BUG + sio_irq_bug = 1; +#endif /* SIO_IRQ_BUG */ +#endif + if (!already_init) { /* * Turn off MCR_IENABLE for all likely serial ports. An unused @@ -504,12 +620,19 @@ * from any used port that shares the interrupt vector. * XXX the gate enable is elsewhere for some multiports. */ - for (xdev = isa_devtab_tty; xdev->id_driver != NULL; xdev++) - if (xdev->id_driver == &siodriver && xdev->id_enabled) - outb(xdev->id_iobase + com_mcr, 0); + for (com_ptr = likely_com_ports; + com_ptr < &likely_com_ports[sizeof likely_com_ports + / sizeof likely_com_ports[0]]; + ++com_ptr) + outb(*com_ptr + com_mcr, 0); already_init = TRUE; } + if (COM_LLCONSOLE(dev)) { + printf("sio%d: reserved for low-level i/o\n", dev->id_unit); + return (0); + } + /* * If the device is on a multiport card and has an AST/4 * compatible interrupt control register, initialize this @@ -561,11 +684,15 @@ * XXX what about the UART bug avoided by waiting in comparam()? * We don't want to to wait long enough to drain at 2 bps. */ - outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); - outb(iobase + com_dlbl, COMBRD(9600) & 0xff); - outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); - outb(iobase + com_cfcr, CFCR_8BITS); - DELAY((16 + 1) * 1000000 / (9600 / 10)); + if (iobase == siocniobase) + DELAY((16 + 1) * 1000000 / (comdefaultrate / 10)); + else { + outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); + outb(iobase + com_dlbl, COMBRD(SIO_TEST_SPEED) & 0xff); + outb(iobase + com_dlbh, (u_int) COMBRD(SIO_TEST_SPEED) >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + DELAY((16 + 1) * 1000000 / (SIO_TEST_SPEED / 10)); + } /* * Enable the interrupt gate and disable device interupts. This @@ -600,7 +727,7 @@ * it's unlikely to do more than allow the null byte out. */ outb(iobase + com_data, 0); - DELAY((1 + 2) * 1000000 / (9600 / 10)); + DELAY((1 + 2) * 1000000 / (SIO_TEST_SPEED / 10)); /* * Turn off loopback mode so that the interrupt gate works again @@ -612,6 +739,37 @@ /* EXTRA DELAY? */ outb(iobase + com_mcr, mcr_image); + /* + * It's a definitly Serial PCMCIA(16550A), but still be required + * for IIR_TXRDY implementation ( Palido 321s, DC-1S... ) + */ + if ( COM_NOPROBE(dev) ) { + /* Reading IIR register twice */ + for ( fn = 0; fn < 2; fn ++ ) { + DELAY(10000); + failures[6] = inb(iobase + com_iir); + } + /* Check IIR_TXRDY clear ? */ + result = IO_COMSIZE; + if ( failures[6] & IIR_TXRDY ) { + /* Nop, Double check with clearing IER */ + outb(iobase + com_ier, 0); + if ( inb(iobase + com_iir) & IIR_NOPEND ) { + /* Ok. we're familia this gang */ + dev->id_flags |= COM_C_IIR_TXRDYBUG; /* Set IIR_TXRDYBUG */ + } else { + /* Unknow, Just omit this chip.. XXX*/ + result = 0; + } + } else { + /* OK. this is well-known guys */ + dev->id_flags &= ~COM_C_IIR_TXRDYBUG; /*Clear IIR_TXRDYBUG*/ + } + outb(iobase + com_cfcr, CFCR_8BITS); + enable_intr(); + return( result ); + } + /* * Check that * o the CFCR, IER and MCR in UART hold the values written to them @@ -620,18 +778,76 @@ * o an output interrupt is generated and its vector is correct. * o the interrupt goes away when the IIR in the UART is read. */ + disable_intr(); /* EXTRA DELAY? */ failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; failures[1] = inb(iobase + com_ier) - IER_ETXRDY; failures[2] = inb(iobase + com_mcr) - mcr_image; DELAY(10000); /* Some internal modems need this time */ - if (idev->id_irq != 0) + if (idev->id_irq != 0 && !COM_NOTST3(idev)) failures[3] = isa_irq_pending(idev) ? 0 : 1; +#if 0 /* isn't this code still necessary? (hosokawa) */ + if (sio_irq_bug || COM_IRQBUG(idev)) { + if (idev->id_irq & 0xff) { + outb(IO_ICU1 + 1, ((idev->id_irq & 0xff)^0xff)); + } + if (idev->id_irq & 0xff00) { + outb(IO_ICU2 + 1, (((idev->id_irq >> 8) & 0xff)^0xff)); + } + } +#endif +#if NCARD > 0 + /* + * Functional ID 2 of PC-card suggests that the card is + * serial cards, so, these tests are nonsence (and in fact, + * many PC-cards fail these tests). I think these tests + * can be omitted when the card is PC-card, but currently, + * following XXX code only skips some tests. + * Any ideas? + * + * Tatsumi Hosokawa + */ + if (probe_pccard) { /* XXX */ + int t; + t = inb(iobase + com_iir) & IIR_IMASK; + failures[4] = !(t == IIR_TXRDY || t == IIR_NOPEND); + if (t == IIR_NOPEND) { + printf("sio%d: Warning: IIR status error.\n", + dev->id_unit); + } + } + else { + failures[4] = !((inb(iobase + com_iir) & IIR_IMASK) + == IIR_TXRDY); + } +#else /* NCARD > 0 */ failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; +#endif /* NCARD > 0 */ DELAY(1000); /* XXX */ if (idev->id_irq != 0) failures[5] = isa_irq_pending(idev) ? 1 : 0; +#if NCARD > 0 + if (probe_pccard && (failures[3] || failures[5])) { /* XXX */ + printf("sio%d: Warning: irq_pending error.\n", dev->id_unit); + failures[3] = failures[5] = 0; + iperror = 1; + } + if (probe_pccard) { /* XXX */ + int t; + t = inb(iobase + com_iir) & IIR_IMASK; + failures[6] = !(t == IIR_TXRDY || t == IIR_NOPEND); + if (t == IIR_TXRDY) { + printf("sio%d: Warning: IIR status error.\n", + dev->id_unit); + } + } + else { + failures[6] = !((inb(iobase + com_iir) & IIR_IMASK) + == IIR_NOPEND); + } +#else /* NCARD > 0 */ failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; +#endif /* NCARD > 0 */ /* * Turn off all device interrupts and check that they go off properly. @@ -648,7 +864,16 @@ DELAY(1000); /* XXX */ if (idev->id_irq != 0) failures[8] = isa_irq_pending(idev) ? 1 : 0; - failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; +#if NCARD > 0 + if (probe_pccard && failures[8]) { /* XXX */ + if (!iperror) { + printf("sio%d: Warning: irq_pending error.\n", + dev->id_unit); + } + failures[8] = 0; + } +#endif /* NCARD > 0 */ + failures[9] = !((inb(iobase + com_iir) & IIR_IMASK) == IIR_NOPEND); enable_intr(); @@ -782,6 +1007,7 @@ com->mcr_image = inb(com->modem_ctl_port); com->line_status_port = iobase + com_lsr; com->modem_status_port = iobase + com_msr; + com->intr_ctl_port = iobase + com_ier; /* * We don't use all the flags from since they @@ -799,6 +1025,8 @@ com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; com->it_in.c_lflag = TTYDEF_LFLAG; com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; + com->lt_out.c_ispeed = com->lt_out.c_ospeed = + com->lt_in.c_ispeed = com->lt_in.c_ospeed = com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; } else com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; @@ -816,7 +1044,9 @@ #endif /* DSI_SOFT_MODEM */ #ifdef COM_MULTIPORT - if (!COM_ISMULTIPORT(isdp)) + if (!COM_ISMULTIPORT(isdp) && !COM_IIR_TXRDYBUG(isdp)) +#else + if (!COM_IIR_TXRDYBUG(isdp)) #endif { u_char scr; @@ -836,6 +1066,7 @@ } outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); DELAY(100); + com->st16650a = 0; switch (inb(com->int_id_port) & IIR_FIFO_MASK) { case FIFO_RX_LOW: printf(" 16450"); @@ -847,47 +1078,37 @@ printf(" 16550?"); break; case FIFO_RX_HIGH: - printf(" 16550A"); if (COM_NOFIFO(isdp)) { - printf(" fifo disabled"); + printf(" 16550A fifo disabled"); } else { com->hasfifo = TRUE; - com->tx_fifo_size = 16; + if (COM_ST16650A(isdp)) { + com->st16650a = 1; + com->tx_fifo_size = 32; + printf(" ST16650A"); + } else { + com->tx_fifo_size = COM_FIFOSIZE(isdp); + printf(" 16550A"); + } + } #ifdef COM_ESP - for (espp = likely_esp_ports; *espp != 0; espp++) - if (espattach(isdp, com, *espp)) { - com->tx_fifo_size = 1024; - break; - } + for (espp = likely_esp_ports; *espp != 0; espp++) + if (espattach(isdp, com, *espp)) { + com->tx_fifo_size = 1024; + break; + } #endif + if (!com->st16650a) { + if (!com->tx_fifo_size) + com->tx_fifo_size = 16; + else + printf(" lookalike with %d bytes FIFO", + com->tx_fifo_size); } -#if 0 - /* - * Check for the Startech ST16C650 chip. - * it has a shadow register under the com_iir, - * which can only be accessed when cfcr == 0xff - */ - { - u_char i, j; - i = inb(iobase + com_iir); - outb(iobase + com_cfcr, 0xff); - outb(iobase + com_iir, 0x0); - outb(iobase + com_cfcr, CFCR_8BITS); - j = inb(iobase + com_iir); - outb(iobase + com_iir, i); - if (i != j) { - printf(" 16550A"); - } else { - com->tx_fifo_size = 32; - printf(" 16650"); - } - if (!com->tx_fifo_size) - printf(" fifo disabled"); - } -#endif break; } + #ifdef COM_ESP if (com->esp) { /* @@ -927,6 +1148,10 @@ COM_MPMASTER(isdp))->id_irq == 0; } #endif /* COM_MULTIPORT */ + if (unit == comconsole) + printf(", console"); + if ( COM_IIR_TXRDYBUG(isdp) ) + printf(" with a bogus IIR_TXRDY register"); printf("\n"); s = spltty(); @@ -955,6 +1180,7 @@ unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, UID_UUCP, GID_DIALER, 0660, "cuala%n", unit); #endif + com->id_flags = isdp->id_flags; /* Heritate id_flags for later */ return (1); } @@ -1094,8 +1320,13 @@ (void) inb(com->data_port); com->prev_modem_status = com->last_modem_status = inb(com->modem_status_port); - outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS - | IER_EMSC); + if (COM_IIR_TXRDYBUG(com)) { + outb(com->intr_ctl_port, IER_ERXRDY | IER_ERLS + | IER_EMSC); + } else { + outb(com->intr_ctl_port, IER_ERXRDY | IER_ETXRDY + | IER_ERLS | IER_EMSC); + } enable_intr(); /* * Handle initial DCD. Callout devices get a fake initial @@ -1336,10 +1567,15 @@ * devices, then the edge from one may be lost because another is * on. */ + COM_LOCK(); do { possibly_more_intrs = FALSE; for (unit = 0; unit < NSIOTOT; ++unit) { com = com_addr(unit); + /* + * XXX COM_LOCK(); + * would it work here, or be counter-productive? + */ if (com != NULL && !com->gone && (inb(com->int_id_port) & IIR_IMASK) @@ -1347,8 +1583,10 @@ siointr1(com); possibly_more_intrs = TRUE; } + /* XXX COM_UNLOCK(); */ } } while (possibly_more_intrs); + COM_UNLOCK(); #endif /* COM_MULTIPORT */ } @@ -1360,10 +1598,31 @@ u_char modem_status; u_char *ioptr; u_char recv_data; + u_char int_ident; + u_char int_ctl; + u_char int_ctl_new; + + int_ctl = inb(com->intr_ctl_port); + int_ctl_new = int_ctl; - while (TRUE) { + while (!com->gone) { line_status = inb(com->line_status_port); +#if NCARD > 0 + /* + * Some PC-cards trigger an interrupt during removal. + * Reads on a removed card return 0xff, which forces + * this interrupt handler into a loop. + * Torsten Kohler + */ + + if (line_status == 0xff) { + log(LOG_ERR, + "sio: strange line status - PC-card removed?\n"); + return; + } +#endif /* NCARD > 0 */ + /* input event? (check first to help avoid overruns) */ while (line_status & LSR_RCV_MASK) { /* break/unnattached error bits or real input? */ @@ -1486,6 +1745,9 @@ ++com->bytes_out; } com->obufq.l_head = ioptr; + if (COM_IIR_TXRDYBUG(com)) { + int_ctl_new = int_ctl | IER_ETXRDY; + } if (ioptr >= com->obufq.l_tail) { struct lbq *qp; @@ -1498,6 +1760,9 @@ com->obufq.l_next = qp; } else { /* output just completed */ + if ( COM_IIR_TXRDYBUG(com) ) { + int_ctl_new = int_ctl & ~IER_ETXRDY; + } com->state &= ~CS_BUSY; } if (!(com->state & CS_ODONE)) { @@ -1506,6 +1771,9 @@ setsofttty(); /* handle at high level ASAP */ } } + if ( COM_IIR_TXRDYBUG(com) && (int_ctl != int_ctl_new)) { + outb(com->intr_ctl_port, int_ctl_new); + } } /* finished? */ @@ -1697,6 +1965,15 @@ return (ENOTTY); } splx(s); + if (com->gone) { + printf("sio%d: gone\n", com->unit); + s = spltty(); + com_addr(com->unit) = 0; + bzero(tp,sizeof *tp); + bzero(com,sizeof *com); + free(com,M_TTYS); + splx(s); + } return (0); } @@ -1915,7 +2192,7 @@ if (cflag & CSTOPB) cfcr |= CFCR_STOPB; - if (com->hasfifo && divisor != 0) { + if (com->hasfifo) { /* * Use a fifo trigger level low enough so that the input * latency from the fifo is less than about 16 msec and @@ -2001,10 +2278,18 @@ if (inb(iobase + com_dlbh) != dlbh) outb(iobase + com_dlbh, dlbh); } + + outb(iobase + com_cfcr, com->cfcr_image = cfcr); + if (!(tp->t_state & TS_TTSTOP)) com->state |= CS_TTGO; + if (cflag & CRTS_IFLOW) { + if (com->st16650a) { + outb(iobase + com_cfcr, 0xbf); + outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x40); + } com->state |= CS_RTS_IFLOW; /* * If CS_RTS_IFLOW just changed from off to on, the change @@ -2019,8 +2304,13 @@ * on here, since comstart() won't do it later. */ outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); + if (com->st16650a) { + outb(iobase + com_cfcr, 0xbf); + outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x40); + } } + /* * Set up state to handle output flow control. * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? @@ -2032,7 +2322,21 @@ com->state |= CS_CTS_OFLOW; if (!(com->last_modem_status & MSR_CTS)) com->state &= ~CS_ODEVREADY; + if (com->st16650a) { + outb(iobase + com_cfcr, 0xbf); + outb(iobase + com_fifo, inb(iobase + com_fifo) | 0x80); + } + } else { + if (com->st16650a) { + outb(iobase + com_cfcr, 0xbf); + outb(iobase + com_fifo, inb(iobase + com_fifo) & ~0x80); + } } + + + outb(iobase + com_cfcr, com->cfcr_image); + + /* XXX shouldn't call functions while intrs are disabled. */ disc_optim(tp, t, com); /* @@ -2075,6 +2379,7 @@ } enable_intr(); if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { + ttwwakeup(tp); splx(s); return; } @@ -2144,7 +2449,6 @@ return; disable_intr(); if (rw & FWRITE) { -#ifdef COM_ESP_BUG_FIXED if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ @@ -2153,7 +2457,6 @@ /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_XMT_RST | com->fifo_image); -#endif com->obufs[0].l_queued = FALSE; com->obufs[1].l_queued = FALSE; if (com->state & CS_ODONE) @@ -2162,7 +2465,6 @@ com->tp->t_state &= ~TS_BUSY; } if (rw & FREAD) { -#ifdef COM_ESP_BUG_FIXED if (com->hasfifo) #ifdef COM_ESP /* XXX avoid h/w bug. */ @@ -2171,7 +2473,6 @@ /* XXX does this flush everything? */ outb(com->iobase + com_fifo, FIFO_RCV_RST | com->fifo_image); -#endif com_events -= (com->iptr - com->ibuf); com->iptr = com->ibuf; } @@ -2360,7 +2661,7 @@ tp->t_state &= ~TS_CAN_BYPASS_L_RINT; /* * Prepare to reduce input latency for packet - * discplines with a end of packet character. + * discplines with a end of packet charactor. */ if (tp->t_line == SLIPDISC) com->hotchar = 0xc0; @@ -2383,8 +2684,7 @@ u_char mcr; }; -static Port_t siocniobase; - +static speed_t siocngetspeed __P((Port_t, struct speedtab *)); static void siocnclose __P((struct siocnstate *sp)); static void siocnopen __P((struct siocnstate *sp)); static void siocntxwait __P((void)); @@ -2405,6 +2705,42 @@ ; } +/* + * Read the serial port specified and try to figure out what speed + * it's currently running at. We're assuming the serial port has + * been initialized and is basicly idle. This routine is only intended + * to be run at system startup. + * + * If the value read from the serial port doesn't make sense, return 0. + */ + +static speed_t +siocngetspeed(iobase, table) + Port_t iobase; + struct speedtab *table; +{ + int code; + u_char dlbh; + u_char dlbl; + u_char cfcr; + + cfcr = inb(iobase + com_cfcr); + outb(iobase + com_cfcr, CFCR_DLAB | cfcr); + + dlbl = inb(iobase + com_dlbl); + dlbh = inb(iobase + com_dlbh); + + outb(iobase + com_cfcr, cfcr); + + code = dlbh << 8 | dlbl; + + for ( ; table->sp_speed != -1; table++) + if (table->sp_code == code) + return (table->sp_speed); + + return 0; /* didn't match anything sane */ +} + static void siocnopen(sp) struct siocnstate *sp; @@ -2478,21 +2814,66 @@ siocnprobe(cp) struct consdev *cp; { - int unit; + speed_t boot_speed; + u_char cfcr; + struct isa_device *dvp; + int s; + struct siocnstate sp; + + /* + * Find our first enabled console, if any. If it is a high-level + * console device, then initialize it and return successfully. + * If it is a low-level console device, then initialize it and + * return unsuccessfully. It must be initialized in both cases + * for early use by console drivers and debuggers. Initializing + * the hardware is not necessary in all cases, since the i/o + * routines initialize it on the fly, but it is necessary if + * input might arrive while the hardware is switched back to an + * uninitialized state. We can't handle multiple console devices + * yet because our low-level routines don't take a device arg. + * We trust the user to set the console flags properly so that we + * don't need to probe. + */ + cp->cn_pri = CN_DEAD; + for (dvp = isa_devtab_tty; dvp->id_driver != NULL; dvp++) + if (dvp->id_driver == &siodriver && dvp->id_enabled + && COM_CONSOLE(dvp)) { + siocniobase = dvp->id_iobase; + s = spltty(); + if (boothowto & RB_SERIAL) { + boot_speed = siocngetspeed(siocniobase, + comspeedtab); + if (boot_speed) + comdefaultrate = boot_speed; + } - /* XXX: ick */ - unit = DEV_TO_UNIT(CONUNIT); - siocniobase = CONADDR; - - /* make sure hardware exists? XXX */ - - /* initialize required fields */ - cp->cn_dev = makedev(CDEV_MAJOR, unit); -#ifdef COMCONSOLE - cp->cn_pri = CN_REMOTE; /* Force a serial port console */ -#else - cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL; -#endif + /* + * Initialize the divisor latch. We can't rely on + * siocnopen() to do this the first time, since it + * avoids writing to the latch if the latch appears + * to have the correct value. Also, if we didn't + * just read the speed from the hardware, then we + * need to set the speed in hardware so that + * switching it later is null. + */ + cfcr = inb(siocniobase + com_cfcr); + outb(siocniobase + com_cfcr, CFCR_DLAB | cfcr); + outb(siocniobase + com_dlbl, + COMBRD(comdefaultrate) & 0xff); + outb(siocniobase + com_dlbh, + (u_int) COMBRD(comdefaultrate) >> 8); + outb(siocniobase + com_cfcr, cfcr); + + siocnopen(&sp); + splx(s); + if (!COM_LLCONSOLE(dvp)) { + cp->cn_dev = makedev(CDEV_MAJOR, dvp->id_unit); + cp->cn_pri = COM_FORCECONSOLE(dvp) + || boothowto & RB_SERIAL + ? CN_REMOTE : CN_NORMAL; + } + break; + } } void diff -urN sys.orig/i386/isa/sio.c.PAO sys/i386/isa/sio.c.PAO --- sys.orig/i386/isa/sio.c.PAO Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/sio.c.PAO Tue Apr 14 08:45:10 1998 @@ -0,0 +1,2892 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * from: @(#)com.c 7.5 (Berkeley) 5/16/91 + * $Id: sio.c,v 1.147.2.13 1998/03/08 09:57:35 jkh Exp $ + */ + +#include "opt_comconsole.h" +#include "opt_ddb.h" +#include "opt_sio.h" +#include "sio.h" +#include "pnp.h" + +#ifndef EXTRA_SIO +#if NPNP > 0 +#define EXTRA_SIO 2 +#else +#define EXTRA_SIO 0 +#endif +#endif + +#define NSIOTOT (NSIO + EXTRA_SIO) + +/* + * Serial driver, based on 386BSD-0.1 com driver. + * Mostly rewritten to use pseudo-DMA. + * Works for National Semiconductor NS8250-NS16550AF UARTs. + * COM driver, based on HP dca driver. + * + * Changes for PC-Card integration: + * - Added PC-Card driver table and handlers + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef DEVFS +#include +#endif + +#include + +#include +#include +#include + +#ifdef COM_ESP +#include +#endif +#include + +#include "card.h" +#if NCARD > 0 +#include +#include +#include +#endif + +#if NPNP > 0 +#include +#endif + +#define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ +#define RB_I_HIGH_WATER (TTYHOG - 2 * RS_IBUFSIZE) +#ifndef DPTOPT /* not sure why this is in dpt.. latency requirements? [JRE] */ +#define RS_IBUFSIZE 256 +#else +#define RS_IBUFSIZE 512 +#endif /* DPTOPT */ + +#define CALLOUT_MASK 0x80 +#define CONTROL_MASK 0x60 +#define CONTROL_INIT_STATE 0x20 +#define CONTROL_LOCK_STATE 0x40 +#define DEV_TO_UNIT(dev) (MINOR_TO_UNIT(minor(dev))) +#define MINOR_MAGIC_MASK (CALLOUT_MASK | CONTROL_MASK) +#define MINOR_TO_UNIT(mynor) ((mynor) & ~MINOR_MAGIC_MASK) + +#ifdef COM_MULTIPORT +/* checks in flags for multiport and which is multiport "master chip" + * for a given card + */ +#define COM_ISMULTIPORT(dev) ((dev)->id_flags & 0x01) +#define COM_MPMASTER(dev) (((dev)->id_flags >> 8) & 0x0ff) +#define COM_NOTAST4(dev) ((dev)->id_flags & 0x04) +#endif /* COM_MULTIPORT */ + +#define COM_LOSESOUTINTS(dev) ((dev)->id_flags & 0x08) +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) +#define COM_IRQBUG(dev) ((dev)->id_flags & 0x10) +#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) + +#define com_scr 7 /* scratch register for 16450-16550 (R/W) */ + +/* + * Input buffer watermarks. + * The external device is asked to stop sending when the buffer exactly reaches + * high water, or when the high level requests it. + * The high level is notified immediately (rather than at a later clock tick) + * when this watermark is reached. + * The buffer size is chosen so the watermark should almost never be reached. + * The low watermark is invisibly 0 since the buffer is always emptied all at + * once. + */ +#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) + +/* + * com state bits. + * (CS_BUSY | CS_TTGO) and (CS_BUSY | CS_TTGO | CS_ODEVREADY) must be higher + * than the other bits so that they can be tested as a group without masking + * off the low bits. + * + * The following com and tty flags correspond closely: + * CS_BUSY = TS_BUSY (maintained by comstart(), siopoll() and + * siostop()) + * CS_TTGO = ~TS_TTSTOP (maintained by comparam() and comstart()) + * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) + * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) + * TS_FLUSH is not used. + * XXX I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + * XXX CS_*FLOW should be CF_*FLOW in com->flags (control flags not state). + */ +#define CS_BUSY 0x80 /* output in progress */ +#define CS_TTGO 0x40 /* output not stopped by XOFF */ +#define CS_ODEVREADY 0x20 /* external device h/w ready (CTS) */ +#define CS_CHECKMSR 1 /* check of MSR scheduled */ +#define CS_CTS_OFLOW 2 /* use CTS output flow control */ +#define CS_DTR_OFF 0x10 /* DTR held off */ +#define CS_ODONE 4 /* output completed */ +#define CS_RTS_IFLOW 8 /* use RTS input flow control */ +#define CSE_BUSYCHECK 1 /* siobusycheck() scheduled */ + +static char const * const error_desc[] = { +#define CE_OVERRUN 0 + "silo overflow", +#define CE_INTERRUPT_BUF_OVERFLOW 1 + "interrupt-level buffer overflow", +#define CE_TTY_BUF_OVERFLOW 2 + "tty-level buffer overflow", +}; + +#define CE_NTYPES 3 +#define CE_RECORD(com, errnum) (++(com)->delta_error_counts[errnum]) + +/* types. XXX - should be elsewhere */ +typedef u_int Port_t; /* hardware port */ +typedef u_char bool_t; /* boolean */ + +/* queue of linear buffers */ +struct lbq { + u_char *l_head; /* next char to process */ + u_char *l_tail; /* one past the last char to process */ + struct lbq *l_next; /* next in queue */ + bool_t l_queued; /* nonzero if queued */ +}; + +/* com device structure */ +struct com_s { + u_char state; /* miscellaneous flag bits */ + bool_t active_out; /* nonzero if the callout device is open */ + u_char cfcr_image; /* copy of value written to CFCR */ +#ifdef COM_ESP + bool_t esp; /* is this unit a hayes esp board? */ +#endif + u_char extra_state; /* more flag bits, separate for order trick */ + u_char fifo_image; /* copy of value written to FIFO */ + bool_t hasfifo; /* nonzero for 16550 UARTs */ + bool_t loses_outints; /* nonzero if device loses output interrupts */ + u_char mcr_image; /* copy of value written to MCR */ +#ifdef COM_MULTIPORT + bool_t multiport; /* is this unit part of a multiport device? */ +#endif /* COM_MULTIPORT */ + bool_t no_irq; /* nonzero if irq is not attached */ + bool_t gone; /* hardware disappeared */ + bool_t poll; /* nonzero if polling is required */ + bool_t poll_output; /* nonzero if polling for output is required */ + int unit; /* unit number */ + int dtr_wait; /* time to hold DTR down on close (* 1/hz) */ + u_int tx_fifo_size; + u_int wopeners; /* # processes waiting for DCD in open() */ + + /* + * The high level of the driver never reads status registers directly + * because there would be too many side effects to handle conveniently. + * Instead, it reads copies of the registers stored here by the + * interrupt handler. + */ + u_char last_modem_status; /* last MSR read by intr handler */ + u_char prev_modem_status; /* last MSR handled by high level */ + + u_char hotchar; /* ldisc-specific char to be handled ASAP */ + u_char *ibuf; /* start of input buffer */ + u_char *ibufend; /* end of input buffer */ + u_char *ihighwater; /* threshold in input buffer */ + u_char *iptr; /* next free spot in input buffer */ + + struct lbq obufq; /* head of queue of output buffers */ + struct lbq obufs[2]; /* output buffers */ + + Port_t data_port; /* i/o ports */ +#ifdef COM_ESP + Port_t esp_port; +#endif + Port_t int_id_port; + Port_t iobase; + Port_t modem_ctl_port; + Port_t line_status_port; + Port_t modem_status_port; + + struct tty *tp; /* cross reference */ + + /* Initial state. */ + struct termios it_in; /* should be in struct tty */ + struct termios it_out; + + /* Lock state. */ + struct termios lt_in; /* should be in struct tty */ + struct termios lt_out; + + bool_t do_timestamp; + bool_t do_dcd_timestamp; + struct timeval timestamp; + struct timeval dcd_timestamp; + + u_long bytes_in; /* statistics */ + u_long bytes_out; + u_int delta_error_counts[CE_NTYPES]; + u_long error_counts[CE_NTYPES]; + + /* + * Ping-pong input buffers. The extra factor of 2 in the sizes is + * to allow for an error byte for each input byte. + */ +#define CE_INPUT_OFFSET RS_IBUFSIZE + u_char ibuf1[2 * RS_IBUFSIZE]; + u_char ibuf2[2 * RS_IBUFSIZE]; + + /* + * Data area for output buffers. Someday we should build the output + * buffer queue without copying data. + */ + u_char obuf1[256]; + u_char obuf2[256]; +#ifdef DEVFS + void *devfs_token_ttyd; + void *devfs_token_ttyl; + void *devfs_token_ttyi; + void *devfs_token_cuaa; + void *devfs_token_cual; + void *devfs_token_cuai; +#endif +}; + +/* + * XXX public functions in drivers should be declared in headers produced + * by `config', not here. + */ + +/* Interrupt handling entry point. */ +inthand2_t siointrts; +void siopoll __P((void)); + +/* Device switch entry points. */ +#define sioreset noreset +#define siommap nommap +#define siostrategy nostrategy + +#ifdef COM_ESP +static int espattach __P((struct isa_device *isdp, struct com_s *com, + Port_t esp_port)); +#endif +static int sioattach __P((struct isa_device *dev)); +static timeout_t siobusycheck; +static timeout_t siodtrwakeup; +static void comhardclose __P((struct com_s *com)); +static void siointr1 __P((struct com_s *com)); +static int commctl __P((struct com_s *com, int bits, int how)); +static int comparam __P((struct tty *tp, struct termios *t)); +static int sioprobe __P((struct isa_device *dev)); +static void siosettimeout __P((void)); +static void comstart __P((struct tty *tp)); +static timeout_t comwakeup; +static void disc_optim __P((struct tty *tp, struct termios *t, + struct com_s *com)); + +#ifdef DSI_SOFT_MODEM +static int LoadSoftModem __P((int unit,int base_io, u_long size, u_char *ptr)); +#endif /* DSI_SOFT_MODEM */ + +static char driver_name[] = "sio"; + +/* table and macro for fast conversion from a unit number to its com struct */ +static struct com_s *p_com_addr[NSIOTOT]; +#define com_addr(unit) (p_com_addr[unit]) + +struct isa_driver siodriver = { + sioprobe, sioattach, driver_name +}; + +static d_open_t sioopen; +static d_close_t sioclose; +static d_read_t sioread; +static d_write_t siowrite; +static d_ioctl_t sioioctl; +static d_stop_t siostop; +static d_devtotty_t siodevtotty; + +#define CDEV_MAJOR 28 +static struct cdevsw sio_cdevsw = { + sioopen, sioclose, sioread, siowrite, + sioioctl, siostop, noreset, siodevtotty, + ttselect, nommap, NULL, driver_name, + NULL, -1, +}; + +static int comconsole = -1; +static speed_t comdefaultrate = CONSPEED; +static u_int com_events; /* input chars + weighted output completions */ +static int sio_timeout; +static int sio_timeouts_until_log; +#if 0 /* XXX */ +static struct tty *sio_tty[NSIOTOT]; +#else +static struct tty sio_tty[NSIOTOT]; +#endif +static const int nsio_tty = NSIOTOT; + +static struct speedtab comspeedtab[] = { + { 0, 0 }, + { 50, COMBRD(50) }, + { 75, COMBRD(75) }, + { 110, COMBRD(110) }, + { 134, COMBRD(134) }, + { 150, COMBRD(150) }, + { 200, COMBRD(200) }, + { 300, COMBRD(300) }, + { 600, COMBRD(600) }, + { 1200, COMBRD(1200) }, + { 1800, COMBRD(1800) }, + { 2400, COMBRD(2400) }, + { 4800, COMBRD(4800) }, + { 9600, COMBRD(9600) }, + { 19200, COMBRD(19200) }, + { 38400, COMBRD(38400) }, + { 57600, COMBRD(57600) }, + { 115200, COMBRD(115200) }, + { -1, -1 } +}; + +/* XXX configure this properly. */ +static Port_t likely_com_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8, }; + +#ifdef COM_ESP +static Port_t likely_esp_ports[] = { 0x140, 0x180, 0x280, 0 }; +#endif + +#if NCARD > 0 +/* + * PC-Card (PCMCIA) specific code. + */ +static int sioinit(struct pccard_devinfo *); /* init device */ +static void siounload(struct pccard_devinfo *); /* Disable driver */ +static int card_intr(struct pccard_devinfo *); /* Interrupt handler */ + +static struct pccard_device sio_info = { + driver_name, + sioinit, + siounload, + card_intr, + 0, /* Attributes - presently unused */ + &tty_imask /* Interrupt mask for device */ + /* XXX - Should this also include net_imask? */ +}; + +DATA_SET(pccarddrv_set, sio_info); + +static int pccard_mode[NSIO]; + +/* + * Initialize the device - called from Slot manager. + */ +int +sioinit(struct pccard_devinfo *devi) +{ + int unit = devi->isahd.id_unit; + /* validate unit number. */ + if (unit >= (NSIOTOT)) + return(ENODEV); + pccard_mode[unit] = 1; + /* Make sure it isn't already probed. */ + if (com_addr(unit)) + return(EBUSY); + /* + * Probe the device. If a value is returned, the + * device was found at the location. + */ + if (sioprobe(&devi->isahd) == 0) + return(ENXIO); + if (sioattach(&devi->isahd) == 0) + return(ENXIO); + + return(0); +} + +/* + * siounload - unload the driver and clear the table. + * XXX TODO: + * This is usually called when the card is ejected, but + * can be caused by a modunload of a controller driver. + * The idea is to reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +static void +siounload(struct pccard_devinfo *devi) +{ + struct com_s *com; + + com = com_addr(devi->isahd.id_unit); + if (!com->iobase) { + printf("sio%d already unloaded!\n",devi->isahd.id_unit); + return; + } + if (com->tp && (com->tp->t_state & TS_ISOPEN)) { + com->gone = 1; + printf("sio%d: unload\n", devi->isahd.id_unit); + com->tp->t_gen++; + ttyclose(com->tp); + ttwakeup(com->tp); + ttwwakeup(com->tp); + } else { + com_addr(com->unit) = NULL; + bzero(com, sizeof *com); + free(com,M_TTYS); + printf("sio%d: unload,gone\n", devi->isahd.id_unit); + } +} + +/* + * card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +card_intr(struct pccard_devinfo *devi) +{ + struct com_s *com; + + com = com_addr(devi->isahd.id_unit); + if (com && !com->gone) + siointr1(com_addr(devi->isahd.id_unit)); + return(1); +} +#endif /* NCARD > 0 */ + +static int +sioprobe(dev) + struct isa_device *dev; +{ + static bool_t already_init; + bool_t failures[10]; + Port_t *com_ptr; + int fn; + struct isa_device *idev; + Port_t iobase; + u_char mcr_image; + int result; + struct isa_device *xdev; +#if 0 + int sio_irq_bug = 0; +#endif +#if NCARD > 0 + int probe_pccard = pccard_mode[dev->id_unit]; + int iperror = 0; +#endif /* NCARD > 0 */ + +#if NCARD > 0 + probe_pccard = pccard_mode[dev->id_unit]; +#endif /* NCARD > 0 */ +#if 0 +#ifdef SIO_IRQ_BUG + sio_irq_bug = 1; +#endif /* SIO_IRQ_BUG */ +#endif + + if (!already_init) { + /* + * Turn off MCR_IENABLE for all likely serial ports. An unused + * port with its MCR_IENABLE gate open will inhibit interrupts + * from any used port that shares the interrupt vector. + * XXX the gate enable is elsewhere for some multiports. + */ + for (com_ptr = likely_com_ports; + com_ptr < &likely_com_ports[sizeof likely_com_ports + / sizeof likely_com_ports[0]]; + ++com_ptr) + outb(*com_ptr + com_mcr, 0); + already_init = TRUE; + } + + /* + * If the device is on a multiport card and has an AST/4 + * compatible interrupt control register, initialize this + * register and prepare to leave MCR_IENABLE clear in the mcr. + * Otherwise, prepare to set MCR_IENABLE in the mcr. + * Point idev to the device struct giving the correct id_irq. + * This is the struct for the master device if there is one. + */ + idev = dev; + mcr_image = MCR_IENABLE; +#ifdef COM_MULTIPORT + if (COM_ISMULTIPORT(dev)) { + idev = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(dev)); + if (idev == NULL) { + printf("sio%d: master device %d not configured\n", + dev->id_unit, COM_MPMASTER(dev)); + return (0); + } + if (!COM_NOTAST4(dev)) { + outb(idev->id_iobase + com_scr, + idev->id_irq ? 0x80 : 0); + mcr_image = 0; + } + } +#endif /* COM_MULTIPORT */ + if (idev->id_irq == 0) + mcr_image = 0; + + bzero(failures, sizeof failures); + iobase = dev->id_iobase; + + /* + * We don't want to get actual interrupts, just masked ones. + * Interrupts from this line should already be masked in the ICU, + * but mask them in the processor as well in case there are some +! * (2) shared interrupts. + */ + disable_intr(); +/* EXTRA DELAY? */ + + /* + * Initialize the speed and the word size and wait long enough to + * drain the maximum of 16 bytes of junk in device output queues. + * The speed is undefined after a master reset and must be set + * before relying on anything related to output. There may be + * junk after a (very fast) soft reboot and (apparently) after + * master reset. + * XXX what about the UART bug avoided by waiting in comparam()? + * We don't want to to wait long enough to drain at 2 bps. + */ + outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); + outb(iobase + com_dlbl, COMBRD(9600) & 0xff); + outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + DELAY((16 + 1) * 1000000 / (9600 / 10)); + + /* + * Enable the interrupt gate and disable device interupts. This + * should leave the device driving the interrupt line low and + * guarantee an edge trigger if an interrupt can be generated. + */ +/* EXTRA DELAY? */ + outb(iobase + com_mcr, mcr_image); + outb(iobase + com_ier, 0); + + /* + * Attempt to set loopback mode so that we can send a null byte + * without annoying any external device. + */ +/* EXTRA DELAY? */ + outb(iobase + com_mcr, mcr_image | MCR_LOOPBACK); + + /* + * Attempt to generate an output interrupt. On 8250's, setting + * IER_ETXRDY generates an interrupt independent of the current + * setting and independent of whether the THR is empty. On 16450's, + * setting IER_ETXRDY generates an interrupt independent of the + * current setting. On 16550A's, setting IER_ETXRDY only + * generates an interrupt when IER_ETXRDY is not already set. + */ + outb(iobase + com_ier, IER_ETXRDY); + + /* + * On some 16x50 incompatibles, setting IER_ETXRDY doesn't generate + * an interrupt. They'd better generate one for actually doing + * output. Loopback may be broken on the same incompatibles but + * it's unlikely to do more than allow the null byte out. + */ + outb(iobase + com_data, 0); + DELAY((1 + 2) * 1000000 / (9600 / 10)); + + /* + * Turn off loopback mode so that the interrupt gate works again + * (MCR_IENABLE was hidden). This should leave the device driving + * an interrupt line high. It doesn't matter if the interrupt + * line oscillates while we are not looking at it, since interrupts + * are disabled. + */ +/* EXTRA DELAY? */ + outb(iobase + com_mcr, mcr_image); + + /* + * Check that + * o the CFCR, IER and MCR in UART hold the values written to them + * (the values happen to be all distinct - this is good for + * avoiding false positive tests from bus echoes). + * o an output interrupt is generated and its vector is correct. + * o the interrupt goes away when the IIR in the UART is read. + */ + disable_intr(); +/* EXTRA DELAY? */ + failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; + failures[1] = inb(iobase + com_ier) - IER_ETXRDY; + failures[2] = inb(iobase + com_mcr) - mcr_image; + DELAY(10000); /* Some internal modems need this time */ + if (idev->id_irq != 0) + failures[3] = isa_irq_pending(idev) ? 0 : 1; +#if 0 /* isn't this code still necessary? (hosokawa) */ + if (sio_irq_bug || COM_IRQBUG(idev)) { + if (idev->id_irq & 0xff) { + outb(IO_ICU1 + 1, ((idev->id_irq & 0xff)^0xff)); + } + if (idev->id_irq & 0xff00) { + outb(IO_ICU2 + 1, (((idev->id_irq >> 8) & 0xff)^0xff)); + } + } +#endif +#if NCARD > 0 + /* + * Functional ID 2 of PC-card suggests that the card is + * serial cards, so, these tests are nonsence (and in fact, + * many PC-cards fail these tests). I think these tests + * can be omitted when the card is PC-card, but currently, + * following XXX code only skips some tests. + * Any ideas? + * + * Tatsumi Hosokawa + */ + if (probe_pccard) { /* XXX */ + int t; + t = inb(iobase + com_iir) & IIR_IMASK; + failures[4] = !(t == IIR_TXRDY || t == IIR_NOPEND); + if (t == IIR_NOPEND) { + printf("sio%d: Warning: IIR status error.\n", + dev->id_unit); + } + } + else { + failures[4] = !((inb(iobase + com_iir) & IIR_IMASK) + == IIR_TXRDY); + } +#else /* NCARD > 0 */ + failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; +#endif /* NCARD > 0 */ + DELAY(1000); /* XXX */ + if (idev->id_irq != 0) + failures[5] = isa_irq_pending(idev) ? 1 : 0; +#if NCARD > 0 + if (probe_pccard && (failures[3] || failures[5])) { /* XXX */ + printf("sio%d: Warning: irq_pending error.\n", dev->id_unit); + failures[3] = failures[5] = 0; + iperror = 1; + } + if (probe_pccard) { /* XXX */ + int t; + t = inb(iobase + com_iir) & IIR_IMASK; + failures[6] = !(t == IIR_TXRDY || t == IIR_NOPEND); + if (t == IIR_TXRDY) { + printf("sio%d: Warning: IIR status error.\n", + dev->id_unit); + } + } + else { + failures[6] = !((inb(iobase + com_iir) & IIR_IMASK) + == IIR_NOPEND); + } +#else /* NCARD > 0 */ + failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; +#endif /* NCARD > 0 */ + + /* + * Turn off all device interrupts and check that they go off properly. + * Leave MCR_IENABLE alone. For ports without a master port, it gates + * the OUT2 output of the UART to + * the ICU input. Closing the gate would give a floating ICU input + * (unless there is another device driving at) and spurious interrupts. + * (On the system that this was first tested on, the input floats high + * and gives a (masked) interrupt as soon as the gate is closed.) + */ + outb(iobase + com_ier, 0); + outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ + failures[7] = inb(iobase + com_ier); + DELAY(1000); /* XXX */ + if (idev->id_irq != 0) + failures[8] = isa_irq_pending(idev) ? 1 : 0; +#if NCARD > 0 + if (probe_pccard && failures[8]) { /* XXX */ + if (!iperror) { + printf("sio%d: Warning: irq_pending error.\n", + dev->id_unit); + } + failures[8] = 0; + } +#endif /* NCARD > 0 */ + failures[9] = !((inb(iobase + com_iir) & IIR_IMASK) == IIR_NOPEND); + + enable_intr(); + + result = IO_COMSIZE; + for (fn = 0; fn < sizeof failures; ++fn) + if (failures[fn]) { + outb(iobase + com_mcr, 0); + result = 0; + if (COM_VERBOSE(dev)) + printf("sio%d: probe test %d failed\n", + dev->id_unit, fn); + } + return (result); +} + +#ifdef COM_ESP +static int +espattach(isdp, com, esp_port) + struct isa_device *isdp; + struct com_s *com; + Port_t esp_port; +{ + u_char dips; + u_char val; + + /* + * Check the ESP-specific I/O port to see if we're an ESP + * card. If not, return failure immediately. + */ + if ((inb(esp_port) & 0xf3) == 0) { + printf(" port 0x%x is not an ESP board?\n", esp_port); + return (0); + } + + /* + * We've got something that claims to be a Hayes ESP card. + * Let's hope so. + */ + + /* Get the dip-switch configuration */ + outb(esp_port + ESP_CMD1, ESP_GETDIPS); + dips = inb(esp_port + ESP_STATUS1); + + /* + * Bits 0,1 of dips say which COM port we are. + */ + if (com->iobase == likely_com_ports[dips & 0x03]) + printf(" : ESP"); + else { + printf(" esp_port has com %d\n", dips & 0x03); + return (0); + } + + /* + * Check for ESP version 2.0 or later: bits 4,5,6 = 010. + */ + outb(esp_port + ESP_CMD1, ESP_GETTEST); + val = inb(esp_port + ESP_STATUS1); /* clear reg 1 */ + val = inb(esp_port + ESP_STATUS2); + if ((val & 0x70) < 0x20) { + printf("-old (%o)", val & 0x70); + return (0); + } + + /* + * Check for ability to emulate 16550: bit 7 == 1 + */ + if ((dips & 0x80) == 0) { + printf(" slave"); + return (0); + } + + /* + * Okay, we seem to be a Hayes ESP card. Whee. + */ + com->esp = TRUE; + com->esp_port = esp_port; + return (1); +} +#endif /* COM_ESP */ + +static int +sioattach(isdp) + struct isa_device *isdp; +{ + struct com_s *com; + dev_t dev; +#ifdef COM_ESP + Port_t *espp; +#endif + Port_t iobase; + int s; + int unit; + + isdp->id_ri_flags |= RI_FAST; + iobase = isdp->id_iobase; + unit = isdp->id_unit; + com = malloc(sizeof *com, M_TTYS, M_NOWAIT); + if (com == NULL) + return (0); + + /* + * sioprobe() has initialized the device registers as follows: + * o cfcr = CFCR_8BITS. + * It is most important that CFCR_DLAB is off, so that the + * data port is not hidden when we enable interrupts. + * o ier = 0. + * Interrupts are only enabled when the line is open. + * o mcr = MCR_IENABLE, or 0 if the port has AST/4 compatible + * interrupt control register or the config specifies no irq. + * Keeping MCR_DTR and MCR_RTS off might stop the external + * device from sending before we are ready. + */ + bzero(com, sizeof *com); + com->unit = unit; + com->cfcr_image = CFCR_8BITS; + com->dtr_wait = 3 * hz; + com->loses_outints = COM_LOSESOUTINTS(isdp) != 0; + com->no_irq = isdp->id_irq == 0; + com->tx_fifo_size = 1; + com->iptr = com->ibuf = com->ibuf1; + com->ibufend = com->ibuf1 + RS_IBUFSIZE; + com->ihighwater = com->ibuf1 + RS_IHIGHWATER; + com->obufs[0].l_head = com->obuf1; + com->obufs[1].l_head = com->obuf2; + + com->iobase = iobase; + com->data_port = iobase + com_data; + com->int_id_port = iobase + com_iir; + com->modem_ctl_port = iobase + com_mcr; + com->mcr_image = inb(com->modem_ctl_port); + com->line_status_port = iobase + com_lsr; + com->modem_status_port = iobase + com_msr; + + /* + * We don't use all the flags from since they + * are only relevant for logins. It's important to have echo off + * initially so that the line doesn't start blathering before the + * echo flag can be turned off. + */ + com->it_in.c_iflag = 0; + com->it_in.c_oflag = 0; + com->it_in.c_cflag = TTYDEF_CFLAG; + com->it_in.c_lflag = 0; + if (unit == comconsole) { + com->it_in.c_iflag = TTYDEF_IFLAG; + com->it_in.c_oflag = TTYDEF_OFLAG; + com->it_in.c_cflag = TTYDEF_CFLAG | CLOCAL; + com->it_in.c_lflag = TTYDEF_LFLAG; + com->lt_out.c_cflag = com->lt_in.c_cflag = CLOCAL; + com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; + } else + com->it_in.c_ispeed = com->it_in.c_ospeed = TTYDEF_SPEED; + termioschars(&com->it_in); + com->it_out = com->it_in; + + /* attempt to determine UART type */ + printf("sio%d: type", unit); + +#ifdef DSI_SOFT_MODEM + if((inb(iobase+7) ^ inb(iobase+7)) & 0x80) { + printf(" Digicom Systems, Inc. SoftModem"); + goto determined_type; + } +#endif /* DSI_SOFT_MODEM */ + +#ifdef COM_MULTIPORT + if (!COM_ISMULTIPORT(isdp)) +#endif + { + u_char scr; + u_char scr1; + u_char scr2; + + scr = inb(iobase + com_scr); + outb(iobase + com_scr, 0xa5); + scr1 = inb(iobase + com_scr); + outb(iobase + com_scr, 0x5a); + scr2 = inb(iobase + com_scr); + outb(iobase + com_scr, scr); + if (scr1 != 0xa5 || scr2 != 0x5a) { + printf(" 8250"); + goto determined_type; + } + } + outb(iobase + com_fifo, FIFO_ENABLE | FIFO_RX_HIGH); + DELAY(100); + switch (inb(com->int_id_port) & IIR_FIFO_MASK) { + case FIFO_RX_LOW: + printf(" 16450"); + break; + case FIFO_RX_MEDL: + printf(" 16450?"); + break; + case FIFO_RX_MEDH: + printf(" 16550?"); + break; + case FIFO_RX_HIGH: + printf(" 16550A"); + if (COM_NOFIFO(isdp)) { + printf(" fifo disabled"); + } else { + com->hasfifo = TRUE; + com->tx_fifo_size = 16; +#ifdef COM_ESP + for (espp = likely_esp_ports; *espp != 0; espp++) + if (espattach(isdp, com, *espp)) { + com->tx_fifo_size = 1024; + break; + } +#endif + } +#if 0 + /* + * Check for the Startech ST16C650 chip. + * it has a shadow register under the com_iir, + * which can only be accessed when cfcr == 0xff + */ + { + u_char i, j; + + i = inb(iobase + com_iir); + outb(iobase + com_cfcr, 0xff); + outb(iobase + com_iir, 0x0); + outb(iobase + com_cfcr, CFCR_8BITS); + j = inb(iobase + com_iir); + outb(iobase + com_iir, i); + if (i != j) { + printf(" 16550A"); + } else { + com->tx_fifo_size = 32; + printf(" 16650"); + } + if (!com->tx_fifo_size) + printf(" fifo disabled"); + } +#endif + break; + } +#ifdef COM_ESP + if (com->esp) { + /* + * Set 16550 compatibility mode. + * We don't use the ESP_MODE_SCALE bit to increase the + * fifo trigger levels because we can't handle large + * bursts of input. + * XXX flow control should be set in comparam(), not here. + */ + outb(com->esp_port + ESP_CMD1, ESP_SETMODE); + outb(com->esp_port + ESP_CMD2, ESP_MODE_RTS | ESP_MODE_FIFO); + + /* Set RTS/CTS flow control. */ + outb(com->esp_port + ESP_CMD1, ESP_SETFLOWTYPE); + outb(com->esp_port + ESP_CMD2, ESP_FLOW_RTS); + outb(com->esp_port + ESP_CMD2, ESP_FLOW_CTS); + + /* Set flow-control levels. */ + outb(com->esp_port + ESP_CMD1, ESP_SETRXFLOW); + outb(com->esp_port + ESP_CMD2, HIBYTE(768)); + outb(com->esp_port + ESP_CMD2, LOBYTE(768)); + outb(com->esp_port + ESP_CMD2, HIBYTE(512)); + outb(com->esp_port + ESP_CMD2, LOBYTE(512)); + } +#endif /* COM_ESP */ + outb(iobase + com_fifo, 0); +determined_type: ; + +#ifdef COM_MULTIPORT + if (COM_ISMULTIPORT(isdp)) { + com->multiport = TRUE; + printf(" (multiport"); + if (unit == COM_MPMASTER(isdp)) + printf(" master"); + printf(")"); + com->no_irq = find_isadev(isa_devtab_tty, &siodriver, + COM_MPMASTER(isdp))->id_irq == 0; + } +#endif /* COM_MULTIPORT */ + printf("\n"); + + s = spltty(); + com_addr(unit) = com; + splx(s); + + dev = makedev(CDEV_MAJOR, 0); + cdevsw_add(&dev, &sio_cdevsw, NULL); +#ifdef DEVFS + com->devfs_token_ttyd = devfs_add_devswf(&sio_cdevsw, + unit, DV_CHR, + UID_ROOT, GID_WHEEL, 0600, "ttyd%n", unit); + com->devfs_token_ttyi = devfs_add_devswf(&sio_cdevsw, + unit | CONTROL_INIT_STATE, DV_CHR, + UID_ROOT, GID_WHEEL, 0600, "ttyid%n", unit); + com->devfs_token_ttyl = devfs_add_devswf(&sio_cdevsw, + unit | CONTROL_LOCK_STATE, DV_CHR, + UID_ROOT, GID_WHEEL, 0600, "ttyld%n", unit); + com->devfs_token_cuaa = devfs_add_devswf(&sio_cdevsw, + unit | CALLOUT_MASK, DV_CHR, + UID_UUCP, GID_DIALER, 0660, "cuaa%n", unit); + com->devfs_token_cuai = devfs_add_devswf(&sio_cdevsw, + unit | CALLOUT_MASK | CONTROL_INIT_STATE, DV_CHR, + UID_UUCP, GID_DIALER, 0660, "cuaia%n", unit); + com->devfs_token_cual = devfs_add_devswf(&sio_cdevsw, + unit | CALLOUT_MASK | CONTROL_LOCK_STATE, DV_CHR, + UID_UUCP, GID_DIALER, 0660, "cuala%n", unit); +#endif + return (1); +} + +static int +sioopen(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct com_s *com; + int error; + Port_t iobase; + int mynor; + int s; + struct tty *tp; + int unit; + + mynor = minor(dev); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIOTOT || (com = com_addr(unit)) == NULL) + return (ENXIO); + if (com->gone) + return (ENXIO); + if (mynor & CONTROL_MASK) + return (0); +#if 0 /* XXX */ + tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); +#else + tp = com->tp = &sio_tty[unit]; +#endif + s = spltty(); + /* + * We jump to this label after all non-interrupted sleeps to pick + * up any changes of the device state. + */ +open_top: + while (com->state & CS_DTR_OFF) { + error = tsleep(&com->dtr_wait, TTIPRI | PCATCH, "siodtr", 0); + if (com_addr(unit) == NULL) + return (ENXIO); + if (error != 0 || com->gone) + goto out; + } + if (tp->t_state & TS_ISOPEN) { + /* + * The device is open, so everything has been initialized. + * Handle conflicts. + */ + if (mynor & CALLOUT_MASK) { + if (!com->active_out) { + error = EBUSY; + goto out; + } + } else { + if (com->active_out) { + if (flag & O_NONBLOCK) { + error = EBUSY; + goto out; + } + error = tsleep(&com->active_out, + TTIPRI | PCATCH, "siobi", 0); + if (com_addr(unit) == NULL) + return (ENXIO); + if (error != 0 || com->gone) + goto out; + goto open_top; + } + } + if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + error = EBUSY; + goto out; + } + } else { + /* + * The device isn't open, so there are no conflicts. + * Initialize it. Initialization is done twice in many + * cases: to preempt sleeping callin opens if we are + * callout, and to complete a callin open after DCD rises. + */ + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + tp->t_termios = mynor & CALLOUT_MASK + ? com->it_out : com->it_in; + (void)commctl(com, TIOCM_DTR | TIOCM_RTS, DMSET); + com->poll = com->no_irq; + com->poll_output = com->loses_outints; + ++com->wopeners; + error = comparam(tp, &tp->t_termios); + --com->wopeners; + if (error != 0) + goto out; + /* + * XXX we should goto open_top if comparam() slept. + */ + ttsetwater(tp); + iobase = com->iobase; + if (com->hasfifo) { + /* + * (Re)enable and drain fifos. + * + * Certain SMC chips cause problems if the fifos + * are enabled while input is ready. Turn off the + * fifo if necessary to clear the input. We test + * the input ready bit after enabling the fifos + * since we've already enabled them in comparam() + * and to handle races between enabling and fresh + * input. + */ + while (TRUE) { + outb(iobase + com_fifo, + FIFO_RCV_RST | FIFO_XMT_RST + | com->fifo_image); + /* + * XXX the delays are for superstitious + * historical reasons. It must be less than + * the character time at the maximum + * supported speed (87 usec at 115200 bps + * 8N1). Otherwise we might loop endlessly + * if data is streaming in. We used to use + * delays of 100. That usually worked + * because DELAY(100) used to usually delay + * for about 85 usec instead of 100. + */ + DELAY(50); + if (!(inb(com->line_status_port) & LSR_RXRDY)) + break; + outb(iobase + com_fifo, 0); + DELAY(50); + (void) inb(com->data_port); + } + } + + disable_intr(); + (void) inb(com->line_status_port); + (void) inb(com->data_port); + com->prev_modem_status = com->last_modem_status + = inb(com->modem_status_port); + outb(iobase + com_ier, IER_ERXRDY | IER_ETXRDY | IER_ERLS + | IER_EMSC); + enable_intr(); + /* + * Handle initial DCD. Callout devices get a fake initial + * DCD (trapdoor DCD). If we are callout, then any sleeping + * callin opens get woken up and resume sleeping on "siobi" + * instead of "siodcd". + */ + /* + * XXX `mynor & CALLOUT_MASK' should be + * `tp->t_cflag & (SOFT_CARRIER | TRAPDOOR_CARRIER) where + * TRAPDOOR_CARRIER is the default initial state for callout + * devices and SOFT_CARRIER is like CLOCAL except it hides + * the true carrier. + */ + if (com->prev_modem_status & MSR_DCD || mynor & CALLOUT_MASK) + (*linesw[tp->t_line].l_modem)(tp, 1); + } + /* + * Wait for DCD if necessary. + */ + if (!(tp->t_state & TS_CARR_ON) && !(mynor & CALLOUT_MASK) + && !(tp->t_cflag & CLOCAL) && !(flag & O_NONBLOCK)) { + ++com->wopeners; + error = tsleep(TSA_CARR_ON(tp), TTIPRI | PCATCH, "siodcd", 0); + if (com_addr(unit) == NULL) + return (ENXIO); + --com->wopeners; + if (error != 0 || com->gone) + goto out; + goto open_top; + } + error = (*linesw[tp->t_line].l_open)(dev, tp); + disc_optim(tp, &tp->t_termios, com); + if (tp->t_state & TS_ISOPEN && mynor & CALLOUT_MASK) + com->active_out = TRUE; + siosettimeout(); +out: + splx(s); + if (!(tp->t_state & TS_ISOPEN) && com->wopeners == 0) + comhardclose(com); + return (error); +} + +static int +sioclose(dev, flag, mode, p) + dev_t dev; + int flag; + int mode; + struct proc *p; +{ + struct com_s *com; + int mynor; + int s; + struct tty *tp; + + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (0); + com = com_addr(MINOR_TO_UNIT(mynor)); + tp = com->tp; + s = spltty(); + (*linesw[tp->t_line].l_close)(tp, flag); + disc_optim(tp, &tp->t_termios, com); + siostop(tp, FREAD | FWRITE); + comhardclose(com); + ttyclose(tp); + siosettimeout(); + splx(s); + if (com->gone) { + printf("sio%d: gone\n", com->unit); + s = spltty(); + com_addr(com->unit) = 0; + bzero(tp,sizeof *tp); + bzero(com,sizeof *com); + free(com,M_TTYS); + splx(s); + } + return (0); +} + +static void +comhardclose(com) + struct com_s *com; +{ + Port_t iobase; + int s; + struct tty *tp; + int unit; + + unit = com->unit; + iobase = com->iobase; + s = spltty(); + com->poll = FALSE; + com->poll_output = FALSE; + com->do_timestamp = FALSE; + com->do_dcd_timestamp = FALSE; + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); + { + outb(iobase + com_ier, 0); + tp = com->tp; + if (tp->t_cflag & HUPCL + /* + * XXX we will miss any carrier drop between here and the + * next open. Perhaps we should watch DCD even when the + * port is closed; it is not sufficient to check it at + * the next open because it might go up and down while + * we're not watching. + */ + || !com->active_out + && !(com->prev_modem_status & MSR_DCD) + && !(com->it_in.c_cflag & CLOCAL) + || !(tp->t_state & TS_ISOPEN)) { + (void)commctl(com, TIOCM_DTR, DMBIC); + if (com->dtr_wait != 0 && !(com->state & CS_DTR_OFF)) { + timeout(siodtrwakeup, com, com->dtr_wait); + com->state |= CS_DTR_OFF; + } + } + } + if (com->hasfifo) { + /* + * Disable fifos so that they are off after controlled + * reboots. Some BIOSes fail to detect 16550s when the + * fifos are enabled. + */ + outb(iobase + com_fifo, 0); + } + com->active_out = FALSE; + wakeup(&com->active_out); + wakeup(TSA_CARR_ON(tp)); /* restart any wopeners */ + splx(s); +} + +static int +sioread(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int mynor; + int unit; + struct tty *tp; + + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + unit = MINOR_TO_UNIT(mynor); + if (com_addr(unit)->gone) + return (ENODEV); + tp = com_addr(unit)->tp; + return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); +} + +static int +siowrite(dev, uio, flag) + dev_t dev; + struct uio *uio; + int flag; +{ + int mynor; + struct tty *tp; + int unit; + + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (ENODEV); + + unit = MINOR_TO_UNIT(mynor); + if (com_addr(unit)->gone) + return (ENODEV); + tp = com_addr(unit)->tp; + /* + * (XXX) We disallow virtual consoles if the physical console is + * a serial port. This is in case there is a display attached that + * is not the console. In that situation we don't need/want the X + * server taking over the console. + */ + if (constty != NULL && unit == comconsole) + constty = NULL; + return ((*linesw[tp->t_line].l_write)(tp, uio, flag)); +} + +static void +siobusycheck(chan) + void *chan; +{ + struct com_s *com; + int s; + + com = (struct com_s *)chan; + + /* + * Clear TS_BUSY if low-level output is complete. + * spl locking is sufficient because siointr1() does not set CS_BUSY. + * If siointr1() clears CS_BUSY after we look at it, then we'll get + * called again. Reading the line status port outside of siointr1() + * is safe because CS_BUSY is clear so there are no output interrupts + * to lose. + */ + s = spltty(); + if (com->state & CS_BUSY) + com->extra_state &= ~CSE_BUSYCHECK; /* False alarm. */ + else if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) + == (LSR_TSRE | LSR_TXRDY)) { + com->tp->t_state &= ~TS_BUSY; + ttwwakeup(com->tp); + com->extra_state &= ~CSE_BUSYCHECK; + } else + timeout(siobusycheck, com, hz / 100); + splx(s); +} + +static void +siodtrwakeup(chan) + void *chan; +{ + struct com_s *com; + + com = (struct com_s *)chan; + com->state &= ~CS_DTR_OFF; + wakeup(&com->dtr_wait); +} + +void +siointr(unit) + int unit; +{ +#ifndef COM_MULTIPORT + siointr1(com_addr(unit)); +#else /* COM_MULTIPORT */ + struct com_s *com; + bool_t possibly_more_intrs; + + /* + * Loop until there is no activity on any port. This is necessary + * to get an interrupt edge more than to avoid another interrupt. + * If the IRQ signal is just an OR of the IRQ signals from several + * devices, then the edge from one may be lost because another is + * on. + */ + do { + possibly_more_intrs = FALSE; + for (unit = 0; unit < NSIOTOT; ++unit) { + com = com_addr(unit); + if (com != NULL + && !com->gone + && (inb(com->int_id_port) & IIR_IMASK) + != IIR_NOPEND) { + siointr1(com); + possibly_more_intrs = TRUE; + } + } + } while (possibly_more_intrs); +#endif /* COM_MULTIPORT */ +} + +static void +siointr1(com) + struct com_s *com; +{ + u_char line_status; + u_char modem_status; + u_char *ioptr; + u_char recv_data; + + while (TRUE) { + line_status = inb(com->line_status_port); + +#if NCARD > 0 + /* + * Some PC-cards trigger an interrupt during removal. + * Reads on a removed card return 0xff, which forces + * this interrupt handler into a loop. + * Torsten Kohler + */ + + if (line_status == 0xff) { + log(LOG_ERR, + "sio: strange line status - PC-card removed?\n"); + return; + } +#endif /* NCARD > 0 */ + + /* input event? (check first to help avoid overruns) */ + while (line_status & LSR_RCV_MASK) { + /* break/unnattached error bits or real input? */ + if (!(line_status & LSR_RXRDY)) + recv_data = 0; + else + recv_data = inb(com->data_port); + if (line_status & (LSR_BI | LSR_FE | LSR_PE)) { + /* + * Don't store BI if IGNBRK or FE/PE if IGNPAR. + * Otherwise, push the work to a higher level + * (to handle PARMRK) if we're bypassing. + * Otherwise, convert BI/FE and PE+INPCK to 0. + * + * This makes bypassing work right in the + * usual "raw" case (IGNBRK set, and IGNPAR + * and INPCK clear). + * + * Note: BI together with FE/PE means just BI. + */ + if (line_status & LSR_BI) { +#if defined(DDB) && defined(BREAK_TO_DEBUGGER) + if (com->unit == comconsole) { + breakpoint(); + goto cont; + } +#endif + if (com->tp == NULL + || com->tp->t_iflag & IGNBRK) + goto cont; + } else { + if (com->tp == NULL + || com->tp->t_iflag & IGNPAR) + goto cont; + } + if (com->tp->t_state & TS_CAN_BYPASS_L_RINT + && (line_status & (LSR_BI | LSR_FE) + || com->tp->t_iflag & INPCK)) + recv_data = 0; + } + ++com->bytes_in; + if (com->hotchar != 0 && recv_data == com->hotchar) + setsofttty(); + ioptr = com->iptr; + if (ioptr >= com->ibufend) + CE_RECORD(com, CE_INTERRUPT_BUF_OVERFLOW); + else { + if (com->do_timestamp) + microtime(&com->timestamp); + ++com_events; + schedsofttty(); +#if 0 /* for testing input latency vs efficiency */ +if (com->iptr - com->ibuf == 8) + setsofttty(); +#endif + ioptr[0] = recv_data; + ioptr[CE_INPUT_OFFSET] = line_status; + com->iptr = ++ioptr; + if (ioptr == com->ihighwater + && com->state & CS_RTS_IFLOW) + outb(com->modem_ctl_port, + com->mcr_image &= ~MCR_RTS); + if (line_status & LSR_OE) + CE_RECORD(com, CE_OVERRUN); + } +cont: + /* + * "& 0x7F" is to avoid the gcc-1.40 generating a slow + * jump from the top of the loop to here + */ + line_status = inb(com->line_status_port) & 0x7F; + } + + /* modem status change? (always check before doing output) */ + modem_status = inb(com->modem_status_port); + if (modem_status != com->last_modem_status) { + if (com->do_dcd_timestamp + && !(com->last_modem_status & MSR_DCD) + && modem_status & MSR_DCD) + microtime(&com->dcd_timestamp); + + /* + * Schedule high level to handle DCD changes. Note + * that we don't use the delta bits anywhere. Some + * UARTs mess them up, and it's easy to remember the + * previous bits and calculate the delta. + */ + com->last_modem_status = modem_status; + if (!(com->state & CS_CHECKMSR)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_CHECKMSR; + setsofttty(); + } + + /* handle CTS change immediately for crisp flow ctl */ + if (com->state & CS_CTS_OFLOW) { + if (modem_status & MSR_CTS) + com->state |= CS_ODEVREADY; + else + com->state &= ~CS_ODEVREADY; + } + } + + /* output queued and everything ready? */ + if (line_status & LSR_TXRDY + && com->state >= (CS_BUSY | CS_TTGO | CS_ODEVREADY)) { + ioptr = com->obufq.l_head; + if (com->tx_fifo_size > 1) { + u_int ocount; + + ocount = com->obufq.l_tail - ioptr; + if (ocount > com->tx_fifo_size) + ocount = com->tx_fifo_size; + com->bytes_out += ocount; + do + outb(com->data_port, *ioptr++); + while (--ocount != 0); + } else { + outb(com->data_port, *ioptr++); + ++com->bytes_out; + } + com->obufq.l_head = ioptr; + if (ioptr >= com->obufq.l_tail) { + struct lbq *qp; + + qp = com->obufq.l_next; + qp->l_queued = FALSE; + qp = qp->l_next; + if (qp != NULL) { + com->obufq.l_head = qp->l_head; + com->obufq.l_tail = qp->l_tail; + com->obufq.l_next = qp; + } else { + /* output just completed */ + com->state &= ~CS_BUSY; + } + if (!(com->state & CS_ODONE)) { + com_events += LOTS_OF_EVENTS; + com->state |= CS_ODONE; + setsofttty(); /* handle at high level ASAP */ + } + } + } + + /* finished? */ +#ifndef COM_MULTIPORT + if ((inb(com->int_id_port) & IIR_IMASK) == IIR_NOPEND) +#endif /* COM_MULTIPORT */ + return; + } +} + +static int +sioioctl(dev, cmd, data, flag, p) + dev_t dev; + int cmd; + caddr_t data; + int flag; + struct proc *p; +{ + struct com_s *com; + int error; + Port_t iobase; + int mynor; + int s; + struct tty *tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + int oldcmd; + struct termios term; +#endif + + mynor = minor(dev); + com = com_addr(MINOR_TO_UNIT(mynor)); + if (com->gone) + return (ENODEV); + iobase = com->iobase; + if (mynor & CONTROL_MASK) { + struct termios *ct; + + switch (mynor & CONTROL_MASK) { + case CONTROL_INIT_STATE: + ct = mynor & CALLOUT_MASK ? &com->it_out : &com->it_in; + break; + case CONTROL_LOCK_STATE: + ct = mynor & CALLOUT_MASK ? &com->lt_out : &com->lt_in; + break; + default: + return (ENODEV); /* /dev/nodev */ + } + switch (cmd) { + case TIOCSETA: + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) + return (error); + *ct = *(struct termios *)data; + return (0); + case TIOCGETA: + *(struct termios *)data = *ct; + return (0); + case TIOCGETD: + *(int *)data = TTYDISC; + return (0); + case TIOCGWINSZ: + bzero(data, sizeof(struct winsize)); + return (0); +#ifdef DSI_SOFT_MODEM + /* + * Download micro-code to Digicom modem. + */ + case TIOCDSIMICROCODE: + { + u_long l; + u_char *p,*pi; + + pi = (u_char*)(*(caddr_t*)data); + error = copyin(pi,&l,sizeof l); + if(error) + {return error;}; + pi += sizeof l; + + p = malloc(l,M_TEMP,M_NOWAIT); + if(!p) + {return ENOBUFS;} + error = copyin(pi,p,l); + if(error) + {free(p,M_TEMP); return error;}; + if(error = LoadSoftModem( + MINOR_TO_UNIT(mynor),iobase,l,p)) + {free(p,M_TEMP); return error;} + free(p,M_TEMP); + return(0); + } +#endif /* DSI_SOFT_MODEM */ + default: + return (ENOTTY); + } + } + tp = com->tp; +#if defined(COMPAT_43) || defined(COMPAT_SUNOS) + term = tp->t_termios; + oldcmd = cmd; + error = ttsetcompat(tp, &cmd, data, &term); + if (error != 0) + return (error); + if (cmd != oldcmd) + data = (caddr_t)&term; +#endif + if (cmd == TIOCSETA || cmd == TIOCSETAW || cmd == TIOCSETAF) { + int cc; + struct termios *dt = (struct termios *)data; + struct termios *lt = mynor & CALLOUT_MASK + ? &com->lt_out : &com->lt_in; + + dt->c_iflag = (tp->t_iflag & lt->c_iflag) + | (dt->c_iflag & ~lt->c_iflag); + dt->c_oflag = (tp->t_oflag & lt->c_oflag) + | (dt->c_oflag & ~lt->c_oflag); + dt->c_cflag = (tp->t_cflag & lt->c_cflag) + | (dt->c_cflag & ~lt->c_cflag); + dt->c_lflag = (tp->t_lflag & lt->c_lflag) + | (dt->c_lflag & ~lt->c_lflag); + for (cc = 0; cc < NCCS; ++cc) + if (lt->c_cc[cc] != 0) + dt->c_cc[cc] = tp->t_cc[cc]; + if (lt->c_ispeed != 0) + dt->c_ispeed = tp->t_ispeed; + if (lt->c_ospeed != 0) + dt->c_ospeed = tp->t_ospeed; + } + error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p); + if (error >= 0) + return (error); + s = spltty(); + error = ttioctl(tp, cmd, data, flag); + disc_optim(tp, &tp->t_termios, com); + if (error >= 0) { + splx(s); + return (error); + } + switch (cmd) { + case TIOCSBRK: + outb(iobase + com_cfcr, com->cfcr_image |= CFCR_SBREAK); + break; + case TIOCCBRK: + outb(iobase + com_cfcr, com->cfcr_image &= ~CFCR_SBREAK); + break; + case TIOCSDTR: + (void)commctl(com, TIOCM_DTR, DMBIS); + break; + case TIOCCDTR: + (void)commctl(com, TIOCM_DTR, DMBIC); + break; + /* + * XXX should disallow changing MCR_RTS if CS_RTS_IFLOW is set. The + * changes get undone on the next call to comparam(). + */ + case TIOCMSET: + (void)commctl(com, *(int *)data, DMSET); + break; + case TIOCMBIS: + (void)commctl(com, *(int *)data, DMBIS); + break; + case TIOCMBIC: + (void)commctl(com, *(int *)data, DMBIC); + break; + case TIOCMGET: + *(int *)data = commctl(com, 0, DMGET); + break; + case TIOCMSDTRWAIT: + /* must be root since the wait applies to following logins */ + error = suser(p->p_ucred, &p->p_acflag); + if (error != 0) { + splx(s); + return (error); + } + com->dtr_wait = *(int *)data * hz / 100; + break; + case TIOCMGDTRWAIT: + *(int *)data = com->dtr_wait * 100 / hz; + break; + case TIOCTIMESTAMP: + com->do_timestamp = TRUE; + *(struct timeval *)data = com->timestamp; + break; + case TIOCDCDTIMESTAMP: + com->do_dcd_timestamp = TRUE; + *(struct timeval *)data = com->dcd_timestamp; + break; + default: + splx(s); + return (ENOTTY); + } + splx(s); + if (com->gone) { + printf("sio%d: gone\n", com->unit); + s = spltty(); + com_addr(com->unit) = 0; + bzero(tp,sizeof *tp); + bzero(com,sizeof *com); + free(com,M_TTYS); + splx(s); + } + return (0); +} + +void +siopoll() +{ + int unit; + + if (com_events == 0) + return; +repeat: + for (unit = 0; unit < NSIOTOT; ++unit) { + u_char *buf; + struct com_s *com; + u_char *ibuf; + int incc; + struct tty *tp; + + com = com_addr(unit); + if (com == NULL) + continue; + if (com->gone) + continue; + tp = com->tp; + if (tp == NULL) { + /* + * XXX forget any events related to closed devices + * (actually never opened devices) so that we don't + * loop. + */ + disable_intr(); + incc = com->iptr - com->ibuf; + com->iptr = com->ibuf; + if (com->state & CS_CHECKMSR) { + incc += LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + } + com_events -= incc; + enable_intr(); + if (incc != 0) + log(LOG_DEBUG, + "sio%d: %d events for device with no tp\n", + unit, incc); + continue; + } + + /* switch the role of the low-level input buffers */ + if (com->iptr == (ibuf = com->ibuf)) { + buf = NULL; /* not used, but compiler can't tell */ + incc = 0; + } else { + buf = ibuf; + disable_intr(); + incc = com->iptr - buf; + com_events -= incc; + if (ibuf == com->ibuf1) + ibuf = com->ibuf2; + else + ibuf = com->ibuf1; + com->ibufend = ibuf + RS_IBUFSIZE; + com->ihighwater = ibuf + RS_IHIGHWATER; + com->iptr = ibuf; + + /* + * There is now room for another low-level buffer full + * of input, so enable RTS if it is now disabled and + * there is room in the high-level buffer. + */ + if ((com->state & CS_RTS_IFLOW) + && !(com->mcr_image & MCR_RTS) + && !(tp->t_state & TS_TBLOCK)) + outb(com->modem_ctl_port, + com->mcr_image |= MCR_RTS); + enable_intr(); + com->ibuf = ibuf; + } + + if (com->state & CS_CHECKMSR) { + u_char delta_modem_status; + + disable_intr(); + delta_modem_status = com->last_modem_status + ^ com->prev_modem_status; + com->prev_modem_status = com->last_modem_status; + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_CHECKMSR; + enable_intr(); + if (delta_modem_status & MSR_DCD) + (*linesw[tp->t_line].l_modem) + (tp, com->prev_modem_status & MSR_DCD); + } + if (com->state & CS_ODONE) { + disable_intr(); + com_events -= LOTS_OF_EVENTS; + com->state &= ~CS_ODONE; + enable_intr(); + if (!(com->state & CS_BUSY) + && !(com->extra_state & CSE_BUSYCHECK)) { + timeout(siobusycheck, com, hz / 100); + com->extra_state |= CSE_BUSYCHECK; + } + (*linesw[tp->t_line].l_start)(tp); + } + if (incc <= 0 || !(tp->t_state & TS_ISOPEN) || + !(tp->t_cflag & CREAD)) + continue; + /* + * Avoid the grotesquely inefficient lineswitch routine + * (ttyinput) in "raw" mode. It usually takes about 450 + * instructions (that's without canonical processing or echo!). + * slinput is reasonably fast (usually 40 instructions plus + * call overhead). + */ + if (tp->t_state & TS_CAN_BYPASS_L_RINT) { + if (tp->t_rawq.c_cc + incc >= RB_I_HIGH_WATER + && (com->state & CS_RTS_IFLOW + || tp->t_iflag & IXOFF) + && !(tp->t_state & TS_TBLOCK)) + ttyblock(tp); + tk_nin += incc; + tk_rawcc += incc; + tp->t_rawcc += incc; + com->delta_error_counts[CE_TTY_BUF_OVERFLOW] + += b_to_q((char *)buf, incc, &tp->t_rawq); + ttwakeup(tp); + if (tp->t_state & TS_TTSTOP + && (tp->t_iflag & IXANY + || tp->t_cc[VSTART] == tp->t_cc[VSTOP])) { + tp->t_state &= ~TS_TTSTOP; + tp->t_lflag &= ~FLUSHO; + comstart(tp); + } + } else { + do { + u_char line_status; + int recv_data; + + line_status = (u_char) buf[CE_INPUT_OFFSET]; + recv_data = (u_char) *buf++; + if (line_status + & (LSR_BI | LSR_FE | LSR_OE | LSR_PE)) { + if (line_status & LSR_BI) + recv_data |= TTY_BI; + if (line_status & LSR_FE) + recv_data |= TTY_FE; + if (line_status & LSR_OE) + recv_data |= TTY_OE; + if (line_status & LSR_PE) + recv_data |= TTY_PE; + } + (*linesw[tp->t_line].l_rint)(recv_data, tp); + } while (--incc > 0); + } + if (com_events == 0) + break; + } + if (com_events >= LOTS_OF_EVENTS) + goto repeat; +} + +static int +comparam(tp, t) + struct tty *tp; + struct termios *t; +{ + u_int cfcr; + int cflag; + struct com_s *com; + int divisor; + u_char dlbh; + u_char dlbl; + int error; + Port_t iobase; + int s; + int unit; + int txtimeout; + + /* do historical conversions */ + if (t->c_ispeed == 0) + t->c_ispeed = t->c_ospeed; + + /* check requested parameters */ + divisor = ttspeedtab(t->c_ospeed, comspeedtab); + if (divisor < 0 || divisor > 0 && t->c_ispeed != t->c_ospeed) + return (EINVAL); + + /* parameters are OK, convert them to the com struct and the device */ + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + iobase = com->iobase; + s = spltty(); + if (divisor == 0) + (void)commctl(com, TIOCM_DTR, DMBIC); /* hang up line */ + else + (void)commctl(com, TIOCM_DTR, DMBIS); + cflag = t->c_cflag; + switch (cflag & CSIZE) { + case CS5: + cfcr = CFCR_5BITS; + break; + case CS6: + cfcr = CFCR_6BITS; + break; + case CS7: + cfcr = CFCR_7BITS; + break; + default: + cfcr = CFCR_8BITS; + break; + } + if (cflag & PARENB) { + cfcr |= CFCR_PENAB; + if (!(cflag & PARODD)) + cfcr |= CFCR_PEVEN; + } + if (cflag & CSTOPB) + cfcr |= CFCR_STOPB; + + if (com->hasfifo) { + /* + * Use a fifo trigger level low enough so that the input + * latency from the fifo is less than about 16 msec and + * the total latency is less than about 30 msec. These + * latencies are reasonable for humans. Serial comms + * protocols shouldn't expect anything better since modem + * latencies are larger. + */ + com->fifo_image = t->c_ospeed <= 4800 + ? FIFO_ENABLE : FIFO_ENABLE | FIFO_RX_HIGH; +#ifdef COM_ESP + /* + * The Hayes ESP card needs the fifo DMA mode bit set + * in compatibility mode. If not, it will interrupt + * for each character received. + */ + if (com->esp) + com->fifo_image |= FIFO_DMA_MODE; +#endif + outb(iobase + com_fifo, com->fifo_image); + } + + /* + * Some UARTs lock up if the divisor latch registers are selected + * while the UART is doing output (they refuse to transmit anything + * more until given a hard reset). Fix this by stopping filling + * the device buffers and waiting for them to drain. Reading the + * line status port outside of siointr1() might lose some receiver + * error bits, but that is acceptable here. + */ + disable_intr(); +retry: + com->state &= ~CS_TTGO; + txtimeout = tp->t_timeout; + enable_intr(); + while ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY)) { + tp->t_state |= TS_SO_OCOMPLETE; + error = ttysleep(tp, TSA_OCOMPLETE(tp), TTIPRI | PCATCH, + "siotx", hz / 100); + if ( txtimeout != 0 + && (!error || error == EAGAIN) + && (txtimeout -= hz / 100) <= 0 + ) + error = EIO; + if (com->gone) + error = ENODEV; + if (error != 0 && error != EAGAIN) { + if (!(tp->t_state & TS_TTSTOP)) { + disable_intr(); + com->state |= CS_TTGO; + enable_intr(); + } + splx(s); + return (error); + } + } + + disable_intr(); /* very important while com_data is hidden */ + + /* + * XXX - clearing CS_TTGO is not sufficient to stop further output, + * because siopoll() calls comstart() which usually sets it again + * because TS_TTSTOP is clear. Setting TS_TTSTOP would not be + * sufficient, for similar reasons. + */ + if ((inb(com->line_status_port) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY)) + goto retry; + + if (divisor != 0) { + outb(iobase + com_cfcr, cfcr | CFCR_DLAB); + /* + * Only set the divisor registers if they would change, + * since on some 16550 incompatibles (UMC8669F), setting + * them while input is arriving them loses sync until + * data stops arriving. + */ + dlbl = divisor & 0xFF; + if (inb(iobase + com_dlbl) != dlbl) + outb(iobase + com_dlbl, dlbl); + dlbh = (u_int) divisor >> 8; + if (inb(iobase + com_dlbh) != dlbh) + outb(iobase + com_dlbh, dlbh); + } + outb(iobase + com_cfcr, com->cfcr_image = cfcr); + if (!(tp->t_state & TS_TTSTOP)) + com->state |= CS_TTGO; + if (cflag & CRTS_IFLOW) { + com->state |= CS_RTS_IFLOW; + /* + * If CS_RTS_IFLOW just changed from off to on, the change + * needs to be propagated to MCR_RTS. This isn't urgent, + * so do it later by calling comstart() instead of repeating + * a lot of code from comstart() here. + */ + } else if (com->state & CS_RTS_IFLOW) { + com->state &= ~CS_RTS_IFLOW; + /* + * CS_RTS_IFLOW just changed from on to off. Force MCR_RTS + * on here, since comstart() won't do it later. + */ + outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); + } + + /* + * Set up state to handle output flow control. + * XXX - worth handling MDMBUF (DCD) flow control at the lowest level? + * Now has 10+ msec latency, while CTS flow has 50- usec latency. + */ + com->state |= CS_ODEVREADY; + com->state &= ~CS_CTS_OFLOW; + if (cflag & CCTS_OFLOW) { + com->state |= CS_CTS_OFLOW; + if (!(com->last_modem_status & MSR_CTS)) + com->state &= ~CS_ODEVREADY; + } + /* XXX shouldn't call functions while intrs are disabled. */ + disc_optim(tp, t, com); + /* + * Recover from fiddling with CS_TTGO. We used to call siointr1() + * unconditionally, but that defeated the careful discarding of + * stale input in sioopen(). + */ + if (com->state >= (CS_BUSY | CS_TTGO)) + siointr1(com); + + enable_intr(); + splx(s); + comstart(tp); + return (0); +} + +static void +comstart(tp) + struct tty *tp; +{ + struct com_s *com; + int s; + int unit; + + unit = DEV_TO_UNIT(tp->t_dev); + com = com_addr(unit); + s = spltty(); + disable_intr(); + if (tp->t_state & TS_TTSTOP) + com->state &= ~CS_TTGO; + else + com->state |= CS_TTGO; + if (tp->t_state & TS_TBLOCK) { + if (com->mcr_image & MCR_RTS && com->state & CS_RTS_IFLOW) + outb(com->modem_ctl_port, com->mcr_image &= ~MCR_RTS); + } else { + if (!(com->mcr_image & MCR_RTS) && com->iptr < com->ihighwater + && com->state & CS_RTS_IFLOW) + outb(com->modem_ctl_port, com->mcr_image |= MCR_RTS); + } + enable_intr(); + if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) { + splx(s); + return; + } + if (tp->t_outq.c_cc != 0) { + struct lbq *qp; + struct lbq *next; + + if (!com->obufs[0].l_queued) { + com->obufs[0].l_tail + = com->obuf1 + q_to_b(&tp->t_outq, com->obuf1, + sizeof com->obuf1); + com->obufs[0].l_next = NULL; + com->obufs[0].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[0]; + } else { + com->obufq.l_head = com->obufs[0].l_head; + com->obufq.l_tail = com->obufs[0].l_tail; + com->obufq.l_next = &com->obufs[0]; + com->state |= CS_BUSY; + } + enable_intr(); + } + if (tp->t_outq.c_cc != 0 && !com->obufs[1].l_queued) { + com->obufs[1].l_tail + = com->obuf2 + q_to_b(&tp->t_outq, com->obuf2, + sizeof com->obuf2); + com->obufs[1].l_next = NULL; + com->obufs[1].l_queued = TRUE; + disable_intr(); + if (com->state & CS_BUSY) { + qp = com->obufq.l_next; + while ((next = qp->l_next) != NULL) + qp = next; + qp->l_next = &com->obufs[1]; + } else { + com->obufq.l_head = com->obufs[1].l_head; + com->obufq.l_tail = com->obufs[1].l_tail; + com->obufq.l_next = &com->obufs[1]; + com->state |= CS_BUSY; + } + enable_intr(); + } + tp->t_state |= TS_BUSY; + } + disable_intr(); + if (com->state >= (CS_BUSY | CS_TTGO)) + siointr1(com); /* fake interrupt to start output */ + enable_intr(); + ttwwakeup(tp); + splx(s); +} + +static void +siostop(tp, rw) + struct tty *tp; + int rw; +{ + struct com_s *com; + + com = com_addr(DEV_TO_UNIT(tp->t_dev)); + if (com->gone) + return; + disable_intr(); + if (rw & FWRITE) { +#ifdef COM_ESP_BUG_FIXED + if (com->hasfifo) +#ifdef COM_ESP + /* XXX avoid h/w bug. */ + if (!com->esp) +#endif + /* XXX does this flush everything? */ + outb(com->iobase + com_fifo, + FIFO_XMT_RST | com->fifo_image); +#endif + com->obufs[0].l_queued = FALSE; + com->obufs[1].l_queued = FALSE; + if (com->state & CS_ODONE) + com_events -= LOTS_OF_EVENTS; + com->state &= ~(CS_ODONE | CS_BUSY); + com->tp->t_state &= ~TS_BUSY; + } + if (rw & FREAD) { +#ifdef COM_ESP_BUG_FIXED + if (com->hasfifo) +#ifdef COM_ESP + /* XXX avoid h/w bug. */ + if (!com->esp) +#endif + /* XXX does this flush everything? */ + outb(com->iobase + com_fifo, + FIFO_RCV_RST | com->fifo_image); +#endif + com_events -= (com->iptr - com->ibuf); + com->iptr = com->ibuf; + } + enable_intr(); + comstart(tp); +} + +static struct tty * +siodevtotty(dev) + dev_t dev; +{ + int mynor; + int unit; + + mynor = minor(dev); + if (mynor & CONTROL_MASK) + return (NULL); + unit = MINOR_TO_UNIT(mynor); + if ((u_int) unit >= NSIOTOT) + return (NULL); + return (&sio_tty[unit]); +} + +static int +commctl(com, bits, how) + struct com_s *com; + int bits; + int how; +{ + int mcr; + int msr; + + if (how == DMGET) { + bits = TIOCM_LE; /* XXX - always enabled while open */ + mcr = com->mcr_image; + if (mcr & MCR_DTR) + bits |= TIOCM_DTR; + if (mcr & MCR_RTS) + bits |= TIOCM_RTS; + msr = com->prev_modem_status; + if (msr & MSR_CTS) + bits |= TIOCM_CTS; + if (msr & MSR_DCD) + bits |= TIOCM_CD; + if (msr & MSR_DSR) + bits |= TIOCM_DSR; + /* + * XXX - MSR_RI is naturally volatile, and we make MSR_TERI + * more volatile by reading the modem status a lot. Perhaps + * we should latch both bits until the status is read here. + */ + if (msr & (MSR_RI | MSR_TERI)) + bits |= TIOCM_RI; + return (bits); + } + mcr = 0; + if (bits & TIOCM_DTR) + mcr |= MCR_DTR; + if (bits & TIOCM_RTS) + mcr |= MCR_RTS; + if (com->gone) + return(0); + disable_intr(); + switch (how) { + case DMSET: + outb(com->modem_ctl_port, + com->mcr_image = mcr | (com->mcr_image & MCR_IENABLE)); + break; + case DMBIS: + outb(com->modem_ctl_port, com->mcr_image |= mcr); + break; + case DMBIC: + outb(com->modem_ctl_port, com->mcr_image &= ~mcr); + break; + } + enable_intr(); + return (0); +} + +static void +siosettimeout() +{ + struct com_s *com; + bool_t someopen; + int unit; + + /* + * Set our timeout period to 1 second if no polled devices are open. + * Otherwise set it to max(1/200, 1/hz). + * Enable timeouts iff some device is open. + */ + untimeout(comwakeup, (void *)NULL); + sio_timeout = hz; + someopen = FALSE; + for (unit = 0; unit < NSIOTOT; ++unit) { + com = com_addr(unit); + if (com != NULL && com->tp != NULL + && com->tp->t_state & TS_ISOPEN && !com->gone) { + someopen = TRUE; + if (com->poll || com->poll_output) { + sio_timeout = hz > 200 ? hz / 200 : 1; + break; + } + } + } + if (someopen) { + sio_timeouts_until_log = hz / sio_timeout; + timeout(comwakeup, (void *)NULL, sio_timeout); + } else { + /* Flush error messages, if any. */ + sio_timeouts_until_log = 1; + comwakeup((void *)NULL); + untimeout(comwakeup, (void *)NULL); + } +} + +static void +comwakeup(chan) + void *chan; +{ + struct com_s *com; + int unit; + + timeout(comwakeup, (void *)NULL, sio_timeout); + + /* + * Recover from lost output interrupts. + * Poll any lines that don't use interrupts. + */ + for (unit = 0; unit < NSIOTOT; ++unit) { + com = com_addr(unit); + if (com != NULL && !com->gone + && (com->state >= (CS_BUSY | CS_TTGO) || com->poll)) { + disable_intr(); + siointr1(com); + enable_intr(); + } + } + + /* + * Check for and log errors, but not too often. + */ + if (--sio_timeouts_until_log > 0) + return; + sio_timeouts_until_log = hz / sio_timeout; + for (unit = 0; unit < NSIOTOT; ++unit) { + int errnum; + + com = com_addr(unit); + if (com == NULL) + continue; + if (com->gone) + continue; + for (errnum = 0; errnum < CE_NTYPES; ++errnum) { + u_int delta; + u_long total; + + disable_intr(); + delta = com->delta_error_counts[errnum]; + com->delta_error_counts[errnum] = 0; + enable_intr(); + if (delta == 0) + continue; + total = com->error_counts[errnum] += delta; + log(LOG_ERR, "sio%d: %u more %s%s (total %lu)\n", + unit, delta, error_desc[errnum], + delta == 1 ? "" : "s", total); + } + } +} + +static void +disc_optim(tp, t, com) + struct tty *tp; + struct termios *t; + struct com_s *com; +{ + if (!(t->c_iflag & (ICRNL | IGNCR | IMAXBEL | INLCR | ISTRIP | IXON)) + && (!(t->c_iflag & BRKINT) || (t->c_iflag & IGNBRK)) + && (!(t->c_iflag & PARMRK) + || (t->c_iflag & (IGNPAR | IGNBRK)) == (IGNPAR | IGNBRK)) + && !(t->c_lflag & (ECHO | ICANON | IEXTEN | ISIG | PENDIN)) + && linesw[tp->t_line].l_rint == ttyinput) + tp->t_state |= TS_CAN_BYPASS_L_RINT; + else + tp->t_state &= ~TS_CAN_BYPASS_L_RINT; + /* + * Prepare to reduce input latency for packet + * discplines with a end of packet character. + */ + if (tp->t_line == SLIPDISC) + com->hotchar = 0xc0; + else if (tp->t_line == PPPDISC) + com->hotchar = 0x7e; + else + com->hotchar = 0; +} + +/* + * Following are all routines needed for SIO to act as console + */ +#include + +struct siocnstate { + u_char dlbl; + u_char dlbh; + u_char ier; + u_char cfcr; + u_char mcr; +}; + +static Port_t siocniobase; + +static void siocnclose __P((struct siocnstate *sp)); +static void siocnopen __P((struct siocnstate *sp)); +static void siocntxwait __P((void)); + +static void +siocntxwait() +{ + int timo; + + /* + * Wait for any pending transmission to finish. Required to avoid + * the UART lockup bug when the speed is changed, and for normal + * transmits. + */ + timo = 100000; + while ((inb(siocniobase + com_lsr) & (LSR_TSRE | LSR_TXRDY)) + != (LSR_TSRE | LSR_TXRDY) && --timo != 0) + ; +} + +static void +siocnopen(sp) + struct siocnstate *sp; +{ + int divisor; + u_char dlbh; + u_char dlbl; + Port_t iobase; + + /* + * Save all the device control registers except the fifo register + * and set our default ones (cs8 -parenb speed=comdefaultrate). + * We can't save the fifo register since it is read-only. + */ + iobase = siocniobase; + sp->ier = inb(iobase + com_ier); + outb(iobase + com_ier, 0); /* spltty() doesn't stop siointr() */ + siocntxwait(); + sp->cfcr = inb(iobase + com_cfcr); + outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); + sp->dlbl = inb(iobase + com_dlbl); + sp->dlbh = inb(iobase + com_dlbh); + /* + * Only set the divisor registers if they would change, since on + * some 16550 incompatibles (Startech), setting them clears the + * data input register. This also reduces the effects of the + * UMC8669F bug. + */ + divisor = ttspeedtab(comdefaultrate, comspeedtab); + dlbl = divisor & 0xFF; + if (sp->dlbl != dlbl) + outb(iobase + com_dlbl, dlbl); + dlbh = (u_int) divisor >> 8; + if (sp->dlbh != dlbh) + outb(iobase + com_dlbh, dlbh); + outb(iobase + com_cfcr, CFCR_8BITS); + sp->mcr = inb(iobase + com_mcr); + /* + * We don't want interrupts, but must be careful not to "disable" + * them by clearing the MCR_IENABLE bit, since that might cause + * an interrupt by floating the IRQ line. + */ + outb(iobase + com_mcr, (sp->mcr & MCR_IENABLE) | MCR_DTR | MCR_RTS); +} + +static void +siocnclose(sp) + struct siocnstate *sp; +{ + Port_t iobase; + + /* + * Restore the device control registers. + */ + siocntxwait(); + iobase = siocniobase; + outb(iobase + com_cfcr, CFCR_DLAB | CFCR_8BITS); + if (sp->dlbl != inb(iobase + com_dlbl)) + outb(iobase + com_dlbl, sp->dlbl); + if (sp->dlbh != inb(iobase + com_dlbh)) + outb(iobase + com_dlbh, sp->dlbh); + outb(iobase + com_cfcr, sp->cfcr); + /* + * XXX damp oscillations of MCR_DTR and MCR_RTS by not restoring them. + */ + outb(iobase + com_mcr, sp->mcr | MCR_DTR | MCR_RTS); + outb(iobase + com_ier, sp->ier); +} + +void +siocnprobe(cp) + struct consdev *cp; +{ + int unit; + + /* XXX: ick */ + unit = DEV_TO_UNIT(CONUNIT); + siocniobase = CONADDR; + + /* make sure hardware exists? XXX */ + + /* initialize required fields */ + cp->cn_dev = makedev(CDEV_MAJOR, unit); +#ifdef COMCONSOLE + cp->cn_pri = CN_REMOTE; /* Force a serial port console */ +#else + cp->cn_pri = (boothowto & RB_SERIAL) ? CN_REMOTE : CN_NORMAL; +#endif +} + +void +siocninit(cp) + struct consdev *cp; +{ + comconsole = DEV_TO_UNIT(cp->cn_dev); +} + +int +siocncheckc(dev) + dev_t dev; +{ + int c; + Port_t iobase; + int s; + struct siocnstate sp; + + iobase = siocniobase; + s = spltty(); + siocnopen(&sp); + if (inb(iobase + com_lsr) & LSR_RXRDY) + c = inb(iobase + com_data); + else + c = -1; + siocnclose(&sp); + splx(s); + return (c); +} + + +int +siocngetc(dev) + dev_t dev; +{ + int c; + Port_t iobase; + int s; + struct siocnstate sp; + + iobase = siocniobase; + s = spltty(); + siocnopen(&sp); + while (!(inb(iobase + com_lsr) & LSR_RXRDY)) + ; + c = inb(iobase + com_data); + siocnclose(&sp); + splx(s); + return (c); +} + +void +siocnputc(dev, c) + dev_t dev; + int c; +{ + int s; + struct siocnstate sp; + + s = spltty(); + siocnopen(&sp); + siocntxwait(); + outb(siocniobase + com_data, c); + siocnclose(&sp); + splx(s); +} + +#ifdef DSI_SOFT_MODEM +/* + * The magic code to download microcode to a "Connection 14.4+Fax" + * modem from Digicom Systems Inc. Very magic. + */ + +#define DSI_ERROR(str) { ptr = str; goto error; } +static int +LoadSoftModem(int unit, int base_io, u_long size, u_char *ptr) +{ + int int_c,int_k; + int data_0188, data_0187; + + /* + * First see if it is a DSI SoftModem + */ + if(!((inb(base_io+7) ^ inb(base_io+7) & 0x80))) + return ENODEV; + + data_0188 = inb(base_io+4); + data_0187 = inb(base_io+3); + outb(base_io+3,0x80); + outb(base_io+4,0x0C); + outb(base_io+0,0x31); + outb(base_io+1,0x8C); + outb(base_io+7,0x10); + outb(base_io+7,0x19); + + if(0x18 != (inb(base_io+7) & 0x1A)) + DSI_ERROR("dsp bus not granted"); + + if(0x01 != (inb(base_io+7) & 0x01)) { + outb(base_io+7,0x18); + outb(base_io+7,0x19); + if(0x01 != (inb(base_io+7) & 0x01)) + DSI_ERROR("program mem not granted"); + } + + int_c = 0; + + while(1) { + if(int_c >= 7 || size <= 0x1800) + break; + + for(int_k = 0 ; int_k < 0x800; int_k++) { + outb(base_io+0,*ptr++); + outb(base_io+1,*ptr++); + outb(base_io+2,*ptr++); + } + + size -= 0x1800; + int_c++; + } + + if(size > 0x1800) { + outb(base_io+7,0x18); + outb(base_io+7,0x19); + if(0x00 != (inb(base_io+7) & 0x01)) + DSI_ERROR("program data not granted"); + + for(int_k = 0 ; int_k < 0x800; int_k++) { + outb(base_io+1,*ptr++); + outb(base_io+2,0); + outb(base_io+1,*ptr++); + outb(base_io+2,*ptr++); + } + + size -= 0x1800; + + while(size > 0x1800) { + for(int_k = 0 ; int_k < 0xC00; int_k++) { + outb(base_io+1,*ptr++); + outb(base_io+2,*ptr++); + } + size -= 0x1800; + } + + if(size < 0x1800) { + for(int_k=0;int_k 0) { + if(int_c == 7) { + outb(base_io+7,0x18); + outb(base_io+7,0x19); + if(0x00 != (inb(base_io+7) & 0x01)) + DSI_ERROR("program data not granted"); + for(int_k = 0 ; int_k < size/3; int_k++) { + outb(base_io+1,*ptr++); + outb(base_io+2,0); + outb(base_io+1,*ptr++); + outb(base_io+2,*ptr++); + } + } else { + for(int_k = 0 ; int_k < size/3; int_k++) { + outb(base_io+0,*ptr++); + outb(base_io+1,*ptr++); + outb(base_io+2,*ptr++); + } + } + } + outb(base_io+7,0x11); + outb(base_io+7,3); + + outb(base_io+4,data_0188 & 0xfb); + + outb(base_io+3,data_0187); + + return 0; +error: + printf("sio%d: DSI SoftModem microcode load failed: <%s>\n",unit,ptr); + outb(base_io+7,0x00); \ + outb(base_io+3,data_0187); \ + outb(base_io+4,data_0188); \ + return EIO; +} +#endif /* DSI_SOFT_MODEM */ + +/* + * support PnP cards if we are using 'em + */ + +#if NPNP > 0 + +static struct siopnp_ids { + u_long vend_id; + char *id_str; +} siopnp_ids[] = { + { 0x8113b04e, "Supra1381"}, + { 0x9012b04e, "Supra1290"}, + { 0x11007256, "USR0011"}, + { 0 } +}; + +static char *siopnp_probe(u_long csn, u_long vend_id); +static void siopnp_attach(u_long csn, u_long vend_id, char *name, + struct isa_device *dev); +static u_long nsiopnp = NSIO; + +static struct pnp_device siopnp = { + "siopnp", + siopnp_probe, + siopnp_attach, + &nsiopnp, + &tty_imask +}; +DATA_SET (pnpdevice_set, siopnp); + +static char * +siopnp_probe(u_long csn, u_long vend_id) +{ + struct siopnp_ids *ids; + char *s = NULL; + + for(ids = siopnp_ids; ids->vend_id != 0; ids++) { + if (vend_id == ids->vend_id) { + s = ids->id_str; + break; + } + } + + if (s) { + struct pnp_cinfo d; + read_pnp_parms(&d, 0); + if (d.enable == 0 || d.flags & 1) { + printf("CSN %d is disabled.\n", csn); + return (NULL); + } + + } + + return (s); +} + +static void +siopnp_attach(u_long csn, u_long vend_id, char *name, struct isa_device *dev) +{ + struct pnp_cinfo d; + struct isa_device *dvp; + + if (dev->id_unit >= NSIOTOT) + return; + + if (read_pnp_parms(&d, 0) == 0) { + printf("failed to read pnp parms\n"); + return; + } + + write_pnp_parms(&d, 0); + + enable_pnp_card(); + + dev->id_iobase = d.port[0]; + dev->id_irq = (1 << d.irq[0]); + dev->id_intr = siointr; + dev->id_ri_flags = RI_FAST; + dev->id_drq = -1; + + if (dev->id_driver == NULL) { + dev->id_driver = &siodriver; + dvp = find_isadev(isa_devtab_tty, &siodriver, 0); + if (dvp != NULL) + dev->id_id = dvp->id_id; + } + + if ((dev->id_alive = sioprobe(dev)) != 0) + sioattach(dev); + else + printf("sio%d: probe failed\n", dev->id_unit); +} +#endif diff -urN sys.orig/i386/isa/sioreg.h sys/i386/isa/sioreg.h --- sys.orig/i386/isa/sioreg.h Wed Nov 6 19:08:33 1996 +++ sys/i386/isa/sioreg.h Tue Apr 14 08:45:11 1998 @@ -106,6 +106,9 @@ #define MSR_DDSR 0x02 #define MSR_DCTS 0x01 +/* speed to initalize to during chip tests */ +#define SIO_TEST_SPEED 9600 + /* * WARNING: Serial console is assumed to be at COM1 address * and CONUNIT must be 0. diff -urN sys.orig/i386/isa/spc.c sys/i386/isa/spc.c --- sys.orig/i386/isa/spc.c Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/spc.c Tue Apr 14 08:47:56 1998 @@ -0,0 +1,1369 @@ +/* + * Copyright (c) 1995, 1996 Takahide Matsutsuka + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * REX-5535 (MB89352A (SPC)) SCSI Driver + * by Takahide Matsutsuka (matsu@cs.titech.ac.jp) February, 1996 + * + * Acknowledgements: Many of the algorithms used in this driver are + * inspired by the work of Tohru Kobayashi (koba@lsi.yokogawa.co.jp). + * Thanks a lot! + */ +/* + * This version work on: + * RATOC REX-5535/5535AC/5535AMC/5535X/5535XM + * + * $Id: spc.c,v 1.1.2.2 1998/02/19 05:01:57 hosokawa Exp $ + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include + +#include "apm.h" +#if NAPM > 0 +#include +#endif /* NAPM > 0 */ + +/* pccard support */ +#include "card.h" +#if NCARD > 0 +#include +#include +#include +#include +#endif /* NCARD > 0 */ + +#include + +#include "spc.h" +#ifndef NSPC +#define NSPC 1 +#endif +#define SPC_HOST_ID 7 +#define NTARGETS 8 +#define NLUNS 8 +#define SPC_TIMEOUT 10000000 + +#define SPC_SET_TC(port, len) do {\ + outb((port) + SPC_TCH, (len)>>16);\ + outb((port) + SPC_TCM, (len)>>8);\ + outb((port) + SPC_TCL, (len));\ + } while(0) +#define SPC_GET_TC(port) (\ + (inb((port) + SPC_TCH)<<16)|\ + (inb((port) + SPC_TCM)<<8)|\ + inb((port) + SPC_TCL)\ + ) + +#define SQ_FLG_USEDISCON (1<<0) +#define SQ_FLG_RSNS (1<<1) /* request sense */ + +static int spc_unit = 0; + +struct spc_queue { + struct spc_queue *cq_next; + int flags; + struct scsi_xfer *xs; + u_char *data; /* data pointer */ + int datalen; /* data pointer */ + struct scsi_xfer *alt_xs; /* */ + u_char *alt_data; /* data pointer */ + int alt_datalen; /* data pointer */ + int alt_statusbyte; +}; + +typedef enum { + IN_BUSFREE = 0, + IN_SELECTION_IN_PROGRESS, + IN_CONNECTED_MO, + IN_CONNECTED, + IN_WAITDISCONNECT, + IN_WAITBUSFREE +} SPC_STATE; + +static struct spc_data { /* one of these per adapter */ + int unit; + int baseport; /* I/O base */ + SPC_STATE state; + struct scsi_link sc_link; /* each existing device */ + struct spc_queue *cq_first; /* command queue first */ + struct spc_queue *cq_last; /* command queue last */ + struct spc_queue *fq_first; /* free queue first */ + struct spc_queue *fq_last; /* free queue last */ + struct spc_queue *busy[NTARGETS][NLUNS]; + int target; /* current target */ + int lun; /* current lun */ + u_char *data; /* current data pointer */ + int datalen; /* current data pointer */ + int statusbyte; + int slot; /* information for reconfiguration */ + int alive; /* is driver alive? */ +} *spcdata[NSPC]; + +/* the below structure is so we have a default dev struct for out link struct */ static struct scsi_device spc_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "spc", + 0, + { 0, 0 } +}; + +static inline void +spc_free_queue(struct spc_data *spc, struct spc_queue *sq) +{ + unsigned int s = splbio(); + if( !spc->fq_first ) + spc->fq_first = sq; + else + spc->fq_last->cq_next = sq; + spc->fq_last = sq; + splx(s); +} + +/* prototypes ... */ +/* called by higher level drivers */ +static int spc_probe(struct isa_device *); +static int spc_attach(struct isa_device *); +static int32_t spc_scsi_cmd(struct scsi_xfer *); +static u_int32_t spc_adapter_info(int); +static void spc_minphys(struct buf *); + +/* private routines */ +static void spc_done(struct spc_data *, int32_t); +static int spc_command(struct spc_data *); +static void spc_selection(struct spc_data *); +static int spc_sel_sub(struct spc_data *, struct spc_queue *, int32_t *); +static int spc_reselection(struct spc_data *, int); +static int spc_poll(struct spc_data *, struct spc_queue *); +static int spc_progtx(int, u_char *, int, int, int *); +static int spc_progrx(int, u_char *, int, int, int *); +static int spc_byte_in(int, int); +static int spc_byte_out(int, int, int); +static int spc_next_phase(int); +static int spc_pollport(int, int, int); +static struct scsi_xfer *spc_alloc_rsns(const struct scsi_xfer *); +static void spc_free_rsns(struct scsi_xfer *); +static void spc_complete(struct spc_data *); +static int spc_init(struct spc_data *); +static void spc_cleanup(struct spc_data *); +static void spc_dumpreg(int, const char *); +#ifdef SCSI_DETACH +static void spcdetach __P((struct isa_device *dev)); +#endif + +/* pccard support */ +#if NCARD > 0 +static int spc_card_intr(struct pccard_devinfo *); +static void spc_card_unload(struct pccard_devinfo *); +static void spc_card_suspend(struct pccard_devinfo *); +static int spc_card_init(struct pccard_devinfo *, int); + +static struct pccard_device spc_info = +{ + "spc", + spc_card_init, + spc_card_unload, + spc_card_intr, +/* spc_card_suspend, */ + 0, /* Attributes - presently unused */ + &bio_imask /* Interrupt mask for device */ + /* This should also include net_imask?? */ +}; + +DATA_SET(pccarddrv_set, spc_info); + +/* + * Called when a power down is wanted. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * spc_card_init with first=0. This is called when the user suspends + * the system, or the APM code suspends the system. + */ +static void +spc_card_suspend(struct pccard_devinfo *devi) +{ + printf("spc%d: suspending\n", devi->isahd.id_unit); +} + +static int probing_pccard = 0; + +/* + * Initialize the device - called from Slot manager. + * if first is set, then initially check for + * the device's existence before initialising it. + * Once initialised, the device table may be set up. + */ +static int +spc_card_init(struct pccard_devinfo *devi, int first) +{ + /* + * validate unit number. + */ + static int already_spcinit; + struct spc_data *spc = spcdata[devi->isahd.id_unit]; + if( first ) { + if( devi->isahd.id_unit >= NSPC ) + return(ENODEV); + /* Make sure it isn't already initialisd. */ + if( already_spcinit == 1 ) { + if( spc_init(spc) == 0 ) + return(ENXIO); + if( spc_attach(&devi->isahd) == 0 ) + return(ENXIO); + return 0; + } + /* + * Probe the device. If a value is returned, the + * device was found at the location. + */ +#if 0 + printf("probe spc\n"); +#endif + probing_pccard = 1; + if( spc_probe(&devi->isahd) == 0 ) { + probing_pccard = 0; + return(ENXIO); + } + probing_pccard = 0; +#if 0 + printf("attach spc\n"); +#endif + if( spc_attach(&devi->isahd) == 0 ) + return(ENXIO); + already_spcinit = 1; + return 0; + + } + /* + * XXX TODO: + * If it was already inited before, the device structure + * should be already initialised. Here we do + * reset spc, + * but I don't know whether this is the best way... + */ + if( !spc_init(spc) ) + return (ENXIO); + spc_dumpreg(spc->baseport, "resume"); + printf("spc%d: resumed\n", spc->unit); + return 0; +} +/* + * spc_card_unload - unload the driver and clear the table. + * XXX TODO: + * This is called usually when the card is ejected, but + * can be caused by the modunload of a controller driver. + * The idea is reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +static void +spc_card_unload(struct pccard_devinfo *devi) +{ + int unit = devi->isahd.id_unit; + struct spc_data *spc = spcdata[unit]; + + /* + * Calling printf() in xxunload() makes hotswap operations + * unstable, so I removed all printf()'s in xxunload() except + * critical ones. + * Tatsumi Hosokawa + */ + + spc->alive = 0; +#ifdef SCSI_DETACH + spcdetach(&devi->isahd); +#endif +#if 0 + printf("spc%d: unload\n", unit); +#endif +} +/* + * spc_card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +spc_card_intr(struct pccard_devinfo *devi) +{ + spcintr(devi->isahd.id_unit); + return 1; +} +#endif /* NCARD > 0 */ + +#define SPC_DISCONNECT /* undef if driver doesn't work well yet... */ +#undef SPC_DEBUG /* display trace data */ + +#ifdef SPC_DEBUG +#define SPC_TRACE(x) printf x; +#else +#define SPC_TRACE(x) +#endif + +struct isa_driver spcdriver = { + spc_probe, spc_attach, "spc", +}; + +struct scsi_adapter spc_switch = { + spc_scsi_cmd, + spc_minphys, + 0, + 0, + spc_adapter_info, + "spc", + { 0, 0 } +}; + +/* + * INITIALIZATION ROUTINES (probe, attach ++) + */ + +/* + * spc_probe: probe for MB89352(A) SCSI controller + * returns non-zero value if a controller is found. + */ +static int +spc_probe(struct isa_device *dvp) +{ + int unit = spc_unit; + struct spc_data *spc; + + SPC_TRACE(("spc: probe\n")); + + if( (unit >= NSPC) && !dvp->id_reconfig ) { + printf("spc%d: unit number too high\n", unit); + return 0; + } + + if ((dvp->id_flags & SPC_FLAGS_PCCARD_ONLY) && !probing_pccard) + return 0; + + if( !dvp->id_reconfig ) { + /* prepare structure for the unit */ + spc = malloc(sizeof(struct spc_data), M_TEMP, M_NOWAIT); + bzero(spc, sizeof(struct spc_data)); + spc->baseport = dvp->id_iobase; + spc->fq_first = spc->fq_last = 0; + spcdata[unit] = spc; + dvp->id_unit = unit; + + if( !spc_init(spc) ) { + /* init failed, maybe scsi bus is off-lined */ + free(spc, M_TEMP); + return 0; + } + + spc_unit++; /* for the next adapter */ + printf("spc%d: REX5535 (MB89352A) PCMCIA SCSI adapter\n", unit); + } else { + /* reconfiguration time */ + spc = spcdata[dvp->id_unit]; + if( !spc_init(spc) ) + return 0; + } + + return 16; +} + +#ifdef SCSI_DETACH +static void +spcdetach(dev) +struct isa_device *dev; +{ + int unit = dev->id_unit; + struct scsibus_data *scbus; + + scbus = (struct scsibus_data *)scsi_extend_get(spcdata[unit]->sc_link.scsibus); + scsi_detachdev(scbus); +} +#endif + + +static int +spc_attach(struct isa_device *dvp) +{ + int unit = dvp->id_unit; + struct spc_data *spc = spcdata[unit]; + struct scsibus_data *scbus; + + SPC_TRACE(("spc%d: attach\n",unit)); + + if( dvp->id_reconfig ) + return 1; + + /* + * fill in the prototype scsi_link. + */ + spc->sc_link.adapter_unit = unit; + spc->sc_link.adapter_targ = SPC_HOST_ID; + spc->sc_link.adapter_softc = spc; + spc->sc_link.adapter = &spc_switch; + spc->sc_link.device = &spc_dev; + + /* + * Prepare the scsibus_data area for the upperlevel + * scsi code. + */ + scbus = scsi_alloc_bus(); + if( !scbus ) return 0; + scbus->adapter_link = &spc->sc_link; + + /* + * ask the adapter what subunits are present + */ + + scsi_attachdevs(scbus); + + return 1; +} + +static u_int32_t +spc_adapter_info(int unit) +{ + SPC_TRACE(("spc%d: adapter_info\n", unit)); + return 2; /* 2 outstanding requests at a time are acceptable */ +} + +static void +spc_minphys(struct buf *bp) +{ + SPC_TRACE(("spc_minphys:%d\n", bp->b_bcount)); +#if 0 /* probably not needed...can it be true? */ +#define PAGESIZ 4096 +#define SPC_NSEG 2 + if( bp->b_bcount > ((SPC_NSEG - 1) * PAGESIZ) ) + bp->b_bcount = ((SPC_NSEG - 1) * PAGESIZ); +#endif +} + +/* + * DRIVER ROUTINES (scsi_cmd, selection, ++) + */ + +/* + * desirable sequence + * 1.spc_scsi_cmd: receive scsi_xfer and queueing + * 2.spcintr: called by kernel interrupt handler + * 3.proceed scsi sequence + * (1) spc_selection: starts new scsi command from queue + * (2) spc_command: proceed scsi phase + * (3) spc_reselection: reselected by target + * (4) spc_complete: if needed, do self request sense + * (5) spc_poll: if booting time, don't use interrupt + */ +/* + * spc_scsi_cmd: receive scsi command from higher level module + */ +static int32_t +spc_scsi_cmd(struct scsi_xfer *xs) +{ + int unit = xs->sc_link->adapter_unit; + struct spc_data *spc = spcdata[unit]; + struct spc_queue *sq; + unsigned int s; + int32_t erc; + + SPC_TRACE(("spc%d:spc_scsi_cmd\n", unit)); + if( !spc->alive ) { + /* now card is off-lined */ + xs->error = XS_DRIVER_STUFFUP; + return (COMPLETE); + } + /* queueing command */ + if( spc->fq_first ) { + s = splbio(); + sq = spc->fq_first; + spc->fq_first = spc->fq_first->cq_next; + splx(s); + } else { + if( (sq = (struct spc_queue *) malloc(sizeof(struct spc_queue), + M_TEMP, M_NOWAIT)) == NULL ) { + /* can't malloc */ + printf("spc%d: queue full\n", spc->unit); + return (TRY_AGAIN_LATER); + } + } + + sq->xs = xs; + sq->data = xs->data; + sq->datalen = xs->datalen; + sq->cq_next = NULL; + sq->alt_xs = NULL; +#ifndef SPC_DISCONNECT + sq->flags = 0; +#else + sq->flags = SQ_FLG_USEDISCON; +#endif + + if( xs->flags & SCSI_NOMASK ) { + /* it's booting time. don't allow interrupts. */ + sq->flags &= ~SQ_FLG_USEDISCON; + erc = spc_poll(spc, sq); + spc_free_queue(spc, sq); + return erc; + } + + s = splbio(); + /* add to end of queue */ + if( spc->cq_first == NULL ) { + spc->cq_first = sq; + } else { + spc->cq_last->cq_next = sq; + } + spc->cq_last = sq; + spc_selection(spc); /* start selection */ + outb(spc->baseport + SPC_SCTL, SPC_SCTL_ENABLE); /* enable interrupt */ + splx(s); + return (SUCCESSFULLY_QUEUED); +} + +/* + * spcintr: called by kernel interrupt handler + */ +void +spcintr(int unit) +{ + struct spc_data *spc = spcdata[unit]; + int port = spc->baseport; + int intr, target = 0; + + SPC_TRACE(("spc%d: spcintr\n", unit)); + intr = inb(port + SPC_INTS); /* read interrupt */ + if( intr & SPC_INTS_RESELECT ) { + target = inb(port + SPC_TEMP); + } + outb(port + SPC_INTS, intr); /* clear interrupt */ + + /* HARDERR */ + if( intr & SPC_INTS_HARDERR ) { + printf("spc%d: HARDERR SERR=0x%d\n", spc->unit, inb(port + SPC_SERR)); + goto err; + } + + /* RESET */ + if( intr & SPC_INTS_RESET ) { + printf("spc%d: reset condition\n", spc->unit); + goto err; + } + + /* SERVREQ */ + if( intr & SPC_INTS_SERVREQ ) { + printf("spc%d: spurious interrupt(SERVREQ)\n", spc->unit); + goto err; + } + + /* DISCONNECT */ + if( intr & SPC_INTS_DISCONNECT ) { + if( spc->state == IN_WAITDISCONNECT ) { + /* nothing to do */ + } else if( spc->state == IN_WAITBUSFREE ) { + spc_complete(spc); + } else { + printf("spc%d: spurious interrupt(DISCONNECT) state=%d\n", + spc->unit, spc->state); + goto err; + } + spc->state = IN_BUSFREE; + } + + /* RESELECT */ + if( intr & SPC_INTS_RESELECT ) { + if( spc->state == IN_SELECTION_IN_PROGRESS ) { + outb(port + SPC_SCMD, SPC_SCMD_RESETATN); /* reset ATN */ + if( (spc->busy[spc->target][spc->lun]->flags & SQ_FLG_RSNS) == 0 ) { + spc->busy[spc->target][spc->lun] = NULL; + } + } + if( spc_reselection(spc, target) < 0 ) { + goto err; + } + if( spc_command(spc) < 0 ) { + goto err; + } + intr &= ~(SPC_INTS_COMPLETE); + } + + /* COMPLETE */ + if( intr & SPC_INTS_COMPLETE ) { + if( spc->state == IN_SELECTION_IN_PROGRESS ) { + spc->state = IN_CONNECTED_MO; + spc->cq_first = spc->cq_first->cq_next; /* delete from queue */ + if( spc_command(spc) < 0 ) { + goto err; + } + } else { + printf("spc%d: spurious interrupt(COMPLETE) state=%d\n", + spc->unit, spc->state); + goto err; + } + } + + /* TIMEOUT */ + if( intr & SPC_INTS_TIMEOUT ) { + outb(port + SPC_SCMD, SPC_SCMD_RESETATN); /* reset ATN */ + if( spc->state == IN_SELECTION_IN_PROGRESS ) { + printf("spc%d: selection timeout id=%d\n", spc->unit, spc->target); + spc->statusbyte = -1; + spc->cq_first = spc->cq_first->cq_next; /* delete from queue */ + spc_complete(spc); + } else { + printf("spc%d: spurious interrupt(TIMEOUT) state=%d\n", + spc->unit, spc->state); + goto err; + } + spc->state = IN_BUSFREE; + } + spc_selection(spc); + return; + + err: + (void) spc_init(spc); + spc_cleanup(spc); + spc->state = IN_BUSFREE; + spc_selection(spc); + return; +} + +/* + * (1) spc_selection: starts new scsi command + */ +static void +spc_selection(struct spc_data *spc) +{ + struct spc_queue *sq; + int32_t erc; + + SPC_TRACE(("spc%d: spc_selection\n", spc->unit)); + + if( spc->state != IN_BUSFREE ) { + return; + } + + again: + if( (sq = spc->cq_first) != NULL ) { + spc->state = IN_SELECTION_IN_PROGRESS; + if( spc_sel_sub(spc, sq, &erc) < 0 ) { + sq->xs->flags |= ITSDONE; + sq->xs->resid = sq->xs->datalen; + sq->xs->error = erc; + scsi_done(sq->xs); + spc->cq_first = sq->cq_next; /* delete from queue */ + spc_free_queue(spc, sq); + spc->state = IN_BUSFREE; + goto again; + } + } +} + +static int +spc_sel_sub(struct spc_data *spc, struct spc_queue *sq, int32_t *erc) +{ + const struct scsi_xfer *xs; + int port = spc->baseport; + int target, lun, val; + + SPC_TRACE(("spc%d: spc_sel_sub\n", spc->unit)); + + xs = sq->xs; + target = xs->sc_link->target; + lun = xs->sc_link->lun; + + if( sq->flags & SQ_FLG_RSNS ) { + /* request sense in progress */ + if( spc->busy[target][lun] != sq ) { + printf("spc%d: ??????\n", spc->unit); + } + } else { + if( spc->busy[target][lun] ) { + /* target is busy */ + printf("spc%d: target=%d, lun=%d is busy\n", spc->unit, target, lun); + *erc = XS_BUSY; + return -1; + } + } + if( xs->flags & SCSI_RESET ) { + /* command is empty, reset controller */ + printf("spc%d: reset controller by higher module\n", spc->unit); + (void) spc_init(spc); + *erc = XS_NOERROR; + return -1; + } + + spc->target = target; + spc->lun = lun; + spc->busy[target][lun] = sq; + spc->data = xs->data; + spc->datalen = xs->datalen; + spc->statusbyte = 0; + + outb(port + SPC_PCTL, 0); /* disable bus free interrupt */ + outb(port + SPC_SCMD, SPC_SCMD_SETATN); /* set ATN for IDENTIFY MSG */ + val = inb(port + SPC_BDID); /* get host id */ + val |= (1 << spc->target); /* set target id */ + outb(port + SPC_TEMP, val); /* set host id & target id */ + outb(port + SPC_TCH, 0x0f); /* set replying time */ + outb(port + SPC_TCM, 0x46); + outb(port + SPC_TCL, 0x04); /* set twait */ + outb(port + SPC_SCMD, SPC_SCMD_SELECT); /* exec selection phase! */ + *erc = XS_NOERROR; + return 0; +} + +/* + * (2) spc_command: proceed scsi phase + */ +static int +spc_command(struct spc_data *spc) +{ + struct scsi_xfer *xs; + struct spc_queue *sq; + int port = spc->baseport; + int phase, xfered, r, msg; + + SPC_TRACE(("spc%d: spc_command\n", spc->unit)); + + sq = spc->busy[spc->target][spc->lun]; + xs = sq->xs; + + again0: + if( (phase = spc_next_phase(port)) < 0 ) { + return -1; + } + + switch( phase ) { + case SPC_PCTL_MSGOUT: + outb(port + SPC_SCMD, SPC_SCMD_RESETATN); /* reset ATN */ + if( spc->state == IN_CONNECTED_MO ) { + /* send identify message */ + if( sq->flags & SQ_FLG_USEDISCON ) { + msg = 0xc0 | spc->lun; + } else { + msg = 0x80 | spc->lun; + } + if( spc_byte_out(port, msg, SPC_PCTL_MSGOUT) < 0 ) { + printf("spc%d: cannot send IDENTIFY MSG\n", spc->unit); + return -1; + } + spc->state = IN_CONNECTED; + } else { + /* send No Operation message */ + if( spc_byte_out(port, 0x08, SPC_PCTL_MSGOUT) < 0 ) { + printf("spc%d: cannot send No Operation MSG\n", spc->unit); + return -1; + } + } + break; + case SPC_PCTL_MSGIN: + if( (msg = spc_byte_in(port, SPC_PCTL_MSGIN)) < 0 ) { + return -1; + } + switch( msg ) { + case SPC_MSG_SAVEDATAP: + sq->data = spc->data; /* save data pointer */ + sq->datalen = spc->datalen; /* save data pointer */ + break; + case SPC_MSG_RESTOREP: + spc->data = sq->data; /* restore pointers */ + spc->datalen = sq->datalen; /* restore pointers */ + break; + case SPC_MSG_DISCONNECT: + spc->state = IN_WAITDISCONNECT; + return 0; + case SPC_MSG_CMDCOMPLETE: + spc->state = IN_WAITBUSFREE; + return 0; + default: + printf("spc%d: invalid message 0x%x\n", spc->unit, msg); + return -1; + } + break; + case SPC_PCTL_COMMAND: + if( spc_progtx(port, (u_char *)xs->cmd, xs->cmdlen, + phase, &xfered) < 0 ) { + return -1; + } + break; + case SPC_PCTL_DATAOUT: + r = spc_progtx(port, spc->data, spc->datalen, phase, &xfered); + spc->data += xfered; + spc->datalen -= xfered; + if( r < 0 ) { + return -1; + } + break; + case SPC_PCTL_DATAIN: + r = spc_progrx(port, spc->data, spc->datalen, phase, &xfered); + spc->data += xfered; + spc->datalen -= xfered; + if( r < 0 ) { + return -1; + } + break; + case SPC_PCTL_STATUS: + if( (spc->statusbyte = spc_byte_in(port, SPC_PCTL_STATUS)) < 0 ) { + return -1; + } + spc->statusbyte &= 0x1e; + break; + default: + printf("spc%d: invalid phase 0x%x\n", spc->unit, phase); + return -1; + } + goto again0; +} + +/* + * (3) spc_reselection: reselected by target + */ +static int +spc_reselection(struct spc_data *spc, int target) +{ + struct spc_queue *sq; + int port = spc->baseport; + int id, phase, val; + + SPC_TRACE(("spc%d: spc_reselection\n", spc->unit)); + + for(id = 0; id < 8; id++) { + if( (target & (1<= 8 ) { + printf("spc%d: cannot get target ID 0x%02x\n", spc->unit, target); + return -1; + } + if( (phase = spc_next_phase(port)) < 0 ) { + return -1; + } + if( phase != SPC_PCTL_MSGIN ) { + printf("spc%d: not MSGIN\n", spc->unit); + return -1; + } + if( (val = spc_byte_in(port, SPC_PCTL_MSGIN)) < 0 ) { + printf("spc%d: cannot recive IDENTIFY MSG\n", spc->unit); + return -1; + } + if( (val & 0x80) == 0 ) { + /* not identify message */ + printf("spc%d: bogus reselection\n", spc->unit); + return -1; + } + spc->target = id; + spc->lun = val & 0x07; /* get LUN */ + sq = spc->busy[id][spc->lun]; + spc->state = IN_CONNECTED; + spc->data = sq->data; /* restore data pointer */ + spc->datalen = sq->datalen; /* restore data pointer */ + return 0; +} + +/* + * (4) spc_complete: if needed, do self request sense + */ +static void +spc_complete(struct spc_data *spc) +{ + struct spc_queue *sq; + + SPC_TRACE(("spc%d: spc_complete\n", spc->unit)); + + sq = spc->busy[spc->target][spc->lun]; + if( sq->flags & SQ_FLG_RSNS ) { /* request sense in progress */ + int statusbyte; + struct scsi_xfer *sxs; + + statusbyte = spc->statusbyte; + sxs = sq->xs; + sq->xs = sq->alt_xs; + sq->alt_xs = NULL; + sq->flags &= ~SQ_FLG_RSNS; + spc->data = sq->alt_data; + spc->datalen = sq->alt_datalen; + spc->statusbyte = sq->alt_statusbyte; + spc_free_rsns(sxs); + if( statusbyte == 0 ) { + spc_done(spc, XS_SENSE); + } else { + printf("spc%d: request sense failed\n", spc->unit); + spc_done(spc, XS_DRIVER_STUFFUP); + } + } else { + switch( spc->statusbyte ) { + case 0x00: + spc_done(spc, XS_NOERROR); + break; + case 0x02: + if( sq->xs->flags & SCSI_ERR_OK ) { + spc_done(spc, XS_NOERROR); + } else if( (sq->flags & SQ_FLG_RSNS) == 0 ) { + struct scsi_xfer *sxs; + sxs = spc_alloc_rsns(sq->xs); + sq->alt_xs = sq->xs; + sq->alt_data = spc->data; + sq->alt_datalen = spc->datalen; + sq->alt_statusbyte = spc->statusbyte; + sq->xs = sxs; + sq->flags |= SQ_FLG_RSNS; + /* in queue */ + sq->cq_next = NULL; + if( spc->cq_first == NULL ) { + spc->cq_first = sq; + } else { + spc->cq_last->cq_next = sq; + } + spc->cq_last = sq; + } + break; + case 0x08: + spc_done(spc, XS_BUSY); + break; + default: + printf("spc%d: unknown statusbyte 0x%x\n", + spc->unit, spc->statusbyte); + spc_done(spc, XS_DRIVER_STUFFUP); + } + } +} + +/* + * (5) spc_poll: use polling (at booting time) + */ +static int +spc_poll(struct spc_data *spc, struct spc_queue *sq) +{ + struct scsi_xfer *xs; + int port = spc->baseport; + int phase, val, wait; + int32_t erc; + + SPC_TRACE(("spc%d: spc_poll\n", spc->unit)); + + xs = sq->xs; + outb(port + SPC_INTS, 0xff); /* reset interrupt */ + spc->state = IN_SELECTION_IN_PROGRESS; + if( spc_sel_sub(spc, sq, &erc) < 0 ) { + printf("spc%d: spc_poll: selection not start\n", spc->unit); + (void) spc_init(spc); + goto err; + } + + /* need to check end of selection phase ... */ + for(wait = SPC_TIMEOUT;;) { + if( (val = inb(port + SPC_INTS)) & SPC_INTS_COMPLETE ) { + /* selection success */ + outb(port + SPC_INTS, 0xff); /* reset interrupt */ + break; + } + if( val & SPC_INTS_TIMEOUT ) { + /* selection timeout */ + outb(port + SPC_SCMD, SPC_SCMD_RESETATN); /* reset ATN */ + goto err; + } + if( wait-- == 0 ) { + printf("spc%d: spc_poll: selecton not complete\n", spc->unit); + (void) spc_init(spc); + goto err; + } + } + + spc->state = IN_CONNECTED_MO; + if( spc_command(spc) < 0 ) { + (void) spc_init(spc); + goto err; + } + + /* wait for bus free */ + if( spc_pollport(port+SPC_INTS, SPC_INTS_DISCONNECT, + SPC_INTS_DISCONNECT) < 0 ) { + printf("spc%d: spc_poll: timeout at bus free\n", spc->unit); + (void) spc_init(spc); + goto err; + } + outb(port + SPC_INTS, 0xff); /* reset interrupt */ + + xs->status = spc->statusbyte; + switch (xs->status) { + case 0x00: + xs->error = XS_NOERROR; + break; + case 0x02: + if( xs->flags & SCSI_ERR_OK ) { + xs->error = XS_NOERROR; + } else if( (sq->flags & SQ_FLG_RSNS) == 0 ) { + struct scsi_xfer *sxs; + sxs = spc_alloc_rsns(xs); + sq->alt_xs = sq->xs; + sq->alt_data = spc->data; + sq->alt_datalen = spc->datalen; + sq->alt_statusbyte = spc->statusbyte; + sq->xs = sxs; + sq->flags |= SQ_FLG_RSNS; + if( spc_poll(spc, sq) != COMPLETE ) { + xs->error = XS_DRIVER_STUFFUP; + printf("spc%d: request sense failed\n", spc->unit); + } else { + xs->error = XS_SENSE; + } + sq->xs = sq->alt_xs; + sq->alt_xs = NULL; + sq->flags &= ~SQ_FLG_RSNS; + spc->data = sq->alt_data; + spc->datalen = sq->alt_datalen; + spc->statusbyte = sq->alt_statusbyte; + spc_free_rsns(sxs); + } else { + return (HAD_ERROR); + } + break; + case 0x08: + xs->error = XS_BUSY; + break; + default: + printf("spc%d: unknown statusbyte 0x%x\n", spc->unit, xs->status); + xs->error = XS_DRIVER_STUFFUP; + } + xs->flags |= ITSDONE; + xs->resid = spc->datalen; + spc->state = IN_BUSFREE; + if( (sq->flags & SQ_FLG_RSNS) == 0 ) { + spc->busy[spc->target][spc->lun] = NULL; + } + return (COMPLETE); + err: + outb(port + SPC_INTS, 0xff); /* reset interrupt */ + xs->error = XS_TIMEOUT; + xs->flags |= ITSDONE; + xs->resid = spc->datalen; + spc->state = IN_BUSFREE; + if( (sq->flags & SQ_FLG_RSNS) == 0 ) { + spc->busy[spc->target][spc->lun] = NULL; + } + return (HAD_ERROR); +} + +/* + * MISC FUNCTIONS + */ +static struct scsi_xfer +*spc_alloc_rsns(const struct scsi_xfer *xs) +{ + struct scsi_xfer *sxs; + struct scsi_sense *scmd; + + if( (sxs = malloc(sizeof(struct scsi_xfer), M_TEMP, M_NOWAIT)) == NULL ) { + return NULL; + } + if( (scmd = malloc(sizeof(struct scsi_sense), M_TEMP, M_NOWAIT)) == NULL ) { + free(sxs, M_TEMP); + return NULL; + } + bzero(sxs, sizeof(struct scsi_xfer)); + bzero(scmd, sizeof(struct scsi_sense)); + scmd->op_code = REQUEST_SENSE; + scmd->byte2 = (xs->sc_link->lun << 5) & 0xe0; + scmd->length = sizeof(struct scsi_sense_data); + sxs->flags = SCSI_DATA_IN; + sxs->cmd = (void *)scmd; + sxs->cmdlen = sizeof(struct scsi_sense); + sxs->data = (u_char *)&xs->sense; + sxs->datalen = sizeof(struct scsi_sense_data); + sxs->timeout = xs->timeout; + sxs->sc_link = xs->sc_link; + return sxs; +} + +static void +spc_free_rsns(struct scsi_xfer *sxs) +{ + free(sxs->cmd, M_TEMP); + free(sxs, M_TEMP); +} + +static void +spc_dumpreg(int port, const char *p) +{ + printf("spc: %s SSTS=0x%x INTS=0x%x PSNS=0x%x PCTL=0x%x\n", + p, + inb(port + SPC_SSTS), + inb(port + SPC_INTS), + inb(port + SPC_PSNS), + inb(port + SPC_PCTL)); +} + +static int +spc_pollport(int port, int mask, int ptn) +{ + int wait, val; + + for(wait = SPC_TIMEOUT; wait > 0; wait--) { + if( ((val = inb(port)) & mask) == ptn ) { + return val; + } + } + return -1; +} + +static int +spc_next_phase(int port) +{ + int psns; + + if( (psns = spc_pollport(port+SPC_PSNS, SPC_PSNS_REQ, SPC_PSNS_REQ)) < 0 ) { + printf("spc: spc_next_phase: REQ not assert\n"); + return -1; + } + return psns & SPC_PSNS_PHASE; +} + +static void +spc_done(struct spc_data *spc, int32_t xserror) +{ + struct spc_queue *sq; + + sq = spc->busy[spc->target][spc->lun]; + spc->busy[spc->target][spc->lun] = NULL; + sq->xs->flags |= ITSDONE; + sq->xs->resid = spc->datalen; + sq->xs->status = spc->statusbyte; + sq->xs->error = xserror; + if( sq->xs->resid ) { /* XXX */ + printf("spc_done: xs %d resid %d\n", sq->xs->datalen, sq->xs->resid); + } + scsi_done(sq->xs); + spc_free_queue(spc, sq); +} + +static int +spc_progtx(int port, u_char *data, int datalen, int phase, int *xfered) +{ + long wait; + int n, ssts, datalenorg; + + datalenorg = datalen; + outb(port + SPC_PCTL, phase); + SPC_SET_TC(port, datalen); /* transfer byte counter set */ + outb(port + SPC_SCMD, SPC_SCMD_PROGTX); /* programed transfer */ + + if( spc_pollport(port + SPC_SSTS, 0xf0, 0xb0) < 0 ) { + spc_dumpreg(port, "spc_progtx"); + *xfered = 0; + return -1; + } + + if( (inb(port + SPC_SSTS) & SPC_SSTS_DREMPTY) == 0 ) { /* XXX */ + spc_dumpreg(port, "AA spc_progtx: not empty"); + } + + for(;;) { + for(wait = SPC_TIMEOUT;;) { + if( ((ssts = inb(port + SPC_SSTS)) & SPC_SSTS_BUSY) == 0) { + goto doneWRITE; + } + if( (ssts & 3) == SPC_SSTS_DREMPTY ) { + break; + } + if( wait-- == 0 ) { + *xfered = datalenorg - SPC_GET_TC(port); + spc_dumpreg(port, "spc_progtx: timeout"); + return -1; + } + } + if( datalen > 0 ) { + if( datalen >= 8 ) { + n = 8; + } else { + n = datalen; + } + outsb(port + SPC_DREG, data, n); + data += n; + datalen -= n; + } + } + doneWRITE: + *xfered = datalenorg - SPC_GET_TC(port); + outb(port + SPC_INTS, SPC_INTS_COMPLETE|SPC_INTS_SERVREQ); + return 0; +} + +static int +spc_progrx(int port, u_char *data, int datalen, int phase, int *xfered) +{ + int datalenorg, ssts, wait; + + datalenorg = datalen; + outb(port + SPC_PCTL, phase); + SPC_SET_TC(port, datalen); /* transfer byte counter set */ + outb(port + SPC_SCMD, SPC_SCMD_PROGRX); /* programed transfer */ + + if( spc_pollport(port+SPC_SSTS, 0xd0, 0x90) < 0 ) { + spc_dumpreg(port, "spc_progrx"); + *xfered = 0; + return -1; + } + + while(1) { + for(wait = SPC_TIMEOUT;;) { + if( ((ssts = inb(port + SPC_SSTS)) & SPC_SSTS_DREMPTY) == 0 ) { + break; + } + if( (ssts & SPC_SSTS_BUSY) == 0) { + goto doneREAD; + } + if( wait-- == 0 ) { + spc_dumpreg(port, "spc_progrx: timeout"); + *xfered = datalenorg - datalen; + return -1; + } + } + if( datalen > 0 ) { + *data++ = inb(port + SPC_DREG); + datalen--; + } else { + inb(port + SPC_DREG); /* dummy read XXX */ + } + } + doneREAD: + if( (inb(port + SPC_SSTS) & SPC_SSTS_DREMPTY) == 0 ) { /* XXX */ + spc_dumpreg(port, "spc_progrx: not empty"); + } + *xfered = datalenorg - datalen; + outb(port + SPC_INTS, SPC_INTS_COMPLETE|SPC_INTS_SERVREQ); + return 0; +} + +static int +spc_byte_in(int port, int phase) +{ + int retval; + + outb(port + SPC_PCTL, phase); + outb(port + SPC_SCMD, SPC_SCMD_SETACK); + if( spc_pollport(port+SPC_PSNS, SPC_PSNS_REQ, 0) < 0 ) { + spc_dumpreg(port, "spc_byte_in"); + return -1; + } + retval = inb(port + SPC_TEMP); /* read */ + outb(port + SPC_SCMD, SPC_SCMD_RESETACK); + return retval; +} + +static int +spc_byte_out(int port, int data, int phase) +{ + outb(port + SPC_PCTL, phase); + outb(port + SPC_TEMP, data); /* write */ + outb(port + SPC_SCMD, SPC_SCMD_SETACK); + if( spc_pollport(port+SPC_PSNS, SPC_PSNS_REQ, 0) < 0 ) { + spc_dumpreg(port, "spc_byte_out"); + return -1; + } + outb(port + SPC_SCMD, SPC_SCMD_RESETACK); + return 0; +} + +static int +spc_init(struct spc_data *spc) +{ + int port = spc->baseport; + long count = SPC_TIMEOUT; + + outb(port + SPC_SCTL, 0x9a); /* SPC software reset */ + outb(port + SPC_BDID, SPC_HOST_ID); /* set initiator id */ + outb(port + SPC_SCMD, 0); + outb(port + SPC_PCTL, 0); + outb(port + SPC_TEMP, 0); + outb(port + SPC_TCH , 0); + outb(port + SPC_TCM , 0); + outb(port + SPC_TCL , 0); + outb(port + SPC_PSNS, 0); + DELAY(100000); /* wait 100ms */ + outb(port + SPC_SCTL, SPC_SCTL_DISABLE); /* clear SPC reset */ + outb(port + SPC_SCMD, SPC_SCMD_RESET); /* RESET SCSI bus */ + DELAY(100000); /* wait 100ms */ + outb(port + SPC_SCMD, 0); /* RESET SCSI bus */ + DELAY(5000000); /* wait 5s */ + outb(port + SPC_SCMD, SPC_SCMD_BUSRELEASE); + + /* confirm bus release ... */ + while (inb(port + SPC_PSNS) && count) count--; + if( !count ) { + printf("spc%d: init failed, driver disabled\n", spc->unit); + spc->alive = 0; + return 0; + } + spc->alive = 1; + return 1; +} + +static void +spc_cleanup(struct spc_data *spc) +{ + int id, lun; + struct spc_queue *sq; + struct scsi_xfer *sxs; + + printf("spc%d: cleanup current id=%d current lun=%d state=%d\n", + spc->unit, spc->target, spc->lun, spc->state); + for(id = 0; id < NTARGETS; id++) { + for(lun = 0; lun < NLUNS; lun++) { + if( (sq = spc->busy[id][lun]) == NULL ) { + continue; + } + printf("spc%d: cleanup id=%d lun=%d\n", spc->unit, id, lun); + if( sq->flags & SQ_FLG_RSNS ) { + sxs = sq->xs; + if( sq->alt_xs == NULL ) { + continue; + } + sq->xs = sq->alt_xs; + if( sxs ) { + spc_free_rsns(sxs); + } + } + sq->xs->flags |= ITSDONE; + sq->xs->error = XS_TIMEOUT; + scsi_done(sq->xs); + spc_free_queue(spc, sq); + spc->busy[id][lun] = NULL; + } + } +} diff -urN sys.orig/i386/isa/spc.h sys/i386/isa/spc.h --- sys.orig/i386/isa/spc.h Thu Jan 1 09:00:00 1970 +++ sys/i386/isa/spc.h Tue Apr 14 08:45:12 1998 @@ -0,0 +1,138 @@ +/* + * Copyright (c) 1995 Takahide Matsutsuka + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Header file for REX-5535 (MB89352A (SPC)) SCSI Driver + * by Takahide Matsutsuka (matsu@cs.titech.ac.jp) February, 1996 + */ +/* + * $Id: spc.h,v 1.1.2.1 1997/12/11 14:00:28 itojun Exp $ + */ + +/* + * config flags + */ +#define SPC_FLAGS_PCCARD_ONLY 0x01 + +/* + * SPC I/O port address + */ +#define SPC_BDID 0x00 +#define SPC_SCTL 0x01 +#define SPC_SCMD 0x02 +#define SPC_TMOD 0x03 +#define SPC_INTS 0x04 +#define SPC_PSNS 0x05 +#define SPC_SSTS 0x06 +#define SPC_SERR 0x07 +#define SPC_PCTL 0x08 +#define SPC_MBC 0x09 +#define SPC_DREG 0x0a +#define SPC_TEMP 0x0b +#define SPC_TCH 0x0c +#define SPC_TCM 0x0d +#define SPC_TCL 0x0e + +/* + * SPC_SCTL spc control + */ +#define SPC_SCTL_ENABLE 0x1b +#define SPC_SCTL_DISABLE 0x18 + +/* + * SPC_SCMD command + */ +#define SPC_SCMD_BUSRELEASE 0x00 +#define SPC_SCMD_RESET 0x10 +#define SPC_SCMD_SELECT 0x20 +#define SPC_SCMD_RESETATN 0x40 +#define SPC_SCMD_SETATN 0x60 +#define SPC_SCMD_TRANSFER 0x80 +#define SPC_SCMD_TRANSPAUSE 0xa0 +#define SPC_SCMD_RESETACK 0xc0 +#define SPC_SCMD_SETACK 0xe0 + +#define SPC_SCMD_PROGTX 0x8c +#define SPC_SCMD_PROGRX 0x84 + +/* + * SPC_INTS interrupt sense + */ +#define SPC_INTS_SELECT 0x80 +#define SPC_INTS_RESELECT 0x40 +#define SPC_INTS_DISCONNECT 0x20 +#define SPC_INTS_COMPLETE 0x10 +#define SPC_INTS_SERVREQ 0x08 +#define SPC_INTS_TIMEOUT 0x04 +#define SPC_INTS_HARDERR 0x02 +#define SPC_INTS_RESET 0x01 + +/* + * SPC_PSNS phase sense + */ +#define SPC_PSNS_REQ 0x80 +#define SPC_PSNS_ACK 0x40 +#define SPC_PSNS_ATN 0x20 +#define SPC_PSNS_SEL 0x10 +#define SPC_PSNS_BSY 0x08 +#define SPC_PSNS_MSG 0x04 +#define SPC_PSNS_CD 0x02 +#define SPC_PSNS_IO 0x01 + +#define SPC_PSNS_PHASE 0x07 + +/* + * SPC_SSTS spc status + */ +#define SPC_SSTS_INIT 0x80 +#define SPC_SSTS_TARG 0x40 +#define SPC_SSTS_BUSY 0x20 +#define SPC_SSTS_XFER 0x10 +#define SPC_SSTS_SCSIRST 0x08 +#define SPC_SSTS_TCZERO 0x04 +#define SPC_SSTS_DRFULL 0x02 +#define SPC_SSTS_DREMPTY 0x01 + +/* + * SPC_PCTL phase control + */ +#define SPC_PCTL_MSG 0x04 +#define SPC_PCTL_CD 0x02 +#define SPC_PCTL_IO 0x01 + +#define SPC_PCTL_DATAOUT 0x00 +#define SPC_PCTL_DATAIN 0x01 +#define SPC_PCTL_COMMAND 0x02 +#define SPC_PCTL_STATUS 0x03 +#define SPC_PCTL_MSGOUT 0x06 +#define SPC_PCTL_MSGIN 0x07 + +/* + * others + */ +#define SPC_MSG_CMDCOMPLETE 0x00 +#define SPC_MSG_SAVEDATAP 0x02 +#define SPC_MSG_RESTOREP 0x03 +#define SPC_MSG_DISCONNECT 0x04 diff -urN sys.orig/i386/isa/syscons.c sys/i386/isa/syscons.c --- sys.orig/i386/isa/syscons.c Sat Feb 28 14:16:14 1998 +++ sys/i386/isa/syscons.c Tue Apr 14 08:45:13 1998 @@ -657,8 +657,18 @@ static int scresume(void *dummy) { - shfts = ctls = alts = agrs = metas = accents = 0; - return 0; + shfts = ctls = alts = agrs = metas = 0; + +#ifdef SYSCONS_VTY_RESUME + wakeup((caddr_t)&cur_console->smode); + if (cur_console->proc && (cur_console->proc != pfind(cur_console->pid))) + cur_console->smode.mode = VT_AUTO; + if(cur_console->smode.mode == VT_PROCESS) { + cur_console->status |= SWITCH_WAIT_ACQ; + psignal(cur_console->proc, cur_console->smode.acqsig); + } +#endif /* SYSCONS_VTY_RESUME */ + return 0; } #endif diff -urN sys.orig/i386/isa/wcd.c sys/i386/isa/wcd.c --- sys.orig/i386/isa/wcd.c Sun Sep 8 19:28:23 1996 +++ sys/i386/isa/wcd.c Tue Apr 14 08:45:14 1998 @@ -63,6 +63,12 @@ static #endif int wcdattach(struct atapi*, int, struct atapi_params*, int); +#ifdef ATAPI_DETACH +#ifndef ATAPI_STATIC +static +#endif +int wcddetach(struct atapi*, int, struct atapi_params*, int); +#endif /* ATAPI_DETACH */ #define NUNIT (NWDC*2) /* Max. number of devices */ #define UNIT(d) ((minor(d) >> 3) & 3) /* Unit part of minor device number */ @@ -234,7 +240,11 @@ }; struct wcd *wcdtab[NUNIT]; /* Drive info by unit number */ +#ifndef ATAPI_DETACH static int wcdnlun = 0; /* Number of configured drives */ +#else /* ATAPI_DETACH */ +static int wcd_unit_use = 0; /* Number of configured drives */ +#endif /* ATAPI_DETACH */ static void wcd_start (struct wcd *t); static void wcd_done (struct wcd *t, struct buf *bp, int resid, @@ -273,10 +283,21 @@ struct atapires result; int lun; +#ifndef ATAPI_DETACH if (wcdnlun >= NUNIT) { printf ("wcd: too many units\n"); return (0); } +#else /* ATAPI_DETACH */ + for (lun=0; lun < NUNIT; lun++) { + if (!(wcd_unit_use & (1 << lun))) + break; + } + if (lun >= NUNIT) { + printf ("wcd: too many units\n"); + return (0); + } +#endif /* ATAPI_DETACH */ if (!atapi_request_immediate) { printf("wcd: configuration error, ATAPI core code not present!\n"); printf("wcd: check `options ATAPI_STATIC' in your kernel config file!\n"); @@ -287,11 +308,20 @@ printf ("wcd: out of memory\n"); return (0); } +#ifndef ATAPI_DETACH wcdtab[wcdnlun] = t; +#else /* ATAPI_DETACH */ + wcdtab[lun] = t; +#endif /* ATAPI_DETACH */ bzero (t, sizeof (struct wcd)); t->ata = ata; t->unit = unit; +#ifndef ATAPI_DETACH lun = t->lun = wcdnlun++; +#else /* ATAPI_DETACH */ + t->lun = lun; + wcd_unit_use |= (1 << lun); +#endif /* ATAPI_DETACH */ t->param = ap; t->flags = F_MEDIA_CHANGED; t->refcnt = 0; @@ -346,6 +376,44 @@ return (1); } +#ifdef ATAPI_DETACH +#ifndef ATAPI_STATIC +static +#endif +int +wcddetach (struct atapi *ata, int unit, struct atapi_params *ap, int debug) +{ + struct wcd **t; + int lun; + + for (lun=0; lun < NUNIT; lun++) { + if ((wcd_unit_use & (1 << lun))) { + t = wcdtab + lun; + if ((*t)->ata->port == ata->port && + (*t)->ata->ctrlr == ata->ctrlr && + (*t)->unit == unit) + break; + } + } + if (lun >= NUNIT) { + printf ("wcd: too many units\n"); + return (0); + } + + t = wcdtab + lun; + if (((*t)->flags & F_BOPEN) || (*t)->refcnt) + /* The device is opened, cannot unload the driver. */ + return EBUSY; + (*t)->ata->attached[(*t)->unit] = 0; + free (*t, M_TEMP); + wcdtab[lun] = NULL; + + wcd_unit_use &= ~(1 << lun); + + return 0; +} +#endif /* ATAPI_DETACH */ + void wcd_describe (struct wcd *t) { char *m; @@ -412,8 +480,13 @@ /* Check that the device number is legal * and the ATAPI driver is loaded. */ +#ifndef ATAPI_DETACH if (lun >= wcdnlun || ! atapi_request_immediate) + return (ENXIO); +#else /* ATAPI_DETACH */ + if (!(wcd_unit_use & (1 << lun)) || ! atapi_request_immediate) return (ENXIO); +#endif /* ATAPI_DETACH */ t = wcdtab[lun]; /* On the first open, read the table of contents. */ @@ -1150,6 +1223,7 @@ { struct wcd **t; +#ifndef ATAPI_DETACH for (t=wcdtab; tflags & F_BOPEN) || (*t)->refcnt) /* The device is opened, cannot unload the driver. */ @@ -1159,6 +1233,22 @@ free (*t, M_TEMP); } wcdnlun = 0; +#else /* ATAPI_DETACH */ + for (int lun=0; lun < NUNIT; ++lun) + if (wcd_unit_use & (1 << lun)) { + t = wcdtab + lun; + if (((*t)->flags & F_BOPEN) || (*t)->refcnt) + /* The device is opened, cannot unload the driver. */ + return EBUSY; + } + for (int lun=0; lun < NUNIT; ++lun) + if (wcd_unit_use & (1 << lun)) { + t = wcdtab + lun; + (*t)->ata->attached[(*t)->unit] = 0; + free (*t, M_TEMP); + } + wcd_unit_use = 0; +#endif /* ATAPI_DETACH */ bzero (wcdtab, sizeof(wcdtab)); return 0; } diff -urN sys.orig/i386/isa/wd.c sys/i386/isa/wd.c --- sys.orig/i386/isa/wd.c Sat Jan 17 07:28:44 1998 +++ sys/i386/isa/wd.c Tue Apr 14 08:47:56 1998 @@ -66,6 +66,8 @@ #if NWDC > 0 +#include "card.h" /* PC-card Flash/Type3 ATA support */ + #include #include #include @@ -116,6 +118,11 @@ /* can't handle that in all cases */ #define WDOPT_32BIT 0x8000 #define WDOPT_SLEEPHACK 0x4000 +/* following two options are used for PC-card IDE/ATAPI devices */ +#define WDOPT_PACKED 0x2000 +#define WDOPT_BROKEN_SIGNATURE 0x1000 + + #define WDOPT_FORCEHD(x) (((x)&0x0f00)>>8) #define WDOPT_MULTIMASK 0x00ff @@ -185,6 +192,9 @@ int dk_currentiosize; /* current io size */ struct diskgeom dk_dd; /* device configuration data */ struct diskslices *dk_slices; /* virtual drives */ + int dk_altsts; + int dk_ctlr; + int dk_digin; }; #define WD_COUNT_RETRIES @@ -255,6 +265,149 @@ #endif +#if NCARD > 0 +#include +#include +#include +/* + * PC-Card (PCMCIA) specific code. + */ +static int card_intr(struct pccard_devinfo *); /* Interrupt handler */ +static void wdunload(struct pccard_devinfo *); /* Disable driver */ +static void wdsuspend(struct pccard_devinfo *); /* Suspend driver */ +static int wdinit(struct pccard_devinfo *, int); /* Init. driver */ +static int wdprobe_pccard(struct isa_device *); /* Probe PC-card */ + +static struct pccard_device wdinfo = +{ + "wdc", + wdinit, + wdunload, + card_intr, +/* wdsuspend, */ + 0, /* Attributes - presently unused */ + &bio_imask /* Interrupt mask for device */ +}; + +DATA_SET(pccarddrv_set, wdinfo); + +static int static_init = 1; +static int lunit_in_use = 0; +static int ctrlr_in_use = 0; + +/* + * Called when a power down is wanted. Shuts down the + * device and configures the device as unavailable (but + * still loaded...). A resume is done by calling + * edinit with first=0. This is called when the user suspends + * the system, or the APM code suspends the system. + */ +static void +wdsuspend(struct pccard_devinfo *devi) +{ + printf("wd%d: suspending\n", devi->isahd.id_unit); +} + +/* + * Initialize the device - called from Slot manager. + * if first is set, then initially check for + * the device's existence before initialising it. + * Once initialised, the device table may be set up. + */ +static int +wdinit(struct pccard_devinfo *devi, int first) +{ +/* + * dynamic configuration mode + */ + static_init = 0; +/* + * validate unit number. + */ + if (devi->isahd.id_unit >= NWDC) + return(ENODEV); +/* + * Probe the device. If a value is returned, the + * device was found at the location. + */ + if (first) { + if (wdprobe_pccard(&devi->isahd)==0) { + printf("Probe Failed\n"); + return(ENXIO); + } + if (wdattach(&devi->isahd)==0) { + printf("Attach Failed\n"); + return(ENXIO); + } + } +/* + * XXX TODO: + * If it was already inited before, the device structure + * should be already initialised. Here we should + * reset (and possibly restart) the hardware, but + * I am not sure of the best way to do this... + */ + return(0); +} + +/* + * wdunload - unload the driver and clear the table. + * XXX TODO: + * This is called usually when the card is ejected, but + * can be caused by the modunload of a controller driver. + * The idea is reset the driver's view of the device + * and ensure that any driver entry points such as + * read and write do not hang. + */ +static void +wdunload(struct pccard_devinfo *devi) +{ + int ctrlr = devi->isahd.id_unit; + int lunit; + + for (lunit=0; lunitdk_ctrlr == ctrlr && + wddrives[lunit]->dk_port == devi->isahd.id_iobase ) { + lunit_in_use &= ~(1<isahd.id_iobase); +#endif + + printf("wdc%d: unloading -- ", ctrlr); + if (wdtab[ctrlr].b_active != 0) + printf("damage!\n"); + else + printf("done\n"); +} + +/* + * card_intr - Shared interrupt called from + * front end of PC-Card handler. + */ +static int +card_intr(struct pccard_devinfo *devi) +{ + wdintr(devi->isahd.id_unit); + return(1); +} + +static int +wdprobe_pccard(struct isa_device *isa_dev) +{ + return wdprobe(isa_dev); +} + +#endif /* NCARD > 0 */ + + + /* * Here we use the pci-subsystem to find out, whether there is * a cmd640b-chip attached on this pci-bus. This public routine @@ -280,6 +433,14 @@ if (unit >= NWDC) return (0); +#if NCARD > 0 +/* + * If PC-Card probe required, then register driver with + * slot manager. + */ + if (!static_init && (ctrlr_in_use & (1< 0 */ du = malloc(sizeof *du, M_TEMP, M_NOWAIT); if (du == NULL) @@ -287,7 +448,15 @@ bzero(du, sizeof *du); du->dk_ctrlr = dvp->id_unit; du->dk_port = dvp->id_iobase; - + du->dk_altsts = wd_altsts; + du->dk_ctlr = wd_ctlr; + du->dk_digin = wd_digin; + + if (dvp->id_flags & WDOPT_PACKED) { + du->dk_altsts -= 0x1f8; + du->dk_ctlr -= 0x1f8; + du->dk_digin -= 0x1f8; + } /* check if we have registers that work */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* set unit 0 */ @@ -305,6 +474,8 @@ if (wdreset(du) == 0) goto reset_ok; #ifdef ATAPI + if (dvp->id_flags & WDOPT_BROKEN_SIGNATURE) + goto reset_ok; /* test for ATAPI signature */ outb(du->dk_port + wd_sdh, WDSD_IBM); /* master */ if (inb(du->dk_port + wd_cyl_lo) == 0x14 && @@ -367,6 +538,9 @@ } +#if NCARD > 0 + ctrlr_in_use |= (1< 0 */ free(du, M_TEMP); return (IO_WDCSIZE); @@ -387,6 +561,11 @@ int unit, lunit; struct isa_device *wdup; struct disk *du; +#if NCARD > 0 + static int once_registered = 0; + static int old_dkunit[NWD]; + int valid_units = 0; +#endif /* NCARD > 0 */ if (dvp->id_unit >= NWDC) return (0); @@ -408,16 +587,38 @@ if (wdup->id_iobase != dvp->id_iobase) continue; lunit = wdup->id_unit; + +#if NCARD > 0 + /* + * XXX + * for PCMCIA Flash ATA/Type III HDD cards. + * HOSOKAWA, Tatsumi + */ + if (lunit_in_use & (1< 0 */ + if (lunit >= NWD) continue; unit = wdup->id_physid; - +#if NCARD > 0 + du = wddrives[lunit]; + if (du == NULL) + du = malloc(sizeof *du, M_TEMP, M_NOWAIT); + if (du == NULL) + continue; +#else /* NCARD > 0 */ du = malloc(sizeof *du, M_TEMP, M_NOWAIT); if (du == NULL) continue; if (wddrives[lunit] != NULL) panic("drive attached twice"); +#endif /* NCARD > 0 */ wddrives[lunit] = du; TAILQ_INIT( &drive_queue[lunit]); bzero(du, sizeof *du); @@ -432,6 +633,15 @@ du->dk_unit = unit; du->dk_lunit = lunit; du->dk_port = dvp->id_iobase; + du->dk_altsts = wd_altsts; + du->dk_ctlr = wd_ctlr; + du->dk_digin = wd_digin; + + if (dvp->id_flags & WDOPT_PACKED) { + du->dk_altsts -= 0x1f8; + du->dk_ctlr -= 0x1f8; + du->dk_digin -= 0x1f8; + } /* * Use the individual device flags or the controller @@ -477,6 +687,13 @@ */ wdtimeout(du); +#if NCARD > 0 + lunit_in_use |= (1< 0 */ #ifdef DEVFS mynor = dkmakeminor(lunit, WHOLE_DISK_SLICE, RAW_PART); du->dk_bdev = devfs_add_devswf(&wd_bdevsw, mynor, @@ -498,10 +715,22 @@ * according to iostat. */ dk_wpms[dk_ndrive] = 4 * 1024 * 1024 / 2; +#if NCARD > 0 + old_dkunit[lunit] = +#endif /* NCARD > 0 */ du->dk_dkunit = dk_ndrive++; } else { +#if NCARD > 0 + old_dkunit[lunit] = +#endif /* NCARD > 0 */ du->dk_dkunit = -1; } +#if NCARD > 0 + } else { + /* reuse previous dk_dkunit */ + du->dk_dkunit = old_dkunit[lunit]; + } +#endif /* NCARD > 0 */ } else { free(du, M_TEMP); wddrives[lunit] = NULL; @@ -512,18 +741,38 @@ * Probe all free IDE units, searching for ATAPI drives. */ for (unit=0; unit<2; ++unit) { +#if NCARD > 0 + for (lunit=0; lunitdk_ctrlr == dvp->id_unit && + wddrives[lunit]->dk_unit == unit) + goto next; +#else /* NCARD > 0 */ for (lunit=0; lunitdk_ctrlr == dvp->id_unit && wddrives[lunit]->dk_unit == unit) goto next; +#endif /* NCARD > 0 */ #ifdef CMD640 +#if NCARD > 0 + if (atapi_attach (dvp->id_unit, unit, dvp->id_iobase)) { + atapictrlr = dvp->id_unit; + valid_units++; + } +#else /* NCARD > 0 */ if (atapi_attach (dvp->id_unit, unit, dvp->id_iobase)) atapictrlr = dvp->id_unit; -#else +#endif /* NCARD > 0 */ +#else /* CMD640 */ +#if NCARD > 0 + if (atapi_attach (dvp->id_unit, unit, dvp->id_iobase)) + valid_units++; +#else /* NCARD > 0 */ atapi_attach (dvp->id_unit, unit, dvp->id_iobase); -#endif +#endif /* NCARD > 0 */ +#endif /* CMD640 */ next: } -#endif +#endif /* ATAPI */ /* * Discard any interrupts generated by wdgetctlr(). wdflushirq() * doesn't work now because the ambient ipl is too high. @@ -538,6 +787,10 @@ wdtab[dvp->id_unit].b_active = 2; #endif +#if NCARD > 0 + if (!static_init && valid_units == 0) + return (0); /* no valid unit found */ +#endif /* NCARD > 0 */ return (1); } @@ -761,7 +1014,7 @@ (bp->b_flags & B_READ) ? "read" : "write", bp->b_bcount, blknum); else - printf(" %d)%x", du->dk_skip, inb(du->dk_port + wd_altsts)); + printf(" %d)%x", du->dk_skip, inb(du->dk_port + du->dk_altsts)); #endif lp = &du->dk_dd; @@ -869,7 +1122,7 @@ printf("cylin %ld head %ld sector %ld addr %x sts %x\n", cylin, head, sector, (int)bp->b_un.b_addr + du->dk_skip * DEV_BSIZE, - inb(du->dk_port + wd_altsts)); + inb(du->dk_port + du->dk_altsts)); #endif } @@ -1169,6 +1422,10 @@ lunit = dkunit(dev); if (lunit >= NWD || dktype(dev) != 0) return (ENXIO); +#if NCARD > 0 + if ((lunit_in_use & (1< 0 */ du = wddrives[lunit]; if (du == NULL) return (ENXIO); @@ -1835,6 +2092,9 @@ lunit = dkunit(dev); /* eventually support floppies? */ part = dkpart(dev); if (lunit >= NWD || (du = wddrives[lunit]) == NULL +#if NCARD > 0 + || (lunit_in_use & (1< 0 */ || du->dk_state < OPEN || (lp = dsgetlabel(dev, du->dk_slices)) == NULL) return (ENXIO); @@ -2049,9 +2309,9 @@ wdc = du->dk_port; (void)wdwait(du, 0, TIMEOUT); - outb(wdc + wd_ctlr, WDCTL_IDS | WDCTL_RST); + outb(wdc + du->dk_ctlr, WDCTL_IDS | WDCTL_RST); DELAY(10 * 1000); - outb(wdc + wd_ctlr, WDCTL_IDS); + outb(wdc + du->dk_ctlr, WDCTL_IDS); #ifdef ATAPI if (wdwait(du, WDCS_READY | WDCS_SEEKCMPLT, TIMEOUT) != 0) err = 1; /* no IDE drive found */ @@ -2063,7 +2323,7 @@ || (du->dk_error = inb(wdc + wd_error)) != 0x01) return (1); #endif - outb(wdc + wd_ctlr, WDCTL_4BIT); + outb(wdc + du->dk_ctlr, WDCTL_4BIT); return (err); } diff -urN sys.orig/i386/scsi/README_ncv_stg sys/i386/scsi/README_ncv_stg --- sys.orig/i386/scsi/README_ncv_stg Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/README_ncv_stg Tue Apr 14 08:45:15 1998 @@ -0,0 +1,4 @@ +ncr53c800, tmc18c30, and scsi_low is ported from NetBSD/pc98 (based on +NetBSD 1.2), but current porting status is not good because the style of +SCSI driver of NetBSD and FreeBSD is different. ++ diff -urN sys.orig/i386/scsi/ncr53c500/ncr53c500.c sys/i386/scsi/ncr53c500/ncr53c500.c --- sys.orig/i386/scsi/ncr53c500/ncr53c500.c Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/ncr53c500/ncr53c500.c Tue Apr 14 08:47:56 1998 @@ -0,0 +1,878 @@ +/* $NetBSD$ */ +/* $Id: ncr53c500.c,v 1.1.2.1 1997/12/11 14:00:47 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1995, 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NetBSD__ +#include +#endif /* __NetBSD__ */ +#ifdef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ + +/*************************************************** + * DEBUG + ***************************************************/ +#ifndef DDB +#define Debugger() panic("should call debugger here (ncr53c500.c)") +#endif /* ! DDB */ + +#define NCV_DEBUG +#ifdef NCV_DEBUG +int ncv_debug; + +struct ncv_statics { + int disconnect; + int reselect; +} ncv_statics[NTARGETS]; +#endif /* NCV_DEBUG */ + +/* static */ +static void ncv_pio_read __P((struct ncv_softc *, u_int8_t *, u_int)); +static void ncv_pio_write __P((struct ncv_softc *, u_int8_t *, u_int)); +static int ncv_synch __P((struct ncv_softc *, struct targ_info *, u_int, u_int)); +static inline int ncv_reselected __P((struct ncv_softc *)); +static inline int ncv_disconnected __P((struct ncv_softc *, struct ccb *cb)); +static inline void ncv_pdma_end __P((struct ncv_softc *sc, struct targ_info *)); +static inline void ncvhw_set_count __P((struct ncv_softc *sc, int)); +static inline u_int ncvhw_get_count __P((struct ncv_softc *sc)); +static inline void ncvhw_select_register_0 __P((struct ncv_softc *)); +static inline void ncvhw_select_register_1 __P((struct ncv_softc *)); +static inline void ncvhw_fpush __P((struct ncv_softc *, u_int8_t *, int)); + +static int ncv_world_start __P((struct ncv_softc *, int)); +static void ncvhw_bus_reset __P((struct ncv_softc *)); +static void ncvhw_reset __P((struct ncv_softc *)); +static void ncvhw_init __P((struct ncv_softc *)); +static void ncvhw_synch __P((struct ncv_softc *sc, struct targ_info *)); +static int ncvhw_power __P((struct ncv_softc *, u_int)); +static int ncvhw_start_selection __P((struct ncv_softc *sc, struct ccb *)); +static void ncvhw_attention __P((struct ncv_softc *)); + +struct scsi_low_funcs ncv_funcs = +{ + SC_LOW_RESET_T ncv_world_start, + SC_LOW_BUSRST_T ncvhw_bus_reset, + + SC_LOW_SELECT_T ncvhw_start_selection, + + SC_LOW_ATTEN_T ncvhw_attention, + SC_LOW_SYNCH_T ncv_synch, + + SC_LOW_INTR_T ncv_sequencer, + + NULL, /* SC_LOW_POWER_T ncvhw_power, */ +}; + +/************************************************************** + * hwfuncs + **************************************************************/ +static inline void +ncvhw_select_register_0(sc) + struct ncv_softc *sc; +{ + DECLARE_IOPORT(sc); + + OUTB(cr0_cfg4, sc->sc_hw.cfg4); +} + +static inline void +ncvhw_select_register_1(sc) + struct ncv_softc *sc; +{ + DECLARE_IOPORT(sc); + + OUTB(cr1_cfg5, sc->sc_hw.cfg5); +} + +static inline void +ncvhw_fpush(sc, buf, len) + struct ncv_softc *sc; + u_int8_t *buf; + int len; +{ + int ptr; + DECLARE_IOPORT(sc); + + for (ptr = 0; ptr < len; ptr ++) + OUTB(cr0_sfifo, buf[ptr]); +} + +int +ncvhw_check(sc) + struct ncv_softc *sc; +{ + DECLARE_IOPORT(sc); + + ncvhw_select_register_0(sc); + OUTB(cr0_cmd, CMD_NOP | CMD_DMA); + if (INB(cr0_cmd) != (CMD_NOP | CMD_DMA)) + return ENODEV; + + OUTB(cr0_cmd, CMD_NOP); + if (INB(cr0_cmd) != CMD_NOP) + return ENODEV; + + return 0; +} + +static void +ncvhw_reset(sc) + struct ncv_softc *sc; +{ + DECLARE_IOPORT(sc); + + ncvhw_select_register_0(sc); + + /* dummy cmd twice */ + OUTB(cr0_cmd, CMD_NOP); + OUTB(cr0_cmd, CMD_NOP); + + /* chip reset */ + OUTB(cr0_cmd, CMD_RSTCHIP); + + /* again dummy cmd twice */ + OUTB(cr0_cmd, CMD_NOP); + OUTB(cr0_cmd, CMD_NOP); +} + +static void +ncvhw_init(sc) + struct ncv_softc *sc; +{ + DECLARE_IOPORT(sc); + + ncvhw_select_register_0(sc); + OUTB(cr0_clk, sc->sc_hw.clk); + OUTB(cr0_srtout, SEL_TOUT); + OUTB(cr0_period, 0); + OUTB(cr0_offs, 0); + + OUTB(cr0_cfg1, sc->sc_hw.cfg1); + OUTB(cr0_cfg2, sc->sc_hw.cfg2); + OUTB(cr0_cfg3, sc->sc_hw.cfg3); + OUTB(cr0_tchsb, 0); + + ncvhw_select_register_1(sc); + OUTB(cr1_fstat, 0x0); + OUTB(cr1_pflag, 0x0); + OUTB(cr1_atacmd, ATACMD_ENGAGE); + + ncvhw_select_register_0(sc); +} + +static void +ncvhw_synch(sc, ti) + struct ncv_softc *sc; + struct targ_info *ti; +{ + DECLARE_IOPORT(sc); + + /* start selection */ + if (ti->ti_flags & SCSI_LOW_NOPARITY) + OUTB(cr0_cfg1, sc->sc_hw.cfg1); + else + OUTB(cr0_cfg1, sc->sc_hw.cfg1 | C1_PARENB); + OUTB(cr0_period, ti->ti_synch.period); + OUTB(cr0_offs, ti->ti_synch.offset); + OUTB(cr0_cfg3, ti->ti_synch.c3img); +} + +static int +ncvhw_power(sc, flags) + struct ncv_softc *sc; + u_int flags; +{ + DECLARE_IOPORT(sc); + + if (flags == SCSI_LOW_POWDOWN) + { + printf("%s power down\n", sc->sc_dvname); + ncvhw_select_register_1(sc); + OUTB(cr1_atacmd, ATACMD_POWDOWN); + } + else + { + switch (sc->sc_rstep) + { + case 0: + printf("%s resume step O\n", sc->sc_dvname); + ncvhw_select_register_1(sc); + OUTB(cr1_atacmd, ATACMD_ENGAGE); + break; + + case 1: + printf("%s resume step I\n", sc->sc_dvname); + ncvhw_reset(sc); + ncvhw_init(sc); + break; + } + } + + return 0; +} + +/************************************************************** + * scsi low interface + **************************************************************/ +static void +ncvhw_attention(sc) + struct ncv_softc *sc; +{ + DECLARE_IOPORT(sc); + + OUTB(cr0_cmd, CMD_SETATN); + delay(200); +} + +static void +ncvhw_bus_reset(sc) + struct ncv_softc *sc; +{ + DECLARE_IOPORT(sc); + + ncvhw_select_register_0(sc); + OUTB(cr0_cmd, CMD_FLUSH); + OUTB(cr0_cmd, CMD_RSTSCSI); + OUTB(cr0_cmd, CMD_NOP | CMD_DMA); +} + +static int +ncvhw_start_selection(sc, cb) + struct ncv_softc *sc; + struct ccb *cb; +{ + struct targ_info *ti = cb->ti; + int s; + DECLARE_IOPORT(sc); + + ID_MSG_SETUP(ti, cb); + ncvhw_select_register_0(sc); + + s = splhigh(); + + if (sc->sc_disc > 0 && (INB(cr0_stat) & STAT_INT)) + { + splx(s); + return 1; + } + + ncvhw_synch(sc, ti); + OUTB(cr0_dstid, ti->ti_id); + OUTB(cr0_cmd, CMD_FLUSH); + + OUTB(cr0_sfifo, ti->ti_msgout); + + if (cb->msgoutlen) + OUTB(cr0_cmd, CMD_SELATNS); + else + { + ncvhw_fpush(sc, cb->cmd, cb->cmdlen); + OUTB(cr0_cmd, CMD_SELATN); + } + + splx(s); + + SCSI_LOW_SETUP_PHASE(PH_SELSTART); + return 0; +} + +static int +ncv_world_start(sc, fdone) + struct ncv_softc *sc; + int fdone; +{ + struct targ_info *ti; + u_int8_t stat; + DECLARE_IOPORT(sc); + + if (sc->sc_flags & HW_INACTIVE) + return EBUSY; + + /* reset current nexus */ + scsi_low_reset_nexus((struct scsi_low_softc *) sc, fdone); + + /* hardware info init */ + for (ti = sc->sc_titab.tqh_first; ti; ti = ti->ti_chain.tqe_next) + { + ti->ti_state = UNIT_RDY; + ti->ti_synch.period = ti->ti_synch.offset = 0; + ti->ti_synch.c3img = sc->sc_hw.cfg3; + ti->ti_maxsynch.c3img = sc->sc_hw.cfg3; + ti->ti_maxsynch.period = sc->sc_hw.mperiod; + ti->ti_maxsynch.offset = sc->sc_hw.moffset; + + scsi_low_calcf(ti); + } + + ncvhw_reset(sc); + ncvhw_init(sc); + scsi_low_bus_reset((struct scsi_low_softc *) sc); + + ncvhw_select_register_0(sc); + INB(cr0_stat); + stat = INB(cr0_istat); + if ((stat & INTR_SBR) == 0) + return ENODEV; + delay(1000); + stat = INB(cr0_istat); + if (stat & INTR_SBR) + return ENODEV; + +#ifdef __NetBSD__ + softintr(sc->sc_mask); +#endif + return 0; +} + +static int +ncv_synch(sc, ti, period, offset) + struct ncv_softc *sc; + struct targ_info *ti; + u_int period, offset; +{ + u_int hwperiod; + + hwperiod = 1000 / ((sc->sc_hw.clk == 0) ? 40 : (5 * sc->sc_hw.clk)); + + if (period < 200 / 4 && period >= 100 / 4) + ti->ti_synch.c3img |= C3_FSCSI; + else + ti->ti_synch.c3img &= ~C3_FSCSI; + + period = ((period * 40 / hwperiod) + 5) / 10; + + ti->ti_synch.period = period & 0x1f; + ti->ti_synch.offset = offset; + + return 0; +} + +/************************************************************** + * PDMA + **************************************************************/ +static inline void +ncvhw_set_count(sc, count) + struct ncv_softc *sc; + int count; +{ + DECLARE_IOPORT(sc); + + OUTB(cr0_tclsb, (u_int8_t) count); + OUTB(cr0_tcmsb, (u_int8_t) (count >> NBBY)); + OUTB(cr0_tchsb, (u_int8_t) (count >> (NBBY * 2))); +} + +static inline u_int +ncvhw_get_count(sc) + struct ncv_softc *sc; +{ + u_int count; + DECLARE_IOPORT(sc); + + count = (u_int) INB(cr0_tclsb); + count |= ((u_int) INB(cr0_tcmsb)) << NBBY; + count |= ((u_int) INB(cr0_tchsb)) << (NBBY * 2); + return count; +} + +static inline void +ncv_pdma_end(sc, ti) + struct ncv_softc *sc; + struct targ_info *ti; +{ + int len; + DECLARE_IOPORT(sc); + + sc->sc_flags &= ~HW_PDMASTART; + if (ti->ti_phase == PH_DATA) + { + len = ncvhw_get_count(sc); + if ((sc->sc_direction & SCSI_LOW_READ) == 0) + len += (INB(cr0_sffl) & 0x1f); + + if ((u_int) len <= (u_int) sc->sc_scp.datalen) + { + sc->sc_scp.data += (sc->sc_scp.datalen - len); + sc->sc_scp.datalen = len; + if ((sc->sc_direction & SCSI_LOW_READ) && + sc->sc_tdatalen != len) + goto bad; + } + else + { +bad: + ti->ti_error |= PDMAERR; + printf("%s strange count hw 0x%x soft 0x%x tlen 0x%x\n", + sc->sc_dvname, len, sc->sc_scp.datalen, + sc->sc_tdatalen); + } + } + else + { + printf("%s data phase miss\n", sc->sc_dvname); + ti->ti_error |= PDMAERR; + } + + ncvhw_select_register_1(sc); + OUTB(cr1_fstat, 0); + ncvhw_select_register_0(sc); +} + +static void +ncv_pio_read(sc, buf, reqlen) + struct ncv_softc *sc; + u_int8_t *buf; + u_int reqlen; +{ + int tout = sc->sc_wc; + register int len; + register u_int8_t fstat; + DECLARE_IOPORT(sc); + + ncvhw_select_register_1(sc); + OUTB(cr1_pflag, 0); + + ncvhw_select_register_0(sc); + ncvhw_set_count(sc, reqlen); + OUTB(cr0_cmd, CMD_TRANS | CMD_DMA); + + ncvhw_select_register_1(sc); + OUTB(cr1_fstat, FIFO_EN); + sc->sc_flags |= HW_PDMASTART; + + while (reqlen >= FIFO_F_SZ && tout > 0) + { + fstat = INB(cr1_fstat); + if (fstat & FIFO_F) + { + INSL(cr1_fdata, buf, FIFO_F_SZ / 4); + buf += FIFO_F_SZ; + reqlen -= FIFO_F_SZ; + continue; + } + else if (fstat & FIFO_BRK) + break; + + tout --; + } + + if (reqlen >= FIFO_2_SZ) + { + fstat = INB(cr1_fstat); + if (fstat & FIFO_2) + { + INSL(cr1_fdata, buf, FIFO_2_SZ / 4); + buf += FIFO_2_SZ; + reqlen -= FIFO_2_SZ; + } + } + + while (reqlen > 0 && tout > 0) + { + fstat = INB(cr1_fstat); + if ((fstat & FIFO_E) == 0) + { + *buf++ = INB(cr1_fdata); + reqlen --; + continue; + } + else if (fstat & FIFO_BRK) + break; + tout --; + } + + ncvhw_select_register_0(sc); + sc->sc_tdatalen = reqlen; + + if (tout <= 0) + printf("%s pio read timeout\n", sc->sc_dvname); +} + +static void +ncv_pio_write(sc, buf, reqlen) + struct ncv_softc *sc; + u_int8_t *buf; + u_int reqlen; +{ + int tout = sc->sc_wc; + register u_int8_t fstat; + DECLARE_IOPORT(sc); + + ncvhw_select_register_1(sc); + OUTB(cr1_pflag, 0); + + ncvhw_select_register_0(sc); + ncvhw_set_count(sc, reqlen); + OUTB(cr0_cmd, CMD_TRANS | CMD_DMA); + + ncvhw_select_register_1(sc); + OUTB(cr1_fstat, FIFO_EN); + sc->sc_flags |= HW_PDMASTART; + + while (reqlen >= FIFO_F_SZ && tout > 0) + { + fstat = INB(cr1_fstat); + if (fstat & FIFO_BRK) + goto done; + + if (fstat & FIFO_E) + { + OUTSL(cr1_fdata, buf, FIFO_F_SZ / 4); + buf += FIFO_F_SZ; + reqlen -= FIFO_F_SZ; + } + else + tout --; + } + + while (reqlen > 0 && tout > 0) + { + fstat = INB(cr1_fstat); + if (fstat & FIFO_BRK) + break; + + if ((fstat & FIFO_F) == 0) /* fifo not full */ + { + OUTB(cr1_fdata, *buf++); + reqlen --; + } + else + tout --; + } + +done: + ncvhw_select_register_0(sc); + + if (tout <= 0) + printf("%s pio write timeout\n", sc->sc_dvname); +} + +/************************************************************** + * disconnect & reselect (HW low) + **************************************************************/ +static inline int +ncv_reselected(sc) + struct ncv_softc *sc; +{ + struct ccb *cb; + u_int sid, imsg; + DECLARE_IOPORT(sc); + + if ((INB(cr0_sffl) & 0x1f) != 2) + { + printf("%s illegal fifo bytes\n", sc->sc_dvname); + return 0; + } + + sid = (u_int) INB(cr0_sfifo); + imsg = (u_int) INB(cr0_sfifo); + if ((imsg & 0x80) == 0) + { + printf("%s lost msg identify\n", sc->sc_dvname); + return 0; + } + + sid = ffs(sid) - 1; + cb = scsi_low_reselected((struct scsi_low_softc *) sc, sid, imsg & 0x7); + if (cb == NULL) + return 0; + +#ifdef NCV_DEBUG + ncv_statics[sid].reselect ++; +#endif + ncvhw_synch(sc, cb->ti); + OUTB(cr0_dstid, sid); + OUTB(cr0_cmd, CMD_MSGOK); + + return 1; +} + +static inline int +ncv_disconnected(sc, cb) + struct ncv_softc *sc; + struct ccb *cb; +{ + DECLARE_IOPORT(sc); + + OUTB(cr0_cmd, CMD_FLUSH); + OUTB(cr0_cfg1, sc->sc_hw.cfg1); + OUTB(cr0_cmd, CMD_ENSEL); + +#ifdef NCV_DEBUG + if (sc->sc_msgphase == DISCASSERT) + ncv_statics[cb->ti->ti_id].disconnect ++; +#endif /* NCV_DEBUG */ + + scsi_low_disconnected((struct scsi_low_softc *) sc, cb); + scsi_low_start((struct scsi_low_softc *) sc); + + return 1; +} + +/************************************************************** + * SEQUENCER + **************************************************************/ +int +ncv_sequencer(sc) + struct ncv_softc *sc; +{ + struct targ_info *ti; + struct ccb *cb; + int len; + u_int8_t status, ireason; + DECLARE_IOPORT(sc); + + if (sc->sc_flags & HW_INACTIVE) + return 0; + + /******************************************** + * Status + ********************************************/ + ncvhw_select_register_0(sc); + status = INB(cr0_stat); + if ((status & STAT_INT) == 0) + return 0; + + ireason = INB(cr0_istat); + if (ireason & INTR_SBR) + { + u_int8_t val; + + /* avoid power off hangup */ + val = INB(cr0_cfg1); + OUTB(cr0_cfg1, val | C1_SRR); + + /* status init */ + ti = sc->sc_titab.tqh_first; + for ( ; ti; ti = ti->ti_chain.tqe_next) + ti->ti_state = UNIT_RDY; + + printf("%s bus reset (power off ?)\n", sc->sc_dvname); + return 1; + } + + /******************************************** + * Debug section + ********************************************/ +#ifdef NCV_DEBUG + if (ncv_debug) + { + scsi_low_print((struct scsi_low_softc *) sc, NULL); + printf("%s st %x ist %x\n\n", sc->sc_dvname, + status, ireason); + if (ncv_debug > 1) + Debugger(); + } +#endif /* NCV_DEBUG */ + + /******************************************** + * Reselect or Disconnect or Nexus check + ********************************************/ + /* (I) reselect */ + if (ireason == INTR_RESELECT) + { + if ((status & PHASE_MASK) == MESSAGE_IN_PHASE) + return ncv_reselected(sc); + + return 1; + } + + /* (II) nexus */ + if ((cb = sc->sc_nexus) == NULL) + return 0; + + /******************************************** + * Internal scsi phase + ********************************************/ + ti = cb->ti; + switch (ti->ti_phase) + { + case PH_SELSTART: + sc->sc_selid = NULL; + + if ((ireason & (INTR_SBR | INTR_DIS | INTR_ILL)) || + (status & (STAT_PE | STAT_GE))) + goto abort; + + if (cb->msgoutlen == 0) + { + if ((status & PHASE_MASK) != MESSAGE_OUT_PHASE) + break; + + SCSI_LOW_SETUP_PHASE(PH_SELECTED); + printf("%s msg identify failed\n", sc->sc_dvname); + } + else + { + if ((status & PHASE_MASK) != MESSAGE_OUT_PHASE) + { + ti->ti_error |= FATALIO; + scsi_low_restart((struct scsi_low_softc *) sc, + "msgout error"); + return 1; + } + + SCSI_LOW_SETUP_PHASE(PH_MSGOUTEXP); + } + break; + + default: + if (sc->sc_selid) + { + scsi_low_print((struct scsi_low_softc *) sc, ti); + panic("%s internal error\n", sc->sc_dvname); + } + + if (sc->sc_flags & HW_PDMASTART) + ncv_pdma_end(sc, ti); + +abort: + if (status & (STAT_PE | STAT_GE)) + { + ti->ti_error |= PARITYERR; + scsi_low_restart((struct scsi_low_softc *) sc, + "scsi bus error"); + return 1; + } + + if (ireason & (INTR_DIS | INTR_ILL)) + { + if (ireason & INTR_ILL) + { + ti->ti_error |= FATALIO; + scsi_low_restart((struct scsi_low_softc *) sc, + "illegal cmd"); + return 1; + } + + return ncv_disconnected(sc, cb); + } + } + + /******************************************** + * Scsi phase sequencer + ********************************************/ + switch (status & PHASE_MASK) + { + case DATA_OUT_PHASE: /* data out */ + SCSI_LOW_SETUP_PHASE(PH_DATA); + sc->sc_direction = SCSI_LOW_WRITE; + ncv_pio_write(sc, sc->sc_scp.data, sc->sc_scp.datalen); + break; + + case DATA_IN_PHASE: /* data in */ + SCSI_LOW_SETUP_PHASE(PH_DATA); + sc->sc_direction = SCSI_LOW_READ; + ncv_pio_read(sc, sc->sc_scp.data, sc->sc_scp.datalen); + break; + + case COMMAND_PHASE: /* cmd out */ + SCSI_LOW_SETUP_PHASE(PH_CMD); + OUTB(cr0_cmd, CMD_FLUSH); + ncvhw_fpush(sc, cb->cmd, cb->cmdlen); + OUTB(cr0_cmd, CMD_TRANS); + break; + + case STATUS_PHASE: /* status in */ + SCSI_LOW_SETUP_PHASE(PH_COMPSEQ); + OUTB(cr0_cmd, CMD_FLUSH); + OUTB(cr0_cmd, CMD_ICCS); + break; + + default: + break; + + case MESSAGE_OUT_PHASE: /* msg out */ + OUTB(cr0_cmd, CMD_FLUSH); + + len = scsi_low_msgout((struct scsi_low_softc *) sc, ti, cb); + if (len == 0) + { + OUTB(cr0_sfifo, ti->ti_msgout); + OUTB(cr0_cmd, CMD_TRANS); + if (cb->msgoutlen == 0) + OUTB(cr0_cmd, CMD_RSTATN); + } + else if (len > 0) + { + ncvhw_fpush(sc, cb->msgout, len); + OUTB(cr0_cmd, CMD_TRANS); + OUTB(cr0_cmd, CMD_RSTATN); + } + break; + + case MESSAGE_IN_PHASE: /* msg in */ + len = INB(cr0_sffl) & 0x1f; + if (ti->ti_phase == PH_COMPSEQ) + { + if ((ireason & INTR_FC) && len == 2) + { + ti->ti_status = + INB(cr0_sfifo); + len --; + } + else + { + scsi_low_restart((struct scsi_low_softc *) sc, + "compseq error"); + + break; + } + } + else if (ireason & INTR_BS) + { + OUTB(cr0_cmd, CMD_FLUSH); + OUTB(cr0_cmd, CMD_TRANS); + break; + } + + SCSI_LOW_SETUP_PHASE(PH_MSGIN); + + if ((ireason & INTR_FC) && len == 1) + { + ti->ti_msgin[ti->ti_msginptr++] = + INB(cr0_sfifo); + scsi_low_msgin((struct scsi_low_softc *) sc, cb); + OUTB(cr0_cmd, CMD_MSGOK); + } + else + { + ti->ti_error |= MSGERR; + printf("%s st %x ist %x\n\n", sc->sc_dvname, + status, ireason); + scsi_low_restart((struct scsi_low_softc *) sc, + "hw msgin error"); + } + break; + } + + return 1; +} diff -urN sys.orig/i386/scsi/ncr53c500/ncr53c500hw.h sys/i386/scsi/ncr53c500/ncr53c500hw.h --- sys.orig/i386/scsi/ncr53c500/ncr53c500hw.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/ncr53c500/ncr53c500hw.h Tue Apr 14 08:45:17 1998 @@ -0,0 +1,72 @@ +/* $NetBSD$ */ +/* $Id: ncr53c500hw.h,v 1.1.2.1 1997/12/11 14:00:50 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __NCR53C500HW_H_ +#define __NCR53C500HW_H_ + +#ifndef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ + +struct ncv_hw +{ + /* configuration images */ + u_int8_t cfg1; + u_int8_t cfg2; + u_int8_t cfg3; + u_int8_t cfg4; + u_int8_t cfg5; + + /* synch */ + u_int8_t clk; + u_int8_t mperiod; + u_int8_t moffset; +}; + +/* dvcfg */ +#ifdef __FreeBSD__ +#define NCV_C5IMG(flags) ((flags >> 24) & 0xff) +#define NCV_CLKFACTOR(flags) ((flags>> 16) & 0x0f) +#else /* __NetBSD__ */ +#define NCV_C5IMG(flags) ((DVCFG_MAJOR(flags) >> 8) & 0xff) +#define NCV_CLKFACTOR(flags) (DVCFG_MAJOR(flags) & 0x0f) +#endif +#define NCVHWCFG_MAX10M 0x01 +#define NCVHWCFG_SCSI1 0x02 +#define NCVHWCFG_SLOW 0x04 +#define NCVHWCFG_FIFOBUG 0x08 +#ifdef __FreeBSD__ +#define NCV_SPECIAL(flags) ((flags >> 20) & 0x0f) +#else /* __NetBSD__ */ +#define NCV_SPECIAL(flags) ((DVCFG_MAJOR(flags) >> 4) & 0x0f) +#endif +#endif /* !__NCR53C500HW_H_ */ diff -urN sys.orig/i386/scsi/ncr53c500/ncr53c500hw.lst sys/i386/scsi/ncr53c500/ncr53c500hw.lst --- sys.orig/i386/scsi/ncr53c500/ncr53c500hw.lst Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/ncr53c500/ncr53c500hw.lst Tue Apr 14 08:45:18 1998 @@ -0,0 +1,43 @@ +/* $NetBSD$ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +static struct ncv_hw ncv_template = { + HOST_SCSI_ID, + C2_FE | C2_SCSI2, + C3_FCLK, + C4_ANE, + 0x80, + + CLK_40M_F, + + 200 / 4, + 15, +}; diff -urN sys.orig/i386/scsi/ncr53c500/ncr53c500if.c sys/i386/scsi/ncr53c500/ncr53c500if.c --- sys.orig/i386/scsi/ncr53c500/ncr53c500if.c Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/ncr53c500/ncr53c500if.c Tue Apr 14 08:47:57 1998 @@ -0,0 +1,479 @@ +/* $Id: ncr53c500if.c,v 1.1.2.1 1997/12/11 14:00:53 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1995, 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NetBSD__ +#include +#endif /* __NetBSD__ */ +#ifdef __FreeBSD__ +#include +#include "ncv.h" +#endif /* __FreeBSD__ */ + +/*************************************************** + * PARAMS + ***************************************************/ +#define NCV_MAX_CCB (2 * (NTARGETS - 1)) + +/*************************************************** + * ISA DEVICE STRUCTURE + ***************************************************/ +#ifdef __NetBSD__ +int ncvintr __P((void *)); + +struct cfdriver ncv_cd = { + NULL, "ncv", DV_DULL +}; + +struct scsi_device ncv_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +struct scsi_adapter ncv = { + scsi_low_scsi_cmd, + scsi_low_scsi_minphys, + scsi_low_target_open, + 0, +}; +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ + +/* pccard support */ +#if NCARD > 0 +static int ncv_card_intr(struct pccard_devinfo *); +static void ncv_card_unload(struct pccard_devinfo *); +static void ncv_card_suspend(struct pccard_devinfo *); +static int ncv_card_resume(struct ncv_softc *); +static int ncv_card_init(struct pccard_devinfo *, int); + +static struct pccard_device ncv_info = { + "ncv", + ncv_card_init, + ncv_card_unload, + ncv_card_intr, +/* ncv_card_suspend, */ + 0, /* Attributes - presently unused */ + &bio_imask /* Interrupt mask for device */ + /* This should also include net_imask?? */ +}; + +DATA_SET(pccarddrv_set, ncv_info); + +#ifdef SCSI_DETACH +static void ncvdetach(struct isa_device *dev); +#endif +#endif /* NCARD */ + +struct isa_driver ncvdriver = { + ncvprobe, + ncvattach, + "ncv" +}; + +struct scsi_device ncv_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "ncv", + 0, + {0, 0} +}; + +static struct scsi_adapter ncv = { + scsi_low_scsi_cmd, + scsi_low_scsi_minphys, + scsi_low_target_open, + 0, + ncv_adapter_info, + "ncv", + {0, 0}, +}; +u_int32_t ncv_adapter_info (int unit) +{ + return 1; +} + +static struct ncv_softc *ncvdata[NNCV]; +#endif /* __FreeBSD__ */ + +/************************************************************** + * DECLARE + **************************************************************/ +extern int delaycount; +extern struct scsi_low_funcs ncv_funcs; + +/************************************************************** + * General probe attach + **************************************************************/ +#ifdef __FreeBSD__ +#if NCARD > 0 +static void +ncv_card_suspend(struct pccard_devinfo *devi) +{ + struct ncv_softc *sc = ncvdata[devi->isahd.id_unit]; + + printf("%s: suspending\n", sc->sc_dvname); +} + +static int +ncv_card_resume(struct ncv_softc *sc) +{ +#if 0 /* XXX ncv_init */ + if (!ncv_init(sc)) + return (ENXIO); +#endif + printf("%s: resumed\n", sc->sc_dvname); + return (0); +} + +static int +ncv_card_init(struct pccard_devinfo *devi, int first) +{ + int unit = devi->isahd.id_unit; + struct ncv_softc *sc = ncvdata[unit]; + + if (NNCV <= unit) + return (ENODEV); + if (!first) + return (ncv_card_resume(sc)); + else + { + /* XXX -- ncv_init */ + printf("probe ncv\n"); + if (ncvprobe(&devi->isahd) == 0) + return (ENXIO); + printf("attach ncv\n"); + if (ncvattach(&devi->isahd) == 0) + return (ENXIO); + } + return (0); +} + +static void +ncv_card_unload(struct pccard_devinfo *devi) +{ + struct ncv_softc *sc = ncvdata[devi->isahd.id_unit]; + + printf("%s: unload\n", sc->sc_dvname); +/* scsi_low_deactivate((struct scsi_low_softc *)sc); */ +#ifdef SCSI_DETACH + ncvdetach(&devi->isahd); +#endif +} + +static int +ncv_card_intr(struct pccard_devinfo *devi) +{ + + (void) ncv_sequencer(ncvdata[devi->isahd.id_unit]); + return 1; +} +#endif /* NCARD > 0 */ +#endif /* FreeBSD */ + +static inline int +ncvhw_probe (struct ncv_softc *sc, int flags) +{ + + if (NCV_CLKFACTOR(flags) > CLK_35M_F) + { + printf("%s invalid dvcfg flags\n", sc->sc_dvname); + return (-1); + } + + sc->sc_hw = ncv_template; + if (NCV_C5IMG(flags) != 0) + { + sc->sc_hw.cfg5 = NCV_C5IMG(flags); + sc->sc_hw.clk = NCV_CLKFACTOR(flags); + + if (NCV_SPECIAL(flags) & NCVHWCFG_MAX10M) + sc->sc_hw.mperiod = 100 / 4; + + /* XXX: + * RATOC scsi cards have fatal fifo asic bug. + * To avoid it, currently make sync offset 0 (async)! + */ + if (NCV_SPECIAL(flags) & NCVHWCFG_FIFOBUG) + { + sc->sc_hw.mperiod = 0; + sc->sc_hw.moffset = 0; + } + + if (NCV_SPECIAL(flags) & NCVHWCFG_SCSI1) + sc->sc_hw.cfg2 &= ~C2_SCSI2; + + if (NCV_SPECIAL(flags) & NCVHWCFG_SLOW) + sc->sc_hw.cfg1 |= C1_SLOW; + } + + /* setup configuration image 3 */ + if (sc->sc_hw.clk != CLK_40M_F && sc->sc_hw.clk <= CLK_25M_F) + sc->sc_hw.cfg3 &= ~C3_FCLK; + +#ifdef __FreeBSD__ + sc->sc_cfgflags = (flags & 0xffff); +#else + sc->sc_cfgflags = DVCFG_MINOR(flags); +#endif + sc->sc_wc = delaycount * 2000; /* 2 sec */ + sc->sc_funcs = &ncv_funcs; + + if (ncvhw_check(sc)) + return (-1); + + return (0); +} + +#ifdef __NetBSD__ +int +ncvprobe(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ncv_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + struct ncv_hw *hw; + DECLARE_IOPORT(sc); + + strcpy(sc->sc_dvname, sc->sc_dev.dv_xname); + if (ia->ia_iobase == IOBASEUNK || ia->ia_irq == IRQUNK) + return 0; + + bc = ia->ia_bc; + if (bus_io_map(bc, ia->ia_iobase, NCVIOSZ, &ioh)) + return 0; + sc->sc_bc = bc; + sc->sc_ioh = ioh; + sc->sc_dealyioh = ia->ia_delayioh; + + if (ncvhw_probe (sc, ia->ia_cfgflags) < 0) + { + bus_io_unmap(bc, ioh, NCVIOSZ); + return 0; + } + else if (scsi_low_attach((struct scsi_low_softc *) sc)) + return 0; + else + { + ia->ia_iosize = NCVIOSZ; + return 1; + } +} +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +int ncvprobe(struct isa_device *dev) +{ + struct ncv_softc *sc; + int unit = dev->id_unit; + struct ncv_hw *hw; + char dvname[SCSI_LOW_DVNAME_LEN]; + DECLARE_IOPORT(sc); + + sprintf(dvname, "ncv%d", unit); + + if (unit >= NNCV && !dev->id_reconfig) + { + printf("%s: unit number too high\n", dvname); + return (0); + } + + if (dev->id_iobase == 0) + { + printf("%s: no ioaddr is given\n", dvname); + return (0); + } + if (dev->id_reconfig || ncvdata[unit] != NULL) + { + sc = ncvdata[unit]; + sc->sc_ioport = dev->id_iobase; + if (ncvhw_probe(sc, dev->id_flags) < 0) + return (0); + } + else + { + sc = malloc(sizeof(struct ncv_softc), M_TEMP, M_NOWAIT); + if (sc == NULL) + { + printf("%s: cannot malloc!\n", sc->sc_dvname); + return (0); + } + bzero(sc, sizeof(struct ncv_softc)); + strcpy (sc->sc_dvname, dvname); + sc->unit = unit; + ncvdata[unit] = sc; + sc->sc_ioport = dev->id_iobase; + + if (ncvhw_probe(sc, dev->id_flags) < 0) + { + free(sc, M_TEMP); + ncvdata[unit] = NULL; + return (0); + } + } + return (NCVIOSZ); +} +#endif + +int +ncvprint(aux, name) + void *aux; + char *name; +{ + + if (name != NULL) + printf("%s: scsibus ", name); + return (UNCONF); +} + +#ifdef __FreeBSD__ +#define adapter_target adapter_targ +#define openings opennings +#endif + +#ifdef __FreeBSD__ +static inline void +ncv_setup_sc(register struct ncv_softc *sc) +#else +inline void ncv_setup_sc(sc) + register struct ncv_softc *sc; +#endif +{ + + scsi_low_init_ccbque(NCV_MAX_CCB); + TAILQ_INIT(&sc->sc_start); + + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = 7; + sc->sc_link.openings = 2; + sc->sc_link.adapter = &ncv; + sc->sc_link.device = &ncv_dev; +} + +#undef adapter_target +#undef openings + +#ifdef __NetBSD__ +void +ncvattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct ncv_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + + printf("\n"); + sc->sc_bc = ia->ia_bc; + sc->sc_dealyioh = ia->ia_delayioh; + if (bus_io_map(sc->sc_bc, ia->ia_iobase, NCVIOSZ, &sc->sc_ioh)) + panic("%s: couldn't map io\n", sc->sc_dvname); + + ncv_setup_sc(sc); + sc->sc_mask = (1 << ia->ia_irq); + sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, + IPL_BIO, ncvintr, sc); + + config_found(self, &sc->sc_link, ncvprint); + timeout(scsi_low_timeout, sc, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); +} +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ + +#ifdef SCSI_DETACH +static void +ncvdetach(struct isa_device *dev) +{ + int unit = dev->id_unit; + struct scsibus_data *scbus; + + scbus = (struct scsibus_data *)scsi_extend_get(ncvdata[unit]->sc_link.scsibus); + scsi_detachdev(scbus); +} +#endif /* SCSI_DETACH */ + +int +ncvattach(dev) + struct isa_device *dev; +{ + int unit = dev->id_unit; + struct ncv_softc *sc = ncvdata[unit]; + struct scsibus_data *scbus; + + if (scsi_low_attach((struct scsi_low_softc *) sc)) + { + printf("%s: scsi low attach failed\n", sc->sc_dvname); + return (0); + } + + ncv_setup_sc(sc); + sc->sc_link.adapter_unit = unit; + + /* + * Prepare the scsibus_data area for the upperlevel + * scsi code. + */ + scbus = scsi_alloc_bus(); + if (!scbus) + return (0); + scbus->adapter_link = &sc->sc_link; + /* + * ask the adapter what subunits are present + */ + scsi_attachdevs(scbus); + timeout(scsi_low_timeout, sc, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + return 1; +} +#endif /* __FreeBSD__ */ + +#ifdef __NetBSD__ +int ncvintr (arg) + void *arg; +{ + return (ncv_sequencer((struct ncv_softc *)arg)); +} +#endif + +#ifdef __FreeBSD__ +void +ncvintr (unit) + int unit; +{ + (void) ncv_sequencer(ncvdata[unit]); +} +#endif diff -urN sys.orig/i386/scsi/ncr53c500/ncr53c500if.h sys/i386/scsi/ncr53c500/ncr53c500if.h --- sys.orig/i386/scsi/ncr53c500/ncr53c500if.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/ncr53c500/ncr53c500if.h Tue Apr 14 08:45:20 1998 @@ -0,0 +1,109 @@ +/* + * Copyright (c) HONDA Naofumi, KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* $Id: ncr53c500if.h,v 1.1.2.1 1997/12/11 14:00:54 itojun Exp $ */ + +/*************************************************** + * BUS IO MAPPINGS & NCR53C500 specific inclusion + ***************************************************/ +#ifdef __NetBSD__ +#include +#include +#include +#include +#include +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +#include +#include +#include +#include +#include + +#include "apm.h" +#if NAPM > 0 +#include +#endif /* NAPM > 0 */ + +/* pccard support */ +#include "card.h" +#if NCARD > 0 +#include +#include +#include +#include +#endif /* NCARD > 0 */ +#endif /* __FreeBSD__ */ + +int ncv_sequencer __P((struct ncv_softc *sc)); +int ncvhw_check __P((struct ncv_softc *)); + +/* (II) os depend declare */ +#ifdef __NetBSD__ +int ncvprobe __P((struct device *, struct device *, void *)); +void ncvattach __P((struct device *, struct device *, void *)); +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +int ncvprobe __P((struct isa_device *dev)); +int ncvattach __P((struct isa_device *dev)); +#endif /* __FreeBSD__ */ + +#ifdef __FreeBSD__ +u_int32_t ncv_adapter_info __P((int)); +extern int dma_init_flag; +#define softintr(y) ipending |= (y) +#endif /* __FreeBSD__ */ + + +#ifdef __NetBSD__ +#define DECLARE_IOPORT(sc) \ + register bus_chipset_tag_t bc = (sc)->sc_bc; \ + register bus_io_handle_t ioh = (sc)->sc_ioh; +#define OUTB(o, val) \ + bus_io_write_1 (bc, ioh, (o), (val)) +#define OUTSL(o,b,l) \ + bus_io_write_multi_4(bc, ioh, (o), (b), (l)) +#define INB(o) \ + bus_io_read_1 (bc, ioh, (o)) +#define INSL(o,b,l) \ + bus_io_read_multi_4(bc, ioh, (o), (b), (l)) +#endif + +#ifdef __FreeBSD__ +#define DECLARE_IOPORT(sc) \ + register int ioport = (sc)->sc_ioport; +#define OUTB(o, val) \ + outb((o)+ioport, (val)) +#define OUTSL(o,b,l) \ + outsl((o)+ioport, (b), (l)) +#define INB(o) \ + inb((o)+ioport) +#define INSL(o,b,l) \ + insl((o)+ioport, (b), (l)) +#endif diff -urN sys.orig/i386/scsi/ncr53c500/ncr53c500reg.h sys/i386/scsi/ncr53c500/ncr53c500reg.h --- sys.orig/i386/scsi/ncr53c500/ncr53c500reg.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/ncr53c500/ncr53c500reg.h Tue Apr 14 08:45:20 1998 @@ -0,0 +1,184 @@ +/* $NetBSD$ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1995, 1996 Naofumi HONDA. + * Copyright (c) 1995, 1996 Kouichi Matsuda. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NCR53C500REG_H_ +#define _NCR53C500REG_H_ + +/* Control Register Set 0 */ +#define NCVIOSZ 0x10 + +#define cr0_tclsb 0x00 /* RW - Transfer Count Low */ +#define cr0_tcmsb 0x01 /* RW - Transfer Count Mid */ +#define cr0_sfifo 0x02 /* RW - FIFO data */ +#define cr0_cmd 0x03 /* RW - Command (2 deep) */ +#define cr0_stat 0x04 /* RO - Status */ +#define cr0_dstid 0x04 /* WO - Select/Reselect Bus ID */ +#define cr0_istat 0x05 /* RO - Interrupt */ +#define cr0_srtout 0x05 /* WO - Select/Reselect Timeout */ +#define cr0_seq 0x06 /* RO - Sequence Step */ +#define cr0_period 0x06 /* WO - Synch Transfer Period */ +#define cr0_sffl 0x07 /* RO - FIFO FLags */ +#define cr0_offs 0x07 /* WO - Synch Ofset */ +#define cr0_cfg1 0x08 /* RW - Configuration #1 */ +#define cr0_clk 0x09 /* WO - Clock Conversion Factor */ +#define cr0_tst 0x0a /* WO - Test (Chip Test Only) */ +#define cr0_cfg2 0x0b /* RW - Configuration #2 */ +#define cr0_cfg3 0x0c /* RW - Configuration #3 */ +#define cr0_cfg4 0x0d /* RW - Configuration #4 */ +#define cr0_tchsb 0x0e /* RW - Transfer Count High */ +#define cr0_fifo_bottom 0x0f /* WO - FIFO bottom */ + +/* Control Register Set 1 */ +#define cr1_jumper 0x00 /* RW - Jumper Sense Port */ +#define cr1_sram_ptr 0x01 /* RW - SRAM Address Pointer */ +#define cr1_sram_data 0x02 /* RW - SRAM Data */ +#define cr1_fdata 0x04 /* RW - PIO FIFO */ +#define cr1_fstat 0x08 /* RW - PIO Status */ +#define cr1_atacmd 0x09 /* RW - ATA Command/Status */ +#define cr1_ataerr 0x0a /* RW - ATA Features/Error */ +#define cr1_pflag 0x0b /* RW - PIO Flag Interrupt Enable */ +#define cr1_cfg5 0x0d /* RW - Configuration #5 */ +#define cr1_sig 0x0e /* RO - Signature */ +#define cr1_cfg6 0x0f /* RW - Configuration #6 */ + +/* atacmd (MPS110 ONLY) */ +#define ATACMD_POWDOWN 0x2d +#define ATACMD_ENGAGE 0x24 + +/* cfg4 */ +#define C4_ANE 0x04 + +/* cfg3 */ +#define C3_NULL 0x00 +#define C3_FCLK 0x08 /* Fast SCSI */ +#define C3_FSCSI 0x10 /* Fast Clock (>25Mhz) */ + +/* cfg2 */ +#define C2_SCSI2 0x08 /* SCSI-2 Enable */ +#define C2_FE 0x40 /* Features Enable */ + +/* cfg1 */ +#define C1_SLOW 0x80 /* Slow Cable Mode */ +#define C1_SRR 0x40 /* SCSI Reset Rep Int Dis */ +#define C1_PARENB 0x10 /* Enable Parity Check */ + +/* clk factor */ +#define CLK_40M_F 0x00 +#define CLK_25M_F 0x05 +#define CLK_30M_F 0x06 +#define CLK_35M_F 0x07 + +/* interrupt status register */ +#define INTR_SBR 0x80 /* SCSI Bus Reset */ +#define INTR_ILL 0x40 /* Illegal Command */ +#define INTR_DIS 0x20 /* Disconnect */ +#define INTR_BS 0x10 /* Bus Service */ +#define INTR_FC 0x08 /* Function Complete */ +#define INTR_RESEL 0x04 /* Reselected */ +#define INTR_SELATN 0x02 /* Select with ATN */ +#define INTR_SEL 0x01 /* Selected */ +#define INTR_RESELECT (INTR_RESEL | INTR_FC) + +/* status register */ +#define STAT_INT 0x80 /* Interrupt */ +#define STAT_GE 0x40 /* Gross Error */ +#define STAT_PE 0x20 /* Parity Error */ +#define STAT_TC 0x10 /* Terminal Count */ + +/* phase bits */ +#define IOI 0x01 +#define CDI 0x02 +#define MSGI 0x04 + +/* Information transfer phases */ +#define DATA_OUT_PHASE (0) +#define DATA_IN_PHASE (IOI) +#define COMMAND_PHASE (CDI) +#define STATUS_PHASE (CDI|IOI) +#define MESSAGE_OUT_PHASE (MSGI|CDI) +#define MESSAGE_IN_PHASE (MSGI|CDI|IOI) + +#define PHASE_MASK (MSGI|CDI|IOI) + +/* fifo status register */ +#define FIFO_SMASK 0x1e +#define FIFO_E 0x10 /* fifo empty */ +#define FIFO_B 0x00 /* there exists any */ +#define FIFO_1 0x08 /* 1/3 <= bytes < 2/3 */ +#define FIFO_2 0x04 /* 2/3 <= bytes < full */ +#define FIFO_F 0x02 /* full */ +#define FIFO_EN 0x01 /* fifo direction */ +#define FIFO_BRK 0x40 /* phase miss */ + +#define FIFO_F_SZ 128 +#define FIFO_1_SZ 44 +#define FIFO_2_SZ 84 + +/* pflags */ +#define PFR_WRITE 0x01 + +/* Commands */ +#define CMD_DMA 0x80 /* DMA Bit */ +#define CMD_NOP 0x00 /* No Operation */ +#define CMD_FLUSH 0x01 /* Flush FIFO */ +#define CMD_RSTCHIP 0x02 /* Reset Chip */ +#define CMD_RSTSCSI 0x03 /* Reset SCSI Bus */ +#define CMD_RESEL 0x40 /* Reselect Sequence */ +#define CMD_SELNATN 0x41 /* Select without ATN */ +#define CMD_SELATN 0x42 /* Select with ATN */ +#define CMD_SELATNS 0x43 /* Select with ATN & Stop */ +#define CMD_ENSEL 0x44 /* Enable (Re)Selection */ +#define CMD_DISSEL 0x45 /* Disable (Re)Selection */ +#define CMD_SELATN3 0x46 /* Select with ATN3 */ +#define CMD_RESEL3 0x47 /* Reselect3 Sequence */ +#define CMD_SNDMSG 0x20 /* Send Message */ +#define CMD_SNDSTAT 0x21 /* Send Status */ +#define CMD_SNDDATA 0x22 /* Send Data */ +#define CMD_DISCSEQ 0x23 /* Disconnect Sequence */ +#define CMD_TERMSEQ 0x24 /* Terminate Sequence */ +#define CMD_TCCS 0x25 /* Target Command Comp Seq */ +#define CMD_DISC 0x27 /* Disconnect */ +#define CMD_RECMSG 0x28 /* Receive Message */ +#define CMD_RECCMD 0x29 /* Receive Command */ +#define CMD_RECDATA 0x2a /* Receive Data */ +#define CMD_RECCSEQ 0x2b /* Receive Command Sequence */ +#define CMD_ABORT 0x04 /* Target Abort DMA */ +#define CMD_TRANS 0x10 /* Transfer Information */ +#define CMD_ICCS 0x11 /* Initiator Cmd Comp Seq */ +#define CMD_MSGOK 0x12 /* Message Accepted */ +#define CMD_TRPAD 0x18 /* Transfer Pad */ +#define CMD_SETATN 0x1a /* Set ATN */ +#define CMD_RSTATN 0x1b /* Reset ATN */ + +/* Default timeout */ +#define SEL_TOUT 0xa3 +#endif /* !_NCR53C500REG_H_ */ diff -urN sys.orig/i386/scsi/ncr53c500/ncr53c500var.h sys/i386/scsi/ncr53c500/ncr53c500var.h --- sys.orig/i386/scsi/ncr53c500/ncr53c500var.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/ncr53c500/ncr53c500var.h Tue Apr 14 08:45:21 1998 @@ -0,0 +1,44 @@ +/* $NetBSD$ */ +/* $Id: ncr53c500var.h,v 1.1.2.1 1997/12/11 14:00:56 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1995, 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _NCR53C500VAR_H_ +#define _NCR53C500VAR_H_ + +/* softc */ +struct ncv_softc { + SCSI_LOW_SOFTC + + struct ncv_hw sc_hw; +}; + +int ncvprint __P((void *, char *)); +#endif /* !_NCR53C500VAR_H_ */ diff -urN sys.orig/i386/scsi/scsi_low/ccbque.h sys/i386/scsi/scsi_low/ccbque.h --- sys.orig/i386/scsi/scsi_low/ccbque.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/scsi_low/ccbque.h Tue Apr 14 08:45:22 1998 @@ -0,0 +1,130 @@ +/* $NetBSD$ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1994, 1995, 1996 NetBSD/pc98 porting staff. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ +/* + * Common command control queue funcs. + * Written by N. Honda. + */ + +#ifndef _CCBQUE_H_ +#define _CCBQUE_H_ + +/* (I) structure and prototype */ +#define GENERIC_CCB_ASSERT(DEV, CCBTYPE) \ +TAILQ_HEAD(CCBTYPE##tab, CCBTYPE); \ +struct CCBTYPE##que { \ + struct CCBTYPE##tab CCBTYPE##tab; \ + int count; \ + int maxccb; \ +}; \ +void DEV##_init_ccbque __P((int)); \ +struct CCBTYPE *DEV##_get_ccb __P((int)); \ +void DEV##_free_ccb __P((struct CCBTYPE *)); + +/* (II) static allocated memory */ +#define GENERIC_CCB_STATIC_ALLOC(DEV, CCBTYPE) \ +static struct CCBTYPE##que CCBTYPE##que; + +/* (III) functions */ +#define GENERIC_CCB(DEV, CCBTYPE, CHAIN) \ + \ +void \ +DEV##_init_ccbque(count) \ + int count; \ +{ \ + if (CCBTYPE##que.maxccb == 0) \ + TAILQ_INIT(&CCBTYPE##que.CCBTYPE##tab) \ + CCBTYPE##que.maxccb += count; \ +} \ + \ +struct CCBTYPE * \ +DEV##_get_ccb(flags) \ + int flags; \ +{ \ + struct CCBTYPE *cb; \ + int s = splbio(); \ + \ + do \ + { \ + if (CCBTYPE##que.count > CCBTYPE##que.maxccb) \ + { \ + if (flags) \ + { \ + cb = NULL; \ + goto done; \ + } \ + else \ + { \ + tsleep((caddr_t) &CCBTYPE##que.count, \ + PRIBIO, "ccbwait", 0); \ + continue; \ + } \ + } \ + \ + if (cb = CCBTYPE##que.CCBTYPE##tab.tqh_first) \ + { \ + TAILQ_REMOVE(&CCBTYPE##que.CCBTYPE##tab, cb, CHAIN)\ + break; \ + } \ + else \ + { \ + if (cb = malloc(sizeof(*cb), M_DEVBUF, M_NOWAIT))\ + { \ + bzero(cb, sizeof(*cb)); \ + break; \ + } \ + else if (flags) \ + goto done; \ + \ + tsleep((caddr_t) &CCBTYPE##que.count, \ + PRIBIO, "ccbwait", 0); \ + } \ + } \ + while (1); \ + CCBTYPE##que.count ++; \ + \ +done: \ + splx(s); \ + return cb; \ +} \ + \ +void \ +DEV##_free_ccb(cb) \ + struct CCBTYPE *cb; \ +{ \ + int s = splbio(); \ + \ + TAILQ_INSERT_TAIL(&CCBTYPE##que.CCBTYPE##tab, cb, CHAIN) \ + CCBTYPE##que.count --; \ + \ + if (CCBTYPE##que.count == CCBTYPE##que.maxccb) \ + wakeup ((caddr_t) &CCBTYPE##que.count); \ + splx(s); \ +} +#endif /* !_CCBQUE_H_ */ diff -urN sys.orig/i386/scsi/scsi_low/scsi_dvcfg.h sys/i386/scsi/scsi_low/scsi_dvcfg.h --- sys.orig/i386/scsi/scsi_low/scsi_dvcfg.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/scsi_low/scsi_dvcfg.h Tue Apr 14 08:45:22 1998 @@ -0,0 +1,54 @@ +/* $NetBSD$ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1994, 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1994, 1995, 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SCSI_DVCFG_H_ +#define _SCSI_DVCFG_H_ + +/* common dvcfg flags defitions for bs, ncv, stg */ + +#define DVF_SCSI_SYNC 0x01 +#define DVF_SCSI_DISC 0x02 +#define DVF_SCSI_WAIT 0x04 +#define DVF_SCSI_LINK 0x08 +#define DVF_SCSI_QTAG 0x10 +#define DVF_SCSI_SP0 0x100 +#define DVF_SCSI_NOPARITY 0x200 +#define DVF_SCSI_SAVESP 0x400 +#define DVF_SCSI_SP1 0x800 +#define DVF_SCSI_PERIOD(XXX) (((XXX) >> 24) & 0xff) +#define DVF_SCSI_OFFSET(XXX) (((XXX) >> 16) & 0xff) +#define DVF_SCSI_SYNCMASK 0xffff0000 + +#define DVF_SCSI_DEFCFG (DVF_SCSI_SYNC | DVF_SCSI_NOPARITY | DVF_SCSI_SYNCMASK) + +#define DVF_SCSI_BITS "\020\13fssp\12noparity\11nosat\005qtag\004cmdlnk\003wait\002disc\001sync" + +#endif /* !_SCSI_DVCFG_H_ */ diff -urN sys.orig/i386/scsi/scsi_low/scsi_low.c sys/i386/scsi/scsi_low/scsi_low.c --- sys.orig/i386/scsi/scsi_low/scsi_low.c Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/scsi_low/scsi_low.c Tue Apr 14 08:45:23 1998 @@ -0,0 +1,1225 @@ +/* $NetBSD$ */ +/* $Id: scsi_low.c,v 1.1.2.1 1997/12/11 14:01:02 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1995, 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NetBSD__ +#include +#endif /* __NetBSD__ */ +#ifdef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ + +/************************************************************** + * power control + **************************************************************/ +#ifdef SCSI_LOW_POWFUNC +static void scsi_low_recover __P((void *)); + +static void +scsi_low_recover(arg) + void *arg; +{ + struct scsi_low_softc *sc = arg; + int s = splbio(); + + switch (sc->sc_rstep) + { + case 0: + sc->sc_rstep = 1; + (*sc->sc_funcs->scsi_low_power) (sc, SCSI_LOW_ENGAGE); + timeout(scsi_low_recover, sc, 1); + break; + + case 1: + sc->sc_flags &= ~HW_RESUME; + scsi_low_start(sc); + break; + } + + splx(s); +} +#endif /* SCSI_LOW_POWFUNC */ + +int +scsi_low_reset(sc, flags) + struct scsi_low_softc *sc; + u_int flags; +{ + +#ifdef SCSI_LOW_POWFUNC + untimeout(scsi_low_recover, sc); + sc->sc_flags &= ~(HW_POWDOWN | HW_RESUME); + + sc->sc_oc = 1; + sc->sc_powc = SCSI_LOW_POWDOWN_TC; +#endif /* SCSI_LOW_POWFUNC */ + + return ((*sc->sc_funcs->scsi_low_reset)(sc, flags)); +} + +/************************************************************** + * allocate targ_info + **************************************************************/ +struct targ_info * +scsi_low_alloc_ti(sc, targ) + struct scsi_low_softc *sc; + int targ; +{ + struct targ_info *ti; + + if (sc->sc_titab.tqh_first == NULL) + TAILQ_INIT(&sc->sc_titab); + + ti = malloc(sizeof(struct targ_info), M_DEVBUF, M_NOWAIT); + if (ti == NULL) + panic("%s short of memory\n", sc->sc_dvname); + + bzero(ti, sizeof(struct targ_info)); + ti->ti_id = targ; + ti->ti_sc = sc; + ti->ti_quirks = 0; +#ifdef SDEV_NOPARITY + ti->ti_quirks |= SDEV_NOPARITY; +#endif +#ifdef SDEV_NODISC + ti->ti_quirks |= SDEV_NODISC; +#endif + ti->ti_cfgflags = 0xffff0000 | SCSI_LOW_SYNC; + + sc->sc_ti[targ] = ti; + TAILQ_INSERT_TAIL(&sc->sc_titab, ti, ti_chain); + + return ti; +} + +void +scsi_low_free_ti(sc) + struct scsi_low_softc *sc; +{ + struct targ_info *ti, *tib; + + for (ti = sc->sc_titab.tqh_first; ti; ti = tib) + { + tib = ti->ti_chain.tqe_next; + free(ti, M_DEVBUF); + } +} + +int +scsi_low_attach(sc) + struct scsi_low_softc *sc; +{ + int i; + + for (i = 0; i < NTARGETS; i ++) + scsi_low_alloc_ti((struct scsi_low_softc *) sc, i); + + if (scsi_low_reset(sc, 0)) + { + scsi_low_free_ti((struct scsi_low_softc *) sc); + return EINVAL; + } + + return 0; +} + +/************************************************************** + * timeout + **************************************************************/ +void +scsi_low_timeout(arg) + void *arg; +{ + struct scsi_low_softc *sc = arg; + struct ccb *cb; + int reset, s = splbio(); + + /* check */ + reset = 0; + if (cb = sc->sc_nexus) + { + if ((cb->tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL) < 0) + { + cb->ti->ti_error |= TIMEOUTIO; + reset = 1; + } + } + else if (sc->sc_disc > 0) + { + struct targ_info *ti; + + for (ti = sc->sc_titab.tqh_first; ti; ti = ti->ti_chain.tqe_next) + { + if ((cb = ti->ti_disccb) == NULL) + continue; + + if ((cb->tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL) < 0) + { + ti->ti_error |= TIMEOUTIO; + reset = 1; + } + } + } + else + { + cb = sc->sc_start.tqh_first; + if (cb) + { + if ((cb->tc -= SCSI_LOW_TIMEOUT_CHECK_INTERVAL) < 0) + { + cb->ti->ti_error |= TIMEOUTIO; + reset = 1; + } + } +#ifdef SCSI_LOW_POWFUNC + else + { + if (sc->sc_oc == 0) + { + sc->sc_powc --; + if (sc->sc_powc < 0) + { + sc->sc_powc = SCSI_LOW_POWDOWN_TC; + + if ((sc->sc_flags & (HW_POWDOWN | HW_RESUME)) == 0) + { + sc->sc_flags |= HW_POWDOWN; + if (sc->sc_funcs->scsi_low_power) + (*sc->sc_funcs->scsi_low_power) (sc, SCSI_LOW_POWDOWN); + } + } + } + else + { + sc->sc_powc = SCSI_LOW_POWDOWN_TC; + sc->sc_oc = 0; + } + } +#endif /* SCSI_LOW_POWFUNC */ + } + + /* try to recover */ + if (reset) + { + struct targ_info *ti; + + printf("%s: scsi bus hangup\n", sc->sc_dvname); + for (ti = sc->sc_titab.tqh_first; ti; ti = ti->ti_chain.tqe_next) + scsi_low_print(sc, ti); + + scsi_low_reset (sc, 1); + scsi_low_start(sc); + } + + timeout(scsi_low_timeout, sc, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + splx(s); +} + +/************************************************************** + * CCB + **************************************************************/ +GENERIC_CCB_STATIC_ALLOC(scsi_low, ccb) +GENERIC_CCB(scsi_low, ccb, ccb_chain) + +/************************************************************** + * SCSI INTERFACE + **************************************************************/ +#define SCSI_LOW_MINPHYS 0x10000 + +void +scsi_low_scsi_minphys(bp) + struct buf *bp; +{ + + if (bp->b_bcount > SCSI_LOW_MINPHYS) + bp->b_bcount = SCSI_LOW_MINPHYS; + minphys(bp); +} + +int +scsi_low_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_low_softc *sc = xs->sc_link->adapter_softc; + struct targ_info *ti; + struct ccb *cb; + int s; + + if (sc->sc_cfgflags & CFG_NOATTEN) + { + if (xs->sc_link->lun > 0) + { + xs->error = XS_DRIVER_STUFFUP; + return COMPLETE; + } + } + + if ((cb = scsi_low_get_ccb(xs->flags & SCSI_NOSLEEP)) == NULL) + return TRY_AGAIN_LATER; + + cb->ti = ti = sc->sc_ti[xs->sc_link->target]; + + cb->xs = xs; + cb->lun = xs->sc_link->lun; + cb->rcnt = 0; + + s = splbio(); + + TAILQ_INSERT_TAIL(&sc->sc_start, cb, ccb_chain) + if (sc->sc_nexus == NULL) + scsi_low_start(sc); + + if (xs->flags & SCSI_POLL) + { + int timeo = xs->timeout; + + while ((xs->flags & ITSDONE) == 0 && timeo -- > 0) + { + delay(1000); /* 1 ms */ + (*sc->sc_funcs->scsi_low_intr) (sc); + } + + if (timeo <= 0 && (xs->flags & ITSDONE) == 0) + { + scsi_low_reset(sc, 0); + ti->ti_error |= (TIMEOUTIO | ABORTIO); + scsi_low_done(sc, cb); + SCSI_LOW_SETUP_PHASE(PH_NULL); + sc->sc_nexus = NULL; + } + + splx(s); + return COMPLETE; + } + + splx(s); + return SUCCESSFULLY_QUEUED; +} + +/************************************************************** + * Start & Done + **************************************************************/ +static struct scsi_test_unit_ready unit_ready_cmd; +static struct scsi_start_stop ss_cmd = { START_STOP, 0, {0,0,}, SSS_START, }; + +#define IO_BLOCK_CHECK(sc) \ +{ \ + if (sc->sc_flags & HW_INACTIVE) \ + return; \ +} + +void +scsi_low_start(sc) + struct scsi_low_softc *sc; +{ + struct scsi_xfer *xs; + struct targ_info *ti; + struct ccb *cb; + int i, wc; + + IO_BLOCK_CHECK(sc) + +#ifdef SCSI_LOW_POWFUNC + sc->sc_oc ++; + + if (sc->sc_flags & (HW_POWDOWN | HW_RESUME)) + { + if (sc->sc_flags & HW_RESUME) + return; + + sc->sc_flags &= ~HW_POWDOWN; + if (sc->sc_funcs->scsi_low_power) + { + sc->sc_flags |= HW_RESUME; + sc->sc_rstep = 0; + (*sc->sc_funcs->scsi_low_power) (sc, SCSI_LOW_ENGAGE); + + timeout(scsi_low_recover, sc, 1); + return; + } + } +#endif /* SCSI_LOW_POWFUNC */ + + /* setup nexus */ + if ((cb = sc->sc_nexus) == NULL) + { + for (cb = sc->sc_start.tqh_first; cb; cb = cb->ccb_chain.tqe_next) + if (cb->ti->ti_phase == PH_NULL) + break; + + if (cb == NULL) + return; + + if (cb != sc->sc_start.tqh_first) + { + TAILQ_REMOVE(&sc->sc_start, cb, ccb_chain); + TAILQ_INSERT_HEAD(&sc->sc_start, cb, ccb_chain); + } + + sc->sc_nexus = cb; + } + + xs = cb->xs; + ti = cb->ti; + cb->msgoutlen = 0; + ti->ti_msginptr = 0; + ti->ti_error = 0; + SCSI_LOW_SETUP_MSGPHASE(NOMSG); + + if (ti->ti_tflags & TARG_CA) + { + /* XXX: + * ahc and ncr(PCI) drivers forget to init the sense_data + * buffer! send-pr! + */ + bzero(&sc->sc_sense, sizeof(sc->sc_sense)); +#ifdef __NetBSD__ + sc->sc_sense_cmd.opcode = REQUEST_SENSE; +#else + sc->sc_sense_cmd.op_code = REQUEST_SENSE; +#endif + sc->sc_sense_cmd.byte2 = (cb->lun << 5); + sc->sc_sense_cmd.length = sizeof(sc->sc_sense); + cb->cmd = (u_int8_t *) &sc->sc_sense_cmd; + cb->cmdlen = sizeof(sc->sc_sense_cmd); + cb->data = (u_int8_t *) &sc->sc_sense; + cb->datalen = sizeof(sc->sc_sense); + cb->tcmax = 15; + } + else switch(ti->ti_state) + { + case UNIT_RDY: + cb->cmd = (u_int8_t *) &unit_ready_cmd; + cb->cmdlen = sizeof(unit_ready_cmd); + cb->datalen = 0; + cb->tcmax = 15; + break; + + case UNIT_START: + cb->cmd = (u_int8_t *) &ss_cmd; + cb->cmdlen = sizeof(ss_cmd); + cb->datalen = 0; + cb->tcmax = 30; + break; + + case UNIT_SYNCH: + ti->ti_state = UNIT_OK; + ti->ti_tflags |= TARG_SYNCH_PEND; + + case UNIT_OK: + if ((ti->ti_tflags & TARG_SYNCH_PEND) && + (xs->flags & SCSI_POLL) == 0) + { + ti->ti_tflags &= ~TARG_SYNCH_PEND; + if (ti->ti_maxsynch.offset > 0) + { + ti->ti_state = UNIT_SYNCH; + cb->msgout[0] = MSG_EXTEND; + cb->msgout[1] = MSG_EXTEND_SYNCHLEN; + cb->msgout[2] = MSG_EXTEND_SYNCHCODE; + cb->msgout[3] = ti->ti_maxsynch.period; + cb->msgout[4] = ti->ti_maxsynch.offset; + cb->msgoutlen = MSG_EXTEND_SYNCHLEN + 2; + cb->cmd = (u_int8_t *) &unit_ready_cmd; + cb->cmdlen = sizeof(unit_ready_cmd); + cb->datalen = 0; + cb->tcmax = 15; + break; + } + } + + cb->cmd = (u_int8_t *) xs->cmd; + cb->cmdlen = xs->cmdlen; + cb->data = xs->data; + cb->datalen = xs->datalen; + cb->tcmax = (xs->timeout >> 10); + break; + } + + /* timeout */ + if (cb->tcmax < SCSI_LOW_MIN_TOUT) + cb->tcmax = SCSI_LOW_MIN_TOUT; + cb->tc = cb->tcmax; + + /* setup data pointer */ + sc->sc_scp.data = ti->ti_sscp.data = cb->data; + sc->sc_scp.datalen = ti->ti_sscp.datalen = cb->datalen; + + if (((*sc->sc_funcs->scsi_low_start_bus)(sc, cb)) == 0) + { + sc->sc_selid = ti; + } + else + { + sc->sc_selid = NULL; + sc->sc_nexus = NULL; + SCSI_LOW_SETUP_PHASE(PH_NULL); + } +} + +int +scsi_low_done(arg, cb) + void *arg; + struct ccb *cb; +{ + struct scsi_low_softc *sc = arg; + struct scsi_xfer *xs = cb->xs; + struct targ_info *ti = cb->ti; + + if (ti->ti_error == 0) + { + if (ti->ti_tflags & TARG_CA) + { + xs->sense = sc->sc_sense; + xs->error = XS_SENSE; + cb->rcnt = MAXRETRY; + } + else switch (ti->ti_status) + { + case ST_GOOD: + if (sc->sc_scp.datalen != 0 && xs->bp) + { + ti->ti_error |= PDMAERR; + xs->error = XS_DRIVER_STUFFUP; + } + else + xs->error = XS_NOERROR; + break; + + case ST_CHKCOND: + case ST_MET: + ti->ti_tflags |= TARG_CA; + return 1; + + case ST_BUSY: + ti->ti_error |= ERRBUSY; + xs->error = XS_BUSY; + break; + + default: + ti->ti_error |= FATALIO; + xs->error = XS_DRIVER_STUFFUP; + break; + } + + if (ti->ti_state < UNIT_OK) + { +check: + ti->ti_tflags &= ~TARG_CA; + ti->ti_state ++; + cb->rcnt = 0; + return 1; + } + } + else + { + if (ti->ti_phase == PH_SELSTART) + { + xs->error = XS_TIMEOUT; + if (ti->ti_state == UNIT_RDY) + ti->ti_error |= ABORTIO; + } + else if (ti->ti_state < UNIT_OK) + { + goto check; + } + else + xs->error = XS_DRIVER_STUFFUP; + + if (ti->ti_error & ABORTIO) + cb->rcnt = MAXRETRY; + } + + /* internal retry check */ + if (xs->error == XS_NOERROR) + { + xs->resid = 0; + ti->ti_tflags &= ~TARG_CA; + } + else + { + if (xs->bp && xs->error != XS_SENSE) + { + printf("%s: buffered io error\n", sc->sc_dvname); + scsi_low_print(sc, ti); + } + + ti->ti_tflags &= ~TARG_CA; + if (cb->rcnt ++ < MAXRETRY) + return 1; + } + + xs->flags |= ITSDONE; + scsi_done(xs); + + /* free our target */ + TAILQ_REMOVE(&sc->sc_start, cb, ccb_chain) + scsi_low_free_ccb(cb); + return 0; +} + +/************************************************************** + * Reset + **************************************************************/ +void +scsi_low_reset_nexus(sc, fdone) + struct scsi_low_softc *sc; + int fdone; +{ + struct targ_info *ti; + struct ccb *cb; + + /* current nexus */ + if (fdone && (cb = sc->sc_nexus) && cb->rcnt ++ >= MAXRETRY) + { + cb->ti->ti_error |= FATALIO; + scsi_low_done(sc, cb); + } + + /* disconnected nexus */ + for (ti = sc->sc_titab.tqh_first; ti; ti = ti->ti_chain.tqe_next) + { + if (ti->ti_phase == PH_DISC) + { + cb = ti->ti_disccb; + ti->ti_disccb = NULL; + TAILQ_INSERT_HEAD(&sc->sc_start, cb, ccb_chain); + if (fdone && cb->rcnt ++ >= MAXRETRY) + { + ti->ti_error |= FATALIO; + scsi_low_done(sc, cb); + } + } + + SCSI_LOW_SETUP_PHASE(PH_NULL); + ti->ti_tflags &= ~TARG_CA; + } + + sc->sc_flags &= ~HW_PDMASTART; + SCSI_LOW_SETUP_MSGPHASE(NOMSG); + sc->sc_disc = 0; + sc->sc_selid = NULL; + sc->sc_nexus = NULL; +} + +/* misc */ +static int tw_pos; +static char tw_chars[] = "|/-\\"; + +void +scsi_low_twiddle_wait(void) +{ + +#ifdef __NetBSD__ + cnputc('\b'); + cnputc(tw_chars[tw_pos++]); +#endif + tw_pos %= (sizeof(tw_chars) - 1); + delay(TWIDDLEWAIT); +} + +void +scsi_low_bus_reset(sc) + struct scsi_low_softc *sc; +{ + int i; + + (*sc->sc_funcs->scsi_low_bus_reset) (sc); + + printf("%s: try to reset scsi bus ", sc->sc_dvname); + for (i = 0; i <= SCSI2_RESET_DELAY / TWIDDLEWAIT ; i++) + scsi_low_twiddle_wait(); +#ifdef __NetBSD__ + cnputc('\b'); +#endif + printf("\n"); +} + +int +scsi_low_restart(sc, s) + struct scsi_low_softc *sc; + u_char *s; +{ + int error; + + if (s) + printf("%s: scsi bus restart. reason: %s\n", + sc->sc_dvname, s); + + if (error = scsi_low_reset(sc, 0)) + return error; + + scsi_low_start(sc); + return 0; +} + +/************************************************************** + * disconnect and reselect + **************************************************************/ +struct ccb * +scsi_low_reselected(sc, targ, lun) + struct scsi_low_softc *sc; + u_int targ; + u_int lun; +{ + struct targ_info *ti; + struct ccb *cb; + u_char *s; + + /* check collision */ + if ((ti = sc->sc_selid) != NULL) + { + SCSI_LOW_SETUP_PHASE(PH_NULL); + sc->sc_selid = NULL; + } + + if (targ >= NTARGETS || targ == HOST_SCSI_ID) + { + s = "scsi id illegal"; + goto bad; + } + + ti = sc->sc_ti[targ]; + if (ti->ti_phase != PH_DISC) + { + s = "phase mismatch"; + goto bad; + } + + cb = ti->ti_disccb; + if (lun != SCSI_LOW_LUNUNK && cb->lun != lun) + { + s = "lun mismatch"; + goto bad; + } + + /* clear the disconnect state */ + ti->ti_disccb = NULL; + SCSI_LOW_SETUP_PHASE(PH_RESEL); + sc->sc_disc --; + + /* setup the nexus */ + TAILQ_INSERT_HEAD(&sc->sc_start, cb, ccb_chain); + sc->sc_nexus = cb; + + /* recover a saved data pointer */ + sc->sc_scp = ti->ti_sscp; + return cb; + +bad: + printf("%s: reselect %s (targ:lun) = (%x:%x)\n", + sc->sc_dvname, s, targ, lun); + return 0; +} + +int +scsi_low_disconnected(sc, cb) + struct scsi_low_softc *sc; + struct ccb *cb; +{ + struct targ_info *ti = cb->ti; + + /* check phase completion */ + switch (sc->sc_msgphase) + { + case DISCASSERT: + TAILQ_REMOVE(&sc->sc_start, cb, ccb_chain); + ti->ti_disccb = cb; + SCSI_LOW_SETUP_PHASE(PH_DISC); + sc->sc_disc ++; + break; + + case NOMSG: + ti->ti_error |= FATALIO; + + case CMDCOMPLETE: + scsi_low_done(sc, cb); + SCSI_LOW_SETUP_PHASE(PH_NULL); + break; + } + + SCSI_LOW_SETUP_MSGPHASE(NOMSG); + sc->sc_nexus = NULL; + return 1; +} + +/************************************************************** + * msgout + **************************************************************/ +int +scsi_low_msgout(sc, ti, cb) + struct scsi_low_softc *sc; + struct targ_info *ti; + struct ccb *cb; +{ + int len; + + if (ti->ti_phase == PH_MSGOUT) + { + if (cb->rcnt ++ < MAXRETRY) + cb->msgoutlen = ti->ti_omsgoutlen; + } + else + SCSI_LOW_SETUP_PHASE(PH_MSGOUT); + + if (ti->ti_ophase == PH_SELECTED) + { + if (cb->msgoutlen) + { + (*sc->sc_funcs->scsi_low_attention)(sc); + SCSI_LOW_SETUP_PHASE(PH_MSGOUTEXP) + } + + ID_MSG_SETUP(ti, cb); + return 0; + } + + if (cb->msgoutlen == 0) + { + cb->msgout[0] = MSG_REJECT; + len = 1; + } + else + { + ti->ti_msgout = cb->msgout[0]; + ti->ti_emsgout = cb->msgout[2]; + len = cb->msgoutlen; + } + + ti->ti_omsgoutlen = cb->msgoutlen; + cb->msgoutlen = 0; + + return len; +} + +/************************************************************** + * msgin + **************************************************************/ +static void scsi_low_msgin_ext __P((struct targ_info *, struct ccb *)); +static void scsi_low_msgin_error __P((struct targ_info *, u_int, u_int8_t)); +static int scsi_low_synch __P((struct targ_info *)); +static void scsi_low_msg_reject __P((struct targ_info *)); + +#define PERIOD (ti->ti_msgin[3]) +#define OFFSET (ti->ti_msgin[4]) + +static int +scsi_low_synch(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *sc = ti->ti_sc; + u_int period = 0, offset = 0, speed; + u_char *s; + + if (PERIOD >= ti->ti_maxsynch.period && + OFFSET <= ti->ti_maxsynch.offset) + { + if (offset = OFFSET) + period = PERIOD; + s = offset ? "synchronous" : "async"; + } + else + s = "illegal"; + + (*sc->sc_funcs->scsi_low_synch) (sc, ti, period, offset); + + printf("%s(%d:0): <%s> offset %d period 0x%x chip(0x%x) ", + sc->sc_dvname, ti->ti_id, s, offset, period, + (u_int) ti->ti_synch.period); + + if (period) + { + speed = 2500 / period; + printf("%d.%d M/s", speed / 10, speed % 10); + } + + printf("\n"); + return 0; +} + +#define MSGWAIT(cnt) {if(ti->ti_msginptr < (cnt)) return;} + +static void +scsi_low_msgin_ext(ti, cb) + struct targ_info *ti; + struct ccb *cb; +{ + struct scsi_low_softc *sc = ti->ti_sc; + int count; + u_int reqlen; + u_int32_t *ptr; + + MSGWAIT(2); + + reqlen = ti->ti_msgin[1]; + if (reqlen == 0) + reqlen = 256; + + if (ti->ti_msginptr >= MAXMSGLEN) + ti->ti_msginptr = 3; /* XXX */ + + MSGWAIT(reqlen + 2); + + switch (MKMSG_EXTEND(ti->ti_msgin[1], ti->ti_msgin[2])) + { + case MKMSG_EXTEND(MSG_EXTEND_MDPLEN, MSG_EXTEND_MDPCODE): + ptr = (u_int32_t *)(&ti->ti_msgin[3]); + count = (int) htonl((long) (*ptr)); + + if(sc->sc_scp.datalen - count >= 0 && + sc->sc_scp.datalen - count <= cb->datalen) + { + sc->sc_scp.datalen -= count; + sc->sc_scp.data += count; + } + else + scsi_low_msgin_error(ti, 7, MSG_REJECT); + break; + + case MKMSG_EXTEND(MSG_EXTEND_SYNCHLEN, MSG_EXTEND_SYNCHCODE): + if (cb->ti->ti_state == UNIT_SYNCH) + scsi_low_synch(ti); + else + { + (*sc->sc_funcs->scsi_low_attention)(sc); + + cb->msgout[0] = MSG_EXTEND; + cb->msgout[1] = MSG_EXTEND_SYNCHLEN; + cb->msgout[2] = MSG_EXTEND_SYNCHCODE; + cb->msgout[3] = 0; + cb->msgout[4] = 0; + cb->msgoutlen = MSG_EXTEND_SYNCHLEN + 2; + } + break; + + case MKMSG_EXTEND(MSG_EXTEND_WIDELEN, MSG_EXTEND_WIDECODE): + (*sc->sc_funcs->scsi_low_attention)(sc); + + cb->msgout[0] = MSG_EXTEND; + cb->msgout[1] = MSG_EXTEND_WIDELEN; + cb->msgout[2] = MSG_EXTEND_WIDECODE; + cb->msgout[3] = 0; + cb->msgoutlen = MSG_EXTEND_WIDELEN + 2; + break; + + default: + scsi_low_msgin_error(ti, 0, MSG_REJECT); + break; + } + + ti->ti_msginptr = 0; + return; +} + +static void +scsi_low_msg_reject(ti) + struct targ_info *ti; +{ + struct scsi_low_softc *sc = ti->ti_sc; + struct ccb *cb; + char *s = "unexpected msg reject"; + + if ((cb = sc->sc_nexus) == NULL) + return; + + switch (ti->ti_ophase) + { + case PH_CMD: + s = "cmd rejected"; + break; + + case PH_MSGOUT: + if (ti->ti_msgout & 0x80) + { + s = "identify msg rejected"; + } + else if (ti->ti_msgout == MSG_EXTEND) + { + switch (ti->ti_emsgout) + { + case MSG_EXTEND_SYNCHCODE: + s = "synchrnous msg rejected"; + break; + + default: + break; + } + } + break; + + default: + break; + } + + printf("%s: %s\n", sc->sc_dvname, s); + ti->ti_error |= MSGERR; +} + +static void +scsi_low_msgin_error(ti, count, msg) + struct targ_info *ti; + u_int count; + u_int8_t msg; +{ + struct scsi_low_softc *sc = ti->ti_sc; + struct ccb *cb; + int n; + + MSGWAIT(count); + + printf("%s: illegal msg", sc->sc_dvname); + for (n = 0; n < ti->ti_msginptr; n ++) + printf("[0x%x] ", (u_int) ti->ti_msgin[n]); + printf("\n"); + + ti->ti_msginptr = 0; + if ((cb = sc->sc_nexus) == NULL) + return; + + (*sc->sc_funcs->scsi_low_attention) (sc); + + cb->msgoutlen = 1; + cb->msgout[0] = msg; +} + +void +scsi_low_msgin(sc, cb) + struct scsi_low_softc *sc; + struct ccb *cb; +{ + struct targ_info *ti = cb->ti; + + switch (ti->ti_msgin[0]) + { + case MSG_SAVESP: + ti->ti_sscp = sc->sc_scp; + break; + + case MSG_RESTORESP: + sc->sc_scp = ti->ti_sscp; + break; + + case MSG_REJECT: + scsi_low_msg_reject(ti); + break; + + case MSG_NOOP: + break; + + case MSG_I_ERROR:/* all I -> T : nothing to do*/ + case MSG_ABORT: + case MSG_PARITY: + case MSG_RESET: + case 0xe: + case 0xf: + scsi_low_msgin_error(ti, 1, MSG_REJECT); + goto resume; + + case MSG_EXTEND: + scsi_low_msgin_ext(ti, cb); + goto resume; + + case 0xd: + scsi_low_msgin_error(ti, 2, MSG_REJECT); + goto resume; + + case MSG_DISCON: + SCSI_LOW_SETUP_MSGPHASE(DISCASSERT); + break; + + case MSG_COMP: + SCSI_LOW_SETUP_MSGPHASE(CMDCOMPLETE); + break; + + case MSG_LCOMP: + case MSG_LCOMP_F: + scsi_low_msgin_error(ti, 1, MSG_REJECT); + goto resume; + + default: + if (ti->ti_msgin[0] & 0x80) + { + if ((ti->ti_msgin[0] & 0x07) != cb->lun) + { + printf("%s: lun error\n", sc->sc_dvname); + scsi_low_msgin_error(ti, 1, MSG_ABORT); + } + break; + } + else if (ti->ti_msgin[0] < 0x20) + scsi_low_msgin_error(ti, 1, MSG_REJECT); + else if (ti->ti_msgin[0] < 0x30) + scsi_low_msgin_error(ti, 2, MSG_REJECT); + else + scsi_low_msgin_error(ti, 1, MSG_REJECT); + goto resume; + } + + ti->ti_msginptr = 0; + +resume: + return; +} + +#define MAXOFFSET 0x10 + +void +scsi_low_calcf(ti) + struct targ_info *ti; +{ + u_int period; + u_int8_t offset; + struct scsi_low_softc *sc = ti->ti_sc; + + ti->ti_flags &= ~SCSI_LOW_DISC; +#ifdef SDEV_NODISC + if ((sc->sc_cfgflags & CFG_NODISC) == 0 && + (ti->ti_quirks & SDEV_NODISC) == 0 && + (ti->ti_cfgflags & SCSI_LOW_DISC) != 0) + ti->ti_flags |= SCSI_LOW_DISC; +#endif + + ti->ti_flags |= SCSI_LOW_NOPARITY; +#ifdef SDEV_NOPARITY + if ((sc->sc_cfgflags & CFG_NOPARITY) == 0 && + (ti->ti_quirks & SDEV_NOPARITY) == 0 && + (ti->ti_cfgflags & SCSI_LOW_NOPARITY) == 0) + ti->ti_flags &= ~SCSI_LOW_NOPARITY; +#endif + + ti->ti_flags &= ~SCSI_LOW_SYNC; + if ((ti->ti_cfgflags & SCSI_LOW_SYNC) && + (sc->sc_cfgflags & CFG_ASYNC) == 0) + { + offset = SCSI_LOW_OFFSET(ti->ti_cfgflags); + if (offset > ti->ti_maxsynch.offset) + offset = ti->ti_maxsynch.offset; + ti->ti_flags |= SCSI_LOW_SYNC; + } + else + offset = 0; + + if (offset > 0) + { + period = SCSI_LOW_PERIOD(ti->ti_cfgflags); + if (period > 100) + period = 100; /* MAX 10M FAST SCSI2 */ + if (period) + period = 2500 / period; + if (period < ti->ti_maxsynch.period) + period = ti->ti_maxsynch.period; + } + else + period = 0; + + ti->ti_maxsynch.offset = offset; + ti->ti_maxsynch.period = period; +} + +int +scsi_low_target_open(link, cf) + struct scsi_link *link; + struct cfdata *cf; +{ + u_int target = link->target; + u_int lun = link->lun; + struct scsi_low_softc *sc; + struct targ_info *ti; + u_int quirks, flags; + + sc = (struct scsi_low_softc *) link->adapter_softc; + ti = sc->sc_ti[target]; + ti->ti_quirks = (u_int) link->quirks; + ti->ti_cfgflags = cf->cf_flags; + if (ti->ti_state > UNIT_SYNCH) + ti->ti_state = UNIT_SYNCH; + + scsi_low_calcf(ti); + + printf("%s(%d:%d): max period(0x%x) max offset(%d) flags 0x%b\n", + sc->sc_dvname, target, lun, + ti->ti_maxsynch.period, + ti->ti_maxsynch.offset, + ti->ti_flags, SCSI_LOW_BITS); + return 0; +} + +/********************************************************** + * DEBUG SECTION + **********************************************************/ +static u_char *phase[] = +{ + "FREE", "ARBSTART", "SELSTART", "SELECTED", + "CMDOUT", "DATA", "MSGIN", "MSGOUT", "STATIN", "DISC", + "COMPSEQ", "RESEL", "EXPMSGOUT" +}; + +void +scsi_low_print(sc, ti) + struct scsi_low_softc *sc; + struct targ_info *ti; +{ + struct ccb *cb = sc->sc_nexus; + + if (ti == NULL) + ti = cb ? cb->ti : NULL; + + printf("%s targ 0x%x nexus 0x%x discs %d\n", + sc->sc_dvname, ti, cb, sc->sc_disc); + + /* target stat */ + if (ti) + { + struct sc_p *sp = &sc->sc_scp; + u_int lun = cb ? cb->lun : 0; + + printf("%s(%d:%d) ph<%s> ", sc->sc_dvname, + ti->ti_id, lun, phase[(int) ti->ti_phase]); + + printf("msgptr %x msg[0] %x status %x tflags %x\n", + (u_int) (ti->ti_msginptr), (u_int) (ti->ti_msgin[0]), + ti->ti_status, ti->ti_tflags); + + printf("msgout %x emsgout %x ti_omsglen %x flags %b\n", + (u_int) ti->ti_msgout, (u_int) ti->ti_emsgout, + ti->ti_omsgoutlen, ti->ti_flags, SCSI_LOW_BITS); + + printf("datalen %x dataaddr %x ", sp->datalen, sp->data); + + if (cb) + printf("cmdlen %x cmdaddr %x cmd[0] %x odatalen %x", + cb->cmdlen, cb->cmd, (u_int) cb->cmd[0], + cb->datalen); + + printf("\n"); + printf("error flags %b\n", ti->ti_error, SCSI_LOW_ERRORBITS); + } +} diff -urN sys.orig/i386/scsi/scsi_low/scsi_low.h sys/i386/scsi/scsi_low/scsi_low.h --- sys.orig/i386/scsi/scsi_low/scsi_low.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/scsi_low/scsi_low.h Tue Apr 14 08:45:24 1998 @@ -0,0 +1,387 @@ +/* $NetBSD$ */ +/* $Id: scsi_low.h,v 1.1.2.1 1997/12/11 14:01:03 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1995, 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _SCSI_LOW_H_ +#define _SCSI_LOW_H_ + +#ifdef __NetBSD__ +#include +#endif +#ifdef __FreeBSD__ +#include +#endif + +#define SCSI_LOW_SYNC DVF_SCSI_SYNC +#define SCSI_LOW_DISC DVF_SCSI_DISC +#define SCSI_LOW_WAIT DVF_SCSI_WAIT +#define SCSI_LOW_LINK DVF_SCSI_LINK +#define SCSI_LOW_QTAG DVF_SCSI_QTAG +#define SCSI_LOW_NOPARITY DVF_SCSI_NOPARITY +#define SCSI_LOW_SAVESP DVF_SCSI_SAVESP +#define SCSI_LOW_DEFCFG DVF_SCSI_DEFCFG +#define SCSI_LOW_BITS DVF_SCSI_BITS + +#define SCSI_LOW_PERIOD(XXX) DVF_SCSI_PERIOD(XXX) +#define SCSI_LOW_OFFSET(XXX) DVF_SCSI_OFFSET(XXX) + +#define HOST_SCSI_ID 7 + +#define SCSI_LOW_MIN_TOUT 24 +#define SCSI_LOW_TIMEOUT_CHECK_INTERVAL 4 +#define SCSI_LOW_POWDOWN_TC 15 + +#define NTARGETS 8 +#define MAXMSGLEN 16 +#define MAXRETRY 3 + +struct targ_info; + +struct ccb { + TAILQ_ENTRY(ccb) ccb_chain; + + struct scsi_xfer *xs; /* scsi upper */ + + struct targ_info *ti; /* targ_info */ + + u_int lun; /* lun */ + + int rcnt; /* retry counter */ + + /***************************************** + * scsi cmd & data + *****************************************/ + u_int8_t *cmd; /* scsi cmd */ + int cmdlen; + + u_int8_t *data; /* scsi data */ + u_int datalen; + + u_int8_t msgout[MAXMSGLEN]; /* scsi msgout */ + u_int msgoutlen; + + /***************************************** + * timeout counter + *****************************************/ + int tc; + int tcmax; +}; + +/* ccb assert */ +#ifdef __NetBSD__ +#include +#endif +#ifdef __FreeBSD__ +#include +#endif + +GENERIC_CCB_ASSERT(scsi_low, ccb) + +/* scsi pointer */ +struct sc_p { + u_int8_t *data; + int datalen; +}; + +#define SCSI_LOW_SETUP_PHASE(PHASE) \ +{ \ + ti->ti_ophase = ti->ti_phase; \ + ti->ti_phase = (PHASE); \ +} + +#define SCSI_LOW_SETUP_MSGPHASE(PHASE) \ +{ \ + sc->sc_msgphase = (PHASE); \ +} + +struct scsi_low_softc; + +struct targ_info { + TAILQ_ENTRY(targ_info) ti_chain; /* targ_info link */ + + struct scsi_low_softc *ti_sc; /* our softc */ + u_int ti_id; /* scsi id */ + +#define TARG_CA 0x01 +#define TARG_SYNCH_PEND 0x02 + u_int ti_tflags; /* target state I */ + +#define UNIT_RDY 0x00 +#define UNIT_START 0x01 +#define UNIT_SYNCH 0x02 +#define UNIT_OK 0x03 + u_int ti_state; /* target state II */ + + u_int ti_flags; + u_int ti_cfgflags; + u_int ti_quirks; + +#define FATALIO 0x01 +#define ABORTIO 0x02 +#define TIMEOUTIO 0x04 +#define PDMAERR 0x08 +#define MSGERR 0x10 +#define PARITYERR 0x20 +#define ERRBUSY 0x40 +#define SCSI_LOW_ERRORBITS "\020\007busy\006parity\005msgerr\004pdmaerr\003timeout\002abort\001fatal" + u_int ti_error; /* error flags */ + + /***************************************** + * scsi phase data + *****************************************/ + struct sc_p ti_sscp; /* saved scsi data pointer */ + +#define PH_NULL 0x00 +#define PH_ARBSTART 0x01 +#define PH_SELSTART 0x02 +#define PH_SELECTED 0x03 +#define PH_CMD 0x04 +#define PH_DATA 0x05 +#define PH_MSGIN 0x06 +#define PH_MSGOUT 0x07 +#define PH_STAT 0x08 +#define PH_DISC 0x09 +#define PH_COMPSEQ 0x0a +#define PH_RESEL 0x0b +#define PH_MSGOUTEXP 0x0c + u_int ti_phase; /* scsi phase */ + u_int ti_ophase; /* old scsi phase */ + + u_int8_t ti_status; /* status in */ + + u_int ti_msginptr; /* msgin ptr */ + u_int8_t ti_msgin[MAXMSGLEN]; /* msgin buffer */ + + u_int8_t ti_msgout; /* last msgout byte */ + u_int8_t ti_emsgout; /* last msgout byte */ + u_int ti_omsgoutlen; /* for retry msgout */ + + struct synch { + u_int8_t offset; + u_int8_t period; + u_int8_t c3img; + } ti_synch, ti_maxsynch; /* synch data */ + + struct ccb *ti_disccb; /* disconnected */ +}; + +/************************************************* + * COMMON HEADER STRUCTURE + *************************************************/ +struct scsi_low_softc; +typedef struct scsi_low_softc *sc_low_t; + +#define SC_LOW_RESET_T (int (*) __P((sc_low_t, int))) +#define SC_LOW_BUSRST_T (void (*) __P((sc_low_t))) +#define SC_LOW_SELECT_T (int (*) __P((sc_low_t, struct ccb *))) +#define SC_LOW_ATTEN_T (void (*) __P((sc_low_t))) +#define SC_LOW_SYNCH_T (int (*) __P((sc_low_t, struct targ_info *, u_int, u_int))) +#define SC_LOW_INTR_T (int (*) __P((void *))) +#define SC_LOW_POWER_T (int (*) __P((sc_low_t, u_int))) + +struct scsi_low_funcs { + int (*scsi_low_reset) __P((sc_low_t, int)); + void (*scsi_low_bus_reset) __P((sc_low_t)); + + int (*scsi_low_start_bus) __P((sc_low_t, struct ccb *)); + + void (*scsi_low_attention) __P((sc_low_t)); + int (*scsi_low_synch) __P((sc_low_t, struct targ_info *, u_int, u_int)); + int (*scsi_low_intr) __P((void *)); + +#define SCSI_LOW_POWDOWN 1 +#define SCSI_LOW_ENGAGE 2 + int (*scsi_low_power) __P((sc_low_t, u_int)); +}; + +/* sc_flags */ +#define HW_POWDOWN 0x01 +#define HW_RESUME 0x02 +#define HW_PDMASTART 0x04 +#define HW_INACTIVE 0x08 + +/* msg phase */ +#define NOMSG 0x00 +#define DISCASSERT 0x01 +#define CMDCOMPLETE 0x02 + +/* read write */ +#define SCSI_LOW_WRITE 0 +#define SCSI_LOW_READ 1 + +#define CFG_NODISC 0x01 +#define CFG_NOPARITY 0x02 +#define CFG_NOATTEN 0x04 +#define CFG_ASYNC 0x08 + +TAILQ_HEAD(targ_info_tab, targ_info); + +#define SCSI_LOW_DVNAME_LEN 16 + +#define SCSI_LOW_SOFTC \ + OS_DEPEND_DEVICE_HEADER \ + OS_DEPEND_MISC_HEADER \ + OS_DEPEND_SCSI_HEADER \ + \ + struct targ_info *sc_ti[NTARGETS]; \ + \ + struct targ_info_tab sc_titab; \ + \ + struct ccbtab sc_start; \ + struct ccb *sc_nexus; \ + \ + int sc_disc; \ + struct targ_info *sc_selid; \ + \ + u_int sc_flags; \ + \ + u_int sc_msgphase; \ + \ + struct sc_p sc_scp; \ + \ + u_int sc_direction; \ + int sc_tdatalen; \ + \ + u_int sc_mask; \ + int sc_wc; \ + \ + u_int sc_oc; \ + int sc_powc; \ + u_int sc_rstep; \ + \ + u_int sc_cfgflags; \ + \ + struct scsi_low_funcs *sc_funcs; \ + \ + struct scsi_sense sc_sense_cmd; \ + struct scsi_sense_data sc_sense; \ + \ + u_char sc_dvname[SCSI_LOW_DVNAME_LEN]; + +/************************************************* + * SCSI LOW SOFTC + *************************************************/ +struct scsi_low_softc { + SCSI_LOW_SOFTC +}; + +/************************************************* + * PROTOTYPE + *************************************************/ +int scsi_low_attach __P((struct scsi_low_softc *)); +void scsi_low_timeout __P((void *)); +int scsi_low_scsi_cmd __P((struct scsi_xfer *)); +void scsi_low_scsi_minphys __P((struct buf *)); +void scsi_low_start __P((struct scsi_low_softc *)); +int scsi_low_done __P((void *, struct ccb *)); +void scsi_low_reset_nexus __P((struct scsi_low_softc *, int)); +int scsi_low_msgout __P((struct scsi_low_softc *, struct targ_info *, struct ccb *)); +void scsi_low_msgin __P((struct scsi_low_softc *, struct ccb *)); +int scsi_low_restart __P((struct scsi_low_softc *, u_char *)); +void scsi_low_free_ti __P((struct scsi_low_softc *)); +struct targ_info *scsi_low_alloc_ti __P((struct scsi_low_softc *, int)); + +#define SCSI_LOW_LUNUNK ((u_int) -1) +struct ccb *scsi_low_reselected __P((struct scsi_low_softc *, u_int, u_int)); +int scsi_low_disconnected __P((struct scsi_low_softc *, struct ccb *)); + +/* reset */ +#define SCSI2_RESET_DELAY 5000000 +#define TWIDDLEWAIT 10000 +void scsi_low_twiddle_wait __P((void)); +void scsi_low_bus_reset __P((struct scsi_low_softc *)); + +void scsi_low_print __P((struct scsi_low_softc *, struct targ_info *)); + +int scsi_low_target_open __P((struct scsi_link *, struct cfdata *)); +void scsi_low_calcf __P((struct targ_info *)); +int scsi_low_reset __P((struct scsi_low_softc *, u_int)); + +#ifdef __NetBSD__ +int scsi_low_activate __P((pisa_device_args_t)); +int scsi_low_deactivate __P((pisa_device_args_t)); +#endif + +#ifdef __FreeBSD__ +int scsi_low_activate __P((struct scsi_low_softc *, struct isa_device *)); +int scsi_low_deactivate __P((struct scsi_low_softc *)); +#endif + +/* misc */ +static inline u_int8_t scsi_low_identify __P((struct targ_info *ti)); + +static inline u_int8_t +scsi_low_identify(ti) + struct targ_info *ti; +{ + + return (ti->ti_flags & SCSI_LOW_DISC) ? 0xc0 : 0x80; +} + +#define ID_MSG_SETUP(TI, CB) \ +{ \ + (TI)->ti_msgout = scsi_low_identify(TI) | (CB)->lun; \ + (TI)->ti_omsgoutlen = 0; \ +} + +/* XXX: use scsi_message.h */ +#define ST_GOOD 0x00 +#define ST_CHKCOND 0x02 +#define ST_MET 0x04 +#define ST_BUSY 0x08 +#define ST_INTERGOOD 0x10 +#define ST_INTERMET 0x14 +#define ST_CONFLICT 0x18 +#define ST_QUEFULL 0x28 + +#define MSG_COMP 0x00 +#define MSG_EXTEND 0x01 + +#define MKMSG_EXTEND(XLEN, XCODE) ((((u_int)(XLEN)) << NBBY) | ((u_int)(XCODE))) +#define MSG_EXTEND_MDPCODE 0x00 +#define MSG_EXTEND_MDPLEN 0x05 +#define MSG_EXTEND_SYNCHCODE 0x01 +#define MSG_EXTEND_SYNCHLEN 0x03 +#define MSG_EXTEND_WIDECODE 0x03 +#define MSG_EXTEND_WIDELEN 0x02 + +#define MSG_SAVESP 0x02 +#define MSG_RESTORESP 0x03 +#define MSG_DISCON 0x04 +#define MSG_I_ERROR 0x05 +#define MSG_ABORT 0x06 +#define MSG_REJECT 0x07 +#define MSG_NOOP 0x08 +#define MSG_PARITY 0x09 +#define MSG_LCOMP 0x0a +#define MSG_LCOMP_F 0x0b +#define MSG_RESET 0x0c +#endif /* !_SCSI_LOW_H_ */ diff -urN sys.orig/i386/scsi/scsi_low/scsi_low_if.h sys/i386/scsi/scsi_low/scsi_low_if.h --- sys.orig/i386/scsi/scsi_low/scsi_low_if.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/scsi_low/scsi_low_if.h Tue Apr 14 08:45:25 1998 @@ -0,0 +1,134 @@ +/* + * Copyright (c) HONDA Naofumi, KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* $Id: scsi_low_if.h,v 1.1.2.1 1997/12/11 14:01:04 itojun Exp $ */ + +/*************************************************** + * misc device header in scsi_low_softc + ***************************************************/ +#ifdef __NetBSD__ +#define OS_DEPEND_DEVICE_HEADER \ + struct device sc_dev; \ + void *sc_ih; + +#define OS_DEPEND_SCSI_HEADER \ + struct scsi_link sc_link; + +#define OS_DEPEND_MISC_HEADER \ + pisa_device_handle_t sc_pdv; \ + bus_chipset_tag_t sc_bc; \ + bus_io_handle_t sc_ioh; \ + bus_io_handle_t sc_delayioh; \ + bus_mem_handle_t sc_memh; + +#endif /* __NetBSD__ */ +#ifdef __FreeBSD__ +#define OS_DEPEND_DEVICE_HEADER \ + struct device sc_dev; \ + int unit; + +#define OS_DEPEND_SCSI_HEADER \ + struct scsi_link sc_link; + +#define OS_DEPEND_MISC_HEADER \ + int sc_ioport; +#endif /* __FreeBSD__ */ + + +/*************************************************** + * include + ***************************************************/ +/* (I) common include */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* (II) os depend include */ +#ifdef __NetBSD__ +#include + +#include +#include +#include + +#include +#include +#include +#include +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#endif /* __FreeBSD__ */ + +#include +#include +#include + +/*************************************************** + * BUS IO MAPPINGS & BS specific inclusion + ***************************************************/ +#ifdef __NetBSD__ +#include +#include +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +#define SCSI_POLL SCSI_NOMASK +#define delay(y) DELAY(y) + +#if 0 +#include +#endif + +#if NCRD > 0 +#include +#include +#include +#include +#endif /* NCRD > 0 */ + +#include +#include +#endif /* __FreeBSD__ */ diff -urN sys.orig/i386/scsi/scsi_low/scsi_low_pisa.c sys/i386/scsi/scsi_low/scsi_low_pisa.c --- sys.orig/i386/scsi/scsi_low/scsi_low_pisa.c Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/scsi_low/scsi_low_pisa.c Tue Apr 14 08:45:26 1998 @@ -0,0 +1,143 @@ +/* $NetBSD$ */ +/* $Id: scsi_low_pisa.c,v 1.1.2.1 1997/12/11 14:01:05 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1995, 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1995, 1996 Naofumi HONDA. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NetBSD__ +#include +#endif /* __NetBSD__ */ +#ifdef __FreeBSD__ +#include +#endif /* __FreeBSD__ */ + +#ifdef __NetBSD__ +#define SCSIBUS_RESCAN +#endif + +#ifdef __FreeBSD__ +static inline int +scsi_low_deactivate_1(struct scsi_low_softc *sc) +#else +inline int +scsi_low_deactivate_1(sc) + struct scsi_low_softc *sc; +#endif +{ + + sc->sc_flags |= HW_INACTIVE; + +#ifdef SCSI_LOW_POWFUNC + untimeout(scsi_low_recover, sc); +#endif /* SCSI_LOW_POWFUNC */ + untimeout(scsi_low_timeout, sc); + + return 0; +} + +#ifdef __NetBSD__ +int +scsi_low_deactivate(arg) + pisa_device_args_t arg; +{ + return (scsi_low_deactivate_1(arg->id)); +} +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +int +scsi_low_deactivate(struct scsi_low_softc *sc) +{ + + return (scsi_low_deactivate_1(sc)); +} +#endif /* __FreeBSD__ */ + +#ifdef __FreeBSD__ +static inline int +scsi_low_activate_1(struct scsi_low_softc *sc, int flags) +#else +inline int +scsi_low_activate_1(sc, flags) + struct scsi_low_softc *sc; + int flags; +#endif +{ + int i, error; + + sc->sc_flags &= ~HW_INACTIVE; +#ifdef __FreeBSD__ + sc->sc_cfgflags = ((sc->sc_cfgflags << 16) | (flags & 0xffff)); +#else /* __NetBSD__ */ + sc->sc_cfgflags = DVCFG_MKCFG(DVCFG_MAJOR(sc->sc_cfgflags), + DVCFG_MINOR(flags)); +#endif + +#ifdef __NetBSD__ + sc->sc_mask = (1 << ia->ia_irq); +#endif + + if (error = scsi_low_reset(sc, 0)) + { + sc->sc_flags |= HW_INACTIVE; + return error; + } + + timeout(scsi_low_timeout, sc, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + + /* rescan the scsi bus */ +#ifdef SCSIBUS_RESCAN + if (arg->event == PISA_EVENT_INSERT && sc->sc_start.tqh_first == NULL) + scsi_probe_busses((int) sc->sc_link.scsibus, -1, -1); +#endif + /* start again */ + scsi_low_start(sc); + return 0; +} + +#ifdef __NetBSD__ +int +scsi_low_activate(arg) + pisa_device_args_t arg; +{ + struct isa_attach_args *ia = arg->ia; + + return (scsi_low_activate_1(arg->id, ia->ia_cfgflags)); +} +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +int +scsi_low_activate(sc, dev) + struct scsi_low_softc *sc; + struct isa_device *dev; +{ + return (scsi_low_activate_1(sc, dev->id_flags)); +} +#endif /* __FreeBSD__ */ diff -urN sys.orig/i386/scsi/tmc18c30/tmc18c30.c sys/i386/scsi/tmc18c30/tmc18c30.c --- sys.orig/i386/scsi/tmc18c30/tmc18c30.c Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/tmc18c30/tmc18c30.c Tue Apr 14 08:45:27 1998 @@ -0,0 +1,829 @@ +/* $NetBSD$ */ +/* $Id: tmc18c30.c,v 1.1.2.1 1997/12/11 14:01:07 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1996 Naofumi HONDA. + * Copyright (c) 1996 Kouichi Matsuda. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NetBSD__ +#include +#endif +#ifdef __FreeBSD__ +#include +#endif + +/*************************************************** + * USER SETTINGS + ***************************************************/ +/* DEVICE CONFIGURATION FLAGS (MINOR) + * + * 0x01 DISCONECT OFF + * 0x02 PARITY LINE OFF + * 0x04 IDENTIFY MSG OFF ( = single lun) + * 0x08 SYNC TRANSFER OFF + */ + +/* #define STG_SYNC_SUPPORT */ /* NOT YET but easy */ + +/* For the 512 fifo type: change below */ +#define TMC18C30_FIFOSZ 0x800 +#define TMC18C30_FCB 1 + +#define TMC18C50_FIFOSZ 0x2000 +#define TMC18C50_FCB 2 + +/*************************************************** + * DEBUG + ***************************************************/ +#ifndef DDB +#define Debugger() panic("should call debugger here (tmc18c30.c)") +#endif /* ! DDB */ + +#define STG_DEBUG +#ifdef STG_DEBUG +int stg_debug; + +struct stg_statics { + int disconnect; + int reselect; +} stg_statics[NTARGETS]; +#endif /* STG_DEBUG */ + +/************************************************************** + * DECLARE + **************************************************************/ +/* static */ +static void stg_pio_read __P((struct stg_softc *, struct targ_info *)); +static void stg_pio_write __P((struct stg_softc *, struct targ_info *)); +static int stg_xfer __P((struct stg_softc *, u_int8_t *, int, int)); +static int stg_synch __P((struct stg_softc *, struct targ_info *, u_int, u_int)); +static int stg_reselected __P((struct stg_softc *)); +static inline int stg_disconnected __P((struct stg_softc *, struct ccb *cb)); +static inline void stg_pdma_end __P((struct stg_softc *, struct ccb *, struct targ_info *)); +static int stghw_select_targ_wait __P((struct stg_softc *, int)); +static void stghw_init __P((struct stg_softc *)); +static void stghw_synch __P((struct stg_softc *, struct targ_info *)); + +static int stg_world_start __P((struct stg_softc *, int)); +static void stghw_reset __P((struct stg_softc *)); +static int stghw_start_selection __P((struct stg_softc *sc, struct ccb *)); +static void stghw_bus_reset __P((struct stg_softc *)); +static void stghw_attention __P((struct stg_softc *)); + +struct scsi_low_funcs stgfuncs = +{ + SC_LOW_RESET_T stg_world_start, + SC_LOW_BUSRST_T stghw_bus_reset, + + SC_LOW_SELECT_T stghw_start_selection, + + SC_LOW_ATTEN_T stghw_attention, + SC_LOW_SYNCH_T stg_synch, + + SC_LOW_INTR_T stg_sequencer, + + NULL, +}; + +/**************************************************** + * hwfuncs + ****************************************************/ +int +stghw_check(sc) + struct stg_softc *sc; +{ + u_int16_t lsb, msb; + DECLARE_IOPORT(sc); + + sc->sc_chip = TMCCHIP_UNK; + sc->sc_fsz = TMC18C50_FIFOSZ; + sc->sc_fcb = TMC18C50_FCB; + sc->sc_fcsp = 0; + + sc->sc_fcRinit = FCTL_INTEN; + sc->sc_fcWinit = FCTL_PARENB | FCTL_INTEN; + + if (sc->sc_cfgflags & CFG_NOATTEN) + sc->sc_busc = 0; + else + sc->sc_busc = BCTL_ATN; + + lsb = INB(tmc_idlsb); + msb = INB(tmc_idmsb); + switch (msb << 8 | lsb) + { + case 0x6127: + /* TMCCHIP_1800 not supported. (it's my policy) */ + sc->sc_chip = TMCCHIP_1800; + return EINVAL; + + case 0x60e9: + sc->sc_chip = TMCCHIP_18C50; + sc->sc_fcsp |= FCTL_CLRINT; + if (INB(tmc_cfg2) & 0x02) + { + sc->sc_chip = TMCCHIP_18C30; + sc->sc_fsz = TMC18C30_FIFOSZ; + sc->sc_fcb = TMC18C30_FCB; + } + break; + + default: + return ENODEV; + } + + sc->sc_icinit = ICTL_ALLINT | sc->sc_fcb; + return 0; +} + +static void +stghw_reset(sc) + struct stg_softc *sc; +{ + DECLARE_IOPORT(sc); + + OUTB(tmc_ictl, 0); + OUTB(tmc_bctl, BCTL_BUSFREE); + OUTB(tmc_fctl, sc->sc_fcsp | sc->sc_fcRinit | FCTL_CLRFIFO); + OUTB(tmc_fctl, sc->sc_fcRinit); + OUTB(tmc_ictl, sc->sc_icinit); + + OUTB(tmc_ssctl, 0); +} + +static void +stghw_init(sc) + struct stg_softc *sc; +{ + DECLARE_IOPORT(sc); + + OUTB(tmc_ictl, 0); + OUTB(tmc_bctl, BCTL_BUSFREE); + OUTB(tmc_fctl, sc->sc_fcsp | sc->sc_fcRinit | FCTL_CLRFIFO); + OUTB(tmc_fctl, sc->sc_fcRinit); + OUTB(tmc_ictl, sc->sc_icinit); + + OUTB(tmc_ssctl, 0); +} + +static void +stghw_synch(sc, ti) + struct stg_softc *sc; + struct targ_info *ti; +{ +#ifdef STG_SYNCH + DECLARE_IOPORT(sc); + + OUTB(tmc_ssctl, ti->ti_synch.c3img); +#endif +} + +/**************************************************** + * scsi low interface + ****************************************************/ +static void +stghw_attention(sc) + struct stg_softc *sc; +{ + DECLARE_IOPORT(sc); + + OUTB(tmc_bctl, BCTL_BUSEN | BCTL_ATN); +} + +static void +stghw_bus_reset(sc) + struct stg_softc *sc; +{ + DECLARE_IOPORT(sc); + + OUTB(tmc_ictl, 0); + OUTB(tmc_fctl, 0); + OUTB(tmc_bctl, BCTL_RST); + delay(100000); + OUTB(tmc_bctl, BCTL_BUSFREE); +} + +static int +stghw_start_selection(sc, cb) + struct stg_softc *sc; + struct ccb *cb; +{ + struct targ_info *ti = cb->ti; + register u_int8_t stat; + int s, tc = sc->sc_wc; + DECLARE_IOPORT(sc); + + if (ti->ti_flags & SCSI_LOW_NOPARITY) + sc->sc_fcRinit &= ~FCTL_PARENB; + else + sc->sc_fcRinit |= FCTL_PARENB; + + s = splhigh(); + if (sc->sc_disc > 0) + { + stat = INB(tmc_bstat); + if ((stat & RESEL_PHASE_MASK)== PHASE_RESELECTED) + { + splx(s); + return 1; + } + + if (stat & (BSTAT_BSY | BSTAT_SEL)) + { + delay(1); /* XXX */ + + stat = INB(tmc_bstat); + if (stat & (BSTAT_BSY | BSTAT_SEL)) + { + splx(s); + return 1; + } + } + + OUTB(tmc_bctl, BCTL_BUSFREE); + OUTB(tmc_scsiid, (1 << HOST_SCSI_ID)); + OUTB(tmc_fctl, sc->sc_fcRinit | FCTL_ARBIT); + splx(s); + } + else + { + splx(s); + + do + { + stat = INB(tmc_bstat); + if ((stat & BSTAT_BSY) == 0) + break; + + if (stat & (BSTAT_SEL | BSTAT_REQ)) + { + printf("%s hardware failure(0x%x)\n", + sc->sc_dev.dv_xname, (u_int) stat); + return 1; + } + else if (tc -- <= 0) + { + printf("%s bus free timeout\n", + sc->sc_dev.dv_xname); + return 1; + } + } + while (1); + + OUTB(tmc_bctl, BCTL_BUSFREE); + OUTB(tmc_scsiid, (1 << HOST_SCSI_ID)); + OUTB(tmc_fctl, sc->sc_fcRinit | FCTL_ARBIT); + } + + SCSI_LOW_SETUP_PHASE(PH_ARBSTART); + return 0; +} + +static int +stg_world_start(sc, fdone) + struct stg_softc *sc; + int fdone; +{ + struct targ_info *ti; + u_int stat; + int error; + + if (sc->sc_flags & HW_INACTIVE) + return EBUSY; + + /* reset current nexus */ + scsi_low_reset_nexus((struct scsi_low_softc *) sc, fdone); + + sc->sc_cfgflags |= CFG_ASYNC; /* XXX */ + + /* hardware info init */ + for (ti = sc->sc_titab.tqh_first; ti; ti = ti->ti_chain.tqe_next) + { + ti->ti_state = UNIT_RDY; + ti->ti_synch.period = ti->ti_synch.offset = 0; + ti->ti_maxsynch.offset = 8; + + scsi_low_calcf(ti); + } + + if (error = stghw_check(sc)) + return error; + + stghw_reset(sc); + scsi_low_bus_reset((struct scsi_low_softc *) sc); + stghw_init(sc); + +#ifdef __NetBSD__ + softintr(sc->sc_mask); +#endif + return 0; +} + +static int +stg_synch(sc, ti, period, offset) + struct stg_softc *sc; + struct targ_info *ti; + u_int period, offset; +{ + u_int res; + + period = period << 2; + + if (period >= 200) + { + ti->ti_synch.c3img = (period - 200) / 50; + if (period % 50) + ti->ti_synch.c3img ++; + ti->ti_synch.c3img |= SSCTL_SYNCHEN; + } + else if (period >= 100) + { + ti->ti_synch.c3img = (period - 100) / 50; + if (period % 50) + ti->ti_synch.c3img ++; + ti->ti_synch.c3img |= SSCTL_SYNCHEN | SSCTL_FSYNCHEN; + } + + ti->ti_synch.period = period; + ti->ti_synch.offset = offset; + return 0; +} + +/************************************************************** + * PDMA functions + **************************************************************/ +static inline void +stg_pdma_end(sc, cb, ti) + struct stg_softc *sc; + struct ccb *cb; + struct targ_info *ti; +{ + u_int len, tres; + DECLARE_IOPORT(sc); + + sc->sc_flags &= ~HW_PDMASTART; + + if (ti->ti_phase == PH_DATA) + { + if ((sc->sc_direction & SCSI_LOW_READ) == 0) + { + len = INW(tmc_fdcnt); + if (len) + { + tres = len + sc->sc_scp.datalen; + if (tres <= (u_int) cb->datalen) + { + sc->sc_scp.data -= len; + sc->sc_scp.datalen = tres; + } + else + { + ti->ti_error |= PDMAERR; + printf("%s len %x >= datalen %x\n", + sc->sc_dev.dv_xname, + len, sc->sc_scp.datalen); + } + } + } + } + else + { + + printf("%s data phase miss\n", sc->sc_dev.dv_xname); + ti->ti_error |= PDMAERR; + } + +#ifdef STG_SYNCH + OUTB(tmc_ssctl, 0); +#endif + OUTB(tmc_fctl, sc->sc_fcRinit); +} + +static void +stg_pio_read(sc, ti) + struct stg_softc *sc; + struct targ_info *ti; +{ + int tout = sc->sc_wc; + u_int res; + u_int8_t stat; + DECLARE_IOPORT(sc); + + OUTB(tmc_fctl, sc->sc_fcRinit | FCTL_FIFOEN); + +#ifdef STG_SYNCH + stghw_synch(sc, ti); +#endif + + sc->sc_flags |= HW_PDMASTART; + while (sc->sc_scp.datalen > 0 && tout -- > 0) + { + res = INW(tmc_fdcnt); + if (res == 0) + { + stat = INB(tmc_bstat); + if ((stat & BSTAT_PHMASK) == BSTAT_IO) + continue; + break; /* phase mismatch */ + } + + /* XXX */ + if (res > sc->sc_scp.datalen) + break; + + sc->sc_scp.datalen -= res; + if (res & 1) + { + sc->sc_scp.data[0] = INB(tmc_rfifo); + sc->sc_scp.data ++; + res --; + } + + INSW(tmc_rfifo, sc->sc_scp.data, res >> 1); + sc->sc_scp.data += res; + } + + if (tout <= 0) + printf("%s pio read timeout\n", sc->sc_dev.dv_xname); +} + +#define WFIFO_CRIT 0x100 + +static void +stg_pio_write(sc, ti) + struct stg_softc *sc; + struct targ_info *ti; +{ + u_int res; + int tout = sc->sc_wc; + register u_int8_t stat; + DECLARE_IOPORT(sc); + + stat = sc->sc_fcWinit | FCTL_FIFOEN | FCTL_FIFOW; + OUTB(tmc_fctl, stat | FCTL_CLRFIFO); + OUTB(tmc_fctl, stat); + +#ifdef STG_SYNCH + stghw_synch(sc, ti); +#endif + + sc->sc_flags |= HW_PDMASTART; + while (sc->sc_scp.datalen > 0 && tout -- > 0) + { + stat = INB(tmc_bstat); + if ((stat & BSTAT_PHMASK) != 0) + break; + + if (INW(tmc_fdcnt) >= WFIFO_CRIT) + continue; + + res = (sc->sc_scp.datalen > WFIFO_CRIT) ? WFIFO_CRIT : + sc->sc_scp.datalen; + sc->sc_scp.datalen -= res; + if ((res & 0x01) != 0) + { + OUTB(tmc_wfifo, *sc->sc_scp.data++); + res --; + } + + OUTSW(tmc_wfifo, sc->sc_scp.data, res >> 1); + sc->sc_scp.data += res; + } + + if (tout <= 0) + printf("%s pio write timeout\n", sc->sc_dev.dv_xname); +} + +static int +stg_xfer(sc, buf, len, phase) + struct stg_softc *sc; + u_int8_t *buf; + int len; + int phase; +{ + int wc, ptr; + u_int8_t stat; + DECLARE_IOPORT(sc); + + if (phase & BSTAT_IO) + OUTB(tmc_fctl, sc->sc_fcRinit); + else + OUTB(tmc_fctl, sc->sc_fcWinit); + + for (ptr = 0; len > 0; len --) + { + wc = sc->sc_wc; + + do + { + stat = INB(tmc_bstat); + if ((stat & BSTAT_PHMASK) != (phase & BSTAT_PHMASK)) + goto bad; + if (stat & BSTAT_REQ) + break; + if (wc -- <= 0) + { + printf("%s xfer tout\n", sc->sc_dev.dv_xname); + goto bad; + } + } + while (1); + + if (phase & BSTAT_IO) + buf[ptr ++] = INB(tmc_rdata); + else + OUTB(tmc_wdata, buf[ptr ++]); + } + +bad: + OUTB(tmc_fctl, sc->sc_fcRinit); + return len; +} + +/************************************************************** + * disconnect & reselect (HW low) + **************************************************************/ +static int +stg_reselected(sc) + struct stg_softc *sc; +{ + struct ccb *cb; + u_int sid; + DECLARE_IOPORT(sc); + + sid = (u_int) INB(tmc_scsiid); + sid &= ~(1 << HOST_SCSI_ID); + sid = ffs(sid) - 1; + cb = scsi_low_reselected((struct scsi_low_softc *) sc, sid, SCSI_LOW_LUNUNK); + if (cb == NULL) + return 0; + +#ifdef STG_DEBUG + stg_statics[sid].reselect ++; +#endif + + /* assert a busy line and ack the selection */ + OUTB(tmc_scsiid, (1 << HOST_SCSI_ID) || (1 << sid)); + OUTB(tmc_fctl, sc->sc_fcsp | + sc->sc_fcRinit | FCTL_CLRFIFO); + OUTB(tmc_fctl, sc->sc_fcRinit); + OUTB(tmc_bctl, BCTL_BSY | BCTL_BUSEN); + return 1; +} + +static inline int +stg_disconnected(sc, cb) + struct stg_softc *sc; + struct ccb *cb; +{ + DECLARE_IOPORT(sc); + + /* clear bus status & fifo */ + OUTB(tmc_fctl, sc->sc_fcRinit | FCTL_CLRFIFO); + OUTB(tmc_fctl, sc->sc_fcRinit); + OUTB(tmc_bctl, BCTL_BUSFREE); + +#ifdef STG_DEBUG + if (sc->sc_msgphase == DISCASSERT) + stg_statics[cb->ti->ti_id].disconnect ++; +#endif + scsi_low_disconnected((struct scsi_low_softc *) sc, cb); + scsi_low_start((struct scsi_low_softc *) sc); + return 1; +} + +/************************************************************** + * SEQUENCER + **************************************************************/ +static int +stghw_select_targ_wait(sc, targ) + struct stg_softc *sc; + int targ; +{ + int wc, error = EIO; + DECLARE_IOPORT(sc); + + OUTB(tmc_scsiid, (1 << HOST_SCSI_ID) | (1 << targ)); + OUTB(tmc_fctl, sc->sc_fcWinit & (~FCTL_INTEN)); + OUTB(tmc_bctl, BCTL_BUSEN | BCTL_SEL | sc->sc_busc); + + for (wc = 50000; wc; wc--) + { + if (INB(tmc_bstat) & BSTAT_BSY) + { + error = 0; + break; + } + + delay(1); + } + + OUTB(tmc_fctl, sc->sc_fcRinit | FCTL_CLRFIFO); + OUTB(tmc_fctl, sc->sc_fcRinit); + return error; +} + +int +stg_sequencer(sc) + struct stg_softc *sc; +{ + struct targ_info *ti; + struct ccb *cb; + int len; + u_int8_t status, astatus; + DECLARE_IOPORT(sc); + + /******************************************* + * interrupt check + *******************************************/ + if (sc->sc_flags & HW_INACTIVE) + return 0; + + astatus = INB(tmc_astat); + status = INB(tmc_bstat); + + if ((astatus & ASTAT_STATMASK) == 0) + return 0; + + if (astatus & ASTAT_SCSIRST) + { + OUTB(tmc_fctl, + sc->sc_fcRinit | FCTL_CLRFIFO); + OUTB(tmc_fctl, sc->sc_fcRinit); + return 1; + } + + /******************************************* + * debug section + *******************************************/ +#ifdef STG_DEBUG + if (stg_debug) + { + scsi_low_print((struct scsi_low_softc *) sc, NULL); + printf("%s st %x ist %x\n\n", sc->sc_dev.dv_xname, + status, astatus); + if (stg_debug > 1) + Debugger(); + } +#endif /* STG_DEBUG */ + + /******************************************* + * reselection & nexus + *******************************************/ + if ((status & RESEL_PHASE_MASK)== PHASE_RESELECTED) + return stg_reselected(sc); + + if ((cb = sc->sc_nexus) == NULL) + return 0; + ti = cb->ti; + + if ((astatus & ASTAT_PARERR) && + ti->ti_phase != PH_ARBSTART && + (ti->ti_flags & SCSI_LOW_NOPARITY) == 0) + { + ti->ti_error |= PARITYERR; + scsi_low_restart((struct scsi_low_softc *) sc, + "scsi bus parity error"); + return 1; + } + + /******************************************* + * aribitration & selection + *******************************************/ + switch (ti->ti_phase) + { + case PH_ARBSTART: + OUTB(tmc_fctl, + sc->sc_fcRinit | FCTL_CLRFIFO); + + if ((astatus & ASTAT_ARBIT) == 0) + { + OUTB(tmc_fctl, sc->sc_fcRinit); + SCSI_LOW_SETUP_PHASE(PH_NULL); + sc->sc_selid = NULL; + sc->sc_nexus = NULL; + return 1; + } + else + OUTB(tmc_fctl, sc->sc_fcWinit); + + /* assert select line */ + OUTB(tmc_scsiid, + (1 << HOST_SCSI_ID) | (1 << ti->ti_id)); + OUTB(tmc_bctl, + BCTL_SEL | BCTL_BUSEN | sc->sc_busc); + SCSI_LOW_SETUP_PHASE(PH_SELSTART); + return 1; + + case PH_SELSTART: + sc->sc_selid = NULL; + if ((status & BSTAT_BSY) == 0) + if (stghw_select_targ_wait(sc, ti->ti_id)) + return stg_disconnected(sc, cb); + + /* attention assert */ + OUTB(tmc_fctl, sc->sc_fcRinit); + OUTB(tmc_bctl, BCTL_BUSEN | sc->sc_busc); + SCSI_LOW_SETUP_PHASE(PH_SELECTED); + return 1; + + case PH_RESEL: + /* clear a busy line */ + OUTB(tmc_fctl, sc->sc_fcRinit); + OUTB(tmc_bctl, BCTL_BUSEN); + break; + } + + /******************************************* + * scsi seq + *******************************************/ + if (sc->sc_flags & HW_PDMASTART) + stg_pdma_end(sc, cb, ti); + + switch (status & PHASE_MASK) + { + case COMMAND_PHASE: + SCSI_LOW_SETUP_PHASE(PH_CMD); + stg_xfer(sc, cb->cmd, cb->cmdlen, COMMAND_PHASE); + break; + + case DATA_OUT_PHASE: + SCSI_LOW_SETUP_PHASE(PH_DATA); + sc->sc_direction = SCSI_LOW_WRITE; + stg_pio_write(sc, ti); + break; + + case DATA_IN_PHASE: + SCSI_LOW_SETUP_PHASE(PH_DATA); + sc->sc_direction = SCSI_LOW_READ; + stg_pio_read(sc, ti); + break; + + case STATUS_PHASE: + SCSI_LOW_SETUP_PHASE(PH_STAT); + ti->ti_status = INB(tmc_rdata); + break; + + case MESSAGE_OUT_PHASE: + OUTB(tmc_fctl, sc->sc_fcWinit); + len = scsi_low_msgout((struct scsi_low_softc *) sc, ti, cb); + if (len == 0) + { + if (cb->msgoutlen == 0) + OUTB(tmc_bctl, BCTL_BUSEN); + OUTB(tmc_wdata, ti->ti_msgout); + } + else if (len > 0) + { + stg_xfer(sc, cb->msgout, len, MESSAGE_OUT_PHASE); + OUTB(tmc_bctl, BCTL_BUSEN); + } + OUTB(tmc_fctl, sc->sc_fcRinit); + break; + + case MESSAGE_IN_PHASE: + SCSI_LOW_SETUP_PHASE(PH_MSGIN); + ti->ti_msgin[ti->ti_msginptr++] = INB(tmc_rdata); + scsi_low_msgin((struct scsi_low_softc *)sc, cb); + if (sc->sc_msgphase) + return stg_disconnected(sc, cb); + break; + + case BUSFREE_PHASE: + printf("%s unexpected disconnection\n", sc->sc_dev.dv_xname); + return stg_disconnected(sc, cb); + + default: + printf("%s unknown phase bus %x intr %x\n", + sc->sc_dev.dv_xname, status, astatus); + break; + } + + return 1; +} diff -urN sys.orig/i386/scsi/tmc18c30/tmc18c30if.c sys/i386/scsi/tmc18c30/tmc18c30if.c --- sys.orig/i386/scsi/tmc18c30/tmc18c30if.c Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/tmc18c30/tmc18c30if.c Tue Apr 14 08:47:57 1998 @@ -0,0 +1,446 @@ +/* $NetBSD$ */ +/* $Id: tmc18c30if.c,v 1.1.2.1 1997/12/11 14:01:09 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1996 Naofumi HONDA. + * Copyright (c) 1996 Kouichi Matsuda. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifdef __NetBSD__ +#include +#endif +#ifdef __FreeBSD__ +#include +#include "stg.h" +#endif + +/*************************************************** + * PARAMS + ***************************************************/ +#define STG_MAX_CCB (2 * (NTARGETS - 1)) + +/*************************************************** + * ISA DEVICE STRUCTURE + ***************************************************/ +#ifdef __NetBSD__ +int stgintr __P((void *)); + +struct cfdriver stg_cd = { + NULL, "stg", DV_DULL +}; + +struct scsi_device stg_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ +}; + +struct scsi_adapter stg = { + scsi_low_scsi_cmd, + scsi_low_scsi_minphys, + scsi_low_target_open, + 0, +}; +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +/* pccard support */ +#if NCARD > 0 +static int stg_card_intr(struct pccard_devinfo *); +static void stg_card_unload(struct pccard_devinfo *); +static void stg_card_suspend(struct pccard_devinfo *); +static int stg_card_resume(struct stg_softc *); +static int stg_card_init(struct pccard_devinfo *, int); + +static struct pccard_device stg_info = { + "stg", + stg_card_init, + stg_card_unload, + stg_card_intr, +/* stg_card_suspend, */ + 0, /* Attributes - presently unused */ + &bio_imask /* Interrupt mask for device */ + /* This should also include net_imask?? */ +}; + +DATA_SET(pccarddrv_set, stg_info); + +#ifdef SCSI_DETACH +static void stgdetach(struct isa_device *dev); +#endif +#endif /* NCARD */ + +struct isa_driver stgdriver = { + stgprobe, + stgattach, + "stg" +}; + +struct scsi_device stg_dev = { + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "stg", + 0, + {0, 0} +}; + +static struct scsi_adapter stg = { + scsi_low_scsi_cmd, + scsi_low_scsi_minphys, + scsi_low_target_open, + 0, + stg_adapter_info, + "stg", + {0, 0}, +}; +u_int32_t stg_adapter_info (int unit) +{ + return 1; +} + +static struct stg_softc *stgdata[NSTG]; +#endif /* __FreeBSD__ */ + +/************************************************************** + * DECLARE + **************************************************************/ +extern int delaycount; +extern struct scsi_low_funcs stgfuncs; + +/************************************************************** + * General probe attach + **************************************************************/ +#ifdef __FreeBSD__ +#if NCARD > 0 +static void +stg_card_suspend(struct pccard_devinfo *devi) +{ + struct stg_softc *sc = stgdata[devi->isahd.id_unit]; + + printf("%s: suspending\n", sc->sc_dvname); +} + +static int +stg_card_resume(struct stg_softc *sc) +{ +#if 0 /* XXX stg_init */ + if (!stg_init(sc)) + return (ENXIO); +#endif + printf("%s: resumed\n", sc->sc_dvname); + return (0); +} + +static int +stg_card_init(struct pccard_devinfo *devi, int first) +{ + int unit = devi->isahd.id_unit; + struct stg_softc *sc = stgdata[unit]; + + if (NSTG <= unit) + return (ENODEV); + if (!first) + return (stg_card_resume(sc)); + else + { + /* XXX -- ncv_init */ + printf("probe stg\n"); + if (stgprobe(&devi->isahd) == 0) + return (ENXIO); + printf("attach stg\n"); + if (stgattach(&devi->isahd) == 0) + return (ENXIO); + } + return (0); +} + +static void +stg_card_unload(struct pccard_devinfo *devi) +{ + struct stg_softc *sc = stgdata[devi->isahd.id_unit]; + + printf("%s: unload\n", sc->sc_dvname); +/* scsi_low_deactivate((struct scsi_low_softc *)sc); */ +#ifdef SCSI_DETACH + stgdetach(&devi->isahd); +#endif +} + +static int +stg_card_intr(struct pccard_devinfo *devi) +{ + + (void) stg_sequencer(stgdata[devi->isahd.id_unit]); + return 1; +} +#endif /* NCARD > 0 */ +#endif /* FreeBSD */ + +#ifdef __NetBSD__ +int +stgprobe(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct stg_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + struct stg_hw *hw; + int rv = 0; + DECLARE_IOPORT(sc); + + if (ia->ia_iobase == IOBASEUNK || ia->ia_irq == IRQUNK) + return 0; + + bc = ia->ia_bc; + if (bus_io_map(bc, ia->ia_iobase, STGIOSZ, &ioh)) + return 0; + + sc->sc_bc = bc; + sc->sc_ioh = ioh; + sc->sc_dealyioh = ia->ia_delayioh; +#ifdef __FreeBSD__ + sc->sc_cfgflags = (ia->ia_cfgflags & 0xffff); +#else /* __NetBSD__ */ + sc->sc_cfgflags = DVCFG_MINOR(ia->ia_cfgflags); +#endif + sc->sc_wc = delaycount * 2000; /* 2 sec */ + sc->sc_funcs = &stgfuncs; + + if (stghw_check(sc)) + goto bad; + + if (scsi_low_attach((struct scsi_low_softc *) sc)) + goto bad; + + ia->ia_iosize = STGIOSZ; + rv = 1; + +bad: + bus_io_unmap(bc, ioh, STGIOSZ); + return rv; +} +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +static int stghw_probe (struct stg_softc *sc, int flags) +{ + sc->sc_cfgflags = flags; + sc->sc_wc = delaycount * 2000; /* 2 sec */ + sc->sc_funcs = &stgfuncs; + return (stghw_check(sc)); +} + +int stgprobe(struct isa_device *dev) +{ + struct stg_softc *sc; + int unit = dev->id_unit; + struct stg_hw *hw; + char dvname[SCSI_LOW_DVNAME_LEN]; + DECLARE_IOPORT(sc); + + sprintf(dvname, "stg%d", unit); + + if (unit >= NSTG && !dev->id_reconfig) + { + printf("%s: unit number too high\n", dvname); + return (0); + } + + if (dev->id_iobase == 0) + { + printf("%s: no ioaddr is given\n", dvname); + return (0); + } + + if (dev->id_reconfig || stgdata[unit] != NULL) + { + sc = stgdata[unit]; + sc->sc_ioport = dev->id_iobase; + +#ifdef __FreeBSD__ + if (stghw_probe(sc, dev->id_flags & 0xffff)) + return (0); +#else /* __NetBSD__ */ + if (stghw_probe(sc, DVCFG_MINOR(dev->id_flags))) + return (0); +#endif + } + else + { + sc = malloc(sizeof(struct stg_softc), M_TEMP, M_NOWAIT); + if (sc == NULL) + { + printf("%s: cannot malloc!\n", sc->sc_dvname); + return (0); + } + bzero(sc, sizeof(struct stg_softc)); + strcpy (sc->sc_dvname, dvname); + sc->unit = unit; + stgdata[unit] = sc; + sc->sc_ioport = dev->id_iobase; + + if (stghw_probe(sc, dev->id_flags)) + { + free(sc, M_TEMP); + stgdata[unit] = NULL; + return (0); + } + } + return (STGIOSZ); +} +#endif + +int +stgprint(aux, name) + void *aux; + char *name; +{ + + if (name != NULL) + printf("%s: scsibus ", name); + return UNCONF; +} + +#ifdef __FreeBSD__ +#define adapter_target adapter_targ +#define openings opennings +#endif + +#ifdef __FreeBSD__ +static inline void +stg_setup_sc(struct stg_softc *sc) +#else +inline void stg_setup_sc(sc) + struct stg_softc *sc; +#endif +{ + + scsi_low_init_ccbque(STG_MAX_CCB); + TAILQ_INIT(&sc->sc_start); + + sc->sc_link.adapter_softc = sc; + sc->sc_link.adapter_target = 7; + sc->sc_link.adapter = &stg; + sc->sc_link.device = &stg_dev; + sc->sc_link.openings = 2; +} + +#undef adapter_target +#undef openings + +#ifdef __NetBSD__ +void +stgattach(parent, self, aux) + struct device *parent; + struct device *self; + void *aux; +{ + struct stg_softc *sc = (void *)self; + struct isa_attach_args *ia = aux; + + printf("\n"); + + sc->sc_bc = ia->ia_bc; + sc->sc_dealyioh = ia->ia_delayioh; + if (bus_io_map(sc->sc_bc, ia->ia_iobase, STGIOSZ, &sc->sc_ioh)) + panic("%s: couldn't map io\n", sc->sc_dev.dv_xname); + + stg_setup_sc(sc); + sc->sc_mask = (1 << ia->ia_irq); + sc->sc_ih = isa_intr_establish(ia->ia_ic, ia->ia_irq, IST_EDGE, + IPL_BIO, stgintr, sc); + + timeout(scsi_low_timeout, sc, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); +} +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ + +#ifdef SCSI_DETACH +static void +stgdetach(struct isa_device *dev) +{ + int unit = dev->id_unit; + struct scsibus_data *scbus; + + scbus = (struct scsibus_data *)scsi_extend_get(stgdata[unit]->sc_link.scsibus); +} +#endif + +int +stgattach(dev) + struct isa_device *dev; +{ + int unit = dev->id_unit; + struct stg_softc *sc = stgdata[unit]; + struct scsibus_data *scbus; + + if (scsi_low_attach((struct scsi_low_softc *) sc)) + { + printf("%s: scsi low attach failed\n", sc->sc_dvname); + return (0); + } + stg_setup_sc(sc); + sc->sc_link.adapter_unit = unit; + + /* + * Prepare the scsibus_data area for the upperlevel + * scsi code. + */ + scbus = scsi_alloc_bus(); + if (!scbus) + return (0); + scbus->adapter_link = &sc->sc_link; + /* + * ask the adapter what subunits are present + */ + scsi_attachdevs(scbus); + timeout(scsi_low_timeout, sc, SCSI_LOW_TIMEOUT_CHECK_INTERVAL * hz); + return 1; +} +#endif /* __FreeBSD__ */ + +#ifdef __NetBSD__ +int stgintr (arg) + void *arg; +{ + return (stg_sequencer((struct stg_softc *)arg)); +} +#endif + +#ifdef __FreeBSD__ +void +stgintr (int unit) +{ + (void) stg_sequencer(stgdata[unit]); +} +#endif diff -urN sys.orig/i386/scsi/tmc18c30/tmc18c30if.h sys/i386/scsi/tmc18c30/tmc18c30if.h --- sys.orig/i386/scsi/tmc18c30/tmc18c30if.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/tmc18c30/tmc18c30if.h Tue Apr 14 08:45:28 1998 @@ -0,0 +1,107 @@ +/* + * Copyright (c) HONDA Naofumi, KATO Takenori, 1996. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer as + * the first lines of this file unmodified. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/* $Id: tmc18c30if.h,v 1.1.2.1 1997/12/11 14:01:11 itojun Exp $ */ + +/*************************************************** + * BUS IO MAPPINGS & TMC18C30 specific inclusion + ***************************************************/ +#ifdef __NetBSD__ +#include +#include +#include +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +#include +#include +#include + +#include "apm.h" +#if NAPM > 0 +#include +#endif /* NAPM > 0 */ + +/* pccard support */ +#include "card.h" +#if NCARD > 0 +#include +#include +#include +#include +#endif /* NCARD > 0 */ +#endif /* __FreeBSD__ */ + +int stg_sequencer __P((struct stg_softc *sc)); +int stghw_check __P((struct stg_softc *)); + +/* (II) os depend declare */ +#ifdef __NetBSD__ +int stgprobe __P((struct device *, struct device *, void *)); +void stgattach __P((struct device *, struct device *, void *)); +#endif /* __NetBSD__ */ + +#ifdef __FreeBSD__ +int stgprobe __P((struct isa_device *dev)); +int stgattach __P((struct isa_device *dev)); +u_int32_t stg_adapter_info __P((int)); +#endif /* __FreeBSD__ */ + +#ifdef __NetBSD__ +#define DECLARE_IOPORT(sc) \ + register bus_chipset_tag_t bc = (sc)->sc_bc; \ + register bus_io_handle_t ioh = (sc)->sc_ioh; +#define OUTB(o, val) \ + bus_io_write_1 (bc, ioh, (o), (val)) +#define OUTW(o, val) \ + bus_io_write_2 (bc, ioh, (o), (val)) +#define OUTSW(o,b,l) \ + bus_io_write_multi_2(bc, ioh, (o), (b), (l)) +#define INB(o) \ + bus_io_read_1 (bc, ioh, (o)) +#define INW(o) \ + bus_io_read_2 (bc, ioh, (o)) +#define INSW(o,b,l) \ + bus_io_read_multi_2(bc, ioh, (o), (b), (l)) +#endif + +#ifdef __FreeBSD__ +#define DECLARE_IOPORT(sc) \ + register int ioport = (sc)->sc_ioport; +#define OUTB(o, val) \ + outb((o)+ioport, (val)) +#define OUTW(o, val) \ + outw((o)+ioport, (val)) +#define OUTSW(o,b,l) \ + outsw((o)+ioport, (b), (l)) +#define INB(o) \ + inb((o)+ioport) +#define INW(o) \ + inw((o)+ioport) +#define INSW(o,b,l) \ + insw((o)+ioport, (b), (l)) +#endif diff -urN sys.orig/i386/scsi/tmc18c30/tmc18c30reg.h sys/i386/scsi/tmc18c30/tmc18c30reg.h --- sys.orig/i386/scsi/tmc18c30/tmc18c30reg.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/tmc18c30/tmc18c30reg.h Tue Apr 14 08:45:32 1998 @@ -0,0 +1,141 @@ +/* $NetBSD$ */ +/* $Id: tmc18c30reg.h,v 1.1.2.1 1997/12/11 14:01:15 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1996 Kouichi Matsuda. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TMC18C30REG_H_ +#define _TMC18C30REG_H_ + +#define tmc_wdata 0x00 +#define tmc_rdata 0x00 + +#define tmc_bctl 0x01 +#define BCTL_BUSFREE 0x00 +#define BCTL_RST 0x01 +#define BCTL_SEL 0x02 +#define BCTL_BSY 0x04 +#define BCTL_ATN 0x08 +#define BCTL_IO 0x10 +#define BCTL_CD 0x20 +#define BCTL_MSG 0x40 +#define BCTL_BUSEN 0x80 +#define tmc_bstat 0x01 +#define BSTAT_BSY 0x01 +#define BSTAT_MSG 0x02 +#define BSTAT_IO 0x04 +#define BSTAT_CMD 0x08 +#define BSTAT_REQ 0x10 +#define BSTAT_SEL 0x20 +#define BSTAT_ACK 0x40 +#define BSTAT_PHMASK (BSTAT_MSG | BSTAT_IO | BSTAT_CMD) + +#define tmc_ictl 0x02 +#define ICTL_FIFO 0x10 +#define ICTL_ARBIT 0x20 +#define ICTL_SEL 0x40 +#define ICTL_CD 0x80 +#define ICTL_ALLINT (ICTL_ARBIT | ICTL_CD | ICTL_SEL) +#define tmc_astat 0x02 +#define ASTAT_INT 0x01 +#define ASTAT_ARBIT 0x02 +#define ASTAT_PARERR 0x04 +#define ASTAT_SCSIRST 0x08 +#define ASTAT_STATMASK 0x0f +#define ASTAT_FIFODIR 0x10 +#define ASTAT_FIFOEN 0x20 +#define ASTAT_PARENB 0x40 +#define ASTAT_BUSEN 0x80 + +#define tmc_ssctl 0x03 +#define SSCTL_FSYNCHEN 0x40 +#define SSCTL_SYNCHEN 0x80 +#define tmc_fstat 0x03 + +#define tmc_fctl 0x04 +#define FCTL_CLRFIFO 0x01 +#define FCTL_ARBIT 0x04 +#define FCTL_PARENB 0x08 +#define FCTL_INTEN 0x10 +#define FCTL_CLRINT 0x20 +#define FCTL_FIFOW 0x40 +#define FCTL_FIFOEN 0x80 +#define tmc_icnd 0x04 + +#define tmc_mctl 0x05 +#define tmc_idlsb 0x05 + +#define tmc_idmsb 0x06 + +#define tmc_wlb 0x07 +#define tmc_rlb 0x07 + +#define tmc_scsiid 0x08 +#define tmc_sdna 0x08 + +#define tmc_istat 0x09 +#define ISTAT_INTEN 0x08 +#define ISTAT_FIFO 0x10 +#define ISTAT_ARBIT 0x20 +#define ISTAT_SEL 0x40 +#define ISTAT_CD 0x80 + +#define tmc_cfg1 0x0a + +#define tmc_ioctl 0x0b +#define tmc_cfg2 0x0b + +#define tmc_wfifo 0x0c +#define tmc_rfifo 0x0c + +#define tmc_fdcnt 0x0e + +/* Information transfer phases */ +#define BUSFREE_PHASE 0x00 +#define DATA_OUT_PHASE (BSTAT_BSY) +#define DATA_IN_PHASE (BSTAT_BSY|BSTAT_IO) +#define COMMAND_PHASE (BSTAT_CMD|BSTAT_BSY) +#define STATUS_PHASE (BSTAT_CMD|BSTAT_BSY|BSTAT_IO) +#define MESSAGE_OUT_PHASE (BSTAT_CMD|BSTAT_MSG|BSTAT_BSY) +#define MESSAGE_IN_PHASE (BSTAT_CMD|BSTAT_MSG|BSTAT_BSY|BSTAT_IO) + +#define PHASE_RESELECTED (BSTAT_SEL|BSTAT_IO) + +#define PHASE_MASK 0x2f +#define RESEL_PHASE_MASK 0x2e + +/* chip type */ +#define TMCCHIP_UNK 0x00 +#define TMCCHIP_1800 0x01 +#define TMCCHIP_18C50 0x02 +#define TMCCHIP_18C30 0x03 + +#define STGIOSZ 0x10 + +#endif /* !_TMC18C30REG_H_ */ diff -urN sys.orig/i386/scsi/tmc18c30/tmc18c30var.h sys/i386/scsi/tmc18c30/tmc18c30var.h --- sys.orig/i386/scsi/tmc18c30/tmc18c30var.h Thu Jan 1 09:00:00 1970 +++ sys/i386/scsi/tmc18c30/tmc18c30var.h Tue Apr 14 08:45:33 1998 @@ -0,0 +1,59 @@ +/* $NetBSD$ */ +/* $Id: tmc18c30var.h,v 1.1.2.1 1997/12/11 14:01:18 itojun Exp $ */ +/* + * [NetBSD for NEC PC98 series] + * Copyright (c) 1996 NetBSD/pc98 porting staff. + * Copyright (c) 1996 Naofumi HONDA. + * Copyright (c) 1996 Kouichi Matsuda. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef _TMC18C30VAR_H_ +#define _TMC18C30VAR_H_ + +/* softc */ +struct stg_softc { + SCSI_LOW_SOFTC + + u_int sc_chip; + + u_int sc_fsz; + u_int8_t sc_fcb; + + u_int8_t sc_fcWinit; + u_int8_t sc_fcRinit; + + u_int8_t sc_fcsp; + u_int8_t sc_icinit; + u_int8_t sc_busc; +}; + +#ifdef __NetBSD__ +int stgprobe __P((struct device *, struct device *, void *)); +void stgattach __P((struct device *, struct device *, void *)); +#endif +int stgprint __P((void *, char *)); +#endif /* !_TMC18C30VAR_H_ */ diff -urN sys.orig/kern/kern_shutdown.c sys/kern/kern_shutdown.c --- sys.orig/kern/kern_shutdown.c Mon Aug 11 11:04:14 1997 +++ sys/kern/kern_shutdown.c Tue Apr 14 08:45:34 1998 @@ -118,6 +118,7 @@ */ static sle_p shutdown_list1; static sle_p shutdown_list2; +static sle_p shutdown_list3; static void dumpsys(void); @@ -249,6 +250,12 @@ } splhigh(); if (howto & RB_HALT) { + ep = shutdown_list3; + while (ep) { + shutdown_list3 = ep->next; + (*ep->function)(howto, ep->arg); + ep = ep->next; + } printf("\n"); printf("The operating system has halted.\n"); printf("Please press any key to reboot.\n\n"); @@ -411,6 +418,9 @@ case SHUTDOWN_POST_SYNC: epp = &shutdown_list2; break; + case SHUTDOWN_PRE_HALT: + epp = &shutdown_list3; + break; default: printf("bad exit callout list specified\n"); return (EINVAL); diff -urN sys.orig/pccard/card.h sys/pccard/card.h --- sys.orig/pccard/card.h Thu Oct 30 09:38:24 1997 +++ sys/pccard/card.h Tue Apr 14 08:45:34 1998 @@ -45,6 +45,8 @@ #define PIOCRWFLAG _IOW('P', 7, int) /* Set flags for drv use */ #define PIOCRWMEM _IOWR('P', 8, unsigned long) /* Set mem for drv use */ #define PIOCSPOW _IOW('P', 9, struct power) /* Set power structure */ +#define PIOCSVIR _IOW('P', 10, int) /* Virtual insert/remove */ +#define PIOCSBEEP _IOW('P', 11, int) /* Select Beep */ /* * Debug codes. */ @@ -54,7 +56,7 @@ /* * Slot states for PIOCGSTATE */ -enum cardstate { noslot, empty, suspend, filled }; +enum cardstate { noslot, empty, suspend, filled, inactive }; /* * Descriptor structure for memory map. diff -urN sys.orig/pccard/cis.h sys/pccard/cis.h --- sys.orig/pccard/cis.h Wed Jun 5 14:30:09 1996 +++ sys/pccard/cis.h Tue Apr 14 08:45:35 1998 @@ -42,6 +42,9 @@ * are terminated with a 0xFF for the tuple code or * the tuple length. */ +#ifndef _PCCARD_CIS_H +#define _PCCARD_CIS_H + #define CIS_NULL 0 /* Empty tuple */ #define CIS_MEM_COMMON 0x01 /* Device descriptor, common memory */ #define CIS_CHECKSUM 0x10 /* Checksum */ @@ -247,3 +250,4 @@ #define CIS_MEM_LENSZ(x) (((x) >> 3) & 0x3) #define CIS_MEM_ADDRSZ(x) (((x) >> 5) & 0x3) #define CIS_MEM_HOST 0x80 +#endif /* _PCCARD_CIS_H */ diff -urN sys.orig/pccard/driver.h sys/pccard/driver.h --- sys.orig/pccard/driver.h Thu Oct 30 09:38:24 1997 +++ sys/pccard/driver.h Tue Apr 14 08:45:36 1998 @@ -16,9 +16,12 @@ int pccard_alloc_intr __P((u_int imask, inthand2_t *hand, int unit, u_int *maskp, u_int *pcic_imask)); #endif +void pccard_driver_init __P((void)); void pccard_configure __P((void)); void pccard_remove_driver __P((struct pccard_device *)); +#if 0 int pcic_probe __P((void)); /* XXX should be linker set */ +#endif enum beepstate { BEEP_ON, BEEP_OFF }; @@ -26,6 +29,6 @@ void pccard_remove_beep __P((void)); void pccard_success_beep __P((void)); void pccard_failure_beep __P((void)); -void pccard_beep_select __P((enum beepstate)); +int pccard_beep_select __P((int)); #endif /* !_PCCARD_DRIVER_H_ */ diff -urN sys.orig/pccard/i82365.h sys/pccard/i82365.h --- sys.orig/pccard/i82365.h Fri Oct 24 03:44:06 1997 +++ sys/pccard/i82365.h Wed Apr 15 16:13:44 1998 @@ -33,18 +33,33 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef _PCCARD_I82365_H +#define _PCCARD_I82365_H + #define PCIC_I82365 0 /* Intel chip */ #define PCIC_IBM 1 /* IBM clone */ #define PCIC_VLSI 2 /* VLSI chip */ #define PCIC_PD672X 3 /* Cirrus logic 627x */ #define PCIC_PD6710 4 /* Cirrus logic 6710 */ -#define PCIC_CL6729 5 /* Cirrus logic 6729 */ +#define PCIC_PD6729 5 /* Cirrus logic 6729 */ #define PCIC_VG468 6 /* Vadem 468 */ #define PCIC_VG469 7 /* Vadem 469 */ #define PCIC_RF5C396 8 /* Ricoh RF5C396 */ #define PCIC_IBM_KING 9 /* IBM KING PCMCIA Controller */ #define PCIC_PC98 10 /* NEC PC98 PCMCIA Controller */ #define PCIC_TI1130 11 /* TI PCI1130 CardBus */ +#define PCIC_TI1131 12 /* TI PCI1131 CardBus */ +#define PCIC_PD6832 13 /* Cirrus logic PD6832 CardBus */ +#define PCIC_O2MICRO 14 /* O2Micro PCI to PC-card bridge */ +#define PCIC_TOSHIBA 15 /* Toshiba PCI to CardBus bridge */ +#define PCIC_RL5C465 16 /* Ricoh PCI to CardBus brige */ +#define PCIC_TI1250 17 /* TI PCI1250 CardBus */ + +/* + * PCI device ID's for PCI to PC-card bridges +*/ +#define PCI_DEVICE_ID_PCIC_CLPD6729 0x11001013ul +#define PCI_DEVICE_ID_PCIC_O2MICRO 0x673a1217ul /* * Address of the controllers. Each controller can manage @@ -57,7 +72,12 @@ * identify the port number, and the lower 6 bits * select one of the 64 possible data registers. */ -#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */ + +#ifndef PCIC_IO +#define PCIC_IO 0x3e0 +#endif /* PCIC_IO */ + +#define PCIC_INDEX_0 PCIC_IO /* index reg, chips 0 and 1 */ #define PCIC_DATA_0 (PCIC_INDEX_0 + 1) /* data reg, chips 0 and 1 */ #define PCIC_INDEX_1 (PCIC_INDEX_0 + 2) /* index reg, chips 2 and 3 */ #define PCIC_DATA_1 (PCIC_INDEX_1 + 1) /* data reg, chips 2 and 3 */ @@ -74,11 +94,20 @@ #define PCIC_IOCTL 0x07 /* I/O Control */ #define PCIC_IO0 0x08 /* I/O Address 0 */ #define PCIC_IO1 0x0c /* I/O Address 1 */ -#define PCIC_MEMBASE 0x10 /* Base of memory window registers */ +#define PCIC_MEMBASE0 0x10 /* Base of memory window registers */ #define PCIC_CDGC 0x16 /* Card Detect and General Control */ +#define PCIC_MEMBASE1 0x18 /* Base of memory window registers */ #define PCIC_MISC1 0x16 /* PD672x: Misc control register 1 per slot */ #define PCIC_GLO_CTRL 0x1e /* Global Control Register */ #define PCIC_MISC2 0x1e /* PD672x: Misc control register 2 per chip */ +#define PCIC_MEMBASE2 0x20 /* Base of memory window registers */ +#define PCIC_MEMBASE3 0x28 /* Base of memory window registers */ +#define PCIC_MEMBASE4 0x30 /* Base of memory window registers */ +#define PCIC_MEMBASEU0 0x40 /* Base of memory window (upper/optional) */ +#define PCIC_MEMBASEU1 0x41 /* Base of memory window (upper/optional) */ +#define PCIC_MEMBASEU2 0x42 /* Base of memory window (upper/optional) */ +#define PCIC_MEMBASEU3 0x43 /* Base of memory window (upper/optional) */ +#define PCIC_MEMBASEU4 0x44 /* Base of memory window (upper/optional) */ #define PCIC_TIME_SETUP0 0x3a #define PCIC_TIME_CMD0 0x3b @@ -221,4 +250,18 @@ #define PCIC_IO_WIN 2 #define PCIC_MEM_WIN 5 +#ifndef PCIC_MAX_SLOTS #define PCIC_MAX_SLOTS 8 +#endif + +/* Cirrus Logic PD-672X specific index */ + +#define CL672X_PCIC_MISC1 0x16 /* Misc Control 1 */ +#define CL672X_PCIC_MISC2 0x1e /* Misc Control 2 */ + +/* Misc 1 regs. */ +#define CL672X_M1_SPKR_EN 0x10 /* Speaker Enable */ +/* Misc 2 regs. */ +#define CL672X_M2_LPDM_EN 0x02 /* Low power dynamic mode */ + +#endif /* _PCCARD_I82365_H */ diff -urN sys.orig/pccard/pccard.c sys/pccard/pccard.c --- sys.orig/pccard/pccard.c Sat Nov 15 23:11:27 1997 +++ sys/pccard/pccard.c Wed Apr 15 11:19:27 1998 @@ -56,6 +56,8 @@ #include +#include /*XXX*/ + /* * XXX We shouldn't be using processor-specific/bus-specific code in * here, but we need the start of the ISA hole (IOM_BEGIN). @@ -78,6 +80,7 @@ #define MIN(a,b) ((a)<(b)?(a):(b)) +static u_int build_freelist(u_int); static int allocate_driver(struct slot *, struct dev_desc *); static void inserted(void *); static void unregister_device_interrupt(struct pccard_devinfo *); @@ -86,7 +89,9 @@ static struct pccard_device *find_driver(char *); static void remove_device(struct pccard_devinfo *); static void slot_irq_handler(int); +#if 0 static void power_off_slot(void *); +#endif #if NAPM > 0 /* @@ -127,6 +132,28 @@ crdioctl, nostop, nullreset, nodevtotty,/* pcmcia */ crdselect, nommap, NULL, "crd", NULL, -1 }; +/* + * Setup PC-card support for all PC-card drivers + */ +void +pccard_driver_init(void) +{ + struct pccard_device **p_drvp; + struct pccard_device *p_drv; + static int already_configured = 0; + + if (already_configured) + return; + already_configured = 1; + + p_drvp = (struct pccard_device **)pccarddrv_set.ls_items; + printf("Initializing PC-card drivers: "); + while ((p_drv = *p_drvp++)) { + printf("%s ", p_drv->name); + pccard_add_driver(p_drv); + } + printf("\n"); +} /* * pccard_configure - called by autoconf code. @@ -138,23 +165,61 @@ * that it sees, and these are mapped to a master * slot number accessed via the character device entries. */ +static u_int +build_freelist(u_int pcic_mask) +{ + inthand2_t *nullfunc; + int irq; + u_int mask, freemask; + + /* No free IRQs (yet). */ + freemask = 0; + + /* Walk through all of the IRQ's and find any that aren't allocated. */ + for (irq = 0; irq < ICU_LEN; irq++) { + /* + * If the PCIC controller can't generate it, don't + * bother checking to see if it it's free. + */ + mask = 1 << irq; + if (!(mask & pcic_mask)) continue; + + /* See if the IRQ is free. */ + if (register_intr(irq, 0, 0, nullfunc, NULL, irq) == 0) { + /* Give it back, but add it to the mask */ + INTRMASK(freemask, mask); + unregister_intr(irq, nullfunc); + } + } +#ifdef PCIC_DEBUG + printf("Freelist of IRQ's <0x%x>\n", freemask); +#endif + return freemask; +} + void pccard_configure(void) { - struct pccard_device **drivers, *drv; + static int already_configured = 0; + u_int free_irqs; + int i; + + if (already_configured) + return; + already_configured = 1; + + /* Determine the list of free interrupts */ + free_irqs = build_freelist(PCIC_INT_MASK_ALLOWED); /*XXX*/ + for (i = 0; i < MAXSLOT; i++) { + if (pccard_slots[i] && pccard_slots[i]->ctrl) + pccard_slots[i]->ctrl->irqs = free_irqs; + } + #include "pcic.h" #if NPCIC > 0 - pcic_probe(); + /*pcic_probe();*/ #endif - - drivers = (struct pccard_device **)pccarddrv_set.ls_items; - printf("Initializing PC-card drivers:"); - while ((drv = *drivers++)) { - printf(" %s", drv->name); - pccard_add_driver(drv); - } - printf("\n"); } /* @@ -277,6 +342,14 @@ } } +static void +really_power_off_slot(void *arg) +{ + struct slot *slt = (struct slot *)arg; + slt->ctrl->disable(slt); +} + +#if 0 /* * Power off the slot. * (doing it immediately makes the removal of some cards unstable) @@ -288,8 +361,15 @@ /* Power off the slot. */ slt->pwr_off_pending = 0; +#if 1 + /* power off the slot 1/2 seconds after removal of cards */ + timeout(really_power_off_slot, (caddr_t)slt, hz / 2); + slt->pwr_off_pending = 1; +#else slt->ctrl->disable(slt); +#endif } +#endif /* * unregister_device_interrupt - Disable the interrupt generation to @@ -345,7 +425,7 @@ unregister_device_interrupt(devi); /* Power off the slot 1/2 second after removal of the card */ - timeout(power_off_slot, (caddr_t)slt, hz / 2); + timeout(really_power_off_slot, (caddr_t)slt, hz / 2); slt->pwr_off_pending = 1; /* De-activate all contexts. */ @@ -439,14 +519,15 @@ * link it into the list of controllers. */ if (ctrl->slots++ == 0) { + static int unit = 0; ctrl->next = cont_list; cont_list = ctrl; if (ctrl->maxmem > NUM_MEM_WINDOWS) ctrl->maxmem = NUM_MEM_WINDOWS; if (ctrl->maxio > NUM_IO_WINDOWS) ctrl->maxio = NUM_IO_WINDOWS; - printf("PC-Card %s (%d mem & %d I/O windows)\n", - ctrl->name, ctrl->maxmem, ctrl->maxio); + printf("PC-Card ctlr(%d) %s (%d mem & %d I/O windows)\n", + unit++, ctrl->name, ctrl->maxmem, ctrl->maxio); } #if NAPM > 0 { @@ -478,10 +559,16 @@ pccard_alloc_intr(u_int imask, inthand2_t *hand, int unit, u_int *maskp, u_int *pcic_imask) { - int irq; + int irq, minirq, maxirq; unsigned int mask; - for (irq = 1; irq < ICU_LEN; irq++) { +#if 0 + minirq = maxirq = PCIC_IRQ; +#else + minirq = 1; + maxirq = ICU_LEN - 1; +#endif /* PCIC_IRQ */ + for (irq = minirq; irq <= maxirq; irq++) { mask = 1ul << irq; if (!(mask & imask)) continue; @@ -532,11 +619,15 @@ * against the slot interrupt (if one has been allocated). */ if (desc->irqmask && drv->imask) { - if ((slt->ctrl->irqs & desc->irqmask) == 0) + if ((slt->ctrl->irqs & desc->irqmask)==0) { + printf("crd: slot_ctrl IRQ mask mismatched."); return(EINVAL); + } if (slt->irq) { - if (((1 << slt->irq) & desc->irqmask) == 0) + if (((1 << slt->irq) & desc->irqmask)==0) { + printf("crd: slot IRQ mismatched."); return(EINVAL); + } slt->irqref++; irq = slt->irq; } else { @@ -548,8 +639,10 @@ irq = pccard_alloc_intr(desc->irqmask, slot_irq_handler, (int)slt, drv->imask, slt->ctrl->imask); - if (irq < 0) + if (irq < 0) { + printf("crd: IRQ allocation failed."); return(EINVAL); + } slt->irq = irq; slt->irqref = 1; slt->ctrl->mapirq(slt, slt->irq); @@ -560,6 +653,7 @@ /* * Create an entry for the device under this slot. */ + devi->running = 1; devi->drv = drv; devi->slt = slt; devi->isahd.id_irq = irq; @@ -587,12 +681,11 @@ * If the enable functions returns no error, then the * device has been successfully installed. If so, then * attach it to the slot, otherwise free it and return - * the error. + * the error. We assume that when we free the device, + * it will also set 'running' to off. */ if (err) remove_device(devi); - else - devi->running = 1; return(err); } @@ -638,14 +731,14 @@ * Enable 5V to the card so that the CIS can be read. */ slt->pwr.vcc = 50; - slt->pwr.vpp = 0; + slt->pwr.vpp = 50; /* * Disable any pending timeouts for this slot, and explicitly * power it off right now. Then, re-enable the power using * the (possibly new) power settings. */ - untimeout(power_off_slot, (caddr_t)slt); - power_off_slot(slt); + untimeout(really_power_off_slot, (caddr_t)slt); + really_power_off_slot(slt); slt->ctrl->power(slt); printf("Card inserted, slot %d\n", slt->slotnum); @@ -673,7 +766,7 @@ * The slot and devices are disabled, but the * data structures are not unlinked. */ - if (slt->state == filled) { + if (slt->state == filled || slt->state == inactive) { int s = splhigh(); disable_slot(slt); slt->state = empty; @@ -686,7 +779,7 @@ case card_inserted: slt->insert_seq = 1; timeout(inserted, (void *)slt, hz/4); - pccard_remove_beep(); + pccard_insert_beep(); break; } } @@ -770,7 +863,7 @@ oldmap = *mp; mp->flags = slt->rwmem|MDF_ACTIVE; #if 0 - printf("Rd at offs %d, size %d\n", (int)uio->uio_offset, + printf("Rd at win %d offs %d, size %d\n", win, (int)uio->uio_offset, uio->uio_resid); #endif while (uio->uio_resid && error == 0) { @@ -857,9 +950,12 @@ struct mem_desc *mp; struct io_desc *ip; int s, err; + int pwval; /* beep is disabled until the 1st call of crdioctl() */ - pccard_beep_select(BEEP_ON); +#if 0 + pccard_beep_select(1); +#endif if (slt == 0 && cmd != PIOCRWMEM) return(ENXIO); @@ -984,6 +1080,59 @@ else pccard_failure_beep(); return err; + /* + * Virtual removal/insertion + * + * State of cards: + * + * insertion virtual removal + * (empty) --------> (filled) --------> (inactive) + * ^ ^ | ^ | | + * | | | | | | + * | +---------------+ +-----------------+ | + * | removal virtual insertion | + * | | + * +----------------------------------------+ + * removal + * + * -- hosokawa + */ + case PIOCSVIR: + pwval = *(int *)data; + /* virtual removal */ + if (!pwval) { + if (slt->state != filled) { + return EINVAL; + } + s = splhigh(); + disable_slot(slt); + slt->state = inactive; + splx(s); + pccard_remove_beep(); +#if 0 + timeout(enable_beep, (void *)NULL, hz/5); +#endif + selwakeup(&slt->selp); + } + /* virtual insertion */ + else { + if (slt->state != inactive) { + return EINVAL; + } + slt->insert_seq = 1; + timeout(inserted, (void *)slt, hz/4); + pccard_insert_beep(); +#if 0 + timeout(enable_beep, (void *)NULL, hz/5); +#endif + break; + } + break; + case PIOCSBEEP: + if (pccard_beep_select(*(int *)data)) { + return EINVAL; + } + break; } return(0); } diff -urN sys.orig/pccard/pccard_beep.c sys/pccard/pccard_beep.c --- sys.orig/pccard/pccard_beep.c Thu Oct 30 09:38:25 1997 +++ sys/pccard/pccard_beep.c Tue Apr 14 08:45:37 1998 @@ -12,6 +12,11 @@ #include +#undef PCCARD_BEEP +#ifndef PCCARD_BEEP +#define PCCARD_BEEP 2 +#endif /* !PCCARD_BEEP */ + #define PCCARD_BEEP_PITCH0 1600 #define PCCARD_BEEP_DURATION0 20 #define PCCARD_BEEP_PITCH1 1200 @@ -19,6 +24,113 @@ #define PCCARD_BEEP_PITCH2 3200 #define PCCARD_BEEP_DURATION2 40 +static void pccard_insert_beep_type0(void) +{ + /* dummy */ +} + +static void pccard_remove_beep_type0(void) +{ + /* dummy */ +} + +static void pccard_success_beep_type0(void) +{ + /* dummy */ +} + +static void pccard_failure_beep_type0(void) +{ + /* dummy */ +} + + +static void pccard_insert_beep_type1(void) +{ + sysbeep(PCCARD_BEEP_PITCH0, PCCARD_BEEP_DURATION0); +} + +static void pccard_remove_beep_type1(void) +{ + sysbeep(PCCARD_BEEP_PITCH0, PCCARD_BEEP_DURATION0); +} + +static void pccard_success_beep_type1(void) +{ + sysbeep(PCCARD_BEEP_PITCH1, PCCARD_BEEP_DURATION1); +} + +static void pccard_failure_beep_type1(void) +{ + sysbeep(PCCARD_BEEP_PITCH2, PCCARD_BEEP_DURATION2); +} + +static void pccard_insert_beep0_type2(void *dummy) +{ + sysbeep_cancel(); + sysbeep(1200, 5); +} + +static void pccard_insert_beep_type2(void) +{ + sysbeep(1600, 20); + timeout(pccard_insert_beep0_type2, NULL, hz / 10); +} + +static void pccard_remove_beep0_type2(void *dummy) +{ + sysbeep_cancel(); + sysbeep(1600, 5); +} + +static void pccard_remove_beep_type2(void) +{ + sysbeep(1200, 20); + timeout(pccard_remove_beep0_type2, NULL, hz / 10); +} + +static void pccard_success_beep1_type2(void *dummy) +{ + sysbeep_cancel(); + sysbeep(800, 15); +} + +static void pccard_success_beep0_type2(void *dummy) +{ + sysbeep_cancel(); + sysbeep(1000, 20); + timeout(pccard_success_beep1_type2, NULL, hz / 15); +} + +static void pccard_success_beep_type2(void) +{ + sysbeep(1200, 20); + timeout(pccard_success_beep0_type2, NULL, hz / 15); +} + +static void pccard_failure_beep1_type2(void *dummy) +{ + sysbeep_cancel(); + sysbeep(2800, 15); +} + +static void pccard_failure_beep0_type2(void *dummy) +{ + sysbeep_cancel(); + sysbeep(2400, 20); + timeout(pccard_failure_beep1_type2, NULL, hz / 15); +} +static void pccard_failure_beep_type2(void) +{ + sysbeep(2000, 20); + timeout(pccard_failure_beep0_type2, NULL, hz / 15); +} + +static void (*insert)(void) = pccard_insert_beep_type0; +static void (*remove)(void) = pccard_remove_beep_type0; +static void (*success)(void) = pccard_success_beep_type0; +static void (*failure)(void) = pccard_failure_beep_type0; + static enum beepstate allow_beep = BEEP_OFF; /* @@ -33,6 +145,58 @@ allow_beep = 1; } +#ifdef PCCARD_BEEP +int pccard_beep_select(int type) +{ + int errcode = 0; + + switch (type) { + case 0: + insert = pccard_insert_beep_type0; + remove = pccard_remove_beep_type0; + success = pccard_success_beep_type0; + failure = pccard_failure_beep_type0; + break; + case 1: + insert = pccard_insert_beep_type1; + remove = pccard_remove_beep_type1; + success = pccard_success_beep_type1; + failure = pccard_failure_beep_type1; + break; + case 2: + insert = pccard_insert_beep_type2; + remove = pccard_remove_beep_type2; + success = pccard_success_beep_type2; + failure = pccard_failure_beep_type2; + break; + default: + errcode = 1; + break; + } + return errcode; +} + +void pccard_insert_beep(void) +{ + insert(); +} + +void pccard_remove_beep(void) +{ + remove(); +} + +void pccard_success_beep(void) +{ + success(); +} + +void pccard_failure_beep(void) +{ + failure(); +} + +#else void pccard_insert_beep(void) { if (allow_beep == BEEP_ON) { @@ -65,3 +229,4 @@ { allow_beep = state; } +#endif /* PCCARD_BEEP */ diff -urN sys.orig/pccard/pcic.c sys/pccard/pcic.c --- sys.orig/pccard/pcic.c Sat Nov 15 23:11:34 1997 +++ sys/pccard/pcic.c Tue Apr 14 13:33:17 1998 @@ -51,11 +51,55 @@ #include #include #include +#include + +static int pcic_probe __P((int, int, int)); + +#ifdef LKM +#define NPCI 0 +#define NPCIC 3 +#else +#include +#include +#endif + +#if NPCI > 0 +extern struct pci_pcic pci_pcics[NPCIC]; +#endif + +static int pcic_isa_probe __P((struct isa_device *)); +static int pcic_isa_attach __P((struct isa_device *)); +struct isa_driver pcicdriver = { + pcic_isa_probe, pcic_isa_attach, "pcic", 0 +}; + +static int +pcic_isa_probe(isa_dev) + struct isa_device *isa_dev; +{ + int ports; + int irq; + + /* isa_dev->id_irq is in "mask" style */ + for (irq = 0; irq < 16; irq++) { + if ((1 << irq) & isa_dev->id_irq) + break; + } + ports = pcic_probe(isa_dev->id_iobase, irq, isa_dev->id_unit); + return ports; +} + +static int +pcic_isa_attach(isa_dev) + struct isa_device *isa_dev; +{ + return 1; /*XXX*/ +} /* * Prototypes for interrupt handler. */ -static void pcicintr __P((int unit)); +void pcicintr __P((int unit)); static int pcic_ioctl __P((struct slot *, int, caddr_t)); static int pcic_power __P((struct slot *)); static timeout_t pcic_reset; @@ -68,7 +112,6 @@ #endif static int pcic_memory(struct slot *, int); static int pcic_io(struct slot *, int); -static u_int build_freelist(u_int); /* * Per-slot data table. @@ -78,17 +121,18 @@ int index; /* Index register */ int data; /* Data register */ int offset; /* Offset value for index */ + int irq; /* controller irq */ char controller; /* Device type */ char revision; /* Device Revision */ struct slot *slt; /* Back ptr to slot */ - u_char (*getb)(struct pcic_slot *, int); - void (*putb)(struct pcic_slot *, int, u_char); + u_char (*getb)(struct pcic_slot *sp, int reg); + void (*putb)(struct pcic_slot *sp, int reg, u_char val); u_char *regs; /* Pointer to regs in mem */ } pcic_slots[PCIC_MAX_SLOTS]; -static int pcic_irq; +static int pcic_irq = 0; /*the control irq for pcic first seen*/ static unsigned pcic_imask; -static struct slot_ctrl cinfo; +static struct slot_ctrl controller_info[NPCIC]; /* @@ -175,20 +219,22 @@ { int err = 0; /* default = success*/ - switch( cmd) { + switch (cmd) { case LKM_E_LOAD: /* * Don't load twice! (lkmexists() is exported by kern_lkm.c) */ - if( lkmexists( lkmtp)) + if (lkmexists(lkmtp)) return( EEXIST); /* * Call the probe routine to find the slots. If * no slots exist, then don't bother loading the module. */ +#if 0 if (pcic_probe() == 0) return(ENODEV); +#endif break; /* Success*/ /* * Attempt to unload the slot driver. @@ -239,18 +285,25 @@ static int pcic_unload(struct lkm_table *lkmtp, int cmd) { - int slot; + int slot, unit; struct pcic_slot *sp = pcic_slots; + struct slot_ctrl *cinfo; untimeout(pcictimeout, 0); - if (pcic_irq) { + if (sp->irq) { for (slot = 0; slot < PCIC_MAX_SLOTS; slot++, sp++) { if (sp->slt) sp->putb(sp, PCIC_STAT_INT, 0); } - unregister_intr(pcic_irq, pcicintr); + unregister_intr(sp->irq, pcicintr); + sp->irq = 0; + } + for (unit; unit < NPCIC; unit++) { + cinfo = &controller_info[unit]; + if (cinfo->slot_use = 'u') { + pccard_remove_controller(cinfo); + } } - pccard_remove_controller(&cinfo); return(0); } @@ -280,38 +333,6 @@ } #endif -static u_int -build_freelist(u_int pcic_mask) -{ - inthand2_t *nullfunc; - int irq; - u_int mask, freemask; - - /* No free IRQs (yet). */ - freemask = 0; - - /* Walk through all of the IRQ's and find any that aren't allocated. */ - for (irq = 1; irq < ICU_LEN; irq++) { - /* - * If the PCIC controller can't generate it, don't - * bother checking to see if it it's free. - */ - mask = 1 << irq; - if (!(mask & pcic_mask)) continue; - - /* See if the IRQ is free. */ - if (register_intr(irq, 0, 0, nullfunc, NULL, irq) == 0) { - /* Give it back, but add it to the mask */ - INTRMASK(freemask, mask); - unregister_intr(irq, nullfunc); - } - } -#ifdef PCIC_DEBUG - printf("Freelist of IRQ's <0x%x>\n", freemask); -#endif - return freemask; -} - /* * entry point from main code to map/unmap memory context. */ @@ -320,7 +341,7 @@ { struct pcic_slot *sp = slt->cdata; struct mem_desc *mp = &slt->mem[win]; - int reg = mp->window * PCIC_MEMSIZE + PCIC_MEMBASE; + int reg = mp->window * PCIC_MEMSIZE + PCIC_MEMBASE0; #ifdef PC98 if (sp->controller == PCIC_PC98) { @@ -538,54 +559,73 @@ * of slot 1. Assume it's the only PCIC whose vendor ID is 0x84, * contact Nate Williams if incorrect. */ -int -pcic_probe(void) +static int +pcic_probe(int iobase, int irq, int unit) { - int slotnum, i, validslots = 0; + int slot, slotnum, i, validslots = 0; u_int free_irqs; struct slot *slt; struct pcic_slot *sp; unsigned char c; static int maybe_vlsi = 0; + int iorange = 0; + int slotoffset = 0; + struct slot_ctrl *cinfo; + + /* try to share slot irq */ + if (irq == 0 && pcic_irq) { + printf("pcic%d: sharing irq %d with other pcic\n", + unit, pcic_irq); + irq = pcic_irq; + } +#if NPCI > 0 + /* pci config overrides the autoconf struct - not checked */ + if (pci_pcics[unit].legacy16) + iobase = pci_pcics[unit].legacy16; +#endif - /* Determine the list of free interrupts */ - free_irqs = build_freelist(PCIC_INT_MASK_ALLOWED); - /* * Initialise controller information structure. */ - cinfo.mapmem = pcic_memory; - cinfo.mapio = pcic_io; - cinfo.ioctl = pcic_ioctl; - cinfo.power = pcic_power; - cinfo.mapirq = pcic_mapirq; - cinfo.reset = pcic_reset; - cinfo.disable = pcic_disable; - cinfo.resume = pcic_resume; - cinfo.maxmem = PCIC_MEM_WIN; - cinfo.maxio = PCIC_IO_WIN; - cinfo.irqs = free_irqs; - cinfo.imask = &pcic_imask; + cinfo = &controller_info[unit]; + cinfo->mapmem = pcic_memory; + cinfo->mapio = pcic_io; + cinfo->ioctl = pcic_ioctl; + cinfo->power = pcic_power; + cinfo->mapirq = pcic_mapirq; + cinfo->reset = pcic_reset; + cinfo->disable = pcic_disable; + cinfo->resume = pcic_resume; + cinfo->maxmem = PCIC_MEM_WIN; + cinfo->maxio = PCIC_IO_WIN; + cinfo->irqs = free_irqs; + cinfo->imask = &pcic_imask; + cinfo->slots = 0; + cinfo->name = "Unknown!"; + cinfo->next = NULL; +#ifdef LKM + cinfo->slot_use = 'u'; +#endif #ifdef LKM bzero(pcic_slots, sizeof(pcic_slots)); #endif - sp = pcic_slots; - for (slotnum = 0; slotnum < PCIC_MAX_SLOTS; slotnum++, sp++) { + slotoffset = unit * 4; + sp = &pcic_slots[slotoffset]; + for (slot = slotoffset; slot < slotoffset + 4; slot++, sp++) { + if (bootverbose) + printf("Checking slot %d.\n", slot); /* * Initialise the PCIC slot table. */ sp->getb = getb1; sp->putb = putb1; - if (slotnum < 4) { - sp->index = PCIC_INDEX_0; - sp->data = PCIC_DATA_0; - sp->offset = slotnum * PCIC_SLOT_SIZE; - } else { - sp->index = PCIC_INDEX_1; - sp->data = PCIC_DATA_1; - sp->offset = (slotnum - 4) * PCIC_SLOT_SIZE; - } + sp->index = iobase; + sp->data = iobase + 1; + iorange = iorange < 2 ? 2 : iorange; + sp->offset = (slot - slotoffset) * PCIC_SLOT_SIZE; + sp->irq = irq; + /* * XXX - Screwed up slot 1 on the VLSI chips. According to * the Linux PCMCIA code from David Hinds, working chipsets @@ -597,6 +637,7 @@ sp->index += 4; sp->data += 4; sp->offset = PCIC_SLOT_SIZE << 1; + iorange = iorange < 6 ? 6 : iorange; } /* * see if there's a PCMCIA controller here @@ -604,6 +645,8 @@ * IBM clone chips use 0x88 and 0x89, apparently */ c = sp->getb(sp, PCIC_ID_REV); + if (bootverbose) + printf("PCIC ID = 0x%02x\n", (u_int)c); sp->revision = -1; switch(c) { /* @@ -669,38 +712,89 @@ sp->revision = 8 - ((c & 0x1F) >> 2); } } +#if NPCI > 0 + if (pci_pcics[unit].bus != 0xffu) { + switch (pci_pcics[unit].pci_id) { + case PCI_DEVICE_ID_PCIC_CLPD6832_CARDBUS: + sp->controller = PCIC_PD6832; + cinfo->name = "Cirrus Logic PD-6832 " + "[i82365 compatible mode]"; + goto skip_isa_id_check; + case PCI_DEVICE_ID_PCIC_TI1130_CARDBUS: + sp->controller = PCIC_TI1130; + cinfo->name = "TI PCI-1130 " + "[i82365 compatible mode]"; + goto skip_isa_id_check; + case PCI_DEVICE_ID_PCIC_TI1131_CARDBUS: + sp->controller = PCIC_TI1131; + cinfo->name = "TI PCI-1131 " + "[i82365 compatible mode]"; + goto skip_isa_id_check; + case PCI_DEVICE_ID_PCIC_TI1250_CARDBUS: + sp->controller = PCIC_TI1250; + cinfo->name = "TI PCI-1250 " + "[i82365 compatible mode]"; + goto skip_isa_id_check; + case PCI_DEVICE_ID_TOSHIBA_TOPCI95_CARDBUS: /* test */ + sp->controller = PCIC_TOSHIBA; + cinfo->name = "TOSHIBA TOPCI95 " + "[i82365 compatible mode]"; + goto skip_isa_id_check; +#ifdef TEST_RL5C465 + case PCI_DEVICE_ID_RICOH_RL5C465_CARDBUS: /* test */ + sp->controller = PCIC_RL5C465; + cinfo->name = "Ricoh RL5C465 " + "[i82365 compatible mode]"; + goto skip_isa_id_check; +#endif + case PCI_DEVICE_ID_PCIC_O2MICRO: + sp->controller = PCIC_O2MICRO; + cinfo->name = "O2micro PCI/PC-Card bridge"; + goto skip_isa_id_check; + case PCI_DEVICE_ID_PCIC_CLPD6729: + sp->controller = PCIC_PD6729; + cinfo->name = "Cirrus Logic PD-6729/6730"; + goto skip_isa_id_check; + } + } +#endif switch(sp->controller) { case PCIC_I82365: - cinfo.name = "Intel 82365"; + cinfo->name = "Intel 82365"; break; case PCIC_IBM: - cinfo.name = "IBM PCIC"; + cinfo->name = "IBM PCIC"; break; case PCIC_IBM_KING: - cinfo.name = "IBM KING PCMCIA Controller"; + cinfo->name = "IBM KING PCMCIA Controller"; break; case PCIC_PD672X: - cinfo.name = "Cirrus Logic PD672X"; + cinfo->name = "Cirrus Logic PD672X"; break; case PCIC_PD6710: - cinfo.name = "Cirrus Logic PD6710"; + cinfo->name = "Cirrus Logic PD6710"; break; case PCIC_VG468: - cinfo.name = "Vadem 468"; + cinfo->name = "Vadem 468"; break; case PCIC_VG469: - cinfo.name = "Vadem 469"; + cinfo->name = "Vadem 469"; break; case PCIC_RF5C396: - cinfo.name = "Ricoh RF5C396"; + cinfo->name = "Ricoh RF5C396"; break; case PCIC_VLSI: - cinfo.name = "VLSI 82C146"; + cinfo->name = "VLSI 82C146"; break; default: - cinfo.name = "Unknown!"; + cinfo->name = "Unknown!"; break; } + +#if NPCI > 0 +skip_isa_id_check: +#endif + #ifndef PCIC_NOCLRREGS /* * clear out the registers. @@ -714,9 +808,11 @@ */ validslots++; sp->slotnum = slotnum; - slt = pccard_alloc_slot(&cinfo); + slt = pccard_alloc_slot(cinfo); if (slt == 0) continue; + printf("pcic%d: slot %d controller I/O address 0x%x\n", + unit, slot - slotoffset, sp->index); slt->cdata = sp; sp->slt = slt; /* @@ -725,24 +821,22 @@ */ if (pcic_irq == 0) { pcic_imask = SWI_MASK; - pcic_irq = pccard_alloc_intr(free_irqs, - pcicintr, 0, &pcic_imask, NULL); - if (pcic_irq < 0) - printf("pcic: failed to allocate IRQ\n"); - else - printf("pcic: controller irq %d\n", pcic_irq); - } - /* - * Modem cards send the speaker audio (dialing noises) - * to the host's speaker. Cirrus Logic PCIC chips must - * enable this. There is also a Low Power Dynamic Mode bit - * that claims to reduce power consumption by 30%, so - * enable it and hope for the best. - */ - if (sp->controller == PCIC_PD672X) { - setb(sp, PCIC_MISC1, PCIC_SPKR_EN); - setb(sp, PCIC_MISC2, PCIC_LPDM_EN); + if (irq) + pcic_irq = irq; + else { + printf( +"pcic%d: no irq specified, no previous pcic irq found\n", unit); + } } +#ifndef NOCIRRUSHACK + switch (sp->controller) { + case PCIC_PD672X: + case PCIC_PD6729: + case PCIC_PD6832: + setb (sp, CL672X_PCIC_MISC1, CL672X_M1_SPKR_EN); + setb (sp, CL672X_PCIC_MISC2, CL672X_M2_LPDM_EN); + } +#endif /* * Check for a card in this slot. */ @@ -756,7 +850,7 @@ /* * Assign IRQ for slot changes */ - if (pcic_irq > 0) + if (sp->irq > 0) sp->putb(sp, PCIC_STAT_INT, (pcic_irq << 4) | 0xF); } #ifdef PC98 @@ -766,15 +860,15 @@ if (inb(PCIC98_REG0) != 0xff) { sp->controller = PCIC_PC98; sp->revision = 0; - cinfo.name = "PC98 Original"; - cinfo.maxmem = 1; - cinfo.maxio = 1; -/* cinfo.irqs = PCIC_INT_MASK_ALLOWED;*/ - cinfo.irqs = 0x1468; + cinfo->name = "PC98 Original"; + cinfo->maxmem = 1; + cinfo->maxio = 1; +/* cinfo->irqs = PCIC_INT_MASK_ALLOWED;*/ + cinfo->irqs = 0x1468; validslots++; sp->slotnum = slotnum; - slt = pccard_alloc_slot(&cinfo); + slt = pccard_alloc_slot(cinfo); if (slt == 0) { printf("pcic98: slt == NULL\n"); goto pcic98_probe_end; @@ -796,7 +890,7 @@ #endif /* PC98 */ if (validslots) timeout(pcictimeout,0,hz/2); - return(validslots); + return validslots ? iorange : 0; } /* @@ -865,6 +959,8 @@ DELAY(100*1000); return (0); #endif + case PCIC_PD6832: + case PCIC_PD6729: case PCIC_PD672X: case PCIC_PD6710: case PCIC_VG468: @@ -907,7 +1003,7 @@ reg |= PCIC_VCC_5V_KING; break; } - reg |= PCIC_VCC_5V; + reg |= PCIC_VCC_3V; if ((sp->controller == PCIC_VG468)|| (sp->controller == PCIC_VG469)) clrb(sp, 0x2f, 0x03) ; @@ -943,9 +1039,10 @@ pcic_mapirq(struct slot *slt, int irq) { struct pcic_slot *sp = slt->cdata; + u_char x; + #ifdef PC98 if (sp->controller == PCIC_PC98) { - unsigned char x; switch (irq) { case 3: x = PCIC98_INT0; break; @@ -977,6 +1074,36 @@ else sp->putb(sp, PCIC_INT_GEN, (sp->getb(sp, PCIC_INT_GEN) & 0xF0) | irq); +#if NPCI > 0 +#if 0 + print_cardbus_registers(0); +#endif + switch (sp->controller) { + /* CLPD-6832 ISA-mode interrupt support (experimental) */ + case PCIC_PD6832: + /* Read "Misc Control 3" register of PD-6832 */ + sp->putb(sp, CL6832_ExCA_EXTEND_INDEX, CL6832_ExCA_EXT_MISC3); + x = sp->getb(sp, CL6832_ExCA_EXTEND_DATA); +#if 1 +printf("Experimental clpd-6832 support:\n"); +printf("please send the following messages to hosokawa@jp.FreeBSD.org\n"); +printf("Misc 3 register is 0x%x\n", (u_int)x); +#endif + x = ((x & ~0x3) | 0x1); /* set "external hardware" bit */ +#if 1 +printf("Set Misc 3 register to 0x%x\n", (u_int)x); +#endif + sp->putb(sp, CL6832_ExCA_EXTEND_INDEX, CL6832_ExCA_EXT_MISC3); + sp->putb(sp, CL6832_ExCA_EXTEND_DATA, x); + + /* check it again */ + sp->putb(sp, CL6832_ExCA_EXTEND_INDEX, CL6832_ExCA_EXT_MISC3); + x = sp->getb(sp, CL6832_ExCA_EXTEND_DATA); +#if 1 +printf("Misc 3 register is 0x%x\n", (u_int)x); +#endif + } +#endif /* NPCI > 0 */ } /* @@ -1021,13 +1148,18 @@ } } slt->insert_seq = 0; - if (sp->controller == PCIC_PD672X || sp->controller == PCIC_PD6710) { + switch (sp->controller) { + case PCIC_PD6710: + case PCIC_PD672X: + case PCIC_PD6729: + case PCIC_PD6832: /* Is this OK? FIXME. */ sp->putb(sp, PCIC_TIME_SETUP0, 0x1); sp->putb(sp, PCIC_TIME_CMD0, 0x6); sp->putb(sp, PCIC_TIME_RECOV0, 0x0); sp->putb(sp, PCIC_TIME_SETUP1, 1); sp->putb(sp, PCIC_TIME_CMD1, 0xf); sp->putb(sp, PCIC_TIME_RECOV1, 0); + sp->putb(sp, PCIC_CDGC, 0x10); } selwakeup(&slt->selp); } @@ -1066,7 +1198,7 @@ * register. If this is non-zero, then a change has occurred * on this card, so send an event to the main code. */ -static void +void pcicintr(int unit) { int slot, s; @@ -1112,7 +1244,7 @@ pcic_resume(struct slot *slt) { struct pcic_slot *sp = slt->cdata; - if (pcic_irq > 0) + if (sp->irq > 0) sp->putb(sp, PCIC_STAT_INT, (pcic_irq << 4) | 0xF); if (sp->controller == PCIC_PD672X) { setb(sp, PCIC_MISC1, PCIC_SPKR_EN); diff -urN sys.orig/pccard/slot.h sys/pccard/slot.h --- sys.orig/pccard/slot.h Thu Oct 30 09:38:27 1997 +++ sys/pccard/slot.h Tue Apr 14 08:45:38 1998 @@ -33,6 +33,10 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +#ifndef _PCCARD_SLOT_H +#define _PCCARD_SLOT_H + + /* * Controller data - Specific to each slot controller. */ @@ -66,6 +70,9 @@ */ struct slot_ctrl *next; /* Allows linked list of controllers */ int slots; /* Slots available */ +#ifdef LKM + char slot_use; /* use */ +#endif }; /* @@ -128,6 +135,9 @@ struct power pwr; /* Power values */ struct slot_ctrl *ctrl; /* Per-controller data */ void *cdata; /* Controller specific data */ +#if NAPM > 0 + int suspend_power; /* Keep power on suspended state */ +#endif /* NAPM > 0 */ int pwr_off_pending;/* Power status of slot */ #ifdef DEVFS void *devfs_token; @@ -141,3 +151,5 @@ struct slot *pccard_alloc_slot(struct slot_ctrl *); void pccard_event(struct slot *, enum card_event); void pccard_remove_controller(struct slot_ctrl *); + +#endif /* _PCCARD_SLOT_H */ diff -urN sys.orig/pci/pci.c sys/pci/pci.c --- sys.orig/pci/pci.c Sat Mar 7 20:33:01 1998 +++ sys/pci/pci.c Tue Apr 14 08:45:39 1998 @@ -403,6 +403,7 @@ u_char reg; u_char pciint; int irq; + u_int classcode; pcici_t tag = pcibus->pb_tag (bus, dev, func); /* @@ -415,8 +416,10 @@ ** Announce this device */ - printf ("%s%d <%s> rev %d", dvp->pd_name, unit, name, - (unsigned) pci_conf_read (tag, PCI_CLASS_REG) & 0xff); + classcode = (unsigned) pci_conf_read (tag, PCI_CLASS_REG) >> 8; + printf ("%s%d <%s> rev %d class %x", dvp->pd_name, unit, name, + (unsigned) pci_conf_read (tag, PCI_CLASS_REG) & 0xff, + classcode); /* ** Get the int pin number (pci interrupt number a-d) @@ -444,6 +447,16 @@ }; printf (" on pci%d:%d:%d\n", bus, dev, func); +#if 0 + if (pci_conf_read(tag, PCI_ID_REG) == 0x71108086ul) { + int elcr2; + /* pci_conf_write (tag, 0x60, 0x0980800aul); */ + pci_conf_write (tag, 0x60, 0x09800a09ul); + elcr2 = inb(0x4d1); + outb(0x4d1, elcr2|0x04); + printf("ELCR2 %02x -> %02x\n", elcr2, elcr2|0x04); + } +#endif /* ** Read the current mapping, diff -urN sys.orig/pci/pcic_p.c sys/pci/pcic_p.c --- sys.orig/pci/pcic_p.c Sun Feb 8 05:42:29 1998 +++ sys/pci/pcic_p.c Tue Apr 14 13:33:16 1998 @@ -32,6 +32,11 @@ #include "pci.h" #if NPCI > 0 +/* share the PCI code with 2.2-RELENG and 3.0-CURRENT */ +#ifndef FREEBSD22 +#define FREEBSD22 +#endif + #include #include #include @@ -39,15 +44,75 @@ #include #include #include +#ifndef FREEBSD22 +#include +#endif /* FREEBSD22 */ #include #include +#ifdef FREEBSD22 +/* enable 3.0-compatible routine for 2.2.x */ +#define PCIFBSD22 +#endif + +#include "pcic.h" + +#ifdef PCIFBSD22 /* compatibility with FreeBSD 3.0 PCI code */ +static u_long pci_cfgread(pcici_t tag, u_long reg, int bytes); +static void pci_cfgwrite(pcici_t tag, u_long reg, u_long data, int bytes); -static u_long pcic_pci_count = 0; +static u_long +pci_cfgread(pcici_t tag, u_long reg, int bytes) +{ + int off; + u_long x; + + off = (reg % 4); + x = pci_conf_read(tag, reg - off); + x >>= (8 * off); + switch (bytes) { + case 1: + x &= 0xffu; + break; + case 2: + x &= 0xffffu; + break; + } + return x; +} + +static void +pci_cfgwrite(pcici_t tag, u_long reg, u_long data, int bytes) +{ + int off; + u_long x, mask; + + off = (reg % 4); + x = pci_conf_read(tag, reg - off); + switch (bytes) { + case 1: + data &= 0xffu; + mask = 0xffu; + break; + case 2: + data &= 0xffffu; + mask = 0xffffu; + break; + } + data <<= (off * 8); + mask <<= (off * 8); + x &= (~mask); + x |= data; + pci_conf_write(tag, reg - off, x); +} +#endif /* PCIFBSD22 */ + +static u_long pcic_pci_count = 0; +static u_int pcic_pci_id = 0; static char *pcic_pci_probe(pcici_t, pcidi_t); static void pcic_pci_attach(pcici_t, int); -static void pd6832_legacy_init(pcici_t tag, int unit); +struct pci_pcic pci_pcics[NPCIC]; static struct pci_device pcic_pci_driver = { "pcic", @@ -66,15 +131,46 @@ static char * pcic_pci_probe(pcici_t tag, pcidi_t type) { + static int initialized = 0; + int i; + + if (!initialized) { + for (i = 0; i < NPCIC; i++) { + pci_pcics[i].bus = pci_pcics[i].slot = 0xffu; /* N/A */ + pci_pcics[i].legacy16 = 0; + } + initialized = 1; + } switch (type) { + /* 32bit CardBus bridges */ + case PCI_DEVICE_ID_PCIC_CLPD6832_CARDBUS: + pcic_pci_id = type; + return "Cirrus Logic PD6832 PCMCIA/CardBus Bridge"; + case PCI_DEVICE_ID_PCIC_TI1130_CARDBUS: + pcic_pci_id = type; + return ("TI PCI-1130 PCMCIA/CardBus Bridge"); + case PCI_DEVICE_ID_PCIC_TI1131_CARDBUS: + pcic_pci_id = type; + return ("TI PCI-1131 PCMCIA/CardBus Bridge"); + case PCI_DEVICE_ID_PCIC_TI1250_CARDBUS: + pcic_pci_id = type; + return ("TI PCI-1250 PCMCIA/CardBus Bridge"); + case PCI_DEVICE_ID_TOSHIBA_TOPCI95_CARDBUS: /* XXX - test */ pcic_pci_id = type; + return ("Toshiba ToPCI95 PCMCIA/CardBus Bridge"); +#ifdef TEST_RL5C465 + case PCI_DEVICE_ID_RICOH_RL5C465_CARDBUS: + pcic_pci_id = type; + return("Ricoo RL5C465 PCMCIA/CardBus Brige"); +#endif + + /* 16bit PC-card bridges */ case PCI_DEVICE_ID_PCIC_CLPD6729: - return ("Cirrus Logic PD6729/6730 PC-Card Controller"); - case PCI_DEVICE_ID_PCIC_CLPD6832: - return ("Cirrus Logic PD6832 CardBus Adapter"); - case PCI_DEVICE_ID_PCIC_TI1130: - return ("TI 1130 PCMCIA/CardBus Bridge"); - case PCI_DEVICE_ID_PCIC_TI1131: - return ("TI 1131 PCI to PCMCIA/CardBus bridge"); + pcic_pci_id = type; + return "Cirrus Logic PD6729/6730 PC-card to PCI Bridge"; + case PCI_DEVICE_ID_PCIC_O2MICRO: + pcic_pci_id = type; + return "O2micro PCI to PC-Card Bridge"; + default: break; } @@ -87,110 +183,263 @@ * it only understands the CL-PD6832. */ static void -pcic_pci_attach(pcici_t config_id, int unit) +pcic_pci_attach(pcici_t tag, int unit) { - u_long pcic_type; /* The vendor id of the PCI pcic */ + int i; + static int x_pcic_unit = 0; + static int pcic_unit = 0; + u_int8_t func; + u_long cardcntl, devcntl; + u_int16_t brgcntl; + u_int8_t bus, slot; - pcic_type = pci_conf_read(config_id, PCI_ID_REG); - switch (pcic_type) { - case PCI_DEVICE_ID_PCIC_CLPD6832: - pd6832_legacy_init(config_id, unit); +#ifdef PCIFBSD22 + switch (pci_mechanism) { + case 1: + bus = (u_int8_t)((tag.cfg1 >> 16ul) & 0xfful); + slot = (u_int8_t)((tag.cfg1 >> 11ul) & 0xfful); + func = (u_int8_t)((tag.cfg1 >> 8ul) & 0x07ul); + break; + case 2: + bus = tag.cfg2.forward; + slot = tag.cfg2.port; break; } +#else /* PCIFBSD22 */ + bus = tag->bus; + slot = tag->slot; +#endif /* PCIFBSD22 */ - if (bootverbose) { - int i, j; - u_char *p; - u_long *pl; + if (bootverbose) + printf("bus = %d, slot = %d, func = %d\n", bus, slot, func); + + /* Assign PCIC unit number (support for multiple PCIC's on PCI bus) */ + for (i = 0; i < x_pcic_unit; i++) { + if (bus == pci_pcics[i].bus && slot == pci_pcics[i].slot) { + goto no_update_pcics; + /* This routine is called every bus/slot/func. + Note that pci_pcics[] corresponds to each PHISICAL + pcic chip, i.e. pci_pcics[0] represents 1st pcic + chip and pci_pcics[1] represents 2nd pcic chip. */ + } + } + pci_pcics[x_pcic_unit].bus = bus; + pci_pcics[x_pcic_unit].slot = slot; + pcic_unit = x_pcic_unit; + x_pcic_unit++; + if (!pci_pcics[pcic_unit].pci_id) + pci_pcics[pcic_unit].pci_id = pcic_pci_id; + +no_update_pcics: + /* Init. CardBus/PC-card controllers as 16-bit PC-card controllers */ + + /* Place any per "slot" initialization here */ + switch (pcic_pci_id) { + /* CardBus Bridges */ + case PCI_DEVICE_ID_PCIC_TI1130_CARDBUS: + case PCI_DEVICE_ID_PCIC_TI1131_CARDBUS: + case PCI_DEVICE_ID_PCIC_TI1250_CARDBUS: + devcntl = pci_cfgread(tag, TI113X_PCI_DEVICE_CONTROL, 1); + if (bootverbose) + printf("TI PCI-113X: device control register is 0x%02x\n", + devcntl); + +/* Usually, it's not necessary to explicit set the following registers + because BOIS will do it for you appropriately. If BIOS doesn't do + a good job, hack the following code:-) */ +#if 0 +/* #define PCIC_TI113X_SERIAL_IRQ */ +#ifndef PCIC_TI113X_SERIAL_IRQ + devcntl = ((devcntl & ~TI113X_DEVCNTL_INTR_MASK) | + TI113X_DEVCNTL_INTR_ISA); +#else + devcntl = ((devcntl & ~TI113X_DEVCNTL_INTR_MASK) | + TI113X_DEVCNTL_INTR_SERIAL); +#endif /* PCIC_TI113X_SERIAL_IRQ */ + pci_cfgwrite(tag, TI113X_PCI_DEVICE_CONTROL, devcntl, 1); + if (bootverbose) + printf("TI PCI-113X: device control register is set to " + "0x%02x\n", devcntl); +#endif /* 0 */ + cardcntl = pci_cfgread(tag, TI113X_PCI_CARD_CONTROL, 1); + if (bootverbose) + printf("TI PCI-113X(%x): card control register [0x%02x]\n", + tag, cardcntl); + cardcntl &= ~(TI113X_CARDCNTL_PCI_IRQ_ENA | + TI113X_CARDCNTL_PCI_IREQ | + TI113X_CARDCNTL_PCI_CSC ); + pci_cfgwrite(tag, TI113X_PCI_CARD_CONTROL, cardcntl, 1); + cardcntl = pci_cfgread(tag, TI113X_PCI_CARD_CONTROL, 1); + if (bootverbose) + printf("TI PCI-113X(%x): card control register is" + " set to 0x%02x\n", tag, cardcntl); + break; + case PCI_DEVICE_ID_PCIC_CLPD6832_CARDBUS: + pci_cfgwrite(tag, PCI_COMMAND_STATUS_REG, + pci_cfgread(tag, PCI_COMMAND_STATUS_REG, 4) & 0xfb000000 | 0x1, 4); + if (bootverbose) + printf("CLPD6832: command status register [0x%02x]\n", + pci_cfgread(tag, PCI_COMMAND_STATUS_REG, 4)); + brgcntl = pci_cfgread(tag, CB_PCI_BRIDGE_CTRL, 2); + brgcntl |= CL6832_BCR_MGMT_INT_ENA; +#ifndef PCIC_CLPD6832_NO_EXPLICIT_ISA_IRQ + brgcntl |= CL6832_BCR_IREQ_INT_ENA; +#endif + pci_cfgwrite(tag, CB_PCI_BRIDGE_CTRL, brgcntl, 2); + if (bootverbose) + printf("CLPD6832: bridge control register is set to " + "0x%02x\n", brgcntl); + break; + case PCI_DEVICE_ID_TOSHIBA_TOPCI95_CARDBUS: + break; + } - printf("PCI Config space:\n"); - for (j = 0; j < 0x98; j += 16) { - printf("%02x: ", j); - for (i = 0; i < 16; i += 4) - printf(" %08x", pci_conf_read(config_id, i+j)); - printf("\n"); + if (func == 0) { + /* Place any per "chip" initialization here */ + switch (pcic_pci_id) { + /* CardBus Bridges */ + case PCI_DEVICE_ID_PCIC_TI1130_CARDBUS: + generic_cardbus_attach(tag, pcic_unit, "TI-1130"); + break; + case PCI_DEVICE_ID_PCIC_TI1131_CARDBUS: + generic_cardbus_attach(tag, pcic_unit, "TI-1131"); + break; + case PCI_DEVICE_ID_PCIC_TI1250_CARDBUS: + generic_cardbus_attach(tag, pcic_unit, "TI-1250"); + break; + case PCI_DEVICE_ID_PCIC_CLPD6832_CARDBUS: + generic_cardbus_attach(tag, pcic_unit, "CLPD-6832"); + break; + case PCI_DEVICE_ID_TOSHIBA_TOPCI95_CARDBUS: + generic_cardbus_attach(tag, pcic_unit, "ToPCI95"); + break; +#ifdef TEST_RL5C465 + case PCI_DEVICE_ID_RICOH_RL5C465_CARDBUS: + generic_cardbus_attach(tag, pcic_unit, "RL5C465"); + break; +#endif + + /* Legacy PC-card Bridges */ + case PCI_DEVICE_ID_PCIC_O2MICRO: + pci_pcics[pcic_unit].legacy16 = + pci_conf_read(tag, PCI_MAP_REG_START) & ~PCI_MAP_IO; + pci_conf_write(tag, PCI_COMMAND_STATUS_REG, + 0x00ff00ff); + break; + case PCI_DEVICE_ID_PCIC_CLPD6729: + pci_pcics[pcic_unit].legacy16 = + pci_conf_read(tag, PCI_MAP_REG_START) & ~PCI_MAP_IO; + /* XXX -- should be initialized more carefully */ + if (!pci_pcics[pcic_unit].legacy16) { + pci_pcics[pcic_unit].legacy16 = PCIC_INDEX_0; + pci_conf_write(tag, PCI_MAP_REG_START, + pci_pcics[pcic_unit].legacy16 | PCI_MAP_IO); + } + pci_conf_write(tag, PCI_COMMAND_STATUS_REG, + 0x00ff00ff); + break; + default: + break; } - p = (u_char *)pmap_mapdev(pci_conf_read(config_id, 0x10), - 0x1000); - pl = (u_long *)p; - printf("Cardbus Socket registers:\n"); - printf("00: "); - for (i = 0; i < 4; i += 1) - printf(" %08x:", pl[i]); - printf("\n10: "); - for (i = 4; i < 8; i += 1) - printf(" %08x:", pl[i]); - printf("\nExCa registers:\n"); - for (i = 0; i < 0x40; i += 16) - printf("%02x: %16D\n", i, p + 0x800 + i, " "); } + + if (!bootverbose) + return; + print_cardbus_registers(tag); + return; } -/* - * Set up the CL-PD6832 to look like a ISA based PCMCIA chip (a - * PD672X). This routine is called once per PCMCIA socket. - */ static void -pd6832_legacy_init(pcici_t tag, int unit) +print_cardbus_registers(pcici_t tag) { - u_long bcr; /* to set interrupts */ - u_short io_port; /* the io_port to map this slot on */ - static int num6832 = 0; /* The number of 6832s initialized */ - - /* - * Some BIOS leave the legacy address uninitialized. This - * insures that the PD6832 puts itself where the driver will - * look. We assume that multiple 6832's should be laid out - * sequentially. We only initialize the first socket's legacy port, - * the other is a dummy. - */ - io_port = PCIC_INDEX_0 + num6832 * CLPD6832_NUM_REGS; - if (unit == 0) - pci_conf_write(tag, CLPD6832_LEGACY_16BIT_IOADDR, - io_port & ~PCI_MAP_IO); - - /* - * I think this should be a call to pci_map_port, but that - * routine won't map regiaters above 0x28, and the register we - * need to map is 0x44. - */ - io_port = pci_conf_read(tag, CLPD6832_LEGACY_16BIT_IOADDR) - & ~PCI_MAP_IO; - - /* - * Configure the first I/O window to contain CLPD6832_NUM_REGS - * words and deactivate the second by setting the limit lower - * than the base. - */ - pci_conf_write(tag, CLPD6832_IO_BASE0, io_port | 1); - pci_conf_write(tag, CLPD6832_IO_LIMIT0, - (io_port + CLPD6832_NUM_REGS) | 1); - - pci_conf_write(tag, CLPD6832_IO_BASE1, (io_port + 0x20) | 1); - pci_conf_write(tag, CLPD6832_IO_LIMIT1, io_port | 1 ); + int i,j; + u_char *p; + u_long *pl; + + printf ("PCI Config space:\n"); + for (j=0;j<0x98;j+=16) { + printf("%02x: ", j); + for (i=0;i<16;i+=4) { + printf(" %08x", pci_conf_read(tag, i+j)); + } + printf("\n"); + } + /* pci_conf_write(tag, 0x44, 1); */ + p = (u_char*)pmap_mapdev(pci_conf_read(tag, 0x10), 0x1000); + pl = (u_long *)p; + printf ("Cardbus Socket registers:\n"); + printf("00: "); + for (i=0;i<4;i+=1) printf(" %08x:",pl[i]); + printf("\n10: "); + for (i=4;i<8;i+=1) printf(" %08x:",pl[i]); + printf ("\nExCa registers:\n"); + for (i=0;i<0x40;i+=16) + printf("%02x: %16D\n",i, p+0x800+i," "); +} - /* - * Set default operating mode (I/O port space) and allocate - * this socket to the current unit. - */ - pci_conf_write(tag, PCI_COMMAND_STATUS_REG, CLPD6832_COMMAND_DEFAULTS ); - pci_conf_write(tag, CLPD6832_SOCKET, unit); +static void +generic_cardbus_attach(pcici_t tag, int pcic_unit, char *name) +{ + u_long legacy16; + u_long devcntl; + int reinit_legacy16; + int i; + legacy16 = pci_cfgread(tag, CB_PCI_LEGACY16_IOADDR, 2) & ~PCI_MAP_IO; /* - * Set up the card inserted/card removed interrupts to come - * through the isa IRQ. + * if BIOS does not set PCIC legacy 16bit I/O address, + * or the second CardBus contorller's I/O address + * set by BIOS conflicts with it of the first one :-<, + * set it to 0x3e0 (for 1st unit) or 0x3e2 (for 2nd + * unit). + * XXX - this code does not support MORE THAN TWO + * PCI-1130 on the same machine. (but most CardBus + * laptops have only one or two CardBus controllers) */ - bcr = pci_conf_read(tag, CLPD6832_BRIDGE_CONTROL); - bcr |= (CLPD6832_BCR_ISA_IRQ|CLPD6832_BCR_MGMT_IRQ_ENA); - pci_conf_write(tag, CLPD6832_BRIDGE_CONTROL, bcr); - - /* After initializing 2 sockets, the chip is fully configured */ - if (unit == 1) - num6832++; + reinit_legacy16 = 0; + if (!legacy16) + reinit_legacy16 = 1; + else + for (i = 0; i < pcic_unit; i++) + if (pci_pcics[i].legacy16 == legacy16) + reinit_legacy16 = 1; + if (reinit_legacy16) { + u_int res; + switch (pcic_unit) { + case 0: + legacy16 = PCIC_INDEX_0 | PCI_MAP_IO; + break; + case 1: + default: /* XXX */ + legacy16 = PCIC_INDEX_1 | PCI_MAP_IO; + break; + } + pci_cfgwrite(tag, CB_PCI_LEGACY16_IOADDR, legacy16, 2); + res = pci_cfgread(tag, CB_PCI_LEGACY16_IOADDR, 2) + & ~PCI_MAP_IO; + if (bootverbose) + printf("%s: unit %d, Legacy PC-card " + "16bit I/O address is set to 0x%x " + "(res: 0x%x)\n", + name, pcic_unit, legacy16, res); + legacy16 = res; + } + else { + /* + * If BIOS sets PCIC legacy 16bit I/O address to + * other address than 0x3e0, leave it untouched. + * (this code *SUPPORTS* multiple PCIC on PCI + * bus) + * pcic_pci_legacy16 is used in pcic_probe(). + */ + if (bootverbose) + printf("%s: unit %d, Legacy PC-card " + "16bit I/O address [0x%x]\n", + name, pcic_unit, legacy16); + } + pci_pcics[pcic_unit].legacy16 = legacy16; - if (bootverbose) - printf("CardBus: Legacy PC-card 16bit I/O address [0x%x]\n", - io_port); } + #endif /* NPCI > 0 */ diff -urN sys.orig/pci/pcic_p.h sys/pci/pcic_p.h --- sys.orig/pci/pcic_p.h Sun Feb 8 05:38:58 1998 +++ sys/pci/pcic_p.h Tue Apr 14 13:33:16 1998 @@ -29,26 +29,142 @@ * $Id: pcic_p.h,v 1.3.2.1 1998/02/07 20:38:58 nate Exp $ */ +#ifndef _PCIC_P_H_ +#define _PCIC_P_H_ + /* PCI/CardBus Device IDs */ -#define PCI_DEVICE_ID_PCIC_CLPD6729 0x11001013ul -#define PCI_DEVICE_ID_PCIC_CLPD6832 0x11101013ul -#define PCI_DEVICE_ID_PCIC_O2MICRO 0x673A1217ul -#define PCI_DEVICE_ID_PCIC_TI1130 0xAC12104Cul -#define PCI_DEVICE_ID_PCIC_TI1131 0xAC15104Cul - -/* CL-PD6832 CardBus defines */ -#define CLPD6832_IO_BASE0 0x002c -#define CLPD6832_IO_LIMIT0 0x0030 -#define CLPD6832_IO_BASE1 0x0034 -#define CLPD6832_IO_LIMIT1 0x0038 -#define CLPD6832_BRIDGE_CONTROL 0x003c -#define CLPD6832_LEGACY_16BIT_IOADDR 0x0044 -#define CLPD6832_SOCKET 0x004c - -/* Configuration constants */ -#define CLPD6832_BCR_MGMT_IRQ_ENA 0x08000000 -#define CLPD6832_BCR_ISA_IRQ 0x00800000 -#define CLPD6832_COMMAND_DEFAULTS 0x00000045 -#define CLPD6832_NUM_REGS 2 +#if 0 +#define PCI_DEVICE_ID_PCIC_CLPD6729 0x11001013ul +#define PCI_DEVICE_ID_PCIC_O2MICRO 0x673A1217ul +#endif +#define PCI_DEVICE_ID_PCIC_CLPD6832_CARDBUS 0x11101013ul +#define PCI_DEVICE_ID_PCIC_TI1130_CARDBUS 0xac12104cul +#define PCI_DEVICE_ID_PCIC_TI1131_CARDBUS 0xac15104cul +#define PCI_DEVICE_ID_PCIC_TI1250_CARDBUS 0xac16104cul +#define PCI_DEVICE_ID_TOSHIBA_TOPCI95_CARDBUS 0x060a1179ul /* XXX - test */ +#define PCI_DEVICE_ID_RICOH_RL5C465_CARDBUS 0x04651180ul /* End of CL-PD6832 defines */ +/* PCI Configuration Registers (common) */ +#define CB_PCI_VENDOR_ID 0x00 /* vendor ID */ +#define CB_PCI_DEVICE_ID 0x02 /* device ID */ +#define CB_PCI_COMMAND 0x04 /* PCI command */ +#define CB_PCI_STATUS 0x06 /* PCI status */ +#define CB_PCI_REVISION_ID 0x08 /* PCI revision ID */ +#define CB_PCI_CLASS 0x09 /* PCI class code */ +#define CB_PCI_CACHE_LINE_SIZE 0x0c /* Cache line size */ +#define CB_PCI_LATENCY 0x0d /* PCI latency timer */ +#define CB_PCI_HEADER_TYPE 0x0e /* PCI header type */ +#define CB_PCI_BIST 0x0f /* Built-in self test */ +#define CB_PCI_SOCKET_BASE 0x10 /* Socket/ExCA base address reg. */ +#define CB_PCI_CB_STATUS 0x16 /* CardBus Status */ +#define CB_PCI_PCI_BUS_NUM 0x18 /* PCI bus number */ +#define CB_PCI_CB_BUS_NUM 0x19 /* CardBus bus number */ +#define CB_PCI_CB_SUB_BUS_NUM 0x1A /* Subordinate CardBus bus number */ +#define CB_PCI_CB_LATENCY 0x1A /* CardBus latency timer */ +#define CB_PCI_MEMBASE0 0x1C /* Memory base register 0 */ +#define CB_PCI_MEMLIMIT0 0x20 /* Memory limit register 0 */ +#define CB_PCI_MEMBASE1 0x24 /* Memory base register 1 */ +#define CB_PCI_MEMLIMIT1 0x28 /* Memory limit register 1 */ +#define CB_PCI_IOBASE0 0x2C /* I/O base register 0 */ +#define CB_PCI_IOLIMIT0 0x30 /* I/O limit register 0 */ +#define CB_PCI_IOBASE1 0x34 /* I/O base register 1 */ +#define CB_PCI_IOLIMIT1 0x38 /* I/O limit register 1 */ +#define CB_PCI_INT_LINE 0x3C /* Interrupt Line */ +#define CB_PCI_INT_PIN 0x3D /* Interrupt Pin */ +#define CB_PCI_BRIDGE_CTRL 0x3E /* Bridge Control */ +#define CB_PCI_SUBSYS_VENDOR_ID 0x40 /* Subsystem Vendor ID */ +#define CB_PCI_SUBSYS_ID 0x42 /* Subsystem ID */ +#define CB_PCI_LEGACY16_IOADDR 0x44 /* Legacy 16bit I/O address */ + + +/* Vendor specific PCI configuration regisrers */ + +/* Texas Instruments PCI-1130/1131 CardBus Controller */ +#define TI113X_PCI_SYSTEM_CONTROL 0x80 /* System Control */ +#define TI113X_PCI_RETRY_STATUS 0x90 /* Retry Status */ +#define TI113X_PCI_CARD_CONTROL 0x91 /* Card Control */ +#define TI113X_PCI_DEVICE_CONTROL 0x92 /* Device Control */ +#define TI113X_PCI_BUFFER_CONTROL 0x93 /* Buffer Control */ +#define TI113X_PCI_SOCKET_DMA0 0x94 /* Socket DMA Register 0 */ +#define TI113X_PCI_SOCKET_DMA1 0x98 /* Socket DMA Register 1 */ + +/* Card control register (TI113X_CARD_CONTROL == 0x91) */ +#define TI113X_CARDCNTL_RING_ENA 0x80u +#define TI113X_CARDCNTL_ZOOM_VIDEO 0x40u +#define TI113X_CARDCNTL_PCI_IRQ_ENA 0x20u +#define TI113X_CARDCNTL_PCI_IREQ 0x10u +#define TI113X_CARDCNTL_PCI_CSC 0x04u +#define TI113X_CARDCNTL_SPKR_ENA 0x02u +#define TI113X_CARDCNTL_INT 0x01u + +/* Device control register (TI113X_DEVICE_CONTROL == 0x92) */ +#define TI113X_DEVCNTL_5V_SOCKET 0x40u +#define TI113X_DEVCNTL_3V_SOCKET 0x20u +#define TI113X_DEVCNTL_INTR_MASK 0x06u +#define TI113X_DEVCNTL_INTR_NONE 0x00u +#define TI113X_DEVCNTL_INTR_ISA 0x02u +#define TI113X_DEVCNTL_INTR_SERIAL 0x04u + +/* Cirrus Logic PD-6832 CardBus Controller */ +#define CL6832_PCI_DMA_SLAVE 0x48 /* DMA Slave configuration */ +#define CL6832_PCI_SOCKET_NUMBER 0x4C /* Socket Number */ + + +/* Vendor specific ExCA registers (for common part, see i82365.h) */ + +/* Texas Instruments PCI-1130/1131 CardBus Controller */ +#define TI113X_ExCA_IO_OFFSET0 0x36 /* Offset of I/O window */ +#define TI113X_ExCA_IO_OFFSET1 0x38 /* Offset of I/O window */ +#define TI113X_ExCA_MEM_WINDOW_PAGE 0x3C /* Memory Window Page */ + +/* Cirrus Logic PD-6832 CardBus Controller */ +#define CL6832_ExCA_MISC1 0x16 /* Misc control 1 */ +#define CL6832_ExCA_FIFO 0x17 /* FIFO control */ +#define CL6832_ExCA_MISC2 0x1e /* Misc control 2 */ +#define CL6832_ExCA_CHIP_INFO 0x1f /* Chip information */ +#define CL6832_ExCA_ATA 0x1f /* ATA Control */ +#define CL6832_ExCA_EXTEND_INDEX 0x2e /* Extended Index */ +#define CL6832_ExCA_EXTEND_DATA 0x2f /* Extended Data */ + +/* Extended Index (CL6832_ExCA_EXTEND_INDEX == 0x2e) */ +#define CL6832_ExCA_EXT_EXT1 0x03 /* Extension Control 1 */ +#define CL6832_ExCA_EXT_GEN_MAP_U0 0x05 /* Gen Map Upper addr 0 */ +#define CL6832_ExCA_EXT_GEN_MAP_U1 0x06 /* Gen Map Upper addr 1 */ +#define CL6832_ExCA_EXT_GEN_MAP_U2 0x07 /* Gen Map Upper addr 2 */ +#define CL6832_ExCA_EXT_GEN_MAP_U3 0x08 /* Gen Map Upper addr 3 */ +#define CL6832_ExCA_EXT_GEN_MAP_U4 0x09 /* Gen Map Upper addr 4 */ +#define CL6832_ExCA_EXT_EXT2 0x0b /* Extension Control 2 */ +#define CL6832_ExCA_EXT_GEN_MAP_U5 0x20 /* Gen Map Upper addr 5 */ +#define CL6832_ExCA_EXT_GEN_MAP_U6 0x21 /* Gen Map Upper addr 6 */ +#define CL6832_ExCA_EXT_PCI_SPACE 0x22 /* PCI Space Control */ +#define CL6832_ExCA_EXT_PCCARD_SPACE 0x23 /* PC-Card Space Control */ +#define CL6832_ExCA_EXT_WINDOW_TYPE 0x24 /* Window Type Select */ +#define CL6832_ExCA_EXT_MISC3 0x25 /* Misc Control 3 */ +#define CL6832_ExCA_EXT_SMB_POWER 0x26 /* SMB Sock Pwr Ctrl Addr */ +#define CL6832_ExCA_EXT_GEN_MAP_EXTRA0 0x27 /* Gen Map Extra Control 0 */ +#define CL6832_ExCA_EXT_GEN_MAP_EXTRA1 0x28 /* Gen Map Extra Control 1 */ +#define CL6832_ExCA_EXT_GEN_MAP_EXTRA2 0x29 /* Gen Map Extra Control 2 */ +#define CL6832_ExCA_EXT_GEN_MAP_EXTRA3 0x2a /* Gen Map Extra Control 3 */ +#define CL6832_ExCA_EXT_GEN_MAP_EXTRA4 0x2b /* Gen Map Extra Control 4 */ +#define CL6832_ExCA_EXT_GEN_MAP_EXTRA5 0x2c /* Gen Map Extra Control 5 */ +#define CL6832_ExCA_EXT_GEN_MAP_EXTRA6 0x2d /* Gen Map Extra Control 6 */ +#define CL6832_ExCA_EXT_CSC 0x2e /* Ext. Card Status Change */ +#define CL6832_ExCA_EXT_MISC4 0x2f /* Misc Control 4 */ +#define CL6832_ExCA_EXT_MISC5 0x30 /* Misc Control 5 */ +#define CL6832_ExCA_EXT_MISC6 0x31 /* Misc Control 5 */ + +/* bridge control register (CB_PCI_BRIDGE_CTRL) */ +#define CL6832_BCR_IREQ_INT_ENA 0x0080u /* IREQ-INT Enable */ +#define CL6832_BCR_MGMT_INT_ENA 0x0800u /* Management Intr Enable */ + +/* infomation about PCI PC-card bridges */ + +struct pci_pcic { + u_int8_t bus; + u_int8_t slot; + u_long legacy16; + u_int pci_id; +}; + +#endif /* _PCIC_P_H_ */ diff -urN sys.orig/pci/pcisupport.c sys/pci/pcisupport.c --- sys.orig/pci/pcisupport.c Sun Mar 1 19:12:00 1998 +++ sys/pci/pcisupport.c Tue Apr 14 08:45:41 1998 @@ -735,10 +735,12 @@ break; case 0x70008086: case 0x122e8086: + case 0x71108086: writeconfig (config_id, conf82371fb); break; case 0x70108086: case 0x12308086: + case 0x71118086: writeconfig (config_id, conf82371fb2); break; #if 0 diff -urN sys.orig/scsi/scsiconf.c sys/scsi/scsiconf.c --- sys.orig/scsi/scsiconf.c Sun Mar 8 15:53:13 1998 +++ sys/scsi/scsiconf.c Tue Apr 14 08:45:41 1998 @@ -27,7 +27,7 @@ #include #include #include -#ifdef PC98 +#if 1 /* PC98 */ #include #endif @@ -52,6 +52,10 @@ static void extend_release __P((struct extend_array *ea, int index)); static void *extend_set __P((struct extend_array *ea, int index, void *value)); +#ifdef SCSI_DETACH +#define UNIT_ALLOC_COUNT 32 +#endif + /* * XXX SCSI_DEVICE_ENTRIES() generates extern switches but it should * generate static switches except for this. Separate macros are @@ -645,10 +649,18 @@ * a wired down device, but we do want "sd 4 target 5" or * even "sd 4 scbus 1" to match. */ +#ifdef SCSI_DETACH + if (IS_SPECIFIED(sdc->unit) && + (IS_SPECIFIED(sdc->target) || IS_SPECIFIED(sdc->cunit ))) { + for(i = 0; i <= sdc->unit; i++) + scsi_unit_use(sd, i); + } +#else if (IS_SPECIFIED(sdc->unit) && (IS_SPECIFIED(sdc->target) || IS_SPECIFIED(sdc->cunit)) && sd->free_unit <= sdc->unit) sd->free_unit = sdc->unit + 1; +#endif } } } @@ -720,7 +732,7 @@ { int i; int found; -#ifdef PC98 +#if 1 /* PC98 */ struct cfdata cf; cf.cf_flags = 0; #endif @@ -736,7 +748,7 @@ sc_link->scsibus == scsi_dinit[i].cunit) { sc_link->dev_unit = scsi_dinit[i].unit; found = 1; -#ifdef PC98 +#if 1 /* PC98 */ cf.cf_flags = scsi_dinit[i].flags; #endif if (bootverbose) @@ -746,10 +758,17 @@ } } - if (!found) + if (!found) { +#ifdef SCSI_DETACH + sc_link->dev_unit = + scsi_free_unit_get(sc_link->device); + scsi_unit_use(sc_link->device, sc_link->dev_unit); +#else sc_link->dev_unit = sc_link->device->free_unit++; +#endif + } -#ifdef PC98 +#if 1 /* PC98 */ if (!found) { for (i = 0; scsi_dinit[i].name; i++) { if ((strcmp(sc_link->device->name, scsi_dinit[i].name) == 0) && @@ -1472,3 +1491,113 @@ } return (bestmatch); } + +#ifdef SCSI_DETACH +void +scsi_unit_use(struct scsi_device *device, int unit) +{ + int n, i; + u_char mask; + u_char *q; + + if (device->free_unit == 0 || unit >= UNIT_ALLOC_COUNT ) { + if (device->free_unit == 0) + device->max_unit_count = UNIT_ALLOC_COUNT; + n = device->max_unit_count * sizeof(char); + q = malloc(n, M_DEVBUF, M_NOWAIT); + bzero(q, n); + if (device->free_unit) { + bcopy(device->free_unit, q, + n - (UNIT_ALLOC_COUNT * sizeof(char))); + free(device->free_unit,M_DEVBUF); + } + device->free_unit = q; + device->max_unit_count =+ UNIT_ALLOC_COUNT; + } + i = unit / (sizeof(char) * 8); + mask = 1 << (unit - (i * sizeof(char) * 8)); + (device->free_unit)[i] = (device->free_unit)[i] | mask; +#ifdef PCCARD_LKM_DEBUG + printf("unit_use:free_unit=%x mask=%x unit=%d\n", + (device->free_unit)[i], mask, unit); +#endif +} + +void +scsi_unit_unuse(struct scsi_device *device, int unit) +{ + int i; + u_char mask; + + i = unit / (sizeof(char) * 8); + mask = 1 << (unit - (i * sizeof(char) * 8)); +#ifdef PCCARD_LKM_DEBUG + printf("unit_unuse:free_unit=%x i=%d\n",(device->free_unit)[i],i); +#endif + (device->free_unit)[i] = (device->free_unit)[i] & ~mask; +#ifdef PCCARD_LKM_DEBUG + printf("unit_unuse:free_unit=%x mask=%x unit=%d\n", + (device->free_unit)[i],~mask,unit); +#endif +} + +int +scsi_free_unit_get(struct scsi_device *device) +{ + int i, j, n; + u_char mask; + + if (device->free_unit == 0) { + device->max_unit_count = UNIT_ALLOC_COUNT; + n = device->max_unit_count * sizeof(char); + device->free_unit = malloc(n,M_DEVBUF,M_NOWAIT); + bzero(device->free_unit,n); + } +#ifdef PCCARD_LKM_DEBUG + printf("scsi:unit_get free_unit = %x\n",device->free_unit); +#endif + for(i = 0; i <= (device->max_unit_count / (sizeof(char) * 8)); i++) { + for(j = 0; j < 8; j++) { + mask = 1 << j; +#ifdef PCCARD_LKM_DEBUG + printf("unit_get:mask=%x free_unit=%x\n", + mask,(device->free_unit)[i]); +#endif + if(mask & (device->free_unit)[i]) + continue; + else + return(i * sizeof(char) * 8 + j); + } + } + return(i * sizeof(char) * 8); +} + +void +scsi_detachdev(struct scsibus_data *scbus) +{ + struct scsi_link *sc_link; + int targ,lun,i; + + for(targ = 0; targ < scbus->maxtarg + 1; targ++) { + for(lun=0; lun < 8; lun++) { + sc_link = (*scbus->sc_link)[targ][lun]; + if (sc_link) { + scsi_unit_unuse(sc_link->device, + sc_link->dev_unit); + extend_release(scbusses,sc_link->scsibus); + scsi_free_unit(sc_link); + } + } + } + free(scbus->sc_link, M_TEMP); + scbus->sc_link = NULL; + free(scbus,M_TEMP); + scbus = NULL; +} + +struct scsibus_data * +scsi_extend_get(int bus) +{ + return (struct scsibus_data *)extend_get(scbusses, bus); +} +#endif /* SCSI_DETACH */ diff -urN sys.orig/scsi/scsiconf.h sys/scsi/scsiconf.h --- sys.orig/scsi/scsiconf.h Tue Dec 30 12:46:56 1997 +++ sys/scsi/scsiconf.h Tue Apr 14 08:45:42 1998 @@ -56,7 +56,7 @@ struct buf; struct scsi_xfer; -#ifdef PC98 +#if 1 /* PC98 */ struct cfdata; #endif @@ -77,7 +77,7 @@ { /* 04*/ int32_t (*scsi_cmd) __P((struct scsi_xfer *xs)); /* 08*/ void (*scsi_minphys) __P((struct buf *bp)); -#ifdef PC98 +#if 1 /* PC98 */ /* 12*/ int32_t (*open_target_lu) __P((struct scsi_link *sc_link, struct cfdata *cf)); #else @@ -170,8 +170,15 @@ /* 80*/ struct extend_array *links; -/* 84*/ int free_unit; +#ifdef SCSI_DETACH +/* 84*/ u_char *free_unit; +#else +/* 88*/ int free_unit; +#endif /* 88*/ struct scsi_device *next; /* Next in list in the registry. */ +#ifdef SCSI_DETACH + int max_unit_count; +#endif }; /* SCSI_DEVICE_ENTRIES: A macro to generate all the entry points from the @@ -486,6 +493,14 @@ errval scsi_change_def( struct scsi_link *sc_link, u_int32_t flags); #endif #endif /* KERNEL */ + +#ifdef SCSI_DETACH +void scsi_unit_use __P((struct scsi_device *, int)); +void scsi_unit_unuse __P((struct scsi_device *, int)); +int scsi_free_unit_get __P((struct scsi_device *)); +struct scsibus_data *scsi_extend_get(int); +void scsi_detachdev __P((struct scsibus_data *)); +#endif #define SCSI_EXTERNALLEN (sizeof(struct scsi_link)) diff -urN sys.orig/sys/systm.h sys/sys/systm.h --- sys.orig/sys/systm.h Mon Aug 11 11:04:12 1997 +++ sys/sys/systm.h Tue Apr 14 08:45:42 1998 @@ -171,6 +171,7 @@ int rm_at_shutdown(bootlist_fn function, void *arg); #define SHUTDOWN_PRE_SYNC 0 #define SHUTDOWN_POST_SYNC 1 +#define SHUTDOWN_PRE_HALT 2 /* forking */ /* XXX not yet */ typedef void (*forklist_fn)(struct proc *parent,struct proc *child,int flags);