dhd_linux.c 22.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
/*
 * Copyright (c) 2010 Broadcom Corporation
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <linux/kernel.h>
#include <linux/etherdevice.h>
19
#include <linux/module.h>
20 21 22 23 24 25 26 27 28 29
#include <net/cfg80211.h>
#include <net/rtnetlink.h>
#include <brcmu_utils.h>
#include <brcmu_wifi.h>

#include "dhd.h"
#include "dhd_bus.h"
#include "dhd_proto.h"
#include "dhd_dbg.h"
#include "wl_cfg80211.h"
30
#include "fwil.h"
31 32

MODULE_AUTHOR("Broadcom Corporation");
H
Hante Meuleman 已提交
33 34
MODULE_DESCRIPTION("Broadcom 802.11 wireless LAN fullmac driver.");
MODULE_SUPPORTED_DEVICE("Broadcom 802.11 WLAN fullmac cards");
35 36
MODULE_LICENSE("Dual BSD/GPL");

37
#define MAX_WAIT_FOR_8021X_TX		50	/* msecs */
38 39

/* Error bits */
40
int brcmf_msg_level;
41 42 43 44 45 46
module_param(brcmf_msg_level, int, 0);


char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
{
	if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
47
		brcmf_err("ifidx %d out of range\n", ifidx);
48 49 50
		return "<if_bad>";
	}

51
	if (drvr->iflist[ifidx] == NULL) {
52
		brcmf_err("null i/f %d\n", ifidx);
53 54 55
		return "<if_null>";
	}

56 57
	if (drvr->iflist[ifidx]->ndev)
		return drvr->iflist[ifidx]->ndev->name;
58 59 60 61 62 63

	return "<if_none>";
}

static void _brcmf_set_multicast_list(struct work_struct *work)
{
64
	struct brcmf_if *ifp;
65 66
	struct net_device *ndev;
	struct netdev_hw_addr *ha;
67
	u32 cmd_value, cnt;
68 69
	__le32 cnt_le;
	char *buf, *bufp;
70 71
	u32 buflen;
	s32 err;
72

73 74
	brcmf_dbg(TRACE, "enter\n");

75
	ifp = container_of(work, struct brcmf_if, multicast_work);
76
	ndev = ifp->ndev;
77 78

	/* Determine initial value of allmulti flag */
79
	cmd_value = (ndev->flags & IFF_ALLMULTI) ? true : false;
80 81

	/* Send down the multicast list first. */
82 83 84 85
	cnt = netdev_mc_count(ndev);
	buflen = sizeof(cnt) + (cnt * ETH_ALEN);
	buf = kmalloc(buflen, GFP_ATOMIC);
	if (!buf)
86
		return;
87
	bufp = buf;
88 89

	cnt_le = cpu_to_le32(cnt);
90
	memcpy(bufp, &cnt_le, sizeof(cnt_le));
91 92 93 94 95 96 97 98 99 100
	bufp += sizeof(cnt_le);

	netdev_for_each_mc_addr(ha, ndev) {
		if (!cnt)
			break;
		memcpy(bufp, ha->addr, ETH_ALEN);
		bufp += ETH_ALEN;
		cnt--;
	}

101 102
	err = brcmf_fil_iovar_data_set(ifp, "mcast_list", buf, buflen);
	if (err < 0) {
103
		brcmf_err("Setting mcast_list failed, %d\n", err);
104
		cmd_value = cnt ? true : cmd_value;
105 106 107 108
	}

	kfree(buf);

109 110
	/*
	 * Now send the allmulti setting.  This is based on the setting in the
111 112 113
	 * net_device flags, but might be modified above to be turned on if we
	 * were trying to set some addresses and dongle rejected it...
	 */
114 115
	err = brcmf_fil_iovar_int_set(ifp, "allmulti", cmd_value);
	if (err < 0)
116
		brcmf_err("Setting allmulti failed, %d\n", err);
117 118 119 120 121

	/*Finally, pick up the PROMISC flag */
	cmd_value = (ndev->flags & IFF_PROMISC) ? true : false;
	err = brcmf_fil_cmd_int_set(ifp, BRCMF_C_SET_PROMISC, cmd_value);
	if (err < 0)
122
		brcmf_err("Setting BRCMF_C_SET_PROMISC failed, %d\n",
123
			  err);
124 125 126 127 128
}

