aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDavid E. O'Brien <obrien@FreeBSD.org>1999-05-14 01:50:10 +0000
committerDavid E. O'Brien <obrien@FreeBSD.org>1999-05-14 01:50:10 +0000
commit99872c353cae59a20abe23594697b93b730034cc (patch)
tree4bc3346cdc5183caffc41d4095aef2854e3055d9
parent32597e933558bd1e7ccbd115ae74e6b9e7d77ced (diff)
downloadsrc-99872c353cae59a20abe23594697b93b730034cc.tar.gz
src-99872c353cae59a20abe23594697b93b730034cc.zip
Virgin import of Xircom PCCARD driver v1.14vendor/xe/1.14
This driver is mostly based on the `xirc2ps' driver for Linux by Werner Koch. Werner has even allowed his code to be distributed under a BSD licence, making our life considerably easier -- thanks Werner! This driver supports: * Intel EtherExpress(TM) PRO/100 PCCARD (16-bit version) * Xircom CreditCard CE2 / CEM28 / CEM33 / CE3 / CEM56 Ethernet adapters. * Toshiba Advanced Network 10/100 PCCARD * Certain Compaq Netelligent 10/100 branded cards v1.14 has major changes to media selection code, and bugfixes in the probe routine. Developed by: Scott Mitchell <scott@uk.freebsd.org> Obtained from: http://www.freebsd-uk.eu.org/~scott/xe_drv/
Notes
Notes: svn path=/vendor-sys/xe/dist/; revision=47136 svn path=/vendor-sys/xe/1.14/; revision=47138; tag=vendor/xe/1.14
-rw-r--r--sys/dev/pccard/if_xe.c2530
-rw-r--r--sys/dev/pccard/if_xereg.h146
2 files changed, 1373 insertions, 1303 deletions
diff --git a/sys/dev/pccard/if_xe.c b/sys/dev/pccard/if_xe.c
index 6a25e8c16041..03c57093494d 100644
--- a/sys/dev/pccard/if_xe.c
+++ b/sys/dev/pccard/if_xe.c
@@ -23,7 +23,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: if_xe.c,v 1.13 1999/01/24 22:14:54 root Exp $
+ * $Id: if_xe.c,v 1.14 1999/02/22 14:16:14 root Exp $
*/
/*
@@ -68,11 +68,20 @@
* CEM33 ditto
* CE3 10BASE-T, 100BASE-TX
* CEM56 ditto
- * Certain Intel and Compaq branded cards are also rumoured to work.
+ * RealPort ditto
+ * Some Compaq and Intel cards using the Xircom hardware are also known to
+ * work. See the website for full details of supported hardware.
*
- * <Acknowledgements>
+ * <Acknowledgements to go here>
*
- * <Contact details>
+ * The web page for the driver is located at:
+ * http://www.dcs.qmw.ac.uk/~scott/xe_drv/
+ * Check here first for new releases, bug fixes, etc.
+ *
+ * Please send all feedback, bug reports, patches, etc. to
+ * <scott@dcs.qmw.ac.uk>. Mail with the tag "[XE]" in the subject line will
+ * be automatically filed in an appropriate folder where I'm more likely to
+ * see it and respond promptly.
*/
#define XE_DEBUG 1
@@ -130,30 +139,46 @@
struct xe_softc {
struct arpcom arpcom;
struct ifmedia ifmedia;
+ struct callout_handle chand;
+ struct ifmib_iso_8802_3 mibdata;
struct isa_device *dev;
struct pccard_devinfo *crd;
- struct ifmib_iso_8802_3 mibdata;
-#if NAPM > 0
- struct apmhook suspend_hook;
- struct apmhook resume_hook;
-#endif /* NAPM > 0 */
+ struct ifnet *ifp;
+ struct ifmedia *ifm;
char *card_type; /* Card model name */
char *vendor; /* Card manufacturer */
+ int unit; /* Unit number, from dev->id_unit */
int srev; /* Silicon revision */
- int modem; /* 1 = Multifunction card with modem */
- int ce3; /* 1 = CE3 class (100Mbit) adapter */
- int cem56; /* 1 = CEM56 class (CE3 + 56Kbps modem) adapter */
- int gone; /* 1 = Card bailed out */
int tx_queued; /* Packets currently waiting to transmit */
int tx_ptr; /* Last value of PTR reg on card */
int tx_collisions; /* Collisions since last successful send */
int tx_timeouts; /* Count of transmit timeouts */
- int probe; /* XXX 0 = use value in port, 1 = look for a port */
- int port; /* XXX 0 = Unknown, 1 = 10Base2, 2 = 10BaseT, 4 = 100BaseTX */
+ int autoneg_status; /* Autonegotiation progress state */
+ int media; /* Private media word */
+ u_char modem; /* 1 = Multifunction card with modem */
+ u_char ce3; /* 1 = CE3 class (100Mbit) adapter */
+ u_char cem56; /* 1 = CEM56 class (CE3 + 56Kbps modem) adapter */
+ u_char phy_ok; /* 1 = MII-compliant PHY found and initialised */
+ u_char gone; /* 1 = Card bailed out */
+#if NAPM > 0
+ struct apmhook suspend_hook;
+ struct apmhook resume_hook;
+#endif /* NAPM > 0 */
};
static struct xe_softc *sca[MAXSLOT];
+/*
+ * MII command structure
+ */
+struct xe_mii_frame {
+ u_int8_t mii_stdelim;
+ u_int8_t mii_opcode;
+ u_int8_t mii_phyaddr;
+ u_int8_t mii_regaddr;
+ u_int8_t mii_turnaround;
+ u_int16_t mii_data;
+};
/*
* For accessing card registers
@@ -164,66 +189,79 @@ static struct xe_softc *sca[MAXSLOT];
#define XE_OUTW(r, w) outw(scp->dev->id_iobase+(r), (w))
#define XE_SELECT_PAGE(p) XE_OUTB(XE_PSR, (p))
-
/*
- * PC-Card driver routines
+ * Horrid stuff for accessing CIS tuples
*/
-static int xe_card_init (struct pccard_devinfo *devi);
-static void xe_card_unload (struct pccard_devinfo *devi);
-static int xe_card_intr (struct pccard_devinfo *devi);
+#define CARD_MAJOR 50
+#define CISTPL_BUFSIZE 512
+#define CISTPL_TYPE(tpl) tpl[0]
+#define CISTPL_LEN(tpl) tpl[2]
+#define CISTPL_DATA(tpl,pos) tpl[4 + ((pos)<<1)]
/*
- * isa_driver member functions
+ * Media autonegotiation progress constants
*/
-static int xe_probe (struct isa_device *dev);
-static int xe_attach (struct isa_device *dev);
+#define XE_AUTONEG_NONE 0 /* No autonegotiation in progress */
+#define XE_AUTONEG_WAITING 1 /* Waiting for transmitter to go idle */
+#define XE_AUTONEG_STARTED 2 /* Waiting for autonegotiation to complete */
+#define XE_AUTONEG_100TX 3 /* Trying to force 100baseTX link */
+#define XE_AUTONEG_FAIL 4 /* Autonegotiation failed */
-/*
- * ifnet member functions
- */
-static void xe_init (void *xscp);
-static void xe_start (struct ifnet *ifp);
-static int xe_ioctl (struct ifnet *ifp, u_long command, caddr_t data);
-static void xe_watchdog (struct ifnet *ifp);
/*
- * Other random functions
+ * Prototypes start here
*/
-static void xe_stop (struct xe_softc *scp);
-static void xe_reset (struct xe_softc *scp);
-static void xe_setmulti (struct xe_softc *scp);
-static void xe_setaddrs (struct xe_softc *scp);
-static int xe_pio_write_packet (struct xe_softc *scp, struct mbuf *mbp);
-#ifdef XE_DEBUG
-static void xe_reg_dump (struct xe_softc *scp);
-#endif
+static int xe_probe (struct isa_device *dev);
+static int xe_card_init (struct pccard_devinfo *devi);
+static int xe_attach (struct isa_device *dev);
+static void xe_init (void *xscp);
+static void xe_start (struct ifnet *ifp);
+static int xe_ioctl (struct ifnet *ifp, u_long command, caddr_t data);
+static int xe_card_intr (struct pccard_devinfo *devi);
+static void xe_watchdog (struct ifnet *ifp);
+static int xe_media_change (struct ifnet *ifp);
+static void xe_media_status (struct ifnet *ifp, struct ifmediareq *mrp);
+static timeout_t xe_setmedia;
+static void xe_hard_reset (struct xe_softc *scp);
+static void xe_soft_reset (struct xe_softc *scp);
+static void xe_stop (struct xe_softc *scp);
+static void xe_enable_intr (struct xe_softc *scp);
+static void xe_disable_intr (struct xe_softc *scp);
+static void xe_setmulti (struct xe_softc *scp);
+static void xe_setaddrs (struct xe_softc *scp);
+static int xe_pio_write_packet (struct xe_softc *scp, struct mbuf *mbp);
+static void xe_card_unload (struct pccard_devinfo *devi);
/*
- * Media selection functions
+ * MII functions
*/
-static int xe_media_change (struct ifnet *ifp);
-static void xe_media_status (struct ifnet *ifp, struct ifmediareq *ifm);
+static void xe_mii_sync (struct xe_softc *scp);
+static int xe_mii_init (struct xe_softc *scp);
+static void xe_mii_send (struct xe_softc *scp, u_int32_t bits, int cnt);
+static int xe_mii_readreg (struct xe_softc *scp, struct xe_mii_frame *frame);
+static int xe_mii_writereg (struct xe_softc *scp, struct xe_mii_frame *frame);
+static u_int16_t xe_phy_readreg (struct xe_softc *scp, u_int16_t reg);
+static void xe_phy_writereg (struct xe_softc *scp, u_int16_t reg, u_int16_t data);
/*
- * MII (Medium Independent Interface) functions
+ * Debug functions
*/
-static void xe_mii_clock (struct xe_softc *scp);
-static u_int16_t xe_mii_getbit (struct xe_softc *scp);
-static void xe_mii_putbit (struct xe_softc *scp, u_int16_t data);
-static void xe_mii_putbits (struct xe_softc *scp, u_int16_t data, int len);
-static u_int16_t xe_mii_read (struct xe_softc *scp, u_int8_t phy, u_int8_t reg);
-static void xe_mii_write (struct xe_softc *scp, u_int8_t phy, u_int8_t reg, u_int16_t data, int len);
-static int xe_mii_init (struct xe_softc *scp);
#ifdef XE_DEBUG
-static void xe_mii_dump (struct xe_softc *scp);
+#define XE_REG_DUMP(scp) xe_reg_dump((scp))
+#define XE_MII_DUMP(scp) xe_mii_dump((scp))
+static void xe_reg_dump (struct xe_softc *scp);
+static void xe_mii_dump (struct xe_softc *scp);
+#else
+#define XE_REG_DUMP(scp)
+#define XE_MII_DUMP(scp)
#endif
#if NAPM > 0
/*
* APM hook functions
*/
-static int xe_suspend (void *xunit);
-static int xe_resume (void *xunit);
+static int xe_suspend (void *xunit);
+static int xe_resume (void *xunit);
#endif /* NAPM > 0 */
@@ -241,9 +279,9 @@ static struct pccard_device xe_info = {
DATA_SET(pccarddrv_set, xe_info);
-
/*
- * ISA driver hooks
+ * ISA driver hooks. I'd like to do without these but the kernel config stuff
+ * seems to require them.
*/
struct isa_driver xedriver = {
xe_probe,
@@ -254,6 +292,7 @@ struct isa_driver xedriver = {
/*
+ * ISA probe routine.
* All of the supported devices are PCMCIA cards. I have no idea if it's even
* possible to successfully probe/attach these at boot time (pccardd normally
* does a lot of setup work) so I don't even bother trying.
@@ -264,198 +303,367 @@ xe_probe (struct isa_device *dev) {
return 0;
}
-
+
/*
- * Attach a device (called when xe_card_init succeeds). Assume that the probe
- * routine has set up the softc structure correctly and that we can trust the
- * unit number.
+ * PCMCIA probe routine.
+ * Probe and identify the device. Called by the slot manager when the card is
+ * inserted or the machine wakes up from suspend mode. Assmes that the slot
+ * structure has been initialised already.
*/
static int
-xe_attach (struct isa_device *dev)
+xe_card_init(struct pccard_devinfo *devi)
{
- struct ifnet *ifp;
struct xe_softc *scp;
- int unit, i;
+ struct isa_device *dev;
+ struct uio uios;
+ struct iovec iov;
+ u_char buf[CISTPL_BUFSIZE];
+ u_char ver_str[CISTPL_BUFSIZE>>1];
+ off_t offs;
+ int unit, success, rc, i;
- unit = dev->id_unit;
+ unit = devi->isahd.id_unit;
scp = sca[unit];
- ifp = &(scp->arpcom.ac_if);
+ dev = &devi->isahd;
+ success = 0;
- /*
- * Power down the interface
- */
- xe_stop(scp);
+#ifdef XE_DEBUG
+ printf("xe: Probing for unit %d\n", unit);
+#endif
- /*
- * Initialise the ifnet structure
- */
- if (!ifp->if_name) {
- ifp->if_softc = scp;
- ifp->if_name = "xe";
- ifp->if_unit = unit;
- ifp->if_timer = 0;
- ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
- ifp->if_linkmib = &scp->mibdata;
- ifp->if_linkmiblen = sizeof scp->mibdata;
- ifp->if_output = ether_output;
- ifp->if_start = xe_start;
- ifp->if_ioctl = xe_ioctl;
- ifp->if_watchdog = xe_watchdog;
- ifp->if_init = xe_init;
- ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
-
- if_attach(ifp);
- ether_ifattach(ifp);
+ /* Check that unit number is OK */
+ if (unit > MAXSLOT) {
+ printf("xe: bad unit (%d)\n", unit);
+ return (ENODEV);
}
-#if NBPFILTER > 0
- /*
- * If BPF is in the kernel, call the attach for it
- */
- bpfattach(ifp, DLT_EN10MB, sizeof(struct ether_header));
+ /* Don't attach an active device */
+ if (scp && !scp->gone) {
+ printf("xe: unit already attached (%d)\n", unit);
+ return (EBUSY);
+ }
+
+ /* Allocate per-instance storage */
+ if (!scp) {
+ if ((scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT)) == NULL) {
+ printf("xe%d: failed to allocage driver storage\n", unit);
+ return (ENOMEM);
+ }
+ bzero(scp, sizeof(*scp));
+ }
+
+ /* Re-attach an existing device */
+ if (scp->gone) {
+ scp->gone = 0;
+ return 0;
+ }
+
+ /* Grep through CIS looking for relevant tuples */
+ offs = 0;
+ do {
+ u_int16_t vendor;
+ u_int8_t rev, media, prod;
+
+ iov.iov_base = buf;
+ iov.iov_len = CISTPL_BUFSIZE;
+ uios.uio_iov = &iov;
+ uios.uio_iovcnt = 1;
+ uios.uio_offset = offs;
+ uios.uio_resid = CISTPL_BUFSIZE;
+ uios.uio_segflg = UIO_SYSSPACE;
+ uios.uio_rw = UIO_READ;
+ uios.uio_procp = 0;
+
+ /*
+ * Read tuples one at a time into buf. Sucks, but it only happens once.
+ * XXX - If the stuff we need isn't in attribute memory, or (worse yet)
+ * XXX - attribute memory isn't mapped, we're FUBAR. Maybe need to do an
+ * XXX - ioctl on the card device and follow links?
+ */
+ if ((rc = cdevsw[CARD_MAJOR]->d_read(makedev(CARD_MAJOR, devi->slt->slotnum), &uios, 0)) == 0) {
+
+ switch (CISTPL_TYPE(buf)) {
+
+ case 0x15: /* Grab version string (needed to ID some weird CE2's) */
+#ifdef XE_DEBUG
+ printf("xe%d: Got version string (0x15)\n", unit);
+#endif
+ for (i = 0; i < CISTPL_LEN(buf); ver_str[i] = CISTPL_DATA(buf, i++));
+ ver_str[i] = '\0';
+ ver_str[CISTPL_BUFSIZE>>1 - 1] = CISTPL_LEN(buf);
+ success++;
+ break;
+
+ case 0x20: /* Figure out what type of card we have */
+#ifdef XE_DEBUG
+ printf("xe%d: Got card ID (0x20)\n", unit);
+#endif
+ vendor = CISTPL_DATA(buf, 0) + (CISTPL_DATA(buf, 1) << 8);
+ rev = CISTPL_DATA(buf, 2);
+ media = CISTPL_DATA(buf, 3);
+ prod = CISTPL_DATA(buf, 4);
+
+ switch (vendor) { /* Get vendor ID */
+ case 0x0105:
+ scp->vendor = "Xircom"; break;
+ case 0x0138:
+ case 0x0183:
+ scp->vendor = "Compaq"; break;
+ case 0x0089:
+ scp->vendor = "Intel"; break;
+ default:
+ scp->vendor = "Unknown";
+ }
+
+ if (!((prod & 0x40) && (media & 0x01))) {
+#ifdef XE_DEBUG
+ printf("xe%d: Not a PCMCIA Ethernet card!\n", unit);
+#endif
+ rc = ENODEV; /* Not a PCMCIA Ethernet device */
+ }
+ else {
+ if (media & 0x10) { /* Ethernet/modem cards */
+#ifdef XE_DEBUG
+ printf("xe%d: Card is Ethernet/modem combo\n", unit);
+#endif
+ scp->modem = 1;
+ switch (prod & 0x0f) {
+ case 1:
+ scp->card_type = "CEM"; break;
+ case 2:
+ scp->card_type = "CEM2"; break;
+ case 3:
+ scp->card_type = "CEM3"; break;
+ case 4:
+ scp->card_type = "CEM33"; break;
+ case 5:
+ scp->ce3 = 1;
+ scp->card_type = "CEM56M"; break;
+ case 6:
+ case 7: /* Some kind of RealPort card */
+ scp->ce3 = 1;
+ scp->cem56 = 1;
+ scp->card_type = "CEM56"; break;
+ default:
+ rc = ENODEV;
+ }
+ }
+ else { /* Ethernet-only cards */
+#ifdef XE_DEBUG
+ printf("xe%d: Card is Ethernet only\n", unit);
+#endif
+ switch (prod & 0x0f) {
+ case 1:
+ scp->card_type = "CE"; break;
+ case 2:
+ scp->card_type = "CE2"; break;
+ case 3:
+ scp->ce3 = 1;
+ scp->card_type = "CE3"; break;
+ default:
+ rc = ENODEV;
+ }
+ }
+ }
+ success++;
+ break;
+
+ case 0x22: /* Get MAC address */
+#ifdef XE_DEBUG
+ printf("xe%d: Got MAC address (0x22)\n", unit);
+#endif
+ if ((CISTPL_LEN(buf) == 8) &&
+ (CISTPL_DATA(buf, 0) == 0x04) &&
+ (CISTPL_DATA(buf, 1) == ETHER_ADDR_LEN)) {
+ for (i = 0; i < ETHER_ADDR_LEN; scp->arpcom.ac_enaddr[i] = CISTPL_DATA(buf, i+2), i++);
+ }
+ success++;
+ break;
+ default:
+ }
+ }
+
+ /* Skip to next tuple */
+ offs += ((CISTPL_LEN(buf) + 2) << 1);
+
+ } while ((CISTPL_TYPE(buf) != 0xff) && (CISTPL_LEN(buf) != 0xff) && (rc == 0));
+
+
+ /* Die now if something went wrong above */
+ if ((rc != 0) || (success < 3)) {
+ free(scp, M_DEVBUF);
+ return rc;
+ }
+
+ /* Check for certain strange CE2's that look like CE's */
+ if (strcmp(scp->card_type, "CE") == 0) {
+ u_char *str = ver_str;
+#ifdef XE_DEBUG
+ printf("xe%d: Checking for weird CE2 string\n", unit);
#endif
+ str += strlen(str) + 1; /* Skip forward to 3rd version string */
+ str += strlen(str) + 1;
+ str += strlen(str) + 1;
+ for (i = 0; i < strlen(str) - 2; i++) {
+ if (bcmp(&str[i], "CE2", 3) ==0) { /* Look for "CE2" string */
+ scp->card_type = "CE2";
+ }
+ }
+ }
+
+ /* Reject unsupported cards */
+ if (strcmp(scp->card_type, "CE") == 0 || strcmp(scp->card_type, "CEM") == 0) {
+ printf("xe%d: Sorry, your %s card is not supported :(\n", unit, scp->card_type);
+ free(scp, M_DEVBUF);
+ return ENODEV;
+ }
+
+ /* Fill in some private data */
+ sca[unit] = scp;
+ scp->dev = &devi->isahd;
+ scp->crd = devi;
+ scp->ifp = &scp->arpcom.ac_if;
+ scp->ifm = &scp->ifmedia;
+ scp->unit = unit;
+ scp->autoneg_status = 0;
+
+ /* Attempt to attach the device */
+ if (!xe_attach(scp->dev)) {
+ sca[unit] = 0;
+ free(scp, M_DEVBUF);
+ return ENXIO;
+ }
+
+#if NAPM > 0
+ /* Establish APM hooks once device attached */
+ scp->suspend_hook.ah_name = "xe_suspend";
+ scp->suspend_hook.ah_fun = xe_suspend;
+ scp->suspend_hook.ah_arg = (void *)unit;
+ scp->suspend_hook.ah_order = APM_MIN_ORDER;
+ apm_hook_establish(APM_HOOK_SUSPEND, &scp->suspend_hook);
+ scp->resume_hook.ah_name = "xe_resume";
+ scp->resume_hook.ah_fun = xe_resume;
+ scp->resume_hook.ah_arg = (void *)unit;
+ scp->resume_hook.ah_order = APM_MIN_ORDER;
+ apm_hook_establish(APM_HOOK_RESUME, &scp->resume_hook);
+#endif /* NAPM > 0 */
+
+ /* Success */
+ return 0;
+}
+
+
+/*
+ * Attach a device (called when xe_card_init succeeds). Assume that the probe
+ * routine has set up the softc structure correctly and that we can trust the
+ * unit number.
+ */
+static int
+xe_attach (struct isa_device *dev) {
+ struct xe_softc *scp = sca[dev->id_unit];
+ int i;
+
+#ifdef XE_DEBUG
+ printf("xe%d: attach\n", scp->unit);
+#endif
+
+ /* Initialise the ifnet structure */
+ if (!scp->ifp->if_name) {
+ scp->ifp->if_softc = scp;
+ scp->ifp->if_name = "xe";
+ scp->ifp->if_unit = scp->unit;
+ scp->ifp->if_timer = 0;
+ scp->ifp->if_flags = (IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST);
+ scp->ifp->if_linkmib = &scp->mibdata;
+ scp->ifp->if_linkmiblen = sizeof scp->mibdata;
+ scp->ifp->if_output = ether_output;
+ scp->ifp->if_start = xe_start;
+ scp->ifp->if_ioctl = xe_ioctl;
+ scp->ifp->if_watchdog = xe_watchdog;
+ scp->ifp->if_init = xe_init;
+ scp->ifp->if_snd.ifq_maxlen = IFQ_MAXLEN;
+ }
+
+ /* Initialise the ifmedia structure */
+ ifmedia_init(scp->ifm, 0, xe_media_change, xe_media_status);
+ callout_handle_init(&scp->chand);
/*
- * Print some useful information
+ * Fill in supported media types. Some cards _do_ support full duplex
+ * operation, but this driver doesn't, yet. Therefore we leave those modes
+ * out of the list. We support some form of autoselection in all cases.
*/
+ if (scp->ce3) {
+ ifmedia_add(scp->ifm, IFM_ETHER|IFM_100_TX, 0, NULL);
+ ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T, 0, NULL);
+ }
+ else {
+ ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_T, 0, NULL);
+ ifmedia_add(scp->ifm, IFM_ETHER|IFM_10_2, 0, NULL);
+ }
+ ifmedia_add(scp->ifm, IFM_ETHER|IFM_AUTO, 0, NULL);
+
+ /* Default is to autoselect best supported media type */
+ ifmedia_set(scp->ifm, IFM_ETHER|IFM_AUTO);
+
+ /* Print some useful information */
printf("\nxe%d: %s %s%s%s\n",
- unit,
+ scp->unit,
scp->vendor,
scp->card_type,
scp->ce3 ? ", 100Mbps capable" : "",
scp->cem56 ? ", with modem" : "");
- printf("xe%d: Ethernet address %02x", unit, scp->arpcom.ac_enaddr[0]);
+ printf("xe%d: Ethernet address %02x", scp->unit, scp->arpcom.ac_enaddr[0]);
for (i = 1; i < ETHER_ADDR_LEN; i++) {
printf(":%02x", scp->arpcom.ac_enaddr[i]);
}
printf("\n");
- return 1;
-}
+ /* Attach the interface */
+ if_attach(scp->ifp);
+ ether_ifattach(scp->ifp);
+#if NBPFILTER > 0
+ /* If BPF is in the kernel, call the attach for it */
+#ifdef XE_DEBUG
+ printf("xe%d: BPF listener attached\n", scp->unit);
+#endif
+ bpfattach(scp->ifp, DLT_EN10MB, sizeof(struct ether_header));
+#endif
-/*
- * Interrupt service routine. This shouldn't ever get called, but if it does
- * we just call the card interrupt handler with the appropriate arguments.
- */
-void
-xeintr(int unit) {
- xe_card_intr(sca[unit]->crd);
+ /* Done */
+ return 1;
}
/*
- * Initialize device. Except for the media selection stuff this is pretty
- * much verbatim from the Linux code.
+ * Initialize device. Completes the reset procedure on the card and starts
+ * output. If there's an autonegotiation in progress we DON'T do anything;
+ * the media selection code will call us again when it's done.
*/
static void
xe_init(void *xscp) {
- struct xe_softc *scp;
- struct ifnet *ifp;
- int unit, s;
-
- scp = xscp;
- ifp = &scp->arpcom.ac_if;
- unit = scp->crd->isahd.id_unit;
+ struct xe_softc *scp = xscp;
+ int s;
- if (scp->gone)
- return;
+#ifdef XE_DEBUG
+ printf("xe%d: init\n", scp->unit);
+#endif
- if (TAILQ_EMPTY(&ifp->if_addrhead))
- return;
+ if (scp->gone) return;
- s = splimp();
+ if (TAILQ_EMPTY(&scp->ifp->if_addrhead)) return;
- /*
- * Reset transmitter flags
- */
+ /* Reset transmitter flags */
scp->tx_queued = 0;
scp->tx_ptr = 0;
scp->tx_collisions = 0;
- ifp->if_timer = 0;
-
- /*
- * Hard, then soft, reset the card.
- */
- XE_SELECT_PAGE(4);
- DELAY(1);
- XE_OUTB(XE_GPR1, 0); /* Power off */
- DELAY(40000);
- if (scp->ce3)
- XE_OUTB(XE_GPR1, 1); /* And back on again */
- else
- XE_OUTB(XE_GPR1, 5); /* Also set AIC bit, whatever that is */
- DELAY(40000);
- XE_OUTB(XE_CR, XE_CR_SOFT_RESET); /* Software reset */
- DELAY(40000);
- XE_OUTB(XE_CR, 0);
- DELAY(40000);
+ scp->ifp->if_timer = 0;
- if (scp->ce3) {
- /*
- * set GP1 and GP2 as outputs (bits 2 & 3)
- * set GP1 low to power on the ML6692 (bit 0)
- * set GP2 high to power on the 10Mhz chip (bit 1)
- */
- XE_SELECT_PAGE(4);
- XE_OUTB(XE_GPR0, 0x0e);
- }
-
- /*
- * Wait for everything to wake up.
- */
- DELAY(500000);
-
- /*
- * Get silicon revision number
- */
- if (scp->ce3)
- scp->srev = (XE_INB(XE_BOV) & 0x70) >> 4;
- else
- scp->srev = (XE_INB(XE_BOV) & 0x30) >> 4;
-#ifdef XE_DEBUG
- printf("xe%d: silicon revision %d\n", unit, scp->srev);
-#endif
-
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
- if (scp->probe) {
- if (!scp->ce3) {
- XE_SELECT_PAGE(4);
- XE_OUTB(XE_GPR0, 4);
- scp->probe = 0;
- }
- }
- else if (scp->port == 2) {
- /* select 10BaseT */
-#ifdef XE_DEBUG
- printf("xe%d: selecting 10BaseT\n", unit);
-#endif
- XE_SELECT_PAGE(0x42);
- XE_OUTB(XE_SWC1, 0x80);
- }
- else if (scp->port == 1) {
- /* select 10Base2 */
-#ifdef XE_DEBUG
- printf("xe%d: selecting 10Base2\n", unit);
-#endif
- XE_SELECT_PAGE(0x42);
- XE_OUTB(XE_SWC1, 0xc0);
- }
- DELAY(40000);
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
+ s = splimp();
- /*
- * Setup the ECR
- */
- XE_SELECT_PAGE(1);
- XE_OUTB(XE_IMR0, 0xff); /* Allow all interrupts */
- XE_OUTB(XE_IMR1, 0x01); /* Enable Tx underrun detection */
XE_SELECT_PAGE(0x42);
- XE_OUTB(XE_SWC0, 0x20); /* Disable source insertion (WTF is that?) */
+ XE_OUTB(XE_SWC0, 0x20); /* Disable source insertion (WTF is that?) */
/*
* Set the 'local memory dividing line' -- splits the 32K card memory into
@@ -467,14 +675,10 @@ xe_init(void *xscp) {
XE_OUTW(XE_RBS, 0x2000);
}
- /*
- * Set up multicast addresses
- */
+ /* Set up multicast addresses */
xe_setmulti(scp);
- /*
- * Fix the data offset register -- reset leaves it off-by-one
- */
+ /* Fix the data offset register -- reset leaves it off-by-one */
XE_SELECT_PAGE(0);
XE_OUTW(XE_DOR, 0x2000);
@@ -490,93 +694,28 @@ xe_init(void *xscp) {
XE_OUTB(XE_TXS0, 0x00); /* TOK, TAB, SQE, LL, TU, JAB, EXC, CRS */
XE_OUTB(XE_TXS1, 0x00); /* TEN, rsv, PTD, EXT, retry_counter:4 */
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
- if (scp->ce3 && xe_mii_init(scp)) {
- u_char val;
-
-#ifdef XE_DEBUG
- printf("xe%d: found an MII\n", unit);
-#endif
- if ((scp->port == 4) || scp->cem56) {
- /* use MII */
-#ifdef XE_DEBUG
- printf("xe%d: using MII\n", unit);
-#endif
- XE_SELECT_PAGE(2);
- val = XE_INB(XE_MSR);
- val |= 0x08;
- XE_OUTB(XE_MSR, val);
- DELAY(20000);
- }
- else {
- XE_SELECT_PAGE(0x42);
- if (scp->port == 2) {
- /* enable 10BaseT */
-#ifdef XE_DEBUG
- printf("xe%d: selecting 10BaseT\n", unit);
-#endif
- XE_OUTB(XE_SWC1, 0x80);
- }
- else {
- /* enable 10Base2 */
-#ifdef XE_DEBUG
- printf("xe%d: selecting 10Base2\n", unit);
-#endif
- XE_OUTB(XE_SWC1, 0xc0);
- }
- DELAY(40000);
- }
- }
- else {
- XE_SELECT_PAGE(0);
- scp->port = (XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ? 2 : 1;
- }
-
/*
- * Configure the LEDs
+ * Check for an in-progress autonegotiation. If one is active, just set
+ * IFF_RUNNING and return. The media selection code will call us again when
+ * it's done.
*/
- XE_SELECT_PAGE(2);
- if (scp->port > 1) {
- XE_OUTB(XE_LED, 0x3b); /* For TP: link and activity */
+ if (scp->autoneg_status) {
+ scp->ifp->if_flags |= IFF_RUNNING;
}
else {
- XE_OUTB(XE_LED, 0x3a); /* For BNC: !collision and activity */
- }
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
- /***** XXX XXX XXX XXX *****/
+ /* Enable receiver, put MAC online */
+ XE_SELECT_PAGE(0x40);
+ XE_OUTB(XE_OCR, XE_OCR_RX_ENABLE|XE_OCR_ONLINE);
- /*
- * Enable receiver, put MAC online
- */
- XE_SELECT_PAGE(0x40);
- XE_OUTB(XE_OCR, XE_OCR_RX_ENABLE|XE_OCR_ONLINE);
+ /* Set up IMR, enable interrupts */
+ xe_enable_intr(scp);
- /*
- * Set up IMR, enable interrupts
- */
- XE_SELECT_PAGE(1);
- XE_OUTB(XE_IMR0, 0xff); /* Enable everything */
- DELAY(1);
- XE_SELECT_PAGE(0);
- XE_OUTB(XE_CR, XE_CR_ENABLE_INTR);
- if (scp->modem && !scp->cem56) { /* This bit is just magic */
- if (!(XE_INB(0x10) & 0x01)) {
- XE_OUTB(0x10, 0x11); /* Unmask master int enable bit */
- }
+ /* Attempt to start output */
+ scp->ifp->if_flags |= IFF_RUNNING;
+ scp->ifp->if_flags &= ~IFF_OACTIVE;
+ xe_start(scp->ifp);
}
- XE_SELECT_PAGE(0);
-
- /*
- * Attempt to start output
- */
- ifp->if_flags |= IFF_RUNNING;
- ifp->if_flags &= ~IFF_OACTIVE;
- xe_start(ifp);
-
(void)splx(s);
}
@@ -591,15 +730,10 @@ xe_init(void *xscp) {
*/
static void
xe_start(struct ifnet *ifp) {
- struct xe_softc *scp;
+ struct xe_softc *scp = ifp->if_softc;
struct mbuf *mbp;
- int unit;
- scp = ifp->if_softc;
- unit = scp->crd->isahd.id_unit;
-
- if (scp->gone)
- return;
+ if (scp->gone) return;
/*
* Loop while there are packets to be sent, and space to send them.
@@ -626,10 +760,11 @@ xe_start(struct ifnet *ifp) {
}
#if NBPFILTER > 0
- /*
- * Tap off here if there is a bpf listener.
- */
+ /* Tap off here if there is a bpf listener */
if (ifp->if_bpf) {
+#ifdef XE_DEBUG
+ printf("xe%d: sending output packet to BPF\n", scp->unit);
+#endif
bpf_mtap(ifp, mbp);
}
#endif /* NBPFILTER > 0 */
@@ -647,7 +782,7 @@ xe_start(struct ifnet *ifp) {
*/
static int
xe_ioctl (register struct ifnet *ifp, u_long command, caddr_t data) {
- struct xe_softc *scp = ifp->if_softc;
+ struct xe_softc *scp;
int s, error;
scp = ifp->if_softc;
@@ -668,30 +803,16 @@ xe_ioctl (register struct ifnet *ifp, u_long command, caddr_t data) {
break;
case SIOCSIFFLAGS:
- if (ifp->if_flags & IFF_LINK2) {
- scp->port = 4; /* 100BASE-TX */
- scp->probe = 0;
- }
- else if (ifp->if_flags & IFF_LINK1) {
- scp->port = 2; /* 10BASE-T */
- scp->probe = 0;
- }
- else if (ifp->if_flags & IFF_LINK0) {
- scp->port = 1; /* 10BASE-2 */
- scp->probe = 0;
- }
- else {
- scp->port = 0; /* Unknown */
- scp->probe = 1;
- }
-
/*
* If the interface is marked up and stopped, then start it. If it is
* marked down and running, then stop it.
*/
if (ifp->if_flags & IFF_UP) {
- if (!(ifp->if_flags & IFF_RUNNING))
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+ xe_hard_reset(scp);
+ xe_setmedia(scp);
xe_init(scp);
+ }
}
else {
if (ifp->if_flags & IFF_RUNNING)
@@ -709,6 +830,14 @@ xe_ioctl (register struct ifnet *ifp, u_long command, caddr_t data) {
error = 0;
break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ /*
+ * Someone wants to get/set media options.
+ */
+ error = ifmedia_ioctl(ifp, (struct ifreq *)data, &scp->ifmedia, command);
+ break;
+
default:
error = EINVAL;
}
@@ -720,6 +849,300 @@ xe_ioctl (register struct ifnet *ifp, u_long command, caddr_t data) {
/*
+ * Card interrupt handler: should return true if the interrupt was for us, in
+ * case we are sharing our IRQ line with other devices (this will probably be
+ * the case for multifunction cards).
+ *
+ * This function is probably more complicated than it needs to be, as it
+ * attempts to deal with the case where multiple packets get sent between
+ * interrupts. This is especially annoying when working out the collision
+ * stats. Not sure whether this case ever really happens or not (maybe on a
+ * slow/heavily loaded machine?) so it's probably best to leave this like it
+ * is.
+ *
+ * Note that the crappy PIO used to get packets on and off the card means that
+ * you will spend a lot of time in this routine -- I can get my P150 to spend
+ * 90% of its time servicing interrupts if I really hammer the network. Could
+ * fix this, but then you'd start dropping/losing packets. The moral of this
+ * story? If you want good network performance _and_ some cycles left over to
+ * get your work done, don't buy a Xircom card. Or convince them to tell me
+ * how to do memory-mapped I/O :)
+ */
+static int
+xe_card_intr(struct pccard_devinfo *devi) {
+ struct xe_softc *scp;
+ struct ifnet *ifp;
+ int unit, result;
+ u_int16_t rx_bytes, rxs, txs;
+ u_int8_t psr, isr, esr, rsr;
+
+ unit = devi->isahd.id_unit;
+ scp = sca[unit];
+ ifp = &scp->arpcom.ac_if;
+ rx_bytes = 0; /* Bytes received on this interrupt */
+ result = 0; /* Set true if the interrupt is for us */
+
+ if (scp->gone)
+ return 0;
+
+ if (scp->ce3) {
+ XE_OUTB(XE_CR, 0); /* Disable interrupts */
+ }
+
+ psr = XE_INB(XE_PSR); /* Stash the current register page */
+
+ /*
+ * Read ISR to see what caused this interrupt. Note that this clears the
+ * ISR on CE2 type cards.
+ */
+ if ((isr = XE_INB(XE_ISR)) && isr != 0xff) {
+
+ result = 1; /* This device did generate an int */
+ esr = XE_INB(XE_ESR); /* Read the other status registers */
+ XE_SELECT_PAGE(0x40);
+ rxs = XE_INB(XE_RXS0);
+ XE_OUTB(XE_RXS0, ~rxs & 0xff);
+ txs = XE_INB(XE_TXS0);
+ txs |= XE_INB(XE_TXS1) << 8;
+ XE_OUTB(XE_TXS0, 0);
+ XE_OUTB(XE_TXS1, 0);
+ XE_SELECT_PAGE(0);
+
+#if XE_DEBUG > 3
+ printf("xe%d: ISR=%#2.2x ESR=%#2.2x RXS=%#2.2x TXS=%#4.4x\n", unit, isr, esr, rxs, txs);
+#endif
+
+ /*
+ * Handle transmit interrupts
+ */
+ if (isr & XE_ISR_TX_PACKET) {
+ u_int8_t new_ptr, sent;
+
+ if ((new_ptr = XE_INB(XE_PTR)) < scp->tx_ptr) /* Update packet count */
+ sent = (0xff - scp->tx_ptr) + new_ptr; /* PTR rolled over */
+ else
+ sent = new_ptr - scp->tx_ptr;
+
+ if (sent > 0) { /* Packets sent since last interrupt */
+ scp->tx_ptr = new_ptr;
+ scp->tx_queued -= sent;
+ ifp->if_opackets += sent;
+ ifp->if_collisions += scp->tx_collisions;
+
+ /*
+ * Collision stats are a PITA. If multiples frames have been sent, we
+ * distribute any outstanding collision count equally amongst them.
+ * However, if we're missing interrupts we're quite likely to also
+ * miss some collisions; thus the total count will be off anyway.
+ * Likewise, if we miss a frame dropped due to excessive collisions
+ * any outstanding collisions count will be held against the next
+ * frame to be successfully sent. Hopefully it averages out in the
+ * end!
+ * XXX - This will screw up if tx_collisions/sent > 14.
+ */
+ switch (scp->tx_collisions) {
+ case 0:
+ break;
+ case 1:
+ scp->mibdata.dot3StatsSingleCollisionFrames++;
+ scp->mibdata.dot3StatsCollFrequencies[0]++;
+ break;
+ default:
+ if (sent == 1) {
+ scp->mibdata.dot3StatsMultipleCollisionFrames++;
+ scp->mibdata.dot3StatsCollFrequencies[scp->tx_collisions-1]++;
+ }
+ else { /* Distribute across multiple frames */
+ scp->mibdata.dot3StatsMultipleCollisionFrames += sent;
+ scp->mibdata.
+ dot3StatsCollFrequencies[scp->tx_collisions/sent] += sent - scp->tx_collisions%sent;
+ scp->mibdata.
+ dot3StatsCollFrequencies[scp->tx_collisions/sent + 1] += scp->tx_collisions%sent;
+ }
+ }
+ scp->tx_collisions = 0;
+ }
+ ifp->if_timer = 0;
+ ifp->if_flags &= ~IFF_OACTIVE;
+ }
+ if (txs & 0x0002) { /* Excessive collisions (packet dropped) */
+ ifp->if_collisions += 16;
+ ifp->if_oerrors++;
+ scp->tx_collisions = 0;
+ scp->mibdata.dot3StatsExcessiveCollisions++;
+ scp->mibdata.dot3StatsMultipleCollisionFrames++;
+ scp->mibdata.dot3StatsCollFrequencies[15]++;
+ XE_OUTB(XE_CR, XE_CR_RESTART_TX);
+ }
+ if (txs & 0x0040) /* Transmit aborted -- probably collisions */
+ scp->tx_collisions++;
+
+
+ /*
+ * Handle receive interrupts
+ */
+ while ((esr = XE_INB(XE_ESR)) & XE_ESR_FULL_PKT_RX) {
+
+ if ((rsr = XE_INB(XE_RSR)) & XE_RSR_RX_OK) {
+ struct ether_header *ehp;
+ struct mbuf *mbp;
+ u_int16_t len;
+
+ len = XE_INW(XE_RBC);
+
+ if (len == 0)
+ continue;
+
+#if 0
+ /*
+ * Limit the amount of time we spend in this loop, dropping packets if
+ * necessary. The Linux code does this with considerably more
+ * finesse, adjusting the threshold dynamically.
+ */
+ if ((rx_bytes += len) > 22000) {
+ ifp->if_iqdrops++;
+ scp->mibData.dot3StatsMissedFrames++;
+ XE_OUTW(XE_DOR, 0x8000);
+ continue;
+ }
+#endif
+
+ if (len & 0x01)
+ len++;
+
+ MGETHDR(mbp, M_DONTWAIT, MT_DATA); /* Allocate a header mbuf */
+ if (mbp != NULL) {
+ mbp->m_pkthdr.rcvif = ifp;
+ mbp->m_pkthdr.len = mbp->m_len = len;
+
+ /*
+ * If the mbuf header isn't big enough for the packet, attach an
+ * mbuf cluster to hold it. The +2 is to allow for the nasty little
+ * alignment hack below.
+ */
+ if (len + 2 > MHLEN) {
+ MCLGET(mbp, M_DONTWAIT);
+ if ((mbp->m_flags & M_EXT) == 0) {
+ m_freem(mbp);
+ mbp = NULL;
+ }
+ }
+ }
+
+ if (mbp != NULL) {
+ /*
+ * The Ethernet header is 14 bytes long; thus the actual packet data
+ * won't be 32-bit aligned when it's dumped into the mbuf. We
+ * offset everything by 2 bytes to fix this. Apparently the
+ * alignment is important for NFS, damn its eyes.
+ */
+ mbp->m_data += 2;
+ ehp = mtod(mbp, struct ether_header *);
+
+ /*
+ * Now get the packet, including the Ethernet header and trailer (?)
+ * We use programmed I/O, because we don't know how to do shared
+ * memory with these cards. So yes, it's real slow, and heavy on
+ * the interrupts (CPU on my P150 maxed out at ~950KBps incoming).
+ */
+ if (scp->srev == 0) { /* Workaround a bug in old cards */
+ u_short rhs;
+
+ XE_SELECT_PAGE(5);
+ rhs = XE_INW(XE_RHS);
+ XE_SELECT_PAGE(0);
+
+ rhs += 3; /* Skip control info */
+
+ if (rhs >= 0x8000)
+ rhs = 0;
+
+ if (rhs + len > 0x8000) {
+ int i;
+
+ /*
+ * XXX - this i-- seems very wrong, but it's what the Linux guys
+ * XXX - do. Need someone with an old CE2 to test this for me.
+ */
+ for (i = 0; i < len; i--, rhs++) {
+ ((char *)ehp)[i] = XE_INB(XE_EDP);
+ if (rhs = 0x8000) {
+ rhs = 0;
+ i--;
+ }
+ }
+ }
+ else
+ insw(scp->dev->id_iobase+XE_EDP, ehp, len >> 1);
+ }
+ else
+ insw(scp->dev->id_iobase+XE_EDP, ehp, len >> 1);
+
+#if NBPFILTER > 0
+ /*
+ * Check if there's a BPF listener on this interface. If so, hand
+ * off the raw packet to bpf.
+ */
+ if (ifp->if_bpf) {
+#ifdef XE_DEBUG
+ printf("xe%d: passing input packet to BPF\n", scp->unit);
+#endif
+ bpf_mtap(ifp, mbp);
+
+ /*
+ * Note that the interface cannot be in promiscuous mode if there
+ * are no BPF listeners. And if we are in promiscuous mode, we
+ * have to check if this packet is really ours.
+ */
+ if ((ifp->if_flags & IFF_PROMISC) &&
+ bcmp(ehp->ether_dhost, scp->arpcom.ac_enaddr, sizeof(ehp->ether_dhost)) != 0 &&
+ (rsr & XE_RSR_PHYS_PKT)) {
+ m_freem(mbp);
+ mbp = NULL;
+ }
+ }
+#endif /* NBPFILTER > 0 */
+
+ if (mbp != NULL) {
+ mbp->m_pkthdr.len = mbp->m_len = len - ETHER_HDR_LEN;
+ mbp->m_data += ETHER_HDR_LEN; /* Strip off Ethernet header */
+ ether_input(ifp, ehp, mbp); /* Send the packet on its way */
+ ifp->if_ipackets++; /* Success! */
+ XE_OUTW(XE_DOR, 0x8000); /* skip_rx_packet command */
+ }
+ }
+ }
+ else if (rsr & XE_RSR_LONG_PKT) { /* Packet length >1518 bytes */
+ scp->mibdata.dot3StatsFrameTooLongs++;
+ ifp->if_ierrors++;
+ }
+ else if (rsr & XE_RSR_CRC_ERR) { /* Bad checksum on packet */
+ scp->mibdata.dot3StatsFCSErrors++;
+ ifp->if_ierrors++;
+ }
+ else if (rsr & XE_RSR_ALIGN_ERR) { /* Packet alignment error */
+ scp->mibdata.dot3StatsAlignmentErrors++;
+ ifp->if_ierrors++;
+ }
+ }
+ if (rxs & 0x10) { /* Receiver overrun */
+ scp->mibdata.dot3StatsInternalMacReceiveErrors++;
+ ifp->if_ierrors++;
+ XE_OUTB(XE_CR, XE_CR_CLEAR_OVERRUN);
+ }
+ }
+
+ XE_SELECT_PAGE(psr); /* Restore saved page */
+ XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); /* Re-enable interrupts */
+
+ /* XXX - Maybe force an int here, instead of dropping packets? */
+ /* XXX - XE_OUTB(XE_CR, XE_CR_ENABLE_INTR|XE_CE_FORCE_INTR); */
+
+ return result;
+}
+
+
+/*
* Device timeout/watchdog routine. Called automatically if we queue a packet
* for transmission but don't get an interrupt within a specified timeout
* (usually 5 seconds). When this happens we assume the worst and reset the
@@ -727,72 +1150,516 @@ xe_ioctl (register struct ifnet *ifp, u_long command, caddr_t data) {
*/
static void
xe_watchdog(struct ifnet *ifp) {
- struct xe_softc *scp;
- int unit;
-
- scp = ifp->if_softc;
- unit = scp->crd->isahd.id_unit;
+ struct xe_softc *scp = ifp->if_softc;
- if (scp->gone)
- return;
+ if (scp->gone) return;
- printf("xe%d: transmit timeout; resetting card\n", unit);
+ printf("xe%d: watchdog timeout; resetting card\n", scp->unit);
scp->tx_timeouts++;
ifp->if_oerrors += scp->tx_queued;
- xe_reset(scp);
+ xe_stop(scp);
+ xe_hard_reset(scp);
+ xe_setmedia(scp);
+ xe_init(scp);
}
/*
- * Take interface offline. This is done by powering down the device, which I
- * assume means just shutting down the transceiver and Ethernet logic. There
- * is probably a more elegant method that doesn't require a full reset to
- * recover from.
+ * Change media selection.
+ */
+static int
+xe_media_change(struct ifnet *ifp) {
+ struct xe_softc *scp = ifp->if_softc;
+
+#ifdef XE_DEBUG
+ printf("xe%d: media_change\n", ifp->if_unit);
+#endif
+
+ if (IFM_TYPE(scp->ifm->ifm_media) != IFM_ETHER)
+ return(EINVAL);
+
+ /*
+ * Some card/media combos aren't always possible -- filter those out here.
+ */
+ if ((IFM_SUBTYPE(scp->ifm->ifm_media) == IFM_AUTO ||
+ IFM_SUBTYPE(scp->ifm->ifm_media) == IFM_100_TX) && !scp->phy_ok)
+ return (EINVAL);
+
+ xe_setmedia(scp);
+
+ return 0;
+}
+
+
+/*
+ * Return current media selection.
*/
static void
-xe_stop(struct xe_softc *scp) {
- struct ifnet *ifp;
+xe_media_status(struct ifnet *ifp, struct ifmediareq *mrp) {
- ifp = &scp->arpcom.ac_if;
+#ifdef XE_DEBUG
+ printf("xe%d: media_status\n", ifp->if_unit);
+#endif
- if (scp->gone)
- return;
+ mrp->ifm_active = ((struct xe_softc *)ifp->if_softc)->media;
+
+ return;
+}
+
+
+/*
+ * Select active media.
+ */
+static void xe_setmedia(void *xscp) {
+ struct xe_softc *scp = xscp;
+ u_int16_t bmcr, bmsr, anar, lpar, aner;
+
+#ifdef XE_DEBUG
+ printf("xe%d: setmedia\n", scp->unit);
+#endif
+
+ /* Cancel any pending timeout */
+ untimeout(xe_setmedia, scp, scp->chand);
+ xe_disable_intr(scp);
+
+ /* Select media */
+ scp->media = IFM_ETHER;
+ switch (IFM_SUBTYPE(scp->ifm->ifm_media)) {
+
+ case IFM_AUTO: /* Autoselect media */
+ scp->media = IFM_ETHER|IFM_AUTO;
+
+ /*
+ * Autoselection is really awful. It goes something like this:
+ *
+ * Wait until the transmitter goes idle (2sec timeout).
+ * Reset card
+ * IF a 100Mbit PHY exists
+ * Start NWAY autonegotiation (3.5sec timeout)
+ * IF that succeeds
+ * Select 100baseTX or 10baseT, whichever was detected
+ * ELSE
+ * Reset card
+ * IF a 100Mbit PHY exists
+ * Try to force a 100baseTX link (3sec timeout)
+ * IF that succeeds
+ * Select 100baseTX
+ * ELSE
+ * Disable the PHY
+ * ENDIF
+ * ENDIF
+ * ENDIF
+ * ENDIF
+ * IF nothing selected so far
+ * IF a 100Mbit PHY exists
+ * Select 10baseT
+ * ELSE
+ * Select 10baseT or 10base2, whichever is connected
+ * ENDIF
+ * ENDIF
+ */
+ switch (scp->autoneg_status) {
+ case XE_AUTONEG_NONE:
+#ifdef XE_DEBUG
+ printf("xe%d: Waiting for idle transmitter\n", scp->unit);
+#endif
+ scp->arpcom.ac_if.if_flags |= IFF_OACTIVE;
+ scp->autoneg_status = XE_AUTONEG_WAITING;
+ scp->chand = timeout(xe_setmedia, scp, hz * 2);
+ return;
+
+ case XE_AUTONEG_WAITING:
+ xe_soft_reset(scp);
+ if (scp->phy_ok) {
+#ifdef XE_DEBUG
+ printf("xe%d: Starting autonegotiation\n", scp->unit);
+#endif
+ bmcr = xe_phy_readreg(scp, PHY_BMCR);
+ bmcr &= ~(PHY_BMCR_AUTONEGENBL);
+ xe_phy_writereg(scp, PHY_BMCR, bmcr);
+ anar = xe_phy_readreg(scp, PHY_ANAR);
+ anar &= ~(PHY_ANAR_100BT4|PHY_ANAR_100BTXFULL|PHY_ANAR_10BTFULL);
+ anar |= PHY_ANAR_100BTXHALF|PHY_ANAR_10BTHALF;
+ xe_phy_writereg(scp, PHY_ANAR, anar);
+ bmcr |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;
+ xe_phy_writereg(scp, PHY_BMCR, bmcr);
+ scp->autoneg_status = XE_AUTONEG_STARTED;
+ scp->chand = timeout(xe_setmedia, scp, hz * 7/2);
+ return;
+ }
+ else {
+ scp->autoneg_status = XE_AUTONEG_FAIL;
+ }
+ break;
+
+ case XE_AUTONEG_STARTED:
+ bmsr = xe_phy_readreg(scp, PHY_BMSR);
+ lpar = xe_phy_readreg(scp, PHY_LPAR);
+ if (bmsr & (PHY_BMSR_AUTONEGCOMP|PHY_BMSR_LINKSTAT)) {
+#ifdef XE_DEBUG
+ printf("xe%d: Autonegotiation complete!\n", scp->unit);
+#endif
+ /*
+ * XXX - Shouldn't have to do this, but (on my hub at least) the
+ * XXX - transmitter won't work after a successful autoneg. So we see
+ * XXX - what the negotiation result was and force that mode. I'm
+ * XXX - sure there is an easy fix for this.
+ */
+ if (lpar & PHY_LPAR_100BTXHALF) {
+ xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL);
+ XE_MII_DUMP(scp);
+ XE_SELECT_PAGE(2);
+ XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08);
+ scp->media = IFM_ETHER|IFM_100_TX;
+ scp->autoneg_status = XE_AUTONEG_NONE;
+ }
+ else {
+ xe_phy_writereg(scp, PHY_BMCR, 0x0000);
+ XE_SELECT_PAGE(2);
+ XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08); /* Disable PHY? */
+ scp->autoneg_status = XE_AUTONEG_FAIL;
+ }
+ }
+ else {
+#ifdef XE_DEBUG
+ printf("xe%d: Autonegotiation failed; trying 100baseTX\n", scp->unit);
+#endif
+ XE_MII_DUMP(scp);
+ xe_soft_reset(scp);
+ if (scp->phy_ok) {
+ xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL);
+ scp->autoneg_status = XE_AUTONEG_100TX;
+ scp->chand = timeout(xe_setmedia, scp, hz * 3);
+ return;
+ }
+ else {
+ scp->autoneg_status = XE_AUTONEG_FAIL;
+ }
+ }
+ break;
+
+ case XE_AUTONEG_100TX:
+ (void)xe_phy_readreg(scp, PHY_BMSR);
+ bmsr = xe_phy_readreg(scp, PHY_BMSR);
+ if (bmsr & PHY_BMSR_LINKSTAT) {
+#ifdef XE_DEBUG
+ printf("xe%d: Got 100baseTX link!\n", scp->unit);
+#endif
+ XE_MII_DUMP(scp);
+ XE_SELECT_PAGE(2);
+ XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08);
+ scp->media = IFM_ETHER|IFM_100_TX;
+ scp->autoneg_status = XE_AUTONEG_NONE;
+ }
+ else {
+#ifdef XE_DEBUG
+ printf("xe%d: Autonegotiation failed; disabling PHY\n", scp->unit);
+#endif
+ XE_MII_DUMP(scp);
+ xe_phy_writereg(scp, PHY_BMCR, 0x0000);
+ XE_SELECT_PAGE(2);
+ XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08); /* Disable PHY? */
+ scp->autoneg_status = XE_AUTONEG_FAIL;
+ }
+ break;
+ }
+
+ /*
+ * If we got down here _and_ autoneg_status is XE_AUTONEG_FAIL, then
+ * either autonegotiation failed, or never got started to begin with. In
+ * either case, select a suitable 10Mbit media and hope it works. We
+ * don't need to reset the card again, since it will have been done
+ * already by the big switch above.
+ */
+ if (scp->autoneg_status == XE_AUTONEG_FAIL) {
+#ifdef XE_DEBUG
+ printf("xe%d: Selecting 10baseX\n", scp->unit);
+#endif
+ if (scp->ce3) {
+ XE_SELECT_PAGE(0x42);
+ XE_OUTB(XE_SWC1, 0x80);
+ scp->media = IFM_ETHER|IFM_10_T;
+ scp->autoneg_status = XE_AUTONEG_NONE;
+ }
+ else {
+ XE_SELECT_PAGE(4);
+ XE_OUTB(XE_GPR0, 4);
+ DELAY(50000);
+ XE_SELECT_PAGE(0x42);
+ XE_OUTB(XE_SWC1, (XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ? 0x80 : 0xc0);
+ scp->media = IFM_ETHER|((XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ? IFM_10_T : IFM_10_2);
+ scp->autoneg_status = XE_AUTONEG_NONE;
+ }
+ }
+ break;
+
+
+ /*
+ * If a specific media has been requested, we just reset the card and
+ * select it (one small exception -- if 100baseTX is requested by there is
+ * no PHY, we fall back to 10baseT operation).
+ */
+ case IFM_100_TX: /* Force 100baseTX */
+ xe_soft_reset(scp);
+ if (scp->phy_ok) {
+#ifdef XE_DEBUG
+ printf("xe%d: Selecting 100baseTX\n", scp->unit);
+#endif
+ XE_SELECT_PAGE(0x42);
+ XE_OUTB(XE_SWC1, 0);
+ xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_SPEEDSEL);
+ XE_SELECT_PAGE(2);
+ XE_OUTB(XE_MSR, XE_INB(XE_MSR) | 0x08);
+ scp->media |= IFM_100_TX;
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case IFM_10_T: /* Force 10baseT */
+ xe_soft_reset(scp);
+#ifdef XE_DEBUG
+ printf("xe%d: Selecting 10baseT\n", scp->unit);
+#endif
+ if (scp->phy_ok) {
+ xe_phy_writereg(scp, PHY_BMCR, 0x0000);
+ XE_SELECT_PAGE(2);
+ XE_OUTB(XE_MSR, XE_INB(XE_MSR) & ~0x08); /* Disable PHY */
+ }
+ XE_SELECT_PAGE(0x42);
+ XE_OUTB(XE_SWC1, 0x80);
+ scp->media |= IFM_10_T;
+ break;
+
+ case IFM_10_2:
+ xe_soft_reset(scp);
+#ifdef XE_DEBUG
+ printf("xe%d: Selecting 10base2\n", scp->unit);
+#endif
+ XE_SELECT_PAGE(0x42);
+ XE_OUTB(XE_SWC1, 0xc0);
+ scp->media |= IFM_10_2;
+ break;
+ }
+
+
+ /*
+ * Finally, the LEDs are set to match whatever media was chosen and the
+ * transmitter is unblocked.
+ */
+#ifdef XE_DEBUG
+ printf("xe%d: Setting LEDs\n", scp->unit);
+#endif
+ XE_SELECT_PAGE(2);
+ switch (IFM_SUBTYPE(scp->media)) {
+ case IFM_100_TX:
+ case IFM_10_T:
+ XE_OUTB(XE_LED, 0x3b);
+ if (scp->cem56)
+ XE_OUTB(0x0b, 0x04); /* 100Mbit LED */
+ break;
+
+ case IFM_10_2:
+ XE_OUTB(XE_LED, 0x3a);
+ break;
+ }
+
+ /* Restart output? */
+ scp->ifp->if_flags &= ~IFF_OACTIVE;
+ xe_init(scp);
+}
+
+
+/*
+ * Hard reset (power cycle) the card.
+ */
+static void
+xe_hard_reset(struct xe_softc *scp) {
+ int s;
+
+#ifdef XE_DEBUG
+ printf("xe%d: hard_reset\n", scp->unit);
+#endif
+
+ if (scp->gone) return;
+
+ s = splimp();
+
+ /*
+ * Power cycle the card.
+ */
+ XE_SELECT_PAGE(4);
+ XE_OUTB(XE_GPR1, 0); /* Power off */
+ DELAY(40000);
+
+ if (scp->ce3)
+ XE_OUTB(XE_GPR1, 1); /* And back on again */
+ else
+ XE_OUTB(XE_GPR1, 5); /* Also set AIC bit, whatever that is */
+ DELAY(40000);
XE_SELECT_PAGE(0);
- XE_OUTB(XE_CR, 0); /* Disable interrupts */
- XE_SELECT_PAGE(1);
- XE_OUTB(XE_IMR0, 0); /* Forbid all interrupts */
+
+ (void)splx(s);
+}
+
+
+/*
+ * Soft reset the card. Also makes sure that the ML6692 and 10Mbit controller
+ * are powered up, sets the silicon revision number in softc, disables
+ * interrupts and checks for the prescence of a 100Mbit PHY. This should
+ * leave us in a position where we can access the PHY and do media
+ * selection. The function imposes a 0.5s delay while the hardware powers up.
+ */
+static void
+xe_soft_reset(struct xe_softc *scp) {
+ int s;
+
+#ifdef XE_DEBUG
+ printf("xe%d: soft_reset\n", scp->unit);
+#endif
+
+ if (scp->gone) return;
+
+ s = splimp();
+
+ /*
+ * Reset the card, (again).
+ */
+ XE_SELECT_PAGE(0);
+ XE_OUTB(XE_CR, XE_CR_SOFT_RESET);
+ DELAY(40000);
+ XE_OUTB(XE_CR, 0);
+ DELAY(40000);
+
+ if (scp->ce3) {
+ /*
+ * set GP1 and GP2 as outputs (bits 2 & 3)
+ * set GP1 low to power on the ML6692 (bit 0)
+ * set GP2 high to power on the 10Mhz chip (bit 1)
+ */
+ XE_SELECT_PAGE(4);
+ XE_OUTB(XE_GPR0, 0x0e);
+ }
+
+ /*
+ * Wait for everything to wake up.
+ */
+ DELAY(500000);
+
+ /*
+ * Get silicon revision number.
+ */
XE_SELECT_PAGE(4);
- XE_OUTB(XE_GPR1, 0); /* Power down (clear bit 0) */
+ if (scp->ce3)
+ scp->srev = (XE_INB(XE_BOV) & 0x70) >> 4;
+ else
+ scp->srev = (XE_INB(XE_BOV) & 0x30) >> 4;
+
+ /*
+ * Shut off interrupts.
+ */
+ xe_disable_intr(scp);
+
+ /*
+ * Check for PHY.
+ */
+ if (scp->ce3) {
+ scp->phy_ok = xe_mii_init(scp);
+ }
+
XE_SELECT_PAGE(0);
- ifp->if_flags &= ~IFF_RUNNING;
- ifp->if_flags &= ~IFF_OACTIVE;
- ifp->if_timer = 0;
+ (void)splx(s);
}
/*
- * Reset the hardware. Power-down the card then re-initialise it. The
- * xe_stop() is redundant if xe_init() also does one, but it can't hurt.
+ * Take interface offline. This is done by powering down the device, which I
+ * assume means just shutting down the transceiver and Ethernet logic. This
+ * requires a _hard_ reset to recover from, as we need to power up again.
*/
static void
-xe_reset(struct xe_softc *scp) {
+xe_stop(struct xe_softc *scp) {
int s;
- if (scp->gone)
- return;
+#ifdef XE_DEBUG
+ printf("xe%d: stop\n", scp->unit);
+#endif
+
+ if (scp->gone) return;
s = splimp();
- xe_stop(scp);
- xe_init(scp);
+ /*
+ * Shut off interrupts.
+ */
+ xe_disable_intr(scp);
+
+ /*
+ * Power down.
+ */
+ XE_SELECT_PAGE(4);
+ XE_OUTB(XE_GPR1, 0);
+ XE_SELECT_PAGE(0);
+
+ /*
+ * ~IFF_RUNNING == interface down.
+ */
+ scp->ifp->if_flags &= ~IFF_RUNNING;
+ scp->ifp->if_flags &= ~IFF_OACTIVE;
+ scp->ifp->if_timer = 0;
(void)splx(s);
}
/*
+ * Enable Ethernet interrupts from the card.
+ */
+static void
+xe_enable_intr(struct xe_softc *scp) {
+#ifdef XE_DEBUG
+ printf("xe%d: enable_intr\n", scp->unit);
+#endif
+
+ XE_SELECT_PAGE(1);
+ XE_OUTB(XE_IMR0, 0xff); /* Unmask everything */
+ XE_OUTB(XE_IMR1, 0x01); /* Unmask TX underrun detection */
+ DELAY(1);
+
+ XE_SELECT_PAGE(0);
+ XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); /* Enable interrupts */
+ if (scp->modem && !scp->cem56) { /* This bit is just magic */
+ if (!(XE_INB(0x10) & 0x01)) {
+ XE_OUTB(0x10, 0x11); /* Unmask master int enable bit */
+ }
+ }
+}
+
+
+/*
+ * Disable all Ethernet interrupts from the card.
+ */
+static void
+xe_disable_intr(struct xe_softc *scp) {
+#ifdef XE_DEBUG
+ printf("xe%d: disable_intr\n", scp->unit);
+#endif
+
+ XE_SELECT_PAGE(0);
+ XE_OUTB(XE_CR, 0); /* Disable interrupts */
+ if (scp->modem && !scp->cem56) { /* More magic (does this work?) */
+ XE_OUTB(0x10, 0x10); /* Mask the master int enable bit */
+ }
+
+ XE_SELECT_PAGE(1);
+ XE_OUTB(XE_IMR0, 0); /* Forbid all interrupts */
+ XE_OUTB(XE_IMR1, 0);
+ XE_SELECT_PAGE(0);
+}
+
+
+/*
* Set up multicast filter and promiscuous mode
*/
static void
@@ -965,6 +1832,35 @@ xe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp) {
}
+/*
+ * The device entry is being removed, probably because someone ejected the
+ * card. The interface should have been brought down manually before calling
+ * this function; if not you may well lose packets. In any case, I shut down
+ * the card and the interface, and hope for the best. The 'gone' flag is set,
+ * so hopefully no-one else will try to access the missing card.
+ */
+static void
+xe_card_unload(struct pccard_devinfo *devi) {
+ struct xe_softc *scp;
+ struct ifnet *ifp;
+ int unit;
+
+ unit = devi->isahd.id_unit;
+ scp = sca[unit];
+ ifp = &scp->arpcom.ac_if;
+
+ if (scp->gone) {
+ printf("xe%d: already unloaded\n", unit);
+ return;
+ }
+
+ if_down(ifp);
+ ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
+ xe_stop(scp);
+ scp->gone = 1;
+}
+
+
/**************************************************************
* *
@@ -972,20 +1868,13 @@ xe_pio_write_packet(struct xe_softc *scp, struct mbuf *mbp) {
* *
**************************************************************/
-#if 0
/*
* Alternative MII/PHY handling code adapted from the xl driver. It doesn't
* seem to work any better than the xirc2_ps stuff, but it's cleaner code.
- * Will probably use this if I can ever get the autoneg to work right :(
*/
-struct xe_mii_frame {
- u_int8_t mii_stdelim;
- u_int8_t mii_opcode;
- u_int8_t mii_phyaddr;
- u_int8_t mii_regaddr;
- u_int8_t mii_turnaround;
- u_int16_t mii_data;
-};
+#define XE_MII_SET(x) XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) | (x))
+#define XE_MII_CLR(x) XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) & ~(x))
+
/*
* Sync the PHYs by setting data bit and strobing the clock 32 times.
@@ -1005,6 +1894,36 @@ xe_mii_sync(struct xe_softc *scp) {
}
}
+
+/*
+ * Look for a MII-compliant PHY. If we find one, reset it.
+ */
+static int
+xe_mii_init(struct xe_softc *scp) {
+ u_int16_t status;
+
+ status = xe_phy_readreg(scp, PHY_BMSR);
+ if ((status & 0xff00) != 0x7800) {
+#ifdef XE_DEBUG
+ printf("xe%d: no PHY found, %0x\n", scp->unit, status);
+#endif
+ return 0;
+ }
+ else {
+#ifdef XE_DEBUG
+ printf("xe%d: PHY OK!\n", scp->unit);
+#endif
+
+ /* Reset the PHY */
+ xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_RESET);
+ DELAY(500);
+ while(xe_phy_readreg(scp, PHY_BMCR) & PHY_BMCR_RESET);
+ XE_MII_DUMP(scp);
+ return 1;
+ }
+}
+
+
/*
* Clock a series of bits through the MII.
*/
@@ -1028,6 +1947,7 @@ xe_mii_send(struct xe_softc *scp, u_int32_t bits, int cnt) {
}
}
+
/*
* Read an PHY register through the MII.
*/
@@ -1119,6 +2039,7 @@ fail:
return(0);
}
+
/*
* Write to a PHY register through the MII.
*/
@@ -1167,6 +2088,10 @@ xe_mii_writereg(struct xe_softc *scp, struct xe_mii_frame *frame) {
return(0);
}
+
+/*
+ * Read a register from the PHY.
+ */
static u_int16_t
xe_phy_readreg(struct xe_softc *scp, u_int16_t reg) {
struct xe_mii_frame frame;
@@ -1180,6 +2105,10 @@ xe_phy_readreg(struct xe_softc *scp, u_int16_t reg) {
return(frame.mii_data);
}
+
+/*
+ * Write to a PHY register.
+ */
static void
xe_phy_writereg(struct xe_softc *scp, u_int16_t reg, u_int16_t data) {
struct xe_mii_frame frame;
@@ -1194,90 +2123,10 @@ xe_phy_writereg(struct xe_softc *scp, u_int16_t reg, u_int16_t data) {
return;
}
-/*
- * Initiate an autonegotiation session.
- */
-static void
-xe_autoneg_xmit(struct xe_softc *scp) {
- u_int16_t phy_sts;
-
- xe_phy_writereg(scp, PHY_BMCR, PHY_BMCR_RESET);
- DELAY(500);
- while(xe_phy_readreg(scp, PHY_BMCR) & PHY_BMCR_RESET);
-
- phy_sts = xe_phy_readreg(scp, PHY_BMCR);
- phy_sts &= ~PHY_BMCR_AUTONEGENBL;
- xe_phy_writereg(scp, PHY_BMCR, phy_sts);
- DELAY(1000);
- phy_sts |= PHY_BMCR_AUTONEGENBL|PHY_BMCR_AUTONEGRSTR;
- phy_sts &= ~PHY_BMCR_ISOLATE;
- xe_phy_writereg(scp, PHY_BMCR, phy_sts);
- DELAY(1000);
-
- return;
-}
-
-/*
- * Invoke autonegotiation on a PHY. Also used with the 3Com internal
- * autoneg logic which is mapped onto the MII.
- */
-static void
-xe_autoneg_mii(struct xe_softc *scp) {
- u_int16_t phy_sts = 0, media, advert, ability;
- int unit = scp->dev->id_unit;
- int i;
-
- /*
- * First, see if autoneg is supported. If not, there's
- * no point in continuing.
- */
- phy_sts = xe_phy_readreg(scp, PHY_BMSR);
- if (!(phy_sts & PHY_BMSR_CANAUTONEG)) {
- printf("xe%d: autonegotiation not supported\n", unit);
- media = xe_phy_readreg(scp, PHY_BMCR);
- media &= ~PHY_BMCR_SPEEDSEL;
- media &= ~PHY_BMCR_DUPLEX;
- xe_phy_writereg(scp, PHY_BMCR, media);
- return;
- }
-
- xe_autoneg_xmit(scp);
- DELAY(5000000);
-
- if (xe_phy_readreg(scp, PHY_BMSR) & PHY_BMSR_AUTONEGCOMP) {
- printf("xe%d: autoneg complete, ", unit);
- phy_sts = xe_phy_readreg(scp, PHY_BMSR);
- } else {
- printf("xe%d: autoneg not complete, ", unit);
- }
-
- media = xe_phy_readreg(scp, PHY_BMCR);
-
- /* Link is good. Report modes and set duplex mode. */
- if (xe_phy_readreg(scp, PHY_BMSR) & PHY_BMSR_LINKSTAT) {
- printf("link status good ");
- advert = xe_phy_readreg(scp, XL_PHY_ANAR);
- ability = xe_phy_readreg(scp, XL_PHY_LPAR);
-
- if (advert & PHY_ANAR_100BT4 && ability & PHY_ANAR_100BT4) {
- printf("(100baseT4)\n");
- } else if (advert & PHY_ANAR_100BTXFULL && ability & PHY_ANAR_100BTXFULL) {
- printf("(full-duplex, 100Mbps)\n");
- } else if (advert & PHY_ANAR_100BTXHALF && ability & PHY_ANAR_100BTXHALF) {
- printf("(half-duplex, 100Mbps)\n");
- } else if (advert & PHY_ANAR_10BTFULL && ability & PHY_ANAR_10BTFULL) {
- printf("(full-duplex, 10Mbps)\n");
- } else if (advert & PHY_ANAR_10BTHALF && ability & PHY_ANAR_10BTHALF) {
- printf("(half-duplex, 10Mbps)\n");
- }
- } else {
- printf("no carrier (forcing half-duplex, 10Mbps)\n");
- xe_phy_writereg(scp, PHY_BMCR, 0x0000);
- }
-}
+#ifdef XE_DEBUG
static void
-xe_mii_new_dump(struct xe_softc *scp) {
+xe_mii_dump(struct xe_softc *scp) {
int i, unit = scp->dev->id_unit;
for (i = 0; i < 2; i++) {
@@ -1288,210 +2137,6 @@ xe_mii_new_dump(struct xe_softc *scp) {
}
printf("\n");
}
-#endif
-
-
-#if 1
-/*
- * Original MII/PHY code from xirc2_ps driver. Seems like it should work,
- * according to the ML6692 and DP83840A specs, but the autonegotiation never
- * completes, even on the 10/100 net at work. It's very weird.
- */
-static void
-xe_mii_clock(struct xe_softc *scp) {
- XE_OUTB(XE_GPR2, 0x04); /* MDCK low */
- DELAY(1);
- XE_OUTB(XE_GPR2, 0x05); /* MDCK high */
- DELAY(1);
-}
-
-static u_int16_t
-xe_mii_getbit(struct xe_softc *scp) {
- u_int16_t data;
-
- XE_OUTB(XE_GPR2, 0x04);
- DELAY(1);
- data = XE_INB(XE_GPR2);
- XE_OUTB(XE_GPR2, 0x05);
- DELAY(1);
- return data & 0x20;
-}
-
-static void
-xe_mii_putbit(struct xe_softc *scp, u_int16_t data) {
- if (data) {
- XE_OUTB(XE_GPR2, 0x0e);
- DELAY(1);
- XE_OUTB(XE_GPR2, 0x0f);
- DELAY(1);
- }
- else {
- XE_OUTB(XE_GPR2, 0x0c);
- DELAY(1);
- XE_OUTB(XE_GPR2, 0x0d);
- DELAY(1);
- }
-}
-
-static void
-xe_mii_putbits(struct xe_softc *scp, u_int16_t data, int len) {
- u_int16_t mask;
- for (mask = 1 << (--len); mask != 0; mask >>= 1)
- xe_mii_putbit(scp, data & mask);
-}
-
-static u_int16_t
-xe_mii_read(struct xe_softc *scp, u_int8_t phy, u_int8_t reg) {
- int i;
- u_int16_t mask, data = 0;
-
- XE_SELECT_PAGE(2);
-
- for (i = 0; i < 32; i++)
- xe_mii_putbit(scp, 1);
-
- xe_mii_putbits(scp, 0x06, 4);
- xe_mii_putbits(scp, phy, 5);
- xe_mii_putbits(scp, reg, 5);
- xe_mii_clock(scp);
- xe_mii_getbit(scp);
-
- for (mask = 1 << 15; mask != 0; mask >>= 1)
- if (xe_mii_getbit(scp))
- data |= mask;
-
- xe_mii_clock(scp);
-
- return data;
-}
-
-static void
-xe_mii_write(struct xe_softc *scp, u_int8_t phy, u_int8_t reg, u_int16_t data, int len) {
- int i;
-
- XE_SELECT_PAGE(2);
-
- for (i = 0; i < 32; i++)
- xe_mii_putbit(scp, 1);
-
- xe_mii_putbits(scp, 0x05, 4);
- xe_mii_putbits(scp, phy, 5);
- xe_mii_putbits(scp, reg, 5);
- xe_mii_putbit(scp, 1);
- xe_mii_putbit(scp, 0);
- xe_mii_putbits(scp, data, len);
- xe_mii_clock(scp);
-}
-
-static int
-xe_mii_init(struct xe_softc *scp) {
- u_int16_t control, status, partner;
- int unit = scp->dev->id_unit, i;
-
- status = xe_mii_read(scp, 0, 1);
- if ((status & 0xff00) != 0x7800) {
-#ifdef XE_DEBUG
- printf("xe%d: no MII found, %0x\n", unit, status);
-#endif
- return 0;
- }
-
- /*
- * XXX - do proper media selection here
- */
- if (scp->probe) {
-#ifdef XE_DEBUG
- printf("xe%d: trying auto-negotiation\n", unit);
-#endif
- control = 0x1000; /* AutoNeg */
- }
- else if (scp->port == 4) {
-#ifdef XE_DEBUG
- printf("xe%d: defaulting to 100Mbps\n", unit);
-#endif
- control = 0x2000; /* 100Mbps */
- }
- else {
-#ifdef XE_DEBUG
- printf("xe%d: defaulting to 10Mbps\n", unit);
-#endif
- control = 0x0000; /* 10Mbps */
- }
-
- xe_mii_write(scp, 0, 0, control, 16);
- DELAY(100);
- control = xe_mii_read(scp, 0, 0);
-
- if (control & 0x0400) {
-#ifdef XE_DEBUG
- printf("xe%d: can't take PHY out of isolation mode\n", unit);
-#endif
- return 0;
- }
-
- if (scp->probe) {
- /* Wait for negotiation to finish */
-#ifdef XE_DEBUG
- printf("xe%d: waiting for auto-negotiation to complete...\n", unit);
-#endif
- for (i = 0; i < 35; i++) {
- DELAY(100000);
- status = xe_mii_read(scp, 0, 1);
- if ((status & 0x0020) && (status &0x0004))
- break;
- }
-
- if (!(status & 0x0020)) {
-#ifdef XE_DEBUG
- printf("xe%d: auto-negotiation failed\n", unit);
-#endif
- control = 0x0000;
- xe_mii_write(scp, 0, 0, control, 16);
- DELAY(100);
- XE_SELECT_PAGE(0);
- scp->port = (XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ? 2 : 1;
- }
- else {
- partner = xe_mii_read(scp, 0, 5);
-#ifdef XE_DEBUG
- printf("xe%d: MII link partner = %04x\n", unit, partner);
-#endif
- if (partner & 0x0080) {
- scp->port = 4;
- }
- else {
- DELAY(100);
- XE_SELECT_PAGE(0);
- scp->port = (XE_INB(XE_ESR) & XE_ESR_MEDIA_SELECT) ? 2 : 1;
- }
- }
- scp->probe = 0;
- }
-
-#ifdef XE_DEBUG
- printf("xe%d: auto-negotiation result: ", unit);
- switch (scp->port) {
- case 4: printf("100BaseTX\n"); break;
- case 2: printf("10BaseT\n"); break;
- case 1: printf("10Base2\n"); break;
- default: printf("unknown\n");
- }
-#endif
-
- return 1;
-}
-
-#ifdef XE_DEBUG
-static void
-xe_mii_dump(struct xe_softc *scp) {
- int i, unit = scp->dev->id_unit;
-
- printf("xe%d: MII register dump\n", unit);
- for (i = 0; i < 7; i++) {
- printf(" %04x", xe_mii_read(scp, 0, i));
- }
- printf("\n");
-}
static void
xe_reg_dump(struct xe_softc *scp) {
@@ -1525,579 +2170,6 @@ xe_reg_dump(struct xe_softc *scp) {
}
}
#endif
-#endif
-
-
-/**************************************************************
- * *
- * P C M C I A F U N C T I O N S *
- * *
- **************************************************************/
-
-#define CARD_MAJOR 50
-
-/*
- * Horrid stuff for accessing CIS tuples
- */
-#define CISTPL_BUFSIZE 512
-#define CISTPL_TYPE(tpl) tpl[0]
-#define CISTPL_LEN(tpl) tpl[2]
-#define CISTPL_DATA(tpl,pos) tpl[4 + ((pos)<<1)]
-
-
-/*
- * Probe and identify the device. Called by the slot manager when the card is
- * inserted or the machine wakes up from suspend mode. Assmes that the slot
- * structure has been initialised already.
- */
-static int
-xe_card_init(struct pccard_devinfo *devi)
-{
- struct xe_softc *scp;
- struct isa_device *dev;
- struct uio uios;
- struct iovec iov;
- u_char buf[CISTPL_BUFSIZE];
- u_char ver_str[CISTPL_BUFSIZE>>1];
- off_t offs;
- int unit, success, rc, i;
-
- unit = devi->isahd.id_unit;
- scp = sca[unit];
- dev = &devi->isahd;
- success = 0;
-
-#ifdef XE_DEBUG
- printf("xe: Probing for unit %d\n", unit);
-#endif
-
- /* Check that unit number is OK */
- if (unit > MAXSLOT) {
- printf("xe: bad unit (%d)\n", unit);
- return (ENODEV);
- }
-
- /* Don't attach an active device */
- if (scp && !scp->gone) {
- printf("xe: unit already attached (%d)\n", unit);
- return (EBUSY);
- }
-
- /* Allocate per-instance storage */
- if (!scp) {
- if ((scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT)) == NULL) {
- printf("xe%d: failed to allocage driver strorage\n", unit);
- return (ENOMEM);
- }
- bzero(scp, sizeof(*scp));
- }
-
- /* Re-attach an existing device */
- if (scp->gone) {
- scp->gone = 0;
- xe_stop(scp);
- return 0;
- }
-
- /* Grep through CIS looking for relevant tuples */
- offs = 0;
- do {
- u_int16_t vendor;
- u_int8_t rev, media, prod;
-
- iov.iov_base = buf;
- iov.iov_len = CISTPL_BUFSIZE;
- uios.uio_iov = &iov;
- uios.uio_iovcnt = 1;
- uios.uio_offset = offs;
- uios.uio_resid = CISTPL_BUFSIZE;
- uios.uio_segflg = UIO_SYSSPACE;
- uios.uio_rw = UIO_READ;
- uios.uio_procp = 0;
-
- /*
- * Read tuples one at a time into buf. Sucks, but it only happens once.
- * XXX - If the stuff we need isn't in attribute memory, or (worse yet)
- * XXX - attribute memory isn't mapped, we're FUBAR. Maybe need to do an
- * XXX - ioctl on the card device and follow links?
- */
- if ((rc = cdevsw[CARD_MAJOR]->d_read(makedev(CARD_MAJOR, devi->slt->slotnum), &uios, 0)) == 0) {
-
- switch (CISTPL_TYPE(buf)) {
-
- case 0x15: /* Grab version string (needed to ID some weird CE2's) */
-#ifdef XE_DEBUG
- printf("xe%d: Got version string (0x15)\n", unit);
-#endif
- for (i = 0; i < CISTPL_LEN(buf); ver_str[i] = CISTPL_DATA(buf, i++));
- ver_str[i] = '\0';
- ver_str[CISTPL_BUFSIZE>>1 - 1] = CISTPL_LEN(buf);
- success++;
- break;
-
- case 0x20: /* Figure out what type of card we have */
-#ifdef XE_DEBUG
- printf("xe%d: Got card ID (0x20)\n", unit);
-#endif
- vendor = CISTPL_DATA(buf, 0) + (CISTPL_DATA(buf, 1) << 8);
- rev = CISTPL_DATA(buf, 2);
- media = CISTPL_DATA(buf, 3);
- prod = CISTPL_DATA(buf, 4);
-
- switch (vendor) { /* Get vendor ID */
- case 0x0105:
- scp->vendor = "Xircom"; break;
- case 0x0138:
- case 0x0183:
- scp->vendor = "Compaq"; break;
- case 0x0089:
- scp->vendor = "Intel"; break;
- default:
- scp->vendor = "Unknown";
- }
-
- if (!((prod & 0x40) && (media & 0x01))) {
-#ifdef XE_DEBUG
- printf("xe%d: Not a PCMCIA Ethernet card!\n", unit);
-#endif
- rc = ENODEV; /* Not a PCMCIA Ethernet device */
- }
- else {
- if (media & 0x10) { /* Ethernet/modem cards */
-#ifdef XE_DEBUG
- printf("xe%d: Card is Ethernet/modem combo\n", unit);
-#endif
- scp->modem = 1;
- switch (prod & 0x0f) {
- case 1:
- scp->card_type = "CEM"; break;
- case 2:
- scp->card_type = "CEM2"; break;
- case 3:
- scp->card_type = "CEM3"; break;
- case 4:
- scp->card_type = "CEM33"; break;
- case 5:
- scp->ce3 = 1;
- scp->card_type = "CEM56M"; break;
- case 6:
- scp->ce3 = 1;
- scp->cem56 = 1;
- scp->card_type = "CEM56"; break;
- default:
- rc = ENODEV;
- }
- }
- else { /* Ethernet-only cards */
-#ifdef XE_DEBUG
- printf("xe%d: Card is Ethernet only\n", unit);
-#endif
- switch (prod & 0x0f) {
- case 1:
- scp->card_type = "CE"; break;
- case 2:
- scp->card_type = "CE2"; break;
- case 3:
- scp->ce3 = 1;
- scp->card_type = "CE3"; break;
- default:
- rc = ENODEV;
- }
- }
- }
- success++;
- break;
-
- case 0x22: /* Get MAC address */
-#ifdef XE_DEBUG
- printf("xe%d: Got MAC address (0x22)\n", unit);
-#endif
- if ((CISTPL_LEN(buf) == 8) &&
- (CISTPL_DATA(buf, 0) == 0x04) &&
- (CISTPL_DATA(buf, 1) == ETHER_ADDR_LEN)) {
- for (i = 0; i < ETHER_ADDR_LEN; scp->arpcom.ac_enaddr[i] = CISTPL_DATA(buf, i+2), i++);
- }
- success++;
- break;
- default:
- }
- }
-
- /* Skip to next tuple */
- offs += ((CISTPL_LEN(buf) + 2) << 1);
-
- } while ((CISTPL_TYPE(buf) != 0xff) && (CISTPL_LEN(buf) != 0xff) && (rc == 0));
-
-
- /* Die now if something went wrong above */
- if ((rc != 0) || (success < 3)) {
- free(scp, M_DEVBUF);
- return rc;
- }
-
- /* Check for certain strange CE2's that look like CE's */
- if (strcmp(scp->card_type, "CE") == 0) {
- u_char len = ver_str[CISTPL_BUFSIZE>>1 - 1];
-#ifdef XE_DEBUG
- printf("xe%d: Checking for weird CE2 string\n", unit);
-#endif
- for (i = 0; i < len - 2; i++)
- if (bcmp("CE2", &ver_str[i], 3) == 0)
- scp->card_type = "CE2";
- }
-
- /* Fill in some private data */
- sca[unit] = scp;
- scp->dev = &devi->isahd;
- scp->crd = devi;
- scp->probe = 1; /* Do media auto-detect by default */
-
- /* Attempt to attach the device */
-#ifdef XE_DEBUG
- printf("xe%d: Attaching...\n", unit);
-#endif
- if (xe_attach(scp->dev) == 0) {
- sca[unit] = 0;
- free(scp, M_DEVBUF);
- return ENXIO;
- }
-
-#if NAPM > 0
- /* Establish APM hooks once device attached */
- scp->suspend_hook.ah_name = "xe_suspend";
- scp->suspend_hook.ah_fun = xe_suspend;
- scp->suspend_hook.ah_arg = (void *)unit;
- scp->suspend_hook.ah_order = APM_MIN_ORDER;
- apm_hook_establish(APM_HOOK_SUSPEND, &scp->suspend_hook);
- scp->resume_hook.ah_name = "xe_resume";
- scp->resume_hook.ah_fun = xe_resume;
- scp->resume_hook.ah_arg = (void *)unit;
- scp->resume_hook.ah_order = APM_MIN_ORDER;
- apm_hook_establish(APM_HOOK_RESUME, &scp->resume_hook);
-#endif /* NAPM > 0 */
-
- /* Success */
- return 0;
-}
-
-/*
- * The device entry is being removed, probably because someone ejected the
- * card. The interface should have been brought down manually before calling
- * this function; if not you may well lose packets. In any case, I shut down
- * the card and the interface, and hope for the best. The 'gone' flag is set,
- * so hopefully no-one else will try to access the missing card.
- */
-static void
-xe_card_unload(struct pccard_devinfo *devi) {
- struct xe_softc *scp;
- struct ifnet *ifp;
- int unit;
-
- unit = devi->isahd.id_unit;
- scp = sca[unit];
- ifp = &scp->arpcom.ac_if;
-
- if (scp->gone) {
- printf("xe%d: already unloaded\n", unit);
- return;
- }
-
- if_down(ifp);
- ifp->if_flags &= ~(IFF_RUNNING|IFF_OACTIVE);
- xe_stop(scp);
- scp->gone = 1;
-}
-
-
-/*
- * Card interrupt handler: should return true if the interrupt was for us, in
- * case we are sharing our IRQ line with other devices (this will probably be
- * the case for multifunction cards).
- *
- * This function is probably more complicated than it needs to be, as it
- * attempts to deal with the case where multiple packets get sent between
- * interrupts. This is especially annoying when working out the collision
- * stats. Not sure whether this case ever really happens or not (maybe on a
- * slow/heavily loaded machine?) so it's probably best to leave this like it
- * is.
- *
- * Note that the crappy PIO used to get packets on and off the card means that
- * you will spend a lot of time in this routine -- I can get my P150 to spend
- * 90% of its time servicing interrupts if I really hammer the network. Could
- * fix this, but then you'd start dropping/losing packets. The moral of this
- * story? If you want good network performance _and_ some cycles left over to
- * get your work done, don't buy a Xircom card. Or convince them to tell me
- * how to do memory-mapped I/O :)
- */
-static int
-xe_card_intr(struct pccard_devinfo *devi) {
- struct xe_softc *scp;
- struct ifnet *ifp;
- int unit, result;
- u_int16_t rx_bytes, rxs, txs;
- u_int8_t psr, isr, esr, rsr;
-
- unit = devi->isahd.id_unit;
- scp = sca[unit];
- ifp = &scp->arpcom.ac_if;
- rx_bytes = 0; /* Bytes received on this interrupt */
- result = 0; /* Set true if the interrupt is for us */
-
- if (scp->gone)
- return 0;
-
- if (scp->ce3) {
- XE_OUTB(XE_CR, 0); /* Disable interrupts */
- }
-
- psr = XE_INB(XE_PSR); /* Stash the current register page */
-
- /*
- * Read ISR to see what caused this interrupt. Note that this clears the
- * ISR on CE2 type cards.
- */
- if ((isr = XE_INB(XE_ISR)) && isr != 0xff) {
-
- result = 1; /* This device did generate an int */
- esr = XE_INB(XE_ESR); /* Read the other status registers */
- XE_SELECT_PAGE(0x40);
- rxs = XE_INB(XE_RXS0);
- XE_OUTB(XE_RXS0, ~rxs & 0xff);
- txs = XE_INB(XE_TXS0);
- txs |= XE_INB(XE_TXS1) << 8;
- XE_OUTB(XE_TXS0, 0);
- XE_OUTB(XE_TXS1, 0);
- XE_SELECT_PAGE(0);
-
-#if XE_DEBUG > 3
- printf("xe%d: ISR=%#2.2x ESR=%#2.2x RXS=%#2.2x TXS=%#4.4x\n", unit, isr, esr, rxs, txs);
-#endif
-
- /*
- * Handle transmit interrupts
- */
- if (isr & XE_ISR_TX_PACKET) {
- u_int8_t new_ptr, sent;
-
- if ((new_ptr = XE_INB(XE_PTR)) < scp->tx_ptr) /* Update packet count */
- sent = (0xff - scp->tx_ptr) + new_ptr; /* PTR rolled over */
- else
- sent = new_ptr - scp->tx_ptr;
-
- if (sent > 0) { /* Packets sent since last interrupt */
- scp->tx_ptr = new_ptr;
- scp->tx_queued -= sent;
- ifp->if_opackets += sent;
- ifp->if_collisions += scp->tx_collisions;
-
- /*
- * Collision stats are a PITA. If multiples frames have been sent, we
- * distribute any outstanding collision count equally amongst them.
- * However, if we're missing interrupts we're quite likely to also
- * miss some collisions; thus the total count will be off anyway.
- * Likewise, if we miss a frame dropped due to excessive collisions
- * any outstanding collisions count will be held against the next
- * frame to be successfully sent. Hopefully it averages out in the
- * end!
- * XXX - This will screw up if tx_collisions/sent > 14. FIX IT!
- */
- switch (scp->tx_collisions) {
- case 0:
- break;
- case 1:
- scp->mibdata.dot3StatsSingleCollisionFrames++;
- scp->mibdata.dot3StatsCollFrequencies[0]++;
- break;
- default:
- if (sent == 1) {
- scp->mibdata.dot3StatsMultipleCollisionFrames++;
- scp->mibdata.dot3StatsCollFrequencies[scp->tx_collisions-1]++;
- }
- else { /* Distribute across multiple frames */
- scp->mibdata.dot3StatsMultipleCollisionFrames += sent;
- scp->mibdata.
- dot3StatsCollFrequencies[scp->tx_collisions/sent] += sent - scp->tx_collisions%sent;
- scp->mibdata.
- dot3StatsCollFrequencies[scp->tx_collisions/sent + 1] += scp->tx_collisions%sent;
- }
- }
- scp->tx_collisions = 0;
- }
- ifp->if_timer = 0;
- ifp->if_flags &= ~IFF_OACTIVE;
- }
- if (txs & 0x0002) { /* Excessive collisions (packet dropped) */
- ifp->if_collisions += 16;
- ifp->if_oerrors++;
- scp->tx_collisions = 0;
- scp->mibdata.dot3StatsExcessiveCollisions++;
- scp->mibdata.dot3StatsMultipleCollisionFrames++;
- scp->mibdata.dot3StatsCollFrequencies[15]++;
- XE_OUTB(XE_CR, XE_CR_RESTART_TX);
- }
- if (txs & 0x0040) /* Transmit aborted -- probably collisions */
- scp->tx_collisions++;
-
-
- /*
- * Handle receive interrupts
- */
- while ((esr = XE_INB(XE_ESR)) & XE_ESR_FULL_PKT_RX) {
-
- if ((rsr = XE_INB(XE_RSR)) & XE_RSR_RX_OK) {
- struct ether_header *ehp;
- struct mbuf *mbp;
- u_int16_t len;
-
- len = XE_INW(XE_RBC);
-
- if (len == 0)
- continue;
-
-#if 0
- /*
- * Limit the amount of time we spend in this loop, dropping packets if
- * necessary. The Linux code does this with considerably more
- * finesse, adjusting the threshold dynamically.
- */
- if ((rx_bytes += len) > 22000) {
- ifp->if_iqdrops++;
- scp->mibData.dot3StatsMissedFrames++;
- XE_OUTW(XE_DOR, 0x8000);
- continue;
- }
-#endif
-
- if (len & 0x01)
- len++;
-
- MGETHDR(mbp, M_DONTWAIT, MT_DATA); /* Allocate a header mbuf */
- if (mbp != NULL) {
- mbp->m_pkthdr.rcvif = ifp;
- mbp->m_pkthdr.len = mbp->m_len = len;
-
- /*
- * If the mbuf header isn't big enough for the packet, attach an
- * mbuf cluster to hold it. The +2 is to allow for the nasty little
- * alignment hack below.
- */
- if (len + 2 > MHLEN) {
- MCLGET(mbp, M_DONTWAIT);
- if ((mbp->m_flags & M_EXT) == 0) {
- m_freem(mbp);
- mbp = NULL;
- }
- }
- }
-
- if (mbp != NULL) {
- /*
- * The Ethernet header is 14 bytes long; thus the actual packet data
- * won't be 32-bit aligned when it's dumped into the mbuf. We
- * offset everything by 2 bytes to fix this. Apparently the
- * alignment is important for NFS, damn its eyes.
- */
- mbp->m_data += 2;
- ehp = mtod(mbp, struct ether_header *);
-
- /*
- * Now get the packet, including the Ethernet header and trailer (?)
- * We use programmed I/O, because we don't know how to do shared
- * memory with these cards. So yes, it's real slow, and heavy on
- * the interrupts (CPU on my P150 maxed out at ~950KBps incoming).
- */
- if (scp->srev == 0) { /* Workaround a bug in old cards */
- u_short rhs;
-
- XE_SELECT_PAGE(5);
- rhs = XE_INW(XE_RHS);
- XE_SELECT_PAGE(0);
-
- rhs += 3; /* Skip control info */
-
- if (rhs >= 0x8000)
- rhs = 0;
-
- if (rhs + len > 0x8000) {
- int i;
-
- /*
- * XXX - this i-- seems very wrong, but it's what the Linux guys
- * XXX - do. Need someone with an old CE2 to test this for me.
- */
- for (i = 0; i < len; i--, rhs++) {
- ((char *)ehp)[i] = XE_INB(XE_EDP);
- if (rhs = 0x8000) {
- rhs = 0;
- i--;
- }
- }
- }
- else
- insw(scp->dev->id_iobase+XE_EDP, ehp, len >> 1);
- }
- else
- insw(scp->dev->id_iobase+XE_EDP, ehp, len >> 1);
-
-#if NBPFILTER > 0
- /*
- * Check if there's a BPF listener on this interface. If so, hand
- * off the raw packet to bpf.
- */
- if (ifp->if_bpf) {
- bpf_mtap(ifp, mbp);
-
- /*
- * Note that the interface cannot be in promiscuous mode if there
- * are no BPF listeners. And if we are in promiscuous mode, we
- * have to check if this packet is really ours.
- */
- if ((ifp->if_flags & IFF_PROMISC) &&
- bcmp(ehp->ether_dhost, scp->arpcom.ac_enaddr, sizeof(ehp->ether_dhost)) != 0 &&
- (rsr & XE_RSR_PHYS_PKT)) {
- m_freem(mbp);
- mbp = NULL;
- }
- }
-#endif /* NBPFILTER > 0 */
-
- if (mbp != NULL) {
- mbp->m_pkthdr.len = mbp->m_len = len - ETHER_HDR_LEN;
- mbp->m_data += ETHER_HDR_LEN; /* Strip off Ethernet header */
- ether_input(ifp, ehp, mbp); /* Send the packet on its way */
- ifp->if_ipackets++; /* Success! */
- XE_OUTW(XE_DOR, 0x8000); /* skip_rx_packet command */
- }
- }
- }
- else if (rsr & XE_RSR_LONG_PKT) { /* Packet length >1518 bytes */
- scp->mibdata.dot3StatsFrameTooLongs++;
- ifp->if_ierrors++;
- }
- else if (rsr & XE_RSR_CRC_ERR) { /* Bad checksum on packet */
- scp->mibdata.dot3StatsFCSErrors++;
- ifp->if_ierrors++;
- }
- else if (rsr & XE_RSR_ALIGN_ERR) { /* Packet alignment error */
- scp->mibdata.dot3StatsAlignmentErrors++;
- ifp->if_ierrors++;
- }
- }
- if (rxs & 0x10) { /* Receiver overrun */
- scp->mibdata.dot3StatsInternalMacReceiveErrors++;
- ifp->if_ierrors++;
- XE_OUTB(XE_CR, XE_CR_CLEAR_OVERRUN);
- }
- }
-
- XE_SELECT_PAGE(psr); /* Restore saved page */
- XE_OUTB(XE_CR, XE_CR_ENABLE_INTR); /* Re-enable interrupts */
-
- /* XXX - force an int here, instead of dropping packets? */
- /* XXX - XE_OUTB(XE_CR, XE_CR_ENABLE_INTR|XE_CE_FORCE_INTR); */
-
- return result;
-}
@@ -2113,18 +2185,11 @@ xe_card_intr(struct pccard_devinfo *devi) {
*/
static int
xe_suspend(void *xunit) {
- struct xe_softc *scp;
- struct ifnet *ifp;
- int unit;
-
- unit = (int)xunit;
- scp = sca[unit];
- ifp = &scp->arpcom.ac_if;
+ struct xe_softc *scp = sca[(int)xunit];
#ifdef XE_DEBUG
- printf("xe%d: APM suspend\n", unit);
+ printf("xe%d: APM suspend\n", scp->unit);
#endif
- if_down(ifp);
return 0;
}
@@ -2133,21 +2198,16 @@ xe_suspend(void *xunit) {
*/
static int
xe_resume(void *xunit) {
- struct xe_softc *scp;
- int unit;
-
- unit = (int)xunit;
- scp = sca[unit];
+ struct xe_softc *scp = sca[(int)xunit];
#ifdef XE_DEBUG
- printf("xe%d: APM resume\n", unit);
+ printf("xe%d: APM resume\n", scp->unit);
#endif
return 0;
}
-#endif /* NAPM > 0 */
+#endif /* NAPM > 0 */
#endif /* NCARD > 0 */
-
#endif /* NXE > 0 */
diff --git a/sys/dev/pccard/if_xereg.h b/sys/dev/pccard/if_xereg.h
index 9dcdf31b15fd..07cb0b1bbb59 100644
--- a/sys/dev/pccard/if_xereg.h
+++ b/sys/dev/pccard/if_xereg.h
@@ -23,13 +23,13 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * $Id: if_xereg.h,v 1.2 1999/01/24 22:15:30 root Exp $
+ * $Id: if_xereg.h,v 1.3 1999/02/22 14:00:53 root Exp $
*/
/*
* Register definitions for Xircom CreditCard Ethernet adapters. See if_xe.c
* for details of supported hardware. Adapted from Werner Koch's 'xirc2ps'
- * driver for Linux.
+ * driver for Linux and the FreeBSD 'xl' driver (for the MII support).
*/
#include "xe.h"
@@ -199,88 +199,98 @@
*/
+
/*
- * MII/PHY defines adapted from the xl driver. These need cleaning up a
- * little if we end up using them.
+ * Definitions for the Micro Linear ML6692 100Base-TX PHY, which handles the
+ * 100Mbit functionality of CE3 type cards, including media autonegotiation.
+ * It appears to be mostly compatible with the National Semiconductor
+ * DP83840A, but with a much smaller register set. Please refer to the data
+ * sheets for these devices for the definitive word on what all this stuff
+ * means :)
+ *
+ * Note that the ML6692 has no 10Mbit capability -- that is handled by another
+ * chip that we don't know anything about.
+ *
+ * Most of these definitions were adapted from the xl driver.
+ */
+
+/*
+ * Masks for the MII-related bits in GPR2. For some reason read and write
+ * data are on separate bits.
*/
#define XE_MII_CLK 0x01
#define XE_MII_DIR 0x08
#define XE_MII_WRD 0x02
#define XE_MII_RDD 0x20
+
+/*
+ * MII command (etc) bit strings.
+ */
#define XE_MII_STARTDELIM 0x01
#define XE_MII_READOP 0x02
#define XE_MII_WRITEOP 0x01
#define XE_MII_TURNAROUND 0x02
-#define XE_MII_SET(x) XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) | (x))
-#define XE_MII_CLR(x) XE_OUTB(XE_GPR2, (XE_INB(XE_GPR2) | 0x04) & ~(x))
-
-#define XL_PHY_GENCTL 0x00
-#define XL_PHY_GENSTS 0x01
-#define XL_PHY_VENID 0x02
-#define XL_PHY_DEVID 0x03
-#define XL_PHY_ANAR 0x04
-#define XL_PHY_LPAR 0x05
-#define XL_PHY_ANER 0x06
-
-#define PHY_ANAR_NEXTPAGE 0x8000
-#define PHY_ANAR_RSVD0 0x4000
-#define PHY_ANAR_TLRFLT 0x2000
-#define PHY_ANAR_RSVD1 0x1000
-#define PHY_ANAR_RSVD2 0x0800
-#define PHY_ANAR_RSVD3 0x0400
-#define PHY_ANAR_100BT4 0x0200
-#define PHY_ANAR_100BTXFULL 0x0100
-#define PHY_ANAR_100BTXHALF 0x0080
-#define PHY_ANAR_10BTFULL 0x0040
-#define PHY_ANAR_10BTHALF 0x0020
-#define PHY_ANAR_PROTO4 0x0010
+/*
+ * PHY registers.
+ */
+#define PHY_BMCR 0x00 /* Basic Mode Control Register */
+#define PHY_BMSR 0x01 /* Basic Mode Status Register */
+#define PHY_ANAR 0x04 /* Auto-Negotiation Advertisment Register */
+#define PHY_LPAR 0x05 /* Auto-Negotiation Link Partner Ability Register */
+#define PHY_ANER 0x06 /* Auto-Negotiation Expansion Register */
+
+#define PHY_BMCR_RESET 0x8000 /* Soft reset PHY. Self-clearing */
+#define PHY_BMCR_LOOPBK 0x4000 /* Enable loopback */
+#define PHY_BMCR_SPEEDSEL 0x2000 /* 1=100Mbps, 0=10Mbps */
+#define PHY_BMCR_AUTONEGENBL 0x1000 /* Auto-negotiation enabled */
+#define PHY_BMCR_ISOLATE 0x0400 /* Isolate ML6692 from MII */
+#define PHY_BMCR_AUTONEGRSTR 0x0200 /* Restart auto-negotiation. Self-clearing */
+#define PHY_BMCR_DUPLEX 0x0100 /* Full duplex operation */
+#define PHY_BMCR_COLLTEST 0x0080 /* Enable collision test */
+
+#define PHY_BMSR_100BT4 0x8000 /* 100Base-T4 capable */
+#define PHY_BMSR_100BTXFULL 0x4000 /* 100Base-TX full duplex capable */
+#define PHY_BMSR_100BTXHALF 0x2000 /* 100Base-TX half duplex capable */
+#define PHY_BMSR_10BTFULL 0x1000 /* 10Base-T full duplex capable */
+#define PHY_BMSR_10BTHALF 0x0800 /* 10Base-T half duplex capable */
+#define PHY_BMSR_AUTONEGCOMP 0x0020 /* Auto-negotiation complete */
+#define PHY_BMSR_CANAUTONEG 0x0008 /* Auto-negotiation supported */
+#define PHY_BMSR_LINKSTAT 0x0004 /* Link is up */
+#define PHY_BMSR_EXTENDED 0x0001 /* Extended register capabilities */
+
+#define PHY_ANAR_NEXTPAGE 0x8000 /* Additional link code word pages */
+#define PHY_ANAR_TLRFLT 0x2000 /* Remote wire fault detected */
+#define PHY_ANAR_100BT4 0x0200 /* 100Base-T4 capable */
+#define PHY_ANAR_100BTXFULL 0x0100 /* 100Base-TX full duplex capable */
+#define PHY_ANAR_100BTXHALF 0x0080 /* 100Base-TX half duplex capable */
+#define PHY_ANAR_10BTFULL 0x0040 /* 10Base-T full duplex capable */
+#define PHY_ANAR_10BTHALF 0x0020 /* 10Base-T half duplex capable */
+#define PHY_ANAR_PROTO4 0x0010 /* Protocol selection (00001 = 802.3) */
#define PHY_ANAR_PROTO3 0x0008
#define PHY_ANAR_PROTO2 0x0004
#define PHY_ANAR_PROTO1 0x0002
#define PHY_ANAR_PROTO0 0x0001
-/*
- * PHY BMCR Basic Mode Control Register
- */
-#define PHY_BMCR 0x00
-#define PHY_BMCR_RESET 0x8000
-#define PHY_BMCR_LOOPBK 0x4000
-#define PHY_BMCR_SPEEDSEL 0x2000
-#define PHY_BMCR_AUTONEGENBL 0x1000
-#define PHY_BMCR_RSVD0 0x0800 /* write as zero */
-#define PHY_BMCR_ISOLATE 0x0400
-#define PHY_BMCR_AUTONEGRSTR 0x0200
-#define PHY_BMCR_DUPLEX 0x0100
-#define PHY_BMCR_COLLTEST 0x0080
-#define PHY_BMCR_RSVD1 0x0040 /* write as zero, don't care */
-#define PHY_BMCR_RSVD2 0x0020 /* write as zero, don't care */
-#define PHY_BMCR_RSVD3 0x0010 /* write as zero, don't care */
-#define PHY_BMCR_RSVD4 0x0008 /* write as zero, don't care */
-#define PHY_BMCR_RSVD5 0x0004 /* write as zero, don't care */
-#define PHY_BMCR_RSVD6 0x0002 /* write as zero, don't care */
-#define PHY_BMCR_RSVD7 0x0001 /* write as zero, don't care */
-
-/*
- * PHY, BMSR Basic Mode Status Register
- */
-#define PHY_BMSR 0x01
-#define PHY_BMSR_100BT4 0x8000
-#define PHY_BMSR_100BTXFULL 0x4000
-#define PHY_BMSR_100BTXHALF 0x2000
-#define PHY_BMSR_10BTFULL 0x1000
-#define PHY_BMSR_10BTHALF 0x0800
-#define PHY_BMSR_RSVD1 0x0400 /* write as zero, don't care */
-#define PHY_BMSR_RSVD2 0x0200 /* write as zero, don't care */
-#define PHY_BMSR_RSVD3 0x0100 /* write as zero, don't care */
-#define PHY_BMSR_RSVD4 0x0080 /* write as zero, don't care */
-#define PHY_BMSR_MFPRESUP 0x0040
-#define PHY_BMSR_AUTONEGCOMP 0x0020
-#define PHY_BMSR_REMFAULT 0x0010
-#define PHY_BMSR_CANAUTONEG 0x0008
-#define PHY_BMSR_LINKSTAT 0x0004
-#define PHY_BMSR_JABBER 0x0002
-#define PHY_BMSR_EXTENDED 0x0001
+#define PHY_LPAR_NEXTPAGE 0x8000 /* Additional link code word pages */
+#define PHY_LPAR_LPACK 0x4000 /* Link partner acknowledged receipt */
+#define PHY_LPAR_TLRFLT 0x2000 /* Remote wire fault detected */
+#define PHY_LPAR_100BT4 0x0200 /* 100Base-T4 capable */
+#define PHY_LPAR_100BTXFULL 0x0100 /* 100Base-TX full duplex capable */
+#define PHY_LPAR_100BTXHALF 0x0080 /* 100Base-TX half duplex capable */
+#define PHY_LPAR_10BTFULL 0x0040 /* 10Base-T full duplex capable */
+#define PHY_LPAR_10BTHALF 0x0020 /* 10Base-T half duplex capable */
+#define PHY_LPAR_PROTO4 0x0010 /* Protocol selection (00001 = 802.3) */
+#define PHY_LPAR_PROTO3 0x0008
+#define PHY_LPAR_PROTO2 0x0004
+#define PHY_LPAR_PROTO1 0x0002
+#define PHY_LPAR_PROTO0 0x0001
+
+#define PHY_ANER_MLFAULT 0x0010 /* More than one link is up! */
+#define PHY_ANER_LPNPABLE 0x0008 /* Link partner supports next page */
+#define PHY_ANER_NPABLE 0x0004 /* Local port supports next page */
+#define PHY_ANER_PAGERX 0x0002 /* Page received */
+#define PHY_ANER_LPAUTONEG 0x0001 /* Link partner can auto-negotiate */
#endif /* NXE > 0 */