]> git.proxmox.com Git - mirror_ubuntu-eoan-kernel.git/blame - net/ncsi/ncsi-cmd.c
net: phy: trigger state machine immediately in phy_start_machine
[mirror_ubuntu-eoan-kernel.git] / net / ncsi / ncsi-cmd.c
CommitLineData
6389eaa7
GS
1/*
2 * Copyright Gavin Shan, IBM Corporation 2016.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 */
9
10#include <linux/module.h>
11#include <linux/kernel.h>
12#include <linux/init.h>
13#include <linux/etherdevice.h>
14#include <linux/netdevice.h>
15#include <linux/skbuff.h>
16
17#include <net/ncsi.h>
18#include <net/net_namespace.h>
19#include <net/sock.h>
20
21#include "internal.h"
22#include "ncsi-pkt.h"
23
24u32 ncsi_calculate_checksum(unsigned char *data, int len)
25{
26 u32 checksum = 0;
27 int i;
28
29 for (i = 0; i < len; i += 2)
30 checksum += (((u32)data[i] << 8) | data[i + 1]);
31
32 checksum = (~checksum + 1);
33 return checksum;
34}
35
36/* This function should be called after the data area has been
37 * populated completely.
38 */
39static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
40 struct ncsi_cmd_arg *nca)
41{
42 u32 checksum;
43 __be32 *pchecksum;
44
45 h->mc_id = 0;
46 h->revision = NCSI_PKT_REVISION;
47 h->reserved = 0;
48 h->id = nca->id;
49 h->type = nca->type;
50 h->channel = NCSI_TO_CHANNEL(nca->package,
51 nca->channel);
52 h->length = htons(nca->payload);
53 h->reserved1[0] = 0;
54 h->reserved1[1] = 0;
55
56 /* Fill with calculated checksum */
57 checksum = ncsi_calculate_checksum((unsigned char *)h,
58 sizeof(*h) + nca->payload);
59 pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
60 nca->payload);
61 *pchecksum = htonl(checksum);
62}
63
64static int ncsi_cmd_handler_default(struct sk_buff *skb,
65 struct ncsi_cmd_arg *nca)
66{
67 struct ncsi_cmd_pkt *cmd;
68
b080db58 69 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
70 ncsi_cmd_build_header(&cmd->cmd.common, nca);
71
72 return 0;
73}
74
75static int ncsi_cmd_handler_sp(struct sk_buff *skb,
76 struct ncsi_cmd_arg *nca)
77{
78 struct ncsi_cmd_sp_pkt *cmd;
79
b080db58 80 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
81 cmd->hw_arbitration = nca->bytes[0];
82 ncsi_cmd_build_header(&cmd->cmd.common, nca);
83
84 return 0;
85}
86
87static int ncsi_cmd_handler_dc(struct sk_buff *skb,
88 struct ncsi_cmd_arg *nca)
89{
90 struct ncsi_cmd_dc_pkt *cmd;
91
b080db58 92 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
93 cmd->ald = nca->bytes[0];
94 ncsi_cmd_build_header(&cmd->cmd.common, nca);
95
96 return 0;
97}
98
99static int ncsi_cmd_handler_rc(struct sk_buff *skb,
100 struct ncsi_cmd_arg *nca)
101{
102 struct ncsi_cmd_rc_pkt *cmd;
103
b080db58 104 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
105 ncsi_cmd_build_header(&cmd->cmd.common, nca);
106
107 return 0;
108}
109
110static int ncsi_cmd_handler_ae(struct sk_buff *skb,
111 struct ncsi_cmd_arg *nca)
112{
113 struct ncsi_cmd_ae_pkt *cmd;
114
b080db58 115 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
116 cmd->mc_id = nca->bytes[0];
117 cmd->mode = htonl(nca->dwords[1]);
118 ncsi_cmd_build_header(&cmd->cmd.common, nca);
119
120 return 0;
121}
122
123static int ncsi_cmd_handler_sl(struct sk_buff *skb,
124 struct ncsi_cmd_arg *nca)
125{
126 struct ncsi_cmd_sl_pkt *cmd;
127
b080db58 128 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
129 cmd->mode = htonl(nca->dwords[0]);
130 cmd->oem_mode = htonl(nca->dwords[1]);
131 ncsi_cmd_build_header(&cmd->cmd.common, nca);
132
133 return 0;
134}
135
136static int ncsi_cmd_handler_svf(struct sk_buff *skb,
137 struct ncsi_cmd_arg *nca)
138{
139 struct ncsi_cmd_svf_pkt *cmd;
140
b080db58 141 cmd = skb_put_zero(skb, sizeof(*cmd));
8579a67e
SMJ
142 cmd->vlan = htons(nca->words[1]);
143 cmd->index = nca->bytes[6];
144 cmd->enable = nca->bytes[7];
6389eaa7
GS
145 ncsi_cmd_build_header(&cmd->cmd.common, nca);
146
147 return 0;
148}
149
150static int ncsi_cmd_handler_ev(struct sk_buff *skb,
151 struct ncsi_cmd_arg *nca)
152{
153 struct ncsi_cmd_ev_pkt *cmd;
154
b080db58 155 cmd = skb_put_zero(skb, sizeof(*cmd));
8579a67e 156 cmd->mode = nca->bytes[3];
6389eaa7
GS
157 ncsi_cmd_build_header(&cmd->cmd.common, nca);
158
159 return 0;
160}
161
162static int ncsi_cmd_handler_sma(struct sk_buff *skb,
163 struct ncsi_cmd_arg *nca)
164{
165 struct ncsi_cmd_sma_pkt *cmd;
166 int i;
167
b080db58 168 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
169 for (i = 0; i < 6; i++)
170 cmd->mac[i] = nca->bytes[i];
171 cmd->index = nca->bytes[6];
172 cmd->at_e = nca->bytes[7];
173 ncsi_cmd_build_header(&cmd->cmd.common, nca);
174
175 return 0;
176}
177
178static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
179 struct ncsi_cmd_arg *nca)
180{
181 struct ncsi_cmd_ebf_pkt *cmd;
182
b080db58 183 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
184 cmd->mode = htonl(nca->dwords[0]);
185 ncsi_cmd_build_header(&cmd->cmd.common, nca);
186
187 return 0;
188}
189
190static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
191 struct ncsi_cmd_arg *nca)
192{
193 struct ncsi_cmd_egmf_pkt *cmd;
194
b080db58 195 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
196 cmd->mode = htonl(nca->dwords[0]);
197 ncsi_cmd_build_header(&cmd->cmd.common, nca);
198
199 return 0;
200}
201
202static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
203 struct ncsi_cmd_arg *nca)
204{
205 struct ncsi_cmd_snfc_pkt *cmd;
206
b080db58 207 cmd = skb_put_zero(skb, sizeof(*cmd));
6389eaa7
GS
208 cmd->mode = nca->bytes[0];
209 ncsi_cmd_build_header(&cmd->cmd.common, nca);
210
211 return 0;
212}
213
fb4ee675
VK
214static int ncsi_cmd_handler_oem(struct sk_buff *skb,
215 struct ncsi_cmd_arg *nca)
216{
217 struct ncsi_cmd_oem_pkt *cmd;
218 unsigned int len;
219
220 len = sizeof(struct ncsi_cmd_pkt_hdr) + 4;
221 if (nca->payload < 26)
222 len += 26;
223 else
224 len += nca->payload;
225
226 cmd = skb_put_zero(skb, len);
227 memcpy(&cmd->mfr_id, nca->data, nca->payload);
228 ncsi_cmd_build_header(&cmd->cmd.common, nca);
229
230 return 0;
231}
232
6389eaa7
GS
233static struct ncsi_cmd_handler {
234 unsigned char type;
235 int payload;
236 int (*handler)(struct sk_buff *skb,
237 struct ncsi_cmd_arg *nca);
238} ncsi_cmd_handlers[] = {
239 { NCSI_PKT_CMD_CIS, 0, ncsi_cmd_handler_default },
240 { NCSI_PKT_CMD_SP, 4, ncsi_cmd_handler_sp },
241 { NCSI_PKT_CMD_DP, 0, ncsi_cmd_handler_default },
242 { NCSI_PKT_CMD_EC, 0, ncsi_cmd_handler_default },
243 { NCSI_PKT_CMD_DC, 4, ncsi_cmd_handler_dc },
244 { NCSI_PKT_CMD_RC, 4, ncsi_cmd_handler_rc },
245 { NCSI_PKT_CMD_ECNT, 0, ncsi_cmd_handler_default },
246 { NCSI_PKT_CMD_DCNT, 0, ncsi_cmd_handler_default },
247 { NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
248 { NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
249 { NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
8579a67e 250 { NCSI_PKT_CMD_SVF, 8, ncsi_cmd_handler_svf },
6389eaa7
GS
251 { NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
252 { NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
253 { NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
254 { NCSI_PKT_CMD_EBF, 4, ncsi_cmd_handler_ebf },
255 { NCSI_PKT_CMD_DBF, 0, ncsi_cmd_handler_default },
256 { NCSI_PKT_CMD_EGMF, 4, ncsi_cmd_handler_egmf },
257 { NCSI_PKT_CMD_DGMF, 0, ncsi_cmd_handler_default },
258 { NCSI_PKT_CMD_SNFC, 4, ncsi_cmd_handler_snfc },
259 { NCSI_PKT_CMD_GVI, 0, ncsi_cmd_handler_default },
260 { NCSI_PKT_CMD_GC, 0, ncsi_cmd_handler_default },
261 { NCSI_PKT_CMD_GP, 0, ncsi_cmd_handler_default },
262 { NCSI_PKT_CMD_GCPS, 0, ncsi_cmd_handler_default },
263 { NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default },
264 { NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default },
265 { NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default },
fb4ee675 266 { NCSI_PKT_CMD_OEM, -1, ncsi_cmd_handler_oem },
6389eaa7
GS
267 { NCSI_PKT_CMD_PLDM, 0, NULL },
268 { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
269};
270
271static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
272{
273 struct ncsi_dev_priv *ndp = nca->ndp;
274 struct ncsi_dev *nd = &ndp->ndev;
275 struct net_device *dev = nd->dev;
276 int hlen = LL_RESERVED_SPACE(dev);
277 int tlen = dev->needed_tailroom;
278 int len = hlen + tlen;
279 struct sk_buff *skb;
280 struct ncsi_request *nr;
281
a0509cbe 282 nr = ncsi_alloc_request(ndp, nca->req_flags);
6389eaa7
GS
283 if (!nr)
284 return NULL;
285
286 /* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
287 * The packet needs padding if its payload is less than 26 bytes to
288 * meet 64 bytes minimal ethernet frame length.
289 */
290 len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
291 if (nca->payload < 26)
292 len += 26;
293 else
294 len += nca->payload;
295
296 /* Allocate skb */
297 skb = alloc_skb(len, GFP_ATOMIC);
298 if (!skb) {
299 ncsi_free_request(nr);
300 return NULL;
301 }
302
303 nr->cmd = skb;
304 skb_reserve(skb, hlen);
305 skb_reset_network_header(skb);
306
307 skb->dev = dev;
308 skb->protocol = htons(ETH_P_NCSI);
309
310 return nr;
311}
312
313int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
314{
315 struct ncsi_request *nr;
316 struct ethhdr *eh;
317 struct ncsi_cmd_handler *nch = NULL;
318 int i, ret;
319
320 /* Search for the handler */
321 for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
322 if (ncsi_cmd_handlers[i].type == nca->type) {
323 if (ncsi_cmd_handlers[i].handler)
324 nch = &ncsi_cmd_handlers[i];
325 else
326 nch = NULL;
327
328 break;
329 }
330 }
331
332 if (!nch) {
333 netdev_err(nca->ndp->ndev.dev,
334 "Cannot send packet with type 0x%02x\n", nca->type);
335 return -ENOENT;
336 }
337
fb4ee675
VK
338 /* Get packet payload length and allocate the request
339 * It is expected that if length set as negative in
340 * handler structure means caller is initializing it
341 * and setting length in nca before calling xmit function
342 */
343 if (nch->payload >= 0)
344 nca->payload = nch->payload;
6389eaa7
GS
345 nr = ncsi_alloc_command(nca);
346 if (!nr)
347 return -ENOMEM;
348
349 /* Prepare the packet */
350 nca->id = nr->id;
351 ret = nch->handler(nr->cmd, nca);
352 if (ret) {
353 ncsi_free_request(nr);
354 return ret;
355 }
356
357 /* Fill the ethernet header */
d58ff351 358 eh = skb_push(nr->cmd, sizeof(*eh));
6389eaa7
GS
359 eh->h_proto = htons(ETH_P_NCSI);
360 eth_broadcast_addr(eh->h_dest);
361 eth_broadcast_addr(eh->h_source);
362
363 /* Start the timer for the request that might not have
364 * corresponding response. Given NCSI is an internal
365 * connection a 1 second delay should be sufficient.
366 */
367 nr->enabled = true;
368 mod_timer(&nr->timer, jiffies + 1 * HZ);
369
370 /* Send NCSI packet */
371 skb_get(nr->cmd);
372 ret = dev_queue_xmit(nr->cmd);
373 if (ret < 0) {
374 ncsi_free_request(nr);
375 return ret;
376 }
377
378 return 0;
379}