staging: dgnc: remove duplicated tty defines
[linux/fpc-iii.git] / net / ncsi / ncsi-cmd.c
blob7567ca63aae24b689e368219826df1be0982446b
1 /*
2 * Copyright Gavin Shan, IBM Corporation 2016.
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 */
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>
17 #include <net/ncsi.h>
18 #include <net/net_namespace.h>
19 #include <net/sock.h>
21 #include "internal.h"
22 #include "ncsi-pkt.h"
24 u32 ncsi_calculate_checksum(unsigned char *data, int len)
26 u32 checksum = 0;
27 int i;
29 for (i = 0; i < len; i += 2)
30 checksum += (((u32)data[i] << 8) | data[i + 1]);
32 checksum = (~checksum + 1);
33 return checksum;
36 /* This function should be called after the data area has been
37 * populated completely.
39 static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
40 struct ncsi_cmd_arg *nca)
42 u32 checksum;
43 __be32 *pchecksum;
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;
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);
64 static int ncsi_cmd_handler_default(struct sk_buff *skb,
65 struct ncsi_cmd_arg *nca)
67 struct ncsi_cmd_pkt *cmd;
69 cmd = skb_put_zero(skb, sizeof(*cmd));
70 ncsi_cmd_build_header(&cmd->cmd.common, nca);
72 return 0;
75 static int ncsi_cmd_handler_sp(struct sk_buff *skb,
76 struct ncsi_cmd_arg *nca)
78 struct ncsi_cmd_sp_pkt *cmd;
80 cmd = skb_put_zero(skb, sizeof(*cmd));
81 cmd->hw_arbitration = nca->bytes[0];
82 ncsi_cmd_build_header(&cmd->cmd.common, nca);
84 return 0;
87 static int ncsi_cmd_handler_dc(struct sk_buff *skb,
88 struct ncsi_cmd_arg *nca)
90 struct ncsi_cmd_dc_pkt *cmd;
92 cmd = skb_put_zero(skb, sizeof(*cmd));
93 cmd->ald = nca->bytes[0];
94 ncsi_cmd_build_header(&cmd->cmd.common, nca);
96 return 0;
99 static int ncsi_cmd_handler_rc(struct sk_buff *skb,
100 struct ncsi_cmd_arg *nca)
102 struct ncsi_cmd_rc_pkt *cmd;
104 cmd = skb_put_zero(skb, sizeof(*cmd));
105 ncsi_cmd_build_header(&cmd->cmd.common, nca);
107 return 0;
110 static int ncsi_cmd_handler_ae(struct sk_buff *skb,
111 struct ncsi_cmd_arg *nca)
113 struct ncsi_cmd_ae_pkt *cmd;
115 cmd = skb_put_zero(skb, sizeof(*cmd));
116 cmd->mc_id = nca->bytes[0];
117 cmd->mode = htonl(nca->dwords[1]);
118 ncsi_cmd_build_header(&cmd->cmd.common, nca);
120 return 0;
123 static int ncsi_cmd_handler_sl(struct sk_buff *skb,
124 struct ncsi_cmd_arg *nca)
126 struct ncsi_cmd_sl_pkt *cmd;
128 cmd = skb_put_zero(skb, sizeof(*cmd));
129 cmd->mode = htonl(nca->dwords[0]);
130 cmd->oem_mode = htonl(nca->dwords[1]);
131 ncsi_cmd_build_header(&cmd->cmd.common, nca);
133 return 0;
136 static int ncsi_cmd_handler_svf(struct sk_buff *skb,
137 struct ncsi_cmd_arg *nca)
139 struct ncsi_cmd_svf_pkt *cmd;
141 cmd = skb_put_zero(skb, sizeof(*cmd));
142 cmd->vlan = htons(nca->words[1]);
143 cmd->index = nca->bytes[6];
144 cmd->enable = nca->bytes[7];
145 ncsi_cmd_build_header(&cmd->cmd.common, nca);
147 return 0;
150 static int ncsi_cmd_handler_ev(struct sk_buff *skb,
151 struct ncsi_cmd_arg *nca)
153 struct ncsi_cmd_ev_pkt *cmd;
155 cmd = skb_put_zero(skb, sizeof(*cmd));
156 cmd->mode = nca->bytes[3];
157 ncsi_cmd_build_header(&cmd->cmd.common, nca);
159 return 0;
162 static int ncsi_cmd_handler_sma(struct sk_buff *skb,
163 struct ncsi_cmd_arg *nca)
165 struct ncsi_cmd_sma_pkt *cmd;
166 int i;
168 cmd = skb_put_zero(skb, sizeof(*cmd));
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);
175 return 0;
178 static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
179 struct ncsi_cmd_arg *nca)
181 struct ncsi_cmd_ebf_pkt *cmd;
183 cmd = skb_put_zero(skb, sizeof(*cmd));
184 cmd->mode = htonl(nca->dwords[0]);
185 ncsi_cmd_build_header(&cmd->cmd.common, nca);
187 return 0;
190 static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
191 struct ncsi_cmd_arg *nca)
193 struct ncsi_cmd_egmf_pkt *cmd;
195 cmd = skb_put_zero(skb, sizeof(*cmd));
196 cmd->mode = htonl(nca->dwords[0]);
197 ncsi_cmd_build_header(&cmd->cmd.common, nca);
199 return 0;
202 static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
203 struct ncsi_cmd_arg *nca)
205 struct ncsi_cmd_snfc_pkt *cmd;
207 cmd = skb_put_zero(skb, sizeof(*cmd));
208 cmd->mode = nca->bytes[0];
209 ncsi_cmd_build_header(&cmd->cmd.common, nca);
211 return 0;
214 static struct ncsi_cmd_handler {
215 unsigned char type;
216 int payload;
217 int (*handler)(struct sk_buff *skb,
218 struct ncsi_cmd_arg *nca);
219 } ncsi_cmd_handlers[] = {
220 { NCSI_PKT_CMD_CIS, 0, ncsi_cmd_handler_default },
221 { NCSI_PKT_CMD_SP, 4, ncsi_cmd_handler_sp },
222 { NCSI_PKT_CMD_DP, 0, ncsi_cmd_handler_default },
223 { NCSI_PKT_CMD_EC, 0, ncsi_cmd_handler_default },
224 { NCSI_PKT_CMD_DC, 4, ncsi_cmd_handler_dc },
225 { NCSI_PKT_CMD_RC, 4, ncsi_cmd_handler_rc },
226 { NCSI_PKT_CMD_ECNT, 0, ncsi_cmd_handler_default },
227 { NCSI_PKT_CMD_DCNT, 0, ncsi_cmd_handler_default },
228 { NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
229 { NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
230 { NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
231 { NCSI_PKT_CMD_SVF, 8, ncsi_cmd_handler_svf },
232 { NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
233 { NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
234 { NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
235 { NCSI_PKT_CMD_EBF, 4, ncsi_cmd_handler_ebf },
236 { NCSI_PKT_CMD_DBF, 0, ncsi_cmd_handler_default },
237 { NCSI_PKT_CMD_EGMF, 4, ncsi_cmd_handler_egmf },
238 { NCSI_PKT_CMD_DGMF, 0, ncsi_cmd_handler_default },
239 { NCSI_PKT_CMD_SNFC, 4, ncsi_cmd_handler_snfc },
240 { NCSI_PKT_CMD_GVI, 0, ncsi_cmd_handler_default },
241 { NCSI_PKT_CMD_GC, 0, ncsi_cmd_handler_default },
242 { NCSI_PKT_CMD_GP, 0, ncsi_cmd_handler_default },
243 { NCSI_PKT_CMD_GCPS, 0, ncsi_cmd_handler_default },
244 { NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default },
245 { NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default },
246 { NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default },
247 { NCSI_PKT_CMD_OEM, 0, NULL },
248 { NCSI_PKT_CMD_PLDM, 0, NULL },
249 { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
252 static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
254 struct ncsi_dev_priv *ndp = nca->ndp;
255 struct ncsi_dev *nd = &ndp->ndev;
256 struct net_device *dev = nd->dev;
257 int hlen = LL_RESERVED_SPACE(dev);
258 int tlen = dev->needed_tailroom;
259 int len = hlen + tlen;
260 struct sk_buff *skb;
261 struct ncsi_request *nr;
263 nr = ncsi_alloc_request(ndp, nca->req_flags);
264 if (!nr)
265 return NULL;
267 /* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
268 * The packet needs padding if its payload is less than 26 bytes to
269 * meet 64 bytes minimal ethernet frame length.
271 len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
272 if (nca->payload < 26)
273 len += 26;
274 else
275 len += nca->payload;
277 /* Allocate skb */
278 skb = alloc_skb(len, GFP_ATOMIC);
279 if (!skb) {
280 ncsi_free_request(nr);
281 return NULL;
284 nr->cmd = skb;
285 skb_reserve(skb, hlen);
286 skb_reset_network_header(skb);
288 skb->dev = dev;
289 skb->protocol = htons(ETH_P_NCSI);
291 return nr;
294 int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
296 struct ncsi_request *nr;
297 struct ethhdr *eh;
298 struct ncsi_cmd_handler *nch = NULL;
299 int i, ret;
301 /* Search for the handler */
302 for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
303 if (ncsi_cmd_handlers[i].type == nca->type) {
304 if (ncsi_cmd_handlers[i].handler)
305 nch = &ncsi_cmd_handlers[i];
306 else
307 nch = NULL;
309 break;
313 if (!nch) {
314 netdev_err(nca->ndp->ndev.dev,
315 "Cannot send packet with type 0x%02x\n", nca->type);
316 return -ENOENT;
319 /* Get packet payload length and allocate the request */
320 nca->payload = nch->payload;
321 nr = ncsi_alloc_command(nca);
322 if (!nr)
323 return -ENOMEM;
325 /* Prepare the packet */
326 nca->id = nr->id;
327 ret = nch->handler(nr->cmd, nca);
328 if (ret) {
329 ncsi_free_request(nr);
330 return ret;
333 /* Fill the ethernet header */
334 eh = skb_push(nr->cmd, sizeof(*eh));
335 eh->h_proto = htons(ETH_P_NCSI);
336 eth_broadcast_addr(eh->h_dest);
337 eth_broadcast_addr(eh->h_source);
339 /* Start the timer for the request that might not have
340 * corresponding response. Given NCSI is an internal
341 * connection a 1 second delay should be sufficient.
343 nr->enabled = true;
344 mod_timer(&nr->timer, jiffies + 1 * HZ);
346 /* Send NCSI packet */
347 skb_get(nr->cmd);
348 ret = dev_queue_xmit(nr->cmd);
349 if (ret < 0) {
350 ncsi_free_request(nr);
351 return ret;
354 return 0;