qemu/hw/net/rocker/rocker_fp.c
<<
>>
Prefs
   1/*
   2 * QEMU rocker switch emulation - front-panel ports
   3 *
   4 * Copyright (c) 2014 Scott Feldman <sfeldma@gmail.com>
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include "qemu/osdep.h"
  18#include "qapi/qapi-types-rocker.h"
  19#include "rocker.h"
  20#include "rocker_hw.h"
  21#include "rocker_fp.h"
  22#include "rocker_world.h"
  23
  24enum duplex {
  25    DUPLEX_HALF = 0,
  26    DUPLEX_FULL
  27};
  28
  29struct fp_port {
  30    Rocker *r;
  31    World *world;
  32    unsigned int index;
  33    char *name;
  34    uint32_t pport;
  35    bool enabled;
  36    uint32_t speed;
  37    uint8_t duplex;
  38    uint8_t autoneg;
  39    uint8_t learning;
  40    NICState *nic;
  41    NICConf conf;
  42};
  43
  44char *fp_port_get_name(FpPort *port)
  45{
  46    return port->name;
  47}
  48
  49bool fp_port_get_link_up(FpPort *port)
  50{
  51    return !qemu_get_queue(port->nic)->link_down;
  52}
  53
  54RockerPort *fp_port_get_info(FpPort *port)
  55{
  56    RockerPort *value = g_malloc0(sizeof(*value));
  57
  58    value->name = g_strdup(port->name);
  59    value->enabled = port->enabled;
  60    value->link_up = fp_port_get_link_up(port);
  61    value->speed = port->speed;
  62    value->duplex = port->duplex;
  63    value->autoneg = port->autoneg;
  64    return value;
  65}
  66
  67void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr)
  68{
  69    memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
  70}
  71
  72void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr)
  73{
  74/* XXX TODO implement and test setting mac addr
  75 * XXX memcpy(port->conf.macaddr.a, macaddr.a, sizeof(port->conf.macaddr.a));
  76 */
  77}
  78
  79uint8_t fp_port_get_learning(FpPort *port)
  80{
  81    return port->learning;
  82}
  83
  84void fp_port_set_learning(FpPort *port, uint8_t learning)
  85{
  86    port->learning = learning;
  87}
  88
  89int fp_port_get_settings(FpPort *port, uint32_t *speed,
  90                         uint8_t *duplex, uint8_t *autoneg)
  91{
  92    *speed = port->speed;
  93    *duplex = port->duplex;
  94    *autoneg = port->autoneg;
  95
  96    return ROCKER_OK;
  97}
  98
  99int fp_port_set_settings(FpPort *port, uint32_t speed,
 100                         uint8_t duplex, uint8_t autoneg)
 101{
 102    /* XXX validate inputs */
 103
 104    port->speed = speed;
 105    port->duplex = duplex;
 106    port->autoneg = autoneg;
 107
 108    return ROCKER_OK;
 109}
 110
 111bool fp_port_from_pport(uint32_t pport, uint32_t *port)
 112{
 113    if (pport < 1 || pport > ROCKER_FP_PORTS_MAX) {
 114        return false;
 115    }
 116    *port = pport - 1;
 117    return true;
 118}
 119
 120int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt)
 121{
 122    NetClientState *nc = qemu_get_queue(port->nic);
 123
 124    if (port->enabled) {
 125        qemu_sendv_packet(nc, iov, iovcnt);
 126    }
 127
 128    return ROCKER_OK;
 129}
 130
 131static ssize_t fp_port_receive_iov(NetClientState *nc, const struct iovec *iov,
 132                                   int iovcnt)
 133{
 134    FpPort *port = qemu_get_nic_opaque(nc);
 135
 136    /* If the port is disabled, we want to drop this pkt
 137     * now rather than queing it for later.  We don't want
 138     * any stale pkts getting into the device when the port
 139     * transitions to enabled.
 140     */
 141
 142    if (!port->enabled) {
 143        return -1;
 144    }
 145
 146    return world_ingress(port->world, port->pport, iov, iovcnt);
 147}
 148
 149static ssize_t fp_port_receive(NetClientState *nc, const uint8_t *buf,
 150                               size_t size)
 151{
 152    const struct iovec iov = {
 153        .iov_base = (uint8_t *)buf,
 154        .iov_len = size
 155    };
 156
 157    return fp_port_receive_iov(nc, &iov, 1);
 158}
 159
 160static void fp_port_cleanup(NetClientState *nc)
 161{
 162}
 163
 164static void fp_port_set_link_status(NetClientState *nc)
 165{
 166    FpPort *port = qemu_get_nic_opaque(nc);
 167
 168    rocker_event_link_changed(port->r, port->pport, !nc->link_down);
 169}
 170
 171static NetClientInfo fp_port_info = {
 172    .type = NET_CLIENT_DRIVER_NIC,
 173    .size = sizeof(NICState),
 174    .receive = fp_port_receive,
 175    .receive_iov = fp_port_receive_iov,
 176    .cleanup = fp_port_cleanup,
 177    .link_status_changed = fp_port_set_link_status,
 178};
 179
 180World *fp_port_get_world(FpPort *port)
 181{
 182    return port->world;
 183}
 184
 185void fp_port_set_world(FpPort *port, World *world)
 186{
 187    DPRINTF("port %d setting world \"%s\"\n", port->index, world_name(world));
 188    port->world = world;
 189}
 190
 191bool fp_port_check_world(FpPort *port, World *world)
 192{
 193    return port->world == world;
 194}
 195
 196bool fp_port_enabled(FpPort *port)
 197{
 198    return port->enabled;
 199}
 200
 201static void fp_port_set_link(FpPort *port, bool up)
 202{
 203    NetClientState *nc = qemu_get_queue(port->nic);
 204
 205    if (up == nc->link_down) {
 206        nc->link_down = !up;
 207        nc->info->link_status_changed(nc);
 208    }
 209}
 210
 211void fp_port_enable(FpPort *port)
 212{
 213    fp_port_set_link(port, true);
 214    port->enabled = true;
 215    DPRINTF("port %d enabled\n", port->index);
 216}
 217
 218void fp_port_disable(FpPort *port)
 219{
 220    port->enabled = false;
 221    fp_port_set_link(port, false);
 222    DPRINTF("port %d disabled\n", port->index);
 223}
 224
 225FpPort *fp_port_alloc(Rocker *r, char *sw_name,
 226                      MACAddr *start_mac, unsigned int index,
 227                      NICPeers *peers)
 228{
 229    FpPort *port = g_new0(FpPort, 1);
 230
 231    port->r = r;
 232    port->index = index;
 233    port->pport = index + 1;
 234
 235    /* front-panel switch port names are 1-based */
 236
 237    port->name = g_strdup_printf("%sp%d", sw_name, port->pport);
 238
 239    memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a));
 240    port->conf.macaddr.a[5] += index;
 241    port->conf.bootindex = -1;
 242    port->conf.peers = *peers;
 243
 244    port->nic = qemu_new_nic(&fp_port_info, &port->conf,
 245                             sw_name, NULL, port);
 246    qemu_format_nic_info_str(qemu_get_queue(port->nic),
 247                             port->conf.macaddr.a);
 248
 249    fp_port_reset(port);
 250
 251    return port;
 252}
 253
 254void fp_port_free(FpPort *port)
 255{
 256    qemu_del_nic(port->nic);
 257    g_free(port->name);
 258    g_free(port);
 259}
 260
 261void fp_port_reset(FpPort *port)
 262{
 263    fp_port_disable(port);
 264    port->speed = 10000;   /* 10Gbps */
 265    port->duplex = DUPLEX_FULL;
 266    port->autoneg = 0;
 267}
 268