xref: /openbmc/qemu/hw/net/rocker/rocker_fp.c (revision 19c63383a72408c9e0400db31bb4a96a913db78d)
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