These changes are the raw update to qemu-2.6.
[kvmfornfv.git] / qemu / hw / net / rocker / rocker_fp.c
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_check_world(FpPort *port, World *world)
190 {
191     return port->world == world;
192 }
193
194 bool fp_port_enabled(FpPort *port)
195 {
196     return port->enabled;
197 }
198
199 static void fp_port_set_link(FpPort *port, bool up)
200 {
201     NetClientState *nc = qemu_get_queue(port->nic);
202
203     if (up == nc->link_down) {
204         nc->link_down = !up;
205         nc->info->link_status_changed(nc);
206     }
207 }
208
209 void fp_port_enable(FpPort *port)
210 {
211     fp_port_set_link(port, true);
212     port->enabled = true;
213     DPRINTF("port %d enabled\n", port->index);
214 }
215
216 void fp_port_disable(FpPort *port)
217 {
218     port->enabled = false;
219     fp_port_set_link(port, false);
220     DPRINTF("port %d disabled\n", port->index);
221 }
222
223 FpPort *fp_port_alloc(Rocker *r, char *sw_name,
224                       MACAddr *start_mac, unsigned int index,
225                       NICPeers *peers)
226 {
227     FpPort *port = g_new0(FpPort, 1);
228
229     if (!port) {
230         return NULL;
231     }
232
233     port->r = r;
234     port->index = index;
235     port->pport = index + 1;
236
237     /* front-panel switch port names are 1-based */
238
239     port->name = g_strdup_printf("%sp%d", sw_name, port->pport);
240
241     memcpy(port->conf.macaddr.a, start_mac, sizeof(port->conf.macaddr.a));
242     port->conf.macaddr.a[5] += index;
243     port->conf.bootindex = -1;
244     port->conf.peers = *peers;
245
246     port->nic = qemu_new_nic(&fp_port_info, &port->conf,
247                              sw_name, NULL, port);
248     qemu_format_nic_info_str(qemu_get_queue(port->nic),
249                              port->conf.macaddr.a);
250
251     fp_port_reset(port);
252
253     return port;
254 }
255
256 void fp_port_free(FpPort *port)
257 {
258     qemu_del_nic(port->nic);
259     g_free(port->name);
260     g_free(port);
261 }
262
263 void fp_port_reset(FpPort *port)
264 {
265     fp_port_disable(port);
266     port->speed = 10000;   /* 10Gbps */
267     port->duplex = DUPLEX_FULL;
268     port->autoneg = 0;
269 }