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