/* * This file is part of the UCB release of Plan 9. It is subject to the license * terms in the LICENSE file found in the top-level directory of this * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No * part of the UCB release of Plan 9, including this file, may be copied, * modified, propagated, or distributed except according to the terms contained * in the LICENSE file. */ /* * SMSC LAN95XX */ #include <u.h>#include <libc.h>#include <fcall.h>#include <thread.h>#include "usb.h"#include "usbfs.h"#include "ether.h" enum { Doburst = 1, Resettime = 1000, E2pbusytime = 1000, Afcdefault = 0xF830A1,// Hsburst = 37, /* from original linux driver */ Hsburst = 8, Fsburst = 129, Defbulkdly = 0x2000, Ethp8021q = 0x8100, MACoffset = 1, PHYinternal = 1, Rxerror = 0x8000, Txfirst = 0x2000, Txlast = 0x1000, /* USB vendor requests */ Writereg = 0xA0, Readreg = 0xA1, /* device registers */ Intsts = 0x08, Txcfg = 0x10, Txon = 1<<2, Hwcfg = 0x14, Bir = 1<<12, Rxdoff = 3<<9, Mef = 1<<5, Lrst = 1<<3, Bce = 1<<1, Pmctrl = 0x20, Phyrst = 1<<4, Ledgpio = 0x24, Ledspd = 1<<24, Ledlnk = 1<<20, Ledfdx = 1<<16, Afccfg = 0x2C, E2pcmd = 0x30, Busy = 1<<31, Timeout = 1<<10, Read = 0, E2pdata = 0x34, Burstcap = 0x38, Intepctl = 0x68, Phyint = 1<<15, Bulkdelay = 0x6C, Maccr = 0x100, Mcpas = 1<<19, Prms = 1<<18, Hpfilt = 1<<13, Txen = 1<<3, Rxen = 1<<2, Addrh = 0x104, Addrl = 0x108, Hashh = 0x10C, Hashl = 0x110, MIIaddr = 0x114, MIIwrite= 1<<1, MIIread = 0<<1, MIIbusy = 1<<0, MIIdata = 0x118, Flow = 0x11C, Vlan1 = 0x120, Coecr = 0x130, Txcoe = 1<<16, Rxcoemd = 1<<1, Rxcoe = 1<<0, /* MII registers */ Bmcr = 0, Bmcrreset= 1<<15, Speed100= 1<<13, Anenable= 1<<12, Anrestart= 1<<9, Fulldpx = 1<<8, Bmsr = 1, Advertise = 4, Adcsma = 0x0001, Ad10h = 0x0020, Ad10f = 0x0040, Ad100h = 0x0080, Ad100f = 0x0100, Adpause = 0x0400, Adpauseasym= 0x0800, Adall = Ad10h|Ad10f|Ad100h|Ad100f, Phyintsrc = 29, Phyintmask = 30, Anegcomp= 1<<6, Linkdown= 1<<4,}; static intwr(Dev *d, int reg, int val){ int ret; ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg, (uchar*)&val, sizeof(val)); if(ret < 0) deprint(2, "%s: wr(%x, %x): %r", argv0, reg, val); return ret;} static intrr(Dev *d, int reg){ int ret, rval; ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg, (uchar*)&rval, sizeof(rval)); if(ret < 0){ fprint(2, "%s: rr(%x): %r", argv0, reg); return 0; } return rval;} static intmiird(Dev *d, int idx){ while(rr(d, MIIaddr) & MIIbusy) ; wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread); while(rr(d, MIIaddr) & MIIbusy) ; return rr(d, MIIdata);} static voidmiiwr(Dev *d, int idx, int val){ while(rr(d, MIIaddr) & MIIbusy) ; wr(d, MIIdata, val); wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite); while(rr(d, MIIaddr) & MIIbusy) ;} static inteepromr(Dev *d, int off, uchar *buf, int len){ int i, v; for(i = 0; i < E2pbusytime; i++) if((rr(d, E2pcmd) & Busy) == 0) break; if(i == E2pbusytime) return -1; for(i = 0; i < len; i++){ wr(d, E2pcmd, Busy|Read|(i+off)); while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy) ; if(v & Timeout) return -1; buf[i] = rr(d, E2pdata); } return 0;} static voidphyinit(Dev *d){ int i; miiwr(d, Bmcr, Bmcrreset|Anenable); for(i = 0; i < Resettime/10; i++){ if((miird(d, Bmcr) & Bmcrreset) == 0) break; sleep(10); } miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);// miiwr(d, Advertise, Adcsma|Ad10f|Ad10h|Adpause|Adpauseasym); miird(d, Phyintsrc); miiwr(d, Phyintmask, Anegcomp|Linkdown); miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);} static intdoreset(Dev *d, int reg, int bit){ int i; if(wr(d, reg, bit) < 0) return -1; for(i = 0; i < Resettime/10; i++){ if((rr(d, reg) & bit) == 0) return 1; sleep(10); } return 0;} static intgetmac(Dev *d, uchar buf[]){ int i; uchar ea[Eaddrlen]; if(eepromr(d, MACoffset, ea, Eaddrlen) < 0) return -1; for(i = 0; i < Eaddrlen; i++) if(ea[i] != 0 && ea[i] != 0xFF){ memmove(buf, ea, Eaddrlen); break; } return Eaddrlen;} static intsmscinit(Ether *ether){ Dev *d; if(ether->cid != S95xx) return -1; d = ether->dev; deprint(2, "%s: setting up SMSC95XX\n", argv0); if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst)) return -1; if(getmac(d, ether->addr) < 0) return -1; wr(d, Addrl, GET4(ether->addr)); wr(d, Addrh, GET2(ether->addr+4)); if(Doburst){ wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce); wr(d, Burstcap, Hsburst); }else{ wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir); wr(d, Burstcap, 0); } wr(d, Bulkdelay, Defbulkdly); wr(d, Intsts, ~0); wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx); wr(d, Flow, 0); wr(d, Afccfg, Afcdefault); wr(d, Vlan1, Ethp8021q); wr(d, Coecr, rr(d,Coecr)&~(Txcoe|Rxcoe)); /* TODO could offload checksums? */ wr(d, Hashh, 0); wr(d, Hashl, 0); wr(d, Maccr, rr(d,Maccr)&~(Prms|Mcpas|Hpfilt)); phyinit(d); wr(d, Intepctl, rr(d, Intepctl)|Phyint); wr(d, Maccr, rr(d, Maccr)|Txen|Rxen); wr(d, Txcfg, Txon); return 0;} static longsmscbread(Ether *e, Buf *bp){ uint hd; int n, m; Buf *rbp; rbp = e->aux; if(rbp->ndata < 4){ rbp->rp = rbp->data; rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? Hsburst*512: Maxpkt); if(rbp->ndata < 0) return -1; } if(rbp->ndata < 4){ werrstr("short frame"); fprint(2, "smsc short frame %d bytes\n", rbp->ndata); return 0; } hd = GET4(rbp->rp); n = hd >> 16; m = (n + 4 + 3) & ~3; if(n < 6 || m > rbp->ndata){ werrstr("frame length"); fprint(2, "smsc length error packet %d buf %d\n", n, rbp->ndata); rbp->ndata = 0; return 0; } if(hd & Rxerror){ fprint(2, "smsc rx error %8.8ux\n", hd); n = 0; }else{ bp->rp = bp->data + Hdrsize; memmove(bp->rp, rbp->rp+4, n); } bp->ndata = n; rbp->rp += m; rbp->ndata -= m; return n;} static longsmscbwrite(Ether *e, Buf *bp){ int n; n = bp->ndata & 0x7FF; bp->rp -= 8; bp->ndata += 8; PUT4(bp->rp, n | Txfirst | Txlast); PUT4(bp->rp+4, n); n = write(e->epout->dfd, bp->rp, bp->ndata); return n;} static intsmscpromiscuous(Ether *e, int on){ USED(on, e);#ifdef TODO /* copied from asix */ int rxctl; deprint(2, "%s: smscpromiscuous %d\n", argv0, on); rxctl = getrxctl(e->dev); if(on != 0) rxctl |= Rxctlprom; else rxctl &= ~Rxctlprom; return wr(e->dev, Cwrxctl, rxctl);#endif return -1;} static intsmscmulticast(Ether *e, uchar *addr, int on){ USED(addr, on, e);#ifdef TODO /* needed for ipv6; copied from asix */ int rxctl; /* BUG: should write multicast filter */ rxctl = getrxctl(e->dev); if(e->nmcasts != 0) rxctl |= Rxctlamall; else rxctl &= ~Rxctlamall; deprint(2, "%s: smscmulticast %d\n", argv0, e->nmcasts); return wr(e->dev, Cwrxctl, rxctl);#endif return -1;} static voidsmscfree(Ether *ether){ free(ether->aux); ether->aux = nil;} intsmscreset(Ether *ether){ Cinfo *ip; Dev *dev; dev = ether->dev; for(ip = cinfo; ip->vid != 0; ip++) if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){ ether->cid = ip->cid; if(smscinit(ether) < 0){ deprint(2, "%s: smsc init failed: %r\n", argv0); return -1; } deprint(2, "%s: smsc reset done\n", argv0); ether->name = "smsc"; if(Doburst){ ether->bufsize = Hsburst*512; ether->aux = emallocz(sizeof(Buf) + ether->bufsize - Maxpkt, 1); }else{ ether->bufsize = Maxpkt; ether->aux = emallocz(sizeof(Buf), 1); } ether->free = smscfree; ether->bread = smscbread; ether->bwrite = smscbwrite; ether->promiscuous = smscpromiscuous; ether->multicast = smscmulticast; ether->mbps = 100; /* BUG */ return 0; } return -1;}
/*
* This file is part of the UCB release of Plan 9. It is subject to the license
* terms in the LICENSE file found in the top-level directory of this
* distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
* part of the UCB release of Plan 9, including this file, may be copied,
* modified, propagated, or distributed except according to the terms contained
* in the LICENSE file.
*/
/*
* SMSC LAN95XX
*/
#include <u.h>
#include <libc.h>
#include <fcall.h>
#include <thread.h>
#include "usb.h"
#include "usbfs.h"
#include "ether.h"
enum {
Doburst = 1,
Resettime = 1000,
E2pbusytime = 1000,
Afcdefault = 0xF830A1,
// Hsburst = 37, /* from original linux driver */
Hsburst = 8,
Fsburst = 129,
Defbulkdly = 0x2000,
Ethp8021q = 0x8100,
MACoffset = 1,
PHYinternal = 1,
Rxerror = 0x8000,
Txfirst = 0x2000,
Txlast = 0x1000,
/* USB vendor requests */
Writereg = 0xA0,
Readreg = 0xA1,
/* device registers */
Intsts = 0x08,
Txcfg = 0x10,
Txon = 1<<2,
Hwcfg = 0x14,
Bir = 1<<12,
Rxdoff = 3<<9,
Mef = 1<<5,
Lrst = 1<<3,
Bce = 1<<1,
Pmctrl = 0x20,
Phyrst = 1<<4,
Ledgpio = 0x24,
Ledspd = 1<<24,
Ledlnk = 1<<20,
Ledfdx = 1<<16,
Afccfg = 0x2C,
E2pcmd = 0x30,
Busy = 1<<31,
Timeout = 1<<10,
Read = 0,
E2pdata = 0x34,
Burstcap = 0x38,
Intepctl = 0x68,
Phyint = 1<<15,
Bulkdelay = 0x6C,
Maccr = 0x100,
Mcpas = 1<<19,
Prms = 1<<18,
Hpfilt = 1<<13,
Txen = 1<<3,
Rxen = 1<<2,
Addrh = 0x104,
Addrl = 0x108,
Hashh = 0x10C,
Hashl = 0x110,
MIIaddr = 0x114,
MIIwrite= 1<<1,
MIIread = 0<<1,
MIIbusy = 1<<0,
MIIdata = 0x118,
Flow = 0x11C,
Vlan1 = 0x120,
Coecr = 0x130,
Txcoe = 1<<16,
Rxcoemd = 1<<1,
Rxcoe = 1<<0,
/* MII registers */
Bmcr = 0,
Bmcrreset= 1<<15,
Speed100= 1<<13,
Anenable= 1<<12,
Anrestart= 1<<9,
Fulldpx = 1<<8,
Bmsr = 1,
Advertise = 4,
Adcsma = 0x0001,
Ad10h = 0x0020,
Ad10f = 0x0040,
Ad100h = 0x0080,
Ad100f = 0x0100,
Adpause = 0x0400,
Adpauseasym= 0x0800,
Adall = Ad10h|Ad10f|Ad100h|Ad100f,
Phyintsrc = 29,
Phyintmask = 30,
Anegcomp= 1<<6,
Linkdown= 1<<4,
};
static int
wr(Dev *d, int reg, int val)
{
int ret;
ret = usbcmd(d, Rh2d|Rvendor|Rdev, Writereg, 0, reg,
(uchar*)&val, sizeof(val));
if(ret < 0)
deprint(2, "%s: wr(%x, %x): %r", argv0, reg, val);
return ret;
}
static int
rr(Dev *d, int reg)
{
int ret, rval;
ret = usbcmd(d, Rd2h|Rvendor|Rdev, Readreg, 0, reg,
(uchar*)&rval, sizeof(rval));
if(ret < 0){
fprint(2, "%s: rr(%x): %r", argv0, reg);
return 0;
}
return rval;
}
static int
miird(Dev *d, int idx)
{
while(rr(d, MIIaddr) & MIIbusy)
;
wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIread);
while(rr(d, MIIaddr) & MIIbusy)
;
return rr(d, MIIdata);
}
static void
miiwr(Dev *d, int idx, int val)
{
while(rr(d, MIIaddr) & MIIbusy)
;
wr(d, MIIdata, val);
wr(d, MIIaddr, PHYinternal<<11 | idx<<6 | MIIwrite);
while(rr(d, MIIaddr) & MIIbusy)
;
}
static int
eepromr(Dev *d, int off, uchar *buf, int len)
{
int i, v;
for(i = 0; i < E2pbusytime; i++)
if((rr(d, E2pcmd) & Busy) == 0)
break;
if(i == E2pbusytime)
return -1;
for(i = 0; i < len; i++){
wr(d, E2pcmd, Busy|Read|(i+off));
while((v = rr(d, E2pcmd) & (Busy|Timeout)) == Busy)
;
if(v & Timeout)
return -1;
buf[i] = rr(d, E2pdata);
}
return 0;
}
static void
phyinit(Dev *d)
{
int i;
miiwr(d, Bmcr, Bmcrreset|Anenable);
for(i = 0; i < Resettime/10; i++){
if((miird(d, Bmcr) & Bmcrreset) == 0)
break;
sleep(10);
}
miiwr(d, Advertise, Adcsma|Adall|Adpause|Adpauseasym);
// miiwr(d, Advertise, Adcsma|Ad10f|Ad10h|Adpause|Adpauseasym);
miird(d, Phyintsrc);
miiwr(d, Phyintmask, Anegcomp|Linkdown);
miiwr(d, Bmcr, miird(d, Bmcr)|Anenable|Anrestart);
}
static int
doreset(Dev *d, int reg, int bit)
{
int i;
if(wr(d, reg, bit) < 0)
return -1;
for(i = 0; i < Resettime/10; i++){
if((rr(d, reg) & bit) == 0)
return 1;
sleep(10);
}
return 0;
}
static int
getmac(Dev *d, uchar buf[])
{
int i;
uchar ea[Eaddrlen];
if(eepromr(d, MACoffset, ea, Eaddrlen) < 0)
return -1;
for(i = 0; i < Eaddrlen; i++)
if(ea[i] != 0 && ea[i] != 0xFF){
memmove(buf, ea, Eaddrlen);
break;
}
return Eaddrlen;
}
static int
smscinit(Ether *ether)
{
Dev *d;
if(ether->cid != S95xx)
return -1;
d = ether->dev;
deprint(2, "%s: setting up SMSC95XX\n", argv0);
if(!doreset(d, Hwcfg, Lrst) || !doreset(d, Pmctrl, Phyrst))
return -1;
if(getmac(d, ether->addr) < 0)
return -1;
wr(d, Addrl, GET4(ether->addr));
wr(d, Addrh, GET2(ether->addr+4));
if(Doburst){
wr(d, Hwcfg, (rr(d,Hwcfg)&~Rxdoff)|Bir|Mef|Bce);
wr(d, Burstcap, Hsburst);
}else{
wr(d, Hwcfg, (rr(d,Hwcfg)&~(Rxdoff|Mef|Bce))|Bir);
wr(d, Burstcap, 0);
}
wr(d, Bulkdelay, Defbulkdly);
wr(d, Intsts, ~0);
wr(d, Ledgpio, Ledspd|Ledlnk|Ledfdx);
wr(d, Flow, 0);
wr(d, Afccfg, Afcdefault);
wr(d, Vlan1, Ethp8021q);
wr(d, Coecr, rr(d,Coecr)&~(Txcoe|Rxcoe)); /* TODO could offload checksums? */
wr(d, Hashh, 0);
wr(d, Hashl, 0);
wr(d, Maccr, rr(d,Maccr)&~(Prms|Mcpas|Hpfilt));
phyinit(d);
wr(d, Intepctl, rr(d, Intepctl)|Phyint);
wr(d, Maccr, rr(d, Maccr)|Txen|Rxen);
wr(d, Txcfg, Txon);
return 0;
}
static long
smscbread(Ether *e, Buf *bp)
{
uint hd;
int n, m;
Buf *rbp;
rbp = e->aux;
if(rbp->ndata < 4){
rbp->rp = rbp->data;
rbp->ndata = read(e->epin->dfd, rbp->rp, Doburst? Hsburst*512:
Maxpkt);
if(rbp->ndata < 0)
return -1;
}
if(rbp->ndata < 4){
werrstr("short frame");
fprint(2, "smsc short frame %d bytes\n", rbp->ndata);
return 0;
}
hd = GET4(rbp->rp);
n = hd >> 16;
m = (n + 4 + 3) & ~3;
if(n < 6 || m > rbp->ndata){
werrstr("frame length");
fprint(2, "smsc length error packet %d buf %d\n", n, rbp->ndata);
rbp->ndata = 0;
return 0;
}
if(hd & Rxerror){
fprint(2, "smsc rx error %8.8ux\n", hd);
n = 0;
}else{
bp->rp = bp->data + Hdrsize;
memmove(bp->rp, rbp->rp+4, n);
}
bp->ndata = n;
rbp->rp += m;
rbp->ndata -= m;
return n;
}
static long
smscbwrite(Ether *e, Buf *bp)
{
int n;
n = bp->ndata & 0x7FF;
bp->rp -= 8;
bp->ndata += 8;
PUT4(bp->rp, n | Txfirst | Txlast);
PUT4(bp->rp+4, n);
n = write(e->epout->dfd, bp->rp, bp->ndata);
return n;
}
static int
smscpromiscuous(Ether *e, int on)
{
USED(on, e);
#ifdef TODO /* copied from asix */
int rxctl;
deprint(2, "%s: smscpromiscuous %d\n", argv0, on);
rxctl = getrxctl(e->dev);
if(on != 0)
rxctl |= Rxctlprom;
else
rxctl &= ~Rxctlprom;
return wr(e->dev, Cwrxctl, rxctl);
#endif
return -1;
}
static int
smscmulticast(Ether *e, uchar *addr, int on)
{
USED(addr, on, e);
#ifdef TODO /* needed for ipv6; copied from asix */
int rxctl;
/* BUG: should write multicast filter */
rxctl = getrxctl(e->dev);
if(e->nmcasts != 0)
rxctl |= Rxctlamall;
else
rxctl &= ~Rxctlamall;
deprint(2, "%s: smscmulticast %d\n", argv0, e->nmcasts);
return wr(e->dev, Cwrxctl, rxctl);
#endif
return -1;
}
static void
smscfree(Ether *ether)
{
free(ether->aux);
ether->aux = nil;
}
int
smscreset(Ether *ether)
{
Cinfo *ip;
Dev *dev;
dev = ether->dev;
for(ip = cinfo; ip->vid != 0; ip++)
if(ip->vid == dev->usb->vid && ip->did == dev->usb->did){
ether->cid = ip->cid;
if(smscinit(ether) < 0){
deprint(2, "%s: smsc init failed: %r\n", argv0);
return -1;
}
deprint(2, "%s: smsc reset done\n", argv0);
ether->name = "smsc";
if(Doburst){
ether->bufsize = Hsburst*512;
ether->aux = emallocz(sizeof(Buf) +
ether->bufsize - Maxpkt, 1);
}else{
ether->bufsize = Maxpkt;
ether->aux = emallocz(sizeof(Buf), 1);
}
ether->free = smscfree;
ether->bread = smscbread;
ether->bwrite = smscbwrite;
ether->promiscuous = smscpromiscuous;
ether->multicast = smscmulticast;
ether->mbps = 100; /* BUG */
return 0;
}
return -1;
}