diff options
Diffstat (limited to 'hw/net/rocker/rocker_fp.c')
-rw-r--r-- | hw/net/rocker/rocker_fp.c | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/hw/net/rocker/rocker_fp.c b/hw/net/rocker/rocker_fp.c new file mode 100644 index 000000000..cbeed65bd --- /dev/null +++ b/hw/net/rocker/rocker_fp.c @@ -0,0 +1,267 @@ +/* + * QEMU rocker switch emulation - front-panel ports + * + * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include "qemu/osdep.h" +#include "qapi/qapi-types-rocker.h" +#include "rocker.h" +#include "rocker_hw.h" +#include "rocker_fp.h" +#include "rocker_world.h" + +enum duplex { + DUPLEX_HALF = 0, + DUPLEX_FULL +}; + +struct fp_port { + Rocker *r; + World *world; + unsigned int index; + char *name; + uint32_t pport; + bool enabled; + uint32_t speed; + uint8_t duplex; + uint8_t autoneg; + uint8_t learning; + NICState *nic; + NICConf conf; +}; + +char *fp_port_get_name(FpPort *port) +{ + return port->name; +} + +bool fp_port_get_link_up(FpPort *port) +{ + return !qemu_get_queue(port->nic)->link_down; +} + +RockerPort *fp_port_get_info(FpPort *port) +{ + RockerPort *value = g_malloc0(sizeof(*value)); + + value->name = g_strdup(port->name); + value->enabled = port->enabled; + value->link_up = fp_port_get_link_up(port); + value->speed = port->speed; + value->duplex = port->duplex; + value->autoneg = port->autoneg; + return value; +} + +void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr) +{ + memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a)); +} + +void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr) +{ +/* XXX TODO implement and test setting mac addr + * XXX memcpy(port->conf.macaddr.a, macaddr.a, sizeof(port->conf.macaddr.a)); + */ +} + +uint8_t fp_port_get_learning(FpPort *port) +{ + return port->learning; +} + +void fp_port_set_learning(FpPort *port, uint8_t learning) +{ + port->learning = learning; +} + +int fp_port_get_settings(FpPort *port, uint32_t *speed, + uint8_t *duplex, uint8_t *autoneg) +{ + *speed = port->speed; + *duplex = port->duplex; + *autoneg = port->autoneg; + + return ROCKER_OK; +} + +int fp_port_set_settings(FpPort *port, uint32_t speed, + uint8_t duplex, uint8_t autoneg) +{ + /* XXX validate inputs */ + + port->speed = speed; + port->duplex = duplex; + port->autoneg = autoneg; + + return ROCKER_OK; +} + +bool fp_port_from_pport(uint32_t pport, uint32_t *port) +{ + if (pport < 1 || pport > ROCKER_FP_PORTS_MAX) { + return false; + } + *port = pport - 1; + return true; +} + +int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt) +{ + NetClientState *nc = qemu_get_queue(port->nic); + + if (port->enabled) { + qemu_sendv_packet(nc, iov, iovcnt); + } + + return ROCKER_OK; +} + +static ssize_t fp_port_receive_iov(NetClientState *nc, const struct iovec *iov, + int iovcnt) +{ + FpPort *port = qemu_get_nic_opaque(nc); + + /* If the port is disabled, we want to drop this pkt + * now rather than queing it for later. We don't want + * any stale pkts getting into the device when the port + * transitions to enabled. + */ + + if (!port->enabled) { + return -1; + } + + return world_ingress(port->world, port->pport, iov, iovcnt); +} + +static ssize_t fp_port_receive(NetClientState *nc, const uint8_t *buf, + size_t size) +{ + const struct iovec iov = { + .iov_base = (uint8_t *)buf, + .iov_len = size + }; + + return fp_port_receive_iov(nc, &iov, 1); +} + +static void fp_port_cleanup(NetClientState *nc) +{ +} + +static void fp_port_set_link_status(NetClientState *nc) +{ + FpPort *port = qemu_get_nic_opaque(nc); + + rocker_event_link_changed(port->r, port->pport, !nc->link_down); +} + +static NetClientInfo fp_port_info = { + .type = NET_CLIENT_DRIVER_NIC, + .size = sizeof(NICState), + .receive = fp_port_receive, + .receive_iov = fp_port_receive_iov, + .cleanup = fp_port_cleanup, + .link_status_changed = fp_port_set_link_status, +}; + +World *fp_port_get_world(FpPort *port) +{ + return port->world; +} + +void fp_port_set_world(FpPort *port, World *world) +{ + DPRINTF("port %d setting world \"%s\"\n", port->index, world_name(world)); + port->world = world; +} + +bool fp_port_check_world(FpPort *port, World *world) +{ + return port->world == world; +} + +bool fp_port_enabled(FpPort *port) +{ + return port->enabled; +} + +static void fp_port_set_link(FpPort *port, bool up) +{ + NetClientState *nc = qemu_get_queue(port->nic); + + if (up == nc->link_down) { + nc->link_down = !up; + nc->info->link_status_changed(nc); + } +} + +void fp_port_enable(FpPort *port) +{ + fp_port_set_link(port, true); + port->enabled = true; + DPRINTF("port %d enabled\n", port->index); +} + +void fp_port_disable(FpPort *port) +{ + port->enabled = false; + fp_port_set_link(port, false); + DPRINTF("port %d disabled\n", port->index); +} + +FpPort *fp_port_alloc(Rocker *r, char *sw_name, + MACAddr *start_mac, unsigned int index, + NICPeers *peers) +{ + FpPort *port = g_new0(FpPort, 1); + + port->r = r; + port->index = index; + port->pport = index + 1; + + /* front-panel switch port names are 1-based */ + + port->name = g_strdup_printf("%sp%d", sw_name, port->pport); + + memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a)); + port->conf.macaddr.a[5] += index; + port->conf.bootindex = -1; + port->conf.peers = *peers; + + port->nic = qemu_new_nic(&fp_port_info, &port->conf, + sw_name, NULL, port); + qemu_format_nic_info_str(qemu_get_queue(port->nic), + port->conf.macaddr.a); + + fp_port_reset(port); + + return port; +} + +void fp_port_free(FpPort *port) +{ + qemu_del_nic(port->nic); + g_free(port->name); + g_free(port); +} + +void fp_port_reset(FpPort *port) +{ + fp_port_disable(port); + port->speed = 10000; /* 10Gbps */ + port->duplex = DUPLEX_FULL; + port->autoneg = 0; +} |