12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
26389eaa7SGavin Shan /*
36389eaa7SGavin Shan * Copyright Gavin Shan, IBM Corporation 2016.
46389eaa7SGavin Shan */
56389eaa7SGavin Shan
66389eaa7SGavin Shan #include <linux/module.h>
76389eaa7SGavin Shan #include <linux/kernel.h>
86389eaa7SGavin Shan #include <linux/init.h>
96389eaa7SGavin Shan #include <linux/etherdevice.h>
106389eaa7SGavin Shan #include <linux/netdevice.h>
116389eaa7SGavin Shan #include <linux/skbuff.h>
126389eaa7SGavin Shan
136389eaa7SGavin Shan #include <net/ncsi.h>
146389eaa7SGavin Shan #include <net/net_namespace.h>
156389eaa7SGavin Shan #include <net/sock.h>
169771b8ccSJustin.Lee1@Dell.com #include <net/genetlink.h>
176389eaa7SGavin Shan
186389eaa7SGavin Shan #include "internal.h"
196389eaa7SGavin Shan #include "ncsi-pkt.h"
206389eaa7SGavin Shan
21ac132852SKumar Thangavel static const int padding_bytes = 26;
22ac132852SKumar Thangavel
ncsi_calculate_checksum(unsigned char * data,int len)236389eaa7SGavin Shan u32 ncsi_calculate_checksum(unsigned char *data, int len)
246389eaa7SGavin Shan {
256389eaa7SGavin Shan u32 checksum = 0;
266389eaa7SGavin Shan int i;
276389eaa7SGavin Shan
286389eaa7SGavin Shan for (i = 0; i < len; i += 2)
296389eaa7SGavin Shan checksum += (((u32)data[i] << 8) | data[i + 1]);
306389eaa7SGavin Shan
316389eaa7SGavin Shan checksum = (~checksum + 1);
326389eaa7SGavin Shan return checksum;
336389eaa7SGavin Shan }
346389eaa7SGavin Shan
356389eaa7SGavin Shan /* This function should be called after the data area has been
366389eaa7SGavin Shan * populated completely.
376389eaa7SGavin Shan */
ncsi_cmd_build_header(struct ncsi_pkt_hdr * h,struct ncsi_cmd_arg * nca)386389eaa7SGavin Shan static void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
396389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
406389eaa7SGavin Shan {
416389eaa7SGavin Shan u32 checksum;
426389eaa7SGavin Shan __be32 *pchecksum;
436389eaa7SGavin Shan
446389eaa7SGavin Shan h->mc_id = 0;
456389eaa7SGavin Shan h->revision = NCSI_PKT_REVISION;
466389eaa7SGavin Shan h->reserved = 0;
476389eaa7SGavin Shan h->id = nca->id;
486389eaa7SGavin Shan h->type = nca->type;
496389eaa7SGavin Shan h->channel = NCSI_TO_CHANNEL(nca->package,
506389eaa7SGavin Shan nca->channel);
516389eaa7SGavin Shan h->length = htons(nca->payload);
526389eaa7SGavin Shan h->reserved1[0] = 0;
536389eaa7SGavin Shan h->reserved1[1] = 0;
546389eaa7SGavin Shan
556389eaa7SGavin Shan /* Fill with calculated checksum */
566389eaa7SGavin Shan checksum = ncsi_calculate_checksum((unsigned char *)h,
576389eaa7SGavin Shan sizeof(*h) + nca->payload);
586389eaa7SGavin Shan pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
5996a1b033STerry S. Duncan ALIGN(nca->payload, 4));
606389eaa7SGavin Shan *pchecksum = htonl(checksum);
616389eaa7SGavin Shan }
626389eaa7SGavin Shan
ncsi_cmd_handler_default(struct sk_buff * skb,struct ncsi_cmd_arg * nca)636389eaa7SGavin Shan static int ncsi_cmd_handler_default(struct sk_buff *skb,
646389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
656389eaa7SGavin Shan {
666389eaa7SGavin Shan struct ncsi_cmd_pkt *cmd;
676389eaa7SGavin Shan
68b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
696389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
706389eaa7SGavin Shan
716389eaa7SGavin Shan return 0;
726389eaa7SGavin Shan }
736389eaa7SGavin Shan
ncsi_cmd_handler_sp(struct sk_buff * skb,struct ncsi_cmd_arg * nca)746389eaa7SGavin Shan static int ncsi_cmd_handler_sp(struct sk_buff *skb,
756389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
766389eaa7SGavin Shan {
776389eaa7SGavin Shan struct ncsi_cmd_sp_pkt *cmd;
786389eaa7SGavin Shan
79b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
806389eaa7SGavin Shan cmd->hw_arbitration = nca->bytes[0];
816389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
826389eaa7SGavin Shan
836389eaa7SGavin Shan return 0;
846389eaa7SGavin Shan }
856389eaa7SGavin Shan
ncsi_cmd_handler_dc(struct sk_buff * skb,struct ncsi_cmd_arg * nca)866389eaa7SGavin Shan static int ncsi_cmd_handler_dc(struct sk_buff *skb,
876389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
886389eaa7SGavin Shan {
896389eaa7SGavin Shan struct ncsi_cmd_dc_pkt *cmd;
906389eaa7SGavin Shan
91b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
926389eaa7SGavin Shan cmd->ald = nca->bytes[0];
936389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
946389eaa7SGavin Shan
956389eaa7SGavin Shan return 0;
966389eaa7SGavin Shan }
976389eaa7SGavin Shan
ncsi_cmd_handler_rc(struct sk_buff * skb,struct ncsi_cmd_arg * nca)986389eaa7SGavin Shan static int ncsi_cmd_handler_rc(struct sk_buff *skb,
996389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1006389eaa7SGavin Shan {
1016389eaa7SGavin Shan struct ncsi_cmd_rc_pkt *cmd;
1026389eaa7SGavin Shan
103b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1046389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1056389eaa7SGavin Shan
1066389eaa7SGavin Shan return 0;
1076389eaa7SGavin Shan }
1086389eaa7SGavin Shan
ncsi_cmd_handler_ae(struct sk_buff * skb,struct ncsi_cmd_arg * nca)1096389eaa7SGavin Shan static int ncsi_cmd_handler_ae(struct sk_buff *skb,
1106389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1116389eaa7SGavin Shan {
1126389eaa7SGavin Shan struct ncsi_cmd_ae_pkt *cmd;
1136389eaa7SGavin Shan
114b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1156389eaa7SGavin Shan cmd->mc_id = nca->bytes[0];
1166389eaa7SGavin Shan cmd->mode = htonl(nca->dwords[1]);
1176389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1186389eaa7SGavin Shan
1196389eaa7SGavin Shan return 0;
1206389eaa7SGavin Shan }
1216389eaa7SGavin Shan
ncsi_cmd_handler_sl(struct sk_buff * skb,struct ncsi_cmd_arg * nca)1226389eaa7SGavin Shan static int ncsi_cmd_handler_sl(struct sk_buff *skb,
1236389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1246389eaa7SGavin Shan {
1256389eaa7SGavin Shan struct ncsi_cmd_sl_pkt *cmd;
1266389eaa7SGavin Shan
127b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1286389eaa7SGavin Shan cmd->mode = htonl(nca->dwords[0]);
1296389eaa7SGavin Shan cmd->oem_mode = htonl(nca->dwords[1]);
1306389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1316389eaa7SGavin Shan
1326389eaa7SGavin Shan return 0;
1336389eaa7SGavin Shan }
1346389eaa7SGavin Shan
ncsi_cmd_handler_svf(struct sk_buff * skb,struct ncsi_cmd_arg * nca)1356389eaa7SGavin Shan static int ncsi_cmd_handler_svf(struct sk_buff *skb,
1366389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1376389eaa7SGavin Shan {
1386389eaa7SGavin Shan struct ncsi_cmd_svf_pkt *cmd;
1396389eaa7SGavin Shan
140b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1418579a67eSSamuel Mendoza-Jonas cmd->vlan = htons(nca->words[1]);
1428579a67eSSamuel Mendoza-Jonas cmd->index = nca->bytes[6];
1438579a67eSSamuel Mendoza-Jonas cmd->enable = nca->bytes[7];
1446389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1456389eaa7SGavin Shan
1466389eaa7SGavin Shan return 0;
1476389eaa7SGavin Shan }
1486389eaa7SGavin Shan
ncsi_cmd_handler_ev(struct sk_buff * skb,struct ncsi_cmd_arg * nca)1496389eaa7SGavin Shan static int ncsi_cmd_handler_ev(struct sk_buff *skb,
1506389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1516389eaa7SGavin Shan {
1526389eaa7SGavin Shan struct ncsi_cmd_ev_pkt *cmd;
1536389eaa7SGavin Shan
154b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1558579a67eSSamuel Mendoza-Jonas cmd->mode = nca->bytes[3];
1566389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1576389eaa7SGavin Shan
1586389eaa7SGavin Shan return 0;
1596389eaa7SGavin Shan }
1606389eaa7SGavin Shan
ncsi_cmd_handler_sma(struct sk_buff * skb,struct ncsi_cmd_arg * nca)1616389eaa7SGavin Shan static int ncsi_cmd_handler_sma(struct sk_buff *skb,
1626389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1636389eaa7SGavin Shan {
1646389eaa7SGavin Shan struct ncsi_cmd_sma_pkt *cmd;
1656389eaa7SGavin Shan int i;
1666389eaa7SGavin Shan
167b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1686389eaa7SGavin Shan for (i = 0; i < 6; i++)
1696389eaa7SGavin Shan cmd->mac[i] = nca->bytes[i];
1706389eaa7SGavin Shan cmd->index = nca->bytes[6];
1716389eaa7SGavin Shan cmd->at_e = nca->bytes[7];
1726389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1736389eaa7SGavin Shan
1746389eaa7SGavin Shan return 0;
1756389eaa7SGavin Shan }
1766389eaa7SGavin Shan
ncsi_cmd_handler_ebf(struct sk_buff * skb,struct ncsi_cmd_arg * nca)1776389eaa7SGavin Shan static int ncsi_cmd_handler_ebf(struct sk_buff *skb,
1786389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1796389eaa7SGavin Shan {
1806389eaa7SGavin Shan struct ncsi_cmd_ebf_pkt *cmd;
1816389eaa7SGavin Shan
182b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1836389eaa7SGavin Shan cmd->mode = htonl(nca->dwords[0]);
1846389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1856389eaa7SGavin Shan
1866389eaa7SGavin Shan return 0;
1876389eaa7SGavin Shan }
1886389eaa7SGavin Shan
ncsi_cmd_handler_egmf(struct sk_buff * skb,struct ncsi_cmd_arg * nca)1896389eaa7SGavin Shan static int ncsi_cmd_handler_egmf(struct sk_buff *skb,
1906389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
1916389eaa7SGavin Shan {
1926389eaa7SGavin Shan struct ncsi_cmd_egmf_pkt *cmd;
1936389eaa7SGavin Shan
194b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
1956389eaa7SGavin Shan cmd->mode = htonl(nca->dwords[0]);
1966389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
1976389eaa7SGavin Shan
1986389eaa7SGavin Shan return 0;
1996389eaa7SGavin Shan }
2006389eaa7SGavin Shan
ncsi_cmd_handler_snfc(struct sk_buff * skb,struct ncsi_cmd_arg * nca)2016389eaa7SGavin Shan static int ncsi_cmd_handler_snfc(struct sk_buff *skb,
2026389eaa7SGavin Shan struct ncsi_cmd_arg *nca)
2036389eaa7SGavin Shan {
2046389eaa7SGavin Shan struct ncsi_cmd_snfc_pkt *cmd;
2056389eaa7SGavin Shan
206b080db58SJohannes Berg cmd = skb_put_zero(skb, sizeof(*cmd));
2076389eaa7SGavin Shan cmd->mode = nca->bytes[0];
2086389eaa7SGavin Shan ncsi_cmd_build_header(&cmd->cmd.common, nca);
2096389eaa7SGavin Shan
2106389eaa7SGavin Shan return 0;
2116389eaa7SGavin Shan }
2126389eaa7SGavin Shan
ncsi_cmd_handler_oem(struct sk_buff * skb,struct ncsi_cmd_arg * nca)213fb4ee675SVijay Khemka static int ncsi_cmd_handler_oem(struct sk_buff *skb,
214fb4ee675SVijay Khemka struct ncsi_cmd_arg *nca)
215fb4ee675SVijay Khemka {
216fb4ee675SVijay Khemka struct ncsi_cmd_oem_pkt *cmd;
217fb4ee675SVijay Khemka unsigned int len;
218ac132852SKumar Thangavel int payload;
219ac132852SKumar Thangavel /* NC-SI spec DSP_0222_1.2.0, section 8.2.2.2
220ac132852SKumar Thangavel * requires payload to be padded with 0 to
221ac132852SKumar Thangavel * 32-bit boundary before the checksum field.
222ac132852SKumar Thangavel * Ensure the padding bytes are accounted for in
223ac132852SKumar Thangavel * skb allocation
224ac132852SKumar Thangavel */
225fb4ee675SVijay Khemka
226ac132852SKumar Thangavel payload = ALIGN(nca->payload, 4);
227fb4ee675SVijay Khemka len = sizeof(struct ncsi_cmd_pkt_hdr) + 4;
228ac132852SKumar Thangavel len += max(payload, padding_bytes);
229fb4ee675SVijay Khemka
230fb4ee675SVijay Khemka cmd = skb_put_zero(skb, len);
231b93884eeSKees Cook unsafe_memcpy(&cmd->mfr_id, nca->data, nca->payload,
232b93884eeSKees Cook /* skb allocated with enough to load the payload */);
233fb4ee675SVijay Khemka ncsi_cmd_build_header(&cmd->cmd.common, nca);
234fb4ee675SVijay Khemka
235fb4ee675SVijay Khemka return 0;
236fb4ee675SVijay Khemka }
237fb4ee675SVijay Khemka
2386389eaa7SGavin Shan static struct ncsi_cmd_handler {
2396389eaa7SGavin Shan unsigned char type;
2406389eaa7SGavin Shan int payload;
2416389eaa7SGavin Shan int (*handler)(struct sk_buff *skb,
2426389eaa7SGavin Shan struct ncsi_cmd_arg *nca);
2436389eaa7SGavin Shan } ncsi_cmd_handlers[] = {
2446389eaa7SGavin Shan { NCSI_PKT_CMD_CIS, 0, ncsi_cmd_handler_default },
2456389eaa7SGavin Shan { NCSI_PKT_CMD_SP, 4, ncsi_cmd_handler_sp },
2466389eaa7SGavin Shan { NCSI_PKT_CMD_DP, 0, ncsi_cmd_handler_default },
2476389eaa7SGavin Shan { NCSI_PKT_CMD_EC, 0, ncsi_cmd_handler_default },
2486389eaa7SGavin Shan { NCSI_PKT_CMD_DC, 4, ncsi_cmd_handler_dc },
2496389eaa7SGavin Shan { NCSI_PKT_CMD_RC, 4, ncsi_cmd_handler_rc },
2506389eaa7SGavin Shan { NCSI_PKT_CMD_ECNT, 0, ncsi_cmd_handler_default },
2516389eaa7SGavin Shan { NCSI_PKT_CMD_DCNT, 0, ncsi_cmd_handler_default },
2526389eaa7SGavin Shan { NCSI_PKT_CMD_AE, 8, ncsi_cmd_handler_ae },
2536389eaa7SGavin Shan { NCSI_PKT_CMD_SL, 8, ncsi_cmd_handler_sl },
2546389eaa7SGavin Shan { NCSI_PKT_CMD_GLS, 0, ncsi_cmd_handler_default },
2558579a67eSSamuel Mendoza-Jonas { NCSI_PKT_CMD_SVF, 8, ncsi_cmd_handler_svf },
2566389eaa7SGavin Shan { NCSI_PKT_CMD_EV, 4, ncsi_cmd_handler_ev },
2576389eaa7SGavin Shan { NCSI_PKT_CMD_DV, 0, ncsi_cmd_handler_default },
2586389eaa7SGavin Shan { NCSI_PKT_CMD_SMA, 8, ncsi_cmd_handler_sma },
2596389eaa7SGavin Shan { NCSI_PKT_CMD_EBF, 4, ncsi_cmd_handler_ebf },
2606389eaa7SGavin Shan { NCSI_PKT_CMD_DBF, 0, ncsi_cmd_handler_default },
2616389eaa7SGavin Shan { NCSI_PKT_CMD_EGMF, 4, ncsi_cmd_handler_egmf },
2626389eaa7SGavin Shan { NCSI_PKT_CMD_DGMF, 0, ncsi_cmd_handler_default },
2636389eaa7SGavin Shan { NCSI_PKT_CMD_SNFC, 4, ncsi_cmd_handler_snfc },
2646389eaa7SGavin Shan { NCSI_PKT_CMD_GVI, 0, ncsi_cmd_handler_default },
2656389eaa7SGavin Shan { NCSI_PKT_CMD_GC, 0, ncsi_cmd_handler_default },
2666389eaa7SGavin Shan { NCSI_PKT_CMD_GP, 0, ncsi_cmd_handler_default },
2676389eaa7SGavin Shan { NCSI_PKT_CMD_GCPS, 0, ncsi_cmd_handler_default },
2686389eaa7SGavin Shan { NCSI_PKT_CMD_GNS, 0, ncsi_cmd_handler_default },
2696389eaa7SGavin Shan { NCSI_PKT_CMD_GNPTS, 0, ncsi_cmd_handler_default },
2706389eaa7SGavin Shan { NCSI_PKT_CMD_GPS, 0, ncsi_cmd_handler_default },
271fb4ee675SVijay Khemka { NCSI_PKT_CMD_OEM, -1, ncsi_cmd_handler_oem },
2726389eaa7SGavin Shan { NCSI_PKT_CMD_PLDM, 0, NULL },
273*67515088SPeter Delevoryas { NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default },
274*67515088SPeter Delevoryas { NCSI_PKT_CMD_GMCMA, 0, ncsi_cmd_handler_default }
2756389eaa7SGavin Shan };
2766389eaa7SGavin Shan
ncsi_alloc_command(struct ncsi_cmd_arg * nca)2776389eaa7SGavin Shan static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
2786389eaa7SGavin Shan {
2796389eaa7SGavin Shan struct ncsi_dev_priv *ndp = nca->ndp;
2806389eaa7SGavin Shan struct ncsi_dev *nd = &ndp->ndev;
2816389eaa7SGavin Shan struct net_device *dev = nd->dev;
2826389eaa7SGavin Shan int hlen = LL_RESERVED_SPACE(dev);
2836389eaa7SGavin Shan int tlen = dev->needed_tailroom;
284ac132852SKumar Thangavel int payload;
2856389eaa7SGavin Shan int len = hlen + tlen;
2866389eaa7SGavin Shan struct sk_buff *skb;
2876389eaa7SGavin Shan struct ncsi_request *nr;
2886389eaa7SGavin Shan
289a0509cbeSGavin Shan nr = ncsi_alloc_request(ndp, nca->req_flags);
2906389eaa7SGavin Shan if (!nr)
2916389eaa7SGavin Shan return NULL;
2926389eaa7SGavin Shan
2936389eaa7SGavin Shan /* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
294ac132852SKumar Thangavel * Payload needs padding so that the checksum field following payload is
295ac132852SKumar Thangavel * aligned to 32-bit boundary.
2966389eaa7SGavin Shan * The packet needs padding if its payload is less than 26 bytes to
2976389eaa7SGavin Shan * meet 64 bytes minimal ethernet frame length.
2986389eaa7SGavin Shan */
2996389eaa7SGavin Shan len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
300ac132852SKumar Thangavel payload = ALIGN(nca->payload, 4);
301ac132852SKumar Thangavel len += max(payload, padding_bytes);
3026389eaa7SGavin Shan
3036389eaa7SGavin Shan /* Allocate skb */
3046389eaa7SGavin Shan skb = alloc_skb(len, GFP_ATOMIC);
3056389eaa7SGavin Shan if (!skb) {
3066389eaa7SGavin Shan ncsi_free_request(nr);
3076389eaa7SGavin Shan return NULL;
3086389eaa7SGavin Shan }
3096389eaa7SGavin Shan
3106389eaa7SGavin Shan nr->cmd = skb;
3116389eaa7SGavin Shan skb_reserve(skb, hlen);
3126389eaa7SGavin Shan skb_reset_network_header(skb);
3136389eaa7SGavin Shan
3146389eaa7SGavin Shan skb->dev = dev;
3156389eaa7SGavin Shan skb->protocol = htons(ETH_P_NCSI);
3166389eaa7SGavin Shan
3176389eaa7SGavin Shan return nr;
3186389eaa7SGavin Shan }
3196389eaa7SGavin Shan
ncsi_xmit_cmd(struct ncsi_cmd_arg * nca)3206389eaa7SGavin Shan int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
3216389eaa7SGavin Shan {
3226389eaa7SGavin Shan struct ncsi_cmd_handler *nch = NULL;
323f6edbf2dSJustin.Lee1@Dell.com struct ncsi_request *nr;
324f6edbf2dSJustin.Lee1@Dell.com unsigned char type;
325f6edbf2dSJustin.Lee1@Dell.com struct ethhdr *eh;
3266389eaa7SGavin Shan int i, ret;
3276389eaa7SGavin Shan
328f6edbf2dSJustin.Lee1@Dell.com /* Use OEM generic handler for Netlink request */
329f6edbf2dSJustin.Lee1@Dell.com if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN)
330f6edbf2dSJustin.Lee1@Dell.com type = NCSI_PKT_CMD_OEM;
331f6edbf2dSJustin.Lee1@Dell.com else
332f6edbf2dSJustin.Lee1@Dell.com type = nca->type;
333f6edbf2dSJustin.Lee1@Dell.com
3346389eaa7SGavin Shan /* Search for the handler */
3356389eaa7SGavin Shan for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
336f6edbf2dSJustin.Lee1@Dell.com if (ncsi_cmd_handlers[i].type == type) {
3376389eaa7SGavin Shan if (ncsi_cmd_handlers[i].handler)
3386389eaa7SGavin Shan nch = &ncsi_cmd_handlers[i];
3396389eaa7SGavin Shan else
3406389eaa7SGavin Shan nch = NULL;
3416389eaa7SGavin Shan
3426389eaa7SGavin Shan break;
3436389eaa7SGavin Shan }
3446389eaa7SGavin Shan }
3456389eaa7SGavin Shan
3466389eaa7SGavin Shan if (!nch) {
3476389eaa7SGavin Shan netdev_err(nca->ndp->ndev.dev,
3486389eaa7SGavin Shan "Cannot send packet with type 0x%02x\n", nca->type);
3496389eaa7SGavin Shan return -ENOENT;
3506389eaa7SGavin Shan }
3516389eaa7SGavin Shan
352fb4ee675SVijay Khemka /* Get packet payload length and allocate the request
353fb4ee675SVijay Khemka * It is expected that if length set as negative in
354fb4ee675SVijay Khemka * handler structure means caller is initializing it
355fb4ee675SVijay Khemka * and setting length in nca before calling xmit function
356fb4ee675SVijay Khemka */
357fb4ee675SVijay Khemka if (nch->payload >= 0)
3586389eaa7SGavin Shan nca->payload = nch->payload;
3596389eaa7SGavin Shan nr = ncsi_alloc_command(nca);
3606389eaa7SGavin Shan if (!nr)
3616389eaa7SGavin Shan return -ENOMEM;
3626389eaa7SGavin Shan
3639771b8ccSJustin.Lee1@Dell.com /* track netlink information */
3649771b8ccSJustin.Lee1@Dell.com if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
3659771b8ccSJustin.Lee1@Dell.com nr->snd_seq = nca->info->snd_seq;
3669771b8ccSJustin.Lee1@Dell.com nr->snd_portid = nca->info->snd_portid;
3679771b8ccSJustin.Lee1@Dell.com nr->nlhdr = *nca->info->nlhdr;
3689771b8ccSJustin.Lee1@Dell.com }
3699771b8ccSJustin.Lee1@Dell.com
3706389eaa7SGavin Shan /* Prepare the packet */
3716389eaa7SGavin Shan nca->id = nr->id;
3726389eaa7SGavin Shan ret = nch->handler(nr->cmd, nca);
3736389eaa7SGavin Shan if (ret) {
3746389eaa7SGavin Shan ncsi_free_request(nr);
3756389eaa7SGavin Shan return ret;
3766389eaa7SGavin Shan }
3776389eaa7SGavin Shan
3786389eaa7SGavin Shan /* Fill the ethernet header */
379d58ff351SJohannes Berg eh = skb_push(nr->cmd, sizeof(*eh));
3806389eaa7SGavin Shan eh->h_proto = htons(ETH_P_NCSI);
3816389eaa7SGavin Shan eth_broadcast_addr(eh->h_dest);
3827c7b58d4SVijay Khemka
3837c7b58d4SVijay Khemka /* If mac address received from device then use it for
3847c7b58d4SVijay Khemka * source address as unicast address else use broadcast
3857c7b58d4SVijay Khemka * address as source address
3867c7b58d4SVijay Khemka */
3877c7b58d4SVijay Khemka if (nca->ndp->gma_flag == 1)
3887c7b58d4SVijay Khemka memcpy(eh->h_source, nca->ndp->ndev.dev->dev_addr, ETH_ALEN);
3897c7b58d4SVijay Khemka else
3906389eaa7SGavin Shan eth_broadcast_addr(eh->h_source);
3916389eaa7SGavin Shan
3926389eaa7SGavin Shan /* Start the timer for the request that might not have
3936389eaa7SGavin Shan * corresponding response. Given NCSI is an internal
3946389eaa7SGavin Shan * connection a 1 second delay should be sufficient.
3956389eaa7SGavin Shan */
3966389eaa7SGavin Shan nr->enabled = true;
3976389eaa7SGavin Shan mod_timer(&nr->timer, jiffies + 1 * HZ);
3986389eaa7SGavin Shan
3996389eaa7SGavin Shan /* Send NCSI packet */
4006389eaa7SGavin Shan skb_get(nr->cmd);
4016389eaa7SGavin Shan ret = dev_queue_xmit(nr->cmd);
4026389eaa7SGavin Shan if (ret < 0) {
4036389eaa7SGavin Shan ncsi_free_request(nr);
4046389eaa7SGavin Shan return ret;
4056389eaa7SGavin Shan }
4066389eaa7SGavin Shan
4076389eaa7SGavin Shan return 0;
4086389eaa7SGavin Shan }
409