static void
_brcmf_set_mac_address(struct work_struct *work)
{
129 130
	struct brcmf_if *ifp;
	s32 err;
131 132 133

	brcmf_dbg(TRACE, "enter\n");

134 135
	ifp = container_of(work, struct brcmf_if, setmacaddr_work);
	err = brcmf_fil_iovar_data_set(ifp, "cur_etheraddr", ifp->mac_addr,
136 137
				       ETH_ALEN);
	if (err < 0) {
138
		brcmf_err("Setting cur_etheraddr failed, %d\n", err);
139 140
	} else {
		brcmf_dbg(TRACE, "MAC address updated to %pM\n",
141 142
			  ifp->mac_addr);
		memcpy(ifp->ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
143
	}
144 145 146 147
}

static int brcmf_netdev_set_mac_address(struct net_device *ndev, void *addr)
{
148
	struct brcmf_if *ifp = netdev_priv(ndev);
149 150
	struct sockaddr *sa = (struct sockaddr *)addr;

151 152
	memcpy(&ifp->mac_addr, sa->sa_data, ETH_ALEN);
	schedule_work(&ifp->setmacaddr_work);
153 154 155 156 157
	return 0;
}

static void brcmf_netdev_set_multicast_list(struct net_device *ndev)
{
158
	struct brcmf_if *ifp = netdev_priv(ndev);
159

160
	schedule_work(&ifp->multicast_work);
161 162 163 164 165
}

static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
{
	int ret;
166
	struct brcmf_if *ifp = netdev_priv(ndev);
167
	struct brcmf_pub *drvr = ifp->drvr;
168
	struct ethhdr *eh;
169 170 171 172

	brcmf_dbg(TRACE, "Enter\n");

	/* Reject if down */
173
	if (!drvr->bus_if->drvr_up ||
174
	    (drvr->bus_if->state != BRCMF_BUS_DATA)) {
175
		brcmf_err("xmit rejected drvup=%d state=%d\n",
176
			  drvr->bus_if->drvr_up,
177
			  drvr->bus_if->state);
178 179 180 181
		netif_stop_queue(ndev);
		return -ENODEV;
	}

182
	if (!drvr->iflist[ifp->idx]) {
183
		brcmf_err("bad ifidx %d\n", ifp->idx);
184 185 186 187 188
		netif_stop_queue(ndev);
		return -ENODEV;
	}

	/* Make sure there's enough room for any header */
189
	if (skb_headroom(skb) < drvr->hdrlen) {
190 191 192
		struct sk_buff *skb2;

		brcmf_dbg(INFO, "%s: insufficient headroom\n",
193
			  brcmf_ifname(drvr, ifp->idx));
194
		drvr->bus_if->tx_realloc++;
195
		skb2 = skb_realloc_headroom(skb, drvr->hdrlen);
196 197 198
		dev_kfree_skb(skb);
		skb = skb2;
		if (skb == NULL) {
199
			brcmf_err("%s: skb_realloc_headroom failed\n",
200
				  brcmf_ifname(drvr, ifp->idx));
201 202 203 204 205
			ret = -ENOMEM;
			goto done;
		}
	}

206 207 208 209 210
	/* validate length for ether packet */
	if (skb->len < sizeof(*eh)) {
		ret = -EINVAL;
		dev_kfree_skb(skb);
		goto done;
211 212
	}

213 214 215 216 217 218 219
	/* handle ethernet header */
	eh = (struct ethhdr *)(skb->data);
	if (is_multicast_ether_addr(eh->h_dest))
		drvr->tx_multicast++;
	if (ntohs(eh->h_proto) == ETH_P_PAE)
		atomic_inc(&drvr->pend_8021x_cnt);

220 221 222 223
	/* If the protocol uses a data header, apply it */
	brcmf_proto_hdrpush(drvr, ifp->idx, skb);

	/* Use bus module to send data frame */
A
Arend van Spriel 已提交
224
	ret =  brcmf_bus_txdata(drvr->bus_if, skb);
225 226 227

done:
	if (ret)
228
		drvr->bus_if->dstats.tx_dropped++;
229
	else
230
		drvr->bus_if->dstats.tx_packets++;
231 232 233 234 235

	/* Return ok: we always eat the packet */
	return 0;
}

236
void brcmf_txflowblock(struct device *dev, bool state)
237 238
{
	struct net_device *ndev;
239 240
	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
	struct brcmf_pub *drvr = bus_if->drvr;
241
	int i;
242 243 244

	brcmf_dbg(TRACE, "Enter\n");

245 246 247 248 249 250 251 252
	for (i = 0; i < BRCMF_MAX_IFS; i++)
		if (drvr->iflist[i]) {
			ndev = drvr->iflist[i]->ndev;
			if (state)
				netif_stop_queue(ndev);
			else
				netif_wake_queue(ndev);
		}
253 254
}

255
void brcmf_rx_frames(struct device *dev, struct sk_buff_head *skb_list)
256 257 258
{
	unsigned char *eth;
	uint len;
259
	struct sk_buff *skb, *pnext;
260
	struct brcmf_if *ifp;
261 262
	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
	struct brcmf_pub *drvr = bus_if->drvr;
263 264
	u8 ifidx;
	int ret;
265 266 267

	brcmf_dbg(TRACE, "Enter\n");

268 269
	skb_queue_walk_safe(skb_list, skb, pnext) {
		skb_unlink(skb, skb_list);
270

271 272 273 274 275 276 277 278 279 280
		/* process and remove protocol-specific header
		 */
		ret = brcmf_proto_hdrpull(drvr, &ifidx, skb);
		if (ret < 0) {
			if (ret != -ENODATA)
				bus_if->dstats.rx_errors++;
			brcmu_pkt_buf_free_skb(skb);
			continue;
		}

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
		/* Get the protocol, maintain skb around eth_type_trans()
		 * The main reason for this hack is for the limitation of
		 * Linux 2.4 where 'eth_type_trans' uses the
		 * 'net->hard_header_len'
		 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
		 * coping of the packet coming from the network stack to add
		 * BDC, Hardware header etc, during network interface
		 * registration
		 * we set the 'net->hard_header_len' to ETH_HLEN + extra space
		 * required
		 * for BDC, Hardware header etc. and not just the ETH_HLEN
		 */
		eth = skb->data;
		len = skb->len;

296
		ifp = drvr->iflist[ifidx];
297
		if (ifp == NULL)
298
			ifp = drvr->iflist[0];
299

300 301 302 303 304 305
		if (!ifp || !ifp->ndev ||
		    ifp->ndev->reg_state != NETREG_REGISTERED) {
			brcmu_pkt_buf_free_skb(skb);
			continue;
		}

306 307 308 309
		skb->dev = ifp->ndev;
		skb->protocol = eth_type_trans(skb, skb->dev);

		if (skb->pkt_type == PACKET_MULTICAST)
310
			bus_if->dstats.multicast++;
311 312 313 314 315 316 317 318

		skb->data = eth;
		skb->len = len;

		/* Strip header, count, deliver upward */
		skb_pull(skb, ETH_HLEN);

		/* Process special event packets and then discard them */
319
		brcmf_fweh_process_skb(drvr, skb, &ifidx);
320

321 322
		if (drvr->iflist[ifidx]) {
			ifp = drvr->iflist[ifidx];
323
			ifp->ndev->last_rx = jiffies;
324
		}
325

326 327
		bus_if->dstats.rx_bytes += skb->len;
		bus_if->dstats.rx_packets++;	/* Local count */
328 329 330 331 332 333 334 335 336 337 338 339 340 341

		if (in_interrupt())
			netif_rx(skb);
		else
			/* If the receive is not processed inside an ISR,
			 * the softirqd must be woken explicitly to service
			 * the NET_RX_SOFTIRQ.  In 2.6 kernels, this is handled
			 * by netif_rx_ni(), but in earlier kernels, we need
			 * to do it manually.
			 */
			netif_rx_ni(skb);
	}
}

342
void brcmf_txcomplete(struct device *dev, struct sk_buff *txp, bool success)
343
{
344
	u8 ifidx;
345 346
	struct ethhdr *eh;
	u16 type;
347 348
	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
	struct brcmf_pub *drvr = bus_if->drvr;
349

350
	brcmf_proto_hdrpull(drvr, &ifidx, txp);
351 352 353 354

	eh = (struct ethhdr *)(txp->data);
	type = ntohs(eh->h_proto);

355
	if (type == ETH_P_PAE) {
356
		atomic_dec(&drvr->pend_8021x_cnt);
357 358 359
		if (waitqueue_active(&drvr->pend_8021x_wait))
			wake_up(&drvr->pend_8021x_wait);
	}
360 361 362 363
}

static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *ndev)
{
364
	struct brcmf_if *ifp = netdev_priv(ndev);
365
	struct brcmf_bus *bus_if = ifp->drvr->bus_if;
366 367 368 369

	brcmf_dbg(TRACE, "Enter\n");

	/* Copy dongle stats to net device stats */
370 371 372 373 374 375 376 377 378
	ifp->stats.rx_packets = bus_if->dstats.rx_packets;
	ifp->stats.tx_packets = bus_if->dstats.tx_packets;
	ifp->stats.rx_bytes = bus_if->dstats.rx_bytes;
	ifp->stats.tx_bytes = bus_if->dstats.tx_bytes;
	ifp->stats.rx_errors = bus_if->dstats.rx_errors;
	ifp->stats.tx_errors = bus_if->dstats.tx_errors;
	ifp->stats.rx_dropped = bus_if->dstats.rx_dropped;
	ifp->stats.tx_dropped = bus_if->dstats.tx_dropped;
	ifp->stats.multicast = bus_if->dstats.multicast;
379 380 381 382

	return &ifp->stats;
}

