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