M pc/devether.c => pc/devether.c +17 -3
@@ 78,7 78,21 @@ etherclose(Chan *c)
long
etherread(Chan *c, void *buf, long n, ulong offset)
{
- return netifread(ether[c->dev], c, buf, n, offset);
+ Ether *ctlr;
+
+ ctlr = ether[c->dev];
+ if((c->qid.path & CHDIR) == 0 && ctlr->ifstat){
+ /*
+ * With some controllers it is necessary to reach
+ * into the chip to extract statistics.
+ */
+ if(NETTYPE(c->qid.path) == Nifstatqid)
+ return (*ctlr->ifstat)(ctlr, buf, n, offset);
+ else if(NETTYPE(c->qid.path) == Nstatqid)
+ (*ctlr->ifstat)(ctlr, buf, 0, offset);
+ }
+
+ return netifread(ctlr, c, buf, n, offset);
}
Block*
@@ 233,7 247,7 @@ etherreset(void)
ctlr->irq = 9;
setvec(Int0vec+ctlr->irq, ctlr->interrupt, ctlr);
- print("ether%d: %s: %dMbps port 0x%luX irq %d",
+ print("ether#%d: %s: %dMbps port 0x%luX irq %d",
ctlrno, ctlr->type, ctlr->mbps, ctlr->port, ctlr->irq);
if(ctlr->mem)
print(" addr 0x%luX", ctlr->mem & ~KZERO);
@@ 245,7 259,7 @@ etherreset(void)
print("\n");
if(ctlr->mbps == 100)
- netifinit(ctlr, "ether", Ntypes, 100*1024);
+ netifinit(ctlr, "ether", Ntypes, 128*1024);
else
netifinit(ctlr, "ether", Ntypes, 32*1024);
ctlr->alen = Eaddrlen;
M pc/dma.c => pc/dma.c +13 -24
@@ 4,9 4,6 @@
#include "dat.h"
#include "fns.h"
-/*
- * headland chip set for the safari.
- */
typedef struct DMAport DMAport;
typedef struct DMA DMA;
typedef struct DMAxfer DMAxfer;
@@ 33,8 30,9 @@ enum
*/
struct DMAxfer
{
- Page pg; /* page used by dma */
- void *va; /* virtual address destination/src */
+ ulong bpa; /* bounce buffer physical address */
+ void* bva; /* bounce buffer virtual address */
+ void* va; /* virtual address destination/src */
long len; /* bytes to be transferred */
int isread;
};
@@ 95,8 93,8 @@ dmainit(void)
dp = &dma[i];
for(chan = 0; chan < 4; chan++){
xp = &dp->x[chan];
- xp->pg.pa = (ulong)xspanalloc(BY2PG, BY2PG, 0);
- xp->pg.va = KZERO|xp->pg.pa;
+ xp->bva = xspanalloc(BY2PG, BY2PG, 0);
+ xp->bpa = PADDR(xp->bva);
xp->len = 0;
xp->isread = 0;
}
@@ 117,9 115,9 @@ long
dmasetup(int chan, void *va, long len, int isread)
{
DMA *dp;
- DMAxfer *xp;
ulong pa;
uchar mode;
+ DMAxfer *xp;
dp = &dma[(chan>>2)&1];
chan = chan & 3;
@@ 132,16 130,17 @@ dmasetup(int chan, void *va, long len, int isread)
pa = PADDR(va);
if((((ulong)va)&0xF0000000) != KZERO
|| (pa&0xFFFF0000) != ((pa+len)&0xFFFF0000)
- || pa > 16*MB){
+ || pa > 16*MB) {
if(len > BY2PG)
len = BY2PG;
if(!isread)
- memmove((void*)(xp->pg.va), va, len);
+ memmove(xp->bva, va, len);
xp->va = va;
xp->len = len;
xp->isread = isread;
- pa = xp->pg.pa;
- } else
+ pa = xp->bpa;
+ }
+ else
xp->len = 0;
/*
@@ 149,7 148,7 @@ dmasetup(int chan, void *va, long len, int isread)
*/
ilock(dp);
mode = (isread ? 0x44 : 0x48) | chan;
- outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */
+ outb(dp->mode, mode); /* single mode dma (give CPU a chance at mem) */
outb(dp->page[chan], pa>>16);
outb(dp->cbp, 0); /* set count & address to their first byte */
outb(dp->addr[chan], pa>>dp->shift); /* set address */
@@ 203,16 202,6 @@ dmaend(int chan)
/*
* copy out of temporary page
*/
- memmove(xp->va, (void*)(xp->pg.va), xp->len);
+ memmove(xp->va, xp->bva, xp->len);
xp->len = 0;
}
-
-void
-dmaemode(int chan, int value)
-{
- if(chan < 4)
- outb(0x40b, value|chan);
- else
- outb(0x4d6, value|chan);
-
-}
D pc/ether509.c => pc/ether509.c +0 -785
@@ 1,785 0,0 @@
-#include "u.h"
-#include "../port/lib.h"
-#include "mem.h"
-#include "dat.h"
-#include "fns.h"
-#include "io.h"
-#include "../port/error.h"
-#include "../port/netif.h"
-
-#include "etherif.h"
-
-enum {
- IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */
- /* Commands */
- GlobalReset = 0x00, /* Global Reset */
- SelectWindow = 0x01, /* SelectWindow command */
- StartCoax = 0x02, /* Start Coaxial Transceiver */
- RxDisable = 0x03, /* RX Disable */
- RxEnable = 0x04, /* RX Enable */
- RxReset = 0x05, /* RX Reset */
- RxDiscard = 0x08, /* RX Discard Top Packet */
- TxEnable = 0x09, /* TX Enable */
- TxDisable = 0x0A, /* TX Disable */
- TxReset = 0x0B, /* TX Reset */
- AckIntr = 0x0D, /* Acknowledge Interrupt */
- SetIntrMask = 0x0E, /* Set Interrupt Mask */
- SetReadZeroMask = 0x0F, /* Set Read Zero Mask */
- SetRxFilter = 0x10, /* Set RX Filter */
- SetTxAvailable = 0x12, /* Set TX Available Threshold */
-
- /* RX Filter Command Bits */
- MyEtherAddr = 0x01, /* Individual address */
- Multicast = 0x02, /* Group (multicast) addresses */
- Broadcast = 0x04, /* Broadcast address */
- Promiscuous = 0x08, /* All addresses (promiscuous mode) */
-
- /* Window Register Offsets */
- Command = 0x0E, /* all windows */
- Status = 0x0E,
-
- ManufacturerID = 0x00, /* window 0 */
- ProductID = 0x02,
- ConfigControl = 0x04,
- AddressConfig = 0x06,
- ResourceConfig = 0x08,
- EEPROMcmd = 0x0A,
- EEPROMdata = 0x0C,
-
- /* AddressConfig Bits */
- XcvrTypeMask = 0xC000, /* Transceiver Type Select */
- Xcvr10BaseT = 0x0000,
- XcvrAUI = 0x4000,
- XcvrBNC = 0xC000,
-
- /* ConfigControl */
- Rst = 0x04, /* Reset Adapter */
- Ena = 0x01, /* Enable Adapter */
-
- Fifo = 0x00, /* window 1 */
- RxStatus = 0x08,
- Timer = 0x0A,
- TxStatus = 0x0B,
- TxFreeBytes = 0x0C,
-
- /* Status/Interrupt Bits */
- Latch = 0x0001, /* Interrupt Latch */
- Failure = 0x0002, /* Adapter Failure */
- TxComplete = 0x0004, /* TX Complete */
- TxAvailable = 0x0008, /* TX Available */
- RxComplete = 0x0010, /* RX Complete */
- Update = 0x0080, /* Update Statistics */
- AllIntr = 0x00FE, /* All Interrupt Bits */
- CmdInProgress = 0x1000, /* Command In Progress */
-
- /* TxStatus Bits */
- TxJabber = 0x20, /* Jabber Error */
- TxUnderrun = 0x10, /* Underrun */
- TxMaxColl = 0x08, /* Maximum Collisions */
-
- /* RxStatus Bits */
- RxByteMask = 0x07FF, /* RX Bytes (0-1514) */
- RxErrMask = 0x3800, /* Type of Error: */
- RxErrOverrun = 0x0000, /* Overrrun */
- RxErrOversize = 0x0800, /* Oversize Packet (>1514) */
- RxErrDribble = 0x1000, /* Dribble Bit(s) */
- RxErrRunt = 0x1800, /* Runt Packet */
- RxErrFraming = 0x2000, /* Alignment (Framing) */
- RxErrCRC = 0x2800, /* CRC */
- RxError = 0x4000, /* Error */
- RxEmpty = 0x8000, /* Incomplete or FIFO empty */
-
- InternalCgf = 0x00, /* window 3 */
-
- FIFOdiag = 0x04, /* window 4 */
- MediaStatus = 0x0A,
-
- /* FIFOdiag bits */
- TxOverrun = 0x0400, /* TX Overrrun */
- RxOverrun = 0x0800, /* RX Overrun */
- RxStatusOverrun = 0x1000, /* RX Status Overrun */
- RxUnderrun = 0x2000, /* RX Underrun */
- RxReceiving = 0x8000, /* RX Receiving */
-
- /* MediaStatus bits */
- JabberEna = 0x0040, /* Jabber Enabled (writeable) */
- LinkBeatEna = 0x0080, /* Link Beat Enabled (writeable) */
- LinkBeatOk = 0x0800, /* Valid link beat detected (ro) */
-};
-
-#define COMMAND(port, cmd, a) outs(port+Command, ((cmd)<<11)|(a))
-
-static void
-attach(Ether *ether)
-{
- ulong port;
- int x;
-
- port = ether->port;
- /*
- * Set the receiver packet filter for our own and
- * and broadcast addresses, set the interrupt masks
- * for all interrupts, and enable the receiver and transmitter.
- * The only interrupt we should see under normal conditions
- * is the receiver interrupt. If the transmit FIFO fills up,
- * we will also see TxAvailable interrupts.
- * Disable Update interrupts for now.
- */
- x = Broadcast|MyEtherAddr;
- if(ether->prom)
- x |= Promiscuous;
- COMMAND(port, SetRxFilter, x);
- COMMAND(port, SetReadZeroMask, (AllIntr|Latch) & ~Update);
- COMMAND(port, SetIntrMask, (AllIntr|Latch) & ~Update);
- COMMAND(port, RxEnable, 0);
- COMMAND(port, TxEnable, 0);
-}
-
-static void
-promiscuous(void *arg, int on)
-{
- ulong port;
-
- port = ((Ether*)arg)->port;
- if(on)
- COMMAND(port, SetRxFilter, Promiscuous|Broadcast|MyEtherAddr);
- else
- COMMAND(port, SetRxFilter, Broadcast|MyEtherAddr);
-}
-
-static void
-receive(Ether *ether)
-{
- ushort status;
- int len;
- ulong port;
-
- port = ether->port;
- while(((status = ins(port+RxStatus)) & RxEmpty) == 0){
- /*
- * If we had an error, log it and continue.
- */
- if(status & RxError){
- switch(status & RxErrMask){
-
- case RxErrOverrun: /* Overrrun */
- ether->overflows++;
- break;
-
- case RxErrOversize: /* Oversize Packet (>1514) */
- case RxErrRunt: /* Runt Packet */
- ether->buffs++;
- break;
- case RxErrFraming: /* Alignment (Framing) */
- ether->frames++;
- break;
-
- case RxErrCRC: /* CRC */
- ether->crcs++;
- break;
- }
- }
- else {
- /*
- * We have a packet. Read it into the
- * buffer. The CRC is already stripped off.
- * Must read len bytes padded to a
- * doubleword. We can pick them out 32-bits
- * at a time.
- */
- ether->inpackets++;
- len = (status & RxByteMask);
- insl(port+Fifo, ðer->rpkt, HOWMANY(len, 4));
-
- /*
- * Copy the packet to whoever wants it.
- */
- etherrloop(ether, ðer->rpkt, len);
- }
-
- /*
- * Discard the packet as we're done with it.
- * Wait for discard to complete.
- */
- COMMAND(port, RxDiscard, 0);
- while(ins(port+Status) & CmdInProgress)
- ;
- }
-}
-
-static int
-istxfifo(void *arg)
-{
- Ether *ether;
- ushort len;
- ulong port;
-
- ether = arg;
- port = ether->port;
- /*
- * If there's no room in the FIFO for this packet,
- * set up an interrupt for when space becomes available.
- * Output packet must be a multiple of 4 in length and
- * we need 4 bytes for the preamble.
- * Assume here that when we are called (via tsleep) that
- * we are safe from interrupts.
- */
- len = ROUNDUP(ether->tlen, 4);
- if(len+4 > ins(port+TxFreeBytes)){
- COMMAND(port, SetTxAvailable, len);
- return 0;
- }
- return 1;
-}
-
-static long
-write(Ether *ether, void *buf, long n)
-{
- ushort len;
- ulong port;
-
- port = ether->port;
-
- ether->tlen = n;
- len = ROUNDUP(ether->tlen, 4);
- tsleep(ðer->tr, istxfifo, ether, 10000);
- if(len+4 > ins(port+TxFreeBytes)){
- print("ether509: transmitter jammed\n");
- return 0;
- }
-
- /*
- * We know there's room, copy the packet to the FIFO.
- * To save copying the packet into a local buffer just
- * so we can set the source address, stuff the packet
- * into the FIFO in 3 pieces.
- * Transmission won't start until the entire packet is
- * in the FIFO, so it's OK to fault here.
- */
- outs(port+Fifo, ether->tlen);
- outs(port+Fifo, 0);
- outss(port+Fifo, buf, Eaddrlen/2);
- outss(port+Fifo, ether->ea, Eaddrlen/2);
- outsl(port+Fifo, (uchar*)buf+2*Eaddrlen, (len-2*Eaddrlen)/4);
- ether->outpackets++;
-
- return n;
-}
-
-static ushort
-getdiag(Ether *ether)
-{
- ushort bytes;
- ulong port;
-
- port = ether->port;
- COMMAND(port, SelectWindow, 4);
- bytes = ins(port+FIFOdiag);
- COMMAND(port, SelectWindow, 1);
- return bytes & 0xFFFF;
-}
-
-static void
-interrupt(Ureg *ur, void *arg)
-{
- ushort status, diag;
- uchar txstatus, x;
- ulong port;
- Ether *ether;
-
- USED(ur);
- ether = arg;
- port = ether->port;
-
- for(;;){
- /*
- * Clear the interrupt latch.
- * It's possible to receive a packet and for another
- * to become complete before we exit the interrupt
- * handler so this must be done first to ensure another
- * interrupt will occur.
- */
- COMMAND(port, AckIntr, Latch);
- status = ins(port+Status);
- if((status & AllIntr) == 0)
- break;
-
- if(status & Failure){
- /*
- * Adapter failure, try to find out why.
- * Reset if necessary.
- * What happens if Tx is active and we reset,
- * need to retransmit?
- * This probably isn't right.
- */
- diag = getdiag(ether);
- print("ether509: status #%ux, diag #%ux\n", status, diag);
-
- if(diag & TxOverrun){
- COMMAND(port, TxReset, 0);
- COMMAND(port, TxEnable, 0);
- wakeup(ðer->tr);
- }
-
- if(diag & RxUnderrun){
- COMMAND(port, RxReset, 0);
- attach(ether);
- }
-
- return;
- }
-
- if(status & RxComplete){
- receive(ether);
- status &= ~RxComplete;
- }
-
- if(status & TxComplete){
- /*
- * Pop the TX Status stack, accumulating errors.
- * If there was a Jabber or Underrun error, reset
- * the transmitter. For all conditions enable
- * the transmitter.
- */
- txstatus = 0;
- do{
- if(x = inb(port+TxStatus))
- outb(port+TxStatus, 0);
- txstatus |= x;
- }while(ins(port+Status) & TxComplete);
-
- if(txstatus & (TxJabber|TxUnderrun))
- COMMAND(port, TxReset, 0);
- COMMAND(port, TxEnable, 0);
- ether->oerrs++;
- }
-
- if(status & (TxAvailable|TxComplete)){
- /*
- * Reset the Tx FIFO threshold.
- */
- if(status & TxAvailable){
- COMMAND(port, AckIntr, TxAvailable);
- wakeup(ðer->tr);
- }
- status &= ~(TxAvailable|TxComplete);
- }
-
- /*
- * Panic if there are any interrupt bits on we haven't
- * dealt with. Should deal with UP (Update Statistics)
- * for happier coexistence with Windows drivers.
- */
- if(status & AllIntr)
- panic("ether509 interrupt: #%lux, #%ux\n", status, getdiag(ether));
- }
-}
-
-typedef struct Adapter Adapter;
-struct Adapter {
- Adapter* next;
- ulong port;
- PCIcfg* pcicfg;
-};
-static Adapter *adapter;
-
-/*
- * Write two 0 bytes to identify the IDport and then reset the
- * ID sequence. Then send the ID sequence to the card to get
- * the card into command state.
- */
-static void
-idseq(void)
-{
- int i;
- uchar al;
- static int reset, untag;
-
- /*
- * One time only:
- * reset any adapters listening
- */
- if(reset == 0){
- outb(IDport, 0);
- outb(IDport, 0);
- outb(IDport, 0xC0);
- delay(20);
- reset = 1;
- }
-
- outb(IDport, 0);
- outb(IDport, 0);
- for(al = 0xFF, i = 0; i < 255; i++){
- outb(IDport, al);
- if(al & 0x80){
- al <<= 1;
- al ^= 0xCF;
- }
- else
- al <<= 1;
- }
-
- /*
- * One time only:
- * write ID sequence to get the attention of all adapters;
- * untag all adapters.
- * If we do a global reset here on all adapters we'll confuse any
- * ISA cards configured for EISA mode.
- */
- if(untag == 0){
- outb(IDport, 0xD0);
- untag = 1;
- }
-}
-
-static ulong
-activate(void)
-{
- int i;
- ushort x, acr;
-
- /*
- * Do the little configuration dance:
- *
- * 2. write the ID sequence to get to command state.
- */
- idseq();
-
- /*
- * 3. Read the Manufacturer ID from the EEPROM.
- * This is done by writing the IDPort with 0x87 (0x80
- * is the 'read EEPROM' command, 0x07 is the offset of
- * the Manufacturer ID field in the EEPROM).
- * The data comes back 1 bit at a time.
- * We seem to need a delay here between reading the bits.
- *
- * If the ID doesn't match, there are no more adapters.
- */
- outb(IDport, 0x87);
- delay(20);
- for(x = 0, i = 0; i < 16; i++){
- delay(20);
- x <<= 1;
- x |= inb(IDport) & 0x01;
- }
- if(x != 0x6D50)
- return 0;
-
- /*
- * 3. Read the Address Configuration from the EEPROM.
- * The Address Configuration field is at offset 0x08 in the EEPROM).
- */
- outb(IDport, 0x88);
- for(acr = 0, i = 0; i < 16; i++){
- delay(20);
- acr <<= 1;
- acr |= inb(IDport) & 0x01;
- }
-
- return (acr & 0x1F)*0x10 + 0x200;
-}
-
-static ulong
-tcm509(Ether *ether)
-{
- ulong port;
- Adapter *ap;
-
- /*
- * Attempt to activate adapters until one matches the
- * address criteria. If adapter is set for EISA mode (0x3F0),
- * tag it and ignore. Otherwise, activate it fully.
- */
- while(port = activate()){
- /*
- * 6. Tag the adapter so it won't respond in future.
- */
- outb(IDport, 0xD1);
- if(port == 0x3F0)
- continue;
-
- /*
- * 6. Activate the adapter by writing the Activate command
- * (0xFF).
- */
- outb(IDport, 0xFF);
- delay(20);
-
- /*
- * 8. Can now talk to the adapter's I/O base addresses.
- * Use the I/O base address from the acr just read.
- *
- * Enable the adapter.
- */
- while(ins(port+Status) & CmdInProgress)
- ;
- COMMAND(port, SelectWindow, 0);
- outs(port+ConfigControl, Ena);
-
- COMMAND(port, TxReset, 0);
- COMMAND(port, RxReset, 0);
- COMMAND(port, AckIntr, 0xFF);
-
- if(ether->port == 0 || ether->port == port)
- return port;
-
- ap = malloc(sizeof(Adapter));
- ap->port = port;
- ap->next = adapter;
- adapter = ap;
- }
-
- return 0;
-}
-
-static ulong
-tcm579(Ether *ether)
-{
- static int slot = 1;
- ushort x;
- ulong port;
- Adapter *ap;
-
- /*
- * First time through, check if this is an EISA machine.
- * If not, nothing to do.
- */
- if(slot == 1 && strncmp((char*)(KZERO|0xFFFD9), "EISA", 4))
- return 0;
-
- /*
- * Continue through the EISA slots looking for a match on both
- * 3COM as the manufacturer and 3C579 or 3C579-TP as the product.
- * If we find an adapter, select window 0, enable it and clear
- * out any lingering status and interrupts.
- * Trying to do a GlobalReset here to re-init the card (as in the
- * 509 code) doesn't seem to work.
- */
- while(slot < MaxEISA){
- port = slot++*0x1000;
- if(ins(port+0xC80+ManufacturerID) != 0x6D50)
- continue;
- x = ins(port+0xC80+ProductID);
- if((x & 0xF0FF) != 0x9050/* || (x != 0x9350 && x != 0x9250)*/)
- continue;
-
- COMMAND(port, SelectWindow, 0);
- outs(port+ConfigControl, Ena);
-
- COMMAND(port, TxReset, 0);
- COMMAND(port, RxReset, 0);
- COMMAND(port, AckIntr, 0xFF);
-
- if(ether->port == 0 || ether->port == port)
- return port;
-
- ap = malloc(sizeof(Adapter));
- ap->port = port;
- ap->next = adapter;
- adapter = ap;
- }
-
- return 0;
-}
-
-static PCIcfg*
-tcm590(Ether *ether)
-{
- PCIcfg* pcicfg;
- static int devno = 0;
- int port;
- Adapter *ap;
-
- pcicfg = malloc(sizeof(PCIcfg));
- for(;;){
- pcicfg->vid = 0x10B7;
- pcicfg->did = 0;
- if((devno = pcimatch(0, devno, pcicfg)) == -1)
- break;
-
- port = pcicfg->baseaddr[0] & ~0x01;
- COMMAND(port, GlobalReset, 0);
- while(ins(port+Status) & CmdInProgress)
- ;
-
- if(ether->port == 0 || ether->port == port)
- return pcicfg;
-
- ap = malloc(sizeof(Adapter));
- ap->pcicfg = pcicfg;
- ap->port = port;
- ap->next = adapter;
- adapter = ap;
-
- pcicfg = malloc(sizeof(PCIcfg));
- }
- free(pcicfg);
-
- return 0;
-}
-
-static ulong
-tcm589(ISAConf *isa)
-{
- if(strcmp(isa->type, "3C589") != 0)
- return 0;
-
- /*
- * The 3com manual register description says that this a noop for
- * PCMCIA but the flow chart at the end shows it.
- */
- COMMAND(isa->port, SelectWindow, 0);
- outs(isa->port+ConfigControl, Ena);
- return isa->port;
-}
-
-/*
- * Get configuration parameters.
- */
-int
-ether509reset(Ether *ether)
-{
- int i, eax;
- uchar ea[Eaddrlen];
- ushort x, acr;
- ulong l, port;
- Adapter *ap, **app;
- PCIcfg *pcicfg;
-
- /*
- * Any adapter matches if no ether->port is supplied,
- * otherwise the ports must match.
- * See if we've already found an adapter that fits
- * the bill.
- * If no match then try for an EISA card, an ISA card
- * and finally for a PCMCIA card.
- */
- port = 0;
- pcicfg = 0;
- for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){
- if(ether->port == 0 || ether->port == ap->port){
- port = ap->port;
- pcicfg = ap->pcicfg;
- *app = ap->next;
- free(ap);
- break;
- }
- }
- if(port == 0)
- port = tcm589(ether);
- if(port == 0 && (pcicfg = tcm590(ether)))
- port = pcicfg->baseaddr[0] & ~0x01;
- if(port == 0)
- port = tcm579(ether);
- if(port == 0)
- port = tcm509(ether);
- if(port == 0)
- return -1;
-
- /*
- * Read the IRQ from the Resource Configuration Register,
- * the ethernet address from the EEPROM, and the address configuration.
- * The EEPROM command is 8 bits, the lower 6 bits being
- * the address offset.
- */
- COMMAND(port, SelectWindow, 0);
- if(strcmp(ether->type, "3C589") != 0 && pcicfg == 0)
- ether->irq = (ins(port+ResourceConfig)>>12) & 0x0F;
- for(eax = 0, i = 0; i < 3; i++, eax += 2){
- while(ins(port+EEPROMcmd) & 0x8000)
- ;
- outs(port+EEPROMcmd, (2<<6)|i);
- while(ins(port+EEPROMcmd) & 0x8000)
- ;
- x = ins(port+EEPROMdata);
- ea[eax] = (x>>8) & 0xFF;
- ea[eax+1] = x & 0xFF;
- }
- if(pcicfg){
- ether->irq = pcicfg->irq;
- COMMAND(port, SelectWindow, 3);
- acr = XcvrAUI;
- l = inl(port+InternalCgf);
- switch(l & 0x700000){
-
- case 0x000000:
- acr = Xcvr10BaseT;
- break;
-
- case 0x300000:
- acr = XcvrBNC;
- break;
- }
- free(pcicfg);
- }
- else
- acr = ins(port+AddressConfig);
-
- /*
- * Finished with window 0. Now set the ethernet address
- * in window 2.
- * Commands have the format 'CCCCCAAAAAAAAAAA' where C
- * is a bit in the command and A is a bit in the argument.
- */
- COMMAND(port, SelectWindow, 2);
- if((ether->ea[0]|ether->ea[1]|ether->ea[2]|ether->ea[3]|ether->ea[4]|ether->ea[5]) == 0){
- for(i = 0; i < sizeof(ether->ea); i++)
- ether->ea[i] = ea[i];
- }
- for(i = 0; i < Eaddrlen; i++)
- outb(port+i, ether->ea[i]);
-
- /*
- * Enable the transceiver if necessary.
- */
- COMMAND(port, SelectWindow, 4);
- x = ins(port+MediaStatus) & ~(LinkBeatEna|JabberEna);
- outs(port+MediaStatus, x);
- switch(acr & XcvrTypeMask){
-
- case Xcvr10BaseT:
- /*
- * Enable Link Beat and Jabber to start the
- * transceiver.
- */
- outs(port+MediaStatus, x|LinkBeatEna|JabberEna);
- break;
-
- case XcvrBNC:
- /*
- * Start the DC-DC converter.
- * Wait > 800 microseconds.
- */
- COMMAND(port, StartCoax, 0);
- delay(1);
- break;
- }
-
- /*
- * Set window 1 for normal operation.
- * Clear out any lingering Tx status.
- */
- COMMAND(port, SelectWindow, 1);
- while(inb(port+TxStatus))
- outb(port+TxStatus, 0);
-
- ether->port = port;
-
- /*
- * Set up the software configuration.
- */
- ether->attach = attach;
- ether->write = write;
- ether->interrupt = interrupt;
-
- ether->promiscuous = promiscuous;
- ether->arg = ether;
-
- return 0;
-}
-
-void
-ether509link(void)
-{
- addethercard("3C509", ether509reset);
-}
M pc/ether589.c => pc/ether589.c +8 -16
@@ 40,7 40,7 @@ enum {
#define COMMAND(port, cmd, a) outs(port+Command, ((cmd)<<11)|(a))
-extern int ether509reset(Ether*);
+extern int etherelnk3reset(Ether*);
static int
configASIC(Ether *ether, int port, int xcvr)
@@ 50,25 50,17 @@ configASIC(Ether *ether, int port, int xcvr)
/* set Window 0 configuration registers */
COMMAND(port, SelectWindow, 0);
+ x = ins(port+ConfigControl);
+ outs(port+ConfigControl, x|1);
+
/* ROM size & base - must be set before we can access ROM */
/* transceiver type is 2 for 'figure it out' */
- x = ins(port + AddressConfig);
- outs(port + AddressConfig, (x & 0xf0) | xcvr);
+ outs(port + AddressConfig, xcvr);
/* IRQ must be 3 on 3C589 */
- x = ins(port + ResourceConfig);
- outs(port + ResourceConfig, 0x3f00 | (x&0xff));
-
- /* move product ID to register */
- while(ins(port+EEPROMcmd) & 0x8000)
- ;
- outs(port+EEPROMcmd, (2<<6)|3);
- while(ins(port+EEPROMcmd) & 0x8000)
- ;
- x = ins(port+EEPROMdata);
- outs(port + ProductID, x);
-
- return ether509reset(ether);
+ outs(port + ResourceConfig, 0x3F00);
+
+ return etherelnk3reset(ether);
}
static int
M pc/ether79c970.c => pc/ether79c970.c +30 -38
@@ 2,6 2,8 @@
* AM79C970
* PCnet-PCI Single-Chip Ethernet Controller for PCI Local Bus
* To do:
+ * bag the tsleep in write, buffer them up;
+ * loop in interrupt routine until all done;
* only issue transmit interrupt if necessary?
* dynamically increase rings as necessary?
* use Block's as receive buffers?
@@ 236,9 238,9 @@ write(Ether* ether, void* buf, long n)
return 0;
/*
- * Copy the packet to the transmit buffer and fill in our
+ * Copy the packet to the transmit buffer and fill in the
* source ethernet address. There's no need to pad to ETHERMINTU
- * here as we set ApadXmit in CSR4.
+ * here as ApadXmit is set in CSR4.
*/
pkt = KADDR(tdre->tbadr);
memmove(pkt->d, buf, n);
@@ 324,38 326,37 @@ typedef struct Adapter Adapter;
struct Adapter {
Adapter* next;
int port;
- PCIcfg* pcicfg;
+ int irq;
};
static Adapter *adapter;
-static PCIcfg*
+static int
amd79c90(Ether* ether)
{
- PCIcfg *pcicfg;
+ PCIcfg pcicfg;
static int devno = 0;
- int port;
+ int irq, port;
Adapter *ap;
- pcicfg = malloc(sizeof(PCIcfg));
for(;;){
- pcicfg->vid = 0x1022;
- pcicfg->did = 0x2000;
- if((devno = pcimatch(0, devno, pcicfg)) == -1)
+ pcicfg.vid = 0x1022;
+ pcicfg.did = 0x2000;
+ if((devno = pcimatch(0, devno, &pcicfg)) == -1)
break;
- port = pcicfg->baseaddr[0] & ~0x01;
- if(ether->port == 0 || ether->port == port)
- return pcicfg;
+ port = pcicfg.baseaddr[0] & ~0x01;
+ irq = pcicfg.irq;
+ if(ether->port == 0 || ether->port == port){
+ ether->irq = irq;
+ return port;
+ }
ap = malloc(sizeof(Adapter));
- ap->pcicfg = pcicfg;
ap->port = port;
+ ap->irq = irq;
ap->next = adapter;
adapter = ap;
-
- pcicfg = malloc(sizeof(PCIcfg));
}
- free(pcicfg);
return 0;
}
@@ 364,7 365,6 @@ static int
reset(Ether* ether)
{
int port, x;
- PCIcfg *pcicfg;
Adapter *ap, **app;
uchar ea[Eaddrlen];
Ctlr *ctlr;
@@ 375,37 375,29 @@ reset(Ether* ether)
* the bill. If not then scan for another.
*/
port = 0;
- pcicfg = 0;
for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){
if(ether->port == 0 || ether->port == ap->port){
port = ap->port;
- pcicfg = ap->pcicfg;
+ ether->irq = ap->irq;
*app = ap->next;
free(ap);
break;
}
}
- if(port == 0 && (pcicfg = amd79c90(ether))){
- port = pcicfg->baseaddr[0] & ~0x01;
- ether->irq = pcicfg->irq;
- }
- if(port == 0)
+ if(port == 0 && (port = amd79c90(ether)) == 0)
return -1;
- if(pcicfg)
- free(pcicfg);
-
/*
- * How can we tell what mode we're in at this point - if we're in WORD
- * mode then the only 32-bit access we are allowed to make is a write to
- * the RDP, which forces the chip to DWORD mode; if we're in DWORD mode
- * then we're not allowed to make any non-DWORD accesses?
- * Assuming we do a DWORD write to the RDP, how can we tell what we're
- * about to overwrite as we can't reliably access the RAP?
+ * How to tell what mode the chip is in at this point - if it's in WORD
+ * mode then the only 32-bit access allowedis a write to the RDP, which
+ * forces the chip to DWORD mode; if it's in DWORD mode then only DWORD
+ * accesses are allowed?
+ * Assuming a DWORD write is done to the RDP, what will be overwritten as
+ * the RAP can't reliably be accessed?
*
* Force DWORD mode by writing to RDP, doing a reset then writing to RDP
- * again; at least we know what state we're in now. The value of RAP after
- * a reset is 0, so the second DWORD write will be to CSR0.
+ * again. The value of RAP after a reset is 0, so the second DWORD write
+ * will be to CSR0.
* Set the software style in BCR20 to be PCnet-PCI to ensure 32-bit access.
* Set the auto pad transmit in CSR4.
*/
@@ 423,7 415,7 @@ reset(Ether* ether)
outl(port+Rap, 0);
/*
- * Check if we are going to override the adapter's station address.
+ * Check if the adapter's station address is to be over-ridden.
* If not, read it from the I/O-space and set in ether->ea prior to loading the
* station address in the initialisation block.
*/
@@ 457,7 449,7 @@ reset(Ether* ether)
/*
* Point the chip at the initialisation block and tell it to go.
* Mask the Idon interrupt and poll for completion. Strt and interrupt
- * enables will be set later when we're ready to attach to the network.
+ * enables will be set later when attaching to the network.
*/
x = PADDR(&ctlr->iblock);
outl(port+Rap, 1);
M pc/ether82557.c => pc/ether82557.c +116 -34
@@ 6,9 6,7 @@
* the PCI scanning code could be made common to other adapters;
* PCI code needs rewritten to handle byte, word, dword accesses
* and using the devno as a bus+dev+function triplet;
- * tidy/fix locking;
- * optionally use memory-mapped registers;
- * stats.
+ * optionally use memory-mapped registers.
*/
#include "u.h"
#include "../port/lib.h"
@@ 141,7 139,7 @@ enum { /* count */
typedef struct Cb {
int command;
ulong link;
- uchar data[24]; /* CbIAS + CbConfigure */
+ uchar data[24]; /* CbIAS + CbConfigure */
} Cb;
typedef struct TxCB {
@@ 185,16 183,18 @@ typedef struct Ctlr {
uchar configdata[24];
- Lock rlock;
+ Lock rlock; /* registers */
- Block* rfd[Nrfd];
+ Block* rfd[Nrfd]; /* receive side */
int rfdl;
int rfdx;
- Lock cbqlock;
- Block* cbqhead;
+ Block* cbqhead; /* transmit side */
Block* cbqtail;
int cbqbusy;
+
+ Lock dlock; /* dump statistical counters */
+ ulong dump[17];
} Ctlr;
static uchar configdata[24] = {
@@ 239,9 239,9 @@ custart(Ctlr* ctlr)
}
ctlr->cbqbusy = 1;
- csr32w(ctlr, General, PADDR(ctlr->cbqhead->rp));
while(csr8r(ctlr, Command))
;
+ csr32w(ctlr, General, PADDR(ctlr->cbqhead->rp));
csr8w(ctlr, Command, CUstart);
}
@@ 250,7 250,6 @@ action(Ctlr* ctlr, Block* bp)
{
Cb *cb;
- ilock(&ctlr->cbqlock);
cb = (Cb*)bp->rp;
cb->command |= CbEL;
@@ 266,8 265,6 @@ action(Ctlr* ctlr, Block* bp)
if(ctlr->cbqbusy == 0)
custart(ctlr);
-
- iunlock(&ctlr->cbqlock);
}
static void
@@ 280,23 277,20 @@ attach(Ether* ether)
ilock(&ctlr->rlock);
status = csr16r(ctlr, Status);
if((status & RUstatus) == RUidle){
- csr32w(ctlr, General, PADDR(ctlr->rfd[ctlr->rfdx]->rp));
while(csr8r(ctlr, Command))
;
+ csr32w(ctlr, General, PADDR(ctlr->rfd[ctlr->rfdx]->rp));
csr8w(ctlr, Command, RUstart);
}
iunlock(&ctlr->rlock);
}
-static void
-configure(void* arg, int promiscuous)
+static Block*
+configure(Ctlr* ctlr, int promiscuous)
{
- Ctlr *ctlr;
Block *bp;
Cb *cb;
- ctlr = ((Ether*)arg)->ctlr;
-
bp = allocb(sizeof(Cb));
cb = (Cb*)bp->rp;
bp->wp += sizeof(Cb);
@@ 306,12 300,84 @@ configure(void* arg, int promiscuous)
memmove(cb->data, ctlr->configdata, sizeof(ctlr->configdata));
if(promiscuous)
cb->data[15] |= 0x01;
+
+ return bp;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+ Ctlr *ctlr;
+ Block *bp;
+
+ ctlr = ((Ether*)arg)->ctlr;
+ bp = configure(ctlr, on);
+
+ ilock(&ctlr->rlock);
action(ctlr, bp);
+ iunlock(&ctlr->rlock);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ Ctlr *ctlr;
+ char buf[512];
+ int len;
+
+ ctlr = ether->ctlr;
+ lock(&ctlr->dlock);
+ ctlr->dump[16] = 0;
+
+ ilock(&ctlr->rlock);
+ while(csr8r(ctlr, Command))
+ ;
+ csr8w(ctlr, Command, DumpSC);
+ iunlock(&ctlr->rlock);
+
+ /*
+ * Wait for completion status, should be 0xA005.
+ */
+ while(ctlr->dump[16] == 0)
+ ;
+
+ ether->oerrs = ctlr->dump[1]+ctlr->dump[2]+ctlr->dump[3];
+ ether->crcs = ctlr->dump[10];
+ ether->frames = ctlr->dump[11];
+ ether->buffs = ctlr->dump[12]+ctlr->dump[15];
+ ether->overflows = ctlr->dump[13];
+
+ if(n == 0){
+ unlock(&ctlr->dlock);
+ return 0;
+ }
+
+ len = sprint(buf, "transmit good frames: %ld\n", ctlr->dump[0]);
+ len += sprint(buf+len, "transmit maximum collisions errors: %ld\n", ctlr->dump[1]);
+ len += sprint(buf+len, "transmit late collisions errors: %ld\n", ctlr->dump[2]);
+ len += sprint(buf+len, "transmit underrun errors: %ld\n", ctlr->dump[3]);
+ len += sprint(buf+len, "transmit lost carrier sense: %ld\n", ctlr->dump[4]);
+ len += sprint(buf+len, "transmit deferred: %ld\n", ctlr->dump[5]);
+ len += sprint(buf+len, "transmit single collisions: %ld\n", ctlr->dump[6]);
+ len += sprint(buf+len, "transmit multiple collisions: %ld\n", ctlr->dump[7]);
+ len += sprint(buf+len, "transmit total collisions: %ld\n", ctlr->dump[8]);
+ len += sprint(buf+len, "receive good frames: %ld\n", ctlr->dump[9]);
+ len += sprint(buf+len, "receive CRC errors: %ld\n", ctlr->dump[10]);
+ len += sprint(buf+len, "receive alignment errors: %ld\n", ctlr->dump[11]);
+ len += sprint(buf+len, "receive resource errors: %ld\n", ctlr->dump[12]);
+ len += sprint(buf+len, "receive overrun errors: %ld\n", ctlr->dump[13]);
+ len += sprint(buf+len, "receive collision detect errors: %ld\n", ctlr->dump[14]);
+ sprint(buf+len, "receive short frame errors: %ld\n", ctlr->dump[15]);
+
+ unlock(&ctlr->dlock);
+
+ return readstr(offset, a, n, buf);
}
static long
write(Ether* ether, void* buf, long n)
{
+ Ctlr *ctlr;
Block *bp;
TxCB *txcb;
@@ 330,7 396,10 @@ write(Ether* ether, void* buf, long n)
memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
bp->wp += n;
- action(ether->ctlr, bp);
+ ctlr = ether->ctlr;
+ ilock(&ctlr->rlock);
+ action(ctlr, bp);
+ iunlock(&ctlr->rlock);
ether->outpackets++;
@@ 349,19 418,23 @@ interrupt(Ureg*, void* arg)
ether = arg;
ctlr = ether->ctlr;
+ ilock(&ctlr->rlock);
for(;;){
status = csr16r(ctlr, Status);
csr8w(ctlr, Ack, (status>>8) & 0xFF);
- if((status & (StatCX|StatFR|StatCNA|StatRNR)) == 0)
- return;
+ if((status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI)) == 0)
+ break;
if(status & StatFR){
bp = ctlr->rfd[ctlr->rfdx];
rfd = (Rfd*)bp->rp;
while(rfd->field & RfdC){
- etherrloop(ether, rfd, rfd->count & 0x3FFF);
- ether->inpackets++;
+ if(rfd->field & RfdOK){
+ etherrloop(ether, rfd, rfd->count & 0x3FFF);
+ ether->inpackets++;
+ }
+else print("%s#%d: rfd->field %uX\n", ctlr->type, ctlr->ctlrno, rfd->field);
/*
* Reinitialise the frame for reception and bump
@@ 390,12 463,12 @@ interrupt(Ureg*, void* arg)
while(csr8r(ctlr, Command))
;
csr8w(ctlr, Command, RUresume);
+print("%s#%d: status %uX\n", ctlr->type, ctlr->ctlrno, status);
status &= ~StatRNR;
}
if(status & StatCNA){
- lock(&ctlr->cbqlock);
while(bp = ctlr->cbqhead){
if((((Cb*)bp->rp)->command & CbC) == 0)
break;
@@ 403,7 476,6 @@ interrupt(Ureg*, void* arg)
freeb(bp);
}
custart(ctlr);
- unlock(&ctlr->cbqlock);
status &= ~StatCNA;
}
@@ 411,6 483,7 @@ interrupt(Ureg*, void* arg)
if(status & (StatCX|StatFR|StatCNA|StatRNR|StatMDI|StatSWI))
panic("%s#%d: status %uX\n", ctlr->type, ctlr->ctlrno, status);
}
+ iunlock(&ctlr->rlock);
}
static void
@@ 612,21 685,26 @@ reset(Ether* ether)
ctlr->type = ether->type;
ctlr->port = port;
+ ilock(&ctlr->rlock);
+
csr32w(ctlr, Port, 0);
delay(1);
- pcicfgr(0, pcidevno, 0, 0x04, &x, 4);
- if((x & 0x05) != 0x05)
- print("PCI command = %uX\n", x);
-
- csr32w(ctlr, General, 0);
while(csr8r(ctlr, Command))
;
+ csr32w(ctlr, General, 0);
csr8w(ctlr, Command, LoadRUB);
+
while(csr8r(ctlr, Command))
;
csr8w(ctlr, Command, LoadCUB);
+ while(csr8r(ctlr, Command))
+ ;
+ csr32w(ctlr, General, PADDR(ctlr->dump));
+ csr8w(ctlr, Command, LoadDCA);
+
+
/*
* Initialise the receive frame and configuration areas.
*/
@@ 638,8 716,8 @@ reset(Ether* ether)
* configuration. However, should check for the existence of the PHY
* and, if found, check whether to use 82503 (serial) or MII (nibble)
* mode. Verify the PHY is a National Semiconductor DP83840 by looking
- * at the Organizationally Unique Identifier (OUI) in registers 2 and 3
- * which should be 0x80017.
+ * at the Organizationally Unique Identifier (OUI) in registers 2 and
+ * 3 which should be 0x80017.
*/
for(i = 1; i < 32; i++){
if((x = dp83840r(ctlr, i, 2)) == 0xFFFF)
@@ 661,7 739,8 @@ reset(Ether* ether)
/*
* Load the chip configuration
*/
- configure(ether, 0);
+ bp = configure(ctlr, 0);
+ action(ctlr, bp);
/*
* Check if the adapter's station address is to be overridden.
@@ 686,6 765,8 @@ reset(Ether* ether)
memmove(cb->data, ether->ea, Eaddrlen);
action(ctlr, bp);
+ iunlock(&ctlr->rlock);
+
/*
* Linkage to the generic ethernet driver.
*/
@@ 693,8 774,9 @@ reset(Ether* ether)
ether->attach = attach;
ether->write = write;
ether->interrupt = interrupt;
+ ether->ifstat = ifstat;
- ether->promiscuous = configure;
+ ether->promiscuous = promiscuous;
ether->arg = ether;
return 0;
A pc/etherelnk3.c => pc/etherelnk3.c +1235 -0
@@ 0,0 1,1235 @@
+/*
+ * Etherlink III and Fast EtherLink adapters.
+ * To do:
+ * check robustness in the face of errors;
+ * autoSelect;
+ * PCI latency timer and master enable;
+ * errata list.
+ *
+ * Product ID:
+ * 9150 ISA 3C509[B]
+ * 9050 ISA 3C509[B]-TP
+ * 9450 ISA 3C509[B]-COMBO
+ * 9550 ISA 3C509[B]-TPO
+ *
+ * 9350 EISA 3C579
+ * 9250 EISA 3C579-TP
+ *
+ * 5920 EISA 3C592-[TP|COMBO|TPO]
+ * 5970 EISA 3C597-TX Fast Etherlink 10BASE-T/100BASE-TX
+ * 5971 EISA 3C597-T4 Fast Etherlink 10BASE-T/100BASE-T4
+ * 5972 EISA 3C597-MII Fast Etherlink 10BASE-T/MII
+ *
+ * 5900 PCI 3C590-[TP|COMBO|TPO]
+ * 5950 PCI 3C595-TX Fast Etherlink Shared 10BASE-T/100BASE-TX
+ * 5951 PCI 3C595-T4 Fast Etherlink Shared 10BASE-T/100BASE-T4
+ * 5952 PCI 3C595-MII Fast Etherlink 10BASE-T/MII
+ *
+ * 9058 PCMCIA 3C589[B]-[TP|COMBO]
+ *
+ * 627C MCA 3C529
+ * 627D MCA 3C529-TP
+ */
+#include "u.h"
+#include "../port/lib.h"
+#include "mem.h"
+#include "dat.h"
+#include "fns.h"
+#include "io.h"
+#include "../port/error.h"
+#include "../port/netif.h"
+
+#include "etherif.h"
+
+enum {
+ IDport = 0x0110, /* anywhere between 0x0100 and 0x01F0 */
+};
+
+enum { /* all windows */
+ Command = 0x000E,
+ IntStatus = 0x000E,
+};
+
+enum { /* Commands */
+ GlobalReset = 0x0000,
+ SelectRegisterWindow = 0x0001,
+ EnableDcConverter = 0x0002,
+ RxDisable = 0x0003,
+ RxEnable = 0x0004,
+ RxReset = 0x0005,
+ TxDone = 0x0007,
+ RxDiscard = 0x0008,
+ TxEnable = 0x0009,
+ TxDisable = 0x000A,
+ TxReset = 0x000B,
+ RequestInterrupt = 0x000C,
+ AcknowledgeInterrupt = 0x000D,
+ SetInterruptEnable = 0x000E,
+ SetIndicationEnable = 0x000F, /* SetReadZeroMask */
+ SetRxFilter = 0x0010,
+ SetRxEarlyThresh = 0x0011,
+ SetTxAvailableThresh = 0x0012,
+ SetTxStartThresh = 0x0013,
+ StartDma = 0x0014, /* initiate busmaster operation */
+ StatisticsEnable = 0x0015,
+ StatisticsDisable = 0x0016,
+ DisableDcConverter = 0x0017,
+ SetTxReclaimThresh = 0x0018, /* PIO-only adapters */
+ PowerUp = 0x001B, /* not all adapters */
+ PowerDownFull = 0x001C, /* not all adapters */
+ PowerAuto = 0x001D, /* not all adapters */
+};
+
+enum { /* (Global|Rx|Tx)Reset command bits */
+ tpAuiReset = 0x0001, /* 10BaseT and AUI transceivers */
+ endecReset = 0x0002, /* internal Ethernet encoder/decoder */
+ networkReset = 0x0004, /* network interface logic */
+ fifoReset = 0x0008, /* FIFO control logic */
+ aismReset = 0x0010, /* autoinitialise state-machine logic */
+ hostReset = 0x0020, /* bus interface logic */
+ dmaReset = 0x0040, /* bus master logic */
+ vcoReset = 0x0080, /* on-board 10Mbps VCO */
+
+ resetMask = 0x00FF,
+};
+
+enum { /* SetRxFilter command bits */
+ receiveIndividual = 0x0001, /* match station address */
+ receiveMulticast = 0x0002,
+ receiveBroadcast = 0x0004,
+ receiveAllFrames = 0x0008, /* promiscuous */
+};
+
+enum { /* StartDma command bits */
+ Upload = 0x0000, /* transfer data from adapter to memory */
+ Download = 0x0001, /* transfer data from memory to adapter */
+};
+
+enum { /* IntStatus bits */
+ interruptLatch = 0x0001,
+ hostError = 0x0002, /* Adapter Failure */
+ txComplete = 0x0004,
+ txAvailable = 0x0008,
+ rxComplete = 0x0010,
+ rxEarly = 0x0020,
+ intRequested = 0x0040,
+ updateStats = 0x0080,
+ transferInt = 0x0100, /* Bus Master Transfer Complete */
+ busMasterInProgress = 0x0800,
+ commandInProgress = 0x1000,
+
+ interruptMask = 0x01FE,
+};
+
+#define COMMAND(port, cmd, a) outs((port)+Command, ((cmd)<<11)|(a))
+#define STATUS(port) ins((port)+IntStatus)
+
+enum { /* Window 0 - setup */
+ Wsetup = 0x0000,
+ /* registers */
+ ManufacturerID = 0x0000, /* 3C5[08]*, 3C59[27] */
+ ProductID = 0x0002, /* 3C5[08]*, 3C59[27] */
+ ConfigControl = 0x0004, /* 3C5[08]*, 3C59[27] */
+ AddressConfig = 0x0006, /* 3C5[08]*, 3C59[27] */
+ ResourceConfig = 0x0008, /* 3C5[08]*, 3C59[27] */
+ EepromCommand = 0x000A,
+ EepromData = 0x000C,
+ /* AddressConfig Bits */
+ xcvrMask9 = 0xC000,
+ /* ConfigControl bits */
+ Ena = 0x0001,
+ /* EepromCommand bits */
+ EepromReadRegister = 0x0080,
+ EepromBusy = 0x8000,
+};
+
+#define EEPROMCMD(port, cmd, a) outs((port)+EepromCommand, (cmd)|(a))
+#define EEPROMBUSY(port) (ins((port)+EepromCommand) & EepromBusy)
+#define EEPROMDATA(port) ins((port)+EepromData)
+
+enum { /* Window 1 - operating set */
+ Wop = 0x0001,
+ /* registers */
+ Fifo = 0x0000,
+ RxError = 0x0004, /* 3C59[0257] only */
+ RxStatus = 0x0008,
+ Timer = 0x000A,
+ TxStatus = 0x000B,
+ TxFree = 0x000C,
+ /* RxError bits */
+ rxOverrun = 0x0001,
+ runtFrame = 0x0002,
+ alignmentError = 0x0004, /* Framing */
+ crcError = 0x0008,
+ oversizedFrame = 0x0010,
+ dribbleBits = 0x0080,
+ /* RxStatus bits */
+ rxBytes = 0x1FFF, /* 3C59[0257] mask */
+ rxBytes9 = 0x07FF, /* 3C5[078]9 mask */
+ rxError9 = 0x3800, /* 3C5[078]9 error mask */
+ rxOverrun9 = 0x0000,
+ oversizedFrame9 = 0x0800,
+ dribbleBits9 = 0x1000,
+ runtFrame9 = 0x1800,
+ alignmentError9 = 0x2000, /* Framing */
+ crcError9 = 0x2800,
+ rxError = 0x4000,
+ rxIncomplete = 0x8000,
+ /* TxStatus Bits */
+ txStatusOverflow = 0x0004,
+ maxCollisions = 0x0008,
+ txUnderrun = 0x0010,
+ txJabber = 0x0020,
+ interruptRequested = 0x0040,
+ txStatusComplete = 0x0080,
+};
+
+enum { /* Window 2 - station address */
+ Wstation = 0x0002,
+};
+
+enum { /* Window 3 - FIFO management */
+ Wfifo = 0x0003,
+ /* registers */
+ InternalConfig = 0x0000, /* 3C509B, 3C589, 3C59[0257] */
+ OtherInt = 0x0004, /* 3C59[0257] */
+ RomControl = 0x0006, /* 3C509B, 3C59[27] */
+ MacControl = 0x0006, /* 3C59[0257] */
+ ResetOptions = 0x0008, /* 3C59[0257] */
+ RxFree = 0x000A,
+ /* InternalConfig bits */
+ xcvr10BaseT = 0x00000000,
+ xcvrAui = 0x00100000, /* 10BASE5 */
+ xcvr10Base2 = 0x00300000,
+ xcvr100BaseTX = 0x00400000,
+ xcvr100BaseFX = 0x00500000,
+ xcvrMii = 0x00600000,
+ xcvrMask = 0x00700000,
+ autoSelect = 0x01000000,
+ /* MacControl bits */
+ deferExtendEnable = 0x0001,
+ deferTimerSelect = 0x001E, /* mask */
+ fullDuplexEnable = 0x0020,
+ allowLargePackets = 0x0040,
+ /* ResetOptions bits */
+ baseT4Available = 0x0001,
+ baseTXAvailable = 0x0002,
+ baseFXAvailable = 0x0004,
+ base10TAvailable = 0x0008,
+ coaxAvailable = 0x0010,
+ auiAvailable = 0x0020,
+ miiAvailable = 0x0040,
+};
+
+enum { /* Window 4 - diagnostic */
+ Wdiagnostic = 0x0004,
+ /* registers */
+ VcoDiagnostic = 0x0002,
+ FifoDiagnostic = 0x0004,
+ NetworkDiagnostic = 0x0006,
+ PhysicalMgmt = 0x0008,
+ MediaStatus = 0x000A,
+ BadSSD = 0x000C,
+ /* FifoDiagnostic bits */
+ txOverrun = 0x0400,
+ rxUnderrun = 0x2000,
+ receiving = 0x8000,
+ /* MediaStatus bits */
+ dataRate100 = 0x0002,
+ crcStripDisable = 0x0004,
+ enableSqeStats = 0x0008,
+ collisionDetect = 0x0010,
+ carrierSense = 0x0020,
+ jabberGuardEnable = 0x0040,
+ linkBeatEnable = 0x0080,
+ jabberDetect = 0x0200,
+ polarityReversed = 0x0400,
+ linkBeatDetect = 0x0800,
+ txInProg = 0x1000,
+ dcConverterEnabled = 0x4000,
+ auiDisable = 0x8000,
+};
+
+enum { /* Window 5 - internal state */
+ Wstate = 0x0005,
+ /* registers */
+ TxStartThresh = 0x0000,
+ TxAvalableThresh = 0x0002,
+ RxEarlyThresh = 0x0006,
+ RxFilter = 0x0008,
+ InterruptEnable = 0x000A,
+ IndicationEnable = 0x000C,
+};
+
+enum { /* Window 6 - statistics */
+ Wstatistics = 0x0006,
+ /* registers */
+ CarrierLost = 0x0000,
+ SqeErrors = 0x0001,
+ MultipleColls = 0x0002,
+ SingleCollFrames = 0x0003,
+ LateCollisions = 0x0004,
+ RxOverruns = 0x0005,
+ FramesXmittedOk = 0x0006,
+ FramesRcvdOk = 0x0007,
+ FramesDeferred = 0x0008,
+ UpperFramesOk = 0x0009,
+ BytesRcvdOk = 0x000A,
+ BytesXmittedOk = 0x000C,
+};
+
+enum { /* Window 7 - bus master operations */
+ Wmaster = 0x0007,
+ /* registers */
+ MasterAddress = 0x0000,
+ MasterLen = 0x0006,
+ MasterStatus = 0x000C,
+ /* MasterStatus bits */
+ masterAbort = 0x0001,
+ targetAbort = 0x0002,
+ targetRetry = 0x0004,
+ targetDisc = 0x0008,
+ masterDownload = 0x1000,
+ masterUpload = 0x4000,
+ masterInProgress = 0x8000,
+
+ masterMask = 0xD00F,
+};
+
+typedef struct {
+ Lock wlock; /* window access */
+
+ int attached;
+ int busmaster;
+ Block* rbp[2]; /* receive buffers */
+ int rbpix;
+
+ Block* txqhead; /* transmit queue */
+ Block* txqtail;
+ int txthreshold;
+ int txbusy;
+
+ long interrupts; /* statistics */
+ long timer;
+
+ long carrierlost;
+ long sqeerrors;
+ long multiplecolls;
+ long singlecollframes;
+ long latecollisions;
+ long rxoverruns;
+ long framesxmittedok;
+ long framesrcvdok;
+ long framesdeferred;
+ long bytesrcvdok;
+ long bytesxmittedok;
+ long badssd;
+
+ int xcvr; /* transceiver type */
+ int rxstatus9; /* old-style RxStatus register */
+} Ctlr;
+
+static Block*
+allocrbp(void)
+{
+ Block *bp;
+ ulong addr;
+
+ /*
+ * The receive buffers must be on a 32-byte
+ * boundary for EISA busmastering.
+ */
+ bp = allocb(ROUNDUP(sizeof(Etherpkt), 4) + 31);
+ addr = (ulong)bp->base;
+ addr = ROUNDUP(addr, 32);
+ bp->rp = (uchar*)addr;
+
+ return bp;
+}
+
+static uchar*
+startdma(Ether* ether, ulong address)
+{
+ int port, status, w;
+ uchar *wp;
+
+ port = ether->port;
+
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wmaster);
+
+ wp = KADDR(inl(port+MasterAddress));
+ status = ins(port+MasterStatus);
+ if(status & (masterInProgress|targetAbort|masterAbort))
+ print("elnk3#%d: BM status 0x%uX\n", ether->ctlrno, status);
+ outs(port+MasterStatus, masterMask);
+ outl(port+MasterAddress, address);
+ outs(port+MasterLen, sizeof(Etherpkt));
+ COMMAND(port, StartDma, Upload);
+
+ COMMAND(port, SelectRegisterWindow, w);
+ return wp;
+}
+
+static void
+promiscuous(void* arg, int on)
+{
+ int filter, port;
+
+ port = ((Ether*)arg)->port;
+
+ filter = receiveBroadcast|receiveIndividual;
+ if(on)
+ filter |= receiveAllFrames;
+ COMMAND(port, SetRxFilter, filter);
+}
+
+static void
+attach(Ether* ether)
+{
+ int port, x;
+ Ctlr *ctlr;
+
+ ctlr = ether->ctlr;
+ ilock(&ctlr->wlock);
+ if(ctlr->attached){
+ iunlock(&ctlr->wlock);
+ return;
+ }
+
+ port = ether->port;
+
+ /*
+ * Set the receiver packet filter for this and broadcast addresses,
+ * set the interrupt masks for all interrupts, enable the receiver
+ * and transmitter.
+ */
+ promiscuous(ether, ether->prom);
+
+ x = interruptMask|interruptLatch;
+ if(ctlr->busmaster)
+ x &= ~(rxEarly|rxComplete);
+ COMMAND(port, SetIndicationEnable, x);
+ COMMAND(port, SetInterruptEnable, x);
+
+ COMMAND(port, RxEnable, 0);
+ COMMAND(port, TxEnable, 0);
+
+ /*
+ * Prime the busmaster channel for receiving directly into a
+ * receive packet buffer if necessary.
+ */
+ ctlr->rbpix = 0;
+ if(ctlr->busmaster)
+ startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
+
+ ctlr->attached = 1;
+ iunlock(&ctlr->wlock);
+}
+
+static void
+statistics(Ether* ether)
+{
+ int port, u, w;
+ Ctlr *ctlr;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ /*
+ * 3C59[27] require a read between a PIO write and
+ * reading a statistics register.
+ */
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wstatistics);
+ STATUS(port);
+
+ ctlr->carrierlost += inb(port+CarrierLost) & 0xFF;
+ ctlr->sqeerrors += inb(port+SqeErrors) & 0xFF;
+ ctlr->multiplecolls += inb(port+MultipleColls) & 0xFF;
+ ctlr->singlecollframes += inb(port+SingleCollFrames) & 0xFF;
+ ctlr->latecollisions += inb(port+LateCollisions) & 0xFF;
+ ctlr->rxoverruns += inb(port+RxOverruns) & 0xFF;
+ ctlr->framesxmittedok += inb(port+FramesXmittedOk) & 0xFF;
+ ctlr->framesrcvdok += inb(port+FramesRcvdOk) & 0xFF;
+ u = inb(port+UpperFramesOk) & 0xFF;
+ ctlr->framesxmittedok += (u & 0x30)<<4;
+ ctlr->framesrcvdok += (u & 0x03)<<8;
+ ctlr->framesdeferred += inb(port+FramesDeferred) & 0xFF;
+ ctlr->bytesrcvdok += ins(port+BytesRcvdOk) & 0xFFFF;
+ ctlr->bytesxmittedok += ins(port+BytesXmittedOk) & 0xFFFF;
+
+ if(ctlr->xcvr == xcvr100BaseTX || ctlr->xcvr == xcvr100BaseFX){
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ STATUS(port);
+ ctlr->badssd += inb(port+BadSSD);
+ }
+
+ COMMAND(port, SelectRegisterWindow, w);
+}
+
+static void
+transmit(Ether* ether)
+{
+ int port, len;
+ Ctlr *ctlr;
+ Block *bp;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ /*
+ * Attempt to top-up the transmit FIFO. If there's room simply
+ * stuff in the packet length (unpadded to a dword boundary), the
+ * packet data (padded) and remove the packet from the queue.
+ * If there's no room post an interrupt for when there is.
+ * This routine is called both from the top level and from interrupt
+ * level and expects to be called with ctlr->wlock already locked
+ * and the correct register window (Wop) in place.
+ */
+ while(bp = ctlr->txqhead){
+ len = ROUNDUP(BLEN(bp), 4);
+ if(len+4 <= ins(port+TxFree)){
+ outl(port+Fifo, BLEN(bp));
+ outsl(port+Fifo, bp->rp, len/4);
+
+ ctlr->txqhead = bp->next;
+ freeb(bp);
+
+ ether->outpackets++;
+ }
+ else if(ctlr->txbusy == 0){
+ ctlr->txbusy = 1;
+ COMMAND(port, SetTxAvailableThresh, len);
+ return;
+ }
+ }
+}
+
+static long
+write(Ether* ether, void* buf, long n)
+{
+ Ctlr *ctlr;
+ Block *bp;
+ int port, w;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ /*
+ * Pack the write request up in a buffer, give it a source address
+ * and place it on the end of the transmit queue. The data written to the
+ * FIFO must be padded to a dword boundary, hence the ROUNDUP allocation.
+ * Call transmit() to stuff it into the TxFIFO if possible.
+ */
+ bp = allocb(ROUNDUP(n, 4));
+ memmove(bp->wp, buf, n);
+ memmove(bp->wp+Eaddrlen, ether->ea, Eaddrlen);
+ bp->wp += n;
+
+ ilock(&ctlr->wlock);
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wop);
+ if(ctlr->txqhead == 0)
+ ctlr->txqhead = bp;
+ else
+ ctlr->txqtail->next = bp;
+ ctlr->txqtail = bp;
+ if(ctlr->txbusy == 0)
+ transmit(ether);
+ COMMAND(port, SelectRegisterWindow, w);
+ iunlock(&ctlr->wlock);
+
+ return n;
+}
+
+static void
+receive(Ether* ether)
+{
+ int len, port, rxerror, rxstatus;
+ Ctlr *ctlr;
+ Block *bp;
+
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ while(((rxstatus = ins(port+RxStatus)) & rxIncomplete) == 0){
+ if(ctlr->busmaster && (STATUS(port) & busMasterInProgress))
+ break;
+
+ /*
+ * If there was an error, log it and continue.
+ * Unfortunately the 3C5[078]9 has the error info in the status register
+ * and the 3C59[0257] implement a separate RxError register.
+ */
+ if(rxstatus & rxError){
+ if(ctlr->rxstatus9){
+iprint("R%uX|", rxstatus);
+ switch(rxstatus & rxError9){
+
+ case rxOverrun9:
+ ether->overflows++;
+ break;
+
+ case oversizedFrame9:
+ case runtFrame9:
+ ether->buffs++;
+ break;
+
+ case alignmentError9:
+ ether->frames++;
+ break;
+
+ case crcError9:
+ ether->crcs++;
+ break;
+
+ }
+ }
+ else{
+ rxerror = inb(port+RxError);
+iprint("R%uX|", rxerror);
+ if(rxerror & rxOverrun)
+ ether->overflows++;
+ if(rxerror & (oversizedFrame|runtFrame))
+ ether->buffs++;
+ if(rxerror & alignmentError)
+ ether->frames++;
+ if(rxerror & crcError)
+ ether->crcs++;
+ }
+
+ COMMAND(port, RxDiscard, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+
+ if(ctlr->busmaster)
+ startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
+ }
+ else{
+ ether->inpackets++;
+ bp = ctlr->rbp[ctlr->rbpix];
+
+ if(ctlr->busmaster == 0){
+ len = (rxstatus & rxBytes9);
+ bp->wp = bp->rp + len;
+ insl(port+Fifo, bp->rp, HOWMANY(len, 4));
+ }
+
+ COMMAND(port, RxDiscard, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+
+ if(ctlr->busmaster){
+ ctlr->rbpix ^= 1;
+ bp->wp = startdma(ether, PADDR(ctlr->rbp[ctlr->rbpix]->rp));
+ }
+
+ etherrloop(ether, (Etherpkt*)bp->rp, BLEN(bp));
+ }
+ }
+}
+
+static void
+interrupt(Ureg*, void* arg)
+{
+ Ether *ether;
+ int port, status, txstatus, w, x;
+ Ctlr *ctlr;
+
+ ether = arg;
+ port = ether->port;
+ ctlr = ether->ctlr;
+
+ lock(&ctlr->wlock);
+ w = (STATUS(port)>>13) & 0x07;
+ COMMAND(port, SelectRegisterWindow, Wop);
+
+ ctlr->interrupts++;
+ ctlr->timer += inb(port+Timer) & 0xFF;
+ for(;;){
+ /*
+ * Clear the interrupt latch.
+ * It's possible to receive a packet and for another
+ * to become complete before exiting the interrupt
+ * handler so this must be done first to ensure another
+ * interrupt will occur.
+ */
+ COMMAND(port, AcknowledgeInterrupt, interruptLatch);
+ status = STATUS(port);
+ if((status & interruptMask) == 0)
+ break;
+
+ if(status & hostError){
+ /*
+ * Adapter failure, try to find out why, reset if
+ * necessary. What happens if Tx is active and a reset
+ * occurs, need to retransmit? This probably isn't right.
+ */
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+FifoDiagnostic);
+ COMMAND(port, SelectRegisterWindow, Wop);
+ print("elnk3#%d: status 0x%uX, diag 0x%uX\n",
+ ether->ctlrno, status, x);
+
+ if(x & txOverrun){
+ COMMAND(port, TxReset, 0);
+ COMMAND(port, TxEnable, 0);
+ wakeup(ðer->tr);
+ }
+
+ if(x & rxUnderrun){
+ /*
+ * This shouldn't happen...
+ * Need to restart any busmastering?
+ */
+ COMMAND(port, RxReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, RxEnable, 0);
+ }
+
+ status &= ~hostError;
+ }
+
+ if(status & (transferInt|rxComplete)){
+ receive(ether);
+ status &= ~(transferInt|rxComplete);
+ }
+
+ if(status & txComplete){
+ /*
+ * Pop the TxStatus stack, accumulating errors.
+ * Adjust the TX start threshold if there was an underrun.
+ * If there was a Jabber or Underrun error, reset
+ * the transmitter.
+ * For all conditions enable the transmitter.
+ */
+ txstatus = 0;
+ do{
+ if(x = inb(port+TxStatus))
+ outb(port+TxStatus, 0);
+ txstatus |= x;
+ }while(STATUS(port) & txComplete);
+
+ if(txstatus & txUnderrun){
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ while(ins(port+MediaStatus) & txInProg)
+ ;
+ COMMAND(port, SelectRegisterWindow, Wop);
+ if(ctlr->txthreshold < ETHERMAXTU)
+ ctlr->txthreshold += ETHERMINTU;
+ }
+
+ if(txstatus & (txJabber|txUnderrun)){
+ COMMAND(port, TxReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, SetTxStartThresh, ctlr->txthreshold);
+ }
+ COMMAND(port, TxEnable, 0);
+ ether->oerrs++;
+ status &= ~txComplete;
+ status |= txAvailable;
+ }
+
+ if(status & txAvailable){
+ COMMAND(port, AcknowledgeInterrupt, txAvailable);
+ ctlr->txbusy = 0;
+ transmit(ether);
+ status &= ~txAvailable;
+ }
+
+ if(status & updateStats){
+ statistics(ether);
+ status &= ~updateStats;
+ }
+
+ /*
+ * Panic if there are any interrupts not dealt with.
+ */
+ if(status & interruptMask)
+ panic("elnk3#%d: interrupt mask 0x%uX\n", ether->ctlrno, status);
+ }
+
+ COMMAND(port, SelectRegisterWindow, w);
+ unlock(&ctlr->wlock);
+}
+
+static long
+ifstat(Ether* ether, void* a, long n, ulong offset)
+{
+ Ctlr *ctlr;
+ char buf[512];
+ int len;
+
+ if(n == 0)
+ return 0;
+
+ ctlr = ether->ctlr;
+
+ ilock(&ctlr->wlock);
+ statistics(ether);
+ iunlock(&ctlr->wlock);
+
+ len = sprint(buf, "interrupts: %ld\n", ctlr->interrupts);
+ len += sprint(buf+len, "timer: %ld\n", ctlr->timer);
+ len += sprint(buf+len, "carrierlost: %ld\n", ctlr->carrierlost);
+ len += sprint(buf+len, "sqeerrors: %ld\n", ctlr->sqeerrors);
+ len += sprint(buf+len, "multiplecolls: %ld\n", ctlr->multiplecolls);
+ len += sprint(buf+len, "singlecollframes: %ld\n", ctlr->singlecollframes);
+ len += sprint(buf+len, "latecollisions: %ld\n", ctlr->latecollisions);
+ len += sprint(buf+len, "rxoverruns: %ld\n", ctlr->rxoverruns);
+ len += sprint(buf+len, "framesxmittedok: %ld\n", ctlr->framesxmittedok);
+ len += sprint(buf+len, "framesrcvdok: %ld\n", ctlr->framesrcvdok);
+ len += sprint(buf+len, "framesdeferred: %ld\n", ctlr->framesdeferred);
+ len += sprint(buf+len, "bytesrcvdok: %ld\n", ctlr->bytesrcvdok);
+ len += sprint(buf+len, "bytesxmittedok: %ld\n", ctlr->bytesxmittedok);
+ sprint(buf+len, "badssd: %ld\n", ctlr->badssd);
+
+ return readstr(offset, a, n, buf);
+}
+
+typedef struct Adapter Adapter;
+struct Adapter {
+ Adapter* next;
+ int port;
+ int irq;
+};
+static Adapter *adapter;
+
+/*
+ * Write two 0 bytes to identify the IDport and then reset the
+ * ID sequence. Then send the ID sequence to the card to get
+ * the card into command state.
+ */
+static void
+idseq(void)
+{
+ int i;
+ uchar al;
+ static int reset, untag;
+
+ /*
+ * One time only:
+ * reset any adapters listening
+ */
+ if(reset == 0){
+ outb(IDport, 0);
+ outb(IDport, 0);
+ outb(IDport, 0xC0);
+ delay(20);
+ reset = 1;
+ }
+
+ outb(IDport, 0);
+ outb(IDport, 0);
+ for(al = 0xFF, i = 0; i < 255; i++){
+ outb(IDport, al);
+ if(al & 0x80){
+ al <<= 1;
+ al ^= 0xCF;
+ }
+ else
+ al <<= 1;
+ }
+
+ /*
+ * One time only:
+ * write ID sequence to get the attention of all adapters;
+ * untag all adapters.
+ * If we do a global reset here on all adapters we'll confuse any
+ * ISA cards configured for EISA mode.
+ */
+ if(untag == 0){
+ outb(IDport, 0xD0);
+ untag = 1;
+ }
+}
+
+static ulong
+activate(void)
+{
+ int i;
+ ushort x, acr;
+
+ /*
+ * Do the little configuration dance:
+ *
+ * 2. write the ID sequence to get to command state.
+ */
+ idseq();
+
+ /*
+ * 3. Read the Manufacturer ID from the EEPROM.
+ * This is done by writing the IDPort with 0x87 (0x80
+ * is the 'read EEPROM' command, 0x07 is the offset of
+ * the Manufacturer ID field in the EEPROM).
+ * The data comes back 1 bit at a time.
+ * We seem to need a delay here between reading the bits.
+ *
+ * If the ID doesn't match, there are no more adapters.
+ */
+ outb(IDport, 0x87);
+ delay(20);
+ for(x = 0, i = 0; i < 16; i++){
+ delay(20);
+ x <<= 1;
+ x |= inb(IDport) & 0x01;
+ }
+ if(x != 0x6D50)
+ return 0;
+
+ /*
+ * 3. Read the Address Configuration from the EEPROM.
+ * The Address Configuration field is at offset 0x08 in the EEPROM).
+ */
+ outb(IDport, 0x88);
+ for(acr = 0, i = 0; i < 16; i++){
+ delay(20);
+ acr <<= 1;
+ acr |= inb(IDport) & 0x01;
+ }
+
+ return (acr & 0x1F)*0x10 + 0x200;
+}
+
+static ulong
+tcm509isa(Ether* ether)
+{
+ int irq, port;
+ Adapter *ap;
+
+ /*
+ * Attempt to activate adapters until one matches the
+ * address criteria. If adapter is set for EISA mode (0x3F0),
+ * tag it and ignore. Otherwise, activate it fully.
+ */
+ while(port = activate()){
+ /*
+ * 6. Tag the adapter so it won't respond in future.
+ */
+ outb(IDport, 0xD1);
+ if(port == 0x3F0)
+ continue;
+
+ /*
+ * 6. Activate the adapter by writing the Activate command
+ * (0xFF).
+ */
+ outb(IDport, 0xFF);
+ delay(20);
+
+ /*
+ * 8. Can now talk to the adapter's I/O base addresses.
+ * Use the I/O base address from the acr just read.
+ *
+ * Enable the adapter and clear out any lingering status
+ * and interrupts.
+ */
+ while(STATUS(port) & commandInProgress)
+ ;
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ outs(port+ConfigControl, Ena);
+
+ COMMAND(port, TxReset, 0);
+ COMMAND(port, RxReset, 0);
+ COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+ irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+ if(ether->port == 0 || ether->port == port){
+ ether->irq = irq;
+ return port;
+ }
+
+ ap = malloc(sizeof(Adapter));
+ ap->port = port;
+ ap->irq = irq;
+ ap->next = adapter;
+ adapter = ap;
+ }
+
+ return 0;
+}
+
+static int
+tcm5XXeisa(Ether* ether)
+{
+ static int slot = 1;
+ ushort x;
+ int irq, port;
+ Adapter *ap;
+
+ /*
+ * First time through, check if this is an EISA machine.
+ * If not, nothing to do.
+ */
+ if(slot == 1 && strncmp((char*)(KZERO|0xFFFD9), "EISA", 4))
+ return 0;
+
+ /*
+ * Continue through the EISA slots looking for a match on both
+ * 3COM as the manufacturer and 3C579-* or 3C59[27]-* as the product.
+ * If we find an adapter, select window 0, enable it and clear
+ * out any lingering status and interrupts.
+ */
+ while(slot < MaxEISA){
+ port = slot++*0x1000;
+ if(ins(port+0xC80+ManufacturerID) != 0x6D50)
+ continue;
+ x = ins(port+0xC80+ProductID);
+ if((x & 0xF0FF) != 0x9050 && (x & 0xFF00) != 0x5900)
+ continue;
+
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ outs(port+ConfigControl, Ena);
+
+ COMMAND(port, TxReset, 0);
+ COMMAND(port, RxReset, 0);
+ COMMAND(port, AcknowledgeInterrupt, 0xFF);
+
+ irq = (ins(port+ResourceConfig)>>12) & 0x0F;
+ if(ether->port == 0 || ether->port == port){
+ ether->irq = irq;
+ return port;
+ }
+
+ ap = malloc(sizeof(Adapter));
+ ap->port = port;
+ ap->irq = irq;
+ ap->next = adapter;
+ adapter = ap;
+ }
+
+ return 0;
+}
+
+static int
+tcm59Xpci(Ether* ether)
+{
+ PCIcfg pcicfg;
+ static int devno = 0;
+ int irq, port;
+ Adapter *ap;
+
+ for(;;){
+ pcicfg.vid = 0x10B7;
+ pcicfg.did = 0;
+ if((devno = pcimatch(0, devno, &pcicfg)) == -1)
+ break;
+ port = pcicfg.baseaddr[0] & ~0x01;
+ COMMAND(port, GlobalReset, 0);
+ while(STATUS(port) & commandInProgress)
+ ;
+ irq = pcicfg.irq;
+ if(ether->port == 0 || ether->port == port){
+ ether->irq = irq;
+ return port;
+ }
+
+ ap = malloc(sizeof(Adapter));
+ ap->port = port;
+ ap->irq = irq;
+ ap->next = adapter;
+ adapter = ap;
+ }
+
+ return 0;
+}
+
+static int
+tcm5XXpcmcia(Ether* ether)
+{
+ if(cistrcmp(ether->type, "3C589") == 0)
+ return ether->port;
+
+ return 0;
+}
+
+int
+etherelnk3reset(Ether* ether)
+{
+ int busmaster, i, port, rxstatus9, x, xcvr;
+ Adapter *ap, **app;
+ uchar ea[Eaddrlen];
+ Ctlr *ctlr;
+
+ /*
+ * Any adapter matches if no ether->port is supplied, otherwise the
+ * ports must match. First see if an adapter that fits the bill has
+ * already been found. If not, scan for adapter on PCI, EISA and finally
+ * using the little ISA configuration dance. The EISA and ISA scan
+ * routines leave Wsetup mapped.
+ * If an adapter is found save the IRQ and transceiver type.
+ */
+ port = 0;
+ rxstatus9 = 0;
+ xcvr = 0;
+ for(app = &adapter, ap = *app; ap; app = &ap->next, ap = ap->next){
+ if(ether->port == 0 || ether->port == ap->port){
+ port = ap->port;
+ ether->irq = ap->irq;
+ *app = ap->next;
+ free(ap);
+ break;
+ }
+ }
+ if(port == 0 && (port = tcm5XXpcmcia(ether))){
+ xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
+ rxstatus9 = 1;
+ }
+ else if(port == 0 && (port = tcm59Xpci(ether))){
+ COMMAND(port, SelectRegisterWindow, Wfifo);
+ xcvr = inl(port+InternalConfig) & xcvrMask;
+ }
+ else if(port == 0 && (port = tcm5XXeisa(ether))){
+ x = ins(port+ProductID);
+ if((x & 0xFF00) == 0x5900){
+ COMMAND(port, SelectRegisterWindow, Wfifo);
+ xcvr = inl(port+InternalConfig) & xcvrMask;
+ }
+ else{
+ xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
+ rxstatus9 = 1;
+ }
+ }
+ else if(port == 0 && (port = tcm509isa(ether))){
+ xcvr = ((ins(port+AddressConfig) & xcvrMask9)>>14)<<20;
+ rxstatus9 = 1;
+ }
+
+ if(port == 0)
+ return -1;
+
+ /*
+ * Check if the adapter's station address is to be overridden.
+ * If not, read it from the EEPROM and set in ether->ea prior to loading the
+ * station address in Wstation. The EEPROM returns 16-bits at a time.
+ */
+ memset(ea, 0, Eaddrlen);
+ if(memcmp(ea, ether->ea, Eaddrlen) == 0){
+ COMMAND(port, SelectRegisterWindow, Wsetup);
+ while(EEPROMBUSY(port))
+ ;
+ for(i = 0; i < Eaddrlen/2; i++){
+ EEPROMCMD(port, EepromReadRegister, i);
+ while(EEPROMBUSY(port))
+ ;
+ x = EEPROMDATA(port);
+ ether->ea[2*i] = (x>>8) & 0xFF;
+ ether->ea[2*i+1] = x & 0xFF;
+ }
+ }
+
+ COMMAND(port, SelectRegisterWindow, Wstation);
+ for(i = 0; i < Eaddrlen; i++)
+ outb(port+i, ether->ea[i]);
+
+ /*
+ * Enable the transceiver if necessary and determine whether
+ * busmastering can be used. Due to bugs in the first revision
+ * of the 3C59[05], don't use busmastering at 10Mbps.
+ */
+ COMMAND(port, SelectRegisterWindow, Wdiagnostic);
+ x = ins(port+MediaStatus) & ~(linkBeatEnable|jabberGuardEnable);
+ outs(port+MediaStatus, x);
+ if(x & dataRate100)
+ busmaster = 1;
+ else
+ busmaster = 0;
+ switch(xcvr){
+
+ case xcvr100BaseTX:
+ case xcvr100BaseFX:
+ ether->mbps = 100;
+ /*FALLTHROUGH*/
+ case xcvr10BaseT:
+ /*
+ * Enable Link Beat and Jabber to start the
+ * transceiver.
+ */
+ x |= linkBeatEnable|jabberGuardEnable;
+ outs(port+MediaStatus, x);
+ break;
+
+ case xcvr10Base2:
+ /*
+ * Start the DC-DC converter.
+ * Wait > 800 microseconds.
+ */
+ COMMAND(port, EnableDcConverter, 0);
+ delay(1);
+ break;
+ }
+
+ /*
+ * Wop is the normal operating register set.
+ * The 3C59[0257] adapters allow access to more than one register window
+ * at a time, but there are situations where switching still needs to be
+ * done, so just do it.
+ * Clear out any lingering Tx status.
+ */
+ COMMAND(port, SelectRegisterWindow, Wop);
+ while(inb(port+TxStatus))
+ outb(port+TxStatus, 0);
+
+ /*
+ * Allocate a controller structure, clear out the
+ * adapter statistics, clear the statistics logged into ctlr
+ * and enable statistics collection. Xcvr is needed in order
+ * to collect the BadSSD statistics.
+ */
+ ether->ctlr = malloc(sizeof(Ctlr));
+ ctlr = ether->ctlr;
+
+ ilock(&ctlr->wlock);
+ ctlr->xcvr = xcvr;
+ statistics(ether);
+ memset(ctlr, 0, sizeof(Ctlr));
+
+ ctlr->busmaster = busmaster;
+ ctlr->xcvr = xcvr;
+ ctlr->rxstatus9 = rxstatus9;
+
+ COMMAND(port, StatisticsEnable, 0);
+
+ /*
+ * Allocate the receive buffers.
+ */
+ ctlr->rbpix = 0;
+ ctlr->rbp[0] = allocrbp();
+ if(ctlr->busmaster)
+ ctlr->rbp[1] = allocrbp();
+
+ /*
+ * Set a base TxStartThresh which will be incremented
+ * if any txUnderrun errors occur and ensure no RxEarly
+ * interrupts happen.
+ */
+ ctlr->txthreshold = ETHERMINTU;
+ COMMAND(port, SetTxStartThresh, ETHERMINTU);
+ COMMAND(port, SetRxEarlyThresh, ETHERMAXTU);
+
+ iunlock(&ctlr->wlock);
+
+ /*
+ * Linkage to the generic ethernet driver.
+ */
+ ether->port = port;
+ ether->attach = attach;
+ ether->write = write;
+ ether->interrupt = interrupt;
+ ether->ifstat = ifstat;
+
+ ether->promiscuous = promiscuous;
+ ether->arg = ether;
+
+ return 0;
+}
+
+void
+etherelnk3link(void)
+{
+ addethercard("elnk3", etherelnk3reset);
+ addethercard("3C509", etherelnk3reset);
+}
M pc/etherif.h => pc/etherif.h +2 -1
@@ 1,5 1,5 @@
enum {
- MaxEther = 8,
+ MaxEther = 24,
Ntypes = 8,
};
@@ 12,6 12,7 @@ struct Ether {
void (*attach)(Ether*); /* filled in by reset routine */
long (*write)(Ether*, void*, long);
void (*interrupt)(Ureg*, void*);
+ long (*ifstat)(Ether*, void*, long, ulong);
void *ctlr;
Etherpkt tpkt; /* transmit buffer */
M pc/fns.h => pc/fns.h +0 -4
@@ 129,7 129,3 @@ ulong ifunroute(ulong);
void parseip(char*, char*);
#define dcflush(a, b)
-
-int scsibtissue(int, int, uchar*, int, uchar*, int);
-Scsiio scsibt24(int, int, ISAConf*);
-Scsiio scsibt32(int, int, ISAConf*);
M pc/scsi.c => pc/scsi.c +92 -15
@@ 78,7 78,7 @@ scsireset(void)
if((ctlr->io = (*lp->reset)(ctlrno, ctlr)) == 0)
break;
- print("scsi%d: %s: port 0x%luX irq %d",
+ print("scsi#%d: %s: port 0x%luX irq %d",
ctlrno, ctlr->type, ctlr->port,
ctlr->irq, ctlr->mem, ctlr->size);
if(ctlr->mem)
@@ 106,7 106,29 @@ scsireset(void)
int
scsiexec(Target *t, int rw, uchar *cmd, int cbytes, void *data, int *dbytes)
{
- return (*scsi[t->ctlrno]->io)(t, rw, cmd, cbytes, data, dbytes);
+ int s;
+
+ /*
+ * Call the device-specific I/O routine.
+ * There should be no calls to 'error()' below this
+ * which percolate back up.
+ */
+ switch(s = (*scsi[t->ctlrno]->io)(t, rw, cmd, cbytes, data, dbytes)){
+
+ default:
+ /*
+ * It's more complicated than this. There are conditions which
+ * are 'ok' but for which the returned status code is not 'STok'.
+ * Also, not all conditions require a reqsense, there may be a
+ * need to do a reqsense here when necessary and making it
+ * available to the caller somehow.
+ *
+ * Later.
+ */
+ break;
+ }
+
+ return s;
}
Target*
@@ 143,7 165,7 @@ scsiprobe(Ctlr *ctlr)
nbytes = Nscratch;
s = scsireqsense(t, 0, t->scratch, &nbytes, 0);
if(s != STok){
- print("scsi%d: unit %d unavailable, status %d\n", t->ctlrno, i, s);
+ print("scsi#%d: unit %d unavailable, status %d\n", t->ctlrno, i, s);
continue;
}
@@ 154,11 176,11 @@ scsiprobe(Ctlr *ctlr)
memset(t->inq, 0, Ninq);
nbytes = Ninq;
s = scsiinquiry(t, 0, t->inq, &nbytes);
- if(s < 0) {
- print("scsi%d: unit %d inquire failed, status %d\n", t->ctlrno, i, s);
+ if(s != STok) {
+ print("scsi#%d: unit %d inquire failed, status %d\n", t->ctlrno, i, s);
continue;
}
- print("scsi%d: unit %d: %s\n", t->ctlrno, i, t->inq+8);
+ print("scsi#%d: unit %d: %s\n", t->ctlrno, i, t->inq+8);
t->ok = 1;
}
}
@@ 264,7 286,7 @@ scsicap(Target *t, char lun, ulong *size, ulong *bsize)
nbytes = 8;
d = scsialloc(nbytes);
if(d == 0)
- error(Enomem);
+ return -1;
s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes);
if(s == STok) {
@@ 302,10 324,10 @@ scsibio(Target *t, char lun, int dir, void *b, long n, long bsize, long bno)
}
nbytes = n*bsize;
s = scsiexec(t, dir, cmd, cdbsiz, b, &nbytes);
- if(s < 0) {
+ if(s != STok) {
nbytes = Nscratch;
scsireqsense(t, lun, t->scratch, &nbytes, 0);
- return -1;
+ return scsierrstr(s);
}
return nbytes;
}
@@ 391,7 413,7 @@ scsireqsense(Target *t, char lun, void *data, int *nbytes, int quiet)
buggery:
if(quiet == 0){
s = key[sense[2]&0x0F];
- print("scsi%d: unit %d reqsense: '%s' code #%2.2ux #%2.2ux\n",
+ print("scsi#%d: unit %d reqsense: '%s' code #%2.2ux #%2.2ux\n",
t->ctlrno, t->target, s, sense[12], sense[13]);
}
free(sense);
@@ 414,7 436,7 @@ scsidiskinfo(Target *t, char lun, int track, uchar *data)
d = scsialloc(nbytes);
if(d == 0)
- error(Enomem);
+ return scsierrstr(STnomem);
memset(d, 0, nbytes);
s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes);
@@ 439,7 461,7 @@ scsitrackinfo(Target *t, char lun, int track, uchar *data)
d = scsialloc(nbytes);
if(d == 0)
- error(Enomem);
+ return scsierrstr(STnomem);
memset(d, 0, nbytes);
s = scsiexec(t, SCSIread, cmd, sizeof(cmd), d, &nbytes);
@@ 465,7 487,7 @@ scsibufsize(Target *t, char lun, int size)
d = scsialloc(nbytes);
if(d == 0)
- error(Enomem);
+ return scsierrstr(STnomem);
memset(d, 0, nbytes);
d[3] = 8;
@@ 491,10 513,65 @@ scsireadcdda(Target *t, char lun, int, void *b, long n, long bsize, long bno)
nbytes = n*bsize;
s = scsiexec(t, SCSIread, cmd, sizeof(cmd), b, &nbytes);
- if(s < 0) {
+ if(s != STok) {
nbytes = Nscratch;
scsireqsense(t, lun, t->scratch, &nbytes, 0);
- return -1;
+ return scsierrstr(s);
}
return nbytes;
}
+
+int
+scsierrstr(int errno)
+{
+ char *p;
+
+ switch(errno){
+ case STnomem:
+ p = Enomem;
+ break;
+ case STtimeout:
+ p = "bus timeout";
+ break;
+ case STownid:
+ p = "playing with myself";
+ break;
+ case STharderr:
+ p = Eio;
+ break;
+ case STok:
+ p = Enoerror;
+ break;
+ case STcheck:
+ p = "check condition";
+ break;
+ case STcondmet:
+ p = "condition met/good";
+ break;
+ case STbusy:
+ p = "busy";
+ break;
+ case STintok:
+ p = "intermediate/good";
+ break;
+ case STintcondmet:
+ p = "intermediate/condition met/good";
+ break;
+ case STresconf:
+ p = "reservation conflict";
+ break;
+ case STterminated:
+ p = "command terminated";
+ break;
+ case STqfull:
+ p = "queue full";
+ break;
+
+ default:
+ p = "unknown SCSI error";
+ break;
+ }
+ strncpy(up->error, p, NAMELEN);
+
+ return -1;
+}
M port/devsd.c => port/devsd.c +71 -53
@@ 47,6 47,8 @@ struct Disk
char vol[NAMELEN];
uchar* inquire;
+ int partok;
+ ulong version;
ulong size;
ulong bsize;
@@ 57,7 59,7 @@ struct Disk
int ndisk;
Disk disk[Ndisk];
-static void sdrdpart(Disk*);
+static int sdrdpart(Disk*);
static long sdio(Chan*, int, char*, ulong, ulong);
static int types[] =
@@ 137,9 139,6 @@ sdinit(void)
* their act together after a reset.
* If 'not ready' comes back, try starting a TypeDA and punt
* the get capacity until the drive is attached.
- * It might be possible to be smarter here, and look at the
- * response from a test-unit-ready which would show if the
- * target was in the process of becoming ready.
*/
if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok) {
nbytes = sizeof(scratch);
@@ 187,8 186,12 @@ sdattach(char *spec)
{
int i;
- for(i = 0; i < ndisk; i++)
- sdrdpart(&disk[i]);
+ for(i = 0; i < ndisk; i++){
+ qlock(&disk[i]);
+ if(disk[i].partok == 0)
+ sdrdpart(&disk[i]);
+ qunlock(&disk[i]);
+ }
return devattach('w', spec);
}
@@ 246,8 249,12 @@ sdclose(Chan *c)
d = &disk[DRIVE(c->qid)];
p = &d->table[PART(c->qid)];
- if((c->mode&3) != OREAD && strcmp(p->name, "partition") == 0)
+ if((c->mode&3) != OREAD && strcmp(p->name, "partition") == 0){
+ qlock(d);
+ d->partok = 0;
sdrdpart(d);
+ qunlock(d);
+ }
}
long
@@ 268,6 275,12 @@ sdbread(Chan *c, long n, ulong offset)
long
sdwrite(Chan *c, char *a, long n, ulong offset)
{
+ Disk *d;
+
+ d = &disk[DRIVE(c->qid)];
+
+ if((d->inquire[0] & 0x1F) == TypeCD)
+ error(Eperm);
return sdio(c, 1, a, n, offset);
}
@@ 277,7 290,7 @@ sdbwrite(Chan *c, Block *bp, ulong offset)
return devbwrite(c, bp, offset);
}
-static void
+static int
sdrdpart(Disk *d)
{
Part *p;
@@ 285,30 298,36 @@ sdrdpart(Disk *d)
char *b, *line[Npart+2], *field[3];
static char MAGIC[] = "plan9 partitions";
+ if(d->partok)
+ return STok;
+
+ p = d->table;
+ strcpy(p->name, "disk");
+ p->beg = 0;
+ p->end = 0;
+ d->npart = 1;
+
/*
- * If the drive wasn't ready when we tried to do a
- * read-capacity earlier (in sdinit()), try again.
- * It might be possible to be smarter here, and look at the
- * response from a test-unit-ready which would show if the
- * target was in the process of becoming ready.
+ * If the drive wasn't ready when a read-capacity was tried
+ * earlier (in sdinit()), try again.
*/
if(d->size == 0 || d->bsize == 0){
- if(scsicap(d->t, d->lun, &d->size, &d->bsize) != STok){
+ n = scsicap(d->t, d->lun, &d->size, &d->bsize);
+ if(n != STok){
d->size = d->bsize = 0;
- error(Eio);
+ return scsierrstr(n);
}
}
+ p->end = d->size + 1;
+
+ if((d->inquire[0] & 0x1F) == TypeCD)
+ return STok;
+
b = scsialloc(d->bsize);
if(b == 0)
- error(Enomem);
- qlock(d);
-
- p = d->table;
+ return scsierrstr(STnomem);
- strcpy(p->name, "disk");
- p->beg = 0;
- p->end = d->size + 1;
p++;
strcpy(p->name, "partition");
p->beg = d->table[0].end - 1;
@@ 316,12 335,6 @@ sdrdpart(Disk *d)
p++;
d->npart = 2;
- if((d->inquire[0] & 0x1F) == TypeCD){
- scsifree(b);
- qunlock(d);
- return;
- }
-
scsibio(d->t, d->lun, SCSIread, b, 1, d->bsize, d->table[0].end-1);
b[d->bsize-1] = '\0';
@@ 331,26 344,24 @@ sdrdpart(Disk *d)
n = parsefields(b, line, Npart+2, "\n");
if(n > 0 && strncmp(line[0], MAGIC, sizeof(MAGIC)-1) == 0) {
for(i = 1; i < n; i++) {
- switch(parsefields(line[i], field, 3, " ")) {
- case 2:
- if(strcmp(field[0], "unit") == 0)
- strncpy(d->vol, field[1], NAMELEN);
- break;
- case 3:
- if(p >= &d->table[Npart])
- break;
- strncpy(p->name, field[0], NAMELEN);
- p->beg = strtoul(field[1], 0, 0);
- p->end = strtoul(field[2], 0, 0);
- if(p->beg > p->end || p->beg >= d->table[0].end)
- break;
- p++;
- }
+ if(parsefields(line[i], field, 3, " ") != 3)
+ break;
+ if(p >= &d->table[Npart])
+ break;
+ strncpy(p->name, field[0], NAMELEN);
+ p->beg = strtoul(field[1], 0, 0);
+ p->end = strtoul(field[2], 0, 0);
+ if(p->beg > p->end || p->beg >= d->table[0].end)
+ break;
+ p++;
}
}
d->npart = p - d->table;
scsifree(b);
- qunlock(d);
+
+ d->partok = 1;
+
+ return STok;
}
static long
@@ 364,9 375,6 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset)
d = &disk[DRIVE(c->qid)];
p = &d->table[PART(c->qid)];
- if(write && (d->inquire[0] & 0x1F) == TypeCD)
- error(Eperm);
-
block = (offset / d->bsize) + p->beg;
n = (offset + len + d->bsize - 1) / d->bsize + p->beg - block;
max = SCSImaxxfer / d->bsize;
@@ 379,15 387,16 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset)
b = scsialloc(n*d->bsize);
if(b == 0)
- error(Enomem);
- if(waserror()) {
- scsifree(b);
- nexterror();
- }
+ return scsierrstr(STnomem);
+
offset %= d->bsize;
if(write) {
if(offset || len % d->bsize) {
x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block);
+ if(x < 0){
+ len = -1;
+ goto buggery;
+ }
if(x < n * d->bsize) {
n = x / d->bsize;
x = n * d->bsize - offset;
@@ 397,6 406,10 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset)
}
memmove(b + offset, a, len);
x = scsibio(d->t, d->lun, SCSIwrite, b, n, d->bsize, block);
+ if(x < 0){
+ len = -1;
+ goto buggery;
+ }
if(x < offset)
len = 0;
else
@@ 405,6 418,10 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset)
}
else {
x = scsibio(d->t, d->lun, SCSIread, b, n, d->bsize, block);
+ if(x < 0){
+ len = -1;
+ goto buggery;
+ }
if(x < offset)
len = 0;
else
@@ 412,7 429,8 @@ sdio(Chan *c, int write, char *a, ulong len, ulong offset)
len = x - offset;
memmove(a, b+offset, len);
}
- poperror();
+
+buggery:
scsifree(b);
return len;
}
M port/portdat.h => port/portdat.h +13 -13
@@ 670,19 670,19 @@ enum
/* Things scsi */
enum
{
- STtimeout =-3,
- STownid =-2,
- STharderr =-1, /* Command status error returns */
- STok = 0,
- STcheck = 2,
- STcondmet = 4,
- STbusy = 8,
- STintok = 16,
- STconflict = 18,
- STintcondmet = 20,
- STterminated = 22,
- STresconf = 24,
- STqfull = 28,
+ STnomem =-4, /* buffer allocation failed */
+ STtimeout =-3, /* bus timeout */
+ STownid =-2, /* playing with myself */
+ STharderr =-1, /* controller error of some kind */
+ STok = 0, /* good */
+ STcheck = 0x02, /* check condition */
+ STcondmet = 0x04, /* condition met/good */
+ STbusy = 0x08, /* busy */
+ STintok = 0x10, /* intermediate/good */
+ STintcondmet = 0x14, /* intermediate/condition met/good */
+ STresconf = 0x18, /* reservation conflict */
+ STterminated = 0x22, /* command terminated */
+ STqfull = 0x28, /* queue full */
SCSIread = 0,
SCSIwrite,
M port/portfns.h => port/portfns.h +1 -0
@@ 247,6 247,7 @@ int screenbits(void);
#define scsialloc(n) mallocz((n)+512, 0)
int scsibio(Target*, char, int, void*, long, long, long);
int scsicap(Target*, char, ulong*, ulong*);
+int scsierrstr(int);
int scsiexec(Target*, int, uchar*, int, void*, int*);
#define scsifree(p) free(p)
int scsiinquiry(Target*, char, void*, int*);