383 384 385 386 387
/*
 * Set current toe component enables in toe_ol iovar,
 * and set toe global enable iovar
 */
static int brcmf_toe_set(struct brcmf_if *ifp, u32 toe_ol)
388
{
389
	s32 err;
390

391 392
	err = brcmf_fil_iovar_int_set(ifp, "toe_ol", toe_ol);
	if (err < 0) {
393
		brcmf_err("Setting toe_ol failed, %d\n", err);
394
		return err;
395 396
	}

397 398
	err = brcmf_fil_iovar_int_set(ifp, "toe", (toe_ol != 0));
	if (err < 0)
399
		brcmf_err("Setting toe failed, %d\n", err);
400

401
	return err;
402 403 404 405 406 407

}

static void brcmf_ethtool_get_drvinfo(struct net_device *ndev,
				    struct ethtool_drvinfo *info)
{
408
	struct brcmf_if *ifp = netdev_priv(ndev);
409
	struct brcmf_pub *drvr = ifp->drvr;
410 411

	sprintf(info->driver, KBUILD_MODNAME);
412
	sprintf(info->version, "%lu", drvr->drv_version);
A
Arend van Spriel 已提交
413
	sprintf(info->bus_info, "%s", dev_name(drvr->bus_if->dev));
414 415
}

