Subject: Re: kern/13797: hme driver does not compile on i386
To: None <gnats-bugs@gnats.netbsd.org, tech-kern@netbsd.org>
From: ITOH Yasufumi <itohy@netbsd.org>
List: tech-kern
Date: 12/23/2002 02:07:49
In article <200108272230.f7RMUv505866@fuji.augustsson.net>
augustss@fuji.augustsson.net writes:

> >Description:
> 	If you try to include the hme driver in a i386 (and others, I
> 	presume) kernel it fails.  The reason is that if_hme_pci.c
> 	refers to the function myetheraddr() which seems to be SUN
> 	specific.

I recently got a PCI HME device (SunSwift) and trying to use it.

The PCI HME device is an Sun PCIO chip, which contains two PCI functions.
Reference: http://www.sun.com/processors/manuals/802-7837.pdf

	PCI bus --+- (function 0) EBus bridge - ROM
	          +- (function 1) HME

The EBus Boot PROM has FCode ROM, and has an Etheret address in it.
The HME device has ROM area, but it doesn't have any data (all 0xff).

I have make it works on Alpha by following changes.
I know this is kludgy.  It steals the Ethernet address
by looking at different device (EBus bridge) in HME match function,
and passes it via static variables.

Is this fix OK?  Do you have better fix?

