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