diff options
author | svn2git <svn2git@FreeBSD.org> | 1994-07-01 00:00:00 -0800 |
---|---|---|
committer | svn2git <svn2git@FreeBSD.org> | 1994-07-01 00:00:00 -0800 |
commit | 5e0e9b99dc3fc0ecd49d929db0d57c784b66f481 (patch) | |
tree | e779b5a6edddbb949b7990751b12d6f25304ba86 /sys/i386/isa | |
parent | a16f65c7d117419bd266c28a1901ef129a337569 (diff) | |
download | src-releng/1.tar.gz src-releng/1.zip |
Release FreeBSD 1.1.5.1release/1.1.5.1_cvsreleng/1
This commit was manufactured to restore the state of the 1.1.5.1-RELEASE image.
Releases prior to 5.3-RELEASE are omitting the secure/ and crypto/ subdirs.
Diffstat (limited to 'sys/i386/isa')
90 files changed, 20908 insertions, 6041 deletions
diff --git a/sys/i386/isa/aha1542.c b/sys/i386/isa/aha1542.c index efeee385ba51..df1e3cada643 100644 --- a/sys/i386/isa/aha1542.c +++ b/sys/i386/isa/aha1542.c @@ -12,7 +12,7 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * $Id: aha1542.c,v 1.20.2.2 1994/05/03 05:16:50 rgrimes Exp $ + * $Id: aha1542.c,v 1.27 1994/06/05 19:18:10 ats Exp $ */ /* @@ -299,8 +299,8 @@ struct aha_data { struct aha_ccb *aha_ccb_free; /* the next free ccb */ struct aha_ccb aha_ccb[AHA_MBX_SIZE]; /* all the CCBs */ int aha_int; /* our irq level */ - int aha_dma; /* out DMA req channel */ - int aha_scsi_dev; /* ourscsi bus address */ + int aha_dma; /* our DMA req channel */ + int aha_scsi_dev; /* our scsi bus address */ struct scsi_link sc_link; /* prototype for subdevs */ } *ahadata[NAHA]; @@ -589,6 +589,7 @@ ahaattach(dev) aha->sc_link.adapter_targ = aha->aha_scsi_dev; aha->sc_link.adapter = &aha_switch; aha->sc_link.device = &aha_dev; + aha->sc_link.flags = SDEV_BOUNCE; /* * ask the adapter what subunits are present @@ -741,7 +742,7 @@ aha_get_ccb(unit, flags) * to come free */ while ((!(rc = aha->aha_ccb_free)) && (!(flags & SCSI_NOSLEEP))) { - sleep(&aha->aha_ccb_free, PRIBIO); + tsleep((caddr_t)&aha->aha_ccb_free, PRIBIO, "ahaccb", 0); } if (rc) { aha->aha_ccb_free = aha->aha_ccb_free->next; @@ -916,7 +917,7 @@ aha_init(unit) #ifdef AHADEBUG printf("aha%d: extended bios flags %x\n", unit, extbios.flags); #endif /* AHADEBUG */ - printf("aha%d: 1542C/CF detected, unlocking mailbox\n"); + printf("aha%d: 1542C/CF detected, unlocking mailbox\n", unit); aha_cmd(unit, 2, 0, 0, 0, AHA_MBX_ENABLE, 0, extbios.mailboxlock); } @@ -1006,7 +1007,7 @@ aha_init(unit) return (EIO); } #else - printf ("\n"); + printf (" (bus speed defaulted)\n"); #endif /*TUNE_1542*/ /* * Initialize mail box diff --git a/sys/i386/isa/bt742a.c b/sys/i386/isa/bt742a.c index e160e9167cb1..990344f28ea6 100644 --- a/sys/i386/isa/bt742a.c +++ b/sys/i386/isa/bt742a.c @@ -12,7 +12,7 @@ * on the understanding that TFS is not responsible for the correct * functioning of this software in any circumstances. * - * $Id: bt742a.c,v 1.12 1993/12/19 00:50:29 wollman Exp $ + * $Id: bt742a.c,v 1.22 1994/06/15 04:19:23 jkh Exp $ */ /* @@ -126,7 +126,7 @@ struct bt_cmd_buf { * these could be bigger but we need the bt_data to fit on a single page.. */ -#define BT_MBX_SIZE 16 /* mail box size (MAX 255 MBxs) */ +#define BT_MBX_SIZE 32 /* mail box size (MAX 255 MBxs) */ /* don't need that many really */ #define BT_CCB_MAX 32 /* store up to 32CCBs at any one time */ /* in bt742a H/W ( Not MAX ? ) */ @@ -299,6 +299,21 @@ struct bt_config { u_char :5; }; +/* + * Determin 32bit address/Data firmware functionality from Bus type + * Note: bt742a/747[s|d]/757/946/445s will return 'E' + * bt542b/545s/545d will be return 'A' + * 94/05/18 amurai@spec.co.jp + */ +#define BT_BUS_TYPE_24bit 'A' /* PC/AT 24 bit address bus type */ +#define BT_BUS_TYPE_32bit 'E' /* EISA/VLB/PCI 32 bit address bus type */ +#define BT_BUS_TYPE_MCA 'M' /* Micro chanel is ? forget it right now */ +struct bt_ext_info { + u_char bus_type; /* Host adapter bus type */ + u_char bios_addr; /* Bios Address-Not use*/ + u_short max_seg; /* Max segment List */ +}; + #define INT9 0x01 #define INT10 0x02 #define INT11 0x04 @@ -568,7 +583,8 @@ btprobe(dev) } /* * If it's there, put in it's interrupt vectors - */ dev->id_unit = unit; + */ + dev->id_unit = unit; dev->id_irq = (1 << bt->bt_int); dev->id_drq = bt->bt_dma; @@ -593,6 +609,7 @@ btattach(dev) bt->sc_link.adapter_targ = bt->bt_scsi_dev; bt->sc_link.adapter = &bt_switch; bt->sc_link.device = &bt_dev; + bt->sc_link.flags = SDEV_BOUNCE; /* * ask the adapter what subunits are present @@ -818,7 +835,8 @@ bt_get_ccb(unit, flags) goto gottit; } else { if (!(flags & SCSI_NOSLEEP)) { - sleep(&bt->bt_ccb_free, PRIBIO); + tsleep((caddr_t)&bt->bt_ccb_free, PRIBIO, + "btccb", 0); } } } @@ -987,6 +1005,7 @@ bt_init(unit) unsigned char ad[4]; volatile int i, sts; struct bt_config conf; + struct bt_ext_info info; /* * reset board, If it doesn't respond, assume @@ -1008,6 +1027,32 @@ bt_init(unit) return (ENXIO); } /* + * Make sure board has a capability of 32bit addressing. + * and Firmware also need a capability of 32bit addressing pointer + * in Extended mailbox and ccb structure. + * 94/05/18 amurai@spec.co.jp + */ + bt_cmd(unit, 1, sizeof(info),0,&info, BT_INQUIRE_EXTENDED,sizeof(info)); + switch (info.bus_type) { + case BT_BUS_TYPE_24bit: /* PC/AT 24 bit address bus */ + printf("bt%d: bt54x-ISA(24bit) bus detected\n", unit); + break; + case BT_BUS_TYPE_32bit: /* EISA/VLB/PCI 32 bit bus */ + printf("bt%d: PCI/EISA/VLB(32bit) bus detected\n",unit); + break; + case BT_BUS_TYPE_MCA: /* forget it right now */ + printf("bt%d: MCA bus architecture detected..", unit); + printf("[giving up]\n"); + return (ENXIO); + break; + default: + printf("bt%d: Unknown state detected...", unit); + printf("[giving up]\n"); + return (ENXIO); + break; + } + + /* * Assume we have a board at this stage * setup dma channel from jumpers and save int * level @@ -1107,8 +1152,6 @@ bt_init(unit) bt->bt_mbx.tmbi = &bt->bt_mbx.mbi[0]; bt_inquire_setup_information(unit); - /* Enable round-robin scheme - appeared at firmware rev. 3.31 */ - bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE); /* * Note that we are going and return (to probe) @@ -1122,9 +1165,14 @@ bt_inquire_setup_information(unit) { struct bt_data *bt = btdata[unit]; struct bt_setup setup; + char dummy[8]; struct bt_boardID bID; int i; + /* Inquire Installed Devices */ + bzero( &dummy[0], sizeof(dummy) ); + bt_cmd(unit, 0, sizeof(dummy), 10, &dummy[0], BT_DEV_GET); + /* Inquire Board ID to Bt742 for firmware version */ bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE); printf("bt%d: version %c.%c, ", @@ -1143,19 +1191,37 @@ bt_inquire_setup_information(unit) } else { printf("no parity, "); } - printf("%d mbxs, %d ccbs\n", setup.num_mbx, bt->numccbs); + printf("%d mbxs, %d ccbs\n", setup.num_mbx, BT_CCB_MAX); + + /* + * Displaying SCSI negotiation value by each target. + * How can I determin FAST scsi value? XXX amurai@spec.co.jp + */ for (i = 0; i < 8; i++) { - if (!setup.sync[i].offset && - !setup.sync[i].period && - !setup.sync[i].valid) + if (!setup.sync[i].valid) continue; + if (!setup.sync[i].offset && !setup.sync[i].period ) + printf("bt%d: targ %d async\n", unit, i); + else + printf("bt%d: targ %d offset=%02d, period=%dnsec\n", + unit, i, + setup.sync[i].offset, + 200 + setup.sync[i].period * 50 ); + } - printf("bt%d: dev%02d Offset=%d,Transfer period=%d, Synchronous? %s", - unit, i, - setup.sync[i].offset, setup.sync[i].period, - setup.sync[i].valid ? "Yes" : "No"); + /* + * Enable round-robin scheme - appeared at firmware rev. 3.31 + * Below rev 2.XX firmware has a problem for issuing + * BT_ROUND_ROBIN command amurai@spec.co.jp + */ + if ( bID.firm_revision != '2' ) { + printf("bt%d: Enabling Round robin scheme\n", unit); + bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE); + } else { + printf("bt%d: Not Enabling Round robin scheme\n", unit); } + } #ifndef min @@ -1434,13 +1500,15 @@ bt_timeout(caddr_t arg1, int arg2) struct bt_data *bt; int s = splbio(); + /* + * A timeout routine in kernel DONOT unlink + * Entry chains when time outed....So infinity Loop.. + * 94/04/20 amurai@spec.co.jp + */ + untimeout(bt_timeout, (caddr_t)ccb); + unit = ccb->xfer->sc_link->adapter_unit; bt = btdata[unit]; - printf("bt%d:%d:%d (%s%d) timed out ", unit - ,ccb->xfer->sc_link->target - ,ccb->xfer->sc_link->lun - ,ccb->xfer->sc_link->device->name - ,ccb->xfer->sc_link->dev_unit); #ifdef UTEST bt_print_active_ccbs(unit); @@ -1467,13 +1535,14 @@ bt_timeout(caddr_t arg1, int arg2) ccb->xfer->retries = 0; /* I MEAN IT ! */ ccb->host_stat = BT_ABORTED; bt_done(unit, ccb); - } else { /* abort the operation that has timed out */ + } else { + /* abort the operation that has timed out */ printf("bt%d: Try to abort\n", unit); bt_send_mbo(unit, ~SCSI_NOMASK, BT_MBO_ABORT, ccb); /* 2 secs for the abort */ - timeout(bt_timeout, (caddr_t)ccb, 2 * hz); ccb->flags = CCB_ABORTED; + timeout(bt_timeout, (caddr_t)ccb, 2 * hz); } splx(s); } diff --git a/sys/i386/isa/bt742a_32.c b/sys/i386/isa/bt742a_32.c new file mode 100644 index 000000000000..f8eabf4de61f --- /dev/null +++ b/sys/i386/isa/bt742a_32.c @@ -0,0 +1,1694 @@ +/* + * Written by Julian Elischer (julian@tfs.com) + * for TRW Financial Systems for use under the MACH(2.5) operating system. + * + * TRW Financial Systems, in accordance with their agreement with Carnegie + * Mellon University, makes this software available to CMU to distribute + * or use in any manner that they see fit as long as this message is kept with + * the software. For this reason TFS also grants any other persons or + * organisations permission to use or modify this software. + * + * TFS supplies this software to be publicly redistributed + * on the understanding that TFS is not responsible for the correct + * functioning of this software in any circumstances. + * + * $Id: bt742a_32.c,v 1.1 1994/06/17 19:05:57 jkh Exp $ + */ + +/* + * Bulogic/Bustek 32 bit Addressing Mode SCSI driver (Was bt742a.c) + * + * THIS DRIVER IS EXPERIMENTAL AND NOT ENABLED BY DEFAULT. It is + * provided here merely for reference purposes. + * + * NOTE: 1. Some bt5xx card can NOT handling 32 bit addressing mode. + * 2. OLD bt445s Revision A,B,C,D(nowired) + any firmware version + * has broken busmaster for handling 32 bit addressing on H/W bus side. + * 3. Extend probing still need to confirm by user base due to + * several H/W and firmware dependency. If you have a problem with + * extend probing, please contact to 'amurai@spec.co.jp' + * + * amurai@spec.co.jp 94/6/16 + */ + +#include <sys/types.h> + +#ifdef KERNEL /* don't laugh.. it compiles to a program too.. look */ +#include <bt.h> +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#endif /* KERNEL */ + +#include <i386/isa/isa_device.h> +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#ifdef KERNEL +#include "ddb.h" +#include "kernel.h" +#else /*KERNEL */ +#define NBT 1 +#endif /*KERNEL */ + +typedef unsigned long int physaddr; + +/* + * I/O Port Interface + */ + +#define BT_BASE bt->bt_base +#define BT_CTRL_STAT_PORT (BT_BASE + 0x0) /* control & status */ +#define BT_CMD_DATA_PORT (BT_BASE + 0x1) /* cmds and datas */ +#define BT_INTR_PORT (BT_BASE + 0x2) /* Intr. stat */ + +/* + * BT_CTRL_STAT bits (write) + */ + +#define BT_HRST 0x80 /* Hardware reset */ +#define BT_SRST 0x40 /* Software reset */ +#define BT_IRST 0x20 /* Interrupt reset */ +#define BT_SCRST 0x10 /* SCSI bus reset */ + +/* + * BT_CTRL_STAT bits (read) + */ + +#define BT_STST 0x80 /* Self test in Progress */ +#define BT_DIAGF 0x40 /* Diagnostic Failure */ +#define BT_INIT 0x20 /* Mbx Init required */ +#define BT_IDLE 0x10 /* Host Adapter Idle */ +#define BT_CDF 0x08 /* cmd/data out port full */ +#define BT_DF 0x04 /* Data in port full */ +#define BT_INVDCMD 0x01 /* Invalid command */ + +/* + * BT_CMD_DATA bits (write) + */ + +#define BT_NOP 0x00 /* No operation */ +#define BT_MBX_INIT 0x01 /* Mbx initialization */ +#define BT_START_SCSI 0x02 /* start scsi command */ +#define BT_START_BIOS 0x03 /* start bios command */ +#define BT_INQUIRE 0x04 /* Adapter Inquiry */ +#define BT_MBO_INTR_EN 0x05 /* Enable MBO available interrupt */ +#define BT_SEL_TIMEOUT_SET 0x06 /* set selection time-out */ +#define BT_BUS_ON_TIME_SET 0x07 /* set bus-on time */ +#define BT_BUS_OFF_TIME_SET 0x08 /* set bus-off time */ +#define BT_SPEED_SET 0x09 /* set transfer speed */ +#define BT_DEV_GET 0x0a /* return installed devices */ +#define BT_CONF_GET 0x0b /* return configuration data */ +#define BT_TARGET_EN 0x0c /* enable target mode */ +#define BT_SETUP_GET 0x0d /* return setup data */ +#define BT_WRITE_CH2 0x1a /* write channel 2 buffer */ +#define BT_READ_CH2 0x1b /* read channel 2 buffer */ +#define BT_WRITE_FIFO 0x1c /* write fifo buffer */ +#define BT_READ_FIFO 0x1d /* read fifo buffer */ +#define BT_ECHO 0x1e /* Echo command data */ +#define BT_MBX_INIT_EXTENDED 0x81 /* Mbx initialization */ +#define BT_INQUIRE_EXTENDED 0x8D /* Adapter Setup Inquiry */ + +/* The following command appeared at FirmWare 3.31 */ +#define BT_ROUND_ROBIN 0x8f /* Enable/Disable(default) round robin */ +#define BT_DISABLE 0x00 /* Parameter value for Disable */ +#define BT_ENABLE 0x01 /* Parameter value for Enable */ + +struct bt_cmd_buf { + u_char byte[16]; +}; + +/* + * BT_INTR_PORT bits (read) + */ + +#define BT_ANY_INTR 0x80 /* Any interrupt */ +#define BT_SCRD 0x08 /* SCSI reset detected */ +#define BT_HACC 0x04 /* Command complete */ +#define BT_MBOA 0x02 /* MBX out empty */ +#define BT_MBIF 0x01 /* MBX in full */ + +/* + * Mail box defs etc. + * these could be bigger but we need the bt_data to fit on a single page.. + */ + +#define BT_MBX_SIZE 32 /* mail box size (MAX 255 MBxs) */ + /* don't need that many really */ +#define BT_CCB_MAX 32 /* store up to 32CCBs at any one time */ + /* in bt742a H/W ( Not MAX ? ) */ +#define CCB_HASH_SIZE 32 /* when we have a physical addr. for */ + /* a ccb and need to find the ccb in */ + /* space, look it up in the hash table */ +#define CCB_HASH_SHIFT 9 /* only hash on multiples of 512 */ +#define CCB_HASH(x) ((((long int)(x))>>CCB_HASH_SHIFT) % CCB_HASH_SIZE) + +#define bt_nextmbx( wmb, mbx, mbio ) \ + if ( (wmb) == &((mbx)->mbio[BT_MBX_SIZE - 1 ]) ) \ + (wmb) = &((mbx)->mbio[0]); \ + else \ + (wmb)++; + +typedef struct bt_mbx_out { + physaddr ccb_addr; + unsigned char dummy[3]; + unsigned char cmd; +} BT_MBO; + +typedef struct bt_mbx_in { + physaddr ccb_addr; + unsigned char btstat; + unsigned char sdstat; + unsigned char dummy; + unsigned char stat; +} BT_MBI; + +struct bt_mbx { + BT_MBO mbo[BT_MBX_SIZE]; + BT_MBI mbi[BT_MBX_SIZE]; + BT_MBO *tmbo; /* Target Mail Box out */ + BT_MBI *tmbi; /* Target Mail Box in */ +}; + +/* + * mbo.cmd values + */ + +#define BT_MBO_FREE 0x0 /* MBO entry is free */ +#define BT_MBO_START 0x1 /* MBO activate entry */ +#define BT_MBO_ABORT 0x2 /* MBO abort entry */ + +/* + * mbi.stat values + */ + +#define BT_MBI_FREE 0x0 /* MBI entry is free */ +#define BT_MBI_OK 0x1 /* completed without error */ +#define BT_MBI_ABORT 0x2 /* aborted ccb */ +#define BT_MBI_UNKNOWN 0x3 /* Tried to abort invalid CCB */ +#define BT_MBI_ERROR 0x4 /* Completed with error */ + +#if defined(BIG_DMA) +WARNING...THIS WON'T WORK(won't fit on 1 page) +/* #define BT_NSEG 2048*/ /* Number of scatter gather segments - to much vm */ +#define BT_NSEG 128 +#else +#define BT_NSEG 33 +#endif /* BIG_DMA */ + +struct bt_scat_gath { + unsigned long seg_len; + physaddr seg_addr; +}; + +struct bt_ccb { + unsigned char opcode; + unsigned char:3, data_in:1, data_out:1,:3; + unsigned char scsi_cmd_length; + unsigned char req_sense_length; + /*------------------------------------longword boundary */ + unsigned long data_length; + /*------------------------------------longword boundary */ + physaddr data_addr; + /*------------------------------------longword boundary */ + unsigned char dummy[2]; + unsigned char host_stat; + unsigned char target_stat; + /*------------------------------------longword boundary */ + unsigned char target; + unsigned char lun; + unsigned char scsi_cmd[12]; /* 12 bytes (bytes only) */ + unsigned char dummy2[1]; + unsigned char link_id; + /*------------------------------------4 longword boundary */ + physaddr link_addr; + /*------------------------------------longword boundary */ + physaddr sense_ptr; +/*-----end of HW fields-------------------------------longword boundary */ + struct scsi_sense_data scsi_sense; + /*------------------------------------longword boundary */ + struct bt_scat_gath scat_gath[BT_NSEG]; + /*------------------------------------longword boundary */ + struct bt_ccb *next; + /*------------------------------------longword boundary */ + struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ + /*------------------------------------longword boundary */ + struct bt_mbx_out *mbx; /* pointer to mail box */ + /*------------------------------------longword boundary */ + int flags; +#define CCB_FREE 0 +#define CCB_ACTIVE 1 +#define CCB_ABORTED 2 + /*------------------------------------longword boundary */ + struct bt_ccb *nexthash; /* if two hash the same */ + /*------------------------------------longword boundary */ + physaddr hashkey; /*physaddr of this ccb */ + /*------------------------------------longword boundary */ +}; + +/* + * opcode fields + */ + +#define BT_INITIATOR_CCB 0x00 /* SCSI Initiator CCB */ +#define BT_TARGET_CCB 0x01 /* SCSI Target CCB */ +#define BT_INIT_SCAT_GATH_CCB 0x02 /* SCSI Initiator with scattter gather */ +#define BT_RESET_CCB 0x81 /* SCSI Bus reset */ + +/* + * bt_ccb.host_stat values + */ + +#define BT_OK 0x00 /* cmd ok */ +#define BT_LINK_OK 0x0a /* Link cmd ok */ +#define BT_LINK_IT 0x0b /* Link cmd ok + int */ +#define BT_SEL_TIMEOUT 0x11 /* Selection time out */ +#define BT_OVER_UNDER 0x12 /* Data over/under run */ +#define BT_BUS_FREE 0x13 /* Bus dropped at unexpected time */ +#define BT_INV_BUS 0x14 /* Invalid bus phase/sequence */ +#define BT_BAD_MBO 0x15 /* Incorrect MBO cmd */ +#define BT_BAD_CCB 0x16 /* Incorrect ccb opcode */ +#define BT_BAD_LINK 0x17 /* Not same values of LUN for links */ +#define BT_INV_TARGET 0x18 /* Invalid target direction */ +#define BT_CCB_DUP 0x19 /* Duplicate CCB received */ +#define BT_INV_CCB 0x1a /* Invalid CCB or segment list */ +#define BT_ABORTED 42 /* pseudo value from driver */ + +struct bt_boardID { + u_char board_type; + u_char custom_feture; + char firm_revision; + u_char firm_version; +}; + +struct bt_setup { + u_char sync_neg:1; + u_char parity:1; + u_char :6; + u_char speed; + u_char bus_on; + u_char bus_off; + u_char num_mbx; + u_char mbx[3]; /* make a sense with back-word compatibility*/ + struct { + u_char offset:4; + u_char period:3; + u_char valid:1; + } sync[8]; + u_char disc_sts; +}; + +struct bt_config { + u_char chan; + u_char intr; + u_char scsi_dev:3; + u_char :5; +}; + +#define BT_INQUIRE_REV_THIRD 0x84 /* Get Adapter FirmWare version #3 */ +#define BT_INQUIRE_REV_FOURTH 0x85 /* Get Adapter FirmWare version #4 */ + +/* + * Determine 32bit address/Data firmware functionality from the bus type + * Note: bt742a/747[s|d]/757/946/445s will return 'E' + * bt542b/545s/545d will return 'A' + * 94/05/18 amurai@spec.co.jp + */ +#define BT_BUS_TYPE_24bit 'A' /* PC/AT 24 bit address bus type */ +#define BT_BUS_TYPE_32bit 'E' /* EISA/VLB/PCI 32 bit address bus type */ +#define BT_BUS_TYPE_MCA 'M' /* Micro chanel is ? forget it right now */ +struct bt_ext_info { + u_char bus_type; /* Host adapter bus type */ + u_char bios_addr; /* Bios Address-Not used */ + u_short max_seg; /* Max segment List */ + u_char num_mbx; /* Number of mailbox */ + int32 mbx_base; /* mailbox base address */ + struct { + u_char resv1:2; /* ??? */ + u_char maxsync:1; /* ON: 10MB/s , OFF: 5MB/s */ + u_char resv2:2; /* ??? */ + u_char sync:1; /* ON: Sync, OFF: async ONLY!! */ + u_char resv3:2; /* ??? */ + } s; + u_char firmid[3]; /* Firmware ver. & rev. w/o last char */ +}; + +#define BT_GET_BOARD_INFO 0x8b /* Get H/W ID and Revision */ +struct bt_board_info { + u_char id[4]; /* i.e bt742a -> '7','4','2','A' */ + u_char ver[2]; /* i.e Board Revision 'H' -> 'H', 0x00 */ +}; + +#define BT_GET_SYNC_VALUE 0x8c /* Get Synchronous Value */ +struct bt_sync_value { + u_char value[8]; /* Synchrnous value (value * 10 nsec) */ +}; + +#define INT9 0x01 +#define INT10 0x02 +#define INT11 0x04 +#define INT12 0x08 +#define INT14 0x20 +#define INT15 0x40 + +#define EISADMA 0x00 +#define CHAN0 0x01 +#define CHAN5 0x20 +#define CHAN6 0x40 +#define CHAN7 0x80 + +#define KVTOPHYS(x) vtophys(x) +#define PAGESIZ 4096 +#define INVALIDATE_CACHE {asm volatile( ".byte 0x0F ;.byte 0x08" ); } + +u_char bt_scratch_buf[256]; + +struct bt_data { + short bt_base; /* base port for each board */ + struct bt_mbx bt_mbx; /* all our mailboxes */ + struct bt_ccb *bt_ccb_free; /* list of free CCBs */ + struct bt_ccb *ccbhash[CCB_HASH_SIZE]; /* phys to kv hash */ + int bt_int; /* int. read off board */ + int bt_dma; /* DMA channel read of board */ + int bt_scsi_dev; /* adapters scsi id */ + int numccbs; /* how many we have malloc'd */ + struct scsi_link sc_link; /* prototype for devs */ +} *btdata[NBT]; + +/***********debug values *************/ +#define BT_SHOWCCBS 0x01 +#define BT_SHOWINTS 0x02 +#define BT_SHOWCMDS 0x04 +#define BT_SHOWMISC 0x08 +int bt_debug = 0; + +#ifdef KERNEL +int btprobe(); +int btattach(); +int btintr(); +int32 bt_scsi_cmd(); +void bt_timeout(caddr_t, int); +void bt_inquire_setup_information(); +void bt_done(); +void btminphys(); +u_int32 bt_adapter_info(); +struct bt_ccb *bt_get_ccb(); +struct bt_ccb *bt_ccb_phys_kv(); + +static int btunit = 0; + +struct isa_driver btdriver = +{ + btprobe, + btattach, + "bt" +}; + +struct scsi_adapter bt_switch = +{ + bt_scsi_cmd, + btminphys, + 0, + 0, + bt_adapter_info, + "bt", + 0, 0 +}; + +/* the below structure is so we have a default dev struct for out link struct */ +struct scsi_device bt_dev = +{ + NULL, /* Use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "bt", + 0, + 0, 0 +}; + +#endif /*KERNEL */ + +#define BT_RESET_TIMEOUT 1000 +#ifndef KERNEL +main() +{ + printf("bt_data is %d bytes\n", sizeof(struct bt_data)); + printf("bt_ccb is %d bytes\n", sizeof(struct bt_ccb)); + printf("bt_mbx is %d bytes\n", sizeof(struct bt_mbx)); +} + +#else /*KERNEL */ + +/* + * bt_cmd(unit,icnt, ocnt,wait, retval, opcode, args) + * + * Activate Adapter command + * icnt: number of args (outbound bytes written after opcode) + * ocnt: number of expected returned bytes + * wait: number of seconds to wait for response + * retval: buffer where to place returned bytes + * opcode: opcode BT_NOP, BT_MBX_INIT, BT_START_SCSI ... + * args: parameters + * + * Performs an adapter command through the ports. Not to be confused with a + * scsi command, which is read in via the dma; one of the adapter commands + * tells it to read in a scsi command. + */ +int +bt_cmd(unit, icnt, ocnt, wait, retval, opcode, args) + int unit; + int icnt; + int ocnt; + int wait; + u_char *retval; + unsigned opcode; + u_char args; +{ + struct bt_data *bt = btdata[unit]; + unsigned *ic = &opcode; + u_char oc; + register i; + int sts; + + /* + * multiply the wait argument by a big constant + * zero defaults to 1 + */ + if (wait) + wait *= 100000; + else + wait = 100000; + /* + * Wait for the adapter to go idle, unless it's one of + * the commands which don't need this + */ + if (opcode != BT_MBX_INIT && opcode != BT_START_SCSI) { + i = 100000; /* 1 sec? */ + while (--i) { + sts = inb(BT_CTRL_STAT_PORT); + if (sts & BT_IDLE) { + break; + } + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, host not idle(0x%x)\n", unit, sts); + return (ENXIO); + } + } + /* + * Now that it is idle, if we expect output, preflush the + * queue feeding to us. + */ + if (ocnt) { + while ((inb(BT_CTRL_STAT_PORT)) & BT_DF) + inb(BT_CMD_DATA_PORT); + } + /* + * Output the command and the number of arguments given + * for each byte, first check the port is empty. + */ + icnt++; + /* include the command */ + while (icnt--) { + sts = inb(BT_CTRL_STAT_PORT); + for (i = wait; i; i--) { + sts = inb(BT_CTRL_STAT_PORT); + if (!(sts & BT_CDF)) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, cmd/data port full\n", unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return (ENXIO); + } + outb(BT_CMD_DATA_PORT, (u_char) (*ic++)); + } + /* + * If we expect input, loop that many times, each time, + * looking for the data register to have valid data + */ + while (ocnt--) { + sts = inb(BT_CTRL_STAT_PORT); + for (i = wait; i; i--) { + sts = inb(BT_CTRL_STAT_PORT); + if (sts & BT_DF) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, cmd/data port empty %d\n", + unit, ocnt); + return (ENXIO); + } + oc = inb(BT_CMD_DATA_PORT); + if (retval) + *retval++ = oc; + } + /* + * Wait for the board to report a finised instruction + */ + i = 100000; /* 1 sec? */ + while (--i) { + sts = inb(BT_INTR_PORT); + if (sts & BT_HACC) { + break; + } + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_cmd, host not finished(0x%x)\n", unit, sts); + return (ENXIO); + } + outb(BT_CTRL_STAT_PORT, BT_IRST); + return (0); +} + +/* + * Check if the device can be found at the port given + * and if so, set it up ready for further work + * as an argument, takes the isa_device structure from + * autoconf.c + */ +int +btprobe(dev) + struct isa_device *dev; +{ + /* + * find unit and check we have that many defined + */ + int unit = btunit; + struct bt_data *bt; + + if (unit >= NBT) { + printf("bt%d: unit number too high\n", unit); + return 0; + } + /* + * Allocate a storage area for us + */ + if (btdata[unit]) { + printf("bt%d: memory already allocated\n", unit); + return 0; + } + bt = malloc(sizeof(struct bt_data), M_TEMP, M_NOWAIT); + if (!bt) { + printf("bt%d: cannot malloc!\n", unit); + return 0; + } + bzero(bt, sizeof(struct bt_data)); + btdata[unit] = bt; + bt->bt_base = dev->id_iobase; + + /* + * Try initialise a unit at this location + * sets up dma and bus speed, loads bt->bt_int + */ + if (bt_init(unit) != 0) { + btdata[unit] = NULL; + free(bt, M_TEMP); + return 0; + } + /* + * If it's there, put in it's interrupt vectors + */ + dev->id_unit = unit; + dev->id_irq = (1 << bt->bt_int); + dev->id_drq = bt->bt_dma; + + btunit++; + return 1; +} + +/* + * Attach all the sub-devices we can find + */ +int +btattach(dev) + struct isa_device *dev; +{ + int unit = dev->id_unit; + struct bt_data *bt = btdata[unit]; + + /* + * fill in the prototype scsi_link. + */ + bt->sc_link.adapter_unit = unit; + bt->sc_link.adapter_targ = bt->bt_scsi_dev; + bt->sc_link.adapter = &bt_switch; + bt->sc_link.device = &bt_dev; + + /* + * Forcely Bounce buffer mechanizum is ON for some broken busmaster + * chip with over 16Mbytes boundary for while... + * amurai@spec.co.jp 94/06/16 + */ + bt->sc_link.flags = SDEV_BOUNCE; /*XXX*/ + + /* + * ask the adapter what subunits are present + */ + scsi_attachdevs(&(bt->sc_link)); + return 1; +} + +/* + * Return some information to the caller about the adapter and its + * capabilities. + */ +u_int32 +bt_adapter_info(unit) + int unit; +{ + return (2); /* 2 outstanding requests at a time per device */ +} + +/* + * Catch an interrupt from the adaptor + */ +int +btintr(unit) + int unit; +{ + struct bt_data *bt = btdata[unit]; + BT_MBI *wmbi; + struct bt_mbx *wmbx; + struct bt_ccb *ccb; + unsigned char stat; + int i, wait; + int found = 0; + +#ifdef UTEST + printf("btintr "); +#endif + /* + * First acknowlege the interrupt, Then if it's + * not telling about a completed operation + * just return. + */ + stat = inb(BT_INTR_PORT); + + /* Mail Box out empty ? */ + if (stat & BT_MBOA) { + printf("bt%d: Available Free mbo post\n", unit); + /* Disable MBO available interrupt */ + outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN); + wait = 100000; /* 1 sec enough? */ + for (i = wait; i; i--) { + if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_intr, cmd/data port full\n", unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return 1; + } + outb(BT_CMD_DATA_PORT, 0x00); /* Disable */ + wakeup((caddr_t)&bt->bt_mbx); + outb(BT_CTRL_STAT_PORT, BT_IRST); + return 1; + } + if (!(stat & BT_MBIF)) { + outb(BT_CTRL_STAT_PORT, BT_IRST); + return 1; + } + /* + * If it IS then process the competed operation + */ + wmbx = &bt->bt_mbx; + wmbi = wmbx->tmbi; + AGAIN: + while (wmbi->stat != BT_MBI_FREE) { + ccb = bt_ccb_phys_kv(bt, (wmbi->ccb_addr)); + if (!ccb) { + wmbi->stat = BT_MBI_FREE; + printf("bt: BAD CCB ADDR!\n"); + continue; + } + found++; + if ((stat = wmbi->stat) != BT_MBI_OK) { + switch (stat) { + case BT_MBI_ABORT: +#ifdef UTEST + if (bt_debug & BT_SHOWMISC) + printf("abort "); +#endif + ccb->host_stat = BT_ABORTED; + break; + + case BT_MBI_UNKNOWN: + ccb = (struct bt_ccb *) 0; +#ifdef UTEST + if (bt_debug & BT_SHOWMISC) + printf("unknown ccb for abort"); +#endif + break; + + case BT_MBI_ERROR: + break; + + default: + panic("Impossible mbxi status"); + + } +#ifdef UTEST + if ((bt_debug & BT_SHOWCMDS) && ccb) { + u_char *cp; + cp = ccb->scsi_cmd; + printf("op=%x %x %x %x %x %x\n", + cp[0], cp[1], cp[2], + cp[3], cp[4], cp[5]); + printf("stat %x for mbi addr = 0x%08x\n" + ,wmbi->stat, wmbi); + printf("addr = 0x%x\n", ccb); + } +#endif + } + wmbi->stat = BT_MBI_FREE; + if (ccb) { + untimeout(bt_timeout, (caddr_t)ccb); + bt_done(unit, ccb); + } + /* Set the IN mail Box pointer for next */ bt_nextmbx(wmbi, wmbx, mbi); + } + if (!found) { + for (i = 0; i < BT_MBX_SIZE; i++) { + if (wmbi->stat != BT_MBI_FREE) { + found++; + break; + } + bt_nextmbx(wmbi, wmbx, mbi); + } + if (!found) { + printf("bt%d: mbi at 0x%08x should be found, stat=%02x..resync\n", + unit, wmbi, stat); + } else { + found = 0; + goto AGAIN; + } + } + wmbx->tmbi = wmbi; + outb(BT_CTRL_STAT_PORT, BT_IRST); + return 1; +} + +/* + * A ccb is put onto the free list. + */ +void +bt_free_ccb(unit, ccb, flags) + int unit; + struct bt_ccb *ccb; + int flags; +{ + struct bt_data *bt = btdata[unit]; + unsigned int opri = 0; + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + + ccb->next = bt->bt_ccb_free; + bt->bt_ccb_free = ccb; + ccb->flags = CCB_FREE; + /* + * If there were none, wake anybody waiting for one to come free, + * starting with queued entries. + */ + if (!ccb->next) { + wakeup((caddr_t)&bt->bt_ccb_free); + } + + if (!(flags & SCSI_NOMASK)) + splx(opri); +} + +/* + * Get a free ccb + * + * If there are none, see if we can allocate a new one. If so, put it in + * the hash table too otherwise either return an error or sleep. + */ +struct bt_ccb * +bt_get_ccb(unit, flags) + int unit; + int flags; +{ + struct bt_data *bt = btdata[unit]; + unsigned opri = 0; + struct bt_ccb *ccbp; + struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ + BT_MBO *wmbo; /* Out Mail Box pointer */ + int hashnum; + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + /* + * If we can and have to, sleep waiting for one to come free + * but only if we can't allocate a new one. + */ + while (!(ccbp = bt->bt_ccb_free)) { + if (bt->numccbs < BT_CCB_MAX) { + if (ccbp = (struct bt_ccb *) malloc(sizeof(struct bt_ccb), + M_TEMP, + M_NOWAIT)) { + bzero(ccbp, sizeof(struct bt_ccb)); + bt->numccbs++; + ccbp->flags = CCB_ACTIVE; + /* + * put in the phystokv hash table + * Never gets taken out. + */ + ccbp->hashkey = KVTOPHYS(ccbp); + hashnum = CCB_HASH(ccbp->hashkey); + ccbp->nexthash = bt->ccbhash[hashnum]; + bt->ccbhash[hashnum] = ccbp; + } else { + printf("bt%d: Can't malloc CCB\n", unit); + } + goto gottit; + } else { + if (!(flags & SCSI_NOSLEEP)) { + tsleep((caddr_t)&bt->bt_ccb_free, PRIBIO, + "btccb", 0); + } + } + } + if (ccbp) { + /* Get CCB from from free list */ + bt->bt_ccb_free = ccbp->next; + ccbp->flags = CCB_ACTIVE; + } + gottit: + if (!(flags & SCSI_NOMASK)) + splx(opri); + + return (ccbp); +} + +/* + * given a physical address, find the ccb that + * it corresponds to: + */ +struct bt_ccb * +bt_ccb_phys_kv(bt, ccb_phys) + struct bt_data *bt; + physaddr ccb_phys; +{ + int hashnum = CCB_HASH(ccb_phys); + struct bt_ccb *ccbp = bt->ccbhash[hashnum]; + + while (ccbp) { + if (ccbp->hashkey == ccb_phys) + break; + ccbp = ccbp->nexthash; + } + return ccbp; +} + +/* + * Get a MBO and then Send it + */ +BT_MBO * +bt_send_mbo(int unit, int flags, int cmd, struct bt_ccb *ccb) +{ + struct bt_data *bt = btdata[unit]; + unsigned opri = 0; + BT_MBO *wmbo; /* Mail Box Out pointer */ + struct bt_mbx *wmbx; /* Mail Box pointer specified unit */ + int i, wait; + + wmbx = &bt->bt_mbx; + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + + /* Get the Target OUT mail Box pointer and move to Next */ + wmbo = wmbx->tmbo; + wmbx->tmbo = (wmbo == &(wmbx->mbo[BT_MBX_SIZE - 1]) ? + &(wmbx->mbo[0]) : wmbo + 1); + + /* + * Check the outmail box is free or not. + * Note: Under the normal operation, it shuld NOT happen to wait. + */ + while (wmbo->cmd != BT_MBO_FREE) { + wait = 100000; /* 1 sec enough? */ + /* Enable MBO available interrupt */ + outb(BT_CMD_DATA_PORT, BT_MBO_INTR_EN); + for (i = wait; i; i--) { + if (!(inb(BT_CTRL_STAT_PORT) & BT_CDF)) + break; + DELAY(10); + } + if (i == 0) { + printf("bt%d: bt_send_mbo, cmd/data port full\n", unit); + outb(BT_CTRL_STAT_PORT, BT_SRST); + return ((BT_MBO *) 0); + } + outb(BT_CMD_DATA_PORT, 0x01); /* Enable */ + tsleep((caddr_t)wmbx, PRIBIO, "btsend", 0); + /* XXX */ /*can't do this! */ + /* May be servicing an int */ + } + /* Link CCB to the Mail Box */ + wmbo->ccb_addr = KVTOPHYS(ccb); + ccb->mbx = wmbo; + wmbo->cmd = cmd; + + /* Send it! */ + outb(BT_CMD_DATA_PORT, BT_START_SCSI); + + if (!(flags & SCSI_NOMASK)) + splx(opri); + + return (wmbo); +} + +/* + * We have a ccb which has been processed by the + * adaptor, now we look to see how the operation + * went. Wake up the owner if waiting + */ +void +bt_done(unit, ccb) + int unit; + struct bt_ccb *ccb; +{ + struct bt_data *bt = btdata[unit]; + struct scsi_sense_data *s1, *s2; + struct scsi_xfer *xs = ccb->xfer; + + SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_done\n")); + /* + * Otherwise, put the results of the operation + * into the xfer and call whoever started it + */ + if ((ccb->host_stat != BT_OK || ccb->target_stat != SCSI_OK) + && (!(xs->flags & SCSI_ERR_OK))) { + + s1 = &(ccb->scsi_sense); + s2 = &(xs->sense); + + if (ccb->host_stat) { + switch (ccb->host_stat) { + case BT_ABORTED: /* No response */ + case BT_SEL_TIMEOUT: /* No response */ + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("timeout reported back\n")); + xs->error = XS_TIMEOUT; + break; + default: /* Other scsi protocol messes */ + xs->error = XS_DRIVER_STUFFUP; + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("unexpected host_stat: %x\n", + ccb->host_stat)); + } + } else { + switch (ccb->target_stat) { + case 0x02: + *s2 = *s1; + xs->error = XS_SENSE; + break; + case 0x08: + xs->error = XS_BUSY; + break; + default: + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("unexpected target_stat: %x\n", + ccb->target_stat)); + xs->error = XS_DRIVER_STUFFUP; + } + } + } else { /* All went correctly OR errors expected */ + xs->resid = 0; + } + xs->flags |= ITSDONE; + bt_free_ccb(unit, ccb, xs->flags); + scsi_done(xs); +} + +/* + * Start the board, ready for normal operation + */ +int +bt_init(unit) + int unit; +{ + struct bt_data *bt = btdata[unit]; + unsigned char ad[4]; + volatile int i, sts; + struct bt_config conf; + struct bt_ext_info info; + struct bt_board_info binfo; + + /* + * reset board, If it doesn't respond, assume + * that it's not there.. good for the probe + */ + + outb(BT_CTRL_STAT_PORT, BT_HRST | BT_SRST); + + for (i = BT_RESET_TIMEOUT; i; i--) { + sts = inb(BT_CTRL_STAT_PORT); + if (sts == (BT_IDLE | BT_INIT)) + break; + DELAY(1000); + } + if (i == 0) { +#ifdef UTEST + printf("bt_init: No answer from board\n"); +#endif + return (ENXIO); + } + + /* + * Displaying Board ID and Hardware Revision + * 94/05/18 amurai@spec.co.jp + */ + bt_cmd(unit, 1, sizeof(binfo),0,&binfo,BT_GET_BOARD_INFO,sizeof(binfo)); + printf("bt%d: Bt%c%c%c%c/%c%d-", unit, + binfo.id[0], + binfo.id[1], + binfo.id[2], + binfo.id[3], + binfo.ver[0], + (unsigned) binfo.ver[1] + ); + + /* + * Make sure board has a capability of 32bit addressing. + * and Firmware also need a capability of 32bit addressing pointer + * in Extended mailbox and ccb structure. + * 94/05/18 amurai@spec.co.jp + */ + bt_cmd(unit, 1, sizeof(info),0,&info, BT_INQUIRE_EXTENDED,sizeof(info)); + switch (info.bus_type) { + case BT_BUS_TYPE_24bit: /* PC/AT 24 bit address bus */ + printf("ISA(24bit) bus\n"); + break; + case BT_BUS_TYPE_32bit: /* EISA/VLB/PCI 32 bit bus */ + printf("PCI/EISA/VLB(32bit) bus\n"); + break; + case BT_BUS_TYPE_MCA: /* forget it right now */ + printf("MCA bus architecture..."); + printf("giving up\n"); + return (ENXIO); + break; + default: + printf("Unknown state..."); + printf("giving up\n"); + return (ENXIO); + break; + } + if ( binfo.id[0] == '5' ) { + printf("bt%d: This driver is designed for using 32 bit addressing\n",unit); + printf("bt%d: mode firmware and EISA/PCI/VLB bus architecture bus\n",unit); + printf("bt%d: WITHOUT any software trick/overhead (i.e.bounce buffer).\n",unit); + printf("bt%d: If you have more than 16MBytes memory\n",unit); + printf("bt%d: your filesystem will get a serious damage.\n",unit); + } else if ( info.bus_type == BT_BUS_TYPE_24bit ) { + printf("bt%d: Your board should report a 32bit bus architecture type..\n",unit); + printf("bt%d: A firmware on your board may have a problem with over\n",unit); + printf("bt%d: 16MBytes memory handling with this driver.\n",unit); + } + + /* + * Assume we have a board at this stage + * setup dma channel from jumpers and save int + * level + */ + printf("bt%d: reading board settings, ", unit); + + bt_cmd(unit, 0, sizeof(conf), 0, &conf, BT_CONF_GET); + switch (conf.chan) { + case EISADMA: + bt->bt_dma = -1; + break; + case CHAN0: + outb(0x0b, 0x0c); + outb(0x0a, 0x00); + bt->bt_dma = 0; + break; + case CHAN5: + outb(0xd6, 0xc1); + outb(0xd4, 0x01); + bt->bt_dma = 5; + break; + case CHAN6: + outb(0xd6, 0xc2); + outb(0xd4, 0x02); + bt->bt_dma = 6; + break; + case CHAN7: + outb(0xd6, 0xc3); + outb(0xd4, 0x03); + bt->bt_dma = 7; + break; + default: + printf("illegal dma setting %x\n", conf.chan); + return (EIO); + } + if (bt->bt_dma == -1) + printf("busmastering, "); + else + printf("dma=%d, ", bt->bt_dma); + + switch (conf.intr) { + case INT9: + bt->bt_int = 9; + break; + case INT10: + bt->bt_int = 10; + break; + case INT11: + bt->bt_int = 11; + break; + case INT12: + bt->bt_int = 12; + break; + case INT14: + bt->bt_int = 14; + break; + case INT15: + bt->bt_int = 15; + break; + default: + printf("illegal int setting\n"); + return (EIO); + } + printf("int=%d\n", bt->bt_int); + + /* who are we on the scsi bus */ + bt->bt_scsi_dev = conf.scsi_dev; + /* + * Initialize mail box + */ + *((physaddr *) ad) = KVTOPHYS(&bt->bt_mbx); + bt_cmd(unit, 5, 0, 0, 0, BT_MBX_INIT_EXTENDED + ,BT_MBX_SIZE + ,ad[0] + ,ad[1] + ,ad[2] + ,ad[3]); + + /* + * Set Pointer chain null for just in case + * Link the ccb's into a free-list W/O mbox + * Initialize mail box status to free + */ + if (bt->bt_ccb_free != (struct bt_ccb *) 0) { + printf("bt%d: bt_ccb_free is NOT initialized but init here\n", + unit); + bt->bt_ccb_free = (struct bt_ccb *) 0; + } + for (i = 0; i < BT_MBX_SIZE; i++) { + bt->bt_mbx.mbo[i].cmd = BT_MBO_FREE; + bt->bt_mbx.mbi[i].stat = BT_MBI_FREE; + } + /* + * Set up initial mail box for round-robin operation. + */ + bt->bt_mbx.tmbo = &bt->bt_mbx.mbo[0]; + bt->bt_mbx.tmbi = &bt->bt_mbx.mbi[0]; + bt_inquire_setup_information(unit, &info); + + + /* + * Note that we are going and return (to probe) + */ + return 0; +} + +void +bt_inquire_setup_information( + int unit, + struct bt_ext_info *info ) +{ + struct bt_data *bt = btdata[unit]; + struct bt_setup setup; + struct bt_sync_value sync; + char dummy[8]; + char sub_ver[3]; + struct bt_boardID bID; + int i; + + /* Inquire Installed Devices */ + bzero( &dummy[0], sizeof(dummy) ); + bt_cmd(unit, 0, sizeof(dummy), 100, &dummy[0], BT_DEV_GET); + + /* + * If board has a capbility of Syncrhonouse mode, + * Get a SCSI Synchronous value + */ + if ( info->s.sync ) { + bt_cmd(unit, 1, sizeof(sync), 100, + &sync,BT_GET_SYNC_VALUE,sizeof(sync)); + } + + /* + * Inquire Board ID to board for firmware version + */ + bt_cmd(unit, 0, sizeof(bID), 0, &bID, BT_INQUIRE); + bt_cmd(unit, 0, 1, 0, &sub_ver[0], BT_INQUIRE_REV_THIRD ); + i = ((int)(bID.firm_revision-'0')) * 10 + (int)(bID.firm_version-'0'); + if ( i >= 33 ) { + bt_cmd(unit, 0, 1, 0, &sub_ver[1], BT_INQUIRE_REV_FOURTH ); + } else { + /* + * Below rev 3.3 firmware has a problem for issuing + * the BT_INQUIRE_REV_FOURTH command. + */ + sub_ver[1]='\0'; + } + sub_ver[2]='\0'; + if (sub_ver[1]==' ') + sub_ver[1]='\0'; + printf("bt%d: version %c.%c%s, ", + unit, bID.firm_revision, bID.firm_version, sub_ver ); + + /* + * Obtain setup information from board. + */ + bt_cmd(unit, 1, sizeof(setup), 0, &setup, BT_SETUP_GET, sizeof(setup)); + + if (setup.sync_neg && info->s.sync ) { + if ( info->s.maxsync ) { + printf("fast sync, "); /* Max 10MB/s */ + } else { + printf("sync, "); /* Max 5MB/s */ + } + } else { + if ( info->s.sync ) { + printf("async, "); /* Never try by board */ + } else { + printf("async only, "); /* Doesn't has a capability on board */ + } + } + if (setup.parity) { + printf("parity, "); + } else { + printf("no parity, "); + } + printf("%d mbxs, %d ccbs\n", setup.num_mbx, BT_CCB_MAX); + + /* + * Displayi SCSI negotiation value by each target. + * amurai@spec.co.jp + */ + for (i = 0; i < 8; i++) { + if (!setup.sync[i].valid ) + continue; + if ( (!setup.sync[i].offset && !setup.sync[i].period) + || !info->s.sync ) { + printf("bt%d: targ %d async\n", unit, i); + } else { + printf("bt%d: targ %d sync rate=%2d.%02dMB/s(%dns), offset=%02d\n", + unit, i, + 100 / sync.value[i], + (100 % sync.value[i]) * 100 / sync.value[i], + sync.value[i] * 10, + setup.sync[i].offset ); + } + } + + /* + * Enable round-robin scheme - appeared at firmware rev. 3.31 + * Below rev 3.XX firmware has a problem for issuing + * BT_ROUND_ROBIN command amurai@spec.co.jp + */ + if ( bID.firm_revision >= '3' ) { + printf("bt%d: Enabling Round robin scheme\n", unit); + bt_cmd(unit, 1, 0, 0, 0, BT_ROUND_ROBIN, BT_ENABLE); + } else { + printf("bt%d: Not Enabling Round robin scheme\n", unit); + } + +} + +#ifndef min +#define min(x,y) (x < y ? x : y) +#endif /* min */ + +void +btminphys(bp) + struct buf *bp; +{ + if (bp->b_bcount > ((BT_NSEG - 1) * PAGESIZ)) { + bp->b_bcount = ((BT_NSEG - 1) * PAGESIZ); + } +} + +/* + * start a scsi operation given the command and the data address. Also needs + * the unit, target and lu. + */ +int32 +bt_scsi_cmd(xs) + struct scsi_xfer *xs; +{ + struct scsi_sense_data *s1, *s2; + struct bt_ccb *ccb; + struct bt_scat_gath *sg; + int seg; /* scatter gather seg being worked on */ + int i = 0; + int c = 0; + int thiskv; + physaddr thisphys, nextphys; + int unit = xs->sc_link->adapter_unit; + int bytes_this_seg, bytes_this_page, datalen, flags; + struct iovec *iovp; + struct bt_data *bt = btdata[unit]; + BT_MBO *mbo; + + SC_DEBUG(xs->sc_link, SDEV_DB2, ("bt_scsi_cmd\n")); + /* + * get a ccb (mbox-out) to use. If the transfer + * is from a buf (possibly from interrupt time) + * then we can't allow it to sleep + */ + flags = xs->flags; + if (xs->bp) + flags |= (SCSI_NOSLEEP); /* just to be sure */ + if (flags & ITSDONE) { + printf("bt%d: Already done?\n", unit); + xs->flags &= ~ITSDONE; + } + if (!(flags & INUSE)) { + printf("bt%d: Not in use?\n", unit); + xs->flags |= INUSE; + } + if (!(ccb = bt_get_ccb(unit, flags))) { + xs->error = XS_DRIVER_STUFFUP; + return (TRY_AGAIN_LATER); + } + SC_DEBUG(xs->sc_link, SDEV_DB3, + ("start ccb(%x)\n", ccb)); + /* + * Put all the arguments for the xfer in the ccb + */ + ccb->xfer = xs; + if (flags & SCSI_RESET) { + ccb->opcode = BT_RESET_CCB; + } else { + /* can't use S/G if zero length */ + ccb->opcode = (xs->datalen ? + BT_INIT_SCAT_GATH_CCB + : BT_INITIATOR_CCB); + } + ccb->target = xs->sc_link->target; + ccb->data_out = 0; + ccb->data_in = 0; + ccb->lun = xs->sc_link->lun; + ccb->scsi_cmd_length = xs->cmdlen; + ccb->sense_ptr = KVTOPHYS(&(ccb->scsi_sense)); + ccb->req_sense_length = sizeof(ccb->scsi_sense); + + if ((xs->datalen) && (!(flags & SCSI_RESET))) { /* can use S/G only if not zero length */ + ccb->data_addr = KVTOPHYS(ccb->scat_gath); + sg = ccb->scat_gath; + seg = 0; +#ifdef TFS + if (flags & SCSI_DATA_UIO) { + iovp = ((struct uio *) xs->data)->uio_iov; + datalen = ((struct uio *) xs->data)->uio_iovcnt; + xs->datalen = 0; + while ((datalen) && (seg < BT_NSEG)) { + sg->seg_addr = (physaddr) iovp->iov_base; + xs->datalen += sg->seg_len = iovp->iov_len; + SC_DEBUGN(xs->sc_link, SDEV_DB4, ("(0x%x@0x%x)" + ,iovp->iov_len, iovp->iov_base)); + sg++; + iovp++; + seg++; + datalen--; + } + } else +#endif /* TFS */ + { + /* + * Set up the scatter gather block + */ + + SC_DEBUG(xs->sc_link, SDEV_DB4, + ("%d @0x%x:- ", xs->datalen, xs->data)); + datalen = xs->datalen; + thiskv = (int) xs->data; + thisphys = KVTOPHYS(thiskv); + + while ((datalen) && (seg < BT_NSEG)) { + bytes_this_seg = 0; + + /* put in the base address */ + sg->seg_addr = thisphys; + + SC_DEBUGN(xs->sc_link, SDEV_DB4, + ("0x%x", thisphys)); + + /* do it at least once */ + nextphys = thisphys; + while ((datalen) && (thisphys == nextphys)) + /* + * This page is contiguous (physically) with + * the the last, just extend the length + */ + { + /* how far to the end of the page */ + nextphys = (thisphys & (~(PAGESIZ - 1))) + + PAGESIZ; + bytes_this_page = nextphys - thisphys; + /**** or the data ****/ + bytes_this_page = min(bytes_this_page + ,datalen); + bytes_this_seg += bytes_this_page; + datalen -= bytes_this_page; + + /* get more ready for the next page */ + thiskv = (thiskv & (~(PAGESIZ - 1))) + + PAGESIZ; + if (datalen) + thisphys = KVTOPHYS(thiskv); + } + /* + * next page isn't contiguous, finish the seg + */ + SC_DEBUGN(xs->sc_link, SDEV_DB4, + ("(0x%x)", bytes_this_seg)); + sg->seg_len = bytes_this_seg; + sg++; + seg++; + } + } + /* end of iov/kv decision */ + ccb->data_length = seg * sizeof(struct bt_scat_gath); + SC_DEBUGN(xs->sc_link, SDEV_DB4, ("\n")); + if (datalen) { + /* + * there's still data, must have run out of segs! + */ + printf("bt%d: bt_scsi_cmd, more than %d DMA segs\n", + unit, BT_NSEG); + xs->error = XS_DRIVER_STUFFUP; + bt_free_ccb(unit, ccb, flags); + return (HAD_ERROR); + } + } else { /* No data xfer, use non S/G values */ + ccb->data_addr = (physaddr) 0; + ccb->data_length = 0; + } + ccb->link_id = 0; + ccb->link_addr = (physaddr) 0; + /* + * Put the scsi command in the ccb and start it + */ + if (!(flags & SCSI_RESET)) { + bcopy(xs->cmd, ccb->scsi_cmd, ccb->scsi_cmd_length); + } + if (bt_send_mbo(unit, flags, BT_MBO_START, ccb) == (BT_MBO *) 0) { + xs->error = XS_DRIVER_STUFFUP; + bt_free_ccb(unit, ccb, flags); + return (TRY_AGAIN_LATER); + } + /* + * Usually return SUCCESSFULLY QUEUED + */ + SC_DEBUG(xs->sc_link, SDEV_DB3, ("cmd_sent\n")); + if (!(flags & SCSI_NOMASK)) { + timeout(bt_timeout, (caddr_t)ccb, (xs->timeout * hz) / 1000); + return (SUCCESSFULLY_QUEUED); + } + /* + * If we can't use interrupts, poll on completion + */ + return (bt_poll(unit, xs, ccb)); +} + +/* + * Poll a particular unit, looking for a particular xs + */ +int +bt_poll(unit, xs, ccb) + int unit; + struct scsi_xfer *xs; + struct bt_ccb *ccb; +{ + struct bt_data *bt = btdata[unit]; + int done = 0; + int count = xs->timeout; + u_char stat; + + /* timeouts are in msec, so we loop in 1000 usec cycles */ + while (count) { + /* + * If we had interrupts enabled, would we + * have got an interrupt? + */ + stat = inb(BT_INTR_PORT); + if (stat & BT_ANY_INTR) { + btintr(unit); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(1000); /* only happens in boot so ok */ + count--; + } + if (count == 0) { + /* + * We timed out, so call the timeout handler manually, + * accounting for the fact that the clock is not running yet + * by taking out the clock queue entry it makes. + */ + bt_timeout((caddr_t)ccb, 0); + + /* + * because we are polling, take out the timeout entry + * bt_timeout made + */ + untimeout(bt_timeout, (caddr_t)ccb); + count = 2000; + while (count) { + /* + * Once again, wait for the int bit + */ + stat = inb(BT_INTR_PORT); + if (stat & BT_ANY_INTR) { + btintr(unit); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(1000); /* only happens in boot so ok */ + count--; + } + if (count == 0) { + /* + * We timed out again... This is bad. Notice that + * this time there is no clock queue entry to remove. + */ + bt_timeout((caddr_t)ccb, 0); + } + } + if (xs->error) + return (HAD_ERROR); + return (COMPLETE); +} + +void +bt_timeout(caddr_t arg1, int arg2) +{ + struct bt_ccb * ccb = (struct bt_ccb *)arg1; + int unit; + struct bt_data *bt; + int s = splbio(); + + /* + * A timeout routine in kernel DONOT unlink + * Entry chains when time outed....So infinity Loop.. + * 94/04/20 amurai@spec.co.jp + */ + untimeout(bt_timeout, (caddr_t)ccb); + + unit = ccb->xfer->sc_link->adapter_unit; + bt = btdata[unit]; + +#ifdef UTEST + bt_print_active_ccbs(unit); +#endif + + /* + * If the ccb's mbx is not free, then the board has gone Far East? + */ + if (bt_ccb_phys_kv(bt, ccb->mbx->ccb_addr) == ccb && + ccb->mbx->cmd != BT_MBO_FREE) { + printf("bt%d: not taking commands!\n", unit); + Debugger("bt742a"); + } + /* + * If it has been through before, then + * a previous abort has failed, don't + * try abort again + */ + if (ccb->flags == CCB_ABORTED) { + /* + * abort timed out + */ + printf("bt%d: Abort Operation has timed out\n", unit); + ccb->xfer->retries = 0; /* I MEAN IT ! */ + ccb->host_stat = BT_ABORTED; + bt_done(unit, ccb); + } else { + /* abort the operation that has timed out */ + printf("bt%d: Try to abort\n", unit); + bt_send_mbo(unit, ~SCSI_NOMASK, + BT_MBO_ABORT, ccb); + /* 2 secs for the abort */ + ccb->flags = CCB_ABORTED; + timeout(bt_timeout, (caddr_t)ccb, 2 * hz); + } + splx(s); +} + +#ifdef UTEST +void +bt_print_ccb(ccb) + struct bt_ccb *ccb; +{ + printf("ccb:%x op:%x cmdlen:%d senlen:%d\n" + ,ccb + ,ccb->opcode + ,ccb->scsi_cmd_length + ,ccb->req_sense_length); + printf(" datlen:%d hstat:%x tstat:%x flags:%x\n" + ,ccb->data_length + ,ccb->host_stat + ,ccb->target_stat + ,ccb->flags); +} + +void +bt_print_active_ccbs(int unit) +{ + struct bt_data *bt = btdata[unit]; + struct bt_ccb *ccb; + int i = 0; + + while (i < CCB_HASH_SIZE) { + ccb = bt->ccbhash[i]; + while (ccb) { + if (ccb->flags != CCB_FREE) + bt_print_ccb(ccb); + ccb = ccb->nexthash; + } + i++; + } +} +#endif /*UTEST */ +#endif /*KERNEL */ diff --git a/sys/i386/isa/clock.c b/sys/i386/isa/clock.c index cd87b28b90e6..5832801d24ae 100644 --- a/sys/i386/isa/clock.c +++ b/sys/i386/isa/clock.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)clock.c 7.2 (Berkeley) 5/12/91 - * $Id: clock.c,v 1.6 1994/02/06 22:48:13 davidg Exp $ + * $Id: clock.c,v 1.9 1994/05/02 09:41:24 sos Exp $ */ /* @@ -45,6 +45,7 @@ #include "time.h" #include "kernel.h" #include "machine/segments.h" +#include "machine/frame.h" #include "i386/isa/icu.h" #include "i386/isa/isa.h" #include "i386/isa/rtc.h" @@ -55,22 +56,225 @@ #ifndef TIMER_FREQ #define TIMER_FREQ 1193182 /* XXX - should be in isa.h */ #endif +#define TIMER_DIV(x) ((TIMER_FREQ+(x)/2)/(x)) + +void hardclock(); +static int beeping; +int timer0_divisor = TIMER_DIV(100); /* XXX should be hz */ +u_int timer0_prescale; +static char timer0_state = 0, timer2_state = 0; +static char timer0_reprogram = 0; +static void (*timer_func)() = hardclock; +static void (*new_function)(); +static u_int new_rate; +static u_int hardclock_divisor; + + +void +timerintr(struct intrframe frame) +{ + timer_func(frame); + switch (timer0_state) { + case 0: + break; + case 1: + if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) { + hardclock(frame); + timer0_prescale = 0; + } + break; + case 2: + disable_intr(); + outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); + outb(TIMER_CNTR0, TIMER_DIV(new_rate)%256); + outb(TIMER_CNTR0, TIMER_DIV(new_rate)/256); + enable_intr(); + timer0_divisor = TIMER_DIV(new_rate); + timer0_prescale = 0; + timer_func = new_function; + timer0_state = 1; + break; + case 3: + if ((timer0_prescale+=timer0_divisor) >= hardclock_divisor) { + hardclock(frame); + disable_intr(); + outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); + outb(TIMER_CNTR0, TIMER_DIV(hz)%256); + outb(TIMER_CNTR0, TIMER_DIV(hz)/256); + enable_intr(); + timer0_divisor = TIMER_DIV(hz); + timer0_prescale = 0; + timer_func = hardclock;; + timer0_state = 0; + } + break; + } +} + + +int +acquire_timer0(int rate, void (*function)() ) +{ + if (timer0_state || !function) + return -1; + + new_function = function; + new_rate = rate; + timer0_state = 2; + return 0; +} + + +int +acquire_timer2(int mode) +{ + if (timer2_state) + return -1; + timer2_state = 1; + outb(TIMER_MODE, TIMER_SEL2 | (mode &0x3f)); + return 0; +} + + +int +release_timer0() +{ + if (!timer0_state) + return -1; + timer0_state = 3; + return 0; +} + + +int +release_timer2() +{ + if (!timer2_state) + return -1; + timer2_state = 0; + outb(TIMER_MODE, TIMER_SEL2|TIMER_SQWAVE|TIMER_16BIT); + return 0; +} + + +static int +getit() +{ + int high, low; + + disable_intr(); + /* select timer0 and latch counter value */ + outb(TIMER_MODE, TIMER_SEL0); + low = inb(TIMER_CNTR0); + high = inb(TIMER_CNTR0); + enable_intr(); + return ((high << 8) | low); +} + + +/* + * Wait "n" microseconds. + * Relies on timer 1 counting down from (TIMER_FREQ / hz) + * Note: timer had better have been programmed before this is first used! + */ +void +DELAY(int n) +{ + int counter_limit, prev_tick, tick, ticks_left, sec, usec; + +#ifdef DELAYDEBUG + int getit_calls = 1; + int n1; + static int state = 0; + + if (state == 0) { + state = 1; + for (n1 = 1; n1 <= 10000000; n1 *= 10) + DELAY(n1); + state = 2; + } + if (state == 1) + printf("DELAY(%d)...", n); +#endif + /* + * Read the counter first, so that the rest of the setup overhead is + * counted. Guess the initial overhead is 20 usec (on most systems it + * takes about 1.5 usec for each of the i/o's in getit(). The loop + * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The + * multiplications and divisions to scale the count take a while). + */ + prev_tick = getit(0, 0); + n -= 20; + /* + * Calculate (n * (TIMER_FREQ / 1e6)) without using floating point + * and without any avoidable overflows. + */ + sec = n / 1000000; + usec = n - sec * 1000000; + ticks_left = sec * TIMER_FREQ + + usec * (TIMER_FREQ / 1000000) + + usec * ((TIMER_FREQ % 1000000) / 1000) / 1000 + + usec * (TIMER_FREQ % 1000) / 1000000; + + while (ticks_left > 0) { + tick = getit(0, 0); +#ifdef DELAYDEBUG + ++getit_calls; +#endif + if (tick > prev_tick) + ticks_left -= prev_tick - (tick - timer0_divisor); + else + ticks_left -= prev_tick - tick; + prev_tick = tick; + } +#ifdef DELAYDEBUG + if (state == 1) + printf(" %d calls to getit() at %d usec each\n", + getit_calls, (n + 5) / getit_calls); +#endif +} + + +static void +sysbeepstop() +{ + outb(IO_PPI, inb(IO_PPI)&0xFC); /* disable counter2 output to speaker */ + release_timer2(); + beeping = 0; +} + + +int +sysbeep(int pitch, int period) +{ + + if (acquire_timer2(TIMER_SQWAVE|TIMER_16BIT)) + return -1; + disable_intr(); + outb(TIMER_CNTR2, pitch); + outb(TIMER_CNTR2, (pitch>>8)); + enable_intr(); + if (!beeping) { + outb(IO_PPI, inb(IO_PPI) | 3); /* enable counter2 output to speaker */ + beeping = period; + timeout(sysbeepstop, 0, period); + } + return 0; +} -static void findcpuspeed(void); void startrtclock() { int s; - findcpuspeed(); /* use the clock (while it's free) - to find the cpu speed */ /* initialize 8253 clock */ outb(TIMER_MODE, TIMER_SEL0|TIMER_RATEGEN|TIMER_16BIT); /* Correct rounding will buy us a better precision in timekeeping */ - outb (IO_TIMER1, (TIMER_FREQ+hz/2)/hz); - outb (IO_TIMER1, ((TIMER_FREQ+hz/2)/hz)/256); + outb (IO_TIMER1, TIMER_DIV(hz)%256); + outb (IO_TIMER1, TIMER_DIV(hz)/256); + timer0_divisor = hardclock_divisor = TIMER_DIV(hz); /* initialize brain-dead battery powered clock */ outb (IO_RTC, RTC_STATUSA); @@ -83,44 +287,18 @@ startrtclock() printf("RTC BIOS diagnostic error %b\n", s, RTCDG_BITS); } -unsigned int delaycount; /* calibrated loop variable (1 millisecond) */ - -#define FIRST_GUESS 0x2000 -static void -findcpuspeed() -{ - unsigned char low; - unsigned int remainder; - - /* Put counter in count down mode */ - outb(IO_TIMER1+3, 0x34); - outb(IO_TIMER1, 0xff); - outb(IO_TIMER1, 0xff); - delaycount = FIRST_GUESS; - spinwait(1); - /* Read the value left in the counter */ - low = inb(IO_TIMER1); /* least siginifcant */ - remainder = inb(IO_TIMER1); /* most significant */ - remainder = (remainder<<8) + low ; - /* Formula for delaycount is : - * (loopcount * timer clock speed)/ (counter ticks * 1000) - */ - delaycount = (FIRST_GUESS * (TIMER_FREQ/1000)) / (0xffff-remainder); -} - /* convert 2 digit BCD number */ int -bcd(i) - int i; +bcd(int i) { return ((i/16)*10 + (i%16)); } + /* convert years to seconds (from 1970) */ unsigned long -ytos(y) -int y; +ytos(int y) { int i; unsigned long ret; @@ -133,16 +311,16 @@ int y; return ret; } + /* convert months to seconds */ unsigned long -mtos(m,leap) -int m,leap; +mtos(int m, int leap) { int i; unsigned long ret; ret = 0; - for(i=1;i<m;i++) { + for(i=1; i<m; i++) { switch(i){ case 1: case 3: case 5: case 7: case 8: case 10: case 12: ret += 31*24*60*60; break; @@ -162,11 +340,10 @@ int m,leap; * from a filesystem. */ void -inittodr(base) - time_t base; +inittodr(time_t base) { unsigned long sec; - int leap,day_week,t,yd; + int leap, day_week, t, yd; int sa,s; /* do we have a realtime clock present? (otherwise we loop below) */ @@ -180,26 +357,25 @@ inittodr(base) sec = bcd(rtcin(RTC_YEAR)) + 1900; if (sec < 1970) sec += 100; - leap = !(sec % 4); sec = ytos(sec); /* year */ - yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec += yd; /* month */ - t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec += t; yd += t; /* date */ + + leap = !(sec % 4); sec = ytos(sec); /* year */ + yd = mtos(bcd(rtcin(RTC_MONTH)),leap); sec+=yd; /* month */ + t = (bcd(rtcin(RTC_DAY))-1) * 24*60*60; sec+=t; yd+=t; /* date */ day_week = rtcin(RTC_WDAY); /* day */ sec += bcd(rtcin(RTC_HRS)) * 60*60; /* hour */ sec += bcd(rtcin(RTC_MIN)) * 60; /* minutes */ sec += bcd(rtcin(RTC_SEC)); /* seconds */ - sec += tz.tz_minuteswest * 60; - time.tv_sec = sec; } + #ifdef garbage /* * Initialze the time of day register, based on the time base which is, e.g. * from a filesystem. */ -test_inittodr(base) - time_t base; +test_inittodr(time_t base) { outb(IO_RTC,9); /* year */ @@ -219,6 +395,7 @@ test_inittodr(base) } #endif + /* * Restart the clock. */ @@ -227,12 +404,14 @@ resettodr() { } + /* * Wire clock interrupt in. */ #define V(s) __CONCAT(V, s) extern void V(clk)(); + void enablertclock() { @@ -240,12 +419,12 @@ enablertclock() INTREN(IRQ0); } + /* * Delay for some number of milliseconds. */ void -spinwait(millisecs) - int millisecs; +spinwait(int millisecs) { DELAY(1000 * millisecs); } diff --git a/sys/i386/isa/com.c b/sys/i386/isa/com.c index dcaf878685be..25b4dee3238c 100644 --- a/sys/i386/isa/com.c +++ b/sys/i386/isa/com.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: com.c,v 1.7 1993/12/19 00:50:32 wollman Exp $ + * $Id: com.c,v 1.10 1994/05/30 03:14:13 ache Exp $ */ #include "com.h" @@ -86,7 +86,7 @@ int comconsinit; int comdefaultrate = TTYDEF_SPEED; int commajor; short com_addr[NCOM]; -struct tty com_tty[NCOM]; +struct tty *com_tty[NCOM]; struct speedtab comspeedtab[] = { 0, 0, @@ -199,12 +199,11 @@ comopen(int /*dev_t*/ dev, int flag, int mode, struct proc *p) unit = UNIT(dev); if (unit >= NCOM || (com_active & (1 << unit)) == 0) return (ENXIO); - tp = &com_tty[unit]; + tp = com_tty[unit] = ttymalloc(com_tty[unit]); tp->t_oproc = comstart; tp->t_param = comparam; tp->t_dev = dev; if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_WOPEN; ttychars(tp); if (tp->t_ispeed == 0) { tp->t_iflag = TTYDEF_IFLAG; @@ -223,9 +222,8 @@ comopen(int /*dev_t*/ dev, int flag, int mode, struct proc *p) tp->t_state |= TS_CARR_ON; while ((flag&O_NONBLOCK) == 0 && (tp->t_cflag&CLOCAL) == 0 && (tp->t_state & TS_CARR_ON) == 0) { - tp->t_state |= TS_WOPEN; - if (error = ttysleep(tp, (caddr_t)&tp->t_raw, TTIPRI | PCATCH, - ttopen, 0)) + if (error = tsleep(tp, TSA_CARR_ON(tp), TTIPRI | PCATCH, + "comdcd", 0)) break; } (void) spl0(); @@ -247,7 +245,7 @@ comclose(dev, flag, mode, p) unit = UNIT(dev); com = com_addr[unit]; - tp = &com_tty[unit]; + tp = com_tty[unit]; (*linesw[tp->t_line].l_close)(tp, flag); outb(com+com_cfcr, inb(com+com_cfcr) & ~CFCR_SBREAK); #ifdef KGDB @@ -255,10 +253,16 @@ comclose(dev, flag, mode, p) if (kgdb_dev != makedev(commajor, unit)) #endif outb(com+com_ier, 0); - if (tp->t_cflag&HUPCL || tp->t_state&TS_WOPEN || - (tp->t_state&TS_ISOPEN) == 0) + if (tp->t_cflag&HUPCL || (tp->t_state&TS_ISOPEN) == 0) (void) commctl(dev, 0, DMSET); ttyclose(tp); +#ifdef broken /* session holds a ref to the tty; can't deallocate */ + ttyfree(tp); + com_tty[unit] = (struct tty *)NULL; +#endif + return (0); + + return(0); } @@ -268,7 +272,7 @@ comread(dev, uio, flag) struct uio *uio; int flag; { - register struct tty *tp = &com_tty[UNIT(dev)]; + register struct tty *tp = com_tty[UNIT(dev)]; return ((*linesw[tp->t_line].l_read)(tp, uio, flag)); } @@ -280,7 +284,7 @@ comwrite(dev, uio, flag) int flag; { int unit = UNIT(dev); - register struct tty *tp = &com_tty[unit]; + register struct tty *tp = com_tty[unit]; /* * (XXX) We disallow virtual consoles if the physical console is @@ -309,7 +313,7 @@ comintr(unit) return; case IIR_RXTOUT: case IIR_RXRDY: - tp = &com_tty[unit]; + tp = com_tty[unit]; /* * Process received bytes. Inline for speed... */ @@ -340,7 +344,7 @@ comintr(unit) } break; case IIR_TXRDY: - tp = &com_tty[unit]; + tp = com_tty[unit]; tp->t_state &=~ (TS_BUSY|TS_FLUSH); if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); @@ -371,7 +375,7 @@ comeint(unit, stat, com) register struct tty *tp; register int c; - tp = &com_tty[unit]; + tp = com_tty[unit]; c = inb(com+com_data); if ((tp->t_state & TS_ISOPEN) == 0) { #ifdef KGDB @@ -401,7 +405,7 @@ commint(unit, com) register struct tty *tp; register int stat; - tp = &com_tty[unit]; + tp = com_tty[unit]; stat = inb(com+com_msr); if ((stat & MSR_DDCD) && (comsoftCAR & (1 << unit)) == 0) { if (stat & MSR_DCD) @@ -432,7 +436,7 @@ comioctl(dev, cmd, data, flag) register com; register int error; - tp = &com_tty[unit]; + tp = com_tty[unit]; error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag); if (error >= 0) return (error); @@ -545,26 +549,17 @@ comstart(tp) s = spltty(); if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP)) goto out; - if (RB_LEN(&tp->t_out) <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_out); - } - if (tp->t_wsel) { - selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); - tp->t_wsel = 0; - tp->t_state &= ~TS_WCOLL; - } - } - if (RB_LEN(&tp->t_out) == 0) + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); + if (RB_LEN(tp->t_out) == 0) goto out; if (inb(com+com_lsr) & LSR_TXRDY) { - c = getc(&tp->t_out); + c = getc(tp->t_out); tp->t_state |= TS_BUSY; outb(com+com_data, c); if (com_hasfifo & (1 << unit)) - for (c = 1; c < 16 && RB_LEN(&tp->t_out); ++c) - outb(com+com_data, getc(&tp->t_out)); + for (c = 1; c < 16 && RB_LEN(tp->t_out); ++c) + outb(com+com_data, getc(tp->t_out)); } out: splx(s); @@ -647,7 +642,7 @@ comcnprobe(cp) /* initialize required fields */ cp->cn_dev = makedev(commajor, unit); - cp->cn_tp = &com_tty[unit]; + cp->cn_tp = com_tty[unit]; #ifdef COMCONSOLE cp->cn_pri = CN_REMOTE; /* Force a serial port console */ #else @@ -747,43 +742,3 @@ comcnputc(dev, c) splx(s); } #endif - -int -comselect(dev, rw, p) - dev_t dev; - int rw; - struct proc *p; -{ - register struct tty *tp = &com_tty[UNIT(dev)]; - int nread; - int s = spltty(); - struct proc *selp; - - switch (rw) { - - case FREAD: - nread = ttnread(tp); - if (nread > 0 || - ((tp->t_cflag&CLOCAL) == 0 && (tp->t_state&TS_CARR_ON) == 0)) - goto win; - if (tp->t_rsel && (selp = pfind(tp->t_rsel)) && selp->p_wchan == (caddr_t)&selwait) - tp->t_state |= TS_RCOLL; - else - tp->t_rsel = p->p_pid; - break; - - case FWRITE: - if (RB_LEN(&tp->t_out) <= tp->t_lowat) - goto win; - if (tp->t_wsel && (selp = pfind(tp->t_wsel)) && selp->p_wchan == (caddr_t)&selwait) - tp->t_state |= TS_WCOLL; - else - tp->t_wsel = p->p_pid; - break; - } - splx(s); - return (0); - win: - splx(s); - return (1); -} diff --git a/sys/i386/isa/debug.h b/sys/i386/isa/debug.h deleted file mode 100644 index 92ecbd67f0fd..000000000000 --- a/sys/i386/isa/debug.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * from: debug.h, part of Bruce Evans interrupt code - * $Id: debug.h,v 1.3 1993/11/13 02:25:20 davidg Exp $ - */ - -#define SHOW_A_LOT_NOT - -#define BDBTRAP(name) \ - ss ; \ - cmpb $0,_bdb_exists ; \ - je 1f ; \ - testb $SEL_RPL_MASK,4(%esp) ; \ - jne 1f ; \ - ss ; \ -bdb_/**/name/**/_ljmp: ; \ - ljmp $0,$0 ; \ -1: - -#if 1 -#define COUNT_EVENT(group, event) incl (group) + (event) * 4 -#else -#define COUNT_EVENT(group, event) -#endif - -#ifdef SHOW_A_LOT - -#define GREEN 0x27 /* 0x27 for true green, 0x07 for mono */ -#define CLI_STI_X 63 -#define CPL_X 46 -#define IMEN_X 64 -#define IPENDING_X 29 -#define RED 0x47 /* 0x47 for true red, 0x70 for mono */ - -#define SHOW_BIT(bit) ; \ - movl %ecx,%eax ; \ - shr $bit,%eax ; \ - andl $1,%eax ; \ - movb bit_colors(%eax),%al ; \ - movb %al,bit * 2 + 1(%ebx) - -#define SHOW_BITS(var, screen_offset) ; \ - pushl %ebx ; \ - pushl %ecx ; \ - movl _Crtat,%ebx ; \ - addl $screen_offset * 2,%ebx ; \ - movl _/**/var,%ecx ; \ - call show_bits ; \ - popl %ecx ; \ - popl %ebx - -#define SHOW_CLI \ - COUNT_EVENT(_intrcnt_show, 0) ; \ - pushl %eax ; \ - movl _Crtat,%eax ; \ - movb $RED,CLI_STI_X * 2 + 1(%eax) ; \ - popl %eax - -#define SHOW_CPL \ - COUNT_EVENT(_intrcnt_show, 1) ; \ - SHOW_BITS(cpl, CPL_X) ; \ - -#define SHOW_IMEN \ - COUNT_EVENT(_intrcnt_show, 2) ; \ - SHOW_BITS(imen, IMEN_X) - -#define SHOW_IPENDING \ - COUNT_EVENT(_intrcnt_show, 3) ; \ - SHOW_BITS(ipending, IPENDING_X) - -#define SHOW_STI \ - COUNT_EVENT(_intrcnt_show, 4) ; \ - pushl %eax ; \ - movl _Crtat,%eax ; \ - movb $GREEN,CLI_STI_X * 2 + 1(%eax) ; \ - popl %eax - -#else /* not SHOW_A_LOT */ - -#define SHOW_CLI COUNT_EVENT(_intrcnt_show, 0) -#define SHOW_CPL COUNT_EVENT(_intrcnt_show, 1) -#define SHOW_IMEN COUNT_EVENT(_intrcnt_show, 2) -#define SHOW_IPENDING COUNT_EVENT(_intrcnt_show, 3) -#define SHOW_STI COUNT_EVENT(_intrcnt_show, 4) - -#endif /* SHOW_A_LOT */ diff --git a/sys/i386/isa/elink.c b/sys/i386/isa/elink.c new file mode 100644 index 000000000000..cd0057644a89 --- /dev/null +++ b/sys/i386/isa/elink.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 1994 Charles Hannum. 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 Charles Hannum. + * 4. 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: elink.c,v 1.1 1994/05/25 20:06:40 ats Exp $ + */ + +/* + * Common code for dealing with 3COM ethernet cards. + */ + +#include <sys/types.h> +#include <sys/cdefs.h> +#include <machine/pio.h> +#include <i386/isa/elink.h> + +/* + * Issue a `global reset' to all cards. We have to be careful to do this only + * once during autoconfig, to prevent resetting boards that have already been + * configured. + */ +void +elink_reset() +{ + static int x = 0; + + if (x == 0) { + x = 1; + outb(ELINK_ID_PORT, ELINK_RESET); + } +} + +/* + * The `ID sequence' is really just snapshots of an 8-bit CRC register as 0 + * bits are shifted in. Different board types use different polynomials. + */ +void +elink_idseq(p) + register u_char p; +{ + register int i; + register u_char c; + + c = 0xff; + for (i = 255; i; i--) { + outb(ELINK_ID_PORT, c); + if (c & 0x80) { + c <<= 1; + c ^= p; + } else + c <<= 1; + } +} diff --git a/sys/i386/isa/elink.h b/sys/i386/isa/elink.h new file mode 100644 index 000000000000..93a5dac6f5ce --- /dev/null +++ b/sys/i386/isa/elink.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 1994 Charles Hannum. 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 Charles Hannum. + * 4. 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: elink.h,v 1.1 1994/05/25 20:06:43 ats Exp $ + */ + +#define ELINK_ID_PORT 0x100 +#define ELINK_RESET 0xc0 + +#define ELINK_507_POLY 0xe7 +#define ELINK_509_POLY 0xcf + +void elink_reset __P((void)); +void elink_idseq __P((u_char p)); diff --git a/sys/i386/isa/fd.c b/sys/i386/isa/fd.c index 8f3e1dc9cc94..f4632b9f2cb1 100644 --- a/sys/i386/isa/fd.c +++ b/sys/i386/isa/fd.c @@ -6,6 +6,12 @@ * This code is derived from software contributed to Berkeley by * Don Ahn. * + * Portions Copyright (c) 1993, 1994 by + * jc@irbs.UUCP (John Capo) + * vak@zebub.msk.su (Serge Vakulenko) + * ache@astral.msk.su (Andrew A. Chernov) + * joerg_wunsch@uriah.sax.de (Joerg Wunsch) + * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: @@ -35,7 +41,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fd.c,v 1.21.2.1 1994/03/07 02:08:43 rgrimes Exp $ + * $Id: fd.c,v 1.26 1994/05/22 12:30:32 joerg Exp $ * */ @@ -59,28 +65,29 @@ #include <sys/buf.h> #include <sys/uio.h> #include <sys/malloc.h> +#include <sys/proc.h> #include <sys/syslog.h> #include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/fdreg.h" #include "i386/isa/fdc.h" -#include "i386/isa/icu.h" #include "i386/isa/rtc.h" -#if NFT > 0 -extern int ftopen(), ftintr(), ftattach(), ftclose(), ftioctl(); -#endif - #define b_cylin b_resid -#define FDBLK 512 /* misuse a flag to identify format operation */ #define B_FORMAT B_XXX +/* + * this biotab field doubles as a field for the physical unit number + * on the controller + */ +#define id_physid id_scsiid + #define NUMTYPES 14 #define NUMDENS (NUMTYPES - 6) -/* This defines (-1) must match index for fd_types */ +/* These defines (-1) must match index for fd_types */ #define F_TAPE_TYPE 0x020 /* bit for fd_types to indicate tape */ #define NO_TYPE 0 /* must match NO_TYPE in ft.c */ #define FD_1720 1 @@ -132,16 +139,17 @@ struct fdc_data fdc_data[NFDC]; struct fd_data { struct fdc_data *fdc; /* pointer to controller structure */ int fdsu; /* this units number on this controller */ - int type; /* Drive type (HD, DD */ + int type; /* Drive type (FD_1440...) */ struct fd_type *ft; /* pointer to the type descriptor */ int flags; #define FD_OPEN 0x01 /* it's open */ #define FD_ACTIVE 0x02 /* it's active */ #define FD_MOTOR 0x04 /* motor should be on */ #define FD_MOTOR_WAIT 0x08 /* motor coming up */ - int skip; - int hddrv; - int track; /* where we think the head is */ + int skip; + int hddrv; + int track; /* where we think the head is */ + int options; /* user configurable options, see ioctl_fd.h */ } fd_data[NFD]; /***********************************************************************\ @@ -153,10 +161,48 @@ struct fd_data { * fdsu is the floppy drive unit number on that controller. (sub-unit) * \***********************************************************************/ -#define id_physid id_scsiid /* this biotab field doubles as a field */ - /* for the physical unit number on the controller */ +#if NFT > 0 +int ftopen(dev_t, int); +int ftintr(/* ftu_t */ int ftu); +int ftclose(/* dev_t */ int, int); +void ftstrategy(struct buf *); +int ftioctl(/* dev_t */ int, int, caddr_t, int, struct proc *); +int ftdump(/* dev_t */ int); +int ftsize(/* dev_t */ int); +int ftattach(struct isa_device *, struct isa_device *); +#endif +/* autoconfig functions */ +static int fdprobe(struct isa_device *); +static int fdattach(struct isa_device *); + +/* exported functions */ +int fdsize (/* dev_t */ int); +void fdintr(fdcu_t); +int Fdopen(/* dev_t */int, int); +int fdclose(/* dev_t */int, int); +void fdstrategy(struct buf *); +int fdioctl(/* dev_t */ int, int, caddr_t, int, struct proc *); + +/* needed for ft driver, thus exported */ +int in_fdc(fdcu_t); +int out_fdc(fdcu_t, int); + +/* internal functions */ +static void set_motor(fdcu_t, int, int); +# define TURNON 1 +# define TURNOFF 0 +static void fd_turnoff(caddr_t arg1, int arg2); +static void fd_motor_on(caddr_t arg1, int arg2); +static void fd_turnon(fdu_t); +static void fdc_reset(fdc_p); +static void fdstart(fdcu_t); +static void fd_timeout(caddr_t, int); +static void fd_pseudointr(caddr_t, int); +static int fdstate(fdcu_t, fdc_p); static int retrier(fdcu_t); +static int fdformat(/* dev_t */ int, struct fd_formb *, struct proc *); + #define DEVIDLE 0 #define FINDWORK 1 @@ -188,25 +234,18 @@ char *fdstates[] = "IOTIMEDOUT" }; - -int fd_debug = 1; +/* CAUTION: fd_debug causes huge amounts of logging output */ +int fd_debug = 0; #define TRACE0(arg) if(fd_debug) printf(arg) -#define TRACE1(arg1,arg2) if(fd_debug) printf(arg1,arg2) +#define TRACE1(arg1, arg2) if(fd_debug) printf(arg1, arg2) #else /* DEBUG */ #define TRACE0(arg) -#define TRACE1(arg1,arg2) +#define TRACE1(arg1, arg2) #endif /* DEBUG */ -static void fdstart(fdcu_t); -void fdintr(fdcu_t); -static void fd_turnoff(caddr_t, int); - /****************************************************************************/ /* autoconfiguration stuff */ /****************************************************************************/ -static int fdprobe(struct isa_device *); -static int fdattach(struct isa_device *); - struct isa_driver fdcdriver = { fdprobe, fdattach, "fdc", }; @@ -214,56 +253,55 @@ struct isa_driver fdcdriver = { /* * probe for existance of controller */ -int +static int fdprobe(dev) struct isa_device *dev; { fdcu_t fdcu = dev->id_unit; if(fdc_data[fdcu].flags & FDC_ATTACHED) { - printf("fdc: same unit (%d) used multiple times\n",fdcu); + printf("fdc: same unit (%d) used multiple times\n", fdcu); return 0; } fdc_data[fdcu].baseport = dev->id_iobase; /* First - lets reset the floppy controller */ - - outb(dev->id_iobase+fdout,0); + outb(dev->id_iobase+FDOUT, 0); DELAY(100); - outb(dev->id_iobase+fdout,FDO_FRST); + outb(dev->id_iobase+FDOUT, FDO_FRST); /* see if it can handle a command */ - if (out_fdc(fdcu,NE7CMD_SPECIFY) < 0) + if (out_fdc(fdcu, NE7CMD_SPECIFY) < 0) { return(0); } - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); return (IO_FDCSIZE); } /* * wire controller into system, look for floppy units */ -int +static int fdattach(dev) struct isa_device *dev; { - unsigned fdt,st0, cyl; - int hdr; + unsigned fdt; fdu_t fdu; fdcu_t fdcu = dev->id_unit; fdc_p fdc = fdc_data + fdcu; fd_p fd; - int fdsu; + int fdsu, st0; struct isa_device *fdup; fdc->fdcu = fdcu; fdc->flags |= FDC_ATTACHED; fdc->dmachan = dev->id_drq; fdc->state = DEVIDLE; - hdr = 0; + /* reset controller, turn motor off, clear fdout mirror reg */ + outb(fdc->baseport + FDOUT, ((fdc->fdout = 0))); printf("fdc%d:", fdcu); /* check for each floppy drive */ @@ -303,25 +341,33 @@ fdattach(dev) continue; } -#ifdef notyet /* select it */ - fd_turnon1(fdu); - spinwait(1000); /* 1 sec */ - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdsu); + set_motor(fdcu, fdsu, TURNON); spinwait(1000); /* 1 sec */ + out_fdc(fdcu, NE7CMD_SEEK); /* seek some steps... */ + out_fdc(fdcu, fdsu); + out_fdc(fdcu, 10); + spinwait(300); /* ...wait a moment... */ + out_fdc(fdcu, NE7CMD_SENSEI); /* make controller happy */ + (void)in_fdc(fdcu); + (void)in_fdc(fdcu); + out_fdc(fdcu, NE7CMD_RECAL); /* ...and go back to 0 */ + out_fdc(fdcu, fdsu); + spinwait(1000); /* a second be enough for full stroke seek */ /* anything responding */ - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (st0 & 0xd0) + (void)in_fdc(fdcu); + set_motor(fdcu, fdsu, TURNOFF); + + if (st0 & NE7_ST0_EC) /* no track 0 -> no drive present */ continue; -#endif fd->track = -2; fd->fdc = fdc; fd->fdsu = fdsu; + fd->options = 0; printf(" [%d: fd%d: ", fdsu, fdu); switch (fdt) { @@ -346,15 +392,10 @@ fdattach(dev) fd->type = NO_TYPE; break; } - - fd_turnoff((caddr_t)fdu, 0); - hdr = 1; } printf("\n"); - /* Set transfer to 500kbps */ - outb(fdc->baseport+fdctl,0); /*XXX*/ - return 1; + return (1); } int @@ -365,102 +406,47 @@ fdsize(dev) } /****************************************************************************/ -/* fdstrategy */ -/****************************************************************************/ -void fdstrategy(struct buf *bp) -{ - register struct buf *dp,*dp0,*dp1; - long nblocks,blknum; - int s; - fdcu_t fdcu; - fdu_t fdu; - fdc_p fdc; - fd_p fd; - - fdu = FDUNIT(minor(bp->b_dev)); - fd = &fd_data[fdu]; - fdc = fd->fdc; - fdcu = fdc->fdcu; - -#if NFT > 0 - /* check for controller already busy with tape */ - if (fdc->flags & FDC_TAPE_BUSY) { - bp->b_error = EBUSY; - bp->b_flags |= B_ERROR; - return; - } -#endif - if ((fdu >= NFD) || (bp->b_blkno < 0)) { - printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", - fdu, bp->b_blkno, bp->b_bcount); - pg("fd:error in fdstrategy"); - bp->b_error = EINVAL; - bp->b_flags |= B_ERROR; - goto bad; - } - /* - * Set up block calculations. - */ - blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/FDBLK; - nblocks = fd->ft->size; - if (blknum + (bp->b_bcount / FDBLK) > nblocks) { - if (blknum == nblocks) { - bp->b_resid = bp->b_bcount; - } else { - bp->b_error = ENOSPC; - bp->b_flags |= B_ERROR; - } - goto bad; - } - bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); - dp = &(fdc->head); - s = splbio(); - disksort(dp, bp); - untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ - fdstart(fdcu); - splx(s); - return; - -bad: - biodone(bp); - return; -} - -/****************************************************************************/ /* motor control stuff */ /* remember to not deselect the drive we're working on */ /****************************************************************************/ -void -set_motor(fdcu, fdu, reset) +static void +set_motor(fdcu, fdsu, turnon) fdcu_t fdcu; - fdu_t fdu; - int reset; + int fdsu; + int turnon; { - int m0,m1; - int selunit; - fd_p fd; - if(fd = fdc_data[fdcu].fd)/* yes an assign! */ - { - selunit = fd->fdsu; + int fdout = fdc_data[fdcu].fdout; + int needspecify = 0; + + if(turnon) { + fdout &= ~FDO_FDSEL; + fdout |= (FDO_MOEN0 << fdsu) + fdsu; + } else + fdout &= ~(FDO_MOEN0 << fdsu); + + if(!turnon + && (fdout & (FDO_MOEN0+FDO_MOEN1+FDO_MOEN2+FDO_MOEN3)) == 0) + /* gonna turn off the last drive, put FDC to bed */ + fdout &= ~ (FDO_FRST|FDO_FDMAEN); + else { + /* make sure controller is selected and specified */ + if((fdout & (FDO_FRST|FDO_FDMAEN)) == 0) + needspecify = 1; + fdout |= (FDO_FRST|FDO_FDMAEN); } - else - { - selunit = 0; + + outb(fdc_data[fdcu].baseport+FDOUT, fdout); + fdc_data[fdcu].fdout = fdout; + TRACE1("[0x%x->FDOUT]", fdout); + + if(needspecify) { + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } - m0 = fd_data[fdcu * DRVS_PER_CTLR + 0].flags & FD_MOTOR; - m1 = fd_data[fdcu * DRVS_PER_CTLR + 1].flags & FD_MOTOR; - outb(fdc_data[fdcu].baseport+fdout, - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0)); - TRACE1("[0x%x->fdout]",( - selunit - | (reset ? 0 : (FDO_FRST|FDO_FDMAEN)) - | (m0 ? FDO_MOEN0 : 0) - | (m1 ? FDO_MOEN1 : 0))); } +/* ARGSUSED */ static void fd_turnoff(caddr_t arg1, int arg2) { @@ -470,11 +456,12 @@ fd_turnoff(caddr_t arg1, int arg2) fd_p fd = fd_data + fdu; s = splbio(); fd->flags &= ~FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNOFF); splx(s); } -void +/* ARGSUSED */ +static void fd_motor_on(caddr_t arg1, int arg2) { fdu_t fdu = (fdu_t)arg1; @@ -490,27 +477,39 @@ fd_motor_on(caddr_t arg1, int arg2) splx(s); } -static void fd_turnon1(fdu_t); - -void +static void fd_turnon(fdu) fdu_t fdu; { fd_p fd = fd_data + fdu; if(!(fd->flags & FD_MOTOR)) { - fd_turnon1(fdu); - fd->flags |= FD_MOTOR_WAIT; + fd->flags |= (FD_MOTOR + FD_MOTOR_WAIT); + set_motor(fd->fdc->fdcu, fd->fdsu, TURNON); timeout(fd_motor_on, (caddr_t)fdu, hz); /* in 1 sec its ok */ } } static void -fd_turnon1(fdu_t fdu) +fdc_reset(fdc) + fdc_p fdc; { - fd_p fd = fd_data + fdu; - fd->flags |= FD_MOTOR; - set_motor(fd->fdc->fdcu,fd->fdsu,0); + fdcu_t fdcu = fdc->fdcu; + + /* Try a reset, keep motor on */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~(FDO_FRST|FDO_FDMAEN)); + DELAY(100); + /* enable FDC, but defer interrupts a moment */ + outb(fdc->baseport + FDOUT, fdc->fdout & ~FDO_FDMAEN); + TRACE1("[0x%x->FDOUT]", fdc->fdout & ~FDO_FDMAEN); + DELAY(100); + outb(fdc->baseport + FDOUT, fdc->fdout); + TRACE1("[0x%x->FDOUT]", fdc->fdout); + + out_fdc(fdcu, NE7CMD_SPECIFY); + out_fdc(fdcu, NE7_SPEC_1(3, 240)); + out_fdc(fdcu, NE7_SPEC_2(2, 0)); } /****************************************************************************/ @@ -522,17 +521,17 @@ in_fdc(fdcu) { int baseport = fdc_data[fdcu].baseport; int i, j = 100000; - while ((i = inb(baseport+fdsts) & (NE7_DIO|NE7_RQM)) + while ((i = inb(baseport+FDSTS) & (NE7_DIO|NE7_RQM)) != (NE7_DIO|NE7_RQM) && j-- > 0) if (i == NE7_RQM) return -1; if (j <= 0) return(-1); #ifdef DEBUG - i = inb(baseport+fddata); - TRACE1("[fddata->0x%x]",(unsigned char)i); + i = inb(baseport+FDDATA); + TRACE1("[FDDATA->0x%x]", (unsigned char)i); return(i); #else - return inb(baseport+fddata); + return inb(baseport+FDDATA); #endif } @@ -546,17 +545,17 @@ out_fdc(fdcu, x) /* Check that the direction bit is set */ i = 100000; - while ((inb(baseport+fdsts) & NE7_DIO) && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_DIO) && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Check that the floppy controller is ready for a command */ i = 100000; - while ((inb(baseport+fdsts) & NE7_RQM) == 0 && i-- > 0); + while ((inb(baseport+FDSTS) & NE7_RQM) == 0 && i-- > 0); if (i <= 0) return (-1); /* Floppy timed out */ /* Send the command and return */ - outb(baseport+fddata,x); - TRACE1("[0x%x->fddata]",x); + outb(baseport+FDDATA, x); + TRACE1("[0x%x->FDDATA]", x); return (0); } @@ -647,17 +646,97 @@ fdclose(dev, flags) int flags; { fdu_t fdu = FDUNIT(minor(dev)); - int type = FDTYPE(minor(dev)); #if NFT > 0 + int type = FDTYPE(minor(dev)); + if (type & F_TAPE_TYPE) - return ftclose(0); + return ftclose(dev, flags); #endif fd_data[fdu].flags &= ~FD_OPEN; + fd_data[fdu].options &= ~FDOPT_NORETRY; return(0); } +/****************************************************************************/ +/* fdstrategy */ +/****************************************************************************/ +void +fdstrategy(struct buf *bp) +{ + register struct buf *dp; + long nblocks, blknum; + int s; + fdcu_t fdcu; + fdu_t fdu; + fdc_p fdc; + fd_p fd; + size_t fdblk; + + fdu = FDUNIT(minor(bp->b_dev)); + fd = &fd_data[fdu]; + fdc = fd->fdc; + fdcu = fdc->fdcu; + fdblk = 128 << (fd->ft->secsize); + +#if NFT > 0 + if (FDTYPE(minor(bp->b_dev)) & F_TAPE_TYPE) { + /* ft tapes do not (yet) support strategy i/o */ + bp->b_error = ENXIO; + bp->b_flags |= B_ERROR; + goto bad; + } + /* check for controller already busy with tape */ + if (fdc->flags & FDC_TAPE_BUSY) { + bp->b_error = EBUSY; + bp->b_flags |= B_ERROR; + goto bad; + } +#endif + if (!(bp->b_flags & B_FORMAT)) { + if ((fdu >= NFD) || (bp->b_blkno < 0)) { + printf("fdstrat: fdu = %d, blkno = %d, bcount = %d\n", + fdu, bp->b_blkno, bp->b_bcount); + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + if ((bp->b_bcount % fdblk) != 0) { + bp->b_error = EINVAL; + bp->b_flags |= B_ERROR; + goto bad; + } + } + + /* + * Set up block calculations. + */ + blknum = (unsigned long) bp->b_blkno * DEV_BSIZE/fdblk; + nblocks = fd->ft->size; + if (blknum + (bp->b_bcount / fdblk) > nblocks) { + if (blknum == nblocks) { + bp->b_resid = bp->b_bcount; + } else { + bp->b_error = ENOSPC; + bp->b_flags |= B_ERROR; + } + goto bad; + } + bp->b_cylin = blknum / (fd->ft->sectrac * fd->ft->heads); + dp = &(fdc->head); + s = splbio(); + disksort(dp, bp); + untimeout(fd_turnoff, (caddr_t)fdu); /* a good idea */ + fdstart(fdcu); + splx(s); + return; + +bad: + biodone(bp); + return; +} + /***************************************************************\ * fdstart * * We have just queued something.. if the controller is not busy * @@ -671,9 +750,7 @@ static void fdstart(fdcu) fdcu_t fdcu; { - register struct buf *dp,*bp; int s; - fdu_t fdu; s = splbio(); if(fdc_data[fdcu].state == DEVIDLE) @@ -683,38 +760,43 @@ fdstart(fdcu) splx(s); } +/* ARGSUSED */ static void fd_timeout(caddr_t arg1, int arg2) { fdcu_t fdcu = (fdcu_t)arg1; fdu_t fdu = fdc_data[fdcu].fdu; - int st0, st3, cyl; - struct buf *dp,*bp; - int s; + int baseport = fdc_data[fdcu].baseport; + struct buf *dp, *bp; + int s; dp = &fdc_data[fdcu].head; - s = splbio(); bp = dp->b_actf; - out_fdc(fdcu,NE7CMD_SENSED); - out_fdc(fdcu,fd_data[fdu].hddrv); - st3 = in_fdc(fdcu); - - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - printf("fd%d: Operation timeout ST0 %b cyl %d ST3 %b\n", - fdu, - st0, - NE7_ST0BITS, - cyl, - st3, - NE7_ST3BITS); + /* + * Due to IBM's brain-dead design, the FDC has a faked ready + * signal, hardwired to ready == true. Thus, any command + * issued if there's no diskette in the drive will _never_ + * complete, and must be aborted by resetting the FDC. + * Many thanks, Big Blue! + */ + + s = splbio(); + + TRACE1("fd%d[fd_timeout()]", fdu); + /* See if the controller is still busy (patiently awaiting data) */ + if(((inb(baseport + FDSTS)) & (NE7_CB|NE7_RQM)) == NE7_CB) + { + TRACE1("[FDSTS->0x%x]", inb(baseport + FDSTS)); + /* yup, it is; kill it now */ + fdc_reset(&fdc_data[fdcu]); + printf("fd%d: Operation timeout\n", fdu); + } if (bp) { retrier(fdcu); - fdc_data[fdcu].status[0] = 0xc0; + fdc_data[fdcu].status[0] = NE7_ST0_IC_RC; fdc_data[fdcu].state = IOTIMEDOUT; if( fdc_data[fdcu].retry < 6) fdc_data[fdcu].retry = 6; @@ -730,11 +812,13 @@ fd_timeout(caddr_t arg1, int arg2) } /* just ensure it has the right spl */ +/* ARGSUSED */ static void fd_pseudointr(caddr_t arg1, int arg2) { fdcu_t fdcu = (fdcu_t)arg1; int s; + s = splbio(); fdintr(fdcu); splx(s); @@ -756,25 +840,26 @@ fdintr(fdcu_t fdcu) (ftintr(fdu)); else #endif - while(fdstate(fdcu, fdc)) - ; + while(fdstate(fdcu, fdc)) + ; } /***********************************************************************\ * The controller state machine. * * if it returns a non zero value, it should be called again immediatly * \***********************************************************************/ -int +static int fdstate(fdcu, fdc) fdcu_t fdcu; fdc_p fdc; { - int read, format, head, trac, sec = 0, i = 0, s, sectrac, cyl, st0; + int read, format, head, sec = 0, i = 0, sectrac, st0, cyl, st3; unsigned long blknum; fdu_t fdu = fdc->fdu; fd_p fd; - register struct buf *dp,*bp; + register struct buf *dp, *bp; struct fd_formb *finfo = NULL; + size_t fdblk; dp = &(fdc->head); bp = dp->b_actf; @@ -787,16 +872,17 @@ fdstate(fdcu, fdc) fdc->state = DEVIDLE; if(fdc->fd) { - printf("unexpected valid fd pointer (fdu = %d)\n" - ,fdc->fdu); + printf("unexpected valid fd pointer (fdu = %d)\n", + fdc->fdu); fdc->fd = (fd_p) 0; fdc->fdu = -1; } - TRACE1("[fdc%d IDLE]",fdcu); + TRACE1("[fdc%d IDLE]", fdcu); return(0); } fdu = FDUNIT(minor(bp->b_dev)); fd = fd_data + fdu; + fdblk = 128 << fd->ft->secsize; if (fdc->fd && (fd != fdc->fd)) { printf("confused fd pointers\n"); @@ -805,9 +891,9 @@ fdstate(fdcu, fdc) format = bp->b_flags & B_FORMAT; if(format) finfo = (struct fd_formb *)bp->b_un.b_addr; - TRACE1("fd%d",fdu); - TRACE1("[%s]",fdstates[fdc->state]); - TRACE1("(0x%x)",fd->flags); + TRACE1("fd%d", fdu); + TRACE1("[%s]", fdstates[fdc->state]); + TRACE1("(0x%x)", fd->flags); untimeout(fd_turnoff, (caddr_t)fdu); timeout(fd_turnoff, (caddr_t)fdu, 4 * hz); switch (fdc->state) @@ -818,6 +904,8 @@ fdstate(fdcu, fdc) fd->skip = 0; fdc->fd = fd; fdc->fdu = fdu; + outb(fdc->baseport+FDCTL, fd->ft->trans); + TRACE1("[0x%x->FDCTL]", fd->ft->trans); /*******************************************************\ * If the next drive has a motor startup pending, then * * it will start up in it's own good time * @@ -838,7 +926,7 @@ fdstate(fdcu, fdc) } else /* at least make sure we are selected */ { - set_motor(fdcu,fd->fdsu,0); + set_motor(fdcu, fd->fdsu, TURNON); } fdc->state = DOSEEK; break; @@ -848,33 +936,55 @@ fdstate(fdcu, fdc) fdc->state = SEEKCOMPLETE; break; } - out_fdc(fdcu,NE7CMD_SEEK); /* Seek function */ - out_fdc(fdcu,fd->fdsu); /* Drive number */ - out_fdc(fdcu,bp->b_cylin * fd->ft->steptrac); + out_fdc(fdcu, NE7CMD_SEEK); /* Seek function */ + out_fdc(fdcu, fd->fdsu); /* Drive number */ + out_fdc(fdcu, bp->b_cylin * fd->ft->steptrac); fd->track = -2; fdc->state = SEEKWAIT; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); return(0); /* will return later */ case SEEKWAIT: - untimeout(fd_timeout, (caddr_t)fdcu); /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 50); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = SEEKCOMPLETE; return(0); /* will return later */ - break; - case SEEKCOMPLETE : /* SEEK DONE, START DMA */ /* Make sure seek really happened*/ if(fd->track == -2) { int descyl = bp->b_cylin * fd->ft->steptrac; - out_fdc(fdcu,NE7CMD_SENSEI); - i = in_fdc(fdcu); - cyl = in_fdc(fdcu); + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if (0 == descyl) + { + /* + * seek to cyl 0 requested; make sure we are + * really there + */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if ((st3 & NE7_ST3_T0) == 0) { + printf( + "fd%d: Seek to cyl 0, but not really there (ST3 = %b)\n", + fdu, st3, NE7_ST3BITS); + if(fdc->retry < 3) + fdc->retry = 3; + return(retrier(fdcu)); + } + } if (cyl != descyl) { - printf("fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", - fdu, descyl, cyl, i, NE7_ST0BITS); + printf( + "fd%d: Seek to cyl %d failed; am at cyl %d (ST0 = 0x%x)\n", + fdu, descyl, cyl, st0, NE7_ST0BITS); return(retrier(fdcu)); } } @@ -884,46 +994,73 @@ fdstate(fdcu, fdc) fd->skip = (char *)&(finfo->fd_formb_cylno(0)) - (char *)finfo; isa_dmastart(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; + format ? bp->b_bcount : fdblk, fdc->dmachan); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; sectrac = fd->ft->sectrac; sec = blknum % (sectrac * fd->ft->heads); head = sec / sectrac; sec = sec % sectrac + 1; -/*XXX*/ fd->hddrv = ((head&1)<<2)+fdu; + fd->hddrv = ((head&1)<<2)+fdu; + + if(format || !read) + { + /* make sure the drive is writable */ + out_fdc(fdcu, NE7CMD_SENSED); + out_fdc(fdcu, fdu); + st3 = in_fdc(fdcu); + if(st3 & NE7_ST3_WP) + { + /* + * XXX YES! this is ugly. + * in order to force the current operation + * to fail, we will have to fake an FDC + * error - all error handling is done + * by the retrier() + */ + fdc->status[0] = NE7_ST0_IC_AT; + fdc->status[1] = NE7_ST1_NW; + fdc->status[2] = 0; + fdc->status[3] = fd->track; + fdc->status[4] = head; + fdc->status[5] = sec; + fdc->retry = 8; /* break out immediately */ + fdc->state = IOTIMEDOUT; /* not really... */ + return (1); + } + } if(format) { /* formatting */ - out_fdc(fdcu,/* NE7CMD_FORMAT */ 0x4d); - out_fdc(fdcu,head << 2 | fdu); - out_fdc(fdcu,finfo->fd_formb_secshift); - out_fdc(fdcu,finfo->fd_formb_nsecs); - out_fdc(fdcu,finfo->fd_formb_gaplen); - out_fdc(fdcu,finfo->fd_formb_fillbyte); + out_fdc(fdcu, NE7CMD_FORMAT); + out_fdc(fdcu, head << 2 | fdu); + out_fdc(fdcu, finfo->fd_formb_secshift); + out_fdc(fdcu, finfo->fd_formb_nsecs); + out_fdc(fdcu, finfo->fd_formb_gaplen); + out_fdc(fdcu, finfo->fd_formb_fillbyte); } else { if (read) { - out_fdc(fdcu,NE7CMD_READ); /* READ */ + out_fdc(fdcu, NE7CMD_READ); /* READ */ } else { - out_fdc(fdcu,NE7CMD_WRITE); /* WRITE */ + out_fdc(fdcu, NE7CMD_WRITE); /* WRITE */ } - out_fdc(fdcu,head << 2 | fdu); /* head & unit */ - out_fdc(fdcu,fd->track); /* track */ - out_fdc(fdcu,head); - out_fdc(fdcu,sec); /* sector XXX +1? */ - out_fdc(fdcu,fd->ft->secsize); /* sector size */ - out_fdc(fdcu,sectrac); /* sectors/track */ - out_fdc(fdcu,fd->ft->gap); /* gap size */ - out_fdc(fdcu,fd->ft->datalen); /* data length */ + out_fdc(fdcu, head << 2 | fdu); /* head & unit */ + out_fdc(fdcu, fd->track); /* track */ + out_fdc(fdcu, head); + out_fdc(fdcu, sec); /* sector XXX +1? */ + out_fdc(fdcu, fd->ft->secsize); /* sector size */ + out_fdc(fdcu, sectrac); /* sectors/track */ + out_fdc(fdcu, fd->ft->gap); /* gap size */ + out_fdc(fdcu, fd->ft->datalen); /* data length */ } fdc->state = IOCOMPLETE; - timeout(fd_timeout, (caddr_t)fdcu, 2 * hz); + timeout(fd_timeout, (caddr_t)fdcu, hz); return(0); /* will return later */ case IOCOMPLETE: /* IO DONE, post-analyze */ untimeout(fd_timeout, (caddr_t)fdcu); @@ -931,30 +1068,43 @@ fdstate(fdcu, fdc) { fdc->status[i] = in_fdc(fdcu); } - case IOTIMEDOUT: /*XXX*/ + fdc->state = IOTIMEDOUT; + /* FALLTHROUGH */ + case IOTIMEDOUT: isa_dmadone(bp->b_flags, bp->b_un.b_addr+fd->skip, - format ? bp->b_bcount : FDBLK, fdc->dmachan); - if (fdc->status[0]&0xF8) + format ? bp->b_bcount : fdblk, fdc->dmachan); + if (fdc->status[0] & NE7_ST0_IC) { - if (fdc->status[1] & 0x10) { + if ((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[1] & NE7_ST1_OR) { /* - * Operation not completed in reasonable time. - * Just restart it, don't increment retry count. - * (vak) + * DMA overrun. Someone hogged the bus + * and didn't release it in time for the + * next FDC transfer. + * Just restart it, don't increment retry + * count. (vak) */ fdc->state = SEEKCOMPLETE; return (1); } + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_IV + && fdc->retry < 6) + fdc->retry = 6; /* force a reset */ + else if((fdc->status[0] & NE7_ST0_IC) == NE7_ST0_IC_AT + && fdc->status[2] & NE7_ST2_WC + && fdc->retry < 3) + fdc->retry = 3; /* force recalibrate */ return(retrier(fdcu)); } /* All OK */ - fd->skip += FDBLK; + fd->skip += fdblk; if (!format && fd->skip < bp->b_bcount) { /* set up next transfer */ - blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/FDBLK - + fd->skip/FDBLK; - bp->b_cylin = (blknum / (fd->ft->sectrac * fd->ft->heads)); + blknum = (unsigned long)bp->b_blkno*DEV_BSIZE/fdblk + + fd->skip/fdblk; + bp->b_cylin = + (blknum / (fd->ft->sectrac * fd->ft->heads)); fdc->state = DOSEEK; } else @@ -970,68 +1120,76 @@ fdstate(fdcu, fdc) } return(1); case RESETCTLR: - /* Try a reset, keep motor on */ - set_motor(fdcu,fd->fdsu,1); - DELAY(100); - set_motor(fdcu,fd->fdsu,0); - outb(fdc->baseport+fdctl,fd->ft->trans); - TRACE1("[0x%x->fdctl]",fd->ft->trans); + fdc_reset(fdc); fdc->retry++; fdc->state = STARTRECAL; break; case STARTRECAL: - out_fdc(fdcu,NE7CMD_SPECIFY); /* specify command */ - out_fdc(fdcu,0xDF); - out_fdc(fdcu,2); - out_fdc(fdcu,NE7CMD_RECAL); /* Recalibrate Function */ - out_fdc(fdcu,fdu); + out_fdc(fdcu, NE7CMD_RECAL); /* Recalibrate Function */ + out_fdc(fdcu, fdu); fdc->state = RECALWAIT; return(0); /* will return later */ case RECALWAIT: /* allow heads to settle */ - timeout(fd_pseudointr, (caddr_t)fdcu, hz / 30); + timeout(fd_pseudointr, (caddr_t)fdcu, hz / 32); fdc->state = RECALCOMPLETE; return(0); /* will return later */ case RECALCOMPLETE: - out_fdc(fdcu,NE7CMD_SENSEI); - st0 = in_fdc(fdcu); - cyl = in_fdc(fdcu); - if (cyl != 0) + do { + out_fdc(fdcu, NE7CMD_SENSEI); + st0 = in_fdc(fdcu); + cyl = in_fdc(fdcu); + /* + * if this was a "ready changed" interrupt, + * fetch status again (can happen after + * enabling controller from reset state) + */ + } while ((st0 & NE7_ST0_IC) == NE7_ST0_IC_RC); + if ((st0 & NE7_ST0_IC) != NE7_ST0_IC_NT || cyl != 0) { printf("fd%d: recal failed ST0 %b cyl %d\n", fdu, st0, NE7_ST0BITS, cyl); + if(fdc->retry < 3) fdc->retry = 3; return(retrier(fdcu)); } fd->track = 0; /* Seek (probably) necessary */ fdc->state = DOSEEK; return(1); /* will return immediatly */ - case MOTORWAIT: + case MOTORWAIT: if(fd->flags & FD_MOTOR_WAIT) { return(0); /* time's not up yet */ } - fdc->state = DOSEEK; + /* + * since the controller was off, it has lost its + * idea about the current track it were; thus, + * recalibrate the bastard + */ + fdc->state = STARTRECAL; return(1); /* will return immediatly */ default: printf("Unexpected FD int->"); - out_fdc(fdcu,NE7CMD_SENSEI); + out_fdc(fdcu, NE7CMD_SENSEI); st0 = in_fdc(fdcu); cyl = in_fdc(fdcu); - printf("ST0 = %lx, PCN = %lx\n",i,sec); - out_fdc(fdcu,0x4A); - out_fdc(fdcu,fd->fdsu); + printf("ST0 = %x, PCN = %x\n", st0, cyl); + out_fdc(fdcu, NE7CMD_READID); + out_fdc(fdcu, fd->fdsu); for(i=0;i<7;i++) { fdc->status[i] = in_fdc(fdcu); } - printf("intr status :%lx %lx %lx %lx %lx %lx %lx ", - fdc->status[0], - fdc->status[1], - fdc->status[2], - fdc->status[3], - fdc->status[4], - fdc->status[5], - fdc->status[6] ); + if(fdc->status[0] != -1) + printf("intr status :%lx %lx %lx %lx %lx %lx %lx\n", + fdc->status[0], + fdc->status[1], + fdc->status[2], + fdc->status[3], + fdc->status[4], + fdc->status[5], + fdc->status[6] ); + else + printf("FDC timed out\n"); return(0); } return(1); /* Come back immediatly to new state */ @@ -1042,11 +1200,13 @@ retrier(fdcu) fdcu_t fdcu; { fdc_p fdc = fdc_data + fdcu; - register struct buf *dp,*bp; + register struct buf *dp, *bp; dp = &(fdc->head); bp = dp->b_actf; + if(fd_data[FDUNIT(minor(bp->b_dev))].options & FDOPT_NORETRY) + goto fail; switch(fdc->retry) { case 0: case 1: case 2: @@ -1061,12 +1221,15 @@ retrier(fdcu) case 7: break; default: + fail: { dev_t sav_b_dev = bp->b_dev; /* Trick diskerr */ - bp->b_dev = makedev(major(bp->b_dev), (FDUNIT(minor(bp->b_dev))<<3)|3); + bp->b_dev = makedev(major(bp->b_dev), + (FDUNIT(minor(bp->b_dev))<<3)|3); diskerr(bp, "fd", "hard error", LOG_PRINTF, - fdc->fd->skip, (struct disklabel *)NULL); + fdc->fd->skip / DEV_BSIZE, + (struct disklabel *)NULL); bp->b_dev = sav_b_dev; printf(" (ST0 %b ", fdc->status[0], NE7_ST0BITS); printf(" ST1 %b ", fdc->status[1], NE7_ST1BITS); @@ -1101,9 +1264,11 @@ fdformat(dev, finfo, p) struct buf *bp; int rv = 0, s; - + size_t fdblk; + fdu = FDUNIT(minor(dev)); fd = &fd_data[fdu]; + fdblk = 128 << fd->ft->secsize; /* set up a buffer header for fdstrategy() */ bp = (struct buf *)malloc(sizeof(struct buf), M_TEMP, M_NOWAIT); @@ -1119,7 +1284,7 @@ fdformat(dev, finfo, p) * seek to the requested cylinder */ bp->b_blkno = (finfo->cyl * (fd->ft->sectrac * fd->ft->heads) - + finfo->head * fd->ft->sectrac) * FDBLK / DEV_BSIZE; + + finfo->head * fd->ft->sectrac) * fdblk / DEV_BSIZE; bp->b_bcount = sizeof(struct fd_idfield_data) * finfo->fd_formb_nsecs; bp->b_un.b_addr = (caddr_t)finfo; @@ -1138,41 +1303,39 @@ fdformat(dev, finfo, p) splx(s); if(rv == EWOULDBLOCK) - { /* timed out */ - biodone(bp); rv = EIO; - } + if(bp->b_flags & B_ERROR) + rv = bp->b_error; + biodone(bp); free(bp, M_TEMP); return rv; } /* - * fdioctl() from jc@irbs.UUCP (John Capo) - * i386/i386/conf.c needs to have fdioctl() declared and remove the line that - * defines fdioctl to be enxio. * - * TODO: Reformat. - * Think about allocating buffer off stack. + * TODO: Think about allocating buffer off stack. * Don't pass uncast 0's and NULL's to read/write/setdisklabel(). * Watch out for NetBSD's different *disklabel() interface. * - * Added functionality for floppy formatting - * joerg_wunsch@uriah.sax.de (Joerg Wunsch) */ int -fdioctl (dev, cmd, addr, flag, p) +fdioctl(dev, cmd, addr, flag, p) dev_t dev; int cmd; caddr_t addr; int flag; struct proc *p; { + fdu_t fdu = FDUNIT(minor(dev)); + fd_p fd = &fd_data[fdu]; + size_t fdblk; + struct fd_type *fdt; struct disklabel *dl; char buffer[DEV_BSIZE]; - int error; + int error = 0; #if NFT > 0 int type = FDTYPE(minor(dev)); @@ -1182,14 +1345,14 @@ fdioctl (dev, cmd, addr, flag, p) return ftioctl(dev, cmd, addr, flag, p); #endif - error = 0; + fdblk = 128 << fd->ft->secsize; switch (cmd) { case DIOCGDINFO: bzero(buffer, sizeof (buffer)); dl = (struct disklabel *)buffer; - dl->d_secsize = FDBLK; + dl->d_secsize = fdblk; fdt = fd_data[FDUNIT(minor(dev))].ft; dl->d_secpercyl = fdt->size / fdt->tracks; dl->d_type = DTYPE_FLOPPY; @@ -1221,12 +1384,12 @@ fdioctl (dev, cmd, addr, flag, p) dl = (struct disklabel *)addr; - if (error = setdisklabel ((struct disklabel *)buffer, - dl, 0, NULL)) + if ((error = + setdisklabel ((struct disklabel *)buffer, dl, 0, NULL))) break; error = writedisklabel(dev, fdstrategy, - (struct disklabel *)buffer, NULL); + (struct disklabel *)buffer, NULL); break; case FD_FORM: @@ -1243,11 +1406,42 @@ fdioctl (dev, cmd, addr, flag, p) *(struct fd_type *)addr = *fd_data[FDUNIT(minor(dev))].ft; break; + case FD_STYPE: /* set drive type */ + /* this is considered harmful; only allow for superuser */ + if(suser(p->p_ucred, &p->p_acflag) != 0) + return EPERM; + *fd_data[FDUNIT(minor(dev))].ft = *(struct fd_type *)addr; + break; + + case FD_GOPTS: /* get drive options */ + *(int *)addr = fd_data[FDUNIT(minor(dev))].options; + break; + + case FD_SOPTS: /* set drive options */ + fd_data[FDUNIT(minor(dev))].options = *(int *)addr; + break; + default: - error = EINVAL; + error = ENOTTY; break; } return (error); } #endif +/* + * Hello emacs, these are the + * Local Variables: + * c-indent-level: 8 + * c-continued-statement-offset: 8 + * c-continued-brace-offset: 0 + * c-brace-offset: -8 + * c-brace-imaginary-offset: 0 + * c-argdecl-indent: 8 + * c-label-offset: -8 + * c++-hanging-braces: 1 + * c++-access-specifier-offset: -8 + * c++-empty-arglist-indent: 8 + * c++-friend-offset: 0 + * End: + */ diff --git a/sys/i386/isa/fdc.h b/sys/i386/isa/fdc.h index 7d75d5a81b48..ef81b11a8387 100644 --- a/sys/i386/isa/fdc.h +++ b/sys/i386/isa/fdc.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fd.c 7.4 (Berkeley) 5/25/91 - * $Id: fdc.h,v 1.2 1994/02/14 22:24:25 nate Exp $ + * $Id: fdc.h,v 1.3 1994/05/22 12:35:38 joerg Exp $ * */ @@ -49,12 +49,12 @@ struct fdc_data #define FDC_HASFTAPE 0x02 #define FDC_TAPE_BUSY 0x04 struct fd_data *fd; - int fdu; /* the active drive */ + int fdu; /* the active drive */ + int state; + int retry; + int fdout; /* mirror of the w/o digital output reg */ + int status[7]; /* copy of the registers */ struct buf head; /* Head of buf chain */ - struct buf rhead; /* Raw head of buf chain */ - int state; - int retry; - int status[7]; /* copy of the registers */ }; /***********************************************************************\ diff --git a/sys/i386/isa/fdreg.h b/sys/i386/isa/fdreg.h index b0e9593a93c5..9b5be2b87b1c 100644 --- a/sys/i386/isa/fdreg.h +++ b/sys/i386/isa/fdreg.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)fdreg.h 7.1 (Berkeley) 5/9/91 - * $Id: fdreg.h,v 1.4 1994/02/07 22:12:42 alm Exp $ + * $Id: fdreg.h,v 1.5 1994/05/22 12:35:40 joerg Exp $ */ /* @@ -42,24 +42,26 @@ #include "../i386/isa/ic/nec765.h" /* registers */ -#define fdout 2 /* Digital Output Register (W) */ +#define FDOUT 2 /* Digital Output Register (W) */ #define FDO_FDSEL 0x03 /* floppy device select */ #define FDO_FRST 0x04 /* floppy controller reset */ #define FDO_FDMAEN 0x08 /* enable floppy DMA and Interrupt */ #define FDO_MOEN0 0x10 /* motor enable drive 0 */ #define FDO_MOEN1 0x20 /* motor enable drive 1 */ -#define FDO_MOEN2 0x30 /* motor enable drive 2 */ -#define FDO_MOEN3 0x40 /* motor enable drive 3 */ +#define FDO_MOEN2 0x40 /* motor enable drive 2 */ +#define FDO_MOEN3 0x80 /* motor enable drive 3 */ -#define fdsts 4 /* NEC 765 Main Status Register (R) */ -#define fddata 5 /* NEC 765 Data Register (R/W) */ +#define FDSTS 4 /* NEC 765 Main Status Register (R) */ +#define FDDATA 5 /* NEC 765 Data Register (R/W) */ -#define fdctl 7 /* Control Register (W) */ +#define FDCTL 7 /* Control Register (W) */ #define FDC_500KBPS 0x00 /* 500KBPS MFM drive transfer rate */ #define FDC_300KBPS 0x01 /* 300KBPS MFM drive transfer rate */ #define FDC_250KBPS 0x02 /* 250KBPS MFM drive transfer rate */ #define FDC_125KBPS 0x03 /* 125KBPS FM drive transfer rate */ + /* for some controllers 1MPBS instead */ -#define fdin 7 /* Digital Input Register (R) */ +#define FDIN 7 /* Digital Input Register (R) */ #define FDI_DCHG 0x80 /* diskette has been changed */ - + /* requires drive and motor being selected */ + /* is cleared by any step pulse to drive */ diff --git a/sys/i386/isa/ft.c b/sys/i386/isa/ft.c index 09bc127090aa..1204613bcd8e 100644 --- a/sys/i386/isa/ft.c +++ b/sys/i386/isa/ft.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993 Steve Gerakines + * Copyright (c) 1993, 1994 Steve Gerakines * * This is freely redistributable software. You may do anything you * wish with it, so long as the above notice stays intact. @@ -17,8 +17,15 @@ * POSSIBILITY OF SUCH DAMAGE. * * ft.c - QIC-40/80 floppy tape driver - * $Id: ft.c,v 1.4 1994/02/14 22:24:28 nate Exp $ + * $Id: ft.c,v 1.7 1994/06/22 05:52:36 jkh Exp $ * + * 06/07/94 v0.9 ++sg + * Tape stuck on segment problem should be gone. Re-wrote buffering + * scheme. Added support for drives that do not automatically perform + * seek load point. Can handle more wakeup types now and should correctly + * report most manufacturer names. Fixed places where unit 0 was being + * sent to the fdc instead of the actual unit number. Added ioctl support + * for an in-core badmap. * * 01/26/94 v0.3b - Jim Babb * Got rid of the hard coded device selection. Moved (some of) the @@ -76,12 +83,13 @@ #include "ftreg.h" /* Enable or disable debugging messages. */ -#define FTDBGALL 0 /* everything */ -/* #define DPRT(a) printf a */ -#define DPRT(a) +#define FTDBGALL 0 /* 1 if you want everything */ +/*#define DPRT(a) printf a */ +#define DPRT(a) /* Constants private to the driver */ #define FTPRI (PRIBIO) /* sleep priority */ +#define FTNBUFF 9 /* 8 for buffering, 1 for header */ /* The following items are needed from the fd driver. */ extern int in_fdc(int); /* read fdc registers */ @@ -92,11 +100,13 @@ extern int hz; /* system clock rate */ /* Type of tape attached */ /* use numbers that don't interfere with the possible floppy types */ #define NO_TYPE 0 /* (same as NO_TYPE in fd.c) */ - /* F_TAPE_TYPE must match value in fd.c */ -#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */ -#define FT_MOUNTAIN (F_TAPE_TYPE | 1) -#define FT_COLORADO (F_TAPE_TYPE | 2) +/* F_TAPE_TYPE must match value in fd.c */ +#define F_TAPE_TYPE 0x020 /* bit for ft->types to indicate tape */ +#define FT_NONE (F_TAPE_TYPE | 0) /* no method required */ +#define FT_MOUNTAIN (F_TAPE_TYPE | 1) /* mountain */ +#define FT_COLORADO (F_TAPE_TYPE | 2) /* colorado */ +#define FT_INSIGHT (F_TAPE_TYPE | 3) /* insight */ /* Mode FDC is currently in: tape or disk */ enum { FDC_TAPE_MODE, FDC_DISK_MODE }; @@ -150,15 +160,25 @@ QIC_Geom *ftg = NULL; /* Current tape's geometry */ /* * things relating to asynchronous commands */ -static int astk_depth; /* async_cmd stack depth */ static int awr_state; /* state of async write */ static int ard_state; /* state of async read */ static int arq_state; /* state of async request */ static int async_retries; /* retries, one per invocation */ static int async_func; /* function to perform */ static int async_state; /* state current function is at */ -static int async_arg[5]; /* up to 5 arguments for async cmds */ +static int async_arg0; /* up to 3 arguments for async cmds */ +static int async_arg1; /**/ +static int async_arg2; /**/ static int async_ret; /* return value */ +static struct _astk { + int over_func; + int over_state; + int over_retries; + int over_arg0; + int over_arg1; + int over_arg2; +} astk[10]; +static struct _astk *astk_ptr = &astk[0]; /* Pointer to stack position */ /* List of valid async (interrupt driven) tape support functions. */ enum { @@ -172,29 +192,36 @@ enum { }; /* Call another asyncronous command from within async_cmd(). */ -#define CALL_ACMD(r,f,a,b,c,d,e) \ - astk[astk_depth].over_retries = async_retries; \ - astk[astk_depth].over_func = async_func; \ - astk[astk_depth].over_state = (r); \ - for (i = 0; i < 5; i++) \ - astk[astk_depth].over_arg[i] = async_arg[i]; \ +#define CALL_ACMD(r,f,a,b,c) \ + astk_ptr->over_retries = async_retries; \ + astk_ptr->over_func = async_func; \ + astk_ptr->over_state = (r); \ + astk_ptr->over_arg0 = async_arg0; \ + astk_ptr->over_arg1 = async_arg1; \ + astk_ptr->over_arg2 = async_arg2; \ async_func = (f); async_state = 0; async_retries = 0; \ - async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ - async_arg[3]=(d); async_arg[4]=(e); \ - astk_depth++; \ + async_arg0=(a); async_arg1=(b); async_arg2=(c); \ + astk_ptr++; \ goto restate /* Perform an asyncronous command from outside async_cmd(). */ -#define ACMD_FUNC(r,f,a,b,c,d,e) over_async = (r); astk_depth = 0; \ +#define ACMD_FUNC(r,f,a,b,c) over_async = (r); astk_ptr = &astk[0]; \ async_func = (f); async_state = 0; async_retries = 0; \ - async_arg[0]=(a); async_arg[1]=(b); async_arg[2]=(c); \ - async_arg[3]=(d); async_arg[4]=(e); \ + async_arg0=(a); async_arg1=(b); async_arg2=(c); \ async_cmd(ftu); \ return /* Various wait channels */ +static char *wc_buff_avail = "bavail"; +static char *wc_buff_done = "bdone"; +static char *wc_iosts_change = "iochg"; +static char *wc_long_delay = "ldelay"; +static char *wc_intr_wait = "intrw"; +#define ftsleep(wc,to) tsleep((caddr_t)(wc),FTPRI,(wc),(to)) + static struct { int buff_avail; + int buff_done; int iosts_change; int long_delay; int intr_wait; @@ -223,8 +250,17 @@ struct ft_data { unsigned char *xptr; /* pointer to buffer blk to xfer */ int xcnt; /* transfer count */ int xblk; /* block number to transfer */ - SegReq *curseg; /* Current segment to do I/O on */ - SegReq *bufseg; /* Buffered segment to r/w ahead */ + int xseg; /* segment being transferred */ + SegReq *segh; /* Current I/O request */ + SegReq *segt; /* Tail of queued I/O requests */ + SegReq *doneh; /* Completed I/O request queue */ + SegReq *donet; /* Completed I/O request tail */ + SegReq *segfree; /* Free segments */ + SegReq *hdr; /* Current tape header */ + int nsegq; /* Segments on request queue */ + int ndoneq; /* Segments on completed queue */ + int nfreelist; /* Segments on free list */ + /* the next 3 should be defines in 'flags' */ int active; /* TRUE if transfer is active */ int rdonly; /* TRUE if tape is read-only */ @@ -261,85 +297,237 @@ void ftstrategy(struct buf *); int ftioctl(dev_t, int, caddr_t, int, struct proc *); int ftdump(dev_t); int ftsize(dev_t); -static void ft_timeout(caddr_t arg1, int arg2); -void async_cmd(ftu_t); -void async_req(ftu_t, int); -void async_read(ftu_t, int); -void async_write(ftu_t, int); -void tape_start(ftu_t); -void tape_end(ftu_t); -void tape_inactive(ftu_t); +static void ft_timeout(caddr_t, int); +static void async_cmd(ftu_t); +static void async_req(ftu_t, int); +static void async_read(ftu_t, int); +static void async_write(ftu_t, int); +static void tape_start(ftu_t, int); +static void tape_end(ftu_t); +static void tape_inactive(ftu_t); +static int tape_cmd(ftu_t, int); +static int tape_status(ftu_t); +static int qic_status(ftu_t, int, int); +static int ftreq_rewind(ftu_t); +static int ftreq_hwinfo(ftu_t, QIC_HWInfo *); + +/*****************************************************************************/ + + +/* + * Allocate a segment I/O buffer from the free list. + */ +static SegReq * +segio_alloc(ft_p ft) +{ + SegReq *r; + + /* Grab first item from free list */ + if ((r = ft->segfree) != NULL) { + ft->segfree = ft->segfree->next; + ft->nfreelist--; + } + DPRT(("segio_alloc: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + return(r); +} + + +/* + * Queue a segment I/O request. + */ +static void +segio_queue(ft_p ft, SegReq *sp) +{ + /* Put request on in process queue. */ + if (ft->segt == NULL) + ft->segh = sp; + else + ft->segt->next = sp; + sp->next = NULL; + ft->segt = sp; + ft->nsegq++; + DPRT(("segio_queue: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); +} +/* + * Segment I/O completed, place on correct queue. + */ +static void +segio_done(ft_p ft, SegReq *sp) +{ + /* First remove from current I/O queue */ + ft->segh = sp->next; + if (ft->segh == NULL) ft->segt = NULL; + ft->nsegq--; + + if (sp->reqtype == FTIO_WRITING) { + /* Place on free list */ + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; + wakeup((caddr_t)wc_buff_avail); + DPRT(("segio_done: (w) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + } else { + /* Put on completed I/O queue */ + if (ft->donet == NULL) + ft->doneh = sp; + else + ft->donet->next = sp; + sp->next = NULL; + ft->donet = sp; + ft->ndoneq++; + wakeup((caddr_t)wc_buff_done); + DPRT(("segio_done: (r) nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); + } +} +/* + * Take I/O request from finished queue to free queue. + */ +static void +segio_free(ft_p ft, SegReq *sp) +{ + /* First remove from done queue */ + ft->doneh = sp->next; + if (ft->doneh == NULL) ft->donet = NULL; + ft->ndoneq--; + + /* Place on free list */ + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; + wakeup((caddr_t)wc_buff_avail); + DPRT(("segio_free: nfree=%d ndone=%d nreq=%d\n", ft->nfreelist, ft->ndoneq, ft->nsegq)); +} + /* * Probe/attach floppy tapes. */ -int ftattach(isadev, fdup) +int +ftattach(isadev, fdup) struct isa_device *isadev, *fdup; { - fdcu_t fdcu = isadev->id_unit; /* fdc active unit */ - fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */ - ftu_t ftu = fdup->id_unit; - ft_p ft; - ftsu_t ftsu = fdup->id_physid; - - if (ftu >= NFT) - return 0; - ft = &ft_data[ftu]; - /* Probe for tape */ - ft->attaching = 1; - ft->type = NO_TYPE; - ft->fdc = fdc; - ft->ftsu = ftsu; - - tape_start(ftu); /* ready controller for tape */ - tape_cmd(ftu, QC_COL_ENABLE1); - tape_cmd(ftu, QC_COL_ENABLE2); - if (tape_status(ftu) >= 0) { - ft->type = FT_COLORADO; - fdc->flags |= FDC_HASFTAPE; - printf(" [%d: ft%d: Colorado tape]", - fdup->id_physid, fdup->id_unit ); - tape_cmd(ftu, QC_COL_DISABLE); - goto out; - } + fdcu_t fdcu = isadev->id_unit; /* fdc active unit */ + fdc_p fdc = fdc_data + fdcu; /* pointer to controller structure */ + ftu_t ftu = fdup->id_unit; + ft_p ft; + ftsu_t ftsu = fdup->id_physid; + QIC_HWInfo hw; + char *manu; + + if (ftu >= NFT) return 0; + ft = &ft_data[ftu]; + + /* Probe for tape */ + ft->attaching = 1; + ft->type = NO_TYPE; + ft->fdc = fdc; + ft->ftsu = ftsu; - tape_start(ftu); /* ready controller for tape */ - tape_cmd(ftu, QC_MTN_ENABLE1); - tape_cmd(ftu, QC_MTN_ENABLE2); - if (tape_status(ftu) >= 0) { - ft->type = FT_MOUNTAIN; - fdc->flags |= FDC_HASFTAPE; - printf(" [%d: ft%d: Mountain tape]", - fdup->id_physid, fdup->id_unit ); - tape_cmd(ftu, QC_MTN_DISABLE); - goto out; - } + /* + * FT_NONE - no method, just do it + */ + tape_start(ftu, 0); + if (tape_status(ftu) >= 0) { + ft->type = FT_NONE; + ftreq_hwinfo(ftu, &hw); + goto out; + } + + /* + * FT_COLORADO - colorado style + */ + tape_start(ftu, 0); + tape_cmd(ftu, QC_COL_ENABLE1); + tape_cmd(ftu, QC_COL_ENABLE2 + ftu); + if (tape_status(ftu) >= 0) { + ft->type = FT_COLORADO; + ftreq_hwinfo(ftu, &hw); + tape_cmd(ftu, QC_COL_DISABLE); + goto out; + } + + /* + * FT_MOUNTAIN - mountain style + */ + tape_start(ftu, 0); + tape_cmd(ftu, QC_MTN_ENABLE1); + tape_cmd(ftu, QC_MTN_ENABLE2); + if (tape_status(ftu) >= 0) { + ft->type = FT_MOUNTAIN; + ftreq_hwinfo(ftu, &hw); + tape_cmd(ftu, QC_MTN_DISABLE); + goto out; + } + + /* + * FT_INSIGHT - insight style + */ + tape_start(ftu, 1); + if (tape_status(ftu) >= 0) { + ft->type = FT_INSIGHT; + ftreq_hwinfo(ftu, &hw); + goto out; + } out: - tape_end(ftu); - ft->attaching = 0; - return(ft->type); + tape_end(ftu); + if (ft->type != NO_TYPE) { + fdc->flags |= FDC_HASFTAPE; + switch(hw.hw_make) { + case 0x0000: + if (ft->type == FT_COLORADO) + manu = "Colorado"; + else if (ft->type == FT_INSIGHT) + manu = "Insight"; + else if (ft->type == FT_MOUNTAIN && hw.hw_model == 0x05) + manu = "Archive"; + else if (ft->type == FT_MOUNTAIN) + manu = "Mountain"; + else + manu = "Unknown"; + break; + case 0x0001: + manu = "Colorado"; + break; + case 0x0005: + if (hw.hw_model >= 0x09) + manu = "Conner"; + else + manu = "Archive"; + break; + case 0x0006: + manu = "Mountain"; + break; + case 0x0007: + manu = "Wangtek"; + break; + case 0x0222: + manu = "IOMega"; + break; + default: + manu = "Unknown"; + break; + } + printf(" [%d: ft%d: %s tape]", fdup->id_physid, fdup->id_unit, manu); + } + ft->attaching = 0; + return(ft->type); } /* * Perform common commands asynchronously. */ -void async_cmd(ftu_t ftu) { +static void +async_cmd(ftu_t ftu) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; int cmd, i, st0, st3, pcn; static int bitn, retval, retpos, nbits, newcn; - static struct { - int over_func; - int over_state; - int over_retries; - int over_arg[5]; - } astk[15]; static int wanttrk, wantblk, wantdir; static int curpos, curtrk, curblk, curdir, curdiff; static int errcnt = 0; @@ -356,7 +544,7 @@ restate: */ switch (async_state) { case 0: - cmd = async_arg[0]; + cmd = async_arg0; #if FTDBGALL DPRT(("===>async_seek cmd = %d\n", cmd)); #endif @@ -364,7 +552,7 @@ restate: async_state = 1; i = 0; if (out_fdc(fdcu, NE7CMD_SEEK) < 0) i = 1; - if (!i && out_fdc(fdcu, 0x00) < 0) i = 1; + if (!i && out_fdc(fdcu, ftu) < 0) i = 1; if (!i && out_fdc(fdcu, newcn) < 0) i = 1; if (i) { if (++async_retries >= 10) { @@ -399,7 +587,7 @@ restate: DPRT(("ft%d: async_seek error st0 = $%02x pcn = %d\n", ftu, st0, pcn)); #endif - if (async_arg[1]) goto complete; + if (async_arg1) goto complete; async_state = 2; timeout(ft_timeout, (caddr_t)ftu, hz/50); break; @@ -420,14 +608,14 @@ restate: case 0: bitn = 0; retval = 0; - cmd = async_arg[0]; - nbits = async_arg[1]; + cmd = async_arg0; + nbits = async_arg1; DPRT(("async_status got cmd = %d nbits = %d\n", cmd,nbits)); - CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_SEEK, QC_NEXTBIT, 0, 0); /* NOTREACHED */ case 1: out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if (st3 < 0) { DPRT(("ft%d: async_status timed out on bit %d r=$%02x\n", @@ -440,7 +628,7 @@ restate: if (bitn >= (nbits+2)) { if ((retval & 1) && (retval & (1 << (nbits+1)))) { async_ret = (retval & ~(1<<(nbits+1))) >> 1; - if (async_arg[0] == QC_STATUS && async_arg[2] == 0 && + if (async_arg0 == QC_STATUS && async_arg2 == 0 && (async_ret & (QS_ERROR|QS_NEWCART))) { async_state = 2; goto restate; @@ -453,31 +641,31 @@ restate: } goto complete; } - CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEK, QC_NEXTBIT, 0, 0); /* NOTREACHED */ case 2: if (async_ret & QS_NEWCART) ft->newcart = 1; - CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1, 0, 0); + CALL_ACMD(3, ACMD_STATUS, QC_ERRCODE, 16, 1); case 3: ft->lasterr = async_ret; if ((ft->lasterr & QS_NEWCART) == 0 && ft->lasterr) { DPRT(("ft%d: QIC error %d occurred on cmd %d\n", ftu, ft->lasterr & 0xff, ft->lasterr >> 8)); } - cmd = async_arg[0]; - nbits = async_arg[1]; - CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1, 0, 0); + cmd = async_arg0; + nbits = async_arg1; + CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 1); case 4: goto complete; case 5: - CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(6, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 6: - CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(7, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 7: - CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0, 0, 0); + CALL_ACMD(8, ACMD_SEEK, QC_NEXTBIT, 0, 0); case 8: - cmd = async_arg[0]; - CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); + cmd = async_arg0; + CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0); } break; @@ -488,9 +676,9 @@ restate: */ switch(async_state) { case 0: - CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(1, ACMD_STATUS, QC_STATUS, 8, 0); case 1: - if ((async_ret & async_arg[0]) != 0) goto complete; + if ((async_ret & async_arg0) != 0) goto complete; async_state = 0; if (++async_retries == 360) { /* 90 secs. */ DPRT(("ft%d: acmd_state exceeded retry count\n", ftu)); @@ -510,13 +698,13 @@ restate: */ switch(async_state) { case 0: - cmd = async_arg[0]; - async_retries = (async_arg[2]) ? (async_arg[2]*4) : 10; - CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0, 0, 0); + cmd = async_arg0; + async_retries = (async_arg2) ? (async_arg2 * 4) : 10; + CALL_ACMD(1, ACMD_SEEK, cmd, 0, 0); case 1: - CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(2, ACMD_STATUS, QC_STATUS, 8, 0); case 2: - if ((async_ret & async_arg[1]) != 0) goto complete; + if ((async_ret & async_arg1) != 0) goto complete; if (--async_retries == 0) { DPRT(("ft%d: acmd_seeksts retries exceeded\n", ftu)); goto complete; @@ -534,12 +722,12 @@ restate: switch(async_state) { case 0: if (!ft->moving) { - CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); /* NOTREACHED */ } async_state = 1; out_fdc(fdcu, 0x4a); /* READ_ID */ - out_fdc(fdcu, 0); + out_fdc(fdcu, ftu); break; case 1: for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); @@ -548,25 +736,36 @@ restate: DPRT(("readid st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d\n", ft->rid[0], ft->rid[1], ft->rid[2], ft->rid[3], ft->rid[4], ft->rid[5], async_ret)); - if ((ft->rid[0] & 0xc0) == 0x40) { - if (++errcnt >= 10) { + if ((ft->rid[0] & 0xc0) != 0 || async_ret < 0) { + /* + * Method for retry: + * errcnt == 1 regular retry + * 2 microstep head 1 + * 3 microstep head 2 + * 4 microstep head back to 0 + * 5 fail + */ + if (++errcnt >= 5) { DPRT(("ft%d: acmd_readid errcnt exceeded\n", fdcu)); - async_ret = ft->lastpos; + async_ret = -2; errcnt = 0; goto complete; } - if (errcnt > 2) { + if (errcnt == 1) { + ft->moving = 0; + CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } else { ft->moving = 0; - CALL_ACMD(4, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(4, ACMD_SEEKSTS, QC_STPAUSE, QS_READY, 0); } - DPRT(("readid retry...\n")); + DPRT(("readid retry %d...\n", errcnt)); async_state = 0; goto restate; } if ((async_ret % ftg->g_blktrk) == (ftg->g_blktrk-1)) { DPRT(("acmd_readid detected last block on track\n")); retpos = async_ret; - CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); + CALL_ACMD(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0); /* NOTREACHED */ } ft->lastpos = async_ret; @@ -574,13 +773,13 @@ restate: goto complete; /* NOTREACHED */ case 2: - CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + CALL_ACMD(3, ACMD_STATE, QS_READY, 0, 0); case 3: ft->moving = 0; async_ret = retpos+1; goto complete; case 4: - CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_SEEK, QC_FORWARD, 0, 0); case 5: ft->moving = 1; async_state = 0; @@ -598,27 +797,27 @@ restate: */ switch (async_state) { case 0: - wanttrk = async_arg[0] / ftg->g_blktrk; - wantblk = async_arg[0] % ftg->g_blktrk; + wanttrk = async_arg0 / ftg->g_blktrk; + wantblk = async_arg0 % ftg->g_blktrk; wantdir = wanttrk & 1; ft->moving = 0; - CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); case 1: curtrk = wanttrk; curdir = curtrk & 1; DPRT(("Changing to track %d\n", wanttrk)); - CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); + CALL_ACMD(2, ACMD_SEEK, QC_SEEKTRACK, 0, 0); case 2: cmd = wanttrk+2; - CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + CALL_ACMD(3, ACMD_SEEKSTS, cmd, QS_READY, 0); case 3: - CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0, 0, 0); + CALL_ACMD(4, ACMD_STATUS, QC_STATUS, 8, 0); case 4: ft->laststs = async_ret; if (wantblk == 0) { curblk = 0; cmd = (wantdir) ? QC_SEEKEND : QC_SEEKSTART; - CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90, 0, 0); + CALL_ACMD(6, ACMD_SEEKSTS, cmd, QS_READY, 90); } if (ft->laststs & QS_BOT) { DPRT(("Tape is at BOT\n")); @@ -632,15 +831,23 @@ restate: async_state = 6; goto restate; } - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_READID, 0, 0, 0); case 5: + if (async_ret < 0) { + ft->moving = 0; + ft->lastpos = -2; + if (async_ret == -2) { + CALL_ACMD(9, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); + } curtrk = (async_ret+1) / ftg->g_blktrk; curblk = (async_ret+1) % ftg->g_blktrk; DPRT(("gotid: curtrk=%d wanttrk=%d curblk=%d wantblk=%d\n", curtrk, wanttrk, curblk, wantblk)); if (curtrk != wanttrk) { /* oops! */ DPRT(("oops!! wrong track!\n")); - CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(1, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); } async_state = 6; goto restate; @@ -650,22 +857,22 @@ restate: ft->lastpos = curblk - 1; async_ret = ft->lastpos; if (ft->moving) goto complete; - CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0, 0, 0); + CALL_ACMD(7, ACMD_STATE, QS_READY, 0, 0); } if (curblk > wantblk) { /* passed it */ ft->moving = 0; - CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(10, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); } - if ((wantblk - curblk) <= 96) { /* approaching it */ - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + if ((wantblk - curblk) <= 256) { /* approaching it */ + CALL_ACMD(5, ACMD_READID, 0, 0, 0); } /* way up ahead */ ft->moving = 0; - CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + CALL_ACMD(14, ACMD_SEEKSTS, QC_STOP, QS_READY, 0); break; case 7: ft->moving = 1; - CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + CALL_ACMD(8, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 8: async_state = 9; @@ -677,26 +884,26 @@ restate: curdiff = ((curblk - wantblk) / QCV_BLKSEG) + 2; if (curdiff >= ftg->g_segtrk) curdiff = ftg->g_segtrk - 1; DPRT(("pos %d past %d, reverse %d\n", curblk, wantblk, curdiff)); - CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0, 0, 0); + CALL_ACMD(11, ACMD_SEEK, QC_SEEKREV, 0, 0); case 11: DPRT(("reverse 1 done\n")); - CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); + CALL_ACMD(12, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0); case 12: DPRT(("reverse 2 done\n")); - CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); + CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90); case 13: - CALL_ACMD(5, ACMD_READID, 0, 0, 0, 0, 0); + CALL_ACMD(5, ACMD_READID, 0, 0, 0); case 14: curdiff = ((wantblk - curblk) / QCV_BLKSEG) - 2; if (curdiff < 0) curdiff = 0; DPRT(("pos %d before %d, forward %d\n", curblk, wantblk, curdiff)); - CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0, 0, 0); + CALL_ACMD(15, ACMD_SEEK, QC_SEEKFWD, 0, 0); case 15: DPRT(("forward 1 done\n")); - CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0, 0, 0); + CALL_ACMD(16, ACMD_SEEK, (curdiff & 0xf)+2, 0, 0); case 16: DPRT(("forward 2 done\n")); - CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90, 0, 0); + CALL_ACMD(13, ACMD_SEEKSTS, ((curdiff>>4)&0xf)+2, QS_READY, 90); } break; } @@ -704,13 +911,14 @@ restate: return; complete: - if (astk_depth) { - astk_depth--; - async_retries = astk[astk_depth].over_retries; - async_func = astk[astk_depth].over_func; - async_state = astk[astk_depth].over_state; - for(i = 0; i < 5; i++) - async_arg[i] = astk[astk_depth].over_arg[i]; + if (astk_ptr != &astk[0]) { + astk_ptr--; + async_retries = astk_ptr->over_retries; + async_func = astk_ptr->over_func; + async_state = astk_ptr->over_state; + async_arg0 = astk_ptr->over_arg0; + async_arg1 = astk_ptr->over_arg1; + async_arg2 = astk_ptr->over_arg2; goto restate; } async_func = ACMD_NONE; @@ -720,6 +928,7 @@ complete: async_req(ftu, 2); break; case FTIO_READING: + case FTIO_RDAHEAD: async_read(ftu, 2); break; case FTIO_WRITING: @@ -735,10 +944,11 @@ complete: /* * Entry point for the async request processor. */ -void async_req(ftu_t ftu, int from) +static void +async_req(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; - SegReq *sp; + SegReq *nsp, *sp; static int over_async, lastreq, domore; int cmd; @@ -747,56 +957,73 @@ void async_req(ftu_t ftu, int from) restate: switch (arq_state) { case 0: /* Process segment */ - ft->io_sts = ft->curseg->reqtype; + sp = ft->segh; + ft->io_sts = (sp == NULL) ? FTIO_READY : sp->reqtype; + if (ft->io_sts == FTIO_WRITING) async_write(ftu, from); else async_read(ftu, from); if (ft->io_sts != FTIO_READY) return; - /* Swap buffered and current segment */ - lastreq = ft->curseg->reqtype; - ft->curseg->reqtype = FTIO_READY; - sp = ft->curseg; - ft->curseg = ft->bufseg; - ft->bufseg = sp; + /* Pull buffer from current I/O queue */ + if (sp != NULL) { + lastreq = sp->reqtype; + segio_done(ft, sp); - wakeup((caddr_t)&ftsem.buff_avail); + /* If I/O cancelled, clear finished queue. */ + if (sp->reqcan) { + while (ft->doneh != NULL) + segio_free(ft, ft->doneh); + lastreq = FTIO_READY; + } + } else + lastreq = FTIO_READY; /* Detect end of track */ if (((ft->xblk / QCV_BLKSEG) % ftg->g_segtrk) == 0) { - domore = (ft->curseg->reqtype != FTIO_READY); - ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0, 0, 0); + ACMD_FUNC(2, ACMD_STATE, QS_BOT|QS_EOT, 0, 0); } arq_state = 1; goto restate; case 1: /* Next request */ - if (ft->curseg->reqtype != FTIO_READY) { - ft->curseg->reqcrc = 0; + /* If we have another request queued, start it running. */ + if (ft->segh != NULL) { + sp = ft->segh; + sp->reqcrc = 0; arq_state = ard_state = awr_state = 0; - ft->xblk = ft->curseg->reqblk; + ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; - ft->xptr = ft->curseg->buff; - DPRT(("I/O reqblk = %d\n", ft->curseg->reqblk)); + ft->xptr = sp->buff; + DPRT(("I/O reqblk = %d\n", ft->xblk)); goto restate; } - if (lastreq == FTIO_READING) { - ft->curseg->reqtype = FTIO_RDAHEAD; - ft->curseg->reqblk = ft->xblk; - ft->curseg->reqcrc = 0; - ft->curseg->reqcan = 0; - bzero(ft->curseg->buff, QCV_SEGSIZE); + + /* If the last request was reading, do read ahead. */ + if ((lastreq == FTIO_READING || lastreq == FTIO_RDAHEAD) && + (sp = segio_alloc(ft)) != NULL) { + sp->reqtype = FTIO_RDAHEAD; + sp->reqblk = ft->xblk; + sp->reqseg = ft->xseg+1; + sp->reqcrc = 0; + sp->reqcan = 0; + segio_queue(ft, sp); + bzero(sp->buff, QCV_SEGSIZE); arq_state = ard_state = awr_state = 0; - ft->xblk = ft->curseg->reqblk; + ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; - ft->xptr = ft->curseg->buff; - DPRT(("Processing readahead reqblk = %d\n", ft->curseg->reqblk)); + ft->xptr = sp->buff; + DPRT(("Processing readahead reqblk = %d\n", ft->xblk)); goto restate; } + if (ft->moving) { DPRT(("No more I/O.. Stopping.\n")); - ACMD_FUNC(7, ACMD_SEEKSTS, QC_STOP, QS_READY, 0, 0, 0); + ft->moving = 0; + ACMD_FUNC(7, ACMD_SEEKSTS, QC_PAUSE, QS_READY, 0); break; } arq_state = 7; @@ -804,26 +1031,26 @@ restate: case 2: /* End of track */ ft->moving = 0; - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); break; case 3: DPRT(("async_req seek head to track %d\n", ft->xblk / ftg->g_blktrk)); - ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_SEEKTRACK, 0, 0); break; case 4: cmd = (ft->xblk / ftg->g_blktrk) + 2; - if (domore) { - ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + if (ft->segh != NULL) { + ACMD_FUNC(5, ACMD_SEEKSTS, cmd, QS_READY, 0); } else { - ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0, 0, 0); + ACMD_FUNC(7, ACMD_SEEKSTS, cmd, QS_READY, 0); } break; case 5: ft->moving = 1; - ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(6, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 6: @@ -832,26 +1059,22 @@ restate: break; case 7: - ft->moving = 0; - - /* Check one last time to see if a request came in. */ - if (ft->curseg->reqtype != FTIO_READY) { - DPRT(("async_req: Never say no!\n")); - arq_state = 1; - goto restate; - } - /* Time to rest. */ ft->active = 0; - wakeup((caddr_t)&ftsem.iosts_change); /* wakeup those who want an i/o chg */ + ft->lastpos = -2; + + /* wakeup those who want an i/o chg */ + wakeup((caddr_t)wc_iosts_change); break; } } + /* * Entry for async read. */ -void async_read(ftu_t ftu, int from) +static void +async_read(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ @@ -859,6 +1082,7 @@ void async_read(ftu_t ftu, int from) int i, cmd, newcn, rddta[7]; int st0, pcn, where; static int over_async; + static int retries = 0; if (from == 2) ard_state = over_async; @@ -872,13 +1096,13 @@ restate: if (ft->lastpos != (ft->xblk-1)) { DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", ftu, ft->lastpos, ft->xblk)); - ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); + ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0); } /* Tape is in position but stopped. */ if (!ft->moving) { DPRT(("async_read ******STARTING TAPE\n")); - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); } ard_state = 1; goto restate; @@ -887,8 +1111,8 @@ restate: /* Tape is now moving and in position-- start DMA now! */ isa_dmastart(B_READ, ft->xptr, QCV_BLKSIZE, 2); out_fdc(fdcu, 0x66); /* read */ - out_fdc(fdcu, 0x00); /* unit */ - out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ + out_fdc(fdcu, ftu); /* unit */ + out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ out_fdc(fdcu, 0x03); /* 1K sectors */ @@ -905,7 +1129,8 @@ restate: #if FTDBGALL /* Compute where the controller thinks we are */ - where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + rddta[5]-1; + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], where, ft->xblk)); @@ -913,34 +1138,44 @@ restate: /* Check for errors */ if ((rddta[0] & 0xc0) != 0x00) { - if (rddta[1] & 0x04) { +#if !FTDBGALL + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; + DPRT(("xd: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", + rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], + where, ft->xblk)); +#endif + if ((rddta[1] & 0x04) == 0x04 && retries < 2) { /* Probably wrong position */ + DPRT(("async_read: doing retry %d\n", retries)); ft->lastpos = ft->xblk; ard_state = 0; + retries++; goto restate; } else { /* CRC/Address-mark/Data-mark, et. al. */ DPRT(("ft%d: CRC error on block %d\n", fdcu, ft->xblk)); - ft->curseg->reqcrc |= (1 << ft->xcnt); + ft->segh->reqcrc |= (1 << ft->xcnt); } } /* Otherwise, transfer completed okay. */ + retries = 0; ft->lastpos = ft->xblk; ft->xblk++; ft->xcnt++; ft->xptr += QCV_BLKSIZE; - if (ft->xcnt < QCV_BLKSEG && ft->curseg->reqcan == 0) { + if (ft->xcnt < QCV_BLKSEG && ft->segh->reqcan == 0) { ard_state = 0; goto restate; } - DPRT(("Read done.. Cancel = %d\n", ft->curseg->reqcan)); + DPRT(("Read done.. Cancel = %d\n", ft->segh->reqcan)); ft->io_sts = FTIO_READY; break; case 3: ft->moving = 1; - ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 4: @@ -960,7 +1195,8 @@ restate: * routine, if it's 1 then it was a timeout, if it's 2, then an * async_cmd completed. */ -void async_write(ftu_t ftu, int from) +static void +async_write(ftu_t ftu, int from) { ft_p ft = &ft_data[ftu]; fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ @@ -982,13 +1218,13 @@ restate: if (ft->lastpos != (ft->xblk-1)) { DPRT(("ft%d: position unknown: lastpos:%d ft->xblk:%d\n", ftu, ft->lastpos, ft->xblk)); - ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0, 0, 0); + ACMD_FUNC(1, ACMD_RUNBLK, ft->xblk, 0, 0); } /* Tape is in position but stopped. */ if (!ft->moving) { DPRT(("async_write ******STARTING TAPE\n")); - ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0, 0, 0); + ACMD_FUNC(3, ACMD_STATE, QS_READY, 0, 0); } awr_state = 1; goto restate; @@ -997,8 +1233,8 @@ restate: /* Tape is now moving and in position-- start DMA now! */ isa_dmastart(B_WRITE, ft->xptr, QCV_BLKSIZE, 2); out_fdc(fdcu, 0x45); /* write */ - out_fdc(fdcu, 0x00); /* unit */ - out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cylinder */ + out_fdc(fdcu, ftu); /* unit */ + out_fdc(fdcu, (ft->xblk % ftg->g_fdside) / ftg->g_fdtrk); /* cyl */ out_fdc(fdcu, ft->xblk / ftg->g_fdside); /* head */ out_fdc(fdcu, (ft->xblk % ftg->g_fdtrk) + 1); /* sector */ out_fdc(fdcu, 0x03); /* 1K sectors */ @@ -1023,13 +1259,16 @@ restate: /* Check for errors */ if ((rddta[0] & 0xc0) != 0x00) { - if (rddta[1] & 0x04) { - /* Probably wrong position */ - ft->lastpos = ft->xblk; - awr_state = 0; - goto restate; - } else if (retries < 5) { +#if !FTDBGALL + where = (rddta[3]*ftg->g_fdtrk) + (rddta[4]*ftg->g_fdside) + + rddta[5]-1; + DPRT(("xfer done: st0:%02x st1:%02x st2:%02x c:%d h:%d s:%d pos:%d want:%d\n", + rddta[0], rddta[1], rddta[2], rddta[3], rddta[4], rddta[5], + where, ft->xblk)); +#endif + if (retries < 3) { /* Something happened -- try again */ + DPRT(("async_write: doing retry %d\n", retries)); ft->lastpos = ft->xblk; awr_state = 0; retries++; @@ -1037,11 +1276,11 @@ restate: } else { /* * Retries failed. Note the unrecoverable error. - * Marking the block as bad is fairly useless. + * Marking the block as bad is useless right now. */ printf("ft%d: unrecoverable write error on block %d\n", ftu, ft->xblk); - ft->curseg->reqcrc |= (1 << ft->xcnt); + ft->segh->reqcrc |= (1 << ft->xcnt); } } @@ -1063,7 +1302,7 @@ restate: case 3: ft->moving = 1; - ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0, 0, 0); + ACMD_FUNC(4, ACMD_SEEK, QC_FORWARD, 0, 0); break; case 4: @@ -1081,11 +1320,14 @@ restate: /* * Interrupt handler for active tape. Bounced off of fdintr(). */ -int ftintr(ftu_t ftu) +int +ftintr(ftu_t ftu) { int st0, pcn, i; ft_p ft = &ft_data[ftu]; - fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ + fdcu_t fdcu = ft->fdc->fdcu; /* fdc active unit */ + int s = splbio(); + st0 = 0; pcn = 0; @@ -1093,12 +1335,14 @@ int ftintr(ftu_t ftu) if (ft->active) { if (async_func != ACMD_NONE) { async_cmd(ftu); + splx(s); return(1); } #if FTDBGALL DPRT(("Got request interrupt\n")); #endif async_req(ftu, 0); + splx(s); return(1); } @@ -1113,20 +1357,21 @@ int ftintr(ftu_t ftu) huh_what: printf("ft%d: unexpected interrupt; st0 = $%02x pcn = %d\n", ftu, st0, pcn); + splx(s); return(1); } switch (ft->cmd_wait) { case FTCMD_RESET: ft->sts_wait = FTSTS_INTERRUPT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); break; case FTCMD_RECAL: case FTCMD_SEEK: if (st0 & 0x20) { /* seek done */ ft->sts_wait = FTSTS_INTERRUPT; ft->pcn = pcn; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); } #if FTDBGALL else @@ -1137,20 +1382,23 @@ huh_what: case FTCMD_READID: for (i = 0; i < 7; i++) ft->rid[i] = in_fdc(fdcu); ft->sts_wait = FTSTS_INTERRUPT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); break; default: goto huh_what; } + splx(s); return(1); } + /* * Interrupt timeout routine. */ -static void ft_timeout(caddr_t arg1, int arg2) +static void +ft_timeout(caddr_t arg1, int arg2) { int s; ftu_t ftu = (ftu_t)arg1; @@ -1166,17 +1414,19 @@ static void ft_timeout(caddr_t arg1, int arg2) async_req(ftu, 1); } else { ft->sts_wait = FTSTS_TIMEOUT; - wakeup((caddr_t)&ftsem.intr_wait); + wakeup((caddr_t)wc_intr_wait); } splx(s); } + /* * Wait for a particular interrupt to occur. ftintr() will wake us up * if it sees what we want. Otherwise, time out and return error. * Should always disable ints before trigger is sent and calling here. */ -int ftintr_wait(ftu_t ftu, int cmd, int ticks) +static int +ftintr_wait(ftu_t ftu, int cmd, int ticks) { int retries, st0, pcn; ft_p ft = &ft_data[ftu]; @@ -1211,8 +1461,7 @@ int ftintr_wait(ftu_t ftu, int cmd, int ticks) goto intrdone; } - if (ticks) timeout(ft_timeout, (caddr_t)ftu, ticks); - sleep((caddr_t)&ftsem.intr_wait, FTPRI); + ftsleep(wc_intr_wait, ticks); intrdone: if (ft->sts_wait == FTSTS_TIMEOUT) { /* timeout */ @@ -1230,11 +1479,13 @@ intrdone: return(0); } + /* * Recalibrate tape drive. Parameter totape is true, if we should * recalibrate to tape drive settings. */ -int tape_recal(ftu_t ftu, int totape) +static int +tape_recal(ftu_t ftu, int totape) { int s; ft_p ft = &ft_data[ftu]; @@ -1248,7 +1499,7 @@ int tape_recal(ftu_t ftu, int totape) s = splbio(); out_fdc(fdcu, NE7CMD_RECAL); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); if (ftintr_wait(ftu, FTCMD_RECAL, hz)) { splx(s); @@ -1265,18 +1516,25 @@ int tape_recal(ftu_t ftu, int totape) return(0); } -static void state_timeout(caddr_t arg1, int arg2) + +/* + * Timeout for long delays. + */ +static void +state_timeout(caddr_t arg1, int arg2) { ftu_t ftu = (ftu_t)arg1; - wakeup((caddr_t)&ftsem.long_delay); + wakeup((caddr_t)wc_long_delay); } + /* * Wait for a particular tape status to be met. If all is TRUE, then * all states must be met, otherwise any state can be met. */ -int tape_state(ftu_t ftu, int all, int mask, int seconds) +static int +tape_state(ftu_t ftu, int all, int mask, int seconds) { int r, tries, maxtries; @@ -1287,20 +1545,19 @@ int tape_state(ftu_t ftu, int all, int mask, int seconds) if (all && (r & mask) == mask) return(r); if ((r & mask) != 0) return(r); } - if (seconds) { - timeout(state_timeout, (caddr_t)ftu, hz/4); - sleep((caddr_t)&ftsem.long_delay, FTPRI); - } + if (seconds) ftsleep(wc_long_delay, hz/4); } DPRT(("ft%d: tape_state failed on mask=$%02x maxtries=%d\n", ftu, mask, maxtries)); return(-1); } + /* * Send a QIC command to tape drive, wait for completion. */ -int tape_cmd(ftu_t ftu, int cmd) +static int +tape_cmd(ftu_t ftu, int cmd) { int newcn; int retries = 0; @@ -1316,7 +1573,7 @@ retry: /* Perform seek */ s = splbio(); out_fdc(fdcu, NE7CMD_SEEK); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); out_fdc(fdcu, newcn); if (ftintr_wait(ftu, FTCMD_SEEK, hz)) { @@ -1338,25 +1595,40 @@ redo: return(0); } + /* * Return status of tape drive */ -int tape_status(ftu_t ftu) +static int +tape_status(ftu_t ftu) { int r, err, tries; - ft_p ft = &ft_data[ftu]; + ft_p ft = &ft_data[ftu]; + int max = (ft->attaching) ? 2 : 3; - for (r = -1, tries = 0; r < 0 && tries < 3; tries++) + for (r = -1, tries = 0; r < 0 && tries < max; tries++) r = qic_status(ftu, QC_STATUS, 8); - if (tries == 3) return(-1); + if (tries == max) return(-1); + +recheck: DPRT(("tape_status got $%04x\n",r)); ft->laststs = r; if (r & (QS_ERROR|QS_NEWCART)) { - if (r & QS_NEWCART) ft->newcart = 1; err = qic_status(ftu, QC_ERRCODE, 16); ft->lasterr = err; - if ((r & QS_NEWCART) == 0 && err && ft->attaching == 0) { + if (r & QS_NEWCART) { + ft->newcart = 1; + /* If tape not referenced, do a seek load point. */ + if ((r & QS_FMTOK) == 0 && !ft->attaching) { + tape_cmd(ftu, QC_SEEKLP); + do { + ftsleep(wc_long_delay, hz); + } while ((r = qic_status(ftu, QC_STATUS, 8)) < 0 || + (r & (QS_READY|QS_CART)) == QS_CART); + goto recheck; + } + } else if (err && !ft->attaching) { DPRT(("ft%d: QIC error %d occurred on cmd %d\n", ftu, err & 0xff, err >> 8)); } @@ -1364,29 +1636,36 @@ int tape_status(ftu_t ftu) ft->laststs = r; DPRT(("tape_status got error code $%04x new sts = $%02x\n",err,r)); } + ft->rdonly = (r & QS_RDONLY); return(r); } + /* * Transfer control to tape drive. */ -void tape_start(ftu_t ftu) +static void +tape_start(ftu_t ftu, int motor) { ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; - int s; - - DPRT(("tape_start start\n")); + int s, mbits; s = splbio(); + DPRT(("tape_start start\n")); /* reset, dma disable */ - outb(fdc->baseport+fdout, 0x00); + outb(fdc->baseport+FDOUT, 0x00); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); - /* raise reset, enable DMA */ - outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); + /* raise reset, enable DMA, motor on if needed */ + if (motor) + mbits = (!ftu) ? FDO_MOEN0 : FDO_MOEN1; + else + mbits = 0; + + outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN | mbits); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); splx(s); @@ -1394,16 +1673,18 @@ void tape_start(ftu_t ftu) tape_recal(ftu, 1); /* set transfer speed */ - outb(fdc->baseport+fdctl, FDC_500KBPS); + outb(fdc->baseport+FDCTL, FDC_500KBPS); DELAY(10); DPRT(("tape_start end\n")); } + /* * Transfer control back to floppy disks. */ -void tape_end(ftu_t ftu) +static void +tape_end(ftu_t ftu) { ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; @@ -1415,41 +1696,59 @@ void tape_end(ftu_t ftu) s = splbio(); /* reset, dma disable */ - outb(fdc->baseport+fdout, 0x00); + outb(fdc->baseport+FDOUT, 0x00); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); /* raise reset, enable DMA */ - outb(fdc->baseport+fdout, FDO_FRST | FDO_FDMAEN); + outb(fdc->baseport+FDOUT, FDO_FRST | FDO_FDMAEN); (void)ftintr_wait(ftu, FTCMD_RESET, hz/10); splx(s); /* set transfer speed */ - outb(fdc->baseport+fdctl, FDC_500KBPS); + outb(fdc->baseport+FDCTL, FDC_500KBPS); DELAY(10); fdc->flags &= ~FDC_TAPE_BUSY; DPRT(("tape_end end\n")); } + /* * Wait for the driver to go inactive, cancel readahead if necessary. */ -void tape_inactive(ftu_t ftu) +static void +tape_inactive(ftu_t ftu) { ft_p ft = &ft_data[ftu]; - - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + int s = splbio(); + + if (ft->segh != NULL) { + if (ft->segh->reqtype == FTIO_RDAHEAD) { + /* cancel read-ahead */ + ft->segh->reqcan = 1; + } else if (ft->segh->reqtype == FTIO_WRITING && !ft->active) { + /* flush out any remaining writes */ + DPRT(("Flushing write I/O chain\n")); + arq_state = ard_state = awr_state = 0; + ft->xblk = ft->segh->reqblk; + ft->xseg = ft->segh->reqseg; + ft->xcnt = 0; + ft->xptr = ft->segh->buff; + ft->active = 1; + timeout(ft_timeout, (caddr_t)ftu, 1); + } } - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + while (ft->active) ftsleep(wc_iosts_change, 0); + splx(s); } + /* * Get the geometry of the tape currently in the drive. */ -int ftgetgeom(ftu_t ftu) +static int +ftgetgeom(ftu_t ftu) { int r, i, tries; int cfg, qic80, ext; @@ -1459,7 +1758,7 @@ int ftgetgeom(ftu_t ftu) r = tape_status(ftu); /* XXX fix me when format mode is finished */ - if ((r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { + if (r < 0 || (r & QS_CART) == 0 || (r & QS_FMTOK) == 0) { DPRT(("ftgetgeom: no cart or not formatted 0x%04x\n",r)); ftg = NULL; ft->newcart = 1; @@ -1539,20 +1838,21 @@ int ftgetgeom(ftu_t ftu) return(0); } + /* * Switch between tape/floppy. This will send the tape enable/disable * codes for this drive's manufacturer. */ -int set_fdcmode(dev_t dev, int newmode) +static int +set_fdcmode(dev_t dev, int newmode) { ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; fdc_p fdc = ft->fdc; - static int havebufs = 0; void *buf; int r, s, i; - SegReq *sp; + SegReq *sp, *rsp; if (newmode == FDC_TAPE_MODE) { /* Wake up the tape drive */ @@ -1560,19 +1860,22 @@ int set_fdcmode(dev_t dev, int newmode) case NO_TYPE: fdc->flags &= ~FDC_TAPE_BUSY; return(ENXIO); + case FT_NONE: + tape_start(ftu, 0); + break; case FT_COLORADO: - tape_start(ftu); + tape_start(ftu, 0); if (tape_cmd(ftu, QC_COL_ENABLE1)) { tape_end(ftu); return(EIO); } - if (tape_cmd(ftu, QC_COL_ENABLE2)) { + if (tape_cmd(ftu, QC_COL_ENABLE2 + ftu)) { tape_end(ftu); return(EIO); } break; case FT_MOUNTAIN: - tape_start(ftu); + tape_start(ftu, 0); if (tape_cmd(ftu, QC_MTN_ENABLE1)) { tape_end(ftu); return(EIO); @@ -1582,56 +1885,93 @@ int set_fdcmode(dev_t dev, int newmode) return(EIO); } break; + case FT_INSIGHT: + tape_start(ftu, 1); + break; default: DPRT(("ft%d: bad tape type\n", ftu)); return(ENXIO); } if (tape_status(ftu) < 0) { - tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); tape_end(ftu); return(EIO); } /* Grab buffers from memory. */ if (!havebufs) { - ft->curseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); - if (ft->curseg == NULL) { - printf("ft%d: not enough memory for buffers\n", ftu); - return(ENOMEM); - } - ft->bufseg = malloc(sizeof(SegReq), M_DEVBUF, M_NOWAIT); - if (ft->bufseg == NULL) { - free(ft->curseg, M_DEVBUF); - printf("ft%d: not enough memory for buffers\n", ftu); - return(ENOMEM); + ft->segh = ft->segt = NULL; + ft->doneh = ft->donet = NULL; + ft->segfree = NULL; + ft->hdr = NULL; + ft->nsegq = ft->ndoneq = ft->nfreelist = 0; + for (i = 0; i < FTNBUFF; i++) { + sp = malloc(sizeof(SegReq), M_DEVBUF, M_WAITOK); + if (sp == NULL) { + printf("ft%d: not enough memory for buffers\n", ftu); + for (sp=ft->segfree; sp != NULL; sp=sp->next) + free(sp, M_DEVBUF); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); + tape_end(ftu); + return(ENOMEM); + } + sp->reqtype = FTIO_READY; + sp->next = ft->segfree; + ft->segfree = sp; + ft->nfreelist++; } + /* take one buffer for header */ + ft->hdr = ft->segfree; + ft->segfree = ft->segfree->next; + ft->nfreelist--; havebufs = 1; } - ft->curseg->reqtype = FTIO_READY; - ft->bufseg->reqtype = FTIO_READY; ft->io_sts = FTIO_READY; /* tape drive is ready */ ft->active = 0; /* interrupt driver not active */ ft->moving = 0; /* tape not moving */ ft->rdonly = 0; /* tape read only */ - ft->newcart = 0; /* a new cart was inserted */ + ft->newcart = 0; /* new cartridge flag */ ft->lastpos = -1; /* tape is rewound */ + async_func = ACMD_NONE; /* No async function */ tape_state(ftu, 0, QS_READY, 60); tape_cmd(ftu, QC_RATE); tape_cmd(ftu, QCF_RT500+2); /* 500K bps */ tape_state(ftu, 0, QS_READY, 60); ft->mode = FTM_PRIMARY; - tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */ + tape_cmd(ftu, QC_PRIMARY); /* Make sure we're in primary mode */ tape_state(ftu, 0, QS_READY, 60); ftg = NULL; /* No geometry yet */ ftgetgeom(ftu); /* Get tape geometry */ ftreq_rewind(ftu); /* Make sure tape is rewound */ } else { - tape_cmd(ftu, (ft->type == FT_COLORADO) ? QC_COL_DISABLE : QC_MTN_DISABLE); + if (ft->type == FT_COLORADO) + tape_cmd(ftu, QC_COL_DISABLE); + else if (ft->type == FT_MOUNTAIN) + tape_cmd(ftu, QC_MTN_DISABLE); tape_end(ftu); ft->newcart = 0; /* clear new cartridge */ + if (ft->hdr != NULL) free(ft->hdr, M_DEVBUF); + if (havebufs) { + for (sp = ft->segfree; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + for (sp = ft->segh; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + for (sp = ft->doneh; sp != NULL;) { + rsp = sp; sp = sp->next; + free(rsp, M_DEVBUF); + } + } havebufs = 0; - free(ft->curseg, M_DEVBUF); - free(ft->bufseg, M_DEVBUF); } return(0); } @@ -1640,7 +1980,8 @@ int set_fdcmode(dev_t dev, int newmode) /* * Perform a QIC status function. */ -int qic_status(ftu_t ftu, int cmd, int nbits) +static int +qic_status(ftu_t ftu, int cmd, int nbits) { int st3, val, r, i; ft_p ft = &ft_data[ftu]; @@ -1653,7 +1994,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits) /* Sense drive status */ out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if ((st3 & 0x10) == 0) { /* track 0 */ @@ -1668,7 +2009,7 @@ int qic_status(ftu_t ftu, int cmd, int nbits) } out_fdc(fdcu, NE7CMD_SENSED); - out_fdc(fdcu, 0x00); + out_fdc(fdcu, ftu); st3 = in_fdc(fdcu); if (st3 < 0) { DPRT(("ft%d: controller timed out on bit %d r=$%02x\n", @@ -1690,11 +2031,13 @@ int qic_status(ftu_t ftu, int cmd, int nbits) return(r); } + /* * Open tape drive for use. Bounced off of Fdopen if tape minor is * detected. */ -int ftopen(dev_t dev, int arg2) { +int +ftopen(dev_t dev, int arg2) { ftu_t ftu = FDUNIT(minor(dev)); int type = FDTYPE(minor(dev)); fdc_p fdc; @@ -1714,19 +2057,21 @@ int ftopen(dev_t dev, int arg2) { return(set_fdcmode(dev, FDC_TAPE_MODE)); /* try to switch to tape */ } + /* * Close tape and return floppy controller to disk mode. */ -int ftclose(dev_t dev, int flags) +int +ftclose(dev_t dev, int flags) { int s; SegReq *sp; ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; + /* Wait for any remaining I/O activity to complete. */ - if (ft->curseg->reqtype == FTIO_RDAHEAD) ft->curseg->reqcan = 1; - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); ft->mode = FTM_PRIMARY; tape_cmd(ftu, QC_PRIMARY); @@ -1735,94 +2080,124 @@ int ftclose(dev_t dev, int flags) return(set_fdcmode(dev, FDC_DISK_MODE)); /* Otherwise, close tape */ } + /* - * Perform strategy on a given buffer (not!). The driver was not - * performing very efficiently using the buffering routines. After - * support for error correction was added, this routine became - * obsolete in favor of doing ioctl's. Ugly, yes. + * Perform strategy on a given buffer (not!). Changed so that the + * driver will at least return 'Operation not supported'. */ -void ftstrategy(struct buf *bp) +void +ftstrategy(struct buf *bp) { - return; + bp->b_error = ENODEV; + bp->b_flags |= B_ERROR; + biodone(bp); } -/* Read or write a segment. */ -int ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p) + +/* + * Read or write a segment. + */ +static int +ftreq_rw(ftu_t ftu, int cmd, QIC_Segment *sr, struct proc *p) { int r, i, j; SegReq *sp; int s; - long blk, bad; + long blk, bad, seg; unsigned char *cp, *cp2; ft_p ft = &ft_data[ftu]; - if (!ft->active) { + if (!ft->active && ft->segh == NULL) { r = tape_status(ftu); - if ((r & QS_CART) == 0) { + if ((r & QS_CART) == 0) return(ENXIO); /* No cartridge */ - } - if ((r & QS_FMTOK) == 0) { + if ((r & QS_FMTOK) == 0) return(ENXIO); /* Not formatted */ - } tape_state(ftu, 0, QS_READY, 90); } if (ftg == NULL || ft->newcart) { - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); tape_state(ftu, 0, QS_READY, 90); - if (ftgetgeom(ftu) < 0) { + if (ftgetgeom(ftu) < 0) return(ENXIO); - } } /* Write not allowed on a read-only tape. */ - if (cmd == QIOWRITE && ft->rdonly) { + if (cmd == QIOWRITE && ft->rdonly) return(EROFS); - } + /* Quick check of request and buffer. */ - if (sr == NULL || sr->sg_data == NULL) { + if (sr == NULL || sr->sg_data == NULL) return(EINVAL); - } - if (sr->sg_trk >= ftg->g_trktape || - sr->sg_seg >= ftg->g_segtrk) { + + /* Make sure requested track and segment is in range. */ + if (sr->sg_trk >= ftg->g_trktape || sr->sg_seg >= ftg->g_segtrk) return(EINVAL); - } + blk = sr->sg_trk * ftg->g_blktrk + sr->sg_seg * QCV_BLKSEG; + seg = sr->sg_trk * ftg->g_segtrk + sr->sg_seg; s = splbio(); if (cmd == QIOREAD) { - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - if (blk == ft->curseg->reqblk) { - sp = ft->curseg; + /* + * See if the driver is reading ahead. + */ + if (ft->doneh != NULL || + (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD)) { + /* + * Eat the completion queue and see if the request + * is already there. + */ + while (ft->doneh != NULL) { + if (blk == ft->doneh->reqblk) { + sp = ft->doneh; + sp->reqtype = FTIO_READING; + sp->reqbad = sr->sg_badmap; + goto rddone; + } + segio_free(ft, ft->doneh); + } + + /* + * Not on the completed queue, in progress maybe? + */ + if (ft->segh != NULL && ft->segh->reqtype == FTIO_RDAHEAD && + blk == ft->segh->reqblk) { + sp = ft->segh; sp->reqtype = FTIO_READING; sp->reqbad = sr->sg_badmap; goto rdwait; - } else - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ + } } /* Wait until we're ready. */ - while (ft->active) sleep((caddr_t)&ftsem.iosts_change, FTPRI); + tape_inactive(ftu); /* Set up a new read request. */ - sp = ft->curseg; + sp = segio_alloc(ft); sp->reqcrc = 0; sp->reqbad = sr->sg_badmap; sp->reqblk = blk; + sp->reqseg = seg; sp->reqcan = 0; sp->reqtype = FTIO_READING; + segio_queue(ft, sp); /* Start the read request off. */ DPRT(("Starting read I/O chain\n")); arq_state = ard_state = awr_state = 0; ft->xblk = sp->reqblk; + ft->xseg = sp->reqseg; ft->xcnt = 0; ft->xptr = sp->buff; ft->active = 1; timeout(ft_timeout, (caddr_t)ftu, 1); rdwait: - sleep((caddr_t)&ftsem.buff_avail, FTPRI); + ftsleep(wc_buff_done, 0); + +rddone: bad = sp->reqbad; sr->sg_crcmap = sp->reqcrc & ~bad; @@ -1833,17 +2208,29 @@ rdwait: copyout(cp, cp2, QCV_BLKSIZE); cp2 += QCV_BLKSIZE; } + segio_free(ft, sp); } else { - if (ft->curseg->reqtype == FTIO_RDAHEAD) { - ft->curseg->reqcan = 1; /* XXX cancel rdahead */ - while (ft->active) - sleep((caddr_t)&ftsem.iosts_change, FTPRI); + if (ft->segh != NULL && ft->segh->reqtype != FTIO_WRITING) + tape_inactive(ftu); + + /* Allocate a buffer and start tape if we're running low. */ + sp = segio_alloc(ft); + if (!ft->active && (sp == NULL || ft->nfreelist <= 1)) { + DPRT(("Starting write I/O chain\n")); + arq_state = ard_state = awr_state = 0; + ft->xblk = ft->segh->reqblk; + ft->xseg = ft->segh->reqseg; + ft->xcnt = 0; + ft->xptr = ft->segh->buff; + ft->active = 1; + timeout(ft_timeout, (caddr_t)ftu, 1); } /* Sleep until a buffer becomes available. */ - while (ft->bufseg->reqtype != FTIO_READY) - sleep((caddr_t)&ftsem.buff_avail, FTPRI); - sp = (ft->curseg->reqtype == FTIO_READY) ? ft->curseg : ft->bufseg; + while (sp == NULL) { + ftsleep(wc_buff_avail, 0); + sp = segio_alloc(ft); + } /* Copy in segment and expand bad blocks. */ bad = sr->sg_badmap; @@ -1853,28 +2240,22 @@ rdwait: copyin(cp, cp2, QCV_BLKSIZE); cp += QCV_BLKSIZE; } - sp->reqblk = blk; + sp->reqseg = seg; sp->reqcan = 0; sp->reqtype = FTIO_WRITING; - - if (!ft->active) { - DPRT(("Starting write I/O chain\n")); - arq_state = ard_state = awr_state = 0; - ft->xblk = sp->reqblk; - ft->xcnt = 0; - ft->xptr = sp->buff; - ft->active = 1; - timeout(ft_timeout, (caddr_t)ftu, 1); - } + segio_queue(ft, sp); } splx(s); return(0); } -/* Rewind to beginning of tape */ -int ftreq_rewind(ftu_t ftu) +/* + * Rewind to beginning of tape + */ +static int +ftreq_rewind(ftu_t ftu) { ft_p ft = &ft_data[ftu]; @@ -1891,8 +2272,12 @@ int ftreq_rewind(ftu_t ftu) return(0); } -/* Move to logical beginning or end of track */ -int ftreq_trkpos(ftu_t ftu, int req) + +/* + * Move to logical beginning or end of track + */ +static int +ftreq_trkpos(ftu_t ftu, int req) { int curtrk, r, cmd; ft_p ft = &ft_data[ftu]; @@ -1919,8 +2304,12 @@ int ftreq_trkpos(ftu_t ftu, int req) return(0); } -/* Seek tape head to a particular track. */ -int ftreq_trkset(ftu_t ftu, int *trk) + +/* + * Seek tape head to a particular track. + */ +static int +ftreq_trkset(ftu_t ftu, int *trk) { int curtrk, r, cmd; ft_p ft = &ft_data[ftu]; @@ -1942,27 +2331,45 @@ int ftreq_trkset(ftu_t ftu, int *trk) return(0); } -/* Start tape moving forward. */ -int ftreq_lfwd(ftu_t ftu) + +/* + * Start tape moving forward. + */ +static int +ftreq_lfwd(ftu_t ftu) { + ft_p ft = &ft_data[ftu]; + tape_inactive(ftu); tape_cmd(ftu, QC_STOP); tape_state(ftu, 0, QS_READY, 90); tape_cmd(ftu, QC_FORWARD); + ft->moving = 1; return(0); } -/* Stop the tape */ -int ftreq_stop(ftu_t ftu) + +/* + * Stop the tape + */ +static int +ftreq_stop(ftu_t ftu) { + ft_p ft = &ft_data[ftu]; + tape_inactive(ftu); tape_cmd(ftu, QC_STOP); tape_state(ftu, 0, QS_READY, 90); + ft->moving = 0; return(0); } -/* Set the particular mode the drive should be in. */ -int ftreq_setmode(ftu_t ftu, int cmd) + +/* + * Set the particular mode the drive should be in. + */ +static int +ftreq_setmode(ftu_t ftu, int cmd) { int r; ft_p ft = &ft_data[ftu]; @@ -1989,8 +2396,12 @@ int ftreq_setmode(ftu_t ftu, int cmd) return(0); } -/* Return drive status bits */ -int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) + +/* + * Return drive status bits + */ +static int +ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) { ft_p ft = &ft_data[ftu]; @@ -2001,8 +2412,12 @@ int ftreq_status(ftu_t ftu, int cmd, int *sts, struct proc *p) return(0); } -/* Return drive configuration bits */ -int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) + +/* + * Return drive configuration bits + */ +static int +ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) { int r, tries; ft_p ft = &ft_data[ftu]; @@ -2018,8 +2433,12 @@ int ftreq_config(ftu_t ftu, int cmd, int *cfg, struct proc *p) return(0); } -/* Return current tape's geometry. */ -int ftreq_geom(ftu_t ftu, QIC_Geom *g) + +/* + * Return current tape's geometry. + */ +static int +ftreq_geom(ftu_t ftu, QIC_Geom *g) { tape_inactive(ftu); if (ftg == NULL && ftgetgeom(ftu) < 0) return(ENXIO); @@ -2027,8 +2446,12 @@ int ftreq_geom(ftu_t ftu, QIC_Geom *g) return(0); } -/* Return drive hardware information */ -int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) + +/* + * Return drive hardware information + */ +static int +ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) { int r, tries; int rom, vend; @@ -2053,10 +2476,31 @@ int ftreq_hwinfo(ftu_t ftu, QIC_HWInfo *hwp) return(0); } + +/* + * Receive or Send the in-core header segment. + */ +static int +ftreq_hdr(ftu_t ftu, int cmd, QIC_Segment *sp) +{ + ft_p ft = &ft_data[ftu]; + QIC_Header *h = (QIC_Header *)ft->hdr->buff; + + if (sp == NULL || sp->sg_data == NULL) return(EINVAL); + if (cmd == QIOSENDHDR) { + copyin(sp->sg_data, ft->hdr->buff, QCV_SEGSIZE); + } else { + if (h->qh_sig != QCV_HDRMAGIC) return(EIO); + copyout(ft->hdr->buff, sp->sg_data, QCV_SEGSIZE); + } + return(0); +} + /* * I/O functions. */ -int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +int +ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) { ftu_t ftu = FDUNIT(minor(dev)); ft_p ft = &ft_data[ftu]; @@ -2104,20 +2548,30 @@ int ftioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) case QIOHWINFO: return(ftreq_hwinfo(ftu, (QIC_HWInfo *)data)); + + case QIOSENDHDR: + case QIORECVHDR: + return(ftreq_hdr(ftu, cmd, (QIC_Segment *)data)); } badreq: DPRT(("ft%d: unknown ioctl(%d) request\n", ftu, cmd)); return(ENXIO); } -/* Not implemented */ -int ftdump(dev_t dev) +/* + * Not implemented + */ +int +ftdump(dev_t dev) { return(EINVAL); } -/* Not implemented */ -int ftsize(dev_t dev) +/* + * Not implemented + */ +int +ftsize(dev_t dev) { return(EINVAL); } diff --git a/sys/i386/isa/ftreg.h b/sys/i386/isa/ftreg.h index 7b4ca6a27236..c29540ce0a96 100644 --- a/sys/i386/isa/ftreg.h +++ b/sys/i386/isa/ftreg.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 1993 Steve Gerakines + * Copyright (c) 1993, 1994 Steve Gerakines * * This is freely redistributable software. You may do anything you * wish with it, so long as the above notice stays intact. @@ -17,6 +17,9 @@ * POSSIBILITY OF SUCH DAMAGE. * * ftreg.h - QIC-40/80 floppy tape driver header + * 06/03/94 v0.9 + * Changed seek load point to QC_SEEKLP, added reqseg to SegReq structure. + * * 10/30/93 v0.3 * More things will end up here. QC_VENDORID and QC_VERSION now used. * @@ -42,7 +45,7 @@ #define QC_SEEKSTART 11 /* seek to track start */ #define QC_SEEKEND 12 /* seek to track end */ #define QC_SEEKTRACK 13 /* seek head to track */ -#define QC_SEEKLOAD 14 /* seek load point */ +#define QC_SEEKLP 14 /* seek load point */ #define QC_FORMAT 15 /* format mode */ #define QC_WRITEREF 16 /* write reference */ #define QC_VERIFY 17 /* verify mode */ @@ -62,7 +65,7 @@ /* Colorado enable/disable. */ #define QC_COL_ENABLE1 46 /* enable */ -#define QC_COL_ENABLE2 2 /* null-op */ +#define QC_COL_ENABLE2 2 /* unit+2 */ #define QC_COL_DISABLE 47 /* disable */ /* Mountain enable/disable. */ @@ -77,5 +80,7 @@ typedef struct segq { long reqcrc; /* CRC Errors found */ long reqbad; /* Bad sector map */ long reqblk; /* Block request starts at */ + long reqseg; /* Segment request is at */ int reqcan; /* Cancel read-ahead */ + struct segq *next; /* Next request */ } SegReq; diff --git a/sys/i386/isa/ic/i82365.h b/sys/i386/isa/ic/i82365.h new file mode 100644 index 000000000000..ab381250ea72 --- /dev/null +++ b/sys/i386/isa/ic/i82365.h @@ -0,0 +1,190 @@ +#ifndef __83265_H__ +#define __83265_H__ + +/*********************************************************************** + * 82365.h -- information necessary for direct manipulation of PCMCIA + * cards and controllers + * + * Support is included for Intel 82365SL PCIC controllers and clones + * thereof. + * + * originally by Barry Jaspan; hacked over by Keith Moore + * + ***********************************************************************/ + +/* + * PCIC Registers + * Each register is given a name, and most of the bits are named too. + * I should really name them all. + * + * Finally, since the banks can be addressed with a regular syntax, + * some macros are provided for that purpose. + */ + +#define PCIC_BASE 0x03e0 /* base adddress of pcic register set */ + +/* First, all the registers */ +#define PCIC_ID_REV 0x00 /* Identification and Revision */ +#define PCIC_STATUS 0x01 /* Interface Status */ +#define PCIC_POWER 0x02 /* Power and RESETDRV control */ +#define PCIC_INT_GEN 0x03 /* Interrupt and General Control */ +#define PCIC_STAT_CHG 0x04 /* Card Status Change */ +#define PCIC_STAT_INT 0x05 /* Card Status Change Interrupt Config */ +#define PCIC_ADDRWINE 0x06 /* Address Window Enable */ +#define PCIC_IOCTL 0x07 /* I/O Control */ +#define PCIC_IO0_STL 0x08 /* I/O Address 0 Start Low Byte */ +#define PCIC_IO0_STH 0x09 /* I/O Address 0 Start High Byte */ +#define PCIC_IO0_SPL 0x0a /* I/O Address 0 Stop Low Byte */ +#define PCIC_IO0_SPH 0x0b /* I/O Address 0 Stop High Byte */ +#define PCIC_IO1_STL 0x0c /* I/O Address 1 Start Low Byte */ +#define PCIC_IO1_STH 0x0d /* I/O Address 1 Start High Byte */ +#define PCIC_IO1_SPL 0x0e /* I/O Address 1 Stop Low Byte */ +#define PCIC_IO1_SPH 0x0f /* I/O Address 1 Stop High Byte */ +#define PCIC_SM0_STL 0x10 /* System Memory Address 0 Mapping Start Low Byte */ +#define PCIC_SM0_STH 0x11 /* System Memory Address 0 Mapping Start High Byte */ +#define PCIC_SM0_SPL 0x12 /* System Memory Address 0 Mapping Stop Low Byte */ +#define PCIC_SM0_SPH 0x13 /* System Memory Address 0 Mapping Stop High Byte */ +#define PCIC_CM0_L 0x14 /* Card Memory Offset Address 0 Low Byte */ +#define PCIC_CM0_H 0x15 /* Card Memory Offset Address 0 High Byte */ +#define PCIC_CDGC 0x16 /* Card Detect and General Control */ +#define PCIC_RES17 0x17 /* Reserved */ +#define PCIC_SM1_STL 0x18 /* System Memory Address 1 Mapping Start Low Byte */ +#define PCIC_SM1_STH 0x19 /* System Memory Address 1 Mapping Start High Byte */ +#define PCIC_SM1_SPL 0x1a /* System Memory Address 1 Mapping Stop Low Byte */ +#define PCIC_SM1_SPH 0x1b /* System Memory Address 1 Mapping Stop High Byte */ +#define PCIC_CM1_L 0x1c /* Card Memory Offset Address 1 Low Byte */ +#define PCIC_CM1_H 0x1d /* Card Memory Offset Address 1 High Byte */ +#define PCIC_GLO_CTRL 0x1e /* Global Control Register */ +#define PCIC_RES1F 0x1f /* Reserved */ +#define PCIC_SM2_STL 0x20 /* System Memory Address 2 Mapping Start Low Byte */ +#define PCIC_SM2_STH 0x21 /* System Memory Address 2 Mapping Start High Byte */ +#define PCIC_SM2_SPL 0x22 /* System Memory Address 2 Mapping Stop Low Byte */ +#define PCIC_SM2_SPH 0x23 /* System Memory Address 2 Mapping Stop High Byte */ +#define PCIC_CM2_L 0x24 /* Card Memory Offset Address 2 Low Byte */ +#define PCIC_CM2_H 0x25 /* Card Memory Offset Address 2 High Byte */ +#define PCIC_RES26 0x26 /* Reserved */ +#define PCIC_RES27 0x27 /* Reserved */ +#define PCIC_SM3_STL 0x28 /* System Memory Address 3 Mapping Start Low Byte */ +#define PCIC_SM3_STH 0x29 /* System Memory Address 3 Mapping Start High Byte */ +#define PCIC_SM3_SPL 0x2a /* System Memory Address 3 Mapping Stop Low Byte */ +#define PCIC_SM3_SPH 0x2b /* System Memory Address 3 Mapping Stop High Byte */ +#define PCIC_CM3_L 0x2c /* Card Memory Offset Address 3 Low Byte */ +#define PCIC_CM3_H 0x2d /* Card Memory Offset Address 3 High Byte */ +#define PCIC_RES2E 0x2e /* Reserved */ +#define PCIC_RES2F 0x2f /* Reserved */ +#define PCIC_SM4_STL 0x30 /* System Memory Address 4 Mapping Start Low Byte */ +#define PCIC_SM4_STH 0x31 /* System Memory Address 4 Mapping Start High Byte */ +#define PCIC_SM4_SPL 0x32 /* System Memory Address 4 Mapping Stop Low Byte */ +#define PCIC_SM4_SPH 0x33 /* System Memory Address 4 Mapping Stop High Byte */ +#define PCIC_CM4_L 0x34 /* Card Memory Offset Address 4 Low Byte */ +#define PCIC_CM4_H 0x35 /* Card Memory Offset Address 4 High Byte */ +#define PCIC_RES36 0x36 /* Reserved */ +#define PCIC_RES37 0x37 /* Reserved */ +#define PCIC_RES38 0x38 /* Reserved */ +#define PCIC_RES39 0x39 /* Reserved */ +#define PCIC_RES3A 0x3a /* Reserved */ +#define PCIC_RES3B 0x3b /* Reserved */ +#define PCIC_RES3C 0x3c /* Reserved */ +#define PCIC_RES3D 0x3d /* Reserved */ +#define PCIC_RES3E 0x3e /* Reserved */ +#define PCIC_RES3F 0x3f /* Reserved */ + +/* Now register bits, ordered by reg # */ + +/* For Identification and Revision (PCIC_ID_REV) */ +#define PCIC_INTEL0 0x82 /* Intel 82365SL Rev. 0; Both Memory and I/O */ +#define PCIC_INTEL1 0x83 /* Intel 82365SL Rev. 1; Both Memory and I/O */ +#define PCIC_IBM1 0x88 /* IBM PCIC clone; Both Memory and I/O */ +#define PCIC_IBM2 0x89 /* IBM PCIC clone; Both Memory and I/O */ + +/* For Interface Status register (PCIC_STATUS) */ +#define PCIC_VPPV 0x80 /* Vpp_valid */ +#define PCIC_POW 0x40 /* PC Card power active */ +#define PCIC_READY 0x20 /* Ready/~Busy */ +#define PCIC_MWP 0x10 /* Memory Write Protect */ +#define PCIC_CD 0x0C /* Both card detect bits */ +#define PCIC_BVD 0x03 /* Both Battery Voltage Detect bits */ + +/* For the Power and RESETDRV register (PCIC_POWER) */ +#define PCIC_OUTENA 0x80 /* Output Enable */ +#define PCIC_DISRST 0x40 /* Disable RESETDRV */ +#define PCIC_APSENA 0x20 /* Auto Pwer Switch Enable */ +#define PCIC_PCPWRE 0x10 /* PC Card Power Enable */ + +/* For the Interrupt and General Control register (PCIC_INT_GEN) */ +#define PCIC_CARDTYPE 0x20 /* Card Type 0 = memory, 1 = I/O */ +#define PCIC_IOCARD 0x20 +#define PCIC_MEMCARD 0x00 +#define PCIC_CARDRESET 0x40 /* Card reset 0 = Reset, 1 = Normal */ + +/* For the Card Status Change register (PCIC_STAT_CHG) */ +#define PCIC_CDTCH 0x08 /* Card Detect Change */ +#define PCIC_RDYCH 0x04 /* Ready Change */ +#define PCIC_BATWRN 0x02 /* Battery Warning */ +#define PCIC_BATDED 0x01 /* Battery Dead */ + +/* For the Address Window Enable Register (PCIC_ADDRWINE) */ +#define PCIC_SM0_EN 0x01 /* Memory Window 0 Enable */ +#define PCIC_SM1_EN 0x02 /* Memory Window 1 Enable */ +#define PCIC_SM2_EN 0x04 /* Memory Window 2 Enable */ +#define PCIC_SM3_EN 0x08 /* Memory Window 3 Enable */ +#define PCIC_SM4_EN 0x10 /* Memory Window 4 Enable */ +#define PCIC_MEMCS16 0x20 /* ~MEMCS16 Decode A23-A12 */ +#define PCIC_IO0_EN 0x40 /* I/O Window 0 Enable */ +#define PCIC_IO1_EN 0x80 /* I/O Window 1 Enable */ + +/* For the I/O Control Register (PCIC_IOCTL) */ +#define PCIC_IO0_16BIT 0x01 /* I/O to this segment is 16 bit */ +#define PCIC_IO0_CS16 0x02 /* I/O cs16 source is the card */ +#define PCIC_IO0_0WS 0x04 /* zero wait states added on 8 bit cycles */ +#define PCIC_IO0_WS 0x08 /* Wait states added for 16 bit cycles */ +#define PCIC_IO1_16BIT 0x10 /* I/O to this segment is 16 bit */ +#define PCIC_IO1_CS16 0x20 /* I/O cs16 source is the card */ +#define PCIC_IO1_0WS 0x04 /* zero wait states added on 8 bit cycles */ +#define PCIC_IO1_WS 0x80 /* Wait states added for 16 bit cycles */ + +/* For the various I/O and Memory windows */ +#define PCIC_ADDR_LOW 0 +#define PCIC_ADDR_HIGH 1 +#define PCIC_START 0x00 /* Start of mapping region */ +#define PCIC_END 0x02 /* End of mapping region */ +#define PCIC_MOFF 0x04 /* Card Memory Mapping region offset */ +#define PCIC_IO0 0x08 /* I/O Address 0 */ +#define PCIC_IO1 0x0c /* I/O Address 1 */ +#define PCIC_SM0 0x10 /* System Memory Address 0 Mapping */ +#define PCIC_SM1 0x18 /* System Memory Address 1 Mapping */ +#define PCIC_SM2 0x20 /* System Memory Address 2 Mapping */ +#define PCIC_SM3 0x28 /* System Memory Address 3 Mapping */ +#define PCIC_SM4 0x30 /* System Memory Address 4 Mapping */ + +/* For System Memory Window start registers + (PCIC_SMx|PCIC_START|PCIC_ADDR_HIGH) */ +#define PCIC_ZEROWS 0x40 /* Zero wait states */ +#define PCIC_DATA16 0x80 /* Data width is 16 bits */ + +/* For System Memory Window stop registers + (PCIC_SMx|PCIC_END|PCIC_ADDR_HIGH) */ +#define PCIC_MW0 0x40 /* Wait state bit 0 */ +#define PCIC_MW1 0x80 /* Wait state bit 1 */ + +/* For System Memory Window offset registers + (PCIC_SMx|PCIC_MOFF|PCIC_ADDR_HIGH) */ +#define PCIC_REG 0x40 /* Attribute/Common select (why called Reg?) */ +#define PCIC_WP 0x80 /* Write-protect this window */ + +/* For Card Detect and General Control register (PCIC_CDGC) */ +#define PCIC_16_DL_INH 0x01 /* 16-bit memory delay inhibit */ +#define PCIC_CNFG_RST_EN 0x02 /* configuration reset enable */ +#define PCIC_GPI_EN 0x04 /* GPI Enable */ +#define PCIC_GPI_TRANS 0x08 /* GPI Transition Control */ +#define PCIC_CDRES_EN 0x10 /* card detect resume enable */ +#define PCIC_SW_CD_INT 0x20 /* s/w card detect interrupt */ + +/* For Global Control register (PCIC_GLO_CTRL) */ +#define PCIC_PWR_DOWN 0x01 /* power down */ +#define PCIC_LVL_MODE 0x02 /* level mode interrupt enable */ +#define PCIC_WB_CSCINT 0x04 /* explicit write-back csc intr */ +#define PCIC_IRQ14_PULSE 0x08 /* irq 14 pulse mode enable */ + +/* DON'T ADD ANYTHING AFTER THIS #endif */ +#endif /* __83265_H__ */ diff --git a/sys/i386/isa/ic/nec765.h b/sys/i386/isa/ic/nec765.h index df6c337482aa..c02e6ef362ba 100644 --- a/sys/i386/isa/ic/nec765.h +++ b/sys/i386/isa/ic/nec765.h @@ -30,8 +30,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * from: @(#)nec765.h 7.1 (Berkeley) 5/9/91 - * $Id: nec765.h,v 1.2 1993/10/16 13:48:50 rgrimes Exp $ + * @(#)nec765.h 7.1 (Berkeley) 5/9/91 */ /* @@ -47,26 +46,90 @@ #define NE7_RQM 0x80 /* Diskette Controller ReQuest for Master */ /* Status register ST0 */ -#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005drv_chck\004drive_rdy\003top_head" +#define NE7_ST0BITS "\020\010invld\007abnrml\006seek_cmplt\005equ_chck\004drive_notrdy\003top_head" + +#define NE7_ST0_IC 0xc0 /* interrupt completion code */ + +#define NE7_ST0_IC_RC 0xc0 /* terminated due to ready changed, n/a */ +#define NE7_ST0_IC_IV 0x80 /* invalid command; must reset FDC */ +#define NE7_ST0_IC_AT 0x40 /* abnormal termination, check error stat */ +#define NE7_ST0_IC_NT 0x00 /* normal termination */ + +#define NE7_ST0_SE 0x20 /* seek end */ +#define NE7_ST0_EC 0x10 /* equipment check, recalibrated but no trk0 */ +#define NE7_ST0_NR 0x08 /* not ready (n/a) */ +#define NE7_ST0_HD 0x04 /* upper head selected */ +#define NE7_ST0_DR 0x03 /* drive code */ /* Status register ST1 */ #define NE7_ST1BITS "\020\010end_of_cyl\006bad_crc\005data_overrun\003sec_not_fnd\002write_protect\001no_am" +#define NE7_ST1_EN 0x80 /* end of cylinder, access past last record */ +#define NE7_ST1_DE 0x20 /* data error, CRC fail in ID or data */ +#define NE7_ST1_OR 0x10 /* DMA overrun, DMA failed to do i/o quickly */ +#define NE7_ST1_ND 0x04 /* no data, sector not found or CRC in ID f. */ +#define NE7_ST1_NW 0x02 /* not writeable, attempt to violate WP */ +#define NE7_ST1_MA 0x01 /* missing address mark (in ID or data field)*/ + /* Status register ST2 */ #define NE7_ST2BITS "\020\007ctrl_mrk\006bad_crc\005wrong_cyl\004scn_eq\003scn_not_fnd\002bad_cyl\001no_dam" +#define NE7_ST2_CM 0x40 /* control mark; found deleted data */ +#define NE7_ST2_DD 0x20 /* data error in data field, CRC fail */ +#define NE7_ST2_WC 0x10 /* wrong cylinder, ID field mismatches cmd */ +#define NE7_ST2_SH 0x08 /* scan equal hit */ +#define NE7_ST2_SN 0x04 /* scan not satisfied */ +#define NE7_ST2_BC 0x02 /* bad cylinder, cylinder marked 0xff */ +#define NE7_ST2_MD 0x01 /* missing address mark in data field */ + /* Status register ST3 */ #define NE7_ST3BITS "\020\010fault\007write_protect\006drdy\005tk0\004two_side\003side_sel\002" +#define NE7_ST3_FT 0x80 /* fault; PC: n/a */ +#define NE7_ST3_WP 0x40 /* write protected */ +#define NE7_ST3_RD 0x20 /* ready; PC: always true */ +#define NE7_ST3_T0 0x10 /* track 0 */ +#define NE7_ST3_TS 0x08 /* two-sided; PC: n/a */ +#define NE7_ST3_HD 0x04 /* upper head select */ +#define NE7_ST3_US 0x03 /* unit select */ + /* Commands */ +/* + * the top three bits -- where appropriate -- are set as follows: + * + * 0x80 - MT multi-track; allow both sides to be handled in single cmd + * 0x40 - MFM modified frequency modulation; use MFM encoding + * 0x20 - SK skip; skip sectors marked as "deleted" + */ +#define NE7CMD_READTRK 0x42 /* read whole track */ #define NE7CMD_SPECIFY 3 /* specify drive parameters - requires unit parameters byte */ #define NE7CMD_SENSED 4 /* sense drive - requires unit select byte */ #define NE7CMD_WRITE 0xc5 /* write - requires eight additional bytes */ #define NE7CMD_READ 0xe6 /* read - requires eight additional bytes */ -#define NE7CMD_FORMAT 0x4c /* format - requires five additional bytes */ #define NE7CMD_RECAL 7 /* recalibrate drive - requires unit select byte */ #define NE7CMD_SENSEI 8 /* sense controller interrupt status */ -#define NE7CMD_SEEK 15 /* seek drive - requires unit select byte +#define NE7CMD_WRITEDEL 0xc9 /* write deleted data */ +#define NE7CMD_READID 0x4a /* read ID field */ +#define NE7CMD_READDEL 0xec /* read deleted data */ +#define NE7CMD_FORMAT 0x4d /* format - requires five additional bytes */ +#define NE7CMD_SEEK 0x0f /* seek drive - requires unit select byte and new cyl byte */ +#define NE7CMD_SCNEQU 0xf1 /* scan equal */ +#define NE7CMD_SCNLE 0xf9 /* scan less or equal */ +#define NE7CMD_SCNGE 0xfd /* scan greater or equal */ + + +/* + * "specify" definitions + * + * acronyms (times are relative to a FDC clock of 8 MHz): + * srt - step rate; PC usually 3 ms + * hut - head unload time; PC usually maximum of 240 ms + * hlt - head load time; PC usually minimum of 2 ms + * nd - no DMA flag; PC usually not set (0) + */ + +#define NE7_SPEC_1(srt, hut) (((16 - (srt)) << 4) | (((hut) / 16))) +#define NE7_SPEC_2(hlt, nd) (((hlt) & 0xFE) | ((nd) & 1)) diff --git a/sys/i386/isa/icu.h b/sys/i386/isa/icu.h index af50335f4468..523f3351a859 100644 --- a/sys/i386/isa/icu.h +++ b/sys/i386/isa/icu.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)icu.h 5.6 (Berkeley) 5/9/91 - * $Id: icu.h,v 1.2 1993/10/16 13:45:51 rgrimes Exp $ + * $Id: icu.h,v 1.3 1994/04/02 07:00:40 davidg Exp $ */ /* @@ -51,12 +51,6 @@ * Interrupt "level" mechanism variables, masks, and macros */ extern unsigned imen; /* interrupt mask enable */ -extern unsigned cpl; /* current priority level mask */ - -extern unsigned highmask; /* group of interrupts masked with splhigh() */ -extern unsigned ttymask; /* group of interrupts masked with spltty() */ -extern unsigned biomask; /* group of interrupts masked with splbio() */ -extern unsigned netmask; /* group of interrupts masked with splimp() */ #define INTREN(s) (imen &= ~(s), SET_ICUS()) #define INTRDIS(s) (imen |= (s), SET_ICUS()) @@ -74,7 +68,7 @@ extern unsigned netmask; /* group of interrupts masked with splimp() */ #endif /* - * Interrupt enable bits -- in order of priority + * Interrupt enable bits - in normal order of priority (which we change) */ #define IRQ0 0x0001 /* highest priority - timer */ #define IRQ1 0x0002 @@ -88,7 +82,7 @@ extern unsigned netmask; /* group of interrupts masked with splimp() */ #define IRQ13 0x2000 #define IRQ14 0x4000 #define IRQ15 0x8000 -#define IRQ3 0x0008 +#define IRQ3 0x0008 /* this is highest after rotation */ #define IRQ4 0x0010 #define IRQ5 0x0020 #define IRQ6 0x0040 diff --git a/sys/i386/isa/icu.s b/sys/i386/isa/icu.s index d1c843951b83..9484e5cdbedd 100644 --- a/sys/i386/isa/icu.s +++ b/sys/i386/isa/icu.s @@ -36,7 +36,7 @@ * * @(#)icu.s 7.2 (Berkeley) 5/21/91 * - * $Id: icu.s,v 1.7 1993/12/20 14:58:21 wollman Exp $ + * $Id: icu.s,v 1.8 1994/04/02 07:00:41 davidg Exp $ */ /* @@ -45,215 +45,131 @@ */ /* - * XXX - this file is now misnamed. All spls are now soft and the only thing - * related to the hardware icu is that the bit numbering is the same in the - * soft priority masks as in the hard ones. + * XXX this file should be named ipl.s. All spls are now soft and the + * only thing related to the hardware icu is that the h/w interrupt + * numbers are used without translation in the masks. */ -#include "sio.h" -#define HIGHMASK 0xffff -#define SOFTCLOCKMASK 0x8000 +#include "../net/netisr.h" .data - .globl _cpl -_cpl: .long 0xffff /* current priority (all off) */ - +_cpl: .long HWI_MASK | SWI_MASK /* current priority (all off) */ .globl _imen -_imen: .long 0xffff /* interrupt mask enable (all off) */ - -/* .globl _highmask */ -_highmask: .long HIGHMASK - - .globl _ttymask, _biomask, _netmask -_ttymask: .long 0 -_biomask: .long 0 -_netmask: .long 0 - - .globl _ipending, _astpending +_imen: .long HWI_MASK /* interrupt mask enable (all h/w off) */ +_high_imask: .long HWI_MASK | SWI_MASK + .globl _tty_imask +_tty_imask: .long 0 + .globl _bio_imask +_bio_imask: .long 0 + .globl _net_imask +_net_imask: .long 0 + .globl _ipending _ipending: .long 0 + .globl _astpending _astpending: .long 0 /* tells us an AST needs to be taken */ - .globl _netisr _netisr: .long 0 /* set with bits for which queue to service */ - vec: .long vec0, vec1, vec2, vec3, vec4, vec5, vec6, vec7 .long vec8, vec9, vec10, vec11, vec12, vec13, vec14, vec15 -#define GENSPL(name, mask, event) \ - .globl _spl/**/name ; \ - ALIGN_TEXT ; \ -_spl/**/name: ; \ - COUNT_EVENT(_intrcnt_spl, event) ; \ - movl _cpl,%eax ; \ - movl %eax,%edx ; \ - orl mask,%edx ; \ - movl %edx,_cpl ; \ - SHOW_CPL ; \ - ret - -#define FASTSPL(mask) \ - movl mask,_cpl ; \ - SHOW_CPL - -#define FASTSPL_VARMASK(varmask) \ - movl varmask,%eax ; \ - movl %eax,_cpl ; \ - SHOW_CPL - .text - ALIGN_TEXT -unpend_v: - COUNT_EVENT(_intrcnt_spl, 0) - bsfl %eax,%eax # slow, but not worth optimizing - btrl %eax,_ipending - jnc unpend_v_next # some intr cleared the in-memory bit - SHOW_IPENDING - movl Vresume(,%eax,4),%eax - testl %eax,%eax - je noresume - jmp %eax - - ALIGN_TEXT -/* - * XXX - must be some fastintr, need to register those too. - */ -noresume: -#if NSIO > 0 - call _softsio1 -#endif -unpend_v_next: - movl _cpl,%eax - movl %eax,%edx - notl %eax - andl _ipending,%eax - je none_to_unpend - jmp unpend_v - /* - * Handle return from interrupt after device handler finishes - */ - ALIGN_TEXT -doreti: - COUNT_EVENT(_intrcnt_spl, 1) - addl $4,%esp # discard unit arg - popl %eax # get previous priority -/* - * Now interrupt frame is a trap frame! - * - * XXX - setting up the interrupt frame to be almost a stack frame is mostly - * a waste of time. + * Handle return from interrupts, traps and syscalls. */ + SUPERALIGN_TEXT +_doreti: + FAKE_MCOUNT(_bintr) /* init "from" _bintr -> _doreti */ + addl $4,%esp /* discard unit number */ + popl %eax /* cpl to restore */ +doreti_next: + /* + * Check for pending HWIs and SWIs atomically with restoring cpl + * and exiting. The check has to be atomic with exiting to stop + * (ipending & ~cpl) changing from zero to nonzero while we're + * looking at it (this wouldn't be fatal but it would increase + * interrupt latency). Restoring cpl has to be atomic with exiting + * so that the stack cannot pile up (the nesting level of interrupt + * handlers is limited by the number of bits in cpl). + */ + movl %eax,%ecx + notl %ecx + cli + andl _ipending,%ecx + jne doreti_unpend +doreti_exit: movl %eax,_cpl - SHOW_CPL - movl %eax,%edx - notl %eax - andl _ipending,%eax - jne unpend_v -none_to_unpend: - testl %edx,%edx # returning to zero priority? - jne 1f # nope, going to non-zero priority - movl _netisr,%eax - testl %eax,%eax # check for softint s/traps - jne 2f # there are some - jmp test_resched # XXX - schedule jumps better - COUNT_EVENT(_intrcnt_spl, 2) # XXX - - ALIGN_TEXT # XXX -1: # XXX - COUNT_EVENT(_intrcnt_spl, 3) + MEXITCOUNT popl %es popl %ds popal addl $8,%esp iret -#include "../net/netisr.h" - -#define DONET(s, c, event) ; \ - .globl c ; \ - btrl $s,_netisr ; \ - jnc 1f ; \ - COUNT_EVENT(_intrcnt_spl, event) ; \ - call c ; \ -1: - ALIGN_TEXT -2: - COUNT_EVENT(_intrcnt_spl, 4) -/* - * XXX - might need extra locking while testing reg copy of netisr, but - * interrupt routines setting it would not cause any new problems (since we - * don't loop, fresh bits will not be processed until the next doreti or spl0). - */ - testl $~((1 << NETISR_SCLK) | (1 << NETISR_AST)),%eax - je test_ASTs # no net stuff, just temporary AST's - FASTSPL_VARMASK(_netmask) -#if 0 - DONET(NETISR_RAW, _rawintr, 5) -#endif - -#ifdef INET - DONET(NETISR_IP, _ipintr, 6) -#endif /* INET */ - -#ifdef IMP - DONET(NETISR_IMP, _impintr, 7) -#endif /* IMP */ - -#ifdef NS - DONET(NETISR_NS, _nsintr, 8) -#endif /* NS */ - -#ifdef ISO - DONET(NETISR_ISO, _clnlintr, 9) -#endif /* ISO */ +doreti_unpend: + /* + * Enabling interrupts is safe because we haven't restored cpl yet. + * The locking from the "btrl" test is probably no longer necessary. + * We won't miss any new pending interrupts because we will check + * for them again. + */ + sti + bsfl %ecx,%ecx /* slow, but not worth optimizing */ + btrl %ecx,_ipending + jnc doreti_next /* some intr cleared memory copy */ + movl ihandlers(,%ecx,4),%edx + testl %edx,%edx + je doreti_next /* "can't happen" */ + cmpl $NHWI,%ecx + jae doreti_swi + cli + movl %eax,_cpl + MEXITCOUNT + jmp %edx -#ifdef CCITT - DONET(NETISR_X25, _pkintr, 29) - DONET(NETISR_HDLC, _hdintr, 30) -#endif /* CCITT */ + ALIGN_TEXT +doreti_swi: + pushl %eax + /* + * The SWI_AST handler has to run at cpl = SWI_AST_MASK and the + * SWI_CLOCK handler at cpl = SWI_CLOCK_MASK, so we have to restore + * all the h/w bits in cpl now and have to worry about stack growth. + * The worst case is currently (30 Jan 1994) 2 SWI handlers nested + * in dying interrupt frames and about 12 HWIs nested in active + * interrupt frames. There are only 4 different SWIs and the HWI + * and SWI masks limit the nesting further. + */ + orl imasks(,%ecx,4),%eax + movl %eax,_cpl + call %edx + popl %eax + jmp doreti_next - FASTSPL($0) -test_ASTs: - btrl $NETISR_SCLK,_netisr - jnc test_resched - COUNT_EVENT(_intrcnt_spl, 10) - FASTSPL($SOFTCLOCKMASK) -/* - * Back to an interrupt frame for a moment. - */ - pushl $0 # previous cpl (probably not used) - pushl $0x7f # dummy unit number - call _softclock - addl $8,%esp # discard dummies - FASTSPL($0) -test_resched: -#ifdef notused1 - btrl $NETISR_AST,_netisr - jnc 2f -#endif -#ifdef notused2 - cmpl $0,_want_resched - je 2f -#endif - cmpl $0,_astpending # XXX - put it back in netisr to - je 2f # reduce the number of tests + ALIGN_TEXT +swi_ast: + addl $8,%esp /* discard raddr & cpl to get trap frame */ testb $SEL_RPL_MASK,TRAPF_CS_OFF(%esp) - # to non-kernel (i.e., user)? - je 2f # nope, leave - COUNT_EVENT(_intrcnt_spl, 11) - movl $0,_astpending + je swi_ast_phantom + movl $T_ASTFLT,(2+8+0)*4(%esp) call _trap -2: - COUNT_EVENT(_intrcnt_spl, 12) - popl %es - popl %ds - popal - addl $8,%esp - iret + subl %eax,%eax /* recover cpl */ + jmp doreti_next + + ALIGN_TEXT +swi_ast_phantom: + /* + * These happen when there is an interrupt in a trap handler before + * ASTs can be masked or in an lcall handler before they can be + * masked or after they are unmasked. They could be avoided for + * trap entries by using interrupt gates, and for lcall exits by + * using by using cli, but they are unavoidable for lcall entries. + */ + cli + orl $SWI_AST_PENDING,_ipending + jmp doreti_exit /* SWI_AST is highest so we must be done */ /* * Interrupt priority mechanism @@ -262,121 +178,84 @@ test_resched: * -- ipending = active interrupts currently masked by cpl */ - GENSPL(bio, _biomask, 13) - GENSPL(clock, $HIGHMASK, 14) /* splclock == splhigh ex for count */ - GENSPL(high, $HIGHMASK, 15) - GENSPL(imp, _netmask, 16) /* splimp == splnet except for count */ - GENSPL(net, _netmask, 17) - GENSPL(softclock, $SOFTCLOCKMASK, 18) - GENSPL(tty, _ttymask, 19) - - .globl _splnone - .globl _spl0 - ALIGN_TEXT -_splnone: -_spl0: - COUNT_EVENT(_intrcnt_spl, 20) -in_spl0: +ENTRY(splz) + /* + * The caller has restored cpl and checked that (ipending & ~cpl) + * is nonzero. We have to repeat the check since if there is an + * interrupt while we're looking, _doreti processing for the + * interrupt will handle all the unmasked pending interrupts + * because we restored early. We're repeating the calculation + * of (ipending & ~cpl) anyway so that the caller doesn't have + * to pass it, so this only costs one "jne". "bsfl %ecx,%ecx" + * is undefined when %ecx is 0 so we can't rely on the secondary + * btrl tests. + */ movl _cpl,%eax - pushl %eax # save old priority - testl $(1 << NETISR_RAW) | (1 << NETISR_IP),_netisr - je over_net_stuff_for_spl0 - movl _netmask,%eax # mask off those network devices - movl %eax,_cpl # set new priority - SHOW_CPL -/* - * XXX - what about other net intrs? - */ -#if 0 - DONET(NETISR_RAW, _rawintr, 21) -#endif - -#ifdef INET - DONET(NETISR_IP, _ipintr, 22) -#endif /* INET */ - -#ifdef IMP - DONET(NETISR_IMP, _impintr, 23) -#endif /* IMP */ - -#ifdef NS - DONET(NETISR_NS, _nsintr, 24) -#endif /* NS */ - -#ifdef ISO - DONET(NETISR_ISO, _clnlintr, 25) -#endif /* ISO */ - -over_net_stuff_for_spl0: - movl $0,_cpl # set new priority - SHOW_CPL - movl _ipending,%eax - testl %eax,%eax - jne unpend_V - popl %eax # return old priority +splz_next: + /* + * We don't need any locking here. (ipending & ~cpl) cannot grow + * while we're looking at it - any interrupt will shrink it to 0. + */ + movl %eax,%ecx + notl %ecx + andl _ipending,%ecx + jne splz_unpend ret - .globl _splx ALIGN_TEXT -_splx: - COUNT_EVENT(_intrcnt_spl, 26) - movl 4(%esp),%eax # new priority - testl %eax,%eax - je in_spl0 # going to "zero level" is special - COUNT_EVENT(_intrcnt_spl, 27) - movl _cpl,%edx # save old priority - movl %eax,_cpl # set new priority - SHOW_CPL - notl %eax - andl _ipending,%eax - jne unpend_V_result_edx - movl %edx,%eax # return old priority - ret - - ALIGN_TEXT -unpend_V_result_edx: - pushl %edx -unpend_V: - COUNT_EVENT(_intrcnt_spl, 28) - bsfl %eax,%eax - btrl %eax,_ipending - jnc unpend_V_next - SHOW_IPENDING - movl Vresume(,%eax,4),%edx +splz_unpend: + bsfl %ecx,%ecx + btrl %ecx,_ipending + jnc splz_next + movl ihandlers(,%ecx,4),%edx testl %edx,%edx - je noresumeV -/* - * We would prefer to call the intr handler directly here but that doesn't - * work for badly behaved handlers that want the interrupt frame. Also, - * there's a problem determining the unit number. We should change the - * interface so that the unit number is not determined at config time. - */ - jmp *vec(,%eax,4) + je splz_next /* "can't happen" */ + cmpl $NHWI,%ecx + jae splz_swi + /* + * We would prefer to call the intr handler directly here but that + * doesn't work for badly behaved handlers that want the interrupt + * frame. Also, there's a problem determining the unit number. + * We should change the interface so that the unit number is not + * determined at config time. + */ + jmp *vec(,%ecx,4) ALIGN_TEXT +splz_swi: + cmpl $SWI_AST,%ecx + je splz_next /* "can't happen" */ + pushl %eax + orl imasks(,%ecx,4),%eax + movl %eax,_cpl + call %edx + popl %eax + movl %eax,_cpl + jmp splz_next + /* - * XXX - must be some fastintr, need to register those too. + * Fake clock IRQ so that it appears to come from our caller and not from + * vec0, so that kernel profiling works. + * XXX do this more generally (for all vectors; look up the C entry point). + * XXX frame bogusness stops us from just jumping to the C entry point. */ -noresumeV: -#if NSIO > 0 - call _softsio1 -#endif -unpend_V_next: - movl _cpl,%eax - notl %eax - andl _ipending,%eax - jne unpend_V - popl %eax - ret + ALIGN_TEXT +vec0: + popl %eax /* return address */ + pushfl +#define KCSEL 8 + pushl $KCSEL + pushl %eax + cli + MEXITCOUNT + jmp _Vclk #define BUILD_VEC(irq_num) \ ALIGN_TEXT ; \ vec/**/irq_num: ; \ int $ICU_OFFSET + (irq_num) ; \ - popl %eax ; \ ret - BUILD_VEC(0) BUILD_VEC(1) BUILD_VEC(2) BUILD_VEC(3) @@ -392,3 +271,58 @@ vec/**/irq_num: ; \ BUILD_VEC(13) BUILD_VEC(14) BUILD_VEC(15) + + ALIGN_TEXT +swi_clock: + MCOUNT + subl %eax,%eax + cmpl $_splz,(%esp) /* XXX call from splz()? */ + jae 1f /* yes, usermode = 0 */ + movl 4+4+TRAPF_CS_OFF(%esp),%eax /* no, check trap frame */ + andl $SEL_RPL_MASK,%eax +1: + pushl %eax + call _softclock + addl $4,%esp + ret + +#define DONET(s, c, event) ; \ + .globl c ; \ + btrl $s,_netisr ; \ + jnc 9f ; \ + call c ; \ +9: + + ALIGN_TEXT +swi_net: + MCOUNT +#if 0 + DONET(NETISR_RAW, _rawintr,netisr_raw) +#endif +#ifdef INET + DONET(NETISR_IP, _ipintr,netisr_ip) +#endif +#ifdef IMP + DONET(NETISR_IMP, _impintr,netisr_imp) +#endif +#ifdef NS + DONET(NETISR_NS, _nsintr,netisr_ns) +#endif +#ifdef ISO + DONET(NETISR_ISO, _clnlintr,netisr_iso) +#endif +#ifdef CCITT + DONET(NETISR_X25, _pkintr, 29) + DONET(NETISR_HDLC, _hdintr, 30) +#endif + ret + + ALIGN_TEXT +swi_tty: + MCOUNT +#include "sio.h" +#if NSIO > 0 + jmp _siopoll +#else + ret +#endif diff --git a/sys/i386/isa/if_ed.c b/sys/i386/isa/if_ed.c index 079f09cc9d3b..e806b1e471f9 100644 --- a/sys/i386/isa/if_ed.c +++ b/sys/i386/isa/if_ed.c @@ -16,7 +16,7 @@ */ /* - * $Id: if_ed.c,v 1.33.2.2 1994/04/17 06:04:33 rgrimes Exp $ + * $Id: if_ed.c,v 1.42 1994/06/16 08:27:21 davidg Exp $ */ #include "ed.h" @@ -65,65 +65,71 @@ #ifndef IFF_ALTPHYS #define IFF_ALTPHYS IFF_LLC0 #endif - + /* * ed_softc: per line info and status */ -struct ed_softc { - struct arpcom arpcom; /* ethernet common */ +struct ed_softc { + struct arpcom arpcom; /* ethernet common */ - char *type_str; /* pointer to type string */ - u_char vendor; /* interface vendor */ - u_char type; /* interface type code */ + char *type_str; /* pointer to type string */ + u_char vendor; /* interface vendor */ + u_char type; /* interface type code */ - u_short asic_addr; /* ASIC I/O bus address */ - u_short nic_addr; /* NIC (DS8390) I/O bus address */ + u_short asic_addr; /* ASIC I/O bus address */ + u_short nic_addr; /* NIC (DS8390) I/O bus address */ /* * The following 'proto' variable is part of a work-around for 8013EBT asics * being write-only. It's sort of a prototype/shadow of the real thing. */ - u_char wd_laar_proto; - u_char isa16bit; /* width of access to card 0=8 or 1=16 */ - int is790; /* set by the probe code if the card is 790 based */ - - caddr_t bpf; /* BPF "magic cookie" */ - caddr_t mem_start; /* NIC memory start address */ - caddr_t mem_end; /* NIC memory end address */ - u_long mem_size; /* total NIC memory size */ - caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ - - u_char mem_shared; /* NIC memory is shared with host */ - u_char xmit_busy; /* transmitter is busy */ - u_char txb_cnt; /* number of transmit buffers */ - u_char txb_inuse; /* number of TX buffers currently in-use*/ - - u_char txb_new; /* pointer to where new buffer will be added */ - u_char txb_next_tx; /* pointer to next buffer ready to xmit */ - u_short txb_len[8]; /* buffered xmit buffer lengths */ - u_char tx_page_start; /* first page of TX buffer area */ - 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 */ -} ed_softc[NED]; - -int ed_attach(struct isa_device *); -void ed_init(int); -void edintr(int); -int ed_ioctl(struct ifnet *, int, caddr_t); -int ed_probe(struct isa_device *); -void ed_start(struct ifnet *); -void ed_reset(int, int); -void ed_watchdog(int); - -static void ed_get_packet(struct ed_softc *, char *, int /*u_short*/); + u_char wd_laar_proto; + u_char isa16bit; /* width of access to card 0=8 or 1=16 */ + int is790; /* set by the probe code if the card is 790 + * based */ + + caddr_t bpf; /* BPF "magic cookie" */ + caddr_t mem_start; /* NIC memory start address */ + caddr_t mem_end; /* NIC memory end address */ + u_long mem_size; /* total NIC memory size */ + caddr_t mem_ring; /* start of RX ring-buffer (in NIC mem) */ + + u_char mem_shared; /* NIC memory is shared with host */ + u_char xmit_busy; /* transmitter is busy */ + u_char txb_cnt; /* number of transmit buffers */ + u_char txb_inuse; /* number of TX buffers currently in-use */ + + u_char txb_new; /* pointer to where new buffer will be added */ + u_char txb_next_tx; /* pointer to next buffer ready to xmit */ + u_short txb_len[8]; /* buffered xmit buffer lengths */ + u_char tx_page_start; /* first page of TX buffer area */ + 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 */ +} ed_softc[NED]; + +int ed_attach(struct isa_device *); +void ed_init(int); +void edintr(int); +int ed_ioctl(struct ifnet *, int, caddr_t); +int ed_probe(struct isa_device *); +void ed_start(struct ifnet *); +void ed_reset(int, int); +void ed_watchdog(int); + +#ifdef MULTICAST +void ds_getmcaf(); + +#endif + +static void ed_get_packet(struct ed_softc *, char *, int /* u_short */ ); static void ed_stop(int); static inline void ed_rint(); static inline void ed_xmit(); static inline char *ed_ring_copy(); -void ed_pio_readmem(), ed_pio_writemem(); +void ed_pio_readmem(), ed_pio_writemem(); u_short ed_pio_write_mbufs(); extern int ether_output(); @@ -138,7 +144,8 @@ struct isa_driver eddriver = { ed_attach, "ed" }; -/* + +/* * Interrupt conversion table for WD/SMC ASIC * (IRQ* are defined in icu.h) */ @@ -152,7 +159,7 @@ static unsigned short ed_intr_mask[] = { IRQ15, IRQ4 }; - + /* * Interrupt conversion table for 585/790 Combo */ @@ -166,6 +173,7 @@ static unsigned short ed_790_intr_mask[] = { IRQ11, IRQ15 }; + #define ETHER_MIN_LEN 64 #define ETHER_MAX_LEN 1518 #define ETHER_ADDR_LEN 6 @@ -185,7 +193,7 @@ ed_probe(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - int nports; + int nports; if (nports = ed_probe_WD80x3(isa_dev)) return (nports); @@ -196,7 +204,7 @@ ed_probe(isa_dev) if (nports = ed_probe_Novell(isa_dev)) return (nports); - return(0); + return (0); } /* @@ -219,7 +227,7 @@ ed_probe(isa_dev) * the others would require changing register pages (which would be * intrusive if this isn't an 8390). * - * Return 1 if 8390 was found, 0 if not. + * Return 1 if 8390 was found, 0 if not. */ int @@ -227,15 +235,15 @@ ed_probe_generic8390(sc) struct ed_softc *sc; { if ((inb(sc->nic_addr + ED_P0_CR) & - (ED_CR_RD2|ED_CR_TXP|ED_CR_STA|ED_CR_STP)) != - (ED_CR_RD2|ED_CR_STP)) + (ED_CR_RD2 | ED_CR_TXP | ED_CR_STA | ED_CR_STP)) != + (ED_CR_RD2 | ED_CR_STP)) return (0); if ((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) != ED_ISR_RST) return (0); - return(1); + return (1); } - + /* * Probe and vendor-specific initialization routine for SMC/WD80x3 boards */ @@ -244,9 +252,9 @@ ed_probe_WD80x3(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - int i; - u_int memsize; - u_char iptr, isa16bit, sum; + int i; + u_int memsize; + u_char iptr, isa16bit, sum; sc->asic_addr = isa_dev->id_iobase; sc->nic_addr = sc->asic_addr + ED_WD_NIC_OFFSET; @@ -256,26 +264,27 @@ ed_probe_WD80x3(isa_dev) outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_POW); DELAY(10000); #endif + /* - * Attempt to do a checksum over the station address PROM. - * If it fails, it's probably not a SMC/WD board. There - * is a problem with this, though: some clone WD boards - * don't pass the checksum test. Danpex boards for one. + * Attempt to do a checksum over the station address PROM. If it + * fails, it's probably not a SMC/WD board. There is a problem with + * this, though: some clone WD boards don't pass the checksum test. + * Danpex boards for one. */ for (sum = 0, i = 0; i < 8; ++i) sum += inb(sc->asic_addr + ED_WD_PROM + i); if (sum != ED_WD_ROM_CHECKSUM_TOTAL) { + /* - * Checksum is invalid. This often happens with cheap - * WD8003E clones. In this case, the checksum byte - * (the eighth byte) seems to always be zero. + * Checksum is invalid. This often happens with cheap WD8003E + * clones. In this case, the checksum byte (the eighth byte) + * seems to always be zero. */ if (inb(sc->asic_addr + ED_WD_CARD_ID) != ED_TYPE_WD8003E || inb(sc->asic_addr + ED_WD_PROM + 7) != 0) - return(0); + return (0); } - /* reset card to force it into a known state. */ #ifdef TOSH_ETHER outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_RST | ED_WD_MSR_POW); @@ -318,7 +327,7 @@ ed_probe_WD80x3(isa_dev) memsize = 16384; isa16bit = 1; break; - case ED_TYPE_WD8013EP: /* also WD8003EP */ + case ED_TYPE_WD8013EP: /* also WD8003EP */ if (inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) { isa16bit = 1; @@ -371,33 +380,35 @@ ed_probe_WD80x3(isa_dev) sc->type_str = ""; break; } + /* - * Make some adjustments to initial values depending on what is - * found in the ICR. + * Make some adjustments to initial values depending on what is found + * in the ICR. */ if (isa16bit && (sc->type != ED_TYPE_WD8013EBT) #ifdef TOSH_ETHER - && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) + && (sc->type != ED_TYPE_TOSHIBA1) && (sc->type != ED_TYPE_TOSHIBA4) #endif && ((inb(sc->asic_addr + ED_WD_ICR) & ED_WD_ICR_16BIT) == 0)) { isa16bit = 0; memsize = 8192; } - #if ED_DEBUG printf("type = %x type_str=%s isa16bit=%d memsize=%d id_msize=%d\n", - sc->type,sc->type_str,isa16bit,memsize,isa_dev->id_msize); - for (i=0; i<8; i++) + sc->type, sc->type_str, isa16bit, memsize, isa_dev->id_msize); + for (i = 0; i < 8; i++) printf("%x -> %x\n", i, inb(sc->asic_addr + i)); #endif + /* * Allow the user to override the autoconfiguration */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; + /* - * (note that if the user specifies both of the following flags - * that '8bit' mode intentionally has precedence) + * (note that if the user specifies both of the following flags that + * '8bit' mode intentionally has precedence) */ if (isa_dev->id_flags & ED_FLAGS_FORCE_16BIT_MODE) isa16bit = 1; @@ -406,60 +417,66 @@ ed_probe_WD80x3(isa_dev) /* * Check 83C584 interrupt configuration register if this board has one - * XXX - we could also check the IO address register. But why - * bother...if we get past this, it *has* to be correct. + * XXX - we could also check the IO address register. But why + * bother...if we get past this, it *has* to be correct. */ if ((sc->type & ED_WD_SOFTCONFIG) && (!sc->is790)) { + /* * Assemble together the encoded interrupt number. */ iptr = (inb(isa_dev->id_iobase + ED_WD_ICR) & ED_WD_ICR_IR2) | - ((inb(isa_dev->id_iobase + ED_WD_IRR) & - (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); + ((inb(isa_dev->id_iobase + ED_WD_IRR) & + (ED_WD_IRR_IR0 | ED_WD_IRR_IR1)) >> 5); + /* - * Translate it using translation table, and check for correctness. + * Translate it using translation table, and check for + * correctness. */ if (ed_intr_mask[iptr] != isa_dev->id_irq) { printf("ed%d: kernel configured irq %d doesn't match board configured irq %d\n", - isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, - ffs(ed_intr_mask[iptr]) - 1); - return(0); + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, + ffs(ed_intr_mask[iptr]) - 1); + return (0); } + /* * Enable the interrupt. */ outb(isa_dev->id_iobase + ED_WD_IRR, - inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); + inb(isa_dev->id_iobase + ED_WD_IRR) | ED_WD_IRR_IEN); } if (sc->is790) { outb(isa_dev->id_iobase + ED_WD790_HWR, - inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); + inb(isa_dev->id_iobase + ED_WD790_HWR) | ED_WD790_HWR_SWH); iptr = (((inb(isa_dev->id_iobase + ED_WD790_GCR) & ED_WD790_GCR_IR2) >> 4) | - (inb(isa_dev->id_iobase + ED_WD790_GCR) & - (ED_WD790_GCR_IR1|ED_WD790_GCR_IR0)) >> 2); + (inb(isa_dev->id_iobase + ED_WD790_GCR) & + (ED_WD790_GCR_IR1 | ED_WD790_GCR_IR0)) >> 2); outb(isa_dev->id_iobase + ED_WD790_HWR, - inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); + inb(isa_dev->id_iobase + ED_WD790_HWR) & ~ED_WD790_HWR_SWH); if (ed_790_intr_mask[iptr] != isa_dev->id_irq) { printf("ed%d: kernel configured irq %d doesn't match board configured irq %d %d\n", - isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, - ffs(ed_790_intr_mask[iptr]) - 1, iptr); + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1, + ffs(ed_790_intr_mask[iptr]) - 1, iptr); return 0; } + /* * Enable interrupts. */ outb(isa_dev->id_iobase + ED_WD790_ICR, - inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); + inb(isa_dev->id_iobase + ED_WD790_ICR) | ED_WD790_ICR_EIL); } - sc->isa16bit = isa16bit; -#ifdef notyet /* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */ +/* XXX - I'm not sure if PIO mode is even possible on WD/SMC boards */ +#ifdef notyet + /* * The following allows the WD/SMC boards to be used in Programmed I/O - * mode - without mapping the NIC memory shared. ...Not the prefered - * way, but it might be the only way. + * mode - without mapping the NIC memory shared. ...Not the prefered + * way, but it might be the only way. */ if (isa_dev->id_flags & ED_FLAGS_FORCE_PIO) { sc->mem_shared = 0; @@ -472,7 +489,7 @@ ed_probe_WD80x3(isa_dev) #endif isa_dev->id_msize = memsize; - sc->mem_start = (caddr_t)isa_dev->id_maddr; + sc->mem_start = (caddr_t) isa_dev->id_maddr; /* * allocate one xmit buffer if < 16k, two buffers otherwise @@ -498,27 +515,6 @@ ed_probe_WD80x3(isa_dev) sc->arpcom.ac_enaddr[i] = inb(sc->asic_addr + ED_WD_PROM + i); if (sc->mem_shared) { - /* - * Set address and enable interface shared memory. - */ - if(!sc->is790) { -#ifdef TOSH_ETHER - outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); - outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); - outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); - -#else - outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & - ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); -#endif - } else { - outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); - outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80)); - outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) | - ((kvtop(sc->mem_start) >> 11) & 0x40) | - (inb(sc->asic_addr + 0x0b) & 0xb0)); - outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80)); - } /* * Set upper address bits and 8/16 bit access to shared memory @@ -530,57 +526,85 @@ ed_probe_WD80x3(isa_dev) (void) inb(0x84); } else { outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = - ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN | - ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + ED_WD_LAAR_L16EN | ED_WD_LAAR_M16EN | + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); } - } else { + } else { if ((sc->type & ED_WD_SOFTCONFIG) || #ifdef TOSH_ETHER (sc->type == ED_TYPE_TOSHIBA1) || (sc->type == ED_TYPE_TOSHIBA4) || #endif (sc->type == ED_TYPE_WD8013EBT) && (!sc->is790)) { outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto = - ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); + ((kvtop(sc->mem_start) >> 19) & ED_WD_LAAR_ADDRHI))); } } /* + * Set address and enable interface shared memory. + */ + if (!sc->is790) { +#ifdef TOSH_ETHER + outb(sc->asic_addr + ED_WD_MSR + 1, ((kvtop(sc->mem_start) >> 8) & 0xe0) | 4); + outb(sc->asic_addr + ED_WD_MSR + 2, ((kvtop(sc->mem_start) >> 16) & 0x0f)); + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB | ED_WD_MSR_POW); + +#else + outb(sc->asic_addr + ED_WD_MSR, ((kvtop(sc->mem_start) >> 13) & + ED_WD_MSR_ADDR) | ED_WD_MSR_MENB); +#endif + } else { + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) | 0x80)); + outb(sc->asic_addr + 0x0b, ((kvtop(sc->mem_start) >> 13) & 0x0f) | + ((kvtop(sc->mem_start) >> 11) & 0x40) | + (inb(sc->asic_addr + 0x0b) & 0xb0)); + outb(sc->asic_addr + 0x04, (inb(sc->asic_addr + 0x04) & ~0x80)); + } + + /* * Now zero memory and verify that it is clear */ bzero(sc->mem_start, memsize); for (i = 0; i < memsize; ++i) if (sc->mem_start[i]) { - printf("ed%d: failed to clear shared memory at %x - check configuration\n", - isa_dev->id_unit, kvtop(sc->mem_start + i)); + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); /* * Disable 16 bit access to shared memory */ if (isa16bit) { + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= - ~ED_WD_LAAR_M16EN)); + ~ED_WD_LAAR_M16EN)); (void) inb(0x84); } - - return(0); + return (0); } - + /* - * Disable 16bit access to shared memory - we leave it disabled so - * that 1) machines reboot properly when the board is set - * 16 bit mode and there are conflicting 8bit devices/ROMS - * in the same 128k address space as this boards shared - * memory. and 2) so that other 8 bit devices with shared - * memory can be used in this 128k region, too. + * Disable 16bit access to shared memory - we leave it + * disabled so that 1) machines reboot properly when the board + * is set 16 bit mode and there are conflicting 8bit + * devices/ROMS in the same 128k address space as this boards + * shared memory. and 2) so that other 8 bit devices with + * shared memory can be used in this 128k region, too. */ if (isa16bit) { + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto &= - ~ED_WD_LAAR_M16EN)); + ~ED_WD_LAAR_M16EN)); (void) inb(0x84); } } - return (ED_WD_IO_PORTS); } @@ -592,84 +616,84 @@ ed_probe_3Com(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - int i; - u_int memsize; - u_char isa16bit, sum; + int i; + u_int memsize; + u_char isa16bit, sum; sc->asic_addr = isa_dev->id_iobase + ED_3COM_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_3COM_NIC_OFFSET; /* * Verify that the kernel configured I/O address matches the board - * configured address + * configured address */ switch (inb(sc->asic_addr + ED_3COM_BCFR)) { case ED_3COM_BCFR_300: if (isa_dev->id_iobase != 0x300) - return(0); + return (0); break; case ED_3COM_BCFR_310: if (isa_dev->id_iobase != 0x310) - return(0); + return (0); break; case ED_3COM_BCFR_330: if (isa_dev->id_iobase != 0x330) - return(0); + return (0); break; case ED_3COM_BCFR_350: if (isa_dev->id_iobase != 0x350) - return(0); + return (0); break; case ED_3COM_BCFR_250: if (isa_dev->id_iobase != 0x250) - return(0); + return (0); break; case ED_3COM_BCFR_280: if (isa_dev->id_iobase != 0x280) - return(0); + return (0); break; case ED_3COM_BCFR_2A0: if (isa_dev->id_iobase != 0x2a0) - return(0); + return (0); break; case ED_3COM_BCFR_2E0: if (isa_dev->id_iobase != 0x2e0) - return(0); + return (0); break; default: - return(0); + return (0); } /* - * Verify that the kernel shared memory address matches the - * board configured address. + * Verify that the kernel shared memory address matches the board + * configured address. */ switch (inb(sc->asic_addr + ED_3COM_PCFR)) { case ED_3COM_PCFR_DC000: if (kvtop(isa_dev->id_maddr) != 0xdc000) - return(0); + return (0); break; case ED_3COM_PCFR_D8000: if (kvtop(isa_dev->id_maddr) != 0xd8000) - return(0); + return (0); break; case ED_3COM_PCFR_CC000: if (kvtop(isa_dev->id_maddr) != 0xcc000) - return(0); + return (0); break; case ED_3COM_PCFR_C8000: if (kvtop(isa_dev->id_maddr) != 0xc8000) - return(0); + return (0); break; default: - return(0); + return (0); } /* * Reset NIC and ASIC. Enable on-board transceiver throughout reset - * sequence because it'll lock up if the cable isn't connected - * if we don't. + * sequence because it'll lock up if the cable isn't connected if we + * don't. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_RST | ED_3COM_CR_XSEL); @@ -677,10 +701,11 @@ ed_probe_3Com(isa_dev) * Wait for a while, then un-reset it */ DELAY(50); + /* * The 3Com ASIC defaults to rather strange settings for the CR after - * a reset - it's important to set it again after the following - * outb (this is done when we map the PROM below). + * a reset - it's important to set it again after the following outb + * (this is done when we map the PROM below). */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); @@ -695,17 +720,18 @@ ed_probe_3Com(isa_dev) sc->mem_shared = 1; /* - * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k - * window to it. + * Hmmm...a 16bit 3Com board has 16k of memory, but only an 8k window + * to it. */ memsize = 8192; /* * Get station address from on-board ROM */ + /* * First, map ethernet address PROM over the top of where the NIC - * registers normally appear. + * registers normally appear. */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_EALO | ED_3COM_CR_XSEL); @@ -714,9 +740,8 @@ ed_probe_3Com(isa_dev) /* * Unmap PROM - select NIC registers. The proper setting of the - * tranceiver is set in ed_init so that the attach code - * is given a chance to set the default based on a compile-time - * config option + * tranceiver is set in ed_init so that the attach code is given a + * chance to set the default based on a compile-time config option */ outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); @@ -727,18 +752,18 @@ ed_probe_3Com(isa_dev) /* * select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); /* - * Attempt to clear WTS bit. If it doesn't clear, then this is a - * 16bit board. + * Attempt to clear WTS bit. If it doesn't clear, then this is a 16bit + * board. */ outb(sc->nic_addr + ED_P0_DCR, 0); /* * select page 2 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2|ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_2 | ED_CR_RD2 | ED_CR_STP); /* * The 3c503 forces the WTS bit to a one if this is a 16bit board @@ -751,24 +776,23 @@ ed_probe_3Com(isa_dev) /* * select page 0 registers */ - outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P2_CR, ED_CR_RD2 | ED_CR_STP); - sc->mem_start = (caddr_t)isa_dev->id_maddr; + sc->mem_start = (caddr_t) isa_dev->id_maddr; sc->mem_size = memsize; sc->mem_end = sc->mem_start + memsize; /* * We have an entire 8k window to put the transmit buffers on the - * 16bit boards. But since the 16bit 3c503's shared memory - * is only fast enough to overlap the loading of one full-size - * packet, trying to load more than 2 buffers can actually - * leave the transmitter idle during the load. So 2 seems - * the best value. (Although a mix of variable-sized packets - * might change this assumption. Nonetheless, we optimize for - * linear transfers of same-size packets.) + * 16bit boards. But since the 16bit 3c503's shared memory is only + * fast enough to overlap the loading of one full-size packet, trying + * to load more than 2 buffers can actually leave the transmitter idle + * during the load. So 2 seems the best value. (Although a mix of + * variable-sized packets might change this assumption. Nonetheless, + * we optimize for linear transfers of same-size packets.) */ if (isa16bit) { - if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) + if (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING) sc->txb_cnt = 1; else sc->txb_cnt = 2; @@ -776,22 +800,22 @@ ed_probe_3Com(isa_dev) sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_16BIT; sc->rec_page_start = ED_3COM_RX_PAGE_OFFSET_16BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + - ED_3COM_RX_PAGE_OFFSET_16BIT; + ED_3COM_RX_PAGE_OFFSET_16BIT; sc->mem_ring = sc->mem_start; } else { sc->txb_cnt = 1; sc->tx_page_start = ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_start = ED_TXBUF_SIZE + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->rec_page_stop = memsize / ED_PAGE_SIZE + - ED_3COM_TX_PAGE_OFFSET_8BIT; + ED_3COM_TX_PAGE_OFFSET_8BIT; sc->mem_ring = sc->mem_start + (ED_PAGE_SIZE * ED_TXBUF_SIZE); } sc->isa16bit = isa16bit; /* - * Initialize GA page start/stop registers. Probably only needed - * if doing DMA, but what the hell. + * Initialize GA page start/stop registers. Probably only needed if + * doing DMA, but what the hell. */ outb(sc->asic_addr + ED_3COM_PSTR, sc->rec_page_start); outb(sc->asic_addr + ED_3COM_PSPR, sc->rec_page_stop); @@ -814,21 +838,22 @@ ed_probe_3Com(isa_dev) break; default: printf("ed%d: Invalid irq configuration (%d) must be 2-5 for 3c503\n", - isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); - return(0); + isa_dev->id_unit, ffs(isa_dev->id_irq) - 1); + return (0); } /* - * Initialize GA configuration register. Set bank and enable shared mem. + * Initialize GA configuration register. Set bank and enable shared + * mem. */ outb(sc->asic_addr + ED_3COM_GACFR, ED_3COM_GACFR_RSEL | - ED_3COM_GACFR_MBS0); + ED_3COM_GACFR_MBS0); /* - * Initialize "Vector Pointer" registers. These gawd-awful things - * are compared to 20 bits of the address on ISA, and if they - * match, the shared memory is disabled. We set them to - * 0xffff0...allegedly the reset vector. + * Initialize "Vector Pointer" registers. These gawd-awful things are + * compared to 20 bits of the address on ISA, and if they match, the + * shared memory is disabled. We set them to 0xffff0...allegedly the + * reset vector. */ outb(sc->asic_addr + ED_3COM_VPTR2, 0xff); outb(sc->asic_addr + ED_3COM_VPTR1, 0xff); @@ -841,13 +866,12 @@ ed_probe_3Com(isa_dev) for (i = 0; i < memsize; ++i) if (sc->mem_start[i]) { - printf("ed%d: failed to clear shared memory at %x - check configuration\n", - isa_dev->id_unit, kvtop(sc->mem_start + i)); - return(0); + printf("ed%d: failed to clear shared memory at %x - check configuration\n", + isa_dev->id_unit, kvtop(sc->mem_start + i)); + return (0); } - isa_dev->id_msize = memsize; - return(ED_3COM_IO_PORTS); + return (ED_3COM_IO_PORTS); } /* @@ -858,10 +882,10 @@ ed_probe_Novell(isa_dev) struct isa_device *isa_dev; { struct ed_softc *sc = &ed_softc[isa_dev->id_unit]; - u_int memsize, n; - u_char romdata[16], isa16bit = 0, tmp; + u_int memsize, n; + u_char romdata[16], isa16bit = 0, tmp; static char test_pattern[32] = "THIS is A memory TEST pattern"; - char test_buffer[32]; + char test_buffer[32]; sc->asic_addr = isa_dev->id_iobase + ED_NOVELL_ASIC_OFFSET; sc->nic_addr = isa_dev->id_iobase + ED_NOVELL_NIC_OFFSET; @@ -869,50 +893,54 @@ ed_probe_Novell(isa_dev) /* XXX - do Novell-specific probe here */ /* Reset the board */ +#ifdef GWETHER + outb(sc->asic_addr + ED_NOVELL_RESET, 0); + DELAY(200); +#endif /* GWETHER */ tmp = inb(sc->asic_addr + ED_NOVELL_RESET); /* * I don't know if this is necessary; probably cruft leftover from - * Clarkson packet driver code. Doesn't do a thing on the boards - * I've tested. -DG [note that a outb(0x84, 0) seems to work - * here, and is non-invasive...but some boards don't seem to reset - * and I don't have complete documentation on what the 'right' - * thing to do is...so we do the invasive thing for now. Yuck.] + * Clarkson packet driver code. Doesn't do a thing on the boards I've + * tested. -DG [note that a outb(0x84, 0) seems to work here, and is + * non-invasive...but some boards don't seem to reset and I don't have + * complete documentation on what the 'right' thing to do is...so we + * do the invasive thing for now. Yuck.] */ outb(sc->asic_addr + ED_NOVELL_RESET, tmp); DELAY(5000); /* * This is needed because some NE clones apparently don't reset the - * NIC properly (or the NIC chip doesn't reset fully on power-up) - * XXX - this makes the probe invasive! ...Done against my better - * judgement. -DLG + * NIC properly (or the NIC chip doesn't reset fully on power-up) XXX + * - this makes the probe invasive! ...Done against my better + * judgement. -DLG */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); DELAY(5000); /* Make sure that we really have an 8390 based board */ if (!ed_probe_generic8390(sc)) - return(0); + return (0); sc->vendor = ED_VENDOR_NOVELL; sc->mem_shared = 0; isa_dev->id_maddr = 0; /* - * Test the ability to read and write to the NIC memory. This has - * the side affect of determining if this is an NE1000 or an NE2000. + * Test the ability to read and write to the NIC memory. This has the + * side affect of determining if this is an NE1000 or an NE2000. */ /* - * This prevents packets from being stored in the NIC memory when - * the readmem routine turns on the start bit in the CR. + * This prevents packets from being stored in the NIC memory when the + * readmem routine turns on the start bit in the CR. */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); /* Temporarily initialize DCR for byte operations */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 8192 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 16384 / ED_PAGE_SIZE); @@ -921,8 +949,8 @@ ed_probe_Novell(isa_dev) /* * Write a test pattern in byte mode. If this fails, then there - * probably isn't any memory at 8k - which likely means - * that the board is an NE2000. + * probably isn't any memory at 8k - which likely means that the board + * is an NE2000. */ ed_pio_writemem(sc, test_pattern, 8192, sizeof(test_pattern)); ed_pio_readmem(sc, 8192, test_buffer, sizeof(test_pattern)); @@ -930,20 +958,21 @@ ed_probe_Novell(isa_dev) if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) { /* not an NE1000 - try NE2000 */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS|ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_WTS | ED_DCR_FT1 | ED_DCR_LS); outb(sc->nic_addr + ED_P0_PSTART, 16384 / ED_PAGE_SIZE); outb(sc->nic_addr + ED_P0_PSTOP, 32768 / ED_PAGE_SIZE); sc->isa16bit = 1; + /* * Write a test pattern in word mode. If this also fails, then - * we don't know what this board is. + * we don't know what this board is. */ ed_pio_writemem(sc, test_pattern, 16384, sizeof(test_pattern)); ed_pio_readmem(sc, 16384, test_buffer, sizeof(test_pattern)); if (bcmp(test_pattern, test_buffer, sizeof(test_pattern))) - return(0); /* not an NE2000 either */ + return (0); /* not an NE2000 either */ sc->type = ED_TYPE_NE2000; sc->type_str = "NE2000"; @@ -951,11 +980,11 @@ ed_probe_Novell(isa_dev) sc->type = ED_TYPE_NE1000; sc->type_str = "NE1000"; } - + /* 8k of memory plus an additional 8k if 16bit */ memsize = 8192 + sc->isa16bit * 8192; -#if 0 /* probably not useful - NE boards only come two ways */ +#if 0 /* probably not useful - NE boards only come two ways */ /* allow kernel config file overrides */ if (isa_dev->id_msize) memsize = isa_dev->id_msize; @@ -969,9 +998,72 @@ ed_probe_Novell(isa_dev) sc->mem_end = sc->mem_start + memsize; sc->tx_page_start = memsize / ED_PAGE_SIZE; +#ifdef GWETHER + { + int x, i, mstart = 0, msize = 0; + char pbuf0[ED_PAGE_SIZE], pbuf[ED_PAGE_SIZE], tbuf[ED_PAGE_SIZE]; + + for (i = 0; i < ED_PAGE_SIZE; i++) + pbuf0[i] = 0; + + /* Clear all the memory. */ + for (x = 1; x < 256; x++) + ed_pio_writemem(sc, pbuf0, x * 256, ED_PAGE_SIZE); + + /* Search for the start of RAM. */ + for (x = 1; x < 256; x++) { + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { + for (i = 0; i < ED_PAGE_SIZE; i++) + pbuf[i] = 255 - x; + ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) { + mstart = x * ED_PAGE_SIZE; + msize = ED_PAGE_SIZE; + break; + } + } + } + + if (mstart == 0) { + printf("ed%d: Cannot find start of RAM.\n", isa_dev->id_unit); + return 0; + } + /* Search for the start of RAM. */ + for (x = (mstart / ED_PAGE_SIZE) + 1; x < 256; x++) { + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf0, tbuf, ED_PAGE_SIZE) == 0) { + for (i = 0; i < ED_PAGE_SIZE; i++) + pbuf[i] = 255 - x; + ed_pio_writemem(sc, pbuf, x * 256, ED_PAGE_SIZE); + ed_pio_readmem(sc, x * 256, tbuf, ED_PAGE_SIZE); + if (memcmp(pbuf, tbuf, ED_PAGE_SIZE) == 0) + msize += ED_PAGE_SIZE; + else { + break; + } + } else { + break; + } + } + + if (msize == 0) { + printf("ed%d: Cannot find any RAM, start : %d, x = %d.\n", isa_dev->id_unit, mstart, x); + return 0; + } + printf("ed%d: RAM start at %d, size : %d.\n", isa_dev->id_unit, mstart, msize); + + sc->mem_size = msize; + sc->mem_start = (char *) mstart; + sc->mem_end = (char *) (msize + mstart); + sc->tx_page_start = mstart / ED_PAGE_SIZE; + } +#endif /* GWETHER */ + /* * Use one xmit buffer if < 16k, two buffers otherwise (if not told - * otherwise). + * otherwise). */ if ((memsize < 16384) || (isa_dev->id_flags & ED_FLAGS_NO_MULTI_BUFFERING)) sc->txb_cnt = 1; @@ -985,14 +1077,19 @@ ed_probe_Novell(isa_dev) ed_pio_readmem(sc, 0, romdata, 16); for (n = 0; n < ETHER_ADDR_LEN; n++) - sc->arpcom.ac_enaddr[n] = romdata[n*(sc->isa16bit+1)]; + sc->arpcom.ac_enaddr[n] = romdata[n * (sc->isa16bit + 1)]; + +#ifdef GWETHER + if (sc->arpcom.ac_enaddr[2] == 0x86) + sc->type_str = "Gateway AT"; +#endif /* GWETHER */ /* clear any pending interrupts that might have occurred above */ outb(sc->nic_addr + ED_P0_ISR, 0xff); - return(ED_NOVELL_IO_PORTS); + return (ED_NOVELL_IO_PORTS); } - + /* * Install interface into kernel networking data structures */ @@ -1004,7 +1101,7 @@ ed_attach(isa_dev) struct ifnet *ifp = &sc->arpcom.ac_if; struct ifaddr *ifa; struct sockaddr_dl *sdl; - + /* * Set interface to stopped condition (reset) */ @@ -1014,7 +1111,7 @@ ed_attach(isa_dev) * Initialize ifnet structure */ ifp->if_unit = isa_dev->id_unit; - ifp->if_name = "ed" ; + ifp->if_name = "ed"; ifp->if_mtu = ETHERMTU; ifp->if_init = ed_init; ifp->if_output = ether_output; @@ -1025,13 +1122,16 @@ ed_attach(isa_dev) /* * Set default state for ALTPHYS flag (used to disable the tranceiver - * for AUI operation), based on compile-time config option. + * for AUI operation), based on compile-time config option. */ if (isa_dev->id_flags & ED_FLAGS_DISABLE_TRANCEIVER) ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS | IFF_ALTPHYS); else ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); +#ifdef MULTICAST + ifp->if_flags |= IFF_MULTICAST; +#endif /* * Attach the interface @@ -1041,20 +1141,22 @@ ed_attach(isa_dev) /* * Search down the ifa address list looking for the AF_LINK type entry */ - ifa = ifp->if_addrlist; + ifa = ifp->if_addrlist; while ((ifa != 0) && (ifa->ifa_addr != 0) && - (ifa->ifa_addr->sa_family != AF_LINK)) + (ifa->ifa_addr->sa_family != AF_LINK)) ifa = ifa->ifa_next; + /* * If we find an AF_LINK type entry we fill in the hardware address. - * This is useful for netstat(1) to keep track of which interface - * is which. + * This is useful for netstat(1) to keep track of which interface is + * which. */ if ((ifa != 0) && (ifa->ifa_addr != 0)) { + /* * Fill in the link-level address for this interface */ - sdl = (struct sockaddr_dl *)ifa->ifa_addr; + sdl = (struct sockaddr_dl *) ifa->ifa_addr; sdl->sdl_type = IFT_ETHER; sdl->sdl_alen = ETHER_ADDR_LEN; sdl->sdl_slen = 0; @@ -1065,17 +1167,17 @@ ed_attach(isa_dev) * Print additional info when attached */ printf("ed%d: address %s, ", isa_dev->id_unit, - ether_sprintf(sc->arpcom.ac_enaddr)); + ether_sprintf(sc->arpcom.ac_enaddr)); if (sc->type_str && (*sc->type_str != 0)) printf("type %s ", sc->type_str); else printf("type unknown (0x%x) ", sc->type); - printf("%s ",sc->isa16bit ? "(16 bit)" : "(8 bit)"); + printf("%s ", sc->isa16bit ? "(16 bit)" : "(8 bit)"); printf("%s\n", ((sc->vendor == ED_VENDOR_3COM) && - (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); + (ifp->if_flags & IFF_ALTPHYS)) ? " tranceiver disabled" : ""); /* * If BPF is in the kernel, call the attach for it @@ -1085,16 +1187,16 @@ ed_attach(isa_dev) #endif return 1; } - + /* * Reset interface. */ void ed_reset(unit, uban) - int unit; - int uban; /* XXX */ + int unit; + int uban; /* XXX */ { - int s; + int s; s = splimp(); @@ -1106,29 +1208,30 @@ ed_reset(unit, uban) (void) splx(s); } - + /* * Take interface offline. */ void ed_stop(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; - int n = 5000; - + int n = 5000; + /* * Stop everything on the interface, and select page 0 registers. */ if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); } + /* - * Wait for interface to enter stopped state, but limit # of checks - * to 'n' (about 5ms). It shouldn't even take 5us on modern - * DS8390's, but just in case it's an old one. + * Wait for interface to enter stopped state, but limit # of checks to + * 'n' (about 5ms). It shouldn't even take 5us on modern DS8390's, but + * just in case it's an old one. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RST) == 0) && --n); @@ -1140,7 +1243,7 @@ ed_stop(unit) */ void ed_watchdog(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; @@ -1151,25 +1254,26 @@ ed_watchdog(unit) } /* - * Initialize device. + * Initialize device. */ void ed_init(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; struct ifnet *ifp = &sc->arpcom.ac_if; - int i, s; - u_char command; + int i, s; + u_char command; /* address not known */ - if (ifp->if_addrlist == (struct ifaddr *)0) return; + if (ifp->if_addrlist == (struct ifaddr *) 0) + return; /* * Initialize the NIC in the exact order outlined in the NS manual. - * This init procedure is "mandatory"...don't change what or when - * things happen. + * This init procedure is "mandatory"...don't change what or when + * things happen. */ s = splimp(); @@ -1190,19 +1294,21 @@ ed_init(unit) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STP); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STP); } if (sc->isa16bit) { + /* - * Set FIFO threshold to 8, No auto-init Remote DMA, - * byte order=80x86, word-wide DMA xfers, + * Set FIFO threshold to 8, No auto-init Remote DMA, byte + * order=80x86, word-wide DMA xfers, */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_WTS|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_WTS | ED_DCR_LS); } else { + /* * Same as above, but byte-wide DMA xfers */ - outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1|ED_DCR_LS); + outb(sc->nic_addr + ED_P0_DCR, ED_DCR_FT1 | ED_DCR_LS); } /* @@ -1210,11 +1316,19 @@ ed_init(unit) */ outb(sc->nic_addr + ED_P0_RBCR0, 0); outb(sc->nic_addr + ED_P0_RBCR1, 0); +#ifndef MULTICAST /* * Enable reception of broadcast packets */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); +#else + + /* + * Tell RCR to do nothing for now. + */ + outb(sc->nic_addr + ED_P0_RCR, ED_RCR_MON); +#endif /* * Place NIC in internal loopback mode @@ -1238,41 +1352,62 @@ ed_init(unit) /* * Clear all interrupts. A '1' in each bit position clears the - * corresponding flag. + * corresponding flag. */ outb(sc->nic_addr + ED_P0_ISR, 0xff); /* * Enable the following interrupts: receive/transmit complete, - * receive/transmit error, and Receiver OverWrite. - * + * receive/transmit error, and Receiver OverWrite. + * * Counter overflow and Remote DMA complete are *not* enabled. */ outb(sc->nic_addr + ED_P0_IMR, - ED_IMR_PRXE|ED_IMR_PTXE|ED_IMR_RXEE|ED_IMR_TXEE|ED_IMR_OVWE); + ED_IMR_PRXE | ED_IMR_PTXE | ED_IMR_RXEE | ED_IMR_TXEE | ED_IMR_OVWE); /* * Program Command Register for page 1 */ if (sc->is790) { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STP); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STP); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STP); } + /* * Copy out our station address */ for (i = 0; i < ETHER_ADDR_LEN; ++i) outb(sc->nic_addr + ED_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); +#ifndef MULTICAST #if NBPFILTER > 0 + /* - * Initialize multicast address hashing registers to accept - * all multicasts (only used when in promiscuous mode) + * Initialize multicast address hashing registers to accept all + * multicasts (only used when in promiscuous mode) */ for (i = 0; i < 8; ++i) outb(sc->nic_addr + ED_P1_MAR0 + i, 0xff); #endif +#else + /* set up multicast addresses and filter modes */ + if (sc != 0 && (ifp->if_flags & IFF_MULTICAST) != 0) { + u_long mcaf[2]; + + if ((ifp->if_flags & IFF_ALLMULTI) != 0) { + mcaf[0] = 0xffffffff; + mcaf[1] = 0xffffffff; + } else + ds_getmcaf(sc, mcaf); + + /* + * Set multicast filter on chip. + */ + for (i = 0; i < 8; i++) + outb(sc->nic_addr + ED_P1_MAR0 + i, ((u_char *) mcaf)[i]); + } +#endif /* * Set Current Page pointer to next_packet (initialized above) @@ -1280,14 +1415,22 @@ ed_init(unit) outb(sc->nic_addr + ED_P1_CURR, sc->next_packet); /* - * Set Command Register for page 0, Remote DMA complete, - * and interface Start. + * Set Command Register for page 0, Remote DMA complete, and interface + * Start. */ if (sc->is790) { outb(sc->nic_addr + ED_P1_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P1_CR, ED_CR_RD2 | ED_CR_STA); } +#ifdef MULTICAST + + /* + * Clear all interrupts + */ + outb(sc->nic_addr + ED_P0_ISR, 0xff); +#endif + /* * Take interface out of loopback */ @@ -1295,7 +1438,7 @@ ed_init(unit) /* * If this is a 3Com board, the tranceiver must be software enabled - * (there is no settable hardware default). + * (there is no settable hardware default). */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { @@ -1304,6 +1447,25 @@ ed_init(unit) outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } +#ifdef MULTICAST + i = ED_RCR_AB; + if (sc != 0) { + if ((ifp->if_flags & IFF_PROMISC) != 0) { + + /* + * Set promiscuous mode. Also reconfigure the + * multicast filter. + */ + int j; + + i |= ED_RCR_PRO | ED_RCR_AM | ED_RCR_AR | ED_RCR_SEP; + for (j = 0; j < 8; j++) + outb(sc->nic_addr + ED_P1_MAR0 + j, 0xff); + } + i |= ED_RCR_AM; + } + outb(sc->nic_addr + ED_P0_RCR, i); +#endif /* * Set 'running' flag, and clear output active flag. @@ -1318,11 +1480,12 @@ ed_init(unit) (void) splx(s); } - + /* * This routine actually starts the transmission on the interface */ -static inline void ed_xmit(ifp) +static inline void +ed_xmit(ifp) struct ifnet *ifp; { struct ed_softc *sc = &ed_softc[ifp->if_unit]; @@ -1336,13 +1499,14 @@ static inline void ed_xmit(ifp) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } + /* * Set TX buffer start page */ outb(sc->nic_addr + ED_P0_TPSR, sc->tx_page_start + - sc->txb_next_tx * ED_TXBUF_SIZE); + sc->txb_next_tx * ED_TXBUF_SIZE); /* * Set TX length @@ -1356,10 +1520,10 @@ static inline void ed_xmit(ifp) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_TXP | ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_TXP|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_TXP | ED_CR_STA); } sc->xmit_busy = 1; - + /* * Point to next transmit buffer slot and wrap if necessary. */ @@ -1389,12 +1553,13 @@ ed_start(ifp) struct ed_softc *sc = &ed_softc[ifp->if_unit]; struct mbuf *m0, *m; caddr_t buffer; - int len; + int len; outloop: + /* - * First, see if there are buffered packets and an idle - * transmitter - should never happen at this point. + * First, see if there are buffered packets and an idle transmitter - + * should never happen at this point. */ if (sc->txb_inuse && (sc->xmit_busy == 0)) { printf("ed: packets buffers, but transmitter idle\n"); @@ -1405,23 +1570,23 @@ outloop: * See if there is room to put another packet in the buffer. */ if (sc->txb_inuse == sc->txb_cnt) { + /* - * No room. Indicate this to the outside world - * and exit. + * No room. Indicate this to the outside world and exit. */ ifp->if_flags |= IFF_OACTIVE; return; } - IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); if (m == 0) { - /* - * We are using the !OACTIVE flag to indicate to the outside - * world that we can accept an additional packet rather than - * that the transmitter is _actually_ active. Indeed, the - * transmitter may be active, but if we haven't filled all - * the buffers with data then we still want to accept more. - */ + + /* + * We are using the !OACTIVE flag to indicate to the outside + * world that we can accept an additional packet rather than + * that the transmitter is _actually_ active. Indeed, the + * transmitter may be active, but if we haven't filled all the + * buffers with data then we still want to accept more. + */ ifp->if_flags &= ~IFF_OACTIVE; return; } @@ -1436,45 +1601,49 @@ outloop: buffer = sc->mem_start + (sc->txb_new * ED_TXBUF_SIZE * ED_PAGE_SIZE); if (sc->mem_shared) { + /* * Special case setup for 16 bit boards... */ if (sc->isa16bit) { switch (sc->vendor) { - /* - * For 16bit 3Com boards (which have 16k of memory), - * we have the xmit buffers in a different page - * of memory ('page 0') - so change pages. - */ + + /* + * For 16bit 3Com boards (which have 16k of + * memory), we have the xmit buffers in a + * different page of memory ('page 0') - so + * change pages. + */ case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, - ED_3COM_GACFR_RSEL); + ED_3COM_GACFR_RSEL); break; - /* - * Enable 16bit access to shared memory on WD/SMC boards - * Don't update wd_laar_proto because we want to restore the - * previous state (because an arp reply in the input code - * may cause a call-back to ed_start) - * XXX - the call-back to 'start' is a bug, IMHO. - */ - case ED_VENDOR_WD_SMC: { - outb(sc->asic_addr + ED_WD_LAAR, - (sc->wd_laar_proto | ED_WD_LAAR_M16EN)); - (void) inb(0x84); - if (sc->is790) { - outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + + /* + * Enable 16bit access to shared memory on + * WD/SMC boards Don't update wd_laar_proto + * because we want to restore the previous + * state (because an arp reply in the input + * code may cause a call-back to ed_start) XXX + * - the call-back to 'start' is a bug, IMHO. + */ + case ED_VENDOR_WD_SMC:{ + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto | ED_WD_LAAR_M16EN)); (void) inb(0x84); + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, ED_WD_MSR_MENB); + (void) inb(0x84); + } + (void) inb(0x84); + break; } - (void) inb(0x84); - break; - } } } - for (len = 0; m != 0; m = m->m_next) { bcopy(mtod(m, caddr_t), buffer, m->m_len); buffer += m->m_len; - len += m->m_len; + len += m->m_len; } /* @@ -1484,23 +1653,23 @@ outloop: switch (sc->vendor) { case ED_VENDOR_3COM: outb(sc->asic_addr + ED_3COM_GACFR, - ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); + ED_3COM_GACFR_RSEL | ED_3COM_GACFR_MBS0); break; - case ED_VENDOR_WD_SMC: { - outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); - (void) inb(0x84); - if (sc->is790) { - outb(sc->asic_addr + ED_WD_MSR, 0x00); + case ED_VENDOR_WD_SMC:{ + if (sc->is790) { + outb(sc->asic_addr + ED_WD_MSR, 0x00); + (void) inb(0x84); + } + outb(sc->asic_addr + ED_WD_LAAR, sc->wd_laar_proto); (void) inb(0x84); + break; } - break; - } } } } else { len = ed_pio_write_mbufs(sc, m, buffer); } - + sc->txb_len[sc->txb_new] = MAX(len, ETHER_MIN_LEN); sc->txb_inuse++; @@ -1514,40 +1683,37 @@ outloop: if (sc->xmit_busy == 0) ed_xmit(ifp); + /* - * If there is BPF support in the configuration, tap off here. - * The following has support for converting trailer packets - * back to normal. - * XXX - support for trailer packets in BPF should be moved into - * the bpf code proper to avoid code duplication in all of - * the drivers. + * If there is BPF support in the configuration, tap off here. The + * following has support for converting trailer packets back to + * normal. XXX - support for trailer packets in BPF should be moved + * into the bpf code proper to avoid code duplication in all of the + * drivers. */ #if NBPFILTER > 0 if (sc->bpf) { u_short etype; - int off, datasize, resid; + int off, datasize, resid; struct ether_header *eh; struct trailer_header trailer_header; - char ether_packet[ETHER_MAX_LEN]; - char *ep; + char ether_packet[ETHER_MAX_LEN]; + char *ep; ep = ether_packet; /* - * We handle trailers below: - * Copy ether header first, then residual data, - * then data. Put all this in a temporary buffer - * 'ether_packet' and send off to bpf. Since the - * system has generated this packet, we assume - * that all of the offsets in the packet are - * correct; if they're not, the system will almost - * certainly crash in m_copydata. - * We make no assumptions about how the data is - * arranged in the mbuf chain (i.e. how much - * data is in each mbuf, if mbuf clusters are - * used, etc.), which is why we use m_copydata - * to get the ether header rather than assume - * that this is located in the first mbuf. + * We handle trailers below: Copy ether header first, then + * residual data, then data. Put all this in a temporary + * buffer 'ether_packet' and send off to bpf. Since the system + * has generated this packet, we assume that all of the + * offsets in the packet are correct; if they're not, the + * system will almost certainly crash in m_copydata. We make + * no assumptions about how the data is arranged in the mbuf + * chain (i.e. how much data is in each mbuf, if mbuf clusters + * are used, etc.), which is why we use m_copydata to get the + * ether header rather than assume that this is located in the + * first mbuf. */ /* copy ether header */ m_copydata(m0, 0, sizeof(struct ether_header), ep); @@ -1555,17 +1721,17 @@ outloop: ep += sizeof(struct ether_header); etype = ntohs(eh->ether_type); if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { datasize = ((etype - ETHERTYPE_TRAIL) << 9); off = datasize + sizeof(struct ether_header); /* copy trailer_header into a data structure */ m_copydata(m0, off, sizeof(struct trailer_header), - (caddr_t)&trailer_header.ether_type); + (caddr_t) & trailer_header.ether_type); /* copy residual data */ - m_copydata(m0, off+sizeof(struct trailer_header), - resid = ntohs(trailer_header.ether_residual) - + m_copydata(m0, off + sizeof(struct trailer_header), + resid = ntohs(trailer_header.ether_residual) - sizeof(struct trailer_header), ep); ep += resid; @@ -1590,35 +1756,36 @@ outloop: */ goto outloop; } - + /* * Ethernet interface receiver interrupt. */ static inline void ed_rint(unit) - int unit; + int unit; { register struct ed_softc *sc = &ed_softc[unit]; - u_char boundry, current; + u_char boundry, current; u_short len; struct ed_ring packet_hdr; - char *packet_ptr; + char *packet_ptr; /* * Set NIC to page 1 registers to get 'current' pointer */ if (sc->is790) { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STA); } + /* - * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e. - * it points to where new data has been buffered. The 'CURR' - * (current) register points to the logical end of the ring-buffer - * - i.e. it points to where additional new data will be added. - * We loop here until the logical beginning equals the logical - * end (or in other words, until the ring-buffer is empty). + * 'sc->next_packet' is the logical beginning of the ring-buffer - + * i.e. it points to where new data has been buffered. The 'CURR' + * (current) register points to the logical end of the ring-buffer - + * i.e. it points to where additional new data will be added. We loop + * here until the logical beginning equals the logical end (or in + * other words, until the ring-buffer is empty). */ while (sc->next_packet != inb(sc->nic_addr + ED_P1_CURR)) { @@ -1628,25 +1795,28 @@ ed_rint(unit) /* * The byte count includes the FCS - Frame Check Sequence (a - * 32 bit CRC). + * 32 bit CRC). */ if (sc->mem_shared) - packet_hdr = *(struct ed_ring *)packet_ptr; + packet_hdr = *(struct ed_ring *) packet_ptr; else ed_pio_readmem(sc, packet_ptr, (char *) &packet_hdr, - sizeof(packet_hdr)); + sizeof(packet_hdr)); len = packet_hdr.count; if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) { + /* * Go get packet. len - 4 removes CRC from length. */ ed_get_packet(sc, packet_ptr + 4, len - 4); ++sc->arpcom.ac_if.if_ipackets; } else { + /* - * Really BAD...probably indicates that the ring pointers - * are corrupted. Also seen on early rev chips under - * high load - the byte order of the length gets switched. + * Really BAD...probably indicates that the ring + * pointers are corrupted. Also seen on early rev + * chips under high load - the byte order of the + * length gets switched. */ log(LOG_ERR, "ed%d: NIC memory corrupt - invalid packet length %d\n", @@ -1662,8 +1832,8 @@ ed_rint(unit) sc->next_packet = packet_hdr.next_packet; /* - * Update NIC boundry pointer - being careful to keep it - * one buffer behind. (as recommended by NS databook) + * Update NIC boundry pointer - being careful to keep it one + * buffer behind. (as recommended by NS databook) */ boundry = sc->next_packet - 1; if (boundry < sc->rec_page_start) @@ -1675,18 +1845,18 @@ ed_rint(unit) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } outb(sc->nic_addr + ED_P0_BNRY, boundry); /* - * Set NIC to page 1 registers before looping to top (prepare to - * get 'CURR' current pointer) + * Set NIC to page 1 registers before looping to top (prepare + * to get 'CURR' current pointer) */ if (sc->is790) { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1|ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_PAGE_1 | ED_CR_RD2 | ED_CR_STA); } } } @@ -1696,10 +1866,10 @@ ed_rint(unit) */ void edintr(unit) - int unit; + int unit; { struct ed_softc *sc = &ed_softc[unit]; - u_char isr; + u_char isr; /* * Set NIC to page 0 registers @@ -1707,36 +1877,36 @@ edintr(unit) if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } + /* * loop until there are no more new interrupts */ while (isr = inb(sc->nic_addr + ED_P0_ISR)) { /* - * reset all the bits that we are 'acknowledging' - * by writing a '1' to each bit position that was set - * (writing a '1' *clears* the bit) + * reset all the bits that we are 'acknowledging' by writing a + * '1' to each bit position that was set (writing a '1' + * *clears* the bit) */ outb(sc->nic_addr + ED_P0_ISR, isr); /* - * Handle transmitter interrupts. Handle these first - * because the receiver will reset the board under - * some conditions. + * Handle transmitter interrupts. Handle these first because + * the receiver will reset the board under some conditions. */ - if (isr & (ED_ISR_PTX|ED_ISR_TXE)) { - u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; + if (isr & (ED_ISR_PTX | ED_ISR_TXE)) { + u_char collisions = inb(sc->nic_addr + ED_P0_NCR) & 0x0f; /* * Check for transmit error. If a TX completed with an * error, we end up throwing the packet away. Really * the only error that is possible is excessive - * collisions, and in this case it is best to allow the - * automatic mechanisms of TCP to backoff the flow. Of - * course, with UDP we're screwed, but this is expected - * when a network is heavily loaded. + * collisions, and in this case it is best to allow + * the automatic mechanisms of TCP to backoff the + * flow. Of course, with UDP we're screwed, but this + * is expected when a network is heavily loaded. */ (void) inb(sc->nic_addr + ED_P0_TSR); if (isr & ED_ISR_TXE) { @@ -1746,8 +1916,9 @@ edintr(unit) */ if ((inb(sc->nic_addr + ED_P0_TSR) & ED_TSR_ABT) && (collisions == 0)) { + /* - * When collisions total 16, the + * When collisions total 16, the * P0_NCR will indicate 0, and the * TSR_ABT is set. */ @@ -1759,9 +1930,10 @@ edintr(unit) */ ++sc->arpcom.ac_if.if_oerrors; } else { + /* * Update total number of successfully - * transmitted packets. + * transmitted packets. */ ++sc->arpcom.ac_if.if_opackets; } @@ -1779,16 +1951,16 @@ edintr(unit) /* * Add in total number of collisions on last - * transmission. + * transmission. */ sc->arpcom.ac_if.if_collisions += collisions; /* * Decrement buffer in-use count if not zero (can only - * be zero if a transmitter interrupt occured while - * not actually transmitting). - * If data is ready to transmit, start it transmitting, - * otherwise defer until after handling receiver + * be zero if a transmitter interrupt occured while + * not actually transmitting). If data is ready to + * transmit, start it transmitting, otherwise defer + * until after handling receiver */ if (sc->txb_inuse && --sc->txb_inuse) ed_xmit(&sc->arpcom.ac_if); @@ -1797,110 +1969,111 @@ edintr(unit) /* * Handle receiver interrupts */ - if (isr & (ED_ISR_PRX|ED_ISR_RXE|ED_ISR_OVW)) { - /* - * Overwrite warning. In order to make sure that a lockup - * of the local DMA hasn't occurred, we reset and - * re-init the NIC. The NSC manual suggests only a - * partial reset/re-init is necessary - but some - * chips seem to want more. The DMA lockup has been - * seen only with early rev chips - Methinks this - * bug was fixed in later revs. -DG - */ + if (isr & (ED_ISR_PRX | ED_ISR_RXE | ED_ISR_OVW)) { + + /* + * Overwrite warning. In order to make sure that a + * lockup of the local DMA hasn't occurred, we reset + * and re-init the NIC. The NSC manual suggests only a + * partial reset/re-init is necessary - but some chips + * seem to want more. The DMA lockup has been seen + * only with early rev chips - Methinks this bug was + * fixed in later revs. -DG + */ if (isr & ED_ISR_OVW) { ++sc->arpcom.ac_if.if_ierrors; #ifdef DIAGNOSTIC log(LOG_WARNING, - "ed%d: warning - receiver ring buffer overrun\n", - unit); + "ed%d: warning - receiver ring buffer overrun\n", + unit); #endif + /* * Stop/reset/re-init NIC */ ed_reset(unit, 0); } else { - /* - * Receiver Error. One or more of: CRC error, frame - * alignment error FIFO overrun, or missed packet. - */ + /* + * Receiver Error. One or more of: CRC error, + * frame alignment error FIFO overrun, or + * missed packet. + */ if (isr & ED_ISR_RXE) { ++sc->arpcom.ac_if.if_ierrors; #ifdef ED_DEBUG printf("ed%d: receive error %x\n", unit, - inb(sc->nic_addr + ED_P0_RSR)); + inb(sc->nic_addr + ED_P0_RSR)); #endif } /* - * Go get the packet(s) - * XXX - Doing this on an error is dubious - * because there shouldn't be any data to - * get (we've configured the interface to - * not accept packets with errors). + * Go get the packet(s) XXX - Doing this on an + * error is dubious because there shouldn't be + * any data to get (we've configured the + * interface to not accept packets with + * errors). */ /* * Enable 16bit access to shared memory first - * on WD/SMC boards. + * on WD/SMC boards. */ if (sc->isa16bit && (sc->vendor == ED_VENDOR_WD_SMC)) { outb(sc->asic_addr + ED_WD_LAAR, (sc->wd_laar_proto |= - ED_WD_LAAR_M16EN)); + ED_WD_LAAR_M16EN)); (void) inb(0x84); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, - ED_WD_MSR_MENB); + ED_WD_MSR_MENB); (void) inb(0x84); } } - - ed_rint (unit); + ed_rint(unit); /* disable 16bit access */ if (sc->isa16bit && - (sc->vendor == ED_VENDOR_WD_SMC)) { + (sc->vendor == ED_VENDOR_WD_SMC)) { - outb(sc->asic_addr + ED_WD_LAAR, - (sc->wd_laar_proto &= - ~ED_WD_LAAR_M16EN)); - (void) inb(0x84); if (sc->is790) { outb(sc->asic_addr + ED_WD_MSR, 0x00); (void) inb(0x84); } + outb(sc->asic_addr + ED_WD_LAAR, + (sc->wd_laar_proto &= + ~ED_WD_LAAR_M16EN)); + (void) inb(0x84); } } } /* * If it looks like the transmitter can take more data, - * attempt to start output on the interface. - * This is done after handling the receiver to - * give the receiver priority. + * attempt to start output on the interface. This is done + * after handling the receiver to give the receiver priority. */ if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) ed_start(&sc->arpcom.ac_if); /* - * return NIC CR to standard state: page 0, remote DMA complete, - * start (toggling the TXP bit off, even if was just set - * in the transmit routine, is *okay* - it is 'edge' - * triggered from low to high) + * return NIC CR to standard state: page 0, remote DMA + * complete, start (toggling the TXP bit off, even if was just + * set in the transmit routine, is *okay* - it is 'edge' + * triggered from low to high) */ if (sc->is790) { outb(sc->nic_addr + ED_P0_CR, ED_CR_STA); } else { - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); } + /* - * If the Network Talley Counters overflow, read them to - * reset them. It appears that old 8390's won't - * clear the ISR flag otherwise - resulting in an - * infinite loop. + * If the Network Talley Counters overflow, read them to reset + * them. It appears that old 8390's won't clear the ISR flag + * otherwise - resulting in an infinite loop. */ if (isr & ED_ISR_CNT) { (void) inb(sc->nic_addr + ED_P0_CNTR0); @@ -1909,7 +2082,7 @@ edintr(unit) } } } - + /* * Process an ioctl request. This code needs some work - it looks * pretty ugly. @@ -1917,13 +2090,13 @@ edintr(unit) int ed_ioctl(ifp, command, data) register struct ifnet *ifp; - int command; + int command; caddr_t data; { - register struct ifaddr *ifa = (struct ifaddr *)data; + register struct ifaddr *ifa = (struct ifaddr *) data; struct ed_softc *sc = &ed_softc[ifp->if_unit]; - struct ifreq *ifr = (struct ifreq *)data; - int s, error = 0; + struct ifreq *ifr = (struct ifreq *) data; + int s, error = 0; s = splimp(); @@ -1936,41 +2109,40 @@ ed_ioctl(ifp, command, data) #ifdef INET case AF_INET: ed_init(ifp->if_unit); /* before arpwhohas */ + /* - * See if another station has *our* IP address. - * i.e.: There is an address conflict! If a - * conflict exists, a message is sent to the - * console. + * See if another station has *our* IP address. i.e.: + * There is an address conflict! If a conflict exists, + * a message is sent to the console. */ - ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; - arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + ((struct arpcom *) ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *) ifp, &IA_SIN(ifa)->sin_addr); break; #endif #ifdef NS - /* - * XXX - This code is probably wrong - */ + + /* + * XXX - This code is probably wrong + */ 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); - else { - /* - * + { + register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr); + + if (ns_nullhost(*ina)) + ina->x_host = + *(union ns_host *) (sc->arpcom.ac_enaddr); + else { + bcopy((caddr_t) ina->x_host.c_host, + (caddr_t) sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + + /* + * Set new address */ - bcopy((caddr_t)ina->x_host.c_host, - (caddr_t)sc->arpcom.ac_enaddr, - sizeof(sc->arpcom.ac_enaddr)); + ed_init(ifp->if_unit); + break; } - /* - * Set new address - */ - ed_init(ifp->if_unit); - break; - } #endif default: ed_init(ifp->if_unit); @@ -1981,13 +2153,15 @@ ed_ioctl(ifp, command, data) 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); + + 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 interface is marked down and it is running, then stop it */ @@ -1996,37 +2170,42 @@ ed_ioctl(ifp, command, data) ed_stop(ifp->if_unit); ifp->if_flags &= ~IFF_RUNNING; } else { - /* - * If interface is marked up and it is stopped, then start it - */ + + /* + * If interface is marked up and it is stopped, then + * start it + */ if ((ifp->if_flags & IFF_UP) && - ((ifp->if_flags & IFF_RUNNING) == 0)) + ((ifp->if_flags & IFF_RUNNING) == 0)) ed_init(ifp->if_unit); } +#ifndef MULTICAST #if NBPFILTER > 0 if (ifp->if_flags & IFF_PROMISC) { + /* - * Set promiscuous mode on interface. - * XXX - for multicasts to work, we would need to - * write 1's in all bits of multicast - * hashing array. For now we assume that - * this was done in ed_init(). + * Set promiscuous mode on interface. XXX - for + * multicasts to work, we would need to write 1's in + * all bits of multicast hashing array. For now we + * assume that this was done in ed_init(). */ outb(sc->nic_addr + ED_P0_RCR, - ED_RCR_PRO|ED_RCR_AM|ED_RCR_AB); + ED_RCR_PRO | ED_RCR_AM | ED_RCR_AB); } else { + /* * XXX - for multicasts to work, we would need to - * rewrite the multicast hashing array with the - * proper hash (would have been destroyed above). + * rewrite the multicast hashing array with the proper + * hash (would have been destroyed above). */ outb(sc->nic_addr + ED_P0_RCR, ED_RCR_AB); } #endif + /* - * An unfortunate hack to provide the (required) software control - * of the tranceiver for 3Com boards. The ALTPHYS flag disables - * the tranceiver if set. + * An unfortunate hack to provide the (required) software + * control of the tranceiver for 3Com boards. The ALTPHYS flag + * disables the tranceiver if set. */ if (sc->vendor == ED_VENDOR_3COM) { if (ifp->if_flags & IFF_ALTPHYS) { @@ -2035,16 +2214,37 @@ ed_ioctl(ifp, command, data) outb(sc->asic_addr + ED_3COM_CR, ED_3COM_CR_XSEL); } } - break; +#else + case SIOCADDMULTI: + case SIOCDELMULTI: + + /* + * Update out multicast list. + */ + error = (command == SIOCADDMULTI) ? + ether_addmulti((struct ifreq *) data, &sc->arpcom) : + ether_delmulti((struct ifreq *) data, &sc->arpcom); + if (error == ENETRESET) { + + /* + * Multicast list has changed; set the hardware filter + * accordingly. + */ + ed_stop(ifp->if_unit); /* XXX for ds_setmcaf? */ + ed_init(ifp->if_unit); + error = 0; + } + break; +#endif default: error = EINVAL; } (void) splx(s); return (error); } - + /* * Macro to calculate a new address within shared memory when given an offset * from an address, taking into account ring-wrap. @@ -2062,13 +2262,13 @@ ed_ioctl(ifp, command, data) static void ed_get_packet(sc, buf, len) struct ed_softc *sc; - char *buf; + char *buf; u_short len; { struct ether_header *eh; - struct mbuf *m, *head = 0, *ed_ring_to_mbuf(); + struct mbuf *m, *head = 0, *ed_ring_to_mbuf(); u_short off; - int resid; + int resid; u_short etype; struct trailer_header trailer_header; @@ -2086,8 +2286,8 @@ ed_get_packet(sc, buf, len) #define EOFF (EROUND - sizeof(struct ether_header)) /* - * The following assumes there is room for - * the ether header in the header mbuf + * The following assumes there is room for the ether header in the + * header mbuf */ head->m_data += EOFF; eh = mtod(head, struct ether_header *); @@ -2101,18 +2301,16 @@ ed_get_packet(sc, buf, len) head->m_len += sizeof(struct ether_header); len -= sizeof(struct ether_header); - etype = ntohs((u_short)eh->ether_type); + etype = ntohs((u_short) eh->ether_type); /* - * Deal with trailer protocol: - * If trailer protocol, calculate the datasize as 'off', - * which is also the offset to the trailer header. - * Set resid to the amount of packet data following the - * trailer header. - * Finally, copy residual data into mbuf chain. + * Deal with trailer protocol: If trailer protocol, calculate the + * datasize as 'off', which is also the offset to the trailer header. + * Set resid to the amount of packet data following the trailer + * header. Finally, copy residual data into mbuf chain. */ if (etype >= ETHERTYPE_TRAIL && - etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + etype < ETHERTYPE_TRAIL + ETHERTYPE_NTRAILER) { off = (etype - ETHERTYPE_TRAIL) << 9; if ((off + sizeof(struct trailer_header)) > len) @@ -2120,14 +2318,15 @@ ed_get_packet(sc, buf, len) /* * If we have shared memory, we can get info directly from the - * stored packet, otherwise we must get a local copy - * of the trailer header using PIO. + * stored packet, otherwise we must get a local copy of the + * trailer header using PIO. */ if (sc->mem_shared) { eh->ether_type = *ringoffset(sc, buf, off, u_short *); - resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); + resid = ntohs(*ringoffset(sc, buf, off + 2, u_short *)); } else { struct trailer_header trailer_header; + ed_pio_readmem(sc, ringoffset(sc, buf, off, caddr_t), (char *) &trailer_header, @@ -2136,30 +2335,35 @@ ed_get_packet(sc, buf, len) resid = trailer_header.ether_residual; } - if ((off + resid) > len) goto bad; /* insanity */ + if ((off + resid) > len) + goto bad; /* insanity */ resid -= sizeof(struct trailer_header); - if (resid < 0) goto bad; /* insanity */ + if (resid < 0) + goto bad; /* insanity */ - m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), + m = ed_ring_to_mbuf(sc, ringoffset(sc, buf, off + 4, char *), head, resid); - if (m == 0) goto bad; + if (m == 0) + goto bad; len = off; - head->m_pkthdr.len -= 4; /* subtract trailer header */ + head->m_pkthdr.len -= 4; /* subtract trailer header */ } /* - * Pull packet off interface. Or if this was a trailer packet, - * the data portion is appended. + * Pull packet off interface. Or if this was a trailer packet, the + * data portion is appended. */ m = ed_ring_to_mbuf(sc, buf, m, len); - if (m == 0) goto bad; + if (m == 0) + goto bad; #if NBPFILTER > 0 + /* - * Check if there's a BPF listener on this interface. - * If so, hand off the raw packet to bpf. + * Check if there's a BPF listener on this interface. If so, hand off + * the raw packet to bpf. */ if (sc->bpf) { bpf_mtap(sc->bpf, head); @@ -2168,17 +2372,17 @@ ed_get_packet(sc, buf, len) * 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 ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && bcmp(eh->ether_dhost, sc->arpcom.ac_enaddr, - sizeof(eh->ether_dhost)) != 0 && + sizeof(eh->ether_dhost)) != 0 && bcmp(eh->ether_dhost, etherbroadcastaddr, sizeof(eh->ether_dhost)) != 0) { - m_freem(head); - return; + m_freem(head); + return; } } #endif @@ -2213,8 +2417,8 @@ bad: if (head) * This routine is currently Novell-specific. */ void -ed_pio_readmem(sc,src,dst,amount) - struct ed_softc *sc; +ed_pio_readmem(sc, src, dst, amount) + struct ed_softc *sc; unsigned short src; unsigned char *dst; unsigned short amount; @@ -2222,24 +2426,25 @@ ed_pio_readmem(sc,src,dst,amount) unsigned short tmp_amount; /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* round up to a word */ tmp_amount = amount; - if (amount & 1) ++amount; + if (amount & 1) + ++amount; /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, amount); - outb(sc->nic_addr + ED_P0_RBCR1, amount>>8); + outb(sc->nic_addr + ED_P0_RBCR1, amount >> 8); /* set up source address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, src); - outb(sc->nic_addr + ED_P0_RSAR1, src>>8); + outb(sc->nic_addr + ED_P0_RSAR1, src >> 8); outb(sc->nic_addr + ED_P0_CR, ED_CR_RD0 | ED_CR_STA); if (sc->isa16bit) { - insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount/2); + insw(sc->asic_addr + ED_NOVELL_DATA, dst, amount / 2); } else insb(sc->asic_addr + ED_NOVELL_DATA, dst, amount); @@ -2251,41 +2456,42 @@ ed_pio_readmem(sc,src,dst,amount) * be even. */ void -ed_pio_writemem(sc,src,dst,len) +ed_pio_writemem(sc, src, dst, len) struct ed_softc *sc; - char *src; + char *src; unsigned short dst; unsigned short len; { - int maxwait=100; /* about 120us */ + int maxwait = 100; /* about 120us */ /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, len); - outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); - outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); if (sc->isa16bit) - outsw(sc->asic_addr + ED_NOVELL_DATA, src, len/2); + outsw(sc->asic_addr + ED_NOVELL_DATA, src, len / 2); else outsb(sc->asic_addr + ED_NOVELL_DATA, src, len); + /* * Wait for remote DMA complete. This is necessary because on the - * transmit side, data is handled internally by the NIC in bursts - * and we can't start another remote DMA until this one completes. - * Not waiting causes really bad things to happen - like the NIC - * irrecoverably jamming the ISA bus. + * transmit side, data is handled internally by the NIC in bursts and + * we can't start another remote DMA until this one completes. Not + * waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); } @@ -2295,7 +2501,7 @@ ed_pio_writemem(sc,src,dst,len) * programmed I/O. */ u_short -ed_pio_write_mbufs(sc,m,dst) +ed_pio_write_mbufs(sc, m, dst) struct ed_softc *sc; struct mbuf *m; unsigned short dst; @@ -2303,40 +2509,40 @@ ed_pio_write_mbufs(sc,m,dst) unsigned short len, mb_offset; struct mbuf *mp; unsigned char residual[2]; - int maxwait=100; /* about 120us */ + int maxwait = 100; /* about 120us */ /* First, count up the total number of bytes to copy */ for (len = 0, mp = m; mp; mp = mp->m_next) len += mp->m_len; - + /* select page 0 registers */ - outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2|ED_CR_STA); + outb(sc->nic_addr + ED_P0_CR, ED_CR_RD2 | ED_CR_STA); /* reset remote DMA complete flag */ outb(sc->nic_addr + ED_P0_ISR, ED_ISR_RDC); /* set up DMA byte count */ outb(sc->nic_addr + ED_P0_RBCR0, len); - outb(sc->nic_addr + ED_P0_RBCR1, len>>8); + outb(sc->nic_addr + ED_P0_RBCR1, len >> 8); /* set up destination address in NIC mem */ outb(sc->nic_addr + ED_P0_RSAR0, dst); - outb(sc->nic_addr + ED_P0_RSAR1, dst>>8); + outb(sc->nic_addr + ED_P0_RSAR1, dst >> 8); /* set remote DMA write */ outb(sc->nic_addr + ED_P0_CR, ED_CR_RD1 | ED_CR_STA); mb_offset = 0; + /* - * Transfer the mbuf chain to the NIC memory. - * The following code isn't too pretty. The problem is that we can only - * transfer words to the board, and if an mbuf has an odd number - * of bytes in it, this is a problem. It's not a simple matter of - * just removing a byte from the next mbuf (adjusting data++ and - * len--) because this will hose-over the mbuf chain which might - * be needed later for BPF. Instead, we maintain an offset - * (mb_offset) which let's us skip over the first byte in the - * following mbuf. + * Transfer the mbuf chain to the NIC memory. The following code isn't + * too pretty. The problem is that we can only transfer words to the + * board, and if an mbuf has an odd number of bytes in it, this is a + * problem. It's not a simple matter of just removing a byte from the + * next mbuf (adjusting data++ and len--) because this will hose-over + * the mbuf chain which might be needed later for BPF. Instead, we + * maintain an offset (mb_offset) which let's us skip over the first + * byte in the following mbuf. */ while (m) { if (m->m_len - mb_offset) { @@ -2347,24 +2553,27 @@ ed_pio_write_mbufs(sc,m,dst) (m->m_len - mb_offset) / 2); /* - * if odd number of bytes, get the odd byte from - * the next mbuf with data + * if odd number of bytes, get the odd byte + * from the next mbuf with data */ if ((m->m_len - mb_offset) & 1) { /* first the last byte in current mbuf */ residual[0] = *(mtod(m, caddr_t) + - m->m_len - 1); - + m->m_len - 1); + /* advance past any empty mbufs */ while (m->m_next && (m->m_next->m_len == 0)) m = m->m_next; if (m->m_next) { - /* remove first byte in next mbuf */ + + /* + * remove first byte in next + * mbuf + */ residual[1] = *(mtod(m->m_next, caddr_t)); mb_offset = 1; } - outw(sc->asic_addr + ED_NOVELL_DATA, *((unsigned short *) residual)); } else @@ -2378,10 +2587,10 @@ ed_pio_write_mbufs(sc,m,dst) /* * Wait for remote DMA complete. This is necessary because on the - * transmit side, data is handled internally by the NIC in bursts - * and we can't start another remote DMA until this one completes. - * Not waiting causes really bad things to happen - like the NIC - * irrecoverably jamming the ISA bus. + * transmit side, data is handled internally by the NIC in bursts and + * we can't start another remote DMA until this one completes. Not + * waiting causes really bad things to happen - like the NIC + * irrecoverably jamming the ISA bus. */ while (((inb(sc->nic_addr + ED_P0_ISR) & ED_ISR_RDC) != ED_ISR_RDC) && --maxwait); @@ -2390,23 +2599,22 @@ ed_pio_write_mbufs(sc,m,dst) sc->arpcom.ac_if.if_unit); ed_reset(sc->arpcom.ac_if.if_unit, 0); } - - return(len); + return (len); } - + /* * Given a source and destination address, copy 'amount' of a packet from * the ring buffer into a linear destination buffer. Takes into account * ring-wrap. */ static inline char * -ed_ring_copy(sc,src,dst,amount) +ed_ring_copy(sc, src, dst, amount) struct ed_softc *sc; - char *src; - char *dst; - u_short amount; + char *src; + char *dst; + u_short amount; { - u_short tmp_amount; + u_short tmp_amount; /* does copy wrap to lower addr in ring buffer? */ if (src + amount > sc->mem_end) { @@ -2414,21 +2622,20 @@ ed_ring_copy(sc,src,dst,amount) /* copy amount up to end of NIC memory */ if (sc->mem_shared) - bcopy(src,dst,tmp_amount); + bcopy(src, dst, tmp_amount); else - ed_pio_readmem(sc,src,dst,tmp_amount); + ed_pio_readmem(sc, src, dst, tmp_amount); amount -= tmp_amount; src = sc->mem_ring; dst += tmp_amount; } - if (sc->mem_shared) bcopy(src, dst, amount); else ed_pio_readmem(sc, src, dst, amount); - return(src + amount); + return (src + amount); } /* @@ -2441,9 +2648,9 @@ ed_ring_copy(sc,src,dst,amount) * amount = amount of data to copy */ struct mbuf * -ed_ring_to_mbuf(sc,src,dst,total_len) +ed_ring_to_mbuf(sc, src, dst, total_len) struct ed_softc *sc; - char *src; + char *src; struct mbuf *dst; u_short total_len; { @@ -2452,18 +2659,19 @@ ed_ring_to_mbuf(sc,src,dst,total_len) while (total_len) { register u_short amount = min(total_len, M_TRAILINGSPACE(m)); - if (amount == 0) { /* no more data in this mbuf, alloc another */ + if (amount == 0) { /* no more data in this mbuf, alloc + * another */ + /* - * If there is enough data for an mbuf cluster, attempt - * to allocate one of those, otherwise, a regular - * mbuf will do. - * Note that a regular mbuf is always required, even if - * we get a cluster - getting a cluster does not - * allocate any mbufs, and one is needed to assign - * the cluster to. The mbuf that has a cluster - * extension can not be used to contain data - only - * the cluster can contain data. - */ + * If there is enough data for an mbuf cluster, + * attempt to allocate one of those, otherwise, a + * regular mbuf will do. Note that a regular mbuf is + * always required, even if we get a cluster - getting + * a cluster does not allocate any mbufs, and one is + * needed to assign the cluster to. The mbuf that has + * a cluster extension can not be used to contain data + * - only the cluster can contain data. + */ dst = m; MGET(m, M_DONTWAIT, MT_DATA); if (m == 0) @@ -2476,7 +2684,6 @@ ed_ring_to_mbuf(sc,src,dst,total_len) dst->m_next = m; amount = min(total_len, M_TRAILINGSPACE(m)); } - src = ed_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); m->m_len += amount; @@ -2485,4 +2692,62 @@ ed_ring_to_mbuf(sc,src,dst,total_len) } return (m); } +#ifdef MULTICAST +/* + * Compute crc for ethernet address + */ +u_long +ds_crc(ep) + u_char *ep; +{ +#define POLYNOMIAL 0x04c11db6 + register u_long crc = 0xffffffffL; + register int carry, i, j; + register u_char b; + + for (i = 6; --i >= 0;) { + b = *ep++; + for (j = 8; --j >= 0;) { + carry = ((crc & 0x80000000L) ? 1 : 0) ^ (b & 0x01); + crc <<= 1; + b >>= 1; + if (carry) + crc = ((crc ^ POLYNOMIAL) | carry); + } + } + return crc; +#undef POLYNOMIAL +} + +/* + * Compute the multicast address filter from the + * list of multicast addresses we need to listen to. + */ +void +ds_getmcaf(sc, mcaf) + struct ed_softc *sc; + u_long *mcaf; +{ + register u_int index; + register u_char *af = (u_char *) mcaf; + register struct ether_multi *enm; + register struct ether_multistep step; + + mcaf[0] = 0; + mcaf[1] = 0; + + ETHER_FIRST_MULTI(step, &sc->arpcom, enm); + while (enm != NULL) { + if (bcmp(enm->enm_addrlo, enm->enm_addrhi, 6) != 0) { + mcaf[0] = 0xffffffff; + mcaf[1] = 0xffffffff; + return; + } + index = ds_crc(enm->enm_addrlo, 6) >> 26; + af[index >> 3] |= 1 << (index & 7); + + ETHER_NEXT_MULTI(step, enm); + } +} +#endif #endif diff --git a/sys/i386/isa/if_edreg.h b/sys/i386/isa/if_edreg.h index 49f7816a4d4e..d2a03f5b508b 100644 --- a/sys/i386/isa/if_edreg.h +++ b/sys/i386/isa/if_edreg.h @@ -1,7 +1,7 @@ /* * National Semiconductor DS8390 NIC register definitions * - * $Id: if_edreg.h,v 1.13.2.1 1994/04/17 06:07:24 rgrimes Exp $ + * $Id: if_edreg.h,v 1.14 1994/04/10 20:06:28 davidg Exp $ * * Modification history * diff --git a/sys/i386/isa/if_el.c b/sys/i386/isa/if_el.c new file mode 100644 index 000000000000..d0de274baf73 --- /dev/null +++ b/sys/i386/isa/if_el.c @@ -0,0 +1,800 @@ +/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted + * to use, copy, modify and distribute this software provided that both + * the copyright notice and this permission notice appear in all copies + * of the software, derivative works or modified versions, and any + * portions thereof. + * + * Questions, comments, bug reports and fixes to kimmel@cs.umass.edu. + */ +/* Except of course for the portions of code lifted from other FreeBSD + * drivers (mainly elread, elget and el_ioctl) + */ +/* 3COM Etherlink 3C501 device driver for FreeBSD */ +/* Yeah, I know these cards suck, but you can also get them for free + * really easily... + */ +/* Bugs/possible improvements: + * - Does not currently support DMA + * - Does not currently support multicasts + */ +#include "el.h" +#if NEL > 0 +#include "bpfilter.h" + +#include "param.h" +#include "systm.h" +#include "errno.h" +#include "ioctl.h" +#include "mbuf.h" +#include "socket.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" +#include "i386/isa/if_elreg.h" + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 + +/* For debugging convenience */ +#ifdef EL_DEBUG +#define dprintf(x) printf x +#else +#define dprintf(x) +#endif + +/* el_softc: per line info and status */ +struct el_softc { + struct arpcom arpcom; /* Ethernet common */ + u_short el_base; /* Base I/O addr */ + caddr_t bpf; /* BPF magic cookie */ + char el_pktbuf[EL_BUFSIZ]; /* Frame buffer */ +} el_softc[NEL]; + +/* Prototypes */ +int el_attach(struct isa_device *); +void el_init(int); +void elintr(int); +int el_ioctl(struct ifnet *,int,caddr_t); +int el_probe(struct isa_device *); +void el_start(struct ifnet *); +void el_reset(int,int); +void el_watchdog(int); + +static void el_stop(int); +static int el_xmit(struct el_softc *,int); +static inline void elread(struct el_softc *,caddr_t,int); +static struct mbuf *elget(caddr_t,int,int,struct ifnet *); +static inline void el_hardreset(int); + +/* isa_driver structure for autoconf */ +struct isa_driver eldriver = { + el_probe, el_attach, "el" +}; + +/* Probe routine. See if the card is there and at the right place. */ +int el_probe(struct isa_device *idev) +{ + struct el_softc *sc; + u_short base; /* Just for convenience */ + u_char station_addr[ETHER_ADDR_LEN]; + int i; + + /* Grab some info for our structure */ + sc = &el_softc[idev->id_unit]; + sc->el_base = idev->id_iobase; + base = sc->el_base; + + /* First check the base */ + if((base < 0x280) || (base > 0x3f0)) { + printf("el%d: ioaddr must be between 0x280 and 0x3f0\n", + idev->id_unit); + return(0); + } + + /* Now attempt to grab the station address from the PROM + * and see if it contains the 3com vendor code. + */ + dprintf(("Probing 3c501 at 0x%x...\n",base)); + + /* Reset the board */ + dprintf(("Resetting board...\n")); + outb(base+EL_AC,EL_AC_RESET); + DELAY(5); + outb(base+EL_AC,0); + dprintf(("Reading station address...\n")); + /* Now read the address */ + for(i=0;i<ETHER_ADDR_LEN;i++) { + outb(base+EL_GPBL,i); + station_addr[i] = inb(base+EL_EAW); + } + dprintf(("Address is %s\n",ether_sprintf(station_addr))); + + /* If the vendor code is ok, return a 1. We'll assume that + * whoever configured this system is right about the IRQ. + */ + if((station_addr[0] != 0x02) || (station_addr[1] != 0x60) + || (station_addr[2] != 0x8c)) { + dprintf(("Bad vendor code.\n")); + return(0); + } else { + dprintf(("Vendor code ok.\n")); + /* Copy the station address into the arpcom structure */ + bcopy(station_addr,sc->arpcom.ac_enaddr,ETHER_ADDR_LEN); + return(1); + } +} + +/* Attach the interface to the kernel data structures. By the time + * this is called, we know that the card exists at the given I/O address. + * We still assume that the IRQ given is correct. + */ +int el_attach(struct isa_device *idev) +{ + struct el_softc *sc; + struct ifnet *ifp; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + u_short base; + int t; + + dprintf(("Attaching el%d...\n",idev->id_unit)); + + /* Get things pointing to the right places. */ + sc = &el_softc[idev->id_unit]; + ifp = &sc->arpcom.ac_if; + base = sc->el_base; + + /* Now reset the board */ + dprintf(("Resetting board...\n")); + el_hardreset(idev->id_unit); + + /* Initialize ifnet structure */ + ifp->if_unit = idev->id_unit; + ifp->if_name = "el"; + ifp->if_mtu = ETHERMTU; + ifp->if_init = el_init; + ifp->if_output = ether_output; + ifp->if_start = el_start; + ifp->if_ioctl = el_ioctl; + ifp->if_reset = el_reset; + ifp->if_watchdog = el_watchdog; + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); + + /* Now we can attach the interface */ + dprintf(("Attaching interface...\n")); + if_attach(ifp); + + /* Put the station address in the ifa address list's AF_LINK + * entry, if any. + */ + ifa = ifp->if_addrlist; + while ((ifa != NULL) && (ifa->ifa_addr != NULL) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + if((ifa != NULL) && (ifa->ifa_addr != NULL)) { + 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); + } + + /* Print out some information for the user */ + printf("el%d: 3c501 address %s\n",idev->id_unit, + ether_sprintf(sc->arpcom.ac_enaddr)); + + /* Finally, attach to bpf filter if it is present. */ +#if NBPFILTER > 0 + dprintf(("Attaching to BPF...\n")); + bpfattach(&sc->bpf,ifp,DLT_EN10MB,sizeof(struct ether_header)); +#endif + + dprintf(("el_attach() finished.\n")); + return(1); +} + +/* This routine resets the interface. */ +void el_reset(int unit,int uban) +{ + int s; + + dprintf(("elreset()\n")); + s = splimp(); + el_stop(unit); + el_init(unit); + splx(s); +} + +static void el_stop(int unit) +{ + struct el_softc *sc; + + sc = &el_softc[unit]; + outb(sc->el_base+EL_AC,0); +} + +/* Do a hardware reset of the 3c501. Do not call until after el_probe()! */ +static inline void el_hardreset(int unit) +{ + register struct el_softc *sc; + register int base; + register int j; + + sc = &el_softc[unit]; + base = sc->el_base; + + /* First reset the board */ + outb(base+EL_AC,EL_AC_RESET); + DELAY(5); + outb(base+EL_AC,0); + + /* Then give it back its ethernet address. Thanks to the mach + * source code for this undocumented goodie... + */ + for(j=0;j<ETHER_ADDR_LEN;j++) + outb(base+j,sc->arpcom.ac_enaddr[j]); +} + +/* Initialize interface. */ +void el_init(int unit) +{ + struct el_softc *sc; + struct ifnet *ifp; + int s; + u_short base; + + /* Set up pointers */ + sc = &el_softc[unit]; + ifp = &sc->arpcom.ac_if; + base = sc->el_base; + + /* If address not known, do nothing. */ + if(ifp->if_addrlist == (struct ifaddr *)0) + return; + + s = splimp(); + + /* First, reset the board. */ + dprintf(("Resetting board...\n")); + el_hardreset(unit); + + /* Configure rx */ + dprintf(("Configuring rx...\n")); + if(ifp->if_flags & IFF_PROMISC) + outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + else + outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + outb(base+EL_RBC,0); + + /* Configure TX */ + dprintf(("Configuring tx...\n")); + outb(base+EL_TXC,0); + + /* Start reception */ + dprintf(("Starting reception...\n")); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + + /* Set flags appropriately */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* And start output. */ + el_start(ifp); + + splx(s); +} + +/* Start output on interface. Get datagrams from the queue and output + * them, giving the receiver a chance between datagrams. Call only + * from splimp or interrupt level! + */ +void el_start(struct ifnet *ifp) +{ + struct el_softc *sc; + u_short base; + struct mbuf *m, *m0; + int s, i, len, retries, done; + + /* Get things pointing in the right directions */ + sc = &el_softc[ifp->if_unit]; + base = sc->el_base; + + dprintf(("el_start()...\n")); + s = splimp(); + + /* Don't do anything if output is active */ + if(sc->arpcom.ac_if.if_flags & IFF_OACTIVE) + return; + sc->arpcom.ac_if.if_flags |= IFF_OACTIVE; + + /* The main loop. They warned me against endless loops, but + * would I listen? NOOO.... + */ + while(1) { + /* Dequeue the next datagram */ + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd,m0); + + /* If there's nothing to send, return. */ + if(m0 == NULL) { + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + splx(s); + return; + } + + /* Disable the receiver */ + outb(base+EL_AC,EL_AC_HOST); + outb(base+EL_RBC,0); + + /* Copy the datagram to the buffer. */ + len = 0; + for(m = m0; m != NULL; m = m->m_next) { + if(m->m_len == 0) + continue; + bcopy(mtod(m,caddr_t),sc->el_pktbuf+len,m->m_len); + len += m->m_len; + } + m_freem(m0); + + len = MAX(len,ETHER_MIN_LEN); + + /* Give the packet to the bpf, if any */ +#if NBPFILTER > 0 + if(sc->bpf) + bpf_tap(sc->bpf,sc->el_pktbuf,len); +#endif + + /* Transfer datagram to board */ + dprintf(("el: xfr pkt length=%d...\n",len)); + i = EL_BUFSIZ - len; + outb(base+EL_GPBL,(i & 0xff)); + outb(base+EL_GPBH,((i>>8)&0xff)); + outsb(base+EL_BUF,sc->el_pktbuf,len); + + /* Now transmit the datagram */ + retries=0; + done=0; + while(!done) { + if(el_xmit(sc,len)) { /* Something went wrong */ + done = -1; + break; + } + /* Check out status */ + i = inb(base+EL_TXS); + dprintf(("tx status=0x%x\n",i)); + if(!(i & EL_TXS_READY)) { + dprintf(("el: err txs=%x\n",i)); + sc->arpcom.ac_if.if_oerrors++; + if(i & (EL_TXS_COLL|EL_TXS_COLL16)) { + if((!(i & EL_TXC_DCOLL16)) && retries < 15) { + retries++; + outb(base+EL_AC,EL_AC_HOST); + } + } + else + done = 1; + } + else { + sc->arpcom.ac_if.if_opackets++; + done = 1; + } + } + if(done == -1) /* Packet not transmitted */ + continue; + + /* Now give the card a chance to receive. + * Gotta love 3c501s... + */ + (void)inb(base+EL_AS); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + splx(s); + /* Interrupt here */ + s = splimp(); + } +} + +/* This function actually attempts to transmit a datagram downloaded + * to the board. Call at splimp or interrupt, after downloading data! + * Returns 0 on success, non-0 on failure + */ +static int el_xmit(struct el_softc *sc,int len) +{ + int gpl; + int i; + + gpl = EL_BUFSIZ - len; + dprintf(("el: xmit...")); + outb((sc->el_base)+EL_GPBL,(gpl & 0xff)); + outb((sc->el_base)+EL_GPBH,((gpl>>8)&0xff)); + outb((sc->el_base)+EL_AC,EL_AC_TXFRX); + i = 20000; + while((inb((sc->el_base)+EL_AS) & EL_AS_TXBUSY) && (i>0)) + i--; + if(i == 0) { + dprintf(("tx not ready\n")); + sc->arpcom.ac_if.if_oerrors++; + return(-1); + } + dprintf(("%d cycles.\n",(20000-i))); + return(0); +} + +/* controller interrupt */ +void elintr(int unit) +{ + register struct el_softc *sc; + register base; + int stat, rxstat, len, done; + + /* Get things pointing properly */ + sc = &el_softc[unit]; + base = sc->el_base; + + dprintf(("elintr: ")); + + /* Check board status */ + stat = inb(base+EL_AS); + if(stat & EL_AS_RXBUSY) { + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + done = 0; + while(!done) { + rxstat = inb(base+EL_RXS); + if(rxstat & EL_RXS_STALE) { + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + /* If there's an overflow, reinit the board. */ + if(!(rxstat & EL_RXS_NOFLOW)) { + dprintf(("overflow.\n")); + el_hardreset(unit); + /* Put board back into receive mode */ + if(sc->arpcom.ac_if.if_flags & IFF_PROMISC) + outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + else + outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + (void)inb(base+EL_AS); + outb(base+EL_RBC,0); + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + /* Incoming packet */ + len = inb(base+EL_RBL); + len |= inb(base+EL_RBH) << 8; + dprintf(("receive len=%d rxstat=%x ",len,rxstat)); + outb(base+EL_AC,EL_AC_HOST); + + /* If packet too short or too long, restore rx mode and return + */ + if((len <= sizeof(struct ether_header)) || (len > ETHER_MAX_LEN)) { + if(sc->arpcom.ac_if.if_flags & IFF_PROMISC) + outb(base+EL_RXC,(EL_RXC_PROMISC|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + else + outb(base+EL_RXC,(EL_RXC_ABROAD|EL_RXC_AGF|EL_RXC_DSHORT|EL_RXC_DDRIB|EL_RXC_DOFLOW)); + (void)inb(base+EL_AS); + outb(base+EL_RBC,0); + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; + } + + sc->arpcom.ac_if.if_ipackets++; + + /* Copy the data into our buffer */ + outb(base+EL_GPBL,0); + outb(base+EL_GPBH,0); + insb(base+EL_BUF,sc->el_pktbuf,len); + outb(base+EL_RBC,0); + outb(base+EL_AC,EL_AC_RX); + dprintf(("%s-->",ether_sprintf(sc->el_pktbuf+6))); + dprintf(("%s\n",ether_sprintf(sc->el_pktbuf))); + + /* Pass data up to upper levels */ + len -= sizeof(struct ether_header); + elread(sc,(caddr_t)(sc->el_pktbuf),len); + + /* Is there another packet? */ + stat = inb(base+EL_AS); + + /* If so, do it all again (i.e. don't set done to 1) */ + if(!(stat & EL_AS_RXBUSY)) + dprintf(("<rescan> ")); + else + done = 1; + } + + (void)inb(base+EL_RXC); + outb(base+EL_AC,(EL_AC_IRQE|EL_AC_RX)); + return; +} + +/* Pass a packet up to the higher levels. Deal with trailer protocol. */ +static inline void elread(struct el_softc *sc,caddr_t buf,int len) +{ + register struct ether_header *eh; + struct mbuf *m; + int off, resid; + + /* Deal with trailer protocol: if type is trailer type + * get true type from first 16-bit word past data. + * Remember that type was trailer by setting off. + */ + eh = (struct ether_header *)buf; + eh->ether_type = ntohs((u_short)eh->ether_type); +#define eldataaddr(eh,off,type) ((type)(((caddr_t)((eh)+1)+(off)))) + if(eh->ether_type >= ETHERTYPE_TRAIL && + eh->ether_type < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + off = (eh->ether_type - ETHERTYPE_TRAIL) * 512; + if(off >= ETHERMTU) + return; + eh->ether_type = ntohs(*eldataaddr(eh,off,u_short *)); + resid = ntohs(*(eldataaddr(eh,off+2,u_short *))); + if((off+resid) > len) + return; + len = off + resid; + } + else + off = 0; + + if(len <= 0) + return; + +#if NBPFILTER > 0 + /* + * Check if there's a bpf filter listening on this interface. + * If so, hand off the raw packet to bpf, which must deal with + * trailers in its own way. + */ + if(sc->bpf) { + eh->ether_type = htons((u_short)eh->ether_type); + bpf_tap(sc->bpf,buf,len+sizeof(struct ether_header)); + eh->ether_type = ntohs((u_short)eh->ether_type); + + /* + * Note that the interface cannot be in promiscuous mode if + * there are no bpf listeners. And if el are in promiscuous + * mode, el have to check if this packet is really ours. + * + * This test does not support multicasts. + */ + if((sc->arpcom.ac_if.if_flags & IFF_PROMISC) + && bcmp(eh->ether_dhost,sc->arpcom.ac_enaddr, + sizeof(eh->ether_dhost)) != 0 + && bcmp(eh->ether_dhost,etherbroadcastaddr, + sizeof(eh->ether_dhost)) != 0) + return; + } +#endif + + /* + * Pull packet off interface. Off is nonzero if packet + * has trailing header; neget will then force this header + * information to be at the front, but we still have to drop + * the type and length which are at the front of any trailer data. + */ + m = elget(buf,len,off,&sc->arpcom.ac_if); + if(m == 0) + return; + + ether_input(&sc->arpcom.ac_if,eh,m); +} + +/* + * Pull read data off a interface. + * Len is length of data, with local net header stripped. + * Off is non-zero if a trailer protocol was used, and + * gives the offset of the trailer information. + * We copy the trailer information and then all the normal + * data into mbufs. When full cluster sized units are present + * we copy into clusters. + */ +struct mbuf * +elget(buf, totlen, off0, ifp) + caddr_t buf; + int totlen, off0; + struct ifnet *ifp; +{ + struct mbuf *top, **mp, *m, *p; + int off = off0, len; + register caddr_t cp = buf; + char *epkt; + + buf += sizeof(struct ether_header); + cp = buf; + epkt = cp + totlen; + + + if (off) { + cp += off + 2 * sizeof(u_short); + totlen -= 2 * sizeof(u_short); + } + + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == 0) + return (0); + m->m_pkthdr.rcvif = ifp; + m->m_pkthdr.len = totlen; + m->m_len = MHLEN; + top = 0; + mp = ⊤ + while (totlen > 0) { + if (top) { + MGET(m, M_DONTWAIT, MT_DATA); + if (m == 0) { + m_freem(top); + return (0); + } + m->m_len = MLEN; + } + len = min(totlen, epkt - cp); + if (len >= MINCLSIZE) { + MCLGET(m, M_DONTWAIT); + if (m->m_flags & M_EXT) + m->m_len = len = min(len, MCLBYTES); + else + len = m->m_len; + } else { + /* + * Place initial small packet/header at end of mbuf. + */ + if (len < m->m_len) { + if (top == 0 && len + max_linkhdr <= m->m_len) + m->m_data += max_linkhdr; + m->m_len = len; + } else + len = m->m_len; + } + bcopy(cp, mtod(m, caddr_t), (unsigned)len); + cp += len; + *mp = m; + mp = &m->m_next; + totlen -= len; + if (cp == epkt) + cp = buf; + } + return (top); +} + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ +int +el_ioctl(ifp, command, data) + register struct ifnet *ifp; + int command; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct el_softc *sc = &el_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splimp(); + + switch (command) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + el_init(ifp->if_unit); /* before arpwhohas */ + /* + * See if another station has *our* IP address. + * i.e.: There is an address conflict! If a + * conflict exists, a message is sent to the + * console. + */ + ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + /* + * XXX - This code is probably wrong + */ + 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); + else { + /* + * + */ + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + /* + * Set new address + */ + el_init(ifp->if_unit); + break; + } +#endif + default: + el_init(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 interface is marked down and it is running, then stop it + */ + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + el_stop(ifp->if_unit); + ifp->if_flags &= ~IFF_RUNNING; + } else { + /* + * If interface is marked up and it is stopped, then start it + */ + if ((ifp->if_flags & IFF_UP) && + ((ifp->if_flags & IFF_RUNNING) == 0)) + el_init(ifp->if_unit); + } + + default: + error = EINVAL; + } + (void) splx(s); + return (error); +} + +/* Device timeout routine */ +void el_watchdog(int unit) +{ + struct el_softc *sc; + + sc = &el_softc[unit]; + + log(LOG_ERR,"el%d: device timeout\n",unit); + sc->arpcom.ac_if.if_oerrors++; + el_reset(unit,0); +} +#endif diff --git a/sys/i386/isa/if_elreg.h b/sys/i386/isa/if_elreg.h new file mode 100644 index 000000000000..806d6ff68d1b --- /dev/null +++ b/sys/i386/isa/if_elreg.h @@ -0,0 +1,76 @@ +/* Copyright (c) 1994, Matthew E. Kimmel. Permission is hereby granted + * to use, copy, modify and distribute this software provided that both + * the copyright notice and this permission notice appear in all copies + * of the software, derivative works or modified versions, and any + * portions thereof. + */ +/* 3COM Etherlink 3C501 Register Definitions */ + +/* I/O Ports */ +#define EL_RXS 0x6 /* Receive status register */ +#define EL_RXC 0x6 /* Receive command register */ +#define EL_TXS 0x7 /* Transmit status register */ +#define EL_TXC 0x7 /* Transmit command register */ +#define EL_GPBL 0x8 /* GP buffer ptr low byte */ +#define EL_GPBH 0x9 /* GP buffer ptr high byte */ +#define EL_RBL 0xa /* Receive buffer ptr low byte */ +#define EL_RBC 0xa /* Receive buffer clear */ +#define EL_RBH 0xb /* Receive buffer ptr high byte */ +#define EL_EAW 0xc /* Ethernet address window */ +#define EL_AS 0xe /* Auxillary status register */ +#define EL_AC 0xe /* Auxillary command register */ +#define EL_BUF 0xf /* Data buffer */ + +/* Receive status register bits */ +#define EL_RXS_OFLOW 0x01 /* Overflow error */ +#define EL_RXS_FCS 0x02 /* FCS error */ +#define EL_RXS_DRIB 0x04 /* Dribble error */ +#define EL_RXS_SHORT 0x08 /* Short frame */ +#define EL_RXS_NOFLOW 0x10 /* No overflow */ +#define EL_RXS_GOOD 0x20 /* Received good frame */ +#define EL_RXS_STALE 0x80 /* Stale receive status */ + +/* Receive command register bits */ +#define EL_RXC_DISABLE 0x00 /* Receiver disabled */ +#define EL_RXC_DOFLOW 0x01 /* Detect overflow */ +#define EL_RXC_DFCS 0x02 /* Detect FCS errs */ +#define EL_RXC_DDRIB 0x04 /* Detect dribble errors */ +#define EL_RXC_DSHORT 0x08 /* Detect short frames */ +#define EL_RXC_DNOFLOW 0x10 /* Detect frames w/o overflow ??? */ +#define EL_RXC_AGF 0x20 /* Accept Good Frames */ +#define EL_RXC_PROMISC 0x40 /* Promiscuous mode */ +#define EL_RXC_ABROAD 0x80 /* Accept address, broadcast */ +#define EL_RXC_AMULTI 0xc0 /* Accept address, multicast */ + +/* Transmit status register bits */ +#define EL_TXS_UFLOW 0x01 /* Underflow */ +#define EL_TXS_COLL 0x02 /* Collision */ +#define EL_TXS_COLL16 0x04 /* Collision 16 */ +#define EL_TXS_READY 0x08 /* Ready for new frame */ + +/* Transmit command register bits */ +#define EL_TXC_DUFLOW 0x01 /* Detect underflow */ +#define EL_TXC_DCOLL 0x02 /* Detect collisions */ +#define EL_TXC_DCOLL16 0x04 /* Detect collision 16 */ +#define EL_TXC_DSUCCESS 0x08 /* Detect success */ + +/* Auxillary status register bits */ +#define EL_AS_RXBUSY 0x01 /* Receive busy */ +#define EL_AS_DMADONE 0x10 /* DMA finished */ +#define EL_AS_TXBUSY 0x80 /* Transmit busy */ + +/* Auxillary command register bits */ +#define EL_AC_HOST 0x00 /* System bus can access buffer */ +#define EL_AC_IRQE 0x01 /* IRQ enable */ +#define EL_AC_TXBAD 0x02 /* Transmit frames with bad FCS */ +#define EL_AC_TXFRX 0x04 /* Transmit followed by receive */ +#define EL_AC_RX 0x08 /* Receive */ +#define EL_AC_LB 0x0c /* Loopback */ +#define EL_AC_DRQ 0x20 /* DMA request */ +#define EL_AC_RIDE 0x40 /* DRQ and IRQ enabled */ +#define EL_AC_RESET 0x80 /* Reset */ + +/* Packet buffer size */ +#define EL_BUFSIZ 2048 + +#define ETHER_ADDR_LEN 6 diff --git a/sys/i386/isa/if_ep.c b/sys/i386/isa/if_ep.c index 43edd4529485..44e9c9fc0d58 100644 --- a/sys/i386/isa/if_ep.c +++ b/sys/i386/isa/if_ep.c @@ -8,7 +8,7 @@ * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. The name of the author may not be used to endorse or promote products - * derived from this software withough specific prior written permission + * 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 @@ -22,62 +22,56 @@ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * From: if_ep.c,v 1.9 1994/01/25 10:46:29 deraadt Exp $ - * $Id: if_ep.c,v 1.7 1994/02/03 11:51:06 davidg Exp $ - */ -/* - * TODO: - * Multi-509 configs. - * don't pass unit into epstop. - * epintr returns an int for magnum. 0=not for me. 1=for me. -1=whoknows? - * deallocate mbufs when ifconfig'd down. + * $Id: if_ep.c,v 1.9 1994/05/02 22:27:33 ats Exp $ */ + #include "ep.h" #if NEP > 0 #include "bpfilter.h" -#include "sys/param.h" +#include <sys/param.h> #if defined(__FreeBSD__) -#include "sys/systm.h" -#include "sys/kernel.h" +#include <sys/systm.h> +#include <sys/kernel.h> #endif -#include "sys/mbuf.h" -#include "sys/socket.h" -#include "sys/ioctl.h" -#include "sys/errno.h" -#include "sys/syslog.h" +#include <sys/mbuf.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/errno.h> +#include <sys/syslog.h> #if defined(__NetBSD__) -#include "sys/select.h" +#include <sys/select.h> #endif -#include "net/if.h" -#include "net/if_dl.h" -#include "net/if_types.h" +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> #ifdef INET -#include "netinet/in.h" -#include "netinet/in_systm.h" -#include "netinet/in_var.h" -#include "netinet/ip.h" -#include "netinet/if_ether.h" +#include <netinet/in.h> +#include <netinet/in_systm.h> +#include <netinet/in_var.h> +#include <netinet/ip.h> +#include <netinet/if_ether.h> #endif #ifdef NS -#include "netns/ns.h" -#include "netns/ns_if.h" +#include <netns/ns.h> +#include <netns/ns_if.h> #endif #if NBPFILTER > 0 -#include "net/bpf.h" -#include "net/bpfdesc.h" +#include <net/bpf.h> +#include <net/bpfdesc.h> #endif -#include "machine/pio.h" +#include <machine/pio.h> -#include "i386/isa/isa.h" -#include "i386/isa/isa_device.h" -#include "i386/isa/icu.h" -#include "i386/isa/if_epreg.h" +#include <i386/isa/isa.h> +#include <i386/isa/isa_device.h> +#include <i386/isa/icu.h> +#include <i386/isa/if_epreg.h> #define ETHER_MIN_LEN 64 #define ETHER_MAX_LEN 1518 @@ -90,12 +84,13 @@ struct ep_softc { struct arpcom arpcom; /* Ethernet common part */ short ep_io_addr; /* i/o bus address */ char ep_connectors; /* Connectors on this card. */ -#define MAX_MBS 4 /* # of mbufs we keep around */ +#define MAX_MBS 8 /* # of mbufs we keep around */ struct mbuf *mb[MAX_MBS]; /* spare mbuf storage. */ int next_mb; /* Which mbuf to use next. */ int last_mb; /* Last mbuf. */ int tx_start_thresh; /* Current TX_start_thresh. */ caddr_t bpf; /* BPF "magic cookie" */ + char bus32bit; /* 32bit access possible */ } ep_softc[NEP]; static int epprobe __P((struct isa_device *)); @@ -104,7 +99,8 @@ static int epioctl __P((struct ifnet * ifp, int, caddr_t)); void epinit __P((int)); void epintr __P((int)); -void epmbufqueue __P((caddr_t, int)); +void epmbuffill __P((caddr_t, int)); +void epmbufempty __P((struct ep_softc *)); void epread __P((struct ep_softc *)); void epreset __P((int)); void epstart __P((struct ifnet *)); @@ -274,7 +270,7 @@ epinit(unit) return; s = splimp(); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; GO_WINDOW(0); @@ -350,7 +346,7 @@ epinit(unit) */ sc->last_mb = 0; sc->next_mb = 0; - epmbufqueue((caddr_t)sc, 0); + epmbuffill((caddr_t)sc, 0); epstart(ifp); @@ -421,10 +417,19 @@ startagain: outw(BASE + EP_W1_TX_PIO_WR_1, 0xffff); /* Second dword meaningless */ for (top = m; m != 0; m = m->m_next) { - outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2); - if (m->m_len & 1) - outb(BASE + EP_W1_TX_PIO_WR_1, - *(mtod(m, caddr_t) + m->m_len - 1)); + if (sc->bus32bit) { + outsl(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), + m->m_len/4); + if (m->m_len & 3) + outsb(BASE + EP_W1_TX_PIO_WR_1, + mtod(m, caddr_t) + m->m_len/4, + m->m_len & 3); + } else { + outsw(BASE + EP_W1_TX_PIO_WR_1, mtod(m, caddr_t), m->m_len/2); + if (m->m_len & 1) + outb(BASE + EP_W1_TX_PIO_WR_1, + *(mtod(m, caddr_t) + m->m_len - 1)); + } } while (pad--) outb(BASE + EP_W1_TX_PIO_WR_1, 0); /* Padding */ @@ -658,7 +663,7 @@ epread(sc) if (m == 0) goto out; } else { - timeout(epmbufqueue, (caddr_t)sc, 0); + timeout(epmbuffill, (caddr_t)sc, 0); sc->next_mb = (sc->next_mb + 1) % MAX_MBS; } if (totlen >= MINCLSIZE) @@ -667,11 +672,22 @@ epread(sc) mcur->m_next = m; lenthisone = min(totlen, M_TRAILINGSPACE(m)); } - insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, - lenthisone / 2); - m->m_len += lenthisone; - if (lenthisone & 1) - *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); + if (sc->bus32bit) { + insl(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 4); + m->m_len += (lenthisone & ~3); + if (lenthisone & 3) + insb(BASE + EP_W1_RX_PIO_RD_1, + mtod(m, caddr_t) + m->m_len, + lenthisone & 3); + m->m_len += (lenthisone & 3); + } else { + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + lenthisone / 2); + m->m_len += lenthisone; + if (lenthisone & 1) + *(mtod(m, caddr_t) + m->m_len - 1) = inb(BASE + EP_W1_RX_PIO_RD_1); + } totlen -= lenthisone; } if (off) { @@ -679,15 +695,17 @@ epread(sc) sc->mb[sc->next_mb] = 0; if (top == 0) { MGETHDR(m, M_DONTWAIT, MT_DATA); - if (top == 0) + if (top == 0) { + top = m0; goto out; + } } else { /* Convert one of our saved mbuf's */ sc->next_mb = (sc->next_mb + 1) % MAX_MBS; top->m_data = top->m_pktdat; top->m_flags = M_PKTHDR; } - insw(BASE + EP_W1_RX_PIO_RD_1, mtod(m, caddr_t) + m->m_len, + insw(BASE + EP_W1_RX_PIO_RD_1, mtod(top, caddr_t), sizeof(struct ether_header)); top->m_next = m0; top->m_len = sizeof(struct ether_header); @@ -700,7 +718,7 @@ epread(sc) top->m_pkthdr.rcvif = &sc->arpcom.ac_if; outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; ++sc->arpcom.ac_if.if_ipackets; #if NBPFILTER > 0 @@ -728,12 +746,11 @@ epread(sc) return; out: outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; if (top) m_freem(top); - return; } @@ -773,7 +790,7 @@ epioctl(ifp, cmd, data) else { ifp->if_flags &= ~IFF_RUNNING; bcopy((caddr_t) ina->x_host.c_host, - (caddr_t)sc->arpcom.ns_addr + (caddr_t)sc->arpcom.ac_enaddr, sizeof(sc->arpcom.ac_enaddr)); } epinit(ifp->if_unit); @@ -789,6 +806,7 @@ epioctl(ifp, cmd, data) if ((ifp->if_flags & IFF_UP) == 0 && ifp->if_flags & IFF_RUNNING) { ifp->if_flags &= ~IFF_RUNNING; epstop(ifp->if_unit); + epmbufempty(sc); break; } if (ifp->if_flags & IFF_UP && (ifp->if_flags & IFF_RUNNING) == 0) @@ -815,7 +833,6 @@ epreset(unit) epstop(unit); epinit(unit); splx(s); - return; } void @@ -838,7 +855,7 @@ epstop(unit) outw(BASE + EP_COMMAND, RX_DISABLE); outw(BASE + EP_COMMAND, RX_DISCARD_TOP_PACK); - while (inb(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) + while (inw(BASE + EP_STATUS) & S_COMMAND_IN_PROGRESS) ; outw(BASE + EP_COMMAND, TX_DISABLE); outw(BASE + EP_COMMAND, STOP_TRANSCEIVER); @@ -848,7 +865,6 @@ epstop(unit) outw(BASE + EP_COMMAND, SET_RD_0_MASK); outw(BASE + EP_COMMAND, SET_INTR_MASK); outw(BASE + EP_COMMAND, SET_RX_FILTER); - return; } @@ -937,24 +953,42 @@ is_eeprom_busy(is) } void -epmbufqueue(sp, dummy_arg) +epmbuffill(sp, dummy_arg) caddr_t sp; int dummy_arg; { struct ep_softc *sc = (struct ep_softc *)sp; - int i; + int s, i; - if (sc->mb[sc->last_mb]) - return; + s = splimp(); i = sc->last_mb; do { - MGET(sc->mb[i], M_DONTWAIT, MT_DATA); - if (!sc->mb[i]) + if(sc->mb[i] == NULL) + MGET(sc->mb[i], M_DONTWAIT, MT_DATA); + if(sc->mb[i] == NULL) break; i = (i + 1) % MAX_MBS; } while (i != sc->next_mb); sc->last_mb = i; - return; + splx(s); +} + +static void +epmbufempty(sc) + struct ep_softc *sc; +{ + int s, i; + + s = splimp(); + for (i = 0; i<MAX_MBS; i++) { + if (sc->mb[i]) { + m_freem(sc->mb[i]); + sc->mb[i] = NULL; + } + } + sc->last_mb = sc->next_mb = 0; + untimeout(epmbuffill, sc); + splx(s); } #endif /* NEP > 0 */ diff --git a/sys/i386/isa/if_ie507.h b/sys/i386/isa/if_ie507.h new file mode 100644 index 000000000000..4bf87fcbb597 --- /dev/null +++ b/sys/i386/isa/if_ie507.h @@ -0,0 +1,19 @@ +/* + * $Id: if_ie507.h,v 1.1 1994/05/25 20:06:49 ats Exp $ + * Definitions for 3C507 + */ + +#define IE507_CTRL 6 /* control port */ +#define IE507_ICTRL 10 /* interrupt control */ +#define IE507_ATTN 11 /* any write here sends a chan attn */ +#define IE507_MADDR 14 /* shared memory configuration */ +#define IE507_IRQ 15 /* IRQ configuration */ + +#define EL_CTRL_BNK1 0x01 /* register bank 1 */ +#define EL_CTRL_IEN 0x04 /* interrupt enable */ +#define EL_CTRL_INTL 0x08 /* interrupt active latch */ +#define EL_CTRL_16BIT 0x10 /* bus width; clear = 8-bit, set = 16-bit */ +#define EL_CTRL_LOOP 0x20 /* loopback mode */ +#define EL_CTRL_NRST 0x80 /* turn off to reset */ +#define EL_CTRL_RESET (EL_CTRL_LOOP) +#define EL_CTRL_NORMAL (EL_CTRL_NRST | EL_CTRL_IEN | EL_CTRL_BNK1) diff --git a/sys/i386/isa/if_is.c b/sys/i386/isa/if_is.c index 341885f36ed1..234a0d937c1f 100644 --- a/sys/i386/isa/if_is.c +++ b/sys/i386/isa/if_is.c @@ -332,15 +332,15 @@ is_attach(isa_dev) * are only 16 bits wide! */ -#define MAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \ +#define ISMAXMEM ((NRBUF+NTBUF)*(BUFSIZE) + (NRBUF+NTBUF)*sizeof(struct mds) \ + sizeof(struct init_block) + 8) - is->init_block = (struct init_block *)malloc(MAXMEM,M_TEMP,M_NOWAIT); + is->init_block = (struct init_block *)malloc(ISMAXMEM,M_TEMP,M_NOWAIT); if (!is->init_block) { printf("is%d : Couldn't allocate memory for card\n",unit); } /* * XXX -- should take corrective action if not - * quadword alilgned, the 8 byte slew factor in MAXMEM + * quadword alilgned, the 8 byte slew factor in ISMAXMEM * allows for this. */ @@ -483,7 +483,7 @@ is_init(unit) /* Address not known */ if (ifp->if_addrlist == (struct ifaddr *)0) return; - s = splnet(); + s = splimp(); /* * Lance must be stopped @@ -984,7 +984,7 @@ is_ioctl(ifp, cmd, data) struct ifreq *ifr = (struct ifreq *)data; int s, error = 0; - s = splnet(); + s = splimp(); switch (cmd) { diff --git a/sys/i386/isa/if_ze.c b/sys/i386/isa/if_ze.c new file mode 100644 index 000000000000..6ffb96c96dbc --- /dev/null +++ b/sys/i386/isa/if_ze.c @@ -0,0 +1,1951 @@ +/*- + * TODO: + * [1] integrate into current if_ed.c + * [2] parse tuples to find out where to map the shared memory buffer, + * and what to write into the configuration register + * [3] move pcic-specific code into a separate module. + * + * Device driver for IBM PCMCIA Credit Card Adapter for Ethernet, + * if_ze.c + * + * Based on the Device driver for National Semiconductor DS8390 ethernet + * adapters by David Greenman. Modifications for PCMCIA by Keith Moore. + * Adapted for FreeBSD 1.1.5 by Jordan Hubbard. + * + * Currently supports only the IBM Credit Card Adapter for Ethernet, but + * could probably work with other PCMCIA cards also, if it were modified + * to get the locations of the PCMCIA configuration option register (COR) + * by parsing the configuration tuples, rather than by hard-coding in + * the value expected by IBM's card. + * + * Sources for data on the PCMCIA/IBM CCAE specific portions of the driver: + * + * [1] _Local Area Network Credit Card Adapters Technical Reference_, + * IBM Corp., SC30-3585-00, part # 33G9243. + * [2] "pre-alpha" PCMCIA support code for Linux by Barry Jaspan. + * [3] Intel 82536SL PC Card Interface Controller Data Sheet, Intel + * Order Number 290423-002 + * [4] National Semiconductor DP83902A ST-NIC (tm) Serial Network + * Interface Controller for Twisted Pair data sheet. + * + * + * Copyright (C) 1993, David Greenman. This software may be used, modified, + * copied, distributed, and sold, in both source and binary form provided + * that the above copyright and these terms are retained. Under no + * circumstances is the author responsible for the proper functioning + * of this software, nor does the author assume any responsibility + * for damages incurred with its use. + */ + +#include "ze.h" +#if NZE > 0 +#include "bpfilter.h" + +#include "param.h" +#include "systm.h" +#include "errno.h" +#include "ioctl.h" +#include "mbuf.h" +#include "socket.h" +#include "syslog.h" + +#include "net/if.h" +#include "net/if_dl.h" +#include "net/if_types.h" +#include "net/netisr.h" + +#ifdef INET +#include "netinet/in.h" +#include "netinet/in_systm.h" +#include "netinet/in_var.h" +#include "netinet/ip.h" +#include "netinet/if_ether.h" +#endif + +#ifdef NS +#include "netns/ns.h" +#include "netns/ns_if.h" +#endif + +#if NBPFILTER > 0 +#include "net/bpf.h" +#include "net/bpfdesc.h" +#endif + +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/icu.h" +#include "i386/isa/if_zereg.h" + +#include "i386/include/pio.h" + + + +/***************************************************************************** + * pcmcia controller chip (PCIC) support * + * (eventually, move this to a separate file) * + *****************************************************************************/ +#include "ic/i82365.h" + +/* + * Each PCIC chip (82365SL or clone) can handle two card slots, and there + * can be up to four PCICs in a system. (On some machines, not all of the + * address lines are decoded, so a card may appear to be in more than one + * slot.) + */ +#define MAXSLOT 8 + +/* + * To access a register on the PCIC for a particular slot, you + * first write the correct OFFSET value for that slot in the + * INDEX register for the PCIC controller. You then read or write + * the value from or to the DATA register for that controller. + * + * The first pair of chips shares I/O addresss for DATA and INDEX, + * as does the second pair. (To the programmer, it looks like each + * pair is a single chip.) The i/o port addresses are hard-wired + * into the PCIC; so the following addresses should be valid for + * any machine that uses this chip. + */ + +#define PCIC_INDEX_0 0x3E0 /* index reg, chips 0 and 1 */ +#define PCIC_DATA_0 0x3E1 /* data register, chips 0 and 1 */ +#define PCIC_INDEX_1 0x3E2 /* index reg, chips 1 and 2 */ +#define PCIC_DATA_1 0x3E3 /* data register, chips 1 and 2 */ + +/* + * Given a slot number, calculate the INDEX and DATA registers + * to talk to that slot. OFFSET is added to the register number + * to address the registers for a particular slot. + */ +#define INDEX(slot) ((slot) < 4 ? PCIC_INDEX_0 : PCIC_INDEX_1) +#define DATA(slot) ((slot) < 4 ? PCIC_DATA_0 : PCIC_DATA_1) +#define OFFSET(slot) ((slot) % 4 * 0x40) + +/* + * There are 5 sets (windows) of memory mapping registers on the PCIC chip + * for each slot, numbered 0..4. + * + * They start at 10/50 hex within the chip's register space (not system + * I/O space), and are eight addresses apart. These are actually pairs of + * 8-bit-wide registers (low byte first, then high byte) since the + * address fields are actually 12 bits long. The upper bits are used + * for other things like 8/16-bit select and wait states. + * + * Memory mapping registers include start/stop addresses to define the + * region to be mapped (in terms of system memory addresses), and + * an offset register to allow for translation from system space + * to card space. The lower 12 bits aren't included in these, so memory is + * mapped in 4K chunks. + */ +#define MEM_START_ADDR(window) (((window) * 0x08) + 0x10) +#define MEM_STOP_ADDR(window) (((window) * 0x08) + 0x12) +#define MEM_OFFSET(window) (((window) * 0x08) + 0x14) +/* + * this bit gets set in the address window enable register (PCIC_ADDRWINE) + * to enable a particular address window. + */ +#define MEM_ENABLE_BIT(window) ((1) << (window)) + +/* + * There are two i/o port addressing windows. I/O ports cannot be + * relocated within system i/o space (unless the card doesn't decode + * all of the address bits); unlike card memory, there is no address + * translation offset. + */ +#define IO_START_ADDR(window) ((window) ? PCIC_IO1_STL : PCIC_IO0_STL) +#define IO_STOP_ADDR(window) ((window) ? PCIC_IO1_SPL : PCIC_IO0_SPL) +#define IO_ENABLE_BIT(window) ((window) ? PCIC_IO1_EN : PCIC_IO0_EN) +#define IO_CS16_BIT(window) ((window) ? PCIC_IO1_CS16 : PCIC_IO0_CS16) + +/* + * read a byte from a pcic register for a particular slot + */ +static inline unsigned char +pcic_getb (int slot, int reg) +{ + outb (INDEX(slot), OFFSET (slot) + reg); + return inb (DATA (slot)); +} + +/* + * write a byte to a pcic register for a particular slot + */ +static inline void +pcic_putb (int slot, int reg, unsigned char val) +{ + outb (INDEX(slot), OFFSET (slot) + reg); + outb (DATA (slot), val); +} + +/* + * read a word from a pcic register for a particular slot + */ +static inline unsigned short +pcic_getw (int slot, int reg) +{ + return pcic_getb (slot, reg) | (pcic_getb (slot, reg+1) << 8); +} + +/* + * write a word to a pcic register at a particular slot + */ +static inline void +pcic_putw (int slot, int reg, unsigned short val) +{ + pcic_putb (slot, reg, val & 0xff); + pcic_putb (slot, reg + 1, (val >> 8) & 0xff); +} + +static void +pcic_print_regs (int slot) +{ + int i, j; + + for (i = 0; i < 0x40; i += 16) { + for (j = 0; j < 16; ++j) + printf ("%02x ", pcic_getb (slot, i + j)); + printf ("\n"); + } +} + +/* + * map a portion of the card's memory space into system memory + * space. + * + * slot = # of the slot the card is plugged into + * window = which pcic memory map registers to use (0..4) + * sys_addr = base system PHYSICAL memory address where we want it. must + * be on an appropriate boundary (lower 12 bits are zero). + * card_addr = the base address of the card's memory to correspond + * to sys_addr + * length = length of the segment to map (may be rounded up as necessary) + * type = which card memory space to map (attribute or shared) + * width = 1 for byte-wide mapping; 2 for word (16-bit) mapping. + */ + +enum memtype { COMMON, ATTRIBUTE }; + +static void +pcic_map_memory (int slot, int window, unsigned long sys_addr, + unsigned long card_addr, unsigned long length, + enum memtype type, int width) +{ + unsigned short offset; + unsigned short mem_start_addr; + unsigned short mem_stop_addr; + + sys_addr >>= 12; + card_addr >>= 12; + length >>= 12; + /* + * compute an offset for the chip such that + * (sys_addr + offset) = card_addr + * but the arithmetic is done modulo 2^14 + */ + offset = (card_addr - sys_addr) & 0x3FFF; + /* + * now OR in the bit for "attribute memory" if necessary + */ + if (type == ATTRIBUTE) { + offset |= (PCIC_REG << 8); + /* REG == "region active" pin on card */ + } + /* + * okay, set up the chip memory mapping registers, and turn + * on the enable bit for this window. + * if we are doing 16-bit wide accesses (width == 2), + * turn on the appropriate bit. + * + * XXX for now, we set all of the wait state bits to zero. + * Not really sure how they should be set. + */ + mem_start_addr = sys_addr & 0xFFF; + if (width == 2) + mem_start_addr |= (PCIC_DATA16 << 8); + mem_stop_addr = (sys_addr + length) & 0xFFF; + + pcic_putw (slot, MEM_START_ADDR(window), mem_start_addr); + pcic_putw (slot, MEM_STOP_ADDR(window), mem_stop_addr); + pcic_putw (slot, MEM_OFFSET(window), offset); + /* + * Assert the bit (PCIC_MEMCS16) that says to decode all of + * the address lines. + */ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) | + MEM_ENABLE_BIT(window) | PCIC_MEMCS16); +} + +static void +pcic_unmap_memory (int slot, int window) +{ + /* + * seems like we need to turn off the enable bit first, after which + * we can clear the registers out just to be sure. + */ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) & ~MEM_ENABLE_BIT(window)); + pcic_putw (slot, MEM_START_ADDR(window), 0); + pcic_putw (slot, MEM_STOP_ADDR(window), 0); + pcic_putw (slot, MEM_OFFSET(window), 0); +} + +/* + * map a range of addresses into system i/o space + * (no translation of i/o addresses is possible) + * + * 'width' is: + * + 0 to tell the PCIC to generate the ISA IOCS16* signal from + * the PCMCIA IOIS16* signal. + * + 1 to select 8-bit width + * + 2 to select 16-bit width + */ + +static void +pcic_map_io (int slot, int window, unsigned short base, unsigned short length, + unsigned short width) +{ + unsigned char x; + + pcic_putw (slot, IO_START_ADDR(window), base); + pcic_putw (slot, IO_STOP_ADDR(window), base+length-1); + /* + * select the bits that determine whether + * an i/o operation is 8 or 16 bits wide + */ + x = pcic_getb (slot, PCIC_IOCTL); + switch (width) { + case 0: /* PCMCIA card decides */ + if (window) + x = (x & 0xf0) | PCIC_IO1_CS16; + else + x = (x & 0x0f) | PCIC_IO0_CS16; + break; + case 1: /* 8 bits wide */ + break; + case 2: /* 16 bits wide */ + if (window) + x = (x & 0xf0) | PCIC_IO1_16BIT; + else + x = (x & 0x0f) | PCIC_IO0_16BIT; + break; + } + pcic_putb (slot, PCIC_IOCTL, x); + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) | IO_ENABLE_BIT(window)); +} + +#ifdef TEST +static void +pcic_unmap_io (int slot, int window) +{ + pcic_putb (slot, PCIC_ADDRWINE, + pcic_getb (slot, PCIC_ADDRWINE) & ~IO_ENABLE_BIT(window)); + pcic_putw (slot, IO_START_ADDR(window), 0); + pcic_putw (slot, IO_STOP_ADDR(window), 0); +} +#endif /* TEST */ + +/* + * tell the PCIC which irq we want to use. only the following are legal: + * 3, 4, 5, 7, 9, 10, 11, 12, 14, 15 + * + * NB: 'irq' is an interrupt NUMBER, not a MASK as in struct isa_device. + */ + +static void +pcic_map_irq (int slot, int irq) +{ + if (irq < 3 || irq == 6 || irq == 8 || irq == 13 || irq > 15) { + printf ("ze: pcic_map_irq (slot %d): illegal irq %d\n", slot, irq); + return; + } + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | (irq & 0x0F)); +} + +static void +pcic_power_on (int slot) +{ + pcic_putb (slot, PCIC_POWER, + pcic_getb (slot, PCIC_POWER) | PCIC_DISRST | PCIC_PCPWRE); + DELAY (50000); + pcic_putb (slot, PCIC_POWER, + pcic_getb (slot, PCIC_POWER) | PCIC_OUTENA); +} + +static void +pcic_reset (int slot) +{ + /* assert RESET (by clearing a bit!), wait a bit, and de-assert it */ + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) & ~PCIC_CARDRESET); + DELAY (50000); + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDRESET); +} + + +/***************************************************************************** + * Driver for Ethernet Adapter * + *****************************************************************************/ +/* + * ze_softc: per line info and status + */ +struct ze_softc { + struct arpcom arpcom; /* ethernet common */ + + char *type_str; /* pointer to type string */ + char *mau; /* type of media access unit */ +#if 0 + u_char vendor; /* interface vendor */ + u_char type; /* interface type code */ +#endif + +#if 0 + u_short vector; /* interrupt vector */ +#endif + u_short nic_addr; /* NIC (DS8390) I/O bus address */ + + caddr_t smem_start; /* shared memory start address */ + caddr_t smem_end; /* shared memory end address */ + u_long smem_size; /* total shared memory size */ + caddr_t smem_ring; /* start of RX ring-buffer (in smem) */ + + caddr_t bpf; /* BPF "magic cookie" */ + + u_char memwidth; /* width of access to card mem 8 or 16 */ + u_char xmit_busy; /* transmitter is busy */ + u_char txb_cnt; /* Number of transmit buffers */ + u_char txb_next; /* Pointer to next buffer ready to xmit */ + u_short txb_next_len; /* next xmit buffer length */ + u_char data_buffered; /* data has been buffered in interface memory */ + u_char tx_page_start; /* first page of TX buffer area */ + + 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 */ +} ze_softc[NZE]; + +int ze_attach(), ze_ioctl(), ze_probe(); +void ze_init(), ze_start(), ze_stop(), ze_intr(); +void ze_reset(), ze_watchdog(), ze_get_packet(); + +static inline void ze_rint(); +static inline void ze_xmit(); +static inline char *ze_ring_copy(); + +extern int ether_output(); + +struct isa_driver zedriver = { + ze_probe, + ze_attach, + "ze" +}; + +#define ETHER_MIN_LEN 64 +#define ETHER_MAX_LEN 1518 +#define ETHER_ADDR_LEN 6 +#define ETHER_HDR_SIZE 14 + +static unsigned char enet_addr[6]; +static unsigned char card_info[256]; + +#define CARD_INFO "IBM Corp.~Ethernet~0933495" + +/* + * scan the card information structure looking for the version/product info + * tuple. when we find it, compare it to the string we are looking for. + * return 1 if we find it, 0 otherwise. + */ + +static int +ze_check_cis (unsigned char *scratch) +{ + int i,j,k; + + card_info[0] = '\0'; + i = 0; + while (scratch[i] != 0xff && i < 1024) { + unsigned char link = scratch[i+2]; + +#if 0 + printf ("[%02x] %02x ", i, link); + for (j = 4; j < 2 * link + 4 && j < 32; j += 2) + printf ("%02x ", scratch[j + i]); + printf ("\n"); +#endif + if (scratch[i] == 0x15) { + /* + * level 1 version/product info + * copy to card_info, translating '\0' to '~' + */ + k = 0; + for (j = i+8; scratch[j] != 0xff; j += 2) + card_info[k++] = scratch[j] == '\0' ? '~' : scratch[j]; + card_info[k++] = '\0'; + return (memcmp (card_info, CARD_INFO, sizeof(CARD_INFO)-1) == 0); + } + i += 4 + 2 * link; + } + return 0; +} + +/* + * Probe each slot looking for an IBM Credit Card Adapter for Ethernet + * For each card that we find, map its card information structure + * into system memory at 'scratch' and see whether it's one of ours. + * Return the slot number if we find a card, or -1 otherwise. + * + * Side effects: + * + On success, leaves CIS mapped into memory at 'scratch'; + * caller must free it. + * + On success, leaves ethernet address in enet_addr. + * + Leaves product/vendor id of last card probed in 'card_info' + */ + +static int +ze_find_adapter (unsigned char *scratch) +{ + int slot; + + for (slot = 0; slot < MAXSLOT; ++slot) { + /* + * see if there's a PCMCIA controller here + * Intel PCMCIA controllers use 0x82 and 0x83 + * IBM clone chips use 0x88 and 0x89, apparently + */ + unsigned char idbyte = pcic_getb (slot, PCIC_ID_REV); + + if (idbyte != 0x82 && idbyte != 0x83 && + idbyte != 0x88 && idbyte != 0x89) { +#if 0 + printf ("ibmccae: pcic slot %d: wierd id/rev code 0x%02x\n", + slot, idbyte); +#endif + continue; + } + if ((pcic_getb (slot, PCIC_STATUS) & PCIC_CD) != PCIC_CD) { + printf ("ze: slot %d: no card in slot\n", slot); + /* no card in slot */ + continue; + } + pcic_power_on (slot); + pcic_reset (slot); + /* + * map the card's attribute memory and examine its + * card information structure tuples for something + * we recognize. + */ + pcic_map_memory (slot, 0, kvtop (scratch), 0L, + 0xFFFL, ATTRIBUTE, 1); + + if ((ze_check_cis (scratch)) > 0) { + /* found it */ + printf ("ze: found card in slot %d\n", slot); + return slot; + } + else + printf ("ze: pcmcia slot %d: %s\n", slot, card_info); + pcic_unmap_memory (slot, 0); + } + return -1; +} + + +/* + * macros to handle casting unsigned long to (char *) so we can + * read/write into physical memory space. + */ + +#define PEEK(addr) (*((unsigned char *)(addr))) +#define POKE(addr,val) do { PEEK(addr) = (val); } while (0) + +/* + * Determine if the device is present + * + * on entry: + * a pointer to an isa_device struct + * on exit: + * NULL if device not found + * or # of i/o addresses used (if found) + */ +int +ze_probe(isa_dev) + struct isa_device *isa_dev; +{ + struct ze_softc *sc = &ze_softc[isa_dev->id_unit]; + int i, x; + u_int memsize; + u_char iptr, memwidth, sum, tmp; + int slot; + + if ((slot = ze_find_adapter (isa_dev->id_maddr)) < 0) + return NULL; + + /* + * okay, we found a card, so set it up + */ + /* + * Inhibit 16 bit memory delay. + * POINTETH.SYS apparently does this, for what reason I don't know. + */ + pcic_putb (slot, PCIC_CDGC, + pcic_getb (slot, PCIC_CDGC) | PCIC_16_DL_INH); + /* + * things to map + * (1) card's EEPROM is already mapped by the find_adapter routine + * but we still need to get the card's ethernet address. + * after that we unmap that part of attribute memory. + * (2) card configuration registers need to be mapped in so we + * can set the configuration and socket # registers. + * (3) shared memory packet buffer + * (4) i/o ports + * (5) IRQ + */ + /* + * Sigh. Location of the ethernet address isn't documented in [1]. + * It was derived by doing a hex dump of all of attribute memory + * and looking for the IBM vendor prefix. + */ + enet_addr[0] = PEEK(isa_dev->id_maddr+0xff0); + enet_addr[1] = PEEK(isa_dev->id_maddr+0xff2); + enet_addr[2] = PEEK(isa_dev->id_maddr+0xff4); + enet_addr[3] = PEEK(isa_dev->id_maddr+0xff6); + enet_addr[4] = PEEK(isa_dev->id_maddr+0xff8); + enet_addr[5] = PEEK(isa_dev->id_maddr+0xffa); + pcic_unmap_memory (slot, 0); + + /* + * (2) map card configuration registers. these are offset + * in card memory space by 0x20000. normally we could get + * this offset from the card information structure, but I'm + * too lazy and am not quite sure if I understand the CIS anyway. + * + * XXX IF YOU'RE TRYING TO PORT THIS DRIVER FOR A DIFFERENT + * PCMCIA CARD, the most likely thing to change is the constant + * 0x20000 in the next statement. Oh yes, also change the + * card id string that we probe for. + */ + pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x20000, 8L, + ATTRIBUTE, 1); + POKE(isa_dev->id_maddr, 0x80); /* reset the card (how long?) */ + DELAY (10000); + /* + * Set the configuration index. According to [1], the adapter won't + * respond to any i/o signals until we do this; it uses the + * Memory Only interface (whatever that is; it's not documented). + * Also turn on "level" (not pulse) interrupts. + * + * XXX probably should init the socket and copy register also, + * so that we can deal with multiple instances of the same card. + */ + POKE(isa_dev->id_maddr, 0x41); + pcic_unmap_memory (slot, 0); + + /* + * (3) now map in the shared memory buffer. This has to be mapped + * as words, not bytes, and on a 16k boundary. The offset value + * was derived by installing IBM's POINTETH.SYS under DOS and + * looking at the PCIC registers; it's not documented in IBM's + * tech ref manual ([1]). + */ + pcic_map_memory (slot, 0, kvtop (isa_dev->id_maddr), 0x4000L, 0x4000L, + COMMON, 2); + + /* + * (4) map i/o ports. + * + * XXX is it possible that the config file leaves this unspecified, + * in which case we have to pick one? + * + * At least one PCMCIA device driver I'v seen maps a block + * of 32 consecutive i/o ports as two windows of 16 ports each. + * Maybe some other pcic chips are restricted to 16-port windows; + * the 82365SL doesn't seem to have that problem. But since + * we have an extra window anyway... + */ +#ifdef SHARED_MEMORY + pcic_map_io (slot, 0, isa_dev->id_iobase, 32, 1); +#else + pcic_map_io (slot, 0, isa_dev->id_iobase, 16, 1); + pcic_map_io (slot, 1, isa_dev->id_iobase+16, 16, 2); +#endif /* SHARED_MEMORY */ + + /* + * (5) configure the card for the desired interrupt + * + * XXX is it possible that the config file leaves this unspecified? + */ + pcic_map_irq (slot, ffs (isa_dev->id_irq) - 1); + + /* tell the PCIC that this is an I/O card (not memory) */ + pcic_putb (slot, PCIC_INT_GEN, + pcic_getb (slot, PCIC_INT_GEN) | PCIC_CARDTYPE); + +#if 0 + /* tell the PCIC to use level-mode interrupts */ + /* XXX this register may not be present on all controllers */ + pcic_putb (slot, PCIC_GLO_CTRL, + pcic_getb (slot, PCIC_GLO_CTRL) | PCIC_LVL_MODE); +#endif + +#if 0 + pcic_print_regs (slot); +#endif + /* + * Setup i/o addresses + */ + sc->nic_addr = isa_dev->id_iobase; +#if 0 + sc->vector = isa_dev->id_irq; +#endif + sc->smem_start = (caddr_t)isa_dev->id_maddr; + +#if 0 + sc->vendor = ZE_VENDOR_IBM; + sc->type = xxx; +#endif + + /* reset card to force it into a known state */ + tmp = inb (isa_dev->id_iobase + ZE_RESET); + DELAY(5000); + outb (isa_dev->id_iobase + ZE_RESET, tmp); + DELAY(5000); + + /* + * query MAM bit in misc register for 10base2 + */ + tmp = inb (isa_dev->id_iobase + ZE_MISC); + sc->mau = tmp & 0x09 ? "10base2" : "10baseT"; + + /* set width/size */ + sc->type_str = "IBM PCMCIA"; + memsize = 16*1024; + sc->memwidth = 16; + + /* allocate 1 xmit buffer */ + sc->smem_ring = sc->smem_start + (ZE_PAGE_SIZE * ZE_TXBUF_SIZE); + sc->txb_cnt = 1; + sc->rec_page_start = ZE_TXBUF_SIZE + ZE_PAGE_OFFSET; + sc->smem_size = memsize; + sc->smem_end = sc->smem_start + memsize; + sc->rec_page_stop = memsize / ZE_PAGE_SIZE + ZE_PAGE_OFFSET; + sc->tx_page_start = ZE_PAGE_OFFSET; + + /* get station address */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + sc->arpcom.ac_enaddr[i] = enet_addr[i]; + + isa_dev->id_msize = memsize; + return 32; +} + +/* + * Install interface into kernel networking data structures + */ +int +ze_attach(isa_dev) + struct isa_device *isa_dev; +{ + struct ze_softc *sc = &ze_softc[isa_dev->id_unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + struct ifaddr *ifa; + struct sockaddr_dl *sdl; + + /* + * Set interface to stopped condition (reset) + */ + ze_stop(isa_dev->id_unit); + + /* + * Initialize ifnet structure + */ + ifp->if_unit = isa_dev->id_unit; + ifp->if_name = "ze" ; + ifp->if_mtu = ETHERMTU; + ifp->if_init = ze_init; + ifp->if_output = ether_output; + ifp->if_start = ze_start; + ifp->if_ioctl = ze_ioctl; + ifp->if_reset = ze_reset; + ifp->if_watchdog = ze_watchdog; + + /* + * Set default state for LLC0 flag (used to disable the tranceiver + * for AUI operation), based on compile-time config option. + */ + if (isa_dev->id_flags & ZE_FLAGS_DISABLE_TRANCEIVER) + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS + | IFF_LLC0); + else + ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_NOTRAILERS); + + /* + * Attach the interface + */ + if_attach(ifp); + + /* + * Search down the ifa address list looking for the AF_LINK type entry + */ + ifa = ifp->if_addrlist; + while ((ifa != 0) && (ifa->ifa_addr != 0) && + (ifa->ifa_addr->sa_family != AF_LINK)) + ifa = ifa->ifa_next; + /* + * If we find an AF_LINK type entry we fill in the hardware address. + * This is useful for netstat(1) to keep track of which interface + * is which. + */ + if ((ifa != 0) && (ifa->ifa_addr != 0)) { + /* + * Fill in the link-level address for this interface + */ + 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); + } + + /* + * Print additional info when attached + */ + printf("ze%d: address %s, type %s (%dbit)%s, MAU %s\n", + isa_dev->id_unit, + ether_sprintf(sc->arpcom.ac_enaddr), sc->type_str, + sc->memwidth, + (ifp->if_flags & IFF_LLC0 ? " [tranceiver disabled]" : ""), + sc->mau); + + /* + * If BPF is in the kernel, call the attach for it + */ +#if NBPFILTER > 0 + bpfattach(&sc->bpf, ifp, DLT_EN10MB, sizeof(struct ether_header)); +#endif + return 1; +} + +/* + * Reset interface. + */ +void +ze_reset(unit) + int unit; +{ + int s; + + s = splnet(); + + /* + * Stop interface and re-initialize. + */ + ze_stop(unit); + ze_init(unit); + + (void) splx(s); +} + +/* + * Take interface offline. + */ +void +ze_stop(unit) + int unit; +{ + struct ze_softc *sc = &ze_softc[unit]; + int n = 5000; + + /* + * Stop everything on the interface, and select page 0 registers. + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STP); + + /* + * Wait for interface to enter stopped state, but limit # of checks + * to 'n' (about 5ms). It shouldn't even take 5us on modern + * DS8390's, but just in case it's an old one. + */ + while (((inb(sc->nic_addr + ZE_P0_ISR) & ZE_ISR_RST) == 0) && --n); + +} + +/* + * Device timeout/watchdog routine. Entered if the device neglects to + * generate an interrupt after a transmit has been started on it. + */ +void +ze_watchdog(unit) + int unit; +{ +#if 1 + struct ze_softc *sc = &ze_softc[unit]; + u_char isr, imr; + u_short imask; + + /* select page zero */ + outb (sc->nic_addr + ZE_P0_CR, + (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_0); + + /* read interrupt status register */ + isr = inb (sc->nic_addr + ZE_P0_ISR) & 0xff; + + /* select page two */ + outb (sc->nic_addr + ZE_P0_CR, + (inb (sc->nic_addr + ZE_P0_CR) & 0x3f) | ZE_CR_PAGE_2); + + /* read interrupt mask register */ + imr = inb (sc->nic_addr + ZE_P2_IMR) & 0xff; + + imask = inb(IO_ICU2) << 8 | inb(IO_ICU1); + + log (LOG_ERR, "ze%d: device timeout, isr=%02x, imr=%02x, imask=%04x\n", + unit, isr, imr, imask); +#else + log(LOG_ERR, "ze%d: device timeout\n", unit); +#endif + + ze_reset(unit); +} + +/* + * Initialize device. + */ +void +ze_init(unit) + int unit; +{ + struct ze_softc *sc = &ze_softc[unit]; + struct ifnet *ifp = &sc->arpcom.ac_if; + int i, s; + u_char command; + + + /* address not known */ + if (ifp->if_addrlist == (struct ifaddr *)0) return; + + /* + * Initialize the NIC in the exact order outlined in the NS manual. + * This init procedure is "mandatory"...don't change what or when + * things happen. + */ + s = splnet(); + + /* reset transmitter flags */ + sc->data_buffered = 0; + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_timer = 0; + + sc->txb_next = 0; + + /* This variable is used below - don't move this assignment */ + sc->next_packet = sc->rec_page_start + 1; + + /* + * Set interface for page 0, Remote DMA complete, Stopped + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STP); + + if (sc->memwidth == 16) { + /* + * Set FIFO threshold to 8, No auto-init Remote DMA, + * byte order=80x86, word-wide DMA xfers + */ + outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1|ZE_DCR_WTS); + } else { + /* + * Same as above, but byte-wide DMA xfers + */ + outb(sc->nic_addr + ZE_P0_DCR, ZE_DCR_FT1); + } + + /* + * Clear Remote Byte Count Registers + */ + outb(sc->nic_addr + ZE_P0_RBCR0, 0); + outb(sc->nic_addr + ZE_P0_RBCR1, 0); + + /* + * Enable reception of broadcast packets + */ + outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB); + + /* + * Place NIC in internal loopback mode + */ + outb(sc->nic_addr + ZE_P0_TCR, ZE_TCR_LB0); + + /* + * Initialize transmit/receive (ring-buffer) Page Start + */ + outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start); + outb(sc->nic_addr + ZE_P0_PSTART, sc->rec_page_start); + + /* + * Initialize Receiver (ring-buffer) Page Stop and Boundry + */ + outb(sc->nic_addr + ZE_P0_PSTOP, sc->rec_page_stop); + outb(sc->nic_addr + ZE_P0_BNRY, sc->rec_page_start); + + /* + * Clear all interrupts. A '1' in each bit position clears the + * corresponding flag. + */ + outb(sc->nic_addr + ZE_P0_ISR, 0xff); + + /* + * Enable the following interrupts: receive/transmit complete, + * receive/transmit error, and Receiver OverWrite. + * + * Counter overflow and Remote DMA complete are *not* enabled. + */ + outb(sc->nic_addr + ZE_P0_IMR, + ZE_IMR_PRXE|ZE_IMR_PTXE|ZE_IMR_RXEE|ZE_IMR_TXEE|ZE_IMR_OVWE); + + /* + * Program Command Register for page 1 + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STP); + + /* + * Copy out our station address + */ + for (i = 0; i < ETHER_ADDR_LEN; ++i) + outb(sc->nic_addr + ZE_P1_PAR0 + i, sc->arpcom.ac_enaddr[i]); + +#if NBPFILTER > 0 + /* + * Initialize multicast address hashing registers to accept + * all multicasts (only used when in promiscuous mode) + */ + for (i = 0; i < 8; ++i) + outb(sc->nic_addr + ZE_P1_MAR0 + i, 0xff); +#endif + + /* + * Set Current Page pointer to next_packet (initialized above) + */ + outb(sc->nic_addr + ZE_P1_CURR, sc->next_packet); + + /* + * Set Command Register for page 0, Remote DMA complete, + * and interface Start. + */ + outb(sc->nic_addr + ZE_P1_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * Take interface out of loopback + */ + outb(sc->nic_addr + ZE_P0_TCR, 0); + +#if 0 + /* + * If this is a 3Com board, the tranceiver must be software enabled + * (there is no settable hardware default). + */ + if (sc->vendor == ZE_VENDOR_3COM) { + if (ifp->if_flags & IFF_LLC0) { + outb(sc->asic_addr + ZE_3COM_CR, 0); + } else { + outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL); + } + } +#endif + + /* + * Set 'running' flag, and clear output active flag. + */ + ifp->if_flags |= IFF_RUNNING; + ifp->if_flags &= ~IFF_OACTIVE; + + /* + * ...and attempt to start output + */ + ze_start(ifp); + + (void) splx(s); +} + +/* + * This routine actually starts the transmission on the interface + */ +static inline void +ze_xmit(ifp) + struct ifnet *ifp; +{ + struct ze_softc *sc = &ze_softc[ifp->if_unit]; + u_short len = sc->txb_next_len; + + /* + * Set NIC for page 0 register access + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * Set TX buffer start page + */ + outb(sc->nic_addr + ZE_P0_TPSR, sc->tx_page_start + + sc->txb_next * ZE_TXBUF_SIZE); + + /* + * Set TX length + */ + outb(sc->nic_addr + ZE_P0_TBCR0, len & 0xff); + outb(sc->nic_addr + ZE_P0_TBCR1, len >> 8); + + /* + * Set page 0, Remote DMA complete, Transmit Packet, and *Start* + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_TXP|ZE_CR_STA); + + sc->xmit_busy = 1; + sc->data_buffered = 0; + + /* + * Switch buffers if we are doing double-buffered transmits + */ + if ((sc->txb_next == 0) && (sc->txb_cnt > 1)) + sc->txb_next = 1; + else + sc->txb_next = 0; + + /* + * Set a timer just in case we never hear from the board again + */ + ifp->if_timer = 2; +} + +/* + * Start output on interface. + * We make two assumptions here: + * 1) that the current priority is set to splnet _before_ this code + * is called *and* is returned to the appropriate priority after + * return + * 2) that the IFF_OACTIVE flag is checked before this code is called + * (i.e. that the output part of the interface is idle) + */ +void +ze_start(ifp) + struct ifnet *ifp; +{ + struct ze_softc *sc = &ze_softc[ifp->if_unit]; + struct mbuf *m0, *m; + caddr_t buffer; + int len; + u_char laar_tmp; + +outloop: + /* + * See if there is room to send more data (i.e. one or both of the + * buffers is empty). + */ + if (sc->data_buffered) + if (sc->xmit_busy) { + /* + * No room. Indicate this to the outside world + * and exit. + */ + ifp->if_flags |= IFF_OACTIVE; + return; + } else { + /* + * Data is buffered, but we're not transmitting, so + * start the xmit on the buffered data. + * Note that ze_xmit() resets the data_buffered flag + * before returning. + */ + ze_xmit(ifp); + } + + IF_DEQUEUE(&sc->arpcom.ac_if.if_snd, m); + if (m == NULL) { + /* + * The following isn't pretty; we are using the !OACTIVE flag to + * indicate to the outside world that we can accept an additional + * packet rather than that the transmitter is _actually_ + * active. Indeed, the transmitter may be active, but if we haven't + * filled the secondary buffer with data then we still want to + * accept more. + * Note that it isn't necessary to test the data_buffered flag - + * we wouldn't have tried to de-queue the packet in the first place + * if it was set. + */ + ifp->if_flags &= ~IFF_OACTIVE; + return; + } + + /* + * Copy the mbuf chain into the transmit buffer + */ +#if 0 + /* + * Enable 16bit access to shared memory on WD/SMC boards + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + laar_tmp = inb(sc->asic_addr + ZE_WD_LAAR); + outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp | ZE_WD_LAAR_M16EN); + } +#endif + + buffer = sc->smem_start + (sc->txb_next * ZE_TXBUF_SIZE * ZE_PAGE_SIZE); + len = 0; + for (m0 = m; m != 0; m = m->m_next) { + bcopy(mtod(m, caddr_t), buffer, m->m_len); + buffer += m->m_len; + len += m->m_len; + } + +#if 0 + /* + * Restore previous shared mem access type + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + outb(sc->asic_addr + ZE_WD_LAAR, laar_tmp); + } +#endif + + sc->txb_next_len = MAX(len, ETHER_MIN_LEN); + + if (sc->txb_cnt > 1) + /* + * only set 'buffered' flag if doing multiple buffers + */ + sc->data_buffered = 1; + + if (sc->xmit_busy == 0) + ze_xmit(ifp); + /* + * If there is BPF support in the configuration, tap off here. + * The following has support for converting trailer packets + * back to normal. + */ +#if NBPFILTER > 0 + if (sc->bpf) { + u_short etype; + int off, datasize, resid; + struct ether_header *eh; + struct trailer_header { + u_short ether_type; + u_short ether_residual; + } trailer_header; + char ether_packet[ETHER_MAX_LEN]; + char *ep; + + ep = ether_packet; + + /* + * We handle trailers below: + * Copy ether header first, then residual data, + * then data. Put all this in a temporary buffer + * 'ether_packet' and send off to bpf. Since the + * system has generated this packet, we assume + * that all of the offsets in the packet are + * correct; if they're not, the system will almost + * certainly crash in m_copydata. + * We make no assumptions about how the data is + * arranged in the mbuf chain (i.e. how much + * data is in each mbuf, if mbuf clusters are + * used, etc.), which is why we use m_copydata + * to get the ether header rather than assume + * that this is located in the first mbuf. + */ + /* copy ether header */ + m_copydata(m0, 0, sizeof(struct ether_header), ep); + eh = (struct ether_header *) ep; + ep += sizeof(struct ether_header); + etype = ntohs(eh->ether_type); + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + datasize = ((etype - ETHERTYPE_TRAIL) << 9); + off = datasize + sizeof(struct ether_header); + + /* copy trailer_header into a data structure */ + m_copydata(m0, off, sizeof(struct trailer_header), + &trailer_header.ether_type); + + /* copy residual data */ + m_copydata(m0, off+sizeof(struct trailer_header), + resid = ntohs(trailer_header.ether_residual) - + sizeof(struct trailer_header), ep); + ep += resid; + + /* copy data */ + m_copydata(m0, sizeof(struct ether_header), + datasize, ep); + ep += datasize; + + /* restore original ether packet type */ + eh->ether_type = trailer_header.ether_type; + + bpf_tap(sc->bpf, ether_packet, ep - ether_packet); + } else + bpf_mtap(sc->bpf, m0); + } +#endif + + m_freem(m0); + + /* + * If we are doing double-buffering, a buffer might be free to + * fill with another packet, so loop back to the top. + */ + if (sc->txb_cnt > 1) + goto outloop; + else { + ifp->if_flags |= IFF_OACTIVE; + return; + } +} + +/* + * Ethernet interface receiver interrupt. + */ +static inline void /* only called from one place, so may as well integrate */ +ze_rint(unit) + int unit; +{ + register struct ze_softc *sc = &ze_softc[unit]; + u_char boundry, current; + u_short len; + struct ze_ring *packet_ptr; + + /* + * Set NIC to page 1 registers to get 'current' pointer + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STA); + + /* + * 'sc->next_packet' is the logical beginning of the ring-buffer - i.e. + * it points to where new data has been buffered. The 'CURR' + * (current) register points to the logical end of the ring-buffer + * - i.e. it points to where additional new data will be added. + * We loop here until the logical beginning equals the logical + * end (or in other words, until the ring-buffer is empty). + */ + while (sc->next_packet != inb(sc->nic_addr + ZE_P1_CURR)) { + + /* get pointer to this buffer header structure */ + packet_ptr = (struct ze_ring *)(sc->smem_ring + + (sc->next_packet - sc->rec_page_start) * ZE_PAGE_SIZE); + + /* + * The byte count includes the FCS - Frame Check Sequence (a + * 32 bit CRC). + */ + len = packet_ptr->count; + if ((len >= ETHER_MIN_LEN) && (len <= ETHER_MAX_LEN)) { + /* + * Go get packet. len - 4 removes CRC from length. + * (packet_ptr + 1) points to data just after the packet ring + * header (+4 bytes) + */ + ze_get_packet(sc, (caddr_t)(packet_ptr + 1), len - 4); + ++sc->arpcom.ac_if.if_ipackets; + } else { + /* + * Really BAD...probably indicates that the ring pointers + * are corrupted. Also seen on early rev chips under + * high load - the byte order of the length gets switched. + */ + log(LOG_ERR, + "ze%d: shared memory corrupt - invalid packet length %d\n", + unit, len); + ze_reset(unit); + return; + } + + /* + * Update next packet pointer + */ + sc->next_packet = packet_ptr->next_packet; + + /* + * Update NIC boundry pointer - being careful to keep it + * one buffer behind. (as recommended by NS databook) + */ + boundry = sc->next_packet - 1; + if (boundry < sc->rec_page_start) + boundry = sc->rec_page_stop - 1; + + /* + * Set NIC to page 0 registers to update boundry register + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + outb(sc->nic_addr + ZE_P0_BNRY, boundry); + + /* + * Set NIC to page 1 registers before looping to top (prepare to + * get 'CURR' current pointer) + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_PAGE_1|ZE_CR_RD2|ZE_CR_STA); + } +} + +/* + * Ethernet interface interrupt processor + */ +void +zeintr(unit) + int unit; +{ + struct ze_softc *sc = &ze_softc[unit]; + u_char isr; + + /* + * Set NIC to page 0 registers + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * loop until there are no more new interrupts + */ + while (isr = inb(sc->nic_addr + ZE_P0_ISR)) { + + /* + * reset all the bits that we are 'acknowleging' + * by writing a '1' to each bit position that was set + * (writing a '1' *clears* the bit) + */ + outb(sc->nic_addr + ZE_P0_ISR, isr); + + /* + * Transmit error. If a TX completed with an error, we end up + * throwing the packet away. Really the only error that is + * possible is excessive collisions, and in this case it is + * best to allow the automatic mechanisms of TCP to backoff + * the flow. Of course, with UDP we're screwed, but this is + * expected when a network is heavily loaded. + */ + if (isr & ZE_ISR_TXE) { + u_char tsr = inb(sc->nic_addr + ZE_P0_TSR); + u_char ncr = inb(sc->nic_addr + ZE_P0_NCR); + + /* + * Excessive collisions (16) + */ + if ((tsr & ZE_TSR_ABT) && (ncr == 0)) { + /* + * When collisions total 16, the P0_NCR will + * indicate 0, and the TSR_ABT is set. + */ + sc->arpcom.ac_if.if_collisions += 16; + } else + sc->arpcom.ac_if.if_collisions += ncr; + + /* + * update output errors counter + */ + ++sc->arpcom.ac_if.if_oerrors; + + /* + * reset tx busy and output active flags + */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + /* + * clear watchdog timer + */ + sc->arpcom.ac_if.if_timer = 0; + } + + + /* + * Receiver Error. One or more of: CRC error, frame alignment error + * FIFO overrun, or missed packet. + */ + if (isr & ZE_ISR_RXE) { + ++sc->arpcom.ac_if.if_ierrors; +#ifdef ZE_DEBUG +#if 0 + printf("ze%d: receive error %x\n", unit, + inb(sc->nic_addr + ZE_P0_RSR)); +#else + printf("ze%d: receive error %b\n", unit, + inb(sc->nic_addr + ZE_P0_RSR), + "\20\8DEF\7REC DISAB\6PHY/MC\5MISSED\4OVR\3ALIGN\2FCS\1RCVD"); +#endif +#endif + } + + /* + * Overwrite warning. In order to make sure that a lockup + * of the local DMA hasn't occurred, we reset and + * re-init the NIC. The NSC manual suggests only a + * partial reset/re-init is necessary - but some + * chips seem to want more. The DMA lockup has been + * seen only with early rev chips - Methinks this + * bug was fixed in later revs. -DG + */ + if (isr & ZE_ISR_OVW) { + ++sc->arpcom.ac_if.if_ierrors; +#if 0 + /* sigh. this happens too often on our net */ + log(LOG_WARNING, + "ze%d: warning - receiver ring buffer overrun\n", + unit); +#endif + /* + * Stop/reset/re-init NIC + */ + ze_reset(unit); + } + + /* + * Transmission completed normally. + */ + if (isr & ZE_ISR_PTX) { + + /* + * reset tx busy and output active flags + */ + sc->xmit_busy = 0; + sc->arpcom.ac_if.if_flags &= ~IFF_OACTIVE; + + /* + * clear watchdog timer + */ + sc->arpcom.ac_if.if_timer = 0; + + /* + * Update total number of successfully transmitted + * packets. + */ + ++sc->arpcom.ac_if.if_opackets; + + /* + * Add in total number of collisions on last + * transmission. + */ + sc->arpcom.ac_if.if_collisions += inb(sc->nic_addr + + ZE_P0_TBCR0); + } + + /* + * Receive Completion. Go and get the packet. + * XXX - Doing this on an error is dubious because there + * shouldn't be any data to get (we've configured the + * interface to not accept packets with errors). + */ + if (isr & (ZE_ISR_PRX|ZE_ISR_RXE)) { +#if 0 + /* + * Enable access to shared memory on WD/SMC boards + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + outb(sc->asic_addr + ZE_WD_LAAR, + inb(sc->asic_addr + ZE_WD_LAAR) + | ZE_WD_LAAR_M16EN); + } +#endif + ze_rint (unit); + +#if 0 + /* + * Disable access to shared memory + */ + if (sc->memwidth == 16) + if (sc->vendor == ZE_VENDOR_WD_SMC) { + outb(sc->asic_addr + ZE_WD_LAAR, + inb(sc->asic_addr + ZE_WD_LAAR) + & ~ZE_WD_LAAR_M16EN); + } +#endif + } + + /* + * If it looks like the transmitter can take more data, + * attempt to start output on the interface. If data is + * already buffered and ready to go, send it first. + */ + if ((sc->arpcom.ac_if.if_flags & IFF_OACTIVE) == 0) { + if (sc->data_buffered) + ze_xmit(&sc->arpcom.ac_if); + ze_start(&sc->arpcom.ac_if); + } + + /* + * return NIC CR to standard state: page 0, remote DMA complete, + * start (toggling the TXP bit off, even if was just set + * in the transmit routine, is *okay* - it is 'edge' + * triggered from low to high) + */ + outb(sc->nic_addr + ZE_P0_CR, ZE_CR_RD2|ZE_CR_STA); + + /* + * If the Network Talley Counters overflow, read them to + * reset them. It appears that old 8390's won't + * clear the ISR flag otherwise - resulting in an + * infinite loop. + */ + if (isr & ZE_ISR_CNT) { + (void) inb(sc->nic_addr + ZE_P0_CNTR0); + (void) inb(sc->nic_addr + ZE_P0_CNTR1); + (void) inb(sc->nic_addr + ZE_P0_CNTR2); + } + } +} + +/* + * Process an ioctl request. This code needs some work - it looks + * pretty ugly. + */ +int +ze_ioctl(ifp, command, data) + register struct ifnet *ifp; + int command; + caddr_t data; +{ + register struct ifaddr *ifa = (struct ifaddr *)data; + struct ze_softc *sc = &ze_softc[ifp->if_unit]; + struct ifreq *ifr = (struct ifreq *)data; + int s, error = 0; + + s = splnet(); + + switch (command) { + + case SIOCSIFADDR: + ifp->if_flags |= IFF_UP; + + switch (ifa->ifa_addr->sa_family) { +#ifdef INET + case AF_INET: + ze_init(ifp->if_unit); /* before arpwhohas */ + /* + * See if another station has *our* IP address. + * i.e.: There is an address conflict! If a + * conflict exists, a message is sent to the + * console. + */ + ((struct arpcom *)ifp)->ac_ipaddr = + IA_SIN(ifa)->sin_addr; + arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr); + break; +#endif +#ifdef NS + /* + * XXX - This code is probably wrong + */ + 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); + else { + /* + * + */ + bcopy((caddr_t)ina->x_host.c_host, + (caddr_t)sc->arpcom.ac_enaddr, + sizeof(sc->arpcom.ac_enaddr)); + } + /* + * Set new address + */ + ze_init(ifp->if_unit); + break; + } +#endif + default: + ze_init(ifp->if_unit); + break; + } + break; + + case SIOCSIFFLAGS: + /* + * If interface is marked down and it is running, then stop it + */ + if (((ifp->if_flags & IFF_UP) == 0) && + (ifp->if_flags & IFF_RUNNING)) { + ze_stop(ifp->if_unit); + ifp->if_flags &= ~IFF_RUNNING; + } else { + /* + * If interface is marked up and it is stopped, then start it + */ + if ((ifp->if_flags & IFF_UP) && + ((ifp->if_flags & IFF_RUNNING) == 0)) + ze_init(ifp->if_unit); + } +#if NBPFILTER > 0 + if (ifp->if_flags & IFF_PROMISC) { + /* + * Set promiscuous mode on interface. + * XXX - for multicasts to work, we would need to + * write 1's in all bits of multicast + * hashing array. For now we assume that + * this was done in ze_init(). + */ + outb(sc->nic_addr + ZE_P0_RCR, + ZE_RCR_PRO|ZE_RCR_AM|ZE_RCR_AB); + } else { + /* + * XXX - for multicasts to work, we would need to + * rewrite the multicast hashing array with the + * proper hash (would have been destroyed above). + */ + outb(sc->nic_addr + ZE_P0_RCR, ZE_RCR_AB); + } +#endif +#if 0 + /* + * An unfortunate hack to provide the (required) software control + * of the tranceiver for 3Com boards. The LLC0 flag disables + * the tranceiver if set. + */ + if (sc->vendor == ZE_VENDOR_3COM) { + if (ifp->if_flags & IFF_LLC0) { + outb(sc->asic_addr + ZE_3COM_CR, 0); + } else { + outb(sc->asic_addr + ZE_3COM_CR, ZE_3COM_CR_XSEL); + } + } +#endif + + break; + + default: + error = EINVAL; + } + (void) splx(s); + return (error); +} + +/* + * Macro to calculate a new address within shared memory when given an offset + * from an address, taking into account ring-wrap. + */ +#define ringoffset(sc, start, off, type) \ + ((type)( ((caddr_t)(start)+(off) >= (sc)->smem_end) ? \ + (((caddr_t)(start)+(off))) - (sc)->smem_end \ + + (sc)->smem_ring: \ + ((caddr_t)(start)+(off)) )) + +/* + * Retreive packet from shared memory and send to the next level up via + * ether_input(). If there is a BPF listener, give a copy to BPF, too. + */ +void +ze_get_packet(sc, buf, len) + struct ze_softc *sc; + char *buf; + u_short len; +{ + struct ether_header *eh; + struct mbuf *m, *head = NULL, *ze_ring_to_mbuf(); + u_short off; + int resid; + u_short etype; + struct trailer_header { + u_short trail_type; + u_short trail_residual; + } trailer_header; + + /* Allocate a header mbuf */ + MGETHDR(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + goto bad; + m->m_pkthdr.rcvif = &sc->arpcom.ac_if; + m->m_pkthdr.len = len; + m->m_len = 0; + head = m; + + eh = (struct ether_header *)buf; + + /* The following sillines is to make NFS happy */ +#define EROUND ((sizeof(struct ether_header) + 3) & ~3) +#define EOFF (EROUND - sizeof(struct ether_header)) + + /* + * The following assumes there is room for + * the ether header in the header mbuf + */ + head->m_data += EOFF; + bcopy(buf, mtod(head, caddr_t), sizeof(struct ether_header)); + buf += sizeof(struct ether_header); + head->m_len += sizeof(struct ether_header); + len -= sizeof(struct ether_header); + + etype = ntohs((u_short)eh->ether_type); + + /* + * Deal with trailer protocol: + * If trailer protocol, calculate the datasize as 'off', + * which is also the offset to the trailer header. + * Set resid to the amount of packet data following the + * trailer header. + * Finally, copy residual data into mbuf chain. + */ + if (etype >= ETHERTYPE_TRAIL && + etype < ETHERTYPE_TRAIL+ETHERTYPE_NTRAILER) { + + off = (etype - ETHERTYPE_TRAIL) << 9; + if ((off + sizeof(struct trailer_header)) > len) + goto bad; /* insanity */ + + eh->ether_type = *ringoffset(sc, buf, off, u_short *); + resid = ntohs(*ringoffset(sc, buf, off+2, u_short *)); + + if ((off + resid) > len) goto bad; /* insanity */ + + resid -= sizeof(struct trailer_header); + if (resid < 0) goto bad; /* insanity */ + + m = ze_ring_to_mbuf(sc, ringoffset(sc, buf, off+4, char *), head, resid); + if (m == NULL) goto bad; + + len = off; + head->m_pkthdr.len -= 4; /* subtract trailer header */ + } + + /* + * Pull packet off interface. Or if this was a trailer packet, + * the data portion is appended. + */ + m = ze_ring_to_mbuf(sc, buf, m, len); + if (m == NULL) goto bad; + +#if NBPFILTER > 0 + /* + * Check if there's a BPF listener on this interface. + * If so, hand off the raw packet to bpf. + */ + if (sc->bpf) { + bpf_mtap(sc->bpf, head); + + /* + * 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 ((sc->arpcom.ac_if.if_flags & IFF_PROMISC) && + 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(head); + return; + } + } +#endif + + /* + * Fix up data start offset in mbuf to point past ether header + */ + m_adj(head, sizeof(struct ether_header)); + + /* + * silly ether_input routine needs 'type' in host byte order + */ + eh->ether_type = ntohs(eh->ether_type); + + ether_input(&sc->arpcom.ac_if, eh, head); + return; + +bad: if (head) + m_freem(head); + return; +} + +/* + * Supporting routines + */ + +/* + * Given a source and destination address, copy 'amount' of a packet from + * the ring buffer into a linear destination buffer. Takes into account + * ring-wrap. + */ +static inline char * +ze_ring_copy(sc,src,dst,amount) + struct ze_softc *sc; + char *src; + char *dst; + u_short amount; +{ + u_short tmp_amount; + + /* does copy wrap to lower addr in ring buffer? */ + if (src + amount > sc->smem_end) { + tmp_amount = sc->smem_end - src; + bcopy(src,dst,tmp_amount); /* copy amount up to end of smem */ + amount -= tmp_amount; + src = sc->smem_ring; + dst += tmp_amount; + } + + bcopy(src, dst, amount); + + return(src + amount); +} + +/* + * Copy data from receive buffer to end of mbuf chain + * allocate additional mbufs as needed. return pointer + * to last mbuf in chain. + * sc = ze info (softc) + * src = pointer in ze ring buffer + * dst = pointer to last mbuf in mbuf chain to copy to + * amount = amount of data to copy + */ +struct mbuf * +ze_ring_to_mbuf(sc,src,dst,total_len) + struct ze_softc *sc; + char *src; + struct mbuf *dst; + u_short total_len; +{ + register struct mbuf *m = dst; + + while (total_len) { + register u_short amount = min(total_len, M_TRAILINGSPACE(m)); + + if (amount == 0) { /* no more data in this mbuf, alloc another */ + /* + * If there is enough data for an mbuf cluster, attempt + * to allocate one of those, otherwise, a regular + * mbuf will do. + * Note that a regular mbuf is always required, even if + * we get a cluster - getting a cluster does not + * allocate any mbufs, and one is needed to assign + * the cluster to. The mbuf that has a cluster + * extension can not be used to contain data - only + * the cluster can contain data. + */ + dst = m; + MGET(m, M_DONTWAIT, MT_DATA); + if (m == NULL) + return (0); + + if (total_len >= MINCLSIZE) + MCLGET(m, M_DONTWAIT); + + m->m_len = 0; + dst->m_next = m; + amount = min(total_len, M_TRAILINGSPACE(m)); + } + + src = ze_ring_copy(sc, src, mtod(m, caddr_t) + m->m_len, amount); + + m->m_len += amount; + total_len -= amount; + + } + return (m); +} +#endif + diff --git a/sys/i386/isa/if_zereg.h b/sys/i386/isa/if_zereg.h new file mode 100644 index 000000000000..3cd501f682cb --- /dev/null +++ b/sys/i386/isa/if_zereg.h @@ -0,0 +1,859 @@ +/* + * National Semiconductor DS8390 NIC register definitions + * + * if_edreg.h,v + * Revision 1.1.2.1 1993/07/21 13:50:04 cgd + * from davidg: + * Added config file override for memory size and added flags to force + * 8bit or 16bit operation, and a flag to disable transmitter double buffering. + * See the updated "ed.relnotes" file for information about how to set + * the flags. + * This should be considered the first "production" release. It still + * needs a manual page, though. + * + * Revision 1.1 1993/07/03 12:21:07 cgd + * add support for David Greenman "ed" driver + * + * Revision 1.2 93/06/23 03:03:05 davidg + * added some additional definitions for the 83C584 bus interface + * chip (SMC/WD boards) + * + * Revision 1.1 93/06/23 03:01:07 davidg + * Initial revision + * + */ + +/* + * Page 0 register offsets + */ +#define ZE_P0_CR 0x00 /* Command Register */ + +#define ZE_P0_CLDA0 0x01 /* Current Local DMA Addr low (read) */ +#define ZE_P0_PSTART 0x01 /* Page Start register (write) */ + +#define ZE_P0_CLDA1 0x02 /* Current Local DMA Addr high (read) */ +#define ZE_P0_PSTOP 0x02 /* Page Stop register (write) */ + +#define ZE_P0_BNRY 0x03 /* Boundary Pointer */ + +#define ZE_P0_TSR 0x04 /* Transmit Status Register (read) */ +#define ZE_P0_TPSR 0x04 /* Transmit Page Start (write) */ + +#define ZE_P0_NCR 0x05 /* Number of Collisions Reg (read) */ +#define ZE_P0_TBCR0 0x05 /* Transmit Byte count, low (write) */ + +#define ZE_P0_FIFO 0x06 /* FIFO register (read) */ +#define ZE_P0_TBCR1 0x06 /* Transmit Byte count, high (write) */ + +#define ZE_P0_ISR 0x07 /* Interrupt Status Register */ + +#define ZE_P0_CRDA0 0x08 /* Current Remote DMA Addr low (read) */ +#define ZE_P0_RSAR0 0x08 /* Remote Start Address low (write) */ + +#define ZE_P0_CRDA1 0x09 /* Current Remote DMA Addr high (read) */ +#define ZE_P0_RSAR1 0x09 /* Remote Start Address high (write) */ + +#define ZE_P0_RBCR0 0x0a /* Remote Byte Count low (write) */ + +#define ZE_P0_RBCR1 0x0b /* Remote Byte Count high (write) */ + +#define ZE_P0_RSR 0x0c /* Receive Status (read) */ +#define ZE_P0_RCR 0x0c /* Receive Configuration Reg (write) */ + +#define ZE_P0_CNTR0 0x0d /* frame alignment error counter (read) */ +#define ZE_P0_TCR 0x0d /* Transmit Configuration Reg (write) */ + +#define ZE_P0_CNTR1 0x0e /* CRC error counter (read) */ +#define ZE_P0_DCR 0x0e /* Data Configuration Reg (write) */ + +#define ZE_P0_CNTR2 0x0f /* missed packet counter (read) */ +#define ZE_P0_IMR 0x0f /* Interrupt Mask Register (write) */ + +/* + * Page 1 register offsets + */ +#define ZE_P1_CR 0x00 /* Command Register */ +#define ZE_P1_PAR0 0x01 /* Physical Address Register 0 */ +#define ZE_P1_PAR1 0x02 /* Physical Address Register 1 */ +#define ZE_P1_PAR2 0x03 /* Physical Address Register 2 */ +#define ZE_P1_PAR3 0x04 /* Physical Address Register 3 */ +#define ZE_P1_PAR4 0x05 /* Physical Address Register 4 */ +#define ZE_P1_PAR5 0x06 /* Physical Address Register 5 */ +#define ZE_P1_CURR 0x07 /* Current RX ring-buffer page */ +#define ZE_P1_MAR0 0x08 /* Multicast Address Register 0 */ +#define ZE_P1_MAR1 0x09 /* Multicast Address Register 1 */ +#define ZE_P1_MAR2 0x0a /* Multicast Address Register 2 */ +#define ZE_P1_MAR3 0x0b /* Multicast Address Register 3 */ +#define ZE_P1_MAR4 0x0c /* Multicast Address Register 4 */ +#define ZE_P1_MAR5 0x0d /* Multicast Address Register 5 */ +#define ZE_P1_MAR6 0x0e /* Multicast Address Register 6 */ +#define ZE_P1_MAR7 0x0f /* Multicast Address Register 7 */ + +/* + * Page 2 register offsets + */ +#define ZE_P2_CR 0x00 /* Command Register */ +#define ZE_P2_PSTART 0x01 /* Page Start (read) */ +#define ZE_P2_CLDA0 0x01 /* Current Local DMA Addr 0 (write) */ +#define ZE_P2_PSTOP 0x02 /* Page Stop (read) */ +#define ZE_P2_CLDA1 0x02 /* Current Local DMA Addr 1 (write) */ +#define ZE_P2_RNPP 0x03 /* Remote Next Packet Pointer */ +#define ZE_P2_TPSR 0x04 /* Transmit Page Start (read) */ +#define ZE_P2_LNPP 0x05 /* Local Next Packet Pointer */ +#define ZE_P2_ACU 0x06 /* Address Counter Upper */ +#define ZE_P2_ACL 0x07 /* Address Counter Lower */ +#define ZE_P2_RCR 0x0c /* Receive Configuration Register (read) */ +#define ZE_P2_TCR 0x0d /* Transmit Configuration Register (read) */ +#define ZE_P2_DCR 0x0e /* Data Configuration Register (read) */ +#define ZE_P2_IMR 0x0f /* Interrupt Mask Register (read) */ + +/* + * Command Register (CR) definitions + */ + +/* + * STP: SToP. Software reset command. Takes the controller offline. No + * packets will be received or transmitted. Any reception or + * transmission in progress will continue to completion before + * entering reset state. To exit this state, the STP bit must + * reset and the STA bit must be set. The software reset has + * executed only when indicated by the RST bit in the ISR being + * set. + */ +#define ZE_CR_STP 0x01 + +/* + * STA: STArt. This bit is used to activate the NIC after either power-up, + * or when the NIC has been put in reset mode by software command + * or error. + */ +#define ZE_CR_STA 0x02 + +/* + * TXP: Transmit Packet. This bit must be set to indicate transmission of + * a packet. TXP is internally reset either after the transmission is + * completed or aborted. This bit should be set only after the Transmit + * Byte Count and Transmit Page Start register have been programmed. + */ +#define ZE_CR_TXP 0x04 + +/* + * RD0, RD1, RD2: Remote DMA Command. These three bits control the operation + * of the remote DMA channel. RD2 can be set to abort any remote DMA + * command in progress. The Remote Byte Count registers should be cleared + * when a remote DMA has been aborted. The Remote Start Addresses are not + * restored to the starting address if the remote DMA is aborted. + * + * RD2 RD1 RD0 function + * 0 0 0 not allowed + * 0 0 1 remote read + * 0 1 0 remote write + * 0 1 1 send packet + * 1 X X abort + */ +#define ZE_CR_RD0 0x08 +#define ZE_CR_RD1 0x10 +#define ZE_CR_RD2 0x20 + +/* + * PS0, PS1: Page Select. The two bits select which register set or 'page' to + * access. + * + * PS1 PS0 page + * 0 0 0 + * 0 1 1 + * 1 0 2 + * 1 1 reserved + */ +#define ZE_CR_PS0 0x40 +#define ZE_CR_PS1 0x80 +/* bit encoded aliases */ +#define ZE_CR_PAGE_0 0x00 /* (for consistency) */ +#define ZE_CR_PAGE_1 0x40 +#define ZE_CR_PAGE_2 0x80 + +/* + * Interrupt Status Register (ISR) definitions + */ + +/* + * PRX: Packet Received. Indicates packet received with no errors. + */ +#define ZE_ISR_PRX 0x01 + +/* + * PTX: Packet Transmitted. Indicates packet transmitted with no errors. + */ +#define ZE_ISR_PTX 0x02 + +/* + * RXE: Receive Error. Indicates that a packet was received with one or more + * the following errors: CRC error, frame alignment error, FIFO overrun, + * missed packet. + */ +#define ZE_ISR_RXE 0x04 + +/* + * TXE: Transmission Error. Indicates that an attempt to transmit a packet + * resulted in one or more of the following errors: excessive + * collisions, FIFO underrun. + */ +#define ZE_ISR_TXE 0x08 + +/* + * OVW: OverWrite. Indicates a receive ring-buffer overrun. Incoming network + * would exceed (has exceeded?) the boundry pointer, resulting in data + * that was previously received and not yet read from the buffer to be + * overwritten. + */ +#define ZE_ISR_OVW 0x10 + +/* + * CNT: Counter Overflow. Set when the MSB of one or more of the Network Talley + * Counters has been set. + */ +#define ZE_ISR_CNT 0x20 + +/* + * RDC: Remote Data Complete. Indicates that a Remote DMA operation has completed. + */ +#define ZE_ISR_RDC 0x40 + +/* + * RST: Reset status. Set when the NIC enters the reset state and cleared when a + * Start Command is issued to the CR. This bit is also set when a receive + * ring-buffer overrun (OverWrite) occurs and is cleared when one or more + * packets have been removed from the ring. This is a read-only bit. + */ +#define ZE_ISR_RST 0x80 + +/* + * Interrupt Mask Register (IMR) definitions + */ + +/* + * PRXE: Packet Received interrupt Enable. If set, a received packet will cause + * an interrupt. + */ +#define ZE_IMR_PRXE 0x01 + +/* + * PTXE: Packet Transmit interrupt Enable. If set, an interrupt is generated when + * a packet transmission completes. + */ +#define ZE_IMR_PTXE 0x02 + +/* + * RXEE: Receive Error interrupt Enable. If set, an interrupt will occur whenever a + * packet is received with an error. + */ +#define ZE_IMR_RXEE 0x04 + +/* + * TXEE: Transmit Error interrupt Enable. If set, an interrupt will occur whenever + * a transmission results in an error. + */ +#define ZE_IMR_TXEE 0x08 + +/* + * OVWE: OverWrite error interrupt Enable. If set, an interrupt is generated whenever + * the receive ring-buffer is overrun. i.e. when the boundry pointer is exceeded. + */ +#define ZE_IMR_OVWE 0x10 + +/* + * CNTE: Counter overflow interrupt Enable. If set, an interrupt is generated whenever + * the MSB of one or more of the Network Statistics counters has been set. + */ +#define ZE_IMR_CNTE 0x20 + +/* + * RDCE: Remote DMA Complete interrupt Enable. If set, an interrupt is generated + * when a remote DMA transfer has completed. + */ +#define ZE_IMR_RDCE 0x40 + +/* + * bit 7 is unused/reserved + */ + +/* + * Data Configuration Register (DCR) definitions + */ + +/* + * WTS: Word Transfer Select. WTS establishes byte or word transfers for + * both remote and local DMA transfers + */ +#define ZE_DCR_WTS 0x01 + +/* + * BOS: Byte Order Select. BOS sets the byte order for the host. + * Should be 0 for 80x86, and 1 for 68000 series processors + */ +#define ZE_DCR_BOS 0x02 + +/* + * LAS: Long Address Select. When LAS is 1, the contents of the remote + * DMA registers RSAR0 and RSAR1 are used to provide A16-A31 + */ +#define ZE_DCR_LAS 0x04 + +/* + * LS: Loopback Select. When 0, loopback mode is selected. Bits D1 and D2 + * of the TCR must also be programmed for loopback operation. + * When 1, normal operation is selected. + */ +#define ZE_DCR_LS 0x08 + +/* + * AR: Auto-initialize Remote. When 0, data must be removed from ring-buffer + * under program control. When 1, remote DMA is automatically initiated + * and the boundry pointer is automatically updated + */ +#define ZE_DCR_AR 0x10 + +/* + * FT0, FT1: Fifo Threshold select. + * FT1 FT0 Word-width Byte-width + * 0 0 1 word 2 bytes + * 0 1 2 words 4 bytes + * 1 0 4 words 8 bytes + * 1 1 8 words 12 bytes + * + * During transmission, the FIFO threshold indicates the number of bytes + * or words that the FIFO has filled from the local DMA before BREQ is + * asserted. The transmission threshold is 16 bytes minus the receiver + * threshold. + */ +#define ZE_DCR_FT0 0x20 +#define ZE_DCR_FT1 0x40 + +/* + * bit 7 (0x80) is unused/reserved + */ + +/* + * Transmit Configuration Register (TCR) definitions + */ + +/* + * CRC: Inhibit CRC. If 0, CRC will be appended by the transmitter, if 0, CRC + * is not appended by the transmitter. + */ +#define ZE_TCR_CRC 0x01 + +/* + * LB0, LB1: Loopback control. These two bits set the type of loopback that is + * to be performed. + * + * LB1 LB0 mode + * 0 0 0 - normal operation (DCR_LS = 0) + * 0 1 1 - internal loopback (DCR_LS = 0) + * 1 0 2 - external loopback (DCR_LS = 1) + * 1 1 3 - external loopback (DCR_LS = 0) + */ +#define ZE_TCR_LB0 0x02 +#define ZE_TCR_LB1 0x04 + +/* + * ATD: Auto Transmit Disable. Clear for normal operation. When set, allows + * another station to disable the NIC's transmitter by transmitting to + * a multicast address hashing to bit 62. Reception of a multicast address + * hashing to bit 63 enables the transmitter. + */ +#define ZE_TCR_ATD 0x08 + +/* + * OFST: Collision Offset enable. This bit when set modifies the backoff + * algorithm to allow prioritization of nodes. + */ +#define ZE_TCR_OFST 0x10 + +/* + * bits 5, 6, and 7 are unused/reserved + */ + +/* + * Transmit Status Register (TSR) definitions + */ + +/* + * PTX: Packet Transmitted. Indicates successful transmission of packet. + */ +#define ZE_TSR_PTX 0x01 + +/* + * bit 1 (0x02) is unused/reserved + */ + +/* + * COL: Transmit Collided. Indicates that the transmission collided at least + * once with another station on the network. + */ +#define ZE_TSR_COL 0x04 + +/* + * ABT: Transmit aborted. Indicates that the transmission was aborted due to + * excessive collisions. + */ +#define ZE_TSR_ABT 0x08 + +/* + * CRS: Carrier Sense Lost. Indicates that carrier was lost during the + * transmission of the packet. (Transmission is not aborted because + * of a loss of carrier) + */ +#define ZE_TSR_CRS 0x10 + +/* + * FU: FIFO Underrun. Indicates that the NIC wasn't able to access bus/ + * transmission memory before the FIFO emptied. Transmission of the + * packet was aborted. + */ +#define ZE_TSR_FU 0x20 + +/* + * CDH: CD Heartbeat. Indicates that the collision detection circuitry + * isn't working correctly during a collision heartbeat test. + */ +#define ZE_TSR_CDH 0x40 + +/* + * OWC: Out of Window Collision: Indicates that a collision occurred after + * a slot time (51.2us). The transmission is rescheduled just as in + * normal collisions. + */ +#define ZE_TSR_OWC 0x80 + +/* + * Receiver Configuration Register (RCR) definitions + */ + +/* + * SEP: Save Errored Packets. If 0, error packets are discarded. If set to 1, + * packets with CRC and frame errors are not discarded. + */ +#define ZE_RCR_SEP 0x01 + +/* + * AR: Accept Runt packet. If 0, packet with less than 64 byte are discarded. + * If set to 1, packets with less than 64 byte are not discarded. + */ +#define ZE_RCR_AR 0x02 + +/* + * AB: Accept Broadcast. If set, packets sent to the broadcast address will be + * accepted. + */ +#define ZE_RCR_AB 0x04 + +/* + * AM: Accept Multicast. If set, packets sent to a multicast address are checked + * for a match in the hashing array. If clear, multicast packets are ignored. + */ +#define ZE_RCR_AM 0x08 + +/* + * PRO: Promiscuous Physical. If set, all packets with a physical addresses are + * accepted. If clear, a physical destination address must match this + * station's address. Note: for full promiscuous mode, RCR_AB and RCR_AM + * must also be set. In addition, the multicast hashing array must be set + * to all 1's so that all multicast addresses are accepted. + */ +#define ZE_RCR_PRO 0x10 + +/* + * MON: Monitor Mode. If set, packets will be checked for good CRC and framing, + * but are not stored in the ring-buffer. If clear, packets are stored (normal + * operation). + */ +#define ZE_RCR_MON 0x20 + +/* + * bits 6 and 7 are unused/reserved. + */ + +/* + * Receiver Status Register (RSR) definitions + */ + +/* + * PRX: Packet Received without error. + */ +#define ZE_RSR_PRX 0x01 + +/* + * CRC: CRC error. Indicates that a packet has a CRC error. Also set for frame + * alignment errors. + */ +#define ZE_RSR_CRC 0x02 + +/* + * FAE: Frame Alignment Error. Indicates that the incoming packet did not end on + * a byte boundry and the CRC did not match at the last byte boundry. + */ +#define ZE_RSR_FAE 0x04 + +/* + * FO: FIFO Overrun. Indicates that the FIFO was not serviced (during local DMA) + * causing it to overrun. Reception of the packet is aborted. + */ +#define ZE_RSR_FO 0x08 + +/* + * MPA: Missed Packet. Indicates that the received packet couldn't be stored in + * the ring-buffer because of insufficient buffer space (exceeding the + * boundry pointer), or because the transfer to the ring-buffer was inhibited + * by RCR_MON - monitor mode. + */ +#define ZE_RSR_MPA 0x10 + +/* + * PHY: Physical address. If 0, the packet received was sent to a physical address. + * If 1, the packet was accepted because of a multicast/broadcast address + * match. + */ +#define ZE_RSR_PHY 0x20 + +/* + * DIS: Receiver Disabled. Set to indicate that the receiver has enetered monitor + * mode. Cleared when the receiver exits monitor mode. + */ +#define ZE_RSR_DIS 0x40 + +/* + * DFR: Deferring. Set to indicate a 'jabber' condition. The CRS and COL inputs + * are active, and the transceiver has set the CD line as a result of the + * jabber. + */ +#define ZE_RSR_DFR 0x80 + +/* + * receive ring discriptor + * + * The National Semiconductor DS8390 Network interface controller uses + * the following receive ring headers. The way this works is that the + * memory on the interface card is chopped up into 256 bytes blocks. + * A contiguous portion of those blocks are marked for receive packets + * by setting start and end block #'s in the NIC. For each packet that + * is put into the receive ring, one of these headers (4 bytes each) is + * tacked onto the front. + */ +struct ze_ring { + struct edr_status { /* received packet status */ + u_char rs_prx:1, /* packet received intack */ + rs_crc:1, /* crc error */ + rs_fae:1, /* frame alignment error */ + rs_fo:1, /* fifo overrun */ + rs_mpa:1, /* packet received intack */ + rs_phy:1, /* packet received intack */ + rs_dis:1, /* packet received intack */ + rs_dfr:1; /* packet received intack */ + } ze_rcv_status; /* received packet status */ + u_char next_packet; /* pointer to next packet */ + u_short count; /* bytes in packet (length + 4) */ +}; + +/* + * Common constants + */ +#define ZE_PAGE_SIZE 256 /* Size of RAM pages in bytes */ +#define ZE_TXBUF_SIZE 6 /* Size of TX buffer in pages */ +#define ZE_PAGE_OFFSET 0x40 /* mem buffer starts at 0x4000 */ + +/* + * Vendor types + */ +#define ZE_VENDOR_WD_SMC 0x00 /* Western Digital/SMC */ +#define ZE_VENDOR_3COM 0x01 /* 3Com */ + +/* + * Compile-time config flags + */ +/* + * this sets the default for enabling/disablng the tranceiver + */ +#define ZE_FLAGS_DISABLE_TRANCEIVER 0x01 + +/* + * This forces the board to be used in 8/16bit mode even if it + * autoconfigs differently + */ +#define ZE_FLAGS_FORCE_8BIT_MODE 0x02 +#define ZE_FLAGS_FORCE_16BIT_MODE 0x04 + +/* + * This disables the use of double transmit buffers. + */ +#define ZE_FLAGS_NO_DOUBLE_BUFFERING 0x08 + +/* + * definitions for IBM credit card adapter for ethernet + */ + +#define ZE_DATA_IO 0x10 +#define ZE_MISC 0x18 +#define ZE_RESET 0x1F + +#if 0 +/* + * Definitions for Western digital/SMC WD80x3 series ASIC + */ +/* + * Memory Select Register (MSR) + */ +#define ZE_WD_MSR 0 + +#define ZE_WD_MSR_ADDR 0x3f /* Memory decode bits 18-13 */ +#define ZE_WD_MSR_MENB 0x40 /* Memory enable */ +#define ZE_WD_MSR_RST 0x80 /* Reset board */ + +/* + * Interface Configuration Register (ICR) + */ +#define ZE_WD_ICR 1 + +#define ZE_WD_ICR_16BIT 0x01 /* 16-bit interface */ +#define ZE_WD_ICR_OAR 0x02 /* select register. 0=BIO 1=EAR */ +#define ZE_WD_ICR_IR2 0x04 /* high order bit of encoded IRQ */ +#define ZE_WD_ICR_MSZ 0x08 /* memory size (0=8k 1=32k) */ +#define ZE_WD_ICR_RLA 0x10 /* recall LAN address */ +#define ZE_WD_ICR_RX7 0x20 /* recall all but i/o and LAN address */ +#define ZE_WD_ICR_RIO 0x40 /* recall i/o address */ +#define ZE_WD_ICR_STO 0x80 /* store to non-volatile memory */ + +/* + * IO Address Register (IAR) + */ +#define ZE_WD_IAR 2 + +/* + * EEROM Address Register + */ +#define ZE_WD_EAR 3 + +/* + * Interrupt Request Register (IRR) + */ +#define ZE_WD_IRR 4 + +#define ZE_WD_IRR_0WS 0x01 /* use 0 wait-states on 8 bit bus */ +#define ZE_WD_IRR_OUT1 0x02 /* WD83C584 pin 1 output */ +#define ZE_WD_IRR_OUT2 0x04 /* WD83C584 pin 2 output */ +#define ZE_WD_IRR_OUT3 0x08 /* WD83C584 pin 3 output */ +#define ZE_WD_IRR_FLASH 0x10 /* Flash RAM is in the ROM socket */ + +/* + * The three bit of the encoded IRQ are decoded as follows: + * + * IR2 IR1 IR0 IRQ + * 0 0 0 2/9 + * 0 0 1 3 + * 0 1 0 5 + * 0 1 1 7 + * 1 0 0 10 + * 1 0 1 11 + * 1 1 0 15 + * 1 1 1 4 + */ +#define ZE_WD_IRR_IR0 0x20 /* bit 0 of encoded IRQ */ +#define ZE_WD_IRR_IR1 0x40 /* bit 1 of encoded IRQ */ +#define ZE_WD_IRR_IEN 0x80 /* Interrupt enable */ + +/* + * LA Address Register (LAAR) + */ +#define ZE_WD_LAAR 5 + +#define ZE_WD_LAAR_ADDRHI 0x1f /* bits 23-19 of RAM address */ +#define ZE_WD_LAAR_0WS16 0x20 /* enable 0 wait-states on 16 bit bus */ +#define ZE_WD_LAAR_L16EN 0x40 /* enable 16-bit operation */ +#define ZE_WD_LAAR_M16EN 0x80 /* enable 16-bit memory access */ + +/* i/o base offset to station address/card-ID PROM */ +#define ZE_WD_PROM 8 + +/* i/o base offset to CARD ID */ +#define ZE_WD_CARD_ID ZE_WD_PROM+6 + +#define ZE_TYPE_WD8003S 0x02 +#define ZE_TYPE_WD8003E 0x03 +#define ZE_TYPE_WD8013EBT 0x05 +#define ZE_TYPE_WD8013EB 0x27 +#define ZE_TYPE_WD8013EBP 0x2c +#define ZE_TYPE_WD8013EPC 0x29 + +/* Bit definitions in card ID */ +#define ZE_WD_REV_MASK 0x1f /* Revision mask */ +#define ZE_WD_SOFTCONFIG 0x20 /* Soft config */ +#define ZE_WD_LARGERAM 0x40 /* Large RAM */ +#define ZE_MICROCHANEL 0x80 /* Microchannel bus (vs. isa) */ + +/* + * Checksum total. All 8 bytes in station address PROM will add up to this + */ +#define ZE_WD_ROM_CHECKSUM_TOTAL 0xFF + +#define ZE_WD_NIC_OFFSET 0x10 /* I/O base offset to NIC */ +#define ZE_WD_ASIC_OFFSET 0 /* I/O base offset to ASIC */ +#define ZE_WD_IO_PORTS 32 /* # of i/o addresses used */ + +#define ZE_WD_PAGE_OFFSET 0 /* page offset for NIC access to mem */ + +/* + * Definitions for 3Com 3c503 + */ +#define ZE_3COM_NIC_OFFSET 0 +#define ZE_3COM_ASIC_OFFSET 0x400 /* offset to nic i/o regs */ + +/* + * XXX - The I/O address range is fragmented in the 3c503; this is the + * number of regs at iobase. + */ +#define ZE_3COM_IO_PORTS 16 /* # of i/o addresses used */ + +#define ZE_3COM_PAGE_OFFSET 0x20 /* memory starts in second bank */ + +/* + * Page Start Register. Must match PSTART in NIC + */ +#define ZE_3COM_PSTR 0 + +/* + * Page Stop Register. Must match PSTOP in NIC + */ +#define ZE_3COM_PSPR 1 + +/* + * Drq Timer Register. Determines number of bytes to be transfered during + * a DMA burst. + */ +#define ZE_3COM_DQTR 2 + +/* + * Base Configuration Register. Read-only register which contains the + * board-configured I/O base address of the adapter. Bit encoded. + */ +#define ZE_3COM_BCFR 3 + +#define ZE_3COM_BCFR_2E0 0x01 +#define ZE_3COM_BCFR_2A0 0x02 +#define ZE_3COM_BCFR_280 0x04 +#define ZE_3COM_BCFR_250 0x08 +#define ZE_3COM_BCFR_350 0x10 +#define ZE_3COM_BCFR_330 0x20 +#define ZE_3COM_BCFR_310 0x40 +#define ZE_3COM_BCFR_300 0x80 + +/* + * EPROM Configuration Register. Read-only register which contains the + * board-configured memory base address. Bit encoded. + */ +#define ZE_3COM_PCFR 4 + +#define ZE_3COM_PCFR_C8000 0x10 +#define ZE_3COM_PCFR_CC000 0x20 +#define ZE_3COM_PCFR_D8000 0x40 +#define ZE_3COM_PCFR_DC000 0x80 + +/* + * GA Configuration Register. Gate-Array Configuration Register. + */ +#define ZE_3COM_GACFR 5 + +/* + * mbs2 mbs1 mbs0 start address + * 0 0 0 0x0000 + * 0 0 1 0x2000 + * 0 1 0 0x4000 + * 0 1 1 0x6000 + * + * Note that with adapters with only 8K, the setting for 0x2000 must + * always be used. + */ +#define ZE_3COM_GACFR_MBS0 0x01 +#define ZE_3COM_GACFR_MBS1 0x02 +#define ZE_3COM_GACFR_MBS2 0x04 + +#define ZE_3COM_GACFR_RSEL 0x08 /* enable shared memory */ +#define ZE_3COM_GACFR_TEST 0x10 /* for GA testing */ +#define ZE_3COM_GACFR_OWS 0x20 /* select 0WS access to GA */ +#define ZE_3COM_GACFR_TCM 0x40 /* Mask DMA interrupts */ +#define ZE_3COM_GACFR_NIM 0x80 /* Mask NIC interrupts */ + +/* + * Control Register. Miscellaneous control functions. + */ +#define ZE_3COM_CR 6 + +#define ZE_3COM_CR_RST 0x01 /* Reset GA and NIC */ +#define ZE_3COM_CR_XSEL 0x02 /* Transceiver select. BNC=1(def) AUI=0 */ +#define ZE_3COM_CR_EALO 0x04 /* window EA PROM 0-15 to I/O base */ +#define ZE_3COM_CR_EAHI 0x08 /* window EA PROM 16-31 to I/O base */ +#define ZE_3COM_CR_SHARE 0x10 /* select interrupt sharing option */ +#define ZE_3COM_CR_DBSEL 0x20 /* Double buffer select */ +#define ZE_3COM_CR_DDIR 0x40 /* DMA direction select */ +#define ZE_3COM_CR_START 0x80 /* Start DMA controller */ + +/* + * Status Register. Miscellaneous status information. + */ +#define ZE_3COM_STREG 7 + +#define ZE_3COM_STREG_REV 0x07 /* GA revision */ +#define ZE_3COM_STREG_DIP 0x08 /* DMA in progress */ +#define ZE_3COM_STREG_DTC 0x10 /* DMA terminal count */ +#define ZE_3COM_STREG_OFLW 0x20 /* Overflow */ +#define ZE_3COM_STREG_UFLW 0x40 /* Underflow */ +#define ZE_3COM_STREG_DPRDY 0x80 /* Data port ready */ + +/* + * Interrupt/DMA Configuration Register + */ +#define ZE_3COM_IDCFR 8 + +#define ZE_3COM_IDCFR_DRQ0 0x01 /* DMA request 1 select */ +#define ZE_3COM_IDCFR_DRQ1 0x02 /* DMA request 2 select */ +#define ZE_3COM_IDCFR_DRQ2 0x04 /* DMA request 3 select */ +#define ZE_3COM_IDCFR_UNUSED 0x08 /* not used */ +#define ZE_3COM_IDCFR_IRQ2 0x10 /* Interrupt request 2 select */ +#define ZE_3COM_IDCFR_IRQ3 0x20 /* Interrupt request 3 select */ +#define ZE_3COM_IDCFR_IRQ4 0x40 /* Interrupt request 4 select */ +#define ZE_3COM_IDCFR_IRQ5 0x80 /* Interrupt request 5 select */ + +/* + * DMA Address Register MSB + */ +#define ZE_3COM_DAMSB 9 + +/* + * DMA Address Register LSB + */ +#define ZE_3COM_DALSB 0x0a + +/* + * Vector Pointer Register 2 + */ +#define ZE_3COM_VPTR2 0x0b + +/* + * Vector Pointer Register 1 + */ +#define ZE_3COM_VPTR1 0x0c + +/* + * Vector Pointer Register 0 + */ +#define ZE_3COM_VPTR0 0x0d + +/* + * Register File Access MSB + */ +#define ZE_3COM_RFMSB 0x0e + +/* + * Register File Access LSB + */ +#define ZE_3COM_RFLSB 0x0f +#endif diff --git a/sys/i386/isa/ipl.h b/sys/i386/isa/ipl.h new file mode 100644 index 000000000000..248ca5666d67 --- /dev/null +++ b/sys/i386/isa/ipl.h @@ -0,0 +1,7 @@ +#ifndef _ISA_IPL_H_ +#define _ISA_IPL_H_ + +#define NHWI 16 /* number of h/w interrupts */ +#define HWI_MASK 0xffff /* bits corresponding to h/w interrupts */ + +#endif /* _ISA_IPL_H_ */ diff --git a/sys/i386/isa/isa.c b/sys/i386/isa/isa.c index 3dbc748d9d55..588be2cccc7f 100644 --- a/sys/i386/isa/isa.c +++ b/sys/i386/isa/isa.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)isa.c 7.2 (Berkeley) 5/13/91 - * $Id: isa.c,v 1.14 1994/01/22 21:52:04 rgrimes Exp $ + * $Id: isa.c,v 1.19 1994/06/22 05:52:39 jkh Exp $ */ /* @@ -124,9 +124,11 @@ haveseen(dvp, tmpdvp) if ((dvp->id_iobase >= tmpdvp->id_iobase) && (dvp->id_iobase <= (tmpdvp->id_iobase + tmpdvp->id_alive - 1))) { +#ifndef ALLOW_CONFLICT_IOADDR conflict(dvp, tmpdvp, dvp->id_iobase, "I/O address", "0x%x"); status = 1; +#endif } } /* @@ -143,12 +145,14 @@ haveseen(dvp, tmpdvp) if((KERNBASE + dvp->id_maddr >= tmpdvp->id_maddr) && (KERNBASE + dvp->id_maddr <= (tmpdvp->id_maddr + tmpdvp->id_msize - 1))) { +#ifndef ALLOW_CONFLICT_MEMADDR conflict(dvp, tmpdvp, dvp->id_maddr, "maddr", "0x%x"); status = 1; +#endif } } -#ifndef COM_MULTIPORT +#ifndef ALLOW_CONFLICT_IRQ /* * Check for IRQ conflicts. */ @@ -160,6 +164,7 @@ haveseen(dvp, tmpdvp) } } #endif +#ifndef ALLOW_CONFLICT_DRQ /* * Check for DRQ conflicts. */ @@ -170,6 +175,7 @@ haveseen(dvp, tmpdvp) status = 1; } } +#endif } return (status); } @@ -213,38 +219,45 @@ isa_configure() { printf("Probing for devices on the ISA bus:\n"); for (dvp = isa_devtab_tty; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) - config_isadev(dvp,&ttymask); + config_isadev(dvp,&tty_imask); } for (dvp = isa_devtab_bio; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) - config_isadev(dvp,&biomask); + config_isadev(dvp,&bio_imask); } for (dvp = isa_devtab_net; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) - config_isadev(dvp,&netmask); + config_isadev(dvp,&net_imask); } for (dvp = isa_devtab_null; dvp->id_driver; dvp++) { if (!haveseen_isadev(dvp)) config_isadev(dvp,(u_int *) NULL); } + bio_imask |= SWI_CLOCK_MASK; + net_imask |= SWI_NET_MASK; + tty_imask |= SWI_TTY_MASK; + /* - * XXX We should really add the tty device to netmask when the line is + * XXX we should really add the tty device to net_imask when the line is * switched to SLIPDISC, and then remove it when it is switched away from - * SLIPDISC. No need to block out ALL ttys during a splnet when only one + * SLIPDISC. No need to block out ALL ttys during a splimp when only one * of them is running slip. + * + * XXX actually, blocking all ttys during a splimp doesn't matter so much + * with sio because the serial interrupt layer doesn't use tty_imask. Only + * non-serial ttys suffer. It's more stupid that ALL 'net's are blocked + * during spltty. */ #include "sl.h" #if NSL > 0 - netmask |= ttymask; - ttymask |= netmask; + net_imask |= tty_imask; + tty_imask = net_imask; +#endif + /* bio_imask |= tty_imask ; can some tty devices use buffers? */ +#ifdef DIAGNOSTIC + printf("bio_imask %x tty_imask %x net_imask %x\n", + bio_imask, tty_imask, net_imask); #endif - /* if netmask == 0, then the loopback code can do some really - * bad things. - */ - if (netmask == 0) - netmask = 0x10000; - /* biomask |= ttymask ; can some tty devices use buffers? */ - printf("biomask %x ttymask %x netmask %x\n", biomask, ttymask, netmask); splnone(); } @@ -337,15 +350,12 @@ extern inthand_t IDTVEC(intr8), IDTVEC(intr9), IDTVEC(intr10), IDTVEC(intr11), IDTVEC(intr12), IDTVEC(intr13), IDTVEC(intr14), IDTVEC(intr15); -static inthand_func_t defvec[16] = { +static inthand_func_t defvec[ICU_LEN] = { &IDTVEC(intr0), &IDTVEC(intr1), &IDTVEC(intr2), &IDTVEC(intr3), &IDTVEC(intr4), &IDTVEC(intr5), &IDTVEC(intr6), &IDTVEC(intr7), &IDTVEC(intr8), &IDTVEC(intr9), &IDTVEC(intr10), &IDTVEC(intr11), &IDTVEC(intr12), &IDTVEC(intr13), &IDTVEC(intr14), &IDTVEC(intr15) }; -/* out of range default interrupt vector gate entry */ -extern inthand_t IDTVEC(intrdefault); - /* * Fill in default interrupt table (in case of spuruious interrupt * during configuration of kernel, setup interrupt control unit @@ -356,12 +366,8 @@ isa_defaultirq() int i; /* icu vectors */ - for (i = NRSVIDT ; i < NRSVIDT+ICU_LEN ; i++) - setidt(i, defvec[i], SDT_SYS386IGT, SEL_KPL); - - /* out of range vectors */ - for (i = NRSVIDT; i < NIDT; i++) - setidt(i, &IDTVEC(intrdefault), SDT_SYS386IGT, SEL_KPL); + for (i = 0; i < ICU_LEN; i++) + setidt(ICU_OFFSET + i, defvec[i], SDT_SYS386IGT, SEL_KPL); /* initialize 8259's */ outb(IO_ICU1, 0x11); /* reset; program device, four bytes */ @@ -530,7 +536,7 @@ isa_dmarangecheck(caddr_t va, unsigned length, unsigned chan) { #define ISARAM_END RAM_END if (phys == 0) panic("isa_dmacheck: no physical page present"); - if (phys > ISARAM_END) + if (phys >= ISARAM_END) return (1); if (priorpage) { if (priorpage + NBPG != phys) @@ -588,14 +594,18 @@ isa_freephysmem(caddr_t va, unsigned length) { /* * Handle a NMI, possibly a machine check. + * This is generally one of two things, either an memory parity error + * or a bus master timeout failure. A bus-master timeout is indicated + * by bit 4 of port 0x461 going high. + * * return true to panic system, false to ignore. */ int isa_nmi(cd) int cd; { - - log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x\n", inb(0x61), inb(0x70)); + log(LOG_CRIT, "\nNMI port 61 %x, port 70 %x, port 461 %x\n", + inb(0x61), inb(0x70), inb(0x461)); return(0); } @@ -627,165 +637,6 @@ isa_strayintr(d) } /* - * Wait "n" microseconds. - * Relies on timer 1 counting down from (TIMER_FREQ / hz) at - * (1 * TIMER_FREQ) Hz. - * Note: timer had better have been programmed before this is first used! - * (The standard programming causes the timer to generate a square wave and - * the counter is decremented twice every cycle.) - */ -#define CF (1 * TIMER_FREQ) -#define TIMER_FREQ 1193182 /* XXX - should be elsewhere */ - -void -DELAY(n) - int n; -{ - int counter_limit; - int prev_tick; - int tick; - int ticks_left; - int sec; - int usec; - -#ifdef DELAYDEBUG - int getit_calls = 1; - int n1; - static int state = 0; - - if (state == 0) { - state = 1; - for (n1 = 1; n1 <= 10000000; n1 *= 10) - DELAY(n1); - state = 2; - } - if (state == 1) - printf("DELAY(%d)...", n); -#endif - - /* - * Read the counter first, so that the rest of the setup overhead is - * counted. Guess the initial overhead is 20 usec (on most systems it - * takes about 1.5 usec for each of the i/o's in getit(). The loop - * takes about 6 usec on a 486/33 and 13 usec on a 386/20. The - * multiplications and divisions to scale the count take a while). - */ - prev_tick = getit(0, 0); - n -= 20; - - /* - * Calculate (n * (CF / 1e6)) without using floating point and without - * any avoidable overflows. - */ - sec = n / 1000000; - usec = n - sec * 1000000; - ticks_left = sec * CF - + usec * (CF / 1000000) - + usec * ((CF % 1000000) / 1000) / 1000 - + usec * (CF % 1000) / 1000000; - - counter_limit = TIMER_FREQ / hz; - while (ticks_left > 0) { - tick = getit(0, 0); -#ifdef DELAYDEBUG - ++getit_calls; -#endif - if (tick > prev_tick) - ticks_left -= prev_tick - (tick - counter_limit); - else - ticks_left -= prev_tick - tick; - prev_tick = tick; - } -#ifdef DELAYDEBUG - if (state == 1) - printf(" %d calls to getit() at %d usec each\n", - getit_calls, (n + 5) / getit_calls); -#endif -} - -int -getit(unit, timer) - int unit; - int timer; -{ - int high; - int low; - - /* - * XXX - isa.h defines bogus timers. There's no such timer as - * IO_TIMER_2 = 0x48. There's a timer in the CMOS RAM chip but - * its interface is quite different. Neither timer is an 8252. - * We actually only call this with unit = 0 and timer = 0. It - * could be static... - */ - /* - * Protect ourself against interrupts. - * XXX - sysbeep() and sysbeepstop() need protection. - */ - disable_intr(); - /* - * Latch the count for 'timer' (cc00xxxx, c = counter, x = any). - */ - outb(IO_TIMER1 + 3, timer << 6); - - low = inb(IO_TIMER1 + timer); - high = inb(IO_TIMER1 + timer); - enable_intr(); - return ((high << 8) | low); -} - -static int beeping; - -static void -sysbeepstop(f, dummy) - caddr_t f; - int dummy; -{ - /* disable counter 2 */ - outb(0x61, inb(0x61) & 0xFC); - if (f) - timeout(sysbeepstop, (caddr_t)0, (int)f); - else - beeping = 0; -} - -void -sysbeep(int pitch, int period) -{ - - outb(0x61, inb(0x61) | 3); /* enable counter 2 */ - /* - * XXX - move timer stuff to clock.c. - * Program counter 2: - * ccaammmb, c counter, a = access, m = mode, b = BCD - * 1011x110, 11 for aa = LSB then MSB, x11 for mmm = square wave. - */ - outb(0x43, 0xb6); /* set command for counter 2, 2 byte write */ - - outb(0x42, pitch); - outb(0x42, (pitch>>8)); - - if (!beeping) { - beeping = period; - timeout(sysbeepstop, (caddr_t)(period/2), period); - } -} - -/* - * Pass command to keyboard controller (8042) - */ -unsigned -kbc_8042cmd(val) - int val; -{ - - while (inb(KBSTATP)&KBS_IBF); - if (val) outb(KBCMDP, val); - while (inb(KBSTATP)&KBS_IBF); - return (inb(KBDATAP)); -} - -/* * find an ISA device in a given isa_devtab_* table, given * the table to search, the expected id_driver entry, and the unit number. * diff --git a/sys/i386/isa/isa.h b/sys/i386/isa/isa.h index cb083fdcf4cb..32786e6008c1 100644 --- a/sys/i386/isa/isa.h +++ b/sys/i386/isa/isa.h @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)isa.h 5.7 (Berkeley) 5/9/91 - * $Id: isa.h,v 1.4 1994/01/05 15:03:28 rgrimes Exp $ + * $Id: isa.h,v 1.5 1994/04/21 14:20:54 sos Exp $ */ #ifndef _I386_ISA_ISA_H_ @@ -47,12 +47,8 @@ #ifndef LOCORE #include <sys/cdefs.h> -unsigned char rtcin __P((int)); extern unsigned int atdevbase; /* offset in virtual memory of ISA io mem */ -void sysbeep __P((int, int)); -unsigned kbd_8042cmd __P((int)); -struct isa_device; -int isa_irq_pending __P((struct isa_device *dvp)); +unsigned char rtcin __P((int)); #endif @@ -69,6 +65,7 @@ int isa_irq_pending __P((struct isa_device *dvp)); #define IO_TIMER1 0x040 /* 8253 Timer #1 */ #define IO_TIMER2 0x048 /* 8253 Timer #2 */ #define IO_KBD 0x060 /* 8042 Keyboard */ +#define IO_PPI 0x061 /* Programmabel Peripheral Interface */ #define IO_RTC 0x070 /* RTC */ #define IO_NMI IO_RTC /* NMI Control */ #define IO_DMAPG 0x080 /* DMA Page Registers */ diff --git a/sys/i386/isa/isa_device.h b/sys/i386/isa/isa_device.h index 6299f89912a4..670dacf4dfa6 100644 --- a/sys/i386/isa/isa_device.h +++ b/sys/i386/isa/isa_device.h @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)isa_device.h 7.1 (Berkeley) 5/9/91 - * $Id: isa_device.h,v 1.5 1994/01/04 20:06:30 nate Exp $ + * $Id: isa_device.h,v 1.7 1994/06/08 14:01:04 davidg Exp $ */ #ifndef _I386_ISA_ISA_DEVICE_H_ @@ -46,7 +46,7 @@ */ struct isa_device { struct isa_driver *id_driver; - short id_iobase; /* base i/o address */ + int id_iobase; /* base i/o address */ u_short id_irq; /* interrupt request */ short id_drq; /* DMA request */ caddr_t id_maddr; /* physical i/o memory address on bus (if any)*/ diff --git a/sys/i386/isa/kbdtables.h b/sys/i386/isa/kbdtables.h index 011bc2cd1879..1fbd3215f367 100644 --- a/sys/i386/isa/kbdtables.h +++ b/sys/i386/isa/kbdtables.h @@ -14,7 +14,7 @@ * DK9210 Aalborg SO Phone: +45 9814 8076 * * @(#)kbdtables.h 1.3 940123 - * $Id: kbdtables.h,v 1.11 1994/02/01 09:27:43 ache Exp $ + * $Id: kbdtables.h,v 1.13 1994/05/27 01:09:16 ache Exp $ */ #define SET8 0x80 /* eight bit for emacs SET8-key */ @@ -374,17 +374,17 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=00 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, /* sc=01 */ 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, 0x1B, DBG, 0x1B, 0x02, 0x00, /* sc=02 */ '1', '!', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, -/* sc=03 */ '2', '"', NOP, NOP, NOP, '@', NOP, NOP, 0x3B, 0x00, -/* sc=04 */ '3', 0xA3, NOP, NOP, NOP, '#', NOP, NOP, 0x3B, 0x00, -/* sc=05 */ '4', '$', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=03 */ '2', '"', NOP, NOP, '@', NOP, NOP, NOP, 0x37, 0x00, +/* sc=04 */ '3', '#', NOP, NOP, 0xA3, NOP, NOP, NOP, 0x37, 0x00, +/* sc=05 */ '4', '$', NOP, NOP, 0xA4, NOP, NOP, NOP, 0x37, 0x00, /* sc=06 */ '5', '%', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, -/* sc=07 */ '6', '&', 0x1E, NOP, NOP, '^', 0x1E, NOP, 0x19, 0x00, -/* sc=08 */ '7', '/', NOP, NOP, NOP, '&', NOP, NOP, 0x3B, 0x00, -/* sc=09 */ '8', '(', NOP, NOP, NOP, '*', NOP, NOP, 0x3B, 0x00, -/* sc=0a */ '9', ')', NOP, NOP, NOP, '(', NOP, NOP, 0x3B, 0x00, -/* sc=0b */ '0', '=', NOP, NOP, NOP, ')', NOP, NOP, 0x3B, 0x00, -/* sc=0c */ '+', '?', 0x1F, 0x1F, '-', '_', 0x1F, 0x1F, 0x00, 0x00, -/* sc=0d */ 0xB4, '`', NOP, NOP, '=', '+', NOP, NOP, 0x33, 0x00, +/* sc=07 */ '6', '&', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, +/* sc=08 */ '7', '/', NOP, NOP, '{', NOP, NOP, NOP, 0x37, 0x00, +/* sc=09 */ '8', '(', NOP, NOP, '[', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0a */ '9', ')', NOP, NOP, ']', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0b */ '0', '=', NOP, NOP, '}', NOP, NOP, NOP, 0x37, 0x00, +/* sc=0c */ '+', '?', NOP, NOP, '\\', NOP, 0x1C, NOP, 0x35, 0x00, +/* sc=0d */ 0x180, '`', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, /* sc=0e */ 0x08, 0x08, 0x7F, 0x7F, 0x08, 0x08, 0x7F, 0x7F, 0x00, 0x00, /* sc=0f */ 0x09, 0x08, NOP, NOP, 0x09, 0x08, NOP, NOP, 0x77, 0x00, /* sc=10 */ 'q', 'Q', 0x11, 0x11, 'q', 'Q', 0x11, 0x11, 0x00, 0x01, @@ -397,8 +397,8 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=17 */ 'i', 'I', 0x09, 0x09, 'i', 'I', 0x09, 0x09, 0x00, 0x01, /* sc=18 */ 'o', 'O', 0x0F, 0x0F, 'o', 'O', 0x0F, 0x0F, 0x00, 0x01, /* sc=19 */ 'p', 'P', 0x10, 0x10, 'p', 'P', 0x10, 0x10, 0x00, 0x01, -/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '[', '{', 0x1B, NOP, 0x31, 0x01, -/* sc=1b */ 0xA8, '^', NOP, NOP, ']', '}', 0x1D, NOP, 0x31, 0x00, +/* sc=1a */ 0xE5, 0xC5, NOP, NOP, '}', ']', NOP, NOP, 0x33, 0x01, +/* sc=1b */ 0xA8, '^', NOP, NOP, '~', NOP, NOP, NOP, 0x37, 0x00, /* sc=1c */ 0x0D, 0x0D, 0x0A, 0x0A, 0x0D, 0x0D, 0x0A, 0x0A, 0x00, 0x00, /* sc=1d */ LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, LCTR, 0xFF, 0x00, /* sc=1e */ 'a', 'A', 0x01, 0x01, 'a', 'A', 0x01, 0x01, 0x00, 0x01, @@ -410,11 +410,11 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=24 */ 'j', 'J', 0x0A, 0x0A, 'j', 'J', 0x0A, 0x0A, 0x00, 0x01, /* sc=25 */ 'k', 'K', 0x0B, 0x0B, 'k', 'K', 0x0B, 0x0B, 0x00, 0x01, /* sc=26 */ 'l', 'L', 0x0C, 0x0C, 'l', 'L', 0x0C, 0x0C, 0x00, 0x01, -/* sc=27 */ 0xF8, 0xD8, NOP, NOP, ';', ':', NOP, NOP, 0x33, 0x01, -/* sc=28 */ 0xE6, 0xC6, NOP, NOP, '\'', '"', NOP, NOP, 0x33, 0x01, -/* sc=29 */ '<', '>', NOP, NOP, '\\', '|', 0x1C, NOP, 0x31, 0x00, +/* sc=27 */ 0xF6, 0xD6, NOP, NOP, '|', '\\', NOP, NOP, 0x33, 0x01, +/* sc=28 */ 0xE4, 0xC4, NOP, NOP, '{', '[', NOP, NOP, 0x33, 0x01, +/* sc=29 */ 0xA7, 0xBD, NOP, NOP, '\\', '|', NOP, NOP, 0x33, 0x00, /* sc=2a */ LSH, LSH, LSH, LSH, LSH, LSH, LSH, LSH, 0xFF, 0x00, -/* sc=2b */ '\'', '*', NOP, NOP, '`', '~', NOP, NOP, 0x33, 0x00, +/* sc=2b */ '\'', '*', NOP, NOP, NOP, NOP, NOP, NOP, 0x3F, 0x00, /* sc=2c */ 'z', 'Z', 0x1A, 0x1A, 'z', 'Z', 0x1A, 0x1A, 0x00, 0x01, /* sc=2d */ 'x', 'X', 0x18, 0x18, 'x', 'X', 0x18, 0x18, 0x00, 0x01, /* sc=2e */ 'c', 'C', 0x03, 0x03, 'c', 'C', 0x03, 0x03, 0x00, 0x01, @@ -457,7 +457,7 @@ keymap_t key_map = { 0x69, /* swedish iso8859 keymap */ /* sc=53 */ 0x7F, '.', 0x7F, 0x7F, 0x7F, 0x7F, RBT, 0x7F, 0x02, 0x02, /* sc=54 */ 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x1F, 0x00, 0x00, /* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, -/* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, +/* sc=56 */ '<', '>', NOP, NOP, '|', NOP, NOP, NOP, 0x37, 0x00, /* sc=57 */ F(11), F(23), F(35), F(47), S(11), S(11), S(11), S(11), 0xFF, 0x00, /* sc=58 */ F(12), F(24), F(36), F(48), S(12), S(12), S(12), S(12), 0xFF, 0x00, /* sc=59 */ 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0x0D, 0xFF, 0x02, @@ -569,7 +569,7 @@ keymap_t key_map = { 0xe9, /* keys number */ /* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02, /* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02, /* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02, -/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, RBT, 0x83, 0x02, /* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, /* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, /* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, @@ -698,7 +698,7 @@ keymap_t key_map = { 0xe9, /* keys number */ /* sc=50 */ F(58), '2', '2', '2', SET8|'2', SET8|'2', SET8|'2', SET8|'2', 0x80, 0x02, /* sc=51 */ F(59), '3', '3', '3', SET8|'3', SET8|'3', SET8|'3', SET8|'3', 0x80, 0x02, /* sc=52 */ F(60), '0', '0', '0', SET8|'0', SET8|'0', SET8|'0', SET8|'0', 0x80, 0x02, -/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, SET8|0x7F, 0x82, 0x02, +/* sc=53 */ F(54), '.', 0x7F, 0x7F, SET8|0x7F, SET8|0x7F, RBT, RBT, 0x83, 0x02, /* sc=54 */ ALK, ALK, ALK, ALK, ALK, ALK, ALK, ALK, 0xFF, 0x00, /* sc=55 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, /* sc=56 */ NOP, NOP, NOP, NOP, NOP, NOP, NOP, NOP, 0xFF, 0x00, diff --git a/sys/i386/isa/lpa.c b/sys/i386/isa/lpa.c index 89434cbf3da7..8d4aa3f7470f 100644 --- a/sys/i386/isa/lpa.c +++ b/sys/i386/isa/lpa.c @@ -45,7 +45,7 @@ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: lpa.c,v 1.5 1994/02/22 09:04:08 rgrimes Exp $ + * $Id: lpa.c,v 1.6 1994/04/11 07:57:36 csgr Exp $ */ /* @@ -178,6 +178,18 @@ lpaprobe(struct isa_device *dvp) u_char data; u_char mask; int i; + static int warned = 0; + + + /* Warn users that the lpa driver should no longer be used */ + if(!warned) { + printf("*************************************************\n"); + printf("WARNING: The lpa driver is now obsolete, and will\n"); + printf("WARNING: be removed soon.\n"); + printf("WARNING: Please change your config to use lpt\n"); + printf("*************************************************\n"); + warned = 1; + } status = IO_LPTSIZE; diff --git a/sys/i386/isa/lpt.c b/sys/i386/isa/lpt.c index 42f92c74f8d0..2715ea8b193f 100644 --- a/sys/i386/isa/lpt.c +++ b/sys/i386/isa/lpt.c @@ -46,7 +46,7 @@ * SUCH DAMAGE. * * from: unknown origin, 386BSD 0.1 - * $Id: lpt.c,v 1.9 1994/02/22 09:05:13 rgrimes Exp $ + * $Id: lpt.c,v 1.13 1994/06/08 14:34:54 davidg Exp $ */ /* @@ -66,11 +66,14 @@ #include "ioctl.h" #include "tty.h" #include "uio.h" +#include "syslog.h" #include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/lptreg.h" +#include "i386/include/lpt.h" + #define LPINITRDY 4 /* wait up to 4 seconds for a ready */ #define LPTOUTTIME 4 /* wait up to 4 seconds for a ready */ #define LPPRI (PZERO+8) @@ -90,15 +93,6 @@ int lptflag = 1; #endif -void lptout(); - -int lptprobe(), lptattach(); -void lptintr(); - -struct isa_driver lptdriver = { - lptprobe, lptattach, "lpt" -}; - #define LPTUNIT(s) ((s)&0x03) #define LPTFLAGS(s) ((s)&0xfc) @@ -122,6 +116,8 @@ struct lpt_softc { u_char sc_irq ; /* IRQ status of port */ #define LP_HAS_IRQ 0x01 /* we have an irq available */ #define LP_USE_IRQ 0x02 /* we are using our irq */ +#define LP_ENABLE_IRQ 0x04 /* enable IRQ on open */ + } lpt_sc[NLPT] ; /* bits for state */ @@ -149,12 +145,21 @@ struct lpt_softc { #define MAX_SPIN 20 /* Max delay for device ready in usecs */ +static void lptout (struct lpt_softc * sc); +int lptprobe (struct isa_device *dvp); +int lptattach (struct isa_device *isdp); +void lptintr (int unit); + +struct isa_driver lptdriver = { + lptprobe, lptattach, "lpt" +}; + /* * Internal routine to lptprobe to do port tests of one byte value */ -int +static int lpt_port_test(short port, u_char data, u_char mask) { int temp, timeout; @@ -279,8 +284,7 @@ end_probe: /* XXX Todo - try and detect if interrupt is working */ int -lptattach(isdp) - struct isa_device *isdp; +lptattach(struct isa_device *isdp) { struct lpt_softc *sc; @@ -292,7 +296,7 @@ lptattach(isdp) /* check if we can use interrupt */ lprintf("oldirq %x\n", sc->sc_irq); if(isdp->id_irq) { - sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ; + sc->sc_irq = LP_HAS_IRQ | LP_USE_IRQ | LP_ENABLE_IRQ; printf("lpt%d: Interrupt-driven port\n", isdp->id_unit); } else { sc->sc_irq = 0; @@ -308,9 +312,7 @@ lptattach(isdp) */ int -lptopen(dev, flag) - dev_t dev; - int flag; +lptopen(dev_t dev, int flag) { struct lpt_softc *sc; int s; @@ -321,7 +323,6 @@ lptopen(dev, flag) if ((unit >= NLPT) || (sc->sc_port == 0)) return (ENXIO); - /* Only check open bit */ if (sc->sc_state) { lprintf("lp: still open\n") ; lprintf("still open %x\n", sc->sc_state); @@ -333,6 +334,13 @@ lptopen(dev, flag) lprintf("lp flags 0x%x\n", sc->sc_flags); port = sc->sc_port; + /* set IRQ status according to ENABLE_IRQ flag */ + if(sc->sc_irq & LP_ENABLE_IRQ) + sc->sc_irq |= LP_USE_IRQ; + else + sc->sc_irq &= ~LP_USE_IRQ; + + /* init printer */ if((sc->sc_flags & LP_NO_PRIME) == 0) { if((sc->sc_flags & LP_PRIMEOPEN) || sc->sc_primed == 0) { @@ -384,20 +392,19 @@ lptopen(dev, flag) lprintf("irq %x\n", sc->sc_irq); if(sc->sc_irq & LP_USE_IRQ) { sc->sc_state |= TOUT; - timeout (lptout, (caddr_t)sc, hz/2); + timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2); } lprintf("opened.\n"); return(0); } -void -lptout (sc) - struct lpt_softc *sc; +static void +lptout (struct lpt_softc * sc) { int pl; lprintf ("T %x ", inb(sc->sc_port+lpt_status)); if (sc->sc_state&OPEN) - timeout (lptout, (caddr_t)sc, hz/2); + timeout ((timeout_func_t)lptout, (caddr_t)sc, hz/2); else sc->sc_state &= ~TOUT; if (sc->sc_state & ERROR) @@ -423,9 +430,7 @@ lptout (sc) */ int -lptclose(dev, flag) - dev_t dev; - int flag; +lptclose(dev_t dev, int flag) { struct lpt_softc *sc = lpt_sc + LPTUNIT(minor(dev)); int port = sc->sc_port; @@ -458,13 +463,13 @@ lptclose(dev, flag) * This code is only used when we are polling the port */ static int -pushbytes(sc) - struct lpt_softc *sc; +pushbytes(struct lpt_softc * sc) { int spin, err, tic; char ch; int port = sc->sc_port; + lprintf("p"); /* loop for every character .. */ while (sc->sc_xfercnt > 0) { /* printer data */ @@ -518,9 +523,7 @@ pushbytes(sc) */ int -lptwrite(dev, uio) - dev_t dev; - struct uio *uio; +lptwrite(dev_t dev, struct uio * uio) { register unsigned n; int pl, err; @@ -531,26 +534,26 @@ lptwrite(dev, uio) sc->sc_cp = sc->sc_inbuf->b_un.b_addr ; uiomove(sc->sc_cp, n, uio); sc->sc_xfercnt = n ; - if(sc->sc_irq & LP_USE_IRQ) - while (sc->sc_xfercnt > 0) { - lprintf("i"); - /* if the printer is ready for a char, */ - /* give it one */ - if ((sc->sc_state & OBUSY) == 0){ - lprintf("\nC %d. ", sc->sc_xfercnt); - pl = spltty(); - lptintr(sc - lpt_sc); - (void) splx(pl); - } - lprintf("W "); - if (sc->sc_state & OBUSY) - if (err = tsleep ((caddr_t)sc, - LPPRI|PCATCH, "lpwrite", 0)) { - sc->sc_state |= INTERRUPTED; - return(err); - } + while ((sc->sc_xfercnt > 0)&&(sc->sc_irq & LP_USE_IRQ)) { + lprintf("i"); + /* if the printer is ready for a char, */ + /* give it one */ + if ((sc->sc_state & OBUSY) == 0){ + lprintf("\nC %d. ", sc->sc_xfercnt); + pl = spltty(); + lptintr(sc - lpt_sc); + (void) splx(pl); } - else { /* polled write */ + lprintf("W "); + if (sc->sc_state & OBUSY) + if (err = tsleep ((caddr_t)sc, + LPPRI|PCATCH, "lpwrite", 0)) { + sc->sc_state |= INTERRUPTED; + return(err); + } + } + /* check to see if we must do a polled write */ + if(!(sc->sc_irq & LP_USE_IRQ) && (sc->sc_xfercnt)) { lprintf("p"); if((err = pushbytes(sc))) return(err); @@ -567,8 +570,7 @@ lptwrite(dev, uio) */ void -lptintr(unit) - int unit; +lptintr(int unit) { struct lpt_softc *sc = lpt_sc + unit; int port = sc->sc_port, sts; @@ -607,22 +609,40 @@ lptintr(unit) } int -lptioctl(dev, cmd, data, flag) - dev_t dev; - int cmd; - caddr_t data; - int flag; +lptioctl(dev_t dev, int cmd, caddr_t data, int flag) { - int error; + int error = 0; + struct lpt_softc *sc; + u_int unit = LPTUNIT(minor(dev)); + u_char old_sc_irq; /* old printer IRQ status */ + + sc = lpt_sc + unit; - error = 0; switch (cmd) { -#ifdef THISISASAMPLE - case XXX: - dothis; andthis; andthat; - error=x; - break; -#endif /* THISISASAMPLE */ + case LPT_IRQ : + if(sc->sc_irq & LP_HAS_IRQ) { + /* + * NOTE: + * If the IRQ status is changed, + * this will only be visible on the + * next open. + * + * If interrupt status changes, + * this gets syslog'd. + */ + old_sc_irq = sc->sc_irq; + if(*(int*)data == 0) + sc->sc_irq &= (~LP_ENABLE_IRQ); + else + sc->sc_irq |= LP_ENABLE_IRQ; + if (old_sc_irq != sc->sc_irq ) + log(LOG_NOTICE, "lpt%c switched to %s mode\n", + (char)unit+'0', + (sc->sc_irq & LP_ENABLE_IRQ)? + "interrupt-driven":"polled"); + } else /* polled port */ + error = EOPNOTSUPP; + break; default: error = ENODEV; } diff --git a/sys/i386/isa/mcd.c b/sys/i386/isa/mcd.c index 190d42be3b75..3245dadc4097 100644 --- a/sys/i386/isa/mcd.c +++ b/sys/i386/isa/mcd.c @@ -39,7 +39,7 @@ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * - * $Id: mcd.c,v 1.10.2.2 1994/03/23 23:51:39 rgrimes Exp $ + * $Id: mcd.c,v 1.16 1994/04/30 17:03:33 gclarkii Exp $ */ static char COPYRIGHT[] = "mcd-driver (C)1993 by H.Veit & B.Moore"; @@ -350,6 +350,9 @@ MCD_TRACE("strategy: drive not valid\n",0,0,0,0); if (bounds_check_with_label(bp,&cd->dlabel,1) <= 0) { goto done; } + } else { + bp->b_pblkno = bp->b_blkno; + bp->b_resid = 0; } /* queue it */ diff --git a/sys/i386/isa/npx.c b/sys/i386/isa/npx.c index 2979e0cfd7ab..e83a503feeed 100644 --- a/sys/i386/isa/npx.c +++ b/sys/i386/isa/npx.c @@ -32,7 +32,7 @@ * SUCH DAMAGE. * * from: @(#)npx.c 7.2 (Berkeley) 5/12/91 - * $Id: npx.c,v 1.6 1994/01/03 07:55:43 davidg Exp $ + * $Id: npx.c,v 1.9 1994/06/11 02:36:32 davidg Exp $ */ #include "npx.h" @@ -114,7 +114,7 @@ struct isa_driver npxdriver = { npxprobe, npxattach, "npx", }; -u_int npx0mask; +u_int npx0_imask = SWI_CLOCK_MASK; struct proc *npxproc; static bool_t npx_ex16; @@ -292,7 +292,7 @@ npxprobe1(dvp) * Bad, we are stuck with IRQ13. */ npx_irq13 = 1; - npx0mask = dvp->id_irq; /* npxattach too late */ + npx0_imask = dvp->id_irq; /* npxattach too late */ return (IO_NPXSIZE); } /* @@ -321,10 +321,12 @@ npxattach(dvp) struct isa_device *dvp; { if (!npx_ex16 && !npx_irq13) { - if (npx_exists) + if (npx_exists) { printf("npx%d: Error reporting broken, using 387 emulator\n",dvp->id_unit); - else + npx_exists = 0; + } else { printf("npx%d: 387 Emulator\n",dvp->id_unit); + } } npxinit(__INITIAL_NPXCW__); return (1); /* XXX unused */ @@ -528,8 +530,8 @@ npxsave(addr) old_icu1_mask = inb(IO_ICU1 + 1); old_icu2_mask = inb(IO_ICU2 + 1); save_idt_npxintr = idt[npx_intrno]; - outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0mask)); - outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0mask >> 8)); + outb(IO_ICU1 + 1, old_icu1_mask & ~(IRQ_SLAVE | npx0_imask)); + outb(IO_ICU2 + 1, old_icu2_mask & ~(npx0_imask >> 8)); idt[npx_intrno] = npx_idt_probeintr; enable_intr(); stop_emulating(); @@ -541,10 +543,10 @@ npxsave(addr) icu1_mask = inb(IO_ICU1 + 1); /* masks may have changed */ icu2_mask = inb(IO_ICU2 + 1); outb(IO_ICU1 + 1, - (icu1_mask & ~npx0mask) | (old_icu1_mask & npx0mask)); + (icu1_mask & ~npx0_imask) | (old_icu1_mask & npx0_imask)); outb(IO_ICU2 + 1, - (icu2_mask & ~(npx0mask >> 8)) - | (old_icu2_mask & (npx0mask >> 8))); + (icu2_mask & ~(npx0_imask >> 8)) + | (old_icu2_mask & (npx0_imask >> 8))); idt[npx_intrno] = save_idt_npxintr; enable_intr(); /* back to usual state */ } diff --git a/sys/i386/isa/pcaudio.c b/sys/i386/isa/pcaudio.c new file mode 100644 index 000000000000..0c48e407a7be --- /dev/null +++ b/sys/i386/isa/pcaudio.c @@ -0,0 +1,430 @@ +/*- + * Copyright (c) 1994 Søren Schmidt + * 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 withough 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: pcaudio.c,v 1.5 1994/05/27 08:51:03 sos Exp $ + */ + +#include "systm.h" +#include "uio.h" +#include "ioctl.h" +#include "proc.h" +#include "file.h" +#include "sound/ulaw.h" +#include "machine/cpufunc.h" +#include "machine/pio.h" +#include "machine/pcaudioio.h" +#include "i386/isa/isa.h" +#include "i386/isa/isa_device.h" +#include "i386/isa/timerreg.h" + +#include "pca.h" +#if NPCA > 0 + +#define BUF_SIZE 4*8192 +#define SAMPLE_RATE 8000 +#define INTERRUPT_RATE 16000 + +static struct pca_status { + char open; /* device open */ + char queries; /* did others try opening */ + unsigned char *buf[2]; /* double buffering */ + unsigned char *buffer; /* current buffer ptr */ + unsigned in_use[2]; /* buffers fill */ + unsigned index; /* index in current buffer */ + unsigned counter; /* sample counter */ + unsigned scale; /* sample counter scale */ + unsigned sample_rate; /* sample rate */ + unsigned processed; /* samples processed */ + unsigned volume; /* volume for pc-speaker */ + char encoding; /* Ulaw, Alaw or linear */ + char current; /* current buffer */ + unsigned char oldval; /* old timer port value */ + char timer_on; /* is playback running */ + char coll; /* select collision */ + pid_t wsel; /* pid of select'ing proc */ +} pca_status; + +static char buffer1[BUF_SIZE]; +static char buffer2[BUF_SIZE]; +static char volume_table[256]; + +static int pca_sleep = 0; +static int pca_initialized = 0; + +void pcaintr(int regs); +int pcaprobe(struct isa_device *dvp); +int pcaattach(struct isa_device *dvp); +int pcaclose(dev_t dev, int flag); +int pcaopen(dev_t dev, int flag); +int pcawrite(dev_t dev, struct uio *uio, int flag); +int pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p); +int pcaselect(dev_t dev, int rw, struct proc *p); + +struct isa_driver pcadriver = { + pcaprobe, pcaattach, "pca", +}; + + +inline void conv(const void *table, void *buff, unsigned long n) +{ + __asm__("1:\tmovb (%2), %3\n" + "\txlatb\n" + "\tmovb %3, (%2)\n" + "\tinc %2\n" + "\tdec %1\n" + "\tjnz 1b\n" + : + :"b" ((long)table), "c" (n), "D" ((long)buff), "a" ((char)n) + :"bx","cx","di","ax"); +} + + +static void +pca_volume(int volume) +{ + int i, j; + + for (i=0; i<256; i++) { + j = ((i-128)*volume)/100; + if (j<-128) + j = -128; + if (j>127) + j = 127; + volume_table[i] = (((255-(j + 128))/4)+1); + } +} + + +static void +pca_init() +{ + pca_status.open = 0; + pca_status.queries = 0; + pca_status.timer_on = 0; + pca_status.buf[0] = (unsigned char *)&buffer1[0]; + pca_status.buf[1] = (unsigned char *)&buffer2[0]; + pca_status.buffer = pca_status.buf[0]; + pca_status.in_use[0] = pca_status.in_use[1] = 0; + pca_status.current = 0; + pca_status.sample_rate = SAMPLE_RATE; + pca_status.scale = (pca_status.sample_rate << 8) / INTERRUPT_RATE; + pca_status.encoding = AUDIO_ENCODING_ULAW; + pca_status.volume = 100; + + pca_volume(pca_status.volume); +} + + +static int +pca_start(void) +{ + /* use the first buffer */ + pca_status.current = 0; + pca_status.index = 0; + pca_status.counter = 0; + pca_status.buffer = pca_status.buf[pca_status.current]; + pca_status.oldval = inb(IO_PPI) | 0x03; + /* acquire the timers */ + if (acquire_timer2(TIMER_LSB|TIMER_ONESHOT)) { + return -1; + } + if (acquire_timer0(INTERRUPT_RATE, pcaintr)) { + release_timer2(); + return -1; + } + pca_status.timer_on = 1; + return 0; +} + + +static void +pca_stop(void) +{ + /* release the timers */ + release_timer0(); + release_timer2(); + /* reset the buffer */ + pca_status.in_use[0] = pca_status.in_use[1] = 0; + pca_status.index = 0; + pca_status.counter = 0; + pca_status.current = 0; + pca_status.buffer = pca_status.buf[pca_status.current]; + pca_status.timer_on = 0; +} + + +static void +pca_pause() +{ + release_timer0(); + release_timer2(); + pca_status.timer_on = 0; +} + + +static void +pca_continue() +{ + pca_status.oldval = inb(IO_PPI) | 0x03; + acquire_timer2(TIMER_LSB|TIMER_ONESHOT); + acquire_timer0(INTERRUPT_RATE, pcaintr); + pca_status.timer_on = 1; +} + + +static void +pca_wait(void) +{ + while (pca_status.in_use[0] || pca_status.in_use[1]) { + pca_sleep = 1; + tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_drain", 0); + } +} + + +int +pcaprobe(struct isa_device *dvp) +{ + return(-1); +} + + +int +pcaattach(struct isa_device *dvp) +{ + printf(" PCM audio driver\n", dvp->id_unit); + pca_init(); + return 1; +} + + +int +pcaopen(dev_t dev, int flag) +{ + /* audioctl device can always be opened */ + if (minor(dev) == 128) + return 0; + if (minor(dev) > 0) + return ENXIO; + + if (!pca_initialized) { + pca_init(); + pca_initialized = 1; + } + + /* audio device can only be open by one process */ + if (pca_status.open) { + pca_status.queries = 1; + return EBUSY; + } + pca_status.buffer = pca_status.buf[0]; + pca_status.in_use[0] = pca_status.in_use[1] = 0; + pca_status.timer_on = 0; + pca_status.open = 1; + pca_status.processed = 0; + return 0; +} + + +int +pcaclose(dev_t dev, int flag) +{ + /* audioctl device can always be closed */ + if (minor(dev) == 128) + return 0; + if (minor(dev) > 0) + return ENXIO; + /* audio device close drains all output and restores timers */ + pca_wait(); + pca_stop(); + pca_status.open = 0; + return 0; +} + + +int +pcawrite(dev_t dev, struct uio *uio, int flag) +{ + int count, which; + + /* only audio device can be written */ + if (minor(dev) > 0) + return ENXIO; + + while ((count = min(BUF_SIZE, uio->uio_resid)) > 0) { + if (pca_status.in_use[0] && pca_status.in_use[1]) { + pca_sleep = 1; + tsleep((caddr_t)&pca_sleep, PZERO|PCATCH, "pca_wait",0); + } + which = pca_status.in_use[0] ? 1 : 0; + if (count && !pca_status.in_use[which]) { + uiomove(pca_status.buf[which], count, uio); + pca_status.processed += count; + switch (pca_status.encoding) { + case AUDIO_ENCODING_ULAW: + conv(ulaw_dsp, pca_status.buf[which], count); + break; + + case AUDIO_ENCODING_ALAW: + break; + + case AUDIO_ENCODING_RAW: + break; + } + pca_status.in_use[which] = count; + if (!pca_status.timer_on) + if (pca_start()) + return EBUSY; + } + } + return 0; +} + + +int +pcaioctl(dev_t dev, int cmd, caddr_t data, int flag, struct proc *p) +{ + audio_info_t *auptr; + + switch(cmd) { + + case AUDIO_GETINFO: + auptr = (audio_info_t *)data; + auptr->play.sample_rate = pca_status.sample_rate; + auptr->play.channels = 1; + auptr->play.precision = 8; + auptr->play.encoding = pca_status.encoding; + + auptr->play.gain = pca_status.volume; + auptr->play.port = 0; + + auptr->play.samples = pca_status.processed; + auptr->play.eof = 0; + auptr->play.pause = !pca_status.timer_on; + auptr->play.error = 0; + auptr->play.waiting = pca_status.queries; + + auptr->play.open = pca_status.open; + auptr->play.active = pca_status.timer_on; + return 0; + + case AUDIO_SETINFO: + auptr = (audio_info_t *)data; + if (auptr->play.sample_rate != (unsigned int)~0) { + pca_status.sample_rate = auptr->play.sample_rate; + pca_status.scale = + (pca_status.sample_rate << 8) / INTERRUPT_RATE; + } + if (auptr->play.encoding != (unsigned int)~0) { + pca_status.encoding = auptr->play.encoding; + } + if (auptr->play.gain != (unsigned int)~0) { + pca_status.volume = auptr->play.gain; + pca_volume(pca_status.volume); + } + if (auptr->play.pause != (unsigned char)~0) { + if (auptr->play.pause) + pca_pause(); + else + pca_continue(); + } + + return 0; + + case AUDIO_DRAIN: + pca_wait(); + return 0; + + case AUDIO_FLUSH: + pca_stop(); + return 0; + + } + return ENXIO; +} + + +void +pcaintr(int regs) +{ + if (pca_status.index < pca_status.in_use[pca_status.current]) { + disable_intr(); + __asm__("outb %0,$0x61\n" + "andb $0xFE,%0\n" + "outb %0,$0x61" + : : "a" ((char)pca_status.oldval) ); + __asm__("xlatb\n" + "outb %0,$0x42" + : : "a" ((char)pca_status.buffer[pca_status.index]), + "b" ((long)volume_table) ); + enable_intr(); + pca_status.counter += pca_status.scale; + pca_status.index = (pca_status.counter >> 8); + } + if (pca_status.index >= pca_status.in_use[pca_status.current]) { + pca_status.index = pca_status.counter = 0; + pca_status.in_use[pca_status.current] = 0; + pca_status.current ^= 1; + pca_status.buffer = pca_status.buf[pca_status.current]; + if (pca_sleep) { + wakeup((caddr_t)&pca_sleep); + pca_sleep = 0; + } + if (pca_status.wsel) { + selwakeup(pca_status.wsel, pca_status.coll); + pca_status.wsel = 0; + pca_status.coll = 0; + } + } +} + + +int +pcaselect(dev_t dev, int rw, struct proc *p) +{ + int s = spltty(); + struct proc *p1; + + switch (rw) { + + case FWRITE: + if (!pca_status.in_use[0] || !pca_status.in_use[1]) { + splx(s); + return(1); + } + if (pca_status.wsel && (p1 = pfind(pca_status.wsel)) + && p1->p_wchan == (caddr_t)&selwait) + pca_status.coll = 1; + else + pca_status.wsel = p->p_pid; + splx(s); + return 0; + default: + splx(s); + return(0); + } +} +#endif diff --git a/sys/i386/isa/pccons.c b/sys/i386/isa/pccons.c index 43fba38b1552..3d729da6dc54 100644 --- a/sys/i386/isa/pccons.c +++ b/sys/i386/isa/pccons.c @@ -34,7 +34,7 @@ * SUCH DAMAGE. * * from: @(#)pccons.c 5.11 (Berkeley) 5/21/91 - * $Id: pccons.c,v 1.13.2.1 1994/05/04 05:09:30 rgrimes Exp $ + * $Id: pccons.c,v 1.17 1994/05/30 03:15:09 ache Exp $ */ /* @@ -63,7 +63,7 @@ int pc_xmode; #endif /* XSERVER */ -struct tty pccons; +struct tty *pccons; struct pcconsoftc { char cs_flags; @@ -289,13 +289,12 @@ pcopen(dev, flag, mode, p) if (minor(dev) != 0) return (ENXIO); - tp = &pccons; + tp = pccons = ttymalloc(pccons); tp->t_oproc = pcstart; tp->t_param = pcparam; tp->t_dev = dev; openf++; if ((tp->t_state & TS_ISOPEN) == 0) { - tp->t_state |= TS_WOPEN; ttychars(tp); tp->t_iflag = TTYDEF_IFLAG; tp->t_oflag = TTYDEF_OFLAG; @@ -316,8 +315,12 @@ pcclose(dev, flag, mode, p) int flag, mode; struct proc *p; { - (*linesw[pccons.t_line].l_close)(&pccons, flag); - ttyclose(&pccons); + (*linesw[pccons->t_line].l_close)(pccons, flag); + ttyclose(pccons); +#ifdef broken /* session holds a ref to the tty; can't deallocate */ + ttyfree(pccons); + pccons = (struct tty *)NULL; +#endif return(0); } @@ -328,7 +331,7 @@ pcread(dev, uio, flag) struct uio *uio; int flag; { - return ((*linesw[pccons.t_line].l_read)(&pccons, uio, flag)); + return ((*linesw[pccons->t_line].l_read)(pccons, uio, flag)); } /*ARGSUSED*/ @@ -338,7 +341,7 @@ pcwrite(dev, uio, flag) struct uio *uio; int flag; { - return ((*linesw[pccons.t_line].l_write)(&pccons, uio, flag)); + return ((*linesw[pccons->t_line].l_write)(pccons, uio, flag)); } /* @@ -347,10 +350,8 @@ pcwrite(dev, uio, flag) * Catch the character, and see who it goes to. */ void -pcrint(dev, irq, cpl) - dev_t dev; - int irq; /* XXX ??? */ - int cpl; +pcrint(unit) + int unit; { int c; char *cp; @@ -361,7 +362,7 @@ pcrint(dev, irq, cpl) if (pcconsoftc.cs_flags & CSF_POLLING) return; #ifdef KDB - if (kdbrintr(c, &pccons)) + if (kdbrintr(c, pccons)) return; #endif if (!openf) @@ -369,11 +370,11 @@ pcrint(dev, irq, cpl) #ifdef XSERVER /* 15 Aug 92*/ /* send at least one character, because cntl-space is a null */ - (*linesw[pccons.t_line].l_rint)(*cp++ & 0xff, &pccons); + (*linesw[pccons->t_line].l_rint)(*cp++ & 0xff, pccons); #endif /* XSERVER */ while (*cp) - (*linesw[pccons.t_line].l_rint)(*cp++ & 0xff, &pccons); + (*linesw[pccons->t_line].l_rint)(*cp++ & 0xff, pccons); } #ifdef XSERVER /* 15 Aug 92*/ @@ -389,7 +390,7 @@ pcioctl(dev, cmd, data, flag) caddr_t data; int flag; { - register struct tty *tp = &pccons; + register struct tty *tp = pccons; register error; #ifdef XSERVER /* 15 Aug 92*/ @@ -436,12 +437,12 @@ pcxint(dev) if (!pcconsintr) return; - pccons.t_state &= ~TS_BUSY; + pccons->t_state &= ~TS_BUSY; pcconsoftc.cs_timo = 0; - if (pccons.t_line) - (*linesw[pccons.t_line].l_start)(&pccons); + if (pccons->t_line) + (*linesw[pccons->t_line].l_start)(pccons); else - pcstart(&pccons); + pcstart(pccons); } void @@ -454,20 +455,11 @@ pcstart(tp) if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) goto out; do { - if (RB_LEN(&tp->t_out) <= tp->t_lowat) { - if (tp->t_state&TS_ASLEEP) { - tp->t_state &= ~TS_ASLEEP; - wakeup((caddr_t)&tp->t_out); - } - if (tp->t_wsel) { - selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL); - tp->t_wsel = 0; - tp->t_state &= ~TS_WCOLL; - } - } - if (RB_LEN(&tp->t_out) == 0) + if (tp->t_state & (TS_SO_OCOMPLETE | TS_SO_OLOWAT) || tp->t_wsel) + ttwwakeup(tp); + if (RB_LEN(tp->t_out) == 0) goto out; - c = getc(&tp->t_out); + c = getc(tp->t_out); tp->t_state |= TS_BUSY; /* 21 Aug 92*/ splx(s); sput(c, 0); @@ -491,7 +483,7 @@ pccnprobe(cp) /* initialize required fields */ cp->cn_dev = makedev(maj, 0); - cp->cn_tp = &pccons; + cp->cn_tp = pccons; cp->cn_pri = CN_INTERNAL; } @@ -639,26 +631,13 @@ static u_char shift_down, ctrl_down, alt_down, caps, num, scroll; /* translate ANSI color codes to standard pc ones */ static char fgansitopc[] = { FG_BLACK, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE, - FG_MAGENTA, FG_CYAN, FG_LIGHTGREY}; + FG_MAGENTA, FG_CYAN, FG_LIGHTGREY +}; static char bgansitopc[] = { BG_BLACK, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE, - BG_MAGENTA, BG_CYAN, BG_LIGHTGREY}; - -static void move_up(u_short *s, u_short *d, u_int len) -{ - s += len; - d += len; - while (len-- > 0) - *--d = *--s; -} - - -static void move_down(u_short *s, u_short *d, u_int len) -{ - while (len-- > 0) - *d++ = *s++; -} + BG_MAGENTA, BG_CYAN, BG_LIGHTGREY +}; /* * sput has support for emulation of the 'pc3' termcap entry. @@ -872,7 +851,7 @@ sput(c, ka) posy = (crtat - Crtat) / vs.ncol; if (vs.cx > posy) vs.cx = posy; - bcopy(Crtat+vs.ncol*vs.cx, Crtat, vs.ncol*(vs.nrow-vs.cx)*CHR); + bcopyw(Crtat+vs.ncol*vs.cx, Crtat, vs.ncol*(vs.nrow-vs.cx)*CHR); fillw((at <<8)+' ', (Crtat + vs.ncol * (vs.nrow - vs.cx)), vs.ncol * vs.cx); @@ -884,7 +863,7 @@ sput(c, ka) posy = (crtat - Crtat) / vs.ncol; if (vs.cx > vs.nrow - posy) vs.cx = vs.nrow - posy; - bcopy(Crtat, Crtat+vs.ncol*vs.cx, vs.ncol*(vs.nrow-vs.cx)*CHR); + bcopyw(Crtat, Crtat+vs.ncol*vs.cx, vs.ncol*(vs.nrow-vs.cx)*CHR); fillw((at <<8)+' ', Crtat, vs.ncol*vs.cx); /* crtat += vs.ncol*vs.cx;*/ /* XXX */ vs.esc = 0; vs.ebrac = 0; vs.eparm = 0; @@ -897,7 +876,7 @@ sput(c, ka) src = Crtat + posy * vs.ncol; dst = src + vs.cx * vs.ncol; count = vs.nrow - (posy + vs.cx); - move_up(src, dst, count * vs.ncol); + bcopyw(src, dst, count * vs.ncol * CHR); fillw((at <<8)+' ', src, vs.cx * vs.ncol); vs.esc = 0; vs.ebrac = 0; vs.eparm = 0; break; @@ -909,7 +888,7 @@ sput(c, ka) dst = Crtat + posy * vs.ncol; src = dst + vs.cx * vs.ncol; count = vs.nrow - (posy + vs.cx); - move_down(src, dst, count * vs.ncol); + bcopyw(src, dst, count * vs.ncol * CHR); src = dst + count * vs.ncol; fillw((at <<8)+' ', src, vs.cx * vs.ncol); vs.esc = 0; vs.ebrac = 0; vs.eparm = 0; @@ -999,7 +978,7 @@ sput(c, ka) } if (sc && crtat >= Crtat+vs.ncol*vs.nrow) { /* scroll check */ if (openf) do (void)sgetc(1); while (scroll); - bcopy(Crtat+vs.ncol, Crtat, vs.ncol*(vs.nrow-1)*CHR); + bcopyw(Crtat+vs.ncol, Crtat, vs.ncol*(vs.nrow-1)*CHR); fillw ((at << 8) + ' ', Crtat + vs.ncol*(vs.nrow-1), vs.ncol); crtat -= vs.ncol; @@ -1557,6 +1536,12 @@ loop: #endif /* !XSERVER*/ } + /* + * Check for cntl-alt-del + */ + if ((dt == 83) && ctrl_down && alt_down) + cpu_reset(); + #include "ddb.h" #if NDDB > 0 /* @@ -1564,7 +1549,7 @@ loop: */ if ((dt == 1) && ctrl_down && alt_down) { Debugger("manual escape to debugger"); - dt |= 0x80; /* discard esc (ddb discarded ctrl-alt) */ + goto loop; } #endif @@ -1797,7 +1782,7 @@ void cons_normal() int pcmmap(dev_t dev, int offset, int nprot) { - if (offset > 0x20000) + if (offset > 0x20000 - PAGE_SIZE) return -1; return i386_btop((0xa0000 + offset)); } diff --git a/sys/i386/isa/psm.c b/sys/i386/isa/psm.c new file mode 100644 index 000000000000..d12298727ca3 --- /dev/null +++ b/sys/i386/isa/psm.c @@ -0,0 +1,464 @@ +/*- + * Copyright (c) 1992, 1993 Erik Forsberg. + * 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. + * + * THIS SOFTWARE IS PROVIDED BY ``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 I 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. + */ + +/* + * Ported to 386bsd Oct 17, 1992 + * Sandi Donno, Computer Science, University of Cape Town, South Africa + * Please send bug reports to sandi@cs.uct.ac.za + * + * Thanks are also due to Rick Macklem, rick@snowhite.cis.uoguelph.ca - + * although I was only partially successful in getting the alpha release + * of his "driver for the Logitech and ATI Inport Bus mice for use with + * 386bsd and the X386 port" to work with my Microsoft mouse, I nevertheless + * found his code to be an invaluable reference when porting this driver + * to 386bsd. + * + * Further modifications for latest 386BSD+patchkit and port to NetBSD, + * Andrew Herbert <andrew@werple.apana.org.au> - 8 June 1993 + * + * Cloned from the Microsoft Bus Mouse driver, also by Erik Forsberg, by + * Andrew Herbert - 12 June 1993 + * + * Modified for PS/2 mouse by Charles Hannum <mycroft@ai.mit.edu> + * - 13 June 1993 + * + * Modified for PS/2 AUX mouse by Shoji Yuen <yuen@nuie.nagoya-u.ac.jp> + * - 24 October 1993 + */ + +#include "psm.h" + +#if NPSM > 0 + +#include "param.h" +#include "kernel.h" +#include "systm.h" +#include "buf.h" +#include "malloc.h" +#include "ioctl.h" +#include "tty.h" +#include "file.h" +#include "proc.h" +#include "vnode.h" + +#include "i386/include/mouse.h" +#include "i386/include/pio.h" /* Julian's fast IO macros */ +#include "i386/isa/isa_device.h" + +#ifdef 0 +#include "syslog.h" /* For debugging */ +#endif + +#define DATA 0 /* Offset for data port, read-write */ +#define CNTRL 4 /* Offset for control port, write-only */ +#define STATUS 4 /* Offset for status port, read-only */ + +/* status bits */ +#define PSM_OUTPUT_ACK 0x02 /* output acknowledge */ + +/* controller commands */ +#define PSM_ENABLE 0xa8 /* enable auxiliary port */ +#define PSM_DISABLE 0xa7 /* disable auxiliary port */ +#define PSM_INT_ENABLE 0x47 /* enable controller interrupts */ +#define PSM_INT_DISABLE 0x65 /* disable controller interrupts */ + +/* m+use commands */ +#define PSM_SET_SCALE11 0xe6 /* set 1:1 scaling */ +#define PSM_SET_SCALE21 0xe7 /* set 2:1 scaling */ +#define PSM_SET_RES 0xe8 /* set resolution */ +#define PSM_GET_SCALE 0xe9 /* set scaling factor */ +#define PSM_SET_STREAM 0xea /* set streaming mode */ +#define PSM_SET_SAMPLE 0xf3 /* set sampling rate */ +#define PSM_DEV_ENABLE 0xf4 /* mouse on */ +#define PSM_DEV_DISABLE 0xf5 /* mouse off */ +#define PSM_RESET 0xff /* reset */ + +#define PSMUNIT(dev) (minor(dev) >> 1) + +#ifndef min +#define min(x,y) (x < y ? x : y) +#endif min + +int psmprobe (struct isa_device *); +int psmattach (struct isa_device *); +void psm_poll_status(void); + +static int psmaddr[NPSM]; /* Base I/O port addresses per unit */ + +#define MSBSZ 1024 /* Output queue size (pwr of 2 is best) */ + +struct ringbuf { + int count, first, last; + char queue[MSBSZ]; +}; + +static struct psm_softc { /* Driver status information */ + struct ringbuf inq; /* Input queue */ + pid_t rsel; /* Process selecting for Input */ + unsigned char state; /* Mouse driver state */ + unsigned char status; /* Mouse button status */ + unsigned char button; /* Previous mouse button status bits */ + int x, y; /* accumulated motion in the X,Y axis */ +} psm_softc[NPSM]; + +#define OPEN 1 /* Device is open */ +#define ASLP 2 /* Waiting for mouse data */ + +struct isa_driver psmdriver = { psmprobe, psmattach, "psm" }; + +#define AUX_PORT 0x60 /* AUX_PORT base (S.Yuen) */ + +static void psm_write_dev(int inport, u_char value) +{ + psm_poll_status(); + outb(inport+CNTRL, 0xd4); + psm_poll_status(); + outb(inport+DATA,value); +} + +static inline void psm_command(int ioport, u_char value) +{ + psm_poll_status(); + outb(ioport+CNTRL, 0x60); + psm_poll_status(); + outb(ioport+DATA, value); +} + +int psmprobe(struct isa_device *dvp) +{ + /* XXX: Needs a real probe routine. */ + + int ioport,c,unit; + + ioport=dvp->id_iobase; + unit=dvp->id_unit; + psm_write_dev(ioport,0xff); /* Reset aux device */ + psm_poll_status(); + outb(ioport+CNTRL,0xa9); + psm_poll_status(); + outb(ioport+CNTRL,0xaa); + c = inb(ioport+DATA); + if(c&0x04) { +/* printf("PS/2 AUX mouse is not found\n");*/ + psm_command(ioport,0x65); + psmaddr[unit] = 0; /* Device not found */ + return(0);} +/* printf("PS/2 AUX mouse found. Installing driver\n");*/ + return (4); +} + +int psmattach(struct isa_device *dvp) +{ + int unit = dvp->id_unit; + int ioport = dvp->id_iobase; + struct psm_softc *sc = &psm_softc[unit]; + + /* Save I/O base address */ + + psmaddr[unit] = ioport; + + /* Disable mouse interrupts */ + + psm_poll_status(); + outb(ioport+CNTRL, PSM_ENABLE); +#ifdef 0 + psm_write(ioport, PSM_SET_RES); + psm_write(ioport, 0x03); /* 8 counts/mm */ + psm_write(ioport, PSM_SET_SCALE); + psm_write(ioport, 0x02); /* 2:1 */ + psm_write(ioport, PSM_SET_SCALE21); + psm_write(ioport, PSM_SET_SAMPLE); + psm_write(ioport, 0x64); /* 100 samples/sec */ + psm_write(ioport, PSM_SET_STREAM); +#endif + psm_poll_status(); + outb(ioport+CNTRL, PSM_DISABLE); + psm_command(ioport, PSM_INT_DISABLE); + + /* Setup initial state */ + + sc->state = 0; + + /* Done */ + + return(0); +} + +int psmopen(dev_t dev, int flag, int fmt, struct proc *p) +{ + int unit = PSMUNIT(dev); + struct psm_softc *sc; + int ioport; + + /* Validate unit number */ + + if (unit >= NPSM) + return(ENXIO); + + /* Get device data */ + + sc = &psm_softc[unit]; + ioport = psmaddr[unit]; + + /* If device does not exist */ + + if (ioport == 0) + return(ENXIO); + + /* Disallow multiple opens */ + if (sc->state & OPEN) + return(EBUSY); + + /* Initialize state */ + + sc->state |= OPEN; + sc->rsel = 0; + sc->status = 0; + sc->button = 0; + sc->x = 0; + sc->y = 0; + + /* Allocate and initialize a ring buffer */ + + sc->inq.count = sc->inq.first = sc->inq.last = 0; + + /* Enable Bus Mouse interrupts */ + + psm_write_dev(ioport, PSM_DEV_ENABLE); + psm_poll_status(); + outb(ioport+CNTRL, PSM_ENABLE); + psm_command(ioport, PSM_INT_ENABLE); + + /* Successful open */ + + return(0); +} + +void psm_poll_status(void) +{ + + while(inb(AUX_PORT+STATUS)&0x03) { + if(inb(AUX_PORT+STATUS) & 0x2 == 0x2) + inb(AUX_PORT+DATA);} + return; +} + + +int psmclose(dev_t dev, int flag, int fmt, struct proc *p) +{ + int unit, ioport; + struct psm_softc *sc; + + /* Get unit and associated info */ + + unit = PSMUNIT(dev); + sc = &psm_softc[unit]; + ioport = psmaddr[unit]; + + /* Disable further mouse interrupts */ + + psm_command(ioport,PSM_INT_DISABLE); + psm_poll_status(); + outb(ioport+CNTRL,PSM_DISABLE ); + + /* Complete the close */ + + sc->state &= ~OPEN; + + /* close is almost always successful */ + + return(0); +} + +int psmread(dev_t dev, struct uio *uio, int flag) +{ + int s; + int error = 0; /* keep compiler quiet, even though initialisation + is unnecessary */ + unsigned length; + struct psm_softc *sc; + unsigned char buffer[100]; + + /* Get device information */ + + sc = &psm_softc[PSMUNIT(dev)]; + + /* Block until mouse activity occured */ + + s = spltty(); + while (sc->inq.count == 0) { + if (minor(dev) & 0x1) { + splx(s); + return(EWOULDBLOCK); + } + sc->state |= ASLP; + error = tsleep((caddr_t)sc, PZERO | PCATCH, "psmrea", 0); + if (error != 0) { + splx(s); + return(error); + } + } + + /* Transfer as many chunks as possible */ + + while (sc->inq.count > 0 && uio->uio_resid > 0) { + length = min(sc->inq.count, uio->uio_resid); + if (length > sizeof(buffer)) + length = sizeof(buffer); + + /* Remove a small chunk from input queue */ + + if (sc->inq.first + length >= MSBSZ) { + bcopy(&sc->inq.queue[sc->inq.first], + buffer, MSBSZ - sc->inq.first); + bcopy(sc->inq.queue, &buffer[MSBSZ-sc->inq.first], + length - (MSBSZ - sc->inq.first)); + } + else + bcopy(&sc->inq.queue[sc->inq.first], buffer, length); + + sc->inq.first = (sc->inq.first + length) % MSBSZ; + sc->inq.count -= length; + + /* Copy data to user process */ + + error = uiomove(buffer, length, uio); + if (error) + break; + } + + sc->x = sc->y = 0; + + /* Allow interrupts again */ + + splx(s); + return(error); +} + +int psmioctl(dev_t dev, caddr_t addr, int cmd, int flag, struct proc *p) +{ + struct psm_softc *sc; + struct mouseinfo info; + int s, error; + + /* Get device information */ + + sc = &psm_softc[PSMUNIT(dev)]; + + /* Perform IOCTL command */ + + switch (cmd) { + + case MOUSEIOCREAD: + + /* Don't modify info while calculating */ + + s = spltty(); + + /* Build mouse status octet */ + + info.status = sc->status; + if (sc->x || sc->y) + info.status |= MOVEMENT; + + /* Encode X and Y motion as good as we can */ + + if (sc->x > 127) + info.xmotion = 127; + else if (sc->x < -128) + info.xmotion = -128; + else + info.xmotion = sc->x; + + if (sc->y > 127) + info.ymotion = 127; + else if (sc->y < -128) + info.ymotion = -128; + else + info.ymotion = sc->y; + + /* Reset historical information */ + + sc->x = 0; + sc->y = 0; + sc->status &= ~BUTCHNGMASK; + + /* Allow interrupts and copy result buffer */ + + splx(s); + error = copyout(&info, addr, sizeof(struct mouseinfo)); + break; + + default: + error = EINVAL; + break; + } + + /* Return error code */ + + return(error); +} + +void psmintr(unit) + int unit; +{ + struct psm_softc *sc = &psm_softc[unit]; + int ioport = psmaddr[unit]; + + sc->inq.queue[sc->inq.last++ % MSBSZ] = inb(ioport+DATA); + sc->inq.count++; + if (sc -> state & ASLP) { + sc->state &= ~ASLP; + wakeup((caddr_t)sc); + } + if (sc->rsel) { + selwakeup(sc->rsel, 0); + sc->rsel = 0; + } +} + +int psmselect(dev_t dev, int rw, struct proc *p) +{ + int s, ret; + struct psm_softc *sc = &psm_softc[PSMUNIT(dev)]; + + /* Silly to select for output */ + + if (rw == FWRITE) + return(0); + + /* Return true if a mouse event available */ + + s = spltty(); + if (sc->inq.count) + ret = 1; + else { + sc->rsel = p->p_pid; + ret = 0; + } + splx(s); + + return(ret); +} +#endif + + diff --git a/sys/i386/isa/seagate.c b/sys/i386/isa/seagate.c new file mode 100644 index 000000000000..31c515166663 --- /dev/null +++ b/sys/i386/isa/seagate.c @@ -0,0 +1,2036 @@ +/* + * (Free/Net/386)BSD ST01/02, Future Domain TMC-885, TMC-950 SCSI driver for + * Julians SCSI-code + * + * Copyright 1994, Kent Palmkvist (kentp@isy.liu.se) + * Copyright 1994, Robert Knier (rknier@qgraph.com) + * Copyright 1992, 1994 Drew Eckhardt (drew@colorado.edu) + * Copyright 1994, Julian Elischer (julian@tfs.com) + * + * Others that has contributed by example code is + * Glen Overby (overby@cray.com) + * Tatu Yllnen + * Brian E Litzinger + * + * 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 DEVELOPERS ``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 DEVELOPERS 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. + */ + +/* + * + * kentp 940307 alpha version based on newscsi-03 version of Julians SCSI-code + * kentp 940314 Added possibility to not use messages + * rknier 940331 Added fast transfer code + * rknier 940407 Added assembler coded data transfers + * + * $Id: seagate.c,v 1.3 1994/06/16 13:26:14 sean Exp $ + */ + +/* + * What should really be done: + * + * Add missing tests for timeouts + * Restructure interrupt enable/disable code (runs to long with int disabled) + * Find bug? giving problem with tape status + * Add code to handle Future Domain 840, 841, 880 and 881 + * adjust timeouts (startup is very slow) + * add code to use tagged commands in SCSI2 + * Add code to handle slow devices better (sleep if device not disconnecting) + * Fix unnecessary interrupts + */ + +/* Note to users trying to share a disk between DOS and unix: + * The ST01/02 is a translating host-adapter. It is not giving DOS + * the same number of heads/tracks/sectors as specified by the disk. + * It is therefore important to look at what numbers DOS thinks the + * disk has. Use these to disklabel your disk in an appropriate manner + */ + +#include <sys/types.h> + +#ifdef KERNEL /* don't laugh.. look for main() */ +#include <sea.h> + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/errno.h> +#include <sys/ioctl.h> +#include <sys/malloc.h> +#include <sys/buf.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <i386/isa/isa_device.h> +#endif /* KERNEL */ +#include <scsi/scsi_all.h> +#include <scsi/scsiconf.h> + +#ifdef KERNEL +#include "ddb.h" +#include "kernel.h" +#else /* KERNEL */ +#define NSEA 1 +#endif /* KERNEL */ + +extern int hz; + +#define SEA_SCB_MAX 8 /* allow maximally 8 scsi control blocks */ +#define SCB_TABLE_SIZE 8 /* start with 8 scb entries in table */ +#define BLOCK_SIZE 512 /* size of READ/WRITE areas on SCSI card */ + +/* + * defining PARITY causes parity data to be checked + */ +#define PARITY 1 + +/* + * defining SEA_BLINDTRANSFER will make DATA IN and DATA OUT to be done with + * blind transfers, i.e. no check is done for scsi phase changes. This will + * result in data loss if the scsi device does not send its data using + * BLOCK_SIZE bytes at a time. + * If SEA_BLINDTRANSFER defined and SEA_ASSEMBLER also defined will result in + * the use of blind transfers coded in assembler. SEA_ASSEMBLER is no good + * without SEA_BLINDTRANSFER defined. + */ +#define SEA_BLINDTRANSFER 1 /* do blind transfers */ +#define SEA_ASSEMBLER 1 /* Use assembly code for fast transfers */ + +/* + * defining SEANOMSGS causes messages not to be used (thereby disabling + * disconnects) + */ +/* #define SEANOMSGS 1 */ + +/* + * defining SEA_NODATAOUT makes dataout phase being aborted + */ +/* #define SEA_NODATAOUT 1 */ + +/* + * defining SEA_SENSEFIRST make REQUEST_SENSE opcode to be placed first + */ +/* #define SEA_SENSEFIRST 1 */ + +#define SEA_FREEBSD11 1 /* intermediate def. for FreeBSD 1.1 BETA */ + /* timeout function has changed */ + +/* Debugging definitions. Should not be used unless you want a lot of + printouts even under normal conditions */ + +/* #define SEADEBUG 1 */ /* General info about errors */ +/* #define SEADEBUG1 1 */ /* Info about internal results and errors */ +/* #define SEADEBUG2 1 */ /* Display a lot about timeouts etc */ +/* #define SEADEBUG3 1 */ +/* #define SEADEBUG4 1 */ +/* #define SEADEBUG5 1 */ +/* #define SEADEBUG6 1 */ /* Display info about queue-lengths */ +/* #define SEADEBUG7 1 */ /* Extra check on STATUS before phase check */ +/* #define SEADEBUG8 1 */ /* Disregard non-BSY state in + sea_information_transfer */ +/* #define SEADEBUG9 1 */ /* Enable printouts */ +/* #define SEADEBUG11 1 */ /* stop everything except access to scsi id 1 */ +/* #define SEADEBUG15 1 */ /* Display every byte sent/received */ + +#define NUM_CONCURRENT 1 /* number of concurrent ops per board */ + +/******************************* board definitions **************************/ +/* + * CONTROL defines + */ + +#define CMD_RST 0x01 /* scsi reset */ +#define CMD_SEL 0x02 /* scsi select */ +#define CMD_BSY 0x04 /* scsi busy */ +#define CMD_ATTN 0x08 /* scsi attention */ +#define CMD_START_ARB 0x10 /* start arbitration bit */ +#define CMD_EN_PARITY 0x20 /* enable scsi parity generation */ +#define CMD_INTR 0x40 /* enable scsi interrupts */ +#define CMD_DRVR_ENABLE 0x80 /* scsi enable */ + +/* + * STATUS + */ + +#define STAT_BSY 0x01 /* scsi busy */ +#define STAT_MSG 0x02 /* scsi msg */ +#define STAT_IO 0x04 /* scsi I/O */ +#define STAT_CD 0x08 /* scsi C/D */ +#define STAT_REQ 0x10 /* scsi req */ +#define STAT_SEL 0x20 /* scsi select */ +#define STAT_PARITY 0x40 /* parity error bit */ +#define STAT_ARB_CMPL 0x80 /* arbitration complete bit */ + +/* + * REQUESTS + */ + +#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG) +#define REQ_DATAOUT 0 +#define REQ_DATAIN STAT_IO +#define REQ_CMDOUT STAT_CD +#define REQ_STATIN (STAT_CD | STAT_IO) +#define REQ_MSGOUT (STAT_MSG | STAT_CD) +#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO) + +#define REQ_UNKNOWN 0xff + +#define SEAGATERAMOFFSET 0x00001800 + +#ifdef PARITY + #define BASE_CMD (CMD_EN_PARITY | CMD_INTR) +#else + #define BASE_CMD (CMD_INTR) +#endif + +#define SEAGATE 1 +#define FD 2 + +/****************************************************************************** + * This should be placed in a more generic file (presume in /sys/scsi) + * Message codes: + */ +#define MSG_ABORT 0x06 +#define MSG_NOP 0x08 +#define MSG_COMMAND_COMPLETE 0x00 +#define MSG_DISCONNECT 0x04 +#define MSG_IDENTIFY 0x80 +#define MSG_BUS_DEV_RESET 0x0c +#define MSG_MESSAGE_REJECT 0x07 +#define MSG_SAVE_POINTERS 0x02 +#define MSG_RESTORE_POINTERS 0x03 +/******************************************************************************/ + +#define IDENTIFY(can_disconnect,lun) (MSG_IDENTIFY | ((can_disconnect) ? \ + 0x40 : 0) | ((lun) & 0x07)) + +/* scsi control block used to keep info about a scsi command */ +struct sea_scb +{ + int flags; /* status of the instruction */ +#define SCB_FREE 0 +#define SCB_ACTIVE 1 +#define SCB_ABORTED 2 +#define SCB_TIMEOUT 4 +#define SCB_ERROR 8 +#define SCB_TIMECHK 16 /* We have set a timeout on this one */ + struct sea_scb *next; /* in free list */ + struct scsi_xfer *xfer; /* the scsi_xfer for this cmd */ + u_char * data; /* position in data buffer so far */ + int32 datalen; /* bytes remaining to transfer */; +}; + +/* + * data structure describing current status of the scsi bus. One for each + * controller card. + */ +struct sea_data +{ + caddr_t basemaddr; /* Base address for card */ + char ctrl_type; /* FD or SEAGATE */ + caddr_t st0x_cr_sr; /* Address of control and status register */ + caddr_t st0x_dr; /* Address of data register */ + u_short vect; /* interrupt vector for this card */ + int our_id; /* our scsi id */ + int numscb; /* number of scsi control blocks */ + struct scsi_link sc_link; /* struct connecting different data */ + struct sea_scb *connected; /* currently connected command */ + struct sea_scb *issue_queue; /* waiting to be issued */ + struct sea_scb *disconnected_queue; /* waiting to reconnect */ + struct sea_scb scbs[SCB_TABLE_SIZE]; + struct sea_scb *free_scb; /* free scb list */ + volatile unsigned char busy[8]; /* index=target, bit=lun, Keep track of + busy luns at device target */ +} *seadata[NSEA]; + +/* flag showing if main routine is running. */ +static volatile int main_running = 0; + +#define STATUS (*(volatile unsigned char *) sea->st0x_cr_sr) +#define CONTROL STATUS +#define DATA (*(volatile unsigned char *) sea->st0x_dr) + +/* + * These are "special" values for the tag parameter passed to sea_select + * Not implemented right now. + */ + +#define TAG_NEXT -1 /* Use next free tag */ +#define TAG_NONE -2 /* + * Establish I_T_L nexus instead of I_T_L_Q + * even on SCSI-II devices. + */ + +typedef struct { + char *signature ; + unsigned offset; + unsigned length; + unsigned char type; +} BiosSignature; + +/* + * Signatures for automatic recognition of board type + */ + +static const BiosSignature signatures[] = { +{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE}, +{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE}, + +/* + * The following two lines are NOT mistakes. One detects ROM revision + * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter, + * and this is not going to change, the "SEAGATE" and "SCSI" together + * are probably "good enough" + */ + +{"SEAGATE SCSI BIOS ", 16, 17, SEAGATE}, +{"SEAGATE SCSI BIOS ", 17, 17, SEAGATE}, + + /* + * However, future domain makes several incompatible SCSI boards, so specific + * signatures must be used. + */ + + {"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 45, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD}, + {"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD}, + {"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD}, + {"FUTURE DOMAIN TMC-950", 5, 21, FD}, + }; + +#define NUM_SIGNATURES (sizeof(signatures) / sizeof(BiosSignature)) + +static const char * seagate_bases[] = { + (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000, + (char *) 0xce000, (char *) 0xdc000, (char *) 0xde000 +}; + +#define NUM_BASES (sizeof(seagate_bases)/sizeof(char *)) + +int sea_probe(struct isa_device *dev); +int sea_attach(struct isa_device *dev); +int seaintr(int unit); +int32 sea_scsi_cmd(struct scsi_xfer *xs); +#ifdef SEA_FREEBSD11 +void sea_timeout(caddr_t, int); +#else +void sea_timeout(struct sea_scb *scb); +#endif +void seaminphys(struct buf *bp); +void sea_done(int unit, struct sea_scb *scb); +u_int32 sea_adapter_info(int unit); +struct sea_scb *sea_get_scb(int unit, int flags); +void sea_free_scb(int unit, struct sea_scb *scb, int flags); +static void sea_main(void); +static void sea_information_transfer(struct sea_data *sea); +int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb); +int sea_init(int unit); +int sea_send_scb(struct sea_data *sea, struct sea_scb *scb); +int sea_reselect(struct sea_data *sea); +int sea_select(struct sea_data *sea, struct sea_scb *scb); +int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count, + u_char **data); +int sea_abort(int unit, struct sea_scb *scb); + +static sea_unit = 0; +static sea_slot = -1; /* last found board seagate_bases address index */ +#define FAIL 1 +#define SUCCESS 0 + +#ifdef KERNEL +struct scsi_adapter sea_switch = +{ + sea_scsi_cmd, + seaminphys, + 0, + 0, + sea_adapter_info, + "sea", + 0,0 +}; + +/* the below structure is so we have a default dev struct for our link struct */ +struct scsi_device sea_dev = +{ + NULL, /* use default error handler */ + NULL, /* have a queue, served by this */ + NULL, /* have no async handler */ + NULL, /* Use default 'done' routine */ + "sea", + 0, + 0,0 +}; + +struct isa_driver seadriver = +{ + sea_probe, + sea_attach, + "sea" +}; + +#endif /* KERNEL */ + +#ifdef SEADEBUG6 +void sea_queue_length() +{ + struct sea_scb *tmp; + int length = 0; + + if(seadata[0]->connected) + length = 1; + for(tmp = seadata[0]->issue_queue; tmp != NULL; tmp = tmp->next, length++); + for(tmp = seadata[0]->disconnected_queue ; tmp != NULL; tmp->next, length++); + printf("length:%d ",length); +} +#endif + +/***********************************************************************\ +* Check if the device can be found at the port given and if so, detect * +* the type of board. Set it up ready for further work. Takes the * +* isa_dev structure from autoconf as an argument. * +* Returns 1 if card recognized, 0 if errors * +\***********************************************************************/ +int +sea_probe(dev) +struct isa_device *dev; +{ + int j; + int unit = sea_unit; + struct sea_data *sea; + dev->id_unit = unit; + +#ifdef SEADEBUG2 + printf("sea_probe "); +#endif + + /* find unit and check we have that many defined */ + if(unit >= NSEA) { + printf("sea%d: unit number too high\n",unit); + return(0); + } + dev->id_unit = unit; +#ifdef SEADEBUG2 + printf("unit: %d\n",unit); + printf("dev_addr: 0x%lx\n",dev->id_maddr); +#endif + /* allocate a storage area for us */ + + if (seadata[unit]) { + printf("sea%d: memory already allocated\n", unit); + return(0); + } +#ifdef SEADEBUG2 + printf("Before malloc\n"); +#endif + sea = malloc(sizeof(struct sea_data), M_TEMP, M_NOWAIT); + if (!sea) { + printf("sea%d: cannot malloc!\n", unit); + return(0); + } + +#ifdef SEADEBUG2 + printf("after malloc\n"); + for(j=0;j<32767;j++); +#endif + bzero(sea,sizeof(struct sea_data)); + seadata[unit] = sea; + + /* check for address if no one specified */ + sea->basemaddr = NULL; + + /* Could try to find a board by looking through all possible addresses */ + /* This is not done the right way now, because I have not found a way */ + /* to get a boards virtual memory address given its physical. There is */ + /* a function that returns the physical address for a given virtual */ + /* address, but not the other way around */ + + if(dev->id_maddr == 0) { +/* + for(sea_slot++;sea_slot<NUM_BASES;sea_slot++) + for(j = 0; !sea->basemaddr && j < NUM_SIGNATURES; ++j) + if(!memcmp((void *)(seagate_bases[sea_slot]+signatures[j].offset), + (void *) signatures[j].signature, signatures[j].length)) { + sea->basemaddr = (void *)seagate_bases[sea_slot]; + break; + } +*/ + } else { + +#ifdef SEADEBUG2 + printf("id_maddr != 0\n"); + for(j = 0; j < 32767 ; j++); + for(j = 0; j < 32767 ; j++); +#endif + /* find sea_slot position for overridden memory address */ + for(j = 0; ((char *)vtophys(dev->id_maddr) != seagate_bases[j]) && + j<NUM_BASES; ++j); + if(j == NUM_BASES) { + printf("sea: board not expected at address 0x%lx\n",dev->id_maddr); + seadata[unit]=NULL; + free(sea, M_TEMP); + return(0); + } else if(sea_slot > j) { + printf("sea: board address 0x%lx already probed!\n", dev->id_maddr); + seadata[unit]=NULL; + free(sea, M_TEMP); + return(0); + } else { + sea->basemaddr = dev->id_maddr; + } + + } +#ifdef SEADEBUG2 + printf("sea->basemaddr = %lx\n", sea->basemaddr); +#endif + + /* check board type */ /* No way to define this through config */ + for(j = 0; j < NUM_SIGNATURES; j++) + if(!memcmp((void *) (sea->basemaddr + signatures[j].offset), + (void *) signatures[j].signature, signatures[j].length)) { + sea->ctrl_type = signatures[j].type; + break; + } + if(j == NUM_SIGNATURES) { + printf("sea: Board type unknown at address 0x%lx\n", + sea->basemaddr); + seadata[unit]=NULL; + free(sea, M_TEMP); + return(0); + } + + /* Find controller and data memory addresses */ + sea->st0x_cr_sr = (void *) (((unsigned char *) sea->basemaddr) + + ((sea->ctrl_type == SEAGATE) ? 0x1a00 : 0x1c00)); + sea->st0x_dr = (void *) (((unsigned char *) sea->basemaddr) + + ((sea->ctrl_type == SEAGATE) ? 0x1c00 : 0x1e00)); + + /* Test controller RAM (works the same way on future domain cards?) */ + *(sea->basemaddr + SEAGATERAMOFFSET) = 0xa5; + *(sea->basemaddr + SEAGATERAMOFFSET + 1) = 0x5a; + + if((*(sea->basemaddr + SEAGATERAMOFFSET) != (char) 0xa5) || + (*(sea->basemaddr + SEAGATERAMOFFSET + 1) != (char) 0x5a)) { + printf("sea%d: Board RAM failure\n",unit); + } + + if(sea_init(unit) != 0) { + seadata[unit] = NULL; + free(sea,M_TEMP); + return(0); + } + + /* if its there put in it's interrupt vector */ + /* (Doesn't use dma, so no drq is set) */ + sea->vect = dev->id_irq; + + sea_unit++; + return(1); +} + +/***********************************************\ +* Attach all sub-devices we can find * +\***********************************************/ +int +sea_attach(dev) + struct isa_device *dev; +{ + int unit = dev->id_unit; + struct sea_data *sea = seadata[unit]; + +#ifdef SEADEBUG2 + printf("sea_attach called\n"); +#endif + + /* fill in the prototype scsi_link */ + sea->sc_link.adapter_unit = unit; + sea->sc_link.adapter_targ = sea->our_id; + sea->sc_link.adapter = &sea_switch; + sea->sc_link.device = &sea_dev; + + /*****************************************************\ + * ask the adapter what subunits are present * + \*****************************************************/ + scsi_attachdevs(&(sea->sc_link)); + return 1; +} + +/***********************************************\ +* Return some information to the caller about * +* the adapter and its capabilities * +\***********************************************/ +u_int32 +sea_adapter_info(unit) + int unit; +{ +#ifdef SEADEBUG2 + printf("sea_adapter_info called\n"); +#endif + return 1; +} + +/***********************************************\ +* Catch an interrupt from the adaptor * +\***********************************************/ +int +seaintr(unit) + int unit; +{ + int done; + struct sea_data *sea = seadata[unit]; + int oldpri; + +#if SEADEBUG2 + printf(";"); +#endif + + do { + done = 1; + /* dispatch to appropriate routine if found and done=0 */ + /* should check to see that this card really caused the interrupt */ + if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) { + /* Reselect interrupt */ +#ifdef SEADEBUG2 + printf(";2"); +#endif + done = 0; +/* enable_intr(); */ /* ?? How should this be done ?? */ + sea_reselect(sea); + } else if (STATUS & STAT_PARITY) { + /* Parity error interrupt */ +#ifdef SEADEBUG2 + printf(";3"); +#endif + printf("sea%d: PARITY interrupt\n", unit); + } else { +#ifdef SEADEBUG2 +/* printf("sea%d: unknown interrupt\n",unit); */ + printf(";4%x", STATUS); +#endif + } + if (!done) { + oldpri = splbio(); /* disable_intr(); */ + if (!main_running) { +#ifdef SEADEBUG2 + printf(";5"); +#endif + main_running = 1; + sea_main(); + /* main_running is cleared in sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + splx(oldpri); /* enable_intr(); */ + } else { + splx(oldpri); /* enable_intr(); */ + } + } + } while (!done); + return 1; +} + +/***********************************************\ +* Setup data structures, and reset the board * +* and the scsi bus * +\***********************************************/ +int +sea_init(unit) + int unit; +{ + long l; + int i; + struct sea_data *sea = seadata[unit]; + +#ifdef SEADEBUG2 + printf("sea_init called\n"); +#endif +/* Reset the scsi bus (I don't know if this is needed */ + CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_RST; + DELAY(25); /* hold reset for at least 25 microseconds */ + CONTROL = BASE_CMD; + DELAY(10); /* wait a Bus Clear Delay (800 ns + bus free delay (800 ns) */ + /* Set our id (don't know anything about this) */ + if(sea->ctrl_type == SEAGATE) + sea->our_id = 7; + else + sea->our_id = 6; + /* init fields used by our routines */ + sea->connected = NULL; + sea->issue_queue = NULL; + sea->disconnected_queue = NULL; + for (i=0; i<8 ; i++) + sea->busy[i] = 0; + + /* link up the free list of scbs */ + sea->numscb = SCB_TABLE_SIZE; + sea->free_scb = (struct sea_scb *) & (sea->scbs[0]); + for(i=1;i< SCB_TABLE_SIZE ; i++) { + sea->scbs[i-1].next = &(sea->scbs[i]); + } + sea->scbs[SCB_TABLE_SIZE - 1].next = NULL; + + return(0); +} + +/***********************************************\ +* * +\***********************************************/ +void seaminphys(bp) + struct buf *bp; +{ +#ifdef SEADEBUG2 +/* printf("seaminphys called\n"); */ + printf(","); +#endif +} + +/***********************************************\ +* start a scsi operation given the command and * +* the data address. Also needs the unit, target * +* and lu * +* get a free scb and set it up * +* call send_scb * +* either start timer or wait until done * +\***********************************************/ +int32 sea_scsi_cmd(xs) +struct scsi_xfer *xs; +{ + struct scsi_sense_data *s1, *s2; + struct sea_scb *scb; + int i = 0; + int flags; + int unit = xs->sc_link->adapter_unit; + struct sea_data *sea = seadata[unit]; + int s; + unsigned int stat; + int32 result; + +#ifdef SEADEBUG2 + /* printf("scsi_cmd\n"); */ + printf("="); +#endif + +#ifdef SEADEBUG11 + if(xs->sc_link->target != 1) { + xs->flags |= ITSDONE; + xs->error = XS_TIMEOUT; + return(HAD_ERROR); + } +#endif + + flags = xs->flags; + if(xs->bp) flags |= (SCSI_NOSLEEP); + if(flags & ITSDONE) { + printf("sea%d: Already done?", unit); + xs->flags &= ~ITSDONE; + } + if(!(flags & INUSE)) { + printf("sea%d: Not in use?", unit); + xs->flags |= INUSE; + } + if (!(scb = sea_get_scb(unit, flags))) { +#ifdef SEADEBUG2 + printf("=2"); +#endif + xs->error = XS_DRIVER_STUFFUP; + return(TRY_AGAIN_LATER); + } + + /* + * Put all the arguments for the xfer in the scb + */ + scb->xfer = xs; + scb->datalen = xs->datalen; + scb->data = xs->data; + + if(flags & SCSI_RESET) { + /* Try to send a reset command to the card. This is done by calling the + * Reset function. Should then return COMPLETE. Need to take care of the + * possible current connected command. + * Not implemented right now. + */ + printf("sea%d: Got a SCSI_RESET!\n",unit); + } + + /* setup the scb to contain necessary values */ + /* The interresting values can be read from the xs that is saved */ + /* I therefore think that the structure can be kept very small */ + /* the driver doesn't use DMA so the scatter/gather is not needed ? */ +#ifdef SEADEBUG6 + sea_queue_length(); +#endif + if (sea_send_scb(sea, scb) == 0) { +#ifdef SEADEBUG2 + printf("=3"); +#endif + xs->error = XS_DRIVER_STUFFUP; + sea_free_scb(unit, scb, flags); + return (TRY_AGAIN_LATER); + } + + /* + * Usually return SUCCESSFULLY QUEUED + */ + if (!(flags & SCSI_NOMASK)) { + if(xs->flags & ITSDONE) { /* timout timer not started, already finished */ + /* Tried to return COMPLETE but the machine hanged with this */ +#ifdef SEADEBUG2 + printf("=6"); +#endif + return(SUCCESSFULLY_QUEUED); + } +#ifdef SEA_FREEBSD11 + timeout(sea_timeout, (caddr_t)scb, (xs->timeout * hz) / 1000); +#else + timeout(sea_timeout, scb, (xs->timeout * hz) / 1000); +#endif + scb->flags |= SCB_TIMECHK; +#ifdef SEADEBUG2 + printf("=4"); +#endif + return(SUCCESSFULLY_QUEUED); + } + + /* + * If we can't use interrupts, poll on completion + */ + + result = sea_poll(unit, xs, scb); +#ifdef SEADEBUG2 + printf("=5 %lx", result); +#endif + return result; +} + +/* + * Get a free scb. If there are none, see if we can allocate a new one. If so, + * put it in the hash table too, otherwise return an error or sleep. + */ + +struct sea_scb * +sea_get_scb(unit, flags) + int unit; + int flags; +{ + struct sea_data *sea = seadata[unit]; + unsigned opri = 0; + struct sea_scb * scbp; + int hashnum; + +#ifdef SEADEBUG2 +/* printf("get_scb\n"); */ + printf("("); +#endif + + if (!(flags & SCSI_NOMASK)) + opri = splbio(); + +#ifdef SEADEBUG3 + printf("(2 %lx ", sea->free_scb); +#endif + + /* + * If we can and have to, sleep waiting for one to come free + * but only if we can´t allocate a new one. + */ + while (!(scbp = sea->free_scb)) { +#ifdef SEADEBUG12 + printf("(3"); +#endif + if (sea->numscb < SEA_SCB_MAX) { + printf("malloced new scbs\n"); + if (scbp = (struct sea_scb *) malloc(sizeof(struct sea_scb), + M_TEMP, M_NOWAIT)) { + bzero(scbp, sizeof(struct sea_scb)); + sea->numscb++; + scbp->flags = SCB_ACTIVE; + scbp->next = NULL; + } else { + printf("sea%d: Can't malloc SCB\n",unit); + } + goto gottit; + } else { +#ifdef SEADEBUG12 + printf("(4"); +#endif + if(!(flags & SCSI_NOSLEEP)) { +#ifdef SEADEBUG2 + printf("(5"); +#endif + tsleep(&sea->free_scb, PRIBIO, "seascb", 0); + } + } + } + if (scbp) { +#ifdef SEADEBUG2 + printf("(6"); +#endif + /* Get SCB from free list */ + sea->free_scb = scbp->next; + scbp->next = NULL; + scbp->flags = SCB_ACTIVE; + } + gottit: + if (!(flags & SCSI_NOMASK)) + splx(opri); + + return(scbp); +} + +/* + * sea_send_scb + * + * Try to send this command to the board. Because this board does not use any + * mailboxes, this routine simply adds the command to the queue held by the + * sea_data structure. + * A check is done to see if the command contains a REQUEST_SENSE command, and + * if so the command is put first in the queue, otherwise the command is added + * to the end of the queue. ?? Not correct ?? + */ +int +sea_send_scb(struct sea_data *sea, struct sea_scb *scb) +{ + struct sea_scb *tmp; + int oldpri = 0; + +#ifdef SEADEBUG2 + printf("+"); +#endif + + if(!(scb->xfer->flags & SCSI_NOSLEEP)) { + oldpri = splbio(); + } + + /* add to head of queue if queue empty or command is REQUEST_SENSE */ + + if (!(sea->issue_queue) +#ifdef SEA_SENSEFIRST + || (scb->xfer->cmd->opcode == (u_char) REQUEST_SENSE) +#endif + ) { +#ifdef SEADEBUG2 + printf("+2"); +#endif + scb->next = sea->issue_queue; + sea->issue_queue = scb; + } else { +#ifdef SEADEBUG2 + printf("+3"); +#endif + for (tmp = sea->issue_queue; tmp->next; tmp = tmp->next); + tmp->next = scb; + scb->next = NULL; /* placed at the end of the queue */ + } + /* Try to do some work on the card */ + if (!main_running) { + main_running = 1; + sea_main(); + /* main running is cleared in sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + } + if(!(scb->xfer->flags & SCSI_NOSLEEP)) { + splx(oldpri); + } + return (1); /* No possible errors right now */ +} + +/* + * sea_main(void) + * + * corroutine that runs as long as more work can be done on the seagate host + * adapter in a system. Both sea_scsi_cmd and sea_intr will try to start it in + * case it is not running. + */ + +static void sea_main(void) +{ + struct sea_data *sea; /* This time we look at all cards */ + struct sea_scb *tmp, *prev; + int done; + int unit; + int oldpri; + +#ifdef SEADEBUG2 + printf("."); +#endif + + /* + * This should not be run with interrupts disabled, but use the splx code + * instead + */ + do { + done = 1; + for (sea=seadata[unit=0]; (unit < NSEA) && seadata[unit] ; + sea=seadata[++unit]) { + oldpri = splbio(); + if (!sea->connected) { +#ifdef SEADEBUG2 + printf(".2"); +#endif + /* + * Search through the issue_queue for a command destined for a + * target that's not busy. + */ + for (tmp = sea->issue_queue, prev = NULL; tmp ; + prev = tmp, tmp = tmp->next) + /* When we find one, remove it from the issue queue. */ + if (!(sea->busy[tmp->xfer->sc_link->target] & + (1 << tmp->xfer->sc_link->lun))) { + if (prev) + prev->next = tmp->next; + else + sea->issue_queue = tmp->next; + tmp->next = NULL; + + /* re-enable interrupts after finding one */ + splx(oldpri); + + /* + * Attempt to establish an I_T_L nexus here. + * On success, sea->connected is set. + * On failure, we must add the command back to + * the issue queue so we can keep trying. + */ +#ifdef SEADEBUG2 + printf(".3"); +#endif + + /* REQUEST_SENSE commands are issued without tagged + * queueing, even on SCSI-II devices because the + * contingent alligence condition exists for the + * entire unit. + */ + + /* First check that if any device has tried a reconnect while + * we have done other things with interrupts disabled + */ + + if ((STATUS & (STAT_SEL | STAT_IO)) == (STAT_SEL | STAT_IO)) { +#ifdef SEADEBUG2 + printf(".7"); +#endif + sea_reselect(sea); + break; + } + if (!sea_select(sea, tmp)) { +#ifdef SEADEBUG2 + /* printf("Select returned ok\n"); */ + printf(".4"); +#endif + break; + } else { + oldpri = splbio(); + tmp->next = sea->issue_queue; + sea->issue_queue = tmp; + splx(oldpri); + printf("sea_main: select failed\n"); + } + } /* if target/lun is not busy */ + } /* if (!sea->connected) */ + + if (sea->connected) { /* we are connected. Do the task */ + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("sea_main: starting information transfer!\n"); */ + printf(".5"); +#endif + sea_information_transfer(sea); +#ifdef SEADEBUG2 +/* printf("sea_main: sea->connected:%lx\n", sea->connected); */ + printf(".6%lx ", sea->connected); +#endif + done = 0; + } else + break; + } /* for instance */ + } while (!done); + main_running = 0; +} + +void +sea_free_scb(unit, scb, flags) + int unit; + struct sea_scb *scb; + int flags; +{ + struct sea_data *sea = seadata[unit]; + unsigned int opri = 0; + +#ifdef SEADEBUG2 +/* printf("free_scb\n"); */ + printf(")"); +#endif + + if(!(flags & SCSI_NOMASK)) + opri = splbio(); + + scb->next = sea->free_scb; + sea->free_scb = scb; + scb->flags = SCB_FREE; + /* + * If there were none, wake anybody waiting for one to come free, + * starting with queued entries. + */ + if(!scb->next) { +#ifdef SEADEBUG2 +/* printf("free_scb waking up sleep\n"); */ + printf(")2"); +#endif +#ifdef SEA_FREEBSD11 + wakeup((caddr_t)&sea->free_scb); +#else + wakeup(&sea->free_scb); +#endif + } + + if (!(flags & SCSI_NOMASK)) + splx(opri); +} + +#ifdef SEA_FREEBSD11 +void +sea_timeout(caddr_t arg1, int arg2) +#else +void +sea_timeout(struct sea_scb *scb) +#endif +{ +#ifdef SEA_FREEBSD11 + struct sea_scb *scb = (struct sea_scb *)arg1; +#endif + int unit; + struct sea_data *sea; + int s=splbio(); + +#ifdef SEADEBUG2 +/* printf("sea_timeout called\n"); */ + printf(":"); +#endif + + unit = scb->xfer->sc_link->adapter_unit; + sea = seadata[unit]; +#ifndef SEADEBUG /* print message only if not waiting unless debug */ + if(!(scb->xfer->flags & SCSI_NOMASK)) +#endif + printf("sea%d:%d:%d (%s%d) timed out ", unit, + scb->xfer->sc_link->target, + scb->xfer->sc_link->lun, + scb->xfer->sc_link->device->name, + scb->xfer->sc_link->dev_unit); + + /* + * If it has been through before, then + * a previous abort has failed, don't + * try abort again + */ + if (/* (sea_abort(unit, scb) != 1) ||*/ (scb->flags & SCB_ABORTED)) { + /* + * abort timed out + */ +#ifdef SEADEBUG2 +/* printf("sea%d: Abort Operation has timed out\n", unit); */ + printf(":2"); +#endif + scb->xfer->retries = 0; + scb->flags |= SCB_ABORTED; + sea_done(unit, scb); + } else { + #ifdef SEADEBUG2 + /* printf("sea%d: Try to abort\n", unit); */ + printf(":3"); + #endif + sea_abort(unit, scb); + /* sea_send_scb(sea, ~SCSI_NOMASK, SEA_SCB_ABORT, scb); */ + /* 2 seconds for the abort */ + #ifdef SEA_FREEBSD11 + timeout(sea_timeout, (caddr_t)scb, 2*hz); + #else + timeout(sea_timeout, scb, 2*hz); + #endif + scb->flags |= (SCB_ABORTED | SCB_TIMECHK); + } + splx(s); +} + +int +sea_reselect(sea) + struct sea_data *sea; +{ + unsigned char target_mask; + long l; + unsigned char lun, phase; + unsigned char msg[3]; + int32 len; + u_char *data; + struct sea_scb *tmp = 0, *prev = 0; + int abort = 0; + +#if SEADEBUG2 +/* printf("sea_reselect called\n"); */ + printf("}"); +#endif + + if (!((target_mask = STATUS) & STAT_SEL)) { + printf("sea: wrong state 0x%x\n", target_mask); + return(0); + } + /* wait for a device to win the reselection phase */ + /* signals this by asserting the I/O signal */ + for(l=10; l && (STATUS & (STAT_SEL | STAT_IO | STAT_BSY)) + != (STAT_SEL | STAT_IO | 0);l--); + /* !! Check for timeout here */ + /* the data bus contains original initiator id ORed with target id */ + target_mask = DATA; + /* see that we really are the initiator */ + if (!(target_mask & ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40))) { + printf("sea: polled reselection was not for me: %x\n",target_mask); + return(0); + } + /* find target who won */ + target_mask &= ((sea->ctrl_type == SEAGATE) ? ~0x80 : ~0x40); + /* host responds by asserting the BSY signal */ + CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY); + /* target should respond by deasserting the SEL signal */ + for(l=50000;l && (STATUS & STAT_SEL);l++); + /* remove the busy status */ + CONTROL = (BASE_CMD | CMD_DRVR_ENABLE); + /* we are connected. Now we wait for the MSGIN condition */ + for(l=50000; l && !(STATUS & STAT_REQ);l--); + /* !! Add timeout check here */ + /* hope we get an IDENTIFY message */ + len = 3; + data = msg; + phase = REQ_MSGIN; + sea_transfer_pio(sea, &phase, &len, &data); + + if (!(msg[0] & 0x80)) { + printf("sea: Expecting IDENTIFY message, got 0x%x\n", msg[0]); + abort = 1; + } else { + lun = (msg[0] & 0x07); + + /* + * Find the command corresponding to the I_T_L or I_T_L_Q nexus we + * just restablished, and remove it from the disconnected queue. + */ + + for(tmp = sea->disconnected_queue, prev = NULL; + tmp; prev=tmp, tmp = tmp->next) + if((target_mask == (1 << tmp->xfer->sc_link->target)) && + (lun == tmp->xfer->sc_link->lun)) { + if(prev) { +#ifdef SEADEBUG2 + printf("}2"); +#endif + prev->next = tmp->next; + } else { +#ifdef SEADEBUG2 + printf("}3"); +#endif + sea->disconnected_queue = tmp->next; + } + tmp->next = NULL; + break; + } + if (!tmp) { + printf("sea: warning : target %02x lun %d not in disconnect_queue\n", + target_mask, lun); + /* + * Since we have an established nexus that we can't do anything with, + * we must abort it. + */ + abort = 1; + } + } + + if(abort) { +#ifdef SEADEBUG2 + printf("}4"); +#endif + msg[0] = MSG_ABORT; + len = 1; + data = msg; + phase = REQ_MSGOUT; + CONTROL = (BASE_CMD | CMD_ATTN); + sea_transfer_pio(sea, &phase, &len, &data); + } else { +#ifdef SEADEBUG2 + printf("}5"); +#endif + sea->connected = tmp; + } + /* return value not used yet */ + return 0; +} + +/* Transfer data in given phase using polled I/O +*/ + +int sea_transfer_pio(struct sea_data *sea, u_char *phase, int32 *count, + u_char **data) +{ + register unsigned char p = *phase, tmp; + register int c = *count; + register unsigned char *d = *data; + unsigned long int timeout; + +#if SEADEBUG2 +/* printf("sea_transfer_pio called: len:%x\n",c); */ + printf("-1 %x %x", c, p); +#endif + + do { + /* wait for assertion of REQ, after which the phase bits will be valid */ + for(timeout = 0; timeout < 5000000L ; timeout++) + if ((tmp = STATUS) & STAT_REQ) + break; + if (!(tmp & STAT_REQ)) { + printf("sea_transfer_pio: timeout waiting for STAT_REQ\n"); + break; + } + + /* check for phase mismatch */ + /* Reached if the target decides that it has finished the transfer */ + if ((tmp & REQ_MASK) != p) { +#ifdef SEADEBUG1 +/* printf("-2 %x", tmp); */ + printf("sea:pio phase mismatch:%x, want:%x, len:%x\n",tmp,p,c); +#endif + break; + } + + /* Do actual transfer from SCSI bus to/from memory */ + if (!(p & STAT_IO)) + DATA = *d; + else + *d = DATA; +#ifdef SEADEBUG15 + printf("-7%x", *d); +#endif + ++d; + + /* The SCSI standard suggests that in MSGOUT phase, the initiator + * should drop ATN on the last byte of the message phase + * after REQ has been asserted for the handshake but before + * the initiator raises ACK. + * Don't know how to accomplish this on the ST01/02 + */ + /* We don't mind right now. */ + + /* The st01 code doesn't wait for STAT_REQ to be deasserted. Is this ok? */ +/* for(timeout=0;timeout<200000L;timeout++) + if(!(STATUS & STAT_REQ)) + break; + if(STATUS & STAT_REQ) + printf("timeout on wait for !STAT_REQ"); */ +/* printf("*"); */ + } while (--c); + + *count = c; + *data = d; + tmp = STATUS; + if (tmp & STAT_REQ) { +#if SEADEBUG2 + printf("-3%x", tmp); +#endif + *phase = tmp & REQ_MASK; + } else { +#if SEADEBUG2 + printf("-4%x", tmp); +#endif + *phase = REQ_UNKNOWN; + } + if (!c || (*phase == p)) { +#if SEADEBUG2 + printf("-5%x %x", c, *phase); +#endif + return 0; + } else { +#if SEADEBUG2 + printf("-6"); +#endif + return -1; + } +} + +/* sea_select + * establish I_T_L or I_T_L_Q nexus for new or existing command + * including ARBITRATION, SELECTION, and initial message out for IDENTIFY and + * queue messages. + * return -1 if selection could not execute for some reason, 0 if selection + * succeded or failed because the taget did not respond + */ +int sea_select(struct sea_data *sea, struct sea_scb *scb) +{ + unsigned char tmp[3], phase; + u_char *data; + int32 len; + unsigned long timeout; + +#ifdef SEADEBUG2 +/* printf("sea_select called\n"); */ + printf("{"); +#endif + + CONTROL = BASE_CMD; + DATA = ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40); + CONTROL = (BASE_CMD & ~CMD_INTR) | CMD_START_ARB; + /* wait for arbitration to complete */ + for (timeout = 0; timeout < 3000000L ; timeout++) { + if (STATUS & STAT_ARB_CMPL) + break; + } + if (!(STATUS & STAT_ARB_CMPL)) { + if (STATUS & STAT_SEL) { + printf("sea: arbitration lost\n"); + scb->flags |= SCB_ERROR; + } else { + printf("sea: arbitration timeout.\n"); + scb->flags |=SCB_TIMEOUT; + } + CONTROL = BASE_CMD; + return(-1); + } + DELAY(2); + +#if SEADEBUG2 +/* printf("after arbitration: STATUS=%x\n", STATUS); */ + printf("{2 %x", STATUS); +#endif + + DATA = (unsigned char)((1 << scb->xfer->sc_link->target) | + ((sea->ctrl_type == SEAGATE) ? 0x80 : 0x40)); +#ifdef SEANOMSGS + CONTROL = (BASE_CMD & (~CMD_INTR))| CMD_DRVR_ENABLE | CMD_SEL; +#else + CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_SEL | CMD_ATTN; +#endif + DELAY(1); + /* wait for a bsy from target */ + for (timeout = 0; timeout < 2000000L; timeout++) { + if (STATUS & STAT_BSY) + break; + } + +#if SEADEBUG2 +/* printf("after wait for BSY: STATUS=%x,count=%lx\n", STATUS, timeout); */ + printf("{3 %x %x", STATUS, timeout); +#endif + + if (!(STATUS & STAT_BSY)) { + /* should return some error to the higher level driver */ + CONTROL = BASE_CMD; +#if SEADEBUG2 +/* printf("sea: target did not respond\n"); */ + printf("{4"); +#endif + scb->flags |= SCB_TIMEOUT; + return 0; + } + + /* Try to make the target to take a message from us */ +#ifdef SEANOMSGS + CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE; +#else + CONTROL = (BASE_CMD & (~CMD_INTR)) | CMD_DRVR_ENABLE | CMD_ATTN; +#endif + + DELAY(1); + + /* should start a msg_out phase */ + for (timeout = 0; timeout < 2000000L ; timeout++) { + if (STATUS & STAT_REQ) + break; + } + + CONTROL = BASE_CMD | CMD_DRVR_ENABLE; + +#if SEADEBUG2 || SEADEBUG9 +/* printf("after wait for STAT_REQ: STATUS=%x,count=%lx\n", STATUS, timeout); + printf("2:nd try after wait for STAT_REQ: STATUS=%x\n", STATUS); */ + printf("{5%x", timeout); +#endif + + if (!(STATUS & STAT_REQ)) { + /* This should not be taken as an error, but more like an unsupported + * feature! + * Should set a flag indicating that the target don't support messages, and + * continue without failure. (THIS IS NOT AN ERROR!) + */ +#if SEADEBUG +/* printf("{6"); */ + printf("sea: WARNING: target %x don't support messages?\n", + scb->xfer->sc_link->target); +#endif + } else { + tmp[0] = IDENTIFY(1, scb->xfer->sc_link->lun); /* allow disconnects */ + len = 1; + data = tmp; + phase = REQ_MSGOUT; + /* Should do test on result of sea_transfer_pio */ +#if SEADEBUG2 +/* printf("Trying a msg out phase\n"); */ + printf("{7"); +#endif + sea_transfer_pio(sea, &phase, &len, &data); + } + if (!(STATUS & STAT_BSY)) { + printf("sea: after successful arbitrate: No STAT_BSY!\n"); + } + +#if SEADEBUG2 + printf("{8"); +#endif + sea->connected = scb; + sea->busy[scb->xfer->sc_link->target] |= (1 << scb->xfer->sc_link->lun); + /* this assignment should depend on possibility to send a message to target */ + CONTROL = BASE_CMD | CMD_DRVR_ENABLE; + /* reset pointer in command ??? */ + return 0; +} + +/* sea_abort + send an abort to the target + return 1 success, 0 on failure + */ +int sea_abort(int unit, struct sea_scb *scb) +{ + struct sea_data *sea = seadata[unit]; + struct sea_scb *tmp, **prev; + unsigned char msg, phase, *msgptr; + int32 len; + int oldpri; + +#ifdef SEADEBUG2 +/* printf("sea_abort called\n"); */ + printf("\\"); +#endif + + oldpri = splbio(); + + /* If the command hasn't been issued yet, we simply remove it from the + * issue queue + */ + for (prev = (struct sea_scb **) &(sea->issue_queue), + tmp = sea->issue_queue; tmp; + prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next) + if (scb == tmp) { + (*prev) = tmp->next; + tmp->next = NULL; + /* set some type of error result for this operation */ + splx(oldpri); +#ifdef SEADEBUG2 + printf("\\2"); +#endif + return 1; + } + + /* If any commands are connected, we're going to fail the abort + * and let the high level SCSI driver retry at a later time or issue a + * reset + */ + + if(sea->connected) { + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("sea:abort error connected\n"); */ + printf("\\3"); +#endif + return 0; + } + + /* If the command is currently disconnected from the bus, and there are + * no connected commands, we reconnect the I_T_L or I_T_L_Q nexus + * associated with it, go into message out, and send an abort message. + */ + + for (tmp = sea->disconnected_queue; tmp; tmp = tmp->next) + if (scb == tmp) { + splx(oldpri); +#ifdef SEADEBUG2 + printf("\\4"); +#endif + if (sea_select(sea,scb)) { +#ifdef SEADEBUG2 + printf("\\5"); +#endif + return 0; + } + msg = MSG_ABORT; + msgptr = &msg; + len = 1; + phase = REQ_MSGOUT; + CONTROL = BASE_CMD | CMD_ATTN; + sea_transfer_pio(sea, &phase, &len, &msgptr); + + oldpri = splbio(); + for (prev = (struct sea_scb **) &(sea->disconnected_queue), + tmp = sea->disconnected_queue; tmp ; + prev = (struct sea_scb **) &(tmp->next), tmp = tmp->next) + if (scb == tmp) { + *prev = tmp->next; + tmp->next = NULL; + /* set some type of error result for the operation */ +#ifdef SEADEBUG2 + printf("\\6"); +#endif + splx(oldpri); + return 1; + } + } + + /* command not found in any queue, race condition in the code ? */ + + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("sea: WARNING: SCSI command probably completed successfully\n" + " before abortion\n"); */ + printf("\\7"); +#endif + return 1; + +} + +void sea_done(int unit, struct sea_scb *scb) +{ + struct sea_data *sea = seadata[unit]; + struct scsi_xfer *xs = scb->xfer; + + +#ifdef SEADEBUG2 +/* printf("sea_done called\n"); */ + printf("&"); +#endif + + if (scb->flags & SCB_TIMECHK) { +#ifdef SEADEBUG2 + printf("&2"); +#endif +#ifdef SEA_FREEBSD11 + untimeout(sea_timeout, (caddr_t)scb); +#else + untimeout(sea_timeout, scb); +#endif + } + + xs->resid = scb->datalen; /* How much of the buffer was not touched */ + + if ((scb->flags == SCB_ACTIVE) || (xs->flags & SCSI_ERR_OK)) { +#ifdef SEADEBUG2 +/* printf("sea_done:Report no err in xs\n"); */ + printf("&3"); +#endif +/* xs->resid = 0; */ +/* xs->error = 0; */ + } else { + + if (!(scb->flags == SCB_ACTIVE)) { + if ((scb->flags & SCB_TIMEOUT) || (scb->flags & SCB_ABORTED)) { +#ifdef SEADEBUG2 + printf("&6"); +#endif + xs->error = XS_TIMEOUT; + } + if (scb->flags & SCB_ERROR) { +#ifdef SEADEBUG2 + printf("&7"); +#endif + xs->error = XS_DRIVER_STUFFUP; + } + } else { + + /* !!! Add code to check for target status */ + /* say all error now */ + xs->error = XS_DRIVER_STUFFUP; +#ifdef SEADEBUG2 + printf("&4"); +#endif + } + } + xs->flags |= ITSDONE; + sea_free_scb(unit, scb, xs->flags); + scsi_done(xs); +#ifdef SEADEBUG2 +/* printf("Leaving sea_done\n"); */ + printf("&5"); +#endif +} + +/* wait for completion of command in polled mode */ + +int sea_poll(int unit, struct scsi_xfer *xs, struct sea_scb *scb) +{ + int count = 500; /* xs->timeout; */ + int oldpri; + +#ifdef SEADEBUG2 +/* printf("sea_poll called\n"); */ + printf("?"); +#endif + + while (count) { + /* try to do something */ + oldpri = splbio(); + if (!main_running) { + main_running = 1; + sea_main(); + /* main_running is cleared in sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + splx(oldpri); + } else { + splx(oldpri); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(10); + count--; + } +#ifdef SEADEBUG2 + printf("?2 %x ", count); +/* printf("sea_poll: count:%x\n",count); */ +#endif + if (count == 0) { + /* we timed out, so call the timeout handler manually, + * accounting for the fact that the clock is not running yet + * by taking out the clock queue entry it makes. + */ +#ifdef SEADEBUG2 + printf("?3"); +#endif +#ifdef SEA_FREEBSD11 + sea_timeout((caddr_t)scb, 0); +#else + sea_timeout(scb); +#endif + + /* because we are polling, take out the timeout entry + * sea_timeout made + */ +#ifdef SEADEBUG2 + printf("?4"); +#endif +#ifdef SEA_FREEBSD11 + untimeout(sea_timeout, (caddr_t) scb); +#else + untimeout(sea_timeout, scb); +#endif + count = 50; + while (count) { + /* once again, wait for the int bit */ + oldpri = splbio(); + if (!main_running) { + main_running = 1; + sea_main(); + /* main_running is cleared by sea_main once it can't + * do more work, and sea_main exits with interrupts + * disabled + */ + splx(oldpri); + } else { + splx(oldpri); + } + if (xs->flags & ITSDONE) { + break; + } + DELAY(10); + count--; + } + if (count == 0) { + /* we timed out again... This is bad. Notice that + * this time there is no clock queue entry to remove + */ +#ifdef SEADEBUG2 + printf("?5"); +#endif +#ifdef SEA_FREEBSD11 + sea_timeout((caddr_t)scb, 0); +#else + sea_timeout(scb); +#endif + } + } +#ifdef SEADEBUG2 +/* printf("sea_poll: xs->error:%x\n",xs->error); */ + printf("?6%x",xs->error); +#endif + if (xs->error) { +#ifdef SEADEBUG2 +/* printf("done return error\n"); */ + printf("?7"); +#endif + return (HAD_ERROR); + } +#ifdef SEADEBUG2 +/* printf("done return complete\n"); */ + printf("?8"); +#endif + return (COMPLETE); +} + +/* + * sea_information_transfer + * Do the transfer. We know we are connected. Update the flags, + * call sea_done when task accomplished. Dialog controlled by the + * target + */ +static void sea_information_transfer (struct sea_data *sea) +{ + long int timeout; + int unit = sea->sc_link.adapter_unit; + unsigned char msgout = MSG_NOP; + int32 len; + int oldpri; + u_char *data; + unsigned char phase, tmp, old_phase=REQ_UNKNOWN; + struct sea_scb *scb = sea->connected; + int loop; + +#if SEADEBUG2 +/* printf("sea_information_transfer called\n"); */ + printf("!"); +#endif + + for(timeout = 0; timeout < 10000000L ; timeout++) { + tmp = STATUS; + if (!(tmp & STAT_BSY)) { +/* for(loop=0;loop < 20 ; loop++) { + if((tmp=STATUS) & STAT_BSY) + break; + } */ +#ifndef SEADEBUG8 + if(!(tmp & STAT_BSY)) { + printf("sea: !STAT_BSY unit in data transfer!\n"); + oldpri = splbio(); + sea->connected = NULL; + scb->flags = SCB_ERROR; + splx(oldpri); + sea_done(unit, scb); + return; + } +#endif + } + + /* we only have a valid SCSI phase when REQ is asserted */ + if (tmp & STAT_REQ) { + phase = (tmp & REQ_MASK); + if (phase != old_phase) { + old_phase = phase; + } + +#ifdef SEADEBUG7 + printf("!2%x", phase); + for(loop=0;loop < 20; loop++) { + phase = STATUS; + printf("!6%x",phase); + phase = phase & REQ_MASK; + } +#endif + + switch (phase) { + case REQ_DATAOUT: +#ifdef SEA_NODATAOUT + printf("sea: SEA_NODATAOUT set, attempted DATAOUT aborted\n"); + msgout = MSG_ABORT; + CONTROL = BASE_CMD | CMD_ATTN; + break; +#endif + case REQ_DATAIN: +/* data = scb->xfer->data; + len = scb->xfer->datalen; +*/ if(!(scb->data)) { + printf("no data address!\n"); + } +#ifdef SEA_BLINDTRANSFER + if (scb->datalen && !(scb->datalen % BLOCK_SIZE)) { + while (scb->datalen) { + for(timeout = 0; timeout < 5000000L ; timeout++) + if((tmp = STATUS) & STAT_REQ) + break; + if(!(tmp & STAT_REQ)) { + printf("sea_transfer_pio: timeout waiting for STAT_REQ\n"); + /* getchar(); */ + } + if((tmp & REQ_MASK) != phase) { +#ifdef SEADEBUG1 + printf("sea:infotransfer phase mismatch:%x, want:%x, len:%x\n", + tmp,phase,scb->datalen); + /* getchar(); */ +#endif + break; + } + if(!(phase & STAT_IO)) { +#ifdef SEA_ASSEMBLER + asm(" + shr $2, %%ecx; + cld; + rep; + movsl; " : : + "D" (sea->st0x_dr), "S" (scb->data), "c" (BLOCK_SIZE) : + "cx", "si", "di" ); + scb->data += BLOCK_SIZE; +#else + for(count=0; count < BLOCK_SIZE; count++) { + DATA = *(scb->data); + scb->data++; + } +#endif + } else { +#ifdef SEA_ASSEMBLER + asm(" + shr $2, %%ecx; + cld; + rep; + movsl; " : : + "S" (sea->st0x_dr), "D" (scb->data), "c" (BLOCK_SIZE) : + "cx", "si", "di" ); + scb->data += BLOCK_SIZE; +#else + for(count=0; count < BLOCK_SIZE; count++) { + *scb->data = DATA; + scb->data++; + } +#endif + } + scb->datalen -= BLOCK_SIZE; + } + } + + /* save current position into the command structure */ +/* scb->xfer->data = data; + scb->xfer->datalen = len; */ +#endif + + sea_transfer_pio(sea, &phase, &(scb->datalen), &(scb->data)); +/* scb->xfer->data = data; + scb->xfer->datalen = len; +*/ break; + + case REQ_MSGIN: + /* don't handle multi-byte messages here, because they + * should not be present here + */ + len = 1; + data = &tmp; + sea_transfer_pio(sea, &phase, &len, &data); + /* scb->MessageIn = tmp; */ + + switch (tmp) { + + case MSG_ABORT: + scb->flags = SCB_ABORTED; + printf("sea:Command aborted by target\n"); + CONTROL = BASE_CMD; + sea_done(unit, scb); + return; + + case MSG_COMMAND_COMPLETE: + oldpri = splbio(); + sea->connected = NULL; + splx(oldpri); +#ifdef SEADEBUG2 + printf("!3"); +#endif + sea->busy[scb->xfer->sc_link->target] &= + ~(1 << scb->xfer->sc_link->lun); + + CONTROL = BASE_CMD; + sea_done(unit, scb); + return; + case MSG_MESSAGE_REJECT: + /* printf("sea: message_reject recieved\n"); */ + printf("!4"); + break; + case MSG_DISCONNECT: + oldpri = splbio(); + scb->next = sea->disconnected_queue; + sea->disconnected_queue = scb; + sea->connected = NULL; + CONTROL = BASE_CMD; + splx(oldpri); +#ifdef SEADEBUG2 +/* printf("msg_disconnect\n"); */ + printf("!5"); +#endif + return; + /* save/restore of pointers are ignored */ + case MSG_SAVE_POINTERS: + case MSG_RESTORE_POINTERS: +#if SEADEBUG2 + printf("sea: rec save/restore ptrs\n"); +#endif + break; + default: + /* this should be handled in the pio data transfer phase, as the + * ATN should be raised before ACK goes false when rejecting a message + */ +#ifdef SEADEBUG + printf("sea: Unknown message in:%x\n", tmp); +#endif + break; + } /* switch (tmp) */ + break; + case REQ_MSGOUT: + len = 1; + data = &msgout; + /* sea->last_message = msgout; */ + sea_transfer_pio(sea, &phase, &len, &data); + if (msgout == MSG_ABORT) { + printf("sea: sent message abort to target\n"); + oldpri = splbio(); + sea->busy[scb->xfer->sc_link->target] &= + ~(1 << scb->xfer->sc_link->lun); + sea->connected = NULL; + scb->flags = SCB_ABORTED; + splx(oldpri); + /* enable interrupt from scsi */ + sea_done(unit, scb); + return; + } + msgout = MSG_NOP; + break; + case REQ_CMDOUT: + len = scb->xfer->cmdlen; + data = (char *) scb->xfer->cmd; + sea_transfer_pio(sea, &phase, &len, &data); + break; + case REQ_STATIN: + len = 1; + data = &tmp; + sea_transfer_pio(sea, &phase, &len, &data); + scb->xfer->status = tmp; + break; + default: + printf("sea: unknown phase\n"); + } /* switch (phase) */ + } /* if (tmp & STAT_REQ) */ + } /* for (...) */ + /* if we get here we have got a timeout! */ + printf("sea: Timeout in data transfer\n"); + scb->flags = SCB_TIMEOUT; + /* should I clear scsi-bus state? */ + sea_done(unit, scb); +} + + diff --git a/sys/i386/isa/sio.c b/sys/i386/isa/sio.c index 4cee4fe8c60f..6fa09247e5f3 100644 --- a/sys/i386/isa/sio.c +++ b/sys/i386/isa/sio.c @@ -31,7 +31,7 @@ * SUCH DAMAGE. * * from: @(#)com.c 7.5 (Berkeley) 5/16/91 - * $Id: sio.c,v 1.28 1994/02/07 18:37:21 ache Exp $ + * $Id: sio.c,v 1.56 1994/06/16 08:08:44 ache Exp $ */ #include "sio.h" @@ -49,17 +49,19 @@ #include "proc.h" #include "user.h" #include "conf.h" +#include "dkstat.h" #include "file.h" #include "uio.h" #include "kernel.h" +#include "malloc.h" #include "syslog.h" +#include "i386/isa/icu.h" /* XXX */ #include "i386/isa/isa.h" #include "i386/isa/isa_device.h" #include "i386/isa/comreg.h" #include "i386/isa/ic/ns16550.h" -#define FAKE_DCD(unit) ((unit) == comconsole) #define LOTS_OF_EVENTS 64 /* helps separate urgent events from input */ #define RB_I_HIGH_WATER (RBSZ - 2 * RS_IBUFSIZE) #define RB_I_LOW_WATER ((RBSZ - 2 * RS_IBUFSIZE) * 7 / 8) @@ -67,38 +69,27 @@ #define TTY_BI TTY_FE /* XXX */ #define TTY_OE TTY_PE /* XXX */ -#ifndef COM_BIDIR -#define UNIT(x) (minor(x)) /* XXX */ -#else /* COM_BIDIR */ -#define COM_UNITMASK 0x7f -#define COM_CALLOUTMASK 0x80 /* for both minor and dev */ -#define UNIT(x) (minor(x) & COM_UNITMASK) -#define CALLOUT(x) (minor(x) & COM_CALLOUTMASK) -#endif /* COM_BIDIR */ +#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_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_NOFIFO(dev) ((dev)->id_flags & 0x02) -#ifndef FIFO_TRIGGER -/* - * This driver is fast enough to work with any value and for high values - * to be only slightly more efficient. Low values may be better because - * they give lower latency. - * TODO: always use low values for low speeds. Mouse movements are jerky - * if more than one packet arrives at once. The low speeds used for - * serial mice help avoid this, but not if (large) fifos are enabled. - */ -#define FIFO_TRIGGER FIFO_TRIGGER_14 -#endif +#define COM_NOFIFO(dev) ((dev)->id_flags & 0x02) +#define COM_VERBOSE(dev) ((dev)->id_flags & 0x80) #define com_scr 7 /* scratch register for 16450-16550 (R/W) */ -#define setsofttty() (ipending |= 1 << 4) /* XXX */ /* * Input buffer watermarks. @@ -124,17 +115,19 @@ * CS_CTS_OFLOW = CCTS_OFLOW (maintained by comparam()) * CS_RTS_IFLOW = CRTS_IFLOW (maintained by comparam()) * TS_FLUSH is not used. - * Bug: I think TIOCSETA doesn't clear TS_TTSTOP when it clears IXON. + * 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 */ -static char *error_desc[] = { +static char const * const error_desc[] = { #define CE_OVERRUN 0 "silo overflow", #define CE_INTERRUPT_BUF_OVERFLOW 1 @@ -153,20 +146,19 @@ typedef u_char bool_t; /* boolean */ /* 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 */ + u_char ftl; /* current rx fifo trigger level */ + u_char ftl_init; /* ftl_max for next open() */ + u_char ftl_max; /* maximum ftl for curent open() */ bool_t hasfifo; /* nonzero for 16550 UARTs */ u_char mcr_image; /* copy of value written to MCR */ -#ifdef COM_BIDIR - bool_t bidir; /* is this unit bidirectional? */ - bool_t active; /* is the port active _at all_? */ - bool_t active_in; /* is the incoming port in use? */ - bool_t active_out; /* is the outgoing port in use? */ -#endif /* COM_BIDIR */ #ifdef COM_MULTIPORT bool_t multiport; /* is this unit part of a multiport device? */ #endif /* COM_MULTIPORT */ - int dtr_wait; /* time to hold DTR down on close (* 1/HZ) */ + 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 @@ -177,6 +169,7 @@ struct com_s { 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 */ @@ -195,6 +188,19 @@ struct com_s { 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; + +#ifdef TIOCTIMESTAMP + bool_t do_timestamp; + struct timeval timestamp; +#endif + u_long bytes_in; /* statistics */ u_long bytes_out; u_int delta_error_counts[CE_NTYPES]; @@ -210,48 +216,56 @@ struct com_s { }; /* - * These functions in the com module ought to be declared (with a prototype) - * in a com-driver system header. The void ones may need to be int to match - * ancient devswitch declarations, but they don't actually return anything. + * The public functions in the com module ought to be declared in a com-driver + * system header. */ #define Dev_t int /* promoted dev_t */ -struct consdev; +/* Interrupt handling entry points. */ +void siointr __P((int unit)); +void siopoll __P((void)); + +/* Device switch entry points. */ +int sioopen __P((Dev_t dev, int oflags, int devtype, + struct proc *p)); int sioclose __P((Dev_t dev, int fflag, int devtype, struct proc *p)); -void siointr __P((int unit)); +int sioread __P((Dev_t dev, struct uio *uio, int ioflag)); +int siowrite __P((Dev_t dev, struct uio *uio, int ioflag)); int sioioctl __P((Dev_t dev, int cmd, caddr_t data, int fflag, struct proc *p)); +void siostop __P((struct tty *tp, int rw)); +#define sioreset noreset +int sioselect __P((Dev_t dev, int rw, struct proc *p)); +#define siommap nommap +#define siostrategy nostrategy + +/* Console device entry points. */ int siocngetc __P((Dev_t dev)); +struct consdev; void siocninit __P((struct consdev *cp)); void siocnprobe __P((struct consdev *cp)); void siocnputc __P((Dev_t dev, int c)); -int sioopen __P((Dev_t dev, int oflags, int devtype, - struct proc *p)); -void siopoll __P((void)); -int sioread __P((Dev_t dev, struct uio *uio, int ioflag)); -int sioselect __P((Dev_t dev, int rw, struct proc *p)); -void siostop __P((struct tty *tp, int rw)); -int siowrite __P((Dev_t dev, struct uio *uio, int ioflag)); -void softsio1 __P((void)); static int sioattach __P((struct isa_device *dev)); +static void siodtrwakeup __P((caddr_t chan, int ticks)); static void comflush __P((struct com_s *com)); static void comhardclose __P((struct com_s *com)); -static void cominit __P((int unit, int rate)); -static void comintr1 __P((struct com_s *com)); +static void siointr1 __P((struct com_s *com)); static void 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 comstart __P((struct tty *tp)); -static void comwakeup __P((caddr_t chan, int ticks)); +static void comstart __P((struct tty *tp)); +static void comwakeup __P((caddr_t chan, int ticks)); static int tiocm_xxx2mcr __P((int tiocm_xxx)); /* table and macro for fast conversion from a unit number to its com struct */ static struct com_s *p_com_addr[NSIO]; #define com_addr(unit) (p_com_addr[unit]) -static struct com_s com_structs[NSIO]; +#ifdef TIOCTIMESTAMP +static struct timeval intr_timestamp; +#endif struct isa_driver siodriver = { sioprobe, sioattach, "sio" @@ -262,15 +276,11 @@ static int comconsole = COMCONSOLE; #else static int comconsole = -1; #endif -static bool_t comconsinit; static speed_t comdefaultrate = TTYDEF_SPEED; static u_int com_events; /* input chars + weighted output completions */ static int commajor; -struct tty sio_tty[NSIO]; -extern struct tty *constty; -extern u_int ipending; /* XXX */ -extern int tk_nin; /* XXX */ -extern int tk_rawcc; /* XXX */ +struct tty *sio_tty[NSIO]; +extern struct tty *constty; /* XXX */ #ifdef KGDB #include "machine/remote-sl.h" @@ -311,7 +321,11 @@ sioprobe(dev) { static bool_t already_init; Port_t *com_ptr; + bool_t failures[10]; + int fn; + struct isa_device *idev; Port_t iobase; + u_char mcr_image; int result; if (!already_init) { @@ -319,6 +333,7 @@ sioprobe(dev) * 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 @@ -327,8 +342,45 @@ sioprobe(dev) outb(*com_ptr + com_mcr, 0); already_init = TRUE; } + + /* + * If the port is on a multiport card and has a master port, + * initialize the common interrupt control register in the + * master 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 found\n", + dev->id_unit, COM_MPMASTER(dev)); + return (0); + } + if (idev->id_irq == 0) { + printf("sio%d: master device %d irq not configured\n", + dev->id_unit, COM_MPMASTER(dev)); + return (0); + } + if (!COM_NOTAST4(dev)) { + outb(idev->id_iobase + com_scr, 0x80); + mcr_image = 0; + } + } + else +#endif /* COM_MULTIPORT */ + if (idev->id_irq == 0) { + printf("sio%d: irq not configured\n", dev->id_unit); + return (0); + } + + bzero(failures, sizeof failures); iobase = dev->id_iobase; - result = IO_COMSIZE; /* * We don't want to get actual interrupts, just masked ones. @@ -339,55 +391,115 @@ sioprobe(dev) disable_intr(); /* - * Initialize the speed so that any junk in the THR or output fifo will - * be transmitted in a known time. (There may be lots of junk after a - * soft reboot, and output interrupts don't work right after a master - * reset, at least for 16550s. (The speed is undefined after MR, but - * MR empties the THR and the TSR so it's not clear why this matters)). - * Enable output interrupts (only) and check the following: + * XXX DELAY() reenables CPU interrupts. This is a problem for + * shared interrupts after the first device using one has been + * successfully probed - config_isadev() has enabled the interrupt + * in the ICU. + */ + outb(IO_ICU1 + 1, 0xff); + + /* + * 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); + 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) * 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. + */ + 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. + */ + 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((2 + 1) * 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. + */ + 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. */ - outb(iobase + com_cfcr, CFCR_DLAB); - outb(iobase + com_dlbl, COMBRD(9600) & 0xff); - outb(iobase + com_dlbh, (u_int) COMBRD(9600) >> 8); - outb(iobase + com_cfcr, CFCR_8BITS); /* ensure IER is addressed */ - outb(iobase + com_mcr, MCR_IENABLE); /* open gate early */ - outb(iobase + com_ier, 0); /* ensure edge on next intr */ - outb(iobase + com_ier, IER_ETXRDY); /* generate interrupt */ - DELAY((16 + 1) * 9600 / 10); /* enough to drain 16 bytes */ - if ( inb(iobase + com_cfcr) != CFCR_8BITS - || inb(iobase + com_ier) != IER_ETXRDY - || inb(iobase + com_mcr) != MCR_IENABLE -#ifndef COM_MULTIPORT /* XXX - need to do more to enable interrupts */ - || !isa_irq_pending(dev) -#endif - || (inb(iobase + com_iir) & IIR_IMASK) != IIR_TXRDY - || isa_irq_pending(dev) - || !(inb(iobase + com_iir) & IIR_NOPEND)) - result = 0; + failures[0] = inb(iobase + com_cfcr) - CFCR_8BITS; + failures[1] = inb(iobase + com_ier) - IER_ETXRDY; + failures[2] = inb(iobase + com_mcr) - mcr_image; + if (idev->id_irq != 0) + failures[3] = isa_irq_pending(idev) ? 0 : 1; + failures[4] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_TXRDY; + failures[5] = isa_irq_pending(idev) ? 1 : 0; + failures[6] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; /* * Turn off all device interrupts and check that they go off properly. - * Leave MCR_IENABLE set. It gates the OUT2 output of the UART to + * 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_mcr, MCR_IENABLE); /* dummy to avoid bus echo */ - if ( inb(iobase + com_ier) != 0 - || isa_irq_pending(dev) - || !(inb(iobase + com_iir) & IIR_NOPEND)) - result = 0; - if (result == 0) - outb(iobase + com_mcr, 0); + outb(iobase + com_cfcr, CFCR_8BITS); /* dummy to avoid bus echo */ + failures[7] = inb(iobase + com_ier); + failures[8] = isa_irq_pending(idev) ? 1 : 0; + failures[9] = (inb(iobase + com_iir) & IIR_IMASK) - IIR_NOPEND; + outb(IO_ICU1 + 1, imen); /* XXX */ 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); } @@ -403,9 +515,9 @@ sioattach(isdp) iobase = isdp->id_iobase; unit = isdp->id_unit; - if (unit == comconsole) - DELAY(1000); /* XXX */ - s = spltty(); + com = malloc(sizeof *com, M_TTYS, M_NOWAIT); + if (com == NULL) + return (0); /* * sioprobe() has initialized the device registers as follows: @@ -414,15 +526,13 @@ sioattach(isdp) * 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. + * o mcr = MCR_IENABLE, or 0 if the port has a master port. * Keeping MCR_DTR and MCR_RTS off might stop the external * device from sending before we are ready. */ - - com = &com_structs[unit]; + bzero(com, sizeof *com); com->cfcr_image = CFCR_8BITS; - com->mcr_image = MCR_IENABLE; - com->dtr_wait = 200; + com->dtr_wait = 3 * hz; com->tx_fifo_size = 1; com->iptr = com->ibuf = com->ibuf1; com->ibufend = com->ibuf1 + RS_IBUFSIZE; @@ -431,9 +541,30 @@ sioattach(isdp) 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; - com->tp = &sio_tty[unit]; + + /* + * We don't use all the flags from <sys/ttydefaults.h> 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; + } + termioschars(&com->it_in); + com->it_in.c_ispeed = com->it_in.c_ospeed = comdefaultrate; + com->it_out = com->it_in; /* attempt to determine UART type */ printf("sio%d: type", unit); @@ -470,10 +601,11 @@ sioattach(isdp) break; case FIFO_TRIGGER_14: printf(" 16550A"); - if (COM_NOFIFO(isdp)) { - printf(" fifo software disabled"); - } else { + if (COM_NOFIFO(isdp)) + printf(" fifo disabled"); + else { com->hasfifo = TRUE; + com->ftl_init = FIFO_TRIGGER_14; com->tx_fifo_size = 16; } break; @@ -483,31 +615,36 @@ determined_type: ; #ifdef COM_MULTIPORT if (COM_ISMULTIPORT(isdp)) { - struct isa_device *masterdev; - com->multiport = TRUE; - printf(" (multiport)"); - - /* set the master's common-interrupt-enable reg., - * as appropriate. YYY See your manual - */ - /* enable only common interrupt for port */ - outb(com->modem_ctl_port, com->mcr_image = 0); - - masterdev = find_isadev(isa_devtab_tty, &siodriver, - COM_MPMASTER(isdp)); - outb(masterdev->id_iobase + com_scr, 0x80); - } else - com->multiport = FALSE; + printf(" (multiport"); + if (unit == COM_MPMASTER(isdp)) + printf(" master"); + printf(")"); + } #endif /* COM_MULTIPORT */ printf("\n"); #ifdef KGDB if (kgdb_dev == makedev(commajor, unit)) { - if (comconsole == unit) + if (unit == comconsole) kgdb_dev = -1; /* can't debug over console port */ else { - cominit(unit, kgdb_rate); + int divisor; + + /* + * XXX now unfinished and broken. Need to do + * something more like a full open(). There's no + * suitable interrupt handler so don't enable device + * interrupts. Watch out for null tp's. + */ + outb(iobase + com_cfcr, CFCR_DLAB); + divisor = ttspeedtab(kgdb_rate, comspeedtab); + outb(iobase + com_dlbl, divisor & 0xFF); + outb(iobase + com_dlbh, (u_int) divisor >> 8); + outb(iobase + com_cfcr, CFCR_8BITS); + outb(com->modem_status_port, + com->mcr_image |= MCR_DTR | MCR_RTS); + if (kgdb_debug_init) { /* * Print prefix of device name, @@ -521,16 +658,11 @@ determined_type: ; } #endif - /* - * Need to reset baud rate, etc. of next print so reset comconsinit. - */ - if (unit == comconsole) - comconsinit = FALSE; - + s = spltty(); com_addr(unit) = com; splx(s); if (!comwakeup_started) { - comwakeup((caddr_t) NULL, 0); + comwakeup((caddr_t)NULL, 0); comwakeup_started = TRUE; } return (1); @@ -544,207 +676,127 @@ sioopen(dev, flag, mode, p) int mode; struct proc *p; { -#ifdef COM_BIDIR - bool_t callout; -#endif /* COM_BIDIR */ struct com_s *com; - int error = 0; + int error; Port_t iobase; + int mynor; int s; struct tty *tp; int unit; - unit = UNIT(dev); + mynor = minor(dev); + unit = MINOR_TO_UNIT(mynor); if ((u_int) unit >= NSIO || (com = com_addr(unit)) == NULL) return (ENXIO); -#ifdef COM_BIDIR - /* if it's a callout device, and bidir not possible on that dev, die */ - callout = CALLOUT(dev); - if (callout && !(com->bidir)) - return (ENXIO); -#endif /* COM_BIDIR */ - - tp = com->tp; + if (mynor & CONTROL_MASK) + return (0); + tp = com->tp = sio_tty[unit] = ttymalloc(sio_tty[unit]); s = spltty(); - -#ifdef COM_BIDIR - -bidir_open_top: - /* if it's bidirectional, we've gotta deal with it... */ - if (com->bidir) { - if (callout) { - if (com->active_in) { - /* it's busy. die */ - splx(s); - return (EBUSY); - } else { - /* it's ours. lock it down, and set it up */ - com->active_out = TRUE; + /* + * 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((caddr_t)&com->dtr_wait, TTIPRI | PCATCH, + "siodtr", 0); + if (error != 0) + 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) { - /* it's busy, outgoing. wait, if possible */ if (flag & O_NONBLOCK) { - /* can't wait; bail */ - splx(s); - return (EBUSY); - } else { - /* wait for it... */ - error = tsleep((caddr_t)&com->active_out, - TTIPRI|PCATCH, - "siooth", - 0); - /* if there was an error, take off. */ - if (error != 0) { - splx(s); - return (error); - } - /* else take it from the top */ - goto bidir_open_top; - } - } else if (com->prev_modem_status & MSR_DCD - || FAKE_DCD(unit)) { - /* there's a carrier on the line; we win */ - com->active_in = TRUE; - } else { - /* there is no carrier on the line */ - if (flag & O_NONBLOCK) { - /* can't wait; let it open */ - com->active_in = TRUE; - } else { - /* put DTR & RTS up */ - /* XXX - bring up RTS earlier? */ - commctl(com, MCR_DTR | MCR_RTS, DMSET); - outb(com->iobase + com_ier, IER_EMSC); - - /* wait for it... */ - error = tsleep((caddr_t)&com->active_in, - TTIPRI|PCATCH, - "siodcd", - 0); - - /* if not active, turn intrs and DTR off */ - if (!com->active) { - outb(com->iobase + com_ier, 0); - commctl(com, MCR_DTR, DMBIC); - } - - /* if there was an error, take off. */ - if (error != 0) { - splx(s); - return (error); - } - /* else take it from the top */ - goto bidir_open_top; + error = EBUSY; + goto out; } + error = tsleep((caddr_t)&com->active_out, + TTIPRI | PCATCH, "siobi", 0); + if (error != 0) + goto out; + goto open_top; } } - } - - com->active = TRUE; -#endif /* COM_BIDIR */ - - tp->t_oproc = comstart; - tp->t_param = comparam; - tp->t_dev = dev; - if (!(tp->t_state & TS_ISOPEN)) { - tp->t_state |= TS_WOPEN; - ttychars(tp); - if (tp->t_ispeed == 0) { - /* - * We no longer use the flags from <sys/ttydefaults.h> - * since those 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. - */ - tp->t_iflag = 0; - tp->t_oflag = 0; -#ifdef COMCONSOLE - if (unit == comconsole) - tp->t_oflag = TTYDEF_OFLAG; -#endif - tp->t_cflag = CREAD | CS8 | HUPCL; - tp->t_lflag = 0; - tp->t_ispeed = tp->t_ospeed = comdefaultrate; + if (tp->t_state & TS_XCLUDE && p->p_ucred->cr_uid != 0) { + error = EBUSY; + goto out; } - + } else { /* - * XXX the full state after a first open() needs to be - * programmable and separate for callin and callout. + * 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. */ -#ifdef COM_BIDIR - if (com->bidir) { - if (callout) - tp->t_cflag |= CLOCAL; - else - tp->t_cflag &= ~CLOCAL; - } -#endif - + tp->t_oproc = comstart; + tp->t_param = comparam; + tp->t_dev = dev; + tp->t_termios = mynor & CALLOUT_MASK + ? com->it_out : com->it_in; commctl(com, MCR_DTR | MCR_RTS, DMSET); + com->ftl_max = com->ftl_init; + ++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->i |