Index: if_hme_pci.c
===================================================================
RCS file: /cvsroot/src/sys/dev/pci/if_hme_pci.c,v
retrieving revision 1.12
diff -p -u -r1.12 if_hme_pci.c
--- if_hme_pci.c	2002/10/02 16:51:26	1.12
+++ if_hme_pci.c	2002/12/22 16:17:01
@@ -58,6 +58,14 @@ __KERNEL_RCSID(0, "$NetBSD: if_hme_pci.c
 
 #include <dev/ic/hmevar.h>
 
+#ifndef HME_USE_LOCAL_MAC_ADDRESS
+#ifdef __sparc__
+#define HME_USE_LOCAL_MAC_ADDRESS	0	/* use system-wide address */
+#else
+#define HME_USE_LOCAL_MAC_ADDRESS	1
+#endif
+#endif
+
 struct hme_pci_softc {
 	struct	hme_softc	hsc_hme;	/* HME device */
 	bus_space_tag_t		hsc_memt;
@@ -71,6 +79,14 @@ void	hmeattach_pci __P((struct device *,
 CFATTACH_DECL(hme_pci, sizeof(struct hme_pci_softc),
     hmematch_pci, hmeattach_pci, NULL, NULL);
 
+#if HME_USE_LOCAL_MAC_ADDRESS
+/*
+ * local Ethernet address
+ */
+u_int8_t	hme_enaddr[ETHER_ADDR_LEN];
+int		hme_has_enaddr;
+#endif	/* HME_USE_LOCAL_MAC_ADDRESS */
+
 int
 hmematch_pci(parent, cf, aux)
 	struct device *parent;
@@ -78,11 +94,95 @@ hmematch_pci(parent, cf, aux)
 	void *aux;
 {
 	struct pci_attach_args *pa = aux;
+#if HME_USE_LOCAL_MAC_ADDRESS
+	bus_space_tag_t		romt;
+	bus_space_handle_t	romh;
+	bus_size_t		romsize;
+	unsigned char		buf[32];
+	int			dataoff, vpdoff;
+	struct pci_vpd		*vpd;
+	static const unsigned char promhdr[] = { 0x55, 0xaa };
+#define PROMHDR_PTR_DATA	0x18
+	static const unsigned char promdat[] = {
+		0x50, 0x43, 0x49, 0x52,		/* "PCIR" */
+		PCI_VENDOR_SUN & 0xff, PCI_VENDOR_SUN >> 8,
+		PCI_PRODUCT_SUN_HMENETWORK & 0xff,
+		PCI_PRODUCT_SUN_HMENETWORK >> 8
+	};
+#define PROMDATA_PTR_VPD	0x08
+#define PROMDATA_DATA2		0x0a		
+	static const unsigned char promdat2[] = {
+		0x18, 0x00,			/* structure length */
+		0x00,				/* structure revision */
+		0x00,				/* interface revision */
+		PCI_SUBCLASS_NETWORK_ETHERNET,	/* subclass code */
+		PCI_CLASS_NETWORK		/* class code */
+	};
+#endif	/* HME_USE_LOCAL_MAC_ADDRESS */
 
 	if (PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN && 
 	    PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_HMENETWORK)
 		return (1);
 
+#if HME_USE_LOCAL_MAC_ADDRESS
+#define PCI_EBUS2_BOOTROM	0x10
+	if (PCI_CLASS(pa->pa_class) == PCI_CLASS_BRIDGE &&
+	    PCI_VENDOR(pa->pa_id) == PCI_VENDOR_SUN &&
+	    (PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_EBUS ||
+	     PCI_PRODUCT(pa->pa_id) == PCI_PRODUCT_SUN_EBUSIII) &&
+	    pci_mapreg_map(pa, PCI_EBUS2_BOOTROM, PCI_MAPREG_TYPE_MEM,
+		BUS_SPACE_MAP_CACHEABLE | BUS_SPACE_MAP_PREFETCHABLE,
+		&romt, &romh, 0, &romsize) == 0) {
+
+		/*
+		 * Dig out VPD (vital product data) and steal Ethernet address.
+		 * The VPD of hme resides in the Boot PROM (PCI FCode) attached
+		 * to the EBus interface.  Wow!
+		 */
+
+		/* read PCI Expension PROM Header */
+		bus_space_read_region_1(romt, romh, 0, buf, sizeof buf);
+		if (memcmp(buf, promhdr, sizeof promhdr) == 0 &&
+		    (dataoff = (buf[PROMHDR_PTR_DATA] |
+			(buf[PROMHDR_PTR_DATA + 1] << 8))) >= 0x1c) {
+
+			/* read PCI Expension PROM Data */
+			bus_space_read_region_1(romt, romh, dataoff,
+			    buf, sizeof buf);
+			if (memcmp(buf, promdat, sizeof promdat) == 0 &&
+			    memcmp(buf + PROMDATA_DATA2, promdat2,
+				sizeof promdat2) == 0 &&
+			    (vpdoff = (buf[PROMDATA_PTR_VPD] |
+				(buf[PROMDATA_PTR_VPD + 1] << 8))) >= 0x1c) {
+
+				/* read PCI VPD */
+				bus_space_read_region_1(romt, romh,
+				    vpdoff, buf, sizeof buf);
+				vpd = (void *)(buf + 3);
+				if (PCI_VPDRES_ISLARGE(buf[0]) &&
+				    PCI_VPDRES_LARGE_NAME(buf[0]) ==
+					PCI_VPDRES_TYPE_VPD &&
+				    /* buf[1] == 0 && buf[2] == 9 && */ /* ? */
+				    vpd->vpd_key0 == 0x4E /* N */ &&
+				    vpd->vpd_key1 == 0x41 /* A */ &&
+				    vpd->vpd_len == ETHER_ADDR_LEN) {
+					/*
+					 * Ethernet address found
+					 */
+					memcpy(hme_enaddr, buf + 6,
+					    ETHER_ADDR_LEN);
+					hme_has_enaddr = 1;
+					printf("hme: read Ethernet address from EBus FCode PROM (%s)\n",
+					    ether_sprintf(buf + 6));
+				}
+			}
+		}
+		bus_space_unmap(romt, romh, romsize);
+		/* do not attach EBus bridge */
+		/* FALLTHROUGH */
+	}
+#endif	/* HME_USE_LOCAL_MAC_ADDRESS */
+
 	return (0);
 }
 
@@ -98,9 +198,10 @@ hmeattach_pci(parent, self, aux)
 	pcireg_t csr;
 	const char *intrstr;
 	int type;
-
+#ifdef __sparc__
 	/* XXX the following declarations should be elsewhere */
 	extern void myetheraddr __P((u_char *));
+#endif
 
 	printf(": Sun Happy Meal Ethernet, rev. %d\n",
 	    PCI_REVISION(pa->pa_class));
@@ -174,7 +275,16 @@ hmeattach_pci(parent, self, aux)
 		return;
 	}
 
-	myetheraddr(sc->sc_enaddr);
+#if HME_USE_LOCAL_MAC_ADDRESS
+	if (hme_has_enaddr)
+		memcpy(sc->sc_enaddr, hme_enaddr, ETHER_ADDR_LEN);
+#endif
+#ifdef __sparc__
+#if HME_USE_LOCAL_MAC_ADDRESS
+	else
+#endif
+		myetheraddr(sc->sc_enaddr);
+#endif
 
 	/*
 	 * Map and establish our interrupt.