xref: /openbmc/qemu/hw/net/rocker/rocker_fp.c (revision 773495364ffbfc6a4d1e13e24e932f96409ba1d3)
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_macaddr(FpPort *port, MACAddr *macaddr)
55 {
56     memcpy(macaddr->a, port->conf.macaddr.a, sizeof(macaddr->a));
57 }
58 
59 void fp_port_set_macaddr(FpPort *port, MACAddr *macaddr)
60 {
61 /* XXX TODO implement and test setting mac addr
62  * XXX memcpy(port->conf.macaddr.a, macaddr.a, sizeof(port->conf.macaddr.a));
63  */
64 }
65 
66 uint8_t fp_port_get_learning(FpPort *port)
67 {
68     return port->learning;
69 }
70 
71 void fp_port_set_learning(FpPort *port, uint8_t learning)
72 {
73     port->learning = learning;
74 }
75 
76 int fp_port_get_settings(FpPort *port, uint32_t *speed,
77                          uint8_t *duplex, uint8_t *autoneg)
78 {
79     *speed = port->speed;
80     *duplex = port->duplex;
81     *autoneg = port->autoneg;
82 
83     return ROCKER_OK;
84 }
85 
86 int fp_port_set_settings(FpPort *port, uint32_t speed,
87                          uint8_t duplex, uint8_t autoneg)
88 {
89     /* XXX validate inputs */
90 
91     port->speed = speed;
92     port->duplex = duplex;
93     port->autoneg = autoneg;
94 
95     return ROCKER_OK;
96 }
97 
98 bool fp_port_from_pport(uint32_t pport, uint32_t *port)
99 {
100     if (pport < 1 || pport > ROCKER_FP_PORTS_MAX) {
101         return false;
102     }
103     *port = pport - 1;
104     return true;
105 }
106 
107 int fp_port_eg(FpPort *port, const struct iovec *iov, int iovcnt)
108 {
109     NetClientState *nc = qemu_get_queue(port->nic);
110 
111     if (port->enabled) {
112         qemu_sendv_packet(nc, iov, iovcnt);
113     }
114 
115     return ROCKER_OK;
116 }
117 
118 static int fp_port_can_receive(NetClientState *nc)
119 {
120     FpPort *port = qemu_get_nic_opaque(nc);
121 
122     return port->enabled;
123 }
124 
125 static ssize_t fp_port_receive_iov(NetClientState *nc, const struct iovec *iov,
126                                    int iovcnt)
127 {
128     FpPort *port = qemu_get_nic_opaque(nc);
129 
130     return world_ingress(port->world, port->pport, iov, iovcnt);
131 }
132 
133 static ssize_t fp_port_receive(NetClientState *nc, const uint8_t *buf,
134                                size_t size)
135 {
136     const struct iovec iov = {
137         .iov_base = (uint8_t *)buf,
138         .iov_len = size
139     };
140 
141     return fp_port_receive_iov(nc, &iov, 1);
142 }
143 
144 static void fp_port_cleanup(NetClientState *nc)
145 {
146 }
147 
148 static void fp_port_set_link_status(NetClientState *nc)
149 {
150     FpPort *port = qemu_get_nic_opaque(nc);
151 
152     rocker_event_link_changed(port->r, port->pport, !nc->link_down);
153 }
154 
155 static NetClientInfo fp_port_info = {
156     .type = NET_CLIENT_OPTIONS_KIND_NIC,
157     .size = sizeof(NICState),
158     .can_receive = fp_port_can_receive,
159     .receive = fp_port_receive,
160     .receive_iov = fp_port_receive_iov,
161     .cleanup = fp_port_cleanup,
162     .link_status_changed = fp_port_set_link_status,
163 };
164 
165 World *fp_port_get_world(FpPort *port)
166 {
167     return port->world;
168 }
169 
170 void fp_port_set_world(FpPort *port, World *world)
171 {
172     DPRINTF("port %d setting world \"%s\"\n", port->index, world_name(world));
173     port->world = world;
174 }
175 
176 bool fp_port_enabled(FpPort *port)
177 {
178     return port->enabled;
179 }
180 
181 void fp_port_enable(FpPort *port)
182 {
183     port->enabled = true;
184     DPRINTF("port %d enabled\n", port->index);
185 }
186 
187 void fp_port_disable(FpPort *port)
188 {
189     port->enabled = false;
190     DPRINTF("port %d disabled\n", port->index);
191 }
192 
193 FpPort *fp_port_alloc(Rocker *r, char *sw_name,
194                       MACAddr *start_mac, unsigned int index,
195                       NICPeers *peers)
196 {
197     FpPort *port = g_malloc0(sizeof(FpPort));
198 
199     if (!port) {
200         return NULL;
201     }
202 
203     port->r = r;
204     port->index = index;
205     port->pport = index + 1;
206 
207     /* front-panel switch port names are 1-based */
208 
209     port->name = g_strdup_printf("%sp%d", sw_name, port->pport);
210 
211     memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a));
212     port->conf.macaddr.a[5] += index;
213     port->conf.bootindex = -1;
214     port->conf.peers = *peers;
215 
216     port->nic = qemu_new_nic(&fp_port_info, &port->conf,
217                              sw_name, NULL, port);
218     qemu_format_nic_info_str(qemu_get_queue(port->nic),
219                              port->conf.macaddr.a);
220 
221     fp_port_reset(port);
222 
223     return port;
224 }
225 
226 void fp_port_free(FpPort *port)
227 {
228     qemu_del_nic(port->nic);
229     g_free(port->name);
230     g_free(port);
231 }
232 
233 void fp_port_reset(FpPort *port)
234 {
235     fp_port_disable(port);
236     port->speed = 10000;   /* 10Gbps */
237     port->duplex = DUPLEX_FULL;
238     port->autoneg = 0;
239 }
240