416 417
static const struct ethtool_ops brcmf_ethtool_ops = {
	.get_drvinfo = brcmf_ethtool_get_drvinfo,
418 419
};

420
static int brcmf_ethtool(struct brcmf_if *ifp, void __user *uaddr)
421
{
422
	struct brcmf_pub *drvr = ifp->drvr;
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454
	struct ethtool_drvinfo info;
	char drvname[sizeof(info.driver)];
	u32 cmd;
	struct ethtool_value edata;
	u32 toe_cmpnt, csum_dir;
	int ret;

	brcmf_dbg(TRACE, "Enter\n");

	/* all ethtool calls start with a cmd word */
	if (copy_from_user(&cmd, uaddr, sizeof(u32)))
		return -EFAULT;

	switch (cmd) {
	case ETHTOOL_GDRVINFO:
		/* Copy out any request driver name */
		if (copy_from_user(&info, uaddr, sizeof(info)))
			return -EFAULT;
		strncpy(drvname, info.driver, sizeof(info.driver));
		drvname[sizeof(info.driver) - 1] = '\0';

		/* clear struct for return */
		memset(&info, 0, sizeof(info));
		info.cmd = cmd;

		/* if requested, identify ourselves */
		if (strcmp(drvname, "?dhd") == 0) {
			sprintf(info.driver, "dhd");
			strcpy(info.version, BRCMF_VERSION_STR);
		}

		/* otherwise, require dongle to be up */
455
		else if (!drvr->bus_if->drvr_up) {
456
			brcmf_err("dongle is not up\n");
457 458 459 460
			return -ENODEV;
		}
		/* finally, report dongle driver type */
		else
461
			sprintf(info.driver, "wl");
462

463
		sprintf(info.version, "%lu", drvr->drv_version);
464 465
		if (copy_to_user(uaddr, &info, sizeof(info)))
			return -EFAULT;
466
		brcmf_dbg(TRACE, "given %*s, returning %s\n",
467 468 469 470 471 472
			  (int)sizeof(drvname), drvname, info.driver);
		break;

		/* Get toe offload components from dongle */
	case ETHTOOL_GRXCSUM:
	case ETHTOOL_GTXCSUM:
473
		ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt);
474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493
		if (ret < 0)
			return ret;

		csum_dir =
		    (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;

		edata.cmd = cmd;
		edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;

		if (copy_to_user(uaddr, &edata, sizeof(edata)))
			return -EFAULT;
		break;

		/* Set toe offload components in dongle */
	case ETHTOOL_SRXCSUM:
	case ETHTOOL_STXCSUM:
		if (copy_from_user(&edata, uaddr, sizeof(edata)))
			return -EFAULT;

		/* Read the current settings, update and write back */
494
		ret = brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_cmpnt);
495 496 497 498 499 500 501 502 503 504 505
		if (ret < 0)
			return ret;

		csum_dir =
		    (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;

		if (edata.data != 0)
			toe_cmpnt |= csum_dir;
		else
			toe_cmpnt &= ~csum_dir;

506
		ret = brcmf_toe_set(ifp, toe_cmpnt);
507 508 509 510 511 512
		if (ret < 0)
			return ret;

		/* If setting TX checksum mode, tell Linux the new mode */
		if (cmd == ETHTOOL_STXCSUM) {
			if (edata.data)
513
				ifp->ndev->features |= NETIF_F_IP_CSUM;
514
			else
515
				ifp->ndev->features &= ~NETIF_F_IP_CSUM;
516 517 518 519 520 521 522 523 524 525 526 527 528 529
		}

		break;

	default:
		return -EOPNOTSUPP;
	}

	return 0;
}

