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