1 #include <linux/delay.h>
2 #include <linux/etherdevice.h>
3 #include <linux/netdevice.h>
4 #include <linux/if_ether.h>
5 #include <linux/if_arp.h>
6 #include <linux/kthread.h>
7 #include <linux/kfifo.h>
8 #include <net/cfg80211.h>
9 
10 #include "mesh.h"
11 #include "decl.h"
12 #include "cmd.h"
13 
14 
15 /***************************************************************************
16  * Mesh sysfs support
17  */
18 
19 /**
20  * Attributes exported through sysfs
21  */
22 
23 /**
24  * @brief Get function for sysfs attribute anycast_mask
25  */
lbs_anycast_get(struct device * dev,struct device_attribute * attr,char * buf)26 static ssize_t lbs_anycast_get(struct device *dev,
27 		struct device_attribute *attr, char * buf)
28 {
29 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
30 	struct cmd_ds_mesh_access mesh_access;
31 	int ret;
32 
33 	memset(&mesh_access, 0, sizeof(mesh_access));
34 
35 	ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_ANYCAST, &mesh_access);
36 	if (ret)
37 		return ret;
38 
39 	return snprintf(buf, 12, "0x%X\n", le32_to_cpu(mesh_access.data[0]));
40 }
41 
42 /**
43  * @brief Set function for sysfs attribute anycast_mask
44  */
lbs_anycast_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)45 static ssize_t lbs_anycast_set(struct device *dev,
46 		struct device_attribute *attr, const char * buf, size_t count)
47 {
48 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
49 	struct cmd_ds_mesh_access mesh_access;
50 	uint32_t datum;
51 	int ret;
52 
53 	memset(&mesh_access, 0, sizeof(mesh_access));
54 	sscanf(buf, "%x", &datum);
55 	mesh_access.data[0] = cpu_to_le32(datum);
56 
57 	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_ANYCAST, &mesh_access);
58 	if (ret)
59 		return ret;
60 
61 	return strlen(buf);
62 }
63 
64 /**
65  * @brief Get function for sysfs attribute prb_rsp_limit
66  */
lbs_prb_rsp_limit_get(struct device * dev,struct device_attribute * attr,char * buf)67 static ssize_t lbs_prb_rsp_limit_get(struct device *dev,
68 		struct device_attribute *attr, char *buf)
69 {
70 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
71 	struct cmd_ds_mesh_access mesh_access;
72 	int ret;
73 	u32 retry_limit;
74 
75 	memset(&mesh_access, 0, sizeof(mesh_access));
76 	mesh_access.data[0] = cpu_to_le32(CMD_ACT_GET);
77 
78 	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
79 			&mesh_access);
80 	if (ret)
81 		return ret;
82 
83 	retry_limit = le32_to_cpu(mesh_access.data[1]);
84 	return snprintf(buf, 10, "%d\n", retry_limit);
85 }
86 
87 /**
88  * @brief Set function for sysfs attribute prb_rsp_limit
89  */
lbs_prb_rsp_limit_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)90 static ssize_t lbs_prb_rsp_limit_set(struct device *dev,
91 		struct device_attribute *attr, const char *buf, size_t count)
92 {
93 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
94 	struct cmd_ds_mesh_access mesh_access;
95 	int ret;
96 	unsigned long retry_limit;
97 
98 	memset(&mesh_access, 0, sizeof(mesh_access));
99 	mesh_access.data[0] = cpu_to_le32(CMD_ACT_SET);
100 
101 	if (!strict_strtoul(buf, 10, &retry_limit))
102 		return -ENOTSUPP;
103 	if (retry_limit > 15)
104 		return -ENOTSUPP;
105 
106 	mesh_access.data[1] = cpu_to_le32(retry_limit);
107 
108 	ret = lbs_mesh_access(priv, CMD_ACT_MESH_SET_GET_PRB_RSP_LIMIT,
109 			&mesh_access);
110 	if (ret)
111 		return ret;
112 
113 	return strlen(buf);
114 }
115 
116 /**
117  * Get function for sysfs attribute mesh
118  */
lbs_mesh_get(struct device * dev,struct device_attribute * attr,char * buf)119 static ssize_t lbs_mesh_get(struct device *dev,
120 		struct device_attribute *attr, char * buf)
121 {
122 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
123 	return snprintf(buf, 5, "0x%X\n", !!priv->mesh_dev);
124 }
125 
126 /**
127  *  Set function for sysfs attribute mesh
128  */
lbs_mesh_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)129 static ssize_t lbs_mesh_set(struct device *dev,
130 		struct device_attribute *attr, const char * buf, size_t count)
131 {
132 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
133 	int enable;
134 	int ret, action = CMD_ACT_MESH_CONFIG_STOP;
135 
136 	sscanf(buf, "%x", &enable);
137 	enable = !!enable;
138 	if (enable == !!priv->mesh_dev)
139 		return count;
140 	if (enable)
141 		action = CMD_ACT_MESH_CONFIG_START;
142 	ret = lbs_mesh_config(priv, action, priv->channel);
143 	if (ret)
144 		return ret;
145 
146 	if (enable)
147 		lbs_add_mesh(priv);
148 	else
149 		lbs_remove_mesh(priv);
150 
151 	return count;
152 }
153 
154 /**
155  * lbs_mesh attribute to be exported per ethX interface
156  * through sysfs (/sys/class/net/ethX/lbs_mesh)
157  */
158 static DEVICE_ATTR(lbs_mesh, 0644, lbs_mesh_get, lbs_mesh_set);
159 
160 /**
161  * anycast_mask attribute to be exported per mshX interface
162  * through sysfs (/sys/class/net/mshX/anycast_mask)
163  */
164 static DEVICE_ATTR(anycast_mask, 0644, lbs_anycast_get, lbs_anycast_set);
165 
166 /**
167  * prb_rsp_limit attribute to be exported per mshX interface
168  * through sysfs (/sys/class/net/mshX/prb_rsp_limit)
169  */
170 static DEVICE_ATTR(prb_rsp_limit, 0644, lbs_prb_rsp_limit_get,
171 		lbs_prb_rsp_limit_set);
172 
173 static struct attribute *lbs_mesh_sysfs_entries[] = {
174 	&dev_attr_anycast_mask.attr,
175 	&dev_attr_prb_rsp_limit.attr,
176 	NULL,
177 };
178 
179 static struct attribute_group lbs_mesh_attr_group = {
180 	.attrs = lbs_mesh_sysfs_entries,
181 };
182 
183 
184 
185 /***************************************************************************
186  * Initializing and starting, stopping mesh
187  */
188 
189 /*
190  * Check mesh FW version and appropriately send the mesh start
191  * command
192  */
lbs_init_mesh(struct lbs_private * priv)193 int lbs_init_mesh(struct lbs_private *priv)
194 {
195 	struct net_device *dev = priv->dev;
196 	int ret = 0;
197 
198 	lbs_deb_enter(LBS_DEB_MESH);
199 
200 	priv->mesh_connect_status = LBS_DISCONNECTED;
201 
202 	/* Determine mesh_fw_ver from fwrelease and fwcapinfo */
203 	/* 5.0.16p0 9.0.0.p0 is known to NOT support any mesh */
204 	/* 5.110.22 have mesh command with 0xa3 command id */
205 	/* 10.0.0.p0 FW brings in mesh config command with different id */
206 	/* Check FW version MSB and initialize mesh_fw_ver */
207 	if (MRVL_FW_MAJOR_REV(priv->fwrelease) == MRVL_FW_V5) {
208 		/* Enable mesh, if supported, and work out which TLV it uses.
209 		   0x100 + 291 is an unofficial value used in 5.110.20.pXX
210 		   0x100 + 37 is the official value used in 5.110.21.pXX
211 		   but we check them in that order because 20.pXX doesn't
212 		   give an error -- it just silently fails. */
213 
214 		/* 5.110.20.pXX firmware will fail the command if the channel
215 		   doesn't match the existing channel. But only if the TLV
216 		   is correct. If the channel is wrong, _BOTH_ versions will
217 		   give an error to 0x100+291, and allow 0x100+37 to succeed.
218 		   It's just that 5.110.20.pXX will not have done anything
219 		   useful */
220 
221 		priv->mesh_tlv = TLV_TYPE_OLD_MESH_ID;
222 		if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
223 				    priv->channel)) {
224 			priv->mesh_tlv = TLV_TYPE_MESH_ID;
225 			if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
226 					    priv->channel))
227 				priv->mesh_tlv = 0;
228 		}
229 	} else
230 	if ((MRVL_FW_MAJOR_REV(priv->fwrelease) >= MRVL_FW_V10) &&
231 		(priv->fwcapinfo & MESH_CAPINFO_ENABLE_MASK)) {
232 		/* 10.0.0.pXX new firmwares should succeed with TLV
233 		 * 0x100+37; Do not invoke command with old TLV.
234 		 */
235 		priv->mesh_tlv = TLV_TYPE_MESH_ID;
236 		if (lbs_mesh_config(priv, CMD_ACT_MESH_CONFIG_START,
237 				    priv->channel))
238 			priv->mesh_tlv = 0;
239 	}
240 
241 
242 	if (priv->mesh_tlv) {
243 		sprintf(priv->mesh_ssid, "mesh");
244 		priv->mesh_ssid_len = 4;
245 
246 		lbs_add_mesh(priv);
247 
248 		if (device_create_file(&dev->dev, &dev_attr_lbs_mesh))
249 			lbs_pr_err("cannot register lbs_mesh attribute\n");
250 
251 		ret = 1;
252 	}
253 
254 	lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
255 	return ret;
256 }
257 
258 
lbs_deinit_mesh(struct lbs_private * priv)259 int lbs_deinit_mesh(struct lbs_private *priv)
260 {
261 	struct net_device *dev = priv->dev;
262 	int ret = 0;
263 
264 	lbs_deb_enter(LBS_DEB_MESH);
265 
266 	if (priv->mesh_tlv) {
267 		device_remove_file(&dev->dev, &dev_attr_lbs_mesh);
268 		ret = 1;
269 	}
270 
271 	lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
272 	return ret;
273 }
274 
275 
276 /**
277  *  @brief This function closes the mshX interface
278  *
279  *  @param dev     A pointer to net_device structure
280  *  @return 	   0
281  */
lbs_mesh_stop(struct net_device * dev)282 static int lbs_mesh_stop(struct net_device *dev)
283 {
284 	struct lbs_private *priv = dev->ml_priv;
285 
286 	lbs_deb_enter(LBS_DEB_MESH);
287 	spin_lock_irq(&priv->driver_lock);
288 
289 	priv->mesh_open = 0;
290 	priv->mesh_connect_status = LBS_DISCONNECTED;
291 
292 	netif_stop_queue(dev);
293 	netif_carrier_off(dev);
294 
295 	spin_unlock_irq(&priv->driver_lock);
296 
297 	schedule_work(&priv->mcast_work);
298 
299 	lbs_deb_leave(LBS_DEB_MESH);
300 	return 0;
301 }
302 
303 /**
304  *  @brief This function opens the mshX interface
305  *
306  *  @param dev     A pointer to net_device structure
307  *  @return 	   0 or -EBUSY if monitor mode active
308  */
lbs_mesh_dev_open(struct net_device * dev)309 static int lbs_mesh_dev_open(struct net_device *dev)
310 {
311 	struct lbs_private *priv = dev->ml_priv;
312 	int ret = 0;
313 
314 	lbs_deb_enter(LBS_DEB_NET);
315 
316 	spin_lock_irq(&priv->driver_lock);
317 
318 	if (priv->wdev->iftype == NL80211_IFTYPE_MONITOR) {
319 		ret = -EBUSY;
320 		goto out;
321 	}
322 
323 	priv->mesh_open = 1;
324 	priv->mesh_connect_status = LBS_CONNECTED;
325 	netif_carrier_on(dev);
326 
327 	if (!priv->tx_pending_len)
328 		netif_wake_queue(dev);
329  out:
330 
331 	spin_unlock_irq(&priv->driver_lock);
332 	lbs_deb_leave_args(LBS_DEB_NET, "ret %d", ret);
333 	return ret;
334 }
335 
336 static const struct net_device_ops mesh_netdev_ops = {
337 	.ndo_open		= lbs_mesh_dev_open,
338 	.ndo_stop 		= lbs_mesh_stop,
339 	.ndo_start_xmit		= lbs_hard_start_xmit,
340 	.ndo_set_mac_address	= lbs_set_mac_address,
341 	.ndo_set_multicast_list = lbs_set_multicast_list,
342 };
343 
344 /**
345  * @brief This function adds mshX interface
346  *
347  *  @param priv    A pointer to the struct lbs_private structure
348  *  @return 	   0 if successful, -X otherwise
349  */
lbs_add_mesh(struct lbs_private * priv)350 int lbs_add_mesh(struct lbs_private *priv)
351 {
352 	struct net_device *mesh_dev = NULL;
353 	int ret = 0;
354 
355 	lbs_deb_enter(LBS_DEB_MESH);
356 
357 	/* Allocate a virtual mesh device */
358 	mesh_dev = alloc_netdev(0, "msh%d", ether_setup);
359 	if (!mesh_dev) {
360 		lbs_deb_mesh("init mshX device failed\n");
361 		ret = -ENOMEM;
362 		goto done;
363 	}
364 	mesh_dev->ml_priv = priv;
365 	priv->mesh_dev = mesh_dev;
366 
367 	mesh_dev->netdev_ops = &mesh_netdev_ops;
368 	mesh_dev->ethtool_ops = &lbs_ethtool_ops;
369 	memcpy(mesh_dev->dev_addr, priv->dev->dev_addr, ETH_ALEN);
370 
371 	SET_NETDEV_DEV(priv->mesh_dev, priv->dev->dev.parent);
372 
373 	mesh_dev->flags |= IFF_BROADCAST | IFF_MULTICAST;
374 	/* Register virtual mesh interface */
375 	ret = register_netdev(mesh_dev);
376 	if (ret) {
377 		lbs_pr_err("cannot register mshX virtual interface\n");
378 		goto err_free;
379 	}
380 
381 	ret = sysfs_create_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
382 	if (ret)
383 		goto err_unregister;
384 
385 	lbs_persist_config_init(mesh_dev);
386 
387 	/* Everything successful */
388 	ret = 0;
389 	goto done;
390 
391 err_unregister:
392 	unregister_netdev(mesh_dev);
393 
394 err_free:
395 	free_netdev(mesh_dev);
396 
397 done:
398 	lbs_deb_leave_args(LBS_DEB_MESH, "ret %d", ret);
399 	return ret;
400 }
401 
lbs_remove_mesh(struct lbs_private * priv)402 void lbs_remove_mesh(struct lbs_private *priv)
403 {
404 	struct net_device *mesh_dev;
405 
406 	mesh_dev = priv->mesh_dev;
407 	if (!mesh_dev)
408 		return;
409 
410 	lbs_deb_enter(LBS_DEB_MESH);
411 	netif_stop_queue(mesh_dev);
412 	netif_carrier_off(mesh_dev);
413 	sysfs_remove_group(&(mesh_dev->dev.kobj), &lbs_mesh_attr_group);
414 	lbs_persist_config_remove(mesh_dev);
415 	unregister_netdev(mesh_dev);
416 	priv->mesh_dev = NULL;
417 	free_netdev(mesh_dev);
418 	lbs_deb_leave(LBS_DEB_MESH);
419 }
420 
421 
422 
423 /***************************************************************************
424  * Sending and receiving
425  */
lbs_mesh_set_dev(struct lbs_private * priv,struct net_device * dev,struct rxpd * rxpd)426 struct net_device *lbs_mesh_set_dev(struct lbs_private *priv,
427 	struct net_device *dev, struct rxpd *rxpd)
428 {
429 	if (priv->mesh_dev) {
430 		if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID) {
431 			if (rxpd->rx_control & RxPD_MESH_FRAME)
432 				dev = priv->mesh_dev;
433 		} else if (priv->mesh_tlv == TLV_TYPE_MESH_ID) {
434 			if (rxpd->u.bss.bss_num == MESH_IFACE_ID)
435 				dev = priv->mesh_dev;
436 		}
437 	}
438 	return dev;
439 }
440 
441 
lbs_mesh_set_txpd(struct lbs_private * priv,struct net_device * dev,struct txpd * txpd)442 void lbs_mesh_set_txpd(struct lbs_private *priv,
443 	struct net_device *dev, struct txpd *txpd)
444 {
445 	if (dev == priv->mesh_dev) {
446 		if (priv->mesh_tlv == TLV_TYPE_OLD_MESH_ID)
447 			txpd->tx_control |= cpu_to_le32(TxPD_MESH_FRAME);
448 		else if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
449 			txpd->u.bss.bss_num = MESH_IFACE_ID;
450 	}
451 }
452 
453 
454 /***************************************************************************
455  * Mesh command handling
456  */
457 
458 /**
459  *  @brief Add or delete Mesh Blinding Table entries
460  *
461  *  @param priv    	A pointer to struct lbs_private structure
462  *  @param add  	TRUE to add the entry, FALSE to delete it
463  *  @param addr1        Destination address to blind or unblind
464  *
465  *  @return 	   	0 on success, error on failure
466  */
lbs_mesh_bt_add_del(struct lbs_private * priv,bool add,u8 * addr1)467 int lbs_mesh_bt_add_del(struct lbs_private *priv, bool add, u8 *addr1)
468 {
469 	struct cmd_ds_bt_access cmd;
470 	int ret = 0;
471 
472 	lbs_deb_enter(LBS_DEB_CMD);
473 
474 	BUG_ON(addr1 == NULL);
475 
476 	memset(&cmd, 0, sizeof(cmd));
477 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
478 	memcpy(cmd.addr1, addr1, ETH_ALEN);
479 	if (add) {
480 		cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_ADD);
481 		lbs_deb_hex(LBS_DEB_MESH, "BT_ADD: blinded MAC addr",
482 			addr1, ETH_ALEN);
483 	} else {
484 		cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_DEL);
485 		lbs_deb_hex(LBS_DEB_MESH, "BT_DEL: blinded MAC addr",
486 			addr1, ETH_ALEN);
487 	}
488 
489 	ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
490 
491 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
492 	return ret;
493 }
494 
495 /**
496  *  @brief Reset/clear the mesh blinding table
497  *
498  *  @param priv    	A pointer to struct lbs_private structure
499  *
500  *  @return 	   	0 on success, error on failure
501  */
lbs_mesh_bt_reset(struct lbs_private * priv)502 int lbs_mesh_bt_reset(struct lbs_private *priv)
503 {
504 	struct cmd_ds_bt_access cmd;
505 	int ret = 0;
506 
507 	lbs_deb_enter(LBS_DEB_CMD);
508 
509 	memset(&cmd, 0, sizeof(cmd));
510 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
511 	cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_RESET);
512 
513 	ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
514 
515 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
516 	return ret;
517 }
518 
519 /**
520  *  @brief Gets the inverted status of the mesh blinding table
521  *
522  *  Normally the firmware "blinds" or ignores traffic from mesh nodes in the
523  *  table, but an inverted table allows *only* traffic from nodes listed in
524  *  the table.
525  *
526  *  @param priv    	A pointer to struct lbs_private structure
527  *  @param invert  	On success, TRUE if the blinding table is inverted,
528  *                        FALSE if it is not inverted
529  *
530  *  @return 	   	0 on success, error on failure
531  */
lbs_mesh_bt_get_inverted(struct lbs_private * priv,bool * inverted)532 int lbs_mesh_bt_get_inverted(struct lbs_private *priv, bool *inverted)
533 {
534 	struct cmd_ds_bt_access cmd;
535 	int ret = 0;
536 
537 	lbs_deb_enter(LBS_DEB_CMD);
538 
539 	BUG_ON(inverted == NULL);
540 
541 	memset(&cmd, 0, sizeof(cmd));
542 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
543 	cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_GET_INVERT);
544 
545 	ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
546 	if (ret == 0)
547 		*inverted = !!cmd.id;
548 
549 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
550 	return ret;
551 }
552 
553 /**
554  *  @brief Sets the inverted status of the mesh blinding table
555  *
556  *  Normally the firmware "blinds" or ignores traffic from mesh nodes in the
557  *  table, but an inverted table allows *only* traffic from nodes listed in
558  *  the table.
559  *
560  *  @param priv    	A pointer to struct lbs_private structure
561  *  @param invert  	TRUE to invert the blinding table (only traffic from
562  *                         listed nodes allowed), FALSE to return it
563  *                         to normal state (listed nodes ignored)
564  *
565  *  @return 	   	0 on success, error on failure
566  */
lbs_mesh_bt_set_inverted(struct lbs_private * priv,bool inverted)567 int lbs_mesh_bt_set_inverted(struct lbs_private *priv, bool inverted)
568 {
569 	struct cmd_ds_bt_access cmd;
570 	int ret = 0;
571 
572 	lbs_deb_enter(LBS_DEB_CMD);
573 
574 	memset(&cmd, 0, sizeof(cmd));
575 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
576 	cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT);
577 	cmd.id = cpu_to_le32(!!inverted);
578 
579 	ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
580 
581 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
582 	return ret;
583 }
584 
585 /**
586  *  @brief List an entry in the mesh blinding table
587  *
588  *  @param priv    	A pointer to struct lbs_private structure
589  *  @param id		The ID of the entry to list
590  *  @param addr1	MAC address associated with the table entry
591  *
592  *  @return 	   	0 on success, error on failure
593  */
lbs_mesh_bt_get_entry(struct lbs_private * priv,u32 id,u8 * addr1)594 int lbs_mesh_bt_get_entry(struct lbs_private *priv, u32 id, u8 *addr1)
595 {
596 	struct cmd_ds_bt_access cmd;
597 	int ret = 0;
598 
599 	lbs_deb_enter(LBS_DEB_CMD);
600 
601 	BUG_ON(addr1 == NULL);
602 
603 	memset(&cmd, 0, sizeof(cmd));
604 	cmd.hdr.size = cpu_to_le16(sizeof(cmd));
605 	cmd.action = cpu_to_le16(CMD_ACT_BT_ACCESS_SET_INVERT);
606 	cmd.id = cpu_to_le32(id);
607 
608 	ret = lbs_cmd_with_response(priv, CMD_BT_ACCESS, &cmd);
609 	if (ret == 0)
610 		memcpy(addr1, cmd.addr1, sizeof(cmd.addr1));
611 
612 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
613 	return ret;
614 }
615 
616 /**
617  *  @brief Access the mesh forwarding table
618  *
619  *  @param priv    	A pointer to struct lbs_private structure
620  *  @param cmd_action	The forwarding table action to perform
621  *  @param cmd		The pre-filled FWT_ACCESS command
622  *
623  *  @return 	   	0 on success and 'cmd' will be filled with the
624  *                        firmware's response
625  */
lbs_cmd_fwt_access(struct lbs_private * priv,u16 cmd_action,struct cmd_ds_fwt_access * cmd)626 int lbs_cmd_fwt_access(struct lbs_private *priv, u16 cmd_action,
627 			struct cmd_ds_fwt_access *cmd)
628 {
629 	int ret;
630 
631 	lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
632 
633 	cmd->hdr.command = cpu_to_le16(CMD_FWT_ACCESS);
634 	cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_fwt_access));
635 	cmd->hdr.result = 0;
636 	cmd->action = cpu_to_le16(cmd_action);
637 
638 	ret = lbs_cmd_with_response(priv, CMD_FWT_ACCESS, cmd);
639 
640 	lbs_deb_leave_args(LBS_DEB_CMD, "ret %d", ret);
641 	return 0;
642 }
643 
lbs_mesh_access(struct lbs_private * priv,uint16_t cmd_action,struct cmd_ds_mesh_access * cmd)644 int lbs_mesh_access(struct lbs_private *priv, uint16_t cmd_action,
645 		    struct cmd_ds_mesh_access *cmd)
646 {
647 	int ret;
648 
649 	lbs_deb_enter_args(LBS_DEB_CMD, "action %d", cmd_action);
650 
651 	cmd->hdr.command = cpu_to_le16(CMD_MESH_ACCESS);
652 	cmd->hdr.size = cpu_to_le16(sizeof(*cmd));
653 	cmd->hdr.result = 0;
654 
655 	cmd->action = cpu_to_le16(cmd_action);
656 
657 	ret = lbs_cmd_with_response(priv, CMD_MESH_ACCESS, cmd);
658 
659 	lbs_deb_leave(LBS_DEB_CMD);
660 	return ret;
661 }
662 
__lbs_mesh_config_send(struct lbs_private * priv,struct cmd_ds_mesh_config * cmd,uint16_t action,uint16_t type)663 static int __lbs_mesh_config_send(struct lbs_private *priv,
664 				  struct cmd_ds_mesh_config *cmd,
665 				  uint16_t action, uint16_t type)
666 {
667 	int ret;
668 	u16 command = CMD_MESH_CONFIG_OLD;
669 
670 	lbs_deb_enter(LBS_DEB_CMD);
671 
672 	/*
673 	 * Command id is 0xac for v10 FW along with mesh interface
674 	 * id in bits 14-13-12.
675 	 */
676 	if (priv->mesh_tlv == TLV_TYPE_MESH_ID)
677 		command = CMD_MESH_CONFIG |
678 			  (MESH_IFACE_ID << MESH_IFACE_BIT_OFFSET);
679 
680 	cmd->hdr.command = cpu_to_le16(command);
681 	cmd->hdr.size = cpu_to_le16(sizeof(struct cmd_ds_mesh_config));
682 	cmd->hdr.result = 0;
683 
684 	cmd->type = cpu_to_le16(type);
685 	cmd->action = cpu_to_le16(action);
686 
687 	ret = lbs_cmd_with_response(priv, command, cmd);
688 
689 	lbs_deb_leave(LBS_DEB_CMD);
690 	return ret;
691 }
692 
lbs_mesh_config_send(struct lbs_private * priv,struct cmd_ds_mesh_config * cmd,uint16_t action,uint16_t type)693 int lbs_mesh_config_send(struct lbs_private *priv,
694 			 struct cmd_ds_mesh_config *cmd,
695 			 uint16_t action, uint16_t type)
696 {
697 	int ret;
698 
699 	if (!(priv->fwcapinfo & FW_CAPINFO_PERSISTENT_CONFIG))
700 		return -EOPNOTSUPP;
701 
702 	ret = __lbs_mesh_config_send(priv, cmd, action, type);
703 	return ret;
704 }
705 
706 /* This function is the CMD_MESH_CONFIG legacy function.  It only handles the
707  * START and STOP actions.  The extended actions supported by CMD_MESH_CONFIG
708  * are all handled by preparing a struct cmd_ds_mesh_config and passing it to
709  * lbs_mesh_config_send.
710  */
lbs_mesh_config(struct lbs_private * priv,uint16_t action,uint16_t chan)711 int lbs_mesh_config(struct lbs_private *priv, uint16_t action, uint16_t chan)
712 {
713 	struct cmd_ds_mesh_config cmd;
714 	struct mrvl_meshie *ie;
715 	DECLARE_SSID_BUF(ssid);
716 
717 	memset(&cmd, 0, sizeof(cmd));
718 	cmd.channel = cpu_to_le16(chan);
719 	ie = (struct mrvl_meshie *)cmd.data;
720 
721 	switch (action) {
722 	case CMD_ACT_MESH_CONFIG_START:
723 		ie->id = WLAN_EID_GENERIC;
724 		ie->val.oui[0] = 0x00;
725 		ie->val.oui[1] = 0x50;
726 		ie->val.oui[2] = 0x43;
727 		ie->val.type = MARVELL_MESH_IE_TYPE;
728 		ie->val.subtype = MARVELL_MESH_IE_SUBTYPE;
729 		ie->val.version = MARVELL_MESH_IE_VERSION;
730 		ie->val.active_protocol_id = MARVELL_MESH_PROTO_ID_HWMP;
731 		ie->val.active_metric_id = MARVELL_MESH_METRIC_ID;
732 		ie->val.mesh_capability = MARVELL_MESH_CAPABILITY;
733 		ie->val.mesh_id_len = priv->mesh_ssid_len;
734 		memcpy(ie->val.mesh_id, priv->mesh_ssid, priv->mesh_ssid_len);
735 		ie->len = sizeof(struct mrvl_meshie_val) -
736 			IEEE80211_MAX_SSID_LEN + priv->mesh_ssid_len;
737 		cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie_val));
738 		break;
739 	case CMD_ACT_MESH_CONFIG_STOP:
740 		break;
741 	default:
742 		return -1;
743 	}
744 	lbs_deb_cmd("mesh config action %d type %x channel %d SSID %s\n",
745 		    action, priv->mesh_tlv, chan,
746 		    print_ssid(ssid, priv->mesh_ssid, priv->mesh_ssid_len));
747 
748 	return __lbs_mesh_config_send(priv, &cmd, action, priv->mesh_tlv);
749 }
750 
751 
752 
753 /***************************************************************************
754  * Persistent configuration support
755  */
756 
mesh_get_default_parameters(struct device * dev,struct mrvl_mesh_defaults * defs)757 static int mesh_get_default_parameters(struct device *dev,
758 				       struct mrvl_mesh_defaults *defs)
759 {
760 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
761 	struct cmd_ds_mesh_config cmd;
762 	int ret;
763 
764 	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
765 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_GET,
766 				   CMD_TYPE_MESH_GET_DEFAULTS);
767 
768 	if (ret)
769 		return -EOPNOTSUPP;
770 
771 	memcpy(defs, &cmd.data[0], sizeof(struct mrvl_mesh_defaults));
772 
773 	return 0;
774 }
775 
776 /**
777  * @brief Get function for sysfs attribute bootflag
778  */
bootflag_get(struct device * dev,struct device_attribute * attr,char * buf)779 static ssize_t bootflag_get(struct device *dev,
780 			    struct device_attribute *attr, char *buf)
781 {
782 	struct mrvl_mesh_defaults defs;
783 	int ret;
784 
785 	ret = mesh_get_default_parameters(dev, &defs);
786 
787 	if (ret)
788 		return ret;
789 
790 	return snprintf(buf, 12, "%d\n", le32_to_cpu(defs.bootflag));
791 }
792 
793 /**
794  * @brief Set function for sysfs attribute bootflag
795  */
bootflag_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)796 static ssize_t bootflag_set(struct device *dev, struct device_attribute *attr,
797 			    const char *buf, size_t count)
798 {
799 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
800 	struct cmd_ds_mesh_config cmd;
801 	uint32_t datum;
802 	int ret;
803 
804 	memset(&cmd, 0, sizeof(cmd));
805 	ret = sscanf(buf, "%d", &datum);
806 	if ((ret != 1) || (datum > 1))
807 		return -EINVAL;
808 
809 	*((__le32 *)&cmd.data[0]) = cpu_to_le32(!!datum);
810 	cmd.length = cpu_to_le16(sizeof(uint32_t));
811 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
812 				   CMD_TYPE_MESH_SET_BOOTFLAG);
813 	if (ret)
814 		return ret;
815 
816 	return strlen(buf);
817 }
818 
819 /**
820  * @brief Get function for sysfs attribute boottime
821  */
boottime_get(struct device * dev,struct device_attribute * attr,char * buf)822 static ssize_t boottime_get(struct device *dev,
823 			    struct device_attribute *attr, char *buf)
824 {
825 	struct mrvl_mesh_defaults defs;
826 	int ret;
827 
828 	ret = mesh_get_default_parameters(dev, &defs);
829 
830 	if (ret)
831 		return ret;
832 
833 	return snprintf(buf, 12, "%d\n", defs.boottime);
834 }
835 
836 /**
837  * @brief Set function for sysfs attribute boottime
838  */
boottime_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)839 static ssize_t boottime_set(struct device *dev,
840 		struct device_attribute *attr, const char *buf, size_t count)
841 {
842 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
843 	struct cmd_ds_mesh_config cmd;
844 	uint32_t datum;
845 	int ret;
846 
847 	memset(&cmd, 0, sizeof(cmd));
848 	ret = sscanf(buf, "%d", &datum);
849 	if ((ret != 1) || (datum > 255))
850 		return -EINVAL;
851 
852 	/* A too small boot time will result in the device booting into
853 	 * standalone (no-host) mode before the host can take control of it,
854 	 * so the change will be hard to revert.  This may be a desired
855 	 * feature (e.g to configure a very fast boot time for devices that
856 	 * will not be attached to a host), but dangerous.  So I'm enforcing a
857 	 * lower limit of 20 seconds:  remove and recompile the driver if this
858 	 * does not work for you.
859 	 */
860 	datum = (datum < 20) ? 20 : datum;
861 	cmd.data[0] = datum;
862 	cmd.length = cpu_to_le16(sizeof(uint8_t));
863 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
864 				   CMD_TYPE_MESH_SET_BOOTTIME);
865 	if (ret)
866 		return ret;
867 
868 	return strlen(buf);
869 }
870 
871 /**
872  * @brief Get function for sysfs attribute channel
873  */
channel_get(struct device * dev,struct device_attribute * attr,char * buf)874 static ssize_t channel_get(struct device *dev,
875 			   struct device_attribute *attr, char *buf)
876 {
877 	struct mrvl_mesh_defaults defs;
878 	int ret;
879 
880 	ret = mesh_get_default_parameters(dev, &defs);
881 
882 	if (ret)
883 		return ret;
884 
885 	return snprintf(buf, 12, "%d\n", le16_to_cpu(defs.channel));
886 }
887 
888 /**
889  * @brief Set function for sysfs attribute channel
890  */
channel_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)891 static ssize_t channel_set(struct device *dev, struct device_attribute *attr,
892 			   const char *buf, size_t count)
893 {
894 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
895 	struct cmd_ds_mesh_config cmd;
896 	uint32_t datum;
897 	int ret;
898 
899 	memset(&cmd, 0, sizeof(cmd));
900 	ret = sscanf(buf, "%d", &datum);
901 	if (ret != 1 || datum < 1 || datum > 11)
902 		return -EINVAL;
903 
904 	*((__le16 *)&cmd.data[0]) = cpu_to_le16(datum);
905 	cmd.length = cpu_to_le16(sizeof(uint16_t));
906 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
907 				   CMD_TYPE_MESH_SET_DEF_CHANNEL);
908 	if (ret)
909 		return ret;
910 
911 	return strlen(buf);
912 }
913 
914 /**
915  * @brief Get function for sysfs attribute mesh_id
916  */
mesh_id_get(struct device * dev,struct device_attribute * attr,char * buf)917 static ssize_t mesh_id_get(struct device *dev, struct device_attribute *attr,
918 			   char *buf)
919 {
920 	struct mrvl_mesh_defaults defs;
921 	int ret;
922 
923 	ret = mesh_get_default_parameters(dev, &defs);
924 
925 	if (ret)
926 		return ret;
927 
928 	if (defs.meshie.val.mesh_id_len > IEEE80211_MAX_SSID_LEN) {
929 		lbs_pr_err("inconsistent mesh ID length");
930 		defs.meshie.val.mesh_id_len = IEEE80211_MAX_SSID_LEN;
931 	}
932 
933 	memcpy(buf, defs.meshie.val.mesh_id, defs.meshie.val.mesh_id_len);
934 	buf[defs.meshie.val.mesh_id_len] = '\n';
935 	buf[defs.meshie.val.mesh_id_len + 1] = '\0';
936 
937 	return defs.meshie.val.mesh_id_len + 1;
938 }
939 
940 /**
941  * @brief Set function for sysfs attribute mesh_id
942  */
mesh_id_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)943 static ssize_t mesh_id_set(struct device *dev, struct device_attribute *attr,
944 			   const char *buf, size_t count)
945 {
946 	struct cmd_ds_mesh_config cmd;
947 	struct mrvl_mesh_defaults defs;
948 	struct mrvl_meshie *ie;
949 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
950 	int len;
951 	int ret;
952 
953 	if (count < 2 || count > IEEE80211_MAX_SSID_LEN + 1)
954 		return -EINVAL;
955 
956 	memset(&cmd, 0, sizeof(struct cmd_ds_mesh_config));
957 	ie = (struct mrvl_meshie *) &cmd.data[0];
958 
959 	/* fetch all other Information Element parameters */
960 	ret = mesh_get_default_parameters(dev, &defs);
961 
962 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
963 
964 	/* transfer IE elements */
965 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
966 
967 	len = count - 1;
968 	memcpy(ie->val.mesh_id, buf, len);
969 	/* SSID len */
970 	ie->val.mesh_id_len = len;
971 	/* IE len */
972 	ie->len = sizeof(struct mrvl_meshie_val) - IEEE80211_MAX_SSID_LEN + len;
973 
974 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
975 				   CMD_TYPE_MESH_SET_MESH_IE);
976 	if (ret)
977 		return ret;
978 
979 	return strlen(buf);
980 }
981 
982 /**
983  * @brief Get function for sysfs attribute protocol_id
984  */
protocol_id_get(struct device * dev,struct device_attribute * attr,char * buf)985 static ssize_t protocol_id_get(struct device *dev,
986 			       struct device_attribute *attr, char *buf)
987 {
988 	struct mrvl_mesh_defaults defs;
989 	int ret;
990 
991 	ret = mesh_get_default_parameters(dev, &defs);
992 
993 	if (ret)
994 		return ret;
995 
996 	return snprintf(buf, 5, "%d\n", defs.meshie.val.active_protocol_id);
997 }
998 
999 /**
1000  * @brief Set function for sysfs attribute protocol_id
1001  */
protocol_id_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1002 static ssize_t protocol_id_set(struct device *dev,
1003 		struct device_attribute *attr, const char *buf, size_t count)
1004 {
1005 	struct cmd_ds_mesh_config cmd;
1006 	struct mrvl_mesh_defaults defs;
1007 	struct mrvl_meshie *ie;
1008 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
1009 	uint32_t datum;
1010 	int ret;
1011 
1012 	memset(&cmd, 0, sizeof(cmd));
1013 	ret = sscanf(buf, "%d", &datum);
1014 	if ((ret != 1) || (datum > 255))
1015 		return -EINVAL;
1016 
1017 	/* fetch all other Information Element parameters */
1018 	ret = mesh_get_default_parameters(dev, &defs);
1019 
1020 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
1021 
1022 	/* transfer IE elements */
1023 	ie = (struct mrvl_meshie *) &cmd.data[0];
1024 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
1025 	/* update protocol id */
1026 	ie->val.active_protocol_id = datum;
1027 
1028 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
1029 				   CMD_TYPE_MESH_SET_MESH_IE);
1030 	if (ret)
1031 		return ret;
1032 
1033 	return strlen(buf);
1034 }
1035 
1036 /**
1037  * @brief Get function for sysfs attribute metric_id
1038  */
metric_id_get(struct device * dev,struct device_attribute * attr,char * buf)1039 static ssize_t metric_id_get(struct device *dev,
1040 		struct device_attribute *attr, char *buf)
1041 {
1042 	struct mrvl_mesh_defaults defs;
1043 	int ret;
1044 
1045 	ret = mesh_get_default_parameters(dev, &defs);
1046 
1047 	if (ret)
1048 		return ret;
1049 
1050 	return snprintf(buf, 5, "%d\n", defs.meshie.val.active_metric_id);
1051 }
1052 
1053 /**
1054  * @brief Set function for sysfs attribute metric_id
1055  */
metric_id_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1056 static ssize_t metric_id_set(struct device *dev, struct device_attribute *attr,
1057 			     const char *buf, size_t count)
1058 {
1059 	struct cmd_ds_mesh_config cmd;
1060 	struct mrvl_mesh_defaults defs;
1061 	struct mrvl_meshie *ie;
1062 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
1063 	uint32_t datum;
1064 	int ret;
1065 
1066 	memset(&cmd, 0, sizeof(cmd));
1067 	ret = sscanf(buf, "%d", &datum);
1068 	if ((ret != 1) || (datum > 255))
1069 		return -EINVAL;
1070 
1071 	/* fetch all other Information Element parameters */
1072 	ret = mesh_get_default_parameters(dev, &defs);
1073 
1074 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
1075 
1076 	/* transfer IE elements */
1077 	ie = (struct mrvl_meshie *) &cmd.data[0];
1078 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
1079 	/* update metric id */
1080 	ie->val.active_metric_id = datum;
1081 
1082 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
1083 				   CMD_TYPE_MESH_SET_MESH_IE);
1084 	if (ret)
1085 		return ret;
1086 
1087 	return strlen(buf);
1088 }
1089 
1090 /**
1091  * @brief Get function for sysfs attribute capability
1092  */
capability_get(struct device * dev,struct device_attribute * attr,char * buf)1093 static ssize_t capability_get(struct device *dev,
1094 		struct device_attribute *attr, char *buf)
1095 {
1096 	struct mrvl_mesh_defaults defs;
1097 	int ret;
1098 
1099 	ret = mesh_get_default_parameters(dev, &defs);
1100 
1101 	if (ret)
1102 		return ret;
1103 
1104 	return snprintf(buf, 5, "%d\n", defs.meshie.val.mesh_capability);
1105 }
1106 
1107 /**
1108  * @brief Set function for sysfs attribute capability
1109  */
capability_set(struct device * dev,struct device_attribute * attr,const char * buf,size_t count)1110 static ssize_t capability_set(struct device *dev, struct device_attribute *attr,
1111 			      const char *buf, size_t count)
1112 {
1113 	struct cmd_ds_mesh_config cmd;
1114 	struct mrvl_mesh_defaults defs;
1115 	struct mrvl_meshie *ie;
1116 	struct lbs_private *priv = to_net_dev(dev)->ml_priv;
1117 	uint32_t datum;
1118 	int ret;
1119 
1120 	memset(&cmd, 0, sizeof(cmd));
1121 	ret = sscanf(buf, "%d", &datum);
1122 	if ((ret != 1) || (datum > 255))
1123 		return -EINVAL;
1124 
1125 	/* fetch all other Information Element parameters */
1126 	ret = mesh_get_default_parameters(dev, &defs);
1127 
1128 	cmd.length = cpu_to_le16(sizeof(struct mrvl_meshie));
1129 
1130 	/* transfer IE elements */
1131 	ie = (struct mrvl_meshie *) &cmd.data[0];
1132 	memcpy(ie, &defs.meshie, sizeof(struct mrvl_meshie));
1133 	/* update value */
1134 	ie->val.mesh_capability = datum;
1135 
1136 	ret = lbs_mesh_config_send(priv, &cmd, CMD_ACT_MESH_CONFIG_SET,
1137 				   CMD_TYPE_MESH_SET_MESH_IE);
1138 	if (ret)
1139 		return ret;
1140 
1141 	return strlen(buf);
1142 }
1143 
1144 
1145 static DEVICE_ATTR(bootflag, 0644, bootflag_get, bootflag_set);
1146 static DEVICE_ATTR(boottime, 0644, boottime_get, boottime_set);
1147 static DEVICE_ATTR(channel, 0644, channel_get, channel_set);
1148 static DEVICE_ATTR(mesh_id, 0644, mesh_id_get, mesh_id_set);
1149 static DEVICE_ATTR(protocol_id, 0644, protocol_id_get, protocol_id_set);
1150 static DEVICE_ATTR(metric_id, 0644, metric_id_get, metric_id_set);
1151 static DEVICE_ATTR(capability, 0644, capability_get, capability_set);
1152 
1153 static struct attribute *boot_opts_attrs[] = {
1154 	&dev_attr_bootflag.attr,
1155 	&dev_attr_boottime.attr,
1156 	&dev_attr_channel.attr,
1157 	NULL
1158 };
1159 
1160 static struct attribute_group boot_opts_group = {
1161 	.name = "boot_options",
1162 	.attrs = boot_opts_attrs,
1163 };
1164 
1165 static struct attribute *mesh_ie_attrs[] = {
1166 	&dev_attr_mesh_id.attr,
1167 	&dev_attr_protocol_id.attr,
1168 	&dev_attr_metric_id.attr,
1169 	&dev_attr_capability.attr,
1170 	NULL
1171 };
1172 
1173 static struct attribute_group mesh_ie_group = {
1174 	.name = "mesh_ie",
1175 	.attrs = mesh_ie_attrs,
1176 };
1177 
lbs_persist_config_init(struct net_device * dev)1178 void lbs_persist_config_init(struct net_device *dev)
1179 {
1180 	int ret;
1181 	ret = sysfs_create_group(&(dev->dev.kobj), &boot_opts_group);
1182 	ret = sysfs_create_group(&(dev->dev.kobj), &mesh_ie_group);
1183 }
1184 
lbs_persist_config_remove(struct net_device * dev)1185 void lbs_persist_config_remove(struct net_device *dev)
1186 {
1187 	sysfs_remove_group(&(dev->dev.kobj), &boot_opts_group);
1188 	sysfs_remove_group(&(dev->dev.kobj), &mesh_ie_group);
1189 }
1190 
1191 
1192 
1193 /***************************************************************************
1194  * Ethtool related
1195  */
1196 
1197 static const char *mesh_stat_strings[] = {
1198 			"drop_duplicate_bcast",
1199 			"drop_ttl_zero",
1200 			"drop_no_fwd_route",
1201 			"drop_no_buffers",
1202 			"fwded_unicast_cnt",
1203 			"fwded_bcast_cnt",
1204 			"drop_blind_table",
1205 			"tx_failed_cnt"
1206 };
1207 
lbs_mesh_ethtool_get_stats(struct net_device * dev,struct ethtool_stats * stats,uint64_t * data)1208 void lbs_mesh_ethtool_get_stats(struct net_device *dev,
1209 	struct ethtool_stats *stats, uint64_t *data)
1210 {
1211 	struct lbs_private *priv = dev->ml_priv;
1212 	struct cmd_ds_mesh_access mesh_access;
1213 	int ret;
1214 
1215 	lbs_deb_enter(LBS_DEB_ETHTOOL);
1216 
1217 	/* Get Mesh Statistics */
1218 	ret = lbs_mesh_access(priv, CMD_ACT_MESH_GET_STATS, &mesh_access);
1219 
1220 	if (ret) {
1221 		memset(data, 0, MESH_STATS_NUM*(sizeof(uint64_t)));
1222 		return;
1223 	}
1224 
1225 	priv->mstats.fwd_drop_rbt = le32_to_cpu(mesh_access.data[0]);
1226 	priv->mstats.fwd_drop_ttl = le32_to_cpu(mesh_access.data[1]);
1227 	priv->mstats.fwd_drop_noroute = le32_to_cpu(mesh_access.data[2]);
1228 	priv->mstats.fwd_drop_nobuf = le32_to_cpu(mesh_access.data[3]);
1229 	priv->mstats.fwd_unicast_cnt = le32_to_cpu(mesh_access.data[4]);
1230 	priv->mstats.fwd_bcast_cnt = le32_to_cpu(mesh_access.data[5]);
1231 	priv->mstats.drop_blind = le32_to_cpu(mesh_access.data[6]);
1232 	priv->mstats.tx_failed_cnt = le32_to_cpu(mesh_access.data[7]);
1233 
1234 	data[0] = priv->mstats.fwd_drop_rbt;
1235 	data[1] = priv->mstats.fwd_drop_ttl;
1236 	data[2] = priv->mstats.fwd_drop_noroute;
1237 	data[3] = priv->mstats.fwd_drop_nobuf;
1238 	data[4] = priv->mstats.fwd_unicast_cnt;
1239 	data[5] = priv->mstats.fwd_bcast_cnt;
1240 	data[6] = priv->mstats.drop_blind;
1241 	data[7] = priv->mstats.tx_failed_cnt;
1242 
1243 	lbs_deb_enter(LBS_DEB_ETHTOOL);
1244 }
1245 
lbs_mesh_ethtool_get_sset_count(struct net_device * dev,int sset)1246 int lbs_mesh_ethtool_get_sset_count(struct net_device *dev, int sset)
1247 {
1248 	struct lbs_private *priv = dev->ml_priv;
1249 
1250 	if (sset == ETH_SS_STATS && dev == priv->mesh_dev)
1251 		return MESH_STATS_NUM;
1252 
1253 	return -EOPNOTSUPP;
1254 }
1255 
lbs_mesh_ethtool_get_strings(struct net_device * dev,uint32_t stringset,uint8_t * s)1256 void lbs_mesh_ethtool_get_strings(struct net_device *dev,
1257 	uint32_t stringset, uint8_t *s)
1258 {
1259 	int i;
1260 
1261 	lbs_deb_enter(LBS_DEB_ETHTOOL);
1262 
1263 	switch (stringset) {
1264 	case ETH_SS_STATS:
1265 		for (i = 0; i < MESH_STATS_NUM; i++) {
1266 			memcpy(s + i * ETH_GSTRING_LEN,
1267 					mesh_stat_strings[i],
1268 					ETH_GSTRING_LEN);
1269 		}
1270 		break;
1271 	}
1272 	lbs_deb_enter(LBS_DEB_ETHTOOL);
1273 }
1274