summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authormdodd <mdodd@FreeBSD.org>2003-03-28 02:15:01 +0000
committermdodd <mdodd@FreeBSD.org>2003-03-28 02:15:01 +0000
commit41a48ccd9eddb0fca56b439efaa5db84a4dcfb37 (patch)
treeaedac85e43b8ac310c9dee10c682733629dafc65
parent2b3cee5134417f2ebe1bf390715f841f8fc7bb55 (diff)
downloadFreeBSD-src-41a48ccd9eddb0fca56b439efaa5db84a4dcfb37.zip
FreeBSD-src-41a48ccd9eddb0fca56b439efaa5db84a4dcfb37.tar.gz
Multicast support, gleaned from NetBSD's if_iy driver.
Submitted by: Craig Boston <craig@xfoil.gank.org>
-rw-r--r--sys/dev/ex/if_ex.c106
-rw-r--r--sys/dev/ex/if_exreg.h2
2 files changed, 103 insertions, 5 deletions
diff --git a/sys/dev/ex/if_ex.c b/sys/dev/ex/if_ex.c
index f7458af..7321663 100644
--- a/sys/dev/ex/if_ex.c
+++ b/sys/dev/ex/if_ex.c
@@ -35,6 +35,7 @@
*
* Revision history:
*
+ * dd-mmm-yyyy: Multicast support ported from NetBSD's if_iy driver.
* 30-Oct-1996: first beta version. Inet and BPF supported, but no multicast.
*/
@@ -54,6 +55,7 @@
#include <net/if.h>
#include <net/if_arp.h>
+#include <net/if_dl.h>
#include <net/if_media.h>
#include <net/ethernet.h>
#include <net/bpf.h>
@@ -103,6 +105,7 @@ static void ex_ifmedia_sts (struct ifnet *, struct ifmediareq *);
static int ex_get_media (u_int32_t iobase);
static void ex_reset (struct ex_softc *);
+static void ex_setmulti (struct ex_softc *);
static void ex_tx_intr (struct ex_softc *);
static void ex_rx_intr (struct ex_softc *);
@@ -235,7 +238,7 @@ ex_attach(device_t dev)
ifp->if_unit = unit;
ifp->if_name = "ex";
ifp->if_mtu = ETHERMTU;
- ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST /* XXX not done yet. | IFF_MULTICAST */;
+ ifp->if_flags = IFF_SIMPLEX | IFF_BROADCAST | IFF_MULTICAST;
ifp->if_output = ether_output;
ifp->if_start = ex_start;
ifp->if_ioctl = ex_ioctl;
@@ -349,6 +352,8 @@ ex_init(void *xsc)
ifp->if_flags &= ~IFF_OACTIVE;
DODEBUG(Status, printf("OIDLE init\n"););
+ ex_setmulti(sc);
+
/*
* Final reset of the board, and enable operation.
*/
@@ -809,11 +814,9 @@ ex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data)
break;
#endif
case SIOCADDMULTI:
- DODEBUG(Start_End, printf("SIOCADDMULTI"););
case SIOCDELMULTI:
- DODEBUG(Start_End, printf("SIOCDELMULTI"););
- /* XXX Support not done yet. */
- error = EINVAL;
+ ex_init(sc);
+ error = 0;
break;
case SIOCSIFMEDIA:
case SIOCGIFMEDIA:
@@ -831,6 +834,99 @@ ex_ioctl(register struct ifnet *ifp, u_long cmd, caddr_t data)
return(error);
}
+static void
+ex_setmulti(struct ex_softc *sc)
+{
+ struct ifnet *ifp;
+ struct ifmultiaddr *maddr;
+ u_int16_t *addr;
+ int iobase = sc->iobase;
+ int count;
+ int timeout, status;
+
+ ifp = &sc->arpcom.ac_if;
+
+ count = 0;
+ TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) {
+ if (maddr->ifma_addr->sa_family != AF_LINK)
+ continue;
+ count++;
+ }
+
+ if ((ifp->if_flags & IFF_PROMISC) || (ifp->if_flags & IFF_ALLMULTI)
+ || count > 63) {
+ /* Interface is in promiscuous mode or there are too many
+ * multicast addresses for the card to handle */
+ outb(iobase + CMD_REG, Bank2_Sel);
+ outb(iobase + REG2, inb(iobase + REG2) | Promisc_Mode);
+ outb(iobase + REG3, inb(iobase + REG3));
+ outb(iobase + CMD_REG, Bank0_Sel);
+ }
+ else if ((ifp->if_flags & IFF_MULTICAST) && (count > 0)) {
+ /* Program multicast addresses plus our MAC address
+ * into the filter */
+ outb(iobase + CMD_REG, Bank2_Sel);
+ outb(iobase + REG2, inb(iobase + REG2) | Multi_IA);
+ outb(iobase + REG3, inb(iobase + REG3));
+ outb(iobase + CMD_REG, Bank0_Sel);
+
+ /* Borrow space from TX buffer; this should be safe
+ * as this is only called from ex_init */
+
+ outw(iobase + HOST_ADDR_REG, sc->tx_lower_limit);
+ outw(iobase + IO_PORT_REG, MC_Setup_CMD);
+ outw(iobase + IO_PORT_REG, 0);
+ outw(iobase + IO_PORT_REG, 0);
+ outw(iobase + IO_PORT_REG, (count + 1) * 6);
+
+ TAILQ_FOREACH(maddr, &ifp->if_multiaddrs, ifma_link) {
+ if (maddr->ifma_addr->sa_family != AF_LINK)
+ continue;
+
+ addr = (u_int16_t*)LLADDR((struct sockaddr_dl *)
+ maddr->ifma_addr);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+ }
+
+ /* Program our MAC address as well */
+ /* XXX: Is this necessary? The Linux driver does this
+ * but the NetBSD driver does not */
+ addr = (u_int16_t*)(&sc->arpcom.ac_enaddr);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+ outw(iobase + IO_PORT_REG, *addr++);
+
+ inw(iobase + IO_PORT_REG);
+ outw(iobase + XMT_BAR, sc->tx_lower_limit);
+ outb(iobase + CMD_REG, MC_Setup_CMD);
+
+ sc->tx_head = sc->tx_lower_limit;
+ sc->tx_tail = sc->tx_head + XMT_HEADER_LEN + (count + 1) * 6;
+
+ for (timeout=0; timeout<100; timeout++) {
+ DELAY(2);
+ if ((inb(iobase + STATUS_REG) & Exec_Int) == 0)
+ continue;
+
+ status = inb(iobase + CMD_REG);
+ outb(iobase + STATUS_REG, Exec_Int);
+ break;
+ }
+
+ sc->tx_head = sc->tx_tail;
+ }
+ else
+ {
+ /* No multicast or promiscuous mode */
+ outb(iobase + CMD_REG, Bank2_Sel);
+ outb(iobase + REG2, inb(iobase + REG2) & 0xDE);
+ /* ~(Multi_IA | Promisc_Mode) */
+ outb(iobase + REG3, inb(iobase + REG3));
+ outb(iobase + CMD_REG, Bank0_Sel);
+ }
+}
static void
ex_reset(struct ex_softc *sc)
diff --git a/sys/dev/ex/if_exreg.h b/sys/dev/ex/if_exreg.h
index a6c957c..6e986e2 100644
--- a/sys/dev/ex/if_exreg.h
+++ b/sys/dev/ex/if_exreg.h
@@ -119,8 +119,10 @@
#define Disc_Bad_Fr 0x80
#define Tx_Chn_ErStp 0x40
#define Tx_Chn_Int_Md 0x20
+#define Multi_IA 0x20
#define No_SA_Ins 0x10
#define RX_CRC_InMem 0x04
+#define Promisc_Mode 0x01
#define BNC_bit 0x20
#define TPE_bit 0x04
#define I_ADDR_REG0 4
OpenPOWER on IntegriCloud