static int brcmf_netdev_ioctl_entry(struct net_device *ndev, struct ifreq *ifr,
				    int cmd)
{
530
	struct brcmf_if *ifp = netdev_priv(ndev);
531
	struct brcmf_pub *drvr = ifp->drvr;
532

533
	brcmf_dbg(TRACE, "ifidx %d, cmd 0x%04x\n", ifp->idx, cmd);
534

535
	if (!drvr->iflist[ifp->idx])
536 537 538
		return -1;

	if (cmd == SIOCETHTOOL)
539
		return brcmf_ethtool(ifp, ifr->ifr_data);
540 541 542 543 544 545

	return -EOPNOTSUPP;
}

static int brcmf_netdev_stop(struct net_device *ndev)
{
546
	struct brcmf_if *ifp = netdev_priv(ndev);
547
	struct brcmf_pub *drvr = ifp->drvr;
548 549

	brcmf_dbg(TRACE, "Enter\n");
550

551
	if (drvr->bus_if->drvr_up == 0)
552 553
		return 0;

554
	brcmf_cfg80211_down(ndev);
555

556
	/* Set state and stop OS transmissions */
J
John W. Linville 已提交
557
	drvr->bus_if->drvr_up = false;
558 559 560 561 562 563 564
	netif_stop_queue(ndev);

	return 0;
}

static int brcmf_netdev_open(struct net_device *ndev)
{
565
	struct brcmf_if *ifp = netdev_priv(ndev);
566
	struct brcmf_pub *drvr = ifp->drvr;
567
	struct brcmf_bus *bus_if = drvr->bus_if;
568 569 570
	u32 toe_ol;
	s32 ret = 0;

571
	brcmf_dbg(TRACE, "ifidx %d\n", ifp->idx);
572

573 574
	/* If bus is not ready, can't continue */
	if (bus_if->state != BRCMF_BUS_DATA) {
575
		brcmf_err("failed bus is not ready\n");
576 577
		return -EAGAIN;
	}
578

579
	atomic_set(&drvr->pend_8021x_cnt, 0);
580

581
	memcpy(ndev->dev_addr, drvr->mac, ETH_ALEN);
582

583 584 585 586 587 588 589 590
	/* Get current TOE mode from dongle */
	if (brcmf_fil_iovar_int_get(ifp, "toe_ol", &toe_ol) >= 0
	    && (toe_ol & TOE_TX_CSUM_OL) != 0)
		drvr->iflist[ifp->idx]->ndev->features |=
			NETIF_F_IP_CSUM;
	else
		drvr->iflist[ifp->idx]->ndev->features &=
			~NETIF_F_IP_CSUM;
591 592

	/* make sure RF is ready for work */
593
	brcmf_fil_cmd_int_set(ifp, BRCMF_C_UP, 0);
594

595 596
	/* Allow transmit calls */
	netif_start_queue(ndev);
J
John W. Linville 已提交
597
	drvr->bus_if->drvr_up = true;
598
	if (brcmf_cfg80211_up(ndev)) {
599
		brcmf_err("failed to bring up cfg80211\n");
600 601 602 603 604 605
		return -1;
	}

	return ret;
}

