# HG changeset patch # User jlarmour # Date 980496927 0 # Node ID f0e3fb000de88ca96d8ca2ba762c789507e01877 # Parent 52c99470ec11980407667b4c848213a3f80418e7 Merge from eCos master repository on 2001-01-26-06:43:02-GMT diff --git a/packages/ChangeLog b/packages/ChangeLog --- a/packages/ChangeLog +++ b/packages/ChangeLog @@ -1,3 +1,7 @@ +2001-01-25 Jesper Skov + + * ecos.db: Added generic i82559 ethernet driver. + 2000-11-25 Jonathan Larmour diff --git a/packages/devs/eth/intel/i82559/current/ChangeLog b/packages/devs/eth/intel/i82559/current/ChangeLog new file mode 100644 --- /dev/null +++ b/packages/devs/eth/intel/i82559/current/ChangeLog @@ -0,0 +1,463 @@ +2001-01-25 Jesper Skov + + * src/if_i82559.c: Replace CYGNUM_HAL_INTERRUPT_PCI_IRQ with + CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT which platform .inl + must provide when appropriate. Changed _deliver functions to match + _isr functions. + * include/i82559_info.h: Remove debug hacks. + Fix declaration. + + * src/if_i82559.c: Moved device descriptors into platform header. + Allow individual devices to hardwire ESA. Handle N devices instead + of just 1 or 2. + * include/i82559_info.h: ESA hardwired flag added. + * cdl/intel_i82559_eth_drivers.cdl: Device details moved to + platform CDL. + + * src/if_i82559.c (i82559_start): Poll status after dump command. + +2001-01-24 Jesper Skov + + * src/if_i82559.c: [changes from yesterday] Some more endian + fiddle, and the device coughs out the initial ARP packets. + (wait_for_cmd_done): semantics changed to wait for pending CU + cmds. + (i82559_reset): Cleaned up to spec. Init register bases after + reset. + Fix initialization of hardwired ESA. + Correct configuration command. + Fix rcv len masking. + (eth_set_mac_address): Set driver's ESA as well. Proper check for + completion. + Fix LE structure offsets. + (i82559_start): Call new eth_set_config to enable device. Last + hacks cleaned up. + Remove a few printfs. + +2001-01-23 Jesper Skov + + * src/if_i82559.c: Debug hackery and some endian issues resolved. + +2001-01-22 Jesper Skov + + * src/if_i82559.c: Major changes, getting rid of device structures + in favor of HAL IO accessor macros. Also added endian conversion + macros where required. + * include/i82559_info.h: Change type of device structures to char. + +2001-01-19 Jesper Skov + + * cdl/intel_i82559_eth_drivers.cdl: Hack for just one interface + now. + * src/if_i82559.c: Fix IO functions. Handle EEPROM not being + attached to device. + + * src/if_i82559.c: Use uncached/physical address translation + macros. + +2001-01-15 Jesper Skov + + * src/if_i82559.c: Handle endian differences between controller + and CPU. Leave usdelay to HAL. Hack around PCI-base-at-0 + assumption. + Increase SK_DELAY (not that it changed anything, but now it's to + the spec), hardwire static ESA. + +2001-01-12 Jesper Skov + + * src/if_i82559.c (pci_init_find_82559s): Check that device has + been found before accessing it. + (pci_init_find_82559s): Only delay if devices were + found. Recognize 82559ER code as well. + +2001-01-10 Jesper Skov + + * src/if_i82559.c: Minor hacks to get it to build. + * include/i82559_info.h: Same. + * cdl/intel_i82559_eth_drivers.cdl: Same. + +2000-12-07 Jesper Skov + + * cdl/intel_i82559_eth_drivers.cdl: Cloned from the EBSA driver. + * include/i82559_info.h: Same. + * src/if_i82559.c: Same. + +2000-11-19 Gary Thomas + + * src/if_ebsa285.c (pci_init_find_82559s): Intel has at least + two devices equivalent to the 82559. Support both (0x1229, 0x01030). + +2000-10-05 Hugo Tyson + + * src/if_ebsa285.c: Deal with device interrupts in a nested + fashion - disable/restore is the semantics now, rather than + unconditionally unmasking. Also go directly to the 21285 PIC's + interrupt control registers to gain atomicity for these. Poll for + ready received packets when acknowledging an interrupt in the + tranmitting world; a race here could lose an Rx interrupt. Which + doesn't matter on a busy system, but in quieter times... there + will always be such a race because of the vague way the i82559's + status bits reflect how it's yanking the interrupt line; you have + to poll until the interrupt is gone before returning else spurious + interrupt failures occur. The issue is to close the window as + tightly as possible, which this change achieves at a minor cost in + performance - because of the time spent polling when not required. + +2000-09-11 Hugo Tyson + + * src/if_ebsa285.c (i82559_poll): Only diddle the interface we + were asked to. This is more correct in terms of the intent of the + API, though it shouldn't really be necessary. + +2000-09-06 Hugo Tyson + + * src/if_ebsa285.c (pci_init_find_82559s): Add asserts and an + unconditional check that the PCI window as configured matches the + address and size of the pci_window region from the MLT. This is + here because at present the MLT and CT cannot intercommunicate + enough. The separation of the PCI window is needed because + otherwise the malloc heap will eat all memory. [This is related + to CR 902624-CR, "MLT needs to be configuration aware"] + +2000-09-01 Hugo Tyson + + * OVERVIEW: This is part of the change to the network stack to + greatly reduce latencies both of (other) DSRs and of thread + scheduling. All the work that the network stack *and* individual + ether drivers used to do in DSRs (including alarm callbacks and + data copies to/from the device memory) is moved into a "fast + network thread" instead. It calls a device's "deliver" function + to do the work that was previously in the DSR. This is a separate + thread so that it can be set higher priority than application + threads in order to minimize packet loss (depending on the + driver), if required (the application threads presumed to be + higher priority in turn than the network thread). A crucial + consequence of this is that we are no longer locking against DSRs, + so a plain mutex can be used rather than the global scheduler + lock, thus simplifying all the splfoo/splx() style functions. + + * src/if_ebsa285.c: Minor: fix the big assert in i82559_send() + which suffered a race condition when called from the fast thread + rather than from a DSR. Major: Add a "deliver" entry to the + interface record for the "fast thread" implementation of the + network internal comms system. Provide a pass-up DSR to the + logical ether driver's DSR and appropriate delivery routine(s). + i82559_poll() now calls i82559_deliver() rather than the DSR. Add + valid data for mux'd DSR to pass on up. + +2000-09-01 Hugo Tyson + + * tests/test_net_realtime.h: Tighten up the latency requirements + by a factor of 5; it all seems happy, so committed. + +2000-08-25 Hugo Tyson + + * src/if_ebsa285.c (i82559_ioctl): A little further diddling; have + a bool to say whether the dot3 info is filled in. + +2000-08-24 Hugo Tyson + + * cdl/ebsa285_eth_drivers.cdl: Do not export a symbol for the + device info file (include/ebsa285_info.h) since nobody needs + (portably) to include it now. + + * src/if_ebsa285.c (i82559_ioctl): Handle new ioctl calls + ETH_DRV_GET_IF_STATS_UD and ETH_DRV_GET_IF_STATS to get loads of + statistical information out. _UD means update. The nonUD one can + be used instead mostly, if we find the performance hit too large. + This should allow SNMP (a) to not explode, (b) to get useful info + out of other device implementations than this one. + + * include/ebsa285_info.h: Remove all the macro cruft for feature + detecting of lots of individual statistics; we now just have a + catch-all struct that SNMP uses, defined in the common ether + driver environment. + +2000-08-15 Hugo Tyson + + * src/if_ebsa285.c (PacketRxReady): Put back the check for very + small packets into the driver; the layer above checks for that + (defensive programming) but only *after* asserting that the size + is large enough, to help detect that scenario from other drivers. + I believe we only have struct ether_header available if CYGPKG_NET + but I could be wrong. + [CASE 104353] + +2000-08-08 Hugo Tyson + + * src/if_ebsa285.c (eth_set_promiscuous_mode): + - ccs->config_bytes[18]=0x70; + + ccs->config_bytes[18]=0x72; // Keep the Padding Enable bit + ...otherwise short frame sends don't work in promisc mode. + [CASE 104289] + +2000-08-07 Gary Thomas + + * src/if_ebsa285.c (pciwindow_mem_alloc): Take out very noisy debug. + +2000-08-03 Gary Thomas + + * src/if_ebsa285.c: Changes for stand-alone mode. + + * cdl/ebsa285_eth_drivers.cdl: Ethernet driver package hierarchy changed. + Add option to control number of interfaces. + +2000-07-28 Hugo Tyson + + * src/if_ebsa285.c (PacketRxReady): Do not attempt to forward + short packets; eth_drv.c assumes there is at least a header there. + (i82559_recv): Also be more careful and ASSERTive about -ve buffer + sizes; be more defensive about sglists. [CASE 104206] + +2000-07-26 Gary Thomas + + * src/if_ebsa285.c: Update for new eth_drv interfaces. + +2000-07-18 Hugo Tyson + + * src/if_ebsa285.c (i82559_poll): Fill in the flesh of this, it + just calls ISR and DSR repeatedly. + (i82559_start): Look in the device record for promiscuous mode + flag; it should be passed though the common layer, but it's not + [yet] - this change from Andrew Lunn/ASCOM. Also a fix and delay + to the promisc mode code per se. + +2000-07-17 Hugo Tyson + + * src/if_ebsa285.c (i82559_poll): New function, just to fill in + the interface record; not used. + +2000-06-27 Hugo Tyson + + * cdl/ebsa285_eth_drivers.cdl: Add sesquipedalian option + CYGDBG_DEVS_ETH_ARM_EBSA285_KEEP_82559_STATISTICS in (now) + component CYGDBG_DEVS_ETH_ARM_EBSA285_KEEP_STATISTICS to control + keeping (well, harvesting really) the i82559's internal stats. + Reputedly, it doesn't service the net whilst this is happening, so + it could be viewed a bad thing. Hence the option. + + * include/ebsa285_info.h: Only describe the I82559_COUNTERS + i82559_counters[2]; structs if full stats are to be kept. + + * src/if_ebsa285.c (update_statistics): Only include this if full + stats are to be kept. + +2000-06-27 Hugo Tyson + + * src/if_ebsa285.c (ResetRxRing): Re-do the management of the + RxRing; have an end-of-list flag (EL) in the last entry, and as + you unload filled slots, drag it round after you. + +2000-06-14 Hugo Tyson + + * cdl/ebsa285_eth_drivers.cdl: Add option to control statistics + keeping. + + * include/ebsa285_info.h: Act on it. + +2000-06-13 Hugo Tyson + + * cdl/ebsa285_eth_drivers.cdl: Properly include the new header + file and define symbols to allow client code to get at it without + knowing the particular device driver name. + + * include/ebsa285_info.h: New file: export various statistics + information about the driver for use by monitoring and + network-management systems. This requires exposing the + (otherwise) internal structures of the driver. + + * src/if_ebsa285.c: remove a lot of structure definitions &c that + are now necessarily in the new header; add a couple of new + routines which provide status and update statistics from the + device into memory; tidy up control of whether stats-keeping is + enabled. + +2000-06-06 Hugo Tyson + + * cdl/ebsa285_eth_drivers.cdl (define_proc): Add #define of + CYGDAT_DEVS_ETH_DESCRIPTION in the config file for information. + +2000-05-12 Hugo Tyson + + * tests/test_net_realtime.h (tnr_print_activity): New routine to + check the system is working, tidied up the API. It works! + +2000-05-11 Hugo Tyson + + * cdl/ebsa285_eth_drivers.cdl: Added export of the test header + below, and config opts for controlling EEPROM writing and all the + status chatter as the device starts up. + + * src/if_ebsa285.c: Reworked the code for reading and setting the + EEPROM that holds the MAC address. This is very ugly, but now + more reliable. Also tidied up printing cruft with neater + configury, and made it an option (for safety) whether it's + possible to write the EEPROM at all. + + * tests/test_net_realtime.h: New file - it is intended to be used + by networking tests to verify that latency is not compromised by + the stack and driver. It's very platform specific, hence the + location in here. This is a preliminary version only. + +2000-04-27 Hugo Tyson + + * src/if_ebsa285.c: A serious re-write. This cuts out a lot of + code from the old version and improves the performance greatly. + + The cruft was mainly doing lots of explicit event communication + between the ISR and DSR, when in fact all the state needed is + present in the tx/rx rings. So both ISRs and DSRs regard their + call as an opportunity to progress everything they can, rather + than only dealing with one interrupt cause at a time; the + connection between them is now rather looser. + + Interrups can now be re-enabled after the ISR (in other words they + are not masked in the ISR), no need to wait for the DSR, but in + consequence some DSR code must mask/unmask intrs as it works. + + The 82559 appears to be a little slow in reacting to commands and + state changes, so some interrupts were being lost - or persisting + beyond their desired life - so there's some kinda polling code to + deal with that also. We also rely on the foreground to kind of + poll in the same way, in the send/can_send calls - we know the + stack will re-try if necessary, though this is rare. + + The driver now works (albeit at much reduced performance) with as + few as 6 rx and tx buffers - in other words the "queue full/out of + rx buffers" states have been tested and all is well. It works + generally fine with 8 buffers of each kind. + + The mux ISR and DSR are now rather more polled than the old + versions; we just try to do things with both devices (if active) + by simply calling each unitary ISR/DSR respectively. + + I also re-ordered some of the code, moving utilities to the end of + the file and grouping together Tx and Rx machines a bit better. + +2000-04-13 Hugo Tyson + + * src/if_ebsa285.c: Attribution to Ron Spence, Pacific Softworks + added as a contributor. + +2000-04-07 Hugo Tyson + + * ecos.db: Re-organize device packages. This is a massive change + involving deleting all the sources for serial and ethernet drivers + from where they used to live in + packages/io/serial/current/src/ARCH/PLATFORM.[ch] + packages/net/drivers/eth/PLATFORM/current/src/... + and reinstating them in + packages/devs/serial/ARCH/PLATFORM/current/src/... + packages/devs/eth/ARCH/PLATFORM/current/src/... + + All these new packages are properly defined in ecos.db, and are + all of type "hardware" so that a "target" can grab them. + + This directory layout is descriptive of the devices we have right + now, arch and platform are separate levels just to make it easier + to navigate in the filesystem and similar to the HAL structure in + the filesystem. + + It is *not* prescriptive of future work; for example, the mythical + common highly-portable 16550 serial driver which works on many + targets would be called "devs/serial/s16550/current", or a serial + device for a particular board (cogent springs to mind) that can + work with different CPUs fitted is "devs/serial/cogent/current". + + Changelogs have been preserved and replicated over all the new + packages, so that no history is lost. + + The contents of individual source files are unchanged; they build + in just the same emvironment except for a very few cases where the + config file name changed in this movement. + + Targets in ecos.db have been redefined to bring in all relevant + hardware packages including net and serial drivers (but the newly + included packages are only active if their desired parent is + available.) + + The names of CDL options (and their #defines of course) stay the + same for the serial drivers, for backward compatibility. + + * templates/*/current.ect: these have had CYGPKG_IO_SERIAL added + rather than it being in (almost) all target definitions. + +2000-03-29 Hugo Tyson + + * src/if_ebsa285.c (i82559_recv): Be happy with NULLs in the + SGlist; it means the caller is out of memory so drop the packet on + the floor. Also remove a completely redundant test. + +2000-03-06 Hugo Tyson + + * src/if_ebsa285.c (i82559_can_send): Update net driver to new + interface style. This is incomplete wrt promiscuous mode, but + that's probably about all. + +2000-02-14 Gary Thomas + + * cdl/ebsa285_eth_drivers.cdl: Update CDL to indicate multiple + interface support. + +2000-02-14 Hugo Tyson + + * src/if_ebsa285.c (pci_init_find_82559s): Tidy comments somewhat + and set debug and stats collecting defines to most friendly + settings. + +2000-02-10 Hugo Tyson + + * src/if_ebsa285.c (PacketRxReady): Fix bug; current descriptor + was not being write back for the callback to use. Hence asserts + on state of rfd were firing in busy times - that leading rfd had + already been drained. + + Also rationalized meaning of DEBUG printy symbols a bit - it's now + chatty during startup/foreground manipulations but not in any + performance related activities ie. rx or tx. + +2000-02-09 John Dallaway + + * cdl/ebsa285_eth_drivers.cdl: + + Reparent under CYGPKG_NET_ETH_DRIVERS and tidy display strings. + +2000-02-08 Hugo Tyson + + * src/if_ebsa285.c: New File. + * cdl/ebsa285_eth_drivers.cdl: New File. + + Initial Checkin of EBSA285 Ethernet driver. + + It's one monolithic file at present, and should be split up into a + more generic Intel 82559 driver plus platform-specific parts (PCI + et al) plus eCos/Red-Hat-BSD-stack parts. + +//=========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//=========================================================================== + + + diff --git a/packages/devs/eth/intel/i82559/current/cdl/intel_i82559_eth_drivers.cdl b/packages/devs/eth/intel/i82559/current/cdl/intel_i82559_eth_drivers.cdl new file mode 100644 --- /dev/null +++ b/packages/devs/eth/intel/i82559/current/cdl/intel_i82559_eth_drivers.cdl @@ -0,0 +1,140 @@ +# ==================================================================== +# +# intel_i82559_eth_drivers.cdl +# +# Intel 82559 ethernet driver +# +# ==================================================================== +#####COPYRIGHTBEGIN#### +# +# ------------------------------------------- +# The contents of this file are subject to the Red Hat eCos Public License +# Version 1.1 (the "License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://www.redhat.com/ +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +# License for the specific language governing rights and limitations under +# the License. +# +# The Original Code is eCos - Embedded Configurable Operating System, +# released September 30, 1998. +# +# The Initial Developer of the Original Code is Red Hat. +# Portions created by Red Hat are +# Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. +# All Rights Reserved. +# ------------------------------------------- +# +#####COPYRIGHTEND#### +# ==================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): hmt +# Original data: hmt +# Contributors: hmt, gthomas, jskov +# Date: 2000-02-01 +# +#####DESCRIPTIONEND#### +# +# ==================================================================== + +cdl_package CYGPKG_DEVS_ETH_INTEL_I82559 { + display "Intel 82559 ethernet driver" + description "Ethernet driver for Intel 82559 controller." + + parent CYGPKG_IO_ETH_DRIVERS + active_if CYGPKG_IO_ETH_DRIVERS + + active_if CYGINT_DEVS_ETH_INTEL_I82559_REQUIRED + + include_dir cyg/devs/eth + + # SNMP demands to know stuff; this sadly makes us break the neat + # abstraction of the device having nothing exported. + include_files include/i82559_info.h + # and tell them that it is available + define_proc { + puts $::cdl_system_header \ + "#define CYGBLD_DEVS_ETH_DEVICE_H " + + puts $::cdl_header "#include CYGDAT_DEVS_ETH_INTEL_I82559_CFG"; + } + + compile -library=libextras.a if_i82559.c + + cdl_option CYGDBG_DEVS_ETH_INTEL_I82559_CHATTER { + display "Prints ethernet device status info during startup" + default_value 0 + description " + The ethernet device initialization code can print lots of info + to confirm that it has found the devices on the PCI bus, read + the MAC address from EEPROM correctly, and so on, and also + displays the mode (10/100MHz, half/full duplex) of the + connection." + } + + cdl_option CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT { + display "Number of supported interfaces." + calculated { CYGINT_DEVS_ETH_INTEL_I82559_REQUIRED } + flavor data + description " + This option selects the number of PCI ethernet interfaces to + be supported by the driver." + } + + cdl_component CYGDBG_DEVS_ETH_INTEL_I82559_KEEP_STATISTICS { + display "Keep Ethernet statistics" + default_value 1 + description " + The ethernet device can maintain statistics about the network, + specifically a great variety of error rates which are useful + for network management. SNMP for example uses this + information. There is some performance cost in maintaining + this information; disable this option to recoup that." + + cdl_option CYGDBG_DEVS_ETH_INTEL_I82559_KEEP_82559_STATISTICS { + display "Keep i82559 Internal statistics" + default_value 1 + description " + The i82559 keeps internal counters, and it is possible to + acquire these. But the i82559 (reputedly) does not service + the network whilst uploading the data to RAM from its + internal registers. If throughput is a problem, disable + this option to acquire only those statistics gathered by + software, so that the i82559 never sleeps." + } + } + + cdl_component CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM { + display "SIOCSIFHWADDR records MAC address in EEPROM" + default_value 0 + description " + The ioctl() socket call with operand SIOCSIFHWADDR sets the + interface hardware address - the MAC address or ethernet + address. This option causes the new MAC address to be written + into the EEPROM associated with the interface, so that the new + MAC address is permanently recorded. Doing this should be a + carefully chosen decision, hence this option." + } + + cdl_component CYGPKG_DEVS_ETH_INTEL_I82559_OPTIONS { + display "Intel 82559 ethernet driver build options" + flavor none + no_define + + cdl_option CYGPKG_DEVS_ETH_INTEL_I82559_CFLAGS_ADD { + display "Additional compiler flags" + flavor data + no_define + default_value { "-D_KERNEL -D__ECOS" } + description " + This option modifies the set of compiler flags for + building the Intel 82559 ethernet driver + package. These flags are used in addition to the set of + global flags." + } + } +} +# EOF intel_i82559_eth_drivers.cdl diff --git a/packages/devs/eth/intel/i82559/current/include/i82559_info.h b/packages/devs/eth/intel/i82559/current/include/i82559_info.h new file mode 100644 --- /dev/null +++ b/packages/devs/eth/intel/i82559/current/include/i82559_info.h @@ -0,0 +1,206 @@ +#ifndef CYGONCE_DEVS_ETH_INTEL_I82559_INFO_H +#define CYGONCE_DEVS_ETH_INTEL_I82559_INFO_H +/*========================================================================== +// +// i82559_info.h +// +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): hmt +// Contributors: hmt +// Date: 2000-05-03 +// Description: +// +//####DESCRIPTIONEND#### +*/ + +#include + +#ifdef CYGDBG_DEVS_ETH_INTEL_I82559_KEEP_STATISTICS +# define KEEP_STATISTICS +# define nDISPLAY_STATISTICS +# define nDISPLAY_82559_STATISTICS +#else +# define nKEEP_STATISTICS +# define nDISPLAY_STATISTICS +# define nDISPLAY_82559_STATISTICS +#endif + + +// ------------------------------------------------------------------------ +// +// STATISTICAL COUNTER STRUCTURE +// +// ------------------------------------------------------------------------ +#ifdef KEEP_STATISTICS +typedef struct { +/* 0 */ cyg_uint32 tx_good; +/* 4 */ cyg_uint32 tx_max_collisions; +/* 8 */ cyg_uint32 tx_late_collisions; +/* 12 */ cyg_uint32 tx_underrun; +/* 16 */ cyg_uint32 tx_carrier_loss; +/* 20 */ cyg_uint32 tx_deferred; +/* 24 */ cyg_uint32 tx_single_collisions; +/* 28 */ cyg_uint32 tx_mult_collisions; +/* 32 */ cyg_uint32 tx_total_collisions; +/* 36 */ cyg_uint32 rx_good; +/* 40 */ cyg_uint32 rx_crc_errors; +/* 44 */ cyg_uint32 rx_align_errors; +/* 48 */ cyg_uint32 rx_resource_errors; +/* 52 */ cyg_uint32 rx_overrun_errors; +/* 56 */ cyg_uint32 rx_collisions; // Always 0 +/* 60 */ cyg_uint32 rx_short_frames; +// In this setup; can also be flow-control counts after. +// If these are to be used, a config command (as in set promiscuous mode) +// must be issued at start, to let those stats escape. Params are in +// comments around the config command setup... +/* 64 */ cyg_uint32 done; +} I82559_COUNTERS; + + +typedef struct { + cyg_uint32 interrupts; + cyg_uint32 rx_count; + cyg_uint32 rx_deliver; + cyg_uint32 rx_resource; + cyg_uint32 rx_restart; + cyg_uint32 tx_count; + cyg_uint32 tx_complete; + cyg_uint32 tx_dropped; +} STATISTICS; + + +extern STATISTICS statistics[CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT]; +#ifdef CYGDBG_DEVS_ETH_INTEL_I82559_KEEP_82559_STATISTICS +extern I82559_COUNTERS i82559_counters[CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT]; +#endif + +#endif // KEEP_STATISTICS + +// ------------------------------------------------------------------------ +// +// DEVICES AND PACKET QUEUES +// +// ------------------------------------------------------------------------ +// The system seems to work OK with as few as 8 of RX and TX descriptors. +// It limps very painfully with only 4. +// Performance is better with more than 8. +// But the size of non-cached (so useless for anything else) +// memory window is 1Mb, so we might as well use it all. +// +// 128 for these uses the whole 1Mb, near enough. + +#ifndef MAX_RX_DESCRIPTORS +#define MAX_RX_DESCRIPTORS 128 // number of Rx descriptors +#endif +#ifndef MAX_TX_DESCRIPTORS +#define MAX_TX_DESCRIPTORS 128 // number of Tx descriptors +#endif + +// Use packet type for selfdocumenting code +typedef cyg_uint8 RFD; +typedef cyg_uint8 TxCB; +typedef cyg_uint8 CFG; + +typedef struct i82559 { + cyg_uint8 // (split up for atomic byte access) + found:1, // was hardware discovered? + mac_addr_ok:1, // can we bring up? + active:1, // has this if been brung up? + hardwired_esa:1, // set if ESA is hardwired via CDL + spare1:4; + cyg_uint8 + spare2:8; + cyg_uint8 + tx_in_progress:1, // transmit in progress flag + tx_queue_full:1, // all Tx descriptors used flag + spare3:6; + cyg_uint8 index; // 0 or 1 or whatever + cyg_uint32 devid; // PCI device id + cyg_uint32 memory_address; // PCI memory address + cyg_uint32 io_address; // memory mapped I/O address + cyg_uint8 mac_address[6]; // mac (hardware) address + void *ndp; // Network Device Pointer + + int next_rx_descriptor; // descriptor index for RFDs + RFD* rx_ring[MAX_RX_DESCRIPTORS]; // location of Rx descriptors + + int tx_descriptor_add; // descriptor index for additions + int tx_descriptor_active; // descriptor index for active tx + int tx_descriptor_remove; // descriptor index for remove + + TxCB* tx_ring[MAX_TX_DESCRIPTORS]; // location of Tx descriptors + unsigned long tx_keys[MAX_TX_DESCRIPTORS]; + // keys for tx q management + + // Interrupt handling stuff + cyg_vector_t vector; // interrupt vector + cyg_handle_t interrupt_handle; // handle for int.handler + cyg_interrupt interrupt_object; + +#ifdef KEEP_STATISTICS + void *p_statistics; // pointer to statistical counters +#endif + +} I82559; + + +// ------------------------------------------------------------------------ +// +// 82559 GENERAL STATUS REGISTER +// +// ------------------------------------------------------------------------ +#define GEN_STATUS_FDX 0x04 // 1 = full duplex, 0 = half +#define GEN_STATUS_100MBPS 0x02 // 1 = 100 Mbps, 0 = 10 Mbps +#define GEN_STATUS_LINK 0x01 // 1 = link up, 0 = link down + +extern int i82559_status( struct eth_drv_sc *sc ); + +// ------------------------------------------------------------------------ + +#ifdef KEEP_STATISTICS +void update_statistics(struct i82559* p_i82559); +#endif + + +#ifdef CYGDBG_DEVS_ETH_INTEL_I82559_KEEP_82559_STATISTICS +#define ETH_STATS_INIT( p ) \ + update_statistics( (struct i82559 *)((p)->driver_private) ) +#else +#define ETH_STATS_INIT( p ) // otherwise do nothing +#endif + +#define CYGDAT_DEVS_ETH_DESCRIPTION "Intel EtherPRO 10/100+ (i82559)" + +#define ETH_DEV_DOT3STATSETHERCHIPSET 1,3,6,1,2,1,10,7,8,2,5 + +#endif /* ifndef CYGONCE_DEVS_ETH_INTEL_I82559_INFO_H */ + +/* EOF i82559_info.h */ + diff --git a/packages/devs/eth/intel/i82559/current/src/if_i82559.c b/packages/devs/eth/intel/i82559/current/src/if_i82559.c new file mode 100644 --- /dev/null +++ b/packages/devs/eth/intel/i82559/current/src/if_i82559.c @@ -0,0 +1,3188 @@ +//========================================================================== +// +// if_i82559.c +// +// Intel 82559 ethernet driver +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//####BSDCOPYRIGHTBEGIN#### +// +// ------------------------------------------- +// +// Portions of this software may have been derived from OpenBSD or +// other sources, and are covered by the appropriate copyright +// disclaimers included herein. +// +// ------------------------------------------- +// +//####BSDCOPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): hmt, gthomas +// Contributors: Ron Spence, Pacific Softworks, jskov +// Date: 2000-02-01 +// Purpose: +// Description: hardware driver for 82559 Intel PRO/100+ ethernet +// Notes: CU commands such as dump and config should, according +// to the docs, set the CU active state while executing. +// That does not seem to be the case though, and the +// driver polls the completion bit in the packet status +// word instead. +// +// Platform code must provide this vector: +// CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT if it +// requires the interrupts to be handled via demuxers. +// +// Platform code can also provide this macro: +// CYGPRI_DEVS_ETH_INTEL_I82559_INTERRUPT_ACK_LOOP(p_i82559) +// to handle delaying for acks to register on the interrupt +// controller as necessary on the EBSA. +// +// FIXME: IN/OUT macros for accessing PCI IO space are probably +// broken on proper BE systems. Tested on a board with +// a Galileo which seems to do some weird stuff to 8/16 +// bit word addressing. +// FIXME: replace -1/-2 return values with proper E-defines +// FIXME: eth_set_mac_address and eth_set_promiscuous_mode +// failures are not handled properly [try writing +// endian swapped cmd - CU starts, but wedges] +// FIXME: For 82557/8 compatibility the eth_set_config and +// eth_set_promiscuous_mode functions probably need +// some tweaking - config bits differ slightly but +// crucially. +// FIXME: EEPROM code not tested on a BE system. +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef CYGPKG_NET +#include +#include /* Needed for struct ifnet */ +#else +#include +#define diag_printf printf +#endif + +#ifdef CYGPKG_IO_PCI +#include +// So we can check the validity of the PCI window against the MLTs opinion, +// and thereby what the malloc heap consumes willy-nilly: +#include CYGHWR_MEMORY_LAYOUT_H +#else +#error "Need PCI package here" +#endif + +// Exported statistics and the like +#include +#include +#include CYGDAT_DEVS_ETH_INTEL_I82559_INL + +// ------------------------------------------------------------------------ +// Platform specific + +#define CYGHWR_INTEL_I82559_PCI_MEM_MAP_BASE (CYGARC_UNCACHED_ADDRESS(0x0ff00000)) +#define CYGHWR_INTEL_I82559_PCI_MEM_MAP_SIZE 0x00100000 + +// ------------------------------------------------------------------------ + +#ifdef CYGDBG_DEVS_ETH_INTEL_I82559_CHATTER +#define notDEBUG_82559 // This one prints stuff as packets come and go +#define DEBUG // Startup printing mainly +#define DEBUG_EE // Some EEPROM specific retries &c +#endif + +#define os_printf diag_printf +#define db_printf diag_printf + +// ------------------------------------------------------------------------ +// I/O access macros as inlines for type safety + +#if (CYG_BYTEORDER == CYG_MSBFIRST) + +#define HAL_CTOLE32(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff)) +#define HAL_LE32TOC(x) ((((x) & 0xff) << 24) | (((x) & 0xff00) << 8) | (((x) & 0xff0000) >> 8) | (((x) >> 24) & 0xff)) + +#define HAL_CTOLE16(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) +#define HAL_LE16TOC(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8)) + +static inline void OUTB(cyg_uint8 value, cyg_uint32 io_address) +{ *((volatile cyg_uint8 *)(io_address)) = value; } + +static inline void OUTW(cyg_uint16 value, cyg_uint32 io_address) +{ *((volatile cyg_uint16 *)(io_address)) = (((value & 0xff) << 8) | ((value & 0xff00) >> 8)); } + +static inline void OUTL(cyg_uint32 value, cyg_uint32 io_address) +{ *((volatile cyg_uint32 *)io_address) = ((((value) & 0xff) << 24) | (((value) & 0xff00) << 8) | (((value) & 0xff0000) >> 8) | (((value) >> 24) & 0xff)); } + +static inline cyg_uint8 INB(cyg_uint32 io_address) +{ return *((volatile cyg_uint8 *)(io_address)); } + +static inline cyg_uint16 INW(cyg_uint32 io_address) +{ cyg_uint16 d; + d = *((volatile cyg_uint16 *)(io_address)); + return (((d & 0xff) << 8) | ((d & 0xff00) >> 8)); +} + +static inline cyg_uint32 INL(cyg_uint32 io_address) +{ cyg_uint32 d; + d = *((volatile cyg_uint32 *)io_address); + return ((((d) & 0xff) << 24) | (((d) & 0xff00) << 8) | (((d) & 0xff0000) >> 8) | (((d) >> 24) & 0xff)); +} +#else +static inline void OUTB(cyg_uint8 value, cyg_uint32 io_address) +{ *((volatile cyg_uint8 *)io_address) = value; } + +static inline void OUTW(cyg_uint16 value, cyg_uint32 io_address) +{ *((volatile cyg_uint16 *)io_address) = value; } + +static inline void OUTL(cyg_uint32 value, cyg_uint32 io_address) +{ *((volatile cyg_uint32 *)io_address) = value; } + +static inline cyg_uint8 INB(cyg_uint32 io_address) +{ return *((volatile cyg_uint8 *)io_address); } + +static inline cyg_uint16 INW(cyg_uint32 io_address) +{ return *((volatile cyg_uint16 *)io_address); } + +static inline cyg_uint32 INL(cyg_uint32 io_address) +{ return *((volatile cyg_uint32 *)io_address); } +#endif // byteorder + +#define VIRT_TO_BUS( _x_ ) virt_to_bus((cyg_uint32)(_x_)) +static inline cyg_uint32 virt_to_bus(cyg_uint32 p_memory) +{ return CYGARC_PHYSICAL_ADDRESS(p_memory); } + +#define BUS_TO_VIRT( _x_ ) bus_to_virt((cyg_uint32)(_x_)) +static inline cyg_uint32 bus_to_virt(cyg_uint32 p_memory) +{ return CYGARC_UNCACHED_ADDRESS(p_memory); } + + +// ------------------------------------------------------------------------ +// +// 82559 REGISTER OFFSETS (I/O SPACE) +// +// ------------------------------------------------------------------------ +#define SCBStatus 0 // Rx/Command Unit command and status. +#define SCBCmd 2 // Rx/Command Unit command and status. +#define SCBPointer 4 // General purpose pointer. +#define SCBPort 8 // Misc. commands and operands. +#define SCBflash 12 // Flash memory control. +#define SCBeeprom 14 // EEPROM memory control. +#define SCBCtrlMDI 16 // MDI interface control. +#define SCBEarlyRx 20 // Early receive byte count. +#define SCBGenControl 28 // 82559 General Control Register +#define SCBGenStatus 29 // 82559 General Status register + + +// ------------------------------------------------------------------------ +// +// 82559 SCB STATUS WORD DEFNITIONS +// +// ------------------------------------------------------------------------ +#define SCB_STATUS_CX 0x8000 // CU finished command (transmit) +#define SCB_STATUS_FR 0x4000 // frame received +#define SCB_STATUS_CNA 0x2000 // CU left active state +#define SCB_STATUS_RNR 0x1000 // receiver left ready state +#define SCB_STATUS_MDI 0x0800 // MDI read/write cycle done +#define SCB_STATUS_SWI 0x0400 // software generated interrupt +#define SCB_STATUS_FCP 0x0100 // flow control pause interrupt + +#define SCB_INTACK_MASK 0xFD00 // all the above + +#define SCB_INTACK_TX (SCB_STATUS_CX | SCB_STATUS_CNA) +#define SCB_INTACK_RX (SCB_STATUS_FR | SCB_STATUS_RNR) + +// ------------------------------------------------------------------------ +// +// 82559 PORT INTERFACE COMMANDS +// +// ------------------------------------------------------------------------ +#define I82559_RESET 0x00000000 // software reset +#define I82559_SELFTEST 0x00000001 // 82559 selftest command +#define I82559_SELECTIVE_RESET 0x00000002 +#define I82559_DUMP 0x00000003 +#define I82559_DUMP_WAKEUP 0x00000007 + + +// ------------------------------------------------------------------------ +// +// 82559 EEPROM INTERFACE +// +// ------------------------------------------------------------------------ +// EEPROM_Ctrl bits. +#define EE_SHIFT_CLK 0x01 // EEPROM shift clock. +#define EE_CS 0x02 // EEPROM chip select. +#define EE_DATA_WRITE 0x04 // EEPROM chip data in. +#define EE_DATA_READ 0x08 // EEPROM chip data out. +#define EE_ENB (0x4800 | EE_CS) + +// Delay between EEPROM clock transitions. +#define eeprom_delay(usec) udelay(usec); + +// The EEPROM commands include the always-set leading bit. +#define EE_WRITE_CMD(a) (5 << (a)) +#define EE_READ_CMD(a) (6 << (a)) +#define EE_ERASE_CMD(a) (7 << (a)) +#define EE_WRITE_EN_CMD(a) (19 << ((a)-2)) +#define EE_WRITE_DIS_CMD(a) (16 << ((a)-2)) +#define EE_ERASE_ALL_CMD(a) (18 << ((a)-2)) + +#define EE_TOP_CMD_BIT(a) ((a)+2) // Counts down to zero +#define EE_TOP_DATA_BIT (15) // Counts down to zero + +#define EEPROM_ENABLE_DELAY (10) // Delay at chip select + +#define EEPROM_SK_DELAY (4) // Delay between clock edges *and* data + // read or transition; 3 of these per bit. +#define EEPROM_DONE_DELAY (100) // Delay when all done + + +// ------------------------------------------------------------------------ +// +// SYSTEM CONTROL BLOCK COMMANDS +// +// ------------------------------------------------------------------------ +// CU COMMANDS +#define CU_NOP 0x0000 +#define CU_START 0x0010 +#define CU_RESUME 0x0020 +#define CU_STATSADDR 0x0040 // Load Dump Statistics ctrs addr +#define CU_SHOWSTATS 0x0050 // Dump statistics counters. +#define CU_ADDR_LOAD 0x0060 // Base address to add to CU commands +#define CU_DUMPSTATS 0x0070 // Dump then reset stats counters. + +// RUC COMMANDS +#define RUC_NOP 0x0000 +#define RUC_START 0x0001 +#define RUC_RESUME 0x0002 +#define RUC_ABORT 0x0004 +#define RUC_ADDR_LOAD 0x0006 // (seems not to clear on acceptance) +#define RUC_RESUMENR 0x0007 + +#define CU_CMD_MASK 0x00f0 +#define RU_CMD_MASK 0x0007 + +#define SCB_M 0x0100 // 0 = enable interrupt, 1 = disable +#define SCB_SI 0x0200 // 1 - cause device to interrupt + +#define CU_STATUS_MASK 0x00C0 +#define RU_STATUS_MASK 0x003C + +#define RU_STATUS_IDLE (0<<2) +#define RU_STATUS_SUS (1<<2) +#define RU_STATUS_NORES (2<<2) +#define RU_STATUS_READY (4<<2) +#define RU_STATUS_NO_RBDS_SUS ((1<<2)|(8<<2)) +#define RU_STATUS_NO_RBDS_NORES ((2<<2)|(8<<2)) +#define RU_STATUS_NO_RBDS_READY ((4<<2)|(8<<2)) + +#define MAX_MEM_RESERVED_IOCTL 1000 + +// ------------------------------------------------------------------------ +// +// RECEIVE FRAME DESCRIPTORS +// +// ------------------------------------------------------------------------ + +// The status is split into two shorts to get atomic access to the EL bit; +// the upper word is not written by the device, so we can just hit it, +// leaving the lower word (which the device updates) alone. Otherwise +// there's a race condition between software moving the end-of-list (EL) +// bit round and the device writing into the previous slot. + +#if (CYG_BYTEORDER == CYG_MSBFIRST) + +#define RFD_STATUS 0 +#define RFD_STATUS_HI 2 // swapped +#define RFD_STATUS_LO 0 // swapped +#define RFD_LINK 4 +#define RFD_RDB_ADDR 8 +#define RFD_SIZE 14 // swapped +#define RFD_COUNT 12 // swapped +#define RFD_BUFFER 16 +#define RFD_SIZEOF 16 + +// Note that status bits are endian converted - so we don't need to +// fiddle the byteorder when accessing the status word! +#define RFD_STATUS_EL 0x00000080 // 1=last RFD in RFA +#define RFD_STATUS_S 0x00000040 // 1=suspend RU after receiving frame +#define RFD_STATUS_H 0x00001000 // 1=RFD is a header RFD +#define RFD_STATUS_SF 0x00000800 // 0=simplified, 1=flexible mode +#define RFD_STATUS_C 0x00800000 // completion of received frame +#define RFD_STATUS_OK 0x00200000 // frame received with no errors + +#define RFD_STATUS_HI_EL 0x0080 // 1=last RFD in RFA +#define RFD_STATUS_HI_S 0x0040 // 1=suspend RU after receiving frame +#define RFD_STATUS_HI_H 0x1000 // 1=RFD is a header RFD +#define RFD_STATUS_HI_SF 0x0800 // 0=simplified, 1=flexible mode + +#define RFD_STATUS_LO_C 0x0080 // completion of received frame +#define RFD_STATUS_LO_OK 0x0020 // frame received with no errors + + +#define RFD_COUNT_MASK 0x3fff +#define RFD_COUNT_F 0x4000 +#define RFD_COUNT_EOF 0x8000 + +#define RFD_RX_CRC 0x00080000 // crc error +#define RFD_RX_ALIGNMENT 0x00040000 // alignment error +#define RFD_RX_RESOURCE 0x00020000 // out of space, no resources +#define RFD_RX_DMA_OVER 0x00010000 // DMA overrun +#define RFD_RX_SHORT 0x80000000 // short frame error +#define RFD_RX_LENGTH 0x20000000 // +#define RFD_RX_ERROR 0x10000000 // receive error +#define RFD_RX_NO_ADR_MATCH 0x04000000 // no address match +#define RFD_RX_IA_MATCH 0x02000000 // individual address does not match +#define RFD_RX_TCO 0x01000000 // TCO indication + +#else // Little-endian + +#define RFD_STATUS 0 +#define RFD_STATUS_HI 0 +#define RFD_STATUS_LO 2 +#define RFD_LINK 4 +#define RFD_RDB_ADDR 8 +#define RFD_SIZE 12 +#define RFD_COUNT 14 +#define RFD_BUFFER 16 + +#define RFD_STATUS_EL 0x80000000 // 1=last RFD in RFA +#define RFD_STATUS_S 0x40000000 // 1=suspend RU after receiving frame +#define RFD_STATUS_H 0x00100000 // 1=RFD is a header RFD +#define RFD_STATUS_SF 0x00080000 // 0=simplified, 1=flexible mode +#define RFD_STATUS_C 0x00008000 // completion of received frame +#define RFD_STATUS_OK 0x00002000 // frame received with no errors + +#define RFD_STATUS_HI_EL 0x8000 // 1=last RFD in RFA +#define RFD_STATUS_HI_S 0x4000 // 1=suspend RU after receiving frame +#define RFD_STATUS_HI_H 0x0010 // 1=RFD is a header RFD +#define RFD_STATUS_HI_SF 0x0008 // 0=simplified, 1=flexible mode + +#define RFD_STATUS_LO_C 0x8000 // completion of received frame +#define RFD_STATUS_LO_OK 0x2000 // frame received with no errors + +#define RFD_COUNT_MASK 0x3fff +#define RFD_COUNT_F 0x4000 +#define RFD_COUNT_EOF 0x8000 + +#define RFD_RX_CRC 0x00000800 // crc error +#define RFD_RX_ALIGNMENT 0x00000400 // alignment error +#define RFD_RX_RESOURCE 0x00000200 // out of space, no resources +#define RFD_RX_DMA_OVER 0x00000100 // DMA overrun +#define RFD_RX_SHORT 0x00000080 // short frame error +#define RFD_RX_LENGTH 0x00000020 // +#define RFD_RX_ERROR 0x00000010 // receive error +#define RFD_RX_NO_ADR_MATCH 0x00000004 // no address match +#define RFD_RX_IA_MATCH 0x00000002 // individual address does not match +#define RFD_RX_TCO 0x00000001 // TCO indication + +#endif // CYG_BYTEORDER + +// ------------------------------------------------------------------------ +// +// TRANSMIT FRAME DESCRIPTORS +// +// ------------------------------------------------------------------------ + +#if (CYG_BYTEORDER == CYG_MSBFIRST) + +#define TxCB_CMD 2 // swapped +#define TxCB_STATUS 0 // swapped +#define TxCB_LINK 4 +#define TxCB_TBD_ADDR 8 +#define TxCB_TX_THRESHOLD 14 // swapped +#define TxCB_TBD_NUMBER 15 // swapped +#define TxCB_COUNT 12 // swapped +#define TxCB_BUFFER 16 +#define TxCB_SIZEOF 16 + +// Note that CMD/STATUS bits are endian converted - so we don't need +// to fiddle the byteorder when accessing the CMD/STATUS word! + +#define TxCB_CMD_TRANSMIT 0x0400 // transmit command +#define TxCB_CMD_SF 0x0800 // 0=simplified, 1=flexible mode +#define TxCB_CMD_NC 0x0010 // 0=CRC insert by controller +#define TxCB_CMD_I 0x0020 // generate interrupt on completion +#define TxCB_CMD_S 0x0040 // suspend on completion +#define TxCB_CMD_EL 0x0080 // last command block in CBL + +#define TxCB_COUNT_MASK 0x3fff +#define TxCB_COUNT_EOF 0x8000 + +#else // Little-endian layout + +#define TxCB_CMD 0 +#define TxCB_STATUS 2 +#define TxCB_LINK 4 +#define TxCB_TBD_ADDR 8 +#define TxCB_TBD_NUMBER 12 +#define TxCB_TX_THRESHOLD 13 +#define TxCB_COUNT 14 +#define TxCB_BUFFER 16 + +#define TxCB_COUNT_MASK 0x3fff +#define TxCB_COUNT_EOF 0x8000 + +#define TxCB_CMD_TRANSMIT 0x0004 // transmit command +#define TxCB_CMD_SF 0x0008 // 0=simplified, 1=flexible mode +#define TxCB_CMD_NC 0x0010 // 0=CRC insert by controller +#define TxCB_CMD_I 0x2000 // generate interrupt on completion +#define TxCB_CMD_S 0x4000 // suspend on completion +#define TxCB_CMD_EL 0x8000 // last command block in CBL + +#endif // CYG_BYTEORDER + +// ------------------------------------------------------------------------ +// +// STRUCTURES ADDED FOR PROMISCUOUS MODE +// +// ------------------------------------------------------------------------ + +#if (CYG_BYTEORDER == CYG_MSBFIRST) + +#define CFG_STATUS 0 +#define CFG_CMD 2 +#define CFG_CB_LINK_OFFSET 4 +#define CFG_BYTES 8 +#define CFG_SIZEOF 32 + +// Note CFG CMD and STATUS swapped, so no need for endian conversion +// in code. +#define CFG_CMD_EL 0x0080 +#define CFG_CMD_SUSPEND 0x0040 +#define CFG_CMD_INT 0x0020 +#define CFG_CMD_IAS 0x0100 // individual address setup +#define CFG_CMD_CONFIGURE 0x0200 // configure + +#define CFG_STATUS_C 0x0080 +#define CFG_STATUS_OK 0x0020 + +#else // Little-endian + +#define CFG_CMD 0 +#define CFG_STATUS 2 +#define CFG_CB_LINK_OFFSET 4 +#define CFG_BYTES 8 +#define CFG_SIZEOF 32 + +#define CFG_CMD_EL 0x8000 +#define CFG_CMD_SUSPEND 0x4000 +#define CFG_CMD_INT 0x2000 +#define CFG_CMD_IAS 0x0001 // individual address setup +#define CFG_CMD_CONFIGURE 0x0002 // configure + +#define CFG_STATUS_C 0x8000 +#define CFG_STATUS_OK 0x2000 + +#endif // CYG_BYTEORDER + + +// ------------------------------------------------------------------------ +// +// STATISTICAL COUNTER STRUCTURE +// +// ------------------------------------------------------------------------ +#ifdef KEEP_STATISTICS +STATISTICS statistics[CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT]; +I82559_COUNTERS i82559_counters[CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT]; +#endif // KEEP_STATISTICS + +// ------------------------------------------------------------------------ +// +// DEVICES AND PACKET QUEUES +// +// ------------------------------------------------------------------------ + +#define MAX_RX_PACKET_SIZE 1536 // maximum Rx packet size +#define MAX_TX_PACKET_SIZE 1536 // maximum Tx packet size + + +// ------------------------------------------------------------------------ +// Use arrays provided by platform header to verify pointers. +#ifdef CYGDBG_USE_ASSERTS +#define CHECK_NDP_SC_LINK() \ + CYG_MACRO_START \ + int i, valid_netdev = 0, valid_sc = 0; \ + for(i = 0; i < CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT; i++) { \ + if (i82559_netdev_array[i] == ndp) valid_netdev = 1; \ + if (i82559_sc_array[i] == sc) valid_sc = 1; \ + if (valid_sc || valid_netdev) break; \ + } \ + CYG_ASSERT( valid_netdev, "Bad ndp" ); \ + CYG_ASSERT( valid_sc, "Bad sc" ); \ + CYG_ASSERT( (void *)p_i82559 == i82559_sc_array[i]->driver_private, \ + "sc pointer bad" ); \ + CYG_MACRO_END +#else +#define CHECK_NDP_SC_LINK() +#endif + +#define IF_BAD_82559( _p_ ) \ +if (({ \ + int i, valid_p = 0; \ + for(i = 0; i < CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT; i++) { \ + if (i82559_priv_array[i] == (_p_)) { \ + valid_p = 1; \ + break; \ + } \ + } \ + CYG_ASSERT(valid_p, "Bad pointer-to-i82559"); \ + (!valid_p); \ +})) + +// ------------------------------------------------------------------------ +// +// Managing the memory that is windowed onto the PCI bus +// +// ------------------------------------------------------------------------ + +static cyg_uint32 i82559_heap_size; +static cyg_uint8 *i82559_heap_base; +static cyg_uint8 *i82559_heap_free; + +static void *mem_reserved_ioctl = (void*)0; +// uncacheable memory reserved for ioctl calls + +// ------------------------------------------------------------------------ +// +// FUNCTION PROTOTYPES +// +// ------------------------------------------------------------------------ + +static int pci_init_find_82559s(void); + +static void i82559_reset(struct i82559* p_i82559); +static int eth_set_mac_address(struct i82559* p_i82559, char *addr); + +static void InitRxRing(struct i82559* p_i82559); +static void ResetRxRing(struct i82559* p_i82559); +static void InitTxRing(struct i82559* p_i82559); +static void ResetTxRing(struct i82559* p_i82559); + +static int eth_set_config(struct i82559* p_i82559); + +#ifdef CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM +static void program_eeprom(cyg_uint32 , cyg_uint32 , cyg_uint8 * ); +#endif +#ifdef CYGPKG_NET +static int eth_set_promiscuous_mode(struct i82559* p_i82559); +#endif + +// debugging/logging only: +void dump_txcb(TxCB* p_txcb); +void DisplayStatistics(void); +void update_statistics(struct i82559* p_i82559); +void dump_rfd(RFD* p_rfd, int anyway ); +void dump_all_rfds( int intf ); +void dump_packet(cyg_uint8 *p_buffer, int length); + +// ------------------------------------------------------------------------ +// utilities +// ------------------------------------------------------------------------ + +typedef enum { + WAIT_RU, // wait before RU cmd + WAIT_CU // wait before CU cmd +} cmd_wait_t; + +static // inline +void +wait_for_cmd_done(long scb_ioaddr, cmd_wait_t type) +{ + register int status; + register int wait; + +#ifdef nDEBUG_82559 + os_printf(">%s %04x\n", __FUNCTION__, INW(scb_ioaddr + SCBCmd)); +#endif + wait = 0x100000; + do status = INW(scb_ioaddr + SCBCmd) ; + while( (0 != ((CU_CMD_MASK | RU_CMD_MASK) & status)) && --wait >= 0); +#ifdef nDEBUG_82559 + os_printf(">%s %04x\n", __FUNCTION__, INW(scb_ioaddr + SCBCmd)); +#endif + CYG_ASSERT( wait > 0, "wait_for_cmd_done: cmd busy" ); + + if (WAIT_CU == type) { + // Also check CU is idle +#ifdef nDEBUG_82559 + os_printf(">%s %04x\n", __FUNCTION__, INW(scb_ioaddr + SCBStatus)); +#endif + wait = 0x100000; + do status = INW(scb_ioaddr + SCBStatus) ; + while( (status & CU_STATUS_MASK) && --wait >= 0); +#ifdef nDEBUG_82559 + os_printf("<%s %04x\n", __FUNCTION__, INW(scb_ioaddr + SCBStatus)); +#endif + CYG_ASSERT( wait > 0, "wait_for_cmd_done: CU busy" ); + } else if (WAIT_RU == type) { + // Can't see any active state in the doc to check for + } +} + +// Short circuit the drv_interrupt_XXX API once we are started: + +static inline int +Mask82559Interrupt(struct i82559* p_i82559) +{ + int old = 0; + +// if (query_enabled) + old |= 1; + cyg_drv_interrupt_mask(p_i82559->vector); +#ifdef CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT +// if (query_enabled) + old |= 2; + cyg_drv_interrupt_mask(CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT); +#endif + + /* it may prove necessary to do something like this + if ( mybits != (mybits & old) ) + /# then at least one of them was disabled, so + # omit both from any restoration: #/ + old = 0; + */ + + return old; +} + +static inline void +UnMask82559Interrupt(struct i82559* p_i82559, int old) +{ + // We must only unmask (enable) those which were unmasked before, + // according to the bits in old. + if (old & 1) + cyg_drv_interrupt_unmask(p_i82559->vector); +#ifdef CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT + if (old & 2) + cyg_drv_interrupt_mask(CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT); +#endif +} + +static void +Acknowledge82559Interrupt(struct i82559* p_i82559) +{ + cyg_drv_interrupt_acknowledge(p_i82559->vector); +#ifdef CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT + cyg_drv_interrupt_acknowledge(CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT); +#endif + +#ifdef CYGPRI_DEVS_ETH_INTEL_I82559_INTERRUPT_ACK_LOOP + CYGPRI_DEVS_ETH_INTEL_I82559_INTERRUPT_ACK_LOOP(p_i82559); +#endif +} + + +externC void hal_delay_us(int); +static void +udelay(int delay) +{ + hal_delay_us(delay); +} + +// ------------------------------------------------------------------------ +// Memory management +// +// Simply carve off from the front of the PCI mapped window into real memory + +static void* +pciwindow_mem_alloc(int size) +{ + void *p_memory; + int _size = size; + + CYG_ASSERT( + (CYGHWR_INTEL_I82559_PCI_MEM_MAP_BASE <= (int)i82559_heap_free) + && + ((CYGHWR_INTEL_I82559_PCI_MEM_MAP_BASE + + CYGHWR_INTEL_I82559_PCI_MEM_MAP_SIZE) > (int)i82559_heap_free) + && + (0 < i82559_heap_size) + && + (CYGHWR_INTEL_I82559_PCI_MEM_MAP_SIZE >= i82559_heap_size) + && + (CYGHWR_INTEL_I82559_PCI_MEM_MAP_BASE == (int)i82559_heap_base), + "Heap variables corrupted" ); + + p_memory = (void *)0; + size = (size + 3) & ~3; + if ( (i82559_heap_free+size) < (i82559_heap_base+i82559_heap_size) ) { + cyg_uint32 *p; + p_memory = (void *)i82559_heap_free; + i82559_heap_free += size; + for ( p = (cyg_uint32 *)p_memory; _size > 0; _size -= 4 ) + *p++ = 0; + } + + return p_memory; +} + + +// ------------------------------------------------------------------------ +// +// GET EEPROM SIZE +// +// ------------------------------------------------------------------------ +static int +get_eeprom_size(long ioaddr) +{ + unsigned short retval = 0; + int ee_addr = ioaddr + SCBeeprom; + int i, addrbits; + + // Should already be not-selected, but anyway: + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay(EEPROM_ENABLE_DELAY); + OUTW(EE_ENB, ee_addr); + eeprom_delay(EEPROM_ENABLE_DELAY); + + // Shift the read command bits out. + for (i = 2; i >= 0; i--) { + short dataval = (6 & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(EE_ENB | dataval , ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB | dataval , ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + } + // Now clock out address zero, looking for the dummy 0 data bit + for ( i = 1; i <= 12; i++ ) { + OUTW(EE_ENB , ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB , ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + retval = INW(ee_addr) & EE_DATA_READ; + if ( 0 == retval ) + break; // The dummy zero est arrive' + } + +#ifdef DEBUG_EE + os_printf( "eeprom data bits %d (ioaddr %x)\n", i, ee_addr ); +#endif + + if ( 6 != i && 8 != i && 1 != i) { +#ifdef DEBUG_EE + os_printf( "*****EEPROM data bits not 6, 8 or 1*****\n" ); +#endif + i = 6; + } + addrbits = i; + + // clear the dataval, leave the clock low to read in the data regardless + OUTW(EE_ENB, ee_addr); + eeprom_delay(1); + + retval = INW(ee_addr); + if ( (EE_DATA_READ & retval) != 0 ) { +#ifdef DEBUG_EE + os_printf( "Size EEPROM: Dummy data bit not 0, reg %x\n" , retval ); +#endif + } + eeprom_delay(1); + + for (i = EE_TOP_DATA_BIT; i >= 0; i--) { + OUTW(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + retval = INW(ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB, ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + } + + // Terminate the EEPROM access. + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay(EEPROM_DONE_DELAY); + + return addrbits; +} + + +// ------------------------------------------------------------------------ +// +// READ EEPROM +// +// ------------------------------------------------------------------------ +static int +read_eeprom(long ioaddr, int location, int addr_len) +{ + unsigned short retval = 0; + int ee_addr = ioaddr + SCBeeprom; + int read_cmd = location | EE_READ_CMD(addr_len); + int i, tries = 10; + + try_again: + // Should already be not-selected, but anyway: + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay(EEPROM_ENABLE_DELAY); + OUTW(EE_ENB, ee_addr); + eeprom_delay(EEPROM_ENABLE_DELAY); + + // Shift the read command bits out, changing only one bit per time. + for (i = EE_TOP_CMD_BIT(addr_len); i >= 0; i--) { + short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(EE_ENB | dataval , ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB | dataval , ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + } + + // clear the dataval, leave the clock low + OUTW(EE_ENB, ee_addr); + eeprom_delay(1); + + retval = INW(ee_addr); + // This should show a zero in the data read bit to confirm that the + // address transfer is compelete. If not, go to the start and try + // again! + if ( (0 != (retval & EE_DATA_READ)) && (tries-- > 0) ) { + // Terminate the EEPROM access. + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay(EEPROM_DONE_DELAY); +#ifdef DEBUG_EE + os_printf( "Warning: Retrying EEPROM read word %d, address %x, try %d\n", + location, ee_addr, tries+1 ); +#endif + goto try_again; + } + + // This fires with one device on one of the customer boards! + // (but is OK on all other h/w. Worrying huh.) + if ( (EE_DATA_READ & retval) != 0 ) { +#ifdef DEBUG_EE + os_printf( "Read EEPROM: Dummy data bit not 0, reg %x\n" , retval ); +#endif + } + eeprom_delay(1); + retval = 0; + + for (i = EE_TOP_DATA_BIT; i >= 0; i--) { + OUTW(EE_ENB | EE_SHIFT_CLK, ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + retval = (retval << 1) | ((INW(ee_addr) & EE_DATA_READ) ? 1 : 0); + eeprom_delay(EEPROM_SK_DELAY); + OUTW(EE_ENB, ee_addr); + eeprom_delay(EEPROM_SK_DELAY); + } + + // Terminate the EEPROM access. + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay(EEPROM_DONE_DELAY); + + return retval; +} + +// ------------------------------------------------------------------------ +// +// NETWORK INTERFACE INITIALIZATION +// +// Function : Init82559 +// +// Description : +// This routine resets, configures, and initializes the chip. +// It also clears the ethernet statistics structure, and selects +// which statistics are supported by this driver. +// +// ------------------------------------------------------------------------ +static bool +i82559_init(struct cyg_netdevtab_entry * ndp) +{ + static int initialized = 0; // only probe PCI et al *once* + + struct eth_drv_sc *sc; + cyg_uint32 selftest; + volatile cyg_uint32 *p_selftest; + cyg_uint32 ioaddr; + int count; + int ints; + struct i82559 *p_i82559; + cyg_uint8 mac_address[ETHER_ADDR_LEN]; + +#ifdef DEBUG + db_printf("intel_i82559_init\n"); +#endif + + sc = (struct eth_drv_sc *)(ndp->device_instance); + p_i82559 = (struct i82559 *)(sc->driver_private); + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "Bad device private pointer %x\n", sc->driver_private ); +#endif + return 0; + } + + CHECK_NDP_SC_LINK(); + + if ( 0 == initialized++ ) { + // then this is the first time ever: + if ( ! pci_init_find_82559s() ) { +#ifdef DEBUG + os_printf( "pci_init_find_82559s failed" ); +#endif + return 0; + } + } + + // If this device is not present, exit + if (0 == p_i82559->found) + return 0; + + ioaddr = p_i82559->io_address; // get I/O address for 82559 + +#ifdef DEBUG + os_printf("Init82559 %d @ %x\n82559 Self Test\n", + p_i82559->index, (int)ndp); +#endif + + ints = Mask82559Interrupt(p_i82559); + + // Reset device + i82559_reset(p_i82559); + + // Perform a system self-test. (get enough mem to round address) + if ( (selftest = (cyg_uint32)pciwindow_mem_alloc(32) ) == 0) + return (0); + p_selftest = (cyg_uint32 *) ((selftest + 15) & ~0xf); + p_selftest[0] = p_selftest[1] = -1; + + OUTL( (VIRT_TO_BUS(p_selftest)) | I82559_SELFTEST, ioaddr + SCBPort); + count = 0x7FFFF; // Timeout for self-test. + do { + udelay(10); + } while ( (p_selftest[1] == -1) && (--count >= 0) ); + + // Reset device again after selftest + i82559_reset(p_i82559); + + Acknowledge82559Interrupt(p_i82559); + UnMask82559Interrupt(p_i82559, ints ); + + if (count < 0) { + // Test timed out. +#ifdef DEBUG + os_printf("Self test failed\n"); +#endif + return (0); + } +#ifdef DEBUG + os_printf(" General self-test: %s.\n" + " Serial sub-system self-test: %s.\n" + " Internal registers self-test: %s.\n" + " ROM checksum self-test: %s (%08X).\n", + HAL_LE32TOC(p_selftest[1]) & 0x1000 ? "failed" : "passed", + HAL_LE32TOC(p_selftest[1]) & 0x0020 ? "failed" : "passed", + HAL_LE32TOC(p_selftest[1]) & 0x0008 ? "failed" : "passed", + HAL_LE32TOC(p_selftest[1]) & 0x0004 ? "failed" : "passed", + HAL_LE32TOC(p_selftest[0])); +#endif + + // FIXME: free self-test memory? + + if (p_i82559->hardwired_esa) { + // Hardwire the address without consulting the EEPROM. + // When this flag is set, the p_i82559 will already contain + // the ESA. Copy it to a mac_address for call to set_mac_addr + mac_address[0] = p_i82559->mac_address[0]; + mac_address[1] = p_i82559->mac_address[1]; + mac_address[2] = p_i82559->mac_address[2]; + mac_address[3] = p_i82559->mac_address[3]; + mac_address[4] = p_i82559->mac_address[4]; + mac_address[5] = p_i82559->mac_address[5]; + + eth_set_mac_address(p_i82559, mac_address); + } else { + int addr_length, i; + cyg_uint16 checksum; + + // read eeprom and get 82559's mac address + addr_length = get_eeprom_size(ioaddr); + // (this is the length of the *EEPROM*s address, not MAC address) + + // If length is 1, it _probably_ means there's no EEPROM + // present. Couldn't find an explicit mention of this in the + // docs, but length=1 appears to be the behaviour in that case. + if (1 == addr_length) { +#ifdef DEBUG_EE + os_printf("Error: No EEPROM present for device %d\n", + p_i82559->index); +#endif + } else { + for (checksum = 0, i = 0, count = 0; count < 64; count++) { + cyg_uint16 value; + // read word from eeprom + value = read_eeprom(ioaddr, count, addr_length); +#ifdef DEBUG_EE + // os_printf( "%2d: %04x\n", count, value ); +#endif + checksum += value; + if (count < 3) { + mac_address[i++] = value & 0xFF; + mac_address[i++] = (value >> 8) & 0xFF; + } + } + + // If the EEPROM checksum is wrong, the MAC address read + // from the EEPROM is probably wrong as well. In that + // case, we don't set mac_addr_ok, but continue the + // initialization. If then somebody calls i82559_start + // without calling eth_set_mac_address() first, we refuse + // to bring up the interface, because running with an + // invalid MAC address is not a very brilliant idea. + + if ((checksum & 0xFFFF) != 0xBABA) { + // selftest verified checksum, verify again +#ifdef DEBUG_EE + os_printf("Warning: Invalid EEPROM checksum %04X for device %d\n", + checksum, p_i82559->index); +#endif + } else { + p_i82559->mac_addr_ok = 1; +#ifdef DEBUG_EE + os_printf("Valid EEPROM checksum\n"); +#endif + } + } + + // record the MAC address in the device structure + p_i82559->mac_address[0] = mac_address[0]; + p_i82559->mac_address[1] = mac_address[1]; + p_i82559->mac_address[2] = mac_address[2]; + p_i82559->mac_address[3] = mac_address[3]; + p_i82559->mac_address[4] = mac_address[4]; + p_i82559->mac_address[5] = mac_address[5]; + } + +#ifdef DEBUG + os_printf("MAC Address = %02X %02X %02X %02X %02X %02X\n", + p_i82559->mac_address[0], p_i82559->mac_address[1], + p_i82559->mac_address[2], p_i82559->mac_address[3], + p_i82559->mac_address[4], p_i82559->mac_address[5]); +#endif + + // and record the net dev pointer + p_i82559->ndp = (void *)ndp; + + InitRxRing(p_i82559); + InitTxRing(p_i82559); + + // Initialize upper level driver + if ( p_i82559->mac_addr_ok ) + (sc->funs->eth_drv->init)(sc, &(p_i82559->mac_address[0]) ); + else + (sc->funs->eth_drv->init)(sc, NULL ); + + return (1); +} + +// ------------------------------------------------------------------------ +// +// Function : i82559_start +// +// ------------------------------------------------------------------------ +static void +i82559_start( struct eth_drv_sc *sc, unsigned char *enaddr, int flags ) +{ + struct i82559 *p_i82559; + cyg_uint32 ioaddr; + int count; + cyg_uint16 status; +#ifdef KEEP_STATISTICS + void *p_statistics; +#endif +#ifdef CYGPKG_NET + struct ifnet *ifp = &sc->sc_arpcom.ac_if; +#endif + + p_i82559 = (struct i82559 *)sc->driver_private; + ioaddr = p_i82559->io_address; // get 82559's I/O address + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_start: Bad device pointer %x\n", p_i82559 ); +#endif + return; + } + + if ( ! p_i82559->mac_addr_ok ) { +#ifdef DEBUG + os_printf("i82559_start %d: invalid MAC address, " + "can't bring up interface\n", + p_i82559->index ); +#endif + return; + } + + if ( p_i82559->active ) + i82559_stop( sc ); + +#ifdef KEEP_STATISTICS +#ifdef CYGDBG_DEVS_ETH_INTEL_I82559_KEEP_STATISTICS + p_i82559->p_statistics = + p_statistics = pciwindow_mem_alloc(sizeof(I82559_COUNTERS)); + memset(p_statistics, 0xFFFFFFFF, sizeof(I82559_COUNTERS)); + // set statistics dump address + wait_for_cmd_done(ioaddr, WAIT_CU); + OUTL(VIRT_TO_BUS(p_statistics), ioaddr + SCBPointer); + OUTW(SCB_M | CU_STATSADDR, ioaddr + SCBCmd); + // Start dump command + wait_for_cmd_done(ioaddr, WAIT_CU); + OUTW(SCB_M | CU_DUMPSTATS, ioaddr + SCBCmd); // start register dump + // ...and wait for it to complete operation + count = 1000; + do { + HAL_READ_UINT16((cyg_uint8*)p_statistics + CFG_STATUS, status); + udelay(1); + } while (0 == (status & CFG_STATUS_C) && (count-- > 0)); +#ifdef CYGDBG_USE_ASSERTS + { + HAL_READ_UINT16((cyg_uint8*)p_statistics + CFG_STATUS, status); + CYG_ASSERT((CFG_STATUS_C | CFG_STATUS_OK) + == (status & (CFG_STATUS_C | CFG_STATUS_OK)), + "Dump stat command incomplete/failedd"); + } +#endif // CYGDBG_USE_ASSERTS +#endif +#endif + + // Enable device + p_i82559->active = 1; + eth_set_config(p_i82559); + +#ifdef CYGPKG_NET + if (( 0 +#ifdef ETH_DRV_FLAGS_PROMISC_MODE + != (flags & ETH_DRV_FLAGS_PROMISC_MODE) +#endif + ) || (ifp->if_flags & IFF_PROMISC) + ) { + eth_set_promiscuous_mode(p_i82559); + } +#endif +#ifdef DEBUG + { + int status = i82559_status( sc ); + os_printf("i82559_start %d flg %x Link = %s, %s Mbps, %s Duplex\n", + p_i82559->index, + *(int *)p_i82559, + status & GEN_STATUS_LINK ? "Up" : "Down", + status & GEN_STATUS_100MBPS ? "100" : "10", + status & GEN_STATUS_FDX ? "Full" : "Half"); + } +#endif + + // Load pointer to Rx Ring and enable receiver + wait_for_cmd_done(ioaddr, WAIT_RU); + OUTL(VIRT_TO_BUS(p_i82559->rx_ring[0]), ioaddr + SCBPointer); + OUTW(RUC_START, ioaddr + SCBCmd); + +} + +// ------------------------------------------------------------------------ +// +// Function : i82559_status +// +// ------------------------------------------------------------------------ +int +i82559_status( struct eth_drv_sc *sc ) +{ + int status; + struct i82559 *p_i82559; + cyg_uint32 ioaddr; + p_i82559 = (struct i82559 *)sc->driver_private; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_status: Bad device pointer %x\n", p_i82559 ); +#endif + return 0; + } + + ioaddr = p_i82559->io_address; // get 82559's I/O address + + status = INB(ioaddr + SCBGenStatus); + + return status; +} + +// ------------------------------------------------------------------------ +// +// Function : BringDown82559 +// +// ------------------------------------------------------------------------ + +static void +i82559_stop( struct eth_drv_sc *sc ) +{ + struct i82559 *p_i82559; + + p_i82559 = (struct i82559 *)sc->driver_private; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_stop: Bad device pointer %x\n", p_i82559 ); +#endif + return; + } + +#ifdef DEBUG + os_printf("i82559_stop %d flg %x\n", p_i82559->index, *(int *)p_i82559 ); +#endif + + p_i82559->active = 0; // stop people tormenting it + i82559_reset(p_i82559); // that should stop it + + ResetRxRing( p_i82559 ); + ResetTxRing( p_i82559 ); +} + + +// ------------------------------------------------------------------------ +// +// Function : InitRxRing +// +// ------------------------------------------------------------------------ +static void +InitRxRing(struct i82559* p_i82559) +{ + int i; + RFD *rfd; + RFD *p_rfd = 0; +#ifdef DEBUG_82559 + os_printf("InitRxRing %d\n", p_i82559->index); +#endif + for ( i = 0; i < MAX_RX_DESCRIPTORS; i++ ) { + rfd = (RFD *)pciwindow_mem_alloc(RFD_SIZEOF + MAX_RX_PACKET_SIZE); + p_i82559->rx_ring[i] = rfd; + if ( i ) + HAL_WRITE_UINT32(p_rfd+RFD_LINK, HAL_CTOLE32(VIRT_TO_BUS(rfd))); + p_rfd = (RFD *)rfd; + } + // link last RFD to first: + HAL_WRITE_UINT32(p_rfd+RFD_LINK, + HAL_CTOLE32(VIRT_TO_BUS(p_i82559->rx_ring[0]))); + + ResetRxRing( p_i82559 ); +} + +// ------------------------------------------------------------------------ +// +// Function : ResetRxRing +// +// ------------------------------------------------------------------------ +static void +ResetRxRing(struct i82559* p_i82559) +{ + RFD *p_rfd; + int i; +#ifdef DEBUG_82559 + os_printf("ResetRxRing %d\n", p_i82559->index); +#endif + for ( i = 0; i < MAX_RX_DESCRIPTORS; i++ ) { + p_rfd = p_i82559->rx_ring[i]; + CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" ); + CYG_ASSERT( (cyg_uint8 *)p_rfd < i82559_heap_free, "rfd over" ); +#ifdef CYGDBG_USE_ASSERTS + { + RFD *p_rfd2; + cyg_uint32 link; + p_rfd2 = p_i82559->rx_ring[ ( i ? (i-1) : (MAX_RX_DESCRIPTORS-1) ) ]; + HAL_READ_UINT32(p_rfd2 + RFD_LINK, link); + CYG_ASSERT( HAL_LE32TOC(link) == VIRT_TO_BUS(p_rfd), + "rfd linked list broken" ); + } +#endif + HAL_WRITE_UINT32(p_rfd + RFD_STATUS, /*0*/RFD_STATUS_S); + HAL_WRITE_UINT16(p_rfd + RFD_COUNT, 0); + HAL_WRITE_UINT32(p_rfd + RFD_RDB_ADDR, HAL_CTOLE32(0xFFFFFFFF)); + HAL_WRITE_UINT16(p_rfd + RFD_SIZE, HAL_CTOLE16(MAX_RX_PACKET_SIZE)); + } + p_i82559->next_rx_descriptor = 0; + // And set an end-of-list marker in the previous one. + HAL_WRITE_UINT32(p_rfd + RFD_STATUS, RFD_STATUS_EL); +} + +// ------------------------------------------------------------------------ +// +// Function : PacketRxReady (Called from delivery thread) +// +// ------------------------------------------------------------------------ +static void +PacketRxReady(struct i82559* p_i82559) +{ + RFD *p_rfd; + int next_descriptor; + int length, ints; + struct cyg_netdevtab_entry *ndp; + struct eth_drv_sc *sc; + cyg_uint32 ioaddr; + cyg_uint16 status; + + ndp = (struct cyg_netdevtab_entry *)(p_i82559->ndp); + sc = (struct eth_drv_sc *)(ndp->device_instance); + + CHECK_NDP_SC_LINK(); + + ioaddr = p_i82559->io_address; + + next_descriptor = p_i82559->next_rx_descriptor; + p_rfd = p_i82559->rx_ring[next_descriptor]; + + CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" ); + CYG_ASSERT( (cyg_uint8 *)p_rfd < i82559_heap_free, "rfd over" ); + + while ( 1 ) { + cyg_uint32 rxstatus; + cyg_uint16 rxstatus_hi; + HAL_READ_UINT32(p_rfd + RFD_STATUS, rxstatus); + if (0 == (rxstatus & RFD_STATUS_C)) + break; + + HAL_READ_UINT16(p_rfd + RFD_STATUS_HI, rxstatus_hi); + rxstatus_hi |= RFD_STATUS_HI_EL; + HAL_WRITE_UINT16(p_rfd + RFD_STATUS_HI, rxstatus_hi); + + HAL_READ_UINT16(p_rfd + RFD_COUNT, length); + length = HAL_LE16TOC(length); + length &= RFD_COUNT_MASK; + +#ifdef DEBUG_82559 + os_printf( "Device %d (eth%d), rx descriptor %d:\n", + p_i82559->index, p_i82559->index, next_descriptor ); +// dump_rfd( p_rfd, 1 ); +#endif + + p_i82559->next_rx_descriptor = next_descriptor; + // Check for bogusly short packets; can happen in promisc mode: + // Asserted against and checked by upper layer driver. +#ifdef CYGPKG_NET + if ( length > sizeof( struct ether_header ) ) + // then it is acceptable; offer the data to the network stack +#endif + (sc->funs->eth_drv->recv)( sc, length ); + + HAL_WRITE_UINT16(p_rfd + RFD_COUNT, 0); + HAL_WRITE_UINT16(p_rfd + RFD_STATUS_LO, 0); + + // The just-emptied slot is now ready for re-use and already marked EL; + // we can now remove the EL marker from the previous one. + if ( 0 == next_descriptor ) + p_rfd = p_i82559->rx_ring[ MAX_RX_DESCRIPTORS-1 ]; + else + p_rfd = p_i82559->rx_ring[ next_descriptor-1 ]; + // The previous one: check it *was* marked before clearing. + HAL_READ_UINT16(p_rfd + RFD_STATUS_HI, rxstatus_hi); + CYG_ASSERT( rxstatus_hi & RFD_STATUS_HI_EL, "No prev EL" ); + // that word is not written by the device. + HAL_WRITE_UINT16(p_rfd + RFD_STATUS_HI, 0); + +#ifdef KEEP_STATISTICS + statistics[p_i82559->index].rx_deliver++; +#endif + if (++next_descriptor >= MAX_RX_DESCRIPTORS) + next_descriptor = 0; + p_rfd = p_i82559->rx_ring[next_descriptor]; + + CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" ); + CYG_ASSERT( (cyg_uint8 *)p_rfd < i82559_heap_free, "rfd over" ); + + } + + // See if the RU has gone idle (usually because of out of resource + // condition) and restart it if needs be. + ints = Mask82559Interrupt(p_i82559); + status = INW(ioaddr + SCBStatus); + if ( RU_STATUS_READY != (status & RU_STATUS_MASK) ) { + // Acknowledge the RX INT sources + OUTW( SCB_INTACK_RX, ioaddr + SCBStatus); + // (see pages 6-10 & 6-90) + +#ifdef KEEP_STATISTICS + statistics[p_i82559->index].rx_restart++; +#endif + // There's an end-of-list marker out there somewhere... + // So mop it up; it takes a little time but this is infrequent. + ResetRxRing( p_i82559 ); + next_descriptor = 0; // re-initialize next desc. + // load pointer to Rx Ring + wait_for_cmd_done(ioaddr, WAIT_RU); + OUTL(VIRT_TO_BUS(p_i82559->rx_ring[0]), + ioaddr + SCBPointer); + OUTW(RUC_START, ioaddr + SCBCmd); + Acknowledge82559Interrupt(p_i82559); + } + UnMask82559Interrupt(p_i82559, ints); + + p_i82559->next_rx_descriptor = next_descriptor; +} + +// and the callback function + +static void +i82559_recv( struct eth_drv_sc *sc, struct eth_drv_sg *sg_list, int sg_len ) +{ + struct i82559 *p_i82559; + RFD *p_rfd; + int next_descriptor; + int total_len; + struct eth_drv_sg *last_sg; + volatile cyg_uint8 *from_p; + cyg_uint32 rxstatus; + + p_i82559 = (struct i82559 *)sc->driver_private; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_recv: Bad device pointer %x\n", p_i82559 ); +#endif + return; + } + + next_descriptor = p_i82559->next_rx_descriptor; + p_rfd = p_i82559->rx_ring[next_descriptor]; + + CYG_ASSERT( (cyg_uint8 *)p_rfd >= i82559_heap_base, "rfd under" ); + CYG_ASSERT( (cyg_uint8 *)p_rfd < i82559_heap_free, "rfd over" ); + + HAL_READ_UINT32(p_rfd + RFD_STATUS, rxstatus); + CYG_ASSERT( rxstatus & RFD_STATUS_C, "No complete frame" ); + CYG_ASSERT( rxstatus & RFD_STATUS_EL, "No marked frame" ); + CYG_ASSERT( rxstatus & RFD_STATUS_C, "No complete frame 2" ); + CYG_ASSERT( rxstatus & RFD_STATUS_EL, "No marked frame 2" ); + + if ( 0 == (rxstatus & RFD_STATUS_C) ) + return; + + HAL_READ_UINT16(p_rfd + RFD_COUNT, total_len); + total_len = HAL_LE16TOC(total_len); + total_len &= RFD_COUNT_MASK; + +#ifdef DEBUG_82559 + os_printf("Rx %d %x (status %x): %d sg's, %d bytes\n", + p_i82559->index, (int)p_i82559, + HAL_LE32TOC(rxstatus), sg_len, total_len); +#endif + + // Copy the data to the network stack + from_p = p_rfd + RFD_BUFFER; + + // check we have memory to copy into; we would be called even if + // caller was out of memory in order to maintain our state. + if ( 0 == sg_len || 0 == sg_list ) + return; // caller was out of mbufs + + CYG_ASSERT( 0 < sg_len, "sg_len underflow" ); + CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" ); + + for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) { + cyg_uint8 *to_p; + int l; + + to_p = (cyg_uint8 *)(sg_list->buf); + l = sg_list->len; + + CYG_ASSERT( 0 <= l, "sg length -ve" ); + + if ( 0 >= l || 0 == to_p ) + return; // caller was out of mbufs + + if ( l > total_len ) + l = total_len; + + memcpy( to_p, (unsigned char *)from_p, l ); + from_p += l; + total_len -= l; + } + + CYG_ASSERT( 0 == total_len, "total_len mismatch in rx" ); + CYG_ASSERT( last_sg == sg_list, "sg count mismatch in rx" ); + CYG_ASSERT( p_rfd + RFD_BUFFER < from_p, "from_p wild in rx" ); + CYG_ASSERT( p_rfd + RFD_BUFFER + MAX_RX_PACKET_SIZE >= from_p, + "from_p overflow in rx" ); +} + + +// ------------------------------------------------------------------------ +// +// Function : InitTxRing +// +// ------------------------------------------------------------------------ +static void +InitTxRing(struct i82559* p_i82559) +{ + int i; + cyg_uint32 ioaddr; + +#ifdef DEBUG_82559 + os_printf("InitTxRing %d\n", p_i82559->index); +#endif + ioaddr = p_i82559->io_address; + for ( i = 0; i < MAX_TX_DESCRIPTORS; i++) { + p_i82559->tx_ring[i] = (TxCB *)pciwindow_mem_alloc( + TxCB_SIZEOF + MAX_TX_PACKET_SIZE); + } + + ResetTxRing(p_i82559); +} + +// ------------------------------------------------------------------------ +// +// Function : ResetTxRing +// +// ------------------------------------------------------------------------ +static void +ResetTxRing(struct i82559* p_i82559) +{ + int i; + cyg_uint32 ioaddr; + +#ifdef DEBUG_82559 + os_printf("ResetTxRing %d\n", p_i82559->index); +#endif + ioaddr = p_i82559->io_address; + p_i82559->tx_descriptor_add = + p_i82559->tx_descriptor_active = + p_i82559->tx_descriptor_remove = 0; + p_i82559->tx_in_progress = + p_i82559->tx_queue_full = 0; + + for ( i = 0; i < MAX_TX_DESCRIPTORS; i++) { + TxCB *p_txcb = p_i82559->tx_ring[i]; + CYG_ASSERT( (cyg_uint8 *)p_txcb >= i82559_heap_base, "txcb under" ); + CYG_ASSERT( (cyg_uint8 *)p_txcb < i82559_heap_free, "txcb over" ); + + HAL_WRITE_UINT16(p_txcb + TxCB_STATUS, 0); + HAL_WRITE_UINT16(p_txcb + TxCB_CMD, 0); + HAL_WRITE_UINT32(p_txcb + TxCB_LINK, + HAL_CTOLE32(VIRT_TO_BUS((cyg_uint32)p_txcb))); + HAL_WRITE_UINT32(p_txcb + TxCB_TBD_ADDR, HAL_CTOLE32(0xFFFFFFFF)); + HAL_WRITE_UINT8(p_txcb + TxCB_TBD_NUMBER, 0); + HAL_WRITE_UINT8(p_txcb + TxCB_TX_THRESHOLD, 16); + HAL_WRITE_UINT16(p_txcb + TxCB_COUNT, + HAL_CTOLE16(TxCB_COUNT_EOF | 0)); + p_i82559->tx_keys[i] = 0; + } +} + +// ------------------------------------------------------------------------ +// +// Function : TxMachine (Called from FG & ISR) +// +// This steps the Tx Machine onto the next record if necessary - allowing +// for missed interrupts, and so on. +// ------------------------------------------------------------------------ + +static void +TxMachine(struct i82559* p_i82559) +{ + int tx_descriptor_active; + cyg_uint32 ioaddr; + + tx_descriptor_active = p_i82559->tx_descriptor_active; + ioaddr = p_i82559->io_address; + + // See if the CU is idle when we think it isn't; this is the only place + // tx_descriptor_active is advanced. (Also recovers from a dropped intr) + if ( p_i82559->tx_in_progress ) { + cyg_uint16 status; + status = INW(ioaddr + SCBStatus); + if ( 0 == (status & CU_STATUS_MASK) ) { + // It is idle. So ack the TX interrupts + OUTW( SCB_INTACK_TX, ioaddr + SCBStatus); + // (see pages 6-10 & 6-90) + + // and step on to the next queued tx. + p_i82559->tx_in_progress = 0; + if ( ++tx_descriptor_active >= MAX_TX_DESCRIPTORS ) + tx_descriptor_active = 0; + p_i82559->tx_descriptor_active = tx_descriptor_active; + } + } + + // is the CU idle, and there a next tx to set going? + if ( ( ! p_i82559->tx_in_progress ) + && p_i82559->tx_descriptor_add != tx_descriptor_active ) { + TxCB *p_txcb = p_i82559->tx_ring[tx_descriptor_active]; + cyg_uint16 status; + status = INW(ioaddr + SCBStatus); + CYG_ASSERT( ( 0 == (status & CU_STATUS_MASK)), "CU not idle"); + CYG_ASSERT( (cyg_uint8 *)p_txcb >= i82559_heap_base, "txcb under" ); + CYG_ASSERT( (cyg_uint8 *)p_txcb < i82559_heap_free, "txcb over" ); +#ifdef DEBUG_82559 + { + unsigned long key = p_i82559->tx_keys[ tx_descriptor_active ]; + os_printf("Tx %d %x: Starting Engines: KEY %x TxCB %x\n", + p_i82559->index, (int)p_i82559, key, p_txcb); + } +#endif + + // start Tx operation + wait_for_cmd_done(ioaddr, WAIT_CU); + OUTL(VIRT_TO_BUS(p_txcb), ioaddr + SCBPointer); + OUTW(CU_START, ioaddr + SCBCmd); + p_i82559->tx_in_progress = 1; + } +} + +// ------------------------------------------------------------------------ +// +// Function : TxDone (Called from delivery thread) +// +// This returns Tx's from the Tx Machine to the stack (ie. reports +// completion) - allowing for missed interrupts, and so on. +// ------------------------------------------------------------------------ + +static void +TxDone(struct i82559* p_i82559) +{ + struct cyg_netdevtab_entry *ndp; + struct eth_drv_sc *sc; + int tx_descriptor_remove = p_i82559->tx_descriptor_remove; + + ndp = (struct cyg_netdevtab_entry *)(p_i82559->ndp); + sc = (struct eth_drv_sc *)(ndp->device_instance); + + CHECK_NDP_SC_LINK(); + + // "Done" txen are from here to active, OR + // the remove one if the queue is full AND its status is nonzero: + while ( (tx_descriptor_remove != p_i82559->tx_descriptor_active) || + ( p_i82559->tx_queue_full) ) { + + cyg_uint16 txstatus; + TxCB *p_txcb = p_i82559->tx_ring[ tx_descriptor_remove ]; + unsigned long key = p_i82559->tx_keys[ tx_descriptor_remove ]; + + HAL_READ_UINT16(p_txcb + TxCB_STATUS, txstatus); + if (0 == txstatus) + break; + +#ifdef DEBUG_82559 + os_printf("TxDone %d %x: KEY %x TxCB %x\n", + p_i82559->index, (int)p_i82559, key, p_txcb ); +#endif + (sc->funs->eth_drv->tx_done)( sc, key, 1 /* status */ ); + + if ( ++tx_descriptor_remove >= MAX_TX_DESCRIPTORS ) + tx_descriptor_remove = 0; + p_i82559->tx_descriptor_remove = tx_descriptor_remove; + p_i82559->tx_queue_full = 0; + } +} + + +// ------------------------------------------------------------------------ +// +// Function : i82559_can_send +// +// ------------------------------------------------------------------------ + +static int +i82559_can_send(struct eth_drv_sc *sc) +{ + struct i82559 *p_i82559; + int ints; + + p_i82559 = (struct i82559 *)sc->driver_private; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_send: Bad device pointer %x\n", p_i82559 ); +#endif + return 0; + } + + // Advance TxMachine atomically + ints = Mask82559Interrupt(p_i82559); + TxMachine(p_i82559); + Acknowledge82559Interrupt(p_i82559); // This can eat an Rx interrupt, so + PacketRxReady(p_i82559); + UnMask82559Interrupt(p_i82559,ints); + + return ! p_i82559->tx_queue_full; +} + +// ------------------------------------------------------------------------ +// +// Function : i82559_send +// +// ------------------------------------------------------------------------ + +static void +i82559_send(struct eth_drv_sc *sc, + struct eth_drv_sg *sg_list, int sg_len, int total_len, + unsigned long key) +{ + struct i82559 *p_i82559; + int tx_descriptor_add, ints; + TxCB *p_txcb; + cyg_uint32 ioaddr; + + p_i82559 = (struct i82559 *)sc->driver_private; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_send: Bad device pointer %x\n", p_i82559 ); +#endif + return; + } + +#ifdef DEBUG_82559 + os_printf("Tx %d %x: %d sg's, %d bytes, KEY %x\n", + p_i82559->index, (int)p_i82559, sg_len, total_len, key ); +#endif + + if ( ! p_i82559->active ) + return; // device inactive, no return +#ifdef KEEP_STATISTICS + statistics[p_i82559->index].tx_count++; +#endif + ioaddr = p_i82559->io_address; // get device I/O address + + if ( p_i82559->tx_queue_full ) { +#ifdef KEEP_STATISTICS + statistics[p_i82559->index].tx_dropped++; +#endif +#ifdef DEBUG_82559 + os_printf( "i82559_send: Queue full, device %x, key %x\n", + p_i82559, key ); +#endif + } + else { + struct eth_drv_sg *last_sg; + volatile cyg_uint8 *to_p; + + tx_descriptor_add = p_i82559->tx_descriptor_add; + + p_i82559->tx_keys[tx_descriptor_add] = key; + + p_txcb = p_i82559->tx_ring[tx_descriptor_add]; + + CYG_ASSERT( (cyg_uint8 *)p_txcb >= i82559_heap_base, "txcb under" ); + CYG_ASSERT( (cyg_uint8 *)p_txcb < i82559_heap_free, "txcb over" ); + + HAL_WRITE_UINT16(p_txcb + TxCB_STATUS, 0); + HAL_WRITE_UINT16(p_txcb + TxCB_CMD, + (TxCB_CMD_TRANSMIT | TxCB_CMD_S + | TxCB_CMD_I | TxCB_CMD_EL)); + HAL_WRITE_UINT32(p_txcb + TxCB_LINK, + HAL_CTOLE32(VIRT_TO_BUS((cyg_uint32)p_txcb))); + HAL_WRITE_UINT32(p_txcb + TxCB_TBD_ADDR, + HAL_CTOLE32(0xFFFFFFFF)); + HAL_WRITE_UINT8(p_txcb + TxCB_TBD_NUMBER, 0); + HAL_WRITE_UINT8(p_txcb + TxCB_TX_THRESHOLD, 16); + HAL_WRITE_UINT16(p_txcb + TxCB_COUNT, + HAL_CTOLE16(TxCB_COUNT_EOF | total_len)); + + // Copy from the sglist into the txcb + to_p = p_txcb + TxCB_BUFFER; + + CYG_ASSERT( 0 < sg_len, "sg_len underflow" ); + CYG_ASSERT( MAX_ETH_DRV_SG >= sg_len, "sg_len overflow" ); + + for ( last_sg = &sg_list[sg_len]; sg_list < last_sg; sg_list++ ) { + cyg_uint8 *from_p; + int l; + + from_p = (cyg_uint8 *)(sg_list->buf); + l = sg_list->len; + + if ( l > total_len ) + l = total_len; + + memcpy( (unsigned char *)to_p, from_p, l ); + to_p += l; + total_len -= l; + + if ( 0 > total_len ) + break; // Should exit via sg_last normally + } + + CYG_ASSERT( 0 == total_len, "length mismatch in tx" ); + CYG_ASSERT( last_sg == sg_list, "sg count mismatch in tx" ); + CYG_ASSERT( p_txcb + TxCB_BUFFER < to_p, "to_p wild in tx" ); + CYG_ASSERT( p_txcb + TxCB_BUFFER + MAX_TX_PACKET_SIZE >= to_p, + "to_p overflow in tx" ); + + // Next descriptor + if ( ++tx_descriptor_add >= MAX_TX_DESCRIPTORS) + tx_descriptor_add = 0; + p_i82559->tx_descriptor_add = tx_descriptor_add; + + // From this instant, interrupts can advance the world and start, + // even complete, this tx request... + + if ( p_i82559->tx_descriptor_remove == tx_descriptor_add ) + p_i82559->tx_queue_full = 1; + } + + // Try advancing the Tx Machine regardless + + // no more interrupts until started + ints = Mask82559Interrupt(p_i82559); + + // Check that either: + // tx is already active, there is other stuff queued, + // OR this tx just added is the current active one + // OR this tx just added is already complete + CYG_ASSERT( + // The machine is busy: + (p_i82559->tx_in_progress == 1) || + // or: The machine is idle and this just added is the next one + (((p_i82559->tx_descriptor_add-1) == p_i82559->tx_descriptor_active) + || ((0 == p_i82559->tx_descriptor_add) && + ((MAX_TX_DESCRIPTORS-1) == p_i82559->tx_descriptor_active))) || + // or: This tx is already complete + (p_i82559->tx_descriptor_add == p_i82559->tx_descriptor_active), + "Active/add mismatch" ); + + // Advance TxMachine atomically + TxMachine(p_i82559); + Acknowledge82559Interrupt(p_i82559); // This can eat an Rx interrupt, so + PacketRxReady(p_i82559); + UnMask82559Interrupt(p_i82559, ints); +} + +// ------------------------------------------------------------------------ +// +// Function : i82559_reset +// +// ------------------------------------------------------------------------ +static void +i82559_reset(struct i82559* p_i82559) +{ + cyg_uint32 ioaddr = p_i82559->io_address; + + // First do soft reset. This must be done before the hard reset, + // otherwise we may create havoc on the PCI bus. + // Wait 20 uSecs afterwards for chip to settle. + OUTL(I82559_SELECTIVE_RESET, ioaddr + SCBPort); + udelay(20); + + // Do hard reset now that the controller is off the PCI bus. + // Wait 20 uSecs afterwards for chip to settle. + OUTL(I82559_RESET, ioaddr + SCBPort); + udelay(20); + + // Set the base addresses + wait_for_cmd_done(ioaddr, WAIT_RU); + OUTL(0, ioaddr + SCBPointer); + OUTW(SCB_M | RUC_ADDR_LOAD, ioaddr + SCBCmd); + + wait_for_cmd_done(ioaddr, WAIT_CU); + OUTL(0, ioaddr + SCBPointer); + OUTW(SCB_M | CU_ADDR_LOAD, ioaddr + SCBCmd); +} + +// ------------------------------------------------------------------------ +// +// INTERRUPT HANDLERS +// +// ------------------------------------------------------------------------ + +static cyg_uint32 +eth_isr(cyg_vector_t vector, cyg_addrword_t data) +{ + struct i82559* p_i82559 = (struct i82559 *)data; + cyg_uint16 status; + cyg_uint32 ioaddr; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_isr: Bad device pointer %x\n", p_i82559 ); +#endif + return 0; + } + + ioaddr = p_i82559->io_address; + status = INW(ioaddr + SCBStatus); + + // Acknowledge all INT sources that were active + OUTW( status & SCB_INTACK_MASK, ioaddr + SCBStatus); + // (see pages 6-10 & 6-90) + +#ifdef KEEP_STATISTICS + statistics[p_i82559->index].interrupts++; + + // receiver left ready state ? + if ( status & SCB_STATUS_RNR ) + statistics[p_i82559->index].rx_resource++; + + // frame receive interrupt ? + if ( status & SCB_STATUS_FR ) + statistics[p_i82559->index].rx_count++; + + // transmit interrupt ? + if ( status & SCB_STATUS_CX ) + statistics[p_i82559->index].tx_complete++; +#endif + + // Advance the Tx Machine regardless + TxMachine(p_i82559); + + // it should have settled down now... + Acknowledge82559Interrupt(p_i82559); + + return CYG_ISR_CALL_DSR; // schedule DSR +} + + +// ------------------------------------------------------------------------ +#ifdef CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT +static cyg_uint32 +eth_mux_isr(cyg_vector_t vector, cyg_addrword_t data) +{ + int i; + struct i82559* p_i82559; + + for (i = 0; i < CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT; i++) { + p_i82559 = i82559_priv_array[i]; + if ( p_i82559->active ) + (void)eth_isr( vector, (cyg_addrword_t)p_i82559 ); + } + + return CYG_ISR_CALL_DSR; +} +#endif + +// ------------------------------------------------------------------------ + +static void +eth_dsr(cyg_vector_t vector, cyg_ucount32 count, cyg_addrword_t data) +{ + struct i82559* p_i82559 = (struct i82559 *)data; + struct cyg_netdevtab_entry *ndp = + (struct cyg_netdevtab_entry *)(p_i82559->ndp); + struct eth_drv_sc *sc = (struct eth_drv_sc *)(ndp->device_instance); + + // but here, it must be a *sc: + eth_drv_dsr( vector, count, (cyg_addrword_t)sc ); +} + +// ------------------------------------------------------------------------ +// This is called from the function below (used to be uni-DSR) +void +i82559_deliver(struct eth_drv_sc *sc) +{ + struct i82559* p_i82559 = (struct i82559 *)(sc->driver_private); + + // First pass any rx data up the stack + PacketRxReady(p_i82559); + + // Then scan for completed Txen and inform the stack + TxDone(p_i82559); +} + + +// ------------------------------------------------------------------------ + +#ifdef CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT +static void +mux_deliver(struct eth_drv_sc *sc) +{ + int i; + + // Since this must mux all devices, the incoming arg is ignored. + for (i = 0; i < CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT; i++) { + sc = i82559_sc_array[i]; + if ( p_i82559->active ) + i82559_deliver( sc ); + } +} +#endif + +// ------------------------------------------------------------------------ +// Device table entry to operate the chip in a polled mode. +// Only diddle the interface we were asked to! + +void +i82559_poll(struct eth_drv_sc *sc) +{ + struct i82559 *p_i82559; + int ints; + p_i82559 = (struct i82559 *)sc->driver_private; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_poll: Bad device pointer %x\n", p_i82559 ); +#endif + return; + } + + // Do these atomically + ints = Mask82559Interrupt(p_i82559); + + // As it happens, this driver always requests the DSR to be called: + (void)eth_isr( p_i82559->vector, (cyg_addrword_t)p_i82559 ); + + // (no harm in calling this ints-off also, when polled) + i82559_deliver( sc ); + + Acknowledge82559Interrupt(p_i82559); + UnMask82559Interrupt(p_i82559, ints); +} + +// ------------------------------------------------------------------------ +// Determine interrupt vector used by a device - for attaching GDB stubs +// packet handler. +int +i82559_int_vector(struct eth_drv_sc *sc) +{ + struct i82559 *p_i82559; + p_i82559 = (struct i82559 *)sc->driver_private; + return (p_i82559->vector); +} + +#if 0 +int +i82559_int_op( struct eth_drv_sc *sc, int mask) +{ + struct i82559 *p_i82559; + p_i82559 = (struct i82559 *)sc->driver_private; + + if ( 1 == mask ) + return Mask82559Interrupt( p_i82559 ); + + if ( 0 == mask ) + UnMask82559Interrupt( p_i82559, 0x0fffffff ); // enable all + + return 0; +} +#endif + + +// ------------------------------------------------------------------------ +// +// Function : pci_init_find_82559s +// +// This is called exactly once at the start of time to: +// o scan the PCI bus for objects +// o record them in the device table +// o acquire all the info needed for the driver to access them +// o instantiate interrupts for them +// o attach those interrupts appropriately +// ------------------------------------------------------------------------ +static int +pci_init_find_82559s( void ) +{ + cyg_pci_device_id devid; + cyg_pci_device dev_info; + cyg_uint16 cmd; + int device_index; + int found_devices = 0; + +#ifdef DEBUG + db_printf("pci_init_find_82559s()\n"); +#endif + + // allocate memory to be used in ioctls later + if (mem_reserved_ioctl != (void*)0) { +#ifdef DEBUG + db_printf("pci_init_find_82559s() called > once\n"); +#endif + return 0; + } + + CYG_ASSERT( CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) == + CYGHWR_INTEL_I82559_PCI_MEM_MAP_BASE, + "PCI window configured does not match PCI memory section base" ); + CYG_ASSERT( CYGMEM_SECTION_pci_window_SIZE == + CYGHWR_INTEL_I82559_PCI_MEM_MAP_SIZE, + "PCI window configured does not match PCI memory section size" ); + + if ( CYGARC_UNCACHED_ADDRESS((CYG_ADDRWORD)CYGMEM_SECTION_pci_window) != + CYGHWR_INTEL_I82559_PCI_MEM_MAP_BASE + || + CYGMEM_SECTION_pci_window_SIZE != + CYGHWR_INTEL_I82559_PCI_MEM_MAP_SIZE ) { +#ifdef DEBUG + db_printf("pci_init_find_82559s(): PCI window misconfigured\n"); +#endif + return 0; + } + + // First initialize the heap in PCI window'd memory + i82559_heap_size = CYGHWR_INTEL_I82559_PCI_MEM_MAP_SIZE; + i82559_heap_base = (cyg_uint8 *)CYGHWR_INTEL_I82559_PCI_MEM_MAP_BASE; + i82559_heap_free = i82559_heap_base; + + mem_reserved_ioctl = pciwindow_mem_alloc(MAX_MEM_RESERVED_IOCTL); + + cyg_pci_init(); +#ifdef DEBUG + db_printf("Finished cyg_pci_init();\n"); +#endif + + devid = CYG_PCI_NULL_DEVID; + + for (device_index = 0; + device_index < CYGNUM_DEVS_ETH_INTEL_I82559_DEV_COUNT; + device_index++) { + struct i82559 *p_i82559 = i82559_priv_array[device_index]; + + p_i82559->index = device_index; + + // Intel 82559 and 82557 are virtually identical, + // with different dev codes + if (cyg_pci_find_device(0x8086, 0x1030, &devid) || + cyg_pci_find_device(0x8086, 0x1209, &devid) || + cyg_pci_find_device(0x8086, 0x1229, &devid) ) { +#ifdef DEBUG + db_printf("eth%d = 82559\n", device_index); +#endif + cyg_pci_get_device_info(devid, &dev_info); + + if (cyg_pci_translate_interrupt(&dev_info, &p_i82559->vector)) { +#ifdef DEBUG + db_printf(" Wired to HAL vector %d\n", p_i82559->vector); +#endif + cyg_drv_interrupt_create( + p_i82559->vector, + 0, // Priority - unused + (CYG_ADDRWORD)p_i82559, // Data item passed to ISR & DSR + eth_isr, // ISR + eth_dsr, // DSR + &p_i82559->interrupt_handle, // handle to intr obj + &p_i82559->interrupt_object ); // space for int obj + + cyg_drv_interrupt_attach(p_i82559->interrupt_handle); + + // Don't unmask the interrupt yet, that could get us into a + // race. +#ifdef CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT + // ALSO attach it to MUX interrupt for multiplexed + // interrupts. This is for certain boards where the + // PCI backplane is wired "straight through" instead of + // with a rotation of interrupt lines in the different + // slots. + { + static cyg_handle_t mux_interrupt_handle = 0; + static cyg_interrupt mux_interrupt_object; + + if ( ! mux_interrupt_handle ) { +#ifdef DEBUG + db_printf(" Also attaching to HAL vector %d\n", + CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT); +#endif + cyg_drv_interrupt_create( + CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT, + 0, // Priority - unused + (CYG_ADDRWORD)p_i82559,// Data item passed to ISR and DSR + eth_mux_isr, // ISR + eth_dsr, // DSR + &mux_interrupt_handle, + &mux_interrupt_object ); + + cyg_drv_interrupt_attach(mux_interrupt_handle); + } + } +#endif // CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT + } + else { + p_i82559->vector=0; +#ifdef DEBUG + db_printf(" Does not generate interrupts.\n"); +#endif + } + + if (cyg_pci_configure_device(&dev_info)) { +#ifdef DEBUG + int i; + db_printf("Found device on bus %d, devfn 0x%02x:\n", + CYG_PCI_DEV_GET_BUS(devid), + CYG_PCI_DEV_GET_DEVFN(devid)); + + if (dev_info.command & CYG_PCI_CFG_COMMAND_ACTIVE) { + db_printf(" Note that board is active. Probed" + " sizes and CPU addresses invalid!\n"); + } + db_printf(" Vendor 0x%04x", dev_info.vendor); + db_printf("\n Device 0x%04x", dev_info.device); + db_printf("\n Command 0x%04x, Status 0x%04x\n", + dev_info.command, dev_info.status); + + db_printf(" Class/Rev 0x%08x", dev_info.class_rev); + db_printf("\n Header 0x%02x\n", dev_info.header_type); + + db_printf(" SubVendor 0x%04x, Sub ID 0x%04x\n", + dev_info.header.normal.sub_vendor, + dev_info.header.normal.sub_id); + + for(i = 0; i < CYG_PCI_MAX_BAR; i++) { + db_printf(" BAR[%d] 0x%08x /", i, dev_info.base_address[i]); + db_printf(" probed size 0x%08x / CPU addr 0x%08x\n", + dev_info.base_size[i], dev_info.base_map[i]); + } + db_printf(" eth%d configured\n", device_index); +#endif + found_devices++; + p_i82559->found = 1; + p_i82559->active = 0; + p_i82559->devid = devid; + p_i82559->memory_address = dev_info.base_map[0]; + p_i82559->io_address = dev_info.base_map[1]; +#ifdef DEBUG + db_printf(" memory address = 0x%08x\n", dev_info.base_map[0]); + db_printf(" I/O address = 0x%08x\n", dev_info.base_map[1]); +#endif + + // Don't use cyg_pci_set_device_info since it clears + // some of the fields we want to print out below. + cyg_pci_read_config_uint16(dev_info.devid, + CYG_PCI_CFG_COMMAND, &cmd); + cmd |= (CYG_PCI_CFG_COMMAND_IO // enable I/O space + | CYG_PCI_CFG_COMMAND_MEMORY // enable memory space + | CYG_PCI_CFG_COMMAND_MASTER); // enable bus master + cyg_pci_write_config_uint16(dev_info.devid, + CYG_PCI_CFG_COMMAND, cmd); + + // Now the PCI part of the device is configured, reset + // it. This should make it safe to enable the + // interrupt + i82559_reset(p_i82559); + + if (p_i82559->vector != 0) { + cyg_drv_interrupt_acknowledge(p_i82559->vector); + cyg_drv_interrupt_unmask(p_i82559->vector); + } +#ifdef DEBUG + db_printf(" **** Device enabled for I/O and Memory " + "and Bus Master\n"); +#endif + } + else { + p_i82559->found = 0; + p_i82559->active = 0; +#ifdef DEBUG + db_printf("Failed to configure device %d\n",device_index); +#endif + } + } + else { + p_i82559->found = 0; + p_i82559->active = 0; +#ifdef DEBUG + db_printf("eth%d not found\n", device_index); +#endif + } + } + + if (0 == found_devices) + return 0; + +#ifdef CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT + // Now enable the mux shared interrupt if it is in use + if (mux_interrupt_handle) { + cyg_drv_interrupt_acknowledge(CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT); + cyg_drv_interrupt_unmask(CYGNUM_DEVS_ETH_INTEL_I82559_MUX_INTERRUPT); + } +#endif + + // Now a delay to ensure the hardware has "come up" before you try to + // use it. Yes, really, the full 2 seconds. It's only really + // necessary if DEBUG is off - otherwise all that printout wastes + // enough time. No kidding. + udelay( 2000000 ); + return 1; +} + +// This function is called to enable the device by setting the necessary +// minimum of the configuration. +static int +eth_set_config(struct i82559* p_i82559) +{ + cyg_uint32 ioaddr; + volatile CFG *ccs; + volatile cyg_uint8* config_bytes; + cyg_uint16 status; + int count; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "eth_set_config: Bad device pointer %x\n", p_i82559 ); +#endif + return -1; + } + + ioaddr = p_i82559->io_address; + + // Check the malloc we did earlier worked + ccs = (CFG *)mem_reserved_ioctl; + if (ccs == (void*)0) + return 2; // Failed + + // Prepare header + HAL_WRITE_UINT16(ccs + CFG_CMD, + (CFG_CMD_EL | CFG_CMD_SUSPEND | CFG_CMD_CONFIGURE)); + HAL_WRITE_UINT16(ccs + CFG_STATUS, 0); + HAL_WRITE_UINT32(ccs + CFG_CB_LINK_OFFSET, + HAL_CTOLE32(VIRT_TO_BUS((cyg_uint32)ccs))); + + // Default values from the Intel Manual + config_bytes = ccs + CFG_BYTES; + config_bytes[0]=0x9; + config_bytes[1]=0xc; + config_bytes[2]=0x0; + config_bytes[3]=0x0; + config_bytes[4]=0x0; + config_bytes[5]=0x0; + config_bytes[6]=0x32; + config_bytes[7]=0x1; + // This is the bit we go through all the trouble for. Must be set + // before the device activates. + config_bytes[8]=0x1; + + // Let chip read configuration + wait_for_cmd_done(ioaddr, WAIT_CU); + OUTL(VIRT_TO_BUS(ccs), ioaddr + SCBPointer); + OUTW(SCB_M | CU_START, ioaddr + SCBCmd); + + // ...and wait for it to complete operation + count = 1000; + do { + HAL_READ_UINT16(ccs + CFG_STATUS, status); + udelay(1); + } while (0 == (status & CFG_STATUS_C) && (count-- > 0)); + + // Check status + if ((status & (CFG_STATUS_C | CFG_STATUS_OK)) + != (CFG_STATUS_C | CFG_STATUS_OK)) { + // Failed! +#ifdef DEBUG_82559 + os_printf("%s:%d Config update failed\n", __FUNCTION__, __LINE__); +#endif + return 1; + } + + return 0; +} + +#ifdef CYGPKG_NET +// ------------------------------------------------------------------------ +// +// Function : eth_set_promiscuous_mode +// +// Return : 0 = It worked. +// non0 = It failed. +// ------------------------------------------------------------------------ + +static int +eth_set_promiscuous_mode(struct i82559* p_i82559) +{ + cyg_uint32 ioaddr; + volatile CFG *ccs; + volatile cyg_uint8* config_bytes; + cyg_uint16 status; + int count; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "eth_set_promiscuos_mode: Bad device pointer %x\n", + p_i82559 ); +#endif + return -1; + } + + ioaddr = p_i82559->io_address; + + // Check the malloc we did earlier worked + ccs = (CFG *)mem_reserved_ioctl; + if (ccs == (void*)0) + return 2; // Failed + + // Prepare header + HAL_WRITE_UINT16(ccs + CFG_CMD, + (CFG_CMD_EL | CFG_CMD_SUSPEND | CFG_CMD_CONFIGURE)); + HAL_WRITE_UINT16(ccs + CFG_STATUS, 0); + HAL_WRITE_UINT32(ccs + CFG_CB_LINK_OFFSET, + HAL_CTOLE32(VIRT_TO_BUS((cyg_uint32)ccs))); + + // Default values from the Intel Manual + config_bytes = ccs + CFG_BYTES; + config_bytes[0]=0x13; + config_bytes[1]=0x8; + config_bytes[2]=0x0; + config_bytes[3]=0x0; + config_bytes[4]=0x0; + config_bytes[5]=0x0; + config_bytes[6]=0xb2; // (promisc ? 0x80 : 0) | 0x32 for small stats, + config_bytes[7]=0x1; // \ ditto | 0x12 for stats with PAUSE stats + config_bytes[8]=0x0; // \ ditto | 0x16 for PAUSE + TCO stats + config_bytes[9]=0x0; + config_bytes[10]=0x28; + config_bytes[11]=0x0; + config_bytes[12]=0x60; + config_bytes[13]=0x0; // arp + config_bytes[14]=0x0; // arp + + config_bytes[15]=0x81; // promiscuous mode set + // \ or 0x80 for normal mode. + config_bytes[16]=0x0; + config_bytes[17]=0x40; + config_bytes[18]=0x72; // Keep the Padding Enable bit + + // Let chip read configuration + wait_for_cmd_done(ioaddr, WAIT_CU); + OUTL(VIRT_TO_BUS(ccs), ioaddr + SCBPointer); + OUTW(SCB_M | CU_START, ioaddr + SCBCmd); + + // ...and wait for it to complete operation + count = 1000; + do { + HAL_READ_UINT16(ccs + CFG_STATUS, status); + udelay(1); + } while (0 == (status & CFG_STATUS_C) && (count-- > 0)); + + // Check status + if ((status & (CFG_STATUS_C | CFG_STATUS_OK)) + != (CFG_STATUS_C | CFG_STATUS_OK)) { + // Failed! +#ifdef DEBUG_82559 + os_printf("%s:%d Config update failed\n", __FUNCTION__, __LINE__); +#endif + return 1; + } + return 0; +} +#endif + +// ------------------------------------------------------------------------ +// We use this as a templete when writing a new MAC address into the +// eeproms. The MAC address in the first few bytes is over written +// with the correct MAC address and then the whole lot is programmed +// into the serial EEPROM. The checksum is calculated on the fly and +// sent instead of the last two bytes. +// The values are copied from the Intel EtherPro10/100+ &c devices +// in the EBSA boards. + +#ifdef CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM + +#define ee00 0x00, 0x00 // shorthand + +static char eeprom_burn[126] = { +/* halfword addresses! */ +/* 0: */ 0x00, 0x90, 0x27, 0x8c, 0x57, 0x82, 0x03, 0x02, +/* 4: */ ee00 , 0x01, 0x02, 0x01, 0x47, ee00 , +/* 8: */ 0x13, 0x72, 0x06, 0x83, 0xa2, 0x40, 0x0c, 0x00, +/* C: */ 0x86, 0x80, ee00 , ee00 , ee00 , +/* 10: */ ee00 , ee00 , ee00 , ee00 , +/* 14: */ ee00 , ee00 , ee00 , ee00 , +/* 18: */ ee00 , ee00 , ee00 , ee00 , +/* 1C: */ ee00 , ee00 , ee00 , ee00 , +/* 20: */ ee00 , ee00 , ee00 , ee00 , +/* 24: */ ee00 , ee00 , ee00 , ee00 , +/* 28: */ ee00 , ee00 , ee00 , ee00 , +/* 2C: */ ee00 , ee00 , ee00 , ee00 , +/* 30: */ 0x28, 0x01, ee00 , ee00 , ee00 , +/* 34: */ ee00 , ee00 , ee00 , ee00 , +/* 38: */ ee00 , ee00 , ee00 , ee00 , +/* 3C: */ ee00 , ee00 , ee00 +}; +#undef ee00 + +#endif + +// ------------------------------------------------------------------------ +// +// Function : eth_set_mac_address +// +// Return : 0 = It worked. +// non0 = It failed. +// ------------------------------------------------------------------------ +static int +eth_set_mac_address(struct i82559* p_i82559, char *addr) +{ + cyg_uint32 ioaddr; + cyg_uint16 status; + volatile CFG *ccs; + volatile cyg_uint8* config_bytes; + int count; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "eth_set_mac_address : Bad device pointer %x\n", + p_i82559 ); +#endif + return -1; + } + + ioaddr = p_i82559->io_address; + + ccs = (CFG *)mem_reserved_ioctl; + if (ccs == (void*)0) + return 2; + + HAL_WRITE_UINT16(ccs + CFG_CMD, + (CFG_CMD_EL | CFG_CMD_SUSPEND | CFG_CMD_IAS)); + HAL_WRITE_UINT16(ccs + CFG_STATUS, 0); + HAL_WRITE_UINT32(ccs + CFG_CB_LINK_OFFSET, + HAL_CTOLE32(VIRT_TO_BUS((cyg_uint32)ccs))); + + config_bytes = ccs + CFG_BYTES; + memcpy((char *)(config_bytes),addr,6); + config_bytes[6]=0x0; + config_bytes[7]=0x0; + + // Let chip read new ESA + wait_for_cmd_done(ioaddr, WAIT_CU); + OUTL(VIRT_TO_BUS(ccs), ioaddr + SCBPointer); + OUTW(SCB_M | CU_START, ioaddr + SCBCmd); + + // ...and wait for it to complete operation + count = 1000; + do { + HAL_READ_UINT16(ccs + CFG_STATUS, status); + udelay(1); + } while (0 == (status & CFG_STATUS_C) && (count-- > 0)); + + // Check status + HAL_READ_UINT16(ccs + CFG_STATUS, status); + if ((status & (CFG_STATUS_C | CFG_STATUS_OK)) + != (CFG_STATUS_C | CFG_STATUS_OK)) { +#ifdef DEBUG_82559 + os_printf("%s:%d ESA update failed\n", __FUNCTION__, __LINE__); +#endif + return 3; + } + +#ifdef CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM + { + int checksum, i, count; + // (this is the length of the *EEPROM*s address, not MAC address) + int addr_length; + + addr_length = get_eeprom_size( ioaddr ); + + // now set this address in the device eeprom .... + (void)memcpy(eeprom_burn,addr,6); + + // No idea what these were for... + // eeprom_burn[20] &= 0xfe; + // eeprom_burn[20] |= p_i82559->index; + + program_eeprom( ioaddr, addr_length, eeprom_burn ); + + // update 82559 driver data structure ... + udelay( 100000 ); + + // by reading EEPROM to get the mac address back + for (checksum = 0, i = 0, count = 0; count < 64; count++) { + cyg_uint16 value; + // read word from eeprom + value = read_eeprom(ioaddr, count, addr_length); + checksum += value; + if (count < 3) { + p_i82559->mac_address[i++] = value & 0xFF; + p_i82559->mac_address[i++] = (value >> 8) & 0xFF; + } + } + +#ifdef DEBUG + os_printf("MAC Address = %02X %02X %02X %02X %02X %02X\n", + p_i82559->mac_address[0], p_i82559->mac_address[1], + p_i82559->mac_address[2], p_i82559->mac_address[3], + p_i82559->mac_address[4], p_i82559->mac_address[5]); +#endif + + p_i82559->mac_addr_ok = 1; + + for ( i = 0, count = 0; i < 6; i++ ) + if ( p_i82559->mac_address[i] != addr[i] ) + count++; + + if ( count ) { +#ifdef DEBUG + os_printf( "Warning: MAC Address read back wrong! %d bytes differ.\n", + count ); +#endif + p_i82559->mac_addr_ok = 0; + } + + // If the EEPROM checksum is wrong, the MAC address read from + // the EEPROM is probably wrong as well. In that case, we + // don't set mac_addr_ok. + if ((checksum & 0xFFFF) != 0xBABA) { +#ifdef DEBUG + os_printf( "Warning: Invalid EEPROM checksum %04X for device %d\n", + checksum, p_i82559->index); +#endif + p_i82559->mac_addr_ok = 0; + } + +#else // CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM + + // record the MAC address in the device structure + p_i82559->mac_address[0] = addr[0]; + p_i82559->mac_address[1] = addr[1]; + p_i82559->mac_address[2] = addr[2]; + p_i82559->mac_address[3] = addr[3]; + p_i82559->mac_address[4] = addr[4]; + p_i82559->mac_address[5] = addr[5]; + p_i82559->mac_addr_ok = 1; + +#endif // ! CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM + + return p_i82559->mac_addr_ok ? 0 : 1; +} + +#ifdef CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM +// ------------------------------------------------------------------------ +static void +write_eeprom(long ioaddr, int location, int addr_len, unsigned short value) +{ + int ee_addr = ioaddr + SCBeeprom; + int write_cmd = location | EE_WRITE_CMD(addr_len); + int i; + + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay( 100 ); + OUTW(EE_ENB, ee_addr); + eeprom_delay( 100 ); + +// os_printf("\n write_eeprom : write_cmd : %x",write_cmd); +// os_printf("\n addr_len : %x value : %x ",addr_len,value); + + /* Shift the write command bits out. */ + for (i = (addr_len+2); i >= 0; i--) { + short dataval = (write_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(EE_ENB | dataval, ee_addr); + eeprom_delay(100); + OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(150); + } + OUTW(EE_ENB, ee_addr); + + for (i = 15; i >= 0; i--) { + short dataval = (value & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(EE_ENB | dataval, ee_addr); + eeprom_delay(100); + OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(150); + } + + /* Terminate the EEPROM access. */ + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay(150000); // let the write take effect +} + +// ------------------------------------------------------------------------ +static int +write_enable_eeprom(long ioaddr, int addr_len) +{ + int ee_addr = ioaddr + SCBeeprom; + int write_en_cmd = EE_WRITE_EN_CMD(addr_len); + int i; + + OUTW(EE_ENB & ~EE_CS, ee_addr); + OUTW(EE_ENB, ee_addr); + +#ifdef DEBUG_82559 + os_printf("write_en_cmd : %x",write_en_cmd); +#endif + + // Shift the wr/er enable command bits out. + for (i = (addr_len+2); i >= 0; i--) { + short dataval = (write_en_cmd & (1 << i)) ? EE_DATA_WRITE : 0; + OUTW(EE_ENB | dataval, ee_addr); + eeprom_delay(100); + OUTW(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr); + eeprom_delay(150); + } + + // Terminate the EEPROM access. + OUTW(EE_ENB & ~EE_CS, ee_addr); + eeprom_delay(EEPROM_DONE_DELAY); +} + + +// ------------------------------------------------------------------------ +static void +program_eeprom(cyg_uint32 ioaddr, cyg_uint32 eeprom_size, cyg_uint8 *data) +{ + cyg_uint32 i; + cyg_uint16 checksum = 0; + cyg_uint16 value; + + // First enable erase/write operations on the eeprom. + // This is done through the EWEN instruction. + write_enable_eeprom( ioaddr, eeprom_size ); + + for (i=0 ; i< 63 ; i++) { + value = ((unsigned short *)data)[i]; + checksum += value; +#ifdef DEBUG_82559 + os_printf("\n i : %x ... value to be written : %x",i,value); +#endif + write_eeprom( ioaddr, i, eeprom_size, value); +#ifdef DEBUG_82559 + os_printf("\n val read : %x ",read_eeprom(ioaddr,i,eeprom_size)); +#endif + } + value = 0xBABA - checksum; +#ifdef DEBUG_82559 + os_printf("\n i : %x ... checksum adjustment val to be written : %x",i,value); +#endif + write_eeprom( ioaddr, i, eeprom_size, value ); +} + +// ------------------------------------------------------------------------ +#endif // ! CYGPKG_DEVS_ETH_INTEL_I82559_WRITE_EEPROM + + +// ------------------------------------------------------------------------ +// +// Function : eth_get_mac_address +// +// ------------------------------------------------------------------------ +#ifdef ETH_DRV_GET_MAC_ADDRESS +static int +eth_get_mac_address(struct i82559* p_i82559, char *addr) +{ + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "eth_get_mac_address : Bad device pointer %x\n", + p_i82559 ); +#endif + return -1; + } + + memcpy( addr, (char *)(&p_i82559->mac_address[0]), 6 ); + return 0; +} +#endif +// ------------------------------------------------------------------------ +// +// Function : i82559_ioctl +// +// ------------------------------------------------------------------------ +static int +i82559_ioctl(struct eth_drv_sc *sc, unsigned long key, + void *data, int data_length) +{ + struct i82559 *p_i82559; + + p_i82559 = (struct i82559 *)sc->driver_private; + + IF_BAD_82559( p_i82559 ) { +#ifdef DEBUG + os_printf( "i82559_ioctl/control: Bad device pointer %x\n", p_i82559 ); +#endif + return -1; + } + +#ifdef DEBUG + db_printf( "i82559_ioctl: device eth%d at %x; key is 0x%x, data at %x[%d]\n", + p_i82559->index, p_i82559, key, data, data_length ); +#endif + + switch ( key ) { + +#ifdef ETH_DRV_SET_MAC_ADDRESS + case ETH_DRV_SET_MAC_ADDRESS: + if ( 6 != data_length ) + return -2; + return eth_set_mac_address( p_i82559, data ); +#endif + +#ifdef ETH_DRV_GET_MAC_ADDRESS + case ETH_DRV_GET_MAC_ADDRESS: + return eth_get_mac_address( p_i82559, data ); +#endif + +#ifdef ETH_DRV_GET_IF_STATS_UD + case ETH_DRV_GET_IF_STATS_UD: // UD == UPDATE + ETH_STATS_INIT( sc ); // so UPDATE the statistics structure +#endif + // drop through +#ifdef ETH_DRV_GET_IF_STATS + case ETH_DRV_GET_IF_STATS: +#endif +#if defined(ETH_DRV_GET_IF_STATS) || defined (ETH_DRV_GET_IF_STATS_UD) + { + struct ether_drv_stats *p = (struct ether_drv_stats *)data; + int i; + static unsigned char my_chipset[] + = { ETH_DEV_DOT3STATSETHERCHIPSET }; + + strcpy( p->description, CYGDAT_DEVS_ETH_DESCRIPTION ); + CYG_ASSERT( 48 > strlen(p->description), "Description too long" ); + + for ( i = 0; i < SNMP_CHIPSET_LEN; i++ ) + if ( 0 == (p->snmp_chipset[i] = my_chipset[i]) ) + break; + + i = i82559_status( sc ); + + if ( !( i & GEN_STATUS_LINK) ) { + p->operational = 2; // LINK DOWN + p->duplex = 1; // UNKNOWN + p->speed = 0; + } + else { + p->operational = 3; // LINK UP + p->duplex = (i & GEN_STATUS_FDX) ? 3 : 2; // 2 = SIMPLEX, 3 = DUPLEX + p->speed = ((i & GEN_STATUS_100MBPS) ? 100 : 10) * 1000000; + } + +#ifdef KEEP_STATISTICS + { + I82559_COUNTERS *pc = &i82559_counters[ p_i82559->index ]; + STATISTICS *ps = &statistics[ p_i82559->index ]; + + // Admit to it... + p->supports_dot3 = true; + + // Those commented out are not available on this chip. + + p->tx_good = pc->tx_good ; + p->tx_max_collisions = pc->tx_max_collisions ; + p->tx_late_collisions = pc->tx_late_collisions ; + p->tx_underrun = pc->tx_underrun ; + p->tx_carrier_loss = pc->tx_carrier_loss ; + p->tx_deferred = pc->tx_deferred ; + //p->tx_sqetesterrors = pc->tx_sqetesterrors ; + p->tx_single_collisions = pc->tx_single_collisions; + p->tx_mult_collisions = pc->tx_mult_collisions ; + p->tx_total_collisions = pc->tx_total_collisions ; + p->rx_good = pc->rx_good ; + p->rx_crc_errors = pc->rx_crc_errors ; + p->rx_align_errors = pc->rx_align_errors ; + p->rx_resource_errors = pc->rx_resource_errors ; + p->rx_overrun_errors = pc->rx_overrun_errors ; + p->rx_collisions = pc->rx_collisions ; + p->rx_short_frames = pc->rx_short_frames ; + //p->rx_too_long_frames = pc->rx_too_long_frames ; + //p->rx_symbol_errors = pc->rx_symbol_errors ; + + p->interrupts = ps->interrupts ; + p->rx_count = ps->rx_count ; + p->rx_deliver = ps->rx_deliver ; + p->rx_resource = ps->rx_resource ; + p->rx_restart = ps->rx_restart ; + p->tx_count = ps->tx_count ; + p->tx_complete = ps->tx_complete ; + p->tx_dropped = ps->tx_dropped ; + } +#endif // KEEP_STATISTICS + + p->tx_queue_len = MAX_TX_DESCRIPTORS; + + return 0; // OK + } +#endif + + default: + break; + } + return -1; +} + +// ------------------------------------------------------------------------ +// +// Statistics update... +// +// ------------------------------------------------------------------------ + +#ifdef KEEP_STATISTICS +#ifdef CYGDBG_DEVS_ETH_INTEL_I82559_KEEP_82559_STATISTICS +void +update_statistics(struct i82559* p_i82559) +{ + I82559_COUNTERS *p_statistics; + cyg_uint32 *p_counter; + cyg_uint32 *p_register; + int reg_count, ints; + + ints = Mask82559Interrupt(p_i82559); + + // This points to the sthared memory stats area/command block + p_statistics = (I82559_COUNTERS *)(p_i82559->p_statistics); + + if ( (p_statistics->done & 0xFFFF) == 0xA007 ) { + p_counter = (cyg_uint32 *)&i82559_counters[ p_i82559->index ]; + p_register = (cyg_uint32 *)p_statistics; + for ( reg_count = 0; + reg_count < sizeof( I82559_COUNTERS ) / sizeof( cyg_uint32 ) - 1; + reg_count++ ) { + *p_counter += *p_register; + p_counter++; + p_register++; + } + p_statistics->done = 0; + // start register dump + wait_for_cmd_done(p_i82559->io_address, WAIT_CU); + OUTW(CU_DUMPSTATS, p_i82559->io_address + SCBCmd); + } + Acknowledge82559Interrupt(p_i82559); // This can eat an Rx interrupt, so + PacketRxReady(p_i82559); + + UnMask82559Interrupt(p_i82559, ints); +} +#endif +#endif // KEEP_STATISTICS + +// ------------------------------------------------------------------------ +// +// +// CODE FOR DEBUGGING PURPOSES ONLY +// +// +// ------------------------------------------------------------------------ +void +dump_txcb(TxCB *p_txcb) +{ + cyg_uint16 tmp8; + cyg_uint16 tmp16; + cyg_uint32 tmp32; + + os_printf("TxCB @ %x\n", (int)p_txcb); + HAL_READ_UINT16(p_txcb + TxCB_STATUS, tmp16); + os_printf("status = %04X ", HAL_LE16TOC(tmp16)); + HAL_READ_UINT16(p_txcb + TxCB_CMD, tmp16); + os_printf("command = %04X ", HAL_LE16TOC(tmp16)); + HAL_READ_UINT32(p_txcb + TxCB_LINK, tmp32); + os_printf("link = %08X ", HAL_LE32TOC(tmp32)); + HAL_READ_UINT32(p_txcb + TxCB_TBD_ADDR, tmp32); + os_printf("tbd = %08X ", HAL_LE32TOC(tmp32)); + HAL_READ_UINT16(p_txcb + TxCB_COUNT, tmp16); + tmp16 = HAL_LE16TOC(tmp16); + os_printf("count = %d ", (tmp16 & TxCB_COUNT_MASK)); + os_printf("eof = %x ", (tmp16 & TxCB_COUNT_EOF) ? 1 : 0); + HAL_READ_UINT8(p_txcb + TxCB_TX_THRESHOLD, tmp8); + os_printf("threshold = %d ", tmp8); + HAL_READ_UINT8(p_txcb + TxCB_TBD_NUMBER, tmp8); + os_printf("tbd number = %d\n", tmp8); +} + +// This is intended to be the body of a THREAD that prints stuff every 10 +// seconds or so: +#ifdef KEEP_STATISTICS +#ifdef DISPLAY_STATISTICS +void +DisplayStatistics(void) +{ + int i; + I82559_COUNTERS *p_statistics; + cyg_uint32 *p_counter; + cyg_uint32 *p_register; + int reg_count; + int status; + + while ( 1 ) { +#ifdef DISPLAY_82559_STATISTICS + for ( i = 0; i < 2; i ++ ) { + p_statistics = (I82559_COUNTERS *)i82559[i].p_statistics; + if ( (p_statistics->done & 0xFFFF) == 0xA007 ) { + p_counter = (cyg_uint32 *)&i82559_counters[i]; + p_register = (cyg_uint32 *)&p_statistics->tx_good; + for ( reg_count = 20; reg_count != 0; reg_count--) { + *p_counter += *p_register; + p_counter++; + p_register++; + } + p_statistics->done = 0; + // start register dump + wait_for_cmd_done(i82559[i].io_address, WAIT_CU); + OUTW(CU_DUMPSTATS, i82559[i].io_address + SCBCmd); + } + } +#endif + os_printf("\nRx\nPackets = %d %d\n", + statistics[0].rx_count, statistics[1].rx_count); + os_printf("Deliver %d %d\n", + statistics[0].rx_deliver, statistics[1].rx_deliver); + os_printf("Resource %d %d\n", + statistics[0].rx_resource, statistics[1].rx_resource); + os_printf("Restart %d %d\n", + statistics[0].rx_restart, statistics[1].rx_restart); + +#ifdef DISPLAY_82559_STATISTICS + os_printf("Count %d %d\n", + i82559_counters[0].rx_good, i82559_counters[1].rx_good); + os_printf("CRC %d %d\n", + i82559_counters[0].rx_crc_errors, i82559_counters[1].rx_crc_errors); + os_printf("Align %d %d\n", + i82559_counters[0].rx_align_errors, i82559_counters[1].rx_align_errors); + os_printf("Resource %d %d\n", + i82559_counters[0].rx_resource_errors, i82559_counters[1].rx_resource_errors); + os_printf("Overrun %d %d\n", + i82559_counters[0].rx_overrun_errors, i82559_counters[1].rx_overrun_errors); + os_printf("Collision %d %d\n", + i82559_counters[0].rx_collisions, i82559_counters[1].rx_collisions); + os_printf("Short %d %d\n", + i82559_counters[0].rx_short_frames, i82559_counters[1].rx_short_frames); +#endif + os_printf("\nTx\nPackets = %d %d\n", + statistics[0].tx_count, statistics[1].tx_count); + os_printf("Complete %d %d\n", + statistics[0].tx_complete, statistics[1].tx_complete); + os_printf("Dropped %d %d\n", + statistics[0].tx_dropped, statistics[1].tx_dropped); + os_printf("Count %d %d\n", + i82559_counters[0].tx_good, i82559_counters[1].tx_good); +#ifdef DISPLAY_82559_STATISTICS + os_printf("Collision %d %d\n", + i82559_counters[0].tx_max_collisions,i82559_counters[1].tx_max_collisions); + os_printf("Late Col. %d %d\n", + i82559_counters[0].tx_late_collisions,i82559_counters[1].tx_late_collisions); + os_printf("Underrun %d %d\n", + i82559_counters[0].tx_underrun,i82559_counters[1].tx_underrun); + os_printf("Carrier %d %d\n", + i82559_counters[0].tx_carrier_loss,i82559_counters[1].tx_carrier_loss); + os_printf("Deferred %d %d\n", + i82559_counters[0].tx_deferred, i82559_counters[1].tx_deferred); + os_printf("1 Col %d %d\n", + i82559_counters[0].tx_single_collisions, i82559_counters[0].tx_single_collisions); + os_printf("Mult. Col %d %d\n", + i82559_counters[0].tx_mult_collisions, i82559_counters[0].tx_mult_collisions); + os_printf("Total Col %d %d\n", + i82559_counters[0].tx_total_collisions, i82559_counters[0].tx_total_collisions); +#endif + status = INB(i82559[0].io_address + SCBGenStatus); + os_printf("Interface 0 Link = %s, %s Mbps, %s Duplex\n", + status & GEN_STATUS_LINK ? "Up" : "Down", + status & GEN_STATUS_100MBPS ? "100" : "10", + status & GEN_STATUS_FDX ? "Full" : "Half"); + + status = INB(i82559[1].io_address + SCBGenStatus); + os_printf("Interface 1 Link = %s, %s Mbps, %s Duplex\n", + status & GEN_STATUS_LINK ? "Up" : "Down", + status & GEN_STATUS_100MBPS ? "100" : "10", + status & GEN_STATUS_FDX ? "Full" : "Half"); + + cyg_thread_delay(1000); + } +} +#endif // DISPLAY_STATISTICS +#endif // KEEP_STATISTICS + +void +dump_rfd(RFD *p_rfd, int anyway ) +{ + cyg_uint32 rxstatus, tmp32; + cyg_uint16 tmp16; + + HAL_READ_UINT32(p_rfd + RFD_STATUS, rxstatus); + if ( (0 != rxstatus) || anyway ) { + os_printf("RFD @ %x = ", (int)p_rfd); + os_printf("status = %x ", HAL_LE32TOC(rxstatus)); + HAL_READ_UINT32(p_rfd + RFD_LINK, tmp32); + os_printf("link = %x ", HAL_LE32TOC(tmp32)); +// HAL_READ_UINT32(p_rfd + RFD_RDB_ADDR, tmp32); +// os_printf("rdb_address = %x ", HAL_LE32TOC(rdb_address)); + HAL_READ_UINT16(p_rfd + RFD_COUNT, tmp16); + tmp16 = HAL_LE16TOC(tmp16); + os_printf("count = %x ", (tmp16 & RFD_COUNT_MASK)); + os_printf("f = %x ", (tmp16 & RFD_COUNT_F) ? 1 : 0); + os_printf("eof = %x ", (tmp16 & RFD_COUNT_EOF) ? 1 : 0); + HAL_READ_UINT16(p_rfd + RFD_SIZE, tmp16); + os_printf("size = %x\n", HAL_LE16TOC(tmp16)); + // FIXME: HAL_BE16TOC surely? This is the network package + // header, right? + os_printf("[%04x %04x %04x] ", + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 0)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 2)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 4))); + os_printf("[%04x %04x %04x] %04x : ", + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 6)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 8)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 10)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 12))); + os_printf("(%04x %04x %04x %04x) ", + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 14)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 16)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 18)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 20)) ); + os_printf("[%04x %04x %04x] ", + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 22)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 24)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 26)) ); + os_printf("%d.%d.%d.%d ", + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 28)), + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 29)), + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 30)), + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 31)) ); + os_printf("[%04x %04x %04x] ", + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 32)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 34)), + *((cyg_uint16 *)(p_rfd + RFD_BUFFER + 36)) ); + os_printf("%d.%d.%d.%d ...\n", + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 38)), + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 39)), + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 40)), + *((cyg_uint8 *)(p_rfd + RFD_BUFFER + 41)) ); + } +} + +void +dump_all_rfds( int intf ) +{ + struct i82559* p_i82559 = i82559_priv_array[intf]; + int i, j; + j = p_i82559->next_rx_descriptor; + os_printf("rx descriptors for interface %d (eth%d):\n", intf, intf ); + for ( i = 0; i < MAX_RX_DESCRIPTORS; i++ ) + dump_rfd( p_i82559->rx_ring[i], (i > (j-3) && (i <= j)) ); + os_printf("next rx descriptor = %x\n\n", j); +} + + +void +dump_packet(cyg_uint8 *p_buffer, int length) +{ + int count; + + count = 0; + while ( length > 0 ) { + if ( count == 0 ) + os_printf("\n"); + count = (count + 1) & 0x0F; + os_printf("%02X ", *p_buffer++); + length--; + } + os_printf("\n"); +} + +// ------------------------------------------------------------------------ + +// EOF if_i82559.c diff --git a/packages/devs/serial/generic/16x5x/current/ChangeLog b/packages/devs/serial/generic/16x5x/current/ChangeLog --- a/packages/devs/serial/generic/16x5x/current/ChangeLog +++ b/packages/devs/serial/generic/16x5x/current/ChangeLog @@ -1,3 +1,8 @@ +2001-01-24 Dave Airlie + + * src/ser_16x5x.c (pc_serial_DSR): Allow RX timeouts to be interpreted + as RXs. + 2000-12-19 Dave Airlie * src/ser_16x5x.c: Add defines for FIFO control register diff --git a/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c b/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c --- a/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c +++ b/packages/devs/serial/generic/16x5x/current/src/ser_16x5x.c @@ -425,8 +425,9 @@ pc_serial_DSR(cyg_vector_t vector, cyg_u // is pending of the low bit of the isr is *0*, not 1. HAL_READ_UINT8(base+REG_isr, _isr); while ((_isr & ISR_nIP) == 0) { - switch (_isr&0x6) { + switch (_isr&0xE) { case ISR_Rx: + case ISR_RxTO: { cyg_uint8 _lsr; unsigned char c; diff --git a/packages/ecos.db b/packages/ecos.db --- a/packages/ecos.db +++ b/packages/ecos.db @@ -25,7 +25,7 @@ # # The Initial Developer of the Original Code is Red Hat. # Portions created by Red Hat are -# Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +# Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. # All Rights Reserved. # ------------------------------------------- # @@ -448,6 +448,15 @@ package CYGPKG_DEVS_ETH_POWERPC_QUICC { description "Ethernet driver for PowerPC QUICC (MPC8xx) based boards." } +package CYGPKG_DEVS_ETH_INTEL_I82559 { + alias { "Intel 82559 ethernet driver" + devs_eth_intel_i82559 i82559_eth_driver } + hardware + directory devs/eth/intel/i82559 + script intel_i82559_eth_drivers.cdl + description "Ethernet driver for Intel 82559 NIC." +} + # Not sure whether this should be "hardware"; if so, it should be mentioned diff --git a/packages/hal/arm/arch/current/ChangeLog b/packages/hal/arm/arch/current/ChangeLog --- a/packages/hal/arm/arch/current/ChangeLog +++ b/packages/hal/arm/arch/current/ChangeLog @@ -1,3 +1,8 @@ +2001-01-24 Jonathan Larmour + + * src/vectors.S (init_flag): Add explicit alignment, just in case. + From Ilko Iliev + 2001-01-11 Hugo Tyson * src/vectors.S (hal_dram_size): Create hal_dram_type in memory at diff --git a/packages/hal/arm/arch/current/src/vectors.S b/packages/hal/arm/arch/current/src/vectors.S --- a/packages/hal/arm/arch/current/src/vectors.S +++ b/packages/hal/arm/arch/current/src/vectors.S @@ -976,6 +976,7 @@ PTR(__interrupt_stack) .data init_flag: + .balign 4 .long 0 .extern hal_default_isr diff --git a/packages/hal/mips/arch/current/ChangeLog b/packages/hal/mips/arch/current/ChangeLog --- a/packages/hal/mips/arch/current/ChangeLog +++ b/packages/hal/mips/arch/current/ChangeLog @@ -1,3 +1,8 @@ +2001-01-25 Jesper Skov + + * include/arch.inc: Allow hal_intc_decode to be defined by variant + or platform. + 2000-12-06 Jesper Skov * include/hal_intr.h (HAL_DELAY_US): Added. diff --git a/packages/hal/mips/arch/current/include/arch.inc b/packages/hal/mips/arch/current/include/arch.inc --- a/packages/hal/mips/arch/current/include/arch.inc +++ b/packages/hal/mips/arch/current/include/arch.inc @@ -293,6 +293,7 @@ .endm #endif +#ifndef CYGPKG_HAL_MIPS_INTC_DECODE_DEFINED .macro hal_intc_decode vnum mfc0 v0,cause # get cause register nop # delay slot @@ -302,6 +303,7 @@ add v0,v0,v1 # index into table lb \vnum,0(v0) # pick up vector number .endm +#endif #ifndef CYGPKG_HAL_MIPS_INTC_TRANSLATE_DEFINED #ifdef CYGIMP_HAL_COMMON_INTERRUPTS_CHAIN diff --git a/packages/hal/powerpc/arch/current/ChangeLog b/packages/hal/powerpc/arch/current/ChangeLog --- a/packages/hal/powerpc/arch/current/ChangeLog +++ b/packages/hal/powerpc/arch/current/ChangeLog @@ -1,3 +1,7 @@ +2001-01-24 Jesper Skov + + * src/powerpc.ld: Added .sdata to .sdata pattern. + 2001-01-16 Gary Thomas * src/vectors.S (_start): Set up IRQ environment _before_ calling diff --git a/packages/hal/powerpc/arch/current/src/powerpc.ld b/packages/hal/powerpc/arch/current/src/powerpc.ld --- a/packages/hal/powerpc/arch/current/src/powerpc.ld +++ b/packages/hal/powerpc/arch/current/src/powerpc.ld @@ -1,4 +1,4 @@ -/*========================================================================== +//========================================================================== // // powerpc.ld // @@ -23,7 +23,7 @@ // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are -// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // @@ -38,7 +38,7 @@ // //####DESCRIPTIONEND#### // -//========================================================================*/ +//========================================================================== STARTUP(vectors.o) ENTRY(__exception_reset) #ifdef EXTRAS @@ -111,7 +111,7 @@ GROUP(libtarget.a libgcc.a) /* We want the small data sections together, so single-instruction */ \ /* offsets can access them all, and initialized data all before */ \ /* uninitialized, so we can shorten the on-disk segment size. */ \ - __SDATA_START__ = ABSOLUTE(.); *(.sdata.*) \ + __SDATA_START__ = ABSOLUTE(.); *(.sdata) *(.sdata.*) \ __SDATA2_START__ = ABSOLUTE(.); *(.sdata2*) } \ > _region_ \ __rom_data_start = LOADADDR(.data); \ diff --git a/packages/hal/powerpc/mbx/current/ChangeLog b/packages/hal/powerpc/mbx/current/ChangeLog --- a/packages/hal/powerpc/mbx/current/ChangeLog +++ b/packages/hal/powerpc/mbx/current/ChangeLog @@ -1,3 +1,7 @@ +2001-01-19 Gary Thomas + + * include/hal_diag.h: Remove unwarranted disclaimer [commentary]. + 2001-01-03 Gary Thomas * cdl/hal_powerpc_mbx.cdl: Let RTC [heartbeat] rate be user diff --git a/packages/hal/powerpc/mbx/current/include/hal_diag.h b/packages/hal/powerpc/mbx/current/include/hal_diag.h --- a/packages/hal/powerpc/mbx/current/include/hal_diag.h +++ b/packages/hal/powerpc/mbx/current/include/hal_diag.h @@ -8,26 +8,6 @@ // HAL Support for Kernel Diagnostic Routines // //============================================================================= -//####UNSUPPORTEDBEGIN#### -// -// ------------------------------------------- -// This source file has been contributed to eCos/Red Hat. It may have been -// changed slightly to provide an interface consistent with those of other -// files. -// -// The functionality and contents of this file is supplied "AS IS" -// without any form of support and will not necessarily be kept up -// to date by Red Hat. -// -// All inquiries about this file, or the functionality provided by it, -// should be directed to the 'ecos-discuss' mailing list (see -// http://sourceware.cygnus.com/ecos/intouch.html for details). -// -// Maintained by: -// ------------------------------------------- -// -//####UNSUPPORTEDEND#### -//============================================================================= //####COPYRIGHTBEGIN#### // // ------------------------------------------- @@ -46,7 +26,7 @@ // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are -// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // diff --git a/packages/hal/sh/cq7708/current/ChangeLog b/packages/hal/sh/cq7708/current/ChangeLog --- a/packages/hal/sh/cq7708/current/ChangeLog +++ b/packages/hal/sh/cq7708/current/ChangeLog @@ -1,3 +1,7 @@ +2001-01-23 Jesper Skov + + * include/platform.inc: Properly init interrupt VSR. + 2000-11-22 Jesper Skov * include/platform.inc: Mangle symbols. diff --git a/packages/hal/sh/cq7708/current/include/platform.inc b/packages/hal/sh/cq7708/current/include/platform.inc --- a/packages/hal/sh/cq7708/current/include/platform.inc +++ b/packages/hal/sh/cq7708/current/include/platform.inc @@ -148,6 +148,7 @@ 2: # Write interrupt vector mov.l $hal_vsr_table,r3 mov.l $cyg_hal_default_interrupt_vsr,r4 + add #CYGNUM_HAL_VECTOR_INTERRUPT*4,r3 mov.l r4,@r3 bra 2f nop diff --git a/packages/hal/sh/cq7750/current/ChangeLog b/packages/hal/sh/cq7750/current/ChangeLog --- a/packages/hal/sh/cq7750/current/ChangeLog +++ b/packages/hal/sh/cq7750/current/ChangeLog @@ -1,3 +1,7 @@ +2001-01-23 Jesper Skov + + * include/platform.inc: Properly init interrupt VSR. + 2000-11-22 Jesper Skov * include/platform.inc: Mangle symbols. diff --git a/packages/hal/sh/cq7750/current/include/platform.inc b/packages/hal/sh/cq7750/current/include/platform.inc --- a/packages/hal/sh/cq7750/current/include/platform.inc +++ b/packages/hal/sh/cq7750/current/include/platform.inc @@ -197,6 +197,7 @@ 2: # Write interrupt vector mov.l $hal_vsr_table,r3 mov.l $cyg_hal_default_interrupt_vsr,r4 + add #CYGNUM_HAL_VECTOR_INTERRUPT*4,r3 mov.l r4,@r3 bra 2f nop diff --git a/packages/hal/sh/edk7708/current/ChangeLog b/packages/hal/sh/edk7708/current/ChangeLog --- a/packages/hal/sh/edk7708/current/ChangeLog +++ b/packages/hal/sh/edk7708/current/ChangeLog @@ -1,3 +1,7 @@ +2001-01-23 Jesper Skov + + * include/platform.inc: Properly init interrupt VSR. + 2000-11-22 Jesper Skov * include/platform.inc: Mangle symbols. diff --git a/packages/hal/sh/edk7708/current/include/platform.inc b/packages/hal/sh/edk7708/current/include/platform.inc --- a/packages/hal/sh/edk7708/current/include/platform.inc +++ b/packages/hal/sh/edk7708/current/include/platform.inc @@ -160,6 +160,7 @@ 2: # Write interrupt vector mov.l $hal_vsr_table,r3 mov.l $cyg_hal_default_interrupt_vsr,r4 + add #CYGNUM_HAL_VECTOR_INTERRUPT*4,r3 mov.l r4,@r3 bra 2f nop diff --git a/packages/kernel/current/ChangeLog b/packages/kernel/current/ChangeLog --- a/packages/kernel/current/ChangeLog +++ b/packages/kernel/current/ChangeLog @@ -1,3 +1,7 @@ +2001-01-24 Jesper Skov + + * src/sched/mlqueue.cxx (highpri): Fix trace call. + 2001-01-09 Nick Garnett * include/mlqueue.hxx: diff --git a/packages/kernel/current/src/sched/mlqueue.cxx b/packages/kernel/current/src/sched/mlqueue.cxx --- a/packages/kernel/current/src/sched/mlqueue.cxx +++ b/packages/kernel/current/src/sched/mlqueue.cxx @@ -552,7 +552,7 @@ Cyg_Thread * Cyg_ThreadQueue_Implementation::highpri(void) { CYG_REPORT_FUNCTYPE("returning thread %08x"); - CYG_REPORT_RETVAL(queue); + CYG_REPORT_RETVAL(get_head()); return get_head(); } diff --git a/packages/pkgconf/fixhtml.tcl b/packages/pkgconf/fixhtml.tcl new file mode 100644 --- /dev/null +++ b/packages/pkgconf/fixhtml.tcl @@ -0,0 +1,103 @@ +#!/bin/sh +# the next line restarts using tclsh \ + exec tclsh "$0" ${1+"$@"} + +#=============================================================================== +# +# fixhtml.tcl +# +# Patch HTML files generated from DocBook sources. +# +#=============================================================================== +#####COPYRIGHTBEGIN#### +# +# =============================================================== +# Copyright (C) 2000, 2001 Red Hat, Inc. +# +# This file is part of the eCos host tools. +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License as +# published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public +# License along with this program; if not, write to the Free +# Software Foundation, Inc., 59 Temple Place - Suite 330, +# Boston, MA 02111-1307, USA. +# +# =============================================================== +# +#####COPYRIGHTEND#### +#=============================================================================== +######DESCRIPTIONBEGIN#### +# +# Author(s): bartv +# Contributors: bartv +# Date: 2000-03-14 +# Purpose: HTML files generated from DocBook sources using the nwalsh +# stylesheets have a number of problems. Most importantly, +# Netscape 4.x does not understand all of the character entities +# defined by HTML 4.0x +# +#####DESCRIPTIONEND#### +#=============================================================================== +# + +# Find out the current year for the copyright message. Ideally +# this would be a range extracted from the sources, but that is +# a little bit tricky. + +set year [clock format [clock seconds] -format "%Y"] + +# The generated files all have a .htm suffix rather than a .html +# suffix. For now this is preserved, to avoid having to change all the +# anchors. It might be better to rename all the files to .html in +# future, and perhaps also to change book1.htm to index.htm + +set files [glob *.html] +foreach file $files { + set status [catch { + + set fd [open $file "r"] + set data [read $fd] + close $fd + + # If there is already a (C) message on the first line, skip this file. + if {[regexp {[^\n]*Copyright (C) [0-9]* Red Hat.*} $data] == 0} { + + + # The DSSSL has the annoying habit of splitting tags over several lines. + # This should sort things out. + regsub -all "\n>" $data ">\n" data + + # Add a copyright banner + set data [concat "\n" $data] + + # Take care of some character entities that Netscape does not understand + regsub -all "&mgr;" $data "\\BC;" data + regsub -all "—" $data "\\—" data + regsub -all "…" $data "\\…" data + regsub -all "“" $data "\\“" data + regsub -all "”" $data "\\”" data + + # Now write the data back to the file. Do not bother to + # keep an old version lying around, the html files can be + # regenerated easily enough. + set fd [open $file "w"] + puts -nonewline $fd $data + close $fd + } + } result] + + if {0 != $status} { + puts "Error while processing file $file\n $result" + exit 1 + } +} + diff --git a/packages/pkgconf/rules.doc b/packages/pkgconf/rules.doc new file mode 100644 --- /dev/null +++ b/packages/pkgconf/rules.doc @@ -0,0 +1,108 @@ +#============================================================================= +# +# rules.doc +# +# Additional rules for processing documentation written in DocBook/SGML +# +#============================================================================= +#####COPYRIGHTBEGIN#### +# +# ------------------------------------------- +# The contents of this file are subject to the Red Hat eCos Public License +# Version 1.1 (the "License"); you may not use this file except in +# compliance with the License. You may obtain a copy of the License at +# http://www.redhat.com/ +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +# License for the specific language governing rights and limitations under +# the License. +# +# The Original Code is eCos - Embedded Configurable Operating System, +# released September 30, 1998. +# +# The Initial Developer of the Original Code is Red Hat. +# Portions created by Red Hat are +# Copyright (C) 2001 Red Hat, Inc. +# All Rights Reserved. +# ------------------------------------------- +# +#####COPYRIGHTEND#### +#============================================================================= +#####DESCRIPTIONBEGIN#### +# +# Author(s): bartv +# Date: 2001-01-11 +# Purpose: Rules for processing documentation +# Description: +# Each package's doc directory's makefile should define the +# following variables: +# TOPLEVEL - of the component repository +# MAIN_SGML - documentation entry point +# OTHER_SGML - any other .sgml files accessed from main +# PICTURES - referenced by the SGML files +#####DESCRIPTIONEND#### +#============================================================================= + +.PHONY: default check html pdf clean + +# Locations of the stylesheets and other SGML support files +CATALOG := /usr/lib/sgml/CATALOG +MAIN_STYLESHEETS := /usr/lib/sgml/stylesheets/nwalsh-modular +ECOS_STYLESHEET := $(TOPLEVEL)/pkgconf/stylesheet.dsl +FIXHTML := $(TOPLEVEL)/pkgconf/fixhtml.tcl + +# The files that will be generated: +ifeq (,$(MAIN_HTML)) + MAIN_HTML := $(subst .sgml,.html,$(MAIN_SGML)) +endif +ifeq (,$(MAIN_PDF)) + MAIN_PDF := $(subst .sgml,.pdf,$(MAIN_SGML)) +endif + +# Rules for generating pictures +GIFS := $(foreach x,$(PICTURES),$(x).gif) +EPS := $(foreach x,$(PICTURES),$(x).eps) + +%.gif: %.fig + convert -crop 0x0 $< $@ + +%.eps: %.fig + convert -crop 0x0 $< $@ + +default: check + +# Validating an sgml document can be achieved with +check: $(MAIN_SGML) $(OTHER_SGML) $(GIFS) $(EPS) + nsgmls -vs -c $(CATALOG) $< + +# Generating HTML from the SGML. In practice multiple .html files may +# be generated, but for the purposes of dependency analysis the others +# can be ignored. +html: $(MAIN_HTML) + +$(MAIN_HTML): $(MAIN_SGML) $(OTHER_SGML) $(GIFS) $(ECOS_STYLESHEET) $(FIXHTML) + jade -t sgml -i html -d $(ECOS_STYLESHEET)#html $< + tclsh $(FIXHTML) + +# PDF files can be generated in a similar fashion. +pdf: $(MAIN_PDF) + +$(MAIN_PDF): $(MAIN_SGML) $(OTHER_SGML) $(EPS) $(ECOS_STYLESHEET) + jade -o $(subst .sgml,.tex,$(MAIN_SGML)) -t tex -V tex-backend -d $(ECOS_STYLESHEET)#print $< + pdfjadetex $(subst .sgml,.tex,$(MAIN_SGML)) + pdfjadetex $(subst .sgml,.tex,$(MAIN_SGML)) + pdfjadetex $(subst .sgml,.tex,$(MAIN_SGML)) + +#$(MAIN_PDF): $(MAIN_SGML) $(OTHER_SGML) $(EPS) $(ECOS_STYLESHEET) +# jade -o $(subst .sgml,.tex,$(MAIN_SGML)) -t tex -V tex-backend -d $(ECOS_STYLESHEET)#print $< +# jadetex $(subst .sgml,.tex,$(MAIN_SGML)) +# jadetex $(subst .sgml,.tex,$(MAIN_SGML)) +# jadetex $(subst .sgml,.tex,$(MAIN_SGML)) +# dvips -o $(subst .sgml,.ps,$(MAIN_SGML)) $(subst .sgml,.dvi,$(MAIN_SGML)) +# ps2pdf $(subst .sgml,.ps,$(MAIN_SGML)) $(subst .sgml,.pdf,$(MAIN_SGML)) + +# Clean. For now assume that all .html, .gif etc files are generated +clean: + rm -rf *.html *.tex *.dvi *.aux *.log *.ps *.pdf *.gif *.eps + diff --git a/packages/pkgconf/stylesheet.dsl b/packages/pkgconf/stylesheet.dsl new file mode 100644 --- /dev/null +++ b/packages/pkgconf/stylesheet.dsl @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +]]> + + +]]> +]> + + + + + + +;; ==================== +;; customize the print stylesheet +;; ==================== + +;; Set the paper parameters as per other eCos documentation +(define %page-width% 7.5in) +(define %page-height% 10.2in) +(define %left-margin% 0.75in) +(define %right-margin% 0.75in) + +;; Assume that we are only producing books, a reasonable assumption +;; given the primary author :-) +(define %two-side% #t) + +;; Use 12pt +;;(define %visual-acuity% "presbyopic") + +;; Do not use graphics in admonitions, our documentation is supposed +;; to look boring :-( +(define %admon-graphics% #f) + +;; Justified text please. +(define %default-quadding% 'justify) + +;; A separate page for each man page please. +(define %refentry-new-page% #t) + +;; The component writer's guide man pages do not describe functions +(define %refentry-functions% #f) + + + + + + + + +;; .html files please. +(define %html-ext% ".html") + +;; Boring admonitions +(define %admon-graphics% #f) + +(define %shade-verbatim% #t) + +;; Use ID attributes as name for component HTML files? +(define %use-id-as-filename% #t) + + + + + + + + diff --git a/packages/redboot/current/ChangeLog b/packages/redboot/current/ChangeLog --- a/packages/redboot/current/ChangeLog +++ b/packages/redboot/current/ChangeLog @@ -1,3 +1,27 @@ +2001-01-24 Jonathan Larmour + + * src/main.c (cyg_start): Rework last change to use more generic + HAL_MEM_REAL_REGION_TOP macro. + +2001-01-24 Hugo Tyson + + * src/main.c (cyg_start): Take notice of the hal_dram_size + variable (only supported on ARMs) to set ram_end. The symbols + given by the CYGMEM_REGION_ram symbols are minima or defaults. + +2001-01-22 Gary Thomas + + * src/net/inet_addr.c (inet_aton): Fix byte order on big endian systems. + + * src/net/icmp.c (__icmp_install_listener): + (__icmp_remove_listener): New functions. + (__icmp_handler): Call listener for unhandled packets. + + * include/net/net.h: Add handler for ICMP [incoming] data. + + * cdl/redboot.cdl: + * src/net/ping.c: New CLI command - ping a host. + 2001-01-18 Gary Thomas * include/net/net.h: diff --git a/packages/redboot/current/cdl/redboot.cdl b/packages/redboot/current/cdl/redboot.cdl --- a/packages/redboot/current/cdl/redboot.cdl +++ b/packages/redboot/current/cdl/redboot.cdl @@ -23,7 +23,7 @@ # # The Initial Developer of the Original Code is Red Hat. # Portions created by Red Hat are -# Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +# Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. # All Rights Reserved. # ------------------------------------------- # @@ -97,6 +97,7 @@ cdl_package CYGPKG_REDBOOT { compile net/bootp.c net/udp.c net/ip.c net/pktbuf.c net/cksum.c compile net/enet.c net/icmp.c net/tcp.c net/timers.c net/arp.c compile net/tftp_client.c net/net_io.c net/inet_addr.c + compile -library=libextras.a net/ping.c description "This option includes networking support in RedBoot." define_proc { puts $::cdl_system_header "#define CYGNUM_HAL_VIRTUAL_VECTOR_AUX_CHANNELS 1" diff --git a/packages/redboot/current/include/net/net.h b/packages/redboot/current/include/net/net.h --- a/packages/redboot/current/include/net/net.h +++ b/packages/redboot/current/include/net/net.h @@ -257,7 +257,6 @@ typedef struct { word seqnum; } icmp_header_t; - typedef struct _pktbuf { struct _pktbuf *next; union { @@ -283,6 +282,8 @@ typedef struct _pktbuf { /* protocol handler */ typedef void (*pkt_handler_t)(pktbuf_t *pkt); +/* ICMP fielder */ +typedef void (*icmp_handler_t)(pktbuf_t *pkt, ip_route_t *src_route); typedef struct _udp_socket { struct _udp_socket *next; @@ -448,6 +449,8 @@ extern void __ip_send(pktbuf_t *pkt, int * Handle incoming ICMP packets. */ extern void __icmp_handler(pktbuf_t *pkt, ip_route_t *r); +extern int __icmp_install_listener(icmp_handler_t handler); +extern void __icmp_remove_listener(void); /* * Handle incoming UDP packets. diff --git a/packages/redboot/current/src/main.c b/packages/redboot/current/src/main.c --- a/packages/redboot/current/src/main.c +++ b/packages/redboot/current/src/main.c @@ -143,6 +143,12 @@ cyg_start(void) ram_start = (unsigned char *)CYGMEM_REGION_ram; ram_end = (unsigned char *)(CYGMEM_REGION_ram+CYGMEM_REGION_ram_SIZE); +#ifdef HAL_MEM_REAL_REGION_TOP + { + unsigned char *ram_end_tmp = ram_end; + ram_end = HAL_MEM_REAL_REGION_TOP( ram_end_tmp ); + } +#endif bist(); diff --git a/packages/redboot/current/src/net/icmp.c b/packages/redboot/current/src/net/icmp.c --- a/packages/redboot/current/src/net/icmp.c +++ b/packages/redboot/current/src/net/icmp.c @@ -23,7 +23,7 @@ // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are -// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // @@ -48,6 +48,33 @@ /* * Handle ICMP packets. */ + +static icmp_handler_t icmp_handler; + +/* + * Install a handler for incoming icmp packets. + * Returns zero if successful, -1 if socket is already used. + */ +int +__icmp_install_listener(icmp_handler_t handler) +{ + if (icmp_handler) { + return -1; + } + icmp_handler = handler; + return 0; +} + + +/* + * Remove the handler + */ +void +__icmp_remove_listener(void) +{ + icmp_handler = (icmp_handler_t)NULL; +} + void __icmp_handler(pktbuf_t *pkt, ip_route_t *r) { @@ -65,6 +92,8 @@ void BSPLOG(bsp_log("icmp: seq<%d>\n", pkt->icmp_hdr->seqnum)); __ip_send(pkt, IP_PROTO_ICMP, r); + } else if (icmp_handler) { + (*icmp_handler)(pkt, r); } __pktbuf_free(pkt); } diff --git a/packages/redboot/current/src/net/inet_addr.c b/packages/redboot/current/src/net/inet_addr.c --- a/packages/redboot/current/src/net/inet_addr.c +++ b/packages/redboot/current/src/net/inet_addr.c @@ -23,7 +23,7 @@ // // The Initial Developer of the Original Code is Red Hat. // Portions created by Red Hat are -// Copyright (C) 1998, 1999, 2000 Red Hat, Inc. +// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. // All Rights Reserved. // ------------------------------------------- // @@ -76,7 +76,11 @@ inet_aton(char *s, in_addr_t *addr) } } // merge result +#ifdef __LITTLE_ENDIAN__ res |= val << ((3-i)*8); // 24, 16, 8, 0 +#else + res = (res << 8) | val; +#endif } addr->s_addr = htonl(res); return true; diff --git a/packages/redboot/current/src/net/ping.c b/packages/redboot/current/src/net/ping.c new file mode 100644 --- /dev/null +++ b/packages/redboot/current/src/net/ping.c @@ -0,0 +1,211 @@ +//========================================================================== +// +// ping.c +// +// Network utility - ping +// +//========================================================================== +//####COPYRIGHTBEGIN#### +// +// ------------------------------------------- +// The contents of this file are subject to the Red Hat eCos Public License +// Version 1.1 (the "License"); you may not use this file except in +// compliance with the License. You may obtain a copy of the License at +// http://www.redhat.com/ +// +// Software distributed under the License is distributed on an "AS IS" +// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the +// License for the specific language governing rights and limitations under +// the License. +// +// The Original Code is eCos - Embedded Configurable Operating System, +// released September 30, 1998. +// +// The Initial Developer of the Original Code is Red Hat. +// Portions created by Red Hat are +// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc. +// All Rights Reserved. +// ------------------------------------------- +// +//####COPYRIGHTEND#### +//========================================================================== +//#####DESCRIPTIONBEGIN#### +// +// Author(s): gthomas +// Contributors: gthomas +// Date: 2001-01-22 +// Purpose: +// Description: +// +// This code is part of RedBoot (tm). +// +//####DESCRIPTIONEND#### +// +//========================================================================== + +#include +#include + +#ifndef CYGPKG_REDBOOT_NETWORKING +#error CYGPKG_REDBOOT_NETWORKING required! +#else + +static void do_ping(int argc, char *argv[]); +RedBoot_cmd("ping", + "Network connectivity test", + "[-v] [-n ] [-t ] [-i ] -h ", + do_ping + ); + +static bool icmp_received; +static icmp_header_t hold_hdr; + +static void +handle_icmp(pktbuf_t *pkt, ip_route_t *src_route) +{ + icmp_header_t *icmp; + + icmp = pkt->icmp_hdr; + memcpy(&hold_hdr, icmp, sizeof(*icmp)); + icmp_received = true; +} + +static void +do_ping(int argc, char *argv[]) +{ + struct option_info opts[7]; + long count, timeout, length, rate, start_time, end_time, timer, received, tries; + char *local_ip_addr, *host_ip_addr; + bool local_ip_addr_set, host_ip_addr_set, count_set, + timeout_set, length_set, rate_set, verbose; + struct sockaddr_in local_addr, host_addr; + ip_addr_t hold_addr; + icmp_header_t *icmp; + pktbuf_t *pkt; + ip_header_t *ip; + unsigned short cksum; + ip_route_t dest_ip; + + init_opts(&opts[0], 'n', true, OPTION_ARG_TYPE_NUM, + (void **)&count, (bool *)&count_set, " - number of packets to test"); + init_opts(&opts[1], 't', true, OPTION_ARG_TYPE_NUM, + (void **)&timeout, (bool *)&timeout_set, " - max #ms per packet [rount trip]"); + init_opts(&opts[2], 'i', true, OPTION_ARG_TYPE_STR, + (void **)&local_ip_addr, (bool *)&local_ip_addr_set, "local IP address"); + init_opts(&opts[3], 'h', true, OPTION_ARG_TYPE_STR, + (void **)&host_ip_addr, (bool *)&host_ip_addr_set, "host IP address"); + init_opts(&opts[4], 'l', true, OPTION_ARG_TYPE_NUM, + (void **)&length, (bool *)&length_set, " - size of payload"); + init_opts(&opts[5], 'v', false, OPTION_ARG_TYPE_FLG, + (void **)&verbose, (bool *)0, "verbose operation"); + init_opts(&opts[6], 'r', true, OPTION_ARG_TYPE_NUM, + (void **)&rate, (bool *)&rate_set, " - time between packets"); + if (!scan_opts(argc, argv, 1, opts, 7, (void **)0, 0, "")) { + printf("PING - Invalid option specified\n"); + return; + } + // Set defaults; this has to be done _after_ the scan, since it will + // have destroyed all values not explicitly set. + if (local_ip_addr_set) { + if (!inet_aton(local_ip_addr, (in_addr_t *)&local_addr)) { + printf("PING - Invalid IP address: %s\n", local_ip_addr); + return; + } + } else { + memcpy((in_addr_t *)&local_addr, __local_ip_addr, sizeof(__local_ip_addr)); + } + if (host_ip_addr_set) { + if (!inet_aton(host_ip_addr, (in_addr_t *)&host_addr)) { + printf("PING - Invalid IP address: %s\n", host_ip_addr); + return; + } + if (__arp_lookup((ip_addr_t *)&host_addr.sin_addr, &dest_ip) < 0) { + printf("PING: Can't find address of server '%s'\n", host_ip_addr); + return; + } + } else { + printf("PING - host IP address required\n"); + return; + } +#define DEFAULT_LENGTH 64 +#define DEFAULT_COUNT 10 +#define DEFAULT_TIMEOUT 1000 +#define DEFAULT_RATE 1000 + if (!rate_set) { + rate = DEFAULT_RATE; + } + if (!length_set) { + length = DEFAULT_LENGTH; + } + if (!count_set) { + count = DEFAULT_COUNT; + } + if (!timeout_set) { + timeout = DEFAULT_TIMEOUT; + } + // Note: two prints here because 'inet_ntoa' returns a static pointer + printf("Network PING - from %s", + inet_ntoa((in_addr_t *)&local_addr)); + printf(" to %s\n", + inet_ntoa((in_addr_t *)&host_addr)); + received = 0; + __icmp_install_listener(handle_icmp); + for (tries = 0; tries < count; tries++) { + // The network stack uses the global variable '__local_ip_addr' + memcpy(hold_addr, __local_ip_addr, sizeof(hold_addr)); + memcpy(__local_ip_addr, &local_addr, sizeof(__local_ip_addr)); + // Build 'ping' request + if ((pkt = __pktbuf_alloc(ETH_MAX_PKTLEN)) == NULL) { + // Give up if no packets - something is wrong + break; + } + + icmp = pkt->icmp_hdr; + ip = pkt->ip_hdr; + pkt->pkt_bytes = length + sizeof(icmp_header_t); + + icmp->type = ICMP_TYPE_ECHOREQUEST; + icmp->code = 0; + icmp->checksum = 0; + icmp->seqnum = htons(tries+1); + cksum = __sum((word *)icmp, pkt->pkt_bytes, 0); + icmp->checksum = htons(cksum); + + memcpy(ip->source, (in_addr_t *)&local_addr, sizeof(ip_addr_t)); + memcpy(ip->destination, (in_addr_t *)&host_addr, sizeof(ip_addr_t)); + ip->protocol = IP_PROTO_ICMP; + ip->length = htons(pkt->pkt_bytes); + + __ip_send(pkt, IP_PROTO_ICMP, &dest_ip); + __pktbuf_free(pkt); + + start_time = MS_TICKS(); + timer = start_time + timeout; + icmp_received = false; + while (!icmp_received && (MS_TICKS() < timer)) { + __enet_poll(); + } + end_time = MS_TICKS(); + + timer = MS_TICKS() + rate; + while (MS_TICKS() < timer) { + __enet_poll(); + } + + // Clean up + memcpy(__local_ip_addr, &hold_addr, sizeof(__local_ip_addr)); + + if (icmp_received) { + received++; + if (verbose) { + printf("icmp - seq: %d, time: %d (ticks)\n", + ntohs(hold_hdr.seqnum), end_time-start_time); + } + } + } + __icmp_remove_listener(); + // Report + printf("PING - received %ld of %ld expected\n", received, count); +} + +#endif //CYGPKG_REDBOOT_NETWORKING