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