2005-01-07 02:29:27 +00:00
|
|
|
/*-
|
1999-09-20 00:24:11 +00:00
|
|
|
* Copyright (c) 1997, 1998, 1999
|
1998-08-16 17:14:59 +00:00
|
|
|
* Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. All advertising materials mentioning features or use of this software
|
|
|
|
* must display the following acknowledgement:
|
|
|
|
* This product includes software developed by Bill Paul.
|
|
|
|
* 4. Neither the name of the author nor the names of any co-contributors
|
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
|
|
|
|
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
|
|
|
* THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2003-11-14 17:16:58 +00:00
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* 3Com 3c90x Etherlink XL PCI NIC driver
|
|
|
|
*
|
1999-04-30 16:15:43 +00:00
|
|
|
* Supports the 3Com "boomerang", "cyclone" and "hurricane" PCI
|
1998-08-16 17:14:59 +00:00
|
|
|
* bus-master chips (3c90x cards and embedded controllers) including
|
|
|
|
* the following:
|
|
|
|
*
|
|
|
|
* 3Com 3c900-TPO 10Mbps/RJ-45
|
|
|
|
* 3Com 3c900-COMBO 10Mbps/RJ-45,AUI,BNC
|
|
|
|
* 3Com 3c905-TX 10/100Mbps/RJ-45
|
|
|
|
* 3Com 3c905-T4 10/100Mbps/RJ-45
|
1998-09-04 16:22:15 +00:00
|
|
|
* 3Com 3c900B-TPO 10Mbps/RJ-45
|
|
|
|
* 3Com 3c900B-COMBO 10Mbps/RJ-45,AUI,BNC
|
1999-04-15 03:18:33 +00:00
|
|
|
* 3Com 3c900B-TPC 10Mbps/RJ-45,BNC
|
1999-04-30 16:15:43 +00:00
|
|
|
* 3Com 3c900B-FL 10Mbps/Fiber-optic
|
1999-04-11 17:44:44 +00:00
|
|
|
* 3Com 3c905B-COMBO 10/100Mbps/RJ-45,AUI,BNC
|
1998-08-16 17:14:59 +00:00
|
|
|
* 3Com 3c905B-TX 10/100Mbps/RJ-45
|
|
|
|
* 3Com 3c905B-FL/FX 10/100Mbps/Fiber-optic
|
1999-09-15 07:19:34 +00:00
|
|
|
* 3Com 3c905C-TX 10/100Mbps/RJ-45 (Tornado ASIC)
|
|
|
|
* 3Com 3c980-TX 10/100Mbps server adapter (Hurricane ASIC)
|
|
|
|
* 3Com 3c980C-TX 10/100Mbps server adapter (Tornado ASIC)
|
|
|
|
* 3Com 3cSOHO100-TX 10/100Mbps/RJ-45 (Hurricane ASIC)
|
1999-12-16 18:33:57 +00:00
|
|
|
* 3Com 3c450-TX 10/100Mbps/RJ-45 (Tornado ASIC)
|
2003-01-06 00:46:03 +00:00
|
|
|
* 3Com 3c555 10/100Mbps/RJ-45 (MiniPCI, Laptop Hurricane)
|
2000-08-28 20:40:03 +00:00
|
|
|
* 3Com 3c556 10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC)
|
|
|
|
* 3Com 3c556B 10/100Mbps/RJ-45 (MiniPCI, Hurricane ASIC)
|
2000-11-02 06:49:38 +00:00
|
|
|
* 3Com 3c575TX 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
|
2000-10-16 23:16:02 +00:00
|
|
|
* 3Com 3c575B 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
|
|
|
|
* 3Com 3c575C 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
|
2001-08-28 00:40:18 +00:00
|
|
|
* 3Com 3cxfem656 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
|
|
|
|
* 3Com 3cxfem656b 10/100Mbps/RJ-45 (Cardbus, Hurricane ASIC)
|
|
|
|
* 3Com 3cxfem656c 10/100Mbps/RJ-45 (Cardbus, Tornado ASIC)
|
1998-09-04 16:22:15 +00:00
|
|
|
* Dell Optiplex GX1 on-board 3c918 10/100Mbps/RJ-45
|
1999-09-15 07:19:34 +00:00
|
|
|
* Dell on-board 3c920 10/100Mbps/RJ-45
|
1998-08-16 17:14:59 +00:00
|
|
|
* Dell Precision on-board 3c905B 10/100Mbps/RJ-45
|
|
|
|
* Dell Latitude laptop docking station embedded 3c905-TX
|
|
|
|
*
|
|
|
|
* Written by Bill Paul <wpaul@ctr.columbia.edu>
|
|
|
|
* Electrical Engineering Department
|
|
|
|
* Columbia University, New York City
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
* The 3c90x series chips use a bus-master DMA interface for transfering
|
|
|
|
* packets to and from the controller chip. Some of the "vortex" cards
|
|
|
|
* (3c59x) also supported a bus master mode, however for those chips
|
|
|
|
* you could only DMA packets to/from a contiguous memory buffer. For
|
|
|
|
* transmission this would mean copying the contents of the queued mbuf
|
2003-01-01 18:49:04 +00:00
|
|
|
* chain into an mbuf cluster and then DMAing the cluster. This extra
|
1998-08-16 17:14:59 +00:00
|
|
|
* copy would sort of defeat the purpose of the bus master support for
|
|
|
|
* any packet that doesn't fit into a single mbuf.
|
|
|
|
*
|
|
|
|
* By contrast, the 3c90x cards support a fragment-based bus master
|
|
|
|
* mode where mbuf chains can be encapsulated using TX descriptors.
|
|
|
|
* This is similar to other PCI chips such as the Texas Instruments
|
|
|
|
* ThunderLAN and the Intel 82557/82558.
|
|
|
|
*
|
|
|
|
* The "vortex" driver (if_vx.c) happens to work for the "boomerang"
|
|
|
|
* bus master chips because they maintain the old PIO interface for
|
|
|
|
* backwards compatibility, but starting with the 3c905B and the
|
|
|
|
* "cyclone" chips, the compatibility interface has been dropped.
|
|
|
|
* Since using bus master DMA is a big win, we use this driver to
|
|
|
|
* support the PCI "boomerang" chips even though they work with the
|
|
|
|
* "vortex" driver in order to obtain better performance.
|
|
|
|
*/
|
|
|
|
|
2005-10-05 10:09:17 +00:00
|
|
|
#ifdef HAVE_KERNEL_OPTION_HEADERS
|
|
|
|
#include "opt_device_polling.h"
|
|
|
|
#endif
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/sockio.h>
|
2003-01-19 00:23:59 +00:00
|
|
|
#include <sys/endian.h>
|
1998-08-16 17:14:59 +00:00
|
|
|
#include <sys/mbuf.h>
|
|
|
|
#include <sys/kernel.h>
|
2004-05-30 20:00:41 +00:00
|
|
|
#include <sys/module.h>
|
1998-08-16 17:14:59 +00:00
|
|
|
#include <sys/socket.h>
|
2005-05-20 07:16:09 +00:00
|
|
|
#include <sys/taskqueue.h>
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
#include <net/if.h>
|
|
|
|
#include <net/if_arp.h>
|
|
|
|
#include <net/ethernet.h>
|
|
|
|
#include <net/if_dl.h>
|
|
|
|
#include <net/if_media.h>
|
2005-06-10 16:49:24 +00:00
|
|
|
#include <net/if_types.h>
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
#include <net/bpf.h>
|
|
|
|
|
1999-03-27 20:41:25 +00:00
|
|
|
#include <machine/bus.h>
|
1999-07-20 21:23:17 +00:00
|
|
|
#include <machine/resource.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/rman.h>
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
#include <dev/mii/mii.h>
|
|
|
|
#include <dev/mii/miivar.h>
|
|
|
|
|
2003-08-22 07:20:27 +00:00
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2003-04-15 06:37:30 +00:00
|
|
|
MODULE_DEPEND(xl, pci, 1, 1, 1);
|
|
|
|
MODULE_DEPEND(xl, ether, 1, 1, 1);
|
2000-04-29 13:41:57 +00:00
|
|
|
MODULE_DEPEND(xl, miibus, 1, 1, 1);
|
|
|
|
|
2003-11-06 03:59:03 +00:00
|
|
|
/* "device miibus" required. See GENERIC if you get errors here. */
|
1999-08-29 15:52:19 +00:00
|
|
|
#include "miibus_if.h"
|
|
|
|
|
2008-08-10 09:45:52 +00:00
|
|
|
#include <dev/xl/if_xlreg.h>
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
/*
|
2004-03-19 23:20:23 +00:00
|
|
|
* TX Checksumming is disabled by default for two reasons:
|
|
|
|
* - TX Checksumming will occasionally produce corrupt packets
|
|
|
|
* - TX Checksumming seems to reduce performance
|
|
|
|
*
|
|
|
|
* Only 905B/C cards were reported to have this problem, it is possible
|
|
|
|
* that later chips _may_ be immune.
|
|
|
|
*/
|
|
|
|
#define XL905B_TXCSUM_BROKEN 1
|
|
|
|
|
|
|
|
#ifdef XL905B_TXCSUM_BROKEN
|
|
|
|
#define XL905B_CSUM_FEATURES 0
|
|
|
|
#else
|
2001-10-22 06:45:42 +00:00
|
|
|
#define XL905B_CSUM_FEATURES (CSUM_IP | CSUM_TCP | CSUM_UDP)
|
2004-03-19 23:20:23 +00:00
|
|
|
#endif
|
2001-10-22 06:45:42 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Various supported device vendors/types and their names.
|
|
|
|
*/
|
2008-03-24 17:49:06 +00:00
|
|
|
static const struct xl_type xl_devs[] = {
|
1998-08-16 17:14:59 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c900-TPO Etherlink XL" },
|
1998-08-16 17:14:59 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10BT_COMBO,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c900-COMBO Etherlink XL" },
|
1998-08-16 17:14:59 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_BOOMERANG_10_100BT,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c905-TX Fast Etherlink XL" },
|
1998-08-16 17:14:59 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_BOOMERANG_100BT4,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c905-T4 Fast Etherlink XL" },
|
1999-04-30 16:15:43 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c900B-TPO Etherlink XL" },
|
1999-04-30 16:15:43 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_COMBO,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c900B-COMBO Etherlink XL" },
|
1999-04-30 16:15:43 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_KRAKATOA_10BT_TPC,
|
1999-04-15 03:18:33 +00:00
|
|
|
"3Com 3c900B-TPC Etherlink XL" },
|
|
|
|
{ TC_VENDORID, TC_DEVICEID_CYCLONE_10FL,
|
|
|
|
"3Com 3c900B-FL Etherlink XL" },
|
1999-04-30 16:15:43 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c905B-TX Fast Etherlink XL" },
|
1998-08-16 17:14:59 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100BT4,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c905B-T4 Fast Etherlink XL" },
|
1998-10-09 03:59:24 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100FX,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c905B-FX/SC Fast Etherlink XL" },
|
1999-04-11 17:44:44 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_CYCLONE_10_100_COMBO,
|
|
|
|
"3Com 3c905B-COMBO Fast Etherlink XL" },
|
1999-05-30 18:09:17 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT,
|
|
|
|
"3Com 3c905C-TX Fast Etherlink XL" },
|
2003-01-22 17:14:33 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B,
|
|
|
|
"3Com 3c920B-EMB Integrated Fast Etherlink XL" },
|
2005-01-03 19:22:37 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_920B_WNM,
|
|
|
|
"3Com 3c920B-EMB-WNM Integrated Fast Etherlink XL" },
|
1999-04-30 16:15:43 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_10_100BT_SERV,
|
1999-03-31 15:45:15 +00:00
|
|
|
"3Com 3c980 Fast Etherlink XL" },
|
1999-09-15 07:20:59 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_TORNADO_10_100BT_SERV,
|
1999-09-15 07:19:34 +00:00
|
|
|
"3Com 3c980C Fast Etherlink XL" },
|
1999-04-12 20:38:45 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_SOHO100TX,
|
|
|
|
"3Com 3cSOHO100-TX OfficeConnect" },
|
1999-12-16 18:33:57 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_TORNADO_HOMECONNECT,
|
|
|
|
"3Com 3c450-TX HomeConnect" },
|
2003-01-06 00:46:03 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_555,
|
|
|
|
"3Com 3c555 Fast Etherlink XL" },
|
2000-08-28 20:40:03 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_556,
|
|
|
|
"3Com 3c556 Fast Etherlink XL" },
|
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_556B,
|
|
|
|
"3Com 3c556B Fast Etherlink XL" },
|
2000-11-02 06:49:38 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_575A,
|
|
|
|
"3Com 3c575TX Fast Etherlink XL" },
|
2000-10-16 23:16:02 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_575B,
|
|
|
|
"3Com 3c575B Fast Etherlink XL" },
|
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_575C,
|
|
|
|
"3Com 3c575C Fast Etherlink XL" },
|
2001-08-28 00:40:18 +00:00
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_656,
|
|
|
|
"3Com 3c656 Fast Etherlink XL" },
|
|
|
|
{ TC_VENDORID, TC_DEVICEID_HURRICANE_656B,
|
|
|
|
"3Com 3c656B Fast Etherlink XL" },
|
|
|
|
{ TC_VENDORID, TC_DEVICEID_TORNADO_656C,
|
2000-12-01 19:41:14 +00:00
|
|
|
"3Com 3c656C Fast Etherlink XL" },
|
1998-08-16 17:14:59 +00:00
|
|
|
{ 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
2005-02-24 22:33:05 +00:00
|
|
|
static int xl_probe(device_t);
|
|
|
|
static int xl_attach(device_t);
|
|
|
|
static int xl_detach(device_t);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-02-24 22:33:05 +00:00
|
|
|
static int xl_newbuf(struct xl_softc *, struct xl_chain_onefrag *);
|
|
|
|
static void xl_stats_update(void *);
|
|
|
|
static void xl_stats_update_locked(struct xl_softc *);
|
2008-03-24 17:49:06 +00:00
|
|
|
static int xl_encap(struct xl_softc *, struct xl_chain *, struct mbuf **);
|
2009-05-30 15:14:44 +00:00
|
|
|
static int xl_rxeof(struct xl_softc *);
|
2005-05-20 07:16:09 +00:00
|
|
|
static void xl_rxeof_task(void *, int);
|
2005-02-24 22:33:05 +00:00
|
|
|
static int xl_rx_resync(struct xl_softc *);
|
|
|
|
static void xl_txeof(struct xl_softc *);
|
|
|
|
static void xl_txeof_90xB(struct xl_softc *);
|
|
|
|
static void xl_txeoc(struct xl_softc *);
|
|
|
|
static void xl_intr(void *);
|
|
|
|
static void xl_start(struct ifnet *);
|
|
|
|
static void xl_start_locked(struct ifnet *);
|
|
|
|
static void xl_start_90xB_locked(struct ifnet *);
|
|
|
|
static int xl_ioctl(struct ifnet *, u_long, caddr_t);
|
|
|
|
static void xl_init(void *);
|
|
|
|
static void xl_init_locked(struct xl_softc *);
|
|
|
|
static void xl_stop(struct xl_softc *);
|
2006-12-06 02:18:41 +00:00
|
|
|
static int xl_watchdog(struct xl_softc *);
|
2007-11-22 02:45:00 +00:00
|
|
|
static int xl_shutdown(device_t);
|
2005-02-24 22:33:05 +00:00
|
|
|
static int xl_suspend(device_t);
|
|
|
|
static int xl_resume(device_t);
|
2001-12-05 10:34:07 +00:00
|
|
|
|
2005-03-26 20:22:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
2009-05-30 15:14:44 +00:00
|
|
|
static int xl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count);
|
|
|
|
static int xl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count);
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
#endif
|
2005-03-26 20:22:58 +00:00
|
|
|
|
2005-02-24 22:33:05 +00:00
|
|
|
static int xl_ifmedia_upd(struct ifnet *);
|
|
|
|
static void xl_ifmedia_sts(struct ifnet *, struct ifmediareq *);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-02-24 22:33:05 +00:00
|
|
|
static int xl_eeprom_wait(struct xl_softc *);
|
|
|
|
static int xl_read_eeprom(struct xl_softc *, caddr_t, int, int, int);
|
|
|
|
static void xl_mii_sync(struct xl_softc *);
|
|
|
|
static void xl_mii_send(struct xl_softc *, u_int32_t, int);
|
|
|
|
static int xl_mii_readreg(struct xl_softc *, struct xl_mii_frame *);
|
|
|
|
static int xl_mii_writereg(struct xl_softc *, struct xl_mii_frame *);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-02-24 22:33:05 +00:00
|
|
|
static void xl_setcfg(struct xl_softc *);
|
|
|
|
static void xl_setmode(struct xl_softc *, int);
|
|
|
|
static void xl_setmulti(struct xl_softc *);
|
|
|
|
static void xl_setmulti_hash(struct xl_softc *);
|
|
|
|
static void xl_reset(struct xl_softc *);
|
|
|
|
static int xl_list_rx_init(struct xl_softc *);
|
|
|
|
static int xl_list_tx_init(struct xl_softc *);
|
|
|
|
static int xl_list_tx_init_90xB(struct xl_softc *);
|
|
|
|
static void xl_wait(struct xl_softc *);
|
|
|
|
static void xl_mediacheck(struct xl_softc *);
|
|
|
|
static void xl_choose_media(struct xl_softc *sc, int *media);
|
|
|
|
static void xl_choose_xcvr(struct xl_softc *, int);
|
|
|
|
static void xl_dma_map_addr(void *, bus_dma_segment_t *, int, int);
|
1998-08-16 17:14:59 +00:00
|
|
|
#ifdef notdef
|
2005-02-24 22:33:05 +00:00
|
|
|
static void xl_testpacket(struct xl_softc *);
|
1998-08-16 17:14:59 +00:00
|
|
|
#endif
|
|
|
|
|
2005-02-24 22:33:05 +00:00
|
|
|
static int xl_miibus_readreg(device_t, int, int);
|
|
|
|
static int xl_miibus_writereg(device_t, int, int, int);
|
|
|
|
static void xl_miibus_statchg(device_t);
|
|
|
|
static void xl_miibus_mediainit(device_t);
|
1999-08-29 15:52:19 +00:00
|
|
|
|
1999-07-20 21:23:17 +00:00
|
|
|
static device_method_t xl_methods[] = {
|
|
|
|
/* Device interface */
|
|
|
|
DEVMETHOD(device_probe, xl_probe),
|
|
|
|
DEVMETHOD(device_attach, xl_attach),
|
|
|
|
DEVMETHOD(device_detach, xl_detach),
|
|
|
|
DEVMETHOD(device_shutdown, xl_shutdown),
|
2001-12-05 10:34:07 +00:00
|
|
|
DEVMETHOD(device_suspend, xl_suspend),
|
|
|
|
DEVMETHOD(device_resume, xl_resume),
|
1999-08-29 15:52:19 +00:00
|
|
|
|
|
|
|
/* bus interface */
|
|
|
|
DEVMETHOD(bus_print_child, bus_generic_print_child),
|
|
|
|
DEVMETHOD(bus_driver_added, bus_generic_driver_added),
|
|
|
|
|
|
|
|
/* MII interface */
|
|
|
|
DEVMETHOD(miibus_readreg, xl_miibus_readreg),
|
|
|
|
DEVMETHOD(miibus_writereg, xl_miibus_writereg),
|
|
|
|
DEVMETHOD(miibus_statchg, xl_miibus_statchg),
|
|
|
|
DEVMETHOD(miibus_mediainit, xl_miibus_mediainit),
|
|
|
|
|
1999-07-20 21:23:17 +00:00
|
|
|
{ 0, 0 }
|
|
|
|
};
|
|
|
|
|
|
|
|
static driver_t xl_driver = {
|
1999-09-20 08:47:11 +00:00
|
|
|
"xl",
|
1999-07-20 21:23:17 +00:00
|
|
|
xl_methods,
|
|
|
|
sizeof(struct xl_softc)
|
|
|
|
};
|
|
|
|
|
|
|
|
static devclass_t xl_devclass;
|
|
|
|
|
2003-04-15 06:37:30 +00:00
|
|
|
DRIVER_MODULE(xl, pci, xl_driver, xl_devclass, 0, 0);
|
1999-09-20 19:06:45 +00:00
|
|
|
DRIVER_MODULE(miibus, xl, miibus_driver, miibus_devclass, 0, 0);
|
1999-07-20 21:23:17 +00:00
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_dma_map_addr(void *arg, bus_dma_segment_t *segs, int nseg, int error)
|
2002-12-17 00:08:49 +00:00
|
|
|
{
|
|
|
|
u_int32_t *paddr;
|
2004-07-04 22:20:52 +00:00
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
paddr = arg;
|
|
|
|
*paddr = segs->ds_addr;
|
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Murphy's law says that it's possible the chip can wedge and
|
|
|
|
* the 'command in progress' bit may never clear. Hence, we wait
|
|
|
|
* only a finite amount of time to avoid getting caught in an
|
|
|
|
* infinite loop. Normally this delay routine would be a macro,
|
1998-08-24 17:51:38 +00:00
|
|
|
* but it isn't called during normal operation so we can afford
|
1998-08-16 17:14:59 +00:00
|
|
|
* to make it a function.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_wait(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
for (i = 0; i < XL_TIMEOUT; i++) {
|
2004-07-04 23:31:28 +00:00
|
|
|
if ((CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY) == 0)
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
|
|
|
}
|
1999-03-31 15:45:15 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
if (i == XL_TIMEOUT)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "command never completed!\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* MII access routines are provided for adapters with external
|
|
|
|
* PHYs (3c905-TX, 3c905-T4, 3c905B-T4) and those with built-in
|
|
|
|
* autoneg logic that's faked up to look like a PHY (3c905B-TX).
|
|
|
|
* Note: if you don't perform the MDIO operations just right,
|
|
|
|
* it's possible to end up with code that works correctly with
|
|
|
|
* some chips/CPUs/processor speeds/bus speeds/etc but not
|
|
|
|
* with others.
|
|
|
|
*/
|
|
|
|
#define MII_SET(x) \
|
|
|
|
CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \
|
2002-10-16 09:14:59 +00:00
|
|
|
CSR_READ_2(sc, XL_W4_PHY_MGMT) | (x))
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
#define MII_CLR(x) \
|
|
|
|
CSR_WRITE_2(sc, XL_W4_PHY_MGMT, \
|
2002-10-16 09:14:59 +00:00
|
|
|
CSR_READ_2(sc, XL_W4_PHY_MGMT) & ~(x))
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Sync the PHYs by setting data bit and strobing the clock 32 times.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_mii_sync(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
register int i;
|
|
|
|
|
|
|
|
XL_SEL_WIN(4);
|
|
|
|
MII_SET(XL_MII_DIR|XL_MII_DATA);
|
|
|
|
|
|
|
|
for (i = 0; i < 32; i++) {
|
|
|
|
MII_SET(XL_MII_CLK);
|
2003-01-06 00:36:13 +00:00
|
|
|
MII_SET(XL_MII_DATA);
|
2003-09-29 02:14:04 +00:00
|
|
|
MII_SET(XL_MII_DATA);
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_CLR(XL_MII_CLK);
|
2003-01-06 00:36:13 +00:00
|
|
|
MII_SET(XL_MII_DATA);
|
2003-09-29 02:14:04 +00:00
|
|
|
MII_SET(XL_MII_DATA);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clock a series of bits through the MII.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_mii_send(struct xl_softc *sc, u_int32_t bits, int cnt)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
XL_SEL_WIN(4);
|
|
|
|
MII_CLR(XL_MII_CLK);
|
|
|
|
|
|
|
|
for (i = (0x1 << (cnt - 1)); i; i >>= 1) {
|
2004-07-04 22:20:52 +00:00
|
|
|
if (bits & i) {
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_SET(XL_MII_DATA);
|
2004-07-04 22:20:52 +00:00
|
|
|
} else {
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_CLR(XL_MII_DATA);
|
2004-07-04 22:20:52 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_CLR(XL_MII_CLK);
|
|
|
|
MII_SET(XL_MII_CLK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read an PHY register through the MII.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_mii_readreg(struct xl_softc *sc, struct xl_mii_frame *frame)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2000-10-13 17:54:19 +00:00
|
|
|
int i, ack;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
/* Set up frame for RX. */
|
1998-08-16 17:14:59 +00:00
|
|
|
frame->mii_stdelim = XL_MII_STARTDELIM;
|
|
|
|
frame->mii_opcode = XL_MII_READOP;
|
|
|
|
frame->mii_turnaround = 0;
|
|
|
|
frame->mii_data = 0;
|
2004-07-04 22:20:52 +00:00
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
/* Select register window 4. */
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(4);
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, XL_W4_PHY_MGMT, 0);
|
2004-07-04 23:31:28 +00:00
|
|
|
/* Turn on data xmit. */
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_SET(XL_MII_DIR);
|
|
|
|
|
|
|
|
xl_mii_sync(sc);
|
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
/* Send command/address info. */
|
1998-08-16 17:14:59 +00:00
|
|
|
xl_mii_send(sc, frame->mii_stdelim, 2);
|
|
|
|
xl_mii_send(sc, frame->mii_opcode, 2);
|
|
|
|
xl_mii_send(sc, frame->mii_phyaddr, 5);
|
|
|
|
xl_mii_send(sc, frame->mii_regaddr, 5);
|
|
|
|
|
|
|
|
/* Idle bit */
|
|
|
|
MII_CLR((XL_MII_CLK|XL_MII_DATA));
|
|
|
|
MII_SET(XL_MII_CLK);
|
|
|
|
|
|
|
|
/* Turn off xmit. */
|
|
|
|
MII_CLR(XL_MII_DIR);
|
|
|
|
|
|
|
|
/* Check for ack */
|
|
|
|
MII_CLR(XL_MII_CLK);
|
|
|
|
ack = CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA;
|
2003-01-10 08:09:58 +00:00
|
|
|
MII_SET(XL_MII_CLK);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now try reading data bits. If the ack failed, we still
|
|
|
|
* need to clock through 16 cycles to keep the PHY(s) in sync.
|
|
|
|
*/
|
|
|
|
if (ack) {
|
2004-07-04 22:20:52 +00:00
|
|
|
for (i = 0; i < 16; i++) {
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_CLR(XL_MII_CLK);
|
|
|
|
MII_SET(XL_MII_CLK);
|
|
|
|
}
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0x8000; i; i >>= 1) {
|
|
|
|
MII_CLR(XL_MII_CLK);
|
|
|
|
if (!ack) {
|
|
|
|
if (CSR_READ_2(sc, XL_W4_PHY_MGMT) & XL_MII_DATA)
|
|
|
|
frame->mii_data |= i;
|
|
|
|
}
|
|
|
|
MII_SET(XL_MII_CLK);
|
|
|
|
}
|
|
|
|
|
|
|
|
fail:
|
|
|
|
MII_CLR(XL_MII_CLK);
|
|
|
|
MII_SET(XL_MII_CLK);
|
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
return (ack ? 1 : 0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Write to a PHY register through the MII.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_mii_writereg(struct xl_softc *sc, struct xl_mii_frame *frame)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2004-07-04 23:31:28 +00:00
|
|
|
|
|
|
|
/* Set up frame for TX. */
|
1998-08-16 17:14:59 +00:00
|
|
|
frame->mii_stdelim = XL_MII_STARTDELIM;
|
|
|
|
frame->mii_opcode = XL_MII_WRITEOP;
|
|
|
|
frame->mii_turnaround = XL_MII_TURNAROUND;
|
2004-07-04 22:20:52 +00:00
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
/* Select the window 4. */
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(4);
|
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
/* Turn on data output. */
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_SET(XL_MII_DIR);
|
|
|
|
|
|
|
|
xl_mii_sync(sc);
|
|
|
|
|
|
|
|
xl_mii_send(sc, frame->mii_stdelim, 2);
|
|
|
|
xl_mii_send(sc, frame->mii_opcode, 2);
|
|
|
|
xl_mii_send(sc, frame->mii_phyaddr, 5);
|
|
|
|
xl_mii_send(sc, frame->mii_regaddr, 5);
|
|
|
|
xl_mii_send(sc, frame->mii_turnaround, 2);
|
|
|
|
xl_mii_send(sc, frame->mii_data, 16);
|
|
|
|
|
|
|
|
/* Idle bit. */
|
|
|
|
MII_SET(XL_MII_CLK);
|
|
|
|
MII_CLR(XL_MII_CLK);
|
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
/* Turn off xmit. */
|
1998-08-16 17:14:59 +00:00
|
|
|
MII_CLR(XL_MII_DIR);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_miibus_readreg(device_t dev, int phy, int reg)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
1999-08-29 15:52:19 +00:00
|
|
|
struct xl_softc *sc;
|
1998-08-16 17:14:59 +00:00
|
|
|
struct xl_mii_frame frame;
|
|
|
|
|
2000-08-28 20:40:03 +00:00
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
1999-09-01 03:16:21 +00:00
|
|
|
/*
|
|
|
|
* Pretend that PHYs are only available at MII address 24.
|
|
|
|
* This is to guard against problems with certain 3Com ASIC
|
|
|
|
* revisions that incorrectly map the internal transceiver
|
|
|
|
* control registers at all MII addresses. This can cause
|
|
|
|
* the miibus code to attach the same PHY several times over.
|
|
|
|
*/
|
2004-07-04 23:31:28 +00:00
|
|
|
if ((sc->xl_flags & XL_FLAG_PHYOK) == 0 && phy != 24)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1999-09-01 03:16:21 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
bzero((char *)&frame, sizeof(frame));
|
1999-08-29 15:52:19 +00:00
|
|
|
frame.mii_phyaddr = phy;
|
1998-08-16 17:14:59 +00:00
|
|
|
frame.mii_regaddr = reg;
|
2004-07-04 23:31:28 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
xl_mii_readreg(sc, &frame);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (frame.mii_data);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_miibus_writereg(device_t dev, int phy, int reg, int data)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
1999-08-29 15:52:19 +00:00
|
|
|
struct xl_softc *sc;
|
1998-08-16 17:14:59 +00:00
|
|
|
struct xl_mii_frame frame;
|
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
if ((sc->xl_flags & XL_FLAG_PHYOK) == 0 && phy != 24)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
2000-08-28 20:40:03 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
bzero((char *)&frame, sizeof(frame));
|
1999-08-29 15:52:19 +00:00
|
|
|
frame.mii_phyaddr = phy;
|
1998-08-16 17:14:59 +00:00
|
|
|
frame.mii_regaddr = reg;
|
|
|
|
frame.mii_data = data;
|
|
|
|
|
|
|
|
xl_mii_writereg(sc, &frame);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1999-08-29 15:52:19 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_miibus_statchg(device_t dev)
|
1999-08-29 15:52:19 +00:00
|
|
|
{
|
2004-07-04 22:20:52 +00:00
|
|
|
struct xl_softc *sc;
|
|
|
|
struct mii_data *mii;
|
1999-08-29 15:52:19 +00:00
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
mii = device_get_softc(sc->xl_miibus);
|
|
|
|
|
1999-10-14 21:49:17 +00:00
|
|
|
xl_setcfg(sc);
|
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
/* Set ASIC's duplex mode to match the PHY. */
|
|
|
|
XL_SEL_WIN(3);
|
|
|
|
if ((mii->mii_media_active & IFM_GMASK) == IFM_FDX)
|
2001-06-01 00:34:50 +00:00
|
|
|
CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX);
|
1999-08-29 15:52:19 +00:00
|
|
|
else
|
|
|
|
CSR_WRITE_1(sc, XL_W3_MAC_CTRL,
|
2004-07-04 23:31:28 +00:00
|
|
|
(CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX));
|
1999-08-29 15:52:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Special support for the 3c905B-COMBO. This card has 10/100 support
|
|
|
|
* plus BNC and AUI ports. This means we will have both an miibus attached
|
|
|
|
* plus some non-MII media settings. In order to allow this, we have to
|
|
|
|
* add the extra media to the miibus's ifmedia struct, but we can't do
|
|
|
|
* that during xl_attach() because the miibus hasn't been attached yet.
|
|
|
|
* So instead, we wait until the miibus probe/attach is done, at which
|
|
|
|
* point we will get a callback telling is that it's safe to add our
|
|
|
|
* extra media.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_miibus_mediainit(device_t dev)
|
1999-08-29 15:52:19 +00:00
|
|
|
{
|
2004-07-04 22:20:52 +00:00
|
|
|
struct xl_softc *sc;
|
|
|
|
struct mii_data *mii;
|
1999-08-29 15:52:19 +00:00
|
|
|
struct ifmedia *ifm;
|
2004-07-04 22:20:52 +00:00
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
sc = device_get_softc(dev);
|
|
|
|
mii = device_get_softc(sc->xl_miibus);
|
|
|
|
ifm = &mii->mii_media;
|
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
if (sc->xl_media & (XL_MEDIAOPT_AUI | XL_MEDIAOPT_10FL)) {
|
1999-08-29 15:52:19 +00:00
|
|
|
/*
|
|
|
|
* Check for a 10baseFL board in disguise.
|
|
|
|
*/
|
|
|
|
if (sc->xl_type == XL_TYPE_905B &&
|
|
|
|
sc->xl_media == XL_MEDIAOPT_10FL) {
|
|
|
|
if (bootverbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "found 10baseFL\n");
|
2004-07-04 23:31:28 +00:00
|
|
|
ifmedia_add(ifm, IFM_ETHER | IFM_10_FL, 0, NULL);
|
|
|
|
ifmedia_add(ifm, IFM_ETHER | IFM_10_FL|IFM_HDX, 0,
|
|
|
|
NULL);
|
1999-08-29 15:52:19 +00:00
|
|
|
if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
|
|
|
|
ifmedia_add(ifm,
|
2004-07-04 23:31:28 +00:00
|
|
|
IFM_ETHER | IFM_10_FL | IFM_FDX, 0, NULL);
|
1999-08-29 15:52:19 +00:00
|
|
|
} else {
|
|
|
|
if (bootverbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "found AUI\n");
|
2004-07-04 23:31:28 +00:00
|
|
|
ifmedia_add(ifm, IFM_ETHER | IFM_10_5, 0, NULL);
|
1999-08-29 15:52:19 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BNC) {
|
|
|
|
if (bootverbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "found BNC\n");
|
2004-07-04 23:31:28 +00:00
|
|
|
ifmedia_add(ifm, IFM_ETHER | IFM_10_2, 0, NULL);
|
1999-08-29 15:52:19 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The EEPROM is slow: give it time to come ready after issuing
|
|
|
|
* it a command.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_eeprom_wait(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < 100; i++) {
|
|
|
|
if (CSR_READ_2(sc, XL_W0_EE_CMD) & XL_EE_BUSY)
|
|
|
|
DELAY(162);
|
|
|
|
else
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == 100) {
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "eeprom failed to come ready\n");
|
2004-07-04 22:20:52 +00:00
|
|
|
return (1);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Read a sequence of words from the EEPROM. Note that ethernet address
|
|
|
|
* data is stored in the EEPROM in network byte order.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_read_eeprom(struct xl_softc *sc, caddr_t dest, int off, int cnt, int swap)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
int err = 0, i;
|
|
|
|
u_int16_t word = 0, *ptr;
|
2004-07-04 22:20:52 +00:00
|
|
|
|
2000-08-28 20:40:03 +00:00
|
|
|
#define EEPROM_5BIT_OFFSET(A) ((((A) << 2) & 0x7F00) | ((A) & 0x003F))
|
2000-10-16 23:16:02 +00:00
|
|
|
#define EEPROM_8BIT_OFFSET(A) ((A) & 0x003F)
|
2004-07-04 22:20:52 +00:00
|
|
|
/*
|
|
|
|
* XXX: WARNING! DANGER!
|
2000-08-28 20:40:03 +00:00
|
|
|
* It's easy to accidentally overwrite the rom content!
|
|
|
|
* Note: the 3c575 uses 8bit EEPROM offsets.
|
|
|
|
*/
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(0);
|
|
|
|
|
|
|
|
if (xl_eeprom_wait(sc))
|
2004-07-04 22:20:52 +00:00
|
|
|
return (1);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2000-08-28 20:40:03 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_EEPROM_OFFSET_30)
|
|
|
|
off += 0x30;
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
for (i = 0; i < cnt; i++) {
|
2000-08-28 20:40:03 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_8BITROM)
|
2004-07-04 22:20:52 +00:00
|
|
|
CSR_WRITE_2(sc, XL_W0_EE_CMD,
|
2000-10-16 23:16:02 +00:00
|
|
|
XL_EE_8BIT_READ | EEPROM_8BIT_OFFSET(off + i));
|
2000-08-28 20:40:03 +00:00
|
|
|
else
|
|
|
|
CSR_WRITE_2(sc, XL_W0_EE_CMD,
|
|
|
|
XL_EE_READ | EEPROM_5BIT_OFFSET(off + i));
|
1998-08-16 17:14:59 +00:00
|
|
|
err = xl_eeprom_wait(sc);
|
|
|
|
if (err)
|
|
|
|
break;
|
|
|
|
word = CSR_READ_2(sc, XL_W0_EE_DATA);
|
|
|
|
ptr = (u_int16_t *)(dest + (i * 2));
|
|
|
|
if (swap)
|
|
|
|
*ptr = ntohs(word);
|
|
|
|
else
|
2004-07-04 22:20:52 +00:00
|
|
|
*ptr = word;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (err ? 1 : 0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NICs older than the 3c905B have only one multicast option, which
|
|
|
|
* is to enable reception of all multicast frames.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_setmulti(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
struct ifmultiaddr *ifma;
|
|
|
|
u_int8_t rxfilt;
|
|
|
|
int mcnt = 0;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(5);
|
|
|
|
rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
|
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_ALLMULTI) {
|
|
|
|
rxfilt |= XL_RXFILTER_ALLMULTI;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-06-26 11:45:06 +00:00
|
|
|
if_maddr_rlock(ifp);
|
2001-02-06 10:12:15 +00:00
|
|
|
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link)
|
1998-08-16 17:14:59 +00:00
|
|
|
mcnt++;
|
2009-06-26 11:45:06 +00:00
|
|
|
if_maddr_runlock(ifp);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
if (mcnt)
|
|
|
|
rxfilt |= XL_RXFILTER_ALLMULTI;
|
|
|
|
else
|
|
|
|
rxfilt &= ~XL_RXFILTER_ALLMULTI;
|
|
|
|
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 3c905B adapters have a hash filter that we can program.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_setmulti_hash(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
int h = 0, i;
|
|
|
|
struct ifmultiaddr *ifma;
|
|
|
|
u_int8_t rxfilt;
|
|
|
|
int mcnt = 0;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(5);
|
|
|
|
rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
|
|
|
|
|
|
|
|
if (ifp->if_flags & IFF_ALLMULTI) {
|
|
|
|
rxfilt |= XL_RXFILTER_ALLMULTI;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
return;
|
|
|
|
} else
|
|
|
|
rxfilt &= ~XL_RXFILTER_ALLMULTI;
|
|
|
|
|
|
|
|
/* first, zot all the existing hash bits */
|
|
|
|
for (i = 0; i < XL_HASHFILT_SIZE; i++)
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_HASH|i);
|
|
|
|
|
|
|
|
/* now program new ones */
|
2009-06-26 11:45:06 +00:00
|
|
|
if_maddr_rlock(ifp);
|
2001-02-06 10:12:15 +00:00
|
|
|
TAILQ_FOREACH(ifma, &ifp->if_multiaddrs, ifma_link) {
|
1998-08-16 17:14:59 +00:00
|
|
|
if (ifma->ifma_addr->sa_family != AF_LINK)
|
|
|
|
continue;
|
2004-06-09 14:34:04 +00:00
|
|
|
/*
|
|
|
|
* Note: the 3c905B currently only supports a 64-bit hash
|
|
|
|
* table, which means we really only need 6 bits, but the
|
|
|
|
* manual indicates that future chip revisions will have a
|
|
|
|
* 256-bit hash table, hence the routine is set up to
|
|
|
|
* calculate 8 bits of position info in case we need it some
|
|
|
|
* day.
|
|
|
|
* Note II, The Sequel: _CURRENT_ versions of the 3c905B have
|
|
|
|
* a 256 bit hash table. This means we have to use all 8 bits
|
|
|
|
* regardless. On older cards, the upper 2 bits will be
|
|
|
|
* ignored. Grrrr....
|
2004-07-04 22:20:52 +00:00
|
|
|
*/
|
2004-06-09 14:34:04 +00:00
|
|
|
h = ether_crc32_be(LLADDR((struct sockaddr_dl *)
|
|
|
|
ifma->ifma_addr), ETHER_ADDR_LEN) & 0xFF;
|
2004-07-04 23:31:28 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
|
|
|
h | XL_CMD_RX_SET_HASH | XL_HASH_SET);
|
1998-08-16 17:14:59 +00:00
|
|
|
mcnt++;
|
|
|
|
}
|
2009-06-26 11:45:06 +00:00
|
|
|
if_maddr_runlock(ifp);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
if (mcnt)
|
|
|
|
rxfilt |= XL_RXFILTER_MULTIHASH;
|
|
|
|
else
|
|
|
|
rxfilt &= ~XL_RXFILTER_MULTIHASH;
|
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, rxfilt | XL_CMD_RX_SET_FILT);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_setcfg(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
1999-04-15 03:18:33 +00:00
|
|
|
u_int32_t icfg;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
/*XL_LOCK_ASSERT(sc);*/
|
|
|
|
|
1999-04-15 03:18:33 +00:00
|
|
|
XL_SEL_WIN(3);
|
|
|
|
icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
|
|
|
|
icfg &= ~XL_ICFG_CONNECTOR_MASK;
|
|
|
|
if (sc->xl_media & XL_MEDIAOPT_MII ||
|
1999-08-29 15:52:19 +00:00
|
|
|
sc->xl_media & XL_MEDIAOPT_BT4)
|
1999-04-15 03:18:33 +00:00
|
|
|
icfg |= (XL_XCVR_MII << XL_ICFG_CONNECTOR_BITS);
|
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BTX)
|
|
|
|
icfg |= (XL_XCVR_AUTO << XL_ICFG_CONNECTOR_BITS);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
|
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_setmode(struct xl_softc *sc, int media)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
u_int32_t icfg;
|
|
|
|
u_int16_t mediastat;
|
2004-07-05 00:15:23 +00:00
|
|
|
char *pmsg = "", *dmsg = "";
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(4);
|
|
|
|
mediastat = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
|
|
|
|
XL_SEL_WIN(3);
|
|
|
|
icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG);
|
|
|
|
|
1999-05-04 20:52:30 +00:00
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BT) {
|
1998-08-16 17:14:59 +00:00
|
|
|
if (IFM_SUBTYPE(media) == IFM_10_T) {
|
2004-07-05 00:15:23 +00:00
|
|
|
pmsg = "10baseT transceiver";
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_10BT;
|
|
|
|
icfg &= ~XL_ICFG_CONNECTOR_MASK;
|
|
|
|
icfg |= (XL_XCVR_10BT << XL_ICFG_CONNECTOR_BITS);
|
2004-07-04 23:31:28 +00:00
|
|
|
mediastat |= XL_MEDIASTAT_LINKBEAT |
|
|
|
|
XL_MEDIASTAT_JABGUARD;
|
1998-08-16 17:14:59 +00:00
|
|
|
mediastat &= ~XL_MEDIASTAT_SQEENB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BFX) {
|
|
|
|
if (IFM_SUBTYPE(media) == IFM_100_FX) {
|
2004-07-05 00:15:23 +00:00
|
|
|
pmsg = "100baseFX port";
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_100BFX;
|
|
|
|
icfg &= ~XL_ICFG_CONNECTOR_MASK;
|
|
|
|
icfg |= (XL_XCVR_100BFX << XL_ICFG_CONNECTOR_BITS);
|
|
|
|
mediastat |= XL_MEDIASTAT_LINKBEAT;
|
|
|
|
mediastat &= ~XL_MEDIASTAT_SQEENB;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
1999-05-04 20:52:30 +00:00
|
|
|
if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
|
1998-08-16 17:14:59 +00:00
|
|
|
if (IFM_SUBTYPE(media) == IFM_10_5) {
|
2004-07-05 00:15:23 +00:00
|
|
|
pmsg = "AUI port";
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_AUI;
|
|
|
|
icfg &= ~XL_ICFG_CONNECTOR_MASK;
|
|
|
|
icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
|
2004-07-04 22:20:52 +00:00
|
|
|
mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
|
|
|
|
XL_MEDIASTAT_JABGUARD);
|
1998-08-16 17:14:59 +00:00
|
|
|
mediastat |= ~XL_MEDIASTAT_SQEENB;
|
|
|
|
}
|
1999-05-04 20:52:30 +00:00
|
|
|
if (IFM_SUBTYPE(media) == IFM_10_FL) {
|
2004-07-05 00:15:23 +00:00
|
|
|
pmsg = "10baseFL transceiver";
|
1999-05-04 20:52:30 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_AUI;
|
|
|
|
icfg &= ~XL_ICFG_CONNECTOR_MASK;
|
|
|
|
icfg |= (XL_XCVR_AUI << XL_ICFG_CONNECTOR_BITS);
|
2004-07-04 22:20:52 +00:00
|
|
|
mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
|
|
|
|
XL_MEDIASTAT_JABGUARD);
|
1999-05-04 20:52:30 +00:00
|
|
|
mediastat |= ~XL_MEDIASTAT_SQEENB;
|
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BNC) {
|
|
|
|
if (IFM_SUBTYPE(media) == IFM_10_2) {
|
2004-07-05 00:15:23 +00:00
|
|
|
pmsg = "AUI port";
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_COAX;
|
|
|
|
icfg &= ~XL_ICFG_CONNECTOR_MASK;
|
|
|
|
icfg |= (XL_XCVR_COAX << XL_ICFG_CONNECTOR_BITS);
|
2004-07-04 22:20:52 +00:00
|
|
|
mediastat &= ~(XL_MEDIASTAT_LINKBEAT |
|
|
|
|
XL_MEDIASTAT_JABGUARD | XL_MEDIASTAT_SQEENB);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((media & IFM_GMASK) == IFM_FDX ||
|
|
|
|
IFM_SUBTYPE(media) == IFM_100_FX) {
|
2004-07-05 00:15:23 +00:00
|
|
|
dmsg = "full";
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(3);
|
2001-06-01 00:34:50 +00:00
|
|
|
CSR_WRITE_1(sc, XL_W3_MAC_CTRL, XL_MACCTRL_DUPLEX);
|
1998-08-16 17:14:59 +00:00
|
|
|
} else {
|
2004-07-05 00:15:23 +00:00
|
|
|
dmsg = "half";
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(3);
|
|
|
|
CSR_WRITE_1(sc, XL_W3_MAC_CTRL,
|
|
|
|
(CSR_READ_1(sc, XL_W3_MAC_CTRL) & ~XL_MACCTRL_DUPLEX));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (IFM_SUBTYPE(media) == IFM_10_2)
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
|
|
|
|
else
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
|
2004-07-04 22:20:52 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
CSR_WRITE_4(sc, XL_W3_INTERNAL_CFG, icfg);
|
|
|
|
XL_SEL_WIN(4);
|
|
|
|
CSR_WRITE_2(sc, XL_W4_MEDIA_STATUS, mediastat);
|
2004-07-05 00:15:23 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
DELAY(800);
|
|
|
|
XL_SEL_WIN(7);
|
2004-07-05 00:15:23 +00:00
|
|
|
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "selecting %s, %s duplex\n", pmsg, dmsg);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_reset(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
1999-04-11 17:44:44 +00:00
|
|
|
register int i;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(0);
|
2004-07-04 22:20:52 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RESET |
|
|
|
|
((sc->xl_flags & XL_FLAG_WEIRDRESET) ?
|
|
|
|
XL_RESETOPT_DISADVFD:0));
|
1999-04-11 17:44:44 +00:00
|
|
|
|
2003-06-29 01:38:57 +00:00
|
|
|
/*
|
|
|
|
* If we're using memory mapped register mode, pause briefly
|
|
|
|
* after issuing the reset command before trying to access any
|
|
|
|
* other registers. With my 3c575C cardbus card, failing to do
|
|
|
|
* this results in the system locking up while trying to poll
|
|
|
|
* the command busy bit in the status register.
|
|
|
|
*/
|
|
|
|
if (sc->xl_flags & XL_FLAG_USE_MMIO)
|
|
|
|
DELAY(100000);
|
|
|
|
|
1999-04-11 17:44:44 +00:00
|
|
|
for (i = 0; i < XL_TIMEOUT; i++) {
|
|
|
|
DELAY(10);
|
|
|
|
if (!(CSR_READ_2(sc, XL_STATUS) & XL_STAT_CMDBUSY))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == XL_TIMEOUT)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "reset didn't complete\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-08-02 21:06:16 +00:00
|
|
|
/* Reset TX and RX. */
|
2001-07-27 20:55:45 +00:00
|
|
|
/* Note: the RX reset takes an absurd amount of time
|
|
|
|
* on newer versions of the Tornado chips such as those
|
|
|
|
* on the 3c905CX and newer 3c908C cards. We wait an
|
|
|
|
* extra amount of time so that xl_wait() doesn't complain
|
|
|
|
* and annoy the users.
|
|
|
|
*/
|
1999-08-02 21:06:16 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
|
2001-07-27 20:55:45 +00:00
|
|
|
DELAY(100000);
|
1999-08-02 21:06:16 +00:00
|
|
|
xl_wait(sc);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
|
|
|
|
xl_wait(sc);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_INVERT_LED_PWR ||
|
2000-10-16 23:16:02 +00:00
|
|
|
sc->xl_flags & XL_FLAG_INVERT_MII_PWR) {
|
2000-08-28 20:40:03 +00:00
|
|
|
XL_SEL_WIN(2);
|
2004-07-04 22:20:52 +00:00
|
|
|
CSR_WRITE_2(sc, XL_W2_RESET_OPTIONS,
|
|
|
|
CSR_READ_2(sc, XL_W2_RESET_OPTIONS) |
|
|
|
|
((sc->xl_flags & XL_FLAG_INVERT_LED_PWR) ?
|
|
|
|
XL_RESETOPT_INVERT_LED : 0) |
|
|
|
|
((sc->xl_flags & XL_FLAG_INVERT_MII_PWR) ?
|
|
|
|
XL_RESETOPT_INVERT_MII : 0));
|
2000-08-28 20:40:03 +00:00
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/* Wait a little while for the chip to get its brains in order. */
|
1999-07-20 21:23:17 +00:00
|
|
|
DELAY(100000);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Probe for a 3Com Etherlink XL chip. Check the PCI vendor and device
|
|
|
|
* IDs against our list and return a device name if we find a match.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_probe(device_t dev)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2008-03-24 17:49:06 +00:00
|
|
|
const struct xl_type *t;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
t = xl_devs;
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
while (t->xl_name != NULL) {
|
1999-07-20 21:23:17 +00:00
|
|
|
if ((pci_get_vendor(dev) == t->xl_vid) &&
|
|
|
|
(pci_get_device(dev) == t->xl_did)) {
|
|
|
|
device_set_desc(dev, t->xl_name);
|
2005-02-24 21:32:56 +00:00
|
|
|
return (BUS_PROBE_DEFAULT);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
t++;
|
|
|
|
}
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (ENXIO);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This routine is a kludge to work around possible hardware faults
|
|
|
|
* or manufacturing defects that can cause the media options register
|
|
|
|
* (or reset options register, as it's called for the first generation
|
1999-08-29 15:52:19 +00:00
|
|
|
* 3c90x adapters) to return an incorrect result. I have encountered
|
1998-08-16 17:14:59 +00:00
|
|
|
* one Dell Latitude laptop docking station with an integrated 3c905-TX
|
|
|
|
* which doesn't have any of the 'mediaopt' bits set. This screws up
|
|
|
|
* the attach routine pretty badly because it doesn't know what media
|
|
|
|
* to look for. If we find ourselves in this predicament, this routine
|
|
|
|
* will try to guess the media options values and warn the user of a
|
|
|
|
* possible manufacturing defect with his adapter/system/whatever.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_mediacheck(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If some of the media options bits are set, assume they are
|
|
|
|
* correct. If not, try to figure it out down below.
|
|
|
|
* XXX I should check for 10baseFL, but I don't have an adapter
|
|
|
|
* to test with.
|
|
|
|
*/
|
1999-05-04 20:52:30 +00:00
|
|
|
if (sc->xl_media & (XL_MEDIAOPT_MASK & ~XL_MEDIAOPT_VCO)) {
|
1998-09-04 16:22:15 +00:00
|
|
|
/*
|
2004-07-04 22:20:52 +00:00
|
|
|
* Check the XCVR value. If it's not in the normal range
|
|
|
|
* of values, we need to fake it up here.
|
|
|
|
*/
|
1998-09-04 16:22:15 +00:00
|
|
|
if (sc->xl_xcvr <= XL_XCVR_AUTO)
|
|
|
|
return;
|
|
|
|
else {
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"bogus xcvr value in EEPROM (%x)\n", sc->xl_xcvr);
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"choosing new default based on card type\n");
|
1998-09-04 16:22:15 +00:00
|
|
|
}
|
|
|
|
} else {
|
1999-05-04 20:52:30 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B &&
|
|
|
|
sc->xl_media & XL_MEDIAOPT_10FL)
|
|
|
|
return;
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"WARNING: no media options bits set in the media options register!!\n");
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"this could be a manufacturing defect in your adapter or system\n");
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"attempting to guess media type; you should probably consult your vendor\n");
|
1998-09-04 16:22:15 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-09-20 20:26:14 +00:00
|
|
|
xl_choose_xcvr(sc, 1);
|
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_choose_xcvr(struct xl_softc *sc, int verbose)
|
1999-09-20 20:26:14 +00:00
|
|
|
{
|
|
|
|
u_int16_t devid;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Read the device ID from the EEPROM.
|
|
|
|
* This is what's loaded into the PCI device ID register, so it has
|
|
|
|
* to be correct otherwise we wouldn't have gotten this far.
|
|
|
|
*/
|
|
|
|
xl_read_eeprom(sc, (caddr_t)&devid, XL_EE_PRODID, 1, 0);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
switch (devid) {
|
1999-05-04 20:52:30 +00:00
|
|
|
case TC_DEVICEID_BOOMERANG_10BT: /* 3c900-TPO */
|
|
|
|
case TC_DEVICEID_KRAKATOA_10BT: /* 3c900B-TPO */
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_media = XL_MEDIAOPT_BT;
|
1998-09-04 16:22:15 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_10BT;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"guessing 10BaseT transceiver\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
|
|
|
case TC_DEVICEID_BOOMERANG_10BT_COMBO: /* 3c900-COMBO */
|
1999-04-30 16:15:43 +00:00
|
|
|
case TC_DEVICEID_KRAKATOA_10BT_COMBO: /* 3c900B-COMBO */
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
|
1998-09-04 16:22:15 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_10BT;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"guessing COMBO (AUI/BNC/TP)\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
1999-04-30 16:15:43 +00:00
|
|
|
case TC_DEVICEID_KRAKATOA_10BT_TPC: /* 3c900B-TPC */
|
1999-04-15 03:18:33 +00:00
|
|
|
sc->xl_media = XL_MEDIAOPT_BT|XL_MEDIAOPT_BNC;
|
|
|
|
sc->xl_xcvr = XL_XCVR_10BT;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "guessing TPC (BNC/TP)\n");
|
1999-04-15 03:18:33 +00:00
|
|
|
break;
|
1999-04-30 16:15:43 +00:00
|
|
|
case TC_DEVICEID_CYCLONE_10FL: /* 3c900B-FL */
|
|
|
|
sc->xl_media = XL_MEDIAOPT_10FL;
|
1999-05-04 20:52:30 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_AUI;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "guessing 10baseFL\n");
|
1999-04-30 16:15:43 +00:00
|
|
|
break;
|
1998-08-16 17:14:59 +00:00
|
|
|
case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */
|
2003-01-06 00:46:03 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_555: /* 3c555 */
|
2000-08-28 20:40:03 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_556: /* 3c556 */
|
|
|
|
case TC_DEVICEID_HURRICANE_556B: /* 3c556B */
|
2000-11-02 06:49:38 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_575A: /* 3c575TX */
|
2000-10-16 23:16:02 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_575B: /* 3c575B */
|
|
|
|
case TC_DEVICEID_HURRICANE_575C: /* 3c575C */
|
2001-08-28 00:40:18 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_656: /* 3c656 */
|
|
|
|
case TC_DEVICEID_HURRICANE_656B: /* 3c656B */
|
|
|
|
case TC_DEVICEID_TORNADO_656C: /* 3c656C */
|
2003-01-22 17:14:33 +00:00
|
|
|
case TC_DEVICEID_TORNADO_10_100BT_920B: /* 3c920B-EMB */
|
2005-01-03 19:22:37 +00:00
|
|
|
case TC_DEVICEID_TORNADO_10_100BT_920B_WNM: /* 3c920B-EMB-WNM */
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_media = XL_MEDIAOPT_MII;
|
1998-09-04 16:22:15 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_MII;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "guessing MII\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
|
|
|
case TC_DEVICEID_BOOMERANG_100BT4: /* 3c905-T4 */
|
|
|
|
case TC_DEVICEID_CYCLONE_10_100BT4: /* 3c905B-T4 */
|
|
|
|
sc->xl_media = XL_MEDIAOPT_BT4;
|
1998-09-04 16:22:15 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_MII;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "guessing 100baseT4/MII\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
1999-04-30 16:15:43 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_10_100BT: /* 3c905B-TX */
|
|
|
|
case TC_DEVICEID_HURRICANE_10_100BT_SERV:/*3c980-TX */
|
1999-09-15 07:19:34 +00:00
|
|
|
case TC_DEVICEID_TORNADO_10_100BT_SERV: /* 3c980C-TX */
|
1999-04-12 20:38:45 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_SOHO100TX: /* 3cSOHO100-TX */
|
1999-05-30 18:11:47 +00:00
|
|
|
case TC_DEVICEID_TORNADO_10_100BT: /* 3c905C-TX */
|
1999-12-16 18:33:57 +00:00
|
|
|
case TC_DEVICEID_TORNADO_HOMECONNECT: /* 3c450-TX */
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_media = XL_MEDIAOPT_BTX;
|
1998-09-04 16:22:15 +00:00
|
|
|
sc->xl_xcvr = XL_XCVR_AUTO;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "guessing 10/100 internal\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
1999-04-15 03:18:33 +00:00
|
|
|
case TC_DEVICEID_CYCLONE_10_100_COMBO: /* 3c905B-COMBO */
|
|
|
|
sc->xl_media = XL_MEDIAOPT_BTX|XL_MEDIAOPT_BNC|XL_MEDIAOPT_AUI;
|
|
|
|
sc->xl_xcvr = XL_XCVR_AUTO;
|
1999-09-20 20:26:14 +00:00
|
|
|
if (verbose)
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"guessing 10/100 plus BNC/AUI\n");
|
1999-04-15 03:18:33 +00:00
|
|
|
break;
|
1998-08-16 17:14:59 +00:00
|
|
|
default:
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"unknown device ID: %x -- defaulting to 10baseT\n", devid);
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_media = XL_MEDIAOPT_BT;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Attach the interface. Allocate softc structures, do ifmedia
|
|
|
|
* setup and ethernet/BPF attach.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_attach(device_t dev)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
1998-08-17 19:06:47 +00:00
|
|
|
u_char eaddr[ETHER_ADDR_LEN];
|
2003-01-19 00:23:59 +00:00
|
|
|
u_int16_t xcvr[2];
|
1998-08-16 17:14:59 +00:00
|
|
|
struct xl_softc *sc;
|
|
|
|
struct ifnet *ifp;
|
2004-07-05 02:34:35 +00:00
|
|
|
int media;
|
2003-03-18 06:29:51 +00:00
|
|
|
int unit, error = 0, rid, res;
|
2003-09-14 16:33:48 +00:00
|
|
|
uint16_t did;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-07-20 21:23:17 +00:00
|
|
|
sc = device_get_softc(dev);
|
2006-09-15 10:40:54 +00:00
|
|
|
sc->xl_dev = dev;
|
2008-03-24 17:49:06 +00:00
|
|
|
|
1999-07-20 21:23:17 +00:00
|
|
|
unit = device_get_unit(dev);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2002-04-04 21:03:38 +00:00
|
|
|
mtx_init(&sc->xl_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK,
|
2004-07-05 02:34:35 +00:00
|
|
|
MTX_DEF);
|
2003-04-17 20:32:06 +00:00
|
|
|
ifmedia_init(&sc->ifmedia, 0, xl_ifmedia_upd, xl_ifmedia_sts);
|
2000-12-04 22:46:50 +00:00
|
|
|
|
2003-09-14 16:33:48 +00:00
|
|
|
did = pci_get_device(dev);
|
|
|
|
|
2000-08-28 20:40:03 +00:00
|
|
|
sc->xl_flags = 0;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_555)
|
2003-01-06 01:55:12 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_PHYOK;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_556 ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_556B)
|
2000-08-28 20:40:03 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK |
|
2000-10-16 23:16:02 +00:00
|
|
|
XL_FLAG_EEPROM_OFFSET_30 | XL_FLAG_WEIRDRESET |
|
|
|
|
XL_FLAG_INVERT_LED_PWR | XL_FLAG_INVERT_MII_PWR;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_555 ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_556)
|
2000-08-28 20:40:03 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_8BITROM;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_556B)
|
2002-10-22 02:33:50 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_NO_XCVR_PWR;
|
2000-08-28 20:40:03 +00:00
|
|
|
|
2005-07-27 06:38:55 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_575B ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_575C ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_656B ||
|
|
|
|
did == TC_DEVICEID_TORNADO_656C)
|
|
|
|
sc->xl_flags |= XL_FLAG_FUNCREG;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_575A ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_575B ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_575C ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_656B ||
|
|
|
|
did == TC_DEVICEID_TORNADO_656C)
|
2005-07-27 06:38:55 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_PHYOK | XL_FLAG_EEPROM_OFFSET_30 |
|
|
|
|
XL_FLAG_8BITROM;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_656)
|
2001-08-28 00:40:18 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_FUNCREG | XL_FLAG_PHYOK;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_575B)
|
2000-10-16 23:16:02 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_INVERT_LED_PWR;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_575C)
|
2000-10-16 23:16:02 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_TORNADO_656C)
|
2000-12-01 19:41:14 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_INVERT_MII_PWR;
|
2003-09-14 16:33:48 +00:00
|
|
|
if (did == TC_DEVICEID_HURRICANE_656 ||
|
|
|
|
did == TC_DEVICEID_HURRICANE_656B)
|
2001-08-28 00:40:18 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_INVERT_MII_PWR |
|
|
|
|
XL_FLAG_INVERT_LED_PWR;
|
2005-01-03 19:22:37 +00:00
|
|
|
if (did == TC_DEVICEID_TORNADO_10_100BT_920B ||
|
|
|
|
did == TC_DEVICEID_TORNADO_10_100BT_920B_WNM)
|
2003-01-19 04:47:54 +00:00
|
|
|
sc->xl_flags |= XL_FLAG_PHYOK;
|
2003-09-14 16:33:48 +00:00
|
|
|
|
|
|
|
switch (did) {
|
2004-04-13 19:34:20 +00:00
|
|
|
case TC_DEVICEID_BOOMERANG_10_100BT: /* 3c905-TX */
|
2003-09-14 16:33:48 +00:00
|
|
|
case TC_DEVICEID_HURRICANE_575A:
|
|
|
|
case TC_DEVICEID_HURRICANE_575B:
|
|
|
|
case TC_DEVICEID_HURRICANE_575C:
|
|
|
|
sc->xl_flags |= XL_FLAG_NO_MMIO;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Map control/status registers.
|
|
|
|
*/
|
2001-02-21 20:54:22 +00:00
|
|
|
pci_enable_busmaster(dev);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2003-09-14 16:33:48 +00:00
|
|
|
if ((sc->xl_flags & XL_FLAG_NO_MMIO) == 0) {
|
|
|
|
rid = XL_PCI_LOMEM;
|
|
|
|
res = SYS_RES_MEMORY;
|
2003-03-18 06:29:51 +00:00
|
|
|
|
2004-03-17 17:50:55 +00:00
|
|
|
sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
|
2003-09-14 16:33:48 +00:00
|
|
|
}
|
1999-07-20 21:23:17 +00:00
|
|
|
|
2003-03-18 06:29:51 +00:00
|
|
|
if (sc->xl_res != NULL) {
|
|
|
|
sc->xl_flags |= XL_FLAG_USE_MMIO;
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "using memory mapped I/O\n");
|
2003-03-18 06:29:51 +00:00
|
|
|
} else {
|
|
|
|
rid = XL_PCI_LOIO;
|
|
|
|
res = SYS_RES_IOPORT;
|
2004-03-17 17:50:55 +00:00
|
|
|
sc->xl_res = bus_alloc_resource_any(dev, res, &rid, RF_ACTIVE);
|
2003-03-18 06:29:51 +00:00
|
|
|
if (sc->xl_res == NULL) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "couldn't map ports/memory\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
error = ENXIO;
|
2003-03-18 06:29:51 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "using port I/O\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
1999-07-20 21:23:17 +00:00
|
|
|
sc->xl_btag = rman_get_bustag(sc->xl_res);
|
|
|
|
sc->xl_bhandle = rman_get_bushandle(sc->xl_res);
|
|
|
|
|
2000-08-28 20:40:03 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_FUNCREG) {
|
|
|
|
rid = XL_PCI_FUNCMEM;
|
2004-03-17 17:50:55 +00:00
|
|
|
sc->xl_fres = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid,
|
|
|
|
RF_ACTIVE);
|
2000-08-28 20:40:03 +00:00
|
|
|
|
|
|
|
if (sc->xl_fres == NULL) {
|
2005-07-27 06:38:55 +00:00
|
|
|
device_printf(dev, "couldn't map funcreg memory\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
2000-08-28 20:40:03 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
sc->xl_ftag = rman_get_bustag(sc->xl_fres);
|
|
|
|
sc->xl_fhandle = rman_get_bushandle(sc->xl_fres);
|
|
|
|
}
|
|
|
|
|
2003-03-31 17:29:43 +00:00
|
|
|
/* Allocate interrupt */
|
1999-07-20 21:23:17 +00:00
|
|
|
rid = 0;
|
2004-03-17 17:50:55 +00:00
|
|
|
sc->xl_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid,
|
1999-07-20 21:23:17 +00:00
|
|
|
RF_SHAREABLE | RF_ACTIVE);
|
|
|
|
if (sc->xl_irq == NULL) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "couldn't map interrupt\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2004-07-05 00:15:23 +00:00
|
|
|
/* Initialize interface name. */
|
2005-06-10 16:49:24 +00:00
|
|
|
ifp = sc->xl_ifp = if_alloc(IFT_ETHER);
|
|
|
|
if (ifp == NULL) {
|
|
|
|
device_printf(dev, "can not if_alloc()\n");
|
|
|
|
error = ENOSPC;
|
|
|
|
goto fail;
|
|
|
|
}
|
2004-07-05 00:15:23 +00:00
|
|
|
ifp->if_softc = sc;
|
|
|
|
if_initname(ifp, device_get_name(dev), device_get_unit(dev));
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/* Reset the adapter. */
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_LOCK(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
xl_reset(sc);
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_UNLOCK(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Get station address from the EEPROM.
|
|
|
|
*/
|
|
|
|
if (xl_read_eeprom(sc, (caddr_t)&eaddr, XL_EE_OEM_ADR0, 3, 1)) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "failed to read station address\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2005-08-18 19:24:30 +00:00
|
|
|
callout_init_mtx(&sc->xl_stat_callout, &sc->xl_mtx, 0);
|
2005-05-20 07:16:09 +00:00
|
|
|
TASK_INIT(&sc->xl_task, 0, xl_rxeof_task, sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
/*
|
2003-03-31 17:29:43 +00:00
|
|
|
* Now allocate a tag for the DMA descriptor lists and a chunk
|
|
|
|
* of DMA-able memory based on the tag. Also obtain the DMA
|
|
|
|
* addresses of the RX and TX ring, which we'll need later.
|
2002-12-17 00:08:49 +00:00
|
|
|
* All of our lists are allocated as a contiguous block
|
|
|
|
* of memory.
|
|
|
|
*/
|
2006-12-06 02:18:41 +00:00
|
|
|
error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
|
2002-12-17 00:08:49 +00:00
|
|
|
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
2003-07-04 11:46:23 +00:00
|
|
|
XL_RX_LIST_SZ, 1, XL_RX_LIST_SZ, 0, NULL, NULL,
|
2003-07-01 15:52:06 +00:00
|
|
|
&sc->xl_ldata.xl_rx_tag);
|
2002-12-17 00:08:49 +00:00
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "failed to allocate rx dma tag\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_alloc(sc->xl_ldata.xl_rx_tag,
|
2003-07-27 13:56:03 +00:00
|
|
|
(void **)&sc->xl_ldata.xl_rx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO,
|
2003-03-31 17:29:43 +00:00
|
|
|
&sc->xl_ldata.xl_rx_dmamap);
|
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "no memory for rx list buffers!\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
|
|
|
|
sc->xl_ldata.xl_rx_tag = NULL;
|
|
|
|
goto fail;
|
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamap_load(sc->xl_ldata.xl_rx_tag,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap, sc->xl_ldata.xl_rx_list,
|
|
|
|
XL_RX_LIST_SZ, xl_dma_map_addr,
|
2003-07-04 11:46:23 +00:00
|
|
|
&sc->xl_ldata.xl_rx_dmaaddr, BUS_DMA_NOWAIT);
|
2003-03-31 17:29:43 +00:00
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "cannot get dma address of the rx ring!\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap);
|
|
|
|
bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
|
|
|
|
sc->xl_ldata.xl_rx_tag = NULL;
|
|
|
|
goto fail;
|
2002-12-17 00:08:49 +00:00
|
|
|
}
|
|
|
|
|
2006-12-06 02:18:41 +00:00
|
|
|
error = bus_dma_tag_create(bus_get_dma_tag(dev), 8, 0,
|
2002-12-17 00:08:49 +00:00
|
|
|
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
2003-07-04 11:46:23 +00:00
|
|
|
XL_TX_LIST_SZ, 1, XL_TX_LIST_SZ, 0, NULL, NULL,
|
2003-07-01 15:52:06 +00:00
|
|
|
&sc->xl_ldata.xl_tx_tag);
|
2002-12-17 00:08:49 +00:00
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "failed to allocate tx dma tag\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
goto fail;
|
2002-12-17 00:08:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
error = bus_dmamem_alloc(sc->xl_ldata.xl_tx_tag,
|
2003-07-27 13:56:03 +00:00
|
|
|
(void **)&sc->xl_ldata.xl_tx_list, BUS_DMA_NOWAIT | BUS_DMA_ZERO,
|
2002-12-17 00:08:49 +00:00
|
|
|
&sc->xl_ldata.xl_tx_dmamap);
|
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "no memory for list buffers!\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
|
|
|
|
sc->xl_ldata.xl_tx_tag = NULL;
|
|
|
|
goto fail;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2003-01-19 00:23:59 +00:00
|
|
|
error = bus_dmamap_load(sc->xl_ldata.xl_tx_tag,
|
|
|
|
sc->xl_ldata.xl_tx_dmamap, sc->xl_ldata.xl_tx_list,
|
|
|
|
XL_TX_LIST_SZ, xl_dma_map_addr,
|
2003-07-04 11:46:23 +00:00
|
|
|
&sc->xl_ldata.xl_tx_dmaaddr, BUS_DMA_NOWAIT);
|
2003-01-19 00:23:59 +00:00
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "cannot get dma address of the tx ring!\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
|
|
|
|
sc->xl_ldata.xl_tx_dmamap);
|
|
|
|
bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
|
|
|
|
sc->xl_ldata.xl_tx_tag = NULL;
|
|
|
|
goto fail;
|
2003-01-19 00:23:59 +00:00
|
|
|
}
|
|
|
|
|
2003-03-31 17:29:43 +00:00
|
|
|
/*
|
|
|
|
* Allocate a DMA tag for the mapping of mbufs.
|
|
|
|
*/
|
2006-12-06 02:18:41 +00:00
|
|
|
error = bus_dma_tag_create(bus_get_dma_tag(dev), 1, 0,
|
2003-04-09 15:05:19 +00:00
|
|
|
BUS_SPACE_MAXADDR_32BIT, BUS_SPACE_MAXADDR, NULL, NULL,
|
2003-07-04 11:46:23 +00:00
|
|
|
MCLBYTES * XL_MAXFRAGS, XL_MAXFRAGS, MCLBYTES, 0, NULL,
|
|
|
|
NULL, &sc->xl_mtag);
|
2002-12-17 00:08:49 +00:00
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "failed to allocate mbuf dma tag\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
goto fail;
|
2002-12-17 00:08:49 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2003-02-19 18:33:29 +00:00
|
|
|
/* We need a spare DMA map for the RX ring. */
|
|
|
|
error = bus_dmamap_create(sc->xl_mtag, 0, &sc->xl_tmpmap);
|
|
|
|
if (error)
|
2003-03-31 17:29:43 +00:00
|
|
|
goto fail;
|
2003-02-19 18:33:29 +00:00
|
|
|
|
1999-09-20 00:24:11 +00:00
|
|
|
/*
|
|
|
|
* Figure out the card type. 3c905B adapters have the
|
|
|
|
* 'supportsNoTxLength' bit set in the capabilities
|
|
|
|
* word in the EEPROM.
|
2003-07-10 05:24:33 +00:00
|
|
|
* Note: my 3c575C cardbus card lies. It returns a value
|
|
|
|
* of 0x1578 for its capabilities word, which is somewhat
|
2004-07-04 22:20:52 +00:00
|
|
|
* nonsensical. Another way to distinguish a 3c90x chip
|
2003-07-10 05:24:33 +00:00
|
|
|
* from a 3c90xB/C chip is to check for the 'supportsLargePackets'
|
|
|
|
* bit. This will only be set for 3c90x boomerage chips.
|
1999-09-20 00:24:11 +00:00
|
|
|
*/
|
|
|
|
xl_read_eeprom(sc, (caddr_t)&sc->xl_caps, XL_EE_CAPS, 1, 0);
|
2003-07-10 05:24:33 +00:00
|
|
|
if (sc->xl_caps & XL_CAPS_NO_TXLENGTH ||
|
|
|
|
!(sc->xl_caps & XL_CAPS_LARGE_PKTS))
|
1999-09-20 00:24:11 +00:00
|
|
|
sc->xl_type = XL_TYPE_905B;
|
|
|
|
else
|
|
|
|
sc->xl_type = XL_TYPE_90X;
|
|
|
|
|
2006-08-12 02:05:13 +00:00
|
|
|
/* Set the TX start threshold for best performance. */
|
|
|
|
sc->xl_tx_thresh = XL_MIN_FRAMELEN;
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
|
|
|
|
ifp->if_ioctl = xl_ioctl;
|
2003-07-10 05:24:33 +00:00
|
|
|
ifp->if_capabilities = IFCAP_VLAN_MTU;
|
2001-09-23 05:13:12 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B) {
|
2001-10-22 06:45:42 +00:00
|
|
|
ifp->if_hwassist = XL905B_CSUM_FEATURES;
|
2004-03-19 23:20:23 +00:00
|
|
|
#ifdef XL905B_TXCSUM_BROKEN
|
|
|
|
ifp->if_capabilities |= IFCAP_RXCSUM;
|
|
|
|
#else
|
2003-07-10 05:24:33 +00:00
|
|
|
ifp->if_capabilities |= IFCAP_HWCSUM;
|
2004-03-19 23:20:23 +00:00
|
|
|
#endif
|
2004-07-09 02:28:23 +00:00
|
|
|
}
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
ifp->if_capenable = ifp->if_capabilities;
|
2005-03-26 20:22:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
|
|
|
ifp->if_capabilities |= IFCAP_POLLING;
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
#endif
|
2004-07-09 02:28:23 +00:00
|
|
|
ifp->if_start = xl_start;
|
1998-08-16 17:14:59 +00:00
|
|
|
ifp->if_init = xl_init;
|
2004-07-02 12:16:02 +00:00
|
|
|
IFQ_SET_MAXLEN(&ifp->if_snd, XL_TX_LIST_CNT - 1);
|
|
|
|
ifp->if_snd.ifq_drv_maxlen = XL_TX_LIST_CNT - 1;
|
|
|
|
IFQ_SET_READY(&ifp->if_snd);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now we have to see what sort of media we have.
|
|
|
|
* This includes probing for an MII interace and a
|
|
|
|
* possible PHY.
|
|
|
|
*/
|
|
|
|
XL_SEL_WIN(3);
|
|
|
|
sc->xl_media = CSR_READ_2(sc, XL_W3_MEDIA_OPT);
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "media options word: %x\n", sc->xl_media);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2003-01-19 00:23:59 +00:00
|
|
|
xl_read_eeprom(sc, (char *)&xcvr, XL_EE_ICFG_0, 2, 0);
|
|
|
|
sc->xl_xcvr = xcvr[0] | xcvr[1] << 16;
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_xcvr &= XL_ICFG_CONNECTOR_MASK;
|
|
|
|
sc->xl_xcvr >>= XL_ICFG_CONNECTOR_BITS;
|
1998-09-04 16:22:15 +00:00
|
|
|
|
|
|
|
xl_mediacheck(sc);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
if (sc->xl_media & XL_MEDIAOPT_MII ||
|
|
|
|
sc->xl_media & XL_MEDIAOPT_BTX ||
|
|
|
|
sc->xl_media & XL_MEDIAOPT_BT4) {
|
1998-08-16 17:14:59 +00:00
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "found MII/AUTO\n");
|
1999-08-29 15:52:19 +00:00
|
|
|
xl_setcfg(sc);
|
|
|
|
if (mii_phy_probe(dev, &sc->xl_miibus,
|
|
|
|
xl_ifmedia_upd, xl_ifmedia_sts)) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "no PHY found!\n");
|
2003-03-31 17:29:43 +00:00
|
|
|
error = ENXIO;
|
|
|
|
goto fail;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
1999-08-29 15:52:19 +00:00
|
|
|
goto done;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
1999-09-20 20:26:14 +00:00
|
|
|
/*
|
|
|
|
* Sanity check. If the user has selected "auto" and this isn't
|
|
|
|
* a 10/100 card of some kind, we need to force the transceiver
|
|
|
|
* type to something sane.
|
|
|
|
*/
|
2005-08-18 19:24:30 +00:00
|
|
|
if (sc->xl_xcvr == XL_XCVR_AUTO)
|
1999-09-20 20:26:14 +00:00
|
|
|
xl_choose_xcvr(sc, bootverbose);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Do ifmedia setup.
|
|
|
|
*/
|
1999-05-04 20:52:30 +00:00
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BT) {
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "found 10baseT\n");
|
1999-05-04 20:52:30 +00:00
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T, 0, NULL);
|
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_T|IFM_HDX, 0, NULL);
|
|
|
|
if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
|
|
|
|
ifmedia_add(&sc->ifmedia,
|
|
|
|
IFM_ETHER|IFM_10_T|IFM_FDX, 0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->xl_media & (XL_MEDIAOPT_AUI|XL_MEDIAOPT_10FL)) {
|
1999-04-30 16:15:43 +00:00
|
|
|
/*
|
|
|
|
* Check for a 10baseFL board in disguise.
|
|
|
|
*/
|
|
|
|
if (sc->xl_type == XL_TYPE_905B &&
|
|
|
|
sc->xl_media == XL_MEDIAOPT_10FL) {
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "found 10baseFL\n");
|
1999-04-30 16:15:43 +00:00
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL, 0, NULL);
|
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_FL|IFM_HDX,
|
|
|
|
0, NULL);
|
|
|
|
if (sc->xl_caps & XL_CAPS_FULL_DUPLEX)
|
|
|
|
ifmedia_add(&sc->ifmedia,
|
|
|
|
IFM_ETHER|IFM_10_FL|IFM_FDX, 0, NULL);
|
|
|
|
} else {
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "found AUI\n");
|
1999-05-04 20:52:30 +00:00
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_5, 0, NULL);
|
1999-04-30 16:15:43 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BNC) {
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "found BNC\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_10_2, 0, NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->xl_media & XL_MEDIAOPT_BFX) {
|
|
|
|
if (bootverbose)
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "found 100baseFX\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
ifmedia_add(&sc->ifmedia, IFM_ETHER|IFM_100_FX, 0, NULL);
|
|
|
|
}
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
media = IFM_ETHER|IFM_100_TX|IFM_FDX;
|
|
|
|
xl_choose_media(sc, &media);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
if (sc->xl_miibus == NULL)
|
|
|
|
ifmedia_set(&sc->ifmedia, media);
|
|
|
|
|
|
|
|
done:
|
2002-10-22 02:33:50 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_NO_XCVR_PWR) {
|
|
|
|
XL_SEL_WIN(0);
|
|
|
|
CSR_WRITE_2(sc, XL_W0_MFG_ID, XL_NO_XCVR_PWR_MAGICBITS);
|
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
2000-07-13 22:54:34 +00:00
|
|
|
* Call MI attach routine.
|
1998-08-16 17:14:59 +00:00
|
|
|
*/
|
2002-11-14 23:49:09 +00:00
|
|
|
ether_ifattach(ifp, eaddr);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
error = bus_setup_intr(dev, sc->xl_irq, INTR_TYPE_NET | INTR_MPSAFE,
|
2007-02-23 12:19:07 +00:00
|
|
|
NULL, xl_intr, sc, &sc->xl_intrhand);
|
2003-03-31 17:29:43 +00:00
|
|
|
if (error) {
|
2004-07-05 00:15:23 +00:00
|
|
|
device_printf(dev, "couldn't set up irq\n");
|
2003-04-17 20:32:06 +00:00
|
|
|
ether_ifdetach(ifp);
|
2003-03-31 17:29:43 +00:00
|
|
|
goto fail;
|
2003-03-19 01:48:14 +00:00
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
fail:
|
2003-03-31 17:29:43 +00:00
|
|
|
if (error)
|
|
|
|
xl_detach(dev);
|
2000-10-13 17:54:19 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (error);
|
1999-07-20 21:23:17 +00:00
|
|
|
}
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
/*
|
|
|
|
* Choose a default media.
|
|
|
|
* XXX This is a leaf function only called by xl_attach() and
|
2005-08-18 19:24:30 +00:00
|
|
|
* acquires/releases the non-recursible driver mutex to
|
|
|
|
* satisfy lock assertions.
|
2004-07-05 02:34:35 +00:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xl_choose_media(struct xl_softc *sc, int *media)
|
|
|
|
{
|
|
|
|
|
|
|
|
XL_LOCK(sc);
|
|
|
|
|
|
|
|
switch (sc->xl_xcvr) {
|
|
|
|
case XL_XCVR_10BT:
|
|
|
|
*media = IFM_ETHER|IFM_10_T;
|
|
|
|
xl_setmode(sc, *media);
|
|
|
|
break;
|
|
|
|
case XL_XCVR_AUI:
|
|
|
|
if (sc->xl_type == XL_TYPE_905B &&
|
|
|
|
sc->xl_media == XL_MEDIAOPT_10FL) {
|
|
|
|
*media = IFM_ETHER|IFM_10_FL;
|
|
|
|
xl_setmode(sc, *media);
|
|
|
|
} else {
|
|
|
|
*media = IFM_ETHER|IFM_10_5;
|
|
|
|
xl_setmode(sc, *media);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XL_XCVR_COAX:
|
|
|
|
*media = IFM_ETHER|IFM_10_2;
|
|
|
|
xl_setmode(sc, *media);
|
|
|
|
break;
|
|
|
|
case XL_XCVR_AUTO:
|
|
|
|
case XL_XCVR_100BTX:
|
|
|
|
case XL_XCVR_MII:
|
|
|
|
/* Chosen by miibus */
|
|
|
|
break;
|
|
|
|
case XL_XCVR_100BFX:
|
|
|
|
*media = IFM_ETHER|IFM_100_FX;
|
|
|
|
break;
|
|
|
|
default:
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "unknown XCVR type: %d\n",
|
2004-07-05 02:34:35 +00:00
|
|
|
sc->xl_xcvr);
|
|
|
|
/*
|
|
|
|
* This will probably be wrong, but it prevents
|
|
|
|
* the ifmedia code from panicking.
|
|
|
|
*/
|
|
|
|
*media = IFM_ETHER|IFM_10_T;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
XL_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
2003-04-17 20:32:06 +00:00
|
|
|
/*
|
|
|
|
* Shutdown hardware and free up resources. This can be called any
|
|
|
|
* time after the mutex has been initialized. It is called in both
|
|
|
|
* the error case in attach and the normal detach case so it needs
|
|
|
|
* to be careful about only freeing resources that have actually been
|
|
|
|
* allocated.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_detach(device_t dev)
|
1999-07-20 21:23:17 +00:00
|
|
|
{
|
|
|
|
struct xl_softc *sc;
|
|
|
|
struct ifnet *ifp;
|
2003-03-18 06:29:51 +00:00
|
|
|
int rid, res;
|
1999-07-20 21:23:17 +00:00
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
2005-06-10 16:49:24 +00:00
|
|
|
ifp = sc->xl_ifp;
|
2004-07-05 00:15:23 +00:00
|
|
|
|
2003-03-31 20:22:00 +00:00
|
|
|
KASSERT(mtx_initialized(&sc->xl_mtx), ("xl mutex not initialized"));
|
1999-07-20 21:23:17 +00:00
|
|
|
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
2006-01-18 09:42:21 +00:00
|
|
|
if (ifp && ifp->if_capenable & IFCAP_POLLING)
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
ether_poll_deregister(ifp);
|
|
|
|
#endif
|
|
|
|
|
2003-03-18 06:29:51 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_USE_MMIO) {
|
2004-07-04 22:20:52 +00:00
|
|
|
rid = XL_PCI_LOMEM;
|
2003-03-18 06:29:51 +00:00
|
|
|
res = SYS_RES_MEMORY;
|
|
|
|
} else {
|
2004-07-04 22:20:52 +00:00
|
|
|
rid = XL_PCI_LOIO;
|
2003-03-18 06:29:51 +00:00
|
|
|
res = SYS_RES_IOPORT;
|
|
|
|
}
|
|
|
|
|
2003-04-17 20:32:06 +00:00
|
|
|
/* These should only be active if attach succeeded */
|
2003-04-21 18:34:04 +00:00
|
|
|
if (device_is_attached(dev)) {
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_LOCK(sc);
|
2003-04-17 20:32:06 +00:00
|
|
|
xl_reset(sc);
|
|
|
|
xl_stop(sc);
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_UNLOCK(sc);
|
|
|
|
taskqueue_drain(taskqueue_swi, &sc->xl_task);
|
|
|
|
callout_drain(&sc->xl_stat_callout);
|
2003-03-31 17:29:43 +00:00
|
|
|
ether_ifdetach(ifp);
|
1999-08-29 15:52:19 +00:00
|
|
|
}
|
2003-04-17 20:32:06 +00:00
|
|
|
if (sc->xl_miibus)
|
|
|
|
device_delete_child(dev, sc->xl_miibus);
|
|
|
|
bus_generic_detach(dev);
|
|
|
|
ifmedia_removeall(&sc->ifmedia);
|
1999-08-29 15:52:19 +00:00
|
|
|
|
2003-03-31 17:29:43 +00:00
|
|
|
if (sc->xl_intrhand)
|
|
|
|
bus_teardown_intr(dev, sc->xl_irq, sc->xl_intrhand);
|
|
|
|
if (sc->xl_irq)
|
|
|
|
bus_release_resource(dev, SYS_RES_IRQ, 0, sc->xl_irq);
|
2000-08-28 20:40:03 +00:00
|
|
|
if (sc->xl_fres != NULL)
|
|
|
|
bus_release_resource(dev, SYS_RES_MEMORY,
|
|
|
|
XL_PCI_FUNCMEM, sc->xl_fres);
|
2003-03-31 17:29:43 +00:00
|
|
|
if (sc->xl_res)
|
|
|
|
bus_release_resource(dev, res, rid, sc->xl_res);
|
|
|
|
|
2005-10-13 21:11:20 +00:00
|
|
|
if (ifp)
|
|
|
|
if_free(ifp);
|
|
|
|
|
2003-03-31 17:29:43 +00:00
|
|
|
if (sc->xl_mtag) {
|
|
|
|
bus_dmamap_destroy(sc->xl_mtag, sc->xl_tmpmap);
|
|
|
|
bus_dma_tag_destroy(sc->xl_mtag);
|
|
|
|
}
|
|
|
|
if (sc->xl_ldata.xl_rx_tag) {
|
|
|
|
bus_dmamap_unload(sc->xl_ldata.xl_rx_tag,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap);
|
|
|
|
bus_dmamem_free(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_list,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap);
|
|
|
|
bus_dma_tag_destroy(sc->xl_ldata.xl_rx_tag);
|
|
|
|
}
|
|
|
|
if (sc->xl_ldata.xl_tx_tag) {
|
|
|
|
bus_dmamap_unload(sc->xl_ldata.xl_tx_tag,
|
|
|
|
sc->xl_ldata.xl_tx_dmamap);
|
|
|
|
bus_dmamem_free(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_list,
|
|
|
|
sc->xl_ldata.xl_tx_dmamap);
|
|
|
|
bus_dma_tag_destroy(sc->xl_ldata.xl_tx_tag);
|
|
|
|
}
|
1999-07-20 21:23:17 +00:00
|
|
|
|
2000-10-13 17:54:19 +00:00
|
|
|
mtx_destroy(&sc->xl_mtx);
|
1999-07-20 21:23:17 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize the transmit descriptors.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_list_tx_init(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
struct xl_chain_data *cd;
|
|
|
|
struct xl_list_data *ld;
|
2002-12-17 00:08:49 +00:00
|
|
|
int error, i;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
cd = &sc->xl_cdata;
|
2002-12-17 00:08:49 +00:00
|
|
|
ld = &sc->xl_ldata;
|
1998-08-16 17:14:59 +00:00
|
|
|
for (i = 0; i < XL_TX_LIST_CNT; i++) {
|
|
|
|
cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
|
2002-12-17 00:08:49 +00:00
|
|
|
error = bus_dmamap_create(sc->xl_mtag, 0,
|
|
|
|
&cd->xl_tx_chain[i].xl_map);
|
|
|
|
if (error)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (error);
|
2003-01-19 00:23:59 +00:00
|
|
|
cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
|
|
|
|
i * sizeof(struct xl_list);
|
1998-08-16 17:14:59 +00:00
|
|
|
if (i == (XL_TX_LIST_CNT - 1))
|
|
|
|
cd->xl_tx_chain[i].xl_next = NULL;
|
|
|
|
else
|
|
|
|
cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
|
|
|
|
}
|
|
|
|
|
|
|
|
cd->xl_tx_free = &cd->xl_tx_chain[0];
|
|
|
|
cd->xl_tx_tail = cd->xl_tx_head = NULL;
|
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
1999-09-20 00:24:11 +00:00
|
|
|
/*
|
|
|
|
* Initialize the transmit descriptors.
|
|
|
|
*/
|
2002-12-17 00:08:49 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_list_tx_init_90xB(struct xl_softc *sc)
|
1999-09-20 00:24:11 +00:00
|
|
|
{
|
|
|
|
struct xl_chain_data *cd;
|
|
|
|
struct xl_list_data *ld;
|
2002-12-17 00:08:49 +00:00
|
|
|
int error, i;
|
1999-09-20 00:24:11 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1999-09-20 00:24:11 +00:00
|
|
|
cd = &sc->xl_cdata;
|
2002-12-17 00:08:49 +00:00
|
|
|
ld = &sc->xl_ldata;
|
1999-09-20 00:24:11 +00:00
|
|
|
for (i = 0; i < XL_TX_LIST_CNT; i++) {
|
|
|
|
cd->xl_tx_chain[i].xl_ptr = &ld->xl_tx_list[i];
|
2002-12-17 00:08:49 +00:00
|
|
|
error = bus_dmamap_create(sc->xl_mtag, 0,
|
|
|
|
&cd->xl_tx_chain[i].xl_map);
|
|
|
|
if (error)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (error);
|
2003-01-19 00:23:59 +00:00
|
|
|
cd->xl_tx_chain[i].xl_phys = ld->xl_tx_dmaaddr +
|
|
|
|
i * sizeof(struct xl_list);
|
1999-09-20 00:24:11 +00:00
|
|
|
if (i == (XL_TX_LIST_CNT - 1))
|
|
|
|
cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[0];
|
|
|
|
else
|
|
|
|
cd->xl_tx_chain[i].xl_next = &cd->xl_tx_chain[i + 1];
|
|
|
|
if (i == 0)
|
|
|
|
cd->xl_tx_chain[i].xl_prev =
|
|
|
|
&cd->xl_tx_chain[XL_TX_LIST_CNT - 1];
|
|
|
|
else
|
|
|
|
cd->xl_tx_chain[i].xl_prev =
|
|
|
|
&cd->xl_tx_chain[i - 1];
|
|
|
|
}
|
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
bzero(ld->xl_tx_list, XL_TX_LIST_SZ);
|
2003-01-19 00:23:59 +00:00
|
|
|
ld->xl_tx_list[0].xl_status = htole32(XL_TXSTAT_EMPTY);
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
cd->xl_tx_prod = 1;
|
|
|
|
cd->xl_tx_cons = 1;
|
|
|
|
cd->xl_tx_cnt = 0;
|
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_sync(ld->xl_tx_tag, ld->xl_tx_dmamap, BUS_DMASYNC_PREWRITE);
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1999-09-20 00:24:11 +00:00
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Initialize the RX descriptors and allocate mbufs for them. Note that
|
|
|
|
* we arrange the descriptors in a closed ring, so that the last descriptor
|
|
|
|
* points back to the first.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_list_rx_init(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
struct xl_chain_data *cd;
|
|
|
|
struct xl_list_data *ld;
|
2003-01-19 00:23:59 +00:00
|
|
|
int error, i, next;
|
|
|
|
u_int32_t nextptr;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
cd = &sc->xl_cdata;
|
2002-12-17 00:08:49 +00:00
|
|
|
ld = &sc->xl_ldata;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
for (i = 0; i < XL_RX_LIST_CNT; i++) {
|
2002-12-17 00:08:49 +00:00
|
|
|
cd->xl_rx_chain[i].xl_ptr = &ld->xl_rx_list[i];
|
|
|
|
error = bus_dmamap_create(sc->xl_mtag, 0,
|
|
|
|
&cd->xl_rx_chain[i].xl_map);
|
|
|
|
if (error)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (error);
|
2002-12-17 00:08:49 +00:00
|
|
|
error = xl_newbuf(sc, &cd->xl_rx_chain[i]);
|
|
|
|
if (error)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (error);
|
2003-01-19 00:23:59 +00:00
|
|
|
if (i == (XL_RX_LIST_CNT - 1))
|
|
|
|
next = 0;
|
|
|
|
else
|
|
|
|
next = i + 1;
|
|
|
|
nextptr = ld->xl_rx_dmaaddr +
|
|
|
|
next * sizeof(struct xl_list_onefrag);
|
|
|
|
cd->xl_rx_chain[i].xl_next = &cd->xl_rx_chain[next];
|
|
|
|
ld->xl_rx_list[i].xl_next = htole32(nextptr);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_sync(ld->xl_rx_tag, ld->xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
|
1998-08-16 17:14:59 +00:00
|
|
|
cd->xl_rx_head = &cd->xl_rx_chain[0];
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Initialize an RX descriptor and attach an MBUF cluster.
|
2003-02-18 18:50:54 +00:00
|
|
|
* If we fail to do so, we need to leave the old mbuf and
|
|
|
|
* the old DMA map untouched so that it can be reused.
|
1998-08-16 17:14:59 +00:00
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_newbuf(struct xl_softc *sc, struct xl_chain_onefrag *c)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
struct mbuf *m_new = NULL;
|
2003-02-18 18:50:54 +00:00
|
|
|
bus_dmamap_t map;
|
2008-03-24 17:49:06 +00:00
|
|
|
bus_dma_segment_t segs[1];
|
|
|
|
int error, nseg;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
2003-02-22 14:46:31 +00:00
|
|
|
m_new = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
|
2001-12-14 05:56:35 +00:00
|
|
|
if (m_new == NULL)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (ENOBUFS);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-09-20 00:24:11 +00:00
|
|
|
m_new->m_len = m_new->m_pkthdr.len = MCLBYTES;
|
|
|
|
|
1999-04-30 16:15:43 +00:00
|
|
|
/* Force longword alignment for packet payload. */
|
1999-09-20 00:24:11 +00:00
|
|
|
m_adj(m_new, ETHER_ALIGN);
|
1999-03-27 20:41:25 +00:00
|
|
|
|
2008-03-24 17:49:06 +00:00
|
|
|
error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, sc->xl_tmpmap, m_new,
|
|
|
|
segs, &nseg, BUS_DMA_NOWAIT);
|
2002-12-17 00:08:49 +00:00
|
|
|
if (error) {
|
|
|
|
m_freem(m_new);
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "can't map mbuf (error %d)\n",
|
2004-07-05 00:15:23 +00:00
|
|
|
error);
|
2004-07-04 22:20:52 +00:00
|
|
|
return (error);
|
2002-12-17 00:08:49 +00:00
|
|
|
}
|
2008-03-24 17:49:06 +00:00
|
|
|
KASSERT(nseg == 1,
|
|
|
|
("%s: too many DMA segments (%d)", __func__, nseg));
|
2003-02-18 18:50:54 +00:00
|
|
|
|
|
|
|
bus_dmamap_unload(sc->xl_mtag, c->xl_map);
|
|
|
|
map = c->xl_map;
|
|
|
|
c->xl_map = sc->xl_tmpmap;
|
|
|
|
sc->xl_tmpmap = map;
|
|
|
|
c->xl_mbuf = m_new;
|
|
|
|
c->xl_ptr->xl_frag.xl_len = htole32(m_new->m_len | XL_LAST_FRAG);
|
|
|
|
c->xl_ptr->xl_status = 0;
|
2008-03-24 17:49:06 +00:00
|
|
|
c->xl_ptr->xl_frag.xl_addr = htole32(segs->ds_addr);
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREREAD);
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_rx_resync(struct xl_softc *sc)
|
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
2000-01-03 15:28:47 +00:00
|
|
|
{
|
|
|
|
struct xl_chain_onefrag *pos;
|
|
|
|
int i;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
2000-01-03 15:28:47 +00:00
|
|
|
pos = sc->xl_cdata.xl_rx_head;
|
|
|
|
|
|
|
|
for (i = 0; i < XL_RX_LIST_CNT; i++) {
|
|
|
|
if (pos->xl_ptr->xl_status)
|
|
|
|
break;
|
|
|
|
pos = pos->xl_next;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (i == XL_RX_LIST_CNT)
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
2000-01-03 15:28:47 +00:00
|
|
|
|
|
|
|
sc->xl_cdata.xl_rx_head = pos;
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (EAGAIN);
|
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
2000-01-03 15:28:47 +00:00
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* A frame has been uploaded: pass the resulting mbuf chain up to
|
|
|
|
* the higher level protocols.
|
|
|
|
*/
|
2009-05-30 15:14:44 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_rxeof(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2004-07-04 22:20:52 +00:00
|
|
|
struct mbuf *m;
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
struct xl_chain_onefrag *cur_rx;
|
|
|
|
int total_len = 0;
|
2009-05-30 15:14:44 +00:00
|
|
|
int rx_npkts = 0;
|
2001-09-23 05:13:12 +00:00
|
|
|
u_int32_t rxstat;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2003-11-14 19:00:32 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
again:
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_rx_tag, sc->xl_ldata.xl_rx_dmamap,
|
|
|
|
BUS_DMASYNC_POSTREAD);
|
2004-07-04 22:20:52 +00:00
|
|
|
while ((rxstat = le32toh(sc->xl_cdata.xl_rx_head->xl_ptr->xl_status))) {
|
2005-03-26 20:22:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
if (ifp->if_capenable & IFCAP_POLLING) {
|
2005-03-26 20:22:58 +00:00
|
|
|
if (sc->rxcycles <= 0)
|
|
|
|
break;
|
|
|
|
sc->rxcycles--;
|
|
|
|
}
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
#endif
|
1998-08-16 17:14:59 +00:00
|
|
|
cur_rx = sc->xl_cdata.xl_rx_head;
|
1998-08-31 15:26:09 +00:00
|
|
|
sc->xl_cdata.xl_rx_head = cur_rx->xl_next;
|
2003-07-10 05:24:33 +00:00
|
|
|
total_len = rxstat & XL_RXSTAT_LENMASK;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Since we have told the chip to allow large frames,
|
|
|
|
* we need to trap giant frame errors in software. We allow
|
|
|
|
* a little more than the normal frame size to account for
|
|
|
|
* frames with VLAN tags.
|
|
|
|
*/
|
|
|
|
if (total_len > XL_MAX_FRAMELEN)
|
|
|
|
rxstat |= (XL_RXSTAT_UP_ERROR|XL_RXSTAT_OVERSIZE);
|
1998-08-31 15:26:09 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* If an error occurs, update stats, clear the
|
|
|
|
* status word and leave the mbuf cluster in place:
|
|
|
|
* it should simply get re-used next time this descriptor
|
2004-07-04 22:20:52 +00:00
|
|
|
* comes up in the ring.
|
1998-08-16 17:14:59 +00:00
|
|
|
*/
|
|
|
|
if (rxstat & XL_RXSTAT_UP_ERROR) {
|
|
|
|
ifp->if_ierrors++;
|
|
|
|
cur_rx->xl_ptr->xl_status = 0;
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
|
1998-08-16 17:14:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-07-10 05:24:33 +00:00
|
|
|
* If the error bit was not set, the upload complete
|
1998-08-16 17:14:59 +00:00
|
|
|
* bit should be set which means we have a valid packet.
|
|
|
|
* If not, something truly strange has happened.
|
|
|
|
*/
|
|
|
|
if (!(rxstat & XL_RXSTAT_UP_CMPLT)) {
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"bad receive status -- packet dropped\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
ifp->if_ierrors++;
|
|
|
|
cur_rx->xl_ptr->xl_status = 0;
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
|
1998-08-16 17:14:59 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
/* No errors; receive the packet. */
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_sync(sc->xl_mtag, cur_rx->xl_map,
|
2003-01-19 00:23:59 +00:00
|
|
|
BUS_DMASYNC_POSTREAD);
|
1998-08-16 17:14:59 +00:00
|
|
|
m = cur_rx->xl_mbuf;
|
1998-08-31 15:26:09 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Try to conjure up a new mbuf cluster. If that
|
|
|
|
* fails, it means we have an out of memory condition and
|
|
|
|
* should leave the buffer in place and continue. This will
|
|
|
|
* result in a lost packet, but there's little else we
|
|
|
|
* can do in this situation.
|
|
|
|
*/
|
2002-12-17 00:08:49 +00:00
|
|
|
if (xl_newbuf(sc, cur_rx)) {
|
1998-08-31 15:26:09 +00:00
|
|
|
ifp->if_ierrors++;
|
|
|
|
cur_rx->xl_ptr->xl_status = 0;
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
|
1998-08-31 15:26:09 +00:00
|
|
|
continue;
|
|
|
|
}
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_rx_tag,
|
|
|
|
sc->xl_ldata.xl_rx_dmamap, BUS_DMASYNC_PREWRITE);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-04-15 03:18:33 +00:00
|
|
|
ifp->if_ipackets++;
|
1998-08-16 17:14:59 +00:00
|
|
|
m->m_pkthdr.rcvif = ifp;
|
1999-04-16 01:56:06 +00:00
|
|
|
m->m_pkthdr.len = m->m_len = total_len;
|
1999-04-30 16:15:43 +00:00
|
|
|
|
2003-07-10 05:24:33 +00:00
|
|
|
if (ifp->if_capenable & IFCAP_RXCSUM) {
|
2001-09-23 05:13:12 +00:00
|
|
|
/* Do IP checksum checking. */
|
|
|
|
if (rxstat & XL_RXSTAT_IPCKOK)
|
|
|
|
m->m_pkthdr.csum_flags |= CSUM_IP_CHECKED;
|
|
|
|
if (!(rxstat & XL_RXSTAT_IPCKERR))
|
|
|
|
m->m_pkthdr.csum_flags |= CSUM_IP_VALID;
|
|
|
|
if ((rxstat & XL_RXSTAT_TCPCOK &&
|
|
|
|
!(rxstat & XL_RXSTAT_TCPCKERR)) ||
|
|
|
|
(rxstat & XL_RXSTAT_UDPCKOK &&
|
|
|
|
!(rxstat & XL_RXSTAT_UDPCKERR))) {
|
|
|
|
m->m_pkthdr.csum_flags |=
|
|
|
|
CSUM_DATA_VALID|CSUM_PSEUDO_HDR;
|
|
|
|
m->m_pkthdr.csum_data = 0xffff;
|
|
|
|
}
|
|
|
|
}
|
2002-11-14 23:49:09 +00:00
|
|
|
|
2003-11-14 19:00:32 +00:00
|
|
|
XL_UNLOCK(sc);
|
2002-11-14 23:49:09 +00:00
|
|
|
(*ifp->if_input)(ifp, m);
|
2003-11-14 19:00:32 +00:00
|
|
|
XL_LOCK(sc);
|
2009-05-30 15:14:44 +00:00
|
|
|
rx_npkts++;
|
2005-08-18 19:24:30 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are running from the taskqueue, the interface
|
|
|
|
* might have been stopped while we were passing the last
|
|
|
|
* packet up the network stack.
|
|
|
|
*/
|
|
|
|
if (!(ifp->if_drv_flags & IFF_DRV_RUNNING))
|
2009-05-30 15:14:44 +00:00
|
|
|
return (rx_npkts);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle the 'end of channel' condition. When the upload
|
|
|
|
* engine hits the end of the RX ring, it will stall. This
|
|
|
|
* is our cue to flush the RX ring, reload the uplist pointer
|
1998-08-24 17:51:38 +00:00
|
|
|
* register and unstall the engine.
|
1998-08-16 17:14:59 +00:00
|
|
|
* XXX This is actually a little goofy. With the ThunderLAN
|
|
|
|
* chip, you get an interrupt when the receiver hits the end
|
|
|
|
* of the receive ring, which tells you exactly when you
|
|
|
|
* you need to reload the ring pointer. Here we have to
|
|
|
|
* fake it. I'm mad at myself for not being clever enough
|
|
|
|
* to avoid the use of a goto here.
|
|
|
|
*/
|
|
|
|
if (CSR_READ_4(sc, XL_UPLIST_PTR) == 0 ||
|
|
|
|
CSR_READ_4(sc, XL_UPLIST_STATUS) & XL_PKTSTAT_UP_STALLED) {
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
|
|
|
|
xl_wait(sc);
|
2002-12-17 00:08:49 +00:00
|
|
|
CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_cdata.xl_rx_head = &sc->xl_cdata.xl_rx_chain[0];
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
|
|
|
|
goto again;
|
|
|
|
}
|
2009-05-30 15:14:44 +00:00
|
|
|
return (rx_npkts);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2005-05-20 07:16:09 +00:00
|
|
|
/*
|
|
|
|
* Taskqueue wrapper for xl_rxeof().
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xl_rxeof_task(void *arg, int pending)
|
|
|
|
{
|
|
|
|
struct xl_softc *sc = (struct xl_softc *)arg;
|
|
|
|
|
|
|
|
XL_LOCK(sc);
|
2005-08-18 19:24:30 +00:00
|
|
|
if (sc->xl_ifp->if_drv_flags & IFF_DRV_RUNNING)
|
|
|
|
xl_rxeof(sc);
|
2005-05-20 07:16:09 +00:00
|
|
|
XL_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* A frame was downloaded to the chip. It's safe for us to clean up
|
|
|
|
* the list buffers.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_txeof(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
struct xl_chain *cur_tx;
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Go through our tx list and free mbufs for those
|
|
|
|
* frames that have been uploaded. Note: the 3c905B
|
|
|
|
* sets a special bit in the status word to let us
|
|
|
|
* know that a frame has been downloaded, but the
|
|
|
|
* original 3c900/3c905 adapters don't do that.
|
|
|
|
* Consequently, we have to use a different test if
|
|
|
|
* xl_type != XL_TYPE_905B.
|
|
|
|
*/
|
2004-07-04 22:20:52 +00:00
|
|
|
while (sc->xl_cdata.xl_tx_head != NULL) {
|
1998-08-16 17:14:59 +00:00
|
|
|
cur_tx = sc->xl_cdata.xl_tx_head;
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
if (CSR_READ_4(sc, XL_DOWNLIST_PTR))
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
1999-09-20 00:24:11 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_cdata.xl_tx_head = cur_tx->xl_next;
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
|
2003-01-19 00:23:59 +00:00
|
|
|
BUS_DMASYNC_POSTWRITE);
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
|
1998-08-16 17:14:59 +00:00
|
|
|
m_freem(cur_tx->xl_mbuf);
|
|
|
|
cur_tx->xl_mbuf = NULL;
|
1999-04-15 03:18:33 +00:00
|
|
|
ifp->if_opackets++;
|
2009-04-21 00:34:31 +00:00
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
cur_tx->xl_next = sc->xl_cdata.xl_tx_free;
|
|
|
|
sc->xl_cdata.xl_tx_free = cur_tx;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->xl_cdata.xl_tx_head == NULL) {
|
2006-12-06 02:18:41 +00:00
|
|
|
sc->xl_wdog_timer = 0;
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_cdata.xl_tx_tail = NULL;
|
|
|
|
} else {
|
1999-04-30 16:15:43 +00:00
|
|
|
if (CSR_READ_4(sc, XL_DMACTL) & XL_DMACTL_DOWN_STALLED ||
|
|
|
|
!CSR_READ_4(sc, XL_DOWNLIST_PTR)) {
|
1998-10-22 15:52:25 +00:00
|
|
|
CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
|
2002-12-17 00:08:49 +00:00
|
|
|
sc->xl_cdata.xl_tx_head->xl_phys);
|
1998-10-22 15:52:25 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
|
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_txeof_90xB(struct xl_softc *sc)
|
1999-09-20 00:24:11 +00:00
|
|
|
{
|
|
|
|
struct xl_chain *cur_tx = NULL;
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1999-09-20 00:24:11 +00:00
|
|
|
int idx;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
|
|
|
|
BUS_DMASYNC_POSTREAD);
|
1999-09-20 00:24:11 +00:00
|
|
|
idx = sc->xl_cdata.xl_tx_cons;
|
2004-07-04 22:20:52 +00:00
|
|
|
while (idx != sc->xl_cdata.xl_tx_prod) {
|
1999-09-20 00:24:11 +00:00
|
|
|
cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
|
|
|
|
|
2003-01-19 00:23:59 +00:00
|
|
|
if (!(le32toh(cur_tx->xl_ptr->xl_status) &
|
|
|
|
XL_TXSTAT_DL_COMPLETE))
|
1999-09-20 00:24:11 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
if (cur_tx->xl_mbuf != NULL) {
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_sync(sc->xl_mtag, cur_tx->xl_map,
|
2003-01-19 00:23:59 +00:00
|
|
|
BUS_DMASYNC_POSTWRITE);
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_unload(sc->xl_mtag, cur_tx->xl_map);
|
1999-09-20 00:24:11 +00:00
|
|
|
m_freem(cur_tx->xl_mbuf);
|
|
|
|
cur_tx->xl_mbuf = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
ifp->if_opackets++;
|
|
|
|
|
|
|
|
sc->xl_cdata.xl_tx_cnt--;
|
|
|
|
XL_INC(idx, XL_TX_LIST_CNT);
|
|
|
|
}
|
|
|
|
|
2006-12-08 03:18:16 +00:00
|
|
|
if (sc->xl_cdata.xl_tx_cnt == 0)
|
|
|
|
sc->xl_wdog_timer = 0;
|
1999-09-20 00:24:11 +00:00
|
|
|
sc->xl_cdata.xl_tx_cons = idx;
|
|
|
|
|
|
|
|
if (cur_tx != NULL)
|
2005-08-09 10:20:02 +00:00
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
1999-09-20 00:24:11 +00:00
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* TX 'end of channel' interrupt handler. Actually, we should
|
|
|
|
* only get a 'TX complete' interrupt if there's a transmit error,
|
|
|
|
* so this is really TX error handler.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_txeoc(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
u_int8_t txstat;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
while ((txstat = CSR_READ_1(sc, XL_TX_STATUS))) {
|
1998-08-16 17:14:59 +00:00
|
|
|
if (txstat & XL_TXSTATUS_UNDERRUN ||
|
|
|
|
txstat & XL_TXSTATUS_JABBER ||
|
|
|
|
txstat & XL_TXSTATUS_RECLAIM) {
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"transmission error: %x\n", txstat);
|
1998-08-16 17:14:59 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
|
|
|
|
xl_wait(sc);
|
1999-09-20 00:24:11 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B) {
|
|
|
|
if (sc->xl_cdata.xl_tx_cnt) {
|
|
|
|
int i;
|
|
|
|
struct xl_chain *c;
|
2004-07-05 02:34:35 +00:00
|
|
|
|
1999-09-20 00:24:11 +00:00
|
|
|
i = sc->xl_cdata.xl_tx_cons;
|
|
|
|
c = &sc->xl_cdata.xl_tx_chain[i];
|
|
|
|
CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
|
|
|
|
c->xl_phys);
|
|
|
|
CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (sc->xl_cdata.xl_tx_head != NULL)
|
|
|
|
CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
|
2002-12-17 00:08:49 +00:00
|
|
|
sc->xl_cdata.xl_tx_head->xl_phys);
|
1999-09-20 00:24:11 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Remember to set this for the
|
|
|
|
* first generation 3c90X chips.
|
|
|
|
*/
|
|
|
|
CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
|
1999-05-05 17:05:07 +00:00
|
|
|
if (txstat & XL_TXSTATUS_UNDERRUN &&
|
|
|
|
sc->xl_tx_thresh < XL_PACKET_SIZE) {
|
|
|
|
sc->xl_tx_thresh += XL_MIN_FRAMELEN;
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev,
|
2004-07-05 00:15:23 +00:00
|
|
|
"tx underrun, increasing tx start threshold to %d bytes\n", sc->xl_tx_thresh);
|
1999-05-05 17:05:07 +00:00
|
|
|
}
|
1999-04-30 16:15:43 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
1999-05-05 17:05:07 +00:00
|
|
|
XL_CMD_TX_SET_START|sc->xl_tx_thresh);
|
1998-10-22 15:52:25 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B) {
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
|
|
|
XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
|
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
|
|
|
|
} else {
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
|
|
|
|
}
|
|
|
|
/*
|
|
|
|
* Write an arbitrary byte to the TX_STATUS register
|
2004-07-04 22:20:52 +00:00
|
|
|
* to clear this interrupt/error and advance to the next.
|
1998-08-16 17:14:59 +00:00
|
|
|
*/
|
|
|
|
CSR_WRITE_1(sc, XL_TX_STATUS, 0x01);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_intr(void *arg)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2004-07-04 23:31:28 +00:00
|
|
|
struct xl_softc *sc = arg;
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
u_int16_t status;
|
|
|
|
|
2000-10-13 17:54:19 +00:00
|
|
|
XL_LOCK(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-03-26 20:22:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
if (ifp->if_capenable & IFCAP_POLLING) {
|
2005-03-26 20:22:58 +00:00
|
|
|
XL_UNLOCK(sc);
|
|
|
|
return;
|
|
|
|
}
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
#endif
|
2005-03-26 20:22:58 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
while ((status = CSR_READ_2(sc, XL_STATUS)) & XL_INTRS &&
|
|
|
|
status != 0xFFFF) {
|
1999-04-30 16:15:43 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
|
|
|
XL_CMD_INTR_ACK|(status & XL_INTRS));
|
|
|
|
|
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
2000-01-03 15:28:47 +00:00
|
|
|
if (status & XL_STAT_UP_COMPLETE) {
|
2004-07-04 22:20:52 +00:00
|
|
|
int curpkts;
|
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
2000-01-03 15:28:47 +00:00
|
|
|
|
|
|
|
curpkts = ifp->if_ipackets;
|
1998-08-16 17:14:59 +00:00
|
|
|
xl_rxeof(sc);
|
It appears that under certain circumstances that I still can't quite pin
down, the dc driver and receiver can fall out of sync with one another,
resulting in a condition where the chip continues to receive packets
but the driver never notices. Normally, the receive handler checks each
descriptor starting from the current producer index to see if the chip
has relinquished ownership, indicating that a packet has been received.
The driver hands the packet off to ether_input() and then prepares the
descriptor to receive another frame before moving on to the next
descriptor in the ring. But sometimes, the chip appears to skip a
descriptor. This leaves the driver testing the status word in a descriptor
that never gets updated. The driver still gets "RX done" interrupts but
never advances further into the RX ring, until the ring fills up and the
chip interrupts again to signal an error condition. Sometimes, the
driver will remain in this desynchronized state, resulting in spotty
performance until the interface is reset.
Fortunately, it's fairly simple to detect this condition: if we call
the rxeof routine but the number of received packets doesn't increase,
we suspect that there could be a problem. In this case, we call a new
routine called dc_rx_resync(), which scans ahead in the RX ring to see
if there's a frame waiting for us somewhere beyond that the driver thinks
is the current producer index. If it finds one, it bumps up the index
and calls the rxeof handler again to snarf up the packet and bring the
driver back in sync with the chip. (It may actually do this several times
in the event that there's more than one "hole" in the ring.)
So far the only card supported by if_dc which has exhibited this problem
is a LinkSys LNE100TX v2.0 (82c115 PNIC II), and it only seems to happen
on one particular system, however the fix is general enough and has low
enough overhead that we may as well apply it for all supported chipsets.
I also implemented the same fix for the 3Com xl driver, which is apparently
vulnerable to the same problem.
Problem originally noted and patch tested by: Matt Dillon
2000-01-03 15:28:47 +00:00
|
|
|
if (curpkts == ifp->if_ipackets) {
|
|
|
|
while (xl_rx_resync(sc))
|
|
|
|
xl_rxeof(sc);
|
|
|
|
}
|
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-09-20 00:24:11 +00:00
|
|
|
if (status & XL_STAT_DOWN_COMPLETE) {
|
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_txeof_90xB(sc);
|
|
|
|
else
|
|
|
|
xl_txeof(sc);
|
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
if (status & XL_STAT_TX_COMPLETE) {
|
|
|
|
ifp->if_oerrors++;
|
|
|
|
xl_txeoc(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status & XL_STAT_ADFAIL) {
|
|
|
|
xl_reset(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
xl_init_locked(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (status & XL_STAT_STATSOFLOW) {
|
|
|
|
sc->xl_stats_no_timeout = 1;
|
2004-07-05 02:34:35 +00:00
|
|
|
xl_stats_update_locked(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_stats_no_timeout = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2004-07-09 02:28:23 +00:00
|
|
|
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
|
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_start_90xB_locked(ifp);
|
|
|
|
else
|
|
|
|
xl_start_locked(ifp);
|
|
|
|
}
|
2004-07-05 02:34:35 +00:00
|
|
|
|
2004-07-09 02:28:23 +00:00
|
|
|
XL_UNLOCK(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
}
|
|
|
|
|
2005-03-26 20:22:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
2009-05-30 15:14:44 +00:00
|
|
|
static int
|
2005-03-26 20:22:58 +00:00
|
|
|
xl_poll(struct ifnet *ifp, enum poll_cmd cmd, int count)
|
|
|
|
{
|
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
2009-05-30 15:14:44 +00:00
|
|
|
int rx_npkts = 0;
|
2005-03-26 20:22:58 +00:00
|
|
|
|
|
|
|
XL_LOCK(sc);
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
2009-05-30 15:14:44 +00:00
|
|
|
rx_npkts = xl_poll_locked(ifp, cmd, count);
|
2005-03-26 20:22:58 +00:00
|
|
|
XL_UNLOCK(sc);
|
2009-05-30 15:14:44 +00:00
|
|
|
return (rx_npkts);
|
2005-03-26 20:22:58 +00:00
|
|
|
}
|
|
|
|
|
2009-05-30 15:14:44 +00:00
|
|
|
static int
|
2005-03-26 20:22:58 +00:00
|
|
|
xl_poll_locked(struct ifnet *ifp, enum poll_cmd cmd, int count)
|
|
|
|
{
|
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
2009-05-30 15:14:44 +00:00
|
|
|
int rx_npkts;
|
2005-03-26 20:22:58 +00:00
|
|
|
|
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
sc->rxcycles = count;
|
2009-05-30 15:14:44 +00:00
|
|
|
rx_npkts = xl_rxeof(sc);
|
2005-03-26 20:22:58 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_txeof_90xB(sc);
|
|
|
|
else
|
|
|
|
xl_txeof(sc);
|
|
|
|
|
|
|
|
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
|
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_start_90xB_locked(ifp);
|
|
|
|
else
|
|
|
|
xl_start_locked(ifp);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cmd == POLL_AND_CHECK_STATUS) {
|
|
|
|
u_int16_t status;
|
|
|
|
|
|
|
|
status = CSR_READ_2(sc, XL_STATUS);
|
|
|
|
if (status & XL_INTRS && status != 0xFFFF) {
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
|
|
|
XL_CMD_INTR_ACK|(status & XL_INTRS));
|
|
|
|
|
|
|
|
if (status & XL_STAT_TX_COMPLETE) {
|
|
|
|
ifp->if_oerrors++;
|
|
|
|
xl_txeoc(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status & XL_STAT_ADFAIL) {
|
|
|
|
xl_reset(sc);
|
|
|
|
xl_init_locked(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (status & XL_STAT_STATSOFLOW) {
|
|
|
|
sc->xl_stats_no_timeout = 1;
|
|
|
|
xl_stats_update_locked(sc);
|
|
|
|
sc->xl_stats_no_timeout = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2009-05-30 15:14:44 +00:00
|
|
|
return (rx_npkts);
|
2005-03-26 20:22:58 +00:00
|
|
|
}
|
|
|
|
#endif /* DEVICE_POLLING */
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
/*
|
|
|
|
* XXX: This is an entry point for callout which needs to take the lock.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
xl_stats_update(void *xsc)
|
|
|
|
{
|
|
|
|
struct xl_softc *sc = xsc;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
2006-12-06 02:18:41 +00:00
|
|
|
|
|
|
|
if (xl_watchdog(sc) == EJUSTRETURN)
|
|
|
|
return;
|
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
xl_stats_update_locked(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-05 02:34:35 +00:00
|
|
|
xl_stats_update_locked(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
struct xl_stats xl_stats;
|
|
|
|
u_int8_t *p;
|
|
|
|
int i;
|
1999-08-29 15:52:19 +00:00
|
|
|
struct mii_data *mii = NULL;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
bzero((char *)&xl_stats, sizeof(struct xl_stats));
|
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
if (sc->xl_miibus != NULL)
|
|
|
|
mii = device_get_softc(sc->xl_miibus);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
p = (u_int8_t *)&xl_stats;
|
|
|
|
|
|
|
|
/* Read all the stats registers. */
|
|
|
|
XL_SEL_WIN(6);
|
|
|
|
|
|
|
|
for (i = 0; i < 16; i++)
|
|
|
|
*p++ = CSR_READ_1(sc, XL_W6_CARRIER_LOST + i);
|
|
|
|
|
|
|
|
ifp->if_ierrors += xl_stats.xl_rx_overrun;
|
|
|
|
|
|
|
|
ifp->if_collisions += xl_stats.xl_tx_multi_collision +
|
2004-07-04 22:20:52 +00:00
|
|
|
xl_stats.xl_tx_single_collision + xl_stats.xl_tx_late_collision;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Boomerang and cyclone chips have an extra stats counter
|
|
|
|
* in window 4 (BadSSD). We have to read this too in order
|
|
|
|
* to clear out all the stats registers and avoid a statsoflow
|
|
|
|
* interrupt.
|
|
|
|
*/
|
|
|
|
XL_SEL_WIN(4);
|
|
|
|
CSR_READ_1(sc, XL_W4_BADSSD);
|
|
|
|
|
2001-12-17 22:24:19 +00:00
|
|
|
if ((mii != NULL) && (!sc->xl_stats_no_timeout))
|
1999-08-29 15:52:19 +00:00
|
|
|
mii_tick(mii);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(7);
|
|
|
|
|
|
|
|
if (!sc->xl_stats_no_timeout)
|
2005-08-18 19:24:30 +00:00
|
|
|
callout_reset(&sc->xl_stat_callout, hz, xl_stats_update, sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Encapsulate an mbuf chain in a descriptor by coupling the mbuf data
|
|
|
|
* pointers to the fragment pointers.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2008-03-24 17:49:06 +00:00
|
|
|
xl_encap(struct xl_softc *sc, struct xl_chain *c, struct mbuf **m_head)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2008-03-24 17:49:06 +00:00
|
|
|
struct mbuf *m_new;
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
2008-03-24 17:49:06 +00:00
|
|
|
int error, i, nseg, total_len;
|
|
|
|
u_int32_t status;
|
2003-07-10 05:24:33 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
2008-03-24 17:49:06 +00:00
|
|
|
error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map, *m_head,
|
|
|
|
sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2003-01-07 01:01:20 +00:00
|
|
|
if (error && error != EFBIG) {
|
2004-07-05 00:15:23 +00:00
|
|
|
if_printf(ifp, "can't map mbuf (error %d)\n", error);
|
2008-03-24 17:49:06 +00:00
|
|
|
return (error);
|
2003-01-07 01:01:20 +00:00
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Handle special case: we used up all 63 fragments,
|
|
|
|
* but we have more mbufs left in the chain. Copy the
|
|
|
|
* data into an mbuf cluster. Note that we don't
|
|
|
|
* bother clearing the values in the other fragment
|
|
|
|
* pointers/counters; it wouldn't gain us anything,
|
|
|
|
* and would waste cycles.
|
|
|
|
*/
|
2002-12-17 00:08:49 +00:00
|
|
|
if (error) {
|
2008-03-24 17:49:06 +00:00
|
|
|
m_new = m_collapse(*m_head, M_DONTWAIT, XL_MAXFRAGS);
|
1998-08-16 17:14:59 +00:00
|
|
|
if (m_new == NULL) {
|
2008-03-24 17:49:06 +00:00
|
|
|
m_freem(*m_head);
|
|
|
|
*m_head = NULL;
|
|
|
|
return (ENOBUFS);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
2008-03-24 17:49:06 +00:00
|
|
|
*m_head = m_new;
|
2003-03-29 06:34:25 +00:00
|
|
|
|
2008-03-24 17:49:06 +00:00
|
|
|
error = bus_dmamap_load_mbuf_sg(sc->xl_mtag, c->xl_map,
|
|
|
|
*m_head, sc->xl_cdata.xl_tx_segs, &nseg, BUS_DMA_NOWAIT);
|
2002-12-17 00:08:49 +00:00
|
|
|
if (error) {
|
2008-03-24 17:49:06 +00:00
|
|
|
m_freem(*m_head);
|
|
|
|
*m_head = NULL;
|
2004-07-05 00:15:23 +00:00
|
|
|
if_printf(ifp, "can't map mbuf (error %d)\n", error);
|
2008-03-24 17:49:06 +00:00
|
|
|
return (error);
|
2002-12-17 00:08:49 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2008-03-24 17:49:06 +00:00
|
|
|
KASSERT(nseg <= XL_MAXFRAGS,
|
|
|
|
("%s: too many DMA segments (%d)", __func__, nseg));
|
|
|
|
if (nseg == 0) {
|
|
|
|
m_freem(*m_head);
|
|
|
|
*m_head = NULL;
|
|
|
|
return (EIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
total_len = 0;
|
|
|
|
for (i = 0; i < nseg; i++) {
|
|
|
|
KASSERT(sc->xl_cdata.xl_tx_segs[i].ds_len <= MCLBYTES,
|
|
|
|
("segment size too large"));
|
|
|
|
c->xl_ptr->xl_frag[i].xl_addr =
|
|
|
|
htole32(sc->xl_cdata.xl_tx_segs[i].ds_addr);
|
|
|
|
c->xl_ptr->xl_frag[i].xl_len =
|
|
|
|
htole32(sc->xl_cdata.xl_tx_segs[i].ds_len);
|
|
|
|
total_len += sc->xl_cdata.xl_tx_segs[i].ds_len;
|
|
|
|
}
|
|
|
|
c->xl_ptr->xl_frag[nseg - 1].xl_len =
|
|
|
|
htole32(sc->xl_cdata.xl_tx_segs[nseg - 1].ds_len | XL_LAST_FRAG);
|
|
|
|
c->xl_ptr->xl_status = htole32(total_len);
|
|
|
|
c->xl_ptr->xl_next = 0;
|
|
|
|
|
2002-12-18 23:16:22 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B) {
|
2003-01-19 00:23:59 +00:00
|
|
|
status = XL_TXSTAT_RND_DEFEAT;
|
2002-12-18 23:16:22 +00:00
|
|
|
|
2004-03-19 23:20:23 +00:00
|
|
|
#ifndef XL905B_TXCSUM_BROKEN
|
2009-07-09 01:58:59 +00:00
|
|
|
if ((*m_head)->m_pkthdr.csum_flags) {
|
|
|
|
if ((*m_head)->m_pkthdr.csum_flags & CSUM_IP)
|
2003-01-19 00:23:59 +00:00
|
|
|
status |= XL_TXSTAT_IPCKSUM;
|
2009-07-09 01:58:59 +00:00
|
|
|
if ((*m_head)->m_pkthdr.csum_flags & CSUM_TCP)
|
2003-01-19 00:23:59 +00:00
|
|
|
status |= XL_TXSTAT_TCPCKSUM;
|
2009-07-09 01:58:59 +00:00
|
|
|
if ((*m_head)->m_pkthdr.csum_flags & CSUM_UDP)
|
2003-01-19 00:23:59 +00:00
|
|
|
status |= XL_TXSTAT_UDPCKSUM;
|
2002-12-18 23:16:22 +00:00
|
|
|
}
|
2004-03-19 23:20:23 +00:00
|
|
|
#endif
|
2003-01-19 00:23:59 +00:00
|
|
|
c->xl_ptr->xl_status = htole32(status);
|
2002-12-18 23:16:22 +00:00
|
|
|
}
|
|
|
|
|
2008-03-24 17:49:06 +00:00
|
|
|
c->xl_mbuf = *m_head;
|
2003-01-19 00:23:59 +00:00
|
|
|
bus_dmamap_sync(sc->xl_mtag, c->xl_map, BUS_DMASYNC_PREWRITE);
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Main transmit routine. To avoid having to do mbuf copies, we put pointers
|
|
|
|
* to the mbuf data regions directly in the transmit lists. We also save a
|
|
|
|
* copy of the pointers since the transmit list fragment pointers are
|
|
|
|
* physical addresses.
|
|
|
|
*/
|
2004-07-09 02:28:23 +00:00
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_start(struct ifnet *ifp)
|
2004-07-09 02:28:23 +00:00
|
|
|
{
|
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
|
|
|
|
|
|
|
XL_LOCK(sc);
|
|
|
|
|
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_start_90xB_locked(ifp);
|
|
|
|
else
|
|
|
|
xl_start_locked(ifp);
|
|
|
|
|
|
|
|
XL_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xl_start_locked(struct ifnet *ifp)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2004-07-04 23:31:28 +00:00
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
1998-08-16 17:14:59 +00:00
|
|
|
struct mbuf *m_head = NULL;
|
|
|
|
struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx;
|
2003-01-19 00:23:59 +00:00
|
|
|
u_int32_t status;
|
2003-01-07 01:01:20 +00:00
|
|
|
int error;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-09 02:28:23 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
2004-07-04 23:31:28 +00:00
|
|
|
|
2009-04-21 00:34:31 +00:00
|
|
|
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
|
|
|
|
IFF_DRV_RUNNING)
|
|
|
|
return;
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Check for an available queue slot. If there are none,
|
|
|
|
* punt.
|
|
|
|
*/
|
|
|
|
if (sc->xl_cdata.xl_tx_free == NULL) {
|
1998-09-25 17:34:19 +00:00
|
|
|
xl_txeoc(sc);
|
|
|
|
xl_txeof(sc);
|
|
|
|
if (sc->xl_cdata.xl_tx_free == NULL) {
|
2005-08-09 10:20:02 +00:00
|
|
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
1998-09-25 17:34:19 +00:00
|
|
|
return;
|
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
start_tx = sc->xl_cdata.xl_tx_free;
|
|
|
|
|
2008-03-24 17:49:06 +00:00
|
|
|
for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
|
|
|
|
sc->xl_cdata.xl_tx_free != NULL;) {
|
2004-07-02 12:16:02 +00:00
|
|
|
IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
|
1998-08-16 17:14:59 +00:00
|
|
|
if (m_head == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
/* Pick a descriptor off the free list. */
|
|
|
|
cur_tx = sc->xl_cdata.xl_tx_free;
|
|
|
|
|
|
|
|
/* Pack the data into the descriptor. */
|
2008-03-24 17:49:06 +00:00
|
|
|
error = xl_encap(sc, cur_tx, &m_head);
|
2003-04-08 01:05:54 +00:00
|
|
|
if (error) {
|
2008-03-24 17:49:06 +00:00
|
|
|
if (m_head == NULL)
|
|
|
|
break;
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
|
|
|
IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
|
|
|
|
break;
|
2003-04-08 01:05:54 +00:00
|
|
|
}
|
2003-01-07 01:01:20 +00:00
|
|
|
|
|
|
|
sc->xl_cdata.xl_tx_free = cur_tx->xl_next;
|
|
|
|
cur_tx->xl_next = NULL;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/* Chain it together. */
|
|
|
|
if (prev != NULL) {
|
|
|
|
prev->xl_next = cur_tx;
|
2003-01-19 00:23:59 +00:00
|
|
|
prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
prev = cur_tx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there's a BPF listener, bounce a copy of this frame
|
|
|
|
* to him.
|
|
|
|
*/
|
2002-11-14 23:49:09 +00:00
|
|
|
BPF_MTAP(ifp, cur_tx->xl_mbuf);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
1998-12-05 02:21:44 +00:00
|
|
|
/*
|
|
|
|
* If there are no packets queued, bail.
|
|
|
|
*/
|
2004-07-09 02:28:23 +00:00
|
|
|
if (cur_tx == NULL)
|
1998-12-05 02:21:44 +00:00
|
|
|
return;
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Place the request for the upload interrupt
|
|
|
|
* in the last descriptor in the chain. This way, if
|
|
|
|
* we're chaining several packets at once, we'll only
|
2007-10-12 06:03:46 +00:00
|
|
|
* get an interrupt once for the whole chain rather than
|
1998-08-16 17:14:59 +00:00
|
|
|
* once for each packet.
|
|
|
|
*/
|
2003-01-19 00:23:59 +00:00
|
|
|
cur_tx->xl_ptr->xl_status = htole32(le32toh(cur_tx->xl_ptr->xl_status) |
|
|
|
|
XL_TXSTAT_DL_INTR);
|
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-04-30 16:15:43 +00:00
|
|
|
/*
|
|
|
|
* Queue the packets. If the TX channel is clear, update
|
|
|
|
* the downlist pointer register.
|
|
|
|
*/
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
|
|
|
|
xl_wait(sc);
|
|
|
|
|
1999-07-08 00:42:02 +00:00
|
|
|
if (sc->xl_cdata.xl_tx_head != NULL) {
|
1999-04-30 16:15:43 +00:00
|
|
|
sc->xl_cdata.xl_tx_tail->xl_next = start_tx;
|
2003-01-19 00:23:59 +00:00
|
|
|
sc->xl_cdata.xl_tx_tail->xl_ptr->xl_next =
|
|
|
|
htole32(start_tx->xl_phys);
|
|
|
|
status = sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status;
|
|
|
|
sc->xl_cdata.xl_tx_tail->xl_ptr->xl_status =
|
|
|
|
htole32(le32toh(status) & ~XL_TXSTAT_DL_INTR);
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_cdata.xl_tx_tail = cur_tx;
|
1999-04-15 03:18:33 +00:00
|
|
|
} else {
|
1999-04-30 16:15:43 +00:00
|
|
|
sc->xl_cdata.xl_tx_head = start_tx;
|
1999-04-15 03:18:33 +00:00
|
|
|
sc->xl_cdata.xl_tx_tail = cur_tx;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
1999-07-07 21:49:14 +00:00
|
|
|
if (!CSR_READ_4(sc, XL_DOWNLIST_PTR))
|
2002-12-17 00:08:49 +00:00
|
|
|
CSR_WRITE_4(sc, XL_DOWNLIST_PTR, start_tx->xl_phys);
|
1999-07-07 21:49:14 +00:00
|
|
|
|
1999-04-30 16:15:43 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
|
|
|
|
|
|
|
|
XL_SEL_WIN(7);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set a timeout in case the chip goes out to lunch.
|
|
|
|
*/
|
2006-12-06 02:18:41 +00:00
|
|
|
sc->xl_wdog_timer = 5;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1998-08-24 17:51:38 +00:00
|
|
|
/*
|
|
|
|
* XXX Under certain conditions, usually on slower machines
|
|
|
|
* where interrupts may be dropped, it's possible for the
|
|
|
|
* adapter to chew up all the buffers in the receive ring
|
|
|
|
* and stall, without us being able to do anything about it.
|
|
|
|
* To guard against this, we need to make a pass over the
|
|
|
|
* RX queue to make sure there aren't any packets pending.
|
|
|
|
* Doing it here means we can flush the receive ring at the
|
|
|
|
* same time the chip is DMAing the transmit descriptors we
|
|
|
|
* just gave it.
|
2004-07-04 22:20:52 +00:00
|
|
|
*
|
1998-08-24 17:51:38 +00:00
|
|
|
* 3Com goes to some lengths to emphasize the Parallel Tasking (tm)
|
|
|
|
* nature of their chips in all their marketing literature;
|
|
|
|
* we may as well take advantage of it. :)
|
|
|
|
*/
|
2005-05-20 07:16:09 +00:00
|
|
|
taskqueue_enqueue(taskqueue_swi, &sc->xl_task);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-12-17 00:08:49 +00:00
|
|
|
static void
|
2004-07-09 02:28:23 +00:00
|
|
|
xl_start_90xB_locked(struct ifnet *ifp)
|
1999-09-20 00:24:11 +00:00
|
|
|
{
|
2004-07-04 23:31:28 +00:00
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
1999-09-20 00:24:11 +00:00
|
|
|
struct mbuf *m_head = NULL;
|
|
|
|
struct xl_chain *prev = NULL, *cur_tx = NULL, *start_tx;
|
2003-01-07 01:01:20 +00:00
|
|
|
int error, idx;
|
1999-09-20 00:24:11 +00:00
|
|
|
|
2004-07-09 02:28:23 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
1999-09-20 00:24:11 +00:00
|
|
|
|
2009-04-21 00:34:31 +00:00
|
|
|
if ((ifp->if_drv_flags & (IFF_DRV_RUNNING | IFF_DRV_OACTIVE)) !=
|
|
|
|
IFF_DRV_RUNNING)
|
2004-07-09 02:28:23 +00:00
|
|
|
return;
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
idx = sc->xl_cdata.xl_tx_prod;
|
|
|
|
start_tx = &sc->xl_cdata.xl_tx_chain[idx];
|
|
|
|
|
2008-03-24 17:49:06 +00:00
|
|
|
for (; !IFQ_DRV_IS_EMPTY(&ifp->if_snd) &&
|
|
|
|
sc->xl_cdata.xl_tx_chain[idx].xl_mbuf == NULL;) {
|
1999-09-20 00:24:11 +00:00
|
|
|
if ((XL_TX_LIST_CNT - sc->xl_cdata.xl_tx_cnt) < 3) {
|
2005-08-09 10:20:02 +00:00
|
|
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
1999-09-20 00:24:11 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-07-02 12:16:02 +00:00
|
|
|
IFQ_DRV_DEQUEUE(&ifp->if_snd, m_head);
|
1999-09-20 00:24:11 +00:00
|
|
|
if (m_head == NULL)
|
|
|
|
break;
|
|
|
|
|
|
|
|
cur_tx = &sc->xl_cdata.xl_tx_chain[idx];
|
|
|
|
|
|
|
|
/* Pack the data into the descriptor. */
|
2008-03-24 17:49:06 +00:00
|
|
|
error = xl_encap(sc, cur_tx, &m_head);
|
2003-04-08 01:05:54 +00:00
|
|
|
if (error) {
|
2008-03-24 17:49:06 +00:00
|
|
|
if (m_head == NULL)
|
|
|
|
break;
|
|
|
|
ifp->if_drv_flags |= IFF_DRV_OACTIVE;
|
|
|
|
IFQ_DRV_PREPEND(&ifp->if_snd, m_head);
|
|
|
|
break;
|
2003-04-08 01:05:54 +00:00
|
|
|
}
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
/* Chain it together. */
|
|
|
|
if (prev != NULL)
|
2003-01-19 00:23:59 +00:00
|
|
|
prev->xl_ptr->xl_next = htole32(cur_tx->xl_phys);
|
1999-09-20 00:24:11 +00:00
|
|
|
prev = cur_tx;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there's a BPF listener, bounce a copy of this frame
|
|
|
|
* to him.
|
|
|
|
*/
|
2002-11-14 23:49:09 +00:00
|
|
|
BPF_MTAP(ifp, cur_tx->xl_mbuf);
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
XL_INC(idx, XL_TX_LIST_CNT);
|
|
|
|
sc->xl_cdata.xl_tx_cnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If there are no packets queued, bail.
|
|
|
|
*/
|
2004-07-05 02:34:35 +00:00
|
|
|
if (cur_tx == NULL)
|
2004-07-09 02:28:23 +00:00
|
|
|
return;
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Place the request for the upload interrupt
|
|
|
|
* in the last descriptor in the chain. This way, if
|
|
|
|
* we're chaining several packets at once, we'll only
|
2007-10-12 06:03:46 +00:00
|
|
|
* get an interrupt once for the whole chain rather than
|
1999-09-20 00:24:11 +00:00
|
|
|
* once for each packet.
|
|
|
|
*/
|
2003-01-19 00:23:59 +00:00
|
|
|
cur_tx->xl_ptr->xl_status = htole32(le32toh(cur_tx->xl_ptr->xl_status) |
|
|
|
|
XL_TXSTAT_DL_INTR);
|
|
|
|
bus_dmamap_sync(sc->xl_ldata.xl_tx_tag, sc->xl_ldata.xl_tx_dmamap,
|
|
|
|
BUS_DMASYNC_PREWRITE);
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
/* Start transmission */
|
|
|
|
sc->xl_cdata.xl_tx_prod = idx;
|
2003-01-19 00:23:59 +00:00
|
|
|
start_tx->xl_prev->xl_ptr->xl_next = htole32(start_tx->xl_phys);
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set a timeout in case the chip goes out to lunch.
|
|
|
|
*/
|
2006-12-06 02:18:41 +00:00
|
|
|
sc->xl_wdog_timer = 5;
|
1999-09-20 00:24:11 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_init(void *xsc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
struct xl_softc *sc = xsc;
|
2004-07-05 02:34:35 +00:00
|
|
|
|
|
|
|
XL_LOCK(sc);
|
|
|
|
xl_init_locked(sc);
|
|
|
|
XL_UNLOCK(sc);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
xl_init_locked(struct xl_softc *sc)
|
|
|
|
{
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
2002-12-17 00:08:49 +00:00
|
|
|
int error, i;
|
1998-08-24 17:51:38 +00:00
|
|
|
u_int16_t rxfilt = 0;
|
1999-08-29 15:52:19 +00:00
|
|
|
struct mii_data *mii = NULL;
|
1998-08-19 15:07:46 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Cancel pending I/O and free all RX/TX buffers.
|
|
|
|
*/
|
|
|
|
xl_stop(sc);
|
|
|
|
|
2000-01-09 21:12:59 +00:00
|
|
|
if (sc->xl_miibus == NULL) {
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
|
|
|
|
xl_wait(sc);
|
|
|
|
}
|
1999-10-25 20:52:32 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
|
1998-08-16 17:14:59 +00:00
|
|
|
xl_wait(sc);
|
1999-10-25 20:52:32 +00:00
|
|
|
DELAY(10000);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
if (sc->xl_miibus != NULL)
|
|
|
|
mii = device_get_softc(sc->xl_miibus);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/* Init our MAC address */
|
|
|
|
XL_SEL_WIN(2);
|
|
|
|
for (i = 0; i < ETHER_ADDR_LEN; i++) {
|
|
|
|
CSR_WRITE_1(sc, XL_W2_STATION_ADDR_LO + i,
|
2005-11-11 16:04:59 +00:00
|
|
|
IF_LLADDR(sc->xl_ifp)[i]);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Clear the station mask. */
|
|
|
|
for (i = 0; i < 3; i++)
|
|
|
|
CSR_WRITE_2(sc, XL_W2_STATION_MASK_LO + (i * 2), 0);
|
1999-02-11 23:59:29 +00:00
|
|
|
#ifdef notdef
|
1998-08-16 17:14:59 +00:00
|
|
|
/* Reset TX and RX. */
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
|
|
|
|
xl_wait(sc);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
|
|
|
|
xl_wait(sc);
|
1999-02-11 23:59:29 +00:00
|
|
|
#endif
|
1998-08-16 17:14:59 +00:00
|
|
|
/* Init circular RX list. */
|
2002-12-17 00:08:49 +00:00
|
|
|
error = xl_list_rx_init(sc);
|
|
|
|
if (error) {
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "initialization of the rx ring failed (%d)\n",
|
2004-07-05 00:15:23 +00:00
|
|
|
error);
|
1998-08-31 15:26:09 +00:00
|
|
|
xl_stop(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Init TX descriptors. */
|
1999-09-20 00:24:11 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
2002-12-17 00:08:49 +00:00
|
|
|
error = xl_list_tx_init_90xB(sc);
|
1999-09-20 00:24:11 +00:00
|
|
|
else
|
2002-12-17 00:08:49 +00:00
|
|
|
error = xl_list_tx_init(sc);
|
|
|
|
if (error) {
|
2006-09-15 10:40:54 +00:00
|
|
|
device_printf(sc->xl_dev, "initialization of the tx ring failed (%d)\n",
|
2004-07-05 00:15:23 +00:00
|
|
|
error);
|
2002-12-17 00:08:49 +00:00
|
|
|
xl_stop(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
return;
|
2002-12-17 00:08:49 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Set the TX freethresh value.
|
|
|
|
* Note that this has no effect on 3c905B "cyclone"
|
|
|
|
* cards but is required for 3c900/3c905 "boomerang"
|
|
|
|
* cards in order to enable the download engine.
|
|
|
|
*/
|
|
|
|
CSR_WRITE_1(sc, XL_TX_FREETHRESH, XL_PACKET_SIZE >> 8);
|
|
|
|
|
1999-04-30 16:15:43 +00:00
|
|
|
/* Set the TX start threshold for best performance. */
|
1999-05-05 17:05:07 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_SET_START|sc->xl_tx_thresh);
|
1999-04-30 16:15:43 +00:00
|
|
|
|
1998-10-22 15:52:25 +00:00
|
|
|
/*
|
|
|
|
* If this is a 3c905B, also set the tx reclaim threshold.
|
|
|
|
* This helps cut down on the number of tx reclaim errors
|
|
|
|
* that could happen on a busy network. The chip multiplies
|
|
|
|
* the register value by 16 to obtain the actual threshold
|
|
|
|
* in bytes, so we divide by 16 when setting the value here.
|
|
|
|
* The existing threshold value can be examined by reading
|
|
|
|
* the register at offset 9 in window 5.
|
|
|
|
*/
|
|
|
|
if (sc->xl_type == XL_TYPE_905B) {
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
1999-09-20 00:24:11 +00:00
|
|
|
XL_CMD_SET_TX_RECLAIM|(XL_PACKET_SIZE >> 4));
|
1998-10-22 15:52:25 +00:00
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/* Set RX filter bits. */
|
|
|
|
XL_SEL_WIN(5);
|
|
|
|
rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
|
|
|
|
|
|
|
|
/* Set the individual bit to receive frames for this host only. */
|
|
|
|
rxfilt |= XL_RXFILTER_INDIVIDUAL;
|
|
|
|
|
|
|
|
/* If we want promiscuous mode, set the allframes bit. */
|
|
|
|
if (ifp->if_flags & IFF_PROMISC) {
|
|
|
|
rxfilt |= XL_RXFILTER_ALLFRAMES;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
} else {
|
|
|
|
rxfilt &= ~XL_RXFILTER_ALLFRAMES;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set capture broadcast bit to capture broadcast frames.
|
|
|
|
*/
|
|
|
|
if (ifp->if_flags & IFF_BROADCAST) {
|
|
|
|
rxfilt |= XL_RXFILTER_BROADCAST;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
} else {
|
|
|
|
rxfilt &= ~XL_RXFILTER_BROADCAST;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Program the multicast filter, if necessary.
|
|
|
|
*/
|
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_setmulti_hash(sc);
|
|
|
|
else
|
|
|
|
xl_setmulti(sc);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Load the address of the RX list. We have to
|
|
|
|
* stall the upload engine before we can manipulate
|
|
|
|
* the uplist pointer register, then unstall it when
|
|
|
|
* we're finished. We also have to wait for the
|
|
|
|
* stall command to complete before proceeding.
|
|
|
|
* Note that we have to do this after any RX resets
|
|
|
|
* have completed since the uplist register is cleared
|
|
|
|
* by a reset.
|
|
|
|
*/
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_STALL);
|
|
|
|
xl_wait(sc);
|
2002-12-17 00:08:49 +00:00
|
|
|
CSR_WRITE_4(sc, XL_UPLIST_PTR, sc->xl_ldata.xl_rx_dmaaddr);
|
1998-08-16 17:14:59 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_UP_UNSTALL);
|
1999-09-20 00:24:11 +00:00
|
|
|
xl_wait(sc);
|
|
|
|
|
|
|
|
if (sc->xl_type == XL_TYPE_905B) {
|
|
|
|
/* Set polling interval */
|
|
|
|
CSR_WRITE_1(sc, XL_DOWN_POLL, 64);
|
|
|
|
/* Load the address of the TX list */
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_STALL);
|
|
|
|
xl_wait(sc);
|
|
|
|
CSR_WRITE_4(sc, XL_DOWNLIST_PTR,
|
2002-12-17 00:08:49 +00:00
|
|
|
sc->xl_cdata.xl_tx_chain[0].xl_phys);
|
1999-09-20 00:24:11 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_DOWN_UNSTALL);
|
|
|
|
xl_wait(sc);
|
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If the coax transceiver is on, make sure to enable
|
|
|
|
* the DC-DC converter.
|
2004-07-04 22:20:52 +00:00
|
|
|
*/
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(3);
|
|
|
|
if (sc->xl_xcvr == XL_XCVR_COAX)
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_START);
|
|
|
|
else
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
|
|
|
|
|
2003-07-10 05:24:33 +00:00
|
|
|
/*
|
|
|
|
* increase packet size to allow reception of 802.1q or ISL packets.
|
|
|
|
* For the 3c90x chip, set the 'allow large packets' bit in the MAC
|
|
|
|
* control register. For 3c90xB/C chips, use the RX packet size
|
|
|
|
* register.
|
|
|
|
*/
|
2004-07-04 22:20:52 +00:00
|
|
|
|
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
2001-05-31 22:08:45 +00:00
|
|
|
CSR_WRITE_2(sc, XL_W3_MAXPKTSIZE, XL_PACKET_SIZE);
|
2003-07-10 05:24:33 +00:00
|
|
|
else {
|
|
|
|
u_int8_t macctl;
|
|
|
|
macctl = CSR_READ_1(sc, XL_W3_MAC_CTRL);
|
|
|
|
macctl |= XL_MACCTRL_ALLOW_LARGE_PACK;
|
|
|
|
CSR_WRITE_1(sc, XL_W3_MAC_CTRL, macctl);
|
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
/* Clear out the stats counters. */
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
|
|
|
|
sc->xl_stats_no_timeout = 1;
|
2004-07-05 02:34:35 +00:00
|
|
|
xl_stats_update_locked(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
sc->xl_stats_no_timeout = 0;
|
|
|
|
XL_SEL_WIN(4);
|
|
|
|
CSR_WRITE_2(sc, XL_W4_NET_DIAG, XL_NETDIAG_UPPER_BYTES_ENABLE);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_ENABLE);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Enable interrupts.
|
|
|
|
*/
|
1998-08-24 17:51:38 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|XL_INTRS);
|
2005-03-26 20:22:58 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
|
|
|
/* Disable interrupts if we are polling. */
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
if (ifp->if_capenable & IFCAP_POLLING)
|
2005-03-26 20:22:58 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
|
|
|
|
else
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
#endif
|
1998-08-24 17:51:38 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
|
2000-10-13 17:54:19 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_FUNCREG)
|
|
|
|
bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/* Set the RX early threshold */
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_SET_THRESH|(XL_PACKET_SIZE >>2));
|
|
|
|
CSR_WRITE_2(sc, XL_DMACTL, XL_DMACTL_UP_RX_EARLY);
|
|
|
|
|
|
|
|
/* Enable receiver and transmitter. */
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_ENABLE);
|
1999-09-20 00:24:11 +00:00
|
|
|
xl_wait(sc);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_ENABLE);
|
|
|
|
xl_wait(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
/* XXX Downcall to miibus. */
|
1999-08-29 15:52:19 +00:00
|
|
|
if (mii != NULL)
|
|
|
|
mii_mediachg(mii);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/* Select window 7 for normal operations. */
|
|
|
|
XL_SEL_WIN(7);
|
|
|
|
|
2005-08-09 10:20:02 +00:00
|
|
|
ifp->if_drv_flags |= IFF_DRV_RUNNING;
|
|
|
|
ifp->if_drv_flags &= ~IFF_DRV_OACTIVE;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2006-12-06 02:18:41 +00:00
|
|
|
sc->xl_wdog_timer = 0;
|
2005-08-18 19:24:30 +00:00
|
|
|
callout_reset(&sc->xl_stat_callout, hz, xl_stats_update, sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Set media options.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_ifmedia_upd(struct ifnet *ifp)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2004-07-04 23:31:28 +00:00
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
1999-08-29 15:52:19 +00:00
|
|
|
struct ifmedia *ifm = NULL;
|
|
|
|
struct mii_data *mii = NULL;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_LOCK(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
if (sc->xl_miibus != NULL)
|
|
|
|
mii = device_get_softc(sc->xl_miibus);
|
|
|
|
if (mii == NULL)
|
|
|
|
ifm = &sc->ifmedia;
|
|
|
|
else
|
|
|
|
ifm = &mii->mii_media;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
switch (IFM_SUBTYPE(ifm->ifm_media)) {
|
1999-04-15 03:18:33 +00:00
|
|
|
case IFM_100_FX:
|
1999-10-14 21:49:17 +00:00
|
|
|
case IFM_10_FL:
|
1999-04-15 03:18:33 +00:00
|
|
|
case IFM_10_2:
|
|
|
|
case IFM_10_5:
|
|
|
|
xl_setmode(sc, ifm->ifm_media);
|
2008-03-24 17:49:06 +00:00
|
|
|
XL_UNLOCK(sc);
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1999-04-15 03:18:33 +00:00
|
|
|
}
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
if (sc->xl_media & XL_MEDIAOPT_MII ||
|
|
|
|
sc->xl_media & XL_MEDIAOPT_BTX ||
|
|
|
|
sc->xl_media & XL_MEDIAOPT_BT4) {
|
2005-08-18 19:24:30 +00:00
|
|
|
xl_init_locked(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
} else {
|
|
|
|
xl_setmode(sc, ifm->ifm_media);
|
|
|
|
}
|
|
|
|
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_UNLOCK(sc);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Report current media status.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2004-07-04 23:31:28 +00:00
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
1998-08-16 17:14:59 +00:00
|
|
|
u_int32_t icfg;
|
2003-08-23 18:09:25 +00:00
|
|
|
u_int16_t status = 0;
|
1999-08-29 15:52:19 +00:00
|
|
|
struct mii_data *mii = NULL;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-08-18 19:24:30 +00:00
|
|
|
XL_LOCK(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
|
1999-08-29 15:52:19 +00:00
|
|
|
if (sc->xl_miibus != NULL)
|
|
|
|
mii = device_get_softc(sc->xl_miibus);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2003-08-23 18:09:25 +00:00
|
|
|
XL_SEL_WIN(4);
|
|
|
|
status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
XL_SEL_WIN(3);
|
|
|
|
icfg = CSR_READ_4(sc, XL_W3_INTERNAL_CFG) & XL_ICFG_CONNECTOR_MASK;
|
|
|
|
icfg >>= XL_ICFG_CONNECTOR_BITS;
|
|
|
|
|
|
|
|
ifmr->ifm_active = IFM_ETHER;
|
2003-08-23 18:09:25 +00:00
|
|
|
ifmr->ifm_status = IFM_AVALID;
|
|
|
|
|
|
|
|
if ((status & XL_MEDIASTAT_CARRIER) == 0)
|
|
|
|
ifmr->ifm_status |= IFM_ACTIVE;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
switch (icfg) {
|
1998-08-16 17:14:59 +00:00
|
|
|
case XL_XCVR_10BT:
|
1999-05-04 20:52:30 +00:00
|
|
|
ifmr->ifm_active = IFM_ETHER|IFM_10_T;
|
1998-08-16 17:14:59 +00:00
|
|
|
if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
|
|
|
|
ifmr->ifm_active |= IFM_FDX;
|
|
|
|
else
|
|
|
|
ifmr->ifm_active |= IFM_HDX;
|
|
|
|
break;
|
|
|
|
case XL_XCVR_AUI:
|
1999-05-04 20:52:30 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B &&
|
|
|
|
sc->xl_media == XL_MEDIAOPT_10FL) {
|
|
|
|
ifmr->ifm_active = IFM_ETHER|IFM_10_FL;
|
|
|
|
if (CSR_READ_1(sc, XL_W3_MAC_CTRL) & XL_MACCTRL_DUPLEX)
|
|
|
|
ifmr->ifm_active |= IFM_FDX;
|
|
|
|
else
|
|
|
|
ifmr->ifm_active |= IFM_HDX;
|
|
|
|
} else
|
|
|
|
ifmr->ifm_active = IFM_ETHER|IFM_10_5;
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
|
|
|
case XL_XCVR_COAX:
|
|
|
|
ifmr->ifm_active = IFM_ETHER|IFM_10_2;
|
|
|
|
break;
|
|
|
|
/*
|
|
|
|
* XXX MII and BTX/AUTO should be separate cases.
|
|
|
|
*/
|
|
|
|
|
|
|
|
case XL_XCVR_100BTX:
|
|
|
|
case XL_XCVR_AUTO:
|
|
|
|
case XL_XCVR_MII:
|
1999-08-29 15:52:19 +00:00
|
|
|
if (mii != NULL) {
|
|
|
|
mii_pollstat(mii);
|
|
|
|
ifmr->ifm_active = mii->mii_media_active;
|
|
|
|
ifmr->ifm_status = mii->mii_media_status;
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case XL_XCVR_100BFX:
|
|
|
|
ifmr->ifm_active = IFM_ETHER|IFM_100_FX;
|
|
|
|
break;
|
|
|
|
default:
|
2004-07-05 00:15:23 +00:00
|
|
|
if_printf(ifp, "unknown XCVR type: %d\n", icfg);
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
|
|
|
}
|
2005-08-18 19:24:30 +00:00
|
|
|
|
|
|
|
XL_UNLOCK(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
struct xl_softc *sc = ifp->if_softc;
|
|
|
|
struct ifreq *ifr = (struct ifreq *) data;
|
2000-10-13 17:54:19 +00:00
|
|
|
int error = 0;
|
1999-08-29 15:52:19 +00:00
|
|
|
struct mii_data *mii = NULL;
|
1999-10-14 21:49:17 +00:00
|
|
|
u_int8_t rxfilt;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
switch (command) {
|
1998-08-16 17:14:59 +00:00
|
|
|
case SIOCSIFFLAGS:
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK(sc);
|
|
|
|
|
1999-10-14 21:49:17 +00:00
|
|
|
XL_SEL_WIN(5);
|
|
|
|
rxfilt = CSR_READ_1(sc, XL_W5_RX_FILTER);
|
1998-08-16 17:14:59 +00:00
|
|
|
if (ifp->if_flags & IFF_UP) {
|
2005-08-09 10:20:02 +00:00
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
|
1999-10-14 21:49:17 +00:00
|
|
|
ifp->if_flags & IFF_PROMISC &&
|
|
|
|
!(sc->xl_if_flags & IFF_PROMISC)) {
|
|
|
|
rxfilt |= XL_RXFILTER_ALLFRAMES;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
|
|
|
XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
XL_SEL_WIN(7);
|
2005-08-09 10:20:02 +00:00
|
|
|
} else if (ifp->if_drv_flags & IFF_DRV_RUNNING &&
|
1999-10-14 21:49:17 +00:00
|
|
|
!(ifp->if_flags & IFF_PROMISC) &&
|
|
|
|
sc->xl_if_flags & IFF_PROMISC) {
|
|
|
|
rxfilt &= ~XL_RXFILTER_ALLFRAMES;
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND,
|
|
|
|
XL_CMD_RX_SET_FILT|rxfilt);
|
|
|
|
XL_SEL_WIN(7);
|
2004-09-26 09:01:02 +00:00
|
|
|
} else {
|
2005-08-09 10:20:02 +00:00
|
|
|
if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0)
|
2004-09-09 12:16:29 +00:00
|
|
|
xl_init_locked(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
}
|
1998-08-16 17:14:59 +00:00
|
|
|
} else {
|
2005-08-09 10:20:02 +00:00
|
|
|
if (ifp->if_drv_flags & IFF_DRV_RUNNING)
|
1998-08-16 17:14:59 +00:00
|
|
|
xl_stop(sc);
|
|
|
|
}
|
1999-10-14 21:49:17 +00:00
|
|
|
sc->xl_if_flags = ifp->if_flags;
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_UNLOCK(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SIOCADDMULTI:
|
|
|
|
case SIOCDELMULTI:
|
2004-07-05 02:34:35 +00:00
|
|
|
/* XXX Downcall from if_addmulti() possibly with locks held. */
|
|
|
|
XL_LOCK(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_setmulti_hash(sc);
|
|
|
|
else
|
|
|
|
xl_setmulti(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_UNLOCK(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
error = 0;
|
|
|
|
break;
|
|
|
|
case SIOCGIFMEDIA:
|
|
|
|
case SIOCSIFMEDIA:
|
1999-08-29 15:52:19 +00:00
|
|
|
if (sc->xl_miibus != NULL)
|
|
|
|
mii = device_get_softc(sc->xl_miibus);
|
|
|
|
if (mii == NULL)
|
|
|
|
error = ifmedia_ioctl(ifp, ifr,
|
|
|
|
&sc->ifmedia, command);
|
|
|
|
else
|
|
|
|
error = ifmedia_ioctl(ifp, ifr,
|
|
|
|
&mii->mii_media, command);
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
2004-07-04 22:20:52 +00:00
|
|
|
case SIOCSIFCAP:
|
Big polling(4) cleanup.
o Axe poll in trap.
o Axe IFF_POLLING flag from if_flags.
o Rework revision 1.21 (Giant removal), in such a way that
poll_mtx is not dropped during call to polling handler.
This fixes problem with idle polling.
o Make registration and deregistration from polling in a
functional way, insted of next tick/interrupt.
o Obsolete kern.polling.enable. Polling is turned on/off
with ifconfig.
Detailed kern_poll.c changes:
- Remove polling handler flags, introduced in 1.21. The are not
needed now.
- Forget and do not check if_flags, if_capenable and if_drv_flags.
- Call all registered polling handlers unconditionally.
- Do not drop poll_mtx, when entering polling handlers.
- In ether_poll() NET_LOCK_GIANT prior to locking poll_mtx.
- In netisr_poll() axe the block, where polling code asks drivers
to unregister.
- In netisr_poll() and ether_poll() do polling always, if any
handlers are present.
- In ether_poll_[de]register() remove a lot of error hiding code. Assert
that arguments are correct, instead.
- In ether_poll_[de]register() use standard return values in case of
error or success.
- Introduce poll_switch() that is a sysctl handler for kern.polling.enable.
poll_switch() goes through interface list and enabled/disables polling.
A message that kern.polling.enable is deprecated is printed.
Detailed driver changes:
- On attach driver announces IFCAP_POLLING in if_capabilities, but
not in if_capenable.
- On detach driver calls ether_poll_deregister() if polling is enabled.
- In polling handler driver obtains its lock and checks IFF_DRV_RUNNING
flag. If there is no, then unlocks and returns.
- In ioctl handler driver checks for IFCAP_POLLING flag requested to
be set or cleared. Driver first calls ether_poll_[de]register(), then
obtains driver lock and [dis/en]ables interrupts.
- In interrupt handler driver checks IFCAP_POLLING flag in if_capenable.
If present, then returns.This is important to protect from spurious
interrupts.
Reviewed by: ru, sam, jhb
2005-10-01 18:56:19 +00:00
|
|
|
#ifdef DEVICE_POLLING
|
|
|
|
if (ifr->ifr_reqcap & IFCAP_POLLING &&
|
|
|
|
!(ifp->if_capenable & IFCAP_POLLING)) {
|
|
|
|
error = ether_poll_register(xl_poll, ifp);
|
|
|
|
if (error)
|
|
|
|
return(error);
|
|
|
|
XL_LOCK(sc);
|
|
|
|
/* Disable interrupts */
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
|
|
|
|
ifp->if_capenable |= IFCAP_POLLING;
|
|
|
|
XL_UNLOCK(sc);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
if (!(ifr->ifr_reqcap & IFCAP_POLLING) &&
|
|
|
|
ifp->if_capenable & IFCAP_POLLING) {
|
|
|
|
error = ether_poll_deregister(ifp);
|
|
|
|
/* Enable interrupts. */
|
|
|
|
XL_LOCK(sc);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|0xFF);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|XL_INTRS);
|
|
|
|
if (sc->xl_flags & XL_FLAG_FUNCREG)
|
|
|
|
bus_space_write_4(sc->xl_ftag, sc->xl_fhandle,
|
|
|
|
4, 0x8000);
|
|
|
|
ifp->if_capenable &= ~IFCAP_POLLING;
|
|
|
|
XL_UNLOCK(sc);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
#endif /* DEVICE_POLLING */
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK(sc);
|
2003-07-10 05:24:33 +00:00
|
|
|
ifp->if_capenable = ifr->ifr_reqcap;
|
|
|
|
if (ifp->if_capenable & IFCAP_TXCSUM)
|
|
|
|
ifp->if_hwassist = XL905B_CSUM_FEATURES;
|
|
|
|
else
|
|
|
|
ifp->if_hwassist = 0;
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_UNLOCK(sc);
|
2003-07-10 05:24:33 +00:00
|
|
|
break;
|
1998-08-16 17:14:59 +00:00
|
|
|
default:
|
2002-11-14 23:49:09 +00:00
|
|
|
error = ether_ioctl(ifp, command, data);
|
1998-08-16 17:14:59 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (error);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
2006-12-06 02:18:41 +00:00
|
|
|
static int
|
|
|
|
xl_watchdog(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
2006-12-06 02:18:41 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
u_int16_t status = 0;
|
2009-04-21 00:42:11 +00:00
|
|
|
int misintr;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2006-12-06 02:18:41 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
|
|
|
|
|
|
|
if (sc->xl_wdog_timer == 0 || --sc->xl_wdog_timer != 0)
|
|
|
|
return (0);
|
2000-10-13 17:54:19 +00:00
|
|
|
|
2009-04-21 00:42:11 +00:00
|
|
|
xl_rxeof(sc);
|
|
|
|
xl_txeoc(sc);
|
|
|
|
misintr = 0;
|
|
|
|
if (sc->xl_type == XL_TYPE_905B) {
|
|
|
|
xl_txeof_90xB(sc);
|
|
|
|
if (sc->xl_cdata.xl_tx_cnt == 0)
|
|
|
|
misintr++;
|
|
|
|
} else {
|
|
|
|
xl_txeof(sc);
|
|
|
|
if (sc->xl_cdata.xl_tx_head == NULL)
|
|
|
|
misintr++;
|
|
|
|
}
|
|
|
|
if (misintr != 0) {
|
|
|
|
device_printf(sc->xl_dev,
|
|
|
|
"watchdog timeout (missed Tx interrupts) -- recovering\n");
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
ifp->if_oerrors++;
|
|
|
|
XL_SEL_WIN(4);
|
|
|
|
status = CSR_READ_2(sc, XL_W4_MEDIA_STATUS);
|
2006-12-06 02:18:41 +00:00
|
|
|
device_printf(sc->xl_dev, "watchdog timeout\n");
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
if (status & XL_MEDIASTAT_CARRIER)
|
2006-12-06 02:18:41 +00:00
|
|
|
device_printf(sc->xl_dev,
|
|
|
|
"no carrier - transceiver cable problem?\n");
|
2004-07-05 00:15:23 +00:00
|
|
|
|
1999-08-02 21:06:16 +00:00
|
|
|
xl_reset(sc);
|
2004-07-05 02:34:35 +00:00
|
|
|
xl_init_locked(sc);
|
|
|
|
|
2004-11-08 16:16:04 +00:00
|
|
|
if (!IFQ_DRV_IS_EMPTY(&ifp->if_snd)) {
|
|
|
|
if (sc->xl_type == XL_TYPE_905B)
|
|
|
|
xl_start_90xB_locked(ifp);
|
|
|
|
else
|
|
|
|
xl_start_locked(ifp);
|
|
|
|
}
|
1998-09-25 17:34:19 +00:00
|
|
|
|
2006-12-06 02:18:41 +00:00
|
|
|
return (EJUSTRETURN);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop the adapter and free any mbufs allocated to the
|
|
|
|
* RX and TX lists.
|
|
|
|
*/
|
2002-08-24 00:02:03 +00:00
|
|
|
static void
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_stop(struct xl_softc *sc)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
|
|
|
register int i;
|
2005-06-10 16:49:24 +00:00
|
|
|
struct ifnet *ifp = sc->xl_ifp;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2004-07-05 02:34:35 +00:00
|
|
|
XL_LOCK_ASSERT(sc);
|
2000-10-13 17:54:19 +00:00
|
|
|
|
2006-12-06 02:18:41 +00:00
|
|
|
sc->xl_wdog_timer = 0;
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISABLE);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STATS_DISABLE);
|
1998-08-24 17:51:38 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB);
|
1998-08-16 17:14:59 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_DISCARD);
|
|
|
|
xl_wait(sc);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_DISABLE);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_COAX_STOP);
|
|
|
|
DELAY(800);
|
1999-09-20 00:24:11 +00:00
|
|
|
|
|
|
|
#ifdef foo
|
1998-08-16 17:14:59 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_RX_RESET);
|
|
|
|
xl_wait(sc);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_TX_RESET);
|
|
|
|
xl_wait(sc);
|
1999-02-11 23:59:29 +00:00
|
|
|
#endif
|
1999-09-20 00:24:11 +00:00
|
|
|
|
1998-08-16 17:14:59 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ACK|XL_STAT_INTLATCH);
|
1999-09-20 00:24:11 +00:00
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_STAT_ENB|0);
|
|
|
|
CSR_WRITE_2(sc, XL_COMMAND, XL_CMD_INTR_ENB|0);
|
2004-07-04 22:20:52 +00:00
|
|
|
if (sc->xl_flags & XL_FLAG_FUNCREG)
|
|
|
|
bus_space_write_4(sc->xl_ftag, sc->xl_fhandle, 4, 0x8000);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/* Stop the stats updater. */
|
2005-08-18 19:24:30 +00:00
|
|
|
callout_stop(&sc->xl_stat_callout);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Free data in the RX lists.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < XL_RX_LIST_CNT; i++) {
|
|
|
|
if (sc->xl_cdata.xl_rx_chain[i].xl_mbuf != NULL) {
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_unload(sc->xl_mtag,
|
2003-01-19 00:23:59 +00:00
|
|
|
sc->xl_cdata.xl_rx_chain[i].xl_map);
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_destroy(sc->xl_mtag,
|
2003-01-19 00:23:59 +00:00
|
|
|
sc->xl_cdata.xl_rx_chain[i].xl_map);
|
1998-08-16 17:14:59 +00:00
|
|
|
m_freem(sc->xl_cdata.xl_rx_chain[i].xl_mbuf);
|
|
|
|
sc->xl_cdata.xl_rx_chain[i].xl_mbuf = NULL;
|
|
|
|
}
|
|
|
|
}
|
2004-08-06 09:08:33 +00:00
|
|
|
if (sc->xl_ldata.xl_rx_list != NULL)
|
|
|
|
bzero(sc->xl_ldata.xl_rx_list, XL_RX_LIST_SZ);
|
1998-08-16 17:14:59 +00:00
|
|
|
/*
|
|
|
|
* Free the TX list buffers.
|
|
|
|
*/
|
|
|
|
for (i = 0; i < XL_TX_LIST_CNT; i++) {
|
|
|
|
if (sc->xl_cdata.xl_tx_chain[i].xl_mbuf != NULL) {
|
2002-12-17 00:08:49 +00:00
|
|
|
bus_dmamap_unload(sc->xl_mtag,
|
|
|
|
sc->xl_cdata.xl_tx_chain[i].xl_map);
|
|
|
|
bus_dmamap_destroy(sc->xl_mtag,
|
|
|
|
sc->xl_cdata.xl_tx_chain[i].xl_map);
|
1998-08-16 17:14:59 +00:00
|
|
|
m_freem(sc->xl_cdata.xl_tx_chain[i].xl_mbuf);
|
|
|
|
sc->xl_cdata.xl_tx_chain[i].xl_mbuf = NULL;
|
|
|
|
}
|
|
|
|
}
|
2004-08-06 09:08:33 +00:00
|
|
|
if (sc->xl_ldata.xl_tx_list != NULL)
|
|
|
|
bzero(sc->xl_ldata.xl_tx_list, XL_TX_LIST_SZ);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2005-08-09 10:20:02 +00:00
|
|
|
ifp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Stop all chip I/O so that the kernel's probe routines don't
|
|
|
|
* get confused by errant DMAs when rebooting.
|
|
|
|
*/
|
2007-11-22 02:45:00 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_shutdown(device_t dev)
|
1998-08-16 17:14:59 +00:00
|
|
|
{
|
1999-07-20 21:23:17 +00:00
|
|
|
struct xl_softc *sc;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
1998-08-16 17:14:59 +00:00
|
|
|
|
2000-10-13 17:54:19 +00:00
|
|
|
XL_LOCK(sc);
|
1999-09-20 00:24:11 +00:00
|
|
|
xl_reset(sc);
|
1998-08-16 17:14:59 +00:00
|
|
|
xl_stop(sc);
|
2000-10-13 17:54:19 +00:00
|
|
|
XL_UNLOCK(sc);
|
2007-11-22 02:45:00 +00:00
|
|
|
|
|
|
|
return (0);
|
1998-08-16 17:14:59 +00:00
|
|
|
}
|
2001-12-05 10:34:07 +00:00
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_suspend(device_t dev)
|
2001-12-05 10:34:07 +00:00
|
|
|
{
|
|
|
|
struct xl_softc *sc;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
|
|
|
XL_LOCK(sc);
|
|
|
|
xl_stop(sc);
|
|
|
|
XL_UNLOCK(sc);
|
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
2001-12-05 10:34:07 +00:00
|
|
|
}
|
|
|
|
|
2002-08-24 00:02:03 +00:00
|
|
|
static int
|
2004-07-04 23:31:28 +00:00
|
|
|
xl_resume(device_t dev)
|
2001-12-05 10:34:07 +00:00
|
|
|
{
|
|
|
|
struct xl_softc *sc;
|
|
|
|
struct ifnet *ifp;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
2005-06-10 16:49:24 +00:00
|
|
|
ifp = sc->xl_ifp;
|
2001-12-05 10:34:07 +00:00
|
|
|
|
2004-07-04 23:31:28 +00:00
|
|
|
XL_LOCK(sc);
|
|
|
|
|
2001-12-05 10:34:07 +00:00
|
|
|
xl_reset(sc);
|
|
|
|
if (ifp->if_flags & IFF_UP)
|
2004-07-05 02:34:35 +00:00
|
|
|
xl_init_locked(sc);
|
2001-12-05 10:34:07 +00:00
|
|
|
|
|
|
|
XL_UNLOCK(sc);
|
2004-07-04 23:31:28 +00:00
|
|
|
|
2004-07-04 22:20:52 +00:00
|
|
|
return (0);
|
2001-12-05 10:34:07 +00:00
|
|
|
}
|