xref: /openbmc/qemu/hw/net/rocker/rocker_fp.c (revision 7d87775f)
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 
24 enum duplex {
25     DUPLEX_HALF = 0,
26     DUPLEX_FULL
27 };
28 
29 struct 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 
44 char *fp_port_get_name(FpPort *port)
45 {
46     return port->name;
47 }
48 
49 bool fp_port_get_link_up(FpPort *port)
50 {
51     return !qemu_get_queue(port->nic)->link_down;
52 }
53 
54 RockerPort *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 
67 void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr)
68 {
69     memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
70 }
71 
72 void 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 
79 uint8_t fp_port_get_learning(FpPort *port)
80 {
81     return port->learning;
82 }
83 
84 void fp_port_set_learning(FpPort *port, uint8_t learning)
85 {
86     port->learning = learning;
87 }
88 
89 int 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 
99 int 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 
111 bool 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 
120 int 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 
131 static 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 queueing 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 
149 static 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 
160 static void fp_port_cleanup(NetClientState *nc)
161 {
162 }
163 
164 static 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 
171 static 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 
180 World *fp_port_get_world(FpPort *port)
181 {
182     return port->world;
183 }
184 
185 void 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 
191 bool fp_port_check_world(FpPort *port, World *world)
192 {
193     return port->world == world;
194 }
195 
196 bool fp_port_enabled(FpPort *port)
197 {
198     return port->enabled;
199 }
200 
201 static 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 
211 void 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 
218 void 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 
225 FpPort *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, sw_name, NULL,
245                              &DEVICE(r)->mem_reentrancy_guard, 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 
254 void fp_port_free(FpPort *port)
255 {
256     qemu_del_nic(port->nic);
257     g_free(port->name);
258     g_free(port);
259 }
260 
261 void 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