/* $NetBSD: enic.c,v 1.4 2014/02/24 22:34:08 christos Exp $ */ /*- * Copyright (c) 2010 The NetBSD Foundation, Inc. * All rights reserved. * * This code was written by Alessandro Forin and Neil Pittman * at Microsoft Research and contributed to The NetBSD Foundation * by Microsoft Corporation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ /* -------------------------------------------------------------------------- * * Module: * * enic.c * * Purpose: * * Driver for the Microsoft eNIC (eMIPS system) Ethernet * * Author: * A. Forin (sandrof) * * References: * Internal Microsoft specifications, file eNIC_Design.docx titled * "eNIC: a simple Ethernet" Revision 4/30/99 * * Giano simulation module, file Peripherals\enic.cpp * * Various other drivers I wrote for said hardware * -------------------------------------------------------------------------- */ #include #include #include #include #include #include #include #include #include #include #include #include "start.h" #include "common.h" #define the_enic ((struct _Enic *)ETHERNET_DEFAULT_ADDRESS) /* forward declarations */ static int enicprobe (struct netif *, void *); static int enicmatch (struct netif *, void *); static void enicinit (struct iodesc *, void *); static int enicget (struct iodesc *, void *, size_t, saseconds_t); static int enicput (struct iodesc *, void *, size_t); static void enicend (struct netif *); #ifdef NET_DEBUG static void dump_packet(void *, int); #endif /* BUGBUG do we have this? */ #define kvtophys(_v_) ((paddr_t)(_v_) & ~0x80000000) #define rpostone(_r_,_p_,_s_) \ (_r_)->SizeAndFlags = ES_F_RECV | (_s_); \ (_r_)->BufferAddressHi32 = 0; \ (_r_)->BufferAddressLo32 = _p_; #define tpostone(_r_,_p_,_s_) \ (_r_)->SizeAndFlags = ES_F_XMIT | (_s_); \ (_r_)->BufferAddressHi32 = 0; \ (_r_)->BufferAddressLo32 = _p_; /* Send a packet */ static int enic_putpkt(struct _Enic *regs, void *buf, int bytes) { paddr_t phys = kvtophys(buf); tpostone(regs,phys,bytes); /* poll till done? */ //printf("\tenic: sent %d at %x\n",bytes,phys); return bytes; } /* Get a packet */ static int enic_getpkt(struct _Enic *regs, void *buf, int bytes, int timeo) { paddr_t phys; unsigned int isr, saf, hi, lo, fl; phys = kvtophys(buf); rpostone(regs,phys,bytes); //printf("\tenic: recv %d at %x\n",bytes,phys); /* poll till we get some */ timeo += getsecs(); for (;;) { /* anything there? */ isr = regs->Control; if (isr & EC_ERROR) { printf("enic: internal error %x\n", isr); regs->Control = EC_RESET; break; } //printf("isr %x ",isr); if ((isr & (EC_DONE|EC_OF_EMPTY)) == EC_DONE) { /* beware, order matters */ saf = regs->SizeAndFlags; hi = regs->BufferAddressHi32; /* BUGBUG 64bit */ lo = regs->BufferAddressLo32; /* this pops the fifo */ __USE(hi); fl = saf & (ES_F_MASK &~ ES_F_DONE); if (fl == ES_F_RECV) { /* and we are done? */ if (phys == lo) return saf & ES_S_MASK; } else if (fl == ES_F_XMIT) { ;/* nothing */ } else if (fl != ES_F_CMD) { printf("enic: invalid saf=x%x (lo=%x, hi=%x)\n", saf, lo, hi); } } if (getsecs() >= timeo) { //printf("enic: timeout\n"); regs->Control = EC_RESET; break; } } return 0; } /* */ static int enic_getmac(struct _Enic *regs, uint8_t *mac) { uint8_t buffer[8]; paddr_t phys = kvtophys(&buffer[0]); int i; regs->Control = EC_RESET; Delay(1); regs->Control = regs->Control & (~EC_RXDIS); buffer[0] = ENIC_CMD_GET_ADDRESS; //printf("%x:%x:%x:%x:%x:%x\n",buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]); regs->SizeAndFlags = (sizeof buffer) | ES_F_CMD; regs->BufferAddressHi32 = 0; regs->BufferAddressLo32 = phys; /* go! */ for (i = 0; i < 100; i++) { Delay(100); if (0 == (regs->Control & EC_OF_EMPTY)) break; } if (i == 100) return 0; //printf("%x:%x:%x:%x:%x:%x\n",buffer[0],buffer[1],buffer[2],buffer[3],buffer[4],buffer[5]); memcpy(mac,buffer,6); return 1; } /* Exported interface */ int enic_present(int unit) { if ((unit != 0) || (the_enic->Tag != PMTTAG_ETHERNET)) return 0; return 1; } extern int try_bootp; extern struct netif_stats enicstats[]; struct netif_dif enicifs[] = { /* dif_unit dif_nsel dif_stats dif_private */ { 0, 1, &enicstats[0], the_enic, }, }; #define NENICIFS (sizeof(enicifs) / sizeof(enicifs[0])) struct netif_stats enicstats[NENICIFS]; struct netif_driver enic_netif_driver = { "enic", /* netif_bname */ enicmatch, /* netif_match */ enicprobe, /* netif_probe */ enicinit, /* netif_init */ enicget, /* netif_get */ enicput, /* netif_put */ enicend, /* netif_end */ enicifs, /* netif_ifs */ NENICIFS /* netif_nifs */ }; static int enicmatch(struct netif *nif, void *machdep_hint) { return (1); } /* NB: We refuse anything but unit==0 */ static int enicprobe(struct netif *nif, void *machdep_hint) { int unit = nif->nif_unit; #ifdef NET_DEBUG printf("enic%d: probe\n", unit); #endif return (enic_present(unit) ? 0 : 1); } static void enicinit(struct iodesc *desc, void *machdep_hint) { #ifdef NET_DEBUG struct netif *nif = (struct netif *)desc->io_netif; int unit = nif->nif_driver->netif_ifs->dif_unit; printf("enic%d: init %s\n", unit, machdep_hint); #endif /* * Yes we wan tDHCP adn this is our MAC */ try_bootp = 1; enic_getmac(the_enic,desc->myea); desc->xid = 0xfe63d095; } static int enicput(struct iodesc *desc, void *pkt, size_t len) { #ifdef NET_DEBUG if (debug) dump_packet(pkt,len); #endif return enic_putpkt(the_enic,pkt,len); } int enicget(struct iodesc *desc, void *pkt, size_t len, saseconds_t timeout) { #ifdef NET_DEBUG printf("enicget: %lx %lx\n",len,timeout); #endif return enic_getpkt(the_enic,pkt,len,timeout); } static void enicend(struct netif *nif) { /* BUGBUG stop it in reset? */ } #ifdef NET_DEBUG static void dump_packet(void *pkt, int len) { struct ether_header *eh = (struct ether_header *)pkt; struct ip *ih = (struct ip *)(eh + 1); printf("ether_dhost = %s\n", ether_sprintf(eh->ether_dhost)); printf("ether_shost = %s\n", ether_sprintf(eh->ether_shost)); printf("ether_type = 0x%x\n", ntohs(eh->ether_type)); if (ntohs(eh->ether_type) == 0x0800) { printf("ip packet version %d\n", ih->ip_v); printf("source ip: 0x%x\n", ih->ip_src.s_addr); printf("dest ip: 0x%x\n", ih->ip_dst.s_addr); } } #endif