xref: /openbmc/qemu/hw/net/rocker/rocker_fp.c (revision d7646f24)
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 "net/clients.h"
18 
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 void 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 
64 void fp_port_get_macaddr(FpPort *port, MACAddr *macaddr)
65 {
66     memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
67 }
68 
69 void 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 
76 uint8_t fp_port_get_learning(FpPort *port)
77 {
78     return port->learning;
79 }
80 
81 void fp_port_set_learning(FpPort *port, uint8_t learning)
82 {
83     port->learning = learning;
84 }
85 
86 int 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 
96 int 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 
108 bool 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 
117 int 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 
128 static 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 
146 static 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 
157 static void fp_port_cleanup(NetClientState *nc)
158 {
159 }
160 
161 static 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 
168 static NetClientInfo fp_port_info = {
169     .type = NET_CLIENT_OPTIONS_KIND_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 
177 World *fp_port_get_world(FpPort *port)
178 {
179     return port->world;
180 }
181 
182 void 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 
188 bool fp_port_enabled(FpPort *port)
189 {
190     return port->enabled;
191 }
192 
193 static void fp_port_set_link(FpPort *port, bool up)
194 {
195     NetClientState *nc = qemu_get_queue(port->nic);
196 
197     if (up == nc->link_down) {
198         nc->link_down = !up;
199         nc->info->link_status_changed(nc);
200     }
201 }
202 
203 void fp_port_enable(FpPort *port)
204 {
205     fp_port_set_link(port, true);
206     port->enabled = true;
207     DPRINTF("port %d enabled\n", port->index);
208 }
209 
210 void fp_port_disable(FpPort *port)
211 {
212     port->enabled = false;
213     fp_port_set_link(port, false);
214     DPRINTF("port %d disabled\n", port->index);
215 }
216 
217 FpPort *fp_port_alloc(Rocker *r, char *sw_name,
218                       MACAddr *start_mac, unsigned int index,
219                       NICPeers *peers)
220 {
221     FpPort *port = g_malloc0(sizeof(FpPort));
222 
223     if (!port) {
224         return NULL;
225     }
226 
227     port->r = r;
228     port->index = index;
229     port->pport = index + 1;
230 
231     /* front-panel switch port names are 1-based */
232 
233     port->name = g_strdup_printf("%sp%d", sw_name, port->pport);
234 
235     memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a));
236     port->conf.macaddr.a[5] += index;
237     port->conf.bootindex = -1;
238     port->conf.peers = *peers;
239 
240     port->nic = qemu_new_nic(&fp_port_info, &port->conf,
241                              sw_name, NULL, port);
242     qemu_format_nic_info_str(qemu_get_queue(port->nic),
243                              port->conf.macaddr.a);
244 
245     fp_port_reset(port);
246 
247     return port;
248 }
249 
250 void fp_port_free(FpPort *port)
251 {
252     qemu_del_nic(port->nic);
253     g_free(port->name);
254     g_free(port);
255 }
256 
257 void fp_port_reset(FpPort *port)
258 {
259     fp_port_disable(port);
260     port->speed = 10000;   /* 10Gbps */
261     port->duplex = DUPLEX_FULL;
262     port->autoneg = 0;
263 }
264