plan9fox/sys/src/9/pc/ether8139.c
cinap_lenrek 4f85115526 kernel: massive pci code rewrite
The new pci code is moved to port/pci.[hc] and shared by
all ports.

Each port has its own PCI controller implementation,
providing the pcicfgrw*() functions for low level pci
config space access. The locking for pcicfgrw*() is now
done by the caller (only port/pci.c).

Device drivers now need to include "../port/pci.h" in
addition to "io.h".

The new code now checks bridge windows and membars,
while enumerating the bus, giving the pc driver a chance
to re-assign them. This is needed because some UEFI
implementations fail to assign the bars for some devices,
so we need to do it outselfs. (See pcireservemem()).

While working on this, it was discovered that the pci
code assimed the smallest I/O bar size is 16 (pcibarsize()),
which is wrong. I/O bars can be as small as 4 bytes.
Bit 1 in an I/O bar is also reserved and should be masked off,
making the port mask: port = bar & ~3;
2020-09-13 20:33:17 +02:00

833 lines
20 KiB
C

/*
* Realtek 8139 (but not the 8129).
* Error recovery for the various over/under -flow conditions
* may need work.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "io.h"
#include "../port/pci.h"
#include "../port/error.h"
#include "../port/netif.h"
#include "../port/etherif.h"
enum { /* registers */
Idr0 = 0x0000, /* MAC address */
Mar0 = 0x0008, /* Multicast address */
Tsd0 = 0x0010, /* Transmit Status Descriptor0 */
Tsad0 = 0x0020, /* Transmit Start Address Descriptor0 */
Rbstart = 0x0030, /* Receive Buffer Start Address */
Erbcr = 0x0034, /* Early Receive Byte Count */
Ersr = 0x0036, /* Early Receive Status */
Cr = 0x0037, /* Command Register */
Capr = 0x0038, /* Current Address of Packet Read */
Cbr = 0x003A, /* Current Buffer Address */
Imr = 0x003C, /* Interrupt Mask */
Isr = 0x003E, /* Interrupt Status */
Tcr = 0x0040, /* Transmit Configuration */
Rcr = 0x0044, /* Receive Configuration */
Tctr = 0x0048, /* Timer Count */
Mpc = 0x004C, /* Missed Packet Counter */
Cr9346 = 0x0050, /* 9346 Command Register */
Config0 = 0x0051, /* Configuration Register 0 */
Config1 = 0x0052, /* Configuration Register 1 */
TimerInt = 0x0054, /* Timer Interrupt */
Msr = 0x0058, /* Media Status */
Config3 = 0x0059, /* Configuration Register 3 */
Config4 = 0x005A, /* Configuration Register 4 */
Mulint = 0x005C, /* Multiple Interrupt Select */
RerID = 0x005E, /* PCI Revision ID */
Tsad = 0x0060, /* Transmit Status of all Descriptors */
Bmcr = 0x0062, /* Basic Mode Control */
Bmsr = 0x0064, /* Basic Mode Status */
Anar = 0x0066, /* Auto-Negotiation Advertisment */
Anlpar = 0x0068, /* Auto-Negotiation Link Partner */
Aner = 0x006A, /* Auto-Negotiation Expansion */
Dis = 0x006C, /* Disconnect Counter */
Fcsc = 0x006E, /* False Carrier Sense Counter */
Nwaytr = 0x0070, /* N-way Test */
Rec = 0x0072, /* RX_ER Counter */
Cscr = 0x0074, /* CS Configuration */
Phy1parm = 0x0078, /* PHY Parameter 1 */
Twparm = 0x007C, /* Twister Parameter */
Phy2parm = 0x0080, /* PHY Parameter 2 */
};
enum { /* Cr */
Bufe = 0x01, /* Rx Buffer Empty */
Te = 0x04, /* Transmitter Enable */
Re = 0x08, /* Receiver Enable */
Rst = 0x10, /* Software Reset */
};
enum { /* Imr/Isr */
Rok = 0x0001, /* Receive OK */
Rer = 0x0002, /* Receive Error */
Tok = 0x0004, /* Transmit OK */
Ter = 0x0008, /* Transmit Error */
Rxovw = 0x0010, /* Receive Buffer Overflow */
PunLc = 0x0020, /* Packet Underrun or Link Change */
Fovw = 0x0040, /* Receive FIFO Overflow */
Clc = 0x2000, /* Cable Length Change */
Timerbit = 0x4000, /* Timer */
Serr = 0x8000, /* System Error */
};
enum { /* Tcr */
Clrabt = 0x00000001, /* Clear Abort */
TxrrSHIFT = 4, /* Transmit Retry Count */
TxrrMASK = 0x000000F0,
MtxdmaSHIFT = 8, /* Max. DMA Burst Size */
MtxdmaMASK = 0x00000700,
Mtxdma2048 = 0x00000700,
Acrc = 0x00010000, /* Append CRC (not) */
LbkSHIFT = 17, /* Loopback Test */
LbkMASK = 0x00060000,
Rtl8139ArevG = 0x00800000, /* RTL8139A Rev. G ID */
IfgSHIFT = 24, /* Interframe Gap */
IfgMASK = 0x03000000,
HwveridSHIFT = 26, /* Hardware Version ID */
HwveridMASK = 0x7C000000,
};
enum { /* Rcr */
Aap = 0x00000001, /* Accept All Packets */
Apm = 0x00000002, /* Accept Physical Match */
Am = 0x00000004, /* Accept Multicast */
Ab = 0x00000008, /* Accept Broadcast */
Ar = 0x00000010, /* Accept Runt */
Aer = 0x00000020, /* Accept Error */
Sel9356 = 0x00000040, /* 9356 EEPROM used */
Wrap = 0x00000080, /* Rx Buffer Wrap Control */
MrxdmaSHIFT = 8, /* Max. DMA Burst Size */
MrxdmaMASK = 0x00000700,
Mrxdmaunlimited = 0x00000700,
RblenSHIFT = 11, /* Receive Buffer Length */
RblenMASK = 0x00001800,
Rblen8K = 0x00000000, /* 8KB+16 */
Rblen16K = 0x00000800, /* 16KB+16 */
Rblen32K = 0x00001000, /* 32KB+16 */
Rblen64K = 0x00001800, /* 64KB+16 */
RxfthSHIFT = 13, /* Receive Buffer Length */
RxfthMASK = 0x0000E000,
Rxfth256 = 0x00008000,
Rxfthnone = 0x0000E000,
Rer8 = 0x00010000, /* Accept Error Packets > 8 bytes */
MulERINT = 0x00020000, /* Multiple Early Interrupt Select */
ErxthSHIFT = 24, /* Early Rx Threshold */
ErxthMASK = 0x0F000000,
Erxthnone = 0x00000000,
};
enum { /* Received Packet Status */
Rcok = 0x0001, /* Receive Completed OK */
Fae = 0x0002, /* Frame Alignment Error */
Crc = 0x0004, /* CRC Error */
Long = 0x0008, /* Long Packet */
Runt = 0x0010, /* Runt Packet Received */
Ise = 0x0020, /* Invalid Symbol Error */
Bar = 0x2000, /* Broadcast Address Received */
Pam = 0x4000, /* Physical Address Matched */
Mar = 0x8000, /* Multicast Address Received */
};
enum { /* Media Status Register */
Rxpf = 0x01, /* Pause Flag */
Txpf = 0x02, /* Pause Flag */
Linkb = 0x04, /* Inverse of Link Status */
Speed10 = 0x08, /* 10Mbps */
Auxstatus = 0x10, /* Aux. Power Present Status */
Rxfce = 0x40, /* Receive Flow Control Enable */
Txfce = 0x80, /* Transmit Flow Control Enable */
};
typedef struct Td Td;
struct Td { /* Soft Transmit Descriptor */
int tsd;
int tsad;
uchar* data;
Block* bp;
};
enum { /* Tsd0 */
SizeSHIFT = 0, /* Descriptor Size */
SizeMASK = 0x00001FFF,
Own = 0x00002000,
Tun = 0x00004000, /* Transmit FIFO Underrun */
Tcok = 0x00008000, /* Transmit COmpleted OK */
EtxthSHIFT = 16, /* Early Tx Threshold */
EtxthMASK = 0x001F0000,
NccSHIFT = 24, /* Number of Collisions Count */
NccMASK = 0x0F000000,
Cdh = 0x10000000, /* CD Heartbeat */
Owc = 0x20000000, /* Out of Window Collision */
Tabt = 0x40000000, /* Transmit Abort */
Crs = 0x80000000, /* Carrier Sense Lost */
};
enum {
Rblen = Rblen64K, /* Receive Buffer Length */
Ntd = 4, /* Number of Transmit Descriptors */
Tdbsz = ROUNDUP(sizeof(Etherpkt), 4),
};
typedef struct Ctlr Ctlr;
typedef struct Ctlr {
int port;
Pcidev* pcidev;
Ctlr* next;
int active;
int id;
QLock alock; /* attach */
Lock ilock; /* init */
void* alloc; /* base of per-Ctlr allocated data */
int pcie; /* flag: pci-express device? */
uvlong mchash; /* multicast hash */
int rcr; /* receive configuration register */
uchar* rbstart; /* receive buffer */
int rblen; /* receive buffer length */
int ierrs; /* receive errors */
Lock tlock; /* transmit */
Td td[Ntd];
int ntd; /* descriptors active */
int tdh; /* host index into td */
int tdi; /* interface index into td */
int etxth; /* early transmit threshold */
int taligned; /* packet required no alignment */
int tunaligned; /* packet required alignment */
int dis; /* disconnect counter */
int fcsc; /* false carrier sense counter */
int rec; /* RX_ER counter */
uint mcast;
} Ctlr;
static Ctlr* ctlrhead;
static Ctlr* ctlrtail;
#define csr8r(c, r) (inb((c)->port+(r)))
#define csr16r(c, r) (ins((c)->port+(r)))
#define csr32r(c, r) (inl((c)->port+(r)))
#define csr8w(c, r, b) (outb((c)->port+(r), (int)(b)))
#define csr16w(c, r, w) (outs((c)->port+(r), (ushort)(w)))
#define csr32w(c, r, l) (outl((c)->port+(r), (ulong)(l)))
static void
rtl8139promiscuous(void* arg, int on)
{
Ether *edev;
Ctlr * ctlr;
edev = arg;
ctlr = edev->ctlr;
ilock(&ctlr->ilock);
if(on)
ctlr->rcr |= Aap;
else
ctlr->rcr &= ~Aap;
csr32w(ctlr, Rcr, ctlr->rcr);
iunlock(&ctlr->ilock);
}
enum {
/* everyone else uses 0x04c11db7, but they both produce the same crc */
Etherpolybe = 0x04c11db6,
Bytemask = (1<<8) - 1,
};
static ulong
ethercrcbe(uchar *addr, long len)
{
int i, j;
ulong c, crc, carry;
crc = ~0UL;
for (i = 0; i < len; i++) {
c = addr[i];
for (j = 0; j < 8; j++) {
carry = ((crc & (1UL << 31))? 1: 0) ^ (c & 1);
crc <<= 1;
c >>= 1;
if (carry)
crc = (crc ^ Etherpolybe) | carry;
}
}
return crc;
}
static ulong
swabl(ulong l)
{
return l>>24 | (l>>8) & (Bytemask<<8) |
(l<<8) & (Bytemask<<16) | l<<24;
}
static void
rtl8139multicast(void* ether, uchar *eaddr, int add)
{
Ether *edev;
Ctlr *ctlr;
if (!add)
return; /* ok to keep receiving on old mcast addrs */
edev = ether;
ctlr = edev->ctlr;
ilock(&ctlr->ilock);
ctlr->mchash |= 1ULL << (ethercrcbe(eaddr, Eaddrlen) >> 26);
ctlr->rcr |= Am;
csr32w(ctlr, Rcr, ctlr->rcr);
/* pci-e variants reverse the order of the hash byte registers */
if (0 && ctlr->pcie) {
csr32w(ctlr, Mar0, swabl(ctlr->mchash>>32));
csr32w(ctlr, Mar0+4, swabl(ctlr->mchash));
} else {
csr32w(ctlr, Mar0, ctlr->mchash);
csr32w(ctlr, Mar0+4, ctlr->mchash>>32);
}
iunlock(&ctlr->ilock);
}
static long
rtl8139ifstat(Ether* edev, void* a, long n, ulong offset)
{
int l;
char *p;
Ctlr *ctlr;
ctlr = edev->ctlr;
p = smalloc(READSTR);
l = snprint(p, READSTR, "rcr %#8.8ux\n", ctlr->rcr);
l += snprint(p+l, READSTR-l, "multicast %ud\n", ctlr->mcast);
l += snprint(p+l, READSTR-l, "ierrs %d\n", ctlr->ierrs);
l += snprint(p+l, READSTR-l, "etxth %d\n", ctlr->etxth);
l += snprint(p+l, READSTR-l, "taligned %d\n", ctlr->taligned);
l += snprint(p+l, READSTR-l, "tunaligned %d\n", ctlr->tunaligned);
ctlr->dis += csr16r(ctlr, Dis);
l += snprint(p+l, READSTR-l, "dis %d\n", ctlr->dis);
ctlr->fcsc += csr16r(ctlr, Fcsc);
l += snprint(p+l, READSTR-l, "fcscnt %d\n", ctlr->fcsc);
ctlr->rec += csr16r(ctlr, Rec);
l += snprint(p+l, READSTR-l, "rec %d\n", ctlr->rec);
l += snprint(p+l, READSTR-l, "Tcr %#8.8lux\n", csr32r(ctlr, Tcr));
l += snprint(p+l, READSTR-l, "Config0 %#2.2ux\n", csr8r(ctlr, Config0));
l += snprint(p+l, READSTR-l, "Config1 %#2.2ux\n", csr8r(ctlr, Config1));
l += snprint(p+l, READSTR-l, "Msr %#2.2ux\n", csr8r(ctlr, Msr));
l += snprint(p+l, READSTR-l, "Config3 %#2.2ux\n", csr8r(ctlr, Config3));
l += snprint(p+l, READSTR-l, "Config4 %#2.2ux\n", csr8r(ctlr, Config4));
l += snprint(p+l, READSTR-l, "Bmcr %#4.4ux\n", csr16r(ctlr, Bmcr));
l += snprint(p+l, READSTR-l, "Bmsr %#4.4ux\n", csr16r(ctlr, Bmsr));
l += snprint(p+l, READSTR-l, "Anar %#4.4ux\n", csr16r(ctlr, Anar));
l += snprint(p+l, READSTR-l, "Anlpar %#4.4ux\n", csr16r(ctlr, Anlpar));
l += snprint(p+l, READSTR-l, "Aner %#4.4ux\n", csr16r(ctlr, Aner));
l += snprint(p+l, READSTR-l, "Nwaytr %#4.4ux\n", csr16r(ctlr, Nwaytr));
snprint(p+l, READSTR-l, "Cscr %#4.4ux\n", csr16r(ctlr, Cscr));
n = readstr(offset, a, n, p);
free(p);
return n;
}
static int
rtl8139reset(Ctlr* ctlr)
{
int timeo;
/*
* Soft reset the controller.
*/
csr8w(ctlr, Cr, Rst);
for(timeo = 0; timeo < 1000; timeo++){
if(!(csr8r(ctlr, Cr) & Rst))
return 0;
delay(1);
}
return -1;
}
static void
rtl8139halt(Ctlr* ctlr)
{
int i;
csr8w(ctlr, Cr, 0);
csr16w(ctlr, Imr, 0);
csr16w(ctlr, Isr, ~0);
for(i = 0; i < Ntd; i++){
if(ctlr->td[i].bp == nil)
continue;
freeb(ctlr->td[i].bp);
ctlr->td[i].bp = nil;
}
}
static void
rtl8139init(Ether* edev)
{
int i;
ulong r;
Ctlr *ctlr;
uchar *alloc;
ctlr = edev->ctlr;
ilock(&ctlr->ilock);
rtl8139halt(ctlr);
/*
* MAC Address.
*/
r = (edev->ea[3]<<24)|(edev->ea[2]<<16)|(edev->ea[1]<<8)|edev->ea[0];
csr32w(ctlr, Idr0, r);
r = (edev->ea[5]<<8)|edev->ea[4];
csr32w(ctlr, Idr0+4, r);
/*
* Receiver
*/
alloc = ctlr->alloc;
ctlr->rbstart = alloc;
alloc += ctlr->rblen+16;
memset(ctlr->rbstart, 0, ctlr->rblen+16);
csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
ctlr->rcr = Rxfth256|Rblen|Mrxdmaunlimited|Ab|Am|Apm;
/*
* Transmitter.
*/
for(i = 0; i < Ntd; i++){
ctlr->td[i].tsd = Tsd0+i*4;
ctlr->td[i].tsad = Tsad0+i*4;
ctlr->td[i].data = alloc;
alloc += Tdbsz;
ctlr->td[i].bp = nil;
}
ctlr->ntd = ctlr->tdh = ctlr->tdi = 0;
ctlr->etxth = 128/32;
/*
* Interrupts.
*/
csr32w(ctlr, TimerInt, 0);
csr16w(ctlr, Imr, Serr|Timerbit|Fovw|PunLc|Rxovw|Ter|Tok|Rer|Rok);
csr32w(ctlr, Mpc, 0);
/*
* Enable receiver/transmitter.
* Need to enable before writing the Rcr or it won't take.
*/
csr8w(ctlr, Cr, Te|Re);
csr32w(ctlr, Tcr, Mtxdma2048);
csr32w(ctlr, Rcr, ctlr->rcr);
csr32w(ctlr, Mar0, 0);
csr32w(ctlr, Mar0+4, 0);
ctlr->mchash = 0;
iunlock(&ctlr->ilock);
}
static void
rtl8139attach(Ether* edev)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
qlock(&ctlr->alock);
if(ctlr->alloc == nil){
ctlr->rblen = 1<<((Rblen>>RblenSHIFT)+13);
ctlr->alloc = mallocalign(ctlr->rblen+16 + Ntd*Tdbsz, 32, 0, 0);
if(ctlr->alloc == nil){
qunlock(&ctlr->alock);
error(Enomem);
}
rtl8139init(edev);
}
qunlock(&ctlr->alock);
}
static void
rtl8139txstart(Ether* edev)
{
Td *td;
int size;
Block *bp;
Ctlr *ctlr;
ctlr = edev->ctlr;
while(ctlr->ntd < Ntd){
bp = qget(edev->oq);
if(bp == nil)
break;
size = BLEN(bp);
td = &ctlr->td[ctlr->tdh];
if(((uintptr)bp->rp) & 0x03){
memmove(td->data, bp->rp, size);
freeb(bp);
csr32w(ctlr, td->tsad, PCIWADDR(td->data));
ctlr->tunaligned++;
}
else{
td->bp = bp;
csr32w(ctlr, td->tsad, PCIWADDR(bp->rp));
ctlr->taligned++;
}
csr32w(ctlr, td->tsd, (ctlr->etxth<<EtxthSHIFT)|size);
ctlr->ntd++;
ctlr->tdh = NEXT(ctlr->tdh, Ntd);
}
}
static void
rtl8139transmit(Ether* edev)
{
Ctlr *ctlr;
ctlr = edev->ctlr;
ilock(&ctlr->tlock);
rtl8139txstart(edev);
iunlock(&ctlr->tlock);
}
static void
rtl8139receive(Ether* edev)
{
Block *bp;
Ctlr *ctlr;
ushort capr;
uchar cr, *p;
int l, length, status;
ctlr = edev->ctlr;
/*
* Capr is where the host is reading from,
* Cbr is where the NIC is currently writing.
*/
capr = (csr16r(ctlr, Capr)+16) % ctlr->rblen;
while(!(csr8r(ctlr, Cr) & Bufe)){
p = ctlr->rbstart+capr;
/*
* Apparently the packet length may be 0xFFF0 if
* the NIC is still copying the packet into memory.
*/
length = (*(p+3)<<8)|*(p+2);
if(length == 0xFFF0)
break;
status = (*(p+1)<<8)|*p;
if(!(status & Rcok)){
if(status & (Ise|Fae))
edev->frames++;
if(status & Crc)
edev->crcs++;
if(status & (Runt|Long))
edev->buffs++;
/*
* Reset the receiver.
* Also may have to restore the multicast list
* here too if it ever gets used.
*/
cr = csr8r(ctlr, Cr);
csr8w(ctlr, Cr, cr & ~Re);
csr32w(ctlr, Rbstart, PCIWADDR(ctlr->rbstart));
csr8w(ctlr, Cr, cr);
csr32w(ctlr, Rcr, ctlr->rcr);
continue;
}
/*
* Receive Completed OK.
* Very simplistic; there are ways this could be done
* without copying, but the juice probably isn't worth
* the squeeze.
* The packet length includes a 4 byte CRC on the end.
*/
capr = (capr+4) % ctlr->rblen;
p = ctlr->rbstart+capr;
capr = (capr+length) % ctlr->rblen;
if(status & Mar)
ctlr->mcast++;
if((bp = iallocb(length)) != nil){
if(p+length >= ctlr->rbstart+ctlr->rblen){
l = ctlr->rbstart+ctlr->rblen - p;
memmove(bp->wp, p, l);
bp->wp += l;
length -= l;
p = ctlr->rbstart;
}
if(length > 0){
memmove(bp->wp, p, length);
bp->wp += length;
}
bp->wp -= 4;
etheriq(edev, bp);
}
capr = ROUNDUP(capr, 4);
csr16w(ctlr, Capr, capr-16);
}
}
static void
rtl8139interrupt(Ureg*, void* arg)
{
Td *td;
Ctlr *ctlr;
Ether *edev;
int isr, msr, tsd;
edev = arg;
ctlr = edev->ctlr;
while((isr = csr16r(ctlr, Isr)) != 0){
csr16w(ctlr, Isr, isr);
if(isr & (Fovw|PunLc|Rxovw|Rer|Rok)){
rtl8139receive(edev);
if(!(isr & Rok))
ctlr->ierrs++;
isr &= ~(Fovw|Rxovw|Rer|Rok);
}
if(isr & (Ter|Tok)){
ilock(&ctlr->tlock);
while(ctlr->ntd){
td = &ctlr->td[ctlr->tdi];
tsd = csr32r(ctlr, td->tsd);
if(!(tsd & (Tabt|Tun|Tcok)))
break;
if(!(tsd & Tcok)){
if(tsd & Tun){
if(ctlr->etxth < ETHERMAXTU/32)
ctlr->etxth++;
}
edev->oerrs++;
}
if(td->bp != nil){
freeb(td->bp);
td->bp = nil;
}
ctlr->ntd--;
ctlr->tdi = NEXT(ctlr->tdi, Ntd);
}
rtl8139txstart(edev);
iunlock(&ctlr->tlock);
isr &= ~(Ter|Tok);
}
if(isr & PunLc){
/*
* Maybe the link changed - do we care very much?
*/
msr = csr8r(ctlr, Msr);
if(!(msr & Linkb)){
if(!(msr & Speed10) && edev->mbps != 100){
edev->mbps = 100;
qsetlimit(edev->oq, 256*1024);
}
else if((msr & Speed10) && edev->mbps != 10){
edev->mbps = 10;
qsetlimit(edev->oq, 65*1024);
}
}
isr &= ~(Clc|PunLc);
}
/*
* Only Serr|Timerbit should be left by now.
* Should anything be done to tidy up? TimerInt isn't
* used so that can be cleared. A PCI bus error is indicated
* by Serr, that's pretty serious; is there anyhing to do
* other than try to reinitialise the chip?
*/
if((isr & (Serr|Timerbit)) != 0){
iprint("rtl8139interrupt: imr %#4.4ux isr %#4.4ux\n",
csr16r(ctlr, Imr), isr);
if(isr & Timerbit)
csr32w(ctlr, TimerInt, 0);
if(isr & Serr)
rtl8139init(edev);
}
}
}
static Ctlr*
rtl8139match(Ether* edev, int id)
{
Pcidev *p;
Ctlr *ctlr;
int port;
/*
* Any adapter matches if no edev->port is supplied,
* otherwise the ports must match.
*/
for(ctlr = ctlrhead; ctlr != nil; ctlr = ctlr->next){
if(ctlr->active)
continue;
p = ctlr->pcidev;
if(((p->did<<16)|p->vid) != id)
continue;
port = p->mem[0].bar & ~3;
if(edev->port != 0 && edev->port != port)
continue;
if(ioalloc(port, p->mem[0].size, 0, "rtl8139") < 0){
print("rtl8139: port %#ux in use\n", port);
continue;
}
pcienable(p);
ctlr->port = port;
if(rtl8139reset(ctlr)) {
pcidisable(p);
iofree(port);
continue;
}
pcisetbme(p);
ctlr->active = 1;
return ctlr;
}
return nil;
}
static struct {
char* name;
int id;
} rtl8139pci[] = {
{ "rtl8139", (0x8139<<16)|0x10EC, }, /* generic */
{ "smc1211", (0x1211<<16)|0x1113, }, /* SMC EZ-Card */
{ "dfe-538tx", (0x1300<<16)|0x1186, }, /* D-Link DFE-538TX */
{ "dfe-560txd", (0x1340<<16)|0x1186, }, /* D-Link DFE-560TXD */
{ nil },
};
static int
rtl8139pnp(Ether* edev)
{
int i, id;
Pcidev *p;
Ctlr *ctlr;
uchar ea[Eaddrlen];
/*
* Make a list of all ethernet controllers
* if not already done.
*/
if(ctlrhead == nil){
p = nil;
while(p = pcimatch(p, 0, 0)){
if(p->ccrb != 0x02 || p->ccru != 0)
continue;
ctlr = malloc(sizeof(Ctlr));
if(ctlr == nil){
print("rtl8139: can't allocate memory\n");
continue;
}
ctlr->pcidev = p;
ctlr->id = (p->did<<16)|p->vid;
if(ctlrhead != nil)
ctlrtail->next = ctlr;
else
ctlrhead = ctlr;
ctlrtail = ctlr;
}
}
/*
* Is it an RTL8139 under a different name?
* Normally a search is made through all the found controllers
* for one which matches any of the known vid+did pairs.
* If a vid+did pair is specified a search is made for that
* specific controller only.
*/
id = 0;
for(i = 0; i < edev->nopt; i++){
if(cistrncmp(edev->opt[i], "id=", 3) == 0)
id = strtol(&edev->opt[i][3], nil, 0);
}
ctlr = nil;
if(id != 0)
ctlr = rtl8139match(edev, id);
else for(i = 0; rtl8139pci[i].name; i++){
if((ctlr = rtl8139match(edev, rtl8139pci[i].id)) != nil)
break;
}
if(ctlr == nil)
return -1;
edev->ctlr = ctlr;
edev->port = ctlr->port;
edev->irq = ctlr->pcidev->intl;
edev->tbdf = ctlr->pcidev->tbdf;
/*
* Check if the adapter's station address is to be overridden.
* If not, read it from the device and set in edev->ea.
*/
memset(ea, 0, Eaddrlen);
if(memcmp(ea, edev->ea, Eaddrlen) == 0){
i = csr32r(ctlr, Idr0);
edev->ea[0] = i;
edev->ea[1] = i>>8;
edev->ea[2] = i>>16;
edev->ea[3] = i>>24;
i = csr32r(ctlr, Idr0+4);
edev->ea[4] = i;
edev->ea[5] = i>>8;
}
edev->attach = rtl8139attach;
edev->transmit = rtl8139transmit;
edev->ifstat = rtl8139ifstat;
edev->arg = edev;
edev->promiscuous = rtl8139promiscuous;
edev->multicast = rtl8139multicast;
// edev->shutdown = rtl8139shutdown;
intrenable(edev->irq, rtl8139interrupt, edev, edev->tbdf, edev->name);
/*
* This should be much more dynamic but will do for now.
*/
if((csr8r(ctlr, Msr) & (Speed10|Linkb)) == 0)
edev->mbps = 100;
return 0;
}
void
ether8139link(void)
{
addethercard("rtl8139", rtl8139pnp);
}