606 607 608 609 610 611 612 613 614 615
static const struct net_device_ops brcmf_netdev_ops_pri = {
	.ndo_open = brcmf_netdev_open,
	.ndo_stop = brcmf_netdev_stop,
	.ndo_get_stats = brcmf_netdev_get_stats,
	.ndo_do_ioctl = brcmf_netdev_ioctl_entry,
	.ndo_start_xmit = brcmf_netdev_start_xmit,
	.ndo_set_mac_address = brcmf_netdev_set_mac_address,
	.ndo_set_rx_mode = brcmf_netdev_set_multicast_list
};

616 617 618 619 620 621 622 623 624 625
static const struct net_device_ops brcmf_netdev_ops_virt = {
	.ndo_open = brcmf_cfg80211_up,
	.ndo_stop = brcmf_cfg80211_down,
	.ndo_get_stats = brcmf_netdev_get_stats,
	.ndo_do_ioctl = brcmf_netdev_ioctl_entry,
	.ndo_start_xmit = brcmf_netdev_start_xmit,
	.ndo_set_mac_address = brcmf_netdev_set_mac_address,
	.ndo_set_rx_mode = brcmf_netdev_set_multicast_list
};

626
int brcmf_net_attach(struct brcmf_if *ifp)
627
{
628
	struct brcmf_pub *drvr = ifp->drvr;
629 630
	struct net_device *ndev;

631 632
	brcmf_dbg(TRACE, "ifidx %d mac %pM\n", ifp->idx, ifp->mac_addr);
	ndev = ifp->ndev;
633

634 635 636 637 638
	/* set appropriate operations */
	if (!ifp->idx)
		ndev->netdev_ops = &brcmf_netdev_ops_pri;
	else
		ndev->netdev_ops = &brcmf_netdev_ops_virt;
639 640 641 642 643 644 645

	ndev->hard_header_len = ETH_HLEN + drvr->hdrlen;
	ndev->ethtool_ops = &brcmf_ethtool_ops;

	drvr->rxsz = ndev->mtu + ndev->hard_header_len +
			      drvr->hdrlen;

646 647
	/* set the mac address */
	memcpy(ndev->dev_addr, ifp->mac_addr, ETH_ALEN);
648 649

	if (register_netdev(ndev) != 0) {
650
		brcmf_err("couldn't register the net device\n");
651 652 653 654 655 656 657 658 659 660 661 662
		goto fail;
	}

	brcmf_dbg(INFO, "%s: Broadcom Dongle Host Driver\n", ndev->name);

	return 0;

fail:
	ndev->netdev_ops = NULL;
	return -EBADE;
}

663
struct brcmf_if *brcmf_add_if(struct brcmf_pub *drvr, int ifidx, s32 bssidx,
664
			      char *name, u8 *addr_mask)
665 666
{
	struct brcmf_if *ifp;
667
	struct net_device *ndev;
668
	int i;
669

670
	brcmf_dbg(TRACE, "idx %d\n", ifidx);
671

672
	ifp = drvr->iflist[ifidx];
673 674 675 676 677
	/*
	 * Delete the existing interface before overwriting it
	 * in case we missed the BRCMF_E_IF_DEL event.
	 */
	if (ifp) {
678
		brcmf_err("ERROR: netdev:%s already exists\n",
679
			  ifp->ndev->name);
680 681 682 683 684 685
		if (ifidx) {
			netif_stop_queue(ifp->ndev);
			unregister_netdev(ifp->ndev);
			free_netdev(ifp->ndev);
			drvr->iflist[ifidx] = NULL;
		} else {
686
			brcmf_err("ignore IF event\n");
687 688
			return ERR_PTR(-EINVAL);
		}
689
	}
690

691
	/* Allocate netdev, including space for private structure */
692 693
	ndev = alloc_netdev(sizeof(struct brcmf_if), name, ether_setup);
	if (!ndev) {
694
		brcmf_err("OOM - alloc_netdev\n");
695
		return ERR_PTR(-ENOMEM);
696
	}
697

698 699
	ifp = netdev_priv(ndev);
	ifp->ndev = ndev;
700 701
	ifp->drvr = drvr;
	drvr->iflist[ifidx] = ifp;
702
	ifp->idx = ifidx;
703
	ifp->bssidx = bssidx;
704 705 706 707

	INIT_WORK(&ifp->setmacaddr_work, _brcmf_set_mac_address);
	INIT_WORK(&ifp->multicast_work, _brcmf_set_multicast_list);

708 709 710
	if (addr_mask != NULL)
		for (i = 0; i < ETH_ALEN; i++)
			ifp->mac_addr[i] = drvr->mac[i] ^ addr_mask[i];
711

712 713
	brcmf_dbg(TRACE, " ==== pid:%x, if:%s (%pM) created ===\n",
		  current->pid, ifp->ndev->name, ifp->mac_addr);
714

715
	return ifp;
716 717
}

