From: Ben Gras Date: Sun, 4 Sep 2005 20:08:22 +0000 (+0000) Subject: Imported AMD LANCE driver by various authors and contributors. X-Git-Tag: v3.1.0~192 X-Git-Url: http://zhaoyanbai.com/repos/%22http:/www.isc.org/icons/zpipe.c?a=commitdiff_plain;h=664f7ff5c968cae45ae08b169d0500bbb3a6d888;p=minix.git Imported AMD LANCE driver by various authors and contributors. --- diff --git a/drivers/Makefile b/drivers/Makefile index aded41d11..4eb46eadf 100644 --- a/drivers/Makefile +++ b/drivers/Makefile @@ -31,6 +31,7 @@ all install depend clean: cd ./random && $(MAKE) $@ cd ./dp8390 && $(MAKE) $@ cd ./sb16 && $(MAKE) $@ + cd ./lance && $(MAKE) $@ image: cd ./libdriver && $(MAKE) build diff --git a/drivers/lance/Makefile b/drivers/lance/Makefile new file mode 100644 index 000000000..c66595ea5 --- /dev/null +++ b/drivers/lance/Makefile @@ -0,0 +1,44 @@ +# Makefile for Intel Pro/100 driver (FXP) +DRIVER = lance + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = .. + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lsysutil +#-lutils -ltimers + +OBJ = lance.o +LIBPCI = $d/libpci/pci.o $d/libpci/pci_table.o + +# build local binary +all build: $(DRIVER) +$(DRIVER): $(OBJ) $(LIBPCI) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBPCI) $(LIBS) + install -S 1k $(DRIVER) + +$(LIBPCI): + cd $d/libpci && $(MAKE) + +# install with other drivers +install: /usr/sbin/$(DRIVER) +/usr/sbin/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f *.o *.bak $(DRIVER) + +mkdepend: + /usr/bin/mkdep "$(CC) -E $(CPPFLAGS)" *.c ../libpci/*.c > .depend + +# Include generated dependencies. +include .depend diff --git a/drivers/lance/lance.c b/drivers/lance/lance.c new file mode 100644 index 000000000..dc111980a --- /dev/null +++ b/drivers/lance/lance.c @@ -0,0 +1,1792 @@ +/* + * lance.c + * + * This file contains a ethernet device driver for AMD LANCE based ethernet + * cards. + * + * The valid messages and their parameters are: + * + * m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR + * |------------+----------+---------+----------+---------+---------| + * | HARDINT | | | | | | + * |------------|----------|---------|----------|---------|---------| + * | DL_WRITE | port nr | proc nr | count | mode | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_WRITEV | port nr | proc nr | count | mode | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_READ | port nr | proc nr | count | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_READV | port nr | proc nr | count | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_INIT | port nr | proc nr | mode | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_GETSTAT | port nr | proc nr | | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_STOP | port_nr | | | | | + * |------------|----------|---------|----------|---------|---------| + * + * The messages sent are: + * + * m-type DL_POR T DL_PROC DL_COUNT DL_STAT DL_CLCK + * |------------|----------|---------|----------|---------|---------| + * |DL_TASK_REPL| port nr | proc nr | rd-count | err|stat| clock | + * |------------|----------|---------|----------|---------|---------| + * + * m_type m3_i1 m3_i2 m3_ca1 + * |------------+---------+-----------+---------------| + * |DL_INIT_REPL| port nr | last port | ethernet addr | + * |------------|---------|-----------|---------------| + * + * Created: Jul 27, 2002 by Kazuya Kodama + * + */ + +#define VERBOSE 0 + +#include "../drivers.h" + +#include +#include +#include +#include +#include + +#include + +#include "lance.h" +#include "../libpci/pci.h" +/*#include "proc.h"*/ + +#include + +/* Joren l'Ami */ +#define out_byte( x, y ) sys_outb( x, y ) +#define out_word( x, y ) sys_outw( x, y ) + +static U8_t in_byte(U16_t port) +{ + U8_t value; + int s; + if ((s=sys_inb(port, &value)) != OK) + printf( "lance: warning, sys_inb failed: %d\n", s ); + return value; +} + +static U16_t in_word( U16_t port) +{ + U16_t value; + int s; + if ((s=sys_inw(port, &value)) != OK) + printf( "lance: warning, sys_inw failed: %d\n", s ); + return value; +} +/* +#define in_byte( x ) inb( x ) +#define in_word( x ) inw( x ) +*/ +/* Joren l'Ami */ + +static ether_card_t ec_table[EC_PORT_NR_MAX]; +static int eth_tasknr= ANY; +static u16_t eth_ign_proto; + +/* Configuration */ +typedef struct ec_conf +{ + port_t ec_port; + int ec_irq; + phys_bytes ec_mem; + char *ec_envvar; +} ec_conf_t; + +/* We hardly use these. Just "LANCE0=on/off" "LANCE1=on/off" mean. */ +ec_conf_t ec_conf[]= /* Card addresses */ +{ + /* I/O port, IRQ, Buffer address, Env. var, Buf selector. */ + { 0x1000, 9, 0x00000, "LANCE0" }, + { 0xD000, 15, 0x00000, "LANCE1" }, +}; + +/* Actually, we use PCI-BIOS info. */ +PRIVATE struct pcitab +{ + u16_t vid; + u16_t did; + int checkclass; +} pcitab[]= +{ + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, 0 }, /* AMD LANCE */ + + { 0x0000, 0x0000, 0 } +}; +/* +struct pci_device pci_dev_list[] = { + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, + "AMD Lance/PCI", 0, 0, 0, 0, 0, 0}, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_LANCE, + "AMD Lance/PCI", 0, 0, 0, 0, 0, 0}, + {0, 0, NULL, 0, 0, 0, 0, 0, 0} +}; +*/ + +/* General */ +_PROTOTYPE( static void do_init, (message *mp) ); +_PROTOTYPE( static void ec_init, (ether_card_t *ec) ); +_PROTOTYPE( static void ec_confaddr, (ether_card_t *ec) ); +_PROTOTYPE( static void ec_reinit, (ether_card_t *ec) ); +_PROTOTYPE( static void ec_check_ints, (ether_card_t *ec) ); +_PROTOTYPE( static void conf_hw, (ether_card_t *ec) ); +/*_PROTOTYPE( static int ec_handler, (irq_hook_t *hook) );*/ +_PROTOTYPE( static void update_conf, (ether_card_t *ec, ec_conf_t *ecp) ); +_PROTOTYPE( static void mess_reply, (message *req, message *reply) ); +_PROTOTYPE( static void do_int, (ether_card_t *ec) ); +_PROTOTYPE( static void reply, + (ether_card_t *ec, int err, int may_block) ); +_PROTOTYPE( static void ec_reset, (ether_card_t *ec) ); +_PROTOTYPE( static void ec_send, (ether_card_t *ec) ); +_PROTOTYPE( static void ec_recv, (ether_card_t *ec) ); +_PROTOTYPE( static void do_vwrite, + (message *mp, int from_int, int vectored) ); +_PROTOTYPE( static void do_vread, (message *mp, int vectored) ); +_PROTOTYPE( static void get_userdata, + (int user_proc, vir_bytes user_addr, + vir_bytes count, void *loc_addr) ); +_PROTOTYPE( static void ec_user2nic, + (ether_card_t *dep, iovec_dat_t *iovp, + vir_bytes offset, int nic_addr, + vir_bytes count) ); +_PROTOTYPE( static void ec_nic2user, + (ether_card_t *ec, int nic_addr, + iovec_dat_t *iovp, vir_bytes offset, + vir_bytes count) ); +_PROTOTYPE( static int calc_iovec_size, (iovec_dat_t *iovp) ); +_PROTOTYPE( static void ec_next_iovec, (iovec_dat_t *iovp) ); +_PROTOTYPE( static void do_getstat, (message *mp) ); +_PROTOTYPE( static void put_userdata, + (int user_proc, + vir_bytes user_addr, vir_bytes count, + void *loc_addr) ); +_PROTOTYPE( static void do_stop, (message *mp) ); + +_PROTOTYPE( static void lance_dump, (void) ); +_PROTOTYPE( static void lance_stop, (void) ); +_PROTOTYPE( static void getAddressing, (int devind, ether_card_t *ec) ); + +/* probe+init LANCE cards */ +_PROTOTYPE( static int lance_probe, (ether_card_t *ec) ); +_PROTOTYPE( static void lance_init_card, (ether_card_t *ec) ); + +/* --- LANCE --- */ +/* General */ +#define Address unsigned long + + +#define virt_to_bus(x) (vir2phys((unsigned long)x)) +unsigned long vir2phys( unsigned long x ) +{ + int r; + unsigned long value; + + if ( (r=sys_umap( SELF, D, x, 4, &value )) != OK ) + panic( "lance", "sys_umap failed", r ); + + return value; +} + + +#define ETH_FRAME_LEN 1518 + +#define LANCE_MUST_PAD 0x00000001 +#define LANCE_ENABLE_AUTOSELECT 0x00000002 +#define LANCE_SELECT_PHONELINE 0x00000004 +#define LANCE_MUST_UNRESET 0x00000008 + +static const struct lance_chip_type +{ + int id_number; + const char *name; + int flags; +} chip_table[] = { + {0x0000, "LANCE 7990", /* Ancient lance chip. */ + LANCE_MUST_PAD + LANCE_MUST_UNRESET}, + {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */ + LANCE_ENABLE_AUTOSELECT}, + {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */ + LANCE_ENABLE_AUTOSELECT}, + {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */ + LANCE_ENABLE_AUTOSELECT}, + {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */ + LANCE_ENABLE_AUTOSELECT}, + {0x2621, "PCnet/PCI-II 79C970A", /* 79C970A PCInetPCI II. */ + LANCE_ENABLE_AUTOSELECT}, + {0x2625, "PCnet-FAST III 79C973",/* 79C973 PCInet-FAST III. */ + LANCE_ENABLE_AUTOSELECT}, + {0x2626, "PCnet/HomePNA 79C978", + LANCE_ENABLE_AUTOSELECT|LANCE_SELECT_PHONELINE}, + {0x0, "PCnet (unknown)", + LANCE_ENABLE_AUTOSELECT}, +}; + +/* ############## for LANCE device ############## */ +#define LANCE_ETH_ADDR 0x0 +#define LANCE_DATA 0x10 +#define LANCE_ADDR 0x12 +#define LANCE_RESET 0x14 +#define LANCE_BUS_IF 0x16 +#define LANCE_TOTAL_SIZE 0x18 + +/* Use 2^4=16 {Rx,Tx} buffers */ +#define LANCE_LOG_RX_BUFFERS 4 +#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS)) +#define RX_RING_MOD_MASK (RX_RING_SIZE - 1) +#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29) + +#define LANCE_LOG_TX_BUFFERS 4 +#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS)) +#define TX_RING_MOD_MASK (TX_RING_SIZE - 1) +#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29) + +/* for lance_interface */ +struct lance_init_block +{ + unsigned short mode; + unsigned char phys_addr[6]; + unsigned long filter[2]; + Address rx_ring; + Address tx_ring; +}; + +struct lance_rx_head +{ + union { + Address base; + unsigned char addr[4]; + } u; + short buf_length; /* 2s complement */ + short msg_length; +}; + +struct lance_tx_head +{ + union { + Address base; + unsigned char addr[4]; + } u; + short buf_length; /* 2s complement */ + short misc; +}; + +struct lance_interface +{ + struct lance_init_block init_block; + struct lance_rx_head rx_ring[RX_RING_SIZE]; + struct lance_tx_head tx_ring[TX_RING_SIZE]; + unsigned char rbuf[RX_RING_SIZE][ETH_FRAME_LEN]; + unsigned char tbuf[TX_RING_SIZE][ETH_FRAME_LEN]; +}; + +/* =============== global variables =============== */ +static struct lance_interface *lp; +static char lance[sizeof(struct lance_interface)+8]; +static int rx_slot_nr = 0; /* Rx-slot number */ +static int tx_slot_nr = 0; /* Tx-slot number */ +static int cur_tx_slot_nr = 0; /* Tx-slot number */ +static char isstored[TX_RING_SIZE]; /* Tx-slot in-use */ + + +/*===========================================================================* + * lance_task * + *===========================================================================*/ +void main( int argc, char **argv ) +{ + message m; + int i,irq,r; + ether_card_t *ec; + long v; + int fkeys, sfkeys; + + env_setargs( argc, argv ); + + fkeys = sfkeys = 0; bit_set( sfkeys, 7 ); + + if ( (r = fkey_map(&fkeys, &sfkeys)) != OK ) + printf( "Error registering key\n" ); + + if((eth_tasknr=getprocnr()) < 0) + panic("lance","couldn't get own proc nr", i); + + v= 0; + (void) env_parse("ETH_IGN_PROTO", "x", 0, &v, 0x0000L, 0xFFFFL); + eth_ign_proto= htons((u16_t) v); + + while (TRUE) + { + for (i=0;iec_irq != 0) + sys_irqenable(&ec->ec_hook); + } + + if ((r= receive(ANY, &m)) != OK) + panic( "lance", "receive failed", r); + + for (i=0;iec_irq != 0) + sys_irqdisable(&ec->ec_hook); + } + +/*printf( "." );*/ + + switch (m.m_type){ + case DL_WRITE: do_vwrite(&m, FALSE, FALSE); break; + case DL_WRITEV: do_vwrite(&m, FALSE, TRUE); break; + case DL_READ: do_vread(&m, FALSE); break; + case DL_READV: do_vread(&m, TRUE); break; + case DL_INIT: do_init(&m); break; + case DL_GETSTAT: do_getstat(&m); break; + case DL_STOP: do_stop(&m); break; + case FKEY_PRESSED: lance_dump(); break; + /*case HARD_STOP: lance_stop(); break;*/ + case SYS_SIG: + { + sigset_t set = m.NOTIFY_ARG; + if ( sigismember( &set, SIGKSTOP ) ) + lance_stop(); + } + break; + case HARD_INT: + for (i=0;imode != EC_ENABLED) + continue; + + /* + printf( "#.\n" ); + */ + + irq=ec->ec_irq; + /*if (ec->ec_int_pending)*/ + { + ec->ec_int_pending = 0; + ec_check_ints(ec); + do_int(ec); + } + } + break; + default: + panic( "lance", "illegal message", m.m_type); + } + } +} + +/*===========================================================================* + * lance_dump * + *===========================================================================*/ +static void lance_dump() +{ + ether_card_t *ec; + int i, isr; + unsigned short ioaddr; + + printf("\n"); + for (i= 0, ec = &ec_table[0]; imode == EC_DISABLED) + printf("lance port %d is disabled\n", i); + else if (ec->mode == EC_SINK) + printf("lance port %d is in sink mode\n", i); + + if (ec->mode != EC_ENABLED) + continue; + + printf("lance statistics of port %d:\n", i); + + printf("recvErr :%8ld\t", ec->eth_stat.ets_recvErr); + printf("sendErr :%8ld\t", ec->eth_stat.ets_sendErr); + printf("OVW :%8ld\n", ec->eth_stat.ets_OVW); + + printf("CRCerr :%8ld\t", ec->eth_stat.ets_CRCerr); + printf("frameAll :%8ld\t", ec->eth_stat.ets_frameAll); + printf("missedP :%8ld\n", ec->eth_stat.ets_missedP); + + printf("packetR :%8ld\t", ec->eth_stat.ets_packetR); + printf("packetT :%8ld\t", ec->eth_stat.ets_packetT); + printf("transDef :%8ld\n", ec->eth_stat.ets_transDef); + + printf("collision :%8ld\t", ec->eth_stat.ets_collision); + printf("transAb :%8ld\t", ec->eth_stat.ets_transAb); + printf("carrSense :%8ld\n", ec->eth_stat.ets_carrSense); + + printf("fifoUnder :%8ld\t", ec->eth_stat.ets_fifoUnder); + printf("fifoOver :%8ld\t", ec->eth_stat.ets_fifoOver); + printf("CDheartbeat:%8ld\n", ec->eth_stat.ets_CDheartbeat); + + printf("OWC :%8ld\t", ec->eth_stat.ets_OWC); + + ioaddr = ec->ec_port; + out_word(ioaddr+LANCE_ADDR, 0x00); + isr=in_word(ioaddr+LANCE_DATA); + printf("isr = 0x%x + 0x%x, flags = 0x%x\n", isr, + in_word(ioaddr+LANCE_DATA), ec->flags); + + printf("irq = %d\tioadr = %d\n", ec->ec_irq, ec->ec_port); + } + printf("virt_to_bus(begin) = 0x%x\t virt_to_bus(end) = 0x%x\n", + virt_to_bus(lance), virt_to_bus(lance + sizeof(lance)) ); +} + +/*===========================================================================* + * lance_stop * + *===========================================================================*/ +static void lance_stop() +{ + message mess; + int i; + + for (i= 0; iDL_PORT; + if (port < 0 || port >= EC_PORT_NR_MAX) + { + reply_mess.m_type= DL_INIT_REPLY; + reply_mess.m3_i1= ENXIO; + mess_reply(mp, &reply_mess); + return; + } + ec= &ec_table[port]; + strcpy(ec->port_name, "eth_card#0"); + ec->port_name[9] += port; + if (ec->mode == EC_DISABLED) + { + /* This is the default, try to (re)locate the device. */ + conf_hw(ec); + if (ec->mode == EC_DISABLED) + { + /* Probe failed, or the device is configured off. */ + reply_mess.m_type= DL_INIT_REPLY; + reply_mess.m3_i1= ENXIO; + mess_reply(mp, &reply_mess); + return; + } + if (ec->mode == EC_ENABLED) + ec_init(ec); + } + + if (ec->mode == EC_SINK) + { + ec->mac_address.ea_addr[0] = + ec->mac_address.ea_addr[1] = + ec->mac_address.ea_addr[2] = + ec->mac_address.ea_addr[3] = + ec->mac_address.ea_addr[4] = + ec->mac_address.ea_addr[5] = 0; + ec_confaddr(ec); + reply_mess.m_type = DL_INIT_REPLY; + reply_mess.m3_i1 = mp->DL_PORT; + reply_mess.m3_i2 = EC_PORT_NR_MAX; + *(ether_addr_t *) reply_mess.m3_ca1 = ec->mac_address; + mess_reply(mp, &reply_mess); + return; + } + assert(ec->mode == EC_ENABLED); + assert(ec->flags & ECF_ENABLED); + + ec->flags &= ~(ECF_PROMISC | ECF_MULTI | ECF_BROAD); + + if (mp->DL_MODE & DL_PROMISC_REQ) + ec->flags |= ECF_PROMISC | ECF_MULTI | ECF_BROAD; + if (mp->DL_MODE & DL_MULTI_REQ) + ec->flags |= ECF_MULTI; + if (mp->DL_MODE & DL_BROAD_REQ) + ec->flags |= ECF_BROAD; + + ec->client = mp->m_source; + ec_reinit(ec); + + reply_mess.m_type = DL_INIT_REPLY; + reply_mess.m3_i1 = mp->DL_PORT; + reply_mess.m3_i2 = EC_PORT_NR_MAX; + *(ether_addr_t *) reply_mess.m3_ca1 = ec->mac_address; + + mess_reply(mp, &reply_mess); +} + + +/*===========================================================================* + * do_int * + *===========================================================================*/ +static void do_int(ec) +ether_card_t *ec; +{ + if (ec->flags & (ECF_PACK_SEND | ECF_PACK_RECV)) + reply(ec, OK, TRUE); +} + +#if 0 +/*===========================================================================* + * ec_handler * + *===========================================================================*/ +static int ec_handler(hook) +irq_hook_t *hook; +{ + /* LANCE interrupt, send message and reenable interrupts. */ +#if 0 + printf(">> ec_handler(): \n"); +#endif + + structof(ether_card_t, ec_hook, hook)->ec_int_pending= 1; + + notify(eth_tasknr, HARD_INT); + + return 0; +} +#endif + +/*===========================================================================* + * conf_hw * + *===========================================================================*/ +static void conf_hw(ec) +ether_card_t *ec; +{ + static eth_stat_t empty_stat = {0, 0, 0, 0, 0, 0 /* ,... */ }; + + int ifnr; + ec_conf_t *ecp; + + ec->mode= EC_DISABLED; /* Superfluous */ + ifnr= ec-ec_table; + + ecp= &ec_conf[ifnr]; + update_conf(ec, ecp); + if (ec->mode != EC_ENABLED) + return; + + if (!lance_probe(ec)) + { + printf("%s: No ethernet card found on PCI-BIOS info.\n", + ec->port_name); + ec->mode= EC_DISABLED; + return; + } + + /* Allocate a memory segment, programmed I/O should set the + * memory segment (linmem) to zero. + */ + if (ec->ec_linmem != 0) + { + assert( 0 ); + /*phys2seg(&ec->ec_memseg, &ec->ec_memoff, ec->ec_linmem);*/ + } + +/* XXX */ if (ec->ec_linmem == 0) ec->ec_linmem= 0xFFFF0000; + + ec->flags = ECF_EMPTY; + ec->eth_stat = empty_stat; +} + + +/*===========================================================================* + * update_conf * + *===========================================================================*/ +static void update_conf(ec, ecp) +ether_card_t *ec; +ec_conf_t *ecp; +{ + long v; + static char ec_fmt[] = "x:d:x:x"; + + /* Get the default settings and modify them from the environment. */ + ec->mode= EC_SINK; + v= ecp->ec_port; + switch (env_parse(ecp->ec_envvar, ec_fmt, 0, &v, 0x0000L, 0xFFFFL)) { + case EP_OFF: + ec->mode= EC_DISABLED; + break; + case EP_ON: + case EP_SET: + ec->mode= EC_ENABLED; /* Might become disabled if + * all probes fail */ + break; + } + + ec->ec_port= v; + + v= ecp->ec_irq | DEI_DEFAULT; + (void) env_parse(ecp->ec_envvar, ec_fmt, 1, &v, 0L, + (long) NR_IRQ_VECTORS - 1); + ec->ec_irq= v; + + v= ecp->ec_mem; + (void) env_parse(ecp->ec_envvar, ec_fmt, 2, &v, 0L, 0xFFFFFL); + ec->ec_linmem= v; + + v= 0; + (void) env_parse(ecp->ec_envvar, ec_fmt, 3, &v, 0x2000L, 0x8000L); + ec->ec_ramsize= v; +} + + +/*===========================================================================* + * ec_init * + *===========================================================================*/ +static void ec_init(ec) +ether_card_t *ec; +{ + int i, r; + + /* General initialization */ + ec->flags = ECF_EMPTY; + /*disable_irq(ec->ec_irq);*/ + lance_init_card(ec); /* Get mac_address, etc ...*/ + + ec_confaddr(ec); + +#if VERBOSE + printf("%s: Ethernet address ", ec->port_name); + for (i= 0; i < 6; i++) + printf("%x%c", ec->mac_address.ea_addr[i], + i < 5 ? ':' : '\n'); +#endif + + /* Finish the initialization */ + ec->flags |= ECF_ENABLED; + + /* Set the interrupt handler */ + /*put_irq_handler(&ec->ec_hook, ec->ec_irq, ec_handler);*/ + ec->ec_hook = ec->ec_irq; + if ((r=sys_irqsetpolicy(ec->ec_irq, 0, &ec->ec_hook)) != OK) + printf("lance: error, couldn't set IRQ policy: %d\n", r); + +/* enable_irq(ec->ec_irq); */ + +/* enter_kdebug(">> ec_init():"); */ + + return; +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +static void reply(ec, err, may_block) +ether_card_t *ec; +int err; +int may_block; +{ + message reply; + int status,r; + clock_t now; + + status = 0; + if (ec->flags & ECF_PACK_SEND) + status |= DL_PACK_SEND; + if (ec->flags & ECF_PACK_RECV) + status |= DL_PACK_RECV; + + reply.m_type = DL_TASK_REPLY; + reply.DL_PORT = ec - ec_table; + reply.DL_PROC = ec->client; + reply.DL_STAT = status | ((u32_t) err << 16); + reply.DL_COUNT = ec->read_s; +#if 1 + if ((r=getuptime(&now)) != OK) + panic("lance", "getuptime() failed:", r); + reply.DL_CLCK = now; +#else + reply.DL_CLCK = 0; +#endif + + r = send(ec->client, &reply); +#if 1 + if (r == ELOCKED && may_block) + { +/* enter_kdebug(">> lance_task: ELOCKED!"); */ + return; + } +#endif + if (r < 0) + panic( "lance", "send failed:", r); + + ec->read_s = 0; + ec->flags &= ~(ECF_PACK_SEND | ECF_PACK_RECV); +} + + +/*===========================================================================* + * mess_reply * + *===========================================================================*/ +static void mess_reply(req, reply_mess) +message *req; +message *reply_mess; +{ + if (send(req->m_source, reply_mess) != OK) + panic( "lance", "unable to mess_reply", NO_NUM); +} + + +/*===========================================================================* + * ec_confaddr * + *===========================================================================*/ +static void ec_confaddr(ec) +ether_card_t *ec; +{ + int i; + char eakey[16]; + static char eafmt[]= "x:x:x:x:x:x"; + long v; + + /* User defined ethernet address? */ + strcpy(eakey, ec_conf[ec-ec_table].ec_envvar); + strcat(eakey, "_EA"); + + for (i= 0; i < 6; i++) + { + v= ec->mac_address.ea_addr[i]; + if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET) + break; + ec->mac_address.ea_addr[i]= v; + } + + if (i != 0 && i != 6) + { + /* It's all or nothing; force a panic. */ + (void) env_parse(eakey, "?", 0, &v, 0L, 0L); + } +} + + +/*===========================================================================* + * ec_reinit * + *===========================================================================*/ +static void ec_reinit(ec) +ether_card_t *ec; +{ + int i; + unsigned short ioaddr = ec->ec_port; + + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x4); /* stop */ + + /* purge Tx-ring */ + tx_slot_nr = cur_tx_slot_nr = 0; + for (i=0; itx_ring[i].u.base = 0; + isstored[i]=0; + } + + /* re-init Rx-ring */ + rx_slot_nr = 0; + for (i=0; irx_ring[i].buf_length = -ETH_FRAME_LEN; + lp->rx_ring[i].u.addr[3] |= 0x80; + } + + /* Set 'Receive Mode' */ + if (ec->flags & ECF_PROMISC) + { + out_word(ioaddr+LANCE_ADDR, 0xf); + out_word(ioaddr+LANCE_DATA, 0x8000); + } + else + { + if (ec->flags & (ECF_BROAD | ECF_MULTI)) + { + out_word(ioaddr+LANCE_ADDR, 0xf); + out_word(ioaddr+LANCE_DATA, 0x0000); + } + else + { + out_word(ioaddr+LANCE_ADDR, 0xf); + out_word(ioaddr+LANCE_DATA, 0x4000); + } + } + + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x142); /* start && enable interrupt */ + + return; +} + +/*===========================================================================* + * ec_check_ints * + *===========================================================================*/ +static void ec_check_ints(ec) +ether_card_t *ec; +{ + int must_restart=0; + int check,status; + int isr=0x0000; + unsigned short ioaddr = ec->ec_port; + + if (!(ec->flags & ECF_ENABLED)) + panic( "lance", "got premature interrupt", NO_NUM); + + for (;;) + { +#if 0 + printf("ETH: Reading ISR..."); +#endif + out_word(ioaddr+LANCE_ADDR, 0x00); + isr=in_word(ioaddr+LANCE_DATA); + if (isr & 0x8600) + out_word( ioaddr+LANCE_DATA, isr & ~0x004f); + out_word(ioaddr+LANCE_DATA, 0x7940); +#if 0 + printf("ISR=0x%x...",in_word(ioaddr+LANCE_DATA)); +#endif +#define ISR_WINT 0x0200 +#define ISR_RINT 0x0400 +#define ISR_RERR 0x1000 +#define ISR_WERR 0x4000 +#define ISR_ERR 0x8000 +#define ISR_RST 0x0000 + + if ((isr & (ISR_WINT|ISR_RINT|ISR_RERR|ISR_WERR|ISR_ERR)) == 0x0000) + { +#if 0 + printf("OK\n"); +#endif + break; + } + + if (isr & ISR_RERR) + { +#if 0 + printf("RERR\n"); +#endif + ec->eth_stat.ets_recvErr++; + } + if ((isr & ISR_WERR) || (isr & ISR_WINT)) + { + if (isr & ISR_WERR) + { +#if 0 + printf("WERR\n"); +#endif + ec->eth_stat.ets_sendErr++; + } + if (isr & ISR_WINT) + { +#if 0 + printf("WINT\n"); +#endif + /* status check: restart if needed. */ + status = lp->tx_ring[cur_tx_slot_nr].u.base; + + /* ??? */ + if (status & 0x40000000) + { + status = lp->tx_ring[cur_tx_slot_nr].misc; + ec->eth_stat.ets_sendErr++; + if (status & 0x0400) + ec->eth_stat.ets_transAb++; + if (status & 0x0800) + ec->eth_stat.ets_carrSense++; + if (status & 0x1000) + ec->eth_stat.ets_OWC++; + if (status & 0x4000) + { + ec->eth_stat.ets_fifoUnder++; + must_restart=1; + } + } + else + { + if (status & 0x18000000) + ec->eth_stat.ets_collision++; + ec->eth_stat.ets_packetT++; + } + } + /* transmit a packet on the next slot if it exists. */ + check = 0; + if (isstored[cur_tx_slot_nr]==1) + { + /* free the tx-slot just transmitted */ + isstored[cur_tx_slot_nr]=0; + cur_tx_slot_nr = (++cur_tx_slot_nr) & TX_RING_MOD_MASK; + + /* next tx-slot is ready? */ + if (isstored[cur_tx_slot_nr]==1) + check=1; + else + check=0; + } + else + { + panic( "lance", "got premature WINT...", NO_NUM); + } + if (check==1) + { + lp->tx_ring[cur_tx_slot_nr].u.addr[3] = 0x83; + out_word(ioaddr+LANCE_ADDR, 0x0000); + out_word(ioaddr+LANCE_DATA, 0x0048); + } + else + if (check==-1) + continue; + /* we set a buffered message in the slot if it exists. */ + /* and transmit it, if needed. */ + if (ec->flags & ECF_SEND_AVAIL) + ec_send(ec); + } + if (isr & ISR_RINT) + { +#if 0 + printf("RINT\n"); +#endif + ec_recv(ec); + } + + if (isr & ISR_RST) + { + ec->flags = ECF_STOPPED; + break; + } + + /* ??? cf. lance driver on linux */ + if (must_restart == 1) + { +#if 0 + printf("ETH: restarting...\n"); +#endif + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x4); /* stop */ + out_word(ioaddr+LANCE_DATA, 0x2); /* start */ + } + } + + if ((ec->flags & (ECF_READING|ECF_STOPPED)) == (ECF_READING|ECF_STOPPED)) + { +#if 0 + printf("ETH: resetting...\n"); +#endif + ec_reset(ec); + } +} + +/*===========================================================================* + * ec_reset * + *===========================================================================*/ +static void ec_reset(ec) +ether_card_t *ec; +{ + /* Stop/start the chip, and clear all RX,TX-slots */ + unsigned short ioaddr = ec->ec_port; + int i; + + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x4); /* stop */ + out_word(ioaddr+LANCE_DATA, 0x2); /* start */ + + /* purge Tx-ring */ + tx_slot_nr = cur_tx_slot_nr = 0; + for (i=0; itx_ring[i].u.base = 0; + isstored[i]=0; + } + + /* re-init Rx-ring */ + rx_slot_nr = 0; + for (i=0; irx_ring[i].buf_length = -ETH_FRAME_LEN; + lp->rx_ring[i].u.addr[3] |= 0x80; + } + + /* store a buffered message on the slot if exists */ + ec_send(ec); + ec->flags &= ~ECF_STOPPED; +} + +/*===========================================================================* + * ec_send * + *===========================================================================*/ +static void ec_send(ec) +ether_card_t *ec; +{ + /* from ec_check_ints() or ec_reset(). */ + /* this function proccesses the buffered message. (slot/transmit) */ + if (!(ec->flags & ECF_SEND_AVAIL)) + return; + + ec->flags &= ~ECF_SEND_AVAIL; + switch(ec->sendmsg.m_type) + { + case DL_WRITE: do_vwrite(&ec->sendmsg, TRUE, FALSE); break; + case DL_WRITEV: do_vwrite(&ec->sendmsg, TRUE, TRUE); break; + default: + panic( "lance", "wrong type:", ec->sendmsg.m_type); + break; + } +} + +/*===========================================================================* + * do_vread * + *===========================================================================*/ +static void do_vread(mp, vectored) +message *mp; +int vectored; +{ + int port, count, size; + ether_card_t *ec; + + port = mp->DL_PORT; + count = mp->DL_COUNT; + ec= &ec_table[port]; + ec->client= mp->DL_PROC; + + if (vectored) + { + get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR, + (count > IOVEC_NR ? IOVEC_NR : count) * + sizeof(iovec_t), ec->read_iovec.iod_iovec); + ec->read_iovec.iod_iovec_s = count; + ec->read_iovec.iod_proc_nr = mp->DL_PROC; + ec->read_iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR; + + ec->tmp_iovec = ec->read_iovec; + size= calc_iovec_size(&ec->tmp_iovec); + } + else + { + ec->read_iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR; + ec->read_iovec.iod_iovec[0].iov_size = mp->DL_COUNT; + ec->read_iovec.iod_iovec_s = 1; + ec->read_iovec.iod_proc_nr = mp->DL_PROC; + ec->read_iovec.iod_iovec_addr = 0; + + size= count; + } + ec->flags |= ECF_READING; + + ec_recv(ec); + + if ((ec->flags & (ECF_READING|ECF_STOPPED)) == (ECF_READING|ECF_STOPPED)) + ec_reset(ec); + reply(ec, OK, FALSE); +} + +/*===========================================================================* + * ec_recv * + *===========================================================================*/ +static void ec_recv(ec) +ether_card_t *ec; +{ + vir_bytes length; + int packet_processed; + int status; + unsigned short ioaddr = ec->ec_port; + + if ((ec->flags & ECF_READING)==0) + return; + if (!(ec->flags & ECF_ENABLED)) + return; + + /* we check all the received slots until find a properly received packet */ + packet_processed = FALSE; + while (!packet_processed) + { + status = lp->rx_ring[rx_slot_nr].u.base >> 24; + if ( (status & 0x80) == 0x00 ) + { + status = lp->rx_ring[rx_slot_nr].u.base >> 24; + + /* ??? */ + if (status != 0x03) + { + if (status & 0x01) + ec->eth_stat.ets_recvErr++; + if (status & 0x04) + ec->eth_stat.ets_fifoOver++; + if (status & 0x08) + ec->eth_stat.ets_CRCerr++; + if (status & 0x10) + ec->eth_stat.ets_OVW++; + if (status & 0x20) + ec->eth_stat.ets_frameAll++; + length = 0; + } + else + { + ec->eth_stat.ets_packetR++; + length = lp->rx_ring[rx_slot_nr].msg_length; + } + if (length > 0) + { + ec_nic2user(ec, (int)(lp->rbuf[rx_slot_nr]), + &ec->read_iovec, 0, length); + + ec->read_s = length; + ec->flags |= ECF_PACK_RECV; + ec->flags &= ~ECF_READING; + packet_processed = TRUE; + } + /* set up this slot again, and we move to the next slot */ + lp->rx_ring[rx_slot_nr].buf_length = -ETH_FRAME_LEN; + lp->rx_ring[rx_slot_nr].u.addr[3] |= 0x80; + + out_word(ioaddr+LANCE_ADDR, 0x00); + out_word(ioaddr+LANCE_DATA, 0x7940); + + rx_slot_nr = (++rx_slot_nr) & RX_RING_MOD_MASK; + } + else + break; + } +} + +/*===========================================================================* + * do_vwrite * + *===========================================================================*/ +static void do_vwrite(mp, from_int, vectored) +message *mp; +int from_int; +int vectored; +{ + int port, count, check; + ether_card_t *ec; + unsigned short ioaddr; + + port = mp->DL_PORT; + count = mp->DL_COUNT; + ec = &ec_table[port]; + ec->client= mp->DL_PROC; + + if (isstored[tx_slot_nr]==1) + { + /* all slots are used, so this message is buffered */ + ec->sendmsg= *mp; + ec->flags |= ECF_SEND_AVAIL; + reply(ec, OK, FALSE); + return; + } + + /* convert the message to write_iovec */ + if (vectored) + { + get_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR, + (count > IOVEC_NR ? IOVEC_NR : count) * + sizeof(iovec_t), ec->write_iovec.iod_iovec); + + ec->write_iovec.iod_iovec_s = count; + ec->write_iovec.iod_proc_nr = mp->DL_PROC; + ec->write_iovec.iod_iovec_addr = (vir_bytes) mp->DL_ADDR; + + ec->tmp_iovec = ec->write_iovec; + ec->write_s = calc_iovec_size(&ec->tmp_iovec); + } + else + { + ec->write_iovec.iod_iovec[0].iov_addr = (vir_bytes) mp->DL_ADDR; + ec->write_iovec.iod_iovec[0].iov_size = mp->DL_COUNT; + + ec->write_iovec.iod_iovec_s = 1; + ec->write_iovec.iod_proc_nr = mp->DL_PROC; + ec->write_iovec.iod_iovec_addr = 0; + + ec->write_s = mp->DL_COUNT; + } + + /* copy write_iovec to the slot on DMA address */ + ec_user2nic(ec, &ec->write_iovec, 0, + (int)(lp->tbuf[tx_slot_nr]), ec->write_s); + /* set-up for transmitting, and transmit it if needed. */ + lp->tx_ring[tx_slot_nr].buf_length = -ec->write_s; + lp->tx_ring[tx_slot_nr].misc = 0x0; + lp->tx_ring[tx_slot_nr].u.base + = virt_to_bus(lp->tbuf[tx_slot_nr]) & 0xffffff; + isstored[tx_slot_nr]=1; + if (cur_tx_slot_nr == tx_slot_nr) + check=1; + else + check=0; + tx_slot_nr = (++tx_slot_nr) & TX_RING_MOD_MASK; + + if (check == 1) + { + ioaddr = ec->ec_port; + lp->tx_ring[cur_tx_slot_nr].u.addr[3] = 0x83; + out_word(ioaddr+LANCE_ADDR, 0x0000); + out_word(ioaddr+LANCE_DATA, 0x0048); + } + + ec->flags |= ECF_PACK_SEND; + + /* reply by calling do_int() if this function is called from interrupt. */ + if (from_int) + return; + reply(ec, OK, FALSE); +} + + +/*===========================================================================* + * get_userdata * + *===========================================================================*/ +static void get_userdata(user_proc, user_addr, count, loc_addr) +int user_proc; +vir_bytes user_addr; +vir_bytes count; +void *loc_addr; +{ + /* + phys_bytes src; + + src = numap_local(user_proc, user_addr, count); + if (!src) + panic( "lance", "umap failed", NO_NUM); + + phys_copy(src, vir2phys(loc_addr), (phys_bytes) count); + */ + int cps; + cps = sys_datacopy(user_proc, user_addr, SELF, (vir_bytes) loc_addr, count); + if (cps != OK) printf("lance: warning, scopy failed: %d\n", cps); +} + +/*===========================================================================* + * ec_user2nic * + *===========================================================================*/ +static void ec_user2nic(ec, iovp, offset, nic_addr, count) +ether_card_t *ec; +iovec_dat_t *iovp; +vir_bytes offset; +int nic_addr; +vir_bytes count; +{ + /*phys_bytes phys_hw, phys_user;*/ + int bytes, i, r; + + /* + phys_hw = vir2phys(nic_addr); + */ + i= 0; + while (count > 0) + { + if (i >= IOVEC_NR) + { + ec_next_iovec(iovp); + i= 0; + continue; + } + if (offset >= iovp->iod_iovec[i].iov_size) + { + offset -= iovp->iod_iovec[i].iov_size; + i++; + continue; + } + bytes = iovp->iod_iovec[i].iov_size - offset; + if (bytes > count) + bytes = count; + + /* + phys_user = numap_local(iovp->iod_proc_nr, + iovp->iod_iovec[i].iov_addr + offset, bytes); + + phys_copy(phys_user, phys_hw, (phys_bytes) bytes); + */ + if ( (r=sys_datacopy(iovp->iod_proc_nr, iovp->iod_iovec[i].iov_addr + offset, + SELF, nic_addr, count )) != OK ) + panic( "lance", "sys_datacopy failed", r ); + + count -= bytes; + nic_addr += bytes; + offset += bytes; + } +} + +/*===========================================================================* + * ec_nic2user * + *===========================================================================*/ +static void ec_nic2user(ec, nic_addr, iovp, offset, count) +ether_card_t *ec; +int nic_addr; +iovec_dat_t *iovp; +vir_bytes offset; +vir_bytes count; +{ + /*phys_bytes phys_hw, phys_user;*/ + int bytes, i, r; + + /*phys_hw = vir2phys(nic_addr);*/ + + i= 0; + while (count > 0) + { + if (i >= IOVEC_NR) + { + ec_next_iovec(iovp); + i= 0; + continue; + } + if (offset >= iovp->iod_iovec[i].iov_size) + { + offset -= iovp->iod_iovec[i].iov_size; + i++; + continue; + } + bytes = iovp->iod_iovec[i].iov_size - offset; + if (bytes > count) + bytes = count; + /* + phys_user = numap_local(iovp->iod_proc_nr, + iovp->iod_iovec[i].iov_addr + offset, bytes); + + phys_copy(phys_hw, phys_user, (phys_bytes) bytes); + */ + if ( (r=sys_datacopy( SELF, nic_addr, iovp->iod_proc_nr, iovp->iod_iovec[i].iov_addr + offset, bytes )) != OK ) + panic( "lance", "sys_datacopy failed: ", r ); + + count -= bytes; + nic_addr += bytes; + offset += bytes; + } +} + + +/*===========================================================================* + * calc_iovec_size * + *===========================================================================*/ +static int calc_iovec_size(iovp) +iovec_dat_t *iovp; +{ + int size,i; + + size = i = 0; + + while (i < iovp->iod_iovec_s) + { + if (i >= IOVEC_NR) + { + ec_next_iovec(iovp); + i= 0; + continue; + } + size += iovp->iod_iovec[i].iov_size; + i++; + } + + return size; +} + +/*===========================================================================* + * ec_next_iovec * + *===========================================================================*/ +static void ec_next_iovec(iovp) +iovec_dat_t *iovp; +{ + iovp->iod_iovec_s -= IOVEC_NR; + iovp->iod_iovec_addr += IOVEC_NR * sizeof(iovec_t); + + get_userdata(iovp->iod_proc_nr, iovp->iod_iovec_addr, + (iovp->iod_iovec_s > IOVEC_NR ? + IOVEC_NR : iovp->iod_iovec_s) * sizeof(iovec_t), + iovp->iod_iovec); +} + + +/*===========================================================================* + * do_getstat * + *===========================================================================*/ +static void do_getstat(mp) +message *mp; +{ + int port; + ether_card_t *ec; + + port = mp->DL_PORT; + if (port < 0 || port >= EC_PORT_NR_MAX) + panic( "lance", "illegal port", port); + + ec= &ec_table[port]; + ec->client= mp->DL_PROC; + + put_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR, + (vir_bytes) sizeof(ec->eth_stat), &ec->eth_stat); + reply(ec, OK, FALSE); +} + +/*===========================================================================* + * put_userdata * + *===========================================================================*/ +static void put_userdata(user_proc, user_addr, count, loc_addr) +int user_proc; +vir_bytes user_addr; +vir_bytes count; +void *loc_addr; +{ + /*phys_bytes dst; + + dst = numap_local(user_proc, user_addr, count); + if (!dst) + panic( "lance", "umap failed", NO_NUM); + + phys_copy(vir2phys(loc_addr), dst, (phys_bytes) count); + */ + int cps; + cps = sys_datacopy(SELF, (vir_bytes) loc_addr, user_proc, user_addr, count); + if (cps != OK) printf("lance: warning, scopy failed: %d\n", cps); +} + +/*===========================================================================* + * do_stop * + *===========================================================================*/ +static void do_stop(mp) +message *mp; +{ + int port; + ether_card_t *ec; + unsigned short ioaddr; + + port = mp->DL_PORT; + if (port < 0 || port >= EC_PORT_NR_MAX) + panic( "lance", "illegal port", port); + ec = &ec_table[port]; + + if (!(ec->flags & ECF_ENABLED)) + return; + + ioaddr = ec->ec_port; + + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x4); /* stop */ + out_word(ioaddr+LANCE_RESET, in_word(ioaddr+LANCE_RESET)); /* reset */ + + ec->flags = ECF_EMPTY; +} + +static void getAddressing(devind, ec) +int devind; +ether_card_t *ec; +{ + unsigned int membase, ioaddr; + int reg, irq; + + for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) + { + ioaddr = pci_attr_r32(devind, reg); + + if ((ioaddr & PCI_BASE_ADDRESS_IO_MASK) == 0 + || (ioaddr & PCI_BASE_ADDRESS_SPACE_IO) == 0) + continue; + /* Strip the I/O address out of the returned value */ + ioaddr &= PCI_BASE_ADDRESS_IO_MASK; + /* Get the memory base address */ + membase = pci_attr_r32(devind, PCI_BASE_ADDRESS_1); + /* KK: Get the IRQ number */ + irq = pci_attr_r8(devind, PCI_INTERRUPT_PIN); + if (irq) + irq = pci_attr_r8(devind, PCI_INTERRUPT_LINE); + /* Get the ROM base address */ + /*pci_attr_r32(devind, PCI_ROM_ADDRESS, &romaddr); + romaddr >>= 10;*/ + /* Take the first one or the one that matches in boot ROM address */ + /*if (pci_ioaddr == 0 + || romaddr == ((unsigned long) rom.rom_segment << 4)) {*/ + ec->ec_linmem = membase; + ec->ec_port = ioaddr; + ec->ec_irq = irq; + /*}*/ + } +} + +/*===========================================================================* + * lance_probe * + *===========================================================================*/ +static int lance_probe(ec) +ether_card_t *ec; +{ + unsigned short pci_cmd, attached = 0; + unsigned short ioaddr; + int lance_version, chip_version; + int devind, just_one, i, r; + + u16_t vid, did; + u32_t bar; + u8_t ilr; + char *dname; + + if ((ec->ec_pcibus | ec->ec_pcidev | ec->ec_pcifunc) != 0) + { + /* Look for specific PCI device */ + r= pci_find_dev(ec->ec_pcibus, ec->ec_pcidev, + ec->ec_pcifunc, &devind); + if (r == 0) + { + printf("%s: no PCI found at %d.%d.%d\n", + ec->port_name, ec->ec_pcibus, + ec->ec_pcidev, ec->ec_pcifunc); + return 0; + } + pci_ids(devind, &vid, &did); + just_one= TRUE; + } + else + { + r= pci_first_dev(&devind, &vid, &did); + if (r == 0) + return 0; + just_one= FALSE; + } + + for(;;) + { + for (i= 0; pcitab[i].vid != 0; i++) + { + if (pcitab[i].vid != vid) + continue; + if (pcitab[i].did != did) + continue; + if (pcitab[i].checkclass) + { + panic("lance", + "class check not implemented", NO_NUM); + } + break; + } + if (pcitab[i].vid != 0) + break; + + if (just_one) + { + printf( + "%s: wrong PCI device (%04x/%04x) found at %d.%d.%d\n", + ec->port_name, vid, did, + ec->ec_pcibus, + ec->ec_pcidev, ec->ec_pcifunc); + return 0; + } + + r= pci_next_dev(&devind, &vid, &did); + if (!r) + return 0; + } + + dname= pci_dev_name(vid, did); + if (!dname) + dname= "unknown device"; + + /* + printf("%s: ", ec->port_name); + printf("%s ", dname); + printf("(%x/", vid); + printf("%x) ", did); + printf("at %s\n", pci_slot_name(devind)); + */ + pci_reserve(devind); + +/* for (i = 0; pci_dev_list[i].vendor != 0; i++) { + if (pci_dev_list[i].suffix == 1) + { + ec->ec_port = pci_dev_list[i].ioaddr; + ec->ec_irq = pci_dev_list[i].irq; + ec->ec_linmem = pci_dev_list[i].membase; + ec->ec_bus = pci_dev_list[i].bus; + ec->ec_dev = pci_dev_list[i].devfn; + ec->ec_fnc = + pci_dev_list[i].suffix = -1; + attached = 1; + break; + } + } + if (attached == 0) + return 0; +*/ + getAddressing(devind, ec); + + /* ===== Bus Master ? ===== */ + /*pcibios_read_config_word(ec->ec_bus, ec->ec_devfn, PCI_COMMAND, &pci_cmd);*/ + pci_cmd = pci_attr_r32(devind, PCI_CR); + if (!(pci_cmd & PCI_COMMAND_MASTER)) { + pci_cmd |= PCI_COMMAND_MASTER; + /*pcibios_write_config_word(ec->ec_bus, ec->ec_devfn, PCI_COMMAND, pci_cmd);*/ + pci_attr_w32(devind, PCI_CR, pci_cmd); + } + + /* ===== Probe Details ===== */ + ioaddr = ec->ec_port; + + out_word(ioaddr+LANCE_RESET, in_word(ioaddr+LANCE_RESET)); /* Reset */ + out_word(ioaddr+LANCE_ADDR, 0x0); /* Sw to win 0 */ + if (in_word(ioaddr+LANCE_DATA) != 0x4) + { + ec->mode=EC_DISABLED; + } + /* Probe Chip Version */ + out_word(ioaddr+LANCE_ADDR, 88); /* Get the version of the chip */ + if (in_word(ioaddr+LANCE_ADDR) != 88) + lance_version = 0; + else + { + chip_version = in_word(ioaddr+LANCE_DATA); + out_word(ioaddr+LANCE_ADDR, 89); + chip_version |= in_word(ioaddr+LANCE_DATA) << 16; + if ((chip_version & 0xfff) != 0x3) + { + ec->mode=EC_DISABLED; + } + chip_version = (chip_version >> 12) & 0xffff; + for (lance_version = 1; chip_table[lance_version].id_number != 0; + ++lance_version) + if (chip_table[lance_version].id_number == chip_version) + break; + } + +#if 0 + printf("%s: %s at %X:%d\n", + ec->port_name, chip_table[lance_version].name, + ec->ec_port, ec->ec_irq); +#endif + + return lance_version; +} + + +/*===========================================================================* + * lance_init_card * + *===========================================================================*/ +static void lance_init_card(ec) +ether_card_t *ec; +{ + int i; + Address l; + unsigned short ioaddr = ec->ec_port; + + /* ============= setup init_block(cf. lance_probe1) ================ */ + /* make sure data structure is 8-byte aligned */ + l = ((Address)lance + 7) & ~7; + lp = (struct lance_interface *)l; + lp->init_block.mode = 0x3; /* disable Rx and Tx */ + lp->init_block.filter[0] = lp->init_block.filter[1] = 0x0; + /* using multiple Rx/Tx buffer */ + lp->init_block.rx_ring + = (virt_to_bus(&lp->rx_ring) & 0xffffff) | RX_RING_LEN_BITS; + lp->init_block.tx_ring + = (virt_to_bus(&lp->tx_ring) & 0xffffff) | TX_RING_LEN_BITS; + + l = virt_to_bus(&lp->init_block); + out_word(ioaddr+LANCE_ADDR, 0x1); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, (unsigned short)l); + out_word(ioaddr+LANCE_ADDR, 0x2); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, (unsigned short)(l >> 16)); + out_word(ioaddr+LANCE_ADDR, 0x4); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x915); + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + + /* ============= Get MAC address (cf. lance_probe1) ================ */ + for (i = 0; i < 6; ++i) + ec->mac_address.ea_addr[i]=in_byte(ioaddr+LANCE_ETH_ADDR+i); + + /* ============ (re)start init_block(cf. lance_reset) =============== */ + /* Reset the LANCE */ + (void)in_word(ioaddr+LANCE_RESET); + + /* ----- Re-initialize the LANCE ----- */ + /* Set station address */ + for (i = 0; i < 6; ++i) + lp->init_block.phys_addr[i] = ec->mac_address.ea_addr[i]; + /* Preset the receive ring headers */ + for (i=0; irx_ring[i].buf_length = -ETH_FRAME_LEN; + /* OWN */ + lp->rx_ring[i].u.base = virt_to_bus(lp->rbuf[i]) & 0xffffff; + /* we set the top byte as the very last thing */ + lp->rx_ring[i].u.addr[3] = 0x80; + } + /* Preset the transmitting ring headers */ + for (i=0; itx_ring[i].u.base = 0; + isstored[i] = 0; + } + lp->init_block.mode = 0x0; /* enable Rx and Tx */ + + l = (Address)virt_to_bus(&lp->init_block); + out_word(ioaddr+LANCE_ADDR, 0x1); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, (short)l); + out_word(ioaddr+LANCE_ADDR, 0x2); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, (short)(l >> 16)); + out_word(ioaddr+LANCE_ADDR, 0x4); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x915); + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + + /* ----- start when init done. ----- */ + out_word(ioaddr+LANCE_DATA, 0x4); /* stop */ + out_word(ioaddr+LANCE_DATA, 0x1); /* init */ + for (i = 10000; i > 0; --i) + if (in_word(ioaddr+LANCE_DATA) & 0x100) + break; + + /* Set 'Multicast Table' */ + for (i=0;i<4;++i) + { + out_word(ioaddr+LANCE_ADDR, 0x8 + i); + out_word(ioaddr+LANCE_DATA, 0xffff); + } + + /* Set 'Receive Mode' */ + if (ec->flags & ECF_PROMISC) + { + out_word(ioaddr+LANCE_ADDR, 0xf); + out_word(ioaddr+LANCE_DATA, 0x8000); + } + else + { + if (ec->flags & (ECF_BROAD | ECF_MULTI)) + { + out_word(ioaddr+LANCE_ADDR, 0xf); + out_word(ioaddr+LANCE_DATA, 0x0000); + } + else + { + out_word(ioaddr+LANCE_ADDR, 0xf); + out_word(ioaddr+LANCE_DATA, 0x4000); + } + } + + out_word(ioaddr+LANCE_ADDR, 0x0); + (void)in_word(ioaddr+LANCE_ADDR); + out_word(ioaddr+LANCE_DATA, 0x142); /* start && enable interrupt */ + + return; +} diff --git a/drivers/lance/lance.h b/drivers/lance/lance.h new file mode 100644 index 000000000..f6b93ecdb --- /dev/null +++ b/drivers/lance/lance.h @@ -0,0 +1,103 @@ +/*#include "kernel.h"*/ +#include +#include + +/* PCI STUFF */ +#define PCI_BASE_ADDRESS_0 0x10 +#define PCI_BASE_ADDRESS_1 0x14 +#define PCI_BASE_ADDRESS_2 0x18 +#define PCI_BASE_ADDRESS_3 0x1c +#define PCI_BASE_ADDRESS_4 0x20 +#define PCI_BASE_ADDRESS_5 0x24 + +#define PCI_BASE_ADDRESS_IO_MASK (~0x03) +#define PCI_BASE_ADDRESS_SPACE_IO 0x01 +#define PCI_INTERRUPT_LINE 0x3c +#define PCI_INTERRUPT_PIN 0x3d + +#define PCI_COMMAND_MASTER 0x4 + +#define PCI_VENDOR_ID_AMD 0x1022 +#define PCI_DEVICE_ID_AMD_LANCE 0x2000 + + +/* supported max number of ether cards */ +#define EC_PORT_NR_MAX 2 + +/* macros for 'mode' */ +#define EC_DISABLED 0x0 +#define EC_SINK 0x1 +#define EC_ENABLED 0x2 + +/* macros for 'flags' */ +#define ECF_EMPTY 0x000 +#define ECF_PACK_SEND 0x001 +#define ECF_PACK_RECV 0x002 +#define ECF_SEND_AVAIL 0x004 +#define ECF_READING 0x010 +#define ECF_PROMISC 0x040 +#define ECF_MULTI 0x080 +#define ECF_BROAD 0x100 +#define ECF_ENABLED 0x200 +#define ECF_STOPPED 0x400 + +/* === macros for ether cards (our generalized version) === */ +#define EC_ISR_RINT 0x0001 +#define EC_ISR_WINT 0x0002 +#define EC_ISR_RERR 0x0010 +#define EC_ISR_WERR 0x0020 +#define EC_ISR_ERR 0x0040 +#define EC_ISR_RST 0x0100 + +/* IOVEC */ +#define IOVEC_NR 16 +typedef struct iovec_dat +{ + iovec_t iod_iovec[IOVEC_NR]; + int iod_iovec_s; + int iod_proc_nr; + vir_bytes iod_iovec_addr; +} iovec_dat_t; + +#define ETH0_SELECTOR 0x61 +#define ETH1_SELECTOR 0x69 + +/* ====== ethernet card info. ====== */ +typedef struct ether_card +{ + /* ####### MINIX style ####### */ + char port_name[sizeof("eth_card#n")]; + int flags; + int mode; + int transfer_mode; + eth_stat_t eth_stat; + iovec_dat_t read_iovec; + iovec_dat_t write_iovec; + iovec_dat_t tmp_iovec; + vir_bytes write_s; + vir_bytes read_s; + int client; + message sendmsg; + + /* ######## device info. ####### */ + port_t ec_port; + phys_bytes ec_linmem; + int ec_irq; + int ec_int_pending; + int ec_hook; + + int ec_ramsize; + /* PCI */ + u8_t ec_pcibus; + u8_t ec_pcidev; + u8_t ec_pcifunc; + + /* Addrassing */ + u16_t ec_memseg; + vir_bytes ec_memoff; + + ether_addr_t mac_address; +} ether_card_t; + +#define DEI_DEFAULT 0x8000 +