xref: /openbmc/linux/net/ncsi/ncsi-cmd.c (revision b93884ee)
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);
231*b93884eeSKees Cook 	unsafe_memcpy(&cmd->mfr_id, nca->data, nca->payload,
232*b93884eeSKees 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                     },
2736389eaa7SGavin Shan 	{ NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
2746389eaa7SGavin Shan };
2756389eaa7SGavin Shan 
ncsi_alloc_command(struct ncsi_cmd_arg * nca)2766389eaa7SGavin Shan static struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
2776389eaa7SGavin Shan {
2786389eaa7SGavin Shan 	struct ncsi_dev_priv *ndp = nca->ndp;
2796389eaa7SGavin Shan 	struct ncsi_dev *nd = &ndp->ndev;
2806389eaa7SGavin Shan 	struct net_device *dev = nd->dev;
2816389eaa7SGavin Shan 	int hlen = LL_RESERVED_SPACE(dev);
2826389eaa7SGavin Shan 	int tlen = dev->needed_tailroom;
283ac132852SKumar Thangavel 	int payload;
2846389eaa7SGavin Shan 	int len = hlen + tlen;
2856389eaa7SGavin Shan 	struct sk_buff *skb;
2866389eaa7SGavin Shan 	struct ncsi_request *nr;
2876389eaa7SGavin Shan 
288a0509cbeSGavin Shan 	nr = ncsi_alloc_request(ndp, nca->req_flags);
2896389eaa7SGavin Shan 	if (!nr)
2906389eaa7SGavin Shan 		return NULL;
2916389eaa7SGavin Shan 
2926389eaa7SGavin Shan 	/* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
293ac132852SKumar Thangavel 	 * Payload needs padding so that the checksum field following payload is
294ac132852SKumar Thangavel 	 * aligned to 32-bit boundary.
2956389eaa7SGavin Shan 	 * The packet needs padding if its payload is less than 26 bytes to
2966389eaa7SGavin Shan 	 * meet 64 bytes minimal ethernet frame length.
2976389eaa7SGavin Shan 	 */
2986389eaa7SGavin Shan 	len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
299ac132852SKumar Thangavel 	payload = ALIGN(nca->payload, 4);
300ac132852SKumar Thangavel 	len += max(payload, padding_bytes);
3016389eaa7SGavin Shan 
3026389eaa7SGavin Shan 	/* Allocate skb */
3036389eaa7SGavin Shan 	skb = alloc_skb(len, GFP_ATOMIC);
3046389eaa7SGavin Shan 	if (!skb) {
3056389eaa7SGavin Shan 		ncsi_free_request(nr);
3066389eaa7SGavin Shan 		return NULL;
3076389eaa7SGavin Shan 	}
3086389eaa7SGavin Shan 
3096389eaa7SGavin Shan 	nr->cmd = skb;
3106389eaa7SGavin Shan 	skb_reserve(skb, hlen);
3116389eaa7SGavin Shan 	skb_reset_network_header(skb);
3126389eaa7SGavin Shan 
3136389eaa7SGavin Shan 	skb->dev = dev;
3146389eaa7SGavin Shan 	skb->protocol = htons(ETH_P_NCSI);
3156389eaa7SGavin Shan 
3166389eaa7SGavin Shan 	return nr;
3176389eaa7SGavin Shan }
3186389eaa7SGavin Shan 
ncsi_xmit_cmd(struct ncsi_cmd_arg * nca)3196389eaa7SGavin Shan int ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
3206389eaa7SGavin Shan {
3216389eaa7SGavin Shan 	struct ncsi_cmd_handler *nch = NULL;
322f6edbf2dSJustin.Lee1@Dell.com 	struct ncsi_request *nr;
323f6edbf2dSJustin.Lee1@Dell.com 	unsigned char type;
324f6edbf2dSJustin.Lee1@Dell.com 	struct ethhdr *eh;
3256389eaa7SGavin Shan 	int i, ret;
3266389eaa7SGavin Shan 
327f6edbf2dSJustin.Lee1@Dell.com 	/* Use OEM generic handler for Netlink request */
328f6edbf2dSJustin.Lee1@Dell.com 	if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN)
329f6edbf2dSJustin.Lee1@Dell.com 		type = NCSI_PKT_CMD_OEM;
330f6edbf2dSJustin.Lee1@Dell.com 	else
331f6edbf2dSJustin.Lee1@Dell.com 		type = nca->type;
332f6edbf2dSJustin.Lee1@Dell.com 
3336389eaa7SGavin Shan 	/* Search for the handler */
3346389eaa7SGavin Shan 	for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
335f6edbf2dSJustin.Lee1@Dell.com 		if (ncsi_cmd_handlers[i].type == type) {
3366389eaa7SGavin Shan 			if (ncsi_cmd_handlers[i].handler)
3376389eaa7SGavin Shan 				nch = &ncsi_cmd_handlers[i];
3386389eaa7SGavin Shan 			else
3396389eaa7SGavin Shan 				nch = NULL;
3406389eaa7SGavin Shan 
3416389eaa7SGavin Shan 			break;
3426389eaa7SGavin Shan 		}
3436389eaa7SGavin Shan 	}
3446389eaa7SGavin Shan 
3456389eaa7SGavin Shan 	if (!nch) {
3466389eaa7SGavin Shan 		netdev_err(nca->ndp->ndev.dev,
3476389eaa7SGavin Shan 			   "Cannot send packet with type 0x%02x\n", nca->type);
3486389eaa7SGavin Shan 		return -ENOENT;
3496389eaa7SGavin Shan 	}
3506389eaa7SGavin Shan 
351fb4ee675SVijay Khemka 	/* Get packet payload length and allocate the request
352fb4ee675SVijay Khemka 	 * It is expected that if length set as negative in
353fb4ee675SVijay Khemka 	 * handler structure means caller is initializing it
354fb4ee675SVijay Khemka 	 * and setting length in nca before calling xmit function
355fb4ee675SVijay Khemka 	 */
356fb4ee675SVijay Khemka 	if (nch->payload >= 0)
3576389eaa7SGavin Shan 		nca->payload = nch->payload;
3586389eaa7SGavin Shan 	nr = ncsi_alloc_command(nca);
3596389eaa7SGavin Shan 	if (!nr)
3606389eaa7SGavin Shan 		return -ENOMEM;
3616389eaa7SGavin Shan 
3629771b8ccSJustin.Lee1@Dell.com 	/* track netlink information */
3639771b8ccSJustin.Lee1@Dell.com 	if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
3649771b8ccSJustin.Lee1@Dell.com 		nr->snd_seq = nca->info->snd_seq;
3659771b8ccSJustin.Lee1@Dell.com 		nr->snd_portid = nca->info->snd_portid;
3669771b8ccSJustin.Lee1@Dell.com 		nr->nlhdr = *nca->info->nlhdr;
3679771b8ccSJustin.Lee1@Dell.com 	}
3689771b8ccSJustin.Lee1@Dell.com 
3696389eaa7SGavin Shan 	/* Prepare the packet */
3706389eaa7SGavin Shan 	nca->id = nr->id;
3716389eaa7SGavin Shan 	ret = nch->handler(nr->cmd, nca);
3726389eaa7SGavin Shan 	if (ret) {
3736389eaa7SGavin Shan 		ncsi_free_request(nr);
3746389eaa7SGavin Shan 		return ret;
3756389eaa7SGavin Shan 	}
3766389eaa7SGavin Shan 
3776389eaa7SGavin Shan 	/* Fill the ethernet header */
378d58ff351SJohannes Berg 	eh = skb_push(nr->cmd, sizeof(*eh));
3796389eaa7SGavin Shan 	eh->h_proto = htons(ETH_P_NCSI);
3806389eaa7SGavin Shan 	eth_broadcast_addr(eh->h_dest);
3817c7b58d4SVijay Khemka 
3827c7b58d4SVijay Khemka 	/* If mac address received from device then use it for
3837c7b58d4SVijay Khemka 	 * source address as unicast address else use broadcast
3847c7b58d4SVijay Khemka 	 * address as source address
3857c7b58d4SVijay Khemka 	 */
3867c7b58d4SVijay Khemka 	if (nca->ndp->gma_flag == 1)
3877c7b58d4SVijay Khemka 		memcpy(eh->h_source, nca->ndp->ndev.dev->dev_addr, ETH_ALEN);
3887c7b58d4SVijay Khemka 	else
3896389eaa7SGavin Shan 		eth_broadcast_addr(eh->h_source);
3906389eaa7SGavin Shan 
3916389eaa7SGavin Shan 	/* Start the timer for the request that might not have
3926389eaa7SGavin Shan 	 * corresponding response. Given NCSI is an internal
3936389eaa7SGavin Shan 	 * connection a 1 second delay should be sufficient.
3946389eaa7SGavin Shan 	 */
3956389eaa7SGavin Shan 	nr->enabled = true;
3966389eaa7SGavin Shan 	mod_timer(&nr->timer, jiffies + 1 * HZ);
3976389eaa7SGavin Shan 
3986389eaa7SGavin Shan 	/* Send NCSI packet */
3996389eaa7SGavin Shan 	skb_get(nr->cmd);
4006389eaa7SGavin Shan 	ret = dev_queue_xmit(nr->cmd);
4016389eaa7SGavin Shan 	if (ret < 0) {
4026389eaa7SGavin Shan 		ncsi_free_request(nr);
4036389eaa7SGavin Shan 		return ret;
4046389eaa7SGavin Shan 	}
4056389eaa7SGavin Shan 
4066389eaa7SGavin Shan 	return 0;
4076389eaa7SGavin Shan }
408