718
void brcmf_del_if(struct brcmf_pub *drvr, int ifidx)
719 720 721 722 723
{
	struct brcmf_if *ifp;

	brcmf_dbg(TRACE, "idx %d\n", ifidx);

724
	ifp = drvr->iflist[ifidx];
725
	if (!ifp) {
726
		brcmf_err("Null interface\n");
727 728
		return;
	}
729 730 731 732 733 734 735 736 737 738 739
	if (ifp->ndev) {
		if (ifidx == 0) {
			if (ifp->ndev->netdev_ops == &brcmf_netdev_ops_pri) {
				rtnl_lock();
				brcmf_netdev_stop(ifp->ndev);
				rtnl_unlock();
			}
		} else {
			netif_stop_queue(ifp->ndev);
		}

740 741 742
		cancel_work_sync(&ifp->setmacaddr_work);
		cancel_work_sync(&ifp->multicast_work);

743
		unregister_netdev(ifp->ndev);
744
		drvr->iflist[ifidx] = NULL;
745
		if (ifidx == 0)
746
			brcmf_cfg80211_detach(drvr->config);
747
		free_netdev(ifp->ndev);
748 749 750
	}
}

751
int brcmf_attach(uint bus_hdrlen, struct device *dev)
752
{
753
	struct brcmf_pub *drvr = NULL;
754
	int ret = 0;
755 756 757 758

	brcmf_dbg(TRACE, "Enter\n");

	/* Allocate primary brcmf_info */
759 760
	drvr = kzalloc(sizeof(struct brcmf_pub), GFP_ATOMIC);
	if (!drvr)
761
		return -ENOMEM;
762

763
	mutex_init(&drvr->proto_block);
764 765

	/* Link to bus module */
766 767
	drvr->hdrlen = bus_hdrlen;
	drvr->bus_if = dev_get_drvdata(dev);
768
	drvr->bus_if->drvr = drvr;
769

770 771 772
	/* create device debugfs folder */
	brcmf_debugfs_attach(drvr);

773
	/* Attach and link in the protocol */
774 775
	ret = brcmf_proto_attach(drvr);
	if (ret != 0) {
776
		brcmf_err("brcmf_prot_attach failed\n");
777 778 779
		goto fail;
	}

780 781 782
	/* attach firmware event handler */
	brcmf_fweh_attach(drvr);

783 784
	INIT_LIST_HEAD(&drvr->bus_if->dcmd_list);

785 786
	init_waitqueue_head(&drvr->pend_8021x_wait);

787
	return ret;
788 789

fail:
790
	brcmf_detach(dev);
791

792
	return ret;
793 794
}

795
int brcmf_bus_start(struct device *dev)
796 797
{
	int ret = -1;
798 799
	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
	struct brcmf_pub *drvr = bus_if->drvr;
800
	struct brcmf_if *ifp;
801 802 803 804

	brcmf_dbg(TRACE, "\n");

	/* Bring up the bus */
A
Arend van Spriel 已提交
805
	ret = brcmf_bus_init(bus_if);
806
	if (ret != 0) {
807
		brcmf_err("brcmf_sdbrcm_bus_init failed %d\n", ret);
808 809 810
		return ret;
	}

811
	/* add primary networking interface */
812
	ifp = brcmf_add_if(drvr, 0, 0, "wlan%d", NULL);
813 814 815
	if (IS_ERR(ifp))
		return PTR_ERR(ifp);

816 817 818 819 820
	/* signal bus ready */
	bus_if->state = BRCMF_BUS_DATA;

	/* Bus is ready, do any initialization */
	ret = brcmf_c_preinit_dcmds(ifp);
821
	if (ret < 0)
822
		goto fail;
823

A
Arend van Spriel 已提交
824
	drvr->config = brcmf_cfg80211_attach(drvr, bus_if->dev);
825 826 827 828
	if (drvr->config == NULL) {
		ret = -ENOMEM;
		goto fail;
	}
829

830 831 832 833
	ret = brcmf_fweh_activate_events(ifp);
	if (ret < 0)
		goto fail;

834
	ret = brcmf_net_attach(ifp);
835
fail:
836
	if (ret < 0) {
837
		brcmf_err("failed: %d\n", ret);
838 839 840
		if (drvr->config)
			brcmf_cfg80211_detach(drvr->config);
		free_netdev(drvr->iflist[0]->ndev);
841
		drvr->iflist[0] = NULL;
842
		return ret;
843 844
	}

845 846 847 848 849 850 851 852
	return 0;
}

static void brcmf_bus_detach(struct brcmf_pub *drvr)
{
	brcmf_dbg(TRACE, "Enter\n");

	if (drvr) {
853 854
		/* Stop the protocol module */
		brcmf_proto_stop(drvr);
855

856
		/* Stop the bus module */
A
Arend van Spriel 已提交
857
		brcmf_bus_stop(drvr->bus_if);
858 859 860
	}
}

861 862 863 864 865 866 867 868 869 870 871
void brcmf_dev_reset(struct device *dev)
{
	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
	struct brcmf_pub *drvr = bus_if->drvr;

	if (drvr == NULL)
		return;

	brcmf_fil_cmd_int_set(drvr->iflist[0], BRCMF_C_TERMINATED, 1);
}

872
void brcmf_detach(struct device *dev)
873
{
874 875 876
	int i;
	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
	struct brcmf_pub *drvr = bus_if->drvr;
877 878 879

	brcmf_dbg(TRACE, "Enter\n");

880 881
	if (drvr == NULL)
		return;
882

883 884 885
	/* stop firmware event handling */
	brcmf_fweh_detach(drvr);

886 887 888 889
	/* make sure primary interface removed last */
	for (i = BRCMF_MAX_IFS-1; i > -1; i--)
		if (drvr->iflist[i])
			brcmf_del_if(drvr, i);
890

891
	brcmf_bus_detach(drvr);
892

893
	if (drvr->prot)
894
		brcmf_proto_detach(drvr);
895

896
	brcmf_debugfs_detach(drvr);
897 898
	bus_if->drvr = NULL;
	kfree(drvr);
899 900
}

901
static int brcmf_get_pend_8021x_cnt(struct brcmf_pub *drvr)
902
{
903
	return atomic_read(&drvr->pend_8021x_cnt);
904 905 906 907
}

int brcmf_netdev_wait_pend8021x(struct net_device *ndev)
{
908
	struct brcmf_if *ifp = netdev_priv(ndev);
909
	struct brcmf_pub *drvr = ifp->drvr;
910 911 912 913 914 915 916 917 918
	int err;

	err = wait_event_timeout(drvr->pend_8021x_wait,
				 !brcmf_get_pend_8021x_cnt(drvr),
				 msecs_to_jiffies(MAX_WAIT_FOR_8021X_TX));

	WARN_ON(!err);

	return !err;
919 920
}

921
static void brcmf_driver_init(struct work_struct *work)
922
{
923 924
	brcmf_debugfs_init();

925
#ifdef CONFIG_BRCMFMAC_SDIO
926
	brcmf_sdio_init();
927
#endif
928
#ifdef CONFIG_BRCMFMAC_USB
929
	brcmf_usb_init();
930
#endif
931 932 933 934 935 936 937 938
}
static DECLARE_WORK(brcmf_driver_work, brcmf_driver_init);

static int __init brcmfmac_module_init(void)
{
	if (!schedule_work(&brcmf_driver_work))
		return -EBUSY;

939
	return 0;
940 941
}

942
static void __exit brcmfmac_module_exit(void)
943
{
944 945
	cancel_work_sync(&brcmf_driver_work);

946 947 948
#ifdef CONFIG_BRCMFMAC_SDIO
	brcmf_sdio_exit();
#endif
949 950 951
#ifdef CONFIG_BRCMFMAC_USB
	brcmf_usb_exit();
#endif
952
	brcmf_debugfs_exit();
953 954
}

955 956
module_init(brcmfmac_module_init);
module_exit(brcmfmac_module_exit);