xref: /openbmc/qemu/hw/net/rocker/rocker_fp.c (revision fee5b753)
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 
25 enum duplex {
26     DUPLEX_HALF = 0,
27     DUPLEX_FULL
28 };
29 
30 struct 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 
45 char *fp_port_get_name(FpPort *port)
46 {
47     return port->name;
48 }
49 
50 bool fp_port_get_link_up(FpPort *port)
51 {
52     return !qemu_get_queue(port->nic)->link_down;
53 }
54 
55 void 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 
65 void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr)
66 {
67     memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
68 }
69 
70 void 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 
77 uint8_t fp_port_get_learning(FpPort *port)
78 {
79     return port->learning;
80 }
81 
82 void fp_port_set_learning(FpPort *port, uint8_t learning)
83 {
84     port->learning = learning;
85 }
86 
87 int 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 
97 int 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 
109 bool 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 
118 int 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 
129 static 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 
147 static 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 
158 static void fp_port_cleanup(NetClientState *nc)
159 {
160 }
161 
162 static 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 
169 static NetClientInfo fp_port_info = {
170     .type = NET_CLIENT_OPTIONS_KIND_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 
178 World *fp_port_get_world(FpPort *port)
179 {
180     return port->world;
181 }
182 
183 void 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 
189 bool fp_port_enabled(FpPort *port)
190 {
191     return port->enabled;
192 }
193 
194 static void fp_port_set_link(FpPort *port, bool up)
195 {
196     NetClientState *nc = qemu_get_queue(port->nic);
197 
198     if (up == nc->link_down) {
199         nc->link_down = !up;
200         nc->info->link_status_changed(nc);
201     }
202 }
203 
204 void fp_port_enable(FpPort *port)
205 {
206     fp_port_set_link(port, true);
207     port->enabled = true;
208     DPRINTF("port %d enabled\n", port->index);
209 }
210 
211 void fp_port_disable(FpPort *port)
212 {
213     port->enabled = false;
214     fp_port_set_link(port, false);
215     DPRINTF("port %d disabled\n", port->index);
216 }
217 
218 FpPort *fp_port_alloc(Rocker *r, char *sw_name,
219                       MACAddr *start_mac, unsigned int index,
220                       NICPeers *peers)
221 {
222     FpPort *port = g_new0(FpPort, 1);
223 
224     if (!port) {
225         return NULL;
226     }
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 
251 void fp_port_free(FpPort *port)
252 {
253     qemu_del_nic(port->nic);
254     g_free(port->name);
255     g_free(port);
256 }
257 
258 void 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