Date: Fri, 20 Jan 2012 23:35:55 +0100 From: Stefan Bethke <stb@lassitu.de> To: FreeBSD Net <freebsd-net@freebsd.org> Subject: Ethernet Switch Framework, the other one Message-ID: <76C5AEDF-39BA-4824-B8F8-BBD451200EF2@lassitu.de>
next in thread | raw e-mail | index | archive | help
--Apple-Mail=_CE96FAC5-9AF0-44EF-BFDB-F3380CF6C0C4 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset=us-ascii If you have followed a number of threads on -embedded, -mips, and -arch, = you'll be aware that I've been busy attempting to come up with a switch = control interface of my own. The goal is to combine what Aleksandr has = done over the past couple of months with some of what I have come up = with. The reason I did start from scratch were some architectural concerns = that I had with Aleksandr's code. Instead of idle talk, I decided to do = a proof of concept implementation, and lo and behold, I did run into = some interesting problems. I'll reply to Aleksandr's post momentarily, = but first I wanted to get my code out for people to review, and use the = chance try and explain the problem space and why I decided to approach = it the way I did. The attached patches contain: - etherswitchcfg.patch the userland utility - miiproxy.patch a device that can act as a proxy between an ethernet = driver and miibus - etherswitch-complete: the above plus two switch drivers, a new kernel = config and related files. All that can also be obtained from = http://gitorious.org/~stb/freebsd/stb-adrianchadd-freebsd-work in the = branch work/ath. The target systems for these switch device drivers are typical WLAN = routers, which usually contain a one-chip switch controller. All of = them support port-based as well as .1q VLANs, as well as many other = interesting features. OpenWrt has a framework for configuring aspects = of these switch controllers, but the original firmware of these devices = usually does not expose the settings for the user. For my proof of concept, I've put together two drivers that know how to = configure an RTL8366RB and AR8x16 switches. The RTL8366RB is configured = through I2C. The system's ethernet interface is connected directly to a = switch port (back-to-back MAC connection via GMII). The Atheros family = of switch controllers is configured through an MDIO bus attachment; the = switch controller uses the frame format, but reassigns phy address bits = as register address bits. Both switch drivers implement the etherswitch_if.m interface, and act as = busses and automatically attach matching drivers as children. I've = written a simple cdev driver that allows the command line utility to = open a control device and submit ioctls that are translated into = etherswitch_if methods. newbus attachments look like this (arrow indicates parent attachment = with interface implemented) iicbus_if etherswitch_if ... <--- iicbus <--- rtl833rb <--- etherswitch mdio_if mdio_if etherswitch_if argemdio <--- mdio <--- arswitch <--- etherswitch All switch controller chips contain PHYs (older models have external = PHYs). These are controlled in the usual manner via MDIO. The MDIO = master is in the switch controller. Both switch drivers export the = miibus_if.m methods readreg and writereg, and attach an miibus for each = one of the switch ports that have a PHY. This allows the command line = utility to submit ifmedia ioctls to query and configure the individual = ports. I've gone through a number of iterations to make sure that the = interdependencies of the drivers are as limited as possible, and that = attachments happen automatically, without code nor hints, whereever = possible. MII Proxy In some of the systems, one system ethernet interface is connected to a = PHY inside the switch controller; the PHY acts completely independent = from the switch. However, the PHYs MDIO slave is connected not to the = ethernet controllers MDIO master, but to the switch MDIO master. This = means solving two problems: allowing a driver control both an ethernet = driver and use another MDIO bus driver to communicate with the PHY at = the same time. The miibus code unfortunately is extremely tailored to the one use case = that was present so far; I'll go into more detail about what was = stopping me from just using miibus directly in a separate post. To = leave miibus unmodified while maintaining functionality and not = reimplementing miibus or the phy drivers, I've decided to create a proxy = driver. This proxy driver solves two issues: be an appropriate parent = for miibus, and provide the capability to have a reference on a = different device (and call methods there) than the parent. A new bus = driver, mdio, provides the MDIO readreg and writereg methods separate = from the miibus driver, thus allowing automatic attachment of parent and = children. For a TL-MR3420 router, devinfo shows this device graph (filtered for = interface and switch components): nexus0 arge0 miiproxy0 miibus4 ukphy4 arge1 argemdio0 mdio0 arswitch0 miibus0 ukphy0 miibus1 ukphy1 miibus2 ukphy2 miibus3 ukphy3 etherswitch0 mdio1 mdioproxy1 mdioproxy0 arge(4) has been extended with a separate mdio host driver that has a = new mdio bus driver attached. The Atheros switch driver arswitch0 is = attached to that bus; the four switch ports with PHYs on them show up as = miibus0 to miibus3 and phy0 to phy3, respectively. arge0, which has the = MII connection (but not the MDIO connection) to PHY for in the switch = controller, has miiproxy0 attached to it, which in turn hosts miibus4 = and phy4. miiproxy0 has a hidden connection to mdioproxy1 that was = established through a hint on arge0; miiproxy0 uses mdioproxy1's parent = to issue readreg and writereg calls on behalf of miibus. Note = mdioproxy0 above which was automatically attached (but is not used). The only hint required to plug this together is hint.arge.0.mdio=3Dmdioproxy1 The simple and straightforward attachment follows the newbus design, and = allows additional drivers to be automatically attached by newbus. The = etherswitch driver provides one possible consumer of the etherswitch_if = API, but other are possible; for example, most switches allow .1d = control frames to be punted to the CPU port, so our STP code could be = used to construct a driver that implements spanning tree for a switch. Stefan --=20 Stefan Bethke <stb@lassitu.de> Fon +49 151 14070811 --Apple-Mail=_CE96FAC5-9AF0-44EF-BFDB-F3380CF6C0C4 Content-Disposition: attachment; filename=etherswitchcfg.patch Content-Type: application/octet-stream; name="etherswitchcfg.patch" Content-Transfer-Encoding: 7bit diff --git a/sbin/Makefile b/sbin/Makefile index f9ba4ca..0cb421f 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -20,6 +20,7 @@ SUBDIR=adjkerntz \ dump \ dumpfs \ dumpon \ + etherswitchcfg \ ffsinfo \ fsck \ fsck_ffs \ diff --git a/sbin/etherswitchcfg/Makefile b/sbin/etherswitchcfg/Makefile new file mode 100644 index 0000000..d21c88b --- /dev/null +++ b/sbin/etherswitchcfg/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 5.4 (Berkeley) 6/5/91 +# $FreeBSD: head/sbin/comcontrol/Makefile 198236 2009-10-19 16:00:24Z ru $ + +PROG= etherswitchcfg +MAN= etherswitchcfg.8 +SRCS= etherswitchcfg.c ifmedia.c +CFLAGS+= -I${.CURDIR}/../../sys + +.include <bsd.prog.mk> diff --git a/sbin/etherswitchcfg/etherswitchcfg.8 b/sbin/etherswitchcfg/etherswitchcfg.8 new file mode 100644 index 0000000..538950c --- /dev/null +++ b/sbin/etherswitchcfg/etherswitchcfg.8 @@ -0,0 +1,114 @@ +.\" $FreeBSD$ +.Dd December 15, 2011 +.Dt ETHERSWITCHCFG 8 +.Os +.Sh NAME +.Nm etherswitchcfg +.Nd configure a built-in Ethernet switch +.Sh SYNOPSIS +.Nm +.Op Fl "f control file" +.Ar info +.Nm +.Op Fl "f control file" +.Ar phy +.Ar phy.register[=value] +.Nm +.Op Fl "f control file" +.Ar port%d +.Ar command parameter +.Nm +.Op Fl "f control file" +.Ar reg +.Ar register[=value] +.Nm +.Op Fl "f control file" +.Ar vlangroup%d +.Ar command parameter +.Sh DESCRIPTION +The +.Nm +utility is used to configure an Ethernet switch built into the system. +.Nm +accepts a number of options: +.Bl -tag -width ".Fl f" -compact +.It Fl "f control file" +Specifies the +.Xr etherswitch 4 +control file that represents the switch to be configured. +It defaults to +.Li /dev/etherswitch0 . +.It Fl m +When reporting port information, also list available media options for +that port. +.It Fl v +Produce more verbose output. +Without this flag, lines that represent inactive or empty configuration +options are omitted. +.El +.Ss phy +The phy command provides access to the registers of the PHYs attached +to or integrated into the switch controller. +PHY registers are specified as phy.register, +where +.Ar phy +is usually the port number, and +.Ar register +is the register number. +Both can be provided as decimal, octal or hexadecimal numbers in any of the formats +understood by +.Xr strtol 4 . +To set the register value, use the form instance.register=value. +.Ss port +The port command selects one of the ports of the switch. +It supports the following commands: +.Bl -tag -width ".Ar vlangroup number" -compact +.It Ar vlangroup number +Sets the VLAN group number that is used to process incoming frames that are not tagged. +.It Ar media mediaspec +Specifies the physical media configuration to be configured for a port. +.It Ar mediaopt mediaoption +Specifies a list of media options for a port. See +.Xr ifconfig 8 +for details on +.Ar media and +.Ar mediaopt . +.El +.Ss reg +The reg command provides access to the registers of the switch controller. +.Ss vlangroup +The vlangroup command selects one of the VLAN groups for configuration. +It supports the following commands: +.Bl -tag -width ".Ar vlangroup" -compact +.It Ar vlan VID +Sets the VLAN ID (802.1q VID) for this VLAN group. +Frames transmitted on tagged member ports of this group will be tagged +with this VID. +Incoming frames carrying this tag will be forwarded according to the +configuration of this VLAN group. +.It Ar members port,... +Configures which ports are to be a member of this VLAN group. +The port numbers are given as a comma-separated list. +Each port can optionally be followed by +.Dq t +to indicate that frames on this port are tagged. +.El +.Sh FILES +.Bl -tag -width /dev/etherswitch? -compact +.It Pa /dev/etherswitch? +Control file for the ethernet switch driver. +.El +.Sh EXAMPLES +Configure VLAN group 1 with a VID of 2 and makes ports 0 and 5 members, +while excluding all other ports. +Port 5 will send and receive tagged frames, while port 0 will be untagged. +Incoming untagged frames on port 0 are assigned to vlangroup1. +.Dl # etherswitchcfg vlangroup1 vlan 2 members 0,5t port0 vlangroup 1 +.Sh SEE ALSO +.Xr etherswitch 4 +.Sh HISTORY +.Nm +first appeared in +.Fx 10.0 . +.Sh AUTHORS +.An Stefan Bethke diff --git a/sbin/etherswitchcfg/etherswitchcfg.c b/sbin/etherswitchcfg/etherswitchcfg.c new file mode 100644 index 0000000..e6129f3 --- /dev/null +++ b/sbin/etherswitchcfg/etherswitchcfg.c @@ -0,0 +1,511 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_media.h> +#include <dev/etherswitch/etherswitch.h> + +int get_media_subtype(int, const char *); +int get_media_mode(int, const char *); +int get_media_options(int, const char *); +int lookup_media_word(struct ifmedia_description *, const char *); +void print_media_word(int, int); +void print_media_word_ifconfig(int); + +/* some constants */ +#define IEEE802DOT1Q_VID_MAX 4094 +#define IFMEDIAREQ_NULISTENTRIES 256 + +enum cmdmode { + MODE_NONE = 0, + MODE_PORT, + MODE_VLANGROUP, + MODE_REGISTER, + MODE_PHYREG +}; + +struct cfg { + int fd; + int verbose; + int mediatypes; + const char *controlfile; + etherswitch_info_t info; + enum cmdmode mode; + int unit; +}; + +struct cmds { + enum cmdmode mode; + const char *name; + int args; + void (*f)(struct cfg *, char *argv[]); +}; +struct cmds cmds[]; + + +static void usage(void); + +static int +read_register(struct cfg *cfg, int r) +{ + struct etherswitch_reg er; + + er.reg = r; + if (ioctl(cfg->fd, IOETHERSWITCHGETREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETREG)"); + return (er.val); +} + +static void +write_register(struct cfg *cfg, int r, int v) +{ + struct etherswitch_reg er; + + er.reg = r; + er.val = v; + if (ioctl(cfg->fd, IOETHERSWITCHSETREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)"); +} + +static int +read_phyregister(struct cfg *cfg, int phy, int reg) +{ + struct etherswitch_phyreg er; + + er.phy = phy; + er.reg = reg; + if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)"); + return (er.val); +} + +static void +write_phyregister(struct cfg *cfg, int phy, int reg, int val) +{ + struct etherswitch_phyreg er; + + er.phy = phy; + er.reg = reg; + er.val = val; + if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)"); +} + +static void +set_port_vlangroup(struct cfg *cfg, char *argv[]) +{ + int v; + etherswitch_port_t p; + + v = strtol(argv[1], NULL, 0); + if (v < 0 || v >= cfg->info.es_nvlangroups) + errx(EX_USAGE, "vlangroup must be between 0 and %d", cfg->info.es_nvlangroups-1); + p.es_port = cfg->unit; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + p.es_vlangroup = v; + if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); +} + +static void +set_port_media(struct cfg *cfg, char *argv[]) +{ + etherswitch_port_t p; + int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; + int subtype; + + bzero(&p, sizeof(p)); + p.es_port = cfg->unit; + p.es_ifmr.ifm_ulist = ifm_ulist; + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]); + p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) | + IFM_TYPE(ifm_ulist[0]) | subtype; + if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); +} + +static void +set_port_mediaopt(struct cfg *cfg, char *argv[]) +{ + etherswitch_port_t p; + int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; + int options; + + bzero(&p, sizeof(p)); + p.es_port = cfg->unit; + p.es_ifmr.ifm_ulist = ifm_ulist; + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]); + if (options == -1) + errx(EX_USAGE, "invalid media options \"%s\"", argv[1]); + if (options & IFM_HDX) { + p.es_ifr.ifr_media &= ~IFM_FDX; + options &= ~IFM_HDX; + } + p.es_ifr.ifr_media |= options; + if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); +} + +static void +set_vlangroup_vid(struct cfg *cfg, char *argv[]) +{ + int v; + etherswitch_vlangroup_t vg; + + v = strtol(argv[1], NULL, 0); + if (v < 0 || v >= IEEE802DOT1Q_VID_MAX) + errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX); + vg.es_vlangroup = cfg->unit; + if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); + vg.es_vid = v; + if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); +} + +static void +set_vlangroup_members(struct cfg *cfg, char *argv[]) +{ + etherswitch_vlangroup_t vg; + int member, untagged; + char *c, *d; + int v; + + member = untagged = 0; + if (strcmp(argv[1], "none") != 0) { + for (c=argv[1]; *c; c=d) { + v = strtol(c, &d, 0); + if (d == c) + break; + if (v < 0 || v >= cfg->info.es_nports) + errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1); + if (d[0] == ',' || d[0] == '\0' || + ((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) { + if (d[0] == 't' || d[0] == 'T') { + untagged &= ~ETHERSWITCH_PORTMASK(v); + d++; + } else + untagged |= ETHERSWITCH_PORTMASK(v); + member |= ETHERSWITCH_PORTMASK(v); + d++; + } else + errx(EX_USAGE, "Invalid members specification \"%s\"", d); + } + } + vg.es_vlangroup = cfg->unit; + if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); + vg.es_member_ports = member; + vg.es_untagged_ports = untagged; + if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); +} + +static int +set_register(struct cfg *cfg, char *arg) +{ + int a, v; + char *c; + + a = strtol(arg, &c, 0); + if (c==arg) + return (1); + if (*c == '=') { + v = strtol(c+1, NULL, 0); + write_register(cfg, a, v); + } + printf("\treg 0x%04x=0x%04x\n", a, read_register(cfg, a)); + return (0); +} + +static int +set_phyregister(struct cfg *cfg, char *arg) +{ + int phy, reg, val; + char *c, *d; + + phy = strtol(arg, &c, 0); + if (c==arg) + return (1); + if (*c != '.') + return (1); + d = c+1; + reg = strtol(d, &c, 0); + if (d == c) + return (1); + if (*c == '=') { + val = strtol(c+1, NULL, 0); + write_phyregister(cfg, phy, reg, val); + } + printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg)); + return (0); +} + +static void +print_port(struct cfg *cfg, int port) +{ + etherswitch_port_t p; + int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; + int i; + + bzero(&p, sizeof(p)); + p.es_port = port; + p.es_ifmr.ifm_ulist = ifm_ulist; + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + printf("port%d:\n", port); + printf("\tvlangroup: %d\n", p.es_vlangroup); + printf("\tmedia: "); + print_media_word(p.es_ifmr.ifm_current, 1); + if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(p.es_ifmr.ifm_active, 0); + putchar(')'); + } + putchar('\n'); + printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier"); + if (cfg->mediatypes) { + printf("\tsupported media:\n"); + if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES) + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + for (i=0; i<p.es_ifmr.ifm_count; i++) { + printf("\t\tmedia "); + print_media_word(ifm_ulist[i], 0); + putchar('\n'); + } + } +} + +static void +print_vlangroup(struct cfg *cfg, int vlangroup) +{ + etherswitch_vlangroup_t vg; + int i, comma; + + vg.es_vlangroup = vlangroup; + if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); + if (cfg->verbose == 0 && vg.es_member_ports == 0) + return; + printf("vlangroup%d:\n", vlangroup); + printf("\tvlan: %d\n", vg.es_vid); + printf("\tmembers "); + comma = 0; + if (vg.es_member_ports != 0) + for (i=0; i<cfg->info.es_nports; i++) { + if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) { + if (comma) + printf(","); + printf("%d", i); + if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0) + printf("t"); + comma = 1; + } + } + else + printf("none"); + printf("\n"); +} + +static void +print_info(struct cfg *cfg) +{ + const char *c; + int i; + + c = strrchr(cfg->controlfile, '/'); + if (c != NULL) + c = c + 1; + else + c = cfg->controlfile; + if (cfg->verbose) + printf("%s: %s with %d ports and %d VLAN groups\n", + c, cfg->info.es_name, cfg->info.es_nports, cfg->info.es_nvlangroups); + for (i=0; i<cfg->info.es_nports; i++) { + print_port(cfg, i); + } + for (i=0; i<cfg->info.es_nvlangroups; i++) { + print_vlangroup(cfg, i); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: etherswitchctl\n"); + exit(EX_USAGE); +} + +static void +newmode(struct cfg *cfg, enum cmdmode mode) +{ + if (mode == cfg->mode) + return; + switch (cfg->mode) { + case MODE_NONE: + break; + case MODE_PORT: + print_port(cfg, cfg->unit); + break; + case MODE_VLANGROUP: + print_vlangroup(cfg, cfg->unit); + break; + case MODE_REGISTER: + case MODE_PHYREG: + break; + } + cfg->mode = mode; +} + +int +main(int argc, char *argv[]) +{ + int ch; + struct cfg cfg; + int i; + + bzero(&cfg, sizeof(cfg)); + cfg.controlfile = "/dev/etherswitch0"; + while ((ch = getopt(argc, argv, "f:mv?")) != -1) + switch(ch) { + case 'f': + cfg.controlfile = optarg; + break; + case 'm': + cfg.mediatypes++; + break; + case 'v': + cfg.verbose++; + break; + case '?': + /* FALLTHROUGH */ + default: + usage(); + } + argc -= optind; + argv += optind; + cfg.fd = open(cfg.controlfile, O_RDONLY); + if (cfg.fd < 0) + err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile); + if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)"); + if (argc == 0) { + print_info(&cfg); + return (0); + } + cfg.mode = MODE_NONE; + while (argc > 0) { + switch(cfg.mode) { + case MODE_NONE: + if (strcmp(argv[0], "info") == 0) { + print_info(&cfg); + } else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) { + if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports) + errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports); + newmode(&cfg, MODE_PORT); + } else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) { + if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups) + errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nvlangroups); + newmode(&cfg, MODE_VLANGROUP); + } else if (strcmp(argv[0], "phy") == 0) { + newmode(&cfg, MODE_PHYREG); + } else if (strcmp(argv[0], "reg") == 0) { + newmode(&cfg, MODE_REGISTER); + } else { + errx(EX_USAGE, "Unknown command \"%s\"", argv[0]); + } + break; + case MODE_PORT: + case MODE_VLANGROUP: + for(i=0; cmds[i].name != NULL; i++) { + if (cfg.mode == cmds[i].mode && strcmp(argv[0], cmds[i].name) == 0 + && argc >= cmds[i].args) { + (cmds[i].f)(&cfg, argv); + argc -= cmds[i].args; + argv += cmds[i].args; + break; + } + } + if (cmds[i].name == NULL) { + newmode(&cfg, MODE_NONE); + continue; + } + break; + case MODE_REGISTER: + if (set_register(&cfg, argv[0]) != 0) { + newmode(&cfg, MODE_NONE); + continue; + } + break; + case MODE_PHYREG: + if (set_phyregister(&cfg, argv[0]) != 0) { + newmode(&cfg, MODE_NONE); + continue; + } + break; + } + argc--; + argv++; + } + /* switch back to command mode to print configuration for last command */ + newmode(&cfg, MODE_NONE); + close(cfg.fd); + return (0); +} + +struct cmds cmds[] = { + { MODE_PORT, "vlangroup", 1, set_port_vlangroup }, + { MODE_PORT, "media", 1, set_port_media }, + { MODE_PORT, "mediaopt", 1, set_port_mediaopt }, + { MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid }, + { MODE_VLANGROUP, "members", 1, set_vlangroup_members }, + { 0, NULL, 0, NULL } +}; diff --git a/sbin/etherswitchcfg/ifmedia.c b/sbin/etherswitchcfg/ifmedia.c new file mode 100644 index 0000000..49881fe --- /dev/null +++ b/sbin/etherswitchcfg/ifmedia.c @@ -0,0 +1,812 @@ +/* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */ +/* $FreeBSD: head/sbin/ifconfig/ifmedia.c 221954 2011-05-15 12:51:00Z marius $ */ + +/* + * Copyright (c) 1997 Jason R. Thorpe. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Jason R. Thorpe. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * based on sbin/ifconfig/ifmedia.c r221954 + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_media.h> +#include <net/route.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void domediaopt(const char *, int, int); +int get_media_subtype(int, const char *); +int get_media_mode(int, const char *); +int get_media_options(int, const char *); +int lookup_media_word(struct ifmedia_description *, const char *); +void print_media_word(int, int); +void print_media_word_ifconfig(int); + +#if 0 +static struct ifmedia_description *get_toptype_desc(int); +static struct ifmedia_type_to_subtype *get_toptype_ttos(int); +static struct ifmedia_description *get_subtype_desc(int, + struct ifmedia_type_to_subtype *ttos); + +#define IFM_OPMODE(x) \ + ((x) & (IFM_IEEE80211_ADHOC | IFM_IEEE80211_HOSTAP | \ + IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR | \ + IFM_IEEE80211_MBSS)) +#define IFM_IEEE80211_STA 0 + +static void +media_status(int s) +{ + struct ifmediareq ifmr; + int *media_list, i; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + /* + * Interface doesn't support SIOC{G,S}IFMEDIA. + */ + return; + } + + if (ifmr.ifm_count == 0) { + warnx("%s: no media types?", name); + return; + } + + media_list = (int *)malloc(ifmr.ifm_count * sizeof(int)); + if (media_list == NULL) + err(1, "malloc"); + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + + printf("\tmedia: "); + print_media_word(ifmr.ifm_current, 1); + if (ifmr.ifm_active != ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(ifmr.ifm_active, 0); + putchar(')'); + } + + putchar('\n'); + + if (ifmr.ifm_status & IFM_AVALID) { + printf("\tstatus: "); + switch (IFM_TYPE(ifmr.ifm_active)) { + case IFM_ETHER: + case IFM_ATM: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("no carrier"); + break; + + case IFM_FDDI: + case IFM_TOKEN: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("inserted"); + else + printf("no ring"); + break; + + case IFM_IEEE80211: + if (ifmr.ifm_status & IFM_ACTIVE) { + /* NB: only sta mode associates */ + if (IFM_OPMODE(ifmr.ifm_active) == IFM_IEEE80211_STA) + printf("associated"); + else + printf("running"); + } else + printf("no carrier"); + break; + } + putchar('\n'); + } + + if (ifmr.ifm_count > 0 && supmedia) { + printf("\tsupported media:\n"); + for (i = 0; i < ifmr.ifm_count; i++) { + printf("\t\t"); + print_media_word_ifconfig(media_list[i]); + putchar('\n'); + } + } + + free(media_list); +} + +struct ifmediareq * +ifmedia_getstate(int s) +{ + static struct ifmediareq *ifmr = NULL; + int *mwords; + + if (ifmr == NULL) { + ifmr = (struct ifmediareq *)malloc(sizeof(struct ifmediareq)); + if (ifmr == NULL) + err(1, "malloc"); + + (void) memset(ifmr, 0, sizeof(struct ifmediareq)); + (void) strncpy(ifmr->ifm_name, name, + sizeof(ifmr->ifm_name)); + + ifmr->ifm_count = 0; + ifmr->ifm_ulist = NULL; + + /* + * We must go through the motions of reading all + * supported media because we need to know both + * the current media type and the top-level type. + */ + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) { + err(1, "SIOCGIFMEDIA"); + } + + if (ifmr->ifm_count == 0) + errx(1, "%s: no media types?", name); + + mwords = (int *)malloc(ifmr->ifm_count * sizeof(int)); + if (mwords == NULL) + err(1, "malloc"); + + ifmr->ifm_ulist = mwords; + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + } + + return ifmr; +} + +static void +setifmediacallback(int s, void *arg) +{ + struct ifmediareq *ifmr = (struct ifmediareq *)arg; + static int did_it = 0; + + if (!did_it) { + ifr.ifr_media = ifmr->ifm_current; + if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) + err(1, "SIOCSIFMEDIA (media)"); + free(ifmr->ifm_ulist); + free(ifmr); + did_it = 1; + } +} + +static void +setmedia(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int subtype; + + ifmr = ifmedia_getstate(s); + + /* + * We are primarily concerned with the top-level type. + * However, "current" may be only IFM_NONE, so we just look + * for the top-level type in the first "supported type" + * entry. + * + * (I'm assuming that all supported media types for a given + * interface will be the same top-level type..) + */ + subtype = get_media_subtype(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & IFM_IMASK) | + IFM_TYPE(ifmr->ifm_ulist[0]) | subtype; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 0, s); +} + +static void +unsetmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 1, s); +} + +static void +domediaopt(const char *val, int clear, int s) +{ + struct ifmediareq *ifmr; + int options; + + ifmr = ifmedia_getstate(s); + + options = get_media_options(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = ifmr->ifm_current; + if (clear) + ifr.ifr_media &= ~options; + else { + if (options & IFM_HDX) { + ifr.ifr_media &= ~IFM_FDX; + options &= ~IFM_HDX; + } + ifr.ifr_media |= options; + } + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediainst(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int inst; + + ifmr = ifmedia_getstate(s); + + inst = atoi(val); + if (inst < 0 || inst > (int)IFM_INST_MAX) + errx(1, "invalid media instance: %s", val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_IMASK) | inst << IFM_ISHIFT; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediamode(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int mode; + + ifmr = ifmedia_getstate(s); + + mode = get_media_mode(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_MMASK) | mode; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} +#endif + +/********************************************************************** + * A good chunk of this is duplicated from sys/net/ifmedia.c + **********************************************************************/ + +static struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_descriptions[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_aliases[] = + IFM_SUBTYPE_ETHERNET_ALIASES; + +static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = + IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_descriptions[] = + IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_aliases[] = + IFM_SUBTYPE_TOKENRING_ALIASES; + +static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = + IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_descriptions[] = + IFM_SUBTYPE_FDDI_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_aliases[] = + IFM_SUBTYPE_FDDI_ALIASES; + +static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = + IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = + IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_aliases[] = + IFM_SUBTYPE_IEEE80211_ALIASES; + +static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = + IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] = + IFM_SUBTYPE_IEEE80211_MODE_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_descriptions[] = + IFM_SUBTYPE_ATM_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_atm_aliases[] = + IFM_SUBTYPE_ATM_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_option_descriptions[] = + IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_descriptions[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_aliases[] = + IFM_SUBTYPE_SHARED_ALIASES; + +static struct ifmedia_description ifm_shared_option_descriptions[] = + IFM_SHARED_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_shared_option_aliases[] = + IFM_SHARED_OPTION_ALIASES; + +struct ifmedia_type_to_subtype { + struct { + struct ifmedia_description *desc; + int alias; + } subtypes[5]; + struct { + struct ifmedia_description *desc; + int alias; + } options[4]; + struct { + struct ifmedia_description *desc; + int alias; + } modes[3]; +}; + +/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ +static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ethernet_descriptions[0], 0 }, + { &ifm_subtype_ethernet_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_ethernet_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_tokenring_descriptions[0], 0 }, + { &ifm_subtype_tokenring_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_tokenring_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_fddi_descriptions[0], 0 }, + { &ifm_subtype_fddi_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_fddi_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ieee80211_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_ieee80211_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { &ifm_subtype_ieee80211_mode_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_mode_aliases[0], 0 }, + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_atm_descriptions[0], 0 }, + { &ifm_subtype_atm_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_atm_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +}; + +int +get_media_subtype(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->subtypes[i].desc, val); + if (rval != -1) + return (rval); + } + errx(1, "unknown media subtype: %s", val); + /*NOTREACHED*/ +} + +int +get_media_mode(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media mode 0x%x", type); + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->modes[i].desc, val); + if (rval != -1) + return (rval); + } + return -1; +} + +int +get_media_options(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + char *optlist, *optptr; + int option = 0, i, rval = 0; + + /* We muck with the string, so copy it. */ + optlist = strdup(val); + if (optlist == NULL) + err(1, "strdup"); + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + /* + * Look up the options in the user-provided comma-separated + * list. + */ + optptr = optlist; + for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) { + for (i = 0; ttos->options[i].desc != NULL; i++) { + option = lookup_media_word(ttos->options[i].desc, optptr); + if (option != -1) + break; + } + if (option == 0) + errx(1, "unknown option: %s", optptr); + rval |= option; + } + + free(optlist); + return (rval); +} + +int +lookup_media_word(struct ifmedia_description *desc, const char *val) +{ + + for (; desc->ifmt_string != NULL; desc++) + if (strcasecmp(desc->ifmt_string, val) == 0) + return (desc->ifmt_word); + + return (-1); +} + +static struct ifmedia_description *get_toptype_desc(int ifmw) +{ + struct ifmedia_description *desc; + + for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return desc; +} + +static struct ifmedia_type_to_subtype *get_toptype_ttos(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return ttos; +} + +static struct ifmedia_description *get_subtype_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + if (ttos->subtypes[i].alias) + continue; + for (desc = ttos->subtypes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +static struct ifmedia_description *get_mode_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + if (ttos->modes[i].alias) + continue; + for (desc = ttos->modes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_MODE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +void +print_media_word(int ifmw, int print_toptype) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; + } else if (print_toptype) { + printf("%s", desc->ifmt_string); + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + + if (print_toptype) + putchar(' '); + + printf("%s", desc->ifmt_string); + + if (print_toptype) { + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL && strcasecmp("autoselect", desc->ifmt_string)) + printf(" mode %s", desc->ifmt_string); + } + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" <"); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + printf("%s", seen_option ? ">" : ""); + + if (print_toptype && IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +void +print_media_word_ifconfig(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + + printf("media %s", desc->ifmt_string); + + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL) + printf(" mode %s", desc->ifmt_string); + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" mediaopt "); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + + if (IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +/********************************************************************** + * ...until here. + **********************************************************************/ --Apple-Mail=_CE96FAC5-9AF0-44EF-BFDB-F3380CF6C0C4 Content-Disposition: attachment; filename=miiproxy.patch Content-Type: application/octet-stream; name="miiproxy.patch" Content-Transfer-Encoding: 7bit diff --git a/sys/dev/etherswitch/mdio.c b/sys/dev/etherswitch/mdio.c new file mode 100644 index 0000000..9302075 --- /dev/null +++ b/sys/dev/etherswitch/mdio.c @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/systm.h> + +#include <dev/etherswitch/mdio.h> + +#include "mdio_if.h" + +static void +mdio_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, mdio_driver.name, -1) == NULL) + BUS_ADD_CHILD(parent, 0, mdio_driver.name, -1); +} + +static int +mdio_probe(device_t dev) +{ + device_set_desc(dev, "MDIO"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +mdio_attach(device_t dev) +{ + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + return (bus_generic_attach(dev)); +} + +static int +mdio_detach(device_t dev) +{ + bus_generic_detach(dev); + return (0); +} + +static int +mdio_readreg(device_t dev, int phy, int reg) +{ + return MDIO_READREG(device_get_parent(dev), phy, reg); +} + +static int +mdio_writereg(device_t dev, int phy, int reg, int val) +{ + return MDIO_WRITEREG(device_get_parent(dev), phy, reg, val); +} + +static int +mdio_print_child(device_t dev, device_t child) +{ + int retval; + + retval = bus_print_child_header(dev, child); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static int +mdio_read_ivar(device_t dev, device_t child __unused, int which, + uintptr_t *result) +{ + struct miibus_ivars *ivars; + + ivars = device_get_ivars(dev); + switch (which) { + default: + return (ENOENT); + } + return (0); +} + +static int +mdio_child_pnpinfo_str(device_t dev __unused, device_t child, char *buf, + size_t buflen) +{ + buf[0] = '\0'; + return (0); +} + +static int +mdio_child_location_str(device_t dev __unused, device_t child, char *buf, + size_t buflen) +{ + buf[0] = '\0'; + return (0); +} + +static void +mdio_hinted_child(device_t dev, const char *name, int unit) +{ + device_add_child(dev, name, unit); +} + +static device_method_t mdio_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, mdio_identify), + DEVMETHOD(device_probe, mdio_probe), + DEVMETHOD(device_attach, mdio_attach), + DEVMETHOD(device_detach, mdio_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, mdio_print_child), + DEVMETHOD(bus_read_ivar, mdio_read_ivar), + DEVMETHOD(bus_child_pnpinfo_str, mdio_child_pnpinfo_str), + DEVMETHOD(bus_child_location_str, mdio_child_location_str), + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, mdio_hinted_child), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, mdio_readreg), + DEVMETHOD(mdio_writereg, mdio_writereg), + + DEVMETHOD_END +}; + +driver_t mdio_driver = { + "mdio", + mdio_methods, + 0 +}; + +devclass_t mdio_devclass; + diff --git a/sys/dev/etherswitch/mdio.h b/sys/dev/etherswitch/mdio.h new file mode 100644 index 0000000..52eddbd --- /dev/null +++ b/sys/dev/etherswitch/mdio.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_MII_MDIO_H_ +#define _DEV_MII_MDIO_H_ + +extern driver_t mdio_driver; +extern devclass_t mdio_devclass; + +#endif diff --git a/sys/dev/etherswitch/mdio_if.m b/sys/dev/etherswitch/mdio_if.m new file mode 100644 index 0000000..9aedd92 --- /dev/null +++ b/sys/dev/etherswitch/mdio_if.m @@ -0,0 +1,24 @@ +# $FreeBSD$ + +#include <sys/bus.h> + +INTERFACE mdio; + +# +# Read register from device on MDIO bus +# +METHOD int readreg { + device_t dev; + int phy; + int reg; +}; + +# +# Write register to device on MDIO bus +# +METHOD int writereg { + device_t dev; + int phy; + int reg; + int val; +}; diff --git a/sys/dev/etherswitch/miiproxy.c b/sys/dev/etherswitch/miiproxy.c new file mode 100644 index 0000000..2791082 --- /dev/null +++ b/sys/dev/etherswitch/miiproxy.c @@ -0,0 +1,437 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <dev/etherswitch/miiproxy.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include "mdio_if.h" +#include "miibus_if.h" + + +MALLOC_DECLARE(M_MIIPROXY); +MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures"); + +driver_t miiproxy_driver; +driver_t mdioproxy_driver; + +struct miiproxy_softc { + device_t parent; + device_t proxy; + device_t mdio; + miiproxy_attach_callback_t attach_callback; + void *attach_arg; +}; + +struct mdioproxy_softc { +}; + +/* + * The rendevous data structures and functions allow two device endpoints to + * match up, so that the proxy endpoint can be associated with a target + * endpoint. The proxy has to know the device name of the target that it + * wants to associate with, for example through a hint. The rendevous code + * makes no assumptions about the devices that want to meet. + */ +struct rendevous_entry; + +enum rendevous_op { + RENDEVOUS_ATTACH, + RENDEVOUS_DETACH +}; + +typedef int (*rendevous_callback_t)(enum rendevous_op, + struct rendevous_entry *); + +static SLIST_HEAD(rendevoushead, rendevous_entry) rendevoushead = + SLIST_HEAD_INITIALIZER(rendevoushead); + +struct rendevous_endpoint { + device_t device; + const char *name; + rendevous_callback_t callback; +}; + +struct rendevous_entry { + SLIST_ENTRY(rendevous_entry) entries; + struct rendevous_endpoint proxy; + struct rendevous_endpoint target; +}; + +/* + * Call the callback routines for both the proxy and the target. If either + * returns an error, undo the attachment. + */ +static int +rendevous_attach(struct rendevous_entry *e, struct rendevous_endpoint *ep) +{ + int error; + + error = e->proxy.callback(RENDEVOUS_ATTACH, e); + if (error == 0) + error = e->target.callback(RENDEVOUS_ATTACH, e); + if (error != 0) { + e->proxy.callback(RENDEVOUS_DETACH, e); + ep->device = NULL; + ep->callback = NULL; + } + return (error); +} + +/* + * Create an entry for the proxy in the rendevous list. The name parameter + * indicates the name of the device that is the target endpoint for this + * rendevous. The callback will be invoked as soon as the target is + * registered: either immediately if the target registered itself earlier, + * or once the target registers. + */ +static int +rendevous_register_proxy(device_t dev, const char *name, + rendevous_callback_t callback) +{ + struct rendevous_entry *e; + + KASSERT(callback != NULL, ("callback must not be NULL")); + SLIST_FOREACH(e, &rendevoushead, entries) { + if (strcmp(name, e->target.name) == 0) { + /* the target is already attached */ + e->proxy.name = device_get_nameunit(dev); + e->proxy.device = dev; + e->proxy.callback = callback; + return (rendevous_attach(e, &e->proxy)); + } + } + e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); + e->proxy.name = device_get_nameunit(dev); + e->proxy.device = dev; + e->proxy.callback = callback; + e->target.name = name; + SLIST_INSERT_HEAD(&rendevoushead, e, entries); + return (0); +} + +/* + * Create an entry in the rendevous list for the target. The callback will + * be called once the proxy has registered. + */ +static int +rendevous_register_target(device_t dev, rendevous_callback_t callback) +{ + struct rendevous_entry *e; + const char *name; + + KASSERT(callback != NULL, ("callback must not be NULL")); + name = device_get_nameunit(dev); + SLIST_FOREACH(e, &rendevoushead, entries) { + if (strcmp(name, e->target.name) == 0) { + e->target.device = dev; + e->target.callback = callback; + return (rendevous_attach(e, &e->target)); + } + } + e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); + e->target.name = name; + e->target.device = dev; + e->target.callback = callback; + SLIST_INSERT_HEAD(&rendevoushead, e, entries); + return (0); +} + +/* + * Remove the registration for the proxy. + */ +static int +rendevous_unregister_proxy(device_t dev) +{ + struct rendevous_entry *e; + int error = 0; + + SLIST_FOREACH(e, &rendevoushead, entries) { + if (e->proxy.device == dev) { + if (e->target.device == NULL) { + SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); + free(e, M_MIIPROXY); + return (0); + } else { + e->proxy.callback(RENDEVOUS_DETACH, e); + e->target.callback(RENDEVOUS_DETACH, e); + } + e->proxy.device = NULL; + e->proxy.callback = NULL; + return (error); + } + } + return (ENOENT); +} + +/* + * Remove the registration for the target. + */ +static int +rendevous_unregister_target(device_t dev) +{ + struct rendevous_entry *e; + int error = 0; + + SLIST_FOREACH(e, &rendevoushead, entries) { + if (e->target.device == dev) { + if (e->proxy.device == NULL) { + SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); + free(e, M_MIIPROXY); + return (0); + } else { + e->proxy.callback(RENDEVOUS_DETACH, e); + e->target.callback(RENDEVOUS_DETACH, e); + } + e->target.device = NULL; + e->target.callback = NULL; + return (error); + } + } + return (ENOENT); +} + +/* + * Functions of the proxy that is interposed between the ethernet interface + * driver and the miibus device. + */ + +static int +miiproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) +{ + struct miiproxy_softc *sc = device_get_softc(rendevous->proxy.device); + + switch (op) { + case RENDEVOUS_ATTACH: + sc->mdio = device_get_parent(rendevous->target.device); + (sc->attach_callback)(sc->attach_arg); + break; + case RENDEVOUS_DETACH: + sc->mdio = NULL; + /* detach miibus */ + } + return (0); +} + +static int +miiproxy_probe(device_t dev) +{ + device_set_desc(dev, "MII/MDIO proxy, MII side"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +miiproxy_attach(device_t dev) +{ + /* + * The ethernet interface needs to call mii_attach_proxy() to pass + * the relevant parameters for rendevous with the MDIO target. + */ + return (bus_generic_attach(dev)); +} + +static int +miiproxy_detach(device_t dev) +{ + rendevous_unregister_proxy(dev); + bus_generic_detach(dev); + return (0); +} + +static int +miiproxy_readreg(device_t dev, int phy, int reg) +{ + struct miiproxy_softc *sc = device_get_softc(dev); + + if (sc->mdio != NULL) + return (MDIO_READREG(sc->mdio, phy, reg)); + return (-1); +} + +static int +miiproxy_writereg(device_t dev, int phy, int reg, int val) +{ + struct miiproxy_softc *sc = device_get_softc(dev); + + if (sc->mdio != NULL) + return (MDIO_WRITEREG(sc->mdio, phy, reg, val)); + return (-1); +} + +static void +miiproxy_statchg(device_t dev) +{ + MIIBUS_STATCHG(device_get_parent(dev)); +} + +static void +miiproxy_linkchg(device_t dev) +{ + MIIBUS_LINKCHG(device_get_parent(dev)); +} + +static void +miiproxy_mediainit(device_t dev) +{ + MIIBUS_MEDIAINIT(device_get_parent(dev)); +} + +/* + * Functions for the MDIO target device driver. + */ +static int +mdioproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) +{ + return (0); +} + +static void +mdioproxy_identify(driver_t *driver, device_t parent) +{ + device_t child; + + if (device_find_child(parent, driver->name, -1) == NULL) { + child = BUS_ADD_CHILD(parent, 0, driver->name, -1); + } +} + +static int +mdioproxy_probe(device_t dev) +{ + device_set_desc(dev, "MII/MDIO proxy, MDIO side"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +mdioproxy_attach(device_t dev) +{ + rendevous_register_target(dev, mdioproxy_rendevous_callback); + return (bus_generic_attach(dev)); +} + +static int +mdioproxy_detach(device_t dev) +{ + rendevous_unregister_target(dev); + bus_generic_detach(dev); + return (0); +} + +/* + * Attach this proxy in place of miibus. The callback is called once all + * parts are in place, so that it can attach the miibus to the proxy device, + * and finish interface initialization. + */ +device_t +mii_attach_proxy(device_t dev, miiproxy_attach_callback_t cb, void *aa) +{ + struct miiproxy_softc *sc; + int error; + const char *name; + device_t miiproxy; + + if (resource_string_value(device_get_name(dev), + device_get_unit(dev), "mdio", &name) != 0) { + if (bootverbose) + printf("mii_attach_proxy: not attaching, no mdio" + " device hint for %s\n", device_get_nameunit(dev)); + return (NULL); + } + + miiproxy = device_add_child(dev, miiproxy_driver.name, -1); + error = bus_generic_attach(dev); + if (error != 0) { + device_printf(dev, "can't attach miiproxy\n"); + return (NULL); + } + sc = device_get_softc(miiproxy); + sc->parent = dev; + sc->proxy = miiproxy; + sc->attach_callback = cb; + sc->attach_arg = aa; + rendevous_register_proxy(miiproxy, name, miiproxy_rendevous_callback); + return (miiproxy); +} + +static device_method_t miiproxy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, miiproxy_probe), + DEVMETHOD(device_attach, miiproxy_attach), + DEVMETHOD(device_detach, miiproxy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* MII interface */ + DEVMETHOD(miibus_readreg, miiproxy_readreg), + DEVMETHOD(miibus_writereg, miiproxy_writereg), + DEVMETHOD(miibus_statchg, miiproxy_statchg), + DEVMETHOD(miibus_linkchg, miiproxy_linkchg), + DEVMETHOD(miibus_mediainit, miiproxy_mediainit), + + DEVMETHOD_END +}; + +static device_method_t mdioproxy_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, mdioproxy_identify), + DEVMETHOD(device_probe, mdioproxy_probe), + DEVMETHOD(device_attach, mdioproxy_attach), + DEVMETHOD(device_detach, mdioproxy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods, + sizeof(struct miiproxy_softc)); +DEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods, + sizeof(struct mdioproxy_softc)); + +devclass_t miiproxy_devclass; +static devclass_t mdioproxy_devclass; + +DRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, mdioproxy_devclass, 0, 0); +DRIVER_MODULE(miibus, miiproxy, miibus_driver, miibus_devclass, 0, 0); +MODULE_VERSION(miiproxy, 1); +MODULE_DEPEND(miiproxy, miibus, 1, 1, 1); diff --git a/sys/dev/etherswitch/miiproxy.h b/sys/dev/etherswitch/miiproxy.h new file mode 100644 index 0000000..5b8ee7c --- /dev/null +++ b/sys/dev/etherswitch/miiproxy.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_ETHERSWITCH_MIIPROXY_H_ +#define _DEV_ETHERSWITCH_MIIPROXY_H_ + +typedef void (*miiproxy_attach_callback_t)(void *); + +extern devclass_t miiproxy_devclass; +extern driver_t miiproxy_driver; + +device_t mii_attach_proxy(device_t dev, miiproxy_attach_callback_t cb, void *aa); + +#endif diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c index 0154ae2..3dd2e15 100644 --- a/sys/mips/atheros/if_arge.c +++ b/sys/mips/atheros/if_arge.c @@ -72,8 +72,18 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#include <opt_arge.h> + +#if defined(ARGE_MDIO) +#include <dev/etherswitch/mdio.h> +#include <dev/etherswitch/miiproxy.h> +#include "mdio_if.h" +#endif + + MODULE_DEPEND(arge, ether, 1, 1, 1); MODULE_DEPEND(arge, miibus, 1, 1, 1); +MODULE_VERSION(arge, 1); #include "miibus_if.h" @@ -102,6 +112,7 @@ typedef enum { #endif static int arge_attach(device_t); +static int arge_attach_finish(struct arge_softc *sc); static int arge_detach(device_t); static void arge_flush_ddr(struct arge_softc *); static int arge_ifmedia_upd(struct ifnet *); @@ -134,6 +145,8 @@ static void arge_intr(void *); static int arge_intr_filter(void *); static void arge_tick(void *); +static void arge_hinted_child(device_t bus, const char *dname, int dunit); + /* * ifmedia callbacks for multiPHY MAC */ @@ -160,6 +173,10 @@ static device_method_t arge_methods[] = { DEVMETHOD(miibus_writereg, arge_miibus_writereg), DEVMETHOD(miibus_statchg, arge_miibus_statchg), + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, arge_hinted_child), + DEVMETHOD_END }; @@ -174,6 +191,37 @@ static devclass_t arge_devclass; DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); +#if defined(ARGE_MDIO) +static int argemdio_probe(device_t); +static int argemdio_attach(device_t); +static int argemdio_detach(device_t); + +/* + * Declare an additional, separate driver for accessing the MDIO bus. + */ +static device_method_t argemdio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, argemdio_probe), + DEVMETHOD(device_attach, argemdio_attach), + DEVMETHOD(device_detach, argemdio_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, arge_miibus_readreg), + DEVMETHOD(mdio_writereg, arge_miibus_writereg), +}; + +DEFINE_CLASS_0(argemdio, argemdio_driver, argemdio_methods, + sizeof(struct arge_softc)); +static devclass_t argemdio_devclass; + +DRIVER_MODULE(miiproxy, arge, miiproxy_driver, miiproxy_devclass, 0, 0); +DRIVER_MODULE(argemdio, nexus, argemdio_driver, argemdio_devclass, 0, 0); +DRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0); +#endif + /* * RedBoot passes MAC address to entry point as environment * variable. platfrom_start parses it and stores in this variable @@ -234,15 +282,22 @@ arge_attach_sysctl(device_t dev) #endif } +#if defined(ARGE_MDIO) +static void +arge_attach_proxy(void *aa) +{ + arge_attach_finish(aa); +} +#endif + static int arge_attach(device_t dev) { - uint8_t eaddr[ETHER_ADDR_LEN]; struct ifnet *ifp; struct arge_softc *sc; - int error = 0, rid, phymask; + int error = 0, rid; uint32_t reg, rnd; - int is_base_mac_empty, i, phys_total; + int is_base_mac_empty, i; uint32_t hint; long eeprom_mac_addr = 0; @@ -277,18 +332,18 @@ arge_attach(device_t dev) * Get which PHY of 5 available we should use for this unit */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), - "phymask", &phymask) != 0) { + "phymask", &sc->arge_phymask) != 0) { /* * Use port 4 (WAN) for GE0. For any other port use * its PHY the same as its unit number */ if (sc->arge_mac_unit == 0) - phymask = (1 << 4); + sc->arge_phymask = (1 << 4); else /* Use all phys up to 4 */ - phymask = (1 << 4) - 1; + sc->arge_phymask = (1 << 4) - 1; - device_printf(dev, "No PHY specified, using mask %d\n", phymask); + device_printf(dev, "No PHY specified, using mask %d\n", sc->arge_phymask); } /* @@ -313,8 +368,6 @@ arge_attach(device_t dev) else sc->arge_duplex_mode = 0; - sc->arge_phymask = phymask; - mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); @@ -323,7 +376,7 @@ arge_attach(device_t dev) /* Map control/status registers. */ sc->arge_rid = 0; sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, - &sc->arge_rid, RF_ACTIVE); + &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->arge_res == NULL) { device_printf(dev, "couldn't map memory\n"); @@ -371,8 +424,8 @@ arge_attach(device_t dev) is_base_mac_empty = 1; for (i = 0; i < ETHER_ADDR_LEN; i++) { - eaddr[i] = ar711_base_mac[i] & 0xff; - if (eaddr[i] != 0) + sc->arge_eaddr[i] = ar711_base_mac[i] & 0xff; + if (sc->arge_eaddr[i] != 0) is_base_mac_empty = 0; } @@ -385,16 +438,15 @@ arge_attach(device_t dev) "Generating random ethernet address.\n"); rnd = arc4random(); - eaddr[0] = 'b'; - eaddr[1] = 's'; - eaddr[2] = 'd'; - eaddr[3] = (rnd >> 24) & 0xff; - eaddr[4] = (rnd >> 16) & 0xff; - eaddr[5] = (rnd >> 8) & 0xff; + sc->arge_eaddr[0] = 'b'; + sc->arge_eaddr[1] = 's'; + sc->arge_eaddr[2] = 'd'; + sc->arge_eaddr[3] = (rnd >> 24) & 0xff; + sc->arge_eaddr[4] = (rnd >> 16) & 0xff; + sc->arge_eaddr[5] = (rnd >> 8) & 0xff; } - if (sc->arge_mac_unit != 0) - eaddr[5] += sc->arge_mac_unit; + sc->arge_eaddr[5] += sc->arge_mac_unit; if (arge_dma_alloc(sc) != 0) { error = ENXIO; @@ -423,19 +475,23 @@ arge_attach(device_t dev) ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536); +#if !defined(ARGE_MDIO) /* Reset MII bus */ ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); DELAY(100); ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); DELAY(100); +#endif /* * Set all Ethernet address registers to the same initial values * set all four addresses to 66-88-aa-cc-dd-ee */ - ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, - (eaddr[2] << 24) | (eaddr[3] << 16) | (eaddr[4] << 8) | eaddr[5]); - ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (eaddr[0] << 8) | eaddr[1]); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, (sc->arge_eaddr[2] << 24) + | (sc->arge_eaddr[3] << 16) | (sc->arge_eaddr[4] << 8) + | sc->arge_eaddr[5]); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (sc->arge_eaddr[0] << 8) + | sc->arge_eaddr[1]); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); @@ -458,30 +514,44 @@ arge_attach(device_t dev) ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, FIFO_RX_FILTMASK_DEFAULT); - /* - * Check if we have single-PHY MAC or multi-PHY - */ - phys_total = 0; - for (i = 0; i < ARGE_NPHY; i++) - if (phymask & (1 << i)) - phys_total ++; +#if defined(ARGE_MDIO) + sc->arge_miiproxy = mii_attach_proxy(sc->arge_dev, arge_attach_proxy, sc); + if (sc->arge_miiproxy == NULL) + return (arge_attach_finish(sc)); +#else + return (arge_attach_finish(sc)); +#endif +fail: + if (error) + arge_detach(dev); - if (phys_total == 0) { - error = EINVAL; - goto fail; - } + return (error); +} - if (phys_total == 1) { - /* Do MII setup. */ - error = mii_attach(dev, &sc->arge_miibus, ifp, - arge_ifmedia_upd, arge_ifmedia_sts, BMSR_DEFCAPMASK, - MII_PHY_ANY, MII_OFFSET_ANY, 0); - if (error != 0) { - device_printf(dev, "attaching PHYs failed\n"); - goto fail; +static int +arge_attach_finish(struct arge_softc *sc) +{ + int error, phy; + + device_printf(sc->arge_dev, "finishing attachment, phymask %04x" + ", proxy %s \n", sc->arge_phymask, sc->arge_miiproxy == NULL ? + "null" : "set"); + for (phy = 0; phy < ARGE_NPHY; phy++) { + if (((1 << phy) & sc->arge_phymask) != 0) { + error = mii_attach(sc->arge_miiproxy != NULL ? + sc->arge_miiproxy : sc->arge_dev, + &sc->arge_miibus, sc->arge_ifp, + arge_ifmedia_upd, arge_ifmedia_sts, + BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + if (error != 0) { + device_printf(sc->arge_dev, "unable to attach" + " PHY %d: %d\n", phy, error); + goto fail; + } } } - else { + if (sc->arge_miibus == NULL) { + /* no PHY, so use hard-coded values */ ifmedia_init(&sc->arge_ifmedia, 0, arge_multiphy_mediachange, arge_multiphy_mediastatus); @@ -494,24 +564,23 @@ arge_attach(device_t dev) } /* Call MI attach routine. */ - ether_ifattach(ifp, eaddr); + ether_ifattach(sc->arge_ifp, sc->arge_eaddr); /* Hook interrupt last to avoid having to lock softc */ - error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, + error = bus_setup_intr(sc->arge_dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); if (error) { - device_printf(dev, "couldn't set up irq\n"); - ether_ifdetach(ifp); + device_printf(sc->arge_dev, "couldn't set up irq\n"); + ether_ifdetach(sc->arge_ifp); goto fail; } /* setup sysctl variables */ - arge_attach_sysctl(dev); - + arge_attach_sysctl(sc->arge_dev); fail: if (error) - arge_detach(dev); + arge_detach(sc->arge_dev); return (error); } @@ -542,6 +611,9 @@ arge_detach(device_t dev) if (sc->arge_miibus) device_delete_child(dev, sc->arge_miibus); + if (sc->arge_miiproxy) + device_delete_child(dev, sc->arge_miiproxy); + bus_generic_detach(dev); if (sc->arge_intrhand) @@ -592,6 +664,13 @@ arge_shutdown(device_t dev) return (0); } +static void +arge_hinted_child(device_t bus, const char *dname, int dunit) +{ + BUS_ADD_CHILD(bus, 0, dname, dunit); + device_printf(bus, "hinted child %s%d\n", dname, dunit); +} + static int arge_miibus_readreg(device_t dev, int phy, int reg) { @@ -600,16 +679,13 @@ arge_miibus_readreg(device_t dev, int phy, int reg) uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - if ((sc->arge_phymask & (1 << phy)) == 0) - return (0); - mtx_lock(&miibus_mtx); - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); - ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); i = ARGE_MII_TIMEOUT; - while ((ARGE_MII_READ(AR71XX_MAC_MII_INDICATOR) & + while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); @@ -620,8 +696,8 @@ arge_miibus_readreg(device_t dev, int phy, int reg) return (-1); } - result = ARGE_MII_READ(AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, @@ -638,19 +714,15 @@ arge_miibus_writereg(device_t dev, int phy, int reg, int data) uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - - if ((sc->arge_phymask & (1 << phy)) == 0) - return (-1); - ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value=%04x\n", __func__, phy, reg, data); mtx_lock(&miibus_mtx); - ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); - ARGE_MII_WRITE(AR71XX_MAC_MII_CONTROL, data); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); i = ARGE_MII_TIMEOUT; - while ((ARGE_MII_READ(AR71XX_MAC_MII_INDICATOR) & + while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); @@ -715,6 +787,8 @@ arge_set_pll(struct arge_softc *sc, int media, int duplex) uint32_t fifo_tx; int if_speed; + ARGEDEBUG(sc, ARGE_DBG_MII, "set_pll(%04x, %s)\n", media, + duplex == IFM_FDX ? "full" : "half"); cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); cfg &= ~(MAC_CFG2_IFACE_MODE_1000 | MAC_CFG2_IFACE_MODE_10_100 @@ -1923,3 +1997,47 @@ arge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) sc->arge_duplex_mode; } +#if defined(ARGE_MDIO) +static int +argemdio_probe(device_t dev) +{ + device_set_desc(dev, "Atheros AR71xx built-in ethernet interface, MDIO controller"); + return (0); +} + +static int +argemdio_attach(device_t dev) +{ + struct arge_softc *sc; + int error = 0; + + sc = device_get_softc(dev); + sc->arge_dev = dev; + sc->arge_mac_unit = device_get_unit(dev); + sc->arge_rid = 0; + sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); + if (sc->arge_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + /* Reset MII bus */ + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); + DELAY(100); + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); + DELAY(100); + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + error = bus_generic_attach(dev); +fail: + return (error); +} + +static int +argemdio_detach(device_t dev) +{ + return (0); +} + +#endif diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h index c1df678..dc3d931 100644 --- a/sys/mips/atheros/if_argevar.h +++ b/sys/mips/atheros/if_argevar.h @@ -67,15 +67,10 @@ #define ARGE_CLEAR_BITS(sc, reg, bits) \ ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) & ~(bits)) -/* - * MII registers access macros - */ -#define ARGE_MII_READ(reg) \ - *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((AR71XX_MII_BASE + reg))) - -#define ARGE_MII_WRITE(reg, val) \ - *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((AR71XX_MII_BASE + reg))) = (val) - +#define ARGE_MDIO_WRITE(_sc, _reg, _val) \ + ARGE_WRITE((_sc), (_reg), (_val)) +#define ARGE_MDIO_READ(_sc, _reg) \ + ARGE_READ((_sc), (_reg)) #define ARGE_DESC_EMPTY (1 << 31) #define ARGE_DESC_MORE (1 << 24) @@ -132,11 +127,14 @@ struct arge_softc { */ uint32_t arge_media_type; uint32_t arge_duplex_mode; + uint32_t arge_phymask; + uint8_t arge_eaddr[ETHER_ADDR_LEN]; struct resource *arge_res; int arge_rid; struct resource *arge_irq; void *arge_intrhand; device_t arge_miibus; + device_t arge_miiproxy; bus_dma_tag_t arge_parent_tag; bus_dma_tag_t arge_tag; struct mtx arge_mtx; @@ -148,7 +146,6 @@ struct arge_softc { int arge_detach; uint32_t arge_intr_status; int arge_mac_unit; - int arge_phymask; int arge_if_flags; uint32_t arge_debug; struct { --Apple-Mail=_CE96FAC5-9AF0-44EF-BFDB-F3380CF6C0C4 Content-Disposition: attachment; filename=etherswitch-complete.patch Content-Type: application/octet-stream; name="etherswitch-complete.patch" Content-Transfer-Encoding: 7bit diff --git a/sbin/Makefile b/sbin/Makefile index f9ba4ca..0cb421f 100644 --- a/sbin/Makefile +++ b/sbin/Makefile @@ -20,6 +20,7 @@ SUBDIR=adjkerntz \ dump \ dumpfs \ dumpon \ + etherswitchcfg \ ffsinfo \ fsck \ fsck_ffs \ diff --git a/sbin/etherswitchcfg/Makefile b/sbin/etherswitchcfg/Makefile new file mode 100644 index 0000000..d21c88b --- /dev/null +++ b/sbin/etherswitchcfg/Makefile @@ -0,0 +1,9 @@ +# @(#)Makefile 5.4 (Berkeley) 6/5/91 +# $FreeBSD: head/sbin/comcontrol/Makefile 198236 2009-10-19 16:00:24Z ru $ + +PROG= etherswitchcfg +MAN= etherswitchcfg.8 +SRCS= etherswitchcfg.c ifmedia.c +CFLAGS+= -I${.CURDIR}/../../sys + +.include <bsd.prog.mk> diff --git a/sbin/etherswitchcfg/etherswitchcfg.8 b/sbin/etherswitchcfg/etherswitchcfg.8 new file mode 100644 index 0000000..538950c --- /dev/null +++ b/sbin/etherswitchcfg/etherswitchcfg.8 @@ -0,0 +1,114 @@ +.\" $FreeBSD$ +.Dd December 15, 2011 +.Dt ETHERSWITCHCFG 8 +.Os +.Sh NAME +.Nm etherswitchcfg +.Nd configure a built-in Ethernet switch +.Sh SYNOPSIS +.Nm +.Op Fl "f control file" +.Ar info +.Nm +.Op Fl "f control file" +.Ar phy +.Ar phy.register[=value] +.Nm +.Op Fl "f control file" +.Ar port%d +.Ar command parameter +.Nm +.Op Fl "f control file" +.Ar reg +.Ar register[=value] +.Nm +.Op Fl "f control file" +.Ar vlangroup%d +.Ar command parameter +.Sh DESCRIPTION +The +.Nm +utility is used to configure an Ethernet switch built into the system. +.Nm +accepts a number of options: +.Bl -tag -width ".Fl f" -compact +.It Fl "f control file" +Specifies the +.Xr etherswitch 4 +control file that represents the switch to be configured. +It defaults to +.Li /dev/etherswitch0 . +.It Fl m +When reporting port information, also list available media options for +that port. +.It Fl v +Produce more verbose output. +Without this flag, lines that represent inactive or empty configuration +options are omitted. +.El +.Ss phy +The phy command provides access to the registers of the PHYs attached +to or integrated into the switch controller. +PHY registers are specified as phy.register, +where +.Ar phy +is usually the port number, and +.Ar register +is the register number. +Both can be provided as decimal, octal or hexadecimal numbers in any of the formats +understood by +.Xr strtol 4 . +To set the register value, use the form instance.register=value. +.Ss port +The port command selects one of the ports of the switch. +It supports the following commands: +.Bl -tag -width ".Ar vlangroup number" -compact +.It Ar vlangroup number +Sets the VLAN group number that is used to process incoming frames that are not tagged. +.It Ar media mediaspec +Specifies the physical media configuration to be configured for a port. +.It Ar mediaopt mediaoption +Specifies a list of media options for a port. See +.Xr ifconfig 8 +for details on +.Ar media and +.Ar mediaopt . +.El +.Ss reg +The reg command provides access to the registers of the switch controller. +.Ss vlangroup +The vlangroup command selects one of the VLAN groups for configuration. +It supports the following commands: +.Bl -tag -width ".Ar vlangroup" -compact +.It Ar vlan VID +Sets the VLAN ID (802.1q VID) for this VLAN group. +Frames transmitted on tagged member ports of this group will be tagged +with this VID. +Incoming frames carrying this tag will be forwarded according to the +configuration of this VLAN group. +.It Ar members port,... +Configures which ports are to be a member of this VLAN group. +The port numbers are given as a comma-separated list. +Each port can optionally be followed by +.Dq t +to indicate that frames on this port are tagged. +.El +.Sh FILES +.Bl -tag -width /dev/etherswitch? -compact +.It Pa /dev/etherswitch? +Control file for the ethernet switch driver. +.El +.Sh EXAMPLES +Configure VLAN group 1 with a VID of 2 and makes ports 0 and 5 members, +while excluding all other ports. +Port 5 will send and receive tagged frames, while port 0 will be untagged. +Incoming untagged frames on port 0 are assigned to vlangroup1. +.Dl # etherswitchcfg vlangroup1 vlan 2 members 0,5t port0 vlangroup 1 +.Sh SEE ALSO +.Xr etherswitch 4 +.Sh HISTORY +.Nm +first appeared in +.Fx 10.0 . +.Sh AUTHORS +.An Stefan Bethke diff --git a/sbin/etherswitchcfg/etherswitchcfg.c b/sbin/etherswitchcfg/etherswitchcfg.c new file mode 100644 index 0000000..e6129f3 --- /dev/null +++ b/sbin/etherswitchcfg/etherswitchcfg.c @@ -0,0 +1,511 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sysexits.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_media.h> +#include <dev/etherswitch/etherswitch.h> + +int get_media_subtype(int, const char *); +int get_media_mode(int, const char *); +int get_media_options(int, const char *); +int lookup_media_word(struct ifmedia_description *, const char *); +void print_media_word(int, int); +void print_media_word_ifconfig(int); + +/* some constants */ +#define IEEE802DOT1Q_VID_MAX 4094 +#define IFMEDIAREQ_NULISTENTRIES 256 + +enum cmdmode { + MODE_NONE = 0, + MODE_PORT, + MODE_VLANGROUP, + MODE_REGISTER, + MODE_PHYREG +}; + +struct cfg { + int fd; + int verbose; + int mediatypes; + const char *controlfile; + etherswitch_info_t info; + enum cmdmode mode; + int unit; +}; + +struct cmds { + enum cmdmode mode; + const char *name; + int args; + void (*f)(struct cfg *, char *argv[]); +}; +struct cmds cmds[]; + + +static void usage(void); + +static int +read_register(struct cfg *cfg, int r) +{ + struct etherswitch_reg er; + + er.reg = r; + if (ioctl(cfg->fd, IOETHERSWITCHGETREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETREG)"); + return (er.val); +} + +static void +write_register(struct cfg *cfg, int r, int v) +{ + struct etherswitch_reg er; + + er.reg = r; + er.val = v; + if (ioctl(cfg->fd, IOETHERSWITCHSETREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETREG)"); +} + +static int +read_phyregister(struct cfg *cfg, int phy, int reg) +{ + struct etherswitch_phyreg er; + + er.phy = phy; + er.reg = reg; + if (ioctl(cfg->fd, IOETHERSWITCHGETPHYREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPHYREG)"); + return (er.val); +} + +static void +write_phyregister(struct cfg *cfg, int phy, int reg, int val) +{ + struct etherswitch_phyreg er; + + er.phy = phy; + er.reg = reg; + er.val = val; + if (ioctl(cfg->fd, IOETHERSWITCHSETPHYREG, &er) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPHYREG)"); +} + +static void +set_port_vlangroup(struct cfg *cfg, char *argv[]) +{ + int v; + etherswitch_port_t p; + + v = strtol(argv[1], NULL, 0); + if (v < 0 || v >= cfg->info.es_nvlangroups) + errx(EX_USAGE, "vlangroup must be between 0 and %d", cfg->info.es_nvlangroups-1); + p.es_port = cfg->unit; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + p.es_vlangroup = v; + if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); +} + +static void +set_port_media(struct cfg *cfg, char *argv[]) +{ + etherswitch_port_t p; + int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; + int subtype; + + bzero(&p, sizeof(p)); + p.es_port = cfg->unit; + p.es_ifmr.ifm_ulist = ifm_ulist; + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + subtype = get_media_subtype(IFM_TYPE(ifm_ulist[0]), argv[1]); + p.es_ifr.ifr_media = (p.es_ifmr.ifm_current & IFM_IMASK) | + IFM_TYPE(ifm_ulist[0]) | subtype; + if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); +} + +static void +set_port_mediaopt(struct cfg *cfg, char *argv[]) +{ + etherswitch_port_t p; + int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; + int options; + + bzero(&p, sizeof(p)); + p.es_port = cfg->unit; + p.es_ifmr.ifm_ulist = ifm_ulist; + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + options = get_media_options(IFM_TYPE(ifm_ulist[0]), argv[1]); + if (options == -1) + errx(EX_USAGE, "invalid media options \"%s\"", argv[1]); + if (options & IFM_HDX) { + p.es_ifr.ifr_media &= ~IFM_FDX; + options &= ~IFM_HDX; + } + p.es_ifr.ifr_media |= options; + if (ioctl(cfg->fd, IOETHERSWITCHSETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETPORT)"); +} + +static void +set_vlangroup_vid(struct cfg *cfg, char *argv[]) +{ + int v; + etherswitch_vlangroup_t vg; + + v = strtol(argv[1], NULL, 0); + if (v < 0 || v >= IEEE802DOT1Q_VID_MAX) + errx(EX_USAGE, "vlan must be between 0 and %d", IEEE802DOT1Q_VID_MAX); + vg.es_vlangroup = cfg->unit; + if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); + vg.es_vid = v; + if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); +} + +static void +set_vlangroup_members(struct cfg *cfg, char *argv[]) +{ + etherswitch_vlangroup_t vg; + int member, untagged; + char *c, *d; + int v; + + member = untagged = 0; + if (strcmp(argv[1], "none") != 0) { + for (c=argv[1]; *c; c=d) { + v = strtol(c, &d, 0); + if (d == c) + break; + if (v < 0 || v >= cfg->info.es_nports) + errx(EX_USAGE, "Member port must be between 0 and %d", cfg->info.es_nports-1); + if (d[0] == ',' || d[0] == '\0' || + ((d[0] == 't' || d[0] == 'T') && (d[1] == ',' || d[1] == '\0'))) { + if (d[0] == 't' || d[0] == 'T') { + untagged &= ~ETHERSWITCH_PORTMASK(v); + d++; + } else + untagged |= ETHERSWITCH_PORTMASK(v); + member |= ETHERSWITCH_PORTMASK(v); + d++; + } else + errx(EX_USAGE, "Invalid members specification \"%s\"", d); + } + } + vg.es_vlangroup = cfg->unit; + if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); + vg.es_member_ports = member; + vg.es_untagged_ports = untagged; + if (ioctl(cfg->fd, IOETHERSWITCHSETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHSETVLANGROUP)"); +} + +static int +set_register(struct cfg *cfg, char *arg) +{ + int a, v; + char *c; + + a = strtol(arg, &c, 0); + if (c==arg) + return (1); + if (*c == '=') { + v = strtol(c+1, NULL, 0); + write_register(cfg, a, v); + } + printf("\treg 0x%04x=0x%04x\n", a, read_register(cfg, a)); + return (0); +} + +static int +set_phyregister(struct cfg *cfg, char *arg) +{ + int phy, reg, val; + char *c, *d; + + phy = strtol(arg, &c, 0); + if (c==arg) + return (1); + if (*c != '.') + return (1); + d = c+1; + reg = strtol(d, &c, 0); + if (d == c) + return (1); + if (*c == '=') { + val = strtol(c+1, NULL, 0); + write_phyregister(cfg, phy, reg, val); + } + printf("\treg %d.0x%02x=0x%04x\n", phy, reg, read_phyregister(cfg, phy, reg)); + return (0); +} + +static void +print_port(struct cfg *cfg, int port) +{ + etherswitch_port_t p; + int ifm_ulist[IFMEDIAREQ_NULISTENTRIES]; + int i; + + bzero(&p, sizeof(p)); + p.es_port = port; + p.es_ifmr.ifm_ulist = ifm_ulist; + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + if (ioctl(cfg->fd, IOETHERSWITCHGETPORT, &p) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETPORT)"); + printf("port%d:\n", port); + printf("\tvlangroup: %d\n", p.es_vlangroup); + printf("\tmedia: "); + print_media_word(p.es_ifmr.ifm_current, 1); + if (p.es_ifmr.ifm_active != p.es_ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(p.es_ifmr.ifm_active, 0); + putchar(')'); + } + putchar('\n'); + printf("\tstatus: %s\n", (p.es_ifmr.ifm_status & IFM_ACTIVE) != 0 ? "active" : "no carrier"); + if (cfg->mediatypes) { + printf("\tsupported media:\n"); + if (p.es_ifmr.ifm_count > IFMEDIAREQ_NULISTENTRIES) + p.es_ifmr.ifm_count = IFMEDIAREQ_NULISTENTRIES; + for (i=0; i<p.es_ifmr.ifm_count; i++) { + printf("\t\tmedia "); + print_media_word(ifm_ulist[i], 0); + putchar('\n'); + } + } +} + +static void +print_vlangroup(struct cfg *cfg, int vlangroup) +{ + etherswitch_vlangroup_t vg; + int i, comma; + + vg.es_vlangroup = vlangroup; + if (ioctl(cfg->fd, IOETHERSWITCHGETVLANGROUP, &vg) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETVLANGROUP)"); + if (cfg->verbose == 0 && vg.es_member_ports == 0) + return; + printf("vlangroup%d:\n", vlangroup); + printf("\tvlan: %d\n", vg.es_vid); + printf("\tmembers "); + comma = 0; + if (vg.es_member_ports != 0) + for (i=0; i<cfg->info.es_nports; i++) { + if ((vg.es_member_ports & ETHERSWITCH_PORTMASK(i)) != 0) { + if (comma) + printf(","); + printf("%d", i); + if ((vg.es_untagged_ports & ETHERSWITCH_PORTMASK(i)) == 0) + printf("t"); + comma = 1; + } + } + else + printf("none"); + printf("\n"); +} + +static void +print_info(struct cfg *cfg) +{ + const char *c; + int i; + + c = strrchr(cfg->controlfile, '/'); + if (c != NULL) + c = c + 1; + else + c = cfg->controlfile; + if (cfg->verbose) + printf("%s: %s with %d ports and %d VLAN groups\n", + c, cfg->info.es_name, cfg->info.es_nports, cfg->info.es_nvlangroups); + for (i=0; i<cfg->info.es_nports; i++) { + print_port(cfg, i); + } + for (i=0; i<cfg->info.es_nvlangroups; i++) { + print_vlangroup(cfg, i); + } +} + +static void +usage(void) +{ + fprintf(stderr, "usage: etherswitchctl\n"); + exit(EX_USAGE); +} + +static void +newmode(struct cfg *cfg, enum cmdmode mode) +{ + if (mode == cfg->mode) + return; + switch (cfg->mode) { + case MODE_NONE: + break; + case MODE_PORT: + print_port(cfg, cfg->unit); + break; + case MODE_VLANGROUP: + print_vlangroup(cfg, cfg->unit); + break; + case MODE_REGISTER: + case MODE_PHYREG: + break; + } + cfg->mode = mode; +} + +int +main(int argc, char *argv[]) +{ + int ch; + struct cfg cfg; + int i; + + bzero(&cfg, sizeof(cfg)); + cfg.controlfile = "/dev/etherswitch0"; + while ((ch = getopt(argc, argv, "f:mv?")) != -1) + switch(ch) { + case 'f': + cfg.controlfile = optarg; + break; + case 'm': + cfg.mediatypes++; + break; + case 'v': + cfg.verbose++; + break; + case '?': + /* FALLTHROUGH */ + default: + usage(); + } + argc -= optind; + argv += optind; + cfg.fd = open(cfg.controlfile, O_RDONLY); + if (cfg.fd < 0) + err(EX_UNAVAILABLE, "Can't open control file: %s", cfg.controlfile); + if (ioctl(cfg.fd, IOETHERSWITCHGETINFO, &cfg.info) != 0) + err(EX_OSERR, "ioctl(IOETHERSWITCHGETINFO)"); + if (argc == 0) { + print_info(&cfg); + return (0); + } + cfg.mode = MODE_NONE; + while (argc > 0) { + switch(cfg.mode) { + case MODE_NONE: + if (strcmp(argv[0], "info") == 0) { + print_info(&cfg); + } else if (sscanf(argv[0], "port%d", &cfg.unit) == 1) { + if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nports) + errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nports); + newmode(&cfg, MODE_PORT); + } else if (sscanf(argv[0], "vlangroup%d", &cfg.unit) == 1) { + if (cfg.unit < 0 || cfg.unit >= cfg.info.es_nvlangroups) + errx(EX_USAGE, "port unit must be between 0 and %d", cfg.info.es_nvlangroups); + newmode(&cfg, MODE_VLANGROUP); + } else if (strcmp(argv[0], "phy") == 0) { + newmode(&cfg, MODE_PHYREG); + } else if (strcmp(argv[0], "reg") == 0) { + newmode(&cfg, MODE_REGISTER); + } else { + errx(EX_USAGE, "Unknown command \"%s\"", argv[0]); + } + break; + case MODE_PORT: + case MODE_VLANGROUP: + for(i=0; cmds[i].name != NULL; i++) { + if (cfg.mode == cmds[i].mode && strcmp(argv[0], cmds[i].name) == 0 + && argc >= cmds[i].args) { + (cmds[i].f)(&cfg, argv); + argc -= cmds[i].args; + argv += cmds[i].args; + break; + } + } + if (cmds[i].name == NULL) { + newmode(&cfg, MODE_NONE); + continue; + } + break; + case MODE_REGISTER: + if (set_register(&cfg, argv[0]) != 0) { + newmode(&cfg, MODE_NONE); + continue; + } + break; + case MODE_PHYREG: + if (set_phyregister(&cfg, argv[0]) != 0) { + newmode(&cfg, MODE_NONE); + continue; + } + break; + } + argc--; + argv++; + } + /* switch back to command mode to print configuration for last command */ + newmode(&cfg, MODE_NONE); + close(cfg.fd); + return (0); +} + +struct cmds cmds[] = { + { MODE_PORT, "vlangroup", 1, set_port_vlangroup }, + { MODE_PORT, "media", 1, set_port_media }, + { MODE_PORT, "mediaopt", 1, set_port_mediaopt }, + { MODE_VLANGROUP, "vlan", 1, set_vlangroup_vid }, + { MODE_VLANGROUP, "members", 1, set_vlangroup_members }, + { 0, NULL, 0, NULL } +}; diff --git a/sbin/etherswitchcfg/ifmedia.c b/sbin/etherswitchcfg/ifmedia.c new file mode 100644 index 0000000..49881fe --- /dev/null +++ b/sbin/etherswitchcfg/ifmedia.c @@ -0,0 +1,812 @@ +/* $NetBSD: ifconfig.c,v 1.34 1997/04/21 01:17:58 lukem Exp $ */ +/* $FreeBSD: head/sbin/ifconfig/ifmedia.c 221954 2011-05-15 12:51:00Z marius $ */ + +/* + * Copyright (c) 1997 Jason R. Thorpe. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed for the NetBSD Project + * by Jason R. Thorpe. + * 4. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * based on sbin/ifconfig/ifmedia.c r221954 + */ + +#include <sys/param.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/sysctl.h> +#include <sys/time.h> + +#include <net/if.h> +#include <net/if_dl.h> +#include <net/if_types.h> +#include <net/if_media.h> +#include <net/route.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void domediaopt(const char *, int, int); +int get_media_subtype(int, const char *); +int get_media_mode(int, const char *); +int get_media_options(int, const char *); +int lookup_media_word(struct ifmedia_description *, const char *); +void print_media_word(int, int); +void print_media_word_ifconfig(int); + +#if 0 +static struct ifmedia_description *get_toptype_desc(int); +static struct ifmedia_type_to_subtype *get_toptype_ttos(int); +static struct ifmedia_description *get_subtype_desc(int, + struct ifmedia_type_to_subtype *ttos); + +#define IFM_OPMODE(x) \ + ((x) & (IFM_IEEE80211_ADHOC | IFM_IEEE80211_HOSTAP | \ + IFM_IEEE80211_IBSS | IFM_IEEE80211_WDS | IFM_IEEE80211_MONITOR | \ + IFM_IEEE80211_MBSS)) +#define IFM_IEEE80211_STA 0 + +static void +media_status(int s) +{ + struct ifmediareq ifmr; + int *media_list, i; + + (void) memset(&ifmr, 0, sizeof(ifmr)); + (void) strncpy(ifmr.ifm_name, name, sizeof(ifmr.ifm_name)); + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) { + /* + * Interface doesn't support SIOC{G,S}IFMEDIA. + */ + return; + } + + if (ifmr.ifm_count == 0) { + warnx("%s: no media types?", name); + return; + } + + media_list = (int *)malloc(ifmr.ifm_count * sizeof(int)); + if (media_list == NULL) + err(1, "malloc"); + ifmr.ifm_ulist = media_list; + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + + printf("\tmedia: "); + print_media_word(ifmr.ifm_current, 1); + if (ifmr.ifm_active != ifmr.ifm_current) { + putchar(' '); + putchar('('); + print_media_word(ifmr.ifm_active, 0); + putchar(')'); + } + + putchar('\n'); + + if (ifmr.ifm_status & IFM_AVALID) { + printf("\tstatus: "); + switch (IFM_TYPE(ifmr.ifm_active)) { + case IFM_ETHER: + case IFM_ATM: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("active"); + else + printf("no carrier"); + break; + + case IFM_FDDI: + case IFM_TOKEN: + if (ifmr.ifm_status & IFM_ACTIVE) + printf("inserted"); + else + printf("no ring"); + break; + + case IFM_IEEE80211: + if (ifmr.ifm_status & IFM_ACTIVE) { + /* NB: only sta mode associates */ + if (IFM_OPMODE(ifmr.ifm_active) == IFM_IEEE80211_STA) + printf("associated"); + else + printf("running"); + } else + printf("no carrier"); + break; + } + putchar('\n'); + } + + if (ifmr.ifm_count > 0 && supmedia) { + printf("\tsupported media:\n"); + for (i = 0; i < ifmr.ifm_count; i++) { + printf("\t\t"); + print_media_word_ifconfig(media_list[i]); + putchar('\n'); + } + } + + free(media_list); +} + +struct ifmediareq * +ifmedia_getstate(int s) +{ + static struct ifmediareq *ifmr = NULL; + int *mwords; + + if (ifmr == NULL) { + ifmr = (struct ifmediareq *)malloc(sizeof(struct ifmediareq)); + if (ifmr == NULL) + err(1, "malloc"); + + (void) memset(ifmr, 0, sizeof(struct ifmediareq)); + (void) strncpy(ifmr->ifm_name, name, + sizeof(ifmr->ifm_name)); + + ifmr->ifm_count = 0; + ifmr->ifm_ulist = NULL; + + /* + * We must go through the motions of reading all + * supported media because we need to know both + * the current media type and the top-level type. + */ + + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) { + err(1, "SIOCGIFMEDIA"); + } + + if (ifmr->ifm_count == 0) + errx(1, "%s: no media types?", name); + + mwords = (int *)malloc(ifmr->ifm_count * sizeof(int)); + if (mwords == NULL) + err(1, "malloc"); + + ifmr->ifm_ulist = mwords; + if (ioctl(s, SIOCGIFMEDIA, (caddr_t)ifmr) < 0) + err(1, "SIOCGIFMEDIA"); + } + + return ifmr; +} + +static void +setifmediacallback(int s, void *arg) +{ + struct ifmediareq *ifmr = (struct ifmediareq *)arg; + static int did_it = 0; + + if (!did_it) { + ifr.ifr_media = ifmr->ifm_current; + if (ioctl(s, SIOCSIFMEDIA, (caddr_t)&ifr) < 0) + err(1, "SIOCSIFMEDIA (media)"); + free(ifmr->ifm_ulist); + free(ifmr); + did_it = 1; + } +} + +static void +setmedia(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int subtype; + + ifmr = ifmedia_getstate(s); + + /* + * We are primarily concerned with the top-level type. + * However, "current" may be only IFM_NONE, so we just look + * for the top-level type in the first "supported type" + * entry. + * + * (I'm assuming that all supported media types for a given + * interface will be the same top-level type..) + */ + subtype = get_media_subtype(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & IFM_IMASK) | + IFM_TYPE(ifmr->ifm_ulist[0]) | subtype; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 0, s); +} + +static void +unsetmediaopt(const char *val, int d, int s, const struct afswtch *afp) +{ + + domediaopt(val, 1, s); +} + +static void +domediaopt(const char *val, int clear, int s) +{ + struct ifmediareq *ifmr; + int options; + + ifmr = ifmedia_getstate(s); + + options = get_media_options(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = ifmr->ifm_current; + if (clear) + ifr.ifr_media &= ~options; + else { + if (options & IFM_HDX) { + ifr.ifr_media &= ~IFM_FDX; + options &= ~IFM_HDX; + } + ifr.ifr_media |= options; + } + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediainst(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int inst; + + ifmr = ifmedia_getstate(s); + + inst = atoi(val); + if (inst < 0 || inst > (int)IFM_INST_MAX) + errx(1, "invalid media instance: %s", val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_IMASK) | inst << IFM_ISHIFT; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} + +static void +setmediamode(const char *val, int d, int s, const struct afswtch *afp) +{ + struct ifmediareq *ifmr; + int mode; + + ifmr = ifmedia_getstate(s); + + mode = get_media_mode(IFM_TYPE(ifmr->ifm_ulist[0]), val); + + strncpy(ifr.ifr_name, name, sizeof(ifr.ifr_name)); + ifr.ifr_media = (ifmr->ifm_current & ~IFM_MMASK) | mode; + + ifmr->ifm_current = ifr.ifr_media; + callback_register(setifmediacallback, (void *)ifmr); +} +#endif + +/********************************************************************** + * A good chunk of this is duplicated from sys/net/ifmedia.c + **********************************************************************/ + +static struct ifmedia_description ifm_type_descriptions[] = + IFM_TYPE_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_descriptions[] = + IFM_SUBTYPE_ETHERNET_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ethernet_aliases[] = + IFM_SUBTYPE_ETHERNET_ALIASES; + +static struct ifmedia_description ifm_subtype_ethernet_option_descriptions[] = + IFM_SUBTYPE_ETHERNET_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_descriptions[] = + IFM_SUBTYPE_TOKENRING_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_tokenring_aliases[] = + IFM_SUBTYPE_TOKENRING_ALIASES; + +static struct ifmedia_description ifm_subtype_tokenring_option_descriptions[] = + IFM_SUBTYPE_TOKENRING_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_descriptions[] = + IFM_SUBTYPE_FDDI_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_fddi_aliases[] = + IFM_SUBTYPE_FDDI_ALIASES; + +static struct ifmedia_description ifm_subtype_fddi_option_descriptions[] = + IFM_SUBTYPE_FDDI_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_descriptions[] = + IFM_SUBTYPE_IEEE80211_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_ieee80211_aliases[] = + IFM_SUBTYPE_IEEE80211_ALIASES; + +static struct ifmedia_description ifm_subtype_ieee80211_option_descriptions[] = + IFM_SUBTYPE_IEEE80211_OPTION_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_descriptions[] = + IFM_SUBTYPE_IEEE80211_MODE_DESCRIPTIONS; + +struct ifmedia_description ifm_subtype_ieee80211_mode_aliases[] = + IFM_SUBTYPE_IEEE80211_MODE_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_descriptions[] = + IFM_SUBTYPE_ATM_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_atm_aliases[] = + IFM_SUBTYPE_ATM_ALIASES; + +static struct ifmedia_description ifm_subtype_atm_option_descriptions[] = + IFM_SUBTYPE_ATM_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_descriptions[] = + IFM_SUBTYPE_SHARED_DESCRIPTIONS; + +static struct ifmedia_description ifm_subtype_shared_aliases[] = + IFM_SUBTYPE_SHARED_ALIASES; + +static struct ifmedia_description ifm_shared_option_descriptions[] = + IFM_SHARED_OPTION_DESCRIPTIONS; + +static struct ifmedia_description ifm_shared_option_aliases[] = + IFM_SHARED_OPTION_ALIASES; + +struct ifmedia_type_to_subtype { + struct { + struct ifmedia_description *desc; + int alias; + } subtypes[5]; + struct { + struct ifmedia_description *desc; + int alias; + } options[4]; + struct { + struct ifmedia_description *desc; + int alias; + } modes[3]; +}; + +/* must be in the same order as IFM_TYPE_DESCRIPTIONS */ +static struct ifmedia_type_to_subtype ifmedia_types_to_subtypes[] = { + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ethernet_descriptions[0], 0 }, + { &ifm_subtype_ethernet_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_ethernet_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_tokenring_descriptions[0], 0 }, + { &ifm_subtype_tokenring_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_tokenring_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_fddi_descriptions[0], 0 }, + { &ifm_subtype_fddi_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_fddi_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_ieee80211_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_ieee80211_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { &ifm_subtype_ieee80211_mode_descriptions[0], 0 }, + { &ifm_subtype_ieee80211_mode_aliases[0], 0 }, + { NULL, 0 }, + }, + }, + { + { + { &ifm_subtype_shared_descriptions[0], 0 }, + { &ifm_subtype_shared_aliases[0], 1 }, + { &ifm_subtype_atm_descriptions[0], 0 }, + { &ifm_subtype_atm_aliases[0], 1 }, + { NULL, 0 }, + }, + { + { &ifm_shared_option_descriptions[0], 0 }, + { &ifm_shared_option_aliases[0], 1 }, + { &ifm_subtype_atm_option_descriptions[0], 0 }, + { NULL, 0 }, + }, + { + { NULL, 0 }, + }, + }, +}; + +int +get_media_subtype(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->subtypes[i].desc, val); + if (rval != -1) + return (rval); + } + errx(1, "unknown media subtype: %s", val); + /*NOTREACHED*/ +} + +int +get_media_mode(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int rval, i; + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media mode 0x%x", type); + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + rval = lookup_media_word(ttos->modes[i].desc, val); + if (rval != -1) + return (rval); + } + return -1; +} + +int +get_media_options(int type, const char *val) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + char *optlist, *optptr; + int option = 0, i, rval = 0; + + /* We muck with the string, so copy it. */ + optlist = strdup(val); + if (optlist == NULL) + err(1, "strdup"); + + /* Find the top-level interface type. */ + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (type == desc->ifmt_word) + break; + if (desc->ifmt_string == NULL) + errx(1, "unknown media type 0x%x", type); + + /* + * Look up the options in the user-provided comma-separated + * list. + */ + optptr = optlist; + for (; (optptr = strtok(optptr, ",")) != NULL; optptr = NULL) { + for (i = 0; ttos->options[i].desc != NULL; i++) { + option = lookup_media_word(ttos->options[i].desc, optptr); + if (option != -1) + break; + } + if (option == 0) + errx(1, "unknown option: %s", optptr); + rval |= option; + } + + free(optlist); + return (rval); +} + +int +lookup_media_word(struct ifmedia_description *desc, const char *val) +{ + + for (; desc->ifmt_string != NULL; desc++) + if (strcasecmp(desc->ifmt_string, val) == 0) + return (desc->ifmt_word); + + return (-1); +} + +static struct ifmedia_description *get_toptype_desc(int ifmw) +{ + struct ifmedia_description *desc; + + for (desc = ifm_type_descriptions; desc->ifmt_string != NULL; desc++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return desc; +} + +static struct ifmedia_type_to_subtype *get_toptype_ttos(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + + for (desc = ifm_type_descriptions, ttos = ifmedia_types_to_subtypes; + desc->ifmt_string != NULL; desc++, ttos++) + if (IFM_TYPE(ifmw) == desc->ifmt_word) + break; + + return ttos; +} + +static struct ifmedia_description *get_subtype_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->subtypes[i].desc != NULL; i++) { + if (ttos->subtypes[i].alias) + continue; + for (desc = ttos->subtypes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_SUBTYPE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +static struct ifmedia_description *get_mode_desc(int ifmw, + struct ifmedia_type_to_subtype *ttos) +{ + int i; + struct ifmedia_description *desc; + + for (i = 0; ttos->modes[i].desc != NULL; i++) { + if (ttos->modes[i].alias) + continue; + for (desc = ttos->modes[i].desc; + desc->ifmt_string != NULL; desc++) { + if (IFM_MODE(ifmw) == desc->ifmt_word) + return desc; + } + } + + return NULL; +} + +void +print_media_word(int ifmw, int print_toptype) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; + } else if (print_toptype) { + printf("%s", desc->ifmt_string); + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + + if (print_toptype) + putchar(' '); + + printf("%s", desc->ifmt_string); + + if (print_toptype) { + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL && strcasecmp("autoselect", desc->ifmt_string)) + printf(" mode %s", desc->ifmt_string); + } + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" <"); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + printf("%s", seen_option ? ">" : ""); + + if (print_toptype && IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +void +print_media_word_ifconfig(int ifmw) +{ + struct ifmedia_description *desc; + struct ifmedia_type_to_subtype *ttos; + int seen_option = 0, i; + + /* Find the top-level interface type. */ + desc = get_toptype_desc(ifmw); + ttos = get_toptype_ttos(ifmw); + if (desc->ifmt_string == NULL) { + printf("<unknown type>"); + return; + } + + /* + * Don't print the top-level type; it's not like we can + * change it, or anything. + */ + + /* Find subtype. */ + desc = get_subtype_desc(ifmw, ttos); + if (desc == NULL) { + printf("<unknown subtype>"); + return; + } + + printf("media %s", desc->ifmt_string); + + desc = get_mode_desc(ifmw, ttos); + if (desc != NULL) + printf(" mode %s", desc->ifmt_string); + + /* Find options. */ + for (i = 0; ttos->options[i].desc != NULL; i++) { + if (ttos->options[i].alias) + continue; + for (desc = ttos->options[i].desc; + desc->ifmt_string != NULL; desc++) { + if (ifmw & desc->ifmt_word) { + if (seen_option == 0) + printf(" mediaopt "); + printf("%s%s", seen_option++ ? "," : "", + desc->ifmt_string); + } + } + } + + if (IFM_INST(ifmw) != 0) + printf(" instance %d", IFM_INST(ifmw)); +} + +/********************************************************************** + * ...until here. + **********************************************************************/ diff --git a/sys/conf/files b/sys/conf/files index 2f8b5f6..ace37f3 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -3523,3 +3523,11 @@ dev/xen/control/control.c optional xen | xenhvm dev/xen/netfront/netfront.c optional xen | xenhvm dev/xen/xenpci/xenpci.c optional xenpci dev/xen/xenpci/evtchn.c optional xenpci + +dev/etherswitch/arswitch.c optional arswitch +dev/etherswitch/etherswitch.c optional etherswitch +dev/etherswitch/etherswitch_if.m optional etherswitch +dev/etherswitch/mdio_if.m optional miiproxy +dev/etherswitch/mdio.c optional miiproxy +dev/etherswitch/miiproxy.c optional miiproxy +dev/etherswitch/rtl8366rb.c optional rtl8366rb diff --git a/sys/conf/kmod.mk b/sys/conf/kmod.mk index 7bf7643..182a687 100644 --- a/sys/conf/kmod.mk +++ b/sys/conf/kmod.mk @@ -352,7 +352,8 @@ MFILES?= dev/acpica/acpi_if.m dev/acpi_support/acpi_wmi_if.m \ kern/bus_if.m kern/clock_if.m \ kern/cpufreq_if.m kern/device_if.m kern/serdev_if.m \ libkern/iconv_converter_if.m opencrypto/cryptodev_if.m \ - pc98/pc98/canbus_if.m + pc98/pc98/canbus_if.m dev/etherswitch/etherswitch_if.m \ + dev/etherswitch/mdio_if.h .for _srcsrc in ${MFILES} .for _ext in c h diff --git a/sys/conf/options.mips b/sys/conf/options.mips index 0384283..5a03c80 100644 --- a/sys/conf/options.mips +++ b/sys/conf/options.mips @@ -70,7 +70,8 @@ OCTEON_BOARD_CAPK_0100ND opt_cvmx.h # # Options that control the Atheros SoC peripherals # -ARGE_DEBUG opt_global.h +ARGE_DEBUG opt_arge.h +ARGE_MDIO opt_arge.h # # Options that control the Ralink RT305xF Etherenet MAC. diff --git a/sys/dev/etherswitch/arswitch.c b/sys/dev/etherswitch/arswitch.c new file mode 100644 index 0000000..460e42b --- /dev/null +++ b/sys/dev/etherswitch/arswitch.c @@ -0,0 +1,675 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <machine/bus.h> +#include <dev/iicbus/iic.h> +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> +#include <dev/etherswitch/mdio.h> + +#include <dev/etherswitch/etherswitch.h> +#include <dev/etherswitch/arswitchreg.h> + +#include "mdio_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + + +struct arswitch_softc { + struct mtx sc_mtx; /* serialize access to softc */ + device_t dev; + int phy4cpu; /* PHY4 is connected to the CPU */ + int numphys; /* PHYs we manage */ + int page; + char *ifname[AR8X16_NUM_PHYS]; + device_t miibus[AR8X16_NUM_PHYS]; + struct ifnet *ifp[AR8X16_NUM_PHYS]; + struct callout callout_tick; + etherswitch_info_t info; +}; + + + +#define ARSWITCH_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define ARSWITCH_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define ARSWITCH_LOCK_ASSERT(_sc, _what) mtx_assert(&(_s)c->sc_mtx, (_what)) +#define ARSWITCH_TRYLOCK(_sc) mtx_trylock(&(_sc)->sc_mtx) + + +#if defined(DEBUG) +#define DPRINTF(dev, args...) device_printf(dev, args) +#define DEVERR(dev, err, fmt, args...) do { \ + if (err != 0) device_printf(dev, fmt, err, args); \ + } while (0) +#define DEBUG_INCRVAR(var) do { \ + var++; \ + } while (0) + +static SYSCTL_NODE(_debug, OID_AUTO, arswitch, CTLFLAG_RD, 0, "arswitch"); +#else +#define DPRINTF(dev, args...) +#define DEVERR(dev, err, fmt, args...) +#define DEBUG_INCRVAR(var) +#endif + +static inline int arswitch_portforphy(int phy); +static void arswitch_tick(void *arg); +static int arswitch_ifmedia_upd(struct ifnet *); +static void arswitch_ifmedia_sts(struct ifnet *, struct ifmediareq *); +static int arswitch_readreg(device_t dev, int addr); +static int arswitch_writereg(device_t dev, int addr, int value); +int arswitch_modifyreg(device_t dev, int addr, int mask, int set); + +static void +arswitch_identify(driver_t *driver, device_t parent) +{ + device_t child; + + if (device_find_child(parent, driver->name, -1) == NULL) { + child = BUS_ADD_CHILD(parent, 0, driver->name, -1); + } +} + +static int +arswitch_probe(device_t dev) +{ + struct arswitch_softc *sc; + uint32_t id; + char *chipname, desc[256]; + + sc = device_get_softc(dev); + bzero(sc, sizeof(*sc)); + sc->page = -1; + id = arswitch_readreg(dev, AR8X16_REG_MASK_CTRL); + switch ((id & AR8X16_MASK_CTRL_VER_MASK) >> AR8X16_MASK_CTRL_VER_SHIFT) { + case 1: + chipname = "AR8216"; + break; + case 2: + chipname = "AR8226"; + break; + case 16: + chipname = "AR8316"; + break; + default: + chipname = NULL; + } + DPRINTF(dev, "chipname=%s, rev=%02x\n", chipname, id & AR8X16_MASK_CTRL_REV_MASK); + if (chipname != NULL) { + snprintf(desc, sizeof(desc), "Atheros %s Ethernet Switch", chipname); + device_set_desc_copy(dev, desc); + return (BUS_PROBE_DEFAULT); + } + return (ENXIO); +} + +static int +arswitch_attach_phys(struct arswitch_softc *sc) +{ + int phy, err = 0; + char name[IFNAMSIZ]; + + /* PHYs need an interface, so we generate a dummy one */ + snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(sc->dev)); + for (phy = 0; phy < sc->numphys; phy++) { + sc->ifp[phy] = if_alloc(IFT_ETHER); + sc->ifp[phy]->if_softc = sc; + sc->ifp[phy]->if_flags |= IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING + | IFF_SIMPLEX; + sc->ifname[phy] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); + bcopy(name, sc->ifname[phy], strlen(name)+1); + if_initname(sc->ifp[phy], sc->ifname[phy], arswitch_portforphy(phy)); + err = mii_attach(sc->dev, &sc->miibus[phy], sc->ifp[phy], + arswitch_ifmedia_upd, arswitch_ifmedia_sts, \ + BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + DPRINTF(sc->dev, "%s attached to pseudo interface %s\n", + device_get_nameunit(sc->miibus[phy]), sc->ifp[phy]->if_xname); + if (err != 0) { + device_printf(sc->dev, "attaching PHY %d failed\n", phy); + } + } + return (err); +} + +static int +arswitch_attach(device_t dev) +{ + struct arswitch_softc *sc; + int err = 0; + + sc = device_get_softc(dev); + bzero(sc, sizeof(*sc)); + sc->dev = dev; + mtx_init(&sc->sc_mtx, "arswitch", NULL, MTX_DEF); + sc->page = -1; + + sc->info.es_nports = 5; /* XXX technically 6, but 6th not used */ + sc->info.es_nvlangroups = 16; + strlcpy(sc->info.es_name, device_get_desc(dev), sizeof(sc->info.es_name)); + + /* XXX make this configurable */ + sc->numphys = 4; + sc->phy4cpu = 1; + +#ifdef NOTYET + arswitch_writereg(dev, AR8X16_REG_MASK_CTRL, AR8X16_MASK_CTRL_SOFT_RESET); + DELAY(1000); + if (arswitch_readreg(dev, AR8X16_REG_MASK_CTRL) & AR8X16_MASK_CTRL_SOFT_RESET) { + device_printf(dev, "unable to reset switch\n"); + return (ENXIO); + } + arswitch_modifyreg(dev, AR8X16_REG_GLOBAL_CTRL, + AR8X16_FLOOD_MASK_BCAST_TO_CPU, + AR8X16_FLOOD_MASK_BCAST_TO_CPU); +#endif + err = arswitch_attach_phys(sc); + if (err != 0) + return (err); + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + err = bus_generic_attach(dev); + if (err != 0) + return (err); + + callout_init_mtx(&sc->callout_tick, &sc->sc_mtx, 0); + arswitch_tick(sc); + + return (err); +} + +static int +arswitch_detach(device_t dev) +{ + struct arswitch_softc *sc = device_get_softc(dev); + int i; + + callout_drain(&sc->callout_tick); + + for (i=0; i < sc->numphys; i++) { + if (sc->miibus[i] != NULL) + device_delete_child(dev, sc->miibus[i]); + if (sc->ifp[i] != NULL) + if_free(sc->ifp[i]); + free(sc->ifname[i], M_DEVBUF); + } + + bus_generic_detach(dev); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +/* + * Convert PHY number to port number. PHY0 is connected to port 1, PHY1 to + * port 2, etc. + */ +static inline int +arswitch_portforphy(int phy) +{ + return (phy+1); +} + +static inline struct mii_data * +arswitch_miiforport(struct arswitch_softc *sc, int port) +{ + int phy = port-1; + + if (phy < 0 || phy >= sc->numphys) + return (NULL); + return (device_get_softc(sc->miibus[phy])); +} + +/* + * Convert port status to ifmedia. + */ +static void +arswitch_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active) +{ + *media_active = IFM_ETHER; + *media_status = IFM_AVALID; + + if ((portstatus & AR8X16_PORT_STS_LINK_UP) != 0) + *media_status |= IFM_ACTIVE; + else { + *media_active |= IFM_NONE; + return; + } + switch (portstatus & AR8X16_PORT_STS_SPEED_MASK) { + case AR8X16_PORT_STS_SPEED_10: + *media_active |= IFM_10_T; + break; + case AR8X16_PORT_STS_SPEED_100: + *media_active |= IFM_100_TX; + break; + case AR8X16_PORT_STS_SPEED_1000: + *media_active |= IFM_1000_T; + break; + } + if ((portstatus & AR8X16_PORT_STS_DUPLEX) == 0) + *media_active |= IFM_FDX; + else + *media_active |= IFM_HDX; + if ((portstatus & AR8X16_PORT_STS_TXFLOW) != 0) + *media_active |= IFM_ETH_TXPAUSE; + if ((portstatus & AR8X16_PORT_STS_RXFLOW) != 0) + *media_active |= IFM_ETH_RXPAUSE; +} + +/* + * Poll the status for all PHYs. We're using the switch port status because + * thats a lot quicker to read than talking to all the PHYs. Care must be + * taken that the resulting ifmedia_active is identical to what the PHY will + * compute, or gratuitous link status changes will occur whenever the PHYs + * update function is called. + */ +static void +arswitch_miipollstat(struct arswitch_softc *sc) +{ + int i; + struct mii_data *mii; + struct mii_softc *miisc; + int portstatus; + + for (i = 0; i < sc->numphys; i++) { + if (sc->miibus[i] == NULL) + continue; + mii = device_get_softc(sc->miibus[i]); + portstatus = arswitch_readreg(sc->dev, + AR8X16_REG_PORT_STS(arswitch_portforphy(i))); + //DPRINTF(sc->dev, "p[%d]=%b\n", arge_portforphy(i), portstatus, + // "\20\3TXMAC\4RXMAC\5TXFLOW\6RXFLOW\7DUPLEX\11LINK_UP\12LINK_AUTO\13LINK_PAUSE"); + arswitch_update_ifmedia(portstatus, &mii->mii_media_status, &mii->mii_media_active); + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != miisc->mii_inst) + continue; + mii_phy_update(miisc, MII_POLLSTAT); + } + } +} + +static void +arswitch_tick(void *arg) +{ + struct arswitch_softc *sc = arg; + + arswitch_miipollstat(sc); + callout_reset(&sc->callout_tick, hz, arswitch_tick, sc); +} + +static etherswitch_info_t * +arswitch_getinfo(device_t dev) +{ + struct arswitch_softc *sc = device_get_softc(dev); + + return (&sc->info); +} + +static inline void +arswitch_split_setpage(device_t dev, uint32_t addr, uint16_t *phy, uint16_t *reg) +{ + struct arswitch_softc *sc = device_get_softc(dev); + uint16_t page; + + page = ((addr) >> 9) & 0xffff; + *phy = (((addr) >> 6) & 0x07) | 0x10; + *reg = ((addr) >> 1) & 0x1f; + + if (sc->page != page) { + MDIO_WRITEREG(device_get_parent(dev), 0x18, 0, page); + sc->page = page; + } +} + +/* + * Read half a register. Some of the registers define control bits, and + * the sequence of half-word accesses matters. The register addresses + * are word-even (mod 4). + */ +static inline int +arswitch_readreg16(device_t dev, int addr) +{ + uint16_t phy, reg; + + arswitch_split_setpage(dev, addr, &phy, ®); + return (MDIO_READREG(device_get_parent(dev), phy, reg)); +} + +/* + * Write half a register + */ +static inline int +arswitch_writereg16(device_t dev, int addr, int data) +{ + uint16_t phy, reg; + + arswitch_split_setpage(dev, addr, &phy, ®); + return (MDIO_WRITEREG(device_get_parent(dev), phy, reg, data)); +} + +static inline int +arswitch_readreg_lsb(device_t dev, int addr) +{ + return (arswitch_readreg16(dev, addr)); +} + +static inline int +arswitch_readreg_msb(device_t dev, int addr) +{ + return (arswitch_readreg16(dev, addr + 2) << 16); +} + +static inline int +arswitch_writereg_lsb(device_t dev, int addr, int data) +{ + return (arswitch_writereg16(dev, addr, data & 0xffff)); +} + +static inline int +arswitch_writereg_msb(device_t dev, int addr, int data) +{ + return (arswitch_writereg16(dev, addr + 2, data >> 16)); +} + +static int +arswitch_readreg(device_t dev, int addr) +{ + return (arswitch_readreg_lsb(dev, addr) | arswitch_readreg_msb(dev, addr)); +} + +static int +arswitch_writereg(device_t dev, int addr, int value) +{ + arswitch_writereg_lsb(dev, addr, value); + return (arswitch_writereg_msb(dev, addr, value)); +} + +int +arswitch_modifyreg(device_t dev, int addr, int mask, int set) +{ + int value; + + value = arswitch_readreg(dev, addr); + value &= ~mask; + value |= set; + return arswitch_writereg(dev, addr, value); +} + +static int +arswitch_getport(device_t dev, etherswitch_port_t *p) +{ + struct arswitch_softc *sc = device_get_softc(dev); + struct mii_data *mii; + struct ifmediareq *ifmr = &p->es_ifmr; + int err; + + if (p->es_port < 0 || p->es_port >= AR8X16_NUM_PORTS) + return (ENXIO); + p->es_vlangroup = 0; + + mii = arswitch_miiforport(sc, p->es_port); + if (p->es_port == 0) { + /* fill in fixed values for CPU port */ + ifmr->ifm_count = 0; + ifmr->ifm_current = ifmr->ifm_active = + IFM_ETHER | IFM_1000_T | IFM_FDX; + ifmr->ifm_mask = 0; + ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; + } else if (mii != NULL) { + err = ifmedia_ioctl(mii->mii_ifp, &p->es_ifr, + &mii->mii_media, SIOCGIFMEDIA); + if (err) + return (err); + } else { + return (ENXIO); + } + return (0); +} + +static int +arswitch_setport(device_t dev, etherswitch_port_t *p) +{ +#if 0 + int err; + struct arswitch_softc *sc; + struct ifmedia *ifm; + struct mii_data *mii; + + if (p->es_port < 0 || p->es_port >= RTL8366RB_NUM_PHYS) + return (ENXIO); + err = smi_rmw(dev, RTL8366RB_PVCR_REG(p->es_port), + RTL8366RB_PVCR_VAL(p->es_port, RTL8366RB_PVCR_PORT_MASK), + RTL8366RB_PVCR_VAL(p->es_port, p->es_vlangroup), ARSWITCH_WAITOK); + if (err) + return (err); + sc = device_get_softc(dev); + mii = device_get_softc(sc->miibus[p->es_port]); + ifm = &mii->mii_media; + err = ifmedia_ioctl(sc->ifp[p->es_port], &p->es_ifr, ifm, SIOCSIFMEDIA); + return (err); +#else + return (0); +#endif +} + +static int +arswitch_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ +#if 0 + uint16_t vmcr[3]; + int i; + + for (i=0; i<3; i++) + vmcr[i] = arswitch_readreg(dev, RTL8366RB_VMCR(i, vg->es_vlangroup)); +#endif + + vg->es_vid = 0; + vg->es_member_ports = 0; + vg->es_untagged_ports = 0; + vg->es_fid = 0; + return (0); +} + +static int +arswitch_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ +#if 0 + int g = vg->es_vlangroup; + + arswitch_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_DOT1Q_REG, g), + (vg->es_vid << RTL8366RB_VMCR_DOT1Q_VID_SHIFT) & RTL8366RB_VMCR_DOT1Q_VID_MASK); + arswitch_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, g), + ((vg->es_member_ports << RTL8366RB_VMCR_MU_MEMBER_SHIFT) & RTL8366RB_VMCR_MU_MEMBER_MASK) | + ((vg->es_untagged_ports << RTL8366RB_VMCR_MU_UNTAG_SHIFT) & RTL8366RB_VMCR_MU_UNTAG_MASK)); + arswitch_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_FID_REG, g), + vg->es_fid); +#endif + return (0); +} + +/* + * access PHYs integrated into the switch chip through the switch's MDIO + * control register. + */ +static int +arswitch_readphy(device_t dev, int phy, int reg) +{ + uint32_t data = 0, ctrl; + int err, timeout; + + if (phy < 0 || phy >= 32) + return (ENXIO); + if (reg < 0 || reg >= 32) + return (ENXIO); + err = arswitch_writereg_msb(dev, AR8X16_REG_MDIO_CTRL, + AR8X16_MDIO_CTRL_BUSY | AR8X16_MDIO_CTRL_MASTER_EN | + AR8X16_MDIO_CTRL_CMD_READ | + (phy << AR8X16_MDIO_CTRL_PHY_ADDR_SHIFT) | + (reg << AR8X16_MDIO_CTRL_REG_ADDR_SHIFT)); + DEVERR(dev, err, "arswitch_readphy()=%d: phy=%d.%02x\n", phy, reg); + if (err != 0) + return (-1); + for (timeout = 100; timeout--; ) { + ctrl = arswitch_readreg_msb(dev, AR8X16_REG_MDIO_CTRL); + if ((ctrl & AR8X16_MDIO_CTRL_BUSY) == 0) + break; + } + if (timeout < 0) + err = EIO; + data = arswitch_readreg_lsb(dev, AR8X16_REG_MDIO_CTRL) & AR8X16_MDIO_CTRL_DATA_MASK; + return (data); +} + +static int +arswitch_writephy(device_t dev, int phy, int reg, int data) +{ + uint32_t ctrl; + int err, timeout; + + if (reg < 0 || reg >= 32) + return (ENXIO); + err = arswitch_writereg_lsb(dev, AR8X16_REG_MDIO_CTRL, + (data & AR8X16_MDIO_CTRL_DATA_MASK)); + DEVERR(dev, err, "arswitch_writephy()=%d: phy=%d.%02x\n", phy, reg); + if (err != 0) + return (err); + err = arswitch_writereg_msb(dev, AR8X16_REG_MDIO_CTRL, + AR8X16_MDIO_CTRL_BUSY | + AR8X16_MDIO_CTRL_MASTER_EN | + AR8X16_MDIO_CTRL_CMD_WRITE | + (phy << AR8X16_MDIO_CTRL_PHY_ADDR_SHIFT) | + (reg << AR8X16_MDIO_CTRL_REG_ADDR_SHIFT)); + DEVERR(dev, err, "arswitch_writephy()=%d: phy=%d.%02x\n", phy, reg); + if (err != 0) + return (err); + for (timeout = 100; timeout--; ) { + ctrl = arswitch_readreg(dev, AR8X16_REG_MDIO_CTRL); + if ((ctrl & AR8X16_MDIO_CTRL_BUSY) == 0) + break; + } + if (timeout < 0) + err = EIO; + DEVERR(dev, err, "arswitch_writephy()=%d: phy=%d.%02x\n", phy, reg); + return (err); +} + +static void +arswitch_statchg(device_t dev) +{ + device_printf(dev, "%s\n", __func__); +} + +static int +arswitch_ifmedia_upd(struct ifnet *ifp) +{ + struct arswitch_softc *sc = ifp->if_softc; + struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit); + + if (mii == NULL) + return (ENXIO); + mii_mediachg(mii); + return (0); +} + +static void +arswitch_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct arswitch_softc *sc = ifp->if_softc; + struct mii_data *mii = arswitch_miiforport(sc, ifp->if_dunit); + + device_printf(sc->dev, "%s\n", __func__); + if (mii == NULL) + return; + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + +static device_method_t arswitch_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, arswitch_identify), + DEVMETHOD(device_probe, arswitch_probe), + DEVMETHOD(device_attach, arswitch_attach), + DEVMETHOD(device_detach, arswitch_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MII interface */ + DEVMETHOD(miibus_readreg, arswitch_readphy), + DEVMETHOD(miibus_writereg, arswitch_writephy), + DEVMETHOD(miibus_statchg, arswitch_statchg), + + /* MDIO interface */ + DEVMETHOD(mdio_readreg, arswitch_readphy), + DEVMETHOD(mdio_writereg, arswitch_writephy), + + /* etherswitch interface */ + DEVMETHOD(etherswitch_getinfo, arswitch_getinfo), + DEVMETHOD(etherswitch_readreg, arswitch_readreg), + DEVMETHOD(etherswitch_writereg, arswitch_writereg), + DEVMETHOD(etherswitch_readphyreg, arswitch_readphy), + DEVMETHOD(etherswitch_writephyreg, arswitch_writephy), + DEVMETHOD(etherswitch_getport, arswitch_getport), + DEVMETHOD(etherswitch_setport, arswitch_setport), + DEVMETHOD(etherswitch_getvgroup, arswitch_getvgroup), + DEVMETHOD(etherswitch_setvgroup, arswitch_setvgroup), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(arswitch, arswitch_driver, arswitch_methods, + sizeof(struct arswitch_softc)); +static devclass_t arswitch_devclass; + +DRIVER_MODULE(arswitch, mdio, arswitch_driver, arswitch_devclass, 0, 0); +DRIVER_MODULE(miibus, arswitch, miibus_driver, miibus_devclass, 0, 0); +DRIVER_MODULE(mdio, arswitch, mdio_driver, mdio_devclass, 0, 0); +DRIVER_MODULE(etherswitch, arswitch, etherswitch_driver, etherswitch_devclass, 0, 0); +MODULE_VERSION(arswitch, 1); +MODULE_DEPEND(arswitch, miibus, 1, 1, 1); /* XXX which versions? */ +MODULE_DEPEND(arswitch, etherswitch, 1, 1, 1); /* XXX which versions? */ diff --git a/sys/dev/etherswitch/arswitchreg.h b/sys/dev/etherswitch/arswitchreg.h new file mode 100644 index 0000000..cd827cf --- /dev/null +++ b/sys/dev/etherswitch/arswitchreg.h @@ -0,0 +1,268 @@ +/*- + * Copyright (c) 2011 Aleksandr Rybalko. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef __AR8X16_SWITCHREG_H__ +#define __AR8X16_SWITCHREG_H__ + +#define AR8X16_REG_MASK_CTRL 0x0000 +#define AR8X16_MASK_CTRL_REV_MASK 0x000000ff +#define AR8X16_MASK_CTRL_VER_MASK 0x0000ff00 +#define AR8X16_MASK_CTRL_VER_SHIFT 8 +#define AR8X16_MASK_CTRL_SOFT_RESET (1 << 31) + +#define AR8X16_REG_MODE 0x0008 +/* DIR-615 E4 U-Boot */ +#define AR8X16_MODE_DIR_615_UBOOT 0x8d1003e0 +/* From Ubiquiti RSPRO */ +#define AR8X16_MODE_RGMII_PORT4_ISO 0x81461bea +#define AR8X16_MODE_RGMII_PORT4_SWITCH 0x01261be2 +/* AVM Fritz!Box 7390 */ +#define AR8X16_MODE_GMII 0x010e5b71 +/* from avm_cpmac/linux_ar_reg.h */ +#define AR8X16_MODE_RESERVED 0x000e1b20 +#define AR8X16_MODE_MAC0_GMII_EN (1u << 0) +#define AR8X16_MODE_MAC0_RGMII_EN (1u << 1) +#define AR8X16_MODE_PHY4_GMII_EN (1u << 2) +#define AR8X16_MODE_PHY4_RGMII_EN (1u << 3) +#define AR8X16_MODE_MAC0_MAC_MODE (1u << 4) +#define AR8X16_MODE_RGMII_RXCLK_DELAY_EN (1u << 6) +#define AR8X16_MODE_RGMII_TXCLK_DELAY_EN (1u << 7) +#define AR8X16_MODE_MAC5_MAC_MODE (1u << 14) +#define AR8X16_MODE_MAC5_PHY_MODE (1u << 15) +#define AR8X16_MODE_TXDELAY_S0 (1u << 21) +#define AR8X16_MODE_TXDELAY_S1 (1u << 22) +#define AR8X16_MODE_RXDELAY_S0 (1u << 23) +#define AR8X16_MODE_LED_OPEN_EN (1u << 24) +#define AR8X16_MODE_SPI_EN (1u << 25) +#define AR8X16_MODE_RXDELAY_S1 (1u << 26) +#define AR8X16_MODE_POWER_ON_SEL (1u << 31) + +#define AR8X16_REG_ISR 0x0010 +#define AR8X16_REG_IMR 0x0014 + +#define AR8X16_REG_SW_MAC_ADDR0 0x0020 +#define AR8X16_REG_SW_MAC_ADDR1 0x0024 + +#define AR8X16_REG_FLOOD_MASK 0x002c +#define AR8X16_FLOOD_MASK_BCAST_TO_CPU (1 << 26) + +#define AR8X16_REG_GLOBAL_CTRL 0x0030 +#define AR8X16_GLOBAL_CTRL_MTU_MASK 0x00000fff + +#define AR8X16_REG_VLAN_CTRL 0x0040 +#define AR8X16_VLAN_OP 0x00000007 +#define AR8X16_VLAN_OP_NOOP 0x0 +#define AR8X16_VLAN_OP_FLUSH 0x1 +#define AR8X16_VLAN_OP_LOAD 0x2 +#define AR8X16_VLAN_OP_PURGE 0x3 +#define AR8X16_VLAN_OP_REMOVE_PORT 0x4 +#define AR8X16_VLAN_OP_GET_NEXT 0x5 +#define AR8X16_VLAN_OP_GET 0x6 +#define AR8X16_VLAN_ACTIVE (1 << 3) +#define AR8X16_VLAN_FULL (1 << 4) +#define AR8X16_VLAN_PORT 0x00000f00 +#define AR8X16_VLAN_PORT_SHIFT 8 +#define AR8X16_VLAN_VID 0x0fff0000 +#define AR8X16_VLAN_VID_SHIFT 16 +#define AR8X16_VLAN_PRIO 0x70000000 +#define AR8X16_VLAN_PRIO_SHIFT 28 +#define AR8X16_VLAN_PRIO_EN (1 << 31) + +#define AR8X16_REG_VLAN_DATA 0x0044 +#define AR8X16_VLAN_MEMBER 0x000003ff +#define AR8X16_VLAN_VALID (1 << 11) + +#define AR8X16_REG_ARL_CTRL0 0x0050 +#define AR8X16_REG_ARL_CTRL1 0x0054 +#define AR8X16_REG_ARL_CTRL2 0x0058 + +#define AR8X16_REG_AT_CTRL 0x005c +#define AR8X16_AT_CTRL_ARP_EN (1 << 20) + +#define AR8X16_REG_IP_PRIORITY_1 0x0060 +#define AR8X16_REG_IP_PRIORITY_2 0x0064 +#define AR8X16_REG_IP_PRIORITY_3 0x0068 +#define AR8X16_REG_IP_PRIORITY_4 0x006C + +#define AR8X16_REG_TAG_PRIO 0x0070 + +#define AR8X16_REG_SERVICE_TAG 0x0074 +#define AR8X16_SERVICE_TAG_MASK 0x0000ffff + +#define AR8X16_REG_CPU_PORT 0x0078 +#define AR8X16_MIRROR_PORT_SHIFT 4 +#define AR8X16_CPU_PORT_EN (1 << 8) + +#define AR8X16_REG_MIB_FUNC0 0x0080 +#define AR8X16_MIB_TIMER_MASK 0x0000ffff +#define AR8X16_MIB_AT_HALF_EN (1 << 16) +#define AR8X16_MIB_BUSY (1 << 17) +#define AR8X16_MIB_FUNC_SHIFT 24 +#define AR8X16_MIB_FUNC_NO_OP 0x0 +#define AR8X16_MIB_FUNC_FLUSH 0x1 +#define AR8X16_MIB_FUNC_CAPTURE 0x3 +#define AR8X16_MIB_FUNC_XXX (1 << 30) /* 0x40000000 */ + +#define AR8X16_REG_MDIO_HIGH_ADDR 0x0094 + +#define AR8X16_REG_MDIO_CTRL 0x0098 +#define AR8X16_MDIO_CTRL_DATA_MASK 0x0000ffff +#define AR8X16_MDIO_CTRL_REG_ADDR_SHIFT 16 +#define AR8X16_MDIO_CTRL_PHY_ADDR_SHIFT 21 +#define AR8X16_MDIO_CTRL_CMD_WRITE 0 +#define AR8X16_MDIO_CTRL_CMD_READ (1 << 27) +#define AR8X16_MDIO_CTRL_MASTER_EN (1 << 30) +#define AR8X16_MDIO_CTRL_BUSY (1 << 31) + +#define AR8X16_REG_PORT_BASE(_p) (0x0100 + (_p) * 0x0100) + +#define AR8X16_REG_PORT_STS(_p) (AR8X16_REG_PORT_BASE((_p)) + 0x0000) +#define AR8X16_PORT_STS_SPEED_MASK 0x00000003 +#define AR8X16_PORT_STS_SPEED_10 0 +#define AR8X16_PORT_STS_SPEED_100 1 +#define AR8X16_PORT_STS_SPEED_1000 2 +#define AR8X16_PORT_STS_TXMAC (1 << 2) +#define AR8X16_PORT_STS_RXMAC (1 << 3) +#define AR8X16_PORT_STS_TXFLOW (1 << 4) +#define AR8X16_PORT_STS_RXFLOW (1 << 5) +#define AR8X16_PORT_STS_DUPLEX (1 << 6) +#define AR8X16_PORT_STS_LINK_UP (1 << 8) +#define AR8X16_PORT_STS_LINK_AUTO (1 << 9) +#define AR8X16_PORT_STS_LINK_PAUSE (1 << 10) + +#define AR8X16_REG_PORT_CTRL(_p) (AR8X16_REG_PORT_BASE((_p)) + 0x0004) +#define AR8X16_PORT_CTRL_STATE_MASK 0x00000007 +#define AR8X16_PORT_CTRL_STATE_DISABLED 0 +#define AR8X16_PORT_CTRL_STATE_BLOCK 1 +#define AR8X16_PORT_CTRL_STATE_LISTEN 2 +#define AR8X16_PORT_CTRL_STATE_LEARN 3 +#define AR8X16_PORT_CTRL_STATE_FORWARD 4 +#define AR8X16_PORT_CTRL_LEARN_LOCK (1 << 7) +#define AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_SHIFT 8 +#define AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_KEEP 0 +#define AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_STRIP 1 +#define AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_ADD 2 +#define AR8X16_PORT_CTRL_EGRESS_VLAN_MODE_DOUBLE_TAG 3 +#define AR8X16_PORT_CTRL_IGMP_SNOOP (1 << 10) +#define AR8X16_PORT_CTRL_HEADER (1 << 11) +#define AR8X16_PORT_CTRL_MAC_LOOP (1 << 12) +#define AR8X16_PORT_CTRL_SINGLE_VLAN (1 << 13) +#define AR8X16_PORT_CTRL_LEARN (1 << 14) +#define AR8X16_PORT_CTRL_DOUBLE_TAG (1 << 15) +#define AR8X16_PORT_CTRL_MIRROR_TX (1 << 16) +#define AR8X16_PORT_CTRL_MIRROR_RX (1 << 17) + +#define AR8X16_REG_PORT_VLAN(_p) (AR8X16_REG_PORT_BASE((_p)) + 0x0008) + +#define AR8X16_PORT_VLAN_DEFAULT_ID_SHIFT 0 +#define AR8X16_PORT_VLAN_DEST_PORTS_SHIFT 16 +#define AR8X16_PORT_VLAN_MODE_MASK 0xc0000000 +#define AR8X16_PORT_VLAN_MODE_SHIFT 30 +#define AR8X16_PORT_VLAN_MODE_PORT_ONLY 0 +#define AR8X16_PORT_VLAN_MODE_PORT_FALLBACK 1 +#define AR8X16_PORT_VLAN_MODE_VLAN_ONLY 2 +#define AR8X16_PORT_VLAN_MODE_SECURE 3 + +#define AR8X16_REG_PORT_RATE_LIM(_p) (AR8X16_REG_PORT_BASE((_p)) + 0x000c) +#define AR8X16_PORT_RATE_LIM_128KB 0 +#define AR8X16_PORT_RATE_LIM_256KB 1 +#define AR8X16_PORT_RATE_LIM_512KB 2 +#define AR8X16_PORT_RATE_LIM_1MB 3 +#define AR8X16_PORT_RATE_LIM_2MB 4 +#define AR8X16_PORT_RATE_LIM_4MB 5 +#define AR8X16_PORT_RATE_LIM_8MB 6 +#define AR8X16_PORT_RATE_LIM_16MB 7 +#define AR8X16_PORT_RATE_LIM_32MB 8 +#define AR8X16_PORT_RATE_LIM_64MB 9 +#define AR8X16_PORT_RATE_LIM_IN_EN (1 << 24) +#define AR8X16_PORT_RATE_LIM_OUT_EN (1 << 23) +#define AR8X16_PORT_RATE_LIM_IN_MASK 0x000f0000 +#define AR8X16_PORT_RATE_LIM_IN_SHIFT 16 +#define AR8X16_PORT_RATE_LIM_OUT_MASK 0x0000000f +#define AR8X16_PORT_RATE_LIM_OUT_SHIFT 0 + +#define AR8X16_REG_PORT_PRIORITY(_p) (AR8X16_REG_PORT_BASE((_p)) + 0x0010) + +#define AR8X16_REG_STATS_BASE(_p) (0x20000 + (_p) * 0x100) + +#define AR8X16_STATS_RXBROAD 0x0000 +#define AR8X16_STATS_RXPAUSE 0x0004 +#define AR8X16_STATS_RXMULTI 0x0008 +#define AR8X16_STATS_RXFCSERR 0x000c +#define AR8X16_STATS_RXALIGNERR 0x0010 +#define AR8X16_STATS_RXRUNT 0x0014 +#define AR8X16_STATS_RXFRAGMENT 0x0018 +#define AR8X16_STATS_RX64BYTE 0x001c +#define AR8X16_STATS_RX128BYTE 0x0020 +#define AR8X16_STATS_RX256BYTE 0x0024 +#define AR8X16_STATS_RX512BYTE 0x0028 +#define AR8X16_STATS_RX1024BYTE 0x002c +#define AR8X16_STATS_RX1518BYTE 0x0030 +#define AR8X16_STATS_RXMAXBYTE 0x0034 +#define AR8X16_STATS_RXTOOLONG 0x0038 +#define AR8X16_STATS_RXGOODBYTE 0x003c +#define AR8X16_STATS_RXBADBYTE 0x0044 +#define AR8X16_STATS_RXOVERFLOW 0x004c +#define AR8X16_STATS_FILTERED 0x0050 +#define AR8X16_STATS_TXBROAD 0x0054 +#define AR8X16_STATS_TXPAUSE 0x0058 +#define AR8X16_STATS_TXMULTI 0x005c +#define AR8X16_STATS_TXUNDERRUN 0x0060 +#define AR8X16_STATS_TX64BYTE 0x0064 +#define AR8X16_STATS_TX128BYTE 0x0068 +#define AR8X16_STATS_TX256BYTE 0x006c +#define AR8X16_STATS_TX512BYTE 0x0070 +#define AR8X16_STATS_TX1024BYTE 0x0074 +#define AR8X16_STATS_TX1518BYTE 0x0078 +#define AR8X16_STATS_TXMAXBYTE 0x007c +#define AR8X16_STATS_TXOVERSIZE 0x0080 +#define AR8X16_STATS_TXBYTE 0x0084 +#define AR8X16_STATS_TXCOLLISION 0x008c +#define AR8X16_STATS_TXABORTCOL 0x0090 +#define AR8X16_STATS_TXMULTICOL 0x0094 +#define AR8X16_STATS_TXSINGLECOL 0x0098 +#define AR8X16_STATS_TXEXCDEFER 0x009c +#define AR8X16_STATS_TXDEFER 0x00a0 +#define AR8X16_STATS_TXLATECOL 0x00a4 + +#define AR8X16_PORT_CPU 0 +#define AR8X16_NUM_PORTS 6 +#define AR8X16_NUM_PHYS 5 +#define AR8X16_MAGIC 0xc000050e + +#define AR8X16_PHY_ID1 0x004d +#define AR8X16_PHY_ID2 0xd041 + +#define AR8X16_PORT_MASK(_port) (1 << (_port)) +#define AR8X16_PORT_MASK_ALL ((1<<AR8X16_NUM_PORTS)-1) +#define AR8X16_PORT_MASK_BUT(_port) (AR8X16_PORT_MASK_ALL & ~(1 << (_port))) + +#define AR8X16_MAX_VLANS 16 + +#endif /* __AR8X16_SWITCHREG_H__ */ + diff --git a/sys/dev/etherswitch/arswitchvar.h b/sys/dev/etherswitch/arswitchvar.h new file mode 100644 index 0000000..9f8bcb0 --- /dev/null +++ b/sys/dev/etherswitch/arswitchvar.h @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2011 Aleksandr Rybalko. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _AR8X16_SWITCHVAR_H_ +#define _AR8X16_SWITCHVAR_H_ + +struct ar8x16_switch_softc { + device_t sc_dev; + device_t parent; + int ports; + int vlans; + struct switch_capability *caps; + int page_reg; + int page_phy; + int devid; + int revid; + uint32_t sc_mii_mode; + uint16_t *vlan_idx; +}; + +/* Ask switch bus to read/write phy registers */ +#define MII_READ(sc, phy, reg) \ + MIIBUS_READREG(sc->parent, phy, reg) +#define MII_WRITE(sc, phy, reg, val) \ + MIIBUS_WRITEREG(sc->parent, phy, reg, val) + +#define REG_REG(reg) (((reg) >> 1) & 0x01e) +#define REG_PHY(reg) (((reg) >> 6) & 0x007) +#define REG_PAGE(reg) (((reg) >> 9) & 0x1ff) +#define VLAN_IDX_VALID 0x8000 + +#define SET4(sc, reg, mask, val) \ + WRITE4(sc, reg, (READ4(sc, reg) & ~mask) | val) + +#define WAIT4(sc, reg, field, value, timeout_usec) \ + ({int result; \ + do { \ + uint32_t c, timeout = timeout_usec; \ + while (1) { \ + c = (READ4(sc, reg) & field); \ + if (c == (value)) { \ + result = 0; \ + break; \ + } else if (!timeout) { \ + result = -1; \ + break; \ + } else { \ + DELAY(1); timeout--; \ + } \ + } \ + } while (0); \ + result;}) + +#endif /* _AR8X16_SWITCHVAR_H_ */ diff --git a/sys/dev/etherswitch/etherswitch.c b/sys/dev/etherswitch/etherswitch.c new file mode 100644 index 0000000..a216289 --- /dev/null +++ b/sys/dev/etherswitch/etherswitch.c @@ -0,0 +1,257 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/conf.h> +#include <sys/fcntl.h> +#include <sys/lock.h> +#include <sys/kernel.h> +#include <sys/malloc.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sx.h> +#include <sys/systm.h> +#include <sys/uio.h> + +#include <net/if.h> + +#include <dev/etherswitch/etherswitch.h> + +#include "etherswitch_if.h" + +#define BUFSIZE 1024 + +struct etherswitch_softc { + device_t sc_dev; + int sc_count; + + struct cdev *sc_devnode; + struct sx sc_lock; +}; + +#define SWITCH_LOCK(sc) sx_xlock(&(sc)->sc_lock) +#define SWITCH_UNLOCK(sc) sx_xunlock(&(sc)->sc_lock) + +static int etherswitch_probe(device_t); +static int etherswitch_attach(device_t); +static int etherswitch_detach(device_t); +static void etherswitch_identify(driver_t *driver, device_t parent); + +devclass_t etherswitch_devclass; + +static device_method_t etherswitch_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, etherswitch_identify), + DEVMETHOD(device_probe, etherswitch_probe), + DEVMETHOD(device_attach, etherswitch_attach), + DEVMETHOD(device_detach, etherswitch_detach), + + { 0, 0 } +}; + +driver_t etherswitch_driver = { + "etherswitch", + etherswitch_methods, + sizeof(struct etherswitch_softc), +}; + +static d_open_t etherswitchopen; +static d_close_t etherswitchclose; +static d_write_t etherswitchwrite; +static d_read_t etherswitchread; +static d_ioctl_t etherswitchioctl; + +static struct cdevsw etherswitch_cdevsw = { + .d_version = D_VERSION, + .d_flags = D_TRACKCLOSE, + .d_open = etherswitchopen, + .d_close = etherswitchclose, + .d_read = etherswitchread, + .d_write = etherswitchwrite, + .d_ioctl = etherswitchioctl, + .d_name = "etherswitch", +}; + +static void +etherswitch_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, "etherswitch", -1) == NULL) + BUS_ADD_CHILD(parent, 0, "etherswitch", -1); +} + +static int +etherswitch_probe(device_t dev) +{ + device_set_desc(dev, "Switch controller"); + + return (0); +} + +static int +etherswitch_attach(device_t dev) +{ + struct etherswitch_softc *sc = (struct etherswitch_softc *)device_get_softc(dev); + + sc->sc_dev = dev; + sx_init(&sc->sc_lock, "etherswitch"); + sc->sc_devnode = make_dev(ðerswitch_cdevsw, device_get_unit(dev), + UID_ROOT, GID_WHEEL, + 0600, "etherswitch%d", device_get_unit(dev)); + if (sc->sc_devnode == NULL) { + device_printf(dev, "failed to create character device\n"); + sx_destroy(&sc->sc_lock); + return (ENXIO); + } + sc->sc_devnode->si_drv1 = sc; + + return (0); +} + +static int +etherswitch_detach(device_t dev) +{ + struct etherswitch_softc *sc = (struct etherswitch_softc *)device_get_softc(dev); + + if (sc->sc_devnode) + destroy_dev(sc->sc_devnode); + sx_destroy(&sc->sc_lock); + + return (0); +} + +static int +etherswitchopen(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + struct etherswitch_softc *sc = dev->si_drv1; + + SWITCH_LOCK(sc); + if (sc->sc_count > 0) { + SWITCH_UNLOCK(sc); + return (EBUSY); + } + + sc->sc_count++; + SWITCH_UNLOCK(sc); + + return (0); +} + +static int +etherswitchclose(struct cdev *dev, int flags, int fmt, struct thread *td) +{ + struct etherswitch_softc *sc = dev->si_drv1; + + SWITCH_LOCK(sc); + if (sc->sc_count == 0) { + SWITCH_UNLOCK(sc); + return (EINVAL); + } + + sc->sc_count--; + + if (sc->sc_count < 0) + panic("%s: etherswitch_count < 0!", __func__); + SWITCH_UNLOCK(sc); + + return (0); +} + +static int +etherswitchwrite(struct cdev *dev, struct uio * uio, int ioflag) +{ + return (EINVAL); +} + +static int +etherswitchread(struct cdev *dev, struct uio * uio, int ioflag) +{ + return (EINVAL); +} + +static int +etherswitchioctl(struct cdev *cdev, u_long cmd, caddr_t data, int flags, struct thread *td) +{ + struct etherswitch_softc *sc = cdev->si_drv1; + device_t dev = sc->sc_dev; + device_t etherswitch = device_get_parent(dev); + etherswitch_info_t *info; + etherswitch_reg_t *reg; + etherswitch_phyreg_t *phyreg; + int error = 0; + + switch (cmd) { + case IOETHERSWITCHGETINFO: + info = ETHERSWITCH_GETINFO(etherswitch); + bcopy(info, data, sizeof(etherswitch_info_t)); + break; + + case IOETHERSWITCHGETREG: + reg = (etherswitch_reg_t *)data; + reg->val = ETHERSWITCH_READREG(etherswitch, reg->reg); + break; + + case IOETHERSWITCHSETREG: + reg = (etherswitch_reg_t *)data; + error = ETHERSWITCH_WRITEREG(etherswitch, reg->reg, reg->val); + break; + + case IOETHERSWITCHGETPORT: + error = ETHERSWITCH_GETPORT(etherswitch, (etherswitch_port_t *)data); + break; + + case IOETHERSWITCHSETPORT: + error = ETHERSWITCH_SETPORT(etherswitch, (etherswitch_port_t *)data); + break; + + case IOETHERSWITCHGETVLANGROUP: + error = ETHERSWITCH_GETVGROUP(etherswitch, (etherswitch_vlangroup_t *)data); + break; + + case IOETHERSWITCHSETVLANGROUP: + error = ETHERSWITCH_SETVGROUP(etherswitch, (etherswitch_vlangroup_t *)data); + break; + + case IOETHERSWITCHGETPHYREG: + phyreg = (etherswitch_phyreg_t *)data; + phyreg->val = ETHERSWITCH_READPHYREG(etherswitch, phyreg->phy, phyreg->reg); + break; + + case IOETHERSWITCHSETPHYREG: + phyreg = (etherswitch_phyreg_t *)data; + error = ETHERSWITCH_WRITEPHYREG(etherswitch, phyreg->phy, phyreg->reg, phyreg->val); + break; + + default: + error = ENOTTY; + } + + return (error); +} + +MODULE_VERSION(etherswitch, 1); diff --git a/sys/dev/etherswitch/etherswitch.h b/sys/dev/etherswitch/etherswitch.h new file mode 100644 index 0000000..90ecb48 --- /dev/null +++ b/sys/dev/etherswitch/etherswitch.h @@ -0,0 +1,69 @@ +/* + * $FreeBSD$ + */ + +#ifndef __SYS_DEV_ETHERSWITCH_ETHERSWITCH_H +#define __SYS_DEV_ETHERSWITCH_ETHERSWITCH_H + +#include <sys/ioccom.h> + +#ifdef _KERNEL +extern devclass_t etherswitch_devclass; +extern driver_t etherswitch_driver; +#endif /* _KERNEL */ + +struct etherswitch_reg { + uint16_t reg; + uint16_t val; +}; +typedef struct etherswitch_reg etherswitch_reg_t; + +struct etherswitch_phyreg { + uint16_t phy; + uint16_t reg; + uint16_t val; +}; +typedef struct etherswitch_phyreg etherswitch_phyreg_t; + +#define ETHERSWITCH_NAMEMAX 64 + +struct etherswitch_info { + int es_nports; + int es_nvlangroups; + char es_name[ETHERSWITCH_NAMEMAX]; +}; +typedef struct etherswitch_info etherswitch_info_t; + +struct etherswitch_port { + int es_port; + int es_vlangroup; + union { + struct ifreq es_uifr; + struct ifmediareq es_uifmr; + } es_ifu; +#define es_ifr es_ifu.es_uifr +#define es_ifmr es_ifu.es_uifmr +}; +typedef struct etherswitch_port etherswitch_port_t; + +struct etherswitch_vlangroup { + int es_vlangroup; + int es_vid; + int es_member_ports; + int es_untagged_ports; + int es_fid; +}; +typedef struct etherswitch_vlangroup etherswitch_vlangroup_t; +#define ETHERSWITCH_PORTMASK(_port) (1 << (_port)) + +#define IOETHERSWITCHGETINFO _IOR('i', 1, etherswitch_info_t) +#define IOETHERSWITCHGETREG _IOWR('i', 2, etherswitch_reg_t) +#define IOETHERSWITCHSETREG _IOW('i', 3, etherswitch_reg_t) +#define IOETHERSWITCHGETPORT _IOWR('i', 4, etherswitch_port_t) +#define IOETHERSWITCHSETPORT _IOW('i', 5, etherswitch_port_t) +#define IOETHERSWITCHGETVLANGROUP _IOWR('i', 6, etherswitch_vlangroup_t) +#define IOETHERSWITCHSETVLANGROUP _IOW('i', 7, etherswitch_vlangroup_t) +#define IOETHERSWITCHGETPHYREG _IOWR('i', 8, etherswitch_phyreg_t) +#define IOETHERSWITCHSETPHYREG _IOW('i', 9, etherswitch_phyreg_t) + +#endif diff --git a/sys/dev/etherswitch/etherswitch_if.m b/sys/dev/etherswitch/etherswitch_if.m new file mode 100644 index 0000000..7429c04 --- /dev/null +++ b/sys/dev/etherswitch/etherswitch_if.m @@ -0,0 +1,86 @@ +# $FreeBSD$ + +#include <sys/bus.h> + +# Needed for ifreq/ifmediareq +#include <sys/socket.h> +#include <net/if.h> + +#include <dev/etherswitch/etherswitch.h> + +INTERFACE etherswitch; + +# +# Return device info +# +METHOD etherswitch_info_t* getinfo { + device_t dev; +} + +# +# Read switch register +# +METHOD int readreg { + device_t dev; + int reg; +}; + +# +# Write switch register +# +METHOD int writereg { + device_t dev; + int reg; + int value; +}; + +# +# Read PHY register +# +METHOD int readphyreg { + device_t dev; + int phy; + int reg; +}; + +# +# Write PHY register +# +METHOD int writephyreg { + device_t dev; + int phy; + int reg; + int value; +}; + +# +# Get port configuration +# +METHOD int getport { + device_t dev; + etherswitch_port_t *vg; +} + +# +# Set port configuration +# +METHOD int setport { + device_t dev; + etherswitch_port_t *vg; +} + +# +# Get VLAN group configuration +# +METHOD int getvgroup { + device_t dev; + etherswitch_vlangroup_t *vg; +} + +# +# Set VLAN group configuration +# +METHOD int setvgroup { + device_t dev; + etherswitch_vlangroup_t *vg; +} diff --git a/sys/dev/etherswitch/mdio.c b/sys/dev/etherswitch/mdio.c new file mode 100644 index 0000000..9302075 --- /dev/null +++ b/sys/dev/etherswitch/mdio.c @@ -0,0 +1,156 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/systm.h> + +#include <dev/etherswitch/mdio.h> + +#include "mdio_if.h" + +static void +mdio_identify(driver_t *driver, device_t parent) +{ + if (device_find_child(parent, mdio_driver.name, -1) == NULL) + BUS_ADD_CHILD(parent, 0, mdio_driver.name, -1); +} + +static int +mdio_probe(device_t dev) +{ + device_set_desc(dev, "MDIO"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +mdio_attach(device_t dev) +{ + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + return (bus_generic_attach(dev)); +} + +static int +mdio_detach(device_t dev) +{ + bus_generic_detach(dev); + return (0); +} + +static int +mdio_readreg(device_t dev, int phy, int reg) +{ + return MDIO_READREG(device_get_parent(dev), phy, reg); +} + +static int +mdio_writereg(device_t dev, int phy, int reg, int val) +{ + return MDIO_WRITEREG(device_get_parent(dev), phy, reg, val); +} + +static int +mdio_print_child(device_t dev, device_t child) +{ + int retval; + + retval = bus_print_child_header(dev, child); + retval += bus_print_child_footer(dev, child); + + return (retval); +} + +static int +mdio_read_ivar(device_t dev, device_t child __unused, int which, + uintptr_t *result) +{ + struct miibus_ivars *ivars; + + ivars = device_get_ivars(dev); + switch (which) { + default: + return (ENOENT); + } + return (0); +} + +static int +mdio_child_pnpinfo_str(device_t dev __unused, device_t child, char *buf, + size_t buflen) +{ + buf[0] = '\0'; + return (0); +} + +static int +mdio_child_location_str(device_t dev __unused, device_t child, char *buf, + size_t buflen) +{ + buf[0] = '\0'; + return (0); +} + +static void +mdio_hinted_child(device_t dev, const char *name, int unit) +{ + device_add_child(dev, name, unit); +} + +static device_method_t mdio_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, mdio_identify), + DEVMETHOD(device_probe, mdio_probe), + DEVMETHOD(device_attach, mdio_attach), + DEVMETHOD(device_detach, mdio_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* bus interface */ + DEVMETHOD(bus_print_child, mdio_print_child), + DEVMETHOD(bus_read_ivar, mdio_read_ivar), + DEVMETHOD(bus_child_pnpinfo_str, mdio_child_pnpinfo_str), + DEVMETHOD(bus_child_location_str, mdio_child_location_str), + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, mdio_hinted_child), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, mdio_readreg), + DEVMETHOD(mdio_writereg, mdio_writereg), + + DEVMETHOD_END +}; + +driver_t mdio_driver = { + "mdio", + mdio_methods, + 0 +}; + +devclass_t mdio_devclass; + diff --git a/sys/dev/etherswitch/mdio.h b/sys/dev/etherswitch/mdio.h new file mode 100644 index 0000000..52eddbd --- /dev/null +++ b/sys/dev/etherswitch/mdio.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_MII_MDIO_H_ +#define _DEV_MII_MDIO_H_ + +extern driver_t mdio_driver; +extern devclass_t mdio_devclass; + +#endif diff --git a/sys/dev/etherswitch/mdio_if.m b/sys/dev/etherswitch/mdio_if.m new file mode 100644 index 0000000..9aedd92 --- /dev/null +++ b/sys/dev/etherswitch/mdio_if.m @@ -0,0 +1,24 @@ +# $FreeBSD$ + +#include <sys/bus.h> + +INTERFACE mdio; + +# +# Read register from device on MDIO bus +# +METHOD int readreg { + device_t dev; + int phy; + int reg; +}; + +# +# Write register to device on MDIO bus +# +METHOD int writereg { + device_t dev; + int phy; + int reg; + int val; +}; diff --git a/sys/dev/etherswitch/miiproxy.c b/sys/dev/etherswitch/miiproxy.c new file mode 100644 index 0000000..2791082 --- /dev/null +++ b/sys/dev/etherswitch/miiproxy.c @@ -0,0 +1,437 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <dev/etherswitch/miiproxy.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include "mdio_if.h" +#include "miibus_if.h" + + +MALLOC_DECLARE(M_MIIPROXY); +MALLOC_DEFINE(M_MIIPROXY, "miiproxy", "miiproxy data structures"); + +driver_t miiproxy_driver; +driver_t mdioproxy_driver; + +struct miiproxy_softc { + device_t parent; + device_t proxy; + device_t mdio; + miiproxy_attach_callback_t attach_callback; + void *attach_arg; +}; + +struct mdioproxy_softc { +}; + +/* + * The rendevous data structures and functions allow two device endpoints to + * match up, so that the proxy endpoint can be associated with a target + * endpoint. The proxy has to know the device name of the target that it + * wants to associate with, for example through a hint. The rendevous code + * makes no assumptions about the devices that want to meet. + */ +struct rendevous_entry; + +enum rendevous_op { + RENDEVOUS_ATTACH, + RENDEVOUS_DETACH +}; + +typedef int (*rendevous_callback_t)(enum rendevous_op, + struct rendevous_entry *); + +static SLIST_HEAD(rendevoushead, rendevous_entry) rendevoushead = + SLIST_HEAD_INITIALIZER(rendevoushead); + +struct rendevous_endpoint { + device_t device; + const char *name; + rendevous_callback_t callback; +}; + +struct rendevous_entry { + SLIST_ENTRY(rendevous_entry) entries; + struct rendevous_endpoint proxy; + struct rendevous_endpoint target; +}; + +/* + * Call the callback routines for both the proxy and the target. If either + * returns an error, undo the attachment. + */ +static int +rendevous_attach(struct rendevous_entry *e, struct rendevous_endpoint *ep) +{ + int error; + + error = e->proxy.callback(RENDEVOUS_ATTACH, e); + if (error == 0) + error = e->target.callback(RENDEVOUS_ATTACH, e); + if (error != 0) { + e->proxy.callback(RENDEVOUS_DETACH, e); + ep->device = NULL; + ep->callback = NULL; + } + return (error); +} + +/* + * Create an entry for the proxy in the rendevous list. The name parameter + * indicates the name of the device that is the target endpoint for this + * rendevous. The callback will be invoked as soon as the target is + * registered: either immediately if the target registered itself earlier, + * or once the target registers. + */ +static int +rendevous_register_proxy(device_t dev, const char *name, + rendevous_callback_t callback) +{ + struct rendevous_entry *e; + + KASSERT(callback != NULL, ("callback must not be NULL")); + SLIST_FOREACH(e, &rendevoushead, entries) { + if (strcmp(name, e->target.name) == 0) { + /* the target is already attached */ + e->proxy.name = device_get_nameunit(dev); + e->proxy.device = dev; + e->proxy.callback = callback; + return (rendevous_attach(e, &e->proxy)); + } + } + e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); + e->proxy.name = device_get_nameunit(dev); + e->proxy.device = dev; + e->proxy.callback = callback; + e->target.name = name; + SLIST_INSERT_HEAD(&rendevoushead, e, entries); + return (0); +} + +/* + * Create an entry in the rendevous list for the target. The callback will + * be called once the proxy has registered. + */ +static int +rendevous_register_target(device_t dev, rendevous_callback_t callback) +{ + struct rendevous_entry *e; + const char *name; + + KASSERT(callback != NULL, ("callback must not be NULL")); + name = device_get_nameunit(dev); + SLIST_FOREACH(e, &rendevoushead, entries) { + if (strcmp(name, e->target.name) == 0) { + e->target.device = dev; + e->target.callback = callback; + return (rendevous_attach(e, &e->target)); + } + } + e = malloc(sizeof(*e), M_MIIPROXY, M_WAITOK | M_ZERO); + e->target.name = name; + e->target.device = dev; + e->target.callback = callback; + SLIST_INSERT_HEAD(&rendevoushead, e, entries); + return (0); +} + +/* + * Remove the registration for the proxy. + */ +static int +rendevous_unregister_proxy(device_t dev) +{ + struct rendevous_entry *e; + int error = 0; + + SLIST_FOREACH(e, &rendevoushead, entries) { + if (e->proxy.device == dev) { + if (e->target.device == NULL) { + SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); + free(e, M_MIIPROXY); + return (0); + } else { + e->proxy.callback(RENDEVOUS_DETACH, e); + e->target.callback(RENDEVOUS_DETACH, e); + } + e->proxy.device = NULL; + e->proxy.callback = NULL; + return (error); + } + } + return (ENOENT); +} + +/* + * Remove the registration for the target. + */ +static int +rendevous_unregister_target(device_t dev) +{ + struct rendevous_entry *e; + int error = 0; + + SLIST_FOREACH(e, &rendevoushead, entries) { + if (e->target.device == dev) { + if (e->proxy.device == NULL) { + SLIST_REMOVE(&rendevoushead, e, rendevous_entry, entries); + free(e, M_MIIPROXY); + return (0); + } else { + e->proxy.callback(RENDEVOUS_DETACH, e); + e->target.callback(RENDEVOUS_DETACH, e); + } + e->target.device = NULL; + e->target.callback = NULL; + return (error); + } + } + return (ENOENT); +} + +/* + * Functions of the proxy that is interposed between the ethernet interface + * driver and the miibus device. + */ + +static int +miiproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) +{ + struct miiproxy_softc *sc = device_get_softc(rendevous->proxy.device); + + switch (op) { + case RENDEVOUS_ATTACH: + sc->mdio = device_get_parent(rendevous->target.device); + (sc->attach_callback)(sc->attach_arg); + break; + case RENDEVOUS_DETACH: + sc->mdio = NULL; + /* detach miibus */ + } + return (0); +} + +static int +miiproxy_probe(device_t dev) +{ + device_set_desc(dev, "MII/MDIO proxy, MII side"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +miiproxy_attach(device_t dev) +{ + /* + * The ethernet interface needs to call mii_attach_proxy() to pass + * the relevant parameters for rendevous with the MDIO target. + */ + return (bus_generic_attach(dev)); +} + +static int +miiproxy_detach(device_t dev) +{ + rendevous_unregister_proxy(dev); + bus_generic_detach(dev); + return (0); +} + +static int +miiproxy_readreg(device_t dev, int phy, int reg) +{ + struct miiproxy_softc *sc = device_get_softc(dev); + + if (sc->mdio != NULL) + return (MDIO_READREG(sc->mdio, phy, reg)); + return (-1); +} + +static int +miiproxy_writereg(device_t dev, int phy, int reg, int val) +{ + struct miiproxy_softc *sc = device_get_softc(dev); + + if (sc->mdio != NULL) + return (MDIO_WRITEREG(sc->mdio, phy, reg, val)); + return (-1); +} + +static void +miiproxy_statchg(device_t dev) +{ + MIIBUS_STATCHG(device_get_parent(dev)); +} + +static void +miiproxy_linkchg(device_t dev) +{ + MIIBUS_LINKCHG(device_get_parent(dev)); +} + +static void +miiproxy_mediainit(device_t dev) +{ + MIIBUS_MEDIAINIT(device_get_parent(dev)); +} + +/* + * Functions for the MDIO target device driver. + */ +static int +mdioproxy_rendevous_callback(enum rendevous_op op, struct rendevous_entry *rendevous) +{ + return (0); +} + +static void +mdioproxy_identify(driver_t *driver, device_t parent) +{ + device_t child; + + if (device_find_child(parent, driver->name, -1) == NULL) { + child = BUS_ADD_CHILD(parent, 0, driver->name, -1); + } +} + +static int +mdioproxy_probe(device_t dev) +{ + device_set_desc(dev, "MII/MDIO proxy, MDIO side"); + + return (BUS_PROBE_SPECIFIC); +} + +static int +mdioproxy_attach(device_t dev) +{ + rendevous_register_target(dev, mdioproxy_rendevous_callback); + return (bus_generic_attach(dev)); +} + +static int +mdioproxy_detach(device_t dev) +{ + rendevous_unregister_target(dev); + bus_generic_detach(dev); + return (0); +} + +/* + * Attach this proxy in place of miibus. The callback is called once all + * parts are in place, so that it can attach the miibus to the proxy device, + * and finish interface initialization. + */ +device_t +mii_attach_proxy(device_t dev, miiproxy_attach_callback_t cb, void *aa) +{ + struct miiproxy_softc *sc; + int error; + const char *name; + device_t miiproxy; + + if (resource_string_value(device_get_name(dev), + device_get_unit(dev), "mdio", &name) != 0) { + if (bootverbose) + printf("mii_attach_proxy: not attaching, no mdio" + " device hint for %s\n", device_get_nameunit(dev)); + return (NULL); + } + + miiproxy = device_add_child(dev, miiproxy_driver.name, -1); + error = bus_generic_attach(dev); + if (error != 0) { + device_printf(dev, "can't attach miiproxy\n"); + return (NULL); + } + sc = device_get_softc(miiproxy); + sc->parent = dev; + sc->proxy = miiproxy; + sc->attach_callback = cb; + sc->attach_arg = aa; + rendevous_register_proxy(miiproxy, name, miiproxy_rendevous_callback); + return (miiproxy); +} + +static device_method_t miiproxy_methods[] = { + /* device interface */ + DEVMETHOD(device_probe, miiproxy_probe), + DEVMETHOD(device_attach, miiproxy_attach), + DEVMETHOD(device_detach, miiproxy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + /* MII interface */ + DEVMETHOD(miibus_readreg, miiproxy_readreg), + DEVMETHOD(miibus_writereg, miiproxy_writereg), + DEVMETHOD(miibus_statchg, miiproxy_statchg), + DEVMETHOD(miibus_linkchg, miiproxy_linkchg), + DEVMETHOD(miibus_mediainit, miiproxy_mediainit), + + DEVMETHOD_END +}; + +static device_method_t mdioproxy_methods[] = { + /* device interface */ + DEVMETHOD(device_identify, mdioproxy_identify), + DEVMETHOD(device_probe, mdioproxy_probe), + DEVMETHOD(device_attach, mdioproxy_attach), + DEVMETHOD(device_detach, mdioproxy_detach), + DEVMETHOD(device_shutdown, bus_generic_shutdown), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(miiproxy, miiproxy_driver, miiproxy_methods, + sizeof(struct miiproxy_softc)); +DEFINE_CLASS_0(mdioproxy, mdioproxy_driver, mdioproxy_methods, + sizeof(struct mdioproxy_softc)); + +devclass_t miiproxy_devclass; +static devclass_t mdioproxy_devclass; + +DRIVER_MODULE(mdioproxy, mdio, mdioproxy_driver, mdioproxy_devclass, 0, 0); +DRIVER_MODULE(miibus, miiproxy, miibus_driver, miibus_devclass, 0, 0); +MODULE_VERSION(miiproxy, 1); +MODULE_DEPEND(miiproxy, miibus, 1, 1, 1); diff --git a/sys/dev/etherswitch/miiproxy.h b/sys/dev/etherswitch/miiproxy.h new file mode 100644 index 0000000..5b8ee7c --- /dev/null +++ b/sys/dev/etherswitch/miiproxy.h @@ -0,0 +1,39 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_ETHERSWITCH_MIIPROXY_H_ +#define _DEV_ETHERSWITCH_MIIPROXY_H_ + +typedef void (*miiproxy_attach_callback_t)(void *); + +extern devclass_t miiproxy_devclass; +extern driver_t miiproxy_driver; + +device_t mii_attach_proxy(device_t dev, miiproxy_attach_callback_t cb, void *aa); + +#endif diff --git a/sys/dev/etherswitch/rtl8366rb.c b/sys/dev/etherswitch/rtl8366rb.c new file mode 100644 index 0000000..afc6eef --- /dev/null +++ b/sys/dev/etherswitch/rtl8366rb.c @@ -0,0 +1,755 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#include <sys/param.h> +#include <sys/bus.h> +#include <sys/errno.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/socket.h> +#include <sys/sockio.h> +#include <sys/sysctl.h> +#include <sys/systm.h> + +#include <net/if.h> +#include <net/if_arp.h> +#include <net/ethernet.h> +#include <net/if_dl.h> +#include <net/if_media.h> +#include <net/if_types.h> + +#include <machine/bus.h> +#include <dev/iicbus/iic.h> +#include <dev/iicbus/iiconf.h> +#include <dev/iicbus/iicbus.h> +#include <dev/mii/mii.h> +#include <dev/mii/miivar.h> + +#include <dev/etherswitch/etherswitch.h> +#include <dev/etherswitch/rtl8366rbvar.h> + +#include "iicbus_if.h" +#include "miibus_if.h" +#include "etherswitch_if.h" + + +struct rtl8366rb_softc { + struct mtx sc_mtx; /* serialize access to softc */ + int smi_acquired; /* serialize access to SMI/I2C bus */ + struct mtx callout_mtx; /* serialize callout */ + device_t dev; + char *ifname[RTL8366RB_NUM_PHYS]; + device_t miibus[RTL8366RB_NUM_PHYS]; + struct ifnet *ifp[RTL8366RB_NUM_PHYS]; + struct callout callout_tick; +}; + +static etherswitch_info_t etherswitch_info = { + .es_nports = 6, + .es_nvlangroups = 16, + .es_name = "Realtek RTL8366RB" +}; + +#define RTL_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) +#define RTL_UNLOCK(_sc) mtx_unlock(&(_sc)->sc_mtx) +#define RTL_LOCK_ASSERT(_sc, _what) mtx_assert(&(_s)c->sc_mtx, (_what)) +#define RTL_TRYLOCK(_sc) mtx_trylock(&(_sc)->sc_mtx) + +#define RTL_WAITOK 0 +#define RTL_NOWAIT 1 + +#define RTL_SMI_ACQUIRED 1 +#define RTL_SMI_ACQUIRED_ASSERT(_sc) \ + KASSERT((_sc)->smi_acquired == RTL_SMI_ACQUIRED, ("smi must be acquired @%s", __FUNCTION__)) + +#if defined(DEBUG) +#define DPRINTF(dev, args...) device_printf(dev, args) +#define DEVERR(dev, err, fmt, args...) do { \ + if (err != 0) device_printf(dev, fmt, err, args); \ + } while (0) +#define DEBUG_INCRVAR(var) do { \ + var++; \ + } while (0) + +static int callout_blocked = 0; +static int iic_select_retries = 0; +static int phy_access_retries = 0; +static SYSCTL_NODE(_debug, OID_AUTO, rtl8366rb, CTLFLAG_RD, 0, "rtl8366rb"); +SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, callout_blocked, CTLFLAG_RW, &callout_blocked, 0, + "number of times the callout couldn't acquire the bus"); +SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, iic_select_retries, CTLFLAG_RW, &iic_select_retries, 0, + "number of times the I2C bus selection had to be retried"); +SYSCTL_INT(_debug_rtl8366rb, OID_AUTO, phy_access_retries, CTLFLAG_RW, &phy_access_retries, 0, + "number of times PHY register access had to be retried"); +#else +#define DPRINTF(dev, args...) +#define DEVERR(dev, err, fmt, args...) +#define DEBUG_INCRVAR(var) +#endif + +static int smi_probe(device_t dev); +static int smi_read(device_t dev, uint16_t addr, uint16_t *data, int sleep); +static int smi_write(device_t dev, uint16_t addr, uint16_t data, int sleep); +static int smi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data, int sleep); +static void rtl8366rb_tick(void *arg); +static int rtl8366rb_ifmedia_upd(struct ifnet *); +static void rtl8366rb_ifmedia_sts(struct ifnet *, struct ifmediareq *); + +static void +rtl8366rb_identify(driver_t *driver, device_t parent) +{ + device_t child; + struct iicbus_ivar *devi; + + if (device_find_child(parent, "rtl8366rb", -1) == NULL) { + child = BUS_ADD_CHILD(parent, 0, "rtl8366rb", -1); + devi = IICBUS_IVAR(child); + devi->addr = RTL8366RB_IIC_ADDR; + } +} + +static int +rtl8366rb_probe(device_t dev) +{ + if (smi_probe(dev) != 0) + return (ENXIO); + device_set_desc(dev, "RTL8366RB Ethernet Switch Controller"); + return (BUS_PROBE_DEFAULT); +} + +static void +rtl8366rb_init(device_t dev) +{ + /* Initialisation for TL-WR1043ND */ + smi_rmw(dev, RTL8366RB_RCR, + RTL8366RB_RCR_HARD_RESET, + RTL8366RB_RCR_HARD_RESET, RTL_WAITOK); + DELAY(100000); + /* Enable 16 VLAN mode */ + smi_rmw(dev, RTL8366RB_SGCR, + RTL8366RB_SGCR_EN_VLAN | RTL8366RB_SGCR_EN_VLAN_4KTB, + RTL8366RB_SGCR_EN_VLAN, RTL_WAITOK); + /* remove port 0 form VLAN 0 */ + smi_rmw(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, 0), + (1 << 0), 0, RTL_WAITOK); + /* add port 0 untagged and port 5 tagged to VLAN 1 */ + smi_rmw(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, 1), + ((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_MEMBER_SHIFT) + | ((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_UNTAG_SHIFT), + ((1 << 5 | 1 << 0) << RTL8366RB_VMCR_MU_MEMBER_SHIFT + | ((1 << 0) << RTL8366RB_VMCR_MU_UNTAG_SHIFT)), + RTL_WAITOK); + /* set PVLAN 1 for port 0 */ + smi_rmw(dev, RTL8366RB_PVCR_REG(0), + RTL8366RB_PVCR_VAL(0, RTL8366RB_PVCR_PORT_MASK), + RTL8366RB_PVCR_VAL(0, 1), RTL_WAITOK); +} + +static int +rtl8366rb_attach(device_t dev) +{ + uint16_t rev = 0; + struct rtl8366rb_softc *sc; + char name[IFNAMSIZ]; + int err = 0; + int i; + + sc = device_get_softc(dev); + bzero(sc, sizeof(*sc)); + sc->dev = dev; + mtx_init(&sc->sc_mtx, "rtl8366rb", NULL, MTX_DEF); + sc->smi_acquired = 0; + mtx_init(&sc->callout_mtx, "rtl8366rbcallout", NULL, MTX_DEF); + + rtl8366rb_init(dev); + smi_read(dev, RTL8366RB_CVCR, &rev, RTL_WAITOK); + device_printf(dev, "rev. %d\n", rev & 0x000f); + + /* attach miibus and phys */ + /* PHYs need an interface, so we generate a dummy one */ + for (i = 0; i < RTL8366RB_NUM_PHYS; i++) { + sc->ifp[i] = if_alloc(IFT_ETHER); + sc->ifp[i]->if_softc = sc; + sc->ifp[i]->if_flags |= IFF_UP | IFF_BROADCAST | IFF_DRV_RUNNING + | IFF_SIMPLEX; + snprintf(name, IFNAMSIZ, "%sport", device_get_nameunit(dev)); + sc->ifname[i] = malloc(strlen(name)+1, M_DEVBUF, M_WAITOK); + bcopy(name, sc->ifname[i], strlen(name)+1); + if_initname(sc->ifp[i], sc->ifname[i], i); + err = mii_attach(dev, &sc->miibus[i], sc->ifp[i], rtl8366rb_ifmedia_upd, \ + rtl8366rb_ifmedia_sts, BMSR_DEFCAPMASK, \ + i, MII_OFFSET_ANY, 0); + if (err != 0) { + device_printf(dev, "attaching PHY %d failed\n", i); + return (err); + } + } + + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + err = bus_generic_attach(dev); + if (err != 0) + return (err); + + callout_init_mtx(&sc->callout_tick, &sc->callout_mtx, 0); + rtl8366rb_tick(sc); + + return (err); +} + +static int +rtl8366rb_detach(device_t dev) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + int i; + + for (i=0; i < RTL8366RB_NUM_PHYS; i++) { + if (sc->miibus[i]) + device_delete_child(dev, sc->miibus[i]); + if (sc->ifp[i] != NULL) + if_free(sc->ifp[i]); + free(sc->ifname[i], M_DEVBUF); + } + bus_generic_detach(dev); + callout_drain(&sc->callout_tick); + mtx_destroy(&sc->callout_mtx); + mtx_destroy(&sc->sc_mtx); + + return (0); +} + +static void +rtl8366rb_update_ifmedia(int portstatus, u_int *media_status, u_int *media_active) +{ + *media_active = IFM_ETHER; + *media_status = IFM_AVALID; + if ((portstatus & RTL8366RB_PLSR_LINK) != 0) + *media_status |= IFM_ACTIVE; + else { + *media_active |= IFM_NONE; + return; + } + switch (portstatus & RTL8366RB_PLSR_SPEED_MASK) { + case RTL8366RB_PLSR_SPEED_10: + *media_active |= IFM_10_T; + break; + case RTL8366RB_PLSR_SPEED_100: + *media_active |= IFM_100_TX; + break; + case RTL8366RB_PLSR_SPEED_1000: + *media_active |= IFM_1000_T; + break; + } + if ((portstatus & RTL8366RB_PLSR_FULLDUPLEX) == 0) + *media_active |= IFM_FDX; + else + *media_active |= IFM_HDX; + if ((portstatus & RTL8366RB_PLSR_TXPAUSE) != 0) + *media_active |= IFM_ETH_TXPAUSE; + if ((portstatus & RTL8366RB_PLSR_RXPAUSE) != 0) + *media_active |= IFM_ETH_RXPAUSE; +} + +static void +rtl833rb_miipollstat(struct rtl8366rb_softc *sc) +{ + int i; + struct mii_data *mii; + struct mii_softc *miisc; + uint16_t value; + int portstatus; + + for (i = 0; i < RTL8366RB_NUM_PHYS; i++) { + mii = device_get_softc(sc->miibus[i]); + if ((i % 2) == 0) { + if (smi_read(sc->dev, RTL8366RB_PLSR_BASE + i/2, &value, RTL_NOWAIT) != 0) { + DEBUG_INCRVAR(callout_blocked); + return; + } + portstatus = value & 0xff; + } else { + portstatus = (value >> 8) & 0xff; + } + rtl8366rb_update_ifmedia(portstatus, &mii->mii_media_status, &mii->mii_media_active); + LIST_FOREACH(miisc, &mii->mii_phys, mii_list) { + if (IFM_INST(mii->mii_media.ifm_cur->ifm_media) != miisc->mii_inst) + continue; + mii_phy_update(miisc, MII_POLLSTAT); + } + } +} + +static void +rtl8366rb_tick(void *arg) +{ + struct rtl8366rb_softc *sc = arg; + + rtl833rb_miipollstat(sc); + callout_reset(&sc->callout_tick, hz, rtl8366rb_tick, sc); +} + +static int +smi_probe(device_t dev) +{ + device_t iicbus, iicha; + int err, i; + uint16_t chipid; + char bytes[2]; + int xferd; + + bytes[0] = RTL8366RB_CIR & 0xff; + bytes[1] = (RTL8366RB_CIR >> 8) & 0xff; + iicbus = device_get_parent(dev); + iicha = device_get_parent(iicbus); + iicbus_reset(iicbus, IIC_FASTEST, RTL8366RB_IIC_ADDR, NULL); + for (i=3; i--; ) { + IICBUS_STOP(iicha); + /* + * we go directly to the host adapter because iicbus.c + * only issues a stop on a bus that was successfully started. + */ + } + err = iicbus_request_bus(iicbus, dev, IIC_WAIT); + if (err != 0) + goto out; + err = iicbus_start(iicbus, RTL8366RB_IIC_ADDR | RTL_IICBUS_READ, RTL_IICBUS_TIMEOUT); + if (err != 0) + goto out; + err = iicbus_write(iicbus, bytes, 2, &xferd, RTL_IICBUS_TIMEOUT); + if (err != 0) + goto out; + err = iicbus_read(iicbus, bytes, 2, &xferd, IIC_LAST_READ, 0); + if (err != 0) + goto out; + chipid = ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff); + DPRINTF(dev, "chip id 0x%04x\n", chipid); + if (chipid != RTL8366RB_CIR_ID8366RB) + err = ENXIO; +out: + iicbus_stop(iicbus); + iicbus_release_bus(iicbus, dev); + return (err == 0 ? 0 : ENXIO); +} + +static int +smi_acquire(struct rtl8366rb_softc *sc, int sleep) +{ + int r = 0; + if (sleep == RTL_WAITOK) + RTL_LOCK(sc); + else + if (RTL_TRYLOCK(sc) == 0) + return (EWOULDBLOCK); + if (sc->smi_acquired == RTL_SMI_ACQUIRED) + r = EBUSY; + else { + r = iicbus_request_bus(device_get_parent(sc->dev), sc->dev, \ + sleep == RTL_WAITOK ? IIC_WAIT : IIC_DONTWAIT); + if (r == 0) + sc->smi_acquired = RTL_SMI_ACQUIRED; + } + RTL_UNLOCK(sc); + return (r); +} + +static int +smi_release(struct rtl8366rb_softc *sc, int sleep) +{ + if (sleep == RTL_WAITOK) + RTL_LOCK(sc); + else + if (RTL_TRYLOCK(sc) == 0) + return (EWOULDBLOCK); + RTL_SMI_ACQUIRED_ASSERT(sc); + iicbus_release_bus(device_get_parent(sc->dev), sc->dev); + sc->smi_acquired = 0; + RTL_UNLOCK(sc); + return (0); +} + +static int +smi_select(device_t dev, int op, int sleep) +{ + int err, i; + device_t iicbus = device_get_parent(dev); + struct iicbus_ivar *devi = IICBUS_IVAR(dev); + int slave = devi->addr; + + RTL_SMI_ACQUIRED_ASSERT((struct rtl8366rb_softc *)device_get_softc(dev)); + /* + * The chip does not use clock stretching when it is busy, + * instead ignoring the command. Retry a few times. + */ + for (i = RTL_IICBUS_RETRIES; i--; ) { + err = iicbus_start(iicbus, slave | op, RTL_IICBUS_TIMEOUT); + if (err != IIC_ENOACK) + break; + if (sleep == RTL_WAITOK) { + DEBUG_INCRVAR(iic_select_retries); + pause("smi_select", RTL_IICBUS_RETRY_SLEEP); + } else + break; + } + return (err); +} + +static int +smi_read_locked(struct rtl8366rb_softc *sc, uint16_t addr, uint16_t *data, int sleep) +{ + int err; + device_t iicbus = device_get_parent(sc->dev); + char bytes[2]; + int xferd; + + RTL_SMI_ACQUIRED_ASSERT(sc); + bytes[0] = addr & 0xff; + bytes[1] = (addr >> 8) & 0xff; + err = smi_select(sc->dev, RTL_IICBUS_READ, sleep); + if (err != 0) + goto out; + err = iicbus_write(iicbus, bytes, 2, &xferd, RTL_IICBUS_TIMEOUT); + if (err != 0) + goto out; + err = iicbus_read(iicbus, bytes, 2, &xferd, IIC_LAST_READ, 0); + if (err != 0) + goto out; + *data = ((bytes[1] & 0xff) << 8) | (bytes[0] & 0xff); + +out: + iicbus_stop(iicbus); + return (err); +} + +static int +smi_write_locked(struct rtl8366rb_softc *sc, uint16_t addr, uint16_t data, int sleep) +{ + int err; + device_t iicbus = device_get_parent(sc->dev); + char bytes[4]; + int xferd; + + RTL_SMI_ACQUIRED_ASSERT(sc); + bytes[0] = addr & 0xff; + bytes[1] = (addr >> 8) & 0xff; + bytes[2] = data & 0xff; + bytes[3] = (data >> 8) & 0xff; + + err = smi_select(sc->dev, RTL_IICBUS_WRITE, sleep); + if (err == 0) + err = iicbus_write(iicbus, bytes, 4, &xferd, RTL_IICBUS_TIMEOUT); + iicbus_stop(iicbus); + + return (err); +} + +static int +smi_read(device_t dev, uint16_t addr, uint16_t *data, int sleep) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + int err; + + err = smi_acquire(sc, sleep); + if (err != 0) + return (EBUSY); + err = smi_read_locked(sc, addr, data, sleep); + smi_release(sc, sleep); + DEVERR(dev, err, "smi_read()=%d: addr=%04x\n", addr); + return (err == 0 ? 0 : EIO); +} + +static int +smi_write(device_t dev, uint16_t addr, uint16_t data, int sleep) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + int err; + + err = smi_acquire(sc, sleep); + if (err != 0) + return (EBUSY); + err = smi_write_locked(sc, addr, data, sleep); + smi_release(sc, sleep); + DEVERR(dev, err, "smi_write()=%d: addr=%04x\n", addr); + return (err == 0 ? 0 : EIO); +} + +static int +smi_rmw(device_t dev, uint16_t addr, uint16_t mask, uint16_t data, int sleep) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + int err; + uint16_t oldv, newv; + + err = smi_acquire(sc, sleep); + if (err != 0) + return (EBUSY); + if (err == 0) { + err = smi_read_locked(sc, addr, &oldv, sleep); + if (err == 0) { + newv = oldv & ~mask; + newv |= data & mask; + if (newv != oldv) + err = smi_write_locked(sc, addr, newv, sleep); + } + } + smi_release(sc, sleep); + DEVERR(dev, err, "smi_rmw()=%d: addr=%04x\n", addr); + return (err == 0 ? 0 : EIO); +} + +static etherswitch_info_t * +rtl_getinfo(device_t dev) +{ + return (ðerswitch_info); +} + +static int +rtl_readreg(device_t dev, int reg) +{ + uint16_t data = 0; + + smi_read(dev, reg, &data, RTL_WAITOK); + return (data); +} + +static int +rtl_writereg(device_t dev, int reg, int value) +{ + return (smi_write(dev, reg, value, RTL_WAITOK)); +} + +static int +rtl_getport(device_t dev, etherswitch_port_t *p) +{ + struct rtl8366rb_softc *sc; + struct ifmedia *ifm; + struct mii_data *mii; + struct ifmediareq *ifmr = &p->es_ifmr; + uint16_t v; + int err; + + if (p->es_port < 0 || p->es_port >= RTL8366RB_NUM_PORTS) + return (ENXIO); + p->es_vlangroup = RTL8366RB_PVCR_GET(p->es_port, + rtl_readreg(dev, RTL8366RB_PVCR_REG(p->es_port))); + + if (p->es_port < RTL8366RB_NUM_PHYS) { + sc = device_get_softc(dev); + mii = device_get_softc(sc->miibus[p->es_port]); + ifm = &mii->mii_media; + err = ifmedia_ioctl(sc->ifp[p->es_port], &p->es_ifr, ifm, SIOCGIFMEDIA); + if (err) + return (err); + } else { + /* fill in fixed values for CPU port */ + ifmr->ifm_count = 0; + smi_read(dev, RTL8366RB_PLSR_BASE + (RTL8366RB_NUM_PHYS)/2, &v, RTL_WAITOK); + v = v >> (8 * ((RTL8366RB_NUM_PHYS) % 2)); + rtl8366rb_update_ifmedia(v, &ifmr->ifm_status, &ifmr->ifm_active); + ifmr->ifm_current = ifmr->ifm_active; + ifmr->ifm_mask = 0; + ifmr->ifm_status = IFM_ACTIVE | IFM_AVALID; + } + return (0); +} + +static int +rtl_setport(device_t dev, etherswitch_port_t *p) +{ + int err; + struct rtl8366rb_softc *sc; + struct ifmedia *ifm; + struct mii_data *mii; + + if (p->es_port < 0 || p->es_port >= RTL8366RB_NUM_PHYS) + return (ENXIO); + err = smi_rmw(dev, RTL8366RB_PVCR_REG(p->es_port), + RTL8366RB_PVCR_VAL(p->es_port, RTL8366RB_PVCR_PORT_MASK), + RTL8366RB_PVCR_VAL(p->es_port, p->es_vlangroup), RTL_WAITOK); + if (err) + return (err); + sc = device_get_softc(dev); + mii = device_get_softc(sc->miibus[p->es_port]); + ifm = &mii->mii_media; + err = ifmedia_ioctl(sc->ifp[p->es_port], &p->es_ifr, ifm, SIOCSIFMEDIA); + return (err); +} + +static int +rtl_getvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ + uint16_t vmcr[3]; + int i; + + for (i=0; i<3; i++) + vmcr[i] = rtl_readreg(dev, RTL8366RB_VMCR(i, vg->es_vlangroup)); + + vg->es_vid = RTL8366RB_VMCR_VID(vmcr); + vg->es_member_ports = RTL8366RB_VMCR_MEMBER(vmcr); + vg->es_untagged_ports = RTL8366RB_VMCR_UNTAG(vmcr); + vg->es_fid = RTL8366RB_VMCR_FID(vmcr); + return (0); +} + +static int +rtl_setvgroup(device_t dev, etherswitch_vlangroup_t *vg) +{ + int g = vg->es_vlangroup; + + rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_DOT1Q_REG, g), + (vg->es_vid << RTL8366RB_VMCR_DOT1Q_VID_SHIFT) & RTL8366RB_VMCR_DOT1Q_VID_MASK); + rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_MU_REG, g), + ((vg->es_member_ports << RTL8366RB_VMCR_MU_MEMBER_SHIFT) & RTL8366RB_VMCR_MU_MEMBER_MASK) | + ((vg->es_untagged_ports << RTL8366RB_VMCR_MU_UNTAG_SHIFT) & RTL8366RB_VMCR_MU_UNTAG_MASK)); + rtl_writereg(dev, RTL8366RB_VMCR(RTL8366RB_VMCR_FID_REG, g), + vg->es_fid); + return (0); +} + +static int +rtl_readphy(device_t dev, int phy, int reg) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + uint16_t data = 0; + int err, i, sleep; + + if (phy < 0 || phy >= RTL8366RB_NUM_PHYS) + return (ENXIO); + if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG) + return (ENXIO); + sleep = RTL_WAITOK; + err = smi_acquire(sc, sleep); + if (err != 0) + return (EBUSY); + for (i = RTL_IICBUS_RETRIES; i--; ) { + err = smi_write_locked(sc, RTL8366RB_PACR, RTL8366RB_PACR_READ, sleep); + if (err == 0) + err = smi_write_locked(sc, RTL8366RB_PHYREG(phy, 0, reg), 0, sleep); + if (err == 0) { + err = smi_read_locked(sc, RTL8366RB_PADR, &data, sleep); + break; + } + DEBUG_INCRVAR(phy_access_retries); + DPRINTF(dev, "rtl_readphy(): chip not responsive, retrying %d more times\n", i); + pause("rtl_readphy", RTL_IICBUS_RETRY_SLEEP); + } + smi_release(sc, sleep); + DEVERR(dev, err, "rtl_readphy()=%d: phy=%d.%02x\n", phy, reg); + return (data); +} + +static int +rtl_writephy(device_t dev, int phy, int reg, int data) +{ + struct rtl8366rb_softc *sc = device_get_softc(dev); + int err, i, sleep; + + if (phy < 0 || phy >= RTL8366RB_NUM_PHYS) + return (ENXIO); + if (reg < 0 || reg >= RTL8366RB_NUM_PHY_REG) + return (ENXIO); + sleep = RTL_WAITOK; + err = smi_acquire(sc, sleep); + if (err != 0) + return (EBUSY); + for (i = RTL_IICBUS_RETRIES; i--; ) { + err = smi_write_locked(sc, RTL8366RB_PACR, RTL8366RB_PACR_WRITE, sleep); + if (err == 0) + err = smi_write_locked(sc, RTL8366RB_PHYREG(phy, 0, reg), data, sleep); + if (err == 0) { + break; + } + DEBUG_INCRVAR(phy_access_retries); + DPRINTF(dev, "rtl_writephy(): chip not responsive, retrying %d more tiems\n", i); + pause("rtl_writephy", RTL_IICBUS_RETRY_SLEEP); + } + smi_release(sc, sleep); + DEVERR(dev, err, "rtl_writephy()=%d: phy=%d.%02x\n", phy, reg); + return (err == 0 ? 0 : EIO); +} + +static int +rtl8366rb_ifmedia_upd(struct ifnet *ifp) +{ + struct rtl8366rb_softc *sc = ifp->if_softc; + struct mii_data *mii = device_get_softc(sc->miibus[ifp->if_dunit]); + + mii_mediachg(mii); + return (0); +} + +static void +rtl8366rb_ifmedia_sts(struct ifnet *ifp, struct ifmediareq *ifmr) +{ + struct rtl8366rb_softc *sc = ifp->if_softc; + struct mii_data *mii = device_get_softc(sc->miibus[ifp->if_dunit]); + + mii_pollstat(mii); + ifmr->ifm_active = mii->mii_media_active; + ifmr->ifm_status = mii->mii_media_status; +} + + +static device_method_t rtl8366rb_methods[] = { + /* Device interface */ + DEVMETHOD(device_identify, rtl8366rb_identify), + DEVMETHOD(device_probe, rtl8366rb_probe), + DEVMETHOD(device_attach, rtl8366rb_attach), + DEVMETHOD(device_detach, rtl8366rb_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MII interface */ + DEVMETHOD(miibus_readreg, rtl_readphy), + DEVMETHOD(miibus_writereg, rtl_writephy), + + /* etherswitch interface */ + DEVMETHOD(etherswitch_getinfo, rtl_getinfo), + DEVMETHOD(etherswitch_readreg, rtl_readreg), + DEVMETHOD(etherswitch_writereg, rtl_writereg), + DEVMETHOD(etherswitch_readphyreg, rtl_readphy), + DEVMETHOD(etherswitch_writephyreg, rtl_writephy), + DEVMETHOD(etherswitch_getport, rtl_getport), + DEVMETHOD(etherswitch_setport, rtl_setport), + DEVMETHOD(etherswitch_getvgroup, rtl_getvgroup), + DEVMETHOD(etherswitch_setvgroup, rtl_setvgroup), + + DEVMETHOD_END +}; + +DEFINE_CLASS_0(rtl8366rb, rtl8366rb_driver, rtl8366rb_methods, + sizeof(struct rtl8366rb_softc)); +static devclass_t rtl8366rb_devclass; + +DRIVER_MODULE(rtl8366rb, iicbus, rtl8366rb_driver, rtl8366rb_devclass, 0, 0); +DRIVER_MODULE(miibus, rtl8366rb, miibus_driver, miibus_devclass, 0, 0); +DRIVER_MODULE(etherswitch, rtl8366rb, etherswitch_driver, etherswitch_devclass, 0, 0); +MODULE_VERSION(rtl8366rb, 1); +MODULE_DEPEND(rtl8366rb, iicbus, 1, 1, 1); /* XXX which versions? */ +MODULE_DEPEND(rtl8366rb, miibus, 1, 1, 1); /* XXX which versions? */ +MODULE_DEPEND(rtl8366rb, etherswitch, 1, 1, 1); /* XXX which versions? */ diff --git a/sys/dev/etherswitch/rtl8366rbvar.h b/sys/dev/etherswitch/rtl8366rbvar.h new file mode 100644 index 0000000..4f3d1ba --- /dev/null +++ b/sys/dev/etherswitch/rtl8366rbvar.h @@ -0,0 +1,176 @@ +/*- + * Copyright (c) 2011-2012 Stefan Bethke. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +#ifndef _DEV_ETHERSWITCH_RTL8366RBVAR_H_ +#define _DEV_ETHERSWITCH_RTL8366RBVAR_H_ + +#define RTL8366RB_IIC_ADDR 0xa8 +#define RTL_IICBUS_TIMEOUT 100 /* us */ +#define RTL_IICBUS_READ 1 +#define RTL_IICBUS_WRITE 0 +/* number of times to try and select the chip on the I2C bus */ +#define RTL_IICBUS_RETRIES 3 +#define RTL_IICBUS_RETRY_SLEEP (hz/1000) + +/* Register definitions */ + +/* Switch Global Configuration */ +#define RTL8366RB_SGCR 0x0000 +#define RTL8366RB_SGCR_EN_BC_STORM_CTRL 0x0001 +#define RTL8366RB_SGCR_MAX_LENGTH_MASK 0x0030 +#define RTL8366RB_SGCR_MAX_LENGTH_1522 0x0000 +#define RTL8366RB_SGCR_MAX_LENGTH_1536 0x0010 +#define RTL8366RB_SGCR_MAX_LENGTH_1552 0x0020 +#define RTL8366RB_SGCR_MAX_LENGTH_9216 0x0030 +#define RTL8366RB_SGCR_EN_VLAN 0x2000 +#define RTL8366RB_SGCR_EN_VLAN_4KTB 0x4000 +#define RTL8366RB_SGCR_EN_QOS 0x8000 + +/* Port Enable Control: DISABLE_PORT[5:0] */ +#define RTL8366RB_PECR 0x0001 + +/* Switch Security Control 0: DIS_LEARN[5:0] */ +#define RTL8366RB_SSCR0 0x0002 + +/* Switch Security Control 1: DIS_AGE[5:0] */ +#define RTL8366RB_SSCR1 0x0003 + +/* Switch Security Control 2 */ +#define RTL8366RB_SSCR2 0x0004 +#define RTL8366RB_SSCR2_DROP_UNKNOWN_DA 0x0001 + +/* Port Link Status: two ports per register */ +#define RTL8366RB_PLSR_BASE 0x0014 +#define RTL8366RB_PLSR_SPEED_MASK 0x03 +#define RTL8366RB_PLSR_SPEED_10 0x00 +#define RTL8366RB_PLSR_SPEED_100 0x01 +#define RTL8366RB_PLSR_SPEED_1000 0x02 +#define RTL8366RB_PLSR_FULLDUPLEX 0x08 +#define RTL8366RB_PLSR_LINK 0x10 +#define RTL8366RB_PLSR_TXPAUSE 0x20 +#define RTL8366RB_PLSR_RXPAUSE 0x40 +#define RTL8366RB_PLSR_NO_AUTO 0x80 + +/* VLAN Member Configuration, 3 registers per VLAN */ +#define RTL8366RB_VMCR_BASE 0x0020 +#define RTL8366RB_VMCR_MULT 3 +#define RTL8366RB_VMCR_DOT1Q_REG 0 +#define RTL8366RB_VMCR_DOT1Q_VID_SHIFT 0 +#define RTL8366RB_VMCR_DOT1Q_VID_MASK 0x0fff +#define RTL8366RB_VMCR_DOT1Q_PCP_SHIFT 12 +#define RTL8366RB_VMCR_DOT1Q_PCP_MASK 0x7000 +#define RTL8366RB_VMCR_MU_REG 1 +#define RTL8366RB_VMCR_MU_MEMBER_SHIFT 0 +#define RTL8366RB_VMCR_MU_MEMBER_MASK 0x00ff +#define RTL8366RB_VMCR_MU_UNTAG_SHIFT 8 +#define RTL8366RB_VMCR_MU_UNTAG_MASK 0xff00 +#define RTL8366RB_VMCR_FID_REG 2 +#define RTL8366RB_VMCR_FID_FID_SHIFT 0 +#define RTL8366RB_VMCR_FID_FID_MASK 0x0007 +#define RTL8366RB_VMCR(_reg, _vlan) \ + (RTL8366RB_VMCR_BASE + _reg + _vlan * RTL8366RB_VMCR_MULT) +/* VLAN Identifier */ +#define RTL8366RB_VMCR_VID(_r) \ + (_r[RTL8366RB_VMCR_DOT1Q_REG] & RTL8366RB_VMCR_DOT1Q_VID_MASK) +/* Priority Code Point */ +#define RTL8366RB_VMCR_PCP(_r) \ + ((_r[RTL8366RB_VMCR_DOT1Q_REG] & RTL8366RB_VMCR_DOT1Q_PCP_MASK) \ + >> RTL8366RB_VMCR_DOT1Q_PCP_SHIFT) +/* Member ports */ +#define RTL8366RB_VMCR_MEMBER(_r) \ + (_r[RTL8366RB_VMCR_MU_REG] & RTL8366RB_VMCR_MU_MEMBER_MASK) +/* Untagged ports */ +#define RTL8366RB_VMCR_UNTAG(_r) \ + ((_r[RTL8366RB_VMCR_MU_REG] & RTL8366RB_VMCR_MU_UNTAG_MASK) \ + >> RTL8366RB_VMCR_MU_UNTAG_SHIFT) +/* Forwarding ID */ +#define RTL8366RB_VMCR_FID(_r) \ + (_r[RTL8366RB_VMCR_FID_REG] & RTL8366RB_VMCR_FID_FID_MASK) + +/* + * Port VLAN Control, 4 ports per register + * Determines the VID for untagged ingress frames through + * index into VMC. + */ +#define RTL8366RB_PVCR_BASE 0x0063 +#define RTL8366RB_PVCR_PORT_SHIFT 4 +#define RTL8366RB_PVCR_PORT_PERREG (16 / RTL8366RB_PVCR_PORT_SHIFT) +#define RTL8366RB_PVCR_PORT_MASK 0x000f +#define RTL8366RB_PVCR_REG(_port) \ + (RTL8366RB_PVCR_BASE + _port / (RTL8366RB_PVCR_PORT_PERREG)) +#define RTL8366RB_PVCR_VAL(_port, _pvlan) \ + ((_pvlan & RTL8366RB_PVCR_PORT_MASK) << \ + ((_port % RTL8366RB_PVCR_PORT_PERREG) * RTL8366RB_PVCR_PORT_SHIFT)) +#define RTL8366RB_PVCR_GET(_port, _val) \ + (((_val) >> ((_port % RTL8366RB_PVCR_PORT_PERREG) * RTL8366RB_PVCR_PORT_SHIFT)) & RTL8366RB_PVCR_PORT_MASK) + +/* Reset Control */ +#define RTL8366RB_RCR 0x0100 +#define RTL8366RB_RCR_HARD_RESET 0x0001 +#define RTL8366RB_RCR_SOFT_RESET 0x0002 + +/* Chip Version Control: CHIP_VER[3:0] */ +#define RTL8366RB_CVCR 0x050A +/* Chip Identifier */ +#define RTL8366RB_CIR 0x0509 +#define RTL8366RB_CIR_ID8366RB 0x5937 + +/* VLAN Ingress Control 2: [5:0] */ +#define RTL8366RB_VIC2R 0x037f + +/* MIB registers */ +#define RTL8366RB_MCNT_BASE 0x1000 +#define RTL8366RB_MCTLR 0x13f0 +#define RTL8366RB_MCTLR_BUSY 0x0001 +#define RTL8366RB_MCTLR_RESET 0x0002 +#define RTL8366RB_MCTLR_RESET_PORT_MASK 0x00fc +#define RTL8366RB_MCTLR_RESET_ALL 0x0800 + +#define RTL8366RB_MCNT(_port, _r) \ + (RTL8366RB_MCNT_BASE + 0x50 * (_port) + (_r)) +#define RTL8366RB_MCTLR_RESET_PORT(_p) \ + (1 << ((_p) + 2)) + +/* PHY Access Control */ +#define RTL8366RB_PACR 0x8000 +#define RTL8366RB_PACR_WRITE 0x0000 +#define RTL8366RB_PACR_READ 0x0001 + +/* PHY Access Data */ +#define RTL8366RB_PADR 0x8002 + +#define RTL8366RB_PHYREG(phy, page, reg) \ + (RTL8366RB_PACR | (1 << (((phy) & 0x1f) + 9)) | (((page) & 0xf) << 5) | ((reg) & 0x1f)) + +/* general characteristics of the chip */ +#define RTL8366RB_NUM_PORTS 6 +#define RTL8366RB_NUM_PHYS (RTL8366RB_NUM_PORTS-1) +#define RTL8366RB_NUM_VLANS 16 +#define RTL8366RB_NUM_PHY_REG 32 + +#endif diff --git a/sys/dev/flash/mx25l.c b/sys/dev/flash/mx25l.c index d5c2270..caaf963 100644 --- a/sys/dev/flash/mx25l.c +++ b/sys/dev/flash/mx25l.c @@ -101,7 +101,9 @@ struct mx25l_flash_ident flash_devices[] = { { "mx25ll64", 0xc2, 0x2017, 64 * 1024, 128, FL_NONE }, { "mx25ll128", 0xc2, 0x2018, 64 * 1024, 256, FL_ERASE_4K | FL_ERASE_32K }, { "s25fl128", 0x01, 0x2018, 64 * 1024, 256, FL_NONE }, + { "s25sl032", 0x01, 0x0215, 64 * 1024, 64, FL_NONE }, { "s25sl064a", 0x01, 0x0216, 64 * 1024, 128, FL_NONE }, + { "w25q64bv", 0xef, 0x4017, 64 * 1024, 128, FL_ERASE_4K }, }; static uint8_t diff --git a/sys/mips/atheros/apb.c b/sys/mips/atheros/apb.c index a2f6163..a682034 100644 --- a/sys/mips/atheros/apb.c +++ b/sys/mips/atheros/apb.c @@ -36,6 +36,8 @@ __FBSDID("$FreeBSD$"); #include <sys/module.h> #include <sys/rman.h> #include <sys/malloc.h> +#include <sys/pcpu.h> +#include <sys/proc.h> #include <machine/bus.h> #include <machine/intr_machdep.h> @@ -360,8 +362,7 @@ apb_intr(void *arg) continue; } - /* TODO: frame instead of NULL? */ - intr_event_handle(event, NULL); + intr_event_handle(event, curthread->td_intr_frame); mips_intrcnt_inc(sc->sc_intr_counter[irq]); } } diff --git a/sys/mips/atheros/ar71xx_gpio.c b/sys/mips/atheros/ar71xx_gpio.c index 592cd2b..9639d1f 100644 --- a/sys/mips/atheros/ar71xx_gpio.c +++ b/sys/mips/atheros/ar71xx_gpio.c @@ -329,7 +329,7 @@ ar71xx_gpio_attach(device_t dev) struct ar71xx_gpio_softc *sc = device_get_softc(dev); int error = 0; int i, j, maxpin; - int mask; + int mask, pinon; int old = 0; KASSERT((device_get_unit(dev) == 0), @@ -394,6 +394,9 @@ ar71xx_gpio_attach(device_t dev) if (resource_int_value(device_get_name(dev), device_get_unit(dev), "pinmask", &mask) != 0) mask = 0; + if (resource_int_value(device_get_name(dev), device_get_unit(dev), + "pinon", &pinon) != 0) + pinon = 0; device_printf(dev, "gpio pinmask=0x%x\n", mask); for (i = 0, j = 0; j < maxpin; j++) { if ((mask & (1 << j)) == 0) @@ -407,6 +410,11 @@ ar71xx_gpio_attach(device_t dev) i++; } sc->gpio_npins = i; + for (i = 0; i < sc->gpio_npins; i++) { + j = sc->gpio_pins[i].gp_pin; + if ((pinon & (1 << j)) != 0) + ar71xx_gpio_pin_set(dev, j, 1); + } device_add_child(dev, "gpioc", device_get_unit(dev)); device_add_child(dev, "gpiobus", device_get_unit(dev)); return (bus_generic_attach(dev)); diff --git a/sys/mips/atheros/ar71xx_pci.c b/sys/mips/atheros/ar71xx_pci.c index a6c7c1b..38d5adb 100644 --- a/sys/mips/atheros/ar71xx_pci.c +++ b/sys/mips/atheros/ar71xx_pci.c @@ -37,6 +37,8 @@ __FBSDID("$FreeBSD$"); #include <sys/kernel.h> #include <sys/module.h> #include <sys/rman.h> +#include <sys/pcpu.h> +#include <sys/proc.h> #include <vm/vm.h> #include <vm/pmap.h> @@ -503,8 +505,7 @@ ar71xx_pci_intr(void *arg) /* Flush DDR FIFO for IP2 */ ar71xx_device_ddr_flush_ip2(); - /* TODO: frame instead of NULL? */ - intr_event_handle(event, NULL); + intr_event_handle(event, curthread->td_intr_frame); mips_intrcnt_inc(sc->sc_intr_counter[irq]); } } diff --git a/sys/mips/atheros/ar71xx_pmc.c b/sys/mips/atheros/ar71xx_pmc.c new file mode 100644 index 0000000..fc6bb08 --- /dev/null +++ b/sys/mips/atheros/ar71xx_pmc.c @@ -0,0 +1,123 @@ +/*- + * Copyright (c) 2011, Adrian Chadd <adrian@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice unmodified, this list of conditions, and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * Tie into the PMC interrupt line and call the HWPMC hooks as needed. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/systm.h> +#include <sys/watchdog.h> +#include <sys/bus.h> +#include <sys/rman.h> +#include <sys/kernel.h> +#include <sys/module.h> +#include <sys/sysctl.h> +#include <sys/pcpu.h> +#include <sys/pmckern.h> + +#include <mips/atheros/ar71xxreg.h> +#include <mips/atheros/ar71xx_cpudef.h> + +struct ar71xx_pmc_softc { + device_t dev; + struct resource * pmc_irq; + void * pmc_intrhand; +}; + +static int +ar71xx_pmc_probe(device_t dev) +{ + + device_set_desc(dev, "Atheros AR71XX PMC interrupt device"); + return (0); +} + +static int +ar71xx_pmc_intr_filter(void *arg) +{ + struct trapframe *tf = curthread->td_intr_frame; + + printf("%s: called; tf=%p\n", __func__, tf); + if (pmc_intr && (*pmc_intr)(PCPU_GET(cpuid), tf) == 1) { + return (FILTER_HANDLED); + } + printf("%s: stray\n", __func__); + return (FILTER_STRAY); +} + +static int +ar71xx_pmc_attach(device_t dev) +{ + struct ar71xx_pmc_softc *sc = device_get_softc(dev); + int rid, error; + + sc->dev = dev; + rid = 0; + sc->pmc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, + RF_SHAREABLE | RF_ACTIVE); + if (sc->pmc_irq == NULL) { + device_printf(dev, "%s: couldn't allocate IRQ\n", __func__); + return ENXIO; + } + + /* XXX INTR_TYPE_NET? */ + error = bus_setup_intr(dev, sc->pmc_irq, INTR_TYPE_NET | INTR_MPSAFE, + ar71xx_pmc_intr_filter, NULL, sc, &sc->pmc_intrhand); + + return (0); +} + +static int +ar71xx_pmc_detach(device_t dev) +{ + struct ar71xx_pmc_softc *sc = device_get_softc(dev); + + if (sc->pmc_intrhand) + bus_teardown_intr(dev, sc->pmc_irq, sc->pmc_intrhand); + + return (0); +} + +static device_method_t ar71xx_pmc_methods[] = { + DEVMETHOD(device_probe, ar71xx_pmc_probe), + DEVMETHOD(device_attach, ar71xx_pmc_attach), + DEVMETHOD(device_detach, ar71xx_pmc_detach), + {0, 0}, +}; + +static driver_t ar71xx_pmc_driver = { + "ar71xx_pmc", + ar71xx_pmc_methods, + sizeof(struct ar71xx_pmc_softc), +}; +static devclass_t ar71xx_pmc_devclass; + +DRIVER_MODULE(ar71xx_pmc, apb, ar71xx_pmc_driver, ar71xx_pmc_devclass, 0, 0); diff --git a/sys/mips/atheros/ar71xxreg.h b/sys/mips/atheros/ar71xxreg.h index da5fc5d..c4d0bfd 100644 --- a/sys/mips/atheros/ar71xxreg.h +++ b/sys/mips/atheros/ar71xxreg.h @@ -272,10 +272,6 @@ */ #define AR71XX_MAC0_BASE 0x19000000 #define AR71XX_MAC1_BASE 0x1A000000 -/* - * All 5 PHYs accessible only through MAC0 register space - */ -#define AR71XX_MII_BASE 0x19000000 #define AR71XX_MAC_CFG1 0x00 #define MAC_CFG1_SOFT_RESET (1 << 31) diff --git a/sys/mips/atheros/ar724x_pci.c b/sys/mips/atheros/ar724x_pci.c index 59be9c0..7098e9e 100644 --- a/sys/mips/atheros/ar724x_pci.c +++ b/sys/mips/atheros/ar724x_pci.c @@ -542,8 +542,7 @@ ar724x_pci_intr(void *arg) return (FILTER_STRAY); } - /* TODO: frame instead of NULL? */ - intr_event_handle(event, NULL); + intr_event_handle(event, curthread->td_intr_frame); mips_intrcnt_inc(sc->sc_intr_counter[irq]); } diff --git a/sys/mips/atheros/files.ar71xx b/sys/mips/atheros/files.ar71xx index b0b7b02..941d354 100644 --- a/sys/mips/atheros/files.ar71xx +++ b/sys/mips/atheros/files.ar71xx @@ -21,3 +21,4 @@ mips/atheros/ar71xx_setup.c standard mips/atheros/ar71xx_chip.c standard mips/atheros/ar724x_chip.c standard mips/atheros/ar91xx_chip.c standard +mips/atheros/ar71xx_pmc.c optional ar71xx_pmc hwpmc diff --git a/sys/mips/atheros/if_arge.c b/sys/mips/atheros/if_arge.c index 0154ae2..3dd2e15 100644 --- a/sys/mips/atheros/if_arge.c +++ b/sys/mips/atheros/if_arge.c @@ -72,8 +72,18 @@ __FBSDID("$FreeBSD$"); #include <dev/pci/pcireg.h> #include <dev/pci/pcivar.h> +#include <opt_arge.h> + +#if defined(ARGE_MDIO) +#include <dev/etherswitch/mdio.h> +#include <dev/etherswitch/miiproxy.h> +#include "mdio_if.h" +#endif + + MODULE_DEPEND(arge, ether, 1, 1, 1); MODULE_DEPEND(arge, miibus, 1, 1, 1); +MODULE_VERSION(arge, 1); #include "miibus_if.h" @@ -102,6 +112,7 @@ typedef enum { #endif static int arge_attach(device_t); +static int arge_attach_finish(struct arge_softc *sc); static int arge_detach(device_t); static void arge_flush_ddr(struct arge_softc *); static int arge_ifmedia_upd(struct ifnet *); @@ -134,6 +145,8 @@ static void arge_intr(void *); static int arge_intr_filter(void *); static void arge_tick(void *); +static void arge_hinted_child(device_t bus, const char *dname, int dunit); + /* * ifmedia callbacks for multiPHY MAC */ @@ -160,6 +173,10 @@ static device_method_t arge_methods[] = { DEVMETHOD(miibus_writereg, arge_miibus_writereg), DEVMETHOD(miibus_statchg, arge_miibus_statchg), + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + DEVMETHOD(bus_hinted_child, arge_hinted_child), + DEVMETHOD_END }; @@ -174,6 +191,37 @@ static devclass_t arge_devclass; DRIVER_MODULE(arge, nexus, arge_driver, arge_devclass, 0, 0); DRIVER_MODULE(miibus, arge, miibus_driver, miibus_devclass, 0, 0); +#if defined(ARGE_MDIO) +static int argemdio_probe(device_t); +static int argemdio_attach(device_t); +static int argemdio_detach(device_t); + +/* + * Declare an additional, separate driver for accessing the MDIO bus. + */ +static device_method_t argemdio_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, argemdio_probe), + DEVMETHOD(device_attach, argemdio_attach), + DEVMETHOD(device_detach, argemdio_detach), + + /* bus interface */ + DEVMETHOD(bus_add_child, device_add_child_ordered), + + /* MDIO access */ + DEVMETHOD(mdio_readreg, arge_miibus_readreg), + DEVMETHOD(mdio_writereg, arge_miibus_writereg), +}; + +DEFINE_CLASS_0(argemdio, argemdio_driver, argemdio_methods, + sizeof(struct arge_softc)); +static devclass_t argemdio_devclass; + +DRIVER_MODULE(miiproxy, arge, miiproxy_driver, miiproxy_devclass, 0, 0); +DRIVER_MODULE(argemdio, nexus, argemdio_driver, argemdio_devclass, 0, 0); +DRIVER_MODULE(mdio, argemdio, mdio_driver, mdio_devclass, 0, 0); +#endif + /* * RedBoot passes MAC address to entry point as environment * variable. platfrom_start parses it and stores in this variable @@ -234,15 +282,22 @@ arge_attach_sysctl(device_t dev) #endif } +#if defined(ARGE_MDIO) +static void +arge_attach_proxy(void *aa) +{ + arge_attach_finish(aa); +} +#endif + static int arge_attach(device_t dev) { - uint8_t eaddr[ETHER_ADDR_LEN]; struct ifnet *ifp; struct arge_softc *sc; - int error = 0, rid, phymask; + int error = 0, rid; uint32_t reg, rnd; - int is_base_mac_empty, i, phys_total; + int is_base_mac_empty, i; uint32_t hint; long eeprom_mac_addr = 0; @@ -277,18 +332,18 @@ arge_attach(device_t dev) * Get which PHY of 5 available we should use for this unit */ if (resource_int_value(device_get_name(dev), device_get_unit(dev), - "phymask", &phymask) != 0) { + "phymask", &sc->arge_phymask) != 0) { /* * Use port 4 (WAN) for GE0. For any other port use * its PHY the same as its unit number */ if (sc->arge_mac_unit == 0) - phymask = (1 << 4); + sc->arge_phymask = (1 << 4); else /* Use all phys up to 4 */ - phymask = (1 << 4) - 1; + sc->arge_phymask = (1 << 4) - 1; - device_printf(dev, "No PHY specified, using mask %d\n", phymask); + device_printf(dev, "No PHY specified, using mask %d\n", sc->arge_phymask); } /* @@ -313,8 +368,6 @@ arge_attach(device_t dev) else sc->arge_duplex_mode = 0; - sc->arge_phymask = phymask; - mtx_init(&sc->arge_mtx, device_get_nameunit(dev), MTX_NETWORK_LOCK, MTX_DEF); callout_init_mtx(&sc->arge_stat_callout, &sc->arge_mtx, 0); @@ -323,7 +376,7 @@ arge_attach(device_t dev) /* Map control/status registers. */ sc->arge_rid = 0; sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, - &sc->arge_rid, RF_ACTIVE); + &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); if (sc->arge_res == NULL) { device_printf(dev, "couldn't map memory\n"); @@ -371,8 +424,8 @@ arge_attach(device_t dev) is_base_mac_empty = 1; for (i = 0; i < ETHER_ADDR_LEN; i++) { - eaddr[i] = ar711_base_mac[i] & 0xff; - if (eaddr[i] != 0) + sc->arge_eaddr[i] = ar711_base_mac[i] & 0xff; + if (sc->arge_eaddr[i] != 0) is_base_mac_empty = 0; } @@ -385,16 +438,15 @@ arge_attach(device_t dev) "Generating random ethernet address.\n"); rnd = arc4random(); - eaddr[0] = 'b'; - eaddr[1] = 's'; - eaddr[2] = 'd'; - eaddr[3] = (rnd >> 24) & 0xff; - eaddr[4] = (rnd >> 16) & 0xff; - eaddr[5] = (rnd >> 8) & 0xff; + sc->arge_eaddr[0] = 'b'; + sc->arge_eaddr[1] = 's'; + sc->arge_eaddr[2] = 'd'; + sc->arge_eaddr[3] = (rnd >> 24) & 0xff; + sc->arge_eaddr[4] = (rnd >> 16) & 0xff; + sc->arge_eaddr[5] = (rnd >> 8) & 0xff; } - if (sc->arge_mac_unit != 0) - eaddr[5] += sc->arge_mac_unit; + sc->arge_eaddr[5] += sc->arge_mac_unit; if (arge_dma_alloc(sc) != 0) { error = ENXIO; @@ -423,19 +475,23 @@ arge_attach(device_t dev) ARGE_WRITE(sc, AR71XX_MAC_MAX_FRAME_LEN, 1536); +#if !defined(ARGE_MDIO) /* Reset MII bus */ ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); DELAY(100); ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); DELAY(100); +#endif /* * Set all Ethernet address registers to the same initial values * set all four addresses to 66-88-aa-cc-dd-ee */ - ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, - (eaddr[2] << 24) | (eaddr[3] << 16) | (eaddr[4] << 8) | eaddr[5]); - ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (eaddr[0] << 8) | eaddr[1]); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR1, (sc->arge_eaddr[2] << 24) + | (sc->arge_eaddr[3] << 16) | (sc->arge_eaddr[4] << 8) + | sc->arge_eaddr[5]); + ARGE_WRITE(sc, AR71XX_MAC_STA_ADDR2, (sc->arge_eaddr[0] << 8) + | sc->arge_eaddr[1]); ARGE_WRITE(sc, AR71XX_MAC_FIFO_CFG0, FIFO_CFG0_ALL << FIFO_CFG0_ENABLE_SHIFT); @@ -458,30 +514,44 @@ arge_attach(device_t dev) ARGE_WRITE(sc, AR71XX_MAC_FIFO_RX_FILTMASK, FIFO_RX_FILTMASK_DEFAULT); - /* - * Check if we have single-PHY MAC or multi-PHY - */ - phys_total = 0; - for (i = 0; i < ARGE_NPHY; i++) - if (phymask & (1 << i)) - phys_total ++; +#if defined(ARGE_MDIO) + sc->arge_miiproxy = mii_attach_proxy(sc->arge_dev, arge_attach_proxy, sc); + if (sc->arge_miiproxy == NULL) + return (arge_attach_finish(sc)); +#else + return (arge_attach_finish(sc)); +#endif +fail: + if (error) + arge_detach(dev); - if (phys_total == 0) { - error = EINVAL; - goto fail; - } + return (error); +} - if (phys_total == 1) { - /* Do MII setup. */ - error = mii_attach(dev, &sc->arge_miibus, ifp, - arge_ifmedia_upd, arge_ifmedia_sts, BMSR_DEFCAPMASK, - MII_PHY_ANY, MII_OFFSET_ANY, 0); - if (error != 0) { - device_printf(dev, "attaching PHYs failed\n"); - goto fail; +static int +arge_attach_finish(struct arge_softc *sc) +{ + int error, phy; + + device_printf(sc->arge_dev, "finishing attachment, phymask %04x" + ", proxy %s \n", sc->arge_phymask, sc->arge_miiproxy == NULL ? + "null" : "set"); + for (phy = 0; phy < ARGE_NPHY; phy++) { + if (((1 << phy) & sc->arge_phymask) != 0) { + error = mii_attach(sc->arge_miiproxy != NULL ? + sc->arge_miiproxy : sc->arge_dev, + &sc->arge_miibus, sc->arge_ifp, + arge_ifmedia_upd, arge_ifmedia_sts, + BMSR_DEFCAPMASK, phy, MII_OFFSET_ANY, 0); + if (error != 0) { + device_printf(sc->arge_dev, "unable to attach" + " PHY %d: %d\n", phy, error); + goto fail; + } } } - else { + if (sc->arge_miibus == NULL) { + /* no PHY, so use hard-coded values */ ifmedia_init(&sc->arge_ifmedia, 0, arge_multiphy_mediachange, arge_multiphy_mediastatus); @@ -494,24 +564,23 @@ arge_attach(device_t dev) } /* Call MI attach routine. */ - ether_ifattach(ifp, eaddr); + ether_ifattach(sc->arge_ifp, sc->arge_eaddr); /* Hook interrupt last to avoid having to lock softc */ - error = bus_setup_intr(dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, + error = bus_setup_intr(sc->arge_dev, sc->arge_irq, INTR_TYPE_NET | INTR_MPSAFE, arge_intr_filter, arge_intr, sc, &sc->arge_intrhand); if (error) { - device_printf(dev, "couldn't set up irq\n"); - ether_ifdetach(ifp); + device_printf(sc->arge_dev, "couldn't set up irq\n"); + ether_ifdetach(sc->arge_ifp); goto fail; } /* setup sysctl variables */ - arge_attach_sysctl(dev); - + arge_attach_sysctl(sc->arge_dev); fail: if (error) - arge_detach(dev); + arge_detach(sc->arge_dev); return (error); } @@ -542,6 +611,9 @@ arge_detach(device_t dev) if (sc->arge_miibus) device_delete_child(dev, sc->arge_miibus); + if (sc->arge_miiproxy) + device_delete_child(dev, sc->arge_miiproxy); + bus_generic_detach(dev); if (sc->arge_intrhand) @@ -592,6 +664,13 @@ arge_shutdown(device_t dev) return (0); } +static void +arge_hinted_child(device_t bus, const char *dname, int dunit) +{ + BUS_ADD_CHILD(bus, 0, dname, dunit); + device_printf(bus, "hinted child %s%d\n", dname, dunit); +} + static int arge_miibus_readreg(device_t dev, int phy, int reg) { @@ -600,16 +679,13 @@ arge_miibus_readreg(device_t dev, int phy, int reg) uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - if ((sc->arge_phymask & (1 << phy)) == 0) - return (0); - mtx_lock(&miibus_mtx); - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); - ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_READ); i = ARGE_MII_TIMEOUT; - while ((ARGE_MII_READ(AR71XX_MAC_MII_INDICATOR) & + while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); @@ -620,8 +696,8 @@ arge_miibus_readreg(device_t dev, int phy, int reg) return (-1); } - result = ARGE_MII_READ(AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; - ARGE_MII_WRITE(AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); + result = ARGE_MDIO_READ(sc, AR71XX_MAC_MII_STATUS) & MAC_MII_STATUS_MASK; + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CMD, MAC_MII_CMD_WRITE); mtx_unlock(&miibus_mtx); ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value[%08x]=%04x\n", __func__, @@ -638,19 +714,15 @@ arge_miibus_writereg(device_t dev, int phy, int reg, int data) uint32_t addr = (phy << MAC_MII_PHY_ADDR_SHIFT) | (reg & MAC_MII_REG_MASK); - - if ((sc->arge_phymask & (1 << phy)) == 0) - return (-1); - ARGEDEBUG(sc, ARGE_DBG_MII, "%s: phy=%d, reg=%02x, value=%04x\n", __func__, phy, reg, data); mtx_lock(&miibus_mtx); - ARGE_MII_WRITE(AR71XX_MAC_MII_ADDR, addr); - ARGE_MII_WRITE(AR71XX_MAC_MII_CONTROL, data); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_ADDR, addr); + ARGE_MDIO_WRITE(sc, AR71XX_MAC_MII_CONTROL, data); i = ARGE_MII_TIMEOUT; - while ((ARGE_MII_READ(AR71XX_MAC_MII_INDICATOR) & + while ((ARGE_MDIO_READ(sc, AR71XX_MAC_MII_INDICATOR) & MAC_MII_INDICATOR_BUSY) && (i--)) DELAY(5); @@ -715,6 +787,8 @@ arge_set_pll(struct arge_softc *sc, int media, int duplex) uint32_t fifo_tx; int if_speed; + ARGEDEBUG(sc, ARGE_DBG_MII, "set_pll(%04x, %s)\n", media, + duplex == IFM_FDX ? "full" : "half"); cfg = ARGE_READ(sc, AR71XX_MAC_CFG2); cfg &= ~(MAC_CFG2_IFACE_MODE_1000 | MAC_CFG2_IFACE_MODE_10_100 @@ -1923,3 +1997,47 @@ arge_multiphy_mediastatus(struct ifnet *ifp, struct ifmediareq *ifmr) sc->arge_duplex_mode; } +#if defined(ARGE_MDIO) +static int +argemdio_probe(device_t dev) +{ + device_set_desc(dev, "Atheros AR71xx built-in ethernet interface, MDIO controller"); + return (0); +} + +static int +argemdio_attach(device_t dev) +{ + struct arge_softc *sc; + int error = 0; + + sc = device_get_softc(dev); + sc->arge_dev = dev; + sc->arge_mac_unit = device_get_unit(dev); + sc->arge_rid = 0; + sc->arge_res = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &sc->arge_rid, RF_ACTIVE | RF_SHAREABLE); + if (sc->arge_res == NULL) { + device_printf(dev, "couldn't map memory\n"); + error = ENXIO; + goto fail; + } + /* Reset MII bus */ + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_RESET); + DELAY(100); + ARGE_WRITE(sc, AR71XX_MAC_MII_CFG, MAC_MII_CFG_CLOCK_DIV_28); + DELAY(100); + bus_generic_probe(dev); + bus_enumerate_hinted_children(dev); + error = bus_generic_attach(dev); +fail: + return (error); +} + +static int +argemdio_detach(device_t dev) +{ + return (0); +} + +#endif diff --git a/sys/mips/atheros/if_argevar.h b/sys/mips/atheros/if_argevar.h index c1df678..dc3d931 100644 --- a/sys/mips/atheros/if_argevar.h +++ b/sys/mips/atheros/if_argevar.h @@ -67,15 +67,10 @@ #define ARGE_CLEAR_BITS(sc, reg, bits) \ ARGE_WRITE(sc, reg, ARGE_READ(sc, (reg)) & ~(bits)) -/* - * MII registers access macros - */ -#define ARGE_MII_READ(reg) \ - *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((AR71XX_MII_BASE + reg))) - -#define ARGE_MII_WRITE(reg, val) \ - *((volatile uint32_t *)MIPS_PHYS_TO_KSEG1((AR71XX_MII_BASE + reg))) = (val) - +#define ARGE_MDIO_WRITE(_sc, _reg, _val) \ + ARGE_WRITE((_sc), (_reg), (_val)) +#define ARGE_MDIO_READ(_sc, _reg) \ + ARGE_READ((_sc), (_reg)) #define ARGE_DESC_EMPTY (1 << 31) #define ARGE_DESC_MORE (1 << 24) @@ -132,11 +127,14 @@ struct arge_softc { */ uint32_t arge_media_type; uint32_t arge_duplex_mode; + uint32_t arge_phymask; + uint8_t arge_eaddr[ETHER_ADDR_LEN]; struct resource *arge_res; int arge_rid; struct resource *arge_irq; void *arge_intrhand; device_t arge_miibus; + device_t arge_miiproxy; bus_dma_tag_t arge_parent_tag; bus_dma_tag_t arge_tag; struct mtx arge_mtx; @@ -148,7 +146,6 @@ struct arge_softc { int arge_detach; uint32_t arge_intr_status; int arge_mac_unit; - int arge_phymask; int arge_if_flags; uint32_t arge_debug; struct { diff --git a/sys/mips/conf/AR91XX_BASE b/sys/mips/conf/AR91XX_BASE index a3686be..ba44bcf 100644 --- a/sys/mips/conf/AR91XX_BASE +++ b/sys/mips/conf/AR91XX_BASE @@ -16,6 +16,7 @@ cpu CPU_MIPS4KC options ISA_MIPS32 makeoptions KERNLOADADDR=0x80050000 options HZ=1000 +options HWPMC_HOOKS files "../atheros/files.ar71xx" hints "AR91XX_BASE.hints" @@ -111,3 +112,9 @@ device random device if_bridge device gpio device gpioled +device vlan + +device hwpmc + +# Tie into the interrupt line for PMC events +device ar71xx_pmc diff --git a/sys/mips/conf/AR91XX_BASE.hints b/sys/mips/conf/AR91XX_BASE.hints index 81442f3..b911d49 100644 --- a/sys/mips/conf/AR91XX_BASE.hints +++ b/sys/mips/conf/AR91XX_BASE.hints @@ -57,3 +57,6 @@ hint.gpio.0.at="apb0" hint.gpio.0.maddr=0x18040000 hint.gpio.0.msize=0x1000 hint.gpio.0.irq=2 + +hint.ar71xx_pmc.0.at="apb0" +hint.ar71xx_pmc.0.irq=5 diff --git a/sys/mips/conf/TL-MR3420 b/sys/mips/conf/TL-MR3420 new file mode 100644 index 0000000..6f89f31 --- /dev/null +++ b/sys/mips/conf/TL-MR3420 @@ -0,0 +1,45 @@ +# +# TL-WR3420 -- Kernel configuration file for the TP-Link MR3420 +# +# $FreeBSD$ +# + +# Include the default AR913x parameters common to all AR913x SoC users. +include "AR91XX_BASE" + +ident TL-MR3420 + +# Override hints with board values +hints "TL-MR3420.hints" + +# Force the board memory - 32mb +options AR71XX_REALMEM=32*1024*1024 + +nodevice ath +nodevice ath_rate_sample +nodevice ath_ahb +nodevice ath_ar5212 +nodevice ath_ar5416 +nodevice ath_ar9130 +nodevice ath_rf5111 + +# ethernet switch device +device etherswitch +device arswitch +device miiproxy +options ARGE_MDIO + +# Enable the uboot environment stuff rather then the +# redboot stuff. +options AR71XX_ENV_UBOOT + +# uzip - to boot natively from flash +device geom_uzip +options GEOM_UZIP + +# Used for the static uboot partition map +device geom_map + +# Boot off of the rootfs, as defined in the geom_map setup. +options ROOTDEVNAME=\"ufs:map/rootfs.uzip\" +options ROOTDEVNAME=\"ufs:da0s1a\" diff --git a/sys/mips/conf/TL-MR3420.hints b/sys/mips/conf/TL-MR3420.hints new file mode 100644 index 0000000..58adf3f --- /dev/null +++ b/sys/mips/conf/TL-MR3420.hints @@ -0,0 +1,95 @@ +# +# This file adds to the values in AR91XX_BASE.hints. +# +# $FreeBSD$ + +# AR7241 has built in switch and hardwired MAC connections, so just supply +# the appropriate values. +hint.arge.0.phymask=0x10 +hint.arge.0.eeprommac=0x1f01fc00 +hint.arge.0.mdio=mdioproxy1 + +hint.arge.1.phymask=0 +hint.arge.1.media=1000 +hint.arge.1.fduplex=1 + +# MDIO bus of arge0 is unused, MDIO bus of arge1 is used to talk to switch +hint.argemdio.0.at="nexus0" +hint.argemdio.0.maddr=0x1A000000 +hint.argemdio.0.msize=0x1000 + +# Where the ART is +#hint.ath.0.eepromaddr=0x1fff1000 + +# from uboot: +# mtdparts=ar7240-nor0:256k(u-boot),64k(u-boot-env),2752k(rootfs),896k(uImage),64k(NVRAM),64k(ART) + +hint.map.0.at="flash/spi0" +hint.map.0.start=0x00000000 +hint.map.0.end=0x00040000 +hint.map.0.name="uboot" +hint.map.0.readonly=1 + +hint.map.1.at="flash/spi0" +hint.map.1.start=0x00004000 +hint.map.1.end=0x00050000 +hint.map.1.name="ubootenv" +hint.map.1.readonly=1 + +hint.map.2.at="flash/spi0" +hint.map.2.start=0x00060000 +hint.map.2.end=0x00300000 +hint.map.2.name="rootfs" +hint.map.2.readonly=1 + +hint.map.3.at="flash/spi0" +hint.map.3.start=0x00300000 +hint.map.3.end=0x003e00000 +hint.map.3.name="uimage" +hint.map.3.readonly=0 + +hint.map.3.at="flash/spi0" +hint.map.3.start=0x003e0000 +hint.map.3.end=0x003f0000 +hint.map.3.name="nvram" +hint.map.3.readonly=0 + +hint.map.5.at="flash/spi0" +hint.map.5.start=0x003f0000 +hint.map.5.end=0x00800000 +hint.map.5.name="art" +hint.map.5.readonly=1 + +# GPIO specific configuration block + +# Don't flip on anything that isn't already enabled. +# This includes leaving the SPI CS1/CS2 pins as GPIO pins as they're +# not used here. +hint.gpio.0.function_set=0x00000000 +hint.gpio.0.function_clear=0x00000000 + +# These are the GPIO LEDs and buttons which can be software controlled. +hint.gpio.0.pinmask=0x1943 +# Switch on GPIO 6 to power up the USB port from the beginning +hint.gpio.0.pinon=0x0040 + +# Pin 0 - QSS (LED) +# Pin 1 - System (LED) +# Pin 6 - USB power (output) +# pin 8 - 3G (LED) +# Pin 11 - Reset (input) +# Pin 12 - QSS Button (input) + +# LEDs are configured separately and driven by the LED device +hint.gpioled.0.at="gpiobus0" +hint.gpioled.0.name="qss" +hint.gpioled.0.pins=0x0001 + +hint.gpioled.1.at="gpiobus0" +hint.gpioled.1.name="system" +hint.gpioled.1.pins=0x0002 + +hint.gpioled.2.at="gpiobus0" +hint.gpioled.2.name="3g" +hint.gpioled.2.pins=0x0008 + diff --git a/sys/mips/conf/TP-WN1043ND b/sys/mips/conf/TP-WN1043ND index d81a6f6..c05b870 100644 --- a/sys/mips/conf/TP-WN1043ND +++ b/sys/mips/conf/TP-WN1043ND @@ -15,6 +15,18 @@ hints "TP-WN1043ND.hints" # Force the board memory - 32mb options AR71XX_REALMEM=32*1024*1024 +# i2c GPIO bus +device gpioiic +device iicbb +device iicbus +device iic + +# ethernet switch device +device etherswitch + +# RTL8366RB support +device rtl8366rb + # read MSDOS formatted disks - USB options MSDOSFS options GEOM_PART_BSD diff --git a/sys/mips/conf/TP-WN1043ND.hints b/sys/mips/conf/TP-WN1043ND.hints index dce3fea..f72f3ce 100644 --- a/sys/mips/conf/TP-WN1043ND.hints +++ b/sys/mips/conf/TP-WN1043ND.hints @@ -81,7 +81,7 @@ hint.map.4.readonly=1 # Don't flip on anything that isn't already enabled. # This includes leaving the SPI CS1/CS2 pins as GPIO pins as they're # not used here. -hint.gpio.0.function_set=0x00000000 +hint.gpio.0.function_set=0x00002000 hint.gpio.0.function_clear=0x00000000 # These are the GPIO LEDs and buttons which can be software controlled. @@ -101,9 +101,9 @@ hint.gpio.0.pinmask=0x001c02ae # Pin 20 - "GPIO20" # LEDs are configured separately and driven by the LED device -hint.gpioled.0.at="gpiobus0" -hint.gpioled.0.name="usb" -hint.gpioled.0.pins=0x0002 +#hint.gpioled.0.at="gpiobus0" +#hint.gpioled.0.name="usb" +#hint.gpioled.0.pins=0x0002 hint.gpioled.1.at="gpiobus0" hint.gpioled.1.name="system" @@ -116,3 +116,17 @@ hint.gpioled.2.pins=0x0020 hint.gpioled.3.at="gpiobus0" hint.gpioled.3.name="wlan" hint.gpioled.3.pins=0x0200 + +# GPIO I2C bus +hint.gpioiic.0.at="gpiobus0" +hint.gpioiic.0.pins=0xc0000 +hint.gpioiic.0.scl=1 +hint.gpioiic.0.sda=0 + +# I2C bus +# Don't be strict about I2C protocol - the relaxed semantics are required +# by the realtek switch PHY. +hint.iicbus.0.strict=0 + +# Bit bang bus - override default delay +#hint.iicbb.0.udelay=3 diff --git a/sys/mips/conf/WRT160NL b/sys/mips/conf/WRT160NL new file mode 100644 index 0000000..083f128 --- /dev/null +++ b/sys/mips/conf/WRT160NL @@ -0,0 +1,49 @@ +# +# WRT160NL -- Kernel configuration file for the Cisco WRT160NL +# +# $FreeBSD$ +# + +# Include the default AR913x parameters common to all AR913x SoC users. +include "AR91XX_BASE" + +ident WRT160NL + +# Override hints with board values +hints "WRT160NL.hints" + +# Force the board memory - 32mb +options AR71XX_REALMEM=32*1024*1024 + +# read MSDOS formatted disks - USB +options MSDOSFS +options GEOM_PART_BSD +options GEOM_PART_MBR + +# Enable the uboot environment stuff rather then the +# redboot stuff. +options AR71XX_ENV_UBOOT + +# uzip - to boot natively from flash +device geom_uzip +options GEOM_UZIP + +# Used for the static uboot partition map +device geom_map + +# Boot off of the rootfs, as defined in the geom_map setup. +#options ROOTDEVNAME=\"ufs:map/rootfs.uzip\" +options ROOTDEVNAME=\"ufs:da0s1a\" + +# stb development stuff +options BREAK_TO_DEBUGGER +options ALT_BREAK_TO_DEBUGGER +#nooptions INVARIANTS +#nooptions INVARIANT_SUPPORT +#nooptions WITNESS +#nooptions WITNESS_SKIPSPIN +options BOOTP +options BOOTP_NFSROOT +options BOOTP_NFSV3 +options NFSCL +options NFS_ROOT diff --git a/sys/mips/conf/WRT160NL.hints b/sys/mips/conf/WRT160NL.hints new file mode 100644 index 0000000..38ecdf0 --- /dev/null +++ b/sys/mips/conf/WRT160NL.hints @@ -0,0 +1,132 @@ +# +# This file adds to the values in AR91XX_BASE.hints. +# +# $FreeBSD$ + +# Hard-code the PHY for now, until there's switch phy support. +hint.arge.0.phymask=0x0001 +hint.arge.0.media=1000 +hint.arge.0.fduplex=1 +# Where is the MAC address stored in flash for this particular unit. +hint.arge.0.eeprommac=0x1f01fc00 + +hint.arge.1.phymask=0x10 + +# Where the ART is +hint.ath.0.eepromaddr=0x1fff1000 + +# +# Define a slightly custom flash layout. + +# The default flash layout: +# +# 128k: uboot +# 1024k: kernel +# 4096k: rootfs +# 2816: unknown +# 64k: board config? +# 64k: ART +# +# from printenv: +# bootargs=console=ttyS0,115200 root=31:02 rootfstype=jffs2 init=/sbin/init +# mtdparts=ar9100-nor0:128k(u-boot),1024k(kernel),4096k(rootfs),64k(art) + +# This isn't a lot of space! +# So: +# 128k: uboot +# 2048k: kernel +# 5888k: rootfs +# 64k: config +# 64k: ART + +hint.map.0.at="flash/spi0" +hint.map.0.start=0x00000000 +hint.map.0.end=0x000040000 +hint.map.0.name="uboot" +hint.map.0.readonly=1 + +hint.map.1.at="flash/spi0" +hint.map.1.start=0x00040000 +hint.map.1.end=0x00220000 +hint.map.1.name="kernel" +hint.map.1.readonly=1 + +hint.map.2.at="flash/spi0" +hint.map.2.start=0x00220000 +hint.map.2.end=0x007e0000 +hint.map.2.name="rootfs" +hint.map.2.readonly=1 + +hint.map.3.at="flash/spi0" +hint.map.3.start=0x007e0000 +hint.map.3.end=0x007f0000 +hint.map.3.name="cfg" +hint.map.3.readonly=0 + +# This is radio calibration section. It is (or should be!) unique +# for each board, to take into account thermal and electrical differences +# as well as the regulatory compliance data. +# +hint.map.4.at="flash/spi0" +hint.map.4.start=0x007f0000 +hint.map.4.end=0x00800000 +hint.map.4.name="art" +hint.map.4.readonly=1 + +hint.map.5.at="flash/spi0" +hint.map.5.start=0x00000000 +hint.map.5.end=0x00800000 +hint.map.5.name="flash" +hint.map.5.readonly=1 + +# GPIO specific configuration block + +# Don't flip on anything that isn't already enabled. +# This includes leaving the SPI CS1/CS2 pins as GPIO pins as they're +# not used here. +hint.gpio.0.function_set=0x00000000 +hint.gpio.0.function_clear=0x00000000 + +# These are the GPIO LEDs and buttons which can be software controlled. +hint.gpio.0.pinmask=0x001c02ae + +# pin 1 - USB (LED) +# pin 2 - System (LED) +# Pin 3 - Reset (input) +# Pin 5 - QSS (LED) +# Pin 7 - QSS Button (input) +# Pin 8 - wired into the chip reset line +# Pin 9 - WLAN +# Pin 10 - UART TX (not GPIO) +# Pin 13 - UART RX (not GPIO) +# Pin 18 - RTL8366RB switch data line +# Pin 19 - RTL8366RB switch clock line +# Pin 20 - "GPIO20" + +# LEDs are configured separately and driven by the LED device +hint.gpioled.0.at="gpiobus0" +hint.gpioled.0.name="usb" +hint.gpioled.0.pins=0x0002 + +hint.gpioled.1.at="gpiobus0" +hint.gpioled.1.name="system" +hint.gpioled.1.pins=0x0004 + +hint.gpioled.2.at="gpiobus0" +hint.gpioled.2.name="qss" +hint.gpioled.2.pins=0x0020 + +hint.gpioled.3.at="gpiobus0" +hint.gpioled.3.name="wlan" +hint.gpioled.3.pins=0x0200 + +# GPIO I2C bus +hint.gpioiic.0.at="gpiobus0" +hint.gpioiic.0.pins=0xc0000 +hint.gpioiic.0.scl=1 +hint.gpioiic.0.sda=0 + +# I2C bus +# Don't be strict about I2C protocol - the relaxed semantics are required +# by the realtek switch PHY. +hint.iicbus.0.strict=0 diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 3a3f5e2..2f53854 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -92,6 +92,7 @@ SUBDIR= ${_3dfx} \ ${_epic} \ esp \ ${_et} \ + etherswitch \ ${_ex} \ ${_exca} \ ${_ext2fs} \ @@ -265,6 +266,7 @@ SUBDIR= ${_3dfx} \ re \ reiserfs \ rl \ + rtl8366rb \ runfw \ ${_s3} \ ${_safe} \ diff --git a/sys/modules/ath/Makefile b/sys/modules/ath/Makefile index 6e2e7b7..acf61b1 100644 --- a/sys/modules/ath/Makefile +++ b/sys/modules/ath/Makefile @@ -105,7 +105,7 @@ SRCS+= ar9160_attach.c # (currently) breaks non-AR9130 chipsets - since this is an embedded # chipset and no other radios are glued to it, this shouldn't pose a # problem. -SRCS+= ar9130_attach.c ar9130_eeprom.c ar9130_phy.c +# SRCS+= ar9130_attach.c ar9130_eeprom.c ar9130_phy.c # AR9002 series chips # + AR9220/AR9280 - Merlin @@ -140,8 +140,10 @@ SRCS+= dfs_null.c CFLAGS+= -I. -I${.CURDIR}/../../dev/ath -I${.CURDIR}/../../dev/ath/ath_hal +.if !defined(KERNBUILDDIR) opt_ah.h: echo '#define AH_SUPPORT_AR5416 1' > $@ +.endif .include <bsd.kmod.mk> diff --git a/sys/modules/etherswitch/Makefile b/sys/modules/etherswitch/Makefile new file mode 100644 index 0000000..b40a41e --- /dev/null +++ b/sys/modules/etherswitch/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/etherswitch +KMOD= etherswitch +SRCS= etherswitch.c device_if.h bus_if.h etherswitch_if.h +SRCS+= opt_cputype.h etherswitch_if.c + +.include <bsd.kmod.mk> diff --git a/sys/modules/rtl8366rb/Makefile b/sys/modules/rtl8366rb/Makefile new file mode 100644 index 0000000..40983ea --- /dev/null +++ b/sys/modules/rtl8366rb/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/etherswitch +KMOD= rtl8366rb +SRCS= rtl8366rb.c rtl8366rbvar.h +SRCS+= device_if.h bus_if.h iicbus_if.h miibus_if.h etherswitch_if.h opt_cputype.h opt_global.h + +.include <bsd.kmod.mk> --Apple-Mail=_CE96FAC5-9AF0-44EF-BFDB-F3380CF6C0C4--
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?76C5AEDF-39BA-4824-B8F8-BBD451200EF2>