162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright Gavin Shan, IBM Corporation 2016.
462306a36Sopenharmony_ci */
562306a36Sopenharmony_ci
662306a36Sopenharmony_ci#include <linux/module.h>
762306a36Sopenharmony_ci#include <linux/kernel.h>
862306a36Sopenharmony_ci#include <linux/init.h>
962306a36Sopenharmony_ci#include <linux/etherdevice.h>
1062306a36Sopenharmony_ci#include <linux/netdevice.h>
1162306a36Sopenharmony_ci#include <linux/skbuff.h>
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <net/ncsi.h>
1462306a36Sopenharmony_ci#include <net/net_namespace.h>
1562306a36Sopenharmony_ci#include <net/sock.h>
1662306a36Sopenharmony_ci#include <net/genetlink.h>
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include "internal.h"
1962306a36Sopenharmony_ci#include "ncsi-pkt.h"
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_cistatic const int padding_bytes = 26;
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ciu32 ncsi_calculate_checksum(unsigned char *data, int len)
2462306a36Sopenharmony_ci{
2562306a36Sopenharmony_ci	u32 checksum = 0;
2662306a36Sopenharmony_ci	int i;
2762306a36Sopenharmony_ci
2862306a36Sopenharmony_ci	for (i = 0; i < len; i += 2)
2962306a36Sopenharmony_ci		checksum += (((u32)data[i] << 8) | data[i + 1]);
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	checksum = (~checksum + 1);
3262306a36Sopenharmony_ci	return checksum;
3362306a36Sopenharmony_ci}
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/* This function should be called after the data area has been
3662306a36Sopenharmony_ci * populated completely.
3762306a36Sopenharmony_ci */
3862306a36Sopenharmony_cistatic void ncsi_cmd_build_header(struct ncsi_pkt_hdr *h,
3962306a36Sopenharmony_ci				  struct ncsi_cmd_arg *nca)
4062306a36Sopenharmony_ci{
4162306a36Sopenharmony_ci	u32 checksum;
4262306a36Sopenharmony_ci	__be32 *pchecksum;
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	h->mc_id        = 0;
4562306a36Sopenharmony_ci	h->revision     = NCSI_PKT_REVISION;
4662306a36Sopenharmony_ci	h->reserved     = 0;
4762306a36Sopenharmony_ci	h->id           = nca->id;
4862306a36Sopenharmony_ci	h->type         = nca->type;
4962306a36Sopenharmony_ci	h->channel      = NCSI_TO_CHANNEL(nca->package,
5062306a36Sopenharmony_ci					  nca->channel);
5162306a36Sopenharmony_ci	h->length       = htons(nca->payload);
5262306a36Sopenharmony_ci	h->reserved1[0] = 0;
5362306a36Sopenharmony_ci	h->reserved1[1] = 0;
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci	/* Fill with calculated checksum */
5662306a36Sopenharmony_ci	checksum = ncsi_calculate_checksum((unsigned char *)h,
5762306a36Sopenharmony_ci					   sizeof(*h) + nca->payload);
5862306a36Sopenharmony_ci	pchecksum = (__be32 *)((void *)h + sizeof(struct ncsi_pkt_hdr) +
5962306a36Sopenharmony_ci		    ALIGN(nca->payload, 4));
6062306a36Sopenharmony_ci	*pchecksum = htonl(checksum);
6162306a36Sopenharmony_ci}
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_cistatic int ncsi_cmd_handler_default(struct sk_buff *skb,
6462306a36Sopenharmony_ci				    struct ncsi_cmd_arg *nca)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct ncsi_cmd_pkt *cmd;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
6962306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	return 0;
7262306a36Sopenharmony_ci}
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int ncsi_cmd_handler_sp(struct sk_buff *skb,
7562306a36Sopenharmony_ci			       struct ncsi_cmd_arg *nca)
7662306a36Sopenharmony_ci{
7762306a36Sopenharmony_ci	struct ncsi_cmd_sp_pkt *cmd;
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
8062306a36Sopenharmony_ci	cmd->hw_arbitration = nca->bytes[0];
8162306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic int ncsi_cmd_handler_dc(struct sk_buff *skb,
8762306a36Sopenharmony_ci			       struct ncsi_cmd_arg *nca)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	struct ncsi_cmd_dc_pkt *cmd;
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
9262306a36Sopenharmony_ci	cmd->ald = nca->bytes[0];
9362306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	return 0;
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_cistatic int ncsi_cmd_handler_rc(struct sk_buff *skb,
9962306a36Sopenharmony_ci			       struct ncsi_cmd_arg *nca)
10062306a36Sopenharmony_ci{
10162306a36Sopenharmony_ci	struct ncsi_cmd_rc_pkt *cmd;
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
10462306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	return 0;
10762306a36Sopenharmony_ci}
10862306a36Sopenharmony_ci
10962306a36Sopenharmony_cistatic int ncsi_cmd_handler_ae(struct sk_buff *skb,
11062306a36Sopenharmony_ci			       struct ncsi_cmd_arg *nca)
11162306a36Sopenharmony_ci{
11262306a36Sopenharmony_ci	struct ncsi_cmd_ae_pkt *cmd;
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
11562306a36Sopenharmony_ci	cmd->mc_id = nca->bytes[0];
11662306a36Sopenharmony_ci	cmd->mode = htonl(nca->dwords[1]);
11762306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_ci	return 0;
12062306a36Sopenharmony_ci}
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_cistatic int ncsi_cmd_handler_sl(struct sk_buff *skb,
12362306a36Sopenharmony_ci			       struct ncsi_cmd_arg *nca)
12462306a36Sopenharmony_ci{
12562306a36Sopenharmony_ci	struct ncsi_cmd_sl_pkt *cmd;
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
12862306a36Sopenharmony_ci	cmd->mode = htonl(nca->dwords[0]);
12962306a36Sopenharmony_ci	cmd->oem_mode = htonl(nca->dwords[1]);
13062306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	return 0;
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cistatic int ncsi_cmd_handler_svf(struct sk_buff *skb,
13662306a36Sopenharmony_ci				struct ncsi_cmd_arg *nca)
13762306a36Sopenharmony_ci{
13862306a36Sopenharmony_ci	struct ncsi_cmd_svf_pkt *cmd;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
14162306a36Sopenharmony_ci	cmd->vlan = htons(nca->words[1]);
14262306a36Sopenharmony_ci	cmd->index = nca->bytes[6];
14362306a36Sopenharmony_ci	cmd->enable = nca->bytes[7];
14462306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	return 0;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic int ncsi_cmd_handler_ev(struct sk_buff *skb,
15062306a36Sopenharmony_ci			       struct ncsi_cmd_arg *nca)
15162306a36Sopenharmony_ci{
15262306a36Sopenharmony_ci	struct ncsi_cmd_ev_pkt *cmd;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
15562306a36Sopenharmony_ci	cmd->mode = nca->bytes[3];
15662306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci	return 0;
15962306a36Sopenharmony_ci}
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_cistatic int ncsi_cmd_handler_sma(struct sk_buff *skb,
16262306a36Sopenharmony_ci				struct ncsi_cmd_arg *nca)
16362306a36Sopenharmony_ci{
16462306a36Sopenharmony_ci	struct ncsi_cmd_sma_pkt *cmd;
16562306a36Sopenharmony_ci	int i;
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
16862306a36Sopenharmony_ci	for (i = 0; i < 6; i++)
16962306a36Sopenharmony_ci		cmd->mac[i] = nca->bytes[i];
17062306a36Sopenharmony_ci	cmd->index = nca->bytes[6];
17162306a36Sopenharmony_ci	cmd->at_e = nca->bytes[7];
17262306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	return 0;
17562306a36Sopenharmony_ci}
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_cistatic int ncsi_cmd_handler_ebf(struct sk_buff *skb,
17862306a36Sopenharmony_ci				struct ncsi_cmd_arg *nca)
17962306a36Sopenharmony_ci{
18062306a36Sopenharmony_ci	struct ncsi_cmd_ebf_pkt *cmd;
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
18362306a36Sopenharmony_ci	cmd->mode = htonl(nca->dwords[0]);
18462306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
18562306a36Sopenharmony_ci
18662306a36Sopenharmony_ci	return 0;
18762306a36Sopenharmony_ci}
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_cistatic int ncsi_cmd_handler_egmf(struct sk_buff *skb,
19062306a36Sopenharmony_ci				 struct ncsi_cmd_arg *nca)
19162306a36Sopenharmony_ci{
19262306a36Sopenharmony_ci	struct ncsi_cmd_egmf_pkt *cmd;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
19562306a36Sopenharmony_ci	cmd->mode = htonl(nca->dwords[0]);
19662306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	return 0;
19962306a36Sopenharmony_ci}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int ncsi_cmd_handler_snfc(struct sk_buff *skb,
20262306a36Sopenharmony_ci				 struct ncsi_cmd_arg *nca)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	struct ncsi_cmd_snfc_pkt *cmd;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	cmd = skb_put_zero(skb, sizeof(*cmd));
20762306a36Sopenharmony_ci	cmd->mode = nca->bytes[0];
20862306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int ncsi_cmd_handler_oem(struct sk_buff *skb,
21462306a36Sopenharmony_ci				struct ncsi_cmd_arg *nca)
21562306a36Sopenharmony_ci{
21662306a36Sopenharmony_ci	struct ncsi_cmd_oem_pkt *cmd;
21762306a36Sopenharmony_ci	unsigned int len;
21862306a36Sopenharmony_ci	int payload;
21962306a36Sopenharmony_ci	/* NC-SI spec DSP_0222_1.2.0, section 8.2.2.2
22062306a36Sopenharmony_ci	 * requires payload to be padded with 0 to
22162306a36Sopenharmony_ci	 * 32-bit boundary before the checksum field.
22262306a36Sopenharmony_ci	 * Ensure the padding bytes are accounted for in
22362306a36Sopenharmony_ci	 * skb allocation
22462306a36Sopenharmony_ci	 */
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	payload = ALIGN(nca->payload, 4);
22762306a36Sopenharmony_ci	len = sizeof(struct ncsi_cmd_pkt_hdr) + 4;
22862306a36Sopenharmony_ci	len += max(payload, padding_bytes);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	cmd = skb_put_zero(skb, len);
23162306a36Sopenharmony_ci	unsafe_memcpy(&cmd->mfr_id, nca->data, nca->payload,
23262306a36Sopenharmony_ci		      /* skb allocated with enough to load the payload */);
23362306a36Sopenharmony_ci	ncsi_cmd_build_header(&cmd->cmd.common, nca);
23462306a36Sopenharmony_ci
23562306a36Sopenharmony_ci	return 0;
23662306a36Sopenharmony_ci}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_cistatic struct ncsi_cmd_handler {
23962306a36Sopenharmony_ci	unsigned char type;
24062306a36Sopenharmony_ci	int           payload;
24162306a36Sopenharmony_ci	int           (*handler)(struct sk_buff *skb,
24262306a36Sopenharmony_ci				 struct ncsi_cmd_arg *nca);
24362306a36Sopenharmony_ci} ncsi_cmd_handlers[] = {
24462306a36Sopenharmony_ci	{ NCSI_PKT_CMD_CIS,    0, ncsi_cmd_handler_default },
24562306a36Sopenharmony_ci	{ NCSI_PKT_CMD_SP,     4, ncsi_cmd_handler_sp      },
24662306a36Sopenharmony_ci	{ NCSI_PKT_CMD_DP,     0, ncsi_cmd_handler_default },
24762306a36Sopenharmony_ci	{ NCSI_PKT_CMD_EC,     0, ncsi_cmd_handler_default },
24862306a36Sopenharmony_ci	{ NCSI_PKT_CMD_DC,     4, ncsi_cmd_handler_dc      },
24962306a36Sopenharmony_ci	{ NCSI_PKT_CMD_RC,     4, ncsi_cmd_handler_rc      },
25062306a36Sopenharmony_ci	{ NCSI_PKT_CMD_ECNT,   0, ncsi_cmd_handler_default },
25162306a36Sopenharmony_ci	{ NCSI_PKT_CMD_DCNT,   0, ncsi_cmd_handler_default },
25262306a36Sopenharmony_ci	{ NCSI_PKT_CMD_AE,     8, ncsi_cmd_handler_ae      },
25362306a36Sopenharmony_ci	{ NCSI_PKT_CMD_SL,     8, ncsi_cmd_handler_sl      },
25462306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GLS,    0, ncsi_cmd_handler_default },
25562306a36Sopenharmony_ci	{ NCSI_PKT_CMD_SVF,    8, ncsi_cmd_handler_svf     },
25662306a36Sopenharmony_ci	{ NCSI_PKT_CMD_EV,     4, ncsi_cmd_handler_ev      },
25762306a36Sopenharmony_ci	{ NCSI_PKT_CMD_DV,     0, ncsi_cmd_handler_default },
25862306a36Sopenharmony_ci	{ NCSI_PKT_CMD_SMA,    8, ncsi_cmd_handler_sma     },
25962306a36Sopenharmony_ci	{ NCSI_PKT_CMD_EBF,    4, ncsi_cmd_handler_ebf     },
26062306a36Sopenharmony_ci	{ NCSI_PKT_CMD_DBF,    0, ncsi_cmd_handler_default },
26162306a36Sopenharmony_ci	{ NCSI_PKT_CMD_EGMF,   4, ncsi_cmd_handler_egmf    },
26262306a36Sopenharmony_ci	{ NCSI_PKT_CMD_DGMF,   0, ncsi_cmd_handler_default },
26362306a36Sopenharmony_ci	{ NCSI_PKT_CMD_SNFC,   4, ncsi_cmd_handler_snfc    },
26462306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GVI,    0, ncsi_cmd_handler_default },
26562306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GC,     0, ncsi_cmd_handler_default },
26662306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GP,     0, ncsi_cmd_handler_default },
26762306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GCPS,   0, ncsi_cmd_handler_default },
26862306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GNS,    0, ncsi_cmd_handler_default },
26962306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GNPTS,  0, ncsi_cmd_handler_default },
27062306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GPS,    0, ncsi_cmd_handler_default },
27162306a36Sopenharmony_ci	{ NCSI_PKT_CMD_OEM,   -1, ncsi_cmd_handler_oem     },
27262306a36Sopenharmony_ci	{ NCSI_PKT_CMD_PLDM,   0, NULL                     },
27362306a36Sopenharmony_ci	{ NCSI_PKT_CMD_GPUUID, 0, ncsi_cmd_handler_default }
27462306a36Sopenharmony_ci};
27562306a36Sopenharmony_ci
27662306a36Sopenharmony_cistatic struct ncsi_request *ncsi_alloc_command(struct ncsi_cmd_arg *nca)
27762306a36Sopenharmony_ci{
27862306a36Sopenharmony_ci	struct ncsi_dev_priv *ndp = nca->ndp;
27962306a36Sopenharmony_ci	struct ncsi_dev *nd = &ndp->ndev;
28062306a36Sopenharmony_ci	struct net_device *dev = nd->dev;
28162306a36Sopenharmony_ci	int hlen = LL_RESERVED_SPACE(dev);
28262306a36Sopenharmony_ci	int tlen = dev->needed_tailroom;
28362306a36Sopenharmony_ci	int payload;
28462306a36Sopenharmony_ci	int len = hlen + tlen;
28562306a36Sopenharmony_ci	struct sk_buff *skb;
28662306a36Sopenharmony_ci	struct ncsi_request *nr;
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci	nr = ncsi_alloc_request(ndp, nca->req_flags);
28962306a36Sopenharmony_ci	if (!nr)
29062306a36Sopenharmony_ci		return NULL;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	/* NCSI command packet has 16-bytes header, payload, 4 bytes checksum.
29362306a36Sopenharmony_ci	 * Payload needs padding so that the checksum field following payload is
29462306a36Sopenharmony_ci	 * aligned to 32-bit boundary.
29562306a36Sopenharmony_ci	 * The packet needs padding if its payload is less than 26 bytes to
29662306a36Sopenharmony_ci	 * meet 64 bytes minimal ethernet frame length.
29762306a36Sopenharmony_ci	 */
29862306a36Sopenharmony_ci	len += sizeof(struct ncsi_cmd_pkt_hdr) + 4;
29962306a36Sopenharmony_ci	payload = ALIGN(nca->payload, 4);
30062306a36Sopenharmony_ci	len += max(payload, padding_bytes);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	/* Allocate skb */
30362306a36Sopenharmony_ci	skb = alloc_skb(len, GFP_ATOMIC);
30462306a36Sopenharmony_ci	if (!skb) {
30562306a36Sopenharmony_ci		ncsi_free_request(nr);
30662306a36Sopenharmony_ci		return NULL;
30762306a36Sopenharmony_ci	}
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	nr->cmd = skb;
31062306a36Sopenharmony_ci	skb_reserve(skb, hlen);
31162306a36Sopenharmony_ci	skb_reset_network_header(skb);
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	skb->dev = dev;
31462306a36Sopenharmony_ci	skb->protocol = htons(ETH_P_NCSI);
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	return nr;
31762306a36Sopenharmony_ci}
31862306a36Sopenharmony_ci
31962306a36Sopenharmony_ciint ncsi_xmit_cmd(struct ncsi_cmd_arg *nca)
32062306a36Sopenharmony_ci{
32162306a36Sopenharmony_ci	struct ncsi_cmd_handler *nch = NULL;
32262306a36Sopenharmony_ci	struct ncsi_request *nr;
32362306a36Sopenharmony_ci	unsigned char type;
32462306a36Sopenharmony_ci	struct ethhdr *eh;
32562306a36Sopenharmony_ci	int i, ret;
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_ci	/* Use OEM generic handler for Netlink request */
32862306a36Sopenharmony_ci	if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN)
32962306a36Sopenharmony_ci		type = NCSI_PKT_CMD_OEM;
33062306a36Sopenharmony_ci	else
33162306a36Sopenharmony_ci		type = nca->type;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/* Search for the handler */
33462306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(ncsi_cmd_handlers); i++) {
33562306a36Sopenharmony_ci		if (ncsi_cmd_handlers[i].type == type) {
33662306a36Sopenharmony_ci			if (ncsi_cmd_handlers[i].handler)
33762306a36Sopenharmony_ci				nch = &ncsi_cmd_handlers[i];
33862306a36Sopenharmony_ci			else
33962306a36Sopenharmony_ci				nch = NULL;
34062306a36Sopenharmony_ci
34162306a36Sopenharmony_ci			break;
34262306a36Sopenharmony_ci		}
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	if (!nch) {
34662306a36Sopenharmony_ci		netdev_err(nca->ndp->ndev.dev,
34762306a36Sopenharmony_ci			   "Cannot send packet with type 0x%02x\n", nca->type);
34862306a36Sopenharmony_ci		return -ENOENT;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci	/* Get packet payload length and allocate the request
35262306a36Sopenharmony_ci	 * It is expected that if length set as negative in
35362306a36Sopenharmony_ci	 * handler structure means caller is initializing it
35462306a36Sopenharmony_ci	 * and setting length in nca before calling xmit function
35562306a36Sopenharmony_ci	 */
35662306a36Sopenharmony_ci	if (nch->payload >= 0)
35762306a36Sopenharmony_ci		nca->payload = nch->payload;
35862306a36Sopenharmony_ci	nr = ncsi_alloc_command(nca);
35962306a36Sopenharmony_ci	if (!nr)
36062306a36Sopenharmony_ci		return -ENOMEM;
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci	/* track netlink information */
36362306a36Sopenharmony_ci	if (nca->req_flags == NCSI_REQ_FLAG_NETLINK_DRIVEN) {
36462306a36Sopenharmony_ci		nr->snd_seq = nca->info->snd_seq;
36562306a36Sopenharmony_ci		nr->snd_portid = nca->info->snd_portid;
36662306a36Sopenharmony_ci		nr->nlhdr = *nca->info->nlhdr;
36762306a36Sopenharmony_ci	}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	/* Prepare the packet */
37062306a36Sopenharmony_ci	nca->id = nr->id;
37162306a36Sopenharmony_ci	ret = nch->handler(nr->cmd, nca);
37262306a36Sopenharmony_ci	if (ret) {
37362306a36Sopenharmony_ci		ncsi_free_request(nr);
37462306a36Sopenharmony_ci		return ret;
37562306a36Sopenharmony_ci	}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_ci	/* Fill the ethernet header */
37862306a36Sopenharmony_ci	eh = skb_push(nr->cmd, sizeof(*eh));
37962306a36Sopenharmony_ci	eh->h_proto = htons(ETH_P_NCSI);
38062306a36Sopenharmony_ci	eth_broadcast_addr(eh->h_dest);
38162306a36Sopenharmony_ci
38262306a36Sopenharmony_ci	/* If mac address received from device then use it for
38362306a36Sopenharmony_ci	 * source address as unicast address else use broadcast
38462306a36Sopenharmony_ci	 * address as source address
38562306a36Sopenharmony_ci	 */
38662306a36Sopenharmony_ci	if (nca->ndp->gma_flag == 1)
38762306a36Sopenharmony_ci		memcpy(eh->h_source, nca->ndp->ndev.dev->dev_addr, ETH_ALEN);
38862306a36Sopenharmony_ci	else
38962306a36Sopenharmony_ci		eth_broadcast_addr(eh->h_source);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci	/* Start the timer for the request that might not have
39262306a36Sopenharmony_ci	 * corresponding response. Given NCSI is an internal
39362306a36Sopenharmony_ci	 * connection a 1 second delay should be sufficient.
39462306a36Sopenharmony_ci	 */
39562306a36Sopenharmony_ci	nr->enabled = true;
39662306a36Sopenharmony_ci	mod_timer(&nr->timer, jiffies + 1 * HZ);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	/* Send NCSI packet */
39962306a36Sopenharmony_ci	skb_get(nr->cmd);
40062306a36Sopenharmony_ci	ret = dev_queue_xmit(nr->cmd);
40162306a36Sopenharmony_ci	if (ret < 0) {
40262306a36Sopenharmony_ci		ncsi_free_request(nr);
40362306a36Sopenharmony_ci		return ret;
40462306a36Sopenharmony_ci	}
40562306a36Sopenharmony_ci
40662306a36Sopenharmony_ci	return 0;
40762306a36Sopenharmony_ci}
408