jehanne/sys/src/kern/amd64/devether.c

571 lines
12 KiB
C
Raw Normal View History

/* Copyright (C) Charles Forsyth
* See /doc/license/NOTICE.Plan9-9k.txt for details about the licensing.
*/
/* Portions of this file are Copyright (C) 9front's team.
* See /doc/license/9front-mit for details about the licensing.
* See http://code.9front.org/hg/plan9front/ for a list of authors.
*/
#include "u.h"
#include "../port/lib.h"
#include "mem.h"
#include "dat.h"
#include "fns.h"
#include "../port/error.h"
#include "io.h"
#include "../port/netif.h"
#include "etherif.h"
static Ether *etherxx[MaxEther];
Chan*
etherattach(Chan *c, Chan *ac, char *spec, int flags)
{
int ctlrno;
char *p;
Chan *chan;
ctlrno = 0;
if(spec && *spec){
ctlrno = jehanne_strtoul(spec, &p, 0);
if((ctlrno == 0 && p == spec) || *p != 0)
error(Ebadarg);
if(ctlrno < 0 || ctlrno >= MaxEther)
error(Ebadarg);
}
if(etherxx[ctlrno] == 0)
error(Enodev);
chan = devattach('l', spec);
if(waserror()){
chanfree(chan);
nexterror();
}
chan->devno = ctlrno;
if(etherxx[ctlrno]->attach)
etherxx[ctlrno]->attach(etherxx[ctlrno]);
poperror();
return chan;
}
static Walkqid*
etherwalk(Chan* chan, Chan* nchan, char** name, int nname)
{
return netifwalk(&etherxx[chan->devno]->netif, chan, nchan, name, nname);
}
static long
etherstat(Chan* chan, uint8_t* dp, long n)
{
return netifstat(&etherxx[chan->devno]->netif, chan, dp, n);
}
static Chan*
etheropen(Chan* chan, unsigned long omode)
{
return netifopen(&etherxx[chan->devno]->netif, chan, omode);
}
static Chan*
ethercreate(Chan* _1, char* _2, unsigned long _3, unsigned long _4)
{
error(Eperm);
return nil;
}
static void
etherclose(Chan* chan)
{
netifclose(&etherxx[chan->devno]->netif, chan);
}
static long
etherread(Chan* chan, void* buf, long n, int64_t off)
{
Ether *ether;
uint32_t offset = off;
ether = etherxx[chan->devno];
if((chan->qid.type & QTDIR) == 0 && ether->ifstat){
/*
* With some controllers it is necessary to reach
* into the chip to extract statistics.
*/
if(NETTYPE(chan->qid.path) == Nifstatqid)
return ether->ifstat(ether, buf, n, offset);
else if(NETTYPE(chan->qid.path) == Nstatqid)
ether->ifstat(ether, buf, 0, offset);
}
return netifread(&ether->netif, chan, buf, n, offset);
}
static Block*
etherbread(Chan* chan, long n, int64_t offset)
{
return netifbread(&etherxx[chan->devno]->netif, chan, n, offset);
}
static long
etherwstat(Chan* chan, uint8_t* dp, long n)
{
return netifwstat(&etherxx[chan->devno]->netif, chan, dp, n);
}
static void
etherrtrace(Netfile* f, Etherpkt* pkt, int len)
{
int i, n;
Block *bp;
if(qwindow(f->iq) <= 0)
return;
if(len > 58)
n = 58;
else
n = len;
bp = iallocb(64);
if(bp == nil)
return;
jehanne_memmove(bp->wp, pkt->d, n);
i = TK2MS(sys->ticks);
bp->wp[58] = len>>8;
bp->wp[59] = len;
bp->wp[60] = i>>24;
bp->wp[61] = i>>16;
bp->wp[62] = i>>8;
bp->wp[63] = i;
bp->wp += 64;
qpass(f->iq, bp);
}
Block*
etheriq(Ether* ether, Block* bp, int fromwire)
{
Etherpkt *pkt;
uint16_t type;
int len, multi, tome, fromme;
Netfile **ep, *f, **fp, *fx;
Block *xbp;
ether->netif.inpackets++;
pkt = (Etherpkt*)bp->rp;
len = BLEN(bp);
type = (pkt->type[0]<<8)|pkt->type[1];
fx = 0;
ep = &ether->netif.f[Ntypes];
multi = pkt->d[0] & 1;
/* check for valid multcast addresses */
if(multi && jehanne_memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) != 0 && ether->netif.prom == 0){
if(!activemulti(&ether->netif, pkt->d, sizeof(pkt->d))){
if(fromwire){
freeb(bp);
bp = 0;
}
return bp;
}
}
/* is it for me? */
tome = jehanne_memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
fromme = jehanne_memcmp(pkt->s, ether->ea, sizeof(pkt->s)) == 0;
/*
* Multiplex the packet to all the connections which want it.
* If the packet is not to be used subsequently (fromwire != 0),
* attempt to simply pass it into one of the connections, thereby
* saving a copy of the data (usual case hopefully).
*/
for(fp = ether->netif.f; fp < ep; fp++){
if(f = *fp)
if(f->type == type || f->type < 0)
if(tome || multi || f->prom){
/* Don't want to hear bridged packets */
if(f->bridge && !fromwire && !fromme)
continue;
if(!f->headersonly){
if(fromwire && fx == 0)
fx = f;
else if(xbp = iallocb(len)){
jehanne_memmove(xbp->wp, pkt, len);
xbp->wp += len;
xbp->flag = bp->flag;
if(qpass(f->iq, xbp) < 0)
ether->netif.inoverflows++;
}
else
ether->netif.inoverflows++;
}
else
etherrtrace(f, pkt, len);
}
}
if(fx){
if(qpass(fx->iq, bp) < 0)
ether->netif.inoverflows++;
return 0;
}
if(fromwire){
freeb(bp);
return 0;
}
return bp;
}
static int
etheroq(Ether* ether, Block* bp)
{
int len, loopback;
Etherpkt *pkt;
ether->netif.outpackets++;
/*
* Check if the packet has to be placed back onto the input queue,
* i.e. if it's a loopback or broadcast packet or the interface is
* in promiscuous mode.
* If it's a loopback packet indicate to etheriq that the data isn't
* needed and return, etheriq will pass-on or free the block.
* To enable bridging to work, only packets that were originated
* by this interface are fed back.
*/
pkt = (Etherpkt*)bp->rp;
len = BLEN(bp);
loopback = jehanne_memcmp(pkt->d, ether->ea, sizeof(pkt->d)) == 0;
if(loopback || jehanne_memcmp(pkt->d, ether->netif.bcast, sizeof(pkt->d)) == 0 || ether->netif.prom)
if(etheriq(ether, bp, loopback) == 0)
return len;
qbwrite(ether->netif.oq, bp);
if(ether->transmit != nil)
ether->transmit(ether);
return len;
}
static long
etherwrite(Chan* chan, void* buf, long n, int64_t _1)
{
Ether *ether;
Block *bp;
int nn, onoff;
Cmdbuf *cb;
ether = etherxx[chan->devno];
if(NETTYPE(chan->qid.path) != Ndataqid) {
nn = netifwrite(&ether->netif, chan, buf, n);
if(nn >= 0)
return nn;
cb = parsecmd(buf, n);
if(cb->f[0] && jehanne_strcmp(cb->f[0], "nonblocking") == 0){
if(cb->nf <= 1)
onoff = 1;
else
onoff = jehanne_atoi(cb->f[1]);
qnoblock(ether->netif.oq, onoff);
jehanne_free(cb);
return n;
}
jehanne_free(cb);
if(ether->ctl!=nil)
return ether->ctl(ether,buf,n);
error(Ebadctl);
}
if(n > ether->maxmtu)
error(Etoobig);
if(n < ether->minmtu)
error(Etoosmall);
bp = allocb(n);
if(waserror()){
freeb(bp);
nexterror();
}
jehanne_memmove(bp->rp, buf, n);
jehanne_memmove(bp->rp+Eaddrlen, ether->ea, Eaddrlen);
poperror();
bp->wp += n;
return etheroq(ether, bp);
}
static long
etherbwrite(Chan* chan, Block* bp, int64_t _1)
{
Ether *ether;
long n;
n = BLEN(bp);
if(NETTYPE(chan->qid.path) != Ndataqid){
if(waserror()) {
freeb(bp);
nexterror();
}
n = etherwrite(chan, bp->rp, n, 0);
poperror();
freeb(bp);
return n;
}
ether = etherxx[chan->devno];
if(n > ether->maxmtu){
freeb(bp);
error(Etoobig);
}
if(n < ether->minmtu){
freeb(bp);
error(Etoosmall);
}
return etheroq(ether, bp);
}
static struct {
char* type;
int (*reset)(Ether*);
} cards[MaxEther+1];
void
addethercard(char* t, int (*r)(Ether*))
{
static int ncard;
if(ncard == MaxEther)
panic("too many ether cards");
cards[ncard].type = t;
cards[ncard].reset = r;
ncard++;
}
int
parseether(uint8_t *to, char *from)
{
char nip[4];
char *p;
int i;
p = from;
for(i = 0; i < Eaddrlen; i++){
if(*p == 0)
return -1;
nip[0] = *p++;
if(*p == 0)
return -1;
nip[1] = *p++;
nip[2] = 0;
to[i] = jehanne_strtoul(nip, 0, 16);
if(*p == ':')
p++;
}
return 0;
}
static Ether*
etherprobe(int cardno, int ctlrno)
{
int i, lg;
unsigned long mb, bsz;
Ether *ether;
char buf[128], name[32];
ether = jehanne_malloc(sizeof(Ether));
if(ether == nil){
jehanne_print("etherprobe: no memory for Ether\n");
return nil;
}
jehanne_memset(ether, 0, sizeof(Ether));
ether->ctlrno = ctlrno;
ether->tbdf = BUSUNKNOWN;
ether->netif.mbps = 10;
ether->minmtu = ETHERMINTU;
ether->maxmtu = ETHERMAXTU;
if(cardno < 0){
if(isaconfig("ether", ctlrno, ether) == 0){
jehanne_free(ether);
return nil;
}
for(cardno = 0; cards[cardno].type; cardno++){
if(jehanne_cistrcmp(cards[cardno].type, ether->type))
continue;
for(i = 0; i < ether->nopt; i++){
if(jehanne_strncmp(ether->opt[i], "ea=", 3))
continue;
if(parseether(ether->ea, &ether->opt[i][3]))
jehanne_memset(ether->ea, 0, Eaddrlen);
}
break;
}
}
if(cardno >= MaxEther || cards[cardno].type == nil){
jehanne_free(ether);
return nil;
}
if(cards[cardno].reset(ether) < 0){
jehanne_free(ether);
return nil;
}
/*
* IRQ2 doesn't really exist, it's used to gang the interrupt
* controllers together. A device set to IRQ2 will appear on
* the second interrupt controller as IRQ9.
*/
if(ether->irq == 2)
ether->irq = 9;
jehanne_snprint(name, sizeof(name), "ether%d", ctlrno);
/*
* If ether->irq is <0, it is a hack to indicate no interrupt
* used by ethersink.
* Or perhaps the driver has some other way to configure
* interrupts for itself, e.g. HyperTransport MSI.
*/
if(ether->irq >= 0)
ether->vector = intrenable(ether->irq, ether->interrupt, ether, ether->tbdf, name);
i = jehanne_sprint(buf, "#l%d: %s: %dMbps port %#p irq %d",
ctlrno, cards[cardno].type, ether->netif.mbps, ether->port, ether->irq);
if(ether->mem)
i += jehanne_sprint(buf+i, " addr %#p", ether->mem);
if(ether->size)
i += jehanne_sprint(buf+i, " size %ld", ether->size);
i += jehanne_sprint(buf+i, ": %2.2ux%2.2ux%2.2ux%2.2ux%2.2ux%2.2ux",
ether->ea[0], ether->ea[1], ether->ea[2],
ether->ea[3], ether->ea[4], ether->ea[5]);
jehanne_sprint(buf+i, "\n");
jehanne_print(buf);
/* compute jehanne_log10(ether->mbps) into lg */
for(lg = 0, mb = ether->netif.mbps; mb >= 10; lg++)
mb /= 10;
if (lg > 0)
lg--;
if (lg > 14) /* 2^(14+17) = 2^31 */
lg = 14;
/* allocate larger output queues for higher-speed interfaces */
bsz = 1UL << (lg + 17); /* 2^17 = 128K, bsz = 2^n × 128K */
while (/* bsz > mainmem->maxsize / 8 && */bsz > 128*1024)
bsz /= 2;
netifinit(&ether->netif, name, Ntypes, bsz);
if(ether->netif.oq == nil) {
ether->netif.oq = qopen(bsz, Qmsg, 0, 0);
ether->netif.limit = bsz;
}
if(ether->netif.oq == 0)
panic("etherreset %s", name);
ether->netif.alen = Eaddrlen;
jehanne_memmove(ether->netif.addr, ether->ea, Eaddrlen);
jehanne_memset(ether->netif.bcast, 0xFF, Eaddrlen);
return ether;
}
static void
etherreset(void)
{
Ether *ether;
int cardno, ctlrno;
for(ctlrno = 0; ctlrno < MaxEther; ctlrno++){
if((ether = etherprobe(-1, ctlrno)) == nil)
continue;
etherxx[ctlrno] = ether;
}
if(getconf("*noetherprobe"))
return;
cardno = ctlrno = 0;
while(cards[cardno].type != nil && ctlrno < MaxEther){
if(etherxx[ctlrno] != nil){
ctlrno++;
continue;
}
if((ether = etherprobe(cardno, ctlrno)) == nil){
cardno++;
continue;
}
etherxx[ctlrno] = ether;
ctlrno++;
}
}
static void
ethershutdown(void)
{
Ether *ether;
int i;
for(i = 0; i < MaxEther; i++){
ether = etherxx[i];
if(ether == nil)
continue;
if(ether->shutdown == nil) {
jehanne_print("#l%d: no shutdown function\n", i);
continue;
}
(*ether->shutdown)(ether);
}
}
int
ethercfgmatch(Ether *e, Pcidev *p, uintmem port)
{
if((e->port == 0 || e->port == port) &&
(e->tbdf == BUSUNKNOWN || p == nil || e->tbdf == p->tbdf))
return 0;
return -1;
}
#define POLY 0xedb88320
/* really slow 32 bit crc for ethers */
uint32_t
ethercrc(uint8_t *p, int len)
{
int i, j;
uint32_t crc, b;
crc = 0xffffffff;
for(i = 0; i < len; i++){
b = *p++;
for(j = 0; j < 8; j++){
crc = (crc>>1) ^ (((crc^b) & 1) ? POLY : 0);
b >>= 1;
}
}
return crc;
}
Dev etherdevtab = {
'l',
"ether",
etherreset,
devinit,
ethershutdown,
etherattach,
etherwalk,
etherstat,
etheropen,
ethercreate,
etherclose,
etherread,
etherbread,
etherwrite,
etherbwrite,
devremove,
etherwstat,
};