xref: /openbmc/linux/net/ncsi/ncsi-rsp.c (revision 21acf63013ed3d6fce3176cc34b74064052a31b4)
1138635ccSGavin Shan /*
2138635ccSGavin Shan  * Copyright Gavin Shan, IBM Corporation 2016.
3138635ccSGavin Shan  *
4138635ccSGavin Shan  * This program is free software; you can redistribute it and/or modify
5138635ccSGavin Shan  * it under the terms of the GNU General Public License as published by
6138635ccSGavin Shan  * the Free Software Foundation; either version 2 of the License, or
7138635ccSGavin Shan  * (at your option) any later version.
8138635ccSGavin Shan  */
9138635ccSGavin Shan 
10138635ccSGavin Shan #include <linux/module.h>
11138635ccSGavin Shan #include <linux/kernel.h>
12138635ccSGavin Shan #include <linux/init.h>
13138635ccSGavin Shan #include <linux/netdevice.h>
14138635ccSGavin Shan #include <linux/skbuff.h>
15138635ccSGavin Shan 
16138635ccSGavin Shan #include <net/ncsi.h>
17138635ccSGavin Shan #include <net/net_namespace.h>
18138635ccSGavin Shan #include <net/sock.h>
19138635ccSGavin Shan 
20138635ccSGavin Shan #include "internal.h"
21138635ccSGavin Shan #include "ncsi-pkt.h"
22138635ccSGavin Shan 
23138635ccSGavin Shan static int ncsi_validate_rsp_pkt(struct ncsi_request *nr,
24138635ccSGavin Shan 				 unsigned short payload)
25138635ccSGavin Shan {
26138635ccSGavin Shan 	struct ncsi_rsp_pkt_hdr *h;
27138635ccSGavin Shan 	u32 checksum;
28138635ccSGavin Shan 	__be32 *pchecksum;
29138635ccSGavin Shan 
30138635ccSGavin Shan 	/* Check NCSI packet header. We don't need validate
31138635ccSGavin Shan 	 * the packet type, which should have been checked
32138635ccSGavin Shan 	 * before calling this function.
33138635ccSGavin Shan 	 */
34138635ccSGavin Shan 	h = (struct ncsi_rsp_pkt_hdr *)skb_network_header(nr->rsp);
35138635ccSGavin Shan 	if (h->common.revision != NCSI_PKT_REVISION)
36138635ccSGavin Shan 		return -EINVAL;
37138635ccSGavin Shan 	if (ntohs(h->common.length) != payload)
38138635ccSGavin Shan 		return -EINVAL;
39138635ccSGavin Shan 
40138635ccSGavin Shan 	/* Check on code and reason */
41138635ccSGavin Shan 	if (ntohs(h->code) != NCSI_PKT_RSP_C_COMPLETED ||
42138635ccSGavin Shan 	    ntohs(h->reason) != NCSI_PKT_RSP_R_NO_ERROR)
43138635ccSGavin Shan 		return -EINVAL;
44138635ccSGavin Shan 
45138635ccSGavin Shan 	/* Validate checksum, which might be zeroes if the
46138635ccSGavin Shan 	 * sender doesn't support checksum according to NCSI
47138635ccSGavin Shan 	 * specification.
48138635ccSGavin Shan 	 */
49138635ccSGavin Shan 	pchecksum = (__be32 *)((void *)(h + 1) + payload - 4);
50138635ccSGavin Shan 	if (ntohl(*pchecksum) == 0)
51138635ccSGavin Shan 		return 0;
52138635ccSGavin Shan 
53138635ccSGavin Shan 	checksum = ncsi_calculate_checksum((unsigned char *)h,
54138635ccSGavin Shan 					   sizeof(*h) + payload - 4);
55138635ccSGavin Shan 	if (*pchecksum != htonl(checksum))
56138635ccSGavin Shan 		return -EINVAL;
57138635ccSGavin Shan 
58138635ccSGavin Shan 	return 0;
59138635ccSGavin Shan }
60138635ccSGavin Shan 
61138635ccSGavin Shan static int ncsi_rsp_handler_cis(struct ncsi_request *nr)
62138635ccSGavin Shan {
63138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
64138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
65138635ccSGavin Shan 	struct ncsi_package *np;
66138635ccSGavin Shan 	struct ncsi_channel *nc;
67138635ccSGavin Shan 	unsigned char id;
68138635ccSGavin Shan 
69138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
70138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, &np, &nc);
71138635ccSGavin Shan 	if (!nc) {
72e6f44ed6SGavin Shan 		if (ndp->flags & NCSI_DEV_PROBED)
73e6f44ed6SGavin Shan 			return -ENXIO;
74e6f44ed6SGavin Shan 
75138635ccSGavin Shan 		id = NCSI_CHANNEL_INDEX(rsp->rsp.common.channel);
76138635ccSGavin Shan 		nc = ncsi_add_channel(np, id);
77138635ccSGavin Shan 	}
78138635ccSGavin Shan 
79138635ccSGavin Shan 	return nc ? 0 : -ENODEV;
80138635ccSGavin Shan }
81138635ccSGavin Shan 
82138635ccSGavin Shan static int ncsi_rsp_handler_sp(struct ncsi_request *nr)
83138635ccSGavin Shan {
84138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
85138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
86138635ccSGavin Shan 	struct ncsi_package *np;
87138635ccSGavin Shan 	unsigned char id;
88138635ccSGavin Shan 
89138635ccSGavin Shan 	/* Add the package if it's not existing. Otherwise,
90138635ccSGavin Shan 	 * to change the state of its child channels.
91138635ccSGavin Shan 	 */
92138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
93138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
94138635ccSGavin Shan 				      &np, NULL);
95138635ccSGavin Shan 	if (!np) {
96e6f44ed6SGavin Shan 		if (ndp->flags & NCSI_DEV_PROBED)
97e6f44ed6SGavin Shan 			return -ENXIO;
98e6f44ed6SGavin Shan 
99138635ccSGavin Shan 		id = NCSI_PACKAGE_INDEX(rsp->rsp.common.channel);
100138635ccSGavin Shan 		np = ncsi_add_package(ndp, id);
101138635ccSGavin Shan 		if (!np)
102138635ccSGavin Shan 			return -ENODEV;
103138635ccSGavin Shan 	}
104138635ccSGavin Shan 
105138635ccSGavin Shan 	return 0;
106138635ccSGavin Shan }
107138635ccSGavin Shan 
108138635ccSGavin Shan static int ncsi_rsp_handler_dp(struct ncsi_request *nr)
109138635ccSGavin Shan {
110138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
111138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
112138635ccSGavin Shan 	struct ncsi_package *np;
113138635ccSGavin Shan 	struct ncsi_channel *nc;
114138635ccSGavin Shan 	unsigned long flags;
115138635ccSGavin Shan 
116138635ccSGavin Shan 	/* Find the package */
117138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
118138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
119138635ccSGavin Shan 				      &np, NULL);
120138635ccSGavin Shan 	if (!np)
121138635ccSGavin Shan 		return -ENODEV;
122138635ccSGavin Shan 
123138635ccSGavin Shan 	/* Change state of all channels attached to the package */
124138635ccSGavin Shan 	NCSI_FOR_EACH_CHANNEL(np, nc) {
125138635ccSGavin Shan 		spin_lock_irqsave(&nc->lock, flags);
126138635ccSGavin Shan 		nc->state = NCSI_CHANNEL_INACTIVE;
127138635ccSGavin Shan 		spin_unlock_irqrestore(&nc->lock, flags);
128138635ccSGavin Shan 	}
129138635ccSGavin Shan 
130138635ccSGavin Shan 	return 0;
131138635ccSGavin Shan }
132138635ccSGavin Shan 
133138635ccSGavin Shan static int ncsi_rsp_handler_ec(struct ncsi_request *nr)
134138635ccSGavin Shan {
135138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
136138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
137138635ccSGavin Shan 	struct ncsi_channel *nc;
138138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
139138635ccSGavin Shan 
140138635ccSGavin Shan 	/* Find the package and channel */
141138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
142138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
143138635ccSGavin Shan 				      NULL, &nc);
144138635ccSGavin Shan 	if (!nc)
145138635ccSGavin Shan 		return -ENODEV;
146138635ccSGavin Shan 
147138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_ENABLE];
148138635ccSGavin Shan 	if (ncm->enable)
149138635ccSGavin Shan 		return -EBUSY;
150138635ccSGavin Shan 
151138635ccSGavin Shan 	ncm->enable = 1;
152138635ccSGavin Shan 	return 0;
153138635ccSGavin Shan }
154138635ccSGavin Shan 
155138635ccSGavin Shan static int ncsi_rsp_handler_dc(struct ncsi_request *nr)
156138635ccSGavin Shan {
157138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
158138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
159138635ccSGavin Shan 	struct ncsi_channel *nc;
160138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
161138635ccSGavin Shan 	int ret;
162138635ccSGavin Shan 
163138635ccSGavin Shan 	ret = ncsi_validate_rsp_pkt(nr, 4);
164138635ccSGavin Shan 	if (ret)
165138635ccSGavin Shan 		return ret;
166138635ccSGavin Shan 
167138635ccSGavin Shan 	/* Find the package and channel */
168138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
169138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
170138635ccSGavin Shan 				      NULL, &nc);
171138635ccSGavin Shan 	if (!nc)
172138635ccSGavin Shan 		return -ENODEV;
173138635ccSGavin Shan 
174138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_ENABLE];
175138635ccSGavin Shan 	if (!ncm->enable)
176138635ccSGavin Shan 		return -EBUSY;
177138635ccSGavin Shan 
178138635ccSGavin Shan 	ncm->enable = 0;
179138635ccSGavin Shan 	return 0;
180138635ccSGavin Shan }
181138635ccSGavin Shan 
182138635ccSGavin Shan static int ncsi_rsp_handler_rc(struct ncsi_request *nr)
183138635ccSGavin Shan {
184138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
185138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
186138635ccSGavin Shan 	struct ncsi_channel *nc;
187138635ccSGavin Shan 	unsigned long flags;
188138635ccSGavin Shan 
189138635ccSGavin Shan 	/* Find the package and channel */
190138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
191138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
192138635ccSGavin Shan 				      NULL, &nc);
193138635ccSGavin Shan 	if (!nc)
194138635ccSGavin Shan 		return -ENODEV;
195138635ccSGavin Shan 
196138635ccSGavin Shan 	/* Update state for the specified channel */
197138635ccSGavin Shan 	spin_lock_irqsave(&nc->lock, flags);
198138635ccSGavin Shan 	nc->state = NCSI_CHANNEL_INACTIVE;
199138635ccSGavin Shan 	spin_unlock_irqrestore(&nc->lock, flags);
200138635ccSGavin Shan 
201138635ccSGavin Shan 	return 0;
202138635ccSGavin Shan }
203138635ccSGavin Shan 
204138635ccSGavin Shan static int ncsi_rsp_handler_ecnt(struct ncsi_request *nr)
205138635ccSGavin Shan {
206138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
207138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
208138635ccSGavin Shan 	struct ncsi_channel *nc;
209138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
210138635ccSGavin Shan 
211138635ccSGavin Shan 	/* Find the package and channel */
212138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
213138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
214138635ccSGavin Shan 				      NULL, &nc);
215138635ccSGavin Shan 	if (!nc)
216138635ccSGavin Shan 		return -ENODEV;
217138635ccSGavin Shan 
218138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
219138635ccSGavin Shan 	if (ncm->enable)
220138635ccSGavin Shan 		return -EBUSY;
221138635ccSGavin Shan 
222138635ccSGavin Shan 	ncm->enable = 1;
223138635ccSGavin Shan 	return 0;
224138635ccSGavin Shan }
225138635ccSGavin Shan 
226138635ccSGavin Shan static int ncsi_rsp_handler_dcnt(struct ncsi_request *nr)
227138635ccSGavin Shan {
228138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
229138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
230138635ccSGavin Shan 	struct ncsi_channel *nc;
231138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
232138635ccSGavin Shan 
233138635ccSGavin Shan 	/* Find the package and channel */
234138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
235138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
236138635ccSGavin Shan 				      NULL, &nc);
237138635ccSGavin Shan 	if (!nc)
238138635ccSGavin Shan 		return -ENODEV;
239138635ccSGavin Shan 
240138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_TX_ENABLE];
241138635ccSGavin Shan 	if (!ncm->enable)
242138635ccSGavin Shan 		return -EBUSY;
243138635ccSGavin Shan 
244138635ccSGavin Shan 	ncm->enable = 1;
245138635ccSGavin Shan 	return 0;
246138635ccSGavin Shan }
247138635ccSGavin Shan 
248138635ccSGavin Shan static int ncsi_rsp_handler_ae(struct ncsi_request *nr)
249138635ccSGavin Shan {
250138635ccSGavin Shan 	struct ncsi_cmd_ae_pkt *cmd;
251138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
252138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
253138635ccSGavin Shan 	struct ncsi_channel *nc;
254138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
255138635ccSGavin Shan 
256138635ccSGavin Shan 	/* Find the package and channel */
257138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
258138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
259138635ccSGavin Shan 				      NULL, &nc);
260138635ccSGavin Shan 	if (!nc)
261138635ccSGavin Shan 		return -ENODEV;
262138635ccSGavin Shan 
263138635ccSGavin Shan 	/* Check if the AEN has been enabled */
264138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_AEN];
265138635ccSGavin Shan 	if (ncm->enable)
266138635ccSGavin Shan 		return -EBUSY;
267138635ccSGavin Shan 
268138635ccSGavin Shan 	/* Update to AEN configuration */
269138635ccSGavin Shan 	cmd = (struct ncsi_cmd_ae_pkt *)skb_network_header(nr->cmd);
270138635ccSGavin Shan 	ncm->enable = 1;
271138635ccSGavin Shan 	ncm->data[0] = cmd->mc_id;
272138635ccSGavin Shan 	ncm->data[1] = ntohl(cmd->mode);
273138635ccSGavin Shan 
274138635ccSGavin Shan 	return 0;
275138635ccSGavin Shan }
276138635ccSGavin Shan 
277138635ccSGavin Shan static int ncsi_rsp_handler_sl(struct ncsi_request *nr)
278138635ccSGavin Shan {
279138635ccSGavin Shan 	struct ncsi_cmd_sl_pkt *cmd;
280138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
281138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
282138635ccSGavin Shan 	struct ncsi_channel *nc;
283138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
284138635ccSGavin Shan 
285138635ccSGavin Shan 	/* Find the package and channel */
286138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
287138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
288138635ccSGavin Shan 				      NULL, &nc);
289138635ccSGavin Shan 	if (!nc)
290138635ccSGavin Shan 		return -ENODEV;
291138635ccSGavin Shan 
292138635ccSGavin Shan 	cmd = (struct ncsi_cmd_sl_pkt *)skb_network_header(nr->cmd);
293138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_LINK];
294138635ccSGavin Shan 	ncm->data[0] = ntohl(cmd->mode);
295138635ccSGavin Shan 	ncm->data[1] = ntohl(cmd->oem_mode);
296138635ccSGavin Shan 
297138635ccSGavin Shan 	return 0;
298138635ccSGavin Shan }
299138635ccSGavin Shan 
300138635ccSGavin Shan static int ncsi_rsp_handler_gls(struct ncsi_request *nr)
301138635ccSGavin Shan {
302138635ccSGavin Shan 	struct ncsi_rsp_gls_pkt *rsp;
303138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
304138635ccSGavin Shan 	struct ncsi_channel *nc;
305138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
306e6f44ed6SGavin Shan 	unsigned long flags;
307138635ccSGavin Shan 
308138635ccSGavin Shan 	/* Find the package and channel */
309138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gls_pkt *)skb_network_header(nr->rsp);
310138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
311138635ccSGavin Shan 				      NULL, &nc);
312138635ccSGavin Shan 	if (!nc)
313138635ccSGavin Shan 		return -ENODEV;
314138635ccSGavin Shan 
315138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_LINK];
316138635ccSGavin Shan 	ncm->data[2] = ntohl(rsp->status);
317138635ccSGavin Shan 	ncm->data[3] = ntohl(rsp->other);
318138635ccSGavin Shan 	ncm->data[4] = ntohl(rsp->oem_status);
319138635ccSGavin Shan 
320a0509cbeSGavin Shan 	if (nr->flags & NCSI_REQ_FLAG_EVENT_DRIVEN)
321e6f44ed6SGavin Shan 		return 0;
322e6f44ed6SGavin Shan 
323e6f44ed6SGavin Shan 	/* Reset the channel monitor if it has been enabled */
324e6f44ed6SGavin Shan 	spin_lock_irqsave(&nc->lock, flags);
32583afdc6aSGavin Shan 	nc->monitor.state = NCSI_CHANNEL_MONITOR_START;
326e6f44ed6SGavin Shan 	spin_unlock_irqrestore(&nc->lock, flags);
327e6f44ed6SGavin Shan 
328138635ccSGavin Shan 	return 0;
329138635ccSGavin Shan }
330138635ccSGavin Shan 
331138635ccSGavin Shan static int ncsi_rsp_handler_svf(struct ncsi_request *nr)
332138635ccSGavin Shan {
333138635ccSGavin Shan 	struct ncsi_cmd_svf_pkt *cmd;
334138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
335138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
336138635ccSGavin Shan 	struct ncsi_channel *nc;
337138635ccSGavin Shan 	struct ncsi_channel_filter *ncf;
338138635ccSGavin Shan 	unsigned short vlan;
339138635ccSGavin Shan 	int ret;
340138635ccSGavin Shan 
341138635ccSGavin Shan 	/* Find the package and channel */
342138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
343138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
344138635ccSGavin Shan 				      NULL, &nc);
345138635ccSGavin Shan 	if (!nc)
346138635ccSGavin Shan 		return -ENODEV;
347138635ccSGavin Shan 
348138635ccSGavin Shan 	cmd = (struct ncsi_cmd_svf_pkt *)skb_network_header(nr->cmd);
349138635ccSGavin Shan 	ncf = nc->filters[NCSI_FILTER_VLAN];
350138635ccSGavin Shan 	if (!ncf)
351138635ccSGavin Shan 		return -ENOENT;
352138635ccSGavin Shan 	if (cmd->index >= ncf->total)
353138635ccSGavin Shan 		return -ERANGE;
354138635ccSGavin Shan 
355138635ccSGavin Shan 	/* Add or remove the VLAN filter */
356138635ccSGavin Shan 	if (!(cmd->enable & 0x1)) {
3578579a67eSSamuel Mendoza-Jonas 		/* HW indexes from 1 */
3588579a67eSSamuel Mendoza-Jonas 		ret = ncsi_remove_filter(nc, NCSI_FILTER_VLAN, cmd->index - 1);
359138635ccSGavin Shan 	} else {
360138635ccSGavin Shan 		vlan = ntohs(cmd->vlan);
361138635ccSGavin Shan 		ret = ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
362138635ccSGavin Shan 	}
363138635ccSGavin Shan 
364138635ccSGavin Shan 	return ret;
365138635ccSGavin Shan }
366138635ccSGavin Shan 
367138635ccSGavin Shan static int ncsi_rsp_handler_ev(struct ncsi_request *nr)
368138635ccSGavin Shan {
369138635ccSGavin Shan 	struct ncsi_cmd_ev_pkt *cmd;
370138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
371138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
372138635ccSGavin Shan 	struct ncsi_channel *nc;
373138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
374138635ccSGavin Shan 
375138635ccSGavin Shan 	/* Find the package and channel */
376138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
377138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
378138635ccSGavin Shan 				      NULL, &nc);
379138635ccSGavin Shan 	if (!nc)
380138635ccSGavin Shan 		return -ENODEV;
381138635ccSGavin Shan 
382138635ccSGavin Shan 	/* Check if VLAN mode has been enabled */
383138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_VLAN];
384138635ccSGavin Shan 	if (ncm->enable)
385138635ccSGavin Shan 		return -EBUSY;
386138635ccSGavin Shan 
387138635ccSGavin Shan 	/* Update to VLAN mode */
388138635ccSGavin Shan 	cmd = (struct ncsi_cmd_ev_pkt *)skb_network_header(nr->cmd);
389138635ccSGavin Shan 	ncm->enable = 1;
390138635ccSGavin Shan 	ncm->data[0] = ntohl(cmd->mode);
391138635ccSGavin Shan 
392138635ccSGavin Shan 	return 0;
393138635ccSGavin Shan }
394138635ccSGavin Shan 
395138635ccSGavin Shan static int ncsi_rsp_handler_dv(struct ncsi_request *nr)
396138635ccSGavin Shan {
397138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
398138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
399138635ccSGavin Shan 	struct ncsi_channel *nc;
400138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
401138635ccSGavin Shan 
402138635ccSGavin Shan 	/* Find the package and channel */
403138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
404138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
405138635ccSGavin Shan 				      NULL, &nc);
406138635ccSGavin Shan 	if (!nc)
407138635ccSGavin Shan 		return -ENODEV;
408138635ccSGavin Shan 
409138635ccSGavin Shan 	/* Check if VLAN mode has been enabled */
410138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_VLAN];
411138635ccSGavin Shan 	if (!ncm->enable)
412138635ccSGavin Shan 		return -EBUSY;
413138635ccSGavin Shan 
414138635ccSGavin Shan 	/* Update to VLAN mode */
415138635ccSGavin Shan 	ncm->enable = 0;
416138635ccSGavin Shan 	return 0;
417138635ccSGavin Shan }
418138635ccSGavin Shan 
419138635ccSGavin Shan static int ncsi_rsp_handler_sma(struct ncsi_request *nr)
420138635ccSGavin Shan {
421138635ccSGavin Shan 	struct ncsi_cmd_sma_pkt *cmd;
422138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
423138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
424138635ccSGavin Shan 	struct ncsi_channel *nc;
425138635ccSGavin Shan 	struct ncsi_channel_filter *ncf;
426138635ccSGavin Shan 	void *bitmap;
427138635ccSGavin Shan 
428138635ccSGavin Shan 	/* Find the package and channel */
429138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
430138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
431138635ccSGavin Shan 				      NULL, &nc);
432138635ccSGavin Shan 	if (!nc)
433138635ccSGavin Shan 		return -ENODEV;
434138635ccSGavin Shan 
435138635ccSGavin Shan 	/* According to NCSI spec 1.01, the mixed filter table
436138635ccSGavin Shan 	 * isn't supported yet.
437138635ccSGavin Shan 	 */
438138635ccSGavin Shan 	cmd = (struct ncsi_cmd_sma_pkt *)skb_network_header(nr->cmd);
439138635ccSGavin Shan 	switch (cmd->at_e >> 5) {
440138635ccSGavin Shan 	case 0x0:	/* UC address */
441138635ccSGavin Shan 		ncf = nc->filters[NCSI_FILTER_UC];
442138635ccSGavin Shan 		break;
443138635ccSGavin Shan 	case 0x1:	/* MC address */
444138635ccSGavin Shan 		ncf = nc->filters[NCSI_FILTER_MC];
445138635ccSGavin Shan 		break;
446138635ccSGavin Shan 	default:
447138635ccSGavin Shan 		return -EINVAL;
448138635ccSGavin Shan 	}
449138635ccSGavin Shan 
450138635ccSGavin Shan 	/* Sanity check on the filter */
451138635ccSGavin Shan 	if (!ncf)
452138635ccSGavin Shan 		return -ENOENT;
453138635ccSGavin Shan 	else if (cmd->index >= ncf->total)
454138635ccSGavin Shan 		return -ERANGE;
455138635ccSGavin Shan 
456138635ccSGavin Shan 	bitmap = &ncf->bitmap;
457138635ccSGavin Shan 	if (cmd->at_e & 0x1) {
458138635ccSGavin Shan 		if (test_and_set_bit(cmd->index, bitmap))
459138635ccSGavin Shan 			return -EBUSY;
460138635ccSGavin Shan 		memcpy(ncf->data + 6 * cmd->index, cmd->mac, 6);
461138635ccSGavin Shan 	} else {
462138635ccSGavin Shan 		if (!test_and_clear_bit(cmd->index, bitmap))
463138635ccSGavin Shan 			return -EBUSY;
464138635ccSGavin Shan 
465138635ccSGavin Shan 		memset(ncf->data + 6 * cmd->index, 0, 6);
466138635ccSGavin Shan 	}
467138635ccSGavin Shan 
468138635ccSGavin Shan 	return 0;
469138635ccSGavin Shan }
470138635ccSGavin Shan 
471138635ccSGavin Shan static int ncsi_rsp_handler_ebf(struct ncsi_request *nr)
472138635ccSGavin Shan {
473138635ccSGavin Shan 	struct ncsi_cmd_ebf_pkt *cmd;
474138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
475138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
476138635ccSGavin Shan 	struct ncsi_channel *nc;
477138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
478138635ccSGavin Shan 
479138635ccSGavin Shan 	/* Find the package and channel */
480138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
481138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel, NULL, &nc);
482138635ccSGavin Shan 	if (!nc)
483138635ccSGavin Shan 		return -ENODEV;
484138635ccSGavin Shan 
485138635ccSGavin Shan 	/* Check if broadcast filter has been enabled */
486138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_BC];
487138635ccSGavin Shan 	if (ncm->enable)
488138635ccSGavin Shan 		return -EBUSY;
489138635ccSGavin Shan 
490138635ccSGavin Shan 	/* Update to broadcast filter mode */
491138635ccSGavin Shan 	cmd = (struct ncsi_cmd_ebf_pkt *)skb_network_header(nr->cmd);
492138635ccSGavin Shan 	ncm->enable = 1;
493138635ccSGavin Shan 	ncm->data[0] = ntohl(cmd->mode);
494138635ccSGavin Shan 
495138635ccSGavin Shan 	return 0;
496138635ccSGavin Shan }
497138635ccSGavin Shan 
498138635ccSGavin Shan static int ncsi_rsp_handler_dbf(struct ncsi_request *nr)
499138635ccSGavin Shan {
500138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
501138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
502138635ccSGavin Shan 	struct ncsi_channel *nc;
503138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
504138635ccSGavin Shan 
505138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
506138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
507138635ccSGavin Shan 				      NULL, &nc);
508138635ccSGavin Shan 	if (!nc)
509138635ccSGavin Shan 		return -ENODEV;
510138635ccSGavin Shan 
511138635ccSGavin Shan 	/* Check if broadcast filter isn't enabled */
512138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_BC];
513138635ccSGavin Shan 	if (!ncm->enable)
514138635ccSGavin Shan 		return -EBUSY;
515138635ccSGavin Shan 
516138635ccSGavin Shan 	/* Update to broadcast filter mode */
517138635ccSGavin Shan 	ncm->enable = 0;
518138635ccSGavin Shan 	ncm->data[0] = 0;
519138635ccSGavin Shan 
520138635ccSGavin Shan 	return 0;
521138635ccSGavin Shan }
522138635ccSGavin Shan 
523138635ccSGavin Shan static int ncsi_rsp_handler_egmf(struct ncsi_request *nr)
524138635ccSGavin Shan {
525138635ccSGavin Shan 	struct ncsi_cmd_egmf_pkt *cmd;
526138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
527138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
528138635ccSGavin Shan 	struct ncsi_channel *nc;
529138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
530138635ccSGavin Shan 
531138635ccSGavin Shan 	/* Find the channel */
532138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
533138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
534138635ccSGavin Shan 				      NULL, &nc);
535138635ccSGavin Shan 	if (!nc)
536138635ccSGavin Shan 		return -ENODEV;
537138635ccSGavin Shan 
538138635ccSGavin Shan 	/* Check if multicast filter has been enabled */
539138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_MC];
540138635ccSGavin Shan 	if (ncm->enable)
541138635ccSGavin Shan 		return -EBUSY;
542138635ccSGavin Shan 
543138635ccSGavin Shan 	/* Update to multicast filter mode */
544138635ccSGavin Shan 	cmd = (struct ncsi_cmd_egmf_pkt *)skb_network_header(nr->cmd);
545138635ccSGavin Shan 	ncm->enable = 1;
546138635ccSGavin Shan 	ncm->data[0] = ntohl(cmd->mode);
547138635ccSGavin Shan 
548138635ccSGavin Shan 	return 0;
549138635ccSGavin Shan }
550138635ccSGavin Shan 
551138635ccSGavin Shan static int ncsi_rsp_handler_dgmf(struct ncsi_request *nr)
552138635ccSGavin Shan {
553138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
554138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
555138635ccSGavin Shan 	struct ncsi_channel *nc;
556138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
557138635ccSGavin Shan 
558138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
559138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
560138635ccSGavin Shan 				      NULL, &nc);
561138635ccSGavin Shan 	if (!nc)
562138635ccSGavin Shan 		return -ENODEV;
563138635ccSGavin Shan 
564138635ccSGavin Shan 	/* Check if multicast filter has been enabled */
565138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_MC];
566138635ccSGavin Shan 	if (!ncm->enable)
567138635ccSGavin Shan 		return -EBUSY;
568138635ccSGavin Shan 
569138635ccSGavin Shan 	/* Update to multicast filter mode */
570138635ccSGavin Shan 	ncm->enable = 0;
571138635ccSGavin Shan 	ncm->data[0] = 0;
572138635ccSGavin Shan 
573138635ccSGavin Shan 	return 0;
574138635ccSGavin Shan }
575138635ccSGavin Shan 
576138635ccSGavin Shan static int ncsi_rsp_handler_snfc(struct ncsi_request *nr)
577138635ccSGavin Shan {
578138635ccSGavin Shan 	struct ncsi_cmd_snfc_pkt *cmd;
579138635ccSGavin Shan 	struct ncsi_rsp_pkt *rsp;
580138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
581138635ccSGavin Shan 	struct ncsi_channel *nc;
582138635ccSGavin Shan 	struct ncsi_channel_mode *ncm;
583138635ccSGavin Shan 
584138635ccSGavin Shan 	/* Find the channel */
585138635ccSGavin Shan 	rsp = (struct ncsi_rsp_pkt *)skb_network_header(nr->rsp);
586138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
587138635ccSGavin Shan 				      NULL, &nc);
588138635ccSGavin Shan 	if (!nc)
589138635ccSGavin Shan 		return -ENODEV;
590138635ccSGavin Shan 
591138635ccSGavin Shan 	/* Check if flow control has been enabled */
592138635ccSGavin Shan 	ncm = &nc->modes[NCSI_MODE_FC];
593138635ccSGavin Shan 	if (ncm->enable)
594138635ccSGavin Shan 		return -EBUSY;
595138635ccSGavin Shan 
596138635ccSGavin Shan 	/* Update to flow control mode */
597138635ccSGavin Shan 	cmd = (struct ncsi_cmd_snfc_pkt *)skb_network_header(nr->cmd);
598138635ccSGavin Shan 	ncm->enable = 1;
599138635ccSGavin Shan 	ncm->data[0] = cmd->mode;
600138635ccSGavin Shan 
601138635ccSGavin Shan 	return 0;
602138635ccSGavin Shan }
603138635ccSGavin Shan 
604138635ccSGavin Shan static int ncsi_rsp_handler_gvi(struct ncsi_request *nr)
605138635ccSGavin Shan {
606138635ccSGavin Shan 	struct ncsi_rsp_gvi_pkt *rsp;
607138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
608138635ccSGavin Shan 	struct ncsi_channel *nc;
609138635ccSGavin Shan 	struct ncsi_channel_version *ncv;
610138635ccSGavin Shan 	int i;
611138635ccSGavin Shan 
612138635ccSGavin Shan 	/* Find the channel */
613138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gvi_pkt *)skb_network_header(nr->rsp);
614138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
615138635ccSGavin Shan 				      NULL, &nc);
616138635ccSGavin Shan 	if (!nc)
617138635ccSGavin Shan 		return -ENODEV;
618138635ccSGavin Shan 
619138635ccSGavin Shan 	/* Update to channel's version info */
620138635ccSGavin Shan 	ncv = &nc->version;
621138635ccSGavin Shan 	ncv->version = ntohl(rsp->ncsi_version);
622138635ccSGavin Shan 	ncv->alpha2 = rsp->alpha2;
623138635ccSGavin Shan 	memcpy(ncv->fw_name, rsp->fw_name, 12);
624138635ccSGavin Shan 	ncv->fw_version = ntohl(rsp->fw_version);
625138635ccSGavin Shan 	for (i = 0; i < ARRAY_SIZE(ncv->pci_ids); i++)
626138635ccSGavin Shan 		ncv->pci_ids[i] = ntohs(rsp->pci_ids[i]);
627138635ccSGavin Shan 	ncv->mf_id = ntohl(rsp->mf_id);
628138635ccSGavin Shan 
629138635ccSGavin Shan 	return 0;
630138635ccSGavin Shan }
631138635ccSGavin Shan 
632138635ccSGavin Shan static int ncsi_rsp_handler_gc(struct ncsi_request *nr)
633138635ccSGavin Shan {
634138635ccSGavin Shan 	struct ncsi_rsp_gc_pkt *rsp;
635138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
636138635ccSGavin Shan 	struct ncsi_channel *nc;
637138635ccSGavin Shan 	struct ncsi_channel_filter *ncf;
638138635ccSGavin Shan 	size_t size, entry_size;
639138635ccSGavin Shan 	int cnt, i;
640138635ccSGavin Shan 
641138635ccSGavin Shan 	/* Find the channel */
642138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gc_pkt *)skb_network_header(nr->rsp);
643138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
644138635ccSGavin Shan 				      NULL, &nc);
645138635ccSGavin Shan 	if (!nc)
646138635ccSGavin Shan 		return -ENODEV;
647138635ccSGavin Shan 
648138635ccSGavin Shan 	/* Update channel's capabilities */
649138635ccSGavin Shan 	nc->caps[NCSI_CAP_GENERIC].cap = ntohl(rsp->cap) &
650138635ccSGavin Shan 					 NCSI_CAP_GENERIC_MASK;
651138635ccSGavin Shan 	nc->caps[NCSI_CAP_BC].cap = ntohl(rsp->bc_cap) &
652138635ccSGavin Shan 				    NCSI_CAP_BC_MASK;
653138635ccSGavin Shan 	nc->caps[NCSI_CAP_MC].cap = ntohl(rsp->mc_cap) &
654138635ccSGavin Shan 				    NCSI_CAP_MC_MASK;
655138635ccSGavin Shan 	nc->caps[NCSI_CAP_BUFFER].cap = ntohl(rsp->buf_cap);
656138635ccSGavin Shan 	nc->caps[NCSI_CAP_AEN].cap = ntohl(rsp->aen_cap) &
657138635ccSGavin Shan 				     NCSI_CAP_AEN_MASK;
658138635ccSGavin Shan 	nc->caps[NCSI_CAP_VLAN].cap = rsp->vlan_mode &
659138635ccSGavin Shan 				      NCSI_CAP_VLAN_MASK;
660138635ccSGavin Shan 
661138635ccSGavin Shan 	/* Build filters */
662138635ccSGavin Shan 	for (i = 0; i < NCSI_FILTER_MAX; i++) {
663138635ccSGavin Shan 		switch (i) {
664138635ccSGavin Shan 		case NCSI_FILTER_VLAN:
665138635ccSGavin Shan 			cnt = rsp->vlan_cnt;
666138635ccSGavin Shan 			entry_size = 2;
667138635ccSGavin Shan 			break;
668138635ccSGavin Shan 		case NCSI_FILTER_MIXED:
669138635ccSGavin Shan 			cnt = rsp->mixed_cnt;
670138635ccSGavin Shan 			entry_size = 6;
671138635ccSGavin Shan 			break;
672138635ccSGavin Shan 		case NCSI_FILTER_MC:
673138635ccSGavin Shan 			cnt = rsp->mc_cnt;
674138635ccSGavin Shan 			entry_size = 6;
675138635ccSGavin Shan 			break;
676138635ccSGavin Shan 		case NCSI_FILTER_UC:
677138635ccSGavin Shan 			cnt = rsp->uc_cnt;
678138635ccSGavin Shan 			entry_size = 6;
679138635ccSGavin Shan 			break;
680138635ccSGavin Shan 		default:
681138635ccSGavin Shan 			continue;
682138635ccSGavin Shan 		}
683138635ccSGavin Shan 
684138635ccSGavin Shan 		if (!cnt || nc->filters[i])
685138635ccSGavin Shan 			continue;
686138635ccSGavin Shan 
687138635ccSGavin Shan 		size = sizeof(*ncf) + cnt * entry_size;
688138635ccSGavin Shan 		ncf = kzalloc(size, GFP_ATOMIC);
689138635ccSGavin Shan 		if (!ncf) {
690138635ccSGavin Shan 			pr_warn("%s: Cannot alloc filter table (%d)\n",
691138635ccSGavin Shan 				__func__, i);
692138635ccSGavin Shan 			return -ENOMEM;
693138635ccSGavin Shan 		}
694138635ccSGavin Shan 
695138635ccSGavin Shan 		ncf->index = i;
696138635ccSGavin Shan 		ncf->total = cnt;
697*21acf630SSamuel Mendoza-Jonas 		if (i == NCSI_FILTER_VLAN) {
698*21acf630SSamuel Mendoza-Jonas 			/* Set VLAN filters active so they are cleared in
699*21acf630SSamuel Mendoza-Jonas 			 * first configuration state
700*21acf630SSamuel Mendoza-Jonas 			 */
701*21acf630SSamuel Mendoza-Jonas 			ncf->bitmap = U64_MAX;
702*21acf630SSamuel Mendoza-Jonas 		} else {
703138635ccSGavin Shan 			ncf->bitmap = 0x0ul;
704*21acf630SSamuel Mendoza-Jonas 		}
705138635ccSGavin Shan 		nc->filters[i] = ncf;
706138635ccSGavin Shan 	}
707138635ccSGavin Shan 
708138635ccSGavin Shan 	return 0;
709138635ccSGavin Shan }
710138635ccSGavin Shan 
711138635ccSGavin Shan static int ncsi_rsp_handler_gp(struct ncsi_request *nr)
712138635ccSGavin Shan {
713138635ccSGavin Shan 	struct ncsi_rsp_gp_pkt *rsp;
714138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
715138635ccSGavin Shan 	struct ncsi_channel *nc;
716138635ccSGavin Shan 	unsigned short enable, vlan;
717138635ccSGavin Shan 	unsigned char *pdata;
718138635ccSGavin Shan 	int table, i;
719138635ccSGavin Shan 
720138635ccSGavin Shan 	/* Find the channel */
721138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gp_pkt *)skb_network_header(nr->rsp);
722138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
723138635ccSGavin Shan 				      NULL, &nc);
724138635ccSGavin Shan 	if (!nc)
725138635ccSGavin Shan 		return -ENODEV;
726138635ccSGavin Shan 
727138635ccSGavin Shan 	/* Modes with explicit enabled indications */
728138635ccSGavin Shan 	if (ntohl(rsp->valid_modes) & 0x1) {	/* BC filter mode */
729138635ccSGavin Shan 		nc->modes[NCSI_MODE_BC].enable = 1;
730138635ccSGavin Shan 		nc->modes[NCSI_MODE_BC].data[0] = ntohl(rsp->bc_mode);
731138635ccSGavin Shan 	}
732138635ccSGavin Shan 	if (ntohl(rsp->valid_modes) & 0x2)	/* Channel enabled */
733138635ccSGavin Shan 		nc->modes[NCSI_MODE_ENABLE].enable = 1;
734138635ccSGavin Shan 	if (ntohl(rsp->valid_modes) & 0x4)	/* Channel Tx enabled */
735138635ccSGavin Shan 		nc->modes[NCSI_MODE_TX_ENABLE].enable = 1;
736138635ccSGavin Shan 	if (ntohl(rsp->valid_modes) & 0x8)	/* MC filter mode */
737138635ccSGavin Shan 		nc->modes[NCSI_MODE_MC].enable = 1;
738138635ccSGavin Shan 
739138635ccSGavin Shan 	/* Modes without explicit enabled indications */
740138635ccSGavin Shan 	nc->modes[NCSI_MODE_LINK].enable = 1;
741138635ccSGavin Shan 	nc->modes[NCSI_MODE_LINK].data[0] = ntohl(rsp->link_mode);
742138635ccSGavin Shan 	nc->modes[NCSI_MODE_VLAN].enable = 1;
743138635ccSGavin Shan 	nc->modes[NCSI_MODE_VLAN].data[0] = rsp->vlan_mode;
744138635ccSGavin Shan 	nc->modes[NCSI_MODE_FC].enable = 1;
745138635ccSGavin Shan 	nc->modes[NCSI_MODE_FC].data[0] = rsp->fc_mode;
746138635ccSGavin Shan 	nc->modes[NCSI_MODE_AEN].enable = 1;
747138635ccSGavin Shan 	nc->modes[NCSI_MODE_AEN].data[0] = ntohl(rsp->aen_mode);
748138635ccSGavin Shan 
749138635ccSGavin Shan 	/* MAC addresses filter table */
750138635ccSGavin Shan 	pdata = (unsigned char *)rsp + 48;
751138635ccSGavin Shan 	enable = rsp->mac_enable;
752138635ccSGavin Shan 	for (i = 0; i < rsp->mac_cnt; i++, pdata += 6) {
753138635ccSGavin Shan 		if (i >= (nc->filters[NCSI_FILTER_UC]->total +
754138635ccSGavin Shan 			  nc->filters[NCSI_FILTER_MC]->total))
755138635ccSGavin Shan 			table = NCSI_FILTER_MIXED;
756138635ccSGavin Shan 		else if (i >= nc->filters[NCSI_FILTER_UC]->total)
757138635ccSGavin Shan 			table = NCSI_FILTER_MC;
758138635ccSGavin Shan 		else
759138635ccSGavin Shan 			table = NCSI_FILTER_UC;
760138635ccSGavin Shan 
761138635ccSGavin Shan 		if (!(enable & (0x1 << i)))
762138635ccSGavin Shan 			continue;
763138635ccSGavin Shan 
764138635ccSGavin Shan 		if (ncsi_find_filter(nc, table, pdata) >= 0)
765138635ccSGavin Shan 			continue;
766138635ccSGavin Shan 
767138635ccSGavin Shan 		ncsi_add_filter(nc, table, pdata);
768138635ccSGavin Shan 	}
769138635ccSGavin Shan 
770138635ccSGavin Shan 	/* VLAN filter table */
771138635ccSGavin Shan 	enable = ntohs(rsp->vlan_enable);
772138635ccSGavin Shan 	for (i = 0; i < rsp->vlan_cnt; i++, pdata += 2) {
773138635ccSGavin Shan 		if (!(enable & (0x1 << i)))
774138635ccSGavin Shan 			continue;
775138635ccSGavin Shan 
776138635ccSGavin Shan 		vlan = ntohs(*(__be16 *)pdata);
777138635ccSGavin Shan 		if (ncsi_find_filter(nc, NCSI_FILTER_VLAN, &vlan) >= 0)
778138635ccSGavin Shan 			continue;
779138635ccSGavin Shan 
780138635ccSGavin Shan 		ncsi_add_filter(nc, NCSI_FILTER_VLAN, &vlan);
781138635ccSGavin Shan 	}
782138635ccSGavin Shan 
783138635ccSGavin Shan 	return 0;
784138635ccSGavin Shan }
785138635ccSGavin Shan 
786138635ccSGavin Shan static int ncsi_rsp_handler_gcps(struct ncsi_request *nr)
787138635ccSGavin Shan {
788138635ccSGavin Shan 	struct ncsi_rsp_gcps_pkt *rsp;
789138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
790138635ccSGavin Shan 	struct ncsi_channel *nc;
791138635ccSGavin Shan 	struct ncsi_channel_stats *ncs;
792138635ccSGavin Shan 
793138635ccSGavin Shan 	/* Find the channel */
794138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gcps_pkt *)skb_network_header(nr->rsp);
795138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
796138635ccSGavin Shan 				      NULL, &nc);
797138635ccSGavin Shan 	if (!nc)
798138635ccSGavin Shan 		return -ENODEV;
799138635ccSGavin Shan 
800138635ccSGavin Shan 	/* Update HNC's statistics */
801138635ccSGavin Shan 	ncs = &nc->stats;
802138635ccSGavin Shan 	ncs->hnc_cnt_hi         = ntohl(rsp->cnt_hi);
803138635ccSGavin Shan 	ncs->hnc_cnt_lo         = ntohl(rsp->cnt_lo);
804138635ccSGavin Shan 	ncs->hnc_rx_bytes       = ntohl(rsp->rx_bytes);
805138635ccSGavin Shan 	ncs->hnc_tx_bytes       = ntohl(rsp->tx_bytes);
806138635ccSGavin Shan 	ncs->hnc_rx_uc_pkts     = ntohl(rsp->rx_uc_pkts);
807138635ccSGavin Shan 	ncs->hnc_rx_mc_pkts     = ntohl(rsp->rx_mc_pkts);
808138635ccSGavin Shan 	ncs->hnc_rx_bc_pkts     = ntohl(rsp->rx_bc_pkts);
809138635ccSGavin Shan 	ncs->hnc_tx_uc_pkts     = ntohl(rsp->tx_uc_pkts);
810138635ccSGavin Shan 	ncs->hnc_tx_mc_pkts     = ntohl(rsp->tx_mc_pkts);
811138635ccSGavin Shan 	ncs->hnc_tx_bc_pkts     = ntohl(rsp->tx_bc_pkts);
812138635ccSGavin Shan 	ncs->hnc_fcs_err        = ntohl(rsp->fcs_err);
813138635ccSGavin Shan 	ncs->hnc_align_err      = ntohl(rsp->align_err);
814138635ccSGavin Shan 	ncs->hnc_false_carrier  = ntohl(rsp->false_carrier);
815138635ccSGavin Shan 	ncs->hnc_runt_pkts      = ntohl(rsp->runt_pkts);
816138635ccSGavin Shan 	ncs->hnc_jabber_pkts    = ntohl(rsp->jabber_pkts);
817138635ccSGavin Shan 	ncs->hnc_rx_pause_xon   = ntohl(rsp->rx_pause_xon);
818138635ccSGavin Shan 	ncs->hnc_rx_pause_xoff  = ntohl(rsp->rx_pause_xoff);
819138635ccSGavin Shan 	ncs->hnc_tx_pause_xon   = ntohl(rsp->tx_pause_xon);
820138635ccSGavin Shan 	ncs->hnc_tx_pause_xoff  = ntohl(rsp->tx_pause_xoff);
821138635ccSGavin Shan 	ncs->hnc_tx_s_collision = ntohl(rsp->tx_s_collision);
822138635ccSGavin Shan 	ncs->hnc_tx_m_collision = ntohl(rsp->tx_m_collision);
823138635ccSGavin Shan 	ncs->hnc_l_collision    = ntohl(rsp->l_collision);
824138635ccSGavin Shan 	ncs->hnc_e_collision    = ntohl(rsp->e_collision);
825138635ccSGavin Shan 	ncs->hnc_rx_ctl_frames  = ntohl(rsp->rx_ctl_frames);
826138635ccSGavin Shan 	ncs->hnc_rx_64_frames   = ntohl(rsp->rx_64_frames);
827138635ccSGavin Shan 	ncs->hnc_rx_127_frames  = ntohl(rsp->rx_127_frames);
828138635ccSGavin Shan 	ncs->hnc_rx_255_frames  = ntohl(rsp->rx_255_frames);
829138635ccSGavin Shan 	ncs->hnc_rx_511_frames  = ntohl(rsp->rx_511_frames);
830138635ccSGavin Shan 	ncs->hnc_rx_1023_frames = ntohl(rsp->rx_1023_frames);
831138635ccSGavin Shan 	ncs->hnc_rx_1522_frames = ntohl(rsp->rx_1522_frames);
832138635ccSGavin Shan 	ncs->hnc_rx_9022_frames = ntohl(rsp->rx_9022_frames);
833138635ccSGavin Shan 	ncs->hnc_tx_64_frames   = ntohl(rsp->tx_64_frames);
834138635ccSGavin Shan 	ncs->hnc_tx_127_frames  = ntohl(rsp->tx_127_frames);
835138635ccSGavin Shan 	ncs->hnc_tx_255_frames  = ntohl(rsp->tx_255_frames);
836138635ccSGavin Shan 	ncs->hnc_tx_511_frames  = ntohl(rsp->tx_511_frames);
837138635ccSGavin Shan 	ncs->hnc_tx_1023_frames = ntohl(rsp->tx_1023_frames);
838138635ccSGavin Shan 	ncs->hnc_tx_1522_frames = ntohl(rsp->tx_1522_frames);
839138635ccSGavin Shan 	ncs->hnc_tx_9022_frames = ntohl(rsp->tx_9022_frames);
840138635ccSGavin Shan 	ncs->hnc_rx_valid_bytes = ntohl(rsp->rx_valid_bytes);
841138635ccSGavin Shan 	ncs->hnc_rx_runt_pkts   = ntohl(rsp->rx_runt_pkts);
842138635ccSGavin Shan 	ncs->hnc_rx_jabber_pkts = ntohl(rsp->rx_jabber_pkts);
843138635ccSGavin Shan 
844138635ccSGavin Shan 	return 0;
845138635ccSGavin Shan }
846138635ccSGavin Shan 
847138635ccSGavin Shan static int ncsi_rsp_handler_gns(struct ncsi_request *nr)
848138635ccSGavin Shan {
849138635ccSGavin Shan 	struct ncsi_rsp_gns_pkt *rsp;
850138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
851138635ccSGavin Shan 	struct ncsi_channel *nc;
852138635ccSGavin Shan 	struct ncsi_channel_stats *ncs;
853138635ccSGavin Shan 
854138635ccSGavin Shan 	/* Find the channel */
855138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gns_pkt *)skb_network_header(nr->rsp);
856138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
857138635ccSGavin Shan 				      NULL, &nc);
858138635ccSGavin Shan 	if (!nc)
859138635ccSGavin Shan 		return -ENODEV;
860138635ccSGavin Shan 
861138635ccSGavin Shan 	/* Update HNC's statistics */
862138635ccSGavin Shan 	ncs = &nc->stats;
863138635ccSGavin Shan 	ncs->ncsi_rx_cmds       = ntohl(rsp->rx_cmds);
864138635ccSGavin Shan 	ncs->ncsi_dropped_cmds  = ntohl(rsp->dropped_cmds);
865138635ccSGavin Shan 	ncs->ncsi_cmd_type_errs = ntohl(rsp->cmd_type_errs);
866138635ccSGavin Shan 	ncs->ncsi_cmd_csum_errs = ntohl(rsp->cmd_csum_errs);
867138635ccSGavin Shan 	ncs->ncsi_rx_pkts       = ntohl(rsp->rx_pkts);
868138635ccSGavin Shan 	ncs->ncsi_tx_pkts       = ntohl(rsp->tx_pkts);
869138635ccSGavin Shan 	ncs->ncsi_tx_aen_pkts   = ntohl(rsp->tx_aen_pkts);
870138635ccSGavin Shan 
871138635ccSGavin Shan 	return 0;
872138635ccSGavin Shan }
873138635ccSGavin Shan 
874138635ccSGavin Shan static int ncsi_rsp_handler_gnpts(struct ncsi_request *nr)
875138635ccSGavin Shan {
876138635ccSGavin Shan 	struct ncsi_rsp_gnpts_pkt *rsp;
877138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
878138635ccSGavin Shan 	struct ncsi_channel *nc;
879138635ccSGavin Shan 	struct ncsi_channel_stats *ncs;
880138635ccSGavin Shan 
881138635ccSGavin Shan 	/* Find the channel */
882138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gnpts_pkt *)skb_network_header(nr->rsp);
883138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
884138635ccSGavin Shan 				      NULL, &nc);
885138635ccSGavin Shan 	if (!nc)
886138635ccSGavin Shan 		return -ENODEV;
887138635ccSGavin Shan 
888138635ccSGavin Shan 	/* Update HNC's statistics */
889138635ccSGavin Shan 	ncs = &nc->stats;
890138635ccSGavin Shan 	ncs->pt_tx_pkts        = ntohl(rsp->tx_pkts);
891138635ccSGavin Shan 	ncs->pt_tx_dropped     = ntohl(rsp->tx_dropped);
892138635ccSGavin Shan 	ncs->pt_tx_channel_err = ntohl(rsp->tx_channel_err);
893138635ccSGavin Shan 	ncs->pt_tx_us_err      = ntohl(rsp->tx_us_err);
894138635ccSGavin Shan 	ncs->pt_rx_pkts        = ntohl(rsp->rx_pkts);
895138635ccSGavin Shan 	ncs->pt_rx_dropped     = ntohl(rsp->rx_dropped);
896138635ccSGavin Shan 	ncs->pt_rx_channel_err = ntohl(rsp->rx_channel_err);
897138635ccSGavin Shan 	ncs->pt_rx_us_err      = ntohl(rsp->rx_us_err);
898138635ccSGavin Shan 	ncs->pt_rx_os_err      = ntohl(rsp->rx_os_err);
899138635ccSGavin Shan 
900138635ccSGavin Shan 	return 0;
901138635ccSGavin Shan }
902138635ccSGavin Shan 
903138635ccSGavin Shan static int ncsi_rsp_handler_gps(struct ncsi_request *nr)
904138635ccSGavin Shan {
905138635ccSGavin Shan 	struct ncsi_rsp_gps_pkt *rsp;
906138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
907138635ccSGavin Shan 	struct ncsi_package *np;
908138635ccSGavin Shan 
909138635ccSGavin Shan 	/* Find the package */
910138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gps_pkt *)skb_network_header(nr->rsp);
911138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
912138635ccSGavin Shan 				      &np, NULL);
913138635ccSGavin Shan 	if (!np)
914138635ccSGavin Shan 		return -ENODEV;
915138635ccSGavin Shan 
916138635ccSGavin Shan 	return 0;
917138635ccSGavin Shan }
918138635ccSGavin Shan 
919138635ccSGavin Shan static int ncsi_rsp_handler_gpuuid(struct ncsi_request *nr)
920138635ccSGavin Shan {
921138635ccSGavin Shan 	struct ncsi_rsp_gpuuid_pkt *rsp;
922138635ccSGavin Shan 	struct ncsi_dev_priv *ndp = nr->ndp;
923138635ccSGavin Shan 	struct ncsi_package *np;
924138635ccSGavin Shan 
925138635ccSGavin Shan 	/* Find the package */
926138635ccSGavin Shan 	rsp = (struct ncsi_rsp_gpuuid_pkt *)skb_network_header(nr->rsp);
927138635ccSGavin Shan 	ncsi_find_package_and_channel(ndp, rsp->rsp.common.channel,
928138635ccSGavin Shan 				      &np, NULL);
929138635ccSGavin Shan 	if (!np)
930138635ccSGavin Shan 		return -ENODEV;
931138635ccSGavin Shan 
932138635ccSGavin Shan 	memcpy(np->uuid, rsp->uuid, sizeof(rsp->uuid));
933138635ccSGavin Shan 
934138635ccSGavin Shan 	return 0;
935138635ccSGavin Shan }
936138635ccSGavin Shan 
937138635ccSGavin Shan static struct ncsi_rsp_handler {
938138635ccSGavin Shan 	unsigned char	type;
939138635ccSGavin Shan 	int             payload;
940138635ccSGavin Shan 	int		(*handler)(struct ncsi_request *nr);
941138635ccSGavin Shan } ncsi_rsp_handlers[] = {
942138635ccSGavin Shan 	{ NCSI_PKT_RSP_CIS,     4, ncsi_rsp_handler_cis     },
943138635ccSGavin Shan 	{ NCSI_PKT_RSP_SP,      4, ncsi_rsp_handler_sp      },
944138635ccSGavin Shan 	{ NCSI_PKT_RSP_DP,      4, ncsi_rsp_handler_dp      },
945138635ccSGavin Shan 	{ NCSI_PKT_RSP_EC,      4, ncsi_rsp_handler_ec      },
946138635ccSGavin Shan 	{ NCSI_PKT_RSP_DC,      4, ncsi_rsp_handler_dc      },
947138635ccSGavin Shan 	{ NCSI_PKT_RSP_RC,      4, ncsi_rsp_handler_rc      },
948138635ccSGavin Shan 	{ NCSI_PKT_RSP_ECNT,    4, ncsi_rsp_handler_ecnt    },
949138635ccSGavin Shan 	{ NCSI_PKT_RSP_DCNT,    4, ncsi_rsp_handler_dcnt    },
950138635ccSGavin Shan 	{ NCSI_PKT_RSP_AE,      4, ncsi_rsp_handler_ae      },
951138635ccSGavin Shan 	{ NCSI_PKT_RSP_SL,      4, ncsi_rsp_handler_sl      },
952138635ccSGavin Shan 	{ NCSI_PKT_RSP_GLS,    16, ncsi_rsp_handler_gls     },
953138635ccSGavin Shan 	{ NCSI_PKT_RSP_SVF,     4, ncsi_rsp_handler_svf     },
954138635ccSGavin Shan 	{ NCSI_PKT_RSP_EV,      4, ncsi_rsp_handler_ev      },
955138635ccSGavin Shan 	{ NCSI_PKT_RSP_DV,      4, ncsi_rsp_handler_dv      },
956138635ccSGavin Shan 	{ NCSI_PKT_RSP_SMA,     4, ncsi_rsp_handler_sma     },
957138635ccSGavin Shan 	{ NCSI_PKT_RSP_EBF,     4, ncsi_rsp_handler_ebf     },
958138635ccSGavin Shan 	{ NCSI_PKT_RSP_DBF,     4, ncsi_rsp_handler_dbf     },
959138635ccSGavin Shan 	{ NCSI_PKT_RSP_EGMF,    4, ncsi_rsp_handler_egmf    },
960138635ccSGavin Shan 	{ NCSI_PKT_RSP_DGMF,    4, ncsi_rsp_handler_dgmf    },
961138635ccSGavin Shan 	{ NCSI_PKT_RSP_SNFC,    4, ncsi_rsp_handler_snfc    },
962138635ccSGavin Shan 	{ NCSI_PKT_RSP_GVI,    36, ncsi_rsp_handler_gvi     },
963138635ccSGavin Shan 	{ NCSI_PKT_RSP_GC,     32, ncsi_rsp_handler_gc      },
964138635ccSGavin Shan 	{ NCSI_PKT_RSP_GP,     -1, ncsi_rsp_handler_gp      },
965138635ccSGavin Shan 	{ NCSI_PKT_RSP_GCPS,  172, ncsi_rsp_handler_gcps    },
966138635ccSGavin Shan 	{ NCSI_PKT_RSP_GNS,   172, ncsi_rsp_handler_gns     },
967138635ccSGavin Shan 	{ NCSI_PKT_RSP_GNPTS, 172, ncsi_rsp_handler_gnpts   },
968138635ccSGavin Shan 	{ NCSI_PKT_RSP_GPS,     8, ncsi_rsp_handler_gps     },
969138635ccSGavin Shan 	{ NCSI_PKT_RSP_OEM,     0, NULL                     },
970138635ccSGavin Shan 	{ NCSI_PKT_RSP_PLDM,    0, NULL                     },
971138635ccSGavin Shan 	{ NCSI_PKT_RSP_GPUUID, 20, ncsi_rsp_handler_gpuuid  }
972138635ccSGavin Shan };
973138635ccSGavin Shan 
974138635ccSGavin Shan int ncsi_rcv_rsp(struct sk_buff *skb, struct net_device *dev,
975138635ccSGavin Shan 		 struct packet_type *pt, struct net_device *orig_dev)
976138635ccSGavin Shan {
977138635ccSGavin Shan 	struct ncsi_rsp_handler *nrh = NULL;
978138635ccSGavin Shan 	struct ncsi_dev *nd;
979138635ccSGavin Shan 	struct ncsi_dev_priv *ndp;
980138635ccSGavin Shan 	struct ncsi_request *nr;
981138635ccSGavin Shan 	struct ncsi_pkt_hdr *hdr;
982138635ccSGavin Shan 	unsigned long flags;
983138635ccSGavin Shan 	int payload, i, ret;
984138635ccSGavin Shan 
985138635ccSGavin Shan 	/* Find the NCSI device */
986138635ccSGavin Shan 	nd = ncsi_find_dev(dev);
987138635ccSGavin Shan 	ndp = nd ? TO_NCSI_DEV_PRIV(nd) : NULL;
988138635ccSGavin Shan 	if (!ndp)
989138635ccSGavin Shan 		return -ENODEV;
990138635ccSGavin Shan 
9917a82ecf4SGavin Shan 	/* Check if it is AEN packet */
992138635ccSGavin Shan 	hdr = (struct ncsi_pkt_hdr *)skb_network_header(skb);
9937a82ecf4SGavin Shan 	if (hdr->type == NCSI_PKT_AEN)
9947a82ecf4SGavin Shan 		return ncsi_aen_handler(ndp, skb);
9957a82ecf4SGavin Shan 
9967a82ecf4SGavin Shan 	/* Find the handler */
997138635ccSGavin Shan 	for (i = 0; i < ARRAY_SIZE(ncsi_rsp_handlers); i++) {
998138635ccSGavin Shan 		if (ncsi_rsp_handlers[i].type == hdr->type) {
999138635ccSGavin Shan 			if (ncsi_rsp_handlers[i].handler)
1000138635ccSGavin Shan 				nrh = &ncsi_rsp_handlers[i];
1001138635ccSGavin Shan 			else
1002138635ccSGavin Shan 				nrh = NULL;
1003138635ccSGavin Shan 
1004138635ccSGavin Shan 			break;
1005138635ccSGavin Shan 		}
1006138635ccSGavin Shan 	}
1007138635ccSGavin Shan 
1008138635ccSGavin Shan 	if (!nrh) {
1009138635ccSGavin Shan 		netdev_err(nd->dev, "Received unrecognized packet (0x%x)\n",
1010138635ccSGavin Shan 			   hdr->type);
1011138635ccSGavin Shan 		return -ENOENT;
1012138635ccSGavin Shan 	}
1013138635ccSGavin Shan 
1014138635ccSGavin Shan 	/* Associate with the request */
1015138635ccSGavin Shan 	spin_lock_irqsave(&ndp->lock, flags);
1016138635ccSGavin Shan 	nr = &ndp->requests[hdr->id];
1017138635ccSGavin Shan 	if (!nr->used) {
1018138635ccSGavin Shan 		spin_unlock_irqrestore(&ndp->lock, flags);
1019138635ccSGavin Shan 		return -ENODEV;
1020138635ccSGavin Shan 	}
1021138635ccSGavin Shan 
1022138635ccSGavin Shan 	nr->rsp = skb;
1023138635ccSGavin Shan 	if (!nr->enabled) {
1024138635ccSGavin Shan 		spin_unlock_irqrestore(&ndp->lock, flags);
1025138635ccSGavin Shan 		ret = -ENOENT;
1026138635ccSGavin Shan 		goto out;
1027138635ccSGavin Shan 	}
1028138635ccSGavin Shan 
1029138635ccSGavin Shan 	/* Validate the packet */
1030138635ccSGavin Shan 	spin_unlock_irqrestore(&ndp->lock, flags);
1031138635ccSGavin Shan 	payload = nrh->payload;
1032138635ccSGavin Shan 	if (payload < 0)
1033138635ccSGavin Shan 		payload = ntohs(hdr->length);
1034138635ccSGavin Shan 	ret = ncsi_validate_rsp_pkt(nr, payload);
1035138635ccSGavin Shan 	if (ret)
1036138635ccSGavin Shan 		goto out;
1037138635ccSGavin Shan 
1038138635ccSGavin Shan 	/* Process the packet */
1039138635ccSGavin Shan 	ret = nrh->handler(nr);
1040138635ccSGavin Shan out:
1041138635ccSGavin Shan 	ncsi_free_request(nr);
1042138635ccSGavin Shan 	return ret;
1043138635ccSGavin